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