1
0
mirror of https://github.com/facebook/zstd.git synced 2025-09-16 09:36:32 +02:00

Merge pull request #3394 from terrelln/issue-3010

[cli-tests] Test file stat read/write
This commit is contained in:
Yann Collet
2022-12-27 16:20:05 -08:00
committed by GitHub
21 changed files with 404 additions and 37 deletions

View File

@@ -66,6 +66,27 @@ extern "C" {
#define UTIL_DISPLAY(...) fprintf(stderr, __VA_ARGS__) #define UTIL_DISPLAY(...) fprintf(stderr, __VA_ARGS__)
#define UTIL_DISPLAYLEVEL(l, ...) { if (g_utilDisplayLevel>=l) { UTIL_DISPLAY(__VA_ARGS__); } } #define UTIL_DISPLAYLEVEL(l, ...) { if (g_utilDisplayLevel>=l) { UTIL_DISPLAY(__VA_ARGS__); } }
static int g_traceDepth = 0;
int g_traceFileStat = 0;
#define UTIL_TRACE_CALL(...) \
{ \
if (g_traceFileStat) { \
UTIL_DISPLAY("Trace:FileStat: %*s> ", g_traceDepth, ""); \
UTIL_DISPLAY(__VA_ARGS__); \
UTIL_DISPLAY("\n"); \
++g_traceDepth; \
} \
}
#define UTIL_TRACE_RET(ret) \
{ \
if (g_traceFileStat) { \
--g_traceDepth; \
UTIL_DISPLAY("Trace:FileStat: %*s< %d\n", g_traceDepth, "", (ret)); \
} \
}
/* A modified version of realloc(). /* A modified version of realloc().
* If UTIL_realloc() fails the original block is freed. * If UTIL_realloc() fails the original block is freed.
*/ */
@@ -121,21 +142,34 @@ int UTIL_requireUserConfirmation(const char* prompt, const char* abortMsg,
* Functions * Functions
***************************************/ ***************************************/
void UTIL_traceFileStat(void)
{
g_traceFileStat = 1;
}
int UTIL_stat(const char* filename, stat_t* statbuf) int UTIL_stat(const char* filename, stat_t* statbuf)
{ {
int ret;
UTIL_TRACE_CALL("UTIL_stat(%s)", filename);
#if defined(_MSC_VER) #if defined(_MSC_VER)
return !_stat64(filename, statbuf); ret = !_stat64(filename, statbuf);
#elif defined(__MINGW32__) && defined (__MSVCRT__) #elif defined(__MINGW32__) && defined (__MSVCRT__)
return !_stati64(filename, statbuf); ret = !_stati64(filename, statbuf);
#else #else
return !stat(filename, statbuf); ret = !stat(filename, statbuf);
#endif #endif
UTIL_TRACE_RET(ret);
return ret;
} }
int UTIL_isRegularFile(const char* infilename) int UTIL_isRegularFile(const char* infilename)
{ {
stat_t statbuf; stat_t statbuf;
return UTIL_stat(infilename, &statbuf) && UTIL_isRegularFileStat(&statbuf); int ret;
UTIL_TRACE_CALL("UTIL_isRegularFile(%s)", infilename);
ret = UTIL_stat(infilename, &statbuf) && UTIL_isRegularFileStat(&statbuf);
UTIL_TRACE_RET(ret);
return ret;
} }
int UTIL_isRegularFileStat(const stat_t* statbuf) int UTIL_isRegularFileStat(const stat_t* statbuf)
@@ -151,44 +185,66 @@ int UTIL_isRegularFileStat(const stat_t* statbuf)
int UTIL_chmod(char const* filename, const stat_t* statbuf, mode_t permissions) int UTIL_chmod(char const* filename, const stat_t* statbuf, mode_t permissions)
{ {
stat_t localStatBuf; stat_t localStatBuf;
UTIL_TRACE_CALL("UTIL_chmod(%s, %u)", filename, (unsigned)permissions);
if (statbuf == NULL) { if (statbuf == NULL) {
if (!UTIL_stat(filename, &localStatBuf)) return 0; if (!UTIL_stat(filename, &localStatBuf)) {
UTIL_TRACE_RET(0);
return 0;
}
statbuf = &localStatBuf; statbuf = &localStatBuf;
} }
if (!UTIL_isRegularFileStat(statbuf)) return 0; /* pretend success, but don't change anything */ if (!UTIL_isRegularFileStat(statbuf)) {
return chmod(filename, permissions); UTIL_TRACE_RET(0);
return 0; /* pretend success, but don't change anything */
}
UTIL_TRACE_CALL("chmod");
{
int const ret = chmod(filename, permissions);
UTIL_TRACE_RET(ret);
UTIL_TRACE_RET(ret);
return ret;
}
} }
/* set access and modification times */ /* set access and modification times */
int UTIL_utime(const char* filename, const stat_t *statbuf) int UTIL_utime(const char* filename, const stat_t *statbuf)
{ {
int ret; int ret;
UTIL_TRACE_CALL("UTIL_utime(%s)", filename);
/* We check that st_mtime is a macro here in order to give us confidence /* We check that st_mtime is a macro here in order to give us confidence
* that struct stat has a struct timespec st_mtim member. We need this * that struct stat has a struct timespec st_mtim member. We need this
* check because there are some platforms that claim to be POSIX 2008 * check because there are some platforms that claim to be POSIX 2008
* compliant but which do not have st_mtim... */ * compliant but which do not have st_mtim... */
#if (PLATFORM_POSIX_VERSION >= 200809L) && defined(st_mtime) #if (PLATFORM_POSIX_VERSION >= 200809L) && defined(st_mtime)
/* (atime, mtime) */ {
struct timespec timebuf[2] = { {0, UTIME_NOW} }; /* (atime, mtime) */
timebuf[1] = statbuf->st_mtim; struct timespec timebuf[2] = { {0, UTIME_NOW} };
ret = utimensat(AT_FDCWD, filename, timebuf, 0); timebuf[1] = statbuf->st_mtim;
ret = utimensat(AT_FDCWD, filename, timebuf, 0);
}
#else #else
struct utimbuf timebuf; {
timebuf.actime = time(NULL); struct utimbuf timebuf;
timebuf.modtime = statbuf->st_mtime; timebuf.actime = time(NULL);
ret = utime(filename, &timebuf); timebuf.modtime = statbuf->st_mtime;
ret = utime(filename, &timebuf);
}
#endif #endif
errno = 0; errno = 0;
UTIL_TRACE_RET(ret);
return ret; return ret;
} }
int UTIL_setFileStat(const char *filename, const stat_t *statbuf) int UTIL_setFileStat(const char *filename, const stat_t *statbuf)
{ {
int res = 0; int res = 0;
stat_t curStatBuf; stat_t curStatBuf;
if (!UTIL_stat(filename, &curStatBuf) || !UTIL_isRegularFileStat(&curStatBuf)) UTIL_TRACE_CALL("UTIL_setFileStat(%s)", filename);
if (!UTIL_stat(filename, &curStatBuf) || !UTIL_isRegularFileStat(&curStatBuf)) {
UTIL_TRACE_RET(-1);
return -1; return -1;
}
/* set access and modification times */ /* set access and modification times */
res += UTIL_utime(filename, statbuf); res += UTIL_utime(filename, statbuf);
@@ -200,13 +256,18 @@ int UTIL_setFileStat(const char *filename, const stat_t *statbuf)
res += UTIL_chmod(filename, &curStatBuf, statbuf->st_mode & 07777); /* Copy file permissions */ res += UTIL_chmod(filename, &curStatBuf, statbuf->st_mode & 07777); /* Copy file permissions */
errno = 0; errno = 0;
UTIL_TRACE_RET(-res);
return -res; /* number of errors is returned */ return -res; /* number of errors is returned */
} }
int UTIL_isDirectory(const char* infilename) int UTIL_isDirectory(const char* infilename)
{ {
stat_t statbuf; stat_t statbuf;
return UTIL_stat(infilename, &statbuf) && UTIL_isDirectoryStat(&statbuf); int ret;
UTIL_TRACE_CALL("UTIL_isDirectory(%s)", infilename);
ret = UTIL_stat(infilename, &statbuf) && UTIL_isDirectoryStat(&statbuf);
UTIL_TRACE_RET(ret);
return ret;
} }
int UTIL_isDirectoryStat(const stat_t* statbuf) int UTIL_isDirectoryStat(const stat_t* statbuf)
@@ -224,33 +285,44 @@ int UTIL_compareStr(const void *p1, const void *p2) {
int UTIL_isSameFile(const char* fName1, const char* fName2) int UTIL_isSameFile(const char* fName1, const char* fName2)
{ {
int ret;
assert(fName1 != NULL); assert(fName2 != NULL); assert(fName1 != NULL); assert(fName2 != NULL);
UTIL_TRACE_CALL("UTIL_isSameFile(%s, %s)", fName1, fName2);
#if defined(_MSC_VER) || defined(_WIN32) #if defined(_MSC_VER) || defined(_WIN32)
/* note : Visual does not support file identification by inode. /* note : Visual does not support file identification by inode.
* inode does not work on Windows, even with a posix layer, like msys2. * inode does not work on Windows, even with a posix layer, like msys2.
* The following work-around is limited to detecting exact name repetition only, * The following work-around is limited to detecting exact name repetition only,
* aka `filename` is considered different from `subdir/../filename` */ * aka `filename` is considered different from `subdir/../filename` */
return !strcmp(fName1, fName2); ret = !strcmp(fName1, fName2);
#else #else
{ stat_t file1Stat; { stat_t file1Stat;
stat_t file2Stat; stat_t file2Stat;
return UTIL_stat(fName1, &file1Stat) ret = UTIL_stat(fName1, &file1Stat)
&& UTIL_stat(fName2, &file2Stat) && UTIL_stat(fName2, &file2Stat)
&& (file1Stat.st_dev == file2Stat.st_dev) && (file1Stat.st_dev == file2Stat.st_dev)
&& (file1Stat.st_ino == file2Stat.st_ino); && (file1Stat.st_ino == file2Stat.st_ino);
} }
#endif #endif
UTIL_TRACE_RET(ret);
return ret;
} }
/* UTIL_isFIFO : distinguish named pipes */ /* UTIL_isFIFO : distinguish named pipes */
int UTIL_isFIFO(const char* infilename) int UTIL_isFIFO(const char* infilename)
{ {
UTIL_TRACE_CALL("UTIL_isFIFO(%s)", infilename);
/* macro guards, as defined in : https://linux.die.net/man/2/lstat */ /* macro guards, as defined in : https://linux.die.net/man/2/lstat */
#if PLATFORM_POSIX_VERSION >= 200112L #if PLATFORM_POSIX_VERSION >= 200112L
stat_t statbuf; {
if (UTIL_stat(infilename, &statbuf) && UTIL_isFIFOStat(&statbuf)) return 1; stat_t statbuf;
if (UTIL_stat(infilename, &statbuf) && UTIL_isFIFOStat(&statbuf)) {
UTIL_TRACE_RET(1);
return 1;
}
}
#endif #endif
(void)infilename; (void)infilename;
UTIL_TRACE_RET(0);
return 0; return 0;
} }
@@ -278,13 +350,20 @@ int UTIL_isBlockDevStat(const stat_t* statbuf)
int UTIL_isLink(const char* infilename) int UTIL_isLink(const char* infilename)
{ {
UTIL_TRACE_CALL("UTIL_isLink(%s)", infilename);
/* macro guards, as defined in : https://linux.die.net/man/2/lstat */ /* macro guards, as defined in : https://linux.die.net/man/2/lstat */
#if PLATFORM_POSIX_VERSION >= 200112L #if PLATFORM_POSIX_VERSION >= 200112L
stat_t statbuf; {
int const r = lstat(infilename, &statbuf); stat_t statbuf;
if (!r && S_ISLNK(statbuf.st_mode)) return 1; int const r = lstat(infilename, &statbuf);
if (!r && S_ISLNK(statbuf.st_mode)) {
UTIL_TRACE_RET(1);
return 1;
}
}
#endif #endif
(void)infilename; (void)infilename;
UTIL_TRACE_RET(0);
return 0; return 0;
} }
@@ -294,13 +373,18 @@ static int g_fakeStdoutIsConsole = 0;
int UTIL_isConsole(FILE* file) int UTIL_isConsole(FILE* file)
{ {
int ret;
UTIL_TRACE_CALL("UTIL_isConsole(%d)", fileno(file));
if (file == stdin && g_fakeStdinIsConsole) if (file == stdin && g_fakeStdinIsConsole)
return 1; ret = 1;
if (file == stderr && g_fakeStderrIsConsole) else if (file == stderr && g_fakeStderrIsConsole)
return 1; ret = 1;
if (file == stdout && g_fakeStdoutIsConsole) else if (file == stdout && g_fakeStdoutIsConsole)
return 1; ret = 1;
return IS_CONSOLE(file); else
ret = IS_CONSOLE(file);
UTIL_TRACE_RET(ret);
return ret;
} }
void UTIL_fakeStdinIsConsole(void) void UTIL_fakeStdinIsConsole(void)
@@ -319,8 +403,16 @@ void UTIL_fakeStderrIsConsole(void)
U64 UTIL_getFileSize(const char* infilename) U64 UTIL_getFileSize(const char* infilename)
{ {
stat_t statbuf; stat_t statbuf;
if (!UTIL_stat(infilename, &statbuf)) return UTIL_FILESIZE_UNKNOWN; UTIL_TRACE_CALL("UTIL_getFileSize(%s)", infilename);
return UTIL_getFileSizeStat(&statbuf); if (!UTIL_stat(infilename, &statbuf)) {
UTIL_TRACE_RET(-1);
return UTIL_FILESIZE_UNKNOWN;
}
{
U64 const size = UTIL_getFileSizeStat(&statbuf);
UTIL_TRACE_RET((int)size);
return size;
}
} }
U64 UTIL_getFileSizeStat(const stat_t* statbuf) U64 UTIL_getFileSizeStat(const stat_t* statbuf)
@@ -397,11 +489,16 @@ U64 UTIL_getTotalFileSize(const char* const * fileNamesTable, unsigned nbFiles)
{ {
U64 total = 0; U64 total = 0;
unsigned n; unsigned n;
UTIL_TRACE_CALL("UTIL_getTotalFileSize(%u)", nbFiles);
for (n=0; n<nbFiles; n++) { for (n=0; n<nbFiles; n++) {
U64 const size = UTIL_getFileSize(fileNamesTable[n]); U64 const size = UTIL_getFileSize(fileNamesTable[n]);
if (size == UTIL_FILESIZE_UNKNOWN) return UTIL_FILESIZE_UNKNOWN; if (size == UTIL_FILESIZE_UNKNOWN) {
UTIL_TRACE_RET(-1);
return UTIL_FILESIZE_UNKNOWN;
}
total += size; total += size;
} }
UTIL_TRACE_RET((int)total);
return total; return total;
} }

View File

@@ -189,6 +189,11 @@ void UTIL_fakeStdinIsConsole(void);
void UTIL_fakeStdoutIsConsole(void); void UTIL_fakeStdoutIsConsole(void);
void UTIL_fakeStderrIsConsole(void); void UTIL_fakeStderrIsConsole(void);
/**
* Emit traces for functions that read, or modify file metadata.
*/
void UTIL_traceFileStat(void);
#define UTIL_FILESIZE_UNKNOWN ((U64)(-1)) #define UTIL_FILESIZE_UNKNOWN ((U64)(-1))
U64 UTIL_getFileSize(const char* infilename); U64 UTIL_getFileSize(const char* infilename);
U64 UTIL_getTotalFileSize(const char* const * fileNamesTable, unsigned nbFiles); U64 UTIL_getTotalFileSize(const char* const * fileNamesTable, unsigned nbFiles);

View File

@@ -1004,6 +1004,7 @@ int main(int argCount, const char* argv[])
if (!strcmp(argument, "--fake-stdin-is-console")) { UTIL_fakeStdinIsConsole(); continue; } if (!strcmp(argument, "--fake-stdin-is-console")) { UTIL_fakeStdinIsConsole(); continue; }
if (!strcmp(argument, "--fake-stdout-is-console")) { UTIL_fakeStdoutIsConsole(); continue; } if (!strcmp(argument, "--fake-stdout-is-console")) { UTIL_fakeStdoutIsConsole(); continue; }
if (!strcmp(argument, "--fake-stderr-is-console")) { UTIL_fakeStderrIsConsole(); continue; } if (!strcmp(argument, "--fake-stderr-is-console")) { UTIL_fakeStderrIsConsole(); continue; }
if (!strcmp(argument, "--trace-file-stat")) { UTIL_traceFileStat(); continue; }
/* long commands with arguments */ /* long commands with arguments */
#ifndef ZSTD_NODICT #ifndef ZSTD_NODICT

View File

@@ -45,6 +45,16 @@ Examples:
./run.py --preserve --verbose basic/help.sh ./run.py --preserve --verbose basic/help.sh
``` ```
### Updating exact output
If a test is failing because a `.stderr.exact` or `.stdout.exact` no longer matches, you can re-run the tests with `--set-exact-output` and the correct output will be written.
Example:
```
./run.py --set-exact-output
./run.py basic/help.sh --set-exact-output
```
## Writing a test ## Writing a test
Test cases are arbitrary executables, and can be written in any language, but are generally shell scripts. Test cases are arbitrary executables, and can be written in any language, but are generally shell scripts.

View File

@@ -0,0 +1,8 @@
#!/bin/sh
set -e
datagen > file
zstd file -q --trace-file-stat -o file.zst
zstd -tq file.zst

View File

@@ -0,0 +1,36 @@
Trace:FileStat: > UTIL_isLink(file)
Trace:FileStat: < 0
Trace:FileStat: > UTIL_isConsole(2)
Trace:FileStat: < 0
Trace:FileStat: > UTIL_getFileSize(file)
Trace:FileStat: > UTIL_stat(file)
Trace:FileStat: < 1
Trace:FileStat: < 65537
Trace:FileStat: > UTIL_isDirectory(file)
Trace:FileStat: > UTIL_stat(file)
Trace:FileStat: < 1
Trace:FileStat: < 0
Trace:FileStat: > UTIL_stat(file)
Trace:FileStat: < 1
Trace:FileStat: > UTIL_stat(file)
Trace:FileStat: < 1
Trace:FileStat: > UTIL_isSameFile(file, file.zst)
Trace:FileStat: > UTIL_stat(file)
Trace:FileStat: < 1
Trace:FileStat: > UTIL_stat(file.zst)
Trace:FileStat: < 0
Trace:FileStat: < 0
Trace:FileStat: > UTIL_isRegularFile(file.zst)
Trace:FileStat: > UTIL_stat(file.zst)
Trace:FileStat: < 0
Trace:FileStat: < 0
Trace:FileStat: > UTIL_isRegularFile(file.zst)
Trace:FileStat: > UTIL_stat(file.zst)
Trace:FileStat: < 1
Trace:FileStat: < 1
Trace:FileStat: > UTIL_getFileSize(file)
Trace:FileStat: > UTIL_stat(file)
Trace:FileStat: < 1
Trace:FileStat: < 65537
Trace:FileStat: > UTIL_utime(file.zst)
Trace:FileStat: < 0

View File

@@ -0,0 +1,8 @@
#!/bin/sh
set -e
datagen > file
zstd file -cq --trace-file-stat > file.zst
zstd -tq file.zst

View File

@@ -0,0 +1,22 @@
Trace:FileStat: > UTIL_isLink(file)
Trace:FileStat: < 0
Trace:FileStat: > UTIL_isConsole(1)
Trace:FileStat: < 0
Trace:FileStat: > UTIL_getFileSize(file)
Trace:FileStat: > UTIL_stat(file)
Trace:FileStat: < 1
Trace:FileStat: < 65537
Trace:FileStat: > UTIL_isDirectory(file)
Trace:FileStat: > UTIL_stat(file)
Trace:FileStat: < 1
Trace:FileStat: < 0
Trace:FileStat: > UTIL_stat(file)
Trace:FileStat: < 1
Trace:FileStat: > UTIL_isRegularFile(/*stdout*\)
Trace:FileStat: > UTIL_stat(/*stdout*\)
Trace:FileStat: < 0
Trace:FileStat: < 0
Trace:FileStat: > UTIL_getFileSize(file)
Trace:FileStat: > UTIL_stat(file)
Trace:FileStat: < 1
Trace:FileStat: < 65537

View File

@@ -0,0 +1,8 @@
#!/bin/sh
set -e
datagen > file
zstd < file -q --trace-file-stat -o file.zst
zstd -tq file.zst

View File

@@ -0,0 +1,28 @@
Trace:FileStat: > UTIL_isConsole(0)
Trace:FileStat: < 0
Trace:FileStat: > UTIL_isConsole(2)
Trace:FileStat: < 0
Trace:FileStat: > UTIL_getFileSize(/*stdin*\)
Trace:FileStat: > UTIL_stat(/*stdin*\)
Trace:FileStat: < 0
Trace:FileStat: < -1
Trace:FileStat: > UTIL_isDirectory(/*stdin*\)
Trace:FileStat: > UTIL_stat(/*stdin*\)
Trace:FileStat: < 0
Trace:FileStat: < 0
Trace:FileStat: > UTIL_isSameFile(/*stdin*\, file.zst)
Trace:FileStat: > UTIL_stat(/*stdin*\)
Trace:FileStat: < 0
Trace:FileStat: < 0
Trace:FileStat: > UTIL_isRegularFile(file.zst)
Trace:FileStat: > UTIL_stat(file.zst)
Trace:FileStat: < 0
Trace:FileStat: < 0
Trace:FileStat: > UTIL_isRegularFile(file.zst)
Trace:FileStat: > UTIL_stat(file.zst)
Trace:FileStat: < 1
Trace:FileStat: < 1
Trace:FileStat: > UTIL_getFileSize(/*stdin*\)
Trace:FileStat: > UTIL_stat(/*stdin*\)
Trace:FileStat: < 0
Trace:FileStat: < -1

View File

@@ -0,0 +1,8 @@
#!/bin/sh
set -e
datagen > file
zstd < file -cq --trace-file-stat > file.zst
zstd -tq file.zst

View File

@@ -0,0 +1,20 @@
Trace:FileStat: > UTIL_isConsole(0)
Trace:FileStat: < 0
Trace:FileStat: > UTIL_isConsole(1)
Trace:FileStat: < 0
Trace:FileStat: > UTIL_getFileSize(/*stdin*\)
Trace:FileStat: > UTIL_stat(/*stdin*\)
Trace:FileStat: < 0
Trace:FileStat: < -1
Trace:FileStat: > UTIL_isDirectory(/*stdin*\)
Trace:FileStat: > UTIL_stat(/*stdin*\)
Trace:FileStat: < 0
Trace:FileStat: < 0
Trace:FileStat: > UTIL_isRegularFile(/*stdout*\)
Trace:FileStat: > UTIL_stat(/*stdout*\)
Trace:FileStat: < 0
Trace:FileStat: < 0
Trace:FileStat: > UTIL_getFileSize(/*stdin*\)
Trace:FileStat: > UTIL_stat(/*stdin*\)
Trace:FileStat: < 0
Trace:FileStat: < -1

View File

@@ -0,0 +1,7 @@
#!/bin/sh
set -e
datagen | zstd -q > file.zst
zstd -dq --trace-file-stat file.zst

View File

@@ -0,0 +1,30 @@
Trace:FileStat: > UTIL_isLink(file.zst)
Trace:FileStat: < 0
Trace:FileStat: > UTIL_isConsole(1)
Trace:FileStat: < 0
Trace:FileStat: > UTIL_isConsole(2)
Trace:FileStat: < 0
Trace:FileStat: > UTIL_isDirectory(file.zst)
Trace:FileStat: > UTIL_stat(file.zst)
Trace:FileStat: < 1
Trace:FileStat: < 0
Trace:FileStat: > UTIL_stat(file.zst)
Trace:FileStat: < 1
Trace:FileStat: > UTIL_stat(file.zst)
Trace:FileStat: < 1
Trace:FileStat: > UTIL_isSameFile(file.zst, file)
Trace:FileStat: > UTIL_stat(file.zst)
Trace:FileStat: < 1
Trace:FileStat: > UTIL_stat(file)
Trace:FileStat: < 0
Trace:FileStat: < 0
Trace:FileStat: > UTIL_isRegularFile(file)
Trace:FileStat: > UTIL_stat(file)
Trace:FileStat: < 0
Trace:FileStat: < 0
Trace:FileStat: > UTIL_isRegularFile(file)
Trace:FileStat: > UTIL_stat(file)
Trace:FileStat: < 1
Trace:FileStat: < 1
Trace:FileStat: > UTIL_utime(file)
Trace:FileStat: < 0

View File

@@ -0,0 +1,7 @@
#!/bin/sh
set -e
datagen | zstd -q > file.zst
zstd -dcq --trace-file-stat file.zst > file

View File

@@ -0,0 +1,14 @@
Trace:FileStat: > UTIL_isLink(file.zst)
Trace:FileStat: < 0
Trace:FileStat: > UTIL_isConsole(1)
Trace:FileStat: < 0
Trace:FileStat: > UTIL_isDirectory(file.zst)
Trace:FileStat: > UTIL_stat(file.zst)
Trace:FileStat: < 1
Trace:FileStat: < 0
Trace:FileStat: > UTIL_stat(file.zst)
Trace:FileStat: < 1
Trace:FileStat: > UTIL_isRegularFile(/*stdout*\)
Trace:FileStat: > UTIL_stat(/*stdout*\)
Trace:FileStat: < 0
Trace:FileStat: < 0

View File

@@ -0,0 +1,7 @@
#!/bin/sh
set -e
datagen | zstd -q > file.zst
zstd -dcq --trace-file-stat < file.zst -o file

View File

@@ -0,0 +1,20 @@
Trace:FileStat: > UTIL_isConsole(0)
Trace:FileStat: < 0
Trace:FileStat: > UTIL_isConsole(2)
Trace:FileStat: < 0
Trace:FileStat: > UTIL_isDirectory(/*stdin*\)
Trace:FileStat: > UTIL_stat(/*stdin*\)
Trace:FileStat: < 0
Trace:FileStat: < 0
Trace:FileStat: > UTIL_isSameFile(/*stdin*\, file)
Trace:FileStat: > UTIL_stat(/*stdin*\)
Trace:FileStat: < 0
Trace:FileStat: < 0
Trace:FileStat: > UTIL_isRegularFile(file)
Trace:FileStat: > UTIL_stat(file)
Trace:FileStat: < 0
Trace:FileStat: < 0
Trace:FileStat: > UTIL_isRegularFile(file)
Trace:FileStat: > UTIL_stat(file)
Trace:FileStat: < 1
Trace:FileStat: < 1

View File

@@ -0,0 +1,7 @@
#!/bin/sh
set -e
datagen | zstd -q > file.zst
zstd -dcq --trace-file-stat < file.zst > file

View File

@@ -0,0 +1,12 @@
Trace:FileStat: > UTIL_isConsole(0)
Trace:FileStat: < 0
Trace:FileStat: > UTIL_isConsole(1)
Trace:FileStat: < 0
Trace:FileStat: > UTIL_isDirectory(/*stdin*\)
Trace:FileStat: > UTIL_stat(/*stdin*\)
Trace:FileStat: < 0
Trace:FileStat: < 0
Trace:FileStat: > UTIL_isRegularFile(/*stdout*\)
Trace:FileStat: > UTIL_stat(/*stdout*\)
Trace:FileStat: < 0
Trace:FileStat: < 0

View File

@@ -209,6 +209,7 @@ class Options:
preserve: bool, preserve: bool,
scratch_dir: str, scratch_dir: str,
test_dir: str, test_dir: str,
set_exact_output: bool,
) -> None: ) -> None:
self.env = env self.env = env
self.timeout = timeout self.timeout = timeout
@@ -216,6 +217,7 @@ class Options:
self.preserve = preserve self.preserve = preserve
self.scratch_dir = scratch_dir self.scratch_dir = scratch_dir
self.test_dir = test_dir self.test_dir = test_dir
self.set_exact_output = set_exact_output
class TestCase: class TestCase:
@@ -335,7 +337,7 @@ class TestCase:
self._test_stdin.close() self._test_stdin.close()
self._test_stdin = None self._test_stdin = None
def _check_output_exact(self, out_name: str, expected: bytes) -> None: def _check_output_exact(self, out_name: str, expected: bytes, exact_name: str) -> None:
""" """
Check the output named :out_name: for an exact match against the :expected: content. Check the output named :out_name: for an exact match against the :expected: content.
Saves the success and message. Saves the success and message.
@@ -349,6 +351,10 @@ class TestCase:
self._success[check_name] = False self._success[check_name] = False
self._message[check_name] = f"{out_name} does not match!\n> diff expected actual\n{diff(expected, actual)}" self._message[check_name] = f"{out_name} does not match!\n> diff expected actual\n{diff(expected, actual)}"
if self._opts.set_exact_output:
with open(exact_name, "wb") as f:
f.write(actual)
def _check_output_glob(self, out_name: str, expected: bytes) -> None: def _check_output_glob(self, out_name: str, expected: bytes) -> None:
""" """
Check the output named :out_name: for a glob match against the :expected: glob. Check the output named :out_name: for a glob match against the :expected: glob.
@@ -386,7 +392,7 @@ class TestCase:
ignore_name = f"{self._test_file}.{out_name}.ignore" ignore_name = f"{self._test_file}.{out_name}.ignore"
if os.path.exists(exact_name): if os.path.exists(exact_name):
return self._check_output_exact(out_name, read_file(exact_name)) return self._check_output_exact(out_name, read_file(exact_name), exact_name)
elif os.path.exists(glob_name): elif os.path.exists(glob_name):
return self._check_output_glob(out_name, read_file(glob_name)) return self._check_output_glob(out_name, read_file(glob_name))
elif os.path.exists(ignore_name): elif os.path.exists(ignore_name):
@@ -394,7 +400,7 @@ class TestCase:
self._success[check_name] = True self._success[check_name] = True
self._message[check_name] = f"{out_name} ignored!" self._message[check_name] = f"{out_name} ignored!"
else: else:
return self._check_output_exact(out_name, bytes()) return self._check_output_exact(out_name, bytes(), exact_name)
def _check_stderr(self) -> None: def _check_stderr(self) -> None:
"""Checks the stderr output against the expectation.""" """Checks the stderr output against the expectation."""
@@ -678,6 +684,11 @@ if __name__ == "__main__":
"Scratch directory located in TEST_DIR/scratch/." "Scratch directory located in TEST_DIR/scratch/."
) )
) )
parser.add_argument(
"--set-exact-output",
action="store_true",
help="Set stderr.exact and stdout.exact for all failing tests, unless .ignore or .glob already exists"
)
parser.add_argument( parser.add_argument(
"tests", "tests",
nargs="*", nargs="*",
@@ -714,6 +725,7 @@ if __name__ == "__main__":
preserve=args.preserve, preserve=args.preserve,
test_dir=args.test_dir, test_dir=args.test_dir,
scratch_dir=scratch_dir, scratch_dir=scratch_dir,
set_exact_output=args.set_exact_output,
) )
if len(args.tests) == 0: if len(args.tests) == 0: