diff --git a/editor/CMakeLists.txt b/editor/CMakeLists.txt index befab95..48891f8 100644 --- a/editor/CMakeLists.txt +++ b/editor/CMakeLists.txt @@ -3379,4 +3379,13 @@ target_link_libraries(step499_test PRIVATE tree_sitter_javascript tree_sitter_typescript tree_sitter_java tree_sitter_rust tree_sitter_go) +add_executable(step500_test tests/step500_test.cpp) +target_include_directories(step500_test PRIVATE src) +target_link_libraries(step500_test PRIVATE + nlohmann_json::nlohmann_json + unofficial::tree-sitter::tree-sitter + tree_sitter_python tree_sitter_cpp tree_sitter_elisp + tree_sitter_javascript tree_sitter_typescript + tree_sitter_java tree_sitter_rust tree_sitter_go) + # Step 12: Dear ImGui shell scaffolding created (main.cpp exists but not built due to dependencies) diff --git a/editor/src/LegacyModernizationScenarioRunner.h b/editor/src/LegacyModernizationScenarioRunner.h new file mode 100644 index 0000000..09338f8 --- /dev/null +++ b/editor/src/LegacyModernizationScenarioRunner.h @@ -0,0 +1,125 @@ +#pragma once + +// Step 500: Scenario - Legacy Modernization +// End-to-end modernization scenario for legacy C -> Rust migration. + +#include "APIBoundaryPreserver.h" +#include "LegacyIdiomDetector.h" +#include "MigrationExecutor.h" +#include "MigrationPlanGenerator.h" +#include "MigrationTestGenerator.h" +#include "ModernizationSuggester.h" +#include "ModernizationWorkflow.h" +#include "SafetyAuditor.h" + +#include +#include + +struct LegacyModernizationScenarioResult { + std::string legacySource; + LegacyAnalysisReport legacyReport; + SafetyReport safetyReport; + ModernizationReport modernizationReport; + ModernizationWorkflow modernizationWorkflow; + MigrationPlan migrationPlan; + MigrationExecution migrationExecution; + APIBoundaryReport apiBoundaryOriginal; + APIBoundaryReport apiBoundaryMigrated; + bool apiBoundaryPreserved = false; + bool securityOwnershipStrengthened = false; + MigrationTestSuite migrationTests; + std::vector notes; +}; + +class LegacyModernizationScenarioRunner { +public: + static LegacyModernizationScenarioResult run() { + LegacyModernizationScenarioResult out; + out.legacySource = sampleLegacySource(); + + out.legacyReport = LegacyIdiomDetector::analyzeFile(out.legacySource, "c", "legacy.c"); + out.safetyReport = SafetyAuditor::audit(out.legacySource, "c", "legacy.c"); + out.modernizationReport = + ModernizationSuggester::suggest(out.legacyReport, out.safetyReport, "rust"); + out.modernizationWorkflow = + ModernizationWorkflowGenerator::generate(out.legacySource, out.modernizationReport); + + auto files = sampleProjectFiles(); + out.migrationPlan = MigrationPlanGenerator::generate("legacy_modernization", files, "c", "rust"); + out.migrationExecution = MigrationExecutor::createExecution(out.migrationPlan, files); + + if (!out.migrationPlan.units.empty()) { + const auto& unit = out.migrationPlan.units.front(); + out.apiBoundaryOriginal = APIBoundaryPreserver::analyze(files.front().source, unit); + out.apiBoundaryMigrated = APIBoundaryPreserver::analyze(sampleMigratedRustSource(), unit); + out.apiBoundaryPreserved = + APIBoundaryPreserver::verifyPreservation(out.apiBoundaryOriginal, out.apiBoundaryMigrated); + out.migrationTests = MigrationTestGenerator::generate(out.apiBoundaryOriginal); + } + + out.securityOwnershipStrengthened = + out.modernizationReport.hasSuggestionTo("Rust ownership + borrow checker"); + + out.notes.push_back("Legacy C modernization scenario executed"); + out.notes.push_back("Pipeline: ingest -> audit -> suggest -> workflow -> migration plan"); + return out; + } + +private: + static std::string sampleLegacySource() { + return + "#include \n" + "#include \n" + "#include \n" + "int shorten(const char* input, char* out) {\n" + " char* p = (char*)malloc(32);\n" + " if (!p) return -1;\n" + " strcpy(p, input);\n" + " sprintf(out, \"%s\", p);\n" + " char c = *(p + 1);\n" + " free(p);\n" + " return (int)c;\n" + "}\n"; + } + + static std::string sampleMigratedRustSource() { + return + "#[no_mangle]\n" + "pub extern \"C\" fn clamp_small(x: i32, lo: i32, hi: i32) -> i32 { x }\n"; + } + + static std::vector sampleProjectFiles() { + std::vector files; + files.push_back({ + "src/simple_utils.c", + "int clamp_small(int x, int lo, int hi) {\n" + " if (x < lo) return lo;\n" + " if (x > hi) return hi;\n" + " return x;\n" + "}\n", + {"clamp_small"}, + {} + }); + + std::string complex = + "int normalize_token(const char* in, char* out) {\n" + " int score = 0;\n" + " if (!in || !out) return -1;\n" + " out[0] = '\\0';\n"; + for (int i = 0; i < 58; ++i) { + complex += " score += " + std::to_string((i % 5) + 1) + ";\n"; + } + complex += + " if (score > 1000) score = 1000;\n" + " return score;\n" + "}\n"; + + files.push_back({ + "src/complex_transform.c", + complex, + {"normalize_token"}, + {} + }); + return files; + } +}; diff --git a/editor/tests/step500_test.cpp b/editor/tests/step500_test.cpp new file mode 100644 index 0000000..d75edc3 --- /dev/null +++ b/editor/tests/step500_test.cpp @@ -0,0 +1,147 @@ +// Step 500: Scenario - Legacy Modernization Tests (12 tests) + +#include "LegacyModernizationScenarioRunner.h" + +#include + +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 {} + +static LegacyModernizationScenarioResult runNow() { + return LegacyModernizationScenarioRunner::run(); +} + +void test_legacy_ingestion_detects_c_idioms_and_legacy_score() { + TEST(legacy_ingestion_detects_c_idioms_and_legacy_score); + auto r = runNow(); + CHECK(r.legacyReport.language == "c", "expected c language"); + CHECK(r.legacyReport.legacyScore > 0, "expected non-zero legacy score"); + CHECK(r.legacyReport.hasPattern("manual-memory-management"), "missing manual memory pattern"); + PASS(); +} + +void test_safety_audit_flags_memory_and_buffer_risks() { + TEST(safety_audit_flags_memory_and_buffer_risks); + auto r = runNow(); + CHECK(r.safetyReport.overallRisk >= 2, "expected elevated safety risk"); + CHECK(r.safetyReport.hasCategory("buffer-overflow"), "missing buffer-overflow finding"); + CHECK(r.safetyReport.hasCategory("use-after-free"), "missing use-after-free finding"); + PASS(); +} + +void test_modernization_suggestions_include_rust_target_and_actions() { + TEST(modernization_suggestions_include_rust_target_and_actions); + auto r = runNow(); + CHECK(r.modernizationReport.sourceLanguage == "c", "expected c source"); + CHECK(r.modernizationReport.targetLanguage == "rust", "expected rust target"); + CHECK(!r.modernizationReport.suggestions.empty(), "expected modernization suggestions"); + PASS(); +} + +void test_security_annotations_strengthened_for_rust_ownership_model() { + TEST(security_annotations_strengthened_for_rust_ownership_model); + auto r = runNow(); + CHECK(r.securityOwnershipStrengthened, "expected rust ownership strengthening"); + CHECK(r.modernizationReport.hasSuggestionTo("Rust ownership + borrow checker"), + "missing rust ownership suggestion"); + PASS(); +} + +void test_workflow_contains_deterministic_and_llm_routing_paths() { + TEST(workflow_contains_deterministic_and_llm_routing_paths); + auto r = runNow(); + CHECK(r.modernizationWorkflow.countByRouting(WorkItemRouting::Deterministic) > 0, + "expected deterministic work items"); + CHECK(r.modernizationWorkflow.countByRouting(WorkItemRouting::LLM) > 0, + "expected llm work items"); + PASS(); +} + +void test_workflow_prioritizes_quick_wins_before_harder_refactors() { + TEST(workflow_prioritizes_quick_wins_before_harder_refactors); + auto r = runNow(); + auto ordered = r.modernizationWorkflow.itemsInOrder(); + CHECK(!ordered.empty(), "expected ordered workflow items"); + CHECK(ordered.front().effort == ModernizeEffort::QuickWin, + "first workflow item should be quick win"); + PASS(); +} + +void test_migration_plan_targets_rust_and_has_units() { + TEST(migration_plan_targets_rust_and_has_units); + auto r = runNow(); + CHECK(r.migrationPlan.sourceLanguage == "c", "expected c source migration plan"); + CHECK(r.migrationPlan.targetLanguage == "rust", "expected rust migration plan target"); + CHECK(r.migrationPlan.units.size() >= 2, "expected multiple migration units"); + PASS(); +} + +void test_migration_plan_has_mixed_routing_deterministic_and_llm() { + TEST(migration_plan_has_mixed_routing_deterministic_and_llm); + auto r = runNow(); + CHECK(r.migrationPlan.countByRouting(MigrationRouting::Deterministic) > 0, + "expected deterministic migration unit"); + CHECK(r.migrationPlan.countByRouting(MigrationRouting::LLM) > 0, + "expected llm migration unit"); + PASS(); +} + +void test_api_boundary_preservation_is_verified() { + TEST(api_boundary_preservation_is_verified); + auto r = runNow(); + CHECK(r.apiBoundaryOriginal.hasFunction("clamp_small"), "original api function missing"); + CHECK(r.apiBoundaryMigrated.hasFunction("clamp_small"), "migrated api function missing"); + CHECK(r.apiBoundaryPreserved, "api boundary preservation should verify"); + PASS(); +} + +void test_ffi_annotations_are_available_for_rust_boundary() { + TEST(ffi_annotations_are_available_for_rust_boundary); + auto r = runNow(); + CHECK(!r.apiBoundaryOriginal.ffiBoundaries.empty(), "expected ffi boundaries"); + CHECK(r.apiBoundaryOriginal.ffiBoundaries[0].annotation.find("FFI") != std::string::npos, + "missing ffi annotation"); + PASS(); +} + +void test_migration_tests_are_generated_from_preserved_api_surface() { + TEST(migration_tests_are_generated_from_preserved_api_surface); + auto r = runNow(); + CHECK(!r.migrationTests.tests.empty(), "expected generated migration tests"); + CHECK(r.migrationTests.targetLanguage == "rust", "expected rust migration tests"); + CHECK(r.migrationTests.hasTestFor("clamp_small"), "missing tests for preserved api"); + PASS(); +} + +void test_scenario_notes_capture_end_to_end_modernization_flow() { + TEST(scenario_notes_capture_end_to_end_modernization_flow); + auto r = runNow(); + CHECK(r.notes.size() >= 2, "expected scenario notes"); + CHECK(r.notes[0].find("Legacy C modernization scenario executed") != std::string::npos, + "missing execution note"); + PASS(); +} + +int main() { + std::cout << "Step 500: Scenario - Legacy Modernization Tests\n"; + + test_legacy_ingestion_detects_c_idioms_and_legacy_score(); // 1 + test_safety_audit_flags_memory_and_buffer_risks(); // 2 + test_modernization_suggestions_include_rust_target_and_actions(); // 3 + test_security_annotations_strengthened_for_rust_ownership_model(); // 4 + test_workflow_contains_deterministic_and_llm_routing_paths(); // 5 + test_workflow_prioritizes_quick_wins_before_harder_refactors(); // 6 + test_migration_plan_targets_rust_and_has_units(); // 7 + test_migration_plan_has_mixed_routing_deterministic_and_llm(); // 8 + test_api_boundary_preservation_is_verified(); // 9 + test_ffi_annotations_are_available_for_rust_boundary(); // 10 + test_migration_tests_are_generated_from_preserved_api_surface(); // 11 + test_scenario_notes_capture_end_to_end_modernization_flow(); // 12 + + std::cout << "\nResults: " << passed << "/" << (passed + failed) + << " passed\n"; + return failed == 0 ? 0 : 1; +} diff --git a/progress.md b/progress.md index 3a6f030..108614e 100644 --- a/progress.md +++ b/progress.md @@ -7645,3 +7645,36 @@ Implements the first Phase 25b end-to-end scenario for: - `editor/src/GreenfieldScenarioRunner.h` within header-size limit (`153` <= `600`) - `editor/tests/step499_test.cpp` within test-file size guidance (`191` lines) - Header-only architecture and naming conventions remain aligned with `ARCHITECTURE.md` + +### Step 500: Scenario - Legacy Modernization +**Status:** PASS (12/12 tests) + +Implements the second Phase 25b end-to-end scenario for modernizing a legacy C codebase +with Rust as the migration target. + +**Files added:** +- `editor/src/LegacyModernizationScenarioRunner.h` - scenario orchestration across: + - legacy C ingestion and idiom detection + - safety audit and modernization suggestion generation + - modernization workflow generation with mixed routing + - migration plan + execution initialization (`c` -> `rust`) + - API boundary analysis/preservation verification + - migration test generation from preserved API surface +- `editor/tests/step500_test.cpp` - 12 scenario tests covering: + - legacy/safety finding expectations + - Rust-target modernization suggestions (including ownership strengthening) + - deterministic + llm workflow and migration routing + - API boundary + FFI annotation verification + - migration test-suite generation and scenario notes +- `editor/CMakeLists.txt` - `step500_test` target + +**Verification run:** +- `cmake -S editor -B editor/build-native` - PASS +- `cmake --build editor/build-native --target step500_test step499_test` - PASS +- `./editor/build-native/step500_test` - PASS (12/12) +- `./editor/build-native/step499_test` - PASS (12/12) regression coverage + +**Architecture gate check:** +- `editor/src/LegacyModernizationScenarioRunner.h` within header-size limit (`125` <= `600`) +- `editor/tests/step500_test.cpp` within test-file size guidance (`147` lines) +- Header-only architecture and naming conventions remain aligned with `ARCHITECTURE.md`