[PATCH v2 1/5] Copy test framework from Git

Michal Sojka sojkam1 at fel.cvut.cz
Mon May 10 09:48:00 PDT 2010


Git uses a simple and yet powerful test framework, written in shell.
The framework is easy to use for both users and developers so I think
it would help if it is used in notmuch as well.

This is a copy of Git's test framework from commit
b6b0afdc30e066788592ca07c9a6c6936c68cc11 in git repository.

Signed-off-by: Michal Sojka <sojkam1 at fel.cvut.cz>
---
 test/Makefile             |   48 +++
 test/README               |  297 ++++++++++++++++
 test/aggregate-results.sh |   34 ++
 test/t0000-basic.sh       |  389 +++++++++++++++++++++
 test/test-lib.sh          |  832 +++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 1600 insertions(+), 0 deletions(-)
 create mode 100644 test/Makefile
 create mode 100644 test/README
 create mode 100755 test/aggregate-results.sh
 create mode 100755 test/t0000-basic.sh
 create mode 100644 test/test-lib.sh

diff --git a/test/Makefile b/test/Makefile
new file mode 100644
index 0000000..25c559b
--- /dev/null
+++ b/test/Makefile
@@ -0,0 +1,48 @@
+# Run tests
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+-include ../config.mak
+
+#GIT_TEST_OPTS=--verbose --debug
+SHELL_PATH ?= $(SHELL)
+TAR ?= $(TAR)
+RM ?= rm -f
+
+# Shell quote;
+SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
+
+T = $(wildcard t[0-9][0-9][0-9][0-9]-*.sh)
+TSVN = $(wildcard t91[0-9][0-9]-*.sh)
+
+all: pre-clean
+	$(MAKE) aggregate-results-and-cleanup
+
+$(T):
+	@echo "*** $@ ***"; GIT_CONFIG=.git/config '$(SHELL_PATH_SQ)' $@ $(GIT_TEST_OPTS)
+
+pre-clean:
+	$(RM) -r test-results
+
+clean:
+	$(RM) -r 'trash directory'.* test-results
+	$(RM) t????/cvsroot/CVSROOT/?*
+	$(RM) -r valgrind/bin
+
+aggregate-results-and-cleanup: $(T)
+	$(MAKE) aggregate-results
+	$(MAKE) clean
+
+aggregate-results:
+	'$(SHELL_PATH_SQ)' ./aggregate-results.sh test-results/t*-*
+
+# we can test NO_OPTIMIZE_COMMITS independently of LC_ALL
+full-svn-test:
+	$(MAKE) $(TSVN) GIT_SVN_NO_OPTIMIZE_COMMITS=1 LC_ALL=C
+	$(MAKE) $(TSVN) GIT_SVN_NO_OPTIMIZE_COMMITS=0 LC_ALL=en_US.UTF-8
+
+valgrind:
+	GIT_TEST_OPTS=--valgrind $(MAKE)
+
+.PHONY: pre-clean $(T) aggregate-results clean valgrind
diff --git a/test/README b/test/README
new file mode 100644
index 0000000..dcd3ebb
--- /dev/null
+++ b/test/README
@@ -0,0 +1,297 @@
+Core GIT Tests
+==============
+
+This directory holds many test scripts for core GIT tools.  The
+first part of this short document describes how to run the tests
+and read their output.
+
+When fixing the tools or adding enhancements, you are strongly
+encouraged to add tests in this directory to cover what you are
+trying to fix or enhance.  The later part of this short document
+describes how your test scripts should be organized.
+
+
+Running Tests
+-------------
+
+The easiest way to run tests is to say "make".  This runs all
+the tests.
+
+    *** t0000-basic.sh ***
+    *   ok 1: .git/objects should be empty after git-init in an empty repo.
+    *   ok 2: .git/objects should have 256 subdirectories.
+    *   ok 3: git-update-index without --add should fail adding.
+    ...
+    *   ok 23: no diff after checkout and git-update-index --refresh.
+    * passed all 23 test(s)
+    *** t0100-environment-names.sh ***
+    *   ok 1: using old names should issue warnings.
+    *   ok 2: using old names but having new names should not issue warnings.
+    ...
+
+Or you can run each test individually from command line, like
+this:
+
+    $ sh ./t3001-ls-files-killed.sh
+    *   ok 1: git-update-index --add to add various paths.
+    *   ok 2: git-ls-files -k to show killed files.
+    *   ok 3: validate git-ls-files -k output.
+    * passed all 3 test(s)
+
+You can pass --verbose (or -v), --debug (or -d), and --immediate
+(or -i) command line argument to the test, or by setting GIT_TEST_OPTS
+appropriately before running "make".
+
+--verbose::
+	This makes the test more verbose.  Specifically, the
+	command being run and their output if any are also
+	output.
+
+--debug::
+	This may help the person who is developing a new test.
+	It causes the command defined with test_debug to run.
+
+--immediate::
+	This causes the test to immediately exit upon the first
+	failed test.
+
+--long-tests::
+	This causes additional long-running tests to be run (where
+	available), for more exhaustive testing.
+
+--valgrind::
+	Execute all Git binaries with valgrind and exit with status
+	126 on errors (just like regular tests, this will only stop
+	the test script when running under -i).  Valgrind errors
+	go to stderr, so you might want to pass the -v option, too.
+
+	Since it makes no sense to run the tests with --valgrind and
+	not see any output, this option implies --verbose.  For
+	convenience, it also implies --tee.
+
+--tee::
+	In addition to printing the test output to the terminal,
+	write it to files named 't/test-results/$TEST_NAME.out'.
+	As the names depend on the tests' file names, it is safe to
+	run the tests with this option in parallel.
+
+--with-dashes::
+	By default tests are run without dashed forms of
+	commands (like git-commit) in the PATH (it only uses
+	wrappers from ../bin-wrappers).  Use this option to include
+	the build directory (..) in the PATH, which contains all
+	the dashed forms of commands.  This option is currently
+	implied by other options like --valgrind and
+	GIT_TEST_INSTALLED.
+
+You can also set the GIT_TEST_INSTALLED environment variable to
+the bindir of an existing git installation to test that installation.
+You still need to have built this git sandbox, from which various
+test-* support programs, templates, and perl libraries are used.
+If your installed git is incomplete, it will silently test parts of
+your built version instead.
+
+When using GIT_TEST_INSTALLED, you can also set GIT_TEST_EXEC_PATH to
+override the location of the dashed-form subcommands (what
+GIT_EXEC_PATH would be used for during normal operation).
+GIT_TEST_EXEC_PATH defaults to `$GIT_TEST_INSTALLED/git --exec-path`.
+
+
+Skipping Tests
+--------------
+
+In some environments, certain tests have no way of succeeding
+due to platform limitation, such as lack of 'unzip' program, or
+filesystem that do not allow arbitrary sequence of non-NUL bytes
+as pathnames.
+
+You should be able to say something like
+
+    $ GIT_SKIP_TESTS=t9200.8 sh ./t9200-git-cvsexport-commit.sh
+
+and even:
+
+    $ GIT_SKIP_TESTS='t[0-4]??? t91?? t9200.8' make
+
+to omit such tests.  The value of the environment variable is a
+SP separated list of patterns that tells which tests to skip,
+and either can match the "t[0-9]{4}" part to skip the whole
+test, or t[0-9]{4} followed by ".$number" to say which
+particular test to skip.
+
+Note that some tests in the existing test suite rely on previous
+test item, so you cannot arbitrarily disable one and expect the
+remainder of test to check what the test originally was intended
+to check.
+
+
+Naming Tests
+------------
+
+The test files are named as:
+
+	tNNNN-commandname-details.sh
+
+where N is a decimal digit.
+
+First digit tells the family:
+
+	0 - the absolute basics and global stuff
+	1 - the basic commands concerning database
+	2 - the basic commands concerning the working tree
+	3 - the other basic commands (e.g. ls-files)
+	4 - the diff commands
+	5 - the pull and exporting commands
+	6 - the revision tree commands (even e.g. merge-base)
+	7 - the porcelainish commands concerning the working tree
+	8 - the porcelainish commands concerning forensics
+	9 - the git tools
+
+Second digit tells the particular command we are testing.
+
+Third digit (optionally) tells the particular switch or group of switches
+we are testing.
+
+If you create files under t/ directory (i.e. here) that is not
+the top-level test script, never name the file to match the above
+pattern.  The Makefile here considers all such files as the
+top-level test script and tries to run all of them.  A care is
+especially needed if you are creating a common test library
+file, similar to test-lib.sh, because such a library file may
+not be suitable for standalone execution.
+
+
+Writing Tests
+-------------
+
+The test script is written as a shell script.  It should start
+with the standard "#!/bin/sh" with copyright notices, and an
+assignment to variable 'test_description', like this:
+
+	#!/bin/sh
+	#
+	# Copyright (c) 2005 Junio C Hamano
+	#
+
+	test_description='xxx test (option --frotz)
+
+	This test registers the following structure in the cache
+	and tries to run git-ls-files with option --frotz.'
+
+
+Source 'test-lib.sh'
+--------------------
+
+After assigning test_description, the test script should source
+test-lib.sh like this:
+
+	. ./test-lib.sh
+
+This test harness library does the following things:
+
+ - If the script is invoked with command line argument --help
+   (or -h), it shows the test_description and exits.
+
+ - Creates an empty test directory with an empty .git/objects
+   database and chdir(2) into it.  This directory is 't/trash directory'
+   if you must know, but I do not think you care.
+
+ - Defines standard test helper functions for your scripts to
+   use.  These functions are designed to make all scripts behave
+   consistently when command line arguments --verbose (or -v),
+   --debug (or -d), and --immediate (or -i) is given.
+
+
+End with test_done
+------------------
+
+Your script will be a sequence of tests, using helper functions
+from the test harness library.  At the end of the script, call
+'test_done'.
+
+
+Test harness library
+--------------------
+
+There are a handful helper functions defined in the test harness
+library for your script to use.
+
+ - test_expect_success <message> <script>
+
+   This takes two strings as parameter, and evaluates the
+   <script>.  If it yields success, test is considered
+   successful.  <message> should state what it is testing.
+
+   Example:
+
+	test_expect_success \
+	    'git-write-tree should be able to write an empty tree.' \
+	    'tree=$(git-write-tree)'
+
+ - test_expect_failure <message> <script>
+
+   This is NOT the opposite of test_expect_success, but is used
+   to mark a test that demonstrates a known breakage.  Unlike
+   the usual test_expect_success tests, which say "ok" on
+   success and "FAIL" on failure, this will say "FIXED" on
+   success and "still broken" on failure.  Failures from these
+   tests won't cause -i (immediate) to stop.
+
+ - test_debug <script>
+
+   This takes a single argument, <script>, and evaluates it only
+   when the test script is started with --debug command line
+   argument.  This is primarily meant for use during the
+   development of a new test script.
+
+ - test_done
+
+   Your test script must have test_done at the end.  Its purpose
+   is to summarize successes and failures in the test script and
+   exit with an appropriate error code.
+
+ - test_tick
+
+   Make commit and tag names consistent by setting the author and
+   committer times to defined stated.  Subsequent calls will
+   advance the times by a fixed amount.
+
+ - test_commit <message> [<filename> [<contents>]]
+
+   Creates a commit with the given message, committing the given
+   file with the given contents (default for both is to reuse the
+   message string), and adds a tag (again reusing the message
+   string as name).  Calls test_tick to make the SHA-1s
+   reproducible.
+
+ - test_merge <message> <commit-or-tag>
+
+   Merges the given rev using the given message.  Like test_commit,
+   creates a tag and calls test_tick before committing.
+
+Tips for Writing Tests
+----------------------
+
+As with any programming projects, existing programs are the best
+source of the information.  However, do _not_ emulate
+t0000-basic.sh when writing your tests.  The test is special in
+that it tries to validate the very core of GIT.  For example, it
+knows that there will be 256 subdirectories under .git/objects/,
+and it knows that the object ID of an empty tree is a certain
+40-byte string.  This is deliberately done so in t0000-basic.sh
+because the things the very basic core test tries to achieve is
+to serve as a basis for people who are changing the GIT internal
+drastically.  For these people, after making certain changes,
+not seeing failures from the basic test _is_ a failure.  And
+such drastic changes to the core GIT that even changes these
+otherwise supposedly stable object IDs should be accompanied by
+an update to t0000-basic.sh.
+
+However, other tests that simply rely on basic parts of the core
+GIT working properly should not have that level of intimate
+knowledge of the core GIT internals.  If all the test scripts
+hardcoded the object IDs like t0000-basic.sh does, that defeats
+the purpose of t0000-basic.sh, which is to isolate that level of
+validation in one place.  Your test also ends up needing
+updating when such a change to the internal happens, so do _not_
+do it and leave the low level of validation to t0000-basic.sh.
diff --git a/test/aggregate-results.sh b/test/aggregate-results.sh
new file mode 100755
index 0000000..d5bab75
--- /dev/null
+++ b/test/aggregate-results.sh
@@ -0,0 +1,34 @@
+#!/bin/sh
+
+fixed=0
+success=0
+failed=0
+broken=0
+total=0
+
+for file
+do
+	while read type value
+	do
+		case $type in
+		'')
+			continue ;;
+		fixed)
+			fixed=$(($fixed + $value)) ;;
+		success)
+			success=$(($success + $value)) ;;
+		failed)
+			failed=$(($failed + $value)) ;;
+		broken)
+			broken=$(($broken + $value)) ;;
+		total)
+			total=$(($total + $value)) ;;
+		esac
+	done <"$file"
+done
+
+printf "%-8s%d\n" fixed $fixed
+printf "%-8s%d\n" success $success
+printf "%-8s%d\n" failed $failed
+printf "%-8s%d\n" broken $broken
+printf "%-8s%d\n" total $total
diff --git a/test/t0000-basic.sh b/test/t0000-basic.sh
new file mode 100755
index 0000000..3ec9cbe
--- /dev/null
+++ b/test/t0000-basic.sh
@@ -0,0 +1,389 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='Test the very basics part #1.
+
+The rest of the test suite does not check the basic operation of git
+plumbing commands to work very carefully.  Their job is to concentrate
+on tricky features that caused bugs in the past to detect regression.
+
+This test runs very basic features, like registering things in cache,
+writing tree, etc.
+
+Note that this test *deliberately* hard-codes many expected object
+IDs.  When object ID computation changes, like in the previous case of
+swapping compression and hashing order, the person who is making the
+modification *should* take notice and update the test vectors here.
+'
+
+################################################################
+# It appears that people try to run tests without building...
+
+../git >/dev/null
+if test $? != 1
+then
+	echo >&2 'You do not seem to have built git yet.'
+	exit 1
+fi
+
+. ./test-lib.sh
+
+################################################################
+# git init has been done in an empty repository.
+# make sure it is empty.
+
+find .git/objects -type f -print >should-be-empty
+test_expect_success \
+    '.git/objects should be empty after git init in an empty repo.' \
+    'cmp -s /dev/null should-be-empty'
+
+# also it should have 2 subdirectories; no fan-out anymore, pack, and info.
+# 3 is counting "objects" itself
+find .git/objects -type d -print >full-of-directories
+test_expect_success \
+    '.git/objects should have 3 subdirectories.' \
+    'test $(wc -l < full-of-directories) = 3'
+
+################################################################
+# Test harness
+test_expect_success 'success is reported like this' '
+    :
+'
+test_expect_failure 'pretend we have a known breakage' '
+    false
+'
+test_expect_failure 'pretend we have fixed a known breakage' '
+    :
+'
+test_set_prereq HAVEIT
+haveit=no
+test_expect_success HAVEIT 'test runs if prerequisite is satisfied' '
+    test_have_prereq HAVEIT &&
+    haveit=yes
+'
+donthaveit=yes
+test_expect_success DONTHAVEIT 'unmet prerequisite causes test to be skipped' '
+    donthaveit=no
+'
+if test $haveit$donthaveit != yesyes
+then
+	say "bug in test framework: prerequisite tags do not work reliably"
+	exit 1
+fi
+
+clean=no
+test_expect_success 'tests clean up after themselves' '
+    test_when_finished clean=yes
+'
+
+cleaner=no
+test_expect_code 1 'tests clean up even after a failure' '
+    test_when_finished cleaner=yes &&
+    (exit 1)
+'
+
+if test $clean$cleaner != yesyes
+then
+	say "bug in test framework: cleanup commands do not work reliably"
+	exit 1
+fi
+
+test_expect_code 2 'failure to clean up causes the test to fail' '
+    test_when_finished "(exit 2)"
+'
+
+################################################################
+# Basics of the basics
+
+# updating a new file without --add should fail.
+test_expect_success 'git update-index without --add should fail adding.' '
+    test_must_fail git update-index should-be-empty
+'
+
+# and with --add it should succeed, even if it is empty (it used to fail).
+test_expect_success \
+    'git update-index with --add should succeed.' \
+    'git update-index --add should-be-empty'
+
+test_expect_success \
+    'writing tree out with git write-tree' \
+    'tree=$(git write-tree)'
+
+# we know the shape and contents of the tree and know the object ID for it.
+test_expect_success \
+    'validate object ID of a known tree.' \
+    'test "$tree" = 7bb943559a305bdd6bdee2cef6e5df2413c3d30a'
+
+# Removing paths.
+rm -f should-be-empty full-of-directories
+test_expect_success 'git update-index without --remove should fail removing.' '
+    test_must_fail git update-index should-be-empty
+'
+
+test_expect_success \
+    'git update-index with --remove should be able to remove.' \
+    'git update-index --remove should-be-empty'
+
+# Empty tree can be written with recent write-tree.
+test_expect_success \
+    'git write-tree should be able to write an empty tree.' \
+    'tree=$(git write-tree)'
+
+test_expect_success \
+    'validate object ID of a known tree.' \
+    'test "$tree" = 4b825dc642cb6eb9a060e54bf8d69288fbee4904'
+
+# Various types of objects
+# Some filesystems do not support symblic links; on such systems
+# some expected values are different
+mkdir path2 path3 path3/subp3
+paths='path0 path2/file2 path3/file3 path3/subp3/file3'
+for p in $paths
+do
+    echo "hello $p" >$p
+done
+if test_have_prereq SYMLINKS
+then
+	for p in $paths
+	do
+		ln -s "hello $p" ${p}sym
+	done
+	expectfilter=cat
+	expectedtree=087704a96baf1c2d1c869a8b084481e121c88b5b
+	expectedptree1=21ae8269cacbe57ae09138dcc3a2887f904d02b3
+	expectedptree2=3c5e5399f3a333eddecce7a9b9465b63f65f51e2
+else
+	expectfilter='grep -v sym'
+	expectedtree=8e18edf7d7edcf4371a3ac6ae5f07c2641db7c46
+	expectedptree1=cfb8591b2f65de8b8cc1020cd7d9e67e7793b325
+	expectedptree2=ce580448f0148b985a513b693fdf7d802cacb44f
+fi
+
+test_expect_success \
+    'adding various types of objects with git update-index --add.' \
+    'find path* ! -type d -print | xargs git update-index --add'
+
+# Show them and see that matches what we expect.
+test_expect_success \
+    'showing stage with git ls-files --stage' \
+    'git ls-files --stage >current'
+
+$expectfilter >expected <<\EOF
+100644 f87290f8eb2cbbea7857214459a0739927eab154 0	path0
+120000 15a98433ae33114b085f3eb3bb03b832b3180a01 0	path0sym
+100644 3feff949ed00a62d9f7af97c15cd8a30595e7ac7 0	path2/file2
+120000 d8ce161addc5173867a3c3c730924388daedbc38 0	path2/file2sym
+100644 0aa34cae68d0878578ad119c86ca2b5ed5b28376 0	path3/file3
+120000 8599103969b43aff7e430efea79ca4636466794f 0	path3/file3sym
+100644 00fb5908cb97c2564a9783c0c64087333b3b464f 0	path3/subp3/file3
+120000 6649a1ebe9e9f1c553b66f5a6e74136a07ccc57c 0	path3/subp3/file3sym
+EOF
+test_expect_success \
+    'validate git ls-files output for a known tree.' \
+    'test_cmp expected current'
+
+test_expect_success \
+    'writing tree out with git write-tree.' \
+    'tree=$(git write-tree)'
+test_expect_success \
+    'validate object ID for a known tree.' \
+    'test "$tree" = "$expectedtree"'
+
+test_expect_success \
+    'showing tree with git ls-tree' \
+    'git ls-tree $tree >current'
+cat >expected <<\EOF
+100644 blob f87290f8eb2cbbea7857214459a0739927eab154	path0
+120000 blob 15a98433ae33114b085f3eb3bb03b832b3180a01	path0sym
+040000 tree 58a09c23e2ca152193f2786e06986b7b6712bdbe	path2
+040000 tree 21ae8269cacbe57ae09138dcc3a2887f904d02b3	path3
+EOF
+test_expect_success SYMLINKS \
+    'git ls-tree output for a known tree.' \
+    'test_cmp expected current'
+
+# This changed in ls-tree pathspec change -- recursive does
+# not show tree nodes anymore.
+test_expect_success \
+    'showing tree with git ls-tree -r' \
+    'git ls-tree -r $tree >current'
+$expectfilter >expected <<\EOF
+100644 blob f87290f8eb2cbbea7857214459a0739927eab154	path0
+120000 blob 15a98433ae33114b085f3eb3bb03b832b3180a01	path0sym
+100644 blob 3feff949ed00a62d9f7af97c15cd8a30595e7ac7	path2/file2
+120000 blob d8ce161addc5173867a3c3c730924388daedbc38	path2/file2sym
+100644 blob 0aa34cae68d0878578ad119c86ca2b5ed5b28376	path3/file3
+120000 blob 8599103969b43aff7e430efea79ca4636466794f	path3/file3sym
+100644 blob 00fb5908cb97c2564a9783c0c64087333b3b464f	path3/subp3/file3
+120000 blob 6649a1ebe9e9f1c553b66f5a6e74136a07ccc57c	path3/subp3/file3sym
+EOF
+test_expect_success \
+    'git ls-tree -r output for a known tree.' \
+    'test_cmp expected current'
+
+# But with -r -t we can have both.
+test_expect_success \
+    'showing tree with git ls-tree -r -t' \
+    'git ls-tree -r -t $tree >current'
+cat >expected <<\EOF
+100644 blob f87290f8eb2cbbea7857214459a0739927eab154	path0
+120000 blob 15a98433ae33114b085f3eb3bb03b832b3180a01	path0sym
+040000 tree 58a09c23e2ca152193f2786e06986b7b6712bdbe	path2
+100644 blob 3feff949ed00a62d9f7af97c15cd8a30595e7ac7	path2/file2
+120000 blob d8ce161addc5173867a3c3c730924388daedbc38	path2/file2sym
+040000 tree 21ae8269cacbe57ae09138dcc3a2887f904d02b3	path3
+100644 blob 0aa34cae68d0878578ad119c86ca2b5ed5b28376	path3/file3
+120000 blob 8599103969b43aff7e430efea79ca4636466794f	path3/file3sym
+040000 tree 3c5e5399f3a333eddecce7a9b9465b63f65f51e2	path3/subp3
+100644 blob 00fb5908cb97c2564a9783c0c64087333b3b464f	path3/subp3/file3
+120000 blob 6649a1ebe9e9f1c553b66f5a6e74136a07ccc57c	path3/subp3/file3sym
+EOF
+test_expect_success SYMLINKS \
+    'git ls-tree -r output for a known tree.' \
+    'test_cmp expected current'
+
+test_expect_success \
+    'writing partial tree out with git write-tree --prefix.' \
+    'ptree=$(git write-tree --prefix=path3)'
+test_expect_success \
+    'validate object ID for a known tree.' \
+    'test "$ptree" = "$expectedptree1"'
+
+test_expect_success \
+    'writing partial tree out with git write-tree --prefix.' \
+    'ptree=$(git write-tree --prefix=path3/subp3)'
+test_expect_success \
+    'validate object ID for a known tree.' \
+    'test "$ptree" = "$expectedptree2"'
+
+cat >badobjects <<EOF
+100644 blob 1000000000000000000000000000000000000000	dir/file1
+100644 blob 2000000000000000000000000000000000000000	dir/file2
+100644 blob 3000000000000000000000000000000000000000	dir/file3
+100644 blob 4000000000000000000000000000000000000000	dir/file4
+100644 blob 5000000000000000000000000000000000000000	dir/file5
+EOF
+
+rm .git/index
+test_expect_success \
+    'put invalid objects into the index.' \
+    'git update-index --index-info < badobjects'
+
+test_expect_success 'writing this tree without --missing-ok.' '
+    test_must_fail git write-tree
+'
+
+test_expect_success \
+    'writing this tree with --missing-ok.' \
+    'git write-tree --missing-ok'
+
+
+################################################################
+rm .git/index
+test_expect_success \
+    'git read-tree followed by write-tree should be idempotent.' \
+    'git read-tree $tree &&
+     test -f .git/index &&
+     newtree=$(git write-tree) &&
+     test "$newtree" = "$tree"'
+
+$expectfilter >expected <<\EOF
+:100644 100644 f87290f8eb2cbbea7857214459a0739927eab154 0000000000000000000000000000000000000000 M	path0
+:120000 120000 15a98433ae33114b085f3eb3bb03b832b3180a01 0000000000000000000000000000000000000000 M	path0sym
+:100644 100644 3feff949ed00a62d9f7af97c15cd8a30595e7ac7 0000000000000000000000000000000000000000 M	path2/file2
+:120000 120000 d8ce161addc5173867a3c3c730924388daedbc38 0000000000000000000000000000000000000000 M	path2/file2sym
+:100644 100644 0aa34cae68d0878578ad119c86ca2b5ed5b28376 0000000000000000000000000000000000000000 M	path3/file3
+:120000 120000 8599103969b43aff7e430efea79ca4636466794f 0000000000000000000000000000000000000000 M	path3/file3sym
+:100644 100644 00fb5908cb97c2564a9783c0c64087333b3b464f 0000000000000000000000000000000000000000 M	path3/subp3/file3
+:120000 120000 6649a1ebe9e9f1c553b66f5a6e74136a07ccc57c 0000000000000000000000000000000000000000 M	path3/subp3/file3sym
+EOF
+test_expect_success \
+    'validate git diff-files output for a know cache/work tree state.' \
+    'git diff-files >current && diff >/dev/null -b current expected'
+
+test_expect_success \
+    'git update-index --refresh should succeed.' \
+    'git update-index --refresh'
+
+test_expect_success \
+    'no diff after checkout and git update-index --refresh.' \
+    'git diff-files >current && cmp -s current /dev/null'
+
+################################################################
+P=$expectedtree
+test_expect_success \
+    'git commit-tree records the correct tree in a commit.' \
+    'commit0=$(echo NO | git commit-tree $P) &&
+     tree=$(git show --pretty=raw $commit0 |
+	 sed -n -e "s/^tree //p" -e "/^author /q") &&
+     test "z$tree" = "z$P"'
+
+test_expect_success \
+    'git commit-tree records the correct parent in a commit.' \
+    'commit1=$(echo NO | git commit-tree $P -p $commit0) &&
+     parent=$(git show --pretty=raw $commit1 |
+	 sed -n -e "s/^parent //p" -e "/^author /q") &&
+     test "z$commit0" = "z$parent"'
+
+test_expect_success \
+    'git commit-tree omits duplicated parent in a commit.' \
+    'commit2=$(echo NO | git commit-tree $P -p $commit0 -p $commit0) &&
+     parent=$(git show --pretty=raw $commit2 |
+	 sed -n -e "s/^parent //p" -e "/^author /q" |
+	 sort -u) &&
+     test "z$commit0" = "z$parent" &&
+     numparent=$(git show --pretty=raw $commit2 |
+	 sed -n -e "s/^parent //p" -e "/^author /q" |
+	 wc -l) &&
+     test $numparent = 1'
+
+test_expect_success 'update-index D/F conflict' '
+	mv path0 tmp &&
+	mv path2 path0 &&
+	mv tmp path2 &&
+	git update-index --add --replace path2 path0/file2 &&
+	numpath0=$(git ls-files path0 | wc -l) &&
+	test $numpath0 = 1
+'
+
+test_expect_success SYMLINKS 'absolute path works as expected' '
+	mkdir first &&
+	ln -s ../.git first/.git &&
+	mkdir second &&
+	ln -s ../first second/other &&
+	mkdir third &&
+	dir="$(cd .git; pwd -P)" &&
+	dir2=third/../second/other/.git &&
+	test "$dir" = "$(test-path-utils make_absolute_path $dir2)" &&
+	file="$dir"/index &&
+	test "$file" = "$(test-path-utils make_absolute_path $dir2/index)" &&
+	basename=blub &&
+	test "$dir/$basename" = "$(cd .git && test-path-utils make_absolute_path "$basename")" &&
+	ln -s ../first/file .git/syml &&
+	sym="$(cd first; pwd -P)"/file &&
+	test "$sym" = "$(test-path-utils make_absolute_path "$dir2/syml")"
+'
+
+test_expect_success 'very long name in the index handled sanely' '
+
+	a=a && # 1
+	a=$a$a$a$a$a$a$a$a$a$a$a$a$a$a$a$a && # 16
+	a=$a$a$a$a$a$a$a$a$a$a$a$a$a$a$a$a && # 256
+	a=$a$a$a$a$a$a$a$a$a$a$a$a$a$a$a$a && # 4096
+	a=${a}q &&
+
+	>path4 &&
+	git update-index --add path4 &&
+	(
+		git ls-files -s path4 |
+		sed -e "s/	.*/	/" |
+		tr -d "\012"
+		echo "$a"
+	) | git update-index --index-info &&
+	len=$(git ls-files "a*" | wc -c) &&
+	test $len = 4098
+'
+
+test_done
diff --git a/test/test-lib.sh b/test/test-lib.sh
new file mode 100644
index 0000000..7422bba
--- /dev/null
+++ b/test/test-lib.sh
@@ -0,0 +1,832 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see http://www.gnu.org/licenses/ .
+
+# if --tee was passed, write the output not only to the terminal, but
+# additionally to the file test-results/$BASENAME.out, too.
+case "$GIT_TEST_TEE_STARTED, $* " in
+done,*)
+	# do not redirect again
+	;;
+*' --tee '*|*' --va'*)
+	mkdir -p test-results
+	BASE=test-results/$(basename "$0" .sh)
+	(GIT_TEST_TEE_STARTED=done ${SHELL-sh} "$0" "$@" 2>&1;
+	 echo $? > $BASE.exit) | tee $BASE.out
+	test "$(cat $BASE.exit)" = 0
+	exit
+	;;
+esac
+
+# Keep the original TERM for say_color
+ORIGINAL_TERM=$TERM
+
+# For repeatability, reset the environment to known value.
+LANG=C
+LC_ALL=C
+PAGER=cat
+TZ=UTC
+TERM=dumb
+export LANG LC_ALL PAGER TERM TZ
+EDITOR=:
+unset VISUAL
+unset GIT_EDITOR
+unset AUTHOR_DATE
+unset AUTHOR_EMAIL
+unset AUTHOR_NAME
+unset COMMIT_AUTHOR_EMAIL
+unset COMMIT_AUTHOR_NAME
+unset EMAIL
+unset GIT_ALTERNATE_OBJECT_DIRECTORIES
+unset GIT_AUTHOR_DATE
+GIT_AUTHOR_EMAIL=author at example.com
+GIT_AUTHOR_NAME='A U Thor'
+unset GIT_COMMITTER_DATE
+GIT_COMMITTER_EMAIL=committer at example.com
+GIT_COMMITTER_NAME='C O Mitter'
+unset GIT_DIFF_OPTS
+unset GIT_DIR
+unset GIT_WORK_TREE
+unset GIT_EXTERNAL_DIFF
+unset GIT_INDEX_FILE
+unset GIT_OBJECT_DIRECTORY
+unset GIT_CEILING_DIRECTORIES
+unset SHA1_FILE_DIRECTORIES
+unset SHA1_FILE_DIRECTORY
+unset GIT_NOTES_REF
+unset GIT_NOTES_DISPLAY_REF
+unset GIT_NOTES_REWRITE_REF
+unset GIT_NOTES_REWRITE_MODE
+GIT_MERGE_VERBOSITY=5
+export GIT_MERGE_VERBOSITY
+export GIT_AUTHOR_EMAIL GIT_AUTHOR_NAME
+export GIT_COMMITTER_EMAIL GIT_COMMITTER_NAME
+export EDITOR
+GIT_TEST_CMP=${GIT_TEST_CMP:-diff -u}
+
+# Protect ourselves from common misconfiguration to export
+# CDPATH into the environment
+unset CDPATH
+
+unset GREP_OPTIONS
+
+case $(echo $GIT_TRACE |tr "[A-Z]" "[a-z]") in
+	1|2|true)
+		echo "* warning: Some tests will not work if GIT_TRACE" \
+			"is set as to trace on STDERR ! *"
+		echo "* warning: Please set GIT_TRACE to something" \
+			"other than 1, 2 or true ! *"
+		;;
+esac
+
+# Convenience
+#
+# A regexp to match 5 and 40 hexdigits
+_x05='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
+_x40="$_x05$_x05$_x05$_x05$_x05$_x05$_x05$_x05"
+
+# Each test should start with something like this, after copyright notices:
+#
+# test_description='Description of this test...
+# This test checks if command xyzzy does the right thing...
+# '
+# . ./test-lib.sh
+[ "x$ORIGINAL_TERM" != "xdumb" ] && (
+		TERM=$ORIGINAL_TERM &&
+		export TERM &&
+		[ -t 1 ] &&
+		tput bold >/dev/null 2>&1 &&
+		tput setaf 1 >/dev/null 2>&1 &&
+		tput sgr0 >/dev/null 2>&1
+	) &&
+	color=t
+
+while test "$#" -ne 0
+do
+	case "$1" in
+	-d|--d|--de|--deb|--debu|--debug)
+		debug=t; shift ;;
+	-i|--i|--im|--imm|--imme|--immed|--immedi|--immedia|--immediat|--immediate)
+		immediate=t; shift ;;
+	-l|--l|--lo|--lon|--long|--long-|--long-t|--long-te|--long-tes|--long-test|--long-tests)
+		GIT_TEST_LONG=t; export GIT_TEST_LONG; shift ;;
+	-h|--h|--he|--hel|--help)
+		help=t; shift ;;
+	-v|--v|--ve|--ver|--verb|--verbo|--verbos|--verbose)
+		verbose=t; shift ;;
+	-q|--q|--qu|--qui|--quie|--quiet)
+		quiet=t; shift ;;
+	--with-dashes)
+		with_dashes=t; shift ;;
+	--no-color)
+		color=; shift ;;
+	--no-python)
+		# noop now...
+		shift ;;
+	--va|--val|--valg|--valgr|--valgri|--valgrin|--valgrind)
+		valgrind=t; verbose=t; shift ;;
+	--tee)
+		shift ;; # was handled already
+	--root=*)
+		root=$(expr "z$1" : 'z[^=]*=\(.*\)')
+		shift ;;
+	*)
+		echo "error: unknown test option '$1'" >&2; exit 1 ;;
+	esac
+done
+
+if test -n "$color"; then
+	say_color () {
+		(
+		TERM=$ORIGINAL_TERM
+		export TERM
+		case "$1" in
+			error) tput bold; tput setaf 1;; # bold red
+			skip)  tput bold; tput setaf 2;; # bold green
+			pass)  tput setaf 2;;            # green
+			info)  tput setaf 3;;            # brown
+			*) test -n "$quiet" && return;;
+		esac
+		shift
+		printf "* %s" "$*"
+		tput sgr0
+		echo
+		)
+	}
+else
+	say_color() {
+		test -z "$1" && test -n "$quiet" && return
+		shift
+		echo "* $*"
+	}
+fi
+
+error () {
+	say_color error "error: $*"
+	GIT_EXIT_OK=t
+	exit 1
+}
+
+say () {
+	say_color info "$*"
+}
+
+test "${test_description}" != "" ||
+error "Test script did not set test_description."
+
+if test "$help" = "t"
+then
+	echo "$test_description"
+	exit 0
+fi
+
+exec 5>&1
+if test "$verbose" = "t"
+then
+	exec 4>&2 3>&1
+else
+	exec 4>/dev/null 3>/dev/null
+fi
+
+test_failure=0
+test_count=0
+test_fixed=0
+test_broken=0
+test_success=0
+
+die () {
+	code=$?
+	if test -n "$GIT_EXIT_OK"
+	then
+		exit $code
+	else
+		echo >&5 "FATAL: Unexpected exit with code $code"
+		exit 1
+	fi
+}
+
+GIT_EXIT_OK=
+trap 'die' EXIT
+
+# The semantics of the editor variables are that of invoking
+# sh -c "$EDITOR \"$@\"" files ...
+#
+# If our trash directory contains shell metacharacters, they will be
+# interpreted if we just set $EDITOR directly, so do a little dance with
+# environment variables to work around this.
+#
+# In particular, quoting isn't enough, as the path may contain the same quote
+# that we're using.
+test_set_editor () {
+	FAKE_EDITOR="$1"
+	export FAKE_EDITOR
+	EDITOR='"$FAKE_EDITOR"'
+	export EDITOR
+}
+
+test_decode_color () {
+	sed	-e 's/.\[1m/<WHITE>/g' \
+		-e 's/.\[31m/<RED>/g' \
+		-e 's/.\[32m/<GREEN>/g' \
+		-e 's/.\[33m/<YELLOW>/g' \
+		-e 's/.\[34m/<BLUE>/g' \
+		-e 's/.\[35m/<MAGENTA>/g' \
+		-e 's/.\[36m/<CYAN>/g' \
+		-e 's/.\[m/<RESET>/g'
+}
+
+q_to_nul () {
+	perl -pe 'y/Q/\000/'
+}
+
+q_to_cr () {
+	tr Q '\015'
+}
+
+append_cr () {
+	sed -e 's/$/Q/' | tr Q '\015'
+}
+
+remove_cr () {
+	tr '\015' Q | sed -e 's/Q$//'
+}
+
+test_tick () {
+	if test -z "${test_tick+set}"
+	then
+		test_tick=1112911993
+	else
+		test_tick=$(($test_tick + 60))
+	fi
+	GIT_COMMITTER_DATE="$test_tick -0700"
+	GIT_AUTHOR_DATE="$test_tick -0700"
+	export GIT_COMMITTER_DATE GIT_AUTHOR_DATE
+}
+
+# Call test_commit with the arguments "<message> [<file> [<contents>]]"
+#
+# This will commit a file with the given contents and the given commit
+# message.  It will also add a tag with <message> as name.
+#
+# Both <file> and <contents> default to <message>.
+
+test_commit () {
+	file=${2:-"$1.t"}
+	echo "${3-$1}" > "$file" &&
+	git add "$file" &&
+	test_tick &&
+	git commit -m "$1" &&
+	git tag "$1"
+}
+
+# Call test_merge with the arguments "<message> <commit>", where <commit>
+# can be a tag pointing to the commit-to-merge.
+
+test_merge () {
+	test_tick &&
+	git merge -m "$1" "$2" &&
+	git tag "$1"
+}
+
+# This function helps systems where core.filemode=false is set.
+# Use it instead of plain 'chmod +x' to set or unset the executable bit
+# of a file in the working directory and add it to the index.
+
+test_chmod () {
+	chmod "$@" &&
+	git update-index --add "--chmod=$@"
+}
+
+# Use test_set_prereq to tell that a particular prerequisite is available.
+# The prerequisite can later be checked for in two ways:
+#
+# - Explicitly using test_have_prereq.
+#
+# - Implicitly by specifying the prerequisite tag in the calls to
+#   test_expect_{success,failure,code}.
+#
+# The single parameter is the prerequisite tag (a simple word, in all
+# capital letters by convention).
+
+test_set_prereq () {
+	satisfied="$satisfied$1 "
+}
+satisfied=" "
+
+test_have_prereq () {
+	case $satisfied in
+	*" $1 "*)
+		: yes, have it ;;
+	*)
+		! : nope ;;
+	esac
+}
+
+# You are not expected to call test_ok_ and test_failure_ directly, use
+# the text_expect_* functions instead.
+
+test_ok_ () {
+	test_success=$(($test_success + 1))
+	say_color "" "  ok $test_count: $@"
+}
+
+test_failure_ () {
+	test_failure=$(($test_failure + 1))
+	say_color error "FAIL $test_count: $1"
+	shift
+	echo "$@" | sed -e 's/^/	/'
+	test "$immediate" = "" || { GIT_EXIT_OK=t; exit 1; }
+}
+
+test_known_broken_ok_ () {
+	test_fixed=$(($test_fixed+1))
+	say_color "" "  FIXED $test_count: $@"
+}
+
+test_known_broken_failure_ () {
+	test_broken=$(($test_broken+1))
+	say_color skip "  still broken $test_count: $@"
+}
+
+test_debug () {
+	test "$debug" = "" || eval "$1"
+}
+
+test_run_ () {
+	test_cleanup=:
+	eval >&3 2>&4 "$1"
+	eval_ret=$?
+	eval >&3 2>&4 "$test_cleanup"
+	return 0
+}
+
+test_skip () {
+	test_count=$(($test_count+1))
+	to_skip=
+	for skp in $GIT_SKIP_TESTS
+	do
+		case $this_test.$test_count in
+		$skp)
+			to_skip=t
+		esac
+	done
+	if test -z "$to_skip" && test -n "$prereq" &&
+	   ! test_have_prereq "$prereq"
+	then
+		to_skip=t
+	fi
+	case "$to_skip" in
+	t)
+		say_color skip >&3 "skipping test: $@"
+		say_color skip "skip $test_count: $1"
+		: true
+		;;
+	*)
+		false
+		;;
+	esac
+}
+
+test_expect_failure () {
+	test "$#" = 3 && { prereq=$1; shift; } || prereq=
+	test "$#" = 2 ||
+	error "bug in the test script: not 2 or 3 parameters to test-expect-failure"
+	if ! test_skip "$@"
+	then
+		say >&3 "checking known breakage: $2"
+		test_run_ "$2"
+		if [ "$?" = 0 -a "$eval_ret" = 0 ]
+		then
+			test_known_broken_ok_ "$1"
+		else
+			test_known_broken_failure_ "$1"
+		fi
+	fi
+	echo >&3 ""
+}
+
+test_expect_success () {
+	test "$#" = 3 && { prereq=$1; shift; } || prereq=
+	test "$#" = 2 ||
+	error "bug in the test script: not 2 or 3 parameters to test-expect-success"
+	if ! test_skip "$@"
+	then
+		say >&3 "expecting success: $2"
+		test_run_ "$2"
+		if [ "$?" = 0 -a "$eval_ret" = 0 ]
+		then
+			test_ok_ "$1"
+		else
+			test_failure_ "$@"
+		fi
+	fi
+	echo >&3 ""
+}
+
+test_expect_code () {
+	test "$#" = 4 && { prereq=$1; shift; } || prereq=
+	test "$#" = 3 ||
+	error "bug in the test script: not 3 or 4 parameters to test-expect-code"
+	if ! test_skip "$@"
+	then
+		say >&3 "expecting exit code $1: $3"
+		test_run_ "$3"
+		if [ "$?" = 0 -a "$eval_ret" = "$1" ]
+		then
+			test_ok_ "$2"
+		else
+			test_failure_ "$@"
+		fi
+	fi
+	echo >&3 ""
+}
+
+# test_external runs external test scripts that provide continuous
+# test output about their progress, and succeeds/fails on
+# zero/non-zero exit code.  It outputs the test output on stdout even
+# in non-verbose mode, and announces the external script with "* run
+# <n>: ..." before running it.  When providing relative paths, keep in
+# mind that all scripts run in "trash directory".
+# Usage: test_external description command arguments...
+# Example: test_external 'Perl API' perl ../path/to/test.pl
+test_external () {
+	test "$#" = 4 && { prereq=$1; shift; } || prereq=
+	test "$#" = 3 ||
+	error >&5 "bug in the test script: not 3 or 4 parameters to test_external"
+	descr="$1"
+	shift
+	if ! test_skip "$descr" "$@"
+	then
+		# Announce the script to reduce confusion about the
+		# test output that follows.
+		say_color "" " run $test_count: $descr ($*)"
+		# Run command; redirect its stderr to &4 as in
+		# test_run_, but keep its stdout on our stdout even in
+		# non-verbose mode.
+		"$@" 2>&4
+		if [ "$?" = 0 ]
+		then
+			test_ok_ "$descr"
+		else
+			test_failure_ "$descr" "$@"
+		fi
+	fi
+}
+
+# Like test_external, but in addition tests that the command generated
+# no output on stderr.
+test_external_without_stderr () {
+	# The temporary file has no (and must have no) security
+	# implications.
+	tmp="$TMPDIR"; if [ -z "$tmp" ]; then tmp=/tmp; fi
+	stderr="$tmp/git-external-stderr.$$.tmp"
+	test_external "$@" 4> "$stderr"
+	[ -f "$stderr" ] || error "Internal error: $stderr disappeared."
+	descr="no stderr: $1"
+	shift
+	say >&3 "expecting no stderr from previous command"
+	if [ ! -s "$stderr" ]; then
+		rm "$stderr"
+		test_ok_ "$descr"
+	else
+		if [ "$verbose" = t ]; then
+			output=`echo; echo Stderr is:; cat "$stderr"`
+		else
+			output=
+		fi
+		# rm first in case test_failure exits.
+		rm "$stderr"
+		test_failure_ "$descr" "$@" "$output"
+	fi
+}
+
+# This is not among top-level (test_expect_success | test_expect_failure)
+# but is a prefix that can be used in the test script, like:
+#
+#	test_expect_success 'complain and die' '
+#           do something &&
+#           do something else &&
+#	    test_must_fail git checkout ../outerspace
+#	'
+#
+# Writing this as "! git checkout ../outerspace" is wrong, because
+# the failure could be due to a segv.  We want a controlled failure.
+
+test_must_fail () {
+	"$@"
+	test $? -gt 0 -a $? -le 129 -o $? -gt 192
+}
+
+# test_cmp is a helper function to compare actual and expected output.
+# You can use it like:
+#
+#	test_expect_success 'foo works' '
+#		echo expected >expected &&
+#		foo >actual &&
+#		test_cmp expected actual
+#	'
+#
+# This could be written as either "cmp" or "diff -u", but:
+# - cmp's output is not nearly as easy to read as diff -u
+# - not all diff versions understand "-u"
+
+test_cmp() {
+	$GIT_TEST_CMP "$@"
+}
+
+# This function can be used to schedule some commands to be run
+# unconditionally at the end of the test to restore sanity:
+#
+#	test_expect_success 'test core.capslock' '
+#		git config core.capslock true &&
+#		test_when_finished "git config --unset core.capslock" &&
+#		hello world
+#	'
+#
+# That would be roughly equivalent to
+#
+#	test_expect_success 'test core.capslock' '
+#		git config core.capslock true &&
+#		hello world
+#		git config --unset core.capslock
+#	'
+#
+# except that the greeting and config --unset must both succeed for
+# the test to pass.
+
+test_when_finished () {
+	test_cleanup="{ $*
+		} && (exit \"\$eval_ret\"); eval_ret=\$?; $test_cleanup"
+}
+
+# Most tests can use the created repository, but some may need to create more.
+# Usage: test_create_repo <directory>
+test_create_repo () {
+	test "$#" = 1 ||
+	error "bug in the test script: not 1 parameter to test-create-repo"
+	owd=`pwd`
+	repo="$1"
+	mkdir -p "$repo"
+	cd "$repo" || error "Cannot setup test environment"
+	"$GIT_EXEC_PATH/git-init" "--template=$TEST_DIRECTORY/../templates/blt/" >&3 2>&4 ||
+	error "cannot run git init -- have you built things yet?"
+	mv .git/hooks .git/hooks-disabled
+	cd "$owd"
+}
+
+test_done () {
+	GIT_EXIT_OK=t
+	test_results_dir="$TEST_DIRECTORY/test-results"
+	mkdir -p "$test_results_dir"
+	test_results_path="$test_results_dir/${0%.sh}-$$"
+
+	echo "total $test_count" >> $test_results_path
+	echo "success $test_success" >> $test_results_path
+	echo "fixed $test_fixed" >> $test_results_path
+	echo "broken $test_broken" >> $test_results_path
+	echo "failed $test_failure" >> $test_results_path
+	echo "" >> $test_results_path
+
+	if test "$test_fixed" != 0
+	then
+		say_color pass "fixed $test_fixed known breakage(s)"
+	fi
+	if test "$test_broken" != 0
+	then
+		say_color error "still have $test_broken known breakage(s)"
+		msg="remaining $(($test_count-$test_broken)) test(s)"
+	else
+		msg="$test_count test(s)"
+	fi
+	case "$test_failure" in
+	0)
+		say_color pass "passed all $msg"
+
+		test -d "$remove_trash" &&
+		cd "$(dirname "$remove_trash")" &&
+		rm -rf "$(basename "$remove_trash")"
+
+		exit 0 ;;
+
+	*)
+		say_color error "failed $test_failure among $msg"
+		exit 1 ;;
+
+	esac
+}
+
+# Test the binaries we have just built.  The tests are kept in
+# t/ subdirectory and are run in 'trash directory' subdirectory.
+TEST_DIRECTORY=$(pwd)
+if test -n "$valgrind"
+then
+	make_symlink () {
+		test -h "$2" &&
+		test "$1" = "$(readlink "$2")" || {
+			# be super paranoid
+			if mkdir "$2".lock
+			then
+				rm -f "$2" &&
+				ln -s "$1" "$2" &&
+				rm -r "$2".lock
+			else
+				while test -d "$2".lock
+				do
+					say "Waiting for lock on $2."
+					sleep 1
+				done
+			fi
+		}
+	}
+
+	make_valgrind_symlink () {
+		# handle only executables
+		test -x "$1" || return
+
+		base=$(basename "$1")
+		symlink_target=$TEST_DIRECTORY/../$base
+		# do not override scripts
+		if test -x "$symlink_target" &&
+		    test ! -d "$symlink_target" &&
+		    test "#!" != "$(head -c 2 < "$symlink_target")"
+		then
+			symlink_target=../valgrind.sh
+		fi
+		case "$base" in
+		*.sh|*.perl)
+			symlink_target=../unprocessed-script
+		esac
+		# create the link, or replace it if it is out of date
+		make_symlink "$symlink_target" "$GIT_VALGRIND/bin/$base" || exit
+	}
+
+	# override all git executables in TEST_DIRECTORY/..
+	GIT_VALGRIND=$TEST_DIRECTORY/valgrind
+	mkdir -p "$GIT_VALGRIND"/bin
+	for file in $TEST_DIRECTORY/../git* $TEST_DIRECTORY/../test-*
+	do
+		make_valgrind_symlink $file
+	done
+	OLDIFS=$IFS
+	IFS=:
+	for path in $PATH
+	do
+		ls "$path"/git-* 2> /dev/null |
+		while read file
+		do
+			make_valgrind_symlink "$file"
+		done
+	done
+	IFS=$OLDIFS
+	PATH=$GIT_VALGRIND/bin:$PATH
+	GIT_EXEC_PATH=$GIT_VALGRIND/bin
+	export GIT_VALGRIND
+elif test -n "$GIT_TEST_INSTALLED" ; then
+	GIT_EXEC_PATH=$($GIT_TEST_INSTALLED/git --exec-path)  ||
+	error "Cannot run git from $GIT_TEST_INSTALLED."
+	PATH=$GIT_TEST_INSTALLED:$TEST_DIRECTORY/..:$PATH
+	GIT_EXEC_PATH=${GIT_TEST_EXEC_PATH:-$GIT_EXEC_PATH}
+else # normal case, use ../bin-wrappers only unless $with_dashes:
+	git_bin_dir="$TEST_DIRECTORY/../bin-wrappers"
+	if ! test -x "$git_bin_dir/git" ; then
+		if test -z "$with_dashes" ; then
+			say "$git_bin_dir/git is not executable; using GIT_EXEC_PATH"
+		fi
+		with_dashes=t
+	fi
+	PATH="$git_bin_dir:$PATH"
+	GIT_EXEC_PATH=$TEST_DIRECTORY/..
+	if test -n "$with_dashes" ; then
+		PATH="$TEST_DIRECTORY/..:$PATH"
+	fi
+fi
+GIT_TEMPLATE_DIR=$(pwd)/../templates/blt
+unset GIT_CONFIG
+GIT_CONFIG_NOSYSTEM=1
+GIT_CONFIG_NOGLOBAL=1
+export PATH GIT_EXEC_PATH GIT_TEMPLATE_DIR GIT_CONFIG_NOSYSTEM GIT_CONFIG_NOGLOBAL
+
+. ../GIT-BUILD-OPTIONS
+
+GITPERLLIB=$(pwd)/../perl/blib/lib:$(pwd)/../perl/blib/arch/auto/Git
+export GITPERLLIB
+test -d ../templates/blt || {
+	error "You haven't built things yet, have you?"
+}
+
+if test -z "$GIT_TEST_INSTALLED" && test -z "$NO_PYTHON"
+then
+	GITPYTHONLIB="$(pwd)/../git_remote_helpers/build/lib"
+	export GITPYTHONLIB
+	test -d ../git_remote_helpers/build || {
+		error "You haven't built git_remote_helpers yet, have you?"
+	}
+fi
+
+if ! test -x ../test-chmtime; then
+	echo >&2 'You need to build test-chmtime:'
+	echo >&2 'Run "make test-chmtime" in the source (toplevel) directory'
+	exit 1
+fi
+
+# Test repository
+test="trash directory.$(basename "$0" .sh)"
+test -n "$root" && test="$root/$test"
+case "$test" in
+/*) TRASH_DIRECTORY="$test" ;;
+ *) TRASH_DIRECTORY="$TEST_DIRECTORY/$test" ;;
+esac
+test ! -z "$debug" || remove_trash=$TRASH_DIRECTORY
+rm -fr "$test" || {
+	GIT_EXIT_OK=t
+	echo >&5 "FATAL: Cannot prepare test area"
+	exit 1
+}
+
+test_create_repo "$test"
+# Use -P to resolve symlinks in our working directory so that the cwd
+# in subprocesses like git equals our $PWD (for pathname comparisons).
+cd -P "$test" || exit 1
+
+this_test=${0##*/}
+this_test=${this_test%%-*}
+for skp in $GIT_SKIP_TESTS
+do
+	to_skip=
+	for skp in $GIT_SKIP_TESTS
+	do
+		case "$this_test" in
+		$skp)
+			to_skip=t
+		esac
+	done
+	case "$to_skip" in
+	t)
+		say_color skip >&3 "skipping test $this_test altogether"
+		say_color skip "skip all tests in $this_test"
+		test_done
+	esac
+done
+
+# Provide an implementation of the 'yes' utility
+yes () {
+	if test $# = 0
+	then
+		y=y
+	else
+		y="$*"
+	fi
+
+	while echo "$y"
+	do
+		:
+	done
+}
+
+# Fix some commands on Windows
+case $(uname -s) in
+*MINGW*)
+	# Windows has its own (incompatible) sort and find
+	sort () {
+		/usr/bin/sort "$@"
+	}
+	find () {
+		/usr/bin/find "$@"
+	}
+	sum () {
+		md5sum "$@"
+	}
+	# git sees Windows-style pwd
+	pwd () {
+		builtin pwd -W
+	}
+	# no POSIX permissions
+	# backslashes in pathspec are converted to '/'
+	# exec does not inherit the PID
+	;;
+*)
+	test_set_prereq POSIXPERM
+	test_set_prereq BSLASHPSPEC
+	test_set_prereq EXECKEEPSPID
+	;;
+esac
+
+test -z "$NO_PERL" && test_set_prereq PERL
+test -z "$NO_PYTHON" && test_set_prereq PYTHON
+
+# test whether the filesystem supports symbolic links
+ln -s x y 2>/dev/null && test -h y 2>/dev/null && test_set_prereq SYMLINKS
+rm -f y
-- 
1.7.1



More information about the notmuch mailing list