Chromium Code Reviews| Index: lib/src/runner/parse_metadata.dart |
| diff --git a/lib/src/runner/parse_metadata.dart b/lib/src/runner/parse_metadata.dart |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..91a9b90454616244a376169594bad466e5bb32ab |
| --- /dev/null |
| +++ b/lib/src/runner/parse_metadata.dart |
| @@ -0,0 +1,97 @@ |
| +// Copyright (c) 2015, 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 unittest.runner.parse_metadata; |
| + |
| +import 'dart:io'; |
| + |
| +import 'package:analyzer/src/generated/ast.dart'; |
| +import 'package:path/path.dart' as p; |
| +import 'package:source_span/source_span.dart'; |
| + |
| +import '../backend/metadata.dart'; |
| +import '../util/dart.dart'; |
| + |
| +/// Parse the test metadata for the test file at [path]. |
| +/// |
| +/// Throws an [AnalysisError] if parsing fails or a [FormatException] if the |
| +/// test annotations are incorrect. |
| +Metadata parseMetadata(String path) { |
| + var testOn; |
| + for (var annotation in parseAnnotations(path)) { |
| + // The annotation syntax is ambiguous between named constructors and |
| + // prefixed annotations. We don't want to have to parse the imports in order |
| + // to resolve this ambiguity, so we just assume that unittest annotations |
|
Bob Nystrom
2015/03/12 21:03:21
Given that you are parsing the directives now, thi
nweiz
2015/03/12 23:40:22
Done.
|
| + // will never be prefixed. |
| + // |
| + // The analyzer parses "@x.y()" as prefix "x", annotation "y", and named |
| + // constructor null. It parses "@x.y.z()" as prefix "x", annotation "y", and |
| + // named constructor "z". |
| + |
| + // Always skip annotations with constructor names, because they must also |
| + // have prefixes and we assume unittest metadata never has a prefix. |
| + if (annotation.constructorName != null) continue; |
| + |
| + var name; |
| + var constructorName; |
| + var identifier = annotation.name; |
| + if (identifier is PrefixedIdentifier) { |
| + name = identifier.prefix.name; |
| + constructorName = identifier.identifier.name; |
| + } else { |
| + name = identifier.name; |
| + } |
| + |
| + if (name != 'TestOn') continue; |
| + if (constructorName != null) { |
| + throw new SourceSpanFormatException( |
| + 'TestOn doesn\'t have a constructor named "$constructorName".', |
| + _spanFor(identifier.identifier, path)); |
| + } |
| + |
| + if (annotation.arguments == null) { |
| + throw new SourceSpanFormatException( |
| + 'TestOn takes one argument.', _spanFor(annotation, path)); |
| + } |
| + |
| + var args = annotation.arguments.arguments; |
| + if (args.isEmpty) { |
| + throw new SourceSpanFormatException( |
| + 'TestOn takes one argument.', _spanFor(annotation.arguments, path)); |
| + } |
| + |
| + if (args.first is NamedExpression) { |
| + throw new SourceSpanFormatException( |
| + "TestOn doesn't take named parameters.", _spanFor(args.first, path)); |
| + } |
| + |
| + if (args.length > 1) { |
| + throw new SourceSpanFormatException( |
| + "TestOn takes only one argument.", |
| + _spanFor(annotation.arguments, path)); |
| + } |
| + |
| + if (args.first is! StringLiteral) { |
| + throw new SourceSpanFormatException( |
| + "TestOn takes a String.", _spanFor(args.first, path)); |
|
Bob Nystrom
2015/03/12 21:03:21
Since you don't handle adjacent strings (which is
nweiz
2015/03/12 23:40:22
StringLiteral is the superclass of both adjacent s
|
| + } |
| + |
| + if (testOn != null) { |
| + throw new SourceSpanFormatException( |
| + "Only a single TestOn annotation may be used for a given test file.", |
| + _spanFor(annotation, path)); |
| + } |
| + |
| + testOn = args.first.stringValue; |
| + } |
| + |
| + return new Metadata(testOn); |
| +} |
| + |
| +/// Creates a [SourceSpan] for [node]. |
| +SourceSpan _spanFor(AstNode node, String path) => |
| + // Load a SourceFile from scratch here since we're only ever going to emit |
| + // one error per file anyway. |
| + new SourceFile(new File(path).readAsStringSync(), url: p.toUri(path)) |
| + .span(node.offset, node.end); |