Skip to content

add lastModified to AssetReader #51

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Feb 18, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions lib/src/asset/cache.dart
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ class CachedAssetReader extends AssetReader {
@override
Stream<AssetId> listAssetIds(Iterable<InputSet> inputSets) =>
_reader.listAssetIds(inputSets);

@override
Future<DateTime> lastModified(AssetId id) => _reader.lastModified(id);
}

/// An [AssetWriter] which takes both an [AssetCache] and an [AssetWriter]. It
Expand Down
8 changes: 8 additions & 0 deletions lib/src/asset/file_based.dart
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,14 @@ class FileBasedAssetReader implements AssetReader {
}
}
}

Future<DateTime> lastModified(AssetId id) async {
var file = await _fileFor(id, packageGraph);
if (!await file.exists()) {
throw new AssetNotFoundException(id);
}
return file.lastModified();
}
}

/// Creates an [AssetId] for [file], which is a part of [packageNode].
Expand Down
7 changes: 7 additions & 0 deletions lib/src/asset/reader.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,17 @@ import 'dart:convert';
import '../generate/input_set.dart';
import 'id.dart';

/// Abstract interface for reading assets.
abstract class AssetReader {
/// Asynchronously reads [id], and returns it as a [String].
Future<String> readAsString(AssetId id, {Encoding encoding: UTF8});

/// Asynchronously checks if [id] exists.
Future<bool> hasInput(AssetId id);

/// Gets a [Stream<AssetId>] of all assets available matching [inputSets].
Stream<AssetId> listAssetIds(Iterable<InputSet> inputSets);

/// Asynchonously gets the last modified [DateTime] of [id].
Future<DateTime> lastModified(AssetId id);
}
5 changes: 4 additions & 1 deletion lib/src/transformer/transformer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,13 @@ class _TransformAssetReader implements AssetReader {
Future<String> readAsString(build.AssetId id, {Encoding encoding: UTF8}) =>
transform.readInputAsString(toBarbackAssetId(id), encoding: encoding);

/// No way to implement this, but luckily its not necessary.
@override
Stream<build.AssetId> listAssetIds(_) => throw new UnimplementedError();

/// No way to implement this, but luckily its not necessary.
Stream<build.AssetId> listAssetIds(_) => throw new UnimplementedError();
@override
Future<DateTime> lastModified(_) => throw new UnimplementedError();
}

