Enable gn_v2 Win/x64/Release/SwS tests

Enables tests on dawn-win-x64-sws-rel that are equivalent to the tests
on win-clang-rel-x64.

In order to allow this, several changes are made to the scripts
used for running the Dawn Node CTS in order to make them compatible
with Windows.

Bug: 385317083
Change-Id: Iae42e5dbad0a180590df6ab85f0b44432f35f220
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/262274
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
Commit-Queue: Brian Sheedy <bsheedy@google.com>
Reviewed-by: Yuly Novikov <ynovikov@chromium.org>
diff --git a/infra/config/global/generated/luci/cr-buildbucket.cfg b/infra/config/global/generated/luci/cr-buildbucket.cfg
index 31cb3d6..2a70c01 100644
--- a/infra/config/global/generated/luci/cr-buildbucket.cfg
+++ b/infra/config/global/generated/luci/cr-buildbucket.cfg
@@ -2689,7 +2689,6 @@
       dimensions: "cores:8"
       dimensions: "os:Windows-10"
       dimensions: "pool:luci.chromium.gpu.ci"
-      dimensions: "ssd:0"
       exe {
         cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build"
         cipd_version: "refs/heads/main"
diff --git a/infra/config/global/gn_standalone_ci.star b/infra/config/global/gn_standalone_ci.star
index 102a0c9..b04d3a8 100644
--- a/infra/config/global/gn_standalone_ci.star
+++ b/infra/config/global/gn_standalone_ci.star
@@ -228,6 +228,7 @@
     ),
     cores = 8,
     os = os.WINDOWS_DEFAULT,
+    ssd = None,
     console_view_entry = consoles.console_view_entry(
         category = "win|build|clang|rel",
         short_name = "x64",
diff --git a/infra/specs/ci.json b/infra/specs/ci.json
index d54e906..ada4114 100644
--- a/infra/specs/ci.json
+++ b/infra/specs/ci.json
@@ -609,5 +609,162 @@
       }
     ]
   },
-  "dawn-win-x64-sws-rel": {}
+  "dawn-win-x64-sws-rel": {
+    "gtest_tests": [
+      {
+        "args": [
+          "--backend=opengles",
+          "--use-angle=swiftshader",
+          "--enable-toggles=gl_force_es_31_and_no_extensions"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/noop_merge.py"
+        },
+        "name": "dawn_end2end_swangle_tests",
+        "resultdb": {
+          "result_format": "gtest_json"
+        },
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "cpu": "x86-64",
+            "gpu": "none",
+            "os": "Windows-10-19045",
+            "pool": "chromium.tests.gpu"
+          },
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "dawn_end2end_tests",
+        "test_id_prefix": "ninja://src/dawn/tests:dawn_end2end_tests/"
+      },
+      {
+        "args": [
+          "--adapter-vendor-id=0x1AE0"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/noop_merge.py"
+        },
+        "name": "dawn_end2end_sws_tests",
+        "resultdb": {
+          "result_format": "gtest_json"
+        },
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "cpu": "x86-64",
+            "gpu": "none",
+            "os": "Windows-10-19045",
+            "pool": "chromium.tests.gpu"
+          },
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "dawn_end2end_tests",
+        "test_id_prefix": "ninja://src/dawn/tests:dawn_end2end_tests/"
+      },
+      {
+        "args": [
+          "--adapter-vendor-id=0x1414"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/noop_merge.py"
+        },
+        "name": "dawn_end2end_warp_tests",
+        "resultdb": {
+          "result_format": "gtest_json"
+        },
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "cpu": "x86-64",
+            "gpu": "none",
+            "os": "Windows-10-19045",
+            "pool": "chromium.tests.gpu"
+          },
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "dawn_end2end_tests",
+        "test_id_prefix": "ninja://src/dawn/tests:dawn_end2end_tests/"
+      },
+      {
+        "merge": {
+          "script": "//testing/merge_scripts/noop_merge.py"
+        },
+        "name": "dawn_unittests",
+        "resultdb": {
+          "result_format": "gtest_json"
+        },
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "cpu": "x86-64",
+            "gpu": "none",
+            "os": "Windows-10-19045",
+            "pool": "chromium.tests.gpu"
+          },
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "dawn_unittests",
+        "test_id_prefix": "ninja://src/dawn/tests:dawn_unittests/"
+      },
+      {
+        "args": [
+          "--use-wire"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/noop_merge.py"
+        },
+        "name": "dawn_wire_unittests",
+        "resultdb": {
+          "result_format": "gtest_json"
+        },
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "cpu": "x86-64",
+            "gpu": "none",
+            "os": "Windows-10-19045",
+            "pool": "chromium.tests.gpu"
+          },
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "dawn_unittests",
+        "test_id_prefix": "ninja://src/dawn/tests:dawn_unittests/"
+      },
+      {
+        "merge": {
+          "script": "//testing/merge_scripts/noop_merge.py"
+        },
+        "name": "tint_unittests",
+        "resultdb": {
+          "result_format": "gtest_json"
+        },
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "cpu": "x86-64",
+            "gpu": "none",
+            "os": "Windows-10-19045",
+            "pool": "chromium.tests.gpu"
+          },
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "tint_unittests",
+        "test_id_prefix": "ninja://src/tint/cmd/test:tint_unittests/"
+      }
+    ],
+    "isolated_scripts": [
+      {
+        "args": [
+          "-backend",
+          "vulkan",
+          "-adapter",
+          "SwiftShader",
+          "webgpu:api,operation,adapter,requestDevice:default:*"
+        ],
+        "name": "dawn_node_sws_cts",
+        "test": "dawn_node_cts",
+        "test_id_prefix": "ninja://scripts/dawn_node_cts:dawn_node_cts/"
+      }
+    ]
+  }
 }
