Skip to content

Commit

Permalink
Expand test framework (#1548)
Browse files Browse the repository at this point in the history
* Expand test framework

A lot of tests are in the form of:

1. Set up some buffer.
2. Run a vim-go command.
3. Check if the output matches what we expect.

This adds `autoload/gotest.vim`, which includes two helper functions to
make this easy:

- `gotest#loadFile()` sets up the buffer state and some other stuff such
  as GOPATH.
- `gotest#assert_buffer()` checks if the current buffer contents match
  what's being expected.

This should make writing many tests easier.

Unfortunately Vim's syntax makes this look a wee bit awkward; but
personally I prefer this method over external fixture files as it's
easier to see what the input and output is on a single screen.
We can use the 'fixture style' if people prefer that. Or another
alternative would be to use Python to make multi-line statements look
better (...but then tests depend on +python).

I added a `fillstruct_test.vim` as a test, and also converted some
existing tests to make sure this works well.

In addition, there are also some related changes:

- Make sure invalid syntax etc. fails the test.
- Make sure `runtest.vim` doesn't set any global variables (only `s:`).
- Support adding `abort` to test funtions.
- Reset GOPATH etc. after each test.

* Add gotest#load_fixture() and gotest#assert_fixture()

This allows easy loading and diffing for fixtures.

Also rename `gotest#loadFile()` to `gotest#write_file()`; the snake_case
is more consistent with Vim's functions, and writing a file is what the
actually does.
  • Loading branch information
arp242 authored Nov 4, 2017
1 parent bd35a4e commit ba6ce99
Show file tree
Hide file tree
Showing 7 changed files with 244 additions and 82 deletions.
59 changes: 32 additions & 27 deletions autoload/go/def_test.vim
Original file line number Diff line number Diff line change
@@ -1,32 +1,37 @@
func Test_jump_to_declaration_guru()
let file_name = "test-fixtures/def/jump.go"
let lnum = 5
let col = 6

let out = printf("%s:%d:%d: defined here as func main", file_name, lnum, col)
let bin_name = "guru"

call go#def#jump_to_declaration(out, "", bin_name)

call assert_equal(file_name, bufname("%"))
call assert_equal(lnum, getcurpos()[1])
call assert_equal(col, getcurpos()[2])
func! Test_jump_to_declaration_guru() abort
try
let l:filename = 'def/jump.go'
let lnum = 5
let col = 6
let l:tmp = gotest#load_fixture(l:filename)

let guru_out = printf("%s:%d:%d: defined here as func main", filename, lnum, col)
call go#def#jump_to_declaration(guru_out, "", 'guru')

call assert_equal(filename, bufname("%"))
call assert_equal(lnum, getcurpos()[1])
call assert_equal(col, getcurpos()[2])
finally
call delete(l:tmp, 'rf')
endtry
endfunc

func Test_jump_to_declaration_godef()
let file_name = "test-fixtures/def/jump.go"
let lnum = 5
let col = 6

" note that the output of godef has two lines
let out = printf("%s:%d:%d\ndefined here as func main", file_name, lnum, col)
let bin_name = "godef"

call go#def#jump_to_declaration(out, "", bin_name)

call assert_equal(file_name, bufname("%"))
call assert_equal(lnum, getcurpos()[1])
call assert_equal(col, getcurpos()[2])
func! Test_jump_to_declaration_godef() abort
try
let filename = 'def/jump.go'
let lnum = 5
let col = 6
let l:tmp = gotest#load_fixture(l:filename)

let godef_out = printf("%s:%d:%d\ndefined here as func main", filename, lnum, col)
call go#def#jump_to_declaration(godef_out, "", 'godef')

call assert_equal(filename, bufname("%"))
call assert_equal(lnum, getcurpos()[1])
call assert_equal(col, getcurpos()[2])
finally
call delete(l:tmp, 'rf')
endtry
endfunc

" vim: sw=2 ts=2 et
19 changes: 19 additions & 0 deletions autoload/go/fillstruct_test.vim
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
func! Test_fillstruct() abort
try
let l:tmp = gotest#write_file('a/a.go', [
\ 'package a',
\ 'import "net/mail"',
\ 'var addr = mail.Address{}'])

