1local helpers = require('test.unit.helpers')(after_each)
2local itp = helpers.gen_itp(it)
3local lfs = require('lfs')
4local child_call_once = helpers.child_call_once
5local sleep = helpers.sleep
6
7local ffi = helpers.ffi
8local cimport = helpers.cimport
9local to_cstr = helpers.to_cstr
10local neq = helpers.neq
11local eq = helpers.eq
12
13cimport('./src/nvim/ex_cmds_defs.h')
14cimport('./src/nvim/buffer_defs.h')
15local options = cimport('./src/nvim/option_defs.h')
16-- TODO: remove: local vim = cimport('./src/nvim/vim.h')
17local undo = cimport('./src/nvim/undo.h')
18local buffer = cimport('./src/nvim/buffer.h')
19
20local old_p_udir = nil
21
22-- Values expected by tests. Set in the setup function and destroyed in teardown
23local file_buffer = nil
24local buffer_hash = nil
25
26child_call_once(function()
27  if old_p_udir == nil then
28    old_p_udir = options.p_udir  -- save the old value of p_udir (undodir)
29  end
30
31  -- create a new buffer
32  local c_file = to_cstr('Xtest-unit-undo')
33  file_buffer = buffer.buflist_new(c_file, c_file, 1, buffer.BLN_LISTED)
34  file_buffer.b_u_numhead = 1 -- Pretend that the buffer has been changed
35
36  -- TODO(christopher.waldon.dev@gmail.com): replace the 32 with UNDO_HASH_SIZE
37  -- requires refactor of UNDO_HASH_SIZE into constant/enum for ffi
38  --
39  -- compute a hash for this undofile
40  buffer_hash = ffi.new('char_u[32]')
41  undo.u_compute_hash(file_buffer, buffer_hash)
42end)
43
44
45describe('u_write_undo', function()
46  setup(function()
47    lfs.mkdir('unit-test-directory')
48    lfs.chdir('unit-test-directory')
49    options.p_udir = to_cstr(lfs.currentdir())  -- set p_udir to be the test dir
50  end)
51
52  teardown(function()
53    lfs.chdir('..')
54    local success, err = lfs.rmdir('unit-test-directory')
55    if not success then
56      print(err)  -- inform tester if directory fails to delete
57    end
58    options.p_udir = old_p_udir  --restore old p_udir
59  end)
60
61  -- Lua wrapper for u_write_undo
62  local function u_write_undo(name, forceit, buf, buf_hash)
63    if name ~= nil then
64      name = to_cstr(name)
65    end
66
67    return undo.u_write_undo(name, forceit, buf, buf_hash)
68  end
69
70  itp('writes an undo file to undodir given a buffer and hash', function()
71    u_write_undo(nil, false, file_buffer, buffer_hash)
72    local correct_name = ffi.string(undo.u_get_undo_file_name(file_buffer.b_ffname, false))
73    local undo_file = io.open(correct_name, "r")
74
75    neq(undo_file, nil)
76    local success, err = os.remove(correct_name)  -- delete the file now that we're done with it.
77    if not success then
78      print(err)  -- inform tester if undofile fails to delete
79    end
80  end)
81
82  itp('writes a correctly-named undo file to undodir given a name, buffer, and hash', function()
83    local correct_name = "undofile.test"
84    u_write_undo(correct_name, false, file_buffer, buffer_hash)
85    local undo_file = io.open(correct_name, "r")
86
87    neq(undo_file, nil)
88    local success, err = os.remove(correct_name)  -- delete the file now that we're done with it.
89    if not success then
90      print(err)  -- inform tester if undofile fails to delete
91    end
92  end)
93
94  itp('does not write an undofile when the buffer has no valid undofile name', function()
95    -- TODO(christopher.waldon.dev@gmail.com): Figure out how to test this.
96    -- it's hard because u_get_undo_file_name() would need to return null
97  end)
98
99  itp('writes the undofile with the same permissions as the original file', function()
100    -- Create Test file and set permissions
101    local test_file_name = "./test.file"
102    local test_permission_file = io.open(test_file_name, "w")
103    test_permission_file:write("testing permissions")
104    test_permission_file:close()
105    local test_permissions = lfs.attributes(test_file_name).permissions
106
107    -- Create vim buffer
108    local c_file = to_cstr(test_file_name)
109    file_buffer = buffer.buflist_new(c_file, c_file, 1, buffer.BLN_LISTED)
110    file_buffer.b_u_numhead = 1 -- Pretend that the buffer has been changed
111
112    u_write_undo(nil, false, file_buffer, buffer_hash)
113
114    -- Find out the correct name of the undofile
115    local undo_file_name = ffi.string(undo.u_get_undo_file_name(file_buffer.b_ffname, false))
116
117    -- Find out the permissions of the new file
118    local permissions = lfs.attributes(undo_file_name).permissions
119    eq(test_permissions, permissions)
120
121    -- delete the file now that we're done with it.
122    local success, err = os.remove(test_file_name)
123    if not success then
124      print(err)  -- inform tester if undofile fails to delete
125    end
126    success, err = os.remove(undo_file_name)
127    if not success then
128      print(err)  -- inform tester if undofile fails to delete
129    end
130  end)
131
132  itp('writes an undofile only readable by the user if the buffer is unnamed', function()
133    local correct_permissions = "rw-------"
134    local undo_file_name = "test.undo"
135
136    -- Create vim buffer
137    file_buffer = buffer.buflist_new(nil, nil, 1, buffer.BLN_LISTED)
138    file_buffer.b_u_numhead = 1 -- Pretend that the buffer has been changed
139
140    u_write_undo(undo_file_name, false, file_buffer, buffer_hash)
141
142    -- Find out the permissions of the new file
143    local permissions = lfs.attributes(undo_file_name).permissions
144    eq(correct_permissions, permissions)
145
146    -- delete the file now that we're done with it.
147    local success, err = os.remove(undo_file_name)
148    if not success then
149      print(err)  -- inform tester if undofile fails to delete
150    end
151  end)
152
153  itp('forces writing undo file for :wundo! command', function()
154    local file_contents = "testing permissions"
155    -- Write a text file where the undofile should go
156    local correct_name = ffi.string(undo.u_get_undo_file_name(file_buffer.b_ffname, false))
157    helpers.write_file(correct_name, file_contents, true, false)
158
159    -- Call with `forceit`.
160    u_write_undo(correct_name, true, file_buffer, buffer_hash)
161
162    local undo_file_contents = helpers.read_file(correct_name)
163
164    neq(file_contents, undo_file_contents)
165    local success, deletion_err = os.remove(correct_name)  -- delete the file now that we're done with it.
166    if not success then
167      print(deletion_err)  -- inform tester if undofile fails to delete
168    end
169  end)
170
171  itp('overwrites an existing undo file', function()
172    u_write_undo(nil, false, file_buffer, buffer_hash)
173    local correct_name = ffi.string(undo.u_get_undo_file_name(file_buffer.b_ffname, false))
174
175    local file_last_modified = lfs.attributes(correct_name).modification
176
177    sleep(1000)  -- Ensure difference in timestamps.
178    file_buffer.b_u_numhead = 1  -- Mark it as if there are changes
179    u_write_undo(nil, false, file_buffer, buffer_hash)
180
181    local file_last_modified_2 = lfs.attributes(correct_name).modification
182
183    -- print(file_last_modified, file_last_modified_2)
184    neq(file_last_modified, file_last_modified_2)
185    local success, err = os.remove(correct_name)  -- delete the file now that we're done with it.
186    if not success then
187      print(err)  -- inform tester if undofile fails to delete
188    end
189  end)
190
191  itp('does not overwrite an existing file that is not an undo file', function()
192    -- TODO: write test
193  end)
194
195  itp('does not overwrite an existing file that has the wrong permissions', function()
196    -- TODO: write test
197  end)
198
199  itp('does not write an undo file if there is no undo information for the buffer', function()
200    file_buffer.b_u_numhead = 0  -- Mark it as if there is no undo information
201    local correct_name = ffi.string(undo.u_get_undo_file_name(file_buffer.b_ffname, false))
202
203    local existing_file = io.open(correct_name,"r")
204    if existing_file then
205      existing_file:close()
206      os.remove(correct_name)
207    end
208    u_write_undo(nil, false, file_buffer, buffer_hash)
209    local undo_file = io.open(correct_name, "r")
210
211    eq(undo_file, nil)
212  end)
213end)
214