Port to vitest. Port everything to typescript. Remove chai, mocha and
chai-as-promised. Adds some docs.
This commit is contained in:
Matt Godbolt
2024-03-08 22:25:09 -06:00
committed by GitHub
parent 45b743e22b
commit fd3dd917f5
82 changed files with 4212 additions and 3862 deletions

View File

@@ -21,7 +21,6 @@ extends:
- plugin:@typescript-eslint/recommended - plugin:@typescript-eslint/recommended
- plugin:import/typescript - plugin:import/typescript
env: env:
mocha: true
node: true node: true
es6: true es6: true
rules: rules:

2
.idea/prettier.xml generated
View File

@@ -1,6 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="PrettierConfiguration"> <component name="PrettierConfiguration">
<option name="myConfigurationMode" value="AUTOMATIC" />
<option name="myRunOnSave" value="true" />
<option name="myRunOnReformat" value="true" /> <option name="myRunOnReformat" value="true" />
</component> </component>
</project> </project>

View File

@@ -1,15 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="All Mocha Tests" type="mocha-javascript-test-runner">
<node-interpreter>/opt/compiler-explorer/node/bin/node</node-interpreter>
<node-options>-r esm</node-options>
<mocha-package>$PROJECT_DIR$/node_modules/mocha</mocha-package>
<working-directory>$PROJECT_DIR$</working-directory>
<pass-parent-env>true</pass-parent-env>
<ui>bdd</ui>
<extra-mocha-options>--recursive</extra-mocha-options>
<test-kind>DIRECTORY</test-kind>
<test-directory>$PROJECT_DIR$/test</test-directory>
<recursive>false</recursive>
<method v="2" />
</configuration>
</component>

View File

@@ -1,2 +0,0 @@
recursive: true
ignore: test/filter-tests.js

View File

@@ -1 +0,0 @@
recursive: true

View File

@@ -1,6 +1,3 @@
// eslint-disable-next-line node/no-unpublished-import
import {it} from 'mocha';
import {assertNoConsoleOutput, stubConsoleOutput} from '../support/utils'; import {assertNoConsoleOutput, stubConsoleOutput} from '../support/utils';
const PANE_DATA_MAP = { const PANE_DATA_MAP = {
@@ -39,7 +36,8 @@ describe('Individual pane testing', () => {
}); });
}); });
afterEach('Ensure no output in console', () => { afterEach(() => {
// Ensure no output in console
return cy.window().then(win => { return cy.window().then(win => {
assertNoConsoleOutput(); assertNoConsoleOutput();
}); });
@@ -104,7 +102,7 @@ describe('Known good state test', () => {
); );
}); });
afterEach('Ensure no output in console', () => { afterEach(() => {
return cy.window().then(win => { return cy.window().then(win => {
assertNoConsoleOutput(); assertNoConsoleOutput();
}); });

View File

@@ -3,11 +3,10 @@
"compilerOptions": { "compilerOptions": {
/* Module resolution */ /* Module resolution */
"target": "esnext", "target": "esnext",
"module": "commonjs", "module": "es2015",
"moduleResolution": "classic", "moduleResolution": "bundler",
/* Code generation */ /* Code generation */
"typeRoots": ["../node_modules/@types"], "typeRoots": ["../node_modules/@types"],
"types": ["mocha", "chai", "chai-http"],
/* https://github.com/cypress-io/cypress/issues/26203#issuecomment-1571861397 */ /* https://github.com/cypress-io/cypress/issues/26203#issuecomment-1571861397 */
"sourceMap": false "sourceMap": false
} }

19
docs/VitestCribSheet.md Normal file
View File

@@ -0,0 +1,19 @@
## `vitest` crib sheet
We just moved to a new testing framework, `vitest`. Here are some notes to help you get started.
### Running tests
- `npm test` will run the tests
- `npm run test:watch` will run the watcher; which runs all the tests and then watches for changes and then runs on the
changed tests. This is much quicker as a lot of the work is cached.
- `npm run test:watch <path>` will do the same with just a path
### Writing tests
In general use `describe`, `it` and `expect` as you would with jest. Import these all from `vitest`. If you need async
things then use `await expect(someAsyncThing()).resolves.toEqual(someValue)`.
The `expect` is pretty rich and supports a lot of matchers, like `expect(x).toContain("moo")` or
`expect(y).not.toHaveProperty("badger")`. You can see the full list in the
[vitest documentation](https://vitest.dev/api/expect.html).

View File

@@ -83,7 +83,7 @@ export class PascalDemangler extends BaseDemangler {
]; ];
} }
protected shouldIgnoreSymbol(text: string) { public shouldIgnoreSymbol(text: string) {
for (const k in this.ignoredsymbols) { for (const k in this.ignoredsymbols) {
if (text.startsWith(this.ignoredsymbols[k])) { if (text.startsWith(this.ignoredsymbols[k])) {
return true; return true;
@@ -93,7 +93,7 @@ export class PascalDemangler extends BaseDemangler {
return false; return false;
} }
protected composeReadableMethodSignature(unitname, classname, methodname, params) { public composeReadableMethodSignature(unitname, classname, methodname, params) {
let signature = ''; let signature = '';
if (classname !== '') signature = classname.toLowerCase() + '.'; if (classname !== '') signature = classname.toLowerCase() + '.';
@@ -104,7 +104,7 @@ export class PascalDemangler extends BaseDemangler {
return signature; return signature;
} }
protected demangle(text) { public demangle(text) {
if (!text.endsWith(':')) return false; if (!text.endsWith(':')) return false;
if (this.shouldIgnoreSymbol(text)) return false; if (this.shouldIgnoreSymbol(text)) return false;
@@ -201,11 +201,11 @@ export class PascalDemangler extends BaseDemangler {
return unmangled; return unmangled;
} }
protected addDemangleToCache(text) { public addDemangleToCache(text) {
this.demangle(text); this.demangle(text);
} }
protected demangleIfNeeded(text) { public demangleIfNeeded(text) {
if (text.includes('$')) { if (text.includes('$')) {
if (this.shouldIgnoreSymbol(text)) { if (this.shouldIgnoreSymbol(text)) {
return text; return text;

View File

@@ -58,7 +58,7 @@ export class FormattingHandler {
const result = await exec.execute(exe, [versionArgument], {}); const result = await exec.execute(exe, [versionArgument], {});
const match = result.stdout.match(versionRegExp); const match = result.stdout.match(versionRegExp);
const formatterClass = getFormatterTypeByKey(type); const formatterClass = getFormatterTypeByKey(type);
const styleList = this.ceProps<string>(`formatter.${formatterName}.styles`); const styleList = this.ceProps<string>(`formatter.${formatterName}.styles`, '');
const styles = styleList === '' ? [] : styleList.split(':'); const styles = styleList === '' ? [] : styleList.split(':');
// If there is an explicit version, grab it. Otherwise try to filter the output // If there is an explicit version, grab it. Otherwise try to filter the output
const version = hasExplicitVersion const version = hasExplicitVersion

View File

@@ -169,7 +169,7 @@ export class AsmParser extends AsmRegex implements IAsmParser {
return inVLIWpacket; return inVLIWpacket;
} }
hasOpcode(line, inNvccCode, inVLIWpacket?) { hasOpcode(line, inNvccCode?, inVLIWpacket?) {
// Remove any leading label definition... // Remove any leading label definition...
const match = line.match(this.labelDef); const match = line.match(this.labelDef);
if (match) { if (match) {

1691
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -89,19 +89,15 @@
"yaml": "^2.4.0" "yaml": "^2.4.0"
}, },
"devDependencies": { "devDependencies": {
"@babel/preset-typescript": "^7.23.3",
"@smithy/util-stream": "^2.1.3", "@smithy/util-stream": "^2.1.3",
"@types/body-parser": "^1.19.5", "@types/body-parser": "^1.19.5",
"@types/bootstrap": "^5.2.10", "@types/bootstrap": "^5.2.10",
"@types/chai": "^4.3.12",
"@types/chai-as-promised": "^7.1.8",
"@types/express": "^4.17.21", "@types/express": "^4.17.21",
"@types/file-saver": "^2.0.7", "@types/file-saver": "^2.0.7",
"@types/fs-extra": "^11.0.4", "@types/fs-extra": "^11.0.4",
"@types/http-proxy": "^1.17.14", "@types/http-proxy": "^1.17.14",
"@types/jquery": "^3.5.29", "@types/jquery": "^3.5.29",
"@types/js-cookie": "^3.0.6", "@types/js-cookie": "^3.0.6",
"@types/mocha": "^10.0.6",
"@types/node-targz": "^0.2.4", "@types/node-targz": "^0.2.4",
"@types/nopt": "^3.0.32", "@types/nopt": "^3.0.32",
"@types/request": "^2.48.12", "@types/request": "^2.48.12",
@@ -111,12 +107,10 @@
"@types/webpack-env": "^1.18.4", "@types/webpack-env": "^1.18.4",
"@typescript-eslint/eslint-plugin": "^7.1.1", "@typescript-eslint/eslint-plugin": "^7.1.1",
"@typescript-eslint/parser": "^7.1.1", "@typescript-eslint/parser": "^7.1.1",
"@vitest/coverage-v8": "^1.3.1",
"approvals": "^6.2.4", "approvals": "^6.2.4",
"aws-sdk-client-mock": "^3.0.1", "aws-sdk-client-mock": "^3.0.1",
"c8": "^9.1.0", "c8": "^9.1.0",
"chai": "^4.4.1",
"chai-as-promised": "^7.1.1",
"chai-http": "^4.4.0",
"cheerio": "^1.0.0-rc.12", "cheerio": "^1.0.0-rc.12",
"css-loader": "^6.10.0", "css-loader": "^6.10.0",
"css-minimizer-webpack-plugin": "^6.0.0", "css-minimizer-webpack-plugin": "^6.0.0",
@@ -137,7 +131,6 @@
"file-loader": "^6.2.0", "file-loader": "^6.2.0",
"lint-staged": "^15.2.2", "lint-staged": "^15.2.2",
"mini-css-extract-plugin": "^2.8.1", "mini-css-extract-plugin": "^2.8.1",
"mocha": "^10.3.0",
"mock-fs": "^5.2.0", "mock-fs": "^5.2.0",
"monaco-editor-webpack-plugin": "^7.1.0", "monaco-editor-webpack-plugin": "^7.1.0",
"nock": "^13.5.4", "nock": "^13.5.4",
@@ -146,10 +139,12 @@
"sass-loader": "^14.1.1", "sass-loader": "^14.1.1",
"sinon": "^17.0.1", "sinon": "^17.0.1",
"source-map-loader": "^5.0.0", "source-map-loader": "^5.0.0",
"supertest": "^6.3.4",
"supervisor": "^0.12.0", "supervisor": "^0.12.0",
"terser-webpack-plugin": "^5.3.10", "terser-webpack-plugin": "^5.3.10",
"ts-loader": "^9.5.1", "ts-loader": "^9.5.1",
"typescript": "^5.3.3", "typescript": "^5.3.3",
"vitest": "^1.3.1",
"webpack": "^5.90.3", "webpack": "^5.90.3",
"webpack-cli": "^5.1.4", "webpack-cli": "^5.1.4",
"webpack-dev-middleware": "^7.0.0", "webpack-dev-middleware": "^7.0.0",
@@ -168,13 +163,14 @@
}, },
"scripts": { "scripts": {
"ci-lint": "eslint --format github .", "ci-lint": "eslint --format github .",
"ci-test": "c8 npm run test", "ci-test": "vitest run --coverage",
"cypress": "cypress run", "cypress": "cypress run",
"lint": "eslint --max-warnings=0 . --fix", "lint": "eslint --max-warnings=0 . --fix",
"lint-check": "eslint --max-warnings=0 . && prettier --check .", "lint-check": "eslint --max-warnings=0 . && prettier --check .",
"lint-files": "eslint --max-warnings=0", "lint-files": "eslint --max-warnings=0",
"test": "node --no-warnings=ExperimentalWarning --loader ts-node/esm ./node_modules/mocha/bin/mocha.js -b 'test/**/*.ts' 'test/**/*.js'", "test": "vitest run",
"test-min": "node --no-warnings=ExperimentalWarning --loader ts-node/esm ./node_modules/mocha/bin/mocha.js -b --config .mocharc-min.yml", "test:watch": "vitest",
"test-min": "vitest run --exclude test/filter-tests.ts",
"fix": "npm run lint && npm run format && npm run ts-check", "fix": "npm run lint && npm run format && npm run ts-check",
"check": "npm run ts-check && npm run lint-check && npm run test-min -- --reporter min", "check": "npm run ts-check && npm run lint-check && npm run test-min -- --reporter min",
"dev": "cross-env NODE_ENV=DEV node --no-warnings=ExperimentalWarning --loader ts-node/esm app.ts", "dev": "cross-env NODE_ENV=DEV node --no-warnings=ExperimentalWarning --loader ts-node/esm app.ts",

View File

@@ -22,13 +22,6 @@
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE. // POSSIBILITY OF SUCH DAMAGE.
// This file is not force-required: it must be loaded by mocha to get access
// to `before`.
import {suppressConsoleLog} from '../lib/logger.js'; import {suppressConsoleLog} from '../lib/logger.js';
if (typeof before === 'function') { suppressConsoleLog();
// this hook will run once before any tests are executed
before(() => {
suppressConsoleLog();
});
}

View File

@@ -1,36 +0,0 @@
// Copyright (c) 2020, Compiler Explorer Authors
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
// This file is force-required by mocha config. That ensure it's loaded always.
// It's force included so that even if you run a single test (e.g. from an IDE),
// this configuration is applied.
import chai from 'chai';
import chaiAsPromised from 'chai-as-promised';
import chaiHttp from 'chai-http';
import deepEqualInAnyOrder from 'deep-equal-in-any-order';
chai.should();
chai.use(chaiAsPromised);
chai.use(chaiHttp);
chai.use(deepEqualInAnyOrder);

View File

@@ -22,6 +22,8 @@
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE. // POSSIBILITY OF SUCH DAMAGE.
import {beforeAll, describe, expect, it} from 'vitest';
import {CompilationEnvironment} from '../lib/compilation-env.js'; import {CompilationEnvironment} from '../lib/compilation-env.js';
import {AnalysisTool, LLVMmcaTool} from '../lib/compilers/index.js'; import {AnalysisTool, LLVMmcaTool} from '../lib/compilers/index.js';
@@ -40,7 +42,7 @@ describe('LLVM-mca tool definition', () => {
let ce: CompilationEnvironment; let ce: CompilationEnvironment;
let a: LLVMmcaTool; let a: LLVMmcaTool;
before(() => { beforeAll(() => {
ce = makeCompilationEnvironment({languages}); ce = makeCompilationEnvironment({languages});
const info = makeFakeCompilerInfo({ const info = makeFakeCompilerInfo({
remote: { remote: {
@@ -55,28 +57,22 @@ describe('LLVM-mca tool definition', () => {
it('should have most filters disabled', () => { it('should have most filters disabled', () => {
if (shouldExist(a)) { if (shouldExist(a)) {
a.getInfo().disabledFilters.should.be.deep.equal([ expect(a.getInfo().disabledFilters).toEqual(['labels', 'directives', 'commentOnly', 'trim', 'debugCalls']);
'labels',
'directives',
'commentOnly',
'trim',
'debugCalls',
]);
} }
}); });
it('should default to most filters off', () => { it('should default to most filters off', () => {
const filters = a.getDefaultFilters(); const filters = a.getDefaultFilters();
filters.intel.should.equal(true); expect(filters.intel).toBe(true);
filters.commentOnly.should.equal(false); expect(filters.commentOnly).toBe(false);
filters.directives.should.equal(false); expect(filters.directives).toBe(false);
filters.labels.should.equal(false); expect(filters.labels).toBe(false);
filters.optOutput.should.equal(false); expect(filters.optOutput).toBe(false);
filters.debugCalls.should.equal(false); expect(filters.debugCalls).toBe(false);
}); });
it('should not support objdump', () => { it('should not support objdump', () => {
a.supportsObjdump().should.equal(false); expect(a.supportsObjdump()).toBe(false);
}); });
it('should support "-o output-file" by default', () => { it('should support "-o output-file" by default', () => {
@@ -87,7 +83,7 @@ describe('LLVM-mca tool definition', () => {
}), }),
'output.txt', 'output.txt',
); );
opts.should.be.deep.equal(['-o', 'output.txt']); expect(opts).toEqual(['-o', 'output.txt']);
}); });
it('should split if disabledFilters is a string', () => { it('should split if disabledFilters is a string', () => {
@@ -100,6 +96,6 @@ describe('LLVM-mca tool definition', () => {
lang: 'analysis', lang: 'analysis',
disabledFilters: 'labels,directives,debugCalls' as any, disabledFilters: 'labels,directives,debugCalls' as any,
}); });
new AnalysisTool(info, ce).getInfo().disabledFilters.should.deep.equal(['labels', 'directives', 'debugCalls']); expect(new AnalysisTool(info, ce).getInfo().disabledFilters).toEqual(['labels', 'directives', 'debugCalls']);
}); });
}); });

View File

@@ -22,6 +22,8 @@
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE. // POSSIBILITY OF SUCH DAMAGE.
import {beforeAll, describe, expect, it} from 'vitest';
import {CompilationEnvironment} from '../lib/compilation-env.js'; import {CompilationEnvironment} from '../lib/compilation-env.js';
import {Dex2OatCompiler} from '../lib/compilers/index.js'; import {Dex2OatCompiler} from '../lib/compilers/index.js';
import * as utils from '../lib/utils.js'; import * as utils from '../lib/utils.js';
@@ -47,37 +49,37 @@ const androidKotlinInfo = {
lang: languages.androidKotlin.id, lang: languages.androidKotlin.id,
} as unknown as CompilerInfo; } as unknown as CompilerInfo;
describe('dex2oat', function () { describe('dex2oat', () => {
let env: CompilationEnvironment; let env: CompilationEnvironment;
before(() => { beforeAll(() => {
env = makeCompilationEnvironment({languages}); env = makeCompilationEnvironment({languages});
}); });
describe('android-java', () => { describe('android-java', () => {
it('Should not crash on instantiation', function () { it('Should not crash on instantiation', () => {
new Dex2OatCompiler(androidJavaInfo, env); new Dex2OatCompiler(androidJavaInfo, env);
}); });
it('Output is shown as-is if full output mode is enabled', function () { it('Output is shown as-is if full output mode is enabled', () => {
return testParse(androidJavaInfo, 'test/android/java', true); return testParse(androidJavaInfo, 'test/android/java', true);
}); });
it('Output is parsed and formatted if full output mode is disabled', function () { it('Output is parsed and formatted if full output mode is disabled', () => {
return testParse(androidJavaInfo, 'test/android/java', false); return testParse(androidJavaInfo, 'test/android/java', false);
}); });
}); });
describe('android-kotlin', () => { describe('android-kotlin', () => {
it('Should not crash on instantiation', function () { it('Should not crash on instantiation', () => {
new Dex2OatCompiler(androidKotlinInfo, env); new Dex2OatCompiler(androidKotlinInfo, env);
}); });
it('Output is shown as-is if full output mode is enabled', function () { it('Output is shown as-is if full output mode is enabled', () => {
return testParse(androidKotlinInfo, 'test/android/kotlin', true); return testParse(androidKotlinInfo, 'test/android/kotlin', true);
}); });
it('Output is parsed and formatted if full output mode is disabled', function () { it('Output is parsed and formatted if full output mode is disabled', () => {
return testParse(androidKotlinInfo, 'test/android/kotlin', false); return testParse(androidKotlinInfo, 'test/android/kotlin', false);
}); });
}); });
@@ -92,7 +94,7 @@ describe('dex2oat', function () {
asm, asm,
}; };
const processed = await compiler.processAsm(objdumpResult); const processed = await compiler.processAsm(objdumpResult);
processed.should.have.property('asm'); expect(processed).toHaveProperty('asm');
const actualSegments = (processed as {asm: ParsedAsmResultLine[]}).asm; const actualSegments = (processed as {asm: ParsedAsmResultLine[]}).asm;
// fullOutput results in no processing, with the entire oatdump text // fullOutput results in no processing, with the entire oatdump text
@@ -107,6 +109,6 @@ describe('dex2oat', function () {
}; };
}); });
actualSegments.should.deep.equal(expectedSegments); expect(actualSegments).toEqual(expectedSegments);
} }
}); });

View File

@@ -22,6 +22,8 @@
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE. // POSSIBILITY OF SUCH DAMAGE.
import {describe, expect, it} from 'vitest';
import {Filter} from '../static/ansi-to-html.js'; import {Filter} from '../static/ansi-to-html.js';
describe('ansi-to-html', () => { describe('ansi-to-html', () => {
@@ -33,21 +35,21 @@ describe('ansi-to-html', () => {
}; };
it('Should leave non-ansi colours alone', () => { it('Should leave non-ansi colours alone', () => {
const filter = new Filter(filterOpts); const filter = new Filter(filterOpts);
filter.toHtml('I am a boring old string').should.equal('I am a boring old string'); expect(filter.toHtml('I am a boring old string')).toEqual('I am a boring old string');
}); });
it('Should handle simple cases', () => { it('Should handle simple cases', () => {
const filter = new Filter(filterOpts); const filter = new Filter(filterOpts);
filter.toHtml('\x1B[38;5;99mTest').should.equal('<span style="color:#875fff">Test</span>'); expect(filter.toHtml('\x1B[38;5;99mTest')).toEqual('<span style="color:#875fff">Test</span>');
}); });
it('Should handle nasty edge cases', () => { it('Should handle nasty edge cases', () => {
const filter = new Filter(filterOpts); const filter = new Filter(filterOpts);
// See #1666, this used to cause catastrophic backtracking. // See #1666, this used to cause catastrophic backtracking.
filter expect(
.toHtml( filter.toHtml(
'\x1B[38;5;9999999999999999999999999999999999999999999999999999999999999999999999999999999' + '\x1B[38;5;9999999999999999999999999999999999999999999999999999999999999999999999999999999' +
'99999999999999999999"mTest', '99999999999999999999"mTest',
) ),
.should.equal( ).toEqual(
'5;9999999999999999999999999999999999999999999999999999999999999' + '5;9999999999999999999999999999999999999999999999999999999999999' +
'99999999999999999999999999999999999999"mTest', '99999999999999999999999999999999999999"mTest',
); );
@@ -56,36 +58,36 @@ describe('ansi-to-html', () => {
// With thanks to https://github.com/rburns/ansi-to-html/pull/84/files // With thanks to https://github.com/rburns/ansi-to-html/pull/84/files
it('renders xterm foreground 256 sequences', () => { it('renders xterm foreground 256 sequences', () => {
const filter = new Filter(filterOpts); const filter = new Filter(filterOpts);
filter.toHtml('\x1B[38;5;196mhello').should.equal('<span style="color:#ff0000">hello</span>'); expect(filter.toHtml('\x1B[38;5;196mhello')).toEqual('<span style="color:#ff0000">hello</span>');
}); });
it('renders xterm background 256 sequences', () => { it('renders xterm background 256 sequences', () => {
const filter = new Filter(filterOpts); const filter = new Filter(filterOpts);
filter.toHtml('\x1B[48;5;196mhello').should.equal('<span style="background-color:#ff0000">hello</span>'); expect(filter.toHtml('\x1B[48;5;196mhello')).toEqual('<span style="background-color:#ff0000">hello</span>');
}); });
it('should ignore reverse video', () => { it('should ignore reverse video', () => {
const filter = new Filter(filterOpts); const filter = new Filter(filterOpts);
filter.toHtml('\x1B[7mhello').should.equal('hello'); expect(filter.toHtml('\x1B[7mhello')).toEqual('hello');
}); });
// tests for #3659 // tests for #3659
it('should stream', () => { it('should stream', () => {
const filter = new Filter(filterOpts); const filter = new Filter(filterOpts);
filter.toHtml('\x1B[38;5;99mfoo'); filter.toHtml('\x1B[38;5;99mfoo');
filter.toHtml('bar').should.equal('<span style="color:#875fff">bar</span>'); expect(filter.toHtml('bar')).toEqual('<span style="color:#875fff">bar</span>');
}); });
it('should handle stream reset', () => { it('should handle stream reset', () => {
const filter = new Filter(filterOpts); const filter = new Filter(filterOpts);
filter.toHtml('\x1B[38;5;99mfoo'); filter.toHtml('\x1B[38;5;99mfoo');
filter.reset(); filter.reset();
filter.toHtml('bar').should.equal('bar'); expect(filter.toHtml('bar')).toEqual('bar');
}); });
// rgb test // rgb test
it('should process rgb colors', () => { it('should process rgb colors', () => {
const filter = new Filter(filterOpts); const filter = new Filter(filterOpts);
filter expect(filter.toHtml('\x1B[38;2;57;170;243mfoo\x1B[48;2;100;100;100mbar')).toEqual(
.toHtml('\x1B[38;2;57;170;243mfoo\x1B[48;2;100;100;100mbar') '<span style="color:#39aaf3">foo<span style="background-color:#646464">bar</span></span>',
.should.equal('<span style="color:#39aaf3">foo<span style="background-color:#646464">bar</span></span>'); );
}); });
}); });

View File

@@ -22,6 +22,8 @@
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE. // POSSIBILITY OF SUCH DAMAGE.
import {beforeAll, describe, expect, it} from 'vitest';
import {unwrap} from '../lib/assert.js'; import {unwrap} from '../lib/assert.js';
import {VcAsmParser} from '../lib/parsers/asm-parser-vc.js'; import {VcAsmParser} from '../lib/parsers/asm-parser-vc.js';
import {AsmParserZ88dk} from '../lib/parsers/asm-parser-z88dk.js'; import {AsmParserZ88dk} from '../lib/parsers/asm-parser-z88dk.js';
@@ -48,7 +50,7 @@ describe('ASM CL parser', () => {
debugCalls: false, debugCalls: false,
}); });
result.asm.should.deep.equal([ expect(result.asm).toEqual([
{ {
source: null, source: null,
text: '<Compilation failed>', text: '<Compilation failed>',
@@ -60,36 +62,37 @@ describe('ASM CL parser', () => {
describe('ASM regex base class', () => { describe('ASM regex base class', () => {
it('should leave unfiltered lines alone', () => { it('should leave unfiltered lines alone', () => {
const line = ' this is a line'; const line = ' this is a line';
AsmRegex.filterAsmLine(line, makeFakeParseFiltersAndOutputOptions({})).should.equal(line); expect(AsmRegex.filterAsmLine(line, makeFakeParseFiltersAndOutputOptions({}))).toEqual(line);
}); });
it('should use up internal whitespace when asked', () => { it('should use up internal whitespace when asked', () => {
AsmRegex.filterAsmLine( expect(
' this is a line', AsmRegex.filterAsmLine(' this is a line', makeFakeParseFiltersAndOutputOptions({trim: true})),
makeFakeParseFiltersAndOutputOptions({trim: true}), ).toEqual(' this is a line');
).should.equal(' this is a line'); expect(
AsmRegex.filterAsmLine('this is a line', makeFakeParseFiltersAndOutputOptions({trim: true})).should.equal( AsmRegex.filterAsmLine('this is a line', makeFakeParseFiltersAndOutputOptions({trim: true})),
'this is a line', ).toEqual('this is a line');
);
}); });
it('should keep whitespace in strings', () => { it('should keep whitespace in strings', () => {
AsmRegex.filterAsmLine( expect(
'equs "this string"', AsmRegex.filterAsmLine('equs "this string"', makeFakeParseFiltersAndOutputOptions({trim: true})),
makeFakeParseFiltersAndOutputOptions({trim: true}), ).toEqual('equs "this string"');
).should.equal('equs "this string"'); expect(
AsmRegex.filterAsmLine( AsmRegex.filterAsmLine(
' equs "this string"', ' equs "this string"',
makeFakeParseFiltersAndOutputOptions({trim: true}), makeFakeParseFiltersAndOutputOptions({trim: true}),
).should.equal(' equs "this string"'); ),
).toEqual(' equs "this string"');
expect(
AsmRegex.filterAsmLine( AsmRegex.filterAsmLine(
'equs "this \\" string \\""', 'equs "this \\" string \\""',
makeFakeParseFiltersAndOutputOptions({trim: true}), makeFakeParseFiltersAndOutputOptions({trim: true}),
).should.equal('equs "this \\" string \\""'); ),
).toEqual('equs "this \\" string \\""');
}); });
it('should not get upset by mismatched strings', () => { it('should not get upset by mismatched strings', () => {
AsmRegex.filterAsmLine( expect(
'a "string \'yeah', AsmRegex.filterAsmLine('a "string \'yeah', makeFakeParseFiltersAndOutputOptions({trim: true})),
makeFakeParseFiltersAndOutputOptions({trim: true}), ).toEqual('a "string \'yeah');
).should.equal('a "string \'yeah');
}); });
}); });
@@ -97,7 +100,7 @@ describe('ASM parser base class', () => {
let parser; let parser;
const filters = {}; const filters = {};
before(() => { beforeAll(() => {
parser = new AsmParser(); parser = new AsmParser();
}); });
@@ -139,10 +142,10 @@ main: # @main
const mov1_line = output.asm.find(line => line.text.trim().startsWith('mov1')); const mov1_line = output.asm.find(line => line.text.trim().startsWith('mov1'));
const call_line = output.asm.find(line => line.text.trim().startsWith('call')); const call_line = output.asm.find(line => line.text.trim().startsWith('call'));
const mov4_line = output.asm.find(line => line.text.trim().startsWith('mov4')); const mov4_line = output.asm.find(line => line.text.trim().startsWith('mov4'));
push_line.source.should.not.have.ownProperty('column'); expect(push_line.source).not.toHaveProperty('column');
mov1_line.source.should.not.have.ownProperty('column'); expect(mov1_line.source).not.toHaveProperty('column');
call_line.source.column.should.equal(20); expect(call_line.source.column).toEqual(20);
mov4_line.source.column.should.equal(9); expect(mov4_line.source.column).toEqual(9);
}); });
it('should parse line numbers when a column is not specified', () => { it('should parse line numbers when a column is not specified', () => {
@@ -167,8 +170,8 @@ main:
`; `;
const output = parser.process(asm, filters); const output = parser.process(asm, filters);
const pushq_line = output.asm.find(line => line.text.trim().startsWith('pushq')); const pushq_line = output.asm.find(line => line.text.trim().startsWith('pushq'));
pushq_line.source.should.not.have.ownProperty('column'); expect(pushq_line.source).not.toHaveProperty('column');
pushq_line.source.line.should.equal(2); expect(pushq_line.source.line).toEqual(2);
}); });
}); });
@@ -176,7 +179,7 @@ describe('ASM parser', () => {
let parser: AsmParser; let parser: AsmParser;
const filters = {}; const filters = {};
before(() => { beforeAll(() => {
parser = new AsmParser(); parser = new AsmParser();
}); });
@@ -197,7 +200,7 @@ ${' '.repeat(65530)}x
ret ret
`; `;
const output = parser.process(asm, filters); const output = parser.process(asm, filters);
parseInt(unwrap(output.parsingTime)).should.be.lessThan(500); // reported as ms, generous timeout for ci runner expect(parseInt(unwrap(output.parsingTime))).toBeLessThan(500); // reported as ms, generous timeout for ci runner
}); });
}); });
@@ -205,7 +208,7 @@ describe('ASM parser z88dk', () => {
let parser: AsmParserZ88dk; let parser: AsmParserZ88dk;
const filters = {}; const filters = {};
before(() => { beforeAll(() => {
parser = new AsmParserZ88dk(undefined as any); parser = new AsmParserZ88dk(undefined as any);
}); });
@@ -226,6 +229,6 @@ ${' '.repeat(65530)}x
ret ret
`; `;
const output = parser.process(asm, filters); const output = parser.process(asm, filters);
parseInt(unwrap(output.parsingTime)).should.be.lessThan(500); // reported as ms, generous timeout for ci runner expect(parseInt(unwrap(output.parsingTime))).toBeLessThan(500); // reported as ms, generous timeout for ci runner
}); });
}); });

View File

@@ -26,6 +26,7 @@ import './utils.js';
import {DescribeInstancesCommand, EC2, Instance} from '@aws-sdk/client-ec2'; import {DescribeInstancesCommand, EC2, Instance} from '@aws-sdk/client-ec2';
import {GetParametersCommand, SSM} from '@aws-sdk/client-ssm'; import {GetParametersCommand, SSM} from '@aws-sdk/client-ssm';
import {mockClient} from 'aws-sdk-client-mock'; import {mockClient} from 'aws-sdk-client-mock';
import {beforeEach, describe, expect, it} from 'vitest';
import * as aws from '../lib/aws.js'; import * as aws from '../lib/aws.js';
@@ -74,24 +75,24 @@ describe('AWS instance fetcher tests', () => {
], ],
}); });
}); });
it('Fetches Bob', () => { it('Fetches Bob', async () => {
const fakeProps = { const fakeProps = {
region: 'not-a-region', region: 'not-a-region',
tagKey: 'Name', tagKey: 'Name',
tagValue: 'Bob', tagValue: 'Bob',
}; };
const fetcher = new aws.InstanceFetcher(prop => fakeProps[prop]); const fetcher = new aws.InstanceFetcher(prop => fakeProps[prop]);
return fetcher.getInstances().should.eventually.deep.equal([instanceC]); await expect(fetcher.getInstances()).resolves.toEqual([instanceC]);
}); });
it('Ignores sleeping nodes', () => { it('Ignores sleeping nodes', async () => {
const fakeProps = { const fakeProps = {
region: 'not-a-region', region: 'not-a-region',
tagKey: 'Name', tagKey: 'Name',
tagValue: 'Alice', tagValue: 'Alice',
}; };
const fetcher = new aws.InstanceFetcher(prop => fakeProps[prop]); const fetcher = new aws.InstanceFetcher(prop => fakeProps[prop]);
return fetcher.getInstances().should.eventually.deep.equal([instanceA, instanceD]); await expect(fetcher.getInstances()).resolves.toEqual([instanceA, instanceD]);
}); });
}); });
@@ -112,30 +113,24 @@ describe('AWS config tests', () => {
], ],
}); });
}); });
it("Doesn't fetch unless region is configured", () => { it("Doesn't fetch unless region is configured", async () => {
const fakeProps = { const fakeProps = {
region: '', region: '',
configValue: 'fromConfigFile', configValue: 'fromConfigFile',
}; };
return aws await aws.initConfig(prop => fakeProps[prop]);
.initConfig(prop => fakeProps[prop]) expect(aws.getConfig('configValue')).toEqual('fromConfigFile');
.then(() => {
aws.getConfig('configValue').should.equal('fromConfigFile');
});
}); });
it('Gets results from SSM, falling back to config if needed', () => { it('Gets results from SSM, falling back to config if needed', async () => {
const fakeProps = { const fakeProps = {
region: 'a non-empty region', region: 'a non-empty region',
configValue: 'fromConfigFile', configValue: 'fromConfigFile',
notInAmazon: 'yay', notInAmazon: 'yay',
}; };
return aws await aws.initConfig(prop => fakeProps[prop]);
.initConfig(prop => fakeProps[prop]) expect(aws.getConfig('configValue')).toEqual('fromAws');
.then(() => { expect(aws.getConfig('onlyOnAws')).toEqual('bibble');
aws.getConfig('configValue').should.equal('fromAws'); expect(aws.getConfig('notInAmazon')).toEqual('yay');
aws.getConfig('onlyOnAws').should.equal('bibble');
aws.getConfig('notInAmazon').should.equal('yay');
});
}); });
}); });

View File

@@ -22,6 +22,8 @@
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE. // POSSIBILITY OF SUCH DAMAGE.
import {beforeAll, describe, expect, it} from 'vitest';
import {BaseCompiler} from '../lib/base-compiler.js'; import {BaseCompiler} from '../lib/base-compiler.js';
import {BuildEnvSetupBase} from '../lib/buildenvsetup/index.js'; import {BuildEnvSetupBase} from '../lib/buildenvsetup/index.js';
import {CompilationEnvironment} from '../lib/compilation-env.js'; import {CompilationEnvironment} from '../lib/compilation-env.js';
@@ -35,7 +37,6 @@ import {
makeFakeCompilerInfo, makeFakeCompilerInfo,
makeFakeParseFiltersAndOutputOptions, makeFakeParseFiltersAndOutputOptions,
path, path,
should,
shouldExist, shouldExist,
} from './utils.js'; } from './utils.js';
@@ -43,7 +44,7 @@ const languages = {
'c++': {id: 'c++'}, 'c++': {id: 'c++'},
} as const; } as const;
describe('Basic compiler invariants', function () { describe('Basic compiler invariants', () => {
let ce: CompilationEnvironment; let ce: CompilationEnvironment;
let compiler: BaseCompiler; let compiler: BaseCompiler;
@@ -58,25 +59,25 @@ describe('Basic compiler invariants', function () {
ldPath: [], ldPath: [],
}; };
before(() => { beforeAll(() => {
ce = makeCompilationEnvironment({languages}); ce = makeCompilationEnvironment({languages});
compiler = new BaseCompiler(info as CompilerInfo, ce); compiler = new BaseCompiler(info as CompilerInfo, ce);
}); });
it('should recognize when optOutput has been request', () => { it('should recognize when optOutput has been request', () => {
compiler.optOutputRequested(['please', 'recognize', '-fsave-optimization-record']).should.equal(true); expect(compiler.optOutputRequested(['please', 'recognize', '-fsave-optimization-record'])).toBe(true);
compiler.optOutputRequested(['please', "don't", 'recognize']).should.equal(false); expect(compiler.optOutputRequested(['please', "don't", 'recognize'])).toBe(false);
}); });
it('should allow comments next to includes (Bug #874)', () => { it('should allow comments next to includes (Bug #874)', () => {
should.equal(compiler.checkSource('#include <cmath> // std::(sin, cos, ...)'), null); expect(compiler.checkSource('#include <cmath> // std::(sin, cos, ...)')).toBeNull();
const badSource = compiler.checkSource('#include </dev/null..> //Muehehehe'); const badSource = compiler.checkSource('#include </dev/null..> //Muehehehe');
if (shouldExist(badSource)) { if (shouldExist(badSource)) {
badSource.should.equal('<stdin>:1:1: no absolute or relative includes please'); expect(badSource).toEqual('<stdin>:1:1: no absolute or relative includes please');
} }
}); });
it('should not warn of path-likes outside C++ includes (Bug #3045)', () => { it('should not warn of path-likes outside C++ includes (Bug #3045)', () => {
function testIncludeG(text: string) { function testIncludeG(text: string) {
should.equal(compiler.checkSource(text), null); expect(compiler.checkSource(text)).toBeNull();
} }
testIncludeG('#include <iostream>'); testIncludeG('#include <iostream>');
testIncludeG('#include <iostream> // <..>'); testIncludeG('#include <iostream> // <..>');
@@ -86,7 +87,7 @@ describe('Basic compiler invariants', function () {
}); });
it('should not allow path C++ includes', () => { it('should not allow path C++ includes', () => {
function testIncludeNotG(text: string) { function testIncludeNotG(text: string) {
should.equal(compiler.checkSource(text), '<stdin>:1:1: no absolute or relative includes please'); expect(compiler.checkSource(text)).toEqual('<stdin>:1:1: no absolute or relative includes please');
} }
testIncludeNotG('#include <./.bashrc>'); testIncludeNotG('#include <./.bashrc>');
testIncludeNotG('#include </dev/null> // <..>'); testIncludeNotG('#include </dev/null> // <..>');
@@ -97,11 +98,11 @@ describe('Basic compiler invariants', function () {
const newConfig: Partial<CompilerInfo> = {...info, explicitVersion: '123'}; const newConfig: Partial<CompilerInfo> = {...info, explicitVersion: '123'};
const forcedVersionCompiler = new BaseCompiler(newConfig as CompilerInfo, ce); const forcedVersionCompiler = new BaseCompiler(newConfig as CompilerInfo, ce);
const result = await forcedVersionCompiler.getVersion(); const result = await forcedVersionCompiler.getVersion();
result.stdout.should.deep.equal(['123']); expect(result.stdout).toEqual(['123']);
}); });
}); });
describe('Compiler execution', function () { describe('Compiler execution', () => {
let ce: CompilationEnvironment; let ce: CompilationEnvironment;
let compiler: BaseCompiler; let compiler: BaseCompiler;
let compilerNoExec: BaseCompiler; let compilerNoExec: BaseCompiler;
@@ -156,7 +157,7 @@ describe('Compiler execution', function () {
options: '--hello-abc -I"/opt/some thing 1.0/include"', options: '--hello-abc -I"/opt/some thing 1.0/include"',
}); });
before(() => { beforeAll(() => {
ce = makeCompilationEnvironment({languages}); ce = makeCompilationEnvironment({languages});
compiler = new BaseCompiler(executingCompilerInfo, ce); compiler = new BaseCompiler(executingCompilerInfo, ce);
win32compiler = new Win32Compiler(win32CompilerInfo, ce); win32compiler = new Win32Compiler(win32CompilerInfo, ce);
@@ -168,7 +169,7 @@ describe('Compiler execution', function () {
function stubOutCallToExec(execStub, compiler, content, result, nthCall) { function stubOutCallToExec(execStub, compiler, content, result, nthCall) {
execStub.onCall(nthCall || 0).callsFake((compiler, args) => { execStub.onCall(nthCall || 0).callsFake((compiler, args) => {
const minusO = args.indexOf('-o'); const minusO = args.indexOf('-o');
minusO.should.be.gte(0); expect(minusO).toBeGreaterThanOrEqual(0);
const output = args[minusO + 1]; const output = args[minusO + 1];
// Maybe we should mock out the FS too; but that requires a lot more work. // Maybe we should mock out the FS too; but that requires a lot more work.
fs.writeFileSync(output, content); fs.writeFileSync(output, content);
@@ -194,7 +195,7 @@ describe('Compiler execution', function () {
libraries, libraries,
[], [],
); );
args.should.deep.equal([ expect(args).toEqual([
'-g', '-g',
'-o', '-o',
'example.s', 'example.s',
@@ -223,7 +224,7 @@ describe('Compiler execution', function () {
libraries, libraries,
[], [],
); );
win32args.should.deep.equal([ expect(win32args).toEqual([
'/nologo', '/nologo',
'/FA', '/FA',
'/c', '/c',
@@ -239,19 +240,19 @@ describe('Compiler execution', function () {
it('buildenv should handle spaces correctly', () => { it('buildenv should handle spaces correctly', () => {
const buildenv = new BuildEnvSetupBase(executingCompilerInfo, ce); const buildenv = new BuildEnvSetupBase(executingCompilerInfo, ce);
buildenv.getCompilerArch().should.equal('magic 8bit'); expect(buildenv.getCompilerArch()).toEqual('magic 8bit');
}); });
it('buildenv compiler without target/march', () => { it('buildenv compiler without target/march', () => {
const buildenv = new BuildEnvSetupBase(noExecuteSupportCompilerInfo, ce); const buildenv = new BuildEnvSetupBase(noExecuteSupportCompilerInfo, ce);
buildenv.getCompilerArch().should.equal(false); expect(buildenv.getCompilerArch()).toBe(false);
buildenv.compilerSupportsX86.should.equal(true); expect(buildenv.compilerSupportsX86).toBe(true);
}); });
it('buildenv compiler without target/march but with options', () => { it('buildenv compiler without target/march but with options', () => {
const buildenv = new BuildEnvSetupBase(someOptionsCompilerInfo, ce); const buildenv = new BuildEnvSetupBase(someOptionsCompilerInfo, ce);
buildenv.getCompilerArch().should.equal(false); expect(buildenv.getCompilerArch()).toBe(false);
buildenv.compilerSupportsX86.should.equal(true); expect(buildenv.compilerSupportsX86).toBe(true);
}); });
it('compiler overrides should be sanitized', () => { it('compiler overrides should be sanitized', () => {
@@ -281,10 +282,10 @@ describe('Compiler execution', function () {
compiler.applyOverridesToExecOptions(execOptions, sanitized); compiler.applyOverridesToExecOptions(execOptions, sanitized);
Object.keys(execOptions.env).should.include('SOMEVAR'); expect(execOptions.env).toHaveProperty('SOMEVAR');
execOptions.env['SOMEVAR'].should.equal('123'); expect(execOptions.env['SOMEVAR']).toEqual('123');
Object.keys(execOptions.env).should.not.include('LD_PRELOAD'); expect(execOptions.env).not.toHaveProperty('LD_PRELOAD');
Object.keys(execOptions.env).should.not.include('ABC$#%@6@5'); expect(execOptions.env).not.toHaveProperty('ABC$#%@6@5');
}); });
// it('should compile', async () => { // it('should compile', async () => {
@@ -632,8 +633,7 @@ Args: []
const dirPath = await compiler.newTempDir(); const dirPath = await compiler.newTempDir();
const optPath = path.join(dirPath, 'temp.out'); const optPath = path.join(dirPath, 'temp.out');
await fs.writeFile(optPath, test); await fs.writeFile(optPath, test);
const a = await compiler.processOptOutput(optPath); expect(await compiler.processOptOutput(optPath)).toEqual([
a.should.deep.equal([
{ {
Args: [], Args: [],
DebugLoc: {Column: 21, File: 'example.cpp', Line: 4}, DebugLoc: {Column: 21, File: 'example.cpp', Line: 4},
@@ -648,81 +648,39 @@ Args: []
it('should normalize extra file path', () => { it('should normalize extra file path', () => {
const withDemangler = {...noExecuteSupportCompilerInfo, demangler: 'demangler-exe', demanglerType: 'cpp'}; const withDemangler = {...noExecuteSupportCompilerInfo, demangler: 'demangler-exe', demanglerType: 'cpp'};
const compiler = new BaseCompiler(withDemangler, ce); const compiler = new BaseCompiler(withDemangler, ce) as any; // to get to the protected...
if (process.platform === 'win32') { if (process.platform === 'win32') {
(compiler as any) expect(compiler.getExtraFilepath('c:/tmp/somefolder', 'test.h')).toEqual('c:\\tmp\\somefolder\\test.h');
.getExtraFilepath('c:/tmp/somefolder', 'test.h')
.should.equal('c:\\tmp\\somefolder\\test.h');
} else { } else {
(compiler as any).getExtraFilepath('/tmp/somefolder', 'test.h').should.equal('/tmp/somefolder/test.h'); expect(compiler.getExtraFilepath('/tmp/somefolder', 'test.h')).toEqual('/tmp/somefolder/test.h');
} }
try { expect(() => compiler.getExtraFilepath('/tmp/somefolder', '../test.h')).toThrow(Error);
(compiler as any).getExtraFilepath('/tmp/somefolder', '../test.h'); expect(() => compiler.getExtraFilepath('/tmp/somefolder', './../test.h')).toThrow(Error);
throw 'Should throw exception 1';
} catch (error) {
if (!(error instanceof Error)) {
throw error;
}
}
try { expect(compiler.getExtraFilepath('/tmp/somefolder', '/tmp/someotherfolder/test.h')).toEqual(
(compiler as any).getExtraFilepath('/tmp/somefolder', './../test.h'); '/tmp/somefolder/tmp/someotherfolder/test.h',
throw 'Should throw exception 2'; );
} catch (error) {
if (!(error instanceof Error)) {
throw error;
}
}
try {
(compiler as any)
.getExtraFilepath('/tmp/somefolder', '/tmp/someotherfolder/test.h')
.should.equal('/tmp/somefolder/tmp/someotherfolder/test.h');
} catch (error) {
if (!(error instanceof Error)) {
throw error;
}
}
try {
(compiler as any).getExtraFilepath('/tmp/somefolder', '\\test.h').should.equal('/tmp/somefolder/test.h');
} catch (error) {
if (!(error instanceof Error)) {
throw error;
}
}
try {
(compiler as any).getExtraFilepath('/tmp/somefolder', 'test_hello/../../etc/passwd');
throw 'Should throw exception 5';
} catch (error) {
if (!(error instanceof Error)) {
throw error;
}
}
if (process.platform === 'win32') { if (process.platform === 'win32') {
(compiler as any) expect(compiler.getExtraFilepath('/tmp/somefolder', '\\test.h')).toEqual('/tmp/somefolder/test.h');
.getExtraFilepath('c:/tmp/somefolder', 'test.txt')
.should.equal('c:\\tmp\\somefolder\\test.txt');
} else {
(compiler as any).getExtraFilepath('/tmp/somefolder', 'test.txt').should.equal('/tmp/somefolder/test.txt');
} }
try { expect(() => compiler.getExtraFilepath('/tmp/somefolder', 'test_hello/../../etc/passwd')).toThrow(Error);
(compiler as any)
.getExtraFilepath('/tmp/somefolder', 'subfolder/hello.h') if (process.platform === 'win32') {
.should.equal('/tmp/somefolder/subfolder/hello.h'); expect(compiler.getExtraFilepath('c:/tmp/somefolder', 'test.txt')).toEqual('c:\\tmp\\somefolder\\test.txt');
} catch (error) { } else {
if (!(error instanceof Error)) { expect(compiler.getExtraFilepath('/tmp/somefolder', 'test.txt')).toEqual('/tmp/somefolder/test.txt');
throw error;
}
} }
expect(compiler.getExtraFilepath('/tmp/somefolder', 'subfolder/hello.h')).toEqual(
'/tmp/somefolder/subfolder/hello.h',
);
}); });
}); });
describe('getDefaultExecOptions', function () { describe('getDefaultExecOptions', () => {
let ce: CompilationEnvironment; let ce: CompilationEnvironment;
const noExecuteSupportCompilerInfo = makeFakeCompilerInfo({ const noExecuteSupportCompilerInfo = makeFakeCompilerInfo({
@@ -737,7 +695,7 @@ describe('getDefaultExecOptions', function () {
extraPath: ['/tmp/p1', '/tmp/p2'], extraPath: ['/tmp/p1', '/tmp/p2'],
}); });
before(() => { beforeAll(() => {
ce = makeCompilationEnvironment({ ce = makeCompilationEnvironment({
languages, languages,
props: { props: {
@@ -750,9 +708,9 @@ describe('getDefaultExecOptions', function () {
it('Have all the paths', () => { it('Have all the paths', () => {
const compiler = new BaseCompiler(noExecuteSupportCompilerInfo, ce); const compiler = new BaseCompiler(noExecuteSupportCompilerInfo, ce);
const options = compiler.getDefaultExecOptions(); const options = compiler.getDefaultExecOptions();
Object.keys(options.env).should.include('PATH'); expect(options.env).toHaveProperty('PATH');
const paths = options.env.PATH.split(path.delimiter); const paths = options.env.PATH.split(path.delimiter);
paths.should.deep.equal(['/usr/local/ninja', '/tmp/p1', '/tmp/p2']); expect(paths).toEqual(['/usr/local/ninja', '/tmp/p1', '/tmp/p2']);
}); });
}); });

View File

@@ -22,6 +22,8 @@
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE. // POSSIBILITY OF SUCH DAMAGE.
import {describe, expect, it} from 'vitest';
import {BaseFormatter} from '../lib/formatters/base.js'; import {BaseFormatter} from '../lib/formatters/base.js';
class Formatter extends BaseFormatter {} class Formatter extends BaseFormatter {}
@@ -35,8 +37,8 @@ describe('Basic formatter functionality', () => {
type: 'foofmt', type: 'foofmt',
version: 'foobar-format 1.0.0', version: 'foobar-format 1.0.0',
}); });
fmt.isValidStyle('foostyle').should.equal(false); expect(fmt.isValidStyle('foostyle')).toBe(false);
fmt.formatterInfo.styles.should.deep.equal([]); expect(fmt.formatterInfo.styles).toEqual([]);
}); });
it('should return an array of args for formatters with styles', () => { it('should return an array of args for formatters with styles', () => {
@@ -47,7 +49,7 @@ describe('Basic formatter functionality', () => {
type: 'foofmt', type: 'foofmt',
version: 'foobar-format 1.0.0', version: 'foobar-format 1.0.0',
}); });
fmt.isValidStyle('foostyle').should.equal(true); expect(fmt.isValidStyle('foostyle')).toBe(true);
fmt.formatterInfo.styles.should.deep.equal(['foostyle']); expect(fmt.formatterInfo.styles).toEqual(['foostyle']);
}); });
}); });

View File

@@ -27,6 +27,7 @@ import {Readable} from 'stream';
import {GetObjectCommand, NoSuchKey, PutObjectCommand, S3} from '@aws-sdk/client-s3'; import {GetObjectCommand, NoSuchKey, PutObjectCommand, S3} from '@aws-sdk/client-s3';
import {sdkStreamMixin} from '@smithy/util-stream'; import {sdkStreamMixin} from '@smithy/util-stream';
import {AwsClientStub, mockClient} from 'aws-sdk-client-mock'; import {AwsClientStub, mockClient} from 'aws-sdk-client-mock';
import {beforeEach, describe, expect, it} from 'vitest';
import {BaseCache} from '../lib/cache/base.js'; import {BaseCache} from '../lib/cache/base.js';
import {createCacheFromConfig} from '../lib/cache/from-config.js'; import {createCacheFromConfig} from '../lib/cache/from-config.js';
@@ -36,55 +37,38 @@ import {NullCache} from '../lib/cache/null.js';
import {OnDiskCache} from '../lib/cache/on-disk.js'; import {OnDiskCache} from '../lib/cache/on-disk.js';
import {S3Cache} from '../lib/cache/s3.js'; import {S3Cache} from '../lib/cache/s3.js';
import {fs, newTempDir, path, shouldExist} from './utils.js'; import {fs, newTempDir, path} from './utils.js';
function basicTests(factory: () => BaseCache) { function basicTests(factory: () => BaseCache) {
it('should start empty', () => { it('should start empty', async () => {
const cache = factory(); const cache = factory();
cache.stats().should.eql({hits: 0, puts: 0, gets: 0}); expect(cache.stats()).toEqual({hits: 0, puts: 0, gets: 0});
return cache await expect(cache.get('not a key')).resolves.toHaveProperty('hit', false);
.get('not a key') expect(cache.stats()).toEqual({hits: 0, puts: 0, gets: 1});
.should.eventually.contain({hit: false})
.then(x => {
cache.stats().should.eql({hits: 0, puts: 0, gets: 1});
return x;
});
}); });
it('should store and retrieve strings', () => { it('should store and retrieve strings', async () => {
const cache = factory(); const cache = factory();
return cache await cache.put('a key', 'a value', 'bob');
.put('a key', 'a value', 'bob') expect(cache.stats()).toEqual({hits: 0, puts: 1, gets: 0});
.then(() => { await expect(cache.get('a key')).resolves.toEqual({
cache.stats().should.eql({hits: 0, puts: 1, gets: 0});
return cache.get('a key').should.eventually.eql({
hit: true, hit: true,
data: Buffer.from('a value'), data: Buffer.from('a value'),
}); });
}) expect(cache.stats()).toEqual({hits: 1, puts: 1, gets: 1});
.then(x => {
cache.stats().should.eql({hits: 1, puts: 1, gets: 1});
return x;
});
}); });
it('should store and retrieve binary buffers', () => { it('should store and retrieve binary buffers', async () => {
const cache = factory(); const cache = factory();
const buffer = Buffer.alloc(2 * 1024); const buffer = Buffer.alloc(2 * 1024);
buffer.fill('@'); buffer.fill('@');
return cache await cache.put('a key', buffer, 'bob');
.put('a key', buffer, 'bob') expect(cache.stats()).toEqual({hits: 0, puts: 1, gets: 0});
.then(() => { await expect(cache.get('a key')).resolves.toEqual({
cache.stats().should.eql({hits: 0, puts: 1, gets: 0});
return cache.get('a key').should.eventually.eql({
hit: true, hit: true,
data: buffer, data: buffer,
}); });
}) expect(cache.stats()).toEqual({hits: 1, puts: 1, gets: 1});
.then(x => {
cache.stats().should.eql({hits: 1, puts: 1, gets: 1});
return x;
});
}); });
} }
@@ -92,26 +76,21 @@ describe('In-memory caches', () => {
basicTests(() => new InMemoryCache('test', 10)); basicTests(() => new InMemoryCache('test', 10));
it('should give extra stats', () => { it('should give extra stats', () => {
const cache = new InMemoryCache('test', 1); const cache = new InMemoryCache('test', 1);
cache expect(cache.statString()).toEqual(
.statString() '0 puts; 0 gets, 0 hits, 0 misses (0.00%), LRU has 0 item(s) totalling 0 bytes',
.should.equal('0 puts; 0 gets, 0 hits, 0 misses (0.00%), LRU has 0 item(s) totalling 0 bytes'); );
}); });
it('should evict old objects', () => { it('should evict old objects', async () => {
const cache = new InMemoryCache('test', 1); const cache = new InMemoryCache('test', 1);
return cache await cache.put('a key', 'a value', 'bob');
.put('a key', 'a value', 'bob')
.then(() => {
const promises: Promise<void>[] = []; const promises: Promise<void>[] = [];
const oneK = ''.padEnd(1024); const oneK = ''.padEnd(1024);
for (let i = 0; i < 1024; i++) { for (let i = 0; i < 1024; i++) {
promises.push(cache.put(`key${i}`, oneK)); promises.push(cache.put(`key${i}`, oneK));
} }
return Promise.all(promises); await Promise.all(promises);
}) await expect(cache.get('a key')).resolves.toHaveProperty('hit', false);
.then(() => {
return cache.get('a key').should.eventually.contain({hit: false});
});
}); });
}); });
@@ -126,17 +105,14 @@ describe('Multi caches', () => {
), ),
); );
it('should write through', () => { it('should write through', async () => {
const subCache1 = new InMemoryCache('test', 1); const subCache1 = new InMemoryCache('test', 1);
const subCache2 = new InMemoryCache('test', 1); const subCache2 = new InMemoryCache('test', 1);
const cache = new MultiCache('test', subCache1, subCache2); const cache = new MultiCache('test', subCache1, subCache2);
return cache.put('a key', 'a value', 'bob').then(() => { await cache.put('a key', 'a value', 'bob');
return Promise.all([ await expect(cache.get('a key')).resolves.toEqual({hit: true, data: Buffer.from('a value')});
cache.get('a key').should.eventually.eql({hit: true, data: Buffer.from('a value')}), await expect(subCache1.get('a key')).resolves.toEqual({hit: true, data: Buffer.from('a value')});
subCache1.get('a key').should.eventually.eql({hit: true, data: Buffer.from('a value')}), await expect(subCache2.get('a key')).resolves.toEqual({hit: true, data: Buffer.from('a value')});
subCache2.get('a key').should.eventually.eql({hit: true, data: Buffer.from('a value')}),
]);
});
}); });
it('services from the first cache hit', async () => { it('services from the first cache hit', async () => {
@@ -146,51 +122,44 @@ describe('Multi caches', () => {
await subCache1.put('a key', 'cache1'); await subCache1.put('a key', 'cache1');
await subCache2.put('a key', 'cache2'); await subCache2.put('a key', 'cache2');
const cache = new MultiCache('test', subCache1, subCache2); const cache = new MultiCache('test', subCache1, subCache2);
await cache.get('a key').should.eventually.eql({hit: true, data: Buffer.from('cache1')}); await expect(cache.get('a key')).resolves.toEqual({hit: true, data: Buffer.from('cache1')});
subCache1.hits.should.equal(1); expect(subCache1.hits).toEqual(1);
subCache1.gets.should.equal(1); expect(subCache1.gets).toEqual(1);
subCache2.hits.should.equal(0); expect(subCache2.hits).toEqual(0);
subCache2.gets.should.equal(0); expect(subCache2.gets).toEqual(0);
await subCache1.get('a key').should.eventually.eql({hit: true, data: Buffer.from('cache1')}); await expect(subCache1.get('a key')).resolves.toEqual({hit: true, data: Buffer.from('cache1')});
await subCache2.get('a key').should.eventually.eql({hit: true, data: Buffer.from('cache2')}); await expect(subCache2.get('a key')).resolves.toEqual({hit: true, data: Buffer.from('cache2')});
}); });
}); });
describe('On disk caches', () => { describe('On disk caches', () => {
basicTests(() => new OnDiskCache('test', newTempDir(), 10)); basicTests(() => new OnDiskCache('test', newTempDir(), 10));
it('should evict old objects', () => { it('should evict old objects', async () => {
const tempDir = newTempDir(); const tempDir = newTempDir();
const cache = new OnDiskCache('test', tempDir, 1); const cache = new OnDiskCache('test', tempDir, 1);
return cache await cache.put('a key', 'a value', 'bob');
.put('a key', 'a value', 'bob')
.then(() => {
const promises: Promise<void>[] = []; const promises: Promise<void>[] = [];
const oneHundredK = ''.padEnd(1024 * 100); const oneHundredK = ''.padEnd(1024 * 100);
for (let i = 0; i < 12; i++) { for (let i = 0; i < 12; i++) {
promises.push(cache.put(`key${i}`, oneHundredK)); promises.push(cache.put(`key${i}`, oneHundredK));
} }
return Promise.all(promises); await Promise.all(promises);
}) await expect(cache.get('a key')).resolves.toHaveProperty('hit', false);
.then(() => {
return cache.get('a key').should.eventually.contain({hit: false});
});
}); });
it('should handle existing data', () => { it('should handle existing data', async () => {
const tempDir = newTempDir(); const tempDir = newTempDir();
fs.writeFileSync(path.join(tempDir, 'abcdef'), 'this is abcdef'); fs.writeFileSync(path.join(tempDir, 'abcdef'), 'this is abcdef');
fs.mkdirSync(path.join(tempDir, 'path')); fs.mkdirSync(path.join(tempDir, 'path'));
fs.writeFileSync(path.join(tempDir, 'path', 'test'), 'this is path/test'); fs.writeFileSync(path.join(tempDir, 'path', 'test'), 'this is path/test');
const cache = new OnDiskCache('test', tempDir, 1); const cache = new OnDiskCache('test', tempDir, 1);
return Promise.all([ await expect(cache.get('abcdef')).resolves.toEqual({hit: true, data: Buffer.from('this is abcdef')});
cache.get('abcdef').should.eventually.eql({hit: true, data: Buffer.from('this is abcdef')}), await expect(cache.get(path.join('path', 'test'))).resolves.toEqual({
cache.get(path.join('path', 'test')).should.eventually.eql({
hit: true, hit: true,
data: Buffer.from('this is path/test'), data: Buffer.from('this is path/test'),
}), });
]);
}); });
// MRG ideally handle the case of pre-populated stuff overflowing the size // MRG ideally handle the case of pre-populated stuff overflowing the size
@@ -223,23 +192,16 @@ describe('S3 tests', () => {
}); });
basicTests(() => new S3Cache('test', 'test.bucket', 'cache', 'uk-north-1')); basicTests(() => new S3Cache('test', 'test.bucket', 'cache', 'uk-north-1'));
it('should correctly handle errors', () => { it('should correctly handle errors', async () => {
mockS3.on(GetObjectCommand, {Bucket: 'test.bucket'}).rejects('Some s3 error'); mockS3.on(GetObjectCommand, {Bucket: 'test.bucket'}).rejects('Some s3 error');
let err: Error | null = null; let err: Error = new Error('not an error');
const cache = new S3Cache('test', 'test.bucket', 'cache', 'uk-north-1', (e: Error, op: string) => { const cache = new S3Cache('test', 'test.bucket', 'cache', 'uk-north-1', (e: Error, op: string) => {
err = e; err = e;
op.should.equal('read'); expect(op).toEqual('read');
});
return cache
.get('doesntmatter')
.should.eventually.contain({hit: false})
.then(x => {
cache.stats().should.eql({hits: 0, puts: 0, gets: 1});
if (shouldExist(err)) {
err.toString().should.equal('Error: Some s3 error');
}
return x;
}); });
await expect(cache.get('doesntmatter')).resolves.toHaveProperty('hit', false);
expect(cache.stats()).toEqual({hits: 0, puts: 0, gets: 1});
expect(err.toString()).toEqual('Error: Some s3 error');
}); });
// BE VERY CAREFUL - the below can be used with sufficient permissions to test on prod (With mocks off)... // BE VERY CAREFUL - the below can be used with sufficient permissions to test on prod (With mocks off)...
@@ -254,39 +216,39 @@ describe('Config tests', () => {
}); });
it('should create null cache on empty config', () => { it('should create null cache on empty config', () => {
const cache = createCacheFromConfig('name', ''); const cache = createCacheFromConfig('name', '');
cache.constructor.should.eql(NullCache); expect(cache.constructor).toEqual(NullCache);
cache.cacheName.should.eql('name'); expect(cache.cacheName).toEqual('name');
}); });
it('should throw on bad types', () => { it('should throw on bad types', () => {
(() => createCacheFromConfig('test', 'InMemory')).should.throw(); expect(() => createCacheFromConfig('test', 'InMemory')).toThrow();
(() => createCacheFromConfig('test', 'NotAType()')).should.throw(); expect(() => createCacheFromConfig('test', 'NotAType()')).toThrow();
}); });
it('should create in memory caches', () => { it('should create in memory caches', () => {
const cache = createCacheFromConfig<InMemoryCache>('test', 'InMemory(123)'); const cache = createCacheFromConfig<InMemoryCache>('test', 'InMemory(123)');
cache.constructor.should.eql(InMemoryCache); expect(cache.constructor).toEqual(InMemoryCache);
cache.cacheMb.should.equal(123); expect(cache.cacheMb).toEqual(123);
(() => createCacheFromConfig('test', 'InMemory()')).should.throw(); expect(() => createCacheFromConfig('test', 'InMemory()')).toThrow();
(() => createCacheFromConfig('test', 'InMemory(argh)')).should.throw(); expect(() => createCacheFromConfig('test', 'InMemory(argh)')).toThrow();
(() => createCacheFromConfig('test', 'InMemory(123,yibble)')).should.throw(); expect(() => createCacheFromConfig('test', 'InMemory(123,yibble)')).toThrow();
}); });
it('should create on disk caches', () => { it('should create on disk caches', () => {
const tempDir = newTempDir(); const tempDir = newTempDir();
const cache = createCacheFromConfig<OnDiskCache>('test', `OnDisk(${tempDir},456)`); const cache = createCacheFromConfig<OnDiskCache>('test', `OnDisk(${tempDir},456)`);
cache.constructor.should.eql(OnDiskCache); expect(cache.constructor).toEqual(OnDiskCache);
cache.path.should.equal(tempDir); expect(cache.path).toEqual(tempDir);
cache.cacheMb.should.equal(456); expect(cache.cacheMb).toEqual(456);
(() => createCacheFromConfig('test', 'OnDisk()')).should.throw(); expect(() => createCacheFromConfig('test', 'OnDisk()')).toThrow();
(() => createCacheFromConfig('test', 'OnDisk(argh,yibble)')).should.throw(); expect(() => createCacheFromConfig('test', 'OnDisk(argh,yibble)')).toThrow();
(() => createCacheFromConfig('test', 'OnDisk(/tmp/moo,456,blah)')).should.throw(); expect(() => createCacheFromConfig('test', 'OnDisk(/tmp/moo,456,blah)')).toThrow();
}); });
it('should create S3 caches', () => { it('should create S3 caches', () => {
const cache = createCacheFromConfig<S3Cache>('test', `S3(test.bucket,cache,uk-north-1)`); const cache = createCacheFromConfig<S3Cache>('test', `S3(test.bucket,cache,uk-north-1)`);
cache.constructor.should.eql(S3Cache); expect(cache.constructor).toEqual(S3Cache);
cache.path.should.equal('cache'); expect(cache.path).toEqual('cache');
cache.region.should.equal('uk-north-1'); expect(cache.region).toEqual('uk-north-1');
(() => createCacheFromConfig('test', 'S3()')).should.throw(); expect(() => createCacheFromConfig('test', 'S3()')).toThrow();
(() => createCacheFromConfig('test', 'S3(argh,yibble)')).should.throw(); expect(() => createCacheFromConfig('test', 'S3(argh,yibble)')).toThrow();
(() => createCacheFromConfig('test', 'S3(/tmp/moo,456,blah,nork)')).should.throw(); expect(() => createCacheFromConfig('test', 'S3(/tmp/moo,456,blah,nork)')).toThrow();
}); });
it('should create multi caches', () => { it('should create multi caches', () => {
const tempDir = newTempDir(); const tempDir = newTempDir();
@@ -294,15 +256,15 @@ describe('Config tests', () => {
'multi', 'multi',
`InMemory(123);OnDisk(${tempDir},456);S3(test.bucket,cache,uk-north-1)`, `InMemory(123);OnDisk(${tempDir},456);S3(test.bucket,cache,uk-north-1)`,
); );
cache.constructor.should.eql(MultiCache); expect(cache.constructor).toEqual(MultiCache);
const upstream: BaseCache[] = (cache as any).upstream; // This isn't pretty. upstream is private. const upstream: BaseCache[] = (cache as any).upstream; // This isn't pretty. upstream is private.
upstream.length.should.equal(3); expect(upstream.length).toEqual(3);
upstream[0].constructor.should.eql(InMemoryCache); expect(upstream[0].constructor).toEqual(InMemoryCache);
upstream[1].constructor.should.eql(OnDiskCache); expect(upstream[1].constructor).toEqual(OnDiskCache);
upstream[2].constructor.should.eql(S3Cache); expect(upstream[2].constructor).toEqual(S3Cache);
upstream[0].cacheName.should.eql('multi'); expect(upstream[0].cacheName).toEqual('multi');
upstream[1].cacheName.should.eql('multi'); expect(upstream[1].cacheName).toEqual('multi');
upstream[2].cacheName.should.eql('multi'); expect(upstream[2].cacheName).toEqual('multi');
}); });
}); });

View File

@@ -22,6 +22,8 @@
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE. // POSSIBILITY OF SUCH DAMAGE.
import {describe, expect, it} from 'vitest';
import * as cfg from '../lib/cfg/cfg.js'; import * as cfg from '../lib/cfg/cfg.js';
import {fs, makeFakeCompilerInfo, path, resolvePathFromTestRoot} from './utils.js'; import {fs, makeFakeCompilerInfo, path, resolvePathFromTestRoot} from './utils.js';
@@ -36,7 +38,7 @@ async function DoCfgTest(cfgArg, filename, isLlvmIr = false) {
contents.asm, contents.asm,
isLlvmIr, isLlvmIr,
); );
structure.should.deep.equal(contents.cfg); expect(structure).toEqual(contents.cfg);
} }
describe('Cfg test cases', () => { describe('Cfg test cases', () => {

View File

@@ -25,15 +25,15 @@
import fs from 'fs'; import fs from 'fs';
import path from 'path'; import path from 'path';
import {assert} from 'chai'; import {describe, expect, it} from 'vitest';
import {languages} from '../lib/languages.js'; import {languages} from '../lib/languages.js';
const img_dir = path.resolve('views/resources/logos'); const img_dir = path.resolve('views/resources/logos');
function checkImage(logo) { function checkImage(logo: string) {
const logoPath = path.join(img_dir, logo); const logoPath = path.join(img_dir, logo);
assert.isTrue(fs.existsSync(logoPath), `${logoPath} logo missing`); expect(fs.existsSync(logoPath)).toBe(true);
} }
describe('Language logo check', () => { describe('Language logo check', () => {

View File

@@ -23,6 +23,7 @@
// POSSIBILITY OF SUCH DAMAGE. // POSSIBILITY OF SUCH DAMAGE.
import _ from 'underscore'; import _ from 'underscore';
import {afterAll, beforeAll, describe, expect, it} from 'vitest';
import {unwrap} from '../lib/assert.js'; import {unwrap} from '../lib/assert.js';
import {languages} from '../lib/languages.js'; import {languages} from '../lib/languages.js';
@@ -34,12 +35,12 @@ describe('Live site checks', () => {
let ceProps; let ceProps;
let compilerProps; let compilerProps;
before(() => { beforeAll(() => {
properties.initialize('etc/config/', ['amazon']); properties.initialize('etc/config/', ['amazon']);
ceProps = properties.propsFor('compiler-explorer'); ceProps = properties.propsFor('compiler-explorer');
compilerProps = new properties.CompilerProps(languages, ceProps); compilerProps = new properties.CompilerProps(languages, ceProps);
}); });
after(() => { afterAll(() => {
properties.reset(); properties.reset();
}); });
@@ -65,9 +66,7 @@ describe('Live site checks', () => {
differences[lang] = difference; differences[lang] = difference;
} }
}); });
differences.should.be.eql( // One or more defined libraries are not listed on their corresponding language libs property array
{}, expect(differences).toEqual({});
'One or more defined libraries are not listed on their corresponding language libs property array',
);
}); });
}); });

View File

@@ -22,34 +22,38 @@
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE. // POSSIBILITY OF SUCH DAMAGE.
import {describe, expect, it} from 'vitest';
import {addDigitSeparator, escapeHTML} from '../shared/common-utils.js'; import {addDigitSeparator, escapeHTML} from '../shared/common-utils.js';
describe('HTML Escape Test Cases', () => { describe('HTML Escape Test Cases', () => {
it('should prevent basic injection', () => { it('should prevent basic injection', () => {
escapeHTML("<script>alert('hi');</script>").should.equal(`&lt;script&gt;alert(&#x27;hi&#x27;);&lt;/script&gt;`); expect(escapeHTML("<script>alert('hi');</script>")).toEqual(
`&lt;script&gt;alert(&#x27;hi&#x27;);&lt;/script&gt;`,
);
}); });
it('should prevent tag injection', () => { it('should prevent tag injection', () => {
escapeHTML('\'"`>').should.equal(`&#x27;&quot;&#x60;&gt;`); expect(escapeHTML('\'"`>')).toEqual(`&#x27;&quot;&#x60;&gt;`);
}); });
}); });
describe('digit separator', () => { describe('digit separator', () => {
it('handles short numbers', () => { it('handles short numbers', () => {
addDigitSeparator('42', '_', 3).should.equal('42'); expect(addDigitSeparator('42', '_', 3)).toEqual('42');
}); });
it('handles long numbers', () => { it('handles long numbers', () => {
addDigitSeparator('1234', '_', 3).should.equal('1_234'); expect(addDigitSeparator('1234', '_', 3)).toEqual('1_234');
addDigitSeparator('123456789', "'", 3).should.equal("123'456'789"); expect(addDigitSeparator('123456789', "'", 3)).toEqual("123'456'789");
addDigitSeparator('1234567890', "'", 3).should.equal("1'234'567'890"); expect(addDigitSeparator('1234567890', "'", 3)).toEqual("1'234'567'890");
}); });
it('handles hex numbers', () => { it('handles hex numbers', () => {
addDigitSeparator('AABBCCDD12345678', '_', 4).should.equal('AABB_CCDD_1234_5678'); expect(addDigitSeparator('AABBCCDD12345678', '_', 4)).toEqual('AABB_CCDD_1234_5678');
addDigitSeparator('01AABBCCDD12345678', '_', 4).should.equal('01_AABB_CCDD_1234_5678'); expect(addDigitSeparator('01AABBCCDD12345678', '_', 4)).toEqual('01_AABB_CCDD_1234_5678');
}); });
it('handles negative numbers', () => { it('handles negative numbers', () => {
addDigitSeparator('-42', '_', 3).should.equal('-42'); expect(addDigitSeparator('-42', '_', 3)).toEqual('-42');
addDigitSeparator('-420', '_', 3).should.equal('-420'); expect(addDigitSeparator('-420', '_', 3)).toEqual('-420');
addDigitSeparator('-4200', '_', 3).should.equal('-4_200'); expect(addDigitSeparator('-4200', '_', 3)).toEqual('-4_200');
addDigitSeparator('-123456789', '_', 3).should.equal('-123_456_789'); expect(addDigitSeparator('-123456789', '_', 3)).toEqual('-123_456_789');
}); });
}); });

View File

@@ -23,6 +23,8 @@
// POSSIBILITY OF SUCH DAMAGE. // POSSIBILITY OF SUCH DAMAGE.
import './utils.js'; import './utils.js';
import {beforeAll, describe, expect, it} from 'vitest';
import {CompilationEnvironment} from '../lib/compilation-env.js'; import {CompilationEnvironment} from '../lib/compilation-env.js';
import {CompilerProps, fakeProps} from '../lib/properties.js'; import {CompilerProps, fakeProps} from '../lib/properties.js';
@@ -35,41 +37,35 @@ const props = {
describe('Compilation environment', () => { describe('Compilation environment', () => {
let compilerProps; let compilerProps;
before(() => { beforeAll(() => {
compilerProps = new CompilerProps({}, fakeProps(props)); compilerProps = new CompilerProps({}, fakeProps(props));
}); });
it('Should cache by default', () => { it('Should cache by default', async () => {
// TODO: Work will need to be done here when CompilationEnvironment's constructor is typed better // TODO: Work will need to be done here when CompilationEnvironment's constructor is typed better
const ce = new CompilationEnvironment(compilerProps, undefined, undefined); const ce = new CompilationEnvironment(compilerProps, undefined, undefined);
return ce await expect(ce.cacheGet('foo')).resolves.toBeNull();
.cacheGet('foo') await ce.cachePut('foo', {res: 'bar'}, undefined);
.should.eventually.equal(null) await expect(ce.cacheGet('foo')).resolves.toEqual({res: 'bar'});
.then(() => ce.cachePut('foo', {res: 'bar'}, undefined)) await expect(ce.cacheGet('baz')).resolves.toBeNull();
.then(() => ce.cacheGet('foo').should.eventually.eql({res: 'bar'}))
.then(() => ce.cacheGet('baz').should.eventually.equal(null));
}); });
it('Should cache when asked', () => { it('Should cache when asked', async () => {
const ce = new CompilationEnvironment(compilerProps, undefined, true); const ce = new CompilationEnvironment(compilerProps, undefined, true);
return ce await expect(ce.cacheGet('foo')).resolves.toBeNull();
.cacheGet('foo') await ce.cachePut('foo', {res: 'bar'}, undefined);
.should.eventually.equal(null) await expect(ce.cacheGet('foo')).resolves.toEqual({res: 'bar'});
.then(() => ce.cachePut('foo', {res: 'bar'}, undefined))
.then(() => ce.cacheGet('foo').should.eventually.eql({res: 'bar'}));
}); });
it("Shouldn't cache when asked", () => { it("Shouldn't cache when asked", async () => {
// TODO: Work will need to be done here when CompilationEnvironment's constructor is typed better // TODO: Work will need to be done here when CompilationEnvironment's constructor is typed better
const ce = new CompilationEnvironment(compilerProps, undefined, false); const ce = new CompilationEnvironment(compilerProps, undefined, false);
return ce await expect(ce.cacheGet('foo')).resolves.toBeNull();
.cacheGet('foo') await ce.cachePut('foo', {res: 'bar'}, undefined);
.should.eventually.equal(null) await expect(ce.cacheGet('foo')).resolves.toBeNull();
.then(() => ce.cachePut('foo', {res: 'bar'}, undefined))
.then(() => ce.cacheGet('foo').should.eventually.equal(null));
}); });
it('Should filter bad options', () => { it('Should filter bad options', () => {
// TODO: Work will need to be done here when CompilationEnvironment's constructor is typed better // TODO: Work will need to be done here when CompilationEnvironment's constructor is typed better
const ce = new CompilationEnvironment(compilerProps, undefined, undefined); const ce = new CompilationEnvironment(compilerProps, undefined, undefined);
ce.findBadOptions(['-O3', '-flto']).should.be.empty; expect(ce.findBadOptions(['-O3', '-flto'])).toEqual([]);
ce.findBadOptions(['-O3', '-plugin']).should.eql(['-plugin']); expect(ce.findBadOptions(['-O3', '-plugin'])).toEqual(['-plugin']);
}); });
}); });

View File

@@ -23,6 +23,8 @@
// POSSIBILITY OF SUCH DAMAGE. // POSSIBILITY OF SUCH DAMAGE.
import './utils.js'; import './utils.js';
import {beforeAll, describe, expect, it} from 'vitest';
import {CompilerFinder} from '../lib/compiler-finder.js'; import {CompilerFinder} from '../lib/compiler-finder.js';
import {ClientOptionsHandler} from '../lib/options-handler.js'; import {ClientOptionsHandler} from '../lib/options-handler.js';
import * as properties from '../lib/properties.js'; import * as properties from '../lib/properties.js';
@@ -87,7 +89,7 @@ const supportsLibrariesOptions = {
supportsLibraries: 'fmt:catch2.2101', supportsLibraries: 'fmt:catch2.2101',
}; };
describe('Compiler-finder', function () { describe('Compiler-finder', () => {
let compilerProps: properties.CompilerProps; let compilerProps: properties.CompilerProps;
let noOptionsAtAllProps: properties.CompilerProps; let noOptionsAtAllProps: properties.CompilerProps;
@@ -98,7 +100,7 @@ describe('Compiler-finder', function () {
let optionsHandler: ClientOptionsHandler; let optionsHandler: ClientOptionsHandler;
before(() => { beforeAll(() => {
compilerProps = new properties.CompilerProps(languages, properties.fakeProps(props)); compilerProps = new properties.CompilerProps(languages, properties.fakeProps(props));
noOptionsAtAllProps = new properties.CompilerProps(languages, properties.fakeProps(noOptionsAtAll)); noOptionsAtAllProps = new properties.CompilerProps(languages, properties.fakeProps(noOptionsAtAll));
@@ -118,7 +120,7 @@ describe('Compiler-finder', function () {
} as unknown as ClientOptionsHandler; } as unknown as ClientOptionsHandler;
}); });
it('should not hang for undefined groups (Bug #860)', () => { it('should not hang for undefined groups (Bug #860)', async () => {
const finder = new CompilerFinder( const finder = new CompilerFinder(
{} as any, {} as any,
compilerProps, compilerProps,
@@ -126,7 +128,7 @@ describe('Compiler-finder', function () {
{} as any, {} as any,
optionsHandler, optionsHandler,
); );
return finder.getCompilers().should.eventually.have.lengthOf(1); await expect(finder.getCompilers()).resolves.toHaveLength(1);
}); });
it('should behave properly if no options are provided at all', async () => { it('should behave properly if no options are provided at all', async () => {
@@ -138,7 +140,7 @@ describe('Compiler-finder', function () {
optionsHandler, optionsHandler,
); );
const compilers = await finder.getCompilers(); const compilers = await finder.getCompilers();
compilers[0].options.should.equal(''); expect(compilers[0].options).toEqual('');
}); });
it('should behave properly if no base options are provided', async () => { it('should behave properly if no base options are provided', async () => {
@@ -150,7 +152,7 @@ describe('Compiler-finder', function () {
optionsHandler, optionsHandler,
); );
const compilers = await finder.getCompilers(); const compilers = await finder.getCompilers();
compilers[0].options.should.equal('bar'); expect(compilers[0].options).toEqual('bar');
}); });
it('should behave properly if only base options are provided', async () => { it('should behave properly if only base options are provided', async () => {
@@ -162,7 +164,7 @@ describe('Compiler-finder', function () {
optionsHandler, optionsHandler,
); );
const compilers = await finder.getCompilers(); const compilers = await finder.getCompilers();
compilers[0].options.should.equal('foo'); expect(compilers[0].options).toEqual('foo');
}); });
it('should behave properly if both options are provided', async () => { it('should behave properly if both options are provided', async () => {
@@ -174,7 +176,7 @@ describe('Compiler-finder', function () {
optionsHandler, optionsHandler,
); );
const compilers = await finder.getCompilers(); const compilers = await finder.getCompilers();
compilers[0].options.should.equal('foo bar'); expect(compilers[0].options).toEqual('foo bar');
}); });
it('should be able to filter libraries', async () => { it('should be able to filter libraries', async () => {
@@ -187,6 +189,6 @@ describe('Compiler-finder', function () {
); );
const compilers = await finder.getCompilers(); const compilers = await finder.getCompilers();
const libsArr = compilers[0].libsArr; const libsArr = compilers[0].libsArr;
libsArr.should.deep.equal(['fmt', 'catch2.2101']); expect(libsArr).toEqual(['fmt', 'catch2.2101']);
}); });
}); });

View File

@@ -22,6 +22,8 @@
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE. // POSSIBILITY OF SUCH DAMAGE.
import {beforeAll, describe, expect, it} from 'vitest';
import {CompilerArguments} from '../../lib/compiler-arguments.js'; import {CompilerArguments} from '../../lib/compiler-arguments.js';
import { import {
BaseParser, BaseParser,
@@ -33,21 +35,14 @@ import {
VCParser, VCParser,
} from '../../lib/compilers/argument-parsers.js'; } from '../../lib/compilers/argument-parsers.js';
import {FakeCompiler} from '../../lib/compilers/fake-for-test.js'; import {FakeCompiler} from '../../lib/compilers/fake-for-test.js';
import {makeCompilationEnvironment, should} from '../utils.js';
const languages = { const languages = {
'c++': {id: 'c++'}, 'c++': {id: 'c++'},
}; };
let env; function makeCompiler(stdout?: string, stderr?: string, code?: number) {
function makeCompiler(stdout, stderr, code) {
if (env === undefined) {
env = makeCompilationEnvironment({languages});
}
if (code === undefined) code = 0; if (code === undefined) code = 0;
const compiler = new FakeCompiler({lang: languages['c++'].id, remote: true}, env); const compiler = new FakeCompiler({lang: languages['c++'].id, remote: true}) as any;
compiler.exec = () => Promise.resolve({code: code, stdout: stdout || '', stderr: stderr || ''}); compiler.exec = () => Promise.resolve({code: code, stdout: stdout || '', stderr: stderr || ''});
compiler.execCompilerCached = compiler.exec; compiler.execCompilerCached = compiler.exec;
compiler.possibleArguments = new CompilerArguments('g82'); compiler.possibleArguments = new CompilerArguments('g82');
@@ -57,156 +52,129 @@ function makeCompiler(stdout, stderr, code) {
describe('option parser', () => { describe('option parser', () => {
it('should do nothing for the base parser', () => { it('should do nothing for the base parser', () => {
const compiler = makeCompiler(); const compiler = makeCompiler();
return BaseParser.parse(compiler).should.deep.equals(compiler); expect(BaseParser.parse(compiler)).toEqual(compiler);
}); });
it('should handle empty options', () => { it('should handle empty options', async () => {
return BaseParser.getOptions(makeCompiler()).should.eventually.deep.equals({}); await expect(BaseParser.getOptions(makeCompiler(), '')).resolves.toEqual({});
}); });
it('should parse single-dash options', () => { it('should parse single-dash options', async () => {
return BaseParser.getOptions(makeCompiler('-foo\n')).should.eventually.deep.equals({ await expect(BaseParser.getOptions(makeCompiler('-foo\n'), '')).resolves.toEqual({
'-foo': { '-foo': {
description: '', description: '',
timesused: 0, timesused: 0,
}, },
}); });
}); });
it('should parse double-dash options', () => { it('should parse double-dash options', async () => {
return BaseParser.getOptions(makeCompiler('--foo\n')).should.eventually.deep.equals({ await expect(BaseParser.getOptions(makeCompiler('--foo\n'), '')).resolves.toEqual({
'--foo': { '--foo': {
description: '', description: '',
timesused: 0, timesused: 0,
}, },
}); });
}); });
it('should parse stderr options', () => { it('should parse stderr options', async () => {
return BaseParser.getOptions(makeCompiler('', '--bar=monkey\n')).should.eventually.deep.equals({ await expect(BaseParser.getOptions(makeCompiler('', '--bar=monkey\n'), '')).resolves.toEqual({
'--bar=monkey': { '--bar=monkey': {
description: '', description: '',
timesused: 0, timesused: 0,
}, },
}); });
}); });
it('handles non-option text', () => { it('handles non-option text', async () => {
return BaseParser.getOptions( await expect(BaseParser.getOptions(makeCompiler('-foo=123\nthis is a fish\n-badger=123'), '')).resolves.toEqual(
makeCompiler('-foo=123\nthis is a fish\n-badger=123'), {
).should.eventually.deep.equals({
'-foo=123': {description: 'this is a fish', timesused: 0}, '-foo=123': {description: 'this is a fish', timesused: 0},
'-badger=123': {description: '', timesused: 0}, '-badger=123': {description: '', timesused: 0},
},
);
}); });
}); it('should ignore if errors occur', async () => {
it('should ignore if errors occur', () => { await expect(BaseParser.getOptions(makeCompiler('--foo\n', '--bar\n', 1), '')).resolves.toEqual({});
return BaseParser.getOptions(makeCompiler('--foo\n', '--bar\n', 1)).should.eventually.deep.equals({});
}); });
}); });
describe('gcc parser', () => { describe('gcc parser', () => {
it('should handle empty options', async () => { it('should handle empty options', async () => {
const result = await GCCParser.parse(makeCompiler()); const result = await GCCParser.parse(makeCompiler());
should.not.exist(result.compiler.supportsGccDump); expect(result.compiler).not.toHaveProperty('supportsGccDump');
result.compiler.options.should.equals(''); expect(result.compiler.options).toEqual('');
}); });
it('should handle options', () => { it('should handle options', async () => {
return GCCParser.parse( const result = await GCCParser.parse(makeCompiler('-masm=intel\n-fdiagnostics-color=[blah]\n-fdump-tree-all'));
makeCompiler('-masm=intel\n-fdiagnostics-color=[blah]\n-fdump-tree-all'), expect(result.compiler.supportsGccDump).toBe(true);
).should.eventually.satisfy(result => { expect(result.compiler.supportsIntel).toBe(true);
return Promise.all([ expect(result.compiler.intelAsm).toEqual('-masm=intel');
result.compiler.supportsGccDump.should.equals(true), expect(result.compiler.options).toEqual('-fdiagnostics-color=always');
result.compiler.supportsIntel.should.equals(true),
result.compiler.intelAsm.should.equals('-masm=intel'),
result.compiler.options.should.equals('-fdiagnostics-color=always'),
]);
});
});
it('should handle undefined options', () => {
return GCCParser.parse(makeCompiler('-fdiagnostics-color=[blah]')).should.eventually.satisfy(result => {
return Promise.all([result.compiler.options.should.equals('-fdiagnostics-color=always')]);
}); });
it('should handle undefined options', async () => {
const result = await GCCParser.parse(makeCompiler('-fdiagnostics-color=[blah]'));
expect(result.compiler.options).toEqual('-fdiagnostics-color=always');
}); });
}); });
describe('clang parser', () => { describe('clang parser', () => {
it('should handle empty options', () => { it('should handle empty options', async () => {
return ClangParser.parse(makeCompiler()).should.eventually.satisfy(result => { const result = await ClangParser.parse(makeCompiler());
return Promise.all([result.compiler.options.should.equals('')]); expect(result.compiler.options).toEqual('');
}); });
}); it('should handle options', async () => {
it('should handle options', () => { const result = await ClangParser.parse(
return ClangParser.parse(
makeCompiler(' -fno-crash-diagnostics\n -fsave-optimization-record\n -fcolor-diagnostics'), makeCompiler(' -fno-crash-diagnostics\n -fsave-optimization-record\n -fcolor-diagnostics'),
).should.eventually.satisfy(result => { );
return Promise.all([ expect(result.compiler.supportsOptOutput).toBe(true);
result.compiler.supportsOptOutput.should.equals(true), expect(result.compiler.optArg).toEqual('-fsave-optimization-record');
result.compiler.optArg.should.equals('-fsave-optimization-record'), expect(result.compiler.options).toContain('-fcolor-diagnostics');
expect(result.compiler.options).toContain('-fno-crash-diagnostics');
result.compiler.options.should.include('-fcolor-diagnostics'), expect(result.compiler.options).not.toContain('-fsave-optimization-record');
result.compiler.options.should.include('-fno-crash-diagnostics'),
result.compiler.options.should.not.include('-fsave-optimization-record'),
]);
});
}); });
}); });
describe('pascal parser', () => { describe('pascal parser', () => {
it('should handle empty options', () => { it('should handle empty options', async () => {
return PascalParser.parse(makeCompiler()).should.eventually.satisfy(result => { const result = await PascalParser.parse(makeCompiler());
return Promise.all([result.compiler.options.should.equals('')]); expect(result.compiler.options).toEqual('');
});
}); });
}); });
describe('popular compiler arguments', () => { describe('popular compiler arguments', () => {
let compiler; let compiler;
before(() => { beforeAll(() => {
compiler = makeCompiler( compiler = makeCompiler(
' -fsave-optimization-record\n -x\n -g\n -fcolor-diagnostics\n -O<number> Optimization level\n -std=<c++11,c++14,c++17z>', ' -fsave-optimization-record\n -x\n -g\n -fcolor-diagnostics\n -O<number> Optimization level\n -std=<c++11,c++14,c++17z>',
); );
}); });
it('should return 5 arguments', () => { it('should return 5 arguments', async () => {
return ClangParser.parse(compiler).then(compiler => { const result = await ClangParser.parse(compiler);
return compiler.should.satisfy(compiler => { expect(result.possibleArguments.getPopularArguments()).toEqual({
return Promise.all([
compiler.possibleArguments.getPopularArguments().should.deep.equal({
'-O<number>': {description: 'Optimization level', timesused: 0}, '-O<number>': {description: 'Optimization level', timesused: 0},
'-fcolor-diagnostics': {description: '', timesused: 0}, '-fcolor-diagnostics': {description: '', timesused: 0},
'-fsave-optimization-record': {description: '', timesused: 0}, '-fsave-optimization-record': {description: '', timesused: 0},
'-g': {description: '', timesused: 0}, '-g': {description: '', timesused: 0},
'-x': {description: '', timesused: 0}, '-x': {description: '', timesused: 0},
}),
]);
});
}); });
}); });
it('should return arguments except the ones excluded', () => { it('should return arguments except the ones excluded', async () => {
return ClangParser.parse(compiler).then(compiler => { const result = await ClangParser.parse(compiler);
return compiler.should.satisfy(compiler => { expect(result.possibleArguments.getPopularArguments(['-O3', '--hello'])).toEqual({
return Promise.all([
compiler.possibleArguments.getPopularArguments(['-O3', '--hello']).should.deep.equal({
'-fcolor-diagnostics': {description: '', timesused: 0}, '-fcolor-diagnostics': {description: '', timesused: 0},
'-fsave-optimization-record': {description: '', timesused: 0}, '-fsave-optimization-record': {description: '', timesused: 0},
'-g': {description: '', timesused: 0}, '-g': {description: '', timesused: 0},
'-x': {description: '', timesused: 0}, '-x': {description: '', timesused: 0},
'-std=<c++11,c++14,c++17z>': {description: '', timesused: 0}, '-std=<c++11,c++14,c++17z>': {description: '', timesused: 0},
}),
]);
});
}); });
}); });
it('should be able to exclude special params with assignments', () => { it('should be able to exclude special params with assignments', async () => {
return ClangParser.parse(compiler).then(compiler => { const result = await ClangParser.parse(compiler);
return compiler.should.satisfy(compiler => { expect(result.possibleArguments.getPopularArguments(['-std=c++14', '-g', '--hello'])).toEqual({
return Promise.all([
compiler.possibleArguments.getPopularArguments(['-std=c++14', '-g', '--hello']).should.deep.equal({
'-O<number>': {description: 'Optimization level', timesused: 0}, '-O<number>': {description: 'Optimization level', timesused: 0},
'-fcolor-diagnostics': {description: '', timesused: 0}, '-fcolor-diagnostics': {description: '', timesused: 0},
'-fsave-optimization-record': {description: '', timesused: 0}, '-fsave-optimization-record': {description: '', timesused: 0},
'-x': {description: '', timesused: 0}, '-x': {description: '', timesused: 0},
}),
]);
});
}); });
}); });
}); });
@@ -224,7 +192,7 @@ describe('VC argument parser', () => {
' /etc Etcetera', ' /etc Etcetera',
]; ];
const stdvers = VCParser.extractPossibleStdvers(lines); const stdvers = VCParser.extractPossibleStdvers(lines);
stdvers.should.deep.equal([ expect(stdvers).toEqual([
{ {
name: 'c++14: ISO/IEC 14882:2014 (default)', name: 'c++14: ISO/IEC 14882:2014 (default)',
value: 'c++14', value: 'c++14',
@@ -257,7 +225,7 @@ describe('ICC argument parser', () => {
'-etc', '-etc',
]; ];
const stdvers = ICCParser.extractPossibleStdvers(lines); const stdvers = ICCParser.extractPossibleStdvers(lines);
stdvers.should.deep.equal([ expect(stdvers).toEqual([
{ {
name: 'c99: conforms to ISO/IEC 9899:1999 standard for C programs', name: 'c99: conforms to ISO/IEC 9899:1999 standard for C programs',
value: 'c99', value: 'c99',
@@ -291,7 +259,7 @@ describe('TableGen argument parser', () => {
' --no-warn-on-unused-template-args - Disable...', ' --no-warn-on-unused-template-args - Disable...',
]; ];
const actions = TableGenParser.extractPossibleActions(lines); const actions = TableGenParser.extractPossibleActions(lines);
actions.should.deep.equal([ expect(actions).toEqual([
{name: 'gen-attrs: Generate attributes', value: '--gen-attrs'}, {name: 'gen-attrs: Generate attributes', value: '--gen-attrs'},
{name: 'print-detailed-records: Print full details...', value: '--print-detailed-records'}, {name: 'print-detailed-records: Print full details...', value: '--print-detailed-records'},
{name: 'gen-x86-mnemonic-tables: Generate X86...', value: '--gen-x86-mnemonic-tables'}, {name: 'gen-x86-mnemonic-tables: Generate X86...', value: '--gen-x86-mnemonic-tables'},

View File

@@ -22,10 +22,10 @@
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE. // POSSIBILITY OF SUCH DAMAGE.
import {ClangCompiler} from '../../lib/compilers/index.js'; import {describe, expect, it} from 'vitest';
import {chai, makeCompilationEnvironment} from '../utils.js';
const expect = chai.expect; import {ClangCompiler} from '../../lib/compilers/index.js';
import {makeCompilationEnvironment} from '../utils.js';
describe('clang tests', () => { describe('clang tests', () => {
const languages = {'c++': {id: 'c++'}}; const languages = {'c++': {id: 'c++'}};
@@ -38,7 +38,7 @@ describe('clang tests', () => {
}; };
describe('device code...', async () => { describe('device code...', async () => {
const clang = new ClangCompiler(info, makeCompilationEnvironment({languages})); const clang = new ClangCompiler(info as any, makeCompilationEnvironment({languages}));
it('Should return null for non-device code', async () => { it('Should return null for non-device code', async () => {
expect(await clang.splitDeviceCode('')).to.be.null; expect(await clang.splitDeviceCode('')).to.be.null;
expect(await clang.splitDeviceCode('mov eax, 00h\nadd r0, r0, #1\n')).to.be.null; expect(await clang.splitDeviceCode('mov eax, 00h\nadd r0, r0, #1\n')).to.be.null;

View File

@@ -22,12 +22,14 @@
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE. // POSSIBILITY OF SUCH DAMAGE.
import {describe, expect, it} from 'vitest';
import {HookCompiler} from '../../lib/compilers/index.js'; import {HookCompiler} from '../../lib/compilers/index.js';
import {makeCompilationEnvironment} from '../utils.js'; import {makeCompilationEnvironment} from '../utils.js';
describe('Hook compiler', () => { describe('Hook compiler', () => {
it('should return correct key', () => { it('should return correct key', () => {
HookCompiler.key.should.equal('hook'); expect(HookCompiler.key).toEqual('hook');
}); });
const info = { const info = {
@@ -36,20 +38,20 @@ describe('Hook compiler', () => {
lang: 'hook', lang: 'hook',
}; };
const languages = {hook: {id: 'hook'}}; const languages = {hook: {id: 'hook'}};
const hook = new HookCompiler(info, makeCompilationEnvironment({languages})); const hook = new HookCompiler(info as any, makeCompilationEnvironment({languages}));
it('should return correct options for filter', () => { it('should return correct options for filter', () => {
hook.optionsForFilter().should.deep.equal(['--dump']); expect(hook.optionsForFilter(undefined as unknown as any)).toEqual(['--dump']);
}); });
it('should return correct output filename', () => { it('should return correct output filename', () => {
const dirPath = '/tmp'; const dirPath = '/tmp';
hook.getOutputFilename(dirPath).should.equal('/tmp/example.out'); expect(hook.getOutputFilename(dirPath)).toEqual('/tmp/example.out');
}); });
it('should correctly add hook_home to the env', () => { it('should correctly add hook_home to the env', () => {
hook.addHookHome(undefined).should.deep.equal({HOOK_HOME: '/opt/hook'}); expect(hook.addHookHome(undefined)).toEqual({HOOK_HOME: '/opt/hook'});
hook.addHookHome({moo: 'moo'}).should.deep.equal({moo: 'moo', HOOK_HOME: '/opt/hook'}); expect(hook.addHookHome({moo: 'moo'})).toEqual({moo: 'moo', HOOK_HOME: '/opt/hook'});
}); });
it('should process and return correct bytecode result', async () => { it('should process and return correct bytecode result', async () => {
@@ -144,6 +146,6 @@ describe('Hook compiler', () => {
const filters = {trim: false}; const filters = {trim: false};
const result = await hook.processAsm({asm: asm}, filters, null); const result = await hook.processAsm({asm: asm}, filters, null);
delete result.parsingTime; delete result.parsingTime;
result.should.deep.equal(expected); expect(result).toEqual(expected);
}); });
}); });

View File

@@ -22,25 +22,27 @@
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE. // POSSIBILITY OF SUCH DAMAGE.
import {describe, expect, it} from 'vitest';
import * as csp from '../lib/csp.js'; import * as csp from '../lib/csp.js';
describe('CSP', () => { describe('CSP', () => {
it('Should work in the godbolt.org domain for every field', () => { it('Should work in the godbolt.org domain for every field', () => {
for (const value of Object.keys(csp.data)) { for (const value of Object.keys(csp.data)) {
csp.data[value].should.include.members(['https://*.godbolt.org', "'self'"]); expect(csp.data[value]).toEqual(expect.arrayContaining(['https://*.godbolt.org', "'self'"]));
} }
}); });
it('Should work in the compiler-explorer domain for every field', () => { it('Should work in the compiler-explorer domain for every field', () => {
for (const value of Object.keys(csp.data)) { for (const value of Object.keys(csp.data)) {
csp.data[value].should.include.members(['https://*.compiler-explorer.com', "'self'"]); expect(csp.data[value]).toEqual(expect.arrayContaining(['https://*.compiler-explorer.com', "'self'"]));
} }
}); });
it('Should work in a localhost environment for every field', () => { it('Should work in a localhost environment for every field', () => {
for (const value of Object.keys(csp.data)) { for (const value of Object.keys(csp.data)) {
csp.data[value].should.include.members(['localhost:*', "'self'"]); expect(csp.data[value]).toEqual(expect.arrayContaining(['localhost:*', "'self'"]));
} }
}); });
it('Should be a valid policy', () => { it('Should be a valid policy', () => {
csp.policy.should.be.a('string'); expect(csp.policy).toEqual(expect.any(String));
}); });
}); });

View File

@@ -22,6 +22,8 @@
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE. // POSSIBILITY OF SUCH DAMAGE.
import {beforeAll, describe, expect, it} from 'vitest';
import {DMDCompiler} from '../lib/compilers/dmd.js'; import {DMDCompiler} from '../lib/compilers/dmd.js';
import {LDCCompiler} from '../lib/compilers/ldc.js'; import {LDCCompiler} from '../lib/compilers/ldc.js';
import {LanguageKey} from '../types/languages.interfaces.js'; import {LanguageKey} from '../types/languages.interfaces.js';
@@ -44,24 +46,24 @@ describe('D', () => {
lang: languages.d.id, lang: languages.d.id,
}; };
before(() => { beforeAll(() => {
ce = makeCompilationEnvironment({languages}); ce = makeCompilationEnvironment({languages});
}); });
it('LDC should not allow -run parameter', () => { it('LDC should not allow -run parameter', () => {
const compiler = new LDCCompiler(makeFakeCompilerInfo(info), ce); const compiler = new LDCCompiler(makeFakeCompilerInfo(info), ce);
compiler.filterUserOptions(['hello', '-run', '--something']).should.deep.equal(['hello', '--something']); expect(compiler.filterUserOptions(['hello', '-run', '--something'])).toEqual(['hello', '--something']);
}); });
it('DMD should not allow -run parameter', () => { it('DMD should not allow -run parameter', () => {
const compiler = new DMDCompiler(makeFakeCompilerInfo(info), ce); const compiler = new DMDCompiler(makeFakeCompilerInfo(info), ce);
compiler.filterUserOptions(['hello', '-run', '--something']).should.deep.equal(['hello', '--something']); expect(compiler.filterUserOptions(['hello', '-run', '--something'])).toEqual(['hello', '--something']);
}); });
it('LDC supports AST output since version 1.4.0', () => { it('LDC supports AST output since version 1.4.0', () => {
const compiler = new LDCCompiler(makeFakeCompilerInfo(info), ce); const compiler = new LDCCompiler(makeFakeCompilerInfo(info), ce);
compiler.couldSupportASTDump('LDC - the LLVM D compiler (1.3.0)').should.equal(false); expect(compiler.couldSupportASTDump('LDC - the LLVM D compiler (1.3.0)')).toEqual(false);
compiler.couldSupportASTDump('LDC - the LLVM D compiler (1.4.0)').should.equal(true); expect(compiler.couldSupportASTDump('LDC - the LLVM D compiler (1.4.0)')).toEqual(true);
compiler.couldSupportASTDump('LDC - the LLVM D compiler (1.8.0git-d54d25b-dirty)').should.equal(true); expect(compiler.couldSupportASTDump('LDC - the LLVM D compiler (1.8.0git-d54d25b-dirty)')).toEqual(true);
compiler.couldSupportASTDump('LDC - the LLVM D compiler (1.10.0)').should.equal(true); expect(compiler.couldSupportASTDump('LDC - the LLVM D compiler (1.10.0)')).toEqual(true);
}); });
}); });

View File

@@ -22,6 +22,8 @@
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE. // POSSIBILITY OF SUCH DAMAGE.
import {describe, expect, it} from 'vitest';
import {unwrap} from '../lib/assert.js'; import {unwrap} from '../lib/assert.js';
import {BaseCompiler} from '../lib/base-compiler.js'; import {BaseCompiler} from '../lib/base-compiler.js';
import {CompilationEnvironment} from '../lib/compilation-env.js'; import {CompilationEnvironment} from '../lib/compilation-env.js';
@@ -32,7 +34,7 @@ import * as properties from '../lib/properties.js';
import {SymbolStore} from '../lib/symbol-store.js'; import {SymbolStore} from '../lib/symbol-store.js';
import * as utils from '../lib/utils.js'; import * as utils from '../lib/utils.js';
import {chai, fs, makeFakeCompilerInfo, path, resolvePathFromTestRoot} from './utils.js'; import {fs, makeFakeCompilerInfo, path, resolvePathFromTestRoot} from './utils.js';
const cppfiltpath = 'c++filt'; const cppfiltpath = 'c++filt';
@@ -67,8 +69,8 @@ const catchCppfiltNonexistence = err => {
} }
}; };
describe('Basic demangling', function () { describe('Basic demangling', () => {
it('One line of asm', function () { it('One line of asm', () => {
const result = { const result = {
asm: [{text: 'Hello, World!'}], asm: [{text: 'Hello, World!'}],
}; };
@@ -77,12 +79,12 @@ describe('Basic demangling', function () {
return Promise.all([ return Promise.all([
demangler.process(result).then(output => { demangler.process(result).then(output => {
output.asm[0].text.should.equal('Hello, World!'); expect(output.asm[0].text).toEqual('Hello, World!');
}), }),
]); ]);
}); });
it('One label and some asm', function () { it('One label and some asm', () => {
const result = {asm: [{text: '_Z6squarei:'}, {text: ' ret'}]}; const result = {asm: [{text: '_Z6squarei:'}, {text: ' ret'}]};
const demangler = new DummyCppDemangler(cppfiltpath, new DummyCompiler(), ['-n']); const demangler = new DummyCppDemangler(cppfiltpath, new DummyCompiler(), ['-n']);
@@ -91,14 +93,14 @@ describe('Basic demangling', function () {
demangler demangler
.process(result) .process(result)
.then(output => { .then(output => {
output.asm[0].text.should.equal('square(int):'); expect(output.asm[0].text).toEqual('square(int):');
output.asm[1].text.should.equal(' ret'); expect(output.asm[1].text).toEqual(' ret');
}) })
.catch(catchCppfiltNonexistence), .catch(catchCppfiltNonexistence),
]); ]);
}); });
it('One label and use of a label', function () { it('One label and use of a label', () => {
const result = {asm: [{text: '_Z6squarei:'}, {text: ' mov eax, $_Z6squarei'}]}; const result = {asm: [{text: '_Z6squarei:'}, {text: ' mov eax, $_Z6squarei'}]};
const demangler = new DummyCppDemangler(cppfiltpath, new DummyCompiler(), ['-n']); const demangler = new DummyCppDemangler(cppfiltpath, new DummyCompiler(), ['-n']);
@@ -107,14 +109,14 @@ describe('Basic demangling', function () {
demangler demangler
.process(result) .process(result)
.then(output => { .then(output => {
output.asm[0].text.should.equal('square(int):'); expect(output.asm[0].text).toEqual('square(int):');
output.asm[1].text.should.equal(' mov eax, $square(int)'); expect(output.asm[1].text).toEqual(' mov eax, $square(int)');
}) })
.catch(catchCppfiltNonexistence), .catch(catchCppfiltNonexistence),
]); ]);
}); });
it('Two destructors', function () { it('Two destructors', () => {
const result = { const result = {
asm: [ asm: [
{text: '_ZN6NormalD0Ev:'}, {text: '_ZN6NormalD0Ev:'},
@@ -134,14 +136,14 @@ describe('Basic demangling', function () {
return demangler return demangler
.process(result) .process(result)
.then(output => { .then(output => {
output.asm[0].text.should.equal('Normal::~Normal() [deleting destructor]:'); expect(output.asm[0].text).toEqual('Normal::~Normal() [deleting destructor]:');
output.asm[1].text.should.equal(' callq operator delete(void*)'); expect(output.asm[1].text).toEqual(' callq operator delete(void*)');
output.asm[6].text.should.equal(' jmp operator delete(void*, unsigned long)'); expect(output.asm[6].text).toEqual(' jmp operator delete(void*, unsigned long)');
}) })
.catch(catchCppfiltNonexistence); .catch(catchCppfiltNonexistence);
}); });
it('Should ignore comments (CL)', function () { it('Should ignore comments (CL)', () => {
const result = {asm: [{text: ' call ??3@YAXPEAX_K@Z ; operator delete'}]}; const result = {asm: [{text: ' call ??3@YAXPEAX_K@Z ; operator delete'}]};
const demangler = new DummyWin32Demangler(cppfiltpath, new DummyCompiler()); const demangler = new DummyWin32Demangler(cppfiltpath, new DummyCompiler());
@@ -150,10 +152,10 @@ describe('Basic demangling', function () {
demangler.collectLabels(); demangler.collectLabels();
const output = demangler.win32RawSymbols; const output = demangler.win32RawSymbols;
unwrap(output).should.deep.equal(['??3@YAXPEAX_K@Z']); expect(unwrap(output)).toEqual(['??3@YAXPEAX_K@Z']);
}); });
it('Should ignore comments (CPP)', function () { it('Should ignore comments (CPP)', () => {
const result = {asm: [{text: ' call hello ; operator delete'}]}; const result = {asm: [{text: ' call hello ; operator delete'}]};
const demangler = new DummyCppDemangler(cppfiltpath, new DummyCompiler(), ['-n']); const demangler = new DummyCppDemangler(cppfiltpath, new DummyCompiler(), ['-n']);
@@ -163,7 +165,7 @@ describe('Basic demangling', function () {
demangler.collectLabels(); demangler.collectLabels();
const output = demangler.othersymbols.listSymbols(); const output = demangler.othersymbols.listSymbols();
output.should.deep.equal(['hello']); expect(output).toEqual(['hello']);
}); });
it('Should also support ARM branch instructions', () => { it('Should also support ARM branch instructions', () => {
@@ -176,7 +178,7 @@ describe('Basic demangling', function () {
demangler.collectLabels(); demangler.collectLabels();
const output = demangler.othersymbols.listSymbols(); const output = demangler.othersymbols.listSymbols();
output.should.deep.equal(['_ZN3FooC1Ev']); expect(output).toEqual(['_ZN3FooC1Ev']);
}); });
it('Should NOT handle undecorated labels', () => { it('Should NOT handle undecorated labels', () => {
@@ -188,10 +190,10 @@ describe('Basic demangling', function () {
demangler.collectLabels(); demangler.collectLabels();
const output = demangler.win32RawSymbols; const output = demangler.win32RawSymbols;
output?.should.deep.equal([]); expect(output).toEqual([]);
}); });
it('Should ignore comments after jmps', function () { it('Should ignore comments after jmps', () => {
const result = {asm: [{text: ' jmp _Z1fP6mytype # TAILCALL'}]}; const result = {asm: [{text: ' jmp _Z1fP6mytype # TAILCALL'}]};
const demangler = new DummyCppDemangler(cppfiltpath, new DummyCompiler(), ['-n']); const demangler = new DummyCppDemangler(cppfiltpath, new DummyCompiler(), ['-n']);
@@ -201,10 +203,10 @@ describe('Basic demangling', function () {
demangler.collectLabels(); demangler.collectLabels();
const output = demangler.othersymbols.listSymbols(); const output = demangler.othersymbols.listSymbols();
output.should.deep.equal(['_Z1fP6mytype']); expect(output).toEqual(['_Z1fP6mytype']);
}); });
it('Should still work with normal jmps', function () { it('Should still work with normal jmps', () => {
const result = {asm: [{text: ' jmp _Z1fP6mytype'}]}; const result = {asm: [{text: ' jmp _Z1fP6mytype'}]};
const demangler = new DummyCppDemangler(cppfiltpath, new DummyCompiler(), ['-n']); const demangler = new DummyCppDemangler(cppfiltpath, new DummyCompiler(), ['-n']);
@@ -214,10 +216,10 @@ describe('Basic demangling', function () {
demangler.collectLabels(); demangler.collectLabels();
const output = demangler.othersymbols.listSymbols(); const output = demangler.othersymbols.listSymbols();
output.should.deep.equal(['_Z1fP6mytype']); expect(output).toEqual(['_Z1fP6mytype']);
}); });
it('Should support CUDA PTX', function () { it('Should support CUDA PTX', () => {
const result = { const result = {
asm: [ asm: [
{text: ' .visible .entry _Z6squarePii('}, {text: ' .visible .entry _Z6squarePii('},
@@ -236,20 +238,20 @@ describe('Basic demangling', function () {
demangler demangler
.process(result) .process(result)
.then(output => { .then(output => {
output.asm[0].text.should.equal(' .visible .entry square(int*, int)('); expect(output.asm[0].text).toEqual(' .visible .entry square(int*, int)(');
output.asm[1].text.should.equal(' .param .u64 square(int*, int)_param_0,'); expect(output.asm[1].text).toEqual(' .param .u64 square(int*, int)_param_0,');
output.asm[2].text.should.equal(' ld.param.u64 %rd1, [square(int*, int)_param_0];'); expect(output.asm[2].text).toEqual(' ld.param.u64 %rd1, [square(int*, int)_param_0];');
output.asm[3].text.should.equal(' .func (.param .b32 func_retval0) cube(int*, int)('); expect(output.asm[3].text).toEqual(' .func (.param .b32 func_retval0) cube(int*, int)(');
output.asm[4].text.should.equal('.global .attribute(.managed) .align 4 .b8 ns::mymanaged[16];'); expect(output.asm[4].text).toEqual('.global .attribute(.managed) .align 4 .b8 ns::mymanaged[16];');
output.asm[5].text.should.equal('.global .texref ns::texRef;'); expect(output.asm[5].text).toEqual('.global .texref ns::texRef;');
output.asm[6].text.should.equal('.const .align 8 .u64 ns::mystr = generic($str);'); expect(output.asm[6].text).toEqual('.const .align 8 .u64 ns::mystr = generic($str);');
}) })
.catch(catchCppfiltNonexistence), .catch(catchCppfiltNonexistence),
]); ]);
}); });
}); });
async function readResultFile(filename) { async function readResultFile(filename: string) {
const data = await fs.readFile(filename); const data = await fs.readFile(filename);
const asm = utils.splitLines(data.toString()).map(line => { const asm = utils.splitLines(data.toString()).map(line => {
return {text: line}; return {text: line};
@@ -258,24 +260,17 @@ async function readResultFile(filename) {
return {asm}; return {asm};
} }
async function DoDemangleTest(filename) { async function DoDemangleTest(filename: string) {
const resultIn = await readResultFile(filename); const resultIn = await readResultFile(filename);
const resultOut = await readResultFile(filename + '.demangle'); const resultOut = await readResultFile(filename + '.demangle');
const demangler = new DummyCppDemangler(cppfiltpath, new DummyCompiler(), ['-n']); const demangler = new DummyCppDemangler(cppfiltpath, new DummyCompiler(), ['-n']);
return demangler.process(resultIn).should.eventually.deep.equal(resultOut); await expect(demangler.process(resultIn)).resolves.toEqual(resultOut);
} }
describe('File demangling', () => { if (process.platform === 'linux') {
if (process.platform !== 'linux') { describe('File demangling', () => {
it('Should be skipped', done => {
done();
});
return;
}
const testcasespath = resolvePathFromTestRoot('demangle-cases'); const testcasespath = resolvePathFromTestRoot('demangle-cases');
/* /*
@@ -297,7 +292,8 @@ describe('File demangling', () => {
}); });
} }
} }
}); });
}
describe('Demangler prefix tree', () => { describe('Demangler prefix tree', () => {
const replacements = new PrefixTree([]); const replacements = new PrefixTree([]);
@@ -305,38 +301,40 @@ describe('Demangler prefix tree', () => {
replacements.add('aa', 'long_a'); replacements.add('aa', 'long_a');
replacements.add('aa_shouldnotmatch', 'ERROR'); replacements.add('aa_shouldnotmatch', 'ERROR');
it('should replace a short match', () => { it('should replace a short match', () => {
replacements.replaceAll('a').should.eq('short_a'); expect(replacements.replaceAll('a')).toEqual('short_a');
}); });
it('should replace using the longest match', () => { it('should replace using the longest match', () => {
replacements.replaceAll('aa').should.eq('long_a'); expect(replacements.replaceAll('aa')).toEqual('long_a');
}); });
it('should replace using both', () => { it('should replace using both', () => {
replacements.replaceAll('aaa').should.eq('long_ashort_a'); expect(replacements.replaceAll('aaa')).toEqual('long_ashort_a');
}); });
it('should replace using both', () => { it('should replace using both', () => {
replacements.replaceAll('a aa a aa').should.eq('short_a long_a short_a long_a'); expect(replacements.replaceAll('a aa a aa')).toEqual('short_a long_a short_a long_a');
}); });
it('should work with empty replacements', () => { it('should work with empty replacements', () => {
new PrefixTree([]).replaceAll('Testing 123').should.eq('Testing 123'); expect(new PrefixTree([]).replaceAll('Testing 123')).toEqual('Testing 123');
}); });
it('should leave unmatching text alone', () => { it('should leave unmatching text alone', () => {
replacements expect(replacements.replaceAll('Some text with none of the first letter of the ordered letter list')).toEqual(
.replaceAll('Some text with none of the first letter of the ordered letter list') 'Some text with none of the first letter of the ordered letter list',
.should.eq('Some text with none of the first letter of the ordered letter list'); );
}); });
it('should handle a mixture', () => { it('should handle a mixture', () => {
replacements.replaceAll('Everyone loves an aardvark').should.eq('Everyone loves short_an long_ardvshort_ark'); expect(replacements.replaceAll('Everyone loves an aardvark')).toEqual(
'Everyone loves short_an long_ardvshort_ark',
);
}); });
it('should find exact matches', () => { it('should find exact matches', () => {
unwrap(replacements.findExact('a')).should.eq('short_a'); expect(unwrap(replacements.findExact('a'))).toEqual('short_a');
unwrap(replacements.findExact('aa')).should.eq('long_a'); expect(unwrap(replacements.findExact('aa'))).toEqual('long_a');
unwrap(replacements.findExact('aa_shouldnotmatch')).should.eq('ERROR'); expect(unwrap(replacements.findExact('aa_shouldnotmatch'))).toEqual('ERROR');
}); });
it('should find not find mismatches', () => { it('should find not find mismatches', () => {
chai.expect(replacements.findExact('aaa')).to.be.null; expect(replacements.findExact('aaa')).toBeNull();
chai.expect(replacements.findExact(' aa')).to.be.null; expect(replacements.findExact(' aa')).toBeNull();
chai.expect(replacements.findExact(' a')).to.be.null; expect(replacements.findExact(' a')).toBeNull();
chai.expect(replacements.findExact('Oh noes')).to.be.null; expect(replacements.findExact('Oh noes')).toBeNull();
chai.expect(replacements.findExact('')).to.be.null; expect(replacements.findExact('')).toBeNull();
}); });
}); });

View File

@@ -24,33 +24,29 @@
import path from 'path'; import path from 'path';
import {assert} from '../lib/assert.js'; import {afterAll, beforeAll, describe, expect, it} from 'vitest';
import * as exec from '../lib/exec.js'; import * as exec from '../lib/exec.js';
import * as props from '../lib/properties.js'; import * as props from '../lib/properties.js';
import {UnprocessedExecResult} from '../types/execution/execution.interfaces.js'; import {UnprocessedExecResult} from '../types/execution/execution.interfaces.js';
import {chai} from './utils.js'; async function testExecOutput(execPromise: Promise<Partial<UnprocessedExecResult>>) {
const x = await execPromise;
const expect = chai.expect; expect(x.filenameTransform).toBeInstanceOf(Function);
function testExecOutput(x: Partial<UnprocessedExecResult>) {
// Work around chai not being able to deepEquals with a function
x.filenameTransform!.should.be.a('function');
delete x.filenameTransform; delete x.filenameTransform;
delete x.execTime; delete x.execTime;
return x; return x;
} }
describe('Execution tests', () => { describe('Execution tests', async () => {
if (process.platform === 'win32') { if (process.platform === 'win32') {
// win32 // win32
describe('Executes external commands', () => { describe('Executes external commands', () => {
// note: we use powershell, since echo is a builtin, and false doesn't exist // note: we use powershell, since echo is a builtin, and false doesn't exist
it('supports output', () => { it('supports output', async () => {
return exec await expect(
.execute('powershell', ['-Command', 'echo "hello world"'], {}) testExecOutput(exec.execute('powershell', ['-Command', 'echo "hello world"'], {})),
.then(testExecOutput) ).resolves.toEqual({
.should.eventually.deep.equals({
code: 0, code: 0,
okToCache: true, okToCache: true,
stderr: '', stderr: '',
@@ -59,13 +55,14 @@ describe('Execution tests', () => {
timedOut: false, timedOut: false,
}); });
}); });
it('limits output', () => { it('limits output', async () => {
return exec await expect(
.execute('powershell', ['-Command', 'echo "A very very very very very long string"'], { testExecOutput(
exec.execute('powershell', ['-Command', 'echo "A very very very very very long string"'], {
maxOutput: 10, maxOutput: 10,
}) }),
.then(testExecOutput) ),
.should.eventually.deep.equals({ ).resolves.toEqual({
code: 0, code: 0,
okToCache: true, okToCache: true,
stderr: '', stderr: '',
@@ -74,11 +71,10 @@ describe('Execution tests', () => {
timedOut: false, timedOut: false,
}); });
}); });
it('handles failing commands', () => { it('handles failing commands', async () => {
return exec await expect(
.execute('powershell', ['-Command', 'function Fail { exit 1 }; Fail'], {}) testExecOutput(exec.execute('powershell', ['-Command', 'function Fail { exit 1 }; Fail'], {})),
.then(testExecOutput) ).resolves.toEqual({
.should.eventually.deep.equals({
code: 1, code: 1,
okToCache: true, okToCache: true,
stderr: '', stderr: '',
@@ -87,11 +83,10 @@ describe('Execution tests', () => {
timedOut: false, timedOut: false,
}); });
}); });
it('handles timouts', () => { it('handles timouts', async () => {
return exec await expect(
.execute('powershell', ['-Command', '"sleep 5"'], {timeoutMs: 10}) testExecOutput(exec.execute('powershell', ['-Command', '"sleep 5"'], {timeoutMs: 10})),
.then(testExecOutput) ).resolves.toEqual({
.should.eventually.deep.equals({
code: 1, code: 1,
okToCache: false, okToCache: false,
stderr: '\nKilled - processing time exceeded\n', stderr: '\nKilled - processing time exceeded\n',
@@ -100,15 +95,15 @@ describe('Execution tests', () => {
timedOut: true, timedOut: true,
}); });
}); });
it('handles missing executables', () => { it('handles missing executables', async () => {
return exec.execute('__not_a_command__', [], {}).should.be.rejectedWith('ENOENT'); await expect(exec.execute('__not_a_command__', [], {})).rejects.toThrow('ENOENT');
}); });
}); });
} else { } else {
// POSIX // POSIX
describe('Executes external commands', () => { describe('Executes external commands', () => {
it('supports output', () => { it('supports output', async () => {
return exec.execute('echo', ['hello', 'world'], {}).then(testExecOutput).should.eventually.deep.equals({ await expect(testExecOutput(exec.execute('echo', ['hello', 'world'], {}))).resolves.toEqual({
code: 0, code: 0,
okToCache: true, okToCache: true,
stderr: '', stderr: '',
@@ -117,11 +112,10 @@ describe('Execution tests', () => {
timedOut: false, timedOut: false,
}); });
}); });
it('limits output', () => { it('limits output', async () => {
return exec return expect(
.execute('echo', ['A very very very very very long string'], {maxOutput: 22}) testExecOutput(exec.execute('echo', ['A very very very very very long string'], {maxOutput: 22})),
.then(testExecOutput) ).resolves.toEqual({
.should.eventually.deep.equals({
code: 0, code: 0,
okToCache: true, okToCache: true,
stderr: '', stderr: '',
@@ -130,8 +124,8 @@ describe('Execution tests', () => {
timedOut: false, timedOut: false,
}); });
}); });
it('handles failing commands', () => { it('handles failing commands', async () => {
return exec.execute('false', [], {}).then(testExecOutput).should.eventually.deep.equals({ await expect(testExecOutput(exec.execute('false', [], {}))).resolves.toEqual({
code: 1, code: 1,
okToCache: true, okToCache: true,
stderr: '', stderr: '',
@@ -140,11 +134,8 @@ describe('Execution tests', () => {
timedOut: false, timedOut: false,
}); });
}); });
it('handles timouts', () => { it('handles timouts', async () => {
return exec await expect(testExecOutput(exec.execute('sleep', ['5'], {timeoutMs: 10}))).resolves.toEqual({
.execute('sleep', ['5'], {timeoutMs: 10})
.then(testExecOutput)
.should.eventually.deep.equals({
code: -1, code: -1,
okToCache: false, okToCache: false,
stderr: '\nKilled - processing time exceeded\n', stderr: '\nKilled - processing time exceeded\n',
@@ -153,14 +144,11 @@ describe('Execution tests', () => {
timedOut: true, timedOut: true,
}); });
}); });
it('handles missing executables', () => { it('handles missing executables', async () => {
return exec.execute('__not_a_command__', [], {}).should.be.rejectedWith('ENOENT'); await expect(exec.execute('__not_a_command__', [], {})).rejects.toThrow('ENOENT');
}); });
it('handles input', () => { it('handles input', async () => {
return exec await expect(testExecOutput(exec.execute('cat', [], {input: 'this is stdin'}))).resolves.toEqual({
.execute('cat', [], {input: 'this is stdin'})
.then(testExecOutput)
.should.eventually.deep.equals({
code: 0, code: 0,
okToCache: true, okToCache: true,
stderr: '', stderr: '',
@@ -173,10 +161,10 @@ describe('Execution tests', () => {
} }
describe('nsjail unit tests', () => { describe('nsjail unit tests', () => {
before(() => { beforeAll(() => {
props.initialize(path.resolve('./test/test-properties/execution'), ['test']); props.initialize(path.resolve('./test/test-properties/execution'), ['test']);
}); });
after(() => { afterAll(() => {
props.reset(); props.reset();
}); });
it('should handle simple cases', () => { it('should handle simple cases', () => {
@@ -186,7 +174,7 @@ describe('Execution tests', () => {
['1', '2', '3'], ['1', '2', '3'],
{}, {},
); );
args.should.deep.equals([ expect(args).toEqual([
'--config', '--config',
exec.getNsJailCfgFilePath('sandbox'), exec.getNsJailCfgFilePath('sandbox'),
'--env=HOME=/app', '--env=HOME=/app',
@@ -196,7 +184,7 @@ describe('Execution tests', () => {
'2', '2',
'3', '3',
]); ]);
options.should.deep.equals({}); expect(options).toEqual({});
expect(filenameTransform).to.be.undefined; expect(filenameTransform).to.be.undefined;
}); });
it('should pass through options', () => { it('should pass through options', () => {
@@ -204,10 +192,10 @@ describe('Execution tests', () => {
timeoutMs: 42, timeoutMs: 42,
maxOutput: -1, maxOutput: -1,
}).options; }).options;
options.should.deep.equals({timeoutMs: 42, maxOutput: -1}); expect(options).toEqual({timeoutMs: 42, maxOutput: -1});
}); });
it('should not pass through unknown configs', () => { it('should not pass through unknown configs', () => {
expect(() => exec.getNsJailOptions('custom-config', '/path/to/compiler', ['1', '2', '3'], {})).to.throw(); expect(() => exec.getNsJailOptions('custom-config', '/path/to/compiler', ['1', '2', '3'], {})).toThrow();
}); });
it('should remap paths when using customCwd', () => { it('should remap paths when using customCwd', () => {
const {args, options, filenameTransform} = exec.getNsJailOptions( const {args, options, filenameTransform} = exec.getNsJailOptions(
@@ -216,7 +204,7 @@ describe('Execution tests', () => {
['/some/custom/cwd/file', '/not/custom/file'], ['/some/custom/cwd/file', '/not/custom/file'],
{customCwd: '/some/custom/cwd'}, {customCwd: '/some/custom/cwd'},
); );
args.should.deep.equals([ expect(args).toEqual([
'--config', '--config',
exec.getNsJailCfgFilePath('sandbox'), exec.getNsJailCfgFilePath('sandbox'),
'--cwd', '--cwd',
@@ -229,42 +217,43 @@ describe('Execution tests', () => {
'/app/file', '/app/file',
'/not/custom/file', '/not/custom/file',
]); ]);
options.should.deep.equals({}); expect(options).toEqual({});
expect(filenameTransform).to.not.be.undefined; expect(filenameTransform).toBeTruthy();
assert(filenameTransform); if (filenameTransform) {
filenameTransform('moo').should.equal('moo'); expect(filenameTransform('moo')).toEqual('moo');
filenameTransform('/some/custom/cwd/file').should.equal('/app/file'); expect(filenameTransform('/some/custom/cwd/file')).toEqual('/app/file');
}
}); });
it('should handle timeouts', () => { it('should handle timeouts', () => {
const args = exec.getNsJailOptions('sandbox', '/path/to/compiler', [], {timeoutMs: 1234}).args; const args = exec.getNsJailOptions('sandbox', '/path/to/compiler', [], {timeoutMs: 1234}).args;
args.should.include('--time_limit=2'); expect(args).toContain('--time_limit=2');
}); });
it('should handle linker paths', () => { it('should handle linker paths', () => {
const {args, options} = exec.getNsJailOptions('sandbox', '/path/to/compiler', [], { const {args, options} = exec.getNsJailOptions('sandbox', '/path/to/compiler', [], {
ldPath: ['/a/lib/path', '/b/lib2'], ldPath: ['/a/lib/path', '/b/lib2'],
}); });
options.should.deep.equals({}); expect(options).toEqual({});
if (process.platform === 'win32') { if (process.platform === 'win32') {
args.should.include('--env=LD_LIBRARY_PATH=/a/lib/path;/b/lib2'); expect(args).toContain('--env=LD_LIBRARY_PATH=/a/lib/path;/b/lib2');
} else { } else {
args.should.include('--env=LD_LIBRARY_PATH=/a/lib/path:/b/lib2'); expect(args).toContain('--env=LD_LIBRARY_PATH=/a/lib/path:/b/lib2');
} }
}); });
it('should handle envs', () => { it('should handle envs', () => {
const {args, options} = exec.getNsJailOptions('sandbox', '/path/to/compiler', [], { const {args, options} = exec.getNsJailOptions('sandbox', '/path/to/compiler', [], {
env: {ENV1: '1', ENV2: '2'}, env: {ENV1: '1', ENV2: '2'},
}); });
options.should.deep.equals({}); expect(options).toEqual({});
args.should.include('--env=ENV1=1'); expect(args).toContain('--env=ENV1=1');
args.should.include('--env=ENV2=2'); expect(args).toContain('--env=ENV2=2');
}); });
}); });
describe('cewrapper unit tests', () => { describe('cewrapper unit tests', () => {
before(() => { beforeAll(() => {
props.initialize(path.resolve('./test/test-properties/execution'), ['test']); props.initialize(path.resolve('./test/test-properties/execution'), ['test']);
}); });
after(() => { afterAll(() => {
props.reset(); props.reset();
}); });
it('passed as arguments', () => { it('passed as arguments', () => {
@@ -276,29 +265,29 @@ describe('Execution tests', () => {
}, },
}); });
options.args.should.deep.equals([ expect(options.args).toEqual([
'--config=' + path.resolve('etc/cewrapper/user-execution.json'), '--config=' + path.resolve('etc/cewrapper/user-execution.json'),
'--time_limit=1', '--time_limit=1',
'/path/to/something', '/path/to/something',
'--help', '--help',
]); ]);
options.options.should.deep.equals({timeoutMs: 42, maxOutput: -1, env: {TEST: 'Hello, World!'}}); expect(options.options).toEqual({timeoutMs: 42, maxOutput: -1, env: {TEST: 'Hello, World!'}});
}); });
}); });
describe('Subdirectory execution', () => { describe('Subdirectory execution', () => {
before(() => { beforeAll(() => {
props.initialize(path.resolve('./test/test-properties/execution'), ['test']); props.initialize(path.resolve('./test/test-properties/execution'), ['test']);
}); });
after(() => { afterAll(() => {
props.reset(); props.reset();
}); });
it('Normal situation without customCwd', () => { it('Normal situation without customCwd', () => {
const {args, options} = exec.getSandboxNsjailOptions('/tmp/hellow/output.s', [], {}); const {args, options} = exec.getSandboxNsjailOptions('/tmp/hellow/output.s', [], {});
options.should.deep.equals({}); expect(options).toEqual({});
args.should.deep.equals([ expect(args).toEqual([
'--config', '--config',
'etc/nsjail/sandbox.cfg', 'etc/nsjail/sandbox.cfg',
'--cwd', '--cwd',
@@ -316,8 +305,8 @@ describe('Execution tests', () => {
customCwd: '/tmp/hellow', customCwd: '/tmp/hellow',
}); });
options.should.deep.equals({}); expect(options).toEqual({});
args.should.deep.equals([ expect(args).toEqual([
'--config', '--config',
'etc/nsjail/sandbox.cfg', 'etc/nsjail/sandbox.cfg',
'--cwd', '--cwd',
@@ -335,8 +324,8 @@ describe('Execution tests', () => {
env: {SOME_DOTNET_THING: '/tmp/hellow/dotnet'}, env: {SOME_DOTNET_THING: '/tmp/hellow/dotnet'},
}); });
options.should.deep.equals({}); expect(options).toEqual({});
args.should.deep.equals([ expect(args).toEqual([
'--config', '--config',
'etc/nsjail/sandbox.cfg', 'etc/nsjail/sandbox.cfg',
'--cwd', '--cwd',
@@ -356,8 +345,8 @@ describe('Execution tests', () => {
env: {CXX_FLAGS: '-L/usr/lib -L/tmp/hellow/curl/lib -L/tmp/hellow/fmt/lib'}, env: {CXX_FLAGS: '-L/usr/lib -L/tmp/hellow/curl/lib -L/tmp/hellow/fmt/lib'},
}); });
options.should.deep.equals({}); expect(options).toEqual({});
args.should.deep.equals([ expect(args).toEqual([
'--config', '--config',
'etc/nsjail/sandbox.cfg', 'etc/nsjail/sandbox.cfg',
'--cwd', '--cwd',
@@ -377,8 +366,8 @@ describe('Execution tests', () => {
ldPath: ['/usr/lib', '', '/tmp/hellow/lib'], ldPath: ['/usr/lib', '', '/tmp/hellow/lib'],
}); });
options.should.deep.equals({}); expect(options).toEqual({});
args.should.deep.equals([ expect(args).toEqual([
'--config', '--config',
'etc/nsjail/sandbox.cfg', 'etc/nsjail/sandbox.cfg',
'--cwd', '--cwd',
@@ -397,9 +386,9 @@ describe('Execution tests', () => {
customCwd: '/tmp/hellow', customCwd: '/tmp/hellow',
}); });
options.should.deep.equals({}); expect(options).toEqual({});
if (process.platform !== 'win32') { if (process.platform !== 'win32') {
args.should.deep.equals([ expect(args).toEqual([
'--config', '--config',
'etc/nsjail/sandbox.cfg', 'etc/nsjail/sandbox.cfg',
'--cwd', '--cwd',
@@ -419,11 +408,11 @@ describe('Execution tests', () => {
appHome: '/tmp/hellow', appHome: '/tmp/hellow',
}); });
options.should.deep.equals({ expect(options).toEqual({
appHome: '/tmp/hellow', appHome: '/tmp/hellow',
}); });
if (process.platform !== 'win32') { if (process.platform !== 'win32') {
args.should.deep.equals([ expect(args).toEqual([
'--config', '--config',
'etc/nsjail/execute.cfg', 'etc/nsjail/execute.cfg',
'--cwd', '--cwd',
@@ -457,11 +446,11 @@ describe('Execution tests', () => {
}, },
); );
options.should.deep.equals({ expect(options).toEqual({
appHome: '/tmp/hellow', appHome: '/tmp/hellow',
}); });
if (process.platform !== 'win32') { if (process.platform !== 'win32') {
args.should.deep.equals([ expect(args).toEqual([
'--config', '--config',
'etc/nsjail/execute.cfg', 'etc/nsjail/execute.cfg',
'--cwd', '--cwd',

View File

@@ -24,25 +24,27 @@
import path from 'path'; import path from 'path';
import approvals from 'approvals'; import {configure, verifyAsJSON} from 'approvals';
import type {ApprovalFailureReporter} from 'approvals/lib/Core/ApprovalFailureReporter.js';
import {beforeAll, describe, expect, it} from 'vitest';
import {CC65AsmParser} from '../lib/parsers/asm-parser-cc65.js'; import {CC65AsmParser} from '../lib/parsers/asm-parser-cc65.js';
import {AsmEWAVRParser} from '../lib/parsers/asm-parser-ewavr.js'; import {AsmEWAVRParser} from '../lib/parsers/asm-parser-ewavr.js';
import {SassAsmParser} from '../lib/parsers/asm-parser-sass.js'; import {SassAsmParser} from '../lib/parsers/asm-parser-sass.js';
import {VcAsmParser} from '../lib/parsers/asm-parser-vc.js'; import {VcAsmParser} from '../lib/parsers/asm-parser-vc.js';
import {AsmParser} from '../lib/parsers/asm-parser.js'; import {AsmParser} from '../lib/parsers/asm-parser.js';
import {fakeProps} from '../lib/properties.js';
import {ParseFiltersAndOutputOptions} from '../types/features/filters.interfaces.js';
import {fs, resolvePathFromTestRoot} from './utils.js'; import {fs, resolvePathFromTestRoot} from './utils.js';
approvals.mocha(resolvePathFromTestRoot('filters-cases')); function processAsm(filename: string, filters: ParseFiltersAndOutputOptions) {
function processAsm(filename, filters) {
const file = fs.readFileSync(filename, 'utf8'); const file = fs.readFileSync(filename, 'utf8');
let parser; let parser: AsmParser;
if (file.includes('Microsoft')) parser = new VcAsmParser(); if (file.includes('Microsoft')) parser = new VcAsmParser();
else if (filename.includes('sass-')) parser = new SassAsmParser(); else if (filename.includes('sass-')) parser = new SassAsmParser();
else if (filename.includes('cc65-')) parser = new CC65AsmParser(); else if (filename.includes('cc65-')) parser = new CC65AsmParser(fakeProps({}));
else if (filename.includes('ewarm-')) parser = new AsmEWAVRParser(); else if (filename.includes('ewarm-')) parser = new AsmEWAVRParser(fakeProps({}));
else { else {
parser = new AsmParser(); parser = new AsmParser();
parser.binaryHideFuncRe = parser.binaryHideFuncRe =
@@ -65,36 +67,55 @@ const optionsOverride = {
errorOnStaleApprovedFiles: process.platform !== 'win32', errorOnStaleApprovedFiles: process.platform !== 'win32',
}; };
function testFilter(filename, suffix, filters) { function testFilter(filename: string, suffix: string, filters: ParseFiltersAndOutputOptions) {
const testName = path.basename(filename + suffix); const testName = path.basename(filename + suffix);
it(testName, () => { it(
testName,
() => {
const result = processAsm(filename, filters); const result = processAsm(filename, filters);
delete result.parsingTime; delete result.parsingTime;
delete result.filteredCount; delete result.filteredCount;
approvals.verifyAsJSON(casesRoot, testName, result, optionsOverride); verifyAsJSON(casesRoot, testName, result, optionsOverride);
}).timeout(10000); // Bump the timeout a bit so that we don't fail for slow cases },
{timeout: 10000},
); // Bump the timeout a bit so that we don't fail for slow cases
}
class VitestReporter implements ApprovalFailureReporter {
name: string = 'VitestReporter';
canReportOn() {
return true;
}
report(approvedFilePath: string, receivedFilePath: string) {
const approvedText = fs.readFileSync(approvedFilePath).toString();
const receivedText = fs.readFileSync(receivedFilePath).toString();
expect(receivedText).toBe(approvedText);
}
} }
/* /*
The before() hooks on mocha are for it()s - They don't execute before the describes! The before() hooks on mocha are for it()s - They don't execute before the describes!
That's sad because then we can't have cases be loaded in a before() for every describe child to see. That's sad because then we can't have cases be loaded in a before() for every describe child to see.
*/ */
describe('Filter test cases', function () { describe('Filter test cases', () => {
describe('No filters', function () { beforeAll(() => configure({reporters: [new VitestReporter()]}));
describe('No filters', () => {
for (const x of cases) testFilter(x, '.none', {}); for (const x of cases) testFilter(x, '.none', {});
}); });
describe('Directive filters', function () { describe('Directive filters', () => {
for (const x of cases) testFilter(x, '.directives', {directives: true}); for (const x of cases) testFilter(x, '.directives', {directives: true});
}); });
describe('Directives and labels together', function () { describe('Directives and labels together', () => {
for (const x of cases) testFilter(x, '.directives.labels', {directives: true, labels: true}); for (const x of cases) testFilter(x, '.directives.labels', {directives: true, labels: true});
}); });
describe('Directives, labels and comments', function () { describe('Directives, labels and comments', () => {
for (const x of cases) { for (const x of cases) {
testFilter(x, '.directives.labels.comments', {directives: true, labels: true, commentOnly: true}); testFilter(x, '.directives.labels.comments', {directives: true, labels: true, commentOnly: true});
} }
}); });
describe('Binary, directives, labels and comments', function () { describe('Binary, directives, labels and comments', () => {
if (process.platform !== 'win32') { if (process.platform !== 'win32') {
for (const x of cases) { for (const x of cases) {
testFilter(x, '.binary.directives.labels.comments', { testFilter(x, '.binary.directives.labels.comments', {
@@ -106,7 +127,7 @@ describe('Filter test cases', function () {
} }
} }
}); });
describe('Binary, directives, labels, comments and library code', function () { describe('Binary, directives, labels, comments and library code', () => {
if (process.platform !== 'win32') { if (process.platform !== 'win32') {
for (const x of cases) { for (const x of cases) {
if (!x.endsWith('-bin.asm')) continue; if (!x.endsWith('-bin.asm')) continue;
@@ -121,7 +142,7 @@ describe('Filter test cases', function () {
} }
} }
}); });
describe('Binary, directives, labels, comments and library code with dontMaskFilenames', function () { describe('Binary, directives, labels, comments and library code with dontMaskFilenames', () => {
if (process.platform !== 'win32') { if (process.platform !== 'win32') {
for (const x of cases) { for (const x of cases) {
if (!x.endsWith('-bin.asm')) continue; if (!x.endsWith('-bin.asm')) continue;
@@ -137,13 +158,13 @@ describe('Filter test cases', function () {
} }
} }
}); });
describe('Directives and comments', function () { describe('Directives and comments', () => {
for (const x of cases) testFilter(x, '.directives.comments', {directives: true, commentOnly: true}); for (const x of cases) testFilter(x, '.directives.comments', {directives: true, commentOnly: true});
}); });
describe('Directives and library code', function () { describe('Directives and library code', () => {
for (const x of cases) testFilter(x, '.directives.library', {directives: true, libraryCode: true}); for (const x of cases) testFilter(x, '.directives.library', {directives: true, libraryCode: true});
}); });
describe('Directives, labels, comments and library code', function () { describe('Directives, labels, comments and library code', () => {
for (const x of cases) { for (const x of cases) {
testFilter(x, '.directives.labels.comments.library', { testFilter(x, '.directives.labels.comments.library', {
directives: true, directives: true,
@@ -153,7 +174,7 @@ describe('Filter test cases', function () {
}); });
} }
}); });
describe('Directives, labels, comments and library code with dontMaskFilenames', function () { describe('Directives, labels, comments and library code with dontMaskFilenames', () => {
for (const x of cases) { for (const x of cases) {
testFilter(x, '.directives.labels.comments.library.dontMaskFilenames', { testFilter(x, '.directives.labels.comments.library.dontMaskFilenames', {
directives: true, directives: true,
@@ -169,21 +190,21 @@ describe('Filter test cases', function () {
describe('AsmParser tests', () => { describe('AsmParser tests', () => {
const parser = new AsmParser(); const parser = new AsmParser();
it('should identify generic opcodes', () => { it('should identify generic opcodes', () => {
parser.hasOpcode(' mov r0, #1').should.be.true; expect(parser.hasOpcode(' mov r0, #1')).toBe(true);
parser.hasOpcode(' ROL A').should.be.true; expect(parser.hasOpcode(' ROL A')).toBe(true);
}); });
it('should not identify non-opcodes as opcodes', () => { it('should not identify non-opcodes as opcodes', () => {
parser.hasOpcode(' ;mov r0, #1').should.be.false; expect(parser.hasOpcode(' ;mov r0, #1')).toBe(false);
parser.hasOpcode('').should.be.false; expect(parser.hasOpcode('')).toBe(false);
parser.hasOpcode('# moose').should.be.false; expect(parser.hasOpcode('# moose')).toBe(false);
}); });
it('should identify llvm opcodes', () => { it('should identify llvm opcodes', () => {
parser.hasOpcode(' %i1 = phi i32 [ %i2, %.preheader ], [ 0, %bb ]').should.be.true; expect(parser.hasOpcode(' %i1 = phi i32 [ %i2, %.preheader ], [ 0, %bb ]')).toBe(true);
}); });
}); });
describe('forceApproveAll should be false', () => { describe('forceApproveAll should be false', () => {
it('should have forceApproveAll false', () => { it('should have forceApproveAll false', () => {
optionsOverride.forceApproveAll.should.be.false; expect(optionsOverride.forceApproveAll).toBe(false);
}); });
}); });

View File

@@ -22,6 +22,8 @@
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE. // POSSIBILITY OF SUCH DAMAGE.
import {beforeAll, describe, expect, it} from 'vitest';
import {GolangCompiler} from '../lib/compilers/golang.js'; import {GolangCompiler} from '../lib/compilers/golang.js';
import * as utils from '../lib/utils.js'; import * as utils from '../lib/utils.js';
import {LanguageKey} from '../types/languages.interfaces.js'; import {LanguageKey} from '../types/languages.interfaces.js';
@@ -43,10 +45,10 @@ const info = {
lang: languages.go.id, lang: languages.go.id,
}; };
function testGoAsm(basefilename) { async function testGoAsm(baseFilename: string) {
const compiler = new GolangCompiler(makeFakeCompilerInfo(info), ce); const compiler = new GolangCompiler(makeFakeCompilerInfo(info), ce);
const asmLines = utils.splitLines(fs.readFileSync(basefilename + '.asm').toString()); const asmLines = utils.splitLines(fs.readFileSync(baseFilename + '.asm').toString());
const result = { const result = {
stderr: asmLines.map(line => { stderr: asmLines.map(line => {
@@ -56,28 +58,25 @@ function testGoAsm(basefilename) {
}), }),
}; };
return compiler.postProcess(result).then(([output]) => { const [output] = await compiler.postProcess(result);
const expectedOutput = utils.splitLines(fs.readFileSync(basefilename + '.output.asm').toString()); const expectedOutput = utils.splitLines(fs.readFileSync(baseFilename + '.output.asm').toString());
expect(utils.splitLines(output.asm)).toEqual(expectedOutput);
utils.splitLines(output.asm).should.deep.equal(expectedOutput); expect(output).toEqual({
return output.should.deep.equal({
asm: expectedOutput.join('\n'), asm: expectedOutput.join('\n'),
stdout: [], stdout: [],
stderr: [], stderr: [],
}); });
});
} }
describe('GO asm tests', () => { describe('GO asm tests', () => {
before(() => { beforeAll(() => {
ce = makeCompilationEnvironment({languages}); ce = makeCompilationEnvironment({languages});
}); });
it('Handles unknown line number correctly', () => { it('Handles unknown line number correctly', async () => {
return testGoAsm('test/golang/bug-901'); await testGoAsm('test/golang/bug-901');
}); });
it('Rewrites PC jumps to labels', () => { it('Rewrites PC jumps to labels', async () => {
return testGoAsm('test/golang/labels'); await testGoAsm('test/golang/labels');
}); });
}); });

View File

@@ -23,6 +23,7 @@
// POSSIBILITY OF SUCH DAMAGE. // POSSIBILITY OF SUCH DAMAGE.
import nock from 'nock'; import nock from 'nock';
import {afterAll, describe, expect, it} from 'vitest';
import * as google from '../lib/shortener/google.js'; import * as google from '../lib/shortener/google.js';
@@ -30,7 +31,7 @@ const googlDomain = 'https://goo.gl';
const shortUrl = '/short'; const shortUrl = '/short';
describe('Google short URL resolver tests', () => { describe('Google short URL resolver tests', () => {
after(() => { afterAll(() => {
nock.cleanAll(); nock.cleanAll();
}); });
@@ -39,25 +40,24 @@ describe('Google short URL resolver tests', () => {
it('Resolves simple URLs', async () => { it('Resolves simple URLs', async () => {
nock(googlDomain).head(shortUrl).reply(302, {}, {location: 'http://long.url/'}); nock(googlDomain).head(shortUrl).reply(302, {}, {location: 'http://long.url/'});
const resp = await resolver.resolve(googlDomain + shortUrl); await expect(resolver.resolve(googlDomain + shortUrl)).resolves.toEqual({longUrl: 'http://long.url/'});
resp.should.deep.equal({longUrl: 'http://long.url/'});
}); });
it('Handles missing long urls', () => { it('Handles missing long urls', async () => {
nock(googlDomain).head(shortUrl).reply(404); nock(googlDomain).head(shortUrl).reply(404);
return resolver.resolve(googlDomain + shortUrl).should.be.rejectedWith('Got response 404'); await expect(resolver.resolve(googlDomain + shortUrl)).rejects.toThrow('Got response 404');
}); });
it('Handles missing location header', () => { it('Handles missing location header', async () => {
nock(googlDomain).head(shortUrl).reply(302); nock(googlDomain).head(shortUrl).reply(302);
return resolver.resolve(googlDomain + shortUrl).should.be.rejectedWith('Missing location url in undefined'); await expect(resolver.resolve(googlDomain + shortUrl)).rejects.toThrow('Missing location url in undefined');
}); });
it('Handles failed requests', () => { it('Handles failed requests', async () => {
nock(googlDomain).head(shortUrl).replyWithError('Something went wrong'); nock(googlDomain).head(shortUrl).replyWithError('Something went wrong');
return resolver.resolve(googlDomain + shortUrl).should.be.rejectedWith('Something went wrong'); await expect(resolver.resolve(googlDomain + shortUrl)).rejects.toThrow('Something went wrong');
}); });
}); });

View File

@@ -1,297 +0,0 @@
// Copyright (c) 2017, Compiler Explorer Authors
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
import express from 'express';
import {ApiHandler} from '../../lib/handlers/api.js';
import {StorageNull} from '../../lib/storage/index.js';
import {chai} from '../utils.js';
const languages = {
'c++': {
id: 'c++',
name: 'C++',
monaco: 'cppp',
extensions: ['.cpp', '.cxx', '.h', '.hpp', '.hxx', '.c'],
},
haskell: {
id: 'haskell',
name: 'Haskell',
monaco: 'haskell',
extensions: ['.hs', '.haskell'],
},
pascal: {
id: 'pascal',
name: 'Pascal',
monaco: 'pascal',
extensions: ['.pas'],
},
};
const compilers = [
{
id: 'gcc900',
name: 'GCC 9.0.0',
lang: 'c++',
},
{
id: 'fpc302',
name: 'FPC 3.0.2',
lang: 'pascal',
},
{
id: 'clangtrunk',
name: 'Clang trunk',
lang: 'c++',
},
];
const compilersLimitedFields = [
{
id: 'gcc900',
name: 'GCC 9.0.0',
},
{
id: 'fpc302',
name: 'FPC 3.0.2',
},
{
id: 'clangtrunk',
name: 'Clang trunk',
},
];
describe('API handling', () => {
let app;
before(() => {
app = express();
const apiHandler = new ApiHandler(
{
handle: res => res.send('compile'),
handleCmake: res => res.send('cmake'),
handlePopularArguments: res => res.send('ok'),
handleOptimizationArguments: res => res.send('ok'),
},
(key, def) => {
switch (key) {
case 'formatters': {
return 'formatt:badformatt';
}
case 'formatter.formatt.exe': {
return 'echo';
}
case 'formatter.formatt.version': {
return 'Release';
}
case 'formatter.formatt.name': {
return 'FormatT';
}
default: {
return def;
}
}
},
new StorageNull('/', {}),
'default',
);
app.use('/api', apiHandler.handle);
apiHandler.setCompilers(compilers);
apiHandler.setLanguages(languages);
});
it('should respond to plain text compiler requests', () => {
return chai
.request(app)
.get('/api/compilers')
.then(res => {
res.should.have.status(200);
res.should.be.text;
res.text.should.contain('Compiler Name');
res.text.should.contain('gcc900');
res.text.should.contain('GCC 9.0.0');
})
.catch(err => {
throw err;
});
});
it('should respond to JSON compiler requests', () => {
return chai
.request(app)
.get('/api/compilers')
.set('Accept', 'application/json')
.then(res => {
res.should.have.status(200);
res.should.be.json;
res.body.should.deep.equals(compilers);
})
.catch(err => {
throw err;
});
});
it('should respond to JSON compiler requests with all fields', () => {
return chai
.request(app)
.get('/api/compilers?fields=all')
.set('Accept', 'application/json')
.then(res => {
res.should.have.status(200);
res.should.be.json;
res.body.should.deep.equals(compilers);
})
.catch(err => {
throw err;
});
});
it('should respond to JSON compiler requests with limited fields', () => {
return chai
.request(app)
.get('/api/compilers?fields=id,name')
.set('Accept', 'application/json')
.then(res => {
res.should.have.status(200);
res.should.be.json;
res.body.should.deep.equals(compilersLimitedFields);
})
.catch(err => {
throw err;
});
});
it('should respond to JSON compilers requests with c++ filter', () => {
return chai
.request(app)
.get('/api/compilers/c++')
.set('Accept', 'application/json')
.then(res => {
res.should.have.status(200);
res.should.be.json;
res.body.should.deep.equals([compilers[0], compilers[2]]);
})
.catch(err => {
throw err;
});
});
it('should respond to JSON compilers requests with pascal filter', () => {
return chai
.request(app)
.get('/api/compilers/pascal')
.set('Accept', 'application/json')
.then(res => {
res.should.have.status(200);
res.should.be.json;
res.body.should.deep.equals([compilers[1]]);
})
.catch(err => {
throw err;
});
});
it('should respond to plain text language requests', () => {
return chai
.request(app)
.get('/api/languages')
.then(res => {
res.should.have.status(200);
res.should.be.text;
res.text.should.contain('Name');
res.text.should.contain('c++');
res.text.should.contain('pascal');
// We should not list languages for which there are no compilers
res.text.should.not.contain('Haskell');
})
.catch(err => {
throw err;
});
});
it('should respond to JSON languages requests', () => {
return chai
.request(app)
.get('/api/languages')
.set('Accept', 'application/json')
.then(res => {
res.should.have.status(200);
res.should.be.json;
res.body.should.deep.equals([languages['c++'], languages.pascal]);
})
.catch(err => {
throw err;
});
});
// TODO(supergrecko): re-write this test case
it.skip('should list the formatters', () => {
if (process.platform !== 'win32') {
// Expects an executable called echo
return chai
.request(app)
.get('/api/formats')
.set('Accept', 'application/json')
.then(res => {
res.should.have.status(200);
res.should.be.json;
res.body.should.deep.equals([{name: 'FormatT', version: 'Release'}]);
})
.catch(err => {
throw err;
});
}
});
it('should not go through with invalid tools', () => {
return chai
.request(app)
.post('/api/format/invalid')
.set('Accept', 'application/json')
.then(res => {
res.should.have.status(422);
res.should.be.json;
res.body.should.deep.equals({exit: 2, answer: "Unknown format tool 'invalid'"});
});
});
/*
it('should not go through with invalid base styles', () => {
return chai.request(app)
.post('/api/format/formatt')
.set('Accept', 'application/json')
.set('Content-Type', 'application/json')
.send({
base: "bad-base",
source: ""
})
.then(res => {
res.should.have.status(422);
res.should.be.json;
res.body.should.deep.equals({exit: 3, answer: "Base style not supported"});
});
});
*/
it('should respond to plain site template requests', () => {
return chai
.request(app)
.get('/api/siteTemplates')
.then(res => {
res.should.have.status(200);
res.should.be.json;
})
.catch(err => {
throw err;
});
});
});

187
test/handlers/api-tests.ts Normal file
View File

@@ -0,0 +1,187 @@
// Copyright (c) 2017, Compiler Explorer Authors
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
import bodyParser from 'body-parser';
import express from 'express';
import request from 'supertest';
import {beforeAll, describe, expect, it} from 'vitest';
import {ApiHandler} from '../../lib/handlers/api.js';
import {CompileHandler} from '../../lib/handlers/compile.js';
import {CompilerProps, fakeProps} from '../../lib/properties.js';
import {StorageNull} from '../../lib/storage/index.js';
import {CompilerInfo} from '../../types/compiler.interfaces.js';
import {Language, LanguageKey} from '../../types/languages.interfaces.js';
import {makeFakeCompilerInfo, makeFakeLanguage} from '../utils.js';
const languages: Partial<Record<LanguageKey, Language>> = {
'c++': makeFakeLanguage({
id: 'c++',
name: 'C++',
monaco: 'cppp',
extensions: ['.cpp', '.cxx', '.h', '.hpp', '.hxx', '.c'],
}),
haskell: makeFakeLanguage({
id: 'haskell',
name: 'Haskell',
monaco: 'haskell',
extensions: ['.hs', '.haskell'],
}),
pascal: makeFakeLanguage({
id: 'pascal',
name: 'Pascal',
monaco: 'pascal',
extensions: ['.pas'],
}),
};
const compilers: CompilerInfo[] = [
makeFakeCompilerInfo({
id: 'gcc900',
name: 'GCC 9.0.0',
lang: 'c++',
}),
makeFakeCompilerInfo({
id: 'fpc302',
name: 'FPC 3.0.2',
lang: 'pascal',
}),
makeFakeCompilerInfo({
id: 'clangtrunk',
name: 'Clang trunk',
lang: 'c++',
}),
];
describe('API handling', () => {
let app;
beforeAll(() => {
app = express();
const apiHandler = new ApiHandler(
{
handle: res => res.send('compile'),
handleCmake: res => res.send('cmake'),
handlePopularArguments: res => res.send('ok'),
handleOptimizationArguments: res => res.send('ok'),
} as unknown as CompileHandler, // TODO(mrg) ideally fake this out or make it a higher-level interface
fakeProps({
formatters: 'formatt:badformatt',
'formatter.formatt.exe': 'echo',
'formatter.formatt.type': 'clangformat',
'formatter.formatt.version': 'Release',
'formatter.formatt.name': 'FormatT',
}),
new StorageNull('/', new CompilerProps(languages, fakeProps({}))),
'default',
);
app.use(bodyParser.json());
app.use('/api', apiHandler.handle);
apiHandler.setCompilers(compilers);
apiHandler.setLanguages(languages);
});
it('should respond to plain text compiler requests', async () => {
const res = await request(app).get('/api/compilers').expect(200).expect('Content-Type', /text/);
expect(res.text).toContain('Compiler Name');
expect(res.text).toContain('gcc900');
expect(res.text).toContain('GCC 9.0.0');
});
it('should respond to JSON compiler requests', async () => {
await request(app)
.get('/api/compilers')
.set('Accept', 'application/json')
.expect('Content-Type', /json/)
.expect(200, compilers);
});
it('should respond to JSON compiler requests with all fields', async () => {
await request(app)
.get('/api/compilers?fields=all')
.set('Accept', 'application/json')
.expect('Content-Type', /json/)
.expect(compilers);
});
it('should respond to JSON compiler requests with limited fields', async () => {
await request(app)
.get('/api/compilers?fields=id,name')
.set('Accept', 'application/json')
.expect('Content-Type', /json/)
.expect(
200,
compilers.map(c => {
return {id: c.id, name: c.name};
}),
);
});
it('should respond to JSON compilers requests with c++ filter', async () => {
await request(app)
.get('/api/compilers/c++')
.set('Accept', 'application/json')
.expect('Content-Type', /json/)
.expect(200, [compilers[0], compilers[2]]);
});
it('should respond to JSON compilers requests with pascal filter', async () => {
await request(app)
.get('/api/compilers/pascal')
.set('Accept', 'application/json')
.expect('Content-Type', /json/)
.expect(200, [compilers[1]]);
});
it('should respond to plain text language requests', async () => {
const res = await request(app).get('/api/languages').expect(200).expect('Content-Type', /text/);
expect(res.text).toContain('Name');
expect(res.text).toContain('c++');
expect(res.text).toContain('c++');
// We should not list languages for which there are no compilers
expect(res.text).not.toContain('haskell');
});
it('should respond to JSON languages requests', async () => {
await request(app)
.get('/api/languages')
.set('Accept', 'application/json')
.expect('Content-Type', /json/)
.expect(200, [languages['c++'], languages.pascal]);
});
it('should not go through with invalid tools', async () => {
await request(app)
.post('/api/format/invalid')
.set('Accept', 'application/json')
.expect('Content-Type', /json/)
.expect(422, {exit: 2, answer: "Unknown format tool 'invalid'"});
});
it('should not go through with invalid base styles', async () => {
await request(app)
.post('/api/format/formatt')
.send({
base: 'bad-base',
source: 'i am source',
})
.set('Accept', 'application/json')
.expect(422, {exit: 3, answer: "Style 'bad-base' is not supported"})
.expect('Content-Type', /json/);
});
it('should respond to plain site template requests', async () => {
await request(app).get('/api/siteTemplates').expect(200).expect('Content-Type', /json/);
});
});

View File

@@ -22,11 +22,11 @@
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE. // POSSIBILITY OF SUCH DAMAGE.
import {expect} from 'chai';
import express from 'express'; import express from 'express';
import request from 'supertest';
import {beforeAll, describe, expect, it} from 'vitest';
import {withAssemblyDocumentationProviders} from '../../lib/handlers/assembly-documentation.js'; import {withAssemblyDocumentationProviders} from '../../lib/handlers/assembly-documentation.js';
import {chai} from '../utils.js';
/** Test matrix of architecture to [opcode, tooptip, html, url] */ /** Test matrix of architecture to [opcode, tooptip, html, url] */
export const TEST_MATRIX: Record<PropertyKey, [string, string, string, string][]> = { export const TEST_MATRIX: Record<PropertyKey, [string, string, string, string][]> = {
@@ -101,7 +101,7 @@ export const TEST_MATRIX: Record<PropertyKey, [string, string, string, string][]
describe('Assembly Documentation API', () => { describe('Assembly Documentation API', () => {
let app: express.Express; let app: express.Express;
before(() => { beforeAll(() => {
app = express(); app = express();
const router = express.Router(); const router = express.Router();
withAssemblyDocumentationProviders(router); withAssemblyDocumentationProviders(router);
@@ -109,44 +109,44 @@ describe('Assembly Documentation API', () => {
}); });
it('should return 404 for unknown architecture', async () => { it('should return 404 for unknown architecture', async () => {
const res = await chai.request(app).get(`/api/asm/not_an_arch/mov`).set('Accept', 'application/json'); await request(app)
expect(res).to.have.status(404); .get(`/api/asm/not_an_arch/mov`)
expect(res).to.be.json; .set('Accept', 'application/json')
expect(res.body).to.deep.equal({error: `No documentation for 'not_an_arch'`}); .expect('Content-Type', /json/)
.expect(404, {error: `No documentation for 'not_an_arch'`});
}); });
for (const [arch, cases] of Object.entries(TEST_MATRIX)) { for (const [arch, cases] of Object.entries(TEST_MATRIX)) {
for (const [opcode, tooltip, html, url] of cases) { for (const [opcode, tooltip, html, url] of cases) {
it(`should process ${arch} text requests`, async () => { it(`should process ${arch} text requests`, async () => {
const res = await chai.request(app).get(`/api/asm/${arch}/${opcode}`).set('Accept', 'text/plain'); const res = await request(app)
expect(res).to.have.status(200); .get(`/api/asm/${arch}/${opcode}`)
expect(res).to.be.html; .set('Accept', 'text/plain')
expect(res.text).to.contain(html); .expect('Content-Type', /html/)
.expect(200);
expect(res.text).toContain(html);
}); });
it(`should process ${arch} json requests`, async () => { it(`should process ${arch} json requests`, async () => {
const res = await chai.request(app).get(`/api/asm/${arch}/${opcode}`).set('Accept', 'application/json'); const res = await request(app)
.get(`/api/asm/${arch}/${opcode}`)
expect(res).to.have.status(200); .set('Accept', 'application/json')
expect(res).to.be.json; .expect('Content-Type', /json/)
expect(res.body.html).to.contain(html); .expect(200);
expect(res.body.tooltip).to.contain(tooltip); expect(res.body.html).toContain(html);
expect(res.body.url).to.contain(url); expect(res.body.tooltip).toContain(tooltip);
expect(res.body.url).toContain(url);
}); });
it(`should return 404 for ${arch} unknown opcode requests`, async () => { it(`should return 404 for ${arch} unknown opcode requests`, async () => {
const res = await chai await request(app)
.request(app)
.get(`/api/asm/${arch}/not_an_opcode`) .get(`/api/asm/${arch}/not_an_opcode`)
.set('Accept', 'application/json'); .set('Accept', 'application/json')
expect(res).to.have.status(404); .expect('Content-Type', /json/)
expect(res).to.be.json; .expect(404, {error: "Unknown opcode 'NOT_AN_OPCODE'"});
expect(res.body).to.deep.equal({error: "Unknown opcode 'NOT_AN_OPCODE'"});
}); });
it(`should return 406 for ${arch} bad accept type requests`, async () => { it(`should return 406 for ${arch} bad accept type requests`, async () => {
const res = await chai.request(app).get(`/api/asm/${arch}/${opcode}`).set('Accept', 'application/pdf'); await request(app).get(`/api/asm/${arch}/${opcode}`).set('Accept', 'application/pdf').expect(406);
expect(res).to.have.status(406);
}); });
} }
} }

View File

@@ -1,553 +0,0 @@
// Copyright (c) 2017, Compiler Explorer Authors
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
import bodyParser from 'body-parser';
import express from 'express';
import {CompileHandler, SetTestMode} from '../../lib/handlers/compile.js';
import {fakeProps} from '../../lib/properties.js';
import {BypassCache} from '../../types/compilation/compilation.interfaces.js';
import {chai, makeCompilationEnvironment} from '../utils.js';
SetTestMode();
const languages = {
a: {id: 'a', name: 'A lang'},
b: {id: 'b', name: 'B lang'},
d: {id: 'd', name: 'D lang'},
};
describe('Compiler tests', () => {
let app, compileHandler;
before(() => {
const compilationEnvironment = makeCompilationEnvironment({languages});
compileHandler = new CompileHandler(compilationEnvironment, fakeProps({}));
const textParser = bodyParser.text({type: () => true});
const formParser = bodyParser.urlencoded({extended: false});
app = express();
app.use(bodyParser.json());
app.post('/noscript/compile', formParser, compileHandler.handle.bind(compileHandler));
app.post('/:compiler/compile', textParser, compileHandler.handle.bind(compileHandler));
app.post('/:compiler/cmake', compileHandler.handleCmake.bind(compileHandler));
});
it('throws for unknown compilers', () => {
return chai
.request(app)
.post('/NOT_A_COMPILER/compile')
.then(res => {
res.should.have.status(404);
});
});
describe('Noscript API', () => {
it('supports compile', () => {
return compileHandler
.setCompilers([
{
compilerType: 'fake-for-test',
exe: 'fake',
fakeResult: {
code: 0,
stdout: [{text: 'Something from stdout'}],
stderr: [{text: 'Something from stderr'}],
asm: [{text: 'ASMASMASM'}],
},
},
])
.then(() => {
return chai
.request(app)
.post('/noscript/compile')
.set('Content-Type', 'application/x-www-form-urlencoded')
.send('compiler=fake-for-test&source=I am a program')
.then(res => {
res.should.have.status(200);
res.should.be.text;
res.text.should.contain('Something from stdout');
res.text.should.contain('Something from stderr');
res.text.should.contain('ASMASMASM');
})
.catch(err => {
throw err;
});
});
});
});
describe('Curl API', () => {
it('supports compile', () => {
return compileHandler
.setCompilers([
{
compilerType: 'fake-for-test',
exe: 'fake',
fakeResult: {
code: 0,
stdout: [{text: 'Something from stdout'}],
stderr: [{text: 'Something from stderr'}],
asm: [{text: 'ASMASMASM'}],
},
},
])
.then(() => {
return chai
.request(app)
.post('/fake-for-test/compile')
.set('Content-Type', 'application/x-www-form-urlencoded')
.send('I am a program /* &compiler=NOT_A_COMPILER&source=etc */')
.then(res => {
res.should.have.status(200);
res.should.be.text;
res.text.should.contain('Something from stdout');
res.text.should.contain('Something from stderr');
res.text.should.contain('ASMASMASM');
})
.catch(err => {
throw err;
});
});
});
it('supports alias compile', () => {
return compileHandler
.setCompilers([
{
id: 'newcompilerid',
alias: ['oldid1', 'oldid2'],
compilerType: 'fake-for-test',
exe: 'fake',
fakeResult: {
code: 0,
stdout: [{text: 'Something from stdout'}],
stderr: [{text: 'Something from stderr'}],
asm: [{text: 'ASMASMASM'}],
},
},
])
.then(() => {
return chai
.request(app)
.post('/oldid1/compile')
.set('Content-Type', 'application/x-www-form-urlencoded')
.send('I am a program /* &compiler=NOT_A_COMPILER&source=etc */')
.then(res => {
res.should.have.status(200);
res.should.be.text;
res.text.should.contain('Something from stdout');
res.text.should.contain('Something from stderr');
res.text.should.contain('ASMASMASM');
})
.catch(err => {
throw err;
});
});
});
});
describe('JSON API', () => {
it('handles text output', () => {
return compileHandler
.setCompilers([
{
compilerType: 'fake-for-test',
exe: 'fake',
fakeResult: {
code: 0,
stdout: [{text: 'Something from stdout'}],
stderr: [{text: 'Something from stderr'}],
asm: [{text: 'ASMASMASM'}],
},
},
])
.then(() => {
return chai
.request(app)
.post('/fake-for-test/compile')
.send({
options: {},
source: 'I am a program',
})
.then(res => {
res.should.have.status(200);
res.should.be.text;
res.text.should.contain('Something from stdout');
res.text.should.contain('Something from stderr');
res.text.should.contain('ASMASMASM');
})
.catch(err => {
throw err;
});
});
});
function makeFakeJson(source, options, fakeResult) {
return compileHandler
.setCompilers([
{
compilerType: 'fake-for-test',
exe: 'fake',
fakeResult: fakeResult || {},
},
])
.then(() =>
chai
.request(app)
.post('/fake-for-test/compile')
.set('Accept', 'application/json')
.send({
options: options || {},
source: source || '',
}),
);
}
function makeFakeWithExtraFilesJson(source, options, files, fakeResult) {
return compileHandler
.setCompilers([
{
compilerType: 'fake-for-test',
exe: 'fake',
fakeResult: fakeResult || {},
},
])
.then(() =>
chai
.request(app)
.post('/fake-for-test/compile')
.set('Accept', 'application/json')
.send({
options: options || {},
source: source || '',
files: files || [],
}),
);
}
function makeFakeCmakeJson(source, options, fakeResult, files) {
return compileHandler
.setCompilers([
{
compilerType: 'fake-for-test',
exe: 'fake',
fakeResult: fakeResult || {},
},
])
.then(() =>
chai
.request(app)
.post('/fake-for-test/cmake')
.set('Accept', 'application/json')
.send({
options: options || {},
source: source || '',
files: files || [],
}),
);
}
it('handles JSON output', () => {
return makeFakeJson(
'I am a program',
{},
{
code: 0,
stdout: [{text: 'Something from stdout'}],
stderr: [{text: 'Something from stderr'}],
asm: [{text: 'ASMASMASM'}],
},
)
.then(res => {
res.should.have.status(200);
res.should.be.json;
res.body.should.deep.equals({
asm: [{text: 'ASMASMASM'}],
code: 0,
input: {
backendOptions: {},
filters: {},
options: [],
source: 'I am a program',
},
stderr: [{text: 'Something from stderr'}],
stdout: [{text: 'Something from stdout'}],
});
})
.catch(err => {
throw err;
});
});
it('parses options and filters', () => {
return makeFakeJson('I am a program', {
userArguments: '-O1 -monkey "badger badger"',
filters: {a: true, b: true, c: true},
}).then(res => {
res.should.have.status(200);
res.should.be.json;
res.body.input.options.should.deep.equals(['-O1', '-monkey', 'badger badger']);
res.body.input.filters.should.deep.equals({a: true, b: true, c: true});
});
});
it('parses extra files', () => {
return makeFakeWithExtraFilesJson(
'I am a program',
{
userArguments: '-O1 -monkey "badger badger"',
filters: {a: true, b: true, c: true},
},
[
{
filename: 'myresource.txt',
contents: 'Hello, World!\nHow are you?\n',
},
],
{},
).then(res => {
res.should.have.status(200);
res.should.be.json;
res.body.input.options.should.deep.equals(['-O1', '-monkey', 'badger badger']);
res.body.input.filters.should.deep.equals({a: true, b: true, c: true});
res.body.input.files.should.deep.equals([
{
filename: 'myresource.txt',
contents: 'Hello, World!\nHow are you?\n',
},
]);
});
});
it('cmakes', () => {
return makeFakeCmakeJson(
'I am a program',
{
userArguments: '-O1 -monkey "badger badger"',
filters: {a: true, b: true, c: true},
},
{},
[
{
filename: 'myresource.txt',
contents: 'Hello, World!\nHow are you?\n',
},
],
).then(res => {
res.should.have.status(200);
res.should.be.json;
res.body.input.options.should.deep.equals({
backendOptions: {},
bypassCache: BypassCache.None,
executeParameters: {
args: [],
runtimeTools: [],
stdin: '',
},
filters: {
a: true,
b: true,
c: true,
},
libraries: [],
options: ['-O1', '-monkey', 'badger badger'],
source: 'I am a program',
tools: [],
});
res.body.input.files.should.deep.equals([
{
filename: 'myresource.txt',
contents: 'Hello, World!\nHow are you?\n',
},
]);
});
});
});
describe('Query API', () => {
function makeFakeQuery(source, query, fakeResult) {
return compileHandler
.setCompilers([
{
compilerType: 'fake-for-test',
exe: 'fake',
fakeResult: fakeResult || {},
},
])
.then(() =>
chai
.request(app)
.post('/fake-for-test/compile')
.query(query || {})
.set('Accept', 'application/json')
.send(source || ''),
);
}
it('error on empty request body', () => {
return compileHandler
.setCompilers([
{
compilerType: 'fake-for-test',
exe: 'fake',
fakeResult: {},
},
])
.then(() => chai.request(app).post('/fake-for-test/compile').set('Accept', 'application/json'))
.then(res => {
res.should.have.status(500);
})
.catch(err => {
throw err;
});
});
it('handles filters set directly', () => {
return makeFakeQuery('source', {filters: 'a,b,c'})
.then(res => {
res.should.have.status(200);
res.should.be.json;
res.body.input.options.should.deep.equals([]);
res.body.input.filters.should.deep.equals({a: true, b: true, c: true});
})
.catch(err => {
throw err;
});
});
it('handles filters added', () => {
return makeFakeQuery('source', {filters: 'a', addFilters: 'e,f'})
.then(res => {
res.should.have.status(200);
res.should.be.json;
res.body.input.options.should.deep.equals([]);
res.body.input.filters.should.deep.equals({a: true, e: true, f: true});
})
.catch(err => {
throw err;
});
});
it('handles filters removed', () => {
return makeFakeQuery('source', {filters: 'a,b,c', removeFilters: 'b,c,d'})
.then(res => {
res.should.have.status(200);
res.should.be.json;
res.body.input.options.should.deep.equals([]);
res.body.input.filters.should.deep.equals({a: true});
})
.catch(err => {
throw err;
});
});
it('handles filters added and removed', () => {
return makeFakeQuery('source', {filters: 'a,b,c', addFilters: 'c,g,h', removeFilters: 'b,c,d,h'})
.then(res => {
res.should.have.status(200);
res.should.be.json;
res.body.input.options.should.deep.equals([]);
res.body.input.filters.should.deep.equals({a: true, g: true});
})
.catch(err => {
throw err;
});
});
});
describe('Multi language', () => {
function makeFakeJson(compiler, lang) {
return compileHandler
.setCompilers([
{
compilerType: 'fake-for-test',
id: 'a',
lang: 'a',
exe: 'fake',
fakeResult: {code: 0, stdout: [], stderr: [], asm: [{text: 'LANG A'}]},
},
{
compilerType: 'fake-for-test',
id: 'b',
lang: 'b',
exe: 'fake',
fakeResult: {code: 0, stdout: [], stderr: [], asm: [{text: 'LANG B'}]},
},
{
compilerType: 'fake-for-test',
id: 'a',
lang: 'b',
exe: 'fake',
fakeResult: {code: 0, stdout: [], stderr: [], asm: [{text: 'LANG B but A'}]},
},
])
.then(() =>
chai.request(app).post(`/${compiler}/compile`).set('Accept', 'application/json').send({
lang: lang,
options: {},
source: '',
}),
);
}
it('finds without language', () => {
return makeFakeJson('b', {})
.then(res => {
res.should.have.status(200);
res.should.be.json;
res.body.asm.should.deep.equals([{text: 'LANG B'}]);
})
.catch(err => {
throw err;
});
});
it('disambiguates by language, choosing A', () => {
return makeFakeJson('a', 'a')
.then(res => {
res.should.have.status(200);
res.should.be.json;
res.body.asm.should.deep.equals([{text: 'LANG A'}]);
})
.catch(err => {
throw err;
});
});
it('disambiguates by language, choosing B', () => {
return makeFakeJson('a', 'b')
.then(res => {
res.should.have.status(200);
res.should.be.json;
res.body.asm.should.deep.equals([{text: 'LANG B but A'}]);
})
.catch(err => {
throw err;
});
});
});
});

View File

@@ -0,0 +1,420 @@
// Copyright (c) 2017, Compiler Explorer Authors
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
import bodyParser from 'body-parser';
import express from 'express';
import request from 'supertest';
import {beforeAll, describe, expect, it} from 'vitest';
import {CompileHandler, SetTestMode} from '../../lib/handlers/compile.js';
import {fakeProps} from '../../lib/properties.js';
import {BypassCache} from '../../types/compilation/compilation.interfaces.js';
import {makeCompilationEnvironment} from '../utils.js';
SetTestMode();
const languages = {
a: {id: 'a', name: 'A lang'},
b: {id: 'b', name: 'B lang'},
d: {id: 'd', name: 'D lang'},
};
describe('Compiler tests', () => {
let app, compileHandler;
beforeAll(() => {
const compilationEnvironment = makeCompilationEnvironment({languages});
compileHandler = new CompileHandler(compilationEnvironment, fakeProps({}));
const textParser = bodyParser.text({type: () => true});
const formParser = bodyParser.urlencoded({extended: false});
app = express();
app.use(bodyParser.json());
app.post('/noscript/compile', formParser, compileHandler.handle.bind(compileHandler));
app.post('/:compiler/compile', textParser, compileHandler.handle.bind(compileHandler));
app.post('/:compiler/cmake', compileHandler.handleCmake.bind(compileHandler));
});
it('throws for unknown compilers', async () => {
await request(app).post('/NOT_A_COMPILER/compile').expect(404);
});
describe('Noscript API', () => {
it('supports compile', async () => {
await compileHandler.setCompilers([
{
compilerType: 'fake-for-test',
exe: 'fake',
fakeResult: {
code: 0,
stdout: [{text: 'Something from stdout'}],
stderr: [{text: 'Something from stderr'}],
asm: [{text: 'ASMASMASM'}],
},
},
]);
const res = await request(app)
.post('/noscript/compile')
.set('Content-Type', 'application/x-www-form-urlencoded')
.send('compiler=fake-for-test&source=I am a program')
.expect(200)
.expect('Content-Type', /text/);
expect(res.text).toContain('Something from stdout');
expect(res.text).toContain('Something from stderr');
expect(res.text).toContain('ASMASMASM');
});
});
describe('Curl API', () => {
it('supports compile', async () => {
await compileHandler.setCompilers([
{
compilerType: 'fake-for-test',
exe: 'fake',
fakeResult: {
code: 0,
stdout: [{text: 'Something from stdout'}],
stderr: [{text: 'Something from stderr'}],
asm: [{text: 'ASMASMASM'}],
},
},
]);
const res = await request(app)
.post('/fake-for-test/compile')
.set('Content-Type', 'application/x-www-form-urlencoded')
.send('I am a program /* &compiler=NOT_A_COMPILER&source=etc */')
.expect(200)
.expect('Content-Type', /text/);
expect(res.text).toContain('Something from stdout');
expect(res.text).toContain('Something from stderr');
expect(res.text).toContain('ASMASMASM');
});
it('supports alias compile', async () => {
await compileHandler.setCompilers([
{
id: 'newcompilerid',
alias: ['oldid1', 'oldid2'],
compilerType: 'fake-for-test',
exe: 'fake',
fakeResult: {
code: 0,
stdout: [{text: 'Something from stdout'}],
stderr: [{text: 'Something from stderr'}],
asm: [{text: 'ASMASMASM'}],
},
},
]);
const res = await request(app)
.post('/oldid1/compile')
.set('Content-Type', 'application/x-www-form-urlencoded')
.send('I am a program /* &compiler=NOT_A_COMPILER&source=etc */')
.expect(200)
.expect('Content-Type', /text/);
expect(res.text).toContain('Something from stdout');
expect(res.text).toContain('Something from stderr');
expect(res.text).toContain('ASMASMASM');
});
});
async function setFakeResult(fakeResult?: any) {
await compileHandler.setCompilers([
{
compilerType: 'fake-for-test',
exe: 'fake',
fakeResult: fakeResult || {},
},
]);
}
describe('JSON API', () => {
it('handles text output', async () => {
await compileHandler.setCompilers([
{
compilerType: 'fake-for-test',
exe: 'fake',
fakeResult: {
code: 0,
stdout: [{text: 'Something from stdout'}],
stderr: [{text: 'Something from stderr'}],
asm: [{text: 'ASMASMASM'}],
},
},
]);
const res = await request(app)
.post('/fake-for-test/compile')
.send({
options: {},
source: 'I am a program',
})
.expect(200)
.expect('Content-Type', /text/);
expect(res.text).toContain('Something from stdout');
expect(res.text).toContain('Something from stderr');
expect(res.text).toContain('ASMASMASM');
});
function makeFakeJson(source: string, options?: any) {
return request(app)
.post('/fake-for-test/compile')
.set('Accept', 'application/json')
.send({
options: options || {},
source: source || '',
});
}
function makeFakeWithExtraFilesJson(source: string, options?: any, files?: any) {
return request(app)
.post('/fake-for-test/compile')
.set('Accept', 'application/json')
.send({
options: options || {},
source: source || '',
files: files || [],
});
}
function makeFakeCmakeJson(source: string, options?: any, files?: any) {
return request(app)
.post('/fake-for-test/cmake')
.set('Accept', 'application/json')
.send({
options: options || {},
source: source || '',
files: files || [],
});
}
it('handles JSON output', async () => {
await setFakeResult({
code: 0,
stdout: [{text: 'Something from stdout'}],
stderr: [{text: 'Something from stderr'}],
asm: [{text: 'ASMASMASM'}],
});
await makeFakeJson('I am a program')
.expect('Content-Type', /json/)
.expect(200, {
asm: [{text: 'ASMASMASM'}],
code: 0,
input: {
backendOptions: {},
filters: {},
options: [],
source: 'I am a program',
},
stderr: [{text: 'Something from stderr'}],
stdout: [{text: 'Something from stdout'}],
});
});
it('parses options and filters', async () => {
await setFakeResult();
const res = await makeFakeJson('I am a program', {
userArguments: '-O1 -monkey "badger badger"',
filters: {a: true, b: true, c: true},
})
.expect('Content-Type', /json/)
.expect(200);
expect(res.body.input.options).toEqual(['-O1', '-monkey', 'badger badger']);
expect(res.body.input.filters).toEqual({a: true, b: true, c: true});
});
it('parses extra files', async () => {
await setFakeResult();
const res = await makeFakeWithExtraFilesJson(
'I am a program',
{
userArguments: '-O1 -monkey "badger badger"',
filters: {a: true, b: true, c: true},
},
[
{
filename: 'myresource.txt',
contents: 'Hello, World!\nHow are you?\n',
},
],
)
.expect('Content-Type', /json/)
.expect(200);
expect(res.body.input.options).toEqual(['-O1', '-monkey', 'badger badger']);
expect(res.body.input.filters).toEqual({a: true, b: true, c: true});
expect(res.body.input.files).toEqual([
{
filename: 'myresource.txt',
contents: 'Hello, World!\nHow are you?\n',
},
]);
});
it('cmakes', async () => {
await setFakeResult();
const res = await makeFakeCmakeJson(
'I am a program',
{
userArguments: '-O1 -monkey "badger badger"',
filters: {a: true, b: true, c: true},
},
[
{
filename: 'myresource.txt',
contents: 'Hello, World!\nHow are you?\n',
},
],
)
.expect('Content-Type', /json/)
.expect(200);
expect(res.body.input.options).toEqual({
backendOptions: {},
bypassCache: BypassCache.None,
executeParameters: {
args: [],
runtimeTools: [],
stdin: '',
},
filters: {
a: true,
b: true,
c: true,
},
libraries: [],
options: ['-O1', '-monkey', 'badger badger'],
source: 'I am a program',
tools: [],
});
expect(res.body.input.files).toEqual([
{
filename: 'myresource.txt',
contents: 'Hello, World!\nHow are you?\n',
},
]);
});
});
describe('Query API', () => {
function makeFakeQuery(source?: any, query?: any) {
return request(app)
.post('/fake-for-test/compile')
.query(query || {})
.set('Accept', 'application/json')
.send(source || '');
}
it('error on empty request body', async () => {
await setFakeResult();
await request(app).post('/fake-for-test/compile').set('Accept', 'application/json').expect(500);
});
it('handles filters set directly', async () => {
await setFakeResult();
const res = await makeFakeQuery('source', {filters: 'a,b,c'}).expect('Content-Type', /json/).expect(200);
expect(res.body.input.options).toEqual([]);
expect(res.body.input.filters).toEqual({a: true, b: true, c: true});
});
it('handles filters added', async () => {
await setFakeResult();
const res = await makeFakeQuery('source', {filters: 'a', addFilters: 'e,f'})
.expect('Content-Type', /json/)
.expect(200);
expect(res.body.input.options).toEqual([]);
expect(res.body.input.filters).toEqual({a: true, e: true, f: true});
});
it('handles filters removed', async () => {
await setFakeResult();
const res = await makeFakeQuery('source', {filters: 'a,b,c', removeFilters: 'b,c,d'})
.expect('Content-Type', /json/)
.expect(200);
expect(res.body.input.options).toEqual([]);
expect(res.body.input.filters).toEqual({a: true});
});
it('handles filters added and removed', async () => {
await setFakeResult();
const res = await makeFakeQuery('source', {filters: 'a,b,c', addFilters: 'c,g,h', removeFilters: 'b,c,d,h'})
.expect('Content-Type', /json/)
.expect(200);
expect(res.body.input.options).toEqual([]);
expect(res.body.input.filters).toEqual({a: true, g: true});
});
});
describe('Multi language', () => {
async function setFakeCompilers() {
await compileHandler.setCompilers([
{
compilerType: 'fake-for-test',
id: 'a',
lang: 'a',
exe: 'fake',
fakeResult: {code: 0, stdout: [], stderr: [], asm: [{text: 'LANG A'}]},
},
{
compilerType: 'fake-for-test',
id: 'b',
lang: 'b',
exe: 'fake',
fakeResult: {code: 0, stdout: [], stderr: [], asm: [{text: 'LANG B'}]},
},
{
compilerType: 'fake-for-test',
id: 'a',
lang: 'b',
exe: 'fake',
fakeResult: {code: 0, stdout: [], stderr: [], asm: [{text: 'LANG B but A'}]},
},
]);
}
function makeFakeJson(compiler: string, lang: any) {
return request(app).post(`/${compiler}/compile`).set('Accept', 'application/json').send({
lang: lang,
options: {},
source: '',
});
}
it('finds without language', async () => {
await setFakeCompilers();
const res = await makeFakeJson('b', {}).expect('Content-Type', /json/).expect(200);
expect(res.body.asm).toEqual([{text: 'LANG B'}]);
});
it('disambiguates by language, choosing A', async () => {
await setFakeCompilers();
const res = await makeFakeJson('b', 'a').expect('Content-Type', /json/).expect(200);
expect(res.body.asm).toEqual([{text: 'LANG B'}]);
});
it('disambiguates by language, choosing B', async () => {
await setFakeCompilers();
const res = await makeFakeJson('a', 'b');
expect(res.body.asm).toEqual([{text: 'LANG B but A'}]);
});
});
});

View File

@@ -24,25 +24,24 @@
import express from 'express'; import express from 'express';
import mockfs from 'mock-fs'; import mockfs from 'mock-fs';
import request from 'supertest';
import {afterAll, beforeAll, beforeEach, describe, expect, it} from 'vitest';
import {CompilationQueue} from '../../lib/compilation-queue.js'; import {CompilationQueue} from '../../lib/compilation-queue.js';
import {HealthCheckHandler} from '../../lib/handlers/health-check.js'; import {HealthCheckHandler} from '../../lib/handlers/health-check.js';
import {chai} from '../utils.js';
describe('Health checks', () => { describe('Health checks', () => {
let app; let app;
let compilationQueue; let compilationQueue;
beforeEach(() => { beforeEach(() => {
compilationQueue = new CompilationQueue(1); compilationQueue = new CompilationQueue(1, 0, 0);
app = express(); app = express();
app.use('/hc', new HealthCheckHandler(compilationQueue).handle); app.use('/hc', new HealthCheckHandler(compilationQueue, '').handle);
}); });
it('should respond with OK', async () => { it('should respond with OK', async () => {
const res = await chai.request(app).get('/hc'); await request(app).get('/hc').expect(200, 'Everything is awesome');
res.should.have.status(200);
res.text.should.be.eql('Everything is awesome');
}); });
it('should use compilation queue', async () => { it('should use compilation queue', async () => {
@@ -50,16 +49,16 @@ describe('Health checks', () => {
compilationQueue._queue.on('active', () => { compilationQueue._queue.on('active', () => {
count++; count++;
}); });
await chai.request(app).get('/hc'); await request(app).get('/hc');
count.should.be.eql(1); expect(count).toEqual(1);
}); });
}); });
describe('Health checks on disk', () => { describe('Health checks on disk', () => {
let app; let app;
before(() => { beforeAll(() => {
const compilationQueue = new CompilationQueue(1); const compilationQueue = new CompilationQueue(1, 0, 0);
app = express(); app = express();
app.use('/hc', new HealthCheckHandler(compilationQueue, '/fake/.nonexist').handle); app.use('/hc', new HealthCheckHandler(compilationQueue, '/fake/.nonexist').handle);
@@ -72,18 +71,15 @@ describe('Health checks on disk', () => {
}); });
}); });
after(() => { afterAll(() => {
mockfs.restore(); mockfs.restore();
}); });
it('should respond with 500 when file not found', async () => { it('should respond with 500 when file not found', async () => {
const res = await chai.request(app).get('/hc'); await request(app).get('/hc').expect(500);
res.should.have.status(500);
}); });
it('should respond with OK and file contents when found', async () => { it('should respond with OK and file contents when found', async () => {
const res = await chai.request(app).get('/hc2'); await request(app).get('/hc2').expect(200, 'Everything is fine');
res.should.have.status(200);
res.text.should.be.eql('Everything is fine');
}); });
}); });

View File

@@ -1,16 +1,16 @@
import {assert} from 'chai'; import {beforeAll, describe, expect, it} from 'vitest';
import {getSiteTemplates, loadSiteTemplates} from '../../lib/handlers/site-templates.js'; import {getSiteTemplates, loadSiteTemplates} from '../../lib/handlers/site-templates.js';
describe('Site Templates Backend', () => { describe('Site Templates Backend', () => {
before(() => { beforeAll(() => {
loadSiteTemplates('etc/config'); loadSiteTemplates('etc/config');
}); });
it('should load site templates properly', () => { it('should load site templates properly', () => {
const templates = getSiteTemplates(); const templates = getSiteTemplates();
// not super comprehensive // not super comprehensive
assert(templates.meta['meta.screenshot_dimentions'] !== undefined); expect(templates.meta).toHaveProperty('meta.screenshot_dimentions');
assert(Object.entries(templates.templates).length > 0); expect(Object.entries(templates.templates).length).toBeTruthy();
}); });
}); });

View File

@@ -23,17 +23,19 @@
// POSSIBILITY OF SUCH DAMAGE. // POSSIBILITY OF SUCH DAMAGE.
import express from 'express'; import express from 'express';
import request from 'supertest';
import {describe, expect, it} from 'vitest';
import {SourceHandler} from '../../lib/handlers/source.js'; import {SourceHandler} from '../../lib/handlers/source.js';
import {chai} from '../utils.js';
describe('Sources', () => { describe('Sources', () => {
const app = express(); const app = express();
const handler = new SourceHandler( const handler = new SourceHandler(
[ [
{ {
name: 'moose',
urlpart: 'moose', urlpart: 'moose',
list: () => Promise.resolve({moose: 'pig'}), list: async () => [{file: 'file', lang: 'lang', name: 'name'}],
load: name => Promise.resolve({file: `File called ${name}`}), load: name => Promise.resolve({file: `File called ${name}`}),
}, },
], ],
@@ -41,32 +43,18 @@ describe('Sources', () => {
); );
app.use('/source', handler.handle.bind(handler)); app.use('/source', handler.handle.bind(handler));
it('should list', () => { it('should list', async () => {
return chai const res = await request(app)
.request(app)
.get('/source/moose/list') .get('/source/moose/list')
.then(res => { .expect('Content-Type', /json/)
res.should.have.status(200); .expect(200, [{file: 'file', lang: 'lang', name: 'name'}]);
res.should.be.json; expect(res.headers['yibble']).toEqual('boing');
res.body.should.deep.equals({moose: 'pig'});
res.should.have.header('Yibble', 'boing');
})
.catch(function (err) {
throw err;
}); });
}); it('should fetch files', async () => {
it('should fetch files', () => { const res = await request(app)
return chai
.request(app)
.get('/source/moose/load/Grunkle') .get('/source/moose/load/Grunkle')
.then(res => { .expect('Content-Type', /json/)
res.should.have.status(200); .expect(200, {file: 'File called Grunkle'});
res.should.be.json; expect(res.headers['yibble']).toEqual('boing');
res.body.should.deep.equals({file: 'File called Grunkle'});
res.should.have.header('Yibble', 'boing');
})
.catch(function (err) {
throw err;
});
}); });
}); });

View File

@@ -22,33 +22,35 @@
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE. // POSSIBILITY OF SUCH DAMAGE.
import {describe, expect, it} from 'vitest';
import {InstructionSets} from '../lib/instructionsets.js'; import {InstructionSets} from '../lib/instructionsets.js';
describe('InstructionSets', async () => { describe('InstructionSets', async () => {
it('should recognize aarch64 for clang target', async () => { it('should recognize aarch64 for clang target', async () => {
const isets = new InstructionSets(); const isets = new InstructionSets();
return isets await expect(
.getCompilerInstructionSetHint('aarch64-linux-gnu', '/opt/compiler-explorer/clang-11.0.1/bin/clang++') isets.getCompilerInstructionSetHint('aarch64-linux-gnu', '/opt/compiler-explorer/clang-11.0.1/bin/clang++'),
.should.eventually.equal('aarch64'); ).resolves.toEqual('aarch64');
}); });
it('should recognize gcc aarch64 from filepath', async () => { it('should recognize gcc aarch64 from filepath', async () => {
const isets = new InstructionSets(); const isets = new InstructionSets();
return isets await expect(
.getCompilerInstructionSetHint( isets.getCompilerInstructionSetHint(
false, false,
'/opt/compiler-explorer/arm64/gcc-12.1.0/aarch64-unknown-linux-gnu/bin/aarch64-unknown-linux-gnu-g++', '/opt/compiler-explorer/arm64/gcc-12.1.0/aarch64-unknown-linux-gnu/bin/aarch64-unknown-linux-gnu-g++',
) ),
.should.eventually.equal('aarch64'); ).resolves.toEqual('aarch64');
}); });
it('should default to amd64 when not apparant', async () => { it('should default to amd64 when not apparent', async () => {
const isets = new InstructionSets(); const isets = new InstructionSets();
return isets await expect(
.getCompilerInstructionSetHint(false, '/opt/compiler-explorer/gcc-12.2.0/bin/g++') isets.getCompilerInstructionSetHint(false, '/opt/compiler-explorer/gcc-12.2.0/bin/g++'),
.should.eventually.equal('amd64'); ).resolves.toEqual('amd64');
}); });
}); });

View File

@@ -22,6 +22,8 @@
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE. // POSSIBILITY OF SUCH DAMAGE.
import {beforeAll, describe, expect, it} from 'vitest';
import {CompilationEnvironment} from '../lib/compilation-env.js'; import {CompilationEnvironment} from '../lib/compilation-env.js';
import {JavaCompiler} from '../lib/compilers/index.js'; import {JavaCompiler} from '../lib/compilers/index.js';
import * as utils from '../lib/utils.js'; import * as utils from '../lib/utils.js';
@@ -40,67 +42,71 @@ const info = {
lang: languages.java.id, lang: languages.java.id,
} as unknown as CompilerInfo; } as unknown as CompilerInfo;
describe('Basic compiler setup', function () { describe('Basic compiler setup', () => {
let env: CompilationEnvironment; let env: CompilationEnvironment;
before(() => { beforeAll(() => {
env = makeCompilationEnvironment({languages}); env = makeCompilationEnvironment({languages});
}); });
it('Should not crash on instantiation', function () { it('Should not crash on instantiation', () => {
new JavaCompiler(info, env); new JavaCompiler(info, env);
}); });
it('should ignore second param for getOutputFilename', function () { it('should ignore second param for getOutputFilename', () => {
// Because javac produces a class files based on user provided class names, // Because javac produces a class files based on user provided class names,
// it's not possible to determine the main class file before compilation/parsing // it's not possible to determine the main class file before compilation/parsing
const compiler = new JavaCompiler(info, env); const compiler = new JavaCompiler(info, env);
if (process.platform === 'win32') { if (process.platform === 'win32') {
compiler.getOutputFilename('/tmp/').should.equal('\\tmp\\example.class'); expect(compiler.getOutputFilename('/tmp/')).toEqual('\\tmp\\example.class');
} else { } else {
compiler.getOutputFilename('/tmp/').should.equal('/tmp/example.class'); expect(compiler.getOutputFilename('/tmp/')).toEqual('/tmp/example.class');
} }
}); });
describe('Forbidden compiler arguments', function () { describe('Forbidden compiler arguments', () => {
it('JavaCompiler should not allow -d parameter', () => { it('JavaCompiler should not allow -d parameter', () => {
const compiler = new JavaCompiler(info, env); const compiler = new JavaCompiler(info, env);
compiler expect(compiler.filterUserOptions(['hello', '-d', '--something', '--something-else'])).toEqual([
.filterUserOptions(['hello', '-d', '--something', '--something-else']) 'hello',
.should.deep.equal(['hello', '--something-else']); '--something-else',
compiler.filterUserOptions(['hello', '-d']).should.deep.equal(['hello']); ]);
compiler.filterUserOptions(['-d', 'something', 'something-else']).should.deep.equal(['something-else']); expect(compiler.filterUserOptions(['hello', '-d'])).toEqual(['hello']);
expect(compiler.filterUserOptions(['-d', 'something', 'something-else'])).toEqual(['something-else']);
}); });
it('JavaCompiler should not allow -s parameter', () => { it('JavaCompiler should not allow -s parameter', () => {
const compiler = new JavaCompiler(info, env); const compiler = new JavaCompiler(info, env);
compiler expect(compiler.filterUserOptions(['hello', '-s', '--something', '--something-else'])).toEqual([
.filterUserOptions(['hello', '-s', '--something', '--something-else']) 'hello',
.should.deep.equal(['hello', '--something-else']); '--something-else',
compiler.filterUserOptions(['hello', '-s']).should.deep.equal(['hello']); ]);
compiler.filterUserOptions(['-s', 'something', 'something-else']).should.deep.equal(['something-else']); expect(compiler.filterUserOptions(['hello', '-s'])).toEqual(['hello']);
expect(compiler.filterUserOptions(['-s', 'something', 'something-else'])).toEqual(['something-else']);
}); });
it('JavaCompiler should not allow --source-path parameter', () => { it('JavaCompiler should not allow --source-path parameter', () => {
const compiler = new JavaCompiler(info, env); const compiler = new JavaCompiler(info, env);
compiler expect(compiler.filterUserOptions(['hello', '--source-path', '--something', '--something-else'])).toEqual([
.filterUserOptions(['hello', '--source-path', '--something', '--something-else']) 'hello',
.should.deep.equal(['hello', '--something-else']); '--something-else',
compiler.filterUserOptions(['hello', '--source-path']).should.deep.equal(['hello']); ]);
compiler expect(compiler.filterUserOptions(['hello', '--source-path'])).toEqual(['hello']);
.filterUserOptions(['--source-path', 'something', 'something-else']) expect(compiler.filterUserOptions(['--source-path', 'something', 'something-else'])).toEqual([
.should.deep.equal(['something-else']); 'something-else',
]);
}); });
it('JavaCompiler should not allow -sourcepath parameter', () => { it('JavaCompiler should not allow -sourcepath parameter', () => {
const compiler = new JavaCompiler(info, env); const compiler = new JavaCompiler(info, env);
compiler expect(compiler.filterUserOptions(['hello', '-sourcepath', '--something', '--something-else'])).toEqual([
.filterUserOptions(['hello', '-sourcepath', '--something', '--something-else']) 'hello',
.should.deep.equal(['hello', '--something-else']); '--something-else',
compiler.filterUserOptions(['hello', '-sourcepath']).should.deep.equal(['hello']); ]);
compiler expect(compiler.filterUserOptions(['hello', '-sourcepath'])).toEqual(['hello']);
.filterUserOptions(['-sourcepath', 'something', 'something-else']) expect(compiler.filterUserOptions(['-sourcepath', 'something', 'something-else'])).toEqual([
.should.deep.equal(['something-else']); 'something-else',
]);
}); });
}); });
}); });
@@ -108,7 +114,7 @@ describe('Basic compiler setup', function () {
describe('javap parsing', () => { describe('javap parsing', () => {
let compiler: JavaCompiler; let compiler: JavaCompiler;
let env: CompilationEnvironment; let env: CompilationEnvironment;
before(() => { beforeAll(() => {
env = makeCompilationEnvironment({languages}); env = makeCompilationEnvironment({languages});
compiler = new JavaCompiler(info, env); compiler = new JavaCompiler(info, env);
}); });
@@ -143,9 +149,9 @@ describe('javap parsing', () => {
}; };
const processed = await compiler.processAsm(result); const processed = await compiler.processAsm(result);
processed.should.have.property('asm'); expect(processed).toHaveProperty('asm');
const asmSegments = (processed as {asm: ParsedAsmResultLine[]}).asm; const asmSegments = (processed as {asm: ParsedAsmResultLine[]}).asm;
asmSegments.should.deep.equal(expectedSegments); expect(asmSegments).toEqual(expectedSegments);
} }
it('should handle errors', async () => { it('should handle errors', async () => {
@@ -153,7 +159,7 @@ describe('javap parsing', () => {
asm: '<Compilation failed>', asm: '<Compilation failed>',
}; };
(await compiler.processAsm(result)).should.deep.equal([{text: '<Compilation failed>', source: null}]); await expect(compiler.processAsm(result)).resolves.toEqual([{text: '<Compilation failed>', source: null}]);
}); });
it('Parses simple class with one method', () => { it('Parses simple class with one method', () => {

View File

@@ -22,22 +22,24 @@
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE. // POSSIBILITY OF SUCH DAMAGE.
import {describe, expect, it} from 'vitest';
import {languages} from '../lib/languages.js'; import {languages} from '../lib/languages.js';
import {fs, path, should} from './utils.js'; import {fs, path} from './utils.js';
describe('Language definitions tests', () => { describe('Language definitions tests', () => {
it('Has id equal to object key', () => { it('Has id equal to object key', () => {
for (const languageKey of Object.keys(languages)) should.equal(languages[languageKey].id, languageKey); for (const languageKey of Object.keys(languages)) expect(languages[languageKey].id).toEqual(languageKey);
}); });
it('Has extensions with leading dots', () => { it('Has extensions with leading dots', () => {
for (const languageKey of Object.keys(languages)) should.equal(languages[languageKey].extensions[0][0], '.'); for (const languageKey of Object.keys(languages)) expect(languages[languageKey].extensions[0][0]).toEqual('.');
}); });
it('Has examples & are initialized', () => { it('Has examples & are initialized', () => {
for (const languageKey of Object.keys(languages)) { for (const languageKey of Object.keys(languages)) {
const lang = languages[languageKey]; const lang = languages[languageKey];
const example = fs.readFileSync(path.join('examples', lang.id, 'default' + lang.extensions[0]), 'utf8'); const example = fs.readFileSync(path.join('examples', lang.id, 'default' + lang.extensions[0]), 'utf8');
should.equal(example, lang.example); expect(example).toEqual(lang.example);
} }
}); });
}); });

View File

@@ -20,6 +20,7 @@
import path from 'path'; import path from 'path';
import fs from 'fs-extra'; import fs from 'fs-extra';
import {beforeAll, describe, expect, it} from 'vitest';
import {BaseCompiler} from '../lib/base-compiler.js'; import {BaseCompiler} from '../lib/base-compiler.js';
import {BuildEnvSetupBase} from '../lib/buildenvsetup/base.js'; import {BuildEnvSetupBase} from '../lib/buildenvsetup/base.js';
@@ -56,7 +57,7 @@ describe('Library directories (c++)', () => {
libsArr: ['fmt.10', 'qt.660', 'cpptrace.030'], libsArr: ['fmt.10', 'qt.660', 'cpptrace.030'],
}; };
before(() => { beforeAll(() => {
ce = makeCompilationEnvironment({languages}); ce = makeCompilationEnvironment({languages});
compiler = new BaseCompiler(info as CompilerInfo, ce); compiler = new BaseCompiler(info as CompilerInfo, ce);
(compiler as any).buildenvsetup = new BuildEnvSetupBase(info as CompilerInfo, ce); (compiler as any).buildenvsetup = new BuildEnvSetupBase(info as CompilerInfo, ce);
@@ -111,7 +112,7 @@ describe('Library directories (c++)', () => {
it('should add libpaths and link to libraries', () => { it('should add libpaths and link to libraries', () => {
const links = compiler.getSharedLibraryLinks([{id: 'fmt', version: '10'}]); const links = compiler.getSharedLibraryLinks([{id: 'fmt', version: '10'}]);
links.should.include('-lfmtd'); expect(links).toContain('-lfmtd');
const fmtpaths = (compiler as any).getSharedLibraryPathsAsArguments( const fmtpaths = (compiler as any).getSharedLibraryPathsAsArguments(
[{id: 'fmt', version: '10'}], [{id: 'fmt', version: '10'}],
@@ -119,7 +120,7 @@ describe('Library directories (c++)', () => {
undefined, undefined,
'/tmp/compiler-explorer-compiler-123', '/tmp/compiler-explorer-compiler-123',
); );
fmtpaths.should.include('-L./lib'); expect(fmtpaths).toContain('-L./lib');
const qtpaths = (compiler as any).getSharedLibraryPathsAsArguments( const qtpaths = (compiler as any).getSharedLibraryPathsAsArguments(
[{id: 'qt', version: '660'}], [{id: 'qt', version: '660'}],
@@ -127,7 +128,7 @@ describe('Library directories (c++)', () => {
undefined, undefined,
'/tmp/compiler-explorer-compiler-123', '/tmp/compiler-explorer-compiler-123',
); );
qtpaths.should.include('-L/tmp/compiler-explorer-compiler-123/qt/lib'); expect(qtpaths).toContain('-L/tmp/compiler-explorer-compiler-123/qt/lib');
}); });
it('should add libpaths and link to libraries when using nsjail', () => { it('should add libpaths and link to libraries when using nsjail', () => {
@@ -139,7 +140,7 @@ describe('Library directories (c++)', () => {
undefined, undefined,
'/tmp/compiler-explorer-compiler-123', '/tmp/compiler-explorer-compiler-123',
); );
fmtpaths.should.include('-L/tmp/compiler-explorer-compiler-123/fmt/lib'); expect(fmtpaths).toContain('-L/tmp/compiler-explorer-compiler-123/fmt/lib');
const qtpaths = (compiler as any).getSharedLibraryPathsAsArguments( const qtpaths = (compiler as any).getSharedLibraryPathsAsArguments(
[{id: 'qt', version: '660'}], [{id: 'qt', version: '660'}],
@@ -147,7 +148,7 @@ describe('Library directories (c++)', () => {
undefined, undefined,
'/tmp/compiler-explorer-compiler-123', '/tmp/compiler-explorer-compiler-123',
); );
qtpaths.should.include('-L/tmp/compiler-explorer-compiler-123/qt/lib'); expect(qtpaths).toContain('-L/tmp/compiler-explorer-compiler-123/qt/lib');
}); });
it('should add extra include paths when using packagedheaders', () => { it('should add extra include paths when using packagedheaders', () => {
@@ -157,15 +158,15 @@ describe('Library directories (c++)', () => {
[{id: 'fmt', version: '10'}], [{id: 'fmt', version: '10'}],
'/tmp/compiler-explorer-compiler-123', '/tmp/compiler-explorer-compiler-123',
); );
fmtpaths.should.not.include('-I/tmp/compiler-explorer-compiler-123/fmt/include'); expect(fmtpaths).not.toContain('-I/tmp/compiler-explorer-compiler-123/fmt/include');
fmtpaths.should.include('-I/opt/compiler-explorer/libs/fmt/1.0/include'); expect(fmtpaths).toContain('-I/opt/compiler-explorer/libs/fmt/1.0/include');
const qtpaths = (compiler as any).getIncludeArguments( const qtpaths = (compiler as any).getIncludeArguments(
[{id: 'qt', version: '660'}], [{id: 'qt', version: '660'}],
'/tmp/compiler-explorer-compiler-123', '/tmp/compiler-explorer-compiler-123',
); );
qtpaths.should.include('-I/opt/compiler-explorer/libs/qt/6.6.0/include'); expect(qtpaths).toContain('-I/opt/compiler-explorer/libs/qt/6.6.0/include');
qtpaths.should.include('-I/tmp/compiler-explorer-compiler-123/qt/include'); expect(qtpaths).toContain('-I/tmp/compiler-explorer-compiler-123/qt/include');
}); });
it('should set LD_LIBRARY_PATH when executing', () => { it('should set LD_LIBRARY_PATH when executing', () => {
@@ -175,17 +176,17 @@ describe('Library directories (c++)', () => {
[{id: 'qt', version: '660'}], [{id: 'qt', version: '660'}],
'/tmp/compiler-explorer-compiler-123', '/tmp/compiler-explorer-compiler-123',
); );
qtpaths.should.include('/tmp/compiler-explorer-compiler-123/qt/lib'); expect(qtpaths).toContain('/tmp/compiler-explorer-compiler-123/qt/lib');
}); });
it('should add libpaths and link when statically linking', () => { it('should add libpaths and link when statically linking', () => {
(compiler as any).executionType = 'nsjail'; (compiler as any).executionType = 'nsjail';
const staticlinks = compiler.getStaticLibraryLinks([{id: 'cpptrace', version: '030'}], []); const staticlinks = compiler.getStaticLibraryLinks([{id: 'cpptrace', version: '030'}], []);
staticlinks.should.include('-lcpptrace'); expect(staticlinks).toContain('-lcpptrace');
staticlinks.should.include('-ldwarf'); expect(staticlinks).toContain('-ldwarf');
staticlinks.should.include('-ldl'); expect(staticlinks).toContain('-ldl');
staticlinks.should.include('-lz'); expect(staticlinks).toContain('-lz');
const libpaths = (compiler as any).getSharedLibraryPathsAsArguments( const libpaths = (compiler as any).getSharedLibraryPathsAsArguments(
[{id: 'cpptrace', version: '030'}], [{id: 'cpptrace', version: '030'}],
@@ -193,7 +194,7 @@ describe('Library directories (c++)', () => {
undefined, undefined,
'/tmp/compiler-explorer-compiler-123', '/tmp/compiler-explorer-compiler-123',
); );
libpaths.should.include('-L/tmp/compiler-explorer-compiler-123/cpptrace/lib'); expect(libpaths).toContain('-L/tmp/compiler-explorer-compiler-123/cpptrace/lib');
}); });
}); });
@@ -214,7 +215,7 @@ describe('Library directories (fortran)', () => {
libsArr: ['json_fortran.830', 'curl.7831'], libsArr: ['json_fortran.830', 'curl.7831'],
}; };
before(() => { beforeAll(() => {
ce = makeCompilationEnvironment({languages}); ce = makeCompilationEnvironment({languages});
compiler = new FortranCompiler(info as CompilerInfo, ce); compiler = new FortranCompiler(info as CompilerInfo, ce);
(compiler as any).buildenvsetup = new BuildEnvSetupBase(info as CompilerInfo, ce); (compiler as any).buildenvsetup = new BuildEnvSetupBase(info as CompilerInfo, ce);
@@ -262,12 +263,12 @@ describe('Library directories (fortran)', () => {
await fs.mkdir(libPath, {recursive: true}); await fs.mkdir(libPath, {recursive: true});
const libPaths = compiler.getSharedLibraryPaths([{id: 'json_fortran', version: '830'}], dirPath); const libPaths = compiler.getSharedLibraryPaths([{id: 'json_fortran', version: '830'}], dirPath);
libPaths.should.include(libPath); expect(libPaths).toContain(libPath);
const libJsonFilepath = path.join(libPath, 'libjson-fortran.a'); const libJsonFilepath = path.join(libPath, 'libjson-fortran.a');
const failedLinks = compiler.getStaticLibraryLinks([{id: 'json_fortran', version: '830'}], libPaths); const failedLinks = compiler.getStaticLibraryLinks([{id: 'json_fortran', version: '830'}], libPaths);
failedLinks.should.not.include(libJsonFilepath); expect(failedLinks).not.toContain(libJsonFilepath);
}); });
it('should add libpaths and link to libraries', async () => { it('should add libpaths and link to libraries', async () => {
@@ -279,13 +280,13 @@ describe('Library directories (fortran)', () => {
const libJsonFilepath = path.join(libPath, 'libjson-fortran.a'); const libJsonFilepath = path.join(libPath, 'libjson-fortran.a');
const libPaths = compiler.getSharedLibraryPaths([{id: 'json_fortran', version: '830'}], dirPath); const libPaths = compiler.getSharedLibraryPaths([{id: 'json_fortran', version: '830'}], dirPath);
libPaths.should.include(libPath); expect(libPaths).toContain(libPath);
await fs.writeFile(libJsonFilepath, 'hello, world!'); await fs.writeFile(libJsonFilepath, 'hello, world!');
// the file is now here and Should be linked to // the file is now here and Should be linked to
const links = compiler.getStaticLibraryLinks([{id: 'json_fortran', version: '830'}], libPaths); const links = compiler.getStaticLibraryLinks([{id: 'json_fortran', version: '830'}], libPaths);
links.should.include(libJsonFilepath); expect(links).toContain(libJsonFilepath);
const paths = (compiler as any).getSharedLibraryPathsAsArguments( const paths = (compiler as any).getSharedLibraryPathsAsArguments(
[{id: 'json_fortran', version: '830'}], [{id: 'json_fortran', version: '830'}],
@@ -293,7 +294,7 @@ describe('Library directories (fortran)', () => {
undefined, undefined,
dirPath, dirPath,
); );
paths.should.include('-L' + libPath); expect(paths).toContain('-L' + libPath);
}); });
it('should add includes for packaged libraries', async () => { it('should add includes for packaged libraries', async () => {
@@ -305,8 +306,8 @@ describe('Library directories (fortran)', () => {
const cInclude = path.join(dirPath, 'json_fortran/include'); const cInclude = path.join(dirPath, 'json_fortran/include');
const paths = (compiler as any).getIncludeArguments([{id: 'json_fortran', version: '830'}], dirPath); const paths = (compiler as any).getIncludeArguments([{id: 'json_fortran', version: '830'}], dirPath);
paths.should.include('-I' + fortranInclude); expect(paths).toContain('-I' + fortranInclude);
paths.should.include('-isystem' + cInclude); expect(paths).toContain('-isystem' + cInclude);
}); });
it('should add includes for non-packaged C libraries', async () => { it('should add includes for non-packaged C libraries', async () => {
@@ -316,6 +317,6 @@ describe('Library directories (fortran)', () => {
const dirPath = await compiler.newTempDir(); const dirPath = await compiler.newTempDir();
const paths = (compiler as any).getIncludeArguments([{id: 'curl', version: '7831'}], dirPath); const paths = (compiler as any).getIncludeArguments([{id: 'curl', version: '7831'}], dirPath);
paths.should.include('-isystem/opt/compiler-explorer/libs/curl/7.83.1/include'); expect(paths).toContain('-isystem/opt/compiler-explorer/libs/curl/7.83.1/include');
}); });
}); });

View File

@@ -23,12 +23,13 @@
// POSSIBILITY OF SUCH DAMAGE. // POSSIBILITY OF SUCH DAMAGE.
import cloneDeep from 'lodash.clonedeep'; import cloneDeep from 'lodash.clonedeep';
import {beforeAll, describe, expect, it} from 'vitest';
import {LlvmAstParser} from '../lib/llvm-ast.js'; import {LlvmAstParser} from '../lib/llvm-ast.js';
import * as properties from '../lib/properties.js'; import * as properties from '../lib/properties.js';
import * as utils from '../lib/utils.js'; import * as utils from '../lib/utils.js';
import {fs, should} from './utils.js'; import {fs} from './utils.js';
const languages = { const languages = {
'c++': {id: 'c++'}, 'c++': {id: 'c++'},
@@ -38,7 +39,7 @@ function mockAstOutput(astLines) {
return {stdout: astLines.map(l => ({text: l}))}; return {stdout: astLines.map(l => ({text: l}))};
} }
describe('llvm-ast', function () { describe('llvm-ast', () => {
let compilerProps; let compilerProps;
let astParser; let astParser;
let astDump; let astDump;
@@ -46,7 +47,7 @@ describe('llvm-ast', function () {
let astDumpWithCTime; let astDumpWithCTime;
let astDumpNestedDecl1346; let astDumpNestedDecl1346;
before(() => { beforeAll(() => {
const fakeProps = new properties.CompilerProps(languages, properties.fakeProps({})); const fakeProps = new properties.CompilerProps(languages, properties.fakeProps({}));
compilerProps = (fakeProps.get as any).bind(fakeProps, 'c++'); compilerProps = (fakeProps.get as any).bind(fakeProps, 'c++');
@@ -60,60 +61,61 @@ describe('llvm-ast', function () {
it('keeps fewer lines than the original', () => { it('keeps fewer lines than the original', () => {
const origHeight = astDump.length; const origHeight = astDump.length;
const processed = astParser.processAst(cloneDeep(compilerOutput)); const processed = astParser.processAst(cloneDeep(compilerOutput));
processed.length.should.be.below(origHeight); expect(processed.length).toBeLessThan(origHeight);
}); });
it('removes invalid slocs', () => { it('removes invalid slocs', () => {
const processed = astParser.processAst(cloneDeep(compilerOutput)); const processed = astParser.processAst(cloneDeep(compilerOutput));
astDump.should.match(/<invalid sloc>/); expect(astDump).toMatch(/<invalid sloc>/);
const fullText = processed.map(l => l.text).join('\n'); const fullText = processed.map(l => l.text).join('\n');
fullText.should.not.match(/<invalid sloc>/); expect(fullText).not.toMatch(/<invalid sloc>/);
}); });
it('keeps reasonable-sized output', () => { it('keeps reasonable-sized output', () => {
astDumpWithCTime.length.should.be.above(200); expect(astDumpWithCTime.length).toBeGreaterThan(200);
const output = mockAstOutput(astDumpWithCTime); const output = mockAstOutput(astDumpWithCTime);
const processed = astParser.processAst(output); const processed = astParser.processAst(output);
processed.length.should.be.below(200); expect(processed.length).toBeLessThan(200);
}); });
it('links some source lines', () => { it('links some source lines', () => {
should.exist(compilerOutput.stdout.find(l => l.text.match(/col:21, line:4:1/))); expect(compilerOutput.stdout.find(l => l.text.match(/col:21, line:4:1/))).toBeTruthy();
should.exist(compilerOutput.stdout.find(l => l.text.match(/line:3:5, col:18/))); expect(compilerOutput.stdout.find(l => l.text.match(/line:3:5, col:18/))).toBeTruthy();
const processed = astParser.processAst(cloneDeep(compilerOutput)); const processed = astParser.processAst(cloneDeep(compilerOutput));
should.exist(processed.find(l => l.source && 0 < l.source.from.line)); expect(processed.find(l => l.source && 0 < l.source.from.line)).toBeTruthy();
processed.find(l => l.text.match(/col:21, line:4:1/)).source.to.line.should.equal(4); expect(processed.find(l => l.text.match(/col:21, line:4:1/))).toMatchObject({
processed.find(l => l.text.match(/col:21, line:4:1/)).source.to.col.should.equal(1); source: {to: {line: 4, col: 1}, from: {line: 2, col: 21}},
processed.find(l => l.text.match(/col:21, line:4:1/)).source.from.col.should.equal(21); });
processed.find(l => l.text.match(/line:3:5, col:18/)).source.from.line.should.equal(3); expect(processed.find(l => l.text.match(/line:3:5, col:18/))).toMatchObject({
processed.find(l => l.text.match(/line:3:5, col:18/)).source.from.col.should.equal(5); source: {to: {line: 3, col: 18}, from: {line: 3, col: 5}},
processed.find(l => l.text.match(/line:3:5, col:18/)).source.to.line.should.equal(3); });
processed.find(l => l.text.match(/line:3:5, col:18/)).source.to.col.should.equal(18);
// Here "from.line" is inherited from the parent "FunctionDecl <<source>:2:1, line:4:1>" // Here "from.line" is inherited from the parent "FunctionDecl <<source>:2:1, line:4:1>"
processed.find(l => l.text.match(/CompoundStmt.*<col:21, line:4:1>/)).source.from.line.should.equal(2); expect(processed.find(l => l.text.match(/CompoundStmt.*<col:21, line:4:1>/))).toMatchObject({
source: {from: {line: 2}},
});
}); });
it('does not truncate nested declarations', () => { it('does not truncate nested declarations', () => {
// See https://github.com/compiler-explorer/compiler-explorer/issues/1346 // See https://github.com/compiler-explorer/compiler-explorer/issues/1346
const output = mockAstOutput(astDumpNestedDecl1346); const output = mockAstOutput(astDumpNestedDecl1346);
const processed = astParser.processAst(output); const processed = astParser.processAst(output);
processed.length.should.be.above(2); expect(processed.length).toBeGreaterThan(2);
should.exist(processed.find(l => l.text.match(/CXXRecordDecl.*struct x/))); expect(processed.find(l => l.text.match(/CXXRecordDecl.*struct x/))).toBeTruthy();
should.exist(processed.find(l => l.text.match(/TypedefDecl.*struct x/))); expect(processed.find(l => l.text.match(/TypedefDecl.*struct x/))).toBeTruthy();
should.exist(processed.find(l => l.text.match(/ElaboratedType/))); expect(processed.find(l => l.text.match(/ElaboratedType/))).toBeTruthy();
should.exist(processed.find(l => l.text.match(/RecordType/))); expect(processed.find(l => l.text.match(/RecordType/))).toBeTruthy();
should.exist(processed.find(l => l.text.match(/CXXRecord/))); expect(processed.find(l => l.text.match(/CXXRecord/))).toBeTruthy();
}); });
}); });
describe('llvm-ast bug-3849a', function () { describe('llvm-ast bug-3849a', () => {
let compilerProps; let compilerProps;
let astParser; let astParser;
let astDump; let astDump;
let compilerOutput; let compilerOutput;
before(() => { beforeAll(() => {
const fakeProps = new properties.CompilerProps(languages, properties.fakeProps({})); const fakeProps = new properties.CompilerProps(languages, properties.fakeProps({}));
compilerProps = (fakeProps.get as any).bind(fakeProps, 'c++'); compilerProps = (fakeProps.get as any).bind(fakeProps, 'c++');
@@ -124,17 +126,17 @@ describe('llvm-ast bug-3849a', function () {
it('should have more than 2 lines', () => { it('should have more than 2 lines', () => {
const processed = astParser.processAst(compilerOutput); const processed = astParser.processAst(compilerOutput);
processed.length.should.be.above(2); expect(processed.length).toBeGreaterThan(2);
}); });
}); });
describe('llvm-ast bug-3849b', function () { describe('llvm-ast bug-3849b', () => {
let compilerProps; let compilerProps;
let astParser; let astParser;
let astDump; let astDump;
let compilerOutput; let compilerOutput;
before(() => { beforeAll(() => {
const fakeProps = new properties.CompilerProps(languages, properties.fakeProps({})); const fakeProps = new properties.CompilerProps(languages, properties.fakeProps({}));
compilerProps = (fakeProps.get as any).bind(fakeProps, 'c++'); compilerProps = (fakeProps.get as any).bind(fakeProps, 'c++');
@@ -145,18 +147,18 @@ describe('llvm-ast bug-3849b', function () {
it('should have not too many lines', () => { it('should have not too many lines', () => {
const processed = astParser.processAst(compilerOutput); const processed = astParser.processAst(compilerOutput);
processed.length.should.be.above(200); expect(processed.length).toBeGreaterThan(200);
processed.length.should.be.below(300); expect(processed.length).toBeLessThan(300);
}); });
}); });
describe('llvm-ast bug-5889', function () { describe('llvm-ast bug-5889', () => {
let compilerProps; let compilerProps;
let astParser; let astParser;
let astDump; let astDump;
let compilerOutput; let compilerOutput;
before(() => { beforeAll(() => {
const fakeProps = new properties.CompilerProps(languages, properties.fakeProps({})); const fakeProps = new properties.CompilerProps(languages, properties.fakeProps({}));
compilerProps = (fakeProps.get as any).bind(fakeProps, 'c++'); compilerProps = (fakeProps.get as any).bind(fakeProps, 'c++');
@@ -167,6 +169,6 @@ describe('llvm-ast bug-5889', function () {
it('should have not too many lines', () => { it('should have not too many lines', () => {
const processed = astParser.processAst(compilerOutput); const processed = astParser.processAst(compilerOutput);
processed.length.should.be.below(50); expect(processed.length).toBeLessThan(50);
}); });
}); });

View File

@@ -22,31 +22,29 @@
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE. // POSSIBILITY OF SUCH DAMAGE.
import {beforeAll, describe, expect, it} from 'vitest';
import {LLVMIRDemangler} from '../lib/demangler/llvm.js'; import {LLVMIRDemangler} from '../lib/demangler/llvm.js';
import {LlvmIrParser} from '../lib/llvm-ir.js'; import {LlvmIrParser} from '../lib/llvm-ir.js';
import * as properties from '../lib/properties.js'; import * as properties from '../lib/properties.js';
import {chai} from './utils.js';
const expect = chai.expect;
const languages = { const languages = {
'c++': {id: 'c++'}, 'c++': {id: 'c++'},
}; };
describe('llvm-ir parseMetaNode', function () { describe('llvm-ir parseMetaNode', () => {
let llvmIrParser; let llvmIrParser;
let compilerProps; let compilerProps;
before(() => { beforeAll(() => {
const fakeProps = new properties.CompilerProps(languages, properties.fakeProps({})); const fakeProps = new properties.CompilerProps(languages, properties.fakeProps({}));
compilerProps = (fakeProps.get as any).bind(fakeProps, 'c++'); compilerProps = (fakeProps.get as any).bind(fakeProps, 'c++');
llvmIrParser = new LlvmIrParser(compilerProps, undefined as unknown as LLVMIRDemangler); llvmIrParser = new LlvmIrParser(compilerProps, undefined as unknown as LLVMIRDemangler);
}); });
it('should parse DILocation node', function () { it('should parse DILocation node', () => {
llvmIrParser.parseMetaNode('!60 = !DILocation(line: 9, column: 15, scope: !58)').should.deep.equal({ expect(llvmIrParser.parseMetaNode('!60 = !DILocation(line: 9, column: 15, scope: !58)')).toEqual({
metaType: 'Location', metaType: 'Location',
metaId: '!60', metaId: '!60',
line: '9', line: '9',
@@ -55,10 +53,10 @@ describe('llvm-ir parseMetaNode', function () {
}); });
}); });
it('should parse distinct DILexicalBlock', function () { it('should parse distinct DILexicalBlock', () => {
llvmIrParser expect(
.parseMetaNode('!50 = distinct !DILexicalBlock(scope: !44, file: !1, line: 8, column: 5)') llvmIrParser.parseMetaNode('!50 = distinct !DILexicalBlock(scope: !44, file: !1, line: 8, column: 5)'),
.should.deep.equal({ ).toEqual({
metaType: 'LexicalBlock', metaType: 'LexicalBlock',
metaId: '!50', metaId: '!50',
scope: '!44', scope: '!44',
@@ -68,14 +66,14 @@ describe('llvm-ir parseMetaNode', function () {
}); });
}); });
it('should parse all value types', function () { it('should parse all value types', () => {
llvmIrParser expect(
.parseMetaNode( llvmIrParser.parseMetaNode(
'!44 = distinct !DISubprogram(name: "func<int, int>", ' + '!44 = distinct !DISubprogram(name: "func<int, int>", ' +
'scope: !1, line: 7, isLocal: false, isDefinition: true, flags: ' + 'scope: !1, line: 7, isLocal: false, isDefinition: true, flags: ' +
'DIFlagPrototyped, ceEmpty: "", ceTest: "a:b\\"c,d")', 'DIFlagPrototyped, ceEmpty: "", ceTest: "a:b\\"c,d")',
) ),
.should.deep.equal({ ).toEqual({
metaType: 'Subprogram', metaType: 'Subprogram',
metaId: '!44', metaId: '!44',
name: 'func<int, int>', name: 'func<int, int>',
@@ -89,10 +87,12 @@ describe('llvm-ir parseMetaNode', function () {
}); });
}); });
it('should parse distinct DILexicalBlock', function () { it('should parse distinct DILexicalBlock', () => {
llvmIrParser expect(
.parseMetaNode('!1 = !DIFile(filename: "/tmp/example.cpp", directory: "/home/compiler-explorer")') llvmIrParser.parseMetaNode(
.should.deep.equal({ '!1 = !DIFile(filename: "/tmp/example.cpp", directory: "/home/compiler-explorer")',
),
).toEqual({
metaType: 'File', metaType: 'File',
metaId: '!1', metaId: '!1',
filename: '/tmp/example.cpp', filename: '/tmp/example.cpp',
@@ -101,11 +101,11 @@ describe('llvm-ir parseMetaNode', function () {
}); });
}); });
describe('llvm-ir getSourceLineNumber', function () { describe('llvm-ir getSourceLineNumber', () => {
let llvmIrParser; let llvmIrParser;
let compilerProps; let compilerProps;
before(() => { beforeAll(() => {
const fakeProps = new properties.CompilerProps(languages, properties.fakeProps({})); const fakeProps = new properties.CompilerProps(languages, properties.fakeProps({}));
compilerProps = (fakeProps.get as any).bind(fakeProps, 'c++'); compilerProps = (fakeProps.get as any).bind(fakeProps, 'c++');
@@ -122,31 +122,31 @@ describe('llvm-ir getSourceLineNumber', function () {
'!16': {scope: '!42'}, '!16': {scope: '!42'},
}; };
it('should return a line number', function () { it('should return a line number', () => {
expect(llvmIrParser.getSourceLineNumber(debugInfo, '!10')).to.equal(10); expect(llvmIrParser.getSourceLineNumber(debugInfo, '!10')).toBe(10);
expect(llvmIrParser.getSourceLineNumber(debugInfo, '!20')).to.equal(20); expect(llvmIrParser.getSourceLineNumber(debugInfo, '!20')).toBe(20);
}); });
it('should return the line number of its parent scope', function () { it('should return the line number of its parent scope', () => {
expect(llvmIrParser.getSourceLineNumber(debugInfo, '!11')).to.equal(10); expect(llvmIrParser.getSourceLineNumber(debugInfo, '!11')).toBe(10);
expect(llvmIrParser.getSourceLineNumber(debugInfo, '!12')).to.equal(10); expect(llvmIrParser.getSourceLineNumber(debugInfo, '!12')).toBe(10);
}); });
it('should return null on non-existend node', function () { it('should return null on non-existend node', () => {
expect(llvmIrParser.getSourceLineNumber(debugInfo, '!16')).to.equal(null); expect(llvmIrParser.getSourceLineNumber(debugInfo, '!16')).toBe(null);
}); });
it('should return null if no higher scope has a line', function () { it('should return null if no higher scope has a line', () => {
expect(llvmIrParser.getSourceLineNumber(debugInfo, '!14')).to.equal(null); expect(llvmIrParser.getSourceLineNumber(debugInfo, '!14')).toBe(null);
expect(llvmIrParser.getSourceLineNumber(debugInfo, '!15')).to.equal(null); expect(llvmIrParser.getSourceLineNumber(debugInfo, '!15')).toBe(null);
}); });
}); });
describe('llvm-ir getSourceColumn', function () { describe('llvm-ir getSourceColumn', () => {
let llvmIrParser; let llvmIrParser;
let compilerProps; let compilerProps;
before(() => { beforeAll(() => {
const fakeProps = new properties.CompilerProps(languages, properties.fakeProps({})); const fakeProps = new properties.CompilerProps(languages, properties.fakeProps({}));
compilerProps = (fakeProps.get as any).bind(fakeProps, 'c++'); compilerProps = (fakeProps.get as any).bind(fakeProps, 'c++');
@@ -163,32 +163,32 @@ describe('llvm-ir getSourceColumn', function () {
'!16': {scope: '!42'}, '!16': {scope: '!42'},
}; };
it('should return a column number', function () { it('should return a column number', () => {
expect(llvmIrParser.getSourceColumn(debugInfo, '!10')).to.equal(10); expect(llvmIrParser.getSourceColumn(debugInfo, '!10')).toBe(10);
expect(llvmIrParser.getSourceColumn(debugInfo, '!20')).to.equal(20); expect(llvmIrParser.getSourceColumn(debugInfo, '!20')).toBe(20);
}); });
it('should return the column number of its parent scope', function () { it('should return the column number of its parent scope', () => {
expect(llvmIrParser.getSourceColumn(debugInfo, '!11')).to.equal(10); expect(llvmIrParser.getSourceColumn(debugInfo, '!11')).toBe(10);
expect(llvmIrParser.getSourceColumn(debugInfo, '!12')).to.equal(10); expect(llvmIrParser.getSourceColumn(debugInfo, '!12')).toBe(10);
}); });
it('should return undefined on non-existend node', function () { it('should return undefined on non-existend node', () => {
expect(llvmIrParser.getSourceColumn(debugInfo, '!16')).to.equal(undefined); expect(llvmIrParser.getSourceColumn(debugInfo, '!16')).toBe(undefined);
expect(llvmIrParser.getSourceColumn(debugInfo, '!30')).to.equal(undefined); expect(llvmIrParser.getSourceColumn(debugInfo, '!30')).toBe(undefined);
}); });
it('should return undefined if no higher scope has a column', function () { it('should return undefined if no higher scope has a column', () => {
expect(llvmIrParser.getSourceColumn(debugInfo, '!14')).to.equal(undefined); expect(llvmIrParser.getSourceColumn(debugInfo, '!14')).toBe(undefined);
expect(llvmIrParser.getSourceColumn(debugInfo, '!15')).to.equal(undefined); expect(llvmIrParser.getSourceColumn(debugInfo, '!15')).toBe(undefined);
}); });
}); });
describe('llvm-ir getFileName', function () { describe('llvm-ir getFileName', () => {
let llvmIrParser; let llvmIrParser;
let compilerProps; let compilerProps;
before(() => { beforeAll(() => {
const fakeProps = new properties.CompilerProps(languages, properties.fakeProps({})); const fakeProps = new properties.CompilerProps(languages, properties.fakeProps({}));
compilerProps = (fakeProps.get as any).bind(fakeProps, 'c++'); compilerProps = (fakeProps.get as any).bind(fakeProps, 'c++');
@@ -203,22 +203,22 @@ describe('llvm-ir getFileName', function () {
'!13': {scope: '!12'}, '!13': {scope: '!12'},
}; };
it('should return a filename', function () { it('should return a filename', () => {
expect(llvmIrParser.getFileName(debugInfo, '!10')).to.equal('/test.cpp'); expect(llvmIrParser.getFileName(debugInfo, '!10')).toBe('/test.cpp');
expect(llvmIrParser.getFileName(debugInfo, '!11')).to.equal('/test.cpp'); expect(llvmIrParser.getFileName(debugInfo, '!11')).toBe('/test.cpp');
}); });
it('should return the filename of its parent scope', function () { it('should return the filename of its parent scope', () => {
expect(llvmIrParser.getFileName(debugInfo, '!12')).to.equal('/test.cpp'); expect(llvmIrParser.getFileName(debugInfo, '!12')).toBe('/test.cpp');
expect(llvmIrParser.getFileName(debugInfo, '!13')).to.equal('/test.cpp'); expect(llvmIrParser.getFileName(debugInfo, '!13')).toBe('/test.cpp');
}); });
it('should return null on non-existend node', function () { it('should return null on non-existend node', () => {
expect(llvmIrParser.getFileName(debugInfo, '!42')).to.equal(null); expect(llvmIrParser.getFileName(debugInfo, '!42')).toBe(null);
}); });
it('should not return source filename', function () { it('should not return source filename', () => {
expect(llvmIrParser.getFileName(debugInfo, '!20')).to.equal(null); expect(llvmIrParser.getFileName(debugInfo, '!20')).toBe(null);
expect(llvmIrParser.getFileName(debugInfo, '!21')).to.equal(null); expect(llvmIrParser.getFileName(debugInfo, '!21')).toBe(null);
}); });
}); });

View File

@@ -22,6 +22,8 @@
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE. // POSSIBILITY OF SUCH DAMAGE.
import {beforeAll, describe, expect, it} from 'vitest';
import {LLCCompiler} from '../lib/compilers/llc.js'; import {LLCCompiler} from '../lib/compilers/llc.js';
import {OptCompiler} from '../lib/compilers/opt.js'; import {OptCompiler} from '../lib/compilers/opt.js';
@@ -50,69 +52,69 @@ function createCompiler(compiler) {
describe('LLVM IR Compiler', () => { describe('LLVM IR Compiler', () => {
let compiler; let compiler;
before(() => { beforeAll(() => {
compiler = createCompiler(LLCCompiler); compiler = createCompiler(LLCCompiler);
}); });
it('llc options for at&t assembly', function () { it('llc options for at&t assembly', () => {
compiler expect(
.optionsForFilter( compiler.optionsForFilter(
{ {
intel: false, intel: false,
binary: false, binary: false,
}, },
'output.s', 'output.s',
) ),
.should.eql(['-o', 'output.s']); ).toEqual(['-o', 'output.s']);
}); });
it('llc options for intel assembly', function () { it('llc options for intel assembly', () => {
compiler expect(
.optionsForFilter( compiler.optionsForFilter(
{ {
intel: true, intel: true,
binary: false, binary: false,
}, },
'output.s', 'output.s',
) ),
.should.eql(['-o', 'output.s', '-x86-asm-syntax=intel']); ).toEqual(['-o', 'output.s', '-x86-asm-syntax=intel']);
}); });
it('llc options for at&t binary', function () { it('llc options for at&t binary', () => {
compiler expect(
.optionsForFilter( compiler.optionsForFilter(
{ {
intel: false, intel: false,
binary: true, binary: true,
}, },
'output.s', 'output.s',
) ),
.should.eql(['-o', 'output.s', '-filetype=obj']); ).toEqual(['-o', 'output.s', '-filetype=obj']);
}); });
it('llc options for intel binary', function () { it('llc options for intel binary', () => {
compiler expect(
.optionsForFilter( compiler.optionsForFilter(
{ {
intel: true, intel: true,
binary: true, binary: true,
}, },
'output.s', 'output.s',
) ),
.should.eql(['-o', 'output.s', '-filetype=obj']); ).toEqual(['-o', 'output.s', '-filetype=obj']);
}); });
it('opt options', function () { it('opt options', () => {
const compiler = createCompiler(OptCompiler); const compiler = createCompiler(OptCompiler);
compiler expect(
.optionsForFilter( compiler.optionsForFilter(
{ {
intel: false, intel: false,
binary: false, binary: false,
}, },
'output.s', 'output.s',
) ),
.should.eql(['-o', 'output.s', '-S']); ).toEqual(['-o', 'output.s', '-S']);
}); });
}); });

View File

@@ -19,6 +19,7 @@
import * as stream from 'stream'; import * as stream from 'stream';
import {describe, expect, it} from 'vitest';
import {YAMLParseError} from 'yaml'; import {YAMLParseError} from 'yaml';
import {LLVMOptTransformer} from '../lib/llvm-opt-transformer.js'; import {LLVMOptTransformer} from '../lib/llvm-opt-transformer.js';
@@ -53,7 +54,7 @@ Args:
for await (const opt of optStream) { for await (const opt of optStream) {
output.push(opt); output.push(opt);
} }
output.should.deep.equal([ expect(output).toEqual([
{ {
Args: [ Args: [
{ {
@@ -105,13 +106,15 @@ broken: duplicate key makes this invalid
const readString = new stream.PassThrough(); const readString = new stream.PassThrough();
readString.push(doc); readString.push(doc);
readString.end(); readString.end();
return (async () => { await expect(
(async () => {
const optStream = stream.pipeline(readString, new LLVMOptTransformer(), res => { const optStream = stream.pipeline(readString, new LLVMOptTransformer(), res => {
return res; return res;
}); });
for await (const _ of optStream) { for await (const _ of optStream) {
// just consume // just consume
} }
})().should.be.rejectedWith(YAMLParseError); })(),
).rejects.toThrow(YAMLParseError);
}); });
}); });

View File

@@ -22,6 +22,8 @@
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE. // POSSIBILITY OF SUCH DAMAGE.
import {beforeAll, describe, expect, it} from 'vitest';
import {LlvmPassDumpParser} from '../lib/parsers/llvm-pass-dump-parser.js'; import {LlvmPassDumpParser} from '../lib/parsers/llvm-pass-dump-parser.js';
import * as properties from '../lib/properties.js'; import * as properties from '../lib/properties.js';
@@ -33,10 +35,10 @@ function deepCopy(obj) {
return JSON.parse(JSON.stringify(obj)); return JSON.parse(JSON.stringify(obj));
} }
describe('llvm-pass-dump-parser filter', function () { describe('llvm-pass-dump-parser filter', () => {
let llvmPassDumpParser; let llvmPassDumpParser;
before(() => { beforeAll(() => {
const fakeProps = new properties.CompilerProps(languages, properties.fakeProps({})); const fakeProps = new properties.CompilerProps(languages, properties.fakeProps({}));
const compilerProps = (fakeProps.get as any).bind(fakeProps, 'c++'); const compilerProps = (fakeProps.get as any).bind(fakeProps, 'c++');
llvmPassDumpParser = new LlvmPassDumpParser(compilerProps); llvmPassDumpParser = new LlvmPassDumpParser(compilerProps);
@@ -63,20 +65,20 @@ describe('llvm-pass-dump-parser filter', function () {
{ text: ' ret void, !dbg !61' }, { text: ' ret void, !dbg !61' },
]; ];
it('should not filter out dbg metadata', function () { it('should not filter out dbg metadata', () => {
const options = {filterDebugInfo: false}; const options = {filterDebugInfo: false};
// prettier-ignore // prettier-ignore
llvmPassDumpParser expect(llvmPassDumpParser
.applyIrFilters(deepCopy(rawFuncIR), options) .applyIrFilters(deepCopy(rawFuncIR), options),
.should.deep.equal(rawFuncIR); ).toEqual(rawFuncIR);
}); });
it('should filter out dbg metadata too', function () { it('should filter out dbg metadata too', () => {
const options = {filterDebugInfo: true}; const options = {filterDebugInfo: true};
// prettier-ignore // prettier-ignore
llvmPassDumpParser expect(llvmPassDumpParser
.applyIrFilters(deepCopy(rawFuncIR), options) .applyIrFilters(deepCopy(rawFuncIR), options),
.should.deep.equal([ ).toEqual([
{ text: ' # Machine code for function f(S1&, S2 const&): NoPHIs, TracksLiveness, TiedOpsRewritten' }, { text: ' # Machine code for function f(S1&, S2 const&): NoPHIs, TracksLiveness, TiedOpsRewritten' },
{ text: 'define dso_local void @f(S1&, S2 const&)(%struct.S1* noundef nonnull align 8 dereferenceable(16) %s1, %struct.S2* noundef nonnull align 8 dereferenceable(16) %s2) {' }, { text: 'define dso_local void @f(S1&, S2 const&)(%struct.S1* noundef nonnull align 8 dereferenceable(16) %s1, %struct.S2* noundef nonnull align 8 dereferenceable(16) %s2) {' },
{ text: 'entry:' }, { text: 'entry:' },
@@ -93,13 +95,13 @@ describe('llvm-pass-dump-parser filter', function () {
]); ]);
}); });
it('should filter out instruction metadata and object attribute group, leave debug instructions in place', function () { it('should filter out instruction metadata and object attribute group, leave debug instructions in place', () => {
// 'hide IR metadata' aims to decrease more visual noise than `hide debug info` // 'hide IR metadata' aims to decrease more visual noise than `hide debug info`
const options = {filterDebugInfo: false, filterIRMetadata: true}; const options = {filterDebugInfo: false, filterIRMetadata: true};
// prettier-ignore // prettier-ignore
llvmPassDumpParser expect(llvmPassDumpParser
.applyIrFilters(deepCopy(rawFuncIR), options) .applyIrFilters(deepCopy(rawFuncIR), options),
.should.deep.equal([ ).toEqual([
{ text: ' # Machine code for function f(S1&, S2 const&): NoPHIs, TracksLiveness, TiedOpsRewritten' }, { text: ' # Machine code for function f(S1&, S2 const&): NoPHIs, TracksLiveness, TiedOpsRewritten' },
{ text: 'define dso_local void @f(S1&, S2 const&)(%struct.S1* noundef nonnull align 8 dereferenceable(16) %s1, %struct.S2* noundef nonnull align 8 dereferenceable(16) %s2) {' }, { text: 'define dso_local void @f(S1&, S2 const&)(%struct.S1* noundef nonnull align 8 dereferenceable(16) %s1, %struct.S2* noundef nonnull align 8 dereferenceable(16) %s2) {' },
{ text: 'entry:' }, { text: 'entry:' },
@@ -122,10 +124,10 @@ describe('llvm-pass-dump-parser filter', function () {
}); });
}); });
describe('llvm-pass-dump-parser Old style IR Dump header', function () { describe('llvm-pass-dump-parser Old style IR Dump header', () => {
let llvmPassDumpParser; let llvmPassDumpParser;
before(() => { beforeAll(() => {
const fakeProps = new properties.CompilerProps(languages, properties.fakeProps({})); const fakeProps = new properties.CompilerProps(languages, properties.fakeProps({}));
const compilerProps = (fakeProps.get as any).bind(fakeProps, 'c++'); const compilerProps = (fakeProps.get as any).bind(fakeProps, 'c++');
llvmPassDumpParser = new LlvmPassDumpParser(compilerProps); llvmPassDumpParser = new LlvmPassDumpParser(compilerProps);
@@ -145,12 +147,12 @@ describe('llvm-pass-dump-parser Old style IR Dump header', function () {
{ text: '}' }, { text: '}' },
]; ];
it('should recognize dump', function () { it('should recognize dump', () => {
const options = {filterDebugInfo: false}; const options = {filterDebugInfo: false};
const brokenDown = llvmPassDumpParser.breakdownOutputIntoPassDumps(deepCopy(rawFuncIR), options); const brokenDown = llvmPassDumpParser.breakdownOutputIntoPassDumps(deepCopy(rawFuncIR), options);
brokenDown.should.deep.equal([ expect(brokenDown).toEqual([
{ {
affectedFunction: undefined, affectedFunction: undefined,
header: 'IR Dump After NoOpModulePass on [module]', header: 'IR Dump After NoOpModulePass on [module]',
@@ -171,10 +173,10 @@ describe('llvm-pass-dump-parser Old style IR Dump header', function () {
}); });
}); });
describe('llvm-pass-dump-parser New style IR Dump header', function () { describe('llvm-pass-dump-parser New style IR Dump header', () => {
let llvmPassDumpParser; let llvmPassDumpParser;
before(() => { beforeAll(() => {
const fakeProps = new properties.CompilerProps(languages, properties.fakeProps({})); const fakeProps = new properties.CompilerProps(languages, properties.fakeProps({}));
const compilerProps = (fakeProps.get as any).bind(fakeProps, 'c++'); const compilerProps = (fakeProps.get as any).bind(fakeProps, 'c++');
llvmPassDumpParser = new LlvmPassDumpParser(compilerProps); llvmPassDumpParser = new LlvmPassDumpParser(compilerProps);
@@ -194,12 +196,12 @@ describe('llvm-pass-dump-parser New style IR Dump header', function () {
{ text: '}' }, { text: '}' },
]; ];
it('should recognize dump', function () { it('should recognize dump', () => {
const options = {filterDebugInfo: false}; const options = {filterDebugInfo: false};
const brokenDown = llvmPassDumpParser.breakdownOutputIntoPassDumps(deepCopy(rawFuncIR), options); const brokenDown = llvmPassDumpParser.breakdownOutputIntoPassDumps(deepCopy(rawFuncIR), options);
brokenDown.should.deep.equal([ expect(brokenDown).toEqual([
{ {
affectedFunction: undefined, affectedFunction: undefined,
header: 'IR Dump After NoOpModulePass on [module]', header: 'IR Dump After NoOpModulePass on [module]',

View File

@@ -22,87 +22,85 @@
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE. // POSSIBILITY OF SUCH DAMAGE.
import {describe, expect, it} from 'vitest';
import {unwrap} from '../lib/assert.js'; import {unwrap} from '../lib/assert.js';
import {MapFileReaderDelphi} from '../lib/mapfiles/map-file-delphi.js'; import {MapFileReaderDelphi} from '../lib/mapfiles/map-file-delphi.js';
import {MapFileReaderVS} from '../lib/mapfiles/map-file-vs.js'; import {MapFileReaderVS} from '../lib/mapfiles/map-file-vs.js';
import {chai} from './utils.js'; describe('Map setup', () => {
it('VS-map preferred load address', () => {
const expect = chai.expect;
describe('Map setup', function () {
it('VS-map preferred load address', function () {
const reader = new MapFileReaderVS(''); const reader = new MapFileReaderVS('');
reader.preferredLoadAddress.should.equal(0x400000, 'default load address'); expect(reader.preferredLoadAddress).toEqual(0x400000);
reader.tryReadingPreferredAddress(' Preferred load address is 00400000'); reader.tryReadingPreferredAddress(' Preferred load address is 00400000');
reader.preferredLoadAddress.should.equal(0x400000); expect(reader.preferredLoadAddress).toEqual(0x400000);
reader.tryReadingPreferredAddress(' Preferred load address is 00410000'); reader.tryReadingPreferredAddress(' Preferred load address is 00410000');
reader.preferredLoadAddress.should.equal(0x410000); expect(reader.preferredLoadAddress).toEqual(0x410000);
}); });
}); });
describe('Code Segments', function () { describe('Code Segments', () => {
it('One normal Delphi-Map segment', function () { it('One normal Delphi-Map segment', () => {
const reader = new MapFileReaderDelphi(''); const reader = new MapFileReaderDelphi('');
reader.tryReadingCodeSegmentInfo(' 0001:00002838 00000080 C=CODE S=.text G=(none) M=output ACBP=A9'); reader.tryReadingCodeSegmentInfo(' 0001:00002838 00000080 C=CODE S=.text G=(none) M=output ACBP=A9');
reader.segments.length.should.equal(1); expect(reader.segments.length).toEqual(1);
let info = reader.getSegmentInfoByStartingAddress('0001', 0x2838); let info = reader.getSegmentInfoByStartingAddress('0001', 0x2838);
expect(unwrap(info).unitName).to.equal('output.pas'); expect(unwrap(info).unitName).toBe('output.pas');
info = reader.getSegmentInfoByStartingAddress(undefined, reader.getSegmentOffset('0001') + 0x2838); info = reader.getSegmentInfoByStartingAddress(undefined, reader.getSegmentOffset('0001') + 0x2838);
expect(unwrap(info).unitName).to.equal('output.pas'); expect(unwrap(info).unitName).toBe('output.pas');
info = reader.getSegmentInfoByStartingAddress('0001', 0x1234); info = reader.getSegmentInfoByStartingAddress('0001', 0x1234);
expect(info, 'Address should not be a Start for any segment').to.be.undefined; expect(info, 'Address should not be a Start for any segment').to.be.undefined;
info = reader.getSegmentInfoAddressIsIn('0001', 0x2838 + 0x10); info = reader.getSegmentInfoAddressIsIn('0001', 0x2838 + 0x10);
expect(unwrap(info).unitName).to.equal('output.pas'); expect(unwrap(info).unitName).toBe('output.pas');
info = reader.getSegmentInfoAddressIsIn(undefined, reader.getSegmentOffset('0001') + 0x2838 + 0x10); info = reader.getSegmentInfoAddressIsIn(undefined, reader.getSegmentOffset('0001') + 0x2838 + 0x10);
expect(unwrap(info).unitName).to.equal('output.pas'); expect(unwrap(info).unitName).toBe('output.pas');
info = reader.getSegmentInfoAddressIsIn('0001', reader.getSegmentOffset('0001') + 0x2838 + 0x80 + 1); info = reader.getSegmentInfoAddressIsIn('0001', reader.getSegmentOffset('0001') + 0x2838 + 0x80 + 1);
expect(info, 'Address should not be in any segment').to.be.undefined; expect(info, 'Address should not be in any segment').to.be.undefined;
info = reader.getSegmentInfoByUnitName('output.pas'); info = reader.getSegmentInfoByUnitName('output.pas');
expect(unwrap(info).unitName).to.equal('output.pas'); expect(unwrap(info).unitName).toBe('output.pas');
unwrap(info).addressInt.should.equal(reader.getSegmentOffset('0001') + 0x2838); expect(unwrap(info).addressInt).toEqual(reader.getSegmentOffset('0001') + 0x2838);
}); });
it('Not include this segment', function () { it('Not include this segment', () => {
const reader = new MapFileReaderDelphi(''); const reader = new MapFileReaderDelphi('');
reader.tryReadingCodeSegmentInfo(' 0002:000000B0 00000023 C=ICODE S=.itext G=(none) M=output ACBP=A9'); reader.tryReadingCodeSegmentInfo(' 0002:000000B0 00000023 C=ICODE S=.itext G=(none) M=output ACBP=A9');
reader.segments.length.should.equal(0); expect(reader.segments.length).toEqual(0);
}); });
it('ICode/IText segments', function () { it('ICode/IText segments', () => {
const reader = new MapFileReaderDelphi(''); const reader = new MapFileReaderDelphi('');
reader.tryReadingCodeSegmentInfo(' 0002:000000B0 00000023 C=ICODE S=.itext G=(none) M=output ACBP=A9'); reader.tryReadingCodeSegmentInfo(' 0002:000000B0 00000023 C=ICODE S=.itext G=(none) M=output ACBP=A9');
reader.isegments.length.should.equal(1); expect(reader.isegments.length).toEqual(1);
}); });
it('One normal VS-Map segment', function () { it('One normal VS-Map segment', () => {
const reader = new MapFileReaderVS(''); const reader = new MapFileReaderVS('');
reader.tryReadingCodeSegmentInfo(' 0001:00002838 00000080H .text$mn CODE'); reader.tryReadingCodeSegmentInfo(' 0001:00002838 00000080H .text$mn CODE');
reader.segments.length.should.equal(1); expect(reader.segments.length).toEqual(1);
let info = reader.getSegmentInfoByStartingAddress('0001', 0x2838); let info = reader.getSegmentInfoByStartingAddress('0001', 0x2838);
unwrap(info).addressInt.should.equal(reader.getSegmentOffset('0001') + 0x2838); expect(unwrap(info).addressInt).toEqual(reader.getSegmentOffset('0001') + 0x2838);
info = reader.getSegmentInfoByStartingAddress(undefined, 0x403838); info = reader.getSegmentInfoByStartingAddress(undefined, 0x403838);
unwrap(info).addressInt.should.equal(reader.getSegmentOffset('0001') + 0x2838); expect(unwrap(info).addressInt).toEqual(reader.getSegmentOffset('0001') + 0x2838);
info = reader.getSegmentInfoAddressIsIn(undefined, reader.getSegmentOffset('0001') + 0x2838 + 0x10); info = reader.getSegmentInfoAddressIsIn(undefined, reader.getSegmentOffset('0001') + 0x2838 + 0x10);
unwrap(info).addressInt.should.equal(reader.getSegmentOffset('0001') + 0x2838); expect(unwrap(info).addressInt).toEqual(reader.getSegmentOffset('0001') + 0x2838);
info = reader.getSegmentInfoAddressIsIn('0001', reader.getSegmentOffset('0001') + 0x2837); info = reader.getSegmentInfoAddressIsIn('0001', reader.getSegmentOffset('0001') + 0x2837);
expect(info).to.be.undefined; expect(info).to.be.undefined;
}); });
it('Repair VS-Map code segment info', function () { it('Repair VS-Map code segment info', () => {
const reader = new MapFileReaderVS(''); const reader = new MapFileReaderVS('');
reader.tryReadingCodeSegmentInfo(' 0002:00000000 00004c73H .text$mn CODE'); reader.tryReadingCodeSegmentInfo(' 0002:00000000 00004c73H .text$mn CODE');
reader.tryReadingNamedAddress( reader.tryReadingNamedAddress(
@@ -110,146 +108,148 @@ describe('Code Segments', function () {
); );
let info = reader.getSegmentInfoByStartingAddress('0002', 0); let info = reader.getSegmentInfoByStartingAddress('0002', 0);
expect(unwrap(info).unitName).to.equal('ConsoleApplication1.obj'); expect(unwrap(info).unitName).toBe('ConsoleApplication1.obj');
reader.getSegmentOffset('0002').should.equal(0x411000); expect(reader.getSegmentOffset('0002')).toEqual(0x411000);
info = reader.getSegmentInfoByStartingAddress(undefined, 0x411000); info = reader.getSegmentInfoByStartingAddress(undefined, 0x411000);
expect(unwrap(info).unitName).to.equal('ConsoleApplication1.obj'); expect(unwrap(info).unitName).toBe('ConsoleApplication1.obj');
}); });
}); });
describe('Symbol info', function () { describe('Symbol info', () => {
it('Delphi-Map symbol test', function () { it('Delphi-Map symbol test', () => {
const reader = new MapFileReaderDelphi(''); const reader = new MapFileReaderDelphi('');
reader.tryReadingNamedAddress(' 0001:00002838 Square'); reader.tryReadingNamedAddress(' 0001:00002838 Square');
reader.namedAddresses.length.should.equal(1); expect(reader.namedAddresses.length).toEqual(1);
let info = reader.getSymbolAt('0001', 0x2838); let info = reader.getSymbolAt('0001', 0x2838);
expect(info).to.not.equal(undefined, 'Symbol Square should have been returned 1'); expect(info).not.toBe(undefined);
expect(unwrap(info).displayName).to.equal('Square'); expect(unwrap(info).displayName).toBe('Square');
info = reader.getSymbolAt(undefined, reader.getSegmentOffset('0001') + 0x2838); info = reader.getSymbolAt(undefined, reader.getSegmentOffset('0001') + 0x2838);
expect(info).to.not.equal(undefined, 'Symbol Square should have been returned 2'); expect(info).not.toBe(undefined);
expect(unwrap(info).displayName).to.equal('Square'); expect(unwrap(info).displayName).toBe('Square');
}); });
it('Delphi-Map D2009 symbol test', function () { it('Delphi-Map D2009 symbol test', () => {
const reader = new MapFileReaderDelphi(''); const reader = new MapFileReaderDelphi('');
reader.tryReadingNamedAddress(' 0001:00002C4C output.MaxArray'); reader.tryReadingNamedAddress(' 0001:00002C4C output.MaxArray');
reader.namedAddresses.length.should.equal(1); expect(reader.namedAddresses.length).toEqual(1);
let info = reader.getSymbolAt('0001', 0x2c4c); let info = reader.getSymbolAt('0001', 0x2c4c);
expect(info).to.not.equal(undefined, 'Symbol MaxArray should have been returned'); expect(info).not.toBe(undefined);
expect(unwrap(info).displayName).to.equal('output.MaxArray'); expect(unwrap(info).displayName).toBe('output.MaxArray');
//todo should not be undefined //todo should not be undefined
info = reader.getSymbolAt(undefined, reader.getSegmentOffset('0001') + 0x2c4c); info = reader.getSymbolAt(undefined, reader.getSegmentOffset('0001') + 0x2c4c);
expect(info).to.not.equal(undefined, 'Symbol MaxArray should have been returned'); expect(info).not.toBe(undefined);
expect(unwrap(info).displayName).to.equal('output.MaxArray'); expect(unwrap(info).displayName).toBe('output.MaxArray');
}); });
it('VS-Map symbol test', function () { it('VS-Map symbol test', () => {
const reader = new MapFileReaderVS(''); const reader = new MapFileReaderVS('');
reader.tryReadingNamedAddress( reader.tryReadingNamedAddress(
' 0002:000006b0 ??$__vcrt_va_start_verify_argument_type@QBD@@YAXXZ 004116b0 f i ConsoleApplication1.obj', ' 0002:000006b0 ??$__vcrt_va_start_verify_argument_type@QBD@@YAXXZ 004116b0 f i ConsoleApplication1.obj',
); );
reader.namedAddresses.length.should.equal(1); expect(reader.namedAddresses.length).toEqual(1);
let info = reader.getSymbolAt('0002', 0x6b0); let info = reader.getSymbolAt('0002', 0x6b0);
expect(info).to.not.equal(undefined, 'Symbol start_verify_argument should have been returned 1'); expect(info).not.toBe(undefined);
expect(unwrap(info).displayName).to.equal('??$__vcrt_va_start_verify_argument_type@QBD@@YAXXZ'); expect(unwrap(info).displayName).toBe('??$__vcrt_va_start_verify_argument_type@QBD@@YAXXZ');
info = reader.getSymbolAt(undefined, 0x4116b0); info = reader.getSymbolAt(undefined, 0x4116b0);
expect(info).to.not.equal(undefined, 'Symbol start_verify_argument should have been returned 2'); expect(info).not.toBe(undefined);
expect(unwrap(info).displayName).to.equal('??$__vcrt_va_start_verify_argument_type@QBD@@YAXXZ'); expect(unwrap(info).displayName).toBe('??$__vcrt_va_start_verify_argument_type@QBD@@YAXXZ');
}); });
it('Delphi-Map Duplication prevention', function () { it('Delphi-Map Duplication prevention', () => {
const reader = new MapFileReaderDelphi(''); const reader = new MapFileReaderDelphi('');
reader.tryReadingNamedAddress(' 0001:00002838 Square'); reader.tryReadingNamedAddress(' 0001:00002838 Square');
reader.namedAddresses.length.should.equal(1); expect(reader.namedAddresses.length).toEqual(1);
reader.tryReadingNamedAddress(' 0001:00002838 Square'); reader.tryReadingNamedAddress(' 0001:00002838 Square');
reader.namedAddresses.length.should.equal(1); expect(reader.namedAddresses.length).toEqual(1);
}); });
}); });
describe('Delphi-Map Line number info', function () { describe('Delphi-Map Line number info', () => {
it('No line', function () { it('No line', () => {
const reader = new MapFileReaderDelphi(''); const reader = new MapFileReaderDelphi('');
reader.tryReadingLineNumbers('').should.equal(false); expect(reader.tryReadingLineNumbers('')).toEqual(false);
}); });
it('One line', function () { it('One line', () => {
const reader = new MapFileReaderDelphi(''); const reader = new MapFileReaderDelphi('');
reader.tryReadingLineNumbers(' 17 0001:000028A4').should.equal(true); expect(reader.tryReadingLineNumbers(' 17 0001:000028A4')).toEqual(true);
let lineInfo = reader.getLineInfoByAddress('0001', 0x28a4); let lineInfo = reader.getLineInfoByAddress('0001', 0x28a4);
expect(unwrap(lineInfo).lineNumber).to.equal(17); expect(unwrap(lineInfo).lineNumber).toBe(17);
lineInfo = reader.getLineInfoByAddress(undefined, reader.getSegmentOffset('0001') + 0x28a4); lineInfo = reader.getLineInfoByAddress(undefined, reader.getSegmentOffset('0001') + 0x28a4);
expect(unwrap(lineInfo).lineNumber).to.equal(17); expect(unwrap(lineInfo).lineNumber).toBe(17);
}); });
it('Multiple lines', function () { it('Multiple lines', () => {
const reader = new MapFileReaderDelphi(''); const reader = new MapFileReaderDelphi('');
reader expect(
.tryReadingLineNumbers(' 12 0001:00002838 13 0001:0000283B 14 0001:00002854 15 0001:00002858') reader.tryReadingLineNumbers(
.should.equal(true); ' 12 0001:00002838 13 0001:0000283B 14 0001:00002854 15 0001:00002858',
),
).toEqual(true);
let lineInfo = reader.getLineInfoByAddress('0001', 0x2838); let lineInfo = reader.getLineInfoByAddress('0001', 0x2838);
expect(unwrap(lineInfo).lineNumber).to.equal(12); expect(unwrap(lineInfo).lineNumber).toBe(12);
lineInfo = reader.getLineInfoByAddress('0001', 0x2858); lineInfo = reader.getLineInfoByAddress('0001', 0x2858);
expect(unwrap(lineInfo).lineNumber).to.equal(15); expect(unwrap(lineInfo).lineNumber).toBe(15);
lineInfo = reader.getLineInfoByAddress('0001', 0x2854); lineInfo = reader.getLineInfoByAddress('0001', 0x2854);
expect(unwrap(lineInfo).lineNumber).to.equal(14); expect(unwrap(lineInfo).lineNumber).toBe(14);
lineInfo = reader.getLineInfoByAddress('0001', 0x283b); lineInfo = reader.getLineInfoByAddress('0001', 0x283b);
expect(unwrap(lineInfo).lineNumber).to.equal(13); expect(unwrap(lineInfo).lineNumber).toBe(13);
}); });
}); });
describe('Delphi-Map load test', function () { describe('Delphi-Map load test', () => {
it('Minimal map', function () { it('Minimal map', () => {
const reader = new MapFileReaderDelphi('test/maps/minimal-delphi.map'); const reader = new MapFileReaderDelphi('test/maps/minimal-delphi.map');
reader.run(); reader.run();
reader.segments.length.should.equal(4); expect(reader.segments.length).toEqual(4);
reader.lineNumbers.length.should.equal(7); expect(reader.lineNumbers.length).toEqual(7);
reader.namedAddresses.length.should.equal(11); expect(reader.namedAddresses.length).toEqual(11);
let info = reader.getSegmentInfoByUnitName('output.pas'); let info = reader.getSegmentInfoByUnitName('output.pas');
unwrap(info).addressInt.should.equal(reader.getSegmentOffset('0001') + 0x2c4c); expect(unwrap(info).addressInt).toEqual(reader.getSegmentOffset('0001') + 0x2c4c);
info = reader.getICodeSegmentInfoByUnitName('output.pas'); info = reader.getICodeSegmentInfoByUnitName('output.pas');
unwrap(info).segment.should.equal('0002'); expect(unwrap(info).segment).toEqual('0002');
unwrap(info).addressWithoutOffset.should.equal(0xb0); expect(unwrap(info).addressWithoutOffset).toEqual(0xb0);
unwrap(info).addressInt.should.equal(0x4040b0); expect(unwrap(info).addressInt).toEqual(0x4040b0);
}); });
}); });
describe('VS-Map load test', function () { describe('VS-Map load test', () => {
it('Minimal map', function () { it('Minimal map', () => {
const reader = new MapFileReaderVS('test/maps/minimal-vs15.map'); const reader = new MapFileReaderVS('test/maps/minimal-vs15.map');
reader.run(); reader.run();
reader.segments.length.should.equal(1); expect(reader.segments.length).toEqual(1);
unwrap(reader.getSegmentInfoByUnitName('ConsoleApplication1.obj')).addressInt.should.equal(0x411000); expect(unwrap(reader.getSegmentInfoByUnitName('ConsoleApplication1.obj')).addressInt).toEqual(0x411000);
reader.getSegmentOffset('0001').should.equal(0x401000, 'offset 1'); expect(reader.getSegmentOffset('0001')).toEqual(0x401000);
reader.getSegmentOffset('0002').should.equal(0x411000, 'offset 2'); expect(reader.getSegmentOffset('0002')).toEqual(0x411000);
reader.getSegmentOffset('0003').should.equal(0x416000, 'offset 3'); expect(reader.getSegmentOffset('0003')).toEqual(0x416000);
reader.getSegmentOffset('0004').should.equal(0x419000, 'offset 4'); expect(reader.getSegmentOffset('0004')).toEqual(0x419000);
reader.getSegmentOffset('0005').should.equal(0x41a000, 'offset 5'); expect(reader.getSegmentOffset('0005')).toEqual(0x41a000);
reader.getSegmentOffset('0007').should.equal(0x41c000, 'offset 7'); expect(reader.getSegmentOffset('0007')).toEqual(0x41c000);
}); });
}); });
describe('VS-Map address checking', function () { describe('VS-Map address checking', () => {
it('Normal defined spaces', function () { it('Normal defined spaces', () => {
const reader = new MapFileReaderVS(''); const reader = new MapFileReaderVS('');
const mainAddresses = [ const mainAddresses = [
@@ -257,14 +257,14 @@ describe('VS-Map address checking', function () {
{startAddress: 16, startAddressHex: '00000010', endAddress: 255, endAddressHex: '000000FF'}, {startAddress: 16, startAddressHex: '00000010', endAddress: 255, endAddressHex: '000000FF'},
]; ];
reader.isWithinAddressSpace(mainAddresses, 3, 5).should.equal(true); expect(reader.isWithinAddressSpace(mainAddresses, 3, 5)).toEqual(true);
reader.isWithinAddressSpace(mainAddresses, 10, 5).should.equal(false); expect(reader.isWithinAddressSpace(mainAddresses, 10, 5)).toEqual(false);
reader.isWithinAddressSpace(mainAddresses, 11, 4).should.equal(false); expect(reader.isWithinAddressSpace(mainAddresses, 11, 4)).toEqual(false);
reader.isWithinAddressSpace(mainAddresses, 16, 10).should.equal(true); expect(reader.isWithinAddressSpace(mainAddresses, 16, 10)).toEqual(true);
reader.isWithinAddressSpace(mainAddresses, 32, 10).should.equal(true); expect(reader.isWithinAddressSpace(mainAddresses, 32, 10)).toEqual(true);
}); });
it('Overlapping regions', function () { it('Overlapping regions', () => {
const reader = new MapFileReaderVS(''); const reader = new MapFileReaderVS('');
const mainAddresses = [ const mainAddresses = [
@@ -272,9 +272,9 @@ describe('VS-Map address checking', function () {
{startAddress: 16, startAddressHex: '00000010', endAddress: 255, endAddressHex: '000000FF'}, {startAddress: 16, startAddressHex: '00000010', endAddress: 255, endAddressHex: '000000FF'},
]; ];
reader.isWithinAddressSpace(mainAddresses, 0, 5).should.equal(true); expect(reader.isWithinAddressSpace(mainAddresses, 0, 5)).toEqual(true);
reader.isWithinAddressSpace(mainAddresses, 11, 5).should.equal(true); expect(reader.isWithinAddressSpace(mainAddresses, 11, 5)).toEqual(true);
reader.isWithinAddressSpace(mainAddresses, 11, 6).should.equal(true); expect(reader.isWithinAddressSpace(mainAddresses, 11, 6)).toEqual(true);
reader.isWithinAddressSpace(mainAddresses, 11, 258).should.equal(true); expect(reader.isWithinAddressSpace(mainAddresses, 11, 258)).toEqual(true);
}); });
}); });

View File

@@ -24,11 +24,13 @@
import path from 'path'; import path from 'path';
import {beforeAll, describe, expect, it} from 'vitest';
import {unwrap} from '../lib/assert.js'; import {unwrap} from '../lib/assert.js';
import {NimCompiler} from '../lib/compilers/nim.js'; import {NimCompiler} from '../lib/compilers/nim.js';
import {LanguageKey} from '../types/languages.interfaces.js'; import {LanguageKey} from '../types/languages.interfaces.js';
import {makeCompilationEnvironment, makeFakeCompilerInfo, should} from './utils.js'; import {makeCompilationEnvironment, makeFakeCompilerInfo} from './utils.js';
const languages = { const languages = {
nim: {id: 'nim' as LanguageKey}, nim: {id: 'nim' as LanguageKey},
@@ -46,21 +48,21 @@ describe('Nim', () => {
lang: languages.nim.id, lang: languages.nim.id,
}; };
before(() => { beforeAll(() => {
ce = makeCompilationEnvironment({languages}); ce = makeCompilationEnvironment({languages});
}); });
it('Nim should not allow --run/-r parameter', () => { it('Nim should not allow --run/-r parameter', () => {
const compiler = new NimCompiler(makeFakeCompilerInfo(info), ce); const compiler = new NimCompiler(makeFakeCompilerInfo(info), ce);
compiler.filterUserOptions(['c', '--run', '--something']).should.deep.equal(['c', '--something']); expect(compiler.filterUserOptions(['c', '--run', '--something'])).toEqual(['c', '--something']);
compiler.filterUserOptions(['cpp', '-r', '--something']).should.deep.equal(['cpp', '--something']); expect(compiler.filterUserOptions(['cpp', '-r', '--something'])).toEqual(['cpp', '--something']);
}); });
it('Nim compile to Cpp if not asked otherwise', () => { it('Nim compile to Cpp if not asked otherwise', () => {
const compiler = new NimCompiler(makeFakeCompilerInfo(info), ce); const compiler = new NimCompiler(makeFakeCompilerInfo(info), ce);
compiler.filterUserOptions([]).should.deep.equal(['compile']); expect(compiler.filterUserOptions([])).toEqual(['compile']);
compiler.filterUserOptions(['badoption']).should.deep.equal(['compile', 'badoption']); expect(compiler.filterUserOptions(['badoption'])).toEqual(['compile', 'badoption']);
compiler.filterUserOptions(['js']).should.deep.equal(['js']); expect(compiler.filterUserOptions(['js'])).toEqual(['js']);
}); });
it('test getCacheFile from possible user-options', () => { it('test getCacheFile from possible user-options', () => {
@@ -74,10 +76,10 @@ describe('Nim', () => {
}; };
for (const lang of ['cpp', 'c', 'objc']) { for (const lang of ['cpp', 'c', 'objc']) {
unwrap(compiler.getCacheFile([lang], input, folder)).should.equal(expected[lang]); expect(unwrap(compiler.getCacheFile([lang], input, folder))).toEqual(expected[lang]);
} }
should.equal(compiler.getCacheFile([], input, folder), null); expect(compiler.getCacheFile([], input, folder)).toBeNull();
should.equal(compiler.getCacheFile(['js'], input, folder), null); expect(compiler.getCacheFile(['js'], input, folder)).toBeNull();
}); });
}); });

View File

@@ -25,6 +25,7 @@
import {fileURLToPath} from 'url'; import {fileURLToPath} from 'url';
import _ from 'underscore'; import _ from 'underscore';
import {beforeAll, describe, expect, it} from 'vitest';
import {AppDefaultArguments} from '../app.js'; import {AppDefaultArguments} from '../app.js';
import {BaseCompiler} from '../lib/base-compiler.js'; import {BaseCompiler} from '../lib/base-compiler.js';
@@ -35,7 +36,7 @@ import {BaseTool} from '../lib/tooling/base-tool.js';
import {CompilerInfo} from '../types/compiler.interfaces.js'; import {CompilerInfo} from '../types/compiler.interfaces.js';
import {LanguageKey} from '../types/languages.interfaces.js'; import {LanguageKey} from '../types/languages.interfaces.js';
import {makeFakeCompilerInfo, should} from './utils.js'; import {makeFakeCompilerInfo} from './utils.js';
const languages = { const languages = {
fake: { fake: {
@@ -165,7 +166,7 @@ describe('Options handler', () => {
} as unknown as ClientOptionsType; } as unknown as ClientOptionsType;
} }
before(() => { beforeAll(() => {
fakeOptionProps = properties.fakeProps(optionsProps); fakeOptionProps = properties.fakeProps(optionsProps);
compilerProps = new properties.CompilerProps(languages, fakeOptionProps); compilerProps = new properties.CompilerProps(languages, fakeOptionProps);
optionsHandler = new ClientOptionsHandler([], compilerProps, {env: ['dev']} as unknown as AppDefaultArguments); optionsHandler = new ClientOptionsHandler([], compilerProps, {env: ['dev']} as unknown as AppDefaultArguments);
@@ -185,9 +186,9 @@ describe('Options handler', () => {
it('should always return an array of paths', () => { it('should always return an array of paths', () => {
const libs = optionsHandler.parseLibraries({fake: optionsProps.libs}); const libs = optionsHandler.parseLibraries({fake: optionsProps.libs});
_.each(libs[languages.fake.id]['fakelib'].versions, version => { _.each(libs[languages.fake.id]['fakelib'].versions, version => {
Array.isArray(version.path).should.equal(true); expect(Array.isArray(version.path)).toEqual(true);
}); });
libs.should.deep.equal({ expect(libs).toEqual({
fake: { fake: {
fakelib: { fakelib: {
description: 'Its is a real, fake lib!', description: 'Its is a real, fake lib!',
@@ -364,10 +365,8 @@ describe('Options handler', () => {
}; };
optionsHandler.setCompilers(compilers); optionsHandler.setCompilers(compilers);
_.each(optionsHandler.get().compilers, compiler => { _.each(optionsHandler.get().compilers, compiler => {
should.equal( expect(compiler['$order']).toEqual(
compiler['$order'],
expectedOrder[(compiler as CompilerInfo).group][(compiler as CompilerInfo).id], expectedOrder[(compiler as CompilerInfo).group][(compiler as CompilerInfo).id],
`group: ${(compiler as CompilerInfo).group} id: ${(compiler as CompilerInfo).id}`,
); );
}); });
optionsHandler.setCompilers([]); optionsHandler.setCompilers([]);
@@ -382,10 +381,10 @@ describe('Options handler', () => {
compiler.initialiseLibraries(clientOptions); compiler.initialiseLibraries(clientOptions);
const staticlinks = compiler.getStaticLibraryLinks([{id: 'fs', version: 'std'}]); const staticlinks = compiler.getStaticLibraryLinks([{id: 'fs', version: 'std'}]);
staticlinks.should.deep.equal(['-lc++fs', '-lrt', '-lpthread']); expect(staticlinks).toEqual(['-lc++fs', '-lrt', '-lpthread']);
const sharedlinks = compiler.getSharedLibraryLinks([{id: 'fs', version: 'std'}]); const sharedlinks = compiler.getSharedLibraryLinks([{id: 'fs', version: 'std'}]);
sharedlinks.should.deep.equal([]); expect(sharedlinks).toEqual([]);
}); });
it('should sort static libraries', () => { it('should sort static libraries', () => {
const libs = optionsHandler.parseLibraries({fake: optionsProps.libs}); const libs = optionsHandler.parseLibraries({fake: optionsProps.libs});
@@ -397,13 +396,13 @@ describe('Options handler', () => {
compiler.initialiseLibraries(clientOptions); compiler.initialiseLibraries(clientOptions);
let staticlinks = compiler.getSortedStaticLibraries([{id: 'someotherlib', version: 'trunk'}]); let staticlinks = compiler.getSortedStaticLibraries([{id: 'someotherlib', version: 'trunk'}]);
staticlinks.should.deep.equal(['someotherlib', 'c++fs']); expect(staticlinks).toEqual(['someotherlib', 'c++fs']);
staticlinks = compiler.getSortedStaticLibraries([ staticlinks = compiler.getSortedStaticLibraries([
{id: 'fs', version: 'std'}, {id: 'fs', version: 'std'},
{id: 'someotherlib', version: 'trunk'}, {id: 'someotherlib', version: 'trunk'},
]); ]);
staticlinks.should.deep.equal(['someotherlib', 'c++fs', 'rt', 'pthread']); expect(staticlinks).toEqual(['someotherlib', 'c++fs', 'rt', 'pthread']);
}); });
it('library sort special case 1', () => { it('library sort special case 1', () => {
const libs = moreOptionsHandler.parseLibraries({fake: moreLibProps.libs}); const libs = moreOptionsHandler.parseLibraries({fake: moreLibProps.libs});
@@ -415,7 +414,7 @@ describe('Options handler', () => {
compiler.initialiseLibraries(clientOptions); compiler.initialiseLibraries(clientOptions);
const staticlinks = compiler.getSortedStaticLibraries([{id: 'fs', version: 'std'}]); const staticlinks = compiler.getSortedStaticLibraries([{id: 'fs', version: 'std'}]);
staticlinks.should.deep.equal(['fsextra', 'c++fs', 'rt', 'pthread']); expect(staticlinks).toEqual(['fsextra', 'c++fs', 'rt', 'pthread']);
}); });
it('library sort special case 2', () => { it('library sort special case 2', () => {
const libs = moreOptionsHandler.parseLibraries({fake: moreLibProps.libs}); const libs = moreOptionsHandler.parseLibraries({fake: moreLibProps.libs});
@@ -431,7 +430,7 @@ describe('Options handler', () => {
{id: 'fs', version: 'std'}, {id: 'fs', version: 'std'},
{id: 'someotherlib', version: 'trunk'}, {id: 'someotherlib', version: 'trunk'},
]); ]);
staticlinks.should.deep.equal(['yalib', 'someotherlib', 'fsextra', 'c++fs', 'rt', 'pthread']); expect(staticlinks).toEqual(['yalib', 'someotherlib', 'fsextra', 'c++fs', 'rt', 'pthread']);
}); });
it('library sort special case 3', () => { it('library sort special case 3', () => {
const libs = moreOptionsHandler.parseLibraries({fake: moreLibProps.libs}); const libs = moreOptionsHandler.parseLibraries({fake: moreLibProps.libs});
@@ -447,7 +446,7 @@ describe('Options handler', () => {
{id: 'fs', version: 'std'}, {id: 'fs', version: 'std'},
{id: 'someotherlib', version: 'trunk'}, {id: 'someotherlib', version: 'trunk'},
]); ]);
staticlinks.should.deep.equal(['fourthlib', 'yalib', 'someotherlib', 'fsextra', 'c++fs', 'rt', 'pthread']); expect(staticlinks).toEqual(['fourthlib', 'yalib', 'someotherlib', 'fsextra', 'c++fs', 'rt', 'pthread']);
}); });
it('filtered library list', () => { it('filtered library list', () => {
const libs = moreOptionsHandler.parseLibraries({fake: moreLibProps.libs}); const libs = moreOptionsHandler.parseLibraries({fake: moreLibProps.libs});
@@ -461,7 +460,7 @@ describe('Options handler', () => {
compiler.initialiseLibraries(clientOptions); compiler.initialiseLibraries(clientOptions);
const libNames = _.keys(compiler.getSupportedLibrariesTest()); const libNames = _.keys(compiler.getSupportedLibrariesTest());
libNames.should.deep.equal(['fs', 'someotherlib']); expect(libNames).toEqual(['fs', 'someotherlib']);
}); });
it('can detect libraries from options', () => { it('can detect libraries from options', () => {
const libs = moreOptionsHandler.parseLibraries({fake: moreLibProps.libs}); const libs = moreOptionsHandler.parseLibraries({fake: moreLibProps.libs});
@@ -476,13 +475,13 @@ describe('Options handler', () => {
libraries: [{id: 'ctre', version: 'trunk'}], libraries: [{id: 'ctre', version: 'trunk'}],
options: ['-O3', '--std=c++17', '-lhello'], options: ['-O3', '--std=c++17', '-lhello'],
}; };
compiler.tryAutodetectLibraries(obj).should.equal(true); expect(compiler.tryAutodetectLibraries(obj)).toEqual(true);
obj.libraries.should.deep.equal([ expect(obj.libraries).toEqual([
{id: 'ctre', version: 'trunk'}, {id: 'ctre', version: 'trunk'},
{id: 'autolib', version: 'autodetect'}, {id: 'autolib', version: 'autodetect'},
]); ]);
obj.options.should.deep.equal(['-O3', '--std=c++17']); expect(obj.options).toEqual(['-O3', '--std=c++17']);
}); });
it("server-side library alias support (just in case client doesn't support it)", () => { it("server-side library alias support (just in case client doesn't support it)", () => {
const libs = moreOptionsHandler.parseLibraries({fake: moreLibProps.libs}); const libs = moreOptionsHandler.parseLibraries({fake: moreLibProps.libs});
@@ -498,7 +497,7 @@ describe('Options handler', () => {
compiler.initialiseLibraries(clientOptions); compiler.initialiseLibraries(clientOptions);
const staticlinks = compiler.getSortedStaticLibraries([{id: 'someotherlib', version: 'master'}]); const staticlinks = compiler.getSortedStaticLibraries([{id: 'someotherlib', version: 'master'}]);
staticlinks.should.deep.equal(['someotherlib', 'c++fs']); expect(staticlinks).toEqual(['someotherlib', 'c++fs']);
}); });
it('should be able to parse basic tools', () => { it('should be able to parse basic tools', () => {
class TestBaseTool extends BaseTool { class TestBaseTool extends BaseTool {
@@ -514,7 +513,7 @@ describe('Options handler', () => {
delete tool.env; delete tool.env;
}); });
tools.should.deep.equal({ expect(tools).toEqual({
fake: { fake: {
faketool: { faketool: {
id: 'faketool', id: 'faketool',

View File

@@ -22,6 +22,8 @@
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE. // POSSIBILITY OF SUCH DAMAGE.
import {describe, expect, it} from 'vitest';
import {Packager} from '../lib/packager.js'; import {Packager} from '../lib/packager.js';
import {fs, newTempDir, path} from './utils.js'; import {fs, newTempDir, path} from './utils.js';
@@ -30,8 +32,10 @@ function writeTestFile(filepath) {
return fs.writeFile(filepath, '#!/bin/sh\n\necho Hello, world!\n\n'); return fs.writeFile(filepath, '#!/bin/sh\n\necho Hello, world!\n\n');
} }
describe('Packager', function () { describe('Packager', () => {
it('should be able to package 1 file', async () => { it(
'should be able to package 1 file',
async () => {
const pack = new Packager(); const pack = new Packager();
const dirPath = newTempDir(); const dirPath = newTempDir();
@@ -40,10 +44,14 @@ describe('Packager', function () {
const targzPath = path.join(dirPath, 'package.tgz'); const targzPath = path.join(dirPath, 'package.tgz');
await pack.package(dirPath, targzPath); await pack.package(dirPath, targzPath);
await fs.exists(targzPath).should.eventually.equal(true); await expect(fs.exists(targzPath)).resolves.toBe(true);
}).timeout(5000); },
{timeout: 5000},
);
it('should be able to unpack', async () => { it(
'should be able to unpack',
async () => {
const pack = new Packager(); const pack = new Packager();
const dirPath = newTempDir(); const dirPath = newTempDir();
@@ -57,6 +65,8 @@ describe('Packager', function () {
await pack2.unpack(targzPath, unpackPath); await pack2.unpack(targzPath, unpackPath);
const unpackedFilepath = path.join(unpackPath, 'hello.txt'); const unpackedFilepath = path.join(unpackPath, 'hello.txt');
await fs.exists(unpackedFilepath).should.eventually.equal(true); await expect(fs.exists(unpackedFilepath)).resolves.toBe(true);
}).timeout(5000); },
{timeout: 5000},
);
}); });

View File

@@ -1,568 +0,0 @@
// Copyright (c) 2017, Compiler Explorer Authors
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
import path from 'path';
import {PascalUtils} from '../lib/compilers/pascal-utils.js';
import {PascalWinCompiler} from '../lib/compilers/pascal-win.js';
import {FPCCompiler} from '../lib/compilers/pascal.js';
import {PascalDemangler} from '../lib/demangler/index.js';
import * as utils from '../lib/utils.js';
import {fs, makeCompilationEnvironment} from './utils.js';
const languages = {
pascal: {id: 'pascal'},
};
describe('Pascal', () => {
let compiler;
before(() => {
const ce = makeCompilationEnvironment({languages});
const info = {
exe: null,
remote: true,
lang: languages.pascal.id,
};
compiler = new FPCCompiler(info, ce);
});
it('Basic compiler setup', () => {
if (process.platform === 'win32') {
compiler.getOutputFilename('/tmp/', 'output.pas').should.equal('\\tmp\\output.s');
} else {
compiler.getOutputFilename('/tmp/', 'output.pas').should.equal('/tmp/output.s');
}
});
describe('Pascal signature composer function', function () {
const demangler = new PascalDemangler();
it('Handle 0 parameter methods', function () {
demangler.composeReadableMethodSignature('', '', 'myfunc', '').should.equal('myfunc()');
demangler.composeReadableMethodSignature('output', '', 'myfunc', '').should.equal('myfunc()');
demangler
.composeReadableMethodSignature('output', 'tmyclass', 'myfunc', '')
.should.equal('tmyclass.myfunc()');
});
it('Handle 1 parameter methods', function () {
demangler.composeReadableMethodSignature('output', '', 'myfunc', 'integer').should.equal('myfunc(integer)');
demangler
.composeReadableMethodSignature('output', 'tmyclass', 'myfunc', 'integer')
.should.equal('tmyclass.myfunc(integer)');
});
it('Handle 2 parameter methods', function () {
demangler
.composeReadableMethodSignature('output', '', 'myfunc', 'integer,string')
.should.equal('myfunc(integer,string)');
demangler
.composeReadableMethodSignature('output', 'tmyclass', 'myfunc', 'integer,string')
.should.equal('tmyclass.myfunc(integer,string)');
});
});
describe('Pascal Demangling FPC 2.6', function () {
const demangler = new PascalDemangler();
it('Should demangle OUTPUT_MAXARRAY$array_of_DOUBLE$array_of_DOUBLE', function () {
demangler
.demangle('OUTPUT_MAXARRAY$array_of_DOUBLE$array_of_DOUBLE:')
.should.equal('maxarray(array_of_double,array_of_double)');
});
it('Should demangle OUTPUT_TMYCLASS_$__MYPROC$ANSISTRING', function () {
demangler.demangle('OUTPUT_TMYCLASS_$__MYPROC$ANSISTRING:').should.equal('tmyclass.myproc(ansistring)');
});
it('Should demangle OUTPUT_TMYCLASS_$__MYFUNC$$ANSISTRING', function () {
demangler.demangle('OUTPUT_TMYCLASS_$__MYFUNC$$ANSISTRING:').should.equal('tmyclass.myfunc()');
});
it('Should demangle OUTPUT_NOPARAMFUNC$$ANSISTRING', function () {
demangler.demangle('OUTPUT_NOPARAMFUNC$$ANSISTRING:').should.equal('noparamfunc()');
});
it('Should demangle OUTPUT_NOPARAMPROC', function () {
demangler.demangle('OUTPUT_NOPARAMPROC:').should.equal('noparamproc()');
});
it('Should demangle U_OUTPUT_MYGLOBALVAR', function () {
demangler.demangle('U_OUTPUT_MYGLOBALVAR:').should.equal('myglobalvar');
});
it('Should demangle OUTPUT_INIT (custom method)', function () {
demangler.demangle('OUTPUT_INIT:').should.equal('init()');
});
it('Should demangle OUTPUT_init (builtin symbol)', function () {
demangler.demangle('OUTPUT_init:').should.equal('unit_initialization');
});
});
describe('Pascal Demangling FPC 3.2', function () {
const demangler = new PascalDemangler();
it('Should demangle OUTPUT_$$_SQUARE$LONGINT$$LONGINT', function () {
demangler.demangle('OUTPUT_$$_SQUARE$LONGINT$$LONGINT:').should.equal('square(longint)');
});
it('Should demangle OUTPUT_$$_MAXARRAY$array_of_DOUBLE$array_of_DOUBLE', function () {
demangler
.demangle('OUTPUT_$$_MAXARRAY$array_of_DOUBLE$array_of_DOUBLE:')
.should.equal('maxarray(array_of_double,array_of_double)');
});
it('Should demangle OUTPUT$_$TMYCLASS_$__$$_MYPROC$ANSISTRING', function () {
demangler
.demangle('OUTPUT$_$TMYCLASS_$__$$_MYPROC$ANSISTRING:')
.should.equal('tmyclass.myproc(ansistring)');
});
it('Should demangle OUTPUT$_$TMYCLASS_$__$$_MYFUNC$$ANSISTRING', function () {
demangler.demangle('OUTPUT$_$TMYCLASS_$__$$_MYFUNC$$ANSISTRING:').should.equal('tmyclass.myfunc()');
});
it('Should demangle OUTPUT$_$TMYCLASS_$__$$_MYFUNC$ANSISTRING$$INTEGER', function () {
demangler
.demangle('OUTPUT$_$TMYCLASS_$__$$_MYFUNC$ANSISTRING$$INTEGER:')
.should.equal('tmyclass.myfunc(ansistring)');
});
it('Should demangle OUTPUT$_$TMYCLASS_$__$$_MYFUNC$ANSISTRING$INTEGER$INTEGER$$INTEGER', function () {
demangler
.demangle('OUTPUT$_$TMYCLASS_$__$$_MYFUNC$ANSISTRING$INTEGER$INTEGER$$INTEGER:')
.should.equal('tmyclass.myfunc(ansistring,integer,integer)');
});
it('Should demangle OUTPUT_$$_NOPARAMFUNC$$ANSISTRING', function () {
demangler.demangle('OUTPUT_$$_NOPARAMFUNC$$ANSISTRING:').should.equal('noparamfunc()');
});
it('Should demangle OUTPUT_$$_NOPARAMPROC', function () {
demangler.demangle('OUTPUT_$$_NOPARAMPROC:').should.equal('noparamproc()');
});
it('Should demangle OUTPUT_$$_INIT', function () {
demangler.demangle('OUTPUT_$$_INIT:').should.equal('init()');
});
it('Should demangle U_$OUTPUT_$$_MYGLOBALVAR', function () {
demangler.demangle('U_$OUTPUT_$$_MYGLOBALVAR:').should.equal('myglobalvar');
});
});
describe('Pascal Demangling Fixed Symbols FPC 2.6', function () {
const demangler = new PascalDemangler();
it('Should demangle OUTPUT_finalize_implicit', function () {
demangler.demangle('OUTPUT_finalize_implicit:').should.equal('unit_finalization_implicit');
});
});
describe('Pascal Demangling Fixed Symbols FPC 3.2', function () {
const demangler = new PascalDemangler();
it('Should demangle OUTPUT_$$_init', function () {
demangler.demangle('OUTPUT_$$_init:').should.equal('unit_initialization');
});
it('Should demangle OUTPUT_$$_finalize', function () {
demangler.demangle('OUTPUT_$$_finalize:').should.equal('unit_finalization');
});
it('Should demangle OUTPUT_$$_init_implicit', function () {
demangler.demangle('OUTPUT_$$_init_implicit:').should.equal('unit_initialization_implicit');
});
it('Should demangle OUTPUT_$$_finalize_implicit', function () {
demangler.demangle('OUTPUT_$$_finalize_implicit:').should.equal('unit_finalization_implicit');
});
it('Should demangle OUTPUT_$$_finalize_implicit', function () {
demangler.demangle('OUTPUT_$$_finalize_implicit:').should.equal('unit_finalization_implicit');
});
});
describe('Pascal NOT Demangling certain symbols FPC 2.6', function () {
const demangler = new PascalDemangler();
it('Should NOT demangle VMT_OUTPUT_TMYCLASS', function () {
demangler.demangle('VMT_OUTPUT_TMYCLASS:').should.equal(false);
});
it('Should NOT demangle RTTI_OUTPUT_TMYCLASS', function () {
demangler.demangle('RTTI_OUTPUT_TMYCLASS:').should.equal(false);
});
it('Should NOT demangle INIT$_OUTPUT', function () {
demangler.demangle('INIT$_OUTPUT:').should.equal(false);
});
it('Should NOT demangle FINALIZE$_OUTPUT', function () {
demangler.demangle('FINALIZE$_OUTPUT:').should.equal(false);
});
it('Should NOT demangle DEBUGSTART_OUTPUT', function () {
demangler.demangle('DEBUGSTART_OUTPUT:').should.equal(false);
});
it('Should NOT demangle DBGREF_OUTPUT_THELLO', function () {
demangler.demangle('DBGREF_OUTPUT_THELLO:').should.equal(false);
});
it('Should NOT demangle non-label', function () {
demangler.demangle(' call OUTPUT$_$TMYCLASS_$__$$_MYTEST2').should.equal(false);
});
});
describe('Pascal NOT Demangling certain symbols FPC 3.2', function () {
const demangler = new PascalDemangler();
it('Should NOT demangle RTTI_$OUTPUT_$$_TMYCLASS', function () {
demangler.demangle('RTTI_$OUTPUT_$$_TMYCLASS:').should.equal(false);
});
it('Should NOT demangle .Ld1', function () {
demangler.demangle('.Ld1:').should.equal(false);
});
it('Should NOT demangle _$OUTPUT$_Ld3 (Same in FPC 2.6 and 3.2)', function () {
demangler.demangle('_$OUTPUT$_Ld3:').should.equal(false);
});
it('Should NOT demangle INIT$_$OUTPUT', function () {
demangler.demangle('INIT$_$OUTPUT:').should.equal(false);
});
it('Should NOT demangle DEBUGSTART_$OUTPUT', function () {
demangler.demangle('DEBUGSTART_$OUTPUT:').should.equal(false);
});
it('Should NOT demangle DBGREF_$OUTPUT_$$_THELLO', function () {
demangler.demangle('DBGREF_$OUTPUT_$$_THELLO:').should.equal(false);
});
});
describe('Add, order and demangle inline', function () {
const demangler = new PascalDemangler();
demangler.demangle('OUTPUT$_$TMYCLASS_$__$$_MYTEST:');
demangler.demangle('U_$OUTPUT_$$_MYGLOBALVAR:');
demangler.demangle('OUTPUT$_$TMYCLASS_$__$$_MYTEST2:');
demangler.demangle('OUTPUT$_$TMYCLASS_$__$$_MYOVERLOAD$ANSISTRING:');
demangler.demangle('OUTPUT$_$TMYCLASS_$__$$_MYOVERLOAD$INTEGER:');
demangler.demangleIfNeeded(' call OUTPUT$_$TMYCLASS_$__$$_MYTEST2').should.equal(' call tmyclass.mytest2()');
demangler.demangleIfNeeded(' movl U_$OUTPUT_$$_MYGLOBALVAR,%eax').should.equal(' movl myglobalvar,%eax');
demangler.demangleIfNeeded(' call OUTPUT$_$TMYCLASS_$__$$_MYTEST2').should.equal(' call tmyclass.mytest2()');
demangler.demangleIfNeeded(' call OUTPUT$_$TMYCLASS_$__$$_MYTEST').should.equal(' call tmyclass.mytest()');
demangler
.demangleIfNeeded(' call OUTPUT$_$TMYCLASS_$__$$_MYOVERLOAD$ANSISTRING')
.should.equal(' call tmyclass.myoverload(ansistring)');
demangler
.demangleIfNeeded(' call OUTPUT$_$TMYCLASS_$__$$_MYOVERLOAD$INTEGER')
.should.equal(' call tmyclass.myoverload(integer)');
demangler.demangleIfNeeded('.Le1').should.equal('.Le1');
demangler.demangleIfNeeded('_$SomeThing').should.equal('_$SomeThing');
});
describe('Add, order and demangle inline - using addDemangleToCache()', function () {
const demangler = new PascalDemangler();
demangler.addDemangleToCache('OUTPUT$_$TMYCLASS_$__$$_MYTEST:');
demangler.addDemangleToCache('U_$OUTPUT_$$_MYGLOBALVAR:');
demangler.addDemangleToCache('OUTPUT$_$TMYCLASS_$__$$_MYTEST2:');
demangler.addDemangleToCache('OUTPUT$_$TMYCLASS_$__$$_MYOVERLOAD$ANSISTRING:');
demangler.addDemangleToCache('OUTPUT$_$TMYCLASS_$__$$_MYOVERLOAD$INTEGER:');
demangler.demangleIfNeeded(' call OUTPUT$_$TMYCLASS_$__$$_MYTEST2').should.equal(' call tmyclass.mytest2()');
demangler.demangleIfNeeded(' movl U_$OUTPUT_$$_MYGLOBALVAR,%eax').should.equal(' movl myglobalvar,%eax');
demangler.demangleIfNeeded(' call OUTPUT$_$TMYCLASS_$__$$_MYTEST2').should.equal(' call tmyclass.mytest2()');
demangler.demangleIfNeeded(' call OUTPUT$_$TMYCLASS_$__$$_MYTEST').should.equal(' call tmyclass.mytest()');
demangler
.demangleIfNeeded(' call OUTPUT$_$TMYCLASS_$__$$_MYOVERLOAD$ANSISTRING')
.should.equal(' call tmyclass.myoverload(ansistring)');
demangler
.demangleIfNeeded(' call OUTPUT$_$TMYCLASS_$__$$_MYOVERLOAD$INTEGER')
.should.equal(' call tmyclass.myoverload(integer)');
demangler.demangleIfNeeded('.Le1').should.equal('.Le1');
});
describe('Pascal Ignored Symbols', function () {
const demangler = new PascalDemangler();
it('Should ignore certain labels', function () {
demangler.shouldIgnoreSymbol('.Le1').should.equal(true);
demangler.shouldIgnoreSymbol('_$SomeThing').should.equal(true);
});
it('Should be able to differentiate between System and User functions', function () {
demangler.shouldIgnoreSymbol('RTTI_OUTPUT_MyProperty').should.equal(true);
demangler.shouldIgnoreSymbol('Rtti_Output_UserFunction').should.equal(false);
});
});
describe('Pascal ASM line number injection', function () {
before(() => {
compiler.demanglerClass = PascalDemangler;
compiler.demangler = new PascalDemangler(null, compiler);
});
it('Should have line numbering', function () {
return new Promise(function (resolve) {
fs.readFile('test/pascal/asm-example.s', function (err, buffer) {
const asmLines = utils.splitLines(buffer.toString());
compiler.preProcessLines(asmLines);
resolve(
Promise.all([
asmLines.should.include('# [output.pas]'),
asmLines.should.include(' .file 1 "output.pas"'),
asmLines.should.include('# [13] Square := num * num + 14;'),
asmLines.should.include(' .loc 1 13 0'),
asmLines.should.include('.Le0:'),
asmLines.should.include(' .cfi_endproc'),
]),
);
});
});
});
});
// describe('Pascal objdump filtering', function () {
// it('Should filter out most of the runtime', function () {
// return new Promise(function (resolve) {
// fs.readFile('test/pascal/objdump-example.s', function (err, buffer) {
// const output = FPCCompiler.preProcessBinaryAsm(buffer.toString());
// resolve(Promise.all([
// utils.splitLines(output).length.should.be.below(500),
// output.should.not.include('fpc_zeromem():'),
// output.should.include('SQUARE():'),
// ]));
// });
// });
// });
// });
describe('Pascal parseOutput', () => {
it('should return parsed output', () => {
const result = {
stdout: 'Hello, world!',
stderr: '',
};
compiler.parseOutput(result, '/tmp/path/output.pas', '/tmp/path').should.deep.equal({
inputFilename: 'output.pas',
stdout: [
{
text: 'Hello, world!',
},
],
stderr: [],
});
});
});
describe('Pascal filetype detection', () => {
const pasUtils = new PascalUtils();
const progSource = fs.readFileSync('test/pascal/prog.dpr').toString('utf8');
const unitSource = fs.readFileSync('test/pascal/example.pas').toString('utf8');
it('Should detect simple program', () => {
pasUtils.isProgram(progSource).should.equal(true);
pasUtils.isProgram(unitSource).should.equal(false);
});
it('Should detect simple unit', () => {
pasUtils.isUnit(progSource).should.equal(false);
pasUtils.isUnit(unitSource).should.equal(true);
});
});
describe('Multifile writing behaviour', function () {
let compiler;
before(() => {
const ce = makeCompilationEnvironment({languages});
const info = {
exe: null,
remote: true,
lang: languages.pascal.id,
};
compiler = new FPCCompiler(info, ce);
});
it('Original behaviour (old unitname)', async function () {
const dirPath = await compiler.newTempDir();
const filters = {};
const files = [];
const source = fs.readFileSync('examples/pascal/default.pas').toString('utf8');
const writeSummary = await compiler.writeAllFiles(dirPath, source, files, filters);
return Promise.all([
writeSummary.inputFilename.should.equal(path.join(dirPath, 'output.pas')),
utils.fileExists(path.join(dirPath, 'output.pas')).should.eventually.equal(true),
utils.fileExists(path.join(dirPath, 'prog.dpr')).should.eventually.equal(false), // note: will be written somewhere else
]);
});
it('Original behaviour (just a unit file)', async function () {
const dirPath = await compiler.newTempDir();
const filters = {};
const files = [];
const source = fs.readFileSync('test/pascal/example.pas').toString('utf8');
const writeSummary = await compiler.writeAllFiles(dirPath, source, files, filters);
return Promise.all([
writeSummary.inputFilename.should.equal(path.join(dirPath, 'example.pas')),
utils.fileExists(path.join(dirPath, 'example.pas')).should.eventually.equal(true),
utils.fileExists(path.join(dirPath, 'prog.dpr')).should.eventually.equal(false), // note: will be written somewhere else
]);
});
it('Writing program instead of a unit', async function () {
const dirPath = await compiler.newTempDir();
const filters = {};
const files = [];
const source = fs.readFileSync('test/pascal/prog.dpr').toString('utf8');
const writeSummary = await compiler.writeAllFiles(dirPath, source, files, filters);
return Promise.all([
writeSummary.inputFilename.should.equal(path.join(dirPath, 'prog.dpr')),
utils.fileExists(path.join(dirPath, 'example.pas')).should.eventually.equal(false),
utils.fileExists(path.join(dirPath, 'prog.dpr')).should.eventually.equal(true),
]);
});
it('Writing program with a unit', async function () {
const dirPath = await compiler.newTempDir();
const filters = {};
const files = [
{
filename: 'example.pas',
contents: '{ hello\n world }',
},
];
const source = fs.readFileSync('test/pascal/prog.dpr').toString('utf8');
const writeSummary = await compiler.writeAllFiles(dirPath, source, files, filters);
return Promise.all([
writeSummary.inputFilename.should.equal(path.join(dirPath, 'prog.dpr')),
utils.fileExists(path.join(dirPath, 'example.pas')).should.eventually.equal(true),
utils.fileExists(path.join(dirPath, 'prog.dpr')).should.eventually.equal(true),
]);
});
});
describe('Multifile writing behaviour Pascal-WIN', function () {
let compiler;
before(() => {
const ce = makeCompilationEnvironment({languages});
const info = {
exe: null,
remote: true,
lang: languages.pascal.id,
};
compiler = new PascalWinCompiler(info, ce);
});
it('Original behaviour (old unitname)', async function () {
const dirPath = await compiler.newTempDir();
const filters = {};
const files = [];
const source = fs.readFileSync('examples/pascal/default.pas').toString('utf8');
const writeSummary = await compiler.writeAllFiles(dirPath, source, files, filters);
return Promise.all([
writeSummary.inputFilename.should.equal(path.join(dirPath, 'output.pas')),
utils.fileExists(path.join(dirPath, 'output.pas')).should.eventually.equal(true),
utils.fileExists(path.join(dirPath, 'prog.dpr')).should.eventually.equal(false), // note: will be written somewhere else
]);
});
it('Original behaviour (just a unit file)', async function () {
const dirPath = await compiler.newTempDir();
const filters = {};
const files = [];
const source = fs.readFileSync('test/pascal/example.pas').toString('utf8');
const writeSummary = await compiler.writeAllFiles(dirPath, source, files, filters);
return Promise.all([
writeSummary.inputFilename.should.equal(path.join(dirPath, 'example.pas')),
utils.fileExists(path.join(dirPath, 'example.pas')).should.eventually.equal(true),
utils.fileExists(path.join(dirPath, 'prog.dpr')).should.eventually.equal(false), // note: will be written somewhere else
]);
});
it('Writing program instead of a unit', async function () {
const dirPath = await compiler.newTempDir();
const filters = {};
const files = [];
const source = fs.readFileSync('test/pascal/prog.dpr').toString('utf8');
const writeSummary = await compiler.writeAllFiles(dirPath, source, files, filters);
return Promise.all([
writeSummary.inputFilename.should.equal(path.join(dirPath, 'prog.dpr')),
utils.fileExists(path.join(dirPath, 'example.pas')).should.eventually.equal(false),
utils.fileExists(path.join(dirPath, 'prog.dpr')).should.eventually.equal(true),
]);
});
it('Writing program with a unit', async function () {
const dirPath = await compiler.newTempDir();
const filters = {};
const files = [
{
filename: 'example.pas',
contents: '{ hello\n world }',
},
];
const source = fs.readFileSync('test/pascal/prog.dpr').toString('utf8');
const writeSummary = await compiler.writeAllFiles(dirPath, source, files, filters);
return Promise.all([
writeSummary.inputFilename.should.equal(path.join(dirPath, 'prog.dpr')),
utils.fileExists(path.join(dirPath, 'example.pas')).should.eventually.equal(true),
utils.fileExists(path.join(dirPath, 'prog.dpr')).should.eventually.equal(true),
]);
});
});
});

567
test/pascal-tests.ts Normal file
View File

@@ -0,0 +1,567 @@
// Copyright (c) 2017, Compiler Explorer Authors
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
import path from 'path';
import {beforeAll, describe, expect, it} from 'vitest';
import {PascalUtils} from '../lib/compilers/pascal-utils.js';
import {PascalWinCompiler} from '../lib/compilers/pascal-win.js';
import {FPCCompiler} from '../lib/compilers/pascal.js';
import {PascalDemangler} from '../lib/demangler/index.js';
import * as utils from '../lib/utils.js';
import {fs, makeCompilationEnvironment} from './utils.js';
const languages = {
pascal: {id: 'pascal'},
};
describe('Pascal', () => {
let compiler;
beforeAll(() => {
const ce = makeCompilationEnvironment({languages});
const info = {
exe: null,
remote: true,
lang: languages.pascal.id,
};
compiler = new FPCCompiler(info as any, ce);
});
it('Basic compiler setup', () => {
if (process.platform === 'win32') {
expect(compiler.getOutputFilename('/tmp/', 'output.pas')).toEqual('\\tmp\\output.s');
} else {
expect(compiler.getOutputFilename('/tmp/', 'output.pas')).toEqual('/tmp/output.s');
}
});
describe('Pascal signature composer function', () => {
const demangler = new PascalDemangler('demangler-exe', compiler);
it('Handle 0 parameter methods', () => {
expect(demangler.composeReadableMethodSignature('', '', 'myfunc', '')).toEqual('myfunc()');
expect(demangler.composeReadableMethodSignature('output', '', 'myfunc', '')).toEqual('myfunc()');
expect(demangler.composeReadableMethodSignature('output', 'tmyclass', 'myfunc', '')).toEqual(
'tmyclass.myfunc()',
);
});
it('Handle 1 parameter methods', () => {
expect(demangler.composeReadableMethodSignature('output', '', 'myfunc', 'integer')).toEqual(
'myfunc(integer)',
);
expect(demangler.composeReadableMethodSignature('output', 'tmyclass', 'myfunc', 'integer')).toEqual(
'tmyclass.myfunc(integer)',
);
});
it('Handle 2 parameter methods', () => {
expect(demangler.composeReadableMethodSignature('output', '', 'myfunc', 'integer,string')).toEqual(
'myfunc(integer,string)',
);
expect(demangler.composeReadableMethodSignature('output', 'tmyclass', 'myfunc', 'integer,string')).toEqual(
'tmyclass.myfunc(integer,string)',
);
});
});
describe('Pascal Demangling FPC 2.6', () => {
const demangler = new PascalDemangler('demangler-exe', compiler);
it('Should demangle OUTPUT_MAXARRAY$array_of_DOUBLE$array_of_DOUBLE', () => {
expect(demangler.demangle('OUTPUT_MAXARRAY$array_of_DOUBLE$array_of_DOUBLE:')).toEqual(
'maxarray(array_of_double,array_of_double)',
);
});
it('Should demangle OUTPUT_TMYCLASS_$__MYPROC$ANSISTRING', () => {
expect(demangler.demangle('OUTPUT_TMYCLASS_$__MYPROC$ANSISTRING:')).toEqual('tmyclass.myproc(ansistring)');
});
it('Should demangle OUTPUT_TMYCLASS_$__MYFUNC$$ANSISTRING', () => {
expect(demangler.demangle('OUTPUT_TMYCLASS_$__MYFUNC$$ANSISTRING:')).toEqual('tmyclass.myfunc()');
});
it('Should demangle OUTPUT_NOPARAMFUNC$$ANSISTRING', () => {
expect(demangler.demangle('OUTPUT_NOPARAMFUNC$$ANSISTRING:')).toEqual('noparamfunc()');
});
it('Should demangle OUTPUT_NOPARAMPROC', () => {
expect(demangler.demangle('OUTPUT_NOPARAMPROC:')).toEqual('noparamproc()');
});
it('Should demangle U_OUTPUT_MYGLOBALVAR', () => {
expect(demangler.demangle('U_OUTPUT_MYGLOBALVAR:')).toEqual('myglobalvar');
});
it('Should demangle OUTPUT_INIT (custom method)', () => {
expect(demangler.demangle('OUTPUT_INIT:')).toEqual('init()');
});
it('Should demangle OUTPUT_init (builtin symbol)', () => {
expect(demangler.demangle('OUTPUT_init:')).toEqual('unit_initialization');
});
});
describe('Pascal Demangling FPC 3.2', () => {
const demangler = new PascalDemangler('demangler-exe', compiler);
it('Should demangle OUTPUT_$$_SQUARE$LONGINT$$LONGINT', () => {
expect(demangler.demangle('OUTPUT_$$_SQUARE$LONGINT$$LONGINT:')).toEqual('square(longint)');
});
it('Should demangle OUTPUT_$$_MAXARRAY$array_of_DOUBLE$array_of_DOUBLE', () => {
expect(demangler.demangle('OUTPUT_$$_MAXARRAY$array_of_DOUBLE$array_of_DOUBLE:')).toEqual(
'maxarray(array_of_double,array_of_double)',
);
});
it('Should demangle OUTPUT$_$TMYCLASS_$__$$_MYPROC$ANSISTRING', () => {
expect(demangler.demangle('OUTPUT$_$TMYCLASS_$__$$_MYPROC$ANSISTRING:')).toEqual(
'tmyclass.myproc(ansistring)',
);
});
it('Should demangle OUTPUT$_$TMYCLASS_$__$$_MYFUNC$$ANSISTRING', () => {
expect(demangler.demangle('OUTPUT$_$TMYCLASS_$__$$_MYFUNC$$ANSISTRING:')).toEqual('tmyclass.myfunc()');
});
it('Should demangle OUTPUT$_$TMYCLASS_$__$$_MYFUNC$ANSISTRING$$INTEGER', () => {
expect(demangler.demangle('OUTPUT$_$TMYCLASS_$__$$_MYFUNC$ANSISTRING$$INTEGER:')).toEqual(
'tmyclass.myfunc(ansistring)',
);
});
it('Should demangle OUTPUT$_$TMYCLASS_$__$$_MYFUNC$ANSISTRING$INTEGER$INTEGER$$INTEGER', () => {
expect(demangler.demangle('OUTPUT$_$TMYCLASS_$__$$_MYFUNC$ANSISTRING$INTEGER$INTEGER$$INTEGER:')).toEqual(
'tmyclass.myfunc(ansistring,integer,integer)',
);
});
it('Should demangle OUTPUT_$$_NOPARAMFUNC$$ANSISTRING', () => {
expect(demangler.demangle('OUTPUT_$$_NOPARAMFUNC$$ANSISTRING:')).toEqual('noparamfunc()');
});
it('Should demangle OUTPUT_$$_NOPARAMPROC', () => {
expect(demangler.demangle('OUTPUT_$$_NOPARAMPROC:')).toEqual('noparamproc()');
});
it('Should demangle OUTPUT_$$_INIT', () => {
expect(demangler.demangle('OUTPUT_$$_INIT:')).toEqual('init()');
});
it('Should demangle U_$OUTPUT_$$_MYGLOBALVAR', () => {
expect(demangler.demangle('U_$OUTPUT_$$_MYGLOBALVAR:')).toEqual('myglobalvar');
});
});
describe('Pascal Demangling Fixed Symbols FPC 2.6', () => {
const demangler = new PascalDemangler('demangler-exe', compiler);
it('Should demangle OUTPUT_finalize_implicit', () => {
expect(demangler.demangle('OUTPUT_finalize_implicit:')).toEqual('unit_finalization_implicit');
});
});
describe('Pascal Demangling Fixed Symbols FPC 3.2', () => {
const demangler = new PascalDemangler('demangler-exe', compiler);
it('Should demangle OUTPUT_$$_init', () => {
expect(demangler.demangle('OUTPUT_$$_init:')).toEqual('unit_initialization');
});
it('Should demangle OUTPUT_$$_finalize', () => {
expect(demangler.demangle('OUTPUT_$$_finalize:')).toEqual('unit_finalization');
});
it('Should demangle OUTPUT_$$_init_implicit', () => {
expect(demangler.demangle('OUTPUT_$$_init_implicit:')).toEqual('unit_initialization_implicit');
});
it('Should demangle OUTPUT_$$_finalize_implicit', () => {
expect(demangler.demangle('OUTPUT_$$_finalize_implicit:')).toEqual('unit_finalization_implicit');
});
it('Should demangle OUTPUT_$$_finalize_implicit', () => {
expect(demangler.demangle('OUTPUT_$$_finalize_implicit:')).toEqual('unit_finalization_implicit');
});
});
describe('Pascal NOT Demangling certain symbols FPC 2.6', () => {
const demangler = new PascalDemangler('demangler-exe', compiler);
it('Should NOT demangle VMT_OUTPUT_TMYCLASS', () => {
expect(demangler.demangle('VMT_OUTPUT_TMYCLASS:')).toEqual(false);
});
it('Should NOT demangle RTTI_OUTPUT_TMYCLASS', () => {
expect(demangler.demangle('RTTI_OUTPUT_TMYCLASS:')).toEqual(false);
});
it('Should NOT demangle INIT$_OUTPUT', () => {
expect(demangler.demangle('INIT$_OUTPUT:')).toEqual(false);
});
it('Should NOT demangle FINALIZE$_OUTPUT', () => {
expect(demangler.demangle('FINALIZE$_OUTPUT:')).toEqual(false);
});
it('Should NOT demangle DEBUGSTART_OUTPUT', () => {
expect(demangler.demangle('DEBUGSTART_OUTPUT:')).toEqual(false);
});
it('Should NOT demangle DBGREF_OUTPUT_THELLO', () => {
expect(demangler.demangle('DBGREF_OUTPUT_THELLO:')).toEqual(false);
});
it('Should NOT demangle non-label', () => {
expect(demangler.demangle(' call OUTPUT$_$TMYCLASS_$__$$_MYTEST2')).toEqual(false);
});
});
describe('Pascal NOT Demangling certain symbols FPC 3.2', () => {
const demangler = new PascalDemangler('demangler-exe', compiler);
it('Should NOT demangle RTTI_$OUTPUT_$$_TMYCLASS', () => {
expect(demangler.demangle('RTTI_$OUTPUT_$$_TMYCLASS:')).toEqual(false);
});
it('Should NOT demangle .Ld1', () => {
expect(demangler.demangle('.Ld1:')).toEqual(false);
});
it('Should NOT demangle _$OUTPUT$_Ld3 (Same in FPC 2.6 and 3.2)', () => {
expect(demangler.demangle('_$OUTPUT$_Ld3:')).toEqual(false);
});
it('Should NOT demangle INIT$_$OUTPUT', () => {
expect(demangler.demangle('INIT$_$OUTPUT:')).toEqual(false);
});
it('Should NOT demangle DEBUGSTART_$OUTPUT', () => {
expect(demangler.demangle('DEBUGSTART_$OUTPUT:')).toEqual(false);
});
it('Should NOT demangle DBGREF_$OUTPUT_$$_THELLO', () => {
expect(demangler.demangle('DBGREF_$OUTPUT_$$_THELLO:')).toEqual(false);
});
});
describe('Add, order and demangle inline', () => {
const demangler = new PascalDemangler('demangler-exe', compiler);
it('should work', () => {
demangler.demangle('OUTPUT$_$TMYCLASS_$__$$_MYTEST:');
demangler.demangle('U_$OUTPUT_$$_MYGLOBALVAR:');
demangler.demangle('OUTPUT$_$TMYCLASS_$__$$_MYTEST2:');
demangler.demangle('OUTPUT$_$TMYCLASS_$__$$_MYOVERLOAD$ANSISTRING:');
demangler.demangle('OUTPUT$_$TMYCLASS_$__$$_MYOVERLOAD$INTEGER:');
expect(demangler.demangleIfNeeded(' call OUTPUT$_$TMYCLASS_$__$$_MYTEST2')).toEqual(
' call tmyclass.mytest2()',
);
expect(demangler.demangleIfNeeded(' movl U_$OUTPUT_$$_MYGLOBALVAR,%eax')).toEqual(
' movl myglobalvar,%eax',
);
expect(demangler.demangleIfNeeded(' call OUTPUT$_$TMYCLASS_$__$$_MYTEST2')).toEqual(
' call tmyclass.mytest2()',
);
expect(demangler.demangleIfNeeded(' call OUTPUT$_$TMYCLASS_$__$$_MYTEST')).toEqual(
' call tmyclass.mytest()',
);
expect(demangler.demangleIfNeeded(' call OUTPUT$_$TMYCLASS_$__$$_MYOVERLOAD$ANSISTRING')).toEqual(
' call tmyclass.myoverload(ansistring)',
);
expect(demangler.demangleIfNeeded(' call OUTPUT$_$TMYCLASS_$__$$_MYOVERLOAD$INTEGER')).toEqual(
' call tmyclass.myoverload(integer)',
);
expect(demangler.demangleIfNeeded('.Le1')).toEqual('.Le1');
expect(demangler.demangleIfNeeded('_$SomeThing')).toEqual('_$SomeThing');
});
});
describe('Add, order and demangle inline - using addDemangleToCache()', () => {
const demangler = new PascalDemangler('demangler-exe', compiler);
it('should work', () => {
demangler.addDemangleToCache('OUTPUT$_$TMYCLASS_$__$$_MYTEST:');
demangler.addDemangleToCache('U_$OUTPUT_$$_MYGLOBALVAR:');
demangler.addDemangleToCache('OUTPUT$_$TMYCLASS_$__$$_MYTEST2:');
demangler.addDemangleToCache('OUTPUT$_$TMYCLASS_$__$$_MYOVERLOAD$ANSISTRING:');
demangler.addDemangleToCache('OUTPUT$_$TMYCLASS_$__$$_MYOVERLOAD$INTEGER:');
expect(demangler.demangleIfNeeded(' call OUTPUT$_$TMYCLASS_$__$$_MYTEST2')).toEqual(
' call tmyclass.mytest2()',
);
expect(demangler.demangleIfNeeded(' movl U_$OUTPUT_$$_MYGLOBALVAR,%eax')).toEqual(
' movl myglobalvar,%eax',
);
expect(demangler.demangleIfNeeded(' call OUTPUT$_$TMYCLASS_$__$$_MYTEST2')).toEqual(
' call tmyclass.mytest2()',
);
expect(demangler.demangleIfNeeded(' call OUTPUT$_$TMYCLASS_$__$$_MYTEST')).toEqual(
' call tmyclass.mytest()',
);
expect(demangler.demangleIfNeeded(' call OUTPUT$_$TMYCLASS_$__$$_MYOVERLOAD$ANSISTRING')).toEqual(
' call tmyclass.myoverload(ansistring)',
);
expect(demangler.demangleIfNeeded(' call OUTPUT$_$TMYCLASS_$__$$_MYOVERLOAD$INTEGER')).toEqual(
' call tmyclass.myoverload(integer)',
);
expect(demangler.demangleIfNeeded('.Le1')).toEqual('.Le1');
});
});
describe('Pascal Ignored Symbols', () => {
const demangler = new PascalDemangler('demangler-exe', compiler);
it('Should ignore certain labels', () => {
expect(demangler.shouldIgnoreSymbol('.Le1')).toEqual(true);
expect(demangler.shouldIgnoreSymbol('_$SomeThing')).toEqual(true);
});
it('Should be able to differentiate between System and User functions', () => {
expect(demangler.shouldIgnoreSymbol('RTTI_OUTPUT_MyProperty')).toEqual(true);
expect(demangler.shouldIgnoreSymbol('Rtti_Output_UserFunction')).toEqual(false);
});
});
describe('Pascal ASM line number injection', () => {
beforeAll(() => {
compiler.demanglerClass = PascalDemangler;
compiler.demangler = new PascalDemangler('demangler-exe', compiler);
});
it('Should have line numbering', async () => {
const asmLines = utils.splitLines((await fs.readFile('test/pascal/asm-example.s')).toString());
compiler.preProcessLines(asmLines);
expect(asmLines).toContain('# [output.pas]');
expect(asmLines).toContain(' .file 1 "output.pas"');
expect(asmLines).toContain('# [13] Square := num * num + 14;');
expect(asmLines).toContain(' .loc 1 13 0');
expect(asmLines).toContain('.Le0:');
expect(asmLines).toContain(' .cfi_endproc');
});
});
// describe('Pascal objdump filtering', function () {
// it('Should filter out most of the runtime', function () {
// return new Promise(function (resolve) {
// fs.readFile('test/pascal/objdump-example.s', function (err, buffer) {
// const output = FPCCompiler.preProcessBinaryAsm(buffer.toString());
// resolve(Promise.all([
// utils.splitLines(output).length.should.be.below(500),
// output.should.not.include('fpc_zeromem():'),
// output.should.include('SQUARE():'),
// ]));
// });
// });
// });
// });
describe('Pascal parseOutput', () => {
it('should return parsed output', () => {
const result = {
stdout: 'Hello, world!',
stderr: '',
};
expect(compiler.parseOutput(result, '/tmp/path/output.pas', '/tmp/path')).toEqual({
inputFilename: 'output.pas',
stdout: [
{
text: 'Hello, world!',
},
],
stderr: [],
});
});
});
describe('Pascal filetype detection', () => {
const pasUtils = new PascalUtils();
const progSource = fs.readFileSync('test/pascal/prog.dpr').toString('utf8');
const unitSource = fs.readFileSync('test/pascal/example.pas').toString('utf8');
it('Should detect simple program', () => {
expect(pasUtils.isProgram(progSource)).toEqual(true);
expect(pasUtils.isProgram(unitSource)).toEqual(false);
});
it('Should detect simple unit', () => {
expect(pasUtils.isUnit(progSource)).toEqual(false);
expect(pasUtils.isUnit(unitSource)).toEqual(true);
});
});
describe('Multifile writing behaviour', () => {
let compiler;
beforeAll(() => {
const ce = makeCompilationEnvironment({languages});
const info = {
exe: null,
remote: true,
lang: languages.pascal.id,
};
compiler = new FPCCompiler(info as unknown as any, ce);
});
it('Original behaviour (old unitname)', async () => {
const dirPath = await compiler.newTempDir();
const filters = {};
const files = [];
const source = fs.readFileSync('examples/pascal/default.pas').toString('utf8');
const writeSummary = await compiler.writeAllFiles(dirPath, source, files, filters);
expect(writeSummary.inputFilename).toEqual(path.join(dirPath, 'output.pas'));
await expect(utils.fileExists(path.join(dirPath, 'output.pas'))).resolves.toBe(true);
await expect(utils.fileExists(path.join(dirPath, 'prog.dpr'))).resolves.toBe(false); // note: will be written somewhere else
});
it('Original behaviour (just a unit file)', async () => {
const dirPath = await compiler.newTempDir();
const filters = {};
const files = [];
const source = fs.readFileSync('test/pascal/example.pas').toString('utf8');
const writeSummary = await compiler.writeAllFiles(dirPath, source, files, filters);
expect(writeSummary.inputFilename).toEqual(path.join(dirPath, 'example.pas'));
await expect(utils.fileExists(path.join(dirPath, 'example.pas'))).resolves.toBe(true);
await expect(utils.fileExists(path.join(dirPath, 'prog.dpr'))).resolves.toBe(false); // note: will be written somewhere else
});
it('Writing program instead of a unit', async () => {
const dirPath = await compiler.newTempDir();
const filters = {};
const files = [];
const source = fs.readFileSync('test/pascal/prog.dpr').toString('utf8');
const writeSummary = await compiler.writeAllFiles(dirPath, source, files, filters);
expect(writeSummary.inputFilename).toEqual(path.join(dirPath, 'prog.dpr'));
await expect(utils.fileExists(path.join(dirPath, 'example.pas'))).resolves.toBe(false);
await expect(utils.fileExists(path.join(dirPath, 'prog.dpr'))).resolves.toBe(true);
});
it('Writing program with a unit', async () => {
const dirPath = await compiler.newTempDir();
const filters = {};
const files = [
{
filename: 'example.pas',
contents: '{ hello\n world }',
},
];
const source = fs.readFileSync('test/pascal/prog.dpr').toString('utf8');
const writeSummary = await compiler.writeAllFiles(dirPath, source, files, filters);
expect(writeSummary.inputFilename).toEqual(path.join(dirPath, 'prog.dpr'));
await expect(utils.fileExists(path.join(dirPath, 'example.pas'))).resolves.toBe(true);
await expect(utils.fileExists(path.join(dirPath, 'prog.dpr'))).resolves.toBe(true);
});
});
describe('Multifile writing behaviour Pascal-WIN', () => {
let compiler;
beforeAll(() => {
const ce = makeCompilationEnvironment({languages});
const info = {
exe: null,
remote: true,
lang: languages.pascal.id,
};
compiler = new PascalWinCompiler(info as any, ce);
});
it('Original behaviour (old unitname)', async () => {
const dirPath = await compiler.newTempDir();
const filters = {};
const files = [];
const source = fs.readFileSync('examples/pascal/default.pas').toString('utf8');
const writeSummary = await compiler.writeAllFiles(dirPath, source, files, filters);
expect(writeSummary.inputFilename).toEqual(path.join(dirPath, 'output.pas'));
await expect(utils.fileExists(path.join(dirPath, 'output.pas'))).resolves.toBe(true);
await expect(utils.fileExists(path.join(dirPath, 'prog.dpr'))).resolves.toBe(false); // note: will be written somewhere else
});
it('Original behaviour (just a unit file)', async () => {
const dirPath = await compiler.newTempDir();
const filters = {};
const files = [];
const source = fs.readFileSync('test/pascal/example.pas').toString('utf8');
const writeSummary = await compiler.writeAllFiles(dirPath, source, files, filters);
expect(writeSummary.inputFilename).toEqual(path.join(dirPath, 'example.pas'));
await expect(utils.fileExists(path.join(dirPath, 'example.pas'))).resolves.toBe(true);
await expect(utils.fileExists(path.join(dirPath, 'prog.dpr'))).resolves.toBe(false); // note: will be written somewhere else
});
it('Writing program instead of a unit', async () => {
const dirPath = await compiler.newTempDir();
const filters = {};
const files = [];
const source = fs.readFileSync('test/pascal/prog.dpr').toString('utf8');
const writeSummary = await compiler.writeAllFiles(dirPath, source, files, filters);
expect(writeSummary.inputFilename).toEqual(path.join(dirPath, 'prog.dpr'));
await expect(utils.fileExists(path.join(dirPath, 'example.pas'))).resolves.toBe(false);
await expect(utils.fileExists(path.join(dirPath, 'prog.dpr'))).resolves.toBe(true);
});
it('Writing program with a unit', async () => {
const dirPath = await compiler.newTempDir();
const filters = {};
const files = [
{
filename: 'example.pas',
contents: '{ hello\n world }',
},
];
const source = fs.readFileSync('test/pascal/prog.dpr').toString('utf8');
const writeSummary = await compiler.writeAllFiles(dirPath, source, files, filters);
expect(writeSummary.inputFilename).toEqual(path.join(dirPath, 'prog.dpr'));
await expect(utils.fileExists(path.join(dirPath, 'example.pas'))).resolves.toBe(true);
await expect(utils.fileExists(path.join(dirPath, 'prog.dpr'))).resolves.toBe(true);
});
});
});

View File

@@ -22,12 +22,15 @@
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE. // POSSIBILITY OF SUCH DAMAGE.
import {describe, expect, it} from 'vitest';
import {MapFileReader} from '../lib/mapfiles/map-file.js';
import {PELabelReconstructor} from '../lib/pe32-support.js'; import {PELabelReconstructor} from '../lib/pe32-support.js';
describe('Basic reconstructions', function () { describe('Basic reconstructions', () => {
it('No lines', function () { it('No lines', () => {
const lines = []; const lines = [];
const reconstructor = new PELabelReconstructor(lines, false, false); const reconstructor = new PELabelReconstructor(lines, false, new MapFileReader('unused'), false);
reconstructor.asmLines.length.should.equal(0); expect(reconstructor.asmLines.length).toEqual(0);
}); });
}); });

View File

@@ -1,116 +0,0 @@
// Copyright (c) 2022, Compiler Explorer Authors
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
export const cases = [
{
input: `# 0 "/app/example.cpp"
# 1 "/app//"
# 0 "<built-in>"
# 0 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 0 "<command-line>" 2
# 1 "/app/example.cpp"
# 1 "/usr/include/assert.h" 1 3 4
# 35 "/usr/include/assert.h" 3 4
# 1 "/usr/include/features.h" 1 3 4
# 461 "/usr/include/features.h" 3 4
# 1 "/usr/include/x86_64-linux-gnu/sys/cdefs.h" 1 3 4
# 452 "/usr/include/x86_64-linux-gnu/sys/cdefs.h" 3 4
# 1 "/usr/include/x86_64-linux-gnu/bits/wordsize.h" 1 3 4
# 453 "/usr/include/x86_64-linux-gnu/sys/cdefs.h" 2 3 4
# 1 "/usr/include/x86_64-linux-gnu/bits/long-double.h" 1 3 4
# 454 "/usr/include/x86_64-linux-gnu/sys/cdefs.h" 2 3 4
# 462 "/usr/include/features.h" 2 3 4
# 485 "/usr/include/features.h" 3 4
# 1 "/usr/include/x86_64-linux-gnu/gnu/stubs.h" 1 3 4
# 10 "/usr/include/x86_64-linux-gnu/gnu/stubs.h" 3 4
# 1 "/usr/include/x86_64-linux-gnu/gnu/stubs-64.h" 1 3 4
# 11 "/usr/include/x86_64-linux-gnu/gnu/stubs.h" 2 3 4
# 486 "/usr/include/features.h" 2 3 4
# 36 "/usr/include/assert.h" 2 3 4
# 66 "/usr/include/assert.h" 3 4
# 66 "/usr/include/assert.h" 3 4
extern "C" {
extern void __assert_fail (const char *__assertion, const char *__file,
unsigned int __line, const char *__function)
throw () __attribute__ ((__noreturn__));
extern void __assert_perror_fail (int __errnum, const char *__file,
unsigned int __line, const char *__function)
throw () __attribute__ ((__noreturn__));
extern void __assert (const char *__assertion, const char *__file, int __line)
throw () __attribute__ ((__noreturn__));
}
# 2 "/app/example.cpp" 2
#line 11 "C:/WinSdk/Include/10.0.18362.0/ucrt\\assert.h"
foo
# 2 "<source>" 2
bar
# 66 "/usr/include/assert.h" 3 4
baz
# 2 "<stdin>"
biz
# 66 "/usr/include/assert.h" 3 4
# 3 "/app/example.cpp"
int main() {
# 4 "/app/example.cpp"
#pragma foo bar
# 4 "/app/example.cpp"
# 5 "/app/example.cpp" 3 4
(static_cast <bool> (
# 5 "/app/example.cpp"
false
# 5 "/app/example.cpp" 3 4
) ? void (0) : __assert_fail (
# 5 "/app/example.cpp"
"false"
# 5 "/app/example.cpp" 3 4
, "/app/example.cpp", 5, __extension__ __PRETTY_FUNCTION__))
# 5 "/app/example.cpp"
;
}`,
output: `bar
biz
int main() {
#pragma foo bar
(static_cast <bool> (
false
) ? void (0) : __assert_fail (
"false"
, "/app/example.cpp", 5, __extension__ __PRETTY_FUNCTION__))
;
}`,
},
];

View File

@@ -22,13 +22,13 @@
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE. // POSSIBILITY OF SUCH DAMAGE.
import {describe, expect, it} from 'vitest';
import {BaseCompiler} from '../lib/base-compiler.js'; import {BaseCompiler} from '../lib/base-compiler.js';
import {CompilationEnvironment} from '../lib/compilation-env.js'; import {CompilationEnvironment} from '../lib/compilation-env.js';
import * as properties from '../lib/properties.js'; import * as properties from '../lib/properties.js';
import {CompilerInfo} from '../types/compiler.interfaces.js'; import {CompilerInfo} from '../types/compiler.interfaces.js';
import * as filterTests from './pp-output-cases/filter-tests.js';
//const makeFakeCompilerInfo = (id: string, lang: string, group: string, semver: string, isSemver: boolean) => { //const makeFakeCompilerInfo = (id: string, lang: string, group: string, semver: string, isSemver: boolean) => {
const makeFakeCompilerInfo = (id, lang, group, semver, isSemver): Partial<CompilerInfo> => { const makeFakeCompilerInfo = (id, lang, group, semver, isSemver): Partial<CompilerInfo> => {
return { return {
@@ -51,9 +51,102 @@ describe('Preprocessor Output Handling', () => {
compilerProps: (() => {}) as unknown as any, compilerProps: (() => {}) as unknown as any,
}; };
const compiler = new BaseCompiler(compilerInfo as CompilerInfo, env as CompilationEnvironment); const compiler = new BaseCompiler(compilerInfo as CompilerInfo, env as CompilationEnvironment);
for (const testCase of filterTests.cases) { for (const testCase of cases) {
const output = compiler.filterPP(testCase.input)[1]; const output = compiler.filterPP(testCase.input)[1];
output.trim().should.eql(testCase.output.trim()); expect(output.trim()).toEqual(testCase.output.trim());
} }
}); });
}); });
const cases = [
{
input: `# 0 "/app/example.cpp"
# 1 "/app//"
# 0 "<built-in>"
# 0 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 0 "<command-line>" 2
# 1 "/app/example.cpp"
# 1 "/usr/include/assert.h" 1 3 4
# 35 "/usr/include/assert.h" 3 4
# 1 "/usr/include/features.h" 1 3 4
# 461 "/usr/include/features.h" 3 4
# 1 "/usr/include/x86_64-linux-gnu/sys/cdefs.h" 1 3 4
# 452 "/usr/include/x86_64-linux-gnu/sys/cdefs.h" 3 4
# 1 "/usr/include/x86_64-linux-gnu/bits/wordsize.h" 1 3 4
# 453 "/usr/include/x86_64-linux-gnu/sys/cdefs.h" 2 3 4
# 1 "/usr/include/x86_64-linux-gnu/bits/long-double.h" 1 3 4
# 454 "/usr/include/x86_64-linux-gnu/sys/cdefs.h" 2 3 4
# 462 "/usr/include/features.h" 2 3 4
# 485 "/usr/include/features.h" 3 4
# 1 "/usr/include/x86_64-linux-gnu/gnu/stubs.h" 1 3 4
# 10 "/usr/include/x86_64-linux-gnu/gnu/stubs.h" 3 4
# 1 "/usr/include/x86_64-linux-gnu/gnu/stubs-64.h" 1 3 4
# 11 "/usr/include/x86_64-linux-gnu/gnu/stubs.h" 2 3 4
# 486 "/usr/include/features.h" 2 3 4
# 36 "/usr/include/assert.h" 2 3 4
# 66 "/usr/include/assert.h" 3 4
# 66 "/usr/include/assert.h" 3 4
extern "C" {
extern void __assert_fail (const char *__assertion, const char *__file,
unsigned int __line, const char *__function)
throw () __attribute__ ((__noreturn__));
extern void __assert_perror_fail (int __errnum, const char *__file,
unsigned int __line, const char *__function)
throw () __attribute__ ((__noreturn__));
extern void __assert (const char *__assertion, const char *__file, int __line)
throw () __attribute__ ((__noreturn__));
}
# 2 "/app/example.cpp" 2
#line 11 "C:/WinSdk/Include/10.0.18362.0/ucrt\\assert.h"
foo
# 2 "<source>" 2
bar
# 66 "/usr/include/assert.h" 3 4
baz
# 2 "<stdin>"
biz
# 66 "/usr/include/assert.h" 3 4
# 3 "/app/example.cpp"
int main() {
# 4 "/app/example.cpp"
#pragma foo bar
# 4 "/app/example.cpp"
# 5 "/app/example.cpp" 3 4
(static_cast <bool> (
# 5 "/app/example.cpp"
false
# 5 "/app/example.cpp" 3 4
) ? void (0) : __assert_fail (
# 5 "/app/example.cpp"
"false"
# 5 "/app/example.cpp" 3 4
, "/app/example.cpp", 5, __extension__ __PRETTY_FUNCTION__))
# 5 "/app/example.cpp"
;
}`,
output: `bar
biz
int main() {
#pragma foo bar
(static_cast <bool> (
false
) ? void (0) : __assert_fail (
"false"
, "/app/example.cpp", 5, __extension__ __PRETTY_FUNCTION__))
;
}`,
},
];

View File

@@ -22,6 +22,8 @@
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE. // POSSIBILITY OF SUCH DAMAGE.
import {beforeAll, describe, expect, it} from 'vitest';
import {PPCICompiler} from '../lib/compilers/ppci.js'; import {PPCICompiler} from '../lib/compilers/ppci.js';
import {LanguageKey} from '../types/languages.interfaces.js'; import {LanguageKey} from '../types/languages.interfaces.js';
@@ -31,7 +33,7 @@ const languages = {
c: {id: 'c' as LanguageKey}, c: {id: 'c' as LanguageKey},
}; };
describe('PPCI', function () { describe('PPCI', () => {
let ce; let ce;
const info = { const info = {
exe: '/dev/null', exe: '/dev/null',
@@ -43,28 +45,30 @@ describe('PPCI', function () {
lang: languages.c.id, lang: languages.c.id,
}; };
before(() => { beforeAll(() => {
ce = makeCompilationEnvironment({languages}); ce = makeCompilationEnvironment({languages});
}); });
it('Should be ok with most arguments', () => { it('Should be ok with most arguments', () => {
const compiler = new PPCICompiler(makeFakeCompilerInfo(info), ce); const compiler = new PPCICompiler(makeFakeCompilerInfo(info), ce);
compiler expect(compiler.filterUserOptions(['hello', '-help', '--something'])).toEqual([
.filterUserOptions(['hello', '-help', '--something']) 'hello',
.should.deep.equal(['hello', '-help', '--something']); '-help',
'--something',
]);
}); });
it('Should be ok with path argument', () => { it('Should be ok with path argument', () => {
const compiler = new PPCICompiler(makeFakeCompilerInfo(info), ce); const compiler = new PPCICompiler(makeFakeCompilerInfo(info), ce);
compiler expect(compiler.filterUserOptions(['hello', '--stuff', '/proc/cpuinfo'])).toEqual([
.filterUserOptions(['hello', '--stuff', '/proc/cpuinfo']) 'hello',
.should.deep.equal(['hello', '--stuff', '/proc/cpuinfo']); '--stuff',
'/proc/cpuinfo',
]);
}); });
it('Should be Not ok with report arguments', () => { it('Should be Not ok with report arguments', () => {
const compiler = new PPCICompiler(makeFakeCompilerInfo(info), ce); const compiler = new PPCICompiler(makeFakeCompilerInfo(info), ce);
compiler expect(compiler.filterUserOptions(['hello', '--report', '--text-report', '--html-report'])).toEqual(['hello']);
.filterUserOptions(['hello', '--report', '--text-report', '--html-report'])
.should.deep.equal(['hello']);
}); });
}); });

View File

@@ -22,10 +22,9 @@
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE. // POSSIBILITY OF SUCH DAMAGE.
import {unwrap} from '../lib/assert.js'; import {afterAll, beforeAll, describe, expect, it} from 'vitest';
import * as properties from '../lib/properties.js';
import {should} from './utils.js'; import * as properties from '../lib/properties.js';
const languages = { const languages = {
a: {id: 'a'}, a: {id: 'a'},
@@ -34,7 +33,7 @@ const languages = {
describe('Properties', () => { describe('Properties', () => {
let casesProps, overridingProps, compilerProps; let casesProps, overridingProps, compilerProps;
before(() => { beforeAll(() => {
properties.initialize('test/example-config/', ['test', 'overridden-base', 'overridden-tip']); properties.initialize('test/example-config/', ['test', 'overridden-base', 'overridden-tip']);
casesProps = properties.propsFor('cases'); casesProps = properties.propsFor('cases');
overridingProps = properties.propsFor('overwrite'); overridingProps = properties.propsFor('overwrite');
@@ -46,121 +45,121 @@ describe('Properties', () => {
); );
}); });
after(() => { afterAll(() => {
properties.reset(); properties.reset();
}); });
it('Has working propsFor', () => { it('Has working propsFor', () => {
should.equal(properties.get('cases', 'exampleProperty'), casesProps('exampleProperty')); expect(properties.get('cases', 'exampleProperty')).toEqual(casesProps('exampleProperty'));
}); });
it('Does not find non existent properties when no default is set', () => { it('Does not find non existent properties when no default is set', () => {
should.equal(casesProps('nonexistentProp'), undefined); expect(casesProps('nonexistentProp')).toEqual(undefined);
}); });
it('Falls back to default if value not found and default is set', () => { it('Falls back to default if value not found and default is set', () => {
// Randomly generated number... // Randomly generated number...
casesProps('nonexistentProp', 4).should.be.equal(4); expect(casesProps('nonexistentProp', 4)).toEqual(4);
should.equal(casesProps('nonexistentProp', 4), 4); expect(casesProps('nonexistentProp', 4)).toEqual(4);
}); });
it('Handles empty properties as empty strings', () => { it('Handles empty properties as empty strings', () => {
should.equal(casesProps('emptyProperty'), ''); expect(casesProps('emptyProperty')).toEqual('');
}); });
it('Handles bad numbers properties as strings', () => { it('Handles bad numbers properties as strings', () => {
should.equal(casesProps('001string'), '001'); expect(casesProps('001string')).toEqual('001');
}); });
it('Handles bad numbers properties as strings', () => { it('Handles bad numbers properties as strings', () => {
should.equal(casesProps('0985string'), '0985'); expect(casesProps('0985string')).toEqual('0985');
}); });
it('Ignores commented out properties', () => { it('Ignores commented out properties', () => {
should.equal(casesProps('commentedProperty'), undefined); expect(casesProps('commentedProperty')).toBeUndefined();
}); });
it('Ignores bad lines', () => { it('Ignores bad lines', () => {
should.equal(casesProps('badLineIfYouSeeThisWithAnErrorItsOk'), undefined); expect(casesProps('badLineIfYouSeeThisWithAnErrorItsOk')).toBeUndefined();
}); });
it('Understands positive integers', () => { it('Understands positive integers', () => {
should.equal(casesProps('numericPropertyPositive'), 42); expect(casesProps('numericPropertyPositive')).toEqual(42);
}); });
it('Understands zero as integer', () => { it('Understands zero as integer', () => {
should.equal(casesProps('numericPropertyZero'), 0); expect(casesProps('numericPropertyZero')).toEqual(0);
}); });
it('Understands negative integers', () => { it('Understands negative integers', () => {
should.equal(casesProps('numericPropertyNegative'), -11); expect(casesProps('numericPropertyNegative')).toEqual(-11);
}); });
it('Understands positive floats', () => { it('Understands positive floats', () => {
should.equal(casesProps('floatPropertyPositive'), 3.14); expect(casesProps('floatPropertyPositive')).toEqual(3.14);
}); });
it('Understands negative floats', () => { it('Understands negative floats', () => {
should.equal(casesProps('floatPropertyNegative'), -9000); expect(casesProps('floatPropertyNegative')).toEqual(-9000);
}); });
it('Does not understand comma decimal as float', () => { it('Does not understand comma decimal as float', () => {
should.equal(casesProps('commaAsDecimalProperty'), '3,14'); expect(casesProps('commaAsDecimalProperty')).toEqual('3,14');
}); });
it('Does not understand DASH-SPACE-NUMBER as a negative number', () => { it('Does not understand DASH-SPACE-NUMBER as a negative number', () => {
should.equal(casesProps('stringPropertyNumberLike'), '- 97'); expect(casesProps('stringPropertyNumberLike')).toEqual('- 97');
}); });
it('Understands yes as true boolean', () => { it('Understands yes as true boolean', () => {
should.equal(casesProps('truePropertyYes'), true); expect(casesProps('truePropertyYes')).toBe(true);
}); });
it('Understands true as true boolean', () => { it('Understands true as true boolean', () => {
should.equal(casesProps('truePropertyTrue'), true); expect(casesProps('truePropertyTrue')).toBe(true);
}); });
it('Does not understand Yes as boolean', () => { it('Does not understand Yes as boolean', () => {
should.equal(casesProps('stringPropertyYes'), 'Yes'); expect(casesProps('stringPropertyYes')).toEqual('Yes');
}); });
it('Does not understand True as boolean', () => { it('Does not understand True as boolean', () => {
should.equal(casesProps('stringPropertyTrue'), 'True'); expect(casesProps('stringPropertyTrue')).toEqual('True');
}); });
it('Understands no as false boolean', () => { it('Understands no as false boolean', () => {
should.equal(casesProps('falsePropertyNo'), false); expect(casesProps('falsePropertyNo')).toBe(false);
}); });
it('Understands false as false boolean', () => { it('Understands false as false boolean', () => {
should.equal(casesProps('falsePropertyFalse'), false); expect(casesProps('falsePropertyFalse')).toBe(false);
}); });
it('Does not understand No as boolean', () => { it('Does not understand No as boolean', () => {
should.equal(casesProps('stringPropertyNo'), 'No'); expect(casesProps('stringPropertyNo')).toEqual('No');
}); });
it('Does not understand False as boolean', () => { it('Does not understand False as boolean', () => {
should.equal(casesProps('stringPropertyFalse'), 'False'); expect(casesProps('stringPropertyFalse')).toEqual('False');
}); });
it('Should find non overridden properties', () => { it('Should find non overridden properties', () => {
should.equal(overridingProps('nonOverriddenProperty'), '.... . .-.. .-.. ---'); expect(overridingProps('nonOverriddenProperty')).toEqual('.... . .-.. .-.. ---');
}); });
it('Should handle overridden properties', () => { it('Should handle overridden properties', () => {
should.equal(overridingProps('overrodeProperty'), 'ACTUALLY USED'); expect(overridingProps('overrodeProperty')).toEqual('ACTUALLY USED');
}); });
it('Should fall back from overridden', () => { it('Should fall back from overridden', () => {
should.equal(overridingProps('localProperty'), 11235813); expect(overridingProps('localProperty')).toEqual(11235813);
}); });
it('should have an identity function if none provided', () => { it('should have an identity function if none provided', () => {
should.equal(compilerProps.get('a', 'foo', '0'), '1'); expect(compilerProps.get('a', 'foo', '0')).toEqual('1');
compilerProps.get(languages, 'foo', '0').should.deep.equal({a: '1'}); expect(compilerProps.get(languages, 'foo', '0')).toEqual({a: '1'});
}); });
it('should return an object of languages if the languages arg is an object itself', () => { it('should return an object of languages if the languages arg is an object itself', () => {
compilerProps.get(languages, 'foo', '0').should.deep.equal({a: '1'}); expect(compilerProps.get(languages, 'foo', '0')).toEqual({a: '1'});
}); });
it('should return a direct result if the language is an ID', () => { it('should return a direct result if the language is an ID', () => {
compilerProps.propsByLangId[languages.a.id] = properties.fakeProps({foo: 'b'}); compilerProps.propsByLangId[languages.a.id] = properties.fakeProps({foo: 'b'});
should.equal(compilerProps.get('a', 'foo', '0'), 'b'); expect(compilerProps.get('a', 'foo', '0')).toEqual('b');
compilerProps.propsByLangId[languages.a.id] = undefined; compilerProps.propsByLangId[languages.a.id] = undefined;
}); });
it('should have backwards compatibility compilerProps behaviour', () => { it('should have backwards compatibility compilerProps behaviour', () => {
should.equal(compilerProps.get('', 'foo', '0'), '1'); expect(compilerProps.get('', 'foo', '0')).toEqual('1');
}); });
it('should report the default value if an unknown language is used', () => { it('should report the default value if an unknown language is used', () => {
should.equal(compilerProps.get('b', 'foo', '0'), '0'); expect(compilerProps.get('b', 'foo', '0')).toEqual('0');
}); });
it('should not check ceProps for falsey values', () => { it('should not check ceProps for falsey values', () => {
// Set bar to be falsey in the language specific setting. // Set bar to be falsey in the language specific setting.
compilerProps.propsByLangId[languages.a.id] = properties.fakeProps({bar: false}); compilerProps.propsByLangId[languages.a.id] = properties.fakeProps({bar: false});
// Now query it with a default of true. We should see false... // Now query it with a default of true. We should see false...
should.equal(compilerProps.get('a', 'bar', true), false); expect(compilerProps.get('a', 'bar', true)).toBe(false);
compilerProps.get(languages, 'bar', true).should.deep.equal({a: false}); expect(compilerProps.get(languages, 'bar', true)).toEqual({a: false});
compilerProps.propsByLangId[languages.a.id] = undefined; compilerProps.propsByLangId[languages.a.id] = undefined;
}); });
it('should not parse version properties as numbers', () => { it('should not parse version properties as numbers', () => {
should.equal(casesProps('libs.example.versions.010.version'), '0.10'); expect(casesProps('libs.example.versions.010.version')).toEqual('0.10');
}); });
it('should not parse semver properties as numbers', () => { it('should not parse semver properties as numbers', () => {
should.equal(casesProps('compiler.example110.semver'), '1.10'); expect(casesProps('compiler.example110.semver')).toEqual('1.10');
}); });
}); });
@@ -173,8 +172,8 @@ describe('Properties blob parsing', () => {
'mybool=false\n', 'mybool=false\n',
'<test props>', '<test props>',
); );
unwrap(props.hello).should.equal('test'); expect(props.hello).toEqual('test');
unwrap(props.etc).should.equal(123); expect(props.etc).toEqual(123);
unwrap(props.mybool).should.equal(false); expect(props.mybool).toBe(false);
}); });
}); });

View File

@@ -22,6 +22,8 @@
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE. // POSSIBILITY OF SUCH DAMAGE.
import {beforeAll, describe, expect, it} from 'vitest';
import {RacketPassDumpParser} from '../lib/parsers/racket-pass-dump-parser.js'; import {RacketPassDumpParser} from '../lib/parsers/racket-pass-dump-parser.js';
import * as properties from '../lib/properties.js'; import * as properties from '../lib/properties.js';
@@ -33,16 +35,16 @@ function deepCopy(obj) {
return JSON.parse(JSON.stringify(obj)); return JSON.parse(JSON.stringify(obj));
} }
describe('racket-pass-dump-parser', function () { describe('racket-pass-dump-parser', () => {
let racketPassDumpParser; let racketPassDumpParser;
before(() => { beforeAll(() => {
const fakeProps = new properties.CompilerProps(languages, properties.fakeProps({})); const fakeProps = new properties.CompilerProps(languages, properties.fakeProps({}));
const compilerProps = (fakeProps.get as any).bind(fakeProps, 'racket'); const compilerProps = (fakeProps.get as any).bind(fakeProps, 'racket');
racketPassDumpParser = new RacketPassDumpParser(compilerProps); racketPassDumpParser = new RacketPassDumpParser(compilerProps);
}); });
it('should recognize step', function () { it('should recognize step', () => {
// prettier-ignore // prettier-ignore
const output = [ const output = [
{ text: ';; compile-linklet: phase: 0' }, { text: ';; compile-linklet: phase: 0' },
@@ -59,7 +61,7 @@ describe('racket-pass-dump-parser', function () {
const brokenDown = racketPassDumpParser.breakdownOutputIntoPassDumps(deepCopy(output), {}); const brokenDown = racketPassDumpParser.breakdownOutputIntoPassDumps(deepCopy(output), {});
brokenDown.should.deep.equal([ expect(brokenDown).toEqual([
{ {
group: 'module: example, linklet: module, phase: 0', group: 'module: example, linklet: module, phase: 0',
header: 'linklet', header: 'linklet',
@@ -69,7 +71,7 @@ describe('racket-pass-dump-parser', function () {
]); ]);
}); });
it('should recognize pass', function () { it('should recognize pass', () => {
// prettier-ignore // prettier-ignore
const output = [ const output = [
{ text: ';; compile-linklet: module: (phases configure-runtime)' }, { text: ';; compile-linklet: module: (phases configure-runtime)' },
@@ -97,7 +99,7 @@ describe('racket-pass-dump-parser', function () {
const brokenDown = racketPassDumpParser.breakdownOutputIntoPassDumps(deepCopy(output), {}); const brokenDown = racketPassDumpParser.breakdownOutputIntoPassDumps(deepCopy(output), {});
brokenDown.should.deep.equal([ expect(brokenDown).toEqual([
{ {
group: 'module: (phases configure-runtime), linklet: decl', group: 'module: (phases configure-runtime), linklet: decl',
header: 'cpnanopass', header: 'cpnanopass',

View File

@@ -1,4 +1,5 @@
import {unwrap} from '../lib/assert.js'; import {describe, expect, it} from 'vitest';
import * as rison from '../static/rison.js'; import * as rison from '../static/rison.js';
// Copied from https://github.com/Nanonid/rison/blob/master/python/rison/tests.py // Copied from https://github.com/Nanonid/rison/blob/master/python/rison/tests.py
@@ -42,15 +43,15 @@ describe('Rison test cases', () => {
for (const [r, obj] of Object.entries(py_testcases)) { for (const [r, obj] of Object.entries(py_testcases)) {
it(`Should decode "${r}"`, () => { it(`Should decode "${r}"`, () => {
// hack to get around "TypeError: Cannot read properties of null (reading 'should')" // hack to get around "TypeError: Cannot read properties of null (reading 'should')"
({x: rison.decode(r)}).should.deep.equal({x: obj}); expect(rison.decode(r)).toEqual(obj);
}); });
it(`Should encode ${JSON.stringify(obj)}`, () => { it(`Should encode ${JSON.stringify(obj)}`, () => {
unwrap(rison.encode(obj)).should.deep.equal(r); expect(rison.encode(obj)).toEqual(r);
}); });
} }
for (const [obj, r] of Object.entries(encode_testcases)) { for (const [obj, r] of Object.entries(encode_testcases)) {
it(`Should encode ${JSON.stringify(obj)}`, () => { it(`Should encode ${JSON.stringify(obj)}`, () => {
unwrap(rison.encode(obj)).should.deep.equal(r); expect(rison.encode(obj)).toEqual(r);
}); });
} }
}); });

View File

@@ -24,58 +24,61 @@
import fs from 'fs'; import fs from 'fs';
import {unwrap} from '../lib/assert.js'; import {describe, expect, it} from 'vitest';
import {Sponsor} from '../lib/sponsors.interfaces.js'; import {Sponsor} from '../lib/sponsors.interfaces.js';
import {loadSponsorsFromString, makeIconSets, parse} from '../lib/sponsors.js'; import {loadSponsorsFromString, makeIconSets, parse} from '../lib/sponsors.js';
import {resolvePathFromTestRoot, should} from './utils.js'; import {resolvePathFromTestRoot} from './utils.js';
describe('Sponsors', () => { describe('Sponsors', () => {
it('should expand names to objects', () => { it('should expand names to objects', () => {
parse('moo').name.should.eq('moo'); expect(parse('moo').name).toEqual('moo');
}); });
it('should handle just names', () => { it('should handle just names', () => {
parse({name: 'moo'}).name.should.eq('moo'); expect(parse({name: 'moo'}).name).toEqual('moo');
}); });
it('should default empty params', () => { it('should default empty params', () => {
const obj = parse('moo'); const obj = parse('moo');
should.equal(obj.description, undefined); expect(obj.description).toBeUndefined();
should.equal(obj.url, undefined); expect(obj.url).toBeUndefined();
obj.onclick.should.eq(''); expect(obj.onclick).toEqual('');
should.equal(obj.img, undefined); expect(obj.img).toBeUndefined();
should.equal(obj.icon, undefined); expect(obj.icon).toBeUndefined();
should.equal(obj.icon_dark, undefined); expect(obj.icon_dark).toBeUndefined();
obj.topIconShowEvery.should.eq(0); expect(obj.topIconShowEvery).toEqual(0);
obj.displayType.should.eq('Above'); expect(obj.displayType).toEqual('Above');
should.equal(obj.statsId, undefined); expect(obj.statsId).toBeUndefined();
obj.style.should.deep.equal({}); expect(obj.style).toEqual({});
}); });
it('should make descriptions always one-sized arrays', () => { it('should make descriptions always one-sized arrays', () => {
unwrap(parse({name: 'moo', description: 'desc'}).description).should.deep.eq(['desc']); expect(parse({name: 'moo', description: 'desc'}).description).toEqual(['desc']);
}); });
it('should pass through descriptions', () => { it('should pass through descriptions', () => {
unwrap(parse({name: 'moo', description: ['desc1', 'desc2']}).description).should.deep.eq(['desc1', 'desc2']); expect(parse({name: 'moo', description: ['desc1', 'desc2']}).description).toEqual(['desc1', 'desc2']);
}); });
it('should pass through icons', () => { it('should pass through icons', () => {
unwrap(parse({name: 'bob', icon: 'icon'}).icon).should.eq('icon'); expect(parse({name: 'bob', icon: 'icon'}).icon).toEqual('icon');
}); });
it('should pick icons over images', () => { it('should pick icons over images', () => {
unwrap(parse({name: 'bob', img: 'img', icon: 'icon'}).icon).should.eq('icon'); expect(parse({name: 'bob', img: 'img', icon: 'icon'}).icon).toEqual('icon');
}); });
it('should pick icons if not img', () => { it('should pick icons if not img', () => {
unwrap(parse({name: 'bob', img: 'img'}).icon).should.eq('img'); expect(parse({name: 'bob', img: 'img'}).icon).toEqual('img');
}); });
it('should pick dark icons if specified', () => { it('should pick dark icons if specified', () => {
unwrap(parse({name: 'bob', icon: 'icon', icon_dark: 'icon_dark'}).icon_dark).should.eq('icon_dark'); expect(parse({name: 'bob', icon: 'icon', icon_dark: 'icon_dark'}).icon_dark).toEqual('icon_dark');
}); });
it('should handle styles', () => { it('should handle styles', () => {
parse({name: 'bob', bgColour: 'red'}).style.should.deep.eq({'background-color': 'red'}); expect(parse({name: 'bob', bgColour: 'red'}).style).toEqual({'background-color': 'red'});
}); });
it('should handle clicks', () => { it('should handle clicks', () => {
expect(
parse({ parse({
name: 'bob', name: 'bob',
url: 'https://some.host/click', url: 'https://some.host/click',
}).onclick.should.eq('window.onSponsorClick("https://some.host/click");'); }).onclick,
).toEqual('window.onSponsorClick("https://some.host/click");');
}); });
it('should load a simple example', () => { it('should load a simple example', () => {
@@ -96,11 +99,11 @@ levels:
- People - People
- Yay - Yay
`); `);
sample.should.not.be.null; expect(sample).not.toBeNull();
const levels = sample.getLevels(); const levels = sample.getLevels();
levels.length.should.eq(2); expect(levels.length).toEqual(2);
levels[0].name.should.eq('Patreon Legends'); expect(levels[0].name).toEqual('Patreon Legends');
levels[1].name.should.eq('Patreons'); expect(levels[1].name).toEqual('Patreons');
}); });
it('should sort sponsors by name', () => { it('should sort sponsors by name', () => {
@@ -115,7 +118,7 @@ levels:
- A - A
- B - B
`).getLevels()[0].sponsors; `).getLevels()[0].sponsors;
peeps.map(sponsor => sponsor.name).should.deep.equals(['A', 'B', 'C', 'D']); expect(peeps.map(sponsor => sponsor.name)).toEqual(['A', 'B', 'C', 'D']);
}); });
it('should sort sponsors by priority then name', () => { it('should sort sponsors by priority then name', () => {
const peeps = loadSponsorsFromString(` const peeps = loadSponsorsFromString(`
@@ -131,11 +134,11 @@ levels:
- name: B - name: B
priority: 50 priority: 50
`).getLevels()[0].sponsors; `).getLevels()[0].sponsors;
peeps expect(
.map(sponsor => { peeps.map(sponsor => {
return {name: sponsor.name, priority: sponsor.priority}; return {name: sponsor.name, priority: sponsor.priority};
}) }),
.should.deep.equals([ ).toEqual([
{name: 'D', priority: 100}, {name: 'D', priority: 100},
{name: 'B', priority: 50}, {name: 'B', priority: 50},
{name: 'C', priority: 50}, {name: 'C', priority: 50},
@@ -166,7 +169,7 @@ levels:
- name: five - name: five
topIconShowEvery: 3 topIconShowEvery: 3
`).getAllTopIcons(); `).getAllTopIcons();
icons.map(s => s.name).should.deep.equals(['one', 'four']); expect(icons.map(s => s.name)).toEqual(['one', 'four']);
}); });
it('should pick icons appropriately when all required every 3', () => { it('should pick icons appropriately when all required every 3', () => {
@@ -174,27 +177,27 @@ levels:
const sponsor2 = parse({name: 'Sponsor2', topIconShowEvery: 3, icon: '2'}); const sponsor2 = parse({name: 'Sponsor2', topIconShowEvery: 3, icon: '2'});
const sponsor3 = parse({name: 'Sponsor3', topIconShowEvery: 3, icon: '3'}); const sponsor3 = parse({name: 'Sponsor3', topIconShowEvery: 3, icon: '3'});
const icons = [sponsor1, sponsor2, sponsor3]; const icons = [sponsor1, sponsor2, sponsor3];
makeIconSets(icons, 10).should.deep.eq([icons]); expect(makeIconSets(icons, 10)).toEqual([icons]);
makeIconSets(icons, 3).should.deep.eq([icons]); expect(makeIconSets(icons, 3)).toEqual([icons]);
makeIconSets(icons, 2).should.deep.eq([ expect(makeIconSets(icons, 2)).toEqual([
[sponsor1, sponsor2], [sponsor1, sponsor2],
[sponsor1, sponsor3], [sponsor1, sponsor3],
[sponsor2, sponsor3], [sponsor2, sponsor3],
]); ]);
makeIconSets(icons, 1).should.deep.eq([[sponsor1], [sponsor2], [sponsor3]]); expect(makeIconSets(icons, 1)).toEqual([[sponsor1], [sponsor2], [sponsor3]]);
}); });
it('should pick icons appropriately when not required on different schedules', () => { it('should pick icons appropriately when not required on different schedules', () => {
const sponsor1 = parse({name: 'Sponsor1', topIconShowEvery: 1, icon: '1'}); const sponsor1 = parse({name: 'Sponsor1', topIconShowEvery: 1, icon: '1'});
const sponsor2 = parse({name: 'Sponsor2', topIconShowEvery: 2, icon: '2'}); const sponsor2 = parse({name: 'Sponsor2', topIconShowEvery: 2, icon: '2'});
const sponsor3 = parse({name: 'Sponsor3', topIconShowEvery: 3, icon: '3'}); const sponsor3 = parse({name: 'Sponsor3', topIconShowEvery: 3, icon: '3'});
const icons = [sponsor1, sponsor2, sponsor3]; const icons = [sponsor1, sponsor2, sponsor3];
makeIconSets(icons, 10).should.deep.eq([icons]); expect(makeIconSets(icons, 10)).toEqual([icons]);
makeIconSets(icons, 3).should.deep.eq([icons]); expect(makeIconSets(icons, 3)).toEqual([icons]);
makeIconSets(icons, 2).should.deep.eq([ expect(makeIconSets(icons, 2)).toEqual([
[sponsor1, sponsor2], [sponsor1, sponsor2],
[sponsor1, sponsor3], [sponsor1, sponsor3],
]); ]);
(() => makeIconSets(icons, 1)).should.throw(); expect(() => makeIconSets(icons, 1)).toThrow();
}); });
it('should pick icons appropriately with a lot of sponsors on representative schedules', () => { it('should pick icons appropriately with a lot of sponsors on representative schedules', () => {
const sponsor1 = parse({name: 'Sponsor1', topIconShowEvery: 1, icon: '1'}); const sponsor1 = parse({name: 'Sponsor1', topIconShowEvery: 1, icon: '1'});
@@ -203,12 +206,12 @@ levels:
const sponsor4 = parse({name: 'Sponsor4', topIconShowEvery: 3, icon: '3'}); const sponsor4 = parse({name: 'Sponsor4', topIconShowEvery: 3, icon: '3'});
const sponsor5 = parse({name: 'Sponsor5', topIconShowEvery: 3, icon: '3'}); const sponsor5 = parse({name: 'Sponsor5', topIconShowEvery: 3, icon: '3'});
const icons = [sponsor1, sponsor2, sponsor3, sponsor4, sponsor5]; const icons = [sponsor1, sponsor2, sponsor3, sponsor4, sponsor5];
makeIconSets(icons, 10).should.deep.eq([icons]); expect(makeIconSets(icons, 10)).toEqual([icons]);
makeIconSets(icons, 3).should.deep.eq([ expect(makeIconSets(icons, 3)).toEqual([
[sponsor1, sponsor2, sponsor3], [sponsor1, sponsor2, sponsor3],
[sponsor1, sponsor4, sponsor5], [sponsor1, sponsor4, sponsor5],
]); ]);
(() => makeIconSets(icons, 1)).should.throw(); expect(() => makeIconSets(icons, 1)).toThrow();
}); });
it('should handle alternating', () => { it('should handle alternating', () => {
const sponsor1 = parse({name: 'Sponsor1', topIconShowEvery: 1, icon: '1'}); const sponsor1 = parse({name: 'Sponsor1', topIconShowEvery: 1, icon: '1'});
@@ -216,12 +219,12 @@ levels:
const sponsor3 = parse({name: 'Sponsor3', topIconShowEvery: 2, icon: '3'}); const sponsor3 = parse({name: 'Sponsor3', topIconShowEvery: 2, icon: '3'});
const sponsor4 = parse({name: 'Sponsor4', topIconShowEvery: 2, icon: '4'}); const sponsor4 = parse({name: 'Sponsor4', topIconShowEvery: 2, icon: '4'});
const icons = [sponsor1, sponsor2, sponsor3, sponsor4]; const icons = [sponsor1, sponsor2, sponsor3, sponsor4];
makeIconSets(icons, 4).should.deep.eq([icons]); expect(makeIconSets(icons, 4)).toEqual([icons]);
makeIconSets(icons, 3).should.deep.eq([ expect(makeIconSets(icons, 3)).toEqual([
[sponsor1, sponsor2, sponsor3], [sponsor1, sponsor2, sponsor3],
[sponsor1, sponsor2, sponsor4], [sponsor1, sponsor2, sponsor4],
]); ]);
(() => makeIconSets(icons, 2)).should.throw(); expect(() => makeIconSets(icons, 2)).toThrow();
}); });
}); });
@@ -244,11 +247,11 @@ describe('Our specific sponsor file', () => {
for (const sponsor of pick) { for (const sponsor of pick) {
countBySponsor.set(sponsor, (countBySponsor.get(sponsor) || 0) + 1); countBySponsor.set(sponsor, (countBySponsor.get(sponsor) || 0) + 1);
} }
pick.length.should.eq(expectedNumIcons); expect(pick.length).toEqual(expectedNumIcons);
} }
for (const topIcon of sponsors.getAllTopIcons()) { for (const topIcon of sponsors.getAllTopIcons()) {
const appearsEvery = countBySponsor.get(topIcon) / numLoads; const appearsEvery = countBySponsor.get(topIcon) / numLoads;
appearsEvery.should.lte(topIcon.topIconShowEvery); expect(appearsEvery).toBeLessThanOrEqual(topIcon.topIconShowEvery);
} }
}); });
}); });

View File

@@ -17,6 +17,8 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE. // SOFTWARE.
import {describe, expect, it} from 'vitest';
import {parse} from '../lib/stack-usage-transformer.js'; import {parse} from '../lib/stack-usage-transformer.js';
describe('stack usage transformer', () => { describe('stack usage transformer', () => {
@@ -26,7 +28,7 @@ example.cpp:6:5:int f()\t32\tdynamic
example.cpp:7:5:int h()\t64\tdynamic,bounded example.cpp:7:5:int h()\t64\tdynamic,bounded
`; `;
const output = parse(doc); const output = parse(doc);
output.should.deep.equal([ expect(output).toEqual([
{ {
BytesUsed: 16, BytesUsed: 16,
DebugLoc: { DebugLoc: {

View File

@@ -22,6 +22,8 @@
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE. // POSSIBILITY OF SUCH DAMAGE.
import {describe, expect, it} from 'vitest';
import {ClientStateGoldenifier, ClientStateNormalizer} from '../lib/clientstate-normalizer.js'; import {ClientStateGoldenifier, ClientStateNormalizer} from '../lib/clientstate-normalizer.js';
import {ClientState} from '../lib/clientstate.js'; import {ClientState} from '../lib/clientstate.js';
@@ -39,7 +41,7 @@ describe('Normalizing clientstate', () => {
// note: this trick is to get rid of undefined parameters // note: this trick is to get rid of undefined parameters
const normalized = JSON.parse(JSON.stringify(normalizer.normalized)); const normalized = JSON.parse(JSON.stringify(normalizer.normalized));
normalized.should.deep.equal(resultdata); expect(normalized).toEqual(resultdata);
}); });
it('Should recognize everything and kitchensink as well', () => { it('Should recognize everything and kitchensink as well', () => {
@@ -55,7 +57,7 @@ describe('Normalizing clientstate', () => {
const normalized = JSON.parse(JSON.stringify(normalizer.normalized)); const normalized = JSON.parse(JSON.stringify(normalizer.normalized));
normalized.should.deep.equal(resultdata); expect(normalized).toEqual(resultdata);
}); });
it('Should support conformanceview', () => { it('Should support conformanceview', () => {
@@ -71,7 +73,7 @@ describe('Normalizing clientstate', () => {
const normalized = JSON.parse(JSON.stringify(normalizer.normalized)); const normalized = JSON.parse(JSON.stringify(normalizer.normalized));
normalized.should.deep.equal(resultdata); expect(normalized).toEqual(resultdata);
}); });
it('Should support executors', () => { it('Should support executors', () => {
@@ -85,7 +87,7 @@ describe('Normalizing clientstate', () => {
const normalized = JSON.parse(JSON.stringify(normalizer.normalized)); const normalized = JSON.parse(JSON.stringify(normalizer.normalized));
normalized.should.deep.equal(resultdata); expect(normalized).toEqual(resultdata);
}); });
it('Should support newer features', () => { it('Should support newer features', () => {
@@ -99,7 +101,7 @@ describe('Normalizing clientstate', () => {
const normalized = JSON.parse(JSON.stringify(normalizer.normalized)); const normalized = JSON.parse(JSON.stringify(normalizer.normalized));
normalized.should.deep.equal(resultdata); expect(normalized).toEqual(resultdata);
}); });
it('Allow output without editor id', () => { it('Allow output without editor id', () => {
@@ -113,7 +115,7 @@ describe('Normalizing clientstate', () => {
const normalized = JSON.parse(JSON.stringify(normalizer.normalized)); const normalized = JSON.parse(JSON.stringify(normalizer.normalized));
normalized.should.deep.equal(resultdata); expect(normalized).toEqual(resultdata);
}); });
}); });
@@ -125,8 +127,8 @@ describe('ClientState parsing', () => {
], ],
}); });
state.sessions[0].compilers.length.should.equal(1); expect(state.sessions[0].compilers.length).toEqual(1);
state.sessions[0].executors.length.should.equal(0); expect(state.sessions[0].executors.length).toEqual(0);
}); });
it('Should work with executor', () => { it('Should work with executor', () => {
@@ -146,14 +148,14 @@ describe('ClientState parsing', () => {
], ],
}); });
state.sessions[0].compilers.length.should.equal(0); expect(state.sessions[0].compilers.length).toEqual(0);
state.sessions[0].executors.length.should.equal(1); expect(state.sessions[0].executors.length).toEqual(1);
}); });
it('Should not contain id-less compilers', () => { it('Should not contain id-less compilers', () => {
const jsonStr = fs.readFileSync('test/state/bug-2231.json', {encoding: 'utf8'}); const jsonStr = fs.readFileSync('test/state/bug-2231.json', {encoding: 'utf8'});
const state = new ClientState(JSON.parse(jsonStr)); const state = new ClientState(JSON.parse(jsonStr));
state.sessions[0].compilers.length.should.equal(1); expect(state.sessions[0].compilers.length).toEqual(1);
}); });
}); });
@@ -161,7 +163,7 @@ describe('Trees', () => {
it('ClientState to GL', () => { it('ClientState to GL', () => {
const jsonStr = fs.readFileSync('test/state/tree.json', {encoding: 'utf8'}); const jsonStr = fs.readFileSync('test/state/tree.json', {encoding: 'utf8'});
const state = new ClientState(JSON.parse(jsonStr)); const state = new ClientState(JSON.parse(jsonStr));
state.trees.length.should.equal(1); expect(state.trees.length).toEqual(1);
const gl = new ClientStateGoldenifier(); const gl = new ClientStateGoldenifier();
gl.fromClientState(state); gl.fromClientState(state);
@@ -169,7 +171,7 @@ describe('Trees', () => {
const golden = JSON.parse(JSON.stringify(gl.golden)); const golden = JSON.parse(JSON.stringify(gl.golden));
const resultdata = JSON.parse(fs.readFileSync('test/state/tree.goldenified.json', {encoding: 'utf8'})); const resultdata = JSON.parse(fs.readFileSync('test/state/tree.goldenified.json', {encoding: 'utf8'}));
golden.should.deep.equal(resultdata); expect(golden).toEqual(resultdata);
}); });
it('GL to ClientState', () => { it('GL to ClientState', () => {
@@ -183,7 +185,7 @@ describe('Trees', () => {
const resultdata = JSON.parse(fs.readFileSync('test/state/tree.normalized.json', {encoding: 'utf8'})); const resultdata = JSON.parse(fs.readFileSync('test/state/tree.normalized.json', {encoding: 'utf8'}));
normalized.should.deep.equal(resultdata); expect(normalized).toEqual(resultdata);
}); });
it('GL to ClientState with correct output pane', () => { it('GL to ClientState with correct output pane', () => {
@@ -199,13 +201,13 @@ describe('Trees', () => {
fs.readFileSync('test/state/tree-gl-outputpane.normalized.json', {encoding: 'utf8'}), fs.readFileSync('test/state/tree-gl-outputpane.normalized.json', {encoding: 'utf8'}),
); );
normalized.should.deep.equal(resultdata); expect(normalized).toEqual(resultdata);
}); });
it('ClientState to Mobile GL', () => { it('ClientState to Mobile GL', () => {
const jsonStr = fs.readFileSync('test/state/tree-mobile.json', {encoding: 'utf8'}); const jsonStr = fs.readFileSync('test/state/tree-mobile.json', {encoding: 'utf8'});
const state = new ClientState(JSON.parse(jsonStr)); const state = new ClientState(JSON.parse(jsonStr));
state.trees.length.should.equal(1); expect(state.trees.length).toEqual(1);
const gl = new ClientStateGoldenifier(); const gl = new ClientStateGoldenifier();
const slides = gl.generatePresentationModeMobileViewerSlides(state); const slides = gl.generatePresentationModeMobileViewerSlides(state);
@@ -214,6 +216,6 @@ describe('Trees', () => {
//fs.writeFileSync('test/state/tree-mobile.goldenified.json', JSON.stringify(golden)); //fs.writeFileSync('test/state/tree-mobile.goldenified.json', JSON.stringify(golden));
const resultdata = JSON.parse(fs.readFileSync('test/state/tree-mobile.goldenified.json', {encoding: 'utf8'})); const resultdata = JSON.parse(fs.readFileSync('test/state/tree-mobile.goldenified.json', {encoding: 'utf8'}));
golden.should.deep.equal(resultdata); expect(golden).toEqual(resultdata);
}); });
}); });

View File

@@ -28,6 +28,7 @@ import {DynamoDB, GetItemCommand, PutItemCommand, QueryCommand, UpdateItemComman
import {GetObjectCommand, PutObjectCommand, S3} from '@aws-sdk/client-s3'; import {GetObjectCommand, PutObjectCommand, S3} from '@aws-sdk/client-s3';
import {sdkStreamMixin} from '@smithy/util-stream'; import {sdkStreamMixin} from '@smithy/util-stream';
import {mockClient} from 'aws-sdk-client-mock'; import {mockClient} from 'aws-sdk-client-mock';
import {beforeEach, describe, expect, it} from 'vitest';
import * as properties from '../../lib/properties.js'; import * as properties from '../../lib/properties.js';
import {StorageS3} from '../../lib/storage/index.js'; import {StorageS3} from '../../lib/storage/index.js';
@@ -47,16 +48,16 @@ describe('Find unique subhash tests', () => {
storagePrefix: 'prefix', storagePrefix: 'prefix',
storageDynamoTable: 'table', storageDynamoTable: 'table',
}); });
it('works when empty', () => { it('works when empty', async () => {
const storage = new StorageS3(httpRootDir, compilerProps, awsProps); const storage = new StorageS3(httpRootDir, compilerProps, awsProps);
mockDynamoDb.on(QueryCommand, {TableName: 'table'}).resolves({}); mockDynamoDb.on(QueryCommand, {TableName: 'table'}).resolves({});
return storage.findUniqueSubhash('ABCDEFGHIJKLMNOPQRSTUV').should.eventually.deep.equal({ await expect(storage.findUniqueSubhash('ABCDEFGHIJKLMNOPQRSTUV')).resolves.toEqual({
alreadyPresent: false, alreadyPresent: false,
prefix: 'ABCDEF', prefix: 'ABCDEF',
uniqueSubHash: 'ABCDEFGHI', uniqueSubHash: 'ABCDEFGHI',
}); });
}); });
it('works when not empty', () => { it('works when not empty', async () => {
const storage = new StorageS3(httpRootDir, compilerProps, awsProps); const storage = new StorageS3(httpRootDir, compilerProps, awsProps);
mockDynamoDb.on(QueryCommand, {TableName: 'table'}).resolves({ mockDynamoDb.on(QueryCommand, {TableName: 'table'}).resolves({
Items: [ Items: [
@@ -67,13 +68,13 @@ describe('Find unique subhash tests', () => {
], ],
}); });
return storage.findUniqueSubhash('ABCDEFGHIJKLMNOPQRSTUV').should.eventually.deep.equal({ await expect(storage.findUniqueSubhash('ABCDEFGHIJKLMNOPQRSTUV')).resolves.toEqual({
alreadyPresent: false, alreadyPresent: false,
prefix: 'ABCDEF', prefix: 'ABCDEF',
uniqueSubHash: 'ABCDEFGHI', uniqueSubHash: 'ABCDEFGHI',
}); });
}); });
it("works when there's a collision", () => { it("works when there's a collision", async () => {
const storage = new StorageS3(httpRootDir, compilerProps, awsProps); const storage = new StorageS3(httpRootDir, compilerProps, awsProps);
mockDynamoDb.on(QueryCommand, {TableName: 'table'}).resolves({ mockDynamoDb.on(QueryCommand, {TableName: 'table'}).resolves({
Items: [ Items: [
@@ -83,13 +84,13 @@ describe('Find unique subhash tests', () => {
}, },
], ],
}); });
return storage.findUniqueSubhash('ABCDEFGHIJKLMNOPQRSTUV').should.eventually.deep.equal({ await expect(storage.findUniqueSubhash('ABCDEFGHIJKLMNOPQRSTUV')).resolves.toEqual({
alreadyPresent: false, alreadyPresent: false,
prefix: 'ABCDEF', prefix: 'ABCDEF',
uniqueSubHash: 'ABCDEFGHIJ', uniqueSubHash: 'ABCDEFGHIJ',
}); });
}); });
it('finds an existing match', () => { it('finds an existing match', async () => {
const storage = new StorageS3(httpRootDir, compilerProps, awsProps); const storage = new StorageS3(httpRootDir, compilerProps, awsProps);
mockDynamoDb.on(QueryCommand, {TableName: 'table'}).resolves({ mockDynamoDb.on(QueryCommand, {TableName: 'table'}).resolves({
Items: [ Items: [
@@ -99,7 +100,7 @@ describe('Find unique subhash tests', () => {
}, },
], ],
}); });
return storage.findUniqueSubhash('ABCDEFGHIJKLMNOPQRSTUV').should.eventually.deep.equal({ await expect(storage.findUniqueSubhash('ABCDEFGHIJKLMNOPQRSTUV')).resolves.toEqual({
alreadyPresent: true, alreadyPresent: true,
prefix: 'ABCDEF', prefix: 'ABCDEF',
uniqueSubHash: 'ABCDEFGHI', uniqueSubHash: 'ABCDEFGHI',
@@ -131,15 +132,15 @@ describe('Stores to s3', () => {
config: 'yo', config: 'yo',
}; };
await storage.storeItem(object, {get: () => 'localhost'}); await storage.storeItem(object, {get: () => 'localhost'});
mockS3 expect(
.commandCalls(PutObjectCommand, { mockS3.commandCalls(PutObjectCommand, {
Bucket: 'bucket', Bucket: 'bucket',
Key: 'prefix/ABCDEFGHIJKLMNOP', Key: 'prefix/ABCDEFGHIJKLMNOP',
Body: 'yo', Body: 'yo',
}) }),
.should.have.lengthOf(1); ).toHaveLength(1);
mockDynamoDb expect(
.commandCalls(PutItemCommand, { mockDynamoDb.commandCalls(PutItemCommand, {
TableName: 'table', TableName: 'table',
Item: { Item: {
prefix: {S: 'ABCDEF'}, prefix: {S: 'ABCDEF'},
@@ -148,8 +149,8 @@ describe('Stores to s3', () => {
stats: {M: {clicks: {N: '0'}}}, stats: {M: {clicks: {N: '0'}}},
creation_ip: {S: 'localhost'}, creation_ip: {S: 'localhost'},
}, },
}) }),
.should.have.lengthOf(1); ).toHaveLength(1);
}); });
}); });
@@ -190,12 +191,12 @@ describe('Retrieves from s3', () => {
.resolves({Body: sdkStreamMixin(stream)}); .resolves({Body: sdkStreamMixin(stream)});
const result = await storage.expandId('ABCDEF'); const result = await storage.expandId('ABCDEF');
result.should.deep.equal({config: 'I am a monkey'}); expect(result).toEqual({config: 'I am a monkey'});
}); });
it('should handle failures', async () => { it('should handle failures', async () => {
const storage = new StorageS3(httpRootDir, compilerProps, awsProps); const storage = new StorageS3(httpRootDir, compilerProps, awsProps);
mockDynamoDb.on(GetItemCommand).resolves({}); mockDynamoDb.on(GetItemCommand).resolves({});
return storage.expandId('ABCDEF').should.be.rejectedWith(Error, 'ID ABCDEF not present in links table'); await expect(storage.expandId('ABCDEF')).rejects.toThrow('ID ABCDEF not present in links table');
}); });
}); });
@@ -217,8 +218,8 @@ describe('Updates counts in s3', async () => {
it('should increment for simple cases', async () => { it('should increment for simple cases', async () => {
const storage = new StorageS3(httpRootDir, compilerProps, awsProps); const storage = new StorageS3(httpRootDir, compilerProps, awsProps);
await storage.incrementViewCount('ABCDEF'); await storage.incrementViewCount('ABCDEF');
mockDynamoDb expect(
.commandCalls(UpdateItemCommand, { mockDynamoDb.commandCalls(UpdateItemCommand, {
ExpressionAttributeValues: {':inc': {N: '1'}}, ExpressionAttributeValues: {':inc': {N: '1'}},
Key: { Key: {
prefix: {S: 'ABCDEF'}, prefix: {S: 'ABCDEF'},
@@ -227,7 +228,7 @@ describe('Updates counts in s3', async () => {
ReturnValues: 'NONE', ReturnValues: 'NONE',
TableName: 'table', TableName: 'table',
UpdateExpression: 'SET stats.clicks = stats.clicks + :inc', UpdateExpression: 'SET stats.clicks = stats.clicks + :inc',
}) }),
.should.have.lengthOf(1); ).toHaveLength(1);
}); });
}); });

View File

@@ -22,8 +22,9 @@
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE. // POSSIBILITY OF SUCH DAMAGE.
import {describe, expect, it} from 'vitest';
import {StorageBase} from '../../lib/storage/index.js'; import {StorageBase} from '../../lib/storage/index.js';
import {should} from '../utils.js';
describe('Hash tests', () => { describe('Hash tests', () => {
// afterEach(() => restore()); // afterEach(() => restore());
@@ -31,14 +32,14 @@ describe('Hash tests', () => {
for (let i = 0; i < 256; ++i) { for (let i = 0; i < 256; ++i) {
const buf = Buffer.of(i); const buf = Buffer.of(i);
const as64 = StorageBase.encodeBuffer(buf); const as64 = StorageBase.encodeBuffer(buf);
as64.should.not.contain('/'); expect(as64).not.toContain('/');
as64.should.not.contain('+'); expect(as64).not.toContain('+');
} }
}); });
const badResult = 'R0Buttabcdefghio1327698asdhjkJJklQp'.toLowerCase(); // Butt hash, see https://github.com/compiler-explorer/compiler-explorer/issues/1297 const badResult = 'R0Buttabcdefghio1327698asdhjkJJklQp'.toLowerCase(); // Butt hash, see https://github.com/compiler-explorer/compiler-explorer/issues/1297
it('should detect profanities in hashes', () => { it('should detect profanities in hashes', () => {
StorageBase.isCleanText('I am the very model of a major general').should.be.true; expect(StorageBase.isCleanText('I am the very model of a major general')).toBe(true);
StorageBase.isCleanText(badResult).should.be.false; expect(StorageBase.isCleanText(badResult)).toBe(false);
}); });
// it('should avoid profanities and illegible characters in hashes', () => { // it('should avoid profanities and illegible characters in hashes', () => {
// const testCase = {some: 'test'}; // const testCase = {some: 'test'};
@@ -62,6 +63,6 @@ describe('Hash tests', () => {
const testCase = {some: 'test'}; const testCase = {some: 'test'};
const {config} = StorageBase.getSafeHash(testCase); const {config} = StorageBase.getSafeHash(testCase);
const asObj = JSON.parse(config); const asObj = JSON.parse(config);
should.not.exist(asObj.nonce); expect(asObj).not.toHaveProperty('nonce');
}); });
}); });

View File

@@ -22,67 +22,69 @@
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE. // POSSIBILITY OF SUCH DAMAGE.
import {describe, expect, it} from 'vitest';
import './utils.js'; import './utils.js';
import {SymbolStore} from '../lib/symbol-store.js'; import {SymbolStore} from '../lib/symbol-store.js';
describe('SymbolStore', function () { describe('SymbolStore', () => {
it('should be empty initially', function () { it('should be empty initially', () => {
const store = new SymbolStore(); const store = new SymbolStore();
store.listSymbols().length.should.equal(0); expect(store.listSymbols().length).toEqual(0);
store.listTranslations().length.should.equal(0); expect(store.listTranslations().length).toEqual(0);
}); });
it('should be able to add an item', function () { it('should be able to add an item', () => {
const store = new SymbolStore(); const store = new SymbolStore();
store.add('test'); store.add('test');
store.listSymbols().length.should.equal(1); expect(store.listSymbols().length).toEqual(1);
store.listTranslations().length.should.equal(1); expect(store.listTranslations().length).toEqual(1);
store.listSymbols()[0].should.equal('test'); expect(store.listSymbols()[0]).toEqual('test');
const translations = store.listTranslations(); const translations = store.listTranslations();
translations[0][0].should.equal('test'); expect(translations[0][0]).toEqual('test');
translations[0][1].should.equal('test'); expect(translations[0][1]).toEqual('test');
}); });
it('should not contain duplicate items', function () { it('should not contain duplicate items', () => {
const store = new SymbolStore(); const store = new SymbolStore();
store.add('test'); store.add('test');
store.add('test'); store.add('test');
store.listSymbols().length.should.equal(1); expect(store.listSymbols().length).toEqual(1);
store.listTranslations().length.should.equal(1); expect(store.listTranslations().length).toEqual(1);
store.listSymbols()[0].should.equal('test'); expect(store.listSymbols()[0]).toEqual('test');
const translations = store.listTranslations(); const translations = store.listTranslations();
translations[0][0].should.equal('test'); expect(translations[0][0]).toEqual('test');
translations[0][1].should.equal('test'); expect(translations[0][1]).toEqual('test');
}); });
it('should return a sorted list', function () { it('should return a sorted list', () => {
const store = new SymbolStore(); const store = new SymbolStore();
store.add('test123'); store.add('test123');
store.add('test123456'); store.add('test123456');
store.listSymbols().length.should.equal(2); expect(store.listSymbols().length).toEqual(2);
store.listTranslations().length.should.equal(2); expect(store.listTranslations().length).toEqual(2);
const translations = store.listTranslations(); const translations = store.listTranslations();
translations[0][0].should.equal('test123456'); expect(translations[0][0]).toEqual('test123456');
translations[1][0].should.equal('test123'); expect(translations[1][0]).toEqual('test123');
}); });
it('should be able to add an array of items', function () { it('should be able to add an array of items', () => {
const store = new SymbolStore(); const store = new SymbolStore();
store.addMany(['test123', 'test123456', 'test123']); store.addMany(['test123', 'test123456', 'test123']);
store.listSymbols().length.should.equal(2); expect(store.listSymbols().length).toEqual(2);
store.listTranslations().length.should.equal(2); expect(store.listTranslations().length).toEqual(2);
const translations = store.listTranslations(); const translations = store.listTranslations();
translations[0][0].should.equal('test123456'); expect(translations[0][0]).toEqual('test123456');
translations[1][0].should.equal('test123'); expect(translations[1][0]).toEqual('test123');
}); });
it('should be possible to exclude items in another store', function () { it('should be possible to exclude items in another store', () => {
const store1 = new SymbolStore(); const store1 = new SymbolStore();
store1.addMany(['test123', 'test123456', 'test123']); store1.addMany(['test123', 'test123456', 'test123']);
@@ -91,11 +93,11 @@ describe('SymbolStore', function () {
store1.exclude(store2); store1.exclude(store2);
const translations = store1.listTranslations(); const translations = store1.listTranslations();
translations.length.should.equal(1); expect(translations.length).toEqual(1);
translations[0][0].should.equal('test123456'); expect(translations[0][0]).toEqual('test123456');
}); });
it('should be possible to exclude items that partially match', function () { it('should be possible to exclude items that partially match', () => {
const store1 = new SymbolStore(); const store1 = new SymbolStore();
store1.addMany(['test123', 'test123456', 'test123']); store1.addMany(['test123', 'test123456', 'test123']);
@@ -104,18 +106,18 @@ describe('SymbolStore', function () {
store1.softExclude(store2); store1.softExclude(store2);
const translations = store1.listTranslations(); const translations = store1.listTranslations();
translations.length.should.equal(1); expect(translations.length).toEqual(1);
translations[0][0].should.equal('test123456'); expect(translations[0][0]).toEqual('test123456');
}); });
it('should be able to check contents', function () { it('should be able to check contents', () => {
const store = new SymbolStore(); const store = new SymbolStore();
store.addMany(['test123', 'test123456', 'test123']); store.addMany(['test123', 'test123456', 'test123']);
store.contains('test123').should.equal(true); expect(store.contains('test123')).toEqual(true);
store.contains('test123456').should.equal(true); expect(store.contains('test123456')).toEqual(true);
store.contains('test456').should.equal(false); expect(store.contains('test456')).toEqual(false);
store.listSymbols().length.should.equal(2); expect(store.listSymbols().length).toEqual(2);
}); });
}); });

View File

@@ -22,6 +22,8 @@
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE. // POSSIBILITY OF SUCH DAMAGE.
import {describe, expect, it} from 'vitest';
import { import {
getToolchainFlagFromOptions, getToolchainFlagFromOptions,
getToolchainPathWithOptionsArr, getToolchainPathWithOptionsArr,
@@ -49,7 +51,7 @@ describe('CompilerDropInTool', () => {
const sourcefile = 'example.cpp'; const sourcefile = 'example.cpp';
const orderedArgs = tool.getOrderedArguments(compilationInfo, includeflags, [], args, sourcefile); const orderedArgs = tool.getOrderedArguments(compilationInfo, includeflags, [], args, sourcefile);
orderedArgs.should.deep.equal([ expect(orderedArgs).toEqual([
'--gcc-toolchain=/opt/compiler-explorer/gcc-7.2.0', '--gcc-toolchain=/opt/compiler-explorer/gcc-7.2.0',
'--gcc-toolchain=/opt/compiler-explorer/gcc-7.2.0', '--gcc-toolchain=/opt/compiler-explorer/gcc-7.2.0',
]); ]);
@@ -70,7 +72,7 @@ describe('CompilerDropInTool', () => {
const sourcefile = 'example.cpp'; const sourcefile = 'example.cpp';
const orderedArgs = tool.getOrderedArguments(compilationInfo, includeflags, [], args, sourcefile); const orderedArgs = tool.getOrderedArguments(compilationInfo, includeflags, [], args, sourcefile);
orderedArgs.should.deep.equal([ expect(orderedArgs).toEqual([
'--gcc-toolchain=' + path.resolve('/opt/compiler-explorer/gcc-8.0'), '--gcc-toolchain=' + path.resolve('/opt/compiler-explorer/gcc-8.0'),
'--gcc-toolchain=' + path.resolve('/opt/compiler-explorer/gcc-8.0'), '--gcc-toolchain=' + path.resolve('/opt/compiler-explorer/gcc-8.0'),
]); ]);
@@ -91,7 +93,7 @@ describe('CompilerDropInTool', () => {
const sourcefile = 'example.cpp'; const sourcefile = 'example.cpp';
const orderedArgs = tool.getOrderedArguments(compilationInfo, includeflags, [], args, sourcefile); const orderedArgs = tool.getOrderedArguments(compilationInfo, includeflags, [], args, sourcefile);
orderedArgs.should.deep.equal(false); expect(orderedArgs).toEqual(false);
}); });
it('Should support ICC compilers', () => { it('Should support ICC compilers', () => {
@@ -109,7 +111,7 @@ describe('CompilerDropInTool', () => {
const sourcefile = 'example.cpp'; const sourcefile = 'example.cpp';
const orderedArgs = tool.getOrderedArguments(compilationInfo, includeflags, [], args, sourcefile); const orderedArgs = tool.getOrderedArguments(compilationInfo, includeflags, [], args, sourcefile);
orderedArgs.should.deep.equal([ expect(orderedArgs).toEqual([
'--gcc-toolchain=' + path.resolve('/opt/compiler-explorer/gcc-8.2.0'), '--gcc-toolchain=' + path.resolve('/opt/compiler-explorer/gcc-8.2.0'),
'--gcc-toolchain=' + path.resolve('/opt/compiler-explorer/gcc-8.2.0'), '--gcc-toolchain=' + path.resolve('/opt/compiler-explorer/gcc-8.2.0'),
]); ]);
@@ -133,7 +135,7 @@ describe('CompilerDropInTool', () => {
const sourcefile = 'example.cpp'; const sourcefile = 'example.cpp';
const orderedArgs = tool.getOrderedArguments(compilationInfo, includeflags, [], args, sourcefile); const orderedArgs = tool.getOrderedArguments(compilationInfo, includeflags, [], args, sourcefile);
orderedArgs.should.deep.equal(false); expect(orderedArgs).toEqual(false);
}); });
it('Should not support using libc++', () => { it('Should not support using libc++', () => {
@@ -152,7 +154,7 @@ describe('CompilerDropInTool', () => {
const sourcefile = 'example.cpp'; const sourcefile = 'example.cpp';
const orderedArgs = tool.getOrderedArguments(compilationInfo, includeflags, [], args, sourcefile); const orderedArgs = tool.getOrderedArguments(compilationInfo, includeflags, [], args, sourcefile);
orderedArgs.should.deep.equal(false); expect(orderedArgs).toEqual(false);
}); });
it('Should support library options', () => { it('Should support library options', () => {
@@ -172,7 +174,7 @@ describe('CompilerDropInTool', () => {
const libOptions = ['-DMYLIBDEF', '-pthread']; const libOptions = ['-DMYLIBDEF', '-pthread'];
const orderedArgs = tool.getOrderedArguments(compilationInfo, includeflags, libOptions, args, sourcefile); const orderedArgs = tool.getOrderedArguments(compilationInfo, includeflags, libOptions, args, sourcefile);
orderedArgs.should.deep.equal([ expect(orderedArgs).toEqual([
'--gcc-toolchain=/opt/compiler-explorer/gcc-8.2.0', '--gcc-toolchain=/opt/compiler-explorer/gcc-8.2.0',
'--gcc-toolchain=/opt/compiler-explorer/gcc-8.2.0', '--gcc-toolchain=/opt/compiler-explorer/gcc-8.2.0',
'-DMYLIBDEF', '-DMYLIBDEF',
@@ -195,12 +197,12 @@ describe('CompilerDropInTool', () => {
'/app/example.cpp', '/app/example.cpp',
]; ];
hasToolchainArg(options).should.be.true; expect(hasToolchainArg(options)).toBe(true);
getToolchainFlagFromOptions(options).should.equal('--gcc-toolchain='); expect(getToolchainFlagFromOptions(options)).toEqual('--gcc-toolchain=');
const newOptions = removeToolchainArg(options); const newOptions = removeToolchainArg(options);
hasToolchainArg(newOptions).should.be.false; expect(hasToolchainArg(newOptions)).toBe(false);
}); });
it('Should be able to swap toolchain', () => { it('Should be able to swap toolchain', () => {
@@ -220,10 +222,10 @@ describe('CompilerDropInTool', () => {
]; ];
const toolchain = getToolchainPathWithOptionsArr(exe, options); const toolchain = getToolchainPathWithOptionsArr(exe, options);
toolchain.should.equals('/opt/compiler-explorer/gcc-12.2.0'); expect(toolchain).toEqual('/opt/compiler-explorer/gcc-12.2.0');
const replacedOptions = replaceToolchainArg(options, '/opt/compiler-explorer/gcc-11.1.0'); const replacedOptions = replaceToolchainArg(options, '/opt/compiler-explorer/gcc-11.1.0');
replacedOptions.should.deep.equal([ expect(replacedOptions).toEqual([
'-gdwarf-4', '-gdwarf-4',
'-g', '-g',
'-o', '-o',

View File

@@ -23,6 +23,7 @@
// POSSIBILITY OF SUCH DAMAGE. // POSSIBILITY OF SUCH DAMAGE.
import express from 'express'; import express from 'express';
import {beforeAll, describe, expect, it} from 'vitest';
import {RouteAPI} from '../lib/handlers/route-api.js'; import {RouteAPI} from '../lib/handlers/route-api.js';
@@ -33,7 +34,7 @@ describe('Basic unfurls', () => {
let config; let config;
let routeApi; let routeApi;
before(() => { beforeAll(() => {
config = { config = {
ceProps: () => {}, ceProps: () => {},
clientOptionsHandler: { clientOptionsHandler: {
@@ -75,7 +76,7 @@ describe('Basic unfurls', () => {
}); });
const res = await prom; const res = await prom;
res.metadata.should.deep.equal({ expect(res.metadata).toEqual({
ogDescription: '', ogDescription: '',
ogTitle: 'Compiler Explorer', ogTitle: 'Compiler Explorer',
}); });
@@ -103,7 +104,7 @@ describe('Basic unfurls', () => {
}); });
const res = await prom; const res = await prom;
res.metadata.should.deep.equal({ expect(res.metadata).toEqual({
ogDescription: ogDescription:
'\ntemplate&lt;typename T&gt;\nconcept TheSameAndAddable = requires(T a, T b) {\n {a+b} -&gt; T;\n};\n\ntemplate&lt;TheSameAndAddable T&gt;\nT sum(T x, T y) {\n return x + y;\n}\n\n#include &lt;string&gt;\n\nint main() {\n int z = 0;\n int w;\n\n return sum(z, w);\n}\n', '\ntemplate&lt;typename T&gt;\nconcept TheSameAndAddable = requires(T a, T b) {\n {a+b} -&gt; T;\n};\n\ntemplate&lt;TheSameAndAddable T&gt;\nT sum(T x, T y) {\n return x + y;\n}\n\n#include &lt;string&gt;\n\nint main() {\n int z = 0;\n int w;\n\n return sum(z, w);\n}\n',
ogTitle: 'Compiler Explorer - C++', ogTitle: 'Compiler Explorer - C++',
@@ -132,7 +133,7 @@ describe('Basic unfurls', () => {
}); });
const res = await prom; const res = await prom;
res.metadata.should.deep.equal({ expect(res.metadata).toEqual({
ogDescription: 'project(hello)\n\nadd_executable(output.s\n example.cpp\n square.cpp)\n', ogDescription: 'project(hello)\n\nadd_executable(output.s\n example.cpp\n square.cpp)\n',
ogTitle: 'Compiler Explorer - C++', ogTitle: 'Compiler Explorer - C++',
}); });

View File

@@ -25,6 +25,7 @@
import path from 'path'; import path from 'path';
import {fileURLToPath} from 'url'; import {fileURLToPath} from 'url';
import {describe, expect, it} from 'vitest';
import winston from 'winston'; import winston from 'winston';
import {makeLogStream} from '../lib/logger.js'; import {makeLogStream} from '../lib/logger.js';
@@ -34,65 +35,65 @@ import {fs} from './utils.js';
describe('Splits lines', () => { describe('Splits lines', () => {
it('handles empty input', () => { it('handles empty input', () => {
utils.splitLines('').should.deep.equals([]); expect(utils.splitLines('')).toEqual([]);
}); });
it('handles a single line with no newline', () => { it('handles a single line with no newline', () => {
utils.splitLines('A line').should.deep.equals(['A line']); expect(utils.splitLines('A line')).toEqual(['A line']);
}); });
it('handles a single line with a newline', () => { it('handles a single line with a newline', () => {
utils.splitLines('A line\n').should.deep.equals(['A line']); expect(utils.splitLines('A line\n')).toEqual(['A line']);
}); });
it('handles multiple lines', () => { it('handles multiple lines', () => {
utils.splitLines('A line\nAnother line\n').should.deep.equals(['A line', 'Another line']); expect(utils.splitLines('A line\nAnother line\n')).toEqual(['A line', 'Another line']);
}); });
it('handles multiple lines ending on a non-newline', () => { it('handles multiple lines ending on a non-newline', () => {
utils.splitLines('A line\nAnother line\nLast line').should.deep.equals(['A line', 'Another line', 'Last line']); expect(utils.splitLines('A line\nAnother line\nLast line')).toEqual(['A line', 'Another line', 'Last line']);
}); });
it('handles empty lines', () => { it('handles empty lines', () => {
utils.splitLines('A line\n\nA line after an empty').should.deep.equals(['A line', '', 'A line after an empty']); expect(utils.splitLines('A line\n\nA line after an empty')).toEqual(['A line', '', 'A line after an empty']);
}); });
it('handles a single empty line', () => { it('handles a single empty line', () => {
utils.splitLines('\n').should.deep.equals(['']); expect(utils.splitLines('\n')).toEqual(['']);
}); });
it('handles multiple empty lines', () => { it('handles multiple empty lines', () => {
utils.splitLines('\n\n\n').should.deep.equals(['', '', '']); expect(utils.splitLines('\n\n\n')).toEqual(['', '', '']);
}); });
it('handles \\r\\n lines', () => { it('handles \\r\\n lines', () => {
utils.splitLines('Some\r\nLines\r\n').should.deep.equals(['Some', 'Lines']); expect(utils.splitLines('Some\r\nLines\r\n')).toEqual(['Some', 'Lines']);
}); });
}); });
describe('Expands tabs', () => { describe('Expands tabs', () => {
it('leaves non-tabs alone', () => { it('leaves non-tabs alone', () => {
utils.expandTabs('This has no tabs at all').should.equals('This has no tabs at all'); expect(utils.expandTabs('This has no tabs at all')).toEqual('This has no tabs at all');
}); });
it('at beginning of line', () => { it('at beginning of line', () => {
utils.expandTabs('\tOne tab').should.equals(' One tab'); expect(utils.expandTabs('\tOne tab')).toEqual(' One tab');
utils.expandTabs('\t\tTwo tabs').should.equals(' Two tabs'); expect(utils.expandTabs('\t\tTwo tabs')).toEqual(' Two tabs');
}); });
it('mid-line', () => { it('mid-line', () => {
utils.expandTabs('0\t1234567A').should.equals('0 1234567A'); expect(utils.expandTabs('0\t1234567A')).toEqual('0 1234567A');
utils.expandTabs('01\t234567A').should.equals('01 234567A'); expect(utils.expandTabs('01\t234567A')).toEqual('01 234567A');
utils.expandTabs('012\t34567A').should.equals('012 34567A'); expect(utils.expandTabs('012\t34567A')).toEqual('012 34567A');
utils.expandTabs('0123\t4567A').should.equals('0123 4567A'); expect(utils.expandTabs('0123\t4567A')).toEqual('0123 4567A');
utils.expandTabs('01234\t567A').should.equals('01234 567A'); expect(utils.expandTabs('01234\t567A')).toEqual('01234 567A');
utils.expandTabs('012345\t67A').should.equals('012345 67A'); expect(utils.expandTabs('012345\t67A')).toEqual('012345 67A');
utils.expandTabs('0123456\t7A').should.equals('0123456 7A'); expect(utils.expandTabs('0123456\t7A')).toEqual('0123456 7A');
utils.expandTabs('01234567\tA').should.equals('01234567 A'); expect(utils.expandTabs('01234567\tA')).toEqual('01234567 A');
}); });
}); });
describe('Parses compiler output', () => { describe('Parses compiler output', () => {
it('handles simple cases', () => { it('handles simple cases', () => {
utils.parseOutput('Line one\nLine two', 'bob.cpp').should.deep.equals([{text: 'Line one'}, {text: 'Line two'}]); expect(utils.parseOutput('Line one\nLine two', 'bob.cpp')).toEqual([{text: 'Line one'}, {text: 'Line two'}]);
utils.parseOutput('Line one\nbob.cpp:1 Line two', 'bob.cpp').should.deep.equals([ expect(utils.parseOutput('Line one\nbob.cpp:1 Line two', 'bob.cpp')).toEqual([
{text: 'Line one'}, {text: 'Line one'},
{ {
tag: {column: 0, line: 1, text: 'Line two', severity: 3, file: 'bob.cpp'}, tag: {column: 0, line: 1, text: 'Line two', severity: 3, file: 'bob.cpp'},
text: '<source>:1 Line two', text: '<source>:1 Line two',
}, },
]); ]);
utils.parseOutput('Line one\nbob.cpp:1:5: Line two', 'bob.cpp').should.deep.equals([ expect(utils.parseOutput('Line one\nbob.cpp:1:5: Line two', 'bob.cpp')).toEqual([
{text: 'Line one'}, {text: 'Line one'},
{ {
tag: {column: 5, line: 1, text: 'Line two', severity: 3, file: 'bob.cpp'}, tag: {column: 5, line: 1, text: 'Line two', severity: 3, file: 'bob.cpp'},
@@ -101,7 +102,7 @@ describe('Parses compiler output', () => {
]); ]);
}); });
it('handles windows output', () => { it('handles windows output', () => {
utils.parseOutput('bob.cpp(1) Oh noes', 'bob.cpp').should.deep.equals([ expect(utils.parseOutput('bob.cpp(1) Oh noes', 'bob.cpp')).toEqual([
{ {
tag: {column: 0, line: 1, text: 'Oh noes', severity: 3, file: 'bob.cpp'}, tag: {column: 0, line: 1, text: 'Oh noes', severity: 3, file: 'bob.cpp'},
text: '<source>(1) Oh noes', text: '<source>(1) Oh noes',
@@ -109,7 +110,7 @@ describe('Parses compiler output', () => {
]); ]);
}); });
it('replaces all references to input source', () => { it('replaces all references to input source', () => {
utils.parseOutput('bob.cpp:1 error in bob.cpp', 'bob.cpp').should.deep.equals([ expect(utils.parseOutput('bob.cpp:1 error in bob.cpp', 'bob.cpp')).toEqual([
{ {
tag: {column: 0, line: 1, text: 'error in <source>', severity: 3, file: 'bob.cpp'}, tag: {column: 0, line: 1, text: 'error in <source>', severity: 3, file: 'bob.cpp'},
text: '<source>:1 error in <source>', text: '<source>:1 error in <source>',
@@ -117,14 +118,14 @@ describe('Parses compiler output', () => {
]); ]);
}); });
it('treats warnings and notes as the correct severity', () => { it('treats warnings and notes as the correct severity', () => {
utils.parseOutput('Line one\nbob.cpp:1:5: warning Line two', 'bob.cpp').should.deep.equals([ expect(utils.parseOutput('Line one\nbob.cpp:1:5: warning Line two', 'bob.cpp')).toEqual([
{text: 'Line one'}, {text: 'Line one'},
{ {
tag: {column: 5, line: 1, text: 'warning Line two', severity: 2, file: 'bob.cpp'}, tag: {column: 5, line: 1, text: 'warning Line two', severity: 2, file: 'bob.cpp'},
text: '<source>:1:5: warning Line two', text: '<source>:1:5: warning Line two',
}, },
]); ]);
utils.parseOutput('Line one\nbob.cpp:1:5: note Line two', 'bob.cpp').should.deep.equals([ expect(utils.parseOutput('Line one\nbob.cpp:1:5: note Line two', 'bob.cpp')).toEqual([
{text: 'Line one'}, {text: 'Line one'},
{ {
tag: {column: 5, line: 1, text: 'note Line two', severity: 1, file: 'bob.cpp'}, tag: {column: 5, line: 1, text: 'note Line two', severity: 1, file: 'bob.cpp'},
@@ -133,9 +134,9 @@ describe('Parses compiler output', () => {
]); ]);
}); });
it('treats <stdin> as if it were the compiler source', () => { it('treats <stdin> as if it were the compiler source', () => {
utils expect(
.parseOutput("<stdin>:120:25: error: variable or field 'transform_data' declared void", 'bob.cpp') utils.parseOutput("<stdin>:120:25: error: variable or field 'transform_data' declared void", 'bob.cpp'),
.should.deep.equals([ ).toEqual([
{ {
tag: { tag: {
column: 25, column: 25,
@@ -150,7 +151,7 @@ describe('Parses compiler output', () => {
}); });
it('parser error with full path', () => { it('parser error with full path', () => {
utils.parseOutput("/app/example.cl:5:30: error: use of undeclared identifier 'ad'").should.deep.equals([ expect(utils.parseOutput("/app/example.cl:5:30: error: use of undeclared identifier 'ad'")).toEqual([
{ {
tag: { tag: {
file: 'example.cl', file: 'example.cl',
@@ -167,7 +168,7 @@ describe('Parses compiler output', () => {
describe('Pascal compiler output', () => { describe('Pascal compiler output', () => {
it('recognize fpc identifier not found error', () => { it('recognize fpc identifier not found error', () => {
utils.parseOutput('output.pas(13,23) Error: Identifier not found "adsadasd"', 'output.pas').should.deep.equals([ expect(utils.parseOutput('output.pas(13,23) Error: Identifier not found "adsadasd"', 'output.pas')).toEqual([
{ {
tag: { tag: {
column: 23, column: 23,
@@ -182,9 +183,9 @@ describe('Pascal compiler output', () => {
}); });
it('recognize fpc exiting error', () => { it('recognize fpc exiting error', () => {
utils expect(
.parseOutput('output.pas(17) Fatal: There were 1 errors compiling module, stopping', 'output.pas') utils.parseOutput('output.pas(17) Fatal: There were 1 errors compiling module, stopping', 'output.pas'),
.should.deep.equals([ ).toEqual([
{ {
tag: { tag: {
column: 0, column: 0,
@@ -199,13 +200,13 @@ describe('Pascal compiler output', () => {
}); });
it('removes the temp path', () => { it('removes the temp path', () => {
utils expect(
.parseOutput( utils.parseOutput(
'Compiling /tmp/path/prog.dpr\noutput.pas(17) Fatal: There were 1 errors compiling module, stopping', 'Compiling /tmp/path/prog.dpr\noutput.pas(17) Fatal: There were 1 errors compiling module, stopping',
'output.pas', 'output.pas',
'/tmp/path/', '/tmp/path/',
) ),
.should.deep.equals([ ).toEqual([
{ {
text: 'Compiling prog.dpr', text: 'Compiling prog.dpr',
}, },
@@ -225,10 +226,8 @@ describe('Pascal compiler output', () => {
describe('Rust compiler output', () => { describe('Rust compiler output', () => {
it('handles simple cases', () => { it('handles simple cases', () => {
utils expect(utils.parseRustOutput('Line one\nLine two', 'bob.rs')).toEqual([{text: 'Line one'}, {text: 'Line two'}]);
.parseRustOutput('Line one\nLine two', 'bob.rs') expect(utils.parseRustOutput('Unrelated\nLine one\n --> bob.rs:1\nUnrelated', 'bob.rs')).toEqual([
.should.deep.equals([{text: 'Line one'}, {text: 'Line two'}]);
utils.parseRustOutput('Unrelated\nLine one\n --> bob.rs:1\nUnrelated', 'bob.rs').should.deep.equals([
{text: 'Unrelated'}, {text: 'Unrelated'},
{ {
tag: {column: 0, line: 1, text: 'Line one', severity: 3}, tag: {column: 0, line: 1, text: 'Line one', severity: 3},
@@ -240,7 +239,7 @@ describe('Rust compiler output', () => {
}, },
{text: 'Unrelated'}, {text: 'Unrelated'},
]); ]);
utils.parseRustOutput('Line one\n --> bob.rs:1:5', 'bob.rs').should.deep.equals([ expect(utils.parseRustOutput('Line one\n --> bob.rs:1:5', 'bob.rs')).toEqual([
{ {
tag: {column: 5, line: 1, text: 'Line one', severity: 3}, tag: {column: 5, line: 1, text: 'Line one', severity: 3},
text: 'Line one', text: 'Line one',
@@ -253,7 +252,7 @@ describe('Rust compiler output', () => {
}); });
it('replaces all references to input source', () => { it('replaces all references to input source', () => {
utils.parseRustOutput('error: Error in bob.rs\n --> bob.rs:1', 'bob.rs').should.deep.equals([ expect(utils.parseRustOutput('error: Error in bob.rs\n --> bob.rs:1', 'bob.rs')).toEqual([
{ {
tag: {column: 0, line: 1, text: 'error: Error in <source>', severity: 3}, tag: {column: 0, line: 1, text: 'error: Error in <source>', severity: 3},
text: 'error: Error in <source>', text: 'error: Error in <source>',
@@ -266,7 +265,7 @@ describe('Rust compiler output', () => {
}); });
it('treats <stdin> as if it were the compiler source', () => { it('treats <stdin> as if it were the compiler source', () => {
utils.parseRustOutput('error: <stdin> is sad\n --> <stdin>:120:25', 'bob.rs').should.deep.equals([ expect(utils.parseRustOutput('error: <stdin> is sad\n --> <stdin>:120:25', 'bob.rs')).toEqual([
{ {
tag: {column: 25, line: 120, text: 'error: <source> is sad', severity: 3}, tag: {column: 25, line: 120, text: 'error: <source> is sad', severity: 3},
text: 'error: <source> is sad', text: 'error: <source> is sad',
@@ -281,9 +280,12 @@ describe('Rust compiler output', () => {
describe('Tool output', () => { describe('Tool output', () => {
it('removes the relative path', () => { it('removes the relative path', () => {
utils expect(
.parseOutput('./example.cpp:1:1: Fatal: There were 1 errors compiling module, stopping', './example.cpp') utils.parseOutput(
.should.deep.equals([ './example.cpp:1:1: Fatal: There were 1 errors compiling module, stopping',
'./example.cpp',
),
).toEqual([
{ {
tag: { tag: {
column: 1, column: 1,
@@ -298,9 +300,9 @@ describe('Tool output', () => {
}); });
it('removes fortran relative path', () => { it('removes fortran relative path', () => {
utils expect(
.parseOutput("./example.f90:5:22: error: No explicit type declared for 'y'", './example.f90') utils.parseOutput("./example.f90:5:22: error: No explicit type declared for 'y'", './example.f90'),
.should.deep.equals([ ).toEqual([
{ {
tag: { tag: {
column: 22, column: 22,
@@ -315,12 +317,12 @@ describe('Tool output', () => {
}); });
it('removes the jailed path', () => { it('removes the jailed path', () => {
utils expect(
.parseOutput( utils.parseOutput(
'/home/ubuntu/example.cpp:1:1: Fatal: There were 1 errors compiling module, stopping', '/home/ubuntu/example.cpp:1:1: Fatal: There were 1 errors compiling module, stopping',
'./example.cpp', './example.cpp',
) ),
.should.deep.equals([ ).toEqual([
{ {
tag: { tag: {
column: 1, column: 1,
@@ -337,39 +339,39 @@ describe('Tool output', () => {
describe('Pads right', () => { describe('Pads right', () => {
it('works', () => { it('works', () => {
utils.padRight('abcd', 8).should.equal('abcd '); expect(utils.padRight('abcd', 8)).toEqual('abcd ');
utils.padRight('a', 8).should.equal('a '); expect(utils.padRight('a', 8)).toEqual('a ');
utils.padRight('', 8).should.equal(' '); expect(utils.padRight('', 8)).toEqual(' ');
utils.padRight('abcd', 4).should.equal('abcd'); expect(utils.padRight('abcd', 4)).toEqual('abcd');
utils.padRight('abcd', 2).should.equal('abcd'); expect(utils.padRight('abcd', 2)).toEqual('abcd');
}); });
}); });
describe('Trim right', () => { describe('Trim right', () => {
it('works', () => { it('works', () => {
utils.trimRight(' ').should.equal(''); expect(utils.trimRight(' ')).toEqual('');
utils.trimRight('').should.equal(''); expect(utils.trimRight('')).toEqual('');
utils.trimRight(' ab ').should.equal(' ab'); expect(utils.trimRight(' ab ')).toEqual(' ab');
utils.trimRight(' a b ').should.equal(' a b'); expect(utils.trimRight(' a b ')).toEqual(' a b');
utils.trimRight('a ').should.equal('a'); expect(utils.trimRight('a ')).toEqual('a');
}); });
}); });
describe('Anonymizes all kind of IPs', () => { describe('Anonymizes all kind of IPs', () => {
it('Ignores localhost', () => { it('Ignores localhost', () => {
utils.anonymizeIp('localhost').should.equal('localhost'); expect(utils.anonymizeIp('localhost')).toEqual('localhost');
utils.anonymizeIp('localhost:42').should.equal('localhost:42'); expect(utils.anonymizeIp('localhost:42')).toEqual('localhost:42');
}); });
it('Removes last octet from IPv4 addresses', () => { it('Removes last octet from IPv4 addresses', () => {
utils.anonymizeIp('127.0.0.0').should.equal('127.0.0.0'); expect(utils.anonymizeIp('127.0.0.0')).toEqual('127.0.0.0');
utils.anonymizeIp('127.0.0.10').should.equal('127.0.0.0'); expect(utils.anonymizeIp('127.0.0.10')).toEqual('127.0.0.0');
utils.anonymizeIp('127.0.0.255').should.equal('127.0.0.0'); expect(utils.anonymizeIp('127.0.0.255')).toEqual('127.0.0.0');
}); });
it('Removes last 3 hextets from IPv6 addresses', () => { it('Removes last 3 hextets from IPv6 addresses', () => {
// Not necessarily valid addresses, we're interested in the format // Not necessarily valid addresses, we're interested in the format
utils.anonymizeIp('ffff:aaaa:dead:beef').should.equal('ffff:0:0:0'); expect(utils.anonymizeIp('ffff:aaaa:dead:beef')).toEqual('ffff:0:0:0');
utils.anonymizeIp('bad:c0de::').should.equal('bad:0:0:0'); expect(utils.anonymizeIp('bad:c0de::')).toEqual('bad:0:0:0');
utils.anonymizeIp(':1d7e::c0fe').should.equal(':0:0:0'); expect(utils.anonymizeIp(':1d7e::c0fe')).toEqual(':0:0:0');
}); });
}); });
@@ -381,7 +383,7 @@ describe('Logger functionality', () => {
infoStream.write('first\n'); infoStream.write('first\n');
infoStream.write('part'); infoStream.write('part');
infoStream.write('ial\n'); infoStream.write('ial\n');
logs.should.deep.equal([ expect(logs).toEqual([
{ {
level: 'info', level: 'info',
msg: 'first', msg: 'first',
@@ -397,7 +399,7 @@ describe('Logger functionality', () => {
const fakeLog = {log: (level: string, msg: string) => logs.push({level, msg})} as any as winston.Logger; const fakeLog = {log: (level: string, msg: string) => logs.push({level, msg})} as any as winston.Logger;
const infoStream = makeLogStream('warn', fakeLog); const infoStream = makeLogStream('warn', fakeLog);
infoStream.write('ooh\n'); infoStream.write('ooh\n');
logs.should.deep.equal([ expect(logs).toEqual([
{ {
level: 'warn', level: 'warn',
msg: 'ooh', msg: 'ooh',
@@ -409,25 +411,25 @@ describe('Logger functionality', () => {
describe('Hash interface', () => { describe('Hash interface', () => {
it('correctly hashes strings', () => { it('correctly hashes strings', () => {
const version = 'Compiler Explorer Tests Version 0'; const version = 'Compiler Explorer Tests Version 0';
utils expect(utils.getHash('cream cheese', version)).toEqual(
.getHash('cream cheese', version) 'cfff2d1f7a213e314a67cce8399160ae884f794a3ee9d4a01cd37a8c22c67d94',
.should.equal('cfff2d1f7a213e314a67cce8399160ae884f794a3ee9d4a01cd37a8c22c67d94'); );
utils expect(utils.getHash('large eggs', version)).toEqual(
.getHash('large eggs', version) '9144dec50b8df5bc5cc24ba008823cafd6616faf2f268af84daf49ac1d24feb0',
.should.equal('9144dec50b8df5bc5cc24ba008823cafd6616faf2f268af84daf49ac1d24feb0'); );
utils expect(utils.getHash('sugar', version)).toEqual(
.getHash('sugar', version) 'afa3c89d0f6a61de6805314c9bd7c52d020425a3a3c7bbdfa7c0daec594e5ef1',
.should.equal('afa3c89d0f6a61de6805314c9bd7c52d020425a3a3c7bbdfa7c0daec594e5ef1'); );
}); });
it('correctly hashes objects', () => { it('correctly hashes objects', () => {
utils expect(
.getHash({ utils.getHash({
toppings: [ toppings: [
{name: 'raspberries', optional: false}, {name: 'raspberries', optional: false},
{name: 'ground cinnamon', optional: true}, {name: 'ground cinnamon', optional: true},
], ],
}) }),
.should.equal('e205d63abd5db363086621fdc62c4c23a51b733bac5855985a8b56642d570491'); ).toEqual('e205d63abd5db363086621fdc62c4c23a51b733bac5855985a8b56642d570491');
}); });
}); });
@@ -435,7 +437,7 @@ describe('GoldenLayout utils', () => {
it('finds every editor & compiler', async () => { it('finds every editor & compiler', async () => {
const state = await fs.readJson('test/example-states/default-state.json'); const state = await fs.readJson('test/example-states/default-state.json');
const contents = utils.glGetMainContents(state.content); const contents = utils.glGetMainContents(state.content);
contents.should.deep.equal({ expect(contents).toEqual({
editors: [ editors: [
{source: 'Editor 1', language: 'c++'}, {source: 'Editor 1', language: 'c++'},
{source: 'Editor 2', language: 'c++'}, {source: 'Editor 2', language: 'c++'},
@@ -455,60 +457,60 @@ describe('GoldenLayout utils', () => {
describe('squashes horizontal whitespace', () => { describe('squashes horizontal whitespace', () => {
it('handles empty input', () => { it('handles empty input', () => {
utils.squashHorizontalWhitespace('').should.equals(''); expect(utils.squashHorizontalWhitespace('')).toEqual('');
utils.squashHorizontalWhitespace(' ').should.equals(''); expect(utils.squashHorizontalWhitespace(' ')).toEqual('');
utils.squashHorizontalWhitespace(' ').should.equals(''); expect(utils.squashHorizontalWhitespace(' ')).toEqual('');
}); });
it('handles leading spaces', () => { it('handles leading spaces', () => {
utils.squashHorizontalWhitespace(' abc').should.equals(' abc'); expect(utils.squashHorizontalWhitespace(' abc')).toEqual(' abc');
utils.squashHorizontalWhitespace(' abc').should.equals(' abc'); expect(utils.squashHorizontalWhitespace(' abc')).toEqual(' abc');
utils.squashHorizontalWhitespace(' abc').should.equals(' abc'); expect(utils.squashHorizontalWhitespace(' abc')).toEqual(' abc');
}); });
it('handles interline spaces', () => { it('handles interline spaces', () => {
utils.squashHorizontalWhitespace('abc abc').should.equals('abc abc'); expect(utils.squashHorizontalWhitespace('abc abc')).toEqual('abc abc');
utils.squashHorizontalWhitespace('abc abc').should.equals('abc abc'); expect(utils.squashHorizontalWhitespace('abc abc')).toEqual('abc abc');
utils.squashHorizontalWhitespace('abc abc').should.equals('abc abc'); expect(utils.squashHorizontalWhitespace('abc abc')).toEqual('abc abc');
}); });
it('handles leading and interline spaces', () => { it('handles leading and interline spaces', () => {
utils.squashHorizontalWhitespace(' abc abc').should.equals(' abc abc'); expect(utils.squashHorizontalWhitespace(' abc abc')).toEqual(' abc abc');
utils.squashHorizontalWhitespace(' abc abc').should.equals(' abc abc'); expect(utils.squashHorizontalWhitespace(' abc abc')).toEqual(' abc abc');
utils.squashHorizontalWhitespace(' abc abc').should.equals(' abc abc'); expect(utils.squashHorizontalWhitespace(' abc abc')).toEqual(' abc abc');
utils.squashHorizontalWhitespace(' abc abc').should.equals(' abc abc'); expect(utils.squashHorizontalWhitespace(' abc abc')).toEqual(' abc abc');
}); });
}); });
describe('replaces all substrings', () => { describe('replaces all substrings', () => {
it('works with no substitutions', () => { it('works with no substitutions', () => {
const string = 'This is a line with no replacements'; const string = 'This is a line with no replacements';
utils.replaceAll(string, 'not present', "won't be substituted").should.equal(string); expect(utils.replaceAll(string, 'not present', "won't be substituted")).toEqual(string);
}); });
it('handles odd cases', () => { it('handles odd cases', () => {
utils.replaceAll('', '', '').should.equal(''); expect(utils.replaceAll('', '', '')).toEqual('');
utils.replaceAll('Hello', '', '').should.equal('Hello'); expect(utils.replaceAll('Hello', '', '')).toEqual('Hello');
}); });
it('works with single replacement', () => { it('works with single replacement', () => {
utils expect(utils.replaceAll('This is a line with a mistook in it', 'mistook', 'mistake')).toEqual(
.replaceAll('This is a line with a mistook in it', 'mistook', 'mistake') 'This is a line with a mistake in it',
.should.equal('This is a line with a mistake in it'); );
utils expect(utils.replaceAll('This is a line with a mistook', 'mistook', 'mistake')).toEqual(
.replaceAll('This is a line with a mistook', 'mistook', 'mistake') 'This is a line with a mistake',
.should.equal('This is a line with a mistake'); );
utils.replaceAll('Mistooks were made', 'Mistooks', 'Mistakes').should.equal('Mistakes were made'); expect(utils.replaceAll('Mistooks were made', 'Mistooks', 'Mistakes')).toEqual('Mistakes were made');
}); });
it('works with multiple replacements', () => { it('works with multiple replacements', () => {
utils.replaceAll('A mistook is a mistook', 'mistook', 'mistake').should.equal('A mistake is a mistake'); expect(utils.replaceAll('A mistook is a mistook', 'mistook', 'mistake')).toEqual('A mistake is a mistake');
utils.replaceAll('aaaaaaaaaaaaaaaaaaaaaaaaaaa', 'a', 'b').should.equal('bbbbbbbbbbbbbbbbbbbbbbbbbbb'); expect(utils.replaceAll('aaaaaaaaaaaaaaaaaaaaaaaaaaa', 'a', 'b')).toEqual('bbbbbbbbbbbbbbbbbbbbbbbbbbb');
}); });
it('works with overlapping replacements', () => { it('works with overlapping replacements', () => {
utils.replaceAll('aaaaaaaa', 'a', 'ba').should.equal('babababababababa'); expect(utils.replaceAll('aaaaaaaa', 'a', 'ba')).toEqual('babababababababa');
}); });
}); });
describe('encodes in our version of base32', () => { describe('encodes in our version of base32', () => {
function doTest(original, expected) { function doTest(original, expected) {
utils.base32Encode(Buffer.from(original)).should.equal(expected); expect(utils.base32Encode(Buffer.from(original))).toEqual(expected);
} }
// Done by hand to check that they are valid // Done by hand to check that they are valid
@@ -545,72 +547,76 @@ describe('encodes in our version of base32', () => {
describe('fileExists', () => { describe('fileExists', () => {
it('Returns true for files that exists', async () => { it('Returns true for files that exists', async () => {
(await utils.fileExists(fileURLToPath(import.meta.url))).should.be.true; await expect(utils.fileExists(fileURLToPath(import.meta.url))).resolves.toBe(true);
}); });
it("Returns false for files that don't exist", async () => { it("Returns false for files that don't exist", async () => {
(await utils.fileExists('./ABC-FileThatDoesNotExist.extension')).should.be.false; await expect(utils.fileExists('./ABC-FileThatDoesNotExist.extension')).resolves.toBe(false);
}); });
it('Returns false for directories that exist', async () => { it('Returns false for directories that exist', async () => {
(await utils.fileExists(path.resolve(path.dirname(fileURLToPath(import.meta.url))))).should.be.false; await expect(utils.fileExists(path.resolve(path.dirname(fileURLToPath(import.meta.url))))).resolves.toBe(false);
}); });
}); });
describe('safe semver', () => { describe('safe semver', () => {
it('should understand most kinds of semvers', () => { it('should understand most kinds of semvers', () => {
utils.asSafeVer('0').should.equal('0.0.0'); expect(utils.asSafeVer('0')).toEqual('0.0.0');
utils.asSafeVer('1').should.equal('1.0.0'); expect(utils.asSafeVer('1')).toEqual('1.0.0');
utils.asSafeVer('1.0').should.equal('1.0.0'); expect(utils.asSafeVer('1.0')).toEqual('1.0.0');
utils.asSafeVer('1.1').should.equal('1.1.0'); expect(utils.asSafeVer('1.1')).toEqual('1.1.0');
utils.asSafeVer('1.1.0').should.equal('1.1.0'); expect(utils.asSafeVer('1.1.0')).toEqual('1.1.0');
utils.asSafeVer('1.1.1').should.equal('1.1.1'); expect(utils.asSafeVer('1.1.1')).toEqual('1.1.1');
utils.asSafeVer('trunk').should.equal(utils.magic_semver.trunk); expect(utils.asSafeVer('trunk')).toEqual(utils.magic_semver.trunk);
utils.asSafeVer('(trunk)').should.equal(utils.magic_semver.trunk); expect(utils.asSafeVer('(trunk)')).toEqual(utils.magic_semver.trunk);
utils.asSafeVer('(123.456.789 test)').should.equal(utils.magic_semver.non_trunk); expect(utils.asSafeVer('(123.456.789 test)')).toEqual(utils.magic_semver.non_trunk);
utils.asSafeVer('0..0').should.equal(utils.magic_semver.non_trunk); expect(utils.asSafeVer('0..0')).toEqual(utils.magic_semver.non_trunk);
utils.asSafeVer('0.0.').should.equal(utils.magic_semver.non_trunk); expect(utils.asSafeVer('0.0.')).toEqual(utils.magic_semver.non_trunk);
utils.asSafeVer('0.').should.equal(utils.magic_semver.non_trunk); expect(utils.asSafeVer('0.')).toEqual(utils.magic_semver.non_trunk);
utils.asSafeVer('.0.0').should.equal(utils.magic_semver.non_trunk); expect(utils.asSafeVer('.0.0')).toEqual(utils.magic_semver.non_trunk);
utils.asSafeVer('.0..').should.equal(utils.magic_semver.non_trunk); expect(utils.asSafeVer('.0..')).toEqual(utils.magic_semver.non_trunk);
utils.asSafeVer('0..').should.equal(utils.magic_semver.non_trunk); expect(utils.asSafeVer('0..')).toEqual(utils.magic_semver.non_trunk);
utils.asSafeVer('123 TEXT').should.equal('123.0.0'); expect(utils.asSafeVer('123 TEXT')).toEqual('123.0.0');
utils.asSafeVer('123.456 TEXT').should.equal('123.456.0'); expect(utils.asSafeVer('123.456 TEXT')).toEqual('123.456.0');
utils.asSafeVer('123.456.789 TEXT').should.equal('123.456.789'); expect(utils.asSafeVer('123.456.789 TEXT')).toEqual('123.456.789');
}); });
}); });
describe('argument splitting', () => { describe('argument splitting', () => {
it('should handle normal things', () => { it('should handle normal things', () => {
utils expect(utils.splitArguments('-hello --world etc --std=c++20')).toEqual([
.splitArguments('-hello --world etc --std=c++20') '-hello',
.should.deep.equal(['-hello', '--world', 'etc', '--std=c++20']); '--world',
'etc',
'--std=c++20',
]);
}); });
it('should handle hash chars', () => { it('should handle hash chars', () => {
utils expect(utils.splitArguments('-Wno#warnings -Wno-#pragma-messages')).toEqual([
.splitArguments('-Wno#warnings -Wno-#pragma-messages') '-Wno#warnings',
.should.deep.equal(['-Wno#warnings', '-Wno-#pragma-messages']); '-Wno-#pragma-messages',
]);
}); });
it('should handle doublequoted args', () => { it('should handle doublequoted args', () => {
utils.splitArguments('--hello "-world etc"').should.deep.equal(['--hello', '-world etc']); expect(utils.splitArguments('--hello "-world etc"')).toEqual(['--hello', '-world etc']);
}); });
it('should handle singlequoted args', () => { it('should handle singlequoted args', () => {
utils.splitArguments("--hello '-world etc'").should.deep.equal(['--hello', '-world etc']); expect(utils.splitArguments("--hello '-world etc'")).toEqual(['--hello', '-world etc']);
}); });
it('should handle cheekyness part 1', () => { it('should handle cheekyness part 1', () => {
/* eslint-disable no-useless-escape */ /* eslint-disable no-useless-escape */
utils.splitArguments('hello #veryfancy etc').should.deep.equal(['hello', '#veryfancy', 'etc']); expect(utils.splitArguments('hello #veryfancy etc')).toEqual(['hello', '#veryfancy', 'etc']);
/* eslint-enable no-useless-escape */ /* eslint-enable no-useless-escape */
}); });
it('should handle cheekyness part 2', () => { it('should handle cheekyness part 2', () => {
utils.splitArguments('hello \\#veryfancy etc').should.deep.equal(['hello', '\\']); expect(utils.splitArguments('hello \\#veryfancy etc')).toEqual(['hello', '\\']);
}); });
}); });

View File

@@ -26,15 +26,16 @@ import os from 'os';
import path from 'path'; import path from 'path';
import {fileURLToPath} from 'url'; import {fileURLToPath} from 'url';
import chai from 'chai';
import fs from 'fs-extra'; import fs from 'fs-extra';
import temp from 'temp'; import temp from 'temp';
import {expect} from 'vitest';
import {CompilationEnvironment} from '../lib/compilation-env.js'; import {CompilationEnvironment} from '../lib/compilation-env.js';
import {CompilationQueue} from '../lib/compilation-queue.js'; import {CompilationQueue} from '../lib/compilation-queue.js';
import {CompilerProps, fakeProps} from '../lib/properties.js'; import {CompilerProps, fakeProps} from '../lib/properties.js';
import {CompilerInfo} from '../types/compiler.interfaces.js'; import {CompilerInfo} from '../types/compiler.interfaces.js';
import {ParseFiltersAndOutputOptions} from '../types/features/filters.interfaces.js'; import {ParseFiltersAndOutputOptions} from '../types/features/filters.interfaces.js';
import {Language} from '../types/languages.interfaces.js';
// TODO: Find proper type for options // TODO: Find proper type for options
export function makeCompilationEnvironment(options: Record<string, any>): CompilationEnvironment { export function makeCompilationEnvironment(options: Record<string, any>): CompilationEnvironment {
@@ -47,14 +48,16 @@ export function makeFakeCompilerInfo(props: Partial<CompilerInfo>): CompilerInfo
return props as CompilerInfo; return props as CompilerInfo;
} }
export function makeFakeLanguage(props: Partial<Language>): Language {
return props as Language;
}
export function makeFakeParseFiltersAndOutputOptions( export function makeFakeParseFiltersAndOutputOptions(
options: Partial<ParseFiltersAndOutputOptions>, options: Partial<ParseFiltersAndOutputOptions>,
): ParseFiltersAndOutputOptions { ): ParseFiltersAndOutputOptions {
return options as ParseFiltersAndOutputOptions; return options as ParseFiltersAndOutputOptions;
} }
export const should = chai.should();
// This combines a should assert and a type guard // This combines a should assert and a type guard
// Example: // Example:
// //
@@ -66,7 +69,8 @@ export const should = chai.should();
// a = null; // a = null;
// shouldExist(a); /* throws should.exist assertion // shouldExist(a); /* throws should.exist assertion
export function shouldExist<T>(value: T, message?: string): value is Exclude<T, null | undefined> { export function shouldExist<T>(value: T, message?: string): value is Exclude<T, null | undefined> {
should.exist(value, message); // TODO: if the message is set we should have a proper message here; since the move to vitest we lost it.
expect(value).toEqual(expect.anything());
return true; return true;
} }
@@ -86,4 +90,4 @@ export function newTempDir() {
} }
// eslint-disable-next-line -- do not rewrite exports // eslint-disable-next-line -- do not rewrite exports
export {chai, path, fs}; export {path, fs};

View File

@@ -24,6 +24,8 @@
import child_process from 'child_process'; import child_process from 'child_process';
import {beforeAll, describe, expect, it} from 'vitest';
import {WineVcCompiler} from '../lib/compilers/wine-vc.js'; import {WineVcCompiler} from '../lib/compilers/wine-vc.js';
import {WslVcCompiler} from '../lib/compilers/wsl-vc.js'; import {WslVcCompiler} from '../lib/compilers/wsl-vc.js';
import {LanguageKey} from '../types/languages.interfaces.js'; import {LanguageKey} from '../types/languages.interfaces.js';
@@ -47,18 +49,18 @@ const info = {
describe('Paths', () => { describe('Paths', () => {
let env; let env;
before(() => { beforeAll(() => {
env = makeCompilationEnvironment({languages}); env = makeCompilationEnvironment({languages});
}); });
it('Linux -> Wine path', () => { it('Linux -> Wine path', () => {
const compiler = new WineVcCompiler(makeFakeCompilerInfo(info), env); const compiler = new WineVcCompiler(makeFakeCompilerInfo(info), env);
compiler.filename('/tmp/123456/output.s').should.equal('Z:/tmp/123456/output.s'); expect(compiler.filename('/tmp/123456/output.s')).toEqual('Z:/tmp/123456/output.s');
}); });
it('Linux -> Windows path', function () { it('Linux -> Windows path', () => {
const compiler = new WslVcCompiler(makeFakeCompilerInfo(info), env); const compiler = new WslVcCompiler(makeFakeCompilerInfo(info), env);
compiler.filename('/mnt/c/tmp/123456/output.s', '/mnt/c/tmp').should.equal('c:/tmp/123456/output.s'); expect(compiler.filename('/mnt/c/tmp/123456/output.s', '/mnt/c/tmp')).toEqual('c:/tmp/123456/output.s');
}); });
}); });
@@ -89,7 +91,7 @@ if (process.platform === 'linux' && child_process.execSync('uname -a').toString(
describe('Wsl compiler', () => { describe('Wsl compiler', () => {
let compiler; let compiler;
before(() => { beforeAll(() => {
compiler = createCompiler(WslVcCompiler); compiler = createCompiler(WslVcCompiler);
}); });

View File

@@ -11,7 +11,6 @@
/* Code generation */ /* Code generation */
"outDir": "./out/dist", "outDir": "./out/dist",
"typeRoots": ["./node_modules/@types"], "typeRoots": ["./node_modules/@types"],
"types": ["mocha", "chai"],
/* Other options */ /* Other options */
"allowJs": true "allowJs": true
} }

11
vitest.config.ts Normal file
View File

@@ -0,0 +1,11 @@
// eslint-disable-next-line node/no-unpublished-import
import {defineConfig} from 'vitest/config';
// eslint-disable-next-line import/no-default-export
export default defineConfig({
test: {
include: ['test/**/*.ts'],
exclude: ['test/_*.ts', 'test/utils.ts'],
setupFiles: ['/test/_setup-fake-aws.ts', '/test/_setup-log.ts'],
},
});