blob: e762341510182f73b34b5c63e0721906ccc515e3 [file] [log] [blame]
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import {encodeVlqList} from '../../testing/SourceMapEncoder.js';
import * as SDK from './sdk.js';
const {buildOriginalScopes, decodePastaRanges} = SDK.SourceMapFunctionRanges;
describe('buildOriginalScopes', () => {
it('returns an empty global scope for an empty ranges array', () => {
const scope = buildOriginalScopes([]);
assert.isEmpty(scope.children);
assert.isFalse(scope.isStackFrame);
assert.deepEqual(scope.start, {line: 0, column: 0});
});
it('throws if a range has zero length (i.e. equal start and end positions)', () => {
assert.throws(() => buildOriginalScopes([{name: 'foo', start: {line: 5, column: 0}, end: {line: 5, column: 0}}]));
});
it('throws if the start position doesn\'t come before the end position', () => {
assert.throws(() => buildOriginalScopes([{name: 'foo', start: {line: 5, column: 0}, end: {line: 2, column: 0}}]));
});
it('throws for partially overlapping ranges (i.e. "straddling")', () => {
/*
* --- A
* | --- B
* --- |
* ---
*/
const rangeA = {start: {line: 0, column: 0}, end: {line: 20, column: 0}, name: 'A'};
const rangeB = {start: {line: 10, column: 0}, end: {line: 30, column: 0}, name: 'B'};
assert.throws(() => buildOriginalScopes([rangeA, rangeB]));
});
it('handles nested scopes', () => {
/*
* --- A
* | --- B
* | |
* | ---
* ---
*/
const rangeA = {start: {line: 0, column: 0}, end: {line: 30, column: 0}, name: 'A'};
const rangeB = {start: {line: 10, column: 0}, end: {line: 20, column: 0}, name: 'B'};
const root = buildOriginalScopes([rangeA, rangeB]);
assert.lengthOf(root.children, 1);
assert.deepNestedInclude(root.children[0], rangeA);
assert.lengthOf(root.children[0].children, 1);
assert.deepNestedInclude(root.children[0].children[0], rangeB);
assert.deepEqual(root.end, rangeA.end);
});
it('handles sibling scopes', () => {
/*
* --- A
* |
* ---
* --- B
* |
* ---
*/
const rangeA = {start: {line: 0, column: 0}, end: {line: 10, column: 0}, name: 'A'};
const rangeB = {start: {line: 20, column: 0}, end: {line: 30, column: 0}, name: 'B'};
const root = buildOriginalScopes([rangeA, rangeB]);
assert.lengthOf(root.children, 2);
assert.deepNestedInclude(root.children[0], rangeA);
assert.deepNestedInclude(root.children[1], rangeB);
assert.deepEqual(root.end, rangeB.end);
});
it('handles siblings where first.end === second.start (because end is exclusive)', () => {
/*
* --- A
* |
* --- --- B
* |
* ---
*/
const rangeA = {start: {line: 0, column: 0}, end: {line: 10, column: 0}, name: 'A'};
const rangeB = {start: {line: 10, column: 0}, end: {line: 20, column: 0}, name: 'B'};
const root = buildOriginalScopes([rangeA, rangeB]);
assert.lengthOf(root.children, 2);
assert.deepNestedInclude(root.children[0], rangeA);
assert.deepNestedInclude(root.children[1], rangeB);
assert.deepEqual(root.end, rangeB.end);
});
it('handles siblings that either have the same start, or the same end', () => {
/*
* --- A --- B
* | |
* | ---
* |
* ---
* --- C
* | --- D
* | |
* --- ---
*/
const rangeA = {start: {line: 0, column: 0}, end: {line: 20, column: 0}, name: 'A'};
const rangeB = {start: {line: 0, column: 0}, end: {line: 10, column: 0}, name: 'B'};
const rangeC = {start: {line: 30, column: 0}, end: {line: 50, column: 0}, name: 'C'};
const rangeD = {start: {line: 40, column: 0}, end: {line: 50, column: 0}, name: 'D'};
const root = buildOriginalScopes([rangeD, rangeB, rangeA, rangeC]); // Shuffle to check sorting
assert.lengthOf(root.children, 2);
assert.deepNestedInclude(root.children[0], rangeA);
assert.deepNestedInclude(root.children[1], rangeC);
assert.lengthOf(root.children[0].children, 1);
assert.deepNestedInclude(root.children[0].children[0], rangeB);
assert.lengthOf(root.children[1].children, 1);
assert.deepNestedInclude(root.children[1].children[0], rangeD);
assert.deepEqual(root.end, rangeC.end);
assert.deepEqual(root.end, rangeD.end);
});
});
describe('decodeBloombergRanges', () => {
it('returns an empty list for an empty string', () => {
assert.deepEqual(decodePastaRanges('', []), []);
});
it('ignores ranges with non-existing name index', () => {
const mapping = encodeVlqList([0, 0, 0, 5, 0]);
assert.deepEqual(decodePastaRanges(mapping, []), []);
});
it('decodes nested ranges', () => {
const mappings = [
encodeVlqList([0, 0, 10, 30, 2]),
encodeVlqList([1, -20, 5, 10, 2]),
].join(',');
assert.deepEqual(decodePastaRanges(mappings, ['foo', 'bar']), [
{start: {line: 0, column: 10}, end: {line: 30, column: 2}, name: 'foo'},
{start: {line: 10, column: 15}, end: {line: 20, column: 4}, name: 'bar'},
]);
});
it('decodes sibling scopes', () => {
const mappings = [
encodeVlqList([0, 0, 10, 10, 2]),
encodeVlqList([1, 10, 0, 10, 0]),
].join(',');
assert.deepEqual(decodePastaRanges(mappings, ['foo', 'bar']), [
{start: {line: 0, column: 10}, end: {line: 10, column: 2}, name: 'foo'},
{start: {line: 20, column: 10}, end: {line: 30, column: 2}, name: 'bar'},
]);
});
});