/* libmisc/tests/test_macro.c - Tests for * * Copyright (C) 2024-2025 Luke T. Shumaker * SPDX-License-Identifier: AGPL-3.0-or-later */ #include /* for free() */ #include /* for strcmp(), strlen(), memcmp(), strdup() */ #include #include "test.h" /** Given `N` from `#define _LM_EVAL _LM_EVAL__{N}`, UNDER is `(N*2)-2`. (16*2)-2=30. */ #define UNDER 30 /** Given `N` from `#define _LM_EVAL _LM_EVAL__{N}`, OVER is `(N*2)-1`. (16*2)-1=31. */ #define OVER 31 /** XUNDER is 0 through `UNDER` inclusive. */ #define XUNDER \ X(0) X(1) X(2) X(3) X(4) X(5) X(6) X(7) X(8) X(9) X(10) X(11) X(12) X(13) X(14) X(15) \ X(16) X(17) X(18) X(19) X(20) X(21) X(22) X(23) X(24) X(25) X(26) X(27) X(28) X(29) X(30) /** XUNDER is 0 through `OVER` inclusive. */ #define XOVER XUNDER X(OVER) static char *without_spaces(const char *in) { char *out = strdup(in); for (size_t i = 0; out[i]; i++) while (out[i] == ' ') for (size_t j = i; out[j]; j++) out[j] = out[j+1]; return out; } int main() { printf("== LM_NEXT_POWER_OF_2 =====================================\n"); /* valid down to 0. */ test_assert(LM_NEXT_POWER_OF_2(0) == 1); test_assert(LM_NEXT_POWER_OF_2(1) == 2); test_assert(LM_NEXT_POWER_OF_2(2) == 4); test_assert(LM_NEXT_POWER_OF_2(3) == 4); test_assert(LM_NEXT_POWER_OF_2(4) == 8); test_assert(LM_NEXT_POWER_OF_2(5) == 8); test_assert(LM_NEXT_POWER_OF_2(6) == 8); test_assert(LM_NEXT_POWER_OF_2(7) == 8); test_assert(LM_NEXT_POWER_OF_2(8) == 16); /* ... */ test_assert(LM_NEXT_POWER_OF_2(16) == 32); /* ... */ test_assert(LM_NEXT_POWER_OF_2(0x7000000000000000) == 0x8000000000000000); /* ... */ test_assert(LM_NEXT_POWER_OF_2(0x8000000000000000-1) == 0x8000000000000000); /* Valid up to 0x8000000000000000-1 = (1<<63)-1 */ printf("== LM_FLOORLOG2 ===========================================\n"); /* valid down to 1. */ test_assert(LM_FLOORLOG2(1) == 0); test_assert(LM_FLOORLOG2(2) == 1); test_assert(LM_FLOORLOG2(3) == 1); test_assert(LM_FLOORLOG2(4) == 2); test_assert(LM_FLOORLOG2(5) == 2); test_assert(LM_FLOORLOG2(6) == 2); test_assert(LM_FLOORLOG2(7) == 2); test_assert(LM_FLOORLOG2(8) == 3); /* ... */ test_assert(LM_FLOORLOG2(16) == 4); /* ... */ test_assert(LM_FLOORLOG2(0x80000000) == 31); /* ... */ test_assert(LM_FLOORLOG2(0xFFFFFFFF) == 31); test_assert(LM_FLOORLOG2(0x100000000) == 32); /* ... */ test_assert(LM_FLOORLOG2(0x8000000000000000) == 63); /* ... */ test_assert(LM_FLOORLOG2(0xFFFFFFFFFFFFFFFF) == 63); printf("== LM_TUPLE ===============================================\n"); test_assert(LM_IF(LM_IS_TUPLE( 9 ))(0)(1)); test_assert(LM_IF(LM_IS_TUPLE( a ))(0)(1)); test_assert(LM_IF(LM_IS_TUPLE( () ))(1)(0)); test_assert(LM_IF(LM_IS_TUPLE( (9) ))(1)(0)); test_assert(LM_IF(LM_IS_TUPLE( (a) ))(1)(0)); test_assert(LM_IF(LM_IS_TUPLE( (a, b) ))(1)(0)); test_assert(LM_IF(LM_IS_EMPTY_TUPLE( () ))(1)(0)); test_assert(LM_IF(LM_IS_EMPTY_TUPLE( 9 ))(0)(1)); test_assert(LM_IF(LM_IS_EMPTY_TUPLE( a ))(0)(1)); test_assert(LM_IF(LM_IS_EMPTY_TUPLE( (9) ))(0)(1)); test_assert(LM_IF(LM_IS_EMPTY_TUPLE( (a) ))(0)(1)); test_assert(LM_IF(LM_IS_EMPTY_TUPLE( (a, b) ))(0)(1)); printf("== LM_TUPLES ==============================================\n"); test_assert(LM_IF(LM_TUPLES_IS_NONEMPTY( ))(0)(1)); test_assert(LM_IF(LM_TUPLES_IS_NONEMPTY( () ))(1)(0)); test_assert(LM_IF(LM_TUPLES_IS_NONEMPTY( (a) ))(1)(0)); test_assert(LM_IF(LM_TUPLES_IS_NONEMPTY( (a)(b) ))(1)(0)); test_assert(LM_IF(LM_TUPLES_IS_NONEMPTY( (a)(b)(c) ))(1)(0)); printf("== LM_FOREACH_PARAM =======================================\n"); /* Basic test. */ { #define FN(A, B) A "-" #B const char *str = LM_FOREACH_PARAM(FN, (" "), a, (b), c); #undef FN test_assert(strcmp(str, " -a -(b) -c") == 0); } /* Test that it works with the documented limit of params. */ { #define X(n) , n #define FN(n) #n "\n" const char *str = LM_FOREACH_PARAM_(FN, () XUNDER); #undef FN #undef X #define X(n) #n "\n" test_assert(strcmp(str, XUNDER) == 0); #undef X } /* Test that it breaks at documented_limit+1 tuples. */ { #define X(n) , n #define FN(n) n const char *str = LM_STR_(LM_FOREACH_PARAM_(FN, () XOVER)); #undef FN #undef X /* This comparison is a little extra complicated in * order to not be sensitive to whitespace in the * suffix. */ #define X(n) #n " " const char *exp_prefix = XUNDER; #undef X const char *exp_suffix = "FN(" LM_STR_(OVER) ")_LM_FOREACH_PARAM_ITEM_indirect()(FN,(),())"; test_assert(strlen(exp_prefix) < strlen(str) && memcmp(exp_prefix, str, strlen(exp_prefix)) == 0); char *act_suffix = without_spaces(&str[strlen(exp_prefix)]); test_assert(strcmp(act_suffix, exp_suffix) == 0); free(act_suffix); } printf("== LM_FOREACH_TUPLE =======================================\n"); /* Basic test. */ { #define FN(a, b) a "-" b const char *str = LM_FOREACH_TUPLE( ("a") ("b") ("c"), FN, " "); #undef FN test_assert(strcmp(str, " -a -b -c") == 0); } /* Test that it works with the documented limit of tuples. */ { #define X(n) (n) #define FN(n) #n "\n" const char *str = LM_FOREACH_TUPLE(XUNDER, FN); #undef FN #undef X #define X(n) #n "\n" test_assert(strcmp(str, XUNDER) == 0); #undef X } /* Test that it breaks at documented_limit+1 tuples. */ { #define X(n) (n) #define FN(n) n const char *str = LM_STR_(LM_FOREACH_TUPLE(XOVER, FN)); #undef FN #undef X /* This comparison is a little extra complicated in * order to not be sensitive to whitespace in the * suffix. */ #define X(n) #n " " const char *exp_prefix = XUNDER; #undef X const char *exp_suffix = "FN(" LM_STR_(OVER) ")_LM_FOREACH_TUPLE_indirect()(,FN,)"; test_assert(strlen(exp_prefix) < strlen(str) && memcmp(exp_prefix, str, strlen(exp_prefix)) == 0); char *act_suffix = without_spaces(&str[strlen(exp_prefix)]); test_assert(strcmp(act_suffix, exp_suffix) == 0); free(act_suffix); } return 0; }