go-i3/golden_test.go

300 lines
7.5 KiB
Go

package i3
import (
"context"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"strings"
"testing"
"github.com/google/go-cmp/cmp"
)
// TestGoldensSubprocess runs in a process which has been started with
// DISPLAY= pointing to an Xvfb instance with i3 -c testdata/i3.config running.
func TestGoldensSubprocess(t *testing.T) {
if os.Getenv("GO_WANT_XVFB") != "1" {
t.Skip("parent process")
}
if _, err := RunCommand("open; mark foo"); err != nil {
t.Fatal(err)
}
t.Run("GetVersion", func(t *testing.T) {
t.Parallel()
got, err := GetVersion()
if err != nil {
t.Fatal(err)
}
got.HumanReadable = "" // too brittle to compare
got.Patch = 0 // the IPC interface does not change across patch releases
abs, err := filepath.Abs("testdata/i3.config")
if err != nil {
t.Fatal(err)
}
want := Version{
Major: 4,
Minor: 22,
Patch: 0,
LoadedConfigFileName: abs,
}
if diff := cmp.Diff(want, got); diff != "" {
t.Fatalf("unexpected GetVersion reply: (-want +got)\n%s", diff)
}
})
t.Run("AtLeast", func(t *testing.T) {
t.Parallel()
if err := AtLeast(4, 14); err != nil {
t.Errorf("AtLeast(4, 14) unexpectedly returned an error: %v", err)
}
if err := AtLeast(4, 0); err != nil {
t.Errorf("AtLeast(4, 0) unexpectedly returned an error: %v", err)
}
if err := AtLeast(4, 999); err == nil {
t.Errorf("AtLeast(4, 999) unexpectedly did not return an error")
}
})
t.Run("GetBarIDs", func(t *testing.T) {
t.Parallel()
got, err := GetBarIDs()
if err != nil {
t.Fatal(err)
}
want := []string{"bar-0"}
if diff := cmp.Diff(want, got); diff != "" {
t.Fatalf("unexpected GetBarIDs reply: (-want +got)\n%s", diff)
}
})
t.Run("GetBarConfig", func(t *testing.T) {
t.Parallel()
got, err := GetBarConfig("bar-0")
if err != nil {
t.Fatal(err)
}
want := BarConfig{
ID: "bar-0",
Mode: "dock",
Position: "bottom",
StatusCommand: "i3status",
Font: "fixed",
WorkspaceButtons: true,
BindingModeIndicator: true,
Colors: BarConfigColors{
Background: "#000000",
Statusline: "#ffffff",
Separator: "#666666",
FocusedBackground: "#000000",
FocusedStatusline: "#ffffff",
FocusedSeparator: "#666666",
FocusedWorkspaceText: "#4c7899",
FocusedWorkspaceBackground: "#285577",
FocusedWorkspaceBorder: "#ffffff",
ActiveWorkspaceText: "#333333",
ActiveWorkspaceBackground: "#5f676a",
ActiveWorkspaceBorder: "#ffffff",
InactiveWorkspaceText: "#333333",
InactiveWorkspaceBackground: "#222222",
InactiveWorkspaceBorder: "#888888",
UrgentWorkspaceText: "#2f343a",
UrgentWorkspaceBackground: "#900000",
UrgentWorkspaceBorder: "#ffffff",
BindingModeText: "#2f343a",
BindingModeBackground: "#900000",
BindingModeBorder: "#ffffff",
},
}
if diff := cmp.Diff(want, got); diff != "" {
t.Fatalf("unexpected GetBarConfig reply: (-want +got)\n%s", diff)
}
})
t.Run("GetBindingModes", func(t *testing.T) {
t.Parallel()
got, err := GetBindingModes()
if err != nil {
t.Fatal(err)
}
want := []string{"default"}
if diff := cmp.Diff(want, got); diff != "" {
t.Fatalf("unexpected GetBindingModes reply: (-want +got)\n%s", diff)
}
})
t.Run("GetMarks", func(t *testing.T) {
t.Parallel()
got, err := GetMarks()
if err != nil {
t.Fatal(err)
}
want := []string{"foo"}
if diff := cmp.Diff(want, got); diff != "" {
t.Fatalf("unexpected GetMarks reply: (-want +got)\n%s", diff)
}
})
t.Run("GetOutputs", func(t *testing.T) {
t.Parallel()
got, err := GetOutputs()
if err != nil {
t.Fatal(err)
}
want := []Output{
{
Name: "xroot-0",
Rect: Rect{Width: 1280, Height: 800},
},
{
Name: "screen",
Active: true,
CurrentWorkspace: "1",
Rect: Rect{Width: 1280, Height: 800},
},
}
if diff := cmp.Diff(want, got); diff != "" {
t.Fatalf("unexpected GetOutputs reply: (-want +got)\n%s", diff)
}
})
t.Run("GetWorkspaces", func(t *testing.T) {
t.Parallel()
got, err := GetWorkspaces()
if err != nil {
t.Fatal(err)
}
want := []Workspace{
{
Num: 1,
Name: "1",
Visible: true,
Focused: true,
Rect: Rect{Width: 1280, Height: 800},
Output: "screen",
},
}
cmpopts := []cmp.Option{
cmp.FilterPath(
func(p cmp.Path) bool {
return p.Last().String() == ".ID"
},
cmp.Ignore()),
}
if diff := cmp.Diff(want, got, cmpopts...); diff != "" {
t.Fatalf("unexpected GetWorkspaces reply: (-want +got)\n%s", diff)
}
})
t.Run("RunCommand", func(t *testing.T) {
t.Parallel()
got, err := RunCommand("norp")
if err != nil && !IsUnsuccessful(err) {
t.Fatal(err)
}
if !IsUnsuccessful(err) {
t.Fatalf("command unexpectedly succeeded")
}
if len(got) != 1 {
t.Fatalf("expected precisely one reply, got %+v", got)
}
if got, want := got[0].Success, false; got != want {
t.Errorf("CommandResult.Success: got %v, want %v", got, want)
}
if want := "Expected one of these tokens:"; !strings.HasPrefix(got[0].Error, want) {
t.Errorf("CommandResult.Error: unexpected error: got %q, want prefix %q", got[0].Error, want)
}
})
t.Run("GetConfig", func(t *testing.T) {
t.Parallel()
got, err := GetConfig()
if err != nil {
t.Fatal(err)
}
configBytes, err := ioutil.ReadFile("testdata/i3.config")
if err != nil {
t.Fatal(err)
}
configPath, err := filepath.Abs("testdata/i3.config")
if err != nil {
t.Fatal(err)
}
want := Config{
Config: string(configBytes),
IncludedConfigs: []IncludedConfig{
{
Path: configPath,
RawContents: string(configBytes),
// Our testdata configuration contains no variables,
// so this field contains configBytes as-is.
VariableReplacedContents: string(configBytes),
},
},
}
if diff := cmp.Diff(want, got); diff != "" {
t.Fatalf("unexpected GetConfig reply: (-want +got)\n%s", diff)
}
})
t.Run("GetTree", func(t *testing.T) {
t.Parallel()
got, err := GetTree()
if err != nil {
t.Fatal(err)
}
// Basic sanity checks:
if got.Root == nil {
t.Fatalf("tree.Root unexpectedly is nil")
}
if got, want := got.Root.Name, "root"; got != want {
t.Fatalf("unexpected tree root name: got %q, want %q", got, want)
}
// Exercise FindFocused to locate at least one workspace.
if node := got.Root.FindFocused(func(n *Node) bool { return n.Type == WorkspaceNode }); node == nil {
t.Fatalf("unexpectedly could not find any workspace node in GetTree reply")
}
// Exercise FindChild to locate at least one workspace.
if node := got.Root.FindChild(func(n *Node) bool { return n.Type == WorkspaceNode }); node == nil {
t.Fatalf("unexpectedly could not find any workspace node in GetTree reply")
}
})
}
func TestGoldens(t *testing.T) {
t.Parallel()
ctx, canc := context.WithCancel(context.Background())
defer canc()
_, DISPLAY, err := launchXvfb(ctx)
if err != nil {
t.Fatal(err)
}
cleanup, err := launchI3(ctx, DISPLAY, "")
if err != nil {
t.Fatal(err)
}
defer cleanup()
cmd := exec.Command(os.Args[0], "-test.run=TestGoldensSubprocess", "-test.v")
cmd.Env = []string{
"GO_WANT_XVFB=1",
"DISPLAY=" + DISPLAY,
"PATH=" + os.Getenv("PATH"),
}
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
t.Fatal(err.Error())
}
}