Skip to content

Conversation

@mitchell-as
Copy link
Contributor

@mitchell-as mitchell-as commented May 16, 2024

The idea is to call exec.Cmd.Wait() in a goroutine (https://github.com/ActiveState/termtest/pull/12/files#diff-13ff3d9e77d1ebdf70d066552cb9e504472550dc97fd74aac53a8ab3a63e54a1) and edit the Expect() functions (https://github.com/ActiveState/termtest/pull/12/files#diff-a63ecf8315eeb0943ebec69499243bd650ae6b99635bde7aa1c0f6ac773368c2R106-R111 and https://github.com/ActiveState/termtest/pull/12/files#diff-2a74ac0f372490e4a5cf19a11e38b9329713643dc44c65ca5f7de285f84c9391R183-R187) to poll for a command's exit status alongside the usual expect checks. A helper function that returns a channel is used to facilitate use inside select{} statements (https://github.com/ActiveState/termtest/pull/12/files#diff-296a01a801f363652ec765418086e861f5db0287df573d54c8fde4eebe0e26e9R43).

ActiveState/cli#3309 uses this PR's commit when running integration tests for State Tool to verify that it doesn't break anything at least.

@CLAassistant
Copy link

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@mitchell-as mitchell-as requested a review from Naatan May 20, 2024 17:08
helpers.go Outdated
// This can be used within a select{} statement.
// If waitExtra is given, waits a little bit before sending cmdExit info. This allows any fellow
// switch cases to handle unprocessed stdout.
func ttExited(tt *TermTest, waitExtra bool) chan *cmdExit {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
func ttExited(tt *TermTest, waitExtra bool) chan *cmdExit {
func (tt *TermTest) waitForCmdExit(waitExtra bool) chan *cmdExit {

Note as this is a property of termtest we should move this out of helpers.

helpers.go Outdated
select {
case <-ticker.C:
if tt.exited != nil {
if waitExtra {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you explain the rationale for this in a comment?

Copy link
Contributor Author

@mitchell-as mitchell-as May 21, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is explained in the function documentation. Should I move/copy that comment here?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No I understand what the param does. What I'm asking is why is this a thing at all? I see this function is called from two different places, once with this param set to true and once set to false. How did you decide one of them needed this and the other didn't?

Copy link
Contributor Author

@mitchell-as mitchell-as May 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added some more comments around this. Basically, you want to wait if you have sibling output consumers that are expecting output so that you don't immediately fail the expect if the process exits. You don't want to wait if you are expecting the process to exit or have exited (i.e. cp.ExpectExit*()). As a side note, if we were to always wait, then every cp.ExpectExit*() would take slightly longer.

e.mutex.Lock()
e.opts.Logger.Println("Encountered timeout")
return fmt.Errorf("after %s: %w", e.opts.Timeout, TimeoutError)
case state := <-ttExited(e.tt, true):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess this is an error condition, because normally the other selects would have fired before this one? Please add a comment explaining.

Though I wonder if this really should be here. Processes exiting non-zero is not an error condition to termtest. Termtest is meant to be used for testing processes in a terminal context, and failure conditions such as non-zero exits are part of that scope. Again though, I might be missing something, so if we want to keep this definitely add a comment.

Copy link
Contributor Author

@mitchell-as mitchell-as May 21, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not quite. The first case would have fired if there is an output match. The second case would have fired if an ultimate timeout was reached, and the process was still active. The third case (this one) will fire if the process exits (either normally, or abnormally; it does not matter) without either of the two cases having (yet) fired.

The most common case of this firing is if we typo an expectation and the process exits normally with unmatched output. I assume this is exactly the case we're trying to catch with this ticket.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, so this would basically alert you that you forgot a tt.ExpectExit() of some sort? That's a nice win!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In some cases, yes, I suppose so.

@mitchell-as
Copy link
Contributor Author

I'm flagging for re-review just to get feedback on my replies. I'll make the requested changes tomorrow and then submit for another review. Thanks for your understanding.

@mitchell-as mitchell-as requested a review from Naatan May 21, 2024 17:08
helpers.go Outdated
select {
case <-ticker.C:
if tt.exited != nil {
if waitExtra {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No I understand what the param does. What I'm asking is why is this a thing at all? I see this function is called from two different places, once with this param set to true and once set to false. How did you decide one of them needed this and the other didn't?

e.mutex.Lock()
e.opts.Logger.Println("Encountered timeout")
return fmt.Errorf("after %s: %w", e.opts.Timeout, TimeoutError)
case state := <-ttExited(e.tt, true):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, so this would basically alert you that you forgot a tt.ExpectExit() of some sort? That's a nice win!

@mitchell-as mitchell-as requested a review from Naatan May 22, 2024 15:52
Copy link
Contributor

@Naatan Naatan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, thanks for the comment, makes sense!

@mitchell-as mitchell-as merged commit 45f7444 into v2-wip May 22, 2024
@mitchell-as mitchell-as deleted the dx-2782 branch May 22, 2024 17:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants