Skip to content

Commit ba6fb1c

Browse files
authored
Support running tests by absolute file: uri (#1893)
Fixes #1891 Fixes #1899 For any path which looks like it has a scheme (contains "://"), parse it as a uri and grab the path from that. On windows, we also remove any leading `/` from the path because it will be followed by a drive letter, which we want to be the first part of the path (and not preceded by a `/`).
1 parent ffeaec6 commit ba6fb1c

File tree

8 files changed

+76
-76
lines changed

8 files changed

+76
-76
lines changed

pkgs/test/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
reporter.
55
* Add support for CHROME_EXECUTABLE environment variable. This overrides any
66
config file settings.
7+
* Support running tests by absolute file uri.
78

89
## 1.22.2
910

pkgs/test/test/runner/configuration/platform_test.dart

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
@TestOn('vm')
66
import 'dart:convert';
7-
import 'dart:io';
87

98
import 'package:test/test.dart';
109
import 'package:test_core/src/util/exit_codes.dart' as exit_codes;
@@ -205,10 +204,8 @@ void main() {
205204
var test = await runTest(['.']);
206205
expect(
207206
test.stdout,
208-
containsInOrder([
209-
'+0: .${Platform.pathSeparator}test_foo.dart: test_foo',
210-
'+1: All tests passed!'
211-
]));
207+
containsInOrder(
208+
['+0: ./test_foo.dart: test_foo', '+1: All tests passed!']));
212209
await test.shouldExit(0);
213210
});
214211

@@ -243,10 +240,8 @@ void main() {
243240
var test = await runTest(['.']);
244241
expect(
245242
test.stdout,
246-
containsInOrder([
247-
'+0: .${Platform.pathSeparator}foo_test.dart: foo_test',
248-
'+1: All tests passed!'
249-
]));
243+
containsInOrder(
244+
['+0: ./foo_test.dart: foo_test', '+1: All tests passed!']));
250245
await test.shouldExit(0);
251246
});
252247

pkgs/test/test/runner/configuration/randomize_order_test.dart

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
@TestOn('vm')
66
import 'dart:convert';
7-
import 'dart:io';
87

98
import 'package:test/test.dart';
109
import 'package:test_descriptor/test_descriptor.dart' as d;
@@ -148,14 +147,14 @@ void main() {
148147
test.stdout,
149148
emitsInAnyOrder([
150149
containsInOrder([
151-
'.${Platform.pathSeparator}1_test.dart: test 1.2',
152-
'.${Platform.pathSeparator}1_test.dart: test 1.3',
153-
'.${Platform.pathSeparator}1_test.dart: test 1.1'
150+
'./1_test.dart: test 1.2',
151+
'./1_test.dart: test 1.3',
152+
'./1_test.dart: test 1.1'
154153
]),
155154
containsInOrder([
156-
'.${Platform.pathSeparator}2_test.dart: test 2.2',
157-
'.${Platform.pathSeparator}2_test.dart: test 2.3',
158-
'.${Platform.pathSeparator}2_test.dart: test 2.1'
155+
'./2_test.dart: test 2.2',
156+
'./2_test.dart: test 2.3',
157+
'./2_test.dart: test 2.1'
159158
]),
160159
contains('+6: All tests passed!')
161160
]));

pkgs/test/test/runner/runner_test.dart

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import 'dart:io';
88
import 'dart:math' as math;
99

10+
import 'package:path/path.dart' as p;
1011
import 'package:test/test.dart';
1112
import 'package:test_core/src/util/exit_codes.dart' as exit_codes;
1213
import 'package:test_descriptor/test_descriptor.dart' as d;
@@ -362,6 +363,30 @@ $_usage''');
362363
expect(test.stdout, emitsThrough(contains('+1: All tests passed!')));
363364
await test.shouldExit(0);
364365
});
366+
367+
test('given a file: uri', () async {
368+
await d.file('test.dart', _success).create();
369+
var fileUri = p.toUri(d.path('test.dart')).toString();
370+
expect(fileUri, startsWith('file:///'));
371+
var test = await runTest([fileUri]);
372+
expect(test.stdout, emitsThrough(contains('+1: All tests passed!')));
373+
await test.shouldExit(0);
374+
});
375+
376+
test('with platform specific relative paths', () async {
377+
await d.dir('foo', [d.file('test.dart', _success)]).create();
378+
var test = await runTest([p.join('foo', 'test.dart')]);
379+
expect(test.stdout, emitsThrough(contains('+1: All tests passed!')));
380+
await test.shouldExit(0);
381+
});
382+
383+
test('with platform specific relative paths containing query params',
384+
() async {
385+
await d.dir('foo', [d.file('test.dart', _success)]).create();
386+
var test = await runTest(['${p.join('foo', 'test.dart')}?line=6']);
387+
expect(test.stdout, emitsThrough(contains('+1: All tests passed!')));
388+
await test.shouldExit(0);
389+
});
365390
});
366391

367392
group('runs successful tests with async setup', () {

pkgs/test/test/runner/shard_test.dart

Lines changed: 12 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,6 @@
33
// BSD-style license that can be found in the LICENSE file.
44

55
@TestOn('vm')
6-
7-
import 'dart:io';
8-
96
import 'package:test/test.dart';
107
import 'package:test_core/src/util/exit_codes.dart' as exit_codes;
118
import 'package:test_descriptor/test_descriptor.dart' as d;
@@ -95,14 +92,10 @@ void main() {
9592
test.stdout,
9693
emitsInOrder([
9794
emitsAnyOf([
98-
containsInOrder([
99-
'+0: .${Platform.pathSeparator}1_test.dart: test 1.1',
100-
'+1: .${Platform.pathSeparator}2_test.dart: test 2.1'
101-
]),
102-
containsInOrder([
103-
'+0: .${Platform.pathSeparator}2_test.dart: test 2.1',
104-
'+1: .${Platform.pathSeparator}1_test.dart: test 1.1'
105-
])
95+
containsInOrder(
96+
['+0: ./1_test.dart: test 1.1', '+1: ./2_test.dart: test 2.1']),
97+
containsInOrder(
98+
['+0: ./2_test.dart: test 2.1', '+1: ./1_test.dart: test 1.1'])
10699
]),
107100
contains('+2: All tests passed!')
108101
]));
@@ -113,14 +106,10 @@ void main() {
113106
test.stdout,
114107
emitsInOrder([
115108
emitsAnyOf([
116-
containsInOrder([
117-
'+0: .${Platform.pathSeparator}1_test.dart: test 1.2',
118-
'+1: .${Platform.pathSeparator}2_test.dart: test 2.2'
119-
]),
120-
containsInOrder([
121-
'+0: .${Platform.pathSeparator}2_test.dart: test 2.2',
122-
'+1: .${Platform.pathSeparator}1_test.dart: test 1.2'
123-
])
109+
containsInOrder(
110+
['+0: ./1_test.dart: test 1.2', '+1: ./2_test.dart: test 2.2']),
111+
containsInOrder(
112+
['+0: ./2_test.dart: test 2.2', '+1: ./1_test.dart: test 1.2'])
124113
]),
125114
contains('+2: All tests passed!')
126115
]));
@@ -131,14 +120,10 @@ void main() {
131120
test.stdout,
132121
emitsInOrder([
133122
emitsAnyOf([
134-
containsInOrder([
135-
'+0: .${Platform.pathSeparator}1_test.dart: test 1.3',
136-
'+1: .${Platform.pathSeparator}2_test.dart: test 2.3'
137-
]),
138-
containsInOrder([
139-
'+0: .${Platform.pathSeparator}2_test.dart: test 2.3',
140-
'+1: .${Platform.pathSeparator}1_test.dart: test 1.3'
141-
])
123+
containsInOrder(
124+
['+0: ./1_test.dart: test 1.3', '+1: ./2_test.dart: test 2.3']),
125+
containsInOrder(
126+
['+0: ./2_test.dart: test 2.3', '+1: ./1_test.dart: test 1.3'])
142127
]),
143128
contains('+2: All tests passed!')
144129
]));

pkgs/test_core/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
* Avoid empty expandable groups for tests without extra output in Github
44
reporter.
5+
* Support running tests by absolute file uri.
56
* Update `vm_service` constraint to `>=6.0.0 <12.0.0`.
67

78
# 0.4.22

pkgs/test_core/lib/src/runner.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import 'dart:io';
77

88
import 'package:async/async.dart';
99
import 'package:boolean_selector/boolean_selector.dart';
10-
import 'package:path/path.dart' as p;
1110
import 'package:stack_trace/stack_trace.dart';
1211
// ignore: deprecated_member_use
1312
import 'package:test_api/backend.dart'
@@ -317,7 +316,8 @@ class Runner {
317316
'Cannot filter by line/column for this test suite, no suite'
318317
'path available.');
319318
}
320-
var absoluteSuitePath = p.absolute(path);
319+
// The absolute path as it will appear in stack traces.
320+
var absoluteSuitePath = File(path).absolute.uri.toFilePath();
321321

322322
bool matchLineAndCol(Frame frame) {
323323
if (frame.uri.scheme != 'file' ||

pkgs/test_core/lib/src/runner/configuration/args.dart

Lines changed: 25 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -177,41 +177,35 @@ Configuration parse(List<String> args) => _Parser(args).parse();
177177

178178
void _parseTestSelection(
179179
String option, Map<String, Set<TestSelection>> selections) {
180-
var firstQuestion = option.indexOf('?');
181-
TestSelection selection;
182-
String path;
183-
if (firstQuestion == -1) {
184-
path = option;
185-
selection = TestSelection();
186-
} else if (option.substring(0, firstQuestion).contains('\\')) {
187-
throw FormatException(
188-
'When passing test path queries, you must pass the path in URI '
189-
'format (use `/` for directory separators instead of `\\`).');
190-
} else {
191-
final uri = Uri.parse(option);
180+
final uri = Uri.parse(option);
181+
// Restore the path to how it was given (namely, we want to report paths
182+
// with their original separators).
183+
var path = Uri.decodeComponent(uri.path);
184+
// Strip out the leading slash before the drive letter on windows.
185+
if (Platform.isWindows && path.startsWith('/')) {
186+
path = path.substring(1);
187+
}
192188

193-
final names = uri.queryParametersAll['name'];
194-
final fullName = uri.queryParameters['full-name'];
195-
final line = uri.queryParameters['line'];
196-
final col = uri.queryParameters['col'];
189+
final names = uri.queryParametersAll['name'];
190+
final fullName = uri.queryParameters['full-name'];
191+
final line = uri.queryParameters['line'];
192+
final col = uri.queryParameters['col'];
197193

198-
if (names != null && names.isNotEmpty && fullName != null) {
199-
throw FormatException(
200-
'Cannot specify both "name=<...>" and "full-name=<...>".',
201-
);
202-
}
203-
path = uri.path;
204-
selection = TestSelection(
205-
testPatterns: fullName != null
206-
? {RegExp('^${RegExp.escape(fullName)}\$')}
207-
: {
208-
if (names != null)
209-
for (var name in names) RegExp(name)
210-
},
211-
line: line == null ? null : int.parse(line),
212-
col: col == null ? null : int.parse(col),
194+
if (names != null && names.isNotEmpty && fullName != null) {
195+
throw FormatException(
196+
'Cannot specify both "name=<...>" and "full-name=<...>".',
213197
);
214198
}
199+
final selection = TestSelection(
200+
testPatterns: fullName != null
201+
? {RegExp('^${RegExp.escape(fullName)}\$')}
202+
: {
203+
if (names != null)
204+
for (var name in names) RegExp(name)
205+
},
206+
line: line == null ? null : int.parse(line),
207+
col: col == null ? null : int.parse(col),
208+
);
215209

216210
selections.update(path, (selections) => selections..add(selection),
217211
ifAbsent: () => {selection});

0 commit comments

Comments
 (0)