From 73226022759d1d4cb13c296258cb84639772df79 Mon Sep 17 00:00:00 2001 From: James Moschou Date: Mon, 19 Jan 2026 12:16:55 +0100 Subject: [PATCH 1/3] Update tests for updated AGTraceType --- Tests/ComputeTests/Shared/Graph/GraphTests.swift | 4 ++-- Tests/ComputeTests/Shared/Subgraph/SubgraphTests.swift | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Tests/ComputeTests/Shared/Graph/GraphTests.swift b/Tests/ComputeTests/Shared/Graph/GraphTests.swift index c27f60dd..13d4ae12 100644 --- a/Tests/ComputeTests/Shared/Graph/GraphTests.swift +++ b/Tests/ComputeTests/Shared/Graph/GraphTests.swift @@ -181,7 +181,7 @@ struct GraphTests { var traceCalls: [(name: String, graph: Graph)] = [] } - var trace = AGTrace() + var trace = AGTraceType() trace.begin_trace = { contextPointer, graph in if let context = contextPointer?.assumingMemoryBound(to: Context.self).pointee { context.traceCalls.append((name: "beginTrace", graph: graph)) @@ -225,7 +225,7 @@ struct GraphTests { var traceCalls: [(name: String, graph: Graph)] = [] } - var trace = AGTrace() + var trace = AGTraceType() trace.begin_trace = { contextPointer, graph in if let context = contextPointer?.assumingMemoryBound(to: Context.self).pointee { context.traceCalls.append((name: "beginTrace", graph: graph)) diff --git a/Tests/ComputeTests/Shared/Subgraph/SubgraphTests.swift b/Tests/ComputeTests/Shared/Subgraph/SubgraphTests.swift index 1e5b77f5..7f7cb043 100644 --- a/Tests/ComputeTests/Shared/Subgraph/SubgraphTests.swift +++ b/Tests/ComputeTests/Shared/Subgraph/SubgraphTests.swift @@ -202,14 +202,14 @@ struct SubgraphTests { @Test func invalidateSubgraph() async throws { - var trace = AGTrace() - trace.created_subgraph = { context, graph in + var trace = AGTraceType() + trace.subgraph_created = { context, graph in guard let reporter = context?.assumingMemoryBound(to: TraceReporter.self).pointee else { return } reporter.createdSubgraphCount += 1 } - trace.invalidate_subgraph = { context, graph in + trace.subgraph_destroy = { context, graph in guard let reporter = context?.assumingMemoryBound(to: TraceReporter.self).pointee else { return } From 06bbc0fc5da42e4fd7da71f3b79bddec956929b4 Mon Sep 17 00:00:00 2001 From: James Moschou Date: Mon, 19 Jan 2026 12:21:22 +0100 Subject: [PATCH 2/3] Fix HashTable implementation --- Sources/Utilities/HashTable.cpp | 32 ++-- Tests/UtilitiesTests/HashTableTests.swift | 172 ++++++++++++++++++++++ 2 files changed, 195 insertions(+), 9 deletions(-) diff --git a/Sources/Utilities/HashTable.cpp b/Sources/Utilities/HashTable.cpp index 119f26dc..990cbfc2 100644 --- a/Sources/Utilities/HashTable.cpp +++ b/Sources/Utilities/HashTable.cpp @@ -133,10 +133,13 @@ void UntypedTable::grow_buckets() { if (new_buckets) { _bucket_mask = num_buckets - 1; for (uint32_t i = 0; !(i >> old_width); i++) { - for (UntypedTable::HashNode *node = old_buckets[i]; node != nullptr; node = node->next) { + UntypedTable::HashNode *node = old_buckets[i]; + while (node != nullptr) { + UntypedTable::HashNode *next = node->next; uint64_t new_bucket = _bucket_mask & node->hash_value; node->next = new_buckets[new_bucket]; new_buckets[new_bucket] = node; + node = next; } } _buckets = new_buckets; @@ -242,11 +245,16 @@ bool UntypedTable::remove(key_type key) { return this->remove_ptr(key); } uint64_t hash_value = _hash(key); - HashNode *node = _buckets[_bucket_mask & hash_value]; + uint64_t bucket = _bucket_mask & hash_value; + HashNode *prev = nullptr; - for (HashNode *candidate = node; candidate != nullptr; candidate = candidate->next) { + for (HashNode *candidate = _buckets[bucket]; candidate != nullptr; candidate = candidate->next) { if (candidate->hash_value == hash_value && _compare(candidate->key, key)) { - node->next = candidate->next; + if (prev == nullptr) { + _buckets[bucket] = candidate->next; + } else { + prev->next = candidate->next; + } if (_did_remove_key) { _did_remove_key(candidate->key); } @@ -258,7 +266,7 @@ bool UntypedTable::remove(key_type key) { _count -= 1; return true; } - node = candidate; + prev = candidate; } return false; @@ -269,10 +277,16 @@ bool UntypedTable::remove_ptr(key_type key) { return false; } - HashNode *node = _buckets[_bucket_mask & _hash(key)]; - for (HashNode *candidate = node; candidate != nullptr; candidate = candidate->next) { + uint64_t bucket = _bucket_mask & _hash(key); + HashNode *prev = nullptr; + + for (HashNode *candidate = _buckets[bucket]; candidate != nullptr; candidate = candidate->next) { if (candidate->key == key) { - node->next = candidate->next; + if (prev == nullptr) { + _buckets[bucket] = candidate->next; + } else { + prev->next = candidate->next; + } if (_did_remove_key) { _did_remove_key(candidate->key); } @@ -284,7 +298,7 @@ bool UntypedTable::remove_ptr(key_type key) { _count -= 1; return true; } - node = candidate; + prev = candidate; } return false; } diff --git a/Tests/UtilitiesTests/HashTableTests.swift b/Tests/UtilitiesTests/HashTableTests.swift index 6d410d1f..964f2c78 100644 --- a/Tests/UtilitiesTests/HashTableTests.swift +++ b/Tests/UtilitiesTests/HashTableTests.swift @@ -78,4 +78,176 @@ struct HashTableTests { } } + @Test("Remove head of bucket then insert - should not create cycle") + func removeHeadThenInsert() { + let table = util.UntypedTable.create() + defer { + util.UntypedTable.destroy(table) + } + + // We'll insert many items to increase chance of bucket collisions + // With 16 initial buckets, inserting 32 items guarantees collisions + var keys: [Int] = Array(0..<32) + + // Insert all keys + for i in 0.. 100 { + fatalError("Cycle detected in hash table - iteration count exceeded expected") + } + }, &iterationCount) + + #expect(iterationCount == 32, "for_each should visit exactly count() items") + } + + @Test("Remove and reinsert same key - lookup should work") + func removeAndReinsertSameKey() { + let table = util.UntypedTable.create() + defer { + util.UntypedTable.destroy(table) + } + + var key1 = 1 + var key2 = 2 + var value1 = 100 + var value2 = 200 + + // Insert two keys + withUnsafePointer(to: &key1) { k1 in + withUnsafePointer(to: &value1) { v1 in + _ = table.insert(k1, v1) + } + } + withUnsafePointer(to: &key2) { k2 in + withUnsafePointer(to: &value2) { v2 in + _ = table.insert(k2, v2) + } + } + + #expect(table.count() == 2) + + // Remove first key + withUnsafePointer(to: &key1) { k1 in + let removed = table.remove(k1) + #expect(removed == true) + } + + #expect(table.count() == 1) + + // Verify key1 is not found + withUnsafePointer(to: &key1) { k1 in + let found = table.__lookupUnsafe(k1, nil) + #expect(found == nil, "Removed key should not be found") + } + + // Verify key2 is still found + withUnsafePointer(to: &key2) { k2 in + let found = table.__lookupUnsafe(k2, nil) + #expect(found != nil, "Remaining key should still be found") + } + + // Reinsert key1 with a new value + var value1New = 999 + withUnsafePointer(to: &key1) { k1 in + withUnsafePointer(to: &value1New) { v1 in + let inserted = table.insert(k1, v1) + #expect(inserted == true, "Reinserting removed key should succeed") + } + } + + #expect(table.count() == 2) + + // Verify key1 is found with new value + withUnsafePointer(to: &key1) { k1 in + let found = table.__lookupUnsafe(k1, nil) + #expect(found != nil, "Reinserted key should be found") + if let found = found { + #expect(found.assumingMemoryBound(to: Int.self).pointee == 999, "Reinserted key should have new value") + } + } + + // Count via for_each should be 2 + var iterationCount = 0 + table.for_each({ _, _, context in + let countPtr = context.assumingMemoryBound(to: Int.self) + countPtr.pointee += 1 + }, &iterationCount) + + #expect(iterationCount == 2, "for_each should visit exactly 2 items after reinsertion") + } + + @Test("Grow buckets should not lose nodes") + func growBucketsPreservesAllNodes() { + let table = util.UntypedTable.create() + defer { + util.UntypedTable.destroy(table) + } + + // Initial bucket_mask_width is 4 (16 buckets) + // Growth happens when count + 1 > 4 << bucket_mask_width + // So growth happens at count > 64 for first growth + // Insert enough items to trigger multiple growths + let itemCount = 200 + var keys: [Int] = Array(0.. Date: Mon, 19 Jan 2026 12:40:59 +0100 Subject: [PATCH 3/3] Wrap all layout descriptor tests in exit tests --- .../Shared/CompareValuesTests.swift | 388 +++++++------- .../Shared/PrefetchCompareValuesTests.swift | 503 +++++++++--------- 2 files changed, 454 insertions(+), 437 deletions(-) diff --git a/Tests/ComputeLayoutDescriptorTests/Shared/CompareValuesTests.swift b/Tests/ComputeLayoutDescriptorTests/Shared/CompareValuesTests.swift index e076208d..6248eaea 100644 --- a/Tests/ComputeLayoutDescriptorTests/Shared/CompareValuesTests.swift +++ b/Tests/ComputeLayoutDescriptorTests/Shared/CompareValuesTests.swift @@ -58,203 +58,209 @@ extension RecursiveEnum: Equatable where Value: Equatable { struct CompareValuesTests { @Test - func compareInlineEnumValues() { - setenv("AG_ASYNC_LAYOUTS", "0", 1) - - #expect( - compareValues( - InlineEnum.empty, - InlineEnum.empty - ) == true - ) - - #expect( - compareValues( - InlineEnum.node(value: Parity(rawValue: 0)), - InlineEnum.empty - ) == false - ) - - #expect( - compareValues( - InlineEnum.empty, - InlineEnum.node(value: Parity(rawValue: 0)) - ) == false - ) - - #expect( - compareValues( - InlineEnum.node(value: Parity(rawValue: 0)), - InlineEnum.node(value: Parity(rawValue: 0)) - ) == true - ) - - #expect( - compareValues( - InlineEnum.node(value: Parity(rawValue: 0)), - InlineEnum.node(value: Parity(rawValue: 1)) - ) == false - ) - - // default mode is .equatableAlways - #expect( - compareValues( - InlineEnum.node(value: Parity(rawValue: 0)), - InlineEnum.node(value: Parity(rawValue: 2)) - ) == true - ) - - #expect( - compareValues( - InlineEnum.node(value: Parity(rawValue: 0)), - InlineEnum.node(value: Parity(rawValue: 2)), - mode: .bitwise - ) == false - ) - - // POD because case is inline - #expect( - compareValues( - InlineEnum.node(value: Parity(rawValue: 0)), - InlineEnum.node(value: Parity(rawValue: 2)), - mode: .equatableUnlessPOD - ) == false - ) - - #expect( - compareValues( - InlineEnum.node(value: Parity(rawValue: 0)), - InlineEnum.node(value: Parity(rawValue: 2)), - mode: .equatableAlways - ) == true - ) + func compareInlineEnumValues() async { + await #expect(processExitsWith: .success) { + setenv("AG_ASYNC_LAYOUTS", "0", 1) + + #expect( + compareValues( + InlineEnum.empty, + InlineEnum.empty + ) == true + ) + + #expect( + compareValues( + InlineEnum.node(value: Parity(rawValue: 0)), + InlineEnum.empty + ) == false + ) + + #expect( + compareValues( + InlineEnum.empty, + InlineEnum.node(value: Parity(rawValue: 0)) + ) == false + ) + + #expect( + compareValues( + InlineEnum.node(value: Parity(rawValue: 0)), + InlineEnum.node(value: Parity(rawValue: 0)) + ) == true + ) + + #expect( + compareValues( + InlineEnum.node(value: Parity(rawValue: 0)), + InlineEnum.node(value: Parity(rawValue: 1)) + ) == false + ) + + // default mode is .equatableAlways + #expect( + compareValues( + InlineEnum.node(value: Parity(rawValue: 0)), + InlineEnum.node(value: Parity(rawValue: 2)) + ) == true + ) + + #expect( + compareValues( + InlineEnum.node(value: Parity(rawValue: 0)), + InlineEnum.node(value: Parity(rawValue: 2)), + mode: .bitwise + ) == false + ) + + // POD because case is inline + #expect( + compareValues( + InlineEnum.node(value: Parity(rawValue: 0)), + InlineEnum.node(value: Parity(rawValue: 2)), + mode: .equatableUnlessPOD + ) == false + ) + + #expect( + compareValues( + InlineEnum.node(value: Parity(rawValue: 0)), + InlineEnum.node(value: Parity(rawValue: 2)), + mode: .equatableAlways + ) == true + ) + } } @Test - func compareRecursiveEnumValues() { - setenv("AG_ASYNC_LAYOUTS", "0", 1) - - #expect( - compareValues( - RecursiveEnum.empty, - RecursiveEnum.empty - ) == true - ) - - #expect( - compareValues( - RecursiveEnum.node(value: Parity(rawValue: 0), next: .empty), - RecursiveEnum.empty - ) == false - ) - - #expect( - compareValues( - RecursiveEnum.empty, - RecursiveEnum.node(value: Parity(rawValue: 0), next: .empty) - ) == false - ) - - #expect( - compareValues( - RecursiveEnum.node(value: Parity(rawValue: 0), next: .empty), - RecursiveEnum.node(value: Parity(rawValue: 0), next: .empty) - ) == true - ) - - #expect( - compareValues( - RecursiveEnum.node(value: Parity(rawValue: 0), next: .empty), - RecursiveEnum.node(value: Parity(rawValue: 1), next: .empty) - ) == false - ) - - // default mode is .equatableAlways - #expect( - compareValues( - RecursiveEnum.node(value: Parity(rawValue: 0), next: .empty), - RecursiveEnum.node(value: Parity(rawValue: 2), next: .empty) - ) == true - ) - - #expect( - compareValues( - RecursiveEnum.node(value: Parity(rawValue: 0), next: .empty), - RecursiveEnum.node(value: Parity(rawValue: 2), next: .empty), - mode: .bitwise - ) == false - ) - - // not POD because case is indirect - #expect( - compareValues( - RecursiveEnum.node(value: Parity(rawValue: 0), next: .empty), - RecursiveEnum.node(value: Parity(rawValue: 2), next: .empty), - mode: .equatableUnlessPOD - ) == true - ) - - #expect( - compareValues( - RecursiveEnum.node(value: Parity(rawValue: 0), next: .empty), - RecursiveEnum.node(value: Parity(rawValue: 2), next: .empty), - mode: .equatableAlways - ) == true - ) + func compareRecursiveEnumValues() async { + await #expect(processExitsWith: .success) { + setenv("AG_ASYNC_LAYOUTS", "0", 1) + + #expect( + compareValues( + RecursiveEnum.empty, + RecursiveEnum.empty + ) == true + ) + + #expect( + compareValues( + RecursiveEnum.node(value: Parity(rawValue: 0), next: .empty), + RecursiveEnum.empty + ) == false + ) + + #expect( + compareValues( + RecursiveEnum.empty, + RecursiveEnum.node(value: Parity(rawValue: 0), next: .empty) + ) == false + ) + + #expect( + compareValues( + RecursiveEnum.node(value: Parity(rawValue: 0), next: .empty), + RecursiveEnum.node(value: Parity(rawValue: 0), next: .empty) + ) == true + ) + + #expect( + compareValues( + RecursiveEnum.node(value: Parity(rawValue: 0), next: .empty), + RecursiveEnum.node(value: Parity(rawValue: 1), next: .empty) + ) == false + ) + + // default mode is .equatableAlways + #expect( + compareValues( + RecursiveEnum.node(value: Parity(rawValue: 0), next: .empty), + RecursiveEnum.node(value: Parity(rawValue: 2), next: .empty) + ) == true + ) + + #expect( + compareValues( + RecursiveEnum.node(value: Parity(rawValue: 0), next: .empty), + RecursiveEnum.node(value: Parity(rawValue: 2), next: .empty), + mode: .bitwise + ) == false + ) + + // not POD because case is indirect + #expect( + compareValues( + RecursiveEnum.node(value: Parity(rawValue: 0), next: .empty), + RecursiveEnum.node(value: Parity(rawValue: 2), next: .empty), + mode: .equatableUnlessPOD + ) == true + ) + + #expect( + compareValues( + RecursiveEnum.node(value: Parity(rawValue: 0), next: .empty), + RecursiveEnum.node(value: Parity(rawValue: 2), next: .empty), + mode: .equatableAlways + ) == true + ) + } } @Test - func compareRecursiveEnumNonEquatableValues() { - setenv("AG_ASYNC_LAYOUTS", "0", 1) - - #expect( - compareValues( - RecursiveEnum.empty, - RecursiveEnum.empty - ) == true - ) - - #expect( - compareValues( - RecursiveEnum.node(value: WithExistential(type: Int.self, int: 0), next: .empty), - RecursiveEnum.empty - ) == false - ) - - #expect( - compareValues( - RecursiveEnum.empty, - RecursiveEnum.node(value: WithExistential(type: Int.self, int: 0), next: .empty) - ) == false - ) - - #expect( - compareValues( - RecursiveEnum.node(value: 62, next: .empty), - RecursiveEnum.node(value: 62, next: .empty) - ) == true - ) - - #expect( - compareValues( - RecursiveEnum.node(value: WithExistential(type: Int.self, int: 0), next: .empty), - RecursiveEnum.node(value: WithExistential(type: Int.self, int: 0), next: .empty) - ) == true - ) - - #expect( - compareValues( - RecursiveEnum.node(value: WithExistential(type: Int.self, int: 0), next: .empty), - RecursiveEnum.node(value: WithExistential(type: Int.self, int: 1), next: .empty) - ) == false - ) - - #expect( - compareValues( - RecursiveEnum.node(value: WithExistential(type: Int.self, int: 0), next: .empty), - RecursiveEnum.node(value: WithExistential(type: String.self, int: 0), next: .empty) - ) == false - ) + func compareRecursiveEnumNonEquatableValues() async { + await #expect(processExitsWith: .success) { + setenv("AG_ASYNC_LAYOUTS", "0", 1) + + #expect( + compareValues( + RecursiveEnum.empty, + RecursiveEnum.empty + ) == true + ) + + #expect( + compareValues( + RecursiveEnum.node(value: WithExistential(type: Int.self, int: 0), next: .empty), + RecursiveEnum.empty + ) == false + ) + + #expect( + compareValues( + RecursiveEnum.empty, + RecursiveEnum.node(value: WithExistential(type: Int.self, int: 0), next: .empty) + ) == false + ) + + #expect( + compareValues( + RecursiveEnum.node(value: 62, next: .empty), + RecursiveEnum.node(value: 62, next: .empty) + ) == true + ) + + #expect( + compareValues( + RecursiveEnum.node(value: WithExistential(type: Int.self, int: 0), next: .empty), + RecursiveEnum.node(value: WithExistential(type: Int.self, int: 0), next: .empty) + ) == true + ) + + #expect( + compareValues( + RecursiveEnum.node(value: WithExistential(type: Int.self, int: 0), next: .empty), + RecursiveEnum.node(value: WithExistential(type: Int.self, int: 1), next: .empty) + ) == false + ) + + #expect( + compareValues( + RecursiveEnum.node(value: WithExistential(type: Int.self, int: 0), next: .empty), + RecursiveEnum.node(value: WithExistential(type: String.self, int: 0), next: .empty) + ) == false + ) + } } } diff --git a/Tests/ComputeLayoutDescriptorTests/Shared/PrefetchCompareValuesTests.swift b/Tests/ComputeLayoutDescriptorTests/Shared/PrefetchCompareValuesTests.swift index 2c6592d6..f2c0de1d 100644 --- a/Tests/ComputeLayoutDescriptorTests/Shared/PrefetchCompareValuesTests.swift +++ b/Tests/ComputeLayoutDescriptorTests/Shared/PrefetchCompareValuesTests.swift @@ -106,41 +106,45 @@ struct PrefetchCompareValuesTests { } } - @Test(arguments: allOptions) - func layoutForVoid(with options: ComparisonOptions) { - setenv("AG_ASYNC_LAYOUTS", "0", 1) + @Test + func layoutForVoid() async { + await #expect(processExitsWith: .success) { + setenv("AG_ASYNC_LAYOUTS", "0", 1) - let layout = prefetchCompareValues(of: Void.self, options: options, priority: 0) - #expect(layout == .trivial) + for options in allOptions { + let layout = prefetchCompareValues(of: Void.self, options: options, priority: 0) + #expect(layout == .trivial) + } + } } @Test func layoutForBool() async { - setenv("AG_ASYNC_LAYOUTS", "0", 1) - setenv("AG_PRINT_LAYOUTS", "1", 1) + await #expect(processExitsWith: .success) { + setenv("AG_ASYNC_LAYOUTS", "0", 1) + setenv("AG_PRINT_LAYOUTS", "1", 1) - let layout0 = prefetchCompareValues( - of: Bool.self, - options: ComparisonOptions(mode: .bitwise), - priority: 0 - ) - #expect(layout0 == .trivial) + let layout0 = prefetchCompareValues( + of: Bool.self, + options: ComparisonOptions(mode: .bitwise), + priority: 0 + ) + #expect(layout0 == .trivial) - let layout1 = prefetchCompareValues( - of: Bool.self, - options: ComparisonOptions(mode: .indirect), - priority: 0 - ) - #expect(layout1 == .trivial) + let layout1 = prefetchCompareValues( + of: Bool.self, + options: ComparisonOptions(mode: .indirect), + priority: 0 + ) + #expect(layout1 == .trivial) - let layout2 = prefetchCompareValues( - of: Bool.self, - options: ComparisonOptions(mode: .equatableUnlessPOD), - priority: 0 - ) - #expect(layout2 == .trivial) + let layout2 = prefetchCompareValues( + of: Bool.self, + options: ComparisonOptions(mode: .equatableUnlessPOD), + priority: 0 + ) + #expect(layout2 == .trivial) - await #expect(processExitsWith: .success) { var output3 = "" let layout3 = await reprintingStandardError(to: &output3) { prefetchCompareValues( @@ -161,29 +165,28 @@ struct PrefetchCompareValuesTests { } } - @Test( - arguments: [ - (Int.self, MemoryLayout.size), - (Double.self, MemoryLayout.size), - (Float.self, MemoryLayout.size), - ] as [(Any.Type, Int)] - ) - func layoutForNumericNonEquatable(of type: Any.Type, size: Int) async { - setenv("AG_ASYNC_LAYOUTS", "0", 1) - setenv("AG_PRINT_LAYOUTS", "1", 1) + @Test + func layoutForNumericNonEquatable() async { + await #expect(processExitsWith: .success) { + setenv("AG_ASYNC_LAYOUTS", "0", 1) + setenv("AG_PRINT_LAYOUTS", "1", 1) - let layout0 = prefetchCompareValues(of: type, options: ComparisonOptions(mode: .bitwise), priority: 0) - #expect(layout0 == .trivial) + let types: [Any.Type] = [Int.self, Double.self, Float.self] + for type in types { + let layout0 = prefetchCompareValues(of: type, options: ComparisonOptions(mode: .bitwise), priority: 0) + #expect(layout0 == .trivial) - let layout1 = prefetchCompareValues(of: type, options: ComparisonOptions(mode: .indirect), priority: 0) - #expect(layout1 == .trivial) + let layout1 = prefetchCompareValues(of: type, options: ComparisonOptions(mode: .indirect), priority: 0) + #expect(layout1 == .trivial) - let layout2 = prefetchCompareValues( - of: type, - options: ComparisonOptions(mode: .equatableUnlessPOD), - priority: 0 - ) - #expect(layout2 == .trivial) + let layout2 = prefetchCompareValues( + of: type, + options: ComparisonOptions(mode: .equatableUnlessPOD), + priority: 0 + ) + #expect(layout2 == .trivial) + } + } } @Test @@ -262,24 +265,24 @@ struct PrefetchCompareValuesTests { @Test func layoutForString() async { - setenv("AG_ASYNC_LAYOUTS", "0", 1) - setenv("AG_PRINT_LAYOUTS", "1", 1) + await #expect(processExitsWith: .success) { + setenv("AG_ASYNC_LAYOUTS", "0", 1) + setenv("AG_PRINT_LAYOUTS", "1", 1) - let layout0 = prefetchCompareValues( - of: String.self, - options: ComparisonOptions(mode: .bitwise), - priority: 0 - ) - #expect(layout0 == .trivial) + let layout0 = prefetchCompareValues( + of: String.self, + options: ComparisonOptions(mode: .bitwise), + priority: 0 + ) + #expect(layout0 == .trivial) - let layout1 = prefetchCompareValues( - of: String.self, - options: ComparisonOptions(mode: .indirect), - priority: 0 - ) - #expect(layout1 == .trivial) + let layout1 = prefetchCompareValues( + of: String.self, + options: ComparisonOptions(mode: .indirect), + priority: 0 + ) + #expect(layout1 == .trivial) - await #expect(processExitsWith: .success) { var output2 = "" let layout2 = await reprintingStandardError(to: &output2) { prefetchCompareValues( @@ -297,9 +300,7 @@ struct PrefetchCompareValuesTests { """ ) - } - await #expect(processExitsWith: .success) { var output3 = "" let layout3 = await reprintingStandardError(to: &output3) { prefetchCompareValues( @@ -320,12 +321,16 @@ struct PrefetchCompareValuesTests { } } - @Test(arguments: allOptions) - func layoutForStaticString(with options: ComparisonOptions) { - setenv("AG_ASYNC_LAYOUTS", "0", 1) + @Test + func layoutForStaticString() async { + await #expect(processExitsWith: .success) { + setenv("AG_ASYNC_LAYOUTS", "0", 1) - let layout = prefetchCompareValues(of: StaticString.self, options: options, priority: 0) - #expect(layout == .trivial) + for options in allOptions { + let layout = prefetchCompareValues(of: StaticString.self, options: options, priority: 0) + #expect(layout == .trivial) + } + } } } @@ -343,82 +348,84 @@ struct PrefetchCompareValuesTests { @Test("Layout for struct enclosing single element equals the layout of the enclosed element") func layoutForStructEnclosingSingleElement() async { - setenv("AG_ASYNC_LAYOUTS", "0", 1) - - struct StructEnclosingSingleElement { - var property: Int - } - - let layout0 = prefetchCompareValues( - of: StructEnclosingSingleElement.self, - options: ComparisonOptions(mode: .bitwise), - priority: 0 - ) - #expect(layout0 == .trivial) + await #expect(processExitsWith: .success) { + setenv("AG_ASYNC_LAYOUTS", "0", 1) - let layout1 = prefetchCompareValues( - of: StructEnclosingSingleElement.self, - options: ComparisonOptions(mode: .indirect), - priority: 0 - ) - #expect(layout1 == .trivial) + struct StructEnclosingSingleElement { + var property: Int + } - let layout2 = prefetchCompareValues( - of: StructEnclosingSingleElement.self, - options: ComparisonOptions(mode: .equatableUnlessPOD), - priority: 0 - ) - #expect(layout2 == .trivial) + let layout0 = prefetchCompareValues( + of: StructEnclosingSingleElement.self, + options: ComparisonOptions(mode: .bitwise), + priority: 0 + ) + #expect(layout0 == .trivial) - let innerLayout = prefetchCompareValues( - of: Int.self, - options: ComparisonOptions(mode: .equatableAlways), - priority: 0 - ) + let layout1 = prefetchCompareValues( + of: StructEnclosingSingleElement.self, + options: ComparisonOptions(mode: .indirect), + priority: 0 + ) + #expect(layout1 == .trivial) - var output3 = "" - let layout3 = await reprintingStandardError(to: &output3) { - prefetchCompareValues( + let layout2 = prefetchCompareValues( of: StructEnclosingSingleElement.self, + options: ComparisonOptions(mode: .equatableUnlessPOD), + priority: 0 + ) + #expect(layout2 == .trivial) + + let innerLayout = prefetchCompareValues( + of: Int.self, options: ComparisonOptions(mode: .equatableAlways), priority: 0 ) + + var output3 = "" + let layout3 = await reprintingStandardError(to: &output3) { + prefetchCompareValues( + of: StructEnclosingSingleElement.self, + options: ComparisonOptions(mode: .equatableAlways), + priority: 0 + ) + } + #expect(layout3 == innerLayout) } - #expect(layout3 == innerLayout) } @Test func layoutForTrivialStruct() async { - setenv("AG_ASYNC_LAYOUTS", "0", 1) - setenv("AG_PRINT_LAYOUTS", "1", 1) + await #expect(processExitsWith: .success) { + setenv("AG_ASYNC_LAYOUTS", "0", 1) + setenv("AG_PRINT_LAYOUTS", "1", 1) - struct TrivialStruct { - var first: Int - var second: Int - } + struct TrivialStruct { + var first: Int + var second: Int + } - let layout0 = prefetchCompareValues( - of: TrivialStruct.self, - options: ComparisonOptions(mode: .bitwise), - priority: 0 - ) - #expect(layout0 == .trivial) + let layout0 = prefetchCompareValues( + of: TrivialStruct.self, + options: ComparisonOptions(mode: .bitwise), + priority: 0 + ) + #expect(layout0 == .trivial) - let layout1 = prefetchCompareValues( - of: TrivialStruct.self, - options: ComparisonOptions(mode: .indirect), - priority: 0 - ) - #expect(layout1 == .trivial) + let layout1 = prefetchCompareValues( + of: TrivialStruct.self, + options: ComparisonOptions(mode: .indirect), + priority: 0 + ) + #expect(layout1 == .trivial) - let layout2 = prefetchCompareValues( - of: TrivialStruct.self, - options: ComparisonOptions(mode: .equatableUnlessPOD), - priority: 0 - ) - #expect(layout2 == .trivial) + let layout2 = prefetchCompareValues( + of: TrivialStruct.self, + options: ComparisonOptions(mode: .equatableUnlessPOD), + priority: 0 + ) + #expect(layout2 == .trivial) - await #expect(processExitsWith: .success) { let _ = prefetchCompareValues( of: Int.self, options: ComparisonOptions(mode: .equatableAlways), @@ -712,17 +719,21 @@ struct PrefetchCompareValuesTests { #expect(layout == nil) } - @Test(arguments: allOptions) - func layoutForStructWithWeakVar(with options: ComparisonOptions) { - setenv("AG_ASYNC_LAYOUTS", "0", 1) + @Test + func layoutForStructWithWeakVar() async { + await #expect(processExitsWith: .success) { + setenv("AG_ASYNC_LAYOUTS", "0", 1) - class EmptyClass {} - struct StructWithWeakVar { - weak var property: EmptyClass? - } + class EmptyClass {} + struct StructWithWeakVar { + weak var property: EmptyClass? + } - let layout = prefetchCompareValues(of: StructWithWeakVar.self, options: options, priority: 0) - #expect(layout == .trivial) + for options in allOptions { + let layout = prefetchCompareValues(of: StructWithWeakVar.self, options: options, priority: 0) + #expect(layout == .trivial) + } + } } } @@ -1190,24 +1201,24 @@ struct PrefetchCompareValuesTests { @Test func layoutForTuple() async { - setenv("AG_ASYNC_LAYOUTS", "0", 1) - setenv("AG_PRINT_LAYOUTS", "1", 1) + await #expect(processExitsWith: .success) { + setenv("AG_ASYNC_LAYOUTS", "0", 1) + setenv("AG_PRINT_LAYOUTS", "1", 1) - let layout0 = prefetchCompareValues( - of: (Int, String).self, - options: ComparisonOptions(mode: .bitwise), - priority: 0 - ) - #expect(layout0 == .trivial) + let layout0 = prefetchCompareValues( + of: (Int, String).self, + options: ComparisonOptions(mode: .bitwise), + priority: 0 + ) + #expect(layout0 == .trivial) - let layout1 = prefetchCompareValues( - of: (Int, String).self, - options: ComparisonOptions(mode: .indirect), - priority: 0 - ) - #expect(layout1 == .trivial) + let layout1 = prefetchCompareValues( + of: (Int, String).self, + options: ComparisonOptions(mode: .indirect), + priority: 0 + ) + #expect(layout1 == .trivial) - await #expect(processExitsWith: .success) { let _ = prefetchCompareValues( of: String.self, options: ComparisonOptions(mode: .equatableUnlessPOD), @@ -1232,9 +1243,7 @@ struct PrefetchCompareValuesTests { """ ) - } - await #expect(processExitsWith: .success) { let _ = prefetchCompareValues( of: Int.self, options: ComparisonOptions(mode: .equatableAlways), @@ -1379,24 +1388,24 @@ struct PrefetchCompareValuesTests { @Test func layoutForArray() async { - setenv("AG_ASYNC_LAYOUTS", "0", 1) - setenv("AG_PRINT_LAYOUTS", "1", 1) + await #expect(processExitsWith: .success) { + setenv("AG_ASYNC_LAYOUTS", "0", 1) + setenv("AG_PRINT_LAYOUTS", "1", 1) - let layout0 = prefetchCompareValues( - of: Array.self, - options: ComparisonOptions(mode: .bitwise), - priority: 0 - ) - #expect(layout0 == .trivial) + let layout0 = prefetchCompareValues( + of: Array.self, + options: ComparisonOptions(mode: .bitwise), + priority: 0 + ) + #expect(layout0 == .trivial) - let layout1 = prefetchCompareValues( - of: Array.self, - options: ComparisonOptions(mode: .indirect), - priority: 0 - ) - #expect(layout1 == .trivial) + let layout1 = prefetchCompareValues( + of: Array.self, + options: ComparisonOptions(mode: .indirect), + priority: 0 + ) + #expect(layout1 == .trivial) - await #expect(processExitsWith: .success) { var output2 = "" let layout2 = await reprintingStandardError(to: &output2) { prefetchCompareValues( @@ -1414,9 +1423,7 @@ struct PrefetchCompareValuesTests { """ ) - } - await #expect(processExitsWith: .success) { var output3 = "" let layout3 = await reprintingStandardError(to: &output3) { prefetchCompareValues( @@ -1437,36 +1444,40 @@ struct PrefetchCompareValuesTests { } } - @Test(arguments: allOptions) - func layoutForArrayOfNotEquatable(with options: ComparisonOptions) { - setenv("AG_ASYNC_LAYOUTS", "0", 1) + @Test + func layoutForArrayOfNotEquatable() async { + await #expect(processExitsWith: .success) { + setenv("AG_ASYNC_LAYOUTS", "0", 1) - class NotEquatable {} + class NotEquatable {} - let layout = prefetchCompareValues(of: Array.self, options: options, priority: 0) - #expect(layout == .trivial) + for options in allOptions { + let layout = prefetchCompareValues(of: Array.self, options: options, priority: 0) + #expect(layout == .trivial) + } + } } @Test func layoutForDictionary() async { - setenv("AG_ASYNC_LAYOUTS", "0", 1) - setenv("AG_PRINT_LAYOUTS", "1", 1) + await #expect(processExitsWith: .success) { + setenv("AG_ASYNC_LAYOUTS", "0", 1) + setenv("AG_PRINT_LAYOUTS", "1", 1) - let layout0 = prefetchCompareValues( - of: Dictionary.self, - options: ComparisonOptions(mode: .bitwise), - priority: 0 - ) - #expect(layout0 == .trivial) + let layout0 = prefetchCompareValues( + of: Dictionary.self, + options: ComparisonOptions(mode: .bitwise), + priority: 0 + ) + #expect(layout0 == .trivial) - let layout1 = prefetchCompareValues( - of: Dictionary.self, - options: ComparisonOptions(mode: .indirect), - priority: 0 - ) - #expect(layout1 == .trivial) + let layout1 = prefetchCompareValues( + of: Dictionary.self, + options: ComparisonOptions(mode: .indirect), + priority: 0 + ) + #expect(layout1 == .trivial) - await #expect(processExitsWith: .success) { var output2 = "" let layout2 = await reprintingStandardError(to: &output2) { prefetchCompareValues( @@ -1484,9 +1495,7 @@ struct PrefetchCompareValuesTests { """ ) - } - await #expect(processExitsWith: .success) { var output3 = "" let layout3 = await reprintingStandardError(to: &output3) { prefetchCompareValues( @@ -1507,40 +1516,44 @@ struct PrefetchCompareValuesTests { } } - @Test(arguments: allOptions) - func layoutForDictionaryOfNotEquatable(with options: ComparisonOptions) { - setenv("AG_ASYNC_LAYOUTS", "0", 1) + @Test + func layoutForDictionaryOfNotEquatable() async { + await #expect(processExitsWith: .success) { + setenv("AG_ASYNC_LAYOUTS", "0", 1) - class NotEquatable {} + class NotEquatable {} - let layout = prefetchCompareValues( - of: Dictionary.self, - options: options, - priority: 0 - ) - #expect(layout == .trivial) + for options in allOptions { + let layout = prefetchCompareValues( + of: Dictionary.self, + options: options, + priority: 0 + ) + #expect(layout == .trivial) + } + } } @Test func layoutForSet() async { - setenv("AG_ASYNC_LAYOUTS", "0", 1) - setenv("AG_PRINT_LAYOUTS", "1", 1) + await #expect(processExitsWith: .success) { + setenv("AG_ASYNC_LAYOUTS", "0", 1) + setenv("AG_PRINT_LAYOUTS", "1", 1) - let layout0 = prefetchCompareValues( - of: Set.self, - options: ComparisonOptions(mode: .bitwise), - priority: 0 - ) - #expect(layout0 == .trivial) + let layout0 = prefetchCompareValues( + of: Set.self, + options: ComparisonOptions(mode: .bitwise), + priority: 0 + ) + #expect(layout0 == .trivial) - let layout1 = prefetchCompareValues( - of: Set.self, - options: ComparisonOptions(mode: .indirect), - priority: 0 - ) - #expect(layout1 == .trivial) + let layout1 = prefetchCompareValues( + of: Set.self, + options: ComparisonOptions(mode: .indirect), + priority: 0 + ) + #expect(layout1 == .trivial) - await #expect(processExitsWith: .success) { var output2 = "" let layout2 = await reprintingStandardError(to: &output2) { prefetchCompareValues( @@ -1558,9 +1571,7 @@ struct PrefetchCompareValuesTests { """ ) - } - await #expect(processExitsWith: .success) { var output3 = "" let layout3 = await reprintingStandardError(to: &output3) { prefetchCompareValues( @@ -1588,38 +1599,38 @@ struct PrefetchCompareValuesTests { @Test func layoutForEquatableStruct() async { - setenv("AG_ASYNC_LAYOUTS", "0", 1) - setenv("AG_PRINT_LAYOUTS", "1", 1) - - struct EquatableStruct: Equatable { - var property: Int = 0 - static func == (lhs: EquatableStruct, rhs: EquatableStruct) -> Bool { - return false + await #expect(processExitsWith: .success) { + setenv("AG_ASYNC_LAYOUTS", "0", 1) + setenv("AG_PRINT_LAYOUTS", "1", 1) + + struct EquatableStruct: Equatable { + var property: Int = 0 + static func == (lhs: EquatableStruct, rhs: EquatableStruct) -> Bool { + return false + } } - } - let layout0 = prefetchCompareValues( - of: EquatableStruct.self, - options: ComparisonOptions(mode: .bitwise), - priority: 0 - ) - #expect(layout0 == .trivial) + let layout0 = prefetchCompareValues( + of: EquatableStruct.self, + options: ComparisonOptions(mode: .bitwise), + priority: 0 + ) + #expect(layout0 == .trivial) - let layout1 = prefetchCompareValues( - of: EquatableStruct.self, - options: ComparisonOptions(mode: .indirect), - priority: 0 - ) - #expect(layout1 == .trivial) + let layout1 = prefetchCompareValues( + of: EquatableStruct.self, + options: ComparisonOptions(mode: .indirect), + priority: 0 + ) + #expect(layout1 == .trivial) - let layout2 = prefetchCompareValues( - of: EquatableStruct.self, - options: ComparisonOptions(mode: .equatableUnlessPOD), - priority: 0 - ) - #expect(layout2 == .trivial) + let layout2 = prefetchCompareValues( + of: EquatableStruct.self, + options: ComparisonOptions(mode: .equatableUnlessPOD), + priority: 0 + ) + #expect(layout2 == .trivial) - await #expect(processExitsWith: .success) { var output3 = "" let layout3 = await reprintingStandardError(to: &output3) { prefetchCompareValues(