diff --git a/infra/specs/test_suites.pyl b/infra/specs/test_suites.pyl
index aed80cd..c3632e7 100644
--- a/infra/specs/test_suites.pyl
+++ b/infra/specs/test_suites.pyl
@@ -42,6 +42,26 @@
         'test': 'dawn_end2end_tests',
       },
     },
+    'dawn_end2end_warp_gtests': {
+      'dawn_end2end_warp_tests': {
+        'args': [
+          '--adapter-vendor-id=0x1414',
+        ],
+        'mixins': [
+          'result_adapter_gtest_json',
+        ],
+        'merge': {
+          # This should be removed in favor of updating the recipe code to allow
+          # the merge script to be skipped entirely. noop is needed currently
+          # because standard_gtest_merge does not work properly with the
+          # results output by --gtest_output. However, noop_merge locks us to
+          # a single shard, and we should not have any need for the outputs of
+          # merge scripts for Dawn.
+          'script': '//testing/merge_scripts/noop_merge.py',
+        },
+        'test': 'dawn_end2end_tests',
+      },
+    },
     'dawn_node_cts_sws_tests': {
       'dawn_node_sws_cts': {
         'args': [
@@ -146,5 +166,14 @@
       'dawn_wire_unit_gtests',
       'tint_unit_gtests',
     ],
+    # Same as swiftshader_gtests but with the addition of WARP end2end tests.
+    'win_software_renderer_gtests': [
+      'dawn_end2end_swangle_gtests',
+      'dawn_end2end_sws_gtests',
+      'dawn_end2end_warp_gtests',
+      'dawn_unit_gtests',
+      'dawn_wire_unit_gtests',
+      'tint_unit_gtests',
+    ],
   },
 }
diff --git a/infra/specs/waterfalls.pyl b/infra/specs/waterfalls.pyl
index ccaa3db..6f7daa9 100644
--- a/infra/specs/waterfalls.pyl
+++ b/infra/specs/waterfalls.pyl
@@ -112,7 +112,10 @@
           'win10',
           'x86-64',
         ],
-        'test_suites': {},
+        'test_suites': {
+          'gtest_tests': 'win_software_renderer_gtests',
+          'isolated_scripts': 'dawn_node_cts_sws_tests',
+        },
       },
     },
   },
diff --git a/scripts/dawn_node_cts/BUILD.gn b/scripts/dawn_node_cts/BUILD.gn
index 67c319b5..b38f058 100644
--- a/scripts/dawn_node_cts/BUILD.gn
+++ b/scripts/dawn_node_cts/BUILD.gn
@@ -35,15 +35,18 @@
     testonly = true
     data_deps = [
       "${dawn_root}/src/dawn/node:dawn_node",
+      "${dawn_root}/testing:test_scripts_shared",
       "${dawn_root}/tools:go_tools",
     ]
     data = [
       "node_helpers.py",
       "run_dawn_node_cts.py",
       "run_npx.py",
-      "run_npx.sh",
       "${dawn_root}/third_party/node/",
       "${dawn_root}/third_party/webgpu-cts/",
     ]
+    if (is_win) {
+      data += [ "run_npx.bat" ]
+    }
   }
 }
diff --git a/scripts/dawn_node_cts/node_helpers.py b/scripts/dawn_node_cts/node_helpers.py
index 88a1b5a..2912147 100644
--- a/scripts/dawn_node_cts/node_helpers.py
+++ b/scripts/dawn_node_cts/node_helpers.py
@@ -28,6 +28,7 @@
 
 import functools
 import os
+import sys
 
 THIS_DIR = os.path.dirname(__file__)
 DAWN_ROOT = os.path.realpath(os.path.join(THIS_DIR, '..', '..'))
