{"thread":{"id":"44349","subject":"Prove \"Tests out of sequence\" Error","startedAt":"2016-10-21T04:14:58Z","lastAt":"2021-03-22T13:47:42Z","messageCount":47,"participants":["Lars Schneider","Stefan Beller","Jeff King","Jacob Keller","Junio C Hamano","Ævar Arnfjörð Bjarmason","SZEDER Gábor","Chris Torek","Johannes Schindelin"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"304556","messageId":"D9C1E13F-88A2-483E-A549-1C2294EACFEB@gmail.com","threadId":"44349","inReplyTo":null,"subject":"Prove \"Tests out of sequence\" Error","fromName":"Lars Schneider","fromEmail":"larsxschneider@gmail.com","sentAt":"2016-10-21T04:14:48Z","receivedAt":"2016-10-21T04:14:58Z","isPatch":false,"sender":{"key":"larsxschneider@gmail.com","avatar":"https://avatars.githubusercontent.com/u/477434?v=4"},"body":"Hi,\n\non TravisCI I see these weird \"Tests out of sequence\" errors with prove\nand they seem to not go away. I assume the reason that they not go away\nis that the \".prove\" file is carried over from on build to another (but I can't \nlook into this file on TravisCI).\n\nHas anyone an idea where these errors might come from?\n\n------------------------------------------------------------------------\nt5547-push-quarantine.sh                         (Wstat: 0 Tests: 5 Failed: 0)\n  Parse errors: Tests out of sequence.  Found (2) but expected (3)\n                Tests out of sequence.  Found (3) but expected (4)\n                Tests out of sequence.  Found (4) but expected (5)\n                Bad plan.  You planned 4 tests but ran 5.\nFiles=760, Tests=15109, 679 wallclock secs (21.91 usr  1.78 sys + 320.79 cusr 529.13 csys = 873.61 CPU)\nResult: FAIL\nmake[1]: *** [prove] Error 1\nmake: *** [test] Error 2\n------------------------------------------------------------------------\n\nExample:\nhttps://s3.amazonaws.com/archive.travis-ci.org/jobs/169385219/log.txt\n\nThanks,\nLars"},{"id":"304558","messageId":"CAGZ79kZo3LdcRmrjQTAvgx=H6U2tdjASK3qv5A2K5J2HQ2NvSw@mail.gmail.com","threadId":"44349","inReplyTo":"D9C1E13F-88A2-483E-A549-1C2294EACFEB@gmail.com","subject":"Re: Prove \"Tests out of sequence\" Error","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2016-10-21T06:10:39Z","receivedAt":"2016-10-21T06:10:45Z","isPatch":false,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"On Thu, Oct 20, 2016 at 9:14 PM, Lars Schneider\n<larsxschneider@gmail.com> wrote:\n> Hi,\n>\n> on TravisCI I see these weird \"Tests out of sequence\" errors with prove\n> and they seem to not go away. I assume the reason that they not go away\n> is that the \".prove\" file is carried over from on build to another (but I can't\n> look into this file on TravisCI).\n>\n> Has anyone an idea where these errors might come from?\n>\n> ------------------------------------------------------------------------\n> t5547-push-quarantine.sh                         (Wstat: 0 Tests: 5 Failed: 0)\n\npush quarantine is a new thing made by Jeff 2 weeks ago, IIRC.\n\n>   Parse errors: Tests out of sequence.  Found (2) but expected (3)\n>                 Tests out of sequence.  Found (3) but expected (4)\n>                 Tests out of sequence.  Found (4) but expected (5)\n>                 Bad plan.  You planned 4 tests but ran 5.\n> Files=760, Tests=15109, 679 wallclock secs (21.91 usr  1.78 sys + 320.79 cusr 529.13 csys = 873.61 CPU)\n> Result: FAIL\n> make[1]: *** [prove] Error 1\n> make: *** [test] Error 2\n> ------------------------------------------------------------------------\n>\n> Example:\n> https://s3.amazonaws.com/archive.travis-ci.org/jobs/169385219/log.txt\n>\n> Thanks,\n> Lars\n"},{"id":"304560","messageId":"20161021082035.xad6wfny5i6wtshh@sigill.intra.peff.net","threadId":"44349","inReplyTo":"CAGZ79kZo3LdcRmrjQTAvgx=H6U2tdjASK3qv5A2K5J2HQ2NvSw@mail.gmail.com","subject":"Re: Prove \"Tests out of sequence\" Error","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2016-10-21T08:20:35Z","receivedAt":"2016-10-21T08:20:45Z","isPatch":false,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Thu, Oct 20, 2016 at 11:10:39PM -0700, Stefan Beller wrote:\n\n> > on TravisCI I see these weird \"Tests out of sequence\" errors with prove\n> > and they seem to not go away. I assume the reason that they not go away\n> > is that the \".prove\" file is carried over from on build to another (but I can't\n> > look into this file on TravisCI).\n> >\n> > Has anyone an idea where these errors might come from?\n> >\n> > ------------------------------------------------------------------------\n> > t5547-push-quarantine.sh                         (Wstat: 0 Tests: 5 Failed: 0)\n> \n> push quarantine is a new thing made by Jeff 2 weeks ago, IIRC.\n\nYes, but I do not see how it can trigger this:\n\n> >   Parse errors: Tests out of sequence.  Found (2) but expected (3)\n> >                 Tests out of sequence.  Found (3) but expected (4)\n> >                 Tests out of sequence.  Found (4) but expected (5)\n> >                 Bad plan.  You planned 4 tests but ran 5.\n\nThe TAP output from one of our tests should look something like:\n\n  ok 1 - subject one\n  ok 2 - subject two\n  ok 3 - subject three\n  ok 4 - subject four\n  # passed all 4 test(s)\n  1..4\n\nthe \"plan\" is the bit at the end. That looks like $test_count\naccidentally got incremented by one and we generated something like:\n\n  ok 1 - subject one\n  ok 3 - subject two\n  ok 4 - subject three\n  ok 5 - subject four\n  1..4\n\nwhich would explain the \"out of sequence\" errors as well as the \"planned\n4 but ran 5\".\n\nBut I do not see how the test script could screw that up. The counting\nis handled entirely by the harness in test-lib.sh.\n\nNor do I see how a stale .prove file could matter. It does not store\ninformation about the test plan at all. E.g., here is the entry from\nmine for t5547:\n\n  t5547-push-quarantine.sh:\n    elapsed: 0.0762169361114502\n    gen: 1\n    last_pass_time: 1477037708.741\n    last_result: 0\n    last_run_time: 1477037708.741\n    last_todo: 0\n    seq: 437\n    total_passes: 1\n\nPuzzling.\n\n-Peff\n"},{"id":"304562","messageId":"20161021084348.dp4jfpfownodl7nz@sigill.intra.peff.net","threadId":"44349","inReplyTo":"20161021082035.xad6wfny5i6wtshh@sigill.intra.peff.net","subject":"Re: Prove \"Tests out of sequence\" Error","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2016-10-21T08:43:48Z","receivedAt":"2016-10-21T08:43:55Z","isPatch":false,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Fri, Oct 21, 2016 at 04:20:35AM -0400, Jeff King wrote:\n\n> Yes, but I do not see how it can trigger this:\n> \n> > >   Parse errors: Tests out of sequence.  Found (2) but expected (3)\n> > >                 Tests out of sequence.  Found (3) but expected (4)\n> > >                 Tests out of sequence.  Found (4) but expected (5)\n> > >                 Bad plan.  You planned 4 tests but ran 5.\n> \n> The TAP output from one of our tests should look something like:\n> \n>   ok 1 - subject one\n>   ok 2 - subject two\n>   ok 3 - subject three\n>   ok 4 - subject four\n>   # passed all 4 test(s)\n>   1..4\n> \n> the \"plan\" is the bit at the end. That looks like $test_count\n> accidentally got incremented by one and we generated something like:\n> \n>   ok 1 - subject one\n>   ok 3 - subject two\n>   ok 4 - subject three\n>   ok 5 - subject four\n>   1..4\n> \n> which would explain the \"out of sequence\" errors as well as the \"planned\n> 4 but ran 5\".\n\nHmm, actually the numbering problem is the other way around. It finds 2\nbut expects 3, so it is more like:\n\n  ok 1 - subject one\n  ok 2 - something else stuck in here!\n  ok 2 - subject two\n  ...\n\nwhich gives us a clue. And thanks to TAP auto-numbering, you can also\ntrigger this like:\n\n  ok 1 - subject one\n  ok\n  ok 2 - subject two\n\nThe \"ok\" by itself is taken to mean \"ok 2\". And now I have enough to\ngenerate this locally. t5547 does:\n\n  test_commit ok &&\n  ...\n  git cat-file commit $commit\n\nwhich will print a line with just \"ok\" on it. Normally this is not sent\nto stdout at all; test output goes to /dev/null unless \"--verbose\" is\ngiven. When \"--verbose\" is used, we get all manner of random program\noutput intermingled with our TAP output, which is an accident waiting to\nhappen. Usually nobody cares, because they only use \"--verbose\" when\ndebugging a test individually (and nothing is parsing the TAP output).\nBut I can trigger the problem with:\n\n  prove t5547-push-quarantine.sh :: -v\n\nThe Travis tests do exactly this (along with --tee to actually save the\noutput). It seems like a minor miracle that this is the first test\noutput that has actually triggered as TAP input. I'd suggest that the\nproblem is not in the test, though, but that our \"--verbose\" option is\nunsuitable for using with a TAP harness.\n\nThe obvious fix would be to send \"--verbose\" output to stderr, but I\nsuspect that would end up annoying for people who do:\n\n  ./t5547-push-quarantine.sh -v | less\n\nto read long output. Probably we need some option like \"--log\" which\nlogs in the same way that \"--tee\" does, but _without_ sending the data\nto stdout. Naively, that just means replacing the \"tee\" invocation with\n\"cat\", but I suspect it will be a lot more complicated than that,\nbecause we still need to let the TAP output go to stdout.\n\n-Peff\n"},{"id":"304566","messageId":"20161021104107.vh3bjx6x6pd6izat@sigill.intra.peff.net","threadId":"44349","inReplyTo":"20161021084348.dp4jfpfownodl7nz@sigill.intra.peff.net","subject":"[PATCH 0/3] fix travis TAP/--verbose conflict","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2016-10-21T10:41:07Z","receivedAt":"2016-10-21T10:41:15Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Fri, Oct 21, 2016 at 04:43:48AM -0400, Jeff King wrote:\n\n> The obvious fix would be to send \"--verbose\" output to stderr, but I\n> suspect that would end up annoying for people who do:\n> \n>   ./t5547-push-quarantine.sh -v | less\n> \n> to read long output. Probably we need some option like \"--log\" which\n> logs in the same way that \"--tee\" does, but _without_ sending the data\n> to stdout. Naively, that just means replacing the \"tee\" invocation with\n> \"cat\", but I suspect it will be a lot more complicated than that,\n> because we still need to let the TAP output go to stdout.\n\nYeah, it was definitely a lot more complicated. This patch series fixes\nit.\n\n  [1/3]: test-lib: handle TEST_OUTPUT_DIRECTORY with spaces\n  [2/3]: test-lib: add --verbose-log option\n  [3/3]: travis: use --verbose-log test option\n\n .travis.yml   |  2 +-\n t/README      |  6 ++++++\n t/test-lib.sh | 24 ++++++++++++++++++++----\n 3 files changed, 27 insertions(+), 5 deletions(-)\n\n-Peff\n"},{"id":"304567","messageId":"20161021104210.lvmet65avogogiqo@sigill.intra.peff.net","threadId":"44349","inReplyTo":"20161021104107.vh3bjx6x6pd6izat@sigill.intra.peff.net","subject":"[PATCH 1/3] test-lib: handle TEST_OUTPUT_DIRECTORY with spaces","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2016-10-21T10:42:10Z","receivedAt":"2016-10-21T10:42:17Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"We are careful in test_done to handle a results directory\nwith a space in it, but the \"--tee\" code path does not.\nDoing:\n\n  export TEST_OUTPUT_DIRECTORY='/tmp/path with spaces'\n  ./t000-init.sh --tee\n\nresults in errors. Let's consistently double-quote our path\nvariables so that this works.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\nI imagine nobody cares much about this, but I just happened to notice it\nwhile writing the rest of the patches (and it's obviously quite easy to\nfix).\n\n t/test-lib.sh | 4 ++--\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\ndiff --git a/t/test-lib.sh b/t/test-lib.sh\nindex 11562bde10..33cbbb7806 100644\n--- a/t/test-lib.sh\n+++ b/t/test-lib.sh\n@@ -58,8 +58,8 @@ done,*)\n \tmkdir -p \"$TEST_OUTPUT_DIRECTORY/test-results\"\n \tBASE=\"$TEST_OUTPUT_DIRECTORY/test-results/$(basename \"$0\" .sh)\"\n \t(GIT_TEST_TEE_STARTED=done ${SHELL_PATH} \"$0\" \"$@\" 2>&1;\n-\t echo $? > $BASE.exit) | tee $BASE.out\n-\ttest \"$(cat $BASE.exit)\" = 0\n+\t echo $? >\"$BASE.exit\") | tee \"$BASE.out\"\n+\ttest \"$(cat \"$BASE.exit\")\" = 0\n \texit\n \t;;\n esac\n-- \n2.10.1.776.ge0e381e\n\n"},{"id":"304568","messageId":"20161021104759.hx6q7u66r3ll7l44@sigill.intra.peff.net","threadId":"44349","inReplyTo":"20161021104107.vh3bjx6x6pd6izat@sigill.intra.peff.net","subject":"[PATCH 2/3] test-lib: add --verbose-log option","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2016-10-21T10:48:00Z","receivedAt":"2016-10-21T10:48:07Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"The \"--verbose\" option redirects output from arbitrary\ntest commands to stdout. This is useful for examining the\noutput manually, like:\n\n  ./t5547-push-quarantine.sh -v | less\n\nBut it also means that the output is intermingled with the\nTAP directives, which can confuse a TAP parser like \"prove\".\nThis has always been a potential problem, but became an\nissue recently when one test happened to output the word\n\"ok\" on a line by itself, which prove interprets as a test\nsuccess:\n\n  $ prove t5547-push-quarantine.sh :: -v\n  t5547-push-quarantine.sh .. 1/? To dest.git\n   * [new branch]      HEAD -> master\n  To dest.git\n   ! [remote rejected] reject -> reject (pre-receive hook declined)\n  error: failed to push some refs to 'dest.git'\n  fatal: git cat-file d08c8eba97f4e683ece08654c7c8d2ba0c03b129: bad file\n  t5547-push-quarantine.sh .. Failed -1/4 subtests\n\n  Test Summary Report\n  -------------------\n  t5547-push-quarantine.sh (Wstat: 0 Tests: 5 Failed: 0)\n    Parse errors: Tests out of sequence.  Found (2) but expected (3)\n                  Tests out of sequence.  Found (3) but expected (4)\n                  Tests out of sequence.  Found (4) but expected (5)\n                  Bad plan.  You planned 4 tests but ran 5.\n  Files=1, Tests=5,  0 wallclock secs ( 0.01 usr +  0.01 sys =  0.02 CPU)\n  Result: FAIL\n\nOne answer is \"if it hurts, don't do it\", but that's not\nquite the whole story. The Travis tests use \"--verbose\n--tee\" so that they can get the benefit of prove's parallel\noptions, along with a verbose log in case there is a\nfailure. We just need the verbose output to go to the log,\nbut keep stdout clean.\n\nGetting this right turns out to be surprisingly difficult.\nHere's the progression of alternatives I considered:\n\n 1. Add an option to write verbose output to stderr. This is\n    hard to capture, though, because we want each test to\n    have its own log (because they're all run in parallel\n    and the jumbled output would be useless).\n\n 2. Add an option to write verbose output to a file in\n    test-results. This works, but the log is missing all of\n    the non-verbose output, which gives context.\n\n 3. Like (2), but teach say_color() to additionally output\n    to the log. This mostly works, but misses any output\n    that happens outside of the say() functions (which isn't\n    a lot, but is a potential maintenance headache).\n\n 4. Like (2), but make the log file the same as the \"--tee\"\n    file. That almost works, but now we have two processes\n    opening the same file. That gives us two separate\n    descriptors, each with their own idea of the current\n    position. They'll each start writing at offset 0, and\n    overwrite each other's data.\n\n 5. Like (4), but in each case open the file for appending.\n    That atomically positions each write at the end of the\n    file.\n\n    It's possible we may still get sheared writes between\n    the two processes, but this is already the case when\n    writing to stdout. It's not a problem in practice\n    because the test harness generally waits for snippets to\n    finish before writing the TAP output.\n\n    We can ignore buffering issues with tee, because POSIX\n    mandates that it does not buffer. Likewise, POSIX\n    specifies \"tee -a\", so it should be available\n    everywhere.\n\nThis patch implements option (5), which seems to work well\nin practice.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\nArguably we don't need a new option for this, and could just do it\nautomatically. I wasn't sure when, though:\n\n  - if \"--tee --verbose\" triggers it, then somebody running that outside\n    of \"prove\" who _wants_ the verbose output on stdout (because they're\n    looking at it, but also want to save a copy to the log) would be\n    regressed\n\n  - possibly \"--tee\" could just always write verbose output to the\n    logfile (but not stdout). That's kind of weirdly magical, and we'd\n    have to update the travis invocation anyway.\n\nSo I went with a new option which implies the other ones. No chance of\nregression, and it's easy to explain.\n\n t/README      |  6 ++++++\n t/test-lib.sh | 22 +++++++++++++++++++---\n 2 files changed, 25 insertions(+), 3 deletions(-)\n\ndiff --git a/t/README b/t/README\nindex 0f764c0aef..4982d1c521 100644\n--- a/t/README\n+++ b/t/README\n@@ -153,6 +153,12 @@ appropriately before running \"make\".\n \tAs the names depend on the tests' file names, it is safe to\n \trun the tests with this option in parallel.\n \n+--verbose-log::\n+\tWrite verbose output to the same logfile as `--tee`, but do\n+\t_not_ write it to stdout. Unlike `--tee --verbose`, this option\n+\tis safe to use when stdout is being consumed by a TAP parser\n+\tlike `prove`. Implies `--tee` and `--verbose`.\n+\n --with-dashes::\n \tBy default tests are run without dashed forms of\n \tcommands (like git-commit) in the PATH (it only uses\ndiff --git a/t/test-lib.sh b/t/test-lib.sh\nindex 33cbbb7806..85946ec40d 100644\n--- a/t/test-lib.sh\n+++ b/t/test-lib.sh\n@@ -54,11 +54,21 @@ case \"$GIT_TEST_TEE_STARTED, $* \" in\n done,*)\n \t# do not redirect again\n \t;;\n-*' --tee '*|*' --va'*)\n+*' --tee '*|*' --va'*|*' --verbose-log '*)\n \tmkdir -p \"$TEST_OUTPUT_DIRECTORY/test-results\"\n \tBASE=\"$TEST_OUTPUT_DIRECTORY/test-results/$(basename \"$0\" .sh)\"\n+\n+\t# Make this filename available to the sub-process in case it is using\n+\t# --verbose-log.\n+\tGIT_TEST_TEE_OUTPUT_FILE=$BASE.out\n+\texport GIT_TEST_TEE_OUTPUT_FILE\n+\n+\t# Truncate before calling \"tee -a\" to get rid of the results\n+\t# from any previous runs.\n+\t>\"$GIT_TEST_TEE_OUTPUT_FILE\"\n+\n \t(GIT_TEST_TEE_STARTED=done ${SHELL_PATH} \"$0\" \"$@\" 2>&1;\n-\t echo $? >\"$BASE.exit\") | tee \"$BASE.out\"\n+\t echo $? >\"$BASE.exit\") | tee -a \"$GIT_TEST_TEE_OUTPUT_FILE\"\n \ttest \"$(cat \"$BASE.exit\")\" = 0\n \texit\n \t;;\n@@ -246,6 +256,9 @@ do\n \t\ttrace=t\n \t\tverbose=t\n \t\tshift ;;\n+\t--verbose-log)\n+\t\tverbose_log=t\n+\t\tshift ;;\n \t*)\n \t\techo \"error: unknown test option '$1'\" >&2; exit 1 ;;\n \tesac\n@@ -319,7 +332,10 @@ fi\n \n exec 5>&1\n exec 6<&0\n-if test \"$verbose\" = \"t\"\n+if test \"$verbose_log\" = \"t\"\n+then\n+\texec 3>>\"$GIT_TEST_TEE_OUTPUT_FILE\" 4>&3\n+elif test \"$verbose\" = \"t\"\n then\n \texec 4>&2 3>&1\n else\n-- \n2.10.1.776.ge0e381e\n\n"},{"id":"304569","messageId":"20161021104812.b5mk2s3ve2iotv3j@sigill.intra.peff.net","threadId":"44349","inReplyTo":"20161021104107.vh3bjx6x6pd6izat@sigill.intra.peff.net","subject":"[PATCH 3/3] travis: use --verbose-log test option","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2016-10-21T10:48:12Z","receivedAt":"2016-10-21T10:48:18Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"Because we run the tests via \"prove\", the output from\n\"--verbose\" may interfere with our TAP output. Using\n\"--verbose-log\" solves this while letting us retain our\non-disk log.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n .travis.yml | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/.travis.yml b/.travis.yml\nindex 37a1e1fb6d..9a65514d82 100644\n--- a/.travis.yml\n+++ b/.travis.yml\n@@ -31,7 +31,7 @@ env:\n     - LINUX_GIT_LFS_VERSION=\"1.2.0\"\n     - DEFAULT_TEST_TARGET=prove\n     - GIT_PROVE_OPTS=\"--timer --jobs 3 --state=failed,slow,save\"\n-    - GIT_TEST_OPTS=\"--verbose --tee\"\n+    - GIT_TEST_OPTS=\"--verbose-log\"\n     - GIT_TEST_HTTPD=true\n     - GIT_TEST_CLONE_2GB=YesPlease\n     # t9810 occasionally fails on Travis CI OS X\n-- \n2.10.1.776.ge0e381e\n"},{"id":"304611","messageId":"CA+P7+xozpk2-WZUYtaKmCazG3Owzpa3bsDejyUuGj0YEQnJWJg@mail.gmail.com","threadId":"44349","inReplyTo":"20161021084348.dp4jfpfownodl7nz@sigill.intra.peff.net","subject":"Re: Prove \"Tests out of sequence\" Error","fromName":"Jacob Keller","fromEmail":"jacob.keller@gmail.com","sentAt":"2016-10-21T15:29:49Z","receivedAt":"2016-10-21T15:30:17Z","isPatch":false,"sender":{"key":"jacob.keller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/874719?v=4"},"body":"On Fri, Oct 21, 2016 at 1:43 AM, Jeff King <peff@peff.net> wrote:\n>   prove t5547-push-quarantine.sh :: -v\n>\n> The Travis tests do exactly this (along with --tee to actually save the\n> output). It seems like a minor miracle that this is the first test\n> output that has actually triggered as TAP input. I'd suggest that the\n> problem is not in the test, though, but that our \"--verbose\" option is\n> unsuitable for using with a TAP harness.\n>\n> The obvious fix would be to send \"--verbose\" output to stderr, but I\n> suspect that would end up annoying for people who do:\n>\n>   ./t5547-push-quarantine.sh -v | less\n>\n> to read long output. Probably we need some option like \"--log\" which\n> logs in the same way that \"--tee\" does, but _without_ sending the data\n> to stdout. Naively, that just means replacing the \"tee\" invocation with\n> \"cat\", but I suspect it will be a lot more complicated than that,\n> because we still need to let the TAP output go to stdout.\n>\n> -Peff\n\nCan we determine that we're running with something monitoring the TAP\noutput? Because then we could make verbose go to stderr instead\ndynamically?\n\nThanks,\nJake\n"},{"id":"304612","messageId":"20161021153534.mjed3sqhxjelxzwx@sigill.intra.peff.net","threadId":"44349","inReplyTo":"CA+P7+xozpk2-WZUYtaKmCazG3Owzpa3bsDejyUuGj0YEQnJWJg@mail.gmail.com","subject":"Re: Prove \"Tests out of sequence\" Error","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2016-10-21T15:35:34Z","receivedAt":"2016-10-21T15:35:41Z","isPatch":false,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Fri, Oct 21, 2016 at 08:29:49AM -0700, Jacob Keller wrote:\n\n> > The Travis tests do exactly this (along with --tee to actually save the\n> > output). It seems like a minor miracle that this is the first test\n> > output that has actually triggered as TAP input. I'd suggest that the\n> > problem is not in the test, though, but that our \"--verbose\" option is\n> > unsuitable for using with a TAP harness.\n> >\n> > The obvious fix would be to send \"--verbose\" output to stderr, but I\n> > suspect that would end up annoying for people who do:\n> >\n> >   ./t5547-push-quarantine.sh -v | less\n> >\n> > to read long output. Probably we need some option like \"--log\" which\n> > logs in the same way that \"--tee\" does, but _without_ sending the data\n> > to stdout. Naively, that just means replacing the \"tee\" invocation with\n> > \"cat\", but I suspect it will be a lot more complicated than that,\n> > because we still need to let the TAP output go to stdout.\n> \n> Can we determine that we're running with something monitoring the TAP\n> output? Because then we could make verbose go to stderr instead\n> dynamically?\n\nI think $HARNESS_ACTIVE could tell us that. But the hard part isn't\nactivating it; it's directing the verbose output to the log without\nsending it to stdout.\n\nSee the patch I posted later in the thread, and my musings on\nauto-activating it. I guess we could do so safely when we see\n$HARNESS_ACTIVE along with \"--tee\" and \"--verbose\", though I don't know\nif it's worth the trouble.\n\nFor $HARNESS_ACTIVE with _just_ \"--verbose\", I don't think it would be a\ngood idea to activate it. We should either silently ignore --verbose\nthen, or complain and die.\n\n-Peff\n"},{"id":"304614","messageId":"CA+P7+xrix3UZDEE1swG_=jBLwWHA04KoYikWBBMNx7aa=Hevmw@mail.gmail.com","threadId":"44349","inReplyTo":"20161021153534.mjed3sqhxjelxzwx@sigill.intra.peff.net","subject":"Re: Prove \"Tests out of sequence\" Error","fromName":"Jacob Keller","fromEmail":"jacob.keller@gmail.com","sentAt":"2016-10-21T15:42:58Z","receivedAt":"2016-10-21T15:43:26Z","isPatch":false,"sender":{"key":"jacob.keller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/874719?v=4"},"body":"On Fri, Oct 21, 2016 at 8:35 AM, Jeff King <peff@peff.net> wrote:\n> On Fri, Oct 21, 2016 at 08:29:49AM -0700, Jacob Keller wrote:\n>\n>> > The Travis tests do exactly this (along with --tee to actually save the\n>> > output). It seems like a minor miracle that this is the first test\n>> > output that has actually triggered as TAP input. I'd suggest that the\n>> > problem is not in the test, though, but that our \"--verbose\" option is\n>> > unsuitable for using with a TAP harness.\n>> >\n>> > The obvious fix would be to send \"--verbose\" output to stderr, but I\n>> > suspect that would end up annoying for people who do:\n>> >\n>> >   ./t5547-push-quarantine.sh -v | less\n>> >\n>> > to read long output. Probably we need some option like \"--log\" which\n>> > logs in the same way that \"--tee\" does, but _without_ sending the data\n>> > to stdout. Naively, that just means replacing the \"tee\" invocation with\n>> > \"cat\", but I suspect it will be a lot more complicated than that,\n>> > because we still need to let the TAP output go to stdout.\n>>\n>> Can we determine that we're running with something monitoring the TAP\n>> output? Because then we could make verbose go to stderr instead\n>> dynamically?\n>\n> I think $HARNESS_ACTIVE could tell us that. But the hard part isn't\n> activating it; it's directing the verbose output to the log without\n> sending it to stdout.\n>\n> See the patch I posted later in the thread, and my musings on\n> auto-activating it. I guess we could do so safely when we see\n> $HARNESS_ACTIVE along with \"--tee\" and \"--verbose\", though I don't know\n> if it's worth the trouble.\n>\n> For $HARNESS_ACTIVE with _just_ \"--verbose\", I don't think it would be a\n> good idea to activate it. We should either silently ignore --verbose\n> then, or complain and die.\n>\n\nWe should probably do that to make sure people realize what might\nhappen. I read your series and it has a good explanation of the\npossible alternatives. I like the approach you chose.\n\nThanks,\nJake\n\n> -Peff\n"},{"id":"304615","messageId":"20161021154840.pdmb5c3gjharsrxe@sigill.intra.peff.net","threadId":"44349","inReplyTo":"CA+P7+xrix3UZDEE1swG_=jBLwWHA04KoYikWBBMNx7aa=Hevmw@mail.gmail.com","subject":"Re: Prove \"Tests out of sequence\" Error","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2016-10-21T15:48:40Z","receivedAt":"2016-10-21T15:48:50Z","isPatch":false,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Fri, Oct 21, 2016 at 08:42:58AM -0700, Jacob Keller wrote:\n\n> > For $HARNESS_ACTIVE with _just_ \"--verbose\", I don't think it would be a\n> > good idea to activate it. We should either silently ignore --verbose\n> > then, or complain and die.\n> \n> We should probably do that to make sure people realize what might\n> happen. I read your series and it has a good explanation of the\n> possible alternatives. I like the approach you chose.\n\nThanks. Do you want to make a patch on top of my series?\n\n-Peff\n"},{"id":"304618","messageId":"CA+P7+xp8TDL59tQgCpmMDJ-BspA1CF6YsnuGMXas1bx_X0qJoA@mail.gmail.com","threadId":"44349","inReplyTo":"20161021154840.pdmb5c3gjharsrxe@sigill.intra.peff.net","subject":"Re: Prove \"Tests out of sequence\" Error","fromName":"Jacob Keller","fromEmail":"jacob.keller@gmail.com","sentAt":"2016-10-21T16:15:28Z","receivedAt":"2016-10-21T16:15:55Z","isPatch":false,"sender":{"key":"jacob.keller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/874719?v=4"},"body":"On Fri, Oct 21, 2016 at 8:48 AM, Jeff King <peff@peff.net> wrote:\n> On Fri, Oct 21, 2016 at 08:42:58AM -0700, Jacob Keller wrote:\n>\n>> > For $HARNESS_ACTIVE with _just_ \"--verbose\", I don't think it would be a\n>> > good idea to activate it. We should either silently ignore --verbose\n>> > then, or complain and die.\n>>\n>> We should probably do that to make sure people realize what might\n>> happen. I read your series and it has a good explanation of the\n>> possible alternatives. I like the approach you chose.\n>\n> Thanks. Do you want to make a patch on top of my series?\n>\n> -Peff\n\nI am not sure I will find time to do it today, so it wouldn't be for a\nfew more days.\n\nThanks,\nJake\n"},{"id":"304622","messageId":"xmqq7f91d3tb.fsf@gitster.mtv.corp.google.com","threadId":"44349","inReplyTo":"20161021104759.hx6q7u66r3ll7l44@sigill.intra.peff.net","subject":"Re: [PATCH 2/3] test-lib: add --verbose-log option","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-10-21T17:12:16Z","receivedAt":"2016-10-21T17:12:25Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n> This patch implements option (5), which seems to work well\n> in practice.\n\nYour journey to reach this final design and implementation may have\nbeen painful---I really like the end result.  This was an accident\nwaiting to happen.\n\nThanks for fixing it.\n\n> @@ -319,7 +332,10 @@ fi\n>  \n>  exec 5>&1\n>  exec 6<&0\n> -if test \"$verbose\" = \"t\"\n> +if test \"$verbose_log\" = \"t\"\n> +then\n> +\texec 3>>\"$GIT_TEST_TEE_OUTPUT_FILE\" 4>&3\n> +elif test \"$verbose\" = \"t\"\n>  then\n>  \texec 4>&2 3>&1\n>  else\n\nOK, unlike \"verbose\" case where we give 3 (saved stdout) to 1 and 4\n(saved stderr) to 2, we send 3 to the output file and 4 to the same.\n\nMakes sense.\n"},{"id":"304623","messageId":"CAGZ79kaPAHdRsTXFUqjB843kSq7jVzy7zf4r7hT4YtyxeCPRdg@mail.gmail.com","threadId":"44349","inReplyTo":"20161021104107.vh3bjx6x6pd6izat@sigill.intra.peff.net","subject":"Re: [PATCH 0/3] fix travis TAP/--verbose conflict","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2016-10-21T17:19:02Z","receivedAt":"2016-10-21T17:19:07Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"On Fri, Oct 21, 2016 at 3:41 AM, Jeff King <peff@peff.net> wrote:\n> On Fri, Oct 21, 2016 at 04:43:48AM -0400, Jeff King wrote:\n>\n>> The obvious fix would be to send \"--verbose\" output to stderr, but I\n>> suspect that would end up annoying for people who do:\n>>\n>>   ./t5547-push-quarantine.sh -v | less\n>>\n>> to read long output. Probably we need some option like \"--log\" which\n>> logs in the same way that \"--tee\" does, but _without_ sending the data\n>> to stdout. Naively, that just means replacing the \"tee\" invocation with\n>> \"cat\", but I suspect it will be a lot more complicated than that,\n>> because we still need to let the TAP output go to stdout.\n>\n> Yeah, it was definitely a lot more complicated. This patch series fixes\n> it.\n>\n>   [1/3]: test-lib: handle TEST_OUTPUT_DIRECTORY with spaces\n>   [2/3]: test-lib: add --verbose-log option\n>   [3/3]: travis: use --verbose-log test option\n\nAll patches look good to me\n(1&3 are obvious, and 2 is very well described).\n\nThanks,\nStefan\n"},{"id":"304647","messageId":"20161021214622.csroal7hws3cnszg@sigill.intra.peff.net","threadId":"44349","inReplyTo":"xmqq7f91d3tb.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH 2/3] test-lib: add --verbose-log option","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2016-10-21T21:46:23Z","receivedAt":"2016-10-21T21:46:32Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Fri, Oct 21, 2016 at 10:12:16AM -0700, Junio C Hamano wrote:\n\n> > -if test \"$verbose\" = \"t\"\n> > +if test \"$verbose_log\" = \"t\"\n> > +then\n> > +\texec 3>>\"$GIT_TEST_TEE_OUTPUT_FILE\" 4>&3\n> > +elif test \"$verbose\" = \"t\"\n> >  then\n> >  \texec 4>&2 3>&1\n> >  else\n> \n> OK, unlike \"verbose\" case where we give 3 (saved stdout) to 1 and 4\n> (saved stderr) to 2, we send 3 to the output file and 4 to the same.\n\nYour comment made me double-check whether we ought to be separating the\ntwo in any way. Because my statement earlier in the thread that the\n--verbose output goes to stdout is not _entirely_ true. It goes to\nstdout and stderr, as written by the various programs in the tests.\n\nSo:\n\n  ./t0000-whatever.sh -v | less\n\nisn't quite sufficient. You really do want \"2>&1\" in there to catch\neverything.\n\nBut for the case of \"--tee\", we already do so, in order to make sure it\nall goes to the logfile. And since this option always implies \"--tee\",\nit is correct here to send \"3\" and \"4\" to the same place.\n\nSo the patch is correct. But when I said earlier that people might be\nannoyed if we just sent --verbose output to stderr, that probably isn't\ntrue. We could perhaps make:\n\n  prove t0000-whatever.sh :: -v\n\n\"just work\" by sending all of the verbose output to stderr. I don't\nthink it really matters, though. Nobody is doing that, because it's\npointless without \"--tee\".\n\n-Peff\n"},{"id":"304667","messageId":"20161022044506.vba6g2q25yxa2air@sigill.intra.peff.net","threadId":"44349","inReplyTo":"CA+P7+xp8TDL59tQgCpmMDJ-BspA1CF6YsnuGMXas1bx_X0qJoA@mail.gmail.com","subject":"[PATCH 4/3] test-lib: bail out when \"-v\" used under \"prove\"","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2016-10-22T04:45:06Z","receivedAt":"2016-10-22T04:45:16Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Fri, Oct 21, 2016 at 09:15:28AM -0700, Jacob Keller wrote:\n\n> >> > For $HARNESS_ACTIVE with _just_ \"--verbose\", I don't think it would be a\n> >> > good idea to activate it. We should either silently ignore --verbose\n> >> > then, or complain and die.\n> >>\n> >> We should probably do that to make sure people realize what might\n> >> happen. I read your series and it has a good explanation of the\n> >> possible alternatives. I like the approach you chose.\n> >\n> > Thanks. Do you want to make a patch on top of my series?\n> \n> I am not sure I will find time to do it today, so it wouldn't be for a\n> few more days.\n\nI thought I'd just knock this out in 5 minutes before I forgot about it.\nBut as with so many things, getting it right proved slightly harder than\nI thought. But I did learn about TAP's \"Bail out!\" directive. And\napparently you can pass it back arbitrary YAML (!). And the \"--verbose\"\noutput really is violating the spec, and they claim that Test::Harness\nwill eventually be tightened to complain (though that was in 2007, and\nit still hasn't happened, so...).\n\nAnyway. Here is the patch I came up with (on top of the others).\n\n-- >8 --\nSubject: test-lib: bail out when \"-v\" used under \"prove\"\n\nWhen there is a TAP harness consuming the output of our test\nscripts, the \"--verbose\" breaks the output by mingling\ntest command output with TAP. Because the TAP::Harness\nmodule used by \"prove\" is fairly lenient, this _usually_\nworks, but it violates the spec, and things get very\nconfusing if the commands happen to output a line that looks\nlike TAP (e.g., the word \"ok\" on its own line).\n\nLet's detect this situation and complain. Just calling\nerror() isn't great, though; prove will tell us that the\nscript failed, but the message doesn't make it through to\nthe user. Instead, we can use the special TAP signal \"Bail\nout!\". This not only shows the message to the user, but\ninstructs the harness to stop running the tests entirely.\nThis is exactly what we want here, as the problem is in the\ncommand-line options, and every test script would produce\nthe same error.\n\nThe result looks like this (the first \"Bailout called\" line\nis in red if prove uses color on your terminal):\n\n $ make GIT_TEST_OPTS='--verbose --tee'\n rm -f -r 'test-results'\n *** prove ***\n Bailout called.  Further testing stopped:  verbose mode forbidden under TAP harness; try --verbose-log\n FAILED--Further testing stopped: verbose mode forbidden under TAP harness; try --verbose-log\n Makefile:39: recipe for target 'prove' failed\n make: *** [prove] Error 255\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n t/test-lib.sh | 10 ++++++++++\n 1 file changed, 10 insertions(+)\n\ndiff --git a/t/test-lib.sh b/t/test-lib.sh\nindex 85946ec40d..b859db61ac 100644\n--- a/t/test-lib.sh\n+++ b/t/test-lib.sh\n@@ -321,6 +321,16 @@ say () {\n \tsay_color info \"$*\"\n }\n \n+if test -n \"$HARNESS_ACTIVE\"\n+then\n+\tif test \"$verbose\" = t || test -n \"$verbose_only\"\n+\tthen\n+\t\tprintf 'Bail out! %s\\n' \\\n+\t\t 'verbose mode forbidden under TAP harness; try --verbose-log'\n+\t\texit 1\n+\tfi\n+fi\n+\n test \"${test_description}\" != \"\" ||\n error \"Test script did not set test_description.\"\n \n-- \n2.10.1.776.ge0e381e\n\n"},{"id":"304670","messageId":"CA+P7+xqwLiwSWAwsreW=nCMU5QiNq4AUGPMawzLofB3vyAqYYw@mail.gmail.com","threadId":"44349","inReplyTo":"20161022044506.vba6g2q25yxa2air@sigill.intra.peff.net","subject":"Re: [PATCH 4/3] test-lib: bail out when \"-v\" used under \"prove\"","fromName":"Jacob Keller","fromEmail":"jacob.keller@gmail.com","sentAt":"2016-10-22T05:25:42Z","receivedAt":"2016-10-22T05:26:08Z","isPatch":true,"sender":{"key":"jacob.keller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/874719?v=4"},"body":"On Fri, Oct 21, 2016 at 9:45 PM, Jeff King <peff@peff.net> wrote:\n> I thought I'd just knock this out in 5 minutes before I forgot about it.\n> But as with so many things, getting it right proved slightly harder than\n> I thought.\n\nAlways seems to be that way, doesn't it?\n\n> But I did learn about TAP's \"Bail out!\" directive. And\n> apparently you can pass it back arbitrary YAML (!). And the \"--verbose\"\n> output really is violating the spec, and they claim that Test::Harness\n> will eventually be tightened to complain (though that was in 2007, and\n> it still hasn't happened, so...).\n>\n> Anyway. Here is the patch I came up with (on top of the others).\n>\n\nNice.\n\n> -- >8 --\n> Subject: test-lib: bail out when \"-v\" used under \"prove\"\n>\n> When there is a TAP harness consuming the output of our test\n> scripts, the \"--verbose\" breaks the output by mingling\n> test command output with TAP. Because the TAP::Harness\n> module used by \"prove\" is fairly lenient, this _usually_\n> works, but it violates the spec, and things get very\n> confusing if the commands happen to output a line that looks\n> like TAP (e.g., the word \"ok\" on its own line).\n>\n> Let's detect this situation and complain. Just calling\n> error() isn't great, though; prove will tell us that the\n> script failed, but the message doesn't make it through to\n> the user. Instead, we can use the special TAP signal \"Bail\n> out!\". This not only shows the message to the user, but\n> instructs the harness to stop running the tests entirely.\n> This is exactly what we want here, as the problem is in the\n> command-line options, and every test script would produce\n> the same error.\n>\n> The result looks like this (the first \"Bailout called\" line\n> is in red if prove uses color on your terminal):\n>\n>  $ make GIT_TEST_OPTS='--verbose --tee'\n>  rm -f -r 'test-results'\n>  *** prove ***\n>  Bailout called.  Further testing stopped:  verbose mode forbidden under TAP harness; try --verbose-log\n>  FAILED--Further testing stopped: verbose mode forbidden under TAP harness; try --verbose-log\n>  Makefile:39: recipe for target 'prove' failed\n>  make: *** [prove] Error 255\n>\n\nNice that makes sense.\n\n> Signed-off-by: Jeff King <peff@peff.net>\n> ---\n>  t/test-lib.sh | 10 ++++++++++\n>  1 file changed, 10 insertions(+)\n>\n> diff --git a/t/test-lib.sh b/t/test-lib.sh\n> index 85946ec40d..b859db61ac 100644\n> --- a/t/test-lib.sh\n> +++ b/t/test-lib.sh\n> @@ -321,6 +321,16 @@ say () {\n>         say_color info \"$*\"\n>  }\n>\n> +if test -n \"$HARNESS_ACTIVE\"\n> +then\n> +       if test \"$verbose\" = t || test -n \"$verbose_only\"\n> +       then\n> +               printf 'Bail out! %s\\n' \\\n> +                'verbose mode forbidden under TAP harness; try --verbose-log'\n> +               exit 1\n> +       fi\n> +fi\n> +\n\nNot too much code, so that's good. I like it.\n\nThanks,\nJake\n\n>  test \"${test_description}\" != \"\" ||\n>  error \"Test script did not set test_description.\"\n>\n> --\n> 2.10.1.776.ge0e381e\n>\n"},{"id":"304786","messageId":"96985FB6-67D9-4146-BDBA-E22699061885@gmail.com","threadId":"44349","inReplyTo":"20161021104107.vh3bjx6x6pd6izat@sigill.intra.peff.net","subject":"Re: [PATCH 0/3] fix travis TAP/--verbose conflict","fromName":"Lars Schneider","fromEmail":"larsxschneider@gmail.com","sentAt":"2016-10-24T18:06:10Z","receivedAt":"2016-10-24T18:11:31Z","isPatch":true,"sender":{"key":"larsxschneider@gmail.com","avatar":"https://avatars.githubusercontent.com/u/477434?v=4"},"body":"\n> On 21 Oct 2016, at 12:41, Jeff King <peff@peff.net> wrote:\n> \n> On Fri, Oct 21, 2016 at 04:43:48AM -0400, Jeff King wrote:\n> \n>> The obvious fix would be to send \"--verbose\" output to stderr, but I\n>> suspect that would end up annoying for people who do:\n>> \n>>  ./t5547-push-quarantine.sh -v | less\n>> \n>> to read long output. Probably we need some option like \"--log\" which\n>> logs in the same way that \"--tee\" does, but _without_ sending the data\n>> to stdout. Naively, that just means replacing the \"tee\" invocation with\n>> \"cat\", but I suspect it will be a lot more complicated than that,\n>> because we still need to let the TAP output go to stdout.\n> \n> Yeah, it was definitely a lot more complicated. This patch series fixes\n> it.\n\nThanks a lot for this detailed and quick fix :-)\n\nCheers,\nLars\n\n"},{"id":"417998","messageId":"87tupwj5y5.fsf@evledraar.gmail.com","threadId":"44349","inReplyTo":"20161021104759.hx6q7u66r3ll7l44@sigill.intra.peff.net","subject":"[PATCH/RFC] test-lib: make --verbose work under prove","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-02-28T20:25:06Z","receivedAt":"2021-02-28T20:26:06Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nAs explained in 452320f1f5 (test-lib: add --verbose-log option,\n2016-10-21) combining the \"--verbose\" option with the TAP-parsing\n\"prove\" utility would result in broken tests.\n\nThis is because \"prove\" expects valid TAP on stdout, and if there's a\nstray line starting with \"ok\", or other invalid TAP syntax it will\nerror on the test.\n\nIn 452320f1f5 numerous potential methods are discussed for dealing\nwith this problem, but not the one implemented here, which I think is\na much simpler approach.\n\nWhen we detect that we're running under \"prove\" we prefix all\nlegitimate TAP directives with \"GIT_TEST_TEE_STARTED\":\n\n    $ GIT_TEST_TEE_STARTED=1 ./t5547-push-quarantine.sh\n    GIT_TEST_TEE_STARTED ok 1 - create picky dest repo\n    GIT_TEST_TEE_STARTED ok 2 - accepted objects work\n    [...]\n    GIT_TEST_TEE_STARTED 1..6\n\nThen, instead of piping the output to \"tee -a\" we pipe it to a helper\nwhich first converts \"ok\" and other TAP syntax to e.g. \"\\ok\", and then\nstrips that \"GIT_TEST_TEE_STARTED \" prefix from the start of the line.\n\nThe end result is that we're guaranteed to have valid TAP syntax on\nstdout. We can thus get rid of the --verbose-log special-case. Since\nthat option was meant to get around the TAP issue let's simply make it\nan alias for \"--verbose --tee\".\n\nWe can also revert relevant parts of 88c6e9d31c (test-lib: --valgrind\nshould not override --verbose-log, 2017-09-05) and\nf5ba2de6bc (test-lib: make \"-x\" work with \"--verbose-log\",\n2017-12-08), which were follow-up changes to work around the previous\n--verbose-log implementation.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n\nOn Fri, Oct 21 2016, Jeff King wrote:\n\n> The \"--verbose\" option redirects output from arbitrary\n> test commands to stdout. This is useful for examining the\n> output manually, like:\n>\n>   ./t5547-push-quarantine.sh -v | less\n>\n> But it also means that the output is intermingled with the\n> TAP directives, which can confuse a TAP parser like \"prove\".\n> This has always been a potential problem, but became an\n> issue recently when one test happened to output the word\n> \"ok\" on a line by itself, which prove interprets as a test\n> success:\n>\n>   $ prove t5547-push-quarantine.sh :: -v\n>   t5547-push-quarantine.sh .. 1/? To dest.git\n>    * [new branch]      HEAD -> master\n>   To dest.git\n>    ! [remote rejected] reject -> reject (pre-receive hook declined)\n>   error: failed to push some refs to 'dest.git'\n>   fatal: git cat-file d08c8eba97f4e683ece08654c7c8d2ba0c03b129: bad file\n>   t5547-push-quarantine.sh .. Failed -1/4 subtests\n>\n>   Test Summary Report\n>   -------------------\n>   t5547-push-quarantine.sh (Wstat: 0 Tests: 5 Failed: 0)\n>     Parse errors: Tests out of sequence.  Found (2) but expected (3)\n>                   Tests out of sequence.  Found (3) but expected (4)\n>                   Tests out of sequence.  Found (4) but expected (5)\n>                   Bad plan.  You planned 4 tests but ran 5.\n>   Files=1, Tests=5,  0 wallclock secs ( 0.01 usr +  0.01 sys =  0.02 CPU)\n>   Result: FAIL\n>\n> One answer is \"if it hurts, don't do it\", but that's not\n> quite the whole story. The Travis tests use \"--verbose\n> --tee\" so that they can get the benefit of prove's parallel\n> options, along with a verbose log in case there is a\n> failure. We just need the verbose output to go to the log,\n> but keep stdout clean.\n>\n> Getting this right turns out to be surprisingly difficult.\n> Here's the progression of alternatives I considered:\n>\n>  1. Add an option to write verbose output to stderr. This is\n>     hard to capture, though, because we want each test to\n>     have its own log (because they're all run in parallel\n>     and the jumbled output would be useless).\n>\n>  2. Add an option to write verbose output to a file in\n>     test-results. This works, but the log is missing all of\n>     the non-verbose output, which gives context.\n>\n>  3. Like (2), but teach say_color() to additionally output\n>     to the log. This mostly works, but misses any output\n>     that happens outside of the say() functions (which isn't\n>     a lot, but is a potential maintenance headache).\n>\n>  4. Like (2), but make the log file the same as the \"--tee\"\n>     file. That almost works, but now we have two processes\n>     opening the same file. That gives us two separate\n>     descriptors, each with their own idea of the current\n>     position. They'll each start writing at offset 0, and\n>     overwrite each other's data.\n>\n>  5. Like (4), but in each case open the file for appending.\n>     That atomically positions each write at the end of the\n>     file.\n>\n>     It's possible we may still get sheared writes between\n>     the two processes, but this is already the case when\n>     writing to stdout. It's not a problem in practice\n>     because the test harness generally waits for snippets to\n>     finish before writing the TAP output.\n>\n>     We can ignore buffering issues with tee, because POSIX\n>     mandates that it does not buffer. Likewise, POSIX\n>     specifies \"tee -a\", so it should be available\n>     everywhere.\n>\n> This patch implements option (5), which seems to work well\n> in practice.\n> \n> Signed-off-by: Jeff King <peff@peff.net>\n> ---\n> Arguably we don't need a new option for this, and could just do it\n> automatically. I wasn't sure when, though:\n>\n>   - if \"--tee --verbose\" triggers it, then somebody running that outside\n>     of \"prove\" who _wants_ the verbose output on stdout (because they're\n>     looking at it, but also want to save a copy to the log) would be\n>     regressed\n>\n>   - possibly \"--tee\" could just always write verbose output to the\n>     logfile (but not stdout). That's kind of weirdly magical, and we'd\n>     have to update the travis invocation anyway.\n>\n> So I went with a new option which implies the other ones. No chance of\n> regression, and it's easy to explain.\n\nI came up with a #6 for this which I found much simpler: We control the\nTAP output, so instead of emitting \"ok\" and having it be corrupted,\nlet's emit \"<some unique string> ok\", and then have our own \"tee -a\" and\nsearch-replace it back & forth.\n\nThe above RFC is in the middle of a topic branch I have locally, so it\ndoesn't apply cleanly to anything.\n\nI just thought I'd send it as an RFC to see if anyone had issues with\nthe approach, and to simply make --verbose-log a synonym for --verbose\n--tee with this fix. Having the verbose output under \"prove\" doesn't\nmatter, it will only report on the TAP, so quieting the output had no UI\nimpact.\n\nThe t/lib-test/* changes of calling \"sed\" once per-line under --tee are\nrelatively inefficient. But it would be rather trivial to convert this\nto a our own \"tee -a\"-alike t/helper/* using strbuf_getline() and wrote\nto stdout and a file if anyone saw a practical issue with that part of\nthe implementation (e.g. maybe it's very slow on Windows or something).\n\n t/README                   |  9 ++++++---\n t/lib-test/tapify-tee-a.sh | 10 ++++++++++\n t/lib-test/tapify.sh       |  5 +++++\n t/t0000-basic.sh           |  8 ++++++++\n t/test-lib.sh              | 40 +++++++++++++++++---------------------\n 5 files changed, 47 insertions(+), 25 deletions(-)\n create mode 100644 t/lib-test/tapify-tee-a.sh\n create mode 100644 t/lib-test/tapify.sh\n\ndiff --git a/t/README b/t/README\nindex 593d4a4e27..bf1f3313ac 100644\n--- a/t/README\n+++ b/t/README\n@@ -157,10 +157,13 @@ appropriately before running \"make\". Short options can be bundled, i.e.\n \n -V::\n --verbose-log::\n-\tWrite verbose output to the same logfile as `--tee`, but do\n-\t_not_ write it to stdout. Unlike `--tee --verbose`, this option\n+\tAn alias for `--verbose --tee`. This option\n \tis safe to use when stdout is being consumed by a TAP parser\n-\tlike `prove`. Implies `--tee` and `--verbose`.\n+\tlike `prove`.\n+\tHistorically this option was different from `--verbose --tee`\n+\tand would not write any verbose output to stdout to ensure the\n+\tTAP-correctness of the output. The TAP-correctness of the\n+\toutput is now sanity checked by the test library,\n \n --with-dashes::\n \tBy default tests are run without dashed forms of\ndiff --git a/t/lib-test/tapify-tee-a.sh b/t/lib-test/tapify-tee-a.sh\nnew file mode 100644\nindex 0000000000..f90f9a89c2\n--- /dev/null\n+++ b/t/lib-test/tapify-tee-a.sh\n@@ -0,0 +1,10 @@\n+#!/bin/sh\n+\n+# A 'tee -a'-alike that protects the sanity of the TAP output, for\n+# test-lib.sh's --tee option\n+\n+while read -r line\n+do\n+      printf \"%s\\n\" \"$line\"\n+      printf \"%s\\n\" \"$line\" | sed 's/^GIT_TEST_TEE_STARTED //' >>\"$1\"\n+done | $TEST_SHELL_PATH \"$TEST_DIRECTORY/lib-test/tapify.sh\"\ndiff --git a/t/lib-test/tapify.sh b/t/lib-test/tapify.sh\nnew file mode 100644\nindex 0000000000..bfa66d33b4\n--- /dev/null\n+++ b/t/lib-test/tapify.sh\n@@ -0,0 +1,5 @@\n+#!/bin/sh\n+\n+sed \\\n+    -e 's/^\\(ok\\|not ok\\|1\\.\\.\\)/\\\\\\1/' \\\n+    -e 's/^GIT_TEST_TEE_STARTED //'\ndiff --git a/t/t0000-basic.sh b/t/t0000-basic.sh\nindex 705d62cc27..2a95a612fb 100755\n--- a/t/t0000-basic.sh\n+++ b/t/t0000-basic.sh\n@@ -76,6 +76,14 @@ _run_sub_test_lib_test_common () {\n \t\t# this variable, so we need a stable setting under which to run\n \t\t# the sub-test.\n \t\tsane_unset HARNESS_ACTIVE &&\n+\n+\t\t# When under --tee pretend we're not as far as\n+\t\t# emitting the \"GIT_TEST_TEE_STARTED \" TAP sanity\n+\t\t# prefix goes. This means we promise not to emit\n+\t\t# invalid TAP, least this test fails under --tee under\n+\t\t# prove(1).\n+\t\tsane_unset GIT_TEST_TEE_STARTED &&\n+\n \t\tcd \"$name\" &&\n \t\twrite_script \"$name.sh\" \"$TEST_SHELL_PATH\" <<-EOF &&\n \t\ttest_description='$descr (run in sub test-lib)\ndiff --git a/t/test-lib.sh b/t/test-lib.sh\nindex af7a16fddd..f533cc02e9 100644\n--- a/t/test-lib.sh\n+++ b/t/test-lib.sh\n@@ -146,9 +146,11 @@ parse_option () {\n \t--no-chain-lint)\n \t\tGIT_TEST_CHAIN_LINT=0 ;;\n \t-x)\n-\t\ttrace=t ;;\n+\t\ttrace=t\n+\t\tverbose=t\n+\t\t;;\n \t-V|--verbose-log)\n-\t\tverbose_log=t\n+\t\tverbose=t\n \t\ttee=t\n \t\t;;\n \t--write-junit-xml)\n@@ -233,7 +235,7 @@ then\n \ttest -z \"$verbose\" && verbose_only=\"$valgrind_only\"\n elif test -n \"$valgrind\"\n then\n-\ttest -z \"$verbose_log\" && verbose=t\n+\tverbose=t\n fi\n \n if test -n \"$stress\"\n@@ -364,9 +366,19 @@ then\n \t(\n \t\tGIT_TEST_TEE_STARTED=done ${TEST_SHELL_PATH} \"$0\" \"$@\" 2>&1\n \t\techo $? >\"$TEST_RESULTS_BASE.exit\"\n-\t) | tee -a \"$GIT_TEST_TEE_OUTPUT_FILE\"\n+\t) | $TEST_SHELL_PATH \\\n+\t\t\"$TEST_DIRECTORY/lib-test/tapify-tee-a.sh\" \\\n+\t\t\"$GIT_TEST_TEE_OUTPUT_FILE\"\n \ttest \"$(cat \"$TEST_RESULTS_BASE.exit\")\" = 0\n \texit\n+elif test -n \"$verbose\" -a -n \"$HARNESS_ACTIVE\"\n+then\n+\tret=\n+\t(\n+\t\tGIT_TEST_TEE_STARTED=done ${TEST_SHELL_PATH} \"$0\" \"$@\" 2>&1\n+\t\tret=$?\n+\t) | $TEST_SHELL_PATH \"$TEST_DIRECTORY/lib-test/tapify.sh\"\n+\texit $ret\n fi\n \n if test -n \"$trace\" && test -n \"$test_untraceable\"\n@@ -392,10 +404,6 @@ then\n \t\ttrace=\n \tfi\n fi\n-if test -n \"$trace\" && test -z \"$verbose_log\"\n-then\n-\tverbose=t\n-fi\n \n # For repeatability, reset the environment to known value.\n # TERM is sanitized below, after saving color control sequences.\n@@ -569,7 +577,7 @@ say_color_tap() {\n \ttest \"$#\" = 2 ||\n \tBUG \"not 2 parameters to say_color_tap\"\n \n-\tsay_color \"$1\" \"$2\"\n+\tsay_color \"$1\" \"${GIT_TEST_TEE_STARTED:+GIT_TEST_TEE_STARTED }$2\"\n }\n \n TERM=dumb\n@@ -590,16 +598,6 @@ say () {\n \tsay_color info \"$*\"\n }\n \n-if test -n \"$HARNESS_ACTIVE\"\n-then\n-\tif test \"$verbose\" = t || test -n \"$verbose_only\"\n-\tthen\n-\t\tprintf 'Bail out! %s\\n' \\\n-\t\t 'verbose mode forbidden under TAP harness; try --verbose-log'\n-\t\texit 1\n-\tfi\n-fi\n-\n test \"${test_description}\" != \"\" ||\n error \"Test script did not set test_description.\"\n \n@@ -889,9 +887,7 @@ maybe_setup_valgrind () {\n \n trace_level_=0\n want_trace () {\n-\ttest \"$trace\" = t && {\n-\t\ttest \"$verbose\" = t || test \"$verbose_log\" = t\n-\t}\n+\ttest \"$trace\" = t && test \"$verbose\" = t\n }\n \n # This is a separate function because some tests use\n-- \n2.31.0.rc0.126.g04f22c5b82\n"},{"id":"418023","messageId":"YDy5LDsnF1THmvUw@coredump.intra.peff.net","threadId":"44349","inReplyTo":"87tupwj5y5.fsf@evledraar.gmail.com","subject":"Re: [PATCH/RFC] test-lib: make --verbose work under prove","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2021-03-01T09:51:40Z","receivedAt":"2021-03-01T09:53:57Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Sun, Feb 28, 2021 at 09:25:06PM +0100, Ævar Arnfjörð Bjarmason wrote:\n\n> As explained in 452320f1f5 (test-lib: add --verbose-log option,\n> 2016-10-21) combining the \"--verbose\" option with the TAP-parsing\n> \"prove\" utility would result in broken tests.\n> \n> This is because \"prove\" expects valid TAP on stdout, and if there's a\n> stray line starting with \"ok\", or other invalid TAP syntax it will\n> error on the test.\n> \n> In 452320f1f5 numerous potential methods are discussed for dealing\n> with this problem, but not the one implemented here, which I think is\n> a much simpler approach.\n\nI guess I don't really have an objection to this method existing, but I\ndon't see the point of it now. The original problem has been solved, and\nI don't find this any simpler than --verbose-log (on the contrary, I\nfind it much more complicated). So I don't know why I would want to use\nthis instead.\n\n-Peff\n"},{"id":"418035","messageId":"87r1kzj7xi.fsf@evledraar.gmail.com","threadId":"44349","inReplyTo":"YDy5LDsnF1THmvUw@coredump.intra.peff.net","subject":"Re: [PATCH/RFC] test-lib: make --verbose work under prove","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-01T13:54:33Z","receivedAt":"2021-03-01T13:55:38Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Mon, Mar 01 2021, Jeff King wrote:\n\n> On Sun, Feb 28, 2021 at 09:25:06PM +0100, Ævar Arnfjörð Bjarmason wrote:\n>\n>> As explained in 452320f1f5 (test-lib: add --verbose-log option,\n>> 2016-10-21) combining the \"--verbose\" option with the TAP-parsing\n>> \"prove\" utility would result in broken tests.\n>> \n>> This is because \"prove\" expects valid TAP on stdout, and if there's a\n>> stray line starting with \"ok\", or other invalid TAP syntax it will\n>> error on the test.\n>> \n>> In 452320f1f5 numerous potential methods are discussed for dealing\n>> with this problem, but not the one implemented here, which I think is\n>> a much simpler approach.\n>\n> I guess I don't really have an objection to this method existing, but I\n> don't see the point of it now. The original problem has been solved, and\n> I don't find this any simpler than --verbose-log (on the contrary, I\n> find it much more complicated). So I don't know why I would want to use\n> this instead.\n\nI guess that's the problem with picking something from the middle of a\nlocal WIP series I have, I forgot to state the end goal of the whole\nthing.\n\nThe point is to be able to run \"prove -v <test> :: --verbose\" and see\nthe verbose output. See the commit message for when TAP was added for\nexamples: 5099b99d25 (test-lib: Adjust output to be valid TAP format,\n2010-06-24)[1].\n\nBecause it's valid TAP with this patch you can easily parse the output:\n\n    HARNESS_ACTIVE=t ./t5547-push-quarantine.sh --verbose -x >t5547-push-quarantine.sh.tap\n    perl -MTAP::Parser -MYAML::XS -wE '\n        my $parser = TAP::Parser->new( { source => \"t5547-push-quarantine.sh.tap\" } );\n        my @lines;\n        while ( my $result = $parser->next ) {\n            push @lines => $result;\n        };\n        say Dump \\@lines\n    ' >t5547-push-quarantine.sh.tap.parsed\n\nGives you a stream like:\n\n    [...]\n    - !!perl/hash:TAP::Parser::Result::Unknown\n      raw: + chmod +x dest.git/hooks/pre-receive\n      type: unknown\n    - !!perl/hash:TAP::Parser::Result::Unknown\n      raw: ''\n      type: unknown\n    - !!perl/hash:TAP::Parser::Result::Test\n      description: '- create picky dest repo'\n      directive: ''\n      explanation: ''\n      ok: ok\n      raw: ok 1 - create picky dest repo\n      test_num: 1\n      type: test\n    - !!perl/hash:TAP::Parser::Result::Unknown\n      raw: ''\n      type: unknown\n    - !!perl/hash:TAP::Parser::Result::Unknown\n      raw: 'expecting success of 5547.2 ''accepted objects work'': '\n      type: unknown\n    - !!perl/hash:TAP::Parser::Result::Unknown\n      raw: \"\\ttest_commit ok &&\"\n      type: unknown\n    - !!perl/hash:TAP::Parser::Result::Unknown\n      raw: \"\\tgit push dest.git HEAD &&\"\n    [...]\n\nThe eventual goal was to make ci/print-test-failures.sh and the code\nthat cares about t/test-results/* capable of working with output like\nthat.\n\nSo e.g. you could optionally get summarized output about just the\nspecific tests that failed, not all the output for failing tests, as\nci/print-test-failures.sh is spewing out now.\n\nI also wanted to have something similar to Documentation/doc-diff but\nfor smart test diffing. E.g. now we have an rc0 out, and the set of\ntests broken on various test boxes I have access to is different than in\nthe last release.\n\nBut it's a pain to summarize that output currently (or at least I've\nalways written one-off tools for it).\n\nBy being able to parse the tap it's easier to massage that output and\ne.g. spew it into a database, or into two text files friendly to \"git\ndiff --no-index\".\n\nBut yes, the implementation isn't any simpler, it's more complex than\njust a \"tee -a\", but I think it's worth it for machine-readable verbose\ntest output.\n\n1. As an aside this is a bit tricker/broken in the prove utility than\n   when I wrote that code initially, sometimes it won't show you verbose\n   output:\n   https://github.com/Perl-Toolchain-Gang/Test-Harness/issues/105\n"},{"id":"418605","messageId":"20210309160219.13779-1-avarab@gmail.com","threadId":"44349","inReplyTo":"87r1kzj7xi.fsf@evledraar.gmail.com","subject":"[PATCH 0/6 + 1] test-lib: make --verbose output valid TAP","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-09T16:02:12Z","receivedAt":"2021-03-09T16:03:19Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"This makes the --verbose valid TAP.\n\nThis is a non-RFC version of an RFC patch I sent in\nhttps://lore.kernel.org/git/87tupwj5y5.fsf@evledraar.gmail.com/\n\nThere's a 7/7 patch that's an RFC at the end. It works for me, but\nsince I haven't used the JUnit output it's likely subtly broken in\nsome way (e.g. the new output doesn't include a timestamp attribute),\nbut it shows how easy it is to add new output targets.\n\nThe eventual goal not included in this series is to have multiple\noutput targets, and e.g. convert ci/print-test-failures.sh to use a\nTAP parser.\n\nMachine-readable \"TAP --verbose -x\" output can bring us a lot of nice\nthings down the line, I have some local WIP code that's a smarter\nversion of ci/print-test-failures.sh that knows how to spew out only\nthe output relevant to the failing test(s).\n\nFuture improvements based on that could be showing what specific line\nin a &&-chain failed, as well as e.g. running a debug version of a\nfailing \"grep\" if we detect that the failing line line was 'grep <pat>\n<filename>'.\n\nÆvar Arnfjörð Bjarmason (7):\n  test-lib: remove test_external\n  test-lib: add say_color_tap helper to emit TAP format\n  test-lib: color \"ok\" TAP directives green under --verbose (or -x)\n  test-lib: add tee with TAP support to test-tool\n  test-lib: indent and format GIT_TEST_TEE_OUTPUT_FILE code\n  test-lib: make --verbose output valid TAP\n  test-lib: generate JUnit output via TAP\n\n Makefile                                      |   2 +-\n .../netrc/t-git-credential-netrc.sh           |   7 +-\n t/README                                      |  35 +--\n t/helper/test-date.c                          |  12 -\n t/helper/test-path-utils.c                    |  21 --\n t/helper/test-tee.c                           |  85 +++++++\n t/helper/test-tool.c                          |   2 +-\n t/helper/test-tool.h                          |   2 +-\n t/helper/test-xml-encode.c                    |  80 -------\n t/t0000-basic.sh                              |   8 +\n t/t0202-gettext-perl.sh                       |  10 +-\n t/t9700-perl-git.sh                           |  10 +-\n t/test-lib-functions.sh                       |  89 +------\n t/test-lib.sh                                 | 226 +++++-------------\n 14 files changed, 177 insertions(+), 412 deletions(-)\n create mode 100644 t/helper/test-tee.c\n delete mode 100644 t/helper/test-xml-encode.c\n\n-- \n2.31.0.rc1.210.g0f8085a843c\n\n"},{"id":"418606","messageId":"20210309160219.13779-4-avarab@gmail.com","threadId":"44349","inReplyTo":"87r1kzj7xi.fsf@evledraar.gmail.com","subject":"[PATCH 3/7] test-lib: color \"ok\" TAP directives green under --verbose (or -x)","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-09T16:02:15Z","receivedAt":"2021-03-09T16:03:19Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change the color of \"ok\" lines to be green when we run under --verbose\nor -x.\n\nWhen a test fails we color the \"not ok\" line red, but the \"ok\" lines\nare not colored.\n\nI think that's the right thing to do in the default output, which is\ne.g.:\n\n    $ ./t0000-basic.sh\n    ok 1 - verify that the running shell supports \"local\"\n    ok 2 - .git/objects should be empty after git init in an empty repo\n    ok 3 - .git/objects should have 3 subdirectories\n    [...]\n\nHaving almost every line colored green in that output would be\ndistracting.\n\nHowever, under --verbose we'll typically might emit 10-20 lines of\nnon-colored stderr and \"info\" colored output describing the test\nitself.\n\nWhen paging through that output always having the boundaries between\ntests highlighted makes the output more readable.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/test-lib.sh | 3 ++-\n 1 file changed, 2 insertions(+), 1 deletion(-)\n\ndiff --git a/t/test-lib.sh b/t/test-lib.sh\nindex 9a3a3d8d16d..8562b11b817 100644\n--- a/t/test-lib.sh\n+++ b/t/test-lib.sh\n@@ -545,6 +545,7 @@ then\n \tsay_color_skip=$(tput setaf 4) # blue\n \tsay_color_warn=$(tput setaf 3) # brown/yellow\n \tsay_color_pass=$(tput setaf 2) # green\n+\tsay_color_pass_all=$(tput bold; tput setaf 2) # bold green\n \tsay_color_info=$(tput setaf 6) # cyan\n \tsay_color_reset=$(tput sgr0)\n \tsay_color_=\"\" # no formatting for normal text\n@@ -673,7 +674,7 @@ test_ok_ () {\n \t\twrite_junit_xml_testcase \"$*\"\n \tfi\n \ttest_success=$(($test_success + 1))\n-\tsay_color_tap \"\" \"ok $test_count - $@\"\n+\tsay_color_tap \"${verbose:+pass}\" \"ok $test_count - $@\"\n }\n \n test_failure_ () {\n-- \n2.31.0.rc1.210.g0f8085a843c\n\n"},{"id":"418607","messageId":"20210309160219.13779-5-avarab@gmail.com","threadId":"44349","inReplyTo":"87r1kzj7xi.fsf@evledraar.gmail.com","subject":"[PATCH 4/7] test-lib: add tee with TAP support to test-tool","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-09T16:02:16Z","receivedAt":"2021-03-09T16:03:20Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a 'test-tool tee' that behaves similarly to 'tee(1)', except that\nit supports sanitizing the TAP emitted on stdout.\n\nThis is done by assuming that the TAP is emitted by a friendly library\nthat promises to prefix valid TAP directives with what we're going to\nstrip with --strip-prefix=\"\".\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Makefile             |  1 +\n t/helper/test-tee.c  | 85 ++++++++++++++++++++++++++++++++++++++++++++\n t/helper/test-tool.c |  1 +\n t/helper/test-tool.h |  1 +\n 4 files changed, 88 insertions(+)\n create mode 100644 t/helper/test-tee.c\n\ndiff --git a/Makefile b/Makefile\nindex dfb0f1000fa..d26b9d62ee9 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -742,6 +742,7 @@ TEST_BUILTINS_OBJS += test-string-list.o\n TEST_BUILTINS_OBJS += test-submodule-config.o\n TEST_BUILTINS_OBJS += test-submodule-nested-repo-config.o\n TEST_BUILTINS_OBJS += test-subprocess.o\n+TEST_BUILTINS_OBJS += test-tee.o\n TEST_BUILTINS_OBJS += test-trace2.o\n TEST_BUILTINS_OBJS += test-urlmatch-normalization.o\n TEST_BUILTINS_OBJS += test-wildmatch.o\ndiff --git a/t/helper/test-tee.c b/t/helper/test-tee.c\nnew file mode 100644\nindex 00000000000..4ed34e9db93\n--- /dev/null\n+++ b/t/helper/test-tee.c\n@@ -0,0 +1,85 @@\n+#include \"test-tool.h\"\n+#include \"parse-options.h\"\n+#include \"strbuf.h\"\n+\n+static int line_has_tap(struct strbuf *line)\n+{\n+\t/*\n+\t * This is a less permissive version of\n+\t * https://metacpan.org/release/Test-Harness/source/lib/TAP/Parser/Grammar.pm\n+\t */\n+\tif (starts_with(line->buf, \"ok\") ||\n+\t    starts_with(line->buf, \"not ok\") ||\n+\t    starts_with(line->buf, \"1..\") ||\n+\t    starts_with(line->buf, \"Bail out!\") ||\n+\t    starts_with(line->buf, \"TAP version\") ||\n+\t    starts_with(line->buf, \"pragma\"))\n+\t\treturn 1;\n+\tif (starts_with(line->buf, \"#\"))\n+\t\t/*\n+\t\t * We're ignoring comments from now, but might treat them\n+\t\t * specially in the future for sanctioned messaging from the\n+\t\t * test-lib.sh.\n+\t\t */\n+\t\treturn 0;\n+\treturn  0;\n+}\n+\n+int cmd__tee(int argc, const char **argv)\n+{\n+\tint tap = 0;\n+\tint escape_stdout = 0, escape_file = 0;\n+\tchar *prefix = NULL;\n+\tsize_t prefix_len = 0;\n+\tconst char *tee_usage[] = {\n+\t\t\"test-tool tee [<options>] <FILE>\",\n+\t\tNULL\n+\t};\n+\tstruct option options[] = {\n+\t\tOPT_BOOL(0, \"escape-stdout\", &escape_stdout,\n+\t\t\t \"escape output on stdout\"),\n+\t\tOPT_BOOL(0, \"escape-file\", &escape_file,\n+\t\t\t \"escape output written to file\"),\n+\t\tOPT_BOOL(0, \"tap\", &tap,\n+\t\t\t \"output is TAP\"),\n+\t\tOPT_STRING(0, \"prefix\", &prefix, \"str\",\n+\t\t\t   \"prefix to strip from the output\"),\n+\t\tOPT_END()\n+\t};\n+\tstruct strbuf line = STRBUF_INIT;\n+\tFILE *logfp = NULL;\n+\n+\targc = parse_options(argc, argv, NULL, options,\n+\t\t\t     tee_usage, PARSE_OPT_STOP_AT_NON_OPTION);\n+\tif (argc > 1) {\n+\t\tfprintf(stderr, \"got bad option: %s\\n\", argv[0]);\n+\t\tusage_with_options(tee_usage, options);\n+\t}\n+\tif (prefix)\n+\t\tprefix_len = strlen(prefix);\n+\n+\tif (argc)\n+\t\tlogfp = xfopen(argv[0], \"w\");\n+\n+\twhile (strbuf_getline(&line, stdin) != EOF) {\n+\t\tsize_t offs = 0;\n+\t\tif (prefix && starts_with(line.buf, prefix))\n+\t\t\toffs = prefix_len;\n+\n+\t\tif (!offs && tap && line_has_tap(&line)) {\n+\t\t\tif (escape_stdout)\n+\t\t\t\tfprintf(stdout, \"\\\\\");\n+\t\t\tif (logfp && escape_file)\n+\t\t\t\tfprintf(logfp, \"\\\\\");\n+\t\t}\n+\n+\t\tfprintf(stdout, \"%s\\n\", line.buf + offs);\n+\t\tif (logfp)\n+\t\t\tfprintf(logfp, \"%s\\n\", line.buf + offs);\n+\t}\n+\tstrbuf_release(&line);\n+\tif (logfp)\n+\t\tfclose(logfp);\n+\n+\treturn 0;\n+}\ndiff --git a/t/helper/test-tool.c b/t/helper/test-tool.c\nindex f97cd9f48a6..1876bad8f42 100644\n--- a/t/helper/test-tool.c\n+++ b/t/helper/test-tool.c\n@@ -70,6 +70,7 @@ static struct test_cmd cmds[] = {\n \t{ \"submodule-config\", cmd__submodule_config },\n \t{ \"submodule-nested-repo-config\", cmd__submodule_nested_repo_config },\n \t{ \"subprocess\", cmd__subprocess },\n+\t{ \"tee\", cmd__tee },\n \t{ \"trace2\", cmd__trace2 },\n \t{ \"urlmatch-normalization\", cmd__urlmatch_normalization },\n \t{ \"xml-encode\", cmd__xml_encode },\ndiff --git a/t/helper/test-tool.h b/t/helper/test-tool.h\nindex 28072c0ad5a..9b3c1f75267 100644\n--- a/t/helper/test-tool.h\n+++ b/t/helper/test-tool.h\n@@ -60,6 +60,7 @@ int cmd__string_list(int argc, const char **argv);\n int cmd__submodule_config(int argc, const char **argv);\n int cmd__submodule_nested_repo_config(int argc, const char **argv);\n int cmd__subprocess(int argc, const char **argv);\n+int cmd__tee(int argc, const char **argv);\n int cmd__trace2(int argc, const char **argv);\n int cmd__urlmatch_normalization(int argc, const char **argv);\n int cmd__xml_encode(int argc, const char **argv);\n-- \n2.31.0.rc1.210.g0f8085a843c\n\n"},{"id":"418608","messageId":"20210309160219.13779-3-avarab@gmail.com","threadId":"44349","inReplyTo":"87r1kzj7xi.fsf@evledraar.gmail.com","subject":"[PATCH 2/7] test-lib: add say_color_tap helper to emit TAP format","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-09T16:02:14Z","receivedAt":"2021-03-09T16:03:20Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Follow-up 5099b99d25 (test-lib: Adjust output to be valid TAP format,\n2010-06-24) and make it easy to distinguishing when we're emitting TAP\ndirectives (with the new \"say_color_tap\"), and when we just want to\nsay something with colored text \"say_color\".\n\nThis also makes it easier to do any follow-up changes where we'd like\nto do something special with the TAP directives, as they're what\ndrives the test when we run under the prove(1) utility.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/test-lib.sh | 21 ++++++++++++++-------\n 1 file changed, 14 insertions(+), 7 deletions(-)\n\ndiff --git a/t/test-lib.sh b/t/test-lib.sh\nindex b0a8bc42510..9a3a3d8d16d 100644\n--- a/t/test-lib.sh\n+++ b/t/test-lib.sh\n@@ -562,6 +562,13 @@ else\n \t}\n fi\n \n+say_color_tap() {\n+\ttest \"$#\" = 2 ||\n+\tBUG \"not 2 parameters to say_color_tap\"\n+\n+\tsay_color \"$1\" \"$2\"\n+}\n+\n TERM=dumb\n export TERM\n \n@@ -666,7 +673,7 @@ test_ok_ () {\n \t\twrite_junit_xml_testcase \"$*\"\n \tfi\n \ttest_success=$(($test_success + 1))\n-\tsay_color \"\" \"ok $test_count - $@\"\n+\tsay_color_tap \"\" \"ok $test_count - $@\"\n }\n \n test_failure_ () {\n@@ -691,7 +698,7 @@ test_failure_ () {\n \t\twrite_junit_xml_testcase \"$1\" \"      $junit_insert\"\n \tfi\n \ttest_failure=$(($test_failure + 1))\n-\tsay_color error \"not ok $test_count - $1\"\n+\tsay_color_tap error \"not ok $test_count - $1\"\n \tshift\n \tprintf '%s\\n' \"$*\" | sed -e 's/^/#\t/'\n \ttest \"$immediate\" = \"\" || { finalize_junit_xml; GIT_EXIT_OK=t; exit 1; }\n@@ -712,7 +719,7 @@ test_known_broken_failure_ () {\n \t\twrite_junit_xml_testcase \"$* (known breakage)\"\n \tfi\n \ttest_broken=$(($test_broken+1))\n-\tsay_color warn \"not ok $test_count - $@ # TODO known breakage\"\n+\tsay_color_tap warn \"not ok $test_count - $@ # TODO known breakage\"\n }\n \n test_debug () {\n@@ -1027,7 +1034,7 @@ test_skip () {\n \t\t\t\t\"      <skipped message=\\\"$message\\\" />\"\n \t\tfi\n \n-\t\tsay_color skip \"ok $test_count # skip $1 ($skipped_reason)\"\n+\t\tsay_color_tap skip \"ok $test_count # skip $1 ($skipped_reason)\"\n \t\t: true\n \t\t;;\n \t*)\n@@ -1153,12 +1160,12 @@ test_done () {\n \t\ttest -z \"$skip_all\" || skip_all=\"# SKIP $skip_all\"\n \t\tcase \"$test_count\" in\n \t\t0)\n-\t\t\tsay \"1..$test_count${skip_all:+ $skip_all}\"\n+\t\t\tsay_color_tap \"\" \"1..$test_count${skip_all:+ $skip_all}\"\n \t\t\t;;\n \t\t*)\n \t\t\ttest -z \"$skip_all\" ||\n \t\t\tsay_color warn \"$skip_all\"\n-\t\t\tsay \"1..$test_count\"\n+\t\t\tsay_color_tap \"\" \"1..$test_count\"\n \t\t\t;;\n \t\tesac\n \n@@ -1181,7 +1188,7 @@ test_done () {\n \n \t*)\n \t\tsay_color error \"# failed $test_failure among $msg\"\n-\t\tsay \"1..$test_count\"\n+\t\tsay_color_tap \"\" \"1..$test_count\"\n \n \t\texit 1 ;;\n \n-- \n2.31.0.rc1.210.g0f8085a843c\n\n"},{"id":"418609","messageId":"20210309160219.13779-2-avarab@gmail.com","threadId":"44349","inReplyTo":"87r1kzj7xi.fsf@evledraar.gmail.com","subject":"[PATCH 1/7] test-lib: remove test_external","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-09T16:02:13Z","receivedAt":"2021-03-09T16:03:20Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Remove the test_external function(s) in favor of running the Perl\ntests with a test_expect_success.\n\nThe only advantage of this wrapper added in\nfb32c410087 (t/test-lib.sh: add test_external and\ntest_external_without_stderr, 2008-06-19) was that we got the\nTest::More output as-is from the two wrapped scripts.\n\nNow we'll instead hang until the script is finished, and report on its\nexit code. The Test::More framework ensures that we exit with non-zero\nexit code on failure.\n\nMy motivation for this is to eliminate a special case where things\nthat aren't test-lib.sh are going to produce TAP, for reasons that'll\nbe clear in subsequent commits.\n\nThis also eliminates special cases I added in d998bd4ab67 (test-lib:\nMake the test_external_* functions TAP-aware, 2010-06-24) from\ntest-lib.sh itself.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n .../netrc/t-git-credential-netrc.sh           |  7 +-\n t/README                                      | 26 ------\n t/t0202-gettext-perl.sh                       | 10 +--\n t/t9700-perl-git.sh                           | 10 +--\n t/test-lib-functions.sh                       | 89 +------------------\n t/test-lib.sh                                 | 42 ++++-----\n 6 files changed, 28 insertions(+), 156 deletions(-)\n\ndiff --git a/contrib/credential/netrc/t-git-credential-netrc.sh b/contrib/credential/netrc/t-git-credential-netrc.sh\nindex 07227d02287..28118a9e194 100755\n--- a/contrib/credential/netrc/t-git-credential-netrc.sh\n+++ b/contrib/credential/netrc/t-git-credential-netrc.sh\n@@ -20,13 +20,10 @@\n \t\t'set up test repository' \\\n \t\t'git config --add gpg.program test.git-config-gpg'\n \n-\t# The external test will outputs its own plan\n-\ttest_external_has_tap=1\n-\n \texport PERL5LIB=\"$GITPERLLIB\"\n-\ttest_external \\\n-\t\t'git-credential-netrc' \\\n+\ttest_expect_success 'git-credential-netrc' '\n \t\tperl \"$GIT_BUILD_DIR\"/contrib/credential/netrc/test.pl\n+\t'\n \n \ttest_done\n )\ndiff --git a/t/README b/t/README\nindex 593d4a4e270..2cc8cbc7185 100644\n--- a/t/README\n+++ b/t/README\n@@ -844,32 +844,6 @@ library for your script to use.\n \t    test_done\n \tfi\n \n- - test_external [<prereq>] <message> <external> <script>\n-\n-   Execute a <script> with an <external> interpreter (like perl). This\n-   was added for tests like t9700-perl-git.sh which do most of their\n-   work in an external test script.\n-\n-\ttest_external \\\n-\t    'GitwebCache::*FileCache*' \\\n-\t    perl \"$TEST_DIRECTORY\"/t9503/test_cache_interface.pl\n-\n-   If the test is outputting its own TAP you should set the\n-   test_external_has_tap variable somewhere before calling the first\n-   test_external* function. See t9700-perl-git.sh for an example.\n-\n-\t# The external test will outputs its own plan\n-\ttest_external_has_tap=1\n-\n- - test_external_without_stderr [<prereq>] <message> <external> <script>\n-\n-   Like test_external but fail if there's any output on stderr,\n-   instead of checking the exit code.\n-\n-\ttest_external_without_stderr \\\n-\t    'Perl API' \\\n-\t    perl \"$TEST_DIRECTORY\"/t9700/test.pl\n-\n  - test_expect_code <exit-code> <command>\n \n    Run a command and ensure that it exits with the given exit code.\ndiff --git a/t/t0202-gettext-perl.sh b/t/t0202-gettext-perl.sh\nindex a29d166e007..06a93b36790 100755\n--- a/t/t0202-gettext-perl.sh\n+++ b/t/t0202-gettext-perl.sh\n@@ -17,11 +17,9 @@ perl -MTest::More -e 0 2>/dev/null || {\n \ttest_done\n }\n \n-# The external test will outputs its own plan\n-test_external_has_tap=1\n-\n-test_external_without_stderr \\\n-    'Perl Git::I18N API' \\\n-    perl \"$TEST_DIRECTORY\"/t0202/test.pl\n+test_expect_success 'run t0202/test.pl to test Git::I18N.pm' '\n+\tperl \"$TEST_DIRECTORY\"/t0202/test.pl 2>stderr &&\n+\ttest_must_be_empty stderr\n+'\n \n test_done\ndiff --git a/t/t9700-perl-git.sh b/t/t9700-perl-git.sh\nindex 102c133112c..574c57b17f1 100755\n--- a/t/t9700-perl-git.sh\n+++ b/t/t9700-perl-git.sh\n@@ -50,11 +50,9 @@ test_expect_success \\\n      git config --add test.pathmulti bar\n      '\n \n-# The external test will outputs its own plan\n-test_external_has_tap=1\n-\n-test_external_without_stderr \\\n-    'Perl API' \\\n-    perl \"$TEST_DIRECTORY\"/t9700/test.pl\n+test_expect_success 'use t9700/test.pl to test Git.pm' '\n+\tperl \"$TEST_DIRECTORY\"/t9700/test.pl 2>stderr &&\n+\ttest_must_be_empty stderr\n+'\n \n test_done\ndiff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh\nindex 3dd68091bbf..ead3afb36b3 100644\n--- a/t/test-lib-functions.sh\n+++ b/t/test-lib-functions.sh\n@@ -468,7 +468,7 @@ write_script () {\n # - Explicitly using test_have_prereq.\n #\n # - Implicitly by specifying the prerequisite tag in the calls to\n-#   test_expect_{success,failure} and test_external{,_without_stderr}.\n+#   test_expect_{success,failure}\n #\n # The single parameter is the prerequisite tag (a simple word, in all\n # capital letters by convention).\n@@ -660,93 +660,6 @@ test_expect_success () {\n \treturn 1\n }\n \n-# test_external runs external test scripts that provide continuous\n-# test output about their progress, and succeeds/fails on\n-# zero/non-zero exit code.  It outputs the test output on stdout even\n-# in non-verbose mode, and announces the external script with \"# run\n-# <n>: ...\" before running it.  When providing relative paths, keep in\n-# mind that all scripts run in \"trash directory\".\n-# Usage: test_external description command arguments...\n-# Example: test_external 'Perl API' perl ../path/to/test.pl\n-test_external () {\n-\ttest \"$#\" = 4 && { test_prereq=$1; shift; } || test_prereq=\n-\ttest \"$#\" = 3 ||\n-\tBUG \"not 3 or 4 parameters to test_external\"\n-\tdescr=\"$1\"\n-\tshift\n-\ttest_verify_prereq\n-\texport test_prereq\n-\tif ! test_skip \"$descr\" \"$@\"\n-\tthen\n-\t\t# Announce the script to reduce confusion about the\n-\t\t# test output that follows.\n-\t\tsay_color \"\" \"# run $test_count: $descr ($*)\"\n-\t\t# Export TEST_DIRECTORY, TRASH_DIRECTORY and GIT_TEST_LONG\n-\t\t# to be able to use them in script\n-\t\texport TEST_DIRECTORY TRASH_DIRECTORY GIT_TEST_LONG\n-\t\t# Run command; redirect its stderr to &4 as in\n-\t\t# test_run_, but keep its stdout on our stdout even in\n-\t\t# non-verbose mode.\n-\t\t\"$@\" 2>&4\n-\t\tif test \"$?\" = 0\n-\t\tthen\n-\t\t\tif test $test_external_has_tap -eq 0; then\n-\t\t\t\ttest_ok_ \"$descr\"\n-\t\t\telse\n-\t\t\t\tsay_color \"\" \"# test_external test $descr was ok\"\n-\t\t\t\ttest_success=$(($test_success + 1))\n-\t\t\tfi\n-\t\telse\n-\t\t\tif test $test_external_has_tap -eq 0; then\n-\t\t\t\ttest_failure_ \"$descr\" \"$@\"\n-\t\t\telse\n-\t\t\t\tsay_color error \"# test_external test $descr failed: $@\"\n-\t\t\t\ttest_failure=$(($test_failure + 1))\n-\t\t\tfi\n-\t\tfi\n-\tfi\n-}\n-\n-# Like test_external, but in addition tests that the command generated\n-# no output on stderr.\n-test_external_without_stderr () {\n-\t# The temporary file has no (and must have no) security\n-\t# implications.\n-\ttmp=${TMPDIR:-/tmp}\n-\tstderr=\"$tmp/git-external-stderr.$$.tmp\"\n-\ttest_external \"$@\" 4> \"$stderr\"\n-\ttest -f \"$stderr\" || error \"Internal error: $stderr disappeared.\"\n-\tdescr=\"no stderr: $1\"\n-\tshift\n-\tsay >&3 \"# expecting no stderr from previous command\"\n-\tif test ! -s \"$stderr\"\n-\tthen\n-\t\trm \"$stderr\"\n-\n-\t\tif test $test_external_has_tap -eq 0; then\n-\t\t\ttest_ok_ \"$descr\"\n-\t\telse\n-\t\t\tsay_color \"\" \"# test_external_without_stderr test $descr was ok\"\n-\t\t\ttest_success=$(($test_success + 1))\n-\t\tfi\n-\telse\n-\t\tif test \"$verbose\" = t\n-\t\tthen\n-\t\t\toutput=$(echo; echo \"# Stderr is:\"; cat \"$stderr\")\n-\t\telse\n-\t\t\toutput=\n-\t\tfi\n-\t\t# rm first in case test_failure exits.\n-\t\trm \"$stderr\"\n-\t\tif test $test_external_has_tap -eq 0; then\n-\t\t\ttest_failure_ \"$descr\" \"$@\" \"$output\"\n-\t\telse\n-\t\t\tsay_color error \"# test_external_without_stderr test $descr failed: $@: $output\"\n-\t\t\ttest_failure=$(($test_failure + 1))\n-\t\tfi\n-\tfi\n-}\n-\n # debugging-friendly alternatives to \"test [-f|-d|-e]\"\n # The commands test the existence or non-existence of $1\n test_path_is_file () {\ndiff --git a/t/test-lib.sh b/t/test-lib.sh\nindex d3f6af6a654..b0a8bc42510 100644\n--- a/t/test-lib.sh\n+++ b/t/test-lib.sh\n@@ -631,8 +631,6 @@ test_fixed=0\n test_broken=0\n test_success=0\n \n-test_external_has_tap=0\n-\n die () {\n \tcode=$?\n \t# This is responsible for running the atexit commands even when a\n@@ -1146,27 +1144,24 @@ test_done () {\n \tfi\n \tcase \"$test_failure\" in\n \t0)\n-\t\tif test $test_external_has_tap -eq 0\n+\t\tif test $test_remaining -gt 0\n \t\tthen\n-\t\t\tif test $test_remaining -gt 0\n-\t\t\tthen\n-\t\t\t\tsay_color pass \"# passed all $msg\"\n-\t\t\tfi\n-\n-\t\t\t# Maybe print SKIP message\n-\t\t\ttest -z \"$skip_all\" || skip_all=\"# SKIP $skip_all\"\n-\t\t\tcase \"$test_count\" in\n-\t\t\t0)\n-\t\t\t\tsay \"1..$test_count${skip_all:+ $skip_all}\"\n-\t\t\t\t;;\n-\t\t\t*)\n-\t\t\t\ttest -z \"$skip_all\" ||\n-\t\t\t\tsay_color warn \"$skip_all\"\n-\t\t\t\tsay \"1..$test_count\"\n-\t\t\t\t;;\n-\t\t\tesac\n+\t\t\tsay_color pass \"# passed all $msg\"\n \t\tfi\n \n+\t\t# Maybe print SKIP message\n+\t\ttest -z \"$skip_all\" || skip_all=\"# SKIP $skip_all\"\n+\t\tcase \"$test_count\" in\n+\t\t0)\n+\t\t\tsay \"1..$test_count${skip_all:+ $skip_all}\"\n+\t\t\t;;\n+\t\t*)\n+\t\t\ttest -z \"$skip_all\" ||\n+\t\t\tsay_color warn \"$skip_all\"\n+\t\t\tsay \"1..$test_count\"\n+\t\t\t;;\n+\t\tesac\n+\n \t\tif test -z \"$debug\"\n \t\tthen\n \t\t\ttest -d \"$TRASH_DIRECTORY\" ||\n@@ -1185,11 +1180,8 @@ test_done () {\n \t\texit 0 ;;\n \n \t*)\n-\t\tif test $test_external_has_tap -eq 0\n-\t\tthen\n-\t\t\tsay_color error \"# failed $test_failure among $msg\"\n-\t\t\tsay \"1..$test_count\"\n-\t\tfi\n+\t\tsay_color error \"# failed $test_failure among $msg\"\n+\t\tsay \"1..$test_count\"\n \n \t\texit 1 ;;\n \n-- \n2.31.0.rc1.210.g0f8085a843c\n\n"},{"id":"418610","messageId":"20210309160219.13779-6-avarab@gmail.com","threadId":"44349","inReplyTo":"87r1kzj7xi.fsf@evledraar.gmail.com","subject":"[PATCH 5/7] test-lib: indent and format GIT_TEST_TEE_OUTPUT_FILE code","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-09T16:02:17Z","receivedAt":"2021-03-09T16:03:20Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change the indentation and formatting of the GIT_TEST_TEE_OUTPUT_FILE\ncode block added in 44138559e8 (test-lib.sh: optionally output to\ntest-results/$TEST.out, too, 2009-02-04).\n\nThis brings it in line with the general style in our test library, and\nmakes it easier to read modifications of it in subsequent commits.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/test-lib.sh | 6 ++++--\n 1 file changed, 4 insertions(+), 2 deletions(-)\n\ndiff --git a/t/test-lib.sh b/t/test-lib.sh\nindex 8562b11b817..aa7068b06b6 100644\n--- a/t/test-lib.sh\n+++ b/t/test-lib.sh\n@@ -361,8 +361,10 @@ then\n \t# from any previous runs.\n \t>\"$GIT_TEST_TEE_OUTPUT_FILE\"\n \n-\t(GIT_TEST_TEE_STARTED=done ${TEST_SHELL_PATH} \"$0\" \"$@\" 2>&1;\n-\t echo $? >\"$TEST_RESULTS_BASE.exit\") | tee -a \"$GIT_TEST_TEE_OUTPUT_FILE\"\n+\t(\n+\t\tGIT_TEST_TEE_STARTED=done ${TEST_SHELL_PATH} \"$0\" \"$@\" 2>&1\n+\t\techo $? >\"$TEST_RESULTS_BASE.exit\"\n+\t) | tee -a \"$GIT_TEST_TEE_OUTPUT_FILE\"\n \ttest \"$(cat \"$TEST_RESULTS_BASE.exit\")\" = 0\n \texit\n fi\n-- \n2.31.0.rc1.210.g0f8085a843c\n\n"},{"id":"418611","messageId":"20210309160219.13779-8-avarab@gmail.com","threadId":"44349","inReplyTo":"87r1kzj7xi.fsf@evledraar.gmail.com","subject":"[RFC/PATCH 7/7] test-lib: generate JUnit output via TAP","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-09T16:02:19Z","receivedAt":"2021-03-09T16:03:20Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Rewrite the home-brew JUnit output generation added in\n22231908151 (tests: optionally write results as JUnit-style .xml,\n2019-01-29) and other subsequent commits to be generated via the TAP\noutput.\n\nThis is now possible that the TAP output emitted with --tee is\nguaranteed to be valid TAP, see the preceding commit.\n\nThe JUnit output is not bug-for-bug compatible with the previous\noutput, but it looks better to me.\n\nThis also requires installing TAP::Formatter::JUnit[1], perhaps that's\nnot palatable to the users of the --write-junit-xml option.\n\nIn any case, it'll be easy to whip up our own TAP emitter with a\nTAP::Parser and TAP::Formatter, both of whom come with perl itself,\nwhich we already rely on for tests.\n\nIt should also be significantly faster on Windows, as we can\ne.g. write all the *.out files, and only do that conversion at the end\nfor all files in a batch, as opposed to the current implementation of\nshelling out to test-tool in a loop for each test.\n\n1. https://metacpan.org/pod/distribution/TAP-Formatter-JUnit/script/tap2junit\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Makefile                   |   1 -\n t/helper/test-date.c       |  12 ----\n t/helper/test-path-utils.c |  21 -------\n t/helper/test-tool.c       |   1 -\n t/helper/test-tool.h       |   1 -\n t/helper/test-xml-encode.c |  80 ------------------------\n t/test-lib.sh              | 123 ++++---------------------------------\n 7 files changed, 12 insertions(+), 227 deletions(-)\n delete mode 100644 t/helper/test-xml-encode.c\n\ndiff --git a/Makefile b/Makefile\nindex d26b9d62ee9..fa7c52f7a42 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -748,7 +748,6 @@ TEST_BUILTINS_OBJS += test-urlmatch-normalization.o\n TEST_BUILTINS_OBJS += test-wildmatch.o\n TEST_BUILTINS_OBJS += test-windows-named-pipe.o\n TEST_BUILTINS_OBJS += test-write-cache.o\n-TEST_BUILTINS_OBJS += test-xml-encode.o\n \n # Do not add more tests here unless they have extra dependencies. Add\n # them in TEST_BUILTINS_OBJS above.\ndiff --git a/t/helper/test-date.c b/t/helper/test-date.c\nindex 099eff4f0fc..ebf133943b3 100644\n--- a/t/helper/test-date.c\n+++ b/t/helper/test-date.c\n@@ -8,7 +8,6 @@ static const char *usage_msg = \"\\n\"\n \"  test-tool date parse [date]...\\n\"\n \"  test-tool date approxidate [date]...\\n\"\n \"  test-tool date timestamp [date]...\\n\"\n-\"  test-tool date getnanos [start-nanos]\\n\"\n \"  test-tool date is64bit\\n\"\n \"  test-tool date time_t-is64bit\\n\";\n \n@@ -92,15 +91,6 @@ static void parse_approx_timestamp(const char **argv)\n \t}\n }\n \n-static void getnanos(const char **argv)\n-{\n-\tdouble seconds = getnanotime() / 1.0e9;\n-\n-\tif (*argv)\n-\t\tseconds -= strtod(*argv, NULL);\n-\tprintf(\"%lf\\n\", seconds);\n-}\n-\n int cmd__date(int argc, const char **argv)\n {\n \tconst char *x;\n@@ -120,8 +110,6 @@ int cmd__date(int argc, const char **argv)\n \t\tparse_approxidate(argv+1);\n \telse if (!strcmp(*argv, \"timestamp\"))\n \t\tparse_approx_timestamp(argv+1);\n-\telse if (!strcmp(*argv, \"getnanos\"))\n-\t\tgetnanos(argv+1);\n \telse if (!strcmp(*argv, \"is64bit\"))\n \t\treturn sizeof(timestamp_t) == 8 ? 0 : 1;\n \telse if (!strcmp(*argv, \"time_t-is64bit\"))\ndiff --git a/t/helper/test-path-utils.c b/t/helper/test-path-utils.c\nindex 313a153209c..090596ac492 100644\n--- a/t/helper/test-path-utils.c\n+++ b/t/helper/test-path-utils.c\n@@ -407,27 +407,6 @@ int cmd__path_utils(int argc, const char **argv)\n \t\treturn !!res;\n \t}\n \n-\tif (argc == 4 && !strcmp(argv[1], \"skip-n-bytes\")) {\n-\t\tint fd = open(argv[2], O_RDONLY), offset = atoi(argv[3]);\n-\t\tchar buffer[65536];\n-\n-\t\tif (fd < 0)\n-\t\t\tdie_errno(\"could not open '%s'\", argv[2]);\n-\t\tif (lseek(fd, offset, SEEK_SET) < 0)\n-\t\t\tdie_errno(\"could not skip %d bytes\", offset);\n-\t\tfor (;;) {\n-\t\t\tssize_t count = read(fd, buffer, sizeof(buffer));\n-\t\t\tif (count < 0)\n-\t\t\t\tdie_errno(\"could not read '%s'\", argv[2]);\n-\t\t\tif (!count)\n-\t\t\t\tbreak;\n-\t\t\tif (write(1, buffer, count) < 0)\n-\t\t\t\tdie_errno(\"could not write to stdout\");\n-\t\t}\n-\t\tclose(fd);\n-\t\treturn 0;\n-\t}\n-\n \tif (argc > 5 && !strcmp(argv[1], \"slice-tests\")) {\n \t\tint res = 0;\n \t\tlong offset, stride, i;\ndiff --git a/t/helper/test-tool.c b/t/helper/test-tool.c\nindex 1876bad8f42..3fa02011b61 100644\n--- a/t/helper/test-tool.c\n+++ b/t/helper/test-tool.c\n@@ -73,7 +73,6 @@ static struct test_cmd cmds[] = {\n \t{ \"tee\", cmd__tee },\n \t{ \"trace2\", cmd__trace2 },\n \t{ \"urlmatch-normalization\", cmd__urlmatch_normalization },\n-\t{ \"xml-encode\", cmd__xml_encode },\n \t{ \"wildmatch\", cmd__wildmatch },\n #ifdef GIT_WINDOWS_NATIVE\n \t{ \"windows-named-pipe\", cmd__windows_named_pipe },\ndiff --git a/t/helper/test-tool.h b/t/helper/test-tool.h\nindex 9b3c1f75267..5028730807e 100644\n--- a/t/helper/test-tool.h\n+++ b/t/helper/test-tool.h\n@@ -63,7 +63,6 @@ int cmd__subprocess(int argc, const char **argv);\n int cmd__tee(int argc, const char **argv);\n int cmd__trace2(int argc, const char **argv);\n int cmd__urlmatch_normalization(int argc, const char **argv);\n-int cmd__xml_encode(int argc, const char **argv);\n int cmd__wildmatch(int argc, const char **argv);\n #ifdef GIT_WINDOWS_NATIVE\n int cmd__windows_named_pipe(int argc, const char **argv);\ndiff --git a/t/helper/test-xml-encode.c b/t/helper/test-xml-encode.c\ndeleted file mode 100644\nindex a648bbd961c..00000000000\n--- a/t/helper/test-xml-encode.c\n+++ /dev/null\n@@ -1,80 +0,0 @@\n-#include \"test-tool.h\"\n-\n-static const char *utf8_replace_character = \"&#xfffd;\";\n-\n-/*\n- * Encodes (possibly incorrect) UTF-8 on <stdin> to <stdout>, to be embedded\n- * in an XML file.\n- */\n-int cmd__xml_encode(int argc, const char **argv)\n-{\n-\tunsigned char buf[1024], tmp[4], *tmp2 = NULL;\n-\tssize_t cur = 0, len = 1, remaining = 0;\n-\tunsigned char ch;\n-\n-\tfor (;;) {\n-\t\tif (++cur == len) {\n-\t\t\tlen = xread(0, buf, sizeof(buf));\n-\t\t\tif (!len)\n-\t\t\t\treturn 0;\n-\t\t\tif (len < 0)\n-\t\t\t\tdie_errno(\"Could not read <stdin>\");\n-\t\t\tcur = 0;\n-\t\t}\n-\t\tch = buf[cur];\n-\n-\t\tif (tmp2) {\n-\t\t\tif ((ch & 0xc0) != 0x80) {\n-\t\t\t\tfputs(utf8_replace_character, stdout);\n-\t\t\t\ttmp2 = NULL;\n-\t\t\t\tcur--;\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\t*tmp2 = ch;\n-\t\t\ttmp2++;\n-\t\t\tif (--remaining == 0) {\n-\t\t\t\tfwrite(tmp, tmp2 - tmp, 1, stdout);\n-\t\t\t\ttmp2 = NULL;\n-\t\t\t}\n-\t\t\tcontinue;\n-\t\t}\n-\n-\t\tif (!(ch & 0x80)) {\n-\t\t\t/* 0xxxxxxx */\n-\t\t\tif (ch == '&')\n-\t\t\t\tfputs(\"&amp;\", stdout);\n-\t\t\telse if (ch == '\\'')\n-\t\t\t\tfputs(\"&apos;\", stdout);\n-\t\t\telse if (ch == '\"')\n-\t\t\t\tfputs(\"&quot;\", stdout);\n-\t\t\telse if (ch == '<')\n-\t\t\t\tfputs(\"&lt;\", stdout);\n-\t\t\telse if (ch == '>')\n-\t\t\t\tfputs(\"&gt;\", stdout);\n-\t\t\telse if (ch >= 0x20)\n-\t\t\t\tfputc(ch, stdout);\n-\t\t\telse if (ch == 0x09 || ch == 0x0a || ch == 0x0d)\n-\t\t\t\tfprintf(stdout, \"&#x%02x;\", ch);\n-\t\t\telse\n-\t\t\t\tfputs(utf8_replace_character, stdout);\n-\t\t} else if ((ch & 0xe0) == 0xc0) {\n-\t\t\t/* 110XXXXx 10xxxxxx */\n-\t\t\ttmp[0] = ch;\n-\t\t\tremaining = 1;\n-\t\t\ttmp2 = tmp + 1;\n-\t\t} else if ((ch & 0xf0) == 0xe0) {\n-\t\t\t/* 1110XXXX 10Xxxxxx 10xxxxxx */\n-\t\t\ttmp[0] = ch;\n-\t\t\tremaining = 2;\n-\t\t\ttmp2 = tmp + 1;\n-\t\t} else if ((ch & 0xf8) == 0xf0) {\n-\t\t\t/* 11110XXX 10XXxxxx 10xxxxxx 10xxxxxx */\n-\t\t\ttmp[0] = ch;\n-\t\t\tremaining = 3;\n-\t\t\ttmp2 = tmp + 1;\n-\t\t} else\n-\t\t\tfputs(utf8_replace_character, stdout);\n-\t}\n-\n-\treturn 0;\n-}\ndiff --git a/t/test-lib.sh b/t/test-lib.sh\nindex 0070d05234b..4dc41eeccc2 100644\n--- a/t/test-lib.sh\n+++ b/t/test-lib.sh\n@@ -371,6 +371,17 @@ then\n \t\t--escape-stdout ${HARNESS_ACTIVE+--escape-file} \\\n \t\t\"$GIT_TEST_TEE_OUTPUT_FILE\"\n \ttest \"$(cat \"$TEST_RESULTS_BASE.exit\")\" = 0\n+\tif test -n \"$write_junit_xml\"\n+\tthen\n+\t\tjunit_xml_dir=\"$TEST_OUTPUT_DIRECTORY/out\"\n+\t\tmkdir -p \"$junit_xml_dir\"\n+\t\tjunit_xml_base=${0##*/}\n+\t\tjunit_xml_path=\"$junit_xml_dir/TEST-${junit_xml_base%.sh}.xml\"\n+\t\tjunit_attrs=\"timestamp=\\\"$(TZ=UTC date +%Y-%m-%dT%H:%M:%S)\\\"\"\n+\t\ttap2junit --name=\"$TEST_NAME\" - \\\n+\t\t\t<\"$GIT_TEST_TEE_OUTPUT_FILE\" \\\n+\t\t\t>\"$junit_xml_path\"\n+\tfi\n \texit\n elif test -n \"$verbose\" -a -n \"$HARNESS_ACTIVE\"\n then\n@@ -588,7 +599,6 @@ export TERM\n \n error () {\n \tsay_color error \"error: $*\"\n-\tfinalize_junit_xml\n \tGIT_EXIT_OK=t\n \texit 1\n }\n@@ -672,56 +682,24 @@ trap '{ code=$?; set +x; } 2>/dev/null; exit $code' INT TERM HUP\n # the test_expect_* functions instead.\n \n test_ok_ () {\n-\tif test -n \"$write_junit_xml\"\n-\tthen\n-\t\twrite_junit_xml_testcase \"$*\"\n-\tfi\n \ttest_success=$(($test_success + 1))\n \tsay_color_tap \"${verbose:+pass}\" \"ok $test_count - $@\"\n }\n \n test_failure_ () {\n-\tif test -n \"$write_junit_xml\"\n-\tthen\n-\t\tjunit_insert=\"<failure message=\\\"not ok $test_count -\"\n-\t\tjunit_insert=\"$junit_insert $(xml_attr_encode \"$1\")\\\">\"\n-\t\tjunit_insert=\"$junit_insert $(xml_attr_encode \\\n-\t\t\t\"$(if test -n \"$GIT_TEST_TEE_OUTPUT_FILE\"\n-\t\t\t   then\n-\t\t\t\ttest-tool path-utils skip-n-bytes \\\n-\t\t\t\t\t\"$GIT_TEST_TEE_OUTPUT_FILE\" $GIT_TEST_TEE_OFFSET\n-\t\t\t   else\n-\t\t\t\tprintf '%s\\n' \"$@\" | sed 1d\n-\t\t\t   fi)\")\"\n-\t\tjunit_insert=\"$junit_insert</failure>\"\n-\t\tif test -n \"$GIT_TEST_TEE_OUTPUT_FILE\"\n-\t\tthen\n-\t\t\tjunit_insert=\"$junit_insert<system-err>$(xml_attr_encode \\\n-\t\t\t\t\"$(cat \"$GIT_TEST_TEE_OUTPUT_FILE\")\")</system-err>\"\n-\t\tfi\n-\t\twrite_junit_xml_testcase \"$1\" \"      $junit_insert\"\n-\tfi\n \ttest_failure=$(($test_failure + 1))\n \tsay_color_tap error \"not ok $test_count - $1\"\n \tshift\n \tprintf '%s\\n' \"$*\" | sed -e 's/^/#\t/'\n-\ttest \"$immediate\" = \"\" || { finalize_junit_xml; GIT_EXIT_OK=t; exit 1; }\n+\ttest \"$immediate\" = \"\" || { GIT_EXIT_OK=t; exit 1; }\n }\n \n test_known_broken_ok_ () {\n-\tif test -n \"$write_junit_xml\"\n-\tthen\n-\t\twrite_junit_xml_testcase \"$* (breakage fixed)\"\n-\tfi\n \ttest_fixed=$(($test_fixed+1))\n \tsay_color error \"ok $test_count - $@ # TODO known breakage vanished\"\n }\n \n test_known_broken_failure_ () {\n-\tif test -n \"$write_junit_xml\"\n-\tthen\n-\t\twrite_junit_xml_testcase \"$* (known breakage)\"\n-\tfi\n \ttest_broken=$(($test_broken+1))\n \tsay_color_tap warn \"not ok $test_count - $@ # TODO known breakage\"\n }\n@@ -983,10 +961,6 @@ test_start_ () {\n \ttest_count=$(($test_count+1))\n \tmaybe_setup_verbose\n \tmaybe_setup_valgrind\n-\tif test -n \"$write_junit_xml\"\n-\tthen\n-\t\tjunit_start=$(test-tool date getnanos)\n-\tfi\n }\n \n test_finish_ () {\n@@ -1029,13 +1003,6 @@ test_skip () {\n \n \tcase \"$to_skip\" in\n \tt)\n-\t\tif test -n \"$write_junit_xml\"\n-\t\tthen\n-\t\t\tmessage=\"$(xml_attr_encode \"$skipped_reason\")\"\n-\t\t\twrite_junit_xml_testcase \"$1\" \\\n-\t\t\t\t\"      <skipped message=\\\"$message\\\" />\"\n-\t\tfi\n-\n \t\tsay_color_tap skip \"ok $test_count # skip $1 ($skipped_reason)\"\n \t\t: true\n \t\t;;\n@@ -1050,53 +1017,6 @@ test_at_end_hook_ () {\n \t:\n }\n \n-write_junit_xml () {\n-\tcase \"$1\" in\n-\t--truncate)\n-\t\t>\"$junit_xml_path\"\n-\t\tjunit_have_testcase=\n-\t\tshift\n-\t\t;;\n-\tesac\n-\tprintf '%s\\n' \"$@\" >>\"$junit_xml_path\"\n-}\n-\n-xml_attr_encode () {\n-\tprintf '%s\\n' \"$@\" | test-tool xml-encode\n-}\n-\n-write_junit_xml_testcase () {\n-\tjunit_attrs=\"name=\\\"$(xml_attr_encode \"$this_test.$test_count $1\")\\\"\"\n-\tshift\n-\tjunit_attrs=\"$junit_attrs classname=\\\"$this_test\\\"\"\n-\tjunit_attrs=\"$junit_attrs time=\\\"$(test-tool \\\n-\t\tdate getnanos $junit_start)\\\"\"\n-\twrite_junit_xml \"$(printf '%s\\n' \\\n-\t\t\"    <testcase $junit_attrs>\" \"$@\" \"    </testcase>\")\"\n-\tjunit_have_testcase=t\n-}\n-\n-finalize_junit_xml () {\n-\tif test -n \"$write_junit_xml\" && test -n \"$junit_xml_path\"\n-\tthen\n-\t\ttest -n \"$junit_have_testcase\" || {\n-\t\t\tjunit_start=$(test-tool date getnanos)\n-\t\t\twrite_junit_xml_testcase \"all tests skipped\"\n-\t\t}\n-\n-\t\t# adjust the overall time\n-\t\tjunit_time=$(test-tool date getnanos $junit_suite_start)\n-\t\tsed -e \"s/\\(<testsuite.*\\) time=\\\"[^\\\"]*\\\"/\\1/\" \\\n-\t\t\t-e \"s/<testsuite [^>]*/& time=\\\"$junit_time\\\"/\" \\\n-\t\t\t-e '/^ *<\\/testsuite/d' \\\n-\t\t\t<\"$junit_xml_path\" >\"$junit_xml_path.new\"\n-\t\tmv \"$junit_xml_path.new\" \"$junit_xml_path\"\n-\n-\t\twrite_junit_xml \"  </testsuite>\" \"</testsuites>\"\n-\t\twrite_junit_xml=\n-\tfi\n-}\n-\n test_atexit_cleanup=:\n test_atexit_handler () {\n \t# In a succeeding test script 'test_atexit_handler' is invoked\n@@ -1119,8 +1039,6 @@ test_done () {\n \t# removed, so the commands can access pidfiles and socket files.\n \ttest_atexit_handler\n \n-\tfinalize_junit_xml\n-\n \tif test -z \"$HARNESS_ACTIVE\"\n \tthen\n \t\tmkdir -p \"$TEST_RESULTS_DIR\"\n@@ -1364,23 +1282,6 @@ then\n \ttest_done\n fi\n \n-if test -n \"$write_junit_xml\"\n-then\n-\tjunit_xml_dir=\"$TEST_OUTPUT_DIRECTORY/out\"\n-\tmkdir -p \"$junit_xml_dir\"\n-\tjunit_xml_base=${0##*/}\n-\tjunit_xml_path=\"$junit_xml_dir/TEST-${junit_xml_base%.sh}.xml\"\n-\tjunit_attrs=\"name=\\\"${junit_xml_base%.sh}\\\"\"\n-\tjunit_attrs=\"$junit_attrs timestamp=\\\"$(TZ=UTC \\\n-\t\tdate +%Y-%m-%dT%H:%M:%S)\\\"\"\n-\twrite_junit_xml --truncate \"<testsuites>\" \"  <testsuite $junit_attrs>\"\n-\tjunit_suite_start=$(test-tool date getnanos)\n-\tif test -n \"$GIT_TEST_TEE_OUTPUT_FILE\"\n-\tthen\n-\t\tGIT_TEST_TEE_OFFSET=0\n-\tfi\n-fi\n-\n # Convenience\n # A regexp to match 5, 35 and 40 hexdigits\n _x05='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'\n-- \n2.31.0.rc1.210.g0f8085a843c\n\n"},{"id":"418612","messageId":"20210309160219.13779-7-avarab@gmail.com","threadId":"44349","inReplyTo":"87r1kzj7xi.fsf@evledraar.gmail.com","subject":"[PATCH 6/7] test-lib: make --verbose output valid TAP","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-09T16:02:18Z","receivedAt":"2021-03-09T16:03:20Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Make the --verbose output be valid TAP, making it machine-readable for\nTAP parsers again.\n\nBoth the verbose and non-verbose test outputs were valid TAP back when\nI added support for TAP in 5099b99d25f (test-lib: Adjust output to be\nvalid TAP format, 2010-06-24).\n\nSometime after that the --verbose output broke due to some tests\nemitting their own lines starting \"ok\" (or otherwise invalidate the\nTAP). That was noticed and fixed in 452320f1f5 (test-lib: add\n--verbose-log option, 2016-10-21) and \"fixed\" by simply turning off\nthe verbose mode when we were running under TAP::Harness (e.g. under\n\"prove\").\n\nThat solution worked for running under Travis CI. After that fix it\nwas made to use the --verbose-log option in 041c72de109 (travis: use\n--verbose-log test option, 2016-10-21), see 522354d70f4 (Add Travis CI\nsupport, 2015-11-27) for the \"cat t/test-results/*.out\" code that was\naimed at.\n\nBut that solution and others discussed in 452320f1f5 closed the door\non us having reliable machine-readable TAP output.\n\nLet's instead revert the work done in 452320f1f5 and, as well as the\nfollow-up commits 88c6e9d31c (test-lib: --valgrind should not override\n--verbose-log, 2017-09-05) and f5ba2de6bc (test-lib: make \"-x\" work\nwith \"--verbose-log\", 2017-12-08), which were only needed to work\naround bugs in the the previous --verbose-log implementation.\n\nReplace it with a simple method for ensuring that we have valid TAP\nboth on stdout, and in any verbose output we write. When we detect\nthat we're running under \"prove\" we prefix all legitimate TAP\ndirectives with \"GIT_TEST_TEE_STARTED\":\n\n    $ GIT_TEST_TEE_STARTED=1 ./t5547-push-quarantine.sh\n    GIT_TEST_TEE_STARTED ok 1 - create picky dest repo\n    GIT_TEST_TEE_STARTED ok 2 - accepted objects work\n    [...]\n    GIT_TEST_TEE_STARTED 1..6\n\nThen, instead of piping the output to \"tee -a\" we pipe it to a helper\nwhich first converts \"ok\" and other TAP syntax to e.g. \"\\ok\", and then\nstrips that \"GIT_TEST_TEE_STARTED \" prefix from the start of the line.\n\nThe end result is that we're guaranteed to have valid TAP syntax on\nstdout.\n\nWe can thus get rid of the --verbose-log special-case. Since that\noption was meant to get around the TAP issue let's simply make it an\nalias for \"--verbose --tee\".\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/README         |  9 ++++++---\n t/t0000-basic.sh |  8 ++++++++\n t/test-lib.sh    | 43 +++++++++++++++++++++----------------------\n 3 files changed, 35 insertions(+), 25 deletions(-)\n\ndiff --git a/t/README b/t/README\nindex 2cc8cbc7185..f09d94e754e 100644\n--- a/t/README\n+++ b/t/README\n@@ -157,10 +157,13 @@ appropriately before running \"make\". Short options can be bundled, i.e.\n \n -V::\n --verbose-log::\n-\tWrite verbose output to the same logfile as `--tee`, but do\n-\t_not_ write it to stdout. Unlike `--tee --verbose`, this option\n+\tAn alias for `--verbose --tee`. This option\n \tis safe to use when stdout is being consumed by a TAP parser\n-\tlike `prove`. Implies `--tee` and `--verbose`.\n+\tlike `prove`.\n+\tHistorically this option was different from `--verbose --tee`\n+\tand would not write any verbose output to stdout to ensure the\n+\tTAP-correctness of the output. The TAP-correctness of the\n+\toutput is now sanity checked by the test library,\n \n --with-dashes::\n \tBy default tests are run without dashed forms of\ndiff --git a/t/t0000-basic.sh b/t/t0000-basic.sh\nindex 705d62cc27a..2a95a612fb0 100755\n--- a/t/t0000-basic.sh\n+++ b/t/t0000-basic.sh\n@@ -76,6 +76,14 @@ _run_sub_test_lib_test_common () {\n \t\t# this variable, so we need a stable setting under which to run\n \t\t# the sub-test.\n \t\tsane_unset HARNESS_ACTIVE &&\n+\n+\t\t# When under --tee pretend we're not as far as\n+\t\t# emitting the \"GIT_TEST_TEE_STARTED \" TAP sanity\n+\t\t# prefix goes. This means we promise not to emit\n+\t\t# invalid TAP, least this test fails under --tee under\n+\t\t# prove(1).\n+\t\tsane_unset GIT_TEST_TEE_STARTED &&\n+\n \t\tcd \"$name\" &&\n \t\twrite_script \"$name.sh\" \"$TEST_SHELL_PATH\" <<-EOF &&\n \t\ttest_description='$descr (run in sub test-lib)\ndiff --git a/t/test-lib.sh b/t/test-lib.sh\nindex aa7068b06b6..0070d05234b 100644\n--- a/t/test-lib.sh\n+++ b/t/test-lib.sh\n@@ -146,9 +146,11 @@ parse_option () {\n \t--no-chain-lint)\n \t\tGIT_TEST_CHAIN_LINT=0 ;;\n \t-x)\n-\t\ttrace=t ;;\n+\t\ttrace=t\n+\t\tverbose=t\n+\t\t;;\n \t-V|--verbose-log)\n-\t\tverbose_log=t\n+\t\tverbose=t\n \t\ttee=t\n \t\t;;\n \t--write-junit-xml)\n@@ -233,7 +235,7 @@ then\n \ttest -z \"$verbose\" && verbose_only=\"$valgrind_only\"\n elif test -n \"$valgrind\"\n then\n-\ttest -z \"$verbose_log\" && verbose=t\n+\tverbose=t\n fi\n \n if test -n \"$stress\"\n@@ -364,9 +366,22 @@ then\n \t(\n \t\tGIT_TEST_TEE_STARTED=done ${TEST_SHELL_PATH} \"$0\" \"$@\" 2>&1\n \t\techo $? >\"$TEST_RESULTS_BASE.exit\"\n-\t) | tee -a \"$GIT_TEST_TEE_OUTPUT_FILE\"\n+\t) | \"$GIT_BUILD_DIR\"/t/helper/test-tool tee \\\n+\t\t--tap --prefix=\"GIT_TEST_TEE_STARTED \" \\\n+\t\t--escape-stdout ${HARNESS_ACTIVE+--escape-file} \\\n+\t\t\"$GIT_TEST_TEE_OUTPUT_FILE\"\n \ttest \"$(cat \"$TEST_RESULTS_BASE.exit\")\" = 0\n \texit\n+elif test -n \"$verbose\" -a -n \"$HARNESS_ACTIVE\"\n+then\n+\tret=\n+\t(\n+\t\tGIT_TEST_TEE_STARTED=done ${TEST_SHELL_PATH} \"$0\" \"$@\" 2>&1\n+\t\tret=$?\n+\t) | \"$GIT_BUILD_DIR\"/t/helper/test-tool tee \\\n+\t\t--tap --prefix=\"GIT_TEST_TEE_STARTED \" \\\n+\t\t--escape-stdout\n+\texit $ret\n fi\n \n if test -n \"$trace\" && test -n \"$test_untraceable\"\n@@ -392,10 +407,6 @@ then\n \t\ttrace=\n \tfi\n fi\n-if test -n \"$trace\" && test -z \"$verbose_log\"\n-then\n-\tverbose=t\n-fi\n \n # For repeatability, reset the environment to known value.\n # TERM is sanitized below, after saving color control sequences.\n@@ -569,7 +580,7 @@ say_color_tap() {\n \ttest \"$#\" = 2 ||\n \tBUG \"not 2 parameters to say_color_tap\"\n \n-\tsay_color \"$1\" \"$2\"\n+\tsay_color \"$1\" \"${GIT_TEST_TEE_STARTED:+GIT_TEST_TEE_STARTED }$2\"\n }\n \n TERM=dumb\n@@ -590,16 +601,6 @@ say () {\n \tsay_color info \"$*\"\n }\n \n-if test -n \"$HARNESS_ACTIVE\"\n-then\n-\tif test \"$verbose\" = t || test -n \"$verbose_only\"\n-\tthen\n-\t\tprintf 'Bail out! %s\\n' \\\n-\t\t 'verbose mode forbidden under TAP harness; try --verbose-log'\n-\t\texit 1\n-\tfi\n-fi\n-\n test \"${test_description}\" != \"\" ||\n error \"Test script did not set test_description.\"\n \n@@ -887,9 +888,7 @@ maybe_setup_valgrind () {\n \n trace_level_=0\n want_trace () {\n-\ttest \"$trace\" = t && {\n-\t\ttest \"$verbose\" = t || test \"$verbose_log\" = t\n-\t}\n+\ttest \"$trace\" = t && test \"$verbose\" = t\n }\n \n # This is a separate function because some tests use\n-- \n2.31.0.rc1.210.g0f8085a843c\n\n"},{"id":"418622","messageId":"20210309175249.GE3590451@szeder.dev","threadId":"44349","inReplyTo":"20210309160219.13779-1-avarab@gmail.com","subject":"Re: [PATCH 0/6 + 1] test-lib: make --verbose output valid TAP","fromName":"SZEDER Gábor","fromEmail":"szeder.dev@gmail.com","sentAt":"2021-03-09T17:52:49Z","receivedAt":"2021-03-09T17:53:47Z","isPatch":true,"sender":{"key":"szeder.dev@gmail.com","avatar":"https://avatars.githubusercontent.com/u/116324?v=4"},"body":"On Tue, Mar 09, 2021 at 05:02:12PM +0100, Ævar Arnfjörð Bjarmason wrote:\n> The eventual goal not included in this series is to have multiple\n> output targets, and e.g. convert ci/print-test-failures.sh to use a\n> TAP parser.\n> \n> Machine-readable \"TAP --verbose -x\" output can bring us a lot of nice\n> things down the line, I have some local WIP code that's a smarter\n> version of ci/print-test-failures.sh that knows how to spew out only\n> the output relevant to the failing test(s).\n\nI wonder what you mean by the word \"relevant\" here, as I can't imagine\nhow you could possibly identify what is relevant for a failing test\nand what isn't.  If you didn't at all meant \"relevant\", but that it\nwill show only the output of the failing test(s), then this is a bad\nexample.  Our test cases depend too much on previous test cases, and a\nfailure of one test can be the result of a change in a previous\nsuccessful tests.\n\nTherefore, any such change to 'ci/print-test-failures.sh' will have my\nfirm NACK.\n\n"},{"id":"418631","messageId":"20210309185911.GF3590451@szeder.dev","threadId":"44349","inReplyTo":"20210309160219.13779-7-avarab@gmail.com","subject":"Re: [PATCH 6/7] test-lib: make --verbose output valid TAP","fromName":"SZEDER Gábor","fromEmail":"szeder.dev@gmail.com","sentAt":"2021-03-09T18:59:11Z","receivedAt":"2021-03-09T18:59:52Z","isPatch":true,"sender":{"key":"szeder.dev@gmail.com","avatar":"https://avatars.githubusercontent.com/u/116324?v=4"},"body":"On Tue, Mar 09, 2021 at 05:02:18PM +0100, Ævar Arnfjörð Bjarmason wrote:\n> Make the --verbose output be valid TAP, making it machine-readable for\n> TAP parsers again.\n> \n> Both the verbose and non-verbose test outputs were valid TAP back when\n> I added support for TAP in 5099b99d25f (test-lib: Adjust output to be\n> valid TAP format, 2010-06-24).\n> \n> Sometime after that the --verbose output broke due to some tests\n> emitting their own lines starting \"ok\" (or otherwise invalidate the\n> TAP). That was noticed and fixed in 452320f1f5 (test-lib: add\n> --verbose-log option, 2016-10-21) and \"fixed\" by simply turning off\n> the verbose mode when we were running under TAP::Harness (e.g. under\n> \"prove\").\n> \n> That solution worked for running under Travis CI. After that fix it\n> was made to use the --verbose-log option in 041c72de109 (travis: use\n> --verbose-log test option, 2016-10-21), see 522354d70f4 (Add Travis CI\n> support, 2015-11-27) for the \"cat t/test-results/*.out\" code that was\n> aimed at.\n> \n> But that solution and others discussed in 452320f1f5 closed the door\n> on us having reliable machine-readable TAP output.\n> \n> Let's instead revert the work done in 452320f1f5 and, as well as the\n> follow-up commits 88c6e9d31c (test-lib: --valgrind should not override\n> --verbose-log, 2017-09-05) and f5ba2de6bc (test-lib: make \"-x\" work\n> with \"--verbose-log\", 2017-12-08), which were only needed to work\n> around bugs in the the previous --verbose-log implementation.\n> \n> Replace it with a simple method for ensuring that we have valid TAP\n> both on stdout, and in any verbose output we write. When we detect\n> that we're running under \"prove\" we prefix all legitimate TAP\n> directives with \"GIT_TEST_TEE_STARTED\":\n> \n>     $ GIT_TEST_TEE_STARTED=1 ./t5547-push-quarantine.sh\n>     GIT_TEST_TEE_STARTED ok 1 - create picky dest repo\n>     GIT_TEST_TEE_STARTED ok 2 - accepted objects work\n>     [...]\n>     GIT_TEST_TEE_STARTED 1..6\n> \n> Then, instead of piping the output to \"tee -a\" we pipe it to a helper\n> which first converts \"ok\" and other TAP syntax to e.g. \"\\ok\", and then\n> strips that \"GIT_TEST_TEE_STARTED \" prefix from the start of the line.\n> \n> The end result is that we're guaranteed to have valid TAP syntax on\n> stdout.\n> \n> We can thus get rid of the --verbose-log special-case. Since that\n> option was meant to get around the TAP issue let's simply make it an\n> alias for \"--verbose --tee\".\n> \n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n\nAfter applying this patch series there is still one place where we\nlook at $verbose_log:\n\n  $ git grep -C4 verbose_log -- test-lib.sh\n  test-lib.sh-\n  test-lib.sh-exec 5>&1\n  test-lib.sh-exec 6<&0\n  test-lib.sh-exec 7>&2\n  test-lib.sh:if test \"$verbose_log\" = \"t\"\n  test-lib.sh-then\n  test-lib.sh-    exec 3>>\"$GIT_TEST_TEE_OUTPUT_FILE\" 4>&3\n  test-lib.sh-elif test \"$verbose\" = \"t\"\n  test-lib.sh-then\n\n"},{"id":"418633","messageId":"20210309191230.GG3590451@szeder.dev","threadId":"44349","inReplyTo":"20210309160219.13779-7-avarab@gmail.com","subject":"Re: [PATCH 6/7] test-lib: make --verbose output valid TAP","fromName":"SZEDER Gábor","fromEmail":"szeder.dev@gmail.com","sentAt":"2021-03-09T19:12:30Z","receivedAt":"2021-03-09T19:13:29Z","isPatch":true,"sender":{"key":"szeder.dev@gmail.com","avatar":"https://avatars.githubusercontent.com/u/116324?v=4"},"body":"On Tue, Mar 09, 2021 at 05:02:18PM +0100, Ævar Arnfjörð Bjarmason wrote:\n> Make the --verbose output be valid TAP, making it machine-readable for\n> TAP parsers again.\n> \n> Both the verbose and non-verbose test outputs were valid TAP back when\n> I added support for TAP in 5099b99d25f (test-lib: Adjust output to be\n> valid TAP format, 2010-06-24).\n> \n> Sometime after that the --verbose output broke due to some tests\n> emitting their own lines starting \"ok\" (or otherwise invalidate the\n> TAP). That was noticed and fixed in 452320f1f5 (test-lib: add\n> --verbose-log option, 2016-10-21) and \"fixed\" by simply turning off\n> the verbose mode when we were running under TAP::Harness (e.g. under\n> \"prove\").\n> \n> That solution worked for running under Travis CI. After that fix it\n> was made to use the --verbose-log option in 041c72de109 (travis: use\n> --verbose-log test option, 2016-10-21), see 522354d70f4 (Add Travis CI\n> support, 2015-11-27) for the \"cat t/test-results/*.out\" code that was\n> aimed at.\n> \n> But that solution and others discussed in 452320f1f5 closed the door\n> on us having reliable machine-readable TAP output.\n> \n> Let's instead revert the work done in 452320f1f5 and, as well as the\n> follow-up commits 88c6e9d31c (test-lib: --valgrind should not override\n> --verbose-log, 2017-09-05) and f5ba2de6bc (test-lib: make \"-x\" work\n> with \"--verbose-log\", 2017-12-08), which were only needed to work\n> around bugs in the the previous --verbose-log implementation.\n> \n> Replace it with a simple method for ensuring that we have valid TAP\n> both on stdout, and in any verbose output we write. When we detect\n> that we're running under \"prove\" we prefix all legitimate TAP\n> directives with \"GIT_TEST_TEE_STARTED\":\n> \n>     $ GIT_TEST_TEE_STARTED=1 ./t5547-push-quarantine.sh\n>     GIT_TEST_TEE_STARTED ok 1 - create picky dest repo\n>     GIT_TEST_TEE_STARTED ok 2 - accepted objects work\n>     [...]\n>     GIT_TEST_TEE_STARTED 1..6\n> \n> Then, instead of piping the output to \"tee -a\" we pipe it to a helper\n> which first converts \"ok\" and other TAP syntax to e.g. \"\\ok\", and then\n> strips that \"GIT_TEST_TEE_STARTED \" prefix from the start of the line.\n> \n> The end result is that we're guaranteed to have valid TAP syntax on\n> stdout.\n> \n> We can thus get rid of the --verbose-log special-case. Since that\n> option was meant to get around the TAP issue let's simply make it an\n> alias for \"--verbose --tee\".\n> \n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  t/README         |  9 ++++++---\n>  t/t0000-basic.sh |  8 ++++++++\n>  t/test-lib.sh    | 43 +++++++++++++++++++++----------------------\n>  3 files changed, 35 insertions(+), 25 deletions(-)\n> \n> diff --git a/t/README b/t/README\n> index 2cc8cbc7185..f09d94e754e 100644\n> --- a/t/README\n> +++ b/t/README\n> @@ -157,10 +157,13 @@ appropriately before running \"make\". Short options can be bundled, i.e.\n>  \n>  -V::\n>  --verbose-log::\n> -\tWrite verbose output to the same logfile as `--tee`, but do\n> -\t_not_ write it to stdout. Unlike `--tee --verbose`, this option\n> +\tAn alias for `--verbose --tee`. This option\n>  \tis safe to use when stdout is being consumed by a TAP parser\n> -\tlike `prove`. Implies `--tee` and `--verbose`.\n> +\tlike `prove`.\n> +\tHistorically this option was different from `--verbose --tee`\n> +\tand would not write any verbose output to stdout to ensure the\n> +\tTAP-correctness of the output. The TAP-correctness of the\n> +\toutput is now sanity checked by the test library,\n\nNot everyone is using a TAP harness to run the tests, and, therefore,\n'--verbose-log' should not spew out verbose output to the terminal.\n\n"},{"id":"418643","messageId":"87k0qghwps.fsf@evledraar.gmail.com","threadId":"44349","inReplyTo":"20210309185911.GF3590451@szeder.dev","subject":"Re: [PATCH 6/7] test-lib: make --verbose output valid TAP","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-09T20:57:03Z","receivedAt":"2021-03-09T20:58:04Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Tue, Mar 09 2021, SZEDER Gábor wrote:\n\n> On Tue, Mar 09, 2021 at 05:02:18PM +0100, Ævar Arnfjörð Bjarmason wrote:\n>> Make the --verbose output be valid TAP, making it machine-readable for\n>> TAP parsers again.\n>> \n>> Both the verbose and non-verbose test outputs were valid TAP back when\n>> I added support for TAP in 5099b99d25f (test-lib: Adjust output to be\n>> valid TAP format, 2010-06-24).\n>> \n>> Sometime after that the --verbose output broke due to some tests\n>> emitting their own lines starting \"ok\" (or otherwise invalidate the\n>> TAP). That was noticed and fixed in 452320f1f5 (test-lib: add\n>> --verbose-log option, 2016-10-21) and \"fixed\" by simply turning off\n>> the verbose mode when we were running under TAP::Harness (e.g. under\n>> \"prove\").\n>> \n>> That solution worked for running under Travis CI. After that fix it\n>> was made to use the --verbose-log option in 041c72de109 (travis: use\n>> --verbose-log test option, 2016-10-21), see 522354d70f4 (Add Travis CI\n>> support, 2015-11-27) for the \"cat t/test-results/*.out\" code that was\n>> aimed at.\n>> \n>> But that solution and others discussed in 452320f1f5 closed the door\n>> on us having reliable machine-readable TAP output.\n>> \n>> Let's instead revert the work done in 452320f1f5 and, as well as the\n>> follow-up commits 88c6e9d31c (test-lib: --valgrind should not override\n>> --verbose-log, 2017-09-05) and f5ba2de6bc (test-lib: make \"-x\" work\n>> with \"--verbose-log\", 2017-12-08), which were only needed to work\n>> around bugs in the the previous --verbose-log implementation.\n>> \n>> Replace it with a simple method for ensuring that we have valid TAP\n>> both on stdout, and in any verbose output we write. When we detect\n>> that we're running under \"prove\" we prefix all legitimate TAP\n>> directives with \"GIT_TEST_TEE_STARTED\":\n>> \n>>     $ GIT_TEST_TEE_STARTED=1 ./t5547-push-quarantine.sh\n>>     GIT_TEST_TEE_STARTED ok 1 - create picky dest repo\n>>     GIT_TEST_TEE_STARTED ok 2 - accepted objects work\n>>     [...]\n>>     GIT_TEST_TEE_STARTED 1..6\n>> \n>> Then, instead of piping the output to \"tee -a\" we pipe it to a helper\n>> which first converts \"ok\" and other TAP syntax to e.g. \"\\ok\", and then\n>> strips that \"GIT_TEST_TEE_STARTED \" prefix from the start of the line.\n>> \n>> The end result is that we're guaranteed to have valid TAP syntax on\n>> stdout.\n>> \n>> We can thus get rid of the --verbose-log special-case. Since that\n>> option was meant to get around the TAP issue let's simply make it an\n>> alias for \"--verbose --tee\".\n>> \n>> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n\n[Relpying to both replies in this sub-thread]\n\n> After applying this patch series there is still one place where we\n> look at $verbose_log:\n>\n>   $ git grep -C4 verbose_log -- test-lib.sh\n>   test-lib.sh-\n>   test-lib.sh-exec 5>&1\n>   test-lib.sh-exec 6<&0\n>   test-lib.sh-exec 7>&2\n>   test-lib.sh:if test \"$verbose_log\" = \"t\"\n>   test-lib.sh-then\n>   test-lib.sh-    exec 3>>\"$GIT_TEST_TEE_OUTPUT_FILE\" 4>&3\n>   test-lib.sh-elif test \"$verbose\" = \"t\"\n>   test-lib.sh-then\n\nYes, well spotted. The way this patch is implemented that exec branch\nshould have been deleted. I just missed it.\n\n[From your <20210309191230.GG3590451@szeder.dev>]:\n\n>> diff --git a/t/README b/t/README\n>> index 2cc8cbc7185..f09d94e754e 100644\n>> --- a/t/README\n>> +++ b/t/README\n>> @@ -157,10 +157,13 @@ appropriately before running \"make\". Short options can be bundled, i.e.\n>>  \n>>  -V::\n>>  --verbose-log::\n>> -\tWrite verbose output to the same logfile as `--tee`, but do\n>> -\t_not_ write it to stdout. Unlike `--tee --verbose`, this option\n>> +\tAn alias for `--verbose --tee`. This option\n>>  \tis safe to use when stdout is being consumed by a TAP parser\n>> -\tlike `prove`. Implies `--tee` and `--verbose`.\n>> +\tlike `prove`.\n>> +\tHistorically this option was different from `--verbose --tee`\n>> +\tand would not write any verbose output to stdout to ensure the\n>> +\tTAP-correctness of the output. The TAP-correctness of the\n>> +\toutput is now sanity checked by the test library,\n>\n> Not everyone is using a TAP harness to run the tests, and, therefore,\n> '--verbose-log' should not spew out verbose output to the terminal.\n\nIOW even though --verbose-log was meant as a hack to make prove happy,\nyou've since come to like the \"verbose in log, but not stdout\" mode and\nwant that kept?\n\nYes, this patch takes that mode away.\n\nYes, I can change it.\n\nWould your use-case for this be satisfied by having prove(1) just emit\ndifferent output for you in this scenario, so you'd need to invoke this\nas something like:\n\n    prove <test> :: --verbose --tee # or --verbose-log\n\nBecuse the advantage of this series is that that sort of thing becomes\nreally trivial without everything needing to be hardcoded in\ntest-lib.sh, observe (this is with my series):\n\n    \n    0 $ PERL5LIB=. prove -v --formatter=SZEDERVerboseLog ./t0201-gettext-fallbacks.sh :: --verbose-log\n    # lib-gettext: No is_IS UTF-8 locale available\n    # lib-gettext: No is_IS ISO-8859-1 locale available\n    ok 1 - sanity: $GIT_INTERNAL_GETTEXT_SH_SCHEME is set (to fallthrough)\n    ok 2 - sanity: $GIT_INTERNAL_GETTEXT_TEST_FALLBACKS is set\n    ok 3 - sanity: $GIT_INTERNAL_GETTEXT_SH_SCHEME\" is fallthrough\n    ok 4 - gettext: our gettext() fallback has pass-through semantics\n    ok 5 - eval_gettext: our eval_gettext() fallback has pass-through semantics\n    ok 6 - eval_gettext: our eval_gettext() fallback can interpolate variables\n    ok 7 - eval_gettext: our eval_gettext() fallback can interpolate variables with spaces\n    ok 8 - eval_gettext: our eval_gettext() fallback can interpolate variables with spaces and quotes\n    # passed all 8 test(s)\n    1..8\n    ok\n    All tests successful.\n    Files=1, Tests=8,  1 wallclock secs ( 0.01 usr  0.01 sys +  0.07 cusr  0.01 csys =  0.10 CPU)\n    Result: PASS\n    $ wc -l test-results/t0201-gettext-fallbacks.out \n    75 test-results/t0201-gettext-fallbacks.out\n\nAll without any patching on top to the test-lib.sh, just:\n    \n    $ cat SZEDERVerboseLog.pm \n    package SZEDERVerboseLog::Session;\n    use base 'TAP::Formatter::Console::Session';\n    \n    sub result {\n        my ($self, $result) = @_;\n        return if $result->is_unknown;\n        return $self->SUPER::result($result);\n    }\n    \n    package SZEDERVerboseLog;\n    use strict;\n    use warnings;\n    use base 'TAP::Formatter::Console';\n    \n    sub open_test {\n        my ($self, $test, $parser) = @_;\n        my $session = SZEDERVerboseLog::Session->new( {\n            name            => $test,\n            formatter       => $self,\n            parser          => $parser,\n        } );\n        return $session;\n    }\n    \n    1;\n\nThe \"is_unknown\" is everything that's not TAP syntax.\n\n"},{"id":"418647","messageId":"87h7lkhwed.fsf@evledraar.gmail.com","threadId":"44349","inReplyTo":"20210309175249.GE3590451@szeder.dev","subject":"Re: [PATCH 0/6 + 1] test-lib: make --verbose output valid TAP","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-09T21:03:54Z","receivedAt":"2021-03-09T21:04:59Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Tue, Mar 09 2021, SZEDER Gábor wrote:\n\n> On Tue, Mar 09, 2021 at 05:02:12PM +0100, Ævar Arnfjörð Bjarmason wrote:\n>> The eventual goal not included in this series is to have multiple\n>> output targets, and e.g. convert ci/print-test-failures.sh to use a\n>> TAP parser.\n>> \n>> Machine-readable \"TAP --verbose -x\" output can bring us a lot of nice\n>> things down the line, I have some local WIP code that's a smarter\n>> version of ci/print-test-failures.sh that knows how to spew out only\n>> the output relevant to the failing test(s).\n>\n> I wonder what you mean by the word \"relevant\" here, as I can't imagine\n> how you could possibly identify what is relevant for a failing test\n> and what isn't.  If you didn't at all meant \"relevant\", but that it\n> will show only the output of the failing test(s), then this is a bad\n> example.  Our test cases depend too much on previous test cases, and a\n> failure of one test can be the result of a change in a previous\n> successful tests.\n>\n> Therefore, any such change to 'ci/print-test-failures.sh' will have my\n> firm NACK.\n\nOn e.g. the github CI every step in the run is an collapsable button, so\nwe could have our cake and eat it too here.\n\nIt seems to me like a sane default would be to have an equivalent to\n\"print-test-failures.sh\" that only prints the --verbose output for the\nfailing tests, and a \"print-all-output-for-failing-tests.sh\" or whatever\nwhich gave you the full output.\n\nEven then, there seem to me to be some low hanging fruit for\nabbreviating even that output. E.g. if you we have 100 tests and we\nfailed only on the 5th, isn't the --verbose -x output up to and\nincluding the 5th going to be enough, or do we need it for the other 95?\n\nThere's also ways to unambiguously abbreviate the output, and that's now\neasy to implement as shown in my other just-sent reply to you.\n\nE.g. a lot of our test setup is really repetitive, so under -x you're\nsometimes looking at the same 5-10+ lines of test setup. We could just\ndo a couple of passes over the output and change output like:\n\n    + foo\n    + bar\n    + he\n    + foo\n    + bar\n    + llo\n    + foo\n    + bar\n    + world\n\nto:\n\n    $1 = +foo\n         +bar\n    + he\n    $1\n    + llo\n    $1\n    + world\n\nOr whatever, I'm not advocating for the specific syntax there, just for\nthe output to be nicer.\n\n\n\n"},{"id":"418651","messageId":"20210309213117.GB1016223@szeder.dev","threadId":"44349","inReplyTo":"87k0qghwps.fsf@evledraar.gmail.com","subject":"Re: [PATCH 6/7] test-lib: make --verbose output valid TAP","fromName":"SZEDER Gábor","fromEmail":"szeder.dev@gmail.com","sentAt":"2021-03-09T21:31:17Z","receivedAt":"2021-03-09T21:32:10Z","isPatch":true,"sender":{"key":"szeder.dev@gmail.com","avatar":"https://avatars.githubusercontent.com/u/116324?v=4"},"body":"On Tue, Mar 09, 2021 at 09:57:03PM +0100, Ævar Arnfjörð Bjarmason wrote:\n> >> diff --git a/t/README b/t/README\n> >> index 2cc8cbc7185..f09d94e754e 100644\n> >> --- a/t/README\n> >> +++ b/t/README\n> >> @@ -157,10 +157,13 @@ appropriately before running \"make\". Short options can be bundled, i.e.\n> >>  \n> >>  -V::\n> >>  --verbose-log::\n> >> -\tWrite verbose output to the same logfile as `--tee`, but do\n> >> -\t_not_ write it to stdout. Unlike `--tee --verbose`, this option\n> >> +\tAn alias for `--verbose --tee`. This option\n> >>  \tis safe to use when stdout is being consumed by a TAP parser\n> >> -\tlike `prove`. Implies `--tee` and `--verbose`.\n> >> +\tlike `prove`.\n> >> +\tHistorically this option was different from `--verbose --tee`\n> >> +\tand would not write any verbose output to stdout to ensure the\n> >> +\tTAP-correctness of the output. The TAP-correctness of the\n> >> +\toutput is now sanity checked by the test library,\n> >\n> > Not everyone is using a TAP harness to run the tests, and, therefore,\n> > '--verbose-log' should not spew out verbose output to the terminal.\n> \n> IOW even though --verbose-log was meant as a hack to make prove happy,\n> you've since come to like the \"verbose in log, but not stdout\" mode and\n> want that kept?\n\nYes, '--verbose-log' proved to be really convenient, even if it was\nmeant to solve a different issue.\n\n> Yes, this patch takes that mode away.\n> \n> Yes, I can change it.\n> \n> Would your use-case for this be satisfied by having prove(1) just emit\n> different output for you in this scenario, so you'd need to invoke this\n> as something like:\n> \n>     prove <test> :: --verbose --tee # or --verbose-log\n\nI use prove to run the test suite, but I don't and won't use prove to\nrun a single test.\n\nThe behavior of './t1234-foo.sh -V' with or without '-x' should not\nchange without _very_ convincing reasons.  \"We now output valid TAP\neven with --verbose, so we don't need it for the TAP harness\" is\ndefinitely not convincing.\n\n> Becuse the advantage of this series is that that sort of thing becomes\n> really trivial without everything needing to be hardcoded in\n> test-lib.sh, observe (this is with my series):\n> \n>     \n>     0 $ PERL5LIB=. prove -v --formatter=SZEDERVerboseLog ./t0201-gettext-fallbacks.sh :: --verbose-log\n\nWell, this doesn't look trivial at all, does it?  In fact, I consider\nthis unusably convoluted.\n\n>     # lib-gettext: No is_IS UTF-8 locale available\n>     # lib-gettext: No is_IS ISO-8859-1 locale available\n>     ok 1 - sanity: $GIT_INTERNAL_GETTEXT_SH_SCHEME is set (to fallthrough)\n>     ok 2 - sanity: $GIT_INTERNAL_GETTEXT_TEST_FALLBACKS is set\n>     ok 3 - sanity: $GIT_INTERNAL_GETTEXT_SH_SCHEME\" is fallthrough\n>     ok 4 - gettext: our gettext() fallback has pass-through semantics\n>     ok 5 - eval_gettext: our eval_gettext() fallback has pass-through semantics\n>     ok 6 - eval_gettext: our eval_gettext() fallback can interpolate variables\n>     ok 7 - eval_gettext: our eval_gettext() fallback can interpolate variables with spaces\n>     ok 8 - eval_gettext: our eval_gettext() fallback can interpolate variables with spaces and quotes\n>     # passed all 8 test(s)\n>     1..8\n>     ok\n>     All tests successful.\n>     Files=1, Tests=8,  1 wallclock secs ( 0.01 usr  0.01 sys +  0.07 cusr  0.01 csys =  0.10 CPU)\n>     Result: PASS\n>     $ wc -l test-results/t0201-gettext-fallbacks.out \n>     75 test-results/t0201-gettext-fallbacks.out\n> \n> All without any patching on top to the test-lib.sh, just:\n>     \n>     $ cat SZEDERVerboseLog.pm \n>     package SZEDERVerboseLog::Session;\n>     use base 'TAP::Formatter::Console::Session';\n>     \n>     sub result {\n>         my ($self, $result) = @_;\n>         return if $result->is_unknown;\n>         return $self->SUPER::result($result);\n>     }\n>     \n>     package SZEDERVerboseLog;\n>     use strict;\n>     use warnings;\n>     use base 'TAP::Formatter::Console';\n>     \n>     sub open_test {\n>         my ($self, $test, $parser) = @_;\n>         my $session = SZEDERVerboseLog::Session->new( {\n>             name            => $test,\n>             formatter       => $self,\n>             parser          => $parser,\n>         } );\n>         return $session;\n>     }\n>     \n>     1;\n> \n> The \"is_unknown\" is everything that's not TAP syntax.\n> \n"},{"id":"418658","messageId":"20210309220717.GC1016223@szeder.dev","threadId":"44349","inReplyTo":"87h7lkhwed.fsf@evledraar.gmail.com","subject":"Re: [PATCH 0/6 + 1] test-lib: make --verbose output valid TAP","fromName":"SZEDER Gábor","fromEmail":"szeder.dev@gmail.com","sentAt":"2021-03-09T22:07:17Z","receivedAt":"2021-03-09T22:07:54Z","isPatch":true,"sender":{"key":"szeder.dev@gmail.com","avatar":"https://avatars.githubusercontent.com/u/116324?v=4"},"body":"On Tue, Mar 09, 2021 at 10:03:54PM +0100, Ævar Arnfjörð Bjarmason wrote:\n> \n> On Tue, Mar 09 2021, SZEDER Gábor wrote:\n> \n> > On Tue, Mar 09, 2021 at 05:02:12PM +0100, Ævar Arnfjörð Bjarmason wrote:\n> >> The eventual goal not included in this series is to have multiple\n> >> output targets, and e.g. convert ci/print-test-failures.sh to use a\n> >> TAP parser.\n> >> \n> >> Machine-readable \"TAP --verbose -x\" output can bring us a lot of nice\n> >> things down the line, I have some local WIP code that's a smarter\n> >> version of ci/print-test-failures.sh that knows how to spew out only\n> >> the output relevant to the failing test(s).\n> >\n> > I wonder what you mean by the word \"relevant\" here, as I can't imagine\n> > how you could possibly identify what is relevant for a failing test\n> > and what isn't.  If you didn't at all meant \"relevant\", but that it\n> > will show only the output of the failing test(s), then this is a bad\n> > example.  Our test cases depend too much on previous test cases, and a\n> > failure of one test can be the result of a change in a previous\n> > successful tests.\n> >\n> > Therefore, any such change to 'ci/print-test-failures.sh' will have my\n> > firm NACK.\n> \n> On e.g. the github CI every step in the run is an collapsable button, so\n> we could have our cake and eat it too here.\n\nWell, one of the things I didn't like in GitHub CI is that I had to\nclick a lot to get to the information I wanted...\n\n> It seems to me like a sane default would be to have an equivalent to\n> \"print-test-failures.sh\" that only prints the --verbose output for the\n> failing tests, and a \"print-all-output-for-failing-tests.sh\" or whatever\n> which gave you the full output.\n> \n> Even then, there seem to me to be some low hanging fruit for\n> abbreviating even that output. E.g. if you we have 100 tests and we\n> failed only on the 5th, isn't the --verbose -x output up to and\n> including the 5th going to be enough, or do we need it for the other 95?\n\nI have a test fix waiting to be sent out, where a failing git command\nin the 'test_when_finished' block of test #21 went unnoticed because\nof the broken && chain, and ultimately caused the failure of test\n#91.\n\nIn my opinion your proposed changes to 'ci/print-test-failures.sh'\nwould make things worse.\n\n"},{"id":"418670","messageId":"xmqqy2evyh7n.fsf@gitster.c.googlers.com","threadId":"44349","inReplyTo":"20210309160219.13779-3-avarab@gmail.com","subject":"Re: [PATCH 2/7] test-lib: add say_color_tap helper to emit TAP format","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-03-10T00:39:56Z","receivedAt":"2021-03-10T00:41:03Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> Follow-up 5099b99d25 (test-lib: Adjust output to be valid TAP format,\n> 2010-06-24) and make it easy to distinguishing when we're emitting TAP\n> directives (with the new \"say_color_tap\"), and when we just want to\n> say something with colored text \"say_color\".\n\nOK.\n\n> @@ -666,7 +673,7 @@ test_ok_ () {\n>  \t\twrite_junit_xml_testcase \"$*\"\n>  \tfi\n>  \ttest_success=$(($test_success + 1))\n> -\tsay_color \"\" \"ok $test_count - $@\"\n> +\tsay_color_tap \"\" \"ok $test_count - $@\"\n\nNot your fault, but let's not forget that we should clean this up\nnot to use \"$@\" here (and instead use $*).\n\n> -\tsay_color warn \"not ok $test_count - $@ # TODO known breakage\"\n> +\tsay_color_tap warn \"not ok $test_count - $@ # TODO known breakage\"\n\nLikewise.\n\nYes, I know say_color (and hence its heir say_color_tap) accepts\nmultiple arguments and eventually pastes them into a single string\nwith \"$*\", but there is no point in producing multiple arguments out\nof \"$@\" here in this case.\n\n\n\nfn0 () {\n\tcnt=0\n\tfor arg\n\tdo\n\t\techo \"$cnt: <$arg>\"\n\t\tcnt=$(( cnt+1 ))\n\tdone\n}\n\nfn1 () {\n\tfn0 \"not - $@ # TODO\"\n}\n\nfn1 1 \"2 3\"\n\n---> \n\n0: <not - 1>\n1: <2 3 # TODO>\n"},{"id":"418671","messageId":"xmqqsg53yg28.fsf@gitster.c.googlers.com","threadId":"44349","inReplyTo":"20210309160219.13779-2-avarab@gmail.com","subject":"Re: [PATCH 1/7] test-lib: remove test_external","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-03-10T01:04:47Z","receivedAt":"2021-03-10T01:05:30Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> Remove the test_external function(s) in favor of running the Perl\n> tests with a test_expect_success.\n> ...\n> My motivation for this is to eliminate a special case where things\n> that aren't test-lib.sh are going to produce TAP, for reasons that'll\n> be clear in subsequent commits.\n\nPuzzled.\n\n>  .../netrc/t-git-credential-netrc.sh           |  7 +-\n>  t/README                                      | 26 ------\n>  t/t0202-gettext-perl.sh                       | 10 +--\n>  t/t9700-perl-git.sh                           | 10 +--\n>  t/test-lib-functions.sh                       | 89 +------------------\n>  t/test-lib.sh                                 | 42 ++++-----\n>  6 files changed, 28 insertions(+), 156 deletions(-)\n\nReducing the number of lines is always good, but is this essentially\nlosing what the commit that added test_external wanted to add?\n\n> diff --git a/contrib/credential/netrc/t-git-credential-netrc.sh b/contrib/credential/netrc/t-git-credential-netrc.sh\n> index 07227d02287..28118a9e194 100755\n> --- a/contrib/credential/netrc/t-git-credential-netrc.sh\n> +++ b/contrib/credential/netrc/t-git-credential-netrc.sh\n> @@ -20,13 +20,10 @@\n>  \t\t'set up test repository' \\\n>  \t\t'git config --add gpg.program test.git-config-gpg'\n>  \n> -\t# The external test will outputs its own plan\n> -\ttest_external_has_tap=1\n> -\n>  \texport PERL5LIB=\"$GITPERLLIB\"\n> -\ttest_external \\\n> -\t\t'git-credential-netrc' \\\n> +\ttest_expect_success 'git-credential-netrc' '\n>  \t\tperl \"$GIT_BUILD_DIR\"/contrib/credential/netrc/test.pl\n> +\t'\n>  \n>  \ttest_done\n>  )\n\nThis is valid because we expect nobody runs this under tap?\n\n> diff --git a/t/t0202-gettext-perl.sh b/t/t0202-gettext-perl.sh\n> index a29d166e007..06a93b36790 100755\n> --- a/t/t0202-gettext-perl.sh\n> +++ b/t/t0202-gettext-perl.sh\n> @@ -17,11 +17,9 @@ perl -MTest::More -e 0 2>/dev/null || {\n>  \ttest_done\n>  }\n>  \n> -# The external test will outputs its own plan\n> -test_external_has_tap=1\n> -\n> -test_external_without_stderr \\\n> -    'Perl Git::I18N API' \\\n> -    perl \"$TEST_DIRECTORY\"/t0202/test.pl\n> +test_expect_success 'run t0202/test.pl to test Git::I18N.pm' '\n> +\tperl \"$TEST_DIRECTORY\"/t0202/test.pl 2>stderr &&\n> +\ttest_must_be_empty stderr\n> +'\n\nSo t0202/test.pl would still produce output that would confuse\nwhoever is reading our output as TAP, and it is OK?  If the\nredirection discards its standard output to /dev/null [*], I would\nsort-of understand how this may work (we would have let the perl\nscript to emit 13 \"ok\" or \"not ok\" to our output, but now we discard\nthat and write just one our own \"ok\" or \"not ok\", depending on what\ncomes out to the standard error stream (e.g. \"# Looks like you\nfailed...\").\n\nBut that is not what is going on.  We'll let these 13 \"ok\" or \"not ok\"\ncome out from the perl script and then add another on our own.\n\n> diff --git a/t/t9700-perl-git.sh b/t/t9700-perl-git.sh\n> index 102c133112c..574c57b17f1 100755\n> --- a/t/t9700-perl-git.sh\n> +++ b/t/t9700-perl-git.sh\n> @@ -50,11 +50,9 @@ test_expect_success \\\n  >       git config --add test.pathmulti bar\n>       '\n>  \n> -# The external test will outputs its own plan\n> -test_external_has_tap=1\n> -\n> -test_external_without_stderr \\\n> -    'Perl API' \\\n> -    perl \"$TEST_DIRECTORY\"/t9700/test.pl\n> +test_expect_success 'use t9700/test.pl to test Git.pm' '\n> +\tperl \"$TEST_DIRECTORY\"/t9700/test.pl 2>stderr &&\n> +\ttest_must_be_empty stderr\n> +'\n\nDitto.  It seems that we are still letting the script, i.e. one of\nthe \"things that aren't test-lib.sh\" to produce TAP anyway.\n\n\n[Footnote]\n\n* If we are truly somehow discarding these output that would be TAP\n(13 tests in 0202 and uncounted in 9700) from being shown (by e.g.\nredirecting output to /dev/null), it would be a regression for those\nwho debug breakage found by these tests.  They used to be told which\none failed and how but now they don't.  So I do not think that is a\nuseful way to go, either.\n\n\n"},{"id":"418672","messageId":"xmqqo8fryfrc.fsf@gitster.c.googlers.com","threadId":"44349","inReplyTo":"20210309160219.13779-7-avarab@gmail.com","subject":"Re: [PATCH 6/7] test-lib: make --verbose output valid TAP","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-03-10T01:11:19Z","receivedAt":"2021-03-10T01:12:30Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> Replace it with a simple method for ensuring that we have valid TAP\n> both on stdout, and in any verbose output we write. When we detect\n> that we're running under \"prove\" we prefix all legitimate TAP\n> directives with \"GIT_TEST_TEE_STARTED\":\n>\n>     $ GIT_TEST_TEE_STARTED=1 ./t5547-push-quarantine.sh\n>     GIT_TEST_TEE_STARTED ok 1 - create picky dest repo\n>     GIT_TEST_TEE_STARTED ok 2 - accepted objects work\n>     [...]\n>     GIT_TEST_TEE_STARTED 1..6\n\nAh, OK, so these perl scripts that use Test::More think they are\nemitting TAP, but because the prefix is not added to any of their\noutput, they are not considered to be a tap output at all.\n\nSo did the \"external\" tests lose a lot of value with this change?\nThey used to point out individual breakage, but now it is just\n\"passed as a whole (or not)\" boolean?\n"},{"id":"418676","messageId":"878s6viw8e.fsf@evledraar.gmail.com","threadId":"44349","inReplyTo":"xmqqsg53yg28.fsf@gitster.c.googlers.com","subject":"Re: [PATCH 1/7] test-lib: remove test_external","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-10T02:22:09Z","receivedAt":"2021-03-10T02:22:56Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Wed, Mar 10 2021, Junio C Hamano wrote:\n\n> Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n>\n>> Remove the test_external function(s) in favor of running the Perl\n>> tests with a test_expect_success.\n>> ...\n>> My motivation for this is to eliminate a special case where things\n>> that aren't test-lib.sh are going to produce TAP, for reasons that'll\n>> be clear in subsequent commits.\n>\n> Puzzled.\n>\n>>  .../netrc/t-git-credential-netrc.sh           |  7 +-\n>>  t/README                                      | 26 ------\n>>  t/t0202-gettext-perl.sh                       | 10 +--\n>>  t/t9700-perl-git.sh                           | 10 +--\n>>  t/test-lib-functions.sh                       | 89 +------------------\n>>  t/test-lib.sh                                 | 42 ++++-----\n>>  6 files changed, 28 insertions(+), 156 deletions(-)\n>\n> Reducing the number of lines is always good, but is this essentially\n> losing what the commit that added test_external wanted to add?\n\nYes, I don't think streaming TAP from external sources is a problem\nworth solving in the first place.\n\n>> diff --git a/contrib/credential/netrc/t-git-credential-netrc.sh b/contrib/credential/netrc/t-git-credential-netrc.sh\n>> index 07227d02287..28118a9e194 100755\n>> --- a/contrib/credential/netrc/t-git-credential-netrc.sh\n>> +++ b/contrib/credential/netrc/t-git-credential-netrc.sh\n>> @@ -20,13 +20,10 @@\n>>  \t\t'set up test repository' \\\n>>  \t\t'git config --add gpg.program test.git-config-gpg'\n>>  \n>> -\t# The external test will outputs its own plan\n>> -\ttest_external_has_tap=1\n>> -\n>>  \texport PERL5LIB=\"$GITPERLLIB\"\n>> -\ttest_external \\\n>> -\t\t'git-credential-netrc' \\\n>> +\ttest_expect_success 'git-credential-netrc' '\n>>  \t\tperl \"$GIT_BUILD_DIR\"/contrib/credential/netrc/test.pl\n>> +\t'\n>>  \n>>  \ttest_done\n>>  )\n>\n> This is valid because we expect nobody runs this under tap?\n\nNo, because at this point we suppress the --verbose output under\nHARNESS_ACTIVE (but see 6/7 later), and under non-verbose we now emit:\n    \n    ./t9700-perl-git.sh \n    ok 1 - set up test repository\n    ok 2 - use t9700/test.pl to test Git.pm\n    # passed all 2 test(s)\n    1..2\n\nSo that the stdout from t9700/test.pl doesn't screw up the test output\nanymore.\n\n>> diff --git a/t/t0202-gettext-perl.sh b/t/t0202-gettext-perl.sh\n>> index a29d166e007..06a93b36790 100755\n>> --- a/t/t0202-gettext-perl.sh\n>> +++ b/t/t0202-gettext-perl.sh\n>> @@ -17,11 +17,9 @@ perl -MTest::More -e 0 2>/dev/null || {\n>>  \ttest_done\n>>  }\n>>  \n>> -# The external test will outputs its own plan\n>> -test_external_has_tap=1\n>> -\n>> -test_external_without_stderr \\\n>> -    'Perl Git::I18N API' \\\n>> -    perl \"$TEST_DIRECTORY\"/t0202/test.pl\n>> +test_expect_success 'run t0202/test.pl to test Git::I18N.pm' '\n>> +\tperl \"$TEST_DIRECTORY\"/t0202/test.pl 2>stderr &&\n>> +\ttest_must_be_empty stderr\n>> +'\n>\n> So t0202/test.pl would still produce output that would confuse\n> whoever is reading our output as TAP, and it is OK?  If the\n> redirection discards its standard output to /dev/null [*], I would\n> sort-of understand how this may work (we would have let the perl\n> script to emit 13 \"ok\" or \"not ok\" to our output, but now we discard\n> that and write just one our own \"ok\" or \"not ok\", depending on what\n> comes out to the standard error stream (e.g. \"# Looks like you\n> failed...\").\n>\n> But that is not what is going on.  We'll let these 13 \"ok\" or \"not ok\"\n> come out from the perl script and then add another on our own.\n\nYes, but just like we don't have stdout from any other program appear\ndirectly in the output of ./tXXXX*.sh the TAP doesn't get to our stderr\neither, i.e. our whole juggling around with FDs 1-4.\n\n>> diff --git a/t/t9700-perl-git.sh b/t/t9700-perl-git.sh\n>> index 102c133112c..574c57b17f1 100755\n>> --- a/t/t9700-perl-git.sh\n>> +++ b/t/t9700-perl-git.sh\n>> @@ -50,11 +50,9 @@ test_expect_success \\\n>   >       git config --add test.pathmulti bar\n>>       '\n>>  \n>> -# The external test will outputs its own plan\n>> -test_external_has_tap=1\n>> -\n>> -test_external_without_stderr \\\n>> -    'Perl API' \\\n>> -    perl \"$TEST_DIRECTORY\"/t9700/test.pl\n>> +test_expect_success 'use t9700/test.pl to test Git.pm' '\n>> +\tperl \"$TEST_DIRECTORY\"/t9700/test.pl 2>stderr &&\n>> +\ttest_must_be_empty stderr\n>> +'\n>\n> Ditto.  It seems that we are still letting the script, i.e. one of\n> the \"things that aren't test-lib.sh\" to produce TAP anyway.\n>\n>\n> [Footnote]\n>\n> * If we are truly somehow discarding these output that would be TAP\n> (13 tests in 0202 and uncounted in 9700) from being shown (by e.g.\n> redirecting output to /dev/null), it would be a regression for those\n> who debug breakage found by these tests.  They used to be told which\n> one failed and how but now they don't.  So I do not think that is a\n> useful way to go, either.\n\nYou still get the full TAP output under --verbose for debugging, we just\ndon't consume it as TAP anymore by \"prove\" etc, i.e. once we get to 6/7:\n    \n    $ GIT_TEST_TEE_STARTED=true HARNESS_ACTIVE=t ./t9700-perl-git.sh --verbose --tee\n    [...]\n    ok 1 - set up test repository\n    \n    ## expecting success of 9700.2 'use t9700/test.pl to test Git.pm': \n            perl \"$TEST_DIRECTORY\"/t9700/test.pl 2>stderr &&\n            test_must_be_empty stderr\n    \n    \\ok 2 - use Git;\n    \\ok 3 - open repository\n    \\ok 4 - config scalar: string\n    \\ok 5 - config array: string\n    \\ok 6 - config scalar: nonexistent\n    \\ok 7 - config array: nonexistent\n    [...]\n    \\ok 44 - cat_blob(outside): size\n    \\ok 45 - unquote unquoted path\n    \\ok 46 - unquote simple quoted path\n    \\ok 47 - unquote escape sequences\n    \\1..47\n    \n    ok 2 - use t9700/test.pl to test Git.pm\n    \n    # passed all 2 test(s)\n    1..2\n\nNotice how it's escaped now.\n\nSo yes, we don't get the benefit of having e.g. prove/CI say that our\n6th test failed anymore in this case.\n\nI think that's a very small price to pay for the benefit of not having\nto monkeypatch Perl's TAP emitting in this case to do the back & forth\nmunging to safe the TAP output I'm doing in 6/7, and only need to do in\none place because I'd gotten rid of this special-case earlier.\n"},{"id":"418677","messageId":"875z1zivlv.fsf@evledraar.gmail.com","threadId":"44349","inReplyTo":"20210309213117.GB1016223@szeder.dev","subject":"Re: [PATCH 6/7] test-lib: make --verbose output valid TAP","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-10T02:35:40Z","receivedAt":"2021-03-10T02:36:24Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Tue, Mar 09 2021, SZEDER Gábor wrote:\n\n> On Tue, Mar 09, 2021 at 09:57:03PM +0100, Ævar Arnfjörð Bjarmason wrote:\n>> >> diff --git a/t/README b/t/README\n>> >> index 2cc8cbc7185..f09d94e754e 100644\n>> >> --- a/t/README\n>> >> +++ b/t/README\n>> >> @@ -157,10 +157,13 @@ appropriately before running \"make\". Short options can be bundled, i.e.\n>> >>  \n>> >>  -V::\n>> >>  --verbose-log::\n>> >> -\tWrite verbose output to the same logfile as `--tee`, but do\n>> >> -\t_not_ write it to stdout. Unlike `--tee --verbose`, this option\n>> >> +\tAn alias for `--verbose --tee`. This option\n>> >>  \tis safe to use when stdout is being consumed by a TAP parser\n>> >> -\tlike `prove`. Implies `--tee` and `--verbose`.\n>> >> +\tlike `prove`.\n>> >> +\tHistorically this option was different from `--verbose --tee`\n>> >> +\tand would not write any verbose output to stdout to ensure the\n>> >> +\tTAP-correctness of the output. The TAP-correctness of the\n>> >> +\toutput is now sanity checked by the test library,\n>> >\n>> > Not everyone is using a TAP harness to run the tests, and, therefore,\n>> > '--verbose-log' should not spew out verbose output to the terminal.\n>> \n>> IOW even though --verbose-log was meant as a hack to make prove happy,\n>> you've since come to like the \"verbose in log, but not stdout\" mode and\n>> want that kept?\n>\n> Yes, '--verbose-log' proved to be really convenient, even if it was\n> meant to solve a different issue.\n\nSure, let's keep it then.\n\n>> Yes, this patch takes that mode away.\n>> \n>> Yes, I can change it.\n>> \n>> Would your use-case for this be satisfied by having prove(1) just emit\n>> different output for you in this scenario, so you'd need to invoke this\n>> as something like:\n>> \n>>     prove <test> :: --verbose --tee # or --verbose-log\n>\n> I use prove to run the test suite, but I don't and won't use prove to\n> run a single test.\n>\n> The behavior of './t1234-foo.sh -V' with or without '-x' should not\n> change without _very_ convincing reasons.\n\nI think I can make it the same, except for emulating the sheared write\nissue 452320f1f5 mentions. I.e. moving to this method inherently\nrequires us to squash together stdout/stderr into one, which isn't what\nwe're doing now.\n\n> \"We now output valid TAP even with --verbose, so we don't need it for\n> the TAP harness\" is definitely not convincing.\n\nIt's more like \"we produce one machine-readable output format, so if you\nneed junit, or an abbreviated format (--verbose-log) or a nyancat\nprogress bar or whatever, it makes more sense to pipe it to another\nprocess that'll munge that for you... :)\n\n>> Becuse the advantage of this series is that that sort of thing becomes\n>> really trivial without everything needing to be hardcoded in\n>> test-lib.sh, observe (this is with my series):\n>> \n>>     \n>>     0 $ PERL5LIB=. prove -v --formatter=SZEDERVerboseLog ./t0201-gettext-fallbacks.sh :: --verbose-log\n>\n> Well, this doesn't look trivial at all, does it?  In fact, I consider\n> this unusably convoluted.\n\n... I meant to say something closer to \"does that output look\nok?\". Obviously we'd then make the --verbose-log run something like that\nunder the hood.\n\nBut in any case, I found it easier to just add this feature to my \"tee\"\nprogram than doing it with Perl's TAP libraries, i.e. something like\nthis on top (will integrate it in an eventual re-roll):\n\n    diff --git a/t/helper/test-tee.c b/t/helper/test-tee.c\n    index 4ed34e9db9..063b9277e0 100644\n    --- a/t/helper/test-tee.c\n    +++ b/t/helper/test-tee.c\n    @@ -29,2 +29,3 @@ int cmd__tee(int argc, const char **argv)\n     {\n    +       int only_tap_on_stdout = 0;\n            int tap = 0;\n    @@ -38,2 +39,4 @@ int cmd__tee(int argc, const char **argv)\n            struct option options[] = {\n    +               OPT_BOOL(0, \"only-tap-on-stdout\", &only_tap_on_stdout,\n    +                        \"only emit TAP on stdout, not 'unknown' etc.\"),\n                    OPT_BOOL(0, \"escape-stdout\", &escape_stdout,\n    @@ -75,3 +78,7 @@ int cmd__tee(int argc, const char **argv)\n     \n    -               fprintf(stdout, \"%s\\n\", line.buf + offs);\n    +               if (!only_tap_on_stdout)\n    +                       fprintf(stdout, \"%s\\n\", line.buf + offs);\n    +               else if (offs)\n    +                       fprintf(stdout, \"%s\\n\", line.buf + offs);\n    +\n                    if (logfp)\n    diff --git a/t/test-lib.sh b/t/test-lib.sh\n    index 479ad4fb38..731ecc36bb 100644\n    --- a/t/test-lib.sh\n    +++ b/t/test-lib.sh\n    @@ -153,2 +153,3 @@ parse_option () {\n                    verbose=t\n    +               verbose_log=t\n                    tee=t\n    @@ -371,2 +372,3 @@ then\n                    --escape-stdout ${HARNESS_ACTIVE+--escape-file} \\\n    +               ${verbose_log+--only-tap-on-stdout} \\\n                    \"$GIT_TEST_TEE_OUTPUT_FILE\"\n\n\n>>     # lib-gettext: No is_IS UTF-8 locale available\n>>     # lib-gettext: No is_IS ISO-8859-1 locale available\n>>     ok 1 - sanity: $GIT_INTERNAL_GETTEXT_SH_SCHEME is set (to fallthrough)\n>>     ok 2 - sanity: $GIT_INTERNAL_GETTEXT_TEST_FALLBACKS is set\n>>     ok 3 - sanity: $GIT_INTERNAL_GETTEXT_SH_SCHEME\" is fallthrough\n>>     ok 4 - gettext: our gettext() fallback has pass-through semantics\n>>     ok 5 - eval_gettext: our eval_gettext() fallback has pass-through semantics\n>>     ok 6 - eval_gettext: our eval_gettext() fallback can interpolate variables\n>>     ok 7 - eval_gettext: our eval_gettext() fallback can interpolate variables with spaces\n>>     ok 8 - eval_gettext: our eval_gettext() fallback can interpolate variables with spaces and quotes\n>>     # passed all 8 test(s)\n>>     1..8\n>>     ok\n>>     All tests successful.\n>>     Files=1, Tests=8,  1 wallclock secs ( 0.01 usr  0.01 sys +  0.07 cusr  0.01 csys =  0.10 CPU)\n>>     Result: PASS\n>>     $ wc -l test-results/t0201-gettext-fallbacks.out \n>>     75 test-results/t0201-gettext-fallbacks.out\n>> \n>> All without any patching on top to the test-lib.sh, just:\n>>     \n>>     $ cat SZEDERVerboseLog.pm \n>>     package SZEDERVerboseLog::Session;\n>>     use base 'TAP::Formatter::Console::Session';\n>>     \n>>     sub result {\n>>         my ($self, $result) = @_;\n>>         return if $result->is_unknown;\n>>         return $self->SUPER::result($result);\n>>     }\n>>     \n>>     package SZEDERVerboseLog;\n>>     use strict;\n>>     use warnings;\n>>     use base 'TAP::Formatter::Console';\n>>     \n>>     sub open_test {\n>>         my ($self, $test, $parser) = @_;\n>>         my $session = SZEDERVerboseLog::Session->new( {\n>>             name            => $test,\n>>             formatter       => $self,\n>>             parser          => $parser,\n>>         } );\n>>         return $session;\n>>     }\n>>     \n>>     1;\n>> \n>> The \"is_unknown\" is everything that's not TAP syntax.\n>> \n\n"},{"id":"418680","messageId":"CAPx1GvcDvCykfOJBBPq1vx19dx2zT4cvs=ZPGBtzzw87ZDagzQ@mail.gmail.com","threadId":"44349","inReplyTo":"20210309160219.13779-7-avarab@gmail.com","subject":"Re: [PATCH 6/7] test-lib: make --verbose output valid TAP","fromName":"Chris Torek","fromEmail":"chris.torek@gmail.com","sentAt":"2021-03-10T07:42:05Z","receivedAt":"2021-03-10T07:43:40Z","isPatch":true,"sender":{"key":"chris.torek@gmail.com","avatar":"https://avatars.githubusercontent.com/u/16826774?v=4"},"body":"There is a lot of historical stuff I can't really comment on\nhere, so I'll just point out this one tiny bit of shell oddity:\n\nOn Tue, Mar 9, 2021 at 8:04 AM Ævar Arnfjörð Bjarmason <avarab@gmail.com> wrote:\n> Make the --verbose output be valid TAP, making it machine-readable for\n> TAP parsers again.\n[snippage]\n\n> diff --git a/t/test-lib.sh b/t/test-lib.sh\n> index aa7068b06b6..0070d05234b 100644\n> --- a/t/test-lib.sh\n> +++ b/t/test-lib.sh\n[snippage]\n> @@ -364,9 +366,22 @@ then\n>         (\n>                 GIT_TEST_TEE_STARTED=done ${TEST_SHELL_PATH} \"$0\" \"$@\" 2>&1\n>                 echo $? >\"$TEST_RESULTS_BASE.exit\"\n> -       ) | tee -a \"$GIT_TEST_TEE_OUTPUT_FILE\"\n> +       ) | \"$GIT_BUILD_DIR\"/t/helper/test-tool tee \\\n> +               --tap --prefix=\"GIT_TEST_TEE_STARTED \" \\\n> +               --escape-stdout ${HARNESS_ACTIVE+--escape-file} \\\n> +               \"$GIT_TEST_TEE_OUTPUT_FILE\"\n>         test \"$(cat \"$TEST_RESULTS_BASE.exit\")\" = 0\n>         exit\n> +elif test -n \"$verbose\" -a -n \"$HARNESS_ACTIVE\"\n> +then\n> +       ret=\n> +       (\n> +               GIT_TEST_TEE_STARTED=done ${TEST_SHELL_PATH} \"$0\" \"$@\" 2>&1\n> +               ret=$?\n> +       ) | \"$GIT_BUILD_DIR\"/t/helper/test-tool tee \\\n> +               --tap --prefix=\"GIT_TEST_TEE_STARTED \" \\\n> +               --escape-stdout\n> +       exit $ret\n>  fi\n>\n>  if test -n \"$trace\" && test -n \"$test_untraceable\"\n[snippage]\n\nIn the block beginning with:\n\n    ret=\n\nwe have a subshell:\n\n    ( ... ) | \"$GIT_BUILD_DIR\"/t/helper/test-tool tee ...\n\nIn the subshell itself, we set `ret=$?`.  But this is inside a\nsubshell, which then exits, so the setting of `ret` will get lost.\n\nDid you perhaps want `{ ...; }` instead here?  Unfortunately the\npipe means that the whole left side may run in a subshell\nanyway, so even that doesn't fix the problem.  We need a\ntemp file, a la the code above that dumps $? into\n\"$TEST_RESULTS_BASE.exit\".\n\nChris\n"},{"id":"419291","messageId":"8735wvfoq8.fsf@evledraar.gmail.com","threadId":"44349","inReplyTo":"875z1zivlv.fsf@evledraar.gmail.com","subject":"Re: [PATCH 6/7] test-lib: make --verbose output valid TAP","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-16T09:10:39Z","receivedAt":"2021-03-16T09:11:24Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Wed, Mar 10 2021, Ævar Arnfjörð Bjarmason wrote:\n\n> On Tue, Mar 09 2021, SZEDER Gábor wrote:\n>\n>> On Tue, Mar 09, 2021 at 09:57:03PM +0100, Ævar Arnfjörð Bjarmason wrote:\n[...]\n>>> Becuse the advantage of this series is that that sort of thing becomes\n>>> really trivial without everything needing to be hardcoded in\n>>> test-lib.sh, observe (this is with my series):\n>>> \n>>>     \n>>>     0 $ PERL5LIB=. prove -v --formatter=SZEDERVerboseLog ./t0201-gettext-fallbacks.sh :: --verbose-log\n>>\n>> Well, this doesn't look trivial at all, does it?  In fact, I consider\n>> this unusably convoluted.\n>\n> ... I meant to say something closer to \"does that output look\n> ok?\". Obviously we'd then make the --verbose-log run something like that\n> under the hood.\n>\n> But in any case, I found it easier to just add this feature to my \"tee\"\n> program than doing it with Perl's TAP libraries, i.e. something like\n> this on top (will integrate it in an eventual re-roll):\n\nFWIW I implemented this. I've got it at https://github.com/avar.git's\navar/support-test-verbose-under-prove-2 if you want to take an early\npeek at it. It turned into a 50-some patch slog through refactoring\nvarious test-lib.sh code, so I'm seeing about how I submit it.\n\nThe end result is that -V now powered by a proper TAP-parsing tap-tee\nprogram. So you'll have e.g. this working as before:\n    \n    $ ./t9004-example.sh -V\n    ok 1 - decorate\n    # passed all 1 test(s)\n    1..1\n\nBut you can also tweak levels of verbosity to stdout (while retaining\nfull logging) by suppling -V N to get N-level comments, as now indicated\nby N number of prefixed \"#\"'s:\n\n   # -V0 is a special-case to get 0th level logging (couldn't think of a\n   # better name), --verbose-log=0 also works\n   $ ./t9004-example.sh -V0\n   ok 1 - decorate\n   1..1\n\nAt level 3 you start getting the source of passing tests (failing tests\nare level 1, unexpected TODO tests level 2, trace level 4):\n    \n    $ ./t9004-example.sh -VVV \n    ok 1 - decorate\n    ###\n    ###     test-tool example-decorate\n    ###\n    # passed all 1 test(s)\n    1..1\n\nSo obviously the devil's in the details, but it would be useful to know\nwhat you think about the approach before I try to roll this into\nsubmitted patches sooner than later.\n\nI'm mainly interesetd in this for this providing building blocks for\nmore advanced features down the road.\n\nE.g. it would now be pretty easy to do something like run -x behind the\nscenes even if the user doesn't ask for it, and then when a test fail\nannotate the specific line that failed using fuzzy-matching on the -x\noutput.\n\nWe could do that now, sort of, but once we've got unambiguous TAP\nstart/end markers that can't be fooled it becomes trivial to write a\nfunction like get_trace_lines_for_nth_test(N-1) to get your own trace\noutput for test N.\n"},{"id":"419706","messageId":"nycvar.QRO.7.76.6.2103191508280.57@tvgsbejvaqbjf.bet","threadId":"44349","inReplyTo":"20210309160219.13779-8-avarab@gmail.com","subject":"Re: [RFC/PATCH 7/7] test-lib: generate JUnit output via TAP","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2021-03-19T14:14:53Z","receivedAt":"2021-03-19T14:15:38Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Ævar,\n\nOn Tue, 9 Mar 2021, Ævar Arnfjörð Bjarmason wrote:\n\n> Rewrite the home-brew JUnit output generation added in\n> 22231908151 (tests: optionally write results as JUnit-style .xml,\n> 2019-01-29) and other subsequent commits to be generated via the TAP\n> output.\n>\n> This is now possible that the TAP output emitted with --tee is\n> guaranteed to be valid TAP, see the preceding commit.\n>\n> The JUnit output is not bug-for-bug compatible with the previous\n> output, but it looks better to me.\n>\n> This also requires installing TAP::Formatter::JUnit[1], perhaps that's\n> not palatable to the users of the --write-junit-xml option.\n\nIndeed. I am trying to keep the dependencies required for the Windows jobs\nof our CI/PR builds to a minimum.\n\nNote, also, that the JUnit output was mostly relevant for when we used\nAzure Pipelines: it has a specific UI to study test results, figure out\nflaky tests, performance, etc.\n\nNow that we use GitHub Actions, we do not have such a nice test aggregator\nanymore, but we might get one again in the future, who knows.\n\n> In any case, it'll be easy to whip up our own TAP emitter with a\n> TAP::Parser and TAP::Formatter, both of whom come with perl itself,\n> which we already rely on for tests.\n>\n> It should also be significantly faster on Windows,\n\nI really hate to have to harp on this when talking to you, but... well,\nhow can I say it? Perl is _slooooooooooooow_ on Windows.\n\nLike, _really_ slow. Ridiculously slow.\n\nI know, you recently got riled up when Jeff suggested that the Perl hook\ncalling FSMonior might be slow, but the truth is: it is super slow. It's\nnot even funny how slow it is. And it fills me with no joy having to\nrepeat it every time the question comes up whether running any part of Git\nthat is written in Perl might affect performance on Windows. I really\ndislike having to be that messenger.\n\nI doubt that you will ever be able to replace my (admittedly slightly\nhacky) C helper with anything written in Perl that does even come close to\nbeing faster.\n\nIn other words, I fear that your work here might not have the outcome you\nhoped for.\n\nCiao,\nJohannes\n\n> as we can e.g. write all the *.out files, and only do that conversion at\n> the end for all files in a batch, as opposed to the current\n> implementation of shelling out to test-tool in a loop for each test.\n>\n> 1. https://metacpan.org/pod/distribution/TAP-Formatter-JUnit/script/tap2junit\n>\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  Makefile                   |   1 -\n>  t/helper/test-date.c       |  12 ----\n>  t/helper/test-path-utils.c |  21 -------\n>  t/helper/test-tool.c       |   1 -\n>  t/helper/test-tool.h       |   1 -\n>  t/helper/test-xml-encode.c |  80 ------------------------\n>  t/test-lib.sh              | 123 ++++---------------------------------\n>  7 files changed, 12 insertions(+), 227 deletions(-)\n>  delete mode 100644 t/helper/test-xml-encode.c\n>\n> diff --git a/Makefile b/Makefile\n> index d26b9d62ee9..fa7c52f7a42 100644\n> --- a/Makefile\n> +++ b/Makefile\n> @@ -748,7 +748,6 @@ TEST_BUILTINS_OBJS += test-urlmatch-normalization.o\n>  TEST_BUILTINS_OBJS += test-wildmatch.o\n>  TEST_BUILTINS_OBJS += test-windows-named-pipe.o\n>  TEST_BUILTINS_OBJS += test-write-cache.o\n> -TEST_BUILTINS_OBJS += test-xml-encode.o\n>\n>  # Do not add more tests here unless they have extra dependencies. Add\n>  # them in TEST_BUILTINS_OBJS above.\n> diff --git a/t/helper/test-date.c b/t/helper/test-date.c\n> index 099eff4f0fc..ebf133943b3 100644\n> --- a/t/helper/test-date.c\n> +++ b/t/helper/test-date.c\n> @@ -8,7 +8,6 @@ static const char *usage_msg = \"\\n\"\n>  \"  test-tool date parse [date]...\\n\"\n>  \"  test-tool date approxidate [date]...\\n\"\n>  \"  test-tool date timestamp [date]...\\n\"\n> -\"  test-tool date getnanos [start-nanos]\\n\"\n>  \"  test-tool date is64bit\\n\"\n>  \"  test-tool date time_t-is64bit\\n\";\n>\n> @@ -92,15 +91,6 @@ static void parse_approx_timestamp(const char **argv)\n>  \t}\n>  }\n>\n> -static void getnanos(const char **argv)\n> -{\n> -\tdouble seconds = getnanotime() / 1.0e9;\n> -\n> -\tif (*argv)\n> -\t\tseconds -= strtod(*argv, NULL);\n> -\tprintf(\"%lf\\n\", seconds);\n> -}\n> -\n>  int cmd__date(int argc, const char **argv)\n>  {\n>  \tconst char *x;\n> @@ -120,8 +110,6 @@ int cmd__date(int argc, const char **argv)\n>  \t\tparse_approxidate(argv+1);\n>  \telse if (!strcmp(*argv, \"timestamp\"))\n>  \t\tparse_approx_timestamp(argv+1);\n> -\telse if (!strcmp(*argv, \"getnanos\"))\n> -\t\tgetnanos(argv+1);\n>  \telse if (!strcmp(*argv, \"is64bit\"))\n>  \t\treturn sizeof(timestamp_t) == 8 ? 0 : 1;\n>  \telse if (!strcmp(*argv, \"time_t-is64bit\"))\n> diff --git a/t/helper/test-path-utils.c b/t/helper/test-path-utils.c\n> index 313a153209c..090596ac492 100644\n> --- a/t/helper/test-path-utils.c\n> +++ b/t/helper/test-path-utils.c\n> @@ -407,27 +407,6 @@ int cmd__path_utils(int argc, const char **argv)\n>  \t\treturn !!res;\n>  \t}\n>\n> -\tif (argc == 4 && !strcmp(argv[1], \"skip-n-bytes\")) {\n> -\t\tint fd = open(argv[2], O_RDONLY), offset = atoi(argv[3]);\n> -\t\tchar buffer[65536];\n> -\n> -\t\tif (fd < 0)\n> -\t\t\tdie_errno(\"could not open '%s'\", argv[2]);\n> -\t\tif (lseek(fd, offset, SEEK_SET) < 0)\n> -\t\t\tdie_errno(\"could not skip %d bytes\", offset);\n> -\t\tfor (;;) {\n> -\t\t\tssize_t count = read(fd, buffer, sizeof(buffer));\n> -\t\t\tif (count < 0)\n> -\t\t\t\tdie_errno(\"could not read '%s'\", argv[2]);\n> -\t\t\tif (!count)\n> -\t\t\t\tbreak;\n> -\t\t\tif (write(1, buffer, count) < 0)\n> -\t\t\t\tdie_errno(\"could not write to stdout\");\n> -\t\t}\n> -\t\tclose(fd);\n> -\t\treturn 0;\n> -\t}\n> -\n>  \tif (argc > 5 && !strcmp(argv[1], \"slice-tests\")) {\n>  \t\tint res = 0;\n>  \t\tlong offset, stride, i;\n> diff --git a/t/helper/test-tool.c b/t/helper/test-tool.c\n> index 1876bad8f42..3fa02011b61 100644\n> --- a/t/helper/test-tool.c\n> +++ b/t/helper/test-tool.c\n> @@ -73,7 +73,6 @@ static struct test_cmd cmds[] = {\n>  \t{ \"tee\", cmd__tee },\n>  \t{ \"trace2\", cmd__trace2 },\n>  \t{ \"urlmatch-normalization\", cmd__urlmatch_normalization },\n> -\t{ \"xml-encode\", cmd__xml_encode },\n>  \t{ \"wildmatch\", cmd__wildmatch },\n>  #ifdef GIT_WINDOWS_NATIVE\n>  \t{ \"windows-named-pipe\", cmd__windows_named_pipe },\n> diff --git a/t/helper/test-tool.h b/t/helper/test-tool.h\n> index 9b3c1f75267..5028730807e 100644\n> --- a/t/helper/test-tool.h\n> +++ b/t/helper/test-tool.h\n> @@ -63,7 +63,6 @@ int cmd__subprocess(int argc, const char **argv);\n>  int cmd__tee(int argc, const char **argv);\n>  int cmd__trace2(int argc, const char **argv);\n>  int cmd__urlmatch_normalization(int argc, const char **argv);\n> -int cmd__xml_encode(int argc, const char **argv);\n>  int cmd__wildmatch(int argc, const char **argv);\n>  #ifdef GIT_WINDOWS_NATIVE\n>  int cmd__windows_named_pipe(int argc, const char **argv);\n> diff --git a/t/helper/test-xml-encode.c b/t/helper/test-xml-encode.c\n> deleted file mode 100644\n> index a648bbd961c..00000000000\n> --- a/t/helper/test-xml-encode.c\n> +++ /dev/null\n> @@ -1,80 +0,0 @@\n> -#include \"test-tool.h\"\n> -\n> -static const char *utf8_replace_character = \"&#xfffd;\";\n> -\n> -/*\n> - * Encodes (possibly incorrect) UTF-8 on <stdin> to <stdout>, to be embedded\n> - * in an XML file.\n> - */\n> -int cmd__xml_encode(int argc, const char **argv)\n> -{\n> -\tunsigned char buf[1024], tmp[4], *tmp2 = NULL;\n> -\tssize_t cur = 0, len = 1, remaining = 0;\n> -\tunsigned char ch;\n> -\n> -\tfor (;;) {\n> -\t\tif (++cur == len) {\n> -\t\t\tlen = xread(0, buf, sizeof(buf));\n> -\t\t\tif (!len)\n> -\t\t\t\treturn 0;\n> -\t\t\tif (len < 0)\n> -\t\t\t\tdie_errno(\"Could not read <stdin>\");\n> -\t\t\tcur = 0;\n> -\t\t}\n> -\t\tch = buf[cur];\n> -\n> -\t\tif (tmp2) {\n> -\t\t\tif ((ch & 0xc0) != 0x80) {\n> -\t\t\t\tfputs(utf8_replace_character, stdout);\n> -\t\t\t\ttmp2 = NULL;\n> -\t\t\t\tcur--;\n> -\t\t\t\tcontinue;\n> -\t\t\t}\n> -\t\t\t*tmp2 = ch;\n> -\t\t\ttmp2++;\n> -\t\t\tif (--remaining == 0) {\n> -\t\t\t\tfwrite(tmp, tmp2 - tmp, 1, stdout);\n> -\t\t\t\ttmp2 = NULL;\n> -\t\t\t}\n> -\t\t\tcontinue;\n> -\t\t}\n> -\n> -\t\tif (!(ch & 0x80)) {\n> -\t\t\t/* 0xxxxxxx */\n> -\t\t\tif (ch == '&')\n> -\t\t\t\tfputs(\"&amp;\", stdout);\n> -\t\t\telse if (ch == '\\'')\n> -\t\t\t\tfputs(\"&apos;\", stdout);\n> -\t\t\telse if (ch == '\"')\n> -\t\t\t\tfputs(\"&quot;\", stdout);\n> -\t\t\telse if (ch == '<')\n> -\t\t\t\tfputs(\"&lt;\", stdout);\n> -\t\t\telse if (ch == '>')\n> -\t\t\t\tfputs(\"&gt;\", stdout);\n> -\t\t\telse if (ch >= 0x20)\n> -\t\t\t\tfputc(ch, stdout);\n> -\t\t\telse if (ch == 0x09 || ch == 0x0a || ch == 0x0d)\n> -\t\t\t\tfprintf(stdout, \"&#x%02x;\", ch);\n> -\t\t\telse\n> -\t\t\t\tfputs(utf8_replace_character, stdout);\n> -\t\t} else if ((ch & 0xe0) == 0xc0) {\n> -\t\t\t/* 110XXXXx 10xxxxxx */\n> -\t\t\ttmp[0] = ch;\n> -\t\t\tremaining = 1;\n> -\t\t\ttmp2 = tmp + 1;\n> -\t\t} else if ((ch & 0xf0) == 0xe0) {\n> -\t\t\t/* 1110XXXX 10Xxxxxx 10xxxxxx */\n> -\t\t\ttmp[0] = ch;\n> -\t\t\tremaining = 2;\n> -\t\t\ttmp2 = tmp + 1;\n> -\t\t} else if ((ch & 0xf8) == 0xf0) {\n> -\t\t\t/* 11110XXX 10XXxxxx 10xxxxxx 10xxxxxx */\n> -\t\t\ttmp[0] = ch;\n> -\t\t\tremaining = 3;\n> -\t\t\ttmp2 = tmp + 1;\n> -\t\t} else\n> -\t\t\tfputs(utf8_replace_character, stdout);\n> -\t}\n> -\n> -\treturn 0;\n> -}\n> diff --git a/t/test-lib.sh b/t/test-lib.sh\n> index 0070d05234b..4dc41eeccc2 100644\n> --- a/t/test-lib.sh\n> +++ b/t/test-lib.sh\n> @@ -371,6 +371,17 @@ then\n>  \t\t--escape-stdout ${HARNESS_ACTIVE+--escape-file} \\\n>  \t\t\"$GIT_TEST_TEE_OUTPUT_FILE\"\n>  \ttest \"$(cat \"$TEST_RESULTS_BASE.exit\")\" = 0\n> +\tif test -n \"$write_junit_xml\"\n> +\tthen\n> +\t\tjunit_xml_dir=\"$TEST_OUTPUT_DIRECTORY/out\"\n> +\t\tmkdir -p \"$junit_xml_dir\"\n> +\t\tjunit_xml_base=${0##*/}\n> +\t\tjunit_xml_path=\"$junit_xml_dir/TEST-${junit_xml_base%.sh}.xml\"\n> +\t\tjunit_attrs=\"timestamp=\\\"$(TZ=UTC date +%Y-%m-%dT%H:%M:%S)\\\"\"\n> +\t\ttap2junit --name=\"$TEST_NAME\" - \\\n> +\t\t\t<\"$GIT_TEST_TEE_OUTPUT_FILE\" \\\n> +\t\t\t>\"$junit_xml_path\"\n> +\tfi\n>  \texit\n>  elif test -n \"$verbose\" -a -n \"$HARNESS_ACTIVE\"\n>  then\n> @@ -588,7 +599,6 @@ export TERM\n>\n>  error () {\n>  \tsay_color error \"error: $*\"\n> -\tfinalize_junit_xml\n>  \tGIT_EXIT_OK=t\n>  \texit 1\n>  }\n> @@ -672,56 +682,24 @@ trap '{ code=$?; set +x; } 2>/dev/null; exit $code' INT TERM HUP\n>  # the test_expect_* functions instead.\n>\n>  test_ok_ () {\n> -\tif test -n \"$write_junit_xml\"\n> -\tthen\n> -\t\twrite_junit_xml_testcase \"$*\"\n> -\tfi\n>  \ttest_success=$(($test_success + 1))\n>  \tsay_color_tap \"${verbose:+pass}\" \"ok $test_count - $@\"\n>  }\n>\n>  test_failure_ () {\n> -\tif test -n \"$write_junit_xml\"\n> -\tthen\n> -\t\tjunit_insert=\"<failure message=\\\"not ok $test_count -\"\n> -\t\tjunit_insert=\"$junit_insert $(xml_attr_encode \"$1\")\\\">\"\n> -\t\tjunit_insert=\"$junit_insert $(xml_attr_encode \\\n> -\t\t\t\"$(if test -n \"$GIT_TEST_TEE_OUTPUT_FILE\"\n> -\t\t\t   then\n> -\t\t\t\ttest-tool path-utils skip-n-bytes \\\n> -\t\t\t\t\t\"$GIT_TEST_TEE_OUTPUT_FILE\" $GIT_TEST_TEE_OFFSET\n> -\t\t\t   else\n> -\t\t\t\tprintf '%s\\n' \"$@\" | sed 1d\n> -\t\t\t   fi)\")\"\n> -\t\tjunit_insert=\"$junit_insert</failure>\"\n> -\t\tif test -n \"$GIT_TEST_TEE_OUTPUT_FILE\"\n> -\t\tthen\n> -\t\t\tjunit_insert=\"$junit_insert<system-err>$(xml_attr_encode \\\n> -\t\t\t\t\"$(cat \"$GIT_TEST_TEE_OUTPUT_FILE\")\")</system-err>\"\n> -\t\tfi\n> -\t\twrite_junit_xml_testcase \"$1\" \"      $junit_insert\"\n> -\tfi\n>  \ttest_failure=$(($test_failure + 1))\n>  \tsay_color_tap error \"not ok $test_count - $1\"\n>  \tshift\n>  \tprintf '%s\\n' \"$*\" | sed -e 's/^/#\t/'\n> -\ttest \"$immediate\" = \"\" || { finalize_junit_xml; GIT_EXIT_OK=t; exit 1; }\n> +\ttest \"$immediate\" = \"\" || { GIT_EXIT_OK=t; exit 1; }\n>  }\n>\n>  test_known_broken_ok_ () {\n> -\tif test -n \"$write_junit_xml\"\n> -\tthen\n> -\t\twrite_junit_xml_testcase \"$* (breakage fixed)\"\n> -\tfi\n>  \ttest_fixed=$(($test_fixed+1))\n>  \tsay_color error \"ok $test_count - $@ # TODO known breakage vanished\"\n>  }\n>\n>  test_known_broken_failure_ () {\n> -\tif test -n \"$write_junit_xml\"\n> -\tthen\n> -\t\twrite_junit_xml_testcase \"$* (known breakage)\"\n> -\tfi\n>  \ttest_broken=$(($test_broken+1))\n>  \tsay_color_tap warn \"not ok $test_count - $@ # TODO known breakage\"\n>  }\n> @@ -983,10 +961,6 @@ test_start_ () {\n>  \ttest_count=$(($test_count+1))\n>  \tmaybe_setup_verbose\n>  \tmaybe_setup_valgrind\n> -\tif test -n \"$write_junit_xml\"\n> -\tthen\n> -\t\tjunit_start=$(test-tool date getnanos)\n> -\tfi\n>  }\n>\n>  test_finish_ () {\n> @@ -1029,13 +1003,6 @@ test_skip () {\n>\n>  \tcase \"$to_skip\" in\n>  \tt)\n> -\t\tif test -n \"$write_junit_xml\"\n> -\t\tthen\n> -\t\t\tmessage=\"$(xml_attr_encode \"$skipped_reason\")\"\n> -\t\t\twrite_junit_xml_testcase \"$1\" \\\n> -\t\t\t\t\"      <skipped message=\\\"$message\\\" />\"\n> -\t\tfi\n> -\n>  \t\tsay_color_tap skip \"ok $test_count # skip $1 ($skipped_reason)\"\n>  \t\t: true\n>  \t\t;;\n> @@ -1050,53 +1017,6 @@ test_at_end_hook_ () {\n>  \t:\n>  }\n>\n> -write_junit_xml () {\n> -\tcase \"$1\" in\n> -\t--truncate)\n> -\t\t>\"$junit_xml_path\"\n> -\t\tjunit_have_testcase=\n> -\t\tshift\n> -\t\t;;\n> -\tesac\n> -\tprintf '%s\\n' \"$@\" >>\"$junit_xml_path\"\n> -}\n> -\n> -xml_attr_encode () {\n> -\tprintf '%s\\n' \"$@\" | test-tool xml-encode\n> -}\n> -\n> -write_junit_xml_testcase () {\n> -\tjunit_attrs=\"name=\\\"$(xml_attr_encode \"$this_test.$test_count $1\")\\\"\"\n> -\tshift\n> -\tjunit_attrs=\"$junit_attrs classname=\\\"$this_test\\\"\"\n> -\tjunit_attrs=\"$junit_attrs time=\\\"$(test-tool \\\n> -\t\tdate getnanos $junit_start)\\\"\"\n> -\twrite_junit_xml \"$(printf '%s\\n' \\\n> -\t\t\"    <testcase $junit_attrs>\" \"$@\" \"    </testcase>\")\"\n> -\tjunit_have_testcase=t\n> -}\n> -\n> -finalize_junit_xml () {\n> -\tif test -n \"$write_junit_xml\" && test -n \"$junit_xml_path\"\n> -\tthen\n> -\t\ttest -n \"$junit_have_testcase\" || {\n> -\t\t\tjunit_start=$(test-tool date getnanos)\n> -\t\t\twrite_junit_xml_testcase \"all tests skipped\"\n> -\t\t}\n> -\n> -\t\t# adjust the overall time\n> -\t\tjunit_time=$(test-tool date getnanos $junit_suite_start)\n> -\t\tsed -e \"s/\\(<testsuite.*\\) time=\\\"[^\\\"]*\\\"/\\1/\" \\\n> -\t\t\t-e \"s/<testsuite [^>]*/& time=\\\"$junit_time\\\"/\" \\\n> -\t\t\t-e '/^ *<\\/testsuite/d' \\\n> -\t\t\t<\"$junit_xml_path\" >\"$junit_xml_path.new\"\n> -\t\tmv \"$junit_xml_path.new\" \"$junit_xml_path\"\n> -\n> -\t\twrite_junit_xml \"  </testsuite>\" \"</testsuites>\"\n> -\t\twrite_junit_xml=\n> -\tfi\n> -}\n> -\n>  test_atexit_cleanup=:\n>  test_atexit_handler () {\n>  \t# In a succeeding test script 'test_atexit_handler' is invoked\n> @@ -1119,8 +1039,6 @@ test_done () {\n>  \t# removed, so the commands can access pidfiles and socket files.\n>  \ttest_atexit_handler\n>\n> -\tfinalize_junit_xml\n> -\n>  \tif test -z \"$HARNESS_ACTIVE\"\n>  \tthen\n>  \t\tmkdir -p \"$TEST_RESULTS_DIR\"\n> @@ -1364,23 +1282,6 @@ then\n>  \ttest_done\n>  fi\n>\n> -if test -n \"$write_junit_xml\"\n> -then\n> -\tjunit_xml_dir=\"$TEST_OUTPUT_DIRECTORY/out\"\n> -\tmkdir -p \"$junit_xml_dir\"\n> -\tjunit_xml_base=${0##*/}\n> -\tjunit_xml_path=\"$junit_xml_dir/TEST-${junit_xml_base%.sh}.xml\"\n> -\tjunit_attrs=\"name=\\\"${junit_xml_base%.sh}\\\"\"\n> -\tjunit_attrs=\"$junit_attrs timestamp=\\\"$(TZ=UTC \\\n> -\t\tdate +%Y-%m-%dT%H:%M:%S)\\\"\"\n> -\twrite_junit_xml --truncate \"<testsuites>\" \"  <testsuite $junit_attrs>\"\n> -\tjunit_suite_start=$(test-tool date getnanos)\n> -\tif test -n \"$GIT_TEST_TEE_OUTPUT_FILE\"\n> -\tthen\n> -\t\tGIT_TEST_TEE_OFFSET=0\n> -\tfi\n> -fi\n> -\n>  # Convenience\n>  # A regexp to match 5, 35 and 40 hexdigits\n>  _x05='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'\n> --\n> 2.31.0.rc1.210.g0f8085a843c\n>\n>\n"},{"id":"419824","messageId":"875z1lz6wl.fsf@evledraar.gmail.com","threadId":"44349","inReplyTo":"nycvar.QRO.7.76.6.2103191508280.57@tvgsbejvaqbjf.bet","subject":"Re: [RFC/PATCH 7/7] test-lib: generate JUnit output via TAP","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-03-21T00:28:58Z","receivedAt":"2021-03-21T00:30:40Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Fri, Mar 19 2021, Johannes Schindelin wrote:\n\n> Hi Ævar,\n>\n> On Tue, 9 Mar 2021, Ævar Arnfjörð Bjarmason wrote:\n>\n>> Rewrite the home-brew JUnit output generation added in\n>> 22231908151 (tests: optionally write results as JUnit-style .xml,\n>> 2019-01-29) and other subsequent commits to be generated via the TAP\n>> output.\n>>\n>> This is now possible that the TAP output emitted with --tee is\n>> guaranteed to be valid TAP, see the preceding commit.\n>>\n>> The JUnit output is not bug-for-bug compatible with the previous\n>> output, but it looks better to me.\n>>\n>> This also requires installing TAP::Formatter::JUnit[1], perhaps that's\n>> not palatable to the users of the --write-junit-xml option.\n>\n> Indeed. I am trying to keep the dependencies required for the Windows jobs\n> of our CI/PR builds to a minimum.\n\nI'm taking this to mean the dependency of the TAP::Formatter::JUnit CPAN\nmodule. Since the tests already depend on Perl, and presumably that Perl\nisn't so broken as to not have the built in TAP modules\n(e.g. TAP::Parser).\n\n> Note, also, that the JUnit output was mostly relevant for when we used\n> Azure Pipelines: it has a specific UI to study test results, figure out\n> flaky tests, performance, etc.\n>\n> Now that we use GitHub Actions, we do not have such a nice test aggregator\n> anymore, but we might get one again in the future, who knows.\n\nSo there's no known current active user of this JUnit output target, or\nat least if such a user exists it's not you anymore?\n\nI'm guessing we'd be unlikely to miss someone targeting JUnit from\ngit.git's tests who's not on this list to chime in. So is it not used\ncurrently & could be removed?\n\n>> In any case, it'll be easy to whip up our own TAP emitter with a\n>> TAP::Parser and TAP::Formatter, both of whom come with perl itself,\n>> which we already rely on for tests.\n>>\n>> It should also be significantly faster on Windows,\n>\n> I really hate to have to harp on this when talking to you, but... well,\n> how can I say it? Perl is _slooooooooooooow_ on Windows.\n>\n> Like, _really_ slow. Ridiculously slow.\n>\n> I know, you recently got riled up when Jeff suggested that the Perl hook\n> calling FSMonior might be slow, but the truth is: it is super slow. It's\n> not even funny how slow it is. And it fills me with no joy having to\n> repeat it every time the question comes up whether running any part of Git\n> that is written in Perl might affect performance on Windows. I really\n> dislike having to be that messenger.\n\nRiled up? No, perplexed: yes. You're referring to\nhttps://lore.kernel.org/git/87h7lgfchm.fsf@evledraar.gmail.com/\n\nSo as an aside about that particular issue / slowness I haven't been\nable to reproduce:\n\nIf it's really a reference to some Windows-specific issue with Perl on\nWindows in particular that would certainly be helpful for the rest of us\ntrying to follow along wondering where these wildly different\nperformance numbers come from.\n\nSo not that we don't want to more to some other fsmonitor implementation\nfor other reasons, but the Perl part of that hook would be rather easy\nto replace with some C JSON encoder or whatever.\n\n> I doubt that you will ever be able to replace my (admittedly slightly\n> hacky) C helper with anything written in Perl that does even come close to\n> being faster.\n>\n> In other words, I fear that your work here might not have the outcome you\n> hoped for.\n\nWe shell out to your C helper at least once for every test we run. By\nconverting the TAP after the full run we're only invoking Perl once\nper-test.\n\nIf that one Perl invocation is still much more expensive than doing that\n~20-50 times per-test we can easily move the loop to the Perl script and\ninvoke Perl once per test suite run. At that point you've got 1 run\nv.s. >20k runs for the current helper being called in a loop by\ntest-lib.sh\n\nBut I don't have access to a Windows test box. So maybe it's slower than\nI'm imagining. How long do the equivalent of these take on Windows (not\nsure if the time built-in is there)?:\n    \n    $ time perl -MTAP::Harness -wE 'say \"hello world\"'\n    hello world\n    \n    real    0m0.018s\n    user    0m0.013s\n    sys     0m0.005s\n    \n    $ time (echo hi | helper/test-tool xml-encode)\n    hi&#x0a;\n    real    0m0.002s\n    user    0m0.001s\n    sys     0m0.002s\n\n\nIn any case, I regret focusing on the TAP::Formatter::JUnit part in this\nRFC. The real meaty part is being able to have stable TAP output, and\nthus not having to add N number of output formats into test-lib.sh\nitself.\n\nA tool that consumes that can then be e.g. Perl's TAP tooling, or just\nsome C program or shellscript that calls the code that the below quoted\ndiff is removing (aside from the test-lib.sh part, which would go away).\n\nBut it would be easier to write such tools using Perl's tooling, as it\nsaves one from writing a parser for TAP.\n\nI'd be surprised if Perl was adding much overhead in that context, and I\nsee that the existing Windows CI jobs are running through \"prove\", so\npresumably even on Windows e.g. this:\n\n    time make T=t000*.sh DEFAULT_TEST_TARGET=prove\n\nIs not much slower than:\n\n    time make T=t000*.sh DEFAULT_TEST_TARGET=test\n\nI think this is the Nth time we've had some variant of this \"Perl in\ngit's tests\" discussion.\n\nI'm all for finding solutions to whatever issues you have, but I find it\nconfusing that in these discussions you seem to conflate and jump\nbetween some or all of (just the ones I'm remembering):\n\n 1. Some imagined future state where there's no Perl whatsoever in the\n    test suite or git.git's build toolchain, which isn't the case now,\n    or anywhere in the forseeable future.\n\n 2. A future state where GFW etc. don't need to ship a Perl to users (I\n    believe this is either close or already there, I haven't kept up..)\n\n 3. The slowness of Perl for: tight loops, a few per test, once per test\n    suite run.\n\n 4. How #3 e.g. in a tight loop compares to calling say sed or awk in a\n    tight shell loop. Is perl uniquely bad on Windows, or is it really a\n    gripe about our use of shellscripting in general?\n\n 6. Not wanting to (understandably) package any more non-core Perl\n    stuff, understandable, but we always have the option of extending\n    perl/FromCPAN/ if we find something truly useful (in this case just\n    having a custom JUnit XML emitter, if it's actually needed by anyone\n    anymore, should be rather easy).\n\nSo e.g. in this case I can guess at some of the gaps and *think* that\nsome code in t/ that uses TAP::Parser (a perl core module) and was\ninvoked either once per test-lib.sh invocation, or better (but perhaps\nnot needed, as it would make it a bit more complex) once per test-suite\nrun should be fine.\n\nThat code could even be implemented as a prove(1) plugin, at which point\nwe'd be invoking Perl exactly as many times as we are now, but I'd\nrather not paint myself into that particular corner without good reason.\n\n\n>> as we can e.g. write all the *.out files, and only do that conversion at\n>> the end for all files in a batch, as opposed to the current\n>> implementation of shelling out to test-tool in a loop for each test.\n>>\n>> 1. https://metacpan.org/pod/distribution/TAP-Formatter-JUnit/script/tap2junit\n>>\n>> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n>> ---\n>>  Makefile                   |   1 -\n>>  t/helper/test-date.c       |  12 ----\n>>  t/helper/test-path-utils.c |  21 -------\n>>  t/helper/test-tool.c       |   1 -\n>>  t/helper/test-tool.h       |   1 -\n>>  t/helper/test-xml-encode.c |  80 ------------------------\n>>  t/test-lib.sh              | 123 ++++---------------------------------\n>>  7 files changed, 12 insertions(+), 227 deletions(-)\n>>  delete mode 100644 t/helper/test-xml-encode.c\n>>\n>> diff --git a/Makefile b/Makefile\n>> index d26b9d62ee9..fa7c52f7a42 100644\n>> --- a/Makefile\n>> +++ b/Makefile\n>> @@ -748,7 +748,6 @@ TEST_BUILTINS_OBJS += test-urlmatch-normalization.o\n>>  TEST_BUILTINS_OBJS += test-wildmatch.o\n>>  TEST_BUILTINS_OBJS += test-windows-named-pipe.o\n>>  TEST_BUILTINS_OBJS += test-write-cache.o\n>> -TEST_BUILTINS_OBJS += test-xml-encode.o\n>>\n>>  # Do not add more tests here unless they have extra dependencies. Add\n>>  # them in TEST_BUILTINS_OBJS above.\n>> diff --git a/t/helper/test-date.c b/t/helper/test-date.c\n>> index 099eff4f0fc..ebf133943b3 100644\n>> --- a/t/helper/test-date.c\n>> +++ b/t/helper/test-date.c\n>> @@ -8,7 +8,6 @@ static const char *usage_msg = \"\\n\"\n>>  \"  test-tool date parse [date]...\\n\"\n>>  \"  test-tool date approxidate [date]...\\n\"\n>>  \"  test-tool date timestamp [date]...\\n\"\n>> -\"  test-tool date getnanos [start-nanos]\\n\"\n>>  \"  test-tool date is64bit\\n\"\n>>  \"  test-tool date time_t-is64bit\\n\";\n>>\n>> @@ -92,15 +91,6 @@ static void parse_approx_timestamp(const char **argv)\n>>  \t}\n>>  }\n>>\n>> -static void getnanos(const char **argv)\n>> -{\n>> -\tdouble seconds = getnanotime() / 1.0e9;\n>> -\n>> -\tif (*argv)\n>> -\t\tseconds -= strtod(*argv, NULL);\n>> -\tprintf(\"%lf\\n\", seconds);\n>> -}\n>> -\n>>  int cmd__date(int argc, const char **argv)\n>>  {\n>>  \tconst char *x;\n>> @@ -120,8 +110,6 @@ int cmd__date(int argc, const char **argv)\n>>  \t\tparse_approxidate(argv+1);\n>>  \telse if (!strcmp(*argv, \"timestamp\"))\n>>  \t\tparse_approx_timestamp(argv+1);\n>> -\telse if (!strcmp(*argv, \"getnanos\"))\n>> -\t\tgetnanos(argv+1);\n>>  \telse if (!strcmp(*argv, \"is64bit\"))\n>>  \t\treturn sizeof(timestamp_t) == 8 ? 0 : 1;\n>>  \telse if (!strcmp(*argv, \"time_t-is64bit\"))\n>> diff --git a/t/helper/test-path-utils.c b/t/helper/test-path-utils.c\n>> index 313a153209c..090596ac492 100644\n>> --- a/t/helper/test-path-utils.c\n>> +++ b/t/helper/test-path-utils.c\n>> @@ -407,27 +407,6 @@ int cmd__path_utils(int argc, const char **argv)\n>>  \t\treturn !!res;\n>>  \t}\n>>\n>> -\tif (argc == 4 && !strcmp(argv[1], \"skip-n-bytes\")) {\n>> -\t\tint fd = open(argv[2], O_RDONLY), offset = atoi(argv[3]);\n>> -\t\tchar buffer[65536];\n>> -\n>> -\t\tif (fd < 0)\n>> -\t\t\tdie_errno(\"could not open '%s'\", argv[2]);\n>> -\t\tif (lseek(fd, offset, SEEK_SET) < 0)\n>> -\t\t\tdie_errno(\"could not skip %d bytes\", offset);\n>> -\t\tfor (;;) {\n>> -\t\t\tssize_t count = read(fd, buffer, sizeof(buffer));\n>> -\t\t\tif (count < 0)\n>> -\t\t\t\tdie_errno(\"could not read '%s'\", argv[2]);\n>> -\t\t\tif (!count)\n>> -\t\t\t\tbreak;\n>> -\t\t\tif (write(1, buffer, count) < 0)\n>> -\t\t\t\tdie_errno(\"could not write to stdout\");\n>> -\t\t}\n>> -\t\tclose(fd);\n>> -\t\treturn 0;\n>> -\t}\n>> -\n>>  \tif (argc > 5 && !strcmp(argv[1], \"slice-tests\")) {\n>>  \t\tint res = 0;\n>>  \t\tlong offset, stride, i;\n>> diff --git a/t/helper/test-tool.c b/t/helper/test-tool.c\n>> index 1876bad8f42..3fa02011b61 100644\n>> --- a/t/helper/test-tool.c\n>> +++ b/t/helper/test-tool.c\n>> @@ -73,7 +73,6 @@ static struct test_cmd cmds[] = {\n>>  \t{ \"tee\", cmd__tee },\n>>  \t{ \"trace2\", cmd__trace2 },\n>>  \t{ \"urlmatch-normalization\", cmd__urlmatch_normalization },\n>> -\t{ \"xml-encode\", cmd__xml_encode },\n>>  \t{ \"wildmatch\", cmd__wildmatch },\n>>  #ifdef GIT_WINDOWS_NATIVE\n>>  \t{ \"windows-named-pipe\", cmd__windows_named_pipe },\n>> diff --git a/t/helper/test-tool.h b/t/helper/test-tool.h\n>> index 9b3c1f75267..5028730807e 100644\n>> --- a/t/helper/test-tool.h\n>> +++ b/t/helper/test-tool.h\n>> @@ -63,7 +63,6 @@ int cmd__subprocess(int argc, const char **argv);\n>>  int cmd__tee(int argc, const char **argv);\n>>  int cmd__trace2(int argc, const char **argv);\n>>  int cmd__urlmatch_normalization(int argc, const char **argv);\n>> -int cmd__xml_encode(int argc, const char **argv);\n>>  int cmd__wildmatch(int argc, const char **argv);\n>>  #ifdef GIT_WINDOWS_NATIVE\n>>  int cmd__windows_named_pipe(int argc, const char **argv);\n>> diff --git a/t/helper/test-xml-encode.c b/t/helper/test-xml-encode.c\n>> deleted file mode 100644\n>> index a648bbd961c..00000000000\n>> --- a/t/helper/test-xml-encode.c\n>> +++ /dev/null\n>> @@ -1,80 +0,0 @@\n>> -#include \"test-tool.h\"\n>> -\n>> -static const char *utf8_replace_character = \"&#xfffd;\";\n>> -\n>> -/*\n>> - * Encodes (possibly incorrect) UTF-8 on <stdin> to <stdout>, to be embedded\n>> - * in an XML file.\n>> - */\n>> -int cmd__xml_encode(int argc, const char **argv)\n>> -{\n>> -\tunsigned char buf[1024], tmp[4], *tmp2 = NULL;\n>> -\tssize_t cur = 0, len = 1, remaining = 0;\n>> -\tunsigned char ch;\n>> -\n>> -\tfor (;;) {\n>> -\t\tif (++cur == len) {\n>> -\t\t\tlen = xread(0, buf, sizeof(buf));\n>> -\t\t\tif (!len)\n>> -\t\t\t\treturn 0;\n>> -\t\t\tif (len < 0)\n>> -\t\t\t\tdie_errno(\"Could not read <stdin>\");\n>> -\t\t\tcur = 0;\n>> -\t\t}\n>> -\t\tch = buf[cur];\n>> -\n>> -\t\tif (tmp2) {\n>> -\t\t\tif ((ch & 0xc0) != 0x80) {\n>> -\t\t\t\tfputs(utf8_replace_character, stdout);\n>> -\t\t\t\ttmp2 = NULL;\n>> -\t\t\t\tcur--;\n>> -\t\t\t\tcontinue;\n>> -\t\t\t}\n>> -\t\t\t*tmp2 = ch;\n>> -\t\t\ttmp2++;\n>> -\t\t\tif (--remaining == 0) {\n>> -\t\t\t\tfwrite(tmp, tmp2 - tmp, 1, stdout);\n>> -\t\t\t\ttmp2 = NULL;\n>> -\t\t\t}\n>> -\t\t\tcontinue;\n>> -\t\t}\n>> -\n>> -\t\tif (!(ch & 0x80)) {\n>> -\t\t\t/* 0xxxxxxx */\n>> -\t\t\tif (ch == '&')\n>> -\t\t\t\tfputs(\"&amp;\", stdout);\n>> -\t\t\telse if (ch == '\\'')\n>> -\t\t\t\tfputs(\"&apos;\", stdout);\n>> -\t\t\telse if (ch == '\"')\n>> -\t\t\t\tfputs(\"&quot;\", stdout);\n>> -\t\t\telse if (ch == '<')\n>> -\t\t\t\tfputs(\"&lt;\", stdout);\n>> -\t\t\telse if (ch == '>')\n>> -\t\t\t\tfputs(\"&gt;\", stdout);\n>> -\t\t\telse if (ch >= 0x20)\n>> -\t\t\t\tfputc(ch, stdout);\n>> -\t\t\telse if (ch == 0x09 || ch == 0x0a || ch == 0x0d)\n>> -\t\t\t\tfprintf(stdout, \"&#x%02x;\", ch);\n>> -\t\t\telse\n>> -\t\t\t\tfputs(utf8_replace_character, stdout);\n>> -\t\t} else if ((ch & 0xe0) == 0xc0) {\n>> -\t\t\t/* 110XXXXx 10xxxxxx */\n>> -\t\t\ttmp[0] = ch;\n>> -\t\t\tremaining = 1;\n>> -\t\t\ttmp2 = tmp + 1;\n>> -\t\t} else if ((ch & 0xf0) == 0xe0) {\n>> -\t\t\t/* 1110XXXX 10Xxxxxx 10xxxxxx */\n>> -\t\t\ttmp[0] = ch;\n>> -\t\t\tremaining = 2;\n>> -\t\t\ttmp2 = tmp + 1;\n>> -\t\t} else if ((ch & 0xf8) == 0xf0) {\n>> -\t\t\t/* 11110XXX 10XXxxxx 10xxxxxx 10xxxxxx */\n>> -\t\t\ttmp[0] = ch;\n>> -\t\t\tremaining = 3;\n>> -\t\t\ttmp2 = tmp + 1;\n>> -\t\t} else\n>> -\t\t\tfputs(utf8_replace_character, stdout);\n>> -\t}\n>> -\n>> -\treturn 0;\n>> -}\n>> diff --git a/t/test-lib.sh b/t/test-lib.sh\n>> index 0070d05234b..4dc41eeccc2 100644\n>> --- a/t/test-lib.sh\n>> +++ b/t/test-lib.sh\n>> @@ -371,6 +371,17 @@ then\n>>  \t\t--escape-stdout ${HARNESS_ACTIVE+--escape-file} \\\n>>  \t\t\"$GIT_TEST_TEE_OUTPUT_FILE\"\n>>  \ttest \"$(cat \"$TEST_RESULTS_BASE.exit\")\" = 0\n>> +\tif test -n \"$write_junit_xml\"\n>> +\tthen\n>> +\t\tjunit_xml_dir=\"$TEST_OUTPUT_DIRECTORY/out\"\n>> +\t\tmkdir -p \"$junit_xml_dir\"\n>> +\t\tjunit_xml_base=${0##*/}\n>> +\t\tjunit_xml_path=\"$junit_xml_dir/TEST-${junit_xml_base%.sh}.xml\"\n>> +\t\tjunit_attrs=\"timestamp=\\\"$(TZ=UTC date +%Y-%m-%dT%H:%M:%S)\\\"\"\n>> +\t\ttap2junit --name=\"$TEST_NAME\" - \\\n>> +\t\t\t<\"$GIT_TEST_TEE_OUTPUT_FILE\" \\\n>> +\t\t\t>\"$junit_xml_path\"\n>> +\tfi\n>>  \texit\n>>  elif test -n \"$verbose\" -a -n \"$HARNESS_ACTIVE\"\n>>  then\n>> @@ -588,7 +599,6 @@ export TERM\n>>\n>>  error () {\n>>  \tsay_color error \"error: $*\"\n>> -\tfinalize_junit_xml\n>>  \tGIT_EXIT_OK=t\n>>  \texit 1\n>>  }\n>> @@ -672,56 +682,24 @@ trap '{ code=$?; set +x; } 2>/dev/null; exit $code' INT TERM HUP\n>>  # the test_expect_* functions instead.\n>>\n>>  test_ok_ () {\n>> -\tif test -n \"$write_junit_xml\"\n>> -\tthen\n>> -\t\twrite_junit_xml_testcase \"$*\"\n>> -\tfi\n>>  \ttest_success=$(($test_success + 1))\n>>  \tsay_color_tap \"${verbose:+pass}\" \"ok $test_count - $@\"\n>>  }\n>>\n>>  test_failure_ () {\n>> -\tif test -n \"$write_junit_xml\"\n>> -\tthen\n>> -\t\tjunit_insert=\"<failure message=\\\"not ok $test_count -\"\n>> -\t\tjunit_insert=\"$junit_insert $(xml_attr_encode \"$1\")\\\">\"\n>> -\t\tjunit_insert=\"$junit_insert $(xml_attr_encode \\\n>> -\t\t\t\"$(if test -n \"$GIT_TEST_TEE_OUTPUT_FILE\"\n>> -\t\t\t   then\n>> -\t\t\t\ttest-tool path-utils skip-n-bytes \\\n>> -\t\t\t\t\t\"$GIT_TEST_TEE_OUTPUT_FILE\" $GIT_TEST_TEE_OFFSET\n>> -\t\t\t   else\n>> -\t\t\t\tprintf '%s\\n' \"$@\" | sed 1d\n>> -\t\t\t   fi)\")\"\n>> -\t\tjunit_insert=\"$junit_insert</failure>\"\n>> -\t\tif test -n \"$GIT_TEST_TEE_OUTPUT_FILE\"\n>> -\t\tthen\n>> -\t\t\tjunit_insert=\"$junit_insert<system-err>$(xml_attr_encode \\\n>> -\t\t\t\t\"$(cat \"$GIT_TEST_TEE_OUTPUT_FILE\")\")</system-err>\"\n>> -\t\tfi\n>> -\t\twrite_junit_xml_testcase \"$1\" \"      $junit_insert\"\n>> -\tfi\n>>  \ttest_failure=$(($test_failure + 1))\n>>  \tsay_color_tap error \"not ok $test_count - $1\"\n>>  \tshift\n>>  \tprintf '%s\\n' \"$*\" | sed -e 's/^/#\t/'\n>> -\ttest \"$immediate\" = \"\" || { finalize_junit_xml; GIT_EXIT_OK=t; exit 1; }\n>> +\ttest \"$immediate\" = \"\" || { GIT_EXIT_OK=t; exit 1; }\n>>  }\n>>\n>>  test_known_broken_ok_ () {\n>> -\tif test -n \"$write_junit_xml\"\n>> -\tthen\n>> -\t\twrite_junit_xml_testcase \"$* (breakage fixed)\"\n>> -\tfi\n>>  \ttest_fixed=$(($test_fixed+1))\n>>  \tsay_color error \"ok $test_count - $@ # TODO known breakage vanished\"\n>>  }\n>>\n>>  test_known_broken_failure_ () {\n>> -\tif test -n \"$write_junit_xml\"\n>> -\tthen\n>> -\t\twrite_junit_xml_testcase \"$* (known breakage)\"\n>> -\tfi\n>>  \ttest_broken=$(($test_broken+1))\n>>  \tsay_color_tap warn \"not ok $test_count - $@ # TODO known breakage\"\n>>  }\n>> @@ -983,10 +961,6 @@ test_start_ () {\n>>  \ttest_count=$(($test_count+1))\n>>  \tmaybe_setup_verbose\n>>  \tmaybe_setup_valgrind\n>> -\tif test -n \"$write_junit_xml\"\n>> -\tthen\n>> -\t\tjunit_start=$(test-tool date getnanos)\n>> -\tfi\n>>  }\n>>\n>>  test_finish_ () {\n>> @@ -1029,13 +1003,6 @@ test_skip () {\n>>\n>>  \tcase \"$to_skip\" in\n>>  \tt)\n>> -\t\tif test -n \"$write_junit_xml\"\n>> -\t\tthen\n>> -\t\t\tmessage=\"$(xml_attr_encode \"$skipped_reason\")\"\n>> -\t\t\twrite_junit_xml_testcase \"$1\" \\\n>> -\t\t\t\t\"      <skipped message=\\\"$message\\\" />\"\n>> -\t\tfi\n>> -\n>>  \t\tsay_color_tap skip \"ok $test_count # skip $1 ($skipped_reason)\"\n>>  \t\t: true\n>>  \t\t;;\n>> @@ -1050,53 +1017,6 @@ test_at_end_hook_ () {\n>>  \t:\n>>  }\n>>\n>> -write_junit_xml () {\n>> -\tcase \"$1\" in\n>> -\t--truncate)\n>> -\t\t>\"$junit_xml_path\"\n>> -\t\tjunit_have_testcase=\n>> -\t\tshift\n>> -\t\t;;\n>> -\tesac\n>> -\tprintf '%s\\n' \"$@\" >>\"$junit_xml_path\"\n>> -}\n>> -\n>> -xml_attr_encode () {\n>> -\tprintf '%s\\n' \"$@\" | test-tool xml-encode\n>> -}\n>> -\n>> -write_junit_xml_testcase () {\n>> -\tjunit_attrs=\"name=\\\"$(xml_attr_encode \"$this_test.$test_count $1\")\\\"\"\n>> -\tshift\n>> -\tjunit_attrs=\"$junit_attrs classname=\\\"$this_test\\\"\"\n>> -\tjunit_attrs=\"$junit_attrs time=\\\"$(test-tool \\\n>> -\t\tdate getnanos $junit_start)\\\"\"\n>> -\twrite_junit_xml \"$(printf '%s\\n' \\\n>> -\t\t\"    <testcase $junit_attrs>\" \"$@\" \"    </testcase>\")\"\n>> -\tjunit_have_testcase=t\n>> -}\n>> -\n>> -finalize_junit_xml () {\n>> -\tif test -n \"$write_junit_xml\" && test -n \"$junit_xml_path\"\n>> -\tthen\n>> -\t\ttest -n \"$junit_have_testcase\" || {\n>> -\t\t\tjunit_start=$(test-tool date getnanos)\n>> -\t\t\twrite_junit_xml_testcase \"all tests skipped\"\n>> -\t\t}\n>> -\n>> -\t\t# adjust the overall time\n>> -\t\tjunit_time=$(test-tool date getnanos $junit_suite_start)\n>> -\t\tsed -e \"s/\\(<testsuite.*\\) time=\\\"[^\\\"]*\\\"/\\1/\" \\\n>> -\t\t\t-e \"s/<testsuite [^>]*/& time=\\\"$junit_time\\\"/\" \\\n>> -\t\t\t-e '/^ *<\\/testsuite/d' \\\n>> -\t\t\t<\"$junit_xml_path\" >\"$junit_xml_path.new\"\n>> -\t\tmv \"$junit_xml_path.new\" \"$junit_xml_path\"\n>> -\n>> -\t\twrite_junit_xml \"  </testsuite>\" \"</testsuites>\"\n>> -\t\twrite_junit_xml=\n>> -\tfi\n>> -}\n>> -\n>>  test_atexit_cleanup=:\n>>  test_atexit_handler () {\n>>  \t# In a succeeding test script 'test_atexit_handler' is invoked\n>> @@ -1119,8 +1039,6 @@ test_done () {\n>>  \t# removed, so the commands can access pidfiles and socket files.\n>>  \ttest_atexit_handler\n>>\n>> -\tfinalize_junit_xml\n>> -\n>>  \tif test -z \"$HARNESS_ACTIVE\"\n>>  \tthen\n>>  \t\tmkdir -p \"$TEST_RESULTS_DIR\"\n>> @@ -1364,23 +1282,6 @@ then\n>>  \ttest_done\n>>  fi\n>>\n>> -if test -n \"$write_junit_xml\"\n>> -then\n>> -\tjunit_xml_dir=\"$TEST_OUTPUT_DIRECTORY/out\"\n>> -\tmkdir -p \"$junit_xml_dir\"\n>> -\tjunit_xml_base=${0##*/}\n>> -\tjunit_xml_path=\"$junit_xml_dir/TEST-${junit_xml_base%.sh}.xml\"\n>> -\tjunit_attrs=\"name=\\\"${junit_xml_base%.sh}\\\"\"\n>> -\tjunit_attrs=\"$junit_attrs timestamp=\\\"$(TZ=UTC \\\n>> -\t\tdate +%Y-%m-%dT%H:%M:%S)\\\"\"\n>> -\twrite_junit_xml --truncate \"<testsuites>\" \"  <testsuite $junit_attrs>\"\n>> -\tjunit_suite_start=$(test-tool date getnanos)\n>> -\tif test -n \"$GIT_TEST_TEE_OUTPUT_FILE\"\n>> -\tthen\n>> -\t\tGIT_TEST_TEE_OFFSET=0\n>> -\tfi\n>> -fi\n>> -\n>>  # Convenience\n>>  # A regexp to match 5, 35 and 40 hexdigits\n>>  _x05='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'\n>> --\n>> 2.31.0.rc1.210.g0f8085a843c\n>>\n>>\n\n"},{"id":"419949","messageId":"nycvar.QRO.7.76.6.2103221429200.50@tvgsbejvaqbjf.bet","threadId":"44349","inReplyTo":"875z1lz6wl.fsf@evledraar.gmail.com","subject":"Re: [RFC/PATCH 7/7] test-lib: generate JUnit output via TAP","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2021-03-22T13:46:35Z","receivedAt":"2021-03-22T13:47:42Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Ævar,\n\nOn Sun, 21 Mar 2021, Ævar Arnfjörð Bjarmason wrote:\n\n> On Fri, Mar 19 2021, Johannes Schindelin wrote:\n>\n> > On Tue, 9 Mar 2021, Ævar Arnfjörð Bjarmason wrote:\n> >\n> >> Rewrite the home-brew JUnit output generation added in\n> >> 22231908151 (tests: optionally write results as JUnit-style .xml,\n> >> 2019-01-29) and other subsequent commits to be generated via the TAP\n> >> output.\n> >>\n> >> This is now possible that the TAP output emitted with --tee is\n> >> guaranteed to be valid TAP, see the preceding commit.\n> >>\n> >> The JUnit output is not bug-for-bug compatible with the previous\n> >> output, but it looks better to me.\n> >>\n> >> This also requires installing TAP::Formatter::JUnit[1], perhaps that's\n> >> not palatable to the users of the --write-junit-xml option.\n> >\n> > Indeed. I am trying to keep the dependencies required for the Windows jobs\n> > of our CI/PR builds to a minimum.\n>\n> I'm taking this to mean the dependency of the TAP::Formatter::JUnit CPAN\n> module. Since the tests already depend on Perl, and presumably that Perl\n> isn't so broken as to not have the built in TAP modules\n> (e.g. TAP::Parser).\n\nI have no idea why you insist on modifying something that works quite\nwell, but yeah, if you want to depend on the implementation detail that we\n_currently_ run even the Visual Studio tests (that do _not_ depend on\n`prove`) in an environment that has that dependency of `prove`, well, I\nguess I'll have to work on maintaining that state of affairs. Doesn't mean\nthat it makes me happy. It really looks to me like a whole lot of work,\nwithout any benefit.\n\n> > Note, also, that the JUnit output was mostly relevant for when we used\n> > Azure Pipelines: it has a specific UI to study test results, figure out\n> > flaky tests, performance, etc.\n> >\n> > Now that we use GitHub Actions, we do not have such a nice test aggregator\n> > anymore, but we might get one again in the future, who knows.\n>\n> So there's no known current active user of this JUnit output target, or\n> at least if such a user exists it's not you anymore?\n>\n> I'm guessing we'd be unlikely to miss someone targeting JUnit from\n> git.git's tests who's not on this list to chime in. So is it not used\n> currently & could be removed?\n\nNo, I want to keep it because I hope that we'll get that lost\nfunctionality back some time soon.\n\nSorry for not spelling that out more clearly.\n\n> >> In any case, it'll be easy to whip up our own TAP emitter with a\n> >> TAP::Parser and TAP::Formatter, both of whom come with perl itself,\n> >> which we already rely on for tests.\n> >>\n> >> It should also be significantly faster on Windows,\n> >\n> > I really hate to have to harp on this when talking to you, but... well,\n> > how can I say it? Perl is _slooooooooooooow_ on Windows.\n> >\n> > Like, _really_ slow. Ridiculously slow.\n> >\n> > I know, you recently got riled up when Jeff suggested that the Perl hook\n> > calling FSMonior might be slow, but the truth is: it is super slow. It's\n> > not even funny how slow it is. And it fills me with no joy having to\n> > repeat it every time the question comes up whether running any part of Git\n> > that is written in Perl might affect performance on Windows. I really\n> > dislike having to be that messenger.\n>\n> Riled up? No, perplexed: yes. You're referring to\n> https://lore.kernel.org/git/87h7lgfchm.fsf@evledraar.gmail.com/\n>\n> So as an aside about that particular issue / slowness I haven't been\n> able to reproduce:\n>\n> If it's really a reference to some Windows-specific issue with Perl on\n> Windows in particular that would certainly be helpful for the rest of us\n> trying to follow along wondering where these wildly different\n> performance numbers come from.\n>\n> So not that we don't want to more to some other fsmonitor implementation\n\nETOOMANYNEGATIONS, can't parse.\n\n> for other reasons, but the Perl part of that hook would be rather easy\n> to replace with some C JSON encoder or whatever.\n\nFor the time being, I'd rather avoid discussing that because we already\nhave an experimental, built-in FSMonitor that I personally use. That\nFSMonitor will nicely side-step all of the Perl slowness.\n\n> > I doubt that you will ever be able to replace my (admittedly slightly\n> > hacky) C helper with anything written in Perl that does even come close to\n> > being faster.\n> >\n> > In other words, I fear that your work here might not have the outcome you\n> > hoped for.\n>\n> We shell out to your C helper at least once for every test we run. By\n> converting the TAP after the full run we're only invoking Perl once\n> per-test.\n>\n> If that one Perl invocation is still much more expensive than doing that\n> ~20-50 times per-test we can easily move the loop to the Perl script and\n> invoke Perl once per test suite run. At that point you've got 1 run\n> v.s. >20k runs for the current helper being called in a loop by\n> test-lib.sh\n>\n> But I don't have access to a Windows test box. So maybe it's slower than\n> I'm imagining. How long do the equivalent of these take on Windows (not\n> sure if the time built-in is there)?:\n>\n>     $ time perl -MTAP::Harness -wE 'say \"hello world\"'\n>     hello world\n>\n>     real    0m0.018s\n>     user    0m0.013s\n>     sys     0m0.005s\n>\n>     $ time (echo hi | helper/test-tool xml-encode)\n>     hi&#x0a;\n>     real    0m0.002s\n>     user    0m0.001s\n>     sys     0m0.002s\n\n$ time perl -MTAP::Harness -wE 'say \"hello world\"'\nhello world\n\nreal    0m0.326s\nuser    0m0.030s\nsys     0m0.124s\n\n$ time perl -MTAP::Harness -wE 'say \"hello world\"'\nhello world\n\nreal    0m0.131s\nuser    0m0.061s\nsys     0m0.046s\n\n$ time perl -MTAP::Harness -wE 'say \"hello world\"'\nhello world\n\nreal    0m0.209s\nuser    0m0.015s\nsys     0m0.156s\n\n$ time (echo hi | t/helper/test-tool xml-encode)\nhi&#x0a;\nreal    0m0.165s\nuser    0m0.015s\nsys     0m0.093s\n\n$ time (echo hi | t/helper/test-tool xml-encode)\nhi&#x0a;\nreal    0m0.092s\nuser    0m0.000s\nsys     0m0.047s\n\n$ time (echo hi | t/helper/test-tool xml-encode)\nhi&#x0a;\nreal    0m0.075s\nuser    0m0.000s\nsys     0m0.046s\n\nCompletely unscientific, but maybe it gives you an idea.\n\n> In any case, I regret focusing on the TAP::Formatter::JUnit part in this\n> RFC. The real meaty part is being able to have stable TAP output, and\n> thus not having to add N number of output formats into test-lib.sh\n> itself.\n>\n> A tool that consumes that can then be e.g. Perl's TAP tooling, or just\n> some C program or shellscript that calls the code that the below quoted\n> diff is removing (aside from the test-lib.sh part, which would go away).\n>\n> But it would be easier to write such tools using Perl's tooling, as it\n> saves one from writing a parser for TAP.\n>\n> I'd be surprised if Perl was adding much overhead in that context, and I\n> see that the existing Windows CI jobs are running through \"prove\", so\n> presumably even on Windows e.g. this:\n>\n>     time make T=t000*.sh DEFAULT_TEST_TARGET=prove\n>\n> Is not much slower than:\n>\n>     time make T=t000*.sh DEFAULT_TEST_TARGET=test\n>\n> I think this is the Nth time we've had some variant of this \"Perl in\n> git's tests\" discussion.\n\nAnd it is still an issue because the Perl we use is running on top of a\nPOSIX emulation layer which makes everything slow.\n\n> I'm all for finding solutions to whatever issues you have, but I find it\n> confusing that in these discussions you seem to conflate and jump\n> between some or all of (just the ones I'm remembering):\n>\n>  1. Some imagined future state where there's no Perl whatsoever in the\n>     test suite or git.git's build toolchain, which isn't the case now,\n>     or anywhere in the forseeable future.\n>\n>  2. A future state where GFW etc. don't need to ship a Perl to users (I\n>     believe this is either close or already there, I haven't kept up..)\n>\n>  3. The slowness of Perl for: tight loops, a few per test, once per test\n>     suite run.\n>\n>  4. How #3 e.g. in a tight loop compares to calling say sed or awk in a\n>     tight shell loop. Is perl uniquely bad on Windows, or is it really a\n>     gripe about our use of shellscripting in general?\n>\n>  6. Not wanting to (understandably) package any more non-core Perl\n>     stuff, understandable, but we always have the option of extending\n>     perl/FromCPAN/ if we find something truly useful (in this case just\n>     having a custom JUnit XML emitter, if it's actually needed by anyone\n>     anymore, should be rather easy).\n>\n> So e.g. in this case I can guess at some of the gaps and *think* that\n> some code in t/ that uses TAP::Parser (a perl core module) and was\n> invoked either once per test-lib.sh invocation, or better (but perhaps\n> not needed, as it would make it a bit more complex) once per test-suite\n> run should be fine.\n>\n> That code could even be implemented as a prove(1) plugin, at which point\n> we'd be invoking Perl exactly as many times as we are now, but I'd\n> rather not paint myself into that particular corner without good reason.\n\nYes, I would be much happier if Perl was not required to run our tests. I\nwould be even happier if we did not run our tests through shell,\nespecially performance tests, because running performance tests through\nshell is like getting the most precise stop watch you can think of and\nthen looking away from the athletes while timing their sprint.\n\nIf you provide patches to bring us closer to that reality, I will try to\nset aside time to help. But this \"let's remove the C helper that serves us\nwell and instead use Perl, just to shuffle things around\" business really\nlooks like a lot of churn to me, and I want to spend my time elsewhere.\n\nCiao,\nDscho\n"}]}