From 5a2bf184889585bd85db352d210abbcf4987f98c Mon Sep 17 00:00:00 2001 From: Adam Barth Date: Tue, 10 Jan 2017 10:15:33 -0800 Subject: [PATCH] Improve test coverage and fix bugs (#7410) This patch improves test coverage for a number of files that had low coverage. This patch also fixes a few minor bugs found by these tests. --- .../flutter/lib/src/gestures/converter.dart | 23 +++++++++- .../flutter/lib/src/painting/edge_insets.dart | 2 +- .../lib/src/services/platform_messages.dart | 20 ++++++--- .../test/gestures/gesture_binding_test.dart | 42 +++++++++++++++++++ .../flutter/test/painting/colors_test.dart | 29 +++++++++++++ .../test/painting/edge_insets_test.dart | 34 +++++++++++++-- .../flutter/test/painting/image_fit_test.dart | 10 ++++- .../test/services/haptic_feedback_test.dart | 20 +++++++++ .../test/services/image_decoder_test.dart | 26 ++++++++++++ .../test/services/path_provider_test.dart | 40 ++++++++++++++++++ .../test/services/platform_messages_test.dart | 40 ++++++++++++++++++ .../test/services/system_navigator_test.dart | 20 +++++++++ .../test/services/system_sound_test.dart | 20 +++++++++ .../test/services/url_launcher_test.dart | 20 +++++++++ 14 files changed, 334 insertions(+), 12 deletions(-) create mode 100644 packages/flutter/test/painting/colors_test.dart create mode 100644 packages/flutter/test/services/haptic_feedback_test.dart create mode 100644 packages/flutter/test/services/image_decoder_test.dart create mode 100644 packages/flutter/test/services/path_provider_test.dart create mode 100644 packages/flutter/test/services/platform_messages_test.dart create mode 100644 packages/flutter/test/services/system_navigator_test.dart create mode 100644 packages/flutter/test/services/system_sound_test.dart create mode 100644 packages/flutter/test/services/url_launcher_test.dart diff --git a/packages/flutter/lib/src/gestures/converter.dart b/packages/flutter/lib/src/gestures/converter.dart index 508347b8a03..5ba1f05a8dd 100644 --- a/packages/flutter/lib/src/gestures/converter.dart +++ b/packages/flutter/lib/src/gestures/converter.dart @@ -289,9 +289,30 @@ class PointerEventConverter { tilt: datum.tilt ); } - _pointers.remove(datum.device); break; case ui.PointerChange.remove: + assert(_pointers.containsKey(datum.device)); + _PointerState state = _pointers[datum.device]; + if (state.down) { + yield new PointerCancelEvent( + timeStamp: timeStamp, + pointer: state.pointer, + kind: kind, + device: datum.device, + position: position, + buttons: datum.buttons, + obscured: datum.obscured, + pressureMin: datum.pressureMin, + pressureMax: datum.pressureMax, + distance: datum.distance, + distanceMax: datum.distanceMax, + radiusMin: datum.radiusMin, + radiusMax: datum.radiusMax, + orientation: datum.orientation, + tilt: datum.tilt + ); + } + _pointers.remove(datum.device); yield new PointerRemovedEvent( timeStamp: timeStamp, kind: kind, diff --git a/packages/flutter/lib/src/painting/edge_insets.dart b/packages/flutter/lib/src/painting/edge_insets.dart index 107499f3907..2bea989035b 100644 --- a/packages/flutter/lib/src/painting/edge_insets.dart +++ b/packages/flutter/lib/src/painting/edge_insets.dart @@ -100,7 +100,7 @@ class EdgeInsets { Size get collapsedSize => new Size(horizontal, vertical); /// An EdgeInsets with top and bottom as well as left and right flipped. - EdgeInsets get flipped => new EdgeInsets.fromLTRB(left, top, right, bottom); + EdgeInsets get flipped => new EdgeInsets.fromLTRB(right, bottom, left, top); /// Returns a new rect that is bigger than the given rect in each direction by /// the amount of inset in each direction. Specifically, the left edge of the diff --git a/packages/flutter/lib/src/services/platform_messages.dart b/packages/flutter/lib/src/services/platform_messages.dart index 3b36f3bf8b3..ec8ec0d5ae1 100644 --- a/packages/flutter/lib/src/services/platform_messages.dart +++ b/packages/flutter/lib/src/services/platform_messages.dart @@ -155,9 +155,13 @@ class PlatformMessages { /// The given callback will replace the currently registered callback (if any). /// To remove the mock handler, pass `null` as the `handler` argument. static void setMockStringMessageHandler(String channel, Future handler(String message)) { - setMockBinaryMessageHandler(channel, (ByteData message) async { - return _encodeUTF8(await handler(_decodeUTF8(message))); - }); + if (handler == null) { + setMockBinaryMessageHandler(channel, null); + } else { + setMockBinaryMessageHandler(channel, (ByteData message) async { + return _encodeUTF8(await handler(_decodeUTF8(message))); + }); + } } /// Sets a message handler that intercepts outgoing messages in JSON form. @@ -165,8 +169,12 @@ class PlatformMessages { /// The given callback will replace the currently registered callback (if any). /// To remove the mock handler, pass `null` as the `handler` argument. static void setMockJSONMessageHandler(String channel, Future handler(dynamic message)) { - setMockStringMessageHandler(channel, (String message) async { - return _encodeJSON(await handler(_decodeJSON(message))); - }); + if (handler == null) { + setMockStringMessageHandler(channel, null); + } else { + setMockStringMessageHandler(channel, (String message) async { + return _encodeJSON(await handler(_decodeJSON(message))); + }); + } } } diff --git a/packages/flutter/test/gestures/gesture_binding_test.dart b/packages/flutter/test/gestures/gesture_binding_test.dart index 20612bc3971..4b56e7a1e87 100644 --- a/packages/flutter/test/gestures/gesture_binding_test.dart +++ b/packages/flutter/test/gestures/gesture_binding_test.dart @@ -132,4 +132,46 @@ void main() { expect(events[0].runtimeType, equals(PointerDownEvent)); expect(events[1].runtimeType, equals(PointerCancelEvent)); }); + + test('Can expand add and hover pointers', () { + ui.PointerDataPacket packet = const ui.PointerDataPacket( + data: const [ + const ui.PointerData(change: ui.PointerChange.add, device: 24), + const ui.PointerData(change: ui.PointerChange.hover, device: 24), + const ui.PointerData(change: ui.PointerChange.remove, device: 24), + const ui.PointerData(change: ui.PointerChange.hover, device: 24), + ] + ); + + List events = PointerEventConverter.expand( + packet.data, ui.window.devicePixelRatio).toList(); + + expect(events.length, 5); + expect(events[0].runtimeType, equals(PointerAddedEvent)); + expect(events[1].runtimeType, equals(PointerHoverEvent)); + expect(events[2].runtimeType, equals(PointerRemovedEvent)); + expect(events[3].runtimeType, equals(PointerAddedEvent)); + expect(events[4].runtimeType, equals(PointerHoverEvent)); + }); + + test('Synthetic hover and cancel for misplaced down and remove', () { + ui.PointerDataPacket packet = const ui.PointerDataPacket( + data: const [ + const ui.PointerData(change: ui.PointerChange.add, device: 25, physicalX: 10.0, physicalY: 10.0), + const ui.PointerData(change: ui.PointerChange.down, device: 25, physicalX: 15.0, physicalY: 17.0), + const ui.PointerData(change: ui.PointerChange.remove, device: 25), + ] + ); + + List events = PointerEventConverter.expand( + packet.data, ui.window.devicePixelRatio).toList(); + + expect(events.length, 5); + expect(events[0].runtimeType, equals(PointerAddedEvent)); + expect(events[1].runtimeType, equals(PointerHoverEvent)); + expect(events[1].delta, equals(const Offset(5.0, 7.0) / ui.window.devicePixelRatio)); + expect(events[2].runtimeType, equals(PointerDownEvent)); + expect(events[3].runtimeType, equals(PointerCancelEvent)); + expect(events[4].runtimeType, equals(PointerRemovedEvent)); + }); } diff --git a/packages/flutter/test/painting/colors_test.dart b/packages/flutter/test/painting/colors_test.dart new file mode 100644 index 00000000000..b6ca3f4ec32 --- /dev/null +++ b/packages/flutter/test/painting/colors_test.dart @@ -0,0 +1,29 @@ +// 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/painting.dart'; +import 'package:flutter_test/flutter_test.dart'; + +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.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)); + expect(color.withHue(123.0), const HSVColor.fromAHSV(0.7, 123.0, 0.3, 0.6)); + expect(color.withSaturation(0.9), const HSVColor.fromAHSV(0.7, 28.0, 0.9, 0.6)); + expect(color.withValue(0.1), const HSVColor.fromAHSV(0.7, 28.0, 0.3, 0.1)); + + expect(color.toColor(), const Color(0xb399816b)); + + HSVColor result = HSVColor.lerp(color, const HSVColor.fromAHSV(0.3, 128.0, 0.7, 0.2), 0.25); + expect(result.alpha, 0.6); + expect(result.hue, 53.0); + expect(result.saturation, greaterThan(0.3999)); + expect(result.saturation, lessThan(0.4001)); + expect(result.value, 0.5); + }); +} diff --git a/packages/flutter/test/painting/edge_insets_test.dart b/packages/flutter/test/painting/edge_insets_test.dart index 9eccb9e6bef..159d4de1531 100644 --- a/packages/flutter/test/painting/edge_insets_test.dart +++ b/packages/flutter/test/painting/edge_insets_test.dart @@ -3,11 +3,39 @@ // found in the LICENSE file. import 'package:flutter/painting.dart'; - -import 'package:test/test.dart'; +import 'package:flutter_test/flutter_test.dart'; void main() { - test("EdgeInsets.lerp()", () { + test('EdgeInsets control test', () { + const EdgeInsets insets = const EdgeInsets.fromLTRB(5.0, 7.0, 11.0, 13.0); + + expect(insets.toString(), 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)); + expect(insets.topRight, const Offset(-11.0, 7.0)); + expect(insets.bottomLeft, const Offset(5.0, -13.0)); + expect(insets.bottomRight, const Offset(-11.0, -13.0)); + + expect(insets.collapsedSize, const Size(16.0, 20.0)); + expect(insets.flipped, const EdgeInsets.fromLTRB(11.0, 13.0, 5.0, 7.0)); + + expect(insets.inflateRect(new Rect.fromLTRB(23.0, 32.0, 124.0, 143.0)), + new Rect.fromLTRB(18.0, 25.0, 135.0, 156.0)); + + expect(insets.deflateRect(new Rect.fromLTRB(23.0, 32.0, 124.0, 143.0)), + new Rect.fromLTRB(28.0, 39.0, 113.0, 130.0)); + + expect(insets.inflateSize(const Size(100.0, 125.0)), const Size(116.0, 145.0)); + expect(insets.deflateSize(const Size(100.0, 125.0)), const Size(84.0, 105.0)); + + expect(insets / 2.0, const EdgeInsets.fromLTRB(2.5, 3.5, 5.5, 6.5)); + expect(insets ~/ 2.0, const EdgeInsets.fromLTRB(2.0, 3.0, 5.0, 6.0)); + expect(insets % 5.0, const EdgeInsets.fromLTRB(0.0, 2.0, 1.0, 3.0)); + + }); + + test('EdgeInsets.lerp()', () { EdgeInsets a = const EdgeInsets.all(10.0); EdgeInsets b = const EdgeInsets.all(20.0); expect(EdgeInsets.lerp(a, b, 0.25), equals(a * 1.25)); diff --git a/packages/flutter/test/painting/image_fit_test.dart b/packages/flutter/test/painting/image_fit_test.dart index 4156061f623..496e7b700ab 100644 --- a/packages/flutter/test/painting/image_fit_test.dart +++ b/packages/flutter/test/painting/image_fit_test.dart @@ -7,7 +7,7 @@ import 'package:flutter/painting.dart'; import 'package:test/test.dart'; void main() { - test("applyImageFit", () { + test('applyImageFit', () { FittedSizes result; result = applyImageFit(ImageFit.scaleDown, const Size(100.0, 1000.0), const Size(200.0, 2000.0)); @@ -17,5 +17,13 @@ void main() { result = applyImageFit(ImageFit.scaleDown, const Size(300.0, 3000.0), const Size(200.0, 2000.0)); expect(result.source, equals(const Size(300.0, 3000.0))); expect(result.destination, equals(const Size(200.0, 2000.0))); + + result = applyImageFit(ImageFit.fitWidth, const Size(2000.0, 400.0), const Size(1000.0, 100.0)); + expect(result.source, equals(const Size(2000.0, 200.0))); + expect(result.destination, equals(const Size(1000.0, 100.0))); + + result = applyImageFit(ImageFit.fitHeight, const Size(400.0, 2000.0), const Size(100.0, 1000.0)); + expect(result.source, equals(const Size(200.0, 2000.0))); + expect(result.destination, equals(const Size(100.0, 1000.0))); }); } diff --git a/packages/flutter/test/services/haptic_feedback_test.dart b/packages/flutter/test/services/haptic_feedback_test.dart new file mode 100644 index 00000000000..72518888d9b --- /dev/null +++ b/packages/flutter/test/services/haptic_feedback_test.dart @@ -0,0 +1,20 @@ +// Copyright 2016 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/services.dart'; +import 'package:test/test.dart'; + +void main() { + test('Haptic feedback control test', () async { + List log = []; + + PlatformMessages.setMockStringMessageHandler('flutter/platform', (String message) async { + log.add(message); + }); + + await HapticFeedback.vibrate(); + + expect(log, equals(['{"method":"HapticFeedback.vibrate","args":[]}'])); + }); +} diff --git a/packages/flutter/test/services/image_decoder_test.dart b/packages/flutter/test/services/image_decoder_test.dart new file mode 100644 index 00000000000..9b0c789fc59 --- /dev/null +++ b/packages/flutter/test/services/image_decoder_test.dart @@ -0,0 +1,26 @@ +// Copyright 2016 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 'dart:ui' as ui; +import 'dart:typed_data'; + +import 'package:flutter/services.dart'; +import 'package:test/test.dart'; + +const List transparentImage = const [ + 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x49, + 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x08, 0x06, + 0x00, 0x00, 0x00, 0x1F, 0x15, 0xC4, 0x89, 0x00, 0x00, 0x00, 0x0A, 0x49, 0x44, + 0x41, 0x54, 0x78, 0x9C, 0x63, 0x00, 0x01, 0x00, 0x00, 0x05, 0x00, 0x01, 0x0D, + 0x0A, 0x2D, 0xB4, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44, 0xAE, +]; + +void main() { + test('Image decoder control test', () async { + ui.Image image = await decodeImageFromList(new Uint8List.fromList(transparentImage)); + expect(image, isNotNull); + expect(image.width, 1); + expect(image.height, 1); + }); +} diff --git a/packages/flutter/test/services/path_provider_test.dart b/packages/flutter/test/services/path_provider_test.dart new file mode 100644 index 00000000000..6fd12371022 --- /dev/null +++ b/packages/flutter/test/services/path_provider_test.dart @@ -0,0 +1,40 @@ +// Copyright 2016 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 'dart:io'; + +import 'package:flutter/services.dart'; +import 'package:test/test.dart'; + +void main() { + test('Path provider control test', () async { + List log = []; + String response; + + PlatformMessages.setMockStringMessageHandler('flutter/platform', (String message) async { + log.add(message); + return response; + }); + + Directory directory = await PathProvider.getTemporaryDirectory(); + + expect(log, equals(['{"method":"PathProvider.getTemporaryDirectory","args":[]}'])); + expect(directory, isNull); + log.clear(); + + directory = await PathProvider.getApplicationDocumentsDirectory(); + + expect(log, equals(['{"method":"PathProvider.getApplicationDocumentsDirectory","args":[]}'])); + expect(directory, isNull); + + String fakePath = "/foo/bar/baz"; + response = '{"path":"$fakePath"}'; + + directory = await PathProvider.getTemporaryDirectory(); + expect(directory.path, equals(fakePath)); + + directory = await PathProvider.getApplicationDocumentsDirectory(); + expect(directory.path, equals(fakePath)); + }); +} diff --git a/packages/flutter/test/services/platform_messages_test.dart b/packages/flutter/test/services/platform_messages_test.dart new file mode 100644 index 00000000000..b7555019b30 --- /dev/null +++ b/packages/flutter/test/services/platform_messages_test.dart @@ -0,0 +1,40 @@ +// Copyright 2016 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/services.dart'; +import 'package:test/test.dart'; + +void main() { + test('Mock string message handler control test', () async { + List log = []; + + PlatformMessages.setMockStringMessageHandler('test1', (String message) async { + log.add(message); + }); + + await PlatformMessages.sendString('test1', 'hello'); + expect(log, equals(['hello'])); + log.clear(); + + PlatformMessages.setMockStringMessageHandler('test1', null); + await PlatformMessages.sendString('test1', 'fail'); + expect(log, isEmpty); + }); + + test('Mock JSON message handler control test', () async { + List log = []; + + PlatformMessages.setMockJSONMessageHandler('test2', (dynamic message) async { + log.add(message); + }); + + await PlatformMessages.sendString('test2', '{"hello": "world"}'); + expect(log, equals(>[{'hello': 'world'}])); + log.clear(); + + PlatformMessages.setMockStringMessageHandler('test2', null); + await PlatformMessages.sendString('test2', '{"fail": "message"}'); + expect(log, isEmpty); + }); +} diff --git a/packages/flutter/test/services/system_navigator_test.dart b/packages/flutter/test/services/system_navigator_test.dart new file mode 100644 index 00000000000..b916ff4b6fa --- /dev/null +++ b/packages/flutter/test/services/system_navigator_test.dart @@ -0,0 +1,20 @@ +// Copyright 2016 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/services.dart'; +import 'package:test/test.dart'; + +void main() { + test('System navigator control test', () async { + List log = []; + + PlatformMessages.setMockStringMessageHandler('flutter/platform', (String message) async { + log.add(message); + }); + + await SystemNavigator.pop(); + + expect(log, equals(['{"method":"SystemNavigator.pop","args":[]}'])); + }); +} diff --git a/packages/flutter/test/services/system_sound_test.dart b/packages/flutter/test/services/system_sound_test.dart new file mode 100644 index 00000000000..8d394e6ce56 --- /dev/null +++ b/packages/flutter/test/services/system_sound_test.dart @@ -0,0 +1,20 @@ +// Copyright 2016 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/services.dart'; +import 'package:test/test.dart'; + +void main() { + test('System sound control test', () async { + List log = []; + + PlatformMessages.setMockStringMessageHandler('flutter/platform', (String message) async { + log.add(message); + }); + + await SystemSound.play(SystemSoundType.click); + + expect(log, equals(['{"method":"SystemSound.play","args":["SystemSoundType.click"]}'])); + }); +} diff --git a/packages/flutter/test/services/url_launcher_test.dart b/packages/flutter/test/services/url_launcher_test.dart new file mode 100644 index 00000000000..3fd0ad2c9bd --- /dev/null +++ b/packages/flutter/test/services/url_launcher_test.dart @@ -0,0 +1,20 @@ +// Copyright 2016 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/services.dart'; +import 'package:test/test.dart'; + +void main() { + test('URL launcher control test', () async { + List log = []; + + PlatformMessages.setMockStringMessageHandler('flutter/platform', (String message) async { + log.add(message); + }); + + await UrlLauncher.launch('http://example.com/'); + + expect(log, equals(['{"method":"UrlLauncher.launch","args":["http://example.com/"]}'])); + }); +}