content: /*
   * The Alluxio Open Foundation licenses this work under the Apache License, version 2.0
   * (the "License"). You may not use this work except in compliance with the License, which is
   * available at www.apache.org/licenses/LICENSE-2.0
   *
   * This software is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
   * either express or implied, as more fully set forth in the License.
   *
   * See the NOTICE file distributed with this work for information regarding copyright ownership.
   */
  
  package alluxio;
  
  import static org.junit.Assert.assertEquals;
  import static org.junit.Assert.assertFalse;
  import static org.junit.Assert.assertNotEquals;
  import static org.junit.Assert.assertTrue;
  import static org.junit.Assume.assumeTrue;
  
  import alluxio.uri.Authority;
  import alluxio.uri.EmbeddedLogicalAuthority;
  import alluxio.uri.MultiMasterAuthority;
  import alluxio.uri.NoAuthority;
  import alluxio.uri.SingleMasterAuthority;
  import alluxio.uri.UnknownAuthority;
  import alluxio.uri.ZookeeperAuthority;
  import alluxio.uri.ZookeeperLogicalAuthority;
  import alluxio.util.OSUtils;
  
  import com.google.common.testing.EqualsTester;
  import org.junit.Test;
  
  import java.util.HashMap;
  import java.util.Map;
  
  /**
   * Unit tests for {@link AlluxioURI}.
   */
  public class AlluxioURITest {
  
    private static final boolean WINDOWS = OSUtils.isWindows();
  
    /**
     * Tests the {@link AlluxioURI#AlluxioURI(String)} constructor for basic Alluxio paths.
     */
    @Test
    public void basicAlluxioUri() {
      AlluxioURI uri = new AlluxioURI("alluxio://localhost:19998/xy z/a b c");
  
      assertTrue(uri.hasAuthority());
      assertEquals("localhost:19998", uri.getAuthority().toString());
      assertTrue(uri.getAuthority() instanceof SingleMasterAuthority);
      SingleMasterAuthority authority = (SingleMasterAuthority) uri.getAuthority();
      assertEquals("localhost", authority.getHost());
      assertEquals(19998, authority.getPort());
  
      assertEquals(2, uri.getDepth());
      assertEquals("a b c", uri.getName());
      assertEquals("alluxio://localhost:19998/xy z", uri.getParent().toString());
      assertEquals("alluxio://localhost:19998/", uri.getParent().getParent().toString());
      assertEquals("/xy z/a b c", uri.getPath());
      assertEquals("alluxio", uri.getScheme());
      assertTrue(uri.hasScheme());
      assertTrue(uri.isAbsolute());
      assertTrue(uri.isPathAbsolute());
      assertEquals("alluxio://localhost:19998/xy z/a b c/d", uri.join("/d").toString());
      assertEquals("alluxio://localhost:19998/xy z/a b c/d", uri.join(new AlluxioURI("/d"))
          .toString());
      assertEquals("alluxio://localhost:19998/xy z/a b c", uri.toString());
    }
  
    /**
     * Tests the {@link AlluxioURI#AlluxioURI(String)} constructor for basic HDFS paths.
     */
    @Test
    public void basicHdfsUri() {
      AlluxioURI uri = new AlluxioURI("hdfs://localhost:8020/xy z/a b c");
  
      assertTrue(uri.hasAuthority());
      assertEquals("localhost:8020", uri.getAuthority().toString());
      assertTrue(uri.getAuthority() instanceof SingleMasterAuthority);
      SingleMasterAuthority authority = (SingleMasterAuthority) uri.getAuthority();
      assertEquals("localhost", authority.getHost());
      assertEquals(8020, authority.getPort());
  
      assertEquals(2, uri.getDepth());
      assertEquals("a b c", uri.getName());
      assertEquals("hdfs://localhost:8020/xy z", uri.getParent().toString());
      assertEquals("hdfs://localhost:8020/", uri.getParent().getParent().toString());
      assertEquals("/xy z/a b c", uri.getPath());
      assertEquals("hdfs", uri.getScheme());
      assertTrue(uri.hasScheme());
      assertTrue(uri.isAbsolute());
      assertTrue(uri.isPathAbsolute());
      assertEquals("hdfs://localhost:8020/xy z/a b c/d", uri.join("/d").toString());
      assertEquals("hdfs://localhost:8020/xy z/a b c/d", uri.join(new AlluxioURI("/d"))
          .toString());
      assertEquals("hdfs://localhost:8020/xy z/a b c", uri.toString());
    }
  
    @Test
    public void basicTwoPartUri() {
      AlluxioURI uri = new AlluxioURI("scheme:part2://localhost:8000/xy z/a b c");
      assertEquals(uri, new AlluxioURI("scheme:part2//localhost:8000/xy z/a b c"));
      assertEquals("scheme:part2", uri.getScheme());
  
      assertTrue(uri.hasAuthority());
      assertEquals("localhost:8000", uri.getAuthority().toString());
      assertTrue(uri.getAuthority() instanceof SingleMasterAuthority);
      SingleMasterAuthority authority = (SingleMasterAuthority) uri.getAuthority();
      assertEquals("localhost", authority.getHost());
      assertEquals(8000, authority.getPort());
  
      assertEquals(2, uri.getDepth());
      assertEquals("a b c", uri.getName());
      assertEquals("scheme:part2://localhost:8000/xy z", uri.getParent().toString());
      assertEquals("scheme:part2://localhost:8000/", uri.getParent().getParent().toString());
      assertEquals("/xy z/a b c", uri.getPath());
      assertTrue(uri.hasScheme());
      assertTrue(uri.isAbsolute());
      assertTrue(uri.isPathAbsolute());
      assertEquals("scheme:part2://localhost:8000/xy z/a b c/d", uri.join("/d").toString());
      assertEquals("scheme:part2://localhost:8000/xy z/a b c/d", uri.join(new AlluxioURI("/d"))
          .toString());
      assertEquals("scheme:part2://localhost:8000/xy z/a b c", uri.toString());
    }
  
    @Test
    public void basicMultiMasterUri() {
      AlluxioURI uri = new AlluxioURI("alluxio://host1:19998,host2:19998,host3:19998/xy z/a b c");
  
      assertTrue(uri.hasAuthority());
      assertEquals("host1:19998,host2:19998,host3:19998", uri.getAuthority().toString());
      assertTrue(uri.getAuthority() instanceof MultiMasterAuthority);
  
      assertEquals(2, uri.getDepth());
      assertEquals("a b c", uri.getName());
      assertEquals("alluxio://host1:19998,host2:19998,host3:19998/xy z", uri.getParent().toString());
      assertEquals("alluxio://host1:19998,host2:19998,host3:19998/",
          uri.getParent().getParent().toString());
      assertEquals("/xy z/a b c", uri.getPath());
      assertEquals("alluxio", uri.getScheme());
      assertTrue(uri.hasScheme());
      assertTrue(uri.isAbsolute());
      assertTrue(uri.isPathAbsolute());
      assertEquals("alluxio://host1:19998,host2:19998,host3:19998/xy z/a b c/d",
          uri.join("/d").toString());
      assertEquals("alluxio://host1:19998,host2:19998,host3:19998/xy z/a b c/d",
          uri.join(new AlluxioURI("/d")).toString());
      assertEquals("alluxio://host1:19998,host2:19998,host3:19998/xy z/a b c", uri.toString());
    }
  
    @Test
    public void semicolonMultiMasterUri() {
      AlluxioURI uri =
          new AlluxioURI("alluxio://host1:1323;host2:54325;host3:64354/xy z/a b c");
      assertTrue(uri.hasAuthority());
      assertEquals("host1:1323,host2:54325,host3:64354", uri.getAuthority().toString());
      assertTrue(uri.getAuthority() instanceof MultiMasterAuthority);
      MultiMasterAuthority authority = (MultiMasterAuthority) uri.getAuthority();
      assertEquals("host1:1323,host2:54325,host3:64354", authority.getMasterAddresses());
    }
  
    @Test
    public void plusMultiMasterUri() {
      AlluxioURI uri =
          new AlluxioURI("alluxio://host1:526+host2:54325+host3:624/xy z/a b c");
      assertTrue(uri.hasAuthority());
      assertTrue(uri.getAuthority() instanceof MultiMasterAuthority);
      MultiMasterAuthority authority = (MultiMasterAuthority) uri.getAuthority();
      assertEquals("host1:526,host2:54325,host3:624", authority.getMasterAddresses());
    }
  
    @Test
    public void basicZookeeperUri() {
      AlluxioURI uri =
          new AlluxioURI("alluxio://zk@host1:2181,host2:2181,host3:2181/xy z/a b c");
      assertEquals(uri,
          new AlluxioURI("alluxio://zk@host1:2181,host2:2181,host3:2181/xy z/a b c"));
      assertEquals("alluxio", uri.getScheme());
  
      assertEquals("zk@host1:2181,host2:2181,host3:2181", uri.getAuthority().toString());
      assertTrue(uri.getAuthority() instanceof ZookeeperAuthority);
      ZookeeperAuthority zkAuthority = (ZookeeperAuthority) uri.getAuthority();
      assertEquals("host1:2181,host2:2181,host3:2181", zkAuthority.getZookeeperAddress());
  
      assertEquals(2, uri.getDepth());
      assertEquals("a b c", uri.getName());
      assertEquals("alluxio://zk@host1:2181,host2:2181,host3:2181/xy z",
          uri.getParent().toString());
      assertEquals("alluxio://zk@host1:2181,host2:2181,host3:2181/",
          uri.getParent().getParent().toString());
      assertEquals("/xy z/a b c", uri.getPath());
      assertTrue(uri.hasAuthority());
      assertTrue(uri.hasScheme());
      assertTrue(uri.isAbsolute());
      assertTrue(uri.isPathAbsolute());
      assertEquals("alluxio://zk@host1:2181,host2:2181,host3:2181/xy z/a b c/d",
          uri.join("/d").toString());
      assertEquals("alluxio://zk@host1:2181,host2:2181,host3:2181/xy z/a b c/d",
          uri.join(new AlluxioURI("/d"))
          .toString());
      assertEquals("alluxio://zk@host1:2181,host2:2181,host3:2181/xy z/a b c",
          uri.toString());
    }
  
    @Test
    public void semicolonZookeeperUri() {
      AlluxioURI uri =
          new AlluxioURI("alluxio://zk@host1:2181;host2:2181;host3:2181/xy z/a b c");
      assertTrue(uri.hasAuthority());
      assertEquals("zk@host1:2181,host2:2181,host3:2181", uri.getAuthority().toString());
      assertTrue(uri.getAuthority() instanceof ZookeeperAuthority);
      ZookeeperAuthority zkAuthority = (ZookeeperAuthority) uri.getAuthority();
      assertEquals("host1:2181,host2:2181,host3:2181", zkAuthority.getZookeeperAddress());
    }
  
    /**
     * Tests the {@link AlluxioURI#AlluxioURI(String)} constructor with query parameter.
     */
    @Test
    public void basicConstructorQuery() {
      /**
       * Some encodings:
       * '&' -> %26
       * '=' -> %3D
       * ' ' -> %20
       * '%' -> %25
       * '+' -> %2B
       */
      String queryPart = "k1=v1&k2= spaces &k3=%3D%20escapes %20%25%26%2B&!@#$^*()-_=[]{};\"'<>,./";
      AlluxioURI uri = new AlluxioURI("hdfs://localhost/a?" + queryPart);
      Map<String, String> queryMap = uri.getQueryMap();
      assertEquals(4, queryMap.size());
      assertEquals("v1", queryMap.get("k1"));
      assertEquals(" spaces ", queryMap.get("k2"));
      assertEquals("= escapes  %&+", queryMap.get("k3"));
      assertEquals("[]{};\"'<>,./", queryMap.get("!@#$^*()-_"));
    }
  
    /**
     * Tests the {@link AlluxioURI#AlluxioURI(String)} constructor for basic paths.
     */
    @Test
    public void basicTests() {
      String[] strs =
          new String[] {"alluxio://localhost:19998/xyz/abc", "hdfs://localhost:19998/xyz/abc",
              "s3://localhost:19998/xyz/abc", "alluxio://localhost:19998/xy z/a b c",
              "hdfs://localhost:19998/xy z/a b c", "s3://localhost:19998/xy z/a b c"};
      for (String str : strs) {
        AlluxioURI uri = new AlluxioURI(str);
        assertEquals(str, uri.toString());
        assertEquals(2, uri.getDepth());
        assertTrue(uri.getAuthority() instanceof SingleMasterAuthority);
        SingleMasterAuthority authority = (SingleMasterAuthority) uri.getAuthority();
        assertEquals("localhost", authority.getHost());
        assertEquals(19998, authority.getPort());
      }
    }
  
    /**
     * Tests the {@link AlluxioURI#AlluxioURI(String)} constructor for an empty URI.
     */
    @Test
    public void emptyURI() {
      AlluxioURI uri = new AlluxioURI("");
      assertEquals("", uri.getAuthority().toString());
      assertTrue(uri.getAuthority() instanceof NoAuthority);
      assertEquals(0, uri.getDepth());
      assertEquals("", uri.getName());
      assertEquals("", uri.getPath());
      assertEquals(null, uri.getScheme());
      assertFalse(uri.hasAuthority());
      assertFalse(uri.hasScheme());
      assertFalse(uri.isAbsolute());
      assertFalse(uri.isPathAbsolute());
      assertEquals("/d", uri.join("/d").toString());
      assertEquals("/d", uri.join(new AlluxioURI("/d")).toString());
      assertEquals("", uri.toString());
    }
  
    /**
     * Tests the {@link AlluxioURI#AlluxioURI(String)} constructor for URI with spaces.
     */
    @Test
    public void pathWithWhiteSpaces() {
      String[] paths = new String[]{
          "/ ",
          "/  ",
          "/ path",
          "/path ",
          "/pa th",
          "/ pa th ",
          "/pa/ th",
          "/pa / th",
          "/ pa / th ",
      };
      for (String path : paths) {
        AlluxioURI uri = new AlluxioURI(path);
        assertEquals(path, uri.getPath());
        assertEquals(path, uri.toString());
        assertTrue(uri.isPathAbsolute());
      }
    }
  
    /**
     * Tests the {@link AlluxioURI#AlluxioURI(String, Authority, String)} constructor to build a URI
     * from its different components.
     */
    @Test
    public void constructFromComponentsTests() {
      String scheme = "alluxio";
      String authority = "127.0.0.1:90909";
      String path = "/a/../b/c.txt";
      String absPath = "/b/c.txt";
  
      AlluxioURI uri0 = new AlluxioURI(null, null, path);
      assertEquals(absPath, uri0.toString());
  
      AlluxioURI uri1 = new AlluxioURI(scheme, null, path);
      assertEquals(scheme + "://" + absPath, uri1.toString());
  
      AlluxioURI uri2 = new AlluxioURI(scheme, Authority.fromString(authority), path);
      assertEquals(scheme + "://" + authority + absPath, uri2.toString());
  
      AlluxioURI uri3 = new AlluxioURI(null, Authority.fromString(authority), path);
      assertEquals("//" + authority + absPath, uri3.toString());
  
      AlluxioURI uri4 = new AlluxioURI("scheme:part1", Authority.fromString(authority), path);
      assertEquals("scheme:part1://" + authority + absPath, uri4.toString());
  
      AlluxioURI uri5 = new AlluxioURI("scheme:part1:part2", Authority.fromString(authority), path);
      assertEquals("scheme:part1:part2://" + authority + absPath, uri5.toString());
    }
  
    /**
     * Tests the {@link AlluxioURI#AlluxioURI(String, Authority, String, Map)} constructor to build an
     * URI from its different components with a query map.
     */
    @Test
    public void constructWithQueryMap() {
      String scheme = "alluxio";
      String authority = "host:1234";
      String path = "/a";
      Map<String, String> queryMap = new HashMap<>();
      queryMap.put("key", "123");
      queryMap.put(" k2 ", " v2 ");
      queryMap.put(" key: !*'();:@&=+$,/?#[]\"% ", " !*'();:@&=+$,/?#[]\"% ");
      queryMap.put(" key: %26 %3D %20 %25 %2B ", " %26 %3D %20 %25 %2B ");
  
      AlluxioURI uri1 = new AlluxioURI(scheme, Authority.fromString(authority), path, queryMap);
      AlluxioURI uri2 = new AlluxioURI(uri1.toString());
      assertEquals(queryMap, uri1.getQueryMap());
      assertEquals(uri1.getQueryMap(), uri2.getQueryMap());
    }
  
    /**
     * Tests to resolve a child {@link AlluxioURI} against a parent {@link AlluxioURI}.
     */
    @Test
    public void constructFromParentAndChildTests() {
      testParentChild("", ".", ".");
      testParentChild("/", "/", ".");
      testParentChild("/", ".", "/");
      testParentChild("hdfs://localhost:8080/a/b/d.txt", "hdfs://localhost:8080/a/b/c.txt",
          "../d.txt");
      testParentChild("/foo/bar", "/foo", "bar");
      testParentChild("/foo/bar/baz", "/foo/bar", "baz");
      testParentChild("/foo/bar/baz", "/foo", "bar/baz");
      testParentChild("foo/bar", "foo", "bar");
      testParentChild("foo/bar/baz", "foo", "bar/baz");
      testParentChild("foo/bar/baz", "foo/bar", "baz");
      testParentChild("/foo", "/bar", "/foo");
      testParentChild("c:/foo", "/bar", "c:/foo");
      testParentChild("c:/foo", "d:/bar", "c:/foo");
      testParentChild("/foo/bar/baz/boo", "/foo/bar", "baz/boo");
      testParentChild("foo/bar/baz/bud", "foo/bar/", "baz/bud");
      testParentChild("/boo/bud", "/foo/bar", "../../boo/bud");
      testParentChild("boo/bud", "foo/bar", "../../boo/bud");
      testParentChild("/foo/boo/bud", "/foo/bar/baz", "../../boo/bud");
      testParentChild("foo/boo/bud", "foo/bar/baz", "../../boo/bud");
      testParentChild("../../../../boo/bud", "../../", "../../boo/bud");
      testParentChild("../../../../boo/bud", "../../foo", "../../../boo/bud");
      testParentChild("../../foo/boo/bud", "../../foo/bar", "../boo/bud");
      testParentChild("", "foo/bar/baz", "../../..");
      testParentChild("../..", "foo/bar/baz", "../../../../..");
  
      testParentChild("foo://bar boo:80/.", "foo://bar boo:80/.", ".");
      testParentChild("foo://bar boo:80/", "foo://bar boo:80/", ".");
      testParentChild("foo://bar boo:80/", "foo://bar boo:80/.", "/");
      testParentChild("foo://bar boo:80/foo", "foo://bar boo:80/", "foo");
      testParentChild("foo://bar boo:80/foo/bar", "foo://bar boo:80/foo", "bar");
      testParentChild("foo://bar boo:80/foo/bar/baz", "foo://bar boo:80/foo/bar", "baz");
      testParentChild("foo://bar boo:80/foo", "foo://bar boo:80/.", "foo");
      testParentChild("foo://bar boo:80/foo/bar/baz", "foo://bar boo:80/foo", "bar/baz");
      testParentChild("foo://bar boo:80/foo/bar/baz", "foo://bar boo:80/foo/bar", "baz");
      testParentChild("foo://bar boo:80/foo", "foo://bar boo:80/bar", "/foo");
      testParentChild("foo://bar boo:80/c:/foo", "foo://bar boo:80/bar", "c:/foo");
      testParentChild("foo://bar boo:80/c:/foo", "foo://bar boo:80/d:/bar", "c:/foo");
      testParentChild("foo://bar boo:80/c:/foo", "foo://bar boo:80/d:/bar",
          "foo://bar boo:80/c:/foo");
  
      testParentChild("parent://host:1234/a/d.txt", "parent://host:1234/a", "d.txt");
      testParentChild("parent:part1://host:1234/a/d.txt", "parent:part1://host:1234/a", "d.txt");
      testParentChild("parent:part1:part2://host:1234/a/d.txt", "parent:part1:part2://host:1234/a",
          "d.txt");
      testParentChild("child://h:1/d.txt", "parent://host:1234/a", "child://h:1/d.txt");
      testParentChild("child:part1://h:1/d.txt", "parent://host:1234/a", "child:part1://h:1/d.txt");
      testParentChild("child:part1:part2://h:1/d.txt", "parent://host:1234/a",
          "child:part1:part2://h:1/d.txt");
    }
  
    /**
     * Tests the {@link AlluxioURI#compareTo(AlluxioURI)} method.
     */
    @Test
    public void compareToTests() {
      AlluxioURI[] uris =
          new AlluxioURI[] {new AlluxioURI("file://127.0.0.0:8081/a/b/c.txt"),
              new AlluxioURI("glusterfs://127.0.0.0:8081/a/b/c.txt"),
              new AlluxioURI("hdfs://127.0.0.0:8081/a/b/c.txt"),
              new AlluxioURI("hdfs://127.0.0.1:8081/a/b/c.txt"),
              new AlluxioURI("hdfs://127.0.0.1:8081/a/b/d.txt"),
              new AlluxioURI("hdfs://127.0.0.1:8081/a/c/c.txt"),
              new AlluxioURI("hdfs://127.0.0.1:8082/a/c/c.txt"),
              new AlluxioURI("hdfs://localhost:8080/a/b/c.txt"),
              new AlluxioURI("s3://localhost:8080/a/b/c.txt"),
              new AlluxioURI("scheme://localhost:8080/a.txt"),
              new AlluxioURI("scheme://localhost:8080/a.txt?a=a"),
              new AlluxioURI("scheme://localhost:8080/a.txt?b=b"),
              new AlluxioURI("scheme://localhost:8080/a.txt?c=c"),
              new AlluxioURI("scheme:scheme://localhost:8080/a.txt"),
              new AlluxioURI("scheme:scheme://localhost:8080/b.txt"),
              new AlluxioURI("scheme:schemeB://localhost:8080/a.txt"),
              new AlluxioURI("scheme:schemeB://localhost:8080/b.txt"),
              new AlluxioURI("schemeA:scheme://localhost:8080/a.txt"),
              new AlluxioURI("schemeA:scheme://localhost:8080/b.txt"),
              new AlluxioURI("schemeA:schemeB:schemeC://localhost:8080/a.txt"),
              new AlluxioURI("schemeA:schemeB:schemeC://localhost:8080/b.txt"),
              new AlluxioURI("schemeA:schemeB:schemeD://localhost:8080/a.txt"),
              new AlluxioURI("schemeA:schemeB:schemeD://localhost:8080/b.txt"),
              new AlluxioURI("schemeE:schemeB:schemeB://localhost:8080/a.txt"),
              new AlluxioURI("schemeE:schemeB:schemeB://localhost:8080/b.txt"),
          };
  
      for (int i = 0; i < uris.length - 1; i++) {
        assertTrue(uris[i].compareTo(uris[i + 1]) < 0);
        assertTrue(uris[i + 1].compareTo(uris[i]) > 0);
        assertEquals(0, uris[i].compareTo(uris[i]));
      }
    }
  
    /**
     * Tests the {@link AlluxioURI#equals(Object)} method.
     */
    @Test
    public void equalsTests() {
      assertFalse(new AlluxioURI("alluxio://127.0.0.1:8080/a/b/c.txt").equals(new AlluxioURI(
          "alluxio://localhost:8080/a/b/c.txt")));
  
      AlluxioURI[] uriFromDifferentConstructor =
          new AlluxioURI[] {new AlluxioURI("alluxio://127.0.0.1:8080/a/b/c.txt"),
              new AlluxioURI("alluxio", Authority.fromString("127.0.0.1:8080"), "/a/b/c.txt"),
              new AlluxioURI(
                  new AlluxioURI("alluxio://127.0.0.1:8080/a"), new AlluxioURI("b/c.txt"))};
      for (int i = 0; i < uriFromDifferentConstructor.length - 1; i++) {
        assertTrue(uriFromDifferentConstructor[i].equals(uriFromDifferentConstructor[i + 1]));
      }
    }
  
    /**
     * Tests the {@link AlluxioURI#equals(Object)} method for multi-component schemes.
     */
    @Test
    public void multiPartSchemeEquals() {
      assertTrue(new AlluxioURI("scheme:part1://127.0.0.1:3306/a.txt")
          .equals(new AlluxioURI("scheme:part1://127.0.0.1:3306/a.txt")));
      assertFalse(new AlluxioURI("part1://127.0.0.1:3306/a.txt")
          .equals(new AlluxioURI("scheme:part1://127.0.0.1:3306/a.txt")));
      assertFalse(new AlluxioURI("scheme:part1://127.0.0.1:3306/a.txt")
          .equals(new AlluxioURI("part1://127.0.0.1:3306/a.txt")));
  
      assertTrue(new AlluxioURI("scheme:part1:part2://127.0.0.1:3306/a.txt")
          .equals(new AlluxioURI("scheme:part1:part2://127.0.0.1:3306/a.txt")));
      assertFalse(new AlluxioURI("part2://127.0.0.1:3306/a.txt")
          .equals(new AlluxioURI("scheme:part1:part2://127.0.0.1:3306/a.txt")));
      assertFalse(new AlluxioURI("scheme:part1:part2://127.0.0.1:3306/a.txt")
          .equals(new AlluxioURI("part2://127.0.0.1:3306/a.txt")));
  
      new EqualsTester()
          .addEqualityGroup(new AlluxioURI("sch:p1:p2://aaaabbbb:12345/"),
              new AlluxioURI("sch:p1:p2://aaaabbbb:12345/"))
          .addEqualityGroup(new AlluxioURI("standard://host:12345/"))
          .testEquals();
    }
  
    /**
     * Tests the {@link AlluxioURI#equals(Object)} method with query component.
     */
    @Test
    public void queryEquals() {
      Map<String, String> queryMap = new HashMap<>();
      queryMap.put("a", "b");
      queryMap.put("c", "d");
  
      assertTrue(new AlluxioURI("scheme://host:123/a.txt?a=b&c=d")
          .equals(new AlluxioURI("scheme://host:123/a.txt?a=b&c=d")));
      // There is no guarantee which order the queryMap will create the query string.
      assertTrue(new AlluxioURI("scheme://host:123/a.txt?c=d&a=b")
          .equals(new AlluxioURI("scheme", Authority.fromString("host:123"), "/a.txt", queryMap))
          || new AlluxioURI("scheme://host:123/a.txt?a=b&c=d")
          .equals(new AlluxioURI("scheme", Authority.fromString("host:123"), "/a.txt", queryMap)));
  
      assertFalse(new AlluxioURI("scheme://host:123/a.txt?a=b&c=d&e=f")
          .equals(new AlluxioURI("scheme://host:123/a.txt?a=b&c=d")));
      assertFalse(new AlluxioURI("scheme://host:123/a.txt?a=b&c=d&e=f")
          .equals(new AlluxioURI("scheme", Authority.fromString("host:123"), "/a.txt", queryMap)));
    }
  
    /**
     * Tests the {@link AlluxioURI#getAuthority()} method.
     */
    @Test
    public void getAuthorityTests() {
      String[] authorities =
          new String[] {"localhost", "localhost:8080", "127.0.0.1", "127.0.0.1:8080", "localhost"};
      for (String authority : authorities) {
        AlluxioURI uri = new AlluxioURI("file", Authority.fromString(authority), "/a/b");
        assertEquals(authority, uri.getAuthority().toString());
      }
  
      assertEquals("",
          new AlluxioURI("file", Authority.fromString(""), "/b/c").getAuthority().toString());
      assertEquals("", new AlluxioURI("file", null, "/b/c").getAuthority().toString());
      assertEquals("",
          new AlluxioURI("file", Authority.fromString(null), "/b/c").getAuthority().toString());
      assertEquals("", new AlluxioURI("file:///b/c").getAuthority().toString());
    }
  
    @Test
    public void authorityTypeTests() {
      assertTrue(new AlluxioURI("file", Authority.fromString("localhost:8080"), "/b/c").getAuthority()
          instanceof SingleMasterAuthority);
  
      assertTrue(new AlluxioURI("file", Authority.fromString("zk@host:2181"), "/b/c").getAuthority()
          instanceof ZookeeperAuthority);
      assertTrue(new AlluxioURI("alluxio://zk@host1:2181,host2:2181,host3:2181/b/c").getAuthority()
          instanceof ZookeeperAuthority);
      assertTrue(new AlluxioURI("alluxio://zk@host1:2181;host2:2181;host3:2181/b/c").getAuthority()
          instanceof ZookeeperAuthority);
  
      assertTrue(new AlluxioURI("file", Authority.fromString(""), "/b/c").getAuthority()
          instanceof NoAuthority);
      assertTrue(new AlluxioURI("file", null, "/b/c").getAuthority()
          instanceof NoAuthority);
      assertTrue(new AlluxioURI("file", Authority.fromString(null), "/b/c").getAuthority()
          instanceof NoAuthority);
      assertTrue(new AlluxioURI("file:///b/c").getAuthority()
          instanceof NoAuthority);
  
      assertTrue(new AlluxioURI("file", Authority.fromString("ebj@logical"), "/b/c").getAuthority()
          instanceof EmbeddedLogicalAuthority);
  
      assertTrue(new AlluxioURI("file", Authority.fromString("zk@logical"), "/b/c").getAuthority()
          instanceof ZookeeperLogicalAuthority);
  
      assertTrue(new AlluxioURI("file", Authority.fromString("localhost"), "/b/c").getAuthority()
          instanceof UnknownAuthority);
    }
  
    /**
     * Tests the {@link AlluxioURI#getDepth()} method.
     */
    @Test
    public void getDepthTests() {
      assertEquals(0, new AlluxioURI("").getDepth());
      assertEquals(0, new AlluxioURI(".").getDepth());
      assertEquals(0, new AlluxioURI("/").getDepth());
      assertEquals(1, new AlluxioURI("/a").getDepth());
      assertEquals(3, new AlluxioURI("/a/b/c.txt").getDepth());
      assertEquals(2, new AlluxioURI("/a/b/").getDepth());
      assertEquals(2, new AlluxioURI("a\\b").getDepth());
      assertEquals(1, new AlluxioURI("C:\\a").getDepth());
      assertEquals(1, new AlluxioURI("C:\\\\a").getDepth());
      assertEquals(0, new AlluxioURI("C:\\\\").getDepth());
      assertEquals(0, new AlluxioURI("alluxio://localhost:19998/").getDepth());
      assertEquals(1, new AlluxioURI("alluxio://localhost:19998/a").getDepth());
      assertEquals(2, new AlluxioURI("alluxio://localhost:19998/a/b.txt").getDepth());
    }
  
    /**
     * Tests the {@link AlluxioURI#getName()} method.
     */
    @Test
    public void getNameTests() {
      assertEquals(".", new AlluxioURI(".").getName());
      assertEquals("", new AlluxioURI("/").getName());
      assertEquals("", new AlluxioURI("alluxio://localhost/").getName());
      assertEquals("", new AlluxioURI("alluxio:/").getName());
      assertEquals("a", new AlluxioURI("alluxio:/a/").getName());
      assertEquals("a.txt", new AlluxioURI("alluxio:/a.txt/").getName());
      assertEquals(" b.txt", new AlluxioURI("alluxio:/a/ b.txt").getName());
      assertEquals("a.txt", new AlluxioURI("/a/a.txt").getName());
    }
  
    /**
     * Tests the {@link AlluxioURI#getParent()} method.
     */
    @Test
    public void getParentTests() {
      assertEquals(null, new AlluxioURI("/").getParent());
      assertEquals(null,
          new AlluxioURI("alluxio://localhost/").getParent());
      assertEquals(new AlluxioURI("alluxio://localhost/"),
          new AlluxioURI("alluxio://localhost/a").getParent());
      assertEquals(new AlluxioURI("/a"), new AlluxioURI("/a/b/../c").getParent());
      assertEquals(new AlluxioURI("alluxio:/a"),
          new AlluxioURI("alluxio:/a/b/../c").getParent());
      assertEquals(new AlluxioURI("alluxio://localhost:80/a"), new AlluxioURI(
          "alluxio://localhost:80/a/b/../c").getParent());
    }
  
    /**
     * Tests the {@link AlluxioURI#getPath()} method.
     */
    @Test
    public void getPathTests() {
      assertEquals(".", new AlluxioURI(".").getPath());
      assertEquals("/", new AlluxioURI("/").getPath());
      assertEquals("/", new AlluxioURI("alluxio:/").getPath());
      assertEquals("/", new AlluxioURI("alluxio://localhost:80/").getPath());
      assertEquals("/a.txt", new AlluxioURI("alluxio://localhost:80/a.txt").getPath());
      assertEquals("/b", new AlluxioURI("alluxio://localhost:80/a/../b").getPath());
      assertEquals("/b", new AlluxioURI("alluxio://localhost:80/a/c/../../b").getPath());
      assertEquals("/a/b", new AlluxioURI("alluxio://localhost:80/a/./b").getPath());
      assertEquals("/a/b", new AlluxioURI("/a/b").getPath());
      assertEquals("/a/b", new AlluxioURI("file:///a/b").getPath());
    }
  
    /**
     * Tests the {@link AlluxioURI#getScheme()} method.
     */
    @Test
    public void getSchemeTests() {
      assertEquals(null, new AlluxioURI(".").getScheme());
      assertEquals(null, new AlluxioURI("/").getScheme());
      assertEquals("file", new AlluxioURI("file:/").getScheme());
      assertEquals("file", new AlluxioURI("file://localhost/").getScheme());
      assertEquals("s3", new AlluxioURI("s3://localhost/").getScheme());
      assertEquals("alluxio", new AlluxioURI("alluxio://localhost/").getScheme());
      assertEquals("hdfs", new AlluxioURI("hdfs://localhost/").getScheme());
      assertEquals("glusterfs", new AlluxioURI("glusterfs://localhost/").getScheme());
      assertEquals("scheme:part1", new AlluxioURI("scheme:part1://localhost/").getScheme());
      assertEquals("scheme:part1:part2",
          new AlluxioURI("scheme:part1:part2://localhost/").getScheme());
    }
  
    /**
     * Tests the {@link AlluxioURI#hasAuthority()} method.
     */
    @Test
    public void hasAuthorityTests() {
      assertFalse(new AlluxioURI(".").hasAuthority());
      assertFalse(new AlluxioURI("/").hasAuthority());
      assertFalse(new AlluxioURI("file:/").hasAuthority());
      assertFalse(new AlluxioURI("file:///test").hasAuthority());
      assertTrue(new AlluxioURI("file://localhost/").hasAuthority());
      assertTrue(new AlluxioURI("file://localhost:8080/").hasAuthority());
      assertTrue(new AlluxioURI(null, Authority.fromString("localhost:8080"), "/").hasAuthority());
      assertTrue(new AlluxioURI(null, Authority.fromString("localhost"), "/").hasAuthority());
    }
  
    /**
     * Tests the {@link AlluxioURI#hasScheme()} method.
     */
    @Test
    public void hasScheme() {
      assertFalse(new AlluxioURI("/").hasScheme());
      assertTrue(new AlluxioURI("file:/").hasScheme());
      assertTrue(new AlluxioURI("file://localhost/").hasScheme());
      assertTrue(new AlluxioURI("file://localhost:8080/").hasScheme());
      assertFalse(new AlluxioURI("//localhost:8080/").hasScheme());
    }
  
    /**
     * Tests the {@link AlluxioURI#isAbsolute()} method.
     */
    @Test
    public void isAbsoluteTests() {
      assertTrue(new AlluxioURI("file:/a").isAbsolute());
      assertTrue(new AlluxioURI("file://localhost/a").isAbsolute());
      assertFalse(new AlluxioURI("//localhost/a").isAbsolute());
      assertFalse(new AlluxioURI("//localhost/").isAbsolute());
      assertFalse(new AlluxioURI("/").isAbsolute());
    }
  
    /**
     * Tests the {@link AlluxioURI#isPathAbsolute()} method.
     */
    @Test
    public void isPathAbsoluteTests() {
      assertFalse(new AlluxioURI(".").isPathAbsolute());
      assertTrue(new AlluxioURI("/").isPathAbsolute());
      assertTrue(new AlluxioURI("file:/").isPathAbsolute());
      assertTrue(new AlluxioURI("file://localhost/").isPathAbsolute());
      assertTrue(new AlluxioURI("file://localhost/a/b").isPathAbsolute());
      assertFalse(new AlluxioURI("a/b").isPathAbsolute());
      assertTrue(new AlluxioURI("C:\\\\a\\b").isPathAbsolute());
    }
  
    /**
     * Tests the {@link AlluxioURI#isRoot()} method.
     */
    @Test
    public void isRootTests() {
      assertFalse(new AlluxioURI(".").isRoot());
      assertTrue(new AlluxioURI("/").isRoot());
      assertTrue(new AlluxioURI("file:/").isRoot());
      assertTrue(new AlluxioURI("alluxio://localhost:19998").isRoot());
      assertTrue(new AlluxioURI("alluxio://localhost:19998/").isRoot());
      assertTrue(new AlluxioURI("hdfs://localhost:19998").isRoot());
      assertTrue(new AlluxioURI("hdfs://localhost:19998/").isRoot());
      assertTrue(new AlluxioURI("file://localhost/").isRoot());
      assertFalse(new AlluxioURI("file://localhost/a/b").isRoot());
      assertFalse(new AlluxioURI("a/b").isRoot());
    }
  
    /**
     * Tests the {@link AlluxioURI#join(String)} and {@link AlluxioURI#join(AlluxioURI)} methods.
     */
    @Test
    public void joinTests() {
      assertEquals(new AlluxioURI("/a"), new AlluxioURI("/").join("a"));
      assertEquals(new AlluxioURI("/a"), new AlluxioURI("/").join(new AlluxioURI("a")));
      assertEquals(new AlluxioURI("/a/b"), new AlluxioURI("/a").join(new AlluxioURI("b")));
      assertEquals(new AlluxioURI("a/b"), new AlluxioURI("a").join(new AlluxioURI("b")));
      assertEquals(new AlluxioURI("/a/c"),
          new AlluxioURI("/a").join(new AlluxioURI("b/../c")));
      assertEquals(new AlluxioURI("a/b.txt"),
          new AlluxioURI("a").join(new AlluxioURI("/b.txt")));
      assertEquals(new AlluxioURI("a/b.txt"),
          new AlluxioURI("a").join(new AlluxioURI("/c/../b.txt")));
      assertEquals(new AlluxioURI("alluxio:/a/b.txt"),
          new AlluxioURI("alluxio:/a").join("/b.txt"));
      assertEquals(new AlluxioURI("alluxio:/a/b.txt"),
          new AlluxioURI("alluxio:/a/c.txt").join(new AlluxioURI("/../b.txt")));
      assertEquals(new AlluxioURI("C:\\\\a\\b"),
          new AlluxioURI("C:\\\\a").join(new AlluxioURI("\\b")));
      assertEquals(new AlluxioURI("/a/b"), new AlluxioURI("/a").joinUnsafe("///b///"));
  
      final String pathWithSpecialChar = "����,��b����$o����[| =B����";
      assertEquals(new AlluxioURI("/" + pathWithSpecialChar),
              new AlluxioURI("/").join(pathWithSpecialChar));
  
      final String pathWithSpecialCharAndColon = "����,��b����$o����[| =B��:��";
      assertEquals(new AlluxioURI("/" + pathWithSpecialCharAndColon),
          new AlluxioURI("/").join(pathWithSpecialCharAndColon));
  
      // join empty string
      assertEquals(new AlluxioURI("/a"), new AlluxioURI("/a").join(""));
      assertEquals(new AlluxioURI("/a"), new AlluxioURI("/a").join(new AlluxioURI("")));
    }
  
    @Test
    public void joinUnsafe() {
      assertEquals(new AlluxioURI("/a"), new AlluxioURI("/").joinUnsafe("a"));
      assertEquals(new AlluxioURI("/a/b"), new AlluxioURI("/a").joinUnsafe("b"));
      assertEquals(new AlluxioURI("a/b"), new AlluxioURI("a").joinUnsafe("b"));
      assertEquals(new AlluxioURI("a/b.txt"), new AlluxioURI("a").joinUnsafe("/b.txt"));
      assertEquals(new AlluxioURI("alluxio:/a/b.txt"),
          new AlluxioURI("alluxio:/a").joinUnsafe("/b.txt"));
      assertEquals(new AlluxioURI("C:\\\\a\\b"), new AlluxioURI("C:\\\\a").joinUnsafe("\\b"));
      assertEquals(new AlluxioURI("/a/b"), new AlluxioURI("/a").joinUnsafe("///b///"));
  
      final String pathWithSpecialChar = "����,��b����$o����[| =B����";
      assertEquals(new AlluxioURI("/" + pathWithSpecialChar),
          new AlluxioURI("/").joinUnsafe(pathWithSpecialChar));
  
      final String pathWithSpecialCharAndColon = "����,��b����$o����[| =B��:��";
      assertEquals(new AlluxioURI("/" + pathWithSpecialCharAndColon),
          new AlluxioURI("/").joinUnsafe(pathWithSpecialCharAndColon));
  
      // The following joins are not "safe", because the new path component requires normalization.
      assertNotEquals(new AlluxioURI("/a/c"), new AlluxioURI("/a").joinUnsafe("b/../c"));
      assertNotEquals(new AlluxioURI("a/b.txt"), new AlluxioURI("a").joinUnsafe("/c/../b.txt"));
      assertNotEquals(new AlluxioURI("alluxio:/a/b.txt"),
          new AlluxioURI("alluxio:/a/c.txt").joinUnsafe("/../b.txt"));
  
      // join empty string
      assertEquals(new AlluxioURI("/a"), new AlluxioURI("/a").joinUnsafe(""));
    }
  
    /**
     * Tests the {@link AlluxioURI#AlluxioURI(String)} constructor to work with file URIs
     * appropriately.
     */
    @Test
    public void fileUriTests() {
      AlluxioURI uri = new AlluxioURI("file:///foo/bar");
      assertFalse(uri.hasAuthority());
      assertEquals("/foo/bar", uri.getPath());
      assertEquals("file:///foo/bar", uri.toString());
    }
  
    /**
     * Tests the {@link AlluxioURI#AlluxioURI(String)} constructor to work with Windows paths
     * appropriately.
     */
    @Test
    public void windowsPathTests() {
      assumeTrue(WINDOWS);
  
      AlluxioURI uri = new AlluxioURI("C:\\foo\\bar");
      assertFalse(uri.hasAuthority());
      assertEquals("/C:/foo/bar", uri.getPath());
      assertEquals("C:/foo/bar", uri.toString());
    }
  
    /**
     * Tests the {@link AlluxioURI#toString()} method to work with paths appropriately.
     */
    @Test
    public void toStringTests() {
      String[] uris =
          new String[] {"/", "/a", "/a/ b", "alluxio://a/b/c d.txt",
              "alluxio://localhost:8080/a/b.txt", "foo", "foo/bar", "/foo/bar#boo", "foo/bar#boo",
              "file:///foo/bar"};
      for (String uri : uris) {
        AlluxioURI turi = new AlluxioURI(uri);
        assertEquals(uri, turi.toString());
      }
  
      assertEquals(".", new AlluxioURI(".").toString());
      assertEquals("file:///a", new AlluxioURI("file:///a").toString());
      assertEquals("file:///a", new AlluxioURI("file", null, "/a").toString());
    }
  
    /**
     * Tests the {@link AlluxioURI#toString()} method to work with Windows paths appropriately.
     */
    @Test
    public void toStringWindowsTests() {
      assumeTrue(WINDOWS);
  
      String[] uris =
          new String[] { "c:/", "c:/foo/bar", "C:/foo/bar#boo", "C:/foo/ bar" };
      for (String uri : uris) {
        AlluxioURI turi = new AlluxioURI(uri);
        assertEquals(uri, turi.toString());
      }
  
      assertEquals("C:/", new AlluxioURI("C:\\\\").toString());
      assertEquals("C:/a/b.txt", new AlluxioURI("C:\\\\a\\b.txt").toString());
    }
  
    /**
     * Tests the {@link AlluxioURI#toString()} method to normalize paths.
     */
    @Test
    public void normalizeTests() {
      assertEquals("/", new AlluxioURI("//").toString());
      assertEquals("/foo", new AlluxioURI("/foo/").toString());
      assertEquals("/foo", new AlluxioURI("/foo/").toString());
      assertEquals("foo", new AlluxioURI("foo/").toString());
      assertEquals("foo", new AlluxioURI("foo//").toString());
      assertEquals("foo/bar", new AlluxioURI("foo//bar").toString());
  
      assertEquals("foo/boo", new AlluxioURI("foo/bar/..//boo").toString());
      assertEquals("foo/boo/baz", new AlluxioURI("foo/bar/..//boo/./baz").toString());
      assertEquals("../foo/boo", new AlluxioURI("../foo/bar/..//boo").toString());
      assertEquals("/../foo/boo", new AlluxioURI("/.././foo/boo").toString());
      assertEquals("foo/boo", new AlluxioURI("./foo/boo").toString());
  
      assertEquals("foo://bar boo:8080/abc/c",
          new AlluxioURI("foo://bar boo:8080/abc///c").toString());
    }
  
    /**
     * Tests the {@link AlluxioURI#toString()} method to normalize Windows paths.
     */
    @Test
    public void normalizeWindowsTests() {
      assumeTrue(WINDOWS);
  
      assertEquals("c:/a/b", new AlluxioURI("c:\\a\\b").toString());
      assertEquals("c:/a/c", new AlluxioURI("c:\\a\\b\\..\\c").toString());
    }
  
    /**
     * Tests the {@link AlluxioURI#AlluxioURI(String, Authority, String)} constructor to throw an
     * exception in case an empty path was provided.
     */
    @Test(expected = IllegalArgumentException.class)
    public void constructFromEmptyPathTest2() {
      new AlluxioURI(null, null, null);
    }
  
    /**
     * Tests the {@link AlluxioURI#AlluxioURI(String, Authority, String)} constructor to throw an
     * exception in case an empty path was provided.
     */
    @Test(expected = IllegalArgumentException.class)
    public void constructFromEmptyPathTest3() {
      new AlluxioURI("file", null, "");
    }
  
    /**
     * Tests the {@link AlluxioURI#AlluxioURI(String)} constructor to throw an exception in case an
     * invalid URI was provided.
     */
    @Test(expected = IllegalArgumentException.class)
    public void invalidURISyntax() {
      new AlluxioURI("://localhost:8080/a");
    }
  
    /**
     * Tests to resolve a child {@link AlluxioURI} against a parent {@link AlluxioURI}.
     *
     * @param target the target path
     * @param parent the parent path
     * @param child the child path
     */
    private void testParentChild(String target, String parent, String child) {
      if (target.length() > 0) {
        assertEquals(new AlluxioURI(target), new AlluxioURI(new AlluxioURI(parent),
            new AlluxioURI(child)));
      } else {
        assertEquals(target,
            new AlluxioURI(new AlluxioURI(parent), new AlluxioURI(child)).toString());
      }
    }
  
    /**
     * Tests the {@link AlluxioURI#getLeadingPath(int)} method.
     */
    @Test
    public void getLeadingPath() {
      assertEquals("/",      new AlluxioURI("/a/b/c/").getLeadingPath(0));
      assertEquals("/a",     new AlluxioURI("/a/b/c/").getLeadingPath(1));
      assertEquals("/a/b",   new AlluxioURI("/a/b/c/").getLeadingPath(2));
      assertEquals("/a/b/c", new AlluxioURI("/a/b/c/").getLeadingPath(3));
      assertEquals(null,     new AlluxioURI("/a/b/c/").getLeadingPath(4));
  
      assertEquals("/",      new AlluxioURI("/").getLeadingPath(0));
  
      assertEquals("",       new AlluxioURI("").getLeadingPath(0));
      assertEquals(null,     new AlluxioURI("").getLeadingPath(1));
      assertEquals(".",       new AlluxioURI(".").getLeadingPath(0));
      assertEquals(null,     new AlluxioURI(".").getLeadingPath(1));
      assertEquals("a/b",    new AlluxioURI("a/b/c").getLeadingPath(1));
    }
  
    /**
     * Tests the {@link AlluxioURI#getRootPath()} method.
     */
    @Test
    public void getRootPath() {
      assertEquals("s3a://s3-bucket-name/",
          new AlluxioURI("s3a://s3-bucket-name/").getRootPath());
      assertEquals("s3a://s3-bucket-name/",
          new AlluxioURI("s3a://s3-bucket-name/folder").getRootPath());
      assertEquals("/",
          new AlluxioURI("/tmp/folder").getRootPath());
    }
  }
  