Improve large-scale test result reporting
This commit is contained in:
		
							parent
							
								
									2a915e2654
								
							
						
					
					
						commit
						510cc1e0c0
					
				| 
						 | 
				
			
			@ -3,3 +3,4 @@
 | 
			
		|||
.lsp-repl-history
 | 
			
		||||
node_modules
 | 
			
		||||
out
 | 
			
		||||
tests
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,12 +1,16 @@
 | 
			
		|||
import * as path from "path";
 | 
			
		||||
import * as fs from "fs";
 | 
			
		||||
import * as process from "process";
 | 
			
		||||
import { promisify } from "util";
 | 
			
		||||
 | 
			
		||||
import PQueue from "p-queue";
 | 
			
		||||
import * as rimraf from "rimraf";
 | 
			
		||||
import { v4 as getUUID } from "uuid";
 | 
			
		||||
 | 
			
		||||
import * as api from "./api";
 | 
			
		||||
import { LangConfig, langs } from "./langs";
 | 
			
		||||
 | 
			
		||||
const TIMEOUT_MS = 3000;
 | 
			
		||||
const CONCURRENCY = 16;
 | 
			
		||||
 | 
			
		||||
function findPosition(str: string, idx: number) {
 | 
			
		||||
  const lines = str.substring(0, idx).split("\n");
 | 
			
		||||
| 
						 | 
				
			
			@ -40,8 +44,11 @@ class Test {
 | 
			
		|||
    this.type = type;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getLog = () => {
 | 
			
		||||
    return this.messages.map((msg: any) => JSON.stringify(msg)).join("\n");
 | 
			
		||||
  getLog = (opts?: any) => {
 | 
			
		||||
    opts = opts || {};
 | 
			
		||||
    return this.messages
 | 
			
		||||
      .map((msg: any) => JSON.stringify(msg, null, opts.pretty && 2))
 | 
			
		||||
      .join("\n");
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  run = async () => {
 | 
			
		||||
| 
						 | 
				
			
			@ -508,6 +515,14 @@ function lint(lang: string) {
 | 
			
		|||
  if (!config.template.endsWith("\n")) {
 | 
			
		||||
    throw new Error("template is missing a trailing newline");
 | 
			
		||||
  }
 | 
			
		||||
  // These can be removed when the types are adjusted to make these
 | 
			
		||||
  // situations impossible.
 | 
			
		||||
  if (config.format && !config.format.input) {
 | 
			
		||||
    throw new Error("formatter is missing test");
 | 
			
		||||
  }
 | 
			
		||||
  if (config.lsp && !(config.lsp.code && config.lsp.item)) {
 | 
			
		||||
    throw new Error("LSP is missing test");
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const testTypes: {
 | 
			
		||||
| 
						 | 
				
			
			@ -546,6 +561,11 @@ function getTestList() {
 | 
			
		|||
  return tests;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function writeLog(lang: string, type: string, log: string) {
 | 
			
		||||
  await promisify(fs.mkdir)(`tests/${lang}`, { recursive: true });
 | 
			
		||||
  await promisify(fs.writeFile)(`tests/${lang}/${type}.log`, log);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function main() {
 | 
			
		||||
  let tests = getTestList();
 | 
			
		||||
  const args = process.argv.slice(2);
 | 
			
		||||
| 
						 | 
				
			
			@ -564,42 +584,68 @@ async function main() {
 | 
			
		|||
    process.exit(1);
 | 
			
		||||
  }
 | 
			
		||||
  const lintSeen = new Set();
 | 
			
		||||
  let lintPassed = 0;
 | 
			
		||||
  let lintFailed = 0;
 | 
			
		||||
  let lintPassed = new Set();
 | 
			
		||||
  let lintFailed = new Map();
 | 
			
		||||
  for (const { lang } of tests) {
 | 
			
		||||
    if (!lintSeen.has(lang)) {
 | 
			
		||||
      lintSeen.add(lang);
 | 
			
		||||
      console.error(`===== LANGUAGE ${lang}, LINT`);
 | 
			
		||||
      try {
 | 
			
		||||
        lint(lang);
 | 
			
		||||
        console.error("passed");
 | 
			
		||||
        lintPassed += 1;
 | 
			
		||||
        lintPassed.add(lang);
 | 
			
		||||
      } catch (err) {
 | 
			
		||||
        console.error("failed");
 | 
			
		||||
        console.error(err);
 | 
			
		||||
        lintFailed += 1;
 | 
			
		||||
        lintFailed.set(lang, err);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  if (lintFailed) {
 | 
			
		||||
  if (lintFailed.size > 0) {
 | 
			
		||||
    console.error(
 | 
			
		||||
      `Language${lintFailed.size !== 1 ? "s" : ""} failed linting:`
 | 
			
		||||
    );
 | 
			
		||||
    console.error(
 | 
			
		||||
      Array.from(lintFailed)
 | 
			
		||||
        .map(([lang, err]) => `  - ${lang} (${err})`)
 | 
			
		||||
        .join("\n")
 | 
			
		||||
    );
 | 
			
		||||
    process.exit(1);
 | 
			
		||||
  }
 | 
			
		||||
  await promisify(rimraf)("tests");
 | 
			
		||||
  const queue = new PQueue({ concurrency: CONCURRENCY });
 | 
			
		||||
  let passed = 0;
 | 
			
		||||
  let failed = 0;
 | 
			
		||||
  for (const { lang, test: type } of tests) {
 | 
			
		||||
    console.error(`===== LANGUAGE ${lang}, TEST ${type}`);
 | 
			
		||||
    const test = new Test(lang, type);
 | 
			
		||||
    try {
 | 
			
		||||
      await test.run();
 | 
			
		||||
      console.error("passed");
 | 
			
		||||
      passed += 1;
 | 
			
		||||
    } catch (err) {
 | 
			
		||||
      console.error("failed");
 | 
			
		||||
      console.error(test.getLog());
 | 
			
		||||
      console.error(err);
 | 
			
		||||
      failed += 1;
 | 
			
		||||
    }
 | 
			
		||||
    let test: Test;
 | 
			
		||||
    queue
 | 
			
		||||
      .add(() => {
 | 
			
		||||
        test = new Test(lang, type);
 | 
			
		||||
        return test.run();
 | 
			
		||||
      })
 | 
			
		||||
      .then(async () => {
 | 
			
		||||
        passed += 1;
 | 
			
		||||
        console.error(`PASSED: ${lang}/${type}`);
 | 
			
		||||
        await writeLog(
 | 
			
		||||
          lang,
 | 
			
		||||
          type,
 | 
			
		||||
          `PASSED: ${lang}/${type}\n` + test.getLog({ pretty: true }) + "\n"
 | 
			
		||||
        );
 | 
			
		||||
      })
 | 
			
		||||
      .catch(async (err) => {
 | 
			
		||||
        failed += 1;
 | 
			
		||||
        console.error(`FAILED: ${lang}/${type}`);
 | 
			
		||||
        console.error(test.getLog());
 | 
			
		||||
        console.error(err);
 | 
			
		||||
        await writeLog(
 | 
			
		||||
          lang,
 | 
			
		||||
          type,
 | 
			
		||||
          `FAILED: ${lang}/${type}\n` +
 | 
			
		||||
            test.getLog({ pretty: true }) +
 | 
			
		||||
            "\n" +
 | 
			
		||||
            err.stack +
 | 
			
		||||
            "\n"
 | 
			
		||||
        );
 | 
			
		||||
      })
 | 
			
		||||
      .catch(console.error);
 | 
			
		||||
  }
 | 
			
		||||
  await queue.onIdle();
 | 
			
		||||
  process.exit(failed ? 1 : 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -35,6 +35,7 @@
 | 
			
		|||
    "npm-run-all": "^4.1.5",
 | 
			
		||||
    "p-queue": "^6.6.0",
 | 
			
		||||
    "parse-passwd": "^1.0.0",
 | 
			
		||||
    "rimraf": "^3.0.2",
 | 
			
		||||
    "shell-quote": "^1.7.2",
 | 
			
		||||
    "style-loader": "^1.2.1",
 | 
			
		||||
    "ts-loader": "^7.0.5",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4189,6 +4189,13 @@ rimraf@^2.2.8, rimraf@^2.5.4, rimraf@^2.6.3:
 | 
			
		|||
  dependencies:
 | 
			
		||||
    glob "^7.1.3"
 | 
			
		||||
 | 
			
		||||
rimraf@^3.0.2:
 | 
			
		||||
  version "3.0.2"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a"
 | 
			
		||||
  integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    glob "^7.1.3"
 | 
			
		||||
 | 
			
		||||
ripemd160@^2.0.0, ripemd160@^2.0.1:
 | 
			
		||||
  version "2.0.2"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue