[tools][cts] Fix unknown test pruning

Changes the way the simplified codepath prunes unknown tests.
Previously, we were actually parsing the expectation queries into Query
objects, which led to edge cases with wildcards such as "*" and
"a:b,c:d,e:f=*" not working properly. Instead, we now directly compare
strings, which is how expectation "queries" are actually used in the
test harness.

Also significantly speeds up the pruning code. Previously, we were
always comparing each expectation to each known test name. For
non-wildcard expectations (i.e. the vast majority of cases), we now
just check for inclusion within a set.

Bug: 372730248
Change-Id: I09c08b5d9fc22e99bac6323b568ccb31601e6dd9
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/213194
Commit-Queue: Brian Sheedy <bsheedy@google.com>
Reviewed-by: dan sinclair <dsinclair@chromium.org>
Auto-Submit: Brian Sheedy <bsheedy@google.com>
Commit-Queue: dan sinclair <dsinclair@chromium.org>
diff --git a/tools/src/cts/expectations/update.go b/tools/src/cts/expectations/update.go
index ae61423..fba9912 100644
--- a/tools/src/cts/expectations/update.go
+++ b/tools/src/cts/expectations/update.go
@@ -107,6 +107,13 @@
 // removeExpectationsForUnknownTests modifies the Content in place so that all
 // contained Expectations apply to tests in the given testlist.
 func (c *Content) removeExpectationsForUnknownTests(testlist *[]query.Query) error {
+	// Converting into a set allows us to much more efficiently check if a
+	// non-wildcard expectation is for a valid test.
+	knownTestNames := container.NewSet[string]()
+	for _, testQuery := range *testlist {
+		knownTestNames.Add(testQuery.ExpectationFileString())
+	}
+
 	prunedChunkSlice := make([]Chunk, 0)
 	for _, chunk := range c.Chunks {
 		prunedChunk := chunk.Clone()
@@ -119,11 +126,19 @@
 
 		prunedChunk.Expectations = make(Expectations, 0)
 		for _, expectation := range chunk.Expectations {
-			for _, testQuery := range *testlist {
-				expectationQuery := query.Parse(expectation.Query)
-				if expectationQuery.Contains(testQuery) {
+			// We don't actually parse the query string into a Query since wildcards
+			// are treated differently between expectations and CTS queries.
+			if strings.HasSuffix(expectation.Query, "*") {
+				testPrefix := expectation.Query[:len(expectation.Query)-1]
+				for testName := range knownTestNames {
+					if strings.HasPrefix(testName, testPrefix) {
+						prunedChunk.Expectations = append(prunedChunk.Expectations, expectation)
+						break
+					}
+				}
+			} else {
+				if knownTestNames.Contains(expectation.Query) {
 					prunedChunk.Expectations = append(prunedChunk.Expectations, expectation)
-					break
 				}
 			}
 		}
diff --git a/tools/src/cts/expectations/update_test.go b/tools/src/cts/expectations/update_test.go
index fcca1ab..a4c1273 100644
--- a/tools/src/cts/expectations/update_test.go
+++ b/tools/src/cts/expectations/update_test.go
@@ -965,16 +965,20 @@
 	startingContent := `
 # BEGIN TAG HEADER
 # OS
-# tags: [ android linux win10 ]
+# tags: [ android linux mac win10 ]
 # END TAG HEADER
 
 # Partially removed, immutable.
+[ android ] * [ Failure ]
+[ mac ] a:* [ Failure ]
+[ linux ] a:b,c:d,e:f=* [ Failure ]
 [ linux ] valid_test1 [ Failure ]
 [ linux ] invalid_test [ Failure ]
 [ linux ] valid_test2 [ Failure ]
 
 # Fully removed, immutable.
 [ android ] invalid_test [ Failure ]
+[ android ] c:* [ Failure ]
 
 # Partially removed, mutable.
 # ##ROLLER_AUTOGENERATED_FAILURES##
@@ -989,16 +993,21 @@
 	knownTests := []query.Query{
 		query.Parse("valid_test1"),
 		query.Parse("valid_test2"),
+		query.Parse("a:b"),
+		query.Parse("a:b,c:d,e:f=g"),
 	}
 
 	content.removeExpectationsForUnknownTests(&knownTests)
 
 	expectedContent := `# BEGIN TAG HEADER
 # OS
-# tags: [ android linux win10 ]
+# tags: [ android linux mac win10 ]
 # END TAG HEADER
 
 # Partially removed, immutable.
+[ android ] * [ Failure ]
+[ mac ] a:* [ Failure ]
+[ linux ] a:b,c:d,e:f=* [ Failure ]
 [ linux ] valid_test1 [ Failure ]
 [ linux ] valid_test2 [ Failure ]
 
@@ -1290,12 +1299,16 @@
 # END TAG HEADER
 
 # Partially removed, immutable.
+[ android ] * [ Failure ]
+[ mac ] a:* [ Failure ]
+[ linux ] z:b,c:d,e:f=* [ Failure ]
 [ linux ] valid_test1 [ Failure ]
 [ linux ] invalid_test [ Failure ]
 [ linux ] valid_test2 [ Failure ]
 
 # Fully removed, immutable.
 [ android ] invalid_test [ Failure ]
+[ android ] c:* [ Failure ]
 
 # Partially removed, mutable.
 # ##ROLLER_AUTOGENERATED_FAILURES##
@@ -1367,6 +1380,9 @@
 # END TAG HEADER
 
 # Partially removed, immutable.
+[ android ] * [ Failure ]
+[ mac ] a:* [ Failure ]
+[ linux ] z:b,c:d,e:f=* [ Failure ]
 [ linux ] valid_test1 [ Failure ]
 [ linux ] valid_test2 [ Failure ]
 
@@ -1409,12 +1425,16 @@
 # END TAG HEADER
 
 # Partially removed, immutable.
+[ android ] * [ Failure ]
+[ mac ] a:* [ Failure ]
+[ linux ] z:b,c:d,e:f=* [ Failure ]
 [ linux ] valid_test1 [ Failure ]
 [ linux ] invalid_test [ Failure ]
 [ linux ] valid_test2 [ Failure ]
 
 # Fully removed, immutable.
 [ android ] invalid_test [ Failure ]
+[ android ] c:* [ Failure ]
 `
 
 	content, err := Parse("expectations.txt", fileContent)
@@ -1480,6 +1500,9 @@
 # END TAG HEADER
 
 # Partially removed, immutable.
+[ android ] * [ Failure ]
+[ mac ] a:* [ Failure ]
+[ linux ] z:b,c:d,e:f=* [ Failure ]
 [ linux ] valid_test1 [ Failure ]
 [ linux ] valid_test2 [ Failure ]