276 lines
8.7 KiB
JavaScript
276 lines
8.7 KiB
JavaScript
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
/* eslint-env node */
|
|
|
|
const { logTest, logTask } = require("./utils/profiling");
|
|
|
|
const fs = require("fs");
|
|
const path = require("path");
|
|
|
|
async function waitForH3(maxRetries, browser, url, commands, context) {
|
|
let attempt = 0;
|
|
while (attempt < maxRetries) {
|
|
await commands.navigate(url);
|
|
|
|
// Get performance entries to check the protocol
|
|
let protocolInfo = await commands.js.run(
|
|
`
|
|
// Get all performance entries
|
|
const entries = performance.getEntries();
|
|
|
|
// Create an array to store the results
|
|
const protocolInfo = entries.map(entry => ({
|
|
name: entry.name,
|
|
protocol: entry.nextHopProtocol,
|
|
}));
|
|
|
|
return protocolInfo;
|
|
`
|
|
);
|
|
|
|
// Log the protocol information
|
|
context.log.info("protocolInfo: " + JSON.stringify(protocolInfo));
|
|
|
|
// Check if the main document is using h3 protocol
|
|
const normalizeUrl = url => url.replace(/\/+$/, "");
|
|
const isH3 = protocolInfo.some(
|
|
entry =>
|
|
normalizeUrl(entry.name) === normalizeUrl(url) &&
|
|
entry.protocol === "h3"
|
|
);
|
|
|
|
if (isH3) {
|
|
context.log.info("Protocol h3 detected!");
|
|
return; // Exit the function if h3 is found
|
|
}
|
|
|
|
// Increment the attempt counter and retry
|
|
attempt++;
|
|
context.log.info(
|
|
`Retry attempt ${attempt} - Protocol is not h3. Retrying...`
|
|
);
|
|
if (browser === "firefox") {
|
|
const script = `
|
|
Services.obs.notifyObservers(null, "net:cancel-all-connections");
|
|
Services.obs.notifyObservers(null, "network:reset-http3-excluded-list");
|
|
`;
|
|
commands.js.runPrivileged(script);
|
|
}
|
|
context.log.info("Waiting 3s to close connection...");
|
|
await commands.wait.byTime(3000);
|
|
}
|
|
}
|
|
|
|
async function waitForComplete(timeout, commands, context, id) {
|
|
let starttime = performance.now();
|
|
let status = "";
|
|
let goodput = 0;
|
|
|
|
while (performance.now() - starttime < timeout) {
|
|
status = await commands.js.run(
|
|
`return document.getElementById('${id}').innerHTML;`
|
|
);
|
|
|
|
if (status.startsWith("success")) {
|
|
goodput = parseFloat(status.split(":")[1]);
|
|
status = "success";
|
|
break;
|
|
} else if (status.startsWith("error")) {
|
|
break;
|
|
}
|
|
|
|
await commands.wait.byTime(1000);
|
|
}
|
|
|
|
let endtime = performance.now();
|
|
|
|
return {
|
|
start: starttime,
|
|
end: endtime,
|
|
status,
|
|
goodput,
|
|
};
|
|
}
|
|
|
|
module.exports = logTest(
|
|
"download/upload test",
|
|
async function (context, commands) {
|
|
let serverUrl = `${context.options.browsertime.server_url}`;
|
|
let iterations = `${context.options.browsertime.iterations}`;
|
|
const [protocol, testType] =
|
|
`${context.options.browsertime.test_type}`.split("_");
|
|
|
|
await commands.measure.start(serverUrl);
|
|
let accumulatedResults = [];
|
|
let browserName = "";
|
|
let file_size = parseInt(context.options.browsertime.test_file_size, 10);
|
|
if (Number.isNaN(file_size)) {
|
|
// default is 32MB
|
|
file_size = 32000000;
|
|
}
|
|
for (let iteration = 0; iteration < iterations; iteration++) {
|
|
await logTask(context, "cycle " + iteration, async function () {
|
|
const driver = context.selenium.driver;
|
|
const webdriver = context.selenium.webdriver;
|
|
let capabilities = await driver.getCapabilities();
|
|
browserName = capabilities.get("browserName");
|
|
|
|
if (protocol === "h3") {
|
|
await waitForH3(10, browserName, serverUrl, commands, context);
|
|
} else {
|
|
await commands.navigate(serverUrl);
|
|
}
|
|
|
|
if (testType === "download") {
|
|
const downloadItem = await driver.findElement(
|
|
webdriver.By.id("downloadBtn")
|
|
);
|
|
|
|
const actions = driver.actions({ async: true });
|
|
await actions.move({ origin: downloadItem }).click().perform();
|
|
|
|
// Start the test and wait for the upload to complete
|
|
let results = await waitForComplete(
|
|
1200000,
|
|
commands,
|
|
context,
|
|
"download_status"
|
|
);
|
|
let downloadTime = results.end - results.start;
|
|
// Store result in megabit/seconds
|
|
let downloadGoodput = (file_size * 8) / ((downloadTime / 1000) * 1e6);
|
|
context.log.info(
|
|
"download results: " +
|
|
results.status +
|
|
" duration: " +
|
|
downloadTime +
|
|
"ms, downloadGoodput: " +
|
|
downloadGoodput +
|
|
"Mbit/s"
|
|
);
|
|
accumulatedResults.push(downloadGoodput);
|
|
} else if (testType === "upload") {
|
|
const uploadItem = await driver.findElement(
|
|
webdriver.By.id("fileUpload")
|
|
);
|
|
|
|
let tagName = await uploadItem.getTagName();
|
|
if (tagName === "input") {
|
|
if (context.options.browsertime.moz_fetch_dir == "None") {
|
|
context.log.error(
|
|
"This test depends on the fetch task. Download the file, 'https://github.com/mozilla/perf-automation/raw/master/test_files/upload-test-32MB.dat' and set the os environment variable MOZ_FETCHES_DIR to that directory."
|
|
);
|
|
}
|
|
|
|
let localFilePath = path.join(
|
|
`${context.options.browsertime.moz_fetch_dir}`,
|
|
`${context.options.browsertime.test_file_name}`
|
|
);
|
|
if (!fs.existsSync(localFilePath)) {
|
|
localFilePath = path.join(
|
|
`${context.options.browsertime.moz_fetch_dir}`,
|
|
"upload-test-32MB.dat"
|
|
);
|
|
}
|
|
|
|
context.log.info("Sending file path: " + localFilePath);
|
|
await uploadItem.sendKeys(localFilePath);
|
|
} else {
|
|
const actions = driver.actions({ async: true });
|
|
await actions.move({ origin: uploadItem }).click().perform();
|
|
}
|
|
|
|
// Start the test and wait for the upload to complete
|
|
let results = await waitForComplete(
|
|
1200000,
|
|
commands,
|
|
context,
|
|
"upload_status"
|
|
);
|
|
let uploadTime = results.end - results.start;
|
|
|
|
// Store result in megabit/seconds
|
|
let uploadGoodput = Number.isNaN(results.goodput)
|
|
? (file_size * 8) / ((uploadTime / 1000) * 1e6)
|
|
: results.goodput;
|
|
context.log.info(
|
|
"upload results: " +
|
|
results.status +
|
|
" duration: " +
|
|
uploadTime +
|
|
"ms, uploadGoodput: " +
|
|
uploadGoodput +
|
|
"Mbit/s"
|
|
);
|
|
if (results.status === "success") {
|
|
accumulatedResults.push(uploadGoodput);
|
|
}
|
|
} else {
|
|
context.log.error("Unsupported test type:" + testType);
|
|
}
|
|
});
|
|
|
|
if (browserName === "firefox" && protocol === "h3") {
|
|
const statsScript = `
|
|
const gDashboard = Cc["@mozilla.org/network/dashboard;1"].getService(
|
|
Ci.nsIDashboard
|
|
);
|
|
let promise = new Promise((resolve, reject) => {
|
|
gDashboard.requestHttp3ConnectionStats((data) => {
|
|
resolve(data);
|
|
});
|
|
});
|
|
let done = false;
|
|
let result = null;
|
|
let error = null;
|
|
promise
|
|
.catch(e => {
|
|
error = e;
|
|
})
|
|
.then(r => {
|
|
result = r;
|
|
done = true;
|
|
});
|
|
// Spin the event loop until done becomes true.
|
|
Services.tm.spinEventLoopUntil(
|
|
"requestHttpConnections",
|
|
() => done
|
|
);
|
|
return result;
|
|
`;
|
|
let res = await commands.js.runPrivileged(statsScript);
|
|
context.log.info("HTTP/3 Connection Stats" + JSON.stringify(res));
|
|
}
|
|
|
|
// No need to close the connection at the last run.
|
|
if (iteration != iterations - 1) {
|
|
await commands.navigate("about:blank");
|
|
if (browserName === "firefox") {
|
|
const script = `
|
|
Services.obs.notifyObservers(null, "net:cancel-all-connections");
|
|
`;
|
|
commands.js.runPrivileged(script);
|
|
context.log.info("Waiting 3s to close connection...");
|
|
await commands.wait.byTime(3000);
|
|
} else {
|
|
// TODO: configiure a shorter idle timeout at server side, so we
|
|
// don't have to wait that long.
|
|
context.log.info("Waiting 35s to close connection...");
|
|
await commands.wait.byTime(35000);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (testType === "download") {
|
|
commands.measure.addObject({
|
|
custom_data: { "download-goodput": accumulatedResults },
|
|
});
|
|
} else if (testType === "upload") {
|
|
commands.measure.addObject({
|
|
custom_data: { "upload-goodput": accumulatedResults },
|
|
});
|
|
}
|
|
}
|
|
);
|