#!/bin/sh
# $Id$

. ./sh/lib/setup
. ./sh/lib/repo-setup
. ./sh/lib/solver-setup
. ./sh/lib/rpm-setup

ORIGREPO=
DESTINATION_REPO=
DEPENDENCY_SOLVER=3
INSTALL_MODE=${INSTALL_MODE:-"dir"}

#tearDown() {
#    [ -n "$REPO" ] && rm -f $REPO/*.*
#}

# The helpers below call us several times per test, and each fail() is counted as one
# skipped test by shunit2. Count only the first call of a given test -- shunit2 exports
# SU_FUNC and rewrites it for every test it runs.
is_rpm_mode_and_rpm_testing_off() {
    [ "$INSTALL_MODE" = "rpm" ] || return 1
    is_rpm_testing_off || return 1

    skip_rpm_testing_off_once
    return 0
}

setUp() {
    [ -z "$ORIGREPO" ] && ORIGREPO=$REPO
    [ -z "$ORIGREPO" ] && fail "empty REPO env variable"
    rm -rf $ORIGREPO/*

    REPO="$ORIGREPO/src"
    DESTINATION_REPO="$ORIGREPO/dest";

    rm -rf $REPO $DESTINATION_REPO

    mkdir -p $REPO || fail "mkdir $REPO failed"
    mkdir -p $DESTINATION_REPO || fail "mkdir $DESTINATION_REPO failed"

    if [ "$INSTALL_MODE" = "dir" ]; then
        RAW_POLDEK="$POLDEK_NOCONF"
        POLDEK_INSTALL="$RAW_POLDEK --st dir -s $REPO --dt dir --destination $DESTINATION_REPO"
        POLDEK_UNINSTALL="$RAW_POLDEK --dt dir --destination $DESTINATION_REPO"
    elif [ "$INSTALL_MODE" = "rpm" ]; then
        rpm_up
    else
        fail "$INSTALL_MODE: invalid INSTALL_MODE"
    fi

    # provide /bin/sh auto requirement
    build_installed sh -p /bin/sh
}

build() {
   build_package $REPO $@
}

build_installed() {
    if [ "$INSTALL_MODE" = "rpm" ]; then
        skip_rpm_testing_off_once && return
        rpm_build_installed $@
    elif [ "$INSTALL_MODE" = "dir" ]; then
        build_package $DESTINATION_REPO $@
    fi
}

# try to install package and check result
# usage: try_install <package> <expected installed> [<expected removed>] [<extra poldek switches>]
try_install() {
    if [ "$INSTALL_MODE" = "rpm" ]; then
        skip_rpm_testing_off_once && return
        rpm_install $@
    elif [ "$INSTALL_MODE" = "dir" ]; then
        local pkg=$1
        try_install_package "dir" "-uv --test" $@

        local n=$($POLDEK_INSTALL -uv --test $pkg | grep -P '^%choose ' | wc -l)
        assertEquals "user should not be asked to choose equivalents ($n)" "$n" "0"
    fi
}

testOr() {
    msgn "Preparing repositories..."
    build a 1-1 -r "'(b or c)'"
    build b
    build c
    try_install "a" "a,b" ""
}

testOrInstalled() {
    msgn "Preparing repositories..."
    build a 1-1 -r "'(b or c)'"
    build b
    build c
    build_installed c
    try_install "a" "a" ""
}

# what the package manager provides itself is there with no package
testOrPackageManager() {
    build a 1-1 -r "'(b or rpmlib(CompressedFileNames) <= 3.0.4-1)'"
    build b
    try_install "a" "a"
}

# the version an upgrade takes away is no alternative, even if available
testOrUpgradedAway() {
    build_installed b 1-1 -p bcap
    build b 1-1 -p bcap
    build b 2-1 -r "'(bcap or c)'"
    build c
    try_install b "b-2-1,c-1-1" "b-1-1"
}

testOrFail() {
    build a 1-1 -r "'(b or c)'"

    is_verbose_mode && $POLDEK_INSTALL -uvt a
    typeset out=$($POLDEK_INSTALL -uvt a)
    echo $out | grep -q 'error' || fail "unexpected success (missing 'error')"
    echo $out | grep 'error' | grep -q 'not found' || fail "unexpected success (missing 'not found')"
}

testAnd() {
    build a 1-1 -r "'(b and c)'"
    build b
    build c
    try_install "a" "a,b,c"
}

testAndInstalled() {
    build a 1-1 -r "'(b and c)'"
    build b
    build c
    build_installed c
    try_install "a" "a,b"
}

testAndFail() {
    build a 1-1 -r "'(b and c)'"
    build b

    is_verbose_mode && $POLDEK_INSTALL -uvt a
    typeset out=$($POLDEK_INSTALL -uvt a)
    echo $out | grep -q 'error' || fail "unexpected success (missing 'error')"
    echo $out | grep 'error' | grep -q 'not found' || fail "unexpected success (missing 'not found')"
}

testOrAnd() {
    build a 1-1 -r "'(b or (c and d))'"
    build b
    build c
    build d
    try_install "a" "a,b"
}

testOrAndInstalled() {
    build a 1-1 -r "'(b or (c and d))'"
    build b
    build c
    build d
    build_installed c
    try_install "a" "a,b"
}

testIf() {
    build a 1-1 -r "'(b if c)'"
    build b
    build c
    try_install "a" "a"
}

testIfInstalled() {
    build a 1-1 -r "'(b if c)'"
    build b
    build c
    build_installed c
    try_install "a" "a,b"
}

testIfElse() {
    build a 1-1 -r "'(b if c else d)'"
    build b
    build c
    build d
    try_install "a" "a,d"
}

testIfElseInstalled() {
    build a 1-1 -r "'(b if c else d)'"
    build b
    build c
    build d
    build_installed c
    try_install "a" "a,b"
}

# skipped as rpm gives error:
# Illegal context for 'unless', please use 'or' instead: Requires:  (b unless c)
xtestUnless() {
    build a 1-1 -r "'(b unless c else d)'"
    build b
    build c
    build d
    try_install "a" "a,b"

    build_installed c
    try_install "a" "a,d"
}

# Illegal context for 'if', please use 'and' instead: Requires:  ((b if c) or d)
xtestIfOr() {
    build a 1-1 -r "'((b if c) or d)'"
    build b
    build c
    build d
    try_install "a" "a,d"

    build_installed c
    try_install "a" "a,b"
}

testWith() {
    build a 1-1 -r "'(foo with bar)'"
    build b 1-1 -p "foo"
    build c 1-2 -p "foo" -p "bar"
    try_install "a" "a,c"
}

testWith2() {
    build a 1-1 -r "'(foo with bar with baz)'"
    build b 1-1 -p "foo"
    build c 1-1 -p "foo" -p "bar"
    build d 1-1 -p "foo" -p "bar" -p "baz"
    try_install "a" "a,d"
}

testWithFail() {
    build a 1-1 -r "'(foo with bar)'"
    build b 1-1 -p "foo"
    build c 1-1 -p "bar"

    is_verbose_mode && $POLDEK_INSTALL -uvt a

    typeset out=$($POLDEK_INSTALL -uvt a)
    echo $out | grep -q 'error' || fail "unexpected success (missing 'error')"
    echo $out | grep 'error' | grep -q 'not found' || fail "unexpected success (missing 'not found')"
}

testWithVersioned() {
    build a 1-1 -r "'(foo >= 1 with bar >= 2)'"
    build b 1-1 -p "'foo = 1'" -p "'bar = 1'"
    build c 1-2 -p "'foo = 1'" -p "'bar = 2'"
    try_install "a" "a,c"
}


testWithout() {
    build a 1-1 -r "'(foo without bar)'"
    build b 1-1 -p "foo"
    build c 1-2 -p "foo" -p "bar"
    try_install "a" "a,b"
}

# error: line 13: Can only chain and/or/with ops: Requires: (foo without bar without baz)
xtestWithout2() {
    build a 1-1 -r "'(foo without bar without baz)'"
    build b 1-1 -p "foo"
    build c 1-1 -p "foo" -p "bar"
    build d 1-1 -p "foo" -p "bar" -p "baz"
    try_install "a" "a,b"
}

testWithoutFail() {
    build a 1-1 -r "'(foo without bar)'"
    build b 1-1 -p "foo" -p "bar"
    build c 1-2 -p "foo" -p "bar"

    is_verbose_mode && $POLDEK_INSTALL -uvt a

    typeset out=$($POLDEK_INSTALL -uvt a)
    echo $out | grep -q 'error' || fail "unexpected success (missing 'error')"
    echo $out | grep 'error' | grep -q 'not found' || fail "unexpected success (missing 'not found')"
}

testWithWithout() {
    build a 1-1 -r "'(foo with (bar without baz))'"
    build b 1-1 -p "foo"
    build c 1-2 -p "foo" -p "bar"
    build d 1-2 -p "foo" -p "bar" -p "baz"
    try_install "a" "a,c"
}

testWithInstalled() {
    # only an installed package that no repository carries provides both
    build_installed x 1-1 -p foo -p bar
    build a 1-1 -r "'(foo with bar)'"
    try_install "a" "a"
}

testWithRangeInstalled() {
    # installed x 1.5 is within the range: no upgrade to the available 1.6
    build_installed x 1.5-1
    build x 1.6-1
    build a 1-1 -r "'(x >= 1 with x < 2)'"
    try_install "a" "a"
}

testWithInstalledFile() {
    # the installed x provides foo and owns the file
    build_installed x 1-1 -p foo -f /usr/bin/booldep-x
    build a 1-1 -r "'(foo with /usr/bin/booldep-x)'"
    try_install "a" "a"
}

testWithoutInstalledFile() {
    # the installed x owns the file, so only y qualifies
    build_installed x 1-1 -p foo -f /usr/bin/booldep-x
    build y 1-1 -p foo
    build z 1-1 -f /usr/bin/booldep-x
    build a 1-1 -r "'(foo without /usr/bin/booldep-x)'"
    try_install "a" "a,y"
}

testWithInstalledProvidedPath() {
    # the installed x declares the path in Provides, without the file
    build_installed x 1-1 -p foo -p /usr/bin/booldep-v
    build a 1-1 -r "'(foo with /usr/bin/booldep-v)'"
    try_install "a" "a"
}

testWithoutInstalledProvidedPath() {
    build_installed x 1-1 -p foo -p /usr/bin/booldep-v
    build y 1-1 -p foo
    build z 1-1 -p /usr/bin/booldep-v
    build a 1-1 -r "'(foo without /usr/bin/booldep-v)'"
    try_install "a" "a,y"
}

testWithoutInstalled() {
    # installed x provides both sides, so only y satisfies the without
    build_installed x 1-1 -p foo -p bar
    build y 1-1 -p foo
    build a 1-1 -r "'(foo without bar)'"
    try_install "a" "a,y"
}

testIfWithOrInstalled() {
    # the installed x satisfies the condition, so z is required
    build_installed x 1-1 -p bar -p baz
    build x 1-1 -p bar -p baz
    build p 1-1 -p foo
    build z
    build a 1-1 -r "'(z if ((foo or bar) with baz))'"
    try_install "a" "a,z"
}

testIfWithoutOrInstalled() {
    build_installed x 1-1 -p bar
    build x 1-1 -p bar
    build p 1-1 -p foo -p qux
    build z
    build a 1-1 -r "'(z if ((foo or bar) without qux))'"
    try_install "a" "a,z"
}

# the pndir index keeps the files a boolean requirement names
testWithPathFromIndex() {
    build a 1-1 -r "'(z with /opt/zzbool/tool)'"
    build pz 1-1 -p z -f /opt/zzbool/tool
    build qz 1-1 -p z
    $POLDEK_NOCONF --st dir -s $REPO --index >/dev/null 2>&1

    POLDEK_INSTALL=$(echo "$POLDEK_INSTALL" | sed 's/--st dir/--st pndir/')
    try_install "a" "a,pz"
}

testWithNoLeftProvider() {
    # used to crash: eval_with/eval_without freed an uninitialized right
    # dvalue when the left side had no providers
    build a 1-1 -r "'(foo with bar)'"
    build b 1-1 -p "bar"

    typeset out=$($POLDEK_INSTALL -uvt a)
    echo $out | grep -q 'error' || fail "unexpected success (missing 'error')"
}

testOrWithNoProvider() {
    # used to crash: eval_or freed the NULL dvalue of an unsatisfiable
    # with-branch
    build a 1-1 -r "'((foo with bar) or c)'"
    build c
    try_install "a" "a,c"
}

testAndWithFail() {
    # an unsatisfiable with-conjunct used to be dropped from the and
    build a 1-1 -r "'(c and (foo with bar))'"
    build c
    build p 1-1 -p "foo"
    build q 1-1 -p "bar"

    typeset out=$($POLDEK_INSTALL -uvt a)
    echo $out | grep -q 'error' || fail "unexpected success (missing 'error')"
}

testAndWithInstalled() {
    # the with is satisfied by the installed x, the and must not fail on it
    build_installed x 1-1 -p foo -p bar
    build a 1-1 -r "'(c and (foo with bar))'"
    build c
    try_install "a" "a,c"
}

testWithoutNoRightProvider() {
    # nothing provides bar, so every foo provider qualifies
    build a 1-1 -r "'(foo without bar)'"
    build b 1-1 -p "foo"
    try_install "a" "a,b"
}

testWithOr() {
    # only p provides foo and one of the or alternatives
    build a 1-1 -r "'(foo with (bar or baz))'"
    build p 1-1 -p "foo" -p "baz"
    build q 1-1 -p "bar"
    try_install "a" "a,p"
}

# upgrading a provider out of an installed package's boolean requirement
# upgrades that package as well
testUpgradeBreaksBooleanUpgradesDependent() {
    build_installed x 1-1
    build_installed q 1-1 -r "'(x >= 1 with x < 2)'"
    build x 2-1
    build q 2-1 -r "'(x >= 2 with x < 3)'"
    try_install x "x-2-1,q-2-1" "x-1-1,q-1-1"
}

# an alternative the package manager provides keeps the requirement met
testUpgradeBooleanHeldByPackageManager() {
    build_installed x 1-1
    build_installed q 1-1 -r "'(rpmlib(CompressedFileNames) <= 3.0.4-1 or x < 2)'"
    build x 2-1
    build q 2-1
    try_install x "x-2-1" "x-1-1"
}

# ... or installs what the requirement still comes down to; b-1 going away
# is no alternative though the repository has it
testUpgradeBreaksBooleanInstallsAlternative() {
    build_installed b 1-1 -p bcap
    build_installed q 1-1 -r "'(bcap or c)'"
    build b 1-1 -p bcap
    build b 2-1
    build c 1-1
    try_install b "b-2-1,c-1-1" "b-1-1"
}

# a cap the new version adds can break it too
testUpgradeAddsCapBreaksBoolean() {
    build_installed a 1-1 -p x
    build_installed q 1-1 -r "'(x without y)'"
    build a 2-1 -p x -p y
    build q 2-1
    try_install a "a-2-1,q-2-1" "a-1-1,q-1-1"
}

# ... and so can a cap that moves to another package: b takes y from c
testUpgradeMovesCapBreaksBoolean() {
    build_installed c 1-1 -p x -p y
    build_installed q 1-1 -r "'(x with y)'"
    build b 1-1 -p y -r "'c >= 2'"
    build c 2-1 -p x
    build q 2-1
    try_install b "b-1-1,c-2-1,q-2-1" "c-1-1,q-1-1"
}

# ... and so can a path the new version adds
testUpgradeAddsPathBreaksBoolean() {
    build_installed a 1-1 -p x
    build_installed q 1-1 -r "'(x without /opt/zzbool/tool)'"
    build a 2-1 -p x -f /opt/zzbool/tool
    build q 2-1
    try_install a "a-2-1,q-2-1" "a-1-1,q-1-1"
}

# what to install is the package itself, not a name = version q provides
testUpgradeBooleanInstallsProviderNotAlias() {
    build_installed a 1-1 -p x -p y
    build_installed q 1-1 -p "'zzprovider = 1-1'" -r "'(x with y)'"
    build a 2-1 -p x
    build zzprovider 1-1 -p x -p y
    try_install a "a-2-1,zzprovider-1-1" "a-1-1"
}

# nor does such a name = version meet a with in a condition: q providing
# zzprovider = 1-1 does not put the zzprovider package there
testUpgradeBooleanConditionNotMetByAlias() {
    build_installed a 1-1 -p z
    build_installed q 1-1 -p "'zzprovider = 1-1'" -r "'(x if (c with d) else z)'"
    build a 2-1
    build x
    build z
    build zzprovider 1-1 -p c -p d
    try_install a "a-2-1,z-1-1" "a-1-1"
}

# a path the pndir index does not list can't be seen in the new version:
# the requirement is left to rpm
testUpgradeBooleanPathUnlisted() {
    build_installed a 1-1 -f /opt/zzbool/tool
    build_installed q 1-1 -r "'(a with /opt/zzbool/tool)'"
    build a 2-1 -f /opt/zzbool/tool
    $POLDEK_NOCONF --st dir -s $REPO --index >/dev/null 2>&1

    POLDEK_INSTALL=$(echo "$POLDEK_INSTALL" | sed 's/--st dir/--st pndir/')
    try_install a "a-2-1" "a-1-1"
}

# the user picks among equivalent providers, as for a plain requirement
testUpgradeBreaksBooleanAsksForEquivalent() {
    [ "$INSTALL_MODE" = "dir" ] || return 0 # the rpm mode runs with --noask
    build_installed b 1-1 -p bcap
    build_installed q 1-1 -r "'(bcap or c)'"
    build b 2-1
    build c1 1-1 -p c
    build c2 1-1 -p c

    typeset n=$($POLDEK_INSTALL -uv --test b | grep -c '^%choose ')
    assertEquals "user not asked to choose a provider of c" "2" "$n"
}

# ... and is refused when neither helps
testUpgradeBreaksBooleanFail() {
    is_rpm_mode_and_rpm_testing_off && return
    build_installed x 1-1
    build_installed q 1-1 -r "'(x >= 1 with x < 2)'"
    build x 2-1

    typeset out=$($POLDEK_INSTALL -uvt x 2>&1)
    echo "$out" | grep -q 'is required by installed q-1-1' || fail "upgrade of x not refused"
}

# --nofollow refuses what needs a package installed, as for a plain
# requirement, instead of upgrading the dependent
testUpgradeBreaksBooleanNoFollow() {
    is_rpm_mode_and_rpm_testing_off && return
    build_installed b 1-1 -p bcap
    build_installed q 1-1 -r "'(bcap or c)'"
    build b 2-1
    build c
    build q 2-1

    typeset out=$($POLDEK_INSTALL -uvt --nofollow b 2>&1)
    echo "$out" | grep -q 'is required by installed q-1-1' || fail "upgrade of b not refused"
}

# installing what an installed package's if condition names brings in what
# the condition makes it require
testInstallMeetsConditionOfInstalled() {
    build_installed q 1-1 -r "'(b if c)'"
    build b
    build c
    try_install c "b-1-1,c-1-1"
}

# ... also when the condition is a path
testInstallMeetsPathConditionOfInstalled() {
    build_installed q 1-1 -r "'(b if /opt/zzbool/tool)'"
    build b
    build c 1-1 -f /opt/zzbool/tool
    try_install c "b-1-1,c-1-1"
}

# a downgrade takes the dependent down too
testDowngradeBreaksBooleanDowngradesDependent() {
    build_installed x 2-1
    build_installed q 2-1 -r "'(x >= 2 with x < 3)'"
    build x 1-1
    build q 1-1 -r "'(x >= 1 with x < 2)'"

    local p=x-1-1.noarch
    [ "$INSTALL_MODE" = "dir" ] && p=x-1-1   # dir mode names packages without arch
    try_install $p "x-1-1,q-1-1" "x-2-1,q-2-1" --downgrade
}

# uninstall side: erase must cascade over packages whose boolean requirement
# stops being satisfiable, and leave the rest alone

try_uninstall() {
    if [ "$INSTALL_MODE" = "rpm" ]; then
        skip_rpm_testing_off_once && return
        rpm_uninstall "$@"
    else
        try_uninstall_package "dir" "-ev --test" "$@"
    fi
}

# usage: rpm_uninstall <package> <expected removed NVRs>
# rpm itself rejects an erase that leaves a dependency broken
rpm_uninstall() {
    typeset package="$1"
    typeset removed=$(echo $2 | sed 's/,/ /g')
    typeset installed=$($RPM -qa --qf '%{NAME}-%{VERSION}-%{RELEASE}\n')

    $RAW_POLDEK --root $DESTINATION_REPO --justdb -ev $package
    assertEquals "erase failed" "$?" "0"

    for i in $installed; do
        if echo " $removed " | grep -q " $i "; then
            if $RPM --quiet -q $i; then fail "$i: package expected to be removed"; fi
        else
            if ! $RPM --quiet -q $i; then fail "$i: package expected to be kept"; fi
        fi
    done
}

testUninstallWith() {
    build_installed a 1-1
    build_installed b 1-1 -r "'(a >= 1 with a < 2)'"
    try_uninstall a "a-1-1,b-1-1"
}

# b goes because of its boolean requirement, and takes c, which requires b
testUninstallCascade() {
    build_installed a 1-1
    build_installed b 1-1 -r "'(a >= 1 with a < 2)'"
    build_installed c 1-1 -r "'(b >= 1 with b < 2)'"
    try_uninstall a "a-1-1,b-1-1,c-1-1"
}

testUninstallOrAlternativePresent() {
    build_installed a 1-1
    build_installed c 1-1
    build_installed b 1-1 -r "'(a or c)'"
    try_uninstall a "a-1-1"
}

# what the package manager provides itself keeps an alternative alive
testUninstallOrPackageManager() {
    build_installed a 1-1
    build_installed b 1-1 -r "'(rpmlib(CompressedFileNames) <= 3.0.4-1 or a)'"
    try_uninstall a "a-1-1"
}

testUninstallOrLastProvider() {
    build_installed a 1-1
    build_installed b 1-1 -r "'(a or c)'"
    try_uninstall a "a-1-1,b-1-1"
}

testUninstallAnd() {
    build_installed a 1-1
    build_installed c 1-1
    build_installed b 1-1 -r "'(a and c)'"
    try_uninstall a "a-1-1,b-1-1"
}

testUninstallPreBroken() {
    # rpm refuses to install b with its requirement unsatisfiable
    [ "$INSTALL_MODE" != "dir" ] && return 0
    build_installed a 1-1 -p x
    build_installed c 1-1 -p y
    build_installed b 1-1 -r "'(x with y)'"
    try_uninstall a "a-1-1"
}

# c provides x as well as y, so after a goes nothing provides x without y
testUninstallWithout() {
    build_installed a 1-1 -p x
    build_installed c 1-1 -p x -p y
    build_installed b 1-1 -r "'(x without y)'"
    try_uninstall a "a-1-1,b-1-1"
}

# both alternatives go in one erase
testUninstallOrBothRemoved() {
    build_installed a 1-1
    build_installed c 1-1
    build_installed b 1-1 -r "'(a or c)'"
    try_uninstall "a c" "a-1-1,c-1-1,b-1-1"
}

# a path counts as provided by a Provides: of it, not only by owning the file
testUninstallOrPathProvided() {
    build_installed p 1-1 -p /usr/bin/zzx
    build_installed q 1-1 -f /usr/bin/zzx
    build_installed d 1-1 -r "'(/usr/bin/zzx or zzc)'"
    try_uninstall q "q-1-1"
}

# the remaining owner of a path counts whatever file lists the session loaded
testUninstallOrPathOwnerAfterRepoLoad() {
    build_installed a 1-1
    build_installed c 1-1 -f /opt/zzrev/file
    build_installed b 1-1 -r "'(a or /opt/zzrev/file)'"
    build zzother 1-1

    local dest="--dt dir --destination $DESTINATION_REPO"
    [ "$INSTALL_MODE" = "rpm" ] && dest="--root $DESTINATION_REPO"
    local out=$($RAW_POLDEK --st dir -s $REPO $dest --noask --cmd "ls; uninstall --test a" 2>&1)
    echo "$out" | grep -q 'marks b-1-1' && fail "b marked although c still owns the path"

    try_uninstall a "a-1-1"
}

# --greedy takes along the packages a boolean requirement comes down to, not
# others with the same name (k keeps sh: rpm does not count the scriptlet's
# Requires(pre) of an installed package)
testUninstallGreedyBoolean() {
    build_installed k 1-1 -r /bin/sh
    build_installed b 1-1 -p x -p y
    build_installed unrelated 1-1 -p "'b = 1-1'"
    build_installed a 1-1 -r "'(x with y)'"
    try_uninstall "--greedy a" "a-1-1,b-1-1"
}

testUninstallGreedyBooleanVersion() {
    build_installed k 1-1 -r /bin/sh
    build_installed b 1-1
    build_installed b 2-1
    build_installed a 1-1 -r "'(b >= 1 with b < 2)'"
    try_uninstall "--greedy a" "a-1-1,b-1-1"
}

testUninstallGreedyBooleanPath() {
    build_installed k 1-1 -r /bin/sh
    build_installed b 1-1 -f /opt/zzgreedy/tool
    build_installed a 1-1 -r "'(/opt/zzgreedy/tool or zzmissing)'"
    try_uninstall "--greedy a" "a-1-1,b-1-1"
}

# ... unless another package still needs it, through a boolean requirement too
testUninstallGreedyBooleanShared() {
    build_installed k 1-1 -r /bin/sh
    build_installed b 1-1 -p bcap
    build_installed a 1-1 -r "'(b with bcap)'"
    build_installed c 1-1 -r "'(b or zzmissing)'"
    try_uninstall "--greedy a" "a-1-1"
}

# the message names the package whose removal broke the requirement: q, with
# a path in Provides:, not a, the provider of the excluded y
testUninstallMarkedByCulprit() {
    is_rpm_mode_and_rpm_testing_off && return
    build_installed a 1-1 -p y
    build_installed q 1-1 -p /usr/bin/zzvirtual
    build_installed b 1-1 -r "'(/usr/bin/zzvirtual without y)'"

    local dest="--dt dir --destination $DESTINATION_REPO"
    [ "$INSTALL_MODE" = "rpm" ] && dest="--root $DESTINATION_REPO"
    local out=$($RAW_POLDEK $dest --noask -ev --test a q 2>&1)
    echo "$out" | grep -q 'q-1-1.* marks b-1-1' || fail "b not marked by q: $out"

    try_uninstall "a q" "a-1-1,q-1-1,b-1-1"
}

testUninstallAndWith() {
    # an unsatisfiable with-conjunct makes the whole and unsatisfiable
    build_installed a 1-1
    build_installed w 1-1 -p x -p y
    build_installed b 1-1 -r "'(a and (x with y))'"
    try_uninstall w "w-1-1,b-1-1"
}

testUninstallOrUnionInWith() {
    # or inside with must union providers (set semantics), not pick a branch
    build_installed w 1-1 -p x -p y
    build_installed p 1-1 -p x -p z
    build_installed q 1-1 -p y
    build_installed b 1-1 -r "'(x with (y or z))'"
    try_uninstall w "w-1-1"
}

testUninstallIfCondition() {
    # removing a plain if-condition only weakens the requirement
    build_installed a 1-1
    build_installed c 1-1
    build_installed b 1-1 -r "'(a if c)'"
    try_uninstall c "c-1-1"
}

testUninstallIfElseSatisfied() {
    build_installed a 1-1
    build_installed c 1-1
    build_installed d 1-1
    build_installed b 1-1 -r "'(a if c else d)'"
    try_uninstall c "c-1-1"
}

testUninstallIfElseBroken() {
    build_installed a 1-1
    build_installed c 1-1
    build_installed b 1-1 -r "'(a if c else d)'"
    try_uninstall c "c-1-1,b-1-1"
}

. ./sh/lib/shunit2
