1 /* Any copyright is dedicated to the Public Domain.
2 http://creativecommons.org/publicdomain/zero/1.0/ */
5 * This test is a fuzzing test for the L10nRegistry API. It was written to find
6 * a hard to reproduce bug in the L10nRegistry code. If it fails, place the seed
7 * from the failing run in the code directly below to make it consistently reproducible.
9 let seed = Math.floor(Math.random() * 1e9);
11 console.log(`Starting a fuzzing run with seed: ${seed}.`);
12 console.log("To reproduce this test locally, re-run it locally with:");
13 console.log(`let seed = ${seed};`);
16 * A simple non-robust psuedo-random number generator.
18 * It is implemented using a Lehmer random number generator.
19 * https://en.wikipedia.org/wiki/16,807
21 * @returns {number} Ranged [0, 1)
24 const multiplier = 16807;
25 const prime = 2147483647;
26 seed = seed * multiplier % prime
27 return (seed - 1) / prime
31 * Generate a name like "mock-dmsxfodrqboljmxdeayt".
34 function generateRandomName() {
36 const letters = "abcdefghijklmnopqrstuvwxyz";
37 for (let i = 0; i < 20; i++) {
38 name += letters[Math.floor(prng() * letters.length)];
44 * Picks one item from an array.
49 function pickOne(list) {
50 return list[Math.floor(prng() * list.length)]
54 * Picks a random subset from an array.
59 function pickN(list, count) {
62 for (let i = 0; i < count && i < list.length; i++) {
63 // Pick a random item.
64 const index = Math.floor(prng() * list.length);
66 // Swap item to the end.
67 const a = list[index];
68 const b = list[list.length - 1];
70 list[list.length - 1] = a
72 // Now that the random item is on the end, pop it off and add it to the results.
73 result.push(list.pop());
80 * Generate a random number
85 function random(min, max) {
86 const delta = max - min;
87 return min + delta * prng();
91 * Generate a random number generator with a distribution more towards the lower end.
96 function randomPow(min, max) {
97 const delta = max - min;
99 return min + delta * r * r;
102 add_task(async function test_fuzzing_sources() {
103 const iterations = 100;
104 const maxSources = 10;
106 const metasources = ["app", "langpack", ""];
107 const availableLocales = ["en", "en-US", "pl", "en-CA", "es-AR", "es-ES"];
109 const l10nReg = new L10nRegistry();
111 for (let i = 0; i < iterations; i++) {
112 console.log("----------------------------------------------------------------------");
113 console.log("Iteration", i);
114 let sourceCount = randomPow(0, maxSources);
119 const locales = new Set();
120 const filenames = new Set();
122 for (let j = 0; j < sourceCount; j++) {
123 const locale = pickOne(availableLocales);
126 let metasource = pickOne(metasources);
127 if (metasource === "langpack") {
128 metasource = `${metasource}-${locale}`
131 const dir = generateRandomName();
132 const filename = generateRandomName() + j + ".ftl";
133 const path = `${dir}/${locale}/${filename}`
134 const name = metasource || "app";
135 const source = "key = value";
137 filenames.add(filename);
139 console.log("Add source", { name, metasource, path, source });
140 fs.push({ path, source });
144 metasource, // metasource,
145 [locale], // locales,
151 l10nReg.registerSources(mocks.map(args => L10nFileSource.createMock(...args)));
153 const bundleLocales = pickN([...locales], random(1, 4));
154 const bundleFilenames = pickN([...filenames], random(1, 10));
156 console.log("generateBundles", {bundleLocales, bundleFilenames});
157 const bundles = l10nReg.generateBundles(
163 console.log("Getting next bundle");
164 const bundle = bundles.next()
165 console.log("Next bundle obtained", bundle);
170 // Increase the frequency of next being called.
176 for (const mock of pickN(mocks, random(0, 3))) {
177 const newMock = mock.slice();
178 newMocks.push(newMock)
180 console.log("l10nReg.updateSources");
181 l10nReg.updateSources(newMocks.map(mock => L10nFileSource.createMock(...mock)));
184 console.log("l10nReg.clearSources");
185 l10nReg.clearSources();
189 console.log("Start the operation loop");
191 console.log("Next operation");
192 const op = pickOne(ops);
193 const result = await op();
195 // The iterator completed.
200 console.log("Clear sources");
201 l10nReg.clearSources();
204 ok(true, "The L10nRegistry fuzzing did not crash.")