Skip to content

Commit 0dc9691

Browse files
committed
make sure the CachedAssetReader/CachedAssetWriter dont throw synchronously
1 parent 8a7a3df commit 0dc9691

File tree

2 files changed

+100
-22
lines changed

2 files changed

+100
-22
lines changed

lib/src/asset/cache.dart

Lines changed: 50 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -57,28 +57,46 @@ class CachedAssetReader extends AssetReader {
5757
}
5858

5959
@override
60+
// Can't use async keyword here because we need to update
61+
// `_pendingHasInputChecks` synchronously.
6062
Future<bool> hasInput(AssetId id) {
61-
if (_cache.contains(id)) return new Future.value(true);
62-
63-
return _pendingHasInputChecks.putIfAbsent(id, () async {
64-
var exists = await _reader.hasInput(id);
65-
_pendingHasInputChecks.remove(id);
66-
return exists;
67-
});
63+
try {
64+
if (_cache.contains(id)) return new Future.value(true);
65+
66+
return _pendingHasInputChecks.putIfAbsent(id, () async {
67+
try {
68+
return await _reader.hasInput(id);
69+
} finally {
70+
_pendingHasInputChecks.remove(id);
71+
}
72+
});
73+
} catch (e, s) {
74+
return new Future.error(e, s);
75+
}
6876
}
6977

7078
@override
79+
// Can't use async keyword here because we need to update
80+
// `_pendingReads` synchronously.
7181
Future<String> readAsString(AssetId id, {Encoding encoding: UTF8}) {
72-
if (_cache.contains(id)) {
73-
return new Future.value(_cache.get(id).stringContents);
82+
try {
83+
if (_cache.contains(id)) {
84+
return new Future.value(_cache.get(id).stringContents);
85+
}
86+
87+
return _pendingReads.putIfAbsent(id, () async {
88+
try {
89+
var content = await _reader.readAsString(id, encoding: encoding);
90+
_cache.put(new Asset(id, content));
91+
return content;
92+
} finally {
93+
// Make sure we always remove the pending read
94+
_pendingReads.remove(id);
95+
}
96+
});
97+
} catch (e, s) {
98+
return new Future.error(e, s);
7499
}
75-
76-
return _pendingReads.putIfAbsent(id, () async {
77-
var content = await _reader.readAsString(id, encoding: encoding);
78-
_cache.put(new Asset(id, content));
79-
_pendingReads.remove(id);
80-
return content;
81-
});
82100
}
83101

84102
@override
@@ -101,14 +119,26 @@ class CachedAssetWriter extends AssetWriter {
101119
CachedAssetWriter(this._cache, this._writer);
102120

103121
@override
122+
// Can't use async keyword here because we need to update `_cache`
123+
// synchronously.
104124
Future writeAsString(Asset asset, {Encoding encoding: UTF8}) {
105-
_cache.put(asset);
106-
return _writer.writeAsString(asset, encoding: encoding);
125+
try {
126+
_cache.put(asset);
127+
return _writer.writeAsString(asset, encoding: encoding);
128+
} catch (e, s) {
129+
return new Future.error(e, s);
130+
}
107131
}
108132

109133
@override
134+
// Can't use async keyword here because we need to update `_cache`
135+
// synchronously.
110136
Future delete(AssetId id) {
111-
_cache.remove(id);
112-
return _writer.delete(id);
137+
try {
138+
_cache.remove(id);
139+
return _writer.delete(id);
140+
} catch (e, s) {
141+
return new Future.error(e, s);
142+
}
113143
}
114144
}

test/asset/cache_test.dart

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,23 @@ main() {
139139
childReaderAssets[b.id] = new DatedString(b.stringContents, time);
140140
expect(await reader.lastModified(b.id), time);
141141
});
142+
143+
group('async methods will never throw synchronously', () {
144+
var cache = new _ThrowingCache();
145+
var reader = new CachedAssetReader(cache, childReader);
146+
147+
test('hasInput', () {
148+
return reader.hasInput(a.id).then((_) {
149+
throw 'future should not succeed';
150+
}, onError: (_) {});
151+
});
152+
153+
test('readAsString', () {
154+
return reader.readAsString(a.id).then((_) {
155+
throw 'future should not succeed';
156+
}, onError: (_) {});
157+
});
158+
});
142159
});
143160

144161
group('CachedAssetWriter', () {
@@ -171,12 +188,12 @@ main() {
171188

172189
test('multiple sync writes for the same asset throws', () async {
173190
writer.writeAsString(a);
174-
expect(() => writer.writeAsString(a), throwsArgumentError);
191+
expect(writer.writeAsString(a), throwsArgumentError);
175192
});
176193

177194
test('multiple async writes for the same asset throws', () async {
178195
await writer.writeAsString(a);
179-
expect(() => writer.writeAsString(a), throwsArgumentError);
196+
expect(writer.writeAsString(a), throwsArgumentError);
180197
});
181198

182199
test('delete deletes from cache and writer', () async {
@@ -185,5 +202,36 @@ main() {
185202
expect(cache.get(a.id), isNull);
186203
expect(childWriterAssets[a.id], isNull);
187204
});
205+
206+
group('async methods will never throw synchronously', () {
207+
var cache = new _ThrowingCache();
208+
var writer = new CachedAssetWriter(cache, childWriter);
209+
210+
test('delete', () {
211+
return writer.delete(a.id).then((_) {
212+
throw 'future should not succeed';
213+
}, onError: (_) {});
214+
});
215+
216+
test('writeAsString', () {
217+
return writer.writeAsString(a).then((_) {
218+
throw 'future should not succeed';
219+
}, onError: (_) {});
220+
});
221+
});
188222
});
189223
}
224+
225+
class _ThrowingCache implements AssetCache {
226+
@override
227+
bool contains(AssetId id) => throw new UnimplementedError();
228+
229+
@override
230+
Asset remove(AssetId id) => throw new UnimplementedError();
231+
232+
@override
233+
Asset get(AssetId id) => throw new UnimplementedError();
234+
235+
@override
236+
void put(Asset id, {bool overwrite: false}) => throw new UnimplementedError();
237+
}

0 commit comments

Comments
 (0)