@@ -148,6 +148,16 @@ module Make<InlineExpectationsTestSig Impl> {
148148 bindingset [ expectedTag]
149149 default predicate tagIsOptional ( string expectedTag ) { none ( ) }
150150
151+ /**
152+ * Holds if expected tag `expectedTag` matches actual tag `actualTag`.
153+ *
154+ * This is normally defined as `expectedTag = actualTag`.
155+ */
156+ bindingset [ expectedValue, actualValue]
157+ default predicate valueMatches ( string expectedValue , string actualValue ) {
158+ expectedValue = actualValue
159+ }
160+
151161 /**
152162 * Returns the actual results of the query that is being tested. Each result consist of the
153163 * following values:
@@ -324,7 +334,7 @@ module Make<InlineExpectationsTestSig Impl> {
324334 predicate matchesActualResult ( ActualTestResult actualResult ) {
325335 onSameLine ( pragma [ only_bind_into ] ( this ) , actualResult ) and
326336 TestImpl:: tagMatches ( this .getTag ( ) , actualResult .getTag ( ) ) and
327- this .getValue ( ) = actualResult .getValue ( )
337+ TestImpl :: valueMatches ( this .getValue ( ) , actualResult .getValue ( ) )
328338 }
329339
330340 predicate isOptional ( ) { TestImpl:: tagIsOptional ( tag ) }
@@ -351,6 +361,15 @@ module Make<InlineExpectationsTestSig Impl> {
351361 string getExpectation ( ) { result = expectation }
352362 }
353363
364+ ValidTestExpectation getAMatchingExpectation (
365+ Impl:: Location location , string element , string tag , string val , boolean optional
366+ ) {
367+ exists ( ActualTestResult actualResult |
368+ result .matchesActualResult ( actualResult ) and
369+ actualResult = TActualResult ( location , element , tag , val , optional )
370+ )
371+ }
372+
354373 query predicate testFailures ( FailureLocatable element , string message ) {
355374 hasFailureMessage ( element , message )
356375 }
@@ -622,6 +641,8 @@ module TestPostProcessing {
622641
623642 private string getQueryId ( ) { queryMetadata ( "id" , result ) }
624643
644+ private string getQueryKind ( ) { queryMetadata ( "kind" , result ) }
645+
625646 signature module InputSig< InlineExpectationsTestSig Input> {
626647 string getRelativeUrl ( Input:: Location location ) ;
627648 }
@@ -630,43 +651,158 @@ module TestPostProcessing {
630651 private import InlineExpectationsTest as InlineExpectationsTest
631652 private import InlineExpectationsTest:: Make< Input >
632653
654+ /**
655+ * Gets the tag to be used for the path-problem source at result row `row`.
656+ *
657+ * This is either `Source` or `Alert`, depending on whether the location
658+ * of the source matches the location of the alert.
659+ */
660+ private string getSourceTag ( int row ) {
661+ getQueryKind ( ) = "path-problem" and
662+ exists ( string loc | queryResults ( "#select" , row , 2 , loc ) |
663+ if queryResults ( "#select" , row , 0 , loc ) then result = "Alert" else result = "Source"
664+ )
665+ }
666+
667+ /**
668+ * Gets the tag to be used for the path-problem sink at result row `row`.
669+ *
670+ * This is either `Sink` or `Alert`, depending on whether the location
671+ * of the sink matches the location of the alert.
672+ */
673+ private string getSinkTag ( int row ) {
674+ getQueryKind ( ) = "path-problem" and
675+ exists ( string loc | queryResults ( "#select" , row , 4 , loc ) |
676+ if queryResults ( "#select" , row , 0 , loc ) then result = "Alert" else result = "Sink"
677+ )
678+ }
679+
680+ /**
681+ * A configuration for matching `// $ Source=foo` comments against actual
682+ * path-problem sources.
683+ */
684+ private module PathProblemSourceTestInput implements TestSig {
685+ string getARelevantTag ( ) { result = getSourceTag ( _) }
686+
687+ bindingset [ expectedValue, actualValue]
688+ predicate valueMatches ( string expectedValue , string actualValue ) {
689+ exists ( expectedValue ) and
690+ actualValue = ""
691+ }
692+
693+ additional predicate hasPathProblemSource (
694+ int row , Input:: Location location , string element , string tag , string value
695+ ) {
696+ getQueryKind ( ) = "path-problem" and
697+ exists ( string loc |
698+ queryResults ( "#select" , row , 2 , loc ) and
699+ queryResults ( "#select" , row , 3 , element ) and
700+ tag = getSourceTag ( row ) and
701+ value = "" and
702+ Input2:: getRelativeUrl ( location ) = loc
703+ )
704+ }
705+
706+ predicate hasActualResult ( Input:: Location location , string element , string tag , string value ) {
707+ hasPathProblemSource ( _, location , element , tag , value )
708+ }
709+ }
710+
711+ private module PathProblemSourceTest = MakeTest< PathProblemSourceTestInput > ;
712+
633713 private module TestInput implements TestSig {
634714 bindingset [ result ]
635715 string getARelevantTag ( ) { any ( ) }
636716
717+ private string getTagRegex ( ) {
718+ exists ( string sourceSinkTags |
719+ getQueryKind ( ) = "problem" and
720+ sourceSinkTags = ""
721+ or
722+ sourceSinkTags = "|" + getSourceTag ( _) + "|" + getSinkTag ( _)
723+ |
724+ result = "(Alert" + sourceSinkTags + ")(\\[(.*)\\])?"
725+ )
726+ }
727+
637728 bindingset [ expectedTag, actualTag]
638729 predicate tagMatches ( string expectedTag , string actualTag ) {
730+ actualTag = expectedTag .regexpCapture ( getTagRegex ( ) , 1 ) and
639731 (
640- expectedTag = "Alert"
732+ getQueryId ( ) = expectedTag . regexpCapture ( getTagRegex ( ) , 3 )
641733 or
642- exists ( string alertTagRegex |
643- alertTagRegex = "Alert\\[(.*)\\]" and
644- getQueryId ( ) = expectedTag .regexpCapture ( alertTagRegex , 1 )
645- )
646- ) and
647- exists ( actualTag )
734+ not exists ( expectedTag .regexpCapture ( getTagRegex ( ) , 3 ) )
735+ )
648736 }
649737
650738 bindingset [ expectedTag]
651739 predicate tagIsOptional ( string expectedTag ) {
652- not expectedTag .regexpMatch ( "Alert(\\[.*\\])?" )
740+ not expectedTag .regexpMatch ( getTagRegex ( ) )
653741 or
654- exists ( string alertTagRegex , string queryId |
655- alertTagRegex = "Alert\\[(.*)\\]" and
656- queryId = expectedTag .regexpCapture ( alertTagRegex , 1 ) and
657- not queryId = getQueryId ( )
742+ exists ( string queryId |
743+ queryId = expectedTag .regexpCapture ( getTagRegex ( ) , 3 ) and
744+ queryId != getQueryId ( )
658745 )
659746 }
660747
661- predicate hasActualResult ( Input:: Location location , string element , string tag , string value ) {
748+ bindingset [ expectedValue, actualValue]
749+ predicate valueMatches ( string expectedValue , string actualValue ) {
750+ expectedValue = actualValue
751+ or
752+ actualValue = ""
753+ }
754+
755+ private predicate hasPathProblemSource = PathProblemSourceTestInput:: hasPathProblemSource / 5 ;
756+
757+ /**
758+ * Gets the expected sink value for result row `row`. This value must
759+ * match the value at the corresponding path-problem source.
760+ */
761+ private string getSinkValue ( int row ) {
762+ exists ( Input:: Location location , string element , string tag , string val |
763+ hasPathProblemSource ( row , location , element , tag , val ) and
764+ result =
765+ PathProblemSourceTest:: getAMatchingExpectation ( location , element , tag , val , false )
766+ .getValue ( )
767+ )
768+ }
769+
770+ private predicate hasPathProblemSink (
771+ int row , Input:: Location location , string element , string tag , string value
772+ ) {
773+ getQueryKind ( ) = "path-problem" and
774+ exists ( string loc |
775+ queryResults ( "#select" , row , 4 , loc ) and
776+ queryResults ( "#select" , row , 5 , element ) and
777+ tag = getSinkTag ( row ) and
778+ Input2:: getRelativeUrl ( location ) = loc
779+ |
780+ not exists ( getSinkValue ( row ) ) and value = ""
781+ or
782+ value = getSinkValue ( row )
783+ )
784+ }
785+
786+ private predicate hasAlert ( Input:: Location location , string element , string tag , string value ) {
787+ getQueryKind ( ) = [ "problem" , "path-problem" ] and
662788 exists ( int row , string loc |
663789 queryResults ( "#select" , row , 0 , loc ) and
664790 queryResults ( "#select" , row , 2 , element ) and
665791 tag = "Alert" and
666792 value = "" and
667- Input2:: getRelativeUrl ( location ) = loc
793+ Input2:: getRelativeUrl ( location ) = loc and
794+ not hasPathProblemSource ( row , location , _, _, _) and
795+ not hasPathProblemSink ( row , location , _, _, _)
668796 )
669797 }
798+
799+ predicate hasActualResult ( Input:: Location location , string element , string tag , string value ) {
800+ hasAlert ( location , element , tag , value )
801+ or
802+ hasPathProblemSource ( _, location , element , tag , value )
803+ or
804+ hasPathProblemSink ( _, location , element , tag , value )
805+ }
670806 }
671807
672808 private module Test = MakeTest< TestInput > ;
0 commit comments