[testharness.js] Fix AssertRecord bug for asserts in setup in workers (#27308)

If a setup function has an assert, then `assert.test` will be null. For
RemoteContext, we assumed that this wasn't the case (to read
test.index), and so we would throw an exception if someone asserted in a
setup function.

Fixes https://www.github.com/web-platform-tests/wpt/issues/27299
diff --git a/resources/test/conftest.py b/resources/test/conftest.py
index de0d297..e46f93b 100644
--- a/resources/test/conftest.py
+++ b/resources/test/conftest.py
@@ -222,7 +222,7 @@
             # one
             for obj in [summarized, self.expected]:
                 obj["summarized_asserts"].sort(
-                    key=lambda x: (x["test"], x["status"], x["assert_name"], tuple(x["args"])))
+                    key=lambda x: (x["test"] or "", x["status"], x["assert_name"], tuple(x["args"])))
 
         assert summarized == self.expected
 
diff --git a/resources/test/tests/functional/setup-function-worker.js b/resources/test/tests/functional/setup-function-worker.js
new file mode 100644
index 0000000..82c1456
--- /dev/null
+++ b/resources/test/tests/functional/setup-function-worker.js
@@ -0,0 +1,14 @@
+importScripts("/resources/testharness.js");
+
+// Regression test for https://github.com/web-platform-tests/wpt/issues/27299,
+// where we broke the ability for a setup function in a worker to contain an
+// assertion (even a passing one).
+setup(function() {
+    assert_true(true, "True is true");
+});
+
+// We must define at least one test for the harness, though it is not what we
+// are testing here.
+test(function() {
+    assert_false(false, "False is false");
+}, 'Worker test');
diff --git a/resources/test/tests/functional/setup-worker-service.html b/resources/test/tests/functional/setup-worker-service.html
new file mode 100644
index 0000000..b7a387f
--- /dev/null
+++ b/resources/test/tests/functional/setup-worker-service.html
@@ -0,0 +1,106 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta name="variant" content="?keep-promise">
+<title>Setup function in a service worker</title>
+<script src="../../variants.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<h1>Setup function in a service worker</h1>
+<p>This test assumes that the browser supports <a href="http://www.w3.org/TR/service-workers/">ServiceWorkers</a>.
+<div id="log"></div>
+
+<script>
+test(function(t) {
+    assert_true("serviceWorker" in navigator,
+                "navigator.serviceWorker exists");
+}, "Browser supports ServiceWorker");
+
+promise_test(function() {
+    // Since the service worker registration could be in an indeterminate
+    // state (due to, for example, a previous test run failing), we start by
+    // unregstering our service worker and then registering it again.
+    var scope = "service-worker-scope";
+    var worker_url = "setup-function-worker.js";
+
+    return navigator.serviceWorker.register(worker_url, {scope: scope})
+        .then(function(registration) {
+            return registration.unregister();
+        }).then(function() {
+            return navigator.serviceWorker.register(worker_url, {scope: scope});
+        }).then(function(registration) {
+            add_completion_callback(function() {
+                registration.unregister();
+            });
+
+            return new Promise(function(resolve) {
+                registration.addEventListener("updatefound", function() {
+                    resolve(registration.installing);
+                });
+            });
+        }).then(function(worker) {
+            fetch_tests_from_worker(worker);
+        });
+}, "Register ServiceWorker");
+</script>
+<script type="text/json" id="expected">
+{
+  "summarized_status": {
+    "status_string": "OK",
+    "message": null
+  },
+  "summarized_tests": [
+    {
+      "status_string": "PASS",
+      "name": "Browser supports ServiceWorker",
+      "properties": {},
+      "message": null
+    },
+    {
+      "message": null,
+      "name": "Register ServiceWorker",
+      "properties": {},
+      "status_string": "PASS"
+    },
+    {
+      "message": null,
+      "name": "Worker test",
+      "properties": {},
+      "status_string": "PASS"
+    }
+  ],
+  "summarized_asserts": [
+    {
+      "assert_name": "assert_true",
+      "test": "Browser supports ServiceWorker",
+      "args": [
+        "true",
+        "\"navigator.serviceWorker exists\""
+      ],
+      "status": 0
+    },
+    {
+      "assert_name": "assert_true",
+      "test": null,
+      "args": [
+        "true",
+        "\"True is true\""
+      ],
+      "status": 0
+    },
+    {
+      "assert_name": "assert_false",
+      "test": "Worker test",
+      "args": [
+        "false",
+        "\"False is false\""
+      ],
+      "status": 0
+    }
+  ],
+  "type": "complete"
+}
+</script>
+</body>
diff --git a/resources/testharness.js b/resources/testharness.js
index 296c93c..84df6ae 100644
--- a/resources/testharness.js
+++ b/resources/testharness.js
@@ -2641,7 +2641,7 @@
             var record = new AssertRecord();
             record.assert_name = assert.assert_name;
             record.args = assert.args;
-            record.test = this.tests[assert.test.index];
+            record.test = assert.test != null ? this.tests[assert.test.index] : null;
             record.status = assert.status;
             record.stack = assert.stack;
             tests.asserts_run.push(record);