139 lines
3.4 KiB
Go
139 lines
3.4 KiB
Go
package nix_test
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"os/exec"
|
|
"slices"
|
|
"strings"
|
|
"syscall"
|
|
"testing"
|
|
|
|
"gensokyo.uk/nix"
|
|
"hakurei.app/command"
|
|
)
|
|
|
|
const (
|
|
nonexistent = "/proc/nonexistent"
|
|
|
|
runAsNixStub = "TEST_RUN_AS_NIX_STUB"
|
|
)
|
|
|
|
var (
|
|
stubExtraArgs = []string{
|
|
"-test.run=TestNixStub",
|
|
"--",
|
|
}
|
|
)
|
|
|
|
// stubNixCommand causes all nix command invocations to invoke the stub for the current test.
|
|
func stubNixCommand(t *testing.T) {
|
|
n := nix.Nix
|
|
nix.Nix = os.Args[0]
|
|
t.Cleanup(func() { nix.Nix = n })
|
|
|
|
cur, ok := os.LookupEnv(runAsNixStub)
|
|
if err := os.Setenv(runAsNixStub, "1"); err != nil {
|
|
t.Fatalf("cannot setenv: %v", err)
|
|
}
|
|
t.Cleanup(func() {
|
|
if !ok {
|
|
if err := os.Unsetenv(runAsNixStub); err != nil {
|
|
t.Fatalf("cannot unsetenv: %v", err)
|
|
}
|
|
return
|
|
}
|
|
if err := os.Setenv(runAsNixStub, cur); err != nil {
|
|
t.Fatalf("cannot setenv: %v", err)
|
|
}
|
|
})
|
|
}
|
|
|
|
// newStubContext creates a context for use with the nix command stub.
|
|
func newStubContext(ctx context.Context, extraArgs []string, stdout, stderr io.Writer) nix.Context {
|
|
return nix.New(ctx, append(stubExtraArgs, extraArgs...), stdout, stderr)
|
|
}
|
|
|
|
type stubContextCommand struct {
|
|
f func(*exec.Cmd)
|
|
|
|
nix.Context
|
|
}
|
|
|
|
func (s *stubContextCommand) Nix(ctx context.Context, arg ...string) *exec.Cmd {
|
|
cmd := s.Context.Nix(ctx, arg...)
|
|
if s.f != nil {
|
|
s.f(cmd)
|
|
}
|
|
return cmd
|
|
}
|
|
|
|
// newStubContext creates a context for use with the nix command stub with a function injected into [nixbuild.Context.Nix].
|
|
func newStubContextCommand(f func(*exec.Cmd), ctx context.Context, extraArgs []string, stdout, stderr io.Writer) nix.Context {
|
|
return &stubContextCommand{f, newStubContext(ctx, extraArgs, stdout, stderr)}
|
|
}
|
|
|
|
// breakNixCommand makes all nix invocations fail for the current test.
|
|
func breakNixCommand(t *testing.T) {
|
|
n := nix.Nix
|
|
nix.Nix = nonexistent
|
|
t.Cleanup(func() { nix.Nix = n })
|
|
}
|
|
|
|
type stubCommandInitFunc func(c command.Command)
|
|
|
|
var stubCommandInit []stubCommandInitFunc
|
|
|
|
// this test mocks the nix command
|
|
func TestNixStub(t *testing.T) {
|
|
if os.Getenv(runAsNixStub) != "1" {
|
|
return
|
|
}
|
|
|
|
var (
|
|
flagExtraExperimentalFeatures string
|
|
)
|
|
c := command.New(os.Stderr, t.Logf, "nix", func(args []string) error {
|
|
if flagExtraExperimentalFeatures != nix.ExperimentalFeaturesFlakes {
|
|
t.Fatalf("%s: %q, want %q",
|
|
nix.ExtraExperimentalFeatures, flagExtraExperimentalFeatures, nix.ExperimentalFeaturesFlakes)
|
|
return syscall.ENOTRECOVERABLE
|
|
}
|
|
return nil
|
|
}).
|
|
Flag(&flagExtraExperimentalFeatures, trimFlagName(nix.ExtraExperimentalFeatures), command.StringFlag(""),
|
|
fmt.Sprintf("expects exactly %q", nix.ExperimentalFeaturesFlakes))
|
|
|
|
c.Command(nix.ValueTrue, command.UsageInternal, func([]string) error { return nil })
|
|
|
|
for _, f := range stubCommandInit {
|
|
f(c)
|
|
}
|
|
|
|
c.MustParse(os.Args[len(stubExtraArgs)+1:], func(err error) {
|
|
if err != nil {
|
|
t.Fatal(err.Error())
|
|
}
|
|
})
|
|
}
|
|
|
|
// checkStdin checks whether entries read from r is equivalent to want.
|
|
func checkStdin(r io.Reader, want ...string) error {
|
|
if got, err := nix.ReadStdin(r); err != nil {
|
|
return err
|
|
} else if !slices.Equal(got, want) {
|
|
return errors.New(fmt.Sprintf("got build %#v, want %#v", got, want))
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func trimFlagName(n string) string { return strings.TrimPrefix(n, "--") }
|
|
|
|
// errorWriter unconditionally returns a non-nil error
|
|
type errorWriter struct{}
|
|
|
|
func (errorWriter) Write([]byte) (int, error) { return 0, syscall.EIO }
|