ufmt_test.gno
10.24 Kb · 370 lines
1package ufmt
2
3import (
4 "bytes"
5 "errors"
6 "fmt"
7 "testing"
8)
9
10type stringer struct{}
11
12func (stringer) String() string {
13 return "I'm a stringer"
14}
15
16func TestSprintf(t *testing.T) {
17 tru := true
18 cases := []struct {
19 format string
20 values []any
21 expectedOutput string
22 }{
23 {"hello %s!", []any{"planet"}, "hello planet!"},
24 {"hello %v!", []any{"planet"}, "hello planet!"},
25 {"hi %%%s!", []any{"worl%d"}, "hi %worl%d!"},
26 {"%s %c %d %t", []any{"foo", 'α', 421, true}, "foo α 421 true"},
27 {"string [%s]", []any{"foo"}, "string [foo]"},
28 {"int [%d]", []any{int(42)}, "int [42]"},
29 {"int [%v]", []any{int(42)}, "int [42]"},
30 {"int8 [%d]", []any{int8(8)}, "int8 [8]"},
31 {"int8 [%v]", []any{int8(8)}, "int8 [8]"},
32 {"int16 [%d]", []any{int16(16)}, "int16 [16]"},
33 {"int16 [%v]", []any{int16(16)}, "int16 [16]"},
34 {"int32 [%d]", []any{int32(32)}, "int32 [32]"},
35 {"int32 [%v]", []any{int32(32)}, "int32 [32]"},
36 {"int64 [%d]", []any{int64(64)}, "int64 [64]"},
37 {"int64 [%v]", []any{int64(64)}, "int64 [64]"},
38 {"uint [%d]", []any{uint(42)}, "uint [42]"},
39 {"uint [%v]", []any{uint(42)}, "uint [42]"},
40 {"uint8 [%d]", []any{uint8(8)}, "uint8 [8]"},
41 {"uint8 [%v]", []any{uint8(8)}, "uint8 [8]"},
42 {"uint16 [%d]", []any{uint16(16)}, "uint16 [16]"},
43 {"uint16 [%v]", []any{uint16(16)}, "uint16 [16]"},
44 {"uint32 [%d]", []any{uint32(32)}, "uint32 [32]"},
45 {"uint32 [%v]", []any{uint32(32)}, "uint32 [32]"},
46 {"uint64 [%d]", []any{uint64(64)}, "uint64 [64]"},
47 {"uint64 [%v]", []any{uint64(64)}, "uint64 [64]"},
48 {"float64 [%e]", []any{float64(64.1)}, "float64 [6.41e+01]"},
49 {"float64 [%E]", []any{float64(64.1)}, "float64 [6.41E+01]"},
50 {"float64 [%f]", []any{float64(64.1)}, "float64 [64.100000]"},
51 {"float64 [%F]", []any{float64(64.1)}, "float64 [64.100000]"},
52 {"float64 [%g]", []any{float64(64.1)}, "float64 [64.1]"},
53 {"float64 [%G]", []any{float64(64.1)}, "float64 [64.1]"},
54 {"bool [%t]", []any{true}, "bool [true]"},
55 {"bool [%v]", []any{true}, "bool [true]"},
56 {"bool [%t]", []any{false}, "bool [false]"},
57 {"bool [%v]", []any{false}, "bool [false]"},
58 {"no args", nil, "no args"},
59 {"finish with %", nil, "finish with %"},
60 {"stringer [%s]", []any{stringer{}}, "stringer [I'm a stringer]"},
61 {"â", nil, "â"},
62 {"Hello, World! 😊", nil, "Hello, World! 😊"},
63 {"unicode formatting: %s", []any{"😊"}, "unicode formatting: 😊"},
64 {"invalid hex [%x]", []any{"invalid"}, "invalid hex [(unhandled)]"},
65 {"rune as character [%c]", []any{rune('A')}, "rune as character [A]"},
66 {"int as character [%c]", []any{int('B')}, "int as character [B]"},
67 {"quoted string [%q]", []any{"hello"}, "quoted string [\"hello\"]"},
68 {"quoted string with escape [%q]", []any{"\thello\nworld\\"}, "quoted string with escape [\"\\thello\\nworld\\\\\"]"},
69 {"invalid quoted string [%q]", []any{123}, "invalid quoted string [(unhandled)]"},
70 {"type of bool [%T]", []any{true}, "type of bool [bool]"},
71 {"type of int [%T]", []any{123}, "type of int [int]"},
72 {"type of string [%T]", []any{"hello"}, "type of string [string]"},
73 {"type of []byte [%T]", []any{[]byte{1, 2, 3}}, "type of []byte [[]byte]"},
74 {"type of []rune [%T]", []any{[]rune{'a', 'b', 'c'}}, "type of []rune [[]rune]"},
75 {"type of unknown [%T]", []any{struct{}{}}, "type of unknown [unknown]"},
76 // mismatch printing
77 {"%s", []any{nil}, "%!s(<nil>)"},
78 {"%s", []any{421}, "%!s(int=421)"},
79 {"%s", []any{"z"}, "z"},
80 {"%s", []any{tru}, "%!s(bool=true)"},
81 {"%s", []any{'z'}, "%!s(int32=122)"},
82
83 {"%c", []any{nil}, "%!c(<nil>)"},
84 {"%c", []any{421}, "ƥ"},
85 {"%c", []any{"z"}, "%!c(string=z)"},
86 {"%c", []any{tru}, "%!c(bool=true)"},
87 {"%c", []any{'z'}, "z"},
88
89 {"%d", []any{nil}, "%!d(<nil>)"},
90 {"%d", []any{421}, "421"},
91 {"%d", []any{"z"}, "%!d(string=z)"},
92 {"%d", []any{tru}, "%!d(bool=true)"},
93 {"%d", []any{'z'}, "122"},
94
95 {"%t", []any{nil}, "%!t(<nil>)"},
96 {"%t", []any{421}, "%!t(int=421)"},
97 {"%t", []any{"z"}, "%!t(string=z)"},
98 {"%t", []any{tru}, "true"},
99 {"%t", []any{'z'}, "%!t(int32=122)"},
100
101 {"%.2f", []any{3.14159}, "3.14"},
102 {"%.4f", []any{3.14159}, "3.1416"},
103 {"%.0f", []any{3.14159}, "3"},
104 {"%.1f", []any{3.0}, "3.0"},
105 {"%.3F", []any{3.14159}, "3.142"},
106 {"%.2e", []any{314.159}, "3.14e+02"},
107 {"%.3E", []any{314.159}, "3.142E+02"},
108 {"%.3g", []any{3.14159}, "3.14"},
109 {"%.5G", []any{3.14159}, "3.1416"},
110 {"%.0f", []any{3.6}, "4"},
111 {"%.0f", []any{3.4}, "3"},
112 {"%.1f", []any{0.0}, "0.0"},
113 {"%.2f", []any{1e6}, "1000000.00"},
114 {"%.2f", []any{1e-6}, "0.00"},
115
116 {"%5s", []any{"Hello World"}, "Hello World"},
117 {"%3s", []any{"Hi"}, " Hi"},
118 {"%2s", []any{"Hello"}, "Hello"},
119 {"%1s", []any{"A"}, "A"},
120 {"%0s", []any{"Test"}, "Test"},
121 {"%5s!", []any{"Hello World"}, "Hello World!"},
122 {"_%5s_", []any{"abc"}, "_ abc_"},
123 {"%2s%4s", []any{"ab", "cde"}, "ab cde"},
124 {"%5s", []any{""}, " "},
125 {"%3s", []any{nil}, "%!s(<nil>)"},
126 {"%2s", []any{123}, "%!s(int=123)"},
127 }
128
129 for _, tc := range cases {
130 name := fmt.Sprintf(tc.format, tc.values...)
131 t.Run(name, func(t *testing.T) {
132 got := Sprintf(tc.format, tc.values...)
133 if got != tc.expectedOutput {
134 t.Errorf("got %q, want %q.", got, tc.expectedOutput)
135 }
136 })
137 }
138}
139
140func TestErrorf(t *testing.T) {
141 tests := []struct {
142 name string
143 format string
144 args []any
145 expected string
146 }{
147 {
148 name: "simple string",
149 format: "error: %s",
150 args: []any{"something went wrong"},
151 expected: "error: something went wrong",
152 },
153 {
154 name: "integer value",
155 format: "value: %d",
156 args: []any{42},
157 expected: "value: 42",
158 },
159 {
160 name: "boolean value",
161 format: "success: %t",
162 args: []any{true},
163 expected: "success: true",
164 },
165 {
166 name: "multiple values",
167 format: "error %d: %s (success=%t)",
168 args: []any{123, "failure occurred", false},
169 expected: "error 123: failure occurred (success=false)",
170 },
171 {
172 name: "literal percent",
173 format: "literal %%",
174 args: []any{},
175 expected: "literal %",
176 },
177 }
178
179 for _, tt := range tests {
180 t.Run(tt.name, func(t *testing.T) {
181 err := Errorf(tt.format, tt.args...)
182 if err.Error() != tt.expected {
183 t.Errorf("Errorf(%q, %v) = %q, expected %q", tt.format, tt.args, err.Error(), tt.expected)
184 }
185 })
186 }
187}
188
189func TestPrintErrors(t *testing.T) {
190 got := Sprintf("error: %s", errors.New("can I be printed?"))
191 expectedOutput := "error: can I be printed?"
192 if got != expectedOutput {
193 t.Errorf("got %q, want %q.", got, expectedOutput)
194 }
195}
196
197func TestSprint(t *testing.T) {
198 tests := []struct {
199 name string
200 args []any
201 expected string
202 }{
203 {
204 name: "Empty args",
205 args: []any{},
206 expected: "",
207 },
208 {
209 name: "String args",
210 args: []any{"Hello", "World"},
211 expected: "Hello World",
212 },
213 {
214 name: "Integer args",
215 args: []any{1, 2, 3},
216 expected: "1 2 3",
217 },
218 {
219 name: "Mixed args",
220 args: []any{"Hello", 42, true, false, "World"},
221 expected: "Hello 42 true false World",
222 },
223 {
224 name: "Unhandled type",
225 args: []any{"Hello", 3.14, []int{1, 2, 3}},
226 expected: "Hello 3.140000 (unhandled)",
227 },
228 }
229
230 for _, tc := range tests {
231 t.Run(tc.name, func(t *testing.T) {
232 got := Sprint(tc.args...)
233 if got != tc.expected {
234 t.Errorf("got %q, want %q.", got, tc.expected)
235 }
236 })
237 }
238}
239
240func TestFprintf(t *testing.T) {
241 var buf bytes.Buffer
242 n, err := Fprintf(&buf, "Count: %d, Message: %s", 42, "hello")
243 if err != nil {
244 t.Fatalf("Fprintf failed: %v", err)
245 }
246
247 const expected = "Count: 42, Message: hello"
248 if buf.String() != expected {
249 t.Errorf("Expected %q, got %q", expected, buf.String())
250 }
251 if n != len(expected) {
252 t.Errorf("Expected %d bytes written, got %d", len(expected), n)
253 }
254}
255
256// TODO: replace os.Stdout with a buffer to capture the output and test it.
257func TestPrintf(t *testing.T) {
258 n, err := Printf("The answer is %d", 42)
259 if err != nil {
260 t.Fatalf("Printf failed: %v", err)
261 }
262
263 const expected = "The answer is 42"
264 if n != len(expected) {
265 t.Errorf("Expected 14 bytes written, got %d", n)
266 }
267}
268
269func TestAppendf(t *testing.T) {
270 b := []byte("Header: ")
271 result := Appendf(b, "Value %d", 7)
272 const expected = "Header: Value 7"
273 if string(result) != expected {
274 t.Errorf("Expected %q, got %q", expected, string(result))
275 }
276}
277
278func TestFprint(t *testing.T) {
279 var buf bytes.Buffer
280 n, err := Fprint(&buf, "Hello", 42, true)
281 if err != nil {
282 t.Fatalf("Fprint failed: %v", err)
283 }
284
285 const expected = "Hello 42 true"
286 if buf.String() != expected {
287 t.Errorf("Expected %q, got %q", expected, buf.String())
288 }
289 if n != len(expected) {
290 t.Errorf("Expected %d bytes written, got %d", len(expected), n)
291 }
292}
293
294// TODO: replace os.Stdout with a buffer to capture the output and test it.
295func TestPrint(t *testing.T) {
296 n, err := Print("Mixed", 3.14, false)
297 if err != nil {
298 t.Fatalf("Print failed: %v", err)
299 }
300
301 const expected = "Mixed 3.140000 false"
302 if n != len(expected) {
303 t.Errorf("Expected 12 bytes written, got %d", n)
304 }
305}
306
307func TestAppend(t *testing.T) {
308 b := []byte{0x01, 0x02}
309 result := Append(b, "Test", 99)
310
311 const expected = "\x01\x02Test 99"
312 if string(result) != expected {
313 t.Errorf("Expected %q, got %q", expected, string(result))
314 }
315}
316
317func TestFprintln(t *testing.T) {
318 var buf bytes.Buffer
319 n, err := Fprintln(&buf, "Line", 1)
320 if err != nil {
321 t.Fatalf("Fprintln failed: %v", err)
322 }
323
324 const expected = "Line 1\n"
325 if buf.String() != expected {
326 t.Errorf("Expected %q, got %q", expected, buf.String())
327 }
328 if n != len(expected) {
329 t.Errorf("Expected %d bytes written, got %d", len(expected), n)
330 }
331}
332
333// TODO: replace os.Stdout with a buffer to capture the output and test it.
334func TestPrintln(t *testing.T) {
335 n, err := Println("Output", "test")
336 if err != nil {
337 t.Fatalf("Println failed: %v", err)
338 }
339
340 const expected = "Output test\n"
341 if n != len(expected) {
342 t.Errorf("Expected 12 bytes written, got %d", n)
343 }
344}
345
346func TestSprintln(t *testing.T) {
347 result := Sprintln("Item", 42)
348
349 const expected = "Item 42\n"
350 if result != expected {
351 t.Errorf("Expected %q, got %q", expected, result)
352 }
353}
354
355func TestAppendln(t *testing.T) {
356 b := []byte("Start:")
357 result := Appendln(b, "End")
358
359 const expected = "Start:End\n"
360 if string(result) != expected {
361 t.Errorf("Expected %q, got %q", expected, string(result))
362 }
363}
364
365func assertNoError(t *testing.T, err error) {
366 t.Helper()
367 if err != nil {
368 t.Fatalf("Unexpected error: %v", err)
369 }
370}