1// Copyright 2014 The Flutter 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 'dart:convert';
6
7import 'package:flutter/foundation.dart';
8import 'package:flutter/painting.dart';
9import 'package:flutter/services.dart';
10import 'package:flutter_test/flutter_test.dart';
11
12class TestAssetBundle extends CachingAssetBundle {
13 Map<String, int> loadCallCount = <String, int>{};
14
15 @override
16 Future<ByteData> load(String key) async {
17 loadCallCount[key] = (loadCallCount[key] ?? 0) + 1;
18 if (key == 'AssetManifest.json') {
19 return ByteData.sublistView(utf8.encode('{"one": ["one"]}'));
20 }
21
22 if (key == 'AssetManifest.bin') {
23 return const StandardMessageCodec().encodeMessage(<String, Object>{'one': <Object>[]})!;
24 }
25
26 if (key == 'AssetManifest.bin.json') {
27 // Encode the manifest data that will be used by the app
28 final ByteData data =
29 const StandardMessageCodec().encodeMessage(<String, Object>{'one': <Object>[]})!;
30 // Simulate the behavior of NetworkAssetBundle.load here, for web tests
31 return ByteData.sublistView(
32 utf8.encode(
33 json.encode(
34 base64.encode(
35 // Encode only the actual bytes of the buffer, and no more...
36 data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes),
37 ),
38 ),
39 ),
40 );
41 }
42
43 if (key == 'counter') {
44 return ByteData.sublistView(utf8.encode(loadCallCount[key]!.toString()));
45 }
46
47 if (key == 'one') {
48 return ByteData(1)..setInt8(0, 49);
49 }
50
51 throw FlutterError('key not found');
52 }
53}
54
55void main() {
56 TestWidgetsFlutterBinding.ensureInitialized();
57
58 test('Caching asset bundle test', () async {
59 final TestAssetBundle bundle = TestAssetBundle();
60
61 final ByteData assetData = await bundle.load('one');
62 expect(assetData.getInt8(0), equals(49));
63
64 expect(bundle.loadCallCount['one'], 1);
65
66 final String assetString = await bundle.loadString('one');
67 expect(assetString, equals('1'));
68
69 expect(bundle.loadCallCount['one'], 2);
70
71 late Object loadException;
72 try {
73 await bundle.loadString('foo');
74 } catch (e) {
75 loadException = e;
76 }
77 expect(loadException, isFlutterError);
78 });
79
80 group('CachingAssetBundle caching behavior', () {
81 test(
82 'caches results for loadString, loadStructuredData, and loadBinaryStructuredData',
83 () async {
84 final TestAssetBundle bundle = TestAssetBundle();
85
86 final String firstLoadStringResult = await bundle.loadString('counter');
87 final String secondLoadStringResult = await bundle.loadString('counter');
88 expect(firstLoadStringResult, '1');
89 expect(secondLoadStringResult, '1');
90
91 final String firstLoadStructuredDataResult = await bundle.loadStructuredData(
92 'AssetManifest.json',
93 (String value) => Future<String>.value('one'),
94 );
95 final String secondLoadStructuredDataResult = await bundle.loadStructuredData(
96 'AssetManifest.json',
97 (String value) => Future<String>.value('two'),
98 );
99 expect(firstLoadStructuredDataResult, 'one');
100 expect(secondLoadStructuredDataResult, 'one');
101
102 final String firstLoadStructuredBinaryDataResult = await bundle.loadStructuredBinaryData(
103 'AssetManifest.bin',
104 (ByteData value) => Future<String>.value('one'),
105 );
106 final String secondLoadStructuredBinaryDataResult = await bundle.loadStructuredBinaryData(
107 'AssetManifest.bin',
108 (ByteData value) => Future<String>.value('two'),
109 );
110 expect(firstLoadStructuredBinaryDataResult, 'one');
111 expect(secondLoadStructuredBinaryDataResult, 'one');
112 },
113 );
114
115 test("clear clears all cached values'", () async {
116 final TestAssetBundle bundle = TestAssetBundle();
117
118 await bundle.loadString('counter');
119 bundle.clear();
120 final String secondLoadStringResult = await bundle.loadString('counter');
121 expect(secondLoadStringResult, '2');
122
123 await bundle.loadStructuredData(
124 'AssetManifest.json',
125 (String value) => Future<String>.value('one'),
126 );
127 bundle.clear();
128 final String secondLoadStructuredDataResult = await bundle.loadStructuredData(
129 'AssetManifest.json',
130 (String value) => Future<String>.value('two'),
131 );
132 expect(secondLoadStructuredDataResult, 'two');
133
134 await bundle.loadStructuredBinaryData(
135 'AssetManifest.bin',
136 (ByteData value) => Future<String>.value('one'),
137 );
138 bundle.clear();
139 final String secondLoadStructuredBinaryDataResult = await bundle.loadStructuredBinaryData(
140 'AssetManifest.bin',
141 (ByteData value) => Future<String>.value('two'),
142 );
143 expect(secondLoadStructuredBinaryDataResult, 'two');
144 });
145
146 test('evict evicts a particular key from the cache', () async {
147 final TestAssetBundle bundle = TestAssetBundle();
148
149 await bundle.loadString('counter');
150 bundle.evict('counter');
151 final String secondLoadStringResult = await bundle.loadString('counter');
152 expect(secondLoadStringResult, '2');
153
154 await bundle.loadStructuredData(
155 'AssetManifest.json',
156 (String value) => Future<String>.value('one'),
157 );
158 bundle.evict('AssetManifest.json');
159 final String secondLoadStructuredDataResult = await bundle.loadStructuredData(
160 'AssetManifest.json',
161 (String value) => Future<String>.value('two'),
162 );
163 expect(secondLoadStructuredDataResult, 'two');
164
165 await bundle.loadStructuredBinaryData(
166 'AssetManifest.bin',
167 (ByteData value) => Future<String>.value('one'),
168 );
169 bundle.evict('AssetManifest.bin');
170 final String secondLoadStructuredBinaryDataResult = await bundle.loadStructuredBinaryData(
171 'AssetManifest.bin',
172 (ByteData value) => Future<String>.value('two'),
173 );
174 expect(secondLoadStructuredBinaryDataResult, 'two');
175 });
176
177 test(
178 'for a given key, subsequent loadStructuredData calls are synchronous after the first call resolves',
179 () async {
180 final TestAssetBundle bundle = TestAssetBundle();
181 await bundle.loadStructuredData('one', (String data) => SynchronousFuture<int>(1));
182 final Future<int> data = bundle.loadStructuredData(
183 'one',
184 (String data) => SynchronousFuture<int>(2),
185 );
186 expect(data, isA<SynchronousFuture<int>>());
187 expect(await data, 1);
188 },
189 );
190
191 test(
192 'for a given key, subsequent loadStructuredBinaryData calls are synchronous after the first call resolves',
193 () async {
194 final TestAssetBundle bundle = TestAssetBundle();
195 await bundle.loadStructuredBinaryData('one', (ByteData data) => 1);
196 final Future<int> data = bundle.loadStructuredBinaryData('one', (ByteData data) => 2);
197 expect(data, isA<SynchronousFuture<int>>());
198 expect(await data, 1);
199 },
200 );
201
202 testWidgets('loadStructuredData handles exceptions correctly', (WidgetTester tester) async {
203 final TestAssetBundle bundle = TestAssetBundle();
204 try {
205 await bundle.loadStructuredData(
206 'AssetManifest.json',
207 (String value) => Future<String>.error('what do they say?'),
208 );
209 fail('expected exception did not happen');
210 } catch (e) {
211 expect(e.toString(), contains('what do they say?'));
212 }
213 });
214
215 testWidgets('loadStructuredBinaryData handles exceptions correctly', (
216 WidgetTester tester,
217 ) async {
218 final TestAssetBundle bundle = TestAssetBundle();
219 try {
220 await bundle.loadStructuredBinaryData(
221 'AssetManifest.bin',
222 (ByteData value) => Future<String>.error('buy more crystals'),
223 );
224 fail('expected exception did not happen');
225 } catch (e) {
226 expect(e.toString(), contains('buy more crystals'));
227 }
228 });
229 });
230
231 test('AssetImage.obtainKey succeeds with ImageConfiguration.empty', () async {
232 // This is a regression test for https://github.com/flutter/flutter/issues/12392
233 final AssetImage assetImage = AssetImage('one', bundle: TestAssetBundle());
234 final AssetBundleImageKey key = await assetImage.obtainKey(ImageConfiguration.empty);
235 expect(key.name, 'one');
236 expect(key.scale, 1.0);
237 });
238
239 test('NetworkAssetBundle control test', () async {
240 final Uri uri = Uri.http('example.org', '/path');
241 final NetworkAssetBundle bundle = NetworkAssetBundle(uri);
242 late FlutterError error;
243 try {
244 await bundle.load('key');
245 } on FlutterError catch (e) {
246 error = e;
247 }
248 expect(error, isNotNull);
249 expect(error.diagnostics.length, 2);
250 expect(error.diagnostics.last, isA<IntProperty>());
251 expect(
252 error.toStringDeep(),
253 'FlutterError\n'
254 ' Unable to load asset: "key".\n'
255 ' HTTP status code: 400\n',
256 );
257 }, skip: isBrowser); // https://github.com/flutter/flutter/issues/39998
258
259 test('toString works as intended', () {
260 final Uri uri = Uri.http('example.org', '/path');
261 final NetworkAssetBundle bundle = NetworkAssetBundle(uri);
262
263 expect(bundle.toString(), 'NetworkAssetBundle#${shortHash(bundle)}($uri)');
264 }, skip: isBrowser); // https://github.com/flutter/flutter/issues/39998
265
266 test('Throws expected exceptions when loading not exists asset', () async {
267 late final FlutterError error;
268 try {
269 await rootBundle.load('not-exists');
270 } on FlutterError catch (e) {
271 error = e;
272 }
273 expect(
274 error.message,
275 equals(
276 'Unable to load asset: "not-exists".\n'
277 'The asset does not exist or has empty data.',
278 ),
279 );
280 }, skip: isBrowser); // https://github.com/flutter/flutter/issues/56314
281
282 test('loadStructuredBinaryData correctly loads ByteData', () async {
283 final TestAssetBundle bundle = TestAssetBundle();
284 final Map<Object?, Object?> assetManifest = await bundle.loadStructuredBinaryData(
285 'AssetManifest.bin',
286 (ByteData data) => const StandardMessageCodec().decodeMessage(data) as Map<Object?, Object?>,
287 );
288 expect(assetManifest.keys.toList(), equals(<String>['one']));
289 expect(assetManifest['one'], <Object>[]);
290 });
291}
292

Provided by KDAB

Privacy Policy
Learn more about Flutter for embedded and desktop on industrialflutter.com