From bcdb64e20eae23339dffdb011cb2f5a4b7653548 Mon Sep 17 00:00:00 2001 From: "nweiz@google.com" Date: Tue, 19 Nov 2013 20:48:45 +0000 Subject: [PATCH] Refactor pkg/path. This CL does several things: * Splits lib/path.dart into multiple libraries. * Renames "Builder" to "Context". * Makes the Context API match the top-level functions ("root" was renamed to "current" and "resolve" was renamed to "absolute"). * The top-level "absolute" function now takes multiple parts, to match [Context.absolute]. R=rnystrom@google.com BUG=14981 Review URL: https://codereview.chromium.org//62753005 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@30423 260f80e4-7a28-3924-810f-c04153c831b5 --- pkg/path/README.md | 8 +- pkg/path/lib/path.dart | 988 +----------------- pkg/path/lib/src/context.dart | 492 +++++++++ pkg/path/lib/src/parsed_path.dart | 186 ++++ pkg/path/lib/src/path_exception.dart | 15 + pkg/path/lib/src/style.dart | 118 +++ pkg/path/lib/src/style/posix.dart | 42 + pkg/path/lib/src/style/url.dart | 25 + pkg/path/lib/src/style/windows.dart | 74 ++ pkg/path/test/browser_test.dart | 14 +- pkg/path/test/io_test.dart | 14 +- pkg/path/test/path_test.dart | 24 +- pkg/path/test/posix_test.dart | 608 ++++++----- pkg/path/test/relative_test.dart | 30 +- pkg/path/test/url_test.dart | 812 +++++++------- pkg/path/test/windows_test.dart | 770 +++++++------- .../src/descriptor/directory_descriptor.dart | 9 +- pkg/stack_trace/test/vm_test.dart | 6 +- 18 files changed, 2141 insertions(+), 2094 deletions(-) create mode 100644 pkg/path/lib/src/context.dart create mode 100644 pkg/path/lib/src/parsed_path.dart create mode 100644 pkg/path/lib/src/path_exception.dart create mode 100644 pkg/path/lib/src/style.dart create mode 100644 pkg/path/lib/src/style/posix.dart create mode 100644 pkg/path/lib/src/style/url.dart create mode 100644 pkg/path/lib/src/style/windows.dart diff --git a/pkg/path/README.md b/pkg/path/README.md index eff60de4a95..2e3eec1b746 100644 --- a/pkg/path/README.md +++ b/pkg/path/README.md @@ -7,7 +7,7 @@ We've tried very hard to make this library do the "right" thing on whatever platform you run it on, including in the browser. When you use the top-level functions, it will assume the current platform's path style and work with that. If you want to explicitly work with paths of a specific style, you can -construct a `path.Builder` for that style. +construct a `path.Context` for that style. ## Using @@ -27,10 +27,10 @@ This calls the top-level [join] function to join "directory" and If you want to work with paths for a specific platform regardless of the underlying platform that the program is running on, you can create a -[Builder] and give it an explicit [Style]: +[Context] and give it an explicit [Style]: - var builder = new path.Builder(style: Style.windows); - builder.join("directory", "file.txt"); + var context = new path.Context(style: Style.windows); + context.join("directory", "file.txt"); This will join "directory" and "file.txt" using the Windows path separator, even when the program is run on a POSIX machine. diff --git a/pkg/path/lib/path.dart b/pkg/path/lib/path.dart index 5f469edd6a6..6f522c5bc74 100644 --- a/pkg/path/lib/path.dart +++ b/pkg/path/lib/path.dart @@ -37,45 +37,47 @@ /// /// If you want to work with paths for a specific platform regardless of the /// underlying platform that the program is running on, you can create a -/// [Builder] and give it an explicit [Style]: +/// [Context] and give it an explicit [Style]: /// -/// var builder = new path.Builder(style: Style.windows); -/// builder.join("directory", "file.txt"); +/// var context = new path.Context(style: Style.windows); +/// context.join("directory", "file.txt"); /// /// This will join "directory" and "file.txt" using the Windows path separator, /// even when the program is run on a POSIX machine. library path; -/// A default builder for manipulating POSIX paths. -final posix = new Builder(style: Style.posix); +import 'src/context.dart'; +import 'src/style.dart'; -/// A default builder for manipulating Windows paths. -final windows = new Builder(style: Style.windows); +export 'src/context.dart'; +export 'src/path_exception.dart'; +export 'src/style.dart'; -/// A default builder for manipulating URLs. -final url = new Builder(style: Style.url); +/// A default context for manipulating POSIX paths. +final posix = new Context(style: Style.posix); -/// Inserts [length] elements in front of the [list] and fills them with the -/// [fillValue]. -void _growListFront(List list, int length, fillValue) => - list.insertAll(0, new List.filled(length, fillValue)); +/// A default context for manipulating Windows paths. +final windows = new Context(style: Style.windows); + +/// A default context for manipulating URLs. +final url = new Context(style: Style.url); /// The result of [Uri.base] last time the current working directory was /// calculated. /// -/// This is used to invalidate [_cachedBuilder] when the working directory has +/// This is used to invalidate [_cachedContext] when the working directory has /// changed since the last time a function was called. Uri _lastBaseUri; -/// An internal builder for the current OS so we can provide a straight +/// An internal context for the current OS so we can provide a straight /// functional interface and not require users to create one. -Builder get _builder { - if (_cachedBuilder != null && Uri.base == _lastBaseUri) return _cachedBuilder; +Context get _context { + if (_cachedContext != null && Uri.base == _lastBaseUri) return _cachedContext; _lastBaseUri = Uri.base; - _cachedBuilder = new Builder(); - return _cachedBuilder; + _cachedContext = new Context(); + return _cachedContext; } -Builder _cachedBuilder; +Context _cachedContext; /// Gets the path to the current working directory. /// @@ -95,13 +97,15 @@ String get current { /// Gets the path separator for the current platform. This is `\` on Windows /// and `/` on other platforms (including the browser). -String get separator => _builder.separator; +String get separator => _context.separator; -/// Converts [path] to an absolute path by resolving it relative to the current -/// working directory. If [path] is already an absolute path, just returns it. +/// Creates a new path by appending the given path parts to [current]. +/// Equivalent to [join()] with [current] as the first argument. Example: /// -/// path.absolute('foo/bar.txt'); // -> /your/current/dir/foo/bar.txt -String absolute(String path) => join(current, path); +/// path.absolute('path', 'to/foo'); // -> '/your/current/dir/path/to/foo' +String absolute(String part1, [String part2, String part3, String part4, + String part5, String part6, String part7]) => + _context.absolute(part1, part2, part3, part4, part5, part6, part7); /// Gets the part of [path] after the last separator. /// @@ -110,8 +114,8 @@ String absolute(String path) => join(current, path); /// /// Trailing separators are ignored. /// -/// builder.basename('path/to/'); // -> 'to' -String basename(String path) => _builder.basename(path); +/// path.basename('path/to/'); // -> 'to' +String basename(String path) => _context.basename(path); /// Gets the part of [path] after the last separator, and without any trailing /// file extension. @@ -120,9 +124,9 @@ String basename(String path) => _builder.basename(path); /// /// Trailing separators are ignored. /// -/// builder.basenameWithoutExtension('path/to/foo.dart/'); // -> 'foo' +/// path.basenameWithoutExtension('path/to/foo.dart/'); // -> 'foo' String basenameWithoutExtension(String path) => - _builder.basenameWithoutExtension(path); + _context.basenameWithoutExtension(path); /// Gets the part of [path] before the last separator. /// @@ -131,7 +135,7 @@ String basenameWithoutExtension(String path) => /// /// Trailing separators are ignored. /// -/// builder.dirname('path/to/'); // -> 'path' +/// path.dirname('path/to/'); // -> 'path' /// /// If an absolute path contains no directories, only a root, then the root /// is returned. @@ -143,7 +147,7 @@ String basenameWithoutExtension(String path) => /// /// path.dirname('foo'); // -> '.' /// path.dirname(''); // -> '.' -String dirname(String path) => _builder.dirname(path); +String dirname(String path) => _context.dirname(path); /// Gets the file extension of [path]: the portion of [basename] from the last /// `.` to the end (including the `.` itself). @@ -158,7 +162,7 @@ String dirname(String path) => _builder.dirname(path); /// /// path.extension('~/.bashrc'); // -> '' /// path.extension('~/.notes.txt'); // -> '.txt' -String extension(String path) => _builder.extension(path); +String extension(String path) => _context.extension(path); // TODO(nweiz): add a UNC example for Windows once issue 7323 is fixed. /// Returns the root of [path], if it's absolute, or the empty string if it's @@ -176,7 +180,7 @@ String extension(String path) => _builder.extension(path); /// path.rootPrefix('path/to/foo'); // -> '' /// path.rootPrefix('http://dartlang.org/path/to/foo'); /// // -> 'http://dartlang.org' -String rootPrefix(String path) => _builder.rootPrefix(path); +String rootPrefix(String path) => _context.rootPrefix(path); /// Returns `true` if [path] is an absolute path and `false` if it is a /// relative path. @@ -190,13 +194,13 @@ String rootPrefix(String path) => _builder.rootPrefix(path); /// relative to the root of the current URL. Since root-relative paths are still /// absolute in every other sense, [isAbsolute] will return true for them. They /// can be detected using [isRootRelative]. -bool isAbsolute(String path) => _builder.isAbsolute(path); +bool isAbsolute(String path) => _context.isAbsolute(path); /// Returns `true` if [path] is a relative path and `false` if it is absolute. /// On POSIX systems, absolute paths start with a `/` (forward slash). On /// Windows, an absolute path starts with `\\`, or a drive letter followed by /// `:/` or `:\`. -bool isRelative(String path) => _builder.isRelative(path); +bool isRelative(String path) => _context.isRelative(path); /// Returns `true` if [path] is a root-relative path and `false` if it's not. /// @@ -206,7 +210,7 @@ bool isRelative(String path) => _builder.isRelative(path); /// can be detected using [isRootRelative]. /// /// No POSIX and Windows paths are root-relative. -bool isRootRelative(String path) => _builder.isRootRelative(path); +bool isRootRelative(String path) => _context.isRootRelative(path); /// Joins the given path parts into a single path using the current platform's /// [separator]. Example: @@ -223,7 +227,7 @@ bool isRootRelative(String path) => _builder.isRootRelative(path); /// path.join('path', '/to', 'foo'); // -> '/to/foo' String join(String part1, [String part2, String part3, String part4, String part5, String part6, String part7, String part8]) => - _builder.join(part1, part2, part3, part4, part5, part6, part7, part8); + _context.join(part1, part2, part3, part4, part5, part6, part7, part8); /// Joins the given path parts into a single path using the current platform's /// [separator]. Example: @@ -240,7 +244,7 @@ String join(String part1, [String part2, String part3, String part4, /// path.joinAll(['path', '/to', 'foo']); // -> '/to/foo' /// /// For a fixed number of parts, [join] is usually terser. -String joinAll(Iterable parts) => _builder.joinAll(parts); +String joinAll(Iterable parts) => _context.joinAll(parts); // TODO(nweiz): add a UNC example for Windows once issue 7323 is fixed. /// Splits [path] into its components using the current platform's [separator]. @@ -263,13 +267,13 @@ String joinAll(Iterable parts) => _builder.joinAll(parts); /// // Browser /// path.split('http://dartlang.org/path/to/foo'); /// // -> ['http://dartlang.org', 'path', 'to', 'foo'] -List split(String path) => _builder.split(path); +List split(String path) => _context.split(path); /// Normalizes [path], simplifying it by handling `..`, and `.`, and /// removing redundant path separators whenever possible. /// /// path.normalize('path/./to/..//file.text'); // -> 'path/file.txt' -String normalize(String path) => _builder.normalize(path); +String normalize(String path) => _context.normalize(path); /// Attempts to convert [path] to an equivalent relative path from the current /// directory. @@ -299,19 +303,19 @@ String normalize(String path) => _builder.normalize(path); /// path.relative('http://dartlang.org', from: 'http://pub.dartlang.org'); /// // -> 'http://dartlang.org' String relative(String path, {String from}) => - _builder.relative(path, from: from); + _context.relative(path, from: from); /// Returns `true` if [child] is a path beneath `parent`, and `false` otherwise. /// /// path.isWithin('/root/path', '/root/path/a'); // -> true /// path.isWithin('/root/path', '/root/other'); // -> false /// path.isWithin('/root/path', '/root/path') // -> false -bool isWithin(String parent, String child) => _builder.isWithin(parent, child); +bool isWithin(String parent, String child) => _context.isWithin(parent, child); /// Removes a trailing extension from the last part of [path]. /// /// withoutExtension('path/to/foo.dart'); // -> 'path/to/foo' -String withoutExtension(String path) => _builder.withoutExtension(path); +String withoutExtension(String path) => _context.withoutExtension(path); /// Returns the path represented by [uri]. /// @@ -329,7 +333,7 @@ String withoutExtension(String path) => _builder.withoutExtension(path); /// // URL /// path.fromUri(Uri.parse('http://dartlang.org/path/to/foo')) /// // -> 'http://dartlang.org/path/to/foo' -String fromUri(Uri uri) => _builder.fromUri(uri); +String fromUri(Uri uri) => _context.fromUri(uri); /// Returns the URI that represents [path]. /// @@ -352,898 +356,4 @@ String fromUri(Uri uri) => _builder.fromUri(uri); /// /// path.toUri('path/to/foo') /// // -> Uri.parse('path/to/foo') -Uri toUri(String path) => _builder.toUri(path); - -/// Validates that there are no non-null arguments following a null one and -/// throws an appropriate [ArgumentError] on failure. -_validateArgList(String method, List args) { - for (var i = 1; i < args.length; i++) { - // Ignore nulls hanging off the end. - if (args[i] == null || args[i - 1] != null) continue; - - var numArgs; - for (numArgs = args.length; numArgs >= 1; numArgs--) { - if (args[numArgs - 1] != null) break; - } - - // Show the arguments. - var message = new StringBuffer(); - message.write("$method("); - message.write(args.take(numArgs) - .map((arg) => arg == null ? "null" : '"$arg"') - .join(", ")); - message.write("): part ${i - 1} was null, but part $i was not."); - throw new ArgumentError(message.toString()); - } -} - -/// An instantiable class for manipulating paths. Unlike the top-level -/// functions, this lets you explicitly select what platform the paths will use. -class Builder { - /// Creates a new path builder for the given style and root directory. - /// - /// If [style] is omitted, it uses the host operating system's path style. If - /// only [root] is omitted, it defaults ".". If *both* [style] and [root] are - /// omitted, [root] defaults to the current working directory. - /// - /// On the browser, the path style is [Style.url]. In Dartium, [root] defaults - /// to the current URL. When using dart2js, it currently defaults to `.` due - /// to technical constraints. - factory Builder({Style style, String root}) { - if (root == null) { - if (style == null) { - root = current; - } else { - root = "."; - } - } - - if (style == null) style = Style.platform; - - return new Builder._(style, root); - } - - Builder._(this.style, this.root); - - /// The style of path that this builder works with. - final Style style; - - /// The root directory that relative paths will be relative to. - final String root; - - /// Gets the path separator for the builder's [style]. On Mac and Linux, - /// this is `/`. On Windows, it's `\`. - String get separator => style.separator; - - /// Gets the part of [path] after the last separator on the builder's - /// platform. - /// - /// builder.basename('path/to/foo.dart'); // -> 'foo.dart' - /// builder.basename('path/to'); // -> 'to' - /// - /// Trailing separators are ignored. - /// - /// builder.basename('path/to/'); // -> 'to' - String basename(String path) => _parse(path).basename; - - /// Gets the part of [path] after the last separator on the builder's - /// platform, and without any trailing file extension. - /// - /// builder.basenameWithoutExtension('path/to/foo.dart'); // -> 'foo' - /// - /// Trailing separators are ignored. - /// - /// builder.basenameWithoutExtension('path/to/foo.dart/'); // -> 'foo' - String basenameWithoutExtension(String path) => - _parse(path).basenameWithoutExtension; - - /// Gets the part of [path] before the last separator. - /// - /// builder.dirname('path/to/foo.dart'); // -> 'path/to' - /// builder.dirname('path/to'); // -> 'path' - /// - /// Trailing separators are ignored. - /// - /// builder.dirname('path/to/'); // -> 'path' - String dirname(String path) { - var parsed = _parse(path); - parsed.removeTrailingSeparators(); - if (parsed.parts.isEmpty) return parsed.root == null ? '.' : parsed.root; - if (parsed.parts.length == 1) { - return parsed.root == null ? '.' : parsed.root; - } - parsed.parts.removeLast(); - parsed.separators.removeLast(); - parsed.removeTrailingSeparators(); - return parsed.toString(); - } - - /// Gets the file extension of [path]: the portion of [basename] from the last - /// `.` to the end (including the `.` itself). - /// - /// builder.extension('path/to/foo.dart'); // -> '.dart' - /// builder.extension('path/to/foo'); // -> '' - /// builder.extension('path.to/foo'); // -> '' - /// builder.extension('path/to/foo.dart.js'); // -> '.js' - /// - /// If the file name starts with a `.`, then it is not considered an - /// extension: - /// - /// builder.extension('~/.bashrc'); // -> '' - /// builder.extension('~/.notes.txt'); // -> '.txt' - String extension(String path) => _parse(path).extension; - - // TODO(nweiz): add a UNC example for Windows once issue 7323 is fixed. - /// Returns the root of [path], if it's absolute, or an empty string if it's - /// relative. - /// - /// // Unix - /// builder.rootPrefix('path/to/foo'); // -> '' - /// builder.rootPrefix('/path/to/foo'); // -> '/' - /// - /// // Windows - /// builder.rootPrefix(r'path\to\foo'); // -> '' - /// builder.rootPrefix(r'C:\path\to\foo'); // -> r'C:\' - /// - /// // URL - /// builder.rootPrefix('path/to/foo'); // -> '' - /// builder.rootPrefix('http://dartlang.org/path/to/foo'); - /// // -> 'http://dartlang.org' - String rootPrefix(String path) { - var root = _parse(path).root; - return root == null ? '' : root; - } - - /// Returns `true` if [path] is an absolute path and `false` if it is a - /// relative path. - /// - /// On POSIX systems, absolute paths start with a `/` (forward slash). On - /// Windows, an absolute path starts with `\\`, or a drive letter followed by - /// `:/` or `:\`. For URLs, absolute paths either start with a protocol and - /// optional hostname (e.g. `http://dartlang.org`, `file://`) or with a `/`. - /// - /// URLs that start with `/` are known as "root-relative", since they're - /// relative to the root of the current URL. Since root-relative paths are - /// still absolute in every other sense, [isAbsolute] will return true for - /// them. They can be detected using [isRootRelative]. - bool isAbsolute(String path) => _parse(path).isAbsolute; - - /// Returns `true` if [path] is a relative path and `false` if it is absolute. - /// On POSIX systems, absolute paths start with a `/` (forward slash). On - /// Windows, an absolute path starts with `\\`, or a drive letter followed by - /// `:/` or `:\`. - bool isRelative(String path) => !this.isAbsolute(path); - - /// Returns `true` if [path] is a root-relative path and `false` if it's not. - /// - /// URLs that start with `/` are known as "root-relative", since they're - /// relative to the root of the current URL. Since root-relative paths are - /// still absolute in every other sense, [isAbsolute] will return true for - /// them. They can be detected using [isRootRelative]. - /// - /// No POSIX and Windows paths are root-relative. - bool isRootRelative(String path) => _parse(path).isRootRelative; - - /// Joins the given path parts into a single path. Example: - /// - /// builder.join('path', 'to', 'foo'); // -> 'path/to/foo' - /// - /// If any part ends in a path separator, then a redundant separator will not - /// be added: - /// - /// builder.join('path/', 'to', 'foo'); // -> 'path/to/foo - /// - /// If a part is an absolute path, then anything before that will be ignored: - /// - /// builder.join('path', '/to', 'foo'); // -> '/to/foo' - /// - String join(String part1, [String part2, String part3, String part4, - String part5, String part6, String part7, String part8]) { - var parts = [part1, part2, part3, part4, part5, part6, part7, part8]; - _validateArgList("join", parts); - return joinAll(parts.where((part) => part != null)); - } - - /// Joins the given path parts into a single path. Example: - /// - /// builder.joinAll(['path', 'to', 'foo']); // -> 'path/to/foo' - /// - /// If any part ends in a path separator, then a redundant separator will not - /// be added: - /// - /// builder.joinAll(['path/', 'to', 'foo']); // -> 'path/to/foo - /// - /// If a part is an absolute path, then anything before that will be ignored: - /// - /// builder.joinAll(['path', '/to', 'foo']); // -> '/to/foo' - /// - /// For a fixed number of parts, [join] is usually terser. - String joinAll(Iterable parts) { - var buffer = new StringBuffer(); - var needsSeparator = false; - var isAbsoluteAndNotRootRelative = false; - - for (var part in parts.where((part) => part != '')) { - if (this.isRootRelative(part) && isAbsoluteAndNotRootRelative) { - // If the new part is root-relative, it preserves the previous root but - // replaces the path after it. - var parsed = _parse(part); - parsed.root = this.rootPrefix(buffer.toString()); - if (parsed.root.contains(style.needsSeparatorPattern)) { - parsed.separators[0] = style.separator; - } - buffer.clear(); - buffer.write(parsed); - } else if (this.isAbsolute(part)) { - isAbsoluteAndNotRootRelative = !this.isRootRelative(part); - // An absolute path discards everything before it. - buffer.clear(); - buffer.write(part); - } else { - if (part.length > 0 && part[0].contains(style.separatorPattern)) { - // The part starts with a separator, so we don't need to add one. - } else if (needsSeparator) { - buffer.write(separator); - } - - buffer.write(part); - } - - // Unless this part ends with a separator, we'll need to add one before - // the next part. - needsSeparator = part.contains(style.needsSeparatorPattern); - } - - return buffer.toString(); - } - - // TODO(nweiz): add a UNC example for Windows once issue 7323 is fixed. - /// Splits [path] into its components using the current platform's - /// [separator]. Example: - /// - /// builder.split('path/to/foo'); // -> ['path', 'to', 'foo'] - /// - /// The path will *not* be normalized before splitting. - /// - /// builder.split('path/../foo'); // -> ['path', '..', 'foo'] - /// - /// If [path] is absolute, the root directory will be the first element in the - /// array. Example: - /// - /// // Unix - /// builder.split('/path/to/foo'); // -> ['/', 'path', 'to', 'foo'] - /// - /// // Windows - /// builder.split(r'C:\path\to\foo'); // -> [r'C:\', 'path', 'to', 'foo'] - List split(String path) { - var parsed = _parse(path); - // Filter out empty parts that exist due to multiple separators in a row. - parsed.parts = parsed.parts.where((part) => !part.isEmpty) - .toList(); - if (parsed.root != null) parsed.parts.insert(0, parsed.root); - return parsed.parts; - } - - /// Normalizes [path], simplifying it by handling `..`, and `.`, and - /// removing redundant path separators whenever possible. - /// - /// builder.normalize('path/./to/..//file.text'); // -> 'path/file.txt' - String normalize(String path) { - var parsed = _parse(path); - parsed.normalize(); - return parsed.toString(); - } - - /// Creates a new path by appending the given path parts to the [root]. - /// Equivalent to [join()] with [root] as the first argument. Example: - /// - /// var builder = new Builder(root: 'root'); - /// builder.resolve('path', 'to', 'foo'); // -> 'root/path/to/foo' - String resolve(String part1, [String part2, String part3, String part4, - String part5, String part6, String part7]) { - return join(root, part1, part2, part3, part4, part5, part6, part7); - } - - /// Attempts to convert [path] to an equivalent relative path relative to - /// [root]. - /// - /// var builder = new Builder(root: '/root/path'); - /// builder.relative('/root/path/a/b.dart'); // -> 'a/b.dart' - /// builder.relative('/root/other.dart'); // -> '../other.dart' - /// - /// If the [from] argument is passed, [path] is made relative to that instead. - /// - /// builder.relative('/root/path/a/b.dart', - /// from: '/root/path'); // -> 'a/b.dart' - /// builder.relative('/root/other.dart', - /// from: '/root/path'); // -> '../other.dart' - /// - /// If [path] and/or [from] are relative paths, they are assumed to be - /// relative to [root]. - /// - /// Since there is no relative path from one drive letter to another on - /// Windows, this will return an absolute path in that case. - /// - /// builder.relative(r'D:\other', from: r'C:\other'); // -> 'D:\other' - /// - /// This will also return an absolute path if an absolute [path] is passed to - /// a builder with a relative [root]. - /// - /// var builder = new Builder(r'some/relative/path'); - /// builder.relative(r'/absolute/path'); // -> '/absolute/path' - /// - /// If [root] is relative, it may be impossible to determine a path from - /// [from] to [path]. For example, if [root] and [path] are "." and [from] is - /// "/", no path can be determined. In this case, a [PathException] will be - /// thrown. - String relative(String path, {String from}) { - from = from == null ? root : this.join(root, from); - - // We can't determine the path from a relative path to an absolute path. - if (this.isRelative(from) && this.isAbsolute(path)) { - return this.normalize(path); - } - - // If the given path is relative, resolve it relative to the root of the - // builder. - if (this.isRelative(path) || this.isRootRelative(path)) { - path = this.resolve(path); - } - - // If the path is still relative and `from` is absolute, we're unable to - // find a path from `from` to `path`. - if (this.isRelative(path) && this.isAbsolute(from)) { - throw new PathException('Unable to find a path to "$path" from "$from".'); - } - - var fromParsed = _parse(from)..normalize(); - var pathParsed = _parse(path)..normalize(); - - if (fromParsed.parts.length > 0 && fromParsed.parts[0] == '.') { - return pathParsed.toString(); - } - - // If the root prefixes don't match (for example, different drive letters - // on Windows), then there is no relative path, so just return the absolute - // one. In Windows, drive letters are case-insenstive and we allow - // calculation of relative paths, even if a path has not been normalized. - if (fromParsed.root != pathParsed.root && - ((fromParsed.root == null || pathParsed.root == null) || - fromParsed.root.toLowerCase().replaceAll('/', '\\') != - pathParsed.root.toLowerCase().replaceAll('/', '\\'))) { - return pathParsed.toString(); - } - - // Strip off their common prefix. - while (fromParsed.parts.length > 0 && pathParsed.parts.length > 0 && - fromParsed.parts[0] == pathParsed.parts[0]) { - fromParsed.parts.removeAt(0); - fromParsed.separators.removeAt(1); - pathParsed.parts.removeAt(0); - pathParsed.separators.removeAt(1); - } - - // If there are any directories left in the from path, we need to walk up - // out of them. If a directory left in the from path is '..', it cannot - // be cancelled by adding a '..'. - if (fromParsed.parts.length > 0 && fromParsed.parts[0] == '..') { - throw new PathException('Unable to find a path to "$path" from "$from".'); - } - _growListFront(pathParsed.parts, fromParsed.parts.length, '..'); - pathParsed.separators[0] = ''; - pathParsed.separators.insertAll(1, - new List.filled(fromParsed.parts.length, style.separator)); - - // Corner case: the paths completely collapsed. - if (pathParsed.parts.length == 0) return '.'; - - // Corner case: path was '.' and some '..' directories were added in front. - // Don't add a final '/.' in that case. - if (pathParsed.parts.length > 1 && pathParsed.parts.last == '.') { - pathParsed.parts.removeLast(); - pathParsed.separators..removeLast()..removeLast()..add(''); - } - - // Make it relative. - pathParsed.root = ''; - pathParsed.removeTrailingSeparators(); - - return pathParsed.toString(); - } - - /// Returns `true` if [child] is a path beneath `parent`, and `false` - /// otherwise. - /// - /// path.isWithin('/root/path', '/root/path/a'); // -> true - /// path.isWithin('/root/path', '/root/other'); // -> false - /// path.isWithin('/root/path', '/root/path') // -> false - bool isWithin(String parent, String child) { - var relative; - try { - relative = this.relative(child, from: parent); - } on PathException catch (_) { - // If no relative path from [parent] to [child] is found, [child] - // definitely isn't a child of [parent]. - return false; - } - - var parts = this.split(relative); - return this.isRelative(relative) && parts.first != '..' && - parts.first != '.'; - } - - /// Removes a trailing extension from the last part of [path]. - /// - /// builder.withoutExtension('path/to/foo.dart'); // -> 'path/to/foo' - String withoutExtension(String path) { - var parsed = _parse(path); - - for (var i = parsed.parts.length - 1; i >= 0; i--) { - if (!parsed.parts[i].isEmpty) { - parsed.parts[i] = parsed.basenameWithoutExtension; - break; - } - } - - return parsed.toString(); - } - - /// Returns the path represented by [uri]. - /// - /// For POSIX and Windows styles, [uri] must be a `file:` URI. For the URL - /// style, this will just convert [uri] to a string. - /// - /// // POSIX - /// builder.fromUri(Uri.parse('file:///path/to/foo')) - /// // -> '/path/to/foo' - /// - /// // Windows - /// builder.fromUri(Uri.parse('file:///C:/path/to/foo')) - /// // -> r'C:\path\to\foo' - /// - /// // URL - /// builder.fromUri(Uri.parse('http://dartlang.org/path/to/foo')) - /// // -> 'http://dartlang.org/path/to/foo' - String fromUri(Uri uri) => style.pathFromUri(uri); - - /// Returns the URI that represents [path]. - /// - /// For POSIX and Windows styles, this will return a `file:` URI. For the URL - /// style, this will just convert [path] to a [Uri]. - /// - /// // POSIX - /// builder.toUri('/path/to/foo') - /// // -> Uri.parse('file:///path/to/foo') - /// - /// // Windows - /// builder.toUri(r'C:\path\to\foo') - /// // -> Uri.parse('file:///C:/path/to/foo') - /// - /// // URL - /// builder.toUri('http://dartlang.org/path/to/foo') - /// // -> Uri.parse('http://dartlang.org/path/to/foo') - Uri toUri(String path) { - if (isRelative(path)) { - return style.relativePathToUri(path); - } else { - return style.absolutePathToUri(join(root, path)); - } - } - - _ParsedPath _parse(String path) { - var before = path; - - // Remove the root prefix, if any. - var root = style.getRoot(path); - var isRootRelative = style.getRelativeRoot(path) != null; - if (root != null) path = path.substring(root.length); - - // Split the parts on path separators. - var parts = []; - var separators = []; - - var firstSeparator = style.separatorPattern.matchAsPrefix(path); - if (firstSeparator != null) { - separators.add(firstSeparator[0]); - path = path.substring(firstSeparator[0].length); - } else { - separators.add(''); - } - - var start = 0; - for (var match in style.separatorPattern.allMatches(path)) { - parts.add(path.substring(start, match.start)); - separators.add(match[0]); - start = match.end; - } - - // Add the final part, if any. - if (start < path.length) { - parts.add(path.substring(start)); - separators.add(''); - } - - return new _ParsedPath(style, root, isRootRelative, parts, separators); - } -} - -/// An enum type describing a "flavor" of path. -abstract class Style { - /// POSIX-style paths use "/" (forward slash) as separators. Absolute paths - /// start with "/". Used by UNIX, Linux, Mac OS X, and others. - static final posix = new _PosixStyle(); - - /// Windows paths use "\" (backslash) as separators. Absolute paths start with - /// a drive letter followed by a colon (example, "C:") or two backslashes - /// ("\\") for UNC paths. - // TODO(rnystrom): The UNC root prefix should include the drive name too, not - // just the "\\". - static final windows = new _WindowsStyle(); - - /// URLs aren't filesystem paths, but they're supported to make it easier to - /// manipulate URL paths in the browser. - /// - /// URLs use "/" (forward slash) as separators. Absolute paths either start - /// with a protocol and optional hostname (e.g. `http://dartlang.org`, - /// `file://`) or with "/". - static final url = new _UrlStyle(); - - /// The style of the host platform. - /// - /// When running on the command line, this will be [windows] or [posix] based - /// on the host operating system. On a browser, this will be [url]. - static final platform = _getPlatformStyle(); - - /// Gets the type of the host platform. - static Style _getPlatformStyle() { - // If we're running a Dart file in the browser from a `file:` URI, - // [Uri.base] will point to a file. If we're running on the standalone, - // it will point to a directory. We can use that fact to determine which - // style to use. - if (Uri.base.scheme != 'file') return Style.url; - if (!Uri.base.path.endsWith('/')) return Style.url; - if (new Uri(path: 'a/b').toFilePath() == 'a\\b') return Style.windows; - return Style.posix; - } - - /// The name of this path style. Will be "posix" or "windows". - String get name; - - /// The path separator for this style. On POSIX, this is `/`. On Windows, - /// it's `\`. - String get separator; - - /// The [Pattern] that can be used to match a separator for a path in this - /// style. Windows allows both "/" and "\" as path separators even though "\" - /// is the canonical one. - Pattern get separatorPattern; - - /// The [Pattern] that matches path components that need a separator after - /// them. - /// - /// Windows and POSIX styles just need separators when the previous component - /// doesn't already end in a separator, but the URL always needs to place a - /// separator between the root and the first component, even if the root - /// already ends in a separator character. For example, to join "file://" and - /// "usr", an additional "/" is needed (making "file:///usr"). - Pattern get needsSeparatorPattern; - - /// The [Pattern] that can be used to match the root prefix of an absolute - /// path in this style. - Pattern get rootPattern; - - /// The [Pattern] that can be used to match the root prefix of a root-relative - /// path in this style. - /// - /// This can be null to indicate that this style doesn't support root-relative - /// paths. - final Pattern relativeRootPattern = null; - - /// A [Builder] that uses this style. - Builder get builder => new Builder(style: this); - - /// Gets the root prefix of [path] if path is absolute. If [path] is relative, - /// returns `null`. - String getRoot(String path) { - // TODO(rnystrom): Use firstMatch() when #7080 is fixed. - var matches = rootPattern.allMatches(path); - if (matches.isNotEmpty) return matches.first[0]; - return getRelativeRoot(path); - } - - /// Gets the root prefix of [path] if it's root-relative. - /// - /// If [path] is relative or absolute and not root-relative, returns `null`. - String getRelativeRoot(String path) { - if (relativeRootPattern == null) return null; - // TODO(rnystrom): Use firstMatch() when #7080 is fixed. - var matches = relativeRootPattern.allMatches(path); - if (matches.isEmpty) return null; - return matches.first[0]; - } - - /// Returns the path represented by [uri] in this style. - String pathFromUri(Uri uri); - - /// Returns the URI that represents the relative path made of [parts]. - Uri relativePathToUri(String path) => - new Uri(pathSegments: builder.split(path)); - - /// Returns the URI that represents [path], which is assumed to be absolute. - Uri absolutePathToUri(String path); - - String toString() => name; -} - -/// The style for POSIX paths. -class _PosixStyle extends Style { - _PosixStyle(); - - final name = 'posix'; - final separator = '/'; - final separatorPattern = new RegExp(r'/'); - final needsSeparatorPattern = new RegExp(r'[^/]$'); - final rootPattern = new RegExp(r'^/'); - - String pathFromUri(Uri uri) { - if (uri.scheme == '' || uri.scheme == 'file') { - return Uri.decodeComponent(uri.path); - } - throw new ArgumentError("Uri $uri must have scheme 'file:'."); - } - - Uri absolutePathToUri(String path) { - var parsed = builder._parse(path); - if (parsed.parts.isEmpty) { - // If the path is a bare root (e.g. "/"), [components] will - // currently be empty. We add two empty components so the URL constructor - // produces "file:///", with a trailing slash. - parsed.parts.addAll(["", ""]); - } else if (parsed.hasTrailingSeparator) { - // If the path has a trailing slash, add a single empty component so the - // URI has a trailing slash as well. - parsed.parts.add(""); - } - - return new Uri(scheme: 'file', pathSegments: parsed.parts); - } -} - -/// The style for Windows paths. -class _WindowsStyle extends Style { - _WindowsStyle(); - - final name = 'windows'; - final separator = '\\'; - final separatorPattern = new RegExp(r'[/\\]'); - final needsSeparatorPattern = new RegExp(r'[^/\\]$'); - final rootPattern = new RegExp(r'^(\\\\[^\\]+\\[^\\/]+|[a-zA-Z]:[/\\])'); - - // Matches a back or forward slash that's not followed by another back or - // forward slash. - final relativeRootPattern = new RegExp(r"^[/\\](?![/\\])"); - - String pathFromUri(Uri uri) { - if (uri.scheme != '' && uri.scheme != 'file') { - throw new ArgumentError("Uri $uri must have scheme 'file:'."); - } - - var path = uri.path; - if (uri.host == '') { - // Drive-letter paths look like "file:///C:/path/to/file". The - // replaceFirst removes the extra initial slash. - if (path.startsWith('/')) path = path.replaceFirst("/", ""); - } else { - // Network paths look like "file://hostname/path/to/file". - path = '\\\\${uri.host}$path'; - } - return Uri.decodeComponent(path.replaceAll("/", "\\")); - } - - Uri absolutePathToUri(String path) { - var parsed = builder._parse(path); - if (parsed.root.startsWith(r'\\')) { - // Network paths become "file://server/share/path/to/file". - - // The root is of the form "\\server\share". We want "server" to be the - // URI host, and "share" to be the first element of the path. - var rootParts = parsed.root.split('\\').where((part) => part != ''); - parsed.parts.insert(0, rootParts.last); - - if (parsed.hasTrailingSeparator) { - // If the path has a trailing slash, add a single empty component so the - // URI has a trailing slash as well. - parsed.parts.add(""); - } - - return new Uri(scheme: 'file', host: rootParts.first, - pathSegments: parsed.parts); - } else { - // Drive-letter paths become "file:///C:/path/to/file". - - // If the path is a bare root (e.g. "C:\"), [parsed.parts] will currently - // be empty. We add an empty component so the URL constructor produces - // "file:///C:/", with a trailing slash. We also add an empty component if - // the URL otherwise has a trailing slash. - if (parsed.parts.length == 0 || parsed.hasTrailingSeparator) { - parsed.parts.add(""); - } - - // Get rid of the trailing "\" in "C:\" because the URI constructor will - // add a separator on its own. - parsed.parts.insert(0, parsed.root.replaceAll(separatorPattern, "")); - - return new Uri(scheme: 'file', pathSegments: parsed.parts); - } - } -} - -/// The style for URL paths. -class _UrlStyle extends Style { - _UrlStyle(); - - final name = 'url'; - final separator = '/'; - final separatorPattern = new RegExp(r'/'); - final needsSeparatorPattern = new RegExp( - r"(^[a-zA-Z][-+.a-zA-Z\d]*://|[^/])$"); - final rootPattern = new RegExp(r"[a-zA-Z][-+.a-zA-Z\d]*://[^/]*"); - final relativeRootPattern = new RegExp(r"^/"); - - String pathFromUri(Uri uri) => uri.toString(); - - Uri relativePathToUri(String path) => Uri.parse(path); - Uri absolutePathToUri(String path) => Uri.parse(path); -} - -// TODO(rnystrom): Make this public? -class _ParsedPath { - /// The [Style] that was used to parse this path. - Style style; - - /// The absolute root portion of the path, or `null` if the path is relative. - /// On POSIX systems, this will be `null` or "/". On Windows, it can be - /// `null`, "//" for a UNC path, or something like "C:\" for paths with drive - /// letters. - String root; - - /// Whether this path is root-relative. - /// - /// See [Builder.isRootRelative]. - bool isRootRelative; - - /// The path-separated parts of the path. All but the last will be - /// directories. - List parts; - - /// The path separators preceding each part. - /// - /// The first one will be an empty string unless the root requires a separator - /// between it and the path. The last one will be an empty string unless the - /// path ends with a trailing separator. - List separators; - - /// The file extension of the last non-empty part, or "" if it doesn't have - /// one. - String get extension => _splitExtension()[1]; - - /// `true` if this is an absolute path. - bool get isAbsolute => root != null; - - _ParsedPath(this.style, this.root, this.isRootRelative, this.parts, - this.separators); - - String get basename { - var copy = this.clone(); - copy.removeTrailingSeparators(); - if (copy.parts.isEmpty) return root == null ? '' : root; - return copy.parts.last; - } - - String get basenameWithoutExtension => _splitExtension()[0]; - - bool get hasTrailingSeparator => - !parts.isEmpty && (parts.last == '' || separators.last != ''); - - void removeTrailingSeparators() { - while (!parts.isEmpty && parts.last == '') { - parts.removeLast(); - separators.removeLast(); - } - if (separators.length > 0) separators[separators.length - 1] = ''; - } - - void normalize() { - // Handle '.', '..', and empty parts. - var leadingDoubles = 0; - var newParts = []; - for (var part in parts) { - if (part == '.' || part == '') { - // Do nothing. Ignore it. - } else if (part == '..') { - // Pop the last part off. - if (newParts.length > 0) { - newParts.removeLast(); - } else { - // Backed out past the beginning, so preserve the "..". - leadingDoubles++; - } - } else { - newParts.add(part); - } - } - - // A relative path can back out from the start directory. - if (!isAbsolute) { - _growListFront(newParts, leadingDoubles, '..'); - } - - // If we collapsed down to nothing, do ".". - if (newParts.length == 0 && !isAbsolute) { - newParts.add('.'); - } - - // Canonicalize separators. - var newSeparators = new List.generate( - newParts.length, (_) => style.separator, growable: true); - newSeparators.insert(0, - isAbsolute && newParts.length > 0 && - root.contains(style.needsSeparatorPattern) ? - style.separator : ''); - - parts = newParts; - separators = newSeparators; - - // Normalize the Windows root if needed. - if (root != null && style == Style.windows) { - root = root.replaceAll('/', '\\'); - } - removeTrailingSeparators(); - } - - String toString() { - var builder = new StringBuffer(); - if (root != null) builder.write(root); - for (var i = 0; i < parts.length; i++) { - builder.write(separators[i]); - builder.write(parts[i]); - } - builder.write(separators.last); - - return builder.toString(); - } - - /// Splits the last non-empty part of the path into a `[basename, extension`] - /// pair. - /// - /// Returns a two-element list. The first is the name of the file without any - /// extension. The second is the extension or "" if it has none. - List _splitExtension() { - var file = parts.lastWhere((p) => p != '', orElse: () => null); - - if (file == null) return ['', '']; - if (file == '..') return ['..', '']; - - var lastDot = file.lastIndexOf('.'); - - // If there is no dot, or it's the first character, like '.bashrc', it - // doesn't count. - if (lastDot <= 0) return [file, '']; - - return [file.substring(0, lastDot), file.substring(lastDot)]; - } - - _ParsedPath clone() => new _ParsedPath( - style, root, isRootRelative, - new List.from(parts), new List.from(separators)); -} - -/// An exception class that's thrown when a path operation is unable to be -/// computed accurately. -class PathException implements Exception { - String message; - - PathException(this.message); - - String toString() => "PathException: $message"; -} +Uri toUri(String path) => _context.toUri(path); diff --git a/pkg/path/lib/src/context.dart b/pkg/path/lib/src/context.dart new file mode 100644 index 00000000000..b897c8ba533 --- /dev/null +++ b/pkg/path/lib/src/context.dart @@ -0,0 +1,492 @@ +// 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 path.context; + +import 'style.dart'; +import 'parsed_path.dart'; +import 'path_exception.dart'; +import '../path.dart' as p; + +/// An instantiable class for manipulating paths. Unlike the top-level +/// functions, this lets you explicitly select what platform the paths will use. +class Context { + /// Creates a new path context for the given style and current directory. + /// + /// If [style] is omitted, it uses the host operating system's path style. If + /// only [current] is omitted, it defaults ".". If *both* [style] and + /// [current] are omitted, [current] defaults to the real current working + /// directory. + /// + /// On the browser, [style] defaults to [Style.url] and [current] defaults to + /// the current URL. + factory Context({Style style, String current}) { + if (current == null) { + if (style == null) { + current = p.current; + } else { + current = "."; + } + } + + if (style == null) style = Style.platform; + + return new Context._(style, current); + } + + Context._(this.style, this.current); + + /// The style of path that this context works with. + final Style style; + + /// The current directory that relative paths will be relative to. + final String current; + + /// Gets the path separator for the context's [style]. On Mac and Linux, + /// this is `/`. On Windows, it's `\`. + String get separator => style.separator; + + /// Creates a new path by appending the given path parts to [current]. + /// Equivalent to [join()] with [current] as the first argument. Example: + /// + /// var context = new Context(current: '/root'); + /// context.absolute('path', 'to', 'foo'); // -> '/root/path/to/foo' + /// + /// If [current] isn't absolute, this won't return an absolute path. + String absolute(String part1, [String part2, String part3, String part4, + String part5, String part6, String part7]) { + return join(current, part1, part2, part3, part4, part5, part6, part7); + } + + /// Gets the part of [path] after the last separator on the context's + /// platform. + /// + /// context.basename('path/to/foo.dart'); // -> 'foo.dart' + /// context.basename('path/to'); // -> 'to' + /// + /// Trailing separators are ignored. + /// + /// context.basename('path/to/'); // -> 'to' + String basename(String path) => _parse(path).basename; + + /// Gets the part of [path] after the last separator on the context's + /// platform, and without any trailing file extension. + /// + /// context.basenameWithoutExtension('path/to/foo.dart'); // -> 'foo' + /// + /// Trailing separators are ignored. + /// + /// context.basenameWithoutExtension('path/to/foo.dart/'); // -> 'foo' + String basenameWithoutExtension(String path) => + _parse(path).basenameWithoutExtension; + + /// Gets the part of [path] before the last separator. + /// + /// context.dirname('path/to/foo.dart'); // -> 'path/to' + /// context.dirname('path/to'); // -> 'path' + /// + /// Trailing separators are ignored. + /// + /// context.dirname('path/to/'); // -> 'path' + String dirname(String path) { + var parsed = _parse(path); + parsed.removeTrailingSeparators(); + if (parsed.parts.isEmpty) return parsed.root == null ? '.' : parsed.root; + if (parsed.parts.length == 1) { + return parsed.root == null ? '.' : parsed.root; + } + parsed.parts.removeLast(); + parsed.separators.removeLast(); + parsed.removeTrailingSeparators(); + return parsed.toString(); + } + + /// Gets the file extension of [path]: the portion of [basename] from the last + /// `.` to the end (including the `.` itself). + /// + /// context.extension('path/to/foo.dart'); // -> '.dart' + /// context.extension('path/to/foo'); // -> '' + /// context.extension('path.to/foo'); // -> '' + /// context.extension('path/to/foo.dart.js'); // -> '.js' + /// + /// If the file name starts with a `.`, then it is not considered an + /// extension: + /// + /// context.extension('~/.bashrc'); // -> '' + /// context.extension('~/.notes.txt'); // -> '.txt' + String extension(String path) => _parse(path).extension; + + // TODO(nweiz): add a UNC example for Windows once issue 7323 is fixed. + /// Returns the root of [path] if it's absolute, or an empty string if it's + /// relative. + /// + /// // Unix + /// context.rootPrefix('path/to/foo'); // -> '' + /// context.rootPrefix('/path/to/foo'); // -> '/' + /// + /// // Windows + /// context.rootPrefix(r'path\to\foo'); // -> '' + /// context.rootPrefix(r'C:\path\to\foo'); // -> r'C:\' + /// + /// // URL + /// context.rootPrefix('path/to/foo'); // -> '' + /// context.rootPrefix('http://dartlang.org/path/to/foo'); + /// // -> 'http://dartlang.org' + String rootPrefix(String path) { + var root = _parse(path).root; + return root == null ? '' : root; + } + + /// Returns `true` if [path] is an absolute path and `false` if it is a + /// relative path. + /// + /// On POSIX systems, absolute paths start with a `/` (forward slash). On + /// Windows, an absolute path starts with `\\`, or a drive letter followed by + /// `:/` or `:\`. For URLs, absolute paths either start with a protocol and + /// optional hostname (e.g. `http://dartlang.org`, `file://`) or with a `/`. + /// + /// URLs that start with `/` are known as "root-relative", since they're + /// relative to the root of the current URL. Since root-relative paths are + /// still absolute in every other sense, [isAbsolute] will return true for + /// them. They can be detected using [isRootRelative]. + bool isAbsolute(String path) => _parse(path).isAbsolute; + + /// Returns `true` if [path] is a relative path and `false` if it is absolute. + /// On POSIX systems, absolute paths start with a `/` (forward slash). On + /// Windows, an absolute path starts with `\\`, or a drive letter followed by + /// `:/` or `:\`. + bool isRelative(String path) => !this.isAbsolute(path); + + /// Returns `true` if [path] is a root-relative path and `false` if it's not. + /// + /// URLs that start with `/` are known as "root-relative", since they're + /// relative to the root of the current URL. Since root-relative paths are + /// still absolute in every other sense, [isAbsolute] will return true for + /// them. They can be detected using [isRootRelative]. + /// + /// No POSIX and Windows paths are root-relative. + bool isRootRelative(String path) => _parse(path).isRootRelative; + + /// Joins the given path parts into a single path. Example: + /// + /// context.join('path', 'to', 'foo'); // -> 'path/to/foo' + /// + /// If any part ends in a path separator, then a redundant separator will not + /// be added: + /// + /// context.join('path/', 'to', 'foo'); // -> 'path/to/foo + /// + /// If a part is an absolute path, then anything before that will be ignored: + /// + /// context.join('path', '/to', 'foo'); // -> '/to/foo' + /// + String join(String part1, [String part2, String part3, String part4, + String part5, String part6, String part7, String part8]) { + var parts = [part1, part2, part3, part4, part5, part6, part7, part8]; + _validateArgList("join", parts); + return joinAll(parts.where((part) => part != null)); + } + + /// Joins the given path parts into a single path. Example: + /// + /// context.joinAll(['path', 'to', 'foo']); // -> 'path/to/foo' + /// + /// If any part ends in a path separator, then a redundant separator will not + /// be added: + /// + /// context.joinAll(['path/', 'to', 'foo']); // -> 'path/to/foo + /// + /// If a part is an absolute path, then anything before that will be ignored: + /// + /// context.joinAll(['path', '/to', 'foo']); // -> '/to/foo' + /// + /// For a fixed number of parts, [join] is usually terser. + String joinAll(Iterable parts) { + var buffer = new StringBuffer(); + var needsSeparator = false; + var isAbsoluteAndNotRootRelative = false; + + for (var part in parts.where((part) => part != '')) { + if (this.isRootRelative(part) && isAbsoluteAndNotRootRelative) { + // If the new part is root-relative, it preserves the previous root but + // replaces the path after it. + var parsed = _parse(part); + parsed.root = this.rootPrefix(buffer.toString()); + if (parsed.root.contains(style.needsSeparatorPattern)) { + parsed.separators[0] = style.separator; + } + buffer.clear(); + buffer.write(parsed.toString()); + } else if (this.isAbsolute(part)) { + isAbsoluteAndNotRootRelative = !this.isRootRelative(part); + // An absolute path discards everything before it. + buffer.clear(); + buffer.write(part); + } else { + if (part.length > 0 && part[0].contains(style.separatorPattern)) { + // The part starts with a separator, so we don't need to add one. + } else if (needsSeparator) { + buffer.write(separator); + } + + buffer.write(part); + } + + // Unless this part ends with a separator, we'll need to add one before + // the next part. + needsSeparator = part.contains(style.needsSeparatorPattern); + } + + return buffer.toString(); + } + + // TODO(nweiz): add a UNC example for Windows once issue 7323 is fixed. + /// Splits [path] into its components using the current platform's + /// [separator]. Example: + /// + /// context.split('path/to/foo'); // -> ['path', 'to', 'foo'] + /// + /// The path will *not* be normalized before splitting. + /// + /// context.split('path/../foo'); // -> ['path', '..', 'foo'] + /// + /// If [path] is absolute, the root directory will be the first element in the + /// array. Example: + /// + /// // Unix + /// context.split('/path/to/foo'); // -> ['/', 'path', 'to', 'foo'] + /// + /// // Windows + /// context.split(r'C:\path\to\foo'); // -> [r'C:\', 'path', 'to', 'foo'] + List split(String path) { + var parsed = _parse(path); + // Filter out empty parts that exist due to multiple separators in a row. + parsed.parts = parsed.parts.where((part) => !part.isEmpty) + .toList(); + if (parsed.root != null) parsed.parts.insert(0, parsed.root); + return parsed.parts; + } + + /// Normalizes [path], simplifying it by handling `..`, and `.`, and + /// removing redundant path separators whenever possible. + /// + /// context.normalize('path/./to/..//file.text'); // -> 'path/file.txt' + String normalize(String path) { + var parsed = _parse(path); + parsed.normalize(); + return parsed.toString(); + } + + /// Attempts to convert [path] to an equivalent relative path relative to + /// [root]. + /// + /// var context = new Context(current: '/root/path'); + /// context.relative('/root/path/a/b.dart'); // -> 'a/b.dart' + /// context.relative('/root/other.dart'); // -> '../other.dart' + /// + /// If the [from] argument is passed, [path] is made relative to that instead. + /// + /// context.relative('/root/path/a/b.dart', + /// from: '/root/path'); // -> 'a/b.dart' + /// context.relative('/root/other.dart', + /// from: '/root/path'); // -> '../other.dart' + /// + /// If [path] and/or [from] are relative paths, they are assumed to be + /// relative to [current]. + /// + /// Since there is no relative path from one drive letter to another on + /// Windows, this will return an absolute path in that case. + /// + /// context.relative(r'D:\other', from: r'C:\other'); // -> 'D:\other' + /// + /// This will also return an absolute path if an absolute [path] is passed to + /// a context with a relative path for [current]. + /// + /// var context = new Context(r'some/relative/path'); + /// context.relative(r'/absolute/path'); // -> '/absolute/path' + /// + /// If [root] is relative, it may be impossible to determine a path from + /// [from] to [path]. For example, if [root] and [path] are "." and [from] is + /// "/", no path can be determined. In this case, a [PathException] will be + /// thrown. + String relative(String path, {String from}) { + from = from == null ? current : this.join(current, from); + + // We can't determine the path from a relative path to an absolute path. + if (this.isRelative(from) && this.isAbsolute(path)) { + return this.normalize(path); + } + + // If the given path is relative, resolve it relative to the context's + // current directory. + if (this.isRelative(path) || this.isRootRelative(path)) { + path = this.absolute(path); + } + + // If the path is still relative and `from` is absolute, we're unable to + // find a path from `from` to `path`. + if (this.isRelative(path) && this.isAbsolute(from)) { + throw new PathException('Unable to find a path to "$path" from "$from".'); + } + + var fromParsed = _parse(from)..normalize(); + var pathParsed = _parse(path)..normalize(); + + if (fromParsed.parts.length > 0 && fromParsed.parts[0] == '.') { + return pathParsed.toString(); + } + + // If the root prefixes don't match (for example, different drive letters + // on Windows), then there is no relative path, so just return the absolute + // one. In Windows, drive letters are case-insenstive and we allow + // calculation of relative paths, even if a path has not been normalized. + if (fromParsed.root != pathParsed.root && + ((fromParsed.root == null || pathParsed.root == null) || + fromParsed.root.toLowerCase().replaceAll('/', '\\') != + pathParsed.root.toLowerCase().replaceAll('/', '\\'))) { + return pathParsed.toString(); + } + + // Strip off their common prefix. + while (fromParsed.parts.length > 0 && pathParsed.parts.length > 0 && + fromParsed.parts[0] == pathParsed.parts[0]) { + fromParsed.parts.removeAt(0); + fromParsed.separators.removeAt(1); + pathParsed.parts.removeAt(0); + pathParsed.separators.removeAt(1); + } + + // If there are any directories left in the from path, we need to walk up + // out of them. If a directory left in the from path is '..', it cannot + // be cancelled by adding a '..'. + if (fromParsed.parts.length > 0 && fromParsed.parts[0] == '..') { + throw new PathException('Unable to find a path to "$path" from "$from".'); + } + pathParsed.parts.insertAll(0, + new List.filled(fromParsed.parts.length, '..')); + pathParsed.separators[0] = ''; + pathParsed.separators.insertAll(1, + new List.filled(fromParsed.parts.length, style.separator)); + + // Corner case: the paths completely collapsed. + if (pathParsed.parts.length == 0) return '.'; + + // Corner case: path was '.' and some '..' directories were added in front. + // Don't add a final '/.' in that case. + if (pathParsed.parts.length > 1 && pathParsed.parts.last == '.') { + pathParsed.parts.removeLast(); + pathParsed.separators..removeLast()..removeLast()..add(''); + } + + // Make it relative. + pathParsed.root = ''; + pathParsed.removeTrailingSeparators(); + + return pathParsed.toString(); + } + + /// Returns `true` if [child] is a path beneath `parent`, and `false` + /// otherwise. + /// + /// path.isWithin('/root/path', '/root/path/a'); // -> true + /// path.isWithin('/root/path', '/root/other'); // -> false + /// path.isWithin('/root/path', '/root/path'); // -> false + bool isWithin(String parent, String child) { + var relative; + try { + relative = this.relative(child, from: parent); + } on PathException catch (_) { + // If no relative path from [parent] to [child] is found, [child] + // definitely isn't a child of [parent]. + return false; + } + + var parts = this.split(relative); + return this.isRelative(relative) && parts.first != '..' && + parts.first != '.'; + } + + /// Removes a trailing extension from the last part of [path]. + /// + /// context.withoutExtension('path/to/foo.dart'); // -> 'path/to/foo' + String withoutExtension(String path) { + var parsed = _parse(path); + + for (var i = parsed.parts.length - 1; i >= 0; i--) { + if (!parsed.parts[i].isEmpty) { + parsed.parts[i] = parsed.basenameWithoutExtension; + break; + } + } + + return parsed.toString(); + } + + /// Returns the path represented by [uri]. + /// + /// For POSIX and Windows styles, [uri] must be a `file:` URI. For the URL + /// style, this will just convert [uri] to a string. + /// + /// // POSIX + /// context.fromUri(Uri.parse('file:///path/to/foo')) + /// // -> '/path/to/foo' + /// + /// // Windows + /// context.fromUri(Uri.parse('file:///C:/path/to/foo')) + /// // -> r'C:\path\to\foo' + /// + /// // URL + /// context.fromUri(Uri.parse('http://dartlang.org/path/to/foo')) + /// // -> 'http://dartlang.org/path/to/foo' + String fromUri(Uri uri) => style.pathFromUri(uri); + + /// Returns the URI that represents [path]. + /// + /// For POSIX and Windows styles, this will return a `file:` URI. For the URL + /// style, this will just convert [path] to a [Uri]. + /// + /// // POSIX + /// context.toUri('/path/to/foo') + /// // -> Uri.parse('file:///path/to/foo') + /// + /// // Windows + /// context.toUri(r'C:\path\to\foo') + /// // -> Uri.parse('file:///C:/path/to/foo') + /// + /// // URL + /// context.toUri('http://dartlang.org/path/to/foo') + /// // -> Uri.parse('http://dartlang.org/path/to/foo') + Uri toUri(String path) { + if (isRelative(path)) { + return style.relativePathToUri(path); + } else { + return style.absolutePathToUri(join(current, path)); + } + } + + ParsedPath _parse(String path) => new ParsedPath.parse(path, style); +} + +/// Validates that there are no non-null arguments following a null one and +/// throws an appropriate [ArgumentError] on failure. +_validateArgList(String method, List args) { + for (var i = 1; i < args.length; i++) { + // Ignore nulls hanging off the end. + if (args[i] == null || args[i - 1] != null) continue; + + var numArgs; + for (numArgs = args.length; numArgs >= 1; numArgs--) { + if (args[numArgs - 1] != null) break; + } + + // Show the arguments. + var message = new StringBuffer(); + message.write("$method("); + message.write(args.take(numArgs) + .map((arg) => arg == null ? "null" : '"$arg"') + .join(", ")); + message.write("): part ${i - 1} was null, but part $i was not."); + throw new ArgumentError(message.toString()); + } +} diff --git a/pkg/path/lib/src/parsed_path.dart b/pkg/path/lib/src/parsed_path.dart new file mode 100644 index 00000000000..5356b44c1df --- /dev/null +++ b/pkg/path/lib/src/parsed_path.dart @@ -0,0 +1,186 @@ +// 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 path.parsed_path; + +import 'style.dart'; + +// TODO(rnystrom): Make this public? +class ParsedPath { + /// The [Style] that was used to parse this path. + Style style; + + /// The absolute root portion of the path, or `null` if the path is relative. + /// On POSIX systems, this will be `null` or "/". On Windows, it can be + /// `null`, "//" for a UNC path, or something like "C:\" for paths with drive + /// letters. + String root; + + /// Whether this path is root-relative. + /// + /// See [Context.isRootRelative]. + bool isRootRelative; + + /// The path-separated parts of the path. All but the last will be + /// directories. + List parts; + + /// The path separators preceding each part. + /// + /// The first one will be an empty string unless the root requires a separator + /// between it and the path. The last one will be an empty string unless the + /// path ends with a trailing separator. + List separators; + + /// The file extension of the last non-empty part, or "" if it doesn't have + /// one. + String get extension => _splitExtension()[1]; + + /// `true` if this is an absolute path. + bool get isAbsolute => root != null; + + factory ParsedPath.parse(String path, Style style) { + var before = path; + + // Remove the root prefix, if any. + var root = style.getRoot(path); + var isRootRelative = style.getRelativeRoot(path) != null; + if (root != null) path = path.substring(root.length); + + // Split the parts on path separators. + var parts = []; + var separators = []; + + var firstSeparator = style.separatorPattern.matchAsPrefix(path); + if (firstSeparator != null) { + separators.add(firstSeparator[0]); + path = path.substring(firstSeparator[0].length); + } else { + separators.add(''); + } + + var start = 0; + for (var match in style.separatorPattern.allMatches(path)) { + parts.add(path.substring(start, match.start)); + separators.add(match[0]); + start = match.end; + } + + // Add the final part, if any. + if (start < path.length) { + parts.add(path.substring(start)); + separators.add(''); + } + + return new ParsedPath._(style, root, isRootRelative, parts, separators); + } + + ParsedPath._(this.style, this.root, this.isRootRelative, this.parts, + this.separators); + + String get basename { + var copy = this.clone(); + copy.removeTrailingSeparators(); + if (copy.parts.isEmpty) return root == null ? '' : root; + return copy.parts.last; + } + + String get basenameWithoutExtension => _splitExtension()[0]; + + bool get hasTrailingSeparator => + !parts.isEmpty && (parts.last == '' || separators.last != ''); + + void removeTrailingSeparators() { + while (!parts.isEmpty && parts.last == '') { + parts.removeLast(); + separators.removeLast(); + } + if (separators.length > 0) separators[separators.length - 1] = ''; + } + + void normalize() { + // Handle '.', '..', and empty parts. + var leadingDoubles = 0; + var newParts = []; + for (var part in parts) { + if (part == '.' || part == '') { + // Do nothing. Ignore it. + } else if (part == '..') { + // Pop the last part off. + if (newParts.length > 0) { + newParts.removeLast(); + } else { + // Backed out past the beginning, so preserve the "..". + leadingDoubles++; + } + } else { + newParts.add(part); + } + } + + // A relative path can back out from the start directory. + if (!isAbsolute) { + newParts.insertAll(0, new List.filled(leadingDoubles, '..')); + } + + // If we collapsed down to nothing, do ".". + if (newParts.length == 0 && !isAbsolute) { + newParts.add('.'); + } + + // Canonicalize separators. + var newSeparators = new List.generate( + newParts.length, (_) => style.separator, growable: true); + newSeparators.insert(0, + isAbsolute && newParts.length > 0 && + root.contains(style.needsSeparatorPattern) ? + style.separator : ''); + + parts = newParts; + separators = newSeparators; + + // Normalize the Windows root if needed. + if (root != null && style == Style.windows) { + root = root.replaceAll('/', '\\'); + } + removeTrailingSeparators(); + } + + String toString() { + var builder = new StringBuffer(); + if (root != null) builder.write(root); + for (var i = 0; i < parts.length; i++) { + builder.write(separators[i]); + builder.write(parts[i]); + } + builder.write(separators.last); + + return builder.toString(); + } + + /// Splits the last non-empty part of the path into a `[basename, extension`] + /// pair. + /// + /// Returns a two-element list. The first is the name of the file without any + /// extension. The second is the extension or "" if it has none. + List _splitExtension() { + var file = parts.lastWhere((p) => p != '', orElse: () => null); + + if (file == null) return ['', '']; + if (file == '..') return ['..', '']; + + var lastDot = file.lastIndexOf('.'); + + // If there is no dot, or it's the first character, like '.bashrc', it + // doesn't count. + if (lastDot <= 0) return [file, '']; + + return [file.substring(0, lastDot), file.substring(lastDot)]; + } + + ParsedPath clone() => new ParsedPath._( + style, root, isRootRelative, + new List.from(parts), new List.from(separators)); +} + diff --git a/pkg/path/lib/src/path_exception.dart b/pkg/path/lib/src/path_exception.dart new file mode 100644 index 00000000000..49bd268e4d5 --- /dev/null +++ b/pkg/path/lib/src/path_exception.dart @@ -0,0 +1,15 @@ +// 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 path.path_exception; + +/// An exception class that's thrown when a path operation is unable to be +/// computed accurately. +class PathException implements Exception { + String message; + + PathException(this.message); + + String toString() => "PathException: $message"; +} diff --git a/pkg/path/lib/src/style.dart b/pkg/path/lib/src/style.dart new file mode 100644 index 00000000000..9da4b43a603 --- /dev/null +++ b/pkg/path/lib/src/style.dart @@ -0,0 +1,118 @@ +// 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 path.style; + +import 'context.dart'; +import 'style/posix.dart'; +import 'style/url.dart'; +import 'style/windows.dart'; + +/// An enum type describing a "flavor" of path. +abstract class Style { + /// POSIX-style paths use "/" (forward slash) as separators. Absolute paths + /// start with "/". Used by UNIX, Linux, Mac OS X, and others. + static final posix = new PosixStyle(); + + /// Windows paths use "\" (backslash) as separators. Absolute paths start with + /// a drive letter followed by a colon (example, "C:") or two backslashes + /// ("\\") for UNC paths. + // TODO(rnystrom): The UNC root prefix should include the drive name too, not + // just the "\\". + static final windows = new WindowsStyle(); + + /// URLs aren't filesystem paths, but they're supported to make it easier to + /// manipulate URL paths in the browser. + /// + /// URLs use "/" (forward slash) as separators. Absolute paths either start + /// with a protocol and optional hostname (e.g. `http://dartlang.org`, + /// `file://`) or with "/". + static final url = new UrlStyle(); + + /// The style of the host platform. + /// + /// When running on the command line, this will be [windows] or [posix] based + /// on the host operating system. On a browser, this will be [url]. + static final platform = _getPlatformStyle(); + + /// Gets the type of the host platform. + static Style _getPlatformStyle() { + // If we're running a Dart file in the browser from a `file:` URI, + // [Uri.base] will point to a file. If we're running on the standalone, + // it will point to a directory. We can use that fact to determine which + // style to use. + if (Uri.base.scheme != 'file') return Style.url; + if (!Uri.base.path.endsWith('/')) return Style.url; + if (new Uri(path: 'a/b').toFilePath() == 'a\\b') return Style.windows; + return Style.posix; + } + + /// The name of this path style. Will be "posix" or "windows". + String get name; + + /// The path separator for this style. On POSIX, this is `/`. On Windows, + /// it's `\`. + String get separator; + + /// The [Pattern] that can be used to match a separator for a path in this + /// style. Windows allows both "/" and "\" as path separators even though "\" + /// is the canonical one. + Pattern get separatorPattern; + + /// The [Pattern] that matches path components that need a separator after + /// them. + /// + /// Windows and POSIX styles just need separators when the previous component + /// doesn't already end in a separator, but the URL always needs to place a + /// separator between the root and the first component, even if the root + /// already ends in a separator character. For example, to join "file://" and + /// "usr", an additional "/" is needed (making "file:///usr"). + Pattern get needsSeparatorPattern; + + /// The [Pattern] that can be used to match the root prefix of an absolute + /// path in this style. + Pattern get rootPattern; + + /// The [Pattern] that can be used to match the root prefix of a root-relative + /// path in this style. + /// + /// This can be null to indicate that this style doesn't support root-relative + /// paths. + final Pattern relativeRootPattern = null; + + /// A [Context] that uses this style. + Context get context => new Context(style: this); + + /// Gets the root prefix of [path] if path is absolute. If [path] is relative, + /// returns `null`. + String getRoot(String path) { + // TODO(rnystrom): Use firstMatch() when #7080 is fixed. + var matches = rootPattern.allMatches(path); + if (matches.isNotEmpty) return matches.first[0]; + return getRelativeRoot(path); + } + + /// Gets the root prefix of [path] if it's root-relative. + /// + /// If [path] is relative or absolute and not root-relative, returns `null`. + String getRelativeRoot(String path) { + if (relativeRootPattern == null) return null; + // TODO(rnystrom): Use firstMatch() when #7080 is fixed. + var matches = relativeRootPattern.allMatches(path); + if (matches.isEmpty) return null; + return matches.first[0]; + } + + /// Returns the path represented by [uri] in this style. + String pathFromUri(Uri uri); + + /// Returns the URI that represents the relative path made of [parts]. + Uri relativePathToUri(String path) => + new Uri(pathSegments: context.split(path)); + + /// Returns the URI that represents [path], which is assumed to be absolute. + Uri absolutePathToUri(String path); + + String toString() => name; +} diff --git a/pkg/path/lib/src/style/posix.dart b/pkg/path/lib/src/style/posix.dart new file mode 100644 index 00000000000..72e70441a7d --- /dev/null +++ b/pkg/path/lib/src/style/posix.dart @@ -0,0 +1,42 @@ +// 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 path.style.posix; + +import '../parsed_path.dart'; +import '../style.dart'; + +/// The style for POSIX paths. +class PosixStyle extends Style { + PosixStyle(); + + final name = 'posix'; + final separator = '/'; + final separatorPattern = new RegExp(r'/'); + final needsSeparatorPattern = new RegExp(r'[^/]$'); + final rootPattern = new RegExp(r'^/'); + + String pathFromUri(Uri uri) { + if (uri.scheme == '' || uri.scheme == 'file') { + return Uri.decodeComponent(uri.path); + } + throw new ArgumentError("Uri $uri must have scheme 'file:'."); + } + + Uri absolutePathToUri(String path) { + var parsed = new ParsedPath.parse(path, this); + if (parsed.parts.isEmpty) { + // If the path is a bare root (e.g. "/"), [components] will + // currently be empty. We add two empty components so the URL constructor + // produces "file:///", with a trailing slash. + parsed.parts.addAll(["", ""]); + } else if (parsed.hasTrailingSeparator) { + // If the path has a trailing slash, add a single empty component so the + // URI has a trailing slash as well. + parsed.parts.add(""); + } + + return new Uri(scheme: 'file', pathSegments: parsed.parts); + } +} diff --git a/pkg/path/lib/src/style/url.dart b/pkg/path/lib/src/style/url.dart new file mode 100644 index 00000000000..4a7003debb5 --- /dev/null +++ b/pkg/path/lib/src/style/url.dart @@ -0,0 +1,25 @@ +// 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 path.style.url; + +import '../style.dart'; + +/// The style for URL paths. +class UrlStyle extends Style { + UrlStyle(); + + final name = 'url'; + final separator = '/'; + final separatorPattern = new RegExp(r'/'); + final needsSeparatorPattern = new RegExp( + r"(^[a-zA-Z][-+.a-zA-Z\d]*://|[^/])$"); + final rootPattern = new RegExp(r"[a-zA-Z][-+.a-zA-Z\d]*://[^/]*"); + final relativeRootPattern = new RegExp(r"^/"); + + String pathFromUri(Uri uri) => uri.toString(); + + Uri relativePathToUri(String path) => Uri.parse(path); + Uri absolutePathToUri(String path) => Uri.parse(path); +} diff --git a/pkg/path/lib/src/style/windows.dart b/pkg/path/lib/src/style/windows.dart new file mode 100644 index 00000000000..1750578e359 --- /dev/null +++ b/pkg/path/lib/src/style/windows.dart @@ -0,0 +1,74 @@ +// 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 path.style.windows; + +import '../parsed_path.dart'; +import '../style.dart'; + +/// The style for Windows paths. +class WindowsStyle extends Style { + WindowsStyle(); + + final name = 'windows'; + final separator = '\\'; + final separatorPattern = new RegExp(r'[/\\]'); + final needsSeparatorPattern = new RegExp(r'[^/\\]$'); + final rootPattern = new RegExp(r'^(\\\\[^\\]+\\[^\\/]+|[a-zA-Z]:[/\\])'); + final relativeRootPattern = new RegExp(r"^[/\\](?![/\\])"); + + String pathFromUri(Uri uri) { + if (uri.scheme != '' && uri.scheme != 'file') { + throw new ArgumentError("Uri $uri must have scheme 'file:'."); + } + + var path = uri.path; + if (uri.host == '') { + // Drive-letter paths look like "file:///C:/path/to/file". The + // replaceFirst removes the extra initial slash. + if (path.startsWith('/')) path = path.replaceFirst("/", ""); + } else { + // Network paths look like "file://hostname/path/to/file". + path = '\\\\${uri.host}$path'; + } + return Uri.decodeComponent(path.replaceAll("/", "\\")); + } + + Uri absolutePathToUri(String path) { + var parsed = new ParsedPath.parse(path, this); + if (parsed.root.startsWith(r'\\')) { + // Network paths become "file://server/share/path/to/file". + + // The root is of the form "\\server\share". We want "server" to be the + // URI host, and "share" to be the first element of the path. + var rootParts = parsed.root.split('\\').where((part) => part != ''); + parsed.parts.insert(0, rootParts.last); + + if (parsed.hasTrailingSeparator) { + // If the path has a trailing slash, add a single empty component so the + // URI has a trailing slash as well. + parsed.parts.add(""); + } + + return new Uri(scheme: 'file', host: rootParts.first, + pathSegments: parsed.parts); + } else { + // Drive-letter paths become "file:///C:/path/to/file". + + // If the path is a bare root (e.g. "C:\"), [parsed.parts] will currently + // be empty. We add an empty component so the URL constructor produces + // "file:///C:/", with a trailing slash. We also add an empty component if + // the URL otherwise has a trailing slash. + if (parsed.parts.length == 0 || parsed.hasTrailingSeparator) { + parsed.parts.add(""); + } + + // Get rid of the trailing "\" in "C:\" because the URI constructor will + // add a separator on its own. + parsed.parts.insert(0, parsed.root.replaceAll(separatorPattern, "")); + + return new Uri(scheme: 'file', pathSegments: parsed.parts); + } + } +} \ No newline at end of file diff --git a/pkg/path/test/browser_test.dart b/pkg/path/test/browser_test.dart index a6e8a680455..d8584a46159 100644 --- a/pkg/path/test/browser_test.dart +++ b/pkg/path/test/browser_test.dart @@ -11,21 +11,21 @@ import 'package:path/path.dart' as path; main() { useHtmlConfiguration(); - group('new Builder()', () { + group('new Context()', () { test('uses the window location if root and style are omitted', () { - var builder = new path.Builder(); - expect(builder.root, + var context = new path.Context(); + expect(context.current, Uri.parse(window.location.href).resolve('.').toString()); }); test('uses "." if root is omitted', () { - var builder = new path.Builder(style: path.Style.platform); - expect(builder.root, "."); + var context = new path.Context(style: path.Style.platform); + expect(context.current, "."); }); test('uses the host platform if style is omitted', () { - var builder = new path.Builder(); - expect(builder.style, path.Style.platform); + var context = new path.Context(); + expect(context.style, path.Style.platform); }); }); diff --git a/pkg/path/test/io_test.dart b/pkg/path/test/io_test.dart index 2d4ac0c51a1..3e52bb95ef7 100644 --- a/pkg/path/test/io_test.dart +++ b/pkg/path/test/io_test.dart @@ -8,20 +8,20 @@ import 'package:unittest/unittest.dart'; import 'package:path/path.dart' as path; main() { - group('new Builder()', () { + group('new Context()', () { test('uses the current directory if root and style are omitted', () { - var builder = new path.Builder(); - expect(builder.root, io.Directory.current.path); + var context = new path.Context(); + expect(context.current, io.Directory.current.path); }); test('uses "." if root is omitted', () { - var builder = new path.Builder(style: path.Style.platform); - expect(builder.root, "."); + var context = new path.Context(style: path.Style.platform); + expect(context.current, "."); }); test('uses the host platform if style is omitted', () { - var builder = new path.Builder(); - expect(builder.style, path.Style.platform); + var context = new path.Context(); + expect(context.style, path.Style.platform); }); }); diff --git a/pkg/path/test/path_test.dart b/pkg/path/test/path_test.dart index d6c2f3493cf..19397cf8d3c 100644 --- a/pkg/path/test/path_test.dart +++ b/pkg/path/test/path_test.dart @@ -23,30 +23,30 @@ main() { }); }); - group('new Builder()', () { - test('uses the given root directory', () { - var builder = new path.Builder(root: '/a/b/c'); - expect(builder.root, '/a/b/c'); + group('new Context()', () { + test('uses the given current directory', () { + var context = new path.Context(current: '/a/b/c'); + expect(context.current, '/a/b/c'); }); test('uses the given style', () { - var builder = new path.Builder(style: path.Style.windows); - expect(builder.style, path.Style.windows); + var context = new path.Context(style: path.Style.windows); + expect(context.style, path.Style.windows); }); }); - test('posix is a default Builder for the POSIX style', () { + test('posix is a default Context for the POSIX style', () { expect(path.posix.style, path.Style.posix); - expect(path.posix.root, "."); + expect(path.posix.current, "."); }); - test('windows is a default Builder for the Windows style', () { + test('windows is a default Context for the Windows style', () { expect(path.windows.style, path.Style.windows); - expect(path.windows.root, "."); + expect(path.windows.current, "."); }); - test('url is a default Builder for the URL style', () { + test('url is a default Context for the URL style', () { expect(path.url.style, path.Style.url); - expect(path.url.root, "."); + expect(path.url.current, "."); }); } diff --git a/pkg/path/test/posix_test.dart b/pkg/path/test/posix_test.dart index b693fcf4a73..83a473e7808 100644 --- a/pkg/path/test/posix_test.dart +++ b/pkg/path/test/posix_test.dart @@ -10,361 +10,355 @@ import 'package:path/path.dart' as path; import 'utils.dart'; main() { - var builder = new path.Builder(style: path.Style.posix, root: '/root/path'); - - if (new path.Builder().style == path.Style.posix) { - group('absolute', () { - expect(path.absolute('a/b.txt'), path.join(path.current, 'a/b.txt')); - expect(path.absolute('/a/b.txt'), '/a/b.txt'); - }); - } + var context = new path.Context( + style: path.Style.posix, current: '/root/path'); test('separator', () { - expect(builder.separator, '/'); + expect(context.separator, '/'); }); test('extension', () { - expect(builder.extension(''), ''); - expect(builder.extension('.'), ''); - expect(builder.extension('..'), ''); - expect(builder.extension('foo.dart'), '.dart'); - expect(builder.extension('foo.dart.js'), '.js'); - expect(builder.extension('a.b/c'), ''); - expect(builder.extension('a.b/c.d'), '.d'); - expect(builder.extension('~/.bashrc'), ''); - expect(builder.extension(r'a.b\c'), r'.b\c'); - expect(builder.extension('foo.dart/'), '.dart'); - expect(builder.extension('foo.dart//'), '.dart'); + expect(context.extension(''), ''); + expect(context.extension('.'), ''); + expect(context.extension('..'), ''); + expect(context.extension('foo.dart'), '.dart'); + expect(context.extension('foo.dart.js'), '.js'); + expect(context.extension('a.b/c'), ''); + expect(context.extension('a.b/c.d'), '.d'); + expect(context.extension('~/.bashrc'), ''); + expect(context.extension(r'a.b\c'), r'.b\c'); + expect(context.extension('foo.dart/'), '.dart'); + expect(context.extension('foo.dart//'), '.dart'); }); test('rootPrefix', () { - expect(builder.rootPrefix(''), ''); - expect(builder.rootPrefix('a'), ''); - expect(builder.rootPrefix('a/b'), ''); - expect(builder.rootPrefix('/a/c'), '/'); - expect(builder.rootPrefix('/'), '/'); + expect(context.rootPrefix(''), ''); + expect(context.rootPrefix('a'), ''); + expect(context.rootPrefix('a/b'), ''); + expect(context.rootPrefix('/a/c'), '/'); + expect(context.rootPrefix('/'), '/'); }); test('dirname', () { - expect(builder.dirname(''), '.'); - expect(builder.dirname('.'), '.'); - expect(builder.dirname('..'), '.'); - expect(builder.dirname('../..'), '..'); - expect(builder.dirname('a'), '.'); - expect(builder.dirname('a/b'), 'a'); - expect(builder.dirname('a/b/c'), 'a/b'); - expect(builder.dirname('a/b.c'), 'a'); - expect(builder.dirname('a/'), '.'); - expect(builder.dirname('a/.'), 'a'); - expect(builder.dirname('a/..'), 'a'); - expect(builder.dirname(r'a\b/c'), r'a\b'); - expect(builder.dirname('/a'), '/'); - expect(builder.dirname('///a'), '/'); - expect(builder.dirname('/'), '/'); - expect(builder.dirname('///'), '/'); - expect(builder.dirname('a/b/'), 'a'); - expect(builder.dirname(r'a/b\c'), 'a'); - expect(builder.dirname('a//'), '.'); - expect(builder.dirname('a/b//'), 'a'); - expect(builder.dirname('a//b'), 'a'); + expect(context.dirname(''), '.'); + expect(context.dirname('.'), '.'); + expect(context.dirname('..'), '.'); + expect(context.dirname('../..'), '..'); + expect(context.dirname('a'), '.'); + expect(context.dirname('a/b'), 'a'); + expect(context.dirname('a/b/c'), 'a/b'); + expect(context.dirname('a/b.c'), 'a'); + expect(context.dirname('a/'), '.'); + expect(context.dirname('a/.'), 'a'); + expect(context.dirname('a/..'), 'a'); + expect(context.dirname(r'a\b/c'), r'a\b'); + expect(context.dirname('/a'), '/'); + expect(context.dirname('///a'), '/'); + expect(context.dirname('/'), '/'); + expect(context.dirname('///'), '/'); + expect(context.dirname('a/b/'), 'a'); + expect(context.dirname(r'a/b\c'), 'a'); + expect(context.dirname('a//'), '.'); + expect(context.dirname('a/b//'), 'a'); + expect(context.dirname('a//b'), 'a'); }); test('basename', () { - expect(builder.basename(''), ''); - expect(builder.basename('.'), '.'); - expect(builder.basename('..'), '..'); - expect(builder.basename('.foo'), '.foo'); - expect(builder.basename('a'), 'a'); - expect(builder.basename('a/b'), 'b'); - expect(builder.basename('a/b/c'), 'c'); - expect(builder.basename('a/b.c'), 'b.c'); - expect(builder.basename('a/'), 'a'); - expect(builder.basename('a/.'), '.'); - expect(builder.basename('a/..'), '..'); - expect(builder.basename(r'a\b/c'), 'c'); - expect(builder.basename('/a'), 'a'); - expect(builder.basename('/'), '/'); - expect(builder.basename('a/b/'), 'b'); - expect(builder.basename(r'a/b\c'), r'b\c'); - expect(builder.basename('a//'), 'a'); - expect(builder.basename('a/b//'), 'b'); - expect(builder.basename('a//b'), 'b'); + expect(context.basename(''), ''); + expect(context.basename('.'), '.'); + expect(context.basename('..'), '..'); + expect(context.basename('.foo'), '.foo'); + expect(context.basename('a'), 'a'); + expect(context.basename('a/b'), 'b'); + expect(context.basename('a/b/c'), 'c'); + expect(context.basename('a/b.c'), 'b.c'); + expect(context.basename('a/'), 'a'); + expect(context.basename('a/.'), '.'); + expect(context.basename('a/..'), '..'); + expect(context.basename(r'a\b/c'), 'c'); + expect(context.basename('/a'), 'a'); + expect(context.basename('/'), '/'); + expect(context.basename('a/b/'), 'b'); + expect(context.basename(r'a/b\c'), r'b\c'); + expect(context.basename('a//'), 'a'); + expect(context.basename('a/b//'), 'b'); + expect(context.basename('a//b'), 'b'); }); test('basenameWithoutExtension', () { - expect(builder.basenameWithoutExtension(''), ''); - expect(builder.basenameWithoutExtension('.'), '.'); - expect(builder.basenameWithoutExtension('..'), '..'); - expect(builder.basenameWithoutExtension('a'), 'a'); - expect(builder.basenameWithoutExtension('a/b'), 'b'); - expect(builder.basenameWithoutExtension('a/b/c'), 'c'); - expect(builder.basenameWithoutExtension('a/b.c'), 'b'); - expect(builder.basenameWithoutExtension('a/'), 'a'); - expect(builder.basenameWithoutExtension('a/.'), '.'); - expect(builder.basenameWithoutExtension(r'a/b\c'), r'b\c'); - expect(builder.basenameWithoutExtension('a/.bashrc'), '.bashrc'); - expect(builder.basenameWithoutExtension('a/b/c.d.e'), 'c.d'); - expect(builder.basenameWithoutExtension('a//'), 'a'); - expect(builder.basenameWithoutExtension('a/b//'), 'b'); - expect(builder.basenameWithoutExtension('a//b'), 'b'); - expect(builder.basenameWithoutExtension('a/b.c/'), 'b'); - expect(builder.basenameWithoutExtension('a/b.c//'), 'b'); - expect(builder.basenameWithoutExtension('a/b c.d e'), 'b c'); + expect(context.basenameWithoutExtension(''), ''); + expect(context.basenameWithoutExtension('.'), '.'); + expect(context.basenameWithoutExtension('..'), '..'); + expect(context.basenameWithoutExtension('a'), 'a'); + expect(context.basenameWithoutExtension('a/b'), 'b'); + expect(context.basenameWithoutExtension('a/b/c'), 'c'); + expect(context.basenameWithoutExtension('a/b.c'), 'b'); + expect(context.basenameWithoutExtension('a/'), 'a'); + expect(context.basenameWithoutExtension('a/.'), '.'); + expect(context.basenameWithoutExtension(r'a/b\c'), r'b\c'); + expect(context.basenameWithoutExtension('a/.bashrc'), '.bashrc'); + expect(context.basenameWithoutExtension('a/b/c.d.e'), 'c.d'); + expect(context.basenameWithoutExtension('a//'), 'a'); + expect(context.basenameWithoutExtension('a/b//'), 'b'); + expect(context.basenameWithoutExtension('a//b'), 'b'); + expect(context.basenameWithoutExtension('a/b.c/'), 'b'); + expect(context.basenameWithoutExtension('a/b.c//'), 'b'); + expect(context.basenameWithoutExtension('a/b c.d e'), 'b c'); }); test('isAbsolute', () { - expect(builder.isAbsolute(''), false); - expect(builder.isAbsolute('a'), false); - expect(builder.isAbsolute('a/b'), false); - expect(builder.isAbsolute('/a'), true); - expect(builder.isAbsolute('/a/b'), true); - expect(builder.isAbsolute('~'), false); - expect(builder.isAbsolute('.'), false); - expect(builder.isAbsolute('..'), false); - expect(builder.isAbsolute('.foo'), false); - expect(builder.isAbsolute('../a'), false); - expect(builder.isAbsolute('C:/a'), false); - expect(builder.isAbsolute(r'C:\a'), false); - expect(builder.isAbsolute(r'\\a'), false); + expect(context.isAbsolute(''), false); + expect(context.isAbsolute('a'), false); + expect(context.isAbsolute('a/b'), false); + expect(context.isAbsolute('/a'), true); + expect(context.isAbsolute('/a/b'), true); + expect(context.isAbsolute('~'), false); + expect(context.isAbsolute('.'), false); + expect(context.isAbsolute('..'), false); + expect(context.isAbsolute('.foo'), false); + expect(context.isAbsolute('../a'), false); + expect(context.isAbsolute('C:/a'), false); + expect(context.isAbsolute(r'C:\a'), false); + expect(context.isAbsolute(r'\\a'), false); }); test('isRelative', () { - expect(builder.isRelative(''), true); - expect(builder.isRelative('a'), true); - expect(builder.isRelative('a/b'), true); - expect(builder.isRelative('/a'), false); - expect(builder.isRelative('/a/b'), false); - expect(builder.isRelative('~'), true); - expect(builder.isRelative('.'), true); - expect(builder.isRelative('..'), true); - expect(builder.isRelative('.foo'), true); - expect(builder.isRelative('../a'), true); - expect(builder.isRelative('C:/a'), true); - expect(builder.isRelative(r'C:\a'), true); - expect(builder.isRelative(r'\\a'), true); + expect(context.isRelative(''), true); + expect(context.isRelative('a'), true); + expect(context.isRelative('a/b'), true); + expect(context.isRelative('/a'), false); + expect(context.isRelative('/a/b'), false); + expect(context.isRelative('~'), true); + expect(context.isRelative('.'), true); + expect(context.isRelative('..'), true); + expect(context.isRelative('.foo'), true); + expect(context.isRelative('../a'), true); + expect(context.isRelative('C:/a'), true); + expect(context.isRelative(r'C:\a'), true); + expect(context.isRelative(r'\\a'), true); }); group('join', () { test('allows up to eight parts', () { - expect(builder.join('a'), 'a'); - expect(builder.join('a', 'b'), 'a/b'); - expect(builder.join('a', 'b', 'c'), 'a/b/c'); - expect(builder.join('a', 'b', 'c', 'd'), 'a/b/c/d'); - expect(builder.join('a', 'b', 'c', 'd', 'e'), 'a/b/c/d/e'); - expect(builder.join('a', 'b', 'c', 'd', 'e', 'f'), 'a/b/c/d/e/f'); - expect(builder.join('a', 'b', 'c', 'd', 'e', 'f', 'g'), 'a/b/c/d/e/f/g'); - expect(builder.join('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'), + expect(context.join('a'), 'a'); + expect(context.join('a', 'b'), 'a/b'); + expect(context.join('a', 'b', 'c'), 'a/b/c'); + expect(context.join('a', 'b', 'c', 'd'), 'a/b/c/d'); + expect(context.join('a', 'b', 'c', 'd', 'e'), 'a/b/c/d/e'); + expect(context.join('a', 'b', 'c', 'd', 'e', 'f'), 'a/b/c/d/e/f'); + expect(context.join('a', 'b', 'c', 'd', 'e', 'f', 'g'), 'a/b/c/d/e/f/g'); + expect(context.join('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'), 'a/b/c/d/e/f/g/h'); }); test('does not add separator if a part ends in one', () { - expect(builder.join('a/', 'b', 'c/', 'd'), 'a/b/c/d'); - expect(builder.join('a\\', 'b'), r'a\/b'); + expect(context.join('a/', 'b', 'c/', 'd'), 'a/b/c/d'); + expect(context.join('a\\', 'b'), r'a\/b'); }); test('ignores parts before an absolute path', () { - expect(builder.join('a', '/', 'b', 'c'), '/b/c'); - expect(builder.join('a', '/b', '/c', 'd'), '/c/d'); - expect(builder.join('a', r'c:\b', 'c', 'd'), r'a/c:\b/c/d'); - expect(builder.join('a', r'\\b', 'c', 'd'), r'a/\\b/c/d'); + expect(context.join('a', '/', 'b', 'c'), '/b/c'); + expect(context.join('a', '/b', '/c', 'd'), '/c/d'); + expect(context.join('a', r'c:\b', 'c', 'd'), r'a/c:\b/c/d'); + expect(context.join('a', r'\\b', 'c', 'd'), r'a/\\b/c/d'); }); test('ignores trailing nulls', () { - expect(builder.join('a', null), equals('a')); - expect(builder.join('a', 'b', 'c', null, null), equals('a/b/c')); + expect(context.join('a', null), equals('a')); + expect(context.join('a', 'b', 'c', null, null), equals('a/b/c')); }); test('ignores empty strings', () { - expect(builder.join(''), ''); - expect(builder.join('', ''), ''); - expect(builder.join('', 'a'), 'a'); - expect(builder.join('a', '', 'b', '', '', '', 'c'), 'a/b/c'); - expect(builder.join('a', 'b', ''), 'a/b'); + expect(context.join(''), ''); + expect(context.join('', ''), ''); + expect(context.join('', 'a'), 'a'); + expect(context.join('a', '', 'b', '', '', '', 'c'), 'a/b/c'); + expect(context.join('a', 'b', ''), 'a/b'); }); test('disallows intermediate nulls', () { - expect(() => builder.join('a', null, 'b'), throwsArgumentError); - expect(() => builder.join(null, 'a'), throwsArgumentError); + expect(() => context.join('a', null, 'b'), throwsArgumentError); + expect(() => context.join(null, 'a'), throwsArgumentError); }); test('join does not modify internal ., .., or trailing separators', () { - expect(builder.join('a/', 'b/c/'), 'a/b/c/'); - expect(builder.join('a/b/./c/..//', 'd/.././..//e/f//'), + expect(context.join('a/', 'b/c/'), 'a/b/c/'); + expect(context.join('a/b/./c/..//', 'd/.././..//e/f//'), 'a/b/./c/..//d/.././..//e/f//'); - expect(builder.join('a/b', 'c/../../../..'), 'a/b/c/../../../..'); - expect(builder.join('a', 'b${builder.separator}'), 'a/b/'); + expect(context.join('a/b', 'c/../../../..'), 'a/b/c/../../../..'); + expect(context.join('a', 'b${context.separator}'), 'a/b/'); }); }); group('joinAll', () { test('allows more than eight parts', () { - expect(builder.joinAll(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']), + expect(context.joinAll(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']), 'a/b/c/d/e/f/g/h/i'); }); test('does not add separator if a part ends in one', () { - expect(builder.joinAll(['a/', 'b', 'c/', 'd']), 'a/b/c/d'); - expect(builder.joinAll(['a\\', 'b']), r'a\/b'); + expect(context.joinAll(['a/', 'b', 'c/', 'd']), 'a/b/c/d'); + expect(context.joinAll(['a\\', 'b']), r'a\/b'); }); test('ignores parts before an absolute path', () { - expect(builder.joinAll(['a', '/', 'b', 'c']), '/b/c'); - expect(builder.joinAll(['a', '/b', '/c', 'd']), '/c/d'); - expect(builder.joinAll(['a', r'c:\b', 'c', 'd']), r'a/c:\b/c/d'); - expect(builder.joinAll(['a', r'\\b', 'c', 'd']), r'a/\\b/c/d'); + expect(context.joinAll(['a', '/', 'b', 'c']), '/b/c'); + expect(context.joinAll(['a', '/b', '/c', 'd']), '/c/d'); + expect(context.joinAll(['a', r'c:\b', 'c', 'd']), r'a/c:\b/c/d'); + expect(context.joinAll(['a', r'\\b', 'c', 'd']), r'a/\\b/c/d'); }); }); group('split', () { test('simple cases', () { - expect(builder.split(''), []); - expect(builder.split('.'), ['.']); - expect(builder.split('..'), ['..']); - expect(builder.split('foo'), equals(['foo'])); - expect(builder.split('foo/bar.txt'), equals(['foo', 'bar.txt'])); - expect(builder.split('foo/bar/baz'), equals(['foo', 'bar', 'baz'])); - expect(builder.split('foo/../bar/./baz'), + expect(context.split(''), []); + expect(context.split('.'), ['.']); + expect(context.split('..'), ['..']); + expect(context.split('foo'), equals(['foo'])); + expect(context.split('foo/bar.txt'), equals(['foo', 'bar.txt'])); + expect(context.split('foo/bar/baz'), equals(['foo', 'bar', 'baz'])); + expect(context.split('foo/../bar/./baz'), equals(['foo', '..', 'bar', '.', 'baz'])); - expect(builder.split('foo//bar///baz'), equals(['foo', 'bar', 'baz'])); - expect(builder.split('foo/\\/baz'), equals(['foo', '\\', 'baz'])); - expect(builder.split('.'), equals(['.'])); - expect(builder.split(''), equals([])); - expect(builder.split('foo/'), equals(['foo'])); - expect(builder.split('//'), equals(['/'])); + expect(context.split('foo//bar///baz'), equals(['foo', 'bar', 'baz'])); + expect(context.split('foo/\\/baz'), equals(['foo', '\\', 'baz'])); + expect(context.split('.'), equals(['.'])); + expect(context.split(''), equals([])); + expect(context.split('foo/'), equals(['foo'])); + expect(context.split('//'), equals(['/'])); }); test('includes the root for absolute paths', () { - expect(builder.split('/foo/bar/baz'), equals(['/', 'foo', 'bar', 'baz'])); - expect(builder.split('/'), equals(['/'])); + expect(context.split('/foo/bar/baz'), equals(['/', 'foo', 'bar', 'baz'])); + expect(context.split('/'), equals(['/'])); }); }); group('normalize', () { test('simple cases', () { - expect(builder.normalize(''), '.'); - expect(builder.normalize('.'), '.'); - expect(builder.normalize('..'), '..'); - expect(builder.normalize('a'), 'a'); - expect(builder.normalize('/'), '/'); - expect(builder.normalize(r'\'), r'\'); - expect(builder.normalize('C:/'), 'C:'); - expect(builder.normalize(r'C:\'), r'C:\'); - expect(builder.normalize(r'\\'), r'\\'); - expect(builder.normalize('a/./\xc5\u0bf8-;\u{1f085}\u{00}/c/d/../'), + expect(context.normalize(''), '.'); + expect(context.normalize('.'), '.'); + expect(context.normalize('..'), '..'); + expect(context.normalize('a'), 'a'); + expect(context.normalize('/'), '/'); + expect(context.normalize(r'\'), r'\'); + expect(context.normalize('C:/'), 'C:'); + expect(context.normalize(r'C:\'), r'C:\'); + expect(context.normalize(r'\\'), r'\\'); + expect(context.normalize('a/./\xc5\u0bf8-;\u{1f085}\u{00}/c/d/../'), 'a/\xc5\u0bf8-;\u{1f085}\u{00}/c'); }); test('collapses redundant separators', () { - expect(builder.normalize(r'a/b/c'), r'a/b/c'); - expect(builder.normalize(r'a//b///c////d'), r'a/b/c/d'); + expect(context.normalize(r'a/b/c'), r'a/b/c'); + expect(context.normalize(r'a//b///c////d'), r'a/b/c/d'); }); test('does not collapse separators for other platform', () { - expect(builder.normalize(r'a\\b\\\c'), r'a\\b\\\c'); + expect(context.normalize(r'a\\b\\\c'), r'a\\b\\\c'); }); test('eliminates "." parts', () { - expect(builder.normalize('./'), '.'); - expect(builder.normalize('/.'), '/'); - expect(builder.normalize('/./'), '/'); - expect(builder.normalize('./.'), '.'); - expect(builder.normalize('a/./b'), 'a/b'); - expect(builder.normalize('a/.b/c'), 'a/.b/c'); - expect(builder.normalize('a/././b/./c'), 'a/b/c'); - expect(builder.normalize('././a'), 'a'); - expect(builder.normalize('a/./.'), 'a'); + expect(context.normalize('./'), '.'); + expect(context.normalize('/.'), '/'); + expect(context.normalize('/./'), '/'); + expect(context.normalize('./.'), '.'); + expect(context.normalize('a/./b'), 'a/b'); + expect(context.normalize('a/.b/c'), 'a/.b/c'); + expect(context.normalize('a/././b/./c'), 'a/b/c'); + expect(context.normalize('././a'), 'a'); + expect(context.normalize('a/./.'), 'a'); }); test('eliminates ".." parts', () { - expect(builder.normalize('..'), '..'); - expect(builder.normalize('../'), '..'); - expect(builder.normalize('../../..'), '../../..'); - expect(builder.normalize('../../../'), '../../..'); - expect(builder.normalize('/..'), '/'); - expect(builder.normalize('/../../..'), '/'); - expect(builder.normalize('/../../../a'), '/a'); - expect(builder.normalize('c:/..'), '.'); - expect(builder.normalize('A:/../../..'), '../..'); - expect(builder.normalize('a/..'), '.'); - expect(builder.normalize('a/b/..'), 'a'); - expect(builder.normalize('a/../b'), 'b'); - expect(builder.normalize('a/./../b'), 'b'); - expect(builder.normalize('a/b/c/../../d/e/..'), 'a/d'); - expect(builder.normalize('a/b/../../../../c'), '../../c'); - expect(builder.normalize(r'z/a/b/../../..\../c'), r'z/..\../c'); - expect(builder.normalize(r'a/b\c/../d'), 'a/d'); + expect(context.normalize('..'), '..'); + expect(context.normalize('../'), '..'); + expect(context.normalize('../../..'), '../../..'); + expect(context.normalize('../../../'), '../../..'); + expect(context.normalize('/..'), '/'); + expect(context.normalize('/../../..'), '/'); + expect(context.normalize('/../../../a'), '/a'); + expect(context.normalize('c:/..'), '.'); + expect(context.normalize('A:/../../..'), '../..'); + expect(context.normalize('a/..'), '.'); + expect(context.normalize('a/b/..'), 'a'); + expect(context.normalize('a/../b'), 'b'); + expect(context.normalize('a/./../b'), 'b'); + expect(context.normalize('a/b/c/../../d/e/..'), 'a/d'); + expect(context.normalize('a/b/../../../../c'), '../../c'); + expect(context.normalize(r'z/a/b/../../..\../c'), r'z/..\../c'); + expect(context.normalize(r'a/b\c/../d'), 'a/d'); }); test('does not walk before root on absolute paths', () { - expect(builder.normalize('..'), '..'); - expect(builder.normalize('../'), '..'); - expect(builder.normalize('http://dartlang.org/..'), 'http:'); - expect(builder.normalize('http://dartlang.org/../../a'), 'a'); - expect(builder.normalize('file:///..'), '.'); - expect(builder.normalize('file:///../../a'), '../a'); - expect(builder.normalize('/..'), '/'); - expect(builder.normalize('a/..'), '.'); - expect(builder.normalize('../a'), '../a'); - expect(builder.normalize('/../a'), '/a'); - expect(builder.normalize('c:/../a'), 'a'); - expect(builder.normalize('/../a'), '/a'); - expect(builder.normalize('a/b/..'), 'a'); - expect(builder.normalize('../a/b/..'), '../a'); - expect(builder.normalize('a/../b'), 'b'); - expect(builder.normalize('a/./../b'), 'b'); - expect(builder.normalize('a/b/c/../../d/e/..'), 'a/d'); - expect(builder.normalize('a/b/../../../../c'), '../../c'); - expect(builder.normalize('a/b/c/../../..d/./.e/f././'), 'a/..d/.e/f.'); + expect(context.normalize('..'), '..'); + expect(context.normalize('../'), '..'); + expect(context.normalize('http://dartlang.org/..'), 'http:'); + expect(context.normalize('http://dartlang.org/../../a'), 'a'); + expect(context.normalize('file:///..'), '.'); + expect(context.normalize('file:///../../a'), '../a'); + expect(context.normalize('/..'), '/'); + expect(context.normalize('a/..'), '.'); + expect(context.normalize('../a'), '../a'); + expect(context.normalize('/../a'), '/a'); + expect(context.normalize('c:/../a'), 'a'); + expect(context.normalize('/../a'), '/a'); + expect(context.normalize('a/b/..'), 'a'); + expect(context.normalize('../a/b/..'), '../a'); + expect(context.normalize('a/../b'), 'b'); + expect(context.normalize('a/./../b'), 'b'); + expect(context.normalize('a/b/c/../../d/e/..'), 'a/d'); + expect(context.normalize('a/b/../../../../c'), '../../c'); + expect(context.normalize('a/b/c/../../..d/./.e/f././'), 'a/..d/.e/f.'); }); test('removes trailing separators', () { - expect(builder.normalize('./'), '.'); - expect(builder.normalize('.//'), '.'); - expect(builder.normalize('a/'), 'a'); - expect(builder.normalize('a/b/'), 'a/b'); - expect(builder.normalize(r'a/b\'), r'a/b\'); - expect(builder.normalize('a/b///'), 'a/b'); + expect(context.normalize('./'), '.'); + expect(context.normalize('.//'), '.'); + expect(context.normalize('a/'), 'a'); + expect(context.normalize('a/b/'), 'a/b'); + expect(context.normalize(r'a/b\'), r'a/b\'); + expect(context.normalize('a/b///'), 'a/b'); }); }); group('relative', () { group('from absolute root', () { test('given absolute path in root', () { - expect(builder.relative('/'), '../..'); - expect(builder.relative('/root'), '..'); - expect(builder.relative('/root/path'), '.'); - expect(builder.relative('/root/path/a'), 'a'); - expect(builder.relative('/root/path/a/b.txt'), 'a/b.txt'); - expect(builder.relative('/root/a/b.txt'), '../a/b.txt'); + expect(context.relative('/'), '../..'); + expect(context.relative('/root'), '..'); + expect(context.relative('/root/path'), '.'); + expect(context.relative('/root/path/a'), 'a'); + expect(context.relative('/root/path/a/b.txt'), 'a/b.txt'); + expect(context.relative('/root/a/b.txt'), '../a/b.txt'); }); test('given absolute path outside of root', () { - expect(builder.relative('/a/b'), '../../a/b'); - expect(builder.relative('/root/path/a'), 'a'); - expect(builder.relative('/root/path/a/b.txt'), 'a/b.txt'); - expect(builder.relative('/root/a/b.txt'), '../a/b.txt'); + expect(context.relative('/a/b'), '../../a/b'); + expect(context.relative('/root/path/a'), 'a'); + expect(context.relative('/root/path/a/b.txt'), 'a/b.txt'); + expect(context.relative('/root/a/b.txt'), '../a/b.txt'); }); test('given relative path', () { // The path is considered relative to the root, so it basically just // normalizes. - expect(builder.relative(''), '.'); - expect(builder.relative('.'), '.'); - expect(builder.relative('a'), 'a'); - expect(builder.relative('a/b.txt'), 'a/b.txt'); - expect(builder.relative('../a/b.txt'), '../a/b.txt'); - expect(builder.relative('a/./b/../c.txt'), 'a/c.txt'); + expect(context.relative(''), '.'); + expect(context.relative('.'), '.'); + expect(context.relative('a'), 'a'); + expect(context.relative('a/b.txt'), 'a/b.txt'); + expect(context.relative('../a/b.txt'), '../a/b.txt'); + expect(context.relative('a/./b/../c.txt'), 'a/c.txt'); }); // Regression test('from root-only path', () { - expect(builder.relative('/', from: '/'), '.'); - expect(builder.relative('/root/path', from: '/'), 'root/path'); + expect(context.relative('/', from: '/'), '.'); + expect(context.relative('/root/path', from: '/'), 'root/path'); }); }); group('from relative root', () { - var r = new path.Builder(style: path.Style.posix, root: 'foo/bar'); + var r = new path.Context(style: path.Style.posix, current: 'foo/bar'); test('given absolute path', () { expect(r.relative('/'), equals('/')); @@ -385,20 +379,21 @@ main() { }); test('from a root with extension', () { - var r = new path.Builder(style: path.Style.posix, root: '/dir.ext'); + var r = new path.Context(style: path.Style.posix, current: '/dir.ext'); expect(r.relative('/dir.ext/file'), 'file'); }); test('with a root parameter', () { - expect(builder.relative('/foo/bar/baz', from: '/foo/bar'), equals('baz')); - expect(builder.relative('..', from: '/foo/bar'), equals('../../root')); - expect(builder.relative('/foo/bar/baz', from: 'foo/bar'), + expect(context.relative('/foo/bar/baz', from: '/foo/bar'), equals('baz')); + expect(context.relative('..', from: '/foo/bar'), equals('../../root')); + expect(context.relative('/foo/bar/baz', from: 'foo/bar'), equals('../../../../foo/bar/baz')); - expect(builder.relative('..', from: 'foo/bar'), equals('../../..')); + expect(context.relative('..', from: 'foo/bar'), equals('../../..')); }); test('with a root parameter and a relative root', () { - var r = new path.Builder(style: path.Style.posix, root: 'relative/root'); + var r = new path.Context( + style: path.Style.posix, current: 'relative/root'); expect(r.relative('/foo/bar/baz', from: '/foo/bar'), equals('baz')); expect(() => r.relative('..', from: '/foo/bar'), throwsPathException); expect(r.relative('/foo/bar/baz', from: 'foo/bar'), @@ -407,7 +402,7 @@ main() { }); test('from a . root', () { - var r = new path.Builder(style: path.Style.posix, root: '.'); + var r = new path.Context(style: path.Style.posix, current: '.'); expect(r.relative('/foo/bar/baz'), equals('/foo/bar/baz')); expect(r.relative('foo/bar/baz'), equals('foo/bar/baz')); }); @@ -415,94 +410,95 @@ main() { group('isWithin', () { test('simple cases', () { - expect(builder.isWithin('foo/bar', 'foo/bar'), isFalse); - expect(builder.isWithin('foo/bar', 'foo/bar/baz'), isTrue); - expect(builder.isWithin('foo/bar', 'foo/baz'), isFalse); - expect(builder.isWithin('foo/bar', '../path/foo/bar/baz'), isTrue); - expect(builder.isWithin('/', '/foo/bar'), isTrue); - expect(builder.isWithin('baz', '/root/path/baz/bang'), isTrue); - expect(builder.isWithin('baz', '/root/path/bang/baz'), isFalse); + expect(context.isWithin('foo/bar', 'foo/bar'), isFalse); + expect(context.isWithin('foo/bar', 'foo/bar/baz'), isTrue); + expect(context.isWithin('foo/bar', 'foo/baz'), isFalse); + expect(context.isWithin('foo/bar', '../path/foo/bar/baz'), isTrue); + expect(context.isWithin('/', '/foo/bar'), isTrue); + expect(context.isWithin('baz', '/root/path/baz/bang'), isTrue); + expect(context.isWithin('baz', '/root/path/bang/baz'), isFalse); }); test('from a relative root', () { - var r = new path.Builder(style: path.Style.posix, root: 'foo/bar'); - expect(builder.isWithin('.', 'a/b/c'), isTrue); - expect(builder.isWithin('.', '../a/b/c'), isFalse); - expect(builder.isWithin('.', '../../a/foo/b/c'), isFalse); - expect(builder.isWithin('/', '/baz/bang'), isTrue); - expect(builder.isWithin('.', '/baz/bang'), isFalse); + var r = new path.Context(style: path.Style.posix, current: 'foo/bar'); + expect(context.isWithin('.', 'a/b/c'), isTrue); + expect(context.isWithin('.', '../a/b/c'), isFalse); + expect(context.isWithin('.', '../../a/foo/b/c'), isFalse); + expect(context.isWithin('/', '/baz/bang'), isTrue); + expect(context.isWithin('.', '/baz/bang'), isFalse); }); }); - group('resolve', () { + group('absolute', () { test('allows up to seven parts', () { - expect(builder.resolve('a'), '/root/path/a'); - expect(builder.resolve('a', 'b'), '/root/path/a/b'); - expect(builder.resolve('a', 'b', 'c'), '/root/path/a/b/c'); - expect(builder.resolve('a', 'b', 'c', 'd'), '/root/path/a/b/c/d'); - expect(builder.resolve('a', 'b', 'c', 'd', 'e'), '/root/path/a/b/c/d/e'); - expect(builder.resolve('a', 'b', 'c', 'd', 'e', 'f'), + expect(context.absolute('a'), '/root/path/a'); + expect(context.absolute('a', 'b'), '/root/path/a/b'); + expect(context.absolute('a', 'b', 'c'), '/root/path/a/b/c'); + expect(context.absolute('a', 'b', 'c', 'd'), '/root/path/a/b/c/d'); + expect(context.absolute('a', 'b', 'c', 'd', 'e'), '/root/path/a/b/c/d/e'); + expect(context.absolute('a', 'b', 'c', 'd', 'e', 'f'), '/root/path/a/b/c/d/e/f'); - expect(builder.resolve('a', 'b', 'c', 'd', 'e', 'f', 'g'), + expect(context.absolute('a', 'b', 'c', 'd', 'e', 'f', 'g'), '/root/path/a/b/c/d/e/f/g'); }); test('does not add separator if a part ends in one', () { - expect(builder.resolve('a/', 'b', 'c/', 'd'), '/root/path/a/b/c/d'); - expect(builder.resolve(r'a\', 'b'), r'/root/path/a\/b'); + expect(context.absolute('a/', 'b', 'c/', 'd'), '/root/path/a/b/c/d'); + expect(context.absolute(r'a\', 'b'), r'/root/path/a\/b'); }); test('ignores parts before an absolute path', () { - expect(builder.resolve('a', '/b', '/c', 'd'), '/c/d'); - expect(builder.resolve('a', r'c:\b', 'c', 'd'), r'/root/path/a/c:\b/c/d'); - expect(builder.resolve('a', r'\\b', 'c', 'd'), r'/root/path/a/\\b/c/d'); + expect(context.absolute('a', '/b', '/c', 'd'), '/c/d'); + expect(context.absolute('a', r'c:\b', 'c', 'd'), + r'/root/path/a/c:\b/c/d'); + expect(context.absolute('a', r'\\b', 'c', 'd'), r'/root/path/a/\\b/c/d'); }); }); test('withoutExtension', () { - expect(builder.withoutExtension(''), ''); - expect(builder.withoutExtension('a'), 'a'); - expect(builder.withoutExtension('.a'), '.a'); - expect(builder.withoutExtension('a.b'), 'a'); - expect(builder.withoutExtension('a/b.c'), 'a/b'); - expect(builder.withoutExtension('a/b.c.d'), 'a/b.c'); - expect(builder.withoutExtension('a/'), 'a/'); - expect(builder.withoutExtension('a/b/'), 'a/b/'); - expect(builder.withoutExtension('a/.'), 'a/.'); - expect(builder.withoutExtension('a/.b'), 'a/.b'); - expect(builder.withoutExtension('a.b/c'), 'a.b/c'); - expect(builder.withoutExtension(r'a.b\c'), r'a'); - expect(builder.withoutExtension(r'a/b\c'), r'a/b\c'); - expect(builder.withoutExtension(r'a/b\c.d'), r'a/b\c'); - expect(builder.withoutExtension('a/b.c/'), 'a/b/'); - expect(builder.withoutExtension('a/b.c//'), 'a/b//'); + expect(context.withoutExtension(''), ''); + expect(context.withoutExtension('a'), 'a'); + expect(context.withoutExtension('.a'), '.a'); + expect(context.withoutExtension('a.b'), 'a'); + expect(context.withoutExtension('a/b.c'), 'a/b'); + expect(context.withoutExtension('a/b.c.d'), 'a/b.c'); + expect(context.withoutExtension('a/'), 'a/'); + expect(context.withoutExtension('a/b/'), 'a/b/'); + expect(context.withoutExtension('a/.'), 'a/.'); + expect(context.withoutExtension('a/.b'), 'a/.b'); + expect(context.withoutExtension('a.b/c'), 'a.b/c'); + expect(context.withoutExtension(r'a.b\c'), r'a'); + expect(context.withoutExtension(r'a/b\c'), r'a/b\c'); + expect(context.withoutExtension(r'a/b\c.d'), r'a/b\c'); + expect(context.withoutExtension('a/b.c/'), 'a/b/'); + expect(context.withoutExtension('a/b.c//'), 'a/b//'); }); test('fromUri', () { - expect(builder.fromUri(Uri.parse('file:///path/to/foo')), '/path/to/foo'); - expect(builder.fromUri(Uri.parse('file:///path/to/foo/')), '/path/to/foo/'); - expect(builder.fromUri(Uri.parse('file:///')), '/'); - expect(builder.fromUri(Uri.parse('foo/bar')), 'foo/bar'); - expect(builder.fromUri(Uri.parse('/path/to/foo')), '/path/to/foo'); - expect(builder.fromUri(Uri.parse('///path/to/foo')), '/path/to/foo'); - expect(builder.fromUri(Uri.parse('file:///path/to/foo%23bar')), + expect(context.fromUri(Uri.parse('file:///path/to/foo')), '/path/to/foo'); + expect(context.fromUri(Uri.parse('file:///path/to/foo/')), '/path/to/foo/'); + expect(context.fromUri(Uri.parse('file:///')), '/'); + expect(context.fromUri(Uri.parse('foo/bar')), 'foo/bar'); + expect(context.fromUri(Uri.parse('/path/to/foo')), '/path/to/foo'); + expect(context.fromUri(Uri.parse('///path/to/foo')), '/path/to/foo'); + expect(context.fromUri(Uri.parse('file:///path/to/foo%23bar')), '/path/to/foo#bar'); - expect(builder.fromUri(Uri.parse('_%7B_%7D_%60_%5E_%20_%22_%25_')), + expect(context.fromUri(Uri.parse('_%7B_%7D_%60_%5E_%20_%22_%25_')), r'_{_}_`_^_ _"_%_'); - expect(() => builder.fromUri(Uri.parse('http://dartlang.org')), + expect(() => context.fromUri(Uri.parse('http://dartlang.org')), throwsArgumentError); }); test('toUri', () { - expect(builder.toUri('/path/to/foo'), Uri.parse('file:///path/to/foo')); - expect(builder.toUri('/path/to/foo/'), Uri.parse('file:///path/to/foo/')); - expect(builder.toUri('/'), Uri.parse('file:///')); - expect(builder.toUri('foo/bar'), Uri.parse('foo/bar')); - expect(builder.toUri('/path/to/foo#bar'), + expect(context.toUri('/path/to/foo'), Uri.parse('file:///path/to/foo')); + expect(context.toUri('/path/to/foo/'), Uri.parse('file:///path/to/foo/')); + expect(context.toUri('/'), Uri.parse('file:///')); + expect(context.toUri('foo/bar'), Uri.parse('foo/bar')); + expect(context.toUri('/path/to/foo#bar'), Uri.parse('file:///path/to/foo%23bar')); - expect(builder.toUri(r'/_{_}_`_^_ _"_%_'), + expect(context.toUri(r'/_{_}_`_^_ _"_%_'), Uri.parse('file:///_%7B_%7D_%60_%5E_%20_%22_%25_')); - expect(builder.toUri(r'_{_}_`_^_ _"_%_'), + expect(context.toUri(r'_{_}_`_^_ _"_%_'), Uri.parse('_%7B_%7D_%60_%5E_%20_%22_%25_')); }); } diff --git a/pkg/path/test/relative_test.dart b/pkg/path/test/relative_test.dart index e9762f47c54..58163d25bd4 100644 --- a/pkg/path/test/relative_test.dart +++ b/pkg/path/test/relative_test.dart @@ -2,7 +2,7 @@ // 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. // -// Test "relative" on all styles of path.Builder, on all platforms. +// Test "relative" on all styles of path.Context, on all platforms. import "package:unittest/unittest.dart"; import "package:path/path.dart" as path; @@ -11,27 +11,27 @@ import "utils.dart"; void main() { test("test relative", () { - relativeTest(new path.Builder(style: path.Style.posix, root: '.'), '/'); - relativeTest(new path.Builder(style: path.Style.posix, root: '/'), '/'); - relativeTest(new path.Builder(style: path.Style.windows, root: r'd:\'), + relativeTest(new path.Context(style: path.Style.posix, current: '.'), '/'); + relativeTest(new path.Context(style: path.Style.posix, current: '/'), '/'); + relativeTest(new path.Context(style: path.Style.windows, current: r'd:\'), r'c:\'); - relativeTest(new path.Builder(style: path.Style.windows, root: '.'), + relativeTest(new path.Context(style: path.Style.windows, current: '.'), r'c:\'); - relativeTest(new path.Builder(style: path.Style.url, root: 'file:///'), + relativeTest(new path.Context(style: path.Style.url, current: 'file:///'), 'http://myserver/'); - relativeTest(new path.Builder(style: path.Style.url, root: '.'), + relativeTest(new path.Context(style: path.Style.url, current: '.'), 'http://myserver/'); - relativeTest(new path.Builder(style: path.Style.url, root: 'file:///'), + relativeTest(new path.Context(style: path.Style.url, current: 'file:///'), '/'); - relativeTest(new path.Builder(style: path.Style.url, root: '.'), '/'); + relativeTest(new path.Context(style: path.Style.url, current: '.'), '/'); }); } -void relativeTest(path.Builder builder, String prefix) { - var isRelative = (builder.root == '.'); +void relativeTest(path.Context context, String prefix) { + var isRelative = (context.current == '.'); // Cases where the arguments are absolute paths. expectRelative(result, pathArg, fromArg) { - expect(builder.normalize(result), builder.relative(pathArg, from: fromArg)); + expect(context.normalize(result), context.relative(pathArg, from: fromArg)); } expectRelative('c/d', '${prefix}a/b/c/d', '${prefix}a/b'); @@ -77,10 +77,10 @@ void relativeTest(path.Builder builder, String prefix) { // Should always throw - no relative path can be constructed. if (isRelative) { - expect(() => builder.relative('.', from: '..'), throwsPathException); - expect(() => builder.relative('a/b', from: '../../d'), + expect(() => context.relative('.', from: '..'), throwsPathException); + expect(() => context.relative('a/b', from: '../../d'), throwsPathException); - expect(() => builder.relative('a/b', from: '${prefix}a/b'), + expect(() => context.relative('a/b', from: '${prefix}a/b'), throwsPathException); // An absolute path relative from a relative path returns the absolute path. expectRelative('${prefix}a/b', '${prefix}a/b', 'c/d'); diff --git a/pkg/path/test/url_test.dart b/pkg/path/test/url_test.dart index 9fb6f0793a7..e149b4f0e22 100644 --- a/pkg/path/test/url_test.dart +++ b/pkg/path/test/url_test.dart @@ -6,489 +6,489 @@ import 'package:unittest/unittest.dart'; import 'package:path/path.dart' as path; main() { - var builder = new path.Builder(style: path.Style.url, - root: 'http://dartlang.org/root/path'); + var context = new path.Context(style: path.Style.url, + current: 'http://dartlang.org/root/path'); test('separator', () { - expect(builder.separator, '/'); + expect(context.separator, '/'); }); test('extension', () { - expect(builder.extension(''), ''); - expect(builder.extension('foo.dart'), '.dart'); - expect(builder.extension('foo.dart.js'), '.js'); - expect(builder.extension('a.b/c'), ''); - expect(builder.extension('a.b/c.d'), '.d'); - expect(builder.extension(r'a.b\c'), r'.b\c'); - expect(builder.extension('foo.dart/'), '.dart'); - expect(builder.extension('foo.dart//'), '.dart'); + expect(context.extension(''), ''); + expect(context.extension('foo.dart'), '.dart'); + expect(context.extension('foo.dart.js'), '.js'); + expect(context.extension('a.b/c'), ''); + expect(context.extension('a.b/c.d'), '.d'); + expect(context.extension(r'a.b\c'), r'.b\c'); + expect(context.extension('foo.dart/'), '.dart'); + expect(context.extension('foo.dart//'), '.dart'); }); test('rootPrefix', () { - expect(builder.rootPrefix(''), ''); - expect(builder.rootPrefix('a'), ''); - expect(builder.rootPrefix('a/b'), ''); - expect(builder.rootPrefix('http://dartlang.org/a/c'), + expect(context.rootPrefix(''), ''); + expect(context.rootPrefix('a'), ''); + expect(context.rootPrefix('a/b'), ''); + expect(context.rootPrefix('http://dartlang.org/a/c'), 'http://dartlang.org'); - expect(builder.rootPrefix('file:///a/c'), 'file://'); - expect(builder.rootPrefix('/a/c'), '/'); - expect(builder.rootPrefix('http://dartlang.org/'), 'http://dartlang.org'); - expect(builder.rootPrefix('file:///'), 'file://'); - expect(builder.rootPrefix('http://dartlang.org'), 'http://dartlang.org'); - expect(builder.rootPrefix('file://'), 'file://'); - expect(builder.rootPrefix('/'), '/'); + expect(context.rootPrefix('file:///a/c'), 'file://'); + expect(context.rootPrefix('/a/c'), '/'); + expect(context.rootPrefix('http://dartlang.org/'), 'http://dartlang.org'); + expect(context.rootPrefix('file:///'), 'file://'); + expect(context.rootPrefix('http://dartlang.org'), 'http://dartlang.org'); + expect(context.rootPrefix('file://'), 'file://'); + expect(context.rootPrefix('/'), '/'); }); test('dirname', () { - expect(builder.dirname(''), '.'); - expect(builder.dirname('a'), '.'); - expect(builder.dirname('a/b'), 'a'); - expect(builder.dirname('a/b/c'), 'a/b'); - expect(builder.dirname('a/b.c'), 'a'); - expect(builder.dirname('a/'), '.'); - expect(builder.dirname('a/.'), 'a'); - expect(builder.dirname(r'a\b/c'), r'a\b'); - expect(builder.dirname('http://dartlang.org/a'), 'http://dartlang.org'); - expect(builder.dirname('file:///a'), 'file://'); - expect(builder.dirname('/a'), '/'); - expect(builder.dirname('http://dartlang.org///a'), 'http://dartlang.org'); - expect(builder.dirname('file://///a'), 'file://'); - expect(builder.dirname('///a'), '/'); - expect(builder.dirname('http://dartlang.org/'), 'http://dartlang.org'); - expect(builder.dirname('http://dartlang.org'), 'http://dartlang.org'); - expect(builder.dirname('file:///'), 'file://'); - expect(builder.dirname('file://'), 'file://'); - expect(builder.dirname('/'), '/'); - expect(builder.dirname('http://dartlang.org///'), 'http://dartlang.org'); - expect(builder.dirname('file://///'), 'file://'); - expect(builder.dirname('///'), '/'); - expect(builder.dirname('a/b/'), 'a'); - expect(builder.dirname(r'a/b\c'), 'a'); - expect(builder.dirname('a//'), '.'); - expect(builder.dirname('a/b//'), 'a'); - expect(builder.dirname('a//b'), 'a'); + expect(context.dirname(''), '.'); + expect(context.dirname('a'), '.'); + expect(context.dirname('a/b'), 'a'); + expect(context.dirname('a/b/c'), 'a/b'); + expect(context.dirname('a/b.c'), 'a'); + expect(context.dirname('a/'), '.'); + expect(context.dirname('a/.'), 'a'); + expect(context.dirname(r'a\b/c'), r'a\b'); + expect(context.dirname('http://dartlang.org/a'), 'http://dartlang.org'); + expect(context.dirname('file:///a'), 'file://'); + expect(context.dirname('/a'), '/'); + expect(context.dirname('http://dartlang.org///a'), 'http://dartlang.org'); + expect(context.dirname('file://///a'), 'file://'); + expect(context.dirname('///a'), '/'); + expect(context.dirname('http://dartlang.org/'), 'http://dartlang.org'); + expect(context.dirname('http://dartlang.org'), 'http://dartlang.org'); + expect(context.dirname('file:///'), 'file://'); + expect(context.dirname('file://'), 'file://'); + expect(context.dirname('/'), '/'); + expect(context.dirname('http://dartlang.org///'), 'http://dartlang.org'); + expect(context.dirname('file://///'), 'file://'); + expect(context.dirname('///'), '/'); + expect(context.dirname('a/b/'), 'a'); + expect(context.dirname(r'a/b\c'), 'a'); + expect(context.dirname('a//'), '.'); + expect(context.dirname('a/b//'), 'a'); + expect(context.dirname('a//b'), 'a'); }); test('basename', () { - expect(builder.basename(''), ''); - expect(builder.basename('a'), 'a'); - expect(builder.basename('a/b'), 'b'); - expect(builder.basename('a/b/c'), 'c'); - expect(builder.basename('a/b.c'), 'b.c'); - expect(builder.basename('a/'), 'a'); - expect(builder.basename('a/.'), '.'); - expect(builder.basename(r'a\b/c'), 'c'); - expect(builder.basename('http://dartlang.org/a'), 'a'); - expect(builder.basename('file:///a'), 'a'); - expect(builder.basename('/a'), 'a'); - expect(builder.basename('http://dartlang.org/'), 'http://dartlang.org'); - expect(builder.basename('http://dartlang.org'), 'http://dartlang.org'); - expect(builder.basename('file:///'), 'file://'); - expect(builder.basename('file://'), 'file://'); - expect(builder.basename('/'), '/'); - expect(builder.basename('a/b/'), 'b'); - expect(builder.basename(r'a/b\c'), r'b\c'); - expect(builder.basename('a//'), 'a'); - expect(builder.basename('a/b//'), 'b'); - expect(builder.basename('a//b'), 'b'); - expect(builder.basename('a b/c d.e f'), 'c d.e f'); + expect(context.basename(''), ''); + expect(context.basename('a'), 'a'); + expect(context.basename('a/b'), 'b'); + expect(context.basename('a/b/c'), 'c'); + expect(context.basename('a/b.c'), 'b.c'); + expect(context.basename('a/'), 'a'); + expect(context.basename('a/.'), '.'); + expect(context.basename(r'a\b/c'), 'c'); + expect(context.basename('http://dartlang.org/a'), 'a'); + expect(context.basename('file:///a'), 'a'); + expect(context.basename('/a'), 'a'); + expect(context.basename('http://dartlang.org/'), 'http://dartlang.org'); + expect(context.basename('http://dartlang.org'), 'http://dartlang.org'); + expect(context.basename('file:///'), 'file://'); + expect(context.basename('file://'), 'file://'); + expect(context.basename('/'), '/'); + expect(context.basename('a/b/'), 'b'); + expect(context.basename(r'a/b\c'), r'b\c'); + expect(context.basename('a//'), 'a'); + expect(context.basename('a/b//'), 'b'); + expect(context.basename('a//b'), 'b'); + expect(context.basename('a b/c d.e f'), 'c d.e f'); }); test('basenameWithoutExtension', () { - expect(builder.basenameWithoutExtension(''), ''); - expect(builder.basenameWithoutExtension('.'), '.'); - expect(builder.basenameWithoutExtension('..'), '..'); - expect(builder.basenameWithoutExtension('a'), 'a'); - expect(builder.basenameWithoutExtension('a/b'), 'b'); - expect(builder.basenameWithoutExtension('a/b/c'), 'c'); - expect(builder.basenameWithoutExtension('a/b.c'), 'b'); - expect(builder.basenameWithoutExtension('a/'), 'a'); - expect(builder.basenameWithoutExtension('a/.'), '.'); - expect(builder.basenameWithoutExtension(r'a/b\c'), r'b\c'); - expect(builder.basenameWithoutExtension('a/.bashrc'), '.bashrc'); - expect(builder.basenameWithoutExtension('a/b/c.d.e'), 'c.d'); - expect(builder.basenameWithoutExtension('a//'), 'a'); - expect(builder.basenameWithoutExtension('a/b//'), 'b'); - expect(builder.basenameWithoutExtension('a//b'), 'b'); - expect(builder.basenameWithoutExtension('a/b.c/'), 'b'); - expect(builder.basenameWithoutExtension('a/b.c//'), 'b'); - expect(builder.basenameWithoutExtension('a/b c.d e.f g'), 'b c.d e'); + expect(context.basenameWithoutExtension(''), ''); + expect(context.basenameWithoutExtension('.'), '.'); + expect(context.basenameWithoutExtension('..'), '..'); + expect(context.basenameWithoutExtension('a'), 'a'); + expect(context.basenameWithoutExtension('a/b'), 'b'); + expect(context.basenameWithoutExtension('a/b/c'), 'c'); + expect(context.basenameWithoutExtension('a/b.c'), 'b'); + expect(context.basenameWithoutExtension('a/'), 'a'); + expect(context.basenameWithoutExtension('a/.'), '.'); + expect(context.basenameWithoutExtension(r'a/b\c'), r'b\c'); + expect(context.basenameWithoutExtension('a/.bashrc'), '.bashrc'); + expect(context.basenameWithoutExtension('a/b/c.d.e'), 'c.d'); + expect(context.basenameWithoutExtension('a//'), 'a'); + expect(context.basenameWithoutExtension('a/b//'), 'b'); + expect(context.basenameWithoutExtension('a//b'), 'b'); + expect(context.basenameWithoutExtension('a/b.c/'), 'b'); + expect(context.basenameWithoutExtension('a/b.c//'), 'b'); + expect(context.basenameWithoutExtension('a/b c.d e.f g'), 'b c.d e'); }); test('isAbsolute', () { - expect(builder.isAbsolute(''), false); - expect(builder.isAbsolute('a'), false); - expect(builder.isAbsolute('a/b'), false); - expect(builder.isAbsolute('http://dartlang.org/a'), true); - expect(builder.isAbsolute('file:///a'), true); - expect(builder.isAbsolute('/a'), true); - expect(builder.isAbsolute('http://dartlang.org/a/b'), true); - expect(builder.isAbsolute('file:///a/b'), true); - expect(builder.isAbsolute('/a/b'), true); - expect(builder.isAbsolute('http://dartlang.org/'), true); - expect(builder.isAbsolute('file:///'), true); - expect(builder.isAbsolute('http://dartlang.org'), true); - expect(builder.isAbsolute('file://'), true); - expect(builder.isAbsolute('/'), true); - expect(builder.isAbsolute('~'), false); - expect(builder.isAbsolute('.'), false); - expect(builder.isAbsolute('../a'), false); - expect(builder.isAbsolute('C:/a'), false); - expect(builder.isAbsolute(r'C:\a'), false); - expect(builder.isAbsolute(r'\\a'), false); + expect(context.isAbsolute(''), false); + expect(context.isAbsolute('a'), false); + expect(context.isAbsolute('a/b'), false); + expect(context.isAbsolute('http://dartlang.org/a'), true); + expect(context.isAbsolute('file:///a'), true); + expect(context.isAbsolute('/a'), true); + expect(context.isAbsolute('http://dartlang.org/a/b'), true); + expect(context.isAbsolute('file:///a/b'), true); + expect(context.isAbsolute('/a/b'), true); + expect(context.isAbsolute('http://dartlang.org/'), true); + expect(context.isAbsolute('file:///'), true); + expect(context.isAbsolute('http://dartlang.org'), true); + expect(context.isAbsolute('file://'), true); + expect(context.isAbsolute('/'), true); + expect(context.isAbsolute('~'), false); + expect(context.isAbsolute('.'), false); + expect(context.isAbsolute('../a'), false); + expect(context.isAbsolute('C:/a'), false); + expect(context.isAbsolute(r'C:\a'), false); + expect(context.isAbsolute(r'\\a'), false); }); test('isRelative', () { - expect(builder.isRelative(''), true); - expect(builder.isRelative('a'), true); - expect(builder.isRelative('a/b'), true); - expect(builder.isRelative('http://dartlang.org/a'), false); - expect(builder.isRelative('file:///a'), false); - expect(builder.isRelative('/a'), false); - expect(builder.isRelative('http://dartlang.org/a/b'), false); - expect(builder.isRelative('file:///a/b'), false); - expect(builder.isRelative('/a/b'), false); - expect(builder.isRelative('http://dartlang.org/'), false); - expect(builder.isRelative('file:///'), false); - expect(builder.isRelative('http://dartlang.org'), false); - expect(builder.isRelative('file://'), false); - expect(builder.isRelative('/'), false); - expect(builder.isRelative('~'), true); - expect(builder.isRelative('.'), true); - expect(builder.isRelative('../a'), true); - expect(builder.isRelative('C:/a'), true); - expect(builder.isRelative(r'C:\a'), true); - expect(builder.isRelative(r'\\a'), true); + expect(context.isRelative(''), true); + expect(context.isRelative('a'), true); + expect(context.isRelative('a/b'), true); + expect(context.isRelative('http://dartlang.org/a'), false); + expect(context.isRelative('file:///a'), false); + expect(context.isRelative('/a'), false); + expect(context.isRelative('http://dartlang.org/a/b'), false); + expect(context.isRelative('file:///a/b'), false); + expect(context.isRelative('/a/b'), false); + expect(context.isRelative('http://dartlang.org/'), false); + expect(context.isRelative('file:///'), false); + expect(context.isRelative('http://dartlang.org'), false); + expect(context.isRelative('file://'), false); + expect(context.isRelative('/'), false); + expect(context.isRelative('~'), true); + expect(context.isRelative('.'), true); + expect(context.isRelative('../a'), true); + expect(context.isRelative('C:/a'), true); + expect(context.isRelative(r'C:\a'), true); + expect(context.isRelative(r'\\a'), true); }); test('isRootRelative', () { - expect(builder.isRootRelative(''), false); - expect(builder.isRootRelative('a'), false); - expect(builder.isRootRelative('a/b'), false); - expect(builder.isRootRelative('http://dartlang.org/a'), false); - expect(builder.isRootRelative('file:///a'), false); - expect(builder.isRootRelative('/a'), true); - expect(builder.isRootRelative('http://dartlang.org/a/b'), false); - expect(builder.isRootRelative('file:///a/b'), false); - expect(builder.isRootRelative('/a/b'), true); - expect(builder.isRootRelative('http://dartlang.org/'), false); - expect(builder.isRootRelative('file:///'), false); - expect(builder.isRootRelative('http://dartlang.org'), false); - expect(builder.isRootRelative('file://'), false); - expect(builder.isRootRelative('/'), true); - expect(builder.isRootRelative('~'), false); - expect(builder.isRootRelative('.'), false); - expect(builder.isRootRelative('../a'), false); - expect(builder.isRootRelative('C:/a'), false); - expect(builder.isRootRelative(r'C:\a'), false); - expect(builder.isRootRelative(r'\\a'), false); + expect(context.isRootRelative(''), false); + expect(context.isRootRelative('a'), false); + expect(context.isRootRelative('a/b'), false); + expect(context.isRootRelative('http://dartlang.org/a'), false); + expect(context.isRootRelative('file:///a'), false); + expect(context.isRootRelative('/a'), true); + expect(context.isRootRelative('http://dartlang.org/a/b'), false); + expect(context.isRootRelative('file:///a/b'), false); + expect(context.isRootRelative('/a/b'), true); + expect(context.isRootRelative('http://dartlang.org/'), false); + expect(context.isRootRelative('file:///'), false); + expect(context.isRootRelative('http://dartlang.org'), false); + expect(context.isRootRelative('file://'), false); + expect(context.isRootRelative('/'), true); + expect(context.isRootRelative('~'), false); + expect(context.isRootRelative('.'), false); + expect(context.isRootRelative('../a'), false); + expect(context.isRootRelative('C:/a'), false); + expect(context.isRootRelative(r'C:\a'), false); + expect(context.isRootRelative(r'\\a'), false); }); group('join', () { test('allows up to eight parts', () { - expect(builder.join('a'), 'a'); - expect(builder.join('a', 'b'), 'a/b'); - expect(builder.join('a', 'b', 'c'), 'a/b/c'); - expect(builder.join('a', 'b', 'c', 'd'), 'a/b/c/d'); - expect(builder.join('a', 'b', 'c', 'd', 'e'), 'a/b/c/d/e'); - expect(builder.join('a', 'b', 'c', 'd', 'e', 'f'), 'a/b/c/d/e/f'); - expect(builder.join('a', 'b', 'c', 'd', 'e', 'f', 'g'), 'a/b/c/d/e/f/g'); - expect(builder.join('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'), + expect(context.join('a'), 'a'); + expect(context.join('a', 'b'), 'a/b'); + expect(context.join('a', 'b', 'c'), 'a/b/c'); + expect(context.join('a', 'b', 'c', 'd'), 'a/b/c/d'); + expect(context.join('a', 'b', 'c', 'd', 'e'), 'a/b/c/d/e'); + expect(context.join('a', 'b', 'c', 'd', 'e', 'f'), 'a/b/c/d/e/f'); + expect(context.join('a', 'b', 'c', 'd', 'e', 'f', 'g'), 'a/b/c/d/e/f/g'); + expect(context.join('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'), 'a/b/c/d/e/f/g/h'); }); test('does not add separator if a part ends in one', () { - expect(builder.join('a/', 'b', 'c/', 'd'), 'a/b/c/d'); - expect(builder.join('a\\', 'b'), r'a\/b'); + expect(context.join('a/', 'b', 'c/', 'd'), 'a/b/c/d'); + expect(context.join('a\\', 'b'), r'a\/b'); }); test('ignores parts before an absolute path', () { - expect(builder.join('a', 'http://dartlang.org', 'b', 'c'), + expect(context.join('a', 'http://dartlang.org', 'b', 'c'), 'http://dartlang.org/b/c'); - expect(builder.join('a', 'file://', 'b', 'c'), 'file:///b/c'); - expect(builder.join('a', '/', 'b', 'c'), '/b/c'); - expect(builder.join('a', '/b', 'http://dartlang.org/c', 'd'), + expect(context.join('a', 'file://', 'b', 'c'), 'file:///b/c'); + expect(context.join('a', '/', 'b', 'c'), '/b/c'); + expect(context.join('a', '/b', 'http://dartlang.org/c', 'd'), 'http://dartlang.org/c/d'); - expect(builder.join( + expect(context.join( 'a', 'http://google.com/b', 'http://dartlang.org/c', 'd'), 'http://dartlang.org/c/d'); - expect(builder.join('a', '/b', '/c', 'd'), '/c/d'); - expect(builder.join('a', r'c:\b', 'c', 'd'), r'a/c:\b/c/d'); - expect(builder.join('a', r'\\b', 'c', 'd'), r'a/\\b/c/d'); + expect(context.join('a', '/b', '/c', 'd'), '/c/d'); + expect(context.join('a', r'c:\b', 'c', 'd'), r'a/c:\b/c/d'); + expect(context.join('a', r'\\b', 'c', 'd'), r'a/\\b/c/d'); }); test('preserves roots before a root-relative path', () { - expect(builder.join('http://dartlang.org', 'a', '/b', 'c'), + expect(context.join('http://dartlang.org', 'a', '/b', 'c'), 'http://dartlang.org/b/c'); - expect(builder.join('file://', 'a', '/b', 'c'), 'file:///b/c'); - expect(builder.join('file://', 'a', '/b', 'c', '/d'), 'file:///d'); + expect(context.join('file://', 'a', '/b', 'c'), 'file:///b/c'); + expect(context.join('file://', 'a', '/b', 'c', '/d'), 'file:///d'); }); test('ignores trailing nulls', () { - expect(builder.join('a', null), equals('a')); - expect(builder.join('a', 'b', 'c', null, null), equals('a/b/c')); + expect(context.join('a', null), equals('a')); + expect(context.join('a', 'b', 'c', null, null), equals('a/b/c')); }); test('ignores empty strings', () { - expect(builder.join(''), ''); - expect(builder.join('', ''), ''); - expect(builder.join('', 'a'), 'a'); - expect(builder.join('a', '', 'b', '', '', '', 'c'), 'a/b/c'); - expect(builder.join('a', 'b', ''), 'a/b'); + expect(context.join(''), ''); + expect(context.join('', ''), ''); + expect(context.join('', 'a'), 'a'); + expect(context.join('a', '', 'b', '', '', '', 'c'), 'a/b/c'); + expect(context.join('a', 'b', ''), 'a/b'); }); test('disallows intermediate nulls', () { - expect(() => builder.join('a', null, 'b'), throwsArgumentError); - expect(() => builder.join(null, 'a'), throwsArgumentError); + expect(() => context.join('a', null, 'b'), throwsArgumentError); + expect(() => context.join(null, 'a'), throwsArgumentError); }); test('Join does not modify internal ., .., or trailing separators', () { - expect(builder.join('a/', 'b/c/'), 'a/b/c/'); - expect(builder.join('a/b/./c/..//', 'd/.././..//e/f//'), + expect(context.join('a/', 'b/c/'), 'a/b/c/'); + expect(context.join('a/b/./c/..//', 'd/.././..//e/f//'), 'a/b/./c/..//d/.././..//e/f//'); - expect(builder.join('a/b', 'c/../../../..'), 'a/b/c/../../../..'); - expect(builder.join('a', 'b${builder.separator}'), 'a/b/'); + expect(context.join('a/b', 'c/../../../..'), 'a/b/c/../../../..'); + expect(context.join('a', 'b${context.separator}'), 'a/b/'); }); }); group('joinAll', () { test('allows more than eight parts', () { - expect(builder.joinAll(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']), + expect(context.joinAll(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']), 'a/b/c/d/e/f/g/h/i'); }); test('ignores parts before an absolute path', () { - expect(builder.joinAll(['a', 'http://dartlang.org', 'b', 'c']), + expect(context.joinAll(['a', 'http://dartlang.org', 'b', 'c']), 'http://dartlang.org/b/c'); - expect(builder.joinAll(['a', 'file://', 'b', 'c']), 'file:///b/c'); - expect(builder.joinAll(['a', '/', 'b', 'c']), '/b/c'); - expect(builder.joinAll(['a', '/b', 'http://dartlang.org/c', 'd']), + expect(context.joinAll(['a', 'file://', 'b', 'c']), 'file:///b/c'); + expect(context.joinAll(['a', '/', 'b', 'c']), '/b/c'); + expect(context.joinAll(['a', '/b', 'http://dartlang.org/c', 'd']), 'http://dartlang.org/c/d'); - expect(builder.joinAll( + expect(context.joinAll( ['a', 'http://google.com/b', 'http://dartlang.org/c', 'd']), 'http://dartlang.org/c/d'); - expect(builder.joinAll(['a', '/b', '/c', 'd']), '/c/d'); - expect(builder.joinAll(['a', r'c:\b', 'c', 'd']), r'a/c:\b/c/d'); - expect(builder.joinAll(['a', r'\\b', 'c', 'd']), r'a/\\b/c/d'); + expect(context.joinAll(['a', '/b', '/c', 'd']), '/c/d'); + expect(context.joinAll(['a', r'c:\b', 'c', 'd']), r'a/c:\b/c/d'); + expect(context.joinAll(['a', r'\\b', 'c', 'd']), r'a/\\b/c/d'); }); test('preserves roots before a root-relative path', () { - expect(builder.joinAll(['http://dartlang.org', 'a', '/b', 'c']), + expect(context.joinAll(['http://dartlang.org', 'a', '/b', 'c']), 'http://dartlang.org/b/c'); - expect(builder.joinAll(['file://', 'a', '/b', 'c']), 'file:///b/c'); - expect(builder.joinAll(['file://', 'a', '/b', 'c', '/d']), 'file:///d'); + expect(context.joinAll(['file://', 'a', '/b', 'c']), 'file:///b/c'); + expect(context.joinAll(['file://', 'a', '/b', 'c', '/d']), 'file:///d'); }); }); group('split', () { test('simple cases', () { - expect(builder.split(''), []); - expect(builder.split('.'), ['.']); - expect(builder.split('..'), ['..']); - expect(builder.split('foo'), equals(['foo'])); - expect(builder.split('foo/bar.txt'), equals(['foo', 'bar.txt'])); - expect(builder.split('foo/bar/baz'), equals(['foo', 'bar', 'baz'])); - expect(builder.split('foo/../bar/./baz'), + expect(context.split(''), []); + expect(context.split('.'), ['.']); + expect(context.split('..'), ['..']); + expect(context.split('foo'), equals(['foo'])); + expect(context.split('foo/bar.txt'), equals(['foo', 'bar.txt'])); + expect(context.split('foo/bar/baz'), equals(['foo', 'bar', 'baz'])); + expect(context.split('foo/../bar/./baz'), equals(['foo', '..', 'bar', '.', 'baz'])); - expect(builder.split('foo//bar///baz'), equals(['foo', 'bar', 'baz'])); - expect(builder.split('foo/\\/baz'), equals(['foo', '\\', 'baz'])); - expect(builder.split('.'), equals(['.'])); - expect(builder.split(''), equals([])); - expect(builder.split('foo/'), equals(['foo'])); - expect(builder.split('http://dartlang.org//'), + expect(context.split('foo//bar///baz'), equals(['foo', 'bar', 'baz'])); + expect(context.split('foo/\\/baz'), equals(['foo', '\\', 'baz'])); + expect(context.split('.'), equals(['.'])); + expect(context.split(''), equals([])); + expect(context.split('foo/'), equals(['foo'])); + expect(context.split('http://dartlang.org//'), equals(['http://dartlang.org'])); - expect(builder.split('file:////'), equals(['file://'])); - expect(builder.split('//'), equals(['/'])); + expect(context.split('file:////'), equals(['file://'])); + expect(context.split('//'), equals(['/'])); }); test('includes the root for absolute paths', () { - expect(builder.split('http://dartlang.org/foo/bar/baz'), + expect(context.split('http://dartlang.org/foo/bar/baz'), equals(['http://dartlang.org', 'foo', 'bar', 'baz'])); - expect(builder.split('file:///foo/bar/baz'), + expect(context.split('file:///foo/bar/baz'), equals(['file://', 'foo', 'bar', 'baz'])); - expect(builder.split('/foo/bar/baz'), equals(['/', 'foo', 'bar', 'baz'])); - expect(builder.split('http://dartlang.org/'), + expect(context.split('/foo/bar/baz'), equals(['/', 'foo', 'bar', 'baz'])); + expect(context.split('http://dartlang.org/'), equals(['http://dartlang.org'])); - expect(builder.split('http://dartlang.org'), + expect(context.split('http://dartlang.org'), equals(['http://dartlang.org'])); - expect(builder.split('file:///'), equals(['file://'])); - expect(builder.split('file://'), equals(['file://'])); - expect(builder.split('/'), equals(['/'])); + expect(context.split('file:///'), equals(['file://'])); + expect(context.split('file://'), equals(['file://'])); + expect(context.split('/'), equals(['/'])); }); }); group('normalize', () { test('simple cases', () { - expect(builder.normalize(''), '.'); - expect(builder.normalize('.'), '.'); - expect(builder.normalize('..'), '..'); - expect(builder.normalize('a'), 'a'); - expect(builder.normalize('http://dartlang.org/'), 'http://dartlang.org'); - expect(builder.normalize('http://dartlang.org'), 'http://dartlang.org'); - expect(builder.normalize('file://'), 'file://'); - expect(builder.normalize('file:///'), 'file://'); - expect(builder.normalize('/'), '/'); - expect(builder.normalize(r'\'), r'\'); - expect(builder.normalize('C:/'), 'C:'); - expect(builder.normalize(r'C:\'), r'C:\'); - expect(builder.normalize(r'\\'), r'\\'); - expect(builder.normalize('a/./\xc5\u0bf8-;\u{1f085}\u{00}/c/d/../'), + expect(context.normalize(''), '.'); + expect(context.normalize('.'), '.'); + expect(context.normalize('..'), '..'); + expect(context.normalize('a'), 'a'); + expect(context.normalize('http://dartlang.org/'), 'http://dartlang.org'); + expect(context.normalize('http://dartlang.org'), 'http://dartlang.org'); + expect(context.normalize('file://'), 'file://'); + expect(context.normalize('file:///'), 'file://'); + expect(context.normalize('/'), '/'); + expect(context.normalize(r'\'), r'\'); + expect(context.normalize('C:/'), 'C:'); + expect(context.normalize(r'C:\'), r'C:\'); + expect(context.normalize(r'\\'), r'\\'); + expect(context.normalize('a/./\xc5\u0bf8-;\u{1f085}\u{00}/c/d/../'), 'a/\xc5\u0bf8-;\u{1f085}\u{00}/c'); }); test('collapses redundant separators', () { - expect(builder.normalize(r'a/b/c'), r'a/b/c'); - expect(builder.normalize(r'a//b///c////d'), r'a/b/c/d'); + expect(context.normalize(r'a/b/c'), r'a/b/c'); + expect(context.normalize(r'a//b///c////d'), r'a/b/c/d'); }); test('does not collapse separators for other platform', () { - expect(builder.normalize(r'a\\b\\\c'), r'a\\b\\\c'); + expect(context.normalize(r'a\\b\\\c'), r'a\\b\\\c'); }); test('eliminates "." parts', () { - expect(builder.normalize('./'), '.'); - expect(builder.normalize('http://dartlang.org/.'), 'http://dartlang.org'); - expect(builder.normalize('file:///.'), 'file://'); - expect(builder.normalize('/.'), '/'); - expect(builder.normalize('http://dartlang.org/./'), + expect(context.normalize('./'), '.'); + expect(context.normalize('http://dartlang.org/.'), 'http://dartlang.org'); + expect(context.normalize('file:///.'), 'file://'); + expect(context.normalize('/.'), '/'); + expect(context.normalize('http://dartlang.org/./'), 'http://dartlang.org'); - expect(builder.normalize('file:///./'), 'file://'); - expect(builder.normalize('/./'), '/'); - expect(builder.normalize('./.'), '.'); - expect(builder.normalize('a/./b'), 'a/b'); - expect(builder.normalize('a/.b/c'), 'a/.b/c'); - expect(builder.normalize('a/././b/./c'), 'a/b/c'); - expect(builder.normalize('././a'), 'a'); - expect(builder.normalize('a/./.'), 'a'); + expect(context.normalize('file:///./'), 'file://'); + expect(context.normalize('/./'), '/'); + expect(context.normalize('./.'), '.'); + expect(context.normalize('a/./b'), 'a/b'); + expect(context.normalize('a/.b/c'), 'a/.b/c'); + expect(context.normalize('a/././b/./c'), 'a/b/c'); + expect(context.normalize('././a'), 'a'); + expect(context.normalize('a/./.'), 'a'); }); test('eliminates ".." parts', () { - expect(builder.normalize('..'), '..'); - expect(builder.normalize('../'), '..'); - expect(builder.normalize('../../..'), '../../..'); - expect(builder.normalize('../../../'), '../../..'); - expect(builder.normalize('http://dartlang.org/..'), + expect(context.normalize('..'), '..'); + expect(context.normalize('../'), '..'); + expect(context.normalize('../../..'), '../../..'); + expect(context.normalize('../../../'), '../../..'); + expect(context.normalize('http://dartlang.org/..'), 'http://dartlang.org'); - expect(builder.normalize('file:///..'), 'file://'); - expect(builder.normalize('/..'), '/'); - expect(builder.normalize('http://dartlang.org/../../..'), + expect(context.normalize('file:///..'), 'file://'); + expect(context.normalize('/..'), '/'); + expect(context.normalize('http://dartlang.org/../../..'), 'http://dartlang.org'); - expect(builder.normalize('file:///../../..'), 'file://'); - expect(builder.normalize('/../../..'), '/'); - expect(builder.normalize('http://dartlang.org/../../../a'), + expect(context.normalize('file:///../../..'), 'file://'); + expect(context.normalize('/../../..'), '/'); + expect(context.normalize('http://dartlang.org/../../../a'), 'http://dartlang.org/a'); - expect(builder.normalize('file:///../../../a'), 'file:///a'); - expect(builder.normalize('/../../../a'), '/a'); - expect(builder.normalize('c:/..'), '.'); - expect(builder.normalize('A:/../../..'), '../..'); - expect(builder.normalize('a/..'), '.'); - expect(builder.normalize('a/b/..'), 'a'); - expect(builder.normalize('a/../b'), 'b'); - expect(builder.normalize('a/./../b'), 'b'); - expect(builder.normalize('a/b/c/../../d/e/..'), 'a/d'); - expect(builder.normalize('a/b/../../../../c'), '../../c'); - expect(builder.normalize('z/a/b/../../..\../c'), 'z/..\../c'); - expect(builder.normalize('a/b\c/../d'), 'a/d'); + expect(context.normalize('file:///../../../a'), 'file:///a'); + expect(context.normalize('/../../../a'), '/a'); + expect(context.normalize('c:/..'), '.'); + expect(context.normalize('A:/../../..'), '../..'); + expect(context.normalize('a/..'), '.'); + expect(context.normalize('a/b/..'), 'a'); + expect(context.normalize('a/../b'), 'b'); + expect(context.normalize('a/./../b'), 'b'); + expect(context.normalize('a/b/c/../../d/e/..'), 'a/d'); + expect(context.normalize('a/b/../../../../c'), '../../c'); + expect(context.normalize('z/a/b/../../..\../c'), 'z/..\../c'); + expect(context.normalize('a/b\c/../d'), 'a/d'); }); test('does not walk before root on absolute paths', () { - expect(builder.normalize('..'), '..'); - expect(builder.normalize('../'), '..'); - expect(builder.normalize('http://dartlang.org/..'), + expect(context.normalize('..'), '..'); + expect(context.normalize('../'), '..'); + expect(context.normalize('http://dartlang.org/..'), 'http://dartlang.org'); - expect(builder.normalize('http://dartlang.org/../a'), + expect(context.normalize('http://dartlang.org/../a'), 'http://dartlang.org/a'); - expect(builder.normalize('file:///..'), 'file://'); - expect(builder.normalize('file:///../a'), 'file:///a'); - expect(builder.normalize('/..'), '/'); - expect(builder.normalize('a/..'), '.'); - expect(builder.normalize('../a'), '../a'); - expect(builder.normalize('/../a'), '/a'); - expect(builder.normalize('c:/../a'), 'a'); - expect(builder.normalize('/../a'), '/a'); - expect(builder.normalize('a/b/..'), 'a'); - expect(builder.normalize('../a/b/..'), '../a'); - expect(builder.normalize('a/../b'), 'b'); - expect(builder.normalize('a/./../b'), 'b'); - expect(builder.normalize('a/b/c/../../d/e/..'), 'a/d'); - expect(builder.normalize('a/b/../../../../c'), '../../c'); - expect(builder.normalize('a/b/c/../../..d/./.e/f././'), 'a/..d/.e/f.'); + expect(context.normalize('file:///..'), 'file://'); + expect(context.normalize('file:///../a'), 'file:///a'); + expect(context.normalize('/..'), '/'); + expect(context.normalize('a/..'), '.'); + expect(context.normalize('../a'), '../a'); + expect(context.normalize('/../a'), '/a'); + expect(context.normalize('c:/../a'), 'a'); + expect(context.normalize('/../a'), '/a'); + expect(context.normalize('a/b/..'), 'a'); + expect(context.normalize('../a/b/..'), '../a'); + expect(context.normalize('a/../b'), 'b'); + expect(context.normalize('a/./../b'), 'b'); + expect(context.normalize('a/b/c/../../d/e/..'), 'a/d'); + expect(context.normalize('a/b/../../../../c'), '../../c'); + expect(context.normalize('a/b/c/../../..d/./.e/f././'), 'a/..d/.e/f.'); }); test('removes trailing separators', () { - expect(builder.normalize('./'), '.'); - expect(builder.normalize('.//'), '.'); - expect(builder.normalize('a/'), 'a'); - expect(builder.normalize('a/b/'), 'a/b'); - expect(builder.normalize(r'a/b\'), r'a/b\'); - expect(builder.normalize('a/b///'), 'a/b'); + expect(context.normalize('./'), '.'); + expect(context.normalize('.//'), '.'); + expect(context.normalize('a/'), 'a'); + expect(context.normalize('a/b/'), 'a/b'); + expect(context.normalize(r'a/b\'), r'a/b\'); + expect(context.normalize('a/b///'), 'a/b'); }); }); group('relative', () { group('from absolute root', () { test('given absolute path in root', () { - expect(builder.relative('http://dartlang.org'), '../..'); - expect(builder.relative('http://dartlang.org/'), '../..'); - expect(builder.relative('/'), '../..'); - expect(builder.relative('http://dartlang.org/root'), '..'); - expect(builder.relative('/root'), '..'); - expect(builder.relative('http://dartlang.org/root/path'), '.'); - expect(builder.relative('/root/path'), '.'); - expect(builder.relative('http://dartlang.org/root/path/a'), 'a'); - expect(builder.relative('/root/path/a'), 'a'); - expect(builder.relative('http://dartlang.org/root/path/a/b.txt'), + expect(context.relative('http://dartlang.org'), '../..'); + expect(context.relative('http://dartlang.org/'), '../..'); + expect(context.relative('/'), '../..'); + expect(context.relative('http://dartlang.org/root'), '..'); + expect(context.relative('/root'), '..'); + expect(context.relative('http://dartlang.org/root/path'), '.'); + expect(context.relative('/root/path'), '.'); + expect(context.relative('http://dartlang.org/root/path/a'), 'a'); + expect(context.relative('/root/path/a'), 'a'); + expect(context.relative('http://dartlang.org/root/path/a/b.txt'), 'a/b.txt'); - expect(builder.relative('/root/path/a/b.txt'), 'a/b.txt'); - expect(builder.relative('http://dartlang.org/root/a/b.txt'), + expect(context.relative('/root/path/a/b.txt'), 'a/b.txt'); + expect(context.relative('http://dartlang.org/root/a/b.txt'), '../a/b.txt'); - expect(builder.relative('/root/a/b.txt'), '../a/b.txt'); + expect(context.relative('/root/a/b.txt'), '../a/b.txt'); }); test('given absolute path outside of root', () { - expect(builder.relative('http://dartlang.org/a/b'), '../../a/b'); - expect(builder.relative('/a/b'), '../../a/b'); - expect(builder.relative('http://dartlang.org/root/path/a'), 'a'); - expect(builder.relative('/root/path/a'), 'a'); - expect(builder.relative('http://dartlang.org/root/path/a/b.txt'), + expect(context.relative('http://dartlang.org/a/b'), '../../a/b'); + expect(context.relative('/a/b'), '../../a/b'); + expect(context.relative('http://dartlang.org/root/path/a'), 'a'); + expect(context.relative('/root/path/a'), 'a'); + expect(context.relative('http://dartlang.org/root/path/a/b.txt'), 'a/b.txt'); - expect(builder.relative('http://dartlang.org/root/path/a/b.txt'), + expect(context.relative('http://dartlang.org/root/path/a/b.txt'), 'a/b.txt'); - expect(builder.relative('http://dartlang.org/root/a/b.txt'), + expect(context.relative('http://dartlang.org/root/a/b.txt'), '../a/b.txt'); }); test('given absolute path with different hostname/protocol', () { - expect(builder.relative(r'http://google.com/a/b'), + expect(context.relative(r'http://google.com/a/b'), r'http://google.com/a/b'); - expect(builder.relative(r'file:///a/b'), + expect(context.relative(r'file:///a/b'), r'file:///a/b'); }); test('given relative path', () { // The path is considered relative to the root, so it basically just // normalizes. - expect(builder.relative(''), '.'); - expect(builder.relative('.'), '.'); - expect(builder.relative('a'), 'a'); - expect(builder.relative('a/b.txt'), 'a/b.txt'); - expect(builder.relative('../a/b.txt'), '../a/b.txt'); - expect(builder.relative('a/./b/../c.txt'), 'a/c.txt'); + expect(context.relative(''), '.'); + expect(context.relative('.'), '.'); + expect(context.relative('a'), 'a'); + expect(context.relative('a/b.txt'), 'a/b.txt'); + expect(context.relative('../a/b.txt'), '../a/b.txt'); + expect(context.relative('a/./b/../c.txt'), 'a/c.txt'); }); // Regression test('from root-only path', () { - expect(builder.relative('http://dartlang.org', + expect(context.relative('http://dartlang.org', from: 'http://dartlang.org'), '.'); - expect(builder.relative('http://dartlang.org/root/path', + expect(context.relative('http://dartlang.org/root/path', from: 'http://dartlang.org'), 'root/path'); }); }); group('from relative root', () { - var r = new path.Builder(style: path.Style.url, root: 'foo/bar'); + var r = new path.Context(style: path.Style.url, current: 'foo/bar'); test('given absolute path', () { expect(r.relative('http://google.com/'), equals('http://google.com')); @@ -513,7 +513,7 @@ main() { }); group('from root-relative root', () { - var r = new path.Builder(style: path.Style.url, root: '/foo/bar'); + var r = new path.Context(style: path.Style.url, current: '/foo/bar'); test('given absolute path', () { expect(r.relative('http://google.com/'), equals('http://google.com')); @@ -538,50 +538,50 @@ main() { }); test('from a root with extension', () { - var r = new path.Builder(style: path.Style.url, root: '/dir.ext'); + var r = new path.Context(style: path.Style.url, current: '/dir.ext'); expect(r.relative('/dir.ext/file'), 'file'); }); test('with a root parameter', () { - expect(builder.relative('/foo/bar/baz', from: '/foo/bar'), equals('baz')); + expect(context.relative('/foo/bar/baz', from: '/foo/bar'), equals('baz')); expect( - builder.relative('/foo/bar/baz', from: 'http://dartlang.org/foo/bar'), + context.relative('/foo/bar/baz', from: 'http://dartlang.org/foo/bar'), equals('baz')); expect( - builder.relative('http://dartlang.org/foo/bar/baz', from: '/foo/bar'), + context.relative('http://dartlang.org/foo/bar/baz', from: '/foo/bar'), equals('baz')); - expect(builder.relative('http://dartlang.org/foo/bar/baz', + expect(context.relative('http://dartlang.org/foo/bar/baz', from: 'file:///foo/bar'), equals('http://dartlang.org/foo/bar/baz')); - expect(builder.relative('http://dartlang.org/foo/bar/baz', + expect(context.relative('http://dartlang.org/foo/bar/baz', from: 'http://dartlang.org/foo/bar'), equals('baz')); expect( - builder.relative('/foo/bar/baz', from: 'file:///foo/bar'), + context.relative('/foo/bar/baz', from: 'file:///foo/bar'), equals('http://dartlang.org/foo/bar/baz')); expect( - builder.relative('file:///foo/bar/baz', from: '/foo/bar'), + context.relative('file:///foo/bar/baz', from: '/foo/bar'), equals('file:///foo/bar/baz')); - expect(builder.relative('..', from: '/foo/bar'), equals('../../root')); - expect(builder.relative('..', from: 'http://dartlang.org/foo/bar'), + expect(context.relative('..', from: '/foo/bar'), equals('../../root')); + expect(context.relative('..', from: 'http://dartlang.org/foo/bar'), equals('../../root')); - expect(builder.relative('..', from: 'file:///foo/bar'), + expect(context.relative('..', from: 'file:///foo/bar'), equals('http://dartlang.org/root')); - expect(builder.relative('..', from: '/foo/bar'), equals('../../root')); + expect(context.relative('..', from: '/foo/bar'), equals('../../root')); - expect(builder.relative('http://dartlang.org/foo/bar/baz', + expect(context.relative('http://dartlang.org/foo/bar/baz', from: 'foo/bar'), equals('../../../../foo/bar/baz')); - expect(builder.relative('file:///foo/bar/baz', from: 'foo/bar'), + expect(context.relative('file:///foo/bar/baz', from: 'foo/bar'), equals('file:///foo/bar/baz')); - expect(builder.relative('/foo/bar/baz', from: 'foo/bar'), + expect(context.relative('/foo/bar/baz', from: 'foo/bar'), equals('../../../../foo/bar/baz')); - expect(builder.relative('..', from: 'foo/bar'), equals('../../..')); + expect(context.relative('..', from: 'foo/bar'), equals('../../..')); }); test('with a root parameter and a relative root', () { - var r = new path.Builder(style: path.Style.url, root: 'relative/root'); + var r = new path.Context(style: path.Style.url, current: 'relative/root'); expect(r.relative('/foo/bar/baz', from: '/foo/bar'), equals('baz')); expect( r.relative('/foo/bar/baz', from: 'http://dartlang.org/foo/bar'), @@ -606,7 +606,7 @@ main() { }); test('from a . root', () { - var r = new path.Builder(style: path.Style.url, root: '.'); + var r = new path.Context(style: path.Style.url, current: '.'); expect(r.relative('http://dartlang.org/foo/bar/baz'), equals('http://dartlang.org/foo/bar/baz')); expect(r.relative('file:///foo/bar/baz'), equals('file:///foo/bar/baz')); @@ -617,120 +617,120 @@ main() { group('isWithin', () { test('simple cases', () { - expect(builder.isWithin('foo/bar', 'foo/bar'), isFalse); - expect(builder.isWithin('foo/bar', 'foo/bar/baz'), isTrue); - expect(builder.isWithin('foo/bar', 'foo/baz'), isFalse); - expect(builder.isWithin('foo/bar', '../path/foo/bar/baz'), isTrue); - expect(builder.isWithin( + expect(context.isWithin('foo/bar', 'foo/bar'), isFalse); + expect(context.isWithin('foo/bar', 'foo/bar/baz'), isTrue); + expect(context.isWithin('foo/bar', 'foo/baz'), isFalse); + expect(context.isWithin('foo/bar', '../path/foo/bar/baz'), isTrue); + expect(context.isWithin( 'http://dartlang.org', 'http://dartlang.org/foo/bar'), isTrue); - expect(builder.isWithin( + expect(context.isWithin( 'http://dartlang.org', 'http://pub.dartlang.org/foo/bar'), isFalse); - expect(builder.isWithin('http://dartlang.org', '/foo/bar'), isTrue); - expect(builder.isWithin('http://dartlang.org/foo', '/foo/bar'), isTrue); - expect(builder.isWithin('http://dartlang.org/foo', '/bar/baz'), isFalse); - expect(builder.isWithin('baz', 'http://dartlang.org/root/path/baz/bang'), + expect(context.isWithin('http://dartlang.org', '/foo/bar'), isTrue); + expect(context.isWithin('http://dartlang.org/foo', '/foo/bar'), isTrue); + expect(context.isWithin('http://dartlang.org/foo', '/bar/baz'), isFalse); + expect(context.isWithin('baz', 'http://dartlang.org/root/path/baz/bang'), isTrue); - expect(builder.isWithin('baz', 'http://dartlang.org/root/path/bang/baz'), + expect(context.isWithin('baz', 'http://dartlang.org/root/path/bang/baz'), isFalse); }); test('from a relative root', () { - var r = new path.Builder(style: path.Style.url, root: 'foo/bar'); - expect(builder.isWithin('.', 'a/b/c'), isTrue); - expect(builder.isWithin('.', '../a/b/c'), isFalse); - expect(builder.isWithin('.', '../../a/foo/b/c'), isFalse); - expect(builder.isWithin( + var r = new path.Context(style: path.Style.url, current: 'foo/bar'); + expect(context.isWithin('.', 'a/b/c'), isTrue); + expect(context.isWithin('.', '../a/b/c'), isFalse); + expect(context.isWithin('.', '../../a/foo/b/c'), isFalse); + expect(context.isWithin( 'http://dartlang.org/', 'http://dartlang.org/baz/bang'), isTrue); - expect(builder.isWithin('.', 'http://dartlang.org/baz/bang'), isFalse); + expect(context.isWithin('.', 'http://dartlang.org/baz/bang'), isFalse); }); }); - group('resolve', () { + group('absolute', () { test('allows up to seven parts', () { - expect(builder.resolve('a'), 'http://dartlang.org/root/path/a'); - expect(builder.resolve('a', 'b'), 'http://dartlang.org/root/path/a/b'); - expect(builder.resolve('a', 'b', 'c'), + expect(context.absolute('a'), 'http://dartlang.org/root/path/a'); + expect(context.absolute('a', 'b'), 'http://dartlang.org/root/path/a/b'); + expect(context.absolute('a', 'b', 'c'), 'http://dartlang.org/root/path/a/b/c'); - expect(builder.resolve('a', 'b', 'c', 'd'), + expect(context.absolute('a', 'b', 'c', 'd'), 'http://dartlang.org/root/path/a/b/c/d'); - expect(builder.resolve('a', 'b', 'c', 'd', 'e'), + expect(context.absolute('a', 'b', 'c', 'd', 'e'), 'http://dartlang.org/root/path/a/b/c/d/e'); - expect(builder.resolve('a', 'b', 'c', 'd', 'e', 'f'), + expect(context.absolute('a', 'b', 'c', 'd', 'e', 'f'), 'http://dartlang.org/root/path/a/b/c/d/e/f'); - expect(builder.resolve('a', 'b', 'c', 'd', 'e', 'f', 'g'), + expect(context.absolute('a', 'b', 'c', 'd', 'e', 'f', 'g'), 'http://dartlang.org/root/path/a/b/c/d/e/f/g'); }); test('does not add separator if a part ends in one', () { - expect(builder.resolve('a/', 'b', 'c/', 'd'), + expect(context.absolute('a/', 'b', 'c/', 'd'), 'http://dartlang.org/root/path/a/b/c/d'); - expect(builder.resolve(r'a\', 'b'), + expect(context.absolute(r'a\', 'b'), r'http://dartlang.org/root/path/a\/b'); }); test('ignores parts before an absolute path', () { - expect(builder.resolve('a', '/b', '/c', 'd'), 'http://dartlang.org/c/d'); - expect(builder.resolve('a', '/b', 'file:///c', 'd'), 'file:///c/d'); - expect(builder.resolve('a', r'c:\b', 'c', 'd'), + expect(context.absolute('a', '/b', '/c', 'd'), 'http://dartlang.org/c/d'); + expect(context.absolute('a', '/b', 'file:///c', 'd'), 'file:///c/d'); + expect(context.absolute('a', r'c:\b', 'c', 'd'), r'http://dartlang.org/root/path/a/c:\b/c/d'); - expect(builder.resolve('a', r'\\b', 'c', 'd'), + expect(context.absolute('a', r'\\b', 'c', 'd'), r'http://dartlang.org/root/path/a/\\b/c/d'); }); }); test('withoutExtension', () { - expect(builder.withoutExtension(''), ''); - expect(builder.withoutExtension('a'), 'a'); - expect(builder.withoutExtension('.a'), '.a'); - expect(builder.withoutExtension('a.b'), 'a'); - expect(builder.withoutExtension('a/b.c'), 'a/b'); - expect(builder.withoutExtension('a/b.c.d'), 'a/b.c'); - expect(builder.withoutExtension('a/'), 'a/'); - expect(builder.withoutExtension('a/b/'), 'a/b/'); - expect(builder.withoutExtension('a/.'), 'a/.'); - expect(builder.withoutExtension('a/.b'), 'a/.b'); - expect(builder.withoutExtension('a.b/c'), 'a.b/c'); - expect(builder.withoutExtension(r'a.b\c'), r'a'); - expect(builder.withoutExtension(r'a/b\c'), r'a/b\c'); - expect(builder.withoutExtension(r'a/b\c.d'), r'a/b\c'); - expect(builder.withoutExtension('a/b.c/'), 'a/b/'); - expect(builder.withoutExtension('a/b.c//'), 'a/b//'); + expect(context.withoutExtension(''), ''); + expect(context.withoutExtension('a'), 'a'); + expect(context.withoutExtension('.a'), '.a'); + expect(context.withoutExtension('a.b'), 'a'); + expect(context.withoutExtension('a/b.c'), 'a/b'); + expect(context.withoutExtension('a/b.c.d'), 'a/b.c'); + expect(context.withoutExtension('a/'), 'a/'); + expect(context.withoutExtension('a/b/'), 'a/b/'); + expect(context.withoutExtension('a/.'), 'a/.'); + expect(context.withoutExtension('a/.b'), 'a/.b'); + expect(context.withoutExtension('a.b/c'), 'a.b/c'); + expect(context.withoutExtension(r'a.b\c'), r'a'); + expect(context.withoutExtension(r'a/b\c'), r'a/b\c'); + expect(context.withoutExtension(r'a/b\c.d'), r'a/b\c'); + expect(context.withoutExtension('a/b.c/'), 'a/b/'); + expect(context.withoutExtension('a/b.c//'), 'a/b//'); }); test('fromUri', () { - expect(builder.fromUri(Uri.parse('http://dartlang.org/path/to/foo')), + expect(context.fromUri(Uri.parse('http://dartlang.org/path/to/foo')), 'http://dartlang.org/path/to/foo'); - expect(builder.fromUri(Uri.parse('http://dartlang.org/path/to/foo/')), + expect(context.fromUri(Uri.parse('http://dartlang.org/path/to/foo/')), 'http://dartlang.org/path/to/foo/'); - expect(builder.fromUri(Uri.parse('file:///path/to/foo')), + expect(context.fromUri(Uri.parse('file:///path/to/foo')), 'file:///path/to/foo'); - expect(builder.fromUri(Uri.parse('foo/bar')), 'foo/bar'); - expect(builder.fromUri(Uri.parse('http://dartlang.org/path/to/foo%23bar')), + expect(context.fromUri(Uri.parse('foo/bar')), 'foo/bar'); + expect(context.fromUri(Uri.parse('http://dartlang.org/path/to/foo%23bar')), 'http://dartlang.org/path/to/foo%23bar'); // Since the resulting "path" is also a URL, special characters should // remain percent-encoded in the result. - expect(builder.fromUri(Uri.parse('_%7B_%7D_%60_%5E_%20_%22_%25_')), + expect(context.fromUri(Uri.parse('_%7B_%7D_%60_%5E_%20_%22_%25_')), r'_%7B_%7D_%60_%5E_%20_%22_%25_'); }); test('toUri', () { - expect(builder.toUri('http://dartlang.org/path/to/foo'), + expect(context.toUri('http://dartlang.org/path/to/foo'), Uri.parse('http://dartlang.org/path/to/foo')); - expect(builder.toUri('http://dartlang.org/path/to/foo/'), + expect(context.toUri('http://dartlang.org/path/to/foo/'), Uri.parse('http://dartlang.org/path/to/foo/')); - expect(builder.toUri('file:///path/to/foo'), + expect(context.toUri('file:///path/to/foo'), Uri.parse('file:///path/to/foo')); - expect(builder.toUri('foo/bar'), Uri.parse('foo/bar')); - expect(builder.toUri('http://dartlang.org/path/to/foo%23bar'), + expect(context.toUri('foo/bar'), Uri.parse('foo/bar')); + expect(context.toUri('http://dartlang.org/path/to/foo%23bar'), Uri.parse('http://dartlang.org/path/to/foo%23bar')); // Since the input path is also a URI, special characters should already // be percent encoded there too. - expect(builder.toUri(r'http://foo.com/_%7B_%7D_%60_%5E_%20_%22_%25_'), + expect(context.toUri(r'http://foo.com/_%7B_%7D_%60_%5E_%20_%22_%25_'), Uri.parse('http://foo.com/_%7B_%7D_%60_%5E_%20_%22_%25_')); - expect(builder.toUri(r'_%7B_%7D_%60_%5E_%20_%22_%25_'), + expect(context.toUri(r'_%7B_%7D_%60_%5E_%20_%22_%25_'), Uri.parse('_%7B_%7D_%60_%5E_%20_%22_%25_')); }); } diff --git a/pkg/path/test/windows_test.dart b/pkg/path/test/windows_test.dart index f1a2395edb8..e5dcac490ef 100644 --- a/pkg/path/test/windows_test.dart +++ b/pkg/path/test/windows_test.dart @@ -10,437 +10,429 @@ import 'package:path/path.dart' as path; import 'utils.dart'; main() { - var builder = new path.Builder(style: path.Style.windows, - root: r'C:\root\path'); - - if (new path.Builder().style == path.Style.windows) { - group('absolute', () { - expect(path.absolute(r'a\b.txt'), path.join(path.current, r'a\b.txt')); - expect(path.absolute(r'C:\a\b.txt'), r'C:\a\b.txt'); - expect(path.absolute(r'\\a\b.txt'), r'\\a\b.txt'); - }); - } + var context = new path.Context(style: path.Style.windows, + current: r'C:\root\path'); group('separator', () { - expect(builder.separator, '\\'); + expect(context.separator, '\\'); }); test('extension', () { - expect(builder.extension(''), ''); - expect(builder.extension('.'), ''); - expect(builder.extension('..'), ''); - expect(builder.extension('a/..'), ''); - expect(builder.extension('foo.dart'), '.dart'); - expect(builder.extension('foo.dart.js'), '.js'); - expect(builder.extension('foo bar\gule fisk.dart.js'), '.js'); - expect(builder.extension(r'a.b\c'), ''); - expect(builder.extension('a.b/c.d'), '.d'); - expect(builder.extension(r'~\.bashrc'), ''); - expect(builder.extension(r'a.b/c'), r''); - expect(builder.extension(r'foo.dart\'), '.dart'); - expect(builder.extension(r'foo.dart\\'), '.dart'); + expect(context.extension(''), ''); + expect(context.extension('.'), ''); + expect(context.extension('..'), ''); + expect(context.extension('a/..'), ''); + expect(context.extension('foo.dart'), '.dart'); + expect(context.extension('foo.dart.js'), '.js'); + expect(context.extension('foo bar\gule fisk.dart.js'), '.js'); + expect(context.extension(r'a.b\c'), ''); + expect(context.extension('a.b/c.d'), '.d'); + expect(context.extension(r'~\.bashrc'), ''); + expect(context.extension(r'a.b/c'), r''); + expect(context.extension(r'foo.dart\'), '.dart'); + expect(context.extension(r'foo.dart\\'), '.dart'); }); test('rootPrefix', () { - expect(builder.rootPrefix(''), ''); - expect(builder.rootPrefix('a'), ''); - expect(builder.rootPrefix(r'a\b'), ''); - expect(builder.rootPrefix(r'C:\a\c'), r'C:\'); - expect(builder.rootPrefix('C:\\'), r'C:\'); - expect(builder.rootPrefix('C:/'), 'C:/'); - expect(builder.rootPrefix(r'\\server\share\a\b'), r'\\server\share'); - expect(builder.rootPrefix(r'\a\b'), r'\'); - expect(builder.rootPrefix(r'/a/b'), r'/'); - expect(builder.rootPrefix(r'\'), r'\'); - expect(builder.rootPrefix(r'/'), r'/'); + expect(context.rootPrefix(''), ''); + expect(context.rootPrefix('a'), ''); + expect(context.rootPrefix(r'a\b'), ''); + expect(context.rootPrefix(r'C:\a\c'), r'C:\'); + expect(context.rootPrefix('C:\\'), r'C:\'); + expect(context.rootPrefix('C:/'), 'C:/'); + expect(context.rootPrefix(r'\\server\share\a\b'), r'\\server\share'); + expect(context.rootPrefix(r'\a\b'), r'\'); + expect(context.rootPrefix(r'/a/b'), r'/'); + expect(context.rootPrefix(r'\'), r'\'); + expect(context.rootPrefix(r'/'), r'/'); }); test('dirname', () { - expect(builder.dirname(r''), '.'); - expect(builder.dirname(r'a'), '.'); - expect(builder.dirname(r'a\b'), 'a'); - expect(builder.dirname(r'a\b\c'), r'a\b'); - expect(builder.dirname(r'a\b.c'), 'a'); - expect(builder.dirname(r'a\'), '.'); - expect(builder.dirname('a/'), '.'); - expect(builder.dirname(r'a\.'), 'a'); - expect(builder.dirname(r'a\b/c'), r'a\b'); - expect(builder.dirname(r'C:\a'), r'C:\'); - expect(builder.dirname(r'C:\\\a'), r'C:\'); - expect(builder.dirname(r'C:\'), r'C:\'); - expect(builder.dirname(r'C:\\\'), r'C:\'); - expect(builder.dirname(r'a\b\'), r'a'); - expect(builder.dirname(r'a/b\c'), 'a/b'); - expect(builder.dirname(r'a\\'), r'.'); - expect(builder.dirname(r'a\b\\'), 'a'); - expect(builder.dirname(r'a\\b'), 'a'); - expect(builder.dirname(r'foo bar\gule fisk'), 'foo bar'); - expect(builder.dirname(r'\\server\share'), r'\\server\share'); - expect(builder.dirname(r'\\server\share\dir'), r'\\server\share'); - expect(builder.dirname(r'\a'), r'\'); - expect(builder.dirname(r'/a'), r'/'); - expect(builder.dirname(r'\'), r'\'); - expect(builder.dirname(r'/'), r'/'); + expect(context.dirname(r''), '.'); + expect(context.dirname(r'a'), '.'); + expect(context.dirname(r'a\b'), 'a'); + expect(context.dirname(r'a\b\c'), r'a\b'); + expect(context.dirname(r'a\b.c'), 'a'); + expect(context.dirname(r'a\'), '.'); + expect(context.dirname('a/'), '.'); + expect(context.dirname(r'a\.'), 'a'); + expect(context.dirname(r'a\b/c'), r'a\b'); + expect(context.dirname(r'C:\a'), r'C:\'); + expect(context.dirname(r'C:\\\a'), r'C:\'); + expect(context.dirname(r'C:\'), r'C:\'); + expect(context.dirname(r'C:\\\'), r'C:\'); + expect(context.dirname(r'a\b\'), r'a'); + expect(context.dirname(r'a/b\c'), 'a/b'); + expect(context.dirname(r'a\\'), r'.'); + expect(context.dirname(r'a\b\\'), 'a'); + expect(context.dirname(r'a\\b'), 'a'); + expect(context.dirname(r'foo bar\gule fisk'), 'foo bar'); + expect(context.dirname(r'\\server\share'), r'\\server\share'); + expect(context.dirname(r'\\server\share\dir'), r'\\server\share'); + expect(context.dirname(r'\a'), r'\'); + expect(context.dirname(r'/a'), r'/'); + expect(context.dirname(r'\'), r'\'); + expect(context.dirname(r'/'), r'/'); }); test('basename', () { - expect(builder.basename(r''), ''); - expect(builder.basename(r'.'), '.'); - expect(builder.basename(r'..'), '..'); - expect(builder.basename(r'.hest'), '.hest'); - expect(builder.basename(r'a'), 'a'); - expect(builder.basename(r'a\b'), 'b'); - expect(builder.basename(r'a\b\c'), 'c'); - expect(builder.basename(r'a\b.c'), 'b.c'); - expect(builder.basename(r'a\'), 'a'); - expect(builder.basename(r'a/'), 'a'); - expect(builder.basename(r'a\.'), '.'); - expect(builder.basename(r'a\b/c'), r'c'); - expect(builder.basename(r'C:\a'), 'a'); - expect(builder.basename(r'C:\'), r'C:\'); - expect(builder.basename(r'a\b\'), 'b'); - expect(builder.basename(r'a/b\c'), 'c'); - expect(builder.basename(r'a\\'), 'a'); - expect(builder.basename(r'a\b\\'), 'b'); - expect(builder.basename(r'a\\b'), 'b'); - expect(builder.basename(r'a\\b'), 'b'); - expect(builder.basename(r'a\fisk hest.ma pa'), 'fisk hest.ma pa'); - expect(builder.basename(r'\\server\share'), r'\\server\share'); - expect(builder.basename(r'\\server\share\dir'), r'dir'); - expect(builder.basename(r'\a'), r'a'); - expect(builder.basename(r'/a'), r'a'); - expect(builder.basename(r'\'), r'\'); - expect(builder.basename(r'/'), r'/'); + expect(context.basename(r''), ''); + expect(context.basename(r'.'), '.'); + expect(context.basename(r'..'), '..'); + expect(context.basename(r'.hest'), '.hest'); + expect(context.basename(r'a'), 'a'); + expect(context.basename(r'a\b'), 'b'); + expect(context.basename(r'a\b\c'), 'c'); + expect(context.basename(r'a\b.c'), 'b.c'); + expect(context.basename(r'a\'), 'a'); + expect(context.basename(r'a/'), 'a'); + expect(context.basename(r'a\.'), '.'); + expect(context.basename(r'a\b/c'), r'c'); + expect(context.basename(r'C:\a'), 'a'); + expect(context.basename(r'C:\'), r'C:\'); + expect(context.basename(r'a\b\'), 'b'); + expect(context.basename(r'a/b\c'), 'c'); + expect(context.basename(r'a\\'), 'a'); + expect(context.basename(r'a\b\\'), 'b'); + expect(context.basename(r'a\\b'), 'b'); + expect(context.basename(r'a\\b'), 'b'); + expect(context.basename(r'a\fisk hest.ma pa'), 'fisk hest.ma pa'); + expect(context.basename(r'\\server\share'), r'\\server\share'); + expect(context.basename(r'\\server\share\dir'), r'dir'); + expect(context.basename(r'\a'), r'a'); + expect(context.basename(r'/a'), r'a'); + expect(context.basename(r'\'), r'\'); + expect(context.basename(r'/'), r'/'); }); test('basenameWithoutExtension', () { - expect(builder.basenameWithoutExtension(''), ''); - expect(builder.basenameWithoutExtension('.'), '.'); - expect(builder.basenameWithoutExtension('..'), '..'); - expect(builder.basenameWithoutExtension('.hest'), '.hest'); - expect(builder.basenameWithoutExtension('a'), 'a'); - expect(builder.basenameWithoutExtension(r'a\b'), 'b'); - expect(builder.basenameWithoutExtension(r'a\b\c'), 'c'); - expect(builder.basenameWithoutExtension(r'a\b.c'), 'b'); - expect(builder.basenameWithoutExtension(r'a\'), 'a'); - expect(builder.basenameWithoutExtension(r'a\.'), '.'); - expect(builder.basenameWithoutExtension(r'a\b/c'), r'c'); - expect(builder.basenameWithoutExtension(r'a\.bashrc'), '.bashrc'); - expect(builder.basenameWithoutExtension(r'a\b\c.d.e'), 'c.d'); - expect(builder.basenameWithoutExtension(r'a\\'), 'a'); - expect(builder.basenameWithoutExtension(r'a\b\\'), 'b'); - expect(builder.basenameWithoutExtension(r'a\\b'), 'b'); - expect(builder.basenameWithoutExtension(r'a\b.c\'), 'b'); - expect(builder.basenameWithoutExtension(r'a\b.c\\'), 'b'); - expect(builder.basenameWithoutExtension(r'C:\f h.ma pa.f s'), 'f h.ma pa'); + expect(context.basenameWithoutExtension(''), ''); + expect(context.basenameWithoutExtension('.'), '.'); + expect(context.basenameWithoutExtension('..'), '..'); + expect(context.basenameWithoutExtension('.hest'), '.hest'); + expect(context.basenameWithoutExtension('a'), 'a'); + expect(context.basenameWithoutExtension(r'a\b'), 'b'); + expect(context.basenameWithoutExtension(r'a\b\c'), 'c'); + expect(context.basenameWithoutExtension(r'a\b.c'), 'b'); + expect(context.basenameWithoutExtension(r'a\'), 'a'); + expect(context.basenameWithoutExtension(r'a\.'), '.'); + expect(context.basenameWithoutExtension(r'a\b/c'), r'c'); + expect(context.basenameWithoutExtension(r'a\.bashrc'), '.bashrc'); + expect(context.basenameWithoutExtension(r'a\b\c.d.e'), 'c.d'); + expect(context.basenameWithoutExtension(r'a\\'), 'a'); + expect(context.basenameWithoutExtension(r'a\b\\'), 'b'); + expect(context.basenameWithoutExtension(r'a\\b'), 'b'); + expect(context.basenameWithoutExtension(r'a\b.c\'), 'b'); + expect(context.basenameWithoutExtension(r'a\b.c\\'), 'b'); + expect(context.basenameWithoutExtension(r'C:\f h.ma pa.f s'), 'f h.ma pa'); }); test('isAbsolute', () { - expect(builder.isAbsolute(''), false); - expect(builder.isAbsolute('.'), false); - expect(builder.isAbsolute('..'), false); - expect(builder.isAbsolute('a'), false); - expect(builder.isAbsolute(r'a\b'), false); - expect(builder.isAbsolute(r'\a\b'), true); - expect(builder.isAbsolute(r'\'), true); - expect(builder.isAbsolute(r'/a/b'), true); - expect(builder.isAbsolute(r'/'), true); - expect(builder.isAbsolute('~'), false); - expect(builder.isAbsolute('.'), false); - expect(builder.isAbsolute(r'..\a'), false); - expect(builder.isAbsolute(r'a:/a\b'), true); - expect(builder.isAbsolute(r'D:/a/b'), true); - expect(builder.isAbsolute(r'c:\'), true); - expect(builder.isAbsolute(r'B:\'), true); - expect(builder.isAbsolute(r'c:\a'), true); - expect(builder.isAbsolute(r'C:\a'), true); - expect(builder.isAbsolute(r'\\server\share'), true); - expect(builder.isAbsolute(r'\\server\share\path'), true); + expect(context.isAbsolute(''), false); + expect(context.isAbsolute('.'), false); + expect(context.isAbsolute('..'), false); + expect(context.isAbsolute('a'), false); + expect(context.isAbsolute(r'a\b'), false); + expect(context.isAbsolute(r'\a\b'), true); + expect(context.isAbsolute(r'\'), true); + expect(context.isAbsolute(r'/a/b'), true); + expect(context.isAbsolute(r'/'), true); + expect(context.isAbsolute('~'), false); + expect(context.isAbsolute('.'), false); + expect(context.isAbsolute(r'..\a'), false); + expect(context.isAbsolute(r'a:/a\b'), true); + expect(context.isAbsolute(r'D:/a/b'), true); + expect(context.isAbsolute(r'c:\'), true); + expect(context.isAbsolute(r'B:\'), true); + expect(context.isAbsolute(r'c:\a'), true); + expect(context.isAbsolute(r'C:\a'), true); + expect(context.isAbsolute(r'\\server\share'), true); + expect(context.isAbsolute(r'\\server\share\path'), true); }); test('isRelative', () { - expect(builder.isRelative(''), true); - expect(builder.isRelative('.'), true); - expect(builder.isRelative('..'), true); - expect(builder.isRelative('a'), true); - expect(builder.isRelative(r'a\b'), true); - expect(builder.isRelative(r'\a\b'), false); - expect(builder.isRelative(r'\'), false); - expect(builder.isRelative(r'/a/b'), false); - expect(builder.isRelative(r'/'), false); - expect(builder.isRelative('~'), true); - expect(builder.isRelative('.'), true); - expect(builder.isRelative(r'..\a'), true); - expect(builder.isRelative(r'a:/a\b'), false); - expect(builder.isRelative(r'D:/a/b'), false); - expect(builder.isRelative(r'c:\'), false); - expect(builder.isRelative(r'B:\'), false); - expect(builder.isRelative(r'c:\a'), false); - expect(builder.isRelative(r'C:\a'), false); - expect(builder.isRelative(r'\\server\share'), false); - expect(builder.isRelative(r'\\server\share\path'), false); + expect(context.isRelative(''), true); + expect(context.isRelative('.'), true); + expect(context.isRelative('..'), true); + expect(context.isRelative('a'), true); + expect(context.isRelative(r'a\b'), true); + expect(context.isRelative(r'\a\b'), false); + expect(context.isRelative(r'\'), false); + expect(context.isRelative(r'/a/b'), false); + expect(context.isRelative(r'/'), false); + expect(context.isRelative('~'), true); + expect(context.isRelative('.'), true); + expect(context.isRelative(r'..\a'), true); + expect(context.isRelative(r'a:/a\b'), false); + expect(context.isRelative(r'D:/a/b'), false); + expect(context.isRelative(r'c:\'), false); + expect(context.isRelative(r'B:\'), false); + expect(context.isRelative(r'c:\a'), false); + expect(context.isRelative(r'C:\a'), false); + expect(context.isRelative(r'\\server\share'), false); + expect(context.isRelative(r'\\server\share\path'), false); }); test('isRootRelative', () { - expect(builder.isRootRelative(''), false); - expect(builder.isRootRelative('.'), false); - expect(builder.isRootRelative('..'), false); - expect(builder.isRootRelative('a'), false); - expect(builder.isRootRelative(r'a\b'), false); - expect(builder.isRootRelative(r'\a\b'), true); - expect(builder.isRootRelative(r'\'), true); - expect(builder.isRootRelative(r'/a/b'), true); - expect(builder.isRootRelative(r'/'), true); - expect(builder.isRootRelative('~'), false); - expect(builder.isRootRelative('.'), false); - expect(builder.isRootRelative(r'..\a'), false); - expect(builder.isRootRelative(r'a:/a\b'), false); - expect(builder.isRootRelative(r'D:/a/b'), false); - expect(builder.isRootRelative(r'c:\'), false); - expect(builder.isRootRelative(r'B:\'), false); - expect(builder.isRootRelative(r'c:\a'), false); - expect(builder.isRootRelative(r'C:\a'), false); - expect(builder.isRootRelative(r'\\server\share'), false); - expect(builder.isRootRelative(r'\\server\share\path'), false); + expect(context.isRootRelative(''), false); + expect(context.isRootRelative('.'), false); + expect(context.isRootRelative('..'), false); + expect(context.isRootRelative('a'), false); + expect(context.isRootRelative(r'a\b'), false); + expect(context.isRootRelative(r'\a\b'), true); + expect(context.isRootRelative(r'\'), true); + expect(context.isRootRelative(r'/a/b'), true); + expect(context.isRootRelative(r'/'), true); + expect(context.isRootRelative('~'), false); + expect(context.isRootRelative('.'), false); + expect(context.isRootRelative(r'..\a'), false); + expect(context.isRootRelative(r'a:/a\b'), false); + expect(context.isRootRelative(r'D:/a/b'), false); + expect(context.isRootRelative(r'c:\'), false); + expect(context.isRootRelative(r'B:\'), false); + expect(context.isRootRelative(r'c:\a'), false); + expect(context.isRootRelative(r'C:\a'), false); + expect(context.isRootRelative(r'\\server\share'), false); + expect(context.isRootRelative(r'\\server\share\path'), false); }); group('join', () { test('allows up to eight parts', () { - expect(builder.join('a'), 'a'); - expect(builder.join('a', 'b'), r'a\b'); - expect(builder.join('a', 'b', 'c'), r'a\b\c'); - expect(builder.join('a', 'b', 'c', 'd'), r'a\b\c\d'); - expect(builder.join('a', 'b', 'c', 'd', 'e'), r'a\b\c\d\e'); - expect(builder.join('a', 'b', 'c', 'd', 'e', 'f'), r'a\b\c\d\e\f'); - expect(builder.join('a', 'b', 'c', 'd', 'e', 'f', 'g'), r'a\b\c\d\e\f\g'); - expect(builder.join('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'), + expect(context.join('a'), 'a'); + expect(context.join('a', 'b'), r'a\b'); + expect(context.join('a', 'b', 'c'), r'a\b\c'); + expect(context.join('a', 'b', 'c', 'd'), r'a\b\c\d'); + expect(context.join('a', 'b', 'c', 'd', 'e'), r'a\b\c\d\e'); + expect(context.join('a', 'b', 'c', 'd', 'e', 'f'), r'a\b\c\d\e\f'); + expect(context.join('a', 'b', 'c', 'd', 'e', 'f', 'g'), r'a\b\c\d\e\f\g'); + expect(context.join('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'), r'a\b\c\d\e\f\g\h'); }); test('does not add separator if a part ends or begins in one', () { - expect(builder.join(r'a\', 'b', r'c\', 'd'), r'a\b\c\d'); - expect(builder.join('a/', 'b'), r'a/b'); + expect(context.join(r'a\', 'b', r'c\', 'd'), r'a\b\c\d'); + expect(context.join('a/', 'b'), r'a/b'); }); test('ignores parts before an absolute path', () { - expect(builder.join('a', r'\b', r'\c', 'd'), r'\c\d'); - expect(builder.join('a', '/b', '/c', 'd'), r'/c\d'); - expect(builder.join('a', r'c:\b', 'c', 'd'), r'c:\b\c\d'); - expect(builder.join('a', r'\\b\c', r'\\d\e', 'f'), r'\\d\e\f'); - expect(builder.join('a', r'c:\b', r'\c', 'd'), r'c:\c\d'); - expect(builder.join('a', r'\\b\c\d', r'\e', 'f'), r'\\b\c\e\f'); + expect(context.join('a', r'\b', r'\c', 'd'), r'\c\d'); + expect(context.join('a', '/b', '/c', 'd'), r'/c\d'); + expect(context.join('a', r'c:\b', 'c', 'd'), r'c:\b\c\d'); + expect(context.join('a', r'\\b\c', r'\\d\e', 'f'), r'\\d\e\f'); + expect(context.join('a', r'c:\b', r'\c', 'd'), r'c:\c\d'); + expect(context.join('a', r'\\b\c\d', r'\e', 'f'), r'\\b\c\e\f'); }); test('ignores trailing nulls', () { - expect(builder.join('a', null), equals('a')); - expect(builder.join('a', 'b', 'c', null, null), equals(r'a\b\c')); + expect(context.join('a', null), equals('a')); + expect(context.join('a', 'b', 'c', null, null), equals(r'a\b\c')); }); test('ignores empty strings', () { - expect(builder.join(''), ''); - expect(builder.join('', ''), ''); - expect(builder.join('', 'a'), 'a'); - expect(builder.join('a', '', 'b', '', '', '', 'c'), r'a\b\c'); - expect(builder.join('a', 'b', ''), r'a\b'); + expect(context.join(''), ''); + expect(context.join('', ''), ''); + expect(context.join('', 'a'), 'a'); + expect(context.join('a', '', 'b', '', '', '', 'c'), r'a\b\c'); + expect(context.join('a', 'b', ''), r'a\b'); }); test('disallows intermediate nulls', () { - expect(() => builder.join('a', null, 'b'), throwsArgumentError); - expect(() => builder.join(null, 'a'), throwsArgumentError); + expect(() => context.join('a', null, 'b'), throwsArgumentError); + expect(() => context.join(null, 'a'), throwsArgumentError); }); test('join does not modify internal ., .., or trailing separators', () { - expect(builder.join('a/', 'b/c/'), 'a/b/c/'); - expect(builder.join(r'a\b\./c\..\\', r'd\..\.\..\\e\f\\'), + expect(context.join('a/', 'b/c/'), 'a/b/c/'); + expect(context.join(r'a\b\./c\..\\', r'd\..\.\..\\e\f\\'), r'a\b\./c\..\\d\..\.\..\\e\f\\'); - expect(builder.join(r'a\b', r'c\..\..\..\..'), r'a\b\c\..\..\..\..'); - expect(builder.join(r'a', 'b${builder.separator}'), r'a\b\'); + expect(context.join(r'a\b', r'c\..\..\..\..'), r'a\b\c\..\..\..\..'); + expect(context.join(r'a', 'b${context.separator}'), r'a\b\'); }); }); group('joinAll', () { test('allows more than eight parts', () { - expect(builder.joinAll(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']), + expect(context.joinAll(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']), r'a\b\c\d\e\f\g\h\i'); }); test('does not add separator if a part ends or begins in one', () { - expect(builder.joinAll([r'a\', 'b', r'c\', 'd']), r'a\b\c\d'); - expect(builder.joinAll(['a/', 'b']), r'a/b'); + expect(context.joinAll([r'a\', 'b', r'c\', 'd']), r'a\b\c\d'); + expect(context.joinAll(['a/', 'b']), r'a/b'); }); test('ignores parts before an absolute path', () { - expect(builder.joinAll(['a', r'\b', r'\c', 'd']), r'\c\d'); - expect(builder.joinAll(['a', '/b', '/c', 'd']), r'/c\d'); - expect(builder.joinAll(['a', r'c:\b', 'c', 'd']), r'c:\b\c\d'); - expect(builder.joinAll(['a', r'\\b\c', r'\\d\e', 'f']), r'\\d\e\f'); - expect(builder.joinAll(['a', r'c:\b', r'\c', 'd']), r'c:\c\d'); - expect(builder.joinAll(['a', r'\\b\c\d', r'\e', 'f']), r'\\b\c\e\f'); + expect(context.joinAll(['a', r'\b', r'\c', 'd']), r'\c\d'); + expect(context.joinAll(['a', '/b', '/c', 'd']), r'/c\d'); + expect(context.joinAll(['a', r'c:\b', 'c', 'd']), r'c:\b\c\d'); + expect(context.joinAll(['a', r'\\b\c', r'\\d\e', 'f']), r'\\d\e\f'); + expect(context.joinAll(['a', r'c:\b', r'\c', 'd']), r'c:\c\d'); + expect(context.joinAll(['a', r'\\b\c\d', r'\e', 'f']), r'\\b\c\e\f'); }); }); group('split', () { test('simple cases', () { - expect(builder.split(''), []); - expect(builder.split('.'), ['.']); - expect(builder.split('..'), ['..']); - expect(builder.split('foo'), equals(['foo'])); - expect(builder.split(r'foo\bar.txt'), equals(['foo', 'bar.txt'])); - expect(builder.split(r'foo\bar/baz'), equals(['foo', 'bar', 'baz'])); - expect(builder.split(r'foo\..\bar\.\baz'), + expect(context.split(''), []); + expect(context.split('.'), ['.']); + expect(context.split('..'), ['..']); + expect(context.split('foo'), equals(['foo'])); + expect(context.split(r'foo\bar.txt'), equals(['foo', 'bar.txt'])); + expect(context.split(r'foo\bar/baz'), equals(['foo', 'bar', 'baz'])); + expect(context.split(r'foo\..\bar\.\baz'), equals(['foo', '..', 'bar', '.', 'baz'])); - expect(builder.split(r'foo\\bar\\\baz'), equals(['foo', 'bar', 'baz'])); - expect(builder.split(r'foo\/\baz'), equals(['foo', 'baz'])); - expect(builder.split('.'), equals(['.'])); - expect(builder.split(''), equals([])); - expect(builder.split('foo/'), equals(['foo'])); - expect(builder.split(r'C:\'), equals([r'C:\'])); + expect(context.split(r'foo\\bar\\\baz'), equals(['foo', 'bar', 'baz'])); + expect(context.split(r'foo\/\baz'), equals(['foo', 'baz'])); + expect(context.split('.'), equals(['.'])); + expect(context.split(''), equals([])); + expect(context.split('foo/'), equals(['foo'])); + expect(context.split(r'C:\'), equals([r'C:\'])); }); test('includes the root for absolute paths', () { - expect(builder.split(r'C:\foo\bar\baz'), + expect(context.split(r'C:\foo\bar\baz'), equals([r'C:\', 'foo', 'bar', 'baz'])); - expect(builder.split(r'C:\\'), equals([r'C:\'])); + expect(context.split(r'C:\\'), equals([r'C:\'])); - expect(builder.split(r'\\server\share\foo\bar\baz'), + expect(context.split(r'\\server\share\foo\bar\baz'), equals([r'\\server\share', 'foo', 'bar', 'baz'])); - expect(builder.split(r'\\server\share'), equals([r'\\server\share'])); + expect(context.split(r'\\server\share'), equals([r'\\server\share'])); - expect(builder.split(r'\foo\bar\baz'), + expect(context.split(r'\foo\bar\baz'), equals([r'\', 'foo', 'bar', 'baz'])); - expect(builder.split(r'\'), equals([r'\'])); + expect(context.split(r'\'), equals([r'\'])); }); }); group('normalize', () { test('simple cases', () { - expect(builder.normalize(''), '.'); - expect(builder.normalize('.'), '.'); - expect(builder.normalize('..'), '..'); - expect(builder.normalize('a'), 'a'); - expect(builder.normalize('/a/b'), r'\a\b'); - expect(builder.normalize(r'\'), r'\'); - expect(builder.normalize(r'\a\b'), r'\a\b'); - expect(builder.normalize('/'), r'\'); - expect(builder.normalize('C:/'), r'C:\'); - expect(builder.normalize(r'C:\'), r'C:\'); - expect(builder.normalize(r'\\server\share'), r'\\server\share'); - expect(builder.normalize('a\\.\\\xc5\u0bf8-;\u{1f085}\u{00}\\c\\d\\..\\'), + expect(context.normalize(''), '.'); + expect(context.normalize('.'), '.'); + expect(context.normalize('..'), '..'); + expect(context.normalize('a'), 'a'); + expect(context.normalize('/a/b'), r'\a\b'); + expect(context.normalize(r'\'), r'\'); + expect(context.normalize(r'\a\b'), r'\a\b'); + expect(context.normalize('/'), r'\'); + expect(context.normalize('C:/'), r'C:\'); + expect(context.normalize(r'C:\'), r'C:\'); + expect(context.normalize(r'\\server\share'), r'\\server\share'); + expect(context.normalize('a\\.\\\xc5\u0bf8-;\u{1f085}\u{00}\\c\\d\\..\\'), 'a\\\xc5\u0bf8-;\u{1f085}\u{00}\x5cc'); }); test('collapses redundant separators', () { - expect(builder.normalize(r'a\b\c'), r'a\b\c'); - expect(builder.normalize(r'a\\b\\\c\\\\d'), r'a\b\c\d'); + expect(context.normalize(r'a\b\c'), r'a\b\c'); + expect(context.normalize(r'a\\b\\\c\\\\d'), r'a\b\c\d'); }); test('eliminates "." parts', () { - expect(builder.normalize(r'.\'), '.'); - expect(builder.normalize(r'c:\.'), r'c:\'); - expect(builder.normalize(r'B:\.\'), r'B:\'); - expect(builder.normalize(r'\\server\share\.'), r'\\server\share'); - expect(builder.normalize(r'.\.'), '.'); - expect(builder.normalize(r'a\.\b'), r'a\b'); - expect(builder.normalize(r'a\.b\c'), r'a\.b\c'); - expect(builder.normalize(r'a\./.\b\.\c'), r'a\b\c'); - expect(builder.normalize(r'.\./a'), 'a'); - expect(builder.normalize(r'a/.\.'), 'a'); - expect(builder.normalize(r'\.'), r'\'); - expect(builder.normalize('/.'), r'\'); + expect(context.normalize(r'.\'), '.'); + expect(context.normalize(r'c:\.'), r'c:\'); + expect(context.normalize(r'B:\.\'), r'B:\'); + expect(context.normalize(r'\\server\share\.'), r'\\server\share'); + expect(context.normalize(r'.\.'), '.'); + expect(context.normalize(r'a\.\b'), r'a\b'); + expect(context.normalize(r'a\.b\c'), r'a\.b\c'); + expect(context.normalize(r'a\./.\b\.\c'), r'a\b\c'); + expect(context.normalize(r'.\./a'), 'a'); + expect(context.normalize(r'a/.\.'), 'a'); + expect(context.normalize(r'\.'), r'\'); + expect(context.normalize('/.'), r'\'); }); test('eliminates ".." parts', () { - expect(builder.normalize('..'), '..'); - expect(builder.normalize(r'..\'), '..'); - expect(builder.normalize(r'..\..\..'), r'..\..\..'); - expect(builder.normalize(r'../..\..\'), r'..\..\..'); - expect(builder.normalize(r'\\server\share\..'), r'\\server\share'); - expect(builder.normalize(r'\\server\share\..\../..\a'), + expect(context.normalize('..'), '..'); + expect(context.normalize(r'..\'), '..'); + expect(context.normalize(r'..\..\..'), r'..\..\..'); + expect(context.normalize(r'../..\..\'), r'..\..\..'); + expect(context.normalize(r'\\server\share\..'), r'\\server\share'); + expect(context.normalize(r'\\server\share\..\../..\a'), r'\\server\share\a'); - expect(builder.normalize(r'c:\..'), r'c:\'); - expect(builder.normalize(r'A:/..\..\..'), r'A:\'); - expect(builder.normalize(r'b:\..\..\..\a'), r'b:\a'); - expect(builder.normalize(r'b:\r\..\..\..\a\c\.\..'), r'b:\a'); - expect(builder.normalize(r'a\..'), '.'); - expect(builder.normalize(r'..\a'), r'..\a'); - expect(builder.normalize(r'c:\..\a'), r'c:\a'); - expect(builder.normalize(r'\..\a'), r'\a'); - expect(builder.normalize(r'a\b\..'), 'a'); - expect(builder.normalize(r'..\a\b\..'), r'..\a'); - expect(builder.normalize(r'a\..\b'), 'b'); - expect(builder.normalize(r'a\.\..\b'), 'b'); - expect(builder.normalize(r'a\b\c\..\..\d\e\..'), r'a\d'); - expect(builder.normalize(r'a\b\..\..\..\..\c'), r'..\..\c'); - expect(builder.normalize(r'a/b/c/../../..d/./.e/f././'), r'a\..d\.e\f.'); + expect(context.normalize(r'c:\..'), r'c:\'); + expect(context.normalize(r'A:/..\..\..'), r'A:\'); + expect(context.normalize(r'b:\..\..\..\a'), r'b:\a'); + expect(context.normalize(r'b:\r\..\..\..\a\c\.\..'), r'b:\a'); + expect(context.normalize(r'a\..'), '.'); + expect(context.normalize(r'..\a'), r'..\a'); + expect(context.normalize(r'c:\..\a'), r'c:\a'); + expect(context.normalize(r'\..\a'), r'\a'); + expect(context.normalize(r'a\b\..'), 'a'); + expect(context.normalize(r'..\a\b\..'), r'..\a'); + expect(context.normalize(r'a\..\b'), 'b'); + expect(context.normalize(r'a\.\..\b'), 'b'); + expect(context.normalize(r'a\b\c\..\..\d\e\..'), r'a\d'); + expect(context.normalize(r'a\b\..\..\..\..\c'), r'..\..\c'); + expect(context.normalize(r'a/b/c/../../..d/./.e/f././'), r'a\..d\.e\f.'); }); test('removes trailing separators', () { - expect(builder.normalize(r'.\'), '.'); - expect(builder.normalize(r'.\\'), '.'); - expect(builder.normalize(r'a/'), 'a'); - expect(builder.normalize(r'a\b\'), r'a\b'); - expect(builder.normalize(r'a\b\\\'), r'a\b'); + expect(context.normalize(r'.\'), '.'); + expect(context.normalize(r'.\\'), '.'); + expect(context.normalize(r'a/'), 'a'); + expect(context.normalize(r'a\b\'), r'a\b'); + expect(context.normalize(r'a\b\\\'), r'a\b'); }); test('normalizes separators', () { - expect(builder.normalize(r'a/b\c'), r'a\b\c'); + expect(context.normalize(r'a/b\c'), r'a\b\c'); }); }); group('relative', () { group('from absolute root', () { test('given absolute path in root', () { - expect(builder.relative(r'C:\'), r'..\..'); - expect(builder.relative(r'C:\root'), '..'); - expect(builder.relative(r'\root'), '..'); - expect(builder.relative(r'C:\root\path'), '.'); - expect(builder.relative(r'\root\path'), '.'); - expect(builder.relative(r'C:\root\path\a'), 'a'); - expect(builder.relative(r'\root\path\a'), 'a'); - expect(builder.relative(r'C:\root\path\a\b.txt'), r'a\b.txt'); - expect(builder.relative(r'C:\root\a\b.txt'), r'..\a\b.txt'); - expect(builder.relative(r'C:/'), r'..\..'); - expect(builder.relative(r'C:/root'), '..'); - expect(builder.relative(r'c:\'), r'..\..'); - expect(builder.relative(r'c:\root'), '..'); + expect(context.relative(r'C:\'), r'..\..'); + expect(context.relative(r'C:\root'), '..'); + expect(context.relative(r'\root'), '..'); + expect(context.relative(r'C:\root\path'), '.'); + expect(context.relative(r'\root\path'), '.'); + expect(context.relative(r'C:\root\path\a'), 'a'); + expect(context.relative(r'\root\path\a'), 'a'); + expect(context.relative(r'C:\root\path\a\b.txt'), r'a\b.txt'); + expect(context.relative(r'C:\root\a\b.txt'), r'..\a\b.txt'); + expect(context.relative(r'C:/'), r'..\..'); + expect(context.relative(r'C:/root'), '..'); + expect(context.relative(r'c:\'), r'..\..'); + expect(context.relative(r'c:\root'), '..'); }); test('given absolute path outside of root', () { - expect(builder.relative(r'C:\a\b'), r'..\..\a\b'); - expect(builder.relative(r'\a\b'), r'..\..\a\b'); - expect(builder.relative(r'C:\root\path\a'), 'a'); - expect(builder.relative(r'C:\root\path\a\b.txt'), r'a\b.txt'); - expect(builder.relative(r'C:\root\a\b.txt'), r'..\a\b.txt'); - expect(builder.relative(r'C:/a/b'), r'..\..\a\b'); - expect(builder.relative(r'C:/root/path/a'), 'a'); - expect(builder.relative(r'c:\a\b'), r'..\..\a\b'); - expect(builder.relative(r'c:\root\path\a'), 'a'); + expect(context.relative(r'C:\a\b'), r'..\..\a\b'); + expect(context.relative(r'\a\b'), r'..\..\a\b'); + expect(context.relative(r'C:\root\path\a'), 'a'); + expect(context.relative(r'C:\root\path\a\b.txt'), r'a\b.txt'); + expect(context.relative(r'C:\root\a\b.txt'), r'..\a\b.txt'); + expect(context.relative(r'C:/a/b'), r'..\..\a\b'); + expect(context.relative(r'C:/root/path/a'), 'a'); + expect(context.relative(r'c:\a\b'), r'..\..\a\b'); + expect(context.relative(r'c:\root\path\a'), 'a'); }); test('given absolute path on different drive', () { - expect(builder.relative(r'D:\a\b'), r'D:\a\b'); + expect(context.relative(r'D:\a\b'), r'D:\a\b'); }); test('given relative path', () { // The path is considered relative to the root, so it basically just // normalizes. - expect(builder.relative(''), '.'); - expect(builder.relative('.'), '.'); - expect(builder.relative('a'), 'a'); - expect(builder.relative(r'a\b.txt'), r'a\b.txt'); - expect(builder.relative(r'..\a\b.txt'), r'..\a\b.txt'); - expect(builder.relative(r'a\.\b\..\c.txt'), r'a\c.txt'); + expect(context.relative(''), '.'); + expect(context.relative('.'), '.'); + expect(context.relative('a'), 'a'); + expect(context.relative(r'a\b.txt'), r'a\b.txt'); + expect(context.relative(r'..\a\b.txt'), r'..\a\b.txt'); + expect(context.relative(r'a\.\b\..\c.txt'), r'a\c.txt'); }); // Regression test('from root-only path', () { - expect(builder.relative(r'C:\', from: r'C:\'), '.'); - expect(builder.relative(r'C:\root\path', from: r'C:\'), r'root\path'); + expect(context.relative(r'C:\', from: r'C:\'), '.'); + expect(context.relative(r'C:\root\path', from: r'C:\'), r'root\path'); }); }); group('from relative root', () { - var r = new path.Builder(style: path.Style.windows, root: r'foo\bar'); + var r = new path.Context(style: path.Style.windows, current: r'foo\bar'); test('given absolute path', () { expect(r.relative(r'C:\'), equals(r'C:\')); @@ -463,7 +455,7 @@ main() { }); group('from root-relative root', () { - var r = new path.Builder(style: path.Style.windows, root: r'\foo\bar'); + var r = new path.Context(style: path.Style.windows, current: r'\foo\bar'); test('given absolute path', () { expect(r.relative(r'C:\'), equals(r'C:\')); @@ -488,23 +480,25 @@ main() { }); test('from a root with extension', () { - var r = new path.Builder(style: path.Style.windows, root: r'C:\dir.ext'); + var r = new path.Context( + style: path.Style.windows, current: r'C:\dir.ext'); expect(r.relative(r'C:\dir.ext\file'), 'file'); }); test('with a root parameter', () { - expect(builder.relative(r'C:\foo\bar\baz', from: r'C:\foo\bar'), + expect(context.relative(r'C:\foo\bar\baz', from: r'C:\foo\bar'), equals('baz')); - expect(builder.relative('..', from: r'C:\foo\bar'), + expect(context.relative('..', from: r'C:\foo\bar'), equals(r'..\..\root')); - expect(builder.relative('..', from: r'D:\foo\bar'), equals(r'C:\root')); - expect(builder.relative(r'C:\foo\bar\baz', from: r'foo\bar'), + expect(context.relative('..', from: r'D:\foo\bar'), equals(r'C:\root')); + expect(context.relative(r'C:\foo\bar\baz', from: r'foo\bar'), equals(r'..\..\..\..\foo\bar\baz')); - expect(builder.relative('..', from: r'foo\bar'), equals(r'..\..\..')); + expect(context.relative('..', from: r'foo\bar'), equals(r'..\..\..')); }); test('with a root parameter and a relative root', () { - var r = new path.Builder(style: path.Style.windows, root: r'relative\root'); + var r = new path.Context( + style: path.Style.windows, current: r'relative\root'); expect(r.relative(r'C:\foo\bar\baz', from: r'C:\foo\bar'), equals('baz')); expect(() => r.relative('..', from: r'C:\foo\bar'), throwsPathException); expect(r.relative(r'C:\foo\bar\baz', from: r'foo\bar'), @@ -513,12 +507,12 @@ main() { }); test('given absolute with different root prefix', () { - expect(builder.relative(r'D:\a\b'), r'D:\a\b'); - expect(builder.relative(r'\\server\share\a\b'), r'\\server\share\a\b'); + expect(context.relative(r'D:\a\b'), r'D:\a\b'); + expect(context.relative(r'\\server\share\a\b'), r'\\server\share\a\b'); }); test('from a . root', () { - var r = new path.Builder(style: path.Style.windows, root: '.'); + var r = new path.Context(style: path.Style.windows, current: '.'); expect(r.relative(r'C:\foo\bar\baz'), equals(r'C:\foo\bar\baz')); expect(r.relative(r'foo\bar\baz'), equals(r'foo\bar\baz')); expect(r.relative(r'\foo\bar\baz'), equals(r'\foo\bar\baz')); @@ -527,115 +521,115 @@ main() { group('isWithin', () { test('simple cases', () { - expect(builder.isWithin(r'foo\bar', r'foo\bar'), isFalse); - expect(builder.isWithin(r'foo\bar', r'foo\bar\baz'), isTrue); - expect(builder.isWithin(r'foo\bar', r'foo\baz'), isFalse); - expect(builder.isWithin(r'foo\bar', r'..\path\foo\bar\baz'), isTrue); - expect(builder.isWithin(r'C:\', r'C:\foo\bar'), isTrue); - expect(builder.isWithin(r'C:\', r'D:\foo\bar'), isFalse); - expect(builder.isWithin(r'C:\', r'\foo\bar'), isTrue); - expect(builder.isWithin(r'C:\foo', r'\foo\bar'), isTrue); - expect(builder.isWithin(r'C:\foo', r'\bar\baz'), isFalse); - expect(builder.isWithin(r'baz', r'C:\root\path\baz\bang'), isTrue); - expect(builder.isWithin(r'baz', r'C:\root\path\bang\baz'), isFalse); + expect(context.isWithin(r'foo\bar', r'foo\bar'), isFalse); + expect(context.isWithin(r'foo\bar', r'foo\bar\baz'), isTrue); + expect(context.isWithin(r'foo\bar', r'foo\baz'), isFalse); + expect(context.isWithin(r'foo\bar', r'..\path\foo\bar\baz'), isTrue); + expect(context.isWithin(r'C:\', r'C:\foo\bar'), isTrue); + expect(context.isWithin(r'C:\', r'D:\foo\bar'), isFalse); + expect(context.isWithin(r'C:\', r'\foo\bar'), isTrue); + expect(context.isWithin(r'C:\foo', r'\foo\bar'), isTrue); + expect(context.isWithin(r'C:\foo', r'\bar\baz'), isFalse); + expect(context.isWithin(r'baz', r'C:\root\path\baz\bang'), isTrue); + expect(context.isWithin(r'baz', r'C:\root\path\bang\baz'), isFalse); }); test('from a relative root', () { - var r = new path.Builder(style: path.Style.windows, root: r'foo\bar'); - expect(builder.isWithin('.', r'a\b\c'), isTrue); - expect(builder.isWithin('.', r'..\a\b\c'), isFalse); - expect(builder.isWithin('.', r'..\..\a\foo\b\c'), isFalse); - expect(builder.isWithin(r'C:\', r'C:\baz\bang'), isTrue); - expect(builder.isWithin('.', r'C:\baz\bang'), isFalse); + var r = new path.Context(style: path.Style.windows, current: r'foo\bar'); + expect(context.isWithin('.', r'a\b\c'), isTrue); + expect(context.isWithin('.', r'..\a\b\c'), isFalse); + expect(context.isWithin('.', r'..\..\a\foo\b\c'), isFalse); + expect(context.isWithin(r'C:\', r'C:\baz\bang'), isTrue); + expect(context.isWithin('.', r'C:\baz\bang'), isFalse); }); }); - group('resolve', () { + group('absolute', () { test('allows up to seven parts', () { - expect(builder.resolve('a'), r'C:\root\path\a'); - expect(builder.resolve('a', 'b'), r'C:\root\path\a\b'); - expect(builder.resolve('a', 'b', 'c'), r'C:\root\path\a\b\c'); - expect(builder.resolve('a', 'b', 'c', 'd'), r'C:\root\path\a\b\c\d'); - expect(builder.resolve('a', 'b', 'c', 'd', 'e'), + expect(context.absolute('a'), r'C:\root\path\a'); + expect(context.absolute('a', 'b'), r'C:\root\path\a\b'); + expect(context.absolute('a', 'b', 'c'), r'C:\root\path\a\b\c'); + expect(context.absolute('a', 'b', 'c', 'd'), r'C:\root\path\a\b\c\d'); + expect(context.absolute('a', 'b', 'c', 'd', 'e'), r'C:\root\path\a\b\c\d\e'); - expect(builder.resolve('a', 'b', 'c', 'd', 'e', 'f'), + expect(context.absolute('a', 'b', 'c', 'd', 'e', 'f'), r'C:\root\path\a\b\c\d\e\f'); - expect(builder.resolve('a', 'b', 'c', 'd', 'e', 'f', 'g'), + expect(context.absolute('a', 'b', 'c', 'd', 'e', 'f', 'g'), r'C:\root\path\a\b\c\d\e\f\g'); }); test('does not add separator if a part ends in one', () { - expect(builder.resolve(r'a\', 'b', r'c\', 'd'), r'C:\root\path\a\b\c\d'); - expect(builder.resolve('a/', 'b'), r'C:\root\path\a/b'); + expect(context.absolute(r'a\', 'b', r'c\', 'd'), r'C:\root\path\a\b\c\d'); + expect(context.absolute('a/', 'b'), r'C:\root\path\a/b'); }); test('ignores parts before an absolute path', () { - expect(builder.resolve('a', '/b', '/c', 'd'), r'C:\c\d'); - expect(builder.resolve('a', r'\b', r'\c', 'd'), r'C:\c\d'); - expect(builder.resolve('a', r'c:\b', 'c', 'd'), r'c:\b\c\d'); - expect(builder.resolve('a', r'\\b\c', r'\\d\e', 'f'), r'\\d\e\f'); + expect(context.absolute('a', '/b', '/c', 'd'), r'C:\c\d'); + expect(context.absolute('a', r'\b', r'\c', 'd'), r'C:\c\d'); + expect(context.absolute('a', r'c:\b', 'c', 'd'), r'c:\b\c\d'); + expect(context.absolute('a', r'\\b\c', r'\\d\e', 'f'), r'\\d\e\f'); }); }); test('withoutExtension', () { - expect(builder.withoutExtension(''), ''); - expect(builder.withoutExtension('a'), 'a'); - expect(builder.withoutExtension('.a'), '.a'); - expect(builder.withoutExtension('a.b'), 'a'); - expect(builder.withoutExtension(r'a\b.c'), r'a\b'); - expect(builder.withoutExtension(r'a\b.c.d'), r'a\b.c'); - expect(builder.withoutExtension(r'a\'), r'a\'); - expect(builder.withoutExtension(r'a\b\'), r'a\b\'); - expect(builder.withoutExtension(r'a\.'), r'a\.'); - expect(builder.withoutExtension(r'a\.b'), r'a\.b'); - expect(builder.withoutExtension(r'a.b\c'), r'a.b\c'); - expect(builder.withoutExtension(r'a/b.c/d'), r'a/b.c/d'); - expect(builder.withoutExtension(r'a\b/c'), r'a\b/c'); - expect(builder.withoutExtension(r'a\b/c.d'), r'a\b/c'); - expect(builder.withoutExtension(r'a.b/c'), r'a.b/c'); - expect(builder.withoutExtension(r'a\b.c\'), r'a\b\'); + expect(context.withoutExtension(''), ''); + expect(context.withoutExtension('a'), 'a'); + expect(context.withoutExtension('.a'), '.a'); + expect(context.withoutExtension('a.b'), 'a'); + expect(context.withoutExtension(r'a\b.c'), r'a\b'); + expect(context.withoutExtension(r'a\b.c.d'), r'a\b.c'); + expect(context.withoutExtension(r'a\'), r'a\'); + expect(context.withoutExtension(r'a\b\'), r'a\b\'); + expect(context.withoutExtension(r'a\.'), r'a\.'); + expect(context.withoutExtension(r'a\.b'), r'a\.b'); + expect(context.withoutExtension(r'a.b\c'), r'a.b\c'); + expect(context.withoutExtension(r'a/b.c/d'), r'a/b.c/d'); + expect(context.withoutExtension(r'a\b/c'), r'a\b/c'); + expect(context.withoutExtension(r'a\b/c.d'), r'a\b/c'); + expect(context.withoutExtension(r'a.b/c'), r'a.b/c'); + expect(context.withoutExtension(r'a\b.c\'), r'a\b\'); }); test('fromUri', () { - expect(builder.fromUri(Uri.parse('file:///C:/path/to/foo')), + expect(context.fromUri(Uri.parse('file:///C:/path/to/foo')), r'C:\path\to\foo'); - expect(builder.fromUri(Uri.parse('file://server/share/path/to/foo')), + expect(context.fromUri(Uri.parse('file://server/share/path/to/foo')), r'\\server\share\path\to\foo'); - expect(builder.fromUri(Uri.parse('file:///C:/')), r'C:\'); - expect(builder.fromUri(Uri.parse('file://server/share')), + expect(context.fromUri(Uri.parse('file:///C:/')), r'C:\'); + expect(context.fromUri(Uri.parse('file://server/share')), r'\\server\share'); - expect(builder.fromUri(Uri.parse('foo/bar')), r'foo\bar'); - expect(builder.fromUri(Uri.parse('/C:/path/to/foo')), r'C:\path\to\foo'); - expect(builder.fromUri(Uri.parse('///C:/path/to/foo')), r'C:\path\to\foo'); - expect(builder.fromUri(Uri.parse('//server/share/path/to/foo')), + expect(context.fromUri(Uri.parse('foo/bar')), r'foo\bar'); + expect(context.fromUri(Uri.parse('/C:/path/to/foo')), r'C:\path\to\foo'); + expect(context.fromUri(Uri.parse('///C:/path/to/foo')), r'C:\path\to\foo'); + expect(context.fromUri(Uri.parse('//server/share/path/to/foo')), r'\\server\share\path\to\foo'); - expect(builder.fromUri(Uri.parse('file:///C:/path/to/foo%23bar')), + expect(context.fromUri(Uri.parse('file:///C:/path/to/foo%23bar')), r'C:\path\to\foo#bar'); - expect(builder.fromUri(Uri.parse('file://server/share/path/to/foo%23bar')), + expect(context.fromUri(Uri.parse('file://server/share/path/to/foo%23bar')), r'\\server\share\path\to\foo#bar'); - expect(builder.fromUri(Uri.parse('_%7B_%7D_%60_%5E_%20_%22_%25_')), + expect(context.fromUri(Uri.parse('_%7B_%7D_%60_%5E_%20_%22_%25_')), r'_{_}_`_^_ _"_%_'); - expect(() => builder.fromUri(Uri.parse('http://dartlang.org')), + expect(() => context.fromUri(Uri.parse('http://dartlang.org')), throwsArgumentError); }); test('toUri', () { - expect(builder.toUri(r'C:\path\to\foo'), + expect(context.toUri(r'C:\path\to\foo'), Uri.parse('file:///C:/path/to/foo')); - expect(builder.toUri(r'C:\path\to\foo\'), + expect(context.toUri(r'C:\path\to\foo\'), Uri.parse('file:///C:/path/to/foo/')); - expect(builder.toUri(r'C:\'), Uri.parse('file:///C:/')); - expect(builder.toUri(r'\\server\share'), Uri.parse('file://server/share')); - expect(builder.toUri(r'\\server\share\'), + expect(context.toUri(r'C:\'), Uri.parse('file:///C:/')); + expect(context.toUri(r'\\server\share'), Uri.parse('file://server/share')); + expect(context.toUri(r'\\server\share\'), Uri.parse('file://server/share/')); - expect(builder.toUri(r'foo\bar'), Uri.parse('foo/bar')); - expect(builder.toUri(r'C:\path\to\foo#bar'), + expect(context.toUri(r'foo\bar'), Uri.parse('foo/bar')); + expect(context.toUri(r'C:\path\to\foo#bar'), Uri.parse('file:///C:/path/to/foo%23bar')); - expect(builder.toUri(r'\\server\share\path\to\foo#bar'), + expect(context.toUri(r'\\server\share\path\to\foo#bar'), Uri.parse('file://server/share/path/to/foo%23bar')); - expect(builder.toUri(r'C:\_{_}_`_^_ _"_%_'), + expect(context.toUri(r'C:\_{_}_`_^_ _"_%_'), Uri.parse('file:///C:/_%7B_%7D_%60_%5E_%20_%22_%25_')); - expect(builder.toUri(r'_{_}_`_^_ _"_%_'), + expect(context.toUri(r'_{_}_`_^_ _"_%_'), Uri.parse('_%7B_%7D_%60_%5E_%20_%22_%25_')); }); } diff --git a/pkg/scheduled_test/lib/src/descriptor/directory_descriptor.dart b/pkg/scheduled_test/lib/src/descriptor/directory_descriptor.dart index e5baf445d57..1b377ae7f6e 100644 --- a/pkg/scheduled_test/lib/src/descriptor/directory_descriptor.dart +++ b/pkg/scheduled_test/lib/src/descriptor/directory_descriptor.dart @@ -13,9 +13,6 @@ import '../../descriptor.dart'; import '../../scheduled_test.dart'; import '../utils.dart'; -/// A path builder to ensure that [load] uses POSIX paths. -final path.Builder _path = new path.Builder(style: path.Style.posix); - /// A descriptor describing a directory containing multiple files. class DirectoryDescriptor extends Descriptor implements LoadableDescriptor { /// The entries contained within this directory. This is intentionally @@ -58,11 +55,11 @@ class DirectoryDescriptor extends Descriptor implements LoadableDescriptor { Stream> load(String pathToLoad) { return futureStream(new Future.value().then((_) { - if (_path.isAbsolute(pathToLoad)) { + if (path.posix.isAbsolute(pathToLoad)) { throw new ArgumentError("Can't load absolute path '$pathToLoad'."); } - var split = _path.split(_path.normalize(pathToLoad)); + var split = path.posix.split(path.posix.normalize(pathToLoad)); if (split.isEmpty || split.first == '.' || split.first == '..') { throw new ArgumentError("Can't load '$pathToLoad' from within " "'$name'."); @@ -88,7 +85,7 @@ class DirectoryDescriptor extends Descriptor implements LoadableDescriptor { return (matchingEntries.first as ReadableDescriptor).read(); } else { return (matchingEntries.first as LoadableDescriptor) - .load(_path.joinAll(remainingPath)); + .load(path.posix.joinAll(remainingPath)); } } })); diff --git a/pkg/stack_trace/test/vm_test.dart b/pkg/stack_trace/test/vm_test.dart index 6570c24c5cc..7e00b7a61cd 100644 --- a/pkg/stack_trace/test/vm_test.dart +++ b/pkg/stack_trace/test/vm_test.dart @@ -43,16 +43,14 @@ void main() { test('.parse parses a real stack trace correctly', () { var string = getStackTraceString(); var trace = new Trace.parse(string); - var builder = new path.Builder(style: path.Style.url); - expect(builder.basename(trace.frames.first.uri.path), + expect(path.url.basename(trace.frames.first.uri.path), equals('vm_test.dart')); expect(trace.frames.first.member, equals('getStackTraceString')); }); test('converts from a native stack trace correctly', () { var trace = new Trace.from(getStackTraceObject()); - var builder = new path.Builder(style: path.Style.url); - expect(builder.basename(trace.frames.first.uri.path), + expect(path.url.basename(trace.frames.first.uri.path), equals('vm_test.dart')); expect(trace.frames.first.member, equals('getStackTraceObject')); });