| // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | |
| // for details. All rights reserved. Use of this source code is governed by a | |
| // BSD-style license that can be found in the LICENSE file. | |
| import 'package:source_span/source_span.dart'; | |
| import 'package:test/test.dart'; | |
| void main() { | |
| late SourceFile file; | |
| setUp(() { | |
| file = SourceFile.fromString(''' | |
| foo bar baz | |
| whiz bang boom | |
| zip zap zop''', url: 'foo.dart'); | |
| }); | |
| group('errors', () { | |
| group('for span()', () { | |
| test('end must come after start', () { | |
| expect(() => file.span(10, 5), throwsArgumentError); | |
| }); | |
| test('start may not be negative', () { | |
| expect(() => file.span(-1, 5), throwsRangeError); | |
| }); | |
| test('end may not be outside the file', () { | |
| expect(() => file.span(10, 100), throwsRangeError); | |
| }); | |
| }); | |
| group('for location()', () { | |
| test('offset may not be negative', () { | |
| expect(() => file.location(-1), throwsRangeError); | |
| }); | |
| test('offset may not be outside the file', () { | |
| expect(() => file.location(100), throwsRangeError); | |
| }); | |
| }); | |
| group('for getLine()', () { | |
| test('offset may not be negative', () { | |
| expect(() => file.getLine(-1), throwsRangeError); | |
| }); | |
| test('offset may not be outside the file', () { | |
| expect(() => file.getLine(100), throwsRangeError); | |
| }); | |
| }); | |
| group('for getColumn()', () { | |
| test('offset may not be negative', () { | |
| expect(() => file.getColumn(-1), throwsRangeError); | |
| }); | |
| test('offset may not be outside the file', () { | |
| expect(() => file.getColumn(100), throwsRangeError); | |
| }); | |
| test('line may not be negative', () { | |
| expect(() => file.getColumn(1, line: -1), throwsRangeError); | |
| }); | |
| test('line may not be outside the file', () { | |
| expect(() => file.getColumn(1, line: 100), throwsRangeError); | |
| }); | |
| test('line must be accurate', () { | |
| expect(() => file.getColumn(1, line: 1), throwsRangeError); | |
| }); | |
| }); | |
| group('getOffset()', () { | |
| test('line may not be negative', () { | |
| expect(() => file.getOffset(-1), throwsRangeError); | |
| }); | |
| test('column may not be negative', () { | |
| expect(() => file.getOffset(1, -1), throwsRangeError); | |
| }); | |
| test('line may not be outside the file', () { | |
| expect(() => file.getOffset(100), throwsRangeError); | |
| }); | |
| test('column may not be outside the file', () { | |
| expect(() => file.getOffset(2, 100), throwsRangeError); | |
| }); | |
| test('column may not be outside the line', () { | |
| expect(() => file.getOffset(1, 20), throwsRangeError); | |
| }); | |
| }); | |
| group('for getText()', () { | |
| test('end must come after start', () { | |
| expect(() => file.getText(10, 5), throwsArgumentError); | |
| }); | |
| test('start may not be negative', () { | |
| expect(() => file.getText(-1, 5), throwsRangeError); | |
| }); | |
| test('end may not be outside the file', () { | |
| expect(() => file.getText(10, 100), throwsRangeError); | |
| }); | |
| }); | |
| group('for span().union()', () { | |
| test('source URLs must match', () { | |
| final other = SourceSpan(SourceLocation(10), SourceLocation(11), '_'); | |
| expect(() => file.span(9, 10).union(other), throwsArgumentError); | |
| }); | |
| test('spans may not be disjoint', () { | |
| expect(() => file.span(9, 10).union(file.span(11, 12)), | |
| throwsArgumentError); | |
| }); | |
| }); | |
| test('for span().expand() source URLs must match', () { | |
| final other = SourceFile.fromString(''' | |
| foo bar baz | |
| whiz bang boom | |
| zip zap zop''', url: 'bar.dart').span(10, 11); | |
| expect(() => file.span(9, 10).expand(other), throwsArgumentError); | |
| }); | |
| }); | |
| test('fields work correctly', () { | |
| expect(file.url, equals(Uri.parse('foo.dart'))); | |
| expect(file.lines, equals(3)); | |
| expect(file.length, equals(38)); | |
| }); | |
| group('new SourceFile()', () { | |
| test('handles CRLF correctly', () { | |
| expect(SourceFile.fromString('foo\r\nbar').getLine(6), equals(1)); | |
| }); | |
| test('handles a lone CR correctly', () { | |
| expect(SourceFile.fromString('foo\rbar').getLine(5), equals(1)); | |
| }); | |
| }); | |
| group('span()', () { | |
| test('returns a span between the given offsets', () { | |
| final span = file.span(5, 10); | |
| expect(span.start, equals(file.location(5))); | |
| expect(span.end, equals(file.location(10))); | |
| }); | |
| test('end defaults to the end of the file', () { | |
| final span = file.span(5); | |
| expect(span.start, equals(file.location(5))); | |
| expect(span.end, equals(file.location(file.length))); | |
| }); | |
| }); | |
| group('getLine()', () { | |
| test('works for a middle character on the line', () { | |
| expect(file.getLine(15), equals(1)); | |
| }); | |
| test('works for the first character of a line', () { | |
| expect(file.getLine(12), equals(1)); | |
| }); | |
| test('works for a newline character', () { | |
| expect(file.getLine(11), equals(0)); | |
| }); | |
| test('works for the last offset', () { | |
| expect(file.getLine(file.length), equals(2)); | |
| }); | |
| }); | |
| group('getColumn()', () { | |
| test('works for a middle character on the line', () { | |
| expect(file.getColumn(15), equals(3)); | |
| }); | |
| test('works for the first character of a line', () { | |
| expect(file.getColumn(12), equals(0)); | |
| }); | |
| test('works for a newline character', () { | |
| expect(file.getColumn(11), equals(11)); | |
| }); | |
| test('works when line is passed as well', () { | |
| expect(file.getColumn(12, line: 1), equals(0)); | |
| }); | |
| test('works for the last offset', () { | |
| expect(file.getColumn(file.length), equals(11)); | |
| }); | |
| }); | |
| group('getOffset()', () { | |
| test('works for a middle character on the line', () { | |
| expect(file.getOffset(1, 3), equals(15)); | |
| }); | |
| test('works for the first character of a line', () { | |
| expect(file.getOffset(1), equals(12)); | |
| }); | |
| test('works for a newline character', () { | |
| expect(file.getOffset(0, 11), equals(11)); | |
| }); | |
| test('works for the last offset', () { | |
| expect(file.getOffset(2, 11), equals(file.length)); | |
| }); | |
| }); | |
| group('getText()', () { | |
| test('returns a substring of the source', () { | |
| expect(file.getText(8, 15), equals('baz\nwhi')); | |
| }); | |
| test('end defaults to the end of the file', () { | |
| expect(file.getText(20), equals('g boom\nzip zap zop')); | |
| }); | |
| }); | |
| group('FileLocation', () { | |
| test('reports the correct line number', () { | |
| expect(file.location(15).line, equals(1)); | |
| }); | |
| test('reports the correct column number', () { | |
| expect(file.location(15).column, equals(3)); | |
| }); | |
| test('pointSpan() returns a FileSpan', () { | |
| final location = file.location(15); | |
| final span = location.pointSpan(); | |
| expect(span, isA<FileSpan>()); | |
| expect(span.start, equals(location)); | |
| expect(span.end, equals(location)); | |
| expect(span.text, isEmpty); | |
| }); | |
| }); | |
| group('FileSpan', () { | |
| test('text returns a substring of the source', () { | |
| expect(file.span(8, 15).text, equals('baz\nwhi')); | |
| }); | |
| test('text includes the last char when end is defaulted to EOF', () { | |
| expect(file.span(29).text, equals('p zap zop')); | |
| }); | |
| group('context', () { | |
| test("contains the span's text", () { | |
| final span = file.span(8, 15); | |
| expect(span.context.contains(span.text), isTrue); | |
| expect(span.context, equals('foo bar baz\nwhiz bang boom\n')); | |
| }); | |
| test('contains the previous line for a point span at the end of a line', | |
| () { | |
| final span = file.span(25, 25); | |
| expect(span.context, equals('whiz bang boom\n')); | |
| }); | |
| test('contains the next line for a point span at the beginning of a line', | |
| () { | |
| final span = file.span(12, 12); | |
| expect(span.context, equals('whiz bang boom\n')); | |
| }); | |
| group('for a point span at the end of a file', () { | |
| test('without a newline, contains the last line', () { | |
| final span = file.span(file.length, file.length); | |
| expect(span.context, equals('zip zap zop')); | |
| }); | |
| test('with a newline, contains an empty line', () { | |
| file = SourceFile.fromString(''' | |
| foo bar baz | |
| whiz bang boom | |
| zip zap zop | |
| ''', url: 'foo.dart'); | |
| final span = file.span(file.length, file.length); | |
| expect(span.context, isEmpty); | |
| }); | |
| }); | |
| }); | |
| group('union()', () { | |
| late FileSpan span; | |
| setUp(() { | |
| span = file.span(5, 12); | |
| }); | |
| test('works with a preceding adjacent span', () { | |
| final other = file.span(0, 5); | |
| final result = span.union(other); | |
| expect(result.start, equals(other.start)); | |
| expect(result.end, equals(span.end)); | |
| expect(result.text, equals('foo bar baz\n')); | |
| }); | |
| test('works with a preceding overlapping span', () { | |
| final other = file.span(0, 8); | |
| final result = span.union(other); | |
| expect(result.start, equals(other.start)); | |
| expect(result.end, equals(span.end)); | |
| expect(result.text, equals('foo bar baz\n')); | |
| }); | |
| test('works with a following adjacent span', () { | |
| final other = file.span(12, 16); | |
| final result = span.union(other); | |
| expect(result.start, equals(span.start)); | |
| expect(result.end, equals(other.end)); | |
| expect(result.text, equals('ar baz\nwhiz')); | |
| }); | |
| test('works with a following overlapping span', () { | |
| final other = file.span(9, 16); | |
| final result = span.union(other); | |
| expect(result.start, equals(span.start)); | |
| expect(result.end, equals(other.end)); | |
| expect(result.text, equals('ar baz\nwhiz')); | |
| }); | |
| test('works with an internal overlapping span', () { | |
| final other = file.span(7, 10); | |
| expect(span.union(other), equals(span)); | |
| }); | |
| test('works with an external overlapping span', () { | |
| final other = file.span(0, 16); | |
| expect(span.union(other), equals(other)); | |
| }); | |
| test('returns a FileSpan for a FileSpan input', () { | |
| expect(span.union(file.span(0, 5)), isA<FileSpan>()); | |
| }); | |
| test('returns a base SourceSpan for a SourceSpan input', () { | |
| final other = SourceSpan(SourceLocation(0, sourceUrl: 'foo.dart'), | |
| SourceLocation(5, sourceUrl: 'foo.dart'), 'hey, '); | |
| final result = span.union(other); | |
| expect(result, isNot(isA<FileSpan>())); | |
| expect(result.start, equals(other.start)); | |
| expect(result.end, equals(span.end)); | |
| expect(result.text, equals('hey, ar baz\n')); | |
| }); | |
| }); | |
| group('expand()', () { | |
| late FileSpan span; | |
| setUp(() { | |
| span = file.span(5, 12); | |
| }); | |
| test('works with a preceding nonadjacent span', () { | |
| final other = file.span(0, 3); | |
| final result = span.expand(other); | |
| expect(result.start, equals(other.start)); | |
| expect(result.end, equals(span.end)); | |
| expect(result.text, equals('foo bar baz\n')); | |
| }); | |
| test('works with a preceding overlapping span', () { | |
| final other = file.span(0, 8); | |
| final result = span.expand(other); | |
| expect(result.start, equals(other.start)); | |
| expect(result.end, equals(span.end)); | |
| expect(result.text, equals('foo bar baz\n')); | |
| }); | |
| test('works with a following nonadjacent span', () { | |
| final other = file.span(14, 16); | |
| final result = span.expand(other); | |
| expect(result.start, equals(span.start)); | |
| expect(result.end, equals(other.end)); | |
| expect(result.text, equals('ar baz\nwhiz')); | |
| }); | |
| test('works with a following overlapping span', () { | |
| final other = file.span(9, 16); | |
| final result = span.expand(other); | |
| expect(result.start, equals(span.start)); | |
| expect(result.end, equals(other.end)); | |
| expect(result.text, equals('ar baz\nwhiz')); | |
| }); | |
| test('works with an internal overlapping span', () { | |
| final other = file.span(7, 10); | |
| expect(span.expand(other), equals(span)); | |
| }); | |
| test('works with an external overlapping span', () { | |
| final other = file.span(0, 16); | |
| expect(span.expand(other), equals(other)); | |
| }); | |
| }); | |
| group('subspan()', () { | |
| late FileSpan span; | |
| setUp(() { | |
| span = file.span(5, 11); // "ar baz" | |
| }); | |
| group('errors', () { | |
| test('start must be greater than zero', () { | |
| expect(() => span.subspan(-1), throwsRangeError); | |
| }); | |
| test('start must be less than or equal to length', () { | |
| expect(() => span.subspan(span.length + 1), throwsRangeError); | |
| }); | |
| test('end must be greater than start', () { | |
| expect(() => span.subspan(2, 1), throwsRangeError); | |
| }); | |
| test('end must be less than or equal to length', () { | |
| expect(() => span.subspan(0, span.length + 1), throwsRangeError); | |
| }); | |
| }); | |
| test('preserves the source URL', () { | |
| final result = span.subspan(1, 2); | |
| expect(result.start.sourceUrl, equals(span.sourceUrl)); | |
| expect(result.end.sourceUrl, equals(span.sourceUrl)); | |
| }); | |
| group('returns the original span', () { | |
| test('with an implicit end', | |
| () => expect(span.subspan(0), equals(span))); | |
| test('with an explicit end', | |
| () => expect(span.subspan(0, span.length), equals(span))); | |
| }); | |
| group('within a single line', () { | |
| test('returns a strict substring of the original span', () { | |
| final result = span.subspan(1, 5); | |
| expect(result.text, equals('r ba')); | |
| expect(result.start.offset, equals(6)); | |
| expect(result.start.line, equals(0)); | |
| expect(result.start.column, equals(6)); | |
| expect(result.end.offset, equals(10)); | |
| expect(result.end.line, equals(0)); | |
| expect(result.end.column, equals(10)); | |
| }); | |
| test('an implicit end goes to the end of the original span', () { | |
| final result = span.subspan(1); | |
| expect(result.text, equals('r baz')); | |
| expect(result.start.offset, equals(6)); | |
| expect(result.start.line, equals(0)); | |
| expect(result.start.column, equals(6)); | |
| expect(result.end.offset, equals(11)); | |
| expect(result.end.line, equals(0)); | |
| expect(result.end.column, equals(11)); | |
| }); | |
| test('can return an empty span', () { | |
| final result = span.subspan(3, 3); | |
| expect(result.text, isEmpty); | |
| expect(result.start.offset, equals(8)); | |
| expect(result.start.line, equals(0)); | |
| expect(result.start.column, equals(8)); | |
| expect(result.end, equals(result.start)); | |
| }); | |
| }); | |
| group('across multiple lines', () { | |
| setUp(() { | |
| span = file.span(22, 30); // "boom\nzip" | |
| }); | |
| test('with start and end in the middle of a line', () { | |
| final result = span.subspan(3, 6); | |
| expect(result.text, equals('m\nz')); | |
| expect(result.start.offset, equals(25)); | |
| expect(result.start.line, equals(1)); | |
| expect(result.start.column, equals(13)); | |
| expect(result.end.offset, equals(28)); | |
| expect(result.end.line, equals(2)); | |
| expect(result.end.column, equals(1)); | |
| }); | |
| test('with start at the end of a line', () { | |
| final result = span.subspan(4, 6); | |
| expect(result.text, equals('\nz')); | |
| expect(result.start.offset, equals(26)); | |
| expect(result.start.line, equals(1)); | |
| expect(result.start.column, equals(14)); | |
| }); | |
| test('with start at the beginning of a line', () { | |
| final result = span.subspan(5, 6); | |
| expect(result.text, equals('z')); | |
| expect(result.start.offset, equals(27)); | |
| expect(result.start.line, equals(2)); | |
| expect(result.start.column, equals(0)); | |
| }); | |
| test('with end at the end of a line', () { | |
| final result = span.subspan(3, 4); | |
| expect(result.text, equals('m')); | |
| expect(result.end.offset, equals(26)); | |
| expect(result.end.line, equals(1)); | |
| expect(result.end.column, equals(14)); | |
| }); | |
| test('with end at the beginning of a line', () { | |
| final result = span.subspan(3, 5); | |
| expect(result.text, equals('m\n')); | |
| expect(result.end.offset, equals(27)); | |
| expect(result.end.line, equals(2)); | |
| expect(result.end.column, equals(0)); | |
| }); | |
| }); | |
| }); | |
| }); | |
| } |