@@ -429,6 +429,104 @@ struct PlanTests {
429429 #expect( moduleGraph. children. count == 1 )
430430 }
431431
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+
432530#if !SWT_NO_SNAPSHOT_TYPES
433531 @Test ( " Test cases of a disabled test are not evaluated " )
434532 func disabledTestCases( ) async throws {
@@ -486,3 +584,42 @@ private struct BasicRecursiveTrait: SuiteTrait, TestTrait, CustomStringConvertib
486584 self . description = description
487585 }
488586}
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