Implement record, constant, alias codegen plus all override applications
This commit is contained in:
parent
56ca762fac
commit
a732fcaad9
2 changed files with 580 additions and 26 deletions
|
|
@ -97,3 +97,294 @@ func testGenerateEnum() throws {
|
|||
#expect(output.contains("case fill"))
|
||||
#expect(output.contains("case start"))
|
||||
}
|
||||
|
||||
@Test("Generates record with fields and methods")
|
||||
func testGenerateRecord() throws {
|
||||
let repo = Repository(namespaces: [
|
||||
Namespace(name: "Gtk", version: "4.0", records: [
|
||||
Record(name: "Rectangle", cType: "GdkRectangle",
|
||||
fields: [
|
||||
Field(name: "x", type: .int32),
|
||||
Field(name: "y", type: .int32),
|
||||
Field(name: "width", type: .int32),
|
||||
Field(name: "height", type: .int32),
|
||||
], methods: [
|
||||
Method(name: "contains_point", cIdentifier: "gdk_rectangle_contains_point",
|
||||
parameters: [
|
||||
Parameter(name: "rect", type: .typeRef("Rectangle"), isInstanceParameter: true),
|
||||
Parameter(name: "x", type: .int32),
|
||||
Parameter(name: "y", type: .int32),
|
||||
], returnType: .boolean),
|
||||
])
|
||||
])
|
||||
])
|
||||
let config = GenerationConfig(library: "Gtk", version: "4.0", girsDirectories: [],
|
||||
targetDirectory: "", externalLibraries: [],
|
||||
generate: ["Gtk.Rectangle"], manual: [], ignore: [], objects: [])
|
||||
let generator = CodeGenerator(config: config)
|
||||
let files = try generator.generateFiles(repository: repo, analysis: Analyzer(config: config).analyze(repository: repo))
|
||||
|
||||
let output = files["Rectangle.swift"] ?? ""
|
||||
#expect(output.contains("public struct Rectangle"))
|
||||
#expect(output.contains("public let x: Int32"))
|
||||
#expect(output.contains("public let y: Int32"))
|
||||
#expect(output.contains("public let width: Int32"))
|
||||
#expect(output.contains("public let height: Int32"))
|
||||
#expect(output.contains("mutating func containsPoint"))
|
||||
#expect(output.contains("gdk_rectangle_contains_point(&self, x, y)"))
|
||||
}
|
||||
|
||||
@Test("Generates constant declaration")
|
||||
func testGenerateConstant() throws {
|
||||
let repo = Repository(namespaces: [
|
||||
Namespace(name: "Gtk", version: "4.0", constants: [
|
||||
Constant(name: "MAJOR_VERSION", value: "4", type: .int32),
|
||||
])
|
||||
])
|
||||
let config = GenerationConfig(library: "Gtk", version: "4.0", girsDirectories: [],
|
||||
targetDirectory: "", externalLibraries: [],
|
||||
generate: ["Gtk.MAJOR_VERSION"], manual: [], ignore: [], objects: [])
|
||||
let generator = CodeGenerator(config: config)
|
||||
let files = try generator.generateFiles(repository: repo, analysis: Analyzer(config: config).analyze(repository: repo))
|
||||
|
||||
let output = files["MajorVersion.swift"] ?? ""
|
||||
#expect(output.contains("public let majorVersion: Int32 = 4"))
|
||||
}
|
||||
|
||||
@Test("Generates typealias declaration")
|
||||
func testGenerateAlias() throws {
|
||||
let repo = Repository(namespaces: [
|
||||
Namespace(name: "Gtk", version: "4.0", aliases: [
|
||||
Alias(name: "Allocation", cType: "GtkAllocation", target: .typeRef("Rectangle")),
|
||||
])
|
||||
])
|
||||
let config = GenerationConfig(library: "Gtk", version: "4.0", girsDirectories: [],
|
||||
targetDirectory: "", externalLibraries: [],
|
||||
generate: ["Gtk.Allocation"], manual: [], ignore: [], objects: [])
|
||||
let generator = CodeGenerator(config: config)
|
||||
let files = try generator.generateFiles(repository: repo, analysis: Analyzer(config: config).analyze(repository: repo))
|
||||
|
||||
let output = files["Allocation.swift"] ?? ""
|
||||
#expect(output.contains("public typealias Allocation = Rectangle"))
|
||||
}
|
||||
|
||||
@Test("Parameterized signal generates trampoline with extraction")
|
||||
func testParameterizedSignalTrampoline() {
|
||||
let signal = Signal(name: "size-allocate", parameters: [
|
||||
Parameter(name: "allocation", type: .typeRef("Rectangle")),
|
||||
Parameter(name: "width", type: .int32),
|
||||
], returnType: .void)
|
||||
let code = CodeGenerator.generateSignalConnection(signal: signal)
|
||||
#expect(code.contains("connectSizeAllocate"))
|
||||
#expect(code.contains("handler as AnyObject"))
|
||||
#expect(code.contains("Unmanaged<AnyObject>.fromOpaque(data!).takeUnretainedValue()"))
|
||||
#expect(!code.contains("// Manual"))
|
||||
#expect(!code.contains("nil, nil, nil"))
|
||||
#expect(code.contains("g_signal_connect_data(pointer, \"size-allocate\""))
|
||||
}
|
||||
|
||||
@Test("Class generation applies MainActor override")
|
||||
func testMainActorOverride() throws {
|
||||
let repo = Repository(namespaces: [
|
||||
Namespace(name: "Gtk", version: "4.0", classes: [
|
||||
Class(name: "Widget", cType: "GtkWidget", parent: nil)
|
||||
])
|
||||
])
|
||||
let config = GenerationConfig(library: "Gtk", version: "4.0", girsDirectories: [],
|
||||
targetDirectory: "", externalLibraries: [],
|
||||
generate: [], manual: [], ignore: [], objects: [
|
||||
.object("Gtk.Widget", overrides: ObjectOverrides(concurrency: .mainActor))
|
||||
])
|
||||
let generator = CodeGenerator(config: config)
|
||||
let output = try generator.generate(repository: repo, analysis: Analyzer(config: config).analyze(repository: repo))
|
||||
#expect(output.contains("@MainActor"))
|
||||
#expect(output.contains("public final class Widget"))
|
||||
}
|
||||
|
||||
@Test("Class generation applies Sendable conformance")
|
||||
func testSendableOverride() throws {
|
||||
let repo = Repository(namespaces: [
|
||||
Namespace(name: "Gdk", version: "4.0", classes: [
|
||||
Class(name: "Texture", cType: "GdkTexture", parent: nil)
|
||||
])
|
||||
])
|
||||
let config = GenerationConfig(library: "Gdk", version: "4.0", girsDirectories: [],
|
||||
targetDirectory: "", externalLibraries: [],
|
||||
generate: [], manual: [], ignore: [], objects: [
|
||||
.object("Gdk.Texture", overrides: ObjectOverrides(concurrency: .sendable))
|
||||
])
|
||||
let generator = CodeGenerator(config: config)
|
||||
let output = try generator.generate(repository: repo, analysis: Analyzer(config: config).analyze(repository: repo))
|
||||
#expect(output.contains("extension Texture: Sendable {}"))
|
||||
}
|
||||
|
||||
@Test("Class generation applies cfgCondition")
|
||||
func testCfgConditionOverride() throws {
|
||||
let repo = Repository(namespaces: [
|
||||
Namespace(name: "Gtk", version: "4.0", classes: [
|
||||
Class(name: "Widget", cType: "GtkWidget", parent: nil)
|
||||
])
|
||||
])
|
||||
let config = GenerationConfig(library: "Gtk", version: "4.0", girsDirectories: [],
|
||||
targetDirectory: "", externalLibraries: [],
|
||||
generate: [], manual: [], ignore: [], objects: [
|
||||
.object("Gtk.Widget", overrides: ObjectOverrides(cfgCondition: "os(macOS)"))
|
||||
])
|
||||
let generator = CodeGenerator(config: config)
|
||||
let output = try generator.generate(repository: repo, analysis: Analyzer(config: config).analyze(repository: repo))
|
||||
#expect(output.contains("#if os(macOS)"))
|
||||
#expect(output.contains("#endif"))
|
||||
}
|
||||
|
||||
@Test("Method override applies visibility and ignore")
|
||||
func testMethodOverrides() throws {
|
||||
let repo = Repository(namespaces: [
|
||||
Namespace(name: "Gtk", version: "4.0", classes: [
|
||||
Class(name: "Widget", cType: "GtkWidget", parent: nil, methods: [
|
||||
Method(name: "show", cIdentifier: "gtk_widget_show",
|
||||
parameters: [
|
||||
Parameter(name: "widget", type: .typeRef("Widget"), isInstanceParameter: true)
|
||||
], returnType: .void),
|
||||
Method(name: "hide", cIdentifier: "gtk_widget_hide",
|
||||
parameters: [
|
||||
Parameter(name: "widget", type: .typeRef("Widget"), isInstanceParameter: true)
|
||||
], returnType: .void),
|
||||
])
|
||||
])
|
||||
])
|
||||
let config = GenerationConfig(library: "Gtk", version: "4.0", girsDirectories: [],
|
||||
targetDirectory: "", externalLibraries: [],
|
||||
generate: [], manual: [], ignore: [], objects: [
|
||||
.function("Gtk.Widget", "show", overrides: FunctionOverrides(ignore: true)),
|
||||
.function("Gtk.Widget", "hide", overrides: FunctionOverrides(visibility: .internal)),
|
||||
])
|
||||
let generator = CodeGenerator(config: config)
|
||||
let output = try generator.generate(repository: repo, analysis: Analyzer(config: config).analyze(repository: repo))
|
||||
#expect(!output.contains("func show"))
|
||||
#expect(output.contains("internal func hide"))
|
||||
#expect(!output.contains("public func hide"))
|
||||
}
|
||||
|
||||
@Test("Signal override ignores signal")
|
||||
func testSignalOverrideIgnore() throws {
|
||||
let repo = Repository(namespaces: [
|
||||
Namespace(name: "Gtk", version: "4.0", classes: [
|
||||
Class(name: "Widget", cType: "GtkWidget", parent: nil, signals: [
|
||||
Signal(name: "map", parameters: [], returnType: .void),
|
||||
Signal(name: "unmap", parameters: [], returnType: .void),
|
||||
])
|
||||
])
|
||||
])
|
||||
let config = GenerationConfig(library: "Gtk", version: "4.0", girsDirectories: [],
|
||||
targetDirectory: "", externalLibraries: [],
|
||||
generate: [], manual: [], ignore: [], objects: [
|
||||
.signal("Gtk.Widget", "unmap", overrides: SignalOverrides(ignore: true)),
|
||||
])
|
||||
let generator = CodeGenerator(config: config)
|
||||
let output = try generator.generate(repository: repo, analysis: Analyzer(config: config).analyze(repository: repo))
|
||||
#expect(output.contains("connectMap"))
|
||||
#expect(!output.contains("connectUnmap"))
|
||||
}
|
||||
|
||||
@Test("Inhibit signal generates Bool-returning handler")
|
||||
func testInhibitSignalHandling() {
|
||||
let signal = Signal(name: "delete-event", parameters: [], returnType: .boolean)
|
||||
let code = CodeGenerator.generateSignalConnection(signal: signal, inhibit: true)
|
||||
#expect(code.contains("() -> Bool"))
|
||||
#expect(code.contains("gboolean"))
|
||||
#expect(code.contains("result ? 1 : 0"))
|
||||
}
|
||||
|
||||
@Test("Function pattern override applies glob rename")
|
||||
func testFunctionPatternOverride() throws {
|
||||
let repo = Repository(namespaces: [
|
||||
Namespace(name: "Gtk", version: "4.0", classes: [
|
||||
Class(name: "Widget", cType: "GtkWidget", parent: nil, methods: [
|
||||
Method(name: "get_parent", cIdentifier: "gtk_widget_get_parent",
|
||||
parameters: [
|
||||
Parameter(name: "widget", type: .typeRef("Widget"), isInstanceParameter: true)
|
||||
], returnType: .typeRef("Widget")),
|
||||
])
|
||||
])
|
||||
])
|
||||
let config = GenerationConfig(library: "Gtk", version: "4.0", girsDirectories: [],
|
||||
targetDirectory: "", externalLibraries: [],
|
||||
generate: [], manual: [], ignore: [], objects: [
|
||||
.functionPattern("Gtk.Widget", pattern: "get_*",
|
||||
rename: RenameRule(regex: "^get_", replacement: "")),
|
||||
])
|
||||
let analyzer = Analyzer(config: config)
|
||||
let analysis = analyzer.analyze(repository: repo)
|
||||
let generator = CodeGenerator(config: config)
|
||||
let output = try generator.generate(repository: repo, analysis: analysis)
|
||||
#expect(output.contains("func parent"))
|
||||
#expect(!output.contains("func getParent"))
|
||||
}
|
||||
|
||||
@Test("Constructor override generates convenience init")
|
||||
func testConstructorOverride() throws {
|
||||
let repo = Repository(namespaces: [
|
||||
Namespace(name: "Gtk", version: "4.0", classes: [
|
||||
Class(name: "Label", cType: "GtkLabel", parent: "Widget", methods: [
|
||||
Method(name: "new", cIdentifier: "gtk_label_new",
|
||||
parameters: [
|
||||
Parameter(name: "str", type: .string)
|
||||
], returnType: .typeRef("Widget")),
|
||||
])
|
||||
])
|
||||
])
|
||||
let config = GenerationConfig(library: "Gtk", version: "4.0", girsDirectories: [],
|
||||
targetDirectory: "", externalLibraries: [],
|
||||
generate: [], manual: [], ignore: [], objects: [
|
||||
.function("Gtk.Label", "new", overrides: FunctionOverrides(constructor: true)),
|
||||
])
|
||||
let generator = CodeGenerator(config: config)
|
||||
let output = try generator.generate(repository: repo, analysis: Analyzer(config: config).analyze(repository: repo))
|
||||
#expect(output.contains("convenience init"))
|
||||
#expect(output.contains("gtk_label_new(str)"))
|
||||
}
|
||||
|
||||
@Test("Record generation included in generateFiles output")
|
||||
func testRecordInGenerateFiles() throws {
|
||||
let repo = Repository(namespaces: [
|
||||
Namespace(name: "Gdk", version: "4.0", records: [
|
||||
Record(name: "RGBA", cType: "GdkRGBA",
|
||||
fields: [
|
||||
Field(name: "red", type: .double),
|
||||
Field(name: "green", type: .double),
|
||||
Field(name: "blue", type: .double),
|
||||
Field(name: "alpha", type: .double),
|
||||
])
|
||||
])
|
||||
])
|
||||
let config = GenerationConfig(library: "Gdk", version: "4.0", girsDirectories: [],
|
||||
targetDirectory: "", externalLibraries: [],
|
||||
generate: ["Gdk.RGBA"], manual: [], ignore: [], objects: [])
|
||||
let generator = CodeGenerator(config: config)
|
||||
let files = try generator.generateFiles(repository: repo, analysis: Analyzer(config: config).analyze(repository: repo))
|
||||
#expect(files.keys.contains("RGBA.swift"))
|
||||
let output = files["RGBA.swift"] ?? ""
|
||||
#expect(output.contains("public struct RGBA"))
|
||||
#expect(output.contains("public let red: Double"))
|
||||
#expect(output.contains("public let green: Double"))
|
||||
#expect(output.contains("public let blue: Double"))
|
||||
#expect(output.contains("public let alpha: Double"))
|
||||
}
|
||||
|
||||
@Test("Constant and alias in single-file generate")
|
||||
func testConstantAndAliasInGenerate() throws {
|
||||
let repo = Repository(namespaces: [
|
||||
Namespace(name: "GLib", version: "2.0", constants: [
|
||||
Constant(name: "VERSION", value: "\"2.0\"", type: .string),
|
||||
], aliases: [
|
||||
Alias(name: "Type", cType: "GType", target: .uint64),
|
||||
])
|
||||
])
|
||||
let config = GenerationConfig(library: "GLib", version: "2.0", girsDirectories: [],
|
||||
targetDirectory: "", externalLibraries: [],
|
||||
generate: [], manual: [], ignore: [], objects: [])
|
||||
let generator = CodeGenerator(config: config)
|
||||
let output = try generator.generate(repository: repo, analysis: Analyzer(config: config).analyze(repository: repo))
|
||||
#expect(output.contains("public let version: String = \"2.0\""))
|
||||
#expect(output.contains("public typealias Type = UInt64"))
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue