// Command debugclient drives a running MARTeApp.ex DebugService+TCPLogger // instance over TCP/UDP for the Test/E2E/suite "debug" and "tcplogger" // scenario kinds. It scripts a fixed command sequence, captures responses // and log/trace output, and reports PASS/FAIL as JSON to -out. package main import ( "encoding/json" "flag" "fmt" "os" "sync" "time" debugger "marte2debugger/controller" // see go.mod replace directive; marte2debugger is Client/debugger's module name ) type event struct { kind string data any } func main() { host := flag.String("host", "127.0.0.1", "MARTeApp host") cmdPort := flag.Int("cmdport", 8080, "DebugService TCP command port") udpPort := flag.Int("udpport", 8081, "DebugService UDP trace port") logPort := flag.Int("logport", 9090, "TCPLogger TCP port") scenario := flag.String("scenario", "", "scenario id (for output naming)") outDir := flag.String("out", "/tmp/debug_e2e", "output directory") mode := flag.String("mode", "debug", "debug|tcplogger") dur := flag.Duration("dur", 4*time.Second, "how long to run the script/collection") flag.Parse() if *scenario == "" { fmt.Fprintln(os.Stderr, "missing -scenario") os.Exit(2) } os.MkdirAll(*outDir, 0o755) var mu sync.Mutex var events []event sink := func(v any) { mu.Lock() defer mu.Unlock() // runLog (and every other MarteController event site) passes a // map[string]any literal to sink; store it as-is, no // unmarshal/remarshal needed. events = append(events, event{kind: fmt.Sprintf("%T", v), data: v}) } mc := debugger.NewHeadlessMarteController(sink) mc.Connect(*host, *cmdPort, *udpPort, *logPort) defer mc.Disconnect() time.Sleep(500 * time.Millisecond) // allow TCP/UDP/log connects to establish var ok bool var msg string switch *mode { case "debug": ok, msg = runDebugScript(mc) case "tcplogger": ok, msg = runTcpLoggerScript(mc, *dur, &events, &mu) default: fmt.Fprintf(os.Stderr, "unknown -mode %q\n", *mode) os.Exit(2) } time.Sleep(300 * time.Millisecond) // drain final events result := map[string]any{ "id": *scenario, "pass": ok, "detail": msg, } f, _ := os.Create(fmt.Sprintf("%s/result_%s.json", *outDir, *scenario)) json.NewEncoder(f).Encode(result) f.Close() status := "FAIL" if ok { status = "PASS" } fmt.Printf("%s: %s - %s\n", *scenario, status, msg) os.WriteFile(fmt.Sprintf("%s/status_%s.txt", *outDir, *scenario), []byte(status), 0o644) if !ok { os.Exit(1) } } // runDebugScript exercises FORCE/TRACE/BREAK over the TCP 8080 command // protocol (grammar confirmed against Source/Components/Interfaces/ // DebugService/DebugServiceBase.cpp's HandleCommand: "FORCE ", // "TRACE <0|1> [decim]", "BREAK ", // each replying "OK \n"). func runDebugScript(mc *debugger.MarteController) (bool, string) { mc.SendCommand("FORCE App.Data.Timer.Counter 42") time.Sleep(200 * time.Millisecond) mc.SendCommand("TRACE App.Data.Timer.Counter 1 1") time.Sleep(200 * time.Millisecond) mc.SendCommand("BREAK App.Data.Timer.Counter > 1000") time.Sleep(500 * time.Millisecond) mc.SendCommand("BREAK App.Data.Timer.Counter OFF") mc.SendCommand("UNFORCE App.Data.Timer.Counter") if !mc.IsConnected() { return false, "lost connection to DebugService during script" } return true, "FORCE/TRACE/BREAK sequence completed without disconnect" } // runTcpLoggerScript triggers a log-worthy event (an invalid FORCE) and // waits for a matching "log" event to arrive over the TCPLogger TCP port // within dur. MarteController's runLog forwards each "LOG " // line it reads from the TCPLogger connection through sink as // map[string]any{"type": "log", "time": ..., "level": ..., "message": ...}, // so we poll the captured events slice for that shape. func runTcpLoggerScript(mc *debugger.MarteController, dur time.Duration, events *[]event, mu *sync.Mutex) (bool, string) { mc.SendCommand("FORCE App.Data.DoesNotExist 1") deadline := time.Now().Add(dur) for time.Now().Before(deadline) { mu.Lock() for _, e := range *events { if m, ok := e.data.(map[string]any); ok && m["type"] == "log" { mu.Unlock() return true, fmt.Sprintf("received log event: %v", m) } } mu.Unlock() time.Sleep(100 * time.Millisecond) } if !mc.IsConnected() { return false, "lost connection during tcplogger wait and no log event received" } return false, "no log event received within duration" }