Research 027 asked for cups with the printer's driver. Measured: both Brother queues already print through IPP Everywhere, so cups and cups-filters are the whole driver, and the AUR vendor packages beside them serve no queue. The desktop's Canon is the exception: its 2012 driver is AUR-only and waits for the mesh's package repository; it stays as found. Seven Go tools: printers (state, device, driverless or not, supply levels), queue, cancel (the account first, sudo -n when CUPS refuses it), print, default, resume, and drivers (which packages bring drivers, which are foreign, which no queue uses).
148 lines
4.6 KiB
Go
148 lines
4.6 KiB
Go
package main
|
|
|
|
// Tests of kit.go, the same in each workstation module.
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// fake records the commands asked and answers each from a function of the command line.
|
|
type fake struct {
|
|
asked []Cmd
|
|
answer func(line string, c Cmd) Result
|
|
}
|
|
|
|
func (f *fake) runner() Runner {
|
|
return func(c Cmd) Result {
|
|
f.asked = append(f.asked, c)
|
|
name, args := argv(c)
|
|
line := strings.TrimSpace(name + " " + strings.Join(args, " "))
|
|
if f.answer == nil {
|
|
return Result{}
|
|
}
|
|
return f.answer(line, c)
|
|
}
|
|
}
|
|
|
|
func (f *fake) lines() []string {
|
|
out := []string{}
|
|
for _, c := range f.asked {
|
|
name, args := argv(c)
|
|
out = append(out, strings.TrimSpace(name+" "+strings.Join(args, " ")))
|
|
}
|
|
return out
|
|
}
|
|
|
|
// using installs a fake runner and a non-root uid for one test.
|
|
func using(t *testing.T, answer func(line string, c Cmd) Result) *fake {
|
|
t.Helper()
|
|
f := &fake{answer: answer}
|
|
wasRun, wasUID := run, euid
|
|
run, euid = f.runner(), func() int { return 1000 }
|
|
t.Cleanup(func() { run, euid = wasRun, wasUID })
|
|
return f
|
|
}
|
|
|
|
func ok(stdout string) Result { return Result{Stdout: stdout} }
|
|
|
|
func TestKitAnActThatNeedsRootGoesThroughSudoWithoutAPromptUnlessAlreadyRoot(t *testing.T) {
|
|
was := euid
|
|
defer func() { euid = was }()
|
|
euid = func() int { return 1000 }
|
|
if name, args := argv(Cmd{Name: "x", Args: []string{"a"}, Root: true}); name != "sudo" || strings.Join(args, " ") != "-n x a" {
|
|
t.Fatalf("not root: %s %v", name, args)
|
|
}
|
|
if name, _ := argv(Cmd{Name: "x"}); name != "x" {
|
|
t.Fatalf("a read is run as the account: %s", name)
|
|
}
|
|
euid = func() int { return 0 }
|
|
if name, _ := argv(Cmd{Name: "x", Root: true}); name != "x" {
|
|
t.Fatalf("as root no sudo: %s", name)
|
|
}
|
|
}
|
|
|
|
func TestKitAFailureIsNamedByHowItFailed(t *testing.T) {
|
|
was := euid
|
|
defer func() { euid = was }()
|
|
euid = func() int { return 1000 }
|
|
cases := []struct {
|
|
c Cmd
|
|
r Result
|
|
want string
|
|
}{
|
|
{Cmd{Name: "nothere"}, Result{Status: 127, Error: "not-found"}, "not installed"},
|
|
{Cmd{Name: "x", Root: true}, Result{Status: 127, Error: "not-found"}, "sudo is not installed"},
|
|
{Cmd{Name: "x", Root: true}, Result{Status: 1, Stderr: "sudo: a password is required\n"}, "sudo -n refused"},
|
|
{Cmd{Name: "x", Root: true}, Result{Status: 1, Stderr: "sudo: x: command not found\n"}, "x is not installed"},
|
|
{Cmd{Name: "x"}, Result{Status: 124, Error: "timeout"}, "within 20s"},
|
|
{Cmd{Name: "x", Args: []string{"y"}}, Result{Status: 3, Stderr: "boom\n"}, "x y exited 3: boom"},
|
|
{Cmd{Name: "x"}, Result{Status: 3}, "said nothing"},
|
|
}
|
|
for _, k := range cases {
|
|
err := failure(k.c, k.r)
|
|
if err == nil || !strings.Contains(err.Error(), k.want) {
|
|
t.Errorf("%+v: %v, want %q", k.r, err, k.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestKitOutputIsBoundedAndSaysSo(t *testing.T) {
|
|
var w bounded
|
|
big := strings.Repeat("a", MostOutput+10)
|
|
n, _ := w.Write([]byte(big))
|
|
if n != len(big) || w.b.Len() != MostOutput || !w.cut {
|
|
t.Fatalf("kept %d of %d, cut %v", w.b.Len(), len(big), w.cut)
|
|
}
|
|
}
|
|
|
|
func TestKitTheRealRunnerRunsEndsAndReportsAMissingProgram(t *testing.T) {
|
|
r := execRun(Cmd{Name: "sh", Args: []string{"-c", "echo out; echo err >&2; exit 3"}})
|
|
if r.Status != 3 || strings.TrimSpace(r.Stdout) != "out" || strings.TrimSpace(r.Stderr) != "err" {
|
|
t.Fatalf("%+v", r)
|
|
}
|
|
r = execRun(Cmd{Name: "sh", Args: []string{"-c", "sleep 5 & sleep 5"}, Timeout: 200 * time.Millisecond})
|
|
if r.Error != "timeout" {
|
|
t.Fatalf("a slow command: %+v", r)
|
|
}
|
|
r = execRun(Cmd{Name: "no-such-program-anywhere"})
|
|
if r.Error != "not-found" {
|
|
t.Fatalf("a missing program: %+v", r)
|
|
}
|
|
r = execRun(Cmd{Name: "cat", Stdin: "given"})
|
|
if r.Stdout != "given" {
|
|
t.Fatalf("stdin: %+v", r)
|
|
}
|
|
start := time.Now()
|
|
r = execRun(Cmd{Name: "sh", Args: []string{"-c", "echo kept; (sleep 3 &) ; exit 0"}, Detached: true})
|
|
if r.Status != 0 || strings.TrimSpace(r.Stdout) != "kept" || time.Since(start) > 2*time.Second {
|
|
t.Fatalf("a detached command returns when it exits, not when its child does: %+v after %s", r, time.Since(start))
|
|
}
|
|
}
|
|
|
|
func TestKitArgumentsAreReadStrictly(t *testing.T) {
|
|
args := map[string]any{"s": "x", "n": float64(5), "f": 1.5, "b": true, "l": []any{"a", "b"}}
|
|
if _, err := text(args, "missing"); err == nil {
|
|
t.Error("a missing required string")
|
|
}
|
|
if n, _ := optWhole(args, "n", 1, 1, 3); n != 3 {
|
|
t.Errorf("held to most: %d", n)
|
|
}
|
|
if _, err := optWhole(args, "n", 1, 6, 9); err == nil {
|
|
t.Error("below least")
|
|
}
|
|
if _, err := optWhole(args, "f", 1, 0, 9); err == nil {
|
|
t.Error("a fraction")
|
|
}
|
|
if l, _ := optList(args, "l"); len(l) != 2 {
|
|
t.Errorf("list: %v", l)
|
|
}
|
|
if b, _ := optFlag(args, "b", false); !b {
|
|
t.Error("flag")
|
|
}
|
|
if err := plainName("name", "--all"); err == nil {
|
|
t.Error("an option as a name")
|
|
}
|
|
}
|