OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. |
| 4 |
| 5 // TODO(jmesserly): this was factored out of |
| 6 // dart-lang/sdk/tools/testing/dart/multitest.dart |
| 7 library dev_compiler.test.tools.multitest; |
| 8 |
| 9 final validMultitestOutcomes = new Set<String>.from([ |
| 10 'ok', |
| 11 'compile-time error', |
| 12 'runtime error', |
| 13 'static type warning', |
| 14 'dynamic type error', |
| 15 'checked mode compile-time error' |
| 16 ]); |
| 17 |
| 18 // Require at least one non-space character before '///' |
| 19 final _multiTestRegExp = new RegExp(r"\S */// \w+:(.*)"); |
| 20 |
| 21 bool isMultiTest(String contents) => _multiTestRegExp.hasMatch(contents); |
| 22 |
| 23 // Multitests are Dart test scripts containing lines of the form |
| 24 // " [some dart code] /// [key]: [error type]" |
| 25 // |
| 26 // For each key in the file, a new test file is made containing all |
| 27 // the normal lines of the file, and all of the multitest lines containing |
| 28 // that key, in the same order as in the source file. The new test is expected |
| 29 // to pass if the error type listed is 'ok', or to fail if there is an error |
| 30 // type of type 'compile-time error', 'runtime error', 'static type warning', or |
| 31 // 'dynamic type error'. The type error tests fail only in checked mode. |
| 32 // There is also a test created from only the untagged lines of the file, |
| 33 // with key "none", which is expected to pass. This library extracts these |
| 34 // tests, writes them into a temporary directory, and passes them to the test |
| 35 // runner. These tests may be referred to in the status files with the |
| 36 // pattern [test name]/[key]. |
| 37 // |
| 38 // For example: file I_am_a_multitest.dart |
| 39 // aaa |
| 40 // bbb /// 02: runtime error |
| 41 // ccc /// 02: continued |
| 42 // ddd /// 07: static type warning |
| 43 // eee /// 10: ok |
| 44 // fff |
| 45 // |
| 46 // should create four tests: |
| 47 // I_am_a_multitest_none.dart |
| 48 // aaa |
| 49 // fff |
| 50 // |
| 51 // I_am_a_multitest_02.dart |
| 52 // aaa |
| 53 // bbb /// 02: runtime error |
| 54 // ccc /// 02: continued |
| 55 // fff |
| 56 // |
| 57 // I_am_a_multitest_07.dart |
| 58 // aaa |
| 59 // ddd /// 07: static type warning |
| 60 // fff |
| 61 // |
| 62 // and I_am_a_multitest_10.dart |
| 63 // aaa |
| 64 // eee /// 10: ok |
| 65 // fff |
| 66 // |
| 67 // Note that it is possible to indicate more than one acceptable outcome |
| 68 // in the case of dynamic and static type warnings |
| 69 // aaa |
| 70 // ddd /// 07: static type warning, dynamic type error |
| 71 // fff |
| 72 |
| 73 void extractTestsFromMultitest(String filePath, String contents, |
| 74 Map<String, String> tests, Map<String, Set<String>> outcomes) { |
| 75 int first_newline = contents.indexOf('\n'); |
| 76 final String line_separator = (first_newline == 0 || |
| 77 contents[first_newline - 1] != '\r') ? '\n' : '\r\n'; |
| 78 List<String> lines = contents.split(line_separator); |
| 79 if (lines.last == '') lines.removeLast(); |
| 80 contents = null; |
| 81 |
| 82 // Create the set of multitests, which will have a new test added each |
| 83 // time we see a multitest line with a new key. |
| 84 Map<String, List<String>> testsAsLines = new Map<String, List<String>>(); |
| 85 |
| 86 // Add the default case with key "none". |
| 87 testsAsLines['none'] = new List<String>(); |
| 88 outcomes['none'] = new Set<String>(); |
| 89 |
| 90 int lineCount = 0; |
| 91 for (String line in lines) { |
| 92 lineCount++; |
| 93 var annotation = new _Annotation.from(line); |
| 94 if (annotation != null) { |
| 95 testsAsLines.putIfAbsent( |
| 96 annotation.key, () => new List<String>.from(testsAsLines["none"])); |
| 97 // Add line to test with annotation.key as key, empty line to the rest. |
| 98 for (var key in testsAsLines.keys) { |
| 99 testsAsLines[key].add(annotation.key == key ? line : ""); |
| 100 } |
| 101 outcomes.putIfAbsent(annotation.key, () => new Set<String>()); |
| 102 if (annotation.rest != 'continued') { |
| 103 for (String nextOutcome in annotation.outcomesList) { |
| 104 if (validMultitestOutcomes.contains(nextOutcome)) { |
| 105 outcomes[annotation.key].add(nextOutcome); |
| 106 } else { |
| 107 print("Warning: Invalid test directive '$nextOutcome' on line " |
| 108 "${lineCount}:\n${annotation.rest} "); |
| 109 } |
| 110 } |
| 111 } |
| 112 } else { |
| 113 for (var test in testsAsLines.values) test.add(line); |
| 114 } |
| 115 } |
| 116 // End marker, has a final line separator so we don't need to add it after |
| 117 // joining the lines. |
| 118 var marker = '// Test created from multitest named $filePath.' |
| 119 '$line_separator'; |
| 120 for (var test in testsAsLines.values) test.add(marker); |
| 121 |
| 122 var keysToDelete = []; |
| 123 // Check that every key (other than the none case) has at least one outcome |
| 124 for (var outcomeKey in outcomes.keys) { |
| 125 if (outcomeKey != 'none' && outcomes[outcomeKey].isEmpty) { |
| 126 print("Warning: Test ${outcomeKey} has no valid annotated outcomes.\n" |
| 127 "Expected one of: ${validMultitestOutcomes.toString()}"); |
| 128 // If this multitest doesn't have an outcome, mark the multitest for |
| 129 // deletion. |
| 130 keysToDelete.add(outcomeKey); |
| 131 } |
| 132 } |
| 133 // If a key/multitest was marked for deletion, do the necessary cleanup. |
| 134 keysToDelete.forEach(outcomes.remove); |
| 135 keysToDelete.forEach(testsAsLines.remove); |
| 136 |
| 137 // Copy all the tests into the output map tests, as multiline strings. |
| 138 for (String key in testsAsLines.keys) { |
| 139 tests[key] = testsAsLines[key].join(line_separator); |
| 140 } |
| 141 } |
| 142 |
| 143 // Represents a mutlitest annotation in the special /// comment. |
| 144 class _Annotation { |
| 145 String key; |
| 146 String rest; |
| 147 List<String> outcomesList; |
| 148 _Annotation() {} |
| 149 factory _Annotation.from(String line) { |
| 150 // Do an early return with "null" if this is not a valid multitest |
| 151 // annotation. |
| 152 if (!line.contains('///')) { |
| 153 return null; |
| 154 } |
| 155 var parts = line.split('///')[1] |
| 156 .split(':') |
| 157 .map((s) => s.trim()) |
| 158 .where((s) => s.length > 0) |
| 159 .toList(); |
| 160 if (parts.length <= 1) { |
| 161 return null; |
| 162 } |
| 163 |
| 164 var annotation = new _Annotation(); |
| 165 annotation.key = parts[0]; |
| 166 annotation.rest = parts[1]; |
| 167 annotation.outcomesList = |
| 168 annotation.rest.split(',').map((s) => s.trim()).toList(); |
| 169 return annotation; |
| 170 } |
| 171 } |
OLD | NEW |