// InterfaceGenerationTests.swift // Covers interface method planning and rendering under the extension-default // model (Phase E1): an interface method's body is rendered as a `public` // protocol-extension DEFAULT implementation (`extension Ping { public func … }`) // calling the C symbol via `self.pointer`, since interface protocols have no // members of their own beyond `pointer`. Unplannable methods (unsupported // parameter types) are recorded as skips, mirroring class members. These // tests lock in that surface so a regression surfaces here. import Testing @testable import GObjectGeneratorCore @Suite("Interface generation") struct InterfaceGenerationTests { /// A registry-backed context in the GLib module. The interface under test /// needs no named types — its methods use primitives and strings only. func makeContext() -> MapContext { let glib = Repository(namespaces: [Namespace(name: "GLib", version: "2.0")]) let registry = TypeRegistry(repositories: ["GLib": glib]) return MapContext(registry: registry, currentModule: "GLib", currentNamespace: "GLib") } /// Renders a single planned interface to Swift source via the public module /// renderer, returning the interface's file body. func render(_ plan: InterfacePlan) -> String { let module = ModulePlan( module: "GLib", types: [.interface(plan)], skips: [], coverage: CoverageStats() ) return renderModule(module)["\(plan.name).swift"] ?? "" } @Test("Interface instance methods become protocol extension default implementations") func methodsBecomeExtensionDefaults() throws { let iface = Interface( name: "Ping", cType: "GPing", methods: [ Method( name: "get_id", cIdentifier: "g_ping_get_id", parameters: [Parameter(name: "self", type: .pointer, cType: "GPing*", isInstanceParameter: true)], returnValue: ReturnValue(type: .long) ) ] ) let (plan, skips) = planInterface(iface, context: makeContext()) #expect(skips.isEmpty) #expect(plan.methods.count == 1) #expect(plan.methods[0].name == "getId") let source = render(plan) // Protocol body is bare — only the `pointer` requirement. #expect(source.contains("public protocol Ping {")) #expect(source.contains("var pointer: UnsafeMutableRawPointer { get }")) // A default implementation — `public`, with a body calling the C symbol. #expect(source.contains("extension Ping {")) #expect(source.contains("public func getId() -> Int")) #expect(source.contains("g_ping_get_id")) } @Test("Unplannable interface methods are recorded as skips, not requirements") func unplannableMethodsSkip() throws { let iface = Interface( name: "Ping", cType: "GPing", methods: [ // Variadic methods cannot be bridged — must skip, not appear. Method( name: "log", cIdentifier: "g_ping_log", parameters: [ Parameter(name: "self", type: .pointer, cType: "GPing*", isInstanceParameter: true), Parameter(name: "...", type: .vaList, cType: ""), ] ) ] ) let (plan, skips) = planInterface(iface, context: makeContext()) #expect(plan.methods.isEmpty) #expect(skips.count == 1) #expect(!render(plan).contains("func log")) } }