Backed out changeset 2450366cf7ca (bug 1891629) for causing win msix mochitest failures
[gecko.git] / intl / l10n / test / test_l10nregistry_fuzzed.js
blobf99c3b61e5dca64bb47f95b0617520ae03371120
1 /* Any copyright is dedicated to the Public Domain.
2    http://creativecommons.org/publicdomain/zero/1.0/ */
4 /**
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.
8  */
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};`);
15 /**
16  * A simple non-robust psuedo-random number generator.
17  *
18  * It is implemented using a Lehmer random number generator.
19  * https://en.wikipedia.org/wiki/16,807
20  *
21  * @returns {number} Ranged [0, 1)
22  */
23 function prng() {
24   const multiplier = 16807;
25   const prime = 2147483647;
26   seed = seed * multiplier % prime
27   return (seed - 1) / prime
30 /**
31  * Generate a name like "mock-dmsxfodrqboljmxdeayt".
32  * @returns {string}
33  */
34 function generateRandomName() {
35   let name = 'mock-'
36   const letters = "abcdefghijklmnopqrstuvwxyz";
37   for (let i = 0; i < 20; i++) {
38     name += letters[Math.floor(prng() * letters.length)];
39   }
40   return name;
43 /**
44  * Picks one item from an array.
45  *
46  * @param {Array<T>}
47  * @returns {T}
48  */
49 function pickOne(list) {
50   return list[Math.floor(prng() * list.length)]
53 /**
54  * Picks a random subset from an array.
55  *
56  * @param {Array<T>}
57  * @returns {Array<T>}
58  */
59 function pickN(list, count) {
60   list = list.slice();
61   const result = [];
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];
69     list[index] = b;
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());
74   }
76   return result
79 /**
80  * Generate a random number
81  * @param {number} min
82  * @param {number} max
83  * @returns {number}
84  */
85 function random(min, max) {
86   const delta = max - min;
87   return min + delta * prng();
90 /**
91  * Generate a random number generator with a distribution more towards the lower end.
92  * @param {number} min
93  * @param {number} max
94  * @returns {number}
95  */
96 function randomPow(min, max) {
97   const delta = max - min;
98   const r = prng()
99   return min + delta * r * r;
102 add_task(async function test_fuzzing_sources() {
103     const iterations = 100;
104     const maxSources = 10;
105   
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);
116       const mocks = [];
117       const fs = [];
119       const locales = new Set();
120       const filenames = new Set();
122       for (let j = 0; j < sourceCount; j++) {
123         const locale = pickOne(availableLocales);
124         locales.add(locale);
126         let metasource = pickOne(metasources);
127         if (metasource === "langpack") {
128           metasource = `${metasource}-${locale}`
129         }
130   
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 });
142         mocks.push([
143           name, // name
144           metasource, // metasource,
145           [locale], // locales,
146           dir + "/{locale}/",
147           fs
148         ])
149       }
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(
158         bundleLocales,
159         bundleFilenames
160       );
162       function next() {
163         console.log("Getting next bundle");
164         const bundle = bundles.next()
165         console.log("Next bundle obtained", bundle);
166         return bundle;
167       }
169       const ops = [
170         // Increase the frequency of next being called.
171         next,
172         next,
173         next,
174         () => {
175           const newMocks = [];
176           for (const mock of pickN(mocks, random(0, 3))) {
177             const newMock = mock.slice();
178             newMocks.push(newMock)
179           }
180           console.log("l10nReg.updateSources");
181           l10nReg.updateSources(newMocks.map(mock => L10nFileSource.createMock(...mock)));
182         },
183         () => {
184           console.log("l10nReg.clearSources");
185           l10nReg.clearSources();
186         }
187       ];
189       console.log("Start the operation loop");
190       while (true) {
191         console.log("Next operation");
192         const op = pickOne(ops);
193         const result = await op();
194         if (result?.done) {
195           // The iterator completed.
196           break;
197         }
198       }
200       console.log("Clear sources");
201       l10nReg.clearSources();
202     }
204     ok(true, "The L10nRegistry fuzzing did not crash.")