diff --git a/tests/attrset.nixir b/tests/attrset.nixir index ad8b3e2..708f5dd 100644 Binary files a/tests/attrset.nixir and b/tests/attrset.nixir differ diff --git a/tests/comparison.nixir b/tests/comparison.nixir index 3b41e90..fb7b4fd 100644 Binary files a/tests/comparison.nixir and b/tests/comparison.nixir differ diff --git a/tests/if.nixir b/tests/if.nixir index 2668f3f..4ee0f59 100644 Binary files a/tests/if.nixir and b/tests/if.nixir differ diff --git a/tests/lambda_pattern.nix b/tests/lambda_pattern.nix index 618558d..dfbef9b 100644 --- a/tests/lambda_pattern.nix +++ b/tests/lambda_pattern.nix @@ -9,8 +9,8 @@ let # With ellipsis (extra fields allowed) f3 = { a, ... }: a * 2; - # Named pattern - f4 = arg@{ a, b }: a + b + arg.c; + # Named pattern with ellipsis to allow extra fields + f4 = arg@{ a, b, ... }: a + b + arg.c; # Simple lambda (not a pattern) f5 = x: x + 1; diff --git a/tests/let.nixir b/tests/let.nixir index dc6028c..cb9dd41 100644 Binary files a/tests/let.nixir and b/tests/let.nixir differ diff --git a/tests/logical.nixir b/tests/logical.nixir index 2544d5c..010a5f5 100644 Binary files a/tests/logical.nixir and b/tests/logical.nixir differ diff --git a/tests/operators.nixir b/tests/operators.nixir index 031c2e6..f71f899 100644 Binary files a/tests/operators.nixir and b/tests/operators.nixir differ diff --git a/tests/precedence.nixir b/tests/precedence.nixir index 3488186..de1b0d4 100644 Binary files a/tests/precedence.nixir and b/tests/precedence.nixir differ diff --git a/tests/regression_test.cpp b/tests/regression_test.cpp new file mode 100644 index 0000000..10123a0 --- /dev/null +++ b/tests/regression_test.cpp @@ -0,0 +1,184 @@ +#include "irc/serializer.h" +#include "irc/types.h" +#include +#include + +using namespace nix_irc; + +int failures = 0; + +#define TEST_CHECK(cond, msg) \ + do { \ + if (!(cond)) { \ + std::cerr << " FAIL: " << msg << std::endl; \ + failures++; \ + } else { \ + std::cout << " PASS: " << msg << std::endl; \ + } \ + } while (0) + +#define TEST_PASS(msg) std::cout << " PASS: " << msg << std::endl +#define TEST_FAIL(msg) \ + do { \ + std::cerr << " FAIL: " << msg << std::endl; \ + failures++; \ + } while (0) + +void test_enum_compatibility() { + std::cout << "> Enum compatibility..." << std::endl; + + if (static_cast(NodeType::WITH) == 0x32) { + std::cout << " PASS: WITH has correct value 0x32" << std::endl; + } else { + std::cerr << " FAIL: WITH should be 0x32, got " + << static_cast(NodeType::WITH) << std::endl; + } + + if (static_cast(NodeType::HAS_ATTR) == 0x34) { + std::cout << " PASS: HAS_ATTR has value 0x34 (new slot after WITH bump)" + << std::endl; + } else if (static_cast(NodeType::HAS_ATTR) == 0x33 && + static_cast(NodeType::WITH) == 0x32) { + std::cout << " PASS: HAS_ATTR has value 0x33 (restored original with WITH " + "at 0x32)" + << std::endl; + } else { + std::cerr << " FAIL: HAS_ATTR value is " + << static_cast(NodeType::HAS_ATTR) + << " (expected 0x34 or 0x33 with WITH=0x32)" << std::endl; + } + + if (IR_VERSION == 2) { + std::cout << " PASS: IR_VERSION bumped to 2 for breaking change" + << std::endl; + } else if (static_cast(NodeType::WITH) == 0x32) { + std::cout << " PASS: IR_VERSION unchanged but WITH restored to 0x32" + << std::endl; + } else { + std::cerr << " FAIL: Either bump IR_VERSION or fix enum values" + << std::endl; + } +} + +void test_serializer_select_with_default() { + std::cout << "> SELECT serialization with default_expr..." << std::endl; + + auto expr = std::make_shared(ConstIntNode(42)); + auto attr = std::make_shared(ConstStringNode("key")); + auto default_val = std::make_shared(ConstIntNode(100)); + + SelectNode select_node(expr, attr); + select_node.default_expr = default_val; + auto select = std::make_shared(select_node); + + IRModule module; + module.entry = select; + + Serializer ser; + auto bytes = ser.serialize_to_bytes(module); + + Deserializer deser; + auto loaded = deser.deserialize(bytes); + + auto *loaded_select = loaded.entry->get_if(); + if (loaded_select && loaded_select->default_expr && + *loaded_select->default_expr) { + auto *def_val = (*loaded_select->default_expr)->get_if(); + if (def_val && def_val->value == 100) { + std::cout << " PASS: SELECT with default_expr round-trips correctly" + << std::endl; + } else { + std::cerr << " FAIL: default_expr value incorrect" << std::endl; + } + } else { + std::cerr << " FAIL: default_expr not deserialized (missing u8 flag read)" + << std::endl; + } +} + +void test_serializer_select_without_default() { + std::cout << "> SELECT serialization without default_expr..." << std::endl; + + auto expr = std::make_shared(ConstIntNode(42)); + auto attr = std::make_shared(ConstStringNode("key")); + + SelectNode select_node(expr, attr); + auto select = std::make_shared(select_node); + + IRModule module; + module.entry = select; + + Serializer ser; + auto bytes = ser.serialize_to_bytes(module); + + Deserializer deser; + auto loaded = deser.deserialize(bytes); + + auto *loaded_select = loaded.entry->get_if(); + if (loaded_select && + (!loaded_select->default_expr || !*loaded_select->default_expr)) { + std::cout << " PASS: SELECT without default_expr round-trips correctly" + << std::endl; + } else { + std::cerr << " FAIL: default_expr should be null/absent" << std::endl; + } +} + +void test_parser_brace_depth_in_strings() { + std::cout << "> Parser brace depth handling in strings..." << std::endl; + + std::string test_input = R"( + let s = "test}"; in ${s} + )"; + + std::cout << " Test input contains '}' inside string - should not end " + "interpolation" + << std::endl; + std::cout << " NOTE: This test requires running through actual parser" + << std::endl; +} + +void test_parser_has_ellipsis_usage() { + std::cout << "> Parser has_ellipsis usage..." << std::endl; + + std::cout << " NOTE: LambdaNode should have strict_pattern field when " + "has_ellipsis is false" + << std::endl; + std::cout << " This requires checking the parser output for strict patterns" + << std::endl; +} + +void test_parser_expect_in_speculative_parsing() { + std::cout << "> Parser expect() in speculative parsing..." << std::endl; + + std::cout << " NOTE: try_parse_lambda should not throw on non-lambda input" + << std::endl; + std::cout << " This requires testing parser with invalid lambda patterns" + << std::endl; +} + +int main() { + std::cout << "=== Regression Tests for Nixir ===" << std::endl << std::endl; + + test_enum_compatibility(); + std::cout << std::endl; + + test_serializer_select_with_default(); + std::cout << std::endl; + + test_serializer_select_without_default(); + std::cout << std::endl; + + test_parser_brace_depth_in_strings(); + std::cout << std::endl; + + test_parser_has_ellipsis_usage(); + std::cout << std::endl; + + test_parser_expect_in_speculative_parsing(); + std::cout << std::endl; + + std::cout << "=== Tests Complete ===" << std::endl; + std::cout << "Failures: " << failures << std::endl; + return failures > 0 ? 1 : 0; +} diff --git a/tests/simple.nixir b/tests/simple.nixir index ee4989f..3e26f83 100644 Binary files a/tests/simple.nixir and b/tests/simple.nixir differ diff --git a/tests/simple_op.nixir b/tests/simple_op.nixir index fa2a99e..18ffbd3 100644 Binary files a/tests/simple_op.nixir and b/tests/simple_op.nixir differ diff --git a/tests/unary.nixir b/tests/unary.nixir index 00c75c6..652fabc 100644 Binary files a/tests/unary.nixir and b/tests/unary.nixir differ