# file      : tests/rep-fetch.testscript
# license   : MIT; see accompanying LICENSE file

.include common.testscript \
         auth.testscript   \
         config.testscript \
         remote.testscript \
         remote-git.testscript

# Source repository:
#
# rep-fetch
# |-- bar
# |   |-- stable                  -> ../foo/stable (prerequisite)
# |   |   |-- libbar-1.0.0.tar.gz -> libfoo >= 1.0.0
# |   |   `-- repositories.manifest
# |   |-- testing                 -> stable (complement),
# |   |   |                          ../foo/testing (prerequisite)
# |   |   |-- libbar-1.1.0.tar.gz -> libfoo >= 1.1.0
# |   |   `-- repositories.manifest
# |   `-- unstable                -> testing (complement),
# |       |                          ../foo/testing (prerequisite)
# |       |-- libbar-1.1.1.tar.gz  -> libfoo >= 1.1.0
# |       `-- repositories.manifest
# |
# |-- foo
# |   |-- stable
# |   |   |-- libfoo-1.0.0.tar.gz
# |   |   `-- repositories.manifest
# |   `-- testing                 -> stable (complement)
# |       |-- libfoo-1.1.0.tar.gz
# |       `-- repositories.manifest
# |
# |-- hello
# |   |-- libhello-1.0.0.tar.gz
# |   `-- repositories.manifest
# |
# |-- libhello-1.0.0
# |   |-- build
# |   |   |-- bootstrap.build
# |   |   |-- export.build
# |   |   `-- root.build
# |   `-- *
# |
# |-- cycle
# |   |-- extra                     -> stable (prerequisite)
# |   |   |-- libbar-1.1.0+1.tar.gz
# |   |   `-- repositories.manifest
# |   |-- math                      -> extra (prerequisite)
# |   |   |-- libbar-1.0.0.tar.gz
# |   |   `-- repositories.manifest
# |   `-- stable                    -> math (prerequisite)
# |       |-- libfoo-1.0.0.tar.gz
# |       `-- repositories.manifest
# |
# |-- no-cycle
# |   |-- extra
# |   |   |-- libbar-1.1.0+1.tar.gz
# |   |   `-- repositories.manifest
# |   |-- math
# |   |   |-- libbar-1.0.0.tar.gz
# |   |   `-- repositories.manifest
# |   `-- stable                    -> extra (prerequisite)
# |       |-- libfoo-1.0.0.tar.gz
# |       `-- repositories.manifest
# |
# `-- git/* (see rep-fetch-git.testscript)

posix = ($cxx.target.class != 'windows')

