[tools] Fix CTS runner

Fixes required after https://dawn-review.googlesource.com/c/dawn/+/143540

Change-Id: I5da1a4963fda504baedf05384077d5a25effada1
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/147962
Commit-Queue: Ben Clayton <bclayton@google.com>
Reviewed-by: James Price <jrprice@google.com>
Auto-Submit: Ben Clayton <bclayton@google.com>
Kokoro: Ben Clayton <bclayton@google.com>
diff --git a/src/dawn/node/README.md b/src/dawn/node/README.md
index 9d7f4ab..7daff45 100644
--- a/src/dawn/node/README.md
+++ b/src/dawn/node/README.md
@@ -59,13 +59,16 @@
 Now you can run CTS:
 
 ```sh
-./tools/run run-cts --dawn-node=<path-to-cts.js> [WebGPU CTS query]
+./tools/run run-cts --bin=<path-build-dir> [WebGPU CTS query]
 ```
 
+Where `<path-build-dir>` is the output directory. \
+Note: `<path-build-dir>` can be omitted if your build directory sits at `<dawn>/out/active`, which is enforced if you use `<dawn>/tools/setup-build` (recommended).
+
 Or if you checked out your own CTS repo:
 
 ```sh
-./tools/run run-cts --dawn-node=<path-to-cts.js> --cts=<path-to-cts> [WebGPU CTS query]
+./tools/run run-cts --bin=<path-build-dir> --cts=<path-to-cts> [WebGPU CTS query]
 ```
 
 If this fails with the error message `TypeError: expander is not a function or its return value is not iterable`, try appending `--build=false` to the start of the `run-cts` command line flags.
@@ -73,7 +76,7 @@
 To test against SwiftShader instead of the default Vulkan device, prefix `./tools/run run-cts` with `VK_ICD_FILENAMES=<swiftshader-cmake-build>/Linux/vk_swiftshader_icd.json`. For example:
 
 ```sh
-VK_ICD_FILENAMES=<swiftshader-cmake-build>/Linux/vk_swiftshader_icd.json ./tools/run run-cts --dawn-node=<path-to-cts.js> [WebGPU CTS query]
+VK_ICD_FILENAMES=<swiftshader-cmake-build>/Linux/vk_swiftshader_icd.json ./tools/run run-cts --bin=<path-build-dir> [WebGPU CTS query]
 ```
 
 The `--flag` parameter must be passed in multiple times, once for each flag begin set. Here are some common arguments:
@@ -87,7 +90,7 @@
 For example, on Windows, to use the d3dcompiler_47.dll from a Chromium checkout, and to dump shader output, we could run the following using Git Bash:
 
 ```sh
-./tools/run run-cts --verbose --dawn-node=/c/src/dawn/node/cts.js --cts=/c/src/webgpu-cts --flag=dlldir="C:\src\chromium\src\out\Release" --flag=enable-dawn-features=dump_shaders 'webgpu:shader,execution,builtin,abs:integer_builtin_functions,abs_unsigned:storageClass="storage";storageMode="read_write";containerType="vector";isAtomic=false;baseType="u32";type="vec2%3Cu32%3E"'
+./tools/run run-cts --verbose --bin=/c/src/dawn/out/active --cts=/c/src/webgpu-cts --flag=dlldir="C:\src\chromium\src\out\Release" --flag=enable-dawn-features=dump_shaders 'webgpu:shader,execution,builtin,abs:integer_builtin_functions,abs_unsigned:storageClass="storage";storageMode="read_write";containerType="vector";isAtomic=false;baseType="u32";type="vec2%3Cu32%3E"'
 ```
 
 Note that we pass `--verbose` above so that all test output, including the dumped shader, is written to stdout.
@@ -169,7 +172,7 @@
 Replacing:
 
 - `[cts-root]` with the path to the CTS root directory. If you are editing the `.vscode/launch.json` from within the CTS workspace, then you may use `${workspaceFolder}`.
