187 lines
4.0 KiB
Go
187 lines
4.0 KiB
Go
package i3
|
||
|
||
import (
|
||
"context"
|
||
"fmt"
|
||
"os"
|
||
"os/exec"
|
||
"sync"
|
||
"testing"
|
||
"time"
|
||
|
||
"golang.org/x/sync/errgroup"
|
||
)
|
||
|
||
// TestSubscribeSubprocess runs in a process which has been started with
|
||
// DISPLAY= pointing to an Xvfb instance with i3 -c testdata/i3.config running.
|
||
func TestSubscribeSubprocess(t *testing.T) {
|
||
if os.Getenv("GO_WANT_XVFB") != "1" {
|
||
t.Skip("parent process")
|
||
}
|
||
|
||
// TODO(https://github.com/i3/i3/issues/2988): as soon as we are targeting
|
||
// i3 4.15, use SendTick to eliminate race conditions in this test.
|
||
|
||
t.Run("subscribe", func(t *testing.T) {
|
||
var eg errgroup.Group
|
||
ws := Subscribe(WorkspaceEventType)
|
||
received := make(chan Event)
|
||
eg.Go(func() error {
|
||
defer close(received)
|
||
if ws.Next() {
|
||
received <- ws.Event()
|
||
}
|
||
return ws.Close()
|
||
})
|
||
// As we can’t know when EventReceiver.Next() actually subscribes, we
|
||
// just continuously switch workspaces.
|
||
ctx, canc := context.WithCancel(context.Background())
|
||
defer canc()
|
||
go func() {
|
||
cnt := 2
|
||
for ctx.Err() == nil {
|
||
RunCommand(fmt.Sprintf("workspace %d", cnt))
|
||
cnt++
|
||
time.Sleep(10 * time.Millisecond)
|
||
}
|
||
}()
|
||
select {
|
||
case <-received:
|
||
case <-time.After(5 * time.Second):
|
||
t.Fatalf("timeout waiting for an event from i3")
|
||
}
|
||
if err := eg.Wait(); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
})
|
||
|
||
t.Run("subscribeParallel", func(t *testing.T) {
|
||
var mu sync.Mutex
|
||
received := make(map[string]int)
|
||
|
||
recv1 := Subscribe(WorkspaceEventType)
|
||
go func() {
|
||
for recv1.Next() {
|
||
ev := recv1.Event().(*WorkspaceEvent)
|
||
if ev.Change == "init" {
|
||
mu.Lock()
|
||
received[ev.Current.Name]++
|
||
mu.Unlock()
|
||
}
|
||
}
|
||
}()
|
||
|
||
recv2 := Subscribe(WorkspaceEventType)
|
||
go func() {
|
||
for recv2.Next() {
|
||
ev := recv2.Event().(*WorkspaceEvent)
|
||
if ev.Change == "init" {
|
||
mu.Lock()
|
||
received[ev.Current.Name]++
|
||
mu.Unlock()
|
||
}
|
||
}
|
||
}()
|
||
|
||
cnt := 2
|
||
start := time.Now()
|
||
for time.Since(start) < 5*time.Second {
|
||
mu.Lock()
|
||
done := received[fmt.Sprintf("%d", cnt-1)] == 2
|
||
mu.Unlock()
|
||
if done {
|
||
return // success
|
||
}
|
||
if _, err := RunCommand(fmt.Sprintf("workspace %d", cnt)); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
cnt++
|
||
time.Sleep(10 * time.Millisecond)
|
||
}
|
||
})
|
||
|
||
t.Run("subscribeMultiple", func(t *testing.T) {
|
||
var eg errgroup.Group
|
||
ws := Subscribe(WorkspaceEventType, ModeEventType)
|
||
received := make(chan struct{})
|
||
eg.Go(func() error {
|
||
defer close(received)
|
||
defer ws.Close()
|
||
seen := map[EventType]bool{
|
||
WorkspaceEventType: false,
|
||
ModeEventType: false,
|
||
}
|
||
Outer:
|
||
for ws.Next() {
|
||
switch ws.Event().(type) {
|
||
case *WorkspaceEvent:
|
||
seen[WorkspaceEventType] = true
|
||
case *ModeEvent:
|
||
seen[ModeEventType] = true
|
||
}
|
||
|
||
for _, seen := range seen {
|
||
if !seen {
|
||
continue Outer
|
||
}
|
||
}
|
||
|
||
return nil
|
||
}
|
||
return ws.Close()
|
||
})
|
||
// As we can’t know when EventReceiver.Next() actually subscribes, we
|
||
// just continuously switch workspaces and modes.
|
||
ctx, canc := context.WithCancel(context.Background())
|
||
defer canc()
|
||
go func() {
|
||
modes := []string{"default", "conf"}
|
||
cnt := 2
|
||
for ctx.Err() == nil {
|
||
RunCommand(fmt.Sprintf("workspace %d", cnt))
|
||
cnt++
|
||
RunCommand(fmt.Sprintf("mode %s", modes[cnt%len(modes)]))
|
||
time.Sleep(10 * time.Millisecond)
|
||
}
|
||
}()
|
||
select {
|
||
case <-received:
|
||
case <-time.After(5 * time.Second):
|
||
t.Fatalf("timeout waiting for an event from i3")
|
||
}
|
||
if err := eg.Wait(); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
})
|
||
}
|
||
|
||
func TestSubscribe(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=TestSubscribeSubprocess", "-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())
|
||
}
|
||
}
|