/// Very simple [AssetWriter] which uses a [Transform].
Expand Down
28 changes: 18 additions & 10 deletions test/asset/cache_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ main() {
group('CachedAssetReader', () {
CachedAssetReader reader;
InMemoryAssetReader childReader;
Map<AssetId, String> childReaderAssets;
Map<AssetId, DatedString> childReaderAssets;

setUp(() {
cache = new AssetCache();
Expand All @@ -78,18 +78,18 @@ main() {
expect(a.stringContents, isNot(otherA.stringContents));
expect(a.id, otherA.id);

childReaderAssets[otherA.id] = otherA.stringContents;
childReaderAssets[otherA.id] = new DatedString(otherA.stringContents);
cache.put(a);
expect(await reader.readAsString(a.id), a.stringContents);
});

test('falls back on the childReader', () async {
childReaderAssets[a.id] = a.stringContents;
childReaderAssets[a.id] = new DatedString(a.stringContents);
expect(await reader.readAsString(a.id), a.stringContents);
});

test('reads add values to the cache', () async {
childReaderAssets[a.id] = a.stringContents;
childReaderAssets[a.id] = new DatedString(a.stringContents);
await reader.readAsString(a.id);
expect(cache.get(a.id), equalsAsset(a));
childReaderAssets.remove(a.id);
Expand All @@ -100,7 +100,7 @@ main() {
expect(await reader.hasInput(a.id), isFalse);
cache.put(a);
expect(await reader.hasInput(a.id), isTrue);
childReaderAssets[a.id] = a.stringContents;
childReaderAssets[a.id] = new DatedString(a.stringContents);
expect(await reader.hasInput(a.id), isTrue);
cache.remove(a.id);
expect(await reader.hasInput(a.id), isTrue);
Expand All @@ -109,7 +109,7 @@ main() {
});

test('Multiple readAsString calls wait on the same future', () async {
childReaderAssets[a.id] = a.stringContents;
childReaderAssets[a.id] = new DatedString(a.stringContents);
var futures = [];
futures.add(reader.readAsString(a.id));
futures.add(reader.readAsString(a.id));
Expand All @@ -121,7 +121,7 @@ main() {
});

test('Multiple hasInput calls return the same future', () async {
childReaderAssets[a.id] = a.stringContents;
childReaderAssets[a.id] = new DatedString(a.stringContents);
var futures = [];
futures.add(reader.hasInput(a.id));
futures.add(reader.hasInput(a.id));
Expand All @@ -131,12 +131,20 @@ main() {
// Subsequent calls should not return the same future.
expect(reader.hasInput(a.id), isNot(futures[0]));
});

test('lastModified uses the reader', () async {
expect(reader.lastModified(a.id), throwsA(assetNotFoundException));

var time = new DateTime.now();
childReaderAssets[b.id] = new DatedString(b.stringContents, time);
expect(await reader.lastModified(b.id), time);
});
});

group('CachedAssetWriter', () {
CachedAssetWriter writer;
InMemoryAssetWriter childWriter;
Map<AssetId, String> childWriterAssets;
Map<AssetId, DatedString> childWriterAssets;

setUp(() {
cache = new AssetCache();
Expand All @@ -154,11 +162,11 @@ main() {
test('writes to the cache and the child writer', () async {
await writer.writeAsString(a);
expect(cache.get(a.id), a);
expect(childWriterAssets[a.id], a.stringContents);
expect(childWriterAssets[a.id].value, a.stringContents);

await writer.writeAsString(b);
expect(cache.get(b.id), b);
expect(childWriterAssets[b.id], b.stringContents);
expect(childWriterAssets[b.id].value, b.stringContents);
});

test('multiple sync writes for the same asset throws', () async {
Expand Down
33 changes: 23 additions & 10 deletions test/asset/file_based_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import '../common/common.dart';
final packageGraph = new PackageGraph.forPath('test/fixtures/basic_pkg');

main() {

group('FileBasedAssetReader', () {
final reader = new FileBasedAssetReader(packageGraph, ignoredDirs: ['pkg']);

Expand Down Expand Up @@ -62,22 +61,36 @@ main() {
new InputSet('basic_pkg', filePatterns: ['{lib,web}/**']),
new InputSet('a', filePatterns: ['lib/**']),
];
expect(await reader.listAssetIds(inputSets).toList(), unorderedEquals([
makeAssetId('basic_pkg|lib/hello.txt'),
makeAssetId('basic_pkg|web/hello.txt'),
makeAssetId('a|lib/a.txt'),
]));
expect(
await reader.listAssetIds(inputSets).toList(),
unorderedEquals([
makeAssetId('basic_pkg|lib/hello.txt'),
makeAssetId('basic_pkg|web/hello.txt'),
makeAssetId('a|lib/a.txt'),
]));
});

test('can list files based on InputSets with globs', () async {
var inputSets = [
new InputSet('basic_pkg', filePatterns: ['web/*.txt']),
new InputSet('a', filePatterns: ['lib/*']),
];
expect(await reader.listAssetIds(inputSets).toList(), unorderedEquals([
makeAssetId('basic_pkg|web/hello.txt'),
makeAssetId('a|lib/a.txt'),
]));
expect(
await reader.listAssetIds(inputSets).toList(),
unorderedEquals([
makeAssetId('basic_pkg|web/hello.txt'),
makeAssetId('a|lib/a.txt'),
]));
});

test('can get lastModified time for files', () async {
expect(await reader.lastModified(makeAssetId('basic_pkg|hello.txt')),
new isInstanceOf<DateTime>());
});

test('lastModified throws AssetNotFoundException appropriately', () async {
expect(reader.lastModified(makeAssetId('basic_pkg|foo.txt')),
throwsA(assetNotFoundException));
});
});

Expand Down
2 changes: 1 addition & 1 deletion test/builder/build_step_impl_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ main() {
// One output.
expect(buildStep.outputs[0].id, outputId);
expect(buildStep.outputs[0].stringContents, 'AB');
expect(writer.assets[outputId], 'AB');
expect(writer.assets[outputId].value, 'AB');
});

group('resolve', () {
Expand Down
2 changes: 1 addition & 1 deletion test/common/assets.dart
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ Map<AssetId, Asset> makeAssets(Map<String, String> assetsMap) {

void addAssets(Iterable<Asset> assets, InMemoryAssetWriter writer) {
for (var asset in assets) {
writer.assets[asset.id] = asset.stringContents;
writer.assets[asset.id] = new DatedString(asset.stringContents);
}
}

Expand Down
5 changes: 3 additions & 2 deletions test/common/common.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import 'package:build/build.dart';

import 'assets.dart';
import 'matchers.dart';
import 'in_memory_writer.dart';

export 'assets.dart';
export 'copy_builder.dart';
Expand All @@ -24,7 +25,7 @@ Future wait(int milliseconds) =>
new Future.delayed(new Duration(milliseconds: milliseconds));

void checkOutputs(Map<String, String> outputs, BuildResult result,
[Map<AssetId, String> actualAssets]) {
[Map<AssetId, DatedString> actualAssets]) {
if (outputs != null) {
var remainingOutputIds =
new List.from(result.outputs.map((asset) => asset.id));
Expand All @@ -35,7 +36,7 @@ void checkOutputs(Map<String, String> outputs, BuildResult result,
/// Check that the writer wrote the assets
if (actualAssets != null) {
expect(actualAssets, contains(asset.id));
expect(actualAssets[asset.id], asset.stringContents);
expect(actualAssets[asset.id].value, asset.stringContents);
}

/// Check that the assets exist in [result.outputs].
Expand Down
12 changes: 10 additions & 2 deletions test/common/in_memory_reader.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@ import 'dart:convert';

import 'package:build/build.dart';

import 'in_memory_writer.dart';

class InMemoryAssetReader implements AssetReader {
final Map<AssetId, String> assets;
final Map<AssetId, DatedString> assets;

InMemoryAssetReader(this.assets);

Expand All @@ -19,7 +21,7 @@ class InMemoryAssetReader implements AssetReader {
@override
Future<String> readAsString(AssetId id, {Encoding encoding: UTF8}) async {
if (!await hasInput(id)) throw new AssetNotFoundException(id);
return assets[id];
return assets[id].value;
}

@override
Expand All @@ -32,4 +34,10 @@ class InMemoryAssetReader implements AssetReader {
if (matches) yield id;
}
}

@override
Future<DateTime> lastModified(AssetId id) async {
if (!await hasInput(id)) throw new AssetNotFoundException(id);
return assets[id].date;
}
}
14 changes: 11 additions & 3 deletions test/common/in_memory_writer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,23 @@ import 'dart:convert';
import 'package:build/build.dart';

class InMemoryAssetWriter implements AssetWriter {
final Map<AssetId, String> assets = {};
final Map<AssetId, DatedString> assets = {};

InMemoryAssetWriter();

Future writeAsString(Asset asset, {Encoding encoding: UTF8}) async {
assets[asset.id] = asset.stringContents;
Future writeAsString(Asset asset,
{Encoding encoding: UTF8, DateTime lastModified}) async {
assets[asset.id] = new DatedString(asset.stringContents, lastModified);
}

Future delete(AssetId id) async {
assets.remove(id);
}
}

class DatedString {
final String value;
final DateTime date;

DatedString(this.value, [DateTime date]) : date = date ?? new DateTime.now();
}
4 changes: 4 additions & 0 deletions test/common/stub_reader.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,9 @@ class StubAssetReader implements AssetReader {
Future<String> readAsString(AssetId id, {Encoding encoding: UTF8}) =>
new Future.value(null);

@override
Stream<AssetId> listAssetIds(_) async* {}

@override
Future<DateTime> lastModified(AssetId id) => new Future.value(null);
}
2 changes: 1 addition & 1 deletion test/generate/build_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ main() {
var graphId = makeAssetId('a|.build/asset_graph.json');
expect(writer.assets, contains(graphId));
var cachedGraph =
new AssetGraph.deserialize(JSON.decode(writer.assets[graphId]));
new AssetGraph.deserialize(JSON.decode(writer.assets[graphId].value));

var expectedGraph = new AssetGraph();
var aCopyNode = makeAssetNode('a|web/a.txt.copy');
Expand Down
16 changes: 8 additions & 8 deletions test/generate/watch_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ main() {
result = await nextResult(results);
checkOutputs({'a|web/b.txt.copy': 'b',}, result, writer.assets);
// Previous outputs should still exist.
expect(writer.assets[makeAssetId('a|web/a.txt.copy')], 'a');
expect(writer.assets[makeAssetId('a|web/a.txt.copy')].value, 'a');
});

test('rebuilds on deleted files', () async {
Expand Down Expand Up @@ -98,7 +98,7 @@ main() {
// The old output file should no longer exist either.
expect(writer.assets[makeAssetId('a|web/a.txt.copy')], isNull);
// Previous outputs should still exist.
expect(writer.assets[makeAssetId('a|web/b.txt.copy')], 'b');
expect(writer.assets[makeAssetId('a|web/b.txt.copy')].value, 'b');
});

test('rebuilds properly update asset_graph.json', () async {
Expand Down Expand Up @@ -126,8 +126,8 @@ main() {
result = await nextResult(results);
checkOutputs({'a|web/c.txt.copy': 'c'}, result, writer.assets);

var cachedGraph = new AssetGraph.deserialize(JSON
.decode(writer.assets[makeAssetId('a|.build/asset_graph.json')]));
var cachedGraph = new AssetGraph.deserialize(JSON.decode(
writer.assets[makeAssetId('a|.build/asset_graph.json')].value));

var expectedGraph = new AssetGraph();
var bCopyNode = makeAssetNode('a|web/b.txt.copy');
Expand Down Expand Up @@ -201,8 +201,8 @@ main() {
checkOutputs({'a|web/b.txt.copy': 'b', 'a|web/b.txt.copy.copy': 'b'},
result, writer.assets);
// Previous outputs should still exist.
expect(writer.assets[makeAssetId('a|web/a.txt.copy')], 'a');
expect(writer.assets[makeAssetId('a|web/a.txt.copy.copy')], 'a');
expect(writer.assets[makeAssetId('a|web/a.txt.copy')].value, 'a');
expect(writer.assets[makeAssetId('a|web/a.txt.copy.copy')].value, 'a');
});

test('deletes propagate through all phases', () async {
Expand Down Expand Up @@ -243,8 +243,8 @@ main() {
expect(writer.assets[makeAssetId('a|web/a.txt.copy')], isNull);
expect(writer.assets[makeAssetId('a|web/a.txt.copy.copy')], isNull);
// Other outputs should still exist.
expect(writer.assets[makeAssetId('a|web/b.txt.copy')], 'b');
expect(writer.assets[makeAssetId('a|web/b.txt.copy.copy')], 'b');
expect(writer.assets[makeAssetId('a|web/b.txt.copy')].value, 'b');
expect(writer.assets[makeAssetId('a|web/b.txt.copy.copy')].value, 'b');
});
});

Expand Down