Git development
 help / color / mirror / Atom feed
From: Jeff King <peff@peff.net>
To: git@vger.kernel.org
Subject: [RFC/PATCH 8/8] test-lib: drop the shell JUnit XML reporting code
Date: Tue, 27 Oct 2026 13:42:31 -0400	[thread overview]
Message-ID: <20261027174231.GH136740@coredump.intra.peff.net> (raw)
In-Reply-To: <20261027173205.GA97799@coredump.intra.peff.net>

Our shell tests can produce JUnit XML with --write-junit-xml, but we
don't have any in-tree callers. The Azure Pipelines consumer went away,
with the remaining CI references removed in e39e332e50 (ci: remove stale
code for Azure Pipelines, 2025-01-10).

GitLab CI does still consume JUnit XML, but that's generated by Meson in
meson-logs/testlog.junit.xml, not by us directly.

It's possible that somebody is invoking the tests with --write-junit-xml
themselves, but we have no easy way of knowing. If so then we'd need to
drop/revert this patch. I tend to doubt it somewhat, though. We fixed a
bug earlier in the series that would cause several tests to fail when
run with "set -e", and we enable that feature automatically when the
test suite is run under recent versions of bash. So there's a reasonable
chance anybody using it would have been seeing test failures since
ffe8005b9d (t: detect errors outside of test cases, 2026-04-21).

Getting rid of the option lets us also drop the test-lib-junit.sh file
and the test-tool xml encoder. Note that the start_test_case_output and
finalize_test_output hooks in test-lib.sh aren't used by anybody, but
I've left them for now. In theory they're generally useful, and they
don't cost very much to support. The other hooks are still used for
GitHub workflow annotations.

Signed-off-by: Jeff King <peff@peff.net>
---
 Makefile                   |   1 -
 t/helper/meson.build       |   1 -
 t/helper/test-tool.c       |   1 -
 t/helper/test-tool.h       |   1 -
 t/helper/test-xml-encode.c |  80 ------------------------
 t/test-lib-junit.sh        | 125 -------------------------------------
 t/test-lib.sh              |   5 +-
 7 files changed, 1 insertion(+), 213 deletions(-)
 delete mode 100644 t/helper/test-xml-encode.c
 delete mode 100644 t/test-lib-junit.sh

diff --git a/Makefile b/Makefile
index a96be506b5..20ccdb7fa7 100644
--- a/Makefile
+++ b/Makefile
@@ -879,7 +879,6 @@ TEST_BUILTINS_OBJS += test-userdiff.o
 TEST_BUILTINS_OBJS += test-wildmatch.o
 TEST_BUILTINS_OBJS += test-windows-named-pipe.o
 TEST_BUILTINS_OBJS += test-write-cache.o
-TEST_BUILTINS_OBJS += test-xml-encode.o
 TEST_BUILTINS_OBJS += test-zlib.o
 
 # Do not add more tests here unless they have extra dependencies. Add
diff --git a/t/helper/meson.build b/t/helper/meson.build
index ae513b4cdc..3dcc4d5219 100644
--- a/t/helper/meson.build
+++ b/t/helper/meson.build
@@ -77,7 +77,6 @@ test_tool_sources = [
   'test-wildmatch.c',
   'test-windows-named-pipe.c',
   'test-write-cache.c',
-  'test-xml-encode.c',
   'test-zlib.c',
 ]
 