# Prepare repositories used by tests if running in the local mode.
#
+if! $remote
{
  rep_create += 2>!

  # Create the signed 'hello' repository.
  #
  cp -r $src/hello $out/hello
  cat <<<$cert_manifest >+$out/hello/repositories.manifest

  $rep_create --key $key $out/hello &$out/hello/packages.manifest \
                                    &$out/hello/signature.manifest

  # Create the unsigned 'hello' repository.
  #
  cp -r $src/hello $out/hello-unsigned

  $rep_create $out/hello-unsigned &$out/hello-unsigned/packages.manifest \
                                  &$out/hello-unsigned/signature.manifest

  # Create 'foo/*' repositories.
  #
  cp -r $src/foo $out/foo

  # Sign foo/stable repository.
  #
  cat <<<$cert_manifest >+$out/foo/stable/repositories.manifest
  $rep_create --key $key $out/foo/stable &$out/foo/stable/packages.manifest \
                                         &$out/foo/stable/signature.manifest

  # Add dependent trust to foo complement repository into the foo/testing
  # repository manifest.
  #
  tv = "trust: $cert_fp
:"

  sed -i -e "s/^\(:\)\$/$tv/" $out/foo/testing/repositories.manifest

  $rep_create $out/foo/testing &$out/foo/testing/packages.manifest \
                               &$out/foo/testing/signature.manifest

  # Create 'bar/*' repositories.
  #
  cp -r $src/bar $out/bar
  $rep_create $out/bar/stable   &$out/bar/stable/packages.manifest \
                                &$out/bar/stable/signature.manifest
  $rep_create $out/bar/testing  &$out/bar/testing/packages.manifest \
                                &$out/bar/testing/signature.manifest
  $rep_create $out/bar/unstable &$out/bar/unstable/packages.manifest \
                                &$out/bar/unstable/signature.manifest

  # Create 'cycle/*' repositories.
  #
  cp -r $src/cycle $out/cycle
  $rep_create $out/cycle/extra  &$out/cycle/extra/packages.manifest \
                                &$out/cycle/extra/signature.manifest
  $rep_create $out/cycle/math   &$out/cycle/math/packages.manifest \
                                &$out/cycle/math/signature.manifest
  $rep_create $out/cycle/stable &$out/cycle/stable/packages.manifest \
                                &$out/cycle/stable/signature.manifest

  # Create git repositories.
  #
  $git_extract $src/git/state0/libfoo.tar
  $git_extract $src/git/state0/libfox.tar
  $git_extract $src/git/state0/libbar.tar
  $git_extract $src/git/state0/style.tar
  $git_extract $src/git/state0/style-basic.tar &$out_git/state0/***

  $git_extract $src/git/state1/libfoo.tar
  $git_extract $src/git/state1/libbaz.tar
  $git_extract $src/git/state1/style.tar
  $git_extract $src/git/state1/style-basic.tar &$out_git/state1/***
}

rep_add      += -d cfg 2>!
rep_list     += -d cfg --prerequisites --complements
rep_remove   += -d cfg 2>!
pkg_status   += -d cfg
pkg_fetch    += -d cfg 2>!
pkg_unpack   += -d cfg 2>!
pkg_checkout += -d cfg
pkg_purge    += -d cfg
pkg_drop     += -d cfg --yes 2>!

: no-repos
:
$clone_cfg;
$* 2>>/EOE != 0
  error: configuration cfg/ has no repositories
    info: use 'bpkg rep-add' to add a repository
  EOE

: pkg-rep
:
{{
  test.options += --auth all

  : hello
  :
  {
    $clone_root_cfg && $rep_add $rep/hello

    $* --trust $cert_fp 2>>~%EOE% &cfg/.bpkg/certs/**
      fetching pkg:build2.org/rep-fetch/hello
      %.*
      1 package(s) in 1 repository(s)
      EOE

    $* 2>>~%EOE%
      fetching pkg:build2.org/rep-fetch/hello
      %.*
      1 package(s) in 1 repository(s)
      EOE
  }

  : bar-unstable
  :
  {
    $clone_root_cfg && $rep_add $rep/bar/unstable

    $* --trust-yes 2>>~%EOE% &cfg/.bpkg/certs/**
      fetching pkg:build2.org/rep-fetch/bar/unstable
      %.*
      fetching pkg:build2.org/rep-fetch/bar/testing (complements pkg:build2.org/rep-fetch/bar/unstable)
      %.*
      fetching pkg:build2.org/rep-fetch/bar/stable (complements pkg:build2.org/rep-fetch/bar/testing)
      %.*
      fetching pkg:build2.org/rep-fetch/foo/stable (prerequisite of pkg:build2.org/rep-fetch/bar/stable)
      %.*
      fetching pkg:build2.org/rep-fetch/foo/testing (prerequisite of pkg:build2.org/rep-fetch/bar/testing)
      %.*
      5 package(s) in 5 repository(s)
      EOE

    $* 2>>~%EOE%
      fetching pkg:build2.org/rep-fetch/bar/unstable
      %.*
      fetching pkg:build2.org/rep-fetch/bar/testing (complements pkg:build2.org/rep-fetch/bar/unstable)
      %.*
      fetching pkg:build2.org/rep-fetch/bar/stable (complements pkg:build2.org/rep-fetch/bar/testing)
      %.*
      fetching pkg:build2.org/rep-fetch/foo/stable (prerequisite of pkg:build2.org/rep-fetch/bar/stable)
      %.*
      fetching pkg:build2.org/rep-fetch/foo/testing (prerequisite of pkg:build2.org/rep-fetch/bar/testing)
      %.*
      5 package(s) in 5 repository(s)
      EOE
  }

  : both
  :
  {
    $clone_root_cfg && $rep_add $rep/hello $rep/bar/unstable

    $* --trust-yes 2>>~%EOE% &cfg/.bpkg/certs/**
      fetching pkg:build2.org/rep-fetch/bar/unstable
      %.*
      fetching pkg:build2.org/rep-fetch/bar/testing (complements pkg:build2.org/rep-fetch/bar/unstable)
      %.*
      fetching pkg:build2.org/rep-fetch/bar/stable (complements pkg:build2.org/rep-fetch/bar/testing)
      %.*
      fetching pkg:build2.org/rep-fetch/foo/stable (prerequisite of pkg:build2.org/rep-fetch/bar/stable)
      %.*
      fetching pkg:build2.org/rep-fetch/foo/testing (prerequisite of pkg:build2.org/rep-fetch/bar/testing)
      %.*
      fetching pkg:build2.org/rep-fetch/hello
      %.*
      6 package(s) in 6 repository(s)
      EOE

    $* 2>>~%EOE%
      fetching pkg:build2.org/rep-fetch/bar/unstable
      %.*
      fetching pkg:build2.org/rep-fetch/bar/testing (complements pkg:build2.org/rep-fetch/bar/unstable)
      %.*
      fetching pkg:build2.org/rep-fetch/bar/stable (complements pkg:build2.org/rep-fetch/bar/testing)
      %.*
      fetching pkg:build2.org/rep-fetch/foo/stable (prerequisite of pkg:build2.org/rep-fetch/bar/stable)
      %.*
      fetching pkg:build2.org/rep-fetch/foo/testing (prerequisite of pkg:build2.org/rep-fetch/bar/testing)
      %.*
      fetching pkg:build2.org/rep-fetch/hello
      %.*
      6 package(s) in 6 repository(s)
      EOE
  }

  : location-args
  :
  {
    $clone_root_cfg

    $* --trust-yes $rep/bar/unstable 2>>~%EOE% &cfg/.bpkg/certs/**
      added pkg:build2.org/rep-fetch/bar/unstable
      %.*
      fetching pkg:build2.org/rep-fetch/bar/testing (complements pkg:build2.org/rep-fetch/bar/unstable)
      %.*
      fetching pkg:build2.org/rep-fetch/bar/stable (complements pkg:build2.org/rep-fetch/bar/testing)
      %.*
      fetching pkg:build2.org/rep-fetch/foo/stable (prerequisite of pkg:build2.org/rep-fetch/bar/stable)
      %.*
      fetching pkg:build2.org/rep-fetch/foo/testing (prerequisite of pkg:build2.org/rep-fetch/bar/testing)
      %.*
      5 package(s) in 5 repository(s)
      EOE

    $* 'pkg:build2.org/rep-fetch/bar/unstable' 2>>~%EOE%
      %.*
      fetching pkg:build2.org/rep-fetch/bar/testing (complements pkg:build2.org/rep-fetch/bar/unstable)
      %.*
      fetching pkg:build2.org/rep-fetch/bar/stable (complements pkg:build2.org/rep-fetch/bar/testing)
      %.*
      fetching pkg:build2.org/rep-fetch/foo/stable (prerequisite of pkg:build2.org/rep-fetch/bar/stable)
      %.*
      fetching pkg:build2.org/rep-fetch/foo/testing (prerequisite of pkg:build2.org/rep-fetch/bar/testing)
      %.*
      5 package(s) in 5 repository(s)
      EOE

    $rep_list >>"EOO"
      pkg:build2.org/rep-fetch/bar/unstable ($rep/bar/unstable)
        complement pkg:build2.org/rep-fetch/bar/testing ($rep/bar/testing)
          complement pkg:build2.org/rep-fetch/bar/stable ($rep/bar/stable)
            prerequisite pkg:build2.org/rep-fetch/foo/stable ($rep/foo/stable)
          prerequisite pkg:build2.org/rep-fetch/foo/testing ($rep/foo/testing)
            complement pkg:build2.org/rep-fetch/foo/stable ($rep/foo/stable)
        prerequisite pkg:build2.org/rep-fetch/foo/testing ($rep/foo/testing)
          complement pkg:build2.org/rep-fetch/foo/stable ($rep/foo/stable)
      EOO
  }

  : prerequisites-cycle
  :
  {
    $clone_root_cfg

    $* --trust-yes $rep/cycle/stable 2>>~%EOE%
      added pkg:build2.org/rep-fetch/cycle/stable
      %.*
      fetching pkg:build2.org/rep-fetch/cycle/math (prerequisite of pkg:build2.org/rep-fetch/cycle/stable)
      %.*
      fetching pkg:build2.org/rep-fetch/cycle/extra (prerequisite of pkg:build2.org/rep-fetch/cycle/math)
      %.*
      3 package(s) in 3 repository(s)
      EOE

    $rep_list >>"EOO"
      pkg:build2.org/rep-fetch/cycle/stable ($rep/cycle/stable)
        prerequisite pkg:build2.org/rep-fetch/cycle/math ($rep/cycle/math)
          prerequisite pkg:build2.org/rep-fetch/cycle/extra ($rep/cycle/extra)
            prerequisite pkg:build2.org/rep-fetch/cycle/stable ($rep/cycle/stable)
      EOO
  }

  : unreferenced-prerequisite
  :
  {{
    +$clone_root_cfg

    # Create the no-cycle repository as the new state of the cycle repository.
    # The cycle dependency is now broken (extra/ doesn't depend on stable/
    # anymore) and the extra/ prerequisite is moved from math/ to stable/.
    #
    nc_rep = [dir_path] $~/pkg/1/build2.org/rep-fetch/cycle/
    +mkdir -p $path.directory($nc_rep)
    +cp -r $src/no-cycle "$nc_rep"     # Strip trailing slash.

    +$rep_create $nc_rep/extra  2>! &$nc_rep/extra/packages.manifest \
                                    &$nc_rep/extra/signature.manifest
    +$rep_create $nc_rep/math   2>! &$nc_rep/math/packages.manifest \
                                    &$nc_rep/math/signature.manifest
    +$rep_create $nc_rep/stable 2>! &$nc_rep/stable/packages.manifest \
                                    &$nc_rep/stable/signature.manifest

    math_rep   = $nc_rep/math
    stable_rep = $nc_rep/stable

    : remove
    :
    {
      $clone_cfg

      $* --trust-yes $rep/cycle/stable 2>>~%EOE%
        added pkg:build2.org/rep-fetch/cycle/stable
        %.*
        fetching pkg:build2.org/rep-fetch/cycle/math (prerequisite of pkg:build2.org/rep-fetch/cycle/stable)
        %.*
        fetching pkg:build2.org/rep-fetch/cycle/extra (prerequisite of pkg:build2.org/rep-fetch/cycle/math)
        %.*
        3 package(s) in 3 repository(s)
        EOE

      $* --trust-yes $stable_rep 2>>~%EOE%
        updated pkg:build2.org/rep-fetch/cycle/stable
        fetching pkg:build2.org/rep-fetch/cycle/stable
        %querying .+stable%
        fetching pkg:build2.org/rep-fetch/cycle/extra (prerequisite of pkg:build2.org/rep-fetch/cycle/stable)
        %querying .+extra%
        fetched pkg:build2.org/rep-fetch/cycle/stable
        2 package(s) in 2 repository(s)
        EOE

      $rep_list >>"EOO"
        pkg:build2.org/rep-fetch/cycle/stable ($nc_rep/stable)
          prerequisite pkg:build2.org/rep-fetch/cycle/extra ($nc_rep/extra)
        EOO
    }

    : no-remove
    :
    {
      $clone_cfg

      $* --trust-yes $rep/cycle/stable 2>>~%EOE%
        added pkg:build2.org/rep-fetch/cycle/stable
        %.*
        fetching pkg:build2.org/rep-fetch/cycle/math (prerequisite of pkg:build2.org/rep-fetch/cycle/stable)
        %.*
        fetching pkg:build2.org/rep-fetch/cycle/extra (prerequisite of pkg:build2.org/rep-fetch/cycle/math)
        %.*
        3 package(s) in 3 repository(s)
        EOE

      $* --trust-yes $math_rep $stable_rep 2>>~%EOE%
        added pkg:build2.org/rep-fetch/cycle/math
        updated pkg:build2.org/rep-fetch/cycle/stable
        fetching pkg:build2.org/rep-fetch/cycle/math
        %querying .+math%
        fetching pkg:build2.org/rep-fetch/cycle/stable
        %querying .+stable%
        fetching pkg:build2.org/rep-fetch/cycle/extra (prerequisite of pkg:build2.org/rep-fetch/cycle/stable)
        %querying .+extra%
        fetched pkg:build2.org/rep-fetch/cycle/math
        fetched pkg:build2.org/rep-fetch/cycle/stable
        3 package(s) in 3 repository(s)
        EOE

      $rep_list >>"EOO"
        pkg:build2.org/rep-fetch/cycle/math ($nc_rep/math)
        pkg:build2.org/rep-fetch/cycle/stable ($nc_rep/stable)
          prerequisite pkg:build2.org/rep-fetch/cycle/extra ($nc_rep/extra)
        EOO
    }
  }}

  : use-auth
  :
  {{
    : dependent-trust
    :
    : Test that the certificate of foo/stable complement repository is
    : silently authenticated for use by the dependent foo/testing repository.
    : In this case the certificate is not saved into the database (see the
    : subsequent 'rep-fetch $rep/foo/stable' test) and certificate file is not
    : persisted (otherwise cleanup of non-empty cfg/ directory would fail).
    :
    {
      $clone_root_cfg

      $* -v $rep/foo/testing <'y' 2>>~%EOE%
        added pkg:build2.org/rep-fetch/foo/testing
        %.*
        warning: repository pkg:build2.org/rep-fetch/foo/testing is unsigned
        %continue without authenticating repositories at .+\? \[y/n\] .+%
        %.+
        info: certificate for repository pkg:build2.org/rep-fetch/foo/stable authenticated by dependent trust
        %.+
        2 package(s) in 2 repository(s)
        EOE

      $* $rep/foo/stable 2>>~%EOE% != 0
        %.+
        warning: authenticity of the certificate for repository pkg:build2.org/rep-fetch/foo/stable cannot be established
        %.+
        EOE
    }

    : dependent-command-line
    :
    : Test that the certificate of foo/stable complement repository is
    : authenticated for use by the command line (persisted into the database
    : and the filesystem) rather than dependent trust.
    :
    {
      $clone_root_cfg

      $* -v --trust $cert_fp $rep/foo/testing <'y' 2>>~%EOE% &cfg/.bpkg/certs/**
        added pkg:build2.org/rep-fetch/foo/testing
        %.*
        warning: repository pkg:build2.org/rep-fetch/foo/testing is unsigned
        %continue without authenticating repositories at .+\? \[y/n\] .+%
        %.+
        info: certificate for repository pkg:build2.org/rep-fetch/foo/stable authenticated by command line
        %.+
        2 package(s) in 2 repository(s)
        EOE
    }

    : dependent-trust-prompt
    :
    : Test that the certificate of foo/stable repository is first authenticated
    : for use by the dependent foo/test repository and then by the user (via
    : the prompt) as a top-level repository during a single rep-fetch
    : operation.
    :
    {
      yy = 'y
y'
      $clone_root_cfg

      $* -v $rep/foo/testing $rep/foo/stable <$yy 2>>~%EOE% &cfg/.bpkg/certs/**
        added pkg:build2.org/rep-fetch/foo/testing
        added pkg:build2.org/rep-fetch/foo/stable
        fetching pkg:build2.org/rep-fetch/foo/testing
        %.*
        warning: repository pkg:build2.org/rep-fetch/foo/testing is unsigned
        %continue without authenticating repositories at .+\? \[y/n\] .+%
        %.+
        info: certificate for repository pkg:build2.org/rep-fetch/foo/stable authenticated by dependent trust
        %.+
        warning: authenticity of the certificate for repository pkg:build2.org/rep-fetch/foo/stable cannot be established
        certificate is for build2.org, "Code Synthesis" <info@build2.org>
        %.+
        %2 package\(s\) in 2 repository\(s\)%
        EOE
    }

    : command-line-dependent-noop
    :
    : Test that the certificate of foo/stable repository is first authenticated
    : by the user (via the command line) as a top-level repository and so
    : authentication for use by the dependent foo/test is noop.
    :
    {
      $clone_root_cfg

      $* -v --trust $cert_fp $rep/foo/stable $rep/foo/testing <'y' 2>>~%EOE% &cfg/.bpkg/certs/**
        added pkg:build2.org/rep-fetch/foo/stable
        added pkg:build2.org/rep-fetch/foo/testing
        fetching pkg:build2.org/rep-fetch/foo/stable
        %.+
        info: certificate for repository pkg:build2.org/rep-fetch/foo/stable authenticated by command line
        %.+
        fetching pkg:build2.org/rep-fetch/foo/testing
        %.*
        warning: repository pkg:build2.org/rep-fetch/foo/testing is unsigned
        %continue without authenticating repositories at .+\? \[y/n\] .+%
        %.*
        2 package(s) in 2 repository(s)
        EOE
    }
  }}
}}

: dir-rep
:
: For dir repository tests we will reuse local git repositories, that have the
: same repository structure.
:
if! $remote
{{
  rep_add += --type dir

  +cp -r $out_git/state0 $~/state0

  rep = $canonicalize([dir_path] $~/state0)

  +cat <<EOI >= $rep/libbar.git/repositories.manifest
     : 1
     role: prerequisite
     type: dir
     location: ../style-basic.git
     EOI

  +$clone_cfg && $rep_add $rep/libbar.git

  d = $canonicalize($rep)

  # Note that on Windows the local repository canonical name path part is
  # converted to lower case.
  #
  if! $posix
    d = [dir_path] $string.lcase($d)

  : prerequisites
  :
  {
    $clone_cfg

    $* 2>>"EOE"
      fetching dir:($d/libbar.git)
      fetching dir:($d/style-basic.git) \(prerequisite of dir:($d/libbar.git)\)
      fetched dir:($d/libbar.git)
      3 package\(s\) in 2 repository\(s\)
      EOE

    $rep_list >>~%EOO%
      %dir:.+libbar\.git dir\+file:///.+libbar\.git%
      %  prerequisite dir:.+style-basic\.git dir\+file:///.+style-basic\.git%
      EOO
  }

  : by-name
  :
  : Here we, in particular, test that the local repository canonical name
  : (dir:/...) is not confused with the repository URL.
  :
  {
    $clone_cfg
    $* 2>!

    $* "dir:($d/libbar.git)" 2>>"EOE"
      fetching dir:($d/libbar.git)
      fetching dir:($d/style-basic.git) \(prerequisite of dir:($d/libbar.git)\)
      fetched dir:($d/libbar.git)
      3 package\(s\) in 2 repository\(s\)
      EOE

    $rep_list >>~%EOO%
      %dir:.+libbar\.git dir\+file:///.+libbar\.git%
      %  prerequisite dir:.+style-basic\.git dir\+file:///.+style-basic\.git%
      EOO
  }

  : shallow
  :
  {{
    +$clone_cfg

    : respected
    :
    {
      $clone_cfg
      $* 2>!

      $* --shallow "dir:($d/libbar.git)" 2>>~"%EOE%"
        %fetching .+libbar.git%
        %fetched .+libbar.git%
        3 package\(s\) in 2 repository\(s\)
        EOE

      $rep_list >>~%EOO%
        %dir:.+libbar\.git dir\+file:///.+libbar\.git%
        %  prerequisite dir:.+style-basic\.git dir\+file:///.+style-basic\.git%
        EOO
    }

    : ignored
    :
    {
      $clone_cfg

      $* --shallow 2>>"EOE"
        fetching dir:($d/libbar.git)
        fetching dir:($d/style-basic.git) \(prerequisite of dir:($d/libbar.git)\)
        fetched dir:($d/libbar.git)
        3 package\(s\) in 2 repository\(s\)
        EOE

      $rep_list >>~%EOO%
        %dir:.+libbar\.git dir\+file:///.+libbar\.git%
        %  prerequisite dir:.+style-basic\.git dir\+file:///.+style-basic\.git%
        EOO
    }
  }}
}}

: iter
:
{{
  rep_add += --type dir

  : multiple-repos
  :
  {
    cp -r $src/libhello-1.0.0 libhello1
    cp -r $src/libhello-1.0.0 libhello2

    $clone_root_cfg && $rep_add libhello1 libhello2

    $* 2>>~%EOE% != 0
      %fetching dir:.+libhello1%
      %fetching dir:.+libhello2%
      error: external package libhello/1.0.0 is available from two repositories
      %  info: repository .+libhello1%
      %  info: repository .+libhello2%
      EOE
  }

  : inc
  :
  {{
    : path-changed
    :
    {
      $clone_root_cfg && $rep_add $src/libhello-1.0.0

      $* 2>!
      $pkg_unpack libhello/1.0.0

      $rep_remove --all

      cp -r $src/libhello-1.0.0 libhello
      $rep_add libhello

      $* 2>!

      $pkg_status libhello >'libhello unpacked 1.0.0 available 1.0.0#1'
    }

    : manifest-changed
    :
    {
      cp -r $src/libhello-1.0.0 libhello

      $clone_root_cfg && $rep_add libhello

      $* 2>!
      $pkg_unpack libhello/1.0.0

      echo "" >+ libhello/manifest
      $* 2>!

      $pkg_status libhello >'libhello unpacked 1.0.0 available 1.0.0#1'
    }

    : pkg-rep
    {{
      +$clone_root_cfg

      +$* --auth all --trust-yes $rep/hello &cfg/.bpkg/certs/** 2>!
      +$pkg_fetch  libhello/1.0.0
      +$pkg_unpack libhello

      : unchanged-external
      :
      : Test that iteration is still incremented when a non-external package
      : from a pkg repository is switched to the same unedited external
      : package.
      :
      {
        $clone_cfg && $rep_add $src/libhello-1.0.0
        $* 2>!

        $pkg_status libhello >'libhello unpacked 1.0.0 available 1.0.0#1'
      }

      : changed-external
      :
      {
        cp -r $src/libhello-1.0.0 libhello
        echo "" >+ libhello/manifest

        $clone_cfg && $rep_add libhello
        $* 2>!

        $pkg_status libhello >'libhello unpacked 1.0.0 available 1.0.0#1'
      }

      -$pkg_purge libhello 2>'purged libhello/1.0.0'
    }}

    : git-rep
    :
    : Test that iteration is still incremented when a non-external package
    : from a git repository is switched to the same unedited external package.
    :
    if ($git_supported && !$remote)
    {
      rep = $canonicalize([dir_path] $out_git/state0)

      $clone_root_cfg

      $* "$rep/style.git#master" 2>! &cfg/.bpkg/repos/*/***

      $pkg_checkout "style/1.0.0" 2>!

      $rep_add $rep/style.git
      $* 2>!

      $pkg_status style >"style unpacked 1.0.0 available 1.0.0#1"

      $pkg_purge style 2>"purged style/1.0.0"
    }
  }}

  : no-inc
  :
  {
    $clone_root_cfg

    $pkg_unpack -e $src/libhello-1.0.0

    $rep_add $src/libhello-1.0.0

    $* 2>>~%EOE%
      %fetching dir:.+libhello-1.0.0%
      %fetched dir:.+libhello-1.0.0%
      1 package(s) in 1 repository(s)
      EOE

    $pkg_status libhello >'libhello unpacked 1.0.0'
  }
}}

