index.js 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. //-------------------------------------------------------------------------------------------------------
  2. // Copyright (C) Microsoft. All rights reserved.
  3. // Copyright (c) 2021 ChakraCore Project Contributors. All rights reserved.
  4. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
  5. //-------------------------------------------------------------------------------------------------------
  6. /* eslint-env node */
  7. const path = require("path");
  8. const fs = require("fs-extra");
  9. const Bluebird = require("bluebird");
  10. const {spawn} = require("child_process");
  11. const slash = require("slash");
  12. const json5 = require("json5");
  13. const walk = require("klaw");
  14. Bluebird.promisifyAll(fs);
  15. const config = json5.parse(fs.readFileSync(path.join(__dirname, "config.json5")));
  16. const rlRoot = path.resolve(__dirname, "..");
  17. const folders = config.folders.map(folder => path.resolve(rlRoot, folder));
  18. const baselineDir = path.join(rlRoot, "baselines");
  19. const argv = require("yargs")
  20. .help()
  21. .alias("help", "h")
  22. .options({
  23. rebase: {
  24. string: true,
  25. description: "Path to host to run the test create/update the baselines"
  26. }
  27. })
  28. .argv;
  29. // Make sure all arguments are valid
  30. for (const folder of folders) {
  31. if (!fs.statSync(folder).isDirectory()) {
  32. throw new Error(`${folder} is not a folder`);
  33. }
  34. }
  35. function hostFlags(specFile) {
  36. return `-wasm -args ${slash(specFile.relative)} -endargs`;
  37. }
  38. function getBaselinePath(specFile) {
  39. return `${slash(path.relative(rlRoot, path.join(baselineDir, specFile.relative.replace(specFile.ext, ""))))}.baseline`;
  40. }
  41. function removePossiblyEmptyFolder(folder) {
  42. return new Promise((resolve, reject) => {
  43. fs.remove(folder, err => {
  44. // ENOENT is the error code if the folder is missing
  45. if (err && err.code !== "ENOENT") {
  46. return reject(err);
  47. }
  48. resolve();
  49. });
  50. });
  51. }
  52. async function generateChakraTests() {
  53. const chakraTestsDestination = path.join(rlRoot, "chakra_generated");
  54. const chakraTests = require("./generateTests");
  55. await removePossiblyEmptyFolder(chakraTestsDestination);
  56. await fs.ensureDirAsync(chakraTestsDestination);
  57. await fs.writeFileAsync(path.join(chakraTestsDestination, ".keep"), "Committing an empty directory on the Windows side to work around a GVFS issue\n");
  58. for (const test of chakraTests) {
  59. const content = await test.getContent(rlRoot);
  60. if (!content) {
  61. return;
  62. }
  63. const testPath = path.join(chakraTestsDestination, `${test.name}.wast`);
  64. const copyrightHeader =
  65. `;;-------------------------------------------------------------------------------------------------------
  66. ;; Copyright (C) Microsoft. All rights reserved.
  67. ;; Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
  68. ;;-------------------------------------------------------------------------------------------------------
  69. `;
  70. await fs.writeFileAsync(testPath, `${copyrightHeader};;AUTO-GENERATED do not modify\n${content}`);
  71. }
  72. }
  73. function main() {
  74. let runs;
  75. return generateChakraTests(
  76. // Walk all the folders to find test files
  77. ).then(() => Bluebird.reduce(folders, (specFiles, folder) => new Promise((resolve) => {
  78. walk(folder)
  79. .on("data", item => {
  80. const ext = path.extname(item.path);
  81. const relative = path.relative(rlRoot, item.path);
  82. if ((
  83. (ext === ".wast" && item.path.indexOf(".fail") === -1) ||
  84. (ext === ".js")
  85. ) &&
  86. !config.excludes.includes(slash(relative))
  87. ) {
  88. specFiles.push({
  89. path: item.path,
  90. ext,
  91. dirname: path.dirname(item.path),
  92. relative
  93. });
  94. }
  95. })
  96. .on("end", () => {
  97. resolve(specFiles);
  98. });
  99. }), [])
  100. ).then(specFiles => {
  101. const runners = {
  102. ".wast": "spec.js",
  103. ".js": "jsapi.js",
  104. };
  105. runs = specFiles.map(specFile => {
  106. const {
  107. ext,
  108. relative
  109. } = specFile;
  110. const dirname = path.dirname(specFile.path);
  111. const useFeature = (allowRequired, feature) => !(allowRequired ^ feature.required) && (
  112. (feature.files || []).includes(slash(relative)) ||
  113. (feature.folders || []).map(folder => path.join(rlRoot, folder)).includes(dirname)
  114. );
  115. const isXplatExcluded = config["xplat-excludes"].indexOf(slash(relative)) !== -1;
  116. const baseline = getBaselinePath(specFile);
  117. const flags = hostFlags(specFile);
  118. const runner = runners[ext];
  119. const requiredFeature = config.features
  120. // Use first required feature found
  121. .filter(useFeature.bind(null, true))[0] ||
  122. // Or use default
  123. {rltags: [], flags: []};
  124. const tests = [{
  125. runner,
  126. tags: [].concat(requiredFeature.rltags || []),
  127. baseline,
  128. flags: [flags].concat(requiredFeature.flags || [])
  129. }, {
  130. runner,
  131. tags: ["exclude_dynapogo"].concat(requiredFeature.rltags || []),
  132. baseline,
  133. flags: [flags, "-nonative"].concat(requiredFeature.flags || [])
  134. }].concat(config.features
  135. .filter(useFeature.bind(null, false))
  136. .map(feature => ({
  137. runner,
  138. baseline,
  139. tags: [].concat(feature.rltags || []),
  140. flags: [flags].concat(feature.flags || [])
  141. }))
  142. );
  143. if (isXplatExcluded) {
  144. for (const test of tests) test.tags.push("exclude_xplat");
  145. }
  146. return tests;
  147. });
  148. const rlexe = (
  149. `<?xml version="1.0" encoding="utf-8"?>
  150. <!-- Auto Generated by convert-test-suite -->
  151. <regress-exe>${
  152. runs.map(run => run.map(test => `
  153. <test>
  154. <default>
  155. <files>${test.runner}</files>
  156. <baseline>${test.baseline}</baseline>
  157. <compile-flags>${test.flags.join(" ")}</compile-flags>${test.tags.length > 0 ? `
  158. <tags>${test.tags.join(",")}</tags>` : ""}
  159. </default>
  160. </test>`).join("")).join("")
  161. }
  162. </regress-exe>
  163. `);
  164. return fs.writeFileAsync(path.join(rlRoot, "rlexe.xml"), rlexe);
  165. }).then(() => {
  166. if (!argv.rebase) {
  167. return;
  168. }
  169. fs.removeSync(baselineDir);
  170. fs.ensureDirSync(baselineDir);
  171. const startRuns = runs.map(run => () => new Bluebird((resolve, reject) => {
  172. const test = run[0];
  173. fs.ensureDirSync(path.dirname(test.baseline));
  174. const baseline = fs.createWriteStream(test.baseline);
  175. baseline.on("open", () => {
  176. const args = [path.resolve(rlRoot, test.runner)].concat(test.flags);
  177. console.log(argv.rebase, args.join(" "));
  178. const engine = spawn(
  179. argv.rebase,
  180. args,
  181. {
  182. cwd: rlRoot,
  183. stdio: [baseline, baseline, baseline],
  184. shell: true
  185. }
  186. );
  187. engine.on("error", reject);
  188. engine.on("close", resolve);
  189. });
  190. baseline.on("error", reject);
  191. }));
  192. const cucurrentRuns = 8;
  193. let running = startRuns.splice(0, cucurrentRuns).map(start => start());
  194. return new Promise((resolve, reject) => {
  195. const checkIfContinue = () => {
  196. // Keep only runs that are not done
  197. running = running.filter(run => !run.isFulfilled());
  198. // If we have nothing left to run, terminate
  199. if (running.length === 0 && startRuns.length === 0) {
  200. return resolve();
  201. }
  202. running.push(...(startRuns.splice(0, cucurrentRuns - running.length).map(start => start())));
  203. Bluebird.any(running).then(checkIfContinue).catch(reject);
  204. };
  205. checkIfContinue();
  206. });
  207. });
  208. }
  209. main().then(() => console.log("done"), err => console.error(err));