v2.7.0 fac7705

parse_test.go RawHistoryFolder

1package duration
2
3import (
4	"testing"
5	"time"
6)
7
8func TestParse(t *testing.T) {
9	tests := []struct {
10		name    string
11		input   string
12		want    time.Duration
13		wantErr bool
14	}{
15		// Single human-friendly units
16		{name: "1 day", input: "1d", want: Day},
17		{name: "2 days", input: "2d", want: 2 * Day},
18		{name: "1 week", input: "1w", want: Week},
19		{name: "2 weeks", input: "2w", want: 2 * Week},
20		{name: "1 month", input: "1M", want: Month},
21		{name: "3 months", input: "3M", want: 3 * Month},
22		{name: "1 year", input: "1y", want: Year},
23		{name: "2 years", input: "2y", want: 2 * Year},
24
25		// Compound human-friendly units
26		{name: "1 year 6 months", input: "1y6M", want: Year + 6*Month},
27		{name: "2 weeks 3 days", input: "2w3d", want: 2*Week + 3*Day},
28		{name: "1 year 2 months 1 week", input: "1y2M1w", want: Year + 2*Month + Week},
29
30		// Mixed with standard Go units
31		{name: "1 day 12 hours", input: "1d12h", want: Day + 12*time.Hour},
32		{name: "2 weeks 6 hours", input: "2w6h", want: 2*Week + 6*time.Hour},
33		{name: "1 month 1 day 1 hour", input: "1M1d1h", want: Month + Day + time.Hour},
34		{name: "1 year 30 minutes", input: "1y30m", want: Year + 30*time.Minute},
35
36		// Standard Go duration units (fallback)
37		{name: "24 hours", input: "24h", want: 24 * time.Hour},
38		{name: "30 minutes", input: "30m", want: 30 * time.Minute},
39		{name: "1 hour 30 minutes", input: "1h30m", want: time.Hour + 30*time.Minute},
40		{name: "1 second", input: "1s", want: time.Second},
41		{name: "500 milliseconds", input: "500ms", want: 500 * time.Millisecond},
42		{name: "1000 microseconds", input: "1000us", want: 1000 * time.Microsecond},
43		{name: "1000000 nanoseconds", input: "1000000ns", want: 1000000 * time.Nanosecond},
44
45		// Special cases
46		{name: "zero duration", input: "0", want: 0},
47		{name: "zero with unit", input: "0d", want: 0},
48		{name: "zero hours", input: "0h", want: 0},
49
50		// Large values
51		{name: "10 years", input: "10y", want: 10 * Year},
52		{name: "52 weeks", input: "52w", want: 52 * Week},
53		{name: "365 days", input: "365d", want: 365 * Day},
54
55		// Edge cases with whitespace (should be trimmed)
56		{name: "whitespace around", input: "  1d  ", want: Day},
57
58		// Error cases
59		{name: "empty string", input: "", wantErr: true},
60		{name: "invalid format", input: "abc", wantErr: true},
61		{name: "invalid unit", input: "1x", wantErr: true},
62		{name: "missing value", input: "d", wantErr: true},
63		{name: "negative not supported by Go", input: "-1d", wantErr: true},
64	}
65
66	for _, tt := range tests {
67		t.Run(tt.name, func(t *testing.T) {
68			got, err := Parse(tt.input)
69			if (err != nil) != tt.wantErr {
70				t.Errorf("Parse(%q) error = %v, wantErr %v", tt.input, err, tt.wantErr)
71				return
72			}
73			if !tt.wantErr && got != tt.want {
74				t.Errorf("Parse(%q) = %v, want %v", tt.input, got, tt.want)
75			}
76		})
77	}
78}
79
80func TestParseConstants(t *testing.T) {
81	// Verify our constants are correct
82	if Day != 24*time.Hour {
83		t.Errorf("Day = %v, want %v", Day, 24*time.Hour)
84	}
85	if Week != 7*24*time.Hour {
86		t.Errorf("Week = %v, want %v", Week, 7*24*time.Hour)
87	}
88	if Month != 30*24*time.Hour {
89		t.Errorf("Month = %v, want %v", Month, 30*24*time.Hour)
90	}
91	if Year != 365*24*time.Hour {
92		t.Errorf("Year = %v, want %v", Year, 365*24*time.Hour)
93	}
94}
95
96func TestParseEquivalences(t *testing.T) {
97	// Test that our human-friendly units produce expected equivalences
98	tests := []struct {
99		name  string
100		human string
101		goStd string
102		hours int
103	}{
104		{name: "1 day = 24 hours", human: "1d", goStd: "24h", hours: 24},
105		{name: "1 week = 168 hours", human: "1w", goStd: "168h", hours: 168},
106		{name: "1 month = 720 hours", human: "1M", goStd: "720h", hours: 720},
107		{name: "1 year = 8760 hours", human: "1y", goStd: "8760h", hours: 8760},
108	}
109
110	for _, tt := range tests {
111		t.Run(tt.name, func(t *testing.T) {
112			humanDur, err := Parse(tt.human)
113			if err != nil {
114				t.Fatalf("Parse(%q) error = %v", tt.human, err)
115			}
116
117			goDur, err := time.ParseDuration(tt.goStd)
118			if err != nil {
119				t.Fatalf("time.ParseDuration(%q) error = %v", tt.goStd, err)
120			}
121
122			if humanDur != goDur {
123				t.Errorf("Parse(%q) = %v, want %v (from %q)", tt.human, humanDur, goDur, tt.goStd)
124			}
125
126			expectedHours := time.Duration(tt.hours) * time.Hour
127			if humanDur != expectedHours {
128				t.Errorf("Parse(%q) = %v, want %d hours", tt.human, humanDur, tt.hours)
129			}
130		})
131	}
132}

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 7

Tags 106

↑↓ moveenter openesc close