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

[integration_test] add support to get timeline #2947

Merged
merged 5 commits into from
Aug 28, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/integration_test/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 0.8.2

* Add support to get timeline.

## 0.8.1

* Show stack trace of widget test errors on the platform side
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,29 @@ import 'package:integration_test/integration_test.dart';
import 'package:integration_test_example/main.dart' as app;

void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
final IntegrationTestWidgetsFlutterBinding binding =
IntegrationTestWidgetsFlutterBinding.ensureInitialized()
as IntegrationTestWidgetsFlutterBinding;
testWidgets('verify text', (WidgetTester tester) async {
// Build our app and trigger a frame.
app.main();

// Trigger a frame.
await tester.pumpAndSettle();
// Trace the timeline of the following operation. The timeline result will
// be written to `build/integration_response_data.json` with the key
// `timeline`.
await binding.traceAction(() async {
CareF marked this conversation as resolved.
Show resolved Hide resolved
// Trigger a frame.
await tester.pumpAndSettle();

// Verify that platform version is retrieved.
expect(
find.byWidgetPredicate(
(Widget widget) =>
widget is Text &&
widget.data.startsWith('Platform: ${Platform.operatingSystem}'),
),
findsOneWidget,
);
// Verify that platform version is retrieved.
expect(
find.byWidgetPredicate(
(Widget widget) =>
widget is Text &&
widget.data.startsWith('Platform: ${Platform.operatingSystem}'),
),
findsOneWidget,
);
});
});
}
91 changes: 91 additions & 0 deletions packages/integration_test/lib/integration_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@
// found in the LICENSE file.

import 'dart:async';
import 'dart:developer' as developer;

import 'package:flutter/rendering.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:vm_service/vm_service.dart' as vm;
import 'package:vm_service/vm_service_io.dart' as vm_io;

import 'common.dart';
import '_extension_io.dart' if (dart.library.html) '_extension_web.dart';
Expand Down Expand Up @@ -191,4 +194,92 @@ class IntegrationTestWidgetsFlutterBinding
);
results[description] ??= _success;
}

vm.VmService _vmService;

/// Initialize the [vm.VmService] settings for the timeline.
@visibleForTesting
Future<void> enableTimeline({
List<String> streams = const <String>['all'],
@visibleForTesting vm.VmService vmService,
}) async {
assert(streams != null);
assert(streams.isNotEmpty);
if (vmService != null) {
_vmService = vmService;
}
if (_vmService == null) {
final developer.ServiceProtocolInfo info =
await developer.Service.getInfo();
assert(info.serverUri != null);
_vmService = await vm_io.vmServiceConnectUri(
'ws://localhost:${info.serverUri.port}${info.serverUri.path}ws',
);
}
await _vmService.setVMTimelineFlags(streams);
}

/// Runs [action] and returns a [vm.Timeline] trace for it.
///
/// Waits for the `Future` returned by [action] to complete prior to stopping
/// the trace.
///
/// The `streams` parameter limits the recorded timeline event streams to only
/// the ones listed. By default, all streams are recorded.
/// See `timeline_streams` in
/// [Dart-SDK/runtime/vm/timeline.cc](https://github.com/dart-lang/sdk/blob/master/runtime/vm/timeline.cc)
///
/// If [retainPriorEvents] is true, retains events recorded prior to calling
/// [action]. Otherwise, prior events are cleared before calling [action]. By
/// default, prior events are cleared.
Future<vm.Timeline> traceTimeline(
Future<dynamic> action(), {
List<String> streams = const <String>['all'],
bool retainPriorEvents = false,
}) async {
await enableTimeline(streams: streams);
if (retainPriorEvents) {
await action();
return await _vmService.getVMTimeline();
}

await _vmService.clearVMTimeline();
final vm.Timestamp startTime = await _vmService.getVMTimelineMicros();
await action();
final vm.Timestamp endTime = await _vmService.getVMTimelineMicros();
return await _vmService.getVMTimeline(
timeOriginMicros: startTime.timestamp,
timeExtentMicros: endTime.timestamp,
);
}

