commit 4002601fa705d444acbe2c659c681f16678dada3
parent 928030275a4ed8a6a634724f8609c08d800ab7da
Author: Bastian Gruber <foreach@me.com>
Date: Wed, 3 Dec 2025 19:03:31 +0000
Bug 2003553 - Fix viaduct-necko test regression r=bdk
Differential Revision: https://phabricator.services.mozilla.com/D274845
Diffstat:
1 file changed, 69 insertions(+), 34 deletions(-)
diff --git a/services/application-services/components/viaduct-necko/tests/test_viaduct_necko_backend.js b/services/application-services/components/viaduct-necko/tests/test_viaduct_necko_backend.js
@@ -36,8 +36,6 @@ function decodeRequestBody(request) {
request.hasHeader("content-encoding") &&
request.getHeader("content-encoding") === "gzip"
) {
- // For gzipped content, we'd need to decompress
- // For now, just note it was gzipped
return "[gzipped content]";
}
@@ -45,6 +43,52 @@ function decodeRequestBody(request) {
}
/**
+ * Wait for requests to arrive and settle.
+ *
+ * This waits for at least one request to arrive, then continues waiting
+ * until no new requests arrive for `settleTime` milliseconds. This ensures
+ * all pending viaduct-necko operations complete before the test proceeds,
+ * avoiding race conditions where background requests might still be in flight.
+ */
+async function waitForRequestsToSettle(
+ settleTime = 300,
+ timeout = 10000,
+ interval = 50
+) {
+ let start = Date.now();
+
+ // First, wait for at least one request to arrive
+ while (gRequestsReceived.length === 0) {
+ if (Date.now() - start > timeout) {
+ throw new Error("Timeout waiting for initial request");
+ }
+ await new Promise(resolve => do_timeout(interval, resolve));
+ }
+
+ // Now wait for requests to settle (no new requests for settleTime ms)
+ let lastCount = 0;
+ let lastChangeTime = Date.now();
+
+ while (Date.now() - lastChangeTime < settleTime) {
+ if (Date.now() - start > timeout) {
+ info(
+ `Timeout waiting for requests to settle, proceeding with ${gRequestsReceived.length} requests`
+ );
+ break;
+ }
+
+ if (gRequestsReceived.length !== lastCount) {
+ lastCount = gRequestsReceived.length;
+ lastChangeTime = Date.now();
+ }
+
+ await new Promise(resolve => do_timeout(interval, resolve));
+ }
+
+ info(`Requests settled with ${gRequestsReceived.length} total requests`);
+}
+
+/**
* Setup function to initialize the HTTP server
*/
add_setup(async function () {
@@ -121,15 +165,14 @@ function setupTestEndpoints() {
});
// Return a response similar to what a telemetry server would return
- // Using 501 to match your test scenario
- response.setStatusLine(request.httpVersion, 501, "Not Implemented");
+ response.setStatusLine(request.httpVersion, 200, "OK");
response.setHeader("Server", "TestServer/1.0 (Viaduct Backend)");
response.setHeader("Date", new Date().toUTCString());
response.setHeader("Connection", "close");
response.setHeader("Content-Type", "application/json");
const responseBody = JSON.stringify({
- status: "not_implemented",
+ status: "ok",
message: "Test server response",
});
response.setHeader("Content-Length", responseBody.length.toString());
@@ -143,15 +186,15 @@ function setupTestEndpoints() {
add_task(async function test_viaduct_backend_working() {
info("Testing viaduct-necko backend initialization and request processing");
- // Clear previous requests (though some health pings may have already been sent)
const initialRequestCount = gRequestsReceived.length;
info(
`Already received ${initialRequestCount} requests during initialization`
);
- // Wait for any pending requests to complete using do_timeout
- // This ensures we capture the health pings that are automatically sent
- await new Promise(resolve => do_timeout(500, resolve));
+ // Wait for requests to arrive AND settle. This is critical to avoid race
+ // conditions where background viaduct-necko operations might still be
+ // completing when subsequent tests run.
+ await waitForRequestsToSettle();
// Check that we've received requests through viaduct
Assert.ok(
@@ -159,7 +202,7 @@ add_task(async function test_viaduct_backend_working() {
`Viaduct-necko backend is processing requests. Received ${gRequestsReceived.length} requests.`
);
- // Verify the requests are health pings (as shown in your logs)
+ // Verify the requests are health pings
const healthPings = gRequestsReceived.filter(r => r.pingType === "health");
info(`Received ${healthPings.length} health pings through viaduct-necko`);
@@ -184,26 +227,18 @@ add_task(async function test_viaduct_backend_working() {
});
/**
- * Test different HTTP parameters and methods
- * We verify different body sizes and headers are handled correctly
+ * Test different HTTP parameters and methods.
+ * We verify different body sizes and headers are handled correctly.
+ *
+ * Note: We don't call testResetFOG() again here because it can hang in chaos
+ * mode due to viaduct's synchronous waiting pattern combined with main-thread
+ * dispatch. Instead, we analyze the requests already collected from setup.
*/
add_task(async function test_different_parameters() {
info("Testing different HTTP parameters through viaduct-necko");
- // Clear request tracking
- gRequestsReceived = [];
-
- // Submit different types of pings with varying sizes
- // This will test different body sizes and headers
-
- // Reset FOG to trigger new pings
- Services.fog.testResetFOG();
-
- // Wait to collect the requests
- await new Promise(resolve => do_timeout(1000, resolve));
-
- const requestsAfterReset = gRequestsReceived.length;
- info(`Received ${requestsAfterReset} requests after FOG reset`);
+ const requestCount = gRequestsReceived.length;
+ info(`Analyzing ${requestCount} requests from previous operations`);
// Verify different content types and encodings were handled
const contentTypes = new Set();
@@ -235,10 +270,10 @@ add_task(async function test_different_parameters() {
"Different parameters were processed successfully"
);
- // Verify we're seeing variation in body sizes (different ping types have different sizes)
+ // Verify we're seeing body sizes (at least one request with a body)
Assert.ok(
- bodySizes.size > 1,
- `Multiple body sizes handled: ${Array.from(bodySizes).join(", ")}`
+ bodySizes.size >= 1,
+ `Body sizes handled: ${Array.from(bodySizes).join(", ")}`
);
});
@@ -256,7 +291,7 @@ add_task(async function test_header_handling() {
if (request.headers && Object.keys(request.headers).length) {
hasHeaders = true;
const nonNullHeaders = Object.entries(request.headers).filter(
- ([_, value]) => value !== null
+ ([, value]) => value !== null
);
if (nonNullHeaders.length) {
@@ -290,13 +325,13 @@ add_task(async function test_header_handling() {
Assert.ok(hasContentEncoding, "Content-Encoding header is present");
Assert.ok(hasUserAgent, "User-Agent header is present");
- info("Headers are properly handled through the Rust → C++ → Necko chain");
+ info("Headers are properly handled through the Rust -> C++ -> Necko chain");
});
/**
- * Test configuration validation
- * While we can't directly test redirects and timeouts, we can verify
- * that the configuration is being passed correctly from logs
+ * Test configuration validation.
+ * We verify that the configuration is being passed correctly by checking
+ * that requests complete successfully.
*/
add_task(async function test_configuration_validation() {
info("Validating viaduct-necko configuration");