зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1688669
- Add a new test for network markers r=canaltinova
Differential Revision: https://phabricator.services.mozilla.com/D102940
This commit is contained in:
Родитель
5d5e29f665
Коммит
3b2478d420
|
@ -24,3 +24,13 @@ support-files = single_frame.html
|
|||
support-files =
|
||||
multi_frame.html
|
||||
single_frame.html
|
||||
|
||||
[browser_test_marker_network_simple.js]
|
||||
support-files = simple.html
|
||||
|
||||
[browser_test_marker_network_redirect.js]
|
||||
support-files =
|
||||
redirect.sjs
|
||||
simple.html
|
||||
page_with_resources.html
|
||||
firefox-logo-nightly.svg
|
||||
|
|
|
@ -0,0 +1,304 @@
|
|||
/* 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/. */
|
||||
|
||||
/**
|
||||
* Test that we emit network markers accordingly.
|
||||
* In this file we'll test the redirect cases.
|
||||
*/
|
||||
add_task(async function test_network_markers_redirect_simple() {
|
||||
// In this test, we request an HTML page that gets redirected. This is a
|
||||
// top-level navigation.
|
||||
if (!AppConstants.MOZ_GECKO_PROFILER) {
|
||||
return;
|
||||
}
|
||||
Assert.ok(
|
||||
!Services.profiler.IsActive(),
|
||||
"The profiler is not currently active"
|
||||
);
|
||||
|
||||
startProfilerForMarkerTests();
|
||||
|
||||
const targetFileNameWithCacheBust = "simple.html?cacheBust=" + Math.random();
|
||||
const url =
|
||||
BASE_URL +
|
||||
"redirect.sjs?" +
|
||||
encodeURIComponent(targetFileNameWithCacheBust);
|
||||
const targetUrl = BASE_URL + targetFileNameWithCacheBust;
|
||||
|
||||
await BrowserTestUtils.withNewTab(url, async contentBrowser => {
|
||||
const contentPid = await SpecialPowers.spawn(
|
||||
contentBrowser,
|
||||
[],
|
||||
() => Services.appinfo.processID
|
||||
);
|
||||
|
||||
const { parentThread, contentThread } = await stopProfilerNowAndGetThreads(
|
||||
contentPid
|
||||
);
|
||||
|
||||
const parentNetworkMarkers = getInflatedNetworkMarkers(parentThread);
|
||||
const contentNetworkMarkers = getInflatedNetworkMarkers(contentThread);
|
||||
info(JSON.stringify(parentNetworkMarkers, null, 2));
|
||||
info(JSON.stringify(contentNetworkMarkers, null, 2));
|
||||
|
||||
Assert.equal(
|
||||
parentNetworkMarkers.length,
|
||||
4,
|
||||
`We should get 2 pairs of network markers in the parent thread.`
|
||||
);
|
||||
|
||||
/* It looks like that for a redirection for the top level navigation, the
|
||||
* content thread sees the markers for the second request only.
|
||||
* See Bug 1692879. */
|
||||
Assert.equal(
|
||||
contentNetworkMarkers.length,
|
||||
2,
|
||||
`We should get one pair of network markers in the content thread.`
|
||||
);
|
||||
|
||||
const parentRedirectMarker = parentNetworkMarkers[1];
|
||||
const parentStopMarker = parentNetworkMarkers[3];
|
||||
const contentStopMarker = contentNetworkMarkers[1];
|
||||
|
||||
Assert.objectContains(parentRedirectMarker, {
|
||||
name: Expect.stringMatches(`Load \\d+:.*${escapeStringRegexp(url)}`),
|
||||
data: Expect.objectContains({
|
||||
status: "STATUS_REDIRECT",
|
||||
URI: url,
|
||||
RedirectURI: targetUrl,
|
||||
requestMethod: "GET",
|
||||
contentType: null,
|
||||
startTime: Expect.number(),
|
||||
endTime: Expect.number(),
|
||||
domainLookupStart: Expect.number(),
|
||||
domainLookupEnd: Expect.number(),
|
||||
connectStart: Expect.number(),
|
||||
tcpConnectEnd: Expect.number(),
|
||||
connectEnd: Expect.number(),
|
||||
requestStart: Expect.number(),
|
||||
responseStart: Expect.number(),
|
||||
responseEnd: Expect.number(),
|
||||
id: Expect.number(),
|
||||
pri: Expect.number(),
|
||||
}),
|
||||
});
|
||||
|
||||
const expectedProperties = {
|
||||
name: Expect.stringMatches(
|
||||
`Load \\d+:.*${escapeStringRegexp(targetUrl)}`
|
||||
),
|
||||
data: Expect.objectContains({
|
||||
status: "STATUS_STOP",
|
||||
URI: targetUrl,
|
||||
requestMethod: "GET",
|
||||
contentType: "text/html",
|
||||
startTime: Expect.number(),
|
||||
endTime: Expect.number(),
|
||||
domainLookupStart: Expect.number(),
|
||||
domainLookupEnd: Expect.number(),
|
||||
connectStart: Expect.number(),
|
||||
tcpConnectEnd: Expect.number(),
|
||||
connectEnd: Expect.number(),
|
||||
requestStart: Expect.number(),
|
||||
responseStart: Expect.number(),
|
||||
responseEnd: Expect.number(),
|
||||
id: Expect.number(),
|
||||
count: Expect.number(),
|
||||
pri: Expect.number(),
|
||||
}),
|
||||
};
|
||||
|
||||
Assert.objectContains(parentStopMarker, expectedProperties);
|
||||
// The cache information is missing from the content marker, it's only part
|
||||
// of the parent marker. See Bug 1544821.
|
||||
Assert.objectContains(parentStopMarker.data, {
|
||||
// Because the request races with the cache, these 2 values are valid:
|
||||
// "Missed" when the cache answered before we get a result from the network.
|
||||
// "Unresolved" when we got a response from the network before the cache subsystem.
|
||||
cache: Expect.stringMatches(/^(Missed|Unresolved)$/),
|
||||
});
|
||||
Assert.objectContains(contentStopMarker, expectedProperties);
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function test_network_markers_redirect_resources() {
|
||||
// In this test we request an HTML file that itself contains resources that
|
||||
// are redirected.
|
||||
if (!AppConstants.MOZ_GECKO_PROFILER) {
|
||||
return;
|
||||
}
|
||||
Assert.ok(
|
||||
!Services.profiler.IsActive(),
|
||||
"The profiler is not currently active"
|
||||
);
|
||||
|
||||
startProfilerForMarkerTests();
|
||||
|
||||
const url = BASE_URL + "page_with_resources.html?cacheBust=" + Math.random();
|
||||
await BrowserTestUtils.withNewTab(url, async contentBrowser => {
|
||||
const contentPid = await SpecialPowers.spawn(
|
||||
contentBrowser,
|
||||
[],
|
||||
() => Services.appinfo.processID
|
||||
);
|
||||
|
||||
const { parentThread, contentThread } = await stopProfilerNowAndGetThreads(
|
||||
contentPid
|
||||
);
|
||||
|
||||
const parentNetworkMarkers = getInflatedNetworkMarkers(parentThread);
|
||||
const contentNetworkMarkers = getInflatedNetworkMarkers(contentThread);
|
||||
info(JSON.stringify(parentNetworkMarkers, null, 2));
|
||||
info(JSON.stringify(contentNetworkMarkers, null, 2));
|
||||
|
||||
Assert.equal(
|
||||
parentNetworkMarkers.length,
|
||||
8,
|
||||
`We should get 4 pairs of network markers in the parent thread.`
|
||||
// 1 - The main page
|
||||
// 2 - The SVG
|
||||
// 3 - The redirected request for the second SVG request.
|
||||
// 4 - The SVG, again
|
||||
);
|
||||
|
||||
/* In this second test, the top level navigation request isn't redirected.
|
||||
* Contrary to Bug 1692879 we get all network markers for redirected
|
||||
* resources. */
|
||||
Assert.equal(
|
||||
contentNetworkMarkers.length,
|
||||
8,
|
||||
`We should get 4 pairs of network markers in the content thread.`
|
||||
);
|
||||
|
||||
// The same resource firefox-logo-nightly.svg is requested twice, but the
|
||||
// second time it is redirected.
|
||||
// We're not interested in the main page, as we test that in other files.
|
||||
// In this page we're only interested in the marker for requested resources.
|
||||
|
||||
const parentPairs = getPairsOfNetworkMarkers(parentNetworkMarkers);
|
||||
const contentPairs = getPairsOfNetworkMarkers(contentNetworkMarkers);
|
||||
|
||||
// First, make sure we properly matched all start with stop markers. This
|
||||
// means that both arrays should contain only arrays of 2 elements.
|
||||
parentPairs.forEach(pair =>
|
||||
Assert.equal(
|
||||
pair.length,
|
||||
2,
|
||||
`For the URL ${pair[0].data.URI} we should get 2 markers in the parent process.`
|
||||
)
|
||||
);
|
||||
contentPairs.forEach(pair =>
|
||||
Assert.equal(
|
||||
pair.length,
|
||||
2,
|
||||
`For the URL ${pair[0].data.URI} we should get 2 markers in the content process.`
|
||||
)
|
||||
);
|
||||
|
||||
const parentFirstStopMarker = parentPairs[1][1];
|
||||
const parentRedirectMarker = parentPairs[2][1];
|
||||
const parentSecondStopMarker = parentPairs[3][1];
|
||||
const contentFirstStopMarker = contentPairs[1][1];
|
||||
const contentRedirectMarker = contentPairs[2][1];
|
||||
const contentSecondStopMarker = contentPairs[3][1];
|
||||
|
||||
const expectedCommonProperties = {
|
||||
requestMethod: "GET",
|
||||
startTime: Expect.number(),
|
||||
endTime: Expect.number(),
|
||||
id: Expect.number(),
|
||||
pri: Expect.number(),
|
||||
};
|
||||
|
||||
const expectedPropertiesForFirstMarker = {
|
||||
name: Expect.stringMatches(/Load \d+:.*firefox-logo-nightly\.svg/),
|
||||
data: Expect.objectContains({
|
||||
...expectedCommonProperties,
|
||||
status: "STATUS_STOP",
|
||||
URI: Expect.stringContains("/firefox-logo-nightly.svg"),
|
||||
contentType: "image/svg+xml",
|
||||
domainLookupStart: Expect.number(),
|
||||
domainLookupEnd: Expect.number(),
|
||||
connectStart: Expect.number(),
|
||||
tcpConnectEnd: Expect.number(),
|
||||
connectEnd: Expect.number(),
|
||||
requestStart: Expect.number(),
|
||||
responseStart: Expect.number(),
|
||||
responseEnd: Expect.number(),
|
||||
}),
|
||||
};
|
||||
|
||||
const expectedPropertiesForRedirectMarker = {
|
||||
name: Expect.stringMatches(
|
||||
/Load \d+:.*redirect.sjs\?firefox-logo-nightly\.svg/
|
||||
),
|
||||
data: Expect.objectContains({
|
||||
...expectedCommonProperties,
|
||||
status: "STATUS_REDIRECT",
|
||||
URI: Expect.stringContains("/redirect.sjs?firefox-logo-nightly.svg"),
|
||||
RedirectURI: Expect.stringContains("/firefox-logo-nightly.svg"),
|
||||
contentType: null,
|
||||
domainLookupStart: Expect.number(),
|
||||
domainLookupEnd: Expect.number(),
|
||||
connectStart: Expect.number(),
|
||||
tcpConnectEnd: Expect.number(),
|
||||
connectEnd: Expect.number(),
|
||||
requestStart: Expect.number(),
|
||||
responseStart: Expect.number(),
|
||||
responseEnd: Expect.number(),
|
||||
}),
|
||||
};
|
||||
|
||||
const expectedPropertiesForSecondMarker = {
|
||||
name: Expect.stringMatches(/Load \d+:.*firefox-logo-nightly\.svg/),
|
||||
data: Expect.objectContains({
|
||||
...expectedCommonProperties,
|
||||
status: "STATUS_STOP",
|
||||
URI: Expect.stringContains("/firefox-logo-nightly.svg"),
|
||||
contentType: "image/svg+xml",
|
||||
}),
|
||||
};
|
||||
|
||||
Assert.objectContains(
|
||||
parentFirstStopMarker,
|
||||
expectedPropertiesForFirstMarker
|
||||
);
|
||||
Assert.objectContains(
|
||||
contentFirstStopMarker,
|
||||
expectedPropertiesForFirstMarker
|
||||
);
|
||||
Assert.objectContains(
|
||||
parentRedirectMarker,
|
||||
expectedPropertiesForRedirectMarker
|
||||
);
|
||||
Assert.objectContains(
|
||||
contentRedirectMarker,
|
||||
expectedPropertiesForRedirectMarker
|
||||
);
|
||||
Assert.objectContains(
|
||||
parentSecondStopMarker,
|
||||
expectedPropertiesForSecondMarker
|
||||
);
|
||||
Assert.objectContains(
|
||||
contentSecondStopMarker,
|
||||
expectedPropertiesForSecondMarker
|
||||
);
|
||||
|
||||
// The cache information is missing from the content marker, it's only part
|
||||
// of the parent marker. See Bug 1544821.
|
||||
// Also, because the request races with the cache, these 2 values are valid:
|
||||
// "Missed" when the cache answered before we get a result from the network.
|
||||
// "Unresolved" when we got a response from the network before the cache subsystem.
|
||||
Assert.objectContains(parentFirstStopMarker.data, {
|
||||
cache: Expect.stringMatches(/^(Missed|Unresolved)$/),
|
||||
});
|
||||
Assert.objectContains(parentRedirectMarker.data, {
|
||||
cache: Expect.stringMatches(/^(Missed|Unresolved)$/),
|
||||
});
|
||||
// The second request to the SVG file is already in the cache, though.
|
||||
Assert.objectContains(parentSecondStopMarker.data, {
|
||||
cache: "Hit",
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,84 @@
|
|||
/* 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/. */
|
||||
|
||||
/**
|
||||
* Test that we emit network markers accordingly
|
||||
*/
|
||||
add_task(async function test_network_markers() {
|
||||
if (!AppConstants.MOZ_GECKO_PROFILER) {
|
||||
return;
|
||||
}
|
||||
Assert.ok(
|
||||
!Services.profiler.IsActive(),
|
||||
"The profiler is not currently active"
|
||||
);
|
||||
|
||||
startProfilerForMarkerTests();
|
||||
|
||||
const url = BASE_URL + "simple.html?cacheBust=" + Math.random();
|
||||
await BrowserTestUtils.withNewTab(url, async contentBrowser => {
|
||||
const contentPid = await SpecialPowers.spawn(
|
||||
contentBrowser,
|
||||
[],
|
||||
() => Services.appinfo.processID
|
||||
);
|
||||
|
||||
const { parentThread, contentThread } = await stopProfilerNowAndGetThreads(
|
||||
contentPid
|
||||
);
|
||||
|
||||
const parentNetworkMarkers = getInflatedNetworkMarkers(parentThread);
|
||||
const contentNetworkMarkers = getInflatedNetworkMarkers(contentThread);
|
||||
info(JSON.stringify(parentNetworkMarkers, null, 2));
|
||||
info(JSON.stringify(contentNetworkMarkers, null, 2));
|
||||
|
||||
Assert.equal(
|
||||
parentNetworkMarkers.length,
|
||||
2,
|
||||
`We should get a pair of network markers in the parent thread.`
|
||||
);
|
||||
Assert.equal(
|
||||
contentNetworkMarkers.length,
|
||||
2,
|
||||
`We should get a pair of network markers in the content thread.`
|
||||
);
|
||||
|
||||
const parentStopMarker = parentNetworkMarkers[1];
|
||||
const contentStopMarker = contentNetworkMarkers[1];
|
||||
|
||||
const expectedProperties = {
|
||||
name: Expect.stringMatches(`Load \\d+:.*${escapeStringRegexp(url)}`),
|
||||
data: Expect.objectContains({
|
||||
status: "STATUS_STOP",
|
||||
URI: url,
|
||||
requestMethod: "GET",
|
||||
contentType: "text/html",
|
||||
startTime: Expect.number(),
|
||||
endTime: Expect.number(),
|
||||
domainLookupStart: Expect.number(),
|
||||
domainLookupEnd: Expect.number(),
|
||||
connectStart: Expect.number(),
|
||||
tcpConnectEnd: Expect.number(),
|
||||
connectEnd: Expect.number(),
|
||||
requestStart: Expect.number(),
|
||||
responseStart: Expect.number(),
|
||||
responseEnd: Expect.number(),
|
||||
id: Expect.number(),
|
||||
count: Expect.number(),
|
||||
pri: Expect.number(),
|
||||
}),
|
||||
};
|
||||
|
||||
Assert.objectContains(parentStopMarker, expectedProperties);
|
||||
// The cache information is missing from the content marker, it's only part
|
||||
// of the parent marker. See Bug 1544821.
|
||||
Assert.objectContains(parentStopMarker.data, {
|
||||
// Because the request races with the cache, these 2 values are valid:
|
||||
// "Missed" when the cache answered before we get a result from the network.
|
||||
// "Unresolved" when we got a response from the network before the cache subsystem.
|
||||
cache: Expect.stringMatches(/^(Missed|Unresolved)$/),
|
||||
});
|
||||
Assert.objectContains(contentStopMarker, expectedProperties);
|
||||
});
|
||||
});
|
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
После Ширина: | Высота: | Размер: 19 KiB |
|
@ -21,8 +21,8 @@ registerCleanupFunction(() => {
|
|||
});
|
||||
|
||||
/**
|
||||
* This is a helper function that will stop the profiler of the browser running
|
||||
* with PID contentPid.
|
||||
* This is a helper function that will stop the profiler and returns the main
|
||||
* threads for the parent process and the content process with PID contentPid.
|
||||
* This happens immediately, without waiting for any sampling to happen or
|
||||
* finish. Use stopProfilerAndGetThreads (without "Now") below instead to wait
|
||||
* for samples before stopping.
|
||||
|
@ -31,6 +31,7 @@ registerCleanupFunction(() => {
|
|||
* @returns {Promise}
|
||||
*/
|
||||
async function stopProfilerNowAndGetThreads(contentPid) {
|
||||
Services.profiler.Pause();
|
||||
const profile = await Services.profiler.getProfileDataAsync();
|
||||
Services.profiler.StopProfiler();
|
||||
|
||||
|
@ -55,8 +56,8 @@ async function stopProfilerNowAndGetThreads(contentPid) {
|
|||
}
|
||||
|
||||
/**
|
||||
* This is a helper function that will stop the profiler of the browser running
|
||||
* with PID contentPid.
|
||||
* This is a helper function that will stop the profiler and returns the main
|
||||
* threads for the parent process and the content process with PID contentPid.
|
||||
* As opposed to stopProfilerNowAndGetThreads (with "Now") above, the profiler
|
||||
* in that PID will not stop until there is at least one periodic sample taken.
|
||||
*
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
</head>
|
||||
<body>
|
||||
Testing
|
||||
<img src='firefox-logo-nightly.svg' width="24"/>
|
||||
<img src='redirect.sjs?firefox-logo-nightly.svg' width="24"/>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,4 @@
|
|||
function handleRequest(request, response) {
|
||||
response.setStatusLine(request.httpVersion, 301, "Moved Permanently");
|
||||
response.setHeader("Location", decodeURIComponent(request.queryString), false);
|
||||
}
|
|
@ -127,6 +127,48 @@ function getInflatedMarkerData(thread) {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the marker schema to create individual objects for each marker, then
|
||||
* keeps only the network markers that match the profiler tests.
|
||||
*
|
||||
* @param {Object} thread The thread from a profile.
|
||||
* @return {InflatedMarker[]} The filtered network markers.
|
||||
*/
|
||||
function getInflatedNetworkMarkers(thread) {
|
||||
const markers = getInflatedMarkerData(thread);
|
||||
return markers.filter(
|
||||
m =>
|
||||
m.data &&
|
||||
m.data.type === "Network" &&
|
||||
// We filter out network markers that aren't related to the test, to
|
||||
// avoid intermittents.
|
||||
m.data.URI.includes("/browser/tools/profiler/")
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* From a list of network markers, this returns pairs of start/stop markers.
|
||||
* If a stop marker can't be found for a start marker, this will return an array
|
||||
* of only 1 element.
|
||||
*
|
||||
* @param {InflatedMarker[]} networkMarkers Network markers
|
||||
* @return {InflatedMarker[][]} Pairs of network markers
|
||||
*/
|
||||
function getPairsOfNetworkMarkers(allNetworkMarkers) {
|
||||
// For each 'start' marker we want to find the 'stop' or 'redirect' marker
|
||||
// with the same id.
|
||||
// Note: the algorithm we use here to match markers is very crude and would
|
||||
// be too slow for the real product, but because it is very simple it's good
|
||||
// for a test. Also the logic will be compatible with future marker sets.
|
||||
// Because we only have a few markers the performance is good enough in this
|
||||
// case.
|
||||
return allNetworkMarkers
|
||||
.filter(({ data }) => data.status === "STATUS_START")
|
||||
.map(startMarker =>
|
||||
allNetworkMarkers.filter(({ data }) => data.id === startMarker.data.id)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* It can be helpful to force the profiler to collect a JavaScript sample. This
|
||||
* function spins on a while loop until at least one more sample is collected.
|
||||
|
@ -221,6 +263,27 @@ function getSchema(profile, name) {
|
|||
throw new Error(`Could not find a schema for "${name}".`);
|
||||
}
|
||||
|
||||
/**
|
||||
* This escapes all characters that have a special meaning in RegExps.
|
||||
* This was stolen from https://github.com/sindresorhus/escape-string-regexp and
|
||||
* so it is licence MIT and:
|
||||
* Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (https://sindresorhus.com).
|
||||
* See the full license in https://raw.githubusercontent.com/sindresorhus/escape-string-regexp/main/license.
|
||||
* @param {string} string The string to be escaped
|
||||
* @returns {string} The result
|
||||
*/
|
||||
function escapeStringRegexp(string) {
|
||||
if (typeof string !== "string") {
|
||||
throw new TypeError("Expected a string");
|
||||
}
|
||||
|
||||
// Escape characters with special meaning either inside or outside character
|
||||
// sets. Use a simple backslash escape when it’s always valid, and a `\xnn`
|
||||
// escape when the simpler form would be disallowed by Unicode patterns’
|
||||
// stricter grammar.
|
||||
return string.replace(/[|\\{}()[\]^$+*?.]/g, "\\$&").replace(/-/g, "\\x2d");
|
||||
}
|
||||
|
||||
/** ------ Assertions helper ------ */
|
||||
/**
|
||||
* This assert helper function makes it easy to check a lot of properties in an
|
||||
|
|
Загрузка…
Ссылка в новой задаче