Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.

Commit e8306b5

Browse files
authored
[integration_test] add support to get timeline (#2947)
1 parent 9d1f131 commit e8306b5

File tree

5 files changed

+151
-13
lines changed

5 files changed

+151
-13
lines changed

packages/integration_test/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 0.8.2
2+
3+
* Add support to get timeline.
4+
15
## 0.8.1
26

37
* Show stack trace of widget test errors on the platform side

packages/integration_test/example/test_driver/example_integration_io.dart

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,22 +13,29 @@ import 'package:integration_test/integration_test.dart';
1313
import 'package:integration_test_example/main.dart' as app;
1414

1515
void main() {
16-
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
16+
final IntegrationTestWidgetsFlutterBinding binding =
17+
IntegrationTestWidgetsFlutterBinding.ensureInitialized()
18+
as IntegrationTestWidgetsFlutterBinding;
1719
testWidgets('verify text', (WidgetTester tester) async {
1820
// Build our app and trigger a frame.
1921
app.main();
2022

21-
// Trigger a frame.
22-
await tester.pumpAndSettle();
23+
// Trace the timeline of the following operation. The timeline result will
24+
// be written to `build/integration_response_data.json` with the key
25+
// `timeline`.
26+
await binding.traceAction(() async {
27+
// Trigger a frame.
28+
await tester.pumpAndSettle();
2329

24-
// Verify that platform version is retrieved.
25-
expect(
26-
find.byWidgetPredicate(
27-
(Widget widget) =>
28-
widget is Text &&
29-
widget.data.startsWith('Platform: ${Platform.operatingSystem}'),
30-
),
31-
findsOneWidget,
32-
);
30+
// Verify that platform version is retrieved.
31+
expect(
32+
find.byWidgetPredicate(
33+
(Widget widget) =>
34+
widget is Text &&
35+
widget.data.startsWith('Platform: ${Platform.operatingSystem}'),
36+
),
37+
findsOneWidget,
38+
);
39+
});
3340
});
3441
}

packages/integration_test/lib/integration_test.dart

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,15 @@
33
// found in the LICENSE file.
44

55
import 'dart:async';
6+
import 'dart:developer' as developer;
67

78
import 'package:flutter/rendering.dart';
89
import 'package:flutter_test/flutter_test.dart';
910
import 'package:flutter/foundation.dart';
1011
import 'package:flutter/services.dart';
1112
import 'package:flutter/widgets.dart';
13+
import 'package:vm_service/vm_service.dart' as vm;
14+
import 'package:vm_service/vm_service_io.dart' as vm_io;
1215

1316
import 'common.dart';
1417
import '_extension_io.dart' if (dart.library.html) '_extension_web.dart';
@@ -191,4 +194,92 @@ class IntegrationTestWidgetsFlutterBinding
191194
);
192195
results[description] ??= _success;
193196
}
197+
198+
vm.VmService _vmService;
199+
200+
/// Initialize the [vm.VmService] settings for the timeline.
201+
@visibleForTesting
202+
Future<void> enableTimeline({
203+
List<String> streams = const <String>['all'],
204+
@visibleForTesting vm.VmService vmService,
205+
}) async {
206+
assert(streams != null);
207+
assert(streams.isNotEmpty);
208+
if (vmService != null) {
209+
_vmService = vmService;
210+
}
211+
if (_vmService == null) {
212+
final developer.ServiceProtocolInfo info =
213+
await developer.Service.getInfo();
214+
assert(info.serverUri != null);
215+
_vmService = await vm_io.vmServiceConnectUri(
216+
'ws://localhost:${info.serverUri.port}${info.serverUri.path}ws',
217+
);
218+
}
219+
await _vmService.setVMTimelineFlags(streams);
220+
}
221+
222+
/// Runs [action] and returns a [vm.Timeline] trace for it.
223+
///
224+
/// Waits for the `Future` returned by [action] to complete prior to stopping
225+
/// the trace.
226+
///
227+
/// The `streams` parameter limits the recorded timeline event streams to only
228+
/// the ones listed. By default, all streams are recorded.
229+
/// See `timeline_streams` in
230+
/// [Dart-SDK/runtime/vm/timeline.cc](https://github.com/dart-lang/sdk/blob/master/runtime/vm/timeline.cc)
231+
///
232+
/// If [retainPriorEvents] is true, retains events recorded prior to calling
233+
/// [action]. Otherwise, prior events are cleared before calling [action]. By
234+
/// default, prior events are cleared.
235+
Future<vm.Timeline> traceTimeline(
236+
Future<dynamic> action(), {
237+
List<String> streams = const <String>['all'],
238+
bool retainPriorEvents = false,
239+
}) async {
240+
await enableTimeline(streams: streams);
241+
if (retainPriorEvents) {
242+
await action();
243+
return await _vmService.getVMTimeline();
244+
}
245+
246+
await _vmService.clearVMTimeline();
247+
final vm.Timestamp startTime = await _vmService.getVMTimelineMicros();
248+
await action();
249+
final vm.Timestamp endTime = await _vmService.getVMTimelineMicros();
250+
return await _vmService.getVMTimeline(
251+
timeOriginMicros: startTime.timestamp,
252+
timeExtentMicros: endTime.timestamp,
253+
);
254+
}
255+
256+
/// This is a convenience wrap of [traceTimeline] and send the result back to
257+
/// the host for the [flutter_driver] style tests.
258+
///
259+
/// This records the timeline during `action` and adds the result to
260+
/// [reportData] with `reportKey`. [reportData] contains the extra information
261+
/// of the test other than test success/fail. It will be passed back to the
262+
/// host and be processed by the [ResponseDataCallback] defined in
263+
/// [integrationDriver]. By default it will be written to
264+
/// `build/integration_response_data.json` with the key `timeline`.
265+
///
266+
/// For tests with multiple calls of this method, `reportKey` needs to be a
267+
/// unique key, otherwise the later result will override earlier one.
268+
///
269+
/// The `streams` and `retainPriorEvents` parameters are passed as-is to
270+
/// [traceTimeline].
271+
Future<void> traceAction(
272+
Future<dynamic> action(), {
273+
List<String> streams = const <String>['all'],
274+
bool retainPriorEvents = false,
275+
String reportKey = 'timeline',
276+
}) async {
277+
vm.Timeline timeline = await traceTimeline(
278+
action,
279+
streams: streams,
280+
retainPriorEvents: retainPriorEvents,
281+
);
282+
reportData ??= <String, dynamic>{};
283+
reportData[reportKey] = timeline.toJson();
284+
}
194285
}

