Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

testscript: add waitmatch command #268

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

mvdan
Copy link
Collaborator

@mvdan mvdan commented Aug 8, 2024

(see commit message)

It blocks until a background process prints a matching line to stdout.
Any given env var names are set to the subexpression strings as well.

This is useful to wait for a background process to be ready before
running more commands to interact with it, such as waiting for an HTTP
server to listen on a TCP port before sending it the first request.

In fact, our interrupt tests already had this problem; we only worked
around it by having the process create a file when it's ready,
and adding a custom command which would wait for the file by polling.
It's much nicer and faster to use waitmatch instead,
and as a bonus, we can refactor existing tests rather than adding more.
@mvdan
Copy link
Collaborator Author

mvdan commented Aug 8, 2024

Note that I went with waitmatch rather than our previously agreed wait-match as we currently don't use any dashes.

Copy link
Owner

@rogpeppe rogpeppe left a comment

Choose a reason for hiding this comment

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

Thanks for doing this! I'm not quite sure that the simple os.Pipe approach quite works though. Also, it would be good to add the command docs to doc.go.

// which matches the given regular expression. Once a match is found, the given
// environment variable names are set to the subexpressions of the match.
func (ts *TestScript) cmdWaitMatch(neg bool, args []string) {
if len(args) < 1 {
Copy link
Owner

Choose a reason for hiding this comment

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

Suggested change
if len(args) < 1 {
if len(args) < 2 {

cmd.Stdin = strings.NewReader(ts.stdin)
cmd.Stdout = &stdoutBuf
stdoutr, stdoutw, err := os.Pipe()
Copy link
Owner

Choose a reason for hiding this comment

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

Also, I'm not sure that os.Pipe is good enough here. If the process is producing a lot of output and the script never calls waitmatch, the pipe buffer could fill up and then the process will be blocked and potentially non-functioning as a result. I suspect we need to write everything to a buffer immediately, and then have a way to layer a reader on top of that.

Also, I wonder if we should make it possible to wait on stderr matches too? It's common for logged output to be printed to stderr and probably not uncommon to need to wait on that. Maybe waitmatch should wait on the combined output, although that might be tricky to arrange because we also need it to be split.

}

// readLine consumes enough bytes to read a line.
func readLine(r io.Reader) ([]byte, error) {
Copy link
Owner

Choose a reason for hiding this comment

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

I don't get why we need to use this rather than (say) bufio.Scanner.

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.

2 participants