• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2016 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5import 'package:flutter/painting.dart';
6import '../flutter_test_alternative.dart';
7
8import '../rendering/rendering_tester.dart';
9import 'mocks_for_image_cache.dart';
10
11void main() {
12  group(ImageCache, () {
13    setUpAll(() {
14      TestRenderingFlutterBinding(); // initializes the imageCache
15    });
16
17    tearDown(() {
18      imageCache.clear();
19      imageCache.maximumSize = 1000;
20      imageCache.maximumSizeBytes = 10485760;
21    });
22
23    test('maintains cache size', () async {
24      imageCache.maximumSize = 3;
25
26      final TestImageInfo a = await extractOneFrame(const TestImageProvider(1, 1).resolve(ImageConfiguration.empty));
27      expect(a.value, equals(1));
28      final TestImageInfo b = await extractOneFrame(const TestImageProvider(1, 2).resolve(ImageConfiguration.empty));
29      expect(b.value, equals(1));
30      final TestImageInfo c = await extractOneFrame(const TestImageProvider(1, 3).resolve(ImageConfiguration.empty));
31      expect(c.value, equals(1));
32      final TestImageInfo d = await extractOneFrame(const TestImageProvider(1, 4).resolve(ImageConfiguration.empty));
33      expect(d.value, equals(1));
34      final TestImageInfo e = await extractOneFrame(const TestImageProvider(1, 5).resolve(ImageConfiguration.empty));
35      expect(e.value, equals(1));
36      final TestImageInfo f = await extractOneFrame(const TestImageProvider(1, 6).resolve(ImageConfiguration.empty));
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));
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));
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));
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));
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));
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));
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));
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));
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));
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));
89      expect(p.value, equals(16));
90
91      // cache has three entries: 3(14), 4(15), 1(16)
92    });
93
94    test('clear removes all images and resets cache size', () async {
95      const TestImage testImage = TestImage(width: 8, height: 8);
96
97      expect(imageCache.currentSize, 0);
98      expect(imageCache.currentSizeBytes, 0);
99
100      await extractOneFrame(const TestImageProvider(1, 1, image: testImage).resolve(ImageConfiguration.empty));
101      await extractOneFrame(const TestImageProvider(2, 2, image: testImage).resolve(ImageConfiguration.empty));
102
103      expect(imageCache.currentSize, 2);
104      expect(imageCache.currentSizeBytes, 256 * 2);
105
106      imageCache.clear();
107
108      expect(imageCache.currentSize, 0);
109      expect(imageCache.currentSizeBytes, 0);
110    });
111
112    test('evicts individual images', () async {
113      const TestImage testImage = TestImage(width: 8, height: 8);
114      await extractOneFrame(const TestImageProvider(1, 1, image: testImage).resolve(ImageConfiguration.empty));
115      await extractOneFrame(const TestImageProvider(2, 2, image: testImage).resolve(ImageConfiguration.empty));
116
117      expect(imageCache.currentSize, 2);
118      expect(imageCache.currentSizeBytes, 256 * 2);
119      expect(imageCache.evict(1), true);
120      expect(imageCache.currentSize, 1);
121      expect(imageCache.currentSizeBytes, 256);
122    });
123
124    test('Increases cache size if an image is loaded that is larger then the maximum size', () async {
125      const TestImage testImage = TestImage(width: 8, height: 8);
126
127      imageCache.maximumSizeBytes = 1;
128      await extractOneFrame(const TestImageProvider(1, 1, image: testImage).resolve(ImageConfiguration.empty));
129      expect(imageCache.currentSize, 1);
130      expect(imageCache.currentSizeBytes, 256);
131      expect(imageCache.maximumSizeBytes, 256 + 1000);
132    });
133
134    test('Returns null if an error is caught resolving an image', () {
135      final ErrorImageProvider errorImage = ErrorImageProvider();
136      expect(() => imageCache.putIfAbsent(errorImage, () => errorImage.load(errorImage)), throwsA(isInstanceOf<Error>()));
137      bool caughtError = false;
138      final ImageStreamCompleter result = imageCache.putIfAbsent(errorImage, () => errorImage.load(errorImage), onError: (dynamic error, StackTrace stackTrace) {
139        caughtError = true;
140      });
141      expect(result, null);
142      expect(caughtError, true);
143    });
144
145    test('already pending image is returned when it is put into the cache again', () async {
146      const TestImage testImage = TestImage(width: 8, height: 8);
147
148      final TestImageStreamCompleter completer1 = TestImageStreamCompleter();
149      final TestImageStreamCompleter completer2 = TestImageStreamCompleter();
150
151      final TestImageStreamCompleter resultingCompleter1 = imageCache.putIfAbsent(testImage, () {
152        return completer1;
153      });
154      final TestImageStreamCompleter resultingCompleter2 = imageCache.putIfAbsent(testImage, () {
155        return completer2;
156      });
157
158      expect(resultingCompleter1, completer1);
159      expect(resultingCompleter2, completer1);
160    });
161
162    test('pending image is removed when cache is cleared', () async {
163      const TestImage testImage = TestImage(width: 8, height: 8);
164
165      final TestImageStreamCompleter completer1 = TestImageStreamCompleter();
166      final TestImageStreamCompleter completer2 = TestImageStreamCompleter();
167
168      final TestImageStreamCompleter resultingCompleter1 = imageCache.putIfAbsent(testImage, () {
169        return completer1;
170      });
171
172      imageCache.clear();
173
174      final TestImageStreamCompleter resultingCompleter2 = imageCache.putIfAbsent(testImage, () {
175        return completer2;
176      });
177
178      expect(resultingCompleter1, completer1);
179      expect(resultingCompleter2, completer2);
180    });
181
182    test('pending image is removed when image is evicted', () async {
183      const TestImage testImage = TestImage(width: 8, height: 8);
184
185      final TestImageStreamCompleter completer1 = TestImageStreamCompleter();
186      final TestImageStreamCompleter completer2 = TestImageStreamCompleter();
187
188      final TestImageStreamCompleter resultingCompleter1 = imageCache.putIfAbsent(testImage, () {
189        return completer1;
190      });
191
192      imageCache.evict(testImage);
193
194      final TestImageStreamCompleter resultingCompleter2 = imageCache.putIfAbsent(testImage, () {
195        return completer2;
196      });
197
198      expect(resultingCompleter1, completer1);
199      expect(resultingCompleter2, completer2);
200    });
201
202    test('failed image can successfully be removed from the cache\'s pending images', () async {
203      const TestImage testImage = TestImage(width: 8, height: 8);
204
205      const FailingTestImageProvider(1, 1, image: testImage)
206          .resolve(ImageConfiguration.empty)
207          .addListener(ImageStreamListener(
208            (ImageInfo image, bool synchronousCall) { },
209            onError: (dynamic exception, StackTrace stackTrace) {
210              final bool evicationResult = imageCache.evict(1);
211              expect(evicationResult, isTrue);
212            },
213          ));
214    });
215  });
216}
217
218