Add stackTrace to AsyncSnapshot (#69507)
This commit is contained in:
parent
0d7270ba0c
commit
85f0aea87e
@ -75,10 +75,11 @@ abstract class StreamBuilderBase<T, S> extends StatefulWidget {
|
|||||||
/// is combined with the new data item in the fold computation.
|
/// is combined with the new data item in the fold computation.
|
||||||
S afterData(S current, T data);
|
S afterData(S current, T data);
|
||||||
|
|
||||||
/// Returns an updated version of the [current] summary following an error.
|
/// Returns an updated version of the [current] summary following an error
|
||||||
|
/// with a stack trace.
|
||||||
///
|
///
|
||||||
/// The default implementation returns [current] as is.
|
/// The default implementation returns [current] as is.
|
||||||
S afterError(S current, Object error) => current;
|
S afterError(S current, Object error, StackTrace stackTrace) => current;
|
||||||
|
|
||||||
/// Returns an updated version of the [current] summary following stream
|
/// Returns an updated version of the [current] summary following stream
|
||||||
/// termination.
|
/// termination.
|
||||||
@ -138,9 +139,9 @@ class _StreamBuilderBaseState<T, S> extends State<StreamBuilderBase<T, S>> {
|
|||||||
setState(() {
|
setState(() {
|
||||||
_summary = widget.afterData(_summary, data);
|
_summary = widget.afterData(_summary, data);
|
||||||
});
|
});
|
||||||
}, onError: (Object error) {
|
}, onError: (Object error, StackTrace stackTrace) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_summary = widget.afterError(_summary, error);
|
_summary = widget.afterError(_summary, error, stackTrace);
|
||||||
});
|
});
|
||||||
}, onDone: () {
|
}, onDone: () {
|
||||||
setState(() {
|
setState(() {
|
||||||
@ -204,22 +205,31 @@ enum ConnectionState {
|
|||||||
@immutable
|
@immutable
|
||||||
class AsyncSnapshot<T> {
|
class AsyncSnapshot<T> {
|
||||||
/// Creates an [AsyncSnapshot] with the specified [connectionState],
|
/// Creates an [AsyncSnapshot] with the specified [connectionState],
|
||||||
/// and optionally either [data] or [error] (but not both).
|
/// and optionally either [data] or [error] with an optional [stackTrace]
|
||||||
const AsyncSnapshot._(this.connectionState, this.data, this.error)
|
/// (but not both data and error).
|
||||||
|
const AsyncSnapshot._(this.connectionState, this.data, this.error, this.stackTrace)
|
||||||
: assert(connectionState != null),
|
: assert(connectionState != null),
|
||||||
assert(!(data != null && error != null));
|
assert(!(data != null && error != null)),
|
||||||
|
assert(stackTrace == null || error != null);
|
||||||
|
|
||||||
/// Creates an [AsyncSnapshot] in [ConnectionState.none] with null data and error.
|
/// Creates an [AsyncSnapshot] in [ConnectionState.none] with null data and error.
|
||||||
const AsyncSnapshot.nothing() : this._(ConnectionState.none, null, null);
|
const AsyncSnapshot.nothing() : this._(ConnectionState.none, null, null, null);
|
||||||
|
|
||||||
/// Creates an [AsyncSnapshot] in [ConnectionState.waiting] with null data and error.
|
/// Creates an [AsyncSnapshot] in [ConnectionState.waiting] with null data and error.
|
||||||
const AsyncSnapshot.waiting() : this._(ConnectionState.waiting, null, null);
|
const AsyncSnapshot.waiting() : this._(ConnectionState.waiting, null, null, null);
|
||||||
|
|
||||||
/// Creates an [AsyncSnapshot] in the specified [state] and with the specified [data].
|
/// Creates an [AsyncSnapshot] in the specified [state] and with the specified [data].
|
||||||
const AsyncSnapshot.withData(ConnectionState state, T data): this._(state, data, null);
|
const AsyncSnapshot.withData(ConnectionState state, T data): this._(state, data, null, null);
|
||||||
|
|
||||||
/// Creates an [AsyncSnapshot] in the specified [state] and with the specified [error].
|
/// Creates an [AsyncSnapshot] in the specified [state] with the specified [error]
|
||||||
const AsyncSnapshot.withError(ConnectionState state, Object error) : this._(state, null, error);
|
/// and a [stackTrace].
|
||||||
|
///
|
||||||
|
/// If no [stackTrace] is explicitly specified, [StackTrace.empty] will be used instead.
|
||||||
|
const AsyncSnapshot.withError(
|
||||||
|
ConnectionState state,
|
||||||
|
Object error, [
|
||||||
|
StackTrace stackTrace = StackTrace.empty,
|
||||||
|
]) : this._(state, null, error, stackTrace);
|
||||||
|
|
||||||
/// Current state of connection to the asynchronous computation.
|
/// Current state of connection to the asynchronous computation.
|
||||||
final ConnectionState connectionState;
|
final ConnectionState connectionState;
|
||||||
@ -254,11 +264,20 @@ class AsyncSnapshot<T> {
|
|||||||
/// If [data] is not null, this will be null.
|
/// If [data] is not null, this will be null.
|
||||||
final Object? error;
|
final Object? error;
|
||||||
|
|
||||||
|
/// The latest stack trace object received by the asynchronous computation.
|
||||||
|
///
|
||||||
|
/// This will not be null iff [error] is not null. Consequently, [stackTrace]
|
||||||
|
/// will be non-null when [hasError] is true.
|
||||||
|
///
|
||||||
|
/// However, even when not null, [stackTrace] might be empty. The stack trace
|
||||||
|
/// is empty when there is an error but no stack trace has been provided.
|
||||||
|
final StackTrace? stackTrace;
|
||||||
|
|
||||||
/// Returns a snapshot like this one, but in the specified [state].
|
/// Returns a snapshot like this one, but in the specified [state].
|
||||||
///
|
///
|
||||||
/// The [data] and [error] fields persist unmodified, even if the new state is
|
/// The [data], [error], and [stackTrace] fields persist unmodified, even if
|
||||||
/// [ConnectionState.none].
|
/// the new state is [ConnectionState.none].
|
||||||
AsyncSnapshot<T> inState(ConnectionState state) => AsyncSnapshot<T>._(state, data, error);
|
AsyncSnapshot<T> inState(ConnectionState state) => AsyncSnapshot<T>._(state, data, error, stackTrace);
|
||||||
|
|
||||||
/// Returns whether this snapshot contains a non-null [data] value.
|
/// Returns whether this snapshot contains a non-null [data] value.
|
||||||
///
|
///
|
||||||
@ -275,7 +294,7 @@ class AsyncSnapshot<T> {
|
|||||||
bool get hasError => error != null;
|
bool get hasError => error != null;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() => '${objectRuntimeType(this, 'AsyncSnapshot')}($connectionState, $data, $error)';
|
String toString() => '${objectRuntimeType(this, 'AsyncSnapshot')}($connectionState, $data, $error, $stackTrace)';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
@ -284,7 +303,8 @@ class AsyncSnapshot<T> {
|
|||||||
return other is AsyncSnapshot<T>
|
return other is AsyncSnapshot<T>
|
||||||
&& other.connectionState == connectionState
|
&& other.connectionState == connectionState
|
||||||
&& other.data == data
|
&& other.data == data
|
||||||
&& other.error == error;
|
&& other.error == error
|
||||||
|
&& other.stackTrace == stackTrace;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -318,12 +338,12 @@ typedef AsyncWidgetBuilder<T> = Widget Function(BuildContext context, AsyncSnaps
|
|||||||
/// of the following snapshots that includes the last one (the one with
|
/// of the following snapshots that includes the last one (the one with
|
||||||
/// ConnectionState.done):
|
/// ConnectionState.done):
|
||||||
///
|
///
|
||||||
/// * `new AsyncSnapshot<int>.withData(ConnectionState.waiting, null)`
|
/// * `AsyncSnapshot<int>.withData(ConnectionState.waiting, null)`
|
||||||
/// * `new AsyncSnapshot<int>.withData(ConnectionState.active, 0)`
|
/// * `AsyncSnapshot<int>.withData(ConnectionState.active, 0)`
|
||||||
/// * `new AsyncSnapshot<int>.withData(ConnectionState.active, 1)`
|
/// * `AsyncSnapshot<int>.withData(ConnectionState.active, 1)`
|
||||||
/// * ...
|
/// * ...
|
||||||
/// * `new AsyncSnapshot<int>.withData(ConnectionState.active, 9)`
|
/// * `AsyncSnapshot<int>.withData(ConnectionState.active, 9)`
|
||||||
/// * `new AsyncSnapshot<int>.withData(ConnectionState.done, 9)`
|
/// * `AsyncSnapshot<int>.withData(ConnectionState.done, 9)`
|
||||||
///
|
///
|
||||||
/// The actual sequence of invocations of the [builder] depends on the relative
|
/// The actual sequence of invocations of the [builder] depends on the relative
|
||||||
/// timing of events produced by the stream and the build rate of the Flutter
|
/// timing of events produced by the stream and the build rate of the Flutter
|
||||||
@ -332,15 +352,15 @@ typedef AsyncWidgetBuilder<T> = Widget Function(BuildContext context, AsyncSnaps
|
|||||||
/// Changing the [StreamBuilder] configuration to another stream during event
|
/// Changing the [StreamBuilder] configuration to another stream during event
|
||||||
/// generation introduces snapshot pairs of the form:
|
/// generation introduces snapshot pairs of the form:
|
||||||
///
|
///
|
||||||
/// * `new AsyncSnapshot<int>.withData(ConnectionState.none, 5)`
|
/// * `AsyncSnapshot<int>.withData(ConnectionState.none, 5)`
|
||||||
/// * `new AsyncSnapshot<int>.withData(ConnectionState.waiting, 5)`
|
/// * `AsyncSnapshot<int>.withData(ConnectionState.waiting, 5)`
|
||||||
///
|
///
|
||||||
/// The latter will be produced only when the new stream is non-null, and the
|
/// The latter will be produced only when the new stream is non-null, and the
|
||||||
/// former only when the old stream is non-null.
|
/// former only when the old stream is non-null.
|
||||||
///
|
///
|
||||||
/// The stream may produce errors, resulting in snapshots of the form:
|
/// The stream may produce errors, resulting in snapshots of the form:
|
||||||
///
|
///
|
||||||
/// * `new AsyncSnapshot<int>.withError(ConnectionState.active, 'some error')`
|
/// * `AsyncSnapshot<int>.withError(ConnectionState.active, 'some error', someStackTrace)`
|
||||||
///
|
///
|
||||||
/// The data and error fields of snapshots produced are only changed when the
|
/// The data and error fields of snapshots produced are only changed when the
|
||||||
/// state is `ConnectionState.active`.
|
/// state is `ConnectionState.active`.
|
||||||
@ -386,7 +406,11 @@ typedef AsyncWidgetBuilder<T> = Widget Function(BuildContext context, AsyncSnaps
|
|||||||
/// Padding(
|
/// Padding(
|
||||||
/// padding: const EdgeInsets.only(top: 16),
|
/// padding: const EdgeInsets.only(top: 16),
|
||||||
/// child: Text('Error: ${snapshot.error}'),
|
/// child: Text('Error: ${snapshot.error}'),
|
||||||
/// )
|
/// ),
|
||||||
|
/// Padding(
|
||||||
|
/// padding: const EdgeInsets.only(top: 8),
|
||||||
|
/// child: Text('Stack trace: ${snapshot.stackTrace}'),
|
||||||
|
/// ),
|
||||||
/// ];
|
/// ];
|
||||||
/// } else {
|
/// } else {
|
||||||
/// switch (snapshot.connectionState) {
|
/// switch (snapshot.connectionState) {
|
||||||
@ -511,8 +535,8 @@ class StreamBuilder<T> extends StreamBuilderBase<T, AsyncSnapshot<T>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
AsyncSnapshot<T> afterError(AsyncSnapshot<T> current, Object error) {
|
AsyncSnapshot<T> afterError(AsyncSnapshot<T> current, Object error, StackTrace stackTrace) {
|
||||||
return AsyncSnapshot<T>.withError(ConnectionState.active, error);
|
return AsyncSnapshot<T>.withError(ConnectionState.active, error, stackTrace);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -559,14 +583,14 @@ class StreamBuilder<T> extends StreamBuilderBase<T, AsyncSnapshot<T>> {
|
|||||||
/// is null, the [builder] will be called with either both or only the latter of
|
/// is null, the [builder] will be called with either both or only the latter of
|
||||||
/// the following snapshots:
|
/// the following snapshots:
|
||||||
///
|
///
|
||||||
/// * `new AsyncSnapshot<String>.withData(ConnectionState.waiting, null)`
|
/// * `AsyncSnapshot<String>.withData(ConnectionState.waiting, null)`
|
||||||
/// * `new AsyncSnapshot<String>.withData(ConnectionState.done, 'some data')`
|
/// * `AsyncSnapshot<String>.withData(ConnectionState.done, 'some data')`
|
||||||
///
|
///
|
||||||
/// If that same future instead completed with an error, the [builder] would be
|
/// If that same future instead completed with an error, the [builder] would be
|
||||||
/// called with either both or only the latter of:
|
/// called with either both or only the latter of:
|
||||||
///
|
///
|
||||||
/// * `new AsyncSnapshot<String>.withData(ConnectionState.waiting, null)`
|
/// * `AsyncSnapshot<String>.withData(ConnectionState.waiting, null)`
|
||||||
/// * `new AsyncSnapshot<String>.withError(ConnectionState.done, 'some error')`
|
/// * `AsyncSnapshot<String>.withError(ConnectionState.done, 'some error', someStackTrace)`
|
||||||
///
|
///
|
||||||
/// The initial snapshot data can be controlled by specifying [initialData]. You
|
/// The initial snapshot data can be controlled by specifying [initialData]. You
|
||||||
/// would use this facility to ensure that if the [builder] is invoked before
|
/// would use this facility to ensure that if the [builder] is invoked before
|
||||||
@ -579,8 +603,8 @@ class StreamBuilder<T> extends StreamBuilderBase<T, AsyncSnapshot<T>> {
|
|||||||
/// old future has already completed successfully with data as above, changing
|
/// old future has already completed successfully with data as above, changing
|
||||||
/// configuration to a new future results in snapshot pairs of the form:
|
/// configuration to a new future results in snapshot pairs of the form:
|
||||||
///
|
///
|
||||||
/// * `new AsyncSnapshot<String>.withData(ConnectionState.none, 'data of first future')`
|
/// * `AsyncSnapshot<String>.withData(ConnectionState.none, 'data of first future')`
|
||||||
/// * `new AsyncSnapshot<String>.withData(ConnectionState.waiting, 'data of second future')`
|
/// * `AsyncSnapshot<String>.withData(ConnectionState.waiting, 'data of second future')`
|
||||||
///
|
///
|
||||||
/// In general, the latter will be produced only when the new future is
|
/// In general, the latter will be produced only when the new future is
|
||||||
/// non-null, and the former only when the old future is non-null.
|
/// non-null, and the former only when the old future is non-null.
|
||||||
@ -768,10 +792,10 @@ class _FutureBuilderState<T> extends State<FutureBuilder<T>> {
|
|||||||
_snapshot = AsyncSnapshot<T>.withData(ConnectionState.done, data);
|
_snapshot = AsyncSnapshot<T>.withData(ConnectionState.done, data);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, onError: (Object error) {
|
}, onError: (Object error, StackTrace stackTrace) {
|
||||||
if (_activeCallbackIdentity == callbackIdentity) {
|
if (_activeCallbackIdentity == callbackIdentity) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_snapshot = AsyncSnapshot<T>.withError(ConnectionState.done, error);
|
_snapshot = AsyncSnapshot<T>.withError(ConnectionState.done, error, stackTrace);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -34,9 +34,20 @@ void main() {
|
|||||||
expect(const AsyncSnapshot<int>.nothing().connectionState, ConnectionState.none);
|
expect(const AsyncSnapshot<int>.nothing().connectionState, ConnectionState.none);
|
||||||
expect(const AsyncSnapshot<int>.nothing().data, isNull);
|
expect(const AsyncSnapshot<int>.nothing().data, isNull);
|
||||||
expect(const AsyncSnapshot<int>.nothing().error, isNull);
|
expect(const AsyncSnapshot<int>.nothing().error, isNull);
|
||||||
|
expect(const AsyncSnapshot<int>.nothing().stackTrace, isNull);
|
||||||
expect(const AsyncSnapshot<int>.waiting().connectionState, ConnectionState.waiting);
|
expect(const AsyncSnapshot<int>.waiting().connectionState, ConnectionState.waiting);
|
||||||
expect(const AsyncSnapshot<int>.waiting().data, isNull);
|
expect(const AsyncSnapshot<int>.waiting().data, isNull);
|
||||||
expect(const AsyncSnapshot<int>.waiting().error, isNull);
|
expect(const AsyncSnapshot<int>.waiting().error, isNull);
|
||||||
|
expect(const AsyncSnapshot<int>.waiting().stackTrace, isNull);
|
||||||
|
});
|
||||||
|
test('withError uses empty stack trace if no stack trace is specified', () {
|
||||||
|
// We need to store the error as a local variable in order for the
|
||||||
|
// equality check on the error to be true.
|
||||||
|
final Error error = Error();
|
||||||
|
expect(
|
||||||
|
AsyncSnapshot<int>.withError(ConnectionState.done, error),
|
||||||
|
AsyncSnapshot<int>.withError(
|
||||||
|
ConnectionState.done, error, StackTrace.empty));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
group('Async smoke tests', () {
|
group('Async smoke tests', () {
|
||||||
@ -67,12 +78,12 @@ void main() {
|
|||||||
await tester.pumpWidget(FutureBuilder<String>(
|
await tester.pumpWidget(FutureBuilder<String>(
|
||||||
key: key, future: null, builder: snapshotText,
|
key: key, future: null, builder: snapshotText,
|
||||||
));
|
));
|
||||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.none, null, null)'), findsOneWidget);
|
expect(find.text('AsyncSnapshot<String>(ConnectionState.none, null, null, null)'), findsOneWidget);
|
||||||
final Completer<String> completer = Completer<String>();
|
final Completer<String> completer = Completer<String>();
|
||||||
await tester.pumpWidget(FutureBuilder<String>(
|
await tester.pumpWidget(FutureBuilder<String>(
|
||||||
key: key, future: completer.future, builder: snapshotText,
|
key: key, future: completer.future, builder: snapshotText,
|
||||||
));
|
));
|
||||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.waiting, null, null)'), findsOneWidget);
|
expect(find.text('AsyncSnapshot<String>(ConnectionState.waiting, null, null, null)'), findsOneWidget);
|
||||||
});
|
});
|
||||||
testWidgets('gracefully handles transition to null future', (WidgetTester tester) async {
|
testWidgets('gracefully handles transition to null future', (WidgetTester tester) async {
|
||||||
final GlobalKey key = GlobalKey();
|
final GlobalKey key = GlobalKey();
|
||||||
@ -80,14 +91,14 @@ void main() {
|
|||||||
await tester.pumpWidget(FutureBuilder<String>(
|
await tester.pumpWidget(FutureBuilder<String>(
|
||||||
key: key, future: completer.future, builder: snapshotText,
|
key: key, future: completer.future, builder: snapshotText,
|
||||||
));
|
));
|
||||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.waiting, null, null)'), findsOneWidget);
|
expect(find.text('AsyncSnapshot<String>(ConnectionState.waiting, null, null, null)'), findsOneWidget);
|
||||||
await tester.pumpWidget(FutureBuilder<String>(
|
await tester.pumpWidget(FutureBuilder<String>(
|
||||||
key: key, future: null, builder: snapshotText,
|
key: key, future: null, builder: snapshotText,
|
||||||
));
|
));
|
||||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.none, null, null)'), findsOneWidget);
|
expect(find.text('AsyncSnapshot<String>(ConnectionState.none, null, null, null)'), findsOneWidget);
|
||||||
completer.complete('hello');
|
completer.complete('hello');
|
||||||
await eventFiring(tester);
|
await eventFiring(tester);
|
||||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.none, null, null)'), findsOneWidget);
|
expect(find.text('AsyncSnapshot<String>(ConnectionState.none, null, null, null)'), findsOneWidget);
|
||||||
});
|
});
|
||||||
testWidgets('gracefully handles transition to other future', (WidgetTester tester) async {
|
testWidgets('gracefully handles transition to other future', (WidgetTester tester) async {
|
||||||
final GlobalKey key = GlobalKey();
|
final GlobalKey key = GlobalKey();
|
||||||
@ -96,35 +107,35 @@ void main() {
|
|||||||
await tester.pumpWidget(FutureBuilder<String>(
|
await tester.pumpWidget(FutureBuilder<String>(
|
||||||
key: key, future: completerA.future, builder: snapshotText,
|
key: key, future: completerA.future, builder: snapshotText,
|
||||||
));
|
));
|
||||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.waiting, null, null)'), findsOneWidget);
|
expect(find.text('AsyncSnapshot<String>(ConnectionState.waiting, null, null, null)'), findsOneWidget);
|
||||||
await tester.pumpWidget(FutureBuilder<String>(
|
await tester.pumpWidget(FutureBuilder<String>(
|
||||||
key: key, future: completerB.future, builder: snapshotText,
|
key: key, future: completerB.future, builder: snapshotText,
|
||||||
));
|
));
|
||||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.waiting, null, null)'), findsOneWidget);
|
expect(find.text('AsyncSnapshot<String>(ConnectionState.waiting, null, null, null)'), findsOneWidget);
|
||||||
completerB.complete('B');
|
completerB.complete('B');
|
||||||
completerA.complete('A');
|
completerA.complete('A');
|
||||||
await eventFiring(tester);
|
await eventFiring(tester);
|
||||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.done, B, null)'), findsOneWidget);
|
expect(find.text('AsyncSnapshot<String>(ConnectionState.done, B, null, null)'), findsOneWidget);
|
||||||
});
|
});
|
||||||
testWidgets('tracks life-cycle of Future to success', (WidgetTester tester) async {
|
testWidgets('tracks life-cycle of Future to success', (WidgetTester tester) async {
|
||||||
final Completer<String> completer = Completer<String>();
|
final Completer<String> completer = Completer<String>();
|
||||||
await tester.pumpWidget(FutureBuilder<String>(
|
await tester.pumpWidget(FutureBuilder<String>(
|
||||||
future: completer.future, builder: snapshotText,
|
future: completer.future, builder: snapshotText,
|
||||||
));
|
));
|
||||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.waiting, null, null)'), findsOneWidget);
|
expect(find.text('AsyncSnapshot<String>(ConnectionState.waiting, null, null, null)'), findsOneWidget);
|
||||||
completer.complete('hello');
|
completer.complete('hello');
|
||||||
await eventFiring(tester);
|
await eventFiring(tester);
|
||||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.done, hello, null)'), findsOneWidget);
|
expect(find.text('AsyncSnapshot<String>(ConnectionState.done, hello, null, null)'), findsOneWidget);
|
||||||
});
|
});
|
||||||
testWidgets('tracks life-cycle of Future to error', (WidgetTester tester) async {
|
testWidgets('tracks life-cycle of Future to error', (WidgetTester tester) async {
|
||||||
final Completer<String> completer = Completer<String>();
|
final Completer<String> completer = Completer<String>();
|
||||||
await tester.pumpWidget(FutureBuilder<String>(
|
await tester.pumpWidget(FutureBuilder<String>(
|
||||||
future: completer.future, builder: snapshotText,
|
future: completer.future, builder: snapshotText,
|
||||||
));
|
));
|
||||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.waiting, null, null)'), findsOneWidget);
|
expect(find.text('AsyncSnapshot<String>(ConnectionState.waiting, null, null, null)'), findsOneWidget);
|
||||||
completer.completeError('bad');
|
completer.completeError('bad', StackTrace.fromString('trace'));
|
||||||
await eventFiring(tester);
|
await eventFiring(tester);
|
||||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.done, null, bad)'), findsOneWidget);
|
expect(find.text('AsyncSnapshot<String>(ConnectionState.done, null, bad, trace)'), findsOneWidget);
|
||||||
});
|
});
|
||||||
testWidgets('runs the builder using given initial data', (WidgetTester tester) async {
|
testWidgets('runs the builder using given initial data', (WidgetTester tester) async {
|
||||||
final GlobalKey key = GlobalKey();
|
final GlobalKey key = GlobalKey();
|
||||||
@ -134,7 +145,7 @@ void main() {
|
|||||||
builder: snapshotText,
|
builder: snapshotText,
|
||||||
initialData: 'I',
|
initialData: 'I',
|
||||||
));
|
));
|
||||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.none, I, null)'), findsOneWidget);
|
expect(find.text('AsyncSnapshot<String>(ConnectionState.none, I, null, null)'), findsOneWidget);
|
||||||
});
|
});
|
||||||
testWidgets('ignores initialData when reconfiguring', (WidgetTester tester) async {
|
testWidgets('ignores initialData when reconfiguring', (WidgetTester tester) async {
|
||||||
final GlobalKey key = GlobalKey();
|
final GlobalKey key = GlobalKey();
|
||||||
@ -144,7 +155,7 @@ void main() {
|
|||||||
builder: snapshotText,
|
builder: snapshotText,
|
||||||
initialData: 'I',
|
initialData: 'I',
|
||||||
));
|
));
|
||||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.none, I, null)'), findsOneWidget);
|
expect(find.text('AsyncSnapshot<String>(ConnectionState.none, I, null, null)'), findsOneWidget);
|
||||||
final Completer<String> completer = Completer<String>();
|
final Completer<String> completer = Completer<String>();
|
||||||
await tester.pumpWidget(FutureBuilder<String>(
|
await tester.pumpWidget(FutureBuilder<String>(
|
||||||
key: key,
|
key: key,
|
||||||
@ -152,7 +163,7 @@ void main() {
|
|||||||
builder: snapshotText,
|
builder: snapshotText,
|
||||||
initialData: 'Ignored',
|
initialData: 'Ignored',
|
||||||
));
|
));
|
||||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.waiting, I, null)'), findsOneWidget);
|
expect(find.text('AsyncSnapshot<String>(ConnectionState.waiting, I, null, null)'), findsOneWidget);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
group('StreamBuilder', () {
|
group('StreamBuilder', () {
|
||||||
@ -161,12 +172,12 @@ void main() {
|
|||||||
await tester.pumpWidget(StreamBuilder<String>(
|
await tester.pumpWidget(StreamBuilder<String>(
|
||||||
key: key, stream: null, builder: snapshotText,
|
key: key, stream: null, builder: snapshotText,
|
||||||
));
|
));
|
||||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.none, null, null)'), findsOneWidget);
|
expect(find.text('AsyncSnapshot<String>(ConnectionState.none, null, null, null)'), findsOneWidget);
|
||||||
final StreamController<String> controller = StreamController<String>();
|
final StreamController<String> controller = StreamController<String>();
|
||||||
await tester.pumpWidget(StreamBuilder<String>(
|
await tester.pumpWidget(StreamBuilder<String>(
|
||||||
key: key, stream: controller.stream, builder: snapshotText,
|
key: key, stream: controller.stream, builder: snapshotText,
|
||||||
));
|
));
|
||||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.waiting, null, null)'), findsOneWidget);
|
expect(find.text('AsyncSnapshot<String>(ConnectionState.waiting, null, null, null)'), findsOneWidget);
|
||||||
});
|
});
|
||||||
testWidgets('gracefully handles transition to null stream', (WidgetTester tester) async {
|
testWidgets('gracefully handles transition to null stream', (WidgetTester tester) async {
|
||||||
final GlobalKey key = GlobalKey();
|
final GlobalKey key = GlobalKey();
|
||||||
@ -174,11 +185,11 @@ void main() {
|
|||||||
await tester.pumpWidget(StreamBuilder<String>(
|
await tester.pumpWidget(StreamBuilder<String>(
|
||||||
key: key, stream: controller.stream, builder: snapshotText,
|
key: key, stream: controller.stream, builder: snapshotText,
|
||||||
));
|
));
|
||||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.waiting, null, null)'), findsOneWidget);
|
expect(find.text('AsyncSnapshot<String>(ConnectionState.waiting, null, null, null)'), findsOneWidget);
|
||||||
await tester.pumpWidget(StreamBuilder<String>(
|
await tester.pumpWidget(StreamBuilder<String>(
|
||||||
key: key, stream: null, builder: snapshotText,
|
key: key, stream: null, builder: snapshotText,
|
||||||
));
|
));
|
||||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.none, null, null)'), findsOneWidget);
|
expect(find.text('AsyncSnapshot<String>(ConnectionState.none, null, null, null)'), findsOneWidget);
|
||||||
});
|
});
|
||||||
testWidgets('gracefully handles transition to other stream', (WidgetTester tester) async {
|
testWidgets('gracefully handles transition to other stream', (WidgetTester tester) async {
|
||||||
final GlobalKey key = GlobalKey();
|
final GlobalKey key = GlobalKey();
|
||||||
@ -187,14 +198,14 @@ void main() {
|
|||||||
await tester.pumpWidget(StreamBuilder<String>(
|
await tester.pumpWidget(StreamBuilder<String>(
|
||||||
key: key, stream: controllerA.stream, builder: snapshotText,
|
key: key, stream: controllerA.stream, builder: snapshotText,
|
||||||
));
|
));
|
||||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.waiting, null, null)'), findsOneWidget);
|
expect(find.text('AsyncSnapshot<String>(ConnectionState.waiting, null, null, null)'), findsOneWidget);
|
||||||
await tester.pumpWidget(StreamBuilder<String>(
|
await tester.pumpWidget(StreamBuilder<String>(
|
||||||
key: key, stream: controllerB.stream, builder: snapshotText,
|
key: key, stream: controllerB.stream, builder: snapshotText,
|
||||||
));
|
));
|
||||||
controllerB.add('B');
|
controllerB.add('B');
|
||||||
controllerA.add('A');
|
controllerA.add('A');
|
||||||
await eventFiring(tester);
|
await eventFiring(tester);
|
||||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.active, B, null)'), findsOneWidget);
|
expect(find.text('AsyncSnapshot<String>(ConnectionState.active, B, null, null)'), findsOneWidget);
|
||||||
});
|
});
|
||||||
testWidgets('tracks events and errors of stream until completion', (WidgetTester tester) async {
|
testWidgets('tracks events and errors of stream until completion', (WidgetTester tester) async {
|
||||||
final GlobalKey key = GlobalKey();
|
final GlobalKey key = GlobalKey();
|
||||||
@ -202,19 +213,19 @@ void main() {
|
|||||||
await tester.pumpWidget(StreamBuilder<String>(
|
await tester.pumpWidget(StreamBuilder<String>(
|
||||||
key: key, stream: controller.stream, builder: snapshotText,
|
key: key, stream: controller.stream, builder: snapshotText,
|
||||||
));
|
));
|
||||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.waiting, null, null)'), findsOneWidget);
|
expect(find.text('AsyncSnapshot<String>(ConnectionState.waiting, null, null, null)'), findsOneWidget);
|
||||||
controller.add('1');
|
controller.add('1');
|
||||||
controller.add('2');
|
controller.add('2');
|
||||||
await eventFiring(tester);
|
await eventFiring(tester);
|
||||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.active, 2, null)'), findsOneWidget);
|
expect(find.text('AsyncSnapshot<String>(ConnectionState.active, 2, null, null)'), findsOneWidget);
|
||||||
controller.add('3');
|
controller.add('3');
|
||||||
controller.addError('bad');
|
controller.addError('bad', StackTrace.fromString('trace'));
|
||||||
await eventFiring(tester);
|
await eventFiring(tester);
|
||||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.active, null, bad)'), findsOneWidget);
|
expect(find.text('AsyncSnapshot<String>(ConnectionState.active, null, bad, trace)'), findsOneWidget);
|
||||||
controller.add('4');
|
controller.add('4');
|
||||||
controller.close();
|
controller.close();
|
||||||
await eventFiring(tester);
|
await eventFiring(tester);
|
||||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.done, 4, null)'), findsOneWidget);
|
expect(find.text('AsyncSnapshot<String>(ConnectionState.done, 4, null, null)'), findsOneWidget);
|
||||||
});
|
});
|
||||||
testWidgets('runs the builder using given initial data', (WidgetTester tester) async {
|
testWidgets('runs the builder using given initial data', (WidgetTester tester) async {
|
||||||
final StreamController<String> controller = StreamController<String>();
|
final StreamController<String> controller = StreamController<String>();
|
||||||
@ -223,7 +234,7 @@ void main() {
|
|||||||
builder: snapshotText,
|
builder: snapshotText,
|
||||||
initialData: 'I',
|
initialData: 'I',
|
||||||
));
|
));
|
||||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.waiting, I, null)'), findsOneWidget);
|
expect(find.text('AsyncSnapshot<String>(ConnectionState.waiting, I, null, null)'), findsOneWidget);
|
||||||
});
|
});
|
||||||
testWidgets('ignores initialData when reconfiguring', (WidgetTester tester) async {
|
testWidgets('ignores initialData when reconfiguring', (WidgetTester tester) async {
|
||||||
final GlobalKey key = GlobalKey();
|
final GlobalKey key = GlobalKey();
|
||||||
@ -233,7 +244,7 @@ void main() {
|
|||||||
builder: snapshotText,
|
builder: snapshotText,
|
||||||
initialData: 'I',
|
initialData: 'I',
|
||||||
));
|
));
|
||||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.none, I, null)'), findsOneWidget);
|
expect(find.text('AsyncSnapshot<String>(ConnectionState.none, I, null, null)'), findsOneWidget);
|
||||||
final StreamController<String> controller = StreamController<String>();
|
final StreamController<String> controller = StreamController<String>();
|
||||||
await tester.pumpWidget(StreamBuilder<String>(
|
await tester.pumpWidget(StreamBuilder<String>(
|
||||||
key: key,
|
key: key,
|
||||||
@ -241,7 +252,7 @@ void main() {
|
|||||||
builder: snapshotText,
|
builder: snapshotText,
|
||||||
initialData: 'Ignored',
|
initialData: 'Ignored',
|
||||||
));
|
));
|
||||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.waiting, I, null)'), findsOneWidget);
|
expect(find.text('AsyncSnapshot<String>(ConnectionState.waiting, I, null, null)'), findsOneWidget);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
group('FutureBuilder and StreamBuilder behave identically on Stream from Future', () {
|
group('FutureBuilder and StreamBuilder behave identically on Stream from Future', () {
|
||||||
@ -251,35 +262,46 @@ void main() {
|
|||||||
FutureBuilder<String>(future: completer.future, builder: snapshotText),
|
FutureBuilder<String>(future: completer.future, builder: snapshotText),
|
||||||
StreamBuilder<String>(stream: completer.future.asStream(), builder: snapshotText),
|
StreamBuilder<String>(stream: completer.future.asStream(), builder: snapshotText),
|
||||||
]));
|
]));
|
||||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.waiting, null, null)'), findsNWidgets(2));
|
expect(find.text('AsyncSnapshot<String>(ConnectionState.waiting, null, null, null)'), findsNWidgets(2));
|
||||||
completer.complete('hello');
|
completer.complete('hello');
|
||||||
await eventFiring(tester);
|
await eventFiring(tester);
|
||||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.done, hello, null)'), findsNWidgets(2));
|
expect(find.text('AsyncSnapshot<String>(ConnectionState.done, hello, null, null)'), findsNWidgets(2));
|
||||||
});
|
});
|
||||||
testWidgets('when completing with error', (WidgetTester tester) async {
|
testWidgets('when completing with error and with empty stack trace', (WidgetTester tester) async {
|
||||||
final Completer<String> completer = Completer<String>();
|
final Completer<String> completer = Completer<String>();
|
||||||
await tester.pumpWidget(Column(children: <Widget>[
|
await tester.pumpWidget(Column(children: <Widget>[
|
||||||
FutureBuilder<String>(future: completer.future, builder: snapshotText),
|
FutureBuilder<String>(future: completer.future, builder: snapshotText),
|
||||||
StreamBuilder<String>(stream: completer.future.asStream(), builder: snapshotText),
|
StreamBuilder<String>(stream: completer.future.asStream(), builder: snapshotText),
|
||||||
]));
|
]));
|
||||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.waiting, null, null)'), findsNWidgets(2));
|
expect(find.text('AsyncSnapshot<String>(ConnectionState.waiting, null, null, null)'), findsNWidgets(2));
|
||||||
completer.completeError('bad');
|
completer.completeError('bad', StackTrace.empty);
|
||||||
await eventFiring(tester);
|
await eventFiring(tester);
|
||||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.done, null, bad)'), findsNWidgets(2));
|
expect(find.text('AsyncSnapshot<String>(ConnectionState.done, null, bad, )'), findsNWidgets(2));
|
||||||
|
});
|
||||||
|
testWidgets('when completing with error and with stack trace', (WidgetTester tester) async {
|
||||||
|
final Completer<String> completer = Completer<String>();
|
||||||
|
await tester.pumpWidget(Column(children: <Widget>[
|
||||||
|
FutureBuilder<String>(future: completer.future, builder: snapshotText),
|
||||||
|
StreamBuilder<String>(stream: completer.future.asStream(), builder: snapshotText),
|
||||||
|
]));
|
||||||
|
expect(find.text('AsyncSnapshot<String>(ConnectionState.waiting, null, null, null)'), findsNWidgets(2));
|
||||||
|
completer.completeError('bad', StackTrace.fromString('trace'));
|
||||||
|
await eventFiring(tester);
|
||||||
|
expect(find.text('AsyncSnapshot<String>(ConnectionState.done, null, bad, trace)'), findsNWidgets(2));
|
||||||
});
|
});
|
||||||
testWidgets('when Future is null', (WidgetTester tester) async {
|
testWidgets('when Future is null', (WidgetTester tester) async {
|
||||||
await tester.pumpWidget(Column(children: <Widget>[
|
await tester.pumpWidget(Column(children: <Widget>[
|
||||||
FutureBuilder<String>(future: null, builder: snapshotText),
|
FutureBuilder<String>(future: null, builder: snapshotText),
|
||||||
StreamBuilder<String>(stream: null, builder: snapshotText),
|
StreamBuilder<String>(stream: null, builder: snapshotText),
|
||||||
]));
|
]));
|
||||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.none, null, null)'), findsNWidgets(2));
|
expect(find.text('AsyncSnapshot<String>(ConnectionState.none, null, null, null)'), findsNWidgets(2));
|
||||||
});
|
});
|
||||||
testWidgets('when initialData is used with null Future and Stream', (WidgetTester tester) async {
|
testWidgets('when initialData is used with null Future and Stream', (WidgetTester tester) async {
|
||||||
await tester.pumpWidget(Column(children: <Widget>[
|
await tester.pumpWidget(Column(children: <Widget>[
|
||||||
FutureBuilder<String>(future: null, builder: snapshotText, initialData: 'I'),
|
FutureBuilder<String>(future: null, builder: snapshotText, initialData: 'I'),
|
||||||
StreamBuilder<String>(stream: null, builder: snapshotText, initialData: 'I'),
|
StreamBuilder<String>(stream: null, builder: snapshotText, initialData: 'I'),
|
||||||
]));
|
]));
|
||||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.none, I, null)'), findsNWidgets(2));
|
expect(find.text('AsyncSnapshot<String>(ConnectionState.none, I, null, null)'), findsNWidgets(2));
|
||||||
});
|
});
|
||||||
testWidgets('when using initialData and completing with data', (WidgetTester tester) async {
|
testWidgets('when using initialData and completing with data', (WidgetTester tester) async {
|
||||||
final Completer<String> completer = Completer<String>();
|
final Completer<String> completer = Completer<String>();
|
||||||
@ -287,10 +309,10 @@ void main() {
|
|||||||
FutureBuilder<String>(future: completer.future, builder: snapshotText, initialData: 'I'),
|
FutureBuilder<String>(future: completer.future, builder: snapshotText, initialData: 'I'),
|
||||||
StreamBuilder<String>(stream: completer.future.asStream(), builder: snapshotText, initialData: 'I'),
|
StreamBuilder<String>(stream: completer.future.asStream(), builder: snapshotText, initialData: 'I'),
|
||||||
]));
|
]));
|
||||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.waiting, I, null)'), findsNWidgets(2));
|
expect(find.text('AsyncSnapshot<String>(ConnectionState.waiting, I, null, null)'), findsNWidgets(2));
|
||||||
completer.complete('hello');
|
completer.complete('hello');
|
||||||
await eventFiring(tester);
|
await eventFiring(tester);
|
||||||
expect(find.text('AsyncSnapshot<String>(ConnectionState.done, hello, null)'), findsNWidgets(2));
|
expect(find.text('AsyncSnapshot<String>(ConnectionState.done, hello, null, null)'), findsNWidgets(2));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
group('StreamBuilderBase', () {
|
group('StreamBuilderBase', () {
|
||||||
@ -326,11 +348,11 @@ void main() {
|
|||||||
final StreamController<String> controller = StreamController<String>();
|
final StreamController<String> controller = StreamController<String>();
|
||||||
await tester.pumpWidget(StringCollector(key: key, stream: controller.stream));
|
await tester.pumpWidget(StringCollector(key: key, stream: controller.stream));
|
||||||
controller.add('1');
|
controller.add('1');
|
||||||
controller.addError('bad');
|
controller.addError('bad', StackTrace.fromString('trace'));
|
||||||
controller.add('2');
|
controller.add('2');
|
||||||
controller.close();
|
controller.close();
|
||||||
await eventFiring(tester);
|
await eventFiring(tester);
|
||||||
expect(find.text('conn, data:1, error:bad, data:2, done'), findsOneWidget);
|
expect(find.text('conn, data:1, error:bad stackTrace:trace, data:2, done'), findsOneWidget);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -352,7 +374,7 @@ class StringCollector extends StreamBuilderBase<String, List<String>> {
|
|||||||
List<String> afterData(List<String> current, String data) => current..add('data:$data');
|
List<String> afterData(List<String> current, String data) => current..add('data:$data');
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<String> afterError(List<String> current, dynamic error) => current..add('error:$error');
|
List<String> afterError(List<String> current, dynamic error, StackTrace stackTrace) => current..add('error:$error stackTrace:$stackTrace');
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<String> afterDone(List<String> current) => current..add('done');
|
List<String> afterDone(List<String> current) => current..add('done');
|
||||||
|
Loading…
x
Reference in New Issue
Block a user