call go#fillstruct#FillStruct()
call gotest#assert_buffer(1, [
\ 'var addr = mail.Address{',
\ '\tName: "",',
\ '\tAddress: "",',
\ '}'])
finally
call delete(l:tmp, 'rf')
endtry
endfunc

" vim: sw=2 ts=2 et
6 changes: 3 additions & 3 deletions autoload/go/fmt_test.vim
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
func Test_run_fmt()
func! Test_run_fmt() abort
let actual_file = tempname()
call writefile(readfile("test-fixtures/fmt/hello.go"), actual_file)

Expand All @@ -13,7 +13,7 @@ func Test_run_fmt()
call assert_equal(expected, actual)
endfunc

func Test_update_file()
func! Test_update_file() abort
let expected = join(readfile("test-fixtures/fmt/hello_golden.go"), "\n")
let source_file = tempname()
call writefile(readfile("test-fixtures/fmt/hello_golden.go"), source_file)
Expand All @@ -30,7 +30,7 @@ func Test_update_file()
call assert_equal(expected, actual)
endfunc

func Test_goimports()
func! Test_goimports() abort
let $GOPATH = 'test-fixtures/fmt/'
let actual_file = tempname()
call writefile(readfile("test-fixtures/fmt/src/imports/goimports.go"), actual_file)
Expand Down
42 changes: 17 additions & 25 deletions autoload/go/tags_test.vim
Original file line number Diff line number Diff line change
@@ -1,30 +1,22 @@
func Test_add_tags()
let input_file = tempname()
call writefile(readfile("test-fixtures/tags/add_all_input.go"), input_file)

let expected = join(readfile("test-fixtures/tags/add_all_golden.go"), "\n")

" run for offset 40, which is inside the struct
silent call go#tags#run(0, 0, 40, "add", input_file, 1)

let actual = join(readfile(input_file), "\n")

call assert_equal(expected, actual)
func! Test_add_tags() abort
try
let l:tmp = gotest#load_fixture('tags/add_all_input.go')
silent call go#tags#run(0, 0, 40, "add", bufname(''), 1)
call gotest#assert_fixture('tags/add_all_golden.go')
finally
call delete(l:tmp, 'rf')
endtry
endfunc


func Test_remove_tags()
let input_file = tempname()
call writefile(readfile("test-fixtures/tags/remove_all_input.go"), input_file)

let expected = join(readfile("test-fixtures/tags/remove_all_golden.go"), "\n")

" run for offset 40, which is inside the struct
silent call go#tags#run(0, 0, 40, "remove", input_file, 1)

let actual = join(readfile(input_file), "\n")

call assert_equal(expected, actual)
func! Test_remove_tags() abort
try
let l:tmp = gotest#load_fixture('tags/remove_all_input.go')
silent call go#tags#run(0, 0, 40, "remove", bufname(''), 1)
call gotest#assert_fixture('tags/remove_all_golden.go')
finally
call delete(l:tmp, 'rf')
endtry
endfunc

" vim: sw=2 ts=2 et
" vim:ts=2:sts=2:sw=2:et
33 changes: 33 additions & 0 deletions autoload/go/util.vim
Original file line number Diff line number Diff line change
Expand Up @@ -362,4 +362,37 @@ function! go#util#archive()
return expand("%:p:gs!\\!/!") . "\n" . strlen(l:buffer) . "\n" . l:buffer
endfunction


" Make a named temporary directory which starts with "prefix".
"
" Unfortunately Vim's tempname() is not portable enough across various systems;
" see: https://github.com/mattn/vim-go/pull/3#discussion_r138084911
function! go#util#tempdir(prefix) abort
" See :help tempfile
if go#util#IsWin()
let l:dirs = [$TMP, $TEMP, 'c:\tmp', 'c:\temp']
else
let l:dirs = [$TMPDIR, '/tmp', './', $HOME]
endif

let l:dir = ''
for l:d in dirs
if !empty(l:d) && filewritable(l:d) == 2
let l:dir = l:d
break
endif
endfor

if l:dir == ''
echoerr 'Unable to find directory to store temporary directory in'
return
endif