: git-rep
:
if! $git_supported
{{
  # Skip git repository tests.
  #
}}
else
{{
  : proto
  :
  if! $remote
  {{
    git_protocol = 'local'
    .include rep-fetch-git.testscript
  }}
  else
  {{
    : https-dumb
    :
    {{
      git_protocol = 'https-dumb'
      .include rep-fetch-git.testscript
    }}

    : https-smart
    :
    {{
      git_protocol = 'https-smart'
      .include rep-fetch-git.testscript
    }}

    : https-smart-unadv
    :
    {{
      git_protocol = 'https-smart-unadv'
      .include rep-fetch-git.testscript
    }}

    : git
    :
    {{
      git_protocol = 'git'
      .include rep-fetch-git.testscript
    }}

    : git-unadv
    :
    {{
      git_protocol = 'git-unadv'
      .include rep-fetch-git.testscript
    }}

    : ssh
    :
    if $git_ssh
    {{
      git_protocol = 'ssh'
      .include rep-fetch-git.testscript
    }}

    : ssh-unadv
    :
    if $git_ssh
    {{
      git_protocol = 'ssh-unadv'
      .include rep-fetch-git.testscript
    }}
  }}

  : strip-ext
  :
  : Test that if the local prerequisite git repository having the .git
  : extension does not exist but the one without the extension does, then
  : it is picked up by rep-fetch.
  :
  if! $remote
  {
    cp -r $out_git/state0/libbar.git libbar.git
    cp -r $out_git/state0/style-basic.git style-basic

    $clone_root_cfg

    rep = ($posix \
           ? "file:$~" \
           : "file:/$regex.replace($~, '\\', '/')")

    rep = "$regex.replace($rep, ' ', '%20')"

    $* "$rep/libbar.git#master" 2>>~%EOE% &cfg/.bpkg/repos/*/***
      %added git:.+libbar#master%
      %fetching git:.+libbar#master%
      %querying .+libbar\.git%
      %fetching .+libbar\.git%
      %fetching git:.+style-basic#stable \(prerequisite of git:.+libbar#master\)%
      %querying .+style-basic%
      %fetching .+style-basic%
      %fetched git:.+libbar#master%
      3 package(s) in 2 repository(s)
      EOE

    $rep_list >>~%EOO%
      %git:.+libbar#master file:.+libbar\.git#master%
      %  prerequisite git:.+style-basic#stable git\+file:.+style-basic#stable \(heads/master\)%
      EOO
  }

  : checkout-submodules
  :
  : Test that if some of the packages are located in the repository
  : submodules directory, then rep-fetch checks out submoodules.
  :
  {
    $clone_root_cfg && $rep_add "$rep_git/state0/libfox.git#master"

    $* 2>>~%EOE% &cfg/.bpkg/repos/*/***
      %fetching git:.+libfox#master%
      %querying .+libfox\.git%
      %fetching .+libfox\.git%
      %info: .+%{0,2}
      %querying .+libbar\.git%?
      %fetching .+libbar\.git for submodule 'libbar'%
      %info: .+%{0,2}
      %submodule path 'libbar': checked out '.+'%
      %querying .+style-basic\.git%?
      %fetching .+style-basic\.git for submodule 'libbar/extras'%
      %info: .+%{0,2}
      %submodule path 'libbar/extras': checked out '.+'%
      %fetched git:.+libfox#master%
      2 package(s) in 1 repository(s)
      EOE
  }

  : re-fetching
  :
  : Test that repository is re-fetched on the location change. Here it happens
  : due to the scheme change.
  :
  if $remote
  {
    $clone_root_cfg && $rep_add "$rep_git_git/state0/libfoo.git#master"
    $* 2>! &cfg/.bpkg/repos/*/***
    $rep_add "$rep_git_https_smart/state0/libfoo.git#master"

    $* 2>>~%EOE%
      %fetching git:.+libfoo#master%
      %info: location changed for git:.+libfoo#master%
      %  info: new location https://.+libfoo\.git#master%
      %  info: old location git://.+libfoo\.git#master%
      %querying .+libfoo\.git%
      %fetched git:.+libfoo#master%
      1 package(s) in 1 repository(s)
      EOE
  }

  : cleanup-failed
  :
  : Test that configuration is cleaned up if fetch fails after the repository
  : filesystem state is changed.
  :
  {
    $clone_root_cfg && $rep_add "$rep_git/state0/libfoo.git#master"

    # Extract the repository path from the output line like this:
    #
    # fetching from <url> in 'cfg/.bpkg/tmp/4bde15f59461'...
    #
    $* -v 2>&1                                         | \
      sed -n -e "s/fetching .+ in '\(.+\)'/\$1/p" | \
      sed -n -e 's%(.+[\\/])tmp([\\/].+)%$1repos$2%p'  | \
      set r

    # Break the repo (drop the remote repository URL) and try to re-fetch.
    #
    rm "$r/.git/config"

    $* 2>>~%EOE% != 0
      %.+
      warning: repository state is now broken and will be cleaned up
        info: run 'bpkg rep-fetch' to update
      EOE

    $pkg_status libfoo >'libfoo unknown'
  }

  : ref-filters
  :
  {{
    test.cleanups += &?cfg/.bpkg/repos/*/***

    : pattern
    :
    {{
      : matching
      :
      {
        $clone_root_cfg

        $* "$rep_git/state0/libfoo.git#v**" 2>>~%EOE%
          %added .+libfoo#v\*\*%
          %fetching .+libfoo#v\*\*%
          %querying .+libfoo\.git%
          %fetching .+libfoo\.git%
          %info: .+%{0,2}
          %fetched .+libfoo#v\*\*%
          2 package(s) in 1 repository(s)
          EOE
      }

      : non-matching
      :
      {
        $clone_root_cfg

        $* "$rep_git/state0/libfoo.git#tags/mast*" 2>>~%EOE%
          %added .+libfoo#tags/mast\*%
          %fetching .+libfoo#tags/mast\*%
          %querying .+libfoo\.git%
          %fetched .+libfoo#tags/mast\*%
          0 package(s) in 1 repository(s)
          EOE
      }
    }}

    : name
    :
    {{
      : non-matching
      :
      {
        $clone_root_cfg

        $* "$rep_git/state0/libfoo.git#foo" 2>>~%EOE% != 0
          %added .+libfoo#foo%
          %fetching .+libfoo#foo%
          %querying .+libfoo\.git%
          error: reference 'foo' is not found
          EOE
      }
    }}

    : commit
    :
    {{
      : non-matching-pattern
      :
      {
        $clone_root_cfg

        if ($remote || !$rep_git_local_unadv)
        {
          $* "$rep_git/state0/libfoo.git#foo*@9ab039761936802d61b8e591d6812a4dd4605029" 2>>~%EOE% != 0
            %added .+libfoo#foo\*@9ab039761936802d61b8e591d6812a4dd4605029%
            %fetching .+libfoo#foo\*@9ab039761936802d61b8e591d6812a4dd4605029%
            %querying .+libfoo\.git%
            error: no names match pattern 'foo*'
            EOE
        }
        elif $posix # Note: transient `permission denied` on Windows for `rm -r .bpkg\tmp`.
        {
          $* "$rep_git/state0/libfoo.git#foo*@9ab039761936802d61b8e591d6812a4dd4605029" 2>>~%EOE% != 0
            %added .+libfoo#foo\*@9ab039761936802d61b8e591d6812a4dd4605029%
            %fetching .+libfoo#foo\*@9ab039761936802d61b8e591d6812a4dd4605029%
            %fetching .+libfoo\.git%
            %.+
            %error: unable to fetch .+%
            %.+
            EOE
        }
      }

      : unexisting
      :
      {
        $clone_root_cfg

        if ($remote || !$rep_git_local_unadv)
        {
          $* "$rep_git/state0/libfoo.git#9ab039761936802d61b8e591d6812a4dd4605029" 2>>~%EOE% != 0
            %added .+libfoo#9ab039761936802d61b8e591d6812a4dd4605029%
            %fetching .+libfoo#9ab039761936802d61b8e591d6812a4dd4605029%
            %querying .+libfoo\.git%
            %fetching .+libfoo\.git%
            %info: .+%{0,2}
            error: unable to fetch commit 9ab039761936802d61b8e591d6812a4dd4605029
            EOE
        }
        elif $posix # Note: transient `permission denied` (see above).
        {
          $* "$rep_git/state0/libfoo.git#9ab039761936802d61b8e591d6812a4dd4605029" 2>>~%EOE% != 0
            %added .+libfoo#9ab039761936802d61b8e591d6812a4dd4605029%
            %fetching .+libfoo#9ab039761936802d61b8e591d6812a4dd4605029%
            %fetching .+libfoo\.git%
            %.+
            %error: unable to fetch .+%
            %.+
            EOE
        }
      }
    }}

    : exclusion
    :
    {
      $clone_root_cfg

      $* "$rep_git/state0/libfoo.git#mast*,-master" 2>>~%EOE%
        %added .+libfoo#mast\*,-master%
        %fetching .+libfoo#mast\*,-master%
        %querying .+libfoo\.git%
        %fetched .+libfoo#mast\*,-master%
        0 package(s) in 1 repository(s)
        EOE
    }

    : inclusion
    :
    {
      $clone_root_cfg

      $* "$rep_git/state0/libfoo.git#tags/mast*,+master" 2>>~%EOE%
        %added .+libfoo#tags/mast\*,\+master%
        %fetching .+libfoo#tags/mast\*,\+master%
        %querying .+libfoo\.git%
        %fetching .+libfoo\.git%
        %info: .+%{0,2}
        %fetched .+libfoo#tags/mast\*,\+master%
        1 package(s) in 1 repository(s)
        EOE
    }

    : default
    :
    {{
      : no-fragment
      :
      {
        $clone_root_cfg

        $* "$rep_git/state0/libfoo.git" 2>>~%EOE%
          %added .+libfoo%
          %fetching .+libfoo%
          %querying .+libfoo\.git%
          %fetching .+libfoo\.git%
          %info: .+%{0,2}
          %fetched .+libfoo%
          2 package(s) in 1 repository(s)
          EOE
      }

      : with-exclusion
      :
      {
        $clone_root_cfg

        $* "$rep_git/state0/libfoo.git##-/tags/v1*" 2>>~%EOE%
          %added .+libfoo##-/tags/v1\*%
          %fetching .+libfoo##-/tags/v1\*%
          %querying .+libfoo\.git%
          %fetching .+libfoo\.git%
          %info: .+%{0,2}
          %fetched .+libfoo##-/tags/v1\*%
          1 package(s) in 1 repository(s)
          EOE
      }

      : latest-revision
      :
      {
        $clone_root_cfg

        $* "$rep_git/state0/libbar.git" 2>>~%EOE%
          %.+
          3 package(s) in 2 repository(s)
          EOE

        $pkg_status libbar >'libbar available 1.0.0+1'
      }
    }}
  }}
}}

