# Enhanced Test Harness Plan with Mock Generation ## Status Update (2026-01-22) ### Recent Changes ✅ 1. **Output parameter implemented** - Parser now supports `--output=` instead of only stdout 2. **AST validation added** - Generated code is parsed with `std.zig.Ast` for syntax validation 3. **Critical bug fixes**: - Fixed pointer type conversion (`?*Type` instead of `*Type`) - Fixed struct field parsing for pointer types - Handles both `SDL_Foo *` and `SDL_Foo*` pointer formats 4. **Usage updated** - Help text now shows both redirect and --output options ### Remaining Tasks - Mock generation (`--mocks` flag) - **NOT YET IMPLEMENTED** - Test project infrastructure - Complete AST rendering (currently warns only, doesn't reformat) - Fix remaining 59 syntax errors in full SDL_gpu.h output ## Overview This plan extends the original test harness to: 1. **Generate C mocks** - Parser creates mock C implementations when `--mocks` flag is passed ⚠️ TODO 2. **Build complete test project** - Compile C mocks + generated Zig bindings ⚠️ TODO 3. **Exercise all functions** - Call every generated wrapper function to verify linkage ⚠️ TODO ## Objectives ### Primary Goals 1. ✅ **Compilation validation** - Verify generated Zig code compiles (DONE: AST parsing validates) 2. ⚠️ **Mock generation** - Auto-generate minimal C mock implementations (TODO) 3. ⚠️ **Linkage testing** - Ensure all Zig wrappers link to C mocks correctly (TODO) 4. ⚠️ **Function coverage** - Call every generated function at least once (TODO) 5. ⚠️ **Runtime testing** - Verify functions execute without crashes (TODO) ### Secondary Goals - Detect ABI mismatches between generated bindings and C mocks - Provide template for integration testing with real SDL3 - Create reproducible test environment - ✅ AST-based formatting of generated code (partially done: validates, needs full render) ## Architecture Overview ``` ┌─────────────────────────────────────────────────────────────┐ │ Test Harness Workflow │ └─────────────────────────────────────────────────────────────┘ 1. Parse Header with --output and optional --mocks ┌──────────────┐ │ SDL_gpu.h │ └──────┬───────┘ │ v ┌──────────────┐ --output=gpu.zig [--mocks] │ sdl-parser │──────────────┐ └──────┬───────┘ │ │ │ v v ┌──────────────┐ ┌──────────────┐ │ gpu.zig │ │ gpu_mock.c │ (TODO) │ (bindings) │ │ (C mocks) │ └──────────────┘ └──────────────┘ │ v ┌──────────────┐ │ std.zig.Ast │ (validates syntax) └──────────────┘ 2. Build Test Project ┌──────────────┐ ┌──────────────┐ │ gpu.zig │ │ gpu_mock.c │ └──────┬───────┘ └──────┬───────┘ │ │ └──────────┬───────────┘ v ┌──────────────┐ │ build.zig │ │ (test proj) │ └──────┬───────┘ v ┌──────────────┐ │ test binary │ └──────────────┘ 3. Run Tests ┌──────────────┐ │ test_main.zig│ └──────┬───────┘ │ v ┌─────────────────────────────┐ │ Call all wrapper functions │ │ - Opaque type creation │ │ - Enum usage │ │ - Struct initialization │ │ - Flag manipulation │ │ - Function calls │ └─────────────────────────────┘ │ v ┌──────────────┐ │ ✅ Success │ │ ❌ Failure │ └──────────────┘ ``` ## Part 1: Mock Generation in Parser ### Requirements **Input**: C header file + `--mocks` flag **Output**: - `gpu.zig` - Zig bindings (as before) - `gpu_mock.c` - C mock implementations - `gpu_mock.h` - C mock header (optional, for documentation) ### Mock Generation Strategy For each C declaration, generate minimal stub: #### Opaque Types ```c // Input: typedef struct SDL_GPUDevice SDL_GPUDevice; // Mock: (no code needed - just forward declaration) ``` #### Functions ```c // Input: // extern SDL_DECLSPEC SDL_GPUDevice* SDLCALL SDL_CreateGPUDevice(bool debug_mode); // Mock: SDL_GPUDevice* SDL_CreateGPUDevice(bool debug_mode) { (void)debug_mode; return NULL; // Safe stub: return null pointer } ``` For functions returning primitives: ```c // Input: // extern SDL_DECLSPEC bool SDLCALL SDL_GPUSupportsShaderFormats(...); // Mock: bool SDL_GPUSupportsShaderFormats(SDL_GPUShaderFormat format_flags, const char *name) { (void)format_flags; (void)name; return false; // Safe stub: return false/0 } ``` For void functions: ```c // Input: // extern SDL_DECLSPEC void SDLCALL SDL_DestroyGPUDevice(SDL_GPUDevice *device); // Mock: void SDL_DestroyGPUDevice(SDL_GPUDevice *device) { (void)device; // No-op } ``` ### Implementation in Parser #### Add Mock Code Generator **File**: `mock_codegen.zig` (new file) ```zig const std = @import("std"); const patterns = @import("patterns.zig"); pub const MockCodeGen = struct { decls: []patterns.Declaration, allocator: std.mem.Allocator, output: std.ArrayList(u8), pub fn generate(allocator: std.mem.Allocator, decls: []patterns.Declaration) ![]const u8 { var gen = MockCodeGen{ .decls = decls, .allocator = allocator, .output = try std.ArrayList(u8).initCapacity(allocator, 4096), }; try gen.writeHeader(); try gen.writeMocks(); return try gen.output.toOwnedSlice(allocator); } fn writeHeader(self: *MockCodeGen) !void { const header = \\// Auto-generated C mock implementations \\// DO NOT EDIT - Generated by sdl-parser --mocks \\ \\#include \\#include \\ \\// Forward declarations for opaque types \\ ; try self.output.appendSlice(self.allocator, header); } fn writeMocks(self: *MockCodeGen) !void { // Write opaque type forward declarations for (self.decls) |decl| { if (decl == .opaque_type) { const opaque = decl.opaque_type; try self.output.writer(self.allocator).print( "typedef struct {s} {s};\n", .{opaque.name, opaque.name} ); } } try self.output.appendSlice(self.allocator, "\n// Function implementations\n\n"); // Write function mocks for (self.decls) |decl| { if (decl == .function_decl) { try self.writeFunctionMock(decl.function_decl); } } } fn writeFunctionMock(self: *MockCodeGen, func: patterns.FunctionDecl) !void { // Write return type try self.output.appendSlice(self.allocator, func.return_type); try self.output.appendSlice(self.allocator, " "); // Write function name try self.output.appendSlice(self.allocator, func.name); try self.output.appendSlice(self.allocator, "("); // Write parameters if (func.params.len == 0) { try self.output.appendSlice(self.allocator, "void"); } else { for (func.params, 0..) |param, i| { if (i > 0) { try self.output.appendSlice(self.allocator, ", "); } try self.output.appendSlice(self.allocator, param.type_name); if (param.name.len > 0) { try self.output.appendSlice(self.allocator, " "); try self.output.appendSlice(self.allocator, param.name); } } } try self.output.appendSlice(self.allocator, ") {\n"); // Write function body // Void all parameters to avoid unused warnings for (func.params) |param| { if (param.name.len > 0) { try self.output.writer(self.allocator).print(" (void){s};\n", .{param.name}); } } // Return appropriate value const return_value = getDefaultReturnValue(func.return_type); if (return_value.len > 0) { try self.output.writer(self.allocator).print(" return {s};\n", .{return_value}); } try self.output.appendSlice(self.allocator, "}\n\n"); } fn getDefaultReturnValue(return_type: []const u8) []const u8 { if (std.mem.eql(u8, return_type, "void")) { return ""; } else if (std.mem.indexOf(u8, return_type, "*") != null) { return "NULL"; // Pointer types } else if (std.mem.eql(u8, return_type, "bool")) { return "false"; } else if (std.mem.eql(u8, return_type, "int") or std.mem.indexOf(u8, return_type, "int") != null) { return "0"; } else if (std.mem.eql(u8, return_type, "float") or std.mem.eql(u8, return_type, "double")) { return "0.0"; } else { // For enum/struct types, return zero-initialized return "0"; } } }; ``` #### Update Parser Main **File**: `parser.zig` - **STATUS: PARTIALLY DONE** ```zig pub fn main() !void { // ... existing setup ... const args = try std.process.argsAlloc(allocator); defer std.process.argsFree(allocator, args); if (args.len < 2) { // ✅ DONE: Updated usage message std.debug.print("Usage: {s} [--output=] [--mocks]\n", .{args[0]}); return error.MissingArgument; } const header_path = args[1]; // ✅ DONE: Parse --output parameter var output_file: ?[]const u8 = null; var generate_mocks = false; // TODO: Proper argument parsing for multiple flags for (args[2..]) |arg| { if (std.mem.startsWith(u8, arg, "--output=")) { output_file = arg["--output=".len..]; } else if (std.mem.eql(u8, arg, "--mocks")) { generate_mocks = true; } } // ... existing parsing ... // ✅ DONE: Generate Zig code const output = try codegen.CodeGen.generate(allocator, decls); defer allocator.free(output); // ✅ DONE: Write to file or stdout if (output_file) |file_path| { try std.fs.cwd().writeFile(.{ .sub_path = file_path, .data = output }); std.debug.print("Generated: {s}\n", .{file_path}); } else { _ = try std.posix.write(std.posix.STDOUT_FILENO, output); } // ✅ DONE: AST validation const output_z = try allocator.dupeZ(u8, output); defer allocator.free(output_z); var ast = try std.zig.Ast.parse(allocator, output_z, .zig); defer ast.deinit(allocator); if (ast.errors.len > 0) { std.debug.print("\nWarning: {d} syntax errors detected\n", .{ast.errors.len}); } // ⚠️ TODO: Generate C mocks if requested if (generate_mocks) { const mock_codegen = @import("mock_codegen.zig"); const mock_output = try mock_codegen.MockCodeGen.generate(allocator, decls); defer allocator.free(mock_output); const mock_filename = try std.fmt.allocPrint(allocator, "{s}_mock.c", .{ std.fs.path.stem(header_path) }); defer allocator.free(mock_filename); try std.fs.cwd().writeFile(.{ .sub_path = mock_filename, .data = mock_output }); std.debug.print("Generated C mocks: {s}\n", .{mock_filename}); } } ``` ## Part 2: Test Project Structure ### Directory Layout ``` lib/sdl3/parser/ ├── parser.zig ├── patterns.zig ├── naming.zig ├── codegen.zig ├── mock_codegen.zig # NEW: Mock C code generator ├── types.zig ├── build.zig │ └── test_project/ # NEW: Complete test harness ├── build.zig # Test project build ├── test_main.zig # Main test runner ├── generated/ # Generated files (gitignored) │ ├── gpu.zig # Generated Zig bindings │ └── gpu_mock.c # Generated C mocks ├── tests/ │ ├── opaque_test.zig # Test opaque type handling │ ├── enum_test.zig # Test enum usage │ ├── struct_test.zig # Test struct usage │ ├── flag_test.zig # Test flag manipulation │ └── function_test.zig # Test all function calls └── golden/ └── gpu.zig # Reference output for regression ``` ### Test Project Build Configuration **File**: `test_project/build.zig` ```zig const std = @import("std"); pub fn build(b: *std.Build) void { const target = b.standardTargetOptions(.{}); const optimize = b.standardOptimizeOption(.{}); // Step 1: Run parser to generate bindings and mocks const parser_path = b.path("../zig-out/bin/sdl-parser"); const header_path = b.path("../../SDL/include/SDL3/SDL_gpu.h"); const run_parser = b.addSystemCommand(&[_][]const u8{ parser_path.getPath(b), header_path.getPath(b), "--mocks", }); // Capture stdout to generated/gpu.zig const gpu_zig_path = b.path("generated/gpu.zig"); run_parser.setStdOut(.{ .write_to_file = gpu_zig_path }); // Step 2: Compile C mocks const mock_c = b.addObject(.{ .name = "gpu_mock", .target = target, .optimize = optimize, }); mock_c.addCSourceFile(.{ .file = b.path("generated/gpu_mock.c"), .flags = &[_][]const u8{"-std=c11"}, }); mock_c.linkLibC(); mock_c.step.dependOn(&run_parser.step); // Step 3: Create test executable const test_exe = b.addExecutable(.{ .name = "gpu-test", .root_module = b.createModule(.{ .root_source_file = b.path("test_main.zig"), .target = target, .optimize = optimize, }), }); test_exe.linkLibC(); test_exe.linkLibrary(mock_c); test_exe.step.dependOn(&run_parser.step); b.installArtifact(test_exe); // Step 4: Run test const run_test = b.addRunArtifact(test_exe); run_test.step.dependOn(b.getInstallStep()); const test_step = b.step("test", "Run all tests"); test_step.dependOn(&run_test.step); // Step 5: Unit tests for generated code const unit_tests = b.addTest(.{ .root_module = b.createModule(.{ .root_source_file = b.path("test_main.zig"), .target = target, .optimize = optimize, }), }); unit_tests.linkLibC(); unit_tests.linkLibrary(mock_c); unit_tests.step.dependOn(&run_parser.step); const run_unit_tests = b.addRunArtifact(unit_tests); const unit_test_step = b.step("test-unit", "Run unit tests"); unit_test_step.dependOn(&run_unit_tests.step); } ``` ### Main Test Runner **File**: `test_project/test_main.zig` ```zig const std = @import("std"); const gpu = @import("generated/gpu.zig"); pub fn main() !void { std.debug.print("SDL3 GPU Binding Test\n", .{}); std.debug.print("======================\n\n", .{}); var test_count: usize = 0; var pass_count: usize = 0; // Test 1: Opaque type functions test_count += 1; if (testOpaqueTypes()) { pass_count += 1; std.debug.print("✅ Opaque types test passed\n", .{}); } else |err| { std.debug.print("❌ Opaque types test failed: {}\n", .{err}); } // Test 2: Enum usage test_count += 1; if (testEnums()) { pass_count += 1; std.debug.print("✅ Enum test passed\n", .{}); } else |err| { std.debug.print("❌ Enum test failed: {}\n", .{err}); } // Test 3: Struct initialization test_count += 1; if (testStructs()) { pass_count += 1; std.debug.print("✅ Struct test passed\n", .{}); } else |err| { std.debug.print("❌ Struct test failed: {}\n", .{err}); } // Test 4: Flag manipulation test_count += 1; if (testFlags()) { pass_count += 1; std.debug.print("✅ Flag test passed\n", .{}); } else |err| { std.debug.print("❌ Flag test failed: {}\n", .{err}); } // Test 5: All function calls test_count += 1; if (testAllFunctions()) { pass_count += 1; std.debug.print("✅ Function call test passed\n", .{}); } else |err| { std.debug.print("❌ Function call test failed: {}\n", .{err}); } std.debug.print("\nResults: {}/{} tests passed\n", .{pass_count, test_count}); if (pass_count == test_count) { std.debug.print("🎉 All tests passed!\n", .{}); return; } else { return error.TestsFailed; } } fn testOpaqueTypes() !void { // Test that we can call functions returning opaque pointers const device = gpu.createGPUDevice(false, false, null); // Device should be null from mock, but call should succeed if (device) |d| { gpu.destroyGPUDevice(d); } } fn testEnums() !void { // Test enum value access const prim_type = gpu.GPUPrimitiveType.primitivetypeTrianglelist; _ = prim_type; // Test numeric enum values don't cause issues const sample_count = gpu.GPUSampleCount.samplecount4; _ = sample_count; const tex_type = gpu.GPUTextureType.texturetype2dArray; _ = tex_type; } fn testStructs() !void { // Test struct initialization const viewport = gpu.GPUViewport{ .x = 0.0, .y = 0.0, .w = 800.0, .h = 600.0, .min_depth = 0.0, .max_depth = 1.0, }; _ = viewport; } fn testFlags() !void { // Test flag creation and manipulation var usage: gpu.GPUTextureUsageFlags = .{}; usage.textureusageSampler = true; usage.textureusageColorTarget = true; try std.testing.expect(usage.textureusageSampler); try std.testing.expect(usage.textureusageColorTarget); try std.testing.expect(!usage.textureusageDepthStencilTarget); } fn testAllFunctions() !void { // Call every generated function at least once // This ensures all wrappers link correctly // Device functions const device = gpu.createGPUDevice(false, false, null); _ = device; // Query functions const supports = gpu.gpuSupportsShaderFormats(.{}, "test"); _ = supports; // ... more function calls ... // This can be auto-generated from the function list } // Unit tests test "opaque types compile" { try testOpaqueTypes(); } test "enums accessible" { try testEnums(); } test "structs initialize" { try testStructs(); } test "flags manipulate" { try testFlags(); } ``` ### Function Coverage Generator **File**: `test_project/tests/function_test.zig` Auto-generate test that calls every function: ```zig const std = @import("std"); const gpu = @import("../generated/gpu.zig"); test "all functions callable" { // This test is auto-generated // It calls every function with dummy arguments to verify linkage // createGPUDevice _ = gpu.createGPUDevice(false, false, null); // destroyGPUDevice gpu.destroyGPUDevice(null); // claimWindowForGPUDevice _ = gpu.claimWindowForGPUDevice(null, null); // ... continue for all 94 functions // Can be generated by iterating through function_decl list } ``` ## Part 3: Implementation Plan ### Phase 0: Infrastructure Improvements ✅ (COMPLETED) **Completed Tasks**: 1. ✅ Added `--output=` parameter support 2. ✅ Integrated `std.zig.Ast` parsing for validation 3. ✅ Fixed pointer type conversion bugs 4. ✅ Fixed struct field parsing for pointer types 5. ✅ Updated usage documentation **Files Modified**: - `parser.zig` - Added output parameter, AST validation - `types.zig` - Fixed pointer type handling for both `Foo *` and `Foo*` - `patterns.zig` - Fixed struct field parsing algorithm - `codegen.zig` - Kept trailing commas (valid Zig syntax) **Current State**: ```bash zig build run -- ../SDL/include/SDL3/SDL_gpu.h --output=gpu.zig # ✅ Works! Generates 49KB file with 169 declarations # ⚠️ 59 syntax errors remain (down from 86) ``` ### Phase 1: Mock Code Generator (3 hours) ⚠️ TODO **Tasks**: 1. ⚠️ Create `mock_codegen.zig` 2. ⚠️ Implement mock generation for: - Opaque type forward declarations - Function stubs with parameter voiding - Default return values 3. ⚠️ Add tests for mock generator 4. ⚠️ Update parser.zig to support --mocks flag (argument parsing needs multi-flag support) **Files**: - `mock_codegen.zig` (new, ~200 lines) - NOT CREATED YET - `parser.zig` (modify, +20 lines) - Needs multi-flag argument parsing - Add mock_codegen tests **Test**: ```bash zig build run -- ../SDL/include/SDL3/SDL_gpu.h --output=gpu.zig --mocks # Should generate gpu.zig and gpu_mock.c ``` ### Phase 2: Test Project Setup (2 hours) ⚠️ TODO **Tasks**: 1. ⚠️ Create test_project directory structure 2. ⚠️ Write test_project/build.zig (needs update for new --output parameter) 3. ⚠️ Set up generated/ output directory 4. ⚠️ Configure gitignore **Files**: - `test_project/build.zig` (new, ~100 lines) - Will use `--output=` instead of stdout redirect - `test_project/.gitignore` (new) - Update main build.zig to add test-project step **Updated Build Script**: ```zig // Use new --output parameter instead of capturing stdout const run_parser = b.addRunArtifact(parser_exe); run_parser.addArgs(&[_][]const u8{ header_path, "--output=generated/gpu.zig", "--mocks", // When Phase 1 is complete }); ``` ### Phase 3: Basic Test Runner (2 hours) ⚠️ TODO **Tasks**: 1. ⚠️ Write test_main.zig with basic test framework 2. ⚠️ Implement opaque type tests 3. ⚠️ Implement enum tests 4. ⚠️ Implement struct tests 5. ⚠️ Implement flag tests 6. ⚠️ Test with actual generated output (includes nullable pointers now) **Files**: - `test_project/test_main.zig` (new, ~150 lines) **Note**: Tests should verify: - Nullable pointer handling (`?*Type`) - Struct fields with correct pointer types - Trailing commas in function parameters (valid syntax) **Test**: ```bash cd test_project zig build test ``` ### Phase 4: Function Coverage (2 hours) ⚠️ TODO **Tasks**: 1. ⚠️ Generate function call test 2. ⚠️ Create helper to call all functions 3. ⚠️ Add safety checks for null returns (critical with `?*` types) 4. ⚠️ Report coverage statistics **Files**: - `test_project/tests/function_test.zig` (new, ~300 lines) - Helper script to generate from decls **Important**: Function tests must handle: - Optional return types (`?*GPUDevice` can be null) - Proper unwrapping before use - Trailing commas in test code ### Phase 5: Golden File & Regression (1 hour) ⚠️ TODO **Tasks**: 1. ⚠️ Generate golden reference file (from current best output) 2. ⚠️ Add diff comparison 3. ⚠️ Add update mechanism 4. ⚠️ Document workflow 5. ⚠️ Decide on AST-formatted vs raw output for golden files **Files**: - `test_project/golden/gpu.zig` (generated) - Update test_main.zig with comparison **Decision Needed**: - Use AST-rendered output (once errors are fixed) for consistent formatting? - Or use raw output to preserve original generation logic? ### Phase 6: Fix Remaining Syntax Errors (2-4 hours) ⚠️ TODO **Current Issue**: 59 syntax errors in full SDL_gpu.h output **Investigation Needed**: 1. ⚠️ Identify patterns causing remaining errors 2. ⚠️ Fix flag parsing edge cases 3. ⚠️ Fix function parameter edge cases 4. ⚠️ Add tests for problematic patterns 5. ⚠️ Enable full AST rendering instead of just validation **Goal**: Get to 0 syntax errors so AST can format the output ## Part 4: Usage Workflow ### Developer Workflow ```bash # 1. Build parser cd lib/sdl3/parser zig build # 2. Run test project cd test_project zig build test # Output: # SDL3 GPU Binding Test # ====================== # # Generating bindings... # Generating C mocks... # Compiling C mocks... # Building test executable... # Running tests... # # ✅ Opaque types test passed # ✅ Enum test passed # ✅ Struct test passed # ✅ Flag test passed # ✅ Function call test passed (94/94 functions) # # Results: 5/5 tests passed # 🎉 All tests passed! ``` ### CI/CD Integration ```yaml # .github/workflows/parser-test.yml name: Parser Tests on: [push, pull_request] jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 with: submodules: true # For SDL3 - name: Setup Zig uses: goto-bus-stop/setup-zig@v2 with: version: 0.14.0 - name: Build Parser run: | cd lib/sdl3/parser zig build - name: Run Unit Tests run: | cd lib/sdl3/parser zig build test - name: Run Integration Tests run: | cd lib/sdl3/parser/test_project zig build test ``` ## Part 5: Success Criteria ### Mock Generation - ✅ Parser accepts --mocks flag - ✅ Generates valid C code - ✅ All functions have stubs - ✅ Compiles with standard C compiler - ✅ No undefined symbols ### Test Project - ✅ Compiles without errors - ✅ Links Zig bindings with C mocks - ✅ All tests pass - ✅ Calls all 94 functions - ✅ No runtime crashes - ✅ No memory leaks (valgrind clean) ### Regression Testing - ✅ Golden file comparison works - ✅ Detects output changes - ✅ Update mechanism functional ## Part 6: Advanced Features ### Auto-Generate Function Tests Script to generate function_test.zig from declarations: ```zig // generate_function_tests.zig const std = @import("std"); const patterns = @import("../patterns.zig"); pub fn generateFunctionTests(decls: []patterns.Declaration, allocator: Allocator) ![]const u8 { var output = std.ArrayList(u8).init(allocator); try output.appendSlice("test \"all functions callable\" {\n"); for (decls) |decl| { if (decl == .function_decl) { const func = decl.function_decl; try output.writer().print(" _ = gpu.{s}(", .{func.name}); // Generate dummy arguments for (func.params, 0..) |param, i| { if (i > 0) try output.appendSlice(", "); const dummy = try getDummyValue(param.type_name, allocator); try output.appendSlice(dummy); } try output.appendSlice(");\n"); } } try output.appendSlice("}\n"); return output.toOwnedSlice(); } ``` ### Memory Safety Testing Add valgrind/sanitizer testing: ```zig // In build.zig const sanitize_test = b.addExecutable(.{ .name = "gpu-test-sanitize", .root_source_file = b.path("test_main.zig"), .target = target, .optimize = .Debug, }); // Enable sanitizers sanitize_test.sanitize = .{ .address = true, .undefined = true }; ``` ## Total Implementation Time - Phase 0: Infrastructure ✅ - **COMPLETED** (4 hours spent) - Output parameter - AST validation - Bug fixes (pointer types, struct fields) - Phase 1: Mock Generator ⚠️ - 3 hours (TODO) - Phase 2: Test Project Setup ⚠️ - 2 hours (TODO) - Phase 3: Basic Tests ⚠️ - 2 hours (TODO) - Phase 4: Function Coverage ⚠️ - 2 hours (TODO) - Phase 5: Regression ⚠️ - 1 hour (TODO) - Phase 6: Fix Syntax Errors ⚠️ - 2-4 hours (NEW) **Total Estimated**: 12-14 hours remaining **Completed**: 4 hours (infrastructure improvements) **Grand Total**: 16-18 hours ## Deliverables 1. ✅ Updated `parser.zig` - **DONE**: Support for --output parameter, AST validation 2. ✅ Updated `types.zig` - **DONE**: Fixed pointer type conversion 3. ✅ Updated `patterns.zig` - **DONE**: Fixed struct field parsing 4. ✅ Updated `codegen.zig` - **DONE**: Verified trailing comma validity 5. ⚠️ `mock_codegen.zig` - C mock generator (TODO) 6. ⚠️ Updated `parser.zig` - Support --mocks flag (TODO - needs multi-flag parsing) 7. ⚠️ `test_project/` - Complete test harness (TODO) 8. ⚠️ `test_main.zig` - Test runner (TODO) 9. ⚠️ `function_test.zig` - Coverage tests (TODO) 10. ⚠️ Golden reference files (TODO) 11. ⚠️ Documentation & README updates (TODO) 12. ⚠️ CI/CD configuration (TODO) ## Current Output Quality **Working Test Case** (test_small.h): ```zig pub const c = @import("c.zig").c; pub const GPUDevice = opaque {}; pub const GPUPrimitiveType = enum(c_int) { primitivetypeTrianglelist, primitivetypeTrianglestrip, }; pub inline fn createGPUDevice(debug_mode: bool,) ?*GPUDevice { return c.SDL_CreateGPUDevice(debug_mode); } ``` ✅ **Status**: Valid Zig code, compiles successfully **Full SDL_gpu.h Output**: - 169 declarations generated - 49KB output file - 59 syntax errors remaining (needs investigation) - Struct pointer fields now correctly parsed - Function return types use nullable pointers