/// This is a convenience wrap of [traceTimeline] and send the result back to
/// the host for the [flutter_driver] style tests.
///
/// This records the timeline during `action` and adds the result to
/// [reportData] with `reportKey`. [reportData] contains the extra information
/// of the test other than test success/fail. It will be passed back to the
/// host and be processed by the [ResponseDataCallback] defined in
/// [integrationDriver]. By default it will be written to
/// `build/integration_response_data.json` with the key `timeline`.
///
/// For tests with multiple calls of this method, `reportKey` needs to be a
/// unique key, otherwise the later result will override earlier one.
///
/// The `streams` and `retainPriorEvents` parameters are passed as-is to
/// [traceTimeline].
Future<void> traceAction(
Future<dynamic> action(), {
List<String> streams = const <String>['all'],
bool retainPriorEvents = false,
String reportKey = 'timeline',
}) async {
vm.Timeline timeline = await traceTimeline(
action,
streams: streams,
retainPriorEvents: retainPriorEvents,
);
reportData ??= <String, dynamic>{};
reportData[reportKey] = timeline.toJson();
}
}
4 changes: 3 additions & 1 deletion packages/integration_test/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: integration_test
description: Runs tests that use the flutter_test API as integration tests.
version: 0.8.1
version: 0.8.2
homepage: https://github.com/flutter/plugins/tree/master/packages/integration_test

environment:
Expand All @@ -15,9 +15,11 @@ dependencies:
flutter_test:
sdk: flutter
path: ^1.6.4
vm_service: ^4.2.0

dev_dependencies:
pedantic: ^1.8.0
mockito: ^4.1.1

flutter:
plugin:
Expand Down
34 changes: 34 additions & 0 deletions packages/integration_test/test/binding_test.dart
Original file line number Diff line number Diff line change
@@ -1,8 +1,18 @@
import 'dart:convert';

import 'package:flutter/material.dart';

import 'package:integration_test/integration_test.dart';
import 'package:integration_test/common.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/mockito.dart';
import 'package:vm_service/vm_service.dart' as vm;

vm.Timeline _ktimelines = vm.Timeline(
traceEvents: <vm.TimelineEvent>[],
timeOriginMicros: 100,
timeExtentMicros: 200,
);

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

MockVM mockVM;
List<int> clockTimes = [100, 200];

setUp(() {
request = integrationBinding.callback(<String, String>{
'command': 'request_data',
});
mockVM = MockVM();
when(mockVM.getVMTimeline(
timeOriginMicros: anyNamed('timeOriginMicros'),
timeExtentMicros: anyNamed('timeExtentMicros'),
)).thenAnswer((_) => Future.value(_ktimelines));
when(mockVM.getVMTimelineMicros()).thenAnswer(
(_) => Future.value(vm.Timestamp(timestamp: clockTimes.removeAt(0))),
);
});

testWidgets('Run Integration app', (WidgetTester tester) async {
Expand Down Expand Up @@ -53,6 +74,17 @@ void main() async {
expect(widgetCenter.dx, windowCenterX);
expect(widgetCenter.dy, windowCenterY);
});

testWidgets('Test traceAction', (WidgetTester tester) async {
await integrationBinding.enableTimeline(vmService: mockVM);
await integrationBinding.traceAction(() async {});
expect(integrationBinding.reportData, isNotNull);
expect(integrationBinding.reportData.containsKey('timeline'), true);
expect(
json.encode(integrationBinding.reportData['timeline']),
json.encode(_ktimelines),
);
});
});

tearDownAll(() async {
Expand All @@ -66,3 +98,5 @@ void main() async {
assert(result.data['answer'] == 42);
});
}

class MockVM extends Mock implements vm.VmService {}