Skip to content

Commit 2994d35

Browse files
authored
Add some a basic debug stepping tests (flutter#24515)
1 parent cc23a7b commit 2994d35

File tree

3 files changed

+125
-0
lines changed

3 files changed

+125
-0
lines changed
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// Copyright 2018 The Chromium Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import 'package:file/file.dart';
6+
import 'package:flutter_tools/src/base/file_system.dart';
7+
8+
import '../src/common.dart';
9+
import 'test_data/stepping_project.dart';
10+
import 'test_driver.dart';
11+
import 'test_utils.dart';
12+
13+
void main() {
14+
group('debugger', () {
15+
Directory tempDir;
16+
final SteppingProject _project = SteppingProject();
17+
FlutterRunTestDriver _flutter;
18+
19+
setUp(() async {
20+
tempDir = createResolvedTempDirectorySync();
21+
await _project.setUpIn(tempDir);
22+
_flutter = FlutterRunTestDriver(tempDir);
23+
});
24+
25+
tearDown(() async {
26+
await _flutter.stop();
27+
tryToDelete(tempDir);
28+
});
29+
30+
test('can step over statements', () async {
31+
await _flutter.run(withDebugger: true);
32+
33+
// Stop at the initial breakpoint that the expected steps are based on.
34+
await _flutter.breakAt(_project.breakpointUri, _project.breakpointLine, restart: true);
35+
36+
// Issue 5 steps, ensuring that we end up on the annotated lines each time.
37+
for (int i = 1; i <= _project.numberOfSteps; i++) {
38+
await _flutter.stepOverOrOverAsyncSuspension();
39+
final SourcePosition location = await _flutter.getSourceLocation();
40+
final int actualLine = location.line;
41+
42+
// Get the line we're expected to stop at by searching for the comment
43+
// within the source code.
44+
final int expectedLine = _project.lineForStep(i);
45+
46+
expect(actualLine, equals(expectedLine),
47+
reason: 'After $i steps, debugger should stop at $expectedLine but stopped at $actualLine');
48+
}
49+
});
50+
}, timeout: const Timeout.factor(3));
51+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// Copyright 2018 The Chromium Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import 'project.dart';
6+
7+
class SteppingProject extends Project {
8+
@override
9+
final String pubspec = '''
10+
name: test
11+
dependencies:
12+
flutter:
13+
sdk: flutter
14+
''';
15+
16+
@override
17+
final String main = r'''
18+
import 'dart:async';
19+
20+
import 'package:flutter/material.dart';
21+
22+
void main() => runApp(new MyApp());
23+
24+
class MyApp extends StatefulWidget {
25+
@override
26+
_MyAppState createState() => _MyAppState();
27+
}
28+
29+
class _MyAppState extends State<MyApp> {
30+
@override
31+
void initState() {
32+
doAsyncStuff();
33+
super.initState();
34+
}
35+
36+
Future<void> doAsyncStuff() async {
37+
print("test"); // BREAKPOINT
38+
await new Future.value(true); // STEP 1
39+
await new Future.microtask(() => true); // STEP 2 // STEP 3
40+
await new Future.delayed(const Duration(milliseconds: 1)); // STEP 4 // STEP 5
41+
print("done!"); // STEP 6
42+
}
43+
44+
@override
45+
Widget build(BuildContext context) {
46+
return new MaterialApp(
47+
title: 'Flutter Demo',
48+
home: new Container(),
49+
);
50+
}
51+
}
52+
''';
53+
54+
int lineForStep(int i) => lineContaining(main, '// STEP $i');
55+
56+
final int numberOfSteps = 6;
57+
}

packages/flutter_tools/test/integration/test_driver.dart

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,8 +163,17 @@ abstract class FlutterTestDriver {
163163
message: 'Isolate did not pause');
164164
}
165165

166+
Future<bool> isAtAsyncSuspension() async {
167+
final Isolate isolate = await _getFlutterIsolate();
168+
return isolate.pauseEvent.atAsyncSuspension == true;
169+
}
170+
166171
Future<Isolate> resume({bool wait = true}) => _resume(wait: wait);
167172
Future<Isolate> stepOver({bool wait = true}) => _resume(step: StepOption.kOver, wait: wait);
173+
Future<Isolate> stepOverAsync({ bool wait = true }) => _resume(step: StepOption.kOverAsyncSuspension, wait: wait);
174+
Future<Isolate> stepOverOrOverAsyncSuspension({ bool wait = true }) async {
175+
return (await isAtAsyncSuspension()) ? stepOverAsync(wait: wait) : stepOver(wait: wait);
176+
}
168177
Future<Isolate> stepInto({bool wait = true}) => _resume(step: StepOption.kInto, wait: wait);
169178
Future<Isolate> stepOut({bool wait = true}) => _resume(step: StepOption.kOut, wait: wait);
170179

@@ -366,6 +375,14 @@ class FlutterRunTestDriver extends FlutterTestDriver {
366375
_vmService.streamListen('Debug'),
367376
]);
368377

378+
// On hot restarts, the isolate ID we have for the Flutter thread will
379+
// exit so we need to invalidate our cached ID.
380+
_vmService.onIsolateEvent.listen((Event event) {
381+
if (event.kind == EventKind.kIsolateExit && event.isolate.id == _flutterIsolateId) {
382+
_flutterIsolateId = null;
383+
}
384+
});
385+
369386
// Because we start paused, resume so the app is in a "running" state as
370387
// expected by tests. Tests will reload/restart as required if they need
371388
// to hit breakpoints, etc.

0 commit comments

Comments
 (0)