1
0
Fork 0
gobject-generator/Tests/GObjectGeneratorCoreTests/InterfaceGenerationTests.swift

85 lines
3.7 KiB
Swift

// 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"))
}
}