Add tests for source map generator

Summary: This adds test for our high-perf source map generator.

Reviewed By: cpojer

Differential Revision: D4455547

fbshipit-source-id: 293bfe09c9c020259ae3daa4fa52f829248cca3c
This commit is contained in:
David Aurelio 2017-01-24 08:49:41 -08:00 committed by Facebook Github Bot
parent 7eef41bc00
commit f6e068bcf0
4 changed files with 327 additions and 0 deletions

View File

@ -45,6 +45,9 @@ class B64Builder {
* Adds `n` markers for generated lines to the mappings. * Adds `n` markers for generated lines to the mappings.
*/ */
markLines(n: number) { markLines(n: number) {
if (n < 1) {
return this;
}
this.hasSegment = false; this.hasSegment = false;
if (this.pos + n >= this.buffer.length) { if (this.pos + n >= this.buffer.length) {
this._realloc(); this._realloc();

View File

@ -0,0 +1,126 @@
/**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
'use strict';
jest.disableAutomock();
const B64Builder = require('../B64Builder');
let builder;
beforeEach(() => {
builder = new B64Builder();
});
it('exposes a fluent interface', () => {
expect(builder.markLines(0)).toBe(builder);
expect(builder.markLines(3)).toBe(builder);
expect(builder.startSegment()).toBe(builder);
expect(builder.append(4)).toBe(builder);
});
it('can create an empty string', () => {
expect(builder.toString()).toEqual('');
});
it('can mark a new line in the generated code', () => {
builder.markLines(1);
expect(builder.toString()).toEqual(';');
});
it('can mark multiple new lines in the generated code', () => {
builder.markLines(4);
expect(builder.toString()).toEqual(';;;;');
});
it('can mark zero new lines in the generated code', () => {
builder.markLines(0);
expect(builder.toString()).toEqual('');
});
it('does not add commas when just starting a segment', () => {
builder.startSegment(0);
expect(builder.toString()).toEqual('A');
});
it('adds a comma when starting a segment after another segment', () => {
builder.startSegment(0);
builder.startSegment(1);
expect(builder.toString()).toEqual('A,C');
});
it('does not add a comma when starting a segment after marking a line', () => {
builder.startSegment(0);
builder.markLines(1);
builder.startSegment(0);
expect(builder.toString()).toEqual('A;A');
});
it('adds a comma when starting a segment after calling `markLines(0)`', () => {
builder.startSegment(0);
builder.markLines(0);
builder.startSegment(1);
expect(builder.toString()).toEqual('A,C');
});
it('can append values that fit within 5 bits (including sign bit)', () => {
builder.append(0b1111);
builder.append(-0b1111);
expect(builder.toString()).toEqual('ef');
});
it('can append values that fit within 10 bits (including sign bit)', () => {
builder.append(0b111100110);
builder.append(-0b110110011);
expect(builder.toString()).toEqual('senb');
});
it('can append values that fit within 15 bits (including sign bit)', () => {
builder.append(0b10011111011001);
builder.append(-0b11001010001001);
expect(builder.toString()).toEqual('y9TzoZ');
});
it('can append values that fit within 20 bits (including sign bit)', () => {
builder.append(0b1110010011101110110);
builder.append(-0b1011000010100100110);
expect(builder.toString()).toEqual('s3zctyiW');
});
it('can append values that fit within 25 bits (including sign bit)', () => {
builder.append(0b100010001111011010110111);
builder.append(-0b100100111100001110101111);
expect(builder.toString()).toEqual('ur7jR/6hvS');
});
it('can append values that fit within 30 bits (including sign bit)', () => {
builder.append(0b10001100100001101010001011111);
builder.append(-0b11111000011000111110011111101);
expect(builder.toString()).toEqual('+lqjyR7v+xhf');
});
it('can append values that fit within 32 bits (including sign bit)', () => {
builder.append(0b1001100101000101001011111110011);
builder.append(-0b1101101101011000110011001110000);
expect(builder.toString()).toEqual('m/rq0sChnzx1tD');
});
it('can handle multiple operations', () => {
builder
.markLines(3)
.startSegment(4)
.append(2)
.append(2)
.append(0)
.append(2345)
.startSegment(12)
.append(987543)
.markLines(1)
.startSegment(0);
expect(builder.toString()).toEqual(';;;IEEAyyE,Yu5o8B;A');
});

View File

@ -0,0 +1,113 @@
/**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
'use strict';
jest.disableAutomock();
const Generator = require('../Generator');
const {objectContaining} = expect;
let generator;
beforeEach(() => {
generator = new Generator();
});
it('adds file name and source code when starting a file', () => {
const file1 = 'just/a/file';
const file2 = 'another/file';
const source1 = 'var a = 1;';
const source2 = 'var a = 2;';
generator.startFile(file1, source1);
generator.startFile(file2, source2);
expect(generator.toMap())
.toEqual(objectContaining({
sources: [file1, file2],
sourcesContent: [source1, source2],
}));
});
it('throws when adding a mapping without starting a file', () => {
expect(() => generator.addSimpleMapping(1, 2)).toThrow();
});
it('throws when adding a mapping after ending a file', () => {
generator.startFile('apples', 'pears');
generator.endFile();
expect(() => generator.addSimpleMapping(1, 2)).toThrow();
});
it('can add a mapping for generated code without corresponding original source', () => {
generator.startFile('apples', 'pears');
generator.addSimpleMapping(12, 87);
expect(generator.toMap())
.toEqual(objectContaining({
mappings: ';;;;;;;;;;;uF',
}));
});
it('can add a mapping with corresponding location in the original source', () => {
generator.startFile('apples', 'pears');
generator.addSourceMapping(2, 3, 456, 7);
expect(generator.toMap())
.toEqual(objectContaining({
mappings: ';GAucO',
}));
});
it('can add a mapping with source location and symbol name', () => {
generator.startFile('apples', 'pears');
generator.addNamedSourceMapping(9, 876, 54, 3, 'arbitrary');
expect(generator.toMap())
.toEqual(objectContaining({
mappings: ';;;;;;;;42BAqDGA',
names: ['arbitrary'],
}));
});
describe('full map generation', () => {
beforeEach(() => {
generator.startFile('apples', 'pears');
generator.addSimpleMapping(1, 2);
generator.addNamedSourceMapping(3, 4, 5, 6, 'plums');
generator.endFile();
generator.startFile('lemons', 'oranges');
generator.addNamedSourceMapping(7, 8, 9, 10, 'tangerines');
generator.addNamedSourceMapping(11, 12, 13, 14, 'tangerines');
generator.addSimpleMapping(15, 16);
});
it('can add multiple mappings for each file', () => {
expect(generator.toMap()).toEqual({
version: 3,
mappings: 'E;;IAIMA;;;;QCIIC;;;;YAIIA;;;;gB',
sources: ['apples', 'lemons'],
sourcesContent: ['pears', 'oranges'],
names: ['plums', 'tangerines'],
});
});
it('can add a `file` property to the map', () => {
expect(generator.toMap('arbitrary'))
.toEqual(objectContaining({
file: 'arbitrary',
}));
});
it('supports direct JSON serialization', () => {
expect(JSON.parse(generator.toString())).toEqual(generator.toMap());
});
it('supports direct JSON serialization with a file name', () => {
const file = 'arbitrary/file';
expect(JSON.parse(generator.toString(file))).toEqual(generator.toMap(file));
});
});

View File

@ -0,0 +1,85 @@
/**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
'use strict';
jest.disableAutomock();
const Generator = require('../Generator');
const {compactMapping, fromRawMappings} = require('..');
describe('flattening mappings / compacting', () => {
it('flattens simple mappings', () => {
expect(compactMapping({generated: {line: 12, column: 34}}))
.toEqual([12, 34]);
});
it('flattens mappings with a source location', () => {
expect(compactMapping({
generated: {column: 34, line: 12},
original: {column: 78, line: 56},
})).toEqual([12, 34, 56, 78]);
});
it('flattens mappings with a source location and a symbol name', () => {
expect(compactMapping({
generated: {column: 34, line: 12},
name: 'arbitrary',
original: {column: 78, line: 56},
})).toEqual([12, 34, 56, 78, 'arbitrary']);
});
});
describe('build map from raw mappings', () => {
it('returns a `Generator` instance', () => {
expect(fromRawMappings([])).toBeInstanceOf(Generator);
});
it('returns a working source map containing all mappings', () => {
const input = [{
code: lines(11),
map: [
[1, 2],
[3, 4, 5, 6, 'apples'],
[7, 8, 9, 10],
[11, 12, 13, 14, 'pears']
],
sourceCode: 'code1',
sourcePath: 'path1',
}, {
code: lines(3),
map: [
[1, 2],
[3, 4, 15, 16, 'bananas'],
],
sourceCode: 'code2',
sourcePath: 'path2',
}, {
code: lines(23),
map: [
[11, 12],
[13, 14, 15, 16, 'bananas'],
[17, 18, 19, 110],
[21, 112, 113, 114, 'pears']
],
sourceCode: 'code3',
sourcePath: 'path3',
}];
expect(fromRawMappings(input).toMap())
.toEqual({
mappings: 'E;;IAIMA;;;;QAII;;;;YAIIC;E;;ICEEC;;;;;;;;;;;Y;;cCAAA;;;;kBAI8F;;;;gHA8FID',
names: ['apples', 'pears', 'bananas'],
sources: ['path1', 'path2', 'path3'],
sourcesContent: ['code1', 'code2', 'code3'],
version: 3,
});
});
});
const lines = n => Array(n).join('\n');