1
2
3
4 package toml_test
5
6 import (
7 "errors"
8 "fmt"
9 "io/fs"
10 "math"
11 "strings"
12 "testing"
13 "time"
14
15 "github.com/BurntSushi/toml"
16 tomltest "github.com/BurntSushi/toml/internal/toml-test"
17 )
18
19 func TestErrorPosition(t *testing.T) {
20
21 tests := []struct {
22 test, err string
23 }{
24 {"array/missing-separator.toml", `
25 toml: error: expected a comma (',') or array terminator (']'), but got '2'
26
27 At line 1, column 13:
28
29 1 | wrong = [ 1 2 3 ]
30 ^`},
31
32 {"array/no-close-2.toml", `
33 toml: error: expected a comma (',') or array terminator (']'), but got end of file
34
35 At line 1, column 10:
36
37 1 | x = [42 #
38 ^`},
39
40 {"array/tables-2.toml", `
41 toml: error: Key 'fruit.variety' has already been defined.
42
43 At line 9, column 3-8:
44
45 7 |
46 8 | # This table conflicts with the previous table
47 9 | [fruit.variety]
48 ^^^^^`},
49 {"datetime/trailing-t.toml", `
50 toml: error: Invalid TOML Datetime: "2006-01-30T".
51
52 At line 2, column 4-15:
53
54 1 | # Date cannot end with trailing T
55 2 | d = 2006-01-30T
56 ^^^^^^^^^^^`},
57 }
58
59 fsys := tomltest.EmbeddedTests()
60 for _, tt := range tests {
61 t.Run(tt.test, func(t *testing.T) {
62 input, err := fs.ReadFile(fsys, "invalid/"+tt.test)
63 if err != nil {
64 t.Fatal(err)
65 }
66
67 var x interface{}
68 _, err = toml.Decode(string(input), &x)
69 if err == nil {
70 t.Fatal("err is nil")
71 }
72
73 var pErr toml.ParseError
74 if !errors.As(err, &pErr) {
75 t.Errorf("err is not a ParseError: %T %[1]v", err)
76 }
77
78 tt.err = tt.err[1:] + "\n"
79 want := pErr.ErrorWithUsage()
80
81 if !strings.Contains(want, tt.err) {
82 t.Fatalf("\nwant:\n%s\nhave:\n%s", tt.err, want)
83 }
84 })
85 }
86 }
87
88 func TestParseError(t *testing.T) {
89 tests := []struct {
90 in interface{}
91 toml, err string
92 }{
93 {
94 &struct{ Int int8 }{},
95 "Int = 200",
96 `| toml: error: 200 is out of range for int8
97 |
98 | At line 1, column 6-9:
99 |
100 | 1 | Int = 200
101 | ^^^
102 | Error help:
103 |
104 | This number is too large; this may be an error in the TOML, but it can also be a
105 | bug in the program that uses too small of an integer.
106 |
107 | The maximum and minimum values are:
108 |
109 | size │ lowest │ highest
110 | ───────┼────────────────┼──────────
111 | int8 │ -128 │ 127
112 | int16 │ -32,768 │ 32,767
113 | int32 │ -2,147,483,648 │ 2,147,483,647
114 | int64 │ -9.2 × 10¹⁷ │ 9.2 × 10¹⁷
115 | uint8 │ 0 │ 255
116 | uint16 │ 0 │ 65535
117 | uint32 │ 0 │ 4294967295
118 | uint64 │ 0 │ 1.8 × 10¹⁸
119 |
120 | int refers to int32 on 32-bit systems and int64 on 64-bit systems.
121 `,
122 },
123 {
124 &struct{ Int int }{},
125 fmt.Sprintf("Int = %d", uint64(math.MaxInt64+1)),
126 `| toml: error: 9223372036854775808 is out of range for int64
127 |
128 | At line 1, column 6-25:
129 |
130 | 1 | Int = 9223372036854775808
131 | ^^^^^^^^^^^^^^^^^^^
132 | Error help:
133 |
134 | This number is too large; this may be an error in the TOML, but it can also be a
135 | bug in the program that uses too small of an integer.
136 |
137 | The maximum and minimum values are:
138 |
139 | size │ lowest │ highest
140 | ───────┼────────────────┼──────────
141 | int8 │ -128 │ 127
142 | int16 │ -32,768 │ 32,767
143 | int32 │ -2,147,483,648 │ 2,147,483,647
144 | int64 │ -9.2 × 10¹⁷ │ 9.2 × 10¹⁷
145 | uint8 │ 0 │ 255
146 | uint16 │ 0 │ 65535
147 | uint32 │ 0 │ 4294967295
148 | uint64 │ 0 │ 1.8 × 10¹⁸
149 |
150 | int refers to int32 on 32-bit systems and int64 on 64-bit systems.
151 `,
152 },
153 {
154 &struct{ Float float32 }{},
155 "Float = 1.1e99",
156 `
157 | toml: error: 1.1e+99 is out of range for float32
158 |
159 | At line 1, column 8-14:
160 |
161 | 1 | Float = 1.1e99
162 | ^^^^^^
163 | Error help:
164 |
165 | This number is too large; this may be an error in the TOML, but it can also be a
166 | bug in the program that uses too small of an integer.
167 |
168 | The maximum and minimum values are:
169 |
170 | size │ lowest │ highest
171 | ───────┼────────────────┼──────────
172 | int8 │ -128 │ 127
173 | int16 │ -32,768 │ 32,767
174 | int32 │ -2,147,483,648 │ 2,147,483,647
175 | int64 │ -9.2 × 10¹⁷ │ 9.2 × 10¹⁷
176 | uint8 │ 0 │ 255
177 | uint16 │ 0 │ 65535
178 | uint32 │ 0 │ 4294967295
179 | uint64 │ 0 │ 1.8 × 10¹⁸
180 |
181 | int refers to int32 on 32-bit systems and int64 on 64-bit systems.
182 `,
183 },
184
185 {
186 &struct{ D time.Duration }{},
187 `D = "99 bottles"`,
188 `
189 | toml: error: invalid duration: "99 bottles"
190 |
191 | At line 1, column 5-15:
192 |
193 | 1 | D = "99 bottles"
194 | ^^^^^^^^^^
195 | Error help:
196 |
197 | A duration must be as "number<unit>", without any spaces. Valid units are:
198 |
199 | ns nanoseconds (billionth of a second)
200 | us, µs microseconds (millionth of a second)
201 | ms milliseconds (thousands of a second)
202 | s seconds
203 | m minutes
204 | h hours
205 |
206 | You can combine multiple units; for example "5m10s" for 5 minutes and 10
207 | seconds.
208 `,
209 },
210 }
211
212 prep := func(s string) string {
213 lines := strings.Split(strings.TrimSpace(s), "\n")
214 for i := range lines {
215 if j := strings.IndexByte(lines[i], '|'); j >= 0 {
216 lines[i] = lines[i][j+1:]
217 lines[i] = strings.Replace(lines[i], " ", "", 1)
218 }
219 }
220 return strings.Join(lines, "\n") + "\n"
221 }
222
223 for _, tt := range tests {
224 t.Run("", func(t *testing.T) {
225 _, err := toml.Decode(tt.toml, tt.in)
226 if err == nil {
227 t.Fatalf("err is nil; decoded: %#v", tt.in)
228 }
229
230 var pErr toml.ParseError
231 if !errors.As(err, &pErr) {
232 t.Fatalf("err is not a ParseError: %#v", err)
233 }
234
235 tt.err = prep(tt.err)
236 have := pErr.ErrorWithUsage()
237
238
239
240 if have != tt.err {
241 t.Fatalf("\nwant:\n%s\nhave:\n%s", tt.err, have)
242 }
243 })
244 }
245 }
246
View as plain text