text: #include "catch.hpp"
  
  #include "util/url.h"
  
  using namespace Tangram;
  
  TEST_CASE("Parse components of a correctly formatted URL", "[Url]") {
  
      // Tests conformance to https://tools.ietf.org/html/rfc1808#section-2.1
  
      Url url("https://some.domain:9000/path/to/file.html;param=val?api_key=mapsRcool#yolo");
  
      CHECK(!url.isEmpty());
      CHECK(url.isAbsolute());
      CHECK(!url.hasDataScheme());
      CHECK(!url.hasBase64Data());
      CHECK(!url.hasFileScheme());
      CHECK(url.hasHttpScheme());
      CHECK(url.hasScheme());
      CHECK(url.scheme() == "https");
      CHECK(url.hasNetLocation());
      CHECK(url.netLocation() == "some.domain:9000");
      CHECK(url.hasPath());
      CHECK(url.path() == "/path/to/file.html");
      CHECK(url.hasParameters());
      CHECK(url.parameters() == "param=val");
      CHECK(url.hasQuery());
      CHECK(url.query() == "api_key=mapsRcool");
      CHECK(url.hasFragment());
      CHECK(url.fragment() == "yolo");
      CHECK(!url.hasMediaType());
      CHECK(!url.hasData());
  
  }
  
  TEST_CASE("Parse components of a correctly formatted data URI", "[Url]") {
  
      // Tests conformance to https://tools.ietf.org/html/rfc2397#section-3
  
      Url url("data:text/html;charset=utf-8;base64,YmFzZTY0");
  
      CHECK(!url.isEmpty());
      CHECK(url.isAbsolute());
      CHECK(url.hasDataScheme());
      CHECK(url.hasBase64Data());
      CHECK(!url.hasFileScheme());
      CHECK(!url.hasHttpScheme());
      CHECK(url.hasScheme());
      CHECK(url.scheme() == "data");
      CHECK(!url.hasNetLocation());
      CHECK(!url.hasPath());
      CHECK(!url.hasParameters());
      CHECK(!url.hasQuery());
      CHECK(!url.hasFragment());
      CHECK(url.hasMediaType());
      CHECK(url.mediaType() == "text/html;charset=utf-8");
      CHECK(url.hasData());
      CHECK(url.data() == "YmFzZTY0");
  
  }
  
  TEST_CASE("Parse an empty URL", "[Url]") {
  
      Url url("");
  
      CHECK(url.isEmpty());
      CHECK(!url.isAbsolute());
      CHECK(!url.hasDataScheme());
      CHECK(!url.hasBase64Data());
      CHECK(!url.hasScheme());
      CHECK(!url.hasNetLocation());
      CHECK(!url.hasPath());
      CHECK(!url.hasParameters());
      CHECK(!url.hasQuery());
      CHECK(!url.hasFragment());
      CHECK(!url.hasMediaType());
      CHECK(!url.hasData());
  
  }
  
  TEST_CASE("Remove dot segments from a path", "[Url]") {
  
      // Tests conformance to https://tools.ietf.org/html/rfc3986#section-5.2.4
  
      CHECK(Url::removeDotSegmentsFromString("") == "");
      CHECK(Url::removeDotSegmentsFromString("a/b/c") == "a/b/c");
      CHECK(Url::removeDotSegmentsFromString("a/b=?.;5/c") == "a/b=?.;5/c");
      CHECK(Url::removeDotSegmentsFromString("/a/b/c/./../../g") == "/a/g");
      CHECK(Url::removeDotSegmentsFromString("../a/b") == "a/b");
      CHECK(Url::removeDotSegmentsFromString("./") == "");
      CHECK(Url::removeDotSegmentsFromString("..") == "");
      CHECK(Url::removeDotSegmentsFromString("a/b/../../..") == "");
      CHECK(Url::removeDotSegmentsFromString("a/b/../c/../d/./e/..") == "a/d");
      CHECK(Url::removeDotSegmentsFromString("a//b//c") == "a//b//c");
      CHECK(Url::removeDotSegmentsFromString("a./b../..c/.d") == "a./b../..c/.d");
  
  }
  
  TEST_CASE("Produce a 'standardized' URL", "[Url]") {
  
      CHECK(Url("http://example.com/path/oops/not/here/../../../file.txt;p?q#f").standardized().string() == "http://example.com/path/file.txt;p?q#f");
      CHECK(Url("http://example.com/../../no/going/back/file.txt;p?q#f").standardized().string() == "http://example.com/no/going/back/file.txt;p?q#f");
      CHECK(Url("data:text/html;charset=utf-8,LoremIpsum").standardized().string() == "data:text/html;charset=utf-8,LoremIpsum");
  
  }
  
  TEST_CASE("Maintain URL components when 'standardized'", "[URL]") {
  
      Url url(Url("http://mapzen.com/nothing/to/see/here/../../../../index.html;p?q#f").standardized());
  
      CHECK(!url.isEmpty());
      CHECK(url.isAbsolute());
      CHECK(!url.hasDataScheme());
      CHECK(!url.hasBase64Data());
      CHECK(!url.hasFileScheme());
      CHECK(url.hasHttpScheme());
      CHECK(url.hasScheme());
      CHECK(url.scheme() == "http");
      CHECK(url.hasNetLocation());
      CHECK(url.netLocation() == "mapzen.com");
      CHECK(url.hasPath());
      CHECK(url.path() == "/index.html");
      CHECK(url.hasParameters());
      CHECK(url.parameters() == "p");
      CHECK(url.hasQuery());
      CHECK(url.query() == "q");
      CHECK(url.hasFragment());
      CHECK(url.fragment() == "f");
      CHECK(!url.hasMediaType());
      CHECK(!url.hasData());
  
  }
  
  TEST_CASE("Resolve a URL against an absolute base URL", "[Url]") {
  
      // https://tools.ietf.org/html/rfc3986#section-5.4.1
  
      Url base("http://a/b/c/d;p?q");
  
      CHECK(base.resolve(Url("g:h")).string() == "g:h");
      CHECK(base.resolve(Url("g")).string() == "http://a/b/c/g");
      CHECK(base.resolve(Url("./g")).string() == "http://a/b/c/g");
      CHECK(base.resolve(Url("g/")).string() == "http://a/b/c/g/");
      CHECK(base.resolve(Url("/g")).string() == "http://a/g");
      CHECK(base.resolve(Url("?y")).string() == "http://a/b/c/d;p?y");
      CHECK(base.resolve(Url("g?y")).string() == "http://a/b/c/g?y");
      CHECK(base.resolve(Url("#s")).string() == "http://a/b/c/d;p?q#s");
      CHECK(base.resolve(Url("g#s")).string() == "http://a/b/c/g#s");
      CHECK(base.resolve(Url("g?y#s")).string() == "http://a/b/c/g?y#s");
      CHECK(base.resolve(Url(";x")).string() == "http://a/b/c/d;x"); // See [1] below.
      CHECK(base.resolve(Url("g;x")).string() == "http://a/b/c/g;x");
      CHECK(base.resolve(Url("g;x?y#s")).string() == "http://a/b/c/g;x?y#s");
      CHECK(base.resolve(Url("")).string() == "http://a/b/c/d;p?q");
  
  }
  
  TEST_CASE("Resolve a URL against a relative base URL", "[Url]") {
  
      Url base("a/b/c/d;p?q");
  
      CHECK(base.resolve(Url("g:h")).string() == "g:h");
      CHECK(base.resolve(Url("g")).string() == "a/b/c/g");
      CHECK(base.resolve(Url("./g")).string() == "a/b/c/g");
      CHECK(base.resolve(Url("g/")).string() == "a/b/c/g/");
      CHECK(base.resolve(Url("/g")).string() == "/g");
      CHECK(base.resolve(Url("?y")).string() == "a/b/c/d;p?y");
      CHECK(base.resolve(Url("g?y")).string() == "a/b/c/g?y");
      CHECK(base.resolve(Url("#s")).string() == "a/b/c/d;p?q#s");
      CHECK(base.resolve(Url("g#s")).string() == "a/b/c/g#s");
      CHECK(base.resolve(Url("g?y#s")).string() == "a/b/c/g?y#s");
      CHECK(base.resolve(Url(";x")).string() == "a/b/c/d;x"); // See [1] below.
      CHECK(base.resolve(Url("g;x")).string() == "a/b/c/g;x");
      CHECK(base.resolve(Url("g;x?y#s")).string() == "a/b/c/g;x?y#s");
      CHECK(base.resolve(Url("")).string() == "a/b/c/d;p?q");
  
  }
  
  TEST_CASE("Resolve a relative URL against an empty base URL", "[Url]") {
  
      Url base("");
  
      CHECK(base.resolve(Url("g:h")).string() == "g:h");
      CHECK(base.resolve(Url("g")).string() == "g");
      CHECK(base.resolve(Url("./g")).string() == "g");
      CHECK(base.resolve(Url("g/")).string() == "g/");
      CHECK(base.resolve(Url("/g")).string() == "/g");
      CHECK(base.resolve(Url("?y")).string() == "?y");
      CHECK(base.resolve(Url("g?y")).string() == "g?y");
      CHECK(base.resolve(Url("#s")).string() == "#s");
      CHECK(base.resolve(Url("g#s")).string() == "g#s");
      CHECK(base.resolve(Url("g?y#s")).string() == "g?y#s");
      CHECK(base.resolve(Url(";x")).string() == ";x");
      CHECK(base.resolve(Url("g;x")).string() == "g;x");
      CHECK(base.resolve(Url("g;x?y#s")).string() == "g;x?y#s");
      CHECK(base.resolve(Url("")).string() == "");
  
  }
  
  TEST_CASE("Resolve an abnormal relative URL against an absolute base URL", "[Url]") {
  
      // https://tools.ietf.org/html/rfc3986#section-5.4.2
  
      Url base("http://a/b/c/d;p?q");
  
      CHECK(base.resolve(Url("../../../g")).string() == "http://a/g");
      CHECK(base.resolve(Url("../../../../g")).string() == "http://a/g");
      CHECK(base.resolve(Url("/./g")).string() == "http://a/g");
      CHECK(base.resolve(Url("/../g")).string() == "http://a/g");
      CHECK(base.resolve(Url("g.")).string() == "http://a/b/c/g.");
      CHECK(base.resolve(Url(".g")).string() == "http://a/b/c/.g");
      CHECK(base.resolve(Url("g..")).string() == "http://a/b/c/g..");
      CHECK(base.resolve(Url("..g")).string() == "http://a/b/c/..g");
      CHECK(base.resolve(Url("./../g")).string() == "http://a/b/g");
      CHECK(base.resolve(Url("./g/.")).string() == "http://a/b/c/g/");
      CHECK(base.resolve(Url("g/./h")).string() == "http://a/b/c/g/h");
      CHECK(base.resolve(Url("g/../h")).string() == "http://a/b/c/h");
      CHECK(base.resolve(Url("g;x=1/./y")).string() == "http://a/b/c/g;x=1/./y"); // See [1] below.
      CHECK(base.resolve(Url("g;x=1/../y")).string() == "http://a/b/c/g;x=1/../y"); // See [1] below.
  
  }
  
  TEST_CASE("Retrieve the file extension from a path string", "[Url]") {
  
      CHECK(Url::getPathExtension("file.txt") == "txt");
      CHECK(Url::getPathExtension("file.txt.gz") == "gz");
      CHECK(Url::getPathExtension("/path/to/a/file.txt") == "txt");
      CHECK(Url::getPathExtension("/path/to/some/other/.././file.txt") == "txt");
      CHECK(Url::getPathExtension("/path/to/some/other/.././folder") == "");
      CHECK(Url::getPathExtension("/path/to/a/file") == "");
      CHECK(Url::getPathExtension("/path/to/a/.txt") == "");
      CHECK(Url::getPathExtension("/path/to/a/file.") == "");
  
  }
  
  TEST_CASE("Escape reserved characters in strings with %-encoding") {
  
      CHECK(Url::escapeReservedCharacters("non-reserved") == "non-reserved");
      CHECK(Url::escapeReservedCharacters("some reserved") == "some%20reserved");
      CHECK(Url::escapeReservedCharacters("🍩") == "%F0%9F%8D%A9");
      CHECK(Url::escapeReservedCharacters("도넛") == "%EB%8F%84%EB%84%9B");
  
      CHECK(Url::unEscapeReservedCharacters("non-reserved") == "non-reserved");
      CHECK(Url::unEscapeReservedCharacters("some%20reserved") == "some reserved");
      CHECK(Url::unEscapeReservedCharacters("%F0%9F%8D%A9") == "🍩");
      CHECK(Url::unEscapeReservedCharacters("%EB%8F%84%EB%84%9B") == "도넛");
  
  }
  
  // [1]:
  // Some of the examples for path resolution given in RFC 3986 don't produce the same result
  // in our implementation because the interpretation of the parameters string in RFC 3986 is
  // in conflict with RFC 1808, which many URL utilities adhere to (e.g. NSURL). In RFC 1808
  // the 'path' component stops at a ';', but in RFC 3986 it goes up to a '?'.
  