| git.druid.rocks | index | druid520 | mp | test/ | suites/ | config_loading.sh |
test/suites/config_loading.sh
# suite: config-loading correctness -- OVERLAYS=/PROFILE=/@sets cycle
# safety, and the two config file formats' (mp.conf, mpx.conf) own
# round-tripping/import mechanisms.
# OVERLAYS=: a self-referencing (or mutually-referencing) overlay chain
# must not hang.
test_overlays_no_hang() {
t_begin "overlays_no_hang"
reset_state
printf 'OVERLAYS=/tmp/rt02_selfref.conf\n' > /tmp/rt02_selfref.conf
echo 'OVERLAYS=/tmp/rt02_selfref.conf' >> "$CONF"
timeout 10 "$MP_BIN" verify >/tmp/rt02.out 2>&1
rc=$?
rm -f /tmp/rt02_selfref.conf
assert_exit0 "$rc" "$CUR_TEST" || return
t_pass
}
# @sets: a diamond (two sets both including one shared set) must NOT be
# misdetected as a self-referential cycle.
test_sets_diamond() {
t_begin "sets_diamond"
reset_state
mkport rt03x <<'EOF'
pkg_name="rt03x"
pkg_ver="1.0"
install:
touch $MP_PREFIX/rt03x-installed
remove:
rm -f $MP_PREFIX/rt03x-installed
EOF
mkdir -p "$WD/sets"
echo 'rt03x' > "$WD/sets/rt03common.set"
echo '@rt03common' > "$WD/sets/rt03a.set"
echo '@rt03common' > "$WD/sets/rt03b.set"
printf '@rt03a\n@rt03b\n' > "$WD/sets/rt03base.set"
out=$(mp install @rt03base 2>&1)
rc=$?
assert_exit0 "$rc" "$CUR_TEST" || return
assert_not_contains "$out" "self-referential" "$CUR_TEST" || return
assert_file_exists "$MP_PREFIX/rt03x-installed" "$CUR_TEST" || return
t_pass
}
# PROFILE= chaining: a profile can itself set PROFILE=<other>, correct
# precedence (the closer profile wins), no infinite loop on a real cycle.
test_profile_chain() {
t_begin "profile_chain"
reset_state
mkdir -p /usr/ports/profiles/rt04min /usr/ports/profiles/rt04srv
printf 'RT04_SHARED=min\nRT04_BASE=1\n' > /usr/ports/profiles/rt04min/mp.conf
printf 'PROFILE=rt04min\nRT04_SHARED=srv\n' > /usr/ports/profiles/rt04srv/mp.conf
echo 'PROFILE=rt04srv' >> "$CONF"
out=$(cd /root/mpbuildtest && perl -I. -Implib -e '
use mplib::config qw($CFG load);
load({ FORCE=>0, NODEPS=>0, UNMASK=>0, DRYRUN=>0, CLIOVERLAY=>[] });
print "BASE=$CFG->{conf}{RT04_BASE} SHARED=$CFG->{conf}{RT04_SHARED}\n";
' 2>&1)
rm -rf /usr/ports/profiles/rt04min /usr/ports/profiles/rt04srv
assert_contains "$out" "BASE=1" "$CUR_TEST" || return
assert_contains "$out" "SHARED=srv" "$CUR_TEST" || return
t_pass
}
# write_conf/read_conf round-trip (mp-managed pkgconf.conf, e.g. via "mp
# use"/"mp hold"): a value containing a space+'#' must survive a write-
# then-read cycle intact -- previously silently truncated at the '#' on
# the very first read, since write_conf never quoted values and
# read_conf's inline-comment stripping ran unconditionally.
test_pkgconf_value_roundtrip() {
t_begin "pkgconf_value_roundtrip"
reset_state
out=$(cd /root/mpbuildtest && perl -I. -Implib -e '
use mplib::config qw(write_conf read_conf);
my $path = "/tmp/rt22_pkgconf.conf";
write_conf($path, { rt22pkg => { CFLAGS => "-O2 #tuned" } });
my (undef, $back) = read_conf($path);
print "CFLAGS=[$back->{rt22pkg}{CFLAGS}]\n";
unlink($path);
' 2>&1)
assert_contains "$out" "CFLAGS=[-O2 #tuned]" "$CUR_TEST" || return
t_pass
}
# mpx.conf IMPORT=<path1> <path2>: an imported file's per-package section
# applies; the BASE file's own value for the same key wins over anything
# imported; among multiple imports, the LAST-listed one wins over an
# earlier one -- mirroring mp.conf's own OVERLAYS= "later wins" rule.
test_mpxconf_import() {
t_begin "mpxconf_import"
reset_state
mkport rt23test <<'EOF'
pkg_name="rt23test"
pkg_ver="1.0"
install:
echo "MICROARCH=[$MP_MICROARCH]" > $MP_PREFIX/rt23-log
touch $MP_PREFIX/rt23test-installed
remove:
rm -f $MP_PREFIX/rt23test-installed $MP_PREFIX/rt23-log
EOF
printf 'rt23test:\n\tMICROARCH="from-a"\n' > /tmp/rt23_a.conf
printf 'rt23test:\n\tMICROARCH="from-b"\n' > /tmp/rt23_b.conf
cp /etc/mpx.conf /tmp/rt23_mpxconf.orig 2>/dev/null || : > /tmp/rt23_mpxconf.orig
printf 'IMPORT=/tmp/rt23_a.conf /tmp/rt23_b.conf\n' > /etc/mpx.conf
mp install rt23test >/tmp/rt23.out 2>&1
log1=$(cat "$MP_PREFIX/rt23-log" 2>/dev/null)
mp remove --force rt23test >/tmp/rt23.out 2>&1
printf 'IMPORT=/tmp/rt23_a.conf /tmp/rt23_b.conf\nrt23test:\n\tMICROARCH="base-wins"\n' > /etc/mpx.conf
mp install rt23test >/tmp/rt23.out 2>&1
log2=$(cat "$MP_PREFIX/rt23-log" 2>/dev/null)
mp remove --force rt23test >/tmp/rt23.out 2>&1
cp /tmp/rt23_mpxconf.orig /etc/mpx.conf 2>/dev/null
rm -f /tmp/rt23_a.conf /tmp/rt23_b.conf /tmp/rt23_mpxconf.orig
assert_contains "$log1" "MICROARCH=[from-b]" "$CUR_TEST" || return
assert_contains "$log2" "MICROARCH=[base-wins]" "$CUR_TEST" || return
t_pass
}
# /etc/mpx.env, if present, is sourced (as arbitrary shell, not mpx.conf's
# fixed key=value grammar) into every phase-script invocation -- an exported
# var from it must reach the phase script, its absence must be a silent
# no-op, and a failing command inside it must abort the phase loudly.
test_mpxenv() {
t_begin "mpxenv"
reset_state
mkport rt24test <<'EOF'
pkg_name="rt24test"
pkg_ver="1.0"
install:
echo "MPXENV_VAR=[$MPXENV_VAR]" > $MP_PREFIX/rt24-log
touch $MP_PREFIX/rt24test-installed
remove:
rm -f $MP_PREFIX/rt24test-installed $MP_PREFIX/rt24-log
EOF
cp /etc/mpx.env /tmp/rt24_mpxenv.orig 2>/dev/null
had_env=$?
rm -f /etc/mpx.env
mp install rt24test >/tmp/rt24.out 2>&1
log_noop=$(cat "$MP_PREFIX/rt24-log" 2>/dev/null)
mp remove --force rt24test >/tmp/rt24.out 2>&1
printf 'export MPXENV_VAR=from-mpxenv\n' > /etc/mpx.env
mp install rt24test >/tmp/rt24.out 2>&1
log_set=$(cat "$MP_PREFIX/rt24-log" 2>/dev/null)
mp remove --force rt24test >/tmp/rt24.out 2>&1
printf 'false\n' > /etc/mpx.env
mp install rt24test >/tmp/rt24_fail.out 2>&1
install_rc=$?
if [ "$had_env" = "0" ]; then
cp /tmp/rt24_mpxenv.orig /etc/mpx.env
else
rm -f /etc/mpx.env
fi
rm -f /tmp/rt24_mpxenv.orig /tmp/rt24.out /tmp/rt24_fail.out
assert_contains "$log_noop" "MPXENV_VAR=[]" "$CUR_TEST" || return
assert_contains "$log_set" "MPXENV_VAR=[from-mpxenv]" "$CUR_TEST" || return
[ "$install_rc" != "0" ] || { t_fail "$CUR_TEST: install should have failed with a broken /etc/mpx.env"; return; }
t_pass
}
# a three-deep OVERLAYS chain (a -> b -> c) must apply every level,
# with the DEEPER (later-loaded) file winning on a key several of them
# define.
test_deep_overlay_chain() {
t_begin "deep_overlay_chain"
reset_state
printf 'OVERLAYS=/tmp/rt28_ov2.conf\nRT28_KEY=ov1\n' > /tmp/rt28_ov1.conf
printf 'OVERLAYS=/tmp/rt28_ov3.conf\nRT28_KEY=ov2\nRT28_ONLY2=yes\n' > /tmp/rt28_ov2.conf
printf 'RT28_KEY=ov3\nRT28_ONLY3=yes\n' > /tmp/rt28_ov3.conf
echo 'OVERLAYS=/tmp/rt28_ov1.conf' >> "$CONF"
out=$(cd /root/mpbuildtest && perl -I. -Implib -e '
use mplib::config qw($CFG load);
load({ FORCE=>0, NODEPS=>0, UNMASK=>0, DRYRUN=>0, CLIOVERLAY=>[] });
print "KEY=$CFG->{conf}{RT28_KEY} ONLY2=$CFG->{conf}{RT28_ONLY2} ONLY3=$CFG->{conf}{RT28_ONLY3}\n";
' 2>&1)
rm -f /tmp/rt28_ov1.conf /tmp/rt28_ov2.conf /tmp/rt28_ov3.conf
assert_contains "$out" "KEY=ov3" "$CUR_TEST" || return
assert_contains "$out" "ONLY2=yes" "$CUR_TEST" || return
assert_contains "$out" "ONLY3=yes" "$CUR_TEST" || return
t_pass
}
# a set that doesn't exist fails loudly with the path that was tried.
test_unknown_set_fails() {
t_begin "unknown_set_fails"
reset_state
out=$(mp install @rt29zzzmissing 2>&1)
rc=$?
assert_exit_nonzero "$rc" "$CUR_TEST" || return
assert_contains "$out" "not found" "$CUR_TEST" || return
t_pass
}
# a set containing "world" expands to every installed package, and a set
# with comment/blank lines skips them cleanly.
test_set_world_and_comments() {
t_begin "set_world_and_comments"
reset_state
printf 'GLOBAL_DEPS=" "\n' >> "$CONF"
mkport rt40a <<'EOF'
pkg_name="rt40a"
pkg_ver="1.0"
install:
touch $MP_PREFIX/rt40a-installed
remove:
rm -f $MP_PREFIX/rt40a-installed
EOF
mkport rt40b <<'EOF'
pkg_name="rt40b"
pkg_ver="1.0"
install:
touch $MP_PREFIX/rt40b-installed
remove:
rm -f $MP_PREFIX/rt40b-installed
EOF
mp install rt40a >/tmp/rt40.out 2>&1
mkdir -p "$WD/sets"
printf '# a comment\n\nworld\n' > "$WD/sets/rt40w.set"
mp install @rt40w >>/tmp/rt40.out 2>&1
rc=$?
assert_exit0 "$rc" "$CUR_TEST" || return
assert_file_exists "$MP_PREFIX/rt40a-installed" "$CUR_TEST" || return
t_pass
}
# a three-level nested @set chain (a -> b -> c) expands depth-first, and
# a @set naming another missing set fails loudly.
test_nested_sets_chain() {
t_begin "nested_sets_chain"
reset_state
printf 'GLOBAL_DEPS=" "\n' >> "$CONF"
mkport rt41x <<'EOF'
pkg_name="rt41x"
pkg_ver="1.0"
install:
touch $MP_PREFIX/rt41x-installed
remove:
rm -f $MP_PREFIX/rt41x-installed
EOF
mkdir -p "$WD/sets"
echo 'rt41x' > "$WD/sets/rt41c.set"
echo '@rt41c' > "$WD/sets/rt41b.set"
echo '@rt41b' > "$WD/sets/rt41a.set"
mp install @rt41a >/tmp/rt41.out 2>&1
rc=$?
assert_exit0 "$rc" "$CUR_TEST" || return
assert_file_exists "$MP_PREFIX/rt41x-installed" "$CUR_TEST" || return
echo '@rt41doesnotexist' > "$WD/sets/rt41bad.set"
out=$(mp install @rt41bad 2>&1)
rc=$?
assert_exit_nonzero "$rc" "$CUR_TEST" || return
assert_contains "$out" "not found" "$CUR_TEST" || return
t_pass
}
# a PROFILE chain that loops back to itself dies loudly (not silently
# stops), and a profile that doesn't exist warns but continues.
test_profile_cycle_dies() {
t_begin "profile_cycle_dies"
reset_state
mkdir -p /usr/ports/profiles/rt42a /usr/ports/profiles/rt42b
printf 'PROFILE=rt42b\n' > /usr/ports/profiles/rt42a/mp.conf
printf 'PROFILE=rt42a\n' > /usr/ports/profiles/rt42b/mp.conf
echo 'PROFILE=rt42a' >> "$CONF"
out=$(cd /root/mpbuildtest && perl -I. -Implib -e '
use mplib::config qw(load);
eval { load({ FORCE=>0, NODEPS=>0, UNMASK=>0, DRYRUN=>0, CLIOVERLAY=>[] }); 1 }
or print "DIED:$@";
print "SURVIVED\n" unless $@;
' 2>&1)
rm -rf /usr/ports/profiles/rt42a /usr/ports/profiles/rt42b
assert_contains "$out" "DIED" "$CUR_TEST" || return
assert_contains "$out" "chains back to itself" "$CUR_TEST" || return
t_pass
}
# profile key fill-in: the base profile's keys survive into the derived
# profile unless the derived one sets them.
test_profile_fill_in_matrix() {
t_begin "profile_fill_in_matrix"
reset_state
mkdir -p /usr/ports/profiles/rt43base /usr/ports/profiles/rt43srv
printf 'RT43_SHARED=base\nRT43_ONLYBASE=yes\n' > /usr/ports/profiles/rt43base/mp.conf
printf 'PROFILE=rt43base\nRT43_SHARED=srv\n' > /usr/ports/profiles/rt43srv/mp.conf
echo 'PROFILE=rt43srv' >> "$CONF"
out=$(cd /root/mpbuildtest && perl -I. -Implib -e '
use mplib::config qw($CFG load);
load({ FORCE=>0, NODEPS=>0, UNMASK=>0, DRYRUN=>0, CLIOVERLAY=>[] });
print "SHARED=$CFG->{conf}{RT43_SHARED} ONLYBASE=$CFG->{conf}{RT43_ONLYBASE}\n";
' 2>&1)
rm -rf /usr/ports/profiles/rt43base /usr/ports/profiles/rt43srv
assert_contains "$out" "SHARED=srv" "$CUR_TEST" || return
assert_contains "$out" "ONLYBASE=yes" "$CUR_TEST" || return
t_pass
}
# two overlays sharing one common child (a diamond) apply the child's
# keys exactly once with the LAST-listed overlay winning among the two.
test_overlay_diamond_precedence() {
t_begin "overlay_diamond_precedence"
reset_state
printf 'RT44_KEY=common\n' > /tmp/rt44_common.conf
printf 'OVERLAYS=/tmp/rt44_common.conf\nRT44_KEY=left\n' > /tmp/rt44_left.conf
printf 'OVERLAYS=/tmp/rt44_common.conf\nRT44_KEY=right\n' > /tmp/rt44_right.conf
echo 'OVERLAYS=/tmp/rt44_left.conf /tmp/rt44_right.conf' >> "$CONF"
out=$(cd /root/mpbuildtest && perl -I. -Implib -e '
use mplib::config qw($CFG load);
load({ FORCE=>0, NODEPS=>0, UNMASK=>0, DRYRUN=>0, CLIOVERLAY=>[] });
print "KEY=$CFG->{conf}{RT44_KEY}\n";
' 2>&1)
rm -f /tmp/rt44_common.conf /tmp/rt44_left.conf /tmp/rt44_right.conf
assert_contains "$out" "KEY=right" "$CUR_TEST" || return
t_pass
}
# a per-package section in an overlay merges key-by-key with the same
# section in the base config (base keeps keys the overlay doesn't set).
test_overlay_pconf_merge() {
t_begin "overlay_pconf_merge"
reset_state
cat >> "$CONF" <<'EOF'
rt45p:
BASE_KEY=yes
SHARED=base
EOF
printf 'rt45p:\n\tSHARED=overlay\n\tOVL_KEY=yes\n' > /tmp/rt45_ovl.conf
echo 'OVERLAYS=/tmp/rt45_ovl.conf' >> "$CONF"
out=$(cd /root/mpbuildtest && perl -I. -Implib -e '
use mplib::config qw($CFG load);
load({ FORCE=>0, NODEPS=>0, UNMASK=>0, DRYRUN=>0, CLIOVERLAY=>[] });
my $s = $CFG->{pconf}{rt45p};
print "BASE=$s->{BASE_KEY} SHARED=$s->{SHARED} OVL=$s->{OVL_KEY}\n";
' 2>&1)
rm -f /tmp/rt45_ovl.conf
assert_contains "$out" "BASE=yes SHARED=overlay OVL=yes" "$CUR_TEST" || return
t_pass
}
# global (unindented) ENV= in mp.conf reaches every port's phase script,
# same as mpx.env's own lower-level mechanism (test_mpxenv above) --
# but this one goes through mplib::hooks::port_env_args (config-layer
# ENV=, not runsh.c's always-sourced-first shell file), and a
# PER-PACKAGE ENV= for the same key must still win over the global one
# (per-package section is appended to @args after the global push, and
# later entries win -- see hooks.pm's own comment on port_env_args).
test_global_env() {
t_begin "global_env"
reset_state
mkport rt71a <<'EOF'
pkg_name="rt71a"
pkg_ver="1.0"
install:
echo "RT71VAR=[$RT71VAR]" > $MP_PREFIX/rt71a-log
touch $MP_PREFIX/rt71a-installed
remove:
rm -f $MP_PREFIX/rt71a-installed $MP_PREFIX/rt71a-log
EOF
mkport rt71b <<'EOF'
pkg_name="rt71b"
pkg_ver="1.0"
install:
echo "RT71VAR=[$RT71VAR]" > $MP_PREFIX/rt71b-log
touch $MP_PREFIX/rt71b-installed
remove:
rm -f $MP_PREFIX/rt71b-installed $MP_PREFIX/rt71b-log
EOF
echo 'ENV=RT71VAR=fromglobal' >> "$CONF"
cat >> "$CONF" <<'EOF'
rt71b:
ENV=RT71VAR=fromperpkg
EOF
mp install rt71a >/tmp/rt71.out 2>&1
log_a=$(cat "$MP_PREFIX/rt71a-log" 2>/dev/null)
mp install rt71b >>/tmp/rt71.out 2>&1
log_b=$(cat "$MP_PREFIX/rt71b-log" 2>/dev/null)
mp remove --force rt71a rt71b >>/tmp/rt71.out 2>&1
assert_contains "$log_a" "RT71VAR=[fromglobal]" "$CUR_TEST" || return
assert_contains "$log_b" "RT71VAR=[fromperpkg]" "$CUR_TEST" || return
t_pass
}
run_config_loading_suite() {
test_overlays_no_hang
test_sets_diamond
test_profile_chain
test_pkgconf_value_roundtrip
test_mpxconf_import
test_mpxenv
test_deep_overlay_chain
test_unknown_set_fails
test_set_world_and_comments
test_nested_sets_chain
test_profile_cycle_dies
test_profile_fill_in_matrix
test_overlay_diamond_precedence
test_overlay_pconf_merge
test_global_env
}