google/xls

Enhancements to test proc termination

Opened this issue · 0 comments

What's hard to do? (limit 100 words)

In test procs, I found that it's easy for the top-level send on the test terminator channel to fire "too early".

Consider the following DUT proc:

proc Foo {
    ...
   
    next(state: ()) {
        let (tok, input) = recv(join(), in_chan);
        assert!(is_valid(input), "input_valid");
        ...
        send(tok, out_chan, some_output);
    }
}

Scenario 1: A proc "smoke test" that just aims to check some input sequence runs successfully without deadlocks or assertion errors. If the test proc does not receive any outputs from the DUT after sending inputs, the send to the terminator channel may execute and prematurely terminate the test as a vacuous "pass" without anything happening. This can even occur when the child procs are instantiated first (they trigger, wait on a receive, and then the same thing occurs at the top).

#[test_proc]
proc foo_proc_test {
    ...
    
    next(state: ()) {
        let tok = send(join(), in_chan, in0);
        // let (tok, out0) = recv(tok, out_chan);  // required to avoid premature termination
        send(tok, terminator, true);
    }
}

Scenario 2: Some length N sequence of inputs is fed into the DUT, where input N is expected to trigger an assertion failure. However, the test proc only invokes N-1 receives before terminating.

#[test_proc]
proc foo_proc_test {
    ...
    
    next(state: ()) {
        let tok = send(join(), in_chan, in0);
        let tok = send(tok, in_chan, in1);
        let tok = send(tok, in_chan, invalid_in2); 
        let (tok, out0) = recv(tok, out_chan);
        let (tok, out0) = recv(tok, out_chan);
        send(tok, terminator, true);
    }
}

Even though we've sent a bad input that might be expected to fire the assertion, in reality, we terminate after the second receive, so the 3rd tick of the DUT proc never happens.

Current best alternative workaround (limit 100 words)

This seems to just require some intuition to do correctly. I was able to debug with an outside hint/guess in my case and then verify this was indeed what was happening by sprinkling in trace_fmts and running the interpreter_main binary with --alsologtostderr.

Your view of the "best case XLS enhancement" (limit 100 words)

Not sure exactly, but some off the cuff thoughts:

  • Is it possible to try to run tests to some quiescent state?
  • Implicitly send on the terminator channel as a separate phase?
  • Even being aware of this gotcha with early termination, I could envision a recv_until_empty or flush_channel being useful, particularly in Scenario 2. That might also amount to a way to indirectly get at #1281 (regular channel, just receive until empty and keep the last value).