@@ -671,6 +671,18 @@ private func printLabels(_ st : TestResult) {
671671 }
672672}
673673
674+ /// Takes a sequence of trees of test results from several properties into
675+ /// a single labelled rose tree for the conjunction.
676+ ///
677+ /// This function is written in continuation passsing style to facilitate the
678+ /// construction of a lazy rose tree that preserves the labels and callbacks
679+ /// associated with the sub-properties.
680+ ///
681+ /// The general idea is that the properties are tested from first to last. If
682+ /// any of them fails, the conjunct need not evaluate further and returns that
683+ /// first failing result. Else, each passing sub-property continues on to the
684+ /// next sub-property and decorates the overall result with its callbacks and
685+ /// labels. Discards act to skip that sub-property, as expected.
674686private func conj( _ k : @escaping ( TestResult ) -> TestResult , xs : [ Rose < TestResult > ] ) -> Rose < TestResult > {
675687 guard let p = xs. first else {
676688 return Rose . mkRose ( { k ( TestResult . succeeded) } , { [ ] } )
@@ -679,16 +691,20 @@ private func conj(_ k : @escaping (TestResult) -> TestResult, xs : [Rose<TestRes
679691 let rose = p. reduce
680692 switch rose {
681693 case . mkRose( let result, _) :
682- if ! result( ) . expect {
694+ guard result ( ) . expect else {
683695 return Rose . pure ( TestResult . failed ( " expectFailure may not occur inside a conjunction " ) )
684696 }
685697
686698 switch result ( ) . ok {
687699 case . some( true ) :
700+ // Passed - Adjoin the labels and test the next property.
688701 return conj ( comp ( addLabels ( result ( ) ) , comp ( addCallbacks ( result ( ) ) , k) ) , xs: [ Rose < TestResult > ] ( xs [ 1 ..< xs. endIndex] ) )
689702 case . some( false ) :
703+ // Failed - Return the first failure and don't continue
704+ // evaluating the other conjuncts.
690705 return rose
691706 case . none:
707+ // Discard - Try the next property.
692708 let rose2 = conj ( comp ( addCallbacks ( result ( ) ) , k) , xs: [ Rose < TestResult > ] ( xs [ 1 ..< xs. endIndex] ) ) . reduce
693709 switch rose2 {
694710 case . mkRose( let result2, _) :
@@ -711,23 +727,28 @@ private func conj(_ k : @escaping (TestResult) -> TestResult, xs : [Rose<TestRes
711727 } ) )
712728}
713729
730+ /// Computes the disjunction of two test results.
714731private func disj( _ p : Rose < TestResult > , q : Rose < TestResult > ) -> Rose < TestResult > {
715732 return p. flatMap { result1 in
716- if ! result1. expect {
733+ guard result1. expect else {
717734 return Rose< TestResult> . pure( TestResult . failed ( " expectFailure may not occur inside a disjunction " ) )
718735 }
719736 switch result1. ok {
720737 case . some( true ) :
738+ // Passed - Don't evaluate the other tree.
721739 return Rose< TestResult> . pure( result1)
722740 case . some( false ) :
723- return q. flatMap { ( result2 : TestResult ) in
724- if !result2. expect {
741+ // Failed - Try the other tree.
742+ return q. flatMap { result2 in
743+ guard result2. expect else {
725744 return Rose< TestResult> . pure( TestResult . failed ( " expectFailure may not occur inside a disjunction " ) )
726745 }
727746 switch result2. ok {
728747 case . some( true ) :
729748 return Rose< TestResult> . pure( result2)
730749 case . some( false ) :
750+ // Failed - Combine the exceptions and callbacks from each
751+ // sub-property.
731752 let callbacks : [ Callback ] = [ . afterFinalFailure( kind: . counterexample, { ( _, _) in return print ( " " ) } ) ]
732753 return Rose< TestResult> . pure( TestResult (
733754 ok: . some( false ) ,
@@ -745,8 +766,9 @@ private func disj(_ p : Rose<TestResult>, q : Rose<TestResult>) -> Rose<TestResu
745766 }
746767 }
747768 case . none:
748- return q. flatMap { ( result2 : TestResult ) in
749- if !result2. expect {
769+ // Discarded - Try the other property.
770+ return q. flatMap { result2 in
771+ guard result2. expect else {
750772 return Rose< TestResult> . pure( TestResult . failed ( " expectFailure may not occur inside a disjunction " ) )
751773 }
752774 switch result2. ok {
0 commit comments