Improve update/expectations/expectations.go tests

Adds full test coverage of
tools/src/cmd/cts/update/expectations/expectations.go.

Bug: 344014313
Change-Id: Icdb660e98836da29436d6614b8f5cea23d86ce8d
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/279717
Commit-Queue: Brian Sheedy <bsheedy@google.com>
Reviewed-by: Ryan Harrison <rharrison@chromium.org>
diff --git a/tools/src/cmd/cts/update/expectations/expectations.go b/tools/src/cmd/cts/update/expectations/expectations.go
index afbffa3..7603432 100644
--- a/tools/src/cmd/cts/update/expectations/expectations.go
+++ b/tools/src/cmd/cts/update/expectations/expectations.go
@@ -100,8 +100,6 @@
 	return out, nil
 }
 
-// TODO(crbug.com/344014313): Add unittests once expectations.Load() and
-// expectations.Save() use dependency injection.
 func (c *cmd) Run(ctx context.Context, cfg common.Config) error {
 	if len(c.flags.expectations) == 0 {
 		c.flags.expectations = common.DefaultExpectationsPaths(cfg.OsWrapper)
diff --git a/tools/src/cmd/cts/update/expectations/expectations_test.go b/tools/src/cmd/cts/update/expectations/expectations_test.go
index a1027e6..6e522d2 100644
--- a/tools/src/cmd/cts/update/expectations/expectations_test.go
+++ b/tools/src/cmd/cts/update/expectations/expectations_test.go
@@ -39,31 +39,28 @@
 )
 
 func TestUpdateExpectations(t *testing.T) {
-	// Setup mock filesystem
-	wrapper := oswrapper.CreateFSTestOSWrapper()
-
-	// Determine the real Dawn root to verify fileutils.DawnRoot works with mock FS
-	realWrapper := oswrapper.GetRealOSWrapper()
-	rootDir := fileutils.DawnRoot(realWrapper)
-	require.NotEmpty(t, rootDir, "Could not determine Dawn root")
-
-	// Populate mock filesystem to confirm DawnRoot detection
-	require.NoError(t, wrapper.MkdirAll(rootDir, 0755))
-	require.NoError(t, wrapper.WriteFile(filepath.Join(rootDir, "DEPS"), []byte(""), 0666))
-
-	// Create test list file at expected location
-	testListPath := filepath.Join(rootDir, "third_party", "gn", "webgpu-cts", "test_list.txt")
-	require.NoError(t, wrapper.MkdirAll(filepath.Dir(testListPath), 0755))
-	require.NoError(t, wrapper.WriteFile(testListPath, []byte("webgpu:test:one\nwebgpu:test:two"), 0666))
-
-	// Create results file
-	resultsPath := filepath.Join(rootDir, "results.txt")
-	resultsContent := "webgpu:test:one linux,nvidia Failure 0s false\ncore"
-	require.NoError(t, wrapper.WriteFile(resultsPath, []byte(resultsContent), 0666))
-
-	// Create expectations file
-	expectationsPath := filepath.Join(rootDir, "expectations.txt")
-	expectationsContent := `# BEGIN TAG HEADER
+	tests := []struct {
+		name                 string
+		resultsFile          string
+		resultsContent       string
+		testListPath         string
+		testListContent      string
+		expectationsPath     string
+		expectationsContent  string
+		expectationsFlag     []string
+		wantErr              string
+		wantLog              string
+		verifyContent        func(t *testing.T, fs oswrapper.FilesystemReader, path string)
+		generateExplicitTags bool
+	}{
+		{
+			name:             "Success",
+			resultsFile:      "results.txt",
+			resultsContent:   "webgpu:test:one linux,nvidia Failure 0s false\ncore",
+			testListPath:     "third_party/gn/webgpu-cts/test_list.txt",
+			testListContent:  "webgpu:test:one\nwebgpu:test:two",
+			expectationsPath: "expectations.txt",
+			expectationsContent: `# BEGIN TAG HEADER
 # OS
 # tags: [ mac win linux android ]
 # GPU
@@ -71,29 +68,164 @@
 # Device
 # tags: [ android-pixel-4 android-pixel-6 chromeos-board-amd64-generic fuchsia-board-qemu-x64 ]
 # END TAG HEADER
+
 # existing
 [ mac ] webgpu:test:two: [ Failure ]
-`
-	require.NoError(t, wrapper.WriteFile(expectationsPath, []byte(expectationsContent), 0666))
+`,
+			expectationsFlag: []string{"expectations.txt"},
+			verifyContent: func(t *testing.T, fs oswrapper.FilesystemReader, path string) {
+				content, err := fs.ReadFile(path)
+				require.NoError(t, err)
+				text := string(content)
+				require.Contains(t, text, "[ linux nvidia ] webgpu:test:one: [ Failure ]")
+				require.Contains(t, text, "[ mac ] webgpu:test:two: [ Failure ]")
+			},
+		},
+		{
+			name:             "Results Fetch Error",
+			resultsFile:      "nonexistent.txt",
+			testListPath:     "third_party/gn/webgpu-cts/test_list.txt",
+			testListContent:  "webgpu:test:one",
+			expectationsPath: "expectations.txt",
+			expectationsFlag: []string{"expectations.txt"},
+			wantErr:          "does not exist",
+		},
+		{
+			name:             "Test List Load Error",
+			resultsFile:      "results.txt",
+			resultsContent:   "webgpu:test:one linux Failure 0s false\ncore",
+			testListPath:     "nonexistent/test_list.txt",
+			expectationsPath: "expectations.txt",
+			expectationsFlag: []string{"expectations.txt"},
+			wantErr:          "failed to load test list",
+		},
+		{
+			name:             "Expectations Load Error",
+			resultsFile:      "results.txt",
+			resultsContent:   "webgpu:test:one linux Failure 0s false\ncore",
+			testListPath:     "third_party/gn/webgpu-cts/test_list.txt",
+			testListContent:  "webgpu:test:one\nwebgpu:test:two",
+			expectationsPath: "expectations.txt",
+			expectationsFlag: []string{"nonexistent.txt"},
+			wantErr:          "does not exist",
+		},
+		{
+			name:                "Expectations Validation Error",
+			resultsFile:         "results.txt",
+			resultsContent:      "webgpu:test:one linux Failure 0s false\ncore",
+			testListPath:        "third_party/gn/webgpu-cts/test_list.txt",
+			testListContent:     "webgpu:test:one",
+			expectationsPath:    "expectations.txt",
+			expectationsContent: "INVALID SYNTAX",
+			expectationsFlag:    []string{"expectations.txt"},
+			wantErr:             "expected status",
+		},
+		{
+			name:             "Compat Mode",
+			resultsFile:      "results.txt",
+			resultsContent:   "webgpu:test:compat linux,nvidia Failure 0s false\ncompat",
+			testListPath:     "third_party/gn/webgpu-cts/test_list.txt",
+			testListContent:  "webgpu:test:compat",
+			expectationsPath: "compat_expectations.txt",
+			expectationsContent: `# BEGIN TAG HEADER
+# OS
+# tags: [ mac win linux android ]
+# GPU
+# tags: [ amd intel nvidia qualcomm ]
+# Device
+# tags: [ android-pixel-4 android-pixel-6 chromeos-board-amd64-generic fuchsia-board-qemu-x64 ]
+# END TAG HEADER
+`,
+			expectationsFlag: []string{"compat_expectations.txt"},
+			verifyContent: func(t *testing.T, fs oswrapper.FilesystemReader, path string) {
+				content, err := fs.ReadFile(path)
+				require.NoError(t, err)
+				require.Contains(t, string(content), "[ linux nvidia ] webgpu:test:compat: [ Failure ]")
+			},
+		},
+		{
+			name:             "Unknown Tests Removal",
+			resultsFile:      "results.txt",
+			resultsContent:   "webgpu:test:known linux,nvidia Failure 0s false\ncore",
+			testListPath:     "third_party/gn/webgpu-cts/test_list.txt",
+			testListContent:  "webgpu:test:known",
+			expectationsPath: "expectations.txt",
+			expectationsContent: `# BEGIN TAG HEADER
+# OS
+# tags: [ mac win linux android ]
+# GPU
+# tags: [ amd intel nvidia qualcomm ]
+# Device
+# tags: [ android-pixel-4 android-pixel-6 chromeos-board-amd64-generic fuchsia-board-qemu-x64 ]
+# END TAG HEADER
 
-	// Initialize command
-	c := &cmd{}
-	c.flags.expectations = []string{expectationsPath}
-	c.flags.results.File = resultsPath
-	c.flags.verbose = true
+webgpu:test:unknown: [ Failure ]`,
+			expectationsFlag: []string{"expectations.txt"},
+			verifyContent: func(t *testing.T, fs oswrapper.FilesystemReader, path string) {
+				content, err := fs.ReadFile(path)
+				require.NoError(t, err)
+				require.NotContains(t, string(content), "webgpu:test:unknown")
+			},
+		},
+	}
 
