| // Copyright (c) 2013, 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 descriptor.pattern; |
| |
| import 'dart:async'; |
| import 'dart:io'; |
| |
| import 'package:path/path.dart' as path; |
| |
| import '../../descriptor.dart'; |
| import '../../scheduled_test.dart'; |
| import '../utils.dart'; |
| |
| /// A function that takes a name for a [Descriptor] and returns a [Descriptor]. |
| /// This is used for [PatternDescriptor]s, where the name isn't known |
| /// ahead-of-time. |
| typedef Descriptor EntryCreator(String name); |
| |
| /// A descriptor that matches filesystem entities by [Pattern] rather than |
| /// by [String]. It's used only for validation. |
| /// |
| /// This class takes an [EntryCreator], which should return a [Descriptor] that |
| /// will be used to validate the concrete filesystem entities that match the |
| /// [pattern]. |
| class PatternDescriptor extends Descriptor { |
| /// The [Pattern] this matches filenames against. Note that the pattern must |
| /// match the entire basename of the file. |
| final Pattern pattern; |
| |
| /// The function used to generate the [Descriptor] for filesystem entities |
| /// matching [pattern]. |
| final EntryCreator _fn; |
| |
| PatternDescriptor(Pattern pattern, this._fn) |
| : super('$pattern'), |
| pattern = pattern; |
| |
| /// Validates that there is some filesystem entity in [parent] that matches |
| /// [pattern] and the child entry. This finds all entities in [parent] |
| /// matching [pattern], then passes each of their names to the [EntityCreator] |
| /// and validates the result. If exactly one succeeds, [this] is considered |
| /// valid. |
| Future validate([String parent]) => schedule(() => validateNow(parent), |
| "validating ${describe()}"); |
| |
| Future validateNow([String parent]) { |
| if (parent == null) parent = defaultRoot; |
| // TODO(nweiz): make sure this works with symlinks. |
| var matchingEntries = new Directory(parent).listSync() |
| .map((entry) => entry is File ? entry.fullPathSync() : entry.path) |
| .where((entry) => fullMatch(path.basename(entry), pattern)) |
| .toList(); |
| matchingEntries.sort(); |
| |
| if (matchingEntries.isEmpty) { |
| fail("No entry found in '$parent' matching ${_patternDescription}."); |
| } |
| |
| return Future.wait(matchingEntries.map((entry) { |
| var descriptor = _fn(path.basename(entry)); |
| return descriptor.validateNow(parent).then((_) { |
| return new Pair(null, descriptor.describe()); |
| }).catchError((error) { |
| return new Pair(error.toString(), descriptor.describe()); |
| }); |
| })).then((results) { |
| var matches = results.where((result) => result.first == null).toList(); |
| // If exactly one entry matching [pattern] validated, we're happy. |
| if (matches.length == 1) return; |
| |
| // If more than one entry matching [pattern] validated, that's bad. |
| if (matches.length > 1) { |
| var resultString = matches.map((result) { |
| return prefixLines(result.last, firstPrefix: '* ', prefix: ' '); |
| }).join('\n'); |
| |
| fail("Multiple valid entries found in '$parent' matching " |
| "$_patternDescription:\n" |
| "$resultString"); |
| } |
| |
| // If no entries matching [pattern] validated, that's also bad. |
| var resultString = results.map((result) { |
| return prefixLines( |
| "Caught error\n" |
| "${prefixLines(result.first)}\n" |
| "while validating\n" |
| "${prefixLines(result.last)}", |
| firstPrefix: '* ', prefix: ' '); |
| }).join('\n'); |
| |
| fail("No valid entries found in '$parent' matching " |
| "$_patternDescription:\n" |
| "$resultString"); |
| }); |
| } |
| |
| String describe() => "entry matching $_patternDescription"; |
| |
| String get _patternDescription { |
| if (pattern is String) return "'$pattern'"; |
| if (pattern is! RegExp) return '$pattern'; |
| |
| var regExp = pattern as RegExp; |
| var flags = new StringBuffer(); |
| if (!regExp.isCaseSensitive) flags.write('i'); |
| if (regExp.isMultiLine) flags.write('m'); |
| return '/${regExp.pattern}/$flags'; |
| } |
| |
| Future create([String parent]) => new Future.error( |
| new UnsupportedError("Pattern descriptors don't support create().")); |
| } |