: fetch-cache
:
{{
  # Enable the test-specific fetch caches.
  #
  options_guard = $~/.build2
  +mkdir $options_guard
  +echo '--no-default-options' >=$options_guard/bpkg.options
  +export -u BPKG_FETCH_CACHE
  test.options += --fetch-cache-path cache

  +$cfg_create -d cfg 2>! &cfg/***

  : pkg-rep
  :
  {{
    test.options += --auth all -v

    +$clone_cfg

    : signed-repository
    :
    {
      $clone_cfg && $rep_add $rep/hello

      # Verify that for the first repository fetch we download all the manifest
      # files and authenticate the certificate.
      #
      $* --trust $cert_fp 2>&1 &cache/*** | set out

      sed -n -e 's%(certificate .* authenticated by command line)%\1%p' <$out >~/.+/

      if $remote
      {
        sed -n -e 's%(https://.*/repositories.manifest)%\1%p' <$out >>~%EOO%
          %.+
          EOO

        sed -n -e 's%(https://.*/packages.manifest)%\1%p' <$out >>~%EOO%
          %.+
          EOO

        sed -n -e 's%(https://.*/signature.manifest)%\1%p' <$out >>~%EOO%
          %.+
          EOO
      }

      # Verify that the fetched manifest files are cached.
      #
      ms = $sort($filesystem.path_search(*/*.manifest, $~/cache/pkg/metadata))

      sed -n -e 's%^(.+packages.manifest)$%\1%p'     <($ms[0]) >~/.+/
      sed -n -e 's%^(.+repositories.manifest)$%\1%p' <($ms[1]) >~/.+/

      rm -r cfg

      $clone_cfg && $rep_add $rep/hello

      # Verify that now, as the metadata and authentication answers are cached,
      # we don't download the repositories and packages manifest files nor
      # authenticate the certificate.
      #
      $* 2>&1 | set out

      sed -n -e 's%(certificate .* authenticated by command line)%\1%p' <$out >:''

      if $remote
      {
        sed -n -e 's%(https://.*/repositories.manifest)%\1%p' <$out >:''
        sed -n -e 's%(https://.*/packages.manifest)%\1%p'     <$out >:''

        # Note: we should still be downloading the signature manifest to perform
        # the up-to-dateness check.
        #
        sed -n -e 's%(https://.*/signature.manifest)%\1%p' <$out >>~%EOO%
          %.+
          EOO
      }

      $rep_list >~'%pkg:build2.org/rep-fetch/hello.+%'
      $pkg_status libhello >'libhello available 1.0.0'

      $rep_remove --all
    }

    : unsigned-repository
    :
    {
      $clone_cfg && $rep_add $rep/hello-unsigned

      # Verify that for the first unsigned repository fetch we trust the
      # repository by command line.
      #
      $* --trust-yes 2>&1 &cache/*** | set out

      sed -n -e 's%(unsigned repository .* trusted by command line)%\1%p' <$out >~/.+/

      rm -r cfg

      $clone_cfg && $rep_add $rep/hello-unsigned

      # Verify that now, as the authentication answers are cached, the unsigned
      # repository is trusted automatically.
      #
      $* 2>&1 | set out

      sed -n -e 's%(unsigned repository .* trusted by command line)%\1%p' <$out >:''

      $rep_list >~'%pkg:build2.org/rep-fetch/hello-unsigned.+%'
      $pkg_status libhello >'libhello available 1.0.0'

      $rep_remove --all
    }
  }}

  : git-rep
  :
  if $git_supported
  {{
    rep_git = ($remote ? $rep_git_https_smart_unadv : $rep_git_local)

    +$clone_cfg

    : basics
    :
    {
      $clone_cfg && $rep_add "$rep_git/state0/libfoo.git#master"

      $* --offline &cache/*** 2>>~%EOE% != 0
        %fetching git:.+libfoo#master%
        %error: no state in fetch cache for repository .+libfoo.git#master in offline mode%
          info: consider turning offline mode off
        EOE

      test.options += --fetch-cache-session 2487f19f-230e-4692-b9e3-0c3654d88a5b

      # Verify that for the first repository fetch we run git-ls-remote and
      # git-fetch.
      #
      $* -v 2>&1 &cache/*** | set out

      sed -n -e 's%^(git.* ls-remote .*)$%\1%p'        <$out >~/.+/
      sed -n -e 's%^(git.* .*fetch .*origin .*)$%\1%p' <$out >~/.+/

      sed -n -e 's%^(fetching git:.*libfoo#master)$%\1%p'          <$out >~/.+/
      sed -n -e 's%^(querying .*libfoo.git \(cache\))$%\1%p'       <$out >~/.+/
      sed -n -e 's%^(fetching .*libfoo.git in .+ \(cache\))$%\1%p' <$out >~/.+/

      # Verify that the fetched filesystem entries are cached.
      #
      out=$filesystem.path_search(*/repository/, $~/cache/git)
      sed -n -e 's%^(.+repository.)$%\1%p' <$out >~/.+/

      out=$filesystem.path_search(*/ls-remote.txt, $~/cache/git)
      sed -n -e 's%^(.+ls-remote.txt)$%\1%p' <$out >~/.+/

      rm -r cfg

      $clone_cfg && $rep_add "$rep_git/state0/libfoo.git#master"

      # Verify that now, as the repository is cached, we don't run
      # git-ls-remote nor git-fetch.
      #
      $* -v 2>&1 | set out

      sed -n -e 's%^(git.* ls-remote .*)$%\1%p'        <$out >:''
      sed -n -e 's%^(git.* .*fetch .*origin .*)$%\1%p' <$out >:''

      sed -n -e 's%^(fetching git:.*libfoo#master)$%\1%p'                                    <$out >~/.+/
      sed -n -e 's%^(skipped fetching and validating .*libfoo.git \(cache, session\))$%\1%p' <$out >~/.+/
      sed -n -e 's%^(fetching .*libfoo.git in .+ \(cache\))$%\1%p'                           <$out >:''

      $rep_list >~'%git:.+libfoo#master.+%'
      $pkg_status libfoo >'libfoo available 1.0.0'

      $rep_remove --all
    }
  }}
}}
