Skip to content

Commit

Permalink
Port dart:io Path tests to package:path.
Browse files Browse the repository at this point in the history
BUG=
R=nweiz@google.com, rnystrom@google.com

Review URL: https://codereview.chromium.org//19231002

git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/path@25403 260f80e4-7a28-3924-810f-c04153c831b5
  • Loading branch information
whesse committed Jul 24, 2013
1 parent 8e2e11f commit 6d0caed
Show file tree
Hide file tree
Showing 6 changed files with 247 additions and 10 deletions.
13 changes: 13 additions & 0 deletions pkgs/path/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,16 @@ Trailing separators are ignored.

builder.dirname('path/to/'); // -> 'path'

If an absolute path contains no directories, only a root, then the root
is returned.

path.dirname('/'); // -> '/' (posix)
path.dirname('c:\'); // -> 'c:\' (windows)

If a relative path has no directories, then '.' is returned.
path.dirname('foo'); // -> '.'
path.dirname(''); // -> '.'

### String extension(String path)

Gets the file extension of [path]: the portion of [basename] from the last
Expand Down Expand Up @@ -200,6 +210,9 @@ If the [from] argument is passed, [path] is made relative to that instead.
path.relative('/root/other.dart',
from: '/root/path'); // -> '../other.dart'

If [path] and/or [from] are relative paths, they are assumed to be relative
to the current directory.

Since there is no relative path from one drive letter to another on Windows,
this will return an absolute path in that case.

Expand Down
39 changes: 32 additions & 7 deletions pkgs/path/lib/path.dart
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,17 @@ String basenameWithoutExtension(String path) =>
/// Trailing separators are ignored.
///
/// builder.dirname('path/to/'); // -> 'path'
///
/// If an absolute path contains no directories, only a root, then the root
/// is returned.
///
/// path.dirname('/'); // -> '/' (posix)
/// path.dirname('c:\'); // -> 'c:\' (windows)
///
/// If a relative path has no directories, then '.' is returned.
///
/// path.dirname('foo'); // -> '.'
/// path.dirname(''); // -> '.'
String dirname(String path) => _builder.dirname(path);

/// Gets the file extension of [path]: the portion of [basename] from the last
Expand Down Expand Up @@ -248,6 +259,9 @@ String normalize(String path) => _builder.normalize(path);
/// path.relative('/root/other.dart',
/// from: '/root/path'); // -> '../other.dart'
///
/// If [path] and/or [from] are relative paths, they are assumed to be relative
/// to the current directory.
///
/// Since there is no relative path from one drive letter to another on Windows,
/// or from one hostname to another for URLs, this will return an absolute path
/// in those cases.
Expand Down Expand Up @@ -582,8 +596,6 @@ class Builder {
///
/// builder.normalize('path/./to/..//file.text'); // -> 'path/file.txt'
String normalize(String path) {
if (path == '') return path;

var parsed = _parse(path);
parsed.normalize();
return parsed.toString();
Expand Down Expand Up @@ -613,6 +625,9 @@ class Builder {
/// 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.
///
Expand All @@ -624,8 +639,6 @@ class Builder {
/// var builder = new Builder(r'some/relative/path');
/// builder.relative(r'/absolute/path'); // -> '/absolute/path'
String relative(String path, {String from}) {
if (path == '') return '.';

from = from == null ? root : this.join(root, from);

// We can't determine the path from a relative path to an absolute path.
Expand Down Expand Up @@ -672,8 +685,12 @@ class Builder {
pathParsed.separators.removeAt(1);
}

// If there are any directories left in the root path, we need to walk up
// out of them.
// 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 ArgumentError('Unable to find a path to "$path" from "$from".');
}
_growListFront(pathParsed.parts, fromParsed.parts.length, '..');
pathParsed.separators[0] = '';
pathParsed.separators.insertAll(1,
Expand All @@ -682,6 +699,13 @@ class Builder {
// 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();
Expand Down Expand Up @@ -1042,7 +1066,8 @@ class _ParsedPath {
return copy._splitExtension()[0];
}

bool get hasTrailingSeparator => !parts.isEmpty && (parts.last == '' || separators.last != '');
bool get hasTrailingSeparator =>
!parts.isEmpty && (parts.last == '' || separators.last != '');

void removeTrailingSeparators() {
while (!parts.isEmpty && parts.last == '') {
Expand Down
47 changes: 46 additions & 1 deletion pkgs/path/test/posix_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ main() {

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'), '');
Expand All @@ -41,12 +43,16 @@ main() {

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'), '/');
Expand All @@ -61,12 +67,16 @@ main() {

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('/'), '/');
Expand All @@ -79,6 +89,8 @@ main() {

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');
Expand All @@ -93,6 +105,7 @@ main() {
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');
});

test('isAbsolute', () {
Expand All @@ -103,6 +116,8 @@ main() {
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);
Expand All @@ -117,6 +132,8 @@ main() {
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);
Expand Down Expand Up @@ -165,6 +182,14 @@ main() {
expect(() => builder.join('a', null, 'b'), throwsArgumentError);
expect(() => builder.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//'),
'a/b/./c/..//d/.././..//e/f//');
expect(builder.join('a/b', 'c/../../../..'), 'a/b/c/../../../..');
expect(builder.join('a', 'b${builder.separator}'), 'a/b/');
});
});

group('joinAll', () {
Expand Down Expand Up @@ -212,12 +237,17 @@ main() {

group('normalize', () {
test('simple cases', () {
expect(builder.normalize(''), '');
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/../'),
'a/\xc5\u0bf8-;\u{1f085}\u{00}/c');
});

test('collapses redundant separators', () {
Expand Down Expand Up @@ -249,31 +279,46 @@ main() {
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');
});

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.');
});

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');
});
});
Expand Down
86 changes: 86 additions & 0 deletions pkgs/path/test/relative_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// 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.
//
// Test "relative" on all styles of path.Builder, on all platforms.

import "package:unittest/unittest.dart";
import "package:path/path.dart" as path;

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:\'),
r'c:\');
relativeTest(new path.Builder(style: path.Style.windows, root: '.'),
r'c:\');
relativeTest(new path.Builder(style: path.Style.url, root: 'file:///'),
'http://myserver/');
relativeTest(new path.Builder(style: path.Style.url, root: '.'),
'http://myserver/');
relativeTest(new path.Builder(style: path.Style.url, root: 'file:///'),
'/');
relativeTest(new path.Builder(style: path.Style.url, root: '.'), '/');
});
}

void relativeTest(path.Builder builder, String prefix) {
var isRelative = (builder.root == '.');
// Cases where the arguments are absolute paths.
expectRelative(result, pathArg, fromArg) {
expect(builder.normalize(result), builder.relative(pathArg, from: fromArg));
}

expectRelative('c/d', '${prefix}a/b/c/d', '${prefix}a/b');
expectRelative('c/d', '${prefix}a/b/c/d', '${prefix}a/b/');
expectRelative('.', '${prefix}a', '${prefix}a');
// Trailing slashes in the inputs have no effect.
expectRelative('../../z/x/y', '${prefix}a/b/z/x/y', '${prefix}a/b/c/d/');
expectRelative('../../z/x/y', '${prefix}a/b/z/x/y', '${prefix}a/b/c/d');
expectRelative('../../z/x/y', '${prefix}a/b/z/x/y/', '${prefix}a/b/c/d');
expectRelative('../../../z/x/y', '${prefix}z/x/y', '${prefix}a/b/c');
expectRelative('../../../z/x/y', '${prefix}z/x/y', '${prefix}a/b/c/');

// Cases where the arguments are relative paths.
expectRelative('c/d', 'a/b/c/d', 'a/b');
expectRelative('.', 'a/b/c', 'a/b/c');
expectRelative('.', 'a/d/../b/c', 'a/b/c/');
expectRelative('.', '', '');
expectRelative('.', '.', '');
expectRelative('.', '', '.');
expectRelative('.', '.', '.');
expectRelative('.', '..', '..');
if (isRelative) expectRelative('..', '..', '.');
expectRelative('a', 'a', '');
expectRelative('a', 'a', '.');
expectRelative('..', '.', 'a');
expectRelative('.', 'a/b/f/../c', 'a/e/../b/c');
expectRelative('d', 'a/b/f/../c/d', 'a/e/../b/c');
expectRelative('..', 'a/b/f/../c', 'a/e/../b/c/e/');
expectRelative('../..', '', 'a/b/');
if (isRelative) expectRelative('../../..', '..', 'a/b/');
expectRelative('../b/c/d', 'b/c/d/', 'a/');
expectRelative('../a/b/c', 'x/y/a//b/./f/../c', 'x//y/z');

// Case where from is an exact substring of path.
expectRelative('a/b', '${prefix}x/y//a/b', '${prefix}x/y/');
expectRelative('a/b', 'x/y//a/b', 'x/y/');
expectRelative('../ya/b', '${prefix}x/ya/b', '${prefix}x/y');
expectRelative('../ya/b', 'x/ya/b', 'x/y');
expectRelative('../b', 'x/y/../b', 'x/y/.');
expectRelative('a/b/c', 'x/y/a//b/./f/../c', 'x/y');
expectRelative('.', '${prefix}x/y//', '${prefix}x/y/');
expectRelative('.', '${prefix}x/y/', '${prefix}x/y');

// Should always throw - no relative path can be constructed.
if (isRelative) {
expect(() => builder.relative('.', from: '..'), throwsArgumentError);
expect(() => builder.relative('a/b', from: '../../d'),
throwsArgumentError);
expect(() => builder.relative('a/b', from: '${prefix}a/b'),
throwsArgumentError);
// An absolute path relative from a relative path returns the absolute path.
expectRelative('${prefix}a/b', '${prefix}a/b', 'c/d');
}
}
Loading

0 comments on commit 6d0caed

Please sign in to comment.