-	// Run command
-	ctx := context.Background()
-	cfg := common.Config{OsWrapper: wrapper}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			wrapper := oswrapper.CreateFSTestOSWrapper()
+			realWrapper := oswrapper.GetRealOSWrapper()
+			rootDir := fileutils.DawnRoot(realWrapper)
 
-	err := c.Run(ctx, cfg)
-	require.NoError(t, err)
+			// Helper to write file relative to root
+			write := func(path, content string) {
+				fullPath := filepath.Join(rootDir, path)
+				require.NoError(t, wrapper.MkdirAll(filepath.Dir(fullPath), 0755))
+				require.NoError(t, wrapper.WriteFile(fullPath, []byte(content), 0666))
+			}
 
-	// Verify expectations were updated
-	content, err := wrapper.ReadFile(expectationsPath)
-	require.NoError(t, err)
+			if tt.resultsContent != "" {
+				write(tt.resultsFile, tt.resultsContent)
+			}
+			if tt.testListContent != "" {
+				write(tt.testListPath, tt.testListContent)
+			}
+			if tt.expectationsContent != "" {
+				write(tt.expectationsPath, tt.expectationsContent)
+			}
 
-	text := string(content)
-	require.Contains(t, text, "crbug.com/0000 [ linux nvidia ] webgpu:test:one: [ Failure ]")
-	require.Contains(t, text, "[ mac ] webgpu:test:two: [ Failure ]")
+			c := &cmd{}
+
+			// Resolve paths to absolute for flags
+			absExpectations := make([]string, len(tt.expectationsFlag))
+			for i, p := range tt.expectationsFlag {
+				absExpectations[i] = filepath.Join(rootDir, p)
+			}
+			c.flags.expectations = absExpectations
+
+			if tt.resultsFile != "" {
+				c.flags.results.File = filepath.Join(rootDir, tt.resultsFile)
+			}
+
+			c.flags.verbose = true
+			c.flags.generateExplicitTags = tt.generateExplicitTags
+
+			ctx := context.Background()
+			cfg := common.Config{OsWrapper: wrapper}
+
+			write("DEPS", "") // Ensure DawnRoot detection works
+
+			err := c.Run(ctx, cfg)
+			if tt.wantErr != "" {
+				require.Error(t, err)
+				require.ErrorContains(t, err, tt.wantErr)
+			} else {
+				require.NoError(t, err)
+			}
+
+			if tt.verifyContent != nil {
+				// Check content of the first expectations file
+				path := filepath.Join(rootDir, tt.expectationsPath)
+				tt.verifyContent(t, wrapper, path)
+			}
+		})
+	}
 }