| // Copyright (c) 2012, 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. |
| |
| #library("status_file_parser"); |
| |
| #import("dart:io"); |
| #import("status_expression.dart"); |
| |
| /** Possible outcomes of running a test. */ |
| const CRASH = "crash"; |
| const TIMEOUT = "timeout"; |
| const FAIL = "fail"; |
| const PASS = "pass"; |
| /** |
| * An indication to skip the test. The caller is responsible for skipping it. |
| */ |
| const SKIP = "skip"; |
| const OK = "ok"; |
| /** |
| * An indication that a test is slow and we should allow extra time for |
| * completion. |
| */ |
| const SLOW = "slow"; |
| |
| const RegExp StripComment = const RegExp("^[^#]*"); |
| const RegExp HeaderPattern = const RegExp(r"^\[([^\]]+)\]"); |
| const RegExp RulePattern = const RegExp(r"\s*([^: ]*)\s*:(.*)"); |
| |
| // TODO(whesse): Implement configuration_info library that contains data |
| // structures for test configuration, including Section. |
| class Section { |
| BooleanExpression condition; |
| List<TestRule> testRules; |
| |
| Section.always() : condition = null, testRules = new List<TestRule>(); |
| Section(this.condition) : testRules = new List<TestRule>(); |
| |
| bool isEnabled(environment) => |
| condition == null || condition.evaluate(environment); |
| } |
| |
| void ReadTestExpectationsInto(TestExpectations expectations, |
| String statusFilePath, |
| environment, |
| onDone) { |
| List<Section> sections = new List<Section>(); |
| |
| void sectionsRead() { |
| for (Section section in sections) { |
| if (section.isEnabled(environment)) { |
| for (var rule in section.testRules) { |
| expectations.addRule(rule, environment); |
| } |
| } |
| } |
| onDone(); |
| } |
| |
| ReadConfigurationInto(statusFilePath, sections, sectionsRead); |
| } |
| |
| void ReadConfigurationInto(path, sections, onDone) { |
| File file = new File(path); |
| if (!file.existsSync()) { |
| throw new Exception('Cannot find test status file $path'); |
| } |
| InputStream file_stream = file.openInputStream(); |
| StringInputStream lines = new StringInputStream(file_stream); |
| |
| Section current = new Section.always(); |
| sections.add(current); |
| |
| lines.onLine = () { |
| String line; |
| while ((line = lines.readLine()) != null) { |
| Match match = StripComment.firstMatch(line); |
| line = (match == null) ? "" : match[0]; |
| line = line.trim(); |
| if (line.isEmpty) continue; |
| |
| match = HeaderPattern.firstMatch(line); |
| if (match != null) { |
| String condition_string = match[1].trim(); |
| List<String> tokens = new Tokenizer(condition_string).tokenize(); |
| ExpressionParser parser = new ExpressionParser(new Scanner(tokens)); |
| current = new Section(parser.parseBooleanExpression()); |
| sections.add(current); |
| continue; |
| } |
| |
| match = RulePattern.firstMatch(line); |
| if (match != null) { |
| String name = match[1].trim(); |
| // TODO(whesse): Handle test names ending in a wildcard (*). |
| String expression_string = match[2].trim(); |
| List<String> tokens = new Tokenizer(expression_string).tokenize(); |
| SetExpression expression = |
| new ExpressionParser(new Scanner(tokens)).parseSetExpression(); |
| current.testRules.add(new TestRule(name, expression)); |
| continue; |
| } |
| |
| print("unmatched line: $line"); |
| } |
| }; |
| |
| lines.onClosed = () { |
| onDone(); |
| }; |
| } |
| |
| |
| class TestRule { |
| String name; |
| SetExpression expression; |
| |
| TestRule(this.name, this.expression); |
| |
| String toString() => 'TestRule($name, $expression)'; |
| } |
| |
| |
| class TestExpectations { |
| Map _map; |
| bool _preprocessed = false; |
| Map _regExpCache; |
| Map _keyToRegExps; |
| |
| /** |
| * Create a TestExpectations object. See the [expectations] method |
| * for an explanation of matching. |
| */ |
| TestExpectations() : _map = new Map(); |
| |
| /** |
| * Add a rule to the expectations. |
| */ |
| void addRule(testRule, environment) { |
| // Once we have started using the expectations we cannot add more |
| // rules. |
| if (_preprocessed) { |
| throw "TestExpectations.addRule: cannot add more rules"; |
| } |
| var values = testRule.expression.evaluate(environment); |
| _map.putIfAbsent(testRule.name, () => new Set()).addAll(values); |
| } |
| |
| /** |
| * Compute the expectations for a test based on the filename. |
| * |
| * For every (key, expectation) pair. Match the key with the file |
| * name. Return 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<String> expectations(String filename) { |
| var result = new Set(); |
| var splitFilename = filename.split('/'); |
| |
| // Create mapping from keys to list of RegExps once and for all. |
| _preprocessForMatching(); |
| |
| _map.forEach((key, expectation) { |
| List regExps = _keyToRegExps[key]; |
| if (regExps.length > splitFilename.length) return; |
| for (var i = 0; i < regExps.length; i++) { |
| if (!regExps[i].hasMatch(splitFilename[i])) return; |
| } |
| // If all components of the status file key matches the filename |
| // add the expectations to the result. |
| result.addAll(expectation); |
| }); |
| |
| // If no expectations were found the expectation is that the test |
| // passes. |
| if (result.isEmpty) { |
| result.add(PASS); |
| } |
| return result; |
| } |
| |
| // Preprocess the expectations for matching against |
| // filenames. Generate lists of regular expressions once and for all |
| // for each key. |
| void _preprocessForMatching() { |
| if (_preprocessed) return; |
| |
| _keyToRegExps = new Map(); |
| _regExpCache = new Map(); |
| |
| _map.forEach((key, expectations) { |
| if (_keyToRegExps[key] != null) return; |
| var splitKey = key.split('/'); |
| var regExps = new List(splitKey.length); |
| for (var i = 0; i < splitKey.length; i++) { |
| var component = splitKey[i]; |
| var regExp = _regExpCache[component]; |
| if (regExp == null) { |
| var pattern = "^${splitKey[i]}\$".replaceAll('*', '.*'); |
| regExp = new RegExp(pattern); |
| _regExpCache[component] = regExp; |
| } |
| regExps[i] = regExp; |
| } |
| _keyToRegExps[key] = regExps; |
| }); |
| |
| _regExpCache = null; |
| _preprocessed = true; |
| } |
| } |