Regres: Scan commit message for additional test lists.

Change-Id: Ib0beec96a6efafa0ee6fb8a43e15142206d3cd4d
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/26558
Tested-by: Ben Clayton <bclayton@google.com>
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
diff --git a/tests/regres/main.go b/tests/regres/main.go
index 396cada..cd2b760 100644
--- a/tests/regres/main.go
+++ b/tests/regres/main.go
@@ -290,6 +290,8 @@
 	return msg, nil
 }
 
+var additionalTestsRE = regexp.MustCompile(`\n\s*Test[s]?:\s*([^\s]+)[^\n]*`)
+
 func (r *regres) testLatest(change *changeInfo) (*CommitTestResults, testlist.Lists, error) {
 	// Get the test results for the latest patchset in the change.
 	test := r.newTest(change.latest)
@@ -304,6 +306,29 @@
 		return nil, nil, cause.Wrap(err, "Failed to load '%s'", change.latest)
 	}
 
+	if matches := additionalTestsRE.FindAllStringSubmatch(change.commitMessage, -1); len(matches) > 0 {
+		log.Println("Change description contains additional test patterns")
+
+		// Change specifies additional tests to try. Load the full test list.
+		fullTestLists, err := test.loadTestLists(fullTestListRelPath)
+		if err != nil {
+			return nil, nil, cause.Wrap(err, "Failed to load '%s'", change.latest)
+		}
+
+		// Add any tests in the full list that match the pattern to the list to test.
+		for _, match := range matches {
+			if len(match) > 1 {
+				pattern := match[1]
+				log.Printf("Adding custom tests with pattern '%s'\n", pattern)
+				filtered := fullTestLists.Filter(func(name string) bool {
+					ok, _ := filepath.Match(pattern, name)
+					return ok
+				})
+				testlists = append(testlists, filtered...)
+			}
+		}
+	}
+
 	cachePath := test.resultsCachePath(testlists)
 
 	if results, err := loadCommitTestResults(cachePath); err == nil {
diff --git a/tests/regres/testlist/testlist.go b/tests/regres/testlist/testlist.go
index 5585200..d2da533 100644
--- a/tests/regres/testlist/testlist.go
+++ b/tests/regres/testlist/testlist.go
@@ -48,9 +48,36 @@
 	Tests []string
 }
 
+// Filter returns a new Group that contains only tests that match the predicate.
+func (g Group) Filter(pred func(string) bool) Group {
+	out := Group{
+		Name: g.Name,
+		File: g.File,
+		API:  g.API,
+	}
+	for _, test := range g.Tests {
+		if pred(test) {
+			out.Tests = append(out.Tests, test)
+		}
+	}
+	return out
+}
+
 // Lists is the full list of tests to be run.
 type Lists []Group
 
+// Filter returns a new Lists that contains only tests that match the predicate.
+func (l Lists) Filter(pred func(string) bool) Lists {
+	out := Lists{}
+	for _, group := range l {
+		filtered := group.Filter(pred)
+		if len(filtered.Tests) > 0 {
+			out = append(out, filtered)
+		}
+	}
+	return out
+}
+
 // Hash returns a SHA1 hash of the set of tests.
 func (l Lists) Hash() string {
 	h := sha1.New()