methods_test.gno
3.66 Kb · 119 lines
1package ufmt
2
3import (
4 "errors"
5 "strings"
6 "testing"
7)
8
9type str struct{ s string }
10
11func (v str) String() string { return v.s }
12
13type errT struct{}
14
15func (errT) Error() string { return "boom" }
16func (errT) String() string { return "stringer" }
17
18type goStr struct{}
19
20func (goStr) GoString() string { return "goStr{}" }
21func (goStr) String() string { return "plain" }
22
23type formatter struct{}
24
25func (formatter) Format(f State, verb rune) {
26 w, wok := f.Width()
27 p, pok := f.Precision()
28 Fprintf(f, "F[%c w=%d/%t p=%d/%t minus=%t plus=%t sharp=%t]",
29 verb, w, wok, p, pok, f.Flag('-'), f.Flag('+'), f.Flag('#'))
30}
31
32type panicky struct{}
33
34func (panicky) String() string { panic("nope") }
35
36type opaque struct{ n int }
37
38// Every want below is what Go's fmt prints for the same call (go1.27.1),
39// except where a comment says gno cannot see what Go sees.
40func TestMethods(t *testing.T) {
41 cases := []struct{ got, want string }{
42 {Sprintf("%v|%s|%q|%x|%10s|%-6v|", str{"hi"}, str{"hi"}, str{"hi"}, str{"hi"}, str{"hi"}, str{"hi"}),
43 `hi|hi|"hi"|6869| hi|hi |`},
44 {Sprintf("%v %s", errT{}, errT{}), "boom boom"}, // Error wins over String
45 {Sprintf("%#v %v", goStr{}, goStr{}), "goStr{} plain"},
46 {Sprintf("%v|%-8.3s|%+#x", formatter{}, formatter{}, formatter{}),
47 "F[v w=0/false p=0/false minus=false plus=false sharp=false]|" +
48 "F[s w=8/true p=3/true minus=true plus=false sharp=false]|" +
49 "F[x w=0/false p=0/false minus=false plus=true sharp=true]"},
50 {Sprintf("%v", panicky{}), "%!v(PANIC=String method: nope)"},
51 {Sprintf("%v", []any{str{"a"}, errT{}}), "[a boom]"},
52 {Sprintf("%s", []error{errors.New("base"), errT{}}), "[base boom]"},
53 {Sprint("a", 1, 2, "b", "c", 3.5, true, nil), "a1 2bc3.5 true <nil>"},
54 {Sprint(str{"x"}, str{"y"}), "x y"},
55 {Sprintln("a", 1, 2, "b"), "a 1 2 b\n"},
56
57 // Go reflects into the struct and prints "{%!d(string=hi)}" and
58 // "{7}"; gno has no reflect, so v2 says so instead of guessing.
59 {Sprintf("%d", str{"hi"}), "%!d(unhandled)"},
60 {Sprintf("%v", opaque{7}), "(unhandled)"},
61 {Sprintf("%T", opaque{7}), "unknown"},
62 // Go prints "%!w(*errors.errorString=&{base})": the type is not
63 // nameable here either.
64 {Sprintf("%w", errors.New("base")), "%!w(unhandled)"},
65 }
66 for i, c := range cases {
67 if c.got != c.want {
68 t.Errorf("case %d: got %q, want %q", i, c.got, c.want)
69 }
70 }
71}
72
73func TestErrorf(t *testing.T) {
74 base := errors.New("base")
75
76 e := Errorf("wrap: %w", base)
77 if e.Error() != "wrap: base" || !errors.Is(e, base) || errors.Unwrap(e) != base {
78 t.Errorf("single %%w: %q, Is=%t", e.Error(), errors.Is(e, base))
79 }
80
81 e2 := Errorf("two: %w and %w", base, errT{})
82 if e2.Error() != "two: base and boom" || !errors.Is(e2, base) || errors.Unwrap(e2) != nil {
83 t.Errorf("two %%w: %q, Is=%t", e2.Error(), errors.Is(e2, base))
84 }
85
86 if got := Errorf("%w", "notanerror").Error(); got != "%!w(string=notanerror)" {
87 t.Errorf("non-error %%w: %q", got)
88 }
89
90 plain := Errorf("plain %d", 1)
91 if plain.Error() != "plain 1" || errors.Unwrap(plain) != nil {
92 t.Errorf("plain: %q", plain.Error())
93 }
94}
95
96func TestWriters(t *testing.T) {
97 var sb strings.Builder
98 n, err := Fprintf(&sb, "%04d|", 7)
99 Fprint(&sb, "a", 1)
100 Fprintln(&sb, "!", 2)
101 if sb.String() != "0007|a1! 2\n" || n != 5 || err != nil {
102 t.Errorf("Fprint*: %q n=%d err=%v", sb.String(), n, err)
103 }
104
105 b := Appendf([]byte("x="), "%x", 255)
106 b = Append(b, " ", true)
107 b = Appendln(b, "", 1)
108 if string(b) != "x=ff true 1\n" {
109 t.Errorf("Append*: %q", string(b))
110 }
111}
112
113// The register dump that v0 could not print: "0x(unhandled verb: %X)".
114func TestRegisterDump(t *testing.T) {
115 got := Sprintf("0x%08X %08x", uint32(0x1004), uint32(0xa00513))
116 if got != "0x00001004 00a00513" {
117 t.Errorf("got %q", got)
118 }
119}