v0.4.0 b6be3a8

workspaces_sensitive_test.go RawHistoryFolder

1package wayland
2
3import (
4	"testing"
5	"time"
6
7	ext "github.com/bnema/go-wayland-bindings/server/extworkspace"
8	"github.com/bnema/neferwl/internal/ports"
9	"github.com/bnema/wlturbo"
10)
11
12// protectedWorkspaceEvents is a real wire client recorder. Child inventory
13// proxies decode names as well as opcodes so unlock refresh is verified.
14type protectedWorkspaceEvents struct {
15	wlturbo.BaseProxy
16	client  *wlturbo.Display
17	handles []uint32
18	names   []string
19	events  []uint32
20}
21
22func (p *protectedWorkspaceEvents) Dispatch(e *wlturbo.Event) {
23	p.events = append(p.events, uint32(e.Opcode))
24	if e.Opcode != uint16(ext.ExtWorkspaceManagerV1EventWorkspaceGroup) && e.Opcode != uint16(ext.ExtWorkspaceManagerV1EventWorkspace) {
25		return
26	}
27	id := e.Uint32()
28	group := e.Opcode == uint16(ext.ExtWorkspaceManagerV1EventWorkspaceGroup)
29	child := &protectedWorkspaceChildEvents{owner: p, group: group}
30	child.SetID(id)
31	registerWireProxy(p.client, child)
32	if !group {
33		p.handles = append(p.handles, id)
34	}
35}
36
37type protectedWorkspaceChildEvents struct {
38	wlturbo.BaseProxy
39	owner *protectedWorkspaceEvents
40	group bool
41}
42
43func (p *protectedWorkspaceChildEvents) Dispatch(e *wlturbo.Event) {
44	kind := uint32(2)
45	if p.group {
46		kind = 1
47	}
48	p.owner.events = append(p.owner.events, kind<<8|uint32(e.Opcode))
49	if !p.group && e.Opcode == uint16(ext.ExtWorkspaceHandleV1EventName) {
50		p.owner.names = append(p.owner.names, e.String())
51	}
52}
53
54func bindProtectedWorkspaceRecorder(t *testing.T, c *wlturbo.Display) *protectedWorkspaceEvents {
55	t.Helper()
56	p := &protectedWorkspaceEvents{client: c}
57	p.SetID(bindProtocol(t, c, "ext_workspace_manager_v1"))
58	registerWireProxy(c, p)
59	roundtrip(t, c)
60	return p
61}
62
63func TestProtectedWorkspaceInventoryAndActivation(t *testing.T) {
64	s, events, _, dir := lifecycleServer(t)
65	state := installSecurity(t, s)
66	snapshot := ports.Workspaces{Outputs: []ports.WorkspaceOutput{{Name: "HEADLESS-1", Workspaces: []ports.WorkspaceInfo{{ID: 42, Name: "workspace-before", Active: true}}}}}
67	if !s.display.Do(func() { s.updateWorkspaces(snapshot) }) {
68		t.Fatal("display stopped")
69	}
70	existingClient := protocolClient(t, s, dir)
71	existing := bindProtectedWorkspaceRecorder(t, existingClient)
72	if len(existing.handles) != 1 || len(existing.names) != 1 || existing.names[0] != "workspace-before" {
73		t.Fatalf("unlocked inventory: %+v", existing)
74	}
75	handle := existing.handles[0]
76	existing.events, existing.names = nil, nil
77	// A pre-protection uncommitted activation is discarded by transition refresh.
78	requestProtocol(t, existingClient, handle, ext.ExtWorkspaceHandleV1RequestActivate)
79	roundtrip(t, existingClient)
80	protect(t, s, state, 1, true)
81	if !s.display.Do(func() { s.updateWorkspaceManagers(s.workspaceSnapshot) }) {
82		t.Fatal("display stopped")
83	}
84	snapshot.Outputs[0].Workspaces[0].Name = "workspace-current"
85	snapshot.Outputs[0].Workspaces[0].Active = false
86	snapshot.Outputs[0].Workspaces[0].Hidden = true
87	if !s.display.Do(func() { s.updateWorkspaces(snapshot) }) {
88		t.Fatal("display stopped")
89	}
90	lateClient := protocolClient(t, s, dir)
91	late := bindProtectedWorkspaceRecorder(t, lateClient)
92	roundtrip(t, existingClient)
93	if len(existing.events) != 0 || len(late.events) != 0 || len(late.handles) != 0 {
94		t.Fatalf("protected inventory: existing=%v late=%v", existing.events, late.events)
95	}
96	// Commit cannot admit pre-lock pending requests; requests made while locked
97	// must not linger and execute after unlock either.
98	requestProtocol(t, existingClient, existing.ID(), ext.ExtWorkspaceManagerV1RequestCommit)
99	requestProtocol(t, existingClient, handle, ext.ExtWorkspaceHandleV1RequestActivate)
100	roundtrip(t, existingClient)
101	select {
102	case ev := <-events:
103		t.Fatalf("protected activation emitted: %v", ev)
104	default:
105	}
106	protect(t, s, state, 2, false)
107	if !s.display.Do(func() { s.updateWorkspaceManagers(s.workspaceSnapshot) }) {
108		t.Fatal("display stopped")
109	}
110	roundtrip(t, existingClient, lateClient)
111	for _, p := range []*protectedWorkspaceEvents{existing, late} {
112		if len(p.names) != 1 || p.names[0] != "workspace-current" {
113			t.Fatalf("unlock names %v", p.names)
114		}
115	}
116	requestProtocol(t, existingClient, existing.ID(), ext.ExtWorkspaceManagerV1RequestCommit)
117	roundtrip(t, existingClient)
118	select {
119	case ev := <-events:
120		t.Fatalf("protected activation survived unlock: %v", ev)
121	default:
122	}
123	requestProtocol(t, existingClient, handle, ext.ExtWorkspaceHandleV1RequestActivate)
124	requestProtocol(t, existingClient, existing.ID(), ext.ExtWorkspaceManagerV1RequestCommit)
125	roundtrip(t, existingClient)
126	select {
127	case ev := <-events:
128		activation, ok := ev.(ports.WorkspaceActivate)
129		if !ok || len(activation.IDs) != 1 || activation.IDs[0] != 42 {
130			t.Fatalf("unlocked activation: %v", ev)
131		}
132	case <-time.After(2 * time.Second):
133		t.Fatal("unlocked activation not emitted")
134	}
135}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 25

Tags 10

↑↓ moveenter openesc close