| // Copyright 2020 The Dart Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| @Timeout(Duration(minutes: 2)) |
| |
| import 'package:dwds/src/debugging/location.dart'; |
| import 'package:dwds/src/utilities/dart_uri.dart'; |
| import 'package:test/test.dart'; |
| |
| import 'fixtures/fakes.dart'; |
| import 'fixtures/utilities.dart'; |
| |
| final sourceMapContents = |
| '{"version":3,"sourceRoot":"","sources":["main.dart"],"names":[],' |
| '"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAUwB,IAAtB,WAAM;AAKJ,' |
| 'IAHF,4BAAkB,aAAa,SAAC,GAAG;AACb,MAApB,WAAM;AACN,YAAgC,+CAAO,AAAK,oBAAO,' |
| 'yCAAC,WAAW;IAChE;AAC0D,IAA3D,AAAS,AAAK,0DAAO;AAAe,kBAAO;;;AAEvC,gBAAQ;' |
| 'AAGV,IAFI,kCAAqC,QAAC;AACX,MAA/B,WAAM,AAAwB,0BAAP,QAAF,AAAE,KAAK,GAAP;' |
| ';EAEzB","file":"main.ddc.js"}'; |
| |
| void main() { |
| const lines = 100; |
| const lineLength = 150; |
| final assetReader = FakeAssetReader(sourceMap: sourceMapContents); |
| final toolConfiguration = TestToolConfiguration.withLoadStrategy( |
| loadStrategy: MockLoadStrategy(assetReader), |
| ); |
| setGlobalsForTesting( |
| toolConfiguration: toolConfiguration, |
| ); |
| final dartUri = DartUri('org-dartlang-app://web/main.dart'); |
| |
| final modules = FakeModules(module: _module); |
| final locations = Locations(assetReader, modules, ''); |
| locations.initialize('fake_entrypoint'); |
| |
| group('JS locations |', () { |
| group('location |', () { |
| test('is zero based', () async { |
| final loc = JsLocation.fromZeroBased(_module, 0, 0); |
| expect(loc, _matchJsLocation(0, 0)); |
| }); |
| |
| test('can compare to other location', () async { |
| final loc00 = JsLocation.fromZeroBased(_module, 0, 0); |
| final loc01 = JsLocation.fromZeroBased(_module, 0, 1); |
| final loc10 = JsLocation.fromZeroBased(_module, 1, 0); |
| final loc11 = JsLocation.fromZeroBased(_module, 1, 1); |
| |
| expect(loc00.compareTo(loc01), isNegative); |
| expect(loc00.compareTo(loc10), isNegative); |
| expect(loc00.compareTo(loc10), isNegative); |
| expect(loc00.compareTo(loc11), isNegative); |
| |
| expect(loc00.compareTo(loc00), isZero); |
| expect(loc11.compareTo(loc00), isPositive); |
| }); |
| }); |
| |
| group('best location |', () { |
| test('does not return location for setup code', () async { |
| final location = await locations.locationForJs(_module, 0, 0); |
| expect(location, isNull); |
| }); |
| |
| test('prefers precise match', () async { |
| final location = await locations.locationForJs(_module, 37, 0); |
| expect(location, _matchLocationForJs(37, 0)); |
| }); |
| |
| test('finds a match in the beginning of the line', () async { |
| final location = await locations.locationForJs(_module, 39, 4); |
| expect(location, _matchLocationForJs(39, 0)); |
| }); |
| |
| test('finds a match in the middle of the line', () async { |
| final location = await locations.locationForJs(_module, 39, 10); |
| expect(location, _matchLocationForJs(39, 6)); |
| }); |
| |
| test('finds a match on a previous line', () async { |
| final location = await locations.locationForJs(_module, 44, 0); |
| expect(location, _matchLocationForJs(43, 18)); |
| }); |
| |
| test('finds a match on a previous line with a closer match after', |
| () async { |
| final location = |
| await locations.locationForJs(_module, 44, lineLength - 1); |
| expect(location, _matchLocationForJs(43, 18)); |
| }); |
| |
| test('finds a match on the last line', () async { |
| final location = |
| await locations.locationForJs(_module, lines - 1, lineLength - 1); |
| expect(location, _matchLocationForJs(50, 2)); |
| }); |
| |
| test('finds a match on invalid line', () async { |
| final location = |
| await locations.locationForJs(_module, lines, lineLength - 1); |
| expect(location, _matchLocationForJs(50, 2)); |
| }); |
| |
| test('finds a match on invalid column on the same line', () async { |
| final location = await locations.locationForJs(_module, 50, lineLength); |
| expect(location, _matchLocationForJs(50, 2)); |
| }); |
| |
| test('finds a match on invalid column on a previous line', () async { |
| final location = |
| await locations.locationForJs(_module, lines - 1, lineLength); |
| expect(location, _matchLocationForJs(50, 2)); |
| }); |
| }); |
| }); |
| |
| group('Dart locations |', () { |
| group('location |', () { |
| test('is one based', () async { |
| final loc = DartLocation.fromZeroBased(dartUri, 0, 0); |
| expect(loc, _matchDartLocation(1, 1)); |
| }); |
| |
| test('can compare to other locations', () async { |
| final loc00 = DartLocation.fromZeroBased(dartUri, 0, 0); |
| final loc01 = DartLocation.fromZeroBased(dartUri, 0, 1); |
| final loc10 = DartLocation.fromZeroBased(dartUri, 1, 0); |
| final loc11 = DartLocation.fromZeroBased(dartUri, 1, 1); |
| |
| expect(loc00.compareTo(loc01), isNegative); |
| expect(loc00.compareTo(loc10), isNegative); |
| expect(loc00.compareTo(loc10), isNegative); |
| expect(loc00.compareTo(loc11), isNegative); |
| |
| expect(loc00.compareTo(loc00), isZero); |
| expect(loc11.compareTo(loc00), isPositive); |
| }); |
| }); |
| |
| group('best location |', () { |
| test('does not return location for dart lines not mapped to JS', |
| () async { |
| final location = await locations.locationForDart(dartUri, 0, 0); |
| expect(location, isNull); |
| }); |
| |
| test('returns location after on the same line', () async { |
| final location = await locations.locationForDart(dartUri, 11, 0); |
| expect(location, _matchLocationForDart(11, 3)); |
| }); |
| |
| test('return null on invalid line', () async { |
| final location = await locations.locationForDart(dartUri, lines, 0); |
| expect(location, isNull); |
| }); |
| |
| test('return null on invalid column', () async { |
| final location = |
| await locations.locationForDart(dartUri, lines - 1, lineLength); |
| expect(location, isNull); |
| }); |
| }); |
| }); |
| } |
| |
| Matcher _matchLocationForDart(int line, int column) => isA<Location>().having( |
| (l) => l.dartLocation, |
| 'dartLocation', |
| _matchDartLocation(line, column), |
| ); |
| |
| Matcher _matchLocationForJs(int line, int column) => isA<Location>() |
| .having((l) => l.jsLocation, 'jsLocation', _matchJsLocation(line, column)); |
| |
| Matcher _matchDartLocation(int line, int column) => isA<DartLocation>() |
| .having((l) => l.line, 'line', line) |
| .having((l) => l.column, 'column', column); |
| |
| Matcher _matchJsLocation(int line, int column) => isA<JsLocation>() |
| .having((l) => l.line, 'line', line) |
| .having((l) => l.column, 'column', column); |
| |
| const _module = 'packages/module'; |
| const _serverPath = 'web/main.dart'; |
| const _sourceMapPath = 'packages/module.js.map'; |
| |
| class MockLoadStrategy extends FakeStrategy { |
| MockLoadStrategy(super.assetReader); |
| |
| @override |
| Future<String?> moduleForServerPath( |
| String entrypoint, |
| String serverPath, |
| ) async => |
| _module; |
| |
| @override |
| Future<String> serverPathForModule(String entrypoint, String module) async => |
| _serverPath; |
| |
| @override |
| Future<String> sourceMapPathForModule( |
| String entrypoint, |
| String module, |
| ) async => |
| _sourceMapPath; |
| |
| @override |
| String serverPathForAppUri(String appUri) => _serverPath; |
| } |