diff options
| -rw-r--r-- | src/nvim/memline.c | 55 | ||||
| -rw-r--r-- | src/nvim/path.c | 11 | ||||
| -rw-r--r-- | src/nvim/testdir/Makefile | 2 | ||||
| -rw-r--r-- | src/nvim/testdir/test78.in | 46 | ||||
| -rw-r--r-- | src/nvim/testdir/test78.ok | 3 | ||||
| -rw-r--r-- | test/functional/ex_cmds/recover_spec.lua | 71 | ||||
| -rw-r--r-- | test/functional/helpers.lua | 11 | ||||
| -rw-r--r-- | test/functional/legacy/078_swapfile_recover_spec.lua | 80 | 
8 files changed, 181 insertions, 98 deletions
| diff --git a/src/nvim/memline.c b/src/nvim/memline.c index 93fbbd512f..448f15cc07 100644 --- a/src/nvim/memline.c +++ b/src/nvim/memline.c @@ -824,7 +824,7 @@ void ml_recover(void)      (void)recover_names(fname, FALSE, i, &fname_used);    }    if (fname_used == NULL) -    goto theend;                        /* out of memory */ +    goto theend;  // user chose invalid number.    /* When called from main() still need to initialize storage structure */    if (called_from_main && ml_open(curbuf) == FAIL) @@ -1244,8 +1244,10 @@ theend:        mf_put(mfp, hp, false, false);      mf_close(mfp, false);           /* will also free(mfp->mf_fname) */    } -  free(buf->b_ml.ml_stack); -  free(buf); +  if (buf != NULL) {  //may be NULL if swap file not found. +    free(buf->b_ml.ml_stack); +    free(buf); +  }    if (serious_error && called_from_main)      ml_close(curbuf, TRUE);    else { @@ -1321,53 +1323,35 @@ recover_names (      if (dir_name[0] == '.' && dir_name[1] == NUL) {     /* check current dir */        if (fname == NULL) {          names[0] = vim_strsave((char_u *)"*.sw?"); -#if defined(UNIX) || defined(WIN3264)          /* For Unix names starting with a dot are special.  MS-Windows           * supports this too, on some file systems. */          names[1] = vim_strsave((char_u *)".*.sw?");          names[2] = vim_strsave((char_u *)".sw?");          num_names = 3; -#else -        num_names = 1; -#endif        } else          num_names = recov_file_names(names, fname_res, TRUE);      } else {                      /* check directory dir_name */        if (fname == NULL) {          names[0] = concat_fnames(dir_name, (char_u *)"*.sw?", TRUE); -#if defined(UNIX) || defined(WIN3264)          /* For Unix names starting with a dot are special.  MS-Windows           * supports this too, on some file systems. */          names[1] = concat_fnames(dir_name, (char_u *)".*.sw?", TRUE);          names[2] = concat_fnames(dir_name, (char_u *)".sw?", TRUE);          num_names = 3; -#else -        num_names = 1; -#endif        } else { -#if defined(UNIX) || defined(WIN3264)          p = dir_name + STRLEN(dir_name);          if (after_pathsep(dir_name, p) && p[-1] == p[-2]) {            /* Ends with '//', Use Full path for swap name */            tail = make_percent_swname(dir_name, fname_res); -        } else -#endif -        tail = path_tail(fname_res); -        tail = concat_fnames(dir_name, tail, TRUE); +        } else { +          tail = path_tail(fname_res); +          tail = concat_fnames(dir_name, tail, TRUE); +        }          num_names = recov_file_names(names, tail, FALSE);          free(tail);        }      } -    // check for out-of-memory -    for (int i = 0; i < num_names; ++i) { -      if (names[i] == NULL) { -        for (int j = 0; j < num_names; ++j) -          free(names[j]); -        num_names = 0; -        break; -      } -    }      if (num_names == 0)        num_files = 0;      else if (expand_wildcards(num_names, names, &num_files, &files, @@ -1453,7 +1437,6 @@ recover_names (    return file_count;  } -#if defined(UNIX) || defined(WIN3264)  /* Need _very_ long file names */  /*   * Append the full path to name with path separators made into percent   * signs, to dir. An unnamed buffer is handled as "" (<currentdir>/"") @@ -1475,7 +1458,6 @@ static char_u *make_percent_swname(char_u *dir, char_u *name)    }    return d;  } -#endif  #ifdef UNIX  static int process_still_running; @@ -1576,17 +1558,12 @@ static time_t swapfile_info(char_u *fname)  }  static int recov_file_names(char_u **names, char_u *path, int prepend_dot) +  FUNC_ATTR_NONNULL_ALL  { -  int num_names; -  char_u      *p; -  int i; - -  num_names = 0; +  int num_names = 0; -  /* -   * May also add the file name with a dot prepended, for swap file in same -   * dir as original file. -   */ +  // May also add the file name with a dot prepended, for swap file in same +  // dir as original file.    if (prepend_dot) {      names[num_names] = modname(path, (char_u *)".sw?", TRUE);      if (names[num_names] == NULL) @@ -1597,8 +1574,8 @@ static int recov_file_names(char_u **names, char_u *path, int prepend_dot)    // Form the normal swap file name pattern by appending ".sw?".    names[num_names] = concat_fnames(path, (char_u *)".sw?", FALSE);    if (num_names >= 1) {     /* check if we have the same name twice */ -    p = names[num_names - 1]; -    i = (int)STRLEN(names[num_names - 1]) - (int)STRLEN(names[num_names]); +    char_u *p = names[num_names - 1]; +    int i = (int)STRLEN(names[num_names - 1]) - (int)STRLEN(names[num_names]);      if (i > 0)        p += i;               /* file name has been expanded to full path */ @@ -3088,7 +3065,6 @@ char_u *makeswapname(char_u *fname, char_u *ffname, buf_T *buf, char_u *dir_name    char_u fname_buf[MAXPATHL];  #endif -#if defined(UNIX) || defined(WIN3264)  /* Need _very_ long file names */    s = dir_name + STRLEN(dir_name);    if (after_pathsep(dir_name, s) && s[-1] == s[-2]) { /* Ends with '//', Use Full path */      r = NULL; @@ -3098,7 +3074,6 @@ char_u *makeswapname(char_u *fname, char_u *ffname, buf_T *buf, char_u *dir_name      }      return r;    } -#endif  #ifdef HAVE_READLINK    /* Expand symlink in the file name, so that we put the swap file with the diff --git a/src/nvim/path.c b/src/nvim/path.c index 9515205643..80f1947ccf 100644 --- a/src/nvim/path.c +++ b/src/nvim/path.c @@ -83,15 +83,12 @@ FileComparison path_full_compare(char_u *s1, char_u *s2, int checkname)    return kDifferentFiles;  } -/// Get the tail of a path: the file name. +/// Gets the tail (i.e., the filename segment) of a path `fname`.  /// -/// @param fname A file path. -/// @return -///   - Empty string, if fname is NULL. -///   - The position of the last path separator + 1. (i.e. empty string, if -///   fname ends in a slash). -///   - Never NULL. +/// @return pointer just past the last path separator (empty string, if fname +///         ends in a slash), or empty string if fname is NULL.  char_u *path_tail(char_u *fname) +  FUNC_ATTR_NONNULL_RET  {    if (fname == NULL) {      return (char_u *)""; diff --git a/src/nvim/testdir/Makefile b/src/nvim/testdir/Makefile index a530e3cf23..0a7c16e2cb 100644 --- a/src/nvim/testdir/Makefile +++ b/src/nvim/testdir/Makefile @@ -24,7 +24,7 @@ SCRIPTS := test_autoformat_join.out                                    \             test61.out  test62.out  test63.out  test64.out  test65.out  \                                     test68.out  test69.out              \             test71.out              test73.out  test74.out              \ -           test76.out              test78.out  test79.out  test80.out  \ +           test76.out                          test79.out  test80.out  \                         test82.out  test83.out                          \             test86.out  test87.out  test88.out                          \             test96.out                                                  \ diff --git a/src/nvim/testdir/test78.in b/src/nvim/testdir/test78.in deleted file mode 100644 index cb0e51edd5..0000000000 --- a/src/nvim/testdir/test78.in +++ /dev/null @@ -1,46 +0,0 @@ -Inserts 10000 lines with text to fill the swap file with two levels of pointer -blocks.  Then recovers from the swap file and checks all text is restored. - -We need about 10000 lines of 100 characters to get two levels of pointer -blocks. - -STARTTEST -:so small.vim -:set fileformat=unix undolevels=-1 -:e! Xtest -ggdG -:let text = "\tabcdefghijklmnoparstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnoparstuvwxyz0123456789" -:let i = 1 -:let linecount = 10000 -:while i <= linecount | call append(i - 1, i . text) | let i += 1 | endwhile -:preserve -:" get the name of the swap file -:redir => swapname -:swapname -:redir END -:let swapname = substitute(swapname, '[[:blank:][:cntrl:]]*\(.\{-}\)[[:blank:][:cntrl:]]*$', '\1', '') -:" make a copy of the swap file in Xswap -:set bin -:exe 'sp ' . swapname -:w! Xswap -:echo swapname -:set nobin -:new -:only! -:bwipe! Xtest -:call rename('Xswap', swapname) -:recover Xtest -:call delete(swapname) -:new -:call append(0, 'recovery start') -:wincmd w -:let linedollar = line('$') -:if linedollar < linecount | exe 'wincmd w' | call append(line('$'), "expected " . linecount . " lines but found only " . linedollar) | exe 'wincmd w' | let linecount = linedollar | endif -:let i = 1 -:while i <= linecount | if getline(i) != i . text | exe 'wincmd w' | call append(line('$'), i . ' differs') | exe 'wincmd w' | endif | let i += 1 | endwhile -:q! -:call append(line('$'), 'recovery end') -:w! test.out -:qa! -ENDTEST - diff --git a/src/nvim/testdir/test78.ok b/src/nvim/testdir/test78.ok deleted file mode 100644 index 6c3ecefe3c..0000000000 --- a/src/nvim/testdir/test78.ok +++ /dev/null @@ -1,3 +0,0 @@ -recovery start - -recovery end diff --git a/test/functional/ex_cmds/recover_spec.lua b/test/functional/ex_cmds/recover_spec.lua new file mode 100644 index 0000000000..d4c9477133 --- /dev/null +++ b/test/functional/ex_cmds/recover_spec.lua @@ -0,0 +1,71 @@ +-- Tests for :recover + +local helpers = require('test.functional.helpers') +local execute, eq, clear, eval, feed, expect, source = +  helpers.execute, helpers.eq, helpers.clear, helpers.eval, helpers.feed, +  helpers.expect, helpers.source + +describe(':recover', function() +  before_each(clear) + +  it('fails if given a non-existent swapfile', function() +    local swapname = 'bogus-swapfile' +    execute('recover '..swapname) -- This should not segfault. #2117 +    eq('E305: No swap file found for '..swapname, eval('v:errmsg')) +  end) + +end) + +describe(':preserve', function() +  local swapdir = lfs.currentdir()..'/testdir_recover_spec' +  before_each(function() +    clear() +    os.remove(swapdir) +    lfs.mkdir(swapdir) +  end) +  after_each(function() +    os.remove(swapdir) +  end) + +  it("saves to custom 'directory' and (R)ecovers (issue #1836)", function() +    local testfile = 'testfile_recover_spec' +    local init = [[ +      set swapfile fileformat=unix undolevels=-1 +      set directory^=]]..swapdir..[[// +    ]] + +    source(init) +    execute('set swapfile fileformat=unix undolevels=-1') +    -- Put swapdir at the start of the 'directory' list. #1836 +    execute('set directory^='..swapdir..'//') +    execute('edit '..testfile) +    feed('isometext<esc>') +    execute('preserve') +    source('redir => g:swapname | swapname | redir END') + +    local swappath1 = eval('g:swapname') + +    --TODO(justinmk): this is an ugly hack to force `helpers` to support +    --multiple sessions. +    local nvim2 = helpers.spawn({helpers.nvim_prog, '-u', 'NONE', '--embed'}) +    helpers.set_session(nvim2) + +    source(init) + +    -- Use the "SwapExists" event to choose the (R)ecover choice at the dialog. +    execute('autocmd SwapExists * let v:swapchoice = "r"') +    execute('silent edit '..testfile) +    source('redir => g:swapname | swapname | redir END') + +    local swappath2 = eval('g:swapname') + +    -- swapfile from session 1 should end in .swp +    assert(testfile..'.swp' == string.match(swappath1, '[^%%]+$')) + +    -- swapfile from session 2 should end in .swo +    assert(testfile..'.swo' == string.match(swappath2, '[^%%]+$')) + +    expect('sometext') +  end) + +end) diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua index 393b42dda5..27c94c34a8 100644 --- a/test/functional/helpers.lua +++ b/test/functional/helpers.lua @@ -18,6 +18,10 @@ if nvim_dir == nvim_prog then      nvim_dir = "."  end +-- Nvim "Unit Under Test" http://en.wikipedia.org/wiki/Device_under_test +local NvimUUT = {} +NvimUUT.__index = NvimUUT +  local prepend_argv  if os.getenv('VALGRIND') then @@ -49,6 +53,10 @@ end  local session, loop_running, loop_stopped, last_error +local function set_session(s) +  session = s +end +  local function request(method, ...)    local status, rv = session:request(method, ...)    if not status then @@ -305,5 +313,6 @@ return {    curwin = curwin,    curtab = curtab,    curbuf_contents = curbuf_contents, -  wait = wait +  wait = wait, +  set_session = set_session  } diff --git a/test/functional/legacy/078_swapfile_recover_spec.lua b/test/functional/legacy/078_swapfile_recover_spec.lua new file mode 100644 index 0000000000..e250c91441 --- /dev/null +++ b/test/functional/legacy/078_swapfile_recover_spec.lua @@ -0,0 +1,80 @@ +-- Inserts 10000 lines with text to fill the swap file with two levels of +-- pointer blocks.  Then recovers from the swap file and checks all text is +-- restored. We need about 10000 lines of 100 characters to get two levels of +-- pointer blocks. + +local helpers = require('test.functional.helpers') +local feed, insert, source = helpers.feed, helpers.insert, helpers.source +local clear, execute, expect, source = helpers.clear, helpers.execute, helpers.expect, helpers.source +local eval = helpers.eval + +describe('78', function() +  setup(clear) +  teardown(function() +    os.remove(".Xtest.swp") +    os.remove(".Xtest.swo") +  end) + +  it('is working', function() +    source([=[ +      set swapfile fileformat=unix undolevels=-1 +      e! Xtest +      let text = "\tabcdefghijklmnoparstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnoparstuvwxyz0123456789" +      let i = 1 +      let linecount = 10000 +      while i <= linecount | call append(i - 1, i . text) | let i += 1 | endwhile +      preserve +       +      " Get the name of the swap file, and clean up the :redir capture. +      redir => g:swapname | swapname | redir END +      let g:swapname = substitute(g:swapname, '[[:blank:][:cntrl:]]*\(.\{-}\)[[:blank:][:cntrl:]]*$', '\1', 'g') +      let g:swapname = fnameescape(g:swapname) +       +      " Make a copy of the swap file in Xswap +      set bin +      exe 'sp ' . g:swapname +      w! Xswap +       +      set nobin +      new +      only! +      bwipe! Xtest +      call rename('Xswap', g:swapname) +       +      "TODO(jkeyes): without 'silent', this hangs the test " at message: +      "    'Recovery completed. You should check if everything is OK.' +      silent recover Xtest +       +      call delete(g:swapname) +      new +      call append(0, 'recovery start') +      wincmd w +       +      let g:linedollar = line('$') +      if g:linedollar < linecount +        wincmd w +        call append(line('$'), "expected " . linecount +          \ . " lines but found only " . g:linedollar) +        wincmd w +        let linecount = g:linedollar +      endif +       +      let i = 1 +      while i <= linecount +        if getline(i) != i . text +          exe 'wincmd w' +          call append(line('$'), i . ' differs') +          exe 'wincmd w' +        endif +        let i += 1 +      endwhile +      q! +      call append(line('$'), 'recovery end') +    ]=]) + +    expect([[ +      recovery start +       +      recovery end]]) +  end) +end) | 
