blob: 60ff18874b7ef3a993c5255c11536d1417be1c63 [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:io';
import 'package:status_file/expectation.dart';
import 'package:status_file/status_file.dart';
import 'configuration.dart';
import 'environment.dart';
/// Tracks the [Expectation]s associated with a set of file paths.
///
/// For any given file path, returns the expected test results for that file.
/// A set can be loaded from a collection of status files. A file path may
/// exist in multiple files (or even multiple sections within the file). When
/// that happens, all of the expectations of every entry are combined.
class ExpectationSet {
/// Reads the expectations defined by the status files at [statusFilePaths]
/// when in [configuration].
static ExpectationSet read(
List<String> statusFilePaths, Configuration configuration) {
try {
var environment = new ConfigurationEnvironment(configuration);
var expectations = new ExpectationSet._();
for (var path in statusFilePaths) {
var file = new StatusFile.read(path);
file.validate(environment);
for (var section in file.sections) {
if (section.isEnabled(environment)) {
for (var entry in section.entries) {
expectations.addEntry(entry);
}
}
}
}
return expectations;
} on SyntaxError catch (error) {
stderr.writeln(error.toString());
exit(1);
throw "unreachable";
}
}
// Only create one copy of each Set<Expectation>.
// We just use .toString as a key, so we may make a few
// sets that only differ in their toString element order.
static Map<String, Set<Expectation>> _cachedSets = {};
Map<String, Set<Expectation>> _map = {};
Map<String, List<RegExp>> _keyToRegExps;
/// Create a TestExpectations object. See the [expectations] method
/// for an explanation of matching.
ExpectationSet._();
/// Add [entry] to the set of expectations.
void addEntry(StatusEntry entry) {
// Once we have started using the expectations we cannot add more
// rules.
if (_keyToRegExps != null) {
throw new StateError("Cannot add entries after it is already in use.");
}
_map
.putIfAbsent(entry.path, () => new Set<Expectation>())
.addAll(entry.expectations);
}
/// Get the expectations for the test at [path].
///
/// For every (key, expectation) pair, matches the key with the file name.
/// Returns the union of the expectations for all the keys that match.
///
/// Normal matching splits the key and the filename into path components and
/// checks that the anchored regular expression "^$keyComponent\$" matches
/// the corresponding filename component.
Set<Expectation> expectations(String path) {
var result = new Set<Expectation>();
var parts = path.split('/');
// Create mapping from keys to list of RegExps once and for all.
_preprocessForMatching();
_map.forEach((key, expectations) {
var regExps = _keyToRegExps[key];
if (regExps.length > parts.length) return;
for (var i = 0; i < regExps.length; i++) {
if (!regExps[i].hasMatch(parts[i])) return;
}
// If all components of the status file key matches the filename
// add the expectations to the result.
result.addAll(expectations);
});
// If no expectations were found the expectation is that the test
// passes.
if (result.isEmpty) {
result.add(Expectation.pass);
}
return _cachedSets.putIfAbsent(result.toString(), () => result);
}
/// Preprocesses the expectations for matching against filenames. Generates
/// lists of regular expressions once and for all for each key.
void _preprocessForMatching() {
if (_keyToRegExps != null) return;
_keyToRegExps = {};
var regExpCache = <String, RegExp>{};
_map.forEach((key, expectations) {
if (_keyToRegExps[key] != null) return;
var splitKey = key.split('/');
var regExps = new List<RegExp>(splitKey.length);
for (var i = 0; i < splitKey.length; i++) {
var component = splitKey[i];
var regExp = regExpCache.putIfAbsent(component,
() => new RegExp("^${splitKey[i]}\$".replaceAll('*', '.*')));
regExps[i] = regExp;
}
_keyToRegExps[key] = regExps;
});
}
}