Skip to content

Commit

Permalink
Merge pull request #505 from takikawa/add-spec-tests
Browse files Browse the repository at this point in the history
Add source map spec tests
  • Loading branch information
bomsy authored Jun 9, 2024
2 parents 43819cc + d243a04 commit 3cb92cc
Show file tree
Hide file tree
Showing 4 changed files with 214 additions and 2 deletions.
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "test/source-map-tests"]
path = test/source-map-tests
url = https://github.com/tc39/source-map-tests.git
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,8 @@
},
"license": "BSD-3-Clause",
"scripts": {
"lint": "eslint --fix *.js lib/ test/",
"test": "node test/run-tests.js",
"lint": "eslint --fix *.js lib/ test/ --ignore-pattern 'test/source-map-tests/**'",
"test": "git submodule update --init --recursive; node test/run-tests.js",
"coverage": "c8 --reporter=text --reporter=html npm test",
"prettier": "prettier --write .",
"clean": "rm -rf coverage",
Expand Down
1 change: 1 addition & 0 deletions test/source-map-tests
Submodule source-map-tests added at 14c897
208 changes: 208 additions & 0 deletions test/test-spec-tests.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
/* -*- Mode: js; js-indent-level: 2; -*- */
/*
* Copyright 2024 Mozilla Foundation and contributors
* Licensed under the New BSD license. See LICENSE or:
* http://opensource.org/licenses/BSD-3-Clause
*/

const fs = require("fs").promises;
const SourceMapConsumer =
require("../lib/source-map-consumer").SourceMapConsumer;

const sourceMapSpecTests = require("./source-map-tests/source-map-spec-tests.json");

async function readJSON(path) {
const file = await fs.open(require.resolve(path));
const json = JSON.parse(await file.readFile());
file.close();
return json;
}

// Known failures due to intentional implementation choices or due to bugs.
const skippedTests = [
// Versions are explicitly checked a bit loosely.
"versionNumericString",
// Stricter sources array checking isn't implemented.
"sourcesNotStringOrNull",
"sourcesAndSourcesContentBothNull",
// Stricter names array checking isn't implemented.
"namesMissing",
"namesNotString",
// This check isn't as strict in this library.
"invalidMappingNotAString1",
// A mapping segment with no fields is technically invalid in the spec.
"invalidMappingSegmentWithZeroFields",
// These tests fail due to imprecision in the spec about the 32-bit limit.
"invalidMappingSegmentWithColumnExceeding32Bits",
"invalidMappingSegmentWithOriginalLineExceeding32Bits",
"invalidMappingSegmentWithOriginalColumnExceeding32Bits",
// A large VLQ that should parse, but currently does not.
"validMappingLargeVLQ",
// The library currently doesn't check the types of offset lines/columns.
"indexMapOffsetLineWrongType",
"indexMapOffsetColumnWrongType",
// The spec is not totally clear about this case.
"indexMapInvalidBaseMappings",
// The spec's definition of overlap can be refined
"indexMapInvalidOverlap",
// The library doesn't support the new ignoreList feature yet.
"ignoreListWrongType1",
"ignoreListWrongType2",
"ignoreListWrongType3",
"ignoreListOutOfBounds",
];

// The source-map library converts null sources to the "null" URL in its
// sources list, so for equality checking we accept this as null.
function nullish(nullOrString) {
if (nullOrString === "null") {
return null;
}
return nullOrString;
}

function mapLine(line) {
return line + 1;
}

async function testMappingAction(assert, rawSourceMap, action) {
return SourceMapConsumer.with(rawSourceMap, null, consumer => {
let mappedPosition = consumer.originalPositionFor({
line: mapLine(action.generatedLine),
column: action.generatedColumn,
});

assert.equal(
mappedPosition.line,
mapLine(action.originalLine),
`original line didn't match, expected ${mapLine(
action.originalLine
)} got ${mappedPosition.line}`
);
assert.equal(
mappedPosition.column,
action.originalColumn,
`original column didn't match, expected ${action.originalColumn} got ${mappedPosition.column}`
);
assert.equal(
nullish(mappedPosition.source),
action.originalSource,
`original source didn't match, expected ${action.originalSource} got ${mappedPosition.source}`
);
if (action.mappedName) {
assert.equal(
mappedPosition.name,
action.mappedName,
`mapped name didn't match, expected ${action.mappedName} got ${mappedPosition.name}`
);
}

// When the source is null, a reverse lookup may not make sense
// because there isn't a unique way to look it up.
if (action.originalSource !== null) {
mappedPosition = consumer.generatedPositionFor({
source: action.originalSource,
line: mapLine(action.originalLine),
column: action.originalColumn,
});

assert.equal(
mappedPosition.line,
mapLine(action.generatedLine),
`generated line didn't match, expected ${mapLine(
action.generatedLine
)} got ${mappedPosition.line}`
);
assert.equal(
mappedPosition.column,
action.generatedColumn,
`generated column didn't match, expected ${action.generatedColumn} got ${mappedPosition.column}`
);
}
});
}

async function testTransitiveMappingAction(assert, rawSourceMap, action) {
return SourceMapConsumer.with(rawSourceMap, null, async consumer => {
assert.ok(
Array.isArray(action.intermediateMaps),
"transitive mapping case requires intermediate maps"
);

let mappedPosition = consumer.originalPositionFor({
line: mapLine(action.generatedLine),
column: action.generatedColumn,
});

for (const intermediateMapPath of action.intermediateMaps) {
const intermediateMap = await readJSON(
`./source-map-tests/resources/${intermediateMapPath}`
);
await SourceMapConsumer.with(
intermediateMap,
null,
consumerIntermediate => {
mappedPosition = consumerIntermediate.originalPositionFor({
line: mappedPosition.line,
column: mappedPosition.column,
});
}
);
}

assert.equal(
mappedPosition.line,
mapLine(action.originalLine),
`original line didn't match, expected ${mapLine(
action.originalLine
)} got ${mappedPosition.line}`
);
assert.equal(
mappedPosition.column,
action.originalColumn,
`original column didn't match, expected ${action.originalColumn} got ${mappedPosition.column}`
);
assert.equal(
mappedPosition.source,
action.originalSource,
`original source didn't match, expected ${action.originalSource} got ${mappedPosition.source}`
);
});
}

for (const testCase of sourceMapSpecTests.tests) {
if (skippedTests.includes(testCase.name)) {
continue;
}
exports[`test from source map spec tests, name: ${testCase.name}`] =
async function (assert) {
const json = await readJSON(
`./source-map-tests/resources/${testCase.sourceMapFile}`
);
try {
const map = await new SourceMapConsumer(json);
map.eachMapping(() => {});
map.destroy();
} catch (exn) {
if (testCase.sourceMapIsValid) {
assert.fail(
"Expected valid source map but failed to load successfully: " +
exn.message
);
}
return;
}
if (!testCase.sourceMapIsValid) {
assert.fail("Expected invalid source map but loaded successfully");
}
if (testCase.testActions) {
for (const testAction of testCase.testActions) {
if (testAction.actionType == "checkMapping") {
await testMappingAction(assert, json, testAction);
} else if (testAction.actionType == "checkMappingTransitive") {
await testTransitiveMappingAction(assert, json, testAction);
}
}
}
};
}

0 comments on commit 3cb92cc

Please sign in to comment.