300 lines
7.5 KiB
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())
|
||
|
}
|
||
|
}
|