package com.android.dialer.helpdesk.command; import static com.android.dialer.helpdesk.command.CommandGate.CallPhase.ACTIVE; import static com.android.dialer.helpdesk.command.CommandGate.CallPhase.ENDED; import static com.android.dialer.helpdesk.command.CommandGate.CallPhase.RINGING; import static com.android.dialer.helpdesk.command.CommandGate.Decision.APPLY; import static com.android.dialer.helpdesk.command.CommandGate.Decision.DROP_BAD_ARG; import static com.android.dialer.helpdesk.command.CommandGate.Decision.DROP_DUPLICATE; import static com.android.dialer.helpdesk.command.CommandGate.Decision.DROP_STALE; import static com.android.dialer.helpdesk.command.CommandGate.Decision.DROP_UNKNOWN_VERB; import java.io.BufferedReader; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * Standalone off-device test for {@link CommandGate}. Modes: * - default: decision assertions. * - "verbs": print the sorted verb set (harness diffs vs Helpdesk::COMMAND_VERBS). * - "dtmf": read args from stdin, print valid/invalid (harness diffs vs valid_dtmf?). */ public class CommandGateTest { static int pass = 0, fail = 0; static void eq(Object got, Object want, String d) { if (got == null ? want == null : got.equals(want)) { pass++; System.out.println(" ok " + d); } else { fail++; System.out.println(" FAIL " + d + " (got: " + got + ")"); } } public static void main(String[] args) throws Exception { if (args.length > 0 && args[0].equals("verbs")) { List v = new ArrayList<>(CommandGate.VERBS); Collections.sort(v); for (String s : v) System.out.println(s); return; } if (args.length > 0 && args[0].equals("dtmf")) { BufferedReader r = new BufferedReader(new InputStreamReader(System.in)); String line; while ((line = r.readLine()) != null) { System.out.println(CommandGate.isValidDtmf(line) ? "valid" : "invalid"); } return; } CommandGate g = new CommandGate(); eq(g.decide("1", "answer", null, RINGING), APPLY, "answer on ringing applies"); eq(g.decide("1", "answer", null, RINGING), DROP_DUPLICATE, "same id dropped (idempotent)"); eq(g.decide("2", "answer", null, ENDED), DROP_STALE, "answer on ended dropped (monotonic)"); eq(g.decide("3", "explode", null, RINGING), DROP_UNKNOWN_VERB, "unknown verb dropped"); eq(g.decide("4", "dtmf", "5", ACTIVE), APPLY, "valid dtmf applies"); eq(g.decide("5", "dtmf", "X", ACTIVE), DROP_BAD_ARG, "bad dtmf arg dropped"); eq(g.decide("6", "dtmf", "#", ACTIVE), APPLY, "'#' is a valid dtmf"); eq(g.decide("7", "mute", null, ACTIVE), APPLY, "mute applies mid-call"); eq(g.decide("8", "hangup", null, ACTIVE), APPLY, "hangup applies mid-call"); eq(g.decide("9", "route", "bluetooth", ACTIVE), APPLY, "route applies"); // a bad command is never recorded as seen -> a corrected retry with the same id still evaluates eq(g.decide("3", "answer", null, RINGING), APPLY, "id reused after prior unknown-verb still applies"); // bounded seen set (no unbounded growth over a long-running service) + per-call reset CommandGate g2 = new CommandGate(); for (int i = 0; i < CommandGate.MAX_SEEN + 500; i++) { g2.decide("id" + i, "mute", null, ACTIVE); } eq(g2.seenSize() <= CommandGate.MAX_SEEN, true, "seen set bounded at MAX_SEEN (no leak)"); g2.reset(); eq(g2.seenSize(), 0, "reset() clears dedup memory (per-call scope)"); eq(g2.decide("id0", "mute", null, ACTIVE), APPLY, "id re-applies after reset()"); System.out.println("\n" + pass + " passed, " + fail + " failed"); System.exit(fail == 0 ? 0 : 1); } }