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, >k2Buf, >k3Buf, >k4Buf);
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}