packages/integration_test/pubspec.yaml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name: integration_test
22
description: Runs tests that use the flutter_test API as integration tests.
3-
version: 0.8.1
3+
version: 0.8.2
44
homepage: https://github.com/flutter/plugins/tree/master/packages/integration_test
55

66
environment:
@@ -15,9 +15,11 @@ dependencies:
1515
flutter_test:
1616
sdk: flutter
1717
path: ^1.6.4
18+
vm_service: ^4.2.0
1819

1920
dev_dependencies:
2021
pedantic: ^1.8.0
22+
mockito: ^4.1.1
2123

2224
flutter:
2325
plugin:

packages/integration_test/test/binding_test.dart

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,18 @@
1+
import 'dart:convert';
2+
13
import 'package:flutter/material.dart';
24

35
import 'package:integration_test/integration_test.dart';
46
import 'package:integration_test/common.dart';
57
import 'package:flutter_test/flutter_test.dart';
8+
import 'package:mockito/mockito.dart';
9+
import 'package:vm_service/vm_service.dart' as vm;
10+
11+
vm.Timeline _ktimelines = vm.Timeline(
12+
traceEvents: <vm.TimelineEvent>[],
13+
timeOriginMicros: 100,
14+
timeExtentMicros: 200,
15+
);
616

717
void main() async {
818
Future<Map<String, dynamic>> request;
@@ -14,10 +24,21 @@ void main() async {
1424
final IntegrationTestWidgetsFlutterBinding integrationBinding =
1525
binding as IntegrationTestWidgetsFlutterBinding;
1626

27+
MockVM mockVM;
28+
List<int> clockTimes = [100, 200];
29+
1730
setUp(() {
1831
request = integrationBinding.callback(<String, String>{
1932
'command': 'request_data',
2033
});
34+
mockVM = MockVM();
35+
when(mockVM.getVMTimeline(
36+
timeOriginMicros: anyNamed('timeOriginMicros'),
37+
timeExtentMicros: anyNamed('timeExtentMicros'),
38+
)).thenAnswer((_) => Future.value(_ktimelines));
39+
when(mockVM.getVMTimelineMicros()).thenAnswer(
40+
(_) => Future.value(vm.Timestamp(timestamp: clockTimes.removeAt(0))),
41+
);
2142
});
2243

2344
testWidgets('Run Integration app', (WidgetTester tester) async {
@@ -53,6 +74,17 @@ void main() async {
5374
expect(widgetCenter.dx, windowCenterX);
5475
expect(widgetCenter.dy, windowCenterY);
5576
});
77+
78+
testWidgets('Test traceAction', (WidgetTester tester) async {
79+
await integrationBinding.enableTimeline(vmService: mockVM);
80+
await integrationBinding.traceAction(() async {});
81+
expect(integrationBinding.reportData, isNotNull);
82+
expect(integrationBinding.reportData.containsKey('timeline'), true);
83+
expect(
84+
json.encode(integrationBinding.reportData['timeline']),
85+
json.encode(_ktimelines),
86+
);
87+
});
5688
});
5789

5890
tearDownAll(() async {
@@ -66,3 +98,5 @@ void main() async {
6698
assert(result.data['answer'] == 42);
6799
});
68100
}
101+
102+
class MockVM extends Mock implements vm.VmService {}

0 commit comments

Comments
 (0)