" Not great randomness, but "good enough" for our purpose here.
let l:rnd = sha256(printf('%s%s', localtime(), fnamemodify(bufname(''), ":p")))
let l:tmp = printf("%s/%s%s", l:dir, a:prefix, l:rnd)
call mkdir(l:tmp, 'p', 0700)
return l:tmp
endfunction

" vim: sw=2 ts=2 et
104 changes: 104 additions & 0 deletions autoload/gotest.vim
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
" Write a Go file to a temporary directory and append this directory to $GOPATH.
"
" The file will written to a:path, which is relative to the temporary directory,
" and this file will be loaded as the current buffer.
"
" The cursor will be placed on the character before any 0x1f byte.
"
" The full path to the created directory is returned, it is the caller's
" responsibility to clean that up!
fun! gotest#write_file(path, contents) abort
let l:dir = go#util#tempdir("vim-go-test/testrun/")
let $GOPATH .= ':' . l:dir
let l:full_path = l:dir . '/src/' . a:path

call mkdir(fnamemodify(l:full_path, ':h'), 'p')
call writefile(a:contents, l:full_path)
exe 'cd ' . l:dir . '/src'
silent exe 'e ' . a:path

" Set cursor.
let l:lnum = 1
for l:line in a:contents
let l:m = match(l:line, '')
if l:m > -1
call setpos('.', [0, l:lnum, l:m, 0])
call setline('.', substitute(getline('.'), '', '', ''))
break
endif

let l:lnum += 1
endfor

return l:dir
endfun

" Load a fixture file from test-fixtures.
"
" The file will be copied to a new GOPATH-compliant temporary directory and
" loaded as the current buffer.
fun! gotest#load_fixture(path) abort
let l:dir = go#util#tempdir("vim-go-test/testrun/")
let $GOPATH .= ':' . l:dir
let l:full_path = l:dir . '/src/' . a:path

call mkdir(fnamemodify(l:full_path, ':h'), 'p')
exe 'cd ' . l:dir . '/src'
silent exe 'noautocmd e ' . a:path
silent exe printf('read %s/test-fixtures/%s', g:vim_go_root, a:path)
silent noautocmd w!

return l:dir
endfun

" Diff the contents of the current buffer to a:want, which should be a list.
" If a:skipHeader is true we won't bother with the package and import
" declarations; so e.g.:
"
" let l:diff = s:diff_buffer(1, ['_ = mail.Address{}'])
"
" will pass, whereas otherwise you'd have to:
"
" let l:diff = s:diff_buffer(0, ['package main', 'import "net/mail", '_ = mail.Address{}'])
fun! gotest#assert_buffer(skipHeader, want) abort
let l:buffer = go#util#GetLines()

if a:skipHeader
for l:lnum in range(0, len(l:buffer) - 1)
" Bit rudimentary, but works reasonably well.
if match(l:buffer[l:lnum], '^\v(func|var|const|import \(|\))') > -1
let l:buffer = l:buffer[l:lnum:]
break
endif
endfor
endif

" Using ' is often easier so we don't have to escape ".
let l:want = map(a:want, 'substitute(v:val, "\\\\t", "\t", "")')

let l:tmp = go#util#tempdir('assert_buffer')
try
call writefile(l:buffer, l:tmp . '/have')
call writefile(l:want, l:tmp . '/want')
call go#fmt#run('gofmt', l:tmp . '/have', l:tmp . '/have')
call go#fmt#run('gofmt', l:tmp . '/want', l:tmp . '/want')
let [l:out, l:err] = go#util#Exec(["diff", "-u", l:tmp . '/have', l:tmp . '/want'])
finally
call delete(l:tmp . '/have')
call delete(l:tmp . '/want')
call delete(l:tmp, 'd')
endtry

if l:err || l:out != ''
let v:errors = extend(v:errors, split(l:out, "\n"))
endif
endfun

" Diff the contents of the current buffer to the fixture file in a:path.
fun! gotest#assert_fixture(path) abort
let l:want = readfile(printf('%s/test-fixtures/%s', g:vim_go_root, a:path))
call gotest#assert_buffer(0, l:want)
endfun


" vim: sw=2 ts=2 et
Loading

0 comments on commit ba6ce99

Please sign in to comment.