clone
ssh://git@git.notashelf.dev:33/notashelf/nixir.gitCommit 531855d91ac4
tarballunverified4 files changed+171-2
@@ -352,8 +352,6 @@ public: template <typename T> bool holds() const { return std::holds_alternative<T>(data); } }; -- struct SourceFile { std::string path; std::string content;@@ -0,0 +1,19 @@+{+ description = "Local flake fixture for nixir integration tests";++ inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.11";++ outputs = { self, nixpkgs }: {+ value = 42;+ nixosConfigurations.demo = nixpkgs.lib.nixosSystem {+ system = "x86_64-linux";+ modules = [+ ({ ... }: {+ networking.hostName = "nixir-demo";+ system.stateVersion = "24.11";+ services.openssh.enable = true;+ })+ ];+ };+ };+}@@ -72,4 +72,32 @@ else fi echo "" +echo "Test 6: Flake Reference Compilation"+echo "-----------------------------------"+flake_ir=$(mktemp /tmp/nixir-flake-value-XXXXXX.nixir)+"$(pwd)/build/nix-irc" "$TEST_DIR/flake_ref#value" "$flake_ir"+result=$(nix-instantiate --plugin-files "$PLUGIN_PATH" --eval --strict --json --expr "builtins.nixIR_loadIR \"$flake_ir\"" 2>&1)+if echo "$result" | grep -q '^42$'; then+ echo "[PASS] Flake reference compiles and evaluates correctly"+else+ echo "[FAIL] Flake reference compilation broken"+ echo "$result"+ exit 1+fi+echo ""++echo "Test 7: NixOS Configuration Attribute Path"+echo "------------------------------------------"+config_ir=$(mktemp /tmp/nixir-flake-config-XXXXXX.nixir)+"$(pwd)/build/nix-irc" "$TEST_DIR/flake_ref#nixosConfigurations.demo.config.networking.hostName" "$config_ir"+result=$(nix-instantiate --plugin-files "$PLUGIN_PATH" --eval --strict --json --expr "builtins.nixIR_loadIR \"$config_ir\"" 2>&1)+if echo "$result" | grep -q '"nixir-demo"'; then+ echo "[PASS] Nested flake attribute selection works for nixosConfigurations"+else+ echo "[FAIL] NixOS configuration flake selection broken"+ echo "$result"+ exit 1+fi+echo ""+ echo "Integration Tests Complete"@@ -1,3 +1,4 @@+#include "irc/lexer.h" #include "irc/parser.h" #include "irc/serializer.h" #include "irc/types.h"@@ -166,6 +167,64 @@ void test_parser_expect_in_speculative_parsing() { } } +void test_implication_right_associativity() {+ std::cout << "> Implication right associativity..." << std::endl;++ Parser parser;+ auto ast = parser.parse("a -> b -> c");++ auto* outer = ast->get_if<BinaryOpNode>();+ TEST_CHECK(outer != nullptr, "Top-level node is BinaryOpNode");+ TEST_CHECK(outer && outer->op == BinaryOp::IMPL, "Top-level operator is implication");++ if (outer) {+ auto* left = outer->left->get_if<VarNode>();+ auto* right = outer->right->get_if<BinaryOpNode>();+ TEST_CHECK(left != nullptr && left->name && *left->name == "a", "Left branch is variable 'a'");+ TEST_CHECK(right != nullptr && right->op == BinaryOp::IMPL,+ "Right branch is nested implication");+ }+}++void test_lookup_path_lexer_position() {+ std::cout << "> Lookup path lexer position..." << std::endl;++ Lexer lexer("<nixpkgs> x");+ auto tokens = lexer.tokenize();++ TEST_CHECK(tokens.size() >= 3, "Lexer produced lookup path, identifier, and EOF");+ TEST_CHECK(tokens[0].type == Token::LOOKUP_PATH, "First token is LOOKUP_PATH");+ TEST_CHECK(tokens[1].type == Token::IDENT && tokens[1].value == "x",+ "Second token is identifier 'x'");+ TEST_CHECK(tokens[1].col == 11, "Identifier column reflects consumed lookup path width");+}++void test_unterminated_block_comment_rejected() {+ std::cout << "> Unterminated block comment rejection..." << std::endl;++ try {+ Lexer lexer("/* unterminated");+ auto tokens = lexer.tokenize();+ (void) tokens;+ TEST_FAIL("Lexer should reject unterminated block comments");+ } catch (const std::exception& e) {+ TEST_PASS("Lexer rejects unterminated block comments");+ }+}++void test_unknown_character_rejected() {+ std::cout << "> Unknown character rejection..." << std::endl;++ try {+ Lexer lexer("1 $ 2");+ auto tokens = lexer.tokenize();+ (void) tokens;+ TEST_FAIL("Lexer should reject unexpected characters");+ } catch (const std::exception& e) {+ TEST_PASS("Lexer rejects unexpected characters");+ }+}+ void test_lookup_path_node() { std::cout << "> Lookup path serialization..." << std::endl; @@ -233,6 +292,53 @@ void test_import_with_lookup_path() { } } +void test_relative_path_import_parsing() {+ std::cout << "> Relative path import parsing..." << std::endl;++ Parser parser;+ auto ast = parser.parse("import ./simple.nix");++ auto* import_node = ast->get_if<ImportNode>();+ TEST_CHECK(import_node != nullptr, "Parsed expression is ImportNode");++ if (import_node && import_node->path) {+ auto* path_node = import_node->path->get_if<ConstPathNode>();+ TEST_CHECK(path_node != nullptr, "Import argument is ConstPathNode");+ TEST_CHECK(path_node && path_node->value == "./simple.nix",+ "Relative path is preserved as './simple.nix'");+ }+}++void test_builtin_call_node() {+ std::cout << "> BuiltinCallNode serialization..." << std::endl;++ auto arg = std::make_shared<Node>(ConstStringNode("/tmp/example-flake"));+ auto builtin =+ std::make_shared<Node>(BuiltinCallNode("getFlake", std::vector<std::shared_ptr<Node>>{arg}));++ IRModule module;+ module.entry = builtin;++ Serializer ser;+ auto bytes = ser.serialize_to_bytes(module);++ Deserializer deser;+ auto loaded = deser.deserialize(bytes);++ auto* loaded_builtin = loaded.entry->get_if<BuiltinCallNode>();+ TEST_CHECK(loaded_builtin != nullptr, "Deserialized node is BuiltinCallNode");+ TEST_CHECK(loaded_builtin && loaded_builtin->builtin_name == "getFlake",+ "Builtin name is 'getFlake'");+ TEST_CHECK(loaded_builtin && loaded_builtin->args.size() == 1, "Builtin has one argument");++ if (loaded_builtin && loaded_builtin->args.size() == 1) {+ auto* loaded_arg = loaded_builtin->args[0]->get_if<ConstStringNode>();+ TEST_CHECK(loaded_arg != nullptr, "Builtin argument is ConstStringNode");+ TEST_CHECK(loaded_arg && loaded_arg->value == "/tmp/example-flake",+ "Builtin argument value round-trips");+ }+}+ void test_uri_node() { std::cout << "> URI node serialization..." << std::endl; @@ -642,6 +748,18 @@ int main() { test_parser_expect_in_speculative_parsing(); std::cout << std::endl; + test_implication_right_associativity();+ std::cout << std::endl;++ test_lookup_path_lexer_position();+ std::cout << std::endl;++ test_unterminated_block_comment_rejected();+ std::cout << std::endl;++ test_unknown_character_rejected();+ std::cout << std::endl;+ test_lookup_path_node(); std::cout << std::endl; @@ -651,6 +769,12 @@ int main() { test_import_with_lookup_path(); std::cout << std::endl; + test_relative_path_import_parsing();+ std::cout << std::endl;++ test_builtin_call_node();+ std::cout << std::endl;+ test_uri_node(); std::cout << std::endl;