#!/usr/bin/env bash # Test di parita' fra il client C e il client Python, su due server mock # indipendenti (stesso stato iniziale) cosi' che gli ID coincidano. # # C_BIN=dist/cellar-cli-asan tests/parity_test.sh # C_BIN=dist/cellar-cli tests/parity_test.sh set -uo pipefail ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) C_BIN="${C_BIN:-$ROOT/dist/cellar-cli-asan}" PY="${PYTHON:-python3}" PY_CLIENT="${PY_CLIENT:-$ROOT/tests/ref/cellar-cli.py}" WORK="$ROOT/tests/tmp" PORT_C="${PORT_C:-18091}" PORT_P="${PORT_P:-18092}" PORT_DEAD="${PORT_DEAD:-18999}" PASS=0 FAIL=0 FAILED=() SRV_C_PID="" SRV_P_PID="" ok() { PASS=$((PASS + 1)); printf ' PASS %s\n' "$1"; } ko() { FAIL=$((FAIL + 1)); FAILED+=("$1"); printf ' FAIL %s\n' "$1"; } cleanup() { [ -n "$SRV_C_PID" ] && kill "$SRV_C_PID" 2>/dev/null [ -n "$SRV_P_PID" ] && kill "$SRV_P_PID" 2>/dev/null wait 2>/dev/null return 0 } trap cleanup EXIT norm() { sed -e "s|$WORK/home-c|@HOME@|g" -e "s|$WORK/home-p|@HOME@|g" \ -e "s|bottle-install-[A-Za-z0-9_]\{6,\}|bottle-install-XXXXXX|g" \ -e "s|$WORK/installs/c|@INSTALL@|g" -e "s|$WORK/installs/p|@INSTALL@|g" \ -e "s|$WORK/bottles|@BOTTLES@|g" \ -e "s|-[0-9]\{8\}-[0-9]\{6\}\.tar\.gz|-TIMESTAMP.tar.gz|g" \ -e "s|$PORT_C|@PORT@|g" -e "s|$PORT_P|@PORT@|g" } check() { local name="$1" shift local stdin_file="-" if [ "${1:-}" = "--stdin" ]; then stdin_file="$2" shift 2 fi local -a cargs=() pargs=() local a for a in "$@"; do cargs+=("${a//@SERVER@/http://127.0.0.1:$PORT_C}") pargs+=("${a//@SERVER@/http://127.0.0.1:$PORT_P}") done local rc_c rc_p if [ "$stdin_file" = "-" ]; then HOME="$WORK/home-c" "$C_BIN" "${cargs[@]}" >"$WORK/c.out" 2>"$WORK/c.err" rc_c=$? HOME="$WORK/home-p" "$PY" "$PY_CLIENT" "${pargs[@]}" >"$WORK/p.out" 2>"$WORK/p.err" rc_p=$? else HOME="$WORK/home-c" "$C_BIN" "${cargs[@]}" <"$stdin_file" >"$WORK/c.out" 2>"$WORK/c.err" rc_c=$? HOME="$WORK/home-p" "$PY" "$PY_CLIENT" "${pargs[@]}" <"$stdin_file" >"$WORK/p.out" 2>"$WORK/p.err" rc_p=$? fi norm <"$WORK/c.out" >"$WORK/c.out.n" norm <"$WORK/p.out" >"$WORK/p.out.n" norm <"$WORK/c.err" >"$WORK/c.err.n" norm <"$WORK/p.err" >"$WORK/p.err.n" local problems="" [ "$rc_c" != "$rc_p" ] && problems+="exit($rc_c vs $rc_p) " diff -q "$WORK/p.out.n" "$WORK/c.out.n" >/dev/null || problems+="stdout " diff -q "$WORK/p.err.n" "$WORK/c.err.n" >/dev/null || problems+="stderr " if [ -z "$problems" ]; then ok "$name" else ko "$name [$problems]" echo " --- stdout (PY < | C >) ---" diff "$WORK/p.out.n" "$WORK/c.out.n" | head -20 | sed 's/^/ /' echo " --- stderr (PY < | C >) ---" diff "$WORK/p.err.n" "$WORK/c.err.n" | head -10 | sed 's/^/ /' fi } # ------------------------------------------------------------------ setup [ -x "$C_BIN" ] || { echo "binario C non trovato: $C_BIN (esegui make asan)"; exit 1; } [ -f "$PY_CLIENT" ] || { echo "client Python di riferimento mancante: $PY_CLIENT"; exit 1; } rm -rf "$WORK" mkdir -p "$WORK"/{home-c,home-p,state-c,state-p,installs/c,installs/p,dl-dir} printf '[cellar]\nserver = http://127.0.0.1:%s\nbottles_dir = %s/installs/c\n\n' "$PORT_C" "$WORK" >"$WORK/home-c/.cellar.conf" printf '[cellar]\nserver = http://127.0.0.1:%s\nbottles_dir = %s/installs/p\n\n' "$PORT_P" "$WORK" >"$WORK/home-p/.cellar.conf" BOTTLES="$WORK/bottles" bash "$ROOT/tests/make_fake_bottle.sh" "$BOTTLES" >/dev/null "$PY" "$ROOT/tests/mock_server.py" --port "$PORT_C" --state "$WORK/state-c" & SRV_C_PID=$! "$PY" "$ROOT/tests/mock_server.py" --port "$PORT_P" --state "$WORK/state-p" & SRV_P_PID=$! for _ in $(seq 50); do curl -sf "http://127.0.0.1:$PORT_C/health" >/dev/null 2>&1 && break sleep 0.1 done for _ in $(seq 50); do curl -sf "http://127.0.0.1:$PORT_P/health" >/dev/null 2>&1 && break sleep 0.1 done # archivio di riferimento creato da tarfile di Python (per testare upload, # download, install e l'estrattore C su un archivio "python-made") "$PY" - "$BOTTLES/Fake Bottle" "$WORK/ref-archive.tar.gz" <<'PYEOF' import sys, tarfile src, dst = sys.argv[1], sys.argv[2] with tarfile.open(dst, "w:gz") as tf: tf.add(src, arcname="Fake Bottle") PYEOF printf '1\nParity Wizard\nFake Bottle\nBackup via wizard\nwizard,tags\n' >"$WORK/wiz-upload.in" printf '1\ny\n' >"$WORK/wiz-install.in" echo "== parita' C ($C_BIN) vs Python ($PY_CLIENT) ==" # ------------------------------------------------------------------ casi check "list vuota (usa ~/.cellar.conf)" "list" check "scan-local tabella" "scan-local" "--bottles-dir" "$BOTTLES" check "scan-local --json" "scan-local" "--bottles-dir" "$BOTTLES" "--json" check "scan-local dir inesistente" "scan-local" "--bottles-dir" "$WORK/nope" check "upload con tutti i campi" "upload" "$WORK/ref-archive.tar.gz" "--name" "Parity Test" \ "--bottle-name" "Fake Bottle" "--description" "descrizione" "--tags" "a,b" \ "--arch" "win32" "--runner" "soda-9.0-1" "--windows-version" "win10" check "list con 1 record" "list" check "download su file" "download" "1" "$WORK/dl.bin" check "download su directory (nome da Content-Disposition)" "download" "1" "$WORK/dl-dir" check "install (prima volta)" "install" "Fake Bottle" check "install (esiste, senza --replace)" "install" "Fake Bottle" check "install --replace" "install" "Fake Bottle" "--replace" check "install con ref inesistente" "install" "NoSuchBottle" check "download id inesistente (404)" "download" "99" "$WORK/dl-404.bin" check "server irraggiungibile" "--server" "http://127.0.0.1:$PORT_DEAD" "list" check "wizard-upload (input da pipe)" --stdin "$WORK/wiz-upload.in" "wizard-upload" "--bottles-dir" "$BOTTLES" check "list con 2 record" "list" check "wizard-install --replace" --stdin "$WORK/wiz-install.in" "wizard-install" "--replace" check "errore: nessun argomento" check "errore: comando sconosciuto" "frobnicate" check "errore: upload senza --name" "upload" "$WORK/ref-archive.tar.gz" check "errore: archive_id non numerico" "download" "abc" "$WORK/out.bin" check "errore: argomento extra" "list" "extra" check "errore: opzione sconosciuta" "scan-local" "--nope" # ------------------------------------------------- verifiche sugli artefatti echo "== artefatti ==" # 1. download: stesso contenuto del file caricato if cmp -s "$WORK/dl.bin" "$WORK/ref-archive.tar.gz"; then ok "download produce byte identici all'upload" else ko "download produce byte identici all'upload" fi if [ -f "$WORK/dl-dir/ref-archive.tar.gz" ]; then ok "download in directory usa il filename del server" else ko "download in directory usa il filename del server" fi # 2. albero installato: Python (archivio python-made) vs C (archivio python-made) if "$PY" "$ROOT/tests/compare_trees.py" "$WORK/installs/p/Fake Bottle" "$WORK/installs/c/Fake Bottle" --mtimes \ >"$WORK/trees1.txt" 2>&1; then ok "albero installato identico (extract python-made)" else ko "albero installato identico (extract python-made)" sed 's/^/ /' "$WORK/trees1.txt" | head -15 fi # 3. archivio creato dal writer C vs writer Python (dall'upload dei wizard) # l'archivio piu' recente e' quello creato dal writer del client (wizard-upload) c_arch=$(ls -t "$WORK"/state-c/storage/*.tar.gz 2>/dev/null | head -1) p_arch=$(ls -t "$WORK"/state-p/storage/*.tar.gz 2>/dev/null | head -1) if [ -n "$c_arch" ] && [ -n "$p_arch" ]; then if "$PY" "$ROOT/tests/tar_compare.py" "$p_arch" "$c_arch" >"$WORK/tar.txt" 2>&1; then ok "archivio C equivalente a quello Python (tarfile)" else ko "archivio C equivalente a quello Python (tarfile)" sed 's/^/ /' "$WORK/tar.txt" | head -15 fi # l'archivio C deve essere leggibile anche da GNU tar if tar tzf "$c_arch" >/dev/null 2>&1; then ok "archivio C leggibile da GNU tar" else ko "archivio C leggibile da GNU tar" fi else ko "archivi dei wizard non trovati negli storage dei mock server" fi # 4. estrazione incrociata: Python estrae l'archivio creato dal C if [ -n "$c_arch" ]; then rm -rf "$WORK/xpy" mkdir -p "$WORK/xpy" if "$PY" - "$c_arch" "$WORK/xpy" <<'PYEOF' import sys, tarfile with tarfile.open(sys.argv[1], "r:gz") as tf: tf.extractall(sys.argv[2], filter="fully_trusted") PYEOF then if "$PY" "$ROOT/tests/compare_trees.py" "$WORK/xpy/Fake Bottle" "$WORK/installs/p/Fake Bottle" --mtimes \ >"$WORK/trees2.txt" 2>&1; then ok "python estrae l'archivio C con metadata identici" else ko "python estrae l'archivio C con metadata identici" sed 's/^/ /' "$WORK/trees2.txt" | head -15 fi else ko "python estrae l'archivio C" fi fi # 5. --help esce 0 in entrambi (il testo e' volutamente diverso) HOME="$WORK/home-c" "$C_BIN" --help >/dev/null 2>&1 rc_c=$? HOME="$WORK/home-p" "$PY" "$PY_CLIENT" --help >/dev/null 2>&1 rc_p=$? if [ "$rc_c" = "0" ] && [ "$rc_p" = "0" ]; then ok "--help esce 0 in entrambi" else ko "--help esce 0 in entrambi ($rc_c vs $rc_p)" fi # ------------------------------------------------------------------ riepilogo echo echo "==================================================" printf 'PASS: %d FAIL: %d\n' "$PASS" "$FAIL" if [ "$FAIL" -gt 0 ]; then printf 'casi falliti:\n' for n in "${FAILED[@]}"; do printf ' - %s\n' "$n"; done exit 1 fi echo "parita' completa ✔"