blob: ad88088b2a1733a3d482d9ea0406a1fc10fbd2d7 [file] [log] [blame]
// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
// for details. 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:io' as io;
import 'package:front_end/src/byte_store/protected_file_byte_store.dart';
import 'package:path/path.dart' as pathos;
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
main() {
defineReflectiveSuite(() {
defineReflectiveTests(ProtectedKeysTest);
defineReflectiveTests(ProtectedFileByteStoreTest);
});
}
List<int> _b(int length) {
return new List<int>.filled(length, 0);
}
@reflectiveTest
class ProtectedFileByteStoreTest {
static const PADDING = 4;
io.Directory cacheDirectory;
String cachePath;
ProtectedFileByteStore store;
int time = 0;
String get protectedKeysText {
String path =
pathos.join(cachePath, ProtectedFileByteStore.PROTECTED_FILE_NAME);
return new io.File(path).readAsStringSync();
}
void setUp() {
io.Directory systemTemp = io.Directory.systemTemp;
cacheDirectory = systemTemp.createTempSync('ProtectedFileByteStoreTest');
cachePath = cacheDirectory.absolute.path;
_createStore();
}
void tearDown() {
try {
cacheDirectory.deleteSync(recursive: true);
} on io.FileSystemException {}
}
test_flush() async {
store.put('a', _b(1));
store.put('b', _b(2));
store.put('c', _b(3));
store.put('d', _b(4));
store.updateProtectedKeys(add: ['b', 'd']);
// Add a delay to give the store time to write to disk.
await new Future.delayed(const Duration(milliseconds: 200));
// Flush, only protected 'b' and 'd' survive.
store.flush();
store.flush();
_assertCacheContent({'b': 2, 'd': 4}, ['a', 'c']);
// Remove 'b' and flush.
// Only 'd' survives.
store.updateProtectedKeys(remove: ['b']);
store.flush();
_assertCacheContent({'d': 4}, ['b']);
}
test_put() async {
store.put('a', _b(65));
store.put('b', _b(63));
store.put('c', _b(1));
// We can access all results.
expect(store.get('a'), hasLength(65));
expect(store.get('b'), hasLength(63));
expect(store.get('c'), hasLength(1));
// Add a delay to give the store time to write to disk.
await new Future.delayed(const Duration(milliseconds: 200));
_assertCacheContent({'a': 65, 'b': 63, 'c': 1}, []);
}
test_put_reservedKey() {
expect(() {
store.put(ProtectedFileByteStore.PROTECTED_FILE_NAME, <int>[]);
}, throwsArgumentError);
}
test_updateProtectedKeys_add() {
store.updateProtectedKeys(add: ['a', 'b']);
_assertKeys({'a': 0, 'b': 0});
time++;
store.updateProtectedKeys(add: ['c']);
_assertKeys({'a': 0, 'b': 0, 'c': 1});
}
test_updateProtectedKeys_add_hasSame() {
store.updateProtectedKeys(add: ['a', 'b', 'c']);
_assertKeys({'a': 0, 'b': 0, 'c': 0});
time++;
store.updateProtectedKeys(add: ['b', 'd']);
_assertKeys({'a': 0, 'b': 1, 'c': 0, 'd': 1});
}
test_updateProtectedKeys_add_removeTooOld() {
store.updateProtectedKeys(add: ['a', 'b']);
_assertKeys({'a': 0, 'b': 0});
// Move time to 10 ms, both 'a' and 'b' are still alive.
time = 10;
store.updateProtectedKeys(add: ['c']);
_assertKeys({'a': 0, 'b': 0, 'c': 10});
// Move time to 11 ms, now 'a' and 'b' are too old and removed.
time = 11;
store.updateProtectedKeys(add: ['d']);
_assertKeys({'c': 10, 'd': 11});
}
test_updateProtectedKeys_add_removeTooOld_nullDuration() {
_createStore(protectionDuration: null);
store.updateProtectedKeys(add: ['a', 'b']);
_assertKeys({'a': 0, 'b': 0});
// Move time far into the future, both 'a' and 'b' are still alive.
time = 1 << 30;
store.updateProtectedKeys(add: ['c']);
_assertKeys({'a': 0, 'b': 0, 'c': time});
}
test_updateProtectedKeys_addRemove() {
store.updateProtectedKeys(add: ['a', 'b', 'c']);
_assertKeys({'a': 0, 'b': 0, 'c': 0});
time++;
store.updateProtectedKeys(add: ['d'], remove: ['b']);
_assertKeys({'a': 0, 'c': 0, 'd': 1});
}
test_updateProtectedKeys_addRemove_same() {
store.updateProtectedKeys(add: ['a', 'b', 'c']);
_assertKeys({'a': 0, 'b': 0, 'c': 0});
time++;
store.updateProtectedKeys(add: ['b'], remove: ['b']);
_assertKeys({'a': 0, 'c': 0});
}
test_updateProtectedKeys_remove() {
store.updateProtectedKeys(add: ['a', 'b', 'c']);
_assertKeys({'a': 0, 'b': 0, 'c': 0});
time++;
store.updateProtectedKeys(remove: ['b']);
_assertKeys({'a': 0, 'c': 0});
}
void _assertCacheContent(Map<String, int> includes, List<String> excludes) {
Map<String, int> keyToLength = {};
for (var file in cacheDirectory.listSync()) {
String key = pathos.basename(file.path);
if (file is io.File) {
keyToLength[key] = file.lengthSync();
}
}
includes.forEach((expectedKey, expectedLength) {
expect(keyToLength, contains(expectedKey));
expect(keyToLength, containsPair(expectedKey, expectedLength + PADDING));
});
for (var excludedKey in excludes) {
expect(keyToLength.keys, isNot(contains(excludedKey)));
}
}
void _assertKeys(Map<String, int> expected) {
var path =
pathos.join(cachePath, ProtectedFileByteStore.PROTECTED_FILE_NAME);
var text = new io.File(path).readAsStringSync();
var keys = new ProtectedKeys.decode(text);
expect(keys.map.keys, expected.keys);
expected.forEach((key, start) {
expect(keys.map, containsPair(key, start));
});
}
void _createStore(
{Duration protectionDuration: const Duration(milliseconds: 10)}) {
store = new ProtectedFileByteStore(cachePath,
protectionDuration: protectionDuration,
cacheSizeBytes: 256,
getCurrentTime: _getTime);
}
int _getTime() => time;
}
@reflectiveTest
class ProtectedKeysTest {
test_decode() {
var keys = new ProtectedKeys({'/a/b/c': 10, '/a/d/e': 123});
String text = keys.encode();
expect(text, r'''
/a/b/c
10
/a/d/e
123''');
keys = _decode(text);
expect(keys.map['/a/b/c'], 10);
expect(keys.map['/a/d/e'], 123);
}
test_decode_empty() {
var keys = _decode('');
expect(keys.map, isEmpty);
}
test_decode_error_notEvenNumberOfLines() {
var keys = _decode('a');
expect(keys.map, isEmpty);
}
test_decode_error_startIsEmpty() {
var keys = _decode('a\n');
expect(keys.map, isEmpty);
}
test_decode_error_startIsNotInt() {
var keys = _decode('a\n1.23');
expect(keys.map, isEmpty);
}
test_decode_error_startIsNotNumber() {
var keys = _decode('a\nb');
expect(keys.map, isEmpty);
}
test_removeOlderThan() {
var keys = new ProtectedKeys({'a': 1, 'b': 2, 'c': 3});
_assertKeys(keys, {'a': 1, 'b': 2, 'c': 3});
keys.removeOlderThan(5, 7);
_assertKeys(keys, {'b': 2, 'c': 3});
}
void _assertKeys(ProtectedKeys keys, Map<String, int> expected) {
expect(keys.map.keys, expected.keys);
expected.forEach((key, start) {
expect(keys.map, containsPair(key, start));
});
}
ProtectedKeys _decode(String text) {
return new ProtectedKeys.decode(text);
}
}