You've already forked pgbackrest
mirror of
https://github.com/pgbackrest/pgbackrest.git
synced 2025-07-07 00:35:37 +02:00
Improve the predictability of floating point numbers formatted as strings.
Some C libraries (e.g.musl) render floating point numbers differently when using printf(). This does not cause any problems for the core code but the unit tests require more predictability to function smoothly without a lot of exceptions. To accomplish this, wrap the various floating point operations in functions that mostly continue doing math using double but format the output string using integers. This leads to more predictable output at the cost of some complexity. Rounding could also be accomplished using nearbyint() but this would require linking the math library, which does not seem worth it for a fairly simple operation.
This commit is contained in:
@ -13,6 +13,19 @@
|
||||
<p>Fix defaults in command-line help.</p>
|
||||
</release-item>
|
||||
</release-improvement-list>
|
||||
|
||||
<release-development-list>
|
||||
<release-item>
|
||||
<github-pull-request id="2636"/>
|
||||
|
||||
<release-item-contributor-list>
|
||||
<release-item-contributor id="david.steele"/>
|
||||
<release-item-reviewer id="david.christensen"/>
|
||||
</release-item-contributor-list>
|
||||
|
||||
<p>Improve the predictability of floating point numbers formatted as strings.</p>
|
||||
</release-item>
|
||||
</release-development-list>
|
||||
</release-core-list>
|
||||
|
||||
<release-doc-list>
|
||||
|
@ -793,7 +793,9 @@ cmdArchiveGet(void)
|
||||
ArchiveTimeoutError,
|
||||
"unable to get WAL file '%s' from the archive asynchronously after %s second(s)\n"
|
||||
"HINT: check '%s' for errors.",
|
||||
strZ(walSegment), strZ(strNewDbl((double)cfgOptionInt64(cfgOptArchiveTimeout) / MSEC_PER_SEC)),
|
||||
strZ(walSegment),
|
||||
strZ(
|
||||
strNewDivP((uint64_t)cfgOptionInt64(cfgOptArchiveTimeout), MSEC_PER_SEC, .precision = 3, .trim = true)),
|
||||
strZ(cfgLoadLogFileName(cfgCmdRoleAsync)));
|
||||
}
|
||||
// Else report that the WAL segment could not be found
|
||||
|
@ -1456,11 +1456,10 @@ backupJobResult(
|
||||
if (copySize != file.sizeOriginal)
|
||||
strCatFmt(logProgress, "%s->", strZ(strSizeFormat(file.sizeOriginal)));
|
||||
|
||||
// Store percentComplete as an integer
|
||||
percentComplete = sizeTotal == 0 ? 10000 : (unsigned int)(((double)*sizeProgress / (double)sizeTotal) * 10000);
|
||||
// Store percentComplete as an integer (used to update progress in the lock file)
|
||||
percentComplete = cvtPctToUInt(*sizeProgress, sizeTotal);
|
||||
|
||||
strCatFmt(
|
||||
logProgress, "%s, %u.%02u%%", strZ(strSizeFormat(copySize)), percentComplete / 100, percentComplete % 100);
|
||||
strCatFmt(logProgress, "%s, %s", strZ(strSizeFormat(copySize)), strZ(strNewPct(*sizeProgress, sizeTotal)));
|
||||
|
||||
// Format log checksum
|
||||
const String *const logChecksum =
|
||||
|
@ -251,7 +251,7 @@ stanzaStatus(const int code, const InfoStanzaRepo *const stanzaData, const Varia
|
||||
{
|
||||
kvPut(
|
||||
backupLockKv, STATUS_KEY_LOCK_BACKUP_PERCENT_COMPLETE_VAR,
|
||||
VARUINT((unsigned int)(((double)stanzaData->sizeComplete / (double)stanzaData->size) * 10000)));
|
||||
VARUINT(cvtPctToUInt(stanzaData->sizeComplete, stanzaData->size)));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2138,7 +2138,7 @@ restoreJobResult(
|
||||
|
||||
// Add size and percent complete
|
||||
sizeRestored += file.size;
|
||||
strCatFmt(log, "%s, %.2lf%%)", strZ(strSizeFormat(file.size)), (double)sizeRestored * 100.00 / (double)sizeTotal);
|
||||
strCatFmt(log, "%s, %s)", strZ(strSizeFormat(file.size)), strZ(strNewPct(sizeRestored, sizeTotal)));
|
||||
|
||||
// If not zero-length add the checksum
|
||||
if (file.size != 0 && !zeroed)
|
||||
|
@ -200,11 +200,6 @@ FN_EXTERN size_t typeToLog(const char *typeName, char *buffer, size_t bufferSize
|
||||
#define FUNCTION_LOG_CHARPY_FORMAT(value, buffer, bufferSize) \
|
||||
ptrToLog(value, "char *[]", buffer, bufferSize)
|
||||
|
||||
#define FUNCTION_LOG_DOUBLE_TYPE \
|
||||
double
|
||||
#define FUNCTION_LOG_DOUBLE_FORMAT(value, buffer, bufferSize) \
|
||||
cvtDoubleToZ(value, buffer, bufferSize)
|
||||
|
||||
#define FUNCTION_LOG_INT_TYPE \
|
||||
int
|
||||
#define FUNCTION_LOG_INT_FORMAT(value, buffer, bufferSize) \
|
||||
|
@ -118,35 +118,98 @@ cvtBoolToConstZ(bool value)
|
||||
return value ? TRUE_Z : FALSE_Z;
|
||||
}
|
||||
|
||||
/**********************************************************************************************************************************/
|
||||
FN_EXTERN size_t
|
||||
cvtDoubleToZ(const double value, char *const buffer, const size_t bufferSize)
|
||||
/***********************************************************************************************************************************
|
||||
Round an integer contained in a string
|
||||
***********************************************************************************************************************************/
|
||||
static size_t
|
||||
cvtRound(size_t result, char *const buffer, const size_t bufferSize)
|
||||
{
|
||||
FUNCTION_TEST_BEGIN();
|
||||
FUNCTION_TEST_PARAM(DOUBLE, value);
|
||||
FUNCTION_TEST_PARAM(SIZE, result);
|
||||
FUNCTION_TEST_PARAM_P(CHARDATA, buffer);
|
||||
FUNCTION_TEST_PARAM(SIZE, bufferSize);
|
||||
FUNCTION_TEST_END();
|
||||
|
||||
ASSERT(buffer != NULL);
|
||||
ASSERT(bufferSize > 0);
|
||||
|
||||
// Convert to a string
|
||||
const size_t result = (size_t)snprintf(buffer, bufferSize, "%lf", value);
|
||||
|
||||
if (result >= bufferSize)
|
||||
for (int roundIdx = (int)result - 2; roundIdx >= 0; roundIdx--)
|
||||
{
|
||||
// Round when the rounding digit is >= 5 (current digit needs to be incremented) or the current digit > 9 (prior carry
|
||||
// overflowed and needs to be carried again)
|
||||
if (((roundIdx == (int)result - 2) && buffer[roundIdx + 1] >= '5') || buffer[roundIdx] > '9')
|
||||
{
|
||||
// Carry to the prior digit
|
||||
if (buffer[roundIdx] >= '9')
|
||||
{
|
||||
// If this is the first digit then add a new prior digit to carry to. Since it will start as zero we can just
|
||||
// set it to one.
|
||||
if (roundIdx == 0)
|
||||
{
|
||||
if (result + 1 >= bufferSize)
|
||||
THROW(AssertError, "buffer overflow");
|
||||
|
||||
// Any formatted double should be at least 8 characters, i.e. 0.000000
|
||||
ASSERT(strlen(buffer) >= 8);
|
||||
// Any formatted double should have a decimal point
|
||||
ASSERT(strchr(buffer, '.') != NULL);
|
||||
memmove(buffer + 1, buffer, ++result);
|
||||
buffer[1] = '0';
|
||||
buffer[0] = '1';
|
||||
}
|
||||
// Else set current digit to zero and carry to prior digit. An overflow is handled on the next iteration.
|
||||
else
|
||||
{
|
||||
buffer[roundIdx] = '0';
|
||||
buffer[roundIdx - 1]++;
|
||||
}
|
||||
}
|
||||
// Else increment current digit
|
||||
else
|
||||
buffer[roundIdx]++;
|
||||
}
|
||||
// Else stop rounding
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
// Remove rightmost digit used to start rounding
|
||||
result--;
|
||||
buffer[result] = '\0';
|
||||
|
||||
// Return string length
|
||||
FUNCTION_TEST_RETURN(SIZE, result);
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Separate the fractional part of an integer container in a string
|
||||
***********************************************************************************************************************************/
|
||||
static size_t
|
||||
cvtFraction(size_t result, const unsigned int precision, const bool trim, char *const buffer, const size_t bufferSize)
|
||||
{
|
||||
FUNCTION_TEST_BEGIN();
|
||||
FUNCTION_TEST_PARAM(SIZE, result);
|
||||
FUNCTION_TEST_PARAM(UINT, precision);
|
||||
FUNCTION_TEST_PARAM(BOOL, trim);
|
||||
FUNCTION_TEST_PARAM_P(CHARDATA, buffer);
|
||||
FUNCTION_TEST_PARAM(SIZE, bufferSize);
|
||||
FUNCTION_TEST_END();
|
||||
|
||||
ASSERT(buffer != NULL);
|
||||
ASSERT(bufferSize > 0);
|
||||
|
||||
// Add decimal point
|
||||
if (result + 1 >= bufferSize)
|
||||
THROW(AssertError, "buffer overflow");
|
||||
|
||||
memmove(buffer + result - precision + 1, buffer + result - precision, precision + 1);
|
||||
buffer[result - precision] = '.';
|
||||
buffer[++result] = '\0';
|
||||
|
||||
// Strip off any final 0s and the decimal point if there are no non-zero digits after it
|
||||
char *end = buffer + strlen(buffer) - 1;
|
||||
if (trim)
|
||||
{
|
||||
char *end = buffer + result - 1;
|
||||
|
||||
while (*end == '0' || *end == '.')
|
||||
{
|
||||
// It should not be possible to go past the beginning because format "%lf" will always write a decimal point
|
||||
// It should not be possible to go past the beginning because a decimal point is always written
|
||||
ASSERT(end > buffer);
|
||||
|
||||
end--;
|
||||
@ -158,8 +221,142 @@ cvtDoubleToZ(const double value, char *const buffer, const size_t bufferSize)
|
||||
// Zero terminate the string
|
||||
end[1] = 0;
|
||||
|
||||
// Calculate length
|
||||
result = (size_t)(end - buffer + 1);
|
||||
}
|
||||
|
||||
// Return string length
|
||||
FUNCTION_TEST_RETURN(SIZE, (size_t)(end - buffer + 1));
|
||||
FUNCTION_TEST_RETURN(SIZE, result);
|
||||
}
|
||||
|
||||
/**********************************************************************************************************************************/
|
||||
FN_EXTERN unsigned int
|
||||
cvtPctToUInt(const uint64_t dividend, const uint64_t divisor)
|
||||
{
|
||||
FUNCTION_TEST_BEGIN();
|
||||
FUNCTION_TEST_PARAM(UINT64, dividend);
|
||||
FUNCTION_TEST_PARAM(UINT64, divisor);
|
||||
FUNCTION_TEST_END();
|
||||
|
||||
ASSERT(dividend <= divisor);
|
||||
|
||||
// If 100% then return a fixed value to avoid any rounding throwing off the result
|
||||
if (dividend == divisor)
|
||||
FUNCTION_TEST_RETURN(UINT, 10000);
|
||||
|
||||
// Calculate percentage
|
||||
char buffer[CVT_PCT_BUFFER_SIZE];
|
||||
|
||||
size_t size = (size_t)snprintf(buffer, sizeof(buffer), "%04" PRIu64, (uint64_t)((double)dividend / (double)divisor * 100000));
|
||||
|
||||
// Round
|
||||
cvtRound(size, buffer, sizeof(buffer));
|
||||
|
||||
FUNCTION_TEST_RETURN(UINT, cvtZToUInt(buffer));
|
||||
}
|
||||
|
||||
/**********************************************************************************************************************************/
|
||||
FN_EXTERN size_t
|
||||
cvtPctToZ(const uint64_t dividend, const uint64_t divisor, char *const buffer, const size_t bufferSize)
|
||||
{
|
||||
FUNCTION_TEST_BEGIN();
|
||||
FUNCTION_TEST_PARAM(UINT64, dividend);
|
||||
FUNCTION_TEST_PARAM(UINT64, divisor);
|
||||
FUNCTION_TEST_PARAM_P(CHARDATA, buffer);
|
||||
FUNCTION_TEST_PARAM(SIZE, bufferSize);
|
||||
FUNCTION_TEST_END();
|
||||
|
||||
ASSERT(buffer != NULL);
|
||||
ASSERT(bufferSize > 0);
|
||||
ASSERT(dividend <= divisor);
|
||||
|
||||
// Calculate percentage as an integer
|
||||
size_t result = (size_t)snprintf(buffer, bufferSize, "%03u", cvtPctToUInt(dividend, divisor));
|
||||
|
||||
if (result >= bufferSize)
|
||||
THROW(AssertError, "buffer overflow");
|
||||
|
||||
// Separate fractional part
|
||||
result = cvtFraction(result, 2, false, buffer, bufferSize);
|
||||
|
||||
// Add percent sign
|
||||
if (result + 1 >= bufferSize)
|
||||
THROW(AssertError, "buffer overflow");
|
||||
|
||||
buffer[result++] = '%';
|
||||
buffer[result] = '\0';
|
||||
|
||||
// Return string length
|
||||
FUNCTION_TEST_RETURN(SIZE, result);
|
||||
}
|
||||
|
||||
/**********************************************************************************************************************************/
|
||||
FN_EXTERN size_t
|
||||
cvtDivToZ(
|
||||
const uint64_t dividend, const uint64_t divisor, const unsigned int precision, const bool trim, char *const buffer,
|
||||
const size_t bufferSize)
|
||||
{
|
||||
FUNCTION_TEST_BEGIN();
|
||||
FUNCTION_TEST_PARAM(UINT64, dividend);
|
||||
FUNCTION_TEST_PARAM(UINT64, divisor);
|
||||
FUNCTION_TEST_PARAM(UINT, precision);
|
||||
FUNCTION_TEST_PARAM(BOOL, trim);
|
||||
FUNCTION_TEST_PARAM_P(CHARDATA, buffer);
|
||||
FUNCTION_TEST_PARAM(SIZE, bufferSize);
|
||||
FUNCTION_TEST_END();
|
||||
|
||||
ASSERT(buffer != NULL);
|
||||
ASSERT(bufferSize > 0);
|
||||
|
||||
// Determine multiplier for precision digits
|
||||
unsigned int multiplier = 1;
|
||||
|
||||
switch (precision)
|
||||
{
|
||||
case 0:
|
||||
break;
|
||||
|
||||
case 1:
|
||||
multiplier = 10;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
multiplier = 100;
|
||||
break;
|
||||
|
||||
default:
|
||||
CHECK_FMT(AssertError, precision <= 3, "precision %u is invalid", precision);
|
||||
multiplier = 1000;
|
||||
break;
|
||||
}
|
||||
|
||||
CHECK_FMT(AssertError, dividend <= UINT64_MAX / multiplier, "dividend %" PRIu64 " is too large", dividend);
|
||||
|
||||
// If possible add a digit for rounding
|
||||
bool round = false;
|
||||
|
||||
if (dividend <= UINT64_MAX / (multiplier * 10))
|
||||
{
|
||||
round = true;
|
||||
multiplier *= 10;
|
||||
}
|
||||
|
||||
// Convert to string
|
||||
size_t result = (size_t)snprintf(buffer, bufferSize, "%0*" PRIu64, (int)precision, dividend * multiplier / divisor);
|
||||
|
||||
if (result >= bufferSize)
|
||||
THROW(AssertError, "buffer overflow");
|
||||
|
||||
// Round
|
||||
if (round)
|
||||
result = cvtRound(result, buffer, bufferSize);
|
||||
|
||||
// Separate fractional part
|
||||
if (precision > 0)
|
||||
result = cvtFraction(result, precision, trim, buffer, bufferSize);
|
||||
|
||||
// Return string length
|
||||
FUNCTION_TEST_RETURN(SIZE, result);
|
||||
}
|
||||
|
||||
/**********************************************************************************************************************************/
|
||||
|
@ -19,7 +19,8 @@ Required buffer sizes
|
||||
***********************************************************************************************************************************/
|
||||
#define CVT_BOOL_BUFFER_SIZE 6
|
||||
#define CVT_BASE10_BUFFER_SIZE 64
|
||||
#define CVT_DOUBLE_BUFFER_SIZE 318
|
||||
#define CVT_DIV_BUFFER_SIZE 68
|
||||
#define CVT_PCT_BUFFER_SIZE 8
|
||||
#define CVT_VARINT128_BUFFER_SIZE 10
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
@ -35,8 +36,14 @@ cvtCharToZ(const char value, char *const buffer, const size_t bufferSize)
|
||||
return (size_t)snprintf(buffer, bufferSize, "%c", value);
|
||||
}
|
||||
|
||||
// Convert double to zero-terminated string
|
||||
FN_EXTERN size_t cvtDoubleToZ(double value, char *buffer, size_t bufferSize);
|
||||
// Convert percentage to an integer. This is useful for exporting a percentage in a way that does not incur rounding.
|
||||
FN_EXTERN unsigned int cvtPctToUInt(uint64_t dividend, uint64_t divisor);
|
||||
|
||||
// Convert percentage to zero-terminated string. This is more reproducible than formatting the results of floating point division.
|
||||
FN_EXTERN size_t cvtPctToZ(uint64_t dividend, uint64_t divisor, char *buffer, size_t bufferSize);
|
||||
|
||||
// Convert division to zero-terminated string. This is more reproducible than formatting the results of floating point division.
|
||||
FN_EXTERN size_t cvtDivToZ(uint64_t dividend, uint64_t divisor, unsigned int precision, bool trim, char *buffer, size_t bufferSize);
|
||||
|
||||
// Convert int to zero-terminated string and vice versa
|
||||
FN_EXTERN size_t cvtIntToZ(const int value, char *buffer, size_t bufferSize);
|
||||
|
@ -162,17 +162,36 @@ strNewZ(const char *const string)
|
||||
|
||||
/**********************************************************************************************************************************/
|
||||
FN_EXTERN String *
|
||||
strNewDbl(const double value)
|
||||
strNewDiv(const uint64_t dividend, const uint64_t divisor, StrNewDivParam param)
|
||||
{
|
||||
FUNCTION_TEST_BEGIN();
|
||||
FUNCTION_TEST_PARAM(DOUBLE, value);
|
||||
FUNCTION_TEST_PARAM(UINT64, dividend);
|
||||
FUNCTION_TEST_PARAM(UINT64, divisor);
|
||||
FUNCTION_TEST_PARAM(UINT, param.precision);
|
||||
FUNCTION_TEST_PARAM(BOOL, param.trim);
|
||||
FUNCTION_TEST_END();
|
||||
|
||||
char working[CVT_DOUBLE_BUFFER_SIZE];
|
||||
char working[CVT_DIV_BUFFER_SIZE];
|
||||
|
||||
cvtDoubleToZ(value, working, sizeof(working));
|
||||
size_t resultSize = cvtDivToZ(dividend, divisor, param.precision, param.trim, working, sizeof(working));
|
||||
|
||||
FUNCTION_TEST_RETURN(STRING, strNewZ(working));
|
||||
FUNCTION_TEST_RETURN(STRING, strNewZN(working, resultSize));
|
||||
}
|
||||
|
||||
/**********************************************************************************************************************************/
|
||||
FN_EXTERN String *
|
||||
strNewPct(const uint64_t dividend, const uint64_t divisor)
|
||||
{
|
||||
FUNCTION_TEST_BEGIN();
|
||||
FUNCTION_TEST_PARAM(UINT64, dividend);
|
||||
FUNCTION_TEST_PARAM(UINT64, divisor);
|
||||
FUNCTION_TEST_END();
|
||||
|
||||
char working[CVT_PCT_BUFFER_SIZE];
|
||||
|
||||
size_t resultSize = cvtPctToZ(dividend, divisor, working, sizeof(working));
|
||||
|
||||
FUNCTION_TEST_RETURN(STRING, strNewZN(working, resultSize));
|
||||
}
|
||||
|
||||
/**********************************************************************************************************************************/
|
||||
@ -1052,26 +1071,31 @@ strSizeFormat(const uint64_t size)
|
||||
|
||||
if (size < 1024)
|
||||
result = strNewFmt("%" PRIu64 "B", size);
|
||||
else if (size < (1024 * 1024))
|
||||
{
|
||||
if ((uint64_t)((double)size / 102.4) % 10 != 0)
|
||||
result = strNewFmt("%.1lfKB", (double)size / 1024);
|
||||
else
|
||||
result = strNewFmt("%" PRIu64 "KB", size / 1024);
|
||||
{
|
||||
char working[CVT_DIV_BUFFER_SIZE];
|
||||
uint64_t divisor = 1024 * 1024 * 1024;
|
||||
unsigned int precision = 1;
|
||||
const char *suffix = "GB";
|
||||
|
||||
if (size < (1024 * 1024))
|
||||
{
|
||||
divisor = 1024;
|
||||
suffix = "KB";
|
||||
}
|
||||
else if (size < (1024 * 1024 * 1024))
|
||||
{
|
||||
if ((uint64_t)((double)size / (1024 * 102.4)) % 10 != 0)
|
||||
result = strNewFmt("%.1lfMB", (double)size / (1024 * 1024));
|
||||
else
|
||||
result = strNewFmt("%" PRIu64 "MB", size / (1024 * 1024));
|
||||
divisor = 1024 * 1024;
|
||||
suffix = "MB";
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((uint64_t)((double)size / (1024 * 1024 * 102.4)) % 10 != 0)
|
||||
result = strNewFmt("%.1lfGB", (double)size / (1024 * 1024 * 1024));
|
||||
else
|
||||
result = strNewFmt("%" PRIu64 "GB", size / (1024 * 1024 * 1024));
|
||||
|
||||
// Skip precision when it would cause overflow
|
||||
if (size > UINT64_MAX / 10)
|
||||
precision = 0;
|
||||
|
||||
// Format size
|
||||
cvtDivToZ(size, divisor, precision, true, working, sizeof(working));
|
||||
result = strNewFmt("%s%s", working, suffix);
|
||||
}
|
||||
|
||||
FUNCTION_TEST_RETURN(STRING, result);
|
||||
|
@ -66,8 +66,21 @@ FN_EXTERN String *strNewZN(const char *string, size_t size);
|
||||
// will be copied but only the data before the NULL character will be used as a string.
|
||||
FN_EXTERN String *strNewBuf(const Buffer *buffer);
|
||||
|
||||
// Create a new fixed length string by converting the double value
|
||||
FN_EXTERN String *strNewDbl(double value);
|
||||
// Create a new fixed length string by performing division
|
||||
typedef struct StrNewDivParam
|
||||
{
|
||||
VAR_PARAM_HEADER;
|
||||
unsigned int precision; // Digits of precision after the decimal
|
||||
bool trim; // Trim trailing zeros and decimal point?
|
||||
} StrNewDivParam;
|
||||
|
||||
#define strNewDivP(dividend, divisor, ...) \
|
||||
strNewDiv(dividend, divisor, (StrNewDivParam){VAR_PARAM_INIT, __VA_ARGS__})
|
||||
|
||||
FN_EXTERN String *strNewDiv(uint64_t dividend, uint64_t divisor, StrNewDivParam param);
|
||||
|
||||
// Create a new fixed length string containing a percentage
|
||||
FN_EXTERN String *strNewPct(uint64_t dividend, uint64_t divisor);
|
||||
|
||||
// Create a new fixed length string by converting a timestamp
|
||||
typedef struct StrNewTimeParam
|
||||
|
@ -448,7 +448,7 @@ cfgOptionDisplayVar(const Variant *const value, const ConfigOptionType optionTyp
|
||||
}
|
||||
else if (optionType == cfgOptTypeTime)
|
||||
{
|
||||
FUNCTION_TEST_RETURN_CONST(STRING, strNewDbl((double)varInt64(value) / MSEC_PER_SEC));
|
||||
FUNCTION_TEST_RETURN_CONST(STRING, strNewDivP(varUInt64Force(value), MSEC_PER_SEC, .precision = 3, .trim = true));
|
||||
}
|
||||
else if (optionType == cfgOptTypeStringId)
|
||||
{
|
||||
|
@ -102,7 +102,7 @@ unit:
|
||||
|
||||
# ----------------------------------------------------------------------------------------------------------------------------
|
||||
- name: type-convert
|
||||
total: 12
|
||||
total: 13
|
||||
|
||||
coverage:
|
||||
- common/type/convert
|
||||
|
@ -816,14 +816,11 @@ testCvgSummaryValue(const unsigned int hit, const unsigned int total)
|
||||
// Else render value
|
||||
else
|
||||
{
|
||||
strCatFmt(result, "%u/%u (", hit, total);
|
||||
|
||||
if (hit == total)
|
||||
strCatZ(result, "100.0");
|
||||
else
|
||||
strCatFmt(result, "%.2f", (float)hit * 100 / (float)total);
|
||||
|
||||
strCatZ(result, "%)");
|
||||
MEM_CONTEXT_TEMP_BEGIN()
|
||||
{
|
||||
strCatFmt(result, "%u/%u (%s)", hit, total, strZ(strNewPct(hit, total)));
|
||||
}
|
||||
MEM_CONTEXT_TEMP_END();
|
||||
}
|
||||
|
||||
FUNCTION_LOG_RETURN(STRING, result);
|
||||
|
@ -1944,7 +1944,7 @@ testRun(void)
|
||||
"P00 WARN: no prior backup exists, incr backup has been changed to full\n"
|
||||
"P00 WARN: --no-online passed and " PG_FILE_POSTMTRPID " exists but --force was passed so backup will continue though"
|
||||
" it looks like " PG_NAME " is running and the backup will probably not be consistent\n"
|
||||
"P01 DETAIL: backup file " TEST_PATH "/pg1/global/pg_control (8KB, 99.86%%) checksum %s\n"
|
||||
"P01 DETAIL: backup file " TEST_PATH "/pg1/global/pg_control (8KB, 99.87%%) checksum %s\n"
|
||||
"P01 DETAIL: backup file " TEST_PATH "/pg1/postgresql.conf (11B, 100.00%%) checksum"
|
||||
" e3db315c260e79211b7b52587123b7aa060f30ab\n"
|
||||
"P00 INFO: new backup label = [FULL-1]\n"
|
||||
@ -3268,7 +3268,7 @@ testRun(void)
|
||||
"P01 DETAIL: backup file " TEST_PATH "/pg1/postgresql.auto.conf (bundle 1/32, 12B, [PCT]) checksum [SHA1]\n"
|
||||
"P01 DETAIL: backup file " TEST_PATH "/pg1/postgresql.conf (bundle 1/64, 11B, [PCT]) checksum [SHA1]\n"
|
||||
"P01 DETAIL: backup file " TEST_PATH "/pg1/PG_VERSION (bundle 1/95, 2B, [PCT]) checksum [SHA1]\n"
|
||||
"P01 DETAIL: backup file " TEST_PATH "/pg1/bigish.dat (bundle 2/0, 8.0KB, [PCT]) checksum [SHA1]\n"
|
||||
"P01 DETAIL: backup file " TEST_PATH "/pg1/bigish.dat (bundle 2/0, 8KB, [PCT]) checksum [SHA1]\n"
|
||||
"P01 DETAIL: backup file " TEST_PATH "/pg1/global/pg_control (bundle 3/0, 8KB, [PCT]) checksum [SHA1]\n"
|
||||
"P00 INFO: execute non-exclusive backup stop and wait for all WAL segments to archive\n"
|
||||
"P00 INFO: backup stop archive = 0000000105DB8EB000000001, lsn = 5db8eb0/180000\n"
|
||||
@ -3569,7 +3569,7 @@ testRun(void)
|
||||
"P01 DETAIL: checksum resumed file " TEST_PATH "/pg1/block-incr-grow (24KB, [PCT]) checksum [SHA1]\n"
|
||||
"P01 DETAIL: backup file " TEST_PATH "/pg1/PG_VERSION (bundle 1/0, 2B, [PCT]) checksum [SHA1]\n"
|
||||
"P01 DETAIL: backup file " TEST_PATH "/pg1/normal-same (bundle 1/2, 4B, [PCT]) checksum [SHA1]\n"
|
||||
"P01 DETAIL: backup file " TEST_PATH "/pg1/grow-to-block-incr (bundle 1/6, 16.0KB, [PCT]) checksum [SHA1]\n"
|
||||
"P01 DETAIL: backup file " TEST_PATH "/pg1/grow-to-block-incr (bundle 1/6, 16KB, [PCT]) checksum [SHA1]\n"
|
||||
"P01 DETAIL: backup file " TEST_PATH "/pg1/global/pg_control (bundle 1/16389, 8KB, [PCT]) checksum [SHA1]\n"
|
||||
"P01 DETAIL: backup file " TEST_PATH "/pg1/block-incr-shrink-block (bundle 1/24581, 16KB, [PCT]) checksum [SHA1]\n"
|
||||
"P01 DETAIL: backup file " TEST_PATH "/pg1/block-incr-shrink-below (bundle 1/40987, 16KB, [PCT]) checksum [SHA1]\n"
|
||||
@ -3704,7 +3704,7 @@ testRun(void)
|
||||
"P01 DETAIL: backup file " TEST_PATH "/pg1/global/pg_control (bundle 1/16411, 8KB, [PCT]) checksum [SHA1]\n"
|
||||
"P01 DETAIL: backup file " TEST_PATH "/pg1/block-incr-shrink-block (bundle 1/24603, 8KB, [PCT]) checksum [SHA1]\n"
|
||||
"P01 DETAIL: backup file " TEST_PATH "/pg1/block-incr-shrink-below (bundle 1/24620, 8B, [PCT]) checksum [SHA1]\n"
|
||||
"P01 DETAIL: backup file " TEST_PATH "/pg1/block-incr-shrink (bundle 1/24628, 16.0KB, [PCT]) checksum [SHA1]\n"
|
||||
"P01 DETAIL: backup file " TEST_PATH "/pg1/block-incr-shrink (bundle 1/24628, 16KB, [PCT]) checksum [SHA1]\n"
|
||||
"P01 DETAIL: match file from prior backup " TEST_PATH "/pg1/block-incr-same (16KB, [PCT]) checksum [SHA1]\n"
|
||||
"P00 DETAIL: reference pg_data/PG_VERSION to 20191103-165320F\n"
|
||||
"P00 DETAIL: reference pg_data/block-incr-same to 20191103-165320F\n"
|
||||
|
@ -607,13 +607,13 @@ testRun(void)
|
||||
" timestamp start/stop: 2018-11-16 15:47:56+00 / 2018-11-16 15:48:09+00\n"
|
||||
" wal start/stop: n/a\n"
|
||||
" database size: 25.7MB, database backup size: 25.7MB\n"
|
||||
" repo1: backup set size: 3MB, backup size: 3KB\n"
|
||||
" repo1: backup set size: 3MB, backup size: 3.1KB\n"
|
||||
"\n"
|
||||
" full backup: 20201116-154900F\n"
|
||||
" timestamp start/stop: 2020-11-16 15:47:56+00 / 2020-11-16 15:48:00+00\n"
|
||||
" wal start/stop: 000000030000000000000001 / 000000030000000000000001\n"
|
||||
" database size: 25.7MB, database backup size: 25.7MB\n"
|
||||
" repo1: backup set size: 3MB, backup size: 3KB\n",
|
||||
" repo1: backup set size: 3MB, backup size: 3.1KB\n",
|
||||
"text - single stanza, valid backup, no priors, no archives in latest DB, backup/expire lock detected");
|
||||
|
||||
// Notify child to release lock
|
||||
@ -1530,7 +1530,7 @@ testRun(void)
|
||||
TEST_RESULT_STR_Z(
|
||||
infoRender(),
|
||||
"stanza: stanza1\n"
|
||||
" status: ok (backup/expire running - 65.27% complete)\n"
|
||||
" status: ok (backup/expire running - 65.28% complete)\n"
|
||||
" cipher: mixed\n"
|
||||
" repo1: none\n"
|
||||
" repo2: aes-256-cbc\n"
|
||||
|
@ -179,7 +179,7 @@ testRun(void)
|
||||
"\n"
|
||||
"file list:\n"
|
||||
" - pg_data/base/1/2\n"
|
||||
" size: 64KB, repo 64KB\n"
|
||||
" size: 64KB, repo 64.1KB\n"
|
||||
" checksum: 1adc95bebe9eea8c112d40cd04ab7a8d75c4f961\n"
|
||||
" bundle: 1\n"
|
||||
" block: size 8KB, map size 58B, checksum size 6B\n"
|
||||
@ -274,7 +274,7 @@ testRun(void)
|
||||
"\n"
|
||||
"file list:\n"
|
||||
" - pg_data/base/1/2\n"
|
||||
" size: 96KB, repo 64KB\n"
|
||||
" size: 96KB, repo 64.1KB\n"
|
||||
" checksum: d4976e362696a43fb09e7d4e780d7d9352a2ec2e\n"
|
||||
" bundle: 1\n"
|
||||
" block: size 8KB, map size 99B, checksum size 6B\n"
|
||||
@ -379,7 +379,7 @@ testRun(void)
|
||||
"\n"
|
||||
"file list:\n"
|
||||
" - pg_data/base/1/2\n"
|
||||
" size: 96KB, repo 64KB\n"
|
||||
" size: 96KB, repo 64.1KB\n"
|
||||
" checksum: d4976e362696a43fb09e7d4e780d7d9352a2ec2e\n"
|
||||
" bundle: 1\n"
|
||||
" block: size 8KB, map size 99B, checksum size 6B\n"
|
||||
@ -473,7 +473,7 @@ testRun(void)
|
||||
"\n"
|
||||
"file list:\n"
|
||||
" - pg_data/base/1/2\n"
|
||||
" size: 96KB, repo 64KB\n"
|
||||
" size: 96KB, repo 64.1KB\n"
|
||||
" checksum: d4976e362696a43fb09e7d4e780d7d9352a2ec2e\n"
|
||||
" bundle: 1\n"
|
||||
" block: size 8KB, map size 99B, checksum size 6B\n"
|
||||
|
@ -2503,7 +2503,7 @@ testRun(void)
|
||||
|
||||
// Replace percent complete and restore size since they can cause a lot of churn when files are added/removed
|
||||
hrnLogReplaceAdd(", [0-9]{1,3}\\.[0-9]{2}%\\)", "[0-9]+\\.[0-9]+%", "PCT", false);
|
||||
hrnLogReplaceAdd(" restore size = [0-9]+[A-Z]+", "[^ ]+$", "SIZE", false);
|
||||
hrnLogReplaceAdd(" restore size = [0-9]+(\\.[0-9]+){0,1}[A-Z]+", "[^ ]+$", "SIZE", false);
|
||||
|
||||
argList = strLstNew();
|
||||
hrnCfgArgRawZ(argList, cfgOptStanza, "test1");
|
||||
|
@ -2337,7 +2337,7 @@ testRun(void)
|
||||
"P00 INFO: backup start archive = 0000000105D95D3000000000, lsn = 5d95d30/0\n"
|
||||
"P00 INFO: check archive for prior segment 0000000105D95D2F000000FF\n"
|
||||
"P00 DETAIL: store zero-length file " TEST_PATH "/pg1/postgresql.auto.conf\n"
|
||||
"P01 DETAIL: backup file " TEST_PATH "/pg1/base/1/2 (bundle 1/0, 256KB, 96.96%) checksum [SHA1]\n"
|
||||
"P01 DETAIL: backup file " TEST_PATH "/pg1/base/1/2 (bundle 1/0, 256KB, 96.97%) checksum [SHA1]\n"
|
||||
"P01 DETAIL: backup file " TEST_PATH "/pg1/global/pg_control (bundle 1/237, 8KB, 100.00%) checksum [SHA1]\n"
|
||||
"P00 DETAIL: reference pg_data/PG_VERSION to 20191002-070640F\n"
|
||||
"P00 DETAIL: reference pg_data/base/1/44 to 20191002-070640F\n"
|
||||
|
@ -25,7 +25,7 @@ testFunction2(void)
|
||||
|
||||
static int
|
||||
testFunction1(
|
||||
int paramInt, bool paramBool, bool *paramBoolP, bool **paramBoolPP, void *paramVoidP, double paramDouble, mode_t paramMode)
|
||||
int paramInt, bool paramBool, bool *paramBoolP, bool **paramBoolPP, void *paramVoidP, mode_t paramMode)
|
||||
{
|
||||
FUNCTION_LOG_BEGIN(logLevelDebug);
|
||||
FUNCTION_LOG_PARAM(INT, paramInt);
|
||||
@ -33,7 +33,6 @@ testFunction1(
|
||||
FUNCTION_LOG_PARAM_P(BOOL, paramBoolP);
|
||||
FUNCTION_LOG_PARAM_PP(BOOL, paramBoolPP);
|
||||
FUNCTION_LOG_PARAM_P(VOID, paramVoidP);
|
||||
FUNCTION_LOG_PARAM(DOUBLE, paramDouble);
|
||||
FUNCTION_LOG_PARAM(MODE, paramMode);
|
||||
FUNCTION_LOG_END();
|
||||
|
||||
@ -185,11 +184,11 @@ testRun(void)
|
||||
if (testBegin("FUNCTION_DEBUG() and FUNCTION_LOG_RETURN()"))
|
||||
{
|
||||
harnessLogLevelSet(logLevelTrace);
|
||||
testFunction1(99, false, NULL, NULL, NULL, 1.17, 0755);
|
||||
testFunction1(99, false, NULL, NULL, NULL, 0755);
|
||||
|
||||
TEST_RESULT_LOG(
|
||||
"P00 DEBUG: " TEST_PGB_PATH "/test/src/module/common/debugOnTest::testFunction1: (paramInt: 99, paramBool: false,"
|
||||
" paramBoolP: null, paramBoolPP: null, paramVoidP: null, paramDouble: 1.17, paramMode: 0755)\n"
|
||||
" paramBoolP: null, paramBoolPP: null, paramVoidP: null, paramMode: 0755)\n"
|
||||
"P00 TRACE: " TEST_PGB_PATH "/test/src/module/common/debugOnTest::testFunction2: (void)\n"
|
||||
"P00 TRACE: " TEST_PGB_PATH "/test/src/module/common/debugOnTest::testFunction2: => void\n"
|
||||
"P00 DEBUG: " TEST_PGB_PATH "/test/src/module/common/debugOnTest::testFunction1: => 1");
|
||||
@ -200,11 +199,11 @@ testRun(void)
|
||||
bool **testBoolPP = &testBoolP;
|
||||
void *testVoidP = NULL;
|
||||
|
||||
testFunction1(99, false, testBoolP, testBoolPP, testVoidP, 1.17, 0755);
|
||||
testFunction1(99, false, testBoolP, testBoolPP, testVoidP, 0755);
|
||||
|
||||
TEST_RESULT_LOG(
|
||||
"P00 DEBUG: " TEST_PGB_PATH "/test/src/module/common/debugOnTest::testFunction1: (paramInt: 99, paramBool: false,"
|
||||
" paramBoolP: *true, paramBoolPP: **true, paramVoidP: null, paramDouble: 1.17, paramMode: 0755)\n"
|
||||
" paramBoolP: *true, paramBoolPP: **true, paramVoidP: null, paramMode: 0755)\n"
|
||||
"P00 TRACE: " TEST_PGB_PATH "/test/src/module/common/debugOnTest::testFunction2: (void)\n"
|
||||
"P00 TRACE: " TEST_PGB_PATH "/test/src/module/common/debugOnTest::testFunction2: => void\n"
|
||||
"P00 DEBUG: " TEST_PGB_PATH "/test/src/module/common/debugOnTest::testFunction1: => 1");
|
||||
@ -212,18 +211,18 @@ testRun(void)
|
||||
testBoolP = NULL;
|
||||
testVoidP = (void *)1;
|
||||
|
||||
testFunction1(99, false, testBoolP, testBoolPP, testVoidP, 1.17, 0755);
|
||||
testFunction1(99, false, testBoolP, testBoolPP, testVoidP, 0755);
|
||||
|
||||
TEST_RESULT_LOG(
|
||||
"P00 DEBUG: " TEST_PGB_PATH "/test/src/module/common/debugOnTest::testFunction1: (paramInt: 99, paramBool: false,"
|
||||
" paramBoolP: null, paramBoolPP: *null, paramVoidP: *void, paramDouble: 1.17, paramMode: 0755)\n"
|
||||
" paramBoolP: null, paramBoolPP: *null, paramVoidP: *void, paramMode: 0755)\n"
|
||||
"P00 TRACE: " TEST_PGB_PATH "/test/src/module/common/debugOnTest::testFunction2: (void)\n"
|
||||
"P00 TRACE: " TEST_PGB_PATH "/test/src/module/common/debugOnTest::testFunction2: => void\n"
|
||||
"P00 DEBUG: " TEST_PGB_PATH "/test/src/module/common/debugOnTest::testFunction1: => 1");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
harnessLogLevelReset();
|
||||
testFunction1(55, true, NULL, NULL, NULL, 0.99, 0755);
|
||||
testFunction1(55, true, NULL, NULL, NULL, 0755);
|
||||
|
||||
TEST_RESULT_LOG("");
|
||||
}
|
||||
|
@ -34,20 +34,74 @@ testRun(void)
|
||||
}
|
||||
|
||||
// *****************************************************************************************************************************
|
||||
if (testBegin("cvtDoubleToZ()"))
|
||||
if (testBegin("cvtDivToZ()"))
|
||||
{
|
||||
char buffer[STACK_TRACE_PARAM_MAX];
|
||||
char buffer[CVT_DIV_BUFFER_SIZE];
|
||||
|
||||
TEST_ERROR(cvtDoubleToZ(999.1234, buffer, 4), AssertError, "buffer overflow");
|
||||
TEST_ERROR(cvtDivToZ(100, 2, 0, false, buffer, 2), AssertError, "buffer overflow");
|
||||
TEST_ERROR(cvtDivToZ(999999, 100, 1, false, buffer, 7), AssertError, "buffer overflow");
|
||||
TEST_ERROR(cvtDivToZ(UINT64_MAX / 100, 100, 2, false, buffer, 19), AssertError, "buffer overflow");
|
||||
|
||||
TEST_RESULT_UINT(cvtDoubleToZ(999.1234, buffer, STACK_TRACE_PARAM_MAX), 8, "convert double to string");
|
||||
TEST_RESULT_Z(buffer, "999.1234", " check buffer");
|
||||
TEST_RESULT_UINT(cvtDivToZ(100, 2, 0, false, buffer, sizeof(buffer)), 2, "no precision");
|
||||
TEST_RESULT_Z(buffer, "50", " check buffer");
|
||||
|
||||
TEST_RESULT_UINT(cvtDoubleToZ(999999999.123456, buffer, STACK_TRACE_PARAM_MAX), 16, "convert double to string");
|
||||
TEST_RESULT_Z(buffer, "999999999.123456", " check buffer");
|
||||
TEST_RESULT_UINT(cvtDivToZ(100, 2, 3, false, buffer, sizeof(buffer)), 6, "all zero precision");
|
||||
TEST_RESULT_Z(buffer, "50.000", " check buffer");
|
||||
|
||||
TEST_RESULT_UINT(cvtDoubleToZ(999.0, buffer, STACK_TRACE_PARAM_MAX), 3, "convert double to string");
|
||||
TEST_RESULT_Z(buffer, "999", " check buffer");
|
||||
TEST_RESULT_UINT(cvtDivToZ(100, 2, 3, true, buffer, sizeof(buffer)), 2, "all zero precision trimmed");
|
||||
TEST_RESULT_Z(buffer, "50", " check buffer");
|
||||
|
||||
TEST_RESULT_UINT(cvtDivToZ(110, 100, 2, true, buffer, sizeof(buffer)), 3, "precision trimmed");
|
||||
TEST_RESULT_Z(buffer, "1.1", " check buffer");
|
||||
|
||||
TEST_RESULT_UINT(cvtDivToZ(5, 4, 1, true, buffer, sizeof(buffer)), 3, "precision rounded");
|
||||
TEST_RESULT_Z(buffer, "1.3", " check buffer");
|
||||
|
||||
TEST_RESULT_UINT(cvtDivToZ(5, 4, 3, true, buffer, sizeof(buffer)), 4, "precision exact");
|
||||
TEST_RESULT_Z(buffer, "1.25", " check buffer");
|
||||
|
||||
TEST_RESULT_UINT(cvtDivToZ(100, 3, 3, false, buffer, sizeof(buffer)), 6, "precision rounded");
|
||||
TEST_RESULT_Z(buffer, "33.333", " check buffer");
|
||||
|
||||
TEST_RESULT_UINT(cvtDivToZ(UINT64_MAX, 3, 0, false, buffer, sizeof(buffer)), 19, "no rounding for max allowed");
|
||||
TEST_RESULT_Z(buffer, "6148914691236517205", " check buffer");
|
||||
|
||||
TEST_RESULT_UINT(cvtDivToZ(1999, 10, 0, false, buffer, sizeof(buffer)), 3, "round up");
|
||||
TEST_RESULT_Z(buffer, "200", " check buffer");
|
||||
|
||||
TEST_RESULT_UINT(cvtDivToZ(999999, 100, 1, false, buffer, sizeof(buffer)), 7, "round up");
|
||||
TEST_RESULT_Z(buffer, "10000.0", " check buffer");
|
||||
|
||||
TEST_RESULT_UINT(cvtDivToZ(9199, 10, 0, false, buffer, sizeof(buffer)), 3, "partial round up");
|
||||
TEST_RESULT_Z(buffer, "920", " check buffer");
|
||||
|
||||
TEST_RESULT_UINT(cvtDivToZ(5555, 100, 1, false, buffer, sizeof(buffer)), 4, "partial round up");
|
||||
TEST_RESULT_Z(buffer, "55.6", " check buffer");
|
||||
}
|
||||
|
||||
// *****************************************************************************************************************************
|
||||
if (testBegin("cvtPctToZ()"))
|
||||
{
|
||||
char buffer[CVT_PCT_BUFFER_SIZE];
|
||||
|
||||
TEST_ERROR(cvtPctToZ(2, 100, buffer, 2), AssertError, "buffer overflow");
|
||||
TEST_ERROR(cvtPctToZ(100, 100, buffer, 2), AssertError, "buffer overflow");
|
||||
TEST_ERROR(cvtPctToZ(2, 100, buffer, 5), AssertError, "buffer overflow");
|
||||
|
||||
TEST_RESULT_UINT(cvtPctToZ(467, 467, buffer, sizeof(buffer)), 7, "100%");
|
||||
TEST_RESULT_Z(buffer, "100.00%", " check buffer");
|
||||
|
||||
TEST_RESULT_UINT(cvtPctToZ(111, 10000, buffer, sizeof(buffer)), 5, "100%");
|
||||
TEST_RESULT_Z(buffer, "1.11%", " check buffer");
|
||||
|
||||
TEST_RESULT_UINT(cvtPctToZ(2, 3, buffer, sizeof(buffer)), 6, "66.67%");
|
||||
TEST_RESULT_Z(buffer, "66.67%", " check buffer");
|
||||
|
||||
TEST_RESULT_UINT(cvtPctToZ(1, 10000, buffer, sizeof(buffer)), 5, "0.01%");
|
||||
TEST_RESULT_Z(buffer, "0.01%", " check buffer");
|
||||
|
||||
TEST_RESULT_UINT(cvtPctToZ(0, 1, buffer, sizeof(buffer)), 5, "0.00%");
|
||||
TEST_RESULT_Z(buffer, "0.00%", " check buffer");
|
||||
}
|
||||
|
||||
// *****************************************************************************************************************************
|
||||
|
@ -65,9 +65,16 @@ testRun(void)
|
||||
TEST_RESULT_STR_Z(strNewBuf(buffer), "12345678", "new string from buffer");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("new from double");
|
||||
TEST_TITLE("new from division");
|
||||
|
||||
TEST_RESULT_STR_Z(strNewDbl(999.1), "999.1", "new");
|
||||
TEST_RESULT_STR_Z(strNewDivP(9991, 10, .precision = 2), "999.10", "new div");
|
||||
TEST_RESULT_STR_Z(strNewDivP(9991, 10, .precision = 2, .trim = true), "999.1", "new div");
|
||||
TEST_RESULT_STR_Z(strNewDivP(9991, 10, .precision = 0), "999", "new div");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("new from percentage");
|
||||
|
||||
TEST_RESULT_STR_Z(strNewPct(2, 3), "66.67%", "new pct");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("new from time");
|
||||
@ -347,6 +354,7 @@ testRun(void)
|
||||
TEST_RESULT_STR_Z(strSizeFormat(20162900), "19.2MB", "19.2 MB");
|
||||
TEST_RESULT_STR_Z(strSizeFormat(1073741824), "1GB", "1 GB");
|
||||
TEST_RESULT_STR_Z(strSizeFormat(1073741824 + 107374183), "1.1GB", "1.1 GB");
|
||||
TEST_RESULT_STR_Z(strSizeFormat(UINT64_MAX / 10), "1717986918.3GB", "uint64 max / 10");
|
||||
TEST_RESULT_STR_Z(strSizeFormat(UINT64_MAX), "17179869183GB", "uint64 max");
|
||||
}
|
||||
|
||||
|
@ -432,9 +432,9 @@ testRun(void)
|
||||
"\n"
|
||||
"<table-row>\n"
|
||||
" <table-cell>common/error/sub</table-cell>\n"
|
||||
" <table-cell>1/1 (100.0%)</table-cell>\n"
|
||||
" <table-cell>1/1 (100.00%)</table-cell>\n"
|
||||
" <table-cell>---</table-cell>\n"
|
||||
" <table-cell>1/1 (100.0%)</table-cell>\n"
|
||||
" <table-cell>1/1 (100.00%)</table-cell>\n"
|
||||
"</table-row>\n"
|
||||
"\n"
|
||||
"<table-row>\n"
|
||||
|
Reference in New Issue
Block a user