mirror of
https://github.com/fastfetch-cli/fastfetch.git
synced 2026-10-27 23:30:47 +01:00
Tests: adds more UTs
This commit is contained in:
@@ -2239,6 +2239,62 @@ if (BUILD_TESTS)
|
||||
PRIVATE libfastfetch
|
||||
)
|
||||
|
||||
add_executable(fastfetch-test-parsing
|
||||
tests/parsing.c
|
||||
)
|
||||
target_link_libraries(fastfetch-test-parsing
|
||||
PRIVATE libfastfetch
|
||||
)
|
||||
|
||||
add_executable(fastfetch-test-properties
|
||||
tests/properties.c
|
||||
)
|
||||
target_link_libraries(fastfetch-test-properties
|
||||
PRIVATE libfastfetch
|
||||
)
|
||||
|
||||
add_executable(fastfetch-test-size
|
||||
tests/size.c
|
||||
)
|
||||
target_link_libraries(fastfetch-test-size
|
||||
PRIVATE libfastfetch
|
||||
)
|
||||
|
||||
add_executable(fastfetch-test-frequency
|
||||
tests/frequency.c
|
||||
)
|
||||
target_link_libraries(fastfetch-test-frequency
|
||||
PRIVATE libfastfetch
|
||||
)
|
||||
|
||||
add_executable(fastfetch-test-percent
|
||||
tests/percent.c
|
||||
)
|
||||
target_link_libraries(fastfetch-test-percent
|
||||
PRIVATE libfastfetch
|
||||
)
|
||||
|
||||
add_executable(fastfetch-test-temps
|
||||
tests/temps.c
|
||||
)
|
||||
target_link_libraries(fastfetch-test-temps
|
||||
PRIVATE libfastfetch
|
||||
)
|
||||
|
||||
add_executable(fastfetch-test-endian
|
||||
tests/endian.c
|
||||
)
|
||||
target_link_libraries(fastfetch-test-endian
|
||||
PRIVATE libfastfetch
|
||||
)
|
||||
|
||||
add_executable(fastfetch-test-endian-bigendian
|
||||
tests/endian-bigendian.c
|
||||
)
|
||||
target_link_libraries(fastfetch-test-endian-bigendian
|
||||
PRIVATE libfastfetch
|
||||
)
|
||||
|
||||
enable_testing()
|
||||
add_test(NAME test-strbuf COMMAND fastfetch-test-strbuf)
|
||||
add_test(NAME test-list COMMAND fastfetch-test-list)
|
||||
@@ -2248,6 +2304,14 @@ if (BUILD_TESTS)
|
||||
add_test(NAME test-strutil COMMAND fastfetch-test-strutil)
|
||||
add_test(NAME test-networking COMMAND fastfetch-test-networking)
|
||||
add_test(NAME test-base64 COMMAND fastfetch-test-base64)
|
||||
add_test(NAME test-parsing COMMAND fastfetch-test-parsing)
|
||||
add_test(NAME test-properties COMMAND fastfetch-test-properties)
|
||||
add_test(NAME test-size COMMAND fastfetch-test-size)
|
||||
add_test(NAME test-frequency COMMAND fastfetch-test-frequency)
|
||||
add_test(NAME test-percent COMMAND fastfetch-test-percent)
|
||||
add_test(NAME test-temps COMMAND fastfetch-test-temps)
|
||||
add_test(NAME test-endian COMMAND fastfetch-test-endian)
|
||||
add_test(NAME test-endian-bigendian COMMAND fastfetch-test-endian-bigendian)
|
||||
endif()
|
||||
|
||||
##################
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
// This translation unit pretends to be compiled on a big endian host, so that the endianness
|
||||
// detection in `common/endian.h` can be exercised on a little endian machine.
|
||||
//
|
||||
// The selection is a preprocessor decision, so it can be pinned with `_Static_assert` instead of by
|
||||
// running anything -- which matters, because the alternative would be to observe a byte swap at run
|
||||
// time on hardware that really is little endian, and that cannot work.
|
||||
//
|
||||
// The case this guards: GCC does not define `__BIG_ENDIAN__` at all, it only documents
|
||||
// `__BYTE_ORDER__` and the `__ORDER_*_ENDIAN__` values. A header that tests `#if __BIG_ENDIAN__`
|
||||
// therefore evaluates it as 0 on s390x and silently picks the little endian branch, byte swapping
|
||||
// values that were already big endian. `__BIG_ENDIAN__` is undefined here for exactly that reason:
|
||||
// a header that only looks at its presence, or at its value, fails the assertions below.
|
||||
//
|
||||
// System headers are pulled in before the macros are faked so that they are not affected.
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "common/textModifier.h"
|
||||
|
||||
// What a big endian compiler reports ...
|
||||
#undef __BYTE_ORDER__
|
||||
#define __BYTE_ORDER__ __ORDER_BIG_ENDIAN__
|
||||
// ... and what GCC does not report on such a machine, even though it is one.
|
||||
#undef __BIG_ENDIAN__
|
||||
|
||||
#include "common/endian.h"
|
||||
|
||||
// On a big endian host the byte order of the machine and the byte order of the data agree, so
|
||||
// reading a big endian value is a no-op ...
|
||||
_Static_assert(FF_READ_BE((uint16_t) 0x1122) == (uint16_t) 0x1122, "FF_READ_BE must be the identity on a big endian host");
|
||||
_Static_assert(FF_READ_BE(0x11223344u) == 0x11223344u, "FF_READ_BE must be the identity on a big endian host");
|
||||
_Static_assert(FF_READ_BE(0x1122334455667788ull) == 0x1122334455667788ull, "FF_READ_BE must be the identity on a big endian host");
|
||||
|
||||
// ... and reading a little endian one swaps.
|
||||
_Static_assert(FF_READ_LE((uint16_t) 0x1122) == (uint16_t) 0x2211, "FF_READ_LE must swap on a big endian host");
|
||||
_Static_assert(FF_READ_LE(0x11223344u) == 0x44332211u, "FF_READ_LE must swap on a big endian host");
|
||||
_Static_assert(FF_READ_LE(0x1122334455667788ull) == 0x8877665544332211ull, "FF_READ_LE must swap on a big endian host");
|
||||
|
||||
int main(void) {
|
||||
// Everything this test asserts is checked at compile time, so reaching this line is the result.
|
||||
puts("\033[32mAll tests passed!" FASTFETCH_TEXT_MODIFIER_RESET);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
#include "common/endian.h"
|
||||
#include "common/textModifier.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
static void verify(bool expression, const char* expressionStr, int lineNo) {
|
||||
if (expression) {
|
||||
return;
|
||||
}
|
||||
|
||||
fprintf(stderr, FASTFETCH_TEXT_MODIFIER_ERROR "[%d] %s\n" FASTFETCH_TEXT_MODIFIER_RESET, lineNo, expressionStr);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
#define VERIFY(expression) verify((expression), #expression, __LINE__)
|
||||
|
||||
// The macros take a value, so the only way to test them is to lay out the bytes of a known number
|
||||
// in memory and read them back through the macro. The expectations below do not depend on the
|
||||
// endianness of the machine running the test:
|
||||
//
|
||||
// * copying the little endian byte sequence of 0x11223344 (44 33 22 11) into a `uint32_t` yields
|
||||
// 0x11223344 on a little endian machine and 0x44332211 on a big endian one. `FF_READ_LE` has to
|
||||
// turn both of them into 0x11223344, which means it is the identity on one machine and a byte
|
||||
// swap on the other.
|
||||
// * `FF_READ_BE` is the mirror image.
|
||||
//
|
||||
// On a little endian machine this only pins that the two macros are the identity / swap pair; the
|
||||
// selection itself (a compiler that does not define `__BIG_ENDIAN__` at all must still be detected
|
||||
// as big endian from `__BYTE_ORDER__`) can only be exercised by a big endian build, or by a cross
|
||||
// target such as `clang --target=s390x-linux-gnu`.
|
||||
static void verify16(void) {
|
||||
const uint8_t littleEndianBytes[2] = { 0x22, 0x11 };
|
||||
const uint8_t bigEndianBytes[2] = { 0x11, 0x22 };
|
||||
|
||||
uint16_t value;
|
||||
|
||||
memcpy(&value, littleEndianBytes, sizeof(value));
|
||||
VERIFY(FF_READ_LE(value) == (uint16_t) 0x1122);
|
||||
|
||||
memcpy(&value, bigEndianBytes, sizeof(value));
|
||||
VERIFY(FF_READ_BE(value) == (uint16_t) 0x1122);
|
||||
}
|
||||
|
||||
static void verify32(void) {
|
||||
const uint8_t littleEndianBytes[4] = { 0x44, 0x33, 0x22, 0x11 };
|
||||
const uint8_t bigEndianBytes[4] = { 0x11, 0x22, 0x33, 0x44 };
|
||||
|
||||
uint32_t value;
|
||||
|
||||
memcpy(&value, littleEndianBytes, sizeof(value));
|
||||
VERIFY(FF_READ_LE(value) == 0x11223344u);
|
||||
|
||||
memcpy(&value, bigEndianBytes, sizeof(value));
|
||||
VERIFY(FF_READ_BE(value) == 0x11223344u);
|
||||
}
|
||||
|
||||
static void verify64(void) {
|
||||
const uint8_t littleEndianBytes[8] = { 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11 };
|
||||
const uint8_t bigEndianBytes[8] = { 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88 };
|
||||
|
||||
uint64_t value;
|
||||
|
||||
memcpy(&value, littleEndianBytes, sizeof(value));
|
||||
VERIFY(FF_READ_LE(value) == 0x1122334455667788ull);
|
||||
|
||||
memcpy(&value, bigEndianBytes, sizeof(value));
|
||||
VERIFY(FF_READ_BE(value) == 0x1122334455667788ull);
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
verify16();
|
||||
verify32();
|
||||
verify64();
|
||||
|
||||
// A byte swap has to stay inside the width of the value it was given. A value whose bytes are
|
||||
// all equal reads the same through either macro, on either kind of machine.
|
||||
{
|
||||
uint16_t narrow = 0x0F0Fu;
|
||||
VERIFY(FF_READ_LE(narrow) == (uint16_t) 0x0F0F);
|
||||
VERIFY(FF_READ_BE(narrow) == (uint16_t) 0x0F0F);
|
||||
|
||||
uint32_t wide = 0x0F0F0F0Fu;
|
||||
VERIFY(FF_READ_LE(wide) == 0x0F0F0F0Fu);
|
||||
VERIFY(FF_READ_BE(wide) == 0x0F0F0F0Fu);
|
||||
|
||||
uint64_t widest = 0x0F0F0F0F0F0F0F0Full;
|
||||
VERIFY(FF_READ_LE(widest) == 0x0F0F0F0F0F0F0F0Full);
|
||||
VERIFY(FF_READ_BE(widest) == 0x0F0F0F0F0F0F0F0Full);
|
||||
}
|
||||
|
||||
puts("\033[32mAll tests passed!" FASTFETCH_TEXT_MODIFIER_RESET);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
#include "common/frequency.h"
|
||||
#include "common/textModifier.h"
|
||||
#include "fastfetch.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static void verify(uint32_t mhz, bool expectedResult, const char* expected, int lineNo) {
|
||||
FF_STRBUF_AUTO_DESTROY result = ffStrbufCreate();
|
||||
const bool returned = ffFreqAppendNum(mhz, &result);
|
||||
|
||||
if (returned != expectedResult || !ffStrbufEqualS(&result, expected)) {
|
||||
fprintf(stderr, FASTFETCH_TEXT_MODIFIER_ERROR "[%d] ffFreqAppendNum(%u): expected %s \"%s\", got %s \"%s\"\n" FASTFETCH_TEXT_MODIFIER_RESET,
|
||||
lineNo, mhz, expectedResult ? "true" : "false", expected, returned ? "true" : "false", result.chars);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
#define VERIFY_FREQ(mhz, expectedResult, expected) verify((mhz), (expectedResult), (expected), __LINE__)
|
||||
|
||||
int main(void) {
|
||||
// Use the real defaults rather than a hand built config, so a changed default is caught here too
|
||||
ffOptionsInitDisplay(&instance.config.display);
|
||||
FFOptionsDisplay* options = &instance.config.display;
|
||||
|
||||
// An unknown frequency prints nothing at all
|
||||
{
|
||||
VERIFY_FREQ(0, false, "");
|
||||
}
|
||||
|
||||
// With `freqNdigits >= 0` the value is printed in GHz, with that many digits after the point
|
||||
{
|
||||
VERIFY_FREQ(1000, true, "1.00 GHz");
|
||||
VERIFY_FREQ(3600, true, "3.60 GHz");
|
||||
VERIFY_FREQ(5000, true, "5.00 GHz");
|
||||
VERIFY_FREQ(100, true, "0.10 GHz");
|
||||
VERIFY_FREQ(1, true, "0.00 GHz"); // rounds down to zero, but is still reported
|
||||
VERIFY_FREQ(4294000, true, "4294.00 GHz"); // the whole `uint32_t` range is usable
|
||||
}
|
||||
|
||||
// `freqNdigits = 0` rounds to whole GHz
|
||||
{
|
||||
options->freqNdigits = 0;
|
||||
VERIFY_FREQ(1000, true, "1 GHz");
|
||||
VERIFY_FREQ(3600, true, "4 GHz"); // rounded, not truncated
|
||||
VERIFY_FREQ(1400, true, "1 GHz");
|
||||
VERIFY_FREQ(0, false, "");
|
||||
options->freqNdigits = 2;
|
||||
}
|
||||
|
||||
// A negative `freqNdigits` selects the integer MHz form instead
|
||||
{
|
||||
options->freqNdigits = -1;
|
||||
VERIFY_FREQ(3600, true, "3600 MHz");
|
||||
VERIFY_FREQ(1000, true, "1000 MHz");
|
||||
VERIFY_FREQ(1, true, "1 MHz");
|
||||
VERIFY_FREQ(0, false, "");
|
||||
options->freqNdigits = 2;
|
||||
}
|
||||
|
||||
// Only `never` removes the space between the number and the unit
|
||||
{
|
||||
options->freqSpaceBeforeUnit = FF_SPACE_BEFORE_UNIT_NEVER;
|
||||
VERIFY_FREQ(3600, true, "3.60GHz");
|
||||
options->freqNdigits = -1;
|
||||
VERIFY_FREQ(3600, true, "3600MHz");
|
||||
options->freqNdigits = 2;
|
||||
|
||||
options->freqSpaceBeforeUnit = FF_SPACE_BEFORE_UNIT_ALWAYS;
|
||||
VERIFY_FREQ(3600, true, "3.60 GHz");
|
||||
|
||||
options->freqSpaceBeforeUnit = FF_SPACE_BEFORE_UNIT_DEFAULT;
|
||||
VERIFY_FREQ(3600, true, "3.60 GHz");
|
||||
}
|
||||
|
||||
// The result is appended to the buffer instead of replacing it
|
||||
{
|
||||
FF_STRBUF_AUTO_DESTROY result = ffStrbufCreateS("CPU: ");
|
||||
if (!ffFreqAppendNum(3600, &result) || !ffStrbufEqualS(&result, "CPU: 3.60 GHz")) {
|
||||
fprintf(stderr, FASTFETCH_TEXT_MODIFIER_ERROR "[%d] ffFreqAppendNum did not append: got \"%s\"\n" FASTFETCH_TEXT_MODIFIER_RESET, __LINE__, result.chars);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
ffOptionsDestroyDisplay(options);
|
||||
|
||||
puts("\033[32mAll tests passed!" FASTFETCH_TEXT_MODIFIER_RESET);
|
||||
return 0;
|
||||
}
|
||||
+190
@@ -0,0 +1,190 @@
|
||||
#include "common/parsing.h"
|
||||
#include "common/textModifier.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static void verify(bool expression, const char* expressionStr, int lineNo) {
|
||||
if (expression) {
|
||||
return;
|
||||
}
|
||||
|
||||
fprintf(stderr, FASTFETCH_TEXT_MODIFIER_ERROR "[%d] %s\n" FASTFETCH_TEXT_MODIFIER_RESET, lineNo, expressionStr);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
#define VERIFY(expression) verify((expression), #expression, __LINE__)
|
||||
|
||||
static void verifyVersionCompare(uint32_t major1, uint32_t minor1, uint32_t patch1, uint32_t major2, uint32_t minor2, uint32_t patch2, int8_t expected, int lineNo) {
|
||||
const FFVersion first = { major1, minor1, patch1 };
|
||||
const FFVersion second = { major2, minor2, patch2 };
|
||||
|
||||
const int8_t forward = ffVersionCompare(&first, &second);
|
||||
const int8_t backward = ffVersionCompare(&second, &first);
|
||||
|
||||
// Only the sign is meaningful to callers, but the comparison is documented to be antisymmetric,
|
||||
// so a caller that negates the result to get "greater" must not be surprised.
|
||||
if (forward != expected || backward != -expected) {
|
||||
fprintf(stderr, FASTFETCH_TEXT_MODIFIER_ERROR "[%d] ffVersionCompare(%u.%u.%u, %u.%u.%u): expected %d/%d, got %d/%d\n" FASTFETCH_TEXT_MODIFIER_RESET,
|
||||
lineNo, major1, minor1, patch1, major2, minor2, patch2, expected, -expected, forward, backward);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
#define VERIFY_COMPARE(...) verifyVersionCompare(__VA_ARGS__, __LINE__)
|
||||
|
||||
static void verifyPretty(uint32_t major, uint32_t minor, uint32_t patch, const char* expected, int lineNo) {
|
||||
const FFVersion version = { major, minor, patch };
|
||||
FF_STRBUF_AUTO_DESTROY result = ffStrbufCreate();
|
||||
ffVersionToPretty(&version, &result);
|
||||
|
||||
if (!ffStrbufEqualS(&result, expected)) {
|
||||
fprintf(stderr, FASTFETCH_TEXT_MODIFIER_ERROR "[%d] ffVersionToPretty(%u.%u.%u): expected \"%s\", got \"%s\"\n" FASTFETCH_TEXT_MODIFIER_RESET,
|
||||
lineNo, major, minor, patch, expected, result.chars);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
#define VERIFY_PRETTY(...) verifyPretty(__VA_ARGS__, __LINE__)
|
||||
|
||||
static void verifySemver(const char* major, const char* minor, const char* patch, const char* expected, int lineNo) {
|
||||
FF_STRBUF_AUTO_DESTROY result = ffStrbufCreate();
|
||||
FF_STRBUF_AUTO_DESTROY majorBuf = ffStrbufCreateS(major);
|
||||
FF_STRBUF_AUTO_DESTROY minorBuf = ffStrbufCreateS(minor);
|
||||
FF_STRBUF_AUTO_DESTROY patchBuf = ffStrbufCreateS(patch);
|
||||
|
||||
ffParseSemver(&result, &majorBuf, &minorBuf, &patchBuf);
|
||||
|
||||
if (!ffStrbufEqualS(&result, expected)) {
|
||||
fprintf(stderr, FASTFETCH_TEXT_MODIFIER_ERROR "[%d] ffParseSemver(\"%s\", \"%s\", \"%s\"): expected \"%s\", got \"%s\"\n" FASTFETCH_TEXT_MODIFIER_RESET,
|
||||
lineNo, major, minor, patch, expected, result.chars);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
#define VERIFY_SEMVER(...) verifySemver(__VA_ARGS__, __LINE__)
|
||||
|
||||
static void verifyGtk(const char* gtk2, const char* gtk3, const char* gtk4, const char* expected, int lineNo) {
|
||||
FF_STRBUF_AUTO_DESTROY result = ffStrbufCreate();
|
||||
FF_STRBUF_AUTO_DESTROY gtk2Buf = ffStrbufCreateS(gtk2);
|
||||
FF_STRBUF_AUTO_DESTROY gtk3Buf = ffStrbufCreateS(gtk3);
|
||||
FF_STRBUF_AUTO_DESTROY gtk4Buf = ffStrbufCreateS(gtk4);
|
||||
|
||||
ffParseGTK(&result, >k2Buf, >k3Buf, >k4Buf);
|
||||
|
||||
if (!ffStrbufEqualS(&result, expected)) {
|
||||
fprintf(stderr, FASTFETCH_TEXT_MODIFIER_ERROR "[%d] ffParseGTK(\"%s\", \"%s\", \"%s\"): expected \"%s\", got \"%s\"\n" FASTFETCH_TEXT_MODIFIER_RESET,
|
||||
lineNo, gtk2, gtk3, gtk4, expected, result.chars);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
#define VERIFY_GTK(...) verifyGtk(__VA_ARGS__, __LINE__)
|
||||
|
||||
int main(void) {
|
||||
// ffVersionCompare: the major field decides first, then minor, then patch. Equal versions
|
||||
// compare equal even when they come from different sources.
|
||||
{
|
||||
VERIFY_COMPARE(0, 0, 0, 0, 0, 0, 0);
|
||||
VERIFY_COMPARE(1, 2, 3, 1, 2, 3, 0);
|
||||
VERIFY_COMPARE(UINT32_MAX, UINT32_MAX, UINT32_MAX, UINT32_MAX, UINT32_MAX, UINT32_MAX, 0);
|
||||
|
||||
VERIFY_COMPARE(2, 0, 0, 1, 99, 99, 1);
|
||||
VERIFY_COMPARE(1, 99, 99, 2, 0, 0, -1);
|
||||
|
||||
VERIFY_COMPARE(1, 2, 0, 1, 1, 99, 1);
|
||||
VERIFY_COMPARE(1, 1, 99, 1, 2, 0, -1);
|
||||
|
||||
VERIFY_COMPARE(1, 2, 3, 1, 2, 2, 1);
|
||||
VERIFY_COMPARE(1, 2, 2, 1, 2, 3, -1);
|
||||
|
||||
// A larger patch must not be able to outvote a smaller minor
|
||||
VERIFY_COMPARE(1, 2, 0, 1, 1, UINT32_MAX, 1);
|
||||
VERIFY_COMPARE(1, 1, UINT32_MAX, 1, 2, 0, -1);
|
||||
|
||||
// ... nor a larger minor a smaller major
|
||||
VERIFY_COMPARE(2, 0, 0, 1, UINT32_MAX, UINT32_MAX, 1);
|
||||
VERIFY_COMPARE(1, UINT32_MAX, UINT32_MAX, 2, 0, 0, -1);
|
||||
}
|
||||
|
||||
// ffVersionToPretty: a zero version prints as nothing at all, and a field is only printed when
|
||||
// it or a later field is non-zero.
|
||||
{
|
||||
VERIFY_PRETTY(0, 0, 0, "");
|
||||
VERIFY_PRETTY(1, 0, 0, "1");
|
||||
VERIFY_PRETTY(0, 1, 0, "0.1");
|
||||
VERIFY_PRETTY(0, 0, 1, "0.0.1");
|
||||
VERIFY_PRETTY(1, 2, 0, "1.2");
|
||||
VERIFY_PRETTY(1, 0, 3, "1.0.3");
|
||||
VERIFY_PRETTY(1, 2, 3, "1.2.3");
|
||||
VERIFY_PRETTY(10, 20, 30, "10.20.30");
|
||||
VERIFY_PRETTY(UINT32_MAX, 0, 0, "4294967295");
|
||||
VERIFY_PRETTY(0, 0, UINT32_MAX, "0.0.4294967295");
|
||||
}
|
||||
|
||||
// ffParseSemver: joins whichever parts were detected. A missing major is reported as 1, which is
|
||||
// how "2.3" from a source that only reports the last two components stays unambiguous.
|
||||
{
|
||||
VERIFY_SEMVER("1", "2", "3", "1.2.3");
|
||||
VERIFY_SEMVER("1", "2", "", "1.2");
|
||||
VERIFY_SEMVER("1", "", "", "1");
|
||||
VERIFY_SEMVER("1", "", "3", "1.0.3");
|
||||
VERIFY_SEMVER("", "2", "3", "1.2.3");
|
||||
VERIFY_SEMVER("", "2", "", "1.2");
|
||||
VERIFY_SEMVER("", "", "3", "1.0.3");
|
||||
VERIFY_SEMVER("", "", "", "");
|
||||
|
||||
// A zero part is a value, not a missing part
|
||||
VERIFY_SEMVER("0", "0", "0", "0.0.0");
|
||||
VERIFY_SEMVER("0", "", "", "0");
|
||||
}
|
||||
|
||||
// ffParseSemver appends instead of replacing, so a caller can prefix its own text
|
||||
{
|
||||
FF_STRBUF_AUTO_DESTROY result = ffStrbufCreateS("v");
|
||||
FF_STRBUF_AUTO_DESTROY majorBuf = ffStrbufCreateS("1");
|
||||
FF_STRBUF_AUTO_DESTROY minorBuf = ffStrbufCreateS("2");
|
||||
FF_STRBUF_AUTO_DESTROY patchBuf = ffStrbufCreateS("3");
|
||||
ffParseSemver(&result, &majorBuf, &minorBuf, &patchBuf);
|
||||
VERIFY(ffStrbufEqualS(&result, "v1.2.3"));
|
||||
}
|
||||
|
||||
// ffParseGTK: the three versions are folded into one line, and every combination of present and
|
||||
// equal versions has its own suffix.
|
||||
{
|
||||
// All three present
|
||||
VERIFY_GTK("1.0", "1.0", "1.0", "1.0 [GTK2/3/4]");
|
||||
VERIFY_GTK("2.0", "2.0", "3.0", "2.0 [GTK2/3], 3.0 [GTK4]");
|
||||
VERIFY_GTK("2.0", "3.0", "3.0", "2.0 [GTK2], 3.0 [GTK3/4]");
|
||||
VERIFY_GTK("2.0", "3.0", "4.0", "2.0 [GTK2], 3.0 [GTK3], 4.0 [GTK4]");
|
||||
|
||||
// GTK2 and GTK4 equal while GTK3 differs is not special cased: it falls back to the plain
|
||||
// three-way listing, so the same version is printed twice with different suffixes.
|
||||
VERIFY_GTK("2.0", "3.0", "2.0", "2.0 [GTK2], 3.0 [GTK3], 2.0 [GTK4]");
|
||||
|
||||
// The comparison is case insensitive, but the value printed is the one from the buffer that
|
||||
// the branch selected -- here the third one, not the first.
|
||||
VERIFY_GTK("ABC", "abc", "aBc", "aBc [GTK2/3/4]");
|
||||
VERIFY_GTK("2.0", "2.0", "3.0", "2.0 [GTK2/3], 3.0 [GTK4]");
|
||||
|
||||
// Two present
|
||||
VERIFY_GTK("1.0", "1.0", "", "1.0 [GTK2/3]");
|
||||
VERIFY_GTK("2.0", "3.0", "", "2.0 [GTK2], 3.0 [GTK3]");
|
||||
VERIFY_GTK("1.0", "", "1.0", "1.0 [GTK2/4]");
|
||||
VERIFY_GTK("2.0", "", "4.0", "2.0 [GTK2], 4.0 [GTK4]");
|
||||
VERIFY_GTK("", "1.0", "1.0", "1.0 [GTK3/4]");
|
||||
VERIFY_GTK("", "3.0", "4.0", "3.0 [GTK3], 4.0 [GTK4]");
|
||||
|
||||
// One present
|
||||
VERIFY_GTK("2.0", "", "", "2.0 [GTK2]");
|
||||
VERIFY_GTK("", "3.0", "", "3.0 [GTK3]");
|
||||
VERIFY_GTK("", "", "4.0", "4.0 [GTK4]");
|
||||
|
||||
// None present
|
||||
VERIFY_GTK("", "", "", "");
|
||||
}
|
||||
|
||||
puts("\033[32mAll tests passed!" FASTFETCH_TEXT_MODIFIER_RESET);
|
||||
return 0;
|
||||
}
|
||||
+216
@@ -0,0 +1,216 @@
|
||||
#include "common/option.h"
|
||||
#include "common/percent.h"
|
||||
#include "common/textModifier.h"
|
||||
#include "fastfetch.h"
|
||||
|
||||
#include <float.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static FFModuleArgs module;
|
||||
|
||||
static void verifyNum(double percent, FFPercentageModuleConfig config, bool parentheses, const char* expected, int lineNo) {
|
||||
FF_STRBUF_AUTO_DESTROY result = ffStrbufCreate();
|
||||
ffPercentAppendNum(&result, percent, config, parentheses, &module);
|
||||
|
||||
if (!ffStrbufEqualS(&result, expected)) {
|
||||
fprintf(stderr, FASTFETCH_TEXT_MODIFIER_ERROR "[%d] ffPercentAppendNum(%f, {%u, %u, %u}, %s): expected \"%s\", got \"%s\"\n" FASTFETCH_TEXT_MODIFIER_RESET,
|
||||
lineNo, percent, config.green, config.yellow, (unsigned) config.type, parentheses ? "true" : "false", expected, result.chars);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
#define VERIFY_NUM(percent, config, parentheses, expected) verifyNum((percent), (config), (parentheses), (expected), __LINE__)
|
||||
|
||||
static void verifyBar(double percent, FFPercentageModuleConfig config, const char* expected, int lineNo) {
|
||||
FF_STRBUF_AUTO_DESTROY result = ffStrbufCreate();
|
||||
ffPercentAppendBar(&result, percent, config, &module);
|
||||
|
||||
if (!ffStrbufEqualS(&result, expected)) {
|
||||
fprintf(stderr, FASTFETCH_TEXT_MODIFIER_ERROR "[%d] ffPercentAppendBar(%f, {%u, %u, %u}): expected \"%s\", got \"%s\"\n" FASTFETCH_TEXT_MODIFIER_RESET,
|
||||
lineNo, percent, config.green, config.yellow, (unsigned) config.type, expected, result.chars);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
#define VERIFY_BAR(percent, config, expected) verifyBar((percent), (config), (expected), __LINE__)
|
||||
|
||||
int main(void) {
|
||||
// Use the real defaults rather than a hand built config, so a changed default is caught here too
|
||||
ffOptionsInitDisplay(&instance.config.display);
|
||||
FFOptionsDisplay* options = &instance.config.display;
|
||||
ffOptionInitModuleArg(&module, "");
|
||||
|
||||
// Pin the threshold colors: the yellow and red defaults depend on whether the terminal is
|
||||
// detected as a light theme, which is not something a test should depend on.
|
||||
ffStrbufSetS(&options->percentColorGreen, "32");
|
||||
ffStrbufSetS(&options->percentColorYellow, "33");
|
||||
ffStrbufSetS(&options->percentColorRed, "31");
|
||||
|
||||
// The plain number. `pipe` disables the color, which is what the first group of cases checks.
|
||||
{
|
||||
options->pipe = true;
|
||||
const FFPercentageModuleConfig config = { .green = 50, .yellow = 80, .type = FF_PERCENTAGE_TYPE_NUM_BIT | FF_PERCENTAGE_TYPE_NUM_COLOR_BIT };
|
||||
|
||||
VERIFY_NUM(0, config, false, "0%");
|
||||
VERIFY_NUM(50, config, false, "50%");
|
||||
VERIFY_NUM(100, config, false, "100%");
|
||||
VERIFY_NUM(99.9, config, false, "100%"); // the default is zero digits, so this rounds
|
||||
|
||||
// An unset percentage prints as a bare dash, without a percent sign
|
||||
VERIFY_NUM(-DBL_MAX, config, false, "-");
|
||||
|
||||
// Parentheses wrap the whole thing, color included
|
||||
VERIFY_NUM(0, config, true, "(0%)");
|
||||
VERIFY_NUM(-DBL_MAX, config, true, "(-)");
|
||||
}
|
||||
|
||||
// percentNdigits
|
||||
{
|
||||
options->pipe = true;
|
||||
options->percentNdigits = 2;
|
||||
const FFPercentageModuleConfig config = { .green = 50, .yellow = 80, .type = FF_PERCENTAGE_TYPE_NUM_BIT };
|
||||
|
||||
VERIFY_NUM(0, config, false, "0.00%");
|
||||
VERIFY_NUM(50, config, false, "50.00%");
|
||||
VERIFY_NUM(33.333, config, false, "33.33%");
|
||||
VERIFY_NUM(100, config, false, "100.00%");
|
||||
VERIFY_NUM(-DBL_MAX, config, false, "-");
|
||||
|
||||
options->percentNdigits = 0;
|
||||
}
|
||||
|
||||
// percentWidth pads on the left, and the percent sign is not part of the padded field
|
||||
{
|
||||
options->pipe = true;
|
||||
options->percentWidth = 5;
|
||||
const FFPercentageModuleConfig config = { .green = 50, .yellow = 80, .type = FF_PERCENTAGE_TYPE_NUM_BIT };
|
||||
|
||||
VERIFY_NUM(0, config, false, " 0%");
|
||||
VERIFY_NUM(50, config, false, " 50%");
|
||||
VERIFY_NUM(100, config, false, " 100%");
|
||||
|
||||
options->percentWidth = 0;
|
||||
}
|
||||
|
||||
// Only `always` adds a space before the percent sign
|
||||
{
|
||||
options->pipe = true;
|
||||
const FFPercentageModuleConfig config = { .green = 50, .yellow = 80, .type = FF_PERCENTAGE_TYPE_NUM_BIT };
|
||||
|
||||
options->percentSpaceBeforeUnit = FF_SPACE_BEFORE_UNIT_ALWAYS;
|
||||
VERIFY_NUM(50, config, false, "50 %");
|
||||
|
||||
options->percentSpaceBeforeUnit = FF_SPACE_BEFORE_UNIT_NEVER;
|
||||
VERIFY_NUM(50, config, false, "50%");
|
||||
|
||||
options->percentSpaceBeforeUnit = FF_SPACE_BEFORE_UNIT_DEFAULT;
|
||||
VERIFY_NUM(50, config, false, "50%");
|
||||
}
|
||||
|
||||
// The color is picked by comparing against the thresholds, and each threshold belongs to the
|
||||
// lower band: exactly `green` is green, exactly `yellow` is yellow.
|
||||
{
|
||||
options->pipe = false;
|
||||
const FFPercentageModuleConfig config = { .green = 50, .yellow = 80, .type = FF_PERCENTAGE_TYPE_NUM_COLOR_BIT };
|
||||
|
||||
VERIFY_NUM(0, config, false, "\e[32m0%\e[m");
|
||||
VERIFY_NUM(50, config, false, "\e[32m50%\e[m");
|
||||
VERIFY_NUM(51, config, false, "\e[33m51%\e[m");
|
||||
VERIFY_NUM(79, config, false, "\e[33m79%\e[m");
|
||||
VERIFY_NUM(80, config, false, "\e[33m80%\e[m");
|
||||
VERIFY_NUM(81, config, false, "\e[31m81%\e[m");
|
||||
VERIFY_NUM(100, config, false, "\e[31m100%\e[m");
|
||||
|
||||
VERIFY_NUM(-DBL_MAX, config, false, "\e[90m-\e[m");
|
||||
VERIFY_NUM(-DBL_MAX, config, true, "(\e[90m-\e[m)");
|
||||
}
|
||||
|
||||
// When `green > yellow` the bands are inverted, so a low value is the alarming one
|
||||
{
|
||||
options->pipe = false;
|
||||
const FFPercentageModuleConfig config = { .green = 80, .yellow = 50, .type = FF_PERCENTAGE_TYPE_NUM_COLOR_BIT };
|
||||
|
||||
VERIFY_NUM(0, config, false, "\e[31m0%\e[m");
|
||||
VERIFY_NUM(49, config, false, "\e[31m49%\e[m");
|
||||
VERIFY_NUM(50, config, false, "\e[33m50%\e[m");
|
||||
VERIFY_NUM(79, config, false, "\e[33m79%\e[m");
|
||||
VERIFY_NUM(80, config, false, "\e[32m80%\e[m");
|
||||
VERIFY_NUM(100, config, false, "\e[32m100%\e[m");
|
||||
}
|
||||
|
||||
// Without the color flag the number is never colored, even when colors are enabled
|
||||
{
|
||||
options->pipe = false;
|
||||
const FFPercentageModuleConfig config = { .green = 50, .yellow = 80, .type = FF_PERCENTAGE_TYPE_NUM_BIT };
|
||||
|
||||
VERIFY_NUM(0, config, false, "0%");
|
||||
VERIFY_NUM(100, config, false, "100%");
|
||||
}
|
||||
|
||||
// A zero `type` means "use the global default" instead of "no flags"
|
||||
{
|
||||
options->pipe = false;
|
||||
FFPercentageModuleConfig config = { .green = 50, .yellow = 80, .type = 0 };
|
||||
|
||||
options->percentType = FF_PERCENTAGE_TYPE_NUM_BIT;
|
||||
VERIFY_NUM(100, config, false, "100%");
|
||||
|
||||
options->percentType = FF_PERCENTAGE_TYPE_NUM_BIT | FF_PERCENTAGE_TYPE_NUM_COLOR_BIT;
|
||||
VERIFY_NUM(100, config, false, "\e[31m100%\e[m");
|
||||
}
|
||||
|
||||
// The bar fills `percent` of the width, rounding to the nearest block. Colors are left to the
|
||||
// `pipe` free cases above; the block characters and the borders are what is checked here.
|
||||
{
|
||||
options->pipe = true;
|
||||
const FFPercentageModuleConfig config = { .green = 50, .yellow = 80, .type = 0 };
|
||||
|
||||
options->barWidth = 10;
|
||||
VERIFY_BAR(0, config, "[ ---------- ]");
|
||||
VERIFY_BAR(50, config, "[ ■■■■■----- ]");
|
||||
VERIFY_BAR(100, config, "[ ■■■■■■■■■■ ]");
|
||||
|
||||
// An unset percentage fills the bar with the "total" character, same as zero
|
||||
VERIFY_BAR(-DBL_MAX, config, "[ ---------- ]");
|
||||
|
||||
// The half block is rounded up at exactly 0.5
|
||||
VERIFY_BAR(4, config, "[ ---------- ]");
|
||||
VERIFY_BAR(5, config, "[ ■--------- ]");
|
||||
VERIFY_BAR(14, config, "[ ■--------- ]");
|
||||
VERIFY_BAR(15, config, "[ ■■-------- ]");
|
||||
|
||||
options->barWidth = 4;
|
||||
VERIFY_BAR(0, config, "[ ---- ]");
|
||||
VERIFY_BAR(25, config, "[ ■--- ]");
|
||||
VERIFY_BAR(50, config, "[ ■■-- ]");
|
||||
VERIFY_BAR(100, config, "[ ■■■■ ]");
|
||||
|
||||
// The borders are configurable and may be removed entirely
|
||||
ffStrbufSetS(&options->barBorderLeft, "");
|
||||
ffStrbufSetS(&options->barBorderRight, "");
|
||||
VERIFY_BAR(50, config, "■■--");
|
||||
|
||||
ffStrbufSetS(&options->barBorderLeft, "<");
|
||||
ffStrbufSetS(&options->barBorderRight, ">");
|
||||
VERIFY_BAR(50, config, "<■■-->");
|
||||
|
||||
ffStrbufSetS(&options->barBorderLeft, "[ ");
|
||||
ffStrbufSetS(&options->barBorderRight, " ]");
|
||||
|
||||
// The bar characters themselves are configurable
|
||||
ffStrbufSetS(&options->barCharElapsed, "#");
|
||||
ffStrbufSetS(&options->barCharTotal, ".");
|
||||
VERIFY_BAR(50, config, "[ ##.. ]");
|
||||
ffStrbufSetS(&options->barCharElapsed, "■");
|
||||
ffStrbufSetS(&options->barCharTotal, "-");
|
||||
|
||||
options->barWidth = 10;
|
||||
}
|
||||
|
||||
ffOptionDestroyModuleArg(&module);
|
||||
ffOptionsDestroyDisplay(options);
|
||||
|
||||
puts("\033[32mAll tests passed!" FASTFETCH_TEXT_MODIFIER_RESET);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
#include "common/properties.h"
|
||||
#include "common/textModifier.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static void verify(bool expression, const char* expressionStr, int lineNo) {
|
||||
if (expression) {
|
||||
return;
|
||||
}
|
||||
|
||||
fprintf(stderr, FASTFETCH_TEXT_MODIFIER_ERROR "[%d] %s\n" FASTFETCH_TEXT_MODIFIER_RESET, lineNo, expressionStr);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
#define VERIFY(expression) verify((expression), #expression, __LINE__)
|
||||
|
||||
static void verifyLine(const char* line, const char* start, bool expectedResult, const char* expectedValue, int lineNo) {
|
||||
FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate();
|
||||
const char* cursor = line;
|
||||
const bool result = ffParsePropLinePointer(&cursor, start, &buffer);
|
||||
|
||||
if (result != expectedResult || (result && !ffStrbufEqualS(&buffer, expectedValue))) {
|
||||
fprintf(stderr, FASTFETCH_TEXT_MODIFIER_ERROR "[%d] ffParsePropLine(\"%s\", \"%s\"): expected %s \"%s\", got %s \"%s\"\n" FASTFETCH_TEXT_MODIFIER_RESET,
|
||||
lineNo, line, start, expectedResult ? "true" : "false", expectedResult ? expectedValue : "",
|
||||
result ? "true" : "false", result ? buffer.chars : "");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
#define VERIFY_LINE(...) verifyLine(__VA_ARGS__, __LINE__)
|
||||
|
||||
static void verifyLines(const char* lines, const char* start, bool expectedResult, const char* expectedValue, int lineNo) {
|
||||
FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate();
|
||||
const bool result = ffParsePropLines(lines, start, &buffer);
|
||||
|
||||
if (result != expectedResult || (result && !ffStrbufEqualS(&buffer, expectedValue))) {
|
||||
fprintf(stderr, FASTFETCH_TEXT_MODIFIER_ERROR "[%d] ffParsePropLines(\"%s\", \"%s\"): expected %s \"%s\", got %s \"%s\"\n" FASTFETCH_TEXT_MODIFIER_RESET,
|
||||
lineNo, lines, start, expectedResult ? "true" : "false", expectedResult ? expectedValue : "",
|
||||
result ? "true" : "false", result ? buffer.chars : "");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
#define VERIFY_LINES(...) verifyLines(__VA_ARGS__, __LINE__)
|
||||
|
||||
int main(void) {
|
||||
// The plain case: `key: value`, with the value running to the end of the line.
|
||||
{
|
||||
VERIFY_LINE("Name: foo", "Name:", true, "foo");
|
||||
VERIFY_LINE("Name: foo\n", "Name:", true, "foo");
|
||||
VERIFY_LINE("Name: foo\nBar: baz", "Name:", true, "foo"); // stops at the newline
|
||||
VERIFY_LINE("Name:foo", "Name:", true, "foo");
|
||||
VERIFY_LINE("Name: ", "Name:", true, ""); // found, but the value is empty
|
||||
VERIFY_LINE("Name:", "Name:", true, "");
|
||||
VERIFY_LINE("Name: a:b", "Name:", true, "a:b"); // only the first colon is the separator
|
||||
VERIFY_LINE("Name: foo bar", "Name:", true, "foo bar"); // spaces inside the value are kept
|
||||
}
|
||||
|
||||
// Any amount of whitespace in the format matches any amount of whitespace in the line, including
|
||||
// none -- the two sides are independent.
|
||||
{
|
||||
VERIFY_LINE("Name: foo", "Name:", true, "foo");
|
||||
VERIFY_LINE("Name:foo", "Name: ", true, "foo");
|
||||
VERIFY_LINE("Name: foo", "Name: ", true, "foo");
|
||||
VERIFY_LINE("Name:\tfoo", "Name: ", true, "foo");
|
||||
VERIFY_LINE("Name: foo", "Name:\t", true, "foo");
|
||||
VERIFY_LINE(" \t Name: foo", "Name:", true, "foo"); // leading whitespace of the line is skipped
|
||||
}
|
||||
|
||||
// A mismatch anywhere in the key makes the whole line fail, and the key must match in full.
|
||||
{
|
||||
VERIFY_LINE("Name: foo", "Value:", false, "");
|
||||
VERIFY_LINE("name: foo", "Name:", true, "foo"); // the key is compared case insensitively
|
||||
VERIFY_LINE("NAME: foo", "name:", true, "foo");
|
||||
VERIFY_LINE("nAmE: foo", "NAME:", true, "foo");
|
||||
VERIFY_LINE("NameX: foo", "Name:", false, "");
|
||||
VERIFY_LINE("Name :foo", "Name:", false, "");
|
||||
VERIFY_LINE("Nam", "Name:", false, ""); // the line ends before the key does
|
||||
VERIFY_LINE("", "Name:", false, "");
|
||||
VERIFY_LINE("\n", "Name:", false, "");
|
||||
VERIFY_LINE("Name", "Name:", false, "");
|
||||
}
|
||||
|
||||
// Trailing spaces are trimmed, but nothing else is: a trailing tab survives.
|
||||
{
|
||||
VERIFY_LINE("Name: foo ", "Name:", true, "foo");
|
||||
VERIFY_LINE("Name: foo\t", "Name:", true, "foo\t");
|
||||
VERIFY_LINE("Name: foo \n", "Name:", true, "foo");
|
||||
}
|
||||
|
||||
// A quoted value ends at its closing quote, so trailing text is not part of the value.
|
||||
{
|
||||
VERIFY_LINE("Name: \"foo bar\"", "Name:", true, "foo bar");
|
||||
VERIFY_LINE("Name: 'foo bar'", "Name:", true, "foo bar");
|
||||
VERIFY_LINE("Name: \"foo\" trailing", "Name:", true, "foo");
|
||||
VERIFY_LINE("Name: \"\"", "Name:", true, "");
|
||||
VERIFY_LINE("Name: \"foo", "Name:", true, "foo"); // unterminated quote: the line end closes it
|
||||
|
||||
// The trim still applies, and it only removes the right hand side
|
||||
VERIFY_LINE("Name: \" foo \"", "Name:", true, " foo");
|
||||
}
|
||||
|
||||
// A key ending in `>` switches the parser to XML mode, where the value ends at `<` instead of at
|
||||
// the end of the line.
|
||||
{
|
||||
VERIFY_LINE("<name>foo</name>", "<name>", true, "foo");
|
||||
VERIFY_LINE("<name></name>", "<name>", true, "");
|
||||
VERIFY_LINE("<name>foo", "<name>", true, "foo"); // no closing tag
|
||||
VERIFY_LINE("<name>foo bar</name>", "<name>", true, "foo bar");
|
||||
VERIFY_LINE("<name> foo</name>", "<name>", true, "foo");
|
||||
VERIFY_LINE("<name>foo\n", "<name>", true, "foo");
|
||||
}
|
||||
|
||||
// ffParsePropLines walks the lines until the key is found, and the first match wins -- including
|
||||
// when that match has an empty value.
|
||||
{
|
||||
VERIFY_LINES("A: 1\nName: foo\nB: 2", "Name:", true, "foo");
|
||||
VERIFY_LINES("A: 1\nName: foo", "Name:", true, "foo");
|
||||
VERIFY_LINES("Name: foo", "Name:", true, "foo");
|
||||
VERIFY_LINES("\n\nName: foo", "Name:", true, "foo");
|
||||
VERIFY_LINES("Name: foo\nName: bar", "Name:", true, "foo");
|
||||
VERIFY_LINES("Name:\nName: bar", "Name:", true, "");
|
||||
VERIFY_LINES("A: 1\nB: 2", "Name:", false, "");
|
||||
VERIFY_LINES("A: 1\nB: 2\n", "Name:", false, "");
|
||||
VERIFY_LINES("", "Name:", false, "");
|
||||
}
|
||||
|
||||
// The value is appended to the buffer, so a caller can collect several keys into one string.
|
||||
{
|
||||
FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreateS("pre");
|
||||
VERIFY(ffParsePropLines("Name: foo", "Name:", &buffer));
|
||||
VERIFY(ffStrbufEqualS(&buffer, "prefoo"));
|
||||
}
|
||||
|
||||
puts("\033[32mAll tests passed!" FASTFETCH_TEXT_MODIFIER_RESET);
|
||||
return 0;
|
||||
}
|
||||
+142
@@ -0,0 +1,142 @@
|
||||
#include "common/size.h"
|
||||
#include "common/textModifier.h"
|
||||
#include "fastfetch.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static void verify(uint64_t bytes, const char* expected, int lineNo) {
|
||||
FF_STRBUF_AUTO_DESTROY result = ffStrbufCreate();
|
||||
ffSizeAppendNum(bytes, &result);
|
||||
|
||||
if (!ffStrbufEqualS(&result, expected)) {
|
||||
fprintf(stderr, FASTFETCH_TEXT_MODIFIER_ERROR "[%d] ffSizeAppendNum(%llu): expected \"%s\", got \"%s\"\n" FASTFETCH_TEXT_MODIFIER_RESET,
|
||||
lineNo, (unsigned long long) bytes, expected, result.chars);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
#define VERIFY_SIZE(bytes, expected) verify((bytes), (expected), __LINE__)
|
||||
|
||||
int main(void) {
|
||||
// Use the real defaults rather than a hand built config, so a changed default is caught here too
|
||||
ffOptionsInitDisplay(&instance.config.display);
|
||||
FFOptionsDisplay* options = &instance.config.display;
|
||||
|
||||
// IEC (the default): 1024 based, with the `KiB` style suffixes
|
||||
{
|
||||
VERIFY_SIZE(0, "0 B");
|
||||
VERIFY_SIZE(1, "1 B");
|
||||
VERIFY_SIZE(999, "999 B");
|
||||
VERIFY_SIZE(1023, "1023 B"); // one byte short of the first prefix
|
||||
VERIFY_SIZE(1024, "1.00 KiB");
|
||||
VERIFY_SIZE(1025, "1.00 KiB"); // below the rounding precision
|
||||
VERIFY_SIZE(1536, "1.50 KiB");
|
||||
VERIFY_SIZE(1024ULL * 1024, "1.00 MiB");
|
||||
VERIFY_SIZE(1024ULL * 1024 * 1024, "1.00 GiB");
|
||||
VERIFY_SIZE(1024ULL * 1024 * 1024 * 1024, "1.00 TiB");
|
||||
VERIFY_SIZE(1024ULL * 1024 * 1024 * 1024 * 1024, "1.00 PiB");
|
||||
VERIFY_SIZE(1024ULL * 1024 * 1024 * 1024 * 1024 * 1024, "1.00 EiB");
|
||||
|
||||
// The largest 64 bit value is 16 EiB; `ZiB` and `YiB` are listed but can never be reached
|
||||
// through an IEC conversion of a `uint64_t`.
|
||||
VERIFY_SIZE(UINT64_MAX, "16.00 EiB");
|
||||
}
|
||||
|
||||
// SI: 1000 based, with the `kB` style suffixes
|
||||
{
|
||||
options->sizeBinaryPrefix = FF_SIZE_BINARY_PREFIX_TYPE_SI;
|
||||
|
||||
VERIFY_SIZE(0, "0 B");
|
||||
VERIFY_SIZE(999, "999 B");
|
||||
VERIFY_SIZE(1000, "1.00 kB");
|
||||
VERIFY_SIZE(1500, "1.50 kB");
|
||||
VERIFY_SIZE(1000000, "1.00 MB");
|
||||
VERIFY_SIZE(1000000000ULL, "1.00 GB");
|
||||
VERIFY_SIZE(1000000000000ULL, "1.00 TB");
|
||||
VERIFY_SIZE(1000000000000000ULL, "1.00 PB");
|
||||
VERIFY_SIZE(1000000000000000000ULL, "1.00 EB");
|
||||
VERIFY_SIZE(UINT64_MAX, "18.45 EB");
|
||||
}
|
||||
|
||||
// JEDEC: 1024 based, but with the `KB` style suffixes. The suffix list stops at `TB`, so the
|
||||
// loop has to stop there as well instead of reading past the end.
|
||||
{
|
||||
options->sizeBinaryPrefix = FF_SIZE_BINARY_PREFIX_TYPE_JEDEC;
|
||||
|
||||
VERIFY_SIZE(1023, "1023 B");
|
||||
VERIFY_SIZE(1024, "1.00 KB");
|
||||
VERIFY_SIZE(1024ULL * 1024, "1.00 MB");
|
||||
VERIFY_SIZE(1024ULL * 1024 * 1024, "1.00 GB");
|
||||
VERIFY_SIZE(1024ULL * 1024 * 1024 * 1024, "1.00 TB");
|
||||
VERIFY_SIZE(1024ULL * 1024 * 1024 * 1024 * 1024, "1024.00 TB"); // no `PB` in JEDEC
|
||||
VERIFY_SIZE(UINT64_MAX, "16777216.00 TB");
|
||||
}
|
||||
|
||||
options->sizeBinaryPrefix = FF_SIZE_BINARY_PREFIX_TYPE_IEC;
|
||||
|
||||
// sizeNdigits: the number of digits after the decimal point. It applies only once a prefix is
|
||||
// used -- a plain byte count is always printed as an integer.
|
||||
{
|
||||
options->sizeNdigits = 0;
|
||||
VERIFY_SIZE(1024, "1 KiB");
|
||||
VERIFY_SIZE(1536, "2 KiB"); // rounded, not truncated
|
||||
VERIFY_SIZE(512, "512 B");
|
||||
VERIFY_SIZE(1023, "1023 B");
|
||||
|
||||
options->sizeNdigits = 4;
|
||||
VERIFY_SIZE(1024, "1.0000 KiB");
|
||||
VERIFY_SIZE(1536, "1.5000 KiB");
|
||||
VERIFY_SIZE(512, "512 B"); // still an integer
|
||||
VERIFY_SIZE(0, "0 B");
|
||||
|
||||
options->sizeNdigits = 2;
|
||||
}
|
||||
|
||||
// sizeMaxPrefix: the index of the largest suffix that may be used. `0` disables the conversion
|
||||
// entirely.
|
||||
{
|
||||
options->sizeMaxPrefix = 0;
|
||||
VERIFY_SIZE(0, "0 B");
|
||||
VERIFY_SIZE(1024, "1024 B");
|
||||
VERIFY_SIZE(UINT64_MAX, "18446744073709551615 B");
|
||||
|
||||
options->sizeMaxPrefix = 1;
|
||||
VERIFY_SIZE(1024, "1.00 KiB");
|
||||
VERIFY_SIZE(1024ULL * 1024, "1024.00 KiB");
|
||||
VERIFY_SIZE(1024ULL * 1024 * 1024, "1048576.00 KiB");
|
||||
|
||||
options->sizeMaxPrefix = 8;
|
||||
}
|
||||
|
||||
// sizeSpaceBeforeUnit: only `never` removes the space, `default` and `always` both keep it
|
||||
{
|
||||
options->sizeSpaceBeforeUnit = FF_SPACE_BEFORE_UNIT_NEVER;
|
||||
VERIFY_SIZE(0, "0B");
|
||||
VERIFY_SIZE(1024, "1.00KiB");
|
||||
VERIFY_SIZE(1536, "1.50KiB");
|
||||
|
||||
options->sizeSpaceBeforeUnit = FF_SPACE_BEFORE_UNIT_ALWAYS;
|
||||
VERIFY_SIZE(0, "0 B");
|
||||
VERIFY_SIZE(1024, "1.00 KiB");
|
||||
|
||||
options->sizeSpaceBeforeUnit = FF_SPACE_BEFORE_UNIT_DEFAULT;
|
||||
VERIFY_SIZE(1024, "1.00 KiB");
|
||||
}
|
||||
|
||||
// The result is appended to the buffer instead of replacing it
|
||||
{
|
||||
FF_STRBUF_AUTO_DESTROY result = ffStrbufCreateS("Size: ");
|
||||
ffSizeAppendNum(1024, &result);
|
||||
if (!ffStrbufEqualS(&result, "Size: 1.00 KiB")) {
|
||||
fprintf(stderr, FASTFETCH_TEXT_MODIFIER_ERROR "[%d] ffSizeAppendNum did not append: got \"%s\"\n" FASTFETCH_TEXT_MODIFIER_RESET, __LINE__, result.chars);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
ffOptionsDestroyDisplay(options);
|
||||
|
||||
puts("\033[32mAll tests passed!" FASTFETCH_TEXT_MODIFIER_RESET);
|
||||
return 0;
|
||||
}
|
||||
+163
-1
@@ -17,6 +17,168 @@ static void verify(bool expression, const char* expressionStr, int lineNo) {
|
||||
#define VERIFY(expression) verify((expression), #expression, __LINE__)
|
||||
|
||||
int main(void) {
|
||||
// The plain C string helpers. These have nothing to do with wcwidth, so they run in every build.
|
||||
{
|
||||
VERIFY(!ffStrSet(nullptr));
|
||||
VERIFY(!ffStrSet(""));
|
||||
VERIFY(!ffStrSet(" "));
|
||||
VERIFY(!ffStrSet(" \t \n"));
|
||||
VERIFY(ffStrSet("a"));
|
||||
VERIFY(ffStrSet(" a"));
|
||||
VERIFY(ffStrSet("a "));
|
||||
}
|
||||
|
||||
{
|
||||
VERIFY(ffStrStartsWith("", ""));
|
||||
VERIFY(ffStrStartsWith("abc", ""));
|
||||
VERIFY(ffStrStartsWith("abc", "a"));
|
||||
VERIFY(ffStrStartsWith("abc", "abc"));
|
||||
VERIFY(!ffStrStartsWith("abc", "abcd")); // the needle is longer than the haystack
|
||||
VERIFY(!ffStrStartsWith("abc", "b"));
|
||||
VERIFY(!ffStrStartsWith("", "a"));
|
||||
VERIFY(!ffStrStartsWith("Abc", "abc"));
|
||||
|
||||
VERIFY(ffStrStartsWithIgnCase("Abc", "abc"));
|
||||
VERIFY(ffStrStartsWithIgnCase("ABC", "abc"));
|
||||
VERIFY(ffStrStartsWithIgnCase("abc", ""));
|
||||
VERIFY(!ffStrStartsWithIgnCase("Abc", "abd"));
|
||||
}
|
||||
|
||||
{
|
||||
VERIFY(ffStrEndsWith("", ""));
|
||||
VERIFY(ffStrEndsWith("abc", ""));
|
||||
VERIFY(ffStrEndsWith("abc", "c"));
|
||||
VERIFY(ffStrEndsWith("abc", "abc"));
|
||||
VERIFY(!ffStrEndsWith("abc", "abcd")); // the needle is longer than the haystack
|
||||
VERIFY(!ffStrEndsWith("abc", "b"));
|
||||
VERIFY(!ffStrEndsWith("", "a"));
|
||||
VERIFY(!ffStrEndsWith("abc", "Abc"));
|
||||
|
||||
VERIFY(ffStrEndsWithIgnCase("abc", "BC"));
|
||||
VERIFY(ffStrEndsWithIgnCase("abc", ""));
|
||||
VERIFY(!ffStrEndsWithIgnCase("abc", "BD"));
|
||||
}
|
||||
|
||||
{
|
||||
VERIFY(ffStrEquals("", ""));
|
||||
VERIFY(ffStrEquals("abc", "abc"));
|
||||
VERIFY(!ffStrEquals("abc", "abcd"));
|
||||
VERIFY(!ffStrEquals("abc", "ABC"));
|
||||
|
||||
VERIFY(ffStrEqualsIgnCase("abc", "ABC"));
|
||||
VERIFY(ffStrEqualsIgnCase("", ""));
|
||||
VERIFY(!ffStrEqualsIgnCase("abc", "abd"));
|
||||
|
||||
VERIFY(ffStrContains("abc", ""));
|
||||
VERIFY(ffStrContains("abc", "b"));
|
||||
VERIFY(ffStrContains("abc", "abc"));
|
||||
VERIFY(!ffStrContains("abc", "d"));
|
||||
|
||||
VERIFY(ffStrContainsIgnCase("abc", "B"));
|
||||
VERIFY(!ffStrContainsIgnCase("abc", "D"));
|
||||
// `strcasestr` reports an empty needle as a match, but the `StrStrIA` it is aliased to on
|
||||
// Windows reports no match, so on Windows this disagrees with `ffStrContains`.
|
||||
#ifndef _WIN32
|
||||
VERIFY(ffStrContainsIgnCase("abc", ""));
|
||||
#endif
|
||||
|
||||
VERIFY(ffStrContainsC("abc", 'b'));
|
||||
VERIFY(!ffStrContainsC("abc", 'd'));
|
||||
VERIFY(ffStrContainsC("abc", '\0')); // `strchr` reports the terminator as a match
|
||||
}
|
||||
|
||||
{
|
||||
VERIFY(ffCharIsEnglishAlphabet('a'));
|
||||
VERIFY(ffCharIsEnglishAlphabet('z'));
|
||||
VERIFY(ffCharIsEnglishAlphabet('A'));
|
||||
VERIFY(ffCharIsEnglishAlphabet('Z'));
|
||||
VERIFY(!ffCharIsEnglishAlphabet('0'));
|
||||
VERIFY(!ffCharIsEnglishAlphabet('_'));
|
||||
VERIFY(!ffCharIsEnglishAlphabet(' '));
|
||||
|
||||
VERIFY(ffCharIsDigit('0'));
|
||||
VERIFY(ffCharIsDigit('9'));
|
||||
VERIFY(!ffCharIsDigit('/')); // one below '0'
|
||||
VERIFY(!ffCharIsDigit(':')); // one above '9'
|
||||
VERIFY(!ffCharIsDigit('a'));
|
||||
|
||||
VERIFY(ffCharIsHexDigit('0'));
|
||||
VERIFY(ffCharIsHexDigit('9'));
|
||||
VERIFY(ffCharIsHexDigit('a'));
|
||||
VERIFY(ffCharIsHexDigit('F'));
|
||||
VERIFY(!ffCharIsHexDigit('g'));
|
||||
VERIFY(!ffCharIsHexDigit('G'));
|
||||
VERIFY(!ffCharIsHexDigit('/'));
|
||||
VERIFY(!ffCharIsHexDigit('@'));
|
||||
}
|
||||
|
||||
{
|
||||
VERIFY(ffHexCharToInt('0') == 0);
|
||||
VERIFY(ffHexCharToInt('9') == 9);
|
||||
VERIFY(ffHexCharToInt('a') == 10);
|
||||
VERIFY(ffHexCharToInt('f') == 15);
|
||||
VERIFY(ffHexCharToInt('A') == 10);
|
||||
VERIFY(ffHexCharToInt('F') == 15);
|
||||
VERIFY(ffHexCharToInt('g') == -1);
|
||||
VERIFY(ffHexCharToInt('G') == -1);
|
||||
VERIFY(ffHexCharToInt('/') == -1); // one below '0'
|
||||
VERIFY(ffHexCharToInt(':') == -1); // one above '9'
|
||||
VERIFY(ffHexCharToInt('@') == -1); // one below 'A'
|
||||
VERIFY(ffHexCharToInt('`') == -1); // one below 'a'
|
||||
VERIFY(ffHexCharToInt(' ') == -1);
|
||||
}
|
||||
|
||||
// ffStrCopy never writes more than `dstBufSiz` bytes and always terminates. It returns a pointer
|
||||
// to the end of the copy, so consecutive calls can be chained.
|
||||
{
|
||||
char buffer[8];
|
||||
|
||||
memset(buffer, 'X', sizeof(buffer));
|
||||
char* end = ffStrCopy(buffer, "abc", sizeof(buffer));
|
||||
VERIFY(ffStrEquals(buffer, "abc"));
|
||||
VERIFY(end == buffer + 3);
|
||||
|
||||
memset(buffer, 'X', sizeof(buffer));
|
||||
end = ffStrCopy(buffer, "abcdef", sizeof(buffer));
|
||||
VERIFY(ffStrEquals(buffer, "abcdef"));
|
||||
VERIFY(end == buffer + 6);
|
||||
|
||||
// Filling the buffer exactly still leaves room for the terminator
|
||||
char exact[4];
|
||||
memset(exact, 'X', sizeof(exact));
|
||||
end = ffStrCopy(exact, "abc", sizeof(exact));
|
||||
VERIFY(ffStrEquals(exact, "abc"));
|
||||
VERIFY(end == exact + 3);
|
||||
|
||||
// One byte short: the copy is truncated to `dstBufSiz - 1` bytes
|
||||
char truncated[3];
|
||||
memset(truncated, 'X', sizeof(truncated));
|
||||
end = ffStrCopy(truncated, "abcdef", sizeof(truncated));
|
||||
VERIFY(ffStrEquals(truncated, "ab"));
|
||||
VERIFY(end == truncated + 2);
|
||||
|
||||
// A one byte buffer holds nothing but the terminator
|
||||
char single[1];
|
||||
single[0] = 'X';
|
||||
end = ffStrCopy(single, "abc", sizeof(single));
|
||||
VERIFY(single[0] == '\0');
|
||||
VERIFY(end == single);
|
||||
|
||||
// A zero sized buffer is left completely alone
|
||||
char untouched[1] = { 'X' };
|
||||
end = ffStrCopy(untouched, "abc", 0);
|
||||
VERIFY(untouched[0] == 'X');
|
||||
VERIFY(end == untouched);
|
||||
|
||||
VERIFY(ffStrCopy(nullptr, "abc", 8) == nullptr);
|
||||
}
|
||||
|
||||
// FF_STR stringifies its argument
|
||||
{
|
||||
VERIFY(ffStrEquals(FF_STR(1), "1"));
|
||||
VERIFY(ffStrEquals(FF_STR(123), "123"));
|
||||
}
|
||||
|
||||
#if FF_ENABLE_WCWIDTH
|
||||
{
|
||||
uint8_t width = 255;
|
||||
@@ -102,7 +264,7 @@ int main(void) {
|
||||
|
||||
puts("\033[32mAll tests passed!" FASTFETCH_TEXT_MODIFIER_RESET);
|
||||
#else
|
||||
puts("\033[33mTests skipped because wcwidth support is disabled." FASTFETCH_TEXT_MODIFIER_RESET);
|
||||
puts("\033[33mwcwidth tests skipped because wcwidth support is disabled." FASTFETCH_TEXT_MODIFIER_RESET);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
+177
@@ -0,0 +1,177 @@
|
||||
#include "common/option.h"
|
||||
#include "common/temps.h"
|
||||
#include "common/textModifier.h"
|
||||
#include "fastfetch.h"
|
||||
|
||||
#include <float.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static FFModuleArgs module;
|
||||
|
||||
static void verify(double celsius, FFColorRangeConfig config, const char* expected, int lineNo) {
|
||||
FF_STRBUF_AUTO_DESTROY result = ffStrbufCreate();
|
||||
ffTempsAppendNum(celsius, &result, config, &module);
|
||||
|
||||
if (!ffStrbufEqualS(&result, expected)) {
|
||||
fprintf(stderr, FASTFETCH_TEXT_MODIFIER_ERROR "[%d] ffTempsAppendNum(%f, {%u, %u}): expected \"%s\", got \"%s\"\n" FASTFETCH_TEXT_MODIFIER_RESET,
|
||||
lineNo, celsius, config.green, config.yellow, expected, result.chars);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
#define VERIFY_TEMP(celsius, config, expected) verify((celsius), (config), (expected), __LINE__)
|
||||
|
||||
int main(void) {
|
||||
// Use the real defaults rather than a hand built config, so a changed default is caught here too
|
||||
ffOptionsInitDisplay(&instance.config.display);
|
||||
FFOptionsDisplay* options = &instance.config.display;
|
||||
ffOptionInitModuleArg(&module, "");
|
||||
|
||||
// Pin the threshold colors: the yellow and red defaults depend on whether the terminal is
|
||||
// detected as a light theme, which is not something a test should depend on.
|
||||
ffStrbufSetS(&options->tempColorGreen, "32");
|
||||
ffStrbufSetS(&options->tempColorYellow, "33");
|
||||
ffStrbufSetS(&options->tempColorRed, "31");
|
||||
|
||||
const FFColorRangeConfig config = { .green = 50, .yellow = 80 };
|
||||
|
||||
// An unknown temperature prints nothing at all, not even the unit
|
||||
{
|
||||
options->pipe = true;
|
||||
VERIFY_TEMP(-DBL_MAX, config, "");
|
||||
}
|
||||
|
||||
// The default unit is Celsius, with one digit after the point
|
||||
{
|
||||
options->pipe = true;
|
||||
VERIFY_TEMP(0, config, "0.0°C");
|
||||
VERIFY_TEMP(25.5, config, "25.5°C");
|
||||
VERIFY_TEMP(-10, config, "-10.0°C");
|
||||
VERIFY_TEMP(-273.15, config, "-273.1°C");
|
||||
}
|
||||
|
||||
// tempNdigits
|
||||
{
|
||||
options->pipe = true;
|
||||
options->tempNdigits = 0;
|
||||
VERIFY_TEMP(25.4, config, "25°C");
|
||||
VERIFY_TEMP(25.6, config, "26°C");
|
||||
VERIFY_TEMP(0, config, "0°C");
|
||||
|
||||
options->tempNdigits = 2;
|
||||
VERIFY_TEMP(25.5, config, "25.50°C");
|
||||
|
||||
options->tempNdigits = 1;
|
||||
}
|
||||
|
||||
// Celsius and Fahrenheit only take a space before the unit when it is forced on, while Kelvin
|
||||
// takes one by default. That asymmetry is deliberate and easy to break.
|
||||
{
|
||||
options->pipe = true;
|
||||
options->tempNdigits = 2; // so that 25.5 + 273.15 prints exactly
|
||||
|
||||
options->tempSpaceBeforeUnit = FF_SPACE_BEFORE_UNIT_DEFAULT;
|
||||
VERIFY_TEMP(25.5, config, "25.50°C");
|
||||
options->tempUnit = FF_TEMPERATURE_UNIT_KELVIN;
|
||||
VERIFY_TEMP(25.5, config, "298.65 K");
|
||||
options->tempUnit = FF_TEMPERATURE_UNIT_DEFAULT;
|
||||
|
||||
options->tempSpaceBeforeUnit = FF_SPACE_BEFORE_UNIT_NEVER;
|
||||
VERIFY_TEMP(25.5, config, "25.50°C");
|
||||
options->tempUnit = FF_TEMPERATURE_UNIT_KELVIN;
|
||||
VERIFY_TEMP(25.5, config, "298.65K");
|
||||
options->tempUnit = FF_TEMPERATURE_UNIT_DEFAULT;
|
||||
|
||||
options->tempSpaceBeforeUnit = FF_SPACE_BEFORE_UNIT_ALWAYS;
|
||||
VERIFY_TEMP(25.5, config, "25.50 °C");
|
||||
options->tempUnit = FF_TEMPERATURE_UNIT_KELVIN;
|
||||
VERIFY_TEMP(25.5, config, "298.65 K");
|
||||
options->tempUnit = FF_TEMPERATURE_UNIT_DEFAULT;
|
||||
|
||||
options->tempSpaceBeforeUnit = FF_SPACE_BEFORE_UNIT_DEFAULT;
|
||||
options->tempNdigits = 1;
|
||||
}
|
||||
|
||||
// Fahrenheit and Kelvin are conversions of the Celsius value that the thresholds were compared
|
||||
// against, so the unit changes what is printed but never which band is picked.
|
||||
{
|
||||
options->pipe = true;
|
||||
|
||||
options->tempUnit = FF_TEMPERATURE_UNIT_FAHRENHEIT;
|
||||
VERIFY_TEMP(0, config, "32.0°F");
|
||||
VERIFY_TEMP(100, config, "212.0°F");
|
||||
VERIFY_TEMP(37, config, "98.6°F");
|
||||
VERIFY_TEMP(-40, config, "-40.0°F"); // the one point where the two scales meet
|
||||
|
||||
options->tempNdigits = 2;
|
||||
options->tempUnit = FF_TEMPERATURE_UNIT_KELVIN;
|
||||
VERIFY_TEMP(0, config, "273.15 K");
|
||||
VERIFY_TEMP(100, config, "373.15 K");
|
||||
VERIFY_TEMP(-273.15, config, "0.00 K");
|
||||
options->tempNdigits = 1;
|
||||
|
||||
options->tempUnit = FF_TEMPERATURE_UNIT_DEFAULT;
|
||||
}
|
||||
|
||||
// Colors, with the same band rules as the percentage module: the threshold itself belongs to the
|
||||
// lower band, and the reset sequence is always emitted when colors are on.
|
||||
{
|
||||
options->pipe = false;
|
||||
|
||||
VERIFY_TEMP(0, config, "\e[32m0.0°C\e[m");
|
||||
VERIFY_TEMP(50, config, "\e[32m50.0°C\e[m");
|
||||
VERIFY_TEMP(51, config, "\e[33m51.0°C\e[m");
|
||||
VERIFY_TEMP(80, config, "\e[33m80.0°C\e[m");
|
||||
VERIFY_TEMP(81, config, "\e[31m81.0°C\e[m");
|
||||
VERIFY_TEMP(100, config, "\e[31m100.0°C\e[m");
|
||||
|
||||
// The color is chosen from the Celsius value, so a display unit that is not Celsius does not
|
||||
// move the threshold
|
||||
options->tempUnit = FF_TEMPERATURE_UNIT_FAHRENHEIT;
|
||||
VERIFY_TEMP(81, config, "\e[31m177.8°F\e[m");
|
||||
options->tempUnit = FF_TEMPERATURE_UNIT_DEFAULT;
|
||||
|
||||
// An unknown temperature has no color at all
|
||||
VERIFY_TEMP(-DBL_MAX, config, "");
|
||||
|
||||
// When `green > yellow` the bands are inverted, so a low value is the alarming one
|
||||
const FFColorRangeConfig inverted = { .green = 80, .yellow = 50 };
|
||||
VERIFY_TEMP(0, inverted, "\e[31m0.0°C\e[m");
|
||||
VERIFY_TEMP(49, inverted, "\e[31m49.0°C\e[m");
|
||||
VERIFY_TEMP(50, inverted, "\e[33m50.0°C\e[m");
|
||||
VERIFY_TEMP(79, inverted, "\e[33m79.0°C\e[m");
|
||||
VERIFY_TEMP(80, inverted, "\e[32m80.0°C\e[m");
|
||||
VERIFY_TEMP(100, inverted, "\e[32m100.0°C\e[m");
|
||||
}
|
||||
|
||||
// The color is restored to the module's output color, so the rest of the line keeps its own
|
||||
{
|
||||
options->pipe = false;
|
||||
ffStrbufSetS(&module.outputColor, "94");
|
||||
VERIFY_TEMP(0, config, "\e[32m0.0°C\e[m\e[94m");
|
||||
ffStrbufClear(&module.outputColor);
|
||||
|
||||
// ... and it falls back to the global output color when the module has none
|
||||
ffStrbufSetS(&options->colorOutput, "95");
|
||||
VERIFY_TEMP(0, config, "\e[32m0.0°C\e[m\e[95m");
|
||||
ffStrbufClear(&options->colorOutput);
|
||||
}
|
||||
|
||||
// The result is appended to the buffer instead of replacing it
|
||||
{
|
||||
options->pipe = true;
|
||||
FF_STRBUF_AUTO_DESTROY result = ffStrbufCreateS("Temp: ");
|
||||
ffTempsAppendNum(25.5, &result, config, &module);
|
||||
if (!ffStrbufEqualS(&result, "Temp: 25.5°C")) {
|
||||
fprintf(stderr, FASTFETCH_TEXT_MODIFIER_ERROR "[%d] ffTempsAppendNum did not append: got \"%s\"\n" FASTFETCH_TEXT_MODIFIER_RESET, __LINE__, result.chars);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
ffOptionDestroyModuleArg(&module);
|
||||
ffOptionsDestroyDisplay(options);
|
||||
|
||||
puts("\033[32mAll tests passed!" FASTFETCH_TEXT_MODIFIER_RESET);
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user