ladybird/Tests/LibC/scanf.cpp
Brian Gianforcaro fd0dbd1ebf Tests: Establish root Tests directory, move Userland/Tests there
With the goal of centralizing all tests in the system, this is a
first step to establish a Tests sub-tree. It will contain all of
the unit tests and test harnesses for the various components in the
system.
2021-05-06 17:54:28 +02:00

246 lines
7.5 KiB
C++

/*
* Copyright (c) 2021, the SerenityOS developers.
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/Array.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef long double longdouble;
typedef long long longlong;
typedef unsigned long long unsignedlonglong;
typedef unsigned long unsignedlong;
typedef char charstar[32];
template<typename T>
constexpr static Array<unsigned char, 32> to_value_t(T x)
{
// The endianness doesn't really matter, since we're going to convert both sides with this anyway.
union Value {
u8 v[32];
T t;
};
auto value = Value { .t = x };
return {
value.v[0],
value.v[1],
value.v[2],
value.v[3],
value.v[4],
value.v[5],
value.v[6],
value.v[7],
value.v[8],
value.v[9],
value.v[10],
value.v[11],
value.v[12],
value.v[13],
value.v[14],
value.v[15],
value.v[16],
value.v[17],
value.v[18],
value.v[19],
value.v[20],
value.v[21],
value.v[22],
value.v[23],
value.v[24],
value.v[25],
value.v[26],
value.v[27],
value.v[28],
value.v[29],
value.v[30],
value.v[31],
};
}
template<size_t N>
constexpr static Array<unsigned char, 32> str_to_value_t(const char (&x)[N])
{
Array<unsigned char, 32> value { 0 };
for (size_t i = 0; i < N; ++i)
value[i] = x[i];
return value;
}
struct Argument {
size_t size;
void* data;
};
static Array<u8, 32> arg_to_value_t(const Argument& arg)
{
if (arg.size == 1)
return to_value_t(*(u8*)arg.data);
if (arg.size == 2)
return to_value_t(*(u16*)arg.data);
if (arg.size == 4)
return to_value_t(*(u32*)arg.data);
if (arg.size == 8)
return to_value_t(*(u64*)arg.data);
if (arg.size == 16) {
auto& data = *(charstar*)arg.data;
Array<unsigned char, 32> value { 0 };
for (size_t i = 0; i < 16; ++i)
value[i] = data[i];
return value;
}
if (arg.size == 32) {
auto& data = *(charstar*)arg.data;
auto length = strlen(data);
Array<unsigned char, 32> value { 0 };
for (size_t i = 0; i < length; ++i)
value[i] = data[i];
return value;
}
VERIFY_NOT_REACHED();
}
#define DECL_WITH_TYPE(ty) \
ty _##ty##arg0; \
ty _##ty##arg1; \
ty _##ty##arg2; \
Argument ty##arg0 { sizeof(ty), &_##ty##arg0 }; \
Argument ty##arg1 { sizeof(ty), &_##ty##arg1 }; \
Argument ty##arg2 { sizeof(ty), &_##ty##arg2 };
DECL_WITH_TYPE(int);
DECL_WITH_TYPE(unsigned);
DECL_WITH_TYPE(long);
DECL_WITH_TYPE(longlong);
DECL_WITH_TYPE(float);
DECL_WITH_TYPE(double);
DECL_WITH_TYPE(longdouble);
DECL_WITH_TYPE(unsignedlong);
DECL_WITH_TYPE(unsignedlonglong);
#undef DECL_WITH_TYPE
charstar _charstararg0;
charstar _charstararg1;
charstar _charstararg2;
Argument charstararg0 { sizeof(charstar), &_charstararg0[0] };
Argument charstararg1 { sizeof(charstar), &_charstararg1[0] };
Argument charstararg2 { sizeof(charstar), &_charstararg2[0] };
struct TestSuite {
const char* format;
const char* input;
int expected_output;
size_t argument_count;
Argument arguments[8];
Array<unsigned char, 32> expected_values[8]; // 32 bytes for each argument's value.
};
const TestSuite test_suites[] {
{ "%d", "", 0, 0, {}, {} },
{ "%x", "0x519", 1, 1, { unsignedarg0 }, { to_value_t(0x519) } },
{ "%x", "0x51g", 1, 1, { unsignedarg0 }, { to_value_t(0x51u) } },
{ "%06x", "0xabcdef", 1, 1, { unsignedarg0 }, { to_value_t(0xabcdefu) } },
{ "\"%%%d#", "\"%42#", 1, 1, { intarg0 }, { to_value_t(42) } },
{ " %d", "42", 1, 1, { intarg0 }, { to_value_t(42) } },
{ "%d", " 42", 1, 1, { intarg0 }, { to_value_t(42) } },
{ "%ld", "42", 1, 1, { longarg0 }, { to_value_t(42l) } },
{ "%lld", "42", 1, 1, { longlongarg0 }, { to_value_t(42ll) } },
{ "%f", "42", 1, 1, { floatarg0 }, { to_value_t(42.0f) } },
{ "%lf", "42", 1, 1, { doublearg0 }, { to_value_t(42.0) } },
{ "%s", "42", 1, 1, { charstararg0 }, { str_to_value_t("42") } },
{ "%d%s", "42yoinks", 2, 2, { intarg0, charstararg0 }, { to_value_t(42), str_to_value_t("yoinks") } },
{ "%[^\n]", "aaaa\n", 1, 1, { charstararg0 }, { str_to_value_t("aaaa") } },
{ "%u.%u.%u", "3.19", 2, 3, { unsignedarg0, unsignedarg1, unsignedarg2 }, { to_value_t(3u), to_value_t(19u) } },
// Failing test case from previous impl:
{ "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") } },
// GCC failure tests
{ "%d.%d.%d", "10.2.0", 3, 3, { intarg0, intarg1, intarg2 }, { to_value_t(10), to_value_t(2), to_value_t(0) } },
{ "%lu", "3054 ", 1, 1, { unsignedlongarg0 }, { to_value_t(3054ul) } },
// "actual" long long and unsigned long long, from #6096
// Note: '9223372036854775806' is the max value for 'long long'.
{ "%lld", "9223372036854775805", 1, 1, { longlongarg0 }, { to_value_t(9223372036854775805LL) } },
{ "%llu", "9223372036854775810", 1, 1, { unsignedlonglongarg0 }, { to_value_t(9223372036854775810ULL) } },
};
bool g_any_failed = false;
static bool check_value_conformance(const TestSuite& test)
{
bool fail = false;
for (int i = 0; i < test.expected_output; ++i) {
auto& arg = test.arguments[i];
auto arg_value = arg_to_value_t(arg);
auto& value = test.expected_values[i];
if (arg_value != value) {
auto arg_ptr = (const u32*)arg_value.data();
auto value_ptr = (const u32*)value.data();
printf(" value %d FAIL, expected %04x%04x%04x%04x%04x%04x%04x%04x but got %04x%04x%04x%04x%04x%04x%04x%04x\n",
i,
value_ptr[0], value_ptr[1], value_ptr[2], value_ptr[3],
value_ptr[4], value_ptr[5], value_ptr[6], value_ptr[7],
arg_ptr[0], arg_ptr[1], arg_ptr[2], arg_ptr[3],
arg_ptr[4], arg_ptr[5], arg_ptr[6], arg_ptr[7]);
fail = true;
} else {
printf(" value %d PASS\n", i);
}
}
return !fail;
}
static void do_one_test(const TestSuite& test)
{
printf("Testing '%s' against '%s'...\n", test.input, test.format);
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
auto rc = sscanf(test.input, test.format,
test.arguments[0].data, test.arguments[1].data, test.arguments[2].data, test.arguments[3].data,
test.arguments[4].data, test.arguments[5].data, test.arguments[6].data, test.arguments[7].data);
#pragma GCC diagnostic pop
bool overall = true;
printf(" output value...\n");
if (rc != test.expected_output) {
printf(" output value FAIL, expected %d but got %d\n", test.expected_output, rc);
overall = false;
} else {
printf(" output value PASS\n");
}
printf(" read values...\n");
if (check_value_conformance(test)) {
printf(" read values PASS\n");
} else {
printf(" read values FAIL\n");
overall = false;
}
if (overall)
printf(" overall PASS\n");
else
printf(" overall FAIL\n");
g_any_failed = g_any_failed || !overall;
}
int main()
{
for (auto& test : test_suites)
do_one_test(test);
return g_any_failed ? 1 : 0;
}