blob: 251123822f3c63c8f40e15c37ee60caf14c77e9b [file] [log] [blame]
// Copyright (c) 2017, 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.
import 'dart:async';
import 'dart:io';
import 'package:async/async.dart';
import 'package:path/path.dart' as p;
import 'package:test/test.dart';
import 'descriptor.dart';
import 'sandbox.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 entity names by [Pattern] rather than
/// by exact [String].
///
/// This descriptor may only be used for validation.
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, Descriptor child(String basename))
: pattern = pattern,
_fn = child,
super('$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]) async {
var inSandbox = parent == null;
parent ??= sandbox;
var matchingEntries = await new Directory(parent).list()
.map((entry) =>
entry is File ? entry.resolveSymbolicLinksSync() : entry.path)
.where((entry) => matchesAll(pattern, p.basename(entry)))
.toList();
matchingEntries.sort();
var location = inSandbox ? "sandbox" : '"${prettyPath(parent)}"';
if (matchingEntries.isEmpty) {
fail('No entries found in $location matching ${_patternDescription}.');
}
var results = await Future.wait(matchingEntries.map((entry) {
var basename = p.basename(entry);
return runZoned(() {
return Result.capture/*<String>*/(new Future.sync(() async {
await _fn(basename).validate(parent);
return basename;
}));
}, onError: (_) {
// Validate may produce multiple errors, but we ignore all but the first
// to avoid cluttering the user with many different errors from many
// different un-matched entries.
});
}).toList());
var successes = results.where((result) => result.isValue).toList();
if (successes.length == 0) {
await waitAndReportErrors(results.map((result) => result.asFuture));
} else if (successes.length > 1) {
fail('Multiple valid entries found in $location matching '
'$_patternDescription:\n'
'${bullet(successes.map((result) => result.asValue.value))}');
}
}
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]) {
throw new UnsupportedError("Pattern descriptors don't support create().");
}
}