Commit 2ee04c92 authored by Adam Barth's avatar Adam Barth Committed by GitHub

Improve test coverage (#7430)

Also, make the exception handling for global key listeners slightly more
robust.
parent 42ccffca
......@@ -151,7 +151,7 @@ abstract class GlobalKey<T extends State<StatefulWidget>> extends Key {
void _register(Element element) {
assert(() {
if (_registry.containsKey(this)) {
int oldCount = _debugDuplicates.putIfAbsent(this, () => 1);
final int oldCount = _debugDuplicates.putIfAbsent(this, () => 1);
assert(oldCount >= 1);
_debugDuplicates[this] = oldCount + 1;
}
......@@ -163,7 +163,7 @@ abstract class GlobalKey<T extends State<StatefulWidget>> extends Key {
void _unregister(Element element) {
assert(() {
if (_registry.containsKey(this) && _debugDuplicates.containsKey(this)) {
int oldCount = _debugDuplicates[this];
final int oldCount = _debugDuplicates[this];
assert(oldCount >= 2);
if (oldCount == 2) {
_debugDuplicates.remove(this);
......@@ -257,17 +257,19 @@ abstract class GlobalKey<T extends State<StatefulWidget>> extends Key {
for (GlobalKey key in _removedKeys) {
if (!_registry.containsKey(key) && _removeListeners.containsKey(key)) {
Set<GlobalKeyRemoveListener> localListeners = new HashSet<GlobalKeyRemoveListener>.from(_removeListeners[key]);
for (GlobalKeyRemoveListener listener in localListeners)
for (GlobalKeyRemoveListener listener in localListeners) {
try {
listener(key);
} catch (e, stack) {
_debugReportException('while notifying GlobalKey listener', e, stack);
}
}
}
}
} catch (e, stack) {
_debugReportException('while notifying GlobalKey listeners', e, stack);
} finally {
_removedKeys.clear();
}
}
}
/// A global key with a debugging label.
......
......@@ -525,7 +525,6 @@ class _GestureSemantics extends SingleChildRenderObjectWidget {
return;
}
}
assert(false);
}
void _handleVerticalDragUpdate(DragUpdateDetails updateDetails) {
......@@ -553,7 +552,6 @@ class _GestureSemantics extends SingleChildRenderObjectWidget {
return;
}
}
assert(false);
}
@override
......
......@@ -72,6 +72,20 @@ class MediaQueryData {
return size.width > size.height ? Orientation.landscape : Orientation.portrait;
}
MediaQueryData copyWith({
Size size,
double devicePixelRatio,
double textScaleFactor,
EdgeInsets padding,
}) {
return new MediaQueryData(
size: size ?? this.size,
devicePixelRatio: devicePixelRatio ?? this.devicePixelRatio,
textScaleFactor: textScaleFactor ?? this.textScaleFactor,
padding: padding ?? this.padding,
);
}
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType)
......
......@@ -6,8 +6,6 @@ import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/animation.dart';
import 'package:flutter/widgets.dart';
import 'animation_tester.dart';
void main() {
setUp(() {
WidgetsFlutterBinding.ensureInitialized();
......@@ -206,17 +204,17 @@ void main() {
duration: const Duration(milliseconds: 100),
vsync: const TestVSync(),
);
expect(controller.toString(), hasOneLineDescription);
expect(controller, hasOneLineDescription);
controller.forward();
WidgetsBinding.instance.handleBeginFrame(const Duration(milliseconds: 10));
WidgetsBinding.instance.handleBeginFrame(const Duration(milliseconds: 20));
expect(controller.toString(), hasOneLineDescription);
expect(controller, hasOneLineDescription);
WidgetsBinding.instance.handleBeginFrame(const Duration(milliseconds: 30));
expect(controller.toString(), hasOneLineDescription);
expect(controller, hasOneLineDescription);
controller.reverse();
WidgetsBinding.instance.handleBeginFrame(const Duration(milliseconds: 40));
WidgetsBinding.instance.handleBeginFrame(const Duration(milliseconds: 50));
expect(controller.toString(), hasOneLineDescription);
expect(controller, hasOneLineDescription);
controller.stop();
});
......
......@@ -6,8 +6,6 @@ import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/animation.dart';
import 'package:flutter/widgets.dart';
import 'animation_tester.dart';
void main() {
setUp(() {
WidgetsFlutterBinding.ensureInitialized();
......@@ -15,16 +13,16 @@ void main() {
});
test('toString control test', () {
expect(kAlwaysCompleteAnimation.toString(), hasOneLineDescription);
expect(kAlwaysDismissedAnimation.toString(), hasOneLineDescription);
expect(const AlwaysStoppedAnimation<double>(0.5).toString(), hasOneLineDescription);
expect(kAlwaysCompleteAnimation, hasOneLineDescription);
expect(kAlwaysDismissedAnimation, hasOneLineDescription);
expect(const AlwaysStoppedAnimation<double>(0.5), hasOneLineDescription);
CurvedAnimation curvedAnimation = new CurvedAnimation(
parent: kAlwaysDismissedAnimation,
curve: Curves.ease
);
expect(curvedAnimation.toString(), hasOneLineDescription);
expect(curvedAnimation, hasOneLineDescription);
curvedAnimation.reverseCurve = Curves.elasticOut;
expect(curvedAnimation.toString(), hasOneLineDescription);
expect(curvedAnimation, hasOneLineDescription);
AnimationController controller = new AnimationController(
duration: const Duration(milliseconds: 500),
vsync: const TestVSync(),
......@@ -37,7 +35,7 @@ void main() {
curve: Curves.ease,
reverseCurve: Curves.elasticOut
);
expect(curvedAnimation.toString(), hasOneLineDescription);
expect(curvedAnimation, hasOneLineDescription);
controller.stop();
});
......@@ -45,9 +43,9 @@ void main() {
ProxyAnimation animation = new ProxyAnimation();
expect(animation.value, 0.0);
expect(animation.status, AnimationStatus.dismissed);
expect(animation.toString(), hasOneLineDescription);
expect(animation, hasOneLineDescription);
animation.parent = kAlwaysDismissedAnimation;
expect(animation.toString(), hasOneLineDescription);
expect(animation, hasOneLineDescription);
});
test('ProxyAnimation set parent generates value changed', () {
......@@ -88,7 +86,7 @@ void main() {
expect(didReceiveCallback, isFalse);
controller.value = 0.7;
expect(didReceiveCallback, isFalse);
expect(animation.toString(), hasOneLineDescription);
expect(animation, hasOneLineDescription);
});
test('TrainHoppingAnimation', () {
......@@ -107,11 +105,11 @@ void main() {
});
expect(didSwitchTrains, isFalse);
expect(animation.value, 0.5);
expect(animation.toString(), hasOneLineDescription);
expect(animation, hasOneLineDescription);
nextTrain.value = 0.25;
expect(didSwitchTrains, isTrue);
expect(animation.value, 0.25);
expect(animation.toString(), hasOneLineDescription);
expect(animation, hasOneLineDescription);
expect(animation.toString(), contains('no next'));
});
}
......@@ -6,8 +6,6 @@ import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/animation.dart';
import 'package:flutter/widgets.dart';
import 'animation_tester.dart';
void main() {
test('Can chain tweens', () {
Tween<double> tween = new Tween<double>(begin: 0.30, end: 0.50);
......
......@@ -9,7 +9,7 @@ void main() {
test('HSVColor control test', () {
const HSVColor color = const HSVColor.fromAHSV(0.7, 28.0, 0.3, 0.6);
expect(color.toString(), hasOneLineDescription);
expect(color, hasOneLineDescription);
expect(color.hashCode, equals(new HSVColor.fromAHSV(0.7, 28.0, 0.3, 0.6).hashCode));
expect(color.withAlpha(0.8), const HSVColor.fromAHSV(0.8, 28.0, 0.3, 0.6));
......
......@@ -9,7 +9,7 @@ void main() {
test('EdgeInsets control test', () {
const EdgeInsets insets = const EdgeInsets.fromLTRB(5.0, 7.0, 11.0, 13.0);
expect(insets.toString(), hasOneLineDescription);
expect(insets, hasOneLineDescription);
expect(insets.hashCode, equals(new EdgeInsets.fromLTRB(5.0, 7.0, 11.0, 13.0).hashCode));
expect(insets.topLeft, const Offset(5.0, 7.0));
......
// Copyright 2015 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_test/flutter_test.dart';
import 'package:flutter/widgets.dart';
void main() {
testWidgets('UniqueKey control test', (WidgetTester tester) async {
Key key = new UniqueKey();
expect(key, hasOneLineDescription);
expect(key, isNot(equals(new UniqueKey())));
});
testWidgets('ObjectKey control test', (WidgetTester tester) async {
Object a = new Object();
Object b = new Object();
Key keyA = new ObjectKey(a);
Key keyA2 = new ObjectKey(a);
Key keyB = new ObjectKey(b);
expect(keyA, hasOneLineDescription);
expect(keyA, equals(keyA2));
expect(keyA.hashCode, equals(keyA2.hashCode));
expect(keyA, isNot(equals(keyB)));
});
testWidgets('GlobalKey duplication', (WidgetTester tester) async {
Key key = new GlobalKey(debugLabel: 'problematic');
await tester.pumpWidget(new Stack(
children: <Widget>[
new Container(
key: const ValueKey<int>(1),
),
new Container(
key: const ValueKey<int>(2),
),
new Container(
key: key
),
],
));
await tester.pumpWidget(new Stack(
children: <Widget>[
new Container(
key: const ValueKey<int>(1),
child: new SizedBox(key: key),
),
new Container(
key: const ValueKey<int>(2),
child: new Placeholder(key: key),
),
],
));
expect(tester.takeException(), isNotNull);
});
testWidgets('GlobalKey notification exception handling', (WidgetTester tester) async {
GlobalKey key = new GlobalKey();
await tester.pumpWidget(new Container(key: key));
GlobalKey.registerRemoveListener(key, (GlobalKey key) {
throw new Exception('Misbehaving listener');
});
bool didReceiveCallback = false;
GlobalKey.registerRemoveListener(key, (GlobalKey key) {
expect(didReceiveCallback, isFalse);
didReceiveCallback = true;
});
await tester.pumpWidget(new Placeholder());
expect(tester.takeException(), isNotNull);
expect(didReceiveCallback, isTrue);
});
}
......@@ -14,7 +14,10 @@ void main() {
await tester.pumpWidget(
new Builder(
builder: (BuildContext context) {
size = MediaQuery.of(context).size;
final MediaQueryData data = MediaQuery.of(context);
expect(data, hasOneLineDescription);
expect(data.hashCode, equals(data.copyWith().hashCode));
size = data.size;
return new Container();
}
)
......
// Copyright 2017 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_test/flutter_test.dart';
import 'package:flutter/widgets.dart';
void main() {
testWidgets('Performance overlay smoke test', (WidgetTester tester) async {
await tester.pumpWidget(new PerformanceOverlay());
await tester.pumpWidget(new PerformanceOverlay.allEnabled());
});
}
// Copyright 2017 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_test/flutter_test.dart';
import 'package:flutter/widgets.dart';
void main() {
testWidgets('Placeholder control test', (WidgetTester tester) async {
GlobalKey<PlaceholderState> key = new GlobalKey<PlaceholderState>();
await tester.pumpWidget(new Placeholder(key: key));
key.currentState.child = new Text('target');
expect(find.text('target'), findsNothing);
await tester.pump();
expect(find.text('target'), findsOneWidget);
});
}
......@@ -18,6 +18,7 @@ void sendFakeKeyEvent(Map<String, dynamic> data) {
void main() {
testWidgets('Can dispose without keyboard', (WidgetTester tester) async {
await tester.pumpWidget(new RawKeyboardListener(child: new Container()));
await tester.pumpWidget(new RawKeyboardListener(child: new Container()));
await tester.pumpWidget(new Container());
});
......
......@@ -96,6 +96,11 @@ Future<Null> runNavigatorTest(
}
void main() {
testWidgets('Route settings', (WidgetTester tester) async {
RouteSettings settings = const RouteSettings(name: 'A');
expect(settings, hasOneLineDescription);
});
testWidgets('Route management - push, replace, pop', (WidgetTester tester) async {
GlobalKey<NavigatorState> navigatorKey = new GlobalKey<NavigatorState>();
await tester.pumpWidget(new Navigator(
......@@ -320,6 +325,7 @@ void main() {
'B: didChangeNext C',
]
);
expect(routeC.isActive, isTrue);
TestRoute routeB;
await runNavigatorTest(
tester,
......
......@@ -14,13 +14,13 @@ void main() {
children: <Widget>[
new Semantics(),
new Semantics(
container: true
container: true,
),
new Semantics(
label: 'label'
label: 'label',
),
],
),
]
)
);
await tester.pumpWidget(
......@@ -29,20 +29,21 @@ void main() {
children: <Widget>[
new Semantics(),
new Semantics(
container: true
container: true,
),
new Semantics(
label: 'label'
label: 'label',
),
],
),
),
]
)
)
);
expect(true, isTrue); // expect that we reach here without crashing
});
testWidgets('SemanticsDebugger reparents subtree', (WidgetTester tester) async {
testWidgets('SemanticsDebugger reparents subtree',
(WidgetTester tester) async {
GlobalKey key = new GlobalKey();
await tester.pumpWidget(
......@@ -51,12 +52,16 @@ void main() {
children: <Widget>[
new Semantics(label: 'label1'),
new Positioned(
key: key, left: 0.0, top: 0.0, width: 100.0, height: 100.0,
child: new Semantics(label: 'label2')
key: key,
left: 0.0,
top: 0.0,
width: 100.0,
height: 100.0,
child: new Semantics(label: 'label2'),
),
],
),
),
]
)
)
);
await tester.pumpWidget(
......@@ -69,16 +74,20 @@ void main() {
child: new Stack(
children: <Widget>[
new Positioned(
key: key, left: 0.0, top: 0.0, width: 100.0, height: 100.0,
child: new Semantics(label: 'label2')
key: key,
left: 0.0,
top: 0.0,
width: 100.0,
height: 100.0,
child: new Semantics(label: 'label2'),
),
new Semantics(label: 'label3'),
]
)
)
]
)
)
],
),
),
],
),
),
);
await tester.pumpWidget(
......@@ -91,19 +100,180 @@ void main() {
child: new Stack(
children: <Widget>[
new Positioned(
key: key, left: 0.0, top: 0.0, width: 100.0, height: 100.0,
child: new Semantics(label: 'label2')
),
key: key,
left: 0.0,
top: 0.0,
width: 100.0,
height: 100.0,
child: new Semantics(label: 'label2')),
new Semantics(label: 'label3'),
new Semantics(label: 'label4'),
]
)
)
]
)
)
],
),
),
],
),
),
);
expect(tester.takeException(), isNull);
});
testWidgets('SemanticsDebugger interaction test',
(WidgetTester tester) async {
final List<String> log = <String>[];
await tester.pumpWidget(
new SemanticsDebugger(
child: new Material(
child: new Block(children: <Widget>[
new RaisedButton(
onPressed: () {
log.add('top');
},
child: new Text('TOP'),
),
new RaisedButton(
onPressed: () {
log.add('bottom');
},
child: new Text('BOTTOM'),
),
]),
),
),
);
await tester.tap(find.text('TOP'));
expect(log, equals(<String>['top']));
log.clear();
await tester.tap(find.text('BOTTOM'));
expect(log, equals(<String>['bottom']));
log.clear();
});
testWidgets('SemanticsDebugger scroll test', (WidgetTester tester) async {
Key childKey = new UniqueKey();
await tester.pumpWidget(
new SemanticsDebugger(
child: new Block(
children: <Widget>[
new Container(
key: childKey,
height: 5000.0,
decoration:
new BoxDecoration(backgroundColor: Colors.green[500]),
),
],
),
),
);
expect(tester.getTopLeft(find.byKey(childKey)).y, equals(0.0));
await tester.fling(find.byType(Block), const Offset(0.0, -200.0), 200.0);
await tester.pump();
expect(tester.getTopLeft(find.byKey(childKey)).y, equals(-480.0));
await tester.fling(find.byType(Block), const Offset(200.0, 0.0), 200.0);
await tester.pump();
expect(tester.getTopLeft(find.byKey(childKey)).y, equals(-480.0));
await tester.fling(find.byType(Block), const Offset(-200.0, 0.0), 200.0);
await tester.pump();
expect(tester.getTopLeft(find.byKey(childKey)).y, equals(-480.0));
await tester.fling(find.byType(Block), const Offset(0.0, 200.0), 200.0);
await tester.pump();
expect(tester.getTopLeft(find.byKey(childKey)).y, equals(0.0));
});
testWidgets('SemanticsDebugger long press', (WidgetTester tester) async {
bool didLongPress = false;
await tester.pumpWidget(
new SemanticsDebugger(
child: new GestureDetector(
onLongPress: () {
expect(didLongPress, isFalse);
didLongPress = true;
},
child: new Text('target'),
),
),
);
await tester.longPress(find.text('target'));
expect(didLongPress, isTrue);
});
testWidgets('SemanticsDebugger slider', (WidgetTester tester) async {
double value = 0.75;
await tester.pumpWidget(
new SemanticsDebugger(
child: new Material(
child: new Center(
child: new Slider(
value: value,
onChanged: (double newValue) {
value = newValue;
},
),
),
),
),
);
await tester.fling(find.byType(Slider), const Offset(-100.0, 0.0), 100.0);
expect(value, equals(0.65));
});
testWidgets('SemanticsDebugger checkbox', (WidgetTester tester) async {
Key keyTop = new UniqueKey();
Key keyBottom = new UniqueKey();
bool valueTop = false;
bool valueBottom = true;
await tester.pumpWidget(
new SemanticsDebugger(
child: new Material(
child: new Block(
children: <Widget>[
new Checkbox(
key: keyTop,
value: valueTop,
onChanged: (bool newValue) {
valueTop = newValue;
},
),
new Checkbox(
key: keyBottom,
value: valueBottom,
onChanged: null,
),
],
),
),
),
);
await tester.tap(find.byKey(keyTop));
expect(valueTop, isTrue);
valueTop = false;
expect(valueTop, isFalse);
await tester.tap(find.byKey(keyBottom));
expect(valueTop, isFalse);
expect(valueTop, isFalse);
});
}
// Copyright 2017 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_test/flutter_test.dart';
import 'package:flutter/widgets.dart';
class TestStatusTransitionWidget extends StatusTransitionWidget {
TestStatusTransitionWidget({
Key key,
this.builder,
Animation<double> animation,
}) : super(key: key, animation: animation);
final WidgetBuilder builder;
@override
Widget build(BuildContext context) => builder(context);
}
void main() {
testWidgets('Status transition control test', (WidgetTester tester) async {
bool didBuild = false;
final AnimationController controller = new AnimationController(
duration: const Duration(seconds: 1),
vsync: const TestVSync(),
);
await tester.pumpWidget(new TestStatusTransitionWidget(
animation: controller,
builder: (BuildContext context) {
expect(didBuild, isFalse);
didBuild = true;
return new Container();
},
));
expect(didBuild, isTrue);
didBuild = false;
controller.forward();
expect(didBuild, isFalse);
await tester.pump();
expect(didBuild, isTrue);
didBuild = false;
await tester.pump(const Duration(milliseconds: 100));
expect(didBuild, isFalse);
await tester.pump(const Duration(milliseconds: 850));
expect(didBuild, isFalse);
await tester.pump(const Duration(milliseconds: 100));
expect(didBuild, isTrue);
didBuild = false;
controller.forward();
await tester.pump(const Duration(milliseconds: 100));
expect(didBuild, isFalse);
controller.stop();
await tester.pump(const Duration(milliseconds: 100));
expect(didBuild, isFalse);
final AnimationController anotherController = new AnimationController(
duration: const Duration(seconds: 1),
vsync: const TestVSync(),
);
await tester.pumpWidget(new TestStatusTransitionWidget(
animation: anotherController,
builder: (BuildContext context) {
expect(didBuild, isFalse);
didBuild = true;
return new Container();
},
));
expect(didBuild, isTrue);
didBuild = false;
controller.reverse();
await tester.pump(const Duration(milliseconds: 100));
expect(didBuild, isFalse);
anotherController.forward();
await tester.pump(const Duration(milliseconds: 100));
expect(didBuild, isTrue);
didBuild = false;
controller.stop();
anotherController.stop();
});
}
......@@ -15,4 +15,5 @@ export 'src/matchers.dart';
export 'src/test_async_utils.dart';
export 'src/stack_manipulation.dart';
export 'src/test_pointer.dart';
export 'src/test_vsync.dart';
export 'src/widget_tester.dart';
......@@ -4,7 +4,12 @@
import 'package:flutter/scheduler.dart';
/// A [TickerProvider] that creates a standalone ticker.
///
/// Useful in tests that create an [AnimationController] outside of the widget
/// tree.
class TestVSync implements TickerProvider {
/// Creates a ticker provider that creates standalone tickers.
const TestVSync();
@override
......
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