diff --git a/Test/E2E/suite/debugclient/debugclient b/Test/E2E/suite/debugclient/debugclient index bb821a1..52db63c 100755 Binary files a/Test/E2E/suite/debugclient/debugclient and b/Test/E2E/suite/debugclient/debugclient differ diff --git a/Test/E2E/suite/debugclient/main.go b/Test/E2E/suite/debugclient/main.go index 1d3757c..dffdb60 100644 --- a/Test/E2E/suite/debugclient/main.go +++ b/Test/E2E/suite/debugclient/main.go @@ -69,7 +69,7 @@ func main() { var msg string switch *mode { case "debug": - ok, msg = runDebugScript(mc) + ok, msg = runDebugScript(mc, &events, &mu) case "tcplogger": ok, msg = runTcpLoggerScript(mc, *dur, &events, &mu, baseline) default: @@ -100,24 +100,98 @@ func main() { } } +// waitForAck polls events (from index start onward) for a MarteController +// "text_line" event exactly matching DebugServiceBase::HandleCommand's +// "OK \n" reply grammar, requiring count > 0. HandleCommand +// prints this reply for every one of FORCE/UNFORCE/TRACE/BREAK regardless of +// enable/disable direction, and count is always "number of signals the +// command actually matched" (DebugServiceBase.cpp:1468-1525) — so count==0 +// means the target signal path was never resolved (e.g. a wire-format/name- +// translation bug), which a bare "did the socket stay open" check like the +// old runDebugScript could never catch. +func waitForAck(events *[]event, mu *sync.Mutex, start int, token string, timeout time.Duration) (string, bool) { + prefix := "OK " + token + " " + deadline := time.Now().Add(timeout) + for time.Now().Before(deadline) { + mu.Lock() + for _, e := range (*events)[start:] { + m, ok := e.data.(map[string]any) + if !ok || m["type"] != "text_line" { + continue + } + line, _ := m["data"].(string) + if !strings.HasPrefix(line, prefix) { + continue + } + var count uint32 + if _, err := fmt.Sscanf(line, prefix+"%d", &count); err == nil && count > 0 { + mu.Unlock() + return line, true + } + } + mu.Unlock() + time.Sleep(50 * time.Millisecond) + } + return "", false +} + // 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) { +// each replying "OK \n"). Each step waits for the real +// "OK " acknowledgement with count > 0 via waitForAck, +// confirming DebugService actually resolved and applied the command against +// the named signal — not merely that the TCP connection stayed alive (which +// would pass even if DebugService silently no-op'd every command, e.g. due +// to a signal-name/wire-format bug). +func runDebugScript(mc *debugger.MarteController, events *[]event, mu *sync.Mutex) (bool, string) { + const ackTimeout = 2 * time.Second + + mu.Lock() + start := len(*events) + mu.Unlock() mc.SendCommand("FORCE App.Data.Timer.Counter 42") - time.Sleep(200 * time.Millisecond) + if _, ok := waitForAck(events, mu, start, "FORCE", ackTimeout); !ok { + return false, "no \"OK FORCE \" ack with count>0 received (signal not resolved)" + } + + mu.Lock() + start = len(*events) + mu.Unlock() mc.SendCommand("TRACE App.Data.Timer.Counter 1 1") - time.Sleep(200 * time.Millisecond) + if _, ok := waitForAck(events, mu, start, "TRACE", ackTimeout); !ok { + return false, "no \"OK TRACE \" ack with count>0 received (signal not resolved)" + } + + mu.Lock() + start = len(*events) + mu.Unlock() mc.SendCommand("BREAK App.Data.Timer.Counter > 1000") - time.Sleep(500 * time.Millisecond) + if _, ok := waitForAck(events, mu, start, "BREAK", ackTimeout); !ok { + return false, "no \"OK BREAK \" ack with count>0 received for SetBreak (signal not resolved)" + } + + mu.Lock() + start = len(*events) + mu.Unlock() mc.SendCommand("BREAK App.Data.Timer.Counter OFF") + if _, ok := waitForAck(events, mu, start, "BREAK", ackTimeout); !ok { + return false, "no \"OK BREAK \" ack with count>0 received for ClearBreak (signal not resolved)" + } + + mu.Lock() + start = len(*events) + mu.Unlock() mc.SendCommand("UNFORCE App.Data.Timer.Counter") + if _, ok := waitForAck(events, mu, start, "UNFORCE", ackTimeout); !ok { + return false, "no \"OK UNFORCE \" ack with count>0 received (signal not resolved)" + } + if !mc.IsConnected() { return false, "lost connection to DebugService during script" } - return true, "FORCE/TRACE/BREAK sequence completed without disconnect" + return true, "FORCE/TRACE/BREAK/UNFORCE all acknowledged with count>0 (real signal resolution confirmed)" } // runTcpLoggerScript triggers a log-worthy event and waits for a matching