зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1773733 - Add a fuzzing test for the l10nregistry; r=nordzilla
Depends on D159446 Differential Revision: https://phabricator.services.mozilla.com/D159714
This commit is contained in:
Родитель
f0ed9daad0
Коммит
caa67f36e3
|
@ -0,0 +1,205 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* This test is a fuzzing test for the L10nRegistry API. It was written to find
|
||||
* a hard to reproduce bug in the L10nRegistry code. If it fails, place the seed
|
||||
* from the failing run in the code directly below to make it consistently reproducible.
|
||||
*/
|
||||
let seed = Math.floor(Math.random() * 1e9);
|
||||
|
||||
console.log(`Starting a fuzzing run with seed: ${seed}.`);
|
||||
console.log("To reproduce this test locally, re-run it locally with:");
|
||||
console.log(`let seed = ${seed};`);
|
||||
|
||||
/**
|
||||
* A simple non-robust psuedo-random number generator.
|
||||
*
|
||||
* It is implemented using a Lehmer random number generator.
|
||||
* https://en.wikipedia.org/wiki/16,807
|
||||
*
|
||||
* @returns {number} Ranged [0, 1)
|
||||
*/
|
||||
function prng() {
|
||||
const multiplier = 16807;
|
||||
const prime = 2147483647;
|
||||
seed = seed * multiplier % prime
|
||||
return (seed - 1) / prime
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a name like "mock-dmsxfodrqboljmxdeayt".
|
||||
* @returns {string}
|
||||
*/
|
||||
function generateRandomName() {
|
||||
let name = 'mock-'
|
||||
const letters = "abcdefghijklmnopqrstuvwxyz";
|
||||
for (let i = 0; i < 20; i++) {
|
||||
name += letters[Math.floor(prng() * letters.length)];
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Picks one item from an array.
|
||||
*
|
||||
* @param {Array<T>}
|
||||
* @returns {T}
|
||||
*/
|
||||
function pickOne(list) {
|
||||
return list[Math.floor(prng() * list.length)]
|
||||
}
|
||||
|
||||
/**
|
||||
* Picks a random subset from an array.
|
||||
*
|
||||
* @param {Array<T>}
|
||||
* @returns {Array<T>}
|
||||
*/
|
||||
function pickN(list, count) {
|
||||
list = list.slice();
|
||||
const result = [];
|
||||
for (let i = 0; i < count && i < list.length; i++) {
|
||||
// Pick a random item.
|
||||
const index = Math.floor(prng() * list.length);
|
||||
|
||||
// Swap item to the end.
|
||||
const a = list[index];
|
||||
const b = list[list.length - 1];
|
||||
list[index] = b;
|
||||
list[list.length - 1] = a
|
||||
|
||||
// Now that the random item is on the end, pop it off and add it to the results.
|
||||
result.push(list.pop());
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a random number
|
||||
* @param {number} min
|
||||
* @param {number} max
|
||||
* @returns {number}
|
||||
*/
|
||||
function random(min, max) {
|
||||
const delta = max - min;
|
||||
return min + delta * prng();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a random number generator with a distribution more towards the lower end.
|
||||
* @param {number} min
|
||||
* @param {number} max
|
||||
* @returns {number}
|
||||
*/
|
||||
function randomPow(min, max) {
|
||||
const delta = max - min;
|
||||
const r = prng()
|
||||
return min + delta * r * r;
|
||||
}
|
||||
|
||||
add_task(async function test_fuzzing_sources() {
|
||||
const iterations = 100;
|
||||
const maxSources = 10;
|
||||
|
||||
const metasources = ["app", "langpack", ""];
|
||||
const availableLocales = ["en", "en-US", "pl", "en-CA", "es-AR", "es-ES"];
|
||||
|
||||
const l10nReg = new L10nRegistry();
|
||||
|
||||
for (let i = 0; i < iterations; i++) {
|
||||
console.log("----------------------------------------------------------------------");
|
||||
console.log("Iteration", i);
|
||||
let sourceCount = randomPow(0, maxSources);
|
||||
|
||||
const mocks = [];
|
||||
const fs = [];
|
||||
|
||||
const locales = new Set();
|
||||
const filenames = new Set();
|
||||
|
||||
for (let j = 0; j < sourceCount; j++) {
|
||||
const locale = pickOne(availableLocales);
|
||||
locales.add(locale);
|
||||
|
||||
let metasource = pickOne(metasources);
|
||||
if (metasource === "langpack") {
|
||||
metasource = `${metasource}-${locale}`
|
||||
}
|
||||
|
||||
const dir = generateRandomName();
|
||||
const filename = generateRandomName() + j + ".ftl";
|
||||
const path = `${dir}/${locale}/${filename}`
|
||||
const name = metasource || "app";
|
||||
const source = "key = value";
|
||||
|
||||
filenames.add(filename);
|
||||
|
||||
console.log("Add source", { name, metasource, path, source });
|
||||
fs.push({ path, source });
|
||||
|
||||
mocks.push([
|
||||
name, // name
|
||||
metasource, // metasource,
|
||||
[locale], // locales,
|
||||
dir + "/{locale}/",
|
||||
fs
|
||||
])
|
||||
}
|
||||
|
||||
l10nReg.registerSources(mocks.map(args => L10nFileSource.createMock(...args)));
|
||||
|
||||
const bundleLocales = pickN([...locales], random(1, 4));
|
||||
const bundleFilenames = pickN([...filenames], random(1, 10));
|
||||
|
||||
console.log("generateBundles", {bundleLocales, bundleFilenames});
|
||||
const bundles = l10nReg.generateBundles(
|
||||
bundleLocales,
|
||||
bundleFilenames
|
||||
);
|
||||
|
||||
function next() {
|
||||
console.log("Getting next bundle");
|
||||
const bundle = bundles.next()
|
||||
console.log("Next bundle obtained", bundle);
|
||||
return bundle;
|
||||
}
|
||||
|
||||
const ops = [
|
||||
// Increase the frequency of next being called.
|
||||
next,
|
||||
next,
|
||||
next,
|
||||
() => {
|
||||
const newMocks = [];
|
||||
for (const mock of pickN(mocks, random(0, 3))) {
|
||||
const newMock = mock.slice();
|
||||
newMocks.push(newMock)
|
||||
}
|
||||
console.log("l10nReg.updateSources");
|
||||
l10nReg.updateSources(newMocks.map(mock => L10nFileSource.createMock(...mock)));
|
||||
},
|
||||
() => {
|
||||
console.log("l10nReg.clearSources");
|
||||
l10nReg.clearSources();
|
||||
}
|
||||
];
|
||||
|
||||
console.log("Start the operation loop");
|
||||
while (true) {
|
||||
console.log("Next operation");
|
||||
const op = pickOne(ops);
|
||||
const result = await op();
|
||||
if (result?.done) {
|
||||
// The iterator completed.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
console.log("Clear sources");
|
||||
l10nReg.clearSources();
|
||||
}
|
||||
|
||||
ok(true, "The L10nRegistry fuzzing did not crash.")
|
||||
});
|
|
@ -3,6 +3,7 @@ head =
|
|||
|
||||
[test_datetimeformat.js]
|
||||
[test_l10nregistry.js]
|
||||
[test_l10nregistry_fuzzed.js]
|
||||
[test_l10nregistry_sync.js]
|
||||
[test_localization.js]
|
||||
[test_localization_sync.js]
|
||||
|
|
Загрузка…
Ссылка в новой задаче