| // 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:async'; |
| import 'dart:ui' as ui show Image; |
| |
| import 'package:flutter/foundation.dart'; |
| import 'package:flutter/painting.dart'; |
| import 'package:flutter/services.dart'; |
| import 'package:quiver/testing/async.dart'; |
| |
| import 'package:test/test.dart'; |
| import '../services/mocks_for_image_cache.dart'; |
| |
| class TestCanvas implements Canvas { |
| TestCanvas([this.invocations]); |
| |
| final List<Invocation> invocations; |
| |
| @override |
| void noSuchMethod(Invocation invocation) { |
| invocations?.add(invocation); |
| } |
| } |
| |
| class SynchronousTestImageProvider extends ImageProvider<int> { |
| @override |
| Future<int> obtainKey(ImageConfiguration configuration) { |
| return new SynchronousFuture<int>(1); |
| } |
| |
| @override |
| ImageStreamCompleter load(int key) { |
| return new OneFrameImageStreamCompleter( |
| new SynchronousFuture<ImageInfo>(new TestImageInfo(key)) |
| ); |
| } |
| } |
| |
| class AsyncTestImageProvider extends ImageProvider<int> { |
| @override |
| Future<int> obtainKey(ImageConfiguration configuration) { |
| return new Future<int>.value(2); |
| } |
| |
| @override |
| ImageStreamCompleter load(int key) { |
| return new OneFrameImageStreamCompleter( |
| new Future<ImageInfo>.value(new TestImageInfo(key)) |
| ); |
| } |
| } |
| |
| class DelayedImageProvider extends ImageProvider<DelayedImageProvider> { |
| final Completer<ImageInfo> _completer = new Completer<ImageInfo>(); |
| |
| @override |
| Future<DelayedImageProvider> obtainKey(ImageConfiguration configuration) { |
| return new SynchronousFuture<DelayedImageProvider>(this); |
| } |
| |
| @override |
| ImageStream resolve(ImageConfiguration configuration) { |
| return super.resolve(configuration); |
| } |
| |
| @override |
| ImageStreamCompleter load(DelayedImageProvider key) { |
| return new OneFrameImageStreamCompleter(_completer.future); |
| } |
| |
| void complete() { |
| _completer.complete(new ImageInfo(image: new TestImage())); |
| } |
| |
| @override |
| String toString() => '$runtimeType#$hashCode()'; |
| } |
| |
| class TestImage extends ui.Image { |
| @override |
| int get width => 100; |
| |
| @override |
| int get height => 100; |
| |
| @override |
| void dispose() { } |
| } |
| |
| void main() { |
| test("Decoration.lerp()", () { |
| final BoxDecoration a = const BoxDecoration(color: const Color(0xFFFFFFFF)); |
| final BoxDecoration b = const BoxDecoration(color: const Color(0x00000000)); |
| |
| BoxDecoration c = Decoration.lerp(a, b, 0.0); |
| expect(c.color, equals(a.color)); |
| |
| c = Decoration.lerp(a, b, 0.25); |
| expect(c.color, equals(Color.lerp(const Color(0xFFFFFFFF), const Color(0x00000000), 0.25))); |
| |
| c = Decoration.lerp(a, b, 1.0); |
| expect(c.color, equals(b.color)); |
| }); |
| |
| test("BoxDecorationImageListenerSync", () { |
| final ImageProvider imageProvider = new SynchronousTestImageProvider(); |
| final DecorationImage backgroundImage = new DecorationImage(image: imageProvider); |
| |
| final BoxDecoration boxDecoration = new BoxDecoration(image: backgroundImage); |
| bool onChangedCalled = false; |
| final BoxPainter boxPainter = boxDecoration.createBoxPainter(() { |
| onChangedCalled = true; |
| }); |
| |
| final TestCanvas canvas = new TestCanvas(); |
| final ImageConfiguration imageConfiguration = const ImageConfiguration(size: Size.zero); |
| boxPainter.paint(canvas, Offset.zero, imageConfiguration); |
| |
| // The onChanged callback should not be invoked during the call to boxPainter.paint |
| expect(onChangedCalled, equals(false)); |
| }); |
| |
| test("BoxDecorationImageListenerAsync", () { |
| new FakeAsync().run((FakeAsync async) { |
| final ImageProvider imageProvider = new AsyncTestImageProvider(); |
| final DecorationImage backgroundImage = new DecorationImage(image: imageProvider); |
| |
| final BoxDecoration boxDecoration = new BoxDecoration(image: backgroundImage); |
| bool onChangedCalled = false; |
| final BoxPainter boxPainter = boxDecoration.createBoxPainter(() { |
| onChangedCalled = true; |
| }); |
| |
| final TestCanvas canvas = new TestCanvas(); |
| final ImageConfiguration imageConfiguration = const ImageConfiguration(size: Size.zero); |
| boxPainter.paint(canvas, Offset.zero, imageConfiguration); |
| |
| // The onChanged callback should be invoked asynchronously. |
| expect(onChangedCalled, equals(false)); |
| async.flushMicrotasks(); |
| expect(onChangedCalled, equals(true)); |
| }); |
| }); |
| |
| // Regression test for https://github.com/flutter/flutter/issues/7289. |
| // A reference test would be better. |
| test("BoxDecoration backgroundImage clip", () { |
| void testDecoration({ BoxShape shape, BorderRadius borderRadius, bool expectClip}) { |
| new FakeAsync().run((FakeAsync async) { |
| final DelayedImageProvider imageProvider = new DelayedImageProvider(); |
| final DecorationImage backgroundImage = new DecorationImage(image: imageProvider); |
| |
| final BoxDecoration boxDecoration = new BoxDecoration( |
| shape: shape, |
| borderRadius: borderRadius, |
| image: backgroundImage, |
| ); |
| |
| final List<Invocation> invocations = <Invocation>[]; |
| final TestCanvas canvas = new TestCanvas(invocations); |
| final ImageConfiguration imageConfiguration = const ImageConfiguration( |
| size: const Size(100.0, 100.0) |
| ); |
| bool onChangedCalled = false; |
| final BoxPainter boxPainter = boxDecoration.createBoxPainter(() { |
| onChangedCalled = true; |
| }); |
| |
| // _BoxDecorationPainter._paintDecorationImage() resolves the background |
| // image and adds a listener to the resolved image stream. |
| boxPainter.paint(canvas, Offset.zero, imageConfiguration); |
| imageProvider.complete(); |
| |
| // Run the listener which calls onChanged() which saves an internal |
| // reference to the TestImage. |
| async.flushMicrotasks(); |
| expect(onChangedCalled, isTrue); |
| boxPainter.paint(canvas, Offset.zero, imageConfiguration); |
| |
| // We expect a clip to preceed the drawImageRect call. |
| final List<Invocation> commands = canvas.invocations.where((Invocation invocation) { |
| return invocation.memberName == #clipPath || invocation.memberName == #drawImageRect; |
| }).toList(); |
| if (expectClip) { // We expect a clip to preceed the drawImageRect call. |
| expect(commands.length, 2); |
| expect(commands[0].memberName, equals(#clipPath)); |
| expect(commands[1].memberName, equals(#drawImageRect)); |
| } else { |
| expect(commands.length, 1); |
| expect(commands[0].memberName, equals(#drawImageRect)); |
| } |
| }); |
| } |
| |
| testDecoration(shape: BoxShape.circle, expectClip: true); |
| testDecoration(borderRadius: const BorderRadius.all(const Radius.circular(16.0)), expectClip: true); |
| testDecoration(expectClip: false); |
| }); |
| } |