Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(291)

Unified Diff: lib/src/runner/parse_metadata.dart

Issue 1086213002: Support a @Timeout annotation. (Closed) Base URL: git@github.com:dart-lang/test@master
Patch Set: CHANGELOG + README Created 5 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « lib/src/runner/loader.dart ('k') | lib/src/runner/vm/isolate_listener.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: lib/src/runner/parse_metadata.dart
diff --git a/lib/src/runner/parse_metadata.dart b/lib/src/runner/parse_metadata.dart
index 1fcbaf072f067d7ed0103337af11dc889e22b76f..5cc4cb61039b3fa19277c68dc193ba882eff2a7f 100644
--- a/lib/src/runner/parse_metadata.dart
+++ b/lib/src/runner/parse_metadata.dart
@@ -12,13 +12,25 @@ import 'package:path/path.dart' as p;
import 'package:source_span/source_span.dart';
import '../backend/metadata.dart';
+import '../frontend/timeout.dart';
import '../util/dart.dart';
+/// The valid argument names for [new Duration].
+const _durationArgs = const [
+ "days",
+ "hours",
+ "minutes",
+ "seconds",
+ "milliseconds",
+ "microseconds"
+];
+
/// 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 timeout;
var testOn;
var contents = new File(path).readAsStringSync();
@@ -56,59 +68,202 @@ Metadata parseMetadata(String path) {
}
}
- if (name != 'TestOn') continue;
- if (constructorName != null) {
- throw new SourceSpanFormatException(
- 'TestOn doesn\'t have a constructor named "$constructorName".',
- _spanFor(identifier.identifier, path));
+ if (name == 'TestOn') {
+ if (testOn != null) {
+ throw new SourceSpanFormatException(
+ "Only a single TestOn annotation may be used for a given test file.",
+ _spanFor(annotation, path));
+ }
+ testOn = _parseTestOn(annotation, constructorName, path);
+ } else if (name == 'Timeout') {
+ if (timeout != null) {
+ throw new SourceSpanFormatException(
+ "Only a single Timeout annotation may be used for a given test file.",
+ _spanFor(annotation, path));
+ }
+ timeout = _parseTimeout(annotation, constructorName, path);
}
+ }
- if (annotation.arguments == null) {
- throw new SourceSpanFormatException(
- 'TestOn takes one argument.', _spanFor(annotation, path));
- }
+ try {
+ return new Metadata.parse(
+ testOn: testOn == null ? null : testOn.stringValue,
+ timeout: timeout);
+ } on SourceSpanFormatException catch (error) {
+ var file = new SourceFile(new File(path).readAsStringSync(),
+ url: p.toUri(path));
+ var span = contextualizeSpan(error.span, testOn, file);
+ if (span == null) rethrow;
+ throw new SourceSpanFormatException(error.message, span);
+ }
+}
- var args = annotation.arguments.arguments;
- if (args.isEmpty) {
- throw new SourceSpanFormatException(
- 'TestOn takes one argument.', _spanFor(annotation.arguments, path));
- }
+/// Parses a `@TestOn` annotation.
+///
+/// [annotation] is the annotation. [constructorName] is the name of the named
+/// constructor for the annotation, if any. [path] is the path to the file from
+/// which the annotation was parsed.
+StringLiteral _parseTestOn(Annotation annotation, String constructorName,
+ String path) {
+ if (constructorName != null) {
+ throw new SourceSpanFormatException(
+ 'TestOn doesn\'t have a constructor named "$constructorName".',
+ _spanFor(annotation, path));
+ }
- if (args.first is NamedExpression) {
- throw new SourceSpanFormatException(
- "TestOn doesn't take named parameters.", _spanFor(args.first, 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));
+ }
+
+ return args.first;
+}
+
+/// Parses a `@Timeout` annotation.
+///
+/// [annotation] is the annotation. [constructorName] is the name of the named
+/// constructor for the annotation, if any. [path] is the path to the file from
+/// which the annotation was parsed.
+Timeout _parseTimeout(Annotation annotation, String constructorName,
+ String path) {
+ if (constructorName != null && constructorName != 'factor') {
+ throw new SourceSpanFormatException(
+ 'Timeout doesn\'t have a constructor named "$constructorName".',
+ _spanFor(annotation, path));
+ }
+
+ var description = 'Timeout';
+ if (constructorName != null) description += '.$constructorName';
+
+ if (annotation.arguments == null) {
+ throw new SourceSpanFormatException(
+ '$description takes one argument.', _spanFor(annotation, path));
+ }
+
+ var args = annotation.arguments.arguments;
+ if (args.isEmpty) {
+ throw new SourceSpanFormatException(
+ '$description takes one argument.',
+ _spanFor(annotation.arguments, path));
+ }
+
+ if (args.first is NamedExpression) {
+ throw new SourceSpanFormatException(
+ "$description doesn't take named parameters.",
+ _spanFor(args.first, path));
+ }
+
+ if (args.length > 1) {
+ throw new SourceSpanFormatException(
+ "$description takes only one argument.",
+ _spanFor(annotation.arguments, path));
+ }
+
+ if (constructorName == null) {
+ return new Timeout(_parseDuration(args.first, path));
+ } else {
+ return new Timeout.factor(_parseNum(args.first, path));
+ }
+}
+
+/// Parses a `const Duration` expression.
+Duration _parseDuration(Expression expression, String path) {
+ if (expression is! InstanceCreationExpression) {
+ throw new SourceSpanFormatException(
+ "Expected a Duration.",
+ _spanFor(expression, path));
+ }
+
+ var constructor = expression as InstanceCreationExpression;
+ if (constructor.constructorName.type.name.name != 'Duration') {
+ throw new SourceSpanFormatException(
+ "Expected a Duration.",
+ _spanFor(constructor, path));
+ }
- if (args.length > 1) {
+ if (constructor.keyword.lexeme != "const") {
+ throw new SourceSpanFormatException(
+ "Duration must use a const constructor.",
+ _spanFor(constructor, path));
+ }
+
+ if (constructor.constructorName.name != null) {
+ throw new SourceSpanFormatException(
+ "Duration doesn't have a constructor named "
+ '"${constructor.constructorName}".',
+ _spanFor(constructor.constructorName, path));
+ }
+
+ var values = {};
+ var args = constructor.argumentList.arguments;
+ for (var argument in args) {
+ if (argument is! NamedExpression) {
throw new SourceSpanFormatException(
- "TestOn takes only one argument.",
- _spanFor(annotation.arguments, path));
+ "Duration doesn't take positional arguments.",
+ _spanFor(argument, path));
}
- if (args.first is! StringLiteral) {
+ var name = argument.name.label.name;
+ if (!_durationArgs.contains(name)) {
throw new SourceSpanFormatException(
- "TestOn takes a String.", _spanFor(args.first, path));
+ 'Duration doesn\'t take an argument named "$name".',
+ _spanFor(argument, path));
}
- if (testOn != null) {
+ if (values.containsKey(name)) {
throw new SourceSpanFormatException(
- "Only a single TestOn annotation may be used for a given test file.",
- _spanFor(annotation, path));
+ 'An argument named "$name" was already passed.',
+ _spanFor(argument, path));
}
- testOn = args.first;
+ values[name] = _parseInt(argument.expression, path);
}
- try {
- return new Metadata.parse(
- testOn: testOn == null ? null : testOn.stringValue);
- } on SourceSpanFormatException catch (error) {
- var file = new SourceFile(new File(path).readAsStringSync(),
- url: p.toUri(path));
- var span = contextualizeSpan(error.span, testOn, file);
- if (span == null) rethrow;
- throw new SourceSpanFormatException(error.message, span);
- }
+ return new Duration(
+ days: values["days"] == null ? 0 : values["days"],
+ hours: values["hours"] == null ? 0 : values["hours"],
+ minutes: values["minutes"] == null ? 0 : values["minutes"],
+ seconds: values["seconds"] == null ? 0 : values["seconds"],
+ milliseconds: values["milliseconds"] == null ? 0 : values["milliseconds"],
+ microseconds:
+ values["microseconds"] == null ? 0 : values["microseconds"]);
+}
+
+/// Parses a constant number literal.
+num _parseNum(Expression expression, String path) {
+ if (expression is IntegerLiteral) return expression.value;
+ if (expression is DoubleLiteral) return expression.value;
+ throw new SourceSpanFormatException(
+ "Expected a number.", _spanFor(expression, path));
+}
+
+/// Parses a constant int literal.
+int _parseInt(Expression expression, String path) {
+ if (expression is IntegerLiteral) return expression.value;
+ throw new SourceSpanFormatException(
+ "Expected an integer.", _spanFor(expression, path));
}
/// Creates a [SourceSpan] for [node].
« no previous file with comments | « lib/src/runner/loader.dart ('k') | lib/src/runner/vm/isolate_listener.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698