Skip to content

Commit 1cbfe8c

Browse files
thepuddsgopherbot
authored andcommitted
fmt: add more function and allocation tests
This is part of a series of CLs that aim to reduce how often interface arguments escape for the print functions in fmt. Currently, method values are one of two reasons reflect.Value.Interface always escapes its reflect.Value. Our later CLs modify behavior around method values, so we add some tests of function formatting (including method values) to help reduce the chances of breaking behavior later. We also add in some allocation tests focused on interface arguments for the print functions. These currently do not show any improvements compared to Go 1.21. These tests were originally in a later CL in our stack (CL 528538), but we split them out into this CL and moved them earlier in the stack. Updates #8618 Change-Id: Iec51abc3b7f86a2711e7497fc2fb7a678b9f8f73 Reviewed-on: https://go-review.googlesource.com/c/go/+/529575 Reviewed-by: Carlos Amedee <[email protected]> Auto-Submit: Ian Lance Taylor <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]>
1 parent 8391579 commit 1cbfe8c

File tree

1 file changed

+64
-4
lines changed

1 file changed

+64
-4
lines changed

src/fmt/fmt_test.go

+64-4
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import (
1111
"io"
1212
"math"
1313
"reflect"
14-
"runtime"
1514
"strings"
1615
"testing"
1716
"time"
@@ -112,6 +111,19 @@ func (p *P) String() string {
112111
return "String(p)"
113112
}
114113

114+
// Fn is a function type with a String method.
115+
type Fn func() int
116+
117+
func (fn Fn) String() string { return "String(fn)" }
118+
119+
var fnValue Fn
120+
121+
// U is a type with two unexported function fields.
122+
type U struct {
123+
u func() string
124+
fn Fn
125+
}
126+
115127
var barray = [5]renamedUint8{1, 2, 3, 4, 5}
116128
var bslice = barray[:]
117129

@@ -714,7 +726,6 @@ var fmtTests = []struct {
714726
// go syntax
715727
{"%#v", A{1, 2, "a", []int{1, 2}}, `fmt_test.A{i:1, j:0x2, s:"a", x:[]int{1, 2}}`},
716728
{"%#v", new(byte), "(*uint8)(0xPTR)"},
717-
{"%#v", TestFmtInterface, "(func(*testing.T))(0xPTR)"},
718729
{"%#v", make(chan int), "(chan int)(0xPTR)"},
719730
{"%#v", uint64(1<<64 - 1), "0xffffffffffffffff"},
720731
{"%#v", 1000000000, "1000000000"},
@@ -737,6 +748,54 @@ var fmtTests = []struct {
737748
{"%#v", 1.2345678, "1.2345678"},
738749
{"%#v", float32(1.2345678), "1.2345678"},
739750

751+
// functions
752+
{"%v", TestFmtInterface, "0xPTR"}, // simple function
753+
{"%v", reflect.ValueOf(TestFmtInterface), "0xPTR"},
754+
{"%v", G.GoString, "0xPTR"}, // method expression
755+
{"%v", reflect.ValueOf(G.GoString), "0xPTR"},
756+
{"%v", G(23).GoString, "0xPTR"}, // method value
757+
{"%v", reflect.ValueOf(G(23).GoString), "0xPTR"},
758+
{"%v", reflect.ValueOf(G(23)).Method(0), "0xPTR"},
759+
{"%v", Fn.String, "0xPTR"}, // method of function type
760+
{"%v", reflect.ValueOf(Fn.String), "0xPTR"},
761+
{"%v", fnValue, "String(fn)"}, // variable of function type with String method
762+
{"%v", reflect.ValueOf(fnValue), "String(fn)"},
763+
{"%v", [1]Fn{fnValue}, "[String(fn)]"}, // array of function type with String method
764+
{"%v", reflect.ValueOf([1]Fn{fnValue}), "[String(fn)]"},
765+
{"%v", fnValue.String, "0xPTR"}, // method value from function type
766+
{"%v", reflect.ValueOf(fnValue.String), "0xPTR"},
767+
{"%v", reflect.ValueOf(fnValue).Method(0), "0xPTR"},
768+
{"%v", U{}.u, "<nil>"}, // unexported function field
769+
{"%v", reflect.ValueOf(U{}.u), "<nil>"},
770+
{"%v", reflect.ValueOf(U{}).Field(0), "<nil>"},
771+
{"%v", U{fn: fnValue}.fn, "String(fn)"}, // unexported field of function type with String method
772+
{"%v", reflect.ValueOf(U{fn: fnValue}.fn), "String(fn)"},
773+
{"%v", reflect.ValueOf(U{fn: fnValue}).Field(1), "<nil>"},
774+
775+
// functions with go syntax
776+
{"%#v", TestFmtInterface, "(func(*testing.T))(0xPTR)"}, // simple function
777+
{"%#v", reflect.ValueOf(TestFmtInterface), "(func(*testing.T))(0xPTR)"},
778+
{"%#v", G.GoString, "(func(fmt_test.G) string)(0xPTR)"}, // method expression
779+
{"%#v", reflect.ValueOf(G.GoString), "(func(fmt_test.G) string)(0xPTR)"},
780+
{"%#v", G(23).GoString, "(func() string)(0xPTR)"}, // method value
781+
{"%#v", reflect.ValueOf(G(23).GoString), "(func() string)(0xPTR)"},
782+
{"%#v", reflect.ValueOf(G(23)).Method(0), "(func() string)(0xPTR)"},
783+
{"%#v", Fn.String, "(func(fmt_test.Fn) string)(0xPTR)"}, // method of function type
784+
{"%#v", reflect.ValueOf(Fn.String), "(func(fmt_test.Fn) string)(0xPTR)"},
785+
{"%#v", fnValue, "(fmt_test.Fn)(nil)"}, // variable of function type with String method
786+
{"%#v", reflect.ValueOf(fnValue), "(fmt_test.Fn)(nil)"},
787+
{"%#v", [1]Fn{fnValue}, "[1]fmt_test.Fn{(fmt_test.Fn)(nil)}"}, // array of function type with String method
788+
{"%#v", reflect.ValueOf([1]Fn{fnValue}), "[1]fmt_test.Fn{(fmt_test.Fn)(nil)}"},
789+
{"%#v", fnValue.String, "(func() string)(0xPTR)"}, // method value from function type
790+
{"%#v", reflect.ValueOf(fnValue.String), "(func() string)(0xPTR)"},
791+
{"%#v", reflect.ValueOf(fnValue).Method(0), "(func() string)(0xPTR)"},
792+
{"%#v", U{}.u, "(func() string)(nil)"}, // unexported function field
793+
{"%#v", reflect.ValueOf(U{}.u), "(func() string)(nil)"},
794+
{"%#v", reflect.ValueOf(U{}).Field(0), "(func() string)(nil)"},
795+
{"%#v", U{fn: fnValue}.fn, "(fmt_test.Fn)(nil)"}, // unexported field of function type with String method
796+
{"%#v", reflect.ValueOf(U{fn: fnValue}.fn), "(fmt_test.Fn)(nil)"},
797+
{"%#v", reflect.ValueOf(U{fn: fnValue}).Field(1), "(fmt_test.Fn)(nil)"},
798+
740799
// Whole number floats are printed without decimals. See Issue 27634.
741800
{"%#v", 1.0, "1"},
742801
{"%#v", 1000000.0, "1e+06"},
@@ -1438,6 +1497,9 @@ var mallocTest = []struct {
14381497
{0, `Fprintf(buf, "%s")`, func() { mallocBuf.Reset(); Fprintf(&mallocBuf, "%s", "hello") }},
14391498
{0, `Fprintf(buf, "%x")`, func() { mallocBuf.Reset(); Fprintf(&mallocBuf, "%x", 7) }},
14401499
{0, `Fprintf(buf, "%x")`, func() { mallocBuf.Reset(); Fprintf(&mallocBuf, "%x", 1<<16) }},
1500+
{1, `Fprintf(buf, "%x")`, func() { mallocBuf.Reset(); i := 1 << 16; Fprintf(&mallocBuf, "%x", i) }}, // not constant
1501+
{4, `Fprintf(buf, "%v")`, func() { mallocBuf.Reset(); s := []int{1, 2}; Fprintf(&mallocBuf, "%v", s) }},
1502+
{1, `Fprintf(buf, "%v")`, func() { mallocBuf.Reset(); type P struct{ x, y int }; Fprintf(&mallocBuf, "%v", P{1, 2}) }},
14411503
{2, `Fprintf(buf, "%80000s")`, func() { mallocBuf.Reset(); Fprintf(&mallocBuf, "%80000s", "hello") }}, // large buffer (>64KB)
14421504
// If the interface value doesn't need to allocate, amortized allocation overhead should be zero.
14431505
{0, `Fprintf(buf, "%x %x %x")`, func() {
@@ -1452,8 +1514,6 @@ func TestCountMallocs(t *testing.T) {
14521514
switch {
14531515
case testing.Short():
14541516
t.Skip("skipping malloc count in short mode")
1455-
case runtime.GOMAXPROCS(0) > 1:
1456-
t.Skip("skipping; GOMAXPROCS>1")
14571517
case race.Enabled:
14581518
t.Skip("skipping malloc count under race detector")
14591519
}

0 commit comments

Comments
 (0)