218 lines
8.3 KiB
Dart
218 lines
8.3 KiB
Dart
// 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/services.dart';
|
|
import 'package:flutter/widgets.dart';
|
|
import 'package:flutter_test/flutter_test.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 condition that waits until no pending platform messages.
|
|
class _InternalNoPendingPlatformMessagesCondition implements WaitCondition {
|
|
/// Creates an [_InternalNoPendingPlatformMessagesCondition] instance.
|
|
const _InternalNoPendingPlatformMessagesCondition();
|
|
|
|
/// Factory constructor to parse an [_InternalNoPendingPlatformMessagesCondition] instance
|
|
/// from the given [SerializableWaitCondition] instance.
|
|
///
|
|
/// The [condition] argument must not be null.
|
|
factory _InternalNoPendingPlatformMessagesCondition.deserialize(SerializableWaitCondition condition) {
|
|
assert(condition != null);
|
|
if (condition.conditionName != 'NoPendingPlatformMessagesCondition')
|
|
throw SerializationException('Error occurred during deserializing from the given condition: ${condition.serialize()}');
|
|
return const _InternalNoPendingPlatformMessagesCondition();
|
|
}
|
|
|
|
@override
|
|
bool get condition {
|
|
final TestDefaultBinaryMessenger binaryMessenger = ServicesBinding.instance.defaultBinaryMessenger;
|
|
return binaryMessenger.pendingMessageCount == 0;
|
|
}
|
|
|
|
@override
|
|
Future<void> wait() async {
|
|
final TestDefaultBinaryMessenger binaryMessenger = ServicesBinding.instance.defaultBinaryMessenger;
|
|
while (!condition) {
|
|
await binaryMessenger.platformMessagesFinished;
|
|
}
|
|
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 'NoPendingPlatformMessagesCondition':
|
|
return _InternalNoPendingPlatformMessagesCondition.deserialize(waitCondition);
|
|
case 'CombinedCondition':
|
|
return _InternalCombinedCondition.deserialize(waitCondition);
|
|
}
|
|
throw SerializationException(
|
|
'Unsupported wait condition $conditionName in ${waitCondition.serialize()}');
|
|
}
|