ac38046718
When running a large test file with `solo: true` on one test, the output looks like: ``` ! test name Skip: does not have "solo" ! test name Skip: does not have "solo" ! test name Skip: does not have "solo" ! test name Skip: does not have "solo" ✓ solo test ! test name Skip: does not have "solo" ``` While custom skip messages might be useful, when the skip messages are from using solo:true this just makes the output spammy and adds extra noise to scan through for the actual solo test you ran. This change prevents creating output events for such skip messages (but still sends them for custom messages, since those are likely far less frequent). The `print` events themselves are still forwarded to the DAP client, so it still has the data for this that it can present elsewhere (for example by showing the test node as skipped, if appropriate). Fixes part of https://github.com/Dart-Code/Dart-Code/issues/5938 Change-Id: I1d26751207e7b89f77e13be0dfcc74710e7d75ec Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/486780 Reviewed-by: Ben Konyi <bkonyi@google.com> Reviewed-by: Jessy Yameogo <yjessy@google.com> Commit-Queue: Ben Konyi <bkonyi@google.com>
449 lines
12 KiB
Dart
449 lines
12 KiB
Dart
// Copyright (c) 2021, 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 'dart:convert';
|
|
|
|
import 'package:test/test.dart';
|
|
|
|
/// A marker used in some test scripts/tests for where to set breakpoints.
|
|
const breakpointMarker = '// BREAKPOINT';
|
|
|
|
/// A simple empty Dart script that should run with no output and no errors.
|
|
const emptyProgram = '''
|
|
void main(List<String> args) {}
|
|
''';
|
|
|
|
/// A simple async Dart script that when stopped at the line of '// BREAKPOINT'
|
|
/// will contain SDK frames in the call stack.
|
|
const sdkStackFrameProgram = '''
|
|
void main() {
|
|
[0].where((i) {
|
|
return i == 0; $breakpointMarker
|
|
}).toList();
|
|
}
|
|
''';
|
|
|
|
/// A simple Dart script that registers a simple service extension that returns
|
|
/// its params and waits until it is called before exiting.
|
|
const serviceExtensionProgram = '''
|
|
import 'dart:async';
|
|
import 'dart:convert';
|
|
import 'dart:developer';
|
|
|
|
void main(List<String> args) async {
|
|
// Using a completer here causes the VM to quit when the extension is called
|
|
// so use a flag.
|
|
// https://github.com/dart-lang/sdk/issues/47279
|
|
var wasCalled = false;
|
|
registerExtension('ext.service.extension', (method, params) async {
|
|
wasCalled = true;
|
|
return ServiceExtensionResponse.result(jsonEncode(params));
|
|
});
|
|
while (!wasCalled) {
|
|
await Future.delayed(const Duration(milliseconds: 100));
|
|
}
|
|
}
|
|
''';
|
|
|
|
/// A simple Dart script that prints its arguments.
|
|
const simpleArgPrintingProgram = r'''
|
|
void main(List<String> args) async {
|
|
print('Hello!');
|
|
print('World!');
|
|
print('args: $args');
|
|
}
|
|
''';
|
|
|
|
/// A simple Dart script that prints to stderr without throwing/terminating.
|
|
///
|
|
/// The output will contain stack traces include both the supplied file, package
|
|
/// and dart URIs.
|
|
String stackPrintingProgram(
|
|
String outputKind, Uri fileUri, Uri packageUri, Uri dartUri) {
|
|
return '''
|
|
import 'dart:io';
|
|
import '$packageUri';
|
|
|
|
void main(List<String> args) async {
|
|
$outputKind.writeln('Start');
|
|
$outputKind.writeln('#0 main ($fileUri:1:2)');
|
|
$outputKind.writeln('#1 main2 ($packageUri:3:4)');
|
|
$outputKind.writeln('#2 main3 ($dartUri:5:6)');
|
|
$outputKind.write('End');
|
|
await Future.delayed(const Duration(seconds: 1));
|
|
}
|
|
''';
|
|
}
|
|
|
|
/// Returns a simple Dart script that prints the provided string repeatedly.
|
|
String stringPrintingProgram(String text) {
|
|
// jsonEncode the string to get it into a quoted/escaped form that can be
|
|
// embedded in the string.
|
|
final encodedTextString = jsonEncode(text);
|
|
return '''
|
|
import 'dart:async';
|
|
|
|
main() async {
|
|
Timer.periodic(Duration(milliseconds: 10), (_) => printSomething());
|
|
}
|
|
|
|
void printSomething() {
|
|
print($encodedTextString);
|
|
}
|
|
''';
|
|
}
|
|
|
|
/// A simple Dart script that just loops forever sleeping for 1 second each
|
|
/// iteration.
|
|
///
|
|
/// A breakpoint marker is included before the loop.
|
|
const infiniteRunningProgram = '''
|
|
void main(List<String> args) async {
|
|
print('Looping'); $breakpointMarker
|
|
while (true) {
|
|
await Future.delayed(const Duration(seconds: 1));
|
|
}
|
|
}
|
|
''';
|
|
|
|
/// A Dart script that loops forever sleeping for 1 second each
|
|
/// iteration.
|
|
///
|
|
/// A top-level String variable `myGlobal` is available with the value
|
|
/// `"Hello, world!"`.
|
|
///
|
|
/// Requires the 'foo' package.
|
|
const globalEvaluationProgram = '''
|
|
import 'package:foo/foo.dart';
|
|
|
|
var myGlobal = 'Hello, world!';
|
|
void main(List<String> args) async {
|
|
while (true) {
|
|
foo();
|
|
print('.');
|
|
await Future.delayed(const Duration(seconds: 1));
|
|
}
|
|
}
|
|
''';
|
|
|
|
/// A simple async Dart script that when stopped at the line of '// BREAKPOINT'
|
|
/// will contain multiple stack frames across some async boundaries.
|
|
const simpleAsyncProgram = '''
|
|
import 'dart:async';
|
|
|
|
Future<void> main() async {
|
|
await one();
|
|
}
|
|
|
|
Future<void> one() async {
|
|
await two();
|
|
}
|
|
|
|
Future<void> two() async {
|
|
await three();
|
|
}
|
|
|
|
Future<void> three() async {
|
|
await Future.delayed(const Duration(microseconds: 1));
|
|
four();
|
|
}
|
|
|
|
void four() {
|
|
print('!'); $breakpointMarker
|
|
}
|
|
''';
|
|
|
|
/// A simple Dart script that should run with no errors and contains a comment
|
|
/// marker '// BREAKPOINT' for use in tests that require stopping at a breakpoint
|
|
/// but require no other context.
|
|
const simpleBreakpointProgram = '''
|
|
void main(List<String> args) async {
|
|
print('Hello!'); $breakpointMarker
|
|
}
|
|
''';
|
|
|
|
/// A simple Dart script that prints "Hello" and then "World" with a breakpoint
|
|
/// on the line that prints "World". By restarting from the parent frame after
|
|
/// hitting the breakpoint, the output would be "Hello", "Hello", "World".
|
|
const restartFrameProgram = '''
|
|
void main(List<String> args) {
|
|
printHello();
|
|
}
|
|
|
|
void printHello() {
|
|
print('Hello');
|
|
print('World'); $breakpointMarker
|
|
}
|
|
''';
|
|
|
|
/// A simple Dart script that prints the numbers from 1 to 5.
|
|
///
|
|
/// A breakpoint marker is on the line that prints '1' and the subsequent 4
|
|
/// lines are valid targets for breakpoints.
|
|
const simpleMultiBreakpointProgram = '''
|
|
void main(List<String> args) async {
|
|
print('1'); $breakpointMarker
|
|
print('2');
|
|
print('3');
|
|
print('4');
|
|
print('5');
|
|
}
|
|
''';
|
|
|
|
/// A program that immediately pauses with `debugger()`.
|
|
const debuggerPauseProgram = '''
|
|
import 'dart:developer';
|
|
|
|
void main(List<String> args) async {
|
|
debugger();
|
|
}
|
|
''';
|
|
|
|
/// A program that immediately pauses with `debugger()` and then has 5 print
|
|
/// statements.
|
|
///
|
|
/// Used for verifying breaking and stepping behaviour.
|
|
const debuggerPauseAndPrintManyProgram = '''
|
|
import 'dart:developer';
|
|
|
|
void main(List<String> args) async {
|
|
debugger();
|
|
print('1');
|
|
print('2');
|
|
print('3');
|
|
print('4');
|
|
print('5');
|
|
}
|
|
''';
|
|
|
|
final simpleBreakpointProgramWith50ExtraLines = '''
|
|
void main(List<String> args) async {
|
|
print('Hello!'); $breakpointMarker
|
|
${'await null;\n' * 50}
|
|
}
|
|
''';
|
|
|
|
/// A Dart script that uses [Isolate.run] to run a short-lived isolate and has
|
|
/// a `debugger()` call after the isolate completes to ensure the app does not
|
|
/// immediately exit.
|
|
const isolateSpawningProgram = '''
|
|
import 'dart:developer';
|
|
import 'dart:isolate';
|
|
|
|
Future<void> main() async {
|
|
await Isolate.run(_compute);
|
|
debugger();
|
|
}
|
|
|
|
Future<void> _compute() async {}
|
|
''';
|
|
|
|
/// A simple Dart script that has a blank line before its breakpoint, used to
|
|
/// ensure breakpoints that resolve to the same place are handled correctly.
|
|
const simpleBreakpointWithLeadingBlankLineProgram = '''
|
|
void main(List<String> args) async {
|
|
|
|
print('Hello!'); $breakpointMarker
|
|
}
|
|
''';
|
|
|
|
/// A simple Dart script that has a breakpoint and an exception used for
|
|
/// testing whether breakpoints and exceptions are being paused on (for example
|
|
/// during detach where they should not).
|
|
const simpleBreakpointAndThrowProgram = '''
|
|
void main(List<String> args) async {
|
|
print('Hello!'); $breakpointMarker
|
|
throw 'error';
|
|
}
|
|
''';
|
|
|
|
/// A simple Dart script that throws an error and catches it in user code.
|
|
const simpleCaughtErrorProgram = r'''
|
|
void main(List<String> args) async {
|
|
try {
|
|
throw 'error';
|
|
} catch (e) {
|
|
print('Caught!');
|
|
}
|
|
}
|
|
''';
|
|
|
|
/// A simple package:test script that has a single group named 'group' with
|
|
/// tests named 'passing', 'failing' and 'skipped' respectively.
|
|
///
|
|
/// The 'passing' test contains a [breakpointMarker].
|
|
const simpleTestProgram = '''
|
|
import 'package:test/test.dart';
|
|
|
|
void main() {
|
|
group('group 1', () {
|
|
test('passing test', () {
|
|
expect(1, equals(1)); $breakpointMarker
|
|
});
|
|
test('failing test', () {
|
|
expect(1, equals(2));
|
|
});
|
|
test('skipped test', () {
|
|
expect(1, equals(2));
|
|
}, skip: true);
|
|
});
|
|
}
|
|
''';
|
|
|
|
/// A simple package:test script that has a multiple tests but one has
|
|
/// `solo: true` so it's the only one run.
|
|
const soloTestProgram = '''
|
|
import 'package:test/test.dart';
|
|
|
|
void main() {
|
|
test('solo test', () {
|
|
expect(1, equals(1));
|
|
}, solo: true);
|
|
test('other test', () {
|
|
expect(1, equals(1));
|
|
});
|
|
}
|
|
''';
|
|
|
|
/// A simple package:test script with a single failing test.
|
|
const simpleFailingTestProgram = '''
|
|
import 'package:test/test.dart';
|
|
|
|
void main() {
|
|
test('failing test', () {
|
|
expect(1, equals(2));
|
|
});
|
|
}
|
|
''';
|
|
|
|
/// A program that pauses the debugger and when resumed, spawns a second
|
|
/// isolate. This can be used to ensure correct breakpoint resolution
|
|
/// in new isolates where breakpoint IDs may overlap with IDs already used by
|
|
/// other isolates.
|
|
///
|
|
/// Two different lines are marked '// BREAKPOINT 1' and '// BREAKPOINT 2'.
|
|
const multiIsolateBreakpointResolutionProgram = '''
|
|
import 'dart:developer';
|
|
import 'dart:isolate';
|
|
|
|
Future<void> main() async {
|
|
print('1'); // BREAKPOINT 1
|
|
print('2'); // BREAKPOINT 2
|
|
debugger();
|
|
Isolate.run(f);
|
|
}
|
|
|
|
void f() {
|
|
debugger();
|
|
}
|
|
|
|
''';
|
|
|
|
final simpleTestBreakpointProgramWith50ExtraLines = '''
|
|
import 'package:test/test.dart';
|
|
|
|
void main() {
|
|
group('group 1', () {
|
|
test('passing test', () async {
|
|
expect(1, equals(1)); $breakpointMarker
|
|
${'await null;\n' * 50}
|
|
});
|
|
});
|
|
}
|
|
''';
|
|
|
|
/// A simple test that prints the numbers from 1 to 5.
|
|
///
|
|
/// A breakpoint marker is on the line that prints '1' and the subsequent 4
|
|
/// lines are valid targets for breakpoints.
|
|
const simpleTestMultiBreakpointProgram = '''
|
|
import 'package:test/test.dart';
|
|
|
|
void main() {
|
|
group('group 1', () {
|
|
test('passing test', () {
|
|
print('1'); $breakpointMarker
|
|
print('2');
|
|
print('3');
|
|
print('4');
|
|
print('5');
|
|
expect(1, equals(1));
|
|
});
|
|
});
|
|
}
|
|
''';
|
|
|
|
/// Matches for the expected output of [simpleTestProgram].
|
|
final simpleTestProgramExpectedOutput = [
|
|
// First test
|
|
'✓ group 1 passing test',
|
|
// Second test
|
|
'Expected: <2>',
|
|
' Actual: <1>',
|
|
// These lines contain paths, so just check the non-path parts.
|
|
allOf(startsWith('package:matcher'), endsWith('expect')),
|
|
endsWith('main.<fn>.<fn>'),
|
|
'✖ group 1 failing test',
|
|
'! group 1 skipped test',
|
|
// Exit
|
|
'',
|
|
'Exited (1).',
|
|
];
|
|
|
|
/// A simple Dart script that throws in user code.
|
|
const simpleThrowingProgram = r'''
|
|
void main(List<String> args) async {
|
|
throw Exception('error text');
|
|
}
|
|
''';
|
|
|
|
/// A small script that throws an exception asynchronously two levels deep
|
|
/// from main (main->outer->inner).
|
|
const asyncExceptionProgram = r'''
|
|
Future<void> main() async => await outer();
|
|
|
|
Future<void> outer() async => await inner();
|
|
|
|
Future<void> inner() async => throw UnimplementedError('NYI');
|
|
''';
|
|
|
|
/// A simple Dart script that sends a `navigate` event to the `ToolEvent`
|
|
/// stream.
|
|
const simpleToolEventProgram = r'''
|
|
import 'dart:developer';
|
|
|
|
void main(List<String> args) async {
|
|
postEvent(
|
|
'navigate',
|
|
{
|
|
'uri': 'file:///file.dart',
|
|
},
|
|
stream: 'ToolEvent',
|
|
);
|
|
}
|
|
''';
|
|
|
|
/// A simple Dart script that sends a `navigate` event to the `ToolEvent`
|
|
/// stream using a dart:core URI.
|
|
const simpleToolEventWithDartCoreUriProgram = r'''
|
|
import 'dart:developer';
|
|
|
|
void main(List<String> args) async {
|
|
postEvent(
|
|
'navigate',
|
|
{
|
|
'uri': 'dart:core',
|
|
},
|
|
stream: 'ToolEvent',
|
|
);
|
|
// resolving postEvent URIs is async, so we need to ensure the program
|
|
// does not immediately terminate. The test script should terminate it when
|
|
// it has had the event.
|
|
await Future.delayed(const Duration(seconds: 10));
|
|
}
|
|
''';
|
|
|
|
/// A marker used in some test scripts/tests for where to expected steps.
|
|
const stepMarker = '// STEP';
|