blob: 10c212fa37a28910cfa1a56647e1ecaf8e67e391 [file] [log] [blame]
// Copyright 2021 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Package cmd provides support for running commands.
package cmd
import (
"bytes"
"context"
"fmt"
"os/exec"
"reflect"
"strings"
)
// CommandRunner is the common interface for this module.
type CommandRunner interface {
RunCommand(ctx context.Context, stdoutBuf, stderrBuf *bytes.Buffer, dir, name string, args ...string) error
}
// RealCommandRunner actually runs commands.
type RealCommandRunner struct{}
// RunCommand runs a command.
func (c RealCommandRunner) RunCommand(ctx context.Context, stdoutBuf, stderrBuf *bytes.Buffer, dir, name string, args ...string) error {
cmd := exec.CommandContext(ctx, name, args...)
cmd.Stdout = stdoutBuf
cmd.Stderr = stderrBuf
cmd.Dir = dir
return cmd.Run()
}
// FakeCommandRunner does not actually run commands.
// It is used for testing.
type FakeCommandRunner struct {
Stdout string
Stderr string
// Only one of ExpectedCmd and ExpectedCmdPartial can be set.
ExpectedCmd []string
ExpectedCmdPartial []string
ExpectedDir string
FailCommand bool
FailError string
}
// RunCommand runs a command (not actually).
func (c FakeCommandRunner) RunCommand(ctx context.Context, stdoutBuf, stderrBuf *bytes.Buffer, dir, name string, args ...string) error {
stdoutBuf.WriteString(c.Stdout)
stderrBuf.WriteString(c.Stderr)
cmd := append([]string{name}, args...)
if len(c.ExpectedCmd) > 0 && len(c.ExpectedCmdPartial) > 0 {
return fmt.Errorf("ExpectedCmd and ExpectedCmdPartial cannot both be set")
} else if len(c.ExpectedCmd) > 0 {
if !reflect.DeepEqual(cmd, c.ExpectedCmd) {
expectedCmd := strings.Join(c.ExpectedCmd, " ")
actualCmd := strings.Join(cmd, " ")
return fmt.Errorf("wrong cmd; expected %s got %s", expectedCmd, actualCmd)
}
} else if len(c.ExpectedCmdPartial) > 0 {
partialStr := strings.Join(c.ExpectedCmdPartial, " ")
cmdStr := strings.Join(cmd, " ")
if !strings.Contains(cmdStr, partialStr) {
return fmt.Errorf("wrong cmd; %s did not contain %s", cmdStr, partialStr)
}
}
if c.ExpectedDir != "" {
if dir != c.ExpectedDir {
return fmt.Errorf("wrong cmd dir; expected %s got %s", c.ExpectedDir, dir)
}
}
if c.FailCommand {
return fmt.Errorf(c.FailError)
}
return nil
}
// FakeCommandRunnerMulti provides multiple command runners.
type FakeCommandRunnerMulti struct {
run int
CommandRunners []FakeCommandRunner
}
// RunCommand runs a command (not actually).
func (c *FakeCommandRunnerMulti) RunCommand(ctx context.Context, stdoutBuf, stderrBuf *bytes.Buffer, dir, name string, args ...string) error {
if c.run >= len(c.CommandRunners) {
return fmt.Errorf("unexpected cmd")
}
err := c.CommandRunners[c.run].RunCommand(ctx, stdoutBuf, stderrBuf, dir, name, args...)
c.run++
return err
}