1-- Other ShaDa tests
2local helpers = require('test.functional.helpers')(after_each)
3local meths, nvim_command, funcs, eq =
4  helpers.meths, helpers.command, helpers.funcs, helpers.eq
5local write_file, spawn, set_session, nvim_prog, exc_exec =
6  helpers.write_file, helpers.spawn, helpers.set_session, helpers.nvim_prog,
7  helpers.exc_exec
8
9local lfs = require('lfs')
10local paths = require('test.config.paths')
11
12local mpack = require('mpack')
13
14local shada_helpers = require('test.functional.shada.helpers')
15local reset, clear, get_shada_rw =
16  shada_helpers.reset, shada_helpers.clear, shada_helpers.get_shada_rw
17local read_shada_file = shada_helpers.read_shada_file
18
19local wshada, _, shada_fname, clean =
20  get_shada_rw('Xtest-functional-shada-shada.shada')
21
22local dirname = 'Xtest-functional-shada-shada.d'
23local dirshada = dirname .. '/main.shada'
24
25describe('ShaDa support code', function()
26  before_each(reset)
27  after_each(function()
28    clear()
29    clean()
30    lfs.rmdir(dirname)
31  end)
32
33  it('preserves `s` item size limit with unknown entries', function()
34    wshada('\100\000\207\000\000\000\000\000\000\004\000\218\003\253' .. ('-'):rep(1024 - 3)
35           .. '\100\000\207\000\000\000\000\000\000\004\001\218\003\254' .. ('-'):rep(1025 - 3))
36    eq(0, exc_exec('wshada ' .. shada_fname))
37    local found = 0
38    for _, v in ipairs(read_shada_file(shada_fname)) do
39      if v.type == 100 then
40        found = found + 1
41      end
42    end
43    eq(2, found)
44    eq(0, exc_exec('set shada-=s10 shada+=s1'))
45    eq(0, exc_exec('wshada ' .. shada_fname))
46    found = 0
47    for _, v in ipairs(read_shada_file(shada_fname)) do
48      if v.type == 100 then
49        found = found + 1
50      end
51    end
52    eq(1, found)
53  end)
54
55  it('preserves `s` item size limit with instance history entries', function()
56    local hist1 = ('-'):rep(1024 - 5)
57    local hist2 = ('-'):rep(1025 - 5)
58    nvim_command('set shada-=s10 shada+=s1')
59    funcs.histadd(':', hist1)
60    funcs.histadd(':', hist2)
61    eq(0, exc_exec('wshada ' .. shada_fname))
62    local found = 0
63    for _, v in ipairs(read_shada_file(shada_fname)) do
64      if v.type == 4 then
65        found = found + 1
66        eq(hist1, v.value[2])
67      end
68    end
69    eq(1, found)
70  end)
71
72  it('leaves .tmp.a in-place when there is error in original ShaDa', function()
73    wshada('Some text file')
74    eq('Vim(wshada):E576: Error while reading ShaDa file: last entry specified that it occupies 109 bytes, but file ended earlier', exc_exec('wshada ' .. shada_fname))
75    eq(1, read_shada_file(shada_fname .. '.tmp.a')[1].type)
76  end)
77
78  it('does not leave .tmp.a in-place when there is error in original ShaDa, but writing with bang', function()
79    wshada('Some text file')
80    eq(0, exc_exec('wshada! ' .. shada_fname))
81    eq(1, read_shada_file(shada_fname)[1].type)
82    eq(nil, lfs.attributes(shada_fname .. '.tmp.a'))
83  end)
84
85  it('leaves .tmp.b in-place when there is error in original ShaDa and it has .tmp.a', function()
86    wshada('Some text file')
87    eq('Vim(wshada):E576: Error while reading ShaDa file: last entry specified that it occupies 109 bytes, but file ended earlier', exc_exec('wshada ' .. shada_fname))
88    eq('Vim(wshada):E576: Error while reading ShaDa file: last entry specified that it occupies 109 bytes, but file ended earlier', exc_exec('wshada ' .. shada_fname))
89    eq(1, read_shada_file(shada_fname .. '.tmp.a')[1].type)
90    eq(1, read_shada_file(shada_fname .. '.tmp.b')[1].type)
91  end)
92
93  it('leaves .tmp.z in-place when there is error in original ShaDa and it has .tmp.a … .tmp.x', function()
94    wshada('Some text file')
95    local i = ('a'):byte()
96    while i < ('z'):byte() do
97      write_file(shada_fname .. ('.tmp.%c'):format(i), 'Some text file', true)
98      i = i + 1
99    end
100    eq('Vim(wshada):E576: Error while reading ShaDa file: last entry specified that it occupies 109 bytes, but file ended earlier', exc_exec('wshada ' .. shada_fname))
101    eq(1, read_shada_file(shada_fname .. '.tmp.z')[1].type)
102  end)
103
104  it('errors out when there are .tmp.a … .tmp.z ShaDa files', function()
105    wshada('')
106    local i = ('a'):byte()
107    while i <= ('z'):byte() do
108      write_file(shada_fname .. ('.tmp.%c'):format(i), '', true)
109      i = i + 1
110    end
111    eq('Vim(wshada):E138: All Xtest-functional-shada-shada.shada.tmp.X files exist, cannot write ShaDa file!', exc_exec('wshada ' .. shada_fname))
112  end)
113
114  it('reads correctly various timestamps', function()
115    local msgpack = {
116      '\100',  -- Positive fixnum 100
117      '\204\255',  -- uint 8 255
118      '\205\010\003',  -- uint 16 2563
119      '\206\255\010\030\004',  -- uint 32 4278853124
120      '\207\005\100\060\250\255\010\030\004',  -- uint 64 388502516579048964
121    }
122    local s = '\100'
123    local e = '\001\192'
124    wshada(s .. table.concat(msgpack, e .. s) .. e)
125    eq(0, exc_exec('wshada ' .. shada_fname))
126    local found = 0
127    local typ = mpack.unpack(s)
128    for _, v in ipairs(read_shada_file(shada_fname)) do
129      if v.type == typ then
130        found = found + 1
131        eq(mpack.unpack(msgpack[found]), v.timestamp)
132      end
133    end
134    eq(#msgpack, found)
135  end)
136
137  it('does not write NONE file', function()
138    local session = spawn({nvim_prog, '-u', 'NONE', '-i', 'NONE', '--embed',
139                           '--headless', '--cmd', 'qall'}, true)
140    session:close()
141    eq(nil, lfs.attributes('NONE'))
142    eq(nil, lfs.attributes('NONE.tmp.a'))
143  end)
144
145  it('does not read NONE file', function()
146    write_file('NONE', '\005\001\015\131\161na\162rX\194\162rc\145\196\001-')
147    local session = spawn({nvim_prog, '-u', 'NONE', '-i', 'NONE', '--embed',
148                           '--headless'}, true)
149    set_session(session)
150    eq('', funcs.getreg('a'))
151    session:close()
152    os.remove('NONE')
153  end)
154
155  local marklike = {[7]=true, [8]=true, [10]=true, [11]=true}
156  local find_file = function(fname)
157    local found = {}
158    for _, v in ipairs(read_shada_file(shada_fname)) do
159      if marklike[v.type] and v.value.f == fname then
160        found[v.type] = (found[v.type] or 0) + 1
161      elseif v.type == 9 then
162        for _, b in ipairs(v.value) do
163          if b.f == fname then
164            found[v.type] = (found[v.type] or 0) + 1
165          end
166        end
167      end
168    end
169    return found
170  end
171
172  it('correctly uses shada-r option', function()
173    nvim_command('set shellslash')
174    meths.set_var('__home', paths.test_source_path)
175    nvim_command('let $HOME = __home')
176    nvim_command('unlet __home')
177    nvim_command('edit ~/README.md')
178    nvim_command('normal! GmAggmaAabc')
179    nvim_command('undo')
180    nvim_command('set shada+=%')
181    nvim_command('wshada! ' .. shada_fname)
182    local readme_fname = funcs.resolve(paths.test_source_path) .. '/README.md'
183    eq({[7]=2, [8]=2, [9]=1, [10]=4, [11]=1}, find_file(readme_fname))
184    nvim_command('set shada+=r~')
185    nvim_command('wshada! ' .. shada_fname)
186    eq({}, find_file(readme_fname))
187    nvim_command('set shada-=r~')
188    nvim_command('wshada! ' .. shada_fname)
189    eq({[7]=2, [8]=2, [9]=1, [10]=4, [11]=1}, find_file(readme_fname))
190    nvim_command('set shada+=r' .. funcs.escape(
191      funcs.escape(paths.test_source_path, '$~'), ' "\\,'))
192    nvim_command('wshada! ' .. shada_fname)
193    eq({}, find_file(readme_fname))
194  end)
195
196  it('correctly ignores case with shada-r option', function()
197    nvim_command('set shellslash')
198    local pwd = funcs.getcwd()
199    local relfname = 'абв/test'
200    local fname = pwd .. '/' .. relfname
201    meths.set_var('__fname', fname)
202    nvim_command('silent! edit `=__fname`')
203    funcs.setline(1, {'a', 'b', 'c', 'd'})
204    nvim_command('normal! GmAggmaAabc')
205    nvim_command('undo')
206    nvim_command('set shada+=%')
207    nvim_command('wshada! ' .. shada_fname)
208    eq({[7]=2, [8]=2, [9]=1, [10]=4, [11]=2}, find_file(fname))
209    nvim_command('set shada+=r' .. pwd .. '/АБВ')
210    nvim_command('wshada! ' .. shada_fname)
211    eq({}, find_file(fname))
212  end)
213
214  it('is able to set &shada after &viminfo', function()
215    meths.set_option('viminfo', '\'10')
216    eq('\'10', meths.get_option('viminfo'))
217    eq('\'10', meths.get_option('shada'))
218    meths.set_option('shada', '')
219    eq('', meths.get_option('viminfo'))
220    eq('', meths.get_option('shada'))
221  end)
222
223  it('is able to set all& after setting &shada', function()
224    meths.set_option('shada', '\'10')
225    eq('\'10', meths.get_option('viminfo'))
226    eq('\'10', meths.get_option('shada'))
227    nvim_command('set all&')
228    eq('!,\'100,<50,s10,h', meths.get_option('viminfo'))
229    eq('!,\'100,<50,s10,h', meths.get_option('shada'))
230  end)
231
232  it('is able to set &shada after &viminfo using :set', function()
233    nvim_command('set viminfo=\'10')
234    eq('\'10', meths.get_option('viminfo'))
235    eq('\'10', meths.get_option('shada'))
236    nvim_command('set shada=')
237    eq('', meths.get_option('viminfo'))
238    eq('', meths.get_option('shada'))
239  end)
240
241  it('does not crash when ShaDa file directory is not writable', function()
242    if helpers.pending_win32(pending) then return end
243
244    funcs.mkdir(dirname, '', 0)
245    eq(0, funcs.filewritable(dirname))
246    reset{shadafile=dirshada, args={'--cmd', 'set shada='}}
247    meths.set_option('shada', '\'10')
248    eq('Vim(wshada):E886: System error while opening ShaDa file '
249       .. 'Xtest-functional-shada-shada.d/main.shada for reading to merge '
250       .. 'before writing it: permission denied',
251       exc_exec('wshada'))
252    meths.set_option('shada', '')
253  end)
254end)
255