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}