Port dart:io Path tests to package:path.
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
diff --git a/README.md b/README.md
index dacacaa..4108ce0 100644
--- a/README.md
+++ b/README.md
@@ -73,6 +73,16 @@
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
@@ -200,6 +210,9 @@
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.
diff --git a/lib/path.dart b/lib/path.dart
index cc0493c..bb88953 100644
--- a/lib/path.dart
+++ b/lib/path.dart
@@ -106,6 +106,17 @@
/// 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
@@ -248,6 +259,9 @@
/// 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.
@@ -582,8 +596,6 @@
///
/// 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();
@@ -613,6 +625,9 @@
/// 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.
///
@@ -624,8 +639,6 @@
/// 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.
@@ -672,8 +685,12 @@
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,
@@ -682,6 +699,13 @@
// 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();
@@ -1042,7 +1066,8 @@
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 == '') {
diff --git a/test/posix_test.dart b/test/posix_test.dart
index c26a5ba..3afb3bb 100644
--- a/test/posix_test.dart
+++ b/test/posix_test.dart
@@ -23,6 +23,8 @@
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'), '');
@@ -41,12 +43,16 @@
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'), '/');
@@ -61,12 +67,16 @@
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('/'), '/');
@@ -79,6 +89,8 @@
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');
@@ -93,6 +105,7 @@
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', () {
@@ -103,6 +116,8 @@
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);
@@ -117,6 +132,8 @@
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);
@@ -165,6 +182,14 @@
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', () {
@@ -212,12 +237,17 @@
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', () {
@@ -249,24 +279,38 @@
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', () {
@@ -274,6 +318,7 @@
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');
});
});
diff --git a/test/relative_test.dart b/test/relative_test.dart
new file mode 100644
index 0000000..b9cd254
--- /dev/null
+++ b/test/relative_test.dart
@@ -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');
+ }
+}
diff --git a/test/url_test.dart b/test/url_test.dart
index d040a59..1cd256d 100644
--- a/test/url_test.dart
+++ b/test/url_test.dart
@@ -89,10 +89,13 @@
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');
});
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');
@@ -107,6 +110,7 @@
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');
});
test('isAbsolute', () {
@@ -235,6 +239,14 @@
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', () {
@@ -305,7 +317,7 @@
group('normalize', () {
test('simple cases', () {
- expect(builder.normalize(''), '');
+ expect(builder.normalize(''), '.');
expect(builder.normalize('.'), '.');
expect(builder.normalize('..'), '..');
expect(builder.normalize('a'), 'a');
@@ -315,6 +327,11 @@
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/../'),
+ 'a/\xc5\u0bf8-;\u{1f085}\u{00}/c');
});
test('collapses redundant separators', () {
@@ -360,12 +377,16 @@
'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');
});
test('does not walk before root on absolute paths', () {
@@ -373,14 +394,23 @@
expect(builder.normalize('../'), '..');
expect(builder.normalize('http://dartlang.org/..'),
'http://dartlang.org');
+ expect(builder.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.');
});
test('removes trailing separators', () {
@@ -388,6 +418,7 @@
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');
});
});
diff --git a/test/windows_test.dart b/test/windows_test.dart
index 7409439..1966b7f 100644
--- a/test/windows_test.dart
+++ b/test/windows_test.dart
@@ -25,8 +25,12 @@
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'), '');
@@ -64,10 +68,14 @@
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');
});
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');
@@ -83,10 +91,15 @@
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');
});
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');
@@ -101,10 +114,13 @@
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');
});
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'), false);
@@ -124,6 +140,8 @@
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'), true);
@@ -184,6 +202,14 @@
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(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\');
+ });
});
group('joinAll', () {
@@ -238,13 +264,17 @@
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(r'\'), '.');
+ expect(builder.normalize('/'), r'.');
expect(builder.normalize('C:/'), r'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}\x5cc');
});
test('collapses redundant separators', () {
@@ -278,12 +308,19 @@
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');
+ // A path starting with '\' is not an absolute path on Windows.
+ 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.');
});
test('removes trailing separators', () {