diff options
Diffstat (limited to 'test/functional/ex_cmds')
| -rw-r--r-- | test/functional/ex_cmds/bang_filter_spec.lua | 51 | ||||
| -rw-r--r-- | test/functional/ex_cmds/dict_notifications_spec.lua | 257 | ||||
| -rw-r--r-- | test/functional/ex_cmds/write_spec.lua | 41 | 
3 files changed, 341 insertions, 8 deletions
diff --git a/test/functional/ex_cmds/bang_filter_spec.lua b/test/functional/ex_cmds/bang_filter_spec.lua new file mode 100644 index 0000000000..a320e6d018 --- /dev/null +++ b/test/functional/ex_cmds/bang_filter_spec.lua @@ -0,0 +1,51 @@ +-- Specs for bang/filter commands + +local helpers = require('test.functional.helpers')(after_each) +local feed, execute, clear = helpers.feed, helpers.execute, helpers.clear +local mkdir, write_file, rmdir = helpers.mkdir, helpers.write_file, helpers.rmdir + +if helpers.pending_win32(pending) then return end + +local Screen = require('test.functional.ui.screen') + + +describe('issues', function() +  local screen + +  before_each(function() +    clear() +    rmdir('bang_filter_spec') +    mkdir('bang_filter_spec') +    write_file('bang_filter_spec/f1', 'f1') +    write_file('bang_filter_spec/f2', 'f2') +    write_file('bang_filter_spec/f3', 'f3') +    screen = Screen.new() +    screen:attach() +  end) + +  after_each(function() +    rmdir('bang_filter_spec') +  end) + +  it('#3269 Last line of shell output is not truncated', function() +    execute([[nnoremap <silent>\l :!ls bang_filter_spec<cr>]]) +    feed([[\l]]) +    screen:expect([[ +      ~                                                    | +      ~                                                    | +      ~                                                    | +      ~                                                    | +      ~                                                    | +      ~                                                    | +      ~                                                    | +      ~                                                    | +      :!ls bang_filter_spec                                | +                                                           | +      f1                                                   | +      f2                                                   | +      f3                                                   | +      Press ENTER or type command to continue^              | +    ]]) +  end) + +end) diff --git a/test/functional/ex_cmds/dict_notifications_spec.lua b/test/functional/ex_cmds/dict_notifications_spec.lua new file mode 100644 index 0000000000..dc87312911 --- /dev/null +++ b/test/functional/ex_cmds/dict_notifications_spec.lua @@ -0,0 +1,257 @@ +local helpers = require('test.functional.helpers')(after_each) +local clear, nvim, source = helpers.clear, helpers.nvim, helpers.source +local eq, next_msg = helpers.eq, helpers.next_message +local exc_exec = helpers.exc_exec + + +describe('dictionary change notifications', function() +  local channel + +  setup(function() +    clear() +    channel = nvim('get_api_info')[1] +    nvim('set_var', 'channel', channel) +  end) + +  -- the same set of tests are applied to top-level dictionaries(g:, b:, w: and +  -- t:) and a dictionary variable, so we generate them in the following +  -- function. +  local function gentests(dict_expr, dict_expr_suffix, dict_init) +    if not dict_expr_suffix then +      dict_expr_suffix = '' +    end + +    local function update(opval, key) +      if not key then +        key = 'watched' +      end +      if opval == '' then +        nvim('command', "unlet "..dict_expr..dict_expr_suffix..key) +      else +        nvim('command', "let "..dict_expr..dict_expr_suffix..key.." "..opval) +      end +    end + +    local function verify_echo() +      -- helper to verify that no notifications are sent after certain change +      -- to a dict +      nvim('command', "call rpcnotify(g:channel, 'echo')") +      eq({'notification', 'echo', {}}, next_msg()) +    end + +    local function verify_value(vals, key) +      if not key then +        key = 'watched' +      end +      eq({'notification', 'values', {key, vals}}, next_msg()) +    end + +    describe('watcher', function() +      if dict_init then +        setup(function() +          source(dict_init) +        end) +      end + +      before_each(function() +        source([[ +        function! g:Changed(dict, key, value) +          if a:dict != ]]..dict_expr..[[ | +            throw 'invalid dict' +          endif +          call rpcnotify(g:channel, 'values', a:key, a:value) +        endfunction +        call dictwatcheradd(]]..dict_expr..[[, "watched", "g:Changed") +        call dictwatcheradd(]]..dict_expr..[[, "watched2", "g:Changed") +        ]]) +      end) + +      after_each(function() +        source([[ +        call dictwatcherdel(]]..dict_expr..[[, "watched", "g:Changed") +        call dictwatcherdel(]]..dict_expr..[[, "watched2", "g:Changed") +        ]]) +        update('= "test"') +        update('= "test2"', 'watched2') +        update('', 'watched2') +        update('') +        verify_echo() +      end) + +      it('is not triggered when unwatched keys are updated', function() +        update('= "noop"', 'unwatched') +        update('.= "noop2"', 'unwatched') +        update('', 'unwatched') +        verify_echo() +      end) + +      it('is triggered by remove()', function() +        update('= "test"') +        verify_value({new = 'test'}) +        nvim('command', 'call remove('..dict_expr..', "watched")') +        verify_value({old = 'test'}) +      end) + +      it('is triggered by extend()', function() +        update('= "xtend"') +        verify_value({new = 'xtend'}) +        nvim('command', [[ +          call extend(]]..dict_expr..[[, {'watched': 'xtend2', 'watched2': 5, 'watched3': 'a'}) +        ]]) +        verify_value({old = 'xtend', new = 'xtend2'}) +        verify_value({new = 5}, 'watched2') +        update('') +        verify_value({old = 'xtend2'}) +        update('', 'watched2') +        verify_value({old = 5}, 'watched2') +        update('', 'watched3') +        verify_echo() +      end) + +      it('is triggered with key patterns', function() +        source([[ +        call dictwatcheradd(]]..dict_expr..[[, "wat*", "g:Changed") +        ]]) +        update('= 1') +        verify_value({new = 1}) +        verify_value({new = 1}) +        update('= 3', 'watched2') +        verify_value({new = 3}, 'watched2') +        verify_value({new = 3}, 'watched2') +        verify_echo() +        source([[ +        call dictwatcherdel(]]..dict_expr..[[, "wat*", "g:Changed") +        ]]) +        -- watch every key pattern +        source([[ +        call dictwatcheradd(]]..dict_expr..[[, "*", "g:Changed") +        ]]) +        update('= 3', 'another_key') +        update('= 4', 'another_key') +        update('', 'another_key') +        update('= 2') +        verify_value({new = 3}, 'another_key') +        verify_value({old = 3, new = 4}, 'another_key') +        verify_value({old = 4}, 'another_key') +        verify_value({old = 1, new = 2}) +        verify_value({old = 1, new = 2}) +        verify_echo() +        source([[ +        call dictwatcherdel(]]..dict_expr..[[, "*", "g:Changed") +        ]]) +      end) + +      -- test a sequence of updates of different types to ensure proper memory +      -- management(with ASAN) +      local function test_updates(tests) +        it('test change sequence', function() +          local input, output +          for i = 1, #tests do +            input, output = unpack(tests[i]) +            update(input) +            verify_value(output) +          end +        end) +      end + +      test_updates({ +        {'= 3', {new = 3}}, +        {'= 6', {old = 3, new = 6}}, +        {'+= 3', {old = 6, new = 9}}, +        {'', {old = 9}} +      }) + +      test_updates({ +        {'= "str"', {new = 'str'}}, +        {'= "str2"', {old = 'str', new = 'str2'}}, +        {'.= "2str"', {old = 'str2', new = 'str22str'}}, +        {'', {old = 'str22str'}} +      }) + +      test_updates({ +        {'= [1, 2]', {new = {1, 2}}}, +        {'= [1, 2, 3]', {old = {1, 2}, new = {1, 2, 3}}}, +        -- the += will update the list in place, so old and new are the same +        {'+= [4, 5]', {old = {1, 2, 3, 4, 5}, new = {1, 2, 3, 4, 5}}}, +        {'', {old = {1, 2, 3, 4 ,5}}} +      }) + +      test_updates({ +        {'= {"k": "v"}', {new = {k = 'v'}}}, +        {'= {"k1": 2}', {old = {k = 'v'}, new = {k1 = 2}}}, +        {'', {old = {k1 = 2}}}, +      }) +    end) +  end + +  gentests('g:') +  gentests('b:') +  gentests('w:') +  gentests('t:') +  gentests('g:dict_var', '.', 'let g:dict_var = {}') + +  describe('multiple watchers on the same dict/key', function() +    setup(function() +      source([[ +      function! g:Watcher1(dict, key, value) +        call rpcnotify(g:channel, '1', a:key, a:value) +      endfunction +      function! g:Watcher2(dict, key, value) +        call rpcnotify(g:channel, '2', a:key, a:value) +      endfunction +      call dictwatcheradd(g:, "key", "g:Watcher1") +      call dictwatcheradd(g:, "key", "g:Watcher2") +      ]]) +    end) + +    it('invokes all callbacks when the key is changed', function() +      nvim('command', 'let g:key = "value"') +      eq({'notification', '1', {'key', {new = 'value'}}}, next_msg()) +      eq({'notification', '2', {'key', {new = 'value'}}}, next_msg()) +    end) + +    it('only removes watchers that fully match dict, key and callback', function() +      nvim('command', 'call dictwatcherdel(g:, "key", "g:Watcher1")') +      nvim('command', 'let g:key = "v2"') +      eq({'notification', '2', {'key', {old = 'value', new = 'v2'}}}, next_msg()) +    end) +  end) + +  describe('errors', function() +    -- WARNING: This suite depends on the above tests +    it('fails to remove if no watcher with matching callback is found', function() +      eq("Vim(call):Couldn't find a watcher matching key and callback", +        exc_exec('call dictwatcherdel(g:, "key", "g:Watcher1")')) +    end) + +    it('fails to remove if no watcher with matching key is found', function() +      eq("Vim(call):Couldn't find a watcher matching key and callback", +        exc_exec('call dictwatcherdel(g:, "invalid_key", "g:Watcher2")')) +    end) + +    it("fails to add/remove if the callback doesn't exist", function() +      eq("Vim(call):Function g:InvalidCb doesn't exist", +        exc_exec('call dictwatcheradd(g:, "key", "g:InvalidCb")')) +      eq("Vim(call):Function g:InvalidCb doesn't exist", +        exc_exec('call dictwatcherdel(g:, "key", "g:InvalidCb")')) +    end) + +    it('fails with empty keys', function() +      eq("Vim(call):E713: Cannot use empty key for Dictionary", +        exc_exec('call dictwatcheradd(g:, "", "g:Watcher1")')) +      eq("Vim(call):E713: Cannot use empty key for Dictionary", +        exc_exec('call dictwatcherdel(g:, "", "g:Watcher1")')) +    end) + +    it('fails to replace a watcher function', function() +      source([[ +      function! g:ReplaceWatcher2() +        function! g:Watcher2() +        endfunction +      endfunction +      ]]) +      eq("Vim(function):E127: Cannot redefine function Watcher2: It is in use", +        exc_exec('call g:ReplaceWatcher2()')) +    end) +  end) +end) diff --git a/test/functional/ex_cmds/write_spec.lua b/test/functional/ex_cmds/write_spec.lua index c1bc5d3140..4ac9f312ef 100644 --- a/test/functional/ex_cmds/write_spec.lua +++ b/test/functional/ex_cmds/write_spec.lua @@ -1,20 +1,26 @@ --- Specs for :write -  local helpers = require('test.functional.helpers')(after_each) -local eq, eval, clear, write_file, execute, source = -	helpers.eq, helpers.eval, helpers.clear, helpers.write_file, -	helpers.execute, helpers.source +local eq, eval, clear, write_file, execute, source, insert = +  helpers.eq, helpers.eval, helpers.clear, helpers.write_file, +  helpers.execute, helpers.source, helpers.insert  if helpers.pending_win32(pending) then return end  describe(':write', function() -  after_each(function() +  local function cleanup()      os.remove('test_bkc_file.txt')      os.remove('test_bkc_link.txt') +    os.remove('test_fifo') +  end +  before_each(function() +    clear() +    cleanup() +  end) +  after_each(function() +    cleanup()    end)    it('&backupcopy=auto preserves symlinks', function() -    clear('--cmd', 'set backupcopy=auto') +    execute('set backupcopy=auto')      write_file('test_bkc_file.txt', 'content0')      execute("silent !ln -s test_bkc_file.txt test_bkc_link.txt")      source([[ @@ -27,7 +33,7 @@ describe(':write', function()    end)    it('&backupcopy=no replaces symlink with new file', function() -    clear('--cmd', 'set backupcopy=no') +    execute('set backupcopy=no')      write_file('test_bkc_file.txt', 'content0')      execute("silent !ln -s test_bkc_file.txt test_bkc_link.txt")      source([[ @@ -38,4 +44,23 @@ describe(':write', function()      eq(eval("['content0']"), eval("readfile('test_bkc_file.txt')"))      eq(eval("['content1']"), eval("readfile('test_bkc_link.txt')"))    end) + +  it("appends FIFO file", function() +    if eval("executable('mkfifo')") == 0 then +      pending('missing "mkfifo" command', function()end) +      return +    end + +    local text = "some fifo text from write_spec" +    assert(os.execute("mkfifo test_fifo")) +    insert(text) + +    -- Blocks until a consumer reads the FIFO. +    execute("write >> test_fifo") + +    -- Read the FIFO, this will unblock the :write above. +    local fifo = assert(io.open("test_fifo")) +    eq(text.."\n", fifo:read("*all")) +    fifo:close() +  end)  end)  | 
