Add dependency injection in check-spec-examples

Updates //tools/src/cmd/check-spec-examples/main.go to support
dependency injection for OS functions. Tests for some functions will be
added in a follow-up CL, while the remaining test coverage will be
blocked on exec calls also being handled via dependency injection.

Bug: 344014313
Change-Id: I10d39b35a74b62a7d8a95ab41a121b808223a5c4
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/280556
Auto-Submit: Brian Sheedy <bsheedy@google.com>
Commit-Queue: dan sinclair <dsinclair@chromium.org>
Reviewed-by: dan sinclair <dsinclair@chromium.org>
Commit-Queue: Brian Sheedy <bsheedy@google.com>
diff --git a/tools/src/cmd/check-spec-examples/main.go b/tools/src/cmd/check-spec-examples/main.go
index 031bd85..6f84bcf 100644
--- a/tools/src/cmd/check-spec-examples/main.go
+++ b/tools/src/cmd/check-spec-examples/main.go
@@ -50,6 +50,7 @@
 	"path/filepath"
 	"strings"
 
+	"dawn.googlesource.com/dawn/tools/src/oswrapper"
 	"golang.org/x/net/html"
 )
 
@@ -77,7 +78,7 @@
 		flag.PrintDefaults()
 	}
 
-	err := run()
+	err := run(oswrapper.GetRealOSWrapper())
 	if err != nil {
 		if errors.Is(err, errInvalidArg) {
 			fmt.Fprintf(os.Stderr, "Error: %v\n\n", err)
@@ -89,7 +90,9 @@
 	}
 }
 
-func run() error {
+// TODO(crbug.com/416755658): Add unittest coverage once exec is handled via
+// dependency injection.
+func run(osWrapper oswrapper.OSWrapper) error {
 	// Parse flags
 	compilerPath := flag.String("compiler", "tint", "path to compiler executable")
 	verbose := flag.Bool("verbose", false, "print examples that pass")
@@ -142,7 +145,7 @@
 			return fmt.Errorf("Failed to load the WGSL spec from '%v': %w", specURL, err)
 		}
 
-		file, err := os.Open(specURL.Path)
+		file, err := osWrapper.Open(specURL.Path)
 		if err != nil {
 			return fmt.Errorf("Failed to load the WGSL spec from '%v': %w", specURL, err)
 		}
@@ -169,20 +172,20 @@
 	}
 
 	// Create a temporary directory to hold the examples as separate files
-	tmpDir, err := os.MkdirTemp("", "wgsl-spec-examples")
+	tmpDir, err := osWrapper.MkdirTemp("", "wgsl-spec-examples")
 	if err != nil {
 		return err
 	}
-	if err := os.MkdirAll(tmpDir, 0666); err != nil {
+	if err := osWrapper.MkdirAll(tmpDir, 0666); err != nil {
 		return fmt.Errorf("Failed to create temporary directory: %w", err)
 	}
-	defer os.RemoveAll(tmpDir)
+	defer osWrapper.RemoveAll(tmpDir)
 
 	// For each compilable WGSL example...
 	for _, e := range examples {
 		exampleURL := specURL.String() + "#" + e.name
 
-		if err := tryCompile(compiler, tmpDir, e); err != nil {
+		if err := tryCompile(compiler, tmpDir, e, osWrapper); err != nil {
 			if !e.expectError {
 				fmt.Printf("✘ %v ✘\n%v\n", exampleURL, err)
 				continue
@@ -206,13 +209,15 @@
 	expectError   bool   // Annotated with 'expect-error' ?
 }
 
+// TODO(crbug.com/416755658): Add unittest coverage once exec is handled via
+// dependency injection.
 // tryCompile attempts to compile the example e in the directory wd, using the
 // compiler at the given path. If the example is annotated with 'function-scope'
 // then the code is wrapped with a basic vertex-stage-entry function.
 // If the first compile fails then a placeholder vertex-state-entry
 // function is appended to the source, and another attempt to compile
 // the shader is made.
-func tryCompile(compiler, wd string, e example) error {
+func tryCompile(compiler, wd string, e example, osWrapper oswrapper.OSWrapper) error {
 	code := e.code
 	if e.functionScope {
 		code = "\n@vertex fn main() -> @builtin(position) vec4<f32> {\n" + code + " return vec4<f32>();}\n"
@@ -220,7 +225,7 @@
 
 	addedStubFunction := false
 	for {
-		err := compile(compiler, wd, e.name, code)
+		err := compile(compiler, wd, e.name, code, osWrapper)
 		if err == nil {
 			return nil
 		}
@@ -235,11 +240,13 @@
 	}
 }
 
+// TODO(crbug.com/416755658): Add unittest coverage once exec is handled via
+// dependency injection.
 // compile creates a file in wd and uses the compiler to attempt to compile it.
-func compile(compiler, wd, name, code string) error {
+func compile(compiler, wd, name, code string, osWrapper oswrapper.OSWrapper) error {
 	filename := name + ".wgsl"
 	path := filepath.Join(wd, filename)
-	if err := os.WriteFile(path, []byte(code), 0666); err != nil {
+	if err := osWrapper.WriteFile(path, []byte(code), 0666); err != nil {
 		return fmt.Errorf("Failed to write example file '%v'", path)
 	}
 	cmd := exec.Command(compiler, filename)