tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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:
Mservices/application-services/components/viaduct-necko/tests/test_viaduct_necko_backend.js | 103+++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------
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");