-- `[cts.js]` this is the path to the `cts.js` module copied by the [build step](#Build)
+- `[cts.js]` this is the path to the `cts.js` file that should be copied to the output directory by the [build step](#build)
 - `test-query` with the test query string. Example: `webgpu:shader,execution,builtin,abs:*`
 
 ## Debugging dawn-node issues in gdb/lldb
diff --git a/tools/src/cmd/run-cts/main.go b/tools/src/cmd/run-cts/main.go
index f31f846..85328d8 100644
--- a/tools/src/cmd/run-cts/main.go
+++ b/tools/src/cmd/run-cts/main.go
@@ -156,11 +156,11 @@
 
 	unrollConstEvalLoopsDefault := runtime.GOOS != "windows"
 
-	var dawnNode, cts, node, npx, resultsPath, expectationsPath, logFilename, backend, adapterName, coverageFile string
+	var bin, cts, node, npx, resultsPath, expectationsPath, logFilename, backend, adapterName, coverageFile string
 	var verbose, isolated, build, validate, dumpShaders, unrollConstEvalLoops, genCoverage bool
 	var numRunners int
 	var flags dawnNodeFlags
-	flag.StringVar(&dawnNode, "dawn-node", "", "path to dawn.node module")
+	flag.StringVar(&bin, "bin", defaultBinPath(), "path to the directory holding cts.js and dawn.node")
 	flag.StringVar(&cts, "cts", defaultCtsPath(), "root directory of WebGPU CTS")
 	flag.StringVar(&node, "node", defaultNodePath(), "path to node executable")
 	flag.StringVar(&npx, "npx", "", "path to npx executable")
@@ -193,18 +193,22 @@
 	defer stdout.Close() // Required to flush the mux chan
 
 	// Check mandatory arguments
-	if dawnNode == "" || cts == "" {
+	if bin == "" || cts == "" {
 		showUsage()
 	}
-	if !isFile(dawnNode) {
-		return fmt.Errorf("'%v' is not a file", dawnNode)
+	for _, dir := range []string{cts, bin} {
+		if !isDir(dir) {
+			return fmt.Errorf("'%v' is not a directory", dir)
+		}
 	}
-	if !isDir(cts) {
-		return fmt.Errorf("'%v' is not a directory", cts)
+	for _, file := range []string{"cts.js", "dawn.node"} {
+		if !isFile(filepath.Join(bin, file)) {
+			return fmt.Errorf("'%v' does not contain '%v'", bin, file)
+		}
 	}
 
 	// Make paths absolute
-	for _, path := range []*string{&dawnNode, &cts} {
+	for _, path := range []*string{&bin, &cts} {
 		abs, err := filepath.Abs(*path)
 		if err != nil {
 			return fmt.Errorf("unable to get absolute path for '%v'", *path)
@@ -261,7 +265,7 @@
 		verbose:              verbose,
 		node:                 node,
 		npx:                  npx,
-		dawnNode:             dawnNode,
+		bin:                  bin,
 		cts:                  cts,
 		tmpDir:               filepath.Join(os.TempDir(), "dawn-cts"),
 		unrollConstEvalLoops: unrollConstEvalLoops,
@@ -280,8 +284,6 @@
 	}
 
 	if genCoverage {
-		dawnOutDir := filepath.Dir(dawnNode)
-
 		profdata, err := exec.LookPath("llvm-profdata")
 		if err != nil {
 			profdata = ""
@@ -297,7 +299,7 @@
 		}
 
 		llvmCov := ""
-		turboCov := filepath.Join(dawnOutDir, "turbo-cov"+fileutils.ExeExt)
+		turboCov := filepath.Join(bin, "turbo-cov"+fileutils.ExeExt)
 		if !fileutils.IsExe(turboCov) {
 			turboCov = ""
 			if path, err := exec.LookPath("llvm-cov"); err == nil {
@@ -308,7 +310,7 @@
 		}
 		r.covEnv = &cov.Env{
 			Profdata: profdata,
-			Binary:   dawnNode,
+			Binary:   bin,
 			Cov:      llvmCov,
 			TurboCov: turboCov,
 		}
@@ -324,7 +326,7 @@
 	}
 
 	cache := cache{}
-	cachePath := dawnNode + ".runcts.cache"
+	cachePath := filepath.Join(bin, "runcts.cache")
 	if err := cache.load(cachePath); err != nil && verbose {
 		fmt.Fprintln(stdout, "failed to load cache from", cachePath, err)
 	}
@@ -462,7 +464,7 @@
 	verbose              bool
 	node                 string
 	npx                  string
-	dawnNode             string
+	bin                  string
 	cts                  string
 	tmpDir               string
 	unrollConstEvalLoops bool
@@ -692,7 +694,7 @@
 			// start at 1, so just inject a placeholder argument.
 			"placeholder-arg",
 			// Actual arguments begin here
-			"--gpu-provider", r.dawnNode,
+			"--gpu-provider", filepath.Join(r.bin, "cts.js"),
 			"--data", filepath.Join(r.cts, "out-node", "data"),
 		}
 		if r.colors {
@@ -1140,7 +1142,7 @@
 		// start at 1, so just inject a placeholder argument.
 		"placeholder-arg",
 		// Actual arguments begin here
-		"--gpu-provider", r.dawnNode,
+		"--gpu-provider", filepath.Join(r.bin, "cts.js"),
 		"--verbose", // always required to emit test pass results
 		"--quiet",
 	}
@@ -1388,6 +1390,18 @@
 	return ""
 }
 
+// defaultBinPath looks for the binary output directory at <dawn>/out/active.
+// This is used as the default for the --bin command line flag.
+func defaultBinPath() string {
+	if dawnRoot := fileutils.DawnRoot(); dawnRoot != "" {
+		bin := filepath.Join(dawnRoot, "out/active")
+		if info, err := os.Stat(bin); err == nil && info.IsDir() {
+			return bin
+		}
+	}
+	return ""
+}
+
 // defaultCtsPath looks for the webgpu-cts directory in dawn's third_party
 // directory. This is used as the default for the --cts command line flag.
 func defaultCtsPath() string {