master ohmu/fastfetch-openbsd / tests / parsing.c
ohmu  ·  2026-10-15
  1#include "common/parsing.h"
  2#include "common/textModifier.h"
  3
  4#include <stdint.h>
  5#include <stdio.h>
  6#include <stdlib.h>
  7
  8static void verify(bool expression, const char* expressionStr, int lineNo) {
  9    if (expression) {
 10        return;
 11    }
 12
 13    fprintf(stderr, FASTFETCH_TEXT_MODIFIER_ERROR "[%d] %s\n" FASTFETCH_TEXT_MODIFIER_RESET, lineNo, expressionStr);
 14    exit(1);
 15}
 16
 17#define VERIFY(expression) verify((expression), #expression, __LINE__)
 18
 19static 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) {
 20    const FFVersion first = { major1, minor1, patch1 };
 21    const FFVersion second = { major2, minor2, patch2 };
 22
 23    const int8_t forward = ffVersionCompare(&first, &second);
 24    const int8_t backward = ffVersionCompare(&second, &first);
 25
 26    // Only the sign is meaningful to callers, but the comparison is documented to be antisymmetric,
 27    // so a caller that negates the result to get "greater" must not be surprised.
 28    if (forward != expected || backward != -expected) {
 29        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,
 30            lineNo, major1, minor1, patch1, major2, minor2, patch2, expected, -expected, forward, backward);
 31        exit(1);
 32    }
 33}
 34
 35#define VERIFY_COMPARE(...) verifyVersionCompare(__VA_ARGS__, __LINE__)
 36
 37static void verifyPretty(uint32_t major, uint32_t minor, uint32_t patch, const char* expected, int lineNo) {
 38    const FFVersion version = { major, minor, patch };
 39    FF_STRBUF_AUTO_DESTROY result = ffStrbufCreate();
 40    ffVersionToPretty(&version, &result);
 41
 42    if (!ffStrbufEqualS(&result, expected)) {
 43        fprintf(stderr, FASTFETCH_TEXT_MODIFIER_ERROR "[%d] ffVersionToPretty(%u.%u.%u): expected \"%s\", got \"%s\"\n" FASTFETCH_TEXT_MODIFIER_RESET,
 44            lineNo, major, minor, patch, expected, result.chars);
 45        exit(1);
 46    }
 47}
 48
 49#define VERIFY_PRETTY(...) verifyPretty(__VA_ARGS__, __LINE__)
 50
 51static void verifySemver(const char* major, const char* minor, const char* patch, const char* expected, int lineNo) {
 52    FF_STRBUF_AUTO_DESTROY result = ffStrbufCreate();
 53    FF_STRBUF_AUTO_DESTROY majorBuf = ffStrbufCreateS(major);
 54    FF_STRBUF_AUTO_DESTROY minorBuf = ffStrbufCreateS(minor);
 55    FF_STRBUF_AUTO_DESTROY patchBuf = ffStrbufCreateS(patch);
 56
 57    ffParseSemver(&result, &majorBuf, &minorBuf, &patchBuf);
 58
 59    if (!ffStrbufEqualS(&result, expected)) {
 60        fprintf(stderr, FASTFETCH_TEXT_MODIFIER_ERROR "[%d] ffParseSemver(\"%s\", \"%s\", \"%s\"): expected \"%s\", got \"%s\"\n" FASTFETCH_TEXT_MODIFIER_RESET,
 61            lineNo, major, minor, patch, expected, result.chars);
 62        exit(1);
 63    }
 64}
 65
 66#define VERIFY_SEMVER(...) verifySemver(__VA_ARGS__, __LINE__)
 67
 68static void verifyGtk(const char* gtk2, const char* gtk3, const char* gtk4, const char* expected, int lineNo) {
 69    FF_STRBUF_AUTO_DESTROY result = ffStrbufCreate();
 70    FF_STRBUF_AUTO_DESTROY gtk2Buf = ffStrbufCreateS(gtk2);
 71    FF_STRBUF_AUTO_DESTROY gtk3Buf = ffStrbufCreateS(gtk3);
 72    FF_STRBUF_AUTO_DESTROY gtk4Buf = ffStrbufCreateS(gtk4);
 73
 74    ffParseGTK(&result, &gtk2Buf, &gtk3Buf, &gtk4Buf);
 75
 76    if (!ffStrbufEqualS(&result, expected)) {
 77        fprintf(stderr, FASTFETCH_TEXT_MODIFIER_ERROR "[%d] ffParseGTK(\"%s\", \"%s\", \"%s\"): expected \"%s\", got \"%s\"\n" FASTFETCH_TEXT_MODIFIER_RESET,
 78            lineNo, gtk2, gtk3, gtk4, expected, result.chars);
 79        exit(1);
 80    }
 81}
 82
 83#define VERIFY_GTK(...) verifyGtk(__VA_ARGS__, __LINE__)
 84
 85int main(void) {
 86    // ffVersionCompare: the major field decides first, then minor, then patch. Equal versions
 87    // compare equal even when they come from different sources.
 88    {
 89        VERIFY_COMPARE(0, 0, 0, 0, 0, 0, 0);
 90        VERIFY_COMPARE(1, 2, 3, 1, 2, 3, 0);
 91        VERIFY_COMPARE(UINT32_MAX, UINT32_MAX, UINT32_MAX, UINT32_MAX, UINT32_MAX, UINT32_MAX, 0);
 92
 93        VERIFY_COMPARE(2, 0, 0, 1, 99, 99, 1);
 94        VERIFY_COMPARE(1, 99, 99, 2, 0, 0, -1);
 95
 96        VERIFY_COMPARE(1, 2, 0, 1, 1, 99, 1);
 97        VERIFY_COMPARE(1, 1, 99, 1, 2, 0, -1);
 98
 99        VERIFY_COMPARE(1, 2, 3, 1, 2, 2, 1);
100        VERIFY_COMPARE(1, 2, 2, 1, 2, 3, -1);
101
102        // A larger patch must not be able to outvote a smaller minor
103        VERIFY_COMPARE(1, 2, 0, 1, 1, UINT32_MAX, 1);
104        VERIFY_COMPARE(1, 1, UINT32_MAX, 1, 2, 0, -1);
105
106        // ... nor a larger minor a smaller major
107        VERIFY_COMPARE(2, 0, 0, 1, UINT32_MAX, UINT32_MAX, 1);
108        VERIFY_COMPARE(1, UINT32_MAX, UINT32_MAX, 2, 0, 0, -1);
109    }
110
111    // ffVersionToPretty: a zero version prints as nothing at all, and a field is only printed when
112    // it or a later field is non-zero.
113    {
114        VERIFY_PRETTY(0, 0, 0, "");
115        VERIFY_PRETTY(1, 0, 0, "1");
116        VERIFY_PRETTY(0, 1, 0, "0.1");
117        VERIFY_PRETTY(0, 0, 1, "0.0.1");
118        VERIFY_PRETTY(1, 2, 0, "1.2");
119        VERIFY_PRETTY(1, 0, 3, "1.0.3");
120        VERIFY_PRETTY(1, 2, 3, "1.2.3");
121        VERIFY_PRETTY(10, 20, 30, "10.20.30");
122        VERIFY_PRETTY(UINT32_MAX, 0, 0, "4294967295");
123        VERIFY_PRETTY(0, 0, UINT32_MAX, "0.0.4294967295");
124    }
125
126    // ffParseSemver: joins whichever parts were detected. A missing major is reported as 1, which is
127    // how "2.3" from a source that only reports the last two components stays unambiguous.
128    {
129        VERIFY_SEMVER("1", "2", "3", "1.2.3");
130        VERIFY_SEMVER("1", "2", "", "1.2");
131        VERIFY_SEMVER("1", "", "", "1");
132        VERIFY_SEMVER("1", "", "3", "1.0.3");
133        VERIFY_SEMVER("", "2", "3", "1.2.3");
134        VERIFY_SEMVER("", "2", "", "1.2");
135        VERIFY_SEMVER("", "", "3", "1.0.3");
136        VERIFY_SEMVER("", "", "", "");
137
138        // A zero part is a value, not a missing part
139        VERIFY_SEMVER("0", "0", "0", "0.0.0");
140        VERIFY_SEMVER("0", "", "", "0");
141    }
142
143    // ffParseSemver appends instead of replacing, so a caller can prefix its own text
144    {
145        FF_STRBUF_AUTO_DESTROY result = ffStrbufCreateS("v");
146        FF_STRBUF_AUTO_DESTROY majorBuf = ffStrbufCreateS("1");
147        FF_STRBUF_AUTO_DESTROY minorBuf = ffStrbufCreateS("2");
148        FF_STRBUF_AUTO_DESTROY patchBuf = ffStrbufCreateS("3");
149        ffParseSemver(&result, &majorBuf, &minorBuf, &patchBuf);
150        VERIFY(ffStrbufEqualS(&result, "v1.2.3"));
151    }
152
153    // ffParseGTK: the three versions are folded into one line, and every combination of present and
154    // equal versions has its own suffix.
155    {
156        // All three present
157        VERIFY_GTK("1.0", "1.0", "1.0", "1.0 [GTK2/3/4]");
158        VERIFY_GTK("2.0", "2.0", "3.0", "2.0 [GTK2/3], 3.0 [GTK4]");
159        VERIFY_GTK("2.0", "3.0", "3.0", "2.0 [GTK2], 3.0 [GTK3/4]");
160        VERIFY_GTK("2.0", "3.0", "4.0", "2.0 [GTK2], 3.0 [GTK3], 4.0 [GTK4]");
161
162        // GTK2 and GTK4 equal while GTK3 differs is not special cased: it falls back to the plain
163        // three-way listing, so the same version is printed twice with different suffixes.
164        VERIFY_GTK("2.0", "3.0", "2.0", "2.0 [GTK2], 3.0 [GTK3], 2.0 [GTK4]");
165
166        // The comparison is case insensitive, but the value printed is the one from the buffer that
167        // the branch selected -- here the third one, not the first.
168        VERIFY_GTK("ABC", "abc", "aBc", "aBc [GTK2/3/4]");
169        VERIFY_GTK("2.0", "2.0", "3.0", "2.0 [GTK2/3], 3.0 [GTK4]");
170
171        // Two present
172        VERIFY_GTK("1.0", "1.0", "", "1.0 [GTK2/3]");
173        VERIFY_GTK("2.0", "3.0", "", "2.0 [GTK2], 3.0 [GTK3]");
174        VERIFY_GTK("1.0", "", "1.0", "1.0 [GTK2/4]");
175        VERIFY_GTK("2.0", "", "4.0", "2.0 [GTK2], 4.0 [GTK4]");
176        VERIFY_GTK("", "1.0", "1.0", "1.0 [GTK3/4]");
177        VERIFY_GTK("", "3.0", "4.0", "3.0 [GTK3], 4.0 [GTK4]");
178
179        // One present
180        VERIFY_GTK("2.0", "", "", "2.0 [GTK2]");
181        VERIFY_GTK("", "3.0", "", "3.0 [GTK3]");
182        VERIFY_GTK("", "", "4.0", "4.0 [GTK4]");
183
184        // None present
185        VERIFY_GTK("", "", "", "");
186    }
187
188    puts("\033[32mAll tests passed!" FASTFETCH_TEXT_MODIFIER_RESET);
189    return 0;
190}