| // 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:test/test.dart'; |
| import 'package:source_span/source_span.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)); |
| }); |
| }); |
| }); |
| }); |
| } |