Prepare to run test suite on Azure Pipeline.

* js/vsts-ci: (22 commits)
  test-date: drop unused parameter to getnanos()
  ci: parallelize testing on Windows
  ci: speed up Windows phase
  tests: optionally skip bin-wrappers/
  t0061: workaround issues with --with-dashes and RUNTIME_PREFIX
  tests: add t/helper/ to the PATH with --with-dashes
  mingw: try to work around issues with the test cleanup
  tests: include detailed trace logs with --write-junit-xml upon failure
  tests: avoid calling Perl just to determine file sizes
  README: add a build badge (status of the Azure Pipelines build)
  mingw: be more generous when wrapping up the setitimer() emulation
  ci: use git-sdk-64-minimal build artifact
  ci: add a Windows job to the Azure Pipelines definition
  Add a build definition for Azure DevOps
  ci/lib.sh: add support for Azure Pipelines
  tests: optionally write results as JUnit-style .xml
  test-date: add a subcommand to measure times in shell scripts
  ci: use a junction on Windows instead of a symlink
  ci: inherit --jobs via MAKEFLAGS in run-build-and-tests
  ci/lib.sh: encapsulate Travis-specific things
  ...
This commit is contained in:
Junio C Hamano 2019-02-06 22:05:26 -08:00
Родитель e7b120bfa5 ba285a712d
Коммит 57cbc53d3e
29 изменённых файлов: 862 добавлений и 49 удалений

Просмотреть файл

@ -763,6 +763,7 @@ TEST_BUILTINS_OBJS += test-submodule-config.o
TEST_BUILTINS_OBJS += test-submodule-nested-repo-config.o
TEST_BUILTINS_OBJS += test-subprocess.o
TEST_BUILTINS_OBJS += test-urlmatch-normalization.o
TEST_BUILTINS_OBJS += test-xml-encode.o
TEST_BUILTINS_OBJS += test-wildmatch.o
TEST_BUILTINS_OBJS += test-windows-named-pipe.o
TEST_BUILTINS_OBJS += test-write-cache.o
@ -2948,6 +2949,16 @@ rpm::
@false
.PHONY: rpm
artifacts-tar:: $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS) $(OTHER_PROGRAMS) \
GIT-BUILD-OPTIONS $(TEST_PROGRAMS) $(test_bindir_programs) \
$(NO_INSTALL) $(MOFILES)
$(QUIET_SUBDIR0)templates $(QUIET_SUBDIR1) \
SHELL_PATH='$(SHELL_PATH_SQ)' PERL_PATH='$(PERL_PATH_SQ)'
test -n "$(ARTIFACTS_DIRECTORY)"
mkdir -p "$(ARTIFACTS_DIRECTORY)"
$(TAR) czf "$(ARTIFACTS_DIRECTORY)/artifacts.tar.gz" $^ templates/blt/
.PHONY: artifacts-tar
htmldocs = git-htmldocs-$(GIT_VERSION)
manpages = git-manpages-$(GIT_VERSION)
.PHONY: dist-doc distclean

Просмотреть файл

