175 lines
5.8 KiB
Go
175 lines
5.8 KiB
Go
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
package wine
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"git.stormux.org/storm/cthulhu/internal/atspi"
|
|
"github.com/godbus/dbus/v5"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestReviewRegistryRetiresInactiveWindowTrees(t *testing.T) {
|
|
r := readyReviewRegistry(t)
|
|
n := registerNotice(t, r, ":1.2")
|
|
r.captureCall = func(_ context.Context, _ string, _ dbus.ObjectPath, notice ReviewFocusNotice) (map[string]dbus.Variant, error) {
|
|
f := reviewWire()
|
|
f["window"] = dbus.MakeVariant(notice.Window)
|
|
f["revision"] = dbus.MakeVariant(notice.Revision)
|
|
nodes := f["nodes"].Value().([]map[string]dbus.Variant)
|
|
nodes[0]["id"] = dbus.MakeVariant(notice.Window)
|
|
for _, node := range nodes[1:] {
|
|
if node["parent"].Value() == "window" {
|
|
node["parent"] = dbus.MakeVariant(notice.Window)
|
|
}
|
|
}
|
|
return f, nil
|
|
}
|
|
for i := 1; i <= 32; i++ {
|
|
n.Window = fmt.Sprintf("window%d", i)
|
|
n.Revision = uint64(i)
|
|
if !r.PresentFocus(n.Sender, n) {
|
|
t.Fatal("focus rejected")
|
|
}
|
|
one, err := r.Capture(context.Background(), n.ID())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
two, err := r.Capture(context.Background(), n.ID())
|
|
if err != nil || one != two {
|
|
t.Fatal("active window tree is not stable")
|
|
}
|
|
if len(r.providers[n.Sender].trees) != 1 {
|
|
t.Fatalf("retained %d inactive trees", len(r.providers[n.Sender].trees)-1)
|
|
}
|
|
}
|
|
}
|
|
|
|
func registryNotice(sender string) ReviewFocusNotice {
|
|
return ReviewFocusNotice{Sender: sender, Path: ReviewObjectPath, Instance: "helper-instance", Window: "window", Focus: "input", Revision: 1, Title: "same title", Role: "window", Bounds: atspi.Rectangle{X: 10, Y: 20, Width: 400, Height: 300}}
|
|
}
|
|
func readyReviewRegistry(t *testing.T) *ReviewRegistry {
|
|
t.Helper()
|
|
registry := NewReviewRegistry(nil)
|
|
// Unit tests substitute transport only; registration still requires lifecycle.
|
|
registry.ctx, registry.cancel = context.WithCancel(context.Background())
|
|
t.Cleanup(func() { registry.Stop(context.Background()) })
|
|
return registry
|
|
}
|
|
func registerNotice(t *testing.T, r *ReviewRegistry, sender string) ReviewFocusNotice {
|
|
t.Helper()
|
|
notice := registryNotice(sender)
|
|
if !r.RegisterProvider(sender, ReviewObjectPath, 1) || !r.PresentFocus(sender, notice) {
|
|
t.Fatal("registration rejected")
|
|
}
|
|
return notice
|
|
}
|
|
func TestReviewRegistryTwoProviders(t *testing.T) {
|
|
r := readyReviewRegistry(t)
|
|
one := registerNotice(t, r, ":1.2")
|
|
two := registerNotice(t, r, ":1.3")
|
|
if one.ID() == two.ID() || len(r.Focuses()) != 2 || !r.Current(one.ID()) || !r.Current(two.ID()) {
|
|
t.Fatal("providers collided")
|
|
}
|
|
if len(r.changes) != 1 {
|
|
t.Fatal("notifications are not coalesced")
|
|
}
|
|
if !r.RegisterProvider(one.Sender, ReviewObjectPath, 1) || r.Current(one.ID()) || len(r.Focuses()) != 1 {
|
|
t.Fatal("registration retained old lifetime")
|
|
}
|
|
}
|
|
func TestReviewRegistrySenderAssociation(t *testing.T) {
|
|
r := readyReviewRegistry(t)
|
|
n := registryNotice(":1.2")
|
|
if r.PresentFocus(n.Sender, n) || r.RegisterProvider("well.known", ReviewObjectPath, 1) || r.RegisterProvider(n.Sender, ReviewObjectPath, 2) || r.RegisterProvider(n.Sender, "/wrong", 1) {
|
|
t.Fatal("invalid provider accepted")
|
|
}
|
|
registerNotice(t, r, n.Sender)
|
|
n.Sender = ":1.99"
|
|
n.Path = "/forged"
|
|
if !r.PresentFocus(":1.2", n) {
|
|
t.Fatal("actual sender rejected")
|
|
}
|
|
got := r.Focuses()[0]
|
|
if got.Sender != ":1.2" || got.Path != ReviewObjectPath {
|
|
t.Fatal("payload forged destination")
|
|
}
|
|
n.Revision = 0
|
|
if r.PresentFocus(":1.2", n) {
|
|
t.Fatal("invalid focus accepted")
|
|
}
|
|
}
|
|
func TestReviewRegistryProviderDisappears(t *testing.T) {
|
|
r := readyReviewRegistry(t)
|
|
n := registerNotice(t, r, ":1.2")
|
|
r.disappeared(n.Sender)
|
|
if r.Current(n.ID()) || len(r.Focuses()) != 0 {
|
|
t.Fatal("dead provider retained")
|
|
}
|
|
if _, err := r.Capture(context.Background(), n.ID()); !errors.Is(err, ErrReviewUnavailable) {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
func TestReviewCaptureDeadlineAndStaleReply(t *testing.T) {
|
|
r := readyReviewRegistry(t)
|
|
n := registerNotice(t, r, ":1.2")
|
|
calls := 0
|
|
r.captureCall = func(ctx context.Context, sender string, path dbus.ObjectPath, notice ReviewFocusNotice) (map[string]dbus.Variant, error) {
|
|
calls++
|
|
fields := reviewWire()
|
|
if calls == 1 {
|
|
fields["status"] = dbus.MakeVariant("stale")
|
|
fields["nodes"] = dbus.MakeVariant([]map[string]dbus.Variant{})
|
|
}
|
|
return fields, nil
|
|
}
|
|
tree, err := r.Capture(context.Background(), n.ID())
|
|
if err != nil || tree == nil || calls != 2 {
|
|
t.Fatalf("stale retry: %v calls=%d", err, calls)
|
|
}
|
|
again, err := r.Capture(context.Background(), n.ID())
|
|
if err != nil || again != tree {
|
|
t.Fatal("capture replaced live tree")
|
|
}
|
|
r.captureCall = func(ctx context.Context, _ string, _ dbus.ObjectPath, _ ReviewFocusNotice) (map[string]dbus.Variant, error) {
|
|
<-ctx.Done()
|
|
return nil, ctx.Err()
|
|
}
|
|
started := time.Now()
|
|
if _, err = r.Capture(context.Background(), n.ID()); err == nil || time.Since(started) > 750*time.Millisecond {
|
|
t.Fatalf("deadline: %v", err)
|
|
}
|
|
r.captureCall = func(context.Context, string, dbus.ObjectPath, ReviewFocusNotice) (map[string]dbus.Variant, error) {
|
|
f := reviewWire()
|
|
f["revision"] = dbus.MakeVariant(uint64(2))
|
|
return f, nil
|
|
}
|
|
if _, err = r.Capture(context.Background(), n.ID()); !errors.Is(err, ErrReviewStale) {
|
|
t.Fatalf("wrong source: %v", err)
|
|
}
|
|
}
|
|
func TestReviewCaptureStopCancels(t *testing.T) {
|
|
r := readyReviewRegistry(t)
|
|
n := registerNotice(t, r, ":1.2")
|
|
entered := make(chan struct{})
|
|
done := make(chan error, 1)
|
|
r.captureCall = func(ctx context.Context, _ string, _ dbus.ObjectPath, _ ReviewFocusNotice) (map[string]dbus.Variant, error) {
|
|
close(entered)
|
|
<-ctx.Done()
|
|
return nil, ctx.Err()
|
|
}
|
|
go func() { _, err := r.Capture(context.Background(), n.ID()); done <- err }()
|
|
<-entered
|
|
r.Stop(context.Background())
|
|
select {
|
|
case err := <-done:
|
|
if err == nil {
|
|
t.Fatal("cancelled capture accepted")
|
|
}
|
|
case <-time.After(time.Second):
|
|
t.Fatal("stop blocked")
|
|
}
|
|
}
|