Skip to content

Commit 4cdbaf6

Browse files
authored
Add predicate keypath assertion exit tests (#1476)
1 parent 92b1b02 commit 4cdbaf6

File tree

2 files changed

+102
-1
lines changed

2 files changed

+102
-1
lines changed

Sources/FoundationEssentials/Predicate/KeyPath+Inspection.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ extension UInt32 {
5050
extension AnyKeyPath {
5151
private static var WORD_SIZE: Int { MemoryLayout<Int>.size }
5252

53-
func _validateForPredicateUsage(restrictArguments: Bool = false) {
53+
func _validateForPredicateUsage(restrictArguments: Bool = true) {
5454
var ptr = unsafeBitCast(self, to: UnsafeRawPointer.self)
5555
ptr = ptr.advanced(by: Self.WORD_SIZE * 3) // skip isa, type metadata, and KVC string pointers
5656
let header = ptr.load(as: UInt32.self)

Tests/FoundationEssentialsTests/PredicateTests.swift

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,107 @@ private struct PredicateTests {
317317
}
318318
_ = #Predicate<Foo> { $0.id == 2 }
319319
}
320+
321+
#if FOUNDATION_EXIT_TESTS
322+
@Test func unsupportedKeyPaths() async {
323+
struct Sample {
324+
let stored: Sample2
325+
var immutableComputed: Sample2 { fatalError() }
326+
var mutableComputed: Sample2 {
327+
get { fatalError() }
328+
set { fatalError() }
329+
}
330+
var optional: Sample2? { fatalError() }
331+
332+
subscript(_ arg: Int) -> Sample2 { fatalError() }
333+
subscript() -> Sample2 { fatalError() }
334+
}
335+
336+
struct Sample2 {
337+
var prop: Int
338+
}
339+
340+
// multiple components
341+
await #expect(processExitsWith: .failure) {
342+
_ = PredicateExpressions.KeyPath(
343+
root: PredicateExpressions.Variable(),
344+
keyPath: \Sample.stored.prop
345+
)
346+
}
347+
await #expect(processExitsWith: .failure) {
348+
_ = PredicateExpressions.KeyPath(
349+
root: PredicateExpressions.Variable(),
350+
keyPath: \Sample.immutableComputed.prop
351+
)
352+
}
353+
await #expect(processExitsWith: .failure) {
354+
_ = PredicateExpressions.KeyPath(
355+
root: PredicateExpressions.Variable(),
356+
keyPath: \Sample.mutableComputed.prop
357+
)
358+
}
359+
await #expect(processExitsWith: .failure) {
360+
_ = PredicateExpressions.KeyPath(
361+
root: PredicateExpressions.Variable(),
362+
keyPath: \Sample.optional?.prop
363+
)
364+
}
365+
await #expect(processExitsWith: .failure) {
366+
_ = PredicateExpressions.KeyPath(
367+
root: PredicateExpressions.Variable(),
368+
keyPath: \Sample.[1].prop
369+
)
370+
}
371+
372+
// subscripts with arguments
373+
await #expect(processExitsWith: .failure) {
374+
_ = PredicateExpressions.KeyPath(
375+
root: PredicateExpressions.Variable(),
376+
keyPath: \Sample.[0]
377+
)
378+
}
379+
380+
// Optional chaining
381+
await #expect(processExitsWith: .failure) {
382+
_ = PredicateExpressions.KeyPath(
383+
root: PredicateExpressions.Variable(),
384+
keyPath: \Sample.optional?
385+
)
386+
}
387+
await #expect(processExitsWith: .failure) {
388+
_ = PredicateExpressions.KeyPath(
389+
root: PredicateExpressions.Variable(),
390+
keyPath: \Sample?.?
391+
)
392+
}
393+
await #expect(processExitsWith: .failure) {
394+
_ = PredicateExpressions.KeyPath(
395+
root: PredicateExpressions.Variable(),
396+
keyPath: \Sample?.?.stored
397+
)
398+
}
399+
400+
// Force unwrapping
401+
await #expect(processExitsWith: .failure) {
402+
_ = PredicateExpressions.KeyPath(
403+
root: PredicateExpressions.Variable(),
404+
keyPath: \Sample.optional!
405+
)
406+
}
407+
await #expect(processExitsWith: .failure) {
408+
_ = PredicateExpressions.KeyPath(
409+
root: PredicateExpressions.Variable(),
410+
keyPath: \Sample?.!
411+
)
412+
}
413+
await #expect(processExitsWith: .failure) {
414+
_ = PredicateExpressions.KeyPath(
415+
root: PredicateExpressions.Variable(),
416+
keyPath: \Sample?.!.stored
417+
)
418+
}
419+
}
420+
#endif
320421

321422
@Test
322423
func regex() throws {

0 commit comments

Comments
 (0)