Remove os usage from get-test-plan

Removes usage of the os module for filesystem-related functions from
//tools/src/cmd/get-test-plan/main.go in favor of dependency injection.

Test coverage will be added in a follow-up CL.

Bug: 344014313
Change-Id: I8bcc739dd445acbe5fbaa5e3f4ad049476e184ea
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/282855
Commit-Queue: dan sinclair <dsinclair@chromium.org>
Auto-Submit: Brian Sheedy <bsheedy@google.com>
Reviewed-by: dan sinclair <dsinclair@chromium.org>
diff --git a/tools/src/cmd/get-test-plan/main.go b/tools/src/cmd/get-test-plan/main.go
index 864277d..c4ad4ed 100644
--- a/tools/src/cmd/get-test-plan/main.go
+++ b/tools/src/cmd/get-test-plan/main.go
@@ -54,6 +54,7 @@
 	"strconv"
 	"strings"
 
+	"dawn.googlesource.com/dawn/tools/src/oswrapper"
 	"golang.org/x/net/html"
 )
 
@@ -108,7 +109,8 @@
 		flag.PrintDefaults()
 	}
 
-	err := run()
+	osWrapper := oswrapper.GetRealOSWrapper()
+	err := run(osWrapper)
 	if err != nil {
 		if errors.Is(err, errInvalidArg) {
 			fmt.Fprintf(os.Stderr, "Error: %v\n\n", err)
@@ -120,7 +122,9 @@
 	}
 }
 
-func run() error {
+// TODO(crbug.com/473064729): Add unittests once HTTP requests are handled via
+// dependency injection.
+func run(osWrapper oswrapper.OSWrapper) error {
 	// Parse flags
 	keyword := flag.String("keyword", "",
 		`if provided, it will be used as the keyword to search WGSL spec for rules
@@ -147,7 +151,7 @@
 	args := flag.Args()
 
 	// Parse spec
-	spec, err := parseSpec(args)
+	spec, err := parseSpec(args, osWrapper)
 	if err != nil {
 		return err
 	}
@@ -164,7 +168,7 @@
 	rules := parser.rules
 
 	if *ctsDir != "" {
-		err := getUnimplementedTestPlan(*parser, *ctsDir)
+		err := getUnimplementedTestPlan(*parser, *ctsDir, osWrapper)
 		if err != nil {
 			return err
 		}
@@ -180,11 +184,11 @@
 		if err != nil {
 			return err
 		}
-		return writeFile(*output, string(j))
+		return writeFile(*output, string(j), osWrapper)
 	} else if strings.HasSuffix(*output, ".txt") {
-		return writeFile(*output, txt)
+		return writeFile(*output, txt, osWrapper)
 	} else if strings.HasSuffix(*output, ".tsv") {
-		return writeFile(*output, tsv)
+		return writeFile(*output, tsv, osWrapper)
 	} else {
 		return fmt.Errorf("unsupported output file extension: %v", *output)
 	}
@@ -298,18 +302,20 @@
 
 // writeFile writes content to path
 // the existing content will be overwritten
-func writeFile(path, content string) error {
-	if err := os.MkdirAll(filepath.Dir(path), 0777); err != nil {
+func writeFile(path, content string, fsWriter oswrapper.FilesystemWriter) error {
+	if err := fsWriter.MkdirAll(filepath.Dir(path), 0777); err != nil {
 		return fmt.Errorf("failed to create directory for '%v': %w", path, err)
 	}
-	if err := os.WriteFile(path, []byte(content), 0666); err != nil {
+	if err := fsWriter.WriteFile(path, []byte(content), 0666); err != nil {
 		return fmt.Errorf("failed to write file '%v': %w", path, err)
 	}
 	return nil
 }
 
+// TODO(crbug.com/473064729): Add unittest coverage once HTTP requests are
+// handled via dependency injection.
 // parseSpec reads the spec from a local file, or the URL to WGSL spec
-func parseSpec(args []string) (*html.Node, error) {
+func parseSpec(args []string, fsReader oswrapper.FilesystemReader) (*html.Node, error) {
 	// Check for explicit WGSL spec path
 	specURL, _ := url.Parse(specPath)
 	switch len(args) {
@@ -347,7 +353,7 @@
 			return nil, fmt.Errorf("failed to load the WGSL spec from '%v': %w", specURL, err)
 		}
 
-		file, err := os.Open(path)
+		file, err := fsReader.Open(path)
 		if err != nil {
 			return nil, fmt.Errorf("failed to load the WGSL spec from '%v': %w", specURL, err)
 		}
@@ -371,7 +377,7 @@
 		if err != nil {
 			return nil, fmt.Errorf("failed to load the WGSL spec from '%v': %w", specURL, err)
 		}
-		file, err := os.Open(path)
+		file, err := fsReader.Open(path)
 		if err != nil {
 			return nil, fmt.Errorf("failed to load the WGSL spec from '%v': %w", specURL, err)
 		}
@@ -838,17 +844,17 @@
 
 // getUnimplementedPlan generate the typescript code of a test plan for rules in sections[start, end]
 // then it writes the generated test plans in the given 'path'
-func getUnimplementedTestPlan(p Parser, path string) error {
+func getUnimplementedTestPlan(p Parser, path string, fsWriter oswrapper.FilesystemWriter) error {
 	rules := p.rules
 	start := p.firstSectionContainingRule
 	end := p.lastSectionContainingRule
 	validationPath := filepath.Join(path, "validation")
-	if err := validationTestPlan(rules, validationPath, start, end); err != nil {
+	if err := validationTestPlan(rules, validationPath, start, end, fsWriter); err != nil {
 		return err
 	}
 
 	executionPath := filepath.Join(path, "execution", "builtin")
-	if err := executionTestPlan(rules, executionPath); err != nil {
+	if err := executionTestPlan(rules, executionPath, fsWriter); err != nil {
 		return err
 	}
 	return nil
@@ -876,7 +882,7 @@
 }
 
 // validationTestPlan generates the typescript code of a test plan for rules in sections[start, end]
-func validationTestPlan(rules []rule, path string, start int, end int) error {
+func validationTestPlan(rules []rule, path string, start int, end int, fsWriter oswrapper.FilesystemWriter) error {
 	content := [][]string{}
 	filePath := []string{}
 	for section := 0; section <= end; section++ {
@@ -906,7 +912,7 @@
 
 	for i := start; i <= end; i++ {
 		if len(content[i]) > 1 {
-			if err := writeFile(filePath[i], strings.Join(content[i], "\n")); err != nil {
+			if err := writeFile(filePath[i], strings.Join(content[i], "\n"), fsWriter); err != nil {
 				return err
 			}
 		}
@@ -917,7 +923,7 @@
 
 // executionTestPlan generates the typescript code of a test plan for rules in the given section
 // the rules in section X.Y.* will be written to path/sectionX_Y.spec.ts
-func executionTestPlan(rules []rule, path string) error {
+func executionTestPlan(rules []rule, path string, fsWriter oswrapper.FilesystemWriter) error {
 	// TODO(SarahM) This generates execution tests for builtin function tests. Add other executions tests.
 	section, err := getBuiltinSectionNum(rules)
 	if err != nil {
@@ -974,7 +980,7 @@
 		// Write the file if there is a test in there
 		// compared with >1 because content has at least the test description
 		if len(content[i]) > 1 {
-			if err := writeFile(filePath[i], strings.Join(content[i], "\n")); err != nil {
+			if err := writeFile(filePath[i], strings.Join(content[i], "\n"), fsWriter); err != nil {
 				return err
 			}
 		}