commit 38c7e07c4038f481f7ad45c6349e96ac16d4a04b
parent 3fe4e28cbeb4ea573e07c3a9a9b61c8d6a4c91fe
Author: Nazım Can Altınova <canaltinova@gmail.com>
Date: Tue, 21 Oct 2025 23:59:09 +0000
Bug 1979114 - Add some tests for jsSource retrieval r=mstange,profiler-reviewers
Differential Revision: https://phabricator.services.mozilla.com/D259270
Diffstat:
4 files changed, 292 insertions(+), 0 deletions(-)
diff --git a/tools/profiler/tests/browser/browser.toml b/tools/profiler/tests/browser/browser.toml
@@ -11,6 +11,13 @@ support-files = ["simple.html"]
["browser_test_feature_js_sourceindex.js"]
support-files = ["simple.html", "tracing.html"]
+["browser_test_feature_js_sources_retrieval.js"]
+support-files = [
+ "simple.html",
+ "page_with_external_js.html",
+ "external_script.js"
+]
+
["browser_test_feature_jsallocations.js"]
support-files = ["do_work_500ms.html"]
diff --git a/tools/profiler/tests/browser/browser_test_feature_js_sources_retrieval.js b/tools/profiler/tests/browser/browser_test_feature_js_sources_retrieval.js
@@ -0,0 +1,239 @@
+/* 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 JS sources are collected and included in profile additional information.
+ */
+add_task(async function test_js_sources_in_profile_additional_info() {
+ Assert.ok(
+ !Services.profiler.IsActive(),
+ "The profiler is not currently active"
+ );
+
+ const url = BASE_URL + "simple.html";
+ await BrowserTestUtils.withNewTab(url, async contentBrowser => {
+ await ProfilerTestUtils.startProfiler({ features: ["js", "jssources"] });
+ // Execute some JavaScript to create sources
+ await SpecialPowers.spawn(contentBrowser, [], () => {
+ content.window.eval(`
+ function testSourceFunction() {
+ console.log("This is a test function");
+ return 42;
+ }
+ testSourceFunction();
+ `);
+ });
+
+ // Get the profile with additional information
+ // Note: We don't have to await here for the pause because the await for
+ // getProfileDataAsGzippedArrayBuffer will actively wait for the pause to
+ // be executed in the child process first.
+ Services.profiler.Pause();
+ const profileData =
+ await Services.profiler.getProfileDataAsGzippedArrayBuffer();
+ await Services.profiler.StopProfiler();
+
+ // Verify we got profile data
+ Assert.ok(!!profileData, "Should receive profile data");
+ Assert.ok(!!profileData.profile, "Should have profile data");
+ Assert.ok(
+ !!profileData.additionalInformation,
+ "Should have additional information"
+ );
+
+ // Check that the additional information has JS sources
+ const sources = profileData.additionalInformation.jsSources;
+ Assert.ok(!!sources, "Additional info should contain jsSources");
+ Assert.equal(typeof sources, "object", "jsSources should be an object");
+
+ // Check that we have some JS sources
+ Assert.greater(
+ Object.keys(sources).length,
+ 0,
+ "Should have at least one JS source"
+ );
+
+ info(`Total JS sources collected: ${sources.length}`);
+
+ // Check the sources to verify they contain actual source text
+ for (const sourceUuid in sources) {
+ const sourceText = sources[sourceUuid];
+ Assert.ok(
+ typeof sourceUuid === "string" && !!sourceUuid.length,
+ "sourceUuid should be a non-empty string"
+ );
+ Assert.ok(
+ typeof sourceText === "string" && !!sourceText.length,
+ `Source ${sourceUuid} should be a non-empty string`
+ );
+ }
+ });
+});
+
+/**
+ * Test that different types of JS sources are handled correctly.
+ */
+add_task(async function test_js_sources_different_types() {
+ Assert.ok(
+ !Services.profiler.IsActive(),
+ "The profiler is not currently active"
+ );
+
+ const url = BASE_URL + "simple.html";
+ await BrowserTestUtils.withNewTab(url, async contentBrowser => {
+ await ProfilerTestUtils.startProfiler({ features: ["js", "jssources"] });
+ // Execute different types of JavaScript to test source collection
+ await SpecialPowers.spawn(contentBrowser, [], () => {
+ // Inline script
+ content.window.eval(`
+ function inlineFunction() {
+ return "inline";
+ }
+ inlineFunction();
+ `);
+
+ // Anonymous function
+ content.window.eval(`
+ (function() {
+ console.log("anonymous function");
+ })();
+ `);
+ });
+
+ // Get profile data
+ // Note: We don't have to await here for the pause because the await for
+ // getProfileDataAsGzippedArrayBuffer will actively wait for the pause to
+ // be executed in the child process first.
+ Services.profiler.Pause();
+ const profileData =
+ await Services.profiler.getProfileDataAsGzippedArrayBuffer();
+ await Services.profiler.StopProfiler();
+
+ const sources = profileData.additionalInformation.jsSources;
+ Assert.ok(!!sources, "Additional info should contain jsSources");
+ Assert.equal(typeof sources, "object", "jsSources should be an object");
+
+ // Check that we captured sources for different types of JavaScript
+ let inlineSourceCount = 0;
+ let inlineSourceLength = 0;
+
+ for (const sourceUuid in sources) {
+ const sourceText = sources[sourceUuid];
+ if (typeof sourceText === "string" && sourceText.length) {
+ // Check if we found our test functions
+ if (
+ sourceText.includes("inlineFunction") ||
+ sourceText.includes("anonymous function")
+ ) {
+ inlineSourceCount += 1;
+ inlineSourceLength += sourceText.length;
+ }
+ }
+ }
+
+ Assert.greater(
+ inlineSourceLength,
+ 0,
+ "Should have collected source text with content"
+ );
+
+ info(`Total source text length: ${inlineSourceLength} characters`);
+
+ Assert.greaterOrEqual(
+ inlineSourceCount,
+ 2,
+ "Should have collected all the test functions"
+ );
+ });
+});
+
+/**
+ * Test that external JS files are properly collected in JS sources.
+ */
+add_task(async function test_js_sources_external_scripts() {
+ Assert.ok(
+ !Services.profiler.IsActive(),
+ "The profiler is not currently active"
+ );
+
+ const url = BASE_URL + "page_with_external_js.html";
+ await BrowserTestUtils.withNewTab(url, async contentBrowser => {
+ await ProfilerTestUtils.startProfiler({ features: ["js", "jssources"] });
+ // Wait for page to load and execute scripts
+ await SpecialPowers.spawn(contentBrowser, [], () => {
+ return new Promise(resolve => {
+ if (content.document.readyState === "complete") {
+ resolve();
+ } else {
+ content.window.addEventListener("load", resolve);
+ }
+ });
+ });
+
+ // Get profile data
+ // Note: We don't have to await here for the pause because the await for
+ // getProfileDataAsGzippedArrayBuffer will actively wait for the pause to
+ // be executed in the child process first.
+ Services.profiler.Pause();
+ const profileData =
+ await Services.profiler.getProfileDataAsGzippedArrayBuffer();
+ await Services.profiler.StopProfiler();
+
+ const sources = profileData.additionalInformation.jsSources;
+ Assert.ok(!!sources, "Additional info should contain jsSources");
+ Assert.equal(typeof sources, "object", "jsSources should be an object");
+
+ // Look for external script sources
+ let foundExternalScript = false;
+ let foundInlineScript = false;
+ let externalScriptSource = null;
+
+ for (const sourceUuid in sources) {
+ const sourceText = sources[sourceUuid];
+ if (typeof sourceText === "string" && sourceText.length) {
+ // Check for external script content
+ if (
+ sourceText.includes("externalFunction") &&
+ sourceText.includes("calculateSum")
+ ) {
+ foundExternalScript = true;
+ externalScriptSource = sourceText;
+ info(`Found external script source (${sourceText.length} chars)`);
+ }
+
+ // Check for inline script content
+ if (
+ sourceText.includes("inlineFunction") &&
+ sourceText.includes("window.onload")
+ ) {
+ foundInlineScript = true;
+ info(`Found inline script source (${sourceText.length} chars)`);
+ }
+ }
+ }
+
+ Assert.ok(
+ foundExternalScript,
+ "Should find external JavaScript file content in sources"
+ );
+
+ Assert.ok(
+ foundInlineScript,
+ "Should find inline JavaScript content in sources"
+ );
+
+ // Verify external script has expected functions
+ Assert.ok(
+ externalScriptSource.includes("This is an external function"),
+ "External script should contain expected comment"
+ );
+
+ Assert.ok(
+ externalScriptSource.includes("calculateSum"),
+ "External script should contain calculateSum function"
+ );
+
+ info("Successfully verified external JS file collection");
+ });
+});
diff --git a/tools/profiler/tests/browser/external_script.js b/tools/profiler/tests/browser/external_script.js
@@ -0,0 +1,16 @@
+// This is an external function
+function externalFunction() {
+ // Do some work for ~2ms to generate samples
+ const start = performance.now();
+ let result = 0;
+ while (performance.now() - start < 10) {
+ for (let i = 0; i < 1000; i++) {
+ result += Math.random() * i;
+ }
+ }
+ return result;
+}
+
+function calculateSum(a, b) {
+ return a + b;
+}
diff --git a/tools/profiler/tests/browser/page_with_external_js.html b/tools/profiler/tests/browser/page_with_external_js.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Test Page with External JS</title>
+ <script src="external_script.js"></script>
+</head>
+<body>
+ <h1>Test Page for External JS Sources</h1>
+ <p>This page loads an external JavaScript file to test JS source collection.</p>
+
+ <script>
+ function inlineFunction() {
+ // Do some work for ~2ms to generate samples
+ const start = performance.now();
+ let result = 0;
+ while (performance.now() - start < 10) {
+ for (let i = 0; i < 1000; i++) {
+ result += Math.sin(i) * Math.cos(i);
+ }
+ }
+ return result;
+ }
+
+ window.onload = function() {
+ inlineFunction();
+ externalFunction();
+ };
+ </script>
+</body>
+</html>