decoration_test.dart 6.7 KB
Newer Older
1 2 3 4
// 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.

5
import 'dart:async';
6
import 'dart:ui' as ui show Image;
7 8

import 'package:flutter/foundation.dart';
9
import 'package:flutter/painting.dart';
10 11
import 'package:flutter/services.dart';
import 'package:quiver/testing/async.dart';
12 13

import 'package:test/test.dart';
14 15 16
import '../services/mocks_for_image_cache.dart';

class TestCanvas implements Canvas {
17 18 19 20
  TestCanvas([this.invocations]);

  final List<Invocation> invocations;

21
  @override
22 23 24
  void noSuchMethod(Invocation invocation) {
    invocations?.add(invocation);
  }
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
}

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))
    );
  }
}
54

55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
class BackgroundImageProvider extends ImageProvider<BackgroundImageProvider> {
  final Completer<ImageInfo> _completer = new Completer<ImageInfo>();

  @override
  Future<BackgroundImageProvider> obtainKey(ImageConfiguration configuration) {
    return new SynchronousFuture<BackgroundImageProvider>(this);
  }

  @override
  ImageStream resolve(ImageConfiguration configuration) {
    return super.resolve(configuration);
  }

  @override
  ImageStreamCompleter load(BackgroundImageProvider 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() { }
}

92 93
void main() {
  test("Decoration.lerp()", () {
94 95
    BoxDecoration a = const BoxDecoration(backgroundColor: const Color(0xFFFFFFFF));
    BoxDecoration b = const BoxDecoration(backgroundColor: const Color(0x00000000));
96 97 98 99 100 101 102 103 104 105

    BoxDecoration c = Decoration.lerp(a, b, 0.0);
    expect(c.backgroundColor, equals(a.backgroundColor));

    c = Decoration.lerp(a, b, 0.25);
    expect(c.backgroundColor, equals(Color.lerp(const Color(0xFFFFFFFF), const Color(0x00000000), 0.25)));

    c = Decoration.lerp(a, b, 1.0);
    expect(c.backgroundColor, equals(b.backgroundColor));
  });
106 107 108 109 110 111 112 113 114 115 116 117

  test("BoxDecorationImageListenerSync", () {
    ImageProvider imageProvider = new SynchronousTestImageProvider();
    BackgroundImage backgroundImage = new BackgroundImage(image: imageProvider);

    BoxDecoration boxDecoration = new BoxDecoration(backgroundImage: backgroundImage);
    bool onChangedCalled = false;
    BoxPainter boxPainter = boxDecoration.createBoxPainter(() {
      onChangedCalled = true;
    });

    TestCanvas canvas = new TestCanvas();
118
    ImageConfiguration imageConfiguration = const ImageConfiguration(size: Size.zero);
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
    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) {
      ImageProvider imageProvider = new AsyncTestImageProvider();
      BackgroundImage backgroundImage = new BackgroundImage(image: imageProvider);

      BoxDecoration boxDecoration = new BoxDecoration(backgroundImage: backgroundImage);
      bool onChangedCalled = false;
      BoxPainter boxPainter = boxDecoration.createBoxPainter(() {
        onChangedCalled = true;
      });

      TestCanvas canvas = new TestCanvas();
137
      ImageConfiguration imageConfiguration = const ImageConfiguration(size: Size.zero);
138 139 140 141 142 143 144 145
      boxPainter.paint(canvas, Offset.zero, imageConfiguration);

      // The onChanged callback should be invoked asynchronously.
      expect(onChangedCalled, equals(false));
      async.flushMicrotasks();
      expect(onChangedCalled, equals(true));
    });
  });
146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200

  // 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) {
        BackgroundImageProvider imageProvider = new BackgroundImageProvider();
        BackgroundImage backgroundImage = new BackgroundImage(image: imageProvider);

        BoxDecoration boxDecoration = new BoxDecoration(
          shape: shape,
          borderRadius: borderRadius,
          backgroundImage: backgroundImage,
        );

        List<Invocation> invocations = <Invocation>[];
        TestCanvas canvas = new TestCanvas(invocations);
        ImageConfiguration imageConfiguration = const ImageConfiguration(
            size: const Size(100.0, 100.0)
        );
        bool onChangedCalled = false;
        BoxPainter boxPainter = boxDecoration.createBoxPainter(() {
          onChangedCalled = true;
        });

        // _BoxDecorationPainter._paintBackgroundImage() 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.
        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: new BorderRadius.all(const Radius.circular(16.0)), expectClip: true);
    testDecoration(expectClip: false);
  });
201
}