@ -1,3 +1,5 @@
[![Build Status](https://dev.azure.com/git/git/_apis/build/status/test-git.git)](https://dev.azure.com/git/git/_build/latest?definitionId=2)
Git - fast, scalable, distributed revision control system
=========================================================

387
azure-pipelines.yml Normal file
Просмотреть файл

@ -0,0 +1,387 @@
resources:
- repo: self
fetchDepth: 1
jobs:
- job: windows_build
displayName: Windows Build
condition: succeeded()
pool: Hosted
timeoutInMinutes: 240
steps:
- powershell: |
if ("$GITFILESHAREPWD" -ne "" -and "$GITFILESHAREPWD" -ne "`$`(gitfileshare.pwd)") {
net use s: \\gitfileshare.file.core.windows.net\test-cache "$GITFILESHAREPWD" /user:AZURE\gitfileshare /persistent:no
cmd /c mklink /d "$(Build.SourcesDirectory)\test-cache" S:\
}
displayName: 'Mount test-cache'
env:
GITFILESHAREPWD: $(gitfileshare.pwd)
- powershell: |
$urlbase = "https://dev.azure.com/git-for-windows/git/_apis/build/builds"
$id = ((Invoke-WebRequest -UseBasicParsing "${urlbase}?definitions=22&statusFilter=completed&resultFilter=succeeded&`$top=1").content | ConvertFrom-JSON).value[0].id
$downloadUrl = ((Invoke-WebRequest -UseBasicParsing "${urlbase}/$id/artifacts").content | ConvertFrom-JSON).value[1].resource.downloadUrl
(New-Object Net.WebClient).DownloadFile($downloadUrl,"git-sdk-64-minimal.zip")
Expand-Archive git-sdk-64-minimal.zip -DestinationPath . -Force
Remove-Item git-sdk-64-minimal.zip
# Let Git ignore the SDK and the test-cache
"/git-sdk-64-minimal/`n/test-cache/`n" | Out-File -NoNewLine -Encoding ascii -Append "$(Build.SourcesDirectory)\.git\info\exclude"
displayName: 'Download git-sdk-64-minimal'
- powershell: |
& git-sdk-64-minimal\usr\bin\bash.exe -lc @"
ci/make-test-artifacts.sh artifacts
"@
if (!$?) { exit(1) }
displayName: Build
env:
HOME: $(Build.SourcesDirectory)
MSYSTEM: MINGW64
DEVELOPER: 1
NO_PERL: 1
- task: PublishPipelineArtifact@0
displayName: 'Publish Pipeline Artifact: test artifacts'
inputs:
artifactName: 'windows-artifacts'
targetPath: '$(Build.SourcesDirectory)\artifacts'
- task: PublishPipelineArtifact@0
displayName: 'Publish Pipeline Artifact: git-sdk-64-minimal'
inputs:
artifactName: 'git-sdk-64-minimal'
targetPath: '$(Build.SourcesDirectory)\git-sdk-64-minimal'
- powershell: |
if ("$GITFILESHAREPWD" -ne "" -and "$GITFILESHAREPWD" -ne "`$`(gitfileshare.pwd)") {
cmd /c rmdir "$(Build.SourcesDirectory)\test-cache"
}
displayName: 'Unmount test-cache'
condition: true
env:
GITFILESHAREPWD: $(gitfileshare.pwd)
- job: windows_test
displayName: Windows Test
dependsOn: windows_build
condition: succeeded()
pool: Hosted
timeoutInMinutes: 240
strategy:
parallel: 10
steps:
- powershell: |
if ("$GITFILESHAREPWD" -ne "" -and "$GITFILESHAREPWD" -ne "`$`(gitfileshare.pwd)") {
net use s: \\gitfileshare.file.core.windows.net\test-cache "$GITFILESHAREPWD" /user:AZURE\gitfileshare /persistent:no
cmd /c mklink /d "$(Build.SourcesDirectory)\test-cache" S:\
}
displayName: 'Mount test-cache'
env:
GITFILESHAREPWD: $(gitfileshare.pwd)
- task: DownloadPipelineArtifact@0
displayName: 'Download Pipeline Artifact: test artifacts'
inputs:
artifactName: 'windows-artifacts'
targetPath: '$(Build.SourcesDirectory)'
- task: DownloadPipelineArtifact@0
displayName: 'Download Pipeline Artifact: git-sdk-64-minimal'
inputs:
artifactName: 'git-sdk-64-minimal'
targetPath: '$(Build.SourcesDirectory)\git-sdk-64-minimal'
- powershell: |
& git-sdk-64-minimal\usr\bin\bash.exe -lc @"
test -f artifacts.tar.gz || {
echo No test artifacts found\; skipping >&2
exit 0
}
tar xf artifacts.tar.gz || exit 1
# Let Git ignore the SDK and the test-cache
printf '%s\n' /git-sdk-64-minimal/ /test-cache/ >>.git/info/exclude
ci/run-test-slice.sh `$SYSTEM_JOBPOSITIONINPHASE `$SYSTEM_TOTALJOBSINPHASE || {
ci/print-test-failures.sh
exit 1
}
"@
if (!$?) { exit(1) }
displayName: 'Test (parallel)'
env:
HOME: $(Build.SourcesDirectory)
MSYSTEM: MINGW64
NO_SVN_TESTS: 1
GIT_TEST_SKIP_REBASE_P: 1
- powershell: |
if ("$GITFILESHAREPWD" -ne "" -and "$GITFILESHAREPWD" -ne "`$`(gitfileshare.pwd)") {
cmd /c rmdir "$(Build.SourcesDirectory)\test-cache"
}
displayName: 'Unmount test-cache'
condition: true
env:
GITFILESHAREPWD: $(gitfileshare.pwd)
- task: PublishTestResults@2
displayName: 'Publish Test Results **/TEST-*.xml'
inputs:
mergeTestResults: true
testRunTitle: 'windows'
platform: Windows
publishRunAttachments: false
condition: succeededOrFailed()
- task: PublishBuildArtifacts@1
displayName: 'Publish trash directories of failed tests'
condition: failed()
inputs:
PathtoPublish: t/failed-test-artifacts
ArtifactName: failed-test-artifacts
- job: linux_clang
displayName: linux-clang
condition: succeeded()
pool: Hosted Ubuntu 1604
steps:
- bash: |
test "$GITFILESHAREPWD" = '$(gitfileshare.pwd)' || ci/mount-fileshare.sh //gitfileshare.file.core.windows.net/test-cache gitfileshare "$GITFILESHAREPWD" "$HOME/test-cache" || exit 1
sudo apt-get update &&
sudo apt-get -y install git gcc make libssl-dev libcurl4-openssl-dev libexpat-dev tcl tk gettext git-email zlib1g-dev apache2-bin &&
export CC=clang || exit 1
ci/install-dependencies.sh || exit 1
ci/run-build-and-tests.sh || {
ci/print-test-failures.sh
exit 1
}
test "$GITFILESHAREPWD" = '$(gitfileshare.pwd)' || sudo umount "$HOME/test-cache" || exit 1
displayName: 'ci/run-build-and-tests.sh'
env:
GITFILESHAREPWD: $(gitfileshare.pwd)
- task: PublishTestResults@2
displayName: 'Publish Test Results **/TEST-*.xml'
inputs:
mergeTestResults: true
testRunTitle: 'linux-clang'
platform: Linux
publishRunAttachments: false
condition: succeededOrFailed()
- task: PublishBuildArtifacts@1
displayName: 'Publish trash directories of failed tests'
condition: failed()
inputs:
PathtoPublish: t/failed-test-artifacts
ArtifactName: failed-test-artifacts
- job: linux_gcc
displayName: linux-gcc
condition: succeeded()
pool: Hosted Ubuntu 1604
steps:
- bash: |
test "$GITFILESHAREPWD" = '$(gitfileshare.pwd)' || ci/mount-fileshare.sh //gitfileshare.file.core.windows.net/test-cache gitfileshare "$GITFILESHAREPWD" "$HOME/test-cache" || exit 1
sudo add-apt-repository ppa:ubuntu-toolchain-r/test &&
sudo apt-get update &&
sudo apt-get -y install git gcc make libssl-dev libcurl4-openssl-dev libexpat-dev tcl tk gettext git-email zlib1g-dev apache2 language-pack-is git-svn gcc-8 || exit 1
ci/install-dependencies.sh || exit 1
ci/run-build-and-tests.sh || {
ci/print-test-failures.sh
exit 1
}
test "$GITFILESHAREPWD" = '$(gitfileshare.pwd)' || sudo umount "$HOME/test-cache" || exit 1
displayName: 'ci/run-build-and-tests.sh'
env:
GITFILESHAREPWD: $(gitfileshare.pwd)
- task: PublishTestResults@2
displayName: 'Publish Test Results **/TEST-*.xml'
inputs:
mergeTestResults: true
testRunTitle: 'linux-gcc'
platform: Linux
publishRunAttachments: false
condition: succeededOrFailed()
- task: PublishBuildArtifacts@1
displayName: 'Publish trash directories of failed tests'
condition: failed()
inputs:
PathtoPublish: t/failed-test-artifacts
ArtifactName: failed-test-artifacts
- job: osx_clang
displayName: osx-clang
condition: succeeded()
pool: Hosted macOS
steps:
- bash: |
test "$GITFILESHAREPWD" = '$(gitfileshare.pwd)' || ci/mount-fileshare.sh //gitfileshare.file.core.windows.net/test-cache gitfileshare "$GITFILESHAREPWD" "$HOME/test-cache" || exit 1
export CC=clang
ci/install-dependencies.sh || exit 1
ci/run-build-and-tests.sh || {
ci/print-test-failures.sh
exit 1
}
test "$GITFILESHAREPWD" = '$(gitfileshare.pwd)' || umount "$HOME/test-cache" || exit 1
displayName: 'ci/run-build-and-tests.sh'
env:
GITFILESHAREPWD: $(gitfileshare.pwd)
- task: PublishTestResults@2
displayName: 'Publish Test Results **/TEST-*.xml'
inputs:
mergeTestResults: true
testRunTitle: 'osx-clang'
platform: macOS
publishRunAttachments: false
condition: succeededOrFailed()
- task: PublishBuildArtifacts@1
displayName: 'Publish trash directories of failed tests'
condition: failed()
inputs:
PathtoPublish: t/failed-test-artifacts
ArtifactName: failed-test-artifacts
- job: osx_gcc
displayName: osx-gcc
condition: succeeded()
pool: Hosted macOS
steps:
- bash: |
test "$GITFILESHAREPWD" = '$(gitfileshare.pwd)' || ci/mount-fileshare.sh //gitfileshare.file.core.windows.net/test-cache gitfileshare "$GITFILESHAREPWD" "$HOME/test-cache" || exit 1
ci/install-dependencies.sh || exit 1
ci/run-build-and-tests.sh || {
ci/print-test-failures.sh
exit 1
}
test "$GITFILESHAREPWD" = '$(gitfileshare.pwd)' || umount "$HOME/test-cache" || exit 1
displayName: 'ci/run-build-and-tests.sh'
env:
GITFILESHAREPWD: $(gitfileshare.pwd)
- task: PublishTestResults@2
displayName: 'Publish Test Results **/TEST-*.xml'
inputs:
mergeTestResults: true
testRunTitle: 'osx-gcc'
platform: macOS
publishRunAttachments: false
condition: succeededOrFailed()
- task: PublishBuildArtifacts@1
displayName: 'Publish trash directories of failed tests'
condition: failed()
inputs:
PathtoPublish: t/failed-test-artifacts
ArtifactName: failed-test-artifacts
- job: gettext_poison
displayName: GETTEXT_POISON
condition: succeeded()
pool: Hosted Ubuntu 1604
steps:
- bash: |
test "$GITFILESHAREPWD" = '$(gitfileshare.pwd)' || ci/mount-fileshare.sh //gitfileshare.file.core.windows.net/test-cache gitfileshare "$GITFILESHAREPWD" "$HOME/test-cache" || exit 1
sudo apt-get update &&
sudo apt-get -y install git gcc make libssl-dev libcurl4-openssl-dev libexpat-dev tcl tk gettext git-email zlib1g-dev &&
export jobname=GETTEXT_POISON || exit 1
ci/run-build-and-tests.sh || {
ci/print-test-failures.sh
exit 1
}
test "$GITFILESHAREPWD" = '$(gitfileshare.pwd)' || sudo umount "$HOME/test-cache" || exit 1
displayName: 'ci/run-build-and-tests.sh'
env:
GITFILESHAREPWD: $(gitfileshare.pwd)
- task: PublishTestResults@2
displayName: 'Publish Test Results **/TEST-*.xml'
inputs:
mergeTestResults: true
testRunTitle: 'gettext-poison'
platform: Linux
publishRunAttachments: false
condition: succeededOrFailed()
- task: PublishBuildArtifacts@1
displayName: 'Publish trash directories of failed tests'
condition: failed()
inputs:
PathtoPublish: t/failed-test-artifacts
ArtifactName: failed-test-artifacts
- job: linux32
displayName: Linux32
condition: succeeded()
pool: Hosted Ubuntu 1604
steps:
- bash: |
test "$GITFILESHAREPWD" = '$(gitfileshare.pwd)' || ci/mount-fileshare.sh //gitfileshare.file.core.windows.net/test-cache gitfileshare "$GITFILESHAREPWD" "$HOME/test-cache" || exit 1
res=0
sudo AGENT_OS="$AGENT_OS" BUILD_BUILDNUMBER="$BUILD_BUILDNUMBER" BUILD_REPOSITORY_URI="$BUILD_REPOSITORY_URI" BUILD_SOURCEBRANCH="$BUILD_SOURCEBRANCH" BUILD_SOURCEVERSION="$BUILD_SOURCEVERSION" SYSTEM_PHASENAME="$SYSTEM_PHASENAME" SYSTEM_TASKDEFINITIONSURI="$SYSTEM_TASKDEFINITIONSURI" SYSTEM_TEAMPROJECT="$SYSTEM_TEAMPROJECT" CC=$CC MAKEFLAGS="$MAKEFLAGS" bash -lxc ci/run-linux32-docker.sh || res=1
sudo chmod a+r t/out/TEST-*.xml
test ! -d t/failed-test-artifacts || sudo chmod a+r t/failed-test-artifacts
test "$GITFILESHAREPWD" = '$(gitfileshare.pwd)' || sudo umount "$HOME/test-cache" || res=1
exit $res
displayName: 'ci/run-linux32-docker.sh'
env:
GITFILESHAREPWD: $(gitfileshare.pwd)
- task: PublishTestResults@2
displayName: 'Publish Test Results **/TEST-*.xml'
inputs:
mergeTestResults: true
testRunTitle: 'linux32'
platform: Linux
publishRunAttachments: false
condition: succeededOrFailed()
- task: PublishBuildArtifacts@1
displayName: 'Publish trash directories of failed tests'
condition: failed()
inputs:
PathtoPublish: t/failed-test-artifacts
ArtifactName: failed-test-artifacts
- job: static_analysis
displayName: StaticAnalysis
condition: succeeded()
pool: Hosted Ubuntu 1604
steps:
- bash: |
test "$GITFILESHAREPWD" = '$(gitfileshare.pwd)' || ci/mount-fileshare.sh //gitfileshare.file.core.windows.net/test-cache gitfileshare "$GITFILESHAREPWD" "$HOME/test-cache" || exit 1
sudo apt-get update &&
sudo apt-get install -y coccinelle &&
export jobname=StaticAnalysis &&
ci/run-static-analysis.sh || exit 1
test "$GITFILESHAREPWD" = '$(gitfileshare.pwd)' || sudo umount "$HOME/test-cache" || exit 1
displayName: 'ci/run-static-analysis.sh'
env:
GITFILESHAREPWD: $(gitfileshare.pwd)
- job: documentation
displayName: Documentation
condition: succeeded()
pool: Hosted Ubuntu 1604
steps:
- bash: |
test "$GITFILESHAREPWD" = '$(gitfileshare.pwd)' || ci/mount-fileshare.sh //gitfileshare.file.core.windows.net/test-cache gitfileshare "$GITFILESHAREPWD" "$HOME/test-cache" || exit 1
sudo apt-get update &&
sudo apt-get install -y asciidoc xmlto asciidoctor &&
export ALREADY_HAVE_ASCIIDOCTOR=yes. &&
export jobname=Documentation &&
ci/test-documentation.sh || exit 1
test "$GITFILESHAREPWD" = '$(gitfileshare.pwd)' || sudo umount "$HOME/test-cache" || exit 1
displayName: 'ci/test-documentation.sh'
env:
GITFILESHAREPWD: $(gitfileshare.pwd)

Просмотреть файл

@ -3,7 +3,7 @@
# Install dependencies required to build and test Git on Linux and macOS
#
. ${0%/*}/lib-travisci.sh
. ${0%/*}/lib.sh
P4WHENCE=http://filehost.perforce.com/perforce/r$LINUX_P4_VERSION
LFSWHENCE=https://github.com/github/git-lfs/releases/download/v$LINUX_GIT_LFS_VERSION
@ -37,7 +37,8 @@ osx-clang|osx-gcc)
brew update --quiet
# Uncomment this if you want to run perf tests:
# brew install gnu-time
brew install git-lfs gettext
test -z "$BREW_INSTALL_PACKAGES" ||
brew install $BREW_INSTALL_PACKAGES
brew link --force gettext
brew install caskroom/cask/perforce
case "$jobname" in

Просмотреть файл

@ -5,18 +5,17 @@ skip_branch_tip_with_tag () {
# at the same commit as the tip of the branch is pushed, and building
# both at the same time is a waste.
#
# Travis gives a tagname e.g. v2.14.0 in $TRAVIS_BRANCH when
# the build is triggered by a push to a tag. Let's see if
# $TRAVIS_BRANCH is exactly at a tag, and if so, if it is
# different from $TRAVIS_BRANCH. That way, we can tell if
# we are building the tip of a branch that is tagged and
# we can skip the build because we won't be skipping a build
# of a tag.
# When the build is triggered by a push to a tag, $CI_BRANCH will
# have that tagname, e.g. v2.14.0. Let's see if $CI_BRANCH is
# exactly at a tag, and if so, if it is different from $CI_BRANCH.
# That way, we can tell if we are building the tip of a branch that
# is tagged and we can skip the build because we won't be skipping a
# build of a tag.
if TAG=$(git describe --exact-match "$TRAVIS_BRANCH" 2>/dev/null) &&
test "$TAG" != "$TRAVIS_BRANCH"
if TAG=$(git describe --exact-match "$CI_BRANCH" 2>/dev/null) &&
test "$TAG" != "$CI_BRANCH"
then
echo "$(tput setaf 2)Tip of $TRAVIS_BRANCH is exactly at $TAG$(tput sgr0)"
echo "$(tput setaf 2)Tip of $CI_BRANCH is exactly at $TAG$(tput sgr0)"
exit 0
fi
}
@ -25,7 +24,7 @@ skip_branch_tip_with_tag () {
# job if we encounter the same tree again and can provide a useful info
# message.
save_good_tree () {
echo "$(git rev-parse $TRAVIS_COMMIT^{tree}) $TRAVIS_COMMIT $TRAVIS_JOB_NUMBER $TRAVIS_JOB_ID" >>"$good_trees_file"
echo "$(git rev-parse $CI_COMMIT^{tree}) $CI_COMMIT $CI_JOB_NUMBER $CI_JOB_ID" >>"$good_trees_file"
# limit the file size
tail -1000 "$good_trees_file" >"$good_trees_file".tmp
mv "$good_trees_file".tmp "$good_trees_file"
@ -35,7 +34,7 @@ save_good_tree () {
# successfully before (e.g. because the branch got rebased, changing only
# the commit messages).
skip_good_tree () {
if ! good_tree_info="$(grep "^$(git rev-parse $TRAVIS_COMMIT^{tree}) " "$good_trees_file")"
if ! good_tree_info="$(grep "^$(git rev-parse $CI_COMMIT^{tree}) " "$good_trees_file")"
then
# Haven't seen this tree yet, or no cached good trees file yet.
# Continue the build job.
@ -45,18 +44,18 @@ skip_good_tree () {
echo "$good_tree_info" | {
read tree prev_good_commit prev_good_job_number prev_good_job_id
if test "$TRAVIS_JOB_ID" = "$prev_good_job_id"
if test "$CI_JOB_ID" = "$prev_good_job_id"
then
cat <<-EOF
$(tput setaf 2)Skipping build job for commit $TRAVIS_COMMIT.$(tput sgr0)
$(tput setaf 2)Skipping build job for commit $CI_COMMIT.$(tput sgr0)
This commit has already been built and tested successfully by this build job.
To force a re-build delete the branch's cache and then hit 'Restart job'.
EOF
else
cat <<-EOF
$(tput setaf 2)Skipping build job for commit $TRAVIS_COMMIT.$(tput sgr0)
$(tput setaf 2)Skipping build job for commit $CI_COMMIT.$(tput sgr0)
This commit's tree has already been built and tested successfully in build job $prev_good_job_number for commit $prev_good_commit.
The log of that build job is available at https://travis-ci.org/$TRAVIS_REPO_SLUG/jobs/$prev_good_job_id
The log of that build job is available at $(url_for_job_id $prev_good_job_id)
To force a re-build delete the branch's cache and then hit 'Restart job'.
EOF
fi
@ -81,7 +80,60 @@ check_unignored_build_artifacts ()
# and installing dependencies.
set -ex
if test true = "$TRAVIS"
then
CI_TYPE=travis
# When building a PR, TRAVIS_BRANCH refers to the *target* branch. Not
# what we want here. We want the source branch instead.
CI_BRANCH="${TRAVIS_PULL_REQUEST_BRANCH:-$TRAVIS_BRANCH}"
CI_COMMIT="$TRAVIS_COMMIT"
CI_JOB_ID="$TRAVIS_JOB_ID"
CI_JOB_NUMBER="$TRAVIS_JOB_NUMBER"
CI_OS_NAME="$TRAVIS_OS_NAME"
CI_REPO_SLUG="$TRAVIS_REPO_SLUG"
cache_dir="$HOME/travis-cache"
url_for_job_id () {
echo "https://travis-ci.org/$CI_REPO_SLUG/jobs/$1"
}
BREW_INSTALL_PACKAGES="git-lfs gettext"
export GIT_PROVE_OPTS="--timer --jobs 3 --state=failed,slow,save"
export GIT_TEST_OPTS="--verbose-log -x --immediate"
export MAKEFLAGS="--jobs=2"
elif test -n "$SYSTEM_COLLECTIONURI" || test -n "$SYSTEM_TASKDEFINITIONSURI"
then
CI_TYPE=azure-pipelines
# We are running in Azure Pipelines
CI_BRANCH="$BUILD_SOURCEBRANCH"
CI_COMMIT="$BUILD_SOURCEVERSION"
CI_JOB_ID="$BUILD_BUILDID"
CI_JOB_NUMBER="$BUILD_BUILDNUMBER"
CI_OS_NAME="$(echo "$AGENT_OS" | tr A-Z a-z)"
test darwin != "$CI_OS_NAME" || CI_OS_NAME=osx
CI_REPO_SLUG="$(expr "$BUILD_REPOSITORY_URI" : '.*/\([^/]*/[^/]*\)$')"
CC="${CC:-gcc}"
# use a subdirectory of the cache dir (because the file share is shared
# among *all* phases)
cache_dir="$HOME/test-cache/$SYSTEM_PHASENAME"
url_for_job_id () {
echo "$SYSTEM_TASKDEFINITIONSURI$SYSTEM_TEAMPROJECT/_build/results?buildId=$1"
}
BREW_INSTALL_PACKAGES=gcc@8
export GIT_PROVE_OPTS="--timer --jobs 10 --state=failed,slow,save"
export GIT_TEST_OPTS="--verbose-log -x --write-junit-xml"
export MAKEFLAGS="--jobs=10"
test windows_nt != "$CI_OS_NAME" ||
GIT_TEST_OPTS="--no-chain-lint --no-bin-wrappers $GIT_TEST_OPTS"
else
echo "Could not identify CI type" >&2
exit 1
fi
good_trees_file="$cache_dir/good-trees"
mkdir -p "$cache_dir"
@ -91,13 +143,11 @@ skip_good_tree
if test -z "$jobname"
then
jobname="$TRAVIS_OS_NAME-$CC"
jobname="$CI_OS_NAME-$CC"
fi
export DEVELOPER=1
export DEFAULT_TEST_TARGET=prove
export GIT_PROVE_OPTS="--timer --jobs 3 --state=failed,slow,save"
export GIT_TEST_OPTS="--verbose-log -x --immediate"
export GIT_TEST_CLONE_2GB=YesPlease
case "$jobname" in

12
ci/make-test-artifacts.sh Executable file
Просмотреть файл

@ -0,0 +1,12 @@
#!/bin/sh
#
# Build Git and store artifacts for testing
#
mkdir -p "$1" # in case ci/lib.sh decides to quit early
. ${0%/*}/lib.sh
make artifacts-tar ARTIFACTS_DIRECTORY="$1"
check_unignored_build_artifacts

25
ci/mount-fileshare.sh Executable file
Просмотреть файл

@ -0,0 +1,25 @@
#!/bin/sh
die () {
echo "$*" >&2
exit 1
}
test $# = 4 ||
die "Usage: $0 <share> <username> <password> <mountpoint>"
mkdir -p "$4" || die "Could not create $4"
case "$(uname -s)" in
Linux)
sudo mount -t cifs -o vers=3.0,username="$2",password="$3",dir_mode=0777,file_mode=0777,serverino "$1" "$4"
;;
Darwin)
pass="$(echo "$3" | sed -e 's/\//%2F/g' -e 's/+/%2B/g')" &&
mount -t smbfs,soft "smb://$2:$pass@${1#//}" "$4"
;;
*)
die "No support for $(uname -s)"
;;
esac ||
die "Could not mount $4"

Просмотреть файл

@ -3,7 +3,7 @@
# Print output of failing tests
#
. ${0%/*}/lib-travisci.sh
. ${0%/*}/lib.sh
# Tracing executed commands would produce too much noise in the loop below.
set +x
@ -38,6 +38,19 @@ do
test_name="${TEST_EXIT%.exit}"
test_name="${test_name##*/}"
trash_dir="trash directory.$test_name"
case "$CI_TYPE" in
travis)
;;
azure-pipelines)
mkdir -p failed-test-artifacts
mv "$trash_dir" failed-test-artifacts
continue
;;
*)
echo "Unhandled CI type: $CI_TYPE" >&2
exit 1
;;
esac
trash_tgz_b64="trash.$test_name.base64"
if [ -d "$trash_dir" ]
then

