Unverified Commit 22db62c1 authored by adazh's avatar adazh Committed by GitHub

Added a composable waitForCondition Driver/extension API. (#38836)

* Added a composable waitForCondition Driver/extension API.

This reverts commit df2db4e5.

* Added SerializableWaitCondition for communicating between driver script on host and extension on device.
parent 9c3e4de4
......@@ -106,52 +106,6 @@ class WaitForAbsentResult extends Result {
Map<String, dynamic> toJson() => <String, dynamic>{};
}
/// A Flutter Driver command that waits until there are no more transient callbacks in the queue.
class WaitUntilNoTransientCallbacks extends Command {
/// Creates a command that waits for there to be no transient callbacks.
const WaitUntilNoTransientCallbacks({ Duration timeout }) : super(timeout: timeout);
/// Deserializes this command from the value generated by [serialize].
WaitUntilNoTransientCallbacks.deserialize(Map<String, String> json)
: super.deserialize(json);
@override
String get kind => 'waitUntilNoTransientCallbacks';
}
/// A Flutter Driver command that waits until the frame is synced.
class WaitUntilNoPendingFrame extends Command {
/// Creates a command that waits until there's no pending frame scheduled.
const WaitUntilNoPendingFrame({ Duration timeout }) : super(timeout: timeout);
/// Deserializes this command from the value generated by [serialize].
WaitUntilNoPendingFrame.deserialize(Map<String, String> json)
: super.deserialize(json);
@override
String get kind => 'waitUntilNoPendingFrame';
}
/// A Flutter Driver command that waits until the Flutter engine rasterizes the
/// first frame.
///
/// {@template flutter.frame_rasterized_vs_presented}
/// Usually, the time that a frame is rasterized is very close to the time that
/// it gets presented on the display. Specifically, rasterization is the last
/// expensive phase of a frame that's still in Flutter's control.
/// {@endtemplate}
class WaitUntilFirstFrameRasterized extends Command {
/// Creates this command.
const WaitUntilFirstFrameRasterized({ Duration timeout }) : super(timeout: timeout);
/// Deserializes this command from the value generated by [serialize].
WaitUntilFirstFrameRasterized.deserialize(Map<String, String> json)
: super.deserialize(json);
@override
String get kind => 'waitUntilFirstFrameRasterized';
}
/// Base class for Flutter Driver finders, objects that describe how the driver
/// should search for elements.
abstract class SerializableFinder {
......
This diff is collapsed.
......@@ -28,6 +28,7 @@ import '../common/render_tree.dart';
import '../common/request_data.dart';
import '../common/semantics.dart';
import '../common/text.dart';
import '../common/wait.dart';
import 'common.dart';
import 'timeline.dart';
......@@ -491,12 +492,17 @@ class FlutterDriver {
await _sendCommand(WaitForAbsent(finder, timeout: timeout));
}
/// Waits until the given [waitCondition] is satisfied.
Future<void> waitForCondition(SerializableWaitCondition waitCondition, {Duration timeout}) async {
await _sendCommand(WaitForCondition(waitCondition, timeout: timeout));
}
/// Waits until there are no more transient callbacks in the queue.
///
/// Use this method when you need to wait for the moment when the application
/// becomes "stable", for example, prior to taking a [screenshot].
Future<void> waitUntilNoTransientCallbacks({ Duration timeout }) async {
await _sendCommand(WaitUntilNoTransientCallbacks(timeout: timeout));
await _sendCommand(WaitForCondition(const NoTransientCallbacks(), timeout: timeout));
}
/// Waits until the next [Window.onReportTimings] is called.
......@@ -504,7 +510,7 @@ class FlutterDriver {
/// Use this method to wait for the first frame to be rasterized during the
/// app launch.
Future<void> waitUntilFirstFrameRasterized() async {
await _sendCommand(const WaitUntilFirstFrameRasterized());
await _sendCommand(const WaitForCondition(FirstFrameRasterized()));
}
Future<DriverOffset> _getOffset(SerializableFinder finder, OffsetType type, { Duration timeout }) async {
......
......@@ -29,6 +29,8 @@ import '../common/render_tree.dart';
import '../common/request_data.dart';
import '../common/semantics.dart';
import '../common/text.dart';
import '../common/wait.dart';
import 'wait_conditions.dart';
const String _extensionMethodName = 'driver';
const String _extensionMethod = 'ext.flutter.$_extensionMethodName';
......@@ -112,9 +114,10 @@ class FlutterDriverExtension {
'tap': _tap,
'waitFor': _waitFor,
'waitForAbsent': _waitForAbsent,
'waitUntilNoTransientCallbacks': _waitUntilNoTransientCallbacks,
'waitUntilNoPendingFrame': _waitUntilNoPendingFrame,
'waitUntilFirstFrameRasterized': _waitUntilFirstFrameRasterized,
'waitForCondition': _waitForCondition,
'waitUntilNoTransientCallbacks': _waitUntilNoTransientCallbacks, // ignore: deprecated_member_use_from_same_package
'waitUntilNoPendingFrame': _waitUntilNoPendingFrame, // ignore: deprecated_member_use_from_same_package
'waitUntilFirstFrameRasterized': _waitUntilFirstFrameRasterized, // ignore: deprecated_member_use_from_same_package
'get_semantics_id': _getSemanticsId,
'get_offset': _getOffset,
'get_diagnostics_tree': _getDiagnosticsTree,
......@@ -134,9 +137,10 @@ class FlutterDriverExtension {
'tap': (Map<String, String> params) => Tap.deserialize(params),
'waitFor': (Map<String, String> params) => WaitFor.deserialize(params),
'waitForAbsent': (Map<String, String> params) => WaitForAbsent.deserialize(params),
'waitUntilNoTransientCallbacks': (Map<String, String> params) => WaitUntilNoTransientCallbacks.deserialize(params),
'waitUntilNoPendingFrame': (Map<String, String> params) => WaitUntilNoPendingFrame.deserialize(params),
'waitUntilFirstFrameRasterized': (Map<String, String> params) => WaitUntilFirstFrameRasterized.deserialize(params),
'waitForCondition': (Map<String, String> params) => WaitForCondition.deserialize(params),
'waitUntilNoTransientCallbacks': (Map<String, String> params) => WaitUntilNoTransientCallbacks.deserialize(params), // ignore: deprecated_member_use_from_same_package
'waitUntilNoPendingFrame': (Map<String, String> params) => WaitUntilNoPendingFrame.deserialize(params), // ignore: deprecated_member_use_from_same_package
'waitUntilFirstFrameRasterized': (Map<String, String> params) => WaitUntilFirstFrameRasterized.deserialize(params), // ignore: deprecated_member_use_from_same_package
'get_semantics_id': (Map<String, String> params) => GetSemanticsId.deserialize(params),
'get_offset': (Map<String, String> params) => GetOffset.deserialize(params),
'get_diagnostics_tree': (Map<String, String> params) => GetDiagnosticsTree.deserialize(params),
......@@ -223,6 +227,7 @@ class FlutterDriverExtension {
}
// This can be used to wait for the first frame being rasterized during app launch.
@Deprecated('This method has been deprecated in favor of _waitForCondition.')
Future<Result> _waitUntilFirstFrameRasterized(Command command) async {
await WidgetsBinding.instance.waitUntilFirstFrameRasterized;
return null;
......@@ -370,6 +375,15 @@ class FlutterDriverExtension {
return const WaitForAbsentResult();
}
Future<Result> _waitForCondition(Command command) async {
assert(command != null);
final WaitForCondition waitForConditionCommand = command;
final WaitCondition condition = deserializeCondition(waitForConditionCommand.condition);
await condition.wait();
return null;
}
@Deprecated('This method has been deprecated in favor of _waitForCondition.')
Future<Result> _waitUntilNoTransientCallbacks(Command command) async {
if (SchedulerBinding.instance.transientCallbackCount != 0)
await _waitUntilFrame(() => SchedulerBinding.instance.transientCallbackCount == 0);
......@@ -393,6 +407,9 @@ class FlutterDriverExtension {
/// `set_frame_sync` method. See [FlutterDriver.runUnsynchronized] for more
/// details on how to do this. Note, disabling frame sync will require the
/// test author to use some other method to avoid flakiness.
///
/// This method has been deprecated in favor of [_waitForCondition].
@Deprecated('This method has been deprecated in favor of _waitForCondition.')
Future<Result> _waitUntilNoPendingFrame(Command command) async {
await _waitUntilFrame(() {
return SchedulerBinding.instance.transientCallbackCount == 0
......
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/scheduler.dart';
import 'package:flutter/widgets.dart';
import '../common/wait.dart';
/// Base class for a condition that can be waited upon.
///
/// This class defines the wait logic and runs on device, while
/// [SerializableWaitCondition] takes care of the serialization between the
/// driver script running on the host and the extension running on device.
///
/// If you subclass this, you might also want to implement a [SerializableWaitCondition]
/// that takes care of serialization.
abstract class WaitCondition {
/// Gets the current status of the [condition], executed in the context of the
/// Flutter app:
///
/// * True, if the condition is satisfied.
/// * False otherwise.
///
/// The future returned by [wait] will complete when this [condition] is
/// fulfilled.
bool get condition;
/// Returns a future that completes when [condition] turns true.
Future<void> wait();
}
/// A condition that waits until no transient callbacks are scheduled.
class _InternalNoTransientCallbacksCondition implements WaitCondition {
/// Creates an [InternalNoTransientCallbacksCondition] instance.
const _InternalNoTransientCallbacksCondition();
/// Factory constructor to parse an [InternalNoTransientCallbacksCondition]
/// instance from the given [SerializableWaitCondition] instance.
///
/// The [condition] argument must not be null.
factory _InternalNoTransientCallbacksCondition.deserialize(SerializableWaitCondition condition) {
assert(condition != null);
if (condition.conditionName != 'NoTransientCallbacksCondition')
throw SerializationException('Error occurred during deserializing from the given condition: ${condition.serialize()}');
return const _InternalNoTransientCallbacksCondition();
}
@override
bool get condition => SchedulerBinding.instance.transientCallbackCount == 0;
@override
Future<void> wait() async {
while (!condition) {
await SchedulerBinding.instance.endOfFrame;
}
assert(condition);
}
}
/// A condition that waits until no pending frame is scheduled.
class _InternalNoPendingFrameCondition implements WaitCondition {
/// Creates an [InternalNoPendingFrameCondition] instance.
const _InternalNoPendingFrameCondition();
/// Factory constructor to parse an [InternalNoPendingFrameCondition] instance
/// from the given [SerializableWaitCondition] instance.
///
/// The [condition] argument must not be null.
factory _InternalNoPendingFrameCondition.deserialize(SerializableWaitCondition condition) {
assert(condition != null);
if (condition.conditionName != 'NoPendingFrameCondition')
throw SerializationException('Error occurred during deserializing from the given condition: ${condition.serialize()}');
return const _InternalNoPendingFrameCondition();
}
@override
bool get condition => !SchedulerBinding.instance.hasScheduledFrame;
@override
Future<void> wait() async {
while (!condition) {
await SchedulerBinding.instance.endOfFrame;
}
assert(condition);
}
}
/// A condition that waits until the Flutter engine has rasterized the first frame.
class _InternalFirstFrameRasterizedCondition implements WaitCondition {
/// Creates an [InternalFirstFrameRasterizedCondition] instance.
const _InternalFirstFrameRasterizedCondition();
/// Factory constructor to parse an [InternalNoPendingFrameCondition] instance
/// from the given [SerializableWaitCondition] instance.
///
/// The [condition] argument must not be null.
factory _InternalFirstFrameRasterizedCondition.deserialize(SerializableWaitCondition condition) {
assert(condition != null);
if (condition.conditionName != 'FirstFrameRasterizedCondition')
throw SerializationException('Error occurred during deserializing from the given condition: ${condition.serialize()}');
return const _InternalFirstFrameRasterizedCondition();
}
@override
bool get condition => WidgetsBinding.instance.firstFrameRasterized;
@override
Future<void> wait() async {
await WidgetsBinding.instance.waitUntilFirstFrameRasterized;
assert(condition);
}
}
/// A combined condition that waits until all the given [conditions] are met.
class _InternalCombinedCondition implements WaitCondition {
/// Creates an [InternalCombinedCondition] instance with the given list of
/// [conditions].
///
/// The [conditions] argument must not be null.
const _InternalCombinedCondition(this.conditions)
: assert(conditions != null);
/// Factory constructor to parse an [InternalCombinedCondition] instance from
/// the given [SerializableWaitCondition] instance.
///
/// The [condition] argument must not be null.
factory _InternalCombinedCondition.deserialize(SerializableWaitCondition condition) {
assert(condition != null);
if (condition.conditionName != 'CombinedCondition')
throw SerializationException('Error occurred during deserializing from the given condition: ${condition.serialize()}');
final CombinedCondition combinedCondition = condition;
if (combinedCondition.conditions == null) {
return const _InternalCombinedCondition(<WaitCondition>[]);
}
final List<WaitCondition> conditions = combinedCondition.conditions.map(
(SerializableWaitCondition serializableCondition) => deserializeCondition(serializableCondition)
).toList();
return _InternalCombinedCondition(conditions);
}
/// A list of conditions it waits for.
final List<WaitCondition> conditions;
@override
bool get condition {
return conditions.every((WaitCondition condition) => condition.condition);
}
@override
Future<void> wait() async {
while (!condition) {
for (WaitCondition condition in conditions) {
assert (condition != null);
await condition.wait();
}
}
assert(condition);
}
}
/// Parses a [WaitCondition] or its subclass from the given serializable [waitCondition].
///
/// The [waitCondition] argument must not be null.
WaitCondition deserializeCondition(SerializableWaitCondition waitCondition) {
assert(waitCondition != null);
final String conditionName = waitCondition.conditionName;
switch (conditionName) {
case 'NoTransientCallbacksCondition':
return _InternalNoTransientCallbacksCondition.deserialize(waitCondition);
case 'NoPendingFrameCondition':
return _InternalNoPendingFrameCondition.deserialize(waitCondition);
case 'FirstFrameRasterizedCondition':
return _InternalFirstFrameRasterizedCondition.deserialize(waitCondition);
case 'CombinedCondition':
return _InternalCombinedCondition.deserialize(waitCondition);
}
throw SerializationException(
'Unsupported wait condition $conditionName in ${waitCondition.serialize()}');
}
......@@ -6,6 +6,7 @@ import 'dart:async';
import 'package:flutter_driver/src/common/error.dart';
import 'package:flutter_driver/src/common/health.dart';
import 'package:flutter_driver/src/common/wait.dart';
import 'package:flutter_driver/src/driver/driver.dart';
import 'package:flutter_driver/src/driver/timeline.dart';
import 'package:json_rpc_2/json_rpc_2.dart' as rpc;
......@@ -248,12 +249,42 @@ void main() {
});
});
group('waitForCondition', () {
test('sends the wait for NoPendingFrameCondition command', () async {
when(mockIsolate.invokeExtension(any, any)).thenAnswer((Invocation i) {
expect(i.positionalArguments[1], <String, dynamic>{
'command': 'waitForCondition',
'timeout': _kSerializedTestTimeout,
'conditionName': 'NoPendingFrameCondition',
});
return makeMockResponse(<String, dynamic>{});
});
await driver.waitForCondition(const NoPendingFrame(), timeout: _kTestTimeout);
});
test('sends the waitForCondition of combined conditions command', () async {
when(mockIsolate.invokeExtension(any, any)).thenAnswer((Invocation i) {
expect(i.positionalArguments[1], <String, dynamic>{
'command': 'waitForCondition',
'timeout': _kSerializedTestTimeout,
'conditionName': 'CombinedCondition',
'conditions': '[{"conditionName":"NoPendingFrameCondition"},{"conditionName":"NoTransientCallbacksCondition"}]',
});
return makeMockResponse(<String, dynamic>{});
});
const SerializableWaitCondition combinedCondition =
CombinedCondition(<SerializableWaitCondition>[NoPendingFrame(), NoTransientCallbacks()]);
await driver.waitForCondition(combinedCondition, timeout: _kTestTimeout);
});
});
group('waitUntilNoTransientCallbacks', () {
test('sends the waitUntilNoTransientCallbacks command', () async {
when(mockIsolate.invokeExtension(any, any)).thenAnswer((Invocation i) {
expect(i.positionalArguments[1], <String, dynamic>{
'command': 'waitUntilNoTransientCallbacks',
'command': 'waitForCondition',
'timeout': _kSerializedTestTimeout,
'conditionName': 'NoTransientCallbacksCondition',
});
return makeMockResponse(<String, dynamic>{});
});
......@@ -261,6 +292,19 @@ void main() {
});
});
group('waitUntilFirstFrameRasterized', () {
test('sends the waitUntilFirstFrameRasterized command', () async {
when(mockIsolate.invokeExtension(any, any)).thenAnswer((Invocation i) {
expect(i.positionalArguments[1], <String, dynamic>{
'command': 'waitForCondition',
'conditionName': 'FirstFrameRasterizedCondition',
});
return makeMockResponse(<String, dynamic>{});
});
await driver.waitUntilFirstFrameRasterized();
});
});
group('getOffset', () {
test('requires a target reference', () async {
expect(driver.getCenter(null), throwsA(isInstanceOf<DriverError>()));
......
......@@ -12,6 +12,7 @@ import 'package:flutter_driver/src/common/find.dart';
import 'package:flutter_driver/src/common/geometry.dart';
import 'package:flutter_driver/src/common/request_data.dart';
import 'package:flutter_driver/src/common/text.dart';
import 'package:flutter_driver/src/common/wait.dart';
import 'package:flutter_driver/src/extension/extension.dart';
import 'package:flutter_test/flutter_test.dart';
......@@ -28,18 +29,18 @@ void main() {
});
testWidgets('returns immediately when transient callback queue is empty', (WidgetTester tester) async {
extension.call(const WaitUntilNoTransientCallbacks().serialize())
.then<void>(expectAsync1((Map<String, dynamic> r) {
result = r;
}));
extension.call(const WaitUntilNoTransientCallbacks().serialize()) // ignore: deprecated_member_use_from_same_package
.then<void>(expectAsync1((Map<String, dynamic> r) {
result = r;
}));
await tester.idle();
expect(
result,
<String, dynamic>{
'isError': false,
'response': null,
},
result,
<String, dynamic>{
'isError': false,
'response': null,
},
);
});
......@@ -48,10 +49,10 @@ void main() {
// Intentionally blank. We only care about existence of a callback.
});
extension.call(const WaitUntilNoTransientCallbacks().serialize())
.then<void>(expectAsync1((Map<String, dynamic> r) {
result = r;
}));
extension.call(const WaitUntilNoTransientCallbacks().serialize()) // ignore: deprecated_member_use_from_same_package
.then<void>(expectAsync1((Map<String, dynamic> r) {
result = r;
}));
// Nothing should happen until the next frame.
await tester.idle();
......@@ -60,11 +61,11 @@ void main() {
// NOW we should receive the result.
await tester.pump();
expect(
result,
<String, dynamic>{
'isError': false,
'response': null,
},
result,
<String, dynamic>{
'isError': false,
'response': null,
},
);
});
......@@ -76,6 +77,176 @@ void main() {
});
});
group('waitForCondition', () {
FlutterDriverExtension extension;
Map<String, dynamic> result;
int messageId = 0;
final List<String> log = <String>[];
setUp(() {
result = null;
extension = FlutterDriverExtension((String message) async { log.add(message); return (messageId += 1).toString(); }, false);
});
testWidgets('waiting for NoTransientCallbacks returns immediately when transient callback queue is empty', (WidgetTester tester) async {
extension.call(const WaitForCondition(NoTransientCallbacks()).serialize())
.then<void>(expectAsync1((Map<String, dynamic> r) {
result = r;
}));
await tester.idle();
expect(
result,
<String, dynamic>{
'isError': false,
'response': null,
},
);
});
testWidgets('waiting for NoTransientCallbacks returns until no transient callbacks', (WidgetTester tester) async {
SchedulerBinding.instance.scheduleFrameCallback((_) {
// Intentionally blank. We only care about existence of a callback.
});
extension.call(const WaitForCondition(NoTransientCallbacks()).serialize())
.then<void>(expectAsync1((Map<String, dynamic> r) {
result = r;
}));
// Nothing should happen until the next frame.
await tester.idle();
expect(result, isNull);
// NOW we should receive the result.
await tester.pump();
expect(
result,
<String, dynamic>{
'isError': false,
'response': null,
},
);
});
testWidgets('waiting for NoPendingFrame returns immediately when frame is synced', (
WidgetTester tester) async {
extension.call(const WaitForCondition(NoPendingFrame()).serialize())
.then<void>(expectAsync1((Map<String, dynamic> r) {
result = r;
}));
await tester.idle();
expect(
result,
<String, dynamic>{
'isError': false,
'response': null,
},
);
});
testWidgets('waiting for NoPendingFrame returns until no pending scheduled frame', (WidgetTester tester) async {
SchedulerBinding.instance.scheduleFrame();
extension.call(const WaitForCondition(NoPendingFrame()).serialize())
.then<void>(expectAsync1((Map<String, dynamic> r) {
result = r;
}));
// Nothing should happen until the next frame.
await tester.idle();
expect(result, isNull);
// NOW we should receive the result.
await tester.pump();
expect(
result,
<String, dynamic>{
'isError': false,
'response': null,
},
);
});
testWidgets(
'waiting for combined conditions returns immediately', (WidgetTester tester) async {
const SerializableWaitCondition combinedCondition =
CombinedCondition(<SerializableWaitCondition>[NoTransientCallbacks(), NoPendingFrame()]);
extension.call(const WaitForCondition(combinedCondition).serialize())
.then<void>(expectAsync1((Map<String, dynamic> r) {
result = r;
}));
await tester.idle();
expect(
result,
<String, dynamic>{
'isError': false,
'response': null,
},
);
});
testWidgets(
'waiting for combined conditions returns until no transient callbacks', (WidgetTester tester) async {
SchedulerBinding.instance.scheduleFrame();
SchedulerBinding.instance.scheduleFrameCallback((_) {
// Intentionally blank. We only care about existence of a callback.
});
const SerializableWaitCondition combinedCondition =
CombinedCondition(<SerializableWaitCondition>[NoTransientCallbacks(), NoPendingFrame()]);
extension.call(const WaitForCondition(combinedCondition).serialize())
.then<void>(expectAsync1((Map<String, dynamic> r) {
result = r;
}));
// Nothing should happen until the next frame.
await tester.idle();
expect(result, isNull);
// NOW we should receive the result.
await tester.pump();
expect(
result,
<String, dynamic>{
'isError': false,
'response': null,
},
);
});
testWidgets(
'waiting for combined conditions returns until no pending scheduled frame', (WidgetTester tester) async {
SchedulerBinding.instance.scheduleFrame();
SchedulerBinding.instance.scheduleFrameCallback((_) {
// Intentionally blank. We only care about existence of a callback.
});
const SerializableWaitCondition combinedCondition =
CombinedCondition(<SerializableWaitCondition>[NoPendingFrame(), NoTransientCallbacks()]);
extension.call(const WaitForCondition(combinedCondition).serialize())
.then<void>(expectAsync1((Map<String, dynamic> r) {
result = r;
}));
// Nothing should happen until the next frame.
await tester.idle();
expect(result, isNull);
// NOW we should receive the result.
await tester.pump();
expect(
result,
<String, dynamic>{
'isError': false,
'response': null,
},
);
});
});
group('getSemanticsId', () {
FlutterDriverExtension extension;
setUp(() {
......@@ -345,7 +516,7 @@ void main() {
testWidgets('returns immediately when frame is synced', (
WidgetTester tester) async {
extension.call(const WaitUntilNoPendingFrame().serialize())
extension.call(const WaitUntilNoPendingFrame().serialize()) // ignore: deprecated_member_use_from_same_package
.then<void>(expectAsync1((Map<String, dynamic> r) {
result = r;
}));
......@@ -366,7 +537,7 @@ void main() {
// Intentionally blank. We only care about existence of a callback.
});
extension.call(const WaitUntilNoPendingFrame().serialize())
extension.call(const WaitUntilNoPendingFrame().serialize()) // ignore: deprecated_member_use_from_same_package
.then<void>(expectAsync1((Map<String, dynamic> r) {
result = r;
}));
......@@ -390,7 +561,7 @@ void main() {
'waits until no pending scheduled frame', (WidgetTester tester) async {
SchedulerBinding.instance.scheduleFrame();
extension.call(const WaitUntilNoPendingFrame().serialize())
extension.call(const WaitUntilNoPendingFrame().serialize()) // ignore: deprecated_member_use_from_same_package
.then<void>(expectAsync1((Map<String, dynamic> r) {
result = r;
}));
......
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter_driver/src/common/wait.dart';
import '../common.dart';
void main() {
group('WaitForCondition', () {
test('WaitForCondition serialize', () {
expect(
const WaitForCondition(NoTransientCallbacks()).serialize(),
<String, String>{'command': 'waitForCondition', 'conditionName': 'NoTransientCallbacksCondition'});
});
test('WaitForCondition serialize with timeout', () {
expect(
const WaitForCondition(NoTransientCallbacks(), timeout: Duration(milliseconds: 10)).serialize(),
<String, String>{'command': 'waitForCondition', 'timeout': '10', 'conditionName': 'NoTransientCallbacksCondition'});
});
test('WaitForCondition deserialize', () {
final Map<String, String> jsonMap = <String, String>{
'command': 'waitForCondition',
'conditionName': 'NoTransientCallbacksCondition',
};
final WaitForCondition waitForCondition = WaitForCondition.deserialize(jsonMap);
expect(waitForCondition.kind, 'waitForCondition');
expect(waitForCondition.condition, equals(const NoTransientCallbacks()));
});
test('WaitForCondition deserialize with timeout', () {
final Map<String, String> jsonMap = <String, String>{
'command': 'waitForCondition',
'timeout': '10',
'conditionName': 'NoTransientCallbacksCondition',
};
final WaitForCondition waitForCondition = WaitForCondition.deserialize(jsonMap);
expect(waitForCondition.kind, 'waitForCondition');
expect(waitForCondition.condition, equals(const NoTransientCallbacks()));
expect(waitForCondition.timeout, equals(const Duration(milliseconds: 10)));
});
});
group('NoTransientCallbacksCondition', () {
test('NoTransientCallbacksCondition serialize', () {
expect(
const NoTransientCallbacks().serialize(),
<String, String>{'conditionName': 'NoTransientCallbacksCondition'});
});
test('NoTransientCallbacksCondition deserialize', () {
final Map<String, String> jsonMap = <String, String>{
'conditionName': 'NoTransientCallbacksCondition',
};
final NoTransientCallbacks condition = NoTransientCallbacks.deserialize(jsonMap);
expect(condition, equals(const NoTransientCallbacks()));
expect(condition.serialize(), equals(jsonMap));
});
test('NoTransientCallbacksCondition deserialize error', () {
expect(
() => NoTransientCallbacks.deserialize(<String, String>{'conditionName': 'Unknown'}),
throwsA(predicate<SerializationException>((SerializationException e) =>
e.message == 'Error occurred during deserializing the NoTransientCallbacksCondition JSON string: {conditionName: Unknown}')));
});
});
group('NoPendingFrameCondition', () {
test('NoPendingFrameCondition serialize', () {
expect(const NoPendingFrame().serialize(), <String, String>{
'conditionName': 'NoPendingFrameCondition',
});
});
test('NoPendingFrameCondition deserialize', () {
final Map<String, String> jsonMap = <String, String>{
'conditionName': 'NoPendingFrameCondition',
};
final NoPendingFrame condition = NoPendingFrame.deserialize(jsonMap);
expect(condition, equals(const NoPendingFrame()));
expect(condition.serialize(), equals(jsonMap));
});
test('NoPendingFrameCondition deserialize error', () {
expect(
() => NoPendingFrame.deserialize(<String, String>{'conditionName': 'Unknown'}),
throwsA(predicate<SerializationException>((SerializationException e) =>
e.message == 'Error occurred during deserializing the NoPendingFrameCondition JSON string: {conditionName: Unknown}')));
});
});
group('FirstFrameRasterizedCondition', () {
test('FirstFrameRasterizedCondition serialize', () {
expect(
const FirstFrameRasterized().serialize(),
<String, String>{'conditionName': 'FirstFrameRasterizedCondition'});
});
test('FirstFrameRasterizedCondition deserialize', () {
final Map<String, String> jsonMap = <String, String>{
'conditionName': 'FirstFrameRasterizedCondition',
};
final FirstFrameRasterized condition = FirstFrameRasterized.deserialize(jsonMap);
expect(condition, equals(const FirstFrameRasterized()));
expect(condition.serialize(), equals(jsonMap));
});
test('FirstFrameRasterizedCondition deserialize error', () {
expect(
() => FirstFrameRasterized.deserialize(<String, String>{'conditionName': 'Unknown'}),
throwsA(predicate<SerializationException>((SerializationException e) =>
e.message == 'Error occurred during deserializing the FirstFrameRasterizedCondition JSON string: {conditionName: Unknown}')));
});
});
group('CombinedCondition', () {
test('CombinedCondition serialize', () {
const CombinedCondition combinedCondition =
CombinedCondition(<SerializableWaitCondition>[
NoTransientCallbacks(),
NoPendingFrame()
]);
expect(combinedCondition.serialize(), <String, String>{
'conditionName': 'CombinedCondition',
'conditions':
'[{"conditionName":"NoTransientCallbacksCondition"},{"conditionName":"NoPendingFrameCondition"}]',
});
});
test('CombinedCondition serialize - empty condition list', () {
const CombinedCondition combinedCondition = CombinedCondition(<SerializableWaitCondition>[]);
expect(combinedCondition.serialize(), <String, String>{
'conditionName': 'CombinedCondition',
'conditions': '[]',
});
});
test('CombinedCondition deserialize - empty condition list', () {
final Map<String, String> jsonMap = <String, String>{
'conditionName': 'CombinedCondition',
'conditions': '[]',
};
final CombinedCondition condition = CombinedCondition.deserialize(jsonMap);
expect(condition.conditions, equals(<SerializableWaitCondition>[]));
expect(condition.serialize(), equals(jsonMap));
});
test('CombinedCondition deserialize', () {
final Map<String, String> jsonMap = <String, String>{
'conditionName': 'CombinedCondition',
'conditions':
'[{"conditionName":"NoPendingFrameCondition"},{"conditionName":"NoTransientCallbacksCondition"}]',
};
final CombinedCondition condition =
CombinedCondition.deserialize(jsonMap);
expect(
condition.conditions,
equals(<SerializableWaitCondition>[
const NoPendingFrame(),
const NoTransientCallbacks(),
]));
expect(condition.serialize(), jsonMap);
});
test('CombinedCondition deserialize - no condition list', () {
final CombinedCondition condition =
CombinedCondition.deserialize(<String, String>{'conditionName': 'CombinedCondition',});
expect(condition.conditions, equals(<SerializableWaitCondition>[]));
expect(condition.serialize(), <String, String>{
'conditionName': 'CombinedCondition',
'conditions': '[]',
});
});
test('CombinedCondition deserialize error', () {
expect(
() => CombinedCondition.deserialize(<String, String>{'conditionName': 'Unknown'}),
throwsA(predicate<SerializationException>((SerializationException e) =>
e.message == 'Error occurred during deserializing the CombinedCondition JSON string: {conditionName: Unknown}')));
});
test('CombinedCondition deserialize error - Unknown condition type', () {
expect(
() {
return CombinedCondition.deserialize(<String, String>{
'conditionName': 'CombinedCondition',
'conditions':
'[{"conditionName":"UnknownCondition"},{"conditionName":"NoTransientCallbacksCondition"}]',
});
},
throwsA(predicate<SerializationException>((SerializationException e) =>
e.message == 'Unsupported wait condition UnknownCondition in the JSON string {conditionName: UnknownCondition}')));
});
});
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment