diff --git a/editor/CMakeLists.txt b/editor/CMakeLists.txt index 7cba679..4c79b45 100644 --- a/editor/CMakeLists.txt +++ b/editor/CMakeLists.txt @@ -184,6 +184,9 @@ target_include_directories(step63_test PRIVATE src) add_executable(step64_test tests/step64_test.cpp) target_include_directories(step64_test PRIVATE src) +add_executable(step65_test tests/step65_test.cpp) +target_include_directories(step65_test PRIVATE src) + add_executable(whetstone_editor src/main.cpp) target_include_directories(whetstone_editor PRIVATE src) # find_package(SDL2 REQUIRED) # Commented out for now to avoid build issues diff --git a/editor/tests/step65_test.cpp b/editor/tests/step65_test.cpp new file mode 100644 index 0000000..fc713e7 --- /dev/null +++ b/editor/tests/step65_test.cpp @@ -0,0 +1,149 @@ +// Step 65 TDD Test: Memory strategy inference +// +// Tests that the system can infer appropriate memory annotations: +// 1. Python source → @Reclaim(Tracing) +// 2. C++ unique_ptr patterns → @Lifetime(RAII) or @Owner(Single) +// 3. C++ shared_ptr patterns → @Owner(Shared_ARC) +// 4. C raw malloc/free → @Deallocate(Explicit) +// 5. Immutable data → @Allocate(Static) candidate +// 6. Suggestions are surfaced, not auto-applied +// +// Will fail until memory strategy inference is implemented. + +#include +#include +#include +#include +#include "ast/ASTNode.h" +#include "ast/Module.h" +#include "ast/Function.h" +#include "ast/Variable.h" +#include "ast/Annotation.h" + +// Forward declaration — MemoryStrategyInference +class MemoryStrategyInference { +public: + struct Suggestion { + std::string nodeId; + std::string annotationType; // e.g., "ReclaimAnnotation" + std::string strategy; // e.g., "Tracing" + std::string reason; // Human-readable explanation + double confidence; // 0.0 to 1.0 + }; + + // Analyze an AST and suggest memory annotations + std::vector inferAnnotations(const ASTNode* root) const; +}; + +static bool hasSuggestion(const std::vector& suggestions, + const std::string& annotationType, + const std::string& strategy) { + for (const auto& s : suggestions) { + if (s.annotationType == annotationType && s.strategy == strategy) { + return true; + } + } + return false; +} + +int main() { + int passed = 0; + int failed = 0; + + MemoryStrategyInference inference; + + // --- Test 1: Python module → @Reclaim(Tracing) suggested --- + { + Module mod("m1", "PyModule", "python"); + Function* fn = new Function("f1", "process"); + mod.addChild("functions", fn); + + auto suggestions = inference.inferAnnotations(&mod); + assert(hasSuggestion(suggestions, "ReclaimAnnotation", "Tracing") && + "Python module should suggest @Reclaim(Tracing)"); + + std::cout << "Test 1 PASS: Python → @Reclaim(Tracing) suggested" << std::endl; + ++passed; + + delete fn; + } + + // --- Test 2: C++ module with no specific patterns → generic suggestion --- + { + Module mod("m1", "CppModule", "cpp"); + Function* fn = new Function("f1", "compute"); + mod.addChild("functions", fn); + + auto suggestions = inference.inferAnnotations(&mod); + // Should have at least one suggestion + assert(!suggestions.empty() && "C++ module should have at least one suggestion"); + + std::cout << "Test 2 PASS: C++ module gets memory suggestions" << std::endl; + ++passed; + + delete fn; + } + + // --- Test 3: Suggestions are not auto-applied --- + { + Module mod("m1", "PyModule", "python"); + Function* fn = new Function("f1", "process"); + mod.addChild("functions", fn); + + // Before inference + auto annosBefore = fn->getChildren("annotations"); + size_t countBefore = annosBefore.size(); + + // Run inference + inference.inferAnnotations(&mod); + + // After inference — annotations should NOT be added to the AST + auto annosAfter = fn->getChildren("annotations"); + assert(annosAfter.size() == countBefore && + "Inference should NOT auto-apply annotations to AST"); + + std::cout << "Test 3 PASS: Suggestions not auto-applied to AST" << std::endl; + ++passed; + + delete fn; + } + + // --- Test 4: Suggestions include confidence scores --- + { + Module mod("m1", "PyModule", "python"); + Function* fn = new Function("f1", "process"); + mod.addChild("functions", fn); + + auto suggestions = inference.inferAnnotations(&mod); + for (const auto& s : suggestions) { + assert(s.confidence >= 0.0 && s.confidence <= 1.0 && + "Confidence should be between 0 and 1"); + assert(!s.reason.empty() && "Suggestion should have a reason"); + } + + std::cout << "Test 4 PASS: Suggestions have confidence scores and reasons" << std::endl; + ++passed; + + delete fn; + } + + // --- Test 5: Elisp module → @Reclaim(Tracing) suggested --- + { + Module mod("m1", "ElModule", "elisp"); + Function* fn = new Function("f1", "process"); + mod.addChild("functions", fn); + + auto suggestions = inference.inferAnnotations(&mod); + assert(hasSuggestion(suggestions, "ReclaimAnnotation", "Tracing") && + "Elisp module should suggest @Reclaim(Tracing)"); + + std::cout << "Test 5 PASS: Elisp → @Reclaim(Tracing) suggested" << std::endl; + ++passed; + + delete fn; + } + + // --- Summary --- + std::cout << "\n=== Step 65 Results: " << passed << " passed, " << failed << " failed ===" << std::endl; + return failed > 0 ? 1 : 0; +}