/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.sshd.common.file.util; import java.io.IOException; import java.net.URI; import java.nio.file.FileSystem; import java.nio.file.LinkOption; import java.nio.file.Path; import java.nio.file.attribute.UserPrincipalLookupService; import java.nio.file.spi.FileSystemProvider; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Set; import org.apache.sshd.common.util.GenericUtils; import org.apache.sshd.util.test.BaseTestSupport; import org.junit.Before; import org.junit.FixMethodOrder; import org.junit.Test; import org.junit.runners.MethodSorters; import org.mockito.Mockito; @FixMethodOrder(MethodSorters.NAME_ASCENDING) public class BasePathTest extends BaseTestSupport { private TestFileSystem fileSystem; public BasePathTest() { super(); } @Before public void setUp() { fileSystem = new TestFileSystem(Mockito.mock(FileSystemProvider.class)); } @Test public void testBasicPathParsing() { assertPathEquals("/", "/"); assertPathEquals("/foo", "/foo"); assertPathEquals("/foo", "/", "foo"); assertPathEquals("/foo/bar", "/foo/bar"); assertPathEquals("/foo/bar", "/", "foo", "bar"); assertPathEquals("/foo/bar", "/foo", "bar"); assertPathEquals("/foo/bar", "/", "foo/bar"); assertPathEquals("foo/bar/baz", "foo/bar/baz"); assertPathEquals("foo/bar/baz", "foo", "bar", "baz"); assertPathEquals("foo/bar/baz", "foo/bar", "baz"); assertPathEquals("foo/bar/baz", "foo", "bar/baz"); } @Test public void testPathParsingWithExtraSeparators() { assertPathEquals("/foo/bar", "///foo/bar"); assertPathEquals("/foo/bar", "/foo///bar//"); assertPathEquals("/foo/bar/baz", "/foo", "/bar", "baz/"); //assertPathEquals("/foo/bar/baz", "/foo\\/bar//\\\\/baz\\/"); } @Test public void testRootPath() { new PathTester(fileSystem, "/") .root("/") .test("/"); } @Test public void testRelativePathSingleName() { new PathTester(fileSystem, "test") .names("test") .test("test"); Path path = parsePath("test"); assertEquals(path, path.getFileName()); } @Test public void testRelativePathTwoNames() { PathTester tester = new PathTester(fileSystem, "foo/bar") .names("foo", "bar"); tester.test("foo/bar"); } @Test public void testRelativePathFourNames() { new PathTester(fileSystem, "foo/bar/baz/test") .names("foo", "bar", "baz", "test") .test("foo/bar/baz/test"); } @Test public void testAbsolutePathSingleName() { new PathTester(fileSystem, "/foo") .root("/") .names("foo") .test("/foo"); } @Test public void testAbsolutePathTwoNames() { new PathTester(fileSystem, "/foo/bar") .root("/") .names("foo", "bar") .test("/foo/bar"); } @Test public void testAbsoluteMultiNamePathFourNames() { new PathTester(fileSystem, "/foo/bar/baz/test") .root("/") .names("foo", "bar", "baz", "test") .test("/foo/bar/baz/test"); } @Test public void testResolveFromRoot() { Path root = parsePath("/"); assertResolvedPathEquals("/foo", root, "foo"); assertResolvedPathEquals("/foo/bar", root, "foo/bar"); assertResolvedPathEquals("/foo/bar", root, "foo", "bar"); assertResolvedPathEquals("/foo/bar/baz/test", root, "foo/bar/baz/test"); assertResolvedPathEquals("/foo/bar/baz/test", root, "foo", "bar/baz", "test"); } @Test public void testResolveFromAbsolute() { Path path = parsePath("/foo"); assertResolvedPathEquals("/foo/bar", path, "bar"); assertResolvedPathEquals("/foo/bar/baz/test", path, "bar/baz/test"); assertResolvedPathEquals("/foo/bar/baz/test", path, "bar/baz", "test"); assertResolvedPathEquals("/foo/bar/baz/test", path, "bar", "baz", "test"); } @Test public void testResolveFromRelative() { Path path = parsePath("foo"); assertResolvedPathEquals("foo/bar", path, "bar"); assertResolvedPathEquals("foo/bar/baz/test", path, "bar/baz/test"); assertResolvedPathEquals("foo/bar/baz/test", path, "bar", "baz", "test"); assertResolvedPathEquals("foo/bar/baz/test", path, "bar/baz", "test"); } @Test public void testResolveWithThisAndParentDirNames() { Path path = parsePath("/foo"); assertResolvedPathEquals("/foo/bar/../baz", path, "bar/../baz"); assertResolvedPathEquals("/foo/bar/../baz", path, "bar", "..", "baz"); assertResolvedPathEquals("/foo/./bar/baz", path, "./bar/baz"); assertResolvedPathEquals("/foo/./bar/baz", path, ".", "bar/baz"); } @Test public void testResolveGivenAbsolutePath() { assertResolvedPathEquals("/test", parsePath("/foo"), "/test"); assertResolvedPathEquals("/test", parsePath("foo"), "/test"); } @Test public void testResolveGivenEmptyPath() { assertResolvedPathEquals("/foo", parsePath("/foo"), ""); assertResolvedPathEquals("foo", parsePath("foo"), ""); } @Test public void testResolveAgainstEmptyPath() { assertResolvedPathEquals("foo/bar", parsePath(""), "foo/bar"); } @Test public void testResolveSiblingGivenEmptyPath() { Path path = parsePath("foo/bar"); Path resolved = path.resolveSibling(""); assertPathEquals("foo", resolved); path = parsePath("foo"); resolved = path.resolveSibling(""); assertPathEquals("", resolved); } @Test public void testResolveSiblingAgainstEmptyPath() { Path path = parsePath(""); Path resolved = path.resolveSibling("foo"); assertPathEquals("foo", resolved); path = parsePath(""); resolved = path.resolveSibling(""); assertPathEquals("", resolved); } @Test public void testRelativizeBothAbsolute() { assertRelativizedPathEquals("b/c", parsePath("/a"), "/a/b/c"); assertRelativizedPathEquals("c/d", parsePath("/a/b"), "/a/b/c/d"); } @Test public void testRelativizeBothRelative() { assertRelativizedPathEquals("b/c", parsePath("a"), "a/b/c"); assertRelativizedPathEquals("d", parsePath("a/b/c"), "a/b/c/d"); } @Test public void testRelativizeAgainstEmptyPath() { assertRelativizedPathEquals("foo/bar", parsePath(""), "foo/bar"); } @Test public void testRelativizeOneAbsoluteOneRelative() { try { parsePath("/foo/bar").relativize(parsePath("foo")); fail(); } catch (IllegalArgumentException expected) { // ignored } try { parsePath("foo").relativize(parsePath("/foo/bar")); fail(); } catch (IllegalArgumentException expected) { // ignored } } @Test public void testNormalizeWithParentDirName() { assertNormalizedPathEquals("/foo/baz", "/foo/bar/../baz"); assertNormalizedPathEquals("/foo/baz", "/foo", "bar", "..", "baz"); } @Test public void testNormalizeWithThisDirName() { assertNormalizedPathEquals("/foo/bar/baz", "/foo/bar/./baz"); assertNormalizedPathEquals("/foo/bar/baz", "/foo", "bar", ".", "baz"); } @Test public void testNormalizeWithThisAndParentDirNames() { assertNormalizedPathEquals("foo/test", "foo/./bar/../././baz/../test"); } @Test public void testNormalizeWithLeadingParentDirNames() { assertNormalizedPathEquals("../../foo/baz", "../../foo/bar/../baz"); } @Test public void testNormalizeWithLeadingThisAndParentDirNames() { assertNormalizedPathEquals("../../foo/baz", "./.././.././foo/bar/../baz"); } @Test public void testNormalizeWithExtraParentDirNamesAtRoot() { assertNormalizedPathEquals("/", "/.."); assertNormalizedPathEquals("/", "/../../.."); assertNormalizedPathEquals("/", "/foo/../../.."); assertNormalizedPathEquals("/", "/../foo/../../bar/baz/../../../.."); } @Test public void testPathWithExtraSlashes() { assertPathEquals("/foo/bar/baz", parsePath("/foo/bar/baz/")); assertPathEquals("/foo/bar/baz", parsePath("/foo//bar///baz")); assertPathEquals("/foo/bar/baz", parsePath("///foo/bar/baz")); } private void assertResolvedPathEquals( String expected, Path path, String firstResolvePath, String... moreResolvePaths) { Path resolved = path.resolve(firstResolvePath); for (String additionalPath : moreResolvePaths) { resolved = resolved.resolve(additionalPath); } assertPathEquals(expected, resolved); Path relative = parsePath(firstResolvePath, moreResolvePaths); resolved = path.resolve(relative); assertPathEquals(expected, resolved); // assert the invariant that p.relativize(p.resolve(q)).equals(q) when q does not have a root // p = path, q = relative, p.resolve(q) = resolved if (relative.getRoot() == null) { assertEquals(relative, path.relativize(resolved)); } } private void assertRelativizedPathEquals(String expected, Path path, String relativizePath) { Path relativized = path.relativize(parsePath(relativizePath)); assertPathEquals(expected, relativized); } private void assertNormalizedPathEquals(String expected, String first, String... more) { assertPathEquals(expected, parsePath(first, more).normalize()); } private void assertPathEquals(String expected, String first, String... more) { assertPathEquals(expected, parsePath(first, more)); } private void assertPathEquals(String expected, Path path) { assertEquals(parsePath(expected), path); } private Path parsePath(String first, String... more) { return fileSystem.getPath(first, more); } private static class TestFileSystem extends BaseFileSystem<TestPath> { TestFileSystem(FileSystemProvider fileSystemProvider) { super(fileSystemProvider); } @Override protected TestPath create(String root, List<String> names) { return new TestPath(this, root, names); } @Override public void close() throws IOException { // ignored } @Override public boolean isOpen() { return false; } @Override public Set<String> supportedFileAttributeViews() { return null; } @Override public UserPrincipalLookupService getUserPrincipalLookupService() { return null; } } private static class TestPath extends BasePath<TestPath, TestFileSystem> { TestPath(TestFileSystem fileSystem, String root, List<String> names) { super(fileSystem, root, names); } @Override protected TestPath create(String root, List<String> names) { return new TestPath(getFileSystem(), root, names); } @Override public URI toUri() { return null; } @Override public Path toRealPath(LinkOption... options) throws IOException { return null; } } public static class PathTester { private final FileSystem fileSystem; private final String string; private String root; private List<String> names = Collections.emptyList(); public PathTester(FileSystem fileSystem, String string) { this.fileSystem = fileSystem; this.string = string; } public PathTester root(String root) { this.root = root; return this; } public PathTester names(Collection<String> names) { this.names = GenericUtils.unmodifiableList(names); return this; } public PathTester names(String... names) { return names(Arrays.asList(names)); } public void test(String first, String... more) { Path path = fileSystem.getPath(first, more); test(path); } public void test(Path path) { assertEquals(string, path.toString()); testRoot(path); testNames(path); testParents(path); testStartsWith(path); testEndsWith(path); testSubpaths(path); } private void testRoot(Path path) { if (root != null) { assertTrue(path + ".isAbsolute() should be true", path.isAbsolute()); assertNotNull(path + ".getRoot() should not be null", path.getRoot()); assertEquals(root, path.getRoot().toString()); } else { assertFalse(path + ".isAbsolute() should be false", path.isAbsolute()); assertNull(path + ".getRoot() should be null", path.getRoot()); } } private void testNames(Path path) { assertEquals(names.size(), path.getNameCount()); assertEquals(names, names(path)); for (int i = 0; i < names.size(); i++) { assertEquals(names.get(i), path.getName(i).toString()); // don't test individual names if this is an individual name if (names.size() > 1) { new PathTester(fileSystem, names.get(i)) .names(names.get(i)) .test(path.getName(i)); } } if (names.size() > 0) { String fileName = names.get(names.size() - 1); assertEquals(fileName, path.getFileName().toString()); // don't test individual names if this is an individual name if (names.size() > 1) { new PathTester(fileSystem, fileName) .names(fileName) .test(path.getFileName()); } } } private void testParents(Path path) { Path parent = path.getParent(); if (((root != null) && (names.size() >= 1)) || (names.size() > 1)) { assertNotNull(parent); } if (parent != null) { String parentName = names.size() == 1 ? root : string.substring(0, string.lastIndexOf('/')); new PathTester(fileSystem, parentName) .root(root) .names(names.subList(0, names.size() - 1)) .test(parent); } } private void testSubpaths(Path path) { if (path.getRoot() == null) { assertEquals(path, path.subpath(0, path.getNameCount())); } if (path.getNameCount() > 1) { String stringWithoutRoot = root == null ? string : string.substring(root.length()); // test start + 1 to end and start to end - 1 subpaths... this recursively tests all subpaths // actually tests most possible subpaths multiple times but... eh Path startSubpath = path.subpath(1, path.getNameCount()); List<String> startNames = split(stringWithoutRoot, "/") .subList(1, path.getNameCount()); new PathTester(fileSystem, join(startNames, "/")) .names(startNames) .test(startSubpath); Path endSubpath = path.subpath(0, path.getNameCount() - 1); List<String> endNames = split(stringWithoutRoot, "/") .subList(0, path.getNameCount() - 1); new PathTester(fileSystem, join(endNames, "/")) .names(endNames) .test(endSubpath); } } private void testStartsWith(Path path) { // empty path doesn't start with any path if (root != null || !names.isEmpty()) { Path other = path; while (other != null) { assertTrue(path + ".startsWith(" + other + ") should be true", path.startsWith(other)); assertTrue(path + ".startsWith(" + other + ") should be true", path.startsWith(other.toString())); other = other.getParent(); } } } private void testEndsWith(Path path) { // empty path doesn't start with any path if (root != null || !names.isEmpty()) { Path other = path; while (other != null) { assertTrue(path + ".endsWith(" + other + ") should be true", path.endsWith(other)); assertTrue(path + ".endsWith(" + other + ") should be true", path.endsWith(other.toString())); if (other.getRoot() != null && other.getNameCount() > 0) { other = other.subpath(0, other.getNameCount()); } else if (other.getNameCount() > 1) { other = other.subpath(1, other.getNameCount()); } else { other = null; } } } } private static List<String> names(Path path) { List<String> list = new ArrayList<>(); for (Path p : path) { list.add(p.toString()); } return list; } private List<String> split(String string, String sep) { return Arrays.asList(string.split(sep)); } private static String join(Iterable<String> strings, String sep) { StringBuilder sb = new StringBuilder(); for (String s : strings) { if (sb.length() > 0) { sb.append(sep); } sb.append(s); } return sb.toString(); } } }