@@ -429,6 +429,104 @@ struct PlanTests {
429
429
#expect( moduleGraph. children. count == 1 )
430
430
}
431
431
432
+ @Suite ( " Containing suite types without @Suite are synthesized " )
433
+ struct ContainingSuiteSynthesis {
434
+ @Test ( " A test function inside a top-level implicit suite " )
435
+ func oneImplicitParent( ) async throws {
436
+ let plan = await Runner . Plan ( selecting: ImplicitParentSuite_A . self)
437
+ let testNames = plan. stepGraph. compactMap { $0. value } . map ( \. test. name)
438
+ #expect( testNames == [
439
+ " ImplicitParentSuite_A " ,
440
+ " example() " ,
441
+ ] )
442
+
443
+ let testFunction = try #require( plan. steps. map ( \. test) . first { $0. name == " example() " } )
444
+ let implicitParentSuite_A = try #require( plan. steps. map ( \. test) . first { $0. name == " ImplicitParentSuite_A " } )
445
+ #expect( implicitParentSuite_A. sourceLocation == testFunction. sourceLocation)
446
+ #expect( implicitParentSuite_A. isSynthesized)
447
+ }
448
+
449
+ @Test ( " A test function in a type hierarchy where the nearest suite is explicit and outer ones are implicit " , arguments: [
450
+ ImplicitGrandparentSuite_B . self,
451
+ ImplicitGrandparentSuite_B . ImplicitParentSuite_B. self,
452
+ ImplicitGrandparentSuite_B . ImplicitParentSuite_B. ExplicitChildSuite_B. self,
453
+ ] as [ Any . Type ] )
454
+ func twoImplicitAncestorsButExplicitParent( suiteType: Any . Type ) async throws {
455
+ let plan = await Runner . Plan ( selecting: suiteType)
456
+ let testNames = plan. stepGraph. compactMap { $0. value } . map ( \. test. name)
457
+ #expect( testNames == [
458
+ " ImplicitGrandparentSuite_B " ,
459
+ " ImplicitParentSuite_B " ,
460
+ " ExplicitChildSuite_B " ,
461
+ " example() " ,
462
+ ] )
463
+
464
+ let testFunction = try #require( plan. steps. map ( \. test) . first { $0. name == " example() " } )
465
+ let explicitChildSuite_B = try #require( plan. steps. map ( \. test) . first { $0. name == " ExplicitChildSuite_B " } )
466
+ #expect( explicitChildSuite_B. sourceLocation != testFunction. sourceLocation)
467
+ #expect( !explicitChildSuite_B. isSynthesized)
468
+
469
+ let implicitParentSuite_B = try #require( plan. steps. map ( \. test) . first { $0. name == " ImplicitParentSuite_B " } )
470
+ #expect( implicitParentSuite_B. sourceLocation == explicitChildSuite_B. sourceLocation)
471
+ #expect( implicitParentSuite_B. isSynthesized)
472
+
473
+ let implicitGrandparentSuite_B = try #require( plan. steps. map ( \. test) . first { $0. name == " ImplicitGrandparentSuite_B " } )
474
+ #expect( implicitGrandparentSuite_B. sourceLocation == implicitParentSuite_B. sourceLocation)
475
+ #expect( implicitGrandparentSuite_B. isSynthesized)
476
+ }
477
+
478
+ @Test ( " A test function in a type hierarchy with both explicit and implicit suites " )
479
+ func mixedAncestors( ) async throws {
480
+ let plan = await Runner . Plan ( selecting: ExplicitGrandparentSuite_C . self)
481
+ let testNames = plan. stepGraph. compactMap { $0. value } . map ( \. test. name)
482
+ #expect( testNames == [
483
+ " ExplicitGrandparentSuite_C " ,
484
+ " ImplicitParentSuite_C " ,
485
+ " ExplicitChildSuite_C " ,
486
+ " example() " ,
487
+ ] )
488
+
489
+ let testFunction = try #require( plan. steps. map ( \. test) . first { $0. name == " example() " } )
490
+ let explicitChildSuite_C = try #require( plan. steps. map ( \. test) . first { $0. name == " ExplicitChildSuite_C " } )
491
+ #expect( explicitChildSuite_C. sourceLocation != testFunction. sourceLocation)
492
+ #expect( !explicitChildSuite_C. isSynthesized)
493
+
494
+ let implicitParentSuite_C = try #require( plan. steps. map ( \. test) . first { $0. name == " ImplicitParentSuite_C " } )
495
+ #expect( implicitParentSuite_C. sourceLocation == explicitChildSuite_C. sourceLocation)
496
+ #expect( implicitParentSuite_C. isSynthesized)
497
+
498
+ let explicitGrandparentSuite_C = try #require( plan. steps. map ( \. test) . first { $0. name == " ExplicitGrandparentSuite_C " } )
499
+ #expect( explicitGrandparentSuite_C. sourceLocation != implicitParentSuite_C. sourceLocation)
500
+ #expect( !explicitGrandparentSuite_C. isSynthesized)
501
+ }
502
+
503
+ @Test ( " A test function in a type hierarchy with all implicit suites " )
504
+ func allImplicitAncestors( ) async throws {
505
+ let plan = await Runner . Plan ( selecting: ImplicitGrandparentSuite_D . self)
506
+ let testNames = plan. stepGraph. compactMap { $0. value } . map ( \. test. name)
507
+ #expect( testNames == [
508
+ " ImplicitGrandparentSuite_D " ,
509
+ " ImplicitParentSuite_D " ,
510
+ " ImplicitChildSuite_D " ,
511
+ " ImplicitGrandchildSuite_D " ,
512
+ " example() " ,
513
+ ] )
514
+
515
+ let testFunction = try #require( plan. steps. map ( \. test) . first { $0. name == " example() " } )
516
+ let implicitGrandchildSuite_D = try #require( plan. steps. map ( \. test) . first { $0. name == " ImplicitGrandchildSuite_D " } )
517
+ #expect( implicitGrandchildSuite_D. sourceLocation == testFunction. sourceLocation)
518
+
519
+ let implicitChildSuite_D = try #require( plan. steps. map ( \. test) . first { $0. name == " ImplicitChildSuite_D " } )
520
+ #expect( implicitChildSuite_D. sourceLocation == implicitGrandchildSuite_D. sourceLocation)
521
+
522
+ let implicitParentSuite_D = try #require( plan. steps. map ( \. test) . first { $0. name == " ImplicitParentSuite_D " } )
523
+ #expect( implicitParentSuite_D. sourceLocation == implicitChildSuite_D. sourceLocation)
524
+
525
+ let implicitGrandparentSuite_D = try #require( plan. steps. map ( \. test) . first { $0. name == " ImplicitGrandparentSuite_D " } )
526
+ #expect( implicitGrandparentSuite_D. sourceLocation == implicitParentSuite_D. sourceLocation)
527
+ }
528
+ }
529
+
432
530
#if !SWT_NO_SNAPSHOT_TYPES
433
531
@Test ( " Test cases of a disabled test are not evaluated " )
434
532
func disabledTestCases( ) async throws {
@@ -486,3 +584,42 @@ private struct BasicRecursiveTrait: SuiteTrait, TestTrait, CustomStringConvertib
486
584
self . description = description
487
585
}
488
586
}
587
+
588
+ // This fixture must not have an explicit `@Suite` attribute to validate suite
589
+ // synthesis. Its children can be `.hidden`, though.
590
+ fileprivate struct ImplicitParentSuite_A {
591
+ @Test ( . hidden) func example( ) { }
592
+ }
593
+
594
+ fileprivate struct ImplicitGrandparentSuite_B {
595
+ // This fixture must not have an explicit `@Suite` attribute to validate suite
596
+ // synthesis. Its children can be `.hidden`, though.
597
+ struct ImplicitParentSuite_B {
598
+ @Suite ( . hidden) struct ExplicitChildSuite_B {
599
+ @Test func example( ) { }
600
+ }
601
+ }
602
+ }
603
+
604
+ @Suite ( . hidden) // This one intentionally _does_ have `@Suite`.
605
+ fileprivate struct ExplicitGrandparentSuite_C {
606
+ // This fixture must not have an explicit `@Suite` attribute to validate suite
607
+ // synthesis. Its children can be `.hidden`, though.
608
+ struct ImplicitParentSuite_C {
609
+ @Suite struct ExplicitChildSuite_C {
610
+ @Test func example( ) { }
611
+ }
612
+ }
613
+ }
614
+
615
+ // These fixture suites must not have explicit `@Suite` attributes to validate
616
+ // suite synthesis.
617
+ fileprivate struct ImplicitGrandparentSuite_D {
618
+ struct ImplicitParentSuite_D {
619
+ struct ImplicitChildSuite_D {
620
+ struct ImplicitGrandchildSuite_D {
621
+ @Test ( . hidden) func example( ) { }
622
+ }
623
+ }
624
+ }
625
+ }
0 commit comments