Merge branch 'js/vsts-ci'

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
commit 57cbc53d3e
29 changed files with 862 additions and 49 deletions

View file

@ -763,6 +763,7 @@ TEST_BUILTINS_OBJS += test-submodule-config.o
TEST_BUILTINS_OBJS += test-submodule-nested-repo-config.o TEST_BUILTINS_OBJS += test-submodule-nested-repo-config.o
TEST_BUILTINS_OBJS += test-subprocess.o TEST_BUILTINS_OBJS += test-subprocess.o
TEST_BUILTINS_OBJS += test-urlmatch-normalization.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-wildmatch.o
TEST_BUILTINS_OBJS += test-windows-named-pipe.o TEST_BUILTINS_OBJS += test-windows-named-pipe.o
TEST_BUILTINS_OBJS += test-write-cache.o TEST_BUILTINS_OBJS += test-write-cache.o
@ -2948,6 +2949,16 @@ rpm::
@false @false
.PHONY: rpm .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) htmldocs = git-htmldocs-$(GIT_VERSION)
manpages = git-manpages-$(GIT_VERSION) manpages = git-manpages-$(GIT_VERSION)
.PHONY: dist-doc distclean .PHONY: dist-doc distclean

View file

@ -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 Git - fast, scalable, distributed revision control system
========================================================= =========================================================

387
azure-pipelines.yml Normal file
View 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)

View file

@ -3,7 +3,7 @@
# Install dependencies required to build and test Git on Linux and macOS # 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 P4WHENCE=http://filehost.perforce.com/perforce/r$LINUX_P4_VERSION
LFSWHENCE=https://github.com/github/git-lfs/releases/download/v$LINUX_GIT_LFS_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 brew update --quiet
# Uncomment this if you want to run perf tests: # Uncomment this if you want to run perf tests:
# brew install gnu-time # brew install gnu-time
brew install git-lfs gettext test -z "$BREW_INSTALL_PACKAGES" ||
brew install $BREW_INSTALL_PACKAGES
brew link --force gettext brew link --force gettext
brew install caskroom/cask/perforce brew install caskroom/cask/perforce
case "$jobname" in case "$jobname" in

View file

@ -5,18 +5,17 @@ skip_branch_tip_with_tag () {
# at the same commit as the tip of the branch is pushed, and building # at the same commit as the tip of the branch is pushed, and building
# both at the same time is a waste. # both at the same time is a waste.
# #
# Travis gives a tagname e.g. v2.14.0 in $TRAVIS_BRANCH when # When the build is triggered by a push to a tag, $CI_BRANCH will
# the build is triggered by a push to a tag. Let's see if # have that tagname, e.g. v2.14.0. Let's see if $CI_BRANCH is
# $TRAVIS_BRANCH is exactly at a tag, and if so, if it is # exactly at a tag, and if so, if it is different from $CI_BRANCH.
# different from $TRAVIS_BRANCH. That way, we can tell if # That way, we can tell if we are building the tip of a branch that
# we are building the tip of a branch that is tagged and # is tagged and we can skip the build because we won't be skipping a
# we can skip the build because we won't be skipping a build # build of a tag.
# of a tag.
if TAG=$(git describe --exact-match "$TRAVIS_BRANCH" 2>/dev/null) && if TAG=$(git describe --exact-match "$CI_BRANCH" 2>/dev/null) &&
test "$TAG" != "$TRAVIS_BRANCH" test "$TAG" != "$CI_BRANCH"
then 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 exit 0
fi fi
} }
@ -25,7 +24,7 @@ skip_branch_tip_with_tag () {
# job if we encounter the same tree again and can provide a useful info # job if we encounter the same tree again and can provide a useful info
# message. # message.
save_good_tree () { 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 # limit the file size
tail -1000 "$good_trees_file" >"$good_trees_file".tmp tail -1000 "$good_trees_file" >"$good_trees_file".tmp
mv "$good_trees_file".tmp "$good_trees_file" 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 # successfully before (e.g. because the branch got rebased, changing only
# the commit messages). # the commit messages).
skip_good_tree () { 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 then
# Haven't seen this tree yet, or no cached good trees file yet. # Haven't seen this tree yet, or no cached good trees file yet.
# Continue the build job. # Continue the build job.
@ -45,18 +44,18 @@ skip_good_tree () {
echo "$good_tree_info" | { echo "$good_tree_info" | {
read tree prev_good_commit prev_good_job_number prev_good_job_id 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 then
cat <<-EOF 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. 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'. To force a re-build delete the branch's cache and then hit 'Restart job'.
EOF EOF
else else
cat <<-EOF 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. 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'. To force a re-build delete the branch's cache and then hit 'Restart job'.
EOF EOF
fi fi
@ -81,7 +80,60 @@ check_unignored_build_artifacts ()
# and installing dependencies. # and installing dependencies.
set -ex set -ex
cache_dir="$HOME/travis-cache" 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" good_trees_file="$cache_dir/good-trees"
mkdir -p "$cache_dir" mkdir -p "$cache_dir"
@ -91,13 +143,11 @@ skip_good_tree
if test -z "$jobname" if test -z "$jobname"
then then
jobname="$TRAVIS_OS_NAME-$CC" jobname="$CI_OS_NAME-$CC"
fi fi
export DEVELOPER=1 export DEVELOPER=1
export DEFAULT_TEST_TARGET=prove 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 export GIT_TEST_CLONE_2GB=YesPlease
case "$jobname" in case "$jobname" in

12
ci/make-test-artifacts.sh Executable file
View 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
View 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"

View file

@ -3,7 +3,7 @@
# Print output of failing tests # Print output of failing tests
# #
. ${0%/*}/lib-travisci.sh . ${0%/*}/lib.sh
# Tracing executed commands would produce too much noise in the loop below. # Tracing executed commands would produce too much noise in the loop below.
set +x set +x
@ -38,6 +38,19 @@ do
test_name="${TEST_EXIT%.exit}" test_name="${TEST_EXIT%.exit}"
test_name="${test_name##*/}" test_name="${test_name##*/}"
trash_dir="trash directory.$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" trash_tgz_b64="trash.$test_name.base64"
if [ -d "$trash_dir" ] if [ -d "$trash_dir" ]
then then

View file

@ -3,11 +3,14 @@
# Build and test Git # 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 make test
if test "$jobname" = "linux-gcc" if test "$jobname" = "linux-gcc"
then then

View file

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

View file

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

View file

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

17
ci/run-test-slice.sh Executable file
View 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

View file

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

View file

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

View file

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

1
t/.gitignore vendored
View file

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

View file

@ -170,6 +170,15 @@ appropriately before running "make".
implied by other options like --valgrind and implied by other options like --valgrind and
GIT_TEST_INSTALLED. 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>:: --root=<directory>::
Create "trash" directories used to store all temporary data during Create "trash" directories used to store all temporary data during
testing under <directory>, instead of the t/ directory. testing under <directory>, instead of the t/ directory.

View file

@ -8,6 +8,7 @@ static const char *usage_msg = "\n"
" test-tool date parse [date]...\n" " test-tool date parse [date]...\n"
" test-tool date approxidate [date]...\n" " test-tool date approxidate [date]...\n"
" test-tool date timestamp [date]...\n" " test-tool date timestamp [date]...\n"
" test-tool date getnanos [start-nanos]\n"
" test-tool date is64bit\n" " test-tool date is64bit\n"
" test-tool date time_t-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) int cmd__date(int argc, const char **argv)
{ {
struct timeval now; struct timeval now;
@ -119,6 +129,8 @@ int cmd__date(int argc, const char **argv)
parse_approxidate(argv+1, &now); parse_approxidate(argv+1, &now);
else if (!strcmp(*argv, "timestamp")) else if (!strcmp(*argv, "timestamp"))
parse_approx_timestamp(argv+1, &now); parse_approx_timestamp(argv+1, &now);
else if (!strcmp(*argv, "getnanos"))
getnanos(argv+1);
else if (!strcmp(*argv, "is64bit")) else if (!strcmp(*argv, "is64bit"))
return sizeof(timestamp_t) == 8 ? 0 : 1; return sizeof(timestamp_t) == 8 ? 0 : 1;
else if (!strcmp(*argv, "time_t-is64bit")) else if (!strcmp(*argv, "time_t-is64bit"))

View file

@ -177,6 +177,14 @@ static int is_dotgitmodules(const char *path)
return is_hfs_dotgitmodules(path) || is_ntfs_dotgitmodules(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) int cmd__path_utils(int argc, const char **argv)
{ {
if (argc == 3 && !strcmp(argv[1], "normalize_path_copy")) { if (argc == 3 && !strcmp(argv[1], "normalize_path_copy")) {
@ -291,6 +299,62 @@ int cmd__path_utils(int argc, const char **argv)
return !!res; 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], fprintf(stderr, "%s: unknown function name: %s\n", argv[0],
argv[1] ? argv[1] : "(there was none)"); argv[1] ? argv[1] : "(there was none)");
return 1; return 1;

View file

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

View file

@ -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__submodule_nested_repo_config(int argc, const char **argv);
int cmd__subprocess(int argc, const char **argv); int cmd__subprocess(int argc, const char **argv);
int cmd__urlmatch_normalization(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); int cmd__wildmatch(int argc, const char **argv);
#ifdef GIT_WINDOWS_NATIVE #ifdef GIT_WINDOWS_NATIVE
int cmd__windows_named_pipe(int argc, const char **argv); int cmd__windows_named_pipe(int argc, const char **argv);

View file

@ -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;
}

View file

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

View file

@ -166,7 +166,8 @@ test_trace () {
expect="$1" expect="$1"
shift shift
GIT_TRACE=1 test-tool run-command "$@" run-command true 2>&1 >/dev/null | \ 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 && echo "$expect true" >expect &&
test_cmp expect actual test_cmp expect actual
} }

View file

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

View file

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

View file

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

View file

@ -111,6 +111,8 @@ do
test -z "$HARNESS_ACTIVE" && quiet=t ;; test -z "$HARNESS_ACTIVE" && quiet=t ;;
--with-dashes) --with-dashes)
with_dashes=t ;; with_dashes=t ;;
--no-bin-wrappers)
no_bin_wrappers=t ;;
--no-color) --no-color)
color= ;; color= ;;
--va|--val|--valg|--valgr|--valgri|--valgrin|--valgrind) --va|--val|--valg|--valgr|--valgri|--valgrin|--valgrind)
@ -139,6 +141,9 @@ do
verbose_log=t verbose_log=t
tee=t tee=t
;; ;;
--write-junit-xml)
write_junit_xml=t
;;
--stress) --stress)
stress=t ;; stress=t ;;
--stress=*) --stress=*)
@ -622,11 +627,35 @@ trap 'exit $?' INT TERM HUP
# the test_expect_* functions instead. # the test_expect_* functions instead.
test_ok_ () { test_ok_ () {
if test -n "$write_junit_xml"
then
write_junit_xml_testcase "$*"
fi
test_success=$(($test_success + 1)) test_success=$(($test_success + 1))
say_color "" "ok $test_count - $@" say_color "" "ok $test_count - $@"
} }
test_failure_ () { 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)) test_failure=$(($test_failure + 1))
say_color error "not ok $test_count - $1" say_color error "not ok $test_count - $1"
shift shift
@ -635,11 +664,19 @@ test_failure_ () {
} }
test_known_broken_ok_ () { test_known_broken_ok_ () {
if test -n "$write_junit_xml"
then
write_junit_xml_testcase "$* (breakage fixed)"
fi
test_fixed=$(($test_fixed+1)) test_fixed=$(($test_fixed+1))
say_color error "ok $test_count - $@ # TODO known breakage vanished" say_color error "ok $test_count - $@ # TODO known breakage vanished"
} }
test_known_broken_failure_ () { test_known_broken_failure_ () {
if test -n "$write_junit_xml"
then
write_junit_xml_testcase "$* (known breakage)"
fi
test_broken=$(($test_broken+1)) test_broken=$(($test_broken+1))
say_color warn "not ok $test_count - $@ # TODO known breakage" say_color warn "not ok $test_count - $@ # TODO known breakage"
} }
@ -897,12 +934,21 @@ test_start_ () {
test_count=$(($test_count+1)) test_count=$(($test_count+1))
maybe_setup_verbose maybe_setup_verbose
maybe_setup_valgrind maybe_setup_valgrind
if test -n "$write_junit_xml"
then
junit_start=$(test-tool date getnanos)
fi
} }
test_finish_ () { test_finish_ () {
echo >&3 "" echo >&3 ""
maybe_teardown_valgrind maybe_teardown_valgrind
maybe_teardown_verbose 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 () { test_skip () {
@ -934,6 +980,13 @@ test_skip () {
case "$to_skip" in case "$to_skip" in
t) 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 >&3 "skipping test: $@"
say_color skip "ok $test_count # skip $1 ($skipped_reason)" say_color skip "ok $test_count # skip $1 ($skipped_reason)"
: true : 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 () { test_done () {
GIT_EXIT_OK=t 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" if test -z "$HARNESS_ACTIVE"
then then
mkdir -p "$TEST_RESULTS_DIR" mkdir -p "$TEST_RESULTS_DIR"
@ -1011,7 +1106,11 @@ test_done () {
error "Tests passed but trash directory already removed before test cleanup; aborting" error "Tests passed but trash directory already removed before test cleanup; aborting"
cd "$TRASH_DIRECTORY/.." && 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" error "Tests passed but test cleanup failed; aborting"
fi fi
test_at_end_hook_ test_at_end_hook_
@ -1117,20 +1216,25 @@ then
PATH=$GIT_TEST_INSTALLED:$GIT_BUILD_DIR/t/helper:$PATH PATH=$GIT_TEST_INSTALLED:$GIT_BUILD_DIR/t/helper:$PATH
GIT_EXEC_PATH=${GIT_TEST_EXEC_PATH:-$GIT_EXEC_PATH} GIT_EXEC_PATH=${GIT_TEST_EXEC_PATH:-$GIT_EXEC_PATH}
else # normal case, use ../bin-wrappers only unless $with_dashes: else # normal case, use ../bin-wrappers only unless $with_dashes:
git_bin_dir="$GIT_BUILD_DIR/bin-wrappers" if test -n "$no_bin_wrappers"
if ! test -x "$git_bin_dir/git"
then then
if test -z "$with_dashes"
then
say "$git_bin_dir/git is not executable; using GIT_EXEC_PATH"
fi
with_dashes=t with_dashes=t
else
git_bin_dir="$GIT_BUILD_DIR/bin-wrappers"
if ! test -x "$git_bin_dir/git"
then
if test -z "$with_dashes"
then
say "$git_bin_dir/git is not executable; using GIT_EXEC_PATH"
fi
with_dashes=t
fi
PATH="$git_bin_dir:$PATH"
fi fi
PATH="$git_bin_dir:$PATH"
GIT_EXEC_PATH=$GIT_BUILD_DIR GIT_EXEC_PATH=$GIT_BUILD_DIR
if test -n "$with_dashes" if test -n "$with_dashes"
then then
PATH="$GIT_BUILD_DIR:$PATH" PATH="$GIT_BUILD_DIR:$GIT_BUILD_DIR/t/helper:$PATH"
fi fi
fi fi
GIT_TEMPLATE_DIR="$GIT_BUILD_DIR"/templates/blt GIT_TEMPLATE_DIR="$GIT_BUILD_DIR"/templates/blt
@ -1178,6 +1282,7 @@ then
else else
mkdir -p "$TRASH_DIRECTORY" mkdir -p "$TRASH_DIRECTORY"
fi fi
# Use -P to resolve symlinks in our working directory so that the cwd # Use -P to resolve symlinks in our working directory so that the cwd
# in subprocesses like git equals our $PWD (for pathname comparisons). # in subprocesses like git equals our $PWD (for pathname comparisons).
cd -P "$TRASH_DIRECTORY" || exit 1 cd -P "$TRASH_DIRECTORY" || exit 1
@ -1191,6 +1296,23 @@ then
test_done test_done
fi 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 # Provide an implementation of the 'yes' utility
yes () { yes () {
if test $# = 0 if test $# = 0