Skip to content

Commit

Permalink
Add ICOMTest2 for more thorough Support module tests.
Browse files Browse the repository at this point in the history
  • Loading branch information
tristanlabelle committed Nov 30, 2024
1 parent de174bb commit 9233a8c
Show file tree
Hide file tree
Showing 10 changed files with 145 additions and 47 deletions.
15 changes: 15 additions & 0 deletions Support/Tests/ABI/include/SWRT_ICOMTest2.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#pragma once

#include "SWRT/windows/unknwn.h"

// ICOMTest2
typedef struct SWRT_ICOMTest2 {
struct SWRT_ICOMTest2_VirtualTable* VirtualTable;
} SWRT_ICOMTest2;

struct SWRT_ICOMTest2_VirtualTable {
SWRT_HResult (__stdcall *QueryInterface)(SWRT_ICOMTest2* _this, SWRT_Guid* riid, void** ppvObject);
uint32_t (__stdcall *AddRef)(SWRT_ICOMTest2* _this);
uint32_t (__stdcall *Release)(SWRT_ICOMTest2* _this);
SWRT_HResult (__stdcall *COMTest2)(SWRT_ICOMTest2* _this);
};
1 change: 1 addition & 0 deletions Support/Tests/ABI/include/module.modulemap
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
module TestsABI {
header "SWRT_ICOMTest.h"
header "SWRT_ICOMTest2.h"
header "SWRT_IWinRTTest.h"
export *
}
51 changes: 39 additions & 12 deletions Support/Tests/Tests/COMExportTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,38 @@ import XCTest
import WindowsRuntime

internal final class COMExportTests: XCTestCase {
final class TestObject: COMPrimaryExport<IUnknownBinding>, ICOMTestProtocol, ICOMTest2Protocol {
override class var implements: [COMImplements] { [
.init(ICOMTestBinding.self),
.init(ICOMTest2Binding.self)
] }

func comTest() throws {}
func comTest2() throws {}
}

func testIUnknownIdentityRule() throws {
let swiftObject = SwiftObject()
let comTest = try swiftObject.queryInterface(ICOMTestBinding.self)
let winRTTest = try swiftObject.queryInterface(IWinRTTestBinding.self)
let testObject = TestObject()
let comTest = try testObject.queryInterface(ICOMTestBinding.self)
let comTest2 = try testObject.queryInterface(ICOMTest2Binding.self)

let unknownReference1 = try comTest._queryInterface(IUnknownBinding.self)
let unknownReference2 = try winRTTest._queryInterface(IUnknownBinding.self)
let unknownReference2 = try comTest2._queryInterface(IUnknownBinding.self)
XCTAssertEqual(unknownReference1.pointer, unknownReference2.pointer)
}

func testQueryInterfaceTransitivityRule() throws {
let swiftObject = SwiftObject()
let unknown = try swiftObject.queryInterface(IUnknownBinding.self)
let inspectable = try swiftObject.queryInterface(IInspectableBinding.self)
let comTest = try swiftObject.queryInterface(ICOMTestBinding.self)
let winRTTest = try swiftObject.queryInterface(IWinRTTestBinding.self)
let testObject = TestObject()
let unknown = try testObject.queryInterface(IUnknownBinding.self)
let comTest = try testObject.queryInterface(ICOMTestBinding.self)
let comTest2 = try testObject.queryInterface(ICOMTest2Binding.self)

// QueryInterface should succeed from/to any pair of implemented interfaces
let objects: [any IUnknownProtocol] = [unknown, inspectable, comTest, winRTTest]
let objects: [any IUnknownProtocol] = [unknown, comTest, comTest2]
for object in objects {
_ = try object.queryInterface(IUnknownBinding.self)
_ = try object.queryInterface(IInspectableBinding.self)
_ = try object.queryInterface(ICOMTestBinding.self)
_ = try object.queryInterface(IWinRTTestBinding.self)
_ = try object.queryInterface(ICOMTest2Binding.self)
}
}

Expand Down Expand Up @@ -55,4 +63,23 @@ internal final class COMExportTests: XCTestCase {
let _ = try Marshalable()._queryInterface(imarshalID)
XCTAssertThrowsError(try { _ = try NonMarshalable()._queryInterface(imarshalID) }())
}

func testImplementsSecondaryInterface() throws {
final class CallCounter: COMPrimaryExport<IUnknownBinding>, ICOMTestProtocol {
override class var implements: [COMImplements] { [
.init(ICOMTestBinding.self)
] }

var count: Int = 0
func comTest() throws { count += 1 }
}

let callCounter = CallCounter()
do {
let comTestReference = try callCounter._queryInterface(ICOMTestBinding.self)
XCTAssertEqual(callCounter.count, 0)
try comTestReference.interop.comTest()
}
XCTAssertEqual(callCounter.count, 1)
}
}
File renamed without changes.
44 changes: 44 additions & 0 deletions Support/Tests/Tests/Support/ICOMTest2.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import COM

internal typealias ICOMTest2 = any ICOMTest2Protocol
internal protocol ICOMTest2Protocol: IUnknownProtocol {
func comTest2() throws
}

import TestsABI

internal enum ICOMTest2Binding: COMTwoWayBinding {
public typealias SwiftObject = ICOMTest2
public typealias ABIStruct = TestsABI.SWRT_ICOMTest2

public static let interfaceID = COMInterfaceID(0x5CF9DEB3, 0xD7C6, 0x42A9, 0x85B3, 0x61D8B68A7B2B)
public static var virtualTablePointer: UnsafeRawPointer { .init(withUnsafePointer(to: &virtualTable) { $0 }) }

public static func _wrap(_ reference: consuming ABIReference) -> SwiftObject {
Import(_wrapping: reference)
}

public static func toCOM(_ object: SwiftObject) throws -> ABIReference {
try Import.toCOM(object)
}

private final class Import: COMImport<ICOMTest2Binding>, ICOMTest2Protocol {
public func comTest2() throws { try _interop.comTest2() }
}

private static var virtualTable: TestsABI.SWRT_ICOMTest2_VirtualTable = .init(
QueryInterface: { IUnknownVirtualTable.QueryInterface($0, $1, $2) },
AddRef: { IUnknownVirtualTable.AddRef($0) },
Release: { IUnknownVirtualTable.Release($0) },
COMTest2: { this in _implement(this) { try $0.comTest2() } })
}

public func uuidof(_: TestsABI.SWRT_ICOMTest2.Type) -> COMInterfaceID {
ICOMTest2Binding.interfaceID
}

extension COMInterop where ABIStruct == TestsABI.SWRT_ICOMTest2 {
public func comTest2() throws {
try COMError.fromABI(this.pointee.VirtualTable.pointee.COMTest2(this))
}
}
File renamed without changes.
15 changes: 15 additions & 0 deletions Support/Tests/Tests/Support/WinRTTestCase.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import XCTest
import WindowsRuntime

internal class WinRTTestCase: XCTestCase {
// Due to caching in the projections, we can't deinitialize the WinRT runtime after each test
private static var winRTInitialization: WinRTInitialization? = nil

override func setUpWithError() throws {
if Self.winRTInitialization == nil {
Self.winRTInitialization = try WinRTInitialization(multithreaded: false)
}

try super.setUpWithError()
}
}
15 changes: 0 additions & 15 deletions Support/Tests/Tests/SwiftObject.swift

This file was deleted.

34 changes: 31 additions & 3 deletions Support/Tests/Tests/WinRTExportTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,43 @@ import WindowsRuntime

internal final class WinRTExportTests: XCTestCase {
func testIInspectableIdentityRule() throws {
let swiftObject = SwiftObject()
let comTest = try swiftObject.queryInterface(ICOMTestBinding.self)
let winRTTest = try swiftObject.queryInterface(IWinRTTestBinding.self)
final class TestObject: WinRTPrimaryExport<IInspectableBinding>, ICOMTestProtocol, IWinRTTestProtocol {
override class var implements: [COMImplements] { [
.init(ICOMTestBinding.self),
.init(IWinRTTestBinding.self)
] }

func comTest() throws {}
func winRTTest() throws {}
}

let testObject = TestObject()
let comTest = try testObject.queryInterface(ICOMTestBinding.self)
let winRTTest = try testObject.queryInterface(IWinRTTestBinding.self)

let inspectableReference1 = try comTest._queryInterface(IInspectableBinding.self)
let inspectableReference2 = try winRTTest._queryInterface(IInspectableBinding.self)
XCTAssertEqual(inspectableReference1.pointer, inspectableReference2.pointer)
}

func testGetIids() throws {
final class TestObject: WinRTPrimaryExport<IInspectableBinding>, ICOMTestProtocol, IWinRTTestProtocol {
override class var implements: [COMImplements] { [
.init(ICOMTestBinding.self),
.init(IWinRTTestBinding.self)
] }

func comTest() throws {}
func winRTTest() throws {}
}

let iids = try TestObject().getIids()
// https://learn.microsoft.com/en-us/windows/win32/api/inspectable/nf-inspectable-iinspectable-getiids:
// "The IUnknown and IInspectable interfaces are excluded."
XCTAssertTrue(iids.contains(ICOMTestBinding.interfaceID), "ICOMTest")
XCTAssertTrue(iids.contains(IWinRTTestBinding.interfaceID), "IWinRTTest")
}

func testIStringable() throws {
final class Stringable: WinRTPrimaryExport<IInspectableBinding>, CustomStringConvertible {
var description: String { "hello" }
Expand Down
17 changes: 0 additions & 17 deletions Support/Tests/Tests/WinRTTestCase.swift

This file was deleted.

0 comments on commit 9233a8c

Please sign in to comment.