@@ -42,6 +43,8 @@
         A filepath to the standalone node executable.
     """
     path = os.path.join(NODE_DIR, 'bin', 'node')
+    if sys.platform == 'win32':
+        path = os.path.join(NODE_DIR, 'node.exe')
     if not os.path.exists(path):
         raise RuntimeError(
             f'Unable to find the node binary under {NODE_DIR}. Is the '
@@ -59,6 +62,9 @@
     """
     path = os.path.join(NODE_DIR, 'lib', 'node_modules', 'npm', 'bin',
                         'npm-cli.js')
+    if sys.platform == 'win32':
+        path = os.path.join(NODE_DIR, 'node_modules', 'npm', 'bin',
+                            'npm-cli.js')
     if not os.path.exists(path):
         raise RuntimeError(
             f'Unable to find the npm-cli.js file under {NODE_DIR}. Is the '
diff --git a/scripts/dawn_node_cts/run_dawn_node_cts.py b/scripts/dawn_node_cts/run_dawn_node_cts.py
index 9170895..987ef40 100755
--- a/scripts/dawn_node_cts/run_dawn_node_cts.py
+++ b/scripts/dawn_node_cts/run_dawn_node_cts.py
@@ -54,6 +54,9 @@
 
 def run_node_cts(output_directory: str, args_to_forward: list[str]) -> None:
     logging.info('Running CTS via node in %s', os.getcwd())
+    npx_wrapper = os.path.join(THIS_DIR, 'run_npx.py')
+    if sys.platform == 'win32':
+        npx_wrapper = os.path.join(THIS_DIR, 'run_npx.bat')
     cmd = [
         sys.executable,
         os.path.join(TOOLS_DIR, 'run.py'),
@@ -61,7 +64,7 @@
         '-bin',
         output_directory,
         '-npx',
-        os.path.join(THIS_DIR, 'run_npx.sh'),
+        npx_wrapper,
     ] + args_to_forward
     subprocess.run(cmd, check=True)
 
diff --git a/scripts/dawn_node_cts/run_npx.bat b/scripts/dawn_node_cts/run_npx.bat
new file mode 100755
index 0000000..85dc73d
--- /dev/null
+++ b/scripts/dawn_node_cts/run_npx.bat
@@ -0,0 +1,39 @@
+@echo off
+REM Copyright 2025 The Dawn & Tint Authors
+REM
+REM Redistribution and use in source and binary forms, with or without
+REM modification, are permitted provided that the following conditions are met:
+REM
+REM 1. Redistributions of source code must retain the above copyright notice, this
+REM    list of conditions and the following disclaimer.
+REM
+REM 2. Redistributions in binary form must reproduce the above copyright notice,
+REM    this list of conditions and the following disclaimer in the documentation
+REM    and/or other materials provided with the distribution.
+REM
+REM 3. Neither the name of the copyright holder nor the names of its
+REM    contributors may be used to endorse or promote products derived from
+REM    this software without specific prior written permission.
+REM
+REM THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+REM AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+REM IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+REM DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+REM FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+REM DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+REM SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+REM CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+REM OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+REM OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+REM This script is necessary since:
+REM   1. The version of Node that is pulled into Dawn does not have "npx" in its
+REM      bin/, only "node". So, invoking npx requires manually invoking "node"
+REM      with the npx-cli.js file.
+REM   2. npx is used by the Go code, which can only receive a single path for
+REM      its npx path override. Hence, the need for run_npx.py to actually run
+REM      npx.
+REM   3. shebangs don't work on Windows, so we need a bash/batch script to
+REM      invoke the Python script that actually runs npx.
+
+python3 "%~dp0run_npx.py" %*
diff --git a/scripts/dawn_node_cts/run_npx.sh b/scripts/dawn_node_cts/run_npx.sh
deleted file mode 100755
index 0ccf745..0000000
--- a/scripts/dawn_node_cts/run_npx.sh
+++ /dev/null
@@ -1,42 +0,0 @@
-#!/usr/bin/env bash
-# Copyright 2025 The Dawn & Tint Authors
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are met:
-#
-# 1. Redistributions of source code must retain the above copyright notice, this
-#    list of conditions and the following disclaimer.
-#
-# 2. Redistributions in binary form must reproduce the above copyright notice,
-#    this list of conditions and the following disclaimer in the documentation
-#    and/or other materials provided with the distribution.
-#
-# 3. Neither the name of the copyright holder nor the names of its
-#    contributors may be used to endorse or promote products derived from
-#    this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-# This script is necessary since:
-#   1. The version of Node that is pulled into Dawn does not have "npx" in its
-#      bin/, only "node". So, invoking npx requires manually invoking "node"
-#      with the npx-cli.js file.
-#   2. npx is used by the Go code, which can only receive a single path for its
-#      npx path override. Hence, the need for run_npx.py to actually run npx.
-#   3. shebangs don't work on Windows, so we need a bash/batch script to invoke
-#      the Python script that actually runs npx.
-
-set -e # Fail on any error.
-
-SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd )"
-
-python3 "${SCRIPT_DIR}/run_npx.py" "${@:1}"