| git.druid.rocks | index | druid520 | mp | test/ | suites/ | integration.sh |
test/suites/integration.sh
# suite: end-to-end integrations -- a full slotted lifecycle with
# buildlink wiring, patches, hooks, revdep safety and prune; mp update's
# dependency-ordered rebuild against a real (offline, self-remoted) git
# ports tree; a sysroot install/remove round-trip with full db isolation;
# export/@set round-tripping; and prune's runtime-vs-build-only closure.
# one package, every system at once: slotted %tag dep with buildlink
# wiring, a synthesized patch phase, a post_install hook, mp info/revdep
# reporting, linkdeps-gated removal, and a prune pass that finds nothing
# to drop.
test_full_lifecycle_slotted() {
t_begin "full_lifecycle_slotted"
reset_state
printf 'GLOBAL_DEPS=" "\n' >> "$CONF"
cat >> "$CONF" <<'EOF'
hook_post_install=touch /tmp/it1-hook.log
EOF
mkport it1prov <<'EOF'
pkg_name="it1prov"
pkg_ver="1.0"
pkg_slot="sP"
pkg_tags="it1tag"
install:
touch $MP_PREFIX/it1prov-installed
remove:
rm -f $MP_PREFIX/it1prov-installed
EOF
mkdir -p "$SCRATCH/it1cons/patches"
mkport it1cons <<'EOF'
pkg_name="it1cons"
pkg_ver="1.0"
pkg_deps="%it1tag"
fetch:
mkdir -p it1cons
install:
echo "DEPLOG=[$MP_DEP_PREFIXES]" > $MP_PREFIX/it1cons-deplog
touch $MP_PREFIX/it1cons-installed
remove:
rm -f $MP_PREFIX/it1cons-installed $MP_PREFIX/it1cons-deplog
EOF
printf -- '--- a/dummy\n+++ b/dummy\n@@ -0,0 +1 @@\n+it1-patched\n' > "$SCRATCH/it1cons/patches/001-fix.patch"
rm -f /tmp/it1-hook.log
mp install it1cons >/tmp/it1.out 2>&1
rc=$?
assert_exit0 "$rc" "$CUR_TEST" || return
assert_file_exists "$MP_PREFIX/it1prov-sP/it1prov-installed" "$CUR_TEST" || return
assert_file_exists "$MP_PREFIX/it1cons-installed" "$CUR_TEST" || return
assert_file_exists /tmp/it1-hook.log "$CUR_TEST" || return
dummy=$(cat "$WD/it1cons/it1cons/dummy" 2>/dev/null)
assert_contains "$dummy" "it1-patched" "$CUR_TEST" || return
deplog=$(cat "$MP_PREFIX/it1cons-deplog" 2>/dev/null)
assert_contains "$deplog" "it1prov-sP" "$CUR_TEST" || return
info_out=$(mp info it1cons 2>&1)
assert_contains "$info_out" "buildlink: it1prov:sP" "$CUR_TEST" || return
revdep_out=$(mp revdep it1prov 2>&1)
assert_contains "$revdep_out" "it1cons" "$CUR_TEST" || return
out=$(mp remove it1prov 2>&1)
assert_contains "$out" "still linked against" "$CUR_TEST" || return
assert_file_exists "$MP_PREFIX/it1prov-sP/it1prov-installed" "$CUR_TEST" || return
mp remove --force it1prov >/tmp/it1.out 2>&1
assert_file_absent "$MP_PREFIX/it1prov-sP/it1prov-installed" "$CUR_TEST" || return
prune_out=$(mp prune --dry-run 2>&1)
assert_not_contains "$prune_out" "would remove" "$CUR_TEST" || return
rm -f /tmp/it1-hook.log
t_pass
}
# mp update must rebuild a multi-package set in dependency order (deepest
# first) and record the bumped versions -- exercised against a real git
# ports tree with a local bare origin, so the sync step runs offline.
test_update_ordering() {
t_begin "update_ordering"
reset_state
printf 'GLOBAL_DEPS=" "\n' >> "$CONF"
rm -rf /usr/mp-upd-ports /usr/mp-upd-ports-bare /tmp/it2-upd.log
mkdir -p /usr/mp-upd-ports/ports/scratch
UPD_S=/usr/mp-upd-ports/ports/scratch
# a uniquely-named local helper (NOT a redefinition of lib.sh's
# mkport: suites run sourced into one shell, so a shadowing mkport()
# here would hijack every later test's port creation).
mkupdport() {
mkdir -p "$UPD_S/$1"
cat > "$UPD_S/$1/pkg.conf"
}
# the order marker is a per-package hook_post_install= (below), not a
# line inside install: itself -- mpx sandboxes a new-format install:
# phase by default (root read-only except the stage dir and the
# prefix, see src/sandbox.c), so a THIRD path outside both is never
# writable from in there; hook_post_install fires as a separate,
# unsandboxed step (a direct Perl system() in mplib::hooks::run_hook,
# never through mpx) right after that package's own install:
# finishes, so it still records the exact same rebuild order.
mkupdport it2base <<'EOF'
pkg_name="it2base"
pkg_ver="1.0"
install:
touch $MP_PREFIX/it2base-installed
remove:
rm -f $MP_PREFIX/it2base-installed
EOF
mkupdport it2mid <<'EOF'
pkg_name="it2mid"
pkg_ver="1.0"
pkg_deps="it2base"
install:
touch $MP_PREFIX/it2mid-installed
remove:
rm -f $MP_PREFIX/it2mid-installed
EOF
mkupdport it2top <<'EOF'
pkg_name="it2top"
pkg_ver="1.0"
pkg_deps="it2mid"
install:
touch $MP_PREFIX/it2top-installed
remove:
rm -f $MP_PREFIX/it2top-installed
EOF
cat >> "$CONF" <<'EOF'
it2base:
hook_post_install=echo it2base >> /tmp/it2-upd.log
it2mid:
hook_post_install=echo it2mid >> /tmp/it2-upd.log
it2top:
hook_post_install=echo it2top >> /tmp/it2-upd.log
EOF
git -C /usr/mp-upd-ports init -q
git -C /usr/mp-upd-ports config user.email t@t
git -C /usr/mp-upd-ports config user.name t
git -C /usr/mp-upd-ports add .
git -C /usr/mp-upd-ports commit -qm init
git clone -q --bare /usr/mp-upd-ports /usr/mp-upd-ports-bare
git -C /usr/mp-upd-ports remote add origin /usr/mp-upd-ports-bare
git -C /usr/mp-upd-ports fetch -q origin
git -C /usr/mp-upd-ports branch --set-upstream-to=origin/master master >/dev/null 2>&1
PORTS_DIR=/usr/mp-upd-ports "$MP_BIN" install it2top >/tmp/it2.out 2>&1
rc=$?
assert_exit0 "$rc" "$CUR_TEST" || { rm -rf /usr/mp-upd-ports /usr/mp-upd-ports-bare /tmp/it2-upd.log; return; }
sed -i 's/pkg_ver="1.0"/pkg_ver="2.0"/' "$UPD_S/it2base/pkg.conf" "$UPD_S/it2mid/pkg.conf" "$UPD_S/it2top/pkg.conf"
rm -f /tmp/it2-upd.log
PORTS_DIR=/usr/mp-upd-ports "$MP_BIN" update it2top it2mid it2base >/tmp/it2.out 2>&1
rc=$?
assert_exit0 "$rc" "$CUR_TEST" || { rm -rf /usr/mp-upd-ports /usr/mp-upd-ports-bare /tmp/it2-upd.log; return; }
log=$(cat /tmp/it2-upd.log 2>/dev/null)
expected="it2base
it2mid
it2top"
if [ "$log" != "$expected" ]; then
t_fail "$CUR_TEST (rebuild order wrong, got: $(echo "$log" | tr '\n' ' '))"
rm -rf /usr/mp-upd-ports /usr/mp-upd-ports-bare /tmp/it2-upd.log
return
fi
db=$(cat "$WD/db")
assert_contains "$db" "it2base 2.0" "$CUR_TEST" || { rm -rf /usr/mp-upd-ports /usr/mp-upd-ports-bare /tmp/it2-upd.log; return; }
assert_contains "$db" "it2mid 2.0" "$CUR_TEST" || { rm -rf /usr/mp-upd-ports /usr/mp-upd-ports-bare /tmp/it2-upd.log; return; }
assert_contains "$db" "it2top 2.0" "$CUR_TEST" || { rm -rf /usr/mp-upd-ports /usr/mp-upd-ports-bare /tmp/it2-upd.log; return; }
rm -rf /usr/mp-upd-ports /usr/mp-upd-ports-bare /tmp/it2-upd.log
t_pass
}
# a SYSROOT=<name>-scoped package (and its dependency) must install,
# track, and remove entirely inside the sysroot's own prefix and db --
# the default root's db never sees either name.
test_sysroot_end_to_end() {
t_begin "sysroot_end_to_end"
reset_state
printf 'GLOBAL_DEPS=" "\n' >> "$CONF"
mkdir -p /usr/ports/sysroots
echo 'MP_PREFIX=/usr/local-it3-sysroot' > /usr/ports/sysroots/it3root.conf
rm -rf /usr/local-it3-sysroot "$WD/sysroots/it3root"
mkdir -p /usr/local-it3-sysroot
mkport it3dep <<'EOF'
pkg_name="it3dep"
pkg_ver="1.0"
install:
touch $MP_PREFIX/it3dep-installed
remove:
rm -f $MP_PREFIX/it3dep-installed
EOF
mkport it3top <<'EOF'
pkg_name="it3top"
pkg_ver="1.0"
pkg_deps="it3dep"
install:
touch $MP_PREFIX/it3top-installed
remove:
rm -f $MP_PREFIX/it3top-installed
EOF
cat >> "$CONF" <<'EOF'
it3top:
SYSROOT=it3root
EOF
mp install it3top >/tmp/it3.out 2>&1
rc=$?
assert_exit0 "$rc" "$CUR_TEST" || { rm -rf /usr/ports/sysroots/it3root.conf /usr/local-it3-sysroot; return; }
assert_file_exists /usr/local-it3-sysroot/it3top-installed "$CUR_TEST" || { rm -rf /usr/ports/sysroots/it3root.conf /usr/local-it3-sysroot; return; }
assert_file_exists /usr/local-it3-sysroot/it3dep-installed "$CUR_TEST" || { rm -rf /usr/ports/sysroots/it3root.conf /usr/local-it3-sysroot; return; }
assert_file_absent "$MP_PREFIX/it3top-installed" "$CUR_TEST" || { rm -rf /usr/ports/sysroots/it3root.conf /usr/local-it3-sysroot; return; }
defaultdb=$(cat "$WD/db")
assert_not_contains "$defaultdb" "it3top" "$CUR_TEST" || { rm -rf /usr/ports/sysroots/it3root.conf /usr/local-it3-sysroot; return; }
assert_not_contains "$defaultdb" "it3dep" "$CUR_TEST" || { rm -rf /usr/ports/sysroots/it3root.conf /usr/local-it3-sysroot; return; }
sysdb=$(cat "$WD/sysroots/it3root/db")
assert_contains "$sysdb" "it3top" "$CUR_TEST" || { rm -rf /usr/ports/sysroots/it3root.conf /usr/local-it3-sysroot; return; }
assert_contains "$sysdb" "it3dep" "$CUR_TEST" || { rm -rf /usr/ports/sysroots/it3root.conf /usr/local-it3-sysroot; return; }
mp remove it3top >/tmp/it3.out 2>&1
assert_file_absent /usr/local-it3-sysroot/it3top-installed "$CUR_TEST" || { rm -rf /usr/ports/sysroots/it3root.conf /usr/local-it3-sysroot; return; }
sysdb=$(cat "$WD/sysroots/it3root/db")
assert_not_contains "$sysdb" "it3top" "$CUR_TEST" || { rm -rf /usr/ports/sysroots/it3root.conf /usr/local-it3-sysroot; return; }
rm -rf /usr/ports/sysroots/it3root.conf /usr/local-it3-sysroot
t_pass
}
# mp export writes exactly the requested roots (not their deps), and
# installing the exported @set later rebuilds the whole graph -- the
# dependency is reused from the db, not re-requested.
test_export_set_roundtrip() {
t_begin "export_set_roundtrip"
reset_state
printf 'GLOBAL_DEPS=" "\n' >> "$CONF"
mkport it4dep <<'EOF'
pkg_name="it4dep"
pkg_ver="1.0"
install:
touch $MP_PREFIX/it4dep-installed
remove:
rm -f $MP_PREFIX/it4dep-installed
EOF
mkport it4root <<'EOF'
pkg_name="it4root"
pkg_ver="1.0"
pkg_deps="it4dep"
install:
touch $MP_PREFIX/it4root-installed
remove:
rm -f $MP_PREFIX/it4root-installed
EOF
mp install it4root >/tmp/it4.out 2>&1
mp export it4set >/tmp/it4.out 2>&1
setfile=$(cat "$WD/sets/it4set.set")
assert_contains "$setfile" "it4root" "$CUR_TEST" || return
assert_not_contains "$setfile" "it4dep" "$CUR_TEST" || return
mp remove it4root >/tmp/it4.out 2>&1
assert_file_absent "$MP_PREFIX/it4root-installed" "$CUR_TEST" || return
mp install @it4set >/tmp/it4.out 2>&1
rc=$?
assert_exit0 "$rc" "$CUR_TEST" || return
assert_file_exists "$MP_PREFIX/it4root-installed" "$CUR_TEST" || return
assert_file_exists "$MP_PREFIX/it4dep-installed" "$CUR_TEST" || return
db=$(cat "$WD/db")
assert_contains "$db" "it4root 1.0" "$CUR_TEST" || return
t_pass
}
# prune's runtime closure: a build-only dependency (pkg_bdepend) is
# prunable once its consumer is installed; a runtime dependency
# (pkg_rdepend) is not -- and a real (non-dry) prune removes exactly the
# build-only one.
test_prune_buildonly_vs_runtime() {
t_begin "prune_buildonly_vs_runtime"
reset_state
printf 'GLOBAL_DEPS=" "\n' >> "$CONF"
mkport it5bdep <<'EOF'
pkg_name="it5bdep"
pkg_ver="1.0"
install:
touch $MP_PREFIX/it5bdep-installed
remove:
rm -f $MP_PREFIX/it5bdep-installed
EOF
mkport it5rdep <<'EOF'
pkg_name="it5rdep"
pkg_ver="1.0"
install:
touch $MP_PREFIX/it5rdep-installed
remove:
rm -f $MP_PREFIX/it5rdep-installed
EOF
mkport it5root <<'EOF'
pkg_name="it5root"
pkg_ver="1.0"
pkg_bdepend="it5bdep"
pkg_rdepend="it5rdep"
install:
touch $MP_PREFIX/it5root-installed
remove:
rm -f $MP_PREFIX/it5root-installed
EOF
mp install it5root >/tmp/it5.out 2>&1
dry=$(mp prune --dry-run 2>&1)
assert_contains "$dry" "would remove: it5bdep" "$CUR_TEST" || return
assert_not_contains "$dry" "it5rdep" "$CUR_TEST" || return
assert_not_contains "$dry" "it5root" "$CUR_TEST" || return
mp prune >/tmp/it5.out 2>&1
assert_file_absent "$MP_PREFIX/it5bdep-installed" "$CUR_TEST" || return
assert_file_exists "$MP_PREFIX/it5rdep-installed" "$CUR_TEST" || return
assert_file_exists "$MP_PREFIX/it5root-installed" "$CUR_TEST" || return
t_pass
}
# a bare name with two installed slots is genuinely ambiguous for mp
# remove -- it must say so, not silently pick one; naming a slot
# explicitly removes exactly that slot and leaves its sibling in place.
test_ambiguous_remove_bare() {
t_begin "ambiguous_remove_bare"
reset_state
printf 'GLOBAL_DEPS=" "\n' >> "$CONF"
mkport it6-12 <<'EOF'
pkg_name="it6stub"
pkg_ver="12.0"
pkg_slot="12"
pkg_pref="10"
install:
touch $MP_PREFIX/it6stub12-installed
remove:
rm -f $MP_PREFIX/it6stub12-installed
EOF
mkport it6-13 <<'EOF'
pkg_name="it6stub"
pkg_ver="13.0"
pkg_slot="13"
pkg_pref="20"
install:
touch $MP_PREFIX/it6stub13-installed
remove:
rm -f $MP_PREFIX/it6stub13-installed
EOF
mp install it6stub:12 it6stub:13 >/tmp/it6.out 2>&1
rc=$?
assert_exit0 "$rc" "$CUR_TEST" || return
out=$(mp remove it6stub 2>&1)
rc=$?
assert_exit_nonzero "$rc" "$CUR_TEST" || return
assert_contains "$out" "ambiguous" "$CUR_TEST" || return
assert_file_exists "$MP_PREFIX/it6stub-12/it6stub12-installed" "$CUR_TEST" || return
assert_file_exists "$MP_PREFIX/it6stub-13/it6stub13-installed" "$CUR_TEST" || return
mp remove it6stub:12 >/tmp/it6.out 2>&1
assert_file_absent "$MP_PREFIX/it6stub-12/it6stub12-installed" "$CUR_TEST" || return
assert_file_exists "$MP_PREFIX/it6stub-13/it6stub13-installed" "$CUR_TEST" || return
db=$(cat "$WD/db")
assert_not_contains "$db" "it6stub:12" "$CUR_TEST" || return
assert_contains "$db" "it6stub:13" "$CUR_TEST" || return
t_pass
}
# reinstall re-fetches the port's current tree (version bump honored)
# and keeps the requested flag on the db entry.
test_reinstall_bumps_version() {
t_begin "reinstall_bumps_version"
reset_state
printf 'GLOBAL_DEPS=" "\n' >> "$CONF"
mkport it7p <<'EOF'
pkg_name="it7p"
pkg_ver="1.0"
install:
touch $MP_PREFIX/it7p-installed
remove:
rm -f $MP_PREFIX/it7p-installed
EOF
mp install it7p >/tmp/it7.out 2>&1
sed -i 's/pkg_ver="1.0"/pkg_ver="2.0"/' "$SCRATCH/it7p/pkg.conf"
mp reinstall it7p >>/tmp/it7.out 2>&1
rc=$?
assert_exit0 "$rc" "$CUR_TEST" || return
db=$(cat "$WD/db")
assert_contains "$db" "it7p 2.0" "$CUR_TEST" || return
assert_file_exists "$MP_PREFIX/it7p-installed" "$CUR_TEST" || return
t_pass
}
# removing a package with a staged build dir runs its remove phase from
# the CURRENT ports tree (re-resolved), not the stale staged copy.
test_remove_reresolves_port() {
t_begin "remove_reresolves_port"
reset_state
printf 'GLOBAL_DEPS=" "\n' >> "$CONF"
mkport it8p <<'EOF'
pkg_name="it8p"
pkg_ver="1.0"
install:
touch $MP_PREFIX/it8p-installed
remove:
rm -f $MP_PREFIX/it8p-installed
EOF
mp install it8p >/tmp/it8.out 2>&1
# a NEW remove phase that also drops a second marker proves the
# re-resolve picked up the live tree, not the staged copy.
cat > "$SCRATCH/it8p/pkg.conf" <<'EOF'
pkg_name="it8p"
pkg_ver="1.0"
install:
touch $MP_PREFIX/it8p-installed
remove:
rm -f $MP_PREFIX/it8p-installed $MP_PREFIX/it8p-stale-marker
EOF
touch "$MP_PREFIX/it8p-stale-marker"
mp remove --force it8p >>/tmp/it8.out 2>&1
rc=$?
assert_exit0 "$rc" "$CUR_TEST" || return
assert_file_absent "$MP_PREFIX/it8p-stale-marker" "$CUR_TEST" || return
assert_file_absent "$MP_PREFIX/it8p-installed" "$CUR_TEST" || return
t_pass
}
# prune never touches a requested root even when nothing depends on it,
# and a --dry-run pass changes nothing on disk.
test_prune_roots_untouchable_dryrun() {
t_begin "prune_roots_untouchable_dryrun"
reset_state
printf 'GLOBAL_DEPS=" "\n' >> "$CONF"
mkport it9p <<'EOF'
pkg_name="it9p"
pkg_ver="1.0"
install:
touch $MP_PREFIX/it9p-installed
remove:
rm -f $MP_PREFIX/it9p-installed
EOF
mp install it9p >/tmp/it9.out 2>&1
dry=$(mp prune --dry-run 2>&1)
assert_not_contains "$dry" "would remove: it9p" "$CUR_TEST" || return
mp prune >>/tmp/it9.out 2>&1
assert_file_exists "$MP_PREFIX/it9p-installed" "$CUR_TEST" || return
db=$(cat "$WD/db")
assert_contains "$db" "it9p" "$CUR_TEST" || return
t_pass
}
# mp update with an explicit name list updates only the named installed
# packages, leaving the other installed one at its old version (the sync
# step runs against a local bare-remoted tree, offline, like
# test_update_ordering).
test_update_named_subset() {
t_begin "update_named_subset"
reset_state
printf 'GLOBAL_DEPS=" "\n' >> "$CONF"
rm -rf /usr/mp-upd2-ports /usr/mp-upd2-ports-bare
mkdir -p /usr/mp-upd2-ports/ports/scratch
UPD2_S=/usr/mp-upd2-ports/ports/scratch
mkupd2port() {
mkdir -p "$UPD2_S/$1"
cat > "$UPD2_S/$1/pkg.conf"
}
mkupd2port it10a <<'EOF'
pkg_name="it10a"
pkg_ver="1.0"
install:
touch $MP_PREFIX/it10a-installed
remove:
rm -f $MP_PREFIX/it10a-installed
EOF
mkupd2port it10b <<'EOF'
pkg_name="it10b"
pkg_ver="1.0"
install:
touch $MP_PREFIX/it10b-installed
remove:
rm -f $MP_PREFIX/it10b-installed
EOF
git -C /usr/mp-upd2-ports init -q
git -C /usr/mp-upd2-ports config user.email t@t
git -C /usr/mp-upd2-ports config user.name t
git -C /usr/mp-upd2-ports add .
git -C /usr/mp-upd2-ports commit -qm init
git clone -q --bare /usr/mp-upd2-ports /usr/mp-upd2-ports-bare
git -C /usr/mp-upd2-ports remote add origin /usr/mp-upd2-ports-bare
git -C /usr/mp-upd2-ports fetch -q origin
git -C /usr/mp-upd2-ports branch --set-upstream-to=origin/master master >/dev/null 2>&1
PORTS_DIR=/usr/mp-upd2-ports "$MP_BIN" install it10a it10b >/tmp/it10.out 2>&1
sed -i 's/pkg_ver="1.0"/pkg_ver="3.0"/' "$UPD2_S/it10a/pkg.conf" "$UPD2_S/it10b/pkg.conf"
PORTS_DIR=/usr/mp-upd2-ports "$MP_BIN" update it10a >>/tmp/it10.out 2>&1
rc=$?
assert_exit0 "$rc" "$CUR_TEST" || { rm -rf /usr/mp-upd2-ports /usr/mp-upd2-ports-bare; return; }
db=$(cat "$WD/db")
assert_contains "$db" "it10a 3.0" "$CUR_TEST" || { rm -rf /usr/mp-upd2-ports /usr/mp-upd2-ports-bare; return; }
assert_contains "$db" "it10b 1.0" "$CUR_TEST" || { rm -rf /usr/mp-upd2-ports /usr/mp-upd2-ports-bare; return; }
rm -rf /usr/mp-upd2-ports /usr/mp-upd2-ports-bare
t_pass
}
run_integration_suite() {
test_full_lifecycle_slotted
test_update_ordering
test_sysroot_end_to_end
test_export_set_roundtrip
test_prune_buildonly_vs_runtime
test_ambiguous_remove_bare
test_reinstall_bumps_version
test_remove_reresolves_port
test_prune_roots_untouchable_dryrun
test_update_named_subset
}