UNPKG

js-slang

Version:

Javascript-based implementations of Source, written in Typescript

289 lines 12.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.expectFinishedResult = exports.asMockedFunc = exports.expectNativeToTimeoutAndError = exports.expectToLooselyMatchJS = exports.expectToMatchJS = exports.expectParsedErrorNoSnapshot = exports.expectWarning = exports.expectDifferentParsedErrors = exports.expectParsedError = exports.expectParsedErrorNoErrorSnapshot = exports.expectResult = exports.getDisplayResult = exports.expectVisualiseListResult = exports.expectDisplayResult = exports.snapshotFailure = exports.snapshotWarning = exports.snapshotSuccess = exports.snapshot = exports.testFailure = exports.testSuccessWithErrors = exports.testSuccess = exports.createTestContext = void 0; const astring_1 = require("astring"); const createContext_1 = require("../createContext"); const gpu_1 = require("../gpu/gpu"); const index_1 = require("../index"); const lazy_1 = require("../lazy/lazy"); const context_1 = require("../mocks/context"); const parser_1 = require("../parser/parser"); const transpiler_1 = require("../transpiler/transpiler"); const types_1 = require("../types"); const stringify_1 = require("./stringify"); function createTestContext({ context, chapter = types_1.Chapter.SOURCE_1, variant = types_1.Variant.DEFAULT, testBuiltins = {} } = {}) { if (context !== undefined) { return context; } else { const testContext = { ...(0, createContext_1.default)(chapter, variant, [], undefined, { rawDisplay: (str1, str2, _externalContext) => { testContext.displayResult.push((str2 === undefined ? '' : str2 + ' ') + str1); return str1; }, prompt: (str, _externalContext) => { testContext.promptResult.push(str); return null; }, alert: (str, _externalContext) => { testContext.alertResult.push(str); }, visualiseList: value => { testContext.visualiseListResult.push(value); } }), displayResult: [], promptResult: [], alertResult: [], visualiseListResult: [] }; Object.entries(testBuiltins).forEach(([key, value]) => (0, createContext_1.defineBuiltin)(testContext, key, value)); return testContext; } } exports.createTestContext = createTestContext; async function testInContext(code, options) { const interpretedTestContext = createTestContext(options); const scheduler = 'preemptive'; const getTestResult = (context, result) => { const testResult = { code, displayResult: context.displayResult, alertResult: context.alertResult, visualiseListResult: context.visualiseListResult, numErrors: context.errors.length, parsedErrors: (0, index_1.parseError)(context.errors), resultStatus: result.status, result: result.status === 'finished' ? result.value : undefined }; if (options.showErrorJSON) { testResult['errors'] = context.errors; } return testResult; }; const interpretedResult = getTestResult(interpretedTestContext, await (0, index_1.runInContext)(code, interpretedTestContext, { scheduler, executionMethod: 'interpreter', variant: options.variant })); if (options.native) { const nativeTestContext = createTestContext(options); let pretranspiled = ''; let transpiled = ''; const parsed = (0, parser_1.parse)(code, nativeTestContext); // Reset errors in context so as not to interfere with actual run. nativeTestContext.errors = []; if (parsed === undefined) { pretranspiled = 'parseError'; } else { // Mutates program switch (options.variant) { case types_1.Variant.GPU: (0, gpu_1.transpileToGPU)(parsed); pretranspiled = (0, astring_1.generate)(parsed); break; case types_1.Variant.LAZY: (0, lazy_1.transpileToLazy)(parsed); pretranspiled = (0, astring_1.generate)(parsed); break; } try { ; ({ transpiled } = (0, transpiler_1.transpile)(parsed, nativeTestContext)); // replace declaration of builtins since they're repetitive transpiled = transpiled.replace(/\n const \w+ = nativeStorage\..*;/g, ''); transpiled = transpiled.replace(/\n\s*const \w+ = .*\.operators\..*;/g, ''); } catch { transpiled = 'parseError'; } } const nativeResult = getTestResult(nativeTestContext, await (0, index_1.runInContext)(code, nativeTestContext, { scheduler, executionMethod: 'native', variant: options.variant })); const propertiesThatShouldBeEqual = [ 'code', 'displayResult', 'alertResult', 'parsedErrors', 'result' ]; const diff = {}; for (const property of propertiesThatShouldBeEqual) { const nativeValue = (0, stringify_1.stringify)(nativeResult[property]); const interpretedValue = (0, stringify_1.stringify)(interpretedResult[property]); if (nativeValue !== interpretedValue) { diff[property] = `native:${nativeValue}\ninterpreted:${interpretedValue}`; } } if (options.showTranspiledCode) { return { ...interpretedResult, ...diff, pretranspiled, transpiled }; } else { return { ...interpretedResult, ...diff }; } } else { return interpretedResult; } } async function testSuccess(code, options = { native: false }) { const testResult = await testInContext(code, options); expect(testResult.parsedErrors).toBe(''); expect(testResult.resultStatus).toBe('finished'); return testResult; } exports.testSuccess = testSuccess; async function testSuccessWithErrors(code, options = { native: false }) { const testResult = await testInContext(code, options); expect(testResult.numErrors).not.toEqual(0); expect(testResult.resultStatus).toBe('finished'); return testResult; } exports.testSuccessWithErrors = testSuccessWithErrors; async function testFailure(code, options = { native: false }) { const testResult = await testInContext(code, options); expect(testResult.numErrors).not.toEqual(0); expect(testResult.resultStatus).toBe('error'); return testResult; } exports.testFailure = testFailure; function snapshot(arg1, arg2) { if (arg2) { return testResult => { expect(testResult).toMatchSnapshot(arg1, arg2); return testResult; }; } else if (arg1) { return testResult => { expect(testResult).toMatchSnapshot(arg1); return testResult; }; } else { return testResult => { return testResult; }; } } exports.snapshot = snapshot; function snapshotSuccess(code, options, snapshotName) { return testSuccess(code, options).then(snapshot(snapshotName)); } exports.snapshotSuccess = snapshotSuccess; function snapshotWarning(code, options, snapshotName) { return testSuccessWithErrors(code, options).then(snapshot(snapshotName)); } exports.snapshotWarning = snapshotWarning; function snapshotFailure(code, options, snapshotName) { return testFailure(code, options).then(snapshot(snapshotName)); } exports.snapshotFailure = snapshotFailure; function expectDisplayResult(code, options = {}) { return expect(testSuccess(code, options) .then(snapshot('expectDisplayResult')) .then(testResult => testResult.displayResult) .catch(e => console.log(e))).resolves; } exports.expectDisplayResult = expectDisplayResult; function expectVisualiseListResult(code, options = {}) { return expect(testSuccess(code, options) .then(snapshot('expectVisualiseListResult')) .then(testResult => testResult.visualiseListResult) .catch(e => console.log(e))).resolves; } exports.expectVisualiseListResult = expectVisualiseListResult; // for use in concurrent testing async function getDisplayResult(code, options = {}) { return await testSuccess(code, options).then(testResult => testResult.displayResult); } exports.getDisplayResult = getDisplayResult; function expectResult(code, options = {}) { return expect(testSuccess(code, options) .then(snapshot('expectResult')) .then(testResult => testResult.result)).resolves; } exports.expectResult = expectResult; function expectParsedErrorNoErrorSnapshot(code, options = {}) { options.showErrorJSON = false; return expect(testFailure(code, options) .then(snapshot('expectParsedErrorNoErrorSnapshot')) .then(testResult => testResult.parsedErrors)).resolves; } exports.expectParsedErrorNoErrorSnapshot = expectParsedErrorNoErrorSnapshot; function expectParsedError(code, options = {}) { return expect(testFailure(code, options) .then(snapshot('expectParsedError')) .then(testResult => testResult.parsedErrors)).resolves; } exports.expectParsedError = expectParsedError; function expectDifferentParsedErrors(code1, code2, options = {}) { return expect(testFailure(code1, options).then(error1 => { expect(testFailure(code2, options).then(error2 => { return expect(error1).not.toEqual(error2); })); })).resolves; } exports.expectDifferentParsedErrors = expectDifferentParsedErrors; function expectWarning(code, options = {}) { return expect(testSuccessWithErrors(code, options) .then(snapshot('expectWarning')) .then(testResult => testResult.parsedErrors)).resolves; } exports.expectWarning = expectWarning; function expectParsedErrorNoSnapshot(code, options = {}) { return expect(testFailure(code, options).then(testResult => testResult.parsedErrors)).resolves; } exports.expectParsedErrorNoSnapshot = expectParsedErrorNoSnapshot; function evalWithBuiltins(code, testBuiltins = {}) { // Ugly, but if you know how to `eval` code with some builtins attached, please change this. let evalstring = ''; for (const key in testBuiltins) { if (testBuiltins.hasOwnProperty(key)) { evalstring = evalstring + 'const ' + key + ' = testBuiltins.' + key + '; '; } } // tslint:disable-next-line:no-eval return eval(evalstring + code); } function expectToMatchJS(code, options = {}) { return testSuccess(code, options) .then(snapshot('expect to match JS')) .then(testResult => expect(testResult.result).toEqual(evalWithBuiltins(code, options.testBuiltins))); } exports.expectToMatchJS = expectToMatchJS; function expectToLooselyMatchJS(code, options = {}) { return testSuccess(code, options) .then(snapshot('expect to loosely match JS')) .then(testResult => expect(testResult.result.replace(/ /g, '')).toEqual(evalWithBuiltins(code, options.testBuiltins).replace(/ /g, ''))); } exports.expectToLooselyMatchJS = expectToLooselyMatchJS; async function expectNativeToTimeoutAndError(code, timeout) { const start = Date.now(); const context = (0, context_1.mockContext)(types_1.Chapter.SOURCE_4); const promise = (0, index_1.runInContext)(code, context, { scheduler: 'preemptive', executionMethod: 'native', throwInfiniteLoops: false }); await promise; const timeTaken = Date.now() - start; expect(timeTaken).toBeLessThan(timeout * 5); expect(timeTaken).toBeGreaterThanOrEqual(timeout); return (0, index_1.parseError)(context.errors); } exports.expectNativeToTimeoutAndError = expectNativeToTimeoutAndError; function asMockedFunc(func) { return func; } exports.asMockedFunc = asMockedFunc; function expectFinishedResult(result) { expect(result.status).toEqual('finished'); } exports.expectFinishedResult = expectFinishedResult; //# sourceMappingURL=testing.js.map