ufmt_test.gno

9.35 Kb · 343 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
102	for _, tc := range cases {
103		name := fmt.Sprintf(tc.format, tc.values...)
104		t.Run(name, func(t *testing.T) {
105			got := Sprintf(tc.format, tc.values...)
106			if got != tc.expectedOutput {
107				t.Errorf("got %q, want %q.", got, tc.expectedOutput)
108			}
109		})
110	}
111}
112
113func TestErrorf(t *testing.T) {
114	tests := []struct {
115		name     string
116		format   string
117		args     []any
118		expected string
119	}{
120		{
121			name:     "simple string",
122			format:   "error: %s",
123			args:     []any{"something went wrong"},
124			expected: "error: something went wrong",
125		},
126		{
127			name:     "integer value",
128			format:   "value: %d",
129			args:     []any{42},
130			expected: "value: 42",
131		},
132		{
133			name:     "boolean value",
134			format:   "success: %t",
135			args:     []any{true},
136			expected: "success: true",
137		},
138		{
139			name:     "multiple values",
140			format:   "error %d: %s (success=%t)",
141			args:     []any{123, "failure occurred", false},
142			expected: "error 123: failure occurred (success=false)",
143		},
144		{
145			name:     "literal percent",
146			format:   "literal %%",
147			args:     []any{},
148			expected: "literal %",
149		},
150	}
151
152	for _, tt := range tests {
153		t.Run(tt.name, func(t *testing.T) {
154			err := Errorf(tt.format, tt.args...)
155			if err.Error() != tt.expected {
156				t.Errorf("Errorf(%q, %v) = %q, expected %q", tt.format, tt.args, err.Error(), tt.expected)
157			}
158		})
159	}
160}
161
162func TestPrintErrors(t *testing.T) {
163	got := Sprintf("error: %s", errors.New("can I be printed?"))
164	expectedOutput := "error: can I be printed?"
165	if got != expectedOutput {
166		t.Errorf("got %q, want %q.", got, expectedOutput)
167	}
168}
169
170func TestSprint(t *testing.T) {
171	tests := []struct {
172		name     string
173		args     []any
174		expected string
175	}{
176		{
177			name:     "Empty args",
178			args:     []any{},
179			expected: "",
180		},
181		{
182			name:     "String args",
183			args:     []any{"Hello", "World"},
184			expected: "Hello World",
185		},
186		{
187			name:     "Integer args",
188			args:     []any{1, 2, 3},
189			expected: "1 2 3",
190		},
191		{
192			name:     "Mixed args",
193			args:     []any{"Hello", 42, true, false, "World"},
194			expected: "Hello 42 true false World",
195		},
196		{
197			name:     "Unhandled type",
198			args:     []any{"Hello", 3.14, []int{1, 2, 3}},
199			expected: "Hello 3.140000 (unhandled)",
200		},
201	}
202
203	for _, tc := range tests {
204		t.Run(tc.name, func(t *testing.T) {
205			got := Sprint(tc.args...)
206			if got != tc.expected {
207				t.Errorf("got %q, want %q.", got, tc.expected)
208			}
209		})
210	}
211}
212
213func TestFprintf(t *testing.T) {
214	var buf bytes.Buffer
215	n, err := Fprintf(&buf, "Count: %d, Message: %s", 42, "hello")
216	if err != nil {
217		t.Fatalf("Fprintf failed: %v", err)
218	}
219
220	const expected = "Count: 42, Message: hello"
221	if buf.String() != expected {
222		t.Errorf("Expected %q, got %q", expected, buf.String())
223	}
224	if n != len(expected) {
225		t.Errorf("Expected %d bytes written, got %d", len(expected), n)
226	}
227}
228
229// TODO: replace os.Stdout with a buffer to capture the output and test it.
230func TestPrintf(t *testing.T) {
231	n, err := Printf("The answer is %d", 42)
232	if err != nil {
233		t.Fatalf("Printf failed: %v", err)
234	}
235
236	const expected = "The answer is 42"
237	if n != len(expected) {
238		t.Errorf("Expected 14 bytes written, got %d", n)
239	}
240}
241
242func TestAppendf(t *testing.T) {
243	b := []byte("Header: ")
244	result := Appendf(b, "Value %d", 7)
245	const expected = "Header: Value 7"
246	if string(result) != expected {
247		t.Errorf("Expected %q, got %q", expected, string(result))
248	}
249}
250
251func TestFprint(t *testing.T) {
252	var buf bytes.Buffer
253	n, err := Fprint(&buf, "Hello", 42, true)
254	if err != nil {
255		t.Fatalf("Fprint failed: %v", err)
256	}
257
258	const expected = "Hello 42 true"
259	if buf.String() != expected {
260		t.Errorf("Expected %q, got %q", expected, buf.String())
261	}
262	if n != len(expected) {
263		t.Errorf("Expected %d bytes written, got %d", len(expected), n)
264	}
265}
266
267// TODO: replace os.Stdout with a buffer to capture the output and test it.
268func TestPrint(t *testing.T) {
269	n, err := Print("Mixed", 3.14, false)
270	if err != nil {
271		t.Fatalf("Print failed: %v", err)
272	}
273
274	const expected = "Mixed 3.140000 false"
275	if n != len(expected) {
276		t.Errorf("Expected 12 bytes written, got %d", n)
277	}
278}
279
280func TestAppend(t *testing.T) {
281	b := []byte{0x01, 0x02}
282	result := Append(b, "Test", 99)
283
284	const expected = "\x01\x02Test 99"
285	if string(result) != expected {
286		t.Errorf("Expected %q, got %q", expected, string(result))
287	}
288}
289
290func TestFprintln(t *testing.T) {
291	var buf bytes.Buffer
292	n, err := Fprintln(&buf, "Line", 1)
293	if err != nil {
294		t.Fatalf("Fprintln failed: %v", err)
295	}
296
297	const expected = "Line 1\n"
298	if buf.String() != expected {
299		t.Errorf("Expected %q, got %q", expected, buf.String())
300	}
301	if n != len(expected) {
302		t.Errorf("Expected %d bytes written, got %d", len(expected), n)
303	}
304}
305
306// TODO: replace os.Stdout with a buffer to capture the output and test it.
307func TestPrintln(t *testing.T) {
308	n, err := Println("Output", "test")
309	if err != nil {
310		t.Fatalf("Println failed: %v", err)
311	}
312
313	const expected = "Output test\n"
314	if n != len(expected) {
315		t.Errorf("Expected 12 bytes written, got %d", n)
316	}
317}
318
319func TestSprintln(t *testing.T) {
320	result := Sprintln("Item", 42)
321
322	const expected = "Item 42\n"
323	if result != expected {
324		t.Errorf("Expected %q, got %q", expected, result)
325	}
326}
327
328func TestAppendln(t *testing.T) {
329	b := []byte("Start:")
330	result := Appendln(b, "End")
331
332	const expected = "Start:End\n"
333	if string(result) != expected {
334		t.Errorf("Expected %q, got %q", expected, string(result))
335	}
336}
337
338func assertNoError(t *testing.T, err error) {
339	t.Helper()
340	if err != nil {
341		t.Fatalf("Unexpected error: %v", err)
342	}
343}