aboutsummaryrefslogtreecommitdiff
path: root/tests/url
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2017-12-10 10:02:19 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2017-12-26 13:25:37 +0300
commite5bfd17637bf297c3cfe509d51027916864092d5 (patch)
tree5dab56d6a5aee0a38da6f597c52b12838b2836b5 /tests/url
parentb1cd193f1bd28837a00cbe6629f9a562f99d961f (diff)
Add basic_url<H,T> class template
Diffstat (limited to 'tests/url')
-rw-r--r--tests/url/buildfile8
-rw-r--r--tests/url/driver.cxx346
-rw-r--r--tests/url/testscript378
3 files changed, 732 insertions, 0 deletions
diff --git a/tests/url/buildfile b/tests/url/buildfile
new file mode 100644
index 0000000..ed8380c
--- /dev/null
+++ b/tests/url/buildfile
@@ -0,0 +1,8 @@
+# file : tests/url/buildfile
+# copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
+# license : MIT; see accompanying LICENSE file
+
+import libs = libbutl%lib{butl}
+libs += $stdmod_lib
+
+exe{driver}: {hxx cxx}{*} $libs test{testscript}
diff --git a/tests/url/driver.cxx b/tests/url/driver.cxx
new file mode 100644
index 0000000..95fe9cb
--- /dev/null
+++ b/tests/url/driver.cxx
@@ -0,0 +1,346 @@
+// file : tests/url/driver.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#include <cassert>
+
+#ifndef __cpp_lib_modules
+#include <string>
+#include <utility> // move()
+#include <iostream>
+#include <iterator> // back_inserter
+#include <stdexcept> // invalid_argument
+#endif
+
+// Other includes.
+
+#ifdef __cpp_modules
+#ifdef __cpp_lib_modules
+import std.core;
+import std.io;
+#endif
+import butl.url;
+import butl.utility; // operator<<(ostream, exception)
+#else
+#include <libbutl/url.mxx>
+#include <libbutl/utility.mxx>
+#endif
+
+using namespace std;
+using namespace butl;
+
+enum class scheme
+{
+ http,
+ https,
+ file
+};
+
+namespace butl
+{
+ template <>
+ struct url_traits<scheme>
+ {
+ using string_type = wstring;
+ using path_type = wstring;
+
+ using scheme_type = scheme;
+ using authority_type = basic_url_authority<string_type>;
+
+ static scheme_type
+ translate_scheme (const string_type& url,
+ string_type&& scheme,
+ optional<authority_type>& /*authority*/,
+ optional<path_type>& path,
+ optional<string_type>& /*query*/,
+ optional<string_type>& /*fragment*/)
+ {
+ // Note that we must compare case-insensitive in the real program.
+ //
+ if (scheme == L"http")
+ return scheme_type::http;
+ else if (scheme == L"https")
+ return scheme_type::https;
+ else if (scheme == L"file")
+ return scheme_type::file;
+ else if (scheme.empty ())
+ {
+ // If the URL looks like an absolute filesystem path, then translate it
+ // to the file URL. If it is not, then leave all the components absent
+ // to fail with a proper exception description.
+ //
+ wchar_t c;
+ if ((c = url[0]) == '/' ||
+ (url.size () > 2 && alpha (c) && url[1] == ':' && url[2] == '/'))
+ path = url;
+
+ return scheme_type::file;
+ }
+ else
+ throw invalid_argument ("unknown scheme");
+ }
+
+ // Translate scheme type back to its string representation.
+ //
+ static string_type
+ translate_scheme (string_type&, /*url*/
+ const scheme_type& scheme,
+ const optional<authority_type>& /*authority*/,
+ const optional<path_type>& /*path*/,
+ const optional<string_type>& /*query*/,
+ const optional<string_type>& /*fragment*/)
+ {
+ switch (scheme)
+ {
+ case scheme_type::http: return L"http";
+ case scheme_type::https: return L"https";
+ case scheme_type::file: return L"file";
+ }
+
+ assert (false); // Can't be here.
+ return L"";
+ }
+
+ static path_type
+ translate_path (string_type&& path)
+ {
+ return path_type (move (path));
+ }
+
+ static string_type
+ translate_path (const path_type& path) {return string_type (path);}
+ };
+}
+
+// Usages:
+//
+// argv[0]
+// argv[0] [-c|-s|-w] <url>
+//
+// Perform some basic tests if no URL is provided. Otherwise round-trip the URL
+// to STDOUT. URL must contain only ASCII characters. Exit with zero code on
+// success. Exit with code one on parsing failure, printing error description
+// to STDERR.
+//
+// -c
+// Print the URL components one per line. Print the special '[null]' string
+// for an absent components. This is the default option if URL is provided.
+//
+// -s
+// Print stringified url object representation.
+//
+// -w
+// Same as above, but use the custom wstring-based url_traits
+// implementation for the basic_url template.
+//
+int
+main (int argc, const char* argv[])
+try
+{
+ using wurl = basic_url<scheme>;
+ using wurl_authority = wurl::authority_type;
+ using wurl_host = wurl::host_type;
+
+ enum class print_mode
+ {
+ str,
+ wstr,
+ comp
+ } mode (print_mode::comp);
+
+ int i (1);
+ for (; i != argc; ++i)
+ {
+ string o (argv[i]);
+ if (o == "-s")
+ mode = print_mode::str;
+ else if (o == "-w")
+ mode = print_mode::wstr;
+ else if (o == "-c")
+ mode = print_mode::comp;
+ else
+ break; // End of options.
+ }
+
+ if (i == argc)
+ {
+ // Test ctors and operators.
+ //
+ {
+ wurl u0 ((wstring ()));
+ assert (u0.empty ());
+ assert (u0 == wurl ());
+
+ wurl u1 (scheme::http,
+ wurl_authority {wstring (), wurl_host (L"[123]"), 0},
+ wstring (L"login"),
+ wstring (L"q="),
+ wstring (L"f"));
+
+ assert (!u1.empty ());
+ assert (u1 != u0);
+
+ wurl u2 (scheme::http,
+ wurl_host (L"123", url_host_kind::ipv6),
+ wstring (L"login"),
+ wstring (L"q="),
+ wstring (L"f"));
+
+ assert (u2 == u1);
+
+ wurl u3 (scheme::http,
+ wurl_host (L"123", url_host_kind::ipv6),
+ 0,
+ wstring (L"login"),
+ wstring (L"q="),
+ wstring (L"f"));
+
+ assert (u3 == u2);
+
+ wurl u4 (scheme::http,
+ L"[123]",
+ wstring (L"login"),
+ wstring (L"q="),
+ wstring (L"f"));
+
+ assert (u4 == u3);
+
+ wurl u5 (scheme::http,
+ L"[123]",
+ 0,
+ wstring (L"login"),
+ wstring (L"q="),
+ wstring (L"f"));
+
+ assert (u5 == u4);
+ }
+
+ // Test encode and decode.
+ //
+ {
+ const char* s ("ABC +");
+ string es (url::encode (s));
+
+ assert (es == "ABC%20%2B");
+ string ds (url::decode (es));
+
+ assert (ds == s);
+ }
+
+ {
+ const char* s ("ABC +");
+
+ string es (url::encode (s,
+ [] (char& c) -> bool
+ {
+ if (c == ' ')
+ {
+ c = '+';
+ return false;
+ }
+ return !url::unreserved (c);
+ }));
+
+ assert (es == "ABC+%2B");
+
+ string ds (url::decode (es.c_str (),
+ [] (char& c)
+ {
+ if (c == '+')
+ c = ' ';
+ }));
+ assert (ds == s);
+ }
+ {
+ const wchar_t s[] = L"ABC ";
+
+ wstring es;
+ wurl::encode (s, s + 4,
+ back_inserter (es),
+ [] (wchar_t& c) -> bool
+ {
+ if (!alnum (c))
+ return true;
+
+ ++c;
+ return false;
+ });
+ assert (es == L"BCD%20");
+
+ wstring ds (wurl::decode (es,
+ [] (wchar_t& c)
+ {
+ if (alnum (c))
+ --c;
+ }));
+ assert (ds == s);
+ }
+ }
+ else // Round-trip the URL.
+ {
+ assert (i + 1 == argc);
+
+ const char* ua (argv[i]);
+
+ switch (mode)
+ {
+ case print_mode::str:
+ {
+ cout << url (ua) << endl;
+ break;
+ }
+ case print_mode::wstr:
+ {
+ // Convert ASCII string to wstring.
+ //
+ wstring s (ua, ua + strlen (ua));
+
+ wcout << wurl (s) << endl;
+ break;
+ }
+ case print_mode::comp:
+ {
+ // Convert ASCII string to wstring.
+ //
+ wstring s (ua, ua + strlen (ua));
+ wurl u (s);
+
+ if (!u.empty ())
+ {
+ wstring s;
+ wcout << wurl::traits::translate_scheme (s,
+ u.scheme,
+ nullopt,
+ nullopt,
+ nullopt,
+ nullopt) << endl;
+ }
+ else
+ wcout << L"[null]" << endl;
+
+ if (u.authority)
+ {
+ const wchar_t* kinds[] = {L"ipv4", L"ipv6", L"name"};
+ const wurl_authority& a (*u.authority);
+
+ wcout << a.user << L'@' << a.host.value << L':' << a.port
+ << " " << kinds[static_cast<size_t> (a.host.kind)] << endl;
+ }
+ else
+ wcout << L"[null]" << endl;
+
+ wcout << (u.path ? *u.path : L"[null]") << endl
+ << (u.query ? *u.query : L"[null]") << endl
+ << (u.fragment ? *u.fragment : L"[null]") << endl;
+ break;
+ }
+ }
+ }
+
+ return 0;
+}
+catch (const invalid_argument& e)
+{
+ cerr << e << endl;
+ return 1;
+}
diff --git a/tests/url/testscript b/tests/url/testscript
new file mode 100644
index 0000000..d81f282
--- /dev/null
+++ b/tests/url/testscript
@@ -0,0 +1,378 @@
+# file : tests/url/testscript
+# copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
+# license : MIT; see accompanying LICENSE file
+
+:basic
+:
+$*
+
+: components
+:
+{
+ : all
+ :
+ $* 'https://user@stage.b2.org:443/libbutl?f=full#description' >>EOO
+ https
+ user@stage.b2.org:443 name
+ libbutl
+ f=full
+ description
+ EOO
+
+ : empty-url
+ :
+ $* '' >>EOO
+ [null]
+ [null]
+ [null]
+ [null]
+ [null]
+ EOO
+
+ : no-id
+ :
+ {
+ $* 'file:#f' 2>'no authority, path or query' != 0 : fragment
+ $* 'file:aaa' 2>'no authority, path or query' != 0 : junk
+ $* 'file:' 2>'no authority, path or query' != 0 : none
+ }
+
+ : scheme
+ :
+ {
+ : detected
+ :
+ $* 'http://build2.org' >>EOO
+ http
+ @build2.org:0 name
+ [null]
+ [null]
+ [null]
+ EOO
+
+ : deduced
+ :
+ $* 'c:/a' >>EOO
+ file
+ [null]
+ c:/a
+ [null]
+ [null]
+ EOO
+
+ $* ':/a' 2>'no scheme' != 0 : none
+ $* 'http' 2>'no scheme' != 0 : unterminated
+ $* 'ht~tp://a.com' 2>'invalid scheme' != 0 : invalid-char
+ $* '1http://a.com' 2>'invalid scheme' != 0 : invalid-first-char
+ }
+
+ : authority
+ {
+ : absent
+ :
+ $* 'file:/tmp/a' >>EOO
+ file
+ [null]
+ tmp/a
+ [null]
+ [null]
+ EOO
+
+ : empty
+ :
+ $* 'file:///tmp/a' >>EOO
+ file
+ @:0 name
+ tmp/a
+ [null]
+ [null]
+ EOO
+
+ : trailing
+ :
+ $* 'http://localhost' >>EOO
+ http
+ @localhost:0 name
+ [null]
+ [null]
+ [null]
+ EOO
+
+ : user
+ :
+ {
+ : non-empty
+ :
+ $* 'http://admin@localhost' >>EOO
+ http
+ admin@localhost:0 name
+ [null]
+ [null]
+ [null]
+ EOO
+
+ : empty
+ :
+ $* 'http://@localhost' >>EOO
+ http
+ @localhost:0 name
+ [null]
+ [null]
+ [null]
+ EOO
+ }
+
+ : host
+ :
+ {
+ : ipv6
+ :
+ {
+ : port
+ :
+ $* 'http://[1:23]:443' >>EOO
+ http
+ @1:23:443 ipv6
+ [null]
+ [null]
+ [null]
+ EOO
+
+ : no-port
+ :
+ $* 'http://[1:23]' >>EOO
+ http
+ @1:23:0 ipv6
+ [null]
+ [null]
+ [null]
+ EOO
+
+ $* 'http://[123' 2>'invalid IPv6 address' != 0 : missed-bracket
+ $* 'http://[123] :80' 2>'invalid IPv6 address' != 0 : extra-char
+ }
+
+ : ipv4
+ :
+ {
+ : valid
+ :
+ $* 'http://0.10.200.255' >>EOO
+ http
+ @0.10.200.255:0 ipv4
+ [null]
+ [null]
+ [null]
+ EOO
+
+ : long
+ :
+ $* 'http://0.10.200.255.30' >>EOO
+ http
+ @0.10.200.255.30:0 name
+ [null]
+ [null]
+ [null]
+ EOO
+
+ : short
+ :
+ $* 'http://0.10.200' >>EOO
+ http
+ @0.10.200:0 name
+ [null]
+ [null]
+ [null]
+ EOO
+
+ : missed
+ :
+ $* 'http://0.10..200' >>EOO
+ http
+ @0.10..200:0 name
+ [null]
+ [null]
+ [null]
+ EOO
+
+ : out-of-range
+ :
+ $* 'http://0.10.200.256' >>EOO
+ http
+ @0.10.200.256:0 name
+ [null]
+ [null]
+ [null]
+ EOO
+ }
+
+ : name
+ :
+ {
+ : valid
+ :
+ $* 'https://www.b2.org' >>EOO
+ https
+ @www.b2.org:0 name
+ [null]
+ [null]
+ [null]
+ EOO
+
+ : encoded
+ :
+ {
+ : valid
+ :
+ $* 'https://www.%62%32.org' >>EOO
+ https
+ @www.b2.org:0 name
+ [null]
+ [null]
+ [null]
+ EOO
+
+ $* 'https://www.%62%3.org' 2>'invalid URL-encoding' != 0 : short
+ $* 'https://www.%62%3x.org' 2>'invalid URL-encoding' != 0 : invalid
+ $* 'https://www.%62%.org' 2>'invalid URL-encoding' != 0 : absent
+ }
+
+ $* 'https://www.b|2.org' 2>'invalid host name' != 0 : invalid-char
+ }
+
+ $* 'http://admin@:80?q=' 2>'no host' != 0: no-host
+ }
+
+ : port
+ :
+ {
+ : valid
+ :
+ $* 'http://build2.org:443' >>EOO
+ http
+ @build2.org:443 name
+ [null]
+ [null]
+ [null]
+ EOO
+
+ $* 'http://build2.org:-433' 2>'invalid port' != 0 : invalid-char
+ $* 'http://build2.org:70000' 2>'invalid port' != 0 : exceeds-max
+ $* 'http://build2.org:0' 2>'invalid port' != 0 : zero
+ }
+ }
+
+ : path
+ :
+ {
+ : absent
+ :
+ $* 'http://b2.org' >>EOO
+ http
+ @b2.org:0 name
+ [null]
+ [null]
+ [null]
+ EOO
+
+ : empty
+ :
+ $* 'http://b2.org/' >>EOO
+ http
+ @b2.org:0 name
+
+ [null]
+ [null]
+ EOO
+
+ : non-empty
+ :
+ $* 'http://b2.org/s/q' >>EOO
+ http
+ @b2.org:0 name
+ s/q
+ [null]
+ [null]
+ EOO
+
+ : encoded
+ :
+ $* 'http://b2.org/%6F/s' >>EOO
+ http
+ @b2.org:0 name
+ o/s
+ [null]
+ [null]
+ EOO
+ }
+
+ : query
+ :
+ {
+ : no-fragment
+ :
+ $* 'http://b2.org/a?x=foo&y=bar' >>EOO
+ http
+ @b2.org:0 name
+ a
+ x=foo&y=bar
+ [null]
+ EOO
+
+ : fragment
+ :
+ $* 'http://b2.org/a?foo#bar' >>EOO
+ http
+ @b2.org:0 name
+ a
+ foo
+ bar
+ EOO
+ }
+
+ : fragment
+ :
+ {
+ $* 'http://b2.org#foo' >>EOO
+ http
+ @b2.org:0 name
+ [null]
+ [null]
+ foo
+ EOO
+ }
+}
+
+: string
+{
+ test.options += -s
+
+ : authority
+ :
+ {
+ : host
+ :
+ {
+ $* 'file:///a' >'file:///a' : empty
+ $* 'http://1.1.1.1' >'http://1.1.1.1' : ipv4
+ $* 'https://[1:2:3]' >'https://[1:2:3]' : ipv6
+ $* 'file://a%d1%84' >'file://a%D1%84' : name
+ }
+
+ $* 'http://admin@localhost' >'http://admin@localhost' : user
+ $* 'http://localhost:8080' >'http://localhost:8080' : port
+ $* 'file:/a' >'file:/a' : absent
+ }
+
+ $* '' >'' : empty
+ $* 'file:/b%7C2' >'file:/b%7C2' : path
+ $* 'http://a?q=' >'http://a?q=' : query
+ $* 'http://a#f' >'http://a#f' : fragment
+}
+
+: wstring
+:
+{
+ u = 'https://user@stage.b2.org:443/libbutl?f=full#description'
+ $* -w "$u" >"$u"
+}