Compat: Make cts validate validate compat-expectations.txt

Bug: dawn:2138
Change-Id: I9025a12d607e2e8d521d7fec1d99e8b64309d0c8
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/156781
Commit-Queue: Gregg Tavares <gman@chromium.org>
Reviewed-by: Austin Eng <enga@chromium.org>
Reviewed-by: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
diff --git a/tools/src/cmd/cts/common/paths.go b/tools/src/cmd/cts/common/paths.go
index a181885..3cbe03d 100644
--- a/tools/src/cmd/cts/common/paths.go
+++ b/tools/src/cmd/cts/common/paths.go
@@ -39,6 +39,10 @@
 	// expectations.txt file.
 	RelativeExpectationsPath = "webgpu-cts/expectations.txt"
 
+	// RelativeCompatExpectationsPath is the dawn-root relative path to the
+	// compat-expectations.txt file.
+	RelativeCompatExpectationsPath = "webgpu-cts/compat-expectations.txt"
+
 	// RelativeSlowExpectationsPath is the dawn-root relative path to the
 	// slow_tests.txt file.
 	RelativeSlowExpectationsPath = "webgpu-cts/slow_tests.txt"
@@ -58,6 +62,25 @@
 	return path
 }
 
+// DefaultCompatExpectationsPath returns the default path to the compat-expectations.txt
+// file. Returns an empty string if the file cannot be found.
+func DefaultCompatExpectationsPath() string {
+	path := filepath.Join(fileutils.DawnRoot(), RelativeCompatExpectationsPath)
+	if _, err := os.Stat(path); err != nil {
+		return ""
+	}
+	return path
+}
+
+// DefaultExpectationsPaths returns the default set of expectations files commands
+// will use if no alternative list of files is supplied.
+func DefaultExpectationsPaths() []string {
+	return []string{
+		DefaultExpectationsPath(),
+		DefaultCompatExpectationsPath(),
+	}
+}
+
 // DefaultSlowExpectationsPath returns the default path to the slow_tests.txt
 // file. Returns an empty string if the file cannot be found.
 func DefaultSlowExpectationsPath() string {
diff --git a/tools/src/cmd/cts/validate/validate.go b/tools/src/cmd/cts/validate/validate.go
index d918f42..626aa11 100644
--- a/tools/src/cmd/cts/validate/validate.go
+++ b/tools/src/cmd/cts/validate/validate.go
@@ -32,6 +32,7 @@
 	"flag"
 	"fmt"
 	"os"
+	"strings"
 
 	"dawn.googlesource.com/dawn/tools/src/cmd/cts/common"
 	"dawn.googlesource.com/dawn/tools/src/cts/expectations"
@@ -41,10 +42,21 @@
 	common.Register(&cmd{})
 }
 
+type arrayFlags []string
+
+func (i *arrayFlags) String() string {
+	return strings.Join((*i), " ")
+}
+
+func (i *arrayFlags) Set(value string) error {
+	*i = append(*i, value)
+	return nil
+}
+
 type cmd struct {
 	flags struct {
-		expectations string // expectations file path
-		slow         string // slow test expectations file path
+		expectations arrayFlags // expectations file path
+		slow         string     // slow test expectations file path
 	}
 }
 
@@ -57,36 +69,41 @@
 }
 
 func (c *cmd) RegisterFlags(ctx context.Context, cfg common.Config) ([]string, error) {
-	defaultExpectations := common.DefaultExpectationsPath()
 	slowExpectations := common.DefaultSlowExpectationsPath()
-	flag.StringVar(&c.flags.expectations, "expectations", defaultExpectations, "path to CTS expectations file to validate")
+	flag.Var(&c.flags.expectations, "expectations", "path to CTS expectations file(s) to validate")
 	flag.StringVar(&c.flags.slow, "slow", slowExpectations, "path to CTS slow expectations file to validate")
 	return nil, nil
 }
 
 func (c *cmd) Run(ctx context.Context, cfg common.Config) error {
-	// Load expectations.txt
-	content, err := expectations.Load(c.flags.expectations)
-	if err != nil {
-		return err
+	if len(c.flags.expectations) == 0 {
+		c.flags.expectations = common.DefaultExpectationsPaths()
 	}
-	diags := content.Validate()
 
-	// Print any diagnostics
-	diags.Print(os.Stdout, c.flags.expectations)
-	if numErrs := diags.NumErrors(); numErrs > 0 {
-		return fmt.Errorf("%v errors found", numErrs)
+	for _, expectationFilename := range c.flags.expectations {
+		// Load expectations.txt
+		content, err := expectations.Load(expectationFilename)
+		if err != nil {
+			return err
+		}
+		diags := content.Validate()
+
+		// Print any diagnostics
+		diags.Print(os.Stdout, expectationFilename)
+		if numErrs := diags.NumErrors(); numErrs > 0 {
+			return fmt.Errorf("%v errors found in %v", numErrs, expectationFilename)
+		}
 	}
 
 	// Load slow_tests.txt
-	content, err = expectations.Load(c.flags.slow)
+	content, err := expectations.Load(c.flags.slow)
 	if err != nil {
 		return err
 	}
 
-	diags = content.ValidateSlowTests()
+	diags := content.ValidateSlowTests()
 	// Print any diagnostics
-	diags.Print(os.Stdout, c.flags.expectations)
+	diags.Print(os.Stdout, c.flags.slow)
 	if numErrs := diags.NumErrors(); numErrs > 0 {
 		return fmt.Errorf("%v errors found", numErrs)
 	}