diff --git a/t/helper/test-tool.c b/t/helper/test-tool.c
index b71a22b43b..b1be64da06 100644
--- a/t/helper/test-tool.c
+++ b/t/helper/test-tool.c
@@ -86,7 +86,6 @@ static struct test_cmd cmds[] = {
 	{ "trace2", cmd__trace2 },
 	{ "truncate", cmd__truncate },
 	{ "userdiff", cmd__userdiff },
-	{ "xml-encode", cmd__xml_encode },
 	{ "wildmatch", cmd__wildmatch },
 #ifdef GIT_WINDOWS_NATIVE
 	{ "windows-named-pipe", cmd__windows_named_pipe },
diff --git a/t/helper/test-tool.h b/t/helper/test-tool.h
index f2885b33d5..17b72276a1 100644
--- a/t/helper/test-tool.h
+++ b/t/helper/test-tool.h
@@ -79,7 +79,6 @@ int cmd__synthesize(int argc, const char **argv);
 int cmd__trace2(int argc, const char **argv);
 int cmd__truncate(int argc, const char **argv);
 int cmd__userdiff(int argc, const char **argv);
-int cmd__xml_encode(int argc, const char **argv);
 int cmd__wildmatch(int argc, const char **argv);
 #ifdef GIT_WINDOWS_NATIVE
 int cmd__windows_named_pipe(int argc, const char **argv);
diff --git a/t/helper/test-xml-encode.c b/t/helper/test-xml-encode.c
deleted file mode 100644
index b2f330d1a4..0000000000
--- a/t/helper/test-xml-encode.c
+++ /dev/null
@@ -1,80 +0,0 @@
-#include "test-tool.h"
-
-static const char *utf8_replace_character = "&#xfffd;";
-
-/*
- * Encodes (possibly incorrect) UTF-8 on <stdin> to <stdout>, to be embedded
- * in an XML file.
- */
-int cmd__xml_encode(int argc UNUSED, const char **argv UNUSED)
-{
-	unsigned char buf[1024], tmp[4], *tmp2 = NULL;
-	ssize_t cur = 0, len = 1, remaining = 0;
-	unsigned char ch;
-
-	for (;;) {
-		if (++cur == len) {
-			len = xread(0, buf, sizeof(buf));
-			if (!len)
-				return 0;
-			if (len < 0)
-				die_errno("Could not read <stdin>");
-			cur = 0;
-		}
-		ch = buf[cur];
-
-		if (tmp2) {
-			if ((ch & 0xc0) != 0x80) {
-				fputs(utf8_replace_character, stdout);
-				tmp2 = NULL;
-				cur--;
-				continue;
-			}
-			*tmp2 = ch;
-			tmp2++;
-			if (--remaining == 0) {
-				fwrite(tmp, tmp2 - tmp, 1, stdout);
-				tmp2 = NULL;
-			}
-			continue;
-		}
-
-		if (!(ch & 0x80)) {
-			/* 0xxxxxxx */
-			if (ch == '&')
-				fputs("&amp;", stdout);
-			else if (ch == '\'')
-				fputs("&apos;", stdout);
-			else if (ch == '"')
-				fputs("&quot;", stdout);
-			else if (ch == '<')
-				fputs("&lt;", stdout);
-			else if (ch == '>')
-				fputs("&gt;", stdout);
-			else if (ch >= 0x20)
-				fputc(ch, stdout);
-			else if (ch == 0x09 || ch == 0x0a || ch == 0x0d)
-				fprintf(stdout, "&#x%02x;", ch);
-			else
-				fputs(utf8_replace_character, stdout);
-		} else if ((ch & 0xe0) == 0xc0) {
-			/* 110XXXXx 10xxxxxx */
-			tmp[0] = ch;
-			remaining = 1;
-			tmp2 = tmp + 1;
-		} else if ((ch & 0xf0) == 0xe0) {
-			/* 1110XXXX 10Xxxxxx 10xxxxxx */
-			tmp[0] = ch;
-			remaining = 2;
-			tmp2 = tmp + 1;
-		} else if ((ch & 0xf8) == 0xf0) {
-			/* 11110XXX 10XXxxxx 10xxxxxx 10xxxxxx */
-			tmp[0] = ch;
-			remaining = 3;
-			tmp2 = tmp + 1;
-		} else
-			fputs(utf8_replace_character, stdout);
-	}
-
-	return 0;
-}
diff --git a/t/test-lib-junit.sh b/t/test-lib-junit.sh
deleted file mode 100644
index 03a7360198..0000000000
--- a/t/test-lib-junit.sh
+++ /dev/null
@@ -1,125 +0,0 @@
-# Library of functions to format test scripts' output in JUnit XML
-# format, to support Git's test suite result to be presented in an
-# easily digestible way on Azure Pipelines.
-#
-# Copyright (c) 2022 Johannes Schindelin
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program.  If not, see https://www.gnu.org/licenses/ .
-#
-# The idea is for `test-lib.sh` to source this file when the user asks
-# for JUnit XML; these functions will then override (empty) functions
-# that are called at the appropriate times during the test runs.
-
-start_test_output () {
-	junit_xml_dir="$TEST_OUTPUT_DIRECTORY/out"
-	mkdir -p "$junit_xml_dir"
-	junit_xml_base=${1##*/}
-	junit_xml_path="$junit_xml_dir/TEST-${junit_xml_base%.sh}.xml"
-	junit_attrs="name=\"${junit_xml_base%.sh}\""
-	junit_attrs="$junit_attrs timestamp=\"$(TZ=UTC \
-		date +%Y-%m-%dT%H:%M:%S)\""
-	write_junit_xml --truncate "<testsuites>" "  <testsuite $junit_attrs>"
-	junit_suite_start=$(test-tool date getnanos)
-	if test -n "$GIT_TEST_TEE_OUTPUT_FILE"
-	then
-		GIT_TEST_TEE_OFFSET=0
-	fi
-}
-
-start_test_case_output () {
-	junit_start=$(test-tool date getnanos)
-}
-
-finalize_test_case_output () {
-	test_case_result=$1
-	shift
-	case "$test_case_result" in
-	ok)
-		set -- "$*"
-		;;
-	failure)
-		junit_insert="<failure message=\"not ok $test_count -"
-		junit_insert="$junit_insert $(xml_attr_encode --no-lf "$1")\">"
-		junit_insert="$junit_insert $(xml_attr_encode \
-			"$(if test -n "$GIT_TEST_TEE_OUTPUT_FILE"
-			   then
-				test-tool path-utils skip-n-bytes \
-					"$GIT_TEST_TEE_OUTPUT_FILE" $GIT_TEST_TEE_OFFSET
-			   else
-				printf '%s\n' "$@" | sed 1d
-			   fi)")"
-		junit_insert="$junit_insert</failure>"
-		if test -n "$GIT_TEST_TEE_OUTPUT_FILE"
-		then
-			junit_insert="$junit_insert<system-err>$(xml_attr_encode \
-				"$(cat "$GIT_TEST_TEE_OUTPUT_FILE")")</system-err>"
-		fi
-		set -- "$1" "      $junit_insert"
-		;;
-	fixed)
-		set -- "$* (breakage fixed)"
-		;;
-	broken)
-		set -- "$* (known breakage)"
-		;;
-	skip)
-		message="$(xml_attr_encode --no-lf "$skipped_reason")"
-		set -- "$1" "      <skipped message=\"$message\" />"
-		;;
-	esac
-
-	junit_attrs="name=\"$(xml_attr_encode --no-lf "$this_test.$test_count $1")\""
-	shift
-	junit_attrs="$junit_attrs classname=\"$this_test\""
-	junit_attrs="$junit_attrs time=\"$(test-tool \
-		date getnanos $junit_start)\""
-	write_junit_xml "$(printf '%s\n' \
-		"    <testcase $junit_attrs>" "$@" "    </testcase>")"
-}
-
-finalize_test_output () {
-	if test -n "$junit_xml_path"
-	then
-		# adjust the overall time
-		junit_time=$(test-tool date getnanos $junit_suite_start)
-		sed -e "s/\(<testsuite.*\) time=\"[^\"]*\"/\1/" \
-			-e "s/<testsuite [^>]*/& time=\"$junit_time\"/" \
-			-e '/^ *<\/testsuite/d' \
-			<"$junit_xml_path" >"$junit_xml_path.new"
-		mv "$junit_xml_path.new" "$junit_xml_path"
-
-		write_junit_xml "  </testsuite>" "</testsuites>"
-		write_junit_xml=
-	fi
-}
-
-write_junit_xml () {
-	case "$1" in
-	--truncate)
-		>"$junit_xml_path"
-		shift
-		;;
-	esac
-	printf '%s\n' "$@" >>"$junit_xml_path"
-}
-
-xml_attr_encode () {
-	if test "x$1" = "x--no-lf"
-	then
-		shift
-		printf '%s' "$*" | test-tool xml-encode
-	else
-		printf '%s\n' "$@" | test-tool xml-encode
-	fi
-}
diff --git a/t/test-lib.sh b/t/test-lib.sh
index 3754db0ea7..c107fd687a 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -195,7 +195,7 @@ mark_option_requires_arg () {
 	store_arg_to=$2
 }
 
