From c518dbaa04a6557314c8c47df8eba6b9db451644 Mon Sep 17 00:00:00 2001 From: alandefreitas Date: Fri, 14 Apr 2023 21:31:52 -0300 Subject: [PATCH] simplify CI and CML --- .drone.star | 6 +- .github/actions/b2_workflow/action.yml | 141 + .github/actions/boost_clone/action.yml | 314 + .github/actions/boost_clone/scan_deps.py | 220 + .github/actions/cmake_workflow/action.yml | 534 ++ .github/actions/package_install/action.yml | 206 + .github/workflows/ci.yml | 606 +- CMakeLists.txt | 232 +- example/file_router/CMakeLists.txt | 16 +- example/finicky/CMakeLists.txt | 12 +- example/magnet/CMakeLists.txt | 14 +- example/mailto/CMakeLists.txt | 12 +- example/qrcode/CMakeLists.txt | 12 +- example/suffix_list/CMakeLists.txt | 12 +- test/CMakeLists.txt | 11 +- test/cmake_test/CMakeLists.txt | 25 +- test/extra/CMakeLists.txt | 36 +- test/fuzz/CMakeLists.txt | 22 +- test/limits/CMakeLists.txt | 27 +- test/unit/CMakeLists.txt | 138 +- test/unit/grammar/copied_strings.cpp | 33 - test/unit/url.cpp | 5 + test/wpt/CMakeLists.txt | 26 - test/wpt/main.cpp | 137 - test/wpt/setters_tests.json | 2053 ----- test/wpt/urltestdata.json | 8211 -------------------- 26 files changed, 1756 insertions(+), 11305 deletions(-) create mode 100644 .github/actions/b2_workflow/action.yml create mode 100644 .github/actions/boost_clone/action.yml create mode 100644 .github/actions/boost_clone/scan_deps.py create mode 100644 .github/actions/cmake_workflow/action.yml create mode 100644 .github/actions/package_install/action.yml delete mode 100644 test/unit/grammar/copied_strings.cpp delete mode 100644 test/wpt/CMakeLists.txt delete mode 100644 test/wpt/main.cpp delete mode 100644 test/wpt/setters_tests.json delete mode 100644 test/wpt/urltestdata.json diff --git a/.drone.star b/.drone.star index 329b9530..5e03a56c 100644 --- a/.drone.star +++ b/.drone.star @@ -116,9 +116,9 @@ def generate(compiler_ranges, cxx_range, max_cxx=2, coverage=True, docs=True, as coverage_desc[2] = 'codecov' latest_compilers = [coverage_desc] + latest_compilers if cmake: - cmake_desc = latest_gcc[:] - cmake_desc[2] = 'cmake-install' - compilers = [cmake_desc] + compilers + # cmake_desc = latest_gcc[:] + # cmake_desc[2] = 'cmake-install' + # compilers = [cmake_desc] + compilers cmake_desc = latest_gcc[:] cmake_desc[2] = 'cmake' diff --git a/.github/actions/b2_workflow/action.yml b/.github/actions/b2_workflow/action.yml new file mode 100644 index 00000000..c59f5b60 --- /dev/null +++ b/.github/actions/b2_workflow/action.yml @@ -0,0 +1,141 @@ +name: 'B2 Workflow' +description: 'This action runs a complete B2 workflow from source files' +inputs: + source-dir: + description: 'The boost source directory.' + required: false + default: '.' + build-variant: + description: 'Custom build variants.' + required: false + default: '' + modules: + description: 'The list of modules we should test.' + required: true + default: '' + gcc_toolchain: + description: 'Create a special GCC toolchain for this version of GCC and update user-config.jam' + required: false + default: '' + toolset: + description: 'Toolset name.' + required: false + default: '' + address-model: + description: 'Valid b2 list of address models.' + required: false + default: '' + cxx: + description: 'Path to C++ compiler.' + required: false + default: '' + cxxflags: + description: 'Extra compiler flags.' + required: false + default: '' + linkflags: + description: 'Extra linker flags.' + required: false + default: '' + cxxstd: + description: 'List of standards with which cmake will build and test the program.' + required: false + default: '' + ubsan: + description: 'List of standards with which cmake will build and test the program.' + required: false + default: 'false' + threading: + description: 'b2 threading option.' + required: false + default: '' + trace-commands: + description: 'Trace commands executed by the workflow.' + required: false + default: 'false' + +runs: + using: "composite" + steps: + - name: Get CPU cores + uses: SimenB/github-actions-cpu-cores@v1 + id: cpu-cores + + - name: Setup msvc dev-cmd + if: runner.os == 'Windows' + uses: ilammy/msvc-dev-cmd@v1 + + - name: Bootstrap + working-directory: ${{ inputs.source-dir }} + shell: bash + run: | + ${{ (inputs.trace-commands == 'true' && 'set -xe') || '' }} + ./bootstrap.sh + ./b2 headers + + - name: Setup GCC Toolchain + if: ${{ inputs.gcc_toolchain }} + shell: bash + run: | + ${{ (inputs.trace-commands == 'true' && 'set -xe') || '' }} + # Create dir for toolchain + GCC_TOOLCHAIN_ROOT="$HOME/gcc-toolchain" + mkdir -p "$GCC_TOOLCHAIN_ROOT" + echo "GCC_TOOLCHAIN_ROOT=\"$GCC_TOOLCHAIN_ROOT\"" >> $GITHUB_ENV + + # Create symlinks for compiler into the toolchain dir + MULTIARCH_TRIPLET="$(dpkg-architecture -qDEB_HOST_MULTIARCH)" + ln -s /usr/include "$GCC_TOOLCHAIN_ROOT/include" + ln -s /usr/bin "$GCC_TOOLCHAIN_ROOT/bin" + mkdir -p "$GCC_TOOLCHAIN_ROOT/lib/gcc/$MULTIARCH_TRIPLET" + ln -s "/usr/lib/gcc/$MULTIARCH_TRIPLET/${{inputs.gcc_toolchain}}" "$GCC_TOOLCHAIN_ROOT/lib/gcc/$MULTIARCH_TRIPLET/${{inputs.gcc_toolchain}}" + + # Write toolchain data to ~/user-config.jam + if [ -n "${{ inputs.cxx }}" -o -n "$GCC_TOOLCHAIN_ROOT" ]; then + echo -n "using ${{inputs.toolset}} : : ${{inputs.cxx}}" > ~/user-config.jam + if [ -n "$GCC_TOOLCHAIN_ROOT" ]; then + echo -n " : \"--gcc-toolchain=$GCC_TOOLCHAIN_ROOT\" \"--gcc-toolchain=$GCC_TOOLCHAIN_ROOT\"" >> ~/user-config.jam + fi + echo " ;" >> ~/user-config.jam + fi + + - name: Setup user-config.jam + if: ${{ !inputs.gcc_toolchain }} + shell: bash + run: | + ${{ (inputs.trace-commands == 'true' && 'set -xe') || '' }} + if [ -n "${{ inputs.cxx }}" -o -n "$GCC_TOOLCHAIN_ROOT" ]; then + echo -n "using ${{ inputs.toolset }} : : ${{ inputs.cxx }}" > ~/user-config.jam + if [ -n "$GCC_TOOLCHAIN_ROOT" ] + then + echo -n " : \"--gcc-toolchain=$GCC_TOOLCHAIN_ROOT\" \"--gcc-toolchain=$GCC_TOOLCHAIN_ROOT\"" >> ~/user-config.jam + fi + echo " ;" >> ~/user-config.jam + fi + + - name: B2 Workflow + working-directory: ${{ inputs.source-dir }} + shell: bash + run: | + ${{ (inputs.trace-commands == 'true' && 'set -xe') || '' }} + + B2_ARGS+=(${{ (steps.cpu-cores.outputs.count != '1' && format('"-j" "{0}"', steps.cpu-cores.outputs.count)) || '' }}) + B2_ARGS+=(${{ (inputs.toolset && format('"toolset={0}"', inputs.toolset)) || '' }}) + B2_ARGS+=(${{ (inputs.address-model && format('"address-model={0}"', inputs.address-model)) || '' }}) + B2_ARGS+=(${{ (inputs.cxxstd && format('"cxxstd={0}"', inputs.cxxstd)) || '' }}) + B2_ARGS+=(${{ (inputs.build-variant && format('"variant={0}"', inputs.build-variant)) || '' }}) + B2_ARGS+=(${{ (inputs.threading && format('"threading={0}"', inputs.threading)) || '' }}) + ${{ (inputs.ubsan == 'true' && 'export UBSAN_OPTIONS="print_stacktrace=1"') || '' }} + B2_ARGS+=(${{ (inputs.ubsan == 'true' && '"cxxflags=-fsanitize=undefined -fno-sanitize-recover=undefined" "linkflags=-fsanitize=undefined -fuse-ld=gold" "define=UBSAN=1" "debug-symbols=on" "visibility=global"') || '' }}) + B2_ARGS+=(${{ (inputs.cxxflags && format('"cxxflags={0}"', inputs.cxxflags)) || '' }}) + B2_ARGS+=(${{ (inputs.linkflags && format('"linkflags={0}"', inputs.linkflags)) || '' }}) + + modules="${{ inputs.modules }}" + for module in ${modules//,/ } + do + B2_ARGS+=("libs/$module/test") + done + + set -x + ./b2 "${B2_ARGS[@]}" + set +x diff --git a/.github/actions/boost_clone/action.yml b/.github/actions/boost_clone/action.yml new file mode 100644 index 00000000..72e8fec9 --- /dev/null +++ b/.github/actions/boost_clone/action.yml @@ -0,0 +1,314 @@ +# +# Copyright (c) 2023 Alan de Freitas (alandefreitas@gmail.com) +# +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +# +# Official repository: https://github.com/CPPAlliance/url +# + +name: 'Boost Clone' +description: 'This workflow clones the boost source directory, attempting to get it from the cache first' +inputs: + boost_dir: + description: 'The boost directory. The default value assumes boost is in-source.' + required: false + default: 'boost' + branch: + description: 'Branch of the super-project' + required: false + default: 'master' + patches: + description: 'Libraries used to patch the boost installation' + required: true + default: '' + modules: + description: 'The boost submodules we need to clone' + required: false + default: '' + scan-modules-dir: + description: 'An independent directory we should scan for boost dependencies to clone' + required: false + default: '' + scan-modules-ignore: + description: 'List of modules that should be ignored in scan-modules' + required: false + default: '' + trace-commands: + description: 'Trace commands executed by the workflow.' + required: false + default: 'false' + +runs: + using: "composite" + steps: + - name: Find python + shell: bash + id: find-python + if: inputs.scan-modules-dir != '' + run: | + # Looking for python + ${{ (inputs.trace-commands == 'true' && 'set -xe') || '' }} + if command -v python3 &> /dev/null; then + python_path="$(which python3)" + elif command -v python &> /dev/null; then + python_version_output=$(python --version) + regex='[0-9]+\.[0-9]+\.[0-9]+' + [[ $python_version_output =~ $regex ]] + python_version=${BASH_REMATCH[0]} + IFS='.' read -r -a version_components <<< "$python_version" + major_version=${version_components[0]} + if [ "$major_version" -lt 3 ]; then + echo "Python $python_version found." >&2 + echo "Please install Python 3!" >&2 + else + python_path="$(which python)" + fi + else + echo "Cannot Python 3!" >&2 + fi + if [ "$python_path" != "" ]; then + $python_path --version + echo "python_path=$python_path" >> $GITHUB_OUTPUT + fi + + - uses: actions/setup-python@v4 + if: inputs.scan-modules-dir != '' && !steps.find-python.outputs.python_path + id: setup-python + with: + python-version: '3.10' + + - name: Scan Required Boost Modules + if: inputs.scan-modules-dir != '' + id: scan-modules + shell: bash + run: | + # Scan ${{ inputs.scan-modules-dir }} + ${{ (inputs.trace-commands == 'true' && 'set -xe') || '' }} + source_dir="${{ inputs.scan-modules-dir }}" + if [[ $source_dir != /* ]]; then + source_dir="$(readlink -f "$source_dir" 2>/dev/null || realpath -e "$source_dir" 2>/dev/null || echo "$(pwd)/$source_dir")" + fi + + python_path="${{ steps.find-python.outputs.python_path || steps.setup-python.outputs.python-path }}" + + # Go to action path to find the script and aux files + # https://github.com/actions/runner/issues/716 + cd "$GITHUB_ACTION_PATH" + + # Pre-cache the files scan_deps needs for scanning + if command -v curl &> /dev/null; then + curl -o "${{ inputs.branch }}.gitmodules" "https://raw.githubusercontent.com/boostorg/boost/${{ inputs.branch }}/.gitmodules" + curl -o "${{ inputs.branch }}.exceptions.txt" "https://raw.githubusercontent.com/boostorg/boostdep/${{ inputs.branch }}/depinst/exceptions.txt" + elif command -v wget &> /dev/null; then + wget -O "${{ inputs.branch }}.gitmodules" "https://raw.githubusercontent.com/boostorg/boost/${{ inputs.branch }}/.gitmodules" + wget -O "${{ inputs.branch }}.exceptions.txt" "https://raw.githubusercontent.com/boostorg/boostdep/${{ inputs.branch }}/depinst/exceptions.txt" + else + # Let scan_deps download the files + $python_path -m pip install requests + fi + ls + + # Run scan_deps on the reference directory + set -e + modules=$($python_path scan_deps.py --dir "$source_dir" --branch ${{ inputs.branch }} ${{ inputs.scan-modules-ignore && format('--ignore {0}', inputs.scan-modules-ignore) }}) + python_exit_code=$? + set -e + if [ $python_exit_code -ne 0 ]; then + echo "Error: Scan deps failed with exit code $python_exit_code" + modules="" + fi + echo "modules=$modules" >> $GITHUB_OUTPUT + + - name: Environment + id: ctx + shell: bash + run: | + # Determine cache key for boost + ${{ (inputs.trace-commands == 'true' && 'set -xe') || '' }} + boost_hash=$(git ls-remote https://github.com/boostorg/boost.git ${{ inputs.branch }} | awk '{ print $1 }') + echo "boost_hash=$boost_hash" >> $GITHUB_OUTPUT + + # Merge input modules and scanned modules + all_modules="" + input_modules="${{ inputs.modules }}" + scanned_modules="${{ steps.scan-modules.outputs.modules }}" + for module in ${input_modules//,/ } + do + module_basename=${module##*/} + all_modules="$all_modules $module_basename" + done + for module in ${scanned_modules// / } + do + module_basename=${module##*/} + all_modules="$all_modules $module_basename" + done + echo "all_modules=$all_modules" >> $GITHUB_OUTPUT + + # Find wget or curl + if command -v curl &> /dev/null; then + curl_executable="curl" + fi + if command -v wget &> /dev/null; then + wget_executable="wget" + fi + + # Add modules hashes to boost cache key + cache_hash="" + if command -v sha1sum >/dev/null 2>&1; then + has_sha1sum=true + else + has_sha1sum=false + fi + for module in ${all_modules// / } + do + module_basename=${module##*/} + + # Ensure the module repo exists so git doesn't fail later on + module_repo_exists=false + if [ -n "$curl_executable" ]; then + module_repo_exists=$(curl --silent --fail --head https://github.com/boostorg/$module_basename >/dev/null && echo "true" || echo "false") + elif [ -n "$wget_executable" ]; then + module_repo_exists=$(wget --quiet --spider https://github.com/boostorg/$module_basename && echo "true" || echo "false") + fi + + # Get a hash for the module + if [ "$module_repo_exists" == "true" ]; then + module_hash=$(git ls-remote https://github.com/boostorg/$module_basename.git ${{ inputs.branch }} | awk '{ print $1 }') + else + module_hash=$boost_hash + fi + + # Update the cache key with a hash for the module only + # We only invalidate the cache if one of the modules has changed. + # Changing only the boost super-project won't invalidate the cache + if [ "$cache_hash" == "" ]; then + cache_hash=$module_hash + else + concatenated_string="${cache_hash}${module_hash}" + if [ "$has_sha1sum" == "true" ]; then + cache_hash=$(echo -n "${concatenated_string}" | sha1sum | awk '{print $1}') + else + cache_hash=$(echo -n "${concatenated_string}" | shasum -a 1 | awk '{print $1}') + fi + fi + done + + # Add patch names and hashes to hash + patches=${{ inputs.patches }} + for patch in ${patches//,/ } + do + patch_hash=$(git ls-remote $patch ${{ inputs.branch }} | awk '{ print $1 }') + if [ "cache_hash" == "" ]; then + cache_hash=$patch_hash + else + concatenated_string="${cache_hash}${patch_hash}" + if [ "$has_sha1sum" == "true" ]; then + cache_hash=$(echo -n "${concatenated_string}" | sha1sum | awk '{print $1}') + else + cache_hash=$(echo -n "${concatenated_string}" | shasum -a 1 | awk '{print $1}') + fi + fi + done + + # If there are no modules, then we update the cache key with the boost-hash + # as we are about to clone all modules + # cache_os=${{ runner.os }} + # cache_os="$(echo "$cache_os" | tr '[:upper:]' '[:lower:]')" + if [ "cache_hash" == "" ]; then + # cache_hash=$cache_os-boost-all-$boost_hash + cache_hash=boost-source-all-$boost_hash + else + # cache_hash=$cache_os-boost-$cache_hash + cache_hash=boost-source-$cache_hash + fi + echo "cache_hash=$cache_hash" >> $GITHUB_OUTPUT + + # absolute cache directory + working_dir="$(pwd)" + boost_dir="${{ inputs.boost_dir }}" + if [[ $boost_dir != /* ]]; then + boost_dir="$(readlink -f "$boost_dir" 2>/dev/null || realpath -e "$boost_dir" 2>/dev/null || echo "$working_dir/$boost_dir")" + fi + echo "boost_dir=$boost_dir" >> $GITHUB_OUTPUT + + # Attempt to get boost with the specified modules from the cache before cloning it + - name: boost cache + id: cache-boost + uses: actions/cache@v3 + with: + path: ${{ steps.ctx.outputs.boost_dir }} + key: ${{ steps.ctx.outputs.cache_hash }} + + # Clone boost if not found in cache + - name: boost clone + if: steps.cache-boost.outputs.cache-hit != 'true' + shell: bash + run: | + git clone https://github.com/boostorg/boost.git -b ${{ inputs.branch }} "${{ inputs.boost_dir }}" + + # Apply patches if boost not found in cache + - name: boost patches + if: steps.cache-boost.outputs.cache-hit != 'true' && inputs.patches != '' + shell: bash + working-directory: ${{ inputs.boost_dir }}/libs + run: | + # Apply boost patches ${{ inputs.patches }} + patches=${{ inputs.patches }} + for patch in ${patches//,/ } + do + git clone $patch -b ${{ inputs.branch }} + done + + - name: Get CPU cores + uses: SimenB/github-actions-cpu-cores@v1 + id: cpu-cores + + # Initialize all submodules if boost not found in cache and no specific modules were specified + - name: Initialize all submodules + if: (steps.cache-boost.outputs.cache-hit != 'true' && steps.ctx.outputs.all_modules == '') + working-directory: ${{ inputs.boost_dir }} + shell: bash + run: | + # Update all boost submodules + git submodule update --depth 1 --jobs ${{ steps.cpu-cores.outputs.count }}--init --recursive + + # Initialize specified submodules if boost not found in cache and submodules were specified + - name: Initialize specified submodules + if: (steps.cache-boost.outputs.cache-hit != 'true' && steps.ctx.outputs.all_modules != '') + working-directory: ${{ inputs.boost_dir }} + shell: bash + run: | + # Scan transitive dependencies and update submodules + ${{ (inputs.trace-commands == 'true' && 'set -xe') || '' }} + git submodule update --depth 1 -q --init tools/boostdep + # Run boostdep for required modules: ${{ steps.ctx.outputs.all_modules }} + + # Initialize each explicitly specified module + modules="${{ steps.ctx.outputs.all_modules }}" + for module in ${modules// / } + do + echo "Initialize submodule $module" + git submodule update --depth 1 --jobs ${{ steps.cpu-cores.outputs.count }} -q --init libs/$module || true + done + + # Initialize dependencies of each explicitly specified module + python_path="${{ steps.find-python.outputs.python_path || steps.setup-python.outputs.python-path }}" + python_exit_code=0 + for module in ${modules// / } + do + echo "Run boostdep for required module $module" + set +e + $python_path tools/boostdep/depinst/depinst.py --include benchmark --include example --include examples --include tools --include source --git_args "--jobs ${{ steps.cpu-cores.outputs.count }} --depth 1" $module + python_exit_code=$? + set -e + if [ $python_exit_code -ne 0 ]; then + echo "Error: Boostdep failed with exit code $python_exit_code" + break + fi + done + + if [ $python_exit_code -ne 0 ]; then + echo "Boostdep failed. Initializing all modules..." + git submodule update --depth 1 --jobs ${{ steps.cpu-cores.outputs.count }}--init --recursive + fi diff --git a/.github/actions/boost_clone/scan_deps.py b/.github/actions/boost_clone/scan_deps.py new file mode 100644 index 00000000..aa6f0a67 --- /dev/null +++ b/.github/actions/boost_clone/scan_deps.py @@ -0,0 +1,220 @@ +#!/usr/bin/env python + +# +# Copyright (c) 2023 Alan de Freitas (alandefreitas@gmail.com) +# +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +# +# Official repository: https://github.com/CPPAlliance/url +# + +# Adapted from boostorg/boostdep/blob/develop/depinst/depinst.py + +# depinst.py - installs the dependencies needed to test +# a Boost library +# +# Copyright 2016-2020 Peter Dimov +# +# Distributed under the Boost Software License, Version 1.0. +# See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt + +# The difference between this script and depinst.py is that depinst.py +# is intended to clone the dependencies of other boost modules, while +# this script is only intended to find what modules should be cloned +# for a library that depends on boost needs to work. + +# The intention of the script is to determine what modules should be +# cloned *before* boost is cloned, thus allowing us to determine if +# we can use cached results. + +from __future__ import print_function + +import re +import sys +import os +import argparse + +verbose = 0 + + +def vprint(level, *args): + if verbose >= level: + print(*args) + + +def is_module(m, gm): + return ('libs/' + m) in gm + + +def module_for_header(h, x, gm): + if h in x: + return x[h] + else: + # boost/function.hpp + m = re.match('boost/([^\\./]*)\\.h[a-z]*$', h) + + if m and is_module(m.group(1), gm): + return m.group(1) + + # boost/numeric/conversion.hpp + m = re.match('boost/([^/]*/[^\\./]*)\\.h[a-z]*$', h) + + if m and is_module(m.group(1), gm): + return m.group(1) + + # boost/numeric/conversion/header.hpp + m = re.match('boost/([^/]*/[^/]*)/', h) + + if m and is_module(m.group(1), gm): + return m.group(1) + + # boost/function/header.hpp + m = re.match('boost/([^/]*)/', h) + + if m and is_module(m.group(1), gm): + return m.group(1) + + vprint(0, 'Cannot determine module for header', h) + + return None + + +def scan_header_dependencies(f, exceptions, submodule_paths): + deps = set() + for line in f: + m = re.match('[ \t]*#[ \t]*include[ \t]*["<](boost/[^">]*)[">]', line) + if m: + h = m.group(1) + mod = module_for_header(h, exceptions, submodule_paths) + deps.add(mod) + return deps + + +def scan_directory(d, exceptions, submodule_paths): + vprint(1, 'Scanning directory', d) + + if os.name == 'nt' and sys.version_info[0] < 3: + d = unicode(d) + + deps = set() + for root, dirs, files in os.walk(d): + for file in files: + fn = os.path.join(root, file) + vprint(2, 'Scanning file', fn) + if sys.version_info[0] < 3: + with open(fn, 'r') as f: + deps.update(scan_header_dependencies(f, exceptions, submodule_paths)) + else: + with open(fn, 'r', encoding='latin-1') as f: + deps.update(scan_header_dependencies(f, exceptions, submodule_paths)) + return deps + + +def list_boost_dependencies(dir, subdirs, exceptions, submodule_paths): + vprint(1, 'Scanning dir', dir) + deps = set() + for subdir in subdirs: + deps.update(scan_directory(os.path.join(dir, subdir), exceptions, submodule_paths)) + return deps + + +def read_exceptions(branch): + # exceptions.txt is the output of "boostdep --list-exceptions" + vprint(1, 'Reading exceptions.txt') + x = {} + module = None + + exceptions_path = os.path.join(os.path.dirname(sys.argv[0]), branch + '.exceptions.txt') + if not os.path.exists(exceptions_path): + import requests + url = "https://raw.githubusercontent.com/boostorg/boostdep/" + branch + "/depinst/exceptions.txt" + response = requests.get(url) + if response.status_code == 200: + content = response.text + with open(exceptions_path, "w") as f: + f.write(content) + + with open(exceptions_path, 'r') as f: + for line in f: + line = line.rstrip() + m = re.match('(.*):$', line) + if m: + module = m.group(1).replace('~', '/') + else: + header = line.lstrip() + x[header] = module + return x + + +def read_gitmodules(branch): + vprint(1, 'Reading .gitmodules') + gm = [] + + gitmodules_path = os.path.join(os.path.dirname(sys.argv[0]), branch + '.gitmodules') + if not os.path.exists(gitmodules_path): + import requests + url = "https://raw.githubusercontent.com/boostorg/boost/" + branch + "/.gitmodules" + response = requests.get(url) + if response.status_code == 200: + content = response.text + with open(gitmodules_path, "w") as f: + f.write(content) + + with open(gitmodules_path, 'r') as f: + for line in f: + line = line.strip() + m = re.match('path[ \t]*=[ \t]*(.*)$', line) + if m: + gm.append(m.group(1)) + + return gm + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description='Installs the dependencies needed to test a Boost library.') + + parser.add_argument('--dir', help="directory to scan") + parser.add_argument('--branch', help="boost branch", default='master') + + parser.add_argument('-I', '--include', help="additional subdirectory to scan; can be repeated", metavar='DIR', + action='append', default=[]) + parser.add_argument('-X', '--exclude', + help="exclude a default subdirectory ('include', 'src', or 'test') from scan; can be repeated", + metavar='DIR', action='append', default=[]) + parser.add_argument('-N', '--ignore', help="exclude top-level dependency even when found in scan; can be repeated", + metavar='LIB', action='append', default=[]) + parser.add_argument('-v', '--verbose', help='enable verbose output', action='count', default=0) + parser.add_argument('-q', '--quiet', help='quiet output (opposite of -v)', action='count', default=0) + + args = parser.parse_args() + + verbose = args.verbose - args.quiet + + vprint(2, '-X:', args.exclude) + vprint(2, '-I:', args.include) + vprint(2, '-N:', args.ignore) + + exceptions = read_exceptions(args.branch) + vprint(2, 'Exceptions:', exceptions) + + submodule_paths = read_gitmodules(args.branch) + vprint(2, '.gitmodules:', submodule_paths) + + subdirs = ['include', 'src', 'source', 'test', 'tests', 'example', 'examples'] + for subdir in args.exclude: + if subdir in subdirs: + subdirs.remove(subdir) + for subdir in args.include: + if subdir not in subdirs: + subdirs.append(subdir) + vprint(1, 'Directories to scan:', *subdirs) + + modules = list_boost_dependencies(args.dir, subdirs, exceptions, submodule_paths) + for ignored in args.ignore: + if ignored in modules: + modules.remove(ignored) + + sorted_modules = sorted(modules) + print(' '.join(sorted_modules)) diff --git a/.github/actions/cmake_workflow/action.yml b/.github/actions/cmake_workflow/action.yml new file mode 100644 index 00000000..ef3bb424 --- /dev/null +++ b/.github/actions/cmake_workflow/action.yml @@ -0,0 +1,534 @@ +name: 'CMake Workflow' +description: 'This action runs a complete CMake workflow from source files' +inputs: + source-dir: + description: 'Directory for the source files.' + required: false + default: '.' + build-dir: + description: 'Directory for the binaries relative to the source directory.' + required: false + default: 'build' + cmake-min-version: + description: 'The minimum cmake version for this workflow. If the existing version is below that, the action attempts to update CMake.' + required: false + default: '3.5' + cmake_exec: + description: 'The cmake executable' + required: false + default: 'cmake' + cc: + description: 'Path to C compiler.' + required: false + default: '' + cxx: + description: 'Path to C++ compiler.' + required: false + default: '' + cxxstd: + description: 'List of standards with which cmake will build and test the program.' + required: false + default: '' + toolchain: + description: 'Path to toolchain.' + required: false + default: '' + generator: + description: 'Generator name.' + required: false + default: '' + build-type: + description: 'Build type.' + required: false + default: 'Release' + build-target: + description: 'Targets to build instead of the default target' + required: false + default: '' + install-prefix: + description: 'Path where the library should be installed.' + required: false + default: '.local/usr' + run-tests: + description: 'Whether we should run tests.' + required: false + default: 'true' + install: + description: 'Whether we should install the library.' + required: false + default: 'true' + extra-args: + description: 'Extra arguments to cmake configure command.' + required: false + default: '' + create-annotations: + description: 'Create github annotations on errors.' + required: false + default: 'true' + ref-source-dir: + description: 'A reference source directory for annotations.' + required: false + default: '' + trace-commands: + description: 'Trace commands executed by the workflow.' + required: false + default: 'false' + +runs: + using: "composite" + steps: + - name: Get CPU cores + uses: SimenB/github-actions-cpu-cores@v1 + id: cpu-cores + + - name: Setup msvc dev-cmd + if: runner.os == 'Windows' + uses: ilammy/msvc-dev-cmd@v1 + + - name: CMake Features + shell: bash + id: version + working-directory: ${{ inputs.source_dir }} + run: | + ${{ (inputs.trace-commands == 'true' && 'set -xe') || '' }} + + # Extract cmake min version + cmake_min_version=${{ inputs.cmake-min-version }} + IFS='.' read -r -a version_components <<< "$cmake_min_version" + major_min_version=${version_components[0]} + minor_min_version=${version_components[1]:-0} + patch_min_version=${version_components[2]:-0} + cmake_min_version="$major_min_version.$minor_min_version.$patch_min_version" + + # Extract cmake current version + cmake_version_output=$(cmake --version) + # extract the version number using a regular expression + regex='[0-9]+\.[0-9]+\.[0-9]+' + [[ $cmake_version_output =~ $regex ]] + cmake_version=${BASH_REMATCH[0]} + IFS='.' read -r -a version_components <<< "$cmake_version" + major_version=${version_components[0]} + minor_version=${version_components[1]} + patch_version=${version_components[2]} + + # Check version requirements + version_is_ge=false + if [ "$major_version" -gt "$major_min_version" ]; then + version_is_ge=true + elif [ "$major_version" -lt "$major_min_version" ]; then + version_is_ge=false + else + # major versions are equal, check minor versions + if [ "$minor_version" -gt "$minor_min_version" ]; then + version_is_ge=true + elif [ "$minor_version" -lt "$minor_min_version" ]; then + version_is_ge=false + else + # major and minor versions are equal, check patch versions + if [ "$patch_version" -ge "$patch_min_version" ]; then + version_is_ge=true + else + version_is_ge=false + fi + fi + fi + + # Update cmake if needed + if [ "$version_is_ge" == "false" ]; then + url_os=${{ runner.os }} + url_os="$(echo "$url_os" | tr '[:upper:]' '[:lower:]')" + if [ "$minor_min_version" -le "19" ]; then + if [ "$url_os" == "windows" ]; then + url_os="win${{ (runner.arch == 'X86' && '32') || '64' }}" + elif [ "$url_os" == "linux" ]; then + url_os="Linux" + elif [ "$url_os" == "macos" ]; then + if [ "$minor_min_version" -le "18" ]; then + url_os="Darwin" + elif [ "$patch_min_version" -le "2" ]; then + url_os="Darwin" + fi + fi + fi + + url_arch=${{ runner.arch }} + url_arch="$(echo "$url_arch" | tr '[:upper:]' '[:lower:]')" + if [ "$url_os" == "windows" ]; then + url_arch="${{ (startswith(runner.arch, 'ARM') && 'arm64') || 'x86_64' }}" + elif [ "$url_os" == "win32" ]; then + url_arch="x86" + elif [ "$url_os" == "win64" ]; then + url_arch="x64" + elif [ "$url_os" == "linux" ]; then + url_arch="${{ (startswith(runner.arch, 'ARM') && 'aarch64') || 'x86_64' }}" + elif [ "$url_os" == "Linux" ]; then + url_arch="${{ (startswith(runner.arch, 'ARM') && 'aarch64') || 'x86_64' }}" + elif [ "$url_os" == "macos" ]; then + url_arch="universal" + fi + + url_extension="${{ (runner.os == 'Windows' && 'zip') || 'tar.gz' }}" + + cmake_basename="cmake-$cmake_min_version-$url_os-$url_arch" + cmake_filename="$cmake_basename.$url_extension" + cmake_url="https://cmake.org/files/v$major_min_version.$minor_min_version/$cmake_filename" + if command -v curl &> /dev/null; then + curl -o "$cmake_filename" "$cmake_url" + elif command -v wget &> /dev/null; then + wget -O "$cmake_filename" "$cmake_url" + fi + + ${{ (runner.os == 'Windows' && 'unzip $cmake_filename') || (inputs.trace-commands == 'true' && 'tar -xvf $cmake_filename') || 'tar -xf $cmake_filename' }} + cmake_bin_path="$(pwd)/$cmake_basename/bin" + echo "$cmake_bin_path" >> $GITHUB_PATH + export PATH=$PATH:"$cmake_bin_path" + $cmake_bin_path/cmake --version + + cmake_version="$cmake_min_version" + major_version="$major_min_version" + minor_version="$minor_min_version" + patch_version="$patch_min_version" + fi + + # Identify features + if [ "$minor_version" -ge 13 ]; then + ${{ (inputs.trace-commands == 'true' && 'echo "CMake version is greater than or equal to 3.13"') || '' }} + ${{ (inputs.trace-commands == 'true' && 'echo "-B syntax is supported"') || '' }} + path_to_build=true + else + ${{ (inputs.trace-commands == 'true' && 'echo "CMake version is NOT greater than or equal to 3.13"') || '' }} + ${{ (inputs.trace-commands == 'true' && 'echo "-B syntax is NOT supported"') || '' }} + path_to_build=false + fi + + if [ "$minor_version" -ge 12 ]; then + ${{ (inputs.trace-commands == 'true' && 'echo "CMake version is greater than or equal to 3.12"') || '' }} + ${{ (inputs.trace-commands == 'true' && 'echo "-j syntax is supported"') || '' }} + parallel_build=true + else + ${{ (inputs.trace-commands == 'true' && 'echo "CMake version is NOT greater than or equal to 3.12"') || '' }} + ${{ (inputs.trace-commands == 'true' && 'echo "-j syntax is NOT supported"') || '' }} + parallel_build=false + fi + + if [ "$minor_version" -ge 15 ]; then + ${{ (inputs.trace-commands == 'true' && 'echo "CMake version is greater than or equal to 3.15"') || '' }} + ${{ (inputs.trace-commands == 'true' && 'echo "--target with multiple targets is supported"') || '' }} + ${{ (inputs.trace-commands == 'true' && 'echo "cmake --install is supported"') || '' }} + build_multiple_targets=true + cmake_install=true + else + ${{ (inputs.trace-commands == 'true' && 'echo "CMake version is NOT greater than or equal to 3.15"') || '' }} + ${{ (inputs.trace-commands == 'true' && 'echo "--target with multiple targets is NOT supported"') || '' }} + ${{ (inputs.trace-commands == 'true' && 'echo "cmake --install is NOT supported"') || '' }} + build_multiple_targets=false + cmake_install=false + fi + + generator="${{ inputs.generator }}" + if [ "$generator" == "" ]; then + generator=$(cmake --system-information | sed -n 's/^CMAKE_GENERATOR [[:space:]]*"\([^"]*\)".*/\1/p') + fi + if [ "$generator" == "" ]; then + generator=${{ ((runner.os == 'macOS') && '"XCode"') || ((runner.os == 'Windows') && '"Visual Studio"') || '"Unix Makefiles"' }} + fi + + if [[ $generator == "Visual Studio"* ]]; then + generator_is_multi_config=true + elif [ "$generator" == "Xcode" ]; then + generator_is_multi_config=true + elif [ "$generator" == "Ninja Multi-Config" ]; then + generator_is_multi_config=true + else + generator_is_multi_config=false + fi + ${{ (inputs.trace-commands == 'true' && 'if [ "$generator_is_multi_config" == "true" ]; then echo "Generator is multi-config"; fi') || '' }} + + echo "cmake_version=$cmake_version" >> $GITHUB_OUTPUT + echo "major_version=$major_version" >> $GITHUB_OUTPUT + echo "minor_version=$minor_version" >> $GITHUB_OUTPUT + echo "patch_version=$patch_version" >> $GITHUB_OUTPUT + echo "cmake_min_version=$cmake_min_version" >> $GITHUB_OUTPUT + echo "major_min_version=$major_min_version" >> $GITHUB_OUTPUT + echo "minor_min_version=$minor_min_version" >> $GITHUB_OUTPUT + echo "patch_min_version=$patch_min_version" >> $GITHUB_OUTPUT + echo "path_to_build=$path_to_build" >> $GITHUB_OUTPUT + echo "parallel_build=$parallel_build" >> $GITHUB_OUTPUT + echo "build_multiple_targets=$build_multiple_targets" >> $GITHUB_OUTPUT + echo "cmake_install=$cmake_install" >> $GITHUB_OUTPUT + echo "generator_is_multi_config=$generator_is_multi_config" >> $GITHUB_OUTPUT + + - name: CMake Workflow + shell: bash + working-directory: ${{ inputs.source_dir }} + run: | + ${{ (inputs.trace-commands == 'true' && 'set -xe') || '' }} + + # compiler executables + cc=${{ inputs.cc }} + if [ "$cc" != "" ]; then + if command -v $cc &> /dev/null; then + cc="$(which $cc)" + elif command -v /usr/bin/$cc &> /dev/null; then + cc="/usr/bin/$cc" + fi + fi + cxx=${{ inputs.cxx }} + if [ "$cxx" != "" ]; then + if command -v $cxx &> /dev/null; then + cxx="$(which $cxx)" + elif command -v /usr/bin/$cxx &> /dev/null; then + cxx="/usr/bin/$cxx" + fi + fi + + # std versions + cxxstds=${{ inputs.cxxstd }} + if [ "$cxxstds" == "" ]; then + cxxstds="defaultcxx" + fi + main_cxxstd=${cxxstds##*,} + + run_tests="${{ inputs.run-tests }}" + if [ "$run_tests" == "true" ]; then + cmake_enable_test_args="-D BUILD_TESTING=ON" + fi + + # absolute directories + working_dir="$(pwd)" + source_dir="${{ inputs.source-dir }}" + if [[ $source_dir != /* ]]; then + source_dir="$(readlink -f "$source_dir" 2>/dev/null || realpath -e "$source_dir" 2>/dev/null || echo "$working_dir/$source_dir")" + fi + + ref_source_dir="${{ inputs.ref-source-dir || inputs.source-dir }}" + if [[ $ref_source_dir != /* ]]; then + ref_source_dir="$(readlink -f "$ref_source_dir" 2>/dev/null || realpath -e "$ref_source_dir" 2>/dev/null || echo "$working_dir/$ref_source_dir")" + fi + + build_dir="${{ inputs.build-dir }}" + if [[ $build_dir != /* ]]; then + build_dir="$(readlink -f "$source_dir/$build_dir" 2>/dev/null || realpath -e "$source_dir/$build_dir" 2>/dev/null || echo "$source_dir/$build_dir")" + fi + + # iterate stds + for cxxstd in ${cxxstds//,/ } + do + if [ "$cxxstd" != "defaultcxx" ]; then + echo "==================================> C++$cxxstd" + fi + std_build_dir="$build_dir$( [ "$cxxstd" == "$main_cxxstd" ] && echo "" || echo "-$cxxstd" )" + + # Configure step + CONFIGURE_ARGS=(${{ (steps.version.outputs.path_to_build == 'true' && '"-S" "$source_dir" "-B" "$std_build_dir"') || '' }}) + CONFIGURE_ARGS+=(${{ (inputs.generator && format('"-G" "{0}"', inputs.generator)) || '' }}) + CONFIGURE_ARGS+=(${{ ((steps.version.outputs.generator_is_multi_config == 'false' && inputs.build-type) && format('"-D" "CMAKE_BUILD_TYPE={0}"', inputs.build-type)) || '' }}) + CONFIGURE_ARGS+=(${{ ((steps.version.outputs.generator_is_multi_config == 'true' && inputs.build-type) && format('"-D" "CMAKE_CONFIGURATION_TYPES={0}"', inputs.build-type)) || '' }}) + CONFIGURE_ARGS+=(${{ (inputs.toolchain && format('"-D" "CMAKE_TOOLCHAIN_FILE={0}"', inputs.toolchain)) || '' }}) + CONFIGURE_ARGS+=(${{ (inputs.toolchain && format('"-D" "CMAKE_TOOLCHAIN_FILE={0}"', inputs.toolchain)) || '' }}) + CONFIGURE_ARGS+=(${{ (inputs.install-prefix && format('"-D" "CMAKE_INSTALL_PREFIX={0}"', inputs.install-prefix)) || '' }}) + CONFIGURE_ARGS+=(${{ (inputs.run-tests && '"-D" "BUILD_TESTING=ON"') || '' }}) + CONFIGURE_ARGS+=(${{ (inputs.cc && '"-D" "CMAKE_C_COMPILER=$cc"') || '' }}) + CONFIGURE_ARGS+=(${{ (inputs.cxx && '"-D" "CMAKE_CXX_COMPILER=$cxx"') || '' }}) + CONFIGURE_ARGS+=($( [ "$cxxstd" == "defaultcxx" ] && echo "" || echo "-D CMAKE_CXX_STANDARD=$cxxstd" )) + CONFIGURE_ARGS+=(${{ inputs.extra-args }}) + SOURCE_DIR_ARG=${{ (steps.version.outputs.path_to_build == 'false' && '"$source_dir"') || '' }} + + mkdir "$std_build_dir" || true + cd "$std_build_dir" + set +e + set -x + cmake "${CONFIGURE_ARGS[@]}" $SOURCE_DIR_ARG 2>&1 | tee -a "$std_build_dir/cmake-configure-output.txt" + ${{ (inputs.trace-commands != 'true' && 'set +x') || '' }} + cmake_exit_code=$? + set -e + cd "$working_dir" + + # Configure step annotations + if [[ ${{ inputs.create-annotations }} == "true" ]]; then + cmake_regex="^CMake (Warning|Error)( at ([^:]+):([[:digit:]]+) \\(([^:]+)\\))?:(.*)" + message_type="" + lines="" + while read line; do + if [[ "$message_type" != "" ]]; then + ${{ (inputs.trace-commands == 'true' && 'echo "$line"') || '' }} + ${{ (inputs.trace-commands == 'true' && 'echo "$lines"') || '' }} + lines="$lines\n$line" + ${{ (inputs.trace-commands == 'true' && 'echo "$lines"') || '' }} + if [[ "${lines: -4}" != "\n\n" ]]; then + continue + fi + else + if [[ $line == "CMake Error"* ]]; then + message_type="error" + lines="$line" + continue + elif [[ $line == "CMake Warning"* ]]; then + message_type="warning" + lines="$line" + continue + fi + fi + + ${{ (inputs.trace-commands == 'true' && 'echo "$lines"') || '' }} + if [[ $lines =~ $cmake_regex ]]; then + filename=${BASH_REMATCH[3]} + if [ "$filename" != "" ]; then + ${{ runner.os != 'macOS' && 'filename="$(realpath -m --relative-to="$ref_source_dir" "$source_dir/$filename")"' }} + fi + line_number=${BASH_REMATCH[4]} + error_type=${BASH_REMATCH[1]} + error_code=${BASH_REMATCH[5]} + error_message=${BASH_REMATCH[6]} + error_message="${error_message:2}" + error_message=$(echo $error_message | sed 's/....$//') + error_message=$(echo "$error_message" | sed 's/:\\n\\n/: /g') + error_message=$(echo "$error_message" | sed 's/.\\n/. /g') + error_message=$(echo "$error_message" | sed 's/\\n/. /g') + error_message=$(echo "$error_message" | sed 's/\n/. /g') + if [ "$filename" == "" ]; then + echo "::$message_type title:CMake-$error_type::CMake: $error_message" + else + echo "::$message_type file=$filename,line=$line_number,title:CMake-$error_type::CMake: $error_message" + fi + fi + message_type="" + lines="" + done < "$std_build_dir/cmake-configure-output.txt" + fi + + if [[ $cmake_exit_code -ne 0 ]]; then + echo "CMake configuration step failed with exit code $cmake_exit_code" + false + fi + + # Build step + set +e + jobs_args="${{ (steps.version.outputs.parallel_build == 'false' && '') || format('-j {0}', steps.cpu-cores.outputs.count) }}" + if [[ "${{ steps.version.outputs.build_multiple_targets }}" == "true" || "${{ inputs.build-target }}" != *" "* ]]; then + set -x + cmake --build "$std_build_dir" --config ${{ inputs.build-type }} $jobs_args ${{ (inputs.build-target != '' && format('--target {0}', inputs.build-target)) || '' }} 2>&1 | tee -a "$std_build_dir/cmake-build-output.txt" + ${{ (inputs.trace-commands != 'true' && 'set +x') || '' }} + else + build_targets="${{ inputs.build-target }}" + for build_target in ${build_targets// / }; do + set -x + cmake --build "$std_build_dir" --config ${{ inputs.build-type }} $jobs_args --target $build_target 2>&1 | tee -a "$std_build_dir/cmake-build-output.txt" + ${{ (inputs.trace-commands != 'true' && 'set +x') || '' }} + done + fi + cmake_exit_code=$? + set -e + + # Build step annotations + if [[ ${{ inputs.create-annotations }} == "true" ]]; then + msvc_regex="^([^\\(\\)]+)\\(([[:digit:]]+)\\): (warning|error) ([^:]+): (.*)$" + gcc_clang_regex="^([^:]+):([[:digit:]]+):([[:digit:]]+)?: (warning|error):([^\\[]*)(\\[-W[A-Za-z0-9-]*\\])?$" + while read line; do + ${{ (inputs.trace-commands == 'true' && 'echo "$line"') || '' }} + if [[ "$line" =~ $gcc_clang_regex ]]; then + filename=${BASH_REMATCH[1]} + ${{ runner.os != 'macOS' && 'filename="$(realpath -m --relative-to="$ref_source_dir" "$filename")"' }} + if [[ $filename == ../* ]]; then + continue + fi + error_type=${BASH_REMATCH[4]} + line_number=${BASH_REMATCH[2]} + column=${BASH_REMATCH[3]} + title="Build Error" + msg="" + compiler="${{ inputs.cxx }}" + if [ "$compiler" != "" ]; then + compiler=$(basename $compiler) + title="$title - $compiler" + msg="$compiler" + fi + error_message=${BASH_REMATCH[5]} + if [ "$msg" != "" ]; then + msg="$msg: $error_message" + else + msg="$error_message" + fi + error_code=${BASH_REMATCH[6]} + if [ "$error_code" != "" ]; then + title="$title - $error_code" + msg="$msg ($error_code)" + fi + echo "::$error_type file=$filename,line=$line_number,col:$column,title:$title::$msg" + elif [[ "$line" =~ $msvc_regex ]]; then + filename=${BASH_REMATCH[1]} + ${{ runner.os != 'macOS' && 'filename="$(realpath -m --relative-to="$ref_source_dir" "$filename")"' }} + if [[ $filename == ../* ]]; then + continue + fi + line_number=${BASH_REMATCH[2]} + error_type=${BASH_REMATCH[3]} + error_code=${BASH_REMATCH[4]} + error_message=${BASH_REMATCH[5]} + compiler="${{ inputs.cxx }}" + if [ "$compiler" != ""]; then + compiler=$(basename $compiler) + fi + echo "::$error_type file=$filename,line=$line_number,title:$compiler: $error_type $error_code::$compiler: $error_message ($error_type - $error_code)" + fi + done < "$std_build_dir/cmake-build-output.txt" + fi + + if [[ $cmake_exit_code -ne 0 ]]; then + echo "CMake build step failed with exit code $cmake_exit_code" + false + fi + + # Install step + mkdir "${{ inputs.install-prefix }}" || true + if [[ "${{ inputs.install }}" == true && "$cxxstd" == "$main_cxxstd" ]]; then + if [[ ${{ steps.version.outputs.cmake_install }} == "true" ]]; then + set -x + cmake --install "$std_build_dir" --config ${{ inputs.build-type }} --prefix "${{ inputs.install-prefix }}" 2>&1 | tee -a "$std_build_dir/cmake-install-output.txt" + ${{ (inputs.trace-commands != 'true' && 'set +x') || '' }} + else + set -x + cmake --build "$std_build_dir" --config ${{ inputs.build-type }} --target install || true 2>&1 | tee -a "$std_build_dir/cmake-install-output.txt" + ${{ (inputs.trace-commands != 'true' && 'set +x') || '' }} + fi + fi + + # Test step + if [[ "$run_tests" == true && "$cxxstd" == "$main_cxxstd" ]]; then + set +e + ctest --test-dir "$std_build_dir" $jobs_args -C ${{ inputs.build-type }} --no-tests=error --progress --output-on-failure 2>&1 | tee -a "$std_build_dir/cmake-test-output.txt" + cmake_exit_code=$? + set -e + + # Test step annotations + if [[ "${{ inputs.create-annotations }}" == true ]]; then + boost_test_regex="^#[[:digit:]]+ ([^\\(\\)]+)\\(([[:digit:]]+)\\) failed: (.*)" + while read line; do + if [[ "$line" =~ $boost_test_regex ]]; then + filename=${BASH_REMATCH[1]} + if [ -e "$ref_source_dir/$filename" ]; then + ${{ runner.os != 'macOS' && 'filename="$(realpath -m --relative-to="$ref_source_dir" "$ref_source_dir/$filename")"' }} + else + test_filename=$(find "$ref_source_dir/test" -name "$filename" | head -n 1 | xargs) + if [ "$test_filename" != "" ]; then + ${{ runner.os != 'macOS' && 'filename="$(realpath -m --relative-to="$ref_source_dir" "$test_filename")"' }} + else + ref_filename=$(find "$ref_source_dir" -name "$filename" | head -n 1 | xargs) + if [ "$ref_filename" == "" ]; then + ${{ runner.os != 'macOS' && 'filename="$(realpath -m --relative-to="$ref_source_dir" "$ref_filename")"' }} + fi + fi + fi + line_number=${BASH_REMATCH[2]} + error_message=${BASH_REMATCH[3]} + echo "::error file=$filename,line=$line_number,title:Boost.Test::Boost.Test: $error_message" + fi + done < "$std_build_dir/cmake-test-output.txt" + fi + + if [[ $cmake_exit_code -ne 0 ]]; then + echo "CMake test step failed with exit code $cmake_exit_code" + false + fi + fi + done diff --git a/.github/actions/package_install/action.yml b/.github/actions/package_install/action.yml new file mode 100644 index 00000000..1d65e613 --- /dev/null +++ b/.github/actions/package_install/action.yml @@ -0,0 +1,206 @@ +name: 'Install dependencies' +description: 'This actions installs dependencies from multiple package managers for a workflow' +inputs: + vcpkg: + description: 'List of packages we should install with vcpkg. (Whitespace-separated)' + required: false + default: '' + apt-get: + description: 'List of packages we should install with apt-get. (Whitespace-separated)' + required: false + default: '' + + vcpkg_triplet: + description: 'The triplet used by vcpkg to install packages.' + required: false + default: '' + vcpkg_dir: + description: 'The directory where vcpkg should be cloned and installed.' + required: false + default: 'vcpkg' + vcpkg_branch: + description: 'vcpkg branch we should use' + required: false + default: 'master' + + apt-get-retries: + description: 'Number of time we should retry when apt-get fails.' + required: false + default: '1' + apt-get-sources: + description: 'List of sources for apt-get.' + required: false + default: '' + apt-get-source-keys: + description: 'List of source keys for apt-get.' + required: false + default: '' + apt-get-ignore-missing: + description: 'Whether apt-get should ignore missing packages.' + required: false + default: 'false' + + +outputs: + vcpkg_toolchain: + description: "vcpkg toolchain file" + value: ${{ steps.ctx.outputs.vcpkg_toolchain }} + +runs: + using: "composite" + steps: + # Install packages on ubuntu + # https://docs.github.com/en/actions/learn-github-actions/contexts#runner-context + - name: apt-get packages + shell: bash + if: ${{ runner.os == 'Linux' && inputs.apt-get }} + run: | + set -xe + + # Determine if apt-get should be called with `sudo`, which is often not the case with containers + if which sudo >/dev/null 2>&1; then + sudo -n apt-get -o Acquire::Retries=${{ inputs.apt-get-retries }} update > /dev/null 2>&1 + if [ $? -eq 0 ] + then + sudo_prefix="sudo " + else + sudo_prefix="" + fi + else + sudo_prefix="" + fi + + # Install sources + SOURCE_KEYS=(${{ inputs.apt-get-source-keys }}) + for key in "${SOURCE_KEYS[@]}" + do + for i in {1..$NET_RETRY_COUNT} + do + wget -O - "$key" | sudo apt-key add - && break || sleep 2 + done + done + + SOURCES=(${{ inputs.apt-get-sources }}) + if [ ${#SOURCES[@]} -gt 0 ] + then + APT_ADD_REPO_COMMON_ARGS=("-y") + APT_ADD_REPO_HAS_SOURCE_ARGS=0 + SOFTWARE_PROPERTIES_VERSION="$(dpkg-query --showformat='${Version}' --show software-properties-common)" + if dpkg --compare-versions "$SOFTWARE_PROPERTIES_VERSION" ge "0.96.24.20" + then + APT_ADD_REPO_COMMON_ARGS+=("-n") + fi + if dpkg --compare-versions "$SOFTWARE_PROPERTIES_VERSION" ge "0.98.10" + then + APT_ADD_REPO_HAS_SOURCE_ARGS=1 + fi + for source in "${SOURCES[@]}" + do + for i in {1..$NET_RETRY_COUNT} + do + APT_ADD_REPO_ARGS=("${APT_ADD_REPO_COMMON_ARGS[@]}") + if [ $APT_ADD_REPO_HAS_SOURCE_ARGS -ne 0 ] + then + case "$source" in + "ppa:"*) + APT_ADD_REPO_ARGS+=("-P") + ;; + "deb "*) + APT_ADD_REPO_ARGS+=("-S") + ;; + *) + APT_ADD_REPO_ARGS+=("-U") + ;; + esac + fi + APT_ADD_REPO_ARGS+=("$source") + $sudo_prefix -E apt-add-repository "${APT_ADD_REPO_ARGS[@]}" && break || sleep 2 + done + done + fi + + # Update and install + $sudo_prefix apt-get -o Acquire::Retries=${{ inputs.apt-get-retries }} update + if [ "${{ inputs.apt-get-ignore-missing }}" == "true" ]; then + apt_get_packages="${{ inputs.apt-get }}" + for package in ${apt_get_packages// / } + do + $sudo_prefix apt-get -o Acquire::Retries=${{ inputs.apt-get-retries }} install --ignore-missing -y $package || true + done + else + $sudo_prefix apt-get -o Acquire::Retries=${{ inputs.apt-get-retries }} install -y ${{ inputs.apt-get }} + fi + + - name: vcpkg environment + id: ctx + if: ${{ inputs.vcpkg }} + shell: bash + run: | + set -xe + + # vcpkg hash + vcpkg_hash="$(git ls-remote https://github.com/microsoft/vcpkg.git ${{ inputs.vcpkg_branch }} | awk '{ print $1 }')" + echo "vcpkg_hash=$vcpkg_hash" >> $GITHUB_OUTPUT + + # vcpkg triplet + default_triplet="${{ (runner.os == 'Windows' && 'x64-windows') || (runner.os == 'Linux' && 'x64-linux') || (runner.os == 'macOS' && 'x64-osx') || '' }}" + input_triplet=${{ inputs.vcpkg_triplet }} + if [ "$input_triplet" == "" ]; then + triplet=$default_triplet + else + triplet=$input_triplet + fi + echo "triplet=$triplet" >> $GITHUB_OUTPUT + if [ "$triplet" == "" ]; then + triplet_suffix="" + else + triplet_suffix=":$triplet" + fi + echo "triplet_suffix=$triplet_suffix" >> $GITHUB_OUTPUT + + # vcpkg executable + vcpkg_target_dir=${{ inputs.vcpkg_dir }} + if [[ $vcpkg_target_dir == /* ]]; then + vcpkg_exec_path=$vcpkg_target_dir + else + vcpkg_exec_path=./$vcpkg_target_dir + fi + vcpkg_bs_exe="${{ (runner.os == 'Windows' && '$vcpkg_exec_path/bootstrap-vcpkg.bat') || '$vcpkg_exec_path/bootstrap-vcpkg.sh' }}" + echo "vcpkg_bs_exe=$vcpkg_bs_exe" >> $GITHUB_OUTPUT + + # vcpkg toolchain + vcpkg_toolchain=$vcpkg_exec_path/scripts/buildsystems/vcpkg.cmake + echo "vcpkg_toolchain=$vcpkg_toolchain" >> $GITHUB_OUTPUT + + # vcpkg cache hash + vcpkg_cache_hash="${{ runner.os }}-$vcpkg_hash$triplet_suffix" + vcpkg_packages=${{ inputs.vcpkg }} + for package in ${vcpkg_packages// / } + do + vcpkg_cache_hash=$vcpkg_cache_hash-$package + done + echo "vcpkg_cache_hash=$vcpkg_cache_hash" >> $GITHUB_OUTPUT + + # Attempt to get vcpkg with its packages from the cache before cloning it + # The cache key includes the vcpkg version, os, packages and triplet + - name: vcpkg cache + if: ${{ inputs.vcpkg }} + id: cache-vcpkg + uses: actions/cache@v3 + with: + path: ${{ inputs.vcpkg_dir }} + key: ${{ steps.ctx.outputs.vcpkg_cache_hash }} + + - name: vcpkg install + if: steps.cache-vcpkg.outputs.cache-hit != 'true' && inputs.vcpkg != '' + shell: bash + run: | + set -xe + git clone https://github.com/microsoft/vcpkg.git -b ${{ inputs.vcpkg_branch }} ${{ inputs.vcpkg_dir }} + ${{ steps.ctx.outputs.vcpkg_bs_exe }} + cd ${{ inputs.vcpkg_dir }} + packages=${{ inputs.vcpkg }} + for package in ${packages// / } + do + vcpkg install $package${{ steps.ctx.outputs.triplet_suffix }} + done \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 747beafe..241a5a96 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,7 +26,7 @@ env: DEFAULT_BUILD_VARIANT: debug,release jobs: - posix: + build: defaults: run: shell: bash @@ -35,482 +35,160 @@ jobs: fail-fast: false matrix: include: - # Linux, gcc - - toolset: gcc-4.8 - cxxstd: "11" - os: ubuntu-22.04 - container: ubuntu:18.04 - install: - - g++-4.8 - - toolset: gcc-4.9 - cxxstd: "11" - os: ubuntu-22.04 - container: ubuntu:16.04 - install: - - g++-4.9 - - toolset: gcc-5 - cxxstd: "11,14" - os: ubuntu-22.04 - container: ubuntu:16.04 - install: - - g++-5 - - toolset: gcc-6 - cxxstd: "11,14" - os: ubuntu-22.04 - container: ubuntu:18.04 - install: - - g++-6 - - toolset: gcc-7 - cxxstd: "14,17" - os: ubuntu-22.04 - container: ubuntu:18.04 - install: - - g++-7 - - toolset: gcc-8 - cxxstd: "17" - os: ubuntu-22.04 - container: ubuntu:18.04 - install: - - g++-8 - - toolset: gcc-9 - cxxstd: "17" - os: ubuntu-22.04 - install: - - g++-9 - - toolset: gcc-10 - cxxstd: "17" - os: ubuntu-22.04 - install: - - g++-10 - - toolset: gcc-11 - cxxstd: "17,20" - os: ubuntu-22.04 - install: - - g++-11 - sources: - - "ppa:ubuntu-toolchain-r/test" - - name: UBSAN - toolset: gcc-11 - cxxstd: "17,20" - ubsan: 1 - os: ubuntu-22.04 - install: - - g++-11 - sources: - - "ppa:ubuntu-toolchain-r/test" + # Latest + - { name: 'GCC 11 (Latest): C++17-20', toolset: gcc-11, cxx: g++-11, cc: gcc-11, cxxstd: "17,20", os: ubuntu-22.04, install: [ g++-11 ], sources: [ "ppa:ubuntu-toolchain-r/test" ] } + - { name: 'Clang 12 (Latest): C++17-20', toolset: clang, cxx: clang++-12, cc: clang-12, cxxstd: "17,20", os: ubuntu-22.04, install: [ clang-12 ] } + - { name: 'MSVC 14.3 (Latest): C++17-20', toolset: msvc-14.3, cxxstd: "17,20", address-model: '32,64', os: windows-2022 } + - { name: 'Clang 12 + libc++ (Latest): C++17-20', toolset: clang, cxx: clang++-12, cc: clang-12, cxxstd: "17,20", cxxflags: -stdlib=libc++, linkflags: -stdlib=libc++, os: ubuntu-20.04, install: [ clang-12, libc++-12-dev, libc++abi-12-dev ] } + - { name: 'AppleClang (Latest): C++11-17', toolset: clang, cxxstd: "11,14,17", os: macos-11 } - # Linux, clang - - toolset: clang - compiler: clang++-3.8 - cxxstd: "11" - os: ubuntu-22.04 - container: ubuntu:16.04 - install: - - clang-3.8 - - toolset: clang - compiler: clang++-4.0 - cxxstd: "11,14" - os: ubuntu-22.04 - container: ubuntu:18.04 - install: - - clang-4.0 - - toolset: clang - compiler: clang++-5.0 - cxxstd: "11,14" - os: ubuntu-22.04 - container: ubuntu:18.04 - install: - - clang-5.0 - - toolset: clang - compiler: clang++-6.0 - cxxstd: "14,17" - os: ubuntu-22.04 - container: ubuntu:18.04 - install: - - clang-6.0 - - toolset: clang - compiler: clang++-7 - cxxstd: "17" - os: ubuntu-22.04 - container: ubuntu:18.04 - install: - - clang-7 - - toolset: clang - compiler: clang++-8 - cxxstd: "17" - os: ubuntu-22.04 - container: ubuntu:18.04 - install: - - clang-8 - - g++-7 - gcc_toolchain: 7 - - toolset: clang - compiler: clang++-9 - cxxstd: "14,17" - os: ubuntu-22.04 - container: ubuntu:18.04 - install: - - clang-9 - - toolset: clang - compiler: clang++-10 - cxxstd: "14,17" - os: ubuntu-20.04 - install: - - clang-10 - - toolset: clang - compiler: clang++-11 - cxxstd: "14,17" - os: ubuntu-20.04 - install: - - clang-11 - - toolset: clang - compiler: clang++-12 - cxxstd: "17,20" - os: ubuntu-22.04 - install: - - clang-12 - - toolset: clang - compiler: clang++-12 - cxxstd: "17,20" - cxxflags: -stdlib=libc++ - linkflags: -stdlib=libc++ - os: ubuntu-20.04 - install: - - clang-12 - - libc++-12-dev - - libc++abi-12-dev + # Oldest + - { name: 'GCC 4.8 (Oldest): C++11', toolset: gcc-4.8, cxx: g++-4.8, cc: gcc-4.8, cxxstd: "11", os: ubuntu-22.04, container: 'ubuntu:18.04', install: [ g++-4.8 ] } + - { name: 'Clang 3.8 (Oldest): C++11', toolset: clang, cxx: clang++-3.8, cc: clang-3.8, cxxstd: "11", os: ubuntu-22.04, container: 'ubuntu:16.04', install: [ clang-3.8 ] } + - { name: 'MSVC 14.2 (Oldest): C++14-17', toolset: msvc-14.2, cxxstd: "14,17", address-model: '32,64', os: windows-2019 } - - toolset: clang - cxxstd: "11,14,17" - os: macos-11 + # Special + - { name: 'UBSan (GCC 11: C++17-20)', toolset: gcc-11, cxx: g++-11, cc: gcc-11, cxxstd: "17,20", ubsan: true, os: ubuntu-22.04, install: [ g++-11 ], sources: [ "ppa:ubuntu-toolchain-r/test" ] } + - { name: 'Shared (GCC)', generator: 'Unix Makefiles', os: ubuntu-22.04, build_shared: true, build_type: Debug, cmake: true } + - { name: 'Shared (VS 2019)', toolset: msvc-14.2, generator: 'Visual Studio 16 2019', address-model: '32,64', cxxstd: "17,20", os: windows-2019, build_shared: true, build_type: Debug } + - { name: 'Shared (VS 2022)', toolset: msvc-14.3, generator: 'Visual Studio 17 2022', address-model: '32,64', cxxstd: "17,20", os: windows-2022, build_shared: true, build_type: Debug } - - name: CMake tests - cmake_tests: 1 - os: ubuntu-22.04 + # GCC + - { name: 'GCC 10: C++17', toolset: gcc-10, cxx: g++-10, cc: gcc-10, cxxstd: "17", os: ubuntu-22.04, install: [ g++-10 ] } + - { name: 'GCC 9: C++17', toolset: gcc-9, cxx: g++-9, cc: gcc-9, cxxstd: "17", os: ubuntu-22.04, install: [ g++-9 ] } + - { name: 'GCC 8: C++17', toolset: gcc-8, cxx: g++-8, cc: gcc-8, cxxstd: "17", os: ubuntu-22.04, container: 'ubuntu:18.04', install: [ g++-8 ] } + - { name: 'GCC 7: C++14-17', toolset: gcc-7, cxx: g++-7, cc: gcc-7, cxxstd: "14,17", os: ubuntu-22.04, container: 'ubuntu:18.04', install: [ g++-7 ] } + - { name: 'GCC 6: C++11-14', toolset: gcc-6, cxx: g++-6, cc: gcc-6, cxxstd: "11,14", os: ubuntu-22.04, container: 'ubuntu:18.04', install: [ g++-6 ] } + - { name: 'GCC 5: C++11-14', toolset: gcc-5, cxx: g++-5, cc: gcc-5, cxxstd: "11,14", os: ubuntu-22.04, container: 'ubuntu:16.04', install: [ g++-5 ] } + - { name: 'GCC 4.9: C++11', toolset: gcc-4.9, cxx: g++-4.9, cc: gcc-4.9, cxxstd: "11", os: ubuntu-22.04, container: 'ubuntu:16.04', install: [ g++-4.9 ] } + # Clang + - { name: 'Clang 11: C++14-17', toolset: clang, cxx: clang++-11, cc: clang-11, cxxstd: "14,17", os: ubuntu-20.04, install: [ clang-11 ] } + - { name: 'Clang 10: C++14-17', toolset: clang, cxx: clang++-10, cc: clang-10, cxxstd: "14,17", os: ubuntu-20.04, install: [ clang-10 ] } + - { name: 'Clang 9: C++14-17', toolset: clang, cxx: clang++-9, cc: clang-9, cxxstd: "14,17", os: ubuntu-22.04, container: 'ubuntu:18.04', install: [ clang-9 ] } + - { name: 'Clang 8: C++17', toolset: clang, cxx: clang++-8, cc: clang-8, cxxstd: "17", os: ubuntu-22.04, container: 'ubuntu:18.04', install: [ clang-8, g++-7 ], gcc_toolchain: 7 } + - { name: 'Clang 7: C++17', toolset: clang, cxx: clang++-7, cc: clang-7, cxxstd: "17", os: ubuntu-22.04, container: 'ubuntu:18.04', install: [ clang-7 ] } + - { name: 'Clang 6.0: C++14-17', toolset: clang, cxx: clang++-6.0, cc: clang-6.0, cxxstd: "14,17", os: ubuntu-22.04, container: 'ubuntu:18.04', install: [ clang-6.0 ] } + - { name: 'Clang 5.0: C++11-14', toolset: clang, cxx: clang++-5.0, cc: clang-5.0, cxxstd: "11,14", os: ubuntu-22.04, container: 'ubuntu:18.04', install: [ clang-5.0 ] } + - { name: 'Clang 4.0: C++11-14', toolset: clang, cxx: clang++-4.0, cc: clang-4.0, cxxstd: "11,14", os: ubuntu-22.04, container: 'ubuntu:18.04', install: [ clang-4.0 ] } + + name: ${{ matrix.name }} timeout-minutes: 120 runs-on: ${{matrix.os}} container: ${{matrix.container}} steps: - - name: Setup environment - run: | - if [ -f "/etc/debian_version" ] - then - echo "DEBIAN_FRONTEND=noninteractive" >> $GITHUB_ENV - export DEBIAN_FRONTEND=noninteractive - fi - if [ -n "${{matrix.container}}" ] - then - echo "GHA_CONTAINER=${{matrix.container}}" >> $GITHUB_ENV - if [ -f "/etc/debian_version" ] - then - apt-get -o Acquire::Retries=$NET_RETRY_COUNT update - apt-get -o Acquire::Retries=$NET_RETRY_COUNT install -y sudo software-properties-common tzdata wget curl apt-transport-https make apt-file unzip libssl-dev build-essential autotools-dev autoconf automake g++ libc++-helpers python ruby cpio gcc-multilib g++-multilib pkgconf python3 ccache libpython-dev - - # install pip: - python_version=$(python3 -c 'import sys; print("{0.major}.{0.minor}".format(sys.version_info))') - if [[ ${python_version} =~ ^3\.[0-5]$ ]]; then - true - else - apt-get install -y python3-distutils - fi - wget https://bootstrap.pypa.io/pip/$python_version/get-pip.py - python3 get-pip.py - - # install git: - # apt-get install -y git - apt-add-repository ppa:git-core/ppa - apt-get -o Acquire::Retries=$NET_RETRY_COUNT update && apt-get -o Acquire::Retries=$NET_RETRY_COUNT install -y git - - # install cmake: - # apt-get -o Acquire::Retries=$NET_RETRY_COUNT install -y cmake - sudo pip3 install cmake - fi - fi - git config --global pack.threads 0 - - - uses: actions/checkout@v3 + - name: Clone Boost.URL + uses: actions/checkout@v3 - name: Install packages - if: matrix.install - run: | - SOURCE_KEYS=(${{join(matrix.source_keys, ' ')}}) - SOURCES=(${{join(matrix.sources, ' ')}}) - for key in "${SOURCE_KEYS[@]}" - do - for i in {1..$NET_RETRY_COUNT} - do - wget -O - "$key" | sudo apt-key add - && break || sleep 2 - done - done - if [ ${#SOURCES[@]} -gt 0 ] - then - APT_ADD_REPO_COMMON_ARGS=("-y") - APT_ADD_REPO_HAS_SOURCE_ARGS=0 - SOFTWARE_PROPERTIES_VERSION="$(dpkg-query --showformat='${Version}' --show software-properties-common)" - if dpkg --compare-versions "$SOFTWARE_PROPERTIES_VERSION" ge "0.96.24.20" - then - APT_ADD_REPO_COMMON_ARGS+=("-n") - fi - if dpkg --compare-versions "$SOFTWARE_PROPERTIES_VERSION" ge "0.98.10" - then - APT_ADD_REPO_HAS_SOURCE_ARGS=1 - fi - for source in "${SOURCES[@]}" - do - for i in {1..$NET_RETRY_COUNT} - do - APT_ADD_REPO_ARGS=("${APT_ADD_REPO_COMMON_ARGS[@]}") - if [ $APT_ADD_REPO_HAS_SOURCE_ARGS -ne 0 ] - then - case "$source" in - "ppa:"*) - APT_ADD_REPO_ARGS+=("-P") - ;; - "deb "*) - APT_ADD_REPO_ARGS+=("-S") - ;; - *) - APT_ADD_REPO_ARGS+=("-U") - ;; - esac - fi - APT_ADD_REPO_ARGS+=("$source") - sudo -E apt-add-repository "${APT_ADD_REPO_ARGS[@]}" && break || sleep 2 - done - done - fi - sudo apt-get -o Acquire::Retries=$NET_RETRY_COUNT update - sudo apt-get -o Acquire::Retries=$NET_RETRY_COUNT install -y ${{join(matrix.install, ' ')}} - - - name: Setup GCC Toolchain - if: matrix.gcc_toolchain - run: | - GCC_TOOLCHAIN_ROOT="$HOME/gcc-toolchain" - echo "GCC_TOOLCHAIN_ROOT=\"$GCC_TOOLCHAIN_ROOT\"" >> $GITHUB_ENV - MULTIARCH_TRIPLET="$(dpkg-architecture -qDEB_HOST_MULTIARCH)" - mkdir -p "$GCC_TOOLCHAIN_ROOT" - ln -s /usr/include "$GCC_TOOLCHAIN_ROOT/include" - ln -s /usr/bin "$GCC_TOOLCHAIN_ROOT/bin" - mkdir -p "$GCC_TOOLCHAIN_ROOT/lib/gcc/$MULTIARCH_TRIPLET" - ln -s "/usr/lib/gcc/$MULTIARCH_TRIPLET/${{matrix.gcc_toolchain}}" "$GCC_TOOLCHAIN_ROOT/lib/gcc/$MULTIARCH_TRIPLET/${{matrix.gcc_toolchain}}" - - - name: Setup Boost - run: | - echo GITHUB_REPOSITORY: $GITHUB_REPOSITORY - LIBRARY=${GITHUB_REPOSITORY#*/} - echo LIBRARY: $LIBRARY - echo "LIBRARY=$LIBRARY" >> $GITHUB_ENV - echo GITHUB_BASE_REF: $GITHUB_BASE_REF - echo GITHUB_REF: $GITHUB_REF - REF=${GITHUB_BASE_REF:-$GITHUB_REF} - REF=${REF#refs/heads/} - echo REF: $REF - BOOST_BRANCH=develop && [ "$REF" == "master" ] && BOOST_BRANCH=master || true - echo BOOST_BRANCH: $BOOST_BRANCH - BUILD_JOBS=$((nproc || sysctl -n hw.ncpu) 2> /dev/null) - echo "BUILD_JOBS=$BUILD_JOBS" >> $GITHUB_ENV - echo "CMAKE_BUILD_PARALLEL_LEVEL=$BUILD_JOBS" >> $GITHUB_ENV - DEPINST_ARGS=() - GIT_VERSION="$(git --version | sed -e 's/git version //')" - if $(dpkg --compare-versions "$GIT_VERSION" ge 2.8.0) - then - DEPINST_ARGS+=("--git_args" "--jobs $GIT_FETCH_JOBS") - fi - cd .. - git clone -b "$BOOST_BRANCH" --depth 1 "https://github.com/boostorg/boost.git" "boost-root" - - cd boost-root - # mkdir -p libs/$LIBRARY - # cp -r $GITHUB_WORKSPACE/* libs/$LIBRARY - # git submodule update --init tools/boostdep - git submodule update --init --recursive - if [ ! -d "libs/$LIBRARY" ]; then - mkdir -p libs/$LIBRARY - fi - find libs/$LIBRARY -mindepth 1 -delete - cp -r $GITHUB_WORKSPACE/* libs/$LIBRARY - - DEPINST_ARGS+=("$LIBRARY") - python tools/boostdep/depinst/depinst.py "${DEPINST_ARGS[@]}" - if [ -z "${{matrix.cmake_tests}}" ] - then - ./bootstrap.sh - ./b2 headers - if [ -n "${{matrix.compiler}}" -o -n "$GCC_TOOLCHAIN_ROOT" ] - then - echo -n "using ${{matrix.toolset}} : : ${{matrix.compiler}}" > ~/user-config.jam - if [ -n "$GCC_TOOLCHAIN_ROOT" ] - then - echo -n " : \"--gcc-toolchain=$GCC_TOOLCHAIN_ROOT\" \"--gcc-toolchain=$GCC_TOOLCHAIN_ROOT\"" >> ~/user-config.jam - fi - echo " ;" >> ~/user-config.jam - fi - fi - - - name: Run tests - if: matrix.cmake_tests == '' - run: | - cd ../boost-root - B2_ARGS=("-j" "$BUILD_JOBS" "toolset=${{matrix.toolset}}" "cxxstd=${{matrix.cxxstd}}") - if [ -n "${{matrix.build_variant}}" ] - then - B2_ARGS+=("variant=${{matrix.build_variant}}") - else - B2_ARGS+=("variant=$DEFAULT_BUILD_VARIANT") - fi - if [ -n "${{matrix.threading}}" ] - then - B2_ARGS+=("threading=${{matrix.threading}}") - fi - if [ -n "${{matrix.ubsan}}" ] - then - export UBSAN_OPTIONS="print_stacktrace=1" - B2_ARGS+=("cxxflags=-fsanitize=undefined -fno-sanitize-recover=undefined" "linkflags=-fsanitize=undefined -fuse-ld=gold" "define=UBSAN=1" "debug-symbols=on" "visibility=global") - fi - if [ -n "${{matrix.cxxflags}}" ] - then - B2_ARGS+=("cxxflags=${{matrix.cxxflags}}") - fi - if [ -n "${{matrix.linkflags}}" ] - then - B2_ARGS+=("linkflags=${{matrix.linkflags}}") - fi - B2_ARGS+=("libs/$LIBRARY/test") - ./b2 "${B2_ARGS[@]}" - - - name: Run CMake tests - if: matrix.cmake_tests != '' - run: | - cd ../boost-root - git submodule update --init --recursive - cd libs/$LIBRARY - mkdir __build__ && cd __build__ - cmake -DCMAKE_INSTALL_PREFIX=~/.local .. - cmake --build . --target install - - windows: - strategy: - fail-fast: false - matrix: - include: - #- toolset: msvc-14.1 - # cxxstd: "14,17,latest" - # addrmd: 32,64 - # os: windows-2016 - - toolset: msvc-14.2 - cxxstd: "14,17" - addrmd: 32,64 - os: windows-2019 - - toolset: msvc-14.3 - cxxstd: "17,20" - addrmd: 32,64 - os: windows-2022 -# - toolset: gcc -# cxxstd: "11,14,17" -# addrmd: 64 -# os: windows-2019 - - runs-on: ${{matrix.os}} - - steps: - - uses: actions/checkout@v3 - - - name: Setup Boost - shell: cmd - run: | - echo GITHUB_REPOSITORY: %GITHUB_REPOSITORY% - for /f %%i in ("%GITHUB_REPOSITORY%") do set LIBRARY=%%~nxi - echo LIBRARY: %LIBRARY% - echo LIBRARY=%LIBRARY%>>%GITHUB_ENV% - echo GITHUB_BASE_REF: %GITHUB_BASE_REF% - echo GITHUB_REF: %GITHUB_REF% - if "%GITHUB_BASE_REF%" == "" set GITHUB_BASE_REF=%GITHUB_REF% - set BOOST_BRANCH=develop - for /f %%i in ("%GITHUB_BASE_REF%") do if "%%~nxi" == "master" set BOOST_BRANCH=master - echo BOOST_BRANCH: %BOOST_BRANCH% - cd .. - git clone -b %BOOST_BRANCH% --depth 1 https://github.com/boostorg/boost.git boost-root - cd boost-root - xcopy /s /e /q %GITHUB_WORKSPACE% libs\%LIBRARY%\ - git submodule update --init tools/boostdep - python tools/boostdep/depinst/depinst.py --git_args "--jobs %GIT_FETCH_JOBS%" %LIBRARY% - cmd /c bootstrap - b2 -d0 headers - - - name: Run tests - shell: cmd - run: | - cd ../boost-root - b2 -j %NUMBER_OF_PROCESSORS% libs/%LIBRARY%/test toolset=${{matrix.toolset}} cxxstd=${{matrix.cxxstd}} address-model=${{matrix.addrmd}} variant=debug,release - - CMake: - defaults: - run: - shell: bash - - strategy: - fail-fast: false - matrix: - include: - - { os: ubuntu-22.04, build_shared: ON, build_type: Debug, generator: 'Unix Makefiles' } - - { os: ubuntu-22.04, build_shared: OFF, build_type: Debug, generator: 'Unix Makefiles' } - - { os: windows-2019, build_shared: ON, build_type: Debug, generator: 'Visual Studio 16 2019' } - - { os: windows-2019, build_shared: OFF, build_type: Debug, generator: 'Visual Studio 16 2019' } - - { os: windows-2022, build_shared: ON, build_type: Debug, generator: 'Visual Studio 17 2022' } - - { os: windows-2022, build_shared: OFF, build_type: Debug, generator: 'Visual Studio 17 2022' } - - timeout-minutes: 120 - runs-on: ${{matrix.os}} - - steps: - - uses: actions/checkout@v3 - - name: Fetch Boost.CI - uses: actions/checkout@v3 + if: ${{ !startsWith(matrix.os, 'windows') && (matrix.container || matrix.install) }} + uses: ./.github/actions/package_install + id: package-install with: - repository: boostorg/boost-ci - ref: master - path: boost-ci-cloned + apt-get: ${{ join(matrix.install, ' ') }} ${{ matrix.container && 'sudo software-properties-common tzdata wget curl apt-transport-https make apt-file unzip libssl-dev build-essential autotools-dev autoconf automake g++ libc++-helpers python ruby cpio gcc-multilib g++-multilib pkgconf python3 ccache libpython-dev python3-distutils python3-pip git cmake' }} + apt-get-ignore-missing: ${{ matrix.container && 'true' }} - - name: Get CI scripts folder - run: | - # Copy ci folder if not testing Boost.CI - [[ "$GITHUB_REPOSITORY" =~ "boost-ci" ]] || cp -r boost-ci-cloned/ci . - rm -rf boost-ci-cloned - - name: Setup Boost - env: {B2_DONT_BOOTSTRAP: 1} - run: source ci/github/install.sh + - name: Clone Boost + uses: ./.github/actions/boost_clone + with: + boost_dir: ../boost-source + branch: ${{ (github.ref_name == 'master' && github.ref_name) || 'develop' }} + scan-modules-dir: . + scan-modules-ignore: url - - name: Run CMake tests + - name: Patch Boost + working-directory: ../boost-source + id: patch + shell: bash run: | - cd "$BOOST_ROOT" - mkdir __build_cmake_test__ && cd __build_cmake_test__ - cmake -G "${{matrix.generator}}" -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DBOOST_INCLUDE_LIBRARIES=$SELF -DBUILD_SHARED_LIBS=${{matrix.build_shared}} -DBUILD_TESTING=ON -DBoost_VERBOSE=ON .. - cmake --build . --target tests boost_url_tests boost_url_limits boost_url_extra --config ${{matrix.build_type}} - ctest --output-on-failure --build-config ${{matrix.build_type}} - - name: Run CMake subdir tests - run: | - cmake_test_folder="$BOOST_ROOT/libs/$SELF/test/cmake_test" # New unified folder - [ -d "$cmake_test_folder" ] || cmake_test_folder="$BOOST_ROOT/libs/$SELF/test/cmake_subdir_test" - cd "$cmake_test_folder" - mkdir __build_cmake_subdir_test__ && cd __build_cmake_subdir_test__ - cmake -G "${{matrix.generator}}" -DBOOST_CI_INSTALL_TEST=OFF -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DBUILD_SHARED_LIBS=${{matrix.build_shared}} .. - cmake --build . --config ${{matrix.build_type}} - ctest --output-on-failure --build-config ${{matrix.build_type}} - - name: Install Library - run: | - cd "$BOOST_ROOT" - mkdir __build_cmake_install_test__ && cd __build_cmake_install_test__ - cmake -G "${{matrix.generator}}" -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DBOOST_INCLUDE_LIBRARIES=$SELF -DBUILD_SHARED_LIBS=${{matrix.build_shared}} -DCMAKE_INSTALL_PREFIX=~/.local -DBoost_VERBOSE=ON -DBoost_DEBUG=ON .. - cmake --build . --target install --config ${{matrix.build_type}} - - name: Run CMake install tests - run: | - cmake_test_folder="$BOOST_ROOT/libs/$SELF/test/cmake_test" # New unified folder - [ -d "$cmake_test_folder" ] || cmake_test_folder="$BOOST_ROOT/libs/$SELF/test/cmake_install_test" - cd "$cmake_test_folder" - mkdir __build_cmake_install_test__ && cd __build_cmake_install_test__ - cmake -G "${{matrix.generator}}" -DBOOST_CI_INSTALL_TEST=ON -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DBUILD_SHARED_LIBS=${{matrix.build_shared}} -DCMAKE_PREFIX_PATH=~/.local .. - cmake --build . --config ${{matrix.build_type}} - ctest --output-on-failure --build-config ${{matrix.build_type}} + set -xe + module=${GITHUB_REPOSITORY#*/} + echo "module=$module" >> $GITHUB_OUTPUT + workspace_root=$(echo "$GITHUB_WORKSPACE" | sed 's/\\/\//g') + rm -r "libs/$module" || true + cd .. + mkdir boost-root || true + cp -r "boost-source"/* "boost-root" + cd boost-root + mkdir "libs/$module" + cp -r "$workspace_root"/* "libs/$module" + cxxstd=${{ matrix.cxxstd }} + latest_std=$(echo $cxxstd | awk -F ',' '{print $NF}') + echo "latest_std=$latest_std" >> $GITHUB_OUTPUT + - name: Boost Workflow + uses: ./.github/actions/cmake_workflow + with: + source-dir: ../boost-root + build-dir: __build_cmake_test__ + generator: ${{ matrix.generator }} + build-type: ${{ matrix.build_type || 'Debug' }} + build-target: tests boost_url_tests boost_url_limits boost_url_extra + run-tests: true + install-prefix: $GITHUB_WORKSPACE/.local + cxxstd: ${{ steps.patch.outputs.latest_std }} + cxx: ${{ matrix.cxx }} + cc: ${{ matrix.cc }} + cmake-min-version: 3.15 # min-version with cmake --install + extra-args: ${{ format('-D Boost_VERBOSE=ON -D BOOST_INCLUDE_LIBRARIES={0} -D BUILD_SHARED_LIBS={1}', steps.patch.outputs.module, (matrix.build_shared && 'ON') || 'OFF') }} + ref-source-dir: ../boost-root/libs/url + + - name: Subdir Workflow + uses: ./.github/actions/cmake_workflow + with: + source-dir: ../boost-root/libs/${{ steps.patch.outputs.module }}/test/cmake_test + build-dir: __build_cmake_subdir_test__ + generator: ${{ matrix.generator }} + build-type: ${{ matrix.build_type || 'Debug' }} + cxxstd: ${{ steps.patch.outputs.latest_std }} + cxx: ${{ matrix.cxx }} + cc: ${{ matrix.cc }} + install: false + cmake-min-version: 3.11 + extra-args: ${{ format('-D BOOST_CI_INSTALL_TEST=OFF -D BUILD_SHARED_LIBS={0}', (matrix.build_shared && 'ON') || 'OFF') }} + ref-source-dir: ../boost-root/libs/url/test/cmake_test + + - name: Package Workflow + uses: ./.github/actions/cmake_workflow + with: + source-dir: ../boost-root/libs/${{ steps.patch.outputs.module }}/test/cmake_test + build-dir: __build_cmake_install_test__ + generator: ${{ matrix.generator }} + build-type: ${{ matrix.build_type || 'Debug' }} + cxxstd: ${{ steps.patch.outputs.latest_std }} + cxx: ${{ matrix.cxx }} + cc: ${{ matrix.cc }} + install: false + extra-args: ${{ format('-D BOOST_CI_INSTALL_TEST=ON -D CMAKE_PREFIX_PATH=$GITHUB_WORKSPACE/.local -D BUILD_SHARED_LIBS={0}', (matrix.build_shared && 'ON') || 'OFF') }} + ref-source-dir: ../boost-root/libs/url/test/cmake_test + + - name: Root Workflow + uses: ./.github/actions/cmake_workflow + with: + source-dir: . + build-dir: __build_root_test__ + generator: ${{ matrix.generator }} + build-type: ${{ matrix.build_type || 'Debug' }} + build-target: tests boost_url_tests boost_url_limits boost_url_extra + run-tests: false + install: false + cxxstd: ${{ steps.patch.outputs.latest_std }} + cxx: ${{ matrix.cxx }} + cc: ${{ matrix.cc }} + extra-args: ${{ format('-D Boost_VERBOSE=ON -D BUILD_TESTING=ON -D BUILD_SHARED_LIBS={0} -D BOOST_SRC_DIR="../boost-root"', (matrix.build_shared && 'ON') || 'OFF') }} + ref-source-dir: ../boost-root/libs/url + + - name: Release Workflow + uses: ./.github/actions/b2_workflow + with: + source-dir: ../boost-root + modules: url + toolset: ${{ matrix.toolset }} + cxx: ${{ (startsWith(matrix.cxx, 'clang') && matrix.cxx) || '' }} + cxxstd: ${{ matrix.cxxstd }} + cxxflags: ${{ matrix.cxxflags }} + linkflags: ${{ matrix.linkflags }} + address-model: ${{ matrix.address-model }} + ubsan: ${{ matrix.ubsan }} + gcc_toolchain: ${{ matrix.gcc_toolchain }} diff --git a/CMakeLists.txt b/CMakeLists.txt index bd5951e3..c1ad16ca 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,149 +8,125 @@ # Official repository: https://github.com/boostorg/url # +####################################################### +### Project ### +####################################################### cmake_minimum_required(VERSION 3.8...3.16) - set(BOOST_URL_VERSION 2) -if(BOOST_SUPERPROJECT_VERSION) - set(BOOST_URL_VERSION ${BOOST_SUPERPROJECT_VERSION}) -endif() - -project(boost_url VERSION "${BOOST_URL_VERSION}" LANGUAGES CXX) - - -set(BOOST_URL_IS_ROOT OFF) -if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) - set(BOOST_URL_IS_ROOT ON) -endif() - -if(BOOST_URL_IS_ROOT) - include(CTest) -endif() - -if(NOT BOOST_SUPERPROJECT_VERSION) - option(BOOST_URL_INSTALL "Install boost::url files" ${BOOST_URL_IS_ROOT}) - option(BOOST_URL_BUILD_TESTS "Build boost::url tests" ${BUILD_TESTING}) - option(BOOST_URL_BUILD_FUZZERS "Build boost::json fuzzers" ${BOOST_URL_BUILD_TESTS}) - option(BOOST_URL_BUILD_EXAMPLES "Build boost::url examples" ${BOOST_URL_IS_ROOT}) -else() - set(BOOST_URL_BUILD_TESTS ${BUILD_TESTING}) -endif() - if (BOOST_SUPERPROJECT_VERSION) - set(BOOST_URL_FIND_PACKAGE_BOOST OFF) -elseif (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/../../CMakeLists.txt" AND - EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/../../Jamroot" AND - EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/../../boost-build.jam" AND - EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/../../bootstrap.sh" AND - EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/../../libs") - set(BOOST_URL_FIND_PACKAGE_BOOST OFF) -else() - set(BOOST_URL_FIND_PACKAGE_BOOST ON) -endif() + set(BOOST_URL_VERSION ${BOOST_SUPERPROJECT_VERSION}) +endif () +project(boost_url VERSION "${BOOST_URL_VERSION}" LANGUAGES CXX) +set(BOOST_URL_IS_ROOT OFF) +if (CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) + set(BOOST_URL_IS_ROOT ON) +endif () +set(__ignore__ CMAKE_C_COMPILER) -if (BOOST_URL_FIND_PACKAGE_BOOST) - find_package(Boost 1.78.0 REQUIRED COMPONENTS container) -elseif (BOOST_URL_IS_ROOT) - set(BOOST_URL_UNIT_TEST_LIBRARIES container filesystem unordered json regex beast) - set(BOOST_INCLUDE_LIBRARIES url ${BOOST_URL_UNIT_TEST_LIBRARIES}) - set(BOOST_EXCLUDE_LIBRARIES url) +####################################################### +### Options ### +####################################################### +option(BOOST_URL_BUILD_TESTS "Build boost::url tests" ${BUILD_TESTING}) +option(BOOST_URL_BUILD_FUZZERS "Build boost::url fuzzers" OFF) +option(BOOST_URL_BUILD_EXAMPLES "Build boost::url examples" ${BOOST_URL_IS_ROOT}) +set(BOOST_SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../.." CACHE STRING "Boost source dir to use when running CMake from this directory") + +####################################################### +### Boost modules ### +####################################################### +# The boost super-project requires one explicit dependency per-line. +set(BOOST_URL_DEPENDENCIES + Boost::align + Boost::assert + Boost::config + Boost::core + Boost::mp11 + Boost::optional + Boost::static_assert + Boost::system + Boost::throw_exception + Boost::type_traits + Boost::variant2 + ) +foreach (BOOST_URL_DEPENDENCY ${BOOST_URL_DEPENDENCIES}) + if (BOOST_URL_DEPENDENCY MATCHES "^[ ]*Boost::([A-Za-z0-9_]+)[ ]*$") + list(APPEND BOOST_URL_INCLUDE_LIBRARIES ${CMAKE_MATCH_1}) + endif () +endforeach () +set(BOOST_URL_UNIT_TEST_LIBRARIES container filesystem unordered) +set(BOOST_URL_EXAMPLE_LIBRARIES json regex beast) +set(BOOST_INCLUDE_LIBRARIES ${BOOST_URL_INCLUDE_LIBRARIES} ${BOOST_URL_UNIT_TEST_LIBRARIES} ${BOOST_URL_EXAMPLE_LIBRARIES}) +set(BOOST_EXCLUDE_LIBRARIES url) + +####################################################### +### Add Boost Subdirectory ### +####################################################### +if (NOT BOOST_SUPERPROJECT_VERSION) set(CMAKE_FOLDER Dependencies) - add_subdirectory(../.. Dependencies/boost EXCLUDE_FROM_ALL) - unset(CMAKE_FOLDER) -endif() + # Find absolute BOOST_SRC_DIR + if (NOT IS_ABSOLUTE ${BOOST_SRC_DIR}) + set(BOOST_SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/${BOOST_SRC_DIR}") + endif () -include(GNUInstallDirs) + # Validate BOOST_SRC_DIR + set(BOOST_SRC_DIR_IS_VALID ON) + foreach (F "CMakeLists.txt" "Jamroot" "boost-build.jam" "bootstrap.sh" "libs") + if (NOT EXISTS "${BOOST_SRC_DIR}/${F}") + message(STATUS "${BOOST_SRC_DIR}/${F} does not exist. Fallback to find_package.") + set(BOOST_SRC_DIR_IS_VALID OFF) + break() + endif () + endforeach () + + # Create Boost interface targets + if (BOOST_SRC_DIR_IS_VALID) + set(BOOST_EXCLUDE_LIBRARIES ${PROJECT_NAME}) + set(PREV_BUILD_TESTING ${BUILD_TESTING}) + set(BUILD_TESTING OFF CACHE BOOL "Build the tests." FORCE) + add_subdirectory(${BOOST_SRC_DIR} Dependencies/boost EXCLUDE_FROM_ALL) + set(BUILD_TESTING ${PREV_BUILD_TESTING} CACHE BOOL "Build the tests." FORCE) + set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${BOOST_SRC_DIR}/tools/cmake/include") + else () + find_package(Boost REQUIRED COMPONENTS container) + foreach (BOOST_INCLUDE_LIBRARY ${BOOST_INCLUDE_LIBRARIES}) + if (NOT TARGET Boost::${BOOST_INCLUDE_LIBRARY}) + add_library(Boost::${BOOST_INCLUDE_LIBRARY} ALIAS Boost::headers) + endif () + endforeach () + endif () + unset(CMAKE_FOLDER) +endif () + +####################################################### +### Library ### +####################################################### +file(GLOB_RECURSE BOOST_URL_HEADERS CONFIGURE_DEPENDS include/boost/*.hpp include/boost/*.natvis) +file(GLOB_RECURSE BOOST_URL_SOURCES CONFIGURE_DEPENDS src/*.cpp) +set_property(GLOBAL PROPERTY USE_FOLDERS ON) +source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR}/include/boost PREFIX "" FILES ${BOOST_URL_HEADERS}) +source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR}/src PREFIX "url" FILES ${BOOST_URL_SOURCES}) function(boost_url_setup_properties target) target_compile_features(${target} PUBLIC cxx_constexpr) target_compile_definitions(${target} PUBLIC BOOST_URL_NO_LIB=1) - - if(BOOST_SUPERPROJECT_VERSION) - target_include_directories(${target} PUBLIC "${PROJECT_SOURCE_DIR}/include") - else() - target_include_directories(${target} - PUBLIC - "$" - "$" - ) - endif() - - if (BOOST_URL_FIND_PACKAGE_BOOST) - target_link_libraries(${target} - PUBLIC - Boost::headers - ) - else() - target_link_libraries(${target} - PUBLIC - Boost::align - Boost::assert - Boost::config - Boost::core - Boost::mp11 - Boost::optional - Boost::static_assert - Boost::system - Boost::throw_exception - Boost::type_traits - Boost::variant2 - ) - endif() - - if(BUILD_SHARED_LIBS) - target_compile_definitions(${target} PUBLIC BOOST_URL_DYN_LINK=1) - else() - target_compile_definitions(${target} PUBLIC BOOST_URL_STATIC_LINK=1) - endif() + target_include_directories(${target} PUBLIC "${PROJECT_SOURCE_DIR}/include") + target_link_libraries(${target} PUBLIC ${BOOST_URL_DEPENDENCIES}) + target_compile_definitions(${target} PUBLIC $,BOOST_URL_DYN_LINK=1,BOOST_URL_STATIC_LINK=1>) target_compile_definitions(${target} PRIVATE BOOST_URL_SOURCE) endfunction() - - -file(GLOB_RECURSE BOOST_URL_HEADERS CONFIGURE_DEPENDS - include/boost/*.hpp - include/boost/*.ipp - include/boost/*.natvis -) - -file(GLOB_RECURSE BOOST_URL_SOURCES CONFIGURE_DEPENDS src/*.cpp) - -set_property(GLOBAL PROPERTY USE_FOLDERS ON) - -source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR}/include/boost PREFIX "" FILES ${BOOST_URL_HEADERS}) -source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR}/src PREFIX "url" FILES ${BOOST_URL_SOURCES}) - add_library(boost_url ${BOOST_URL_HEADERS} ${BOOST_URL_SOURCES}) add_library(Boost::url ALIAS boost_url) boost_url_setup_properties(boost_url) -if(BUILD_SHARED_LIBS) - target_compile_definitions(boost_url PUBLIC BOOST_URL_DYN_LINK=1) -else() - target_compile_definitions(boost_url PUBLIC BOOST_URL_STATIC_LINK=1) -endif() -target_compile_definitions(boost_url PRIVATE BOOST_URL_SOURCE) - - - -if(BOOST_URL_INSTALL AND NOT BOOST_SUPERPROJECT_VERSION) - install(TARGETS boost_url - RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" - LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" - ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" - ) - - install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/boost - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} - FILES_MATCHING - PATTERN "*.hpp" - PATTERN "*.ipp" - ) -endif() - - -if(BOOST_URL_BUILD_TESTS) +####################################################### +### Tests ### +####################################################### +if (BOOST_URL_BUILD_TESTS) + if (BOOST_URL_IS_ROOT) + include(CTest) + endif () add_subdirectory(test) -endif() +endif () -if(BOOST_URL_BUILD_EXAMPLES) +if (BOOST_URL_BUILD_EXAMPLES) add_subdirectory(example) -endif() +endif () diff --git a/example/file_router/CMakeLists.txt b/example/file_router/CMakeLists.txt index 5d171c60..a502fed1 100644 --- a/example/file_router/CMakeLists.txt +++ b/example/file_router/CMakeLists.txt @@ -7,20 +7,12 @@ # Official repository: https://github.com/boostorg/url # -source_group("" FILES - file_router.cpp - ) - -add_executable(file_router - file_router.cpp - ) - -set_property(TARGET file_router PROPERTY FOLDER "Examples") +add_executable(file_router file_router.cpp) target_link_libraries(file_router PRIVATE Boost::url Boost::filesystem) if (TARGET boost_filesystem AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU") get_target_property(FS_IS_IMPORTED boost_filesystem IMPORTED) - if (FS_IS_IMPORTED) - target_compile_options(boost_filesystem PUBLIC -Wno-error=restrict) - endif() + target_compile_options(boost_filesystem PUBLIC $<$:-Wno-error=restrict>) endif() +source_group("" FILES file_router.cpp) +set_property(TARGET file_router PROPERTY FOLDER "Examples") diff --git a/example/finicky/CMakeLists.txt b/example/finicky/CMakeLists.txt index 33f98811..b8b1c551 100644 --- a/example/finicky/CMakeLists.txt +++ b/example/finicky/CMakeLists.txt @@ -7,13 +7,7 @@ # Official repository: https://github.com/boostorg/url # -source_group("" FILES - finicky.cpp - ) - -add_executable(finicky - finicky.cpp - ) - -set_property(TARGET finicky PROPERTY FOLDER "Examples") +add_executable(finicky finicky.cpp) target_link_libraries(finicky PRIVATE Boost::url Boost::json Boost::regex) +source_group("" FILES finicky.cpp) +set_property(TARGET finicky PROPERTY FOLDER "Examples") diff --git a/example/magnet/CMakeLists.txt b/example/magnet/CMakeLists.txt index 73ed489f..de246681 100644 --- a/example/magnet/CMakeLists.txt +++ b/example/magnet/CMakeLists.txt @@ -7,15 +7,7 @@ # Official repository: https://github.com/boostorg/url # -source_group("" FILES - magnet.cpp - filter_view.hpp - ) - -add_executable(magnet - magnet.cpp - filter_view.hpp - ) - -set_property(TARGET magnet PROPERTY FOLDER "Examples") +add_executable(magnet magnet.cpp filter_view.hpp) target_link_libraries(magnet PRIVATE Boost::url) +source_group("" FILES magnet.cpp filter_view.hpp) +set_property(TARGET magnet PROPERTY FOLDER "Examples") diff --git a/example/mailto/CMakeLists.txt b/example/mailto/CMakeLists.txt index a589cab6..1e749a10 100644 --- a/example/mailto/CMakeLists.txt +++ b/example/mailto/CMakeLists.txt @@ -7,13 +7,7 @@ # Official repository: https://github.com/boostorg/url # -source_group("" FILES - mailto.cpp - ) - -add_executable(mailto - mailto.cpp - ) - -set_property(TARGET mailto PROPERTY FOLDER "Examples") +add_executable(mailto mailto.cpp) target_link_libraries(mailto PRIVATE Boost::url) +source_group("" FILES mailto.cpp) +set_property(TARGET mailto PROPERTY FOLDER "Examples") diff --git a/example/qrcode/CMakeLists.txt b/example/qrcode/CMakeLists.txt index 5395d1f4..92321a62 100644 --- a/example/qrcode/CMakeLists.txt +++ b/example/qrcode/CMakeLists.txt @@ -7,13 +7,7 @@ # Official repository: https://github.com/boostorg/url # -source_group("" FILES - qrcode.cpp - ) - -add_executable(qrcode - qrcode.cpp - ) - -set_property(TARGET qrcode PROPERTY FOLDER "Examples") +add_executable(qrcode qrcode.cpp) target_link_libraries(qrcode PRIVATE Boost::url) +source_group("" FILES qrcode.cpp) +set_property(TARGET qrcode PROPERTY FOLDER "Examples") diff --git a/example/suffix_list/CMakeLists.txt b/example/suffix_list/CMakeLists.txt index cc6a2c9a..afa93793 100644 --- a/example/suffix_list/CMakeLists.txt +++ b/example/suffix_list/CMakeLists.txt @@ -7,13 +7,7 @@ # Official repository: https://github.com/boostorg/url # -source_group("" FILES - suffix_list.cpp - ) - -add_executable(suffix_list - suffix_list.cpp - ) - -set_property(TARGET suffix_list PROPERTY FOLDER "Examples") +add_executable(suffix_list suffix_list.cpp) target_link_libraries(suffix_list PRIVATE Boost::url) +source_group("" FILES suffix_list.cpp) +set_property(TARGET suffix_list PROPERTY FOLDER "Examples") diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 5d3fec3f..be370918 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -8,16 +8,19 @@ # Official repository: https://github.com/boostorg/url # +# Custom target for all tests if(NOT TARGET boost_url_all_tests) add_custom_target(boost_url_all_tests) set_property(TARGET boost_url_all_tests PROPERTY FOLDER Dependencies) endif() +set(SUITE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/../extra/test_main.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../extra/test_suite.hpp) + +add_subdirectory(unit) add_subdirectory(extra) add_subdirectory(limits) -add_subdirectory(unit) -#add_subdirectory(wpt) - -if (BOOST_URL_BUILD_FUZZERS) +if (BOOST_URL_BUILD_FUZZERS AND CMAKE_CXX_COMPILER_ID STREQUAL "Clang") add_subdirectory(fuzz) endif() diff --git a/test/cmake_test/CMakeLists.txt b/test/cmake_test/CMakeLists.txt index 0c9dcc48..196abd0e 100644 --- a/test/cmake_test/CMakeLists.txt +++ b/test/cmake_test/CMakeLists.txt @@ -10,28 +10,17 @@ cmake_minimum_required(VERSION 3.5...3.16) project(cmake_subdir_test LANGUAGES CXX) if(BOOST_CI_INSTALL_TEST) - find_package(boost_url REQUIRED) + find_package(Boost CONFIG REQUIRED COMPONENTS url) else() - set(DEPENDENCIES - # boostdep --brief url - # Primary dependencies - align assert config core mp11 optional static_assert system throw_exception type_traits variant2 - # Secondary dependencies - detail move predef utility winapi preprocessor io - ) - foreach (dep ${DEPENDENCIES}) - if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/../../../${dep}/CMakeLists.txt") - add_subdirectory(../../../${dep} boostorg/${dep}) - endif() - endforeach() - + set(BOOST_URL_BUILD_TESTS OFF CACHE BOOL "Build the tests." FORCE) add_subdirectory(../.. boostorg/url) endif() add_executable(main main.cpp) target_link_libraries(main Boost::url) -enable_testing() -add_test(NAME main COMMAND main) - -add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure -C $) +if (BUILD_TESTING) + enable_testing() + add_test(NAME main COMMAND main) + add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure -C $) +endif() diff --git a/test/extra/CMakeLists.txt b/test/extra/CMakeLists.txt index 3af6013a..20c499d0 100644 --- a/test/extra/CMakeLists.txt +++ b/test/extra/CMakeLists.txt @@ -8,34 +8,18 @@ # Official repository: https://github.com/boostorg/url # -set(TEST_FILES - Jamfile - test_suite.cpp - ) +# Files +set(TEST_FILES Jamfile test_suite.cpp) -file(GLOB_RECURSE SUITE_FILES CONFIGURE_DEPENDS - ../../extra/*.cpp - ../../extra/*.hpp - ../../extra/*.ipp -) - -#set(SUITE_FILES ../../extra/test_main.cpp ../../extra/test_suite.hpp) - -source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${TEST_FILES}) -source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR}/../../extra PREFIX "_extra" FILES ${SUITE_FILES}) +# Test target add_executable(boost_url_extra ${TEST_FILES} ${SUITE_FILES}) target_include_directories(boost_url_extra PRIVATE . ../../extra) -if (BOOST_URL_FIND_PACKAGE_BOOST) - target_link_libraries(boost_url_extra PRIVATE Boost::headers) -else() - target_link_libraries(boost_url_extra PRIVATE - Boost::align - Boost::config - Boost::core - Boost::optional - Boost::type_traits - Boost::system - Boost::variant2) -endif() +target_link_libraries(boost_url_extra PRIVATE Boost::url) + +# Folders +source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${TEST_FILES}) +source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR}/../../extra PREFIX "_extra" FILES ${SUITE_FILES}) + +# CTest Target add_test(NAME boost_url_extra COMMAND boost_url_extra) add_dependencies(boost_url_all_tests boost_url_extra) diff --git a/test/fuzz/CMakeLists.txt b/test/fuzz/CMakeLists.txt index 41da3155..cf0bc16b 100644 --- a/test/fuzz/CMakeLists.txt +++ b/test/fuzz/CMakeLists.txt @@ -5,23 +5,25 @@ # https://www.boost.org/LICENSE_1_0.txt # -source_group("" FILES - fuzz_parse.cpp - ) +# Files +source_group("" FILES fuzz_parse.cpp) -function(add_boost_url_fuzzer NAME SOURCE) - add_library(fuzzerlib_${NAME} ${SOURCE}) - set_property(TARGET fuzzerlib_${NAME} PROPERTY FOLDER "fuzzing") +function(add_boost_url_fuzzer NAME SOURCE_FILES) + # Fuzzer library + add_library(fuzzerlib_${NAME} ${SOURCE_FILES}) target_link_libraries(fuzzerlib_${NAME} PRIVATE Boost::url) + set_property(TARGET fuzzerlib_${NAME} PROPERTY FOLDER "fuzzing") if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") - add_executable(fuzzer_${NAME} ${SOURCE}) - set_property(TARGET fuzzer_${NAME} PROPERTY FOLDER "fuzzing") + # Fuzzer executable + add_executable(fuzzer_${NAME} ${SOURCE_FILES}) target_link_libraries(fuzzer_${NAME} PRIVATE Boost::url) - target_compile_options(fuzzer_${NAME} PRIVATE -g -O1 -fsanitize=fuzzer,address,undefined) - target_link_options(fuzzer_${NAME} PRIVATE -fsanitize=fuzzer,address,undefined) + target_compile_options(fuzzer_${NAME} PRIVATE -g -O2 -fsanitize=fuzzer,address,undefined -fno-sanitize-recover=undefined) + target_link_libraries(fuzzer_${NAME} PRIVATE -fsanitize=fuzzer -fuse-ld=lld) + # Custom target to run fuzzer executable add_custom_target(fuzz_${NAME} fuzzer_${NAME} -rss_limit_mb=8192 -max_total_time=30 -timeout=30 DEPENDS fuzz_${NAME}) + set_property(TARGET fuzzer_${NAME} PROPERTY FOLDER "fuzzing") endif () endfunction() diff --git a/test/limits/CMakeLists.txt b/test/limits/CMakeLists.txt index a4fccfab..f6dc1504 100644 --- a/test/limits/CMakeLists.txt +++ b/test/limits/CMakeLists.txt @@ -7,30 +7,21 @@ # Official repository: https://github.com/boostorg/url # -set(SUITE_FILES ../../extra/test_main.cpp ../../extra/test_suite.hpp) - -source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES limits.cpp Jamfile) -source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR}/../../extra PREFIX "_extra" FILES ${SUITE_FILES}) -source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR}/../../src PREFIX "url" FILES ${BOOST_URL_SOURCES}) - +# Boost.URL library variant for limits add_library(boost_url_small_limits ${BOOST_URL_HEADERS} ${BOOST_URL_SOURCES}) boost_url_setup_properties(boost_url_small_limits) target_compile_definitions(boost_url_small_limits PUBLIC BOOST_URL_MAX_SIZE=16 BOOST_URL_NO_LIB=1) +# Test target add_executable(boost_url_limits limits.cpp Jamfile ${SUITE_FILES}) target_include_directories(boost_url_limits PRIVATE ../../include ../../extra ../../..) -if (BOOST_URL_FIND_PACKAGE_BOOST) - target_link_libraries(boost_url_limits PRIVATE Boost::headers) -else() - target_link_libraries(boost_url_limits PRIVATE - Boost::align - Boost::config - Boost::core - Boost::optional - Boost::type_traits - Boost::system - Boost::variant2) -endif() target_link_libraries(boost_url_limits PRIVATE boost_url_small_limits) + +# Folders +source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES limits.cpp Jamfile) +source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR}/../../extra PREFIX "_extra" FILES ${SUITE_FILES}) +source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR}/../../src PREFIX "url" FILES ${BOOST_URL_SOURCES}) + +# CTest target add_test(NAME boost_url_limits COMMAND boost_url_limits) add_dependencies(boost_url_all_tests boost_url_limits) diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt index 25357fbc..87c87730 100644 --- a/test/unit/CMakeLists.txt +++ b/test/unit/CMakeLists.txt @@ -8,131 +8,39 @@ # Official repository: https://github.com/boostorg/url # +# Global tests target if(NOT TARGET tests) add_custom_target(tests) set_property(TARGET tests PROPERTY FOLDER Dependencies) endif() -set(BOOST_URL_TESTS_FILES - CMakeLists.txt - Jamfile - test_rule.hpp - authority_view.cpp - doc_3_urls.cpp - doc_grammar.cpp - decode_view.cpp - encode.cpp - encoding_opts.cpp - error.cpp - error_types.cpp - format.cpp - grammar.cpp - host_type.cpp - ignore_case.cpp - ipv4_address.cpp - ipv6_address.cpp - natvis.cpp - optional.cpp - param.cpp - params_base.cpp - params_encoded_view.cpp - params_view.cpp - params_encoded_base.cpp - params_encoded_ref.cpp - params_ref.cpp - parse.cpp - parse_path.cpp - parse_query.cpp - pct_string_view.cpp - scheme.cpp - segments_base.cpp - segments_encoded_base.cpp - segments_encoded_ref.cpp - segments_encoded_view.cpp - segments_ref.cpp - segments_view.cpp - snippets.cpp - static_url.cpp - string_view.cpp - url.cpp - url_base.cpp - url_view.cpp - url_view_base.cpp - urls.cpp - variant.cpp - grammar/alnum_chars.cpp - grammar/alpha_chars.cpp - grammar/charset.cpp - grammar/ci_string.cpp - grammar/dec_octet_rule.cpp - grammar/delim_rule.cpp - grammar/digit_chars.cpp - grammar/grammar_error.cpp - grammar/grammar_parse.cpp - grammar/hexdig_chars.cpp - grammar/literal_rule.cpp - grammar/lut_chars.cpp - grammar/not_empty_rule.cpp - grammar/optional_rule.cpp - grammar/range_rule.cpp - grammar/recycled.cpp - grammar/string_token.cpp - grammar/string_view_base.cpp - grammar/token_rule.cpp - grammar/tuple_rule.cpp - grammar/type_traits.cpp - grammar/unsigned_rule.cpp - grammar/variant_rule.cpp - grammar/vchars.cpp - rfc/absolute_uri_rule.cpp - rfc/authority_rule.cpp - rfc/gen_delim_chars.cpp - rfc/ipv4_address_rule.cpp - rfc/ipv6_address_rule.cpp - rfc/origin_form_rule.cpp - rfc/pchars.cpp - rfc/pct_encoded_rule.cpp - rfc/query_rule.cpp - rfc/relative_ref_rule.cpp - rfc/reserved_chars.cpp - rfc/sub_delim_chars.cpp - rfc/unreserved_chars.cpp - rfc/uri_rule.cpp - rfc/uri_reference_rule.cpp - example/router/router.cpp - compat/ada.cpp - ) - -set(SUITE_FILES ../../extra/test_main.cpp ../../extra/test_suite.hpp) +# Files +file(GLOB_RECURSE BOOST_URL_TESTS_FILES CONFIGURE_DEPENDS *.cpp *.hpp) +list(APPEND BOOST_URL_TESTS_FILES CMakeLists.txt Jamfile) set(EXAMPLE_FILES ../../example/router/impl/matches.cpp ../../example/router/detail/impl/router.cpp) +# Test target +add_executable(boost_url_tests ${BOOST_URL_TESTS_FILES} ${SUITE_FILES} ${EXAMPLE_FILES}) +target_include_directories(boost_url_tests PRIVATE . ../../extra ../../example/router) +target_link_libraries(boost_url_tests PUBLIC Boost::url) +foreach (BOOST_URL_UNIT_TEST_LIBRARY ${BOOST_URL_UNIT_TEST_LIBRARIES}) + target_link_libraries(boost_url_tests PUBLIC Boost::${BOOST_URL_UNIT_TEST_LIBRARY}) +endforeach () + +# Compile options +target_compile_options(boost_url_tests PUBLIC $<$:-Wno-unused-but-set-variable>) +target_compile_options(boost_url_tests PUBLIC $<$:-Wno-unused-function>) +if (TARGET boost_filesystem AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + get_target_property(FS_IS_IMPORTED boost_filesystem IMPORTED) + target_compile_options(boost_filesystem PUBLIC $<$:-Wno-error=restrict>) +endif() + +# Folders source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${BOOST_URL_TESTS_FILES}) source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR}/../../extra PREFIX "_extra" FILES ${SUITE_FILES}) source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR}/../../example/router PREFIX "_router" FILES ${EXAMPLE_FILES}) -add_executable(boost_url_tests ${BOOST_URL_TESTS_FILES} ${SUITE_FILES} ${EXAMPLE_FILES}) -target_include_directories(boost_url_tests PRIVATE . ../../extra) -target_include_directories(boost_url_tests PRIVATE ../../example/router) -# The include dependencies are found in the CMakeLists.txt -# of the root project directory. -# See: BOOST_URL_UNIT_TEST_LIBRARIES -target_link_libraries(boost_url_tests PRIVATE - Boost::url - Boost::container - Boost::filesystem - Boost::unordered) +# CTest target add_test(NAME boost_url_tests COMMAND boost_url_tests) add_dependencies(boost_url_all_tests boost_url_tests) -if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") - target_compile_options( - boost_url_tests - PUBLIC - -Wno-unused-but-set-variable - -Wno-unused-function) - if (TARGET boost_filesystem AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU") - get_target_property(FS_IS_IMPORTED boost_filesystem IMPORTED) - if (FS_IS_IMPORTED) - target_compile_options(boost_filesystem PUBLIC -Wno-restrict) - endif() - endif() -endif() \ No newline at end of file + diff --git a/test/unit/grammar/copied_strings.cpp b/test/unit/grammar/copied_strings.cpp deleted file mode 100644 index 049d0080..00000000 --- a/test/unit/grammar/copied_strings.cpp +++ /dev/null @@ -1,33 +0,0 @@ -// -// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/boostorg/url -// - -// Test that header file is self-contained. -#include - -#include "test_suite.hpp" - -namespace boost { -namespace urls { -namespace grammar { - -struct copied_strings_test -{ - void - run() - { - } -}; - -TEST_SUITE( - copied_strings_test, - "boost.url.grammar.copied_strings"); - -} // grammar -} // urls -} // boost diff --git a/test/unit/url.cpp b/test/unit/url.cpp index 432fa0f4..9139798b 100644 --- a/test/unit/url.cpp +++ b/test/unit/url.cpp @@ -1224,7 +1224,12 @@ struct url_test resolve(base, base, base); BOOST_TEST_CSTR_EQ(base.buffer(), "http://www.example.com/user/"); } + } + // complete string comparison + { + url_view u("https://user:p%61ss@www.%65xample.com:443/p%61th/to/page?k%65y=h%65llo%20world#fr%61gment"); + BOOST_TEST_EQ(u, url_view("https://user:pass@www.example.com:443/path/to/page?key=hello%20world#fragment")); } } diff --git a/test/wpt/CMakeLists.txt b/test/wpt/CMakeLists.txt deleted file mode 100644 index 17565e5e..00000000 --- a/test/wpt/CMakeLists.txt +++ /dev/null @@ -1,26 +0,0 @@ -# -# Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) -# Copyright (c) 2021 DMitry Arkhipov (grisumbras@gmail.com) -# -# Distributed under the Boost Software License, Version 1.0. (See accompanying -# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -# -# Official repository: https://github.com/boostorg/url -# - -if(NOT TARGET tests) - add_custom_target(tests) - set_property(TARGET tests PROPERTY FOLDER Dependencies) -endif() - -set(F - CMakeLists.txt - main.cpp - ) - -source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} PREFIX "" FILES ${F}) -add_executable(boost_wpt_tests ${F}) -target_include_directories(boost_wpt_tests PRIVATE . ../../extra) -target_link_libraries(boost_wpt_tests PRIVATE Boost::url Boost::json) -add_test(NAME boost_url_tests COMMAND boost_url_tests) -add_dependencies(tests boost_url_tests) diff --git a/test/wpt/main.cpp b/test/wpt/main.cpp deleted file mode 100644 index 08a33fdd..00000000 --- a/test/wpt/main.cpp +++ /dev/null @@ -1,137 +0,0 @@ -// -// Copyright (c) 2021 Vinnie Falco (vinnie dot falco at gmail dot com) -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/boostorg/url -// - -#include -#include -#include "test_suite.hpp" -#include -#include -#include -#include -#include - -/* - Runs tests against the web-platform-tests set - of input vectors. Paths to zero or more files - must be provided on the command line. The names - have to match the original names from this - repo: - - https://github.com/web-platform-tests/wpt/tree/master/url/resources - - or - - https://github.com/web-platform-tests/wpt/tree/982c7addc45086db44c44e5c442c97703409d675/url/resources -*/ - -namespace json = boost::json; -using namespace boost::urls; - -test_suite::debug_stream Log(std::cout); -int fail_ = 0; -int total_ = 0; - -json::value -read_json(char const* path) -{ - std::ostringstream oss; - std::ifstream f(path); - oss << f.rdbuf(); - std::string s = oss.str(); - return json::parse(s); -} - -string_view -filename(char const* path) -{ - string_view s(path); -#ifdef _MSC_VER - auto n = s.find_last_of('\\'); -#else - auto n = s.find_last_of('/'); -#endif - if(n == string_view::npos) - return s; - return s.substr(n + 1); -} - -void -do_setters_scheme(json::array const& ja) -{ - for(auto const& jv : ja) - { - ++total_; - auto href = jv.at("href").as_string(); - url u = parse_uri_reference(href).value(); - auto const& ex = jv.at("expected").as_object(); - try - { - u.set_scheme(jv.at("new_value").as_string()); - } - catch(std::exception const& e) - { - if(ex.at("href").as_string() != href) - { - Log << "caught exception: " << e.what() << std::endl; - Log << "set_scheme failed: " << href << - ", " << jv.at("new_value") << std::endl; - ++fail_; - } - } - } -} - -void -do_setters_user(json::array const& ja) -{ - for(auto const& jv : ja) - { - ++total_; - auto href = jv.at("href").as_string(); - url u = parse_uri_reference(href).value(); - // VFALCO TODO - } -} - -void -do_setters_tests(json::value const& jv) -{ - for(auto const& v : jv.as_object()) - { - if(v.key() == "protocol") - do_setters_scheme(v.value().as_array()); - } -} - -int main(int argc, char** argv) -{ - for(int i = 1; i < argc; ++i) - { - try - { - auto jv = read_json(argv[i]); - auto s = filename(argv[i]); - Log << "file: " << s << std::endl; - if(s == "setters_tests.json") - do_setters_tests(jv); - } - catch(std::exception const& e) - { - Log << "caught exception: " << e.what() << std::endl; - } - } - if(fail_ == 0) - Log << total_ << " total success" << std::endl; - else - Log << fail_ << " of " << total_ << " failures" << std::endl; - - if(fail_ > 0) - return EXIT_FAILURE; - return EXIT_SUCCESS; -} diff --git a/test/wpt/setters_tests.json b/test/wpt/setters_tests.json deleted file mode 100644 index b709ef52..00000000 --- a/test/wpt/setters_tests.json +++ /dev/null @@ -1,2053 +0,0 @@ -{ - "comment": [ - "## Tests for setters of https://url.spec.whatwg.org/#urlutils-members", - "", - "This file contains a JSON object.", - "Other than 'comment', each key is an attribute of the `URL` interface", - "defined in WHATWG’s URL Standard.", - "The values are arrays of test case objects for that attribute.", - "", - "To run a test case for the attribute `attr`:", - "", - "* Create a new `URL` object with the value for the 'href' key", - " the constructor single parameter. (Without a base URL.)", - " This must not throw.", - "* Set the attribute `attr` to (invoke its setter with)", - " with the value of for 'new_value' key.", - "* The value for the 'expected' key is another object.", - " For each `key` / `value` pair of that object,", - " get the attribute `key` (invoke its getter).", - " The returned string must be equal to `value`.", - "", - "Note: the 'href' setter is already covered by urltestdata.json." - ], - "protocol": [ - { - "comment": "The empty string is not a valid scheme. Setter leaves the URL unchanged.", - "href": "a://example.net", - "new_value": "", - "expected": { - "href": "a://example.net", - "protocol": "a:" - } - }, - { - "href": "a://example.net", - "new_value": "b", - "expected": { - "href": "b://example.net", - "protocol": "b:" - } - }, - { - "href": "javascript:alert(1)", - "new_value": "defuse", - "expected": { - "href": "defuse:alert(1)", - "protocol": "defuse:" - } - }, - { - "comment": "Upper-case ASCII is lower-cased", - "href": "a://example.net", - "new_value": "B", - "expected": { - "href": "b://example.net", - "protocol": "b:" - } - }, - { - "comment": "Non-ASCII is rejected", - "href": "a://example.net", - "new_value": "é", - "expected": { - "href": "a://example.net", - "protocol": "a:" - } - }, - { - "comment": "No leading digit", - "href": "a://example.net", - "new_value": "0b", - "expected": { - "href": "a://example.net", - "protocol": "a:" - } - }, - { - "comment": "No leading punctuation", - "href": "a://example.net", - "new_value": "+b", - "expected": { - "href": "a://example.net", - "protocol": "a:" - } - }, - { - "href": "a://example.net", - "new_value": "bC0+-.", - "expected": { - "href": "bc0+-.://example.net", - "protocol": "bc0+-.:" - } - }, - { - "comment": "Only some punctuation is acceptable", - "href": "a://example.net", - "new_value": "b,c", - "expected": { - "href": "a://example.net", - "protocol": "a:" - } - }, - { - "comment": "Non-ASCII is rejected", - "href": "a://example.net", - "new_value": "bé", - "expected": { - "href": "a://example.net", - "protocol": "a:" - } - }, - { - "comment": "Can’t switch from URL containing username/password/port to file", - "href": "http://test@example.net", - "new_value": "file", - "expected": { - "href": "http://test@example.net/", - "protocol": "http:" - } - }, - { - "href": "https://example.net:1234", - "new_value": "file", - "expected": { - "href": "https://example.net:1234/", - "protocol": "https:" - } - }, - { - "href": "wss://x:x@example.net:1234", - "new_value": "file", - "expected": { - "href": "wss://x:x@example.net:1234/", - "protocol": "wss:" - } - }, - { - "comment": "Can’t switch from file URL with no host", - "href": "file://localhost/", - "new_value": "http", - "expected": { - "href": "file:///", - "protocol": "file:" - } - }, - { - "href": "file:///test", - "new_value": "https", - "expected": { - "href": "file:///test", - "protocol": "file:" - } - }, - { - "href": "file:", - "new_value": "wss", - "expected": { - "href": "file:///", - "protocol": "file:" - } - }, - { - "comment": "Can’t switch from special scheme to non-special", - "href": "http://example.net", - "new_value": "b", - "expected": { - "href": "http://example.net/", - "protocol": "http:" - } - }, - { - "href": "file://hi/path", - "new_value": "s", - "expected": { - "href": "file://hi/path", - "protocol": "file:" - } - }, - { - "href": "https://example.net", - "new_value": "s", - "expected": { - "href": "https://example.net/", - "protocol": "https:" - } - }, - { - "href": "ftp://example.net", - "new_value": "test", - "expected": { - "href": "ftp://example.net/", - "protocol": "ftp:" - } - }, - { - "comment": "Cannot-be-a-base URL doesn’t have a host, but URL in a special scheme must.", - "href": "mailto:me@example.net", - "new_value": "http", - "expected": { - "href": "mailto:me@example.net", - "protocol": "mailto:" - } - }, - { - "comment": "Can’t switch from non-special scheme to special", - "href": "ssh://me@example.net", - "new_value": "http", - "expected": { - "href": "ssh://me@example.net", - "protocol": "ssh:" - } - }, - { - "href": "ssh://me@example.net", - "new_value": "https", - "expected": { - "href": "ssh://me@example.net", - "protocol": "ssh:" - } - }, - { - "href": "ssh://me@example.net", - "new_value": "file", - "expected": { - "href": "ssh://me@example.net", - "protocol": "ssh:" - } - }, - { - "href": "ssh://example.net", - "new_value": "file", - "expected": { - "href": "ssh://example.net", - "protocol": "ssh:" - } - }, - { - "href": "nonsense:///test", - "new_value": "https", - "expected": { - "href": "nonsense:///test", - "protocol": "nonsense:" - } - }, - { - "comment": "Stuff after the first ':' is ignored", - "href": "http://example.net", - "new_value": "https:foo : bar", - "expected": { - "href": "https://example.net/", - "protocol": "https:" - } - }, - { - "comment": "Stuff after the first ':' is ignored", - "href": "data:text/html,

Test", - "new_value": "view-source+data:foo : bar", - "expected": { - "href": "view-source+data:text/html,

Test", - "protocol": "view-source+data:" - } - }, - { - "comment": "Port is set to null if it is the default for new scheme.", - "href": "http://foo.com:443/", - "new_value": "https", - "expected": { - "href": "https://foo.com/", - "protocol": "https:", - "port": "" - } - } - ], - "username": [ - { - "comment": "No host means no username", - "href": "file:///home/you/index.html", - "new_value": "me", - "expected": { - "href": "file:///home/you/index.html", - "username": "" - } - }, - { - "comment": "No host means no username", - "href": "unix:/run/foo.socket", - "new_value": "me", - "expected": { - "href": "unix:/run/foo.socket", - "username": "" - } - }, - { - "comment": "Cannot-be-a-base means no username", - "href": "mailto:you@example.net", - "new_value": "me", - "expected": { - "href": "mailto:you@example.net", - "username": "" - } - }, - { - "href": "javascript:alert(1)", - "new_value": "wario", - "expected": { - "href": "javascript:alert(1)", - "username": "" - } - }, - { - "href": "http://example.net", - "new_value": "me", - "expected": { - "href": "http://me@example.net/", - "username": "me" - } - }, - { - "href": "http://:secret@example.net", - "new_value": "me", - "expected": { - "href": "http://me:secret@example.net/", - "username": "me" - } - }, - { - "href": "http://me@example.net", - "new_value": "", - "expected": { - "href": "http://example.net/", - "username": "" - } - }, - { - "href": "http://me:secret@example.net", - "new_value": "", - "expected": { - "href": "http://:secret@example.net/", - "username": "" - } - }, - { - "comment": "UTF-8 percent encoding with the userinfo encode set.", - "href": "http://example.net", - "new_value": "\u0000\u0001\t\n\r\u001f !\"#$%&'()*+,-./09:;<=>?@AZ[\\]^_`az{|}~\u007f\u0080\u0081Éé", - "expected": { - "href": "http://%00%01%09%0A%0D%1F%20!%22%23$%&'()*+,-.%2F09%3A%3B%3C%3D%3E%3F%40AZ%5B%5C%5D%5E_%60az%7B%7C%7D~%7F%C2%80%C2%81%C3%89%C3%A9@example.net/", - "username": "%00%01%09%0A%0D%1F%20!%22%23$%&'()*+,-.%2F09%3A%3B%3C%3D%3E%3F%40AZ%5B%5C%5D%5E_%60az%7B%7C%7D~%7F%C2%80%C2%81%C3%89%C3%A9" - } - }, - { - "comment": "Bytes already percent-encoded are left as-is.", - "href": "http://example.net", - "new_value": "%c3%89té", - "expected": { - "href": "http://%c3%89t%C3%A9@example.net/", - "username": "%c3%89t%C3%A9" - } - }, - { - "href": "sc:///", - "new_value": "x", - "expected": { - "href": "sc:///", - "username": "" - } - }, - { - "href": "javascript://x/", - "new_value": "wario", - "expected": { - "href": "javascript://wario@x/", - "username": "wario" - } - }, - { - "href": "file://test/", - "new_value": "test", - "expected": { - "href": "file://test/", - "username": "" - } - } - ], - "password": [ - { - "comment": "No host means no password", - "href": "file:///home/me/index.html", - "new_value": "secret", - "expected": { - "href": "file:///home/me/index.html", - "password": "" - } - }, - { - "comment": "No host means no password", - "href": "unix:/run/foo.socket", - "new_value": "secret", - "expected": { - "href": "unix:/run/foo.socket", - "password": "" - } - }, - { - "comment": "Cannot-be-a-base means no password", - "href": "mailto:me@example.net", - "new_value": "secret", - "expected": { - "href": "mailto:me@example.net", - "password": "" - } - }, - { - "href": "http://example.net", - "new_value": "secret", - "expected": { - "href": "http://:secret@example.net/", - "password": "secret" - } - }, - { - "href": "http://me@example.net", - "new_value": "secret", - "expected": { - "href": "http://me:secret@example.net/", - "password": "secret" - } - }, - { - "href": "http://:secret@example.net", - "new_value": "", - "expected": { - "href": "http://example.net/", - "password": "" - } - }, - { - "href": "http://me:secret@example.net", - "new_value": "", - "expected": { - "href": "http://me@example.net/", - "password": "" - } - }, - { - "comment": "UTF-8 percent encoding with the userinfo encode set.", - "href": "http://example.net", - "new_value": "\u0000\u0001\t\n\r\u001f !\"#$%&'()*+,-./09:;<=>?@AZ[\\]^_`az{|}~\u007f\u0080\u0081Éé", - "expected": { - "href": "http://:%00%01%09%0A%0D%1F%20!%22%23$%&'()*+,-.%2F09%3A%3B%3C%3D%3E%3F%40AZ%5B%5C%5D%5E_%60az%7B%7C%7D~%7F%C2%80%C2%81%C3%89%C3%A9@example.net/", - "password": "%00%01%09%0A%0D%1F%20!%22%23$%&'()*+,-.%2F09%3A%3B%3C%3D%3E%3F%40AZ%5B%5C%5D%5E_%60az%7B%7C%7D~%7F%C2%80%C2%81%C3%89%C3%A9" - } - }, - { - "comment": "Bytes already percent-encoded are left as-is.", - "href": "http://example.net", - "new_value": "%c3%89té", - "expected": { - "href": "http://:%c3%89t%C3%A9@example.net/", - "password": "%c3%89t%C3%A9" - } - }, - { - "href": "sc:///", - "new_value": "x", - "expected": { - "href": "sc:///", - "password": "" - } - }, - { - "href": "javascript://x/", - "new_value": "bowser", - "expected": { - "href": "javascript://:bowser@x/", - "password": "bowser" - } - }, - { - "href": "file://test/", - "new_value": "test", - "expected": { - "href": "file://test/", - "password": "" - } - } - ], - "host": [ - { - "comment": "Non-special scheme", - "href": "sc://x/", - "new_value": "\u0000", - "expected": { - "href": "sc://x/", - "host": "x", - "hostname": "x" - } - }, - { - "href": "sc://x/", - "new_value": "\u0009", - "expected": { - "href": "sc:///", - "host": "", - "hostname": "" - } - }, - { - "href": "sc://x/", - "new_value": "\u000A", - "expected": { - "href": "sc:///", - "host": "", - "hostname": "" - } - }, - { - "href": "sc://x/", - "new_value": "\u000D", - "expected": { - "href": "sc:///", - "host": "", - "hostname": "" - } - }, - { - "href": "sc://x/", - "new_value": " ", - "expected": { - "href": "sc://x/", - "host": "x", - "hostname": "x" - } - }, - { - "href": "sc://x/", - "new_value": "#", - "expected": { - "href": "sc:///", - "host": "", - "hostname": "" - } - }, - { - "href": "sc://x/", - "new_value": "/", - "expected": { - "href": "sc:///", - "host": "", - "hostname": "" - } - }, - { - "href": "sc://x/", - "new_value": "?", - "expected": { - "href": "sc:///", - "host": "", - "hostname": "" - } - }, - { - "href": "sc://x/", - "new_value": "@", - "expected": { - "href": "sc://x/", - "host": "x", - "hostname": "x" - } - }, - { - "href": "sc://x/", - "new_value": "ß", - "expected": { - "href": "sc://%C3%9F/", - "host": "%C3%9F", - "hostname": "%C3%9F" - } - }, - { - "comment": "IDNA Nontransitional_Processing", - "href": "https://x/", - "new_value": "ß", - "expected": { - "href": "https://xn--zca/", - "host": "xn--zca", - "hostname": "xn--zca" - } - }, - { - "comment": "Cannot-be-a-base means no host", - "href": "mailto:me@example.net", - "new_value": "example.com", - "expected": { - "href": "mailto:me@example.net", - "host": "" - } - }, - { - "comment": "Cannot-be-a-base means no host", - "href": "data:text/plain,Stuff", - "new_value": "example.net", - "expected": { - "href": "data:text/plain,Stuff", - "host": "" - } - }, - { - "href": "http://example.net", - "new_value": "example.com:8080", - "expected": { - "href": "http://example.com:8080/", - "host": "example.com:8080", - "hostname": "example.com", - "port": "8080" - } - }, - { - "comment": "Port number is unchanged if not specified in the new value", - "href": "http://example.net:8080", - "new_value": "example.com", - "expected": { - "href": "http://example.com:8080/", - "host": "example.com:8080", - "hostname": "example.com", - "port": "8080" - } - }, - { - "comment": "Port number is unchanged if not specified", - "href": "http://example.net:8080", - "new_value": "example.com:", - "expected": { - "href": "http://example.com:8080/", - "host": "example.com:8080", - "hostname": "example.com", - "port": "8080" - } - }, - { - "comment": "The empty host is not valid for special schemes", - "href": "http://example.net", - "new_value": "", - "expected": { - "href": "http://example.net/", - "host": "example.net" - } - }, - { - "comment": "The empty host is OK for non-special schemes", - "href": "view-source+http://example.net/foo", - "new_value": "", - "expected": { - "href": "view-source+http:///foo", - "host": "" - } - }, - { - "comment": "Path-only URLs can gain a host", - "href": "a:/foo", - "new_value": "example.net", - "expected": { - "href": "a://example.net/foo", - "host": "example.net" - } - }, - { - "comment": "IPv4 address syntax is normalized", - "href": "http://example.net", - "new_value": "0x7F000001:8080", - "expected": { - "href": "http://127.0.0.1:8080/", - "host": "127.0.0.1:8080", - "hostname": "127.0.0.1", - "port": "8080" - } - }, - { - "comment": "IPv6 address syntax is normalized", - "href": "http://example.net", - "new_value": "[::0:01]:2", - "expected": { - "href": "http://[::1]:2/", - "host": "[::1]:2", - "hostname": "[::1]", - "port": "2" - } - }, - { - "comment": "IPv6 literal address with port, crbug.com/1012416", - "href": "http://example.net", - "new_value": "[2001:db8::2]:4002", - "expected": { - "href": "http://[2001:db8::2]:4002/", - "host": "[2001:db8::2]:4002", - "hostname": "[2001:db8::2]", - "port": "4002" - } - }, - { - "comment": "Default port number is removed", - "href": "http://example.net", - "new_value": "example.com:80", - "expected": { - "href": "http://example.com/", - "host": "example.com", - "hostname": "example.com", - "port": "" - } - }, - { - "comment": "Default port number is removed", - "href": "https://example.net", - "new_value": "example.com:443", - "expected": { - "href": "https://example.com/", - "host": "example.com", - "hostname": "example.com", - "port": "" - } - }, - { - "comment": "Default port number is only removed for the relevant scheme", - "href": "https://example.net", - "new_value": "example.com:80", - "expected": { - "href": "https://example.com:80/", - "host": "example.com:80", - "hostname": "example.com", - "port": "80" - } - }, - { - "comment": "Port number is removed if new port is scheme default and existing URL has a non-default port", - "href": "http://example.net:8080", - "new_value": "example.com:80", - "expected": { - "href": "http://example.com/", - "host": "example.com", - "hostname": "example.com", - "port": "" - } - }, - { - "comment": "Stuff after a / delimiter is ignored", - "href": "http://example.net/path", - "new_value": "example.com/stuff", - "expected": { - "href": "http://example.com/path", - "host": "example.com", - "hostname": "example.com", - "port": "" - } - }, - { - "comment": "Stuff after a / delimiter is ignored", - "href": "http://example.net/path", - "new_value": "example.com:8080/stuff", - "expected": { - "href": "http://example.com:8080/path", - "host": "example.com:8080", - "hostname": "example.com", - "port": "8080" - } - }, - { - "comment": "Stuff after a ? delimiter is ignored", - "href": "http://example.net/path", - "new_value": "example.com?stuff", - "expected": { - "href": "http://example.com/path", - "host": "example.com", - "hostname": "example.com", - "port": "" - } - }, - { - "comment": "Stuff after a ? delimiter is ignored", - "href": "http://example.net/path", - "new_value": "example.com:8080?stuff", - "expected": { - "href": "http://example.com:8080/path", - "host": "example.com:8080", - "hostname": "example.com", - "port": "8080" - } - }, - { - "comment": "Stuff after a # delimiter is ignored", - "href": "http://example.net/path", - "new_value": "example.com#stuff", - "expected": { - "href": "http://example.com/path", - "host": "example.com", - "hostname": "example.com", - "port": "" - } - }, - { - "comment": "Stuff after a # delimiter is ignored", - "href": "http://example.net/path", - "new_value": "example.com:8080#stuff", - "expected": { - "href": "http://example.com:8080/path", - "host": "example.com:8080", - "hostname": "example.com", - "port": "8080" - } - }, - { - "comment": "Stuff after a \\ delimiter is ignored for special schemes", - "href": "http://example.net/path", - "new_value": "example.com\\stuff", - "expected": { - "href": "http://example.com/path", - "host": "example.com", - "hostname": "example.com", - "port": "" - } - }, - { - "comment": "Stuff after a \\ delimiter is ignored for special schemes", - "href": "http://example.net/path", - "new_value": "example.com:8080\\stuff", - "expected": { - "href": "http://example.com:8080/path", - "host": "example.com:8080", - "hostname": "example.com", - "port": "8080" - } - }, - { - "comment": "\\ is not a delimiter for non-special schemes, but still forbidden in hosts", - "href": "view-source+http://example.net/path", - "new_value": "example.com\\stuff", - "expected": { - "href": "view-source+http://example.net/path", - "host": "example.net", - "hostname": "example.net", - "port": "" - } - }, - { - "comment": "Anything other than ASCII digit stops the port parser in a setter but is not an error", - "href": "view-source+http://example.net/path", - "new_value": "example.com:8080stuff2", - "expected": { - "href": "view-source+http://example.com:8080/path", - "host": "example.com:8080", - "hostname": "example.com", - "port": "8080" - } - }, - { - "comment": "Anything other than ASCII digit stops the port parser in a setter but is not an error", - "href": "http://example.net/path", - "new_value": "example.com:8080stuff2", - "expected": { - "href": "http://example.com:8080/path", - "host": "example.com:8080", - "hostname": "example.com", - "port": "8080" - } - }, - { - "comment": "Anything other than ASCII digit stops the port parser in a setter but is not an error", - "href": "http://example.net/path", - "new_value": "example.com:8080+2", - "expected": { - "href": "http://example.com:8080/path", - "host": "example.com:8080", - "hostname": "example.com", - "port": "8080" - } - }, - { - "comment": "Port numbers are 16 bit integers", - "href": "http://example.net/path", - "new_value": "example.com:65535", - "expected": { - "href": "http://example.com:65535/path", - "host": "example.com:65535", - "hostname": "example.com", - "port": "65535" - } - }, - { - "comment": "Port numbers are 16 bit integers, overflowing is an error. Hostname is still set, though.", - "href": "http://example.net/path", - "new_value": "example.com:65536", - "expected": { - "href": "http://example.com/path", - "host": "example.com", - "hostname": "example.com", - "port": "" - } - }, - { - "comment": "Broken IPv6", - "href": "http://example.net/", - "new_value": "[google.com]", - "expected": { - "href": "http://example.net/", - "host": "example.net", - "hostname": "example.net" - } - }, - { - "href": "http://example.net/", - "new_value": "[::1.2.3.4x]", - "expected": { - "href": "http://example.net/", - "host": "example.net", - "hostname": "example.net" - } - }, - { - "href": "http://example.net/", - "new_value": "[::1.2.3.]", - "expected": { - "href": "http://example.net/", - "host": "example.net", - "hostname": "example.net" - } - }, - { - "href": "http://example.net/", - "new_value": "[::1.2.]", - "expected": { - "href": "http://example.net/", - "host": "example.net", - "hostname": "example.net" - } - }, - { - "href": "http://example.net/", - "new_value": "[::1.]", - "expected": { - "href": "http://example.net/", - "host": "example.net", - "hostname": "example.net" - } - }, - { - "href": "file://y/", - "new_value": "x:123", - "expected": { - "href": "file://y/", - "host": "y", - "hostname": "y", - "port": "" - } - }, - { - "href": "file://y/", - "new_value": "loc%41lhost", - "expected": { - "href": "file:///", - "host": "", - "hostname": "", - "port": "" - } - }, - { - "href": "file://hi/x", - "new_value": "", - "expected": { - "href": "file:///x", - "host": "", - "hostname": "", - "port": "" - } - }, - { - "href": "sc://test@test/", - "new_value": "", - "expected": { - "href": "sc://test@test/", - "host": "test", - "hostname": "test", - "username": "test" - } - }, - { - "href": "sc://test:12/", - "new_value": "", - "expected": { - "href": "sc://test:12/", - "host": "test:12", - "hostname": "test", - "port": "12" - } - }, - { - "comment": "Leading / is not stripped", - "href": "http://example.com/", - "new_value": "///bad.com", - "expected": { - "href": "http://example.com/", - "host": "example.com", - "hostname": "example.com" - } - }, - { - "comment": "Leading / is not stripped", - "href": "sc://example.com/", - "new_value": "///bad.com", - "expected": { - "href": "sc:///", - "host": "", - "hostname": "" - } - } - ], - "hostname": [ - { - "comment": "Non-special scheme", - "href": "sc://x/", - "new_value": "\u0000", - "expected": { - "href": "sc://x/", - "host": "x", - "hostname": "x" - } - }, - { - "href": "sc://x/", - "new_value": "\u0009", - "expected": { - "href": "sc:///", - "host": "", - "hostname": "" - } - }, - { - "href": "sc://x/", - "new_value": "\u000A", - "expected": { - "href": "sc:///", - "host": "", - "hostname": "" - } - }, - { - "href": "sc://x/", - "new_value": "\u000D", - "expected": { - "href": "sc:///", - "host": "", - "hostname": "" - } - }, - { - "href": "sc://x/", - "new_value": " ", - "expected": { - "href": "sc://x/", - "host": "x", - "hostname": "x" - } - }, - { - "href": "sc://x/", - "new_value": "#", - "expected": { - "href": "sc:///", - "host": "", - "hostname": "" - } - }, - { - "href": "sc://x/", - "new_value": "/", - "expected": { - "href": "sc:///", - "host": "", - "hostname": "" - } - }, - { - "href": "sc://x/", - "new_value": "?", - "expected": { - "href": "sc:///", - "host": "", - "hostname": "" - } - }, - { - "href": "sc://x/", - "new_value": "@", - "expected": { - "href": "sc://x/", - "host": "x", - "hostname": "x" - } - }, - { - "comment": "Cannot-be-a-base means no host", - "href": "mailto:me@example.net", - "new_value": "example.com", - "expected": { - "href": "mailto:me@example.net", - "host": "" - } - }, - { - "comment": "Cannot-be-a-base means no host", - "href": "data:text/plain,Stuff", - "new_value": "example.net", - "expected": { - "href": "data:text/plain,Stuff", - "host": "" - } - }, - { - "href": "http://example.net:8080", - "new_value": "example.com", - "expected": { - "href": "http://example.com:8080/", - "host": "example.com:8080", - "hostname": "example.com", - "port": "8080" - } - }, - { - "comment": "The empty host is not valid for special schemes", - "href": "http://example.net", - "new_value": "", - "expected": { - "href": "http://example.net/", - "host": "example.net" - } - }, - { - "comment": "The empty host is OK for non-special schemes", - "href": "view-source+http://example.net/foo", - "new_value": "", - "expected": { - "href": "view-source+http:///foo", - "host": "" - } - }, - { - "comment": "Path-only URLs can gain a host", - "href": "a:/foo", - "new_value": "example.net", - "expected": { - "href": "a://example.net/foo", - "host": "example.net" - } - }, - { - "comment": "IPv4 address syntax is normalized", - "href": "http://example.net:8080", - "new_value": "0x7F000001", - "expected": { - "href": "http://127.0.0.1:8080/", - "host": "127.0.0.1:8080", - "hostname": "127.0.0.1", - "port": "8080" - } - }, - { - "comment": "IPv6 address syntax is normalized", - "href": "http://example.net", - "new_value": "[::0:01]", - "expected": { - "href": "http://[::1]/", - "host": "[::1]", - "hostname": "[::1]", - "port": "" - } - }, - { - "comment": ": delimiter invalidates entire value", - "href": "http://example.net/path", - "new_value": "example.com:8080", - "expected": { - "href": "http://example.net/path", - "host": "example.net", - "hostname": "example.net", - "port": "" - } - }, - { - "comment": ": delimiter invalidates entire value", - "href": "http://example.net:8080/path", - "new_value": "example.com:", - "expected": { - "href": "http://example.net:8080/path", - "host": "example.net:8080", - "hostname": "example.net", - "port": "8080" - } - }, - { - "comment": "Stuff after a / delimiter is ignored", - "href": "http://example.net/path", - "new_value": "example.com/stuff", - "expected": { - "href": "http://example.com/path", - "host": "example.com", - "hostname": "example.com", - "port": "" - } - }, - { - "comment": "Stuff after a ? delimiter is ignored", - "href": "http://example.net/path", - "new_value": "example.com?stuff", - "expected": { - "href": "http://example.com/path", - "host": "example.com", - "hostname": "example.com", - "port": "" - } - }, - { - "comment": "Stuff after a # delimiter is ignored", - "href": "http://example.net/path", - "new_value": "example.com#stuff", - "expected": { - "href": "http://example.com/path", - "host": "example.com", - "hostname": "example.com", - "port": "" - } - }, - { - "comment": "Stuff after a \\ delimiter is ignored for special schemes", - "href": "http://example.net/path", - "new_value": "example.com\\stuff", - "expected": { - "href": "http://example.com/path", - "host": "example.com", - "hostname": "example.com", - "port": "" - } - }, - { - "comment": "\\ is not a delimiter for non-special schemes, but still forbidden in hosts", - "href": "view-source+http://example.net/path", - "new_value": "example.com\\stuff", - "expected": { - "href": "view-source+http://example.net/path", - "host": "example.net", - "hostname": "example.net", - "port": "" - } - }, - { - "comment": "Broken IPv6", - "href": "http://example.net/", - "new_value": "[google.com]", - "expected": { - "href": "http://example.net/", - "host": "example.net", - "hostname": "example.net" - } - }, - { - "href": "http://example.net/", - "new_value": "[::1.2.3.4x]", - "expected": { - "href": "http://example.net/", - "host": "example.net", - "hostname": "example.net" - } - }, - { - "href": "http://example.net/", - "new_value": "[::1.2.3.]", - "expected": { - "href": "http://example.net/", - "host": "example.net", - "hostname": "example.net" - } - }, - { - "href": "http://example.net/", - "new_value": "[::1.2.]", - "expected": { - "href": "http://example.net/", - "host": "example.net", - "hostname": "example.net" - } - }, - { - "href": "http://example.net/", - "new_value": "[::1.]", - "expected": { - "href": "http://example.net/", - "host": "example.net", - "hostname": "example.net" - } - }, - { - "href": "file://y/", - "new_value": "x:123", - "expected": { - "href": "file://y/", - "host": "y", - "hostname": "y", - "port": "" - } - }, - { - "href": "file://y/", - "new_value": "loc%41lhost", - "expected": { - "href": "file:///", - "host": "", - "hostname": "", - "port": "" - } - }, - { - "href": "file://hi/x", - "new_value": "", - "expected": { - "href": "file:///x", - "host": "", - "hostname": "", - "port": "" - } - }, - { - "href": "sc://test@test/", - "new_value": "", - "expected": { - "href": "sc://test@test/", - "host": "test", - "hostname": "test", - "username": "test" - } - }, - { - "href": "sc://test:12/", - "new_value": "", - "expected": { - "href": "sc://test:12/", - "host": "test:12", - "hostname": "test", - "port": "12" - } - }, - { - "comment": "Drop /. from path", - "href": "non-spec:/.//p", - "new_value": "h", - "expected": { - "href": "non-spec://h//p", - "host": "h", - "hostname": "h", - "pathname": "//p" - } - }, - { - "href": "non-spec:/.//p", - "new_value": "", - "expected": { - "href": "non-spec:////p", - "host": "", - "hostname": "", - "pathname": "//p" - } - }, - { - "comment": "Leading / is not stripped", - "href": "http://example.com/", - "new_value": "///bad.com", - "expected": { - "href": "http://example.com/", - "host": "example.com", - "hostname": "example.com" - } - }, - { - "comment": "Leading / is not stripped", - "href": "sc://example.com/", - "new_value": "///bad.com", - "expected": { - "href": "sc:///", - "host": "", - "hostname": "" - } - } - ], - "port": [ - { - "href": "http://example.net", - "new_value": "8080", - "expected": { - "href": "http://example.net:8080/", - "host": "example.net:8080", - "hostname": "example.net", - "port": "8080" - } - }, - { - "comment": "Port number is removed if empty is the new value", - "href": "http://example.net:8080", - "new_value": "", - "expected": { - "href": "http://example.net/", - "host": "example.net", - "hostname": "example.net", - "port": "" - } - }, - { - "comment": "Default port number is removed", - "href": "http://example.net:8080", - "new_value": "80", - "expected": { - "href": "http://example.net/", - "host": "example.net", - "hostname": "example.net", - "port": "" - } - }, - { - "comment": "Default port number is removed", - "href": "https://example.net:4433", - "new_value": "443", - "expected": { - "href": "https://example.net/", - "host": "example.net", - "hostname": "example.net", - "port": "" - } - }, - { - "comment": "Default port number is only removed for the relevant scheme", - "href": "https://example.net", - "new_value": "80", - "expected": { - "href": "https://example.net:80/", - "host": "example.net:80", - "hostname": "example.net", - "port": "80" - } - }, - { - "comment": "Stuff after a / delimiter is ignored", - "href": "http://example.net/path", - "new_value": "8080/stuff", - "expected": { - "href": "http://example.net:8080/path", - "host": "example.net:8080", - "hostname": "example.net", - "port": "8080" - } - }, - { - "comment": "Stuff after a ? delimiter is ignored", - "href": "http://example.net/path", - "new_value": "8080?stuff", - "expected": { - "href": "http://example.net:8080/path", - "host": "example.net:8080", - "hostname": "example.net", - "port": "8080" - } - }, - { - "comment": "Stuff after a # delimiter is ignored", - "href": "http://example.net/path", - "new_value": "8080#stuff", - "expected": { - "href": "http://example.net:8080/path", - "host": "example.net:8080", - "hostname": "example.net", - "port": "8080" - } - }, - { - "comment": "Stuff after a \\ delimiter is ignored for special schemes", - "href": "http://example.net/path", - "new_value": "8080\\stuff", - "expected": { - "href": "http://example.net:8080/path", - "host": "example.net:8080", - "hostname": "example.net", - "port": "8080" - } - }, - { - "comment": "Anything other than ASCII digit stops the port parser in a setter but is not an error", - "href": "view-source+http://example.net/path", - "new_value": "8080stuff2", - "expected": { - "href": "view-source+http://example.net:8080/path", - "host": "example.net:8080", - "hostname": "example.net", - "port": "8080" - } - }, - { - "comment": "Anything other than ASCII digit stops the port parser in a setter but is not an error", - "href": "http://example.net/path", - "new_value": "8080stuff2", - "expected": { - "href": "http://example.net:8080/path", - "host": "example.net:8080", - "hostname": "example.net", - "port": "8080" - } - }, - { - "comment": "Anything other than ASCII digit stops the port parser in a setter but is not an error", - "href": "http://example.net/path", - "new_value": "8080+2", - "expected": { - "href": "http://example.net:8080/path", - "host": "example.net:8080", - "hostname": "example.net", - "port": "8080" - } - }, - { - "comment": "Port numbers are 16 bit integers", - "href": "http://example.net/path", - "new_value": "65535", - "expected": { - "href": "http://example.net:65535/path", - "host": "example.net:65535", - "hostname": "example.net", - "port": "65535" - } - }, - { - "comment": "Port numbers are 16 bit integers, overflowing is an error", - "href": "http://example.net:8080/path", - "new_value": "65536", - "expected": { - "href": "http://example.net:8080/path", - "host": "example.net:8080", - "hostname": "example.net", - "port": "8080" - } - }, - { - "comment": "Port numbers are 16 bit integers, overflowing is an error", - "href": "non-special://example.net:8080/path", - "new_value": "65536", - "expected": { - "href": "non-special://example.net:8080/path", - "host": "example.net:8080", - "hostname": "example.net", - "port": "8080" - } - }, - { - "href": "file://test/", - "new_value": "12", - "expected": { - "href": "file://test/", - "port": "" - } - }, - { - "href": "file://localhost/", - "new_value": "12", - "expected": { - "href": "file:///", - "port": "" - } - }, - { - "href": "non-base:value", - "new_value": "12", - "expected": { - "href": "non-base:value", - "port": "" - } - }, - { - "href": "sc:///", - "new_value": "12", - "expected": { - "href": "sc:///", - "port": "" - } - }, - { - "href": "sc://x/", - "new_value": "12", - "expected": { - "href": "sc://x:12/", - "port": "12" - } - }, - { - "href": "javascript://x/", - "new_value": "12", - "expected": { - "href": "javascript://x:12/", - "port": "12" - } - } - ], - "pathname": [ - { - "comment": "Cannot-be-a-base don’t have a path", - "href": "mailto:me@example.net", - "new_value": "/foo", - "expected": { - "href": "mailto:me@example.net", - "pathname": "me@example.net" - } - }, - { - "comment": "Special URLs cannot have their paths erased", - "href": "file:///some/path", - "new_value": "", - "expected": { - "href": "file:///", - "pathname": "/" - } - }, - { - "comment": "Non-special URLs can have their paths erased", - "href": "foo://somehost/some/path", - "new_value": "", - "expected": { - "href": "foo://somehost", - "pathname": "" - } - }, - { - "comment": "Non-special URLs with an empty host can have their paths erased", - "href": "foo:///some/path", - "new_value": "", - "expected": { - "href": "foo://", - "pathname": "" - } - }, - { - "comment": "Path-only URLs cannot have their paths erased", - "href": "foo:/some/path", - "new_value": "", - "expected": { - "href": "foo:/", - "pathname": "/" - } - }, - { - "comment": "Path-only URLs always have an initial slash", - "href": "foo:/some/path", - "new_value": "test", - "expected": { - "href": "foo:/test", - "pathname": "/test" - } - }, - { - "href": "unix:/run/foo.socket?timeout=10", - "new_value": "/var/log/../run/bar.socket", - "expected": { - "href": "unix:/var/run/bar.socket?timeout=10", - "pathname": "/var/run/bar.socket" - } - }, - { - "href": "https://example.net#nav", - "new_value": "home", - "expected": { - "href": "https://example.net/home#nav", - "pathname": "/home" - } - }, - { - "href": "https://example.net#nav", - "new_value": "../home", - "expected": { - "href": "https://example.net/home#nav", - "pathname": "/home" - } - }, - { - "comment": "\\ is a segment delimiter for 'special' URLs", - "href": "http://example.net/home?lang=fr#nav", - "new_value": "\\a\\%2E\\b\\%2e.\\c", - "expected": { - "href": "http://example.net/a/c?lang=fr#nav", - "pathname": "/a/c" - } - }, - { - "comment": "\\ is *not* a segment delimiter for non-'special' URLs", - "href": "view-source+http://example.net/home?lang=fr#nav", - "new_value": "\\a\\%2E\\b\\%2e.\\c", - "expected": { - "href": "view-source+http://example.net/\\a\\%2E\\b\\%2e.\\c?lang=fr#nav", - "pathname": "/\\a\\%2E\\b\\%2e.\\c" - } - }, - { - "comment": "UTF-8 percent encoding with the default encode set. Tabs and newlines are removed.", - "href": "a:/", - "new_value": "\u0000\u0001\t\n\r\u001f !\"#$%&'()*+,-./09:;<=>?@AZ[\\]^_`az{|}~\u007f\u0080\u0081Éé", - "expected": { - "href": "a:/%00%01%1F%20!%22%23$%&'()*+,-./09:;%3C=%3E%3F@AZ[\\]^_%60az%7B|%7D~%7F%C2%80%C2%81%C3%89%C3%A9", - "pathname": "/%00%01%1F%20!%22%23$%&'()*+,-./09:;%3C=%3E%3F@AZ[\\]^_%60az%7B|%7D~%7F%C2%80%C2%81%C3%89%C3%A9" - } - }, - { - "comment": "Bytes already percent-encoded are left as-is, including %2E outside dotted segments.", - "href": "http://example.net", - "new_value": "%2e%2E%c3%89té", - "expected": { - "href": "http://example.net/%2e%2E%c3%89t%C3%A9", - "pathname": "/%2e%2E%c3%89t%C3%A9" - } - }, - { - "comment": "? needs to be encoded", - "href": "http://example.net", - "new_value": "?", - "expected": { - "href": "http://example.net/%3F", - "pathname": "/%3F" - } - }, - { - "comment": "# needs to be encoded", - "href": "http://example.net", - "new_value": "#", - "expected": { - "href": "http://example.net/%23", - "pathname": "/%23" - } - }, - { - "comment": "? needs to be encoded, non-special scheme", - "href": "sc://example.net", - "new_value": "?", - "expected": { - "href": "sc://example.net/%3F", - "pathname": "/%3F" - } - }, - { - "comment": "# needs to be encoded, non-special scheme", - "href": "sc://example.net", - "new_value": "#", - "expected": { - "href": "sc://example.net/%23", - "pathname": "/%23" - } - }, - { - "comment": "? doesn't mess up encoding", - "href": "http://example.net", - "new_value": "/?é", - "expected": { - "href": "http://example.net/%3F%C3%A9", - "pathname": "/%3F%C3%A9" - } - }, - { - "comment": "# doesn't mess up encoding", - "href": "http://example.net", - "new_value": "/#é", - "expected": { - "href": "http://example.net/%23%C3%A9", - "pathname": "/%23%C3%A9" - } - }, - { - "comment": "File URLs and (back)slashes", - "href": "file://monkey/", - "new_value": "\\\\", - "expected": { - "href": "file://monkey//", - "pathname": "//" - } - }, - { - "comment": "File URLs and (back)slashes", - "href": "file:///unicorn", - "new_value": "//\\/", - "expected": { - "href": "file://////", - "pathname": "////" - } - }, - { - "comment": "File URLs and (back)slashes", - "href": "file:///unicorn", - "new_value": "//monkey/..//", - "expected": { - "href": "file://///", - "pathname": "///" - } - }, - { - "comment": "Serialize /. in path", - "href": "non-spec:/", - "new_value": "/.//p", - "expected": { - "href": "non-spec:/.//p", - "pathname": "//p" - } - }, - { - "href": "non-spec:/", - "new_value": "/..//p", - "expected": { - "href": "non-spec:/.//p", - "pathname": "//p" - } - }, - { - "href": "non-spec:/", - "new_value": "//p", - "expected": { - "href": "non-spec:/.//p", - "pathname": "//p" - } - }, - { - "comment": "Drop /. from path", - "href": "non-spec:/.//", - "new_value": "p", - "expected": { - "href": "non-spec:/p", - "pathname": "/p" - } - } - ], - "search": [ - { - "href": "https://example.net#nav", - "new_value": "lang=fr", - "expected": { - "href": "https://example.net/?lang=fr#nav", - "search": "?lang=fr" - } - }, - { - "href": "https://example.net?lang=en-US#nav", - "new_value": "lang=fr", - "expected": { - "href": "https://example.net/?lang=fr#nav", - "search": "?lang=fr" - } - }, - { - "href": "https://example.net?lang=en-US#nav", - "new_value": "?lang=fr", - "expected": { - "href": "https://example.net/?lang=fr#nav", - "search": "?lang=fr" - } - }, - { - "href": "https://example.net?lang=en-US#nav", - "new_value": "??lang=fr", - "expected": { - "href": "https://example.net/??lang=fr#nav", - "search": "??lang=fr" - } - }, - { - "href": "https://example.net?lang=en-US#nav", - "new_value": "?", - "expected": { - "href": "https://example.net/?#nav", - "search": "" - } - }, - { - "href": "https://example.net?lang=en-US#nav", - "new_value": "", - "expected": { - "href": "https://example.net/#nav", - "search": "" - } - }, - { - "href": "https://example.net?lang=en-US", - "new_value": "", - "expected": { - "href": "https://example.net/", - "search": "" - } - }, - { - "href": "https://example.net", - "new_value": "", - "expected": { - "href": "https://example.net/", - "search": "" - } - }, - { - "comment": "UTF-8 percent encoding with the query encode set. Tabs and newlines are removed.", - "href": "a:/", - "new_value": "\u0000\u0001\t\n\r\u001f !\"#$%&'()*+,-./09:;<=>?@AZ[\\]^_`az{|}~\u007f\u0080\u0081Éé", - "expected": { - "href": "a:/?%00%01%1F%20!%22%23$%&'()*+,-./09:;%3C=%3E?@AZ[\\]^_`az{|}~%7F%C2%80%C2%81%C3%89%C3%A9", - "search": "?%00%01%1F%20!%22%23$%&'()*+,-./09:;%3C=%3E?@AZ[\\]^_`az{|}~%7F%C2%80%C2%81%C3%89%C3%A9" - } - }, - { - "comment": "Bytes already percent-encoded are left as-is", - "href": "http://example.net", - "new_value": "%c3%89té", - "expected": { - "href": "http://example.net/?%c3%89t%C3%A9", - "search": "?%c3%89t%C3%A9" - } - } - ], - "hash": [ - { - "href": "https://example.net", - "new_value": "main", - "expected": { - "href": "https://example.net/#main", - "hash": "#main" - } - }, - { - "href": "https://example.net#nav", - "new_value": "main", - "expected": { - "href": "https://example.net/#main", - "hash": "#main" - } - }, - { - "href": "https://example.net?lang=en-US", - "new_value": "##nav", - "expected": { - "href": "https://example.net/?lang=en-US##nav", - "hash": "##nav" - } - }, - { - "href": "https://example.net?lang=en-US#nav", - "new_value": "#main", - "expected": { - "href": "https://example.net/?lang=en-US#main", - "hash": "#main" - } - }, - { - "href": "https://example.net?lang=en-US#nav", - "new_value": "#", - "expected": { - "href": "https://example.net/?lang=en-US#", - "hash": "" - } - }, - { - "href": "https://example.net?lang=en-US#nav", - "new_value": "", - "expected": { - "href": "https://example.net/?lang=en-US", - "hash": "" - } - }, - { - "href": "http://example.net", - "new_value": "#foo bar", - "expected": { - "href": "http://example.net/#foo%20bar", - "hash": "#foo%20bar" - } - }, - { - "href": "http://example.net", - "new_value": "#foo\"bar", - "expected": { - "href": "http://example.net/#foo%22bar", - "hash": "#foo%22bar" - } - }, - { - "href": "http://example.net", - "new_value": "#foobar", - "expected": { - "href": "http://example.net/#foo%3Ebar", - "hash": "#foo%3Ebar" - } - }, - { - "href": "http://example.net", - "new_value": "#foo`bar", - "expected": { - "href": "http://example.net/#foo%60bar", - "hash": "#foo%60bar" - } - }, - { - "comment": "Simple percent-encoding; tabs and newlines are removed", - "href": "a:/", - "new_value": "\u0000\u0001\t\n\r\u001f !\"#$%&'()*+,-./09:;<=>?@AZ[\\]^_`az{|}~\u007f\u0080\u0081Éé", - "expected": { - "href": "a:/#%00%01%1F%20!%22#$%&'()*+,-./09:;%3C=%3E?@AZ[\\]^_%60az{|}~%7F%C2%80%C2%81%C3%89%C3%A9", - "hash": "#%00%01%1F%20!%22#$%&'()*+,-./09:;%3C=%3E?@AZ[\\]^_%60az{|}~%7F%C2%80%C2%81%C3%89%C3%A9" - } - }, - { - "comment": "Percent-encode NULLs in fragment", - "href": "http://example.net", - "new_value": "a\u0000b", - "expected": { - "href": "http://example.net/#a%00b", - "hash": "#a%00b" - } - }, - { - "comment": "Percent-encode NULLs in fragment", - "href": "non-spec:/", - "new_value": "a\u0000b", - "expected": { - "href": "non-spec:/#a%00b", - "hash": "#a%00b" - } - }, - { - "comment": "Bytes already percent-encoded are left as-is", - "href": "http://example.net", - "new_value": "%c3%89té", - "expected": { - "href": "http://example.net/#%c3%89t%C3%A9", - "hash": "#%c3%89t%C3%A9" - } - }, - { - "href": "javascript:alert(1)", - "new_value": "castle", - "expected": { - "href": "javascript:alert(1)#castle", - "hash": "#castle" - } - } - ] -} diff --git a/test/wpt/urltestdata.json b/test/wpt/urltestdata.json deleted file mode 100644 index c7b6637c..00000000 --- a/test/wpt/urltestdata.json +++ /dev/null @@ -1,8211 +0,0 @@ -[ - "# Based on http://trac.webkit.org/browser/trunk/LayoutTests/fast/url/script-tests/segments.js", - { - "input": "http://example\t.\norg", - "base": "http://example.org/foo/bar", - "href": "http://example.org/", - "origin": "http://example.org", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.org", - "hostname": "example.org", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "http://user:pass@foo:21/bar;par?b#c", - "base": "http://example.org/foo/bar", - "href": "http://user:pass@foo:21/bar;par?b#c", - "origin": "http://foo:21", - "protocol": "http:", - "username": "user", - "password": "pass", - "host": "foo:21", - "hostname": "foo", - "port": "21", - "pathname": "/bar;par", - "search": "?b", - "hash": "#c" - }, - { - "input": "https://test:@test", - "base": "about:blank", - "href": "https://test@test/", - "origin": "https://test", - "protocol": "https:", - "username": "test", - "password": "", - "host": "test", - "hostname": "test", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "https://:@test", - "base": "about:blank", - "href": "https://test/", - "origin": "https://test", - "protocol": "https:", - "username": "", - "password": "", - "host": "test", - "hostname": "test", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "non-special://test:@test/x", - "base": "about:blank", - "href": "non-special://test@test/x", - "origin": "null", - "protocol": "non-special:", - "username": "test", - "password": "", - "host": "test", - "hostname": "test", - "port": "", - "pathname": "/x", - "search": "", - "hash": "" - }, - { - "input": "non-special://:@test/x", - "base": "about:blank", - "href": "non-special://test/x", - "origin": "null", - "protocol": "non-special:", - "username": "", - "password": "", - "host": "test", - "hostname": "test", - "port": "", - "pathname": "/x", - "search": "", - "hash": "" - }, - { - "input": "http:foo.com", - "base": "http://example.org/foo/bar", - "href": "http://example.org/foo/foo.com", - "origin": "http://example.org", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.org", - "hostname": "example.org", - "port": "", - "pathname": "/foo/foo.com", - "search": "", - "hash": "" - }, - { - "input": "\t :foo.com \n", - "base": "http://example.org/foo/bar", - "href": "http://example.org/foo/:foo.com", - "origin": "http://example.org", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.org", - "hostname": "example.org", - "port": "", - "pathname": "/foo/:foo.com", - "search": "", - "hash": "" - }, - { - "input": " foo.com ", - "base": "http://example.org/foo/bar", - "href": "http://example.org/foo/foo.com", - "origin": "http://example.org", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.org", - "hostname": "example.org", - "port": "", - "pathname": "/foo/foo.com", - "search": "", - "hash": "" - }, - { - "input": "a:\t foo.com", - "base": "http://example.org/foo/bar", - "href": "a: foo.com", - "origin": "null", - "protocol": "a:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": " foo.com", - "search": "", - "hash": "" - }, - { - "input": "http://f:21/ b ? d # e ", - "base": "http://example.org/foo/bar", - "href": "http://f:21/%20b%20?%20d%20#%20e", - "origin": "http://f:21", - "protocol": "http:", - "username": "", - "password": "", - "host": "f:21", - "hostname": "f", - "port": "21", - "pathname": "/%20b%20", - "search": "?%20d%20", - "hash": "#%20e" - }, - { - "input": "lolscheme:x x#x x", - "base": "about:blank", - "href": "lolscheme:x x#x%20x", - "protocol": "lolscheme:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "x x", - "search": "", - "hash": "#x%20x" - }, - { - "input": "http://f:/c", - "base": "http://example.org/foo/bar", - "href": "http://f/c", - "origin": "http://f", - "protocol": "http:", - "username": "", - "password": "", - "host": "f", - "hostname": "f", - "port": "", - "pathname": "/c", - "search": "", - "hash": "" - }, - { - "input": "http://f:0/c", - "base": "http://example.org/foo/bar", - "href": "http://f:0/c", - "origin": "http://f:0", - "protocol": "http:", - "username": "", - "password": "", - "host": "f:0", - "hostname": "f", - "port": "0", - "pathname": "/c", - "search": "", - "hash": "" - }, - { - "input": "http://f:00000000000000/c", - "base": "http://example.org/foo/bar", - "href": "http://f:0/c", - "origin": "http://f:0", - "protocol": "http:", - "username": "", - "password": "", - "host": "f:0", - "hostname": "f", - "port": "0", - "pathname": "/c", - "search": "", - "hash": "" - }, - { - "input": "http://f:00000000000000000000080/c", - "base": "http://example.org/foo/bar", - "href": "http://f/c", - "origin": "http://f", - "protocol": "http:", - "username": "", - "password": "", - "host": "f", - "hostname": "f", - "port": "", - "pathname": "/c", - "search": "", - "hash": "" - }, - { - "input": "http://f:b/c", - "base": "http://example.org/foo/bar", - "failure": true - }, - { - "input": "http://f: /c", - "base": "http://example.org/foo/bar", - "failure": true - }, - { - "input": "http://f:\n/c", - "base": "http://example.org/foo/bar", - "href": "http://f/c", - "origin": "http://f", - "protocol": "http:", - "username": "", - "password": "", - "host": "f", - "hostname": "f", - "port": "", - "pathname": "/c", - "search": "", - "hash": "" - }, - { - "input": "http://f:fifty-two/c", - "base": "http://example.org/foo/bar", - "failure": true - }, - { - "input": "http://f:999999/c", - "base": "http://example.org/foo/bar", - "failure": true - }, - { - "input": "non-special://f:999999/c", - "base": "http://example.org/foo/bar", - "failure": true - }, - { - "input": "http://f: 21 / b ? d # e ", - "base": "http://example.org/foo/bar", - "failure": true - }, - { - "input": "", - "base": "http://example.org/foo/bar", - "href": "http://example.org/foo/bar", - "origin": "http://example.org", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.org", - "hostname": "example.org", - "port": "", - "pathname": "/foo/bar", - "search": "", - "hash": "" - }, - { - "input": " \t", - "base": "http://example.org/foo/bar", - "href": "http://example.org/foo/bar", - "origin": "http://example.org", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.org", - "hostname": "example.org", - "port": "", - "pathname": "/foo/bar", - "search": "", - "hash": "" - }, - { - "input": ":foo.com/", - "base": "http://example.org/foo/bar", - "href": "http://example.org/foo/:foo.com/", - "origin": "http://example.org", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.org", - "hostname": "example.org", - "port": "", - "pathname": "/foo/:foo.com/", - "search": "", - "hash": "" - }, - { - "input": ":foo.com\\", - "base": "http://example.org/foo/bar", - "href": "http://example.org/foo/:foo.com/", - "origin": "http://example.org", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.org", - "hostname": "example.org", - "port": "", - "pathname": "/foo/:foo.com/", - "search": "", - "hash": "" - }, - { - "input": ":", - "base": "http://example.org/foo/bar", - "href": "http://example.org/foo/:", - "origin": "http://example.org", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.org", - "hostname": "example.org", - "port": "", - "pathname": "/foo/:", - "search": "", - "hash": "" - }, - { - "input": ":a", - "base": "http://example.org/foo/bar", - "href": "http://example.org/foo/:a", - "origin": "http://example.org", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.org", - "hostname": "example.org", - "port": "", - "pathname": "/foo/:a", - "search": "", - "hash": "" - }, - { - "input": ":/", - "base": "http://example.org/foo/bar", - "href": "http://example.org/foo/:/", - "origin": "http://example.org", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.org", - "hostname": "example.org", - "port": "", - "pathname": "/foo/:/", - "search": "", - "hash": "" - }, - { - "input": ":\\", - "base": "http://example.org/foo/bar", - "href": "http://example.org/foo/:/", - "origin": "http://example.org", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.org", - "hostname": "example.org", - "port": "", - "pathname": "/foo/:/", - "search": "", - "hash": "" - }, - { - "input": ":#", - "base": "http://example.org/foo/bar", - "href": "http://example.org/foo/:#", - "origin": "http://example.org", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.org", - "hostname": "example.org", - "port": "", - "pathname": "/foo/:", - "search": "", - "hash": "" - }, - { - "input": "#", - "base": "http://example.org/foo/bar", - "href": "http://example.org/foo/bar#", - "origin": "http://example.org", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.org", - "hostname": "example.org", - "port": "", - "pathname": "/foo/bar", - "search": "", - "hash": "" - }, - { - "input": "#/", - "base": "http://example.org/foo/bar", - "href": "http://example.org/foo/bar#/", - "origin": "http://example.org", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.org", - "hostname": "example.org", - "port": "", - "pathname": "/foo/bar", - "search": "", - "hash": "#/" - }, - { - "input": "#\\", - "base": "http://example.org/foo/bar", - "href": "http://example.org/foo/bar#\\", - "origin": "http://example.org", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.org", - "hostname": "example.org", - "port": "", - "pathname": "/foo/bar", - "search": "", - "hash": "#\\" - }, - { - "input": "#;?", - "base": "http://example.org/foo/bar", - "href": "http://example.org/foo/bar#;?", - "origin": "http://example.org", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.org", - "hostname": "example.org", - "port": "", - "pathname": "/foo/bar", - "search": "", - "hash": "#;?" - }, - { - "input": "?", - "base": "http://example.org/foo/bar", - "href": "http://example.org/foo/bar?", - "origin": "http://example.org", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.org", - "hostname": "example.org", - "port": "", - "pathname": "/foo/bar", - "search": "", - "hash": "" - }, - { - "input": "/", - "base": "http://example.org/foo/bar", - "href": "http://example.org/", - "origin": "http://example.org", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.org", - "hostname": "example.org", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": ":23", - "base": "http://example.org/foo/bar", - "href": "http://example.org/foo/:23", - "origin": "http://example.org", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.org", - "hostname": "example.org", - "port": "", - "pathname": "/foo/:23", - "search": "", - "hash": "" - }, - { - "input": "/:23", - "base": "http://example.org/foo/bar", - "href": "http://example.org/:23", - "origin": "http://example.org", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.org", - "hostname": "example.org", - "port": "", - "pathname": "/:23", - "search": "", - "hash": "" - }, - { - "input": "\\x", - "base": "http://example.org/foo/bar", - "href": "http://example.org/x", - "origin": "http://example.org", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.org", - "hostname": "example.org", - "port": "", - "pathname": "/x", - "search": "", - "hash": "" - }, - { - "input": "\\\\x\\hello", - "base": "http://example.org/foo/bar", - "href": "http://x/hello", - "origin": "http://x", - "protocol": "http:", - "username": "", - "password": "", - "host": "x", - "hostname": "x", - "port": "", - "pathname": "/hello", - "search": "", - "hash": "" - }, - { - "input": "::", - "base": "http://example.org/foo/bar", - "href": "http://example.org/foo/::", - "origin": "http://example.org", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.org", - "hostname": "example.org", - "port": "", - "pathname": "/foo/::", - "search": "", - "hash": "" - }, - { - "input": "::23", - "base": "http://example.org/foo/bar", - "href": "http://example.org/foo/::23", - "origin": "http://example.org", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.org", - "hostname": "example.org", - "port": "", - "pathname": "/foo/::23", - "search": "", - "hash": "" - }, - { - "input": "foo://", - "base": "http://example.org/foo/bar", - "href": "foo://", - "origin": "null", - "protocol": "foo:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "", - "search": "", - "hash": "" - }, - { - "input": "http://a:b@c:29/d", - "base": "http://example.org/foo/bar", - "href": "http://a:b@c:29/d", - "origin": "http://c:29", - "protocol": "http:", - "username": "a", - "password": "b", - "host": "c:29", - "hostname": "c", - "port": "29", - "pathname": "/d", - "search": "", - "hash": "" - }, - { - "input": "http::@c:29", - "base": "http://example.org/foo/bar", - "href": "http://example.org/foo/:@c:29", - "origin": "http://example.org", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.org", - "hostname": "example.org", - "port": "", - "pathname": "/foo/:@c:29", - "search": "", - "hash": "" - }, - { - "input": "http://&a:foo(b]c@d:2/", - "base": "http://example.org/foo/bar", - "href": "http://&a:foo(b%5Dc@d:2/", - "origin": "http://d:2", - "protocol": "http:", - "username": "&a", - "password": "foo(b%5Dc", - "host": "d:2", - "hostname": "d", - "port": "2", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "http://::@c@d:2", - "base": "http://example.org/foo/bar", - "href": "http://:%3A%40c@d:2/", - "origin": "http://d:2", - "protocol": "http:", - "username": "", - "password": "%3A%40c", - "host": "d:2", - "hostname": "d", - "port": "2", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "http://foo.com:b@d/", - "base": "http://example.org/foo/bar", - "href": "http://foo.com:b@d/", - "origin": "http://d", - "protocol": "http:", - "username": "foo.com", - "password": "b", - "host": "d", - "hostname": "d", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "http://foo.com/\\@", - "base": "http://example.org/foo/bar", - "href": "http://foo.com//@", - "origin": "http://foo.com", - "protocol": "http:", - "username": "", - "password": "", - "host": "foo.com", - "hostname": "foo.com", - "port": "", - "pathname": "//@", - "search": "", - "hash": "" - }, - { - "input": "http:\\\\foo.com\\", - "base": "http://example.org/foo/bar", - "href": "http://foo.com/", - "origin": "http://foo.com", - "protocol": "http:", - "username": "", - "password": "", - "host": "foo.com", - "hostname": "foo.com", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "http:\\\\a\\b:c\\d@foo.com\\", - "base": "http://example.org/foo/bar", - "href": "http://a/b:c/d@foo.com/", - "origin": "http://a", - "protocol": "http:", - "username": "", - "password": "", - "host": "a", - "hostname": "a", - "port": "", - "pathname": "/b:c/d@foo.com/", - "search": "", - "hash": "" - }, - { - "input": "foo:/", - "base": "http://example.org/foo/bar", - "href": "foo:/", - "origin": "null", - "protocol": "foo:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "foo:/bar.com/", - "base": "http://example.org/foo/bar", - "href": "foo:/bar.com/", - "origin": "null", - "protocol": "foo:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/bar.com/", - "search": "", - "hash": "" - }, - { - "input": "foo://///////", - "base": "http://example.org/foo/bar", - "href": "foo://///////", - "origin": "null", - "protocol": "foo:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "///////", - "search": "", - "hash": "" - }, - { - "input": "foo://///////bar.com/", - "base": "http://example.org/foo/bar", - "href": "foo://///////bar.com/", - "origin": "null", - "protocol": "foo:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "///////bar.com/", - "search": "", - "hash": "" - }, - { - "input": "foo:////://///", - "base": "http://example.org/foo/bar", - "href": "foo:////://///", - "origin": "null", - "protocol": "foo:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "//://///", - "search": "", - "hash": "" - }, - { - "input": "c:/foo", - "base": "http://example.org/foo/bar", - "href": "c:/foo", - "origin": "null", - "protocol": "c:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/foo", - "search": "", - "hash": "" - }, - { - "input": "//foo/bar", - "base": "http://example.org/foo/bar", - "href": "http://foo/bar", - "origin": "http://foo", - "protocol": "http:", - "username": "", - "password": "", - "host": "foo", - "hostname": "foo", - "port": "", - "pathname": "/bar", - "search": "", - "hash": "" - }, - { - "input": "http://foo/path;a??e#f#g", - "base": "http://example.org/foo/bar", - "href": "http://foo/path;a??e#f#g", - "origin": "http://foo", - "protocol": "http:", - "username": "", - "password": "", - "host": "foo", - "hostname": "foo", - "port": "", - "pathname": "/path;a", - "search": "??e", - "hash": "#f#g" - }, - { - "input": "http://foo/abcd?efgh?ijkl", - "base": "http://example.org/foo/bar", - "href": "http://foo/abcd?efgh?ijkl", - "origin": "http://foo", - "protocol": "http:", - "username": "", - "password": "", - "host": "foo", - "hostname": "foo", - "port": "", - "pathname": "/abcd", - "search": "?efgh?ijkl", - "hash": "" - }, - { - "input": "http://foo/abcd#foo?bar", - "base": "http://example.org/foo/bar", - "href": "http://foo/abcd#foo?bar", - "origin": "http://foo", - "protocol": "http:", - "username": "", - "password": "", - "host": "foo", - "hostname": "foo", - "port": "", - "pathname": "/abcd", - "search": "", - "hash": "#foo?bar" - }, - { - "input": "[61:24:74]:98", - "base": "http://example.org/foo/bar", - "href": "http://example.org/foo/[61:24:74]:98", - "origin": "http://example.org", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.org", - "hostname": "example.org", - "port": "", - "pathname": "/foo/[61:24:74]:98", - "search": "", - "hash": "" - }, - { - "input": "http:[61:27]/:foo", - "base": "http://example.org/foo/bar", - "href": "http://example.org/foo/[61:27]/:foo", - "origin": "http://example.org", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.org", - "hostname": "example.org", - "port": "", - "pathname": "/foo/[61:27]/:foo", - "search": "", - "hash": "" - }, - { - "input": "http://[1::2]:3:4", - "base": "http://example.org/foo/bar", - "failure": true - }, - { - "input": "http://2001::1", - "base": "http://example.org/foo/bar", - "failure": true - }, - { - "input": "http://2001::1]", - "base": "http://example.org/foo/bar", - "failure": true - }, - { - "input": "http://2001::1]:80", - "base": "http://example.org/foo/bar", - "failure": true - }, - { - "input": "http://[2001::1]", - "base": "http://example.org/foo/bar", - "href": "http://[2001::1]/", - "origin": "http://[2001::1]", - "protocol": "http:", - "username": "", - "password": "", - "host": "[2001::1]", - "hostname": "[2001::1]", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "http://[::127.0.0.1]", - "base": "http://example.org/foo/bar", - "href": "http://[::7f00:1]/", - "origin": "http://[::7f00:1]", - "protocol": "http:", - "username": "", - "password": "", - "host": "[::7f00:1]", - "hostname": "[::7f00:1]", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "http://[0:0:0:0:0:0:13.1.68.3]", - "base": "http://example.org/foo/bar", - "href": "http://[::d01:4403]/", - "origin": "http://[::d01:4403]", - "protocol": "http:", - "username": "", - "password": "", - "host": "[::d01:4403]", - "hostname": "[::d01:4403]", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "http://[2001::1]:80", - "base": "http://example.org/foo/bar", - "href": "http://[2001::1]/", - "origin": "http://[2001::1]", - "protocol": "http:", - "username": "", - "password": "", - "host": "[2001::1]", - "hostname": "[2001::1]", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "http:/example.com/", - "base": "http://example.org/foo/bar", - "href": "http://example.org/example.com/", - "origin": "http://example.org", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.org", - "hostname": "example.org", - "port": "", - "pathname": "/example.com/", - "search": "", - "hash": "" - }, - { - "input": "ftp:/example.com/", - "base": "http://example.org/foo/bar", - "href": "ftp://example.com/", - "origin": "ftp://example.com", - "protocol": "ftp:", - "username": "", - "password": "", - "host": "example.com", - "hostname": "example.com", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "https:/example.com/", - "base": "http://example.org/foo/bar", - "href": "https://example.com/", - "origin": "https://example.com", - "protocol": "https:", - "username": "", - "password": "", - "host": "example.com", - "hostname": "example.com", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "madeupscheme:/example.com/", - "base": "http://example.org/foo/bar", - "href": "madeupscheme:/example.com/", - "origin": "null", - "protocol": "madeupscheme:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/example.com/", - "search": "", - "hash": "" - }, - { - "input": "file:/example.com/", - "base": "http://example.org/foo/bar", - "href": "file:///example.com/", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/example.com/", - "search": "", - "hash": "" - }, - { - "input": "file://example:1/", - "base": "about:blank", - "failure": true - }, - { - "input": "file://example:test/", - "base": "about:blank", - "failure": true - }, - { - "input": "file://example%/", - "base": "about:blank", - "failure": true - }, - { - "input": "file://[example]/", - "base": "about:blank", - "failure": true - }, - { - "input": "ftps:/example.com/", - "base": "http://example.org/foo/bar", - "href": "ftps:/example.com/", - "origin": "null", - "protocol": "ftps:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/example.com/", - "search": "", - "hash": "" - }, - { - "input": "gopher:/example.com/", - "base": "http://example.org/foo/bar", - "href": "gopher:/example.com/", - "origin": "null", - "protocol": "gopher:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/example.com/", - "search": "", - "hash": "" - }, - { - "input": "ws:/example.com/", - "base": "http://example.org/foo/bar", - "href": "ws://example.com/", - "origin": "ws://example.com", - "protocol": "ws:", - "username": "", - "password": "", - "host": "example.com", - "hostname": "example.com", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "wss:/example.com/", - "base": "http://example.org/foo/bar", - "href": "wss://example.com/", - "origin": "wss://example.com", - "protocol": "wss:", - "username": "", - "password": "", - "host": "example.com", - "hostname": "example.com", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "data:/example.com/", - "base": "http://example.org/foo/bar", - "href": "data:/example.com/", - "origin": "null", - "protocol": "data:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/example.com/", - "search": "", - "hash": "" - }, - { - "input": "javascript:/example.com/", - "base": "http://example.org/foo/bar", - "href": "javascript:/example.com/", - "origin": "null", - "protocol": "javascript:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/example.com/", - "search": "", - "hash": "" - }, - { - "input": "mailto:/example.com/", - "base": "http://example.org/foo/bar", - "href": "mailto:/example.com/", - "origin": "null", - "protocol": "mailto:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/example.com/", - "search": "", - "hash": "" - }, - { - "input": "http:example.com/", - "base": "http://example.org/foo/bar", - "href": "http://example.org/foo/example.com/", - "origin": "http://example.org", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.org", - "hostname": "example.org", - "port": "", - "pathname": "/foo/example.com/", - "search": "", - "hash": "" - }, - { - "input": "ftp:example.com/", - "base": "http://example.org/foo/bar", - "href": "ftp://example.com/", - "origin": "ftp://example.com", - "protocol": "ftp:", - "username": "", - "password": "", - "host": "example.com", - "hostname": "example.com", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "https:example.com/", - "base": "http://example.org/foo/bar", - "href": "https://example.com/", - "origin": "https://example.com", - "protocol": "https:", - "username": "", - "password": "", - "host": "example.com", - "hostname": "example.com", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "madeupscheme:example.com/", - "base": "http://example.org/foo/bar", - "href": "madeupscheme:example.com/", - "origin": "null", - "protocol": "madeupscheme:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "example.com/", - "search": "", - "hash": "" - }, - { - "input": "ftps:example.com/", - "base": "http://example.org/foo/bar", - "href": "ftps:example.com/", - "origin": "null", - "protocol": "ftps:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "example.com/", - "search": "", - "hash": "" - }, - { - "input": "gopher:example.com/", - "base": "http://example.org/foo/bar", - "href": "gopher:example.com/", - "origin": "null", - "protocol": "gopher:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "example.com/", - "search": "", - "hash": "" - }, - { - "input": "ws:example.com/", - "base": "http://example.org/foo/bar", - "href": "ws://example.com/", - "origin": "ws://example.com", - "protocol": "ws:", - "username": "", - "password": "", - "host": "example.com", - "hostname": "example.com", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "wss:example.com/", - "base": "http://example.org/foo/bar", - "href": "wss://example.com/", - "origin": "wss://example.com", - "protocol": "wss:", - "username": "", - "password": "", - "host": "example.com", - "hostname": "example.com", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "data:example.com/", - "base": "http://example.org/foo/bar", - "href": "data:example.com/", - "origin": "null", - "protocol": "data:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "example.com/", - "search": "", - "hash": "" - }, - { - "input": "javascript:example.com/", - "base": "http://example.org/foo/bar", - "href": "javascript:example.com/", - "origin": "null", - "protocol": "javascript:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "example.com/", - "search": "", - "hash": "" - }, - { - "input": "mailto:example.com/", - "base": "http://example.org/foo/bar", - "href": "mailto:example.com/", - "origin": "null", - "protocol": "mailto:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "example.com/", - "search": "", - "hash": "" - }, - { - "input": "/a/b/c", - "base": "http://example.org/foo/bar", - "href": "http://example.org/a/b/c", - "origin": "http://example.org", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.org", - "hostname": "example.org", - "port": "", - "pathname": "/a/b/c", - "search": "", - "hash": "" - }, - { - "input": "/a/ /c", - "base": "http://example.org/foo/bar", - "href": "http://example.org/a/%20/c", - "origin": "http://example.org", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.org", - "hostname": "example.org", - "port": "", - "pathname": "/a/%20/c", - "search": "", - "hash": "" - }, - { - "input": "/a%2fc", - "base": "http://example.org/foo/bar", - "href": "http://example.org/a%2fc", - "origin": "http://example.org", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.org", - "hostname": "example.org", - "port": "", - "pathname": "/a%2fc", - "search": "", - "hash": "" - }, - { - "input": "/a/%2f/c", - "base": "http://example.org/foo/bar", - "href": "http://example.org/a/%2f/c", - "origin": "http://example.org", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.org", - "hostname": "example.org", - "port": "", - "pathname": "/a/%2f/c", - "search": "", - "hash": "" - }, - { - "input": "#β", - "base": "http://example.org/foo/bar", - "href": "http://example.org/foo/bar#%CE%B2", - "origin": "http://example.org", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.org", - "hostname": "example.org", - "port": "", - "pathname": "/foo/bar", - "search": "", - "hash": "#%CE%B2" - }, - { - "input": "data:text/html,test#test", - "base": "http://example.org/foo/bar", - "href": "data:text/html,test#test", - "origin": "null", - "protocol": "data:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "text/html,test", - "search": "", - "hash": "#test" - }, - { - "input": "tel:1234567890", - "base": "http://example.org/foo/bar", - "href": "tel:1234567890", - "origin": "null", - "protocol": "tel:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "1234567890", - "search": "", - "hash": "" - }, - "# Based on https://felixfbecker.github.io/whatwg-url-custom-host-repro/", - { - "input": "ssh://example.com/foo/bar.git", - "base": "http://example.org/", - "href": "ssh://example.com/foo/bar.git", - "origin": "null", - "protocol": "ssh:", - "username": "", - "password": "", - "host": "example.com", - "hostname": "example.com", - "port": "", - "pathname": "/foo/bar.git", - "search": "", - "hash": "" - }, - "# Based on http://trac.webkit.org/browser/trunk/LayoutTests/fast/url/file.html", - { - "input": "file:c:\\foo\\bar.html", - "base": "file:///tmp/mock/path", - "href": "file:///c:/foo/bar.html", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/c:/foo/bar.html", - "search": "", - "hash": "" - }, - { - "input": " File:c|////foo\\bar.html", - "base": "file:///tmp/mock/path", - "href": "file:///c:////foo/bar.html", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/c:////foo/bar.html", - "search": "", - "hash": "" - }, - { - "input": "C|/foo/bar", - "base": "file:///tmp/mock/path", - "href": "file:///C:/foo/bar", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/C:/foo/bar", - "search": "", - "hash": "" - }, - { - "input": "/C|\\foo\\bar", - "base": "file:///tmp/mock/path", - "href": "file:///C:/foo/bar", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/C:/foo/bar", - "search": "", - "hash": "" - }, - { - "input": "//C|/foo/bar", - "base": "file:///tmp/mock/path", - "href": "file:///C:/foo/bar", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/C:/foo/bar", - "search": "", - "hash": "" - }, - { - "input": "//server/file", - "base": "file:///tmp/mock/path", - "href": "file://server/file", - "protocol": "file:", - "username": "", - "password": "", - "host": "server", - "hostname": "server", - "port": "", - "pathname": "/file", - "search": "", - "hash": "" - }, - { - "input": "\\\\server\\file", - "base": "file:///tmp/mock/path", - "href": "file://server/file", - "protocol": "file:", - "username": "", - "password": "", - "host": "server", - "hostname": "server", - "port": "", - "pathname": "/file", - "search": "", - "hash": "" - }, - { - "input": "/\\server/file", - "base": "file:///tmp/mock/path", - "href": "file://server/file", - "protocol": "file:", - "username": "", - "password": "", - "host": "server", - "hostname": "server", - "port": "", - "pathname": "/file", - "search": "", - "hash": "" - }, - { - "input": "file:///foo/bar.txt", - "base": "file:///tmp/mock/path", - "href": "file:///foo/bar.txt", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/foo/bar.txt", - "search": "", - "hash": "" - }, - { - "input": "file:///home/me", - "base": "file:///tmp/mock/path", - "href": "file:///home/me", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/home/me", - "search": "", - "hash": "" - }, - { - "input": "//", - "base": "file:///tmp/mock/path", - "href": "file:///", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "///", - "base": "file:///tmp/mock/path", - "href": "file:///", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "///test", - "base": "file:///tmp/mock/path", - "href": "file:///test", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/test", - "search": "", - "hash": "" - }, - { - "input": "file://test", - "base": "file:///tmp/mock/path", - "href": "file://test/", - "protocol": "file:", - "username": "", - "password": "", - "host": "test", - "hostname": "test", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "file://localhost", - "base": "file:///tmp/mock/path", - "href": "file:///", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "file://localhost/", - "base": "file:///tmp/mock/path", - "href": "file:///", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "file://localhost/test", - "base": "file:///tmp/mock/path", - "href": "file:///test", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/test", - "search": "", - "hash": "" - }, - { - "input": "test", - "base": "file:///tmp/mock/path", - "href": "file:///tmp/mock/test", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/tmp/mock/test", - "search": "", - "hash": "" - }, - { - "input": "file:test", - "base": "file:///tmp/mock/path", - "href": "file:///tmp/mock/test", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/tmp/mock/test", - "search": "", - "hash": "" - }, - "# Based on http://trac.webkit.org/browser/trunk/LayoutTests/fast/url/script-tests/path.js", - { - "input": "http://example.com/././foo", - "base": "about:blank", - "href": "http://example.com/foo", - "origin": "http://example.com", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.com", - "hostname": "example.com", - "port": "", - "pathname": "/foo", - "search": "", - "hash": "" - }, - { - "input": "http://example.com/./.foo", - "base": "about:blank", - "href": "http://example.com/.foo", - "origin": "http://example.com", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.com", - "hostname": "example.com", - "port": "", - "pathname": "/.foo", - "search": "", - "hash": "" - }, - { - "input": "http://example.com/foo/.", - "base": "about:blank", - "href": "http://example.com/foo/", - "origin": "http://example.com", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.com", - "hostname": "example.com", - "port": "", - "pathname": "/foo/", - "search": "", - "hash": "" - }, - { - "input": "http://example.com/foo/./", - "base": "about:blank", - "href": "http://example.com/foo/", - "origin": "http://example.com", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.com", - "hostname": "example.com", - "port": "", - "pathname": "/foo/", - "search": "", - "hash": "" - }, - { - "input": "http://example.com/foo/bar/..", - "base": "about:blank", - "href": "http://example.com/foo/", - "origin": "http://example.com", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.com", - "hostname": "example.com", - "port": "", - "pathname": "/foo/", - "search": "", - "hash": "" - }, - { - "input": "http://example.com/foo/bar/../", - "base": "about:blank", - "href": "http://example.com/foo/", - "origin": "http://example.com", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.com", - "hostname": "example.com", - "port": "", - "pathname": "/foo/", - "search": "", - "hash": "" - }, - { - "input": "http://example.com/foo/..bar", - "base": "about:blank", - "href": "http://example.com/foo/..bar", - "origin": "http://example.com", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.com", - "hostname": "example.com", - "port": "", - "pathname": "/foo/..bar", - "search": "", - "hash": "" - }, - { - "input": "http://example.com/foo/bar/../ton", - "base": "about:blank", - "href": "http://example.com/foo/ton", - "origin": "http://example.com", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.com", - "hostname": "example.com", - "port": "", - "pathname": "/foo/ton", - "search": "", - "hash": "" - }, - { - "input": "http://example.com/foo/bar/../ton/../../a", - "base": "about:blank", - "href": "http://example.com/a", - "origin": "http://example.com", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.com", - "hostname": "example.com", - "port": "", - "pathname": "/a", - "search": "", - "hash": "" - }, - { - "input": "http://example.com/foo/../../..", - "base": "about:blank", - "href": "http://example.com/", - "origin": "http://example.com", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.com", - "hostname": "example.com", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "http://example.com/foo/../../../ton", - "base": "about:blank", - "href": "http://example.com/ton", - "origin": "http://example.com", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.com", - "hostname": "example.com", - "port": "", - "pathname": "/ton", - "search": "", - "hash": "" - }, - { - "input": "http://example.com/foo/%2e", - "base": "about:blank", - "href": "http://example.com/foo/", - "origin": "http://example.com", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.com", - "hostname": "example.com", - "port": "", - "pathname": "/foo/", - "search": "", - "hash": "" - }, - { - "input": "http://example.com/foo/%2e%2", - "base": "about:blank", - "href": "http://example.com/foo/%2e%2", - "origin": "http://example.com", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.com", - "hostname": "example.com", - "port": "", - "pathname": "/foo/%2e%2", - "search": "", - "hash": "" - }, - { - "input": "http://example.com/foo/%2e./%2e%2e/.%2e/%2e.bar", - "base": "about:blank", - "href": "http://example.com/%2e.bar", - "origin": "http://example.com", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.com", - "hostname": "example.com", - "port": "", - "pathname": "/%2e.bar", - "search": "", - "hash": "" - }, - { - "input": "http://example.com////../..", - "base": "about:blank", - "href": "http://example.com//", - "origin": "http://example.com", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.com", - "hostname": "example.com", - "port": "", - "pathname": "//", - "search": "", - "hash": "" - }, - { - "input": "http://example.com/foo/bar//../..", - "base": "about:blank", - "href": "http://example.com/foo/", - "origin": "http://example.com", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.com", - "hostname": "example.com", - "port": "", - "pathname": "/foo/", - "search": "", - "hash": "" - }, - { - "input": "http://example.com/foo/bar//..", - "base": "about:blank", - "href": "http://example.com/foo/bar/", - "origin": "http://example.com", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.com", - "hostname": "example.com", - "port": "", - "pathname": "/foo/bar/", - "search": "", - "hash": "" - }, - { - "input": "http://example.com/foo", - "base": "about:blank", - "href": "http://example.com/foo", - "origin": "http://example.com", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.com", - "hostname": "example.com", - "port": "", - "pathname": "/foo", - "search": "", - "hash": "" - }, - { - "input": "http://example.com/%20foo", - "base": "about:blank", - "href": "http://example.com/%20foo", - "origin": "http://example.com", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.com", - "hostname": "example.com", - "port": "", - "pathname": "/%20foo", - "search": "", - "hash": "" - }, - { - "input": "http://example.com/foo%", - "base": "about:blank", - "href": "http://example.com/foo%", - "origin": "http://example.com", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.com", - "hostname": "example.com", - "port": "", - "pathname": "/foo%", - "search": "", - "hash": "" - }, - { - "input": "http://example.com/foo%2", - "base": "about:blank", - "href": "http://example.com/foo%2", - "origin": "http://example.com", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.com", - "hostname": "example.com", - "port": "", - "pathname": "/foo%2", - "search": "", - "hash": "" - }, - { - "input": "http://example.com/foo%2zbar", - "base": "about:blank", - "href": "http://example.com/foo%2zbar", - "origin": "http://example.com", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.com", - "hostname": "example.com", - "port": "", - "pathname": "/foo%2zbar", - "search": "", - "hash": "" - }, - { - "input": "http://example.com/foo%2©zbar", - "base": "about:blank", - "href": "http://example.com/foo%2%C3%82%C2%A9zbar", - "origin": "http://example.com", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.com", - "hostname": "example.com", - "port": "", - "pathname": "/foo%2%C3%82%C2%A9zbar", - "search": "", - "hash": "" - }, - { - "input": "http://example.com/foo%41%7a", - "base": "about:blank", - "href": "http://example.com/foo%41%7a", - "origin": "http://example.com", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.com", - "hostname": "example.com", - "port": "", - "pathname": "/foo%41%7a", - "search": "", - "hash": "" - }, - { - "input": "http://example.com/foo\t\u0091%91", - "base": "about:blank", - "href": "http://example.com/foo%C2%91%91", - "origin": "http://example.com", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.com", - "hostname": "example.com", - "port": "", - "pathname": "/foo%C2%91%91", - "search": "", - "hash": "" - }, - { - "input": "http://example.com/foo%00%51", - "base": "about:blank", - "href": "http://example.com/foo%00%51", - "origin": "http://example.com", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.com", - "hostname": "example.com", - "port": "", - "pathname": "/foo%00%51", - "search": "", - "hash": "" - }, - { - "input": "http://example.com/(%28:%3A%29)", - "base": "about:blank", - "href": "http://example.com/(%28:%3A%29)", - "origin": "http://example.com", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.com", - "hostname": "example.com", - "port": "", - "pathname": "/(%28:%3A%29)", - "search": "", - "hash": "" - }, - { - "input": "http://example.com/%3A%3a%3C%3c", - "base": "about:blank", - "href": "http://example.com/%3A%3a%3C%3c", - "origin": "http://example.com", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.com", - "hostname": "example.com", - "port": "", - "pathname": "/%3A%3a%3C%3c", - "search": "", - "hash": "" - }, - { - "input": "http://example.com/foo\tbar", - "base": "about:blank", - "href": "http://example.com/foobar", - "origin": "http://example.com", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.com", - "hostname": "example.com", - "port": "", - "pathname": "/foobar", - "search": "", - "hash": "" - }, - { - "input": "http://example.com\\\\foo\\\\bar", - "base": "about:blank", - "href": "http://example.com//foo//bar", - "origin": "http://example.com", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.com", - "hostname": "example.com", - "port": "", - "pathname": "//foo//bar", - "search": "", - "hash": "" - }, - { - "input": "http://example.com/%7Ffp3%3Eju%3Dduvgw%3Dd", - "base": "about:blank", - "href": "http://example.com/%7Ffp3%3Eju%3Dduvgw%3Dd", - "origin": "http://example.com", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.com", - "hostname": "example.com", - "port": "", - "pathname": "/%7Ffp3%3Eju%3Dduvgw%3Dd", - "search": "", - "hash": "" - }, - { - "input": "http://example.com/@asdf%40", - "base": "about:blank", - "href": "http://example.com/@asdf%40", - "origin": "http://example.com", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.com", - "hostname": "example.com", - "port": "", - "pathname": "/@asdf%40", - "search": "", - "hash": "" - }, - { - "input": "http://example.com/你好你好", - "base": "about:blank", - "href": "http://example.com/%E4%BD%A0%E5%A5%BD%E4%BD%A0%E5%A5%BD", - "origin": "http://example.com", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.com", - "hostname": "example.com", - "port": "", - "pathname": "/%E4%BD%A0%E5%A5%BD%E4%BD%A0%E5%A5%BD", - "search": "", - "hash": "" - }, - { - "input": "http://example.com/‥/foo", - "base": "about:blank", - "href": "http://example.com/%E2%80%A5/foo", - "origin": "http://example.com", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.com", - "hostname": "example.com", - "port": "", - "pathname": "/%E2%80%A5/foo", - "search": "", - "hash": "" - }, - { - "input": "http://example.com//foo", - "base": "about:blank", - "href": "http://example.com/%EF%BB%BF/foo", - "origin": "http://example.com", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.com", - "hostname": "example.com", - "port": "", - "pathname": "/%EF%BB%BF/foo", - "search": "", - "hash": "" - }, - { - "input": "http://example.com/‮/foo/‭/bar", - "base": "about:blank", - "href": "http://example.com/%E2%80%AE/foo/%E2%80%AD/bar", - "origin": "http://example.com", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.com", - "hostname": "example.com", - "port": "", - "pathname": "/%E2%80%AE/foo/%E2%80%AD/bar", - "search": "", - "hash": "" - }, - "# Based on http://trac.webkit.org/browser/trunk/LayoutTests/fast/url/script-tests/relative.js", - { - "input": "http://www.google.com/foo?bar=baz#", - "base": "about:blank", - "href": "http://www.google.com/foo?bar=baz#", - "origin": "http://www.google.com", - "protocol": "http:", - "username": "", - "password": "", - "host": "www.google.com", - "hostname": "www.google.com", - "port": "", - "pathname": "/foo", - "search": "?bar=baz", - "hash": "" - }, - { - "input": "http://www.google.com/foo?bar=baz# »", - "base": "about:blank", - "href": "http://www.google.com/foo?bar=baz#%20%C2%BB", - "origin": "http://www.google.com", - "protocol": "http:", - "username": "", - "password": "", - "host": "www.google.com", - "hostname": "www.google.com", - "port": "", - "pathname": "/foo", - "search": "?bar=baz", - "hash": "#%20%C2%BB" - }, - { - "input": "data:test# »", - "base": "about:blank", - "href": "data:test#%20%C2%BB", - "origin": "null", - "protocol": "data:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "test", - "search": "", - "hash": "#%20%C2%BB" - }, - { - "input": "http://www.google.com", - "base": "about:blank", - "href": "http://www.google.com/", - "origin": "http://www.google.com", - "protocol": "http:", - "username": "", - "password": "", - "host": "www.google.com", - "hostname": "www.google.com", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "http://192.0x00A80001", - "base": "about:blank", - "href": "http://192.168.0.1/", - "origin": "http://192.168.0.1", - "protocol": "http:", - "username": "", - "password": "", - "host": "192.168.0.1", - "hostname": "192.168.0.1", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "http://www/foo%2Ehtml", - "base": "about:blank", - "href": "http://www/foo%2Ehtml", - "origin": "http://www", - "protocol": "http:", - "username": "", - "password": "", - "host": "www", - "hostname": "www", - "port": "", - "pathname": "/foo%2Ehtml", - "search": "", - "hash": "" - }, - { - "input": "http://www/foo/%2E/html", - "base": "about:blank", - "href": "http://www/foo/html", - "origin": "http://www", - "protocol": "http:", - "username": "", - "password": "", - "host": "www", - "hostname": "www", - "port": "", - "pathname": "/foo/html", - "search": "", - "hash": "" - }, - { - "input": "http://user:pass@/", - "base": "about:blank", - "failure": true - }, - { - "input": "http://%25DOMAIN:foobar@foodomain.com/", - "base": "about:blank", - "href": "http://%25DOMAIN:foobar@foodomain.com/", - "origin": "http://foodomain.com", - "protocol": "http:", - "username": "%25DOMAIN", - "password": "foobar", - "host": "foodomain.com", - "hostname": "foodomain.com", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "http:\\\\www.google.com\\foo", - "base": "about:blank", - "href": "http://www.google.com/foo", - "origin": "http://www.google.com", - "protocol": "http:", - "username": "", - "password": "", - "host": "www.google.com", - "hostname": "www.google.com", - "port": "", - "pathname": "/foo", - "search": "", - "hash": "" - }, - { - "input": "http://foo:80/", - "base": "about:blank", - "href": "http://foo/", - "origin": "http://foo", - "protocol": "http:", - "username": "", - "password": "", - "host": "foo", - "hostname": "foo", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "http://foo:81/", - "base": "about:blank", - "href": "http://foo:81/", - "origin": "http://foo:81", - "protocol": "http:", - "username": "", - "password": "", - "host": "foo:81", - "hostname": "foo", - "port": "81", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "httpa://foo:80/", - "base": "about:blank", - "href": "httpa://foo:80/", - "origin": "null", - "protocol": "httpa:", - "username": "", - "password": "", - "host": "foo:80", - "hostname": "foo", - "port": "80", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "http://foo:-80/", - "base": "about:blank", - "failure": true - }, - { - "input": "https://foo:443/", - "base": "about:blank", - "href": "https://foo/", - "origin": "https://foo", - "protocol": "https:", - "username": "", - "password": "", - "host": "foo", - "hostname": "foo", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "https://foo:80/", - "base": "about:blank", - "href": "https://foo:80/", - "origin": "https://foo:80", - "protocol": "https:", - "username": "", - "password": "", - "host": "foo:80", - "hostname": "foo", - "port": "80", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "ftp://foo:21/", - "base": "about:blank", - "href": "ftp://foo/", - "origin": "ftp://foo", - "protocol": "ftp:", - "username": "", - "password": "", - "host": "foo", - "hostname": "foo", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "ftp://foo:80/", - "base": "about:blank", - "href": "ftp://foo:80/", - "origin": "ftp://foo:80", - "protocol": "ftp:", - "username": "", - "password": "", - "host": "foo:80", - "hostname": "foo", - "port": "80", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "gopher://foo:70/", - "base": "about:blank", - "href": "gopher://foo:70/", - "origin": "null", - "protocol": "gopher:", - "username": "", - "password": "", - "host": "foo:70", - "hostname": "foo", - "port": "70", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "gopher://foo:443/", - "base": "about:blank", - "href": "gopher://foo:443/", - "origin": "null", - "protocol": "gopher:", - "username": "", - "password": "", - "host": "foo:443", - "hostname": "foo", - "port": "443", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "ws://foo:80/", - "base": "about:blank", - "href": "ws://foo/", - "origin": "ws://foo", - "protocol": "ws:", - "username": "", - "password": "", - "host": "foo", - "hostname": "foo", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "ws://foo:81/", - "base": "about:blank", - "href": "ws://foo:81/", - "origin": "ws://foo:81", - "protocol": "ws:", - "username": "", - "password": "", - "host": "foo:81", - "hostname": "foo", - "port": "81", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "ws://foo:443/", - "base": "about:blank", - "href": "ws://foo:443/", - "origin": "ws://foo:443", - "protocol": "ws:", - "username": "", - "password": "", - "host": "foo:443", - "hostname": "foo", - "port": "443", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "ws://foo:815/", - "base": "about:blank", - "href": "ws://foo:815/", - "origin": "ws://foo:815", - "protocol": "ws:", - "username": "", - "password": "", - "host": "foo:815", - "hostname": "foo", - "port": "815", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "wss://foo:80/", - "base": "about:blank", - "href": "wss://foo:80/", - "origin": "wss://foo:80", - "protocol": "wss:", - "username": "", - "password": "", - "host": "foo:80", - "hostname": "foo", - "port": "80", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "wss://foo:81/", - "base": "about:blank", - "href": "wss://foo:81/", - "origin": "wss://foo:81", - "protocol": "wss:", - "username": "", - "password": "", - "host": "foo:81", - "hostname": "foo", - "port": "81", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "wss://foo:443/", - "base": "about:blank", - "href": "wss://foo/", - "origin": "wss://foo", - "protocol": "wss:", - "username": "", - "password": "", - "host": "foo", - "hostname": "foo", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "wss://foo:815/", - "base": "about:blank", - "href": "wss://foo:815/", - "origin": "wss://foo:815", - "protocol": "wss:", - "username": "", - "password": "", - "host": "foo:815", - "hostname": "foo", - "port": "815", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "http:/example.com/", - "base": "about:blank", - "href": "http://example.com/", - "origin": "http://example.com", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.com", - "hostname": "example.com", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "ftp:/example.com/", - "base": "about:blank", - "href": "ftp://example.com/", - "origin": "ftp://example.com", - "protocol": "ftp:", - "username": "", - "password": "", - "host": "example.com", - "hostname": "example.com", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "https:/example.com/", - "base": "about:blank", - "href": "https://example.com/", - "origin": "https://example.com", - "protocol": "https:", - "username": "", - "password": "", - "host": "example.com", - "hostname": "example.com", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "madeupscheme:/example.com/", - "base": "about:blank", - "href": "madeupscheme:/example.com/", - "origin": "null", - "protocol": "madeupscheme:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/example.com/", - "search": "", - "hash": "" - }, - { - "input": "file:/example.com/", - "base": "about:blank", - "href": "file:///example.com/", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/example.com/", - "search": "", - "hash": "" - }, - { - "input": "ftps:/example.com/", - "base": "about:blank", - "href": "ftps:/example.com/", - "origin": "null", - "protocol": "ftps:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/example.com/", - "search": "", - "hash": "" - }, - { - "input": "gopher:/example.com/", - "base": "about:blank", - "href": "gopher:/example.com/", - "origin": "null", - "protocol": "gopher:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/example.com/", - "search": "", - "hash": "" - }, - { - "input": "ws:/example.com/", - "base": "about:blank", - "href": "ws://example.com/", - "origin": "ws://example.com", - "protocol": "ws:", - "username": "", - "password": "", - "host": "example.com", - "hostname": "example.com", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "wss:/example.com/", - "base": "about:blank", - "href": "wss://example.com/", - "origin": "wss://example.com", - "protocol": "wss:", - "username": "", - "password": "", - "host": "example.com", - "hostname": "example.com", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "data:/example.com/", - "base": "about:blank", - "href": "data:/example.com/", - "origin": "null", - "protocol": "data:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/example.com/", - "search": "", - "hash": "" - }, - { - "input": "javascript:/example.com/", - "base": "about:blank", - "href": "javascript:/example.com/", - "origin": "null", - "protocol": "javascript:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/example.com/", - "search": "", - "hash": "" - }, - { - "input": "mailto:/example.com/", - "base": "about:blank", - "href": "mailto:/example.com/", - "origin": "null", - "protocol": "mailto:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/example.com/", - "search": "", - "hash": "" - }, - { - "input": "http:example.com/", - "base": "about:blank", - "href": "http://example.com/", - "origin": "http://example.com", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.com", - "hostname": "example.com", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "ftp:example.com/", - "base": "about:blank", - "href": "ftp://example.com/", - "origin": "ftp://example.com", - "protocol": "ftp:", - "username": "", - "password": "", - "host": "example.com", - "hostname": "example.com", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "https:example.com/", - "base": "about:blank", - "href": "https://example.com/", - "origin": "https://example.com", - "protocol": "https:", - "username": "", - "password": "", - "host": "example.com", - "hostname": "example.com", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "madeupscheme:example.com/", - "base": "about:blank", - "href": "madeupscheme:example.com/", - "origin": "null", - "protocol": "madeupscheme:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "example.com/", - "search": "", - "hash": "" - }, - { - "input": "ftps:example.com/", - "base": "about:blank", - "href": "ftps:example.com/", - "origin": "null", - "protocol": "ftps:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "example.com/", - "search": "", - "hash": "" - }, - { - "input": "gopher:example.com/", - "base": "about:blank", - "href": "gopher:example.com/", - "origin": "null", - "protocol": "gopher:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "example.com/", - "search": "", - "hash": "" - }, - { - "input": "ws:example.com/", - "base": "about:blank", - "href": "ws://example.com/", - "origin": "ws://example.com", - "protocol": "ws:", - "username": "", - "password": "", - "host": "example.com", - "hostname": "example.com", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "wss:example.com/", - "base": "about:blank", - "href": "wss://example.com/", - "origin": "wss://example.com", - "protocol": "wss:", - "username": "", - "password": "", - "host": "example.com", - "hostname": "example.com", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "data:example.com/", - "base": "about:blank", - "href": "data:example.com/", - "origin": "null", - "protocol": "data:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "example.com/", - "search": "", - "hash": "" - }, - { - "input": "javascript:example.com/", - "base": "about:blank", - "href": "javascript:example.com/", - "origin": "null", - "protocol": "javascript:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "example.com/", - "search": "", - "hash": "" - }, - { - "input": "mailto:example.com/", - "base": "about:blank", - "href": "mailto:example.com/", - "origin": "null", - "protocol": "mailto:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "example.com/", - "search": "", - "hash": "" - }, - "# Based on http://trac.webkit.org/browser/trunk/LayoutTests/fast/url/segments-userinfo-vs-host.html", - { - "input": "http:@www.example.com", - "base": "about:blank", - "href": "http://www.example.com/", - "origin": "http://www.example.com", - "protocol": "http:", - "username": "", - "password": "", - "host": "www.example.com", - "hostname": "www.example.com", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "http:/@www.example.com", - "base": "about:blank", - "href": "http://www.example.com/", - "origin": "http://www.example.com", - "protocol": "http:", - "username": "", - "password": "", - "host": "www.example.com", - "hostname": "www.example.com", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "http://@www.example.com", - "base": "about:blank", - "href": "http://www.example.com/", - "origin": "http://www.example.com", - "protocol": "http:", - "username": "", - "password": "", - "host": "www.example.com", - "hostname": "www.example.com", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "http:a:b@www.example.com", - "base": "about:blank", - "href": "http://a:b@www.example.com/", - "origin": "http://www.example.com", - "protocol": "http:", - "username": "a", - "password": "b", - "host": "www.example.com", - "hostname": "www.example.com", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "http:/a:b@www.example.com", - "base": "about:blank", - "href": "http://a:b@www.example.com/", - "origin": "http://www.example.com", - "protocol": "http:", - "username": "a", - "password": "b", - "host": "www.example.com", - "hostname": "www.example.com", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "http://a:b@www.example.com", - "base": "about:blank", - "href": "http://a:b@www.example.com/", - "origin": "http://www.example.com", - "protocol": "http:", - "username": "a", - "password": "b", - "host": "www.example.com", - "hostname": "www.example.com", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "http://@pple.com", - "base": "about:blank", - "href": "http://pple.com/", - "origin": "http://pple.com", - "protocol": "http:", - "username": "", - "password": "", - "host": "pple.com", - "hostname": "pple.com", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "http::b@www.example.com", - "base": "about:blank", - "href": "http://:b@www.example.com/", - "origin": "http://www.example.com", - "protocol": "http:", - "username": "", - "password": "b", - "host": "www.example.com", - "hostname": "www.example.com", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "http:/:b@www.example.com", - "base": "about:blank", - "href": "http://:b@www.example.com/", - "origin": "http://www.example.com", - "protocol": "http:", - "username": "", - "password": "b", - "host": "www.example.com", - "hostname": "www.example.com", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "http://:b@www.example.com", - "base": "about:blank", - "href": "http://:b@www.example.com/", - "origin": "http://www.example.com", - "protocol": "http:", - "username": "", - "password": "b", - "host": "www.example.com", - "hostname": "www.example.com", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "http:/:@/www.example.com", - "base": "about:blank", - "failure": true, - "inputCanBeRelative": true - }, - { - "input": "http://user@/www.example.com", - "base": "about:blank", - "failure": true - }, - { - "input": "http:@/www.example.com", - "base": "about:blank", - "failure": true, - "inputCanBeRelative": true - }, - { - "input": "http:/@/www.example.com", - "base": "about:blank", - "failure": true, - "inputCanBeRelative": true - }, - { - "input": "http://@/www.example.com", - "base": "about:blank", - "failure": true - }, - { - "input": "https:@/www.example.com", - "base": "about:blank", - "failure": true, - "inputCanBeRelative": true - }, - { - "input": "http:a:b@/www.example.com", - "base": "about:blank", - "failure": true, - "inputCanBeRelative": true - }, - { - "input": "http:/a:b@/www.example.com", - "base": "about:blank", - "failure": true, - "inputCanBeRelative": true - }, - { - "input": "http://a:b@/www.example.com", - "base": "about:blank", - "failure": true - }, - { - "input": "http::@/www.example.com", - "base": "about:blank", - "failure": true, - "inputCanBeRelative": true - }, - { - "input": "http:a:@www.example.com", - "base": "about:blank", - "href": "http://a@www.example.com/", - "origin": "http://www.example.com", - "protocol": "http:", - "username": "a", - "password": "", - "host": "www.example.com", - "hostname": "www.example.com", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "http:/a:@www.example.com", - "base": "about:blank", - "href": "http://a@www.example.com/", - "origin": "http://www.example.com", - "protocol": "http:", - "username": "a", - "password": "", - "host": "www.example.com", - "hostname": "www.example.com", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "http://a:@www.example.com", - "base": "about:blank", - "href": "http://a@www.example.com/", - "origin": "http://www.example.com", - "protocol": "http:", - "username": "a", - "password": "", - "host": "www.example.com", - "hostname": "www.example.com", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "http://www.@pple.com", - "base": "about:blank", - "href": "http://www.@pple.com/", - "origin": "http://pple.com", - "protocol": "http:", - "username": "www.", - "password": "", - "host": "pple.com", - "hostname": "pple.com", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "http:@:www.example.com", - "base": "about:blank", - "failure": true, - "inputCanBeRelative": true - }, - { - "input": "http:/@:www.example.com", - "base": "about:blank", - "failure": true, - "inputCanBeRelative": true - }, - { - "input": "http://@:www.example.com", - "base": "about:blank", - "failure": true - }, - { - "input": "http://:@www.example.com", - "base": "about:blank", - "href": "http://www.example.com/", - "origin": "http://www.example.com", - "protocol": "http:", - "username": "", - "password": "", - "host": "www.example.com", - "hostname": "www.example.com", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - "# Others", - { - "input": "/", - "base": "http://www.example.com/test", - "href": "http://www.example.com/", - "origin": "http://www.example.com", - "protocol": "http:", - "username": "", - "password": "", - "host": "www.example.com", - "hostname": "www.example.com", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "/test.txt", - "base": "http://www.example.com/test", - "href": "http://www.example.com/test.txt", - "origin": "http://www.example.com", - "protocol": "http:", - "username": "", - "password": "", - "host": "www.example.com", - "hostname": "www.example.com", - "port": "", - "pathname": "/test.txt", - "search": "", - "hash": "" - }, - { - "input": ".", - "base": "http://www.example.com/test", - "href": "http://www.example.com/", - "origin": "http://www.example.com", - "protocol": "http:", - "username": "", - "password": "", - "host": "www.example.com", - "hostname": "www.example.com", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "..", - "base": "http://www.example.com/test", - "href": "http://www.example.com/", - "origin": "http://www.example.com", - "protocol": "http:", - "username": "", - "password": "", - "host": "www.example.com", - "hostname": "www.example.com", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "test.txt", - "base": "http://www.example.com/test", - "href": "http://www.example.com/test.txt", - "origin": "http://www.example.com", - "protocol": "http:", - "username": "", - "password": "", - "host": "www.example.com", - "hostname": "www.example.com", - "port": "", - "pathname": "/test.txt", - "search": "", - "hash": "" - }, - { - "input": "./test.txt", - "base": "http://www.example.com/test", - "href": "http://www.example.com/test.txt", - "origin": "http://www.example.com", - "protocol": "http:", - "username": "", - "password": "", - "host": "www.example.com", - "hostname": "www.example.com", - "port": "", - "pathname": "/test.txt", - "search": "", - "hash": "" - }, - { - "input": "../test.txt", - "base": "http://www.example.com/test", - "href": "http://www.example.com/test.txt", - "origin": "http://www.example.com", - "protocol": "http:", - "username": "", - "password": "", - "host": "www.example.com", - "hostname": "www.example.com", - "port": "", - "pathname": "/test.txt", - "search": "", - "hash": "" - }, - { - "input": "../aaa/test.txt", - "base": "http://www.example.com/test", - "href": "http://www.example.com/aaa/test.txt", - "origin": "http://www.example.com", - "protocol": "http:", - "username": "", - "password": "", - "host": "www.example.com", - "hostname": "www.example.com", - "port": "", - "pathname": "/aaa/test.txt", - "search": "", - "hash": "" - }, - { - "input": "../../test.txt", - "base": "http://www.example.com/test", - "href": "http://www.example.com/test.txt", - "origin": "http://www.example.com", - "protocol": "http:", - "username": "", - "password": "", - "host": "www.example.com", - "hostname": "www.example.com", - "port": "", - "pathname": "/test.txt", - "search": "", - "hash": "" - }, - { - "input": "中/test.txt", - "base": "http://www.example.com/test", - "href": "http://www.example.com/%E4%B8%AD/test.txt", - "origin": "http://www.example.com", - "protocol": "http:", - "username": "", - "password": "", - "host": "www.example.com", - "hostname": "www.example.com", - "port": "", - "pathname": "/%E4%B8%AD/test.txt", - "search": "", - "hash": "" - }, - { - "input": "http://www.example2.com", - "base": "http://www.example.com/test", - "href": "http://www.example2.com/", - "origin": "http://www.example2.com", - "protocol": "http:", - "username": "", - "password": "", - "host": "www.example2.com", - "hostname": "www.example2.com", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "//www.example2.com", - "base": "http://www.example.com/test", - "href": "http://www.example2.com/", - "origin": "http://www.example2.com", - "protocol": "http:", - "username": "", - "password": "", - "host": "www.example2.com", - "hostname": "www.example2.com", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "file:...", - "base": "http://www.example.com/test", - "href": "file:///...", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/...", - "search": "", - "hash": "" - }, - { - "input": "file:..", - "base": "http://www.example.com/test", - "href": "file:///", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "file:a", - "base": "http://www.example.com/test", - "href": "file:///a", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/a", - "search": "", - "hash": "" - }, - "# Based on http://trac.webkit.org/browser/trunk/LayoutTests/fast/url/host.html", - "Basic canonicalization, uppercase should be converted to lowercase", - { - "input": "http://ExAmPlE.CoM", - "base": "http://other.com/", - "href": "http://example.com/", - "origin": "http://example.com", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.com", - "hostname": "example.com", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "http://example example.com", - "base": "http://other.com/", - "failure": true - }, - { - "input": "http://Goo%20 goo%7C|.com", - "base": "http://other.com/", - "failure": true - }, - { - "input": "http://[]", - "base": "http://other.com/", - "failure": true - }, - { - "input": "http://[:]", - "base": "http://other.com/", - "failure": true - }, - "U+3000 is mapped to U+0020 (space) which is disallowed", - { - "input": "http://GOO\u00a0\u3000goo.com", - "base": "http://other.com/", - "failure": true - }, - "Other types of space (no-break, zero-width, zero-width-no-break) are name-prepped away to nothing. U+200B, U+2060, and U+FEFF, are ignored", - { - "input": "http://GOO\u200b\u2060\ufeffgoo.com", - "base": "http://other.com/", - "href": "http://googoo.com/", - "origin": "http://googoo.com", - "protocol": "http:", - "username": "", - "password": "", - "host": "googoo.com", - "hostname": "googoo.com", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - "Leading and trailing C0 control or space", - { - "input": "\u0000\u001b\u0004\u0012 http://example.com/\u001f \u000d ", - "base": "about:blank", - "href": "http://example.com/", - "origin": "http://example.com", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.com", - "hostname": "example.com", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - "Ideographic full stop (full-width period for Chinese, etc.) should be treated as a dot. U+3002 is mapped to U+002E (dot)", - { - "input": "http://www.foo。bar.com", - "base": "http://other.com/", - "href": "http://www.foo.bar.com/", - "origin": "http://www.foo.bar.com", - "protocol": "http:", - "username": "", - "password": "", - "host": "www.foo.bar.com", - "hostname": "www.foo.bar.com", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - "Invalid unicode characters should fail... U+FDD0 is disallowed; %ef%b7%90 is U+FDD0", - { - "input": "http://\ufdd0zyx.com", - "base": "http://other.com/", - "failure": true - }, - "This is the same as previous but escaped", - { - "input": "http://%ef%b7%90zyx.com", - "base": "http://other.com/", - "failure": true - }, - "U+FFFD", - { - "input": "https://\ufffd", - "base": "about:blank", - "failure": true - }, - { - "input": "https://%EF%BF%BD", - "base": "about:blank", - "failure": true - }, - { - "input": "https://x/\ufffd?\ufffd#\ufffd", - "base": "about:blank", - "href": "https://x/%EF%BF%BD?%EF%BF%BD#%EF%BF%BD", - "origin": "https://x", - "protocol": "https:", - "username": "", - "password": "", - "host": "x", - "hostname": "x", - "port": "", - "pathname": "/%EF%BF%BD", - "search": "?%EF%BF%BD", - "hash": "#%EF%BF%BD" - }, - "Domain is ASCII, but a label is invalid IDNA", - { - "input": "http://a.b.c.xn--pokxncvks", - "base": "about:blank", - "failure": true - }, - { - "input": "http://10.0.0.xn--pokxncvks", - "base": "about:blank", - "failure": true - }, - "Test name prepping, fullwidth input should be converted to ASCII and NOT IDN-ized. This is 'Go' in fullwidth UTF-8/UTF-16.", - { - "input": "http://Go.com", - "base": "http://other.com/", - "href": "http://go.com/", - "origin": "http://go.com", - "protocol": "http:", - "username": "", - "password": "", - "host": "go.com", - "hostname": "go.com", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - "URL spec forbids the following. https://www.w3.org/Bugs/Public/show_bug.cgi?id=24257", - { - "input": "http://%41.com", - "base": "http://other.com/", - "failure": true - }, - { - "input": "http://%ef%bc%85%ef%bc%94%ef%bc%91.com", - "base": "http://other.com/", - "failure": true - }, - "...%00 in fullwidth should fail (also as escaped UTF-8 input)", - { - "input": "http://%00.com", - "base": "http://other.com/", - "failure": true - }, - { - "input": "http://%ef%bc%85%ef%bc%90%ef%bc%90.com", - "base": "http://other.com/", - "failure": true - }, - "Basic IDN support, UTF-8 and UTF-16 input should be converted to IDN", - { - "input": "http://你好你好", - "base": "http://other.com/", - "href": "http://xn--6qqa088eba/", - "origin": "http://xn--6qqa088eba", - "protocol": "http:", - "username": "", - "password": "", - "host": "xn--6qqa088eba", - "hostname": "xn--6qqa088eba", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "https://faß.ExAmPlE/", - "base": "about:blank", - "href": "https://xn--fa-hia.example/", - "origin": "https://xn--fa-hia.example", - "protocol": "https:", - "username": "", - "password": "", - "host": "xn--fa-hia.example", - "hostname": "xn--fa-hia.example", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "sc://faß.ExAmPlE/", - "base": "about:blank", - "href": "sc://fa%C3%9F.ExAmPlE/", - "origin": "null", - "protocol": "sc:", - "username": "", - "password": "", - "host": "fa%C3%9F.ExAmPlE", - "hostname": "fa%C3%9F.ExAmPlE", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - "Invalid escaped characters should fail and the percents should be escaped. https://www.w3.org/Bugs/Public/show_bug.cgi?id=24191", - { - "input": "http://%zz%66%a.com", - "base": "http://other.com/", - "failure": true - }, - "If we get an invalid character that has been escaped.", - { - "input": "http://%25", - "base": "http://other.com/", - "failure": true - }, - { - "input": "http://hello%00", - "base": "http://other.com/", - "failure": true - }, - "Escaped numbers should be treated like IP addresses if they are.", - { - "input": "http://%30%78%63%30%2e%30%32%35%30.01", - "base": "http://other.com/", - "href": "http://192.168.0.1/", - "origin": "http://192.168.0.1", - "protocol": "http:", - "username": "", - "password": "", - "host": "192.168.0.1", - "hostname": "192.168.0.1", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "http://%30%78%63%30%2e%30%32%35%30.01%2e", - "base": "http://other.com/", - "href": "http://192.168.0.1/", - "origin": "http://192.168.0.1", - "protocol": "http:", - "username": "", - "password": "", - "host": "192.168.0.1", - "hostname": "192.168.0.1", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "http://192.168.0.257", - "base": "http://other.com/", - "failure": true - }, - "Invalid escaping in hosts causes failure", - { - "input": "http://%3g%78%63%30%2e%30%32%35%30%2E.01", - "base": "http://other.com/", - "failure": true - }, - "A space in a host causes failure", - { - "input": "http://192.168.0.1 hello", - "base": "http://other.com/", - "failure": true - }, - { - "input": "https://x x:12", - "base": "about:blank", - "failure": true - }, - "Fullwidth and escaped UTF-8 fullwidth should still be treated as IP", - { - "input": "http://0Xc0.0250.01", - "base": "http://other.com/", - "href": "http://192.168.0.1/", - "origin": "http://192.168.0.1", - "protocol": "http:", - "username": "", - "password": "", - "host": "192.168.0.1", - "hostname": "192.168.0.1", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - "Domains with empty labels", - { - "input": "http://./", - "base": "about:blank", - "href": "http://./", - "origin": "http://.", - "protocol": "http:", - "username": "", - "password": "", - "host": ".", - "hostname": ".", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "http://../", - "base": "about:blank", - "href": "http://../", - "origin": "http://..", - "protocol": "http:", - "username": "", - "password": "", - "host": "..", - "hostname": "..", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - "Broken IPv6", - { - "input": "http://[www.google.com]/", - "base": "about:blank", - "failure": true - }, - { - "input": "http://[google.com]", - "base": "http://other.com/", - "failure": true - }, - { - "input": "http://[::1.2.3.4x]", - "base": "http://other.com/", - "failure": true - }, - { - "input": "http://[::1.2.3.]", - "base": "http://other.com/", - "failure": true - }, - { - "input": "http://[::1.2.]", - "base": "http://other.com/", - "failure": true - }, - { - "input": "http://[::1.]", - "base": "http://other.com/", - "failure": true - }, - "Misc Unicode", - { - "input": "http://foo:💩@example.com/bar", - "base": "http://other.com/", - "href": "http://foo:%F0%9F%92%A9@example.com/bar", - "origin": "http://example.com", - "protocol": "http:", - "username": "foo", - "password": "%F0%9F%92%A9", - "host": "example.com", - "hostname": "example.com", - "port": "", - "pathname": "/bar", - "search": "", - "hash": "" - }, - "# resolving a fragment against any scheme succeeds", - { - "input": "#", - "base": "test:test", - "href": "test:test#", - "origin": "null", - "protocol": "test:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "test", - "search": "", - "hash": "" - }, - { - "input": "#x", - "base": "mailto:x@x.com", - "href": "mailto:x@x.com#x", - "origin": "null", - "protocol": "mailto:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "x@x.com", - "search": "", - "hash": "#x" - }, - { - "input": "#x", - "base": "data:,", - "href": "data:,#x", - "origin": "null", - "protocol": "data:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": ",", - "search": "", - "hash": "#x" - }, - { - "input": "#x", - "base": "about:blank", - "href": "about:blank#x", - "origin": "null", - "protocol": "about:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "blank", - "search": "", - "hash": "#x" - }, - { - "input": "#", - "base": "test:test?test", - "href": "test:test?test#", - "origin": "null", - "protocol": "test:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "test", - "search": "?test", - "hash": "" - }, - "# multiple @ in authority state", - { - "input": "https://@test@test@example:800/", - "base": "http://doesnotmatter/", - "href": "https://%40test%40test@example:800/", - "origin": "https://example:800", - "protocol": "https:", - "username": "%40test%40test", - "password": "", - "host": "example:800", - "hostname": "example", - "port": "800", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "https://@@@example", - "base": "http://doesnotmatter/", - "href": "https://%40%40@example/", - "origin": "https://example", - "protocol": "https:", - "username": "%40%40", - "password": "", - "host": "example", - "hostname": "example", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - "non-az-09 characters", - { - "input": "http://`{}:`{}@h/`{}?`{}", - "base": "http://doesnotmatter/", - "href": "http://%60%7B%7D:%60%7B%7D@h/%60%7B%7D?`{}", - "origin": "http://h", - "protocol": "http:", - "username": "%60%7B%7D", - "password": "%60%7B%7D", - "host": "h", - "hostname": "h", - "port": "", - "pathname": "/%60%7B%7D", - "search": "?`{}", - "hash": "" - }, - "byte is ' and url is special", - { - "input": "http://host/?'", - "base": "about:blank", - "href": "http://host/?%27", - "origin": "http://host", - "protocol": "http:", - "username": "", - "password": "", - "host": "host", - "hostname": "host", - "port": "", - "pathname": "/", - "search": "?%27", - "hash": "" - }, - { - "input": "notspecial://host/?'", - "base": "about:blank", - "href": "notspecial://host/?'", - "origin": "null", - "protocol": "notspecial:", - "username": "", - "password": "", - "host": "host", - "hostname": "host", - "port": "", - "pathname": "/", - "search": "?'", - "hash": "" - }, - "# Credentials in base", - { - "input": "/some/path", - "base": "http://user@example.org/smth", - "href": "http://user@example.org/some/path", - "origin": "http://example.org", - "protocol": "http:", - "username": "user", - "password": "", - "host": "example.org", - "hostname": "example.org", - "port": "", - "pathname": "/some/path", - "search": "", - "hash": "" - }, - { - "input": "", - "base": "http://user:pass@example.org:21/smth", - "href": "http://user:pass@example.org:21/smth", - "origin": "http://example.org:21", - "protocol": "http:", - "username": "user", - "password": "pass", - "host": "example.org:21", - "hostname": "example.org", - "port": "21", - "pathname": "/smth", - "search": "", - "hash": "" - }, - { - "input": "/some/path", - "base": "http://user:pass@example.org:21/smth", - "href": "http://user:pass@example.org:21/some/path", - "origin": "http://example.org:21", - "protocol": "http:", - "username": "user", - "password": "pass", - "host": "example.org:21", - "hostname": "example.org", - "port": "21", - "pathname": "/some/path", - "search": "", - "hash": "" - }, - "# a set of tests designed by zcorpan for relative URLs with unknown schemes", - { - "input": "i", - "base": "sc:sd", - "failure": true - }, - { - "input": "i", - "base": "sc:sd/sd", - "failure": true - }, - { - "input": "i", - "base": "sc:/pa/pa", - "href": "sc:/pa/i", - "origin": "null", - "protocol": "sc:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/pa/i", - "search": "", - "hash": "" - }, - { - "input": "i", - "base": "sc://ho/pa", - "href": "sc://ho/i", - "origin": "null", - "protocol": "sc:", - "username": "", - "password": "", - "host": "ho", - "hostname": "ho", - "port": "", - "pathname": "/i", - "search": "", - "hash": "" - }, - { - "input": "i", - "base": "sc:///pa/pa", - "href": "sc:///pa/i", - "origin": "null", - "protocol": "sc:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/pa/i", - "search": "", - "hash": "" - }, - { - "input": "../i", - "base": "sc:sd", - "failure": true - }, - { - "input": "../i", - "base": "sc:sd/sd", - "failure": true - }, - { - "input": "../i", - "base": "sc:/pa/pa", - "href": "sc:/i", - "origin": "null", - "protocol": "sc:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/i", - "search": "", - "hash": "" - }, - { - "input": "../i", - "base": "sc://ho/pa", - "href": "sc://ho/i", - "origin": "null", - "protocol": "sc:", - "username": "", - "password": "", - "host": "ho", - "hostname": "ho", - "port": "", - "pathname": "/i", - "search": "", - "hash": "" - }, - { - "input": "../i", - "base": "sc:///pa/pa", - "href": "sc:///i", - "origin": "null", - "protocol": "sc:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/i", - "search": "", - "hash": "" - }, - { - "input": "/i", - "base": "sc:sd", - "failure": true - }, - { - "input": "/i", - "base": "sc:sd/sd", - "failure": true - }, - { - "input": "/i", - "base": "sc:/pa/pa", - "href": "sc:/i", - "origin": "null", - "protocol": "sc:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/i", - "search": "", - "hash": "" - }, - { - "input": "/i", - "base": "sc://ho/pa", - "href": "sc://ho/i", - "origin": "null", - "protocol": "sc:", - "username": "", - "password": "", - "host": "ho", - "hostname": "ho", - "port": "", - "pathname": "/i", - "search": "", - "hash": "" - }, - { - "input": "/i", - "base": "sc:///pa/pa", - "href": "sc:///i", - "origin": "null", - "protocol": "sc:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/i", - "search": "", - "hash": "" - }, - { - "input": "?i", - "base": "sc:sd", - "failure": true - }, - { - "input": "?i", - "base": "sc:sd/sd", - "failure": true - }, - { - "input": "?i", - "base": "sc:/pa/pa", - "href": "sc:/pa/pa?i", - "origin": "null", - "protocol": "sc:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/pa/pa", - "search": "?i", - "hash": "" - }, - { - "input": "?i", - "base": "sc://ho/pa", - "href": "sc://ho/pa?i", - "origin": "null", - "protocol": "sc:", - "username": "", - "password": "", - "host": "ho", - "hostname": "ho", - "port": "", - "pathname": "/pa", - "search": "?i", - "hash": "" - }, - { - "input": "?i", - "base": "sc:///pa/pa", - "href": "sc:///pa/pa?i", - "origin": "null", - "protocol": "sc:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/pa/pa", - "search": "?i", - "hash": "" - }, - { - "input": "#i", - "base": "sc:sd", - "href": "sc:sd#i", - "origin": "null", - "protocol": "sc:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "sd", - "search": "", - "hash": "#i" - }, - { - "input": "#i", - "base": "sc:sd/sd", - "href": "sc:sd/sd#i", - "origin": "null", - "protocol": "sc:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "sd/sd", - "search": "", - "hash": "#i" - }, - { - "input": "#i", - "base": "sc:/pa/pa", - "href": "sc:/pa/pa#i", - "origin": "null", - "protocol": "sc:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/pa/pa", - "search": "", - "hash": "#i" - }, - { - "input": "#i", - "base": "sc://ho/pa", - "href": "sc://ho/pa#i", - "origin": "null", - "protocol": "sc:", - "username": "", - "password": "", - "host": "ho", - "hostname": "ho", - "port": "", - "pathname": "/pa", - "search": "", - "hash": "#i" - }, - { - "input": "#i", - "base": "sc:///pa/pa", - "href": "sc:///pa/pa#i", - "origin": "null", - "protocol": "sc:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/pa/pa", - "search": "", - "hash": "#i" - }, - "# make sure that relative URL logic works on known typically non-relative schemes too", - { - "input": "about:/../", - "base": "about:blank", - "href": "about:/", - "origin": "null", - "protocol": "about:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "data:/../", - "base": "about:blank", - "href": "data:/", - "origin": "null", - "protocol": "data:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "javascript:/../", - "base": "about:blank", - "href": "javascript:/", - "origin": "null", - "protocol": "javascript:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "mailto:/../", - "base": "about:blank", - "href": "mailto:/", - "origin": "null", - "protocol": "mailto:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - "# unknown schemes and their hosts", - { - "input": "sc://ñ.test/", - "base": "about:blank", - "href": "sc://%C3%B1.test/", - "origin": "null", - "protocol": "sc:", - "username": "", - "password": "", - "host": "%C3%B1.test", - "hostname": "%C3%B1.test", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "sc://\u0000/", - "base": "about:blank", - "failure": true - }, - { - "input": "sc:// /", - "base": "about:blank", - "failure": true - }, - { - "input": "sc://%/", - "base": "about:blank", - "href": "sc://%/", - "protocol": "sc:", - "username": "", - "password": "", - "host": "%", - "hostname": "%", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "sc://@/", - "base": "about:blank", - "failure": true - }, - { - "input": "sc://te@s:t@/", - "base": "about:blank", - "failure": true - }, - { - "input": "sc://:/", - "base": "about:blank", - "failure": true - }, - { - "input": "sc://:12/", - "base": "about:blank", - "failure": true - }, - { - "input": "sc://[/", - "base": "about:blank", - "failure": true - }, - { - "input": "sc://\\/", - "base": "about:blank", - "failure": true - }, - { - "input": "sc://]/", - "base": "about:blank", - "failure": true - }, - { - "input": "x", - "base": "sc://ñ", - "href": "sc://%C3%B1/x", - "origin": "null", - "protocol": "sc:", - "username": "", - "password": "", - "host": "%C3%B1", - "hostname": "%C3%B1", - "port": "", - "pathname": "/x", - "search": "", - "hash": "" - }, - "# unknown schemes and backslashes", - { - "input": "sc:\\../", - "base": "about:blank", - "href": "sc:\\../", - "origin": "null", - "protocol": "sc:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "\\../", - "search": "", - "hash": "" - }, - "# unknown scheme with path looking like a password", - { - "input": "sc::a@example.net", - "base": "about:blank", - "href": "sc::a@example.net", - "origin": "null", - "protocol": "sc:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": ":a@example.net", - "search": "", - "hash": "" - }, - "# unknown scheme with bogus percent-encoding", - { - "input": "wow:%NBD", - "base": "about:blank", - "href": "wow:%NBD", - "origin": "null", - "protocol": "wow:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "%NBD", - "search": "", - "hash": "" - }, - { - "input": "wow:%1G", - "base": "about:blank", - "href": "wow:%1G", - "origin": "null", - "protocol": "wow:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "%1G", - "search": "", - "hash": "" - }, - "# unknown scheme with non-URL characters", - { - "input": "wow:\uFFFF", - "base": "about:blank", - "href": "wow:%EF%BF%BF", - "origin": "null", - "protocol": "wow:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "%EF%BF%BF", - "search": "", - "hash": "" - }, - { - "input": "http://example.com/\uD800\uD801\uDFFE\uDFFF\uFDD0\uFDCF\uFDEF\uFDF0\uFFFE\uFFFF?\uD800\uD801\uDFFE\uDFFF\uFDD0\uFDCF\uFDEF\uFDF0\uFFFE\uFFFF", - "base": "about:blank", - "href": "http://example.com/%EF%BF%BD%F0%90%9F%BE%EF%BF%BD%EF%B7%90%EF%B7%8F%EF%B7%AF%EF%B7%B0%EF%BF%BE%EF%BF%BF?%EF%BF%BD%F0%90%9F%BE%EF%BF%BD%EF%B7%90%EF%B7%8F%EF%B7%AF%EF%B7%B0%EF%BF%BE%EF%BF%BF", - "origin": "http://example.com", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.com", - "hostname": "example.com", - "port": "", - "pathname": "/%EF%BF%BD%F0%90%9F%BE%EF%BF%BD%EF%B7%90%EF%B7%8F%EF%B7%AF%EF%B7%B0%EF%BF%BE%EF%BF%BF", - "search": "?%EF%BF%BD%F0%90%9F%BE%EF%BF%BD%EF%B7%90%EF%B7%8F%EF%B7%AF%EF%B7%B0%EF%BF%BE%EF%BF%BF", - "hash": "" - }, - "Forbidden host code points", - { - "input": "http://ab", - "base": "about:blank", - "failure": true - }, - { - "input": "http://a^b", - "base": "about:blank", - "failure": true - }, - { - "input": "non-special://ab", - "base": "about:blank", - "failure": true - }, - { - "input": "non-special://a^b", - "base": "about:blank", - "failure": true - }, - { - "input": "foo://ho\u0000st/", - "base": "about:blank", - "failure": true - }, - { - "input": "foo://ho|st/", - "base": "about:blank", - "failure": true - }, - "Forbidden host codepoints: tabs and newlines are removed during preprocessing", - { - "input": "foo://ho\u0009st/", - "base": "about:blank", - "hash": "", - "host": "host", - "hostname": "host", - "href":"foo://host/", - "password": "", - "pathname": "/", - "port":"", - "protocol": "foo:", - "search": "", - "username": "" - }, - { - "input": "foo://ho\u000Ast/", - "base": "about:blank", - "hash": "", - "host": "host", - "hostname": "host", - "href":"foo://host/", - "password": "", - "pathname": "/", - "port":"", - "protocol": "foo:", - "search": "", - "username": "" - }, - { - "input": "foo://ho\u000Dst/", - "base": "about:blank", - "hash": "", - "host": "host", - "hostname": "host", - "href":"foo://host/", - "password": "", - "pathname": "/", - "port":"", - "protocol": "foo:", - "search": "", - "username": "" - }, - "Encoded forbidden host codepoints in special URLs", - { - "input": "http://ho%00st/", - "base": "about:blank", - "failure": true - }, - { - "input": "http://ho%09st/", - "base": "about:blank", - "failure": true - }, - { - "input": "http://ho%0Ast/", - "base": "about:blank", - "failure": true - }, - { - "input": "http://ho%0Dst/", - "base": "about:blank", - "failure": true - }, - { - "input": "http://ho%20st/", - "base": "about:blank", - "failure": true - }, - { - "input": "http://ho%23st/", - "base": "about:blank", - "failure": true - }, - { - "input": "http://ho%2Fst/", - "base": "about:blank", - "failure": true - }, - { - "input": "http://ho%3Ast/", - "base": "about:blank", - "failure": true - }, - { - "input": "http://ho%3Cst/", - "base": "about:blank", - "failure": true - }, - { - "input": "http://ho%3Est/", - "base": "about:blank", - "failure": true - }, - { - "input": "http://ho%3Fst/", - "base": "about:blank", - "failure": true - }, - { - "input": "http://ho%40st/", - "base": "about:blank", - "failure": true - }, - { - "input": "http://ho%5Bst/", - "base": "about:blank", - "failure": true - }, - { - "input": "http://ho%5Cst/", - "base": "about:blank", - "failure": true - }, - { - "input": "http://ho%5Dst/", - "base": "about:blank", - "failure": true - }, - { - "input": "http://ho%7Cst/", - "base": "about:blank", - "failure": true - }, - "Allowed host code points", - { - "input": "http://\u001F!\"$&'()*+,-.;=_`{}~/", - "base": "about:blank", - "href": "http://\u001F!\"$&'()*+,-.;=_`{}~/", - "origin": "http://\u001F!\"$&'()*+,-.;=_`{}~", - "protocol": "http:", - "username": "", - "password": "", - "host": "\u001F!\"$&'()*+,-.;=_`{}~", - "hostname": "\u001F!\"$&'()*+,-.;=_`{}~", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "sc://\u001F!\"$&'()*+,-.;=_`{}~/", - "base": "about:blank", - "href": "sc://%1F!\"$&'()*+,-.;=_`{}~/", - "origin": "null", - "protocol": "sc:", - "username": "", - "password": "", - "host": "%1F!\"$&'()*+,-.;=_`{}~", - "hostname": "%1F!\"$&'()*+,-.;=_`{}~", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - "# Hosts and percent-encoding", - { - "input": "ftp://example.com%80/", - "base": "about:blank", - "failure": true - }, - { - "input": "ftp://example.com%A0/", - "base": "about:blank", - "failure": true - }, - { - "input": "https://example.com%80/", - "base": "about:blank", - "failure": true - }, - { - "input": "https://example.com%A0/", - "base": "about:blank", - "failure": true - }, - { - "input": "ftp://%e2%98%83", - "base": "about:blank", - "href": "ftp://xn--n3h/", - "origin": "ftp://xn--n3h", - "protocol": "ftp:", - "username": "", - "password": "", - "host": "xn--n3h", - "hostname": "xn--n3h", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "https://%e2%98%83", - "base": "about:blank", - "href": "https://xn--n3h/", - "origin": "https://xn--n3h", - "protocol": "https:", - "username": "", - "password": "", - "host": "xn--n3h", - "hostname": "xn--n3h", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - "# tests from jsdom/whatwg-url designed for code coverage", - { - "input": "http://127.0.0.1:10100/relative_import.html", - "base": "about:blank", - "href": "http://127.0.0.1:10100/relative_import.html", - "origin": "http://127.0.0.1:10100", - "protocol": "http:", - "username": "", - "password": "", - "host": "127.0.0.1:10100", - "hostname": "127.0.0.1", - "port": "10100", - "pathname": "/relative_import.html", - "search": "", - "hash": "" - }, - { - "input": "http://facebook.com/?foo=%7B%22abc%22", - "base": "about:blank", - "href": "http://facebook.com/?foo=%7B%22abc%22", - "origin": "http://facebook.com", - "protocol": "http:", - "username": "", - "password": "", - "host": "facebook.com", - "hostname": "facebook.com", - "port": "", - "pathname": "/", - "search": "?foo=%7B%22abc%22", - "hash": "" - }, - { - "input": "https://localhost:3000/jqueryui@1.2.3", - "base": "about:blank", - "href": "https://localhost:3000/jqueryui@1.2.3", - "origin": "https://localhost:3000", - "protocol": "https:", - "username": "", - "password": "", - "host": "localhost:3000", - "hostname": "localhost", - "port": "3000", - "pathname": "/jqueryui@1.2.3", - "search": "", - "hash": "" - }, - "# tab/LF/CR", - { - "input": "h\tt\nt\rp://h\to\ns\rt:9\t0\n0\r0/p\ta\nt\rh?q\tu\ne\rry#f\tr\na\rg", - "base": "about:blank", - "href": "http://host:9000/path?query#frag", - "origin": "http://host:9000", - "protocol": "http:", - "username": "", - "password": "", - "host": "host:9000", - "hostname": "host", - "port": "9000", - "pathname": "/path", - "search": "?query", - "hash": "#frag" - }, - "# Stringification of URL.searchParams", - { - "input": "?a=b&c=d", - "base": "http://example.org/foo/bar", - "href": "http://example.org/foo/bar?a=b&c=d", - "origin": "http://example.org", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.org", - "hostname": "example.org", - "port": "", - "pathname": "/foo/bar", - "search": "?a=b&c=d", - "searchParams": "a=b&c=d", - "hash": "" - }, - { - "input": "??a=b&c=d", - "base": "http://example.org/foo/bar", - "href": "http://example.org/foo/bar??a=b&c=d", - "origin": "http://example.org", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.org", - "hostname": "example.org", - "port": "", - "pathname": "/foo/bar", - "search": "??a=b&c=d", - "searchParams": "%3Fa=b&c=d", - "hash": "" - }, - "# Scheme only", - { - "input": "http:", - "base": "http://example.org/foo/bar", - "href": "http://example.org/foo/bar", - "origin": "http://example.org", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.org", - "hostname": "example.org", - "port": "", - "pathname": "/foo/bar", - "search": "", - "searchParams": "", - "hash": "" - }, - { - "input": "http:", - "base": "https://example.org/foo/bar", - "failure": true - }, - { - "input": "sc:", - "base": "https://example.org/foo/bar", - "href": "sc:", - "origin": "null", - "protocol": "sc:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "", - "search": "", - "searchParams": "", - "hash": "" - }, - "# Percent encoding of fragments", - { - "input": "http://foo.bar/baz?qux#foo\bbar", - "base": "about:blank", - "href": "http://foo.bar/baz?qux#foo%08bar", - "origin": "http://foo.bar", - "protocol": "http:", - "username": "", - "password": "", - "host": "foo.bar", - "hostname": "foo.bar", - "port": "", - "pathname": "/baz", - "search": "?qux", - "searchParams": "qux=", - "hash": "#foo%08bar" - }, - { - "input": "http://foo.bar/baz?qux#foo\"bar", - "base": "about:blank", - "href": "http://foo.bar/baz?qux#foo%22bar", - "origin": "http://foo.bar", - "protocol": "http:", - "username": "", - "password": "", - "host": "foo.bar", - "hostname": "foo.bar", - "port": "", - "pathname": "/baz", - "search": "?qux", - "searchParams": "qux=", - "hash": "#foo%22bar" - }, - { - "input": "http://foo.bar/baz?qux#foobar", - "base": "about:blank", - "href": "http://foo.bar/baz?qux#foo%3Ebar", - "origin": "http://foo.bar", - "protocol": "http:", - "username": "", - "password": "", - "host": "foo.bar", - "hostname": "foo.bar", - "port": "", - "pathname": "/baz", - "search": "?qux", - "searchParams": "qux=", - "hash": "#foo%3Ebar" - }, - { - "input": "http://foo.bar/baz?qux#foo`bar", - "base": "about:blank", - "href": "http://foo.bar/baz?qux#foo%60bar", - "origin": "http://foo.bar", - "protocol": "http:", - "username": "", - "password": "", - "host": "foo.bar", - "hostname": "foo.bar", - "port": "", - "pathname": "/baz", - "search": "?qux", - "searchParams": "qux=", - "hash": "#foo%60bar" - }, - "# IPv4 parsing (via https://github.com/nodejs/node/pull/10317)", - { - "input": "http://1.2.3.4/", - "base": "http://other.com/", - "href": "http://1.2.3.4/", - "origin": "http://1.2.3.4", - "protocol": "http:", - "username": "", - "password": "", - "host": "1.2.3.4", - "hostname": "1.2.3.4", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "http://1.2.3.4./", - "base": "http://other.com/", - "href": "http://1.2.3.4/", - "origin": "http://1.2.3.4", - "protocol": "http:", - "username": "", - "password": "", - "host": "1.2.3.4", - "hostname": "1.2.3.4", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "http://192.168.257", - "base": "http://other.com/", - "href": "http://192.168.1.1/", - "origin": "http://192.168.1.1", - "protocol": "http:", - "username": "", - "password": "", - "host": "192.168.1.1", - "hostname": "192.168.1.1", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "http://192.168.257.", - "base": "http://other.com/", - "href": "http://192.168.1.1/", - "origin": "http://192.168.1.1", - "protocol": "http:", - "username": "", - "password": "", - "host": "192.168.1.1", - "hostname": "192.168.1.1", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "http://192.168.257.com", - "base": "http://other.com/", - "href": "http://192.168.257.com/", - "origin": "http://192.168.257.com", - "protocol": "http:", - "username": "", - "password": "", - "host": "192.168.257.com", - "hostname": "192.168.257.com", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "http://256", - "base": "http://other.com/", - "href": "http://0.0.1.0/", - "origin": "http://0.0.1.0", - "protocol": "http:", - "username": "", - "password": "", - "host": "0.0.1.0", - "hostname": "0.0.1.0", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "http://256.com", - "base": "http://other.com/", - "href": "http://256.com/", - "origin": "http://256.com", - "protocol": "http:", - "username": "", - "password": "", - "host": "256.com", - "hostname": "256.com", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "http://999999999", - "base": "http://other.com/", - "href": "http://59.154.201.255/", - "origin": "http://59.154.201.255", - "protocol": "http:", - "username": "", - "password": "", - "host": "59.154.201.255", - "hostname": "59.154.201.255", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "http://999999999.", - "base": "http://other.com/", - "href": "http://59.154.201.255/", - "origin": "http://59.154.201.255", - "protocol": "http:", - "username": "", - "password": "", - "host": "59.154.201.255", - "hostname": "59.154.201.255", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "http://999999999.com", - "base": "http://other.com/", - "href": "http://999999999.com/", - "origin": "http://999999999.com", - "protocol": "http:", - "username": "", - "password": "", - "host": "999999999.com", - "hostname": "999999999.com", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "http://10000000000", - "base": "http://other.com/", - "failure": true - }, - { - "input": "http://10000000000.com", - "base": "http://other.com/", - "href": "http://10000000000.com/", - "origin": "http://10000000000.com", - "protocol": "http:", - "username": "", - "password": "", - "host": "10000000000.com", - "hostname": "10000000000.com", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "http://4294967295", - "base": "http://other.com/", - "href": "http://255.255.255.255/", - "origin": "http://255.255.255.255", - "protocol": "http:", - "username": "", - "password": "", - "host": "255.255.255.255", - "hostname": "255.255.255.255", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "http://4294967296", - "base": "http://other.com/", - "failure": true - }, - { - "input": "http://0xffffffff", - "base": "http://other.com/", - "href": "http://255.255.255.255/", - "origin": "http://255.255.255.255", - "protocol": "http:", - "username": "", - "password": "", - "host": "255.255.255.255", - "hostname": "255.255.255.255", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "http://0xffffffff1", - "base": "http://other.com/", - "failure": true - }, - { - "input": "http://256.256.256.256", - "base": "http://other.com/", - "failure": true - }, - { - "input": "https://0x.0x.0", - "base": "about:blank", - "href": "https://0.0.0.0/", - "origin": "https://0.0.0.0", - "protocol": "https:", - "username": "", - "password": "", - "host": "0.0.0.0", - "hostname": "0.0.0.0", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - "More IPv4 parsing (via https://github.com/jsdom/whatwg-url/issues/92)", - { - "input": "https://0x100000000/test", - "base": "about:blank", - "failure": true - }, - { - "input": "https://256.0.0.1/test", - "base": "about:blank", - "failure": true - }, - "# file URLs containing percent-encoded Windows drive letters (shouldn't work)", - { - "input": "file:///C%3A/", - "base": "about:blank", - "href": "file:///C%3A/", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/C%3A/", - "search": "", - "hash": "" - }, - { - "input": "file:///C%7C/", - "base": "about:blank", - "href": "file:///C%7C/", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/C%7C/", - "search": "", - "hash": "" - }, - { - "input": "file://%43%3A", - "base": "about:blank", - "failure": true - }, - { - "input": "file://%43%7C", - "base": "about:blank", - "failure": true - }, - { - "input": "file://%43|", - "base": "about:blank", - "failure": true - }, - { - "input": "file://C%7C", - "base": "about:blank", - "failure": true - }, - { - "input": "file://%43%7C/", - "base": "about:blank", - "failure": true - }, - { - "input": "https://%43%7C/", - "base": "about:blank", - "failure": true - }, - { - "input": "asdf://%43|/", - "base": "about:blank", - "failure": true - }, - { - "input": "asdf://%43%7C/", - "base": "about:blank", - "href": "asdf://%43%7C/", - "origin": "null", - "protocol": "asdf:", - "username": "", - "password": "", - "host": "%43%7C", - "hostname": "%43%7C", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - "# file URLs relative to other file URLs (via https://github.com/jsdom/whatwg-url/pull/60)", - { - "input": "pix/submit.gif", - "base": "file:///C:/Users/Domenic/Dropbox/GitHub/tmpvar/jsdom/test/level2/html/files/anchor.html", - "href": "file:///C:/Users/Domenic/Dropbox/GitHub/tmpvar/jsdom/test/level2/html/files/pix/submit.gif", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/C:/Users/Domenic/Dropbox/GitHub/tmpvar/jsdom/test/level2/html/files/pix/submit.gif", - "search": "", - "hash": "" - }, - { - "input": "..", - "base": "file:///C:/", - "href": "file:///C:/", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/C:/", - "search": "", - "hash": "" - }, - { - "input": "..", - "base": "file:///", - "href": "file:///", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - "# More file URL tests by zcorpan and annevk", - { - "input": "/", - "base": "file:///C:/a/b", - "href": "file:///C:/", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/C:/", - "search": "", - "hash": "" - }, - { - "input": "/", - "base": "file://h/C:/a/b", - "href": "file://h/C:/", - "protocol": "file:", - "username": "", - "password": "", - "host": "h", - "hostname": "h", - "port": "", - "pathname": "/C:/", - "search": "", - "hash": "" - }, - { - "input": "/", - "base": "file://h/a/b", - "href": "file://h/", - "protocol": "file:", - "username": "", - "password": "", - "host": "h", - "hostname": "h", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "//d:", - "base": "file:///C:/a/b", - "href": "file:///d:", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/d:", - "search": "", - "hash": "" - }, - { - "input": "//d:/..", - "base": "file:///C:/a/b", - "href": "file:///d:/", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/d:/", - "search": "", - "hash": "" - }, - { - "input": "..", - "base": "file:///ab:/", - "href": "file:///", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "..", - "base": "file:///1:/", - "href": "file:///", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "", - "base": "file:///test?test#test", - "href": "file:///test?test", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/test", - "search": "?test", - "hash": "" - }, - { - "input": "file:", - "base": "file:///test?test#test", - "href": "file:///test?test", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/test", - "search": "?test", - "hash": "" - }, - { - "input": "?x", - "base": "file:///test?test#test", - "href": "file:///test?x", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/test", - "search": "?x", - "hash": "" - }, - { - "input": "file:?x", - "base": "file:///test?test#test", - "href": "file:///test?x", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/test", - "search": "?x", - "hash": "" - }, - { - "input": "#x", - "base": "file:///test?test#test", - "href": "file:///test?test#x", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/test", - "search": "?test", - "hash": "#x" - }, - { - "input": "file:#x", - "base": "file:///test?test#test", - "href": "file:///test?test#x", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/test", - "search": "?test", - "hash": "#x" - }, - "# File URLs and many (back)slashes", - { - "input": "file:\\\\//", - "base": "about:blank", - "href": "file:////", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "//", - "search": "", - "hash": "" - }, - { - "input": "file:\\\\\\\\", - "base": "about:blank", - "href": "file:////", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "//", - "search": "", - "hash": "" - }, - { - "input": "file:\\\\\\\\?fox", - "base": "about:blank", - "href": "file:////?fox", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "//", - "search": "?fox", - "hash": "" - }, - { - "input": "file:\\\\\\\\#guppy", - "base": "about:blank", - "href": "file:////#guppy", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "//", - "search": "", - "hash": "#guppy" - }, - { - "input": "file://spider///", - "base": "about:blank", - "href": "file://spider///", - "protocol": "file:", - "username": "", - "password": "", - "host": "spider", - "hostname": "spider", - "port": "", - "pathname": "///", - "search": "", - "hash": "" - }, - { - "input": "file:\\\\localhost//", - "base": "about:blank", - "href": "file:////", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "//", - "search": "", - "hash": "" - }, - { - "input": "file:///localhost//cat", - "base": "about:blank", - "href": "file:///localhost//cat", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/localhost//cat", - "search": "", - "hash": "" - }, - { - "input": "file://\\/localhost//cat", - "base": "about:blank", - "href": "file:////localhost//cat", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "//localhost//cat", - "search": "", - "hash": "" - }, - { - "input": "file://localhost//a//../..//", - "base": "about:blank", - "href": "file://///", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "///", - "search": "", - "hash": "" - }, - { - "input": "/////mouse", - "base": "file:///elephant", - "href": "file://///mouse", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "///mouse", - "search": "", - "hash": "" - }, - { - "input": "\\//pig", - "base": "file://lion/", - "href": "file:///pig", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/pig", - "search": "", - "hash": "" - }, - { - "input": "\\/localhost//pig", - "base": "file://lion/", - "href": "file:////pig", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "//pig", - "search": "", - "hash": "" - }, - { - "input": "//localhost//pig", - "base": "file://lion/", - "href": "file:////pig", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "//pig", - "search": "", - "hash": "" - }, - { - "input": "/..//localhost//pig", - "base": "file://lion/", - "href": "file://lion//localhost//pig", - "protocol": "file:", - "username": "", - "password": "", - "host": "lion", - "hostname": "lion", - "port": "", - "pathname": "//localhost//pig", - "search": "", - "hash": "" - }, - { - "input": "file://", - "base": "file://ape/", - "href": "file:///", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - "# File URLs with non-empty hosts", - { - "input": "/rooibos", - "base": "file://tea/", - "href": "file://tea/rooibos", - "protocol": "file:", - "username": "", - "password": "", - "host": "tea", - "hostname": "tea", - "port": "", - "pathname": "/rooibos", - "search": "", - "hash": "" - }, - { - "input": "/?chai", - "base": "file://tea/", - "href": "file://tea/?chai", - "protocol": "file:", - "username": "", - "password": "", - "host": "tea", - "hostname": "tea", - "port": "", - "pathname": "/", - "search": "?chai", - "hash": "" - }, - "# Windows drive letter handling with the 'file:' base URL", - { - "input": "C|", - "base": "file://host/dir/file", - "href": "file://host/C:", - "protocol": "file:", - "username": "", - "password": "", - "host": "host", - "hostname": "host", - "port": "", - "pathname": "/C:", - "search": "", - "hash": "" - }, - { - "input": "C|", - "base": "file://host/D:/dir1/dir2/file", - "href": "file://host/C:", - "protocol": "file:", - "username": "", - "password": "", - "host": "host", - "hostname": "host", - "port": "", - "pathname": "/C:", - "search": "", - "hash": "" - }, - { - "input": "C|#", - "base": "file://host/dir/file", - "href": "file://host/C:#", - "protocol": "file:", - "username": "", - "password": "", - "host": "host", - "hostname": "host", - "port": "", - "pathname": "/C:", - "search": "", - "hash": "" - }, - { - "input": "C|?", - "base": "file://host/dir/file", - "href": "file://host/C:?", - "protocol": "file:", - "username": "", - "password": "", - "host": "host", - "hostname": "host", - "port": "", - "pathname": "/C:", - "search": "", - "hash": "" - }, - { - "input": "C|/", - "base": "file://host/dir/file", - "href": "file://host/C:/", - "protocol": "file:", - "username": "", - "password": "", - "host": "host", - "hostname": "host", - "port": "", - "pathname": "/C:/", - "search": "", - "hash": "" - }, - { - "input": "C|\n/", - "base": "file://host/dir/file", - "href": "file://host/C:/", - "protocol": "file:", - "username": "", - "password": "", - "host": "host", - "hostname": "host", - "port": "", - "pathname": "/C:/", - "search": "", - "hash": "" - }, - { - "input": "C|\\", - "base": "file://host/dir/file", - "href": "file://host/C:/", - "protocol": "file:", - "username": "", - "password": "", - "host": "host", - "hostname": "host", - "port": "", - "pathname": "/C:/", - "search": "", - "hash": "" - }, - { - "input": "C", - "base": "file://host/dir/file", - "href": "file://host/dir/C", - "protocol": "file:", - "username": "", - "password": "", - "host": "host", - "hostname": "host", - "port": "", - "pathname": "/dir/C", - "search": "", - "hash": "" - }, - { - "input": "C|a", - "base": "file://host/dir/file", - "href": "file://host/dir/C|a", - "protocol": "file:", - "username": "", - "password": "", - "host": "host", - "hostname": "host", - "port": "", - "pathname": "/dir/C|a", - "search": "", - "hash": "" - }, - "# Windows drive letter quirk in the file slash state", - { - "input": "/c:/foo/bar", - "base": "file:///c:/baz/qux", - "href": "file:///c:/foo/bar", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/c:/foo/bar", - "search": "", - "hash": "" - }, - { - "input": "/c|/foo/bar", - "base": "file:///c:/baz/qux", - "href": "file:///c:/foo/bar", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/c:/foo/bar", - "search": "", - "hash": "" - }, - { - "input": "file:\\c:\\foo\\bar", - "base": "file:///c:/baz/qux", - "href": "file:///c:/foo/bar", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/c:/foo/bar", - "search": "", - "hash": "" - }, - { - "input": "/c:/foo/bar", - "base": "file://host/path", - "href": "file://host/c:/foo/bar", - "protocol": "file:", - "username": "", - "password": "", - "host": "host", - "hostname": "host", - "port": "", - "pathname": "/c:/foo/bar", - "search": "", - "hash": "" - }, - "# Do not drop the host in the presence of a drive letter", - { - "input": "file://example.net/C:/", - "base": "about:blank", - "href": "file://example.net/C:/", - "protocol": "file:", - "username": "", - "password": "", - "host": "example.net", - "hostname": "example.net", - "port": "", - "pathname": "/C:/", - "search": "", - "hash": "" - }, - { - "input": "file://1.2.3.4/C:/", - "base": "about:blank", - "href": "file://1.2.3.4/C:/", - "protocol": "file:", - "username": "", - "password": "", - "host": "1.2.3.4", - "hostname": "1.2.3.4", - "port": "", - "pathname": "/C:/", - "search": "", - "hash": "" - }, - { - "input": "file://[1::8]/C:/", - "base": "about:blank", - "href": "file://[1::8]/C:/", - "protocol": "file:", - "username": "", - "password": "", - "host": "[1::8]", - "hostname": "[1::8]", - "port": "", - "pathname": "/C:/", - "search": "", - "hash": "" - }, - "# Copy the host from the base URL in the following cases", - { - "input": "C|/", - "base": "file://host/", - "href": "file://host/C:/", - "protocol": "file:", - "username": "", - "password": "", - "host": "host", - "hostname": "host", - "port": "", - "pathname": "/C:/", - "search": "", - "hash": "" - }, - { - "input": "/C:/", - "base": "file://host/", - "href": "file://host/C:/", - "protocol": "file:", - "username": "", - "password": "", - "host": "host", - "hostname": "host", - "port": "", - "pathname": "/C:/", - "search": "", - "hash": "" - }, - { - "input": "file:C:/", - "base": "file://host/", - "href": "file://host/C:/", - "protocol": "file:", - "username": "", - "password": "", - "host": "host", - "hostname": "host", - "port": "", - "pathname": "/C:/", - "search": "", - "hash": "" - }, - { - "input": "file:/C:/", - "base": "file://host/", - "href": "file://host/C:/", - "protocol": "file:", - "username": "", - "password": "", - "host": "host", - "hostname": "host", - "port": "", - "pathname": "/C:/", - "search": "", - "hash": "" - }, - "# Copy the empty host from the input in the following cases", - { - "input": "//C:/", - "base": "file://host/", - "href": "file:///C:/", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/C:/", - "search": "", - "hash": "" - }, - { - "input": "file://C:/", - "base": "file://host/", - "href": "file:///C:/", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/C:/", - "search": "", - "hash": "" - }, - { - "input": "///C:/", - "base": "file://host/", - "href": "file:///C:/", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/C:/", - "search": "", - "hash": "" - }, - { - "input": "file:///C:/", - "base": "file://host/", - "href": "file:///C:/", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/C:/", - "search": "", - "hash": "" - }, - "# Windows drive letter quirk (no host)", - { - "input": "file:/C|/", - "base": "about:blank", - "href": "file:///C:/", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/C:/", - "search": "", - "hash": "" - }, - { - "input": "file://C|/", - "base": "about:blank", - "href": "file:///C:/", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/C:/", - "search": "", - "hash": "" - }, - "# file URLs without base URL by Rimas Misevičius", - { - "input": "file:", - "base": "about:blank", - "href": "file:///", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "file:?q=v", - "base": "about:blank", - "href": "file:///?q=v", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/", - "search": "?q=v", - "hash": "" - }, - { - "input": "file:#frag", - "base": "about:blank", - "href": "file:///#frag", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/", - "search": "", - "hash": "#frag" - }, - "# file: drive letter cases from https://crbug.com/1078698", - { - "input": "file:///Y:", - "base": "about:blank", - "href": "file:///Y:", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/Y:", - "search": "", - "hash": "" - }, - { - "input": "file:///Y:/", - "base": "about:blank", - "href": "file:///Y:/", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/Y:/", - "search": "", - "hash": "" - }, - { - "input": "file:///./Y", - "base": "about:blank", - "href": "file:///Y", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/Y", - "search": "", - "hash": "" - }, - { - "input": "file:///./Y:", - "base": "about:blank", - "href": "file:///Y:", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/Y:", - "search": "", - "hash": "" - }, - { - "input": "\\\\\\.\\Y:", - "base": "about:blank", - "failure": true, - "inputCanBeRelative": true - }, - "# file: drive letter cases from https://crbug.com/1078698 but lowercased", - { - "input": "file:///y:", - "base": "about:blank", - "href": "file:///y:", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/y:", - "search": "", - "hash": "" - }, - { - "input": "file:///y:/", - "base": "about:blank", - "href": "file:///y:/", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/y:/", - "search": "", - "hash": "" - }, - { - "input": "file:///./y", - "base": "about:blank", - "href": "file:///y", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/y", - "search": "", - "hash": "" - }, - { - "input": "file:///./y:", - "base": "about:blank", - "href": "file:///y:", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/y:", - "search": "", - "hash": "" - }, - { - "input": "\\\\\\.\\y:", - "base": "about:blank", - "failure": true, - "inputCanBeRelative": true - }, - "# Additional file URL tests for (https://github.com/whatwg/url/issues/405)", - { - "input": "file://localhost//a//../..//foo", - "base": "about:blank", - "href": "file://///foo", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "///foo", - "search": "", - "hash": "" - }, - { - "input": "file://localhost////foo", - "base": "about:blank", - "href": "file://////foo", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "////foo", - "search": "", - "hash": "" - }, - { - "input": "file:////foo", - "base": "about:blank", - "href": "file:////foo", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "//foo", - "search": "", - "hash": "" - }, - { - "input": "file:///one/two", - "base": "file:///", - "href": "file:///one/two", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/one/two", - "search": "", - "hash": "" - }, - { - "input": "file:////one/two", - "base": "file:///", - "href": "file:////one/two", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "//one/two", - "search": "", - "hash": "" - }, - { - "input": "//one/two", - "base": "file:///", - "href": "file://one/two", - "protocol": "file:", - "username": "", - "password": "", - "host": "one", - "hostname": "one", - "port": "", - "pathname": "/two", - "search": "", - "hash": "" - }, - { - "input": "///one/two", - "base": "file:///", - "href": "file:///one/two", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/one/two", - "search": "", - "hash": "" - }, - { - "input": "////one/two", - "base": "file:///", - "href": "file:////one/two", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "//one/two", - "search": "", - "hash": "" - }, - { - "input": "file:///.//", - "base": "file:////", - "href": "file:////", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "//", - "search": "", - "hash": "" - }, - "File URL tests for https://github.com/whatwg/url/issues/549", - { - "input": "file:.//p", - "base": "about:blank", - "href": "file:////p", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "//p", - "search": "", - "hash": "" - }, - { - "input": "file:/.//p", - "base": "about:blank", - "href": "file:////p", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "//p", - "search": "", - "hash": "" - }, - "# IPv6 tests", - { - "input": "http://[1:0::]", - "base": "http://example.net/", - "href": "http://[1::]/", - "origin": "http://[1::]", - "protocol": "http:", - "username": "", - "password": "", - "host": "[1::]", - "hostname": "[1::]", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "http://[0:1:2:3:4:5:6:7:8]", - "base": "http://example.net/", - "failure": true - }, - { - "input": "https://[0::0::0]", - "base": "about:blank", - "failure": true - }, - { - "input": "https://[0:.0]", - "base": "about:blank", - "failure": true - }, - { - "input": "https://[0:0:]", - "base": "about:blank", - "failure": true - }, - { - "input": "https://[0:1:2:3:4:5:6:7.0.0.0.1]", - "base": "about:blank", - "failure": true - }, - { - "input": "https://[0:1.00.0.0.0]", - "base": "about:blank", - "failure": true - }, - { - "input": "https://[0:1.290.0.0.0]", - "base": "about:blank", - "failure": true - }, - { - "input": "https://[0:1.23.23]", - "base": "about:blank", - "failure": true - }, - "# Empty host", - { - "input": "http://?", - "base": "about:blank", - "failure": true - }, - { - "input": "http://#", - "base": "about:blank", - "failure": true - }, - "Port overflow (2^32 + 81)", - { - "input": "http://f:4294967377/c", - "base": "http://example.org/", - "failure": true - }, - "Port overflow (2^64 + 81)", - { - "input": "http://f:18446744073709551697/c", - "base": "http://example.org/", - "failure": true - }, - "Port overflow (2^128 + 81)", - { - "input": "http://f:340282366920938463463374607431768211537/c", - "base": "http://example.org/", - "failure": true - }, - "# Non-special-URL path tests", - { - "input": "sc://ñ", - "base": "about:blank", - "href": "sc://%C3%B1", - "origin": "null", - "protocol": "sc:", - "username": "", - "password": "", - "host": "%C3%B1", - "hostname": "%C3%B1", - "port": "", - "pathname": "", - "search": "", - "hash": "" - }, - { - "input": "sc://ñ?x", - "base": "about:blank", - "href": "sc://%C3%B1?x", - "origin": "null", - "protocol": "sc:", - "username": "", - "password": "", - "host": "%C3%B1", - "hostname": "%C3%B1", - "port": "", - "pathname": "", - "search": "?x", - "hash": "" - }, - { - "input": "sc://ñ#x", - "base": "about:blank", - "href": "sc://%C3%B1#x", - "origin": "null", - "protocol": "sc:", - "username": "", - "password": "", - "host": "%C3%B1", - "hostname": "%C3%B1", - "port": "", - "pathname": "", - "search": "", - "hash": "#x" - }, - { - "input": "#x", - "base": "sc://ñ", - "href": "sc://%C3%B1#x", - "origin": "null", - "protocol": "sc:", - "username": "", - "password": "", - "host": "%C3%B1", - "hostname": "%C3%B1", - "port": "", - "pathname": "", - "search": "", - "hash": "#x" - }, - { - "input": "?x", - "base": "sc://ñ", - "href": "sc://%C3%B1?x", - "origin": "null", - "protocol": "sc:", - "username": "", - "password": "", - "host": "%C3%B1", - "hostname": "%C3%B1", - "port": "", - "pathname": "", - "search": "?x", - "hash": "" - }, - { - "input": "sc://?", - "base": "about:blank", - "href": "sc://?", - "protocol": "sc:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "", - "search": "", - "hash": "" - }, - { - "input": "sc://#", - "base": "about:blank", - "href": "sc://#", - "protocol": "sc:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "", - "search": "", - "hash": "" - }, - { - "input": "///", - "base": "sc://x/", - "href": "sc:///", - "protocol": "sc:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "////", - "base": "sc://x/", - "href": "sc:////", - "protocol": "sc:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "//", - "search": "", - "hash": "" - }, - { - "input": "////x/", - "base": "sc://x/", - "href": "sc:////x/", - "protocol": "sc:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "//x/", - "search": "", - "hash": "" - }, - { - "input": "tftp://foobar.com/someconfig;mode=netascii", - "base": "about:blank", - "href": "tftp://foobar.com/someconfig;mode=netascii", - "origin": "null", - "protocol": "tftp:", - "username": "", - "password": "", - "host": "foobar.com", - "hostname": "foobar.com", - "port": "", - "pathname": "/someconfig;mode=netascii", - "search": "", - "hash": "" - }, - { - "input": "telnet://user:pass@foobar.com:23/", - "base": "about:blank", - "href": "telnet://user:pass@foobar.com:23/", - "origin": "null", - "protocol": "telnet:", - "username": "user", - "password": "pass", - "host": "foobar.com:23", - "hostname": "foobar.com", - "port": "23", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "ut2004://10.10.10.10:7777/Index.ut2", - "base": "about:blank", - "href": "ut2004://10.10.10.10:7777/Index.ut2", - "origin": "null", - "protocol": "ut2004:", - "username": "", - "password": "", - "host": "10.10.10.10:7777", - "hostname": "10.10.10.10", - "port": "7777", - "pathname": "/Index.ut2", - "search": "", - "hash": "" - }, - { - "input": "redis://foo:bar@somehost:6379/0?baz=bam&qux=baz", - "base": "about:blank", - "href": "redis://foo:bar@somehost:6379/0?baz=bam&qux=baz", - "origin": "null", - "protocol": "redis:", - "username": "foo", - "password": "bar", - "host": "somehost:6379", - "hostname": "somehost", - "port": "6379", - "pathname": "/0", - "search": "?baz=bam&qux=baz", - "hash": "" - }, - { - "input": "rsync://foo@host:911/sup", - "base": "about:blank", - "href": "rsync://foo@host:911/sup", - "origin": "null", - "protocol": "rsync:", - "username": "foo", - "password": "", - "host": "host:911", - "hostname": "host", - "port": "911", - "pathname": "/sup", - "search": "", - "hash": "" - }, - { - "input": "git://github.com/foo/bar.git", - "base": "about:blank", - "href": "git://github.com/foo/bar.git", - "origin": "null", - "protocol": "git:", - "username": "", - "password": "", - "host": "github.com", - "hostname": "github.com", - "port": "", - "pathname": "/foo/bar.git", - "search": "", - "hash": "" - }, - { - "input": "irc://myserver.com:6999/channel?passwd", - "base": "about:blank", - "href": "irc://myserver.com:6999/channel?passwd", - "origin": "null", - "protocol": "irc:", - "username": "", - "password": "", - "host": "myserver.com:6999", - "hostname": "myserver.com", - "port": "6999", - "pathname": "/channel", - "search": "?passwd", - "hash": "" - }, - { - "input": "dns://fw.example.org:9999/foo.bar.org?type=TXT", - "base": "about:blank", - "href": "dns://fw.example.org:9999/foo.bar.org?type=TXT", - "origin": "null", - "protocol": "dns:", - "username": "", - "password": "", - "host": "fw.example.org:9999", - "hostname": "fw.example.org", - "port": "9999", - "pathname": "/foo.bar.org", - "search": "?type=TXT", - "hash": "" - }, - { - "input": "ldap://localhost:389/ou=People,o=JNDITutorial", - "base": "about:blank", - "href": "ldap://localhost:389/ou=People,o=JNDITutorial", - "origin": "null", - "protocol": "ldap:", - "username": "", - "password": "", - "host": "localhost:389", - "hostname": "localhost", - "port": "389", - "pathname": "/ou=People,o=JNDITutorial", - "search": "", - "hash": "" - }, - { - "input": "git+https://github.com/foo/bar", - "base": "about:blank", - "href": "git+https://github.com/foo/bar", - "origin": "null", - "protocol": "git+https:", - "username": "", - "password": "", - "host": "github.com", - "hostname": "github.com", - "port": "", - "pathname": "/foo/bar", - "search": "", - "hash": "" - }, - { - "input": "urn:ietf:rfc:2648", - "base": "about:blank", - "href": "urn:ietf:rfc:2648", - "origin": "null", - "protocol": "urn:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "ietf:rfc:2648", - "search": "", - "hash": "" - }, - { - "input": "tag:joe@example.org,2001:foo/bar", - "base": "about:blank", - "href": "tag:joe@example.org,2001:foo/bar", - "origin": "null", - "protocol": "tag:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "joe@example.org,2001:foo/bar", - "search": "", - "hash": "" - }, - "Serialize /. in path", - { - "input": "non-spec:/.//", - "base": "about:blank", - "href": "non-spec:/.//", - "protocol": "non-spec:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "//", - "search": "", - "hash": "" - }, - { - "input": "non-spec:/..//", - "base": "about:blank", - "href": "non-spec:/.//", - "protocol": "non-spec:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "//", - "search": "", - "hash": "" - }, - { - "input": "non-spec:/a/..//", - "base": "about:blank", - "href": "non-spec:/.//", - "protocol": "non-spec:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "//", - "search": "", - "hash": "" - }, - { - "input": "non-spec:/.//path", - "base": "about:blank", - "href": "non-spec:/.//path", - "protocol": "non-spec:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "//path", - "search": "", - "hash": "" - }, - { - "input": "non-spec:/..//path", - "base": "about:blank", - "href": "non-spec:/.//path", - "protocol": "non-spec:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "//path", - "search": "", - "hash": "" - }, - { - "input": "non-spec:/a/..//path", - "base": "about:blank", - "href": "non-spec:/.//path", - "protocol": "non-spec:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "//path", - "search": "", - "hash": "" - }, - { - "input": "/.//path", - "base": "non-spec:/p", - "href": "non-spec:/.//path", - "protocol": "non-spec:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "//path", - "search": "", - "hash": "" - }, - { - "input": "/..//path", - "base": "non-spec:/p", - "href": "non-spec:/.//path", - "protocol": "non-spec:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "//path", - "search": "", - "hash": "" - }, - { - "input": "..//path", - "base": "non-spec:/p", - "href": "non-spec:/.//path", - "protocol": "non-spec:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "//path", - "search": "", - "hash": "" - }, - { - "input": "a/..//path", - "base": "non-spec:/p", - "href": "non-spec:/.//path", - "protocol": "non-spec:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "//path", - "search": "", - "hash": "" - }, - { - "input": "", - "base": "non-spec:/..//p", - "href": "non-spec:/.//p", - "protocol": "non-spec:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "//p", - "search": "", - "hash": "" - }, - { - "input": "path", - "base": "non-spec:/..//p", - "href": "non-spec:/.//path", - "protocol": "non-spec:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "//path", - "search": "", - "hash": "" - }, - "Do not serialize /. in path", - { - "input": "../path", - "base": "non-spec:/.//p", - "href": "non-spec:/path", - "protocol": "non-spec:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/path", - "search": "", - "hash": "" - }, - "# percent encoded hosts in non-special-URLs", - { - "input": "non-special://%E2%80%A0/", - "base": "about:blank", - "href": "non-special://%E2%80%A0/", - "protocol": "non-special:", - "username": "", - "password": "", - "host": "%E2%80%A0", - "hostname": "%E2%80%A0", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "non-special://H%4fSt/path", - "base": "about:blank", - "href": "non-special://H%4fSt/path", - "protocol": "non-special:", - "username": "", - "password": "", - "host": "H%4fSt", - "hostname": "H%4fSt", - "port": "", - "pathname": "/path", - "search": "", - "hash": "" - }, - "# IPv6 in non-special-URLs", - { - "input": "non-special://[1:2:0:0:5:0:0:0]/", - "base": "about:blank", - "href": "non-special://[1:2:0:0:5::]/", - "protocol": "non-special:", - "username": "", - "password": "", - "host": "[1:2:0:0:5::]", - "hostname": "[1:2:0:0:5::]", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "non-special://[1:2:0:0:0:0:0:3]/", - "base": "about:blank", - "href": "non-special://[1:2::3]/", - "protocol": "non-special:", - "username": "", - "password": "", - "host": "[1:2::3]", - "hostname": "[1:2::3]", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "non-special://[1:2::3]:80/", - "base": "about:blank", - "href": "non-special://[1:2::3]:80/", - "protocol": "non-special:", - "username": "", - "password": "", - "host": "[1:2::3]:80", - "hostname": "[1:2::3]", - "port": "80", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "non-special://[:80/", - "base": "about:blank", - "failure": true - }, - { - "input": "blob:https://example.com:443/", - "base": "about:blank", - "href": "blob:https://example.com:443/", - "protocol": "blob:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "https://example.com:443/", - "search": "", - "hash": "" - }, - { - "input": "blob:d3958f5c-0777-0845-9dcf-2cb28783acaf", - "base": "about:blank", - "href": "blob:d3958f5c-0777-0845-9dcf-2cb28783acaf", - "protocol": "blob:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "d3958f5c-0777-0845-9dcf-2cb28783acaf", - "search": "", - "hash": "" - }, - "Invalid IPv4 radix digits", - { - "input": "http://0x7f.0.0.0x7g", - "base": "about:blank", - "href": "http://0x7f.0.0.0x7g/", - "protocol": "http:", - "username": "", - "password": "", - "host": "0x7f.0.0.0x7g", - "hostname": "0x7f.0.0.0x7g", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "http://0X7F.0.0.0X7G", - "base": "about:blank", - "href": "http://0x7f.0.0.0x7g/", - "protocol": "http:", - "username": "", - "password": "", - "host": "0x7f.0.0.0x7g", - "hostname": "0x7f.0.0.0x7g", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - "Invalid IPv4 portion of IPv6 address", - { - "input": "http://[::127.0.0.0.1]", - "base": "about:blank", - "failure": true - }, - "Uncompressed IPv6 addresses with 0", - { - "input": "http://[0:1:0:1:0:1:0:1]", - "base": "about:blank", - "href": "http://[0:1:0:1:0:1:0:1]/", - "protocol": "http:", - "username": "", - "password": "", - "host": "[0:1:0:1:0:1:0:1]", - "hostname": "[0:1:0:1:0:1:0:1]", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "http://[1:0:1:0:1:0:1:0]", - "base": "about:blank", - "href": "http://[1:0:1:0:1:0:1:0]/", - "protocol": "http:", - "username": "", - "password": "", - "host": "[1:0:1:0:1:0:1:0]", - "hostname": "[1:0:1:0:1:0:1:0]", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - "Percent-encoded query and fragment", - { - "input": "http://example.org/test?\u0022", - "base": "about:blank", - "href": "http://example.org/test?%22", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.org", - "hostname": "example.org", - "port": "", - "pathname": "/test", - "search": "?%22", - "hash": "" - }, - { - "input": "http://example.org/test?\u0023", - "base": "about:blank", - "href": "http://example.org/test?#", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.org", - "hostname": "example.org", - "port": "", - "pathname": "/test", - "search": "", - "hash": "" - }, - { - "input": "http://example.org/test?\u003C", - "base": "about:blank", - "href": "http://example.org/test?%3C", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.org", - "hostname": "example.org", - "port": "", - "pathname": "/test", - "search": "?%3C", - "hash": "" - }, - { - "input": "http://example.org/test?\u003E", - "base": "about:blank", - "href": "http://example.org/test?%3E", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.org", - "hostname": "example.org", - "port": "", - "pathname": "/test", - "search": "?%3E", - "hash": "" - }, - { - "input": "http://example.org/test?\u2323", - "base": "about:blank", - "href": "http://example.org/test?%E2%8C%A3", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.org", - "hostname": "example.org", - "port": "", - "pathname": "/test", - "search": "?%E2%8C%A3", - "hash": "" - }, - { - "input": "http://example.org/test?%23%23", - "base": "about:blank", - "href": "http://example.org/test?%23%23", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.org", - "hostname": "example.org", - "port": "", - "pathname": "/test", - "search": "?%23%23", - "hash": "" - }, - { - "input": "http://example.org/test?%GH", - "base": "about:blank", - "href": "http://example.org/test?%GH", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.org", - "hostname": "example.org", - "port": "", - "pathname": "/test", - "search": "?%GH", - "hash": "" - }, - { - "input": "http://example.org/test?a#%EF", - "base": "about:blank", - "href": "http://example.org/test?a#%EF", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.org", - "hostname": "example.org", - "port": "", - "pathname": "/test", - "search": "?a", - "hash": "#%EF" - }, - { - "input": "http://example.org/test?a#%GH", - "base": "about:blank", - "href": "http://example.org/test?a#%GH", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.org", - "hostname": "example.org", - "port": "", - "pathname": "/test", - "search": "?a", - "hash": "#%GH" - }, - "URLs that require a non-about:blank base. (Also serve as invalid base tests.)", - { - "input": "a", - "base": "about:blank", - "failure": true, - "inputCanBeRelative": true - }, - { - "input": "a/", - "base": "about:blank", - "failure": true, - "inputCanBeRelative": true - }, - { - "input": "a//", - "base": "about:blank", - "failure": true, - "inputCanBeRelative": true - }, - "Bases that don't fail to parse but fail to be bases", - { - "input": "test-a-colon.html", - "base": "a:", - "failure": true - }, - { - "input": "test-a-colon-b.html", - "base": "a:b", - "failure": true - }, - "Other base URL tests, that must succeed", - { - "input": "test-a-colon-slash.html", - "base": "a:/", - "href": "a:/test-a-colon-slash.html", - "protocol": "a:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/test-a-colon-slash.html", - "search": "", - "hash": "" - }, - { - "input": "test-a-colon-slash-slash.html", - "base": "a://", - "href": "a:///test-a-colon-slash-slash.html", - "protocol": "a:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/test-a-colon-slash-slash.html", - "search": "", - "hash": "" - }, - { - "input": "test-a-colon-slash-b.html", - "base": "a:/b", - "href": "a:/test-a-colon-slash-b.html", - "protocol": "a:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/test-a-colon-slash-b.html", - "search": "", - "hash": "" - }, - { - "input": "test-a-colon-slash-slash-b.html", - "base": "a://b", - "href": "a://b/test-a-colon-slash-slash-b.html", - "protocol": "a:", - "username": "", - "password": "", - "host": "b", - "hostname": "b", - "port": "", - "pathname": "/test-a-colon-slash-slash-b.html", - "search": "", - "hash": "" - }, - "Null code point in fragment", - { - "input": "http://example.org/test?a#b\u0000c", - "base": "about:blank", - "href": "http://example.org/test?a#b%00c", - "protocol": "http:", - "username": "", - "password": "", - "host": "example.org", - "hostname": "example.org", - "port": "", - "pathname": "/test", - "search": "?a", - "hash": "#b%00c" - }, - { - "input": "non-spec://example.org/test?a#b\u0000c", - "base": "about:blank", - "href": "non-spec://example.org/test?a#b%00c", - "protocol": "non-spec:", - "username": "", - "password": "", - "host": "example.org", - "hostname": "example.org", - "port": "", - "pathname": "/test", - "search": "?a", - "hash": "#b%00c" - }, - { - "input": "non-spec:/test?a#b\u0000c", - "base": "about:blank", - "href": "non-spec:/test?a#b%00c", - "protocol": "non-spec:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/test", - "search": "?a", - "hash": "#b%00c" - }, - "First scheme char - not allowed: https://github.com/whatwg/url/issues/464", - { - "input": "10.0.0.7:8080/foo.html", - "base": "file:///some/dir/bar.html", - "href": "file:///some/dir/10.0.0.7:8080/foo.html", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/some/dir/10.0.0.7:8080/foo.html", - "search": "", - "hash": "" - }, - "Subsequent scheme chars - not allowed", - { - "input": "a!@$*=/foo.html", - "base": "file:///some/dir/bar.html", - "href": "file:///some/dir/a!@$*=/foo.html", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/some/dir/a!@$*=/foo.html", - "search": "", - "hash": "" - }, - "First and subsequent scheme chars - allowed", - { - "input": "a1234567890-+.:foo/bar", - "base": "http://example.com/dir/file", - "href": "a1234567890-+.:foo/bar", - "protocol": "a1234567890-+.:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "foo/bar", - "search": "", - "hash": "" - }, - "IDNA ignored code points in file URLs hosts", - { - "input": "file://a\u00ADb/p", - "base": "about:blank", - "href": "file://ab/p", - "protocol": "file:", - "username": "", - "password": "", - "host": "ab", - "hostname": "ab", - "port": "", - "pathname": "/p", - "search": "", - "hash": "" - }, - { - "input": "file://a%C2%ADb/p", - "base": "about:blank", - "href": "file://ab/p", - "protocol": "file:", - "username": "", - "password": "", - "host": "ab", - "hostname": "ab", - "port": "", - "pathname": "/p", - "search": "", - "hash": "" - }, - "Empty host after the domain to ASCII", - { - "input": "file://\u00ad/p", - "base": "about:blank", - "failure": true - }, - { - "input": "file://%C2%AD/p", - "base": "about:blank", - "failure": true - }, - { - "input": "file://xn--/p", - "base": "about:blank", - "failure": true - }, - "https://bugzilla.mozilla.org/show_bug.cgi?id=1647058", - { - "input": "#link", - "base": "https://example.org/##link", - "href": "https://example.org/#link", - "protocol": "https:", - "username": "", - "password": "", - "host": "example.org", - "hostname": "example.org", - "port": "", - "pathname": "/", - "search": "", - "hash": "#link" - }, - "UTF-8 percent-encode of C0 control percent-encode set and supersets", - { - "input": "non-special:cannot-be-a-base-url-\u0000\u0001\u001F\u001E\u007E\u007F\u0080", - "base": "about:blank", - "hash": "", - "host": "", - "hostname": "", - "href": "non-special:cannot-be-a-base-url-%00%01%1F%1E~%7F%C2%80", - "origin": "null", - "password": "", - "pathname": "cannot-be-a-base-url-%00%01%1F%1E~%7F%C2%80", - "port": "", - "protocol": "non-special:", - "search": "", - "username": "" - }, - { - "input": "https://www.example.com/path{\u007Fpath.html?query'\u007F=query#fragment<\u007Ffragment", - "base": "about:blank", - "hash": "#fragment%3C%7Ffragment", - "host": "www.example.com", - "hostname": "www.example.com", - "href": "https://www.example.com/path%7B%7Fpath.html?query%27%7F=query#fragment%3C%7Ffragment", - "origin": "https://www.example.com", - "password": "", - "pathname": "/path%7B%7Fpath.html", - "port": "", - "protocol": "https:", - "search": "?query%27%7F=query", - "username": "" - }, - { - "input": "https://user:pass[\u007F@foo/bar", - "base": "http://example.org", - "hash": "", - "host": "foo", - "hostname": "foo", - "href": "https://user:pass%5B%7F@foo/bar", - "origin": "https://foo", - "password": "pass%5B%7F", - "pathname": "/bar", - "port": "", - "protocol": "https:", - "search": "", - "username": "user" - }, - "Tests for the distinct percent-encode sets", - { - "input": "foo:// !\"$%&'()*+,-.;<=>@[\\]^_`{|}~@host/", - "base": "about:blank", - "hash": "", - "host": "host", - "hostname": "host", - "href": "foo://%20!%22$%&'()*+,-.%3B%3C%3D%3E%40%5B%5C%5D%5E_%60%7B%7C%7D~@host/", - "origin": "null", - "password": "", - "pathname": "/", - "port":"", - "protocol": "foo:", - "search": "", - "username": "%20!%22$%&'()*+,-.%3B%3C%3D%3E%40%5B%5C%5D%5E_%60%7B%7C%7D~" - }, - { - "input": "wss:// !\"$%&'()*+,-.;<=>@[]^_`{|}~@host/", - "base": "about:blank", - "hash": "", - "host": "host", - "hostname": "host", - "href": "wss://%20!%22$%&'()*+,-.%3B%3C%3D%3E%40%5B%5D%5E_%60%7B%7C%7D~@host/", - "origin": "wss://host", - "password": "", - "pathname": "/", - "port":"", - "protocol": "wss:", - "search": "", - "username": "%20!%22$%&'()*+,-.%3B%3C%3D%3E%40%5B%5D%5E_%60%7B%7C%7D~" - }, - { - "input": "foo://joe: !\"$%&'()*+,-.:;<=>@[\\]^_`{|}~@host/", - "base": "about:blank", - "hash": "", - "host": "host", - "hostname": "host", - "href": "foo://joe:%20!%22$%&'()*+,-.%3A%3B%3C%3D%3E%40%5B%5C%5D%5E_%60%7B%7C%7D~@host/", - "origin": "null", - "password": "%20!%22$%&'()*+,-.%3A%3B%3C%3D%3E%40%5B%5C%5D%5E_%60%7B%7C%7D~", - "pathname": "/", - "port":"", - "protocol": "foo:", - "search": "", - "username": "joe" - }, - { - "input": "wss://joe: !\"$%&'()*+,-.:;<=>@[]^_`{|}~@host/", - "base": "about:blank", - "hash": "", - "host": "host", - "hostname": "host", - "href": "wss://joe:%20!%22$%&'()*+,-.%3A%3B%3C%3D%3E%40%5B%5D%5E_%60%7B%7C%7D~@host/", - "origin": "wss://host", - "password": "%20!%22$%&'()*+,-.%3A%3B%3C%3D%3E%40%5B%5D%5E_%60%7B%7C%7D~", - "pathname": "/", - "port":"", - "protocol": "wss:", - "search": "", - "username": "joe" - }, - { - "input": "foo://!\"$%&'()*+,-.;=_`{}~/", - "base": "about:blank", - "hash": "", - "host": "!\"$%&'()*+,-.;=_`{}~", - "hostname": "!\"$%&'()*+,-.;=_`{}~", - "href":"foo://!\"$%&'()*+,-.;=_`{}~/", - "origin": "null", - "password": "", - "pathname": "/", - "port":"", - "protocol": "foo:", - "search": "", - "username": "" - }, - { - "input": "wss://!\"$&'()*+,-.;=_`{}~/", - "base": "about:blank", - "hash": "", - "host": "!\"$&'()*+,-.;=_`{}~", - "hostname": "!\"$&'()*+,-.;=_`{}~", - "href":"wss://!\"$&'()*+,-.;=_`{}~/", - "origin": "wss://!\"$&'()*+,-.;=_`{}~", - "password": "", - "pathname": "/", - "port":"", - "protocol": "wss:", - "search": "", - "username": "" - }, - { - "input": "foo://host/ !\"$%&'()*+,-./:;<=>@[\\]^_`{|}~", - "base": "about:blank", - "hash": "", - "host": "host", - "hostname": "host", - "href": "foo://host/%20!%22$%&'()*+,-./:;%3C=%3E@[\\]^_%60%7B|%7D~", - "origin": "null", - "password": "", - "pathname": "/%20!%22$%&'()*+,-./:;%3C=%3E@[\\]^_%60%7B|%7D~", - "port":"", - "protocol": "foo:", - "search": "", - "username": "" - }, - { - "input": "wss://host/ !\"$%&'()*+,-./:;<=>@[\\]^_`{|}~", - "base": "about:blank", - "hash": "", - "host": "host", - "hostname": "host", - "href": "wss://host/%20!%22$%&'()*+,-./:;%3C=%3E@[/]^_%60%7B|%7D~", - "origin": "wss://host", - "password": "", - "pathname": "/%20!%22$%&'()*+,-./:;%3C=%3E@[/]^_%60%7B|%7D~", - "port":"", - "protocol": "wss:", - "search": "", - "username": "" - }, - { - "input": "foo://host/dir/? !\"$%&'()*+,-./:;<=>?@[\\]^_`{|}~", - "base": "about:blank", - "hash": "", - "host": "host", - "hostname": "host", - "href": "foo://host/dir/?%20!%22$%&'()*+,-./:;%3C=%3E?@[\\]^_`{|}~", - "origin": "null", - "password": "", - "pathname": "/dir/", - "port":"", - "protocol": "foo:", - "search": "?%20!%22$%&'()*+,-./:;%3C=%3E?@[\\]^_`{|}~", - "username": "" - }, - { - "input": "wss://host/dir/? !\"$%&'()*+,-./:;<=>?@[\\]^_`{|}~", - "base": "about:blank", - "hash": "", - "host": "host", - "hostname": "host", - "href": "wss://host/dir/?%20!%22$%&%27()*+,-./:;%3C=%3E?@[\\]^_`{|}~", - "origin": "wss://host", - "password": "", - "pathname": "/dir/", - "port":"", - "protocol": "wss:", - "search": "?%20!%22$%&%27()*+,-./:;%3C=%3E?@[\\]^_`{|}~", - "username": "" - }, - { - "input": "foo://host/dir/# !\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", - "base": "about:blank", - "hash": "#%20!%22#$%&'()*+,-./:;%3C=%3E?@[\\]^_%60{|}~", - "host": "host", - "hostname": "host", - "href": "foo://host/dir/#%20!%22#$%&'()*+,-./:;%3C=%3E?@[\\]^_%60{|}~", - "origin": "null", - "password": "", - "pathname": "/dir/", - "port":"", - "protocol": "foo:", - "search": "", - "username": "" - }, - { - "input": "wss://host/dir/# !\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", - "base": "about:blank", - "hash": "#%20!%22#$%&'()*+,-./:;%3C=%3E?@[\\]^_%60{|}~", - "host": "host", - "hostname": "host", - "href": "wss://host/dir/#%20!%22#$%&'()*+,-./:;%3C=%3E?@[\\]^_%60{|}~", - "origin": "wss://host", - "password": "", - "pathname": "/dir/", - "port":"", - "protocol": "wss:", - "search": "", - "username": "" - }, - "Ensure that input schemes are not ignored when resolving non-special URLs", - { - "input": "abc:rootless", - "base": "abc://host/path", - "hash": "", - "host": "", - "hostname": "", - "href":"abc:rootless", - "password": "", - "pathname": "rootless", - "port":"", - "protocol": "abc:", - "search": "", - "username": "" - }, - { - "input": "abc:rootless", - "base": "abc:/path", - "hash": "", - "host": "", - "hostname": "", - "href":"abc:rootless", - "password": "", - "pathname": "rootless", - "port":"", - "protocol": "abc:", - "search": "", - "username": "" - }, - { - "input": "abc:rootless", - "base": "abc:path", - "hash": "", - "host": "", - "hostname": "", - "href":"abc:rootless", - "password": "", - "pathname": "rootless", - "port":"", - "protocol": "abc:", - "search": "", - "username": "" - }, - { - "input": "abc:/rooted", - "base": "abc://host/path", - "hash": "", - "host": "", - "hostname": "", - "href":"abc:/rooted", - "password": "", - "pathname": "/rooted", - "port":"", - "protocol": "abc:", - "search": "", - "username": "" - }, - "Empty query and fragment with blank should throw an error", - { - "input": "#", - "base": null, - "failure": true - }, - { - "input": "?", - "base": null, - "failure": true - }, - "Last component looks like a number, but not valid IPv4", - { - "input": "http://1.2.3.4.5", - "base": "http://other.com/", - "failure": true - }, - { - "input": "http://1.2.3.4.5.", - "base": "http://other.com/", - "failure": true - }, - { - "input": "http://0..0x300/", - "base": "about:blank", - "failure": true - }, - { - "input": "http://0..0x300./", - "base": "about:blank", - "failure": true - }, - { - "input": "http://256.256.256.256.256", - "base": "http://other.com/", - "failure": true - }, - { - "input": "http://256.256.256.256.256.", - "base": "http://other.com/", - "failure": true - }, - { - "input": "http://1.2.3.08", - "base": "about:blank", - "failure": true - }, - { - "input": "http://1.2.3.08.", - "base": "about:blank", - "failure": true - }, - { - "input": "http://1.2.3.09", - "base": "about:blank", - "failure": true - }, - { - "input": "http://09.2.3.4", - "base": "about:blank", - "failure": true - }, - { - "input": "http://09.2.3.4.", - "base": "about:blank", - "failure": true - }, - { - "input": "http://01.2.3.4.5", - "base": "about:blank", - "failure": true - }, - { - "input": "http://01.2.3.4.5.", - "base": "about:blank", - "failure": true - }, - { - "input": "http://0x100.2.3.4", - "base": "about:blank", - "failure": true - }, - { - "input": "http://0x100.2.3.4.", - "base": "about:blank", - "failure": true - }, - { - "input": "http://0x1.2.3.4.5", - "base": "about:blank", - "failure": true - }, - { - "input": "http://0x1.2.3.4.5.", - "base": "about:blank", - "failure": true - }, - { - "input": "http://foo.1.2.3.4", - "base": "about:blank", - "failure": true - }, - { - "input": "http://foo.1.2.3.4.", - "base": "about:blank", - "failure": true - }, - { - "input": "http://foo.2.3.4", - "base": "about:blank", - "failure": true - }, - { - "input": "http://foo.2.3.4.", - "base": "about:blank", - "failure": true - }, - { - "input": "http://foo.09", - "base": "about:blank", - "failure": true - }, - { - "input": "http://foo.09.", - "base": "about:blank", - "failure": true - }, - { - "input": "http://foo.0x4", - "base": "about:blank", - "failure": true - }, - { - "input": "http://foo.0x4.", - "base": "about:blank", - "failure": true - }, - { - "input": "http://foo.09..", - "base": "about:blank", - "hash": "", - "host": "foo.09..", - "hostname": "foo.09..", - "href":"http://foo.09../", - "password": "", - "pathname": "/", - "port":"", - "protocol": "http:", - "search": "", - "username": "" - }, - { - "input": "http://0999999999999999999/", - "base": "about:blank", - "failure": true - }, - { - "input": "http://foo.0x", - "base": "about:blank", - "failure": true - }, - { - "input": "http://foo.0XFfFfFfFfFfFfFfFfFfAcE123", - "base": "about:blank", - "failure": true - }, - { - "input": "http://💩.123/", - "base": "about:blank", - "failure": true - } -]