tests: cover flake refs and lexer/parser regressions
Signed-off-by: NotAShelf <raf@notashelf.dev> Change-Id: I1b5f90bbb210262a9287a9b8eac02e9d6a6a6964
This commit is contained in:
parent
b319ef6f3f
commit
531855d91a
4 changed files with 171 additions and 2 deletions
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue