Bug 1757552 - [devtools] Generate actor-less reps stubs.r=bomsy.

This patch is introducing the machinery to automatically generate/check some
stubs used by Reps.
We're focusing on stubs that shouldn't be represented by a front as it's easier
to deal with; we should then have follow up and incremental patches for each
stubs.

Some data can't be retrieved after being serialized/deserialized (`-0`, unsafe int, …),
and in such case the associated test was modified to directly pass the object.

Differential Revision: https://phabricator.services.mozilla.com/D139933
This commit is contained in:
Nicolas Chevobbe 2022-03-03 16:16:50 +00:00
Родитель 4b2d62bd4f
Коммит e954c570c9
12 изменённых файлов: 278 добавлений и 17 удалений

Просмотреть файл

@ -74,6 +74,7 @@ devtools/client/preferences/
devtools/shared/css/generated/properties-db.js
devtools/client/webconsole/test/node/fixtures/stubs/*.js
!devtools/client/webconsole/test/node/fixtures/stubs/index.js
devtools/client/shared/components/test/node/stubs/reps/*.js
# Ignore devtools files testing sourcemaps / code style
devtools/client/framework/test/code_*

Просмотреть файл

@ -34,4 +34,7 @@ DevToolsModules(
)
MOCHITEST_CHROME_MANIFESTS += ["test/chrome/chrome.ini"]
BROWSER_CHROME_MANIFESTS += ["test/browser/browser.ini"]
BROWSER_CHROME_MANIFESTS += [
"test/browser/browser.ini",
"test/node/stubs/reps/stubs.ini",
]

Просмотреть файл

@ -6,3 +6,4 @@ support-files =
!/devtools/client/shared/test/telemetry-test-helpers.js
[browser_notification_box_basic.js]
[browser_reps_stubs.js]

Просмотреть файл

@ -0,0 +1,218 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/* import-globals-from ../../../../shared/test/shared-head.js */
Services.scriptloader.loadSubScript(
"chrome://mochitests/content/browser/devtools/client/shared/test/shared-head.js",
this
);
const TEST_URI = "data:text/html;charset=utf-8,stub generation";
/**
* A Map keyed by filename, and for which the value is also a Map, with the key being the
* label for the stub, and the value the expression to evaluate to get the stub.
*/
const EXPRESSIONS_BY_FILE = {
"infinity.js": new Map([
["Infinity", `Infinity`],
["NegativeInfinity", `-Infinity`],
]),
"nan.js": new Map([["NaN", `2 * document`]]),
"null.js": new Map([["Null", `null`]]),
"number.js": new Map([
["Int", `2 + 3`],
["True", `true`],
["False", `false`],
["NegZeroGrip", `1 / -Infinity`],
]),
"undefined.js": new Map([["Undefined", `undefined`]]),
// XXX: File a bug blocking Bug 1671400 for enabling automatic generation for one of
// the following file.
// "accessible.js",
// "accessor.js",
// "attribute.js",
// "big-int.js",
// "comment-node.js",
// "date-time.js",
// "document-type.js",
// "document.js",
// "element-node.js",
// "error.js",
// "event.js",
// "failure.js",
// "function.js",
// "grip-array.js",
// "grip-map-entry.js",
// "grip-map.js",
// "grip.js",
// "long-string.js",
// "object-with-text.js",
// "object-with-url.js",
// "promise.js",
// "regexp.js",
// "stylesheet.js",
// "symbol.js",
// "text-node.js",
// "window.js",
};
add_task(async function() {
const isStubsUpdate = env.get(STUBS_UPDATE_ENV) == "true";
const tab = await addTab(TEST_URI);
const {
CommandsFactory,
} = require("devtools/shared/commands/commands-factory");
const commands = await CommandsFactory.forTab(tab);
await commands.targetCommand.startListening();
let failed = false;
for (const stubFile of Object.keys(EXPRESSIONS_BY_FILE)) {
info(`${isStubsUpdate ? "Update" : "Check"} ${stubFile}`);
const generatedStubs = await generateStubs(commands, stubFile);
if (isStubsUpdate) {
await writeStubsToFile(env, stubFile, generatedStubs);
ok(true, `${stubFile} was updated`);
continue;
}
const existingStubs = getStubFile(stubFile);
if (generatedStubs.size !== existingStubs.size) {
failed = true;
continue;
}
for (const [key, packet] of generatedStubs) {
const packetStr = getSerializedPacket(packet, { sortKeys: true });
const grip = getSerializedPacket(existingStubs.get(key), {
sortKeys: true,
});
is(packetStr, grip, `"${key}" packet has expected value`);
failed = failed || packetStr !== grip;
}
}
if (failed) {
ok(
false,
"The reps stubs need to be updated by running `" +
`mach test ${getCurrentTestFilePath()} --headless --setenv STUBS_UPDATE=true` +
"`"
);
} else {
ok(true, "Stubs are up to date");
}
await removeTab(tab);
});
async function generateStubs(commands, stubFile) {
const stubs = new Map();
for (const [key, expression] of EXPRESSIONS_BY_FILE[stubFile]) {
const { result } = await commands.scriptCommand.execute(expression);
stubs.set(key, getCleanedPacket(key, result));
}
return stubs;
}
function getCleanedPacket(key, packet) {
// TODO: Remove / normalize any data that is not stable
return packet;
}
// HELPER
const CHROME_PREFIX = "chrome://mochitests/content/browser/";
const STUBS_FOLDER = "devtools/client/shared/components/test/node/stubs/reps/";
const STUBS_UPDATE_ENV = "STUBS_UPDATE";
/**
* Write stubs to a given file
*
* @param {Object} env
* @param {String} fileName: The file to write the stubs in.
* @param {Map} packets: A Map of the packets.
*/
async function writeStubsToFile(env, fileName, packets) {
const mozRepo = env.get("MOZ_DEVELOPER_REPO_DIR");
const filePath = `${mozRepo}/${STUBS_FOLDER + fileName}`;
const stubs = Array.from(packets.entries()).map(([key, packet]) => {
const stringifiedPacket = getSerializedPacket(packet);
return `stubs.set(\`${key}\`, ${stringifiedPacket});`;
});
const fileContent = `/* 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/>. */
"use strict";
/*
* THIS FILE IS AUTOGENERATED. DO NOT MODIFY BY HAND. RUN browser_reps_stubs.js with STUBS_UPDATE=true env TO UPDATE.
*/
const stubs = new Map();
${stubs.join("\n\n")}
module.exports = stubs;
`;
const textEncoder = new TextEncoder();
await IOUtils.write(filePath, textEncoder.encode(fileContent));
}
function getStubFile(fileName) {
return require(CHROME_PREFIX + STUBS_FOLDER + fileName);
}
function sortObjectKeys(obj) {
const isArray = Array.isArray(obj);
const isObject = Object.prototype.toString.call(obj) === "[object Object]";
const isFront = obj?._grip;
if (isObject && !isFront) {
// Reorder keys for objects, but skip fronts to avoid infinite recursion.
const sortedKeys = Object.keys(obj).sort((k1, k2) => k1.localeCompare(k2));
const withSortedKeys = {};
sortedKeys.forEach(k => {
withSortedKeys[k] = k !== "stacktrace" ? sortObjectKeys(obj[k]) : obj[k];
});
return withSortedKeys;
} else if (isArray) {
return obj.map(item => sortObjectKeys(item));
}
return obj;
}
/**
* @param {Object} packet
* The packet to serialize.
* @param {Object}
* - {Boolean} sortKeys: pass true to sort all keys alphabetically in the
* packet before serialization. For instance stub comparison should not
* fail if the order of properties changed.
*/
function getSerializedPacket(packet, { sortKeys = false } = {}) {
if (sortKeys) {
packet = sortObjectKeys(packet);
}
return JSON.stringify(
packet,
function(_, value) {
// The message can have fronts that we need to serialize
if (value && value._grip) {
return { _grip: value._grip, actorID: value.actorID };
}
return value;
},
2
);
}

Просмотреть файл

@ -67,7 +67,7 @@ describe("Boolean", () => {
describe("Negative Zero", () => {
const stubNegativeZeroGrip = stubs.get("NegZeroGrip");
const stubNegativeZeroValue = stubs.get("NegZeroValue");
const stubNegativeZeroValue = -0;
it("correctly selects Number Rep for negative zero grip", () => {
expect(getRep(stubNegativeZeroGrip)).toBe(Number.rep);
@ -121,12 +121,11 @@ describe("Zero", () => {
});
describe("Unsafe Int", () => {
const stub = stubs.get("UnsafeInt");
it("renders with expected test content for a long number", () => {
const renderedComponent = shallow(
Rep({
object: stub,
// eslint-disable-next-line no-loss-of-precision
object: 900719925474099122,
shouldRenderTooltip: true,
})
);

Просмотреть файл

@ -0,0 +1,11 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
// This file is a fake test so we can have support files in the stubs.ini, which are then
// referenced as support files in the webconsole mochitest ini file.
"use strict";
add_task(function() {
ok(true, "this is not a test");
});

Просмотреть файл

@ -3,12 +3,16 @@
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
"use strict";
/*
* THIS FILE IS AUTOGENERATED. DO NOT MODIFY BY HAND. RUN browser_reps_stubs.js with STUBS_UPDATE=true env TO UPDATE.
*/
const stubs = new Map();
stubs.set("Infinity", {
stubs.set(`Infinity`, {
type: "Infinity",
});
stubs.set("NegativeInfinity", {
stubs.set(`NegativeInfinity`, {
type: "-Infinity",
});

Просмотреть файл

@ -3,9 +3,12 @@
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
"use strict";
/*
* THIS FILE IS AUTOGENERATED. DO NOT MODIFY BY HAND. RUN browser_reps_stubs.js with STUBS_UPDATE=true env TO UPDATE.
*/
const stubs = new Map();
stubs.set("NaN", {
stubs.set(`NaN`, {
type: "NaN",
});

Просмотреть файл

@ -3,9 +3,12 @@
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
"use strict";
/*
* THIS FILE IS AUTOGENERATED. DO NOT MODIFY BY HAND. RUN browser_reps_stubs.js with STUBS_UPDATE=true env TO UPDATE.
*/
const stubs = new Map();
stubs.set("Null", {
stubs.set(`Null`, {
type: "null",
});

Просмотреть файл

@ -3,16 +3,19 @@
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
"use strict";
/*
* THIS FILE IS AUTOGENERATED. DO NOT MODIFY BY HAND. RUN browser_reps_stubs.js with STUBS_UPDATE=true env TO UPDATE.
*/
const stubs = new Map();
stubs.set("Int", 5);
stubs.set("True", true);
stubs.set("False", false);
stubs.set("NegZeroValue", -0);
stubs.set("NegZeroGrip", {
stubs.set(`Int`, 5);
stubs.set(`True`, true);
stubs.set(`False`, false);
stubs.set(`NegZeroGrip`, {
type: "-0",
});
// eslint-disable-next-line no-loss-of-precision
stubs.set("UnsafeInt", 900719925474099122);
module.exports = stubs;

Просмотреть файл

@ -0,0 +1,12 @@
[DEFAULT]
tags = devtools
subsuite = devtools
support-files =
infinity.js
nan.js
null.js
number.js
undefined.js
[browser_dummy.js]
skip-if=true #This is only here so we can expose the support files in other ini files.

Просмотреть файл

@ -3,9 +3,12 @@
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
"use strict";
/*
* THIS FILE IS AUTOGENERATED. DO NOT MODIFY BY HAND. RUN browser_reps_stubs.js with STUBS_UPDATE=true env TO UPDATE.
*/
const stubs = new Map();
stubs.set("Undefined", {
stubs.set(`Undefined`, {
type: "undefined",
});