main 98594d0

limits_test.go RawHistoryFolder

1// SPDX-License-Identifier: MIT
2
3package template
4
5import (
6	"math"
7	"strings"
8	"testing"
9)
10
11// The options of Context that the C# runner's loader needs (package action):
12// nesting limit, error limit and the runner's YAML restrictions.
13
14func readWith(t *testing.T, set func(*Context), src string) []string {
15	t.Helper()
16	schema, err := WorkflowSchema()
17	if err != nil {
18		t.Fatal(err)
19	}
20	ctx := NewContext(schema)
21	set(ctx)
22	ReadYAML(ctx, "workflow-root-strict", "f.yml", []byte(src))
23	return ctx.Messages()
24}
25
26func TestMaxDepth(t *testing.T) {
27	nest := func(n int) string { return "x: " + strings.Repeat("[", n) + strings.Repeat("]", n) + "\n" }
28	set := func(c *Context) { c.MaxDepth = 10 }
29	// Like TemplateMemory.IncrementDepth: the root mapping is level one, so
30	// nine brackets fit and ten do not. The unexpected key "x" is skipped, and
31	// skipping counts levels too.
32	got := readWith(t, set, nest(9))
33	for _, m := range got {
34		if strings.Contains(m, "depth") {
35			t.Errorf("9 brackets: %q", got)
36		}
37	}
38	got = readWith(t, set, nest(10))
39	if len(got) == 0 || got[len(got)-1] != "f.yml: Maximum object depth exceeded" {
40		t.Errorf("10 brackets: %q", got)
41	}
42	// No limit by default.
43	for _, m := range readWith(t, func(*Context) {}, nest(500)) {
44		if strings.Contains(m, "depth") {
45			t.Errorf("default limit: %q", m)
46		}
47	}
48}
49
50func TestErrorLimit(t *testing.T) {
51	var b strings.Builder
52	for i := 0; i < 30; i++ {
53		b.WriteString("k" + string(rune('a'+i%26)) + string(rune('a'+i/26)) + ": 1\n")
54	}
55	if got := readWith(t, func(c *Context) { c.ErrorLimit = 10 }, b.String()); len(got) != 10 {
56		t.Errorf("%d errors with a limit of 10", len(got))
57	}
58	if got := readWith(t, func(*Context) {}, b.String()); len(got) < 30 {
59		t.Errorf("%d errors without a limit", len(got))
60	}
61}
62
63func TestRunnerYAML(t *testing.T) {
64	runner := func(c *Context) { c.RunnerYAML = true }
65	tests := []struct {
66		name, src, want string
67	}{
68		{"anchor", "on: &a push\n", "f.yml: Anchors are not currently supported. Remove the anchor 'a'"},
69		{"alias", "a: &a [1]\nb: *a\n", "f.yml: Anchors are not currently supported. Remove the anchor 'a'"},
70		{"!!str is unknown to the runner", "on: !!str push\n", "f.yml: Unexpected tag 'tag:yaml.org,2002:str'"},
71		{"!!int on a string", "on: !!int push\n", "f.yml: The value 'push' on line 1 and column 5 is invalid for the type 'tag:yaml.org,2002:int'"},
72		{"tag on a quoted scalar", "on: !!int '1'\n", "f.yml: The scalar style 'SingleQuoted' on line 1 and column 5 is not valid with the tag 'tag:yaml.org,2002:int'"},
73		{"second document", "on: push\n---\non: pull_request\n", "f.yml: Expected stream end parse event"},
74		{"empty", "", "f.yml: Expected document start parse event"},
75		{"collection key", "? [a]\n: b\n", "f.yml: Expected mapping end"},
76	}
77	for _, tc := range tests {
78		got := readWith(t, runner, tc.src)
79		found := false
80		for _, m := range got {
81			found = found || m == tc.want
82		}
83		if !found {
84			t.Errorf("%s: %q, want %q", tc.name, got, tc.want)
85		}
86		// None of this is an error without the option (except an empty file
87		// and a collection key, which have other messages).
88	}
89	for _, src := range []string{"on: &a push\n", "on: !!str push\n", "on: !!int 1\n"} {
90		for _, m := range readWith(t, func(*Context) {}, src) {
91			if strings.Contains(m, "Anchors") || strings.Contains(m, "Unexpected tag") {
92				t.Errorf("%q without RunnerYAML: %q", src, m)
93			}
94		}
95	}
96	// A valid tag passes.
97	for _, src := range []string{"on: !!null\n", "on: !!int 12\n", "on: !!float 1.5\n", "on: !!bool true\n", "on: !!int 0x1F\n"} {
98		for _, m := range readWith(t, runner, src) {
99			if strings.Contains(m, "tag") || strings.Contains(m, "invalid for the type") {
100				t.Errorf("%q: %q", src, m)
101			}
102		}
103	}
104}
105
106func TestExceptionError(t *testing.T) {
107	schema, _ := WorkflowSchema()
108	ctx := NewContext(schema)
109	ctx.ErrorException("System.ArgumentNullException", "msg")
110	ctx.ErrorException("System.ArgumentOutOfRangeException", "msg")
111	ctx.ErrorException("System.NotSupportedException", "msg")
112	want := []string{
113		"System.ArgumentNullException: Value cannot be null. (Parameter 'msg')",
114		"System.ArgumentOutOfRangeException: Specified argument was out of the range of valid values. (Parameter 'msg')",
115		"System.NotSupportedException: msg",
116	}
117	got := ctx.Messages()
118	if strings.Join(got, "\n") != strings.Join(want, "\n") {
119		t.Errorf("got %q", got)
120	}
121}
122
123func TestDotNetNumberString(t *testing.T) {
124	for _, tc := range []struct {
125		in   float64
126		want string
127	}{
128		{0, "0"}, {1, "1"}, {1.5, "1.5"}, {-5, "-5"}, {0.1, "0.1"}, {0.30000000000000004, "0.3"},
129		{100000, "100000"}, {1e14, "100000000000000"}, {1e15, "1E+15"}, {123456789012345678, "1.23456789012346E+17"},
130		{1e20, "1E+20"}, {1e21, "1E+21"}, {0.0001, "0.0001"}, {0.00001, "1E-05"}, {1e-6, "1E-06"}, {1.5e-7, "1.5E-07"},
131		{12345.6789, "12345.6789"}, {999999999999999, "999999999999999"}, {9999999999999999, "1E+16"},
132	} {
133		if got := DotNetNumberString(tc.in); got != tc.want {
134			t.Errorf("DotNetNumberString(%v) = %q, want %q", tc.in, got, tc.want)
135		}
136	}
137	if got := DotNetNumberString(math.Copysign(0, -1)); got != "-0" {
138		t.Errorf("negative zero: %q", got)
139	}
140	if DotNetNumberString(math.Inf(1)) != "Infinity" || DotNetNumberString(math.Inf(-1)) != "-Infinity" || DotNetNumberString(math.NaN()) != "NaN" {
141		t.Error("non-finite numbers")
142	}
143}

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 1

Tags 3

↑↓ moveenter openesc close