diff --git a/.devcontainer/devcontainer-lock.json b/.devcontainer/devcontainer-lock.json new file mode 100644 index 00000000..d19e0879 --- /dev/null +++ b/.devcontainer/devcontainer-lock.json @@ -0,0 +1,19 @@ +{ + "features": { + "ghcr.io/devcontainers-extra/features/apt-get-packages:1": { + "version": "1.0.8", + "resolved": "ghcr.io/devcontainers-extra/features/apt-get-packages@sha256:776fa11c30c50fa91d5e1bf050444b0f130827457109c349db92e8e126b750ec", + "integrity": "sha256:776fa11c30c50fa91d5e1bf050444b0f130827457109c349db92e8e126b750ec" + }, + "ghcr.io/devcontainers/features/common-utils:2": { + "version": "2.5.9", + "resolved": "ghcr.io/devcontainers/features/common-utils@sha256:cb0c4d3c276f157eed17935747e364178d75fee17f55c4e129966f64633deb3a", + "integrity": "sha256:cb0c4d3c276f157eed17935747e364178d75fee17f55c4e129966f64633deb3a" + }, + "ghcr.io/devcontainers/features/git:1": { + "version": "1.3.5", + "resolved": "ghcr.io/devcontainers/features/git@sha256:27905dc196c01f77d6ba8709cb82eeaf330b3b108772e2f02d1cd0d826de1251", + "integrity": "sha256:27905dc196c01f77d6ba8709cb82eeaf330b3b108772e2f02d1cd0d826de1251" + } + } +} diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 00000000..fe0e1952 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,43 @@ +{ + "name": "Swift", + "image": "swift:6.3", + "features": { + "ghcr.io/devcontainers/features/common-utils:2": { + "installZsh": "false", + "username": "vscode", + "upgradePackages": "false" + }, + "ghcr.io/devcontainers/features/git:1": { + "version": "os-provided", + "ppa": "false" + }, + "ghcr.io/devcontainers-extra/features/apt-get-packages:1": { + "packages": "libssl-dev" + } + }, + "runArgs": [ + "--cap-add=SYS_PTRACE", + "--security-opt", + "seccomp=unconfined" + ], + // Configure tool-specific properties. + "customizations": { + // Configure properties specific to VS Code. + "vscode": { + // Set *default* container specific settings.json values on container create. + "settings": { + "swift.path": "/usr/bin", + "lldb.library": "/usr/lib/liblldb.so" + }, + // Add the IDs of extensions you want installed when the container is created. + "extensions": [ + "swiftlang.swift-vscode" + ] + } + }, + // Use 'forwardPorts' to make a list of ports inside the container available locally. + // "forwardPorts": [], + + // Set `remoteUser` to `root` to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root. + "remoteUser": "vscode" +} diff --git a/.github/workflows/swift.yml b/.github/workflows/swift.yml index 812ab75b..493e59ec 100644 --- a/.github/workflows/swift.yml +++ b/.github/workflows/swift.yml @@ -52,3 +52,5 @@ jobs: submodules: true - name: Build run: swift build + - name: Test + run: swift test diff --git a/Package.swift b/Package.swift index 8c7bb5b7..e9f8e06a 100644 --- a/Package.swift +++ b/Package.swift @@ -54,6 +54,9 @@ let package = Package( // When -enable-library-evolution is specified verify-emitted-module-interface command fails "-no-verify-emitted-module-interface", ]), + ], + linkerSettings: [ + .linkedLibrary("crypto", .when(platforms: [.linux])) ] ), .testTarget( diff --git a/Sources/ComputeCxx/Graph/Graph.cpp b/Sources/ComputeCxx/Graph/Graph.cpp index 73c2d105..688d59e1 100644 --- a/Sources/ComputeCxx/Graph/Graph.cpp +++ b/Sources/ComputeCxx/Graph/Graph.cpp @@ -2081,4 +2081,42 @@ const char *Graph::key_name(uint32_t key_id) const { IAG::precondition_failure("invalid string key id: %u", key_id); } +#pragma mark - Printing + +#if !TARGET_OS_MAC +namespace { + +int cycle_verbosity() { + const char *print_cycles = getenv("IAG_PRINT_CYCLES"); + if (print_cycles) { + return atoi(print_cycles); + } + return 1; +} + +int trap_cycles() { + const char *trap_cycles = getenv("IAG_TRAP_CYCLES"); + if (trap_cycles) { + return atoi(trap_cycles) != 0; + } + return false; +} + +} // namespace + +void Graph::print_cycle(data::ptr node) { + foreach_trace([&node](Trace &trace) { trace.log_message("cycle detected through attribute: %u", node); }); + + static int verbosity = cycle_verbosity(); + if (verbosity >= 1) { + fprintf(stdout, "=== AttributeGraph: cycle detected through attribute %u ===\n", node.offset()); + } + + static bool traps = trap_cycles(); + if (traps) { + precondition_failure("cyclic graph: %u", node); + } +} +#endif + } // namespace IAG diff --git a/Sources/ComputeCxx/Swift/ContextDescriptor.cpp b/Sources/ComputeCxx/Swift/ContextDescriptor.cpp index 6bd68958..78ee291d 100644 --- a/Sources/ComputeCxx/Swift/ContextDescriptor.cpp +++ b/Sources/ComputeCxx/Swift/ContextDescriptor.cpp @@ -145,7 +145,7 @@ uint64_t class_type_descriptor::immediate_members_offset() const { uint64_t class_type_descriptor::field_offset_vector_offset() const { // FieldOffsetVectorOffset is a private field, // but we know comes directly after Numfields which is a uint32_t - uint32_t offset = *(&_base.NumFields + sizeof(uint32_t)); + uint32_t offset = *(reinterpret_cast(&_base.NumFields) + sizeof(uint32_t)); if (base()->hasResilientSuperclass()) { return immediate_members_offset() + offset; diff --git a/Sources/ComputeCxx/Swift/Metadata.cpp b/Sources/ComputeCxx/Swift/Metadata.cpp index 6b9ae203..15a6a838 100644 --- a/Sources/ComputeCxx/Swift/Metadata.cpp +++ b/Sources/ComputeCxx/Swift/Metadata.cpp @@ -670,14 +670,15 @@ bool metadata::visit_heap_class(metadata_visitor &visitor) const { } if ((class_type->Flags & ::swift::ClassFlags::UsesSwiftRefcounting) == 0) { - size_t *ivar_offsets = nullptr; // TODO: use new + ptrdiff_t *ivar_offsets = nullptr; #if SWIFT_OBJC_INTEROP unsigned int ivar_count; Ivar *ivar_list = class_copyIvarList(reinterpret_cast((void *)this), &ivar_count); if (ivar_list) { if (ivar_count == fields->NumFields) { - ivar_offsets = (size_t *)calloc(ivar_count, sizeof(size_t)); // or bzero? + ivar_offsets = (ptrdiff_t *)alloca(sizeof(ptrdiff_t) * ivar_count); + memset(ivar_offsets, 0, sizeof(ptrdiff_t) * ivar_count); for (unsigned int ivar_index = 0; ivar_index < ivar_count; ++ivar_index) { ivar_offsets[ivar_index] = ivar_getOffset(ivar_list[ivar_index]); } @@ -689,37 +690,27 @@ bool metadata::visit_heap_class(metadata_visitor &visitor) const { if (ivar_offsets && *ivar_offsets != 0) { unsigned index = 0; for (auto &field : fields->getFields()) { - size_t offset = ivar_offsets[index]; - size_t end_offset = index + 1 < fields->NumFields ? ivar_offsets[index + 1] : -1; + ptrdiff_t offset = ivar_offsets[index]; + ptrdiff_t end_offset = index + 1 < fields->NumFields ? ivar_offsets[index + 1] : vw_size(); size_t field_size = offset <= end_offset ? end_offset - offset : -1; if (!visitor.visit_field(*this, field, offset, field_size)) { - if (ivar_offsets) { - free(ivar_offsets); - } return false; } index += 1; } - if (ivar_offsets) { - free(ivar_offsets); - } return true; } - - if (ivar_offsets) { - free(ivar_offsets); - } return visitor.unknown_result(); } else { if (class_context->hasFieldOffsetVector()) { auto offset = reinterpret_cast(class_context)->field_offset_vector_offset(); - auto asWords = *reinterpret_cast(this); - const size_t *field_offsets = reinterpret_cast(asWords + offset); + auto asWords = reinterpret_cast(this); + const ptrdiff_t *field_offsets = reinterpret_cast(asWords + offset); unsigned index = 0; for (auto &field : class_context->Fields->getFields()) { - size_t field_offset = field_offsets[index]; - size_t end_offset = index + 1 < class_context->NumFields ? field_offsets[index + 1] : vw_size(); + ptrdiff_t field_offset = field_offsets[index]; + ptrdiff_t end_offset = index + 1 < class_context->NumFields ? field_offsets[index + 1] : vw_size(); size_t field_size = field_offset <= end_offset ? end_offset - field_offset : -1; if (!visitor.visit_field(*this, field, field_offset, field_size)) { return false; diff --git a/Tests/ComputeLayoutDescriptorTests/Shared/CompareValuesTests.swift b/Tests/ComputeLayoutDescriptorTests/Shared/CompareValuesTests.swift index ec6ac601..fde04b31 100644 --- a/Tests/ComputeLayoutDescriptorTests/Shared/CompareValuesTests.swift +++ b/Tests/ComputeLayoutDescriptorTests/Shared/CompareValuesTests.swift @@ -1,3 +1,4 @@ +import Foundation import Testing /// A type that can be equal without being bitwise identical. diff --git a/Tests/ComputeLayoutDescriptorTests/Shared/PrefetchCompareValuesTests.swift b/Tests/ComputeLayoutDescriptorTests/Shared/PrefetchCompareValuesTests.swift index c2641a5d..c382bf09 100644 --- a/Tests/ComputeLayoutDescriptorTests/Shared/PrefetchCompareValuesTests.swift +++ b/Tests/ComputeLayoutDescriptorTests/Shared/PrefetchCompareValuesTests.swift @@ -1,5 +1,9 @@ +import Foundation import Testing +// This suite relies heavily on process isolation, which does not complete on Linux. +#if !os(Linux) + public struct ValueLayout: CustomStringConvertible, Equatable { let storage: UnsafePointer @@ -1946,3 +1950,5 @@ struct PrefetchCompareValuesTests { } } + +#endif diff --git a/Tests/ComputeSwiftTests/Shared/ArrayBuilder.swift b/Tests/ComputeSwiftTests/Shared/ArrayBuilder.swift new file mode 100644 index 00000000..2984e7e5 --- /dev/null +++ b/Tests/ComputeSwiftTests/Shared/ArrayBuilder.swift @@ -0,0 +1,18 @@ +@resultBuilder +struct ArrayBuilder { + static func buildPartialBlock(first: Element) -> [Element] { + [first] + } + + static func buildPartialBlock(accumulated: [Element], next: Element) -> [Element] { + var result = accumulated + result.append(next) + return result + } +} + +extension Array { + init(@ArrayBuilder build: () -> [Element]) { + self = build() + } +} diff --git a/Tests/ComputeSwiftTests/Shared/MetadataTests.swift b/Tests/ComputeSwiftTests/Shared/MetadataTests.swift index 6b5e5e3f..ed6d8b63 100644 --- a/Tests/ComputeSwiftTests/Shared/MetadataTests.swift +++ b/Tests/ComputeSwiftTests/Shared/MetadataTests.swift @@ -5,64 +5,72 @@ import Testing struct MetadataTests { @Test( - arguments: [ - (TestClass.self, "TestClass"), - (TestStruct.self, "TestStruct"), - (TestEnum.self, "TestEnum"), - (TestTaggedEnum.self, "TestTaggedEnum"), - (TestIndirectEnum.self, "TestIndirectEnum"), - (TestOptionalClass.self, "Optional"), - (TestOptionalStruct.self, "Optional"), - (TestForeignClass.self, "CFDateRef"), - (TestTuple.self, "(String, Int)"), - (TestFunction.self, "(String) -> Int"), - (TestExistential.self, "Hashable"), - (TestConstrainedExistential.self, "any Sequence"), - (TestComposedExistential.self, "CustomStringConvertible & Hashable"), - (TestMetatype.self, "TestClass.Type"), - (TestObjCClass.self, "NSDate"), - (TestExistentialMetatype.self, "Hashable.Protocol"), - (TestNamespace.TestNestedStruct.self, "TestNamespace.TestNestedStruct"), + arguments: Array<(Any.Type, String)> { + (TestClass.self, "TestClass") + (TestStruct.self, "TestStruct") + (TestEnum.self, "TestEnum") + (TestTaggedEnum.self, "TestTaggedEnum") + (TestIndirectEnum.self, "TestIndirectEnum") + (TestOptionalClass.self, "Optional") + (TestOptionalStruct.self, "Optional") + #if canImport(CoreFoundation) + (TestForeignClass.self, "CFDateRef") + #endif + (TestTuple.self, "(String, Int)") + (TestFunction.self, "(String) -> Int") + (TestExistential.self, "Hashable") + (TestConstrainedExistential.self, "any Sequence") + (TestComposedExistential.self, "CustomStringConvertible & Hashable") + (TestMetatype.self, "TestClass.Type") + #if canImport(Darwin) + (TestObjCClass.self, "NSDate") + #endif + (TestExistentialMetatype.self, "Hashable.Protocol") + (TestNamespace.TestNestedStruct.self, "TestNamespace.TestNestedStruct") ( TestGenericStruct.self, "TestGenericStruct" - ), + ) ( TestGenericStruct.TestNestedGenericStruct.self, "TestGenericStruct.TestNestedGenericStruct" - ), - (TestPackedGenericStruct.self, "TestPackedGenericStruct"), - ] as [(Any.Type, String)] + ) + (TestPackedGenericStruct.self, "TestPackedGenericStruct") + } ) func description(of type: Any.Type, equals expectedDescription: String) { #expect(Metadata(type).description == expectedDescription) } @Test( - arguments: [ - (TestClass.self, .class), - (TestStruct.self, .struct), - (TestEnum.self, .enum), - (TestTaggedEnum.self, .enum), - (TestIndirectEnum.self, .enum), - (TestOptionalClass.self, .optional), - (TestOptionalStruct.self, .optional), - (TestForeignClass.self, .none), - (TestTuple.self, .tuple), - (TestFunction.self, .function), - (TestExistential.self, .existential), - (TestConstrainedExistential.self, .none), - (TestComposedExistential.self, .existential), - (TestMetatype.self, .metatype), - (TestObjCClass.self, .none), - (TestExistentialMetatype.self, .metatype), - (TestNamespace.TestNestedStruct.self, .struct), - (TestGenericStruct.self, .struct), + arguments: Array<(Any.Type, Metadata.Kind)> { + (TestClass.self, Metadata.Kind.class) + (TestStruct.self, Metadata.Kind.struct) + (TestEnum.self, Metadata.Kind.enum) + (TestTaggedEnum.self, Metadata.Kind.enum) + (TestIndirectEnum.self, Metadata.Kind.enum) + (TestOptionalClass.self, Metadata.Kind.optional) + (TestOptionalStruct.self, Metadata.Kind.optional) + #if canImport(CoreFoundation) + (TestForeignClass.self, Metadata.Kind.none) + #endif + (TestTuple.self, Metadata.Kind.tuple) + (TestFunction.self, Metadata.Kind.function) + (TestExistential.self, Metadata.Kind.existential) + (TestConstrainedExistential.self, Metadata.Kind.none) + (TestComposedExistential.self, Metadata.Kind.existential) + (TestMetatype.self, Metadata.Kind.metatype) + #if canImport(Darwin) + (TestObjCClass.self, Metadata.Kind.none) + #endif + (TestExistentialMetatype.self, Metadata.Kind.metatype) + (TestNamespace.TestNestedStruct.self, Metadata.Kind.struct) + (TestGenericStruct.self, Metadata.Kind.struct) ( TestGenericStruct.TestNestedGenericStruct.self, - .struct - ), - (TestPackedGenericStruct.self, .struct), - ] as [(Any.Type, Metadata.Kind)] + Metadata.Kind.struct + ) + (TestPackedGenericStruct.self, Metadata.Kind.struct) + } ) func kind(of type: Any.Type, equals expectedKind: Metadata.Kind) { #expect(Metadata(type).kind == expectedKind) @@ -73,31 +81,35 @@ struct MetadataTests { @Test( "Metadata for nominal type has a valid signature", - arguments: [ - (TestClass.self, true), - (TestStruct.self, true), - (TestEnum.self, true), - (TestTaggedEnum.self, true), - (TestIndirectEnum.self, true), - (TestOptionalClass.self, true), - (TestOptionalStruct.self, true), - (TestForeignClass.self, false), - (TestTuple.self, false), - (TestFunction.self, false), - (TestExistential.self, false), - (TestConstrainedExistential.self, false), - (TestComposedExistential.self, false), - (TestMetatype.self, false), - (TestObjCClass.self, false), - (TestExistentialMetatype.self, false), - (TestNamespace.self, true), - (TestNamespace.TestNestedStruct.self, true), + arguments: Array<(Any.Type, Bool)> { + (TestClass.self, true) + (TestStruct.self, true) + (TestEnum.self, true) + (TestTaggedEnum.self, true) + (TestIndirectEnum.self, true) + (TestOptionalClass.self, true) + (TestOptionalStruct.self, true) + #if canImport(CoreFoundation) + (TestForeignClass.self, false) + #endif + (TestTuple.self, false) + (TestFunction.self, false) + (TestExistential.self, false) + (TestConstrainedExistential.self, false) + (TestComposedExistential.self, false) + (TestMetatype.self, false) + #if canImport(Darwin) + (TestObjCClass.self, false) + #endif + (TestExistentialMetatype.self, false) + (TestNamespace.self, true) + (TestNamespace.TestNestedStruct.self, true) ( TestGenericStruct.TestNestedGenericStruct.self, true - ), - (TestPackedGenericStruct.self, true), - ] as [(Any.Type, Bool)] + ) + (TestPackedGenericStruct.self, true) + } ) func signature(of type: Any.Type, isValid: Bool) { if isValid { @@ -139,31 +151,35 @@ struct MetadataTests { @Test( "Metadata for nominal type has a descriptor", - arguments: [ - (TestClass.self, true), - (TestStruct.self, true), - (TestEnum.self, true), - (TestTaggedEnum.self, true), - (TestIndirectEnum.self, true), - (TestOptionalClass.self, true), - (TestOptionalStruct.self, true), - (TestForeignClass.self, false), - (TestTuple.self, false), - (TestFunction.self, false), - (TestExistential.self, false), - (TestConstrainedExistential.self, false), - (TestComposedExistential.self, false), - (TestMetatype.self, false), - (TestObjCClass.self, false), - (TestExistentialMetatype.self, false), - (TestNamespace.TestNestedStruct.self, true), - (TestGenericStruct.self, true), + arguments: Array<(Any.Type, Bool)> { + (TestClass.self, true) + (TestStruct.self, true) + (TestEnum.self, true) + (TestTaggedEnum.self, true) + (TestIndirectEnum.self, true) + (TestOptionalClass.self, true) + (TestOptionalStruct.self, true) + #if canImport(CoreFoundation) + (TestForeignClass.self, false) + #endif + (TestTuple.self, false) + (TestFunction.self, false) + (TestExistential.self, false) + (TestConstrainedExistential.self, false) + (TestComposedExistential.self, false) + (TestMetatype.self, false) + #if canImport(Darwin) + (TestObjCClass.self, false) + #endif + (TestExistentialMetatype.self, false) + (TestNamespace.TestNestedStruct.self, true) + (TestGenericStruct.self, true) ( TestGenericStruct.TestNestedGenericStruct.self, true - ), - (TestPackedGenericStruct.self, true), - ] as [(Any.Type, Bool)] + ) + (TestPackedGenericStruct.self, true) + } ) func descriptor(of type: Any.Type, hasDescriptor: Bool) { if hasDescriptor { @@ -175,31 +191,35 @@ struct MetadataTests { @Test( "Metadata for non-class nominal type has a nominal descriptor", - arguments: [ - (TestClass.self, false), - (TestStruct.self, true), - (TestEnum.self, true), - (TestTaggedEnum.self, true), - (TestIndirectEnum.self, true), - (TestOptionalClass.self, true), - (TestOptionalStruct.self, true), - (TestForeignClass.self, false), - (TestTuple.self, false), - (TestFunction.self, false), - (TestExistential.self, false), - (TestConstrainedExistential.self, false), - (TestComposedExistential.self, false), - (TestMetatype.self, false), - (TestObjCClass.self, false), - (TestExistentialMetatype.self, false), - (TestNamespace.TestNestedStruct.self, true), - (TestGenericStruct.self, true), + arguments: Array<(Any.Type, Bool)> { + (TestClass.self, false) + (TestStruct.self, true) + (TestEnum.self, true) + (TestTaggedEnum.self, true) + (TestIndirectEnum.self, true) + (TestOptionalClass.self, true) + (TestOptionalStruct.self, true) + #if canImport(CoreFoundation) + (TestForeignClass.self, false) + #endif + (TestTuple.self, false) + (TestFunction.self, false) + (TestExistential.self, false) + (TestConstrainedExistential.self, false) + (TestComposedExistential.self, false) + (TestMetatype.self, false) + #if canImport(Darwin) + (TestObjCClass.self, false) + #endif + (TestExistentialMetatype.self, false) + (TestNamespace.TestNestedStruct.self, true) + (TestGenericStruct.self, true) ( TestGenericStruct.TestNestedGenericStruct.self, true - ), - (TestPackedGenericStruct.self, true), - ] as [(Any.Type, Bool)] + ) + (TestPackedGenericStruct.self, true) + } ) func nominalDescriptor(of type: Any.Type, hasNominalDescriptor: Bool) { if hasNominalDescriptor { @@ -211,31 +231,35 @@ struct MetadataTests { @Test( "Metadata for nominal type has a nominal descriptor name", - arguments: [ - (TestClass.self, nil), - (TestStruct.self, "TestStruct"), - (TestEnum.self, "TestEnum"), - (TestTaggedEnum.self, "TestTaggedEnum"), - (TestIndirectEnum.self, "TestIndirectEnum"), - (TestOptionalClass.self, "Optional"), - (TestOptionalStruct.self, "Optional"), - (TestForeignClass.self, nil), - (TestTuple.self, nil), - (TestFunction.self, nil), - (TestExistential.self, nil), - (TestConstrainedExistential.self, nil), - (TestComposedExistential.self, nil), - (TestMetatype.self, nil), - (TestObjCClass.self, nil), - (TestExistentialMetatype.self, nil), - (TestNamespace.TestNestedStruct.self, "TestNestedStruct"), - (TestGenericStruct.self, "TestGenericStruct"), + arguments: Array<(Any.Type, String?)> { + (TestClass.self, nil as String?) + (TestStruct.self, "TestStruct") + (TestEnum.self, "TestEnum") + (TestTaggedEnum.self, "TestTaggedEnum") + (TestIndirectEnum.self, "TestIndirectEnum") + (TestOptionalClass.self, "Optional") + (TestOptionalStruct.self, "Optional") + #if canImport(CoreFoundation) + (TestForeignClass.self, nil as String?) + #endif + (TestTuple.self, nil as String?) + (TestFunction.self, nil as String?) + (TestExistential.self, nil as String?) + (TestConstrainedExistential.self, nil as String?) + (TestComposedExistential.self, nil as String?) + (TestMetatype.self, nil as String?) + #if canImport(Darwin) + (TestObjCClass.self, nil as String?) + #endif + (TestExistentialMetatype.self, nil as String?) + (TestNamespace.TestNestedStruct.self, "TestNestedStruct") + (TestGenericStruct.self, "TestGenericStruct") ( TestGenericStruct.TestNestedGenericStruct.self, "TestNestedGenericStruct" - ), - (TestPackedGenericStruct.self, "TestPackedGenericStruct"), - ] as [(Any.Type, String?)] + ) + (TestPackedGenericStruct.self, "TestPackedGenericStruct") + } ) func nominalDescriptorName(of type: Any.Type, equals expectedNominalDescriptorName: String?) { if let expectedNominalDescriptorName { diff --git a/Tests/ComputeSwiftTests/Shared/TestTypes.swift b/Tests/ComputeSwiftTests/Shared/TestTypes.swift index 0cb4985f..817b2e38 100644 --- a/Tests/ComputeSwiftTests/Shared/TestTypes.swift +++ b/Tests/ComputeSwiftTests/Shared/TestTypes.swift @@ -1,5 +1,9 @@ import Foundation +#if canImport(CoreFoundation) +import CoreFoundation +#endif + // Class class TestClass { @@ -36,7 +40,7 @@ typealias TestOptionalStruct = TestStruct? // Foreign Class -#if os(macOS) || os(iOS) +#if canImport(CoreFoundation) typealias TestForeignClass = CFDate #endif @@ -65,7 +69,9 @@ typealias TestMetatype = TestClass.Type // ObjC +#if canImport(Darwin) typealias TestObjCClass = NSDate +#endif // Existential metatype diff --git a/Tests/ComputeTests/Shared/Attribute/AttributeBodyTests.swift b/Tests/ComputeTests/Shared/Attribute/AttributeBodyTests.swift index e9e2a78d..274be260 100644 --- a/Tests/ComputeTests/Shared/Attribute/AttributeBodyTests.swift +++ b/Tests/ComputeTests/Shared/Attribute/AttributeBodyTests.swift @@ -13,7 +13,7 @@ struct AttributeBodyTests { #expect(DefaultAttributeBody.flags == [.mainThread]) } - @Suite + @Suite(.serialized) struct DestroySelfTests { @Test @@ -31,6 +31,10 @@ struct AttributeBodyTests { let graph = Graph() let subgraph = Subgraph(graph: graph) + let oldSubgraph = Subgraph.current + defer { + Subgraph.current = oldSubgraph + } Subgraph.current = subgraph var destroyed = false @@ -64,6 +68,10 @@ struct AttributeBodyTests { let graph = Graph() let subgraph = Subgraph(graph: graph) + let oldSubgraph = Subgraph.current + defer { + Subgraph.current = oldSubgraph + } Subgraph.current = subgraph var destroyed = false @@ -97,6 +105,10 @@ struct AttributeBodyTests { let graph = Graph() let subgraph = Subgraph(graph: graph) + let oldSubgraph = Subgraph.current + defer { + Subgraph.current = oldSubgraph + } Subgraph.current = subgraph var destroyed = false diff --git a/Tests/ComputeTests/Shared/Attribute/AttributeTests.swift b/Tests/ComputeTests/Shared/Attribute/AttributeTests.swift index 3626cd4c..91986e74 100644 --- a/Tests/ComputeTests/Shared/Attribute/AttributeTests.swift +++ b/Tests/ComputeTests/Shared/Attribute/AttributeTests.swift @@ -1,3 +1,4 @@ +import Foundation import Testing struct Triple { diff --git a/Tests/ComputeTests/Shared/Graph+DictionaryDescription.swift b/Tests/ComputeTests/Shared/Graph+DictionaryDescription.swift index 85922552..962d3014 100644 --- a/Tests/ComputeTests/Shared/Graph+DictionaryDescription.swift +++ b/Tests/ComputeTests/Shared/Graph+DictionaryDescription.swift @@ -1,5 +1,7 @@ import Foundation +#if canImport(Darwin) + extension Graph { public struct DictionaryDescription: Equatable, Codable { @@ -173,7 +175,8 @@ extension Graph { public func dictionaryDescription(includeValues: Bool = false) -> DictionaryDescription? { // TODO: Conform DescriptionOption to CustomStringConvertible so we don't have to access rawValue here let options = - [DescriptionOption.format.rawValue: "graph/dict", DescriptionOption.includeValues.rawValue: includeValues] as NSDictionary + [DescriptionOption.format.rawValue: "graph/dict", DescriptionOption.includeValues.rawValue: includeValues] + as NSDictionary guard let description = Graph.description(self, options: options) as? NSDictionary else { return nil } @@ -221,3 +224,5 @@ extension Graph { } } + +#endif diff --git a/Tests/ComputeTests/Shared/Graph/GraphTests.swift b/Tests/ComputeTests/Shared/Graph/GraphTests.swift index aa82e711..7b3544d9 100644 --- a/Tests/ComputeTests/Shared/Graph/GraphTests.swift +++ b/Tests/ComputeTests/Shared/Graph/GraphTests.swift @@ -2,7 +2,7 @@ import Foundation import Testing import _ComputeTestSupport -@Suite +@Suite(.serialized) struct GraphTests { @Suite @@ -10,7 +10,11 @@ struct GraphTests { @Test func typeID() { + #if os(Linux) + let description = String(cfString: CFCopyTypeIDDescription(Graph.typeID)) + #else let description = CFCopyTypeIDDescription(Graph.typeID) as String? + #endif #if COMPATIBILITY_TESTS #expect(description == "AGGraphStorage") #else @@ -280,6 +284,7 @@ struct GraphTests { } + #if canImport(Darwin) @Suite struct DescriptionTests { @@ -334,7 +339,7 @@ struct GraphTests { ) } } - + @Test func graphDescription() async throws { try await #require(processExitsWith: .success) { @@ -375,9 +380,12 @@ struct GraphTests { ) ] ) - + // FIXME: post-process the subgraphs array in the description output. - withKnownIssue("Subgraphs is sorted by pointer address, which we can't predict deterministically.", isIntermittent: true) { + withKnownIssue( + "Subgraphs is sorted by pointer address, which we can't predict deterministically.", + isIntermittent: true + ) { assertValuesEqualWithDiff(description, expectedDescription) } } @@ -478,5 +486,6 @@ struct GraphTests { } } + #endif } diff --git a/Tests/ComputeTests/Shared/Subgraph/SubgraphTests.swift b/Tests/ComputeTests/Shared/Subgraph/SubgraphTests.swift index e791b394..874feea3 100644 --- a/Tests/ComputeTests/Shared/Subgraph/SubgraphTests.swift +++ b/Tests/ComputeTests/Shared/Subgraph/SubgraphTests.swift @@ -1,7 +1,7 @@ import Foundation import Testing -@Suite +@Suite(.serialized) struct SubgraphTests { @Suite @@ -9,7 +9,11 @@ struct SubgraphTests { @Test func typeID() { + #if os(Linux) + let description = String(cfString: CFCopyTypeIDDescription(Subgraph.typeID)) + #else let description = CFCopyTypeIDDescription(Subgraph.typeID) as String? + #endif #if COMPATIBILITY_TESTS #expect(description == "AGSubgraph") #else diff --git a/Tests/ComputeTests/Shims.swift b/Tests/ComputeTests/Shims.swift index 3e3f7328..4aec2f00 100644 --- a/Tests/ComputeTests/Shims.swift +++ b/Tests/ComputeTests/Shims.swift @@ -3,3 +3,42 @@ let prefetchLayoutsEnvironmentVariable = "IAG_PREFETCH_LAYOUTS" let asyncLayoutsEnvironmentVariable = "IAG_ASYNC_LAYOUTS" let printLayoutsEnvironmentVariable = "IAG_PRINT_LAYOUTS" + +extension Graph: @retroactive Equatable { + public static func == (_ lhs: Graph, _ rhs: Graph) -> Bool { + unsafeBitCast(lhs, to: UnsafeRawPointer.self) == unsafeBitCast(rhs, to: UnsafeRawPointer.self) + } +} + +extension Subgraph: @retroactive Equatable { + public static func == (_ lhs: Subgraph, _ rhs: Subgraph) -> Bool { + unsafeBitCast(lhs, to: UnsafeRawPointer.self) == unsafeBitCast(rhs, to: UnsafeRawPointer.self) + } +} + +#if os(Linux) +extension String { + init?(cfString: CFString) { + if let pointer = CFStringGetCStringPtr(cfString, CFStringBuiltInEncodings.UTF8.rawValue) { + self.init(cString: pointer) + return + } + + let length = CFStringGetLength(cfString) + let maxSize = CFStringGetMaximumSizeForEncoding(length, CFStringBuiltInEncodings.UTF8.rawValue) + 1 + let buffer = UnsafeMutablePointer.allocate(capacity: maxSize) + defer { + buffer.deallocate() + } + guard CFStringGetCString(cfString, buffer, maxSize, CFStringBuiltInEncodings.UTF8.rawValue) else { + return nil + } + self.init(cString: buffer) + } +} + +func autoreleasepool(invoking body: () throws(E) -> Result) throws(E) -> Result +where E: Error, Result: ~Copyable { + try body() +} +#endif