v0.2.0 13c6a90

launcher_test.go RawHistoryFolder

1package launcher
2
3import (
4	"context"
5	"os"
6	"path/filepath"
7	"reflect"
8	"slices"
9	"testing"
10	"time"
11
12	"github.com/bnema/neferwl/internal/logging"
13	"github.com/bnema/neferwl/internal/ports"
14)
15
16func TestChildEnv(t *testing.T) {
17	got := ChildEnv([]string{"TERM=x", "DISPLAY=:0", "WAYLAND_SOCKET=4", "WAYLAND_DISPLAY=old", "HOME=/home/me", "PATH=/bin", "LC_ALL=C", "XDG_RUNTIME_DIR=old", "XCURSOR_THEME=Adwaita", "XCURSOR_PATH=/icons", "SECRET=bad", "NOTIFY_SOCKET=/tmp/notify", "DBUS_SESSION_BUS_ADDRESS=unix:path=/run/me/bus"}, "wayland-7", "/run/me", 24)
18	want := []string{"DBUS_SESSION_BUS_ADDRESS=unix:path=/run/me/bus", "HOME=/home/me", "LC_ALL=C", "PATH=/bin", "WAYLAND_DISPLAY=wayland-7", "XCURSOR_PATH=/icons", "XCURSOR_SIZE=24", "XCURSOR_THEME=Adwaita", "XDG_CURRENT_DESKTOP=neferwl", "XDG_RUNTIME_DIR=/run/me", "XDG_SESSION_TYPE=wayland"}
19	if !reflect.DeepEqual(got, want) {
20		t.Fatalf("got %v want %v", got, want)
21	}
22	// The compositor's cursor size wins over the inherited one, valid or not.
23	for _, size := range []string{"", "32", "abc", "1000"} {
24		env := ChildEnv([]string{"XCURSOR_SIZE=" + size}, "w", "/r", 48)
25		if !slices.Contains(env, "XCURSOR_SIZE=48") {
26			t.Errorf("XCURSOR_SIZE=%q: %v", size, env)
27		}
28	}
29}
30
31func TestRun(t *testing.T) {
32	dir := t.TempDir()
33	file := filepath.Join(dir, "result")
34	ctx, cancel := context.WithCancel(context.Background())
35	done := make(chan error, 1)
36	reqs := make(chan ports.SpawnRequest, 3)
37	go func() {
38		done <- New([]string{"PATH=/bin", "FILE=" + file}, logging.For(ctx, "launcher")).Run(ctx, reqs)
39	}()
40	reqs <- ports.SpawnRequest{}
41	reqs <- ports.SpawnRequest{Argv: []string{"nonexistent-neferwl-binary"}}
42	// Env adds to the child environment (slot tokens).
43	reqs <- ports.SpawnRequest{Argv: []string{"sh", "-c", "echo ok$NEFERWL_SLOT > $FILE"}, Env: []string{"NEFERWL_SLOT=7"}}
44	deadline := time.After(3 * time.Second)
45	for {
46		data, err := os.ReadFile(file)
47		if err == nil {
48			if string(data) != "ok7\n" {
49				t.Fatalf("output: %q", data)
50			}
51			break
52		}
53		select {
54		case <-deadline:
55			t.Fatal("child did not write")
56		case <-time.After(10 * time.Millisecond):
57		}
58	}
59	cancel()
60	select {
61	case err := <-done:
62		if err != nil {
63			t.Fatal(err)
64		}
65	case <-time.After(time.Second):
66		t.Fatal("launcher did not stop")
67	}
68}
69
70func TestChildPath(t *testing.T) {
71	dir := t.TempDir()
72	name := "neferwl-child-only-script"
73	result := filepath.Join(dir, "result")
74	if err := os.WriteFile(filepath.Join(dir, name), []byte("#!/bin/sh\nprintf ok > \"$1\"\n"), 0700); err != nil {
75		t.Fatal(err)
76	}
77	ctx, cancel := context.WithCancel(context.Background())
78	defer cancel()
79	reqs := make(chan ports.SpawnRequest, 1)
80	done := make(chan error, 1)
81	go func() { done <- New([]string{"PATH=" + dir}, logging.For(ctx, "launcher")).Run(ctx, reqs) }()
82	reqs <- ports.SpawnRequest{Argv: []string{name, result}}
83	deadline := time.After(3 * time.Second)
84	for {
85		if data, err := os.ReadFile(result); err == nil {
86			if string(data) != "ok" {
87				t.Fatalf("result: %q", data)
88			}
89			break
90		}
91		select {
92		case <-deadline:
93			t.Fatal("child not started")
94		case <-time.After(10 * time.Millisecond):
95		}
96	}
97	cancel()
98	select {
99	case <-done:
100	case <-time.After(time.Second):
101		t.Fatal("launcher hung")
102	}
103}

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 23

Tags 10

↑↓ moveenter openesc close