#!/usr/bin/env bash # Off-device test runner for the helpdesk Dialer patch overlay. # Compiles every overlay + test source with the tree's prebuilt JDK (the one the OS build uses), # runs each *Test, and cross-checks the pure-logic ports against the REAL backend code. # Run: bash components/dialer-patch/tests/run.sh set -uo pipefail cd "$(dirname "$0")/.." # -> components/dialer-patch BACKEND=../backend/lib/helpdesk/domain JAVAC=$(ls /build/grapheneos/prebuilts/jdk/jdk*/linux-x86/bin/javac 2>/dev/null | head -1); [ -x "$JAVAC" ] || JAVAC=$(command -v javac) JAVA=$(ls /build/grapheneos/prebuilts/jdk/jdk*/linux-x86/bin/java 2>/dev/null | head -1); [ -x "$JAVA" ] || JAVA=$(command -v java) [ -x "$JAVAC" ] || { echo "no javac found"; exit 1; } OUT=$(mktemp -d); fail=0 # Compile only the Android-independent subpackages (http/util/command/queue) + tests. The device- # coupled classes in the helpdesk/ root (Config/Registry/Dispatcher/Emitter — import android.*/incallui/ # org.json) are verified by `m Dialer`, not off-device. H=overlay/java/com/android/dialer/helpdesk mapfile -t SRCS < <(find "$H/http" "$H/util" "$H/command" "$H/queue" tests -name '*.java') [ ${#SRCS[@]} -gt 0 ] || { echo "no sources found"; exit 1; } "$JAVAC" -encoding UTF-8 -d "$OUT" "${SRCS[@]}" || { echo "COMPILE FAILED"; exit 1; } echo "compiled ${#SRCS[@]} sources with $("$JAVAC" -version 2>&1)" echo; echo "== unit suites ==" for f in $(find tests -name '*Test.java'); do pkg=$(grep -m1 '^package ' "$f" | sed 's/package //; s/;//') fqcn="$pkg.$(basename "$f" .java)" echo "- $fqcn" "$JAVA" -cp "$OUT" "$fqcn" || fail=1 done echo; echo "== cross-checks vs real backend ==" # 1) auth signer signatures (ASCII + UTF-8 Czech body) == server.rb HMAC recipe SOUT=$("$JAVA" -cp "$OUT" com.android.dialer.helpdesk.http.DeviceAuthSignerTest) JSIG=$(echo "$SOUT" | sed -n 's/^SIG=//p'); JSIGCZ=$(echo "$SOUT" | sed -n 's/^SIG_CZ=//p') RSIG=$(ruby -e 'require "openssl"; b="{\"call_id\":\"c1\"}"; puts OpenSSL::HMAC.hexdigest("SHA256","dev-device-secret","1751932800.n0nce123.#{b}")') RSIGCZ=$(ruby -e 'require "openssl"; b="{\"notes\":\"Děkujeme\"}"; puts OpenSSL::HMAC.hexdigest("SHA256","dev-device-secret","1751932800.n0nce123.#{b}")') if [ -n "$JSIG" ] && [ "$JSIG" = "$RSIG" ]; then echo " ok DeviceAuthSigner (ASCII) == server.rb verifier"; else echo " FAIL signer ascii ($JSIG vs $RSIG)"; fail=1; fi if [ -n "$JSIGCZ" ] && [ "$JSIGCZ" = "$RSIGCZ" ]; then echo " ok DeviceAuthSigner (UTF-8 Czech body, byte[] path) == server.rb"; else echo " FAIL signer utf8 ($JSIGCZ vs $RSIGCZ)"; fail=1; fi # 2) E.164 normalizer == Helpdesk::Phone.e164 over a vector set VEC=$(printf '%s\n' "601 234 567" "00420601234567" "+420601234567" "+420 601-234-567" "(602) 000 000" "hidden" "" "12345" "+441632960000" "777888999" "0042060123" " 606 606 606 ") JX=$("$JAVA" -cp "$OUT" com.android.dialer.helpdesk.util.CzE164Test xcheck <<<"$VEC") RX=$(ruby -r"$BACKEND" -e 'STDIN.each_line{|l| v=Helpdesk::Phone.e164(l.chomp); puts(v.nil? ? "" : v)}' <<<"$VEC") if [ "$JX" = "$RX" ]; then echo " ok CzE164 == Helpdesk::Phone.e164 over $(wc -l <<<"$VEC" | tr -d ' ') vectors"; else echo " FAIL E.164 cross-check"; diff <(echo "$JX") <(echo "$RX"); fail=1; fi # 3) business-hours reflex == Helpdesk::BusinessHours over a full-year sweep (all weekends + holidays) BH=../backend/lib/helpdesk/business_hours SWEEP=$(for d in $(seq 0 364); do for hh in 07 08 10 15 16; do date -d "2026-01-01 +$d days" +%Y-%m-%dT$hh:00 2>/dev/null; done; done) JBH=$("$JAVA" -cp "$OUT" com.android.dialer.helpdesk.util.BusinessHoursTest xcheck <<<"$SWEEP") RBH=$(ruby -r"$BH" -e 'STDIN.each_line{|l| l=l.chomp; next if l.empty?; y,mo,dy,h=l.scan(/\d+/).first(4).map(&:to_i); t=Time.new(y,mo,dy,h,0,0); puts(Helpdesk::BusinessHours.open?(t) ? "open" : "closed")}' <<<"$SWEEP") if [ "$JBH" = "$RBH" ]; then echo " ok BusinessHours == Helpdesk::BusinessHours over $(wc -l <<<"$SWEEP" | tr -d ' ') datetimes"; else echo " FAIL business-hours cross-check"; diff <(echo "$JBH") <(echo "$RBH") | head; fail=1; fi # 4) reverse-command verb set + DTMF rule == backend Helpdesk::COMMAND_VERBS / valid_dtmf? JV=$("$JAVA" -cp "$OUT" com.android.dialer.helpdesk.command.CommandGateTest verbs) RV=$(ruby -r"$BACKEND" -e 'puts Helpdesk::COMMAND_VERBS.sort') if [ "$JV" = "$RV" ]; then echo " ok CommandGate.VERBS == Helpdesk::COMMAND_VERBS"; else echo " FAIL command verbs"; diff <(echo "$JV") <(echo "$RV"); fail=1; fi DARGS=$(printf '%s\n' 0 5 9 '*' '#' 'A' '55' '' ' ' 'x' '12') JD=$("$JAVA" -cp "$OUT" com.android.dialer.helpdesk.command.CommandGateTest dtmf <<<"$DARGS") RD=$(ruby -e 'STDIN.each_line{|l| a=l.chomp; puts(a.match?(/\A[0-9*#]\z/) ? "valid":"invalid")}' <<<"$DARGS") if [ "$JD" = "$RD" ]; then echo " ok isValidDtmf == valid_dtmf? over $(wc -l <<<"$DARGS" | tr -d ' ') args"; else echo " FAIL dtmf rule"; diff <(echo "$JD") <(echo "$RD"); fail=1; fi echo; [ "$fail" = 0 ] && echo "ALL GREEN" || echo "FAILURES ABOVE" exit $fail