title: src/tests.c
src/tests.c
Classes
Name | |
---|---|
struct | _Natural |
Types
Functions
Name | |
---|---|
u0 | utf8_ucs4_conversions(byte * cstring) |
Defines
Name | |
---|---|
TEST(DOES) |
Detailed Description
File for testing the library. Not part of the library.
Types Documentation
typedef Natural
typedef struct _Natural Natural;
Functions Documentation
function utf8_ucs4_conversions
u0 utf8_ucs4_conversions(
byte * cstring
)
Macros Documentation
define TEST
#define TEST(
DOES
)
do { \
[println](/crelude/Files/io_8h.md#define-println)("\n" ANSI([BOLD](/crelude/Files/common_8h.md#define-bold)) "[###]" [ANSI](/crelude/Files/common_8h.md#define-ansi)([RESET](/crelude/Files/common_8h.md#define-reset)) " "\
[ANSI](/crelude/Files/common_8h.md#define-ansi)([UNDER](/crelude/Files/common_8h.md#define-under)) "Test Case" ANSI([RESET](/crelude/Files/common_8h.md#define-reset)) ": %s", DOES); \
} while (false); [always](/crelude/Files/common_8h.md#define-always)
Source code
#include <crelude/common.h>
#include <crelude/io.h>
#include <crelude/utf.h>
#include <crelude/base64.h>
#include <crelude/argparse.h>
#include <stdio.h>
#include <locale.h>
#define TEST(DOES) do { \
println("\n" ANSI(BOLD) "[###]" ANSI(RESET) " "\
ANSI(UNDER) "Test Case" ANSI(RESET) ": %s", DOES); \
} while (false); always
u0 utf8_ucs4_conversions(byte *cstring)
{
// UTF-8 string.
string s = to_string(cstring);
println("s = \"%s\"", UNWRAP(s));
FOR_EACH(c, s) println("byte: 0x%X", c);
println("s.len = %zu", s.len);
// Convert to UCS-4.
println("[***] Convert to UCS-4.");
runic ucs4 = SMAKE(rune, s.len + 1);
ucs4 = utf8_to_ucs4(ucs4, s);
println("ucs4 = \"%ls\"", (wchar_t *)UNWRAP(ucs4));
FOR_EACH(c, ucs4) println("rune: U+%08X = '%lc'", c, c);
println("ucs4.len = %zu", ucs4.len);
// And back again.
println("[***] Convert to UTF-8.");
string utf8 = SMAKE(byte, 4 * ucs4.len + 4);
utf8 = ucs4_to_utf8(utf8, ucs4);
println("utf8 = \"%s\"", UNWRAP(utf8));
FOR_EACH(c, utf8) println("byte: 0x%X", c);
println("utf8.len = %zu", utf8.len);
}
newtype(Natural, u64); // New-type idiom.
#ifndef IMPLEMENTATION
ierr main(i32 argc, const byte **argv)
{
UNUSED(argc); UNUSED(argv);
byte *locale; UNUSED(locale);
locale = setlocale(LC_ALL, "");
println("Locale is UTF-8? %s.",
is_locale_utf8(locale) ? "true" : "false");
Natural n = { 7 }; // How to use newtypes.
n.value = 4; UNUSED(n);
// -- UTF-8 <--> UCS-4, conversions. -- //
TEST("Converts between UTF-8 and UCS-4.") {
println("=== Chinese Characters ===");
utf8_ucs4_conversions("你好");
println("\n=== Latin-1/ASCII Characters ===");
utf8_ucs4_conversions("AaBb");
println("\n=== Look-alike Characters ===");
utf8_ucs4_conversions("maña"); // U+00F1 = ñ.
utf8_ucs4_conversions("maña"); // n + U+0303 = ñ.
// The second string has one extra 'code-point', but both
// have the exact same number of human distinguishable 'graphemes'.
}
// -- UTF-8 Escapes -- //
TEST("Unescaped ASCII/UTF-8 strings to UTF-8") {
println("=== Unescaping Strings ===");
string unescaped = SMAKE(byte, 128);
string escaped = STRING("Hello, \\U1F30E.");
println("escaped.len = %zu", escaped.len);
unescaped = utf8_unescape(unescaped, escaped);
println("unescaped.len = %zu", unescaped.len);
println("\"%s\" --> \"%s\"", escaped.value, unescaped.value);
}
TEST("Escaped UTF-8 strings to escaped ASCII") {
println("=== Escaped Strings ===");
string escaped = SMAKE(byte, 128);
string unescaped = STRING("Hello, 🌎.");
println("unescaped.len = %zu", unescaped.len);
escaped = utf8_escape(escaped, unescaped, false);
println("escaped.len = %zu", escaped.len);
println("\"%s\" --> \"%s\"", unescaped.value, escaped.value);
}
TEST("The new (internal) printf variant function") {
rune c = U'𰻝';
println("Code point for '%C' = %U.", c, c);
runic phrase = INIT(rune, { 0x73AB, 0x7470, 0x6C34 });
println("The runes { %V{U+%04X}{, } }, represent: \"%r\".", phrase, phrase);
println("Which are the three graphemes: %V{'%C'}{, }.", phrase);
long long int some_ll_int = 36;
println("A normal base-10 long long integer: %05lld.", some_ll_int);
StringBuilder builder = AMAKE(byte, 5);
extend(&builder, &STR("Hello"), sizeof(byte));
push(&builder, ",", sizeof(byte));
string name = STRING("Bob.");
extend(&builder, &name, sizeof(byte));
assert('.' == *(byte *)pop(&builder, sizeof(byte)));
push(&builder, "!", sizeof(byte));
insert(&builder, 6, " ", sizeof(byte));
string slice = SLICE(string, builder, 0, -1);
println("The characters %D{(%c)}-, say: \"%S\".", builder, slice);
string slice_with_nul = SLICE(string, builder, 0, 12);
println("With ASCII values: [%V{#%02hhX}{, }].", slice_with_nul);
string alt = STRING("Strings may be printed like this too.\n");
print("%Vc{}", alt);
println("(that is, %V02hhX-)", alt); // No curly-braces needed if unambiguous!
}
TEST("String comparison: string_cmp vs. strcmp") {
string s0 = STRING("Latino");
string s1 = STRING("Latina");
byte *p0 = UNWRAP(s0);
byte *p1 = UNWRAP(s1);
println("string_cmp: %s <~> %s = %hd", p0, p1, string_cmp(s0, s1));
println("strcmp: %s <~> %s = %d", p0, p1, strcmp(p0, p1));
assert(string_cmp(s0, s1) == strcmp(p0, p1));
string s2 = SLICE(string, s0, 0, -2);
string s3 = SLICE(string, s1, 0, -2);
println("string_cmp: %S <~> %S = %hd", s2, s3, string_cmp(s2, s3));
println("string_cmp: %S <~> %S = %hd", s3, s0, string_cmp(s3, s0));
println("string_cmp: %S <~> %S = %hd", s2, s0, string_cmp(s2, s0));
p1[s1.len - 1] = '\0';
println("strcmp: %s <~> %s = %d", p1, p0, strcmp(p1, p0));
}
TEST("Array removal and block swapping") {
newarray(IntArray, int);
IntArray arr = AMAKE(int, 5);
PUSH(arr, 0);
__auto_type list = LIST(sliceof(int), { 1, 2, 3, 4, 5, 6 });
__auto_type more = LIST(sliceof(int), { 7, 8, 9 });
UNSHIFT(arr, -1);
println("list[..%zu] = { %V%d{, } };", list.len, list);
println("more[..%zu] = { %V%d{, } };", more.len, more);
EXTEND(arr, list);
EXTEND(arr, more);
println("arr = %D%d{, };", arr);
int *popped = SHIFT(arr);
println("arr = %D%d{, }; (popped: %d)", arr, *popped);
SWAP(arr, 3); // Swap the three first elements to the back.
println("arr = %D%d{, }; (swapped 3 first elements around)", arr);
int *rmvd = REMOVE(arr, 7); // Remove 8th element (0).
println("arr = %D%d{, }; (removed at index %zu, element: %d)", arr, 7, *rmvd);
SWAP(arr, 7);
println("arr = %D%d{, }; (swapped at index 7)", arr);
sliceof(int) *cutout = CUT(arr, 2, 4);
println("arr = %D%d{, }; (cut out: %V%d{, })", arr, *cutout);
}
TEST("Base64 encoding and decoding.") {
sliceof(u16) data = INIT(u16, {
__extension__ 0b0000000000000000, //< 0 (0x00 0x00).
__extension__ 0b0011001000110010, //< 12,850 (0x32 0x32).
__extension__ 0b0000011000000110, //< 1,542 (0x06 0x06).
__extension__ 0b0111010101110101 //< 30,069 (0x75 0x75).
}); //< 64-bit integer value = 55,190,430,840,181 (big endian).
MemSlice bytes = TO_BYTES(data);
println("{ %V%hu{, } } <=> { %V{0x%02hhX}{, } }", data, bytes);
MemSlice repr = bytes;
if (is_little_endian()) // Convert to big endian byte order.
repr = reverse_endianness(repr);
println(" (decimal: %lu)", *(u64 *)PTR(repr));
MemSlice encoded = base64_encode(bytes);
println("encodes to: %V%c{}", encoded);
if (is_little_endian())
FREE_INSIDE(repr);
MemSlice decoded = base64_decode(encoded);
repr = decoded;
if (is_little_endian())
repr = reverse_endianness(repr);
println("decodes to: %lu", *(u64 *)PTR(repr));
FREE_INSIDE(encoded);
if (is_little_endian())
FREE_INSIDE(repr);
string str = STRING("Hello, World!");
println("\"%S\" <=> { %V{0x%hhX}{, } }", str, str);
encoded = base64_encode(TO_BYTES(str));
println("encodes to: %V%c{}", encoded);
decoded = base64_decode(encoded);
println("decodes to: \"%V%c{}\".", decoded);
FREE_INSIDE(encoded);
FREE_INSIDE(decoded);
}
TEST("Maps / Hash Tables.") {
// key type, value type, bucket capacity.
// v v v
mapof(byte *, i32) map = MMAKE(byte *, i32, 9);
// ^ Initial bucket size (9) is an inflated guess of how
// many entries we might expect the hash-map to have.
// Hash-function can be changed.
// Important, since different types are hashed differently.
// i.e. pointers should be hashed for what's behind them,
// instead of their raw address value, (usually).
assert(map.hasher == cstring_hash);
map.hasher = cstring_hash; //< this is set by default using _Generic.
mapof(string, i16) _m = MMAKE(string, i16, 5);
hashnode(string, i16) node;
string some_key = STRING("key");
i16 val = 69;
init_hashnode(&node, &_m, 93967, &some_key, &val);
println("node.key.hash = %lu;", node.key.hash);
println("node.key.value = \"%S\";", node.key.value);
println("node.value = %hd;", node.value);
println("node.next = %p;", node.next);
puts("");
i32 *value;
println("number of entries: %zu.", map.len);
ASSOCIATE(map, "hello", 38);
value = LOOKUP(map, "hello");
// `value` points to the item mapped to by key "hello".
println("value (%p): %d", value, *value);
println("number of entries: %zu.", map.len);
// drop value at key "hello" from table.
DROP(map, "hello");
value = LOOKUP(map, "hello");
// `value` should now point to NULL.
println("value (%p): -", value);
println("number of entries: %zu.", map.len);
puts("");
free_map(&map);
// maps work with any key and value types.
mapof(string, u16) dict = MMAKE(string, u16, 2);
assert(dict.hasher == string_hash);
println("hash(\"ad\") = %llX; hash(\"da\") = %llX;",
hash_string(STR("ad")), hash_string(STR("da")));
assert(DROP(dict, STR("ab")) == false);
string ab = STRING("ab");
string bc = STRING("bc");
string ac = STRING("ac");
string ca = STRING("ca");
string ad = STRING("ad");
string da = STRING("da");
ASSOCIATE(dict, ab, 0x6162);
ASSOCIATE(dict, bc, 0x6263);
ASSOCIATE(dict, ac, 0x6163);
ASSOCIATE(dict, ca, 0x0000);
ASSOCIATE(dict, ca, 0x6361); //< overwrite.
ASSOCIATE(dict, ad, 0x6164);
ASSOCIATE(dict, da, 0x6461);
println("dict: %S -> 0x%04hX", STR("ab"), deref(u16, LOOKUP(dict, STR("ab")), 0));
println("dict: %S -> 0x%04hX", STR("bc"), deref(u16, LOOKUP(dict, STR("bc")), 0));
println("dict: %S -> 0x%04hX", STR("ac"), deref(u16, LOOKUP(dict, STR("ac")), 0));
println("dict: %S -> 0x%04hX", STR("ca"), deref(u16, LOOKUP(dict, STR("ca")), 0));
println("dict: %S -> 0x%04hX", STR("ad"), deref(u16, LOOKUP(dict, STR("ad")), 0));
println("dict: %S -> 0x%04hX", STR("da"), deref(u16, LOOKUP(dict, STR("da")), 0));
println("\nDump hashmap layout:");
dump_hashmap(&dict, "\"%S\"", "0x%02hX");
assert(DROP(dict, STR("bc")) == true);
dump_hashmap(&dict, "\"%S\"", "0x%02hX");
println(" ^^ after dropping \"bc\".");
assert(DROP(dict, STR("da")) == true);
dump_hashmap(&dict, "\"%S\"", "0x%02hX");
println(" ^^ after dropping \"da\".");
assert(!is_empty_map(&dict));
assert(HAS_KEY(dict, STR("ac")) == true);
empty_map(&dict);
assert(HAS_KEY(dict, STR("ac")) == false);
assert(is_empty_map(&dict));
free_map(&dict);
assert(is_empty_map(&dict));
mapof(i32, string) table = MMAKE(i32, string, 3);
assert(table.hasher == upcast_hash);
string hello = STRING("Hello, ");
string world = STRING("World!");
ASSOCIATE(table, -3, hello);
ASSOCIATE(table, +7, world);
// Iterate through all keys present in table.
sliceof(i32) *keys = KEYS(table);
println("All keys in table: %V%d{, }.", *keys);
foreach (key, *keys)
println("table[%d] = \"%S\";", key, *LOOKUP(table, key));
assert(!HAS_KEY(table, 3));
assert(HAS_KEY(table, -3));
FREE_INSIDE(*keys);
assert(!is_empty_map(&table));
free_map(&table);
assert(is_empty_map(&table));
}
TEST("Argument parsing") {
ArgParser ctx;
mapof(ArgID, Arg) options = MMAKE(ArgID, Arg, 15);
ArgID arg_a, arg_b, arg_c, arg_q, arg_m, arg_l, arg_v, arg_n, arg_s;
// init for parsing
arginit(&ctx);
// register arguments
arg_a = argreg(&ctx, "a", nil, false, "Option A.");
arg_b = argreg(&ctx, "-b", nil, false, "Option B.");
arg_c = argreg(&ctx, "c", nil, false, "Option C.");
arg_q = argreg(&ctx, "q", "--quiet", false, "Stay quiet.");
arg_m = argreg(&ctx, "-m", "message", true, "Send a message.");
arg_l = argreg(&ctx, "-l", "--list", false, "List values.");
arg_v = argreg(&ctx, "-v", "--volume", true, "Sets the volume.");
arg_n = argreg(&ctx, "-n", "--number", true, "Sets the number.");
arg_s = argreg(&ctx, nil, "--skip", true, "Sets the skip count.");
// sample cli arguments
sliceof(string) args = INIT(string, {
STRING("-abc"), STRING("+q"), // '+' toggles off instead.
STRING("--message"), STRING("hello world"),
STRING("-l"), STRING("-v"), STRING("10"),
STRING("-n43"), // short option with argument glued on.
STRING("--skip=19") // long option with argument glued on.
});
// ./prog -abc +q --message "hello world" -l -v 10 -n43 --skip=19
ierr err;
foreach (arg, args) {
err = argparse(&ctx, &options, arg);
if (OK != err) {
eprintln("error parsing arguments: %S", ctx.error_message);
break;
}
}
if (OK == err) {
println("Option -a enabled? %b", (*LOOKUP(options, arg_a)).is_on);
println("Option -b enabled? %b", (*LOOKUP(options, arg_b)).is_on);
println("Option -c enabled? %b", (*LOOKUP(options, arg_c)).is_on);
println("Option -q enabled? %b", (*LOOKUP(options, arg_q)).is_on);
println("Option -m value? %S", (*LOOKUP(options, arg_m)).value);
println("Option -l enabled? %b", (*LOOKUP(options, arg_l)).is_on);
println("Option -v value? %S", (*LOOKUP(options, arg_v)).value);
println("Option -n value? %S", (*LOOKUP(options, arg_n)).value);
println("Option --skip value? %S", (*LOOKUP(options, arg_s)).value);
}
}
return EXIT_SUCCESS;
}
#endif
Updated on 23 August 2022 at 00:54:19 UTC