Просмотреть файл

@ -3,11 +3,14 @@
# Build and test Git
#
. ${0%/*}/lib-travisci.sh
. ${0%/*}/lib.sh
ln -s "$cache_dir/.prove" t/.prove
case "$CI_OS_NAME" in
windows*) cmd //c mklink //j t\\.prove "$(cygpath -aw "$cache_dir/.prove")";;
*) ln -s "$cache_dir/.prove" t/.prove;;
esac
make --jobs=2
make
make test
if test "$jobname" = "linux-gcc"
then

Просмотреть файл

@ -55,6 +55,6 @@ linux32 --32bit i386 su -m -l $CI_USER -c '
set -ex
cd /usr/src/git
test -n "$cache_dir" && ln -s "$cache_dir/.prove" t/.prove
make --jobs=2
make
make test
'

Просмотреть файл

@ -3,7 +3,7 @@
# Download and run Docker image to build and test 32-bit Git
#
. ${0%/*}/lib-travisci.sh
. ${0%/*}/lib.sh
docker pull daald/ubuntu32:xenial

Просмотреть файл

@ -3,9 +3,9 @@
# Perform various static code analysis checks
#
. ${0%/*}/lib-travisci.sh
. ${0%/*}/lib.sh
make --jobs=2 coccicheck
make coccicheck
set +x

17
ci/run-test-slice.sh Executable file
Просмотреть файл

@ -0,0 +1,17 @@
#!/bin/sh
#
# Test Git in parallel
#
. ${0%/*}/lib.sh
case "$CI_OS_NAME" in
windows*) cmd //c mklink //j t\\.prove "$(cygpath -aw "$cache_dir/.prove")";;
*) ln -s "$cache_dir/.prove" t/.prove;;
esac
make --quiet -C t T="$(cd t &&
./helper/test-tool path-utils slice-tests "$1" "$2" t[0-9]*.sh |
tr '\n' ' ')"
check_unignored_build_artifacts

Просмотреть файл

@ -6,7 +6,7 @@
# supported) and a commit hash.
#
. ${0%/*}/lib-travisci.sh
. ${0%/*}/lib.sh
test $# -ne 2 && echo "Unexpected number of parameters" && exit 1
test -z "$GFW_CI_TOKEN" && echo "GFW_CI_TOKEN not defined" && exit

Просмотреть файл

@ -3,15 +3,16 @@
# Perform sanity checks on documentation and build it.
#
. ${0%/*}/lib-travisci.sh
. ${0%/*}/lib.sh
test -n "$ALREADY_HAVE_ASCIIDOCTOR" ||
gem install asciidoctor
make check-builtins
make check-docs
# Build docs with AsciiDoc
make --jobs=2 doc > >(tee stdout.log) 2> >(tee stderr.log >&2)
make doc > >(tee stdout.log) 2> >(tee stderr.log >&2)
! test -s stderr.log
test -s Documentation/git.html
test -s Documentation/git.xml
@ -23,7 +24,7 @@ check_unignored_build_artifacts
# Build docs with AsciiDoctor
make clean
make --jobs=2 USE_ASCIIDOCTOR=1 doc > >(tee stdout.log) 2> >(tee stderr.log >&2)
make USE_ASCIIDOCTOR=1 doc > >(tee stdout.log) 2> >(tee stderr.log >&2)
sed '/^GIT_VERSION = / d' stderr.log
! test -s stderr.log
test -s Documentation/git.html

Просмотреть файл

@ -2175,7 +2175,7 @@ static void stop_timer_thread(void)
if (timer_event)
SetEvent(timer_event); /* tell thread to terminate */
if (timer_thread) {
int rc = WaitForSingleObject(timer_thread, 1000);
int rc = WaitForSingleObject(timer_thread, 10000);
if (rc == WAIT_TIMEOUT)
error("timer thread did not terminate timely");
else if (rc != WAIT_OBJECT_0)

1
t/.gitignore поставляемый
Просмотреть файл

@ -2,3 +2,4 @@
/test-results
/.prove
/chainlinttmp
/out/

Просмотреть файл

@ -170,6 +170,15 @@ appropriately before running "make".
implied by other options like --valgrind and
GIT_TEST_INSTALLED.
--no-bin-wrappers::
By default, the test suite uses the wrappers in
`../bin-wrappers/` to execute `git` and friends. With this option,
`../git` and friends are run directly. This is not recommended
in general, as the wrappers contain safeguards to ensure that no
files from an installed Git are used, but can speed up test runs
especially on platforms where running shell scripts is expensive
(most notably, Windows).
--root=<directory>::
Create "trash" directories used to store all temporary data during
testing under <directory>, instead of the t/ directory.

Просмотреть файл

@ -8,6 +8,7 @@ static const char *usage_msg = "\n"
" test-tool date parse [date]...\n"
" test-tool date approxidate [date]...\n"
" test-tool date timestamp [date]...\n"
" test-tool date getnanos [start-nanos]\n"
" test-tool date is64bit\n"
" test-tool date time_t-is64bit\n";
@ -91,6 +92,15 @@ static void parse_approx_timestamp(const char **argv, struct timeval *now)
}
}
static void getnanos(const char **argv)
{
double seconds = getnanotime() / 1.0e9;
if (*argv)
seconds -= strtod(*argv, NULL);
printf("%lf\n", seconds);
}
int cmd__date(int argc, const char **argv)
{
struct timeval now;
@ -119,6 +129,8 @@ int cmd__date(int argc, const char **argv)
parse_approxidate(argv+1, &now);
else if (!strcmp(*argv, "timestamp"))
parse_approx_timestamp(argv+1, &now);
else if (!strcmp(*argv, "getnanos"))
getnanos(argv+1);
else if (!strcmp(*argv, "is64bit"))
return sizeof(timestamp_t) == 8 ? 0 : 1;
else if (!strcmp(*argv, "time_t-is64bit"))

Просмотреть файл

@ -177,6 +177,14 @@ static int is_dotgitmodules(const char *path)
return is_hfs_dotgitmodules(path) || is_ntfs_dotgitmodules(path);
}
static int cmp_by_st_size(const void *a, const void *b)
{
intptr_t x = (intptr_t)((struct string_list_item *)a)->util;
intptr_t y = (intptr_t)((struct string_list_item *)b)->util;
return x > y ? -1 : (x < y ? +1 : 0);
}
int cmd__path_utils(int argc, const char **argv)
{
if (argc == 3 && !strcmp(argv[1], "normalize_path_copy")) {
@ -291,6 +299,62 @@ int cmd__path_utils(int argc, const char **argv)
return !!res;
}
if (argc > 2 && !strcmp(argv[1], "file-size")) {
int res = 0, i;
struct stat st;
for (i = 2; i < argc; i++)
if (stat(argv[i], &st))
res = error_errno("Cannot stat '%s'", argv[i]);
else
printf("%"PRIuMAX"\n", (uintmax_t)st.st_size);
return !!res;
}
if (argc == 4 && !strcmp(argv[1], "skip-n-bytes")) {
int fd = open(argv[2], O_RDONLY), offset = atoi(argv[3]);
char buffer[65536];
if (fd < 0)
die_errno("could not open '%s'", argv[2]);
if (lseek(fd, offset, SEEK_SET) < 0)
die_errno("could not skip %d bytes", offset);
for (;;) {
ssize_t count = read(fd, buffer, sizeof(buffer));
if (count < 0)
die_errno("could not read '%s'", argv[2]);
if (!count)
break;
if (write(1, buffer, count) < 0)
die_errno("could not write to stdout");
}
close(fd);
return 0;
}
if (argc > 5 && !strcmp(argv[1], "slice-tests")) {
int res = 0;
long offset, stride, i;
struct string_list list = STRING_LIST_INIT_NODUP;
struct stat st;
offset = strtol(argv[2], NULL, 10);
stride = strtol(argv[3], NULL, 10);
if (stride < 1)
stride = 1;
for (i = 4; i < argc; i++)
if (stat(argv[i], &st))
res = error_errno("Cannot stat '%s'", argv[i]);
else
string_list_append(&list, argv[i])->util =
(void *)(intptr_t)st.st_size;
QSORT(list.items, list.nr, cmp_by_st_size);
for (i = offset; i < list.nr; i+= stride)
printf("%s\n", list.items[i].string);
return !!res;
}
fprintf(stderr, "%s: unknown function name: %s\n", argv[0],
argv[1] ? argv[1] : "(there was none)");
return 1;

Просмотреть файл

@ -51,6 +51,7 @@ static struct test_cmd cmds[] = {
{ "submodule-nested-repo-config", cmd__submodule_nested_repo_config },
{ "subprocess", cmd__subprocess },
{ "urlmatch-normalization", cmd__urlmatch_normalization },
{ "xml-encode", cmd__xml_encode },
{ "wildmatch", cmd__wildmatch },
#ifdef GIT_WINDOWS_NATIVE
{ "windows-named-pipe", cmd__windows_named_pipe },

Просмотреть файл

@ -48,6 +48,7 @@ int cmd__submodule_config(int argc, const char **argv);
int cmd__submodule_nested_repo_config(int argc, const char **argv);
int cmd__subprocess(int argc, const char **argv);
int cmd__urlmatch_normalization(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);

Просмотреть файл

@ -0,0 +1,80 @@
#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, const char **argv)
{
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;
}

Просмотреть файл

@ -24,7 +24,7 @@ generate_random_characters () {
}
file_size () {
perl -e 'print -s $ARGV[0]' "$1"
test-tool path-utils file-size "$1"
}
filter_git () {

Просмотреть файл

@ -166,7 +166,8 @@ test_trace () {
expect="$1"
shift
GIT_TRACE=1 test-tool run-command "$@" run-command true 2>&1 >/dev/null | \
sed -e 's/.* run_command: //' -e '/trace: .*/d' >actual &&
sed -e 's/.* run_command: //' -e '/trace: .*/d' \
-e '/RUNTIME_PREFIX requested/d' >actual &&
echo "$expect true" >expect &&
test_cmp expect actual
}

Просмотреть файл

@ -8,7 +8,7 @@ test_description='adding and checking out large blobs'
# This should be moved to test-lib.sh together with the
# copy in t0021 after both topics have graduated to 'master'.
file_size () {
perl -e 'print -s $ARGV[0]' "$1"
test-tool path-utils file-size "$1"
}
test_expect_success setup '

Просмотреть файл

@ -7,7 +7,7 @@ test_description='pack-object compression configuration'
# This should be moved to test-lib.sh together with the
# copy in t0021 after both topics have graduated to 'master'.
file_size () {
perl -e 'print -s $ARGV[0]' "$1"
test-tool path-utils file-size "$1"
}
test_expect_success setup '

Просмотреть файл

@ -6,7 +6,7 @@ test_description='compression setting of fast-import utility'
# This should be moved to test-lib.sh together with the
# copy in t0021 after both topics have graduated to 'master'.
file_size () {
perl -e 'print -s $ARGV[0]' "$1"
test-tool path-utils file-size "$1"
}
import_large () {

Просмотреть файл

@ -111,6 +111,8 @@ do
test -z "$HARNESS_ACTIVE" && quiet=t ;;
--with-dashes)
with_dashes=t ;;
--no-bin-wrappers)
no_bin_wrappers=t ;;
--no-color)
color= ;;
--va|--val|--valg|--valgr|--valgri|--valgrin|--valgrind)
@ -139,6 +141,9 @@ do
verbose_log=t
tee=t
;;
--write-junit-xml)
write_junit_xml=t
;;
--stress)
stress=t ;;
--stress=*)
@ -622,11 +627,35 @@ trap 'exit $?' INT TERM HUP
# the test_expect_* functions instead.
test_ok_ () {
if test -n "$write_junit_xml"
then
write_junit_xml_testcase "$*"
fi
test_success=$(($test_success + 1))
say_color "" "ok $test_count - $@"
}
test_failure_ () {
if test -n "$write_junit_xml"
then
junit_insert="<failure message=\"not ok $test_count -"
junit_insert="$junit_insert $(xml_attr_encode "$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
write_junit_xml_testcase "$1" " $junit_insert"
fi
test_failure=$(($test_failure + 1))
say_color error "not ok $test_count - $1"
shift
@ -635,11 +664,19 @@ test_failure_ () {
}
test_known_broken_ok_ () {
if test -n "$write_junit_xml"
then
write_junit_xml_testcase "$* (breakage fixed)"
fi
test_fixed=$(($test_fixed+1))
say_color error "ok $test_count - $@ # TODO known breakage vanished"
}
test_known_broken_failure_ () {
if test -n "$write_junit_xml"
then
write_junit_xml_testcase "$* (known breakage)"
fi
test_broken=$(($test_broken+1))
say_color warn "not ok $test_count - $@ # TODO known breakage"
}
@ -897,12 +934,21 @@ test_start_ () {
test_count=$(($test_count+1))
maybe_setup_verbose
maybe_setup_valgrind
if test -n "$write_junit_xml"
then
junit_start=$(test-tool date getnanos)
fi
}
test_finish_ () {
echo >&3 ""
maybe_teardown_valgrind
maybe_teardown_verbose
if test -n "$GIT_TEST_TEE_OFFSET"
then
GIT_TEST_TEE_OFFSET=$(test-tool path-utils file-size \
"$GIT_TEST_TEE_OUTPUT_FILE")
fi
}
test_skip () {
@ -934,6 +980,13 @@ test_skip () {
case "$to_skip" in
t)
if test -n "$write_junit_xml"
then
message="$(xml_attr_encode "$skipped_reason")"
write_junit_xml_testcase "$1" \
" <skipped message=\"$message\" />"
fi
say_color skip >&3 "skipping test: $@"
say_color skip "ok $test_count # skip $1 ($skipped_reason)"
: true
@ -949,9 +1002,51 @@ test_at_end_hook_ () {
:
}
write_junit_xml () {
case "$1" in
--truncate)
>"$junit_xml_path"
junit_have_testcase=
shift
;;
esac
printf '%s\n' "$@" >>"$junit_xml_path"
}
xml_attr_encode () {
printf '%s\n' "$@" | test-tool xml-encode
}
write_junit_xml_testcase () {
junit_attrs="name=\"$(xml_attr_encode "$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>")"
junit_have_testcase=t
}
test_done () {
GIT_EXIT_OK=t
if test -n "$write_junit_xml" && test -n "$junit_xml_path"
then
test -n "$junit_have_testcase" || {
junit_start=$(test-tool date getnanos)
write_junit_xml_testcase "all tests skipped"
}
# adjust the overall time
junit_time=$(test-tool date getnanos $junit_suite_start)
sed "s/<testsuite [^>]*/& time=\"$junit_time\"/" \
<"$junit_xml_path" >"$junit_xml_path.new"
mv "$junit_xml_path.new" "$junit_xml_path"
write_junit_xml " </testsuite>" "</testsuites>"
fi
if test -z "$HARNESS_ACTIVE"
then
mkdir -p "$TEST_RESULTS_DIR"
@ -1011,7 +1106,11 @@ test_done () {
error "Tests passed but trash directory already removed before test cleanup; aborting"
cd "$TRASH_DIRECTORY/.." &&
rm -fr "$TRASH_DIRECTORY" ||
rm -fr "$TRASH_DIRECTORY" || {
# try again in a bit
sleep 5;
rm -fr "$TRASH_DIRECTORY"
} ||
error "Tests passed but test cleanup failed; aborting"
fi
test_at_end_hook_
@ -1117,6 +1216,10 @@ then
PATH=$GIT_TEST_INSTALLED:$GIT_BUILD_DIR/t/helper:$PATH
GIT_EXEC_PATH=${GIT_TEST_EXEC_PATH:-$GIT_EXEC_PATH}
else # normal case, use ../bin-wrappers only unless $with_dashes:
if test -n "$no_bin_wrappers"
then
with_dashes=t
else
git_bin_dir="$GIT_BUILD_DIR/bin-wrappers"
if ! test -x "$git_bin_dir/git"
then
@ -1127,10 +1230,11 @@ else # normal case, use ../bin-wrappers only unless $with_dashes:
with_dashes=t
fi
PATH="$git_bin_dir:$PATH"
fi
GIT_EXEC_PATH=$GIT_BUILD_DIR
if test -n "$with_dashes"
then
PATH="$GIT_BUILD_DIR:$PATH"
PATH="$GIT_BUILD_DIR:$GIT_BUILD_DIR/t/helper:$PATH"
fi
fi
GIT_TEMPLATE_DIR="$GIT_BUILD_DIR"/templates/blt
@ -1178,6 +1282,7 @@ then
else
mkdir -p "$TRASH_DIRECTORY"
fi
# Use -P to resolve symlinks in our working directory so that the cwd
# in subprocesses like git equals our $PWD (for pathname comparisons).
cd -P "$TRASH_DIRECTORY" || exit 1
@ -1191,6 +1296,23 @@ then
test_done
fi
if test -n "$write_junit_xml"
then
junit_xml_dir="$TEST_OUTPUT_DIRECTORY/out"
mkdir -p "$junit_xml_dir"
junit_xml_base=${0##*/}
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
fi
# Provide an implementation of the 'yes' utility
yes () {
if test $# = 0