Serenity Operating System
1/*
2 * Copyright (c) 2021, the SerenityOS developers.
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7#include <LibTest/TestCase.h>
8
9#include <AK/Array.h>
10#include <stdio.h>
11#include <string.h>
12
13typedef long double longdouble;
14typedef long long longlong;
15typedef unsigned long long unsignedlonglong;
16typedef unsigned long unsignedlong;
17typedef char charstar[32];
18
19template<typename T>
20constexpr static Array<unsigned char, 32> to_value_t(T x)
21{
22 // The endianness doesn't really matter, since we're going to convert both sides with this anyway.
23 union Value {
24 u8 v[32];
25 T t;
26 };
27
28 auto value = Value { .t = x };
29
30 return {
31 value.v[0],
32 value.v[1],
33 value.v[2],
34 value.v[3],
35 value.v[4],
36 value.v[5],
37 value.v[6],
38 value.v[7],
39 value.v[8],
40 value.v[9],
41 value.v[10],
42 value.v[11],
43 value.v[12],
44 value.v[13],
45 value.v[14],
46 value.v[15],
47 value.v[16],
48 value.v[17],
49 value.v[18],
50 value.v[19],
51 value.v[20],
52 value.v[21],
53 value.v[22],
54 value.v[23],
55 value.v[24],
56 value.v[25],
57 value.v[26],
58 value.v[27],
59 value.v[28],
60 value.v[29],
61 value.v[30],
62 value.v[31],
63 };
64}
65
66template<size_t N>
67constexpr static Array<unsigned char, 32> str_to_value_t(char const (&x)[N])
68{
69 Array<unsigned char, 32> value { 0 };
70 for (size_t i = 0; i < N; ++i)
71 value[i] = x[i];
72 return value;
73}
74
75struct Argument {
76 size_t size;
77 void* data;
78};
79
80static Array<u8, 32> arg_to_value_t(Argument const& arg)
81{
82 if (arg.size == 1)
83 return to_value_t(*(u8*)arg.data);
84
85 if (arg.size == 2)
86 return to_value_t(*(u16*)arg.data);
87
88 if (arg.size == 4)
89 return to_value_t(*(u32*)arg.data);
90
91 if (arg.size == 8)
92 return to_value_t(*(u64*)arg.data);
93
94 if (arg.size == 16) {
95 auto& data = *(charstar*)arg.data;
96 Array<unsigned char, 32> value { 0 };
97 for (size_t i = 0; i < 16; ++i)
98 value[i] = data[i];
99 return value;
100 }
101
102 if (arg.size == 32) {
103 auto& data = *(charstar*)arg.data;
104 auto length = strlen(data);
105 Array<unsigned char, 32> value { 0 };
106 for (size_t i = 0; i < length; ++i)
107 value[i] = data[i];
108 return value;
109 }
110
111 VERIFY_NOT_REACHED();
112}
113
114#define DECL_WITH_TYPE(ty) \
115 ty _##ty##arg0; \
116 ty _##ty##arg1; \
117 ty _##ty##arg2; \
118 Argument ty##arg0 { sizeof(ty), &_##ty##arg0 }; \
119 Argument ty##arg1 { sizeof(ty), &_##ty##arg1 }; \
120 Argument ty##arg2 { sizeof(ty), &_##ty##arg2 };
121
122DECL_WITH_TYPE(int);
123DECL_WITH_TYPE(unsigned);
124DECL_WITH_TYPE(long);
125DECL_WITH_TYPE(longlong);
126DECL_WITH_TYPE(float);
127DECL_WITH_TYPE(double);
128DECL_WITH_TYPE(longdouble);
129DECL_WITH_TYPE(unsignedlong);
130DECL_WITH_TYPE(unsignedlonglong);
131
132#undef DECL_WITH_TYPE
133
134charstar _charstararg0;
135charstar _charstararg1;
136charstar _charstararg2;
137Argument charstararg0 { sizeof(charstar), &_charstararg0[0] };
138Argument charstararg1 { sizeof(charstar), &_charstararg1[0] };
139Argument charstararg2 { sizeof(charstar), &_charstararg2[0] };
140
141struct TestSuite {
142 char const* format;
143 char const* input;
144 int expected_return_value;
145 size_t argument_count;
146 Argument arguments[8];
147 Array<unsigned char, 32> expected_values[8]; // 32 bytes for each argument's value.
148};
149
150const TestSuite test_suites[] {
151 { "%d", "", 0, 0, {}, {} },
152 { "%x", "0x519", 1, 1, { unsignedarg0 }, { to_value_t(0x519) } },
153 { "%x", "0x51g", 1, 1, { unsignedarg0 }, { to_value_t(0x51u) } },
154 { "%06x", "0xabcdef", 1, 1, { unsignedarg0 }, { to_value_t(0xabcdefu) } },
155 { "%X", "0xCAFEBABE", 1, 1, { unsignedarg0 }, { to_value_t(0xcafebabe) } },
156 { "%04X", "0x5E4E", 1, 1, { unsignedarg0 }, { to_value_t(0x5e4e) } },
157 { "%X", "0x51Eg", 1, 1, { unsignedarg0 }, { to_value_t(0x51e) } },
158 { "\"%%%d#", "\"%42#", 1, 1, { intarg0 }, { to_value_t(42) } },
159 { " %d", "42", 1, 1, { intarg0 }, { to_value_t(42) } },
160 { "%d", " 42", 1, 1, { intarg0 }, { to_value_t(42) } },
161 { "%ld", "42", 1, 1, { longarg0 }, { to_value_t(42l) } },
162 { "%lld", "42", 1, 1, { longlongarg0 }, { to_value_t(42ll) } },
163 { "%f", "42", 1, 1, { floatarg0 }, { to_value_t(42.0f) } },
164 { "%lf", "42", 1, 1, { doublearg0 }, { to_value_t(42.0) } },
165 { "%s", "42", 1, 1, { charstararg0 }, { str_to_value_t("42") } },
166 { "%d%s", "42yoinks", 2, 2, { intarg0, charstararg0 }, { to_value_t(42), str_to_value_t("yoinks") } },
167 { "%[^\n]", "aaaa\n", 1, 1, { charstararg0 }, { str_to_value_t("aaaa") } },
168 { "%u.%u.%u", "3.19", 2, 3, { unsignedarg0, unsignedarg1, unsignedarg2 }, { to_value_t(3u), to_value_t(19u) } },
169 // Failing test case from previous impl:
170 { "SSH-%d.%d-%[^\n]\n", "SSH-2.0-OpenSSH_8.2p1 Ubuntu-4ubuntu0.1\n", 3, 3, { intarg0, intarg1, charstararg0 }, { to_value_t(2), to_value_t(0), str_to_value_t("OpenSSH_8.2p1 Ubuntu-4ubuntu0.1") } },
171 // GCC failure tests
172 { "%d.%d.%d", "10.2.0", 3, 3, { intarg0, intarg1, intarg2 }, { to_value_t(10), to_value_t(2), to_value_t(0) } },
173 { "%lu", "3054 ", 1, 1, { unsignedlongarg0 }, { to_value_t(3054ul) } },
174 // "actual" long long and unsigned long long, from #6096
175 // Note: '9223372036854775806' is the max value for 'long long'.
176 { "%lld", "9223372036854775805", 1, 1, { longlongarg0 }, { to_value_t(9223372036854775805LL) } },
177 { "%llu", "9223372036854775810", 1, 1, { unsignedlonglongarg0 }, { to_value_t(9223372036854775810ULL) } },
178 { "%n", "", 0, 1, { intarg0 }, { to_value_t(0) } },
179 { "%d %n", "1 a", 1, 2, { intarg0, intarg1 }, { to_value_t(1), to_value_t(2) } },
180 { "%*d", " 42", 0, 0, {}, {} },
181 { "%d%*1[:/]%d", "24/7", 2, 2, { intarg0, intarg1 }, { to_value_t(24), to_value_t(7) } },
182 { " %[^a]", " b", 1, 1, { charstararg0 }, { str_to_value_t("b") } },
183};
184
185bool g_any_failed = false;
186
187static bool check_value_conformance(TestSuite const& test)
188{
189 bool fail = false;
190 for (size_t i = 0; i < test.argument_count; ++i) {
191 auto& arg = test.arguments[i];
192 auto arg_value = arg_to_value_t(arg);
193 auto& value = test.expected_values[i];
194 if (arg_value != value) {
195 auto arg_ptr = (u32 const*)arg_value.data();
196 auto value_ptr = (u32 const*)value.data();
197 printf(" value %zu FAIL,\n", i);
198 printf(" expected %08x%08x%08x%08x%08x%08x%08x%08x\n",
199 value_ptr[0], value_ptr[1], value_ptr[2], value_ptr[3],
200 value_ptr[4], value_ptr[5], value_ptr[6], value_ptr[7]);
201 printf(" but got %08x%08x%08x%08x%08x%08x%08x%08x\n",
202 arg_ptr[0], arg_ptr[1], arg_ptr[2], arg_ptr[3],
203 arg_ptr[4], arg_ptr[5], arg_ptr[6], arg_ptr[7]);
204 fail = true;
205 } else {
206 printf(" value %zu PASS\n", i);
207 }
208 }
209
210 return !fail;
211}
212
213static void do_one_test(TestSuite const& test)
214{
215 printf("Testing '%s' against '%s'...\n", test.input, test.format);
216
217#pragma GCC diagnostic push
218#pragma GCC diagnostic ignored "-Wformat-nonliteral"
219 auto rc = sscanf(test.input, test.format,
220 test.arguments[0].data, test.arguments[1].data, test.arguments[2].data, test.arguments[3].data,
221 test.arguments[4].data, test.arguments[5].data, test.arguments[6].data, test.arguments[7].data);
222#pragma GCC diagnostic pop
223
224 bool overall = true;
225 printf(" return value...\n");
226 if (rc != test.expected_return_value) {
227 printf(" return value FAIL, expected %d but got %d\n", test.expected_return_value, rc);
228 overall = false;
229 } else {
230 printf(" return value PASS\n");
231 }
232
233 printf(" read values...\n");
234 if (check_value_conformance(test)) {
235 printf(" read values PASS\n");
236 } else {
237 printf(" read values FAIL\n");
238 overall = false;
239 }
240
241 if (overall)
242 printf(" overall PASS\n");
243 else
244 printf(" overall FAIL\n");
245
246 VERIFY(overall);
247}
248
249TEST_CASE(scanf)
250{
251 for (auto& test : test_suites)
252 do_one_test(test);
253}