v0.1.0 d9218d6

logging_test.go RawHistoryFolder

1package logging
2
3import (
4	"bytes"
5	"context"
6	"encoding/json"
7	"os"
8	"path/filepath"
9	"testing"
10	"time"
11)
12
13func TestParseDebug(t *testing.T) {
14	for _, tc := range []struct {
15		input     string
16		wantError bool
17		selected  []string
18	}{
19		{"", false, nil}, {"core,app", false, []string{"core", "app"}},
20		{"all", false, []string{"all"}}, {"unknown", true, nil}, {"core,", true, nil},
21		{"all,input-motion", false, []string{"all", "input-motion"}},
22	} {
23		got, err := ParseDebug(tc.input)
24		if (err != nil) != tc.wantError {
25			t.Fatalf("ParseDebug(%q): %v", tc.input, err)
26		}
27		for _, name := range tc.selected {
28			if !got[name] {
29				t.Errorf("ParseDebug(%q) missing %s", tc.input, name)
30			}
31		}
32	}
33}
34
35func TestComponentLevelsAndRotation(t *testing.T) {
36	t.Setenv("XDG_STATE_HOME", t.TempDir())
37	dir := filepath.Join(os.Getenv("XDG_STATE_HOME"), "neferwl", "runs")
38	path := filepath.Join(dir, "latest.log")
39	for run := 0; run < keepRuns+2; run++ {
40		time.Sleep(2 * time.Millisecond) // distinct millisecond timestamps
41		ctx, closeLog, err := Open(context.Background(), "info", "core")
42		if err != nil {
43			t.Fatal(err)
44		}
45		core := For(ctx, "core")
46		app := For(ctx, "app")
47		core.Debug().Msg("visible")
48		app.Debug().Msg("hidden")
49		app.Info().Msg("startup")
50		if err := closeLog(); err != nil {
51			t.Fatal(err)
52		}
53		data, err := os.ReadFile(path)
54		if err != nil {
55			t.Fatal(err)
56		}
57		lines := splitLines(data)
58		if len(lines) != 2 {
59			t.Fatalf("expected 2 entries, got %q", data)
60		}
61		for _, line := range lines {
62			var entry map[string]any
63			if err := json.Unmarshal(line, &entry); err != nil {
64				t.Fatal(err)
65			}
66			if entry["component"] != "core" && entry["component"] != "app" {
67				t.Fatalf("missing component: %v", entry)
68			}
69		}
70	}
71	runs, _ := filepath.Glob(filepath.Join(dir, "2*.log"))
72	if len(runs) != keepRuns {
73		t.Fatalf("kept %d runs, want %d", len(runs), keepRuns)
74	}
75}
76
77func splitLines(data []byte) [][]byte {
78	var lines [][]byte
79	for _, line := range bytes.Split(data, []byte("\n")) {
80		if len(line) > 0 {
81			lines = append(lines, line)
82		}
83	}
84	return lines
85}
86
87// A category alone raises its component to debug, so its lines are written.
88func TestCategoryEnablesComponentDebug(t *testing.T) {
89	t.Setenv("XDG_STATE_HOME", t.TempDir())
90	ctx, closeLog, err := Open(context.Background(), "info", "input-motion")
91	if err != nil {
92		t.Fatal(err)
93	}
94	input, core := For(ctx, "input"), For(ctx, "core")
95	input.Debug().Msg("motion")
96	core.Debug().Msg("hidden")
97	if err := closeLog(); err != nil {
98		t.Fatal(err)
99	}
100	data, err := os.ReadFile(filepath.Join(os.Getenv("XDG_STATE_HOME"), "neferwl", "runs", "latest.log"))
101	if err != nil {
102		t.Fatal(err)
103	}
104	if lines := splitLines(data); len(lines) != 1 || !bytes.Contains(lines[0], []byte(`"motion"`)) {
105		t.Fatalf("got %q", data)
106	}
107}
108
109// Narrow categories are on only when named, never through "all".
110func TestEnabledCategories(t *testing.T) {
111	for _, tc := range []struct {
112		debug string
113		want  bool
114	}{{"all", false}, {"input", false}, {"input-motion", true}, {"all,input-motion", true}} {
115		sel, err := ParseDebug(tc.debug)
116		if err != nil {
117			t.Fatal(err)
118		}
119		ctx := context.WithValue(context.Background(), debugKey{}, debugSet(sel))
120		if got := Enabled(ctx, "input-motion"); got != tc.want {
121			t.Errorf("%q: %v", tc.debug, got)
122		}
123	}
124}

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