-# These functions can be overridden e.g. to output JUnit XML
+# These functions can be overridden e.g. to add CI workflow annotations
 start_test_output () { :; }
 start_test_case_output () { :; }
 finalize_test_case_output () { :; }
@@ -260,9 +260,6 @@ parse_option () {
 		verbose_log=t
 		tee=t
 		;;
-	--write-junit-xml)
-		. "$TEST_DIRECTORY/test-lib-junit.sh"
-		;;
 	--github-workflow-markup)
 		. "$TEST_DIRECTORY/test-lib-github-workflow-markup.sh"
 		;;
-- 
2.56.0.559.g7c1a1d8168

      parent reply	other threads:[~2026-10-27 17:42 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-27 17:32 [PATCH 0/8] running unit tests inside a test-lib script Jeff King
2026-10-27 17:32 ` [PATCH 1/8] unit-tests: report the correct unexpected argument Jeff King
2026-10-27 17:32 ` [PATCH 2/8] test-lib: drop unused test_external_has_tap variable Jeff King
2026-10-27 17:35 ` [PATCH 3/8] test-lib: stop inventing a JUnit testcase for empty suites Jeff King
2026-10-27 17:36 ` [PATCH 4/8] test-lib: allow an external program to supply TAP output Jeff King
2026-10-27 17:38 ` [PATCH 5/8] t: run clar unit tests through test-lib.sh Jeff King
2026-10-27 17:40 ` [PATCH 6/8] t: drop redundant unit-test invocations Jeff King
2026-10-27 17:41 ` [RFC/PATCH 7/8] test-tool: drop the run-command testsuite mode Jeff King
2026-10-27 17:42 ` Jeff King [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261027174231.GH136740@coredump.intra.peff.net \
    --to=peff@peff.net \
    --cc=git@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox