image_cache_test.dart 8.91 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
2 3 4
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5
import 'package:flutter/painting.dart';
6
import '../flutter_test_alternative.dart';
7

8
import '../rendering/rendering_tester.dart';
9 10 11
import 'mocks_for_image_cache.dart';

void main() {
12
  group(ImageCache, () {
13 14 15 16
    setUpAll(() {
      TestRenderingFlutterBinding(); // initializes the imageCache
    });

17 18 19 20 21
    tearDown(() {
      imageCache.clear();
      imageCache.maximumSize = 1000;
      imageCache.maximumSizeBytes = 10485760;
    });
22

23 24
    test('maintains cache size', () async {
      imageCache.maximumSize = 3;
25

26
      final TestImageInfo a = await extractOneFrame(const TestImageProvider(1, 1).resolve(ImageConfiguration.empty)) as TestImageInfo;
27
      expect(a.value, equals(1));
28
      final TestImageInfo b = await extractOneFrame(const TestImageProvider(1, 2).resolve(ImageConfiguration.empty)) as TestImageInfo;
29
      expect(b.value, equals(1));
30
      final TestImageInfo c = await extractOneFrame(const TestImageProvider(1, 3).resolve(ImageConfiguration.empty)) as TestImageInfo;
31
      expect(c.value, equals(1));
32
      final TestImageInfo d = await extractOneFrame(const TestImageProvider(1, 4).resolve(ImageConfiguration.empty)) as TestImageInfo;
33
      expect(d.value, equals(1));
34
      final TestImageInfo e = await extractOneFrame(const TestImageProvider(1, 5).resolve(ImageConfiguration.empty)) as TestImageInfo;
35
      expect(e.value, equals(1));
36
      final TestImageInfo f = await extractOneFrame(const TestImageProvider(1, 6).resolve(ImageConfiguration.empty)) as TestImageInfo;
37
      expect(f.value, equals(1));
38

39
      expect(f, equals(a));
40

41
      // cache still only has one entry in it: 1(1)
42

43
      final TestImageInfo g = await extractOneFrame(const TestImageProvider(2, 7).resolve(ImageConfiguration.empty)) as TestImageInfo;
44
      expect(g.value, equals(7));
45

46
      // cache has two entries in it: 1(1), 2(7)
47

48
      final TestImageInfo h = await extractOneFrame(const TestImageProvider(1, 8).resolve(ImageConfiguration.empty)) as TestImageInfo;
49
      expect(h.value, equals(1));
50

51
      // cache still has two entries in it: 2(7), 1(1)
52

53
      final TestImageInfo i = await extractOneFrame(const TestImageProvider(3, 9).resolve(ImageConfiguration.empty)) as TestImageInfo;
54
      expect(i.value, equals(9));
55

56
      // cache has three entries in it: 2(7), 1(1), 3(9)
57

58
      final TestImageInfo j = await extractOneFrame(const TestImageProvider(1, 10).resolve(ImageConfiguration.empty)) as TestImageInfo;
59
      expect(j.value, equals(1));
60

61
      // cache still has three entries in it: 2(7), 3(9), 1(1)
62

63
      final TestImageInfo k = await extractOneFrame(const TestImageProvider(4, 11).resolve(ImageConfiguration.empty)) as TestImageInfo;
64
      expect(k.value, equals(11));
65

66
      // cache has three entries: 3(9), 1(1), 4(11)
67

68
      final TestImageInfo l = await extractOneFrame(const TestImageProvider(1, 12).resolve(ImageConfiguration.empty)) as TestImageInfo;
69
      expect(l.value, equals(1));
70

71
      // cache has three entries: 3(9), 4(11), 1(1)
72

73
      final TestImageInfo m = await extractOneFrame(const TestImageProvider(2, 13).resolve(ImageConfiguration.empty)) as TestImageInfo;
74
      expect(m.value, equals(13));
75

76
      // cache has three entries: 4(11), 1(1), 2(13)
77

78
      final TestImageInfo n = await extractOneFrame(const TestImageProvider(3, 14).resolve(ImageConfiguration.empty)) as TestImageInfo;
79
      expect(n.value, equals(14));
80

81
      // cache has three entries: 1(1), 2(13), 3(14)
82

83
      final TestImageInfo o = await extractOneFrame(const TestImageProvider(4, 15).resolve(ImageConfiguration.empty)) as TestImageInfo;
84
      expect(o.value, equals(15));
85

86
      // cache has three entries: 2(13), 3(14), 4(15)
87

88
      final TestImageInfo p = await extractOneFrame(const TestImageProvider(1, 16).resolve(ImageConfiguration.empty)) as TestImageInfo;
89
      expect(p.value, equals(16));
90

91 92
      // cache has three entries: 3(14), 4(15), 1(16)
    });
93

94
    test('clear removes all images and resets cache size', () async {
95
      const TestImage testImage = TestImage(width: 8, height: 8);
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112

      expect(imageCache.currentSize, 0);
      expect(imageCache.currentSizeBytes, 0);

      await extractOneFrame(const TestImageProvider(1, 1, image: testImage).resolve(ImageConfiguration.empty));
      await extractOneFrame(const TestImageProvider(2, 2, image: testImage).resolve(ImageConfiguration.empty));

      expect(imageCache.currentSize, 2);
      expect(imageCache.currentSizeBytes, 256 * 2);

      imageCache.clear();

      expect(imageCache.currentSize, 0);
      expect(imageCache.currentSizeBytes, 0);
    });

    test('evicts individual images', () async {
113
      const TestImage testImage = TestImage(width: 8, height: 8);
114 115 116 117 118 119 120 121 122
      await extractOneFrame(const TestImageProvider(1, 1, image: testImage).resolve(ImageConfiguration.empty));
      await extractOneFrame(const TestImageProvider(2, 2, image: testImage).resolve(ImageConfiguration.empty));

      expect(imageCache.currentSize, 2);
      expect(imageCache.currentSizeBytes, 256 * 2);
      expect(imageCache.evict(1), true);
      expect(imageCache.currentSize, 1);
      expect(imageCache.currentSizeBytes, 256);
    });
123

124
    test('Do not cache large images', () async {
125
      const TestImage testImage = TestImage(width: 8, height: 8);
126 127 128

      imageCache.maximumSizeBytes = 1;
      await extractOneFrame(const TestImageProvider(1, 1, image: testImage).resolve(ImageConfiguration.empty));
129 130 131
      expect(imageCache.currentSize, 0);
      expect(imageCache.currentSizeBytes, 0);
      expect(imageCache.maximumSizeBytes, 1);
132
    });
133 134 135

    test('Returns null if an error is caught resolving an image', () {
      final ErrorImageProvider errorImage = ErrorImageProvider();
136
      expect(() => imageCache.putIfAbsent(errorImage, () => errorImage.load(errorImage, null)), throwsA(isInstanceOf<Error>()));
137
      bool caughtError = false;
138
      final ImageStreamCompleter result = imageCache.putIfAbsent(errorImage, () => errorImage.load(errorImage, null), onError: (dynamic error, StackTrace stackTrace) {
139 140 141 142 143
        caughtError = true;
      });
      expect(result, null);
      expect(caughtError, true);
    });
144 145 146 147 148 149 150 151 152

    test('already pending image is returned when it is put into the cache again', () async {
      const TestImage testImage = TestImage(width: 8, height: 8);

      final TestImageStreamCompleter completer1 = TestImageStreamCompleter();
      final TestImageStreamCompleter completer2 = TestImageStreamCompleter();

      final TestImageStreamCompleter resultingCompleter1 = imageCache.putIfAbsent(testImage, () {
        return completer1;
153
      }) as TestImageStreamCompleter;
154 155
      final TestImageStreamCompleter resultingCompleter2 = imageCache.putIfAbsent(testImage, () {
        return completer2;
156
      }) as TestImageStreamCompleter;
157 158 159 160 161 162 163 164 165 166 167 168 169

      expect(resultingCompleter1, completer1);
      expect(resultingCompleter2, completer1);
    });

    test('pending image is removed when cache is cleared', () async {
      const TestImage testImage = TestImage(width: 8, height: 8);

      final TestImageStreamCompleter completer1 = TestImageStreamCompleter();
      final TestImageStreamCompleter completer2 = TestImageStreamCompleter();

      final TestImageStreamCompleter resultingCompleter1 = imageCache.putIfAbsent(testImage, () {
        return completer1;
170
      }) as TestImageStreamCompleter;
171 172 173 174 175

      imageCache.clear();

      final TestImageStreamCompleter resultingCompleter2 = imageCache.putIfAbsent(testImage, () {
        return completer2;
176
      }) as TestImageStreamCompleter;
177 178 179 180 181 182 183 184 185 186 187 188 189

      expect(resultingCompleter1, completer1);
      expect(resultingCompleter2, completer2);
    });

    test('pending image is removed when image is evicted', () async {
      const TestImage testImage = TestImage(width: 8, height: 8);

      final TestImageStreamCompleter completer1 = TestImageStreamCompleter();
      final TestImageStreamCompleter completer2 = TestImageStreamCompleter();

      final TestImageStreamCompleter resultingCompleter1 = imageCache.putIfAbsent(testImage, () {
        return completer1;
190
      }) as TestImageStreamCompleter;
191 192 193 194 195

      imageCache.evict(testImage);

      final TestImageStreamCompleter resultingCompleter2 = imageCache.putIfAbsent(testImage, () {
        return completer2;
196
      }) as TestImageStreamCompleter;
197 198 199 200 201 202 203 204

      expect(resultingCompleter1, completer1);
      expect(resultingCompleter2, completer2);
    });

    test('failed image can successfully be removed from the cache\'s pending images', () async {
      const TestImage testImage = TestImage(width: 8, height: 8);

205 206 207 208 209 210 211 212 213
      const FailingTestImageProvider(1, 1, image: testImage)
          .resolve(ImageConfiguration.empty)
          .addListener(ImageStreamListener(
            (ImageInfo image, bool synchronousCall) { },
            onError: (dynamic exception, StackTrace stackTrace) {
              final bool evicationResult = imageCache.evict(1);
              expect(evicationResult, isTrue);
            },
          ));
214
    });
215 216
  });
}