├── debian ├── compat ├── docs ├── source │ └── format ├── install ├── rules ├── control ├── copyright └── changelog ├── .codeclimate.yml ├── regress.sh ├── PKGBUILD ├── README.md ├── git-test.1 ├── LICENSE ├── git-test └── test.sh /debian/compat: -------------------------------------------------------------------------------- 1 | 9 2 | -------------------------------------------------------------------------------- /debian/docs: -------------------------------------------------------------------------------- 1 | README.md 2 | -------------------------------------------------------------------------------- /debian/source/format: -------------------------------------------------------------------------------- 1 | 3.0 (native) 2 | -------------------------------------------------------------------------------- /debian/install: -------------------------------------------------------------------------------- 1 | git-test /usr/bin 2 | git-test.1 /usr/share/man/man1 -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | # -*- makefile -*- 3 | 4 | # Uncomment this to turn on verbose mode. 5 | #export DH_VERBOSE=1 6 | 7 | %: 8 | dh $@ 9 | -------------------------------------------------------------------------------- /.codeclimate.yml: -------------------------------------------------------------------------------- 1 | --- 2 | engines: 3 | fixme: 4 | enabled: true 5 | shellcheck: 6 | enabled: true 7 | ratings: 8 | paths: 9 | - "git-test" 10 | - "test.sh" 11 | - "regress.sh" 12 | exclude_paths: 13 | - debian/ 14 | -------------------------------------------------------------------------------- /regress.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -x 2 | 3 | rev=$(git rev-parse --short HEAD) 4 | cp test.sh tests_of_${rev}.sh 5 | cp all_shells.sh all_shells_${rev}.sh 6 | export GIT_TEST_VERIFY="./all_shells_${rev}.sh tests_of_${rev}.sh -v" 7 | ./git-test -v -o reports "$@" 8 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: git-test 2 | Section: unknown 3 | Priority: extra 4 | Maintainer: Anders Eurenius 5 | Build-Depends: debhelper (>= 8.0.0) 6 | Standards-Version: 3.9.4 7 | #Homepage: 8 | #Vcs-Git: git://git.debian.org/collab-maint/git-test.git 9 | #Vcs-Browser: http://git.debian.org/?p=collab-maint/git-test.git;a=summary 10 | 11 | Package: git-test 12 | Architecture: any 13 | Depends: git | git-core 14 | Description: Git extension to conveniently test all distinct versions 15 | git-test checks out each commit in turn and runs tests, skipping commits 16 | whose contents have already been tested. 17 | -------------------------------------------------------------------------------- /PKGBUILD: -------------------------------------------------------------------------------- 1 | # -*- mode: pkgbuild -*- 2 | pkgname=git-test-git 3 | pkgrel=1 4 | pkgver=r48.2324a42 5 | pkgdesc=Git extension to conveniently test all distinct versions 6 | arch=(any) 7 | url=https://github.com/spotify/git-test 8 | depends=(git) 9 | source=('git-test-git::git+ssh://git@github.com/spotify/git-test.git') 10 | md5sums=(SKIP) 11 | 12 | pkgver() { 13 | cd "$srcdir/$pkgname" 14 | ( set -o pipefail 15 | git describe --long --tags 2>/dev/null | sed 's/\([^-]*-g\)/r\1/;s/-/./g' || 16 | printf "r%s.%s" "$(git rev-list --count HEAD)" "$(git rev-parse --short HEAD)" 17 | ) 18 | } 19 | 20 | package() { 21 | cd "$srcdir/$pkgname" 22 | install -Dm 0755 git-test "$pkgdir/usr/bin/git-test" 23 | install -Dm 0644 git-test.1 "$pkgdir/usr/share/man/man1/git-test.1" 24 | } 25 | -------------------------------------------------------------------------------- /debian/copyright: -------------------------------------------------------------------------------- 1 | Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ 2 | Upstream-Name: git-test 3 | Source: https://github.com/spotify/git-test 4 | 5 | Files: * 6 | Copyright: 2014-2015 Spotify AB 7 | License: Apache-2.0 8 | Licensed under the Apache License, Version 2.0 (the "License"); 9 | you may not use this file except in compliance with the License. 10 | You may obtain a copy of the License at 11 | . 12 | http://www.apache.org/licenses/LICENSE-2.0 13 | . 14 | Unless required by applicable law or agreed to in writing, software 15 | distributed under the License is distributed on an "AS IS" BASIS, 16 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | See the License for the specific language governing permissions and 18 | limitations under the License. 19 | . 20 | On Debian systems, the complete text of the Apache version 2.0 license 21 | can be found in "/usr/share/common-licenses/Apache-2.0". 22 | -------------------------------------------------------------------------------- /debian/changelog: -------------------------------------------------------------------------------- 1 | git-test (1.0.4) unstable; urgency=medium 2 | 3 | * Fix #10: gettext compatibility issue 4 | * Fix #13: Lock directory misbehaviours 5 | * Fix #14: Use require_clean_work_tree 6 | * Make verbose the default 7 | * Fix #9: test root branch 8 | * Explain test root feature 9 | 10 | -- Anders Eurenius Wed, 17 Aug 2016 16:20:16 +0200 11 | 12 | git-test (1.0.3) unstable; urgency=medium 13 | 14 | * Add homebrew to install instructions 15 | * Remove internal Spotify URLs from Arch PKGBUILD 16 | * Fix #5: Allow file/s and branch/es with same name 17 | * Shellcheck cleanliness 18 | 19 | -- Anders Eurenius Wed, 17 Aug 2016 16:52:00 +0200 20 | 21 | git-test (1.0.2) unstable; urgency=low 22 | 23 | * Set shebang to /bin/bash and explain why 24 | * Match table style to github markdown 25 | * Fix some unused, underquote ShellCheck warnings 26 | * Emit column headers on stdout 27 | * Add all_shells, regress scripts 28 | * Ignore stderr when looking up git-dir, fixes #2 29 | 30 | -- Anders Eurenius Sun, 30 Aug 2015 18:05:00 +0200 31 | 32 | git-test (1.0.1) UNRELEASED; urgency=low 33 | 34 | * Add missing newline after table header, fixes #1 35 | * Ignore stderr when looking up git-dir, fixes #2 36 | 37 | -- Anders Eurenius Tue, 31 Mar 2015 21:05:33 +0200 38 | 39 | git-test (1.0.0) unstable; urgency=low 40 | 41 | * Initial Release. 42 | 43 | -- Anders Eurenius Tue, 17 Feb 2015 17:09:52 +0100 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # git-test -- test your commits 2 | 3 | Run tests on each *distinct* tree in a revision list, skipping versions whose 4 | contents have already been tested. 5 | 6 | The 99% example is simply: 7 | 8 | git test -v 9 | 10 | By default it uses heuristics to try to determine what "local commits" to 11 | test, but you can supply another ref spec. `git-test` looks at each commit and 12 | checks the hash of the directory tree against the cache. You can also configure 13 | a ref (usually a branch) to test against, per repo or or per branch. 14 | 15 | From the point of view of `git-test`, a test can be any shell command and a 16 | test is considered successful if that shell command returns with a `0` exit 17 | status. This means `git-test` can be used both for specialised tests of a 18 | single feature or failure mode or for running a comprehensive set of automated 19 | tests. The cache is keyed on both directory tree and test, so it won't confuse 20 | the unit tests with the integration tests, or a specific regression test. 21 | 22 | ## Motivation 23 | 24 | An important design goal for `git-test` has been to make it convenient to use. 25 | 26 | Ideally, you should have a work flow where you run your unit tests whenever 27 | you save and run unit tests on all your local commits whenever you've done 28 | something with version control. 29 | 30 | For ease, `git-test` offers a few advantages over a simple for loop over a 31 | `git rev-list`: 32 | 33 | - By default it spends some effort on working out which commits to test. 34 | - Cached results, which are keyed to tree contents, rather than commit. This 35 | means that commits can be amended or reordered, but only content trees that 36 | have never been tested before will be tested. 37 | - Separate pre- and post-action hooks, the results of which don't actually 38 | factor into the test result. (Useful if cleaning fails if there is nothing 39 | to clean, for instance.) 40 | - Configuration of housekeeping and verification steps using 41 | - `git config`, 42 | - environment variables or 43 | - command line arguments 44 | - Selective redo, for where you trust failures but not successes, vice versa, 45 | or trust nothing. 46 | - Save output (both `STDOUT` and `STDERR`) from cleaning and verifying to 47 | an easily referenced symlink farm. 48 | 49 | 50 | ## Configure 51 | 52 | Mostly just this: 53 | 54 | git config test.verify "test command that returns nonzero on fail" 55 | 56 | to default to testing against origin/master: 57 | 58 | git config test.branch origin/master 59 | 60 | to do the same, but for a single branch: 61 | 62 | git config branch.mybranch.test parentbranch 63 | 64 | 65 | ## Self-Test 66 | 67 | To try the test script with different shells: 68 | 69 | for sh in /bin/dash /bin/bash /bin/ksh /bin/mksh /bin/pdksh; do 70 | echo $sh 71 | sh test.sh -s $sh 72 | done 73 | 74 | Note that since version 1.0.2, the shebang is set to `/bin/bash`. Other shells 75 | are now supported on a "patches welcome" basis. (This is largely because I 76 | couldn't find a shell I could run in my GNU/Linux environment that behaves 77 | like the OS X (FreeBSD?) `sh` shell, which has very different behaviour from 78 | all the others.) 79 | 80 | To regression test properly: 81 | 82 | rev=$(git rev-parse --short HEAD) 83 | cp test.sh regressions_${rev}.sh 84 | GIT_TEST_VERIFY="sh regressions_${rev}.sh" git test -v 85 | 86 | (The reason for copying the script is to test each commit against the new 87 | tests, and the reason for naming it based on the current commit is to key the 88 | cache correctly.) 89 | 90 | 91 | ## Installation 92 | 93 | You can just have the `git-test` script in your `PATH`, but there are other 94 | options: 95 | 96 | ### Homebrew (on OS X) 97 | 98 | If you have [Homebrew](http://brew.sh) installed, you can install 99 | `git-test` with: 100 | 101 | $ brew install git-test 102 | 103 | ### From source 104 | 105 | Aside from the packaging, you can also install from source. It's a single 106 | POSIX shell script that uses core git, so all that's required for plain `git 107 | test` to work (besides git, of course) is that `git-test` needs to be 108 | somewhere in your `PATH` (or `GIT_EXEC_PATH`). 109 | 110 | You can install from source by doing the following: 111 | 112 | $ install git-test /usr/local/bin 113 | $ install git-test.1 /usr/local/share/man1 114 | 115 | Or just add this directory to your `PATH` environment variable. 116 | 117 | ### Debian GNU/Linux 118 | 119 | The usual 120 | 121 | $ fakeroot debian/rules binary 122 | 123 | Should give you a Debian package. 124 | 125 | ### Arch Linux 126 | 127 | With Arch Linux, you can use the provided `PKGBUILD` file. Simply download the 128 | file and run `makepkg` in the same directory as the file. It will always build 129 | the latest `git` version of this package, even if you have an old checkout. 130 | -------------------------------------------------------------------------------- /git-test.1: -------------------------------------------------------------------------------- 1 | .TH GIT-TEST "1" "August 2016" "git-test version 1.0.4" "User Commands" 2 | .SH "NAME" 3 | git-test \- manual page for git-test version 1.0.4 4 | .SH "SYNOPSIS" 5 | git test [options] [refs...] 6 | 7 | git test --clear [refs...] 8 | 9 | git test --version 10 | .SH "DESCRIPTION" 11 | Run tests on each distinct tree in a revision list. 12 | 13 | The 99% example is simply: 14 | .IP 15 | git test 16 | .PP 17 | 18 | If no refs are specified, the default operation is to test the commits on the 19 | current branch that are not in the origin branch of the same name, nor in 20 | origin/master. If neither origin/"current branch", nor origin/master exist, 21 | git-test will refuse to run in order to protect against surprisingly large 22 | runs. This would happen, for instance, if you're simply not using a remote 23 | named origin, in a large project with many versions. 24 | 25 | This is currently not configurable, but if you want any other ref, you can 26 | just supply it: 27 | .IP 28 | git test HEAD ^jeff/development 29 | .PP 30 | 31 | Note that ref exclusions are almost always necessary since not having them 32 | would mean testing everything since the dawn of time. 33 | 34 | For example, this will test all commits on master that have not been pushed 35 | to origin: 36 | .IP 37 | git test \-\-verify="make test" master ^origin/master 38 | .PP 39 | Tests are assumed to be deterministic and git-test uses a cache of trees with 40 | known results to speed up testing. Having non-deterministic, "flappy" tests, 41 | is a very special circle of hell, but there are some limited facilities for 42 | dealing with this situation. (Besides the obvious, \fBfixing your tests.\fR) 43 | 44 | Running with the verbose option shows progress as it happens: 45 | .IP 46 | .nf 47 | $ git test 48 | the-branch ^origin/the-branch ^origin/master will test 2 commits 49 | iter commit tree result 50 | 0000 83d611b 718c490 ... pass 51 | 0001 0be28e9 6908e0d ... pass 52 | .fi 53 | .PP 54 | Immediately re-running the tests will just take the results from the cache: 55 | .IP 56 | .nf 57 | $ git test 58 | the-branch ^origin/the-branch ^origin/master will test 2 commits 59 | iter commit tree result 60 | 0000 83d611b 718c490 ... pass (cached) 61 | 0001 0be28e9 6908e0d ... pass (cached) 62 | .fi 63 | 64 | .SH "CONFIGURATION" 65 | To specify what to run on each tree, the arguments --pre, --post and --verify 66 | can be used to set housekeeping commands and a (required) verifying command. 67 | The result of the housekeeping commands are ignored, because it's often 68 | convenient to just let it fail if there is nothing to clean up, or there's no 69 | artifact to save, or whatever it is you were trying to do. 70 | 71 | To avoid having to specify action(s) on every invocation, they can be set 72 | using git-config feature, preferrably locally: 73 | .IP 74 | git config test.verify "make test" 75 | .IP 76 | git config test.pre "rm -f test-output" 77 | .IP 78 | git config test.post "cp a.out a.out.\\$commit" 79 | .PP 80 | Alternatively, these can be set through environment variables 81 | \fBGIT_TEST_PRE\fR, \fBGIT_TEST_POST\fR and \fBGIT_TEST_VERIFY\fR 82 | respectively. 83 | 84 | A default ref (usually a branch) to test against can be configured: 85 | .IP 86 | git config test.branch origin/release 87 | .PP 88 | A default ref can also be set for a specific branch: 89 | .IP 90 | git config branch.myfeature.test origin/staging 91 | .PP 92 | .SH "OPTIONS" 93 | Available options are 94 | .TP 95 | \fB\-v\fR, \fB\-\-verbose\fR 96 | be more explicit about what is going on 97 | .TP 98 | \fB\-q\fR, \fB\-\-quiet\fR 99 | be quiet 100 | .TP 101 | \fB\-r\fR, \fB\-\-redo\fR ... 102 | test if [any/pass/fail/both/never] result cached for tree. See the \fBREDO 103 | MODES\fR 104 | section 105 | .TP 106 | \fB\-o\fR, \fB\-\-output\fR ... 107 | output directory for reports. See the \fBSAVING OUTPUT\fR section 108 | .TP 109 | \fB\-\-pre\fR ..., \fB\-\-post\fR ... 110 | command to run before and after running the test. Overrides the environment 111 | variables GIT_TEST_PRE, GIT_TEST_POST and the git-config options test.pre, 112 | test.post 113 | .TP 114 | \fB\-\-verify\fR ... 115 | the command to run as test. Overrides the environment variable 116 | GIT_TEST_VERIFY and the git-config option test.verify 117 | .SH "ACTIONS" 118 | .TP 119 | \fB\-\-clear\fR 120 | clear the results cache 121 | .TP 122 | \fB\-\-version\fR 123 | print version info and quit 124 | .SH "REDO MODES" 125 | If the tests have false negatives, git-test can be instructed to re-test 126 | failing commits. For symmetry, re-test everything, (all or any) re-test 127 | passing tests and the default behavior to never re-test can be specified. 128 | .TS 129 | l l . 130 | Argument Action 131 | a, any, all, always Always run all test 132 | p, pass, passed, passing Retest passed (and flappy) tests 133 | f, fail, failed, failing Retest failed (and flappy) tests 134 | b, both, flap, flappy Retest only flappy tests 135 | n, no, none, never Never retest, always trust cache 136 | .TE 137 | (Default: none) 138 | .SH "SAVING OUTPUT" 139 | By adding -o/--output to specify where, you can tell git-test to save stdout 140 | and stderr from the actions in separate files by tree. 141 | 142 | Files are created for each tree and symlinked from commit and test-run 143 | timestamp directories. 144 | 145 | Example: 146 | .nf 147 | .sp 148 | reports/ 149 | |-- 1418737218 150 | | `-- 0000_pass -> ../tree/f864c32_4aab77c_pass 151 | | `-- 0001_pass -> ../tree/da47834_4aab77c_pass 152 | |-- 1418737342 153 | | `-- 0000_pass -> ../tree/da47834_4aab77c_pass 154 | |-- commit 155 | | `-- 6eaff36_pass -> ../tree/f864c32_4aab77c_pass 156 | | `-- 9d0234d_pass -> ../tree/da47834_4aab77c_pass 157 | |-- latest -> 1418737342 158 | `-- tree 159 | |-- da47834_4aab77c_pass 160 | `-- f864c32_4aab77c_pass 161 | .sp 162 | .fi 163 | .SH "CLEARING THE CACHE" 164 | If refs are supplied when clearing the cache, the trees of the specified 165 | commits will be cleared, even though those exact trees may appear in other 166 | commits. It is what was requested and doing anything else would likely be 167 | prohibitively expensive and very confusing. 168 | .SH "AUTHORS" 169 | Anders Eurenius 170 | .SH "COPYRIGHT" 171 | Copyright (C) 2015 Spotify AB 172 | Copyright (C) 2016 Anders Eurenius 173 | .SH "LICENSE" 174 | Apache License 2.0 175 | .SH "SEE ALSO" 176 | The full documentation for 177 | .B git-test 178 | is the source. Whether it conflicts with any other information or not, the 179 | computer will execute the code, never the documentation. 180 | 181 | \fBgit-rebase\fR(1), in particular, the exec feature 182 | 183 | \fBgit-bisect\fR(1) might also be of interest 184 | 185 | .RE 186 | For more information, please re-read. 187 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /git-test: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Copyright 2014-2015 Spotify AB. All rights reserved. 4 | # 5 | # The contents of this file are licensed under the Apache License, Version 2.0 6 | # (the "License"); you may not use this file except in compliance with the 7 | # License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations under 15 | # the License. 16 | # 17 | set -e 18 | 19 | VERSION=1.0.4 20 | 21 | export NONGIT_OK=Yes 22 | export SUBDIRECTORY_OK=Yes 23 | export OPTIONS_KEEPDASHDASH= 24 | export OPTIONS_STUCKLONG=t 25 | export OPTIONS_SPEC="\ 26 | git test [options] [refs...] 27 | 28 | git test --clear [refs...] 29 | 30 | Run tests on each distinct tree 31 | 32 | Example: 33 | git test --verify=\"make test\" master ^origin/master 34 | -- 35 | Available options are 36 | v,verbose! be more explicit about what is going on 37 | q,quiet! be quiet 38 | r,redo= re-test even if [any/pass/fail/both/no] result cached for tree 39 | o,output= output directory for reports 40 | cache=* specify cache directory 41 | pre= command to run before running tests 42 | post= command to run after running tests 43 | verify= the command to run as test 44 | Actions: 45 | clear! clear the results cache 46 | version! print version info and quit" 47 | 48 | CORE=$(git --exec-path) 49 | PATH="$CORE:$PATH" 50 | 51 | . git-sh-setup 52 | . git-sh-i18n 53 | 54 | CR="$(printf "\r")$(tput el)" 55 | NL=' 56 | ' 57 | 58 | OUT=4 59 | ERR=5 60 | exec 3<&0 61 | exec 4>&1 62 | exec 5>&2 63 | 64 | rc=0 65 | atexit_cleanup() { 66 | git checkout -q "$starting_point" 67 | unlock 68 | exit $rc 69 | } 70 | 71 | lock() { 72 | mkdir -p "$cache" 73 | msg="There's a lock dir, as if there is a git-test already in progress." 74 | if ! mkdir "$cache"/testing >/dev/null 2>&1 ; then 75 | echo "$(eval_gettext "$msg")" 1>& $ERR 76 | echo "$(eval_gettext "(lock: \${cache}/testing)")" 1>& $ERR 77 | rc=5 78 | exit 5 79 | fi 80 | } 81 | 82 | unlock() { 83 | if [ -d "$cache"/testing ]; then 84 | rmdir "$cache"/testing 85 | fi 86 | } 87 | 88 | 89 | progress () { 90 | if [ "$1" = err ] ; then 91 | if [ -z "$GIT_QUIET" ] ; then 92 | printf "$CR%04d | %s | %s | " \ 93 | "$iteration" "$short" "$small" 1>& $ERR 94 | gettext "$2" 1>& $ERR 95 | fi 96 | else 97 | if [ -z "$GIT_QUIET" ]; then 98 | printf "%s" "$CR" 1>& $ERR 99 | printf "%04d | %s | %s | " \ 100 | "$iteration" "$short" "$small" 1>& $OUT 101 | gettext "$2" 1>& $OUT 102 | echo 1>& $OUT 103 | fi 104 | fi 105 | } 106 | 107 | 108 | tree_of_commit() { 109 | words=( $(git cat-file -p "$1" | grep tree) ) 110 | printf "%s" "${words[1]}" 111 | } 112 | 113 | make_output_dirs() { 114 | if test -n "$output" ; then 115 | mkdir -p "$output"/commit "$output"/tree "$output"/"$now" 116 | if test -h "$output"/latest ; then 117 | rm -f "$output"/latest 118 | fi 119 | ln -s "$now" "$output"/latest 120 | fi 121 | } 122 | 123 | 124 | decide_refs() { 125 | if test -n "$*" ; then 126 | echo "$*" 127 | return 128 | fi 129 | 130 | local args="" 131 | local branch 132 | branch="$(git symbolic-ref --short -q HEAD || true)" 133 | 134 | if git config "branch.${branch}.test" >/dev/null 2>&1 ; then 135 | args="^$(git config "branch.${branch}.test")" 136 | elif git config "test.branch" >/dev/null 2>&1 ; then 137 | args="^$(git config test.branch)" 138 | elif git rev-parse --symbolic-full-name '@{u}' >/dev/null 2>&1 ; then 139 | upstream="$(git rev-parse --symbolic-full-name '@{u}')" 140 | args="^${upstream#refs/*/}" 141 | fi 142 | 143 | for remote in $(git remote) ; do 144 | remote_master="$(git branch --list --remotes "$remote/master")" 145 | if [ -n "$remote_master" ] ; then 146 | args="$args ^${remote_master# }" 147 | fi 148 | remote_branch="$(git branch --list --remotes "$remote/$branch")" 149 | if [ -n "$remote_branch" ] ; then 150 | args="$args ^${remote_branch# }" 151 | fi 152 | done 153 | 154 | if [ -z "$args" ] ; then 155 | gettext "Cowardly refusing to test the entire history. 156 | (If that's what you really want, you must specify at least HEAD)" >& $ERR 157 | rc=5 158 | else 159 | echo "$branch $args" | tr " " "$NL" | sort | uniq | tr "$NL" " " 160 | fi 161 | } 162 | 163 | 164 | check_cache() { 165 | if test -f "$cache/${1}_pass" ; then pass=pass; else pass='' ; fi 166 | if test -f "$cache/${1}_fail" ; then fail=fail; else fail='' ; fi 167 | 168 | echo "$pass$fail" 169 | } 170 | 171 | recheck_cache() { 172 | results=$(check_cache "$1") 173 | if [ "$results" = "passfail" ]; then 174 | results=FLAPPY 175 | fi 176 | echo $results 177 | } 178 | 179 | redo_check() { 180 | result=$(check_cache "$1") 181 | 182 | if [ -z "$result" ]; then 183 | return 184 | elif [ "$redo" = "all" ]; then 185 | return 186 | elif [ "$redo" = "pass" ] && [ "${result%fail}" = "pass" ]; then 187 | return 188 | elif [ "$redo" = "fail" ] && [ "${result#pass}" = "fail" ]; then 189 | return 190 | elif [ "$redo" = "both" ] && [ "${result}" = "passfail" ]; then 191 | return 192 | elif [ "$result" = "passfail" ]; then 193 | echo FLAPPY 194 | else 195 | echo "$result" 196 | fi 197 | } 198 | 199 | link_result() { 200 | dest="../tree/${cache_key}_${result}" 201 | iter="$(printf "%s/%s/%04d_%s" "$output" "$now" "$iteration" "$result")" 202 | 203 | if test -f "$output/tree/${cache_key}_${result}" ; then 204 | ln -sf "$dest" "${iter}" 205 | ln -sf "$dest" "${output}/commit/${short}_${result}" 206 | fi 207 | 208 | 209 | } 210 | 211 | 212 | run_test() { 213 | progress err "checkout" 214 | git checkout -q "$commit" 215 | 216 | if test -n "$output" ; then 217 | out="${output}/tree/${cache_key}" 218 | else 219 | out=/dev/null 220 | fi 221 | 222 | if test -n "$pre"; then 223 | progress err "pre-action" 224 | gettext "Running pre-action" >$out 225 | echo "--------" >$out 226 | eval_gettext "Running: '\$pre'" >$out 227 | ( $pre ) >$out 2>&1 || true 228 | fi 229 | 230 | gettext "Verifying" >$out 231 | echo "--------" >$out 232 | eval_gettext "Running: '\$verify'" >$out 233 | progress err "testing" 234 | if ( eval "$verify" ) >$out 2>&1; then 235 | result=pass 236 | else 237 | result=fail 238 | fi 239 | 240 | if test -n "$post"; then 241 | progress err "post-action" 242 | gettext "Running post-action" >$out 243 | echo "--------" >$out 244 | gettext "Running: '\$post'" >$out 245 | ( $post ) >$out 2>&1 || true 246 | fi 247 | 248 | if test "$out" != /dev/null ; then 249 | mv "${out}" "${out}_${result}" 250 | fi 251 | 252 | mkdir -p "${cache}" 253 | touch "${cache}/${cache_key}_${result}" 254 | echo $result 255 | } 256 | 257 | 258 | run_tests() { 259 | commits="" 260 | iteration=0 261 | verification="$(echo "$verify" | git hash-object --stdin)" 262 | ver="$(git rev-parse --short=7 "$verification")" 263 | 264 | if [ -z "$GIT_QUIET" ]; then 265 | gettext "iter | commit | tree | result" 266 | echo 267 | gettext " ----|---------|---------|--------------" 268 | echo 269 | fi 270 | 271 | for commit in "$@" ; do 272 | tree=$(tree_of_commit "$commit") 273 | small=$(git rev-parse --short=7 "$tree") 274 | short=$(git rev-parse --short=7 "$commit") 275 | 276 | cache_key="${small}_${ver}" 277 | 278 | result=$(redo_check "$cache_key") 279 | 280 | if test -n "$result"; then 281 | if test -z "$GIT_QUIET"; then 282 | progress err "cached" 283 | progress out "$result (cached)" 284 | fi 285 | else 286 | result=$(run_test) 287 | 288 | if test -z "$GIT_QUIET"; then 289 | overall=$(recheck_cache "$cache_key") 290 | 291 | if [ "$result" != "$overall" ]; then 292 | progress out "$result ($overall)" 293 | else 294 | progress out "$result" 295 | fi 296 | fi 297 | fi 298 | 299 | if test "$result" = "fail"; then 300 | rc=5 301 | fi 302 | 303 | link_result 304 | 305 | iteration=$((iteration + 1)) 306 | done 307 | } 308 | 309 | 310 | GIT_DIR=$(git rev-parse --git-dir 2>/dev/null || true) 311 | 312 | # Defaults 313 | action=test 314 | cache=${GIT_TEST_CACHE:-"$GIT_DIR"/test-cache} 315 | pre=${GIT_TEST_PRE:-$(git config test.pre || true)} 316 | post=${GIT_TEST_POST:-$(git config test.post || true)} 317 | verify=${GIT_TEST_VERIFY:-$(git config test.verify || true)} 318 | 319 | ALL_PATTERN='^\(a\|all\|any\|always\)\?$' 320 | FAIL_PATTERN='^\(f\|fail\|failed\|failing\)$' 321 | PASS_PATTERN='^\(p\|pass\|passed\|passing\)$' 322 | BOTH_PATTERN='^\(b\|both\|flap\|flappy\)$' 323 | NONE_PATTERN='^\(n\|no\|none\|never\)$' 324 | 325 | parse_redo() { 326 | if echo "$1" | grep "$ALL_PATTERN" >/dev/null ; then 327 | redo=all 328 | elif echo "$1" | grep "$FAIL_PATTERN" >/dev/null ; then 329 | redo=fail 330 | elif echo "$1" | grep "$PASS_PATTERN" >/dev/null ; then 331 | redo=pass 332 | elif echo "$1" | grep "$NONE_PATTERN" >/dev/null ; then 333 | redo= 334 | elif echo "$1" | grep "$BOTH_PATTERN" >/dev/null ; then 335 | redo=both 336 | else 337 | gettext "Unknown redo mode requested" 338 | rc=1 339 | exit $rc 340 | fi 341 | } 342 | 343 | while test $# != 0 344 | do 345 | case $1 in 346 | -v|--verbose) 347 | GIT_QUIET= 348 | ;; 349 | -q|--quiet) 350 | verbose= 351 | GIT_QUIET=true 352 | git_am_opt="$git_am_opt -q" 353 | ;; 354 | -r|--redo) 355 | parse_redo "$2" 356 | shift 357 | ;; 358 | --redo=*) 359 | parse_redo "${1#--redo=}" 360 | ;; 361 | -o|--output) 362 | output="$2" 363 | shift 364 | ;; 365 | --output=*) 366 | output="${1#--output=}" 367 | ;; 368 | --cache) 369 | cache="$2" 370 | shift 371 | ;; 372 | --cache=*) 373 | cache="${1#--cache=}" 374 | ;; 375 | --pre) 376 | pre="$2" 377 | shift 378 | ;; 379 | --pre=*) 380 | pre="${1#--pre=}" 381 | ;; 382 | --post) 383 | post="$2" 384 | shift 385 | ;; 386 | --post=*) 387 | post="${1#--post=}" 388 | ;; 389 | --verify) 390 | verify="$2" 391 | shift 392 | ;; 393 | --verify=*) 394 | verify="${1#--verify=}" 395 | ;; 396 | --clear) 397 | action=clear 398 | ;; 399 | --version) 400 | action=version 401 | ;; 402 | --) 403 | shift 404 | break 405 | ;; 406 | esac 407 | shift 408 | done 409 | 410 | if [ $action = version ] ; then 411 | echo "git-test version $VERSION" 412 | exit 0 413 | elif [ -z "$GIT_DIR" ] ; then 414 | usage 415 | exit 5 416 | else 417 | set_reflog_action test 418 | require_work_tree_exists 419 | require_clean_work_tree test 420 | cd_to_toplevel 421 | 422 | # Current state 423 | starting_point=$(git symbolic-ref --short -q HEAD || git rev-parse HEAD) 424 | now=$(date +%s) 425 | fi 426 | 427 | if [ $action = clear ] ; then 428 | if test -z "$*" ; then 429 | rm -f "$cache"/*_fail "$cache"/*_pass 430 | else 431 | refs=( $(decide_refs "$@") ) 432 | commits="$(git rev-list --reverse "${refs[@]}" -- | tr "$NL" " ")" 433 | count="$(echo "$commits" | wc -w)" 434 | 435 | if test 1 -gt "$count" ; then 436 | gettext "List of commits to clear is empty" 1>&2 437 | exit 438 | fi 439 | 440 | if test -z "$GIT_QUIET" ; then 441 | args="$*" 442 | eval_gettext "\$args will clear \$count commits" 443 | echo 444 | fi 445 | 446 | for commit in $commits; do 447 | tree=$(tree_of_commit "$commit") 448 | small=$(git rev-parse --short=7 "$tree") 449 | rm -f "$cache"/"$small"_*_fail "$cache"/"$small"_*_pass 450 | done 451 | fi 452 | 453 | rm -Rf "$cache"/testing 454 | exit 0 455 | fi 456 | 457 | if [ -z "$verify" ] ; then 458 | gettext "A verification action is required. Specify one using 459 | the argument --verify=\"...\" 460 | or 461 | configure it using 'git config test.verify ...' 462 | or 463 | the environment variable GIT_TEST_VERIFY 464 | " 465 | echo 466 | exit 5 467 | fi 468 | 469 | lock 470 | 471 | trap "atexit_cleanup" INT TERM EXIT 472 | 473 | refs=( $(decide_refs "$@") ) 474 | commits=( $(git rev-list --reverse "${refs[@]}" -- | tr "$NL" " ") ) 475 | count="$(echo "${commits[@]}" | wc -w)" 476 | 477 | if test 1 -gt "$count" ; then 478 | gettext 'List of commits to test is empty' 1>&2 479 | exit 480 | elif test -z "$GIT_QUIET" ; then 481 | printf "$(eval_gettext "%s will test %d commits\n")" "${refs[*]}" "$count" 482 | fi 483 | 484 | make_output_dirs 485 | 486 | run_tests "${commits[@]}" 487 | 488 | exit 1 489 | -------------------------------------------------------------------------------- /test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Copyright 2014-2015 Spotify AB. All rights reserved. 4 | # 5 | # The contents of this file are licensed under the Apache License, Version 2.0 6 | # (the "License"); you may not use this file except in compliance with the 7 | # License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations under 15 | # the License. 16 | # 17 | export OPTIONS_SPEC="\ 18 | bash $0 [options] 19 | 20 | Run tests 21 | 22 | Example: 23 | sh $0 -v 24 | -- 25 | Available options are 26 | v,verbose! be more explicit about what is going on 27 | q,quiet! be quiet 28 | x,exit! exit after first test failure 29 | s,shell= shell to use [default: /bin/sh] 30 | " 31 | 32 | eval "$( 33 | echo "$OPTIONS_SPEC" \ 34 | | git rev-parse --parseopt $parseopt_extra -- "$@" || 35 | echo exit $? 36 | )" 37 | 38 | CR="$(printf "\r")" 39 | NL=' 40 | ' 41 | 42 | verb=1 43 | quit=0 44 | last="" 45 | pass=0 46 | fail=0 47 | 48 | shshell=${shshell:-/bin/bash} 49 | 50 | total_argc=$# 51 | while [ $# != 0 ] 52 | do 53 | case $1 in 54 | -v|--verbose) 55 | verb=2 56 | ;; 57 | -q|--quiet) 58 | verb=0 59 | ;; 60 | -x|--exit) 61 | quit=1 62 | ;; 63 | -s|--shell) 64 | shshell=$2 65 | shift 66 | ;; 67 | --shell=*) 68 | shshell="${1#--shell=}" 69 | ;; 70 | --) 71 | shift 72 | break 73 | ;; 74 | *) 75 | echo "\$1: $1" 76 | echo "$OPTIONS_SPEC" 77 | exit 0 78 | ;; 79 | esac 80 | shift 81 | done 82 | 83 | PROJECT="${shshell} $(pwd)/git-test" 84 | TMPDIR="${TMPDIR:=/tmp}" 85 | SUBJECT="$TMPDIR/subject/" 86 | 87 | rm -Rf "$SUBJECT" 88 | mkdir -p "$SUBJECT" 89 | cd "$SUBJECT" 90 | 91 | 92 | dot() { 93 | if [ $verb -eq 1 ]; then 94 | if [ -n "$1" ]; then printf . ; else printf f ; fi 95 | elif [ $verb -eq 2 ]; then 96 | if [ -n "$1" ]; then echo "...pass" ; else echo "...fail"; fi 97 | fi 98 | 99 | if [ $quit -gt 0 ] && ! [ -n "$1" ]; then 100 | if [ $verb -lt 2 ]; then 101 | printf "\n%s\n" "$last" 102 | fi 103 | exit 5 104 | fi 105 | } 106 | 107 | info() { 108 | last="$*" 109 | if [ $verb -ge 2 ]; then 110 | printf "%s\n" "$@" 111 | fi 112 | } 113 | 114 | fail() { 115 | fail=$(expr $fail + 1) 116 | dot 117 | } 118 | 119 | pass() { 120 | pass=$(expr $pass + 1) 121 | dot "t" 122 | } 123 | 124 | check() { 125 | if [ "$?" -eq 0 ]; then pass ; else fail ; fi 126 | } 127 | 128 | check_fail() { 129 | if [ "$?" -ne 0 ]; then pass ; else fail ; fi 130 | } 131 | 132 | is_empty() { 133 | if [ -f "$1" ] && ! [ -s "$1" ]; then pass ; else fail ; fi 134 | } 135 | 136 | add_commit() { 137 | echo "$1" > subject 138 | git add subject 139 | git commit -m "$2" subject 140 | } 141 | 142 | get_tree() { 143 | git rev-parse --short $(git cat-file -p $1 | grep tree | grep -o '[^ ]*$') 144 | } 145 | 146 | setup_for_redo() { 147 | rm -rf .git/test-cache/* 148 | touch .git/test-cache/${tree_a}_${ver}_pass 149 | touch .git/test-cache/${tree_b}_${ver}_pass # flappy! 150 | touch .git/test-cache/${tree_b}_${ver}_fail # 151 | touch .git/test-cache/${tree_c}_${ver}_fail 152 | } 153 | 154 | unset GIT_TEST_CLEAN 155 | unset GIT_TEST_VERIFY 156 | 157 | info "# Basic sanity tests" 158 | 159 | info "Should be able to report version without repo" 160 | $PROJECT --version >out 2>err ; check 161 | grep "^git-test version" out >/dev/null 2>&1 ; check 162 | 163 | info "Empty repo should not pass tests" 164 | git init -q >/dev/null 2>&1 165 | git config user.email "test.user@example.com" >/dev/null 2>&1 166 | git config user.name "User deTest" >/dev/null 2>&1 167 | 168 | $PROJECT >out 2>err ; check_fail 169 | 170 | info "( Create repo with initial subject file )" 171 | add_commit 'contains the SUT.' "first file" >out 2>err ; check 172 | 173 | info "Check that a verification action is required" 174 | $PROJECT HEAD >out 2>err ; check_fail 175 | grep "A verification action is required." out >/dev/null 2>&1 ; check 176 | 177 | printf pass > verify_result 178 | verify="{ touch ran_test && [ \"\$(cat verify_result)\" = \"pass\" ]; }" 179 | info "Configure verification in git config" 180 | git config --local test.verify "$verify" 181 | 182 | info "Clear results cache" 183 | $PROJECT -v --clear >out 2>err ; check 184 | info "Clearing results doesn't output anything on stdout" 185 | is_empty out 186 | info "Clearing results doesn't output anything on stderr" 187 | is_empty err 188 | 189 | info "Pass test, because git-test uses the git config" 190 | rm -f ran_test 191 | $PROJECT -v master >out 2>err ; check 192 | grep "master will test *1 commit" out >/dev/null 2>&1 ; check 193 | grep "^0000 .* pass *$" out >/dev/null 2>&1 ; check 194 | 195 | info "Pass test without checking, because the result is cached" 196 | printf fail > verify_result 197 | rm -f ran_test 198 | $PROJECT -v --verify="$verify" master >out 2>err ; check 199 | grep "^0000 .* pass (cached)" out >/dev/null 2>&1 ; check 200 | 201 | info "Didn't run the verify action" 202 | ! test -f ran_test ; check 203 | 204 | info "Clear cache" 205 | $PROJECT --clear 206 | 207 | info "Fail test after clearing the cache" 208 | rm -f ran_test 209 | $PROJECT -v master >out 2>err ; check_fail 210 | grep "^0000 .* fail *$" out >/dev/null 2>&1 ; check 211 | 212 | info "This time we ran the verify action" 213 | test -f ran_test ; check 214 | 215 | add_commit "is the SUT" "Frob the subject file" >/dev/null 2>&1 216 | $PROJECT --clear 217 | 218 | git config --local test.verify "grep contains subject" 219 | info "Fail on new commit, because code and tests are out of sync" 220 | $PROJECT -v master >out 2>err ; check_fail 221 | grep "master will test *2 commits" out >/dev/null 2>&1 ; check 222 | grep "^0000 .* pass *$" out >/dev/null 2>&1 ; check 223 | grep "^0001 .* fail *$" out >/dev/null 2>&1 ; check 224 | 225 | info "No results came from cache" 226 | ! grep "(cached)" err >/dev/null 2>&1 ; check 227 | 228 | info "Check that results are cached" 229 | $PROJECT -v master >out 2>err ; check_fail 230 | grep "master will test *2 commits" out >/dev/null 2>&1 ; check 231 | grep "^0000 .* pass (cached)$" out >/dev/null 2>&1 ; check 232 | grep "^0001 .* fail (cached)$" out >/dev/null 2>&1 ; check 233 | 234 | info "# Feature: Redo modes" 235 | 236 | # Preliminaries for redo tests 237 | add_commit "some other thing" "another commit" >/dev/null 2>&1 238 | add_commit "MOOAAR SUT!" " fuuu" >/dev/null 2>&1 239 | $PROJECT --clear >/dev/null 2>&1 ; check 240 | verify="grep SUT subject" 241 | verification="$(echo "$verify" | git hash-object --stdin)" 242 | ver="$(git rev-parse --short $verification)" 243 | git config test.verify "$verify" 244 | 245 | tree_a=$(get_tree HEAD~3) 246 | tree_b=$(get_tree HEAD~2) 247 | tree_c=$(get_tree HEAD^) 248 | tree_d=$(get_tree HEAD) 249 | 250 | # ... 251 | 252 | info "Check that --redo=all re-tests both pass and fail" 253 | setup_for_redo 254 | $PROJECT -v --redo=all master >out 2>err ; check_fail 255 | grep "master will test *4 commits" out >/dev/null 2>&1 ; check 256 | grep "^0000 .* pass *$" out >/dev/null 2>&1 ; check 257 | grep "^0002 .* fail *$" out >/dev/null 2>&1 ; check 258 | grep -v "(cached)" out >/dev/null 2>&1 ; check 259 | 260 | info "Check that --redo=pass re-tests pass and not fail" 261 | setup_for_redo 262 | $PROJECT -v --redo=pass master >out 2>err ; check_fail 263 | grep "master will test *4 commits" out >/dev/null 2>&1 ; check 264 | grep "^0000 .* pass *$" out >/dev/null 2>&1 ; check 265 | grep "^0001 .* pass (FLAPPY)$" out >/dev/null 2>&1 ; check 266 | grep "^0002 .* fail (cached)$" out >/dev/null 2>&1 ; check 267 | grep "^0003 .* pass *$" out >/dev/null 2>&1 ; check 268 | 269 | info "Check that --redo=fail re-tests fail and not pass" 270 | setup_for_redo 271 | $PROJECT -v --redo=fail master >out 2>err ; check_fail 272 | grep "master will test *4 commits" out >/dev/null 2>&1 ; check 273 | grep "^0000 .* pass (cached)$" out >/dev/null 2>&1 ; check 274 | grep "^0001 .* pass (FLAPPY)$" out >/dev/null 2>&1 ; check 275 | grep "^0002 .* fail$" out >/dev/null 2>&1 ; check 276 | grep "^0003 .* pass *$" out >/dev/null 2>&1 ; check 277 | 278 | 279 | info "Check that --redo=both re-tests only flappy tests" 280 | setup_for_redo 281 | $PROJECT -v --redo=both master >out 2>err ; check_fail 282 | grep "master will test *4 commits" out >/dev/null 2>&1 ; check 283 | grep "^0000 .* pass (cached)$" out >/dev/null 2>&1 ; check 284 | grep "^0001 .* pass (FLAPPY)$" out >/dev/null 2>&1 ; check 285 | grep "^0002 .* fail (cached)$" out >/dev/null 2>&1 ; check 286 | grep "^0003 .* pass *$" out >/dev/null 2>&1 ; check 287 | 288 | $PROJECT --clear 289 | git branch long-history 290 | git reset --hard HEAD~2 >/dev/null 2>&1 291 | 292 | info "# Feature: pre-action" 293 | $PROJECT --clear 294 | 295 | info "Check that pre- and post-actions are run" 296 | export GIT_TEST_PRE="touch ran-pre" 297 | export GIT_TEST_POST="touch ran-post" 298 | export GIT_TEST_VERIFY="touch tested" 299 | $PROJECT -v -ra master >out 2>err ; check 300 | tr "$CR" "$NL" < err > err2 && mv err2 err 301 | grep "master will test *2 commits" out >/dev/null 2>&1 ; check 302 | grep "^0000 .* pass" out >/dev/null 2>&1 ; check 303 | grep "^0001 .* pass" out >/dev/null 2>&1 ; check 304 | grep "pre-action" err >/dev/null 2>&1 ; check 305 | grep "post-action" err >/dev/null 2>&1 ; check 306 | test -f ran-pre ; check 307 | test -f ran-post ; check 308 | 309 | info "Check that pre- and post-actions are permitted to fail" 310 | export GIT_TEST_PRE="false" 311 | export GIT_TEST_POST="false" 312 | export GIT_TEST_VERIFY="true" 313 | $PROJECT -v -ra master >out 2>err ; check 314 | tr "$CR" "$NL" < err > err2 && mv err2 err 315 | grep "master will test *2 commits" out >/dev/null 2>&1 ; check 316 | grep "^0000 .* pass" out >/dev/null 2>&1 ; check 317 | grep "^0001 .* pass" out >/dev/null 2>&1 ; check 318 | grep "pre-action" err >/dev/null 2>&1 ; check 319 | grep "post-action" err >/dev/null 2>&1 ; check 320 | 321 | 322 | info "# Priority of conflicting configurations" 323 | 324 | unset GIT_TEST_PRE 325 | unset GIT_TEST_POST 326 | export GIT_TEST_VERIFY="echo 'environment' > winner" 327 | git config test.verify "echo 'config' > winner" 328 | verify_redir="echo 'argument' > winner" 329 | 330 | info "Check that command-line argument is highest priority" 331 | $PROJECT -v -ra --verify="$verify_redir" master >out 2>err ; check 332 | result=$(cat winner) 333 | test "$result" = "argument" ; check 334 | 335 | info "Check that environment variable is second highest priority" 336 | $PROJECT -v -ra master >out 2>err ; check 337 | result=$(cat winner) 338 | test "$result" = "environment" ; check 339 | 340 | info "Check that git config is third highest priority" 341 | unset GIT_TEST_VERIFY 342 | $PROJECT -v -ra master >out 2>err ; check 343 | result=$(cat winner) 344 | test "$result" = "config" ; check 345 | 346 | info "# Regressions" 347 | 348 | info "Should refuse empty rev-list" 349 | $PROJECT -v -ra master ^master >out 2>err ; check 350 | is_empty out ; check 351 | grep "List of commits to test is empty" err >/dev/null 2>&1 ; check 352 | grep "^iter commit" err out >/dev/null 2>&1 ; check_fail 353 | 354 | info "Should fail with helpful message if lock dir is present" 355 | mkdir -p .git/test-cache/testing 356 | $PROJECT -v -ra master >out 2>err ; check_fail 357 | grep "git-test already in progress" err >/dev/null 2>&1 ; check 358 | grep "(lock: .*.git/test-cache/testing)" err >/dev/null 2>&1 ; check 359 | 360 | info "Re-running git-test should not remove lock dir" 361 | $PROJECT -v -ra master >out 2>err ; check_fail 362 | grep "git-test already in progress" err >/dev/null 2>&1 ; check 363 | grep "(lock: .*.git/test-cache/testing)" err >/dev/null 2>&1 ; check 364 | 365 | info "Check that selective --clear removes correct cache entries" 366 | git reset --hard long-history >out 2>err ; check 367 | git config test.verify "$verify" 368 | setup_for_redo 369 | touch .git/test-cache/${tree_d}_${ver}_fail 370 | ls .git/test-cache/*_*_* | wc -l | grep '^ *5 *$' >/dev/null ; check 371 | $PROJECT -v --clear master~1 ^master~3 >out 2>err ; check 372 | ls .git/test-cache/*_*_* | wc -l | grep '^ *2 *$' >/dev/null ; check 373 | 374 | info "Should present actual ref spec and commit count" 375 | git branch upstream master~2 >/dev/null 2>&1 ; check 376 | git branch --set-upstream-to=upstream >/dev/null 2>&1 ; check 377 | $PROJECT -v --verify=true >out 2>err ; check 378 | grep ".upstream.*will test *2 commits" out >/dev/null 2>&1 ; check 379 | 380 | info "Should show commit table header" 381 | $PROJECT --clear >/dev/null 2>&1 ; check 382 | $PROJECT -v --verify=true >out 2>err ; check 383 | grep "^iter.*commit.*tree.*result$" out err >/dev/null 2>&1 ; check 384 | 385 | info "Should just show version, even when not in a repo" 386 | GIT_DIR=.git/refs $PROJECT --version >out 2>err ; check 387 | grep "Not a git repo" out err >/dev/null 2>&1 ; check_fail 388 | 389 | info "Should not confuse files and branches" 390 | $PROJECT --clear >/dev/null 2>&1 ; check 391 | git checkout -b subject >/dev/null 2>&1 ; check 392 | add_commit "x" "differentiate branches" >/dev/null 2>&1 ; check 393 | $PROJECT -v --verify=true subject ^master >out 2>err ; check 394 | grep "^iter.*commit.*tree.*result$" out err >/dev/null 2>&1 ; check 395 | 396 | info "Should refuse to run if work tree is dirty" 397 | echo "y" > subject 398 | $PROJECT -v -ra master >out 2>err ; check_fail 399 | grep "Cannot test: You have unstaged changes." err >/dev/null ; check 400 | git checkout -- subject >/dev/null 2>&1 ; check 401 | 402 | info "TODO: check output report feature/s" 403 | 404 | if [ $verb -ge 1 ]; then 405 | echo 406 | echo "passed: $pass failed: $fail" 407 | fi 408 | 409 | if test "$fail" -eq 0 ; then exit 0 ; else exit 5 ; fi 410 | --------------------------------------------------------------------------------