patch 7.4.944
Problem:    Writing tests for Vim script is hard.
Solution:   Add assertEqual(), assertFalse() and assertTrue() functions.  Add
            the v:errors variable.  Add the runtest script. Add a first new
            style test script.
diff --git a/src/testdir/Makefile b/src/testdir/Makefile
index dba031f..03384aa 100644
--- a/src/testdir/Makefile
+++ b/src/testdir/Makefile
@@ -68,15 +68,17 @@
 		test_utf8.out \
 		test_writefile.out
 
+NEW_TESTS = test_assert.res
+
 SCRIPTS_GUI = test16.out
 
 SCRIPTS_BENCH = bench_re_freeze.out
 
-.SUFFIXES: .in .out
+.SUFFIXES: .in .out .res .vim
 
-nongui:	nolog $(SCRIPTS) report
+nongui:	nolog $(SCRIPTS) newtests report
 
-gui:	nolog $(SCRIPTS) $(SCRIPTS_GUI) report
+gui:	nolog $(SCRIPTS) $(SCRIPTS_GUI) newtests report
 
 benchmark: $(SCRIPTS_BENCH)
 
@@ -95,7 +97,7 @@
 RUN_VIM = VIMRUNTIME=$(SCRIPTSOURCE); export VIMRUNTIME; $(VALGRIND) $(VIMPROG) -u unix.vim -U NONE --noplugin -s dotest.in
 
 clean:
-	-rm -rf *.out *.failed *.rej *.orig test.log $(RM_ON_RUN) $(RM_ON_START) valgrind.*
+	-rm -rf *.out *.failed *.res *.rej *.orig test.log $(RM_ON_RUN) $(RM_ON_START) valgrind.*
 
 test1.out: test1.in
 	-rm -rf $*.failed $(RM_ON_RUN) $(RM_ON_START) wrongtermsize
@@ -157,3 +159,14 @@
 
 nolog:
 	-rm -f test.log
+
+
+# New style of tests uses Vim script with assert calls.  These are easier
+# to write and a lot easier to read and debug.
+# Limitation: Only works with the +eval feature.
+RUN_VIMTEST = VIMRUNTIME=$(SCRIPTSOURCE); export VIMRUNTIME; $(VALGRIND) $(VIMPROG) -u unix.vim -U NONE --noplugin
+
+newtests: $(NEW_TESTS)
+
+.vim.res:
+	$(RUN_VIMTEST) -u runtest.vim $*.vim
diff --git a/src/testdir/runtest.vim b/src/testdir/runtest.vim
new file mode 100644
index 0000000..8589bf6
--- /dev/null
+++ b/src/testdir/runtest.vim
@@ -0,0 +1,97 @@
+" This script is sourced while editing the .vim file with the tests.
+" When the script is successful the .res file will be created.
+" Errors are appended to the test.log file.
+"
+" The test script may contain anything, only functions that start with
+" "Test_" are special.  These will be invoked and should contain assert
+" functions.  See test_assert.vim for an example.
+"
+" It is possible to source other files that contain "Test_" functions.  This
+" can speed up testing, since Vim does not need to restart.  But be careful
+" that the tests do not interfere with each other.
+"
+" If an error cannot be detected properly with an assert function add the
+" error to the v:errors list:
+"   call add(v:errors, 'test foo failed: Cannot find xyz')
+"
+" If preparation for each Test_ function is needed, define a SetUp function.
+" It will be called before each Test_ function.
+"
+" If cleanup after each Test_ function is needed, define a TearDown function.
+" It will be called after each Test_ function.
+
+" Without the +eval feature we can't run these tests, bail out.
+if 0
+  quit!
+endif
+
+" Check that the screen size is at least 24 x 80 characters.
+if &lines < 24 || &columns < 80 
+  let error = 'Screen size too small! Tests require at least 24 lines with 80 characters'
+  echoerr error
+  split test.log
+  $put =error
+  w
+  cquit
+endif
+
+" Source the test script.  First grab the file name, in case the script
+" navigates away.
+let testname = expand('%')
+source %
+
+" Locate Test_ functions and execute them.
+redir @q
+function /^Test_
+redir END
+let tests = split(substitute(@q, 'function \(\k*()\)', '\1', 'g'))
+
+let done = 0
+let fail = 0
+let errors = []
+for test in tests
+  if exists("*SetUp")
+    call SetUp()
+  endif
+
+  let done += 1
+  try
+    exe 'call ' . test
+  catch
+    let fail += 1
+    call add(v:errors, 'Caught exception in ' . test . ': ' . v:exception . ' @ ' . v:throwpoint)
+  endtry
+
+  if len(v:errors) > 0
+    let fail += 1
+    call add(errors, 'Found errors in ' . test . ':')
+    call extend(errors, v:errors)
+    let v:errors = []
+  endif
+
+  if exists("*TearDown")
+    call TearDown()
+  endif
+endfor
+
+if fail == 0
+  " Success, create the .res file so that make knows it's done.
+  split %:r.res
+  write
+endif
+
+if len(errors) > 0
+  " Append errors to test.log
+  split test.log
+  call append(line('$'), '')
+  call append(line('$'), 'From ' . testname . ':')
+  call append(line('$'), errors)
+  write
+endif
+
+echo 'Executed ' . done . (done > 1 ? ' tests': ' test')
+if fail > 0
+  echo fail . ' FAILED'
+endif
+
+qall!
diff --git a/src/testdir/test_assert.vim b/src/testdir/test_assert.vim
new file mode 100644
index 0000000..61c77c5
--- /dev/null
+++ b/src/testdir/test_assert.vim
@@ -0,0 +1,19 @@
+" Test that the methods used for testing work.
+
+func Test_assertFalse()
+  call assertFalse(0)
+endfunc
+
+func Test_assertTrue()
+  call assertTrue(1)
+  call assertTrue(123)
+endfunc
+
+func Test_assertEqual()
+  let s = 'foo'
+  call assertEqual('foo', s)
+  let n = 4
+  call assertEqual(4, n)
+  let l = [1, 2, 3]
+  call assertEqual([1, 2, 3], l)
+endfunc