| // Copyright 2014 The Flutter 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 as TestDefaultBinaryMessenger; |
| return binaryMessenger.pendingMessageCount == 0; |
| } |
| |
| @override |
| Future<void> wait() async { |
| final TestDefaultBinaryMessenger binaryMessenger = ServicesBinding.instance!.defaultBinaryMessenger as TestDefaultBinaryMessenger; |
| 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 as CombinedCondition; |
| final List<WaitCondition> conditions = combinedCondition.conditions.map(deserializeCondition).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 (final 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()}'); |
| } |