1package ufmt23import (4 "bytes"5 "errors"6 "fmt"7 "testing"8)910type stringer struct{}1112func (stringer) String() string {13 return "I'm a stringer"14}1516func TestSprintf(t *testing.T) {17 tru := true18 cases := []struct {19 format string20 values []any21 expectedOutput string22 }{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 printing77 {"%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)"},8283 {"%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"},8889 {"%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"},9495 {"%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)"},100101 {"%.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"},115116 {"%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 }128129 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}139140func TestErrorf(t *testing.T) {141 tests := []struct {142 name string143 format string144 args []any145 expected string146 }{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 }178179 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}188189func 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}196197func TestSprint(t *testing.T) {198 tests := []struct {199 name string200 args []any201 expected string202 }{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 }229230 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}239240func TestFprintf(t *testing.T) {241 var buf bytes.Buffer242 n, err := Fprintf(&buf, "Count: %d, Message: %s", 42, "hello")243 if err != nil {244 t.Fatalf("Fprintf failed: %v", err)245 }246247 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}255256// 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 }262263 const expected = "The answer is 42"264 if n != len(expected) {265 t.Errorf("Expected 14 bytes written, got %d", n)266 }267}268269func 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}277278func TestFprint(t *testing.T) {279 var buf bytes.Buffer280 n, err := Fprint(&buf, "Hello", 42, true)281 if err != nil {282 t.Fatalf("Fprint failed: %v", err)283 }284285 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}293294// 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 }300301 const expected = "Mixed 3.140000 false"302 if n != len(expected) {303 t.Errorf("Expected 12 bytes written, got %d", n)304 }305}306307func TestAppend(t *testing.T) {308 b := []byte{0x01, 0x02}309 result := Append(b, "Test", 99)310311 const expected = "\x01\x02Test 99"312 if string(result) != expected {313 t.Errorf("Expected %q, got %q", expected, string(result))314 }315}316317func TestFprintln(t *testing.T) {318 var buf bytes.Buffer319 n, err := Fprintln(&buf, "Line", 1)320 if err != nil {321 t.Fatalf("Fprintln failed: %v", err)322 }323324 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}332333// 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 }339340 const expected = "Output test\n"341 if n != len(expected) {342 t.Errorf("Expected 12 bytes written, got %d", n)343 }344}345346func TestSprintln(t *testing.T) {347 result := Sprintln("Item", 42)348349 const expected = "Item 42\n"350 if result != expected {351 t.Errorf("Expected %q, got %q", expected, result)352 }353}354355func TestAppendln(t *testing.T) {356 b := []byte("Start:")357 result := Appendln(b, "End")358359 const expected = "Start:End\n"360 if string(result) != expected {361 t.Errorf("Expected %q, got %q", expected, string(result))362 }363}364365func assertNoError(t *testing.T, err error) {366 t.Helper()367 if err != nil {368 t.Fatalf("Unexpected error: %v", err)369 }370}371Signatures reconstructed verbatim from vm/qfuncs — interface params keep their inline definitions.