Skip to content

Commit 35a8fd8

Browse files
fix(print): format output
1 parent b2fab7d commit 35a8fd8

File tree

6 files changed

+357
-45
lines changed

6 files changed

+357
-45
lines changed

.github/workflows/ci.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ jobs:
8181
fail-fast: false
8282
matrix:
8383
os: ["ubuntu-latest"]
84-
go: ["1.23"]
84+
go: ["1.24"]
8585
runs-on: ${{ matrix.os }}
8686
steps:
8787
- uses: actions/checkout@v4

print.go

+20-8
Original file line numberDiff line numberDiff line change
@@ -20,17 +20,30 @@ func Sprintln(args ...any) string {
2020
return Sprint(args...) + "\n"
2121
}
2222

23+
2324
func Sprintf(format string, args ...any) string {
2425
var result string
2526
index := 0
2627

2728
for i := 0; i < len(format); i++ {
28-
if format[i] == '%' && i+1 < len(format) {
29-
i++
29+
// If the current character is '%', it's a format specifier
30+
if format[i] == '%' {
31+
// Check if it's a literal '%' (i.e. '%%')
32+
if i+1 < len(format) && format[i+1] == '%' {
33+
result += "%"
34+
i++ // Skip the next '%'
35+
continue
36+
}
37+
38+
// Check if we've run out of arguments and append "invalid"
3039
if index >= len(args) {
31-
result += "%" + string(format[i])
40+
result += "invalid"
41+
i++ // Skip over the format specifier (d, s, etc.)
3242
continue
3343
}
44+
45+
// Otherwise, process the format specifier
46+
i++ // Move to the format specifier (e.g., 'd', 's', etc.)
3447
switch format[i] {
3548
case 'd':
3649
if v, ok := args[index].(int); ok {
@@ -58,9 +71,6 @@ func Sprintf(format string, args ...any) string {
5871
} else {
5972
result += "invalid"
6073
}
61-
case '%':
62-
result += "%"
63-
index--
6474
case 'T':
6575
if v, ok := args[index].(bool); ok {
6676
if v {
@@ -78,10 +88,12 @@ func Sprintf(format string, args ...any) string {
7888
result += "invalid"
7989
}
8090
default:
81-
result += "%" + string(format[i])
91+
// For unknown format specifiers, just append "invalid"
92+
result += "invalid"
8293
}
83-
index++
94+
index++ // Move to the next argument
8495
} else {
96+
// Regular character, just append it
8597
result += string(format[i])
8698
}
8799
}

print_test.go

+297-1
Original file line numberDiff line numberDiff line change
@@ -1 +1,297 @@
1-
package gouse
1+
package gouse
2+
3+
import (
4+
"bytes"
5+
"os"
6+
"testing"
7+
)
8+
9+
func TestSprint(t *testing.T) {
10+
tests := []struct {
11+
args []any
12+
expected string
13+
}{
14+
{
15+
args: []any{"Hello", "world"},
16+
expected: "Hello world",
17+
},
18+
{
19+
args: []any{1, 2, 3},
20+
expected: "1 2 3",
21+
},
22+
{
23+
args: []any{"Go", 3.14, true},
24+
expected: "Go 3.14 true",
25+
},
26+
{
27+
args: []any{},
28+
expected: "",
29+
},
30+
{
31+
args: []any{nil},
32+
expected: "nil",
33+
},
34+
}
35+
36+
for _, test := range tests {
37+
t.Run("Sprint Test", func(t *testing.T) {
38+
actual := Sprint(test.args...)
39+
if actual != test.expected {
40+
t.Errorf("Sprint(%v) = %v; expected %v", test.args, actual, test.expected)
41+
}
42+
})
43+
}
44+
}
45+
46+
func TestSprintln(t *testing.T) {
47+
tests := []struct {
48+
args []any
49+
expected string
50+
}{
51+
{
52+
args: []any{"Hello", "world"},
53+
expected: "Hello world\n",
54+
},
55+
{
56+
args: []any{1, 2, 3},
57+
expected: "1 2 3\n",
58+
},
59+
{
60+
args: []any{"Go", 3.14, true},
61+
expected: "Go 3.14 true\n",
62+
},
63+
{
64+
args: []any{},
65+
expected: "\n",
66+
},
67+
{
68+
args: []any{nil},
69+
expected: "nil\n",
70+
},
71+
}
72+
73+
for _, test := range tests {
74+
t.Run("Sprintln Test", func(t *testing.T) {
75+
actual := Sprintln(test.args...)
76+
if actual != test.expected {
77+
t.Errorf("Sprintln(%v) = %v; expected %v", test.args, actual, test.expected)
78+
}
79+
})
80+
}
81+
}
82+
83+
func TestSprintf(t *testing.T) {
84+
tests := []struct {
85+
format string
86+
args []any
87+
expected string
88+
}{
89+
{
90+
format: "Hello %s!",
91+
args: []any{"world"},
92+
expected: "Hello world!",
93+
},
94+
{
95+
format: "Value: %d",
96+
args: []any{123},
97+
expected: "Value: 123",
98+
},
99+
{
100+
format: "Pi is approximately %f",
101+
args: []any{3.14159},
102+
expected: "Pi is approximately 3.14",
103+
},
104+
{
105+
format: "Boolean: %t",
106+
args: []any{true},
107+
expected: "Boolean: true",
108+
},
109+
{
110+
format: "Character: %c",
111+
args: []any{65}, // ASCII value of 'A'
112+
expected: "Character: A",
113+
},
114+
{
115+
format: "Invalid: %s %d",
116+
args: []any{true, "hello"},
117+
expected: "Invalid: invalid invalid",
118+
},
119+
{
120+
format: "Mix: %s, %d, %f",
121+
args: []any{"test", 123, 3.14159},
122+
expected: "Mix: test, 123, 3.14",
123+
},
124+
{
125+
format: "No argument: %d",
126+
args: []any{},
127+
expected: "No argument: invalid",
128+
},
129+
{
130+
format: "Percent sign: %% should appear",
131+
args: []any{},
132+
expected: "Percent sign: % should appear",
133+
},
134+
{
135+
format: "%T",
136+
args: []any{true},
137+
expected: "true",
138+
},
139+
{
140+
format: "%T",
141+
args: []any{false},
142+
expected: "false",
143+
},
144+
{
145+
format: "Invalid type: %f",
146+
args: []any{"not a float"},
147+
expected: "Invalid type: invalid",
148+
},
149+
}
150+
151+
for _, test := range tests {
152+
t.Run("Sprintf Test", func(t *testing.T) {
153+
actual := Sprintf(test.format, test.args...)
154+
if actual != test.expected {
155+
t.Errorf("Sprintf(%q, %v) = %v; expected %v", test.format, test.args, actual, test.expected)
156+
}
157+
})
158+
}
159+
}
160+
161+
func captureOutput(f func()) string {
162+
// Create a pipe to capture output
163+
r, w, _ := os.Pipe()
164+
165+
// Save the original stdout (so we can restore it later)
166+
stdout := os.Stdout
167+
// Redirect os.Stdout to the pipe's write end
168+
os.Stdout = w
169+
170+
// Call the function that writes to stdout
171+
f()
172+
173+
// Close the write end of the pipe
174+
w.Close()
175+
176+
// Read the captured output from the read end of the pipe
177+
var buf bytes.Buffer
178+
_, _ = buf.ReadFrom(r)
179+
180+
// Restore the original stdout
181+
os.Stdout = stdout
182+
183+
// Return the captured output as a string
184+
return buf.String()
185+
}
186+
187+
func TestPrintln(t *testing.T) {
188+
tests := []struct {
189+
args []any
190+
expected string
191+
}{
192+
{
193+
args: []any{"Hello", "World"},
194+
expected: "Hello World\n",
195+
},
196+
{
197+
args: []any{"SingleLine"},
198+
expected: "SingleLine\n",
199+
},
200+
{
201+
args: []any{123, "is a number"},
202+
expected: "123 is a number\n",
203+
},
204+
}
205+
206+
for _, test := range tests {
207+
t.Run("Println Test", func(t *testing.T) {
208+
actual := captureOutput(func() {
209+
Println(test.args...)
210+
})
211+
if actual != test.expected {
212+
t.Errorf("Println(%v) = %v; expected %v", test.args, actual, test.expected)
213+
}
214+
})
215+
}
216+
}
217+
218+
func TestPrint(t *testing.T) {
219+
tests := []struct {
220+
args []any
221+
expected string
222+
}{
223+
{
224+
args: []any{"Hello", "World"},
225+
expected: "Hello World",
226+
},
227+
{
228+
args: []any{"SingleLine"},
229+
expected: "SingleLine",
230+
},
231+
{
232+
args: []any{123, "is a number"},
233+
expected: "123 is a number",
234+
},
235+
}
236+
237+
for _, test := range tests {
238+
t.Run("Print Test", func(t *testing.T) {
239+
actual := captureOutput(func() {
240+
Print(test.args...)
241+
})
242+
if actual != test.expected {
243+
t.Errorf("Print(%v) = %v; expected %v", test.args, actual, test.expected)
244+
}
245+
})
246+
}
247+
}
248+
249+
func TestPrintf(t *testing.T) {
250+
tests := []struct {
251+
format string
252+
args []any
253+
expected string
254+
}{
255+
{
256+
format: "Hello %s!",
257+
args: []any{"World"},
258+
expected: "Hello World!",
259+
},
260+
{
261+
format: "The number is: %d",
262+
args: []any{123},
263+
expected: "The number is: 123",
264+
},
265+
{
266+
format: "Pi: %f",
267+
args: []any{3.14159},
268+
expected: "Pi: 3.14",
269+
},
270+
{
271+
format: "Boolean: %t",
272+
args: []any{true},
273+
expected: "Boolean: true",
274+
},
275+
{
276+
format: "Character: %c",
277+
args: []any{65},
278+
expected: "Character: A",
279+
},
280+
{
281+
format: "No argument: %d",
282+
args: []any{},
283+
expected: "No argument: invalid",
284+
},
285+
}
286+
287+
for _, test := range tests {
288+
t.Run("Printf Test", func(t *testing.T) {
289+
actual := captureOutput(func() {
290+
Printf(test.format, test.args...)
291+
})
292+
if actual != test.expected {
293+
t.Errorf("Printf(%q, %v) = %v; expected %v", test.format, test.args, actual, test.expected)
294+
}
295+
})
296+
}
297+
}

0 commit comments

Comments
 (0)