Skip to content

Commit

Permalink
Add test for reconnect with manually entered password.
Browse files Browse the repository at this point in the history
  • Loading branch information
joshkunz committed May 5, 2023
1 parent 0596078 commit fe317ab
Show file tree
Hide file tree
Showing 3 changed files with 123 additions and 2 deletions.
5 changes: 5 additions & 0 deletions src/getpass.cc
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ void SetEcho(FILE *stream, bool echo_state, bool echo_nl_state) {
struct termios flags;
int res = tcgetattr(fileno(stream), &flags);
if (res != 0) {
if (errno == ENOTTY) {
// If the output device is not a tty, then we don't need to
// worry about the echo.
return;
}
perror("SetEcho (tcgetattr)");
std::exit(1);
}
Expand Down
105 changes: 104 additions & 1 deletion t/integration/integration/integration_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package integration_test

import (
"bufio"
"bytes"
"context"
"io"
"io/ioutil"
Expand Down Expand Up @@ -963,6 +965,7 @@ func TestExclude(t *testing.T) {
}

func TestReconnect(t *testing.T) {
t.Parallel()
ctx := context.Background()

root, err := testmpd.NewRoot(&testmpd.Options{LibraryRoot: "/music"})
Expand Down Expand Up @@ -1030,9 +1033,10 @@ func TestReconnect(t *testing.T) {
if err := restartMPD.Shutdown(); err != nil {
t.Errorf("restart mpd did not shutdown cleanly: %v", err)
}

}

func TestReconnect_Timeout(t *testing.T) {
t.Parallel()
ctx := context.Background()

as, mpd := run(ctx, t, runOptions{
Expand Down Expand Up @@ -1077,3 +1081,102 @@ func TestReconnect_Timeout(t *testing.T) {
t.Errorf("ashuffle stderr does not include %q", wantMsg)
}
}

func TestReconnect_PasswordPrompt(t *testing.T) {
t.Parallel()
ctx := context.Background()

mpd, err := testmpd.New(ctx, &testmpd.Options{
LibraryRoot: "/music",
DefaultPermissions: []string{"read"},
Passwords: []testmpd.Password{
{
Password: "super_secret_mpd_password",
Permissions: []string{"read", "add", "control", "admin"},
},
},
})
if err != nil {
t.Fatalf("failed to create new MPD instance: %v", err)
}

stdinr, stdinw := io.Pipe()
stdoutr, stdoutw := io.Pipe()
defer stdoutr.Close()
defer stdoutw.Close()

as, err := testashuffle.New(ctx, ashuffleBin, &testashuffle.Options{
MPDAddress: mpd,
Args: []string{"--tweak", "reconnect-timeout=10s"},
Stdin: stdinr,
Stdout: stdoutw,
ShutdownTimeout: 30 * time.Second,
})
if err != nil {
t.Fatalf("failed to start ashuffle: %v", err)
}

scanStdout := bufio.NewScanner(stdoutr)
scanStdout.Split(func(data []byte, _ bool) (int, []byte, error) {
idx := bytes.IndexRune(data, ':')
if idx < 0 {
return 0, nil, nil
}
return idx, data[:idx+1], nil
})
for scanStdout.Scan() {
if strings.Contains(scanStdout.Text(), "mpd password:") {
break
}
}
if err := scanStdout.Err(); err != nil {
t.Fatalf("scanner threw error: %v", err)
}

// Make sure that stdout Pipe doesn't block future writes.
go io.Copy(io.Discard, stdoutr)

// Enter the password.
if _, err := io.WriteString(stdinw, "super_secret_mpd_password\n"); err != nil {
t.Fatalf("failed to write password to ashuffle stdin: %v", err)
}

// Close out stdin now that we've entered our password.
stdinr.Close()
stdinw.Close()

// Wait for ashuffle to startup, and start playing a song.
tryWaitFor(func() bool { return mpd.PlayState() == testmpd.StatePlay })

if state := mpd.PlayState(); state != testmpd.StatePlay {
t.Errorf("[before shutdown] mpd.PlayState() = %v, want play", state)
}

// Shutdown MPD, ashuffle should remain running. But we won't actually
// know till later. Because we entered the password manually this should
// immediately exit ashuffle.
if err := mpd.Shutdown(); err != nil {
t.Fatalf("Failed to shutdown MPD: %v", err)
}
if !mpd.IsOk() {
t.Errorf("MPD had errors: %+v", mpd.Errors)
}

err = as.Shutdown(testashuffle.ShutdownSoft)
if err == nil {
t.Logf("stdout:\n%s", as.Stdout)
t.Logf("stderr:\n%s", as.Stderr)
t.Fatalf("ashuffle should have exited with an error, but shutdown cleanly instead")
}

exitErr, ok := err.(*exec.ExitError)
if !ok {
t.Logf("stdout:\n%s", as.Stdout)
t.Logf("stderr:\n%s", as.Stderr)
t.Fatalf("ashuffle shutdown did not produce exit error, produced %#v", err)
}

if got := exitErr.ExitCode(); got != 1 {
t.Errorf("ashuffle exited with code %d, want 1 (full err: %v)", got, err)
}
}
15 changes: 14 additions & 1 deletion t/integration/testashuffle/testashuffle.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"context"
"errors"
"fmt"
"io"
"io/ioutil"
"os"
"os/exec"
Expand Down Expand Up @@ -143,6 +144,8 @@ type Options struct {
Args []string
EnableHeapProfile bool
ShutdownTimeout time.Duration
Stdin io.Reader
Stdout io.Writer
}

func New(ctx context.Context, path string, opts *Options) (*Ashuffle, error) {
Expand All @@ -169,7 +172,17 @@ func New(ctx context.Context, path string, opts *Options) (*Ashuffle, error) {

var stdout, stderr bytes.Buffer
cmd.Stderr = &stderr
cmd.Stdout = &stdout
if opts != nil && opts.Stdout != nil {
// If the user provides their own stdout channel, then tee between
// both the buffer, and their channel.
cmd.Stdout = io.MultiWriter(opts.Stdout, &stdout)
} else {
cmd.Stdout = &stdout
}

if opts != nil && opts.Stdin != nil {
cmd.Stdin = opts.Stdin
}

shutdownTimeout := maxShutdownWait
if opts != nil && opts.ShutdownTimeout != 0 {
Expand Down

0 comments on commit fe317ab

Please sign in to comment.