// Step 312: C# Generator Tests (12 tests) // Verifies CSharpGenerator produces idiomatic C# from Whetstone AST: // public void, Allman braces, foreach, class, interface, async Task, await, // lambda, generic types, decorator → [Attribute]. #include "ast/CSharpGenerator.h" #include "ast/Module.h" #include "ast/Function.h" #include "ast/Variable.h" #include "ast/Parameter.h" #include "ast/Statement.h" #include "ast/Expression.h" #include "ast/Type.h" #include "ast/ClassDeclaration.h" #include "ast/GenericType.h" #include "ast/AsyncNodes.h" #include "ast/Serialization.h" #include #include #include #include using json = nlohmann::json; static int passed = 0, failed = 0; #define TEST(name) { std::cout << " " << #name << "... "; } #define PASS() { std::cout << "PASS\n"; ++passed; } #define FAIL(msg) { std::cout << "FAIL: " << msg << "\n"; ++failed; } #define CHECK(cond, msg) if (!(cond)) { FAIL(msg); return; } else {} // 1. Generate a simple function → public void void test_generate_function() { TEST(generate_function); auto fn = std::make_unique("fn1", "DoWork"); auto retStmt = std::make_unique(); retStmt->id = "r1"; auto retVal = std::make_unique("s1", "done"); retStmt->addChild("value", retVal.release()); fn->addChild("body", retStmt.release()); CSharpGenerator gen; std::string out = gen.generate(fn.get()); CHECK(out.find("public void DoWork") != std::string::npos, "missing 'public void DoWork'"); CHECK(out.find("return \"done\"") != std::string::npos, "missing return statement"); CHECK(out.find("{\n") != std::string::npos, "missing Allman-style opening brace"); PASS(); } // 2. Generate variable → var/type void test_generate_variable() { TEST(generate_variable); auto var = std::make_unique("v1", "count"); auto type = std::make_unique("pt1", "int"); var->addChild("type", type.release()); auto init = std::make_unique("il1", 42); var->addChild("initializer", init.release()); CSharpGenerator gen; std::string out = gen.generate(var.get()); CHECK(out.find("int count") != std::string::npos, "missing 'int count'"); CHECK(out.find("= 42") != std::string::npos, "missing initial value"); CHECK(out.find(";") != std::string::npos, "missing semicolon"); PASS(); } // 3. Type mappings void test_type_mappings() { TEST(type_mappings); CSharpGenerator gen; PrimitiveType intT("t1", "int"); PrimitiveType strT("t2", "string"); PrimitiveType boolT("t3", "bool"); PrimitiveType voidT("t4", "void"); PrimitiveType floatT("t5", "float"); PrimitiveType dblT("t6", "double"); CHECK(gen.generate(&intT) == "int", "int should map to int"); CHECK(gen.generate(&strT) == "string", "string should map to string"); CHECK(gen.generate(&boolT) == "bool", "bool should map to bool"); CHECK(gen.generate(&voidT) == "void", "void should map to void"); CHECK(gen.generate(&floatT) == "float", "float should map to float"); CHECK(gen.generate(&dblT) == "double", "double should map to double"); PASS(); } // 4. Generate class with inheritance (Allman braces) void test_generate_class() { TEST(generate_class); auto cls = std::make_unique("cls1", "Dog"); cls->superClass = "Animal"; auto field = std::make_unique("f1", "breed"); cls->addChild("fields", field.release()); auto meth = std::make_unique("m1", "Bark"); meth->className = "Dog"; meth->visibility = "public"; cls->addChild("methods", meth.release()); CSharpGenerator gen; std::string out = gen.generate(cls.get()); CHECK(out.find("class Dog") != std::string::npos, "missing 'class Dog'"); CHECK(out.find(": Animal") != std::string::npos, "missing ': Animal' C# inheritance"); CHECK(out.find("breed") != std::string::npos, "missing field"); CHECK(out.find("Bark") != std::string::npos, "missing method"); PASS(); } // 5. Generate interface void test_generate_interface() { TEST(generate_interface); auto iface = std::make_unique("if1", "IRenderable"); auto m1 = std::make_unique("m1", "Render"); auto m2 = std::make_unique("m2", "Update"); iface->addChild("methods", m1.release()); iface->addChild("methods", m2.release()); CSharpGenerator gen; std::string out = gen.generate(iface.get()); CHECK(out.find("interface IRenderable") != std::string::npos, "missing 'interface IRenderable'"); CHECK(out.find("Render") != std::string::npos, "missing 'Render'"); CHECK(out.find("Update") != std::string::npos, "missing 'Update'"); PASS(); } // 6. Generate async Task method void test_generate_async_function() { TEST(generate_async_function); auto af = std::make_unique("af1", "LoadAsync"); auto awaitExpr = std::make_unique("aw1"); auto call = std::make_unique(); call->id = "fc1"; call->functionName = "FetchData"; awaitExpr->addChild("expression", call.release()); auto stmt = std::make_unique(); stmt->id = "es1"; stmt->addChild("expression", awaitExpr.release()); af->addChild("body", stmt.release()); CSharpGenerator gen; std::string out = gen.generate(af.get()); CHECK(out.find("public async Task LoadAsync") != std::string::npos, "missing 'public async Task LoadAsync'"); CHECK(out.find("await FetchData()") != std::string::npos, "missing 'await FetchData()'"); PASS(); } // 7. Generate C# lambda (x) => body void test_generate_lambda() { TEST(generate_lambda); auto lam = std::make_unique("lam1"); auto p1 = std::make_unique("p1", "x"); lam->addChild("parameters", p1.release()); auto body = std::make_unique("bo1", "*"); body->addChild("left", new VariableReference("vr1", "x")); body->addChild("right", new IntegerLiteral("il1", 2)); lam->addChild("body", body.release()); CSharpGenerator gen; std::string out = gen.generate(lam.get()); CHECK(out.find("(x) =>") != std::string::npos, "missing '(x) =>' lambda syntax"); CHECK(out.find("x * 2") != std::string::npos, "missing lambda body"); PASS(); } // 8. Generate generic type void test_generate_generic_type() { TEST(generate_generic_type); auto gen_type = std::make_unique("g1", "Dictionary"); auto tp1 = std::make_unique("tp1", "TKey"); auto tp2 = std::make_unique("tp2", "TValue"); gen_type->addChild("typeParameters", tp1.release()); gen_type->addChild("typeParameters", tp2.release()); CSharpGenerator gen; std::string out = gen.generate(gen_type.get()); CHECK(out.find("Dictionary") != std::string::npos, "missing 'Dictionary'"); PASS(); } // 9. Generate decorator annotation → [Attribute] void test_generate_decorator() { TEST(generate_decorator); auto dec = std::make_unique("dec1", "Serializable"); CSharpGenerator gen; std::string out = gen.generate(dec.get()); CHECK(out.find("[Serializable]") != std::string::npos, "missing '[Serializable]'"); PASS(); } // 10. Collection types: List<>, HashSet<>, Dictionary<> void test_collection_types() { TEST(collection_types); CSharpGenerator gen; auto listT = std::make_unique(); listT->id = "lt1"; auto listEl = std::make_unique("pt1", "string"); listT->addChild("elementType", listEl.release()); CHECK(gen.generate(listT.get()).find("List") != std::string::npos, "List expected"); auto setT = std::make_unique(); setT->id = "st1"; auto setEl = std::make_unique("pt2", "int"); setT->addChild("elementType", setEl.release()); CHECK(gen.generate(setT.get()).find("HashSet") != std::string::npos, "HashSet expected"); auto mapT = std::make_unique(); mapT->id = "mt1"; auto kT = std::make_unique("pt3", "string"); auto vT = std::make_unique("pt4", "int"); mapT->addChild("keyType", kT.release()); mapT->addChild("valueType", vT.release()); CHECK(gen.generate(mapT.get()).find("Dictionary") != std::string::npos, "Dictionary expected"); PASS(); } // 11. Generate foreach loop void test_generate_foreach_loop() { TEST(generate_foreach_loop); auto loop = std::make_unique(); loop->id = "fl1"; loop->iteratorName = "item"; auto iter = std::make_unique("vr1", "collection"); loop->addChild("iterable", iter.release()); auto bodyStmt = std::make_unique(); bodyStmt->id = "es1"; auto call = std::make_unique(); call->id = "fc1"; call->functionName = "Process"; call->addChild("arguments", new VariableReference("vr2", "item")); bodyStmt->addChild("expression", call.release()); loop->addChild("body", bodyStmt.release()); CSharpGenerator gen; std::string out = gen.generate(loop.get()); CHECK(out.find("foreach") != std::string::npos, "missing 'foreach'"); CHECK(out.find("item") != std::string::npos, "missing iterator 'item'"); CHECK(out.find("collection") != std::string::npos, "missing 'collection'"); PASS(); } // 12. Full module generation void test_generate_full_module() { TEST(generate_full_module); auto mod = std::make_unique("mod1", "test_module", "csharp"); auto var = std::make_unique("v1", "version"); auto init = std::make_unique("s1", "2.0"); var->addChild("initializer", init.release()); mod->addChild("variables", var.release()); auto fn = std::make_unique("fn1", "Main"); auto retStmt = std::make_unique(); retStmt->id = "r1"; fn->addChild("body", retStmt.release()); mod->addChild("functions", fn.release()); CSharpGenerator gen; std::string out = gen.generate(mod.get()); CHECK(out.find("Module:") != std::string::npos, "missing Module comment"); CHECK(out.find("version") != std::string::npos, "missing variable"); CHECK(out.find("public void Main") != std::string::npos, "missing function"); PASS(); } int main() { std::cout << "Step 312: C# Generator Tests\n"; test_generate_function(); // 1 test_generate_variable(); // 2 test_type_mappings(); // 3 test_generate_class(); // 4 test_generate_interface(); // 5 test_generate_async_function(); // 6 test_generate_lambda(); // 7 test_generate_generic_type(); // 8 test_generate_decorator(); // 9 test_collection_types(); // 10 test_generate_foreach_loop(); // 11 test_generate_full_module(); // 12 std::cout << "\nResults: " << passed << "/" << (passed + failed) << " passed\n"; return failed == 0 ? 0 : 1; }