1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204
|
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2024 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for Swift project authors
//
#if canImport(Foundation) && !SWT_NO_ABI_ENTRY_POINT
@testable @_spi(Experimental) @_spi(ForToolsIntegrationOnly) import Testing
#if canImport(Foundation)
private import Foundation
#endif
private import _TestingInternals
@Suite("ABI entry point tests")
struct ABIEntryPointTests {
#if !SWT_NO_SNAPSHOT_TYPES
@available(*, deprecated)
@Test func v0_experimental() async throws {
var arguments = __CommandLineArguments_v0()
arguments.filter = ["NonExistentTestThatMatchesNothingHopefully"]
arguments.eventStreamVersion = 0
arguments.verbosity = .min
let result = try await _invokeEntryPointV0Experimental(passing: arguments) { recordJSON in
let record = try! JSON.decode(ABIv0.Record.self, from: recordJSON)
_ = record.version
}
#expect(result == EXIT_SUCCESS)
}
@available(*, deprecated)
@Test("v0 experimental entry point with a large number of filter arguments")
func v0_experimental_manyFilters() async throws {
var arguments = __CommandLineArguments_v0()
arguments.filter = (1...100).map { "NonExistentTestThatMatchesNothingHopefully_\($0)" }
arguments.eventStreamVersion = 0
arguments.verbosity = .min
let result = try await _invokeEntryPointV0Experimental(passing: arguments)
#expect(result == EXIT_SUCCESS)
}
@available(*, deprecated)
private func _invokeEntryPointV0Experimental(
passing arguments: __CommandLineArguments_v0,
recordHandler: @escaping @Sendable (_ recordJSON: UnsafeRawBufferPointer) -> Void = { _ in }
) async throws -> CInt {
// Get the ABI entry point by dynamically looking it up at runtime.
let copyABIEntryPoint_v0 = try withTestingLibraryImageAddress { testingLibrary in
try #require(
symbol(in: testingLibrary, named: "swt_copyABIEntryPoint_v0").map {
unsafeBitCast($0, to: (@convention(c) () -> UnsafeMutableRawPointer).self)
}
)
}
let abiEntryPoint = copyABIEntryPoint_v0().assumingMemoryBound(to: ABIEntryPoint_v0.self)
defer {
abiEntryPoint.deinitialize(count: 1)
abiEntryPoint.deallocate()
}
let argumentsJSON = try JSON.withEncoding(of: arguments) { argumentsJSON in
let result = UnsafeMutableRawBufferPointer.allocate(byteCount: argumentsJSON.count, alignment: 1)
result.copyMemory(from: argumentsJSON)
return result
}
defer {
argumentsJSON.deallocate()
}
// Call the entry point function.
return try await abiEntryPoint.pointee(.init(argumentsJSON), recordHandler)
}
#endif
@Test func v0() async throws {
var arguments = __CommandLineArguments_v0()
arguments.filter = ["NonExistentTestThatMatchesNothingHopefully"]
arguments.eventStreamVersion = 0
arguments.verbosity = .min
let result = try await _invokeEntryPointV0(passing: arguments) { recordJSON in
let record = try! JSON.decode(ABIv0.Record.self, from: recordJSON)
_ = record.version
}
#expect(result)
}
@Test("v0 entry point with a large number of filter arguments")
func v0_manyFilters() async throws {
var arguments = __CommandLineArguments_v0()
arguments.filter = (1...100).map { "NonExistentTestThatMatchesNothingHopefully_\($0)" }
arguments.eventStreamVersion = 0
arguments.verbosity = .min
let result = try await _invokeEntryPointV0(passing: arguments)
#expect(result)
}
@Test("v0 entry point listing tests only")
func v0_listingTestsOnly() async throws {
var arguments = __CommandLineArguments_v0()
arguments.listTests = true
arguments.eventStreamVersion = 0
arguments.verbosity = .min
try await confirmation("Test matched", expectedCount: 1...) { testMatched in
_ = try await _invokeEntryPointV0(passing: arguments) { recordJSON in
let record = try! JSON.decode(ABIv0.Record.self, from: recordJSON)
if case .test = record.kind {
testMatched()
} else {
Issue.record("Unexpected record \(record)")
}
}
}
}
private func _invokeEntryPointV0(
passing arguments: __CommandLineArguments_v0,
recordHandler: @escaping @Sendable (_ recordJSON: UnsafeRawBufferPointer) -> Void = { _ in }
) async throws -> Bool {
#if !(!SWT_FIXED_SWIFTPM_8111 && os(Windows))
#if !os(Linux) && !os(FreeBSD) && !os(Android) && !SWT_NO_DYNAMIC_LINKING
// Get the ABI entry point by dynamically looking it up at runtime.
//
// NOTE: The standard Linux linker does not allow exporting symbols from
// executables, so dlsym() does not let us find this function on that
// platform when built as an executable rather than a dynamic library.
let abiv0_getEntryPoint = try withTestingLibraryImageAddress { testingLibrary in
try #require(
symbol(in: testingLibrary, named: "swt_abiv0_getEntryPoint").map {
unsafeBitCast($0, to: (@convention(c) () -> UnsafeRawPointer).self)
}
)
}
#endif
#endif
let abiEntryPoint = unsafeBitCast(abiv0_getEntryPoint(), to: ABIv0.EntryPoint.self)
let argumentsJSON = try JSON.withEncoding(of: arguments) { argumentsJSON in
let result = UnsafeMutableRawBufferPointer.allocate(byteCount: argumentsJSON.count, alignment: 1)
result.copyMemory(from: argumentsJSON)
return result
}
defer {
argumentsJSON.deallocate()
}
// Call the entry point function.
return try await abiEntryPoint(.init(argumentsJSON), recordHandler)
}
#if canImport(Foundation)
@Test func decodeEmptyConfiguration() throws {
let emptyBuffer = UnsafeRawBufferPointer(start: nil, count: 0)
#expect(throws: DecodingError.self) {
_ = try JSON.decode(__CommandLineArguments_v0.self, from: emptyBuffer)
}
}
#endif
}
#if !SWT_NO_DYNAMIC_LINKING
private func withTestingLibraryImageAddress<R>(_ body: (ImageAddress?) throws -> R) throws -> R {
let addressInTestingLibrary = unsafeBitCast(ABIv0.entryPoint, to: UnsafeRawPointer.self)
var testingLibraryAddress: ImageAddress?
#if SWT_TARGET_OS_APPLE
var info = Dl_info()
try #require(0 != dladdr(addressInTestingLibrary, &info))
testingLibraryAddress = dlopen(info.dli_fname, RTLD_NOLOAD)
try #require(testingLibraryAddress != nil)
defer {
dlclose(testingLibraryAddress)
}
#elseif os(Linux) || os(FreeBSD) || os(Android)
// When using glibc, dladdr() is only available if __USE_GNU is specified.
#elseif os(Windows)
let flags = DWORD(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS)
try addressInTestingLibrary.withMemoryRebound(to: wchar_t.self, capacity: MemoryLayout<UnsafeRawPointer>.stride / MemoryLayout<wchar_t>.stride) { addressInTestingLibrary in
try #require(GetModuleHandleExW(flags, addressInTestingLibrary, &testingLibraryAddress))
}
defer {
FreeLibrary(testingLibraryAddress)
}
#else
#warning("Platform-specific implementation missing: cannot find the testing library image the test suite is linked against")
#endif
return try body(testingLibraryAddress)
}
#endif
#endif
|