Hello libart-uri
All checks were successful
on-push / build-and-test (push) Successful in 15s

This commit is contained in:
2025-10-18 00:39:16 +02:00
commit e892f3ca97
28 changed files with 1435 additions and 0 deletions

17
.editorconfig Normal file
View File

@@ -0,0 +1,17 @@
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
indent_size = 4
max_line_length = off
trim_trailing_whitespace = false
[*.yaml]
indent_size = 2

1
.gitattributes vendored Normal file
View File

@@ -0,0 +1 @@
* text=auto

View File

@@ -0,0 +1,31 @@
name: on-push
on:
push:
tags-ignore:
- '*'
branches:
- '**'
jobs:
build-and-test:
runs-on: linux
container: code.helloryan.se/art/infra/buildenv/x86_64-fedora_42-unified:latest
volumes:
- /build
steps:
- name: Configure repository access
run: |
git config --global http.$GITHUB_SERVER_URL/.extraheader "Authorization: token ${{ secrets.ACT_RUNNER_TOKEN }}"
- name: Configure build directory
run: |
bpkg create -d /build cc config.cxx=clang++ config.cc.coptions="-Wall -Werror -Wno-unknown-pragmas"
- name: Build package
run: |
cd /build
bpkg build --yes --trust-yes $GITHUB_SERVER_URL/$GITHUB_REPOSITORY.git##$GITHUB_SHA
- name: Test package
run: |
cd /build
b test

1
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1 @@
patreon: helloryan

32
.gitignore vendored Normal file
View File

@@ -0,0 +1,32 @@
.bdep/
Doxyfile
# Local default options files.
#
.build2/local/
# Compiler/linker output.
#
*.d
*.t
*.i
*.i.*
*.ii
*.ii.*
*.o
*.obj
*.gcm
*.pcm
*.ifc
*.so
*.dylib
*.dll
*.a
*.lib
*.exp
*.pdb
*.ilk
*.exe
*.exe.dlls/
*.exe.manifest
*.pc

31
LICENSE Normal file
View File

@@ -0,0 +1,31 @@
Copyright © 2024 Ryan. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. All advertising materials mentioning features or use of this software must
display the following acknowledgement:
This product includes software developed by Ryan, http://helloryan.se/.
4. Neither the name(s) of the copyright holder(s) nor the names of its
contributors may be used to endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY COPYRIGHT HOLDER "AS IS" AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
NO EVENT SHALL COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

10
README.md Normal file
View File

@@ -0,0 +1,10 @@
# libart-uri
![Build badge](https://code.helloryan.se/art/libart-uri/actions/workflows/on-push.yaml/badge.svg)
libart-uri is a URI-parser library for C++.
## Sponsorship
You can sponsor the development of this project via Patreon. Read more
over at https://patreon.com/helloryan.

9
art/uri/.gitignore vendored Normal file
View File

@@ -0,0 +1,9 @@
# Generated version header.
#
version.hxx
# Unit test executables and Testscript output directories
# (can be symlinks).
#
*.test
test-*.test

66
art/uri/buildfile Normal file
View File

@@ -0,0 +1,66 @@
intf_libs = # Interface dependencies.
impl_libs = # Implementation dependencies.
./: lib{art-uri}: libul{art-uri}
libul{art-uri}: {hxx ixx txx cxx}{** -**.test... -version} \
{hxx }{ version}
libul{art-uri}: $impl_libs $intf_libs
# Unit tests.
#
exe{*.test}:
{
test = true
install = false
}
test_libs = # Test dependencies.
import test_libs =+ libart-validation%lib{art-validation}
for t: cxx{**.test...}
{
d = $directory($t)
n = $name($t)...
./: $d/exe{$n}: $t $d/{hxx ixx txx}{+$n} $d/testscript{+$n} $test_libs
$d/exe{$n}: lib{art-uri}: bin.whole = false
$d/exe{$n}: test.arguments = --print-failure
}
hxx{version}: in{version} $src_root/manifest
{
dist = true
clean = ($src_root != $out_root)
}
# Build options.
#
cxx.poptions =+ "-I$out_root" "-I$src_root"
# Export options.
#
lib{art-uri}:
{
cxx.export.poptions = "-I$out_root" "-I$src_root"
cxx.export.libs = $intf_libs
}
# For pre-releases use the complete version to make sure they cannot
# be used in place of another pre-release or the final version. See
# the version module for details on the version.* variable values.
#
if $version.pre_release
lib{art-uri}: bin.lib.version = "-$version.project_id"
else
lib{art-uri}: bin.lib.version = "-$version.major.$version.minor"
# Install into the art/uri/ subdirectory of, say, /usr/include/
# recreating subdirectories.
#
{hxx ixx txx}{*}:
{
install = include/art/uri/
install.subdirs = true
}

133
art/uri/grammar.hxx Normal file
View File

@@ -0,0 +1,133 @@
#ifndef art__uri__grammar_hxx_
#define art__uri__grammar_hxx_
namespace art::uri::grammar
{
inline
bool
is_alpha(char c)
{
return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z');
}
inline
bool
is_digit(char c)
{
return '0' <= c && c <= '9';
}
inline
bool
is_unreserved(char c)
{
if (is_alpha(c) || is_digit(c)) {
return true;
}
switch (c) {
case '-':
case '.':
case '_':
case '~':
return true;
}
return false;
}
inline
bool
is_subdelim(char c)
{
switch (c) {
case '!':
case '$':
case '&':
case '(':
case ')':
case '*':
case '+':
case ',':
case ';':
case '=':
case '\'':
return true;
}
return false;
}
inline
bool
is_scheme_start(char c)
{
return is_alpha(c);
}
inline
bool
is_scheme(char c)
{
return is_alpha(c) || is_digit(c) || c == '+' || c == '-' || c == '.';
}
inline
bool
is_userinfo(char c)
{
return is_unreserved(c) || is_subdelim(c) || c == ':' || c == '%';
}
inline
bool
is_regname(char c)
{
return is_unreserved(c) || is_subdelim(c);
}
inline
bool
is_host(char c)
{
return is_regname(c);
}
inline
bool
is_port(char c)
{
return is_digit(c);
}
inline
bool
is_pchar(char c)
{
return is_unreserved(c) || is_subdelim(c) || c == ':' || c == '@' || c == '%';
}
inline
bool
is_segment_nc(char c)
{
return is_pchar(c) && c != ':';
}
inline
bool
is_query(char c)
{
return is_pchar(c) || c == '/' || c == '?';
}
inline
bool
is_fragment(char c)
{
return is_pchar(c) || c == '/' || c == '?';
}
} // namespace art::uri::grammar
#endif

284
art/uri/uri.cxx Normal file
View File

@@ -0,0 +1,284 @@
#include <art/uri/uri.hxx>
#include <sstream>
#include <vector>
#include <iostream>
namespace art::uri
{
uri_t::
uri_t()
{}
uri_t::
uri_t(std::string scheme, std::string host, std::string path)
: scheme_{std::move(scheme)},
host_{std::move(host)},
path_{std::move(path)}
{}
uri_t::
uri_t(std::string scheme,
std::string host,
std::string port,
std::string path)
: scheme_{std::move(scheme)},
host_{std::move(host)},
port_{std::move(port)},
path_{std::move(path)}
{}
uri_t::
uri_t(std::string scheme,
std::string host,
std::string port,
std::string path,
std::string query)
: scheme_{std::move(scheme)},
host_{std::move(host)},
port_{std::move(port)},
path_{std::move(path)},
query_{std::move(query)}
{}
uri_t::
uri_t(std::string scheme,
std::string host,
std::string port,
std::string path,
std::string query,
std::string fragment)
: scheme_{std::move(scheme)},
host_{std::move(host)},
port_{std::move(port)},
path_{std::move(path)},
query_{std::move(query)},
fragment_{std::move(fragment)}
{}
uri_t::
uri_t(std::string scheme,
std::string userinfo,
std::string host,
std::string port,
std::string path,
std::string query,
std::string fragment)
: scheme_{std::move(scheme)},
userinfo_{std::move(userinfo)},
host_{std::move(host)},
port_{std::move(port)},
path_{std::move(path)},
query_{std::move(query)},
fragment_{std::move(fragment)}
{}
uri_t::
uri_t(std::optional<std::string> scheme,
std::optional<std::string> userinfo,
std::optional<std::string> host,
std::optional<std::string> port,
std::string path,
std::optional<std::string> query,
std::optional<std::string> fragment)
: scheme_{std::move(scheme)},
userinfo_{std::move(userinfo)},
host_{std::move(host)},
port_{std::move(port)},
path_{std::move(path)},
query_{std::move(query)},
fragment_{std::move(fragment)}
{}
std::optional<std::string> const&
uri_t::
scheme() const
{
return scheme_;
}
std::string
uri_t::
scheme_str() const
{
return scheme().value_or(std::string{});
}
std::optional<std::string> const&
uri_t::
userinfo() const
{
return userinfo_;
}
std::string
uri_t::
userinfo_str() const
{
return userinfo().value_or(std::string{});
}
std::optional<std::string> const&
uri_t::
host() const
{
return host_;
}
std::string
uri_t::
host_str() const
{
return host().value_or(std::string{});
}
std::optional<std::string> const&
uri_t::
port() const
{
return port_;
}
std::string
uri_t::
port_str() const
{
return port().value_or(std::string{});
}
std::string
uri_t::
path_str() const
{
return path_;
}
std::optional<std::string> const&
uri_t::
query() const
{
return query_;
}
std::string
uri_t::
query_str() const
{
return query().value_or(std::string{});
}
std::optional<std::string> const&
uri_t::
fragment() const
{
return fragment_;
}
std::string
uri_t::
fragment_str() const
{
return fragment().value_or(std::string{});
}
std::string
to_string(uri_t const& uri)
{
std::ostringstream str;
// Scheme
//
if (auto scheme = uri.scheme(); scheme) {
str <<*scheme <<':';
}
// Authority
//
if (auto host = uri.host(); host) {
str <<"//";
// Userinfo
//
if (auto userinfo = uri.userinfo(); userinfo) {
str <<*userinfo <<'@';
}
// Host
//
str <<*host;
// Port
if (auto port = uri.port(); port) {
str <<':' <<*port;
}
}
// Path
//
str <<uri.path_str();
// Query
//
if (auto query = uri.query(); query) {
str <<'?' <<*query;
}
// Fragment
//
if (auto fragment = uri.fragment(); fragment) {
str <<'#' <<*fragment;
}
return str.str();
}
uri_t
normalize_path(uri_t const& uri)
{
std::stringstream path{uri.path_str()};
std::vector<std::string> segments;
for (std::string segment; std::getline(path, segment, '/');) {
std::cout << "found segment: " << segment << '\n';
if (segment.empty()) {
continue;
}
if (segment == ".") {
continue;
}
if (segment == "..") {
if (!segments.empty()) {
segments.pop_back();
}
continue;
}
segments.push_back(segment);
}
std::string normalized;
for (auto const& j : segments) {
normalized += '/';
normalized += j;
}
return uri_t{
uri.scheme(),
uri.userinfo(),
uri.host(),
uri.port(),
normalized.empty() ? "/" : normalized,
uri.query(),
uri.fragment()
};
}
std::optional<uri_t>
try_parse(std::string const& str)
{
return try_parse(str.begin(), str.end());
}
} // namespace uri

118
art/uri/uri.hxx Normal file
View File

@@ -0,0 +1,118 @@
#ifndef art__uri__uri_hxx_
#define art__uri__uri_hxx_
#include <art/uri/grammar.hxx>
#include <optional>
#include <stdexcept>
#include <string>
namespace art::uri
{
class uri_t
{
public:
uri_t();
uri_t(std::string, std::string, std::string);
uri_t(std::string, std::string, std::string, std::string);
uri_t(std::string,
std::string,
std::string,
std::string,
std::string);
uri_t(std::string,
std::string,
std::string,
std::string,
std::string,
std::string);
uri_t(std::string,
std::string,
std::string,
std::string,
std::string,
std::string,
std::string);
uri_t(std::optional<std::string>,
std::optional<std::string>,
std::optional<std::string>,
std::optional<std::string>,
std::string,
std::optional<std::string>,
std::optional<std::string>);
std::optional<std::string> const&
scheme() const;
std::string
scheme_str() const;
std::optional<std::string> const&
userinfo() const;
std::string
userinfo_str() const;
std::optional<std::string> const&
host() const;
std::string
host_str() const;
std::optional<std::string> const&
port() const;
std::string
port_str() const;
std::string
path_str() const;
std::optional<std::string> const&
query() const;
std::string
query_str() const;
std::optional<std::string> const&
fragment() const;
std::string
fragment_str() const;
private:
std::optional<std::string> scheme_;
std::optional<std::string> userinfo_;
std::optional<std::string> host_;
std::optional<std::string> port_;
std::string path_;
std::optional<std::string> query_;
std::optional<std::string> fragment_;
};
std::string
to_string(uri_t const&);
uri_t
normalize_path(uri_t const&);
template<typename Iterator>
std::optional<uri_t>
try_parse(Iterator first, Iterator last);
std::optional<uri_t>
try_parse(std::string const&);
} // namespace art::uri
#include <art/uri/uri.txx>
#endif

242
art/uri/uri.txx Normal file
View File

@@ -0,0 +1,242 @@
namespace art::uri
{
template<typename Iterator>
std::optional<uri_t>
try_parse(Iterator first, Iterator last)
{
std::optional<std::string> opt_scheme;
std::optional<std::string> opt_userinfo;
std::optional<std::string> opt_host;
std::optional<std::string> opt_port;
std::string path;
std::optional<std::string> opt_query;
std::optional<std::string> opt_fragment;
auto try_parse_scheme = [&](auto init)
{
auto c = init;
std::string scheme;
while (c != last && grammar::is_scheme(*c)) {
scheme += *c++;
}
if (c != last && *c == ':') {
++c; // skips ':'
opt_scheme = std::move(scheme);
return c;
}
return init;
};
auto try_parse_userinfo = [&](auto init)
{
auto c = init;
std::string userinfo;
while (c != last && grammar::is_userinfo(*c)) {
userinfo += *c++;
}
if (c != last && *c == '@') {
++c; // skips '@'
opt_userinfo = std::move(userinfo);
return c;
}
return init;
};
auto try_parse_host = [&](auto init)
{
auto c = init;
opt_host = std::string{};
while (c != last && grammar::is_host(*c)) {
*opt_host += *c++;
}
return c;
};
auto try_parse_port = [&](auto init)
{
auto c = init;
if (c != last && *c == ':') {
++c; // skips ':'
opt_port = std::string{};
while (c != last && grammar::is_digit(*c)) {
*opt_port += *c++;
}
return c;
}
return init;
};
auto try_parse_authority = [&](auto init)
{
auto c = init;
if (c == last || *c != '/') {
return init;
}
++c; // skips first '/'
if (c == last || *c != '/') {
return init;
}
++c; // skips second '/'
c = try_parse_userinfo(c);
c = try_parse_host(c);
c = try_parse_port(c);
return c;
};
auto try_parse_path = [&](auto init)
{
auto c = init;
while (c != last && (grammar::is_pchar(*c) || *c == '/')) {
path += *c++;
}
return c;
};
auto try_parse_query = [&](auto init)
{
auto c = init;
if (c != last && *c == '?') {
++c; // skips '?'
opt_query = std::string{};
while (c != last && grammar::is_query(*c)) {
*opt_query += *c++;
}
return c;
}
return init;
};
auto try_parse_fragment = [&](auto init)
{
auto c = init;
if (c != last && *c == '#') {
++c; // skips '?#'
opt_fragment = std::string{};
while (c != last && grammar::is_fragment(*c)) {
*opt_fragment += *c++;
}
return c;
}
return init;
};
first = try_parse_scheme(first);
first = try_parse_authority(first);
first = try_parse_path(first);
first = try_parse_query(first);
first = try_parse_fragment(first);
if (first != last) {
return std::nullopt;
}
auto percent_decode = [](std::string const& input)
{
auto hex_to_char = [](char c)
{
if (c>= '0' && c <= '9')
return c - '0';
if (c>= 'a' && c <= 'f')
return c - 'a' + 10;
if (c>= 'A' && c <= 'F')
return c - 'A' + 10;
throw std::invalid_argument{"invalid hex character"};
};
auto make_byte = [&](char a, char b)
{
return hex_to_char(a) << 4 | hex_to_char(b);
};
std::string str;
auto j = input.begin();
while (j != input.end()) {
if ('%' == *j) {
++j;
if (j == input.end()) {
break;
}
char one = *j++;
if (j == input.end()) {
break;
}
char two = *j++;
str += make_byte(one, two);
continue;
}
str += *j;
++j;
}
return str;
};
if (opt_userinfo) {
opt_userinfo = percent_decode(*opt_userinfo);
}
path = percent_decode(path);
if (opt_query) {
opt_query = percent_decode(*opt_query);
}
if (opt_fragment) {
opt_fragment = percent_decode(*opt_fragment);
}
return uri_t{opt_scheme,
opt_userinfo,
opt_host,
opt_port,
path,
opt_query,
opt_fragment};
}
} // namespace art::uri

37
art/uri/version.hxx.in Normal file
View File

@@ -0,0 +1,37 @@
#ifndef art__uri__version_hxx_
#define art__uri__version_hxx_
// The numeric version format is AAAAABBBBBCCCCCDDDE where:
//
// AAAAA - major version number
// BBBBB - minor version number
// CCCCC - bugfix version number
// DDD - alpha / beta (DDD + 500) version number
// E - final (0) / snapshot (1)
//
// When DDDE is not 0, 1 is subtracted from AAAAABBBBBCCCCC. For example:
//
// Version AAAAABBBBBCCCCCDDDE
//
// 0.1.0 0000000001000000000
// 0.1.2 0000000001000020000
// 1.2.3 0000100002000030000
// 2.2.0-a.1 0000200001999990010
// 3.0.0-b.2 0000299999999995020
// 2.2.0-a.1.z 0000200001999990011
//
#define LIBART_URI_VERSION $libart_uri.version.project_number$ULL
#define LIBART_URI_VERSION_STR "$libart_uri.version.project$"
#define LIBART_URI_VERSION_ID "$libart_uri.version.project_id$"
#define LIBART_URI_VERSION_FULL "$libart_uri.version$"
#define LIBART_URI_VERSION_MAJOR $libart_uri.version.major$
#define LIBART_URI_VERSION_MINOR $libart_uri.version.minor$
#define LIBART_URI_VERSION_PATCH $libart_uri.version.patch$
#define LIBART_URI_PRE_RELEASE $libart_uri.version.pre_release$
#define LIBART_URI_SNAPSHOT_SN $libart_uri.version.snapshot_sn$ULL
#define LIBART_URI_SNAPSHOT_ID "$libart_uri.version.snapshot_id$"
#endif

4
build/.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
/config.build
/root/
/bootstrap/
build/

7
build/bootstrap.build Normal file
View File

@@ -0,0 +1,7 @@
project = libart-uri
using version
using config
using test
using install
using dist

6
build/export.build Normal file
View File

@@ -0,0 +1,6 @@
$out_root/
{
include art/uri/
}
export $out_root/art/uri/$import.target

16
build/root.build Normal file
View File

@@ -0,0 +1,16 @@
# Uncomment to suppress warnings coming from external libraries.
#
#cxx.internal.scope = current
cxx.std = latest
using cxx
hxx{*}: extension = hxx
ixx{*}: extension = ixx
txx{*}: extension = txx
cxx{*}: extension = cxx
# The test target for cross-testing (running tests under Wine, etc).
#
test.target = $cxx.target

5
buildfile Normal file
View File

@@ -0,0 +1,5 @@
./: {art/ tests/} doc{README.md} legal{LICENSE} manifest
# Don't install tests.
#
tests/: install = false

12
manifest Normal file
View File

@@ -0,0 +1,12 @@
: 1
name: libart-uri
version: 0.1.0-a.0.z
language: c++
summary: art-uri C++ library
license: BSD-4-Clause
description-file: README.md
url: https://art.helloryan.se/
email: art@helloryan.se
depends: * build2 >= 0.17.0
depends: * bpkg >= 0.17.0
depends: libart-validation ^0.1.0-

6
repositories.manifest Normal file
View File

@@ -0,0 +1,6 @@
: 1
summary: libart-uri project repository
:
role: prerequisite
location: https://code.helloryan.se/art/libart-validation.git##HEAD

8
tests/.gitignore vendored Normal file
View File

@@ -0,0 +1,8 @@
# Test executables.
#
driver
# Testscript output directories (can be symlinks).
#
test
test-*

6
tests/basics/buildfile Normal file
View File

@@ -0,0 +1,6 @@
libs =
import libs =+ libart-validation%lib{art-validation}
import libs =+ libart-uri%lib{art-uri}
./: exe{driver}: {hxx ixx txx cxx}{**} $libs

327
tests/basics/driver.cxx Normal file
View File

@@ -0,0 +1,327 @@
#include <art/uri/uri.hxx>
#include <art/validation/main.hxx>
#include <functional>
#include <memory>
#include <iostream>
VALIDATION_TEST(test_01)
{
auto opt_uri = art::uri::try_parse("");
VALIDATION_ASSERT_TRUE((bool)opt_uri);
auto uri = *opt_uri;
VALIDATION_ASSERT_FALSE((bool)uri.scheme());
VALIDATION_ASSERT_FALSE((bool)uri.userinfo());
VALIDATION_ASSERT_FALSE((bool)uri.host());
VALIDATION_ASSERT_FALSE((bool)uri.port());
VALIDATION_ASSERT_FALSE((bool)uri.query());
VALIDATION_ASSERT_FALSE((bool)uri.fragment());
VALIDATION_ASSERT_TRUE(uri.path_str().empty());
}
VALIDATION_TEST(test_02)
{
auto opt_uri = art::uri::try_parse("http:///index.html");
VALIDATION_ASSERT_TRUE((bool)opt_uri);
auto uri = *opt_uri;
VALIDATION_ASSERT_EQUAL((bool)uri.scheme(), true);
VALIDATION_ASSERT_EQUAL((bool)uri.userinfo(), false);
VALIDATION_ASSERT_EQUAL((bool)uri.host(), true);
VALIDATION_ASSERT_EQUAL((bool)uri.port(), false);
VALIDATION_ASSERT_EQUAL((bool)uri.query(), false);
VALIDATION_ASSERT_EQUAL((bool)uri.fragment(), false);
VALIDATION_ASSERT_EQUAL(uri.scheme_str(), "http");
VALIDATION_ASSERT_EQUAL(uri.host_str(), "");
VALIDATION_ASSERT_EQUAL(uri.path_str(), "/index.html");
}
VALIDATION_TEST(test_03)
{
auto opt_uri = art::uri::try_parse("http://host.domain./index.html");
VALIDATION_ASSERT_TRUE((bool)opt_uri);
auto uri = *opt_uri;
VALIDATION_ASSERT_TRUE((bool)uri.scheme());
VALIDATION_ASSERT_FALSE((bool)uri.userinfo());
VALIDATION_ASSERT_TRUE((bool)uri.host());
VALIDATION_ASSERT_FALSE((bool)uri.port());
VALIDATION_ASSERT_FALSE((bool)uri.query());
VALIDATION_ASSERT_FALSE((bool)uri.fragment());
VALIDATION_ASSERT_EQUAL(uri.scheme_str(), "http");
VALIDATION_ASSERT_EQUAL(uri.host_str(), "host.domain.");
VALIDATION_ASSERT_EQUAL(uri.path_str(), "/index.html");
}
VALIDATION_TEST(test_04)
{
auto opt_uri = art::uri::try_parse("https://host.domain.:8443/index.html");
VALIDATION_ASSERT_TRUE((bool)opt_uri);
auto uri = *opt_uri;
VALIDATION_ASSERT_TRUE((bool)uri.scheme());
VALIDATION_ASSERT_FALSE((bool)uri.userinfo());
VALIDATION_ASSERT_TRUE((bool)uri.host());
VALIDATION_ASSERT_TRUE((bool)uri.port());
VALIDATION_ASSERT_FALSE((bool)uri.query());
VALIDATION_ASSERT_FALSE((bool)uri.fragment());
VALIDATION_ASSERT_EQUAL(uri.scheme_str(), "https");
VALIDATION_ASSERT_EQUAL(uri.host_str(), "host.domain.");
VALIDATION_ASSERT_EQUAL(uri.port_str(), "8443");
VALIDATION_ASSERT_EQUAL(uri.path_str(), "/index.html");
}
VALIDATION_TEST(test_05)
{
auto opt_uri = art::uri::try_parse("https://host.domain.:8443/secodeh?q=hamsters");
VALIDATION_ASSERT_TRUE((bool)opt_uri);
auto uri = *opt_uri;
VALIDATION_ASSERT_TRUE((bool)uri.scheme());
VALIDATION_ASSERT_FALSE((bool)uri.userinfo());
VALIDATION_ASSERT_TRUE((bool)uri.host());
VALIDATION_ASSERT_TRUE((bool)uri.port());
VALIDATION_ASSERT_TRUE((bool)uri.query());
VALIDATION_ASSERT_FALSE((bool)uri.fragment());
VALIDATION_ASSERT_EQUAL(uri.scheme_str(), "https");
VALIDATION_ASSERT_EQUAL(uri.host_str(), "host.domain.");
VALIDATION_ASSERT_EQUAL(uri.port_str(), "8443");
VALIDATION_ASSERT_EQUAL(uri.path_str(), "/secodeh");
VALIDATION_ASSERT_EQUAL(uri.query_str(), "q=hamsters");
}
VALIDATION_TEST(test_06)
{
auto opt_uri = art::uri::try_parse("https://host.domain.:8443/secodeh?q=hamsters#results");
VALIDATION_ASSERT_TRUE((bool)opt_uri);
auto uri = *opt_uri;
VALIDATION_ASSERT_TRUE((bool)uri.scheme());
VALIDATION_ASSERT_FALSE((bool)uri.userinfo());
VALIDATION_ASSERT_TRUE((bool)uri.host());
VALIDATION_ASSERT_TRUE((bool)uri.port());
VALIDATION_ASSERT_TRUE((bool)uri.query());
VALIDATION_ASSERT_TRUE((bool)uri.fragment());
VALIDATION_ASSERT_EQUAL(uri.scheme_str(), "https");
VALIDATION_ASSERT_EQUAL(uri.host_str(), "host.domain.");
VALIDATION_ASSERT_EQUAL(uri.port_str(), "8443");
VALIDATION_ASSERT_EQUAL(uri.path_str(), "/secodeh");
VALIDATION_ASSERT_EQUAL(uri.query_str(), "q=hamsters");
VALIDATION_ASSERT_EQUAL(uri.fragment_str(), "results");
}
VALIDATION_TEST(test_07)
{
auto opt_uri = art::uri::try_parse(
"https://admin:qwerty@host.domain.:8443/secodeh?q=hamsters#results"
);
VALIDATION_ASSERT_TRUE((bool)opt_uri);
auto uri = *opt_uri;
VALIDATION_ASSERT_TRUE((bool)uri.scheme());
VALIDATION_ASSERT_TRUE((bool)uri.userinfo());
VALIDATION_ASSERT_TRUE((bool)uri.host());
VALIDATION_ASSERT_TRUE((bool)uri.port());
VALIDATION_ASSERT_TRUE((bool)uri.query());
VALIDATION_ASSERT_TRUE((bool)uri.fragment());
VALIDATION_ASSERT_EQUAL(uri.scheme_str(), "https");
VALIDATION_ASSERT_EQUAL(uri.userinfo_str(), "admin:qwerty");
VALIDATION_ASSERT_EQUAL(uri.host_str(), "host.domain.");
VALIDATION_ASSERT_EQUAL(uri.port_str(), "8443");
VALIDATION_ASSERT_EQUAL(uri.path_str(), "/secodeh");
VALIDATION_ASSERT_EQUAL(uri.query_str(), "q=hamsters");
VALIDATION_ASSERT_EQUAL(uri.fragment_str(), "results");
}
VALIDATION_TEST(test_08)
{
auto opt_uri = art::uri::try_parse("//host.domain./index.html");
VALIDATION_ASSERT_TRUE((bool)opt_uri);
auto uri = *opt_uri;
VALIDATION_ASSERT_FALSE((bool)uri.scheme());
VALIDATION_ASSERT_FALSE((bool)uri.userinfo());
VALIDATION_ASSERT_TRUE((bool)uri.host());
VALIDATION_ASSERT_FALSE((bool)uri.port());
VALIDATION_ASSERT_FALSE((bool)uri.query());
VALIDATION_ASSERT_FALSE((bool)uri.fragment());
VALIDATION_ASSERT_EQUAL(uri.host_str(), "host.domain.");
VALIDATION_ASSERT_EQUAL(uri.path_str(), "/index.html");
}
VALIDATION_TEST(test_09)
{
auto opt_uri = art::uri::try_parse("/index.html");
VALIDATION_ASSERT_TRUE((bool)opt_uri);
auto uri = *opt_uri;
VALIDATION_ASSERT_EQUAL((bool)uri.scheme(), false);
VALIDATION_ASSERT_FALSE((bool)uri.userinfo());
VALIDATION_ASSERT_FALSE((bool)uri.host());
VALIDATION_ASSERT_FALSE((bool)uri.port());
VALIDATION_ASSERT_FALSE((bool)uri.query());
VALIDATION_ASSERT_FALSE((bool)uri.fragment());
VALIDATION_ASSERT_EQUAL(uri.path_str(), "/index.html");
}
VALIDATION_TEST(test_10)
{
auto opt_uri = art::uri::try_parse("index.html");
VALIDATION_ASSERT_TRUE((bool)opt_uri);
auto uri = *opt_uri;
VALIDATION_ASSERT_EQUAL((bool)uri.scheme(), false);
VALIDATION_ASSERT_FALSE((bool)uri.userinfo());
VALIDATION_ASSERT_FALSE((bool)uri.host());
VALIDATION_ASSERT_FALSE((bool)uri.port());
VALIDATION_ASSERT_FALSE((bool)uri.query());
VALIDATION_ASSERT_FALSE((bool)uri.fragment());
VALIDATION_ASSERT_EQUAL(uri.path_str(), "index.html");
}
VALIDATION_TEST(test_11)
{
auto opt_uri = art::uri::try_parse("/files/index:1.html");
VALIDATION_ASSERT_TRUE((bool)opt_uri);
auto uri = *opt_uri;
VALIDATION_ASSERT_EQUAL((bool)uri.scheme(), false);
VALIDATION_ASSERT_EQUAL((bool)uri.userinfo(), false);
VALIDATION_ASSERT_EQUAL((bool)uri.host(), false);
VALIDATION_ASSERT_EQUAL((bool)uri.port(), false);
VALIDATION_ASSERT_EQUAL((bool)uri.query(), false);
VALIDATION_ASSERT_EQUAL((bool)uri.fragment(), false);
VALIDATION_ASSERT_EQUAL(uri.path_str(), "/files/index:1.html");
}
VALIDATION_TEST(test_12)
{
auto opt_uri = art::uri::try_parse("files/index:1.html");
VALIDATION_ASSERT_TRUE((bool)opt_uri);
auto uri = *opt_uri;
VALIDATION_ASSERT_EQUAL((bool)uri.scheme(), false);
VALIDATION_ASSERT_EQUAL((bool)uri.userinfo(), false);
VALIDATION_ASSERT_EQUAL((bool)uri.host(), false);
VALIDATION_ASSERT_EQUAL((bool)uri.port(), false);
VALIDATION_ASSERT_EQUAL((bool)uri.query(), false);
VALIDATION_ASSERT_EQUAL((bool)uri.fragment(), false);
VALIDATION_ASSERT_EQUAL(uri.path_str(), "files/index:1.html");
}
VALIDATION_TEST(test_13)
{
auto opt_uri = art::uri::try_parse("?q=hamsters");
VALIDATION_ASSERT_TRUE((bool)opt_uri);
auto uri = *opt_uri;
VALIDATION_ASSERT_EQUAL((bool)uri.scheme(), false);
VALIDATION_ASSERT_EQUAL((bool)uri.userinfo(), false);
VALIDATION_ASSERT_EQUAL((bool)uri.host(), false);
VALIDATION_ASSERT_EQUAL((bool)uri.port(), false);
VALIDATION_ASSERT_EQUAL((bool)uri.query(), true);
VALIDATION_ASSERT_EQUAL((bool)uri.fragment(), false);
VALIDATION_ASSERT_EQUAL(uri.query_str(), "q=hamsters");
}
VALIDATION_TEST(test_14)
{
auto opt_uri = art::uri::try_parse("?q=hamsters#results");
VALIDATION_ASSERT_TRUE((bool)opt_uri);
auto uri = *opt_uri;
VALIDATION_ASSERT_EQUAL((bool)uri.scheme(), false);
VALIDATION_ASSERT_EQUAL((bool)uri.userinfo(), false);
VALIDATION_ASSERT_EQUAL((bool)uri.host(), false);
VALIDATION_ASSERT_EQUAL((bool)uri.port(), false);
VALIDATION_ASSERT_EQUAL((bool)uri.query(), true);
VALIDATION_ASSERT_EQUAL((bool)uri.fragment(), true);
VALIDATION_ASSERT_EQUAL(uri.query_str(), "q=hamsters");
VALIDATION_ASSERT_EQUAL(uri.fragment_str(), "results");
}
VALIDATION_TEST(test_15)
{
auto opt_uri = art::uri::try_parse("#results");
VALIDATION_ASSERT_TRUE((bool)opt_uri);
auto uri = *opt_uri;
VALIDATION_ASSERT_EQUAL((bool)uri.scheme(), false);
VALIDATION_ASSERT_EQUAL((bool)uri.userinfo(), false);
VALIDATION_ASSERT_EQUAL((bool)uri.host(), false);
VALIDATION_ASSERT_EQUAL((bool)uri.port(), false);
VALIDATION_ASSERT_EQUAL((bool)uri.query(), false);
VALIDATION_ASSERT_EQUAL((bool)uri.fragment(), true);
VALIDATION_ASSERT_EQUAL(uri.fragment_str(), "results");
}
VALIDATION_TEST(test_16)
{
auto opt_uri = art::uri::try_parse("#results?gui-sort=asc");
VALIDATION_ASSERT_TRUE((bool)opt_uri);
auto uri = *opt_uri;
VALIDATION_ASSERT_EQUAL((bool)uri.scheme(), false);
VALIDATION_ASSERT_EQUAL((bool)uri.userinfo(), false);
VALIDATION_ASSERT_EQUAL((bool)uri.host(), false);
VALIDATION_ASSERT_EQUAL((bool)uri.port(), false);
VALIDATION_ASSERT_EQUAL((bool)uri.query(), false);
VALIDATION_ASSERT_EQUAL((bool)uri.fragment(), true);
VALIDATION_ASSERT_EQUAL(uri.fragment_str(), "results?gui-sort=asc");
}
int
main(int argc, char* argv[])
{
return art::validation::main(argc, argv);
}

4
tests/build/.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
/config.build
/root/
/bootstrap/
build/

View File

@@ -0,0 +1,5 @@
project = # Unnamed tests subproject.
using config
using test
using dist

16
tests/build/root.build Normal file
View File

@@ -0,0 +1,16 @@
cxx.std = latest
using cxx
hxx{*}: extension = hxx
ixx{*}: extension = ixx
txx{*}: extension = txx
cxx{*}: extension = cxx
# Every exe{} in this subproject is by default a test.
#
exe{*}: test = true
# The test target for cross-testing (running tests under Wine, etc).
#
test.target = $cxx.target

1
tests/buildfile Normal file
View File

@@ -0,0 +1 @@
./: {*/ -build/}