зеркало из https://github.com/microsoft/git.git
Merge branch 'js/azure-pipelines-msvc'
CI updates. * js/azure-pipelines-msvc: ci: also build and test with MS Visual Studio on Azure Pipelines ci: really use shallow clones on Azure Pipelines tests: let --immediate and --write-junit-xml play well together test-tool run-command: learn to run (parts of) the testsuite vcxproj: include more generated files vcxproj: only copy `git-remote-http.exe` once it was built msvc: work around a bug in GetEnvironmentVariable() msvc: handle DEVELOPER=1 msvc: ignore some libraries when linking compat/win32/path-utils.h: add #include guards winansi: use FLEX_ARRAY to avoid compiler warning msvc: avoid using minus operator on unsigned types push: do not pretend to return `int` from `die_push_simple()`
This commit is contained in:
Коммит
6d5291be45
4
Makefile
4
Makefile
|
@ -3042,6 +3042,10 @@ rpm::
|
|||
@false
|
||||
.PHONY: rpm
|
||||
|
||||
ifneq ($(INCLUDE_DLLS_IN_ARTIFACTS),)
|
||||
OTHER_PROGRAMS += $(shell echo *.dll t/helper/*.dll)
|
||||
endif
|
||||
|
||||
artifacts-tar:: $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS) $(OTHER_PROGRAMS) \
|
||||
GIT-BUILD-OPTIONS $(TEST_PROGRAMS) $(test_bindir_programs) \
|
||||
$(MOFILES)
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
resources:
|
||||
- repo: self
|
||||
fetchDepth: 1
|
||||
variables:
|
||||
Agent.Source.Git.ShallowFetchDepth: 1
|
||||
|
||||
jobs:
|
||||
- job: windows_build
|
||||
|
@ -131,6 +130,165 @@ jobs:
|
|||
PathtoPublish: t/failed-test-artifacts
|
||||
ArtifactName: failed-test-artifacts
|
||||
|
||||
- job: vs_build
|
||||
displayName: Visual Studio Build
|
||||
condition: succeeded()
|
||||
pool: Hosted VS2017
|
||||
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 @"
|
||||
make vcxproj
|
||||
"@
|
||||
if (!$?) { exit(1) }
|
||||
displayName: Generate Visual Studio Solution
|
||||
env:
|
||||
HOME: $(Build.SourcesDirectory)
|
||||
MSYSTEM: MINGW64
|
||||
DEVELOPER: 1
|
||||
NO_PERL: 1
|
||||
GIT_CONFIG_PARAMETERS: "'user.name=CI' 'user.email=ci@git'"
|
||||
- powershell: |
|
||||
$urlbase = "https://dev.azure.com/git/git/_apis/build/builds"
|
||||
$id = ((Invoke-WebRequest -UseBasicParsing "${urlbase}?definitions=9&statusFilter=completed&resultFilter=succeeded&`$top=1").content | ConvertFrom-JSON).value[0].id
|
||||
$downloadUrl = ((Invoke-WebRequest -UseBasicParsing "${urlbase}/$id/artifacts").content | ConvertFrom-JSON).value[0].resource.downloadUrl
|
||||
(New-Object Net.WebClient).DownloadFile($downloadUrl, "compat.zip")
|
||||
Expand-Archive compat.zip -DestinationPath . -Force
|
||||
Remove-Item compat.zip
|
||||
displayName: 'Download vcpkg artifacts'
|
||||
- task: MSBuild@1
|
||||
inputs:
|
||||
solution: git.sln
|
||||
platform: x64
|
||||
configuration: Release
|
||||
maximumCpuCount: 4
|
||||
- powershell: |
|
||||
& compat\vcbuild\vcpkg_copy_dlls.bat release
|
||||
if (!$?) { exit(1) }
|
||||
& git-sdk-64-minimal\usr\bin\bash.exe -lc @"
|
||||
mkdir -p artifacts &&
|
||||
eval \"`$(make -n artifacts-tar INCLUDE_DLLS_IN_ARTIFACTS=YesPlease ARTIFACTS_DIRECTORY=artifacts | grep ^tar)\"
|
||||
"@
|
||||
if (!$?) { exit(1) }
|
||||
displayName: Bundle artifact tar
|
||||
env:
|
||||
HOME: $(Build.SourcesDirectory)
|
||||
MSYSTEM: MINGW64
|
||||
DEVELOPER: 1
|
||||
NO_PERL: 1
|
||||
MSVC: 1
|
||||
VCPKG_ROOT: $(Build.SourcesDirectory)\compat\vcbuild\vcpkg
|
||||
- powershell: |
|
||||
$tag = (Invoke-WebRequest -UseBasicParsing "https://gitforwindows.org/latest-tag.txt").content
|
||||
$version = (Invoke-WebRequest -UseBasicParsing "https://gitforwindows.org/latest-version.txt").content
|
||||
$url = "https://github.com/git-for-windows/git/releases/download/${tag}/PortableGit-${version}-64-bit.7z.exe"
|
||||
(New-Object Net.WebClient).DownloadFile($url,"PortableGit.exe")
|
||||
& .\PortableGit.exe -y -oartifacts\PortableGit
|
||||
# Wait until it is unpacked
|
||||
while (-not @(Remove-Item -ErrorAction SilentlyContinue PortableGit.exe; $?)) { sleep 1 }
|
||||
displayName: Download & extract portable Git
|
||||
- task: PublishPipelineArtifact@0
|
||||
displayName: 'Publish Pipeline Artifact: MSVC test artifacts'
|
||||
inputs:
|
||||
artifactName: 'vs-artifacts'
|
||||
targetPath: '$(Build.SourcesDirectory)\artifacts'
|
||||
- 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: vs_test
|
||||
displayName: Visual Studio Test
|
||||
dependsOn: vs_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: VS test artifacts'
|
||||
inputs:
|
||||
artifactName: 'vs-artifacts'
|
||||
targetPath: '$(Build.SourcesDirectory)'
|
||||
- powershell: |
|
||||
& PortableGit\git-cmd.exe --command=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' /PortableGit/ /test-cache/ >>.git/info/exclude
|
||||
|
||||
cd t &&
|
||||
PATH=\"`$PWD/helper:`$PATH\" &&
|
||||
test-tool.exe run-command testsuite -V -x --write-junit-xml \
|
||||
`$(test-tool.exe path-utils slice-tests \
|
||||
`$SYSTEM_JOBPOSITIONINPHASE `$SYSTEM_TOTALJOBSINPHASE t[0-9]*.sh)
|
||||
"@
|
||||
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: 'vs'
|
||||
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-vs-test-artifacts
|
||||
|
||||
- job: linux_clang
|
||||
displayName: linux-clang
|
||||
condition: succeeded()
|
||||
|
|
|
@ -143,8 +143,8 @@ static int push_url_of_remote(struct remote *remote, const char ***url_p)
|
|||
return remote->url_nr;
|
||||
}
|
||||
|
||||
static NORETURN int die_push_simple(struct branch *branch,
|
||||
struct remote *remote)
|
||||
static NORETURN void die_push_simple(struct branch *branch,
|
||||
struct remote *remote)
|
||||
{
|
||||
/*
|
||||
* There's no point in using shorten_unambiguous_ref here,
|
||||
|
|
13
cache.h
13
cache.h
|
@ -748,6 +748,19 @@ struct cache_entry *index_file_exists(struct index_state *istate, const char *na
|
|||
*/
|
||||
int index_name_pos(const struct index_state *, const char *name, int namelen);
|
||||
|
||||
/*
|
||||
* Some functions return the negative complement of an insert position when a
|
||||
* precise match was not found but a position was found where the entry would
|
||||
* need to be inserted. This helper protects that logic from any integer
|
||||
* underflow.
|
||||
*/
|
||||
static inline int index_pos_to_insert_pos(uintmax_t pos)
|
||||
{
|
||||
if (pos > INT_MAX)
|
||||
die("overflow: -1 - %"PRIuMAX, pos);
|
||||
return -1 - (int)pos;
|
||||
}
|
||||
|
||||
#define ADD_CACHE_OK_TO_ADD 1 /* Ok to add */
|
||||
#define ADD_CACHE_OK_TO_REPLACE 2 /* Ok to replace file/directory */
|
||||
#define ADD_CACHE_SKIP_DFCHECK 4 /* Ok to skip DF conflict checks */
|
||||
|
|
|
@ -1665,6 +1665,8 @@ char *mingw_getenv(const char *name)
|
|||
if (!w_key)
|
||||
die("Out of memory, (tried to allocate %u wchar_t's)", len_key);
|
||||
xutftowcs(w_key, name, len_key);
|
||||
/* GetEnvironmentVariableW() only sets the last error upon failure */
|
||||
SetLastError(ERROR_SUCCESS);
|
||||
len_value = GetEnvironmentVariableW(w_key, w_value, ARRAY_SIZE(w_value));
|
||||
if (!len_value && GetLastError() == ERROR_ENVVAR_NOT_FOUND) {
|
||||
free(w_key);
|
||||
|
|
|
@ -68,8 +68,54 @@ while (@ARGV) {
|
|||
} elsif ("$arg" =~ /^-L/ && "$arg" ne "-LTCG") {
|
||||
$arg =~ s/^-L/-LIBPATH:/;
|
||||
push(@lflags, $arg);
|
||||
} elsif ("$arg" =~ /^-R/) {
|
||||
} elsif ("$arg" =~ /^-[Rl]/) {
|
||||
# eat
|
||||
} elsif ("$arg" eq "-Werror") {
|
||||
push(@cflags, "-WX");
|
||||
} elsif ("$arg" eq "-Wall") {
|
||||
# cl.exe understands -Wall, but it is really overzealous
|
||||
push(@cflags, "-W4");
|
||||
# disable the "signed/unsigned mismatch" warnings; our source code violates that
|
||||
push(@cflags, "-wd4018");
|
||||
push(@cflags, "-wd4245");
|
||||
push(@cflags, "-wd4389");
|
||||
# disable the "unreferenced formal parameter" warning; our source code violates that
|
||||
push(@cflags, "-wd4100");
|
||||
# disable the "conditional expression is constant" warning; our source code violates that
|
||||
push(@cflags, "-wd4127");
|
||||
# disable the "const object should be initialized" warning; these warnings affect only objects that are `static`
|
||||
push(@cflags, "-wd4132");
|
||||
# disable the "function/data pointer conversion in expression" warning; our source code violates that
|
||||
push(@cflags, "-wd4152");
|
||||
# disable the "non-constant aggregate initializer" warning; our source code violates that
|
||||
push(@cflags, "-wd4204");
|
||||
# disable the "cannot be initialized using address of automatic variable" warning; our source code violates that
|
||||
push(@cflags, "-wd4221");
|
||||
# disable the "possible loss of data" warnings; our source code violates that
|
||||
push(@cflags, "-wd4244");
|
||||
push(@cflags, "-wd4267");
|
||||
# disable the "array is too small to include a terminating null character" warning; we ab-use strings to initialize OIDs
|
||||
push(@cflags, "-wd4295");
|
||||
# disable the "'<<': result of 32-bit shift implicitly converted to 64 bits" warning; our source code violates that
|
||||
push(@cflags, "-wd4334");
|
||||
# disable the "declaration hides previous local declaration" warning; our source code violates that
|
||||
push(@cflags, "-wd4456");
|
||||
# disable the "declaration hides function parameter" warning; our source code violates that
|
||||
push(@cflags, "-wd4457");
|
||||
# disable the "declaration hides global declaration" warning; our source code violates that
|
||||
push(@cflags, "-wd4459");
|
||||
# disable the "potentially uninitialized local variable '<name>' used" warning; our source code violates that
|
||||
push(@cflags, "-wd4701");
|
||||
# disable the "unreachable code" warning; our source code violates that
|
||||
push(@cflags, "-wd4702");
|
||||
# disable the "potentially uninitialized local pointer variable used" warning; our source code violates that
|
||||
push(@cflags, "-wd4703");
|
||||
# disable the "assignment within conditional expression" warning; our source code violates that
|
||||
push(@cflags, "-wd4706");
|
||||
# disable the "'inet_ntoa': Use inet_ntop() or InetNtop() instead" warning; our source code violates that
|
||||
push(@cflags, "-wd4996");
|
||||
} elsif ("$arg" =~ /^-W[a-z]/) {
|
||||
# let's ignore those
|
||||
} else {
|
||||
push(@args, $arg);
|
||||
}
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
#ifndef WIN32_PATH_UTILS_H
|
||||
#define WIN32_PATH_UTILS_H
|
||||
|
||||
#define has_dos_drive_prefix(path) \
|
||||
(isalpha(*(path)) && (path)[1] == ':' ? 2 : 0)
|
||||
int win32_skip_dos_drive_prefix(char **path);
|
||||
|
@ -18,3 +21,5 @@ static inline char *win32_find_last_dir_sep(const char *path)
|
|||
#define find_last_dir_sep win32_find_last_dir_sep
|
||||
int win32_offset_1st_component(const char *path);
|
||||
#define offset_1st_component win32_offset_1st_component
|
||||
|
||||
#endif
|
||||
|
|
|
@ -546,7 +546,7 @@ static HANDLE swap_osfhnd(int fd, HANDLE new_handle)
|
|||
typedef struct _OBJECT_NAME_INFORMATION
|
||||
{
|
||||
UNICODE_STRING Name;
|
||||
WCHAR NameBuffer[0];
|
||||
WCHAR NameBuffer[FLEX_ARRAY];
|
||||
} OBJECT_NAME_INFORMATION, *POBJECT_NAME_INFORMATION;
|
||||
|
||||
#define ObjectNameInformation 1
|
||||
|
|
|
@ -703,20 +703,24 @@ vcxproj:
|
|||
perl contrib/buildsystems/generate -g Vcxproj
|
||||
git add -f git.sln {*,*/lib,t/helper/*}/*.vcxproj
|
||||
|
||||
# Generate the LinkOrCopyBuiltins.targets file
|
||||
# Generate the LinkOrCopyBuiltins.targets and LinkOrCopyRemoteHttp.targets file
|
||||
(echo '<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">' && \
|
||||
echo ' <Target Name="CopyBuiltins_AfterBuild" AfterTargets="AfterBuild">' && \
|
||||
for name in $(BUILT_INS);\
|
||||
do \
|
||||
echo ' <Copy SourceFiles="$$(OutDir)\git.exe" DestinationFiles="$$(OutDir)\'"$$name"'" SkipUnchangedFiles="true" UseHardlinksIfPossible="true" />'; \
|
||||
done && \
|
||||
echo ' </Target>' && \
|
||||
echo '</Project>') >git/LinkOrCopyBuiltins.targets
|
||||
(echo '<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">' && \
|
||||
echo ' <Target Name="CopyBuiltins_AfterBuild" AfterTargets="AfterBuild">' && \
|
||||
for name in $(REMOTE_CURL_ALIASES); \
|
||||
do \
|
||||
echo ' <Copy SourceFiles="$$(OutDir)\'"$(REMOTE_CURL_PRIMARY)"'" DestinationFiles="$$(OutDir)\'"$$name"'" SkipUnchangedFiles="true" UseHardlinksIfPossible="true" />'; \
|
||||
done && \
|
||||
echo ' </Target>' && \
|
||||
echo '</Project>') >git/LinkOrCopyBuiltins.targets
|
||||
git add -f git/LinkOrCopyBuiltins.targets
|
||||
echo '</Project>') >git-remote-http/LinkOrCopyRemoteHttp.targets
|
||||
git add -f git/LinkOrCopyBuiltins.targets git-remote-http/LinkOrCopyRemoteHttp.targets
|
||||
|
||||
# Add command-list.h
|
||||
$(MAKE) MSVC=1 SKIP_VCPKG=1 prefix=/mingw64 command-list.h
|
||||
|
@ -724,11 +728,10 @@ vcxproj:
|
|||
|
||||
# Add scripts
|
||||
rm -f perl/perl.mak
|
||||
$(MAKE) MSVC=1 SKIP_VCPKG=1 prefix=/mingw64 \
|
||||
$(SCRIPT_LIB) $(SCRIPT_SH_GEN) $(SCRIPT_PERL_GEN)
|
||||
$(MAKE) MSVC=1 SKIP_VCPKG=1 prefix=/mingw64 $(SCRIPT_LIB) $(SCRIPTS)
|
||||
# Strip out the sane tool path, needed only for building
|
||||
sed -i '/^git_broken_path_fix ".*/d' git-sh-setup
|
||||
git add -f $(SCRIPT_LIB) $(SCRIPT_SH_GEN) $(SCRIPT_PERL_GEN)
|
||||
git add -f $(SCRIPT_LIB) $(SCRIPTS)
|
||||
|
||||
# Add Perl module
|
||||
$(MAKE) $(LIB_PERL_GEN)
|
||||
|
@ -758,6 +761,10 @@ vcxproj:
|
|||
$(MAKE) -C templates
|
||||
git add -f templates/boilerplates.made templates/blt/
|
||||
|
||||
# Add the translated messages
|
||||
make MSVC=1 SKIP_VCPKG=1 prefix=/mingw64 $(MOFILES)
|
||||
git add -f $(MOFILES)
|
||||
|
||||
# Add build options
|
||||
$(MAKE) MSVC=1 SKIP_VCPKG=1 prefix=/mingw64 GIT-BUILD-OPTIONS
|
||||
git add -f GIT-BUILD-OPTIONS
|
||||
|
|
|
@ -278,6 +278,9 @@ EOM
|
|||
if ($target eq 'git') {
|
||||
print F " <Import Project=\"LinkOrCopyBuiltins.targets\" />\n";
|
||||
}
|
||||
if ($target eq 'git-remote-http') {
|
||||
print F " <Import Project=\"LinkOrCopyRemoteHttp.targets\" />\n";
|
||||
}
|
||||
print F << "EOM";
|
||||
</Project>
|
||||
EOM
|
||||
|
|
|
@ -1276,7 +1276,7 @@ static int add_index_entry_with_check(struct index_state *istate, struct cache_e
|
|||
*/
|
||||
if (istate->cache_nr > 0 &&
|
||||
strcmp(ce->name, istate->cache[istate->cache_nr - 1]->name) > 0)
|
||||
pos = -istate->cache_nr - 1;
|
||||
pos = index_pos_to_insert_pos(istate->cache_nr);
|
||||
else
|
||||
pos = index_name_stage_pos(istate, ce->name, ce_namelen(ce), ce_stage(ce));
|
||||
|
||||
|
@ -1915,7 +1915,7 @@ static size_t estimate_cache_size(size_t ondisk_size, unsigned int entries)
|
|||
/*
|
||||
* Account for potential alignment differences.
|
||||
*/
|
||||
per_entry += align_padding_size(sizeof(struct cache_entry), -sizeof(struct ondisk_cache_entry));
|
||||
per_entry += align_padding_size(per_entry, 0);
|
||||
return ondisk_size + entries * per_entry;
|
||||
}
|
||||
|
||||
|
|
|
@ -70,7 +70,7 @@ int sha1_pos(const unsigned char *hash, void *table, size_t nr,
|
|||
if (miv < lov)
|
||||
return -1;
|
||||
if (hiv < miv)
|
||||
return -1 - nr;
|
||||
return index_pos_to_insert_pos(nr);
|
||||
if (lov != hiv) {
|
||||
/*
|
||||
* At this point miv could be equal
|
||||
|
@ -97,7 +97,7 @@ int sha1_pos(const unsigned char *hash, void *table, size_t nr,
|
|||
lo = mi + 1;
|
||||
mi = lo + (hi - lo) / 2;
|
||||
} while (lo < hi);
|
||||
return -lo-1;
|
||||
return index_pos_to_insert_pos(lo);
|
||||
}
|
||||
|
||||
int bsearch_hash(const unsigned char *sha1, const uint32_t *fanout_nbo,
|
||||
|
|
|
@ -10,9 +10,14 @@
|
|||
|
||||
#include "test-tool.h"
|
||||
#include "git-compat-util.h"
|
||||
#include "cache.h"
|
||||
#include "run-command.h"
|
||||
#include "argv-array.h"
|
||||
#include "strbuf.h"
|
||||
#include "parse-options.h"
|
||||
#include "string-list.h"
|
||||
#include "thread-utils.h"
|
||||
#include "wildmatch.h"
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
|
@ -50,11 +55,159 @@ static int task_finished(int result,
|
|||
return 1;
|
||||
}
|
||||
|
||||
struct testsuite {
|
||||
struct string_list tests, failed;
|
||||
int next;
|
||||
int quiet, immediate, verbose, verbose_log, trace, write_junit_xml;
|
||||
};
|
||||
#define TESTSUITE_INIT \
|
||||
{ STRING_LIST_INIT_DUP, STRING_LIST_INIT_DUP, -1, 0, 0, 0, 0, 0, 0 }
|
||||
|
||||
static int next_test(struct child_process *cp, struct strbuf *err, void *cb,
|
||||
void **task_cb)
|
||||
{
|
||||
struct testsuite *suite = cb;
|
||||
const char *test;
|
||||
if (suite->next >= suite->tests.nr)
|
||||
return 0;
|
||||
|
||||
test = suite->tests.items[suite->next++].string;
|
||||
argv_array_pushl(&cp->args, "sh", test, NULL);
|
||||
if (suite->quiet)
|
||||
argv_array_push(&cp->args, "--quiet");
|
||||
if (suite->immediate)
|
||||
argv_array_push(&cp->args, "-i");
|
||||
if (suite->verbose)
|
||||
argv_array_push(&cp->args, "-v");
|
||||
if (suite->verbose_log)
|
||||
argv_array_push(&cp->args, "-V");
|
||||
if (suite->trace)
|
||||
argv_array_push(&cp->args, "-x");
|
||||
if (suite->write_junit_xml)
|
||||
argv_array_push(&cp->args, "--write-junit-xml");
|
||||
|
||||
strbuf_addf(err, "Output of '%s':\n", test);
|
||||
*task_cb = (void *)test;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int test_finished(int result, struct strbuf *err, void *cb,
|
||||
void *task_cb)
|
||||
{
|
||||
struct testsuite *suite = cb;
|
||||
const char *name = (const char *)task_cb;
|
||||
|
||||
if (result)
|
||||
string_list_append(&suite->failed, name);
|
||||
|
||||
strbuf_addf(err, "%s: '%s'\n", result ? "FAIL" : "SUCCESS", name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_failed(struct strbuf *out, void *cb, void *task_cb)
|
||||
{
|
||||
struct testsuite *suite = cb;
|
||||
const char *name = (const char *)task_cb;
|
||||
|
||||
string_list_append(&suite->failed, name);
|
||||
strbuf_addf(out, "FAILED TO START: '%s'\n", name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char * const testsuite_usage[] = {
|
||||
"test-run-command testsuite [<options>] [<pattern>...]",
|
||||
NULL
|
||||
};
|
||||
|
||||
static int testsuite(int argc, const char **argv)
|
||||
{
|
||||
struct testsuite suite = TESTSUITE_INIT;
|
||||
int max_jobs = 1, i, ret;
|
||||
DIR *dir;
|
||||
struct dirent *d;
|
||||
struct option options[] = {
|
||||
OPT_BOOL('i', "immediate", &suite.immediate,
|
||||
"stop at first failed test case(s)"),
|
||||
OPT_INTEGER('j', "jobs", &max_jobs, "run <N> jobs in parallel"),
|
||||
OPT_BOOL('q', "quiet", &suite.quiet, "be terse"),
|
||||
OPT_BOOL('v', "verbose", &suite.verbose, "be verbose"),
|
||||
OPT_BOOL('V', "verbose-log", &suite.verbose_log,
|
||||
"be verbose, redirected to a file"),
|
||||
OPT_BOOL('x', "trace", &suite.trace, "trace shell commands"),
|
||||
OPT_BOOL(0, "write-junit-xml", &suite.write_junit_xml,
|
||||
"write JUnit-style XML files"),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
memset(&suite, 0, sizeof(suite));
|
||||
suite.tests.strdup_strings = suite.failed.strdup_strings = 1;
|
||||
|
||||
argc = parse_options(argc, argv, NULL, options,
|
||||
testsuite_usage, PARSE_OPT_STOP_AT_NON_OPTION);
|
||||
|
||||
if (max_jobs <= 0)
|
||||
max_jobs = online_cpus();
|
||||
|
||||
dir = opendir(".");
|
||||
if (!dir)
|
||||
die("Could not open the current directory");
|
||||
while ((d = readdir(dir))) {
|
||||
const char *p = d->d_name;
|
||||
|
||||
if (*p != 't' || !isdigit(p[1]) || !isdigit(p[2]) ||
|
||||
!isdigit(p[3]) || !isdigit(p[4]) || p[5] != '-' ||
|
||||
!ends_with(p, ".sh"))
|
||||
continue;
|
||||
|
||||
/* No pattern: match all */
|
||||
if (!argc) {
|
||||
string_list_append(&suite.tests, p);
|
||||
continue;
|
||||
}
|
||||
|
||||
for (i = 0; i < argc; i++)
|
||||
if (!wildmatch(argv[i], p, 0)) {
|
||||
string_list_append(&suite.tests, p);
|
||||
break;
|
||||
}
|
||||
}
|
||||
closedir(dir);
|
||||
|
||||
if (!suite.tests.nr)
|
||||
die("No tests match!");
|
||||
if (max_jobs > suite.tests.nr)
|
||||
max_jobs = suite.tests.nr;
|
||||
|
||||
fprintf(stderr, "Running %d tests (%d at a time)\n",
|
||||
suite.tests.nr, max_jobs);
|
||||
|
||||
ret = run_processes_parallel(max_jobs, next_test, test_failed,
|
||||
test_finished, &suite);
|
||||
|
||||
if (suite.failed.nr > 0) {
|
||||
ret = 1;
|
||||
fprintf(stderr, "%d tests failed:\n\n", suite.failed.nr);
|
||||
for (i = 0; i < suite.failed.nr; i++)
|
||||
fprintf(stderr, "\t%s\n", suite.failed.items[i].string);
|
||||
}
|
||||
|
||||
string_list_clear(&suite.tests, 0);
|
||||
string_list_clear(&suite.failed, 0);
|
||||
|
||||
return !!ret;
|
||||
}
|
||||
|
||||
int cmd__run_command(int argc, const char **argv)
|
||||
{
|
||||
struct child_process proc = CHILD_PROCESS_INIT;
|
||||
int jobs;
|
||||
|
||||
if (argc > 1 && !strcmp(argv[1], "testsuite"))
|
||||
exit(testsuite(argc - 1, argv + 1));
|
||||
|
||||
if (argc < 3)
|
||||
return 1;
|
||||
while (!strcmp(argv[1], "env")) {
|
||||
|
|
|
@ -572,6 +572,7 @@ export TERM
|
|||
|
||||
error () {
|
||||
say_color error "error: $*"
|
||||
finalize_junit_xml
|
||||
GIT_EXIT_OK=t
|
||||
exit 1
|
||||
}
|
||||
|
@ -700,7 +701,7 @@ test_failure_ () {
|
|||
say_color error "not ok $test_count - $1"
|
||||
shift
|
||||
printf '%s\n' "$*" | sed -e 's/^/# /'
|
||||
test "$immediate" = "" || { GIT_EXIT_OK=t; exit 1; }
|
||||
test "$immediate" = "" || { finalize_junit_xml; GIT_EXIT_OK=t; exit 1; }
|
||||
}
|
||||
|
||||
test_known_broken_ok_ () {
|
||||
|
@ -1068,6 +1069,25 @@ write_junit_xml_testcase () {
|
|||
junit_have_testcase=t
|
||||
}
|
||||
|
||||
finalize_junit_xml () {
|
||||
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>"
|
||||
write_junit_xml=
|
||||
fi
|
||||
}
|
||||
|
||||
test_atexit_cleanup=:
|
||||
test_atexit_handler () {
|
||||
# In a succeeding test script 'test_atexit_handler' is invoked
|
||||
|
@ -1090,21 +1110,7 @@ test_done () {
|
|||
# removed, so the commands can access pidfiles and socket files.
|
||||
test_atexit_handler
|
||||
|
||||
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
|
||||
finalize_junit_xml
|
||||
|
||||
if test -z "$HARNESS_ACTIVE"
|
||||
then
|
||||
|
|
Загрузка…
Ссылка в новой задаче