OLD | NEW |
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 library test.runner.parse_metadata; | 5 library test.runner.parse_metadata; |
6 | 6 |
7 import 'dart:io'; | 7 import 'dart:io'; |
8 | 8 |
9 import 'package:analyzer/analyzer.dart'; | 9 import 'package:analyzer/analyzer.dart'; |
10 import 'package:analyzer/src/generated/ast.dart'; | 10 import 'package:analyzer/src/generated/ast.dart'; |
(...skipping 14 matching lines...) Expand all Loading... |
25 "microseconds" | 25 "microseconds" |
26 ]; | 26 ]; |
27 | 27 |
28 /// Parse the test metadata for the test file at [path]. | 28 /// Parse the test metadata for the test file at [path]. |
29 /// | 29 /// |
30 /// Throws an [AnalysisError] if parsing fails or a [FormatException] if the | 30 /// Throws an [AnalysisError] if parsing fails or a [FormatException] if the |
31 /// test annotations are incorrect. | 31 /// test annotations are incorrect. |
32 Metadata parseMetadata(String path) { | 32 Metadata parseMetadata(String path) { |
33 var timeout; | 33 var timeout; |
34 var testOn; | 34 var testOn; |
| 35 var skip; |
35 | 36 |
36 var contents = new File(path).readAsStringSync(); | 37 var contents = new File(path).readAsStringSync(); |
37 var directives = parseDirectives(contents, name: path).directives; | 38 var directives = parseDirectives(contents, name: path).directives; |
38 var annotations = directives.isEmpty ? [] : directives.first.metadata; | 39 var annotations = directives.isEmpty ? [] : directives.first.metadata; |
39 | 40 |
40 // We explicitly *don't* just look for "package:test" imports here, | 41 // We explicitly *don't* just look for "package:test" imports here, |
41 // because it could be re-exported from another library. | 42 // because it could be re-exported from another library. |
42 var prefixes = directives.map((directive) { | 43 var prefixes = directives.map((directive) { |
43 if (directive is! ImportDirective) return null; | 44 if (directive is! ImportDirective) return null; |
44 if (directive.prefix == null) return null; | 45 if (directive.prefix == null) return null; |
(...skipping 30 matching lines...) Expand all Loading... |
75 _spanFor(annotation, path)); | 76 _spanFor(annotation, path)); |
76 } | 77 } |
77 testOn = _parseTestOn(annotation, constructorName, path); | 78 testOn = _parseTestOn(annotation, constructorName, path); |
78 } else if (name == 'Timeout') { | 79 } else if (name == 'Timeout') { |
79 if (timeout != null) { | 80 if (timeout != null) { |
80 throw new SourceSpanFormatException( | 81 throw new SourceSpanFormatException( |
81 "Only a single Timeout annotation may be used for a given test file.
", | 82 "Only a single Timeout annotation may be used for a given test file.
", |
82 _spanFor(annotation, path)); | 83 _spanFor(annotation, path)); |
83 } | 84 } |
84 timeout = _parseTimeout(annotation, constructorName, path); | 85 timeout = _parseTimeout(annotation, constructorName, path); |
| 86 } else if (name == 'Skip') { |
| 87 if (skip != null) { |
| 88 throw new SourceSpanFormatException( |
| 89 "Only a single Skip annotation may be used for a given test file.", |
| 90 _spanFor(annotation, path)); |
| 91 } |
| 92 skip = _parseSkip(annotation, constructorName, path); |
85 } | 93 } |
86 } | 94 } |
87 | 95 |
88 try { | 96 try { |
89 return new Metadata.parse( | 97 return new Metadata.parse( |
90 testOn: testOn == null ? null : testOn.stringValue, | 98 testOn: testOn == null ? null : testOn.stringValue, |
91 timeout: timeout); | 99 timeout: timeout, |
| 100 skip: skip); |
92 } on SourceSpanFormatException catch (error) { | 101 } on SourceSpanFormatException catch (error) { |
93 var file = new SourceFile(new File(path).readAsStringSync(), | 102 var file = new SourceFile(new File(path).readAsStringSync(), |
94 url: p.toUri(path)); | 103 url: p.toUri(path)); |
95 var span = contextualizeSpan(error.span, testOn, file); | 104 var span = contextualizeSpan(error.span, testOn, file); |
96 if (span == null) rethrow; | 105 if (span == null) rethrow; |
97 throw new SourceSpanFormatException(error.message, span); | 106 throw new SourceSpanFormatException(error.message, span); |
98 } | 107 } |
99 } | 108 } |
100 | 109 |
101 /// Parses a `@TestOn` annotation. | 110 /// Parses a `@TestOn` annotation. |
(...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
181 _spanFor(annotation.arguments, path)); | 190 _spanFor(annotation.arguments, path)); |
182 } | 191 } |
183 | 192 |
184 if (constructorName == null) { | 193 if (constructorName == null) { |
185 return new Timeout(_parseDuration(args.first, path)); | 194 return new Timeout(_parseDuration(args.first, path)); |
186 } else { | 195 } else { |
187 return new Timeout.factor(_parseNum(args.first, path)); | 196 return new Timeout.factor(_parseNum(args.first, path)); |
188 } | 197 } |
189 } | 198 } |
190 | 199 |
| 200 /// Parses a `@Skip` annotation. |
| 201 /// |
| 202 /// [annotation] is the annotation. [constructorName] is the name of the named |
| 203 /// constructor for the annotation, if any. [path] is the path to the file from |
| 204 /// which the annotation was parsed. |
| 205 /// |
| 206 /// Returns either `true` or a reason string. |
| 207 _parseSkip(Annotation annotation, String constructorName, String path) { |
| 208 if (constructorName != null) { |
| 209 throw new SourceSpanFormatException( |
| 210 'Skip doesn\'t have a constructor named "$constructorName".', |
| 211 _spanFor(annotation, path)); |
| 212 } |
| 213 |
| 214 if (annotation.arguments == null) { |
| 215 throw new SourceSpanFormatException( |
| 216 'Skip must have parentheses.', _spanFor(annotation, path)); |
| 217 } |
| 218 |
| 219 var args = annotation.arguments.arguments; |
| 220 if (args.length > 1) { |
| 221 throw new SourceSpanFormatException( |
| 222 'Skip takes zero arguments or one argument.', |
| 223 _spanFor(annotation.arguments, path)); |
| 224 } |
| 225 |
| 226 if (args.isEmpty) return true; |
| 227 |
| 228 if (args.first is NamedExpression) { |
| 229 throw new SourceSpanFormatException( |
| 230 "Skip doesn't take named parameters.", _spanFor(args.first, path)); |
| 231 } |
| 232 |
| 233 if (args.first is! StringLiteral) { |
| 234 throw new SourceSpanFormatException( |
| 235 "Skip takes a String.", _spanFor(args.first, path)); |
| 236 } |
| 237 |
| 238 return args.first.stringValue; |
| 239 } |
| 240 |
191 /// Parses a `const Duration` expression. | 241 /// Parses a `const Duration` expression. |
192 Duration _parseDuration(Expression expression, String path) { | 242 Duration _parseDuration(Expression expression, String path) { |
193 if (expression is! InstanceCreationExpression) { | 243 if (expression is! InstanceCreationExpression) { |
194 throw new SourceSpanFormatException( | 244 throw new SourceSpanFormatException( |
195 "Expected a Duration.", | 245 "Expected a Duration.", |
196 _spanFor(expression, path)); | 246 _spanFor(expression, path)); |
197 } | 247 } |
198 | 248 |
199 var constructor = expression as InstanceCreationExpression; | 249 var constructor = expression as InstanceCreationExpression; |
200 if (constructor.constructorName.type.name.name != 'Duration') { | 250 if (constructor.constructorName.type.name.name != 'Duration') { |
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
265 throw new SourceSpanFormatException( | 315 throw new SourceSpanFormatException( |
266 "Expected an integer.", _spanFor(expression, path)); | 316 "Expected an integer.", _spanFor(expression, path)); |
267 } | 317 } |
268 | 318 |
269 /// Creates a [SourceSpan] for [node]. | 319 /// Creates a [SourceSpan] for [node]. |
270 SourceSpan _spanFor(AstNode node, String path) => | 320 SourceSpan _spanFor(AstNode node, String path) => |
271 // Load a SourceFile from scratch here since we're only ever going to emit | 321 // Load a SourceFile from scratch here since we're only ever going to emit |
272 // one error per file anyway. | 322 // one error per file anyway. |
273 new SourceFile(new File(path).readAsStringSync(), url: p.toUri(path)) | 323 new SourceFile(new File(path).readAsStringSync(), url: p.toUri(path)) |
274 .span(node.offset, node.end); | 324 .span(node.offset, node.end); |
OLD | NEW |