diff --git a/packages/flutter/lib/src/services/asset_bundle.dart b/packages/flutter/lib/src/services/asset_bundle.dart index 3b9aa44fa1..be882122e7 100644 --- a/packages/flutter/lib/src/services/asset_bundle.dart +++ b/packages/flutter/lib/src/services/asset_bundle.dart @@ -51,10 +51,14 @@ import 'platform_messages.dart'; /// * [rootBundle] abstract class AssetBundle { /// Retrieve a binary resource from the asset bundle as a data stream. + /// + /// Throws an exception if the asset is not found. Future load(String key); /// Retrieve a string from the asset bundle. /// + /// Throws an exception if the asset is not found. + /// /// If the `cache` argument is set to false, then the data will not be /// cached, and reading the data may bypass the cache. This is useful if the /// caller is going to be doing its own caching. (It might not be cached if @@ -97,15 +101,20 @@ class NetworkAssetBundle extends AssetBundle { @override Future load(String key) async { final http.Response response = await _httpClient.get(_urlFromKey(key)); - if (response.statusCode == 200) - return null; + if (response.statusCode != 200) + throw new FlutterError('Unable to load asset: $key'); return response.bodyBytes.buffer.asByteData(); } @override Future loadString(String key, { bool cache: true }) async { final http.Response response = await _httpClient.get(_urlFromKey(key)); - return response.statusCode == 200 ? response.body : null; + if (response.statusCode != 200) + throw new FlutterError( + 'Unable to load asset: $key\n' + 'HTTP status code: ${response.statusCode}' + ); + return response.body; } /// Retrieve a string from the asset bundle, parse it with the given function, @@ -149,6 +158,8 @@ abstract class CachingAssetBundle extends AssetBundle { Future _fetchString(String key) async { final ByteData data = await load(key); + if (data == null) + throw new FlutterError('Unable to load asset: $key'); return UTF8.decode(data.buffer.asUint8List()); } @@ -202,9 +213,13 @@ abstract class CachingAssetBundle extends AssetBundle { /// An [AssetBundle] that loads resources using platform messages. class PlatformAssetBundle extends CachingAssetBundle { @override - Future load(String key) { + Future load(String key) async { final Uint8List encoded = UTF8.encoder.convert(key); - return PlatformMessages.sendBinary('flutter/assets', encoded.buffer.asByteData()); + final ByteData asset = + await PlatformMessages.sendBinary('flutter/assets', encoded.buffer.asByteData()); + if (asset == null) + throw new FlutterError('Unable to load asset: $key'); + return asset; } } diff --git a/packages/flutter/test/services/asset_bundle_test.dart b/packages/flutter/test/services/asset_bundle_test.dart new file mode 100644 index 0000000000..34821fb206 --- /dev/null +++ b/packages/flutter/test/services/asset_bundle_test.dart @@ -0,0 +1,46 @@ +// Copyright 2017 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. + +import 'dart:async'; +import 'dart:typed_data'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; + +class TestAssetBundle extends CachingAssetBundle { + Map loadCallCount = {}; + + @override + Future load(String key) async { + loadCallCount[key] = loadCallCount[key] ?? 0 + 1; + if (key == 'one') + return new ByteData(1)..setInt8(0, 49); + throw new FlutterError('key not found'); + } +} + +void main() { + test('Caching asset bundle test', () async { + final TestAssetBundle bundle = new TestAssetBundle(); + + final ByteData assetData = await bundle.load('one'); + expect(assetData.getInt8(0), equals(49)); + + expect(bundle.loadCallCount['one'], 1); + + final String assetString = await bundle.loadString('one'); + expect(assetString, equals('1')); + + expect(bundle.loadCallCount['one'], 1); + + FlutterError loadException; + try { + await bundle.loadString('foo'); + } catch (e) { + loadException = e; + } + expect(loadException, isFlutterError); + }); +}