diff --git a/debug_test.go b/debug_test.go new file mode 100644 index 0000000..4fc042c --- /dev/null +++ b/debug_test.go @@ -0,0 +1,21 @@ +package notation + +import ( + "fmt" + "reflect" + "testing" +) + +func TestDebugNode(t *testing.T) { + const expect = `"foobarbaz"` + o := "foobarbaz" + n := reflectValue(none, reflect.ValueOf(o)) + s := fmt.Sprint(n) + if s != expect { + t.Fatalf( + "failed to get debug string of node, got: %s, expected: %s", + s, + expect, + ) + } +} diff --git a/env_test.go b/env_test.go new file mode 100644 index 0000000..55b0c1c --- /dev/null +++ b/env_test.go @@ -0,0 +1,51 @@ +package notation + +import ( + "os" + "strings" + "testing" +) + +func withEnv(t *testing.T, e ...string) (revert func()) { + var r []func() + revert = func() { + for i := range r { + r[i]() + } + } + + revertOne := func(key, value string, existed bool) func() { + return func() { + if existed { + if err := os.Setenv(key, value); err != nil { + t.Fatal(err) + } + + return + } + + if err := os.Unsetenv(key); err != nil { + t.Fatal(err) + } + } + } + + for i := range e { + var key, value string + p := strings.Split(e[i], "=") + key = p[0] + if len(p) > 1 { + value = p[1] + } + + prev, ok := os.LookupEnv(key) + if err := os.Setenv(key, value); err != nil { + revert() + t.Fatal(err) + } + + r = append(r, revertOne(key, prev, ok)) + } + + return +} diff --git a/fprint.go b/fprint.go index fbfcedc..d06d78d 100644 --- a/fprint.go +++ b/fprint.go @@ -1,125 +1,168 @@ package notation -import ( - "fmt" - "io" -) - -type writer struct { - w io.Writer - n int - err error +func unwrappable(n node) bool { + return n.len == n.wrapLen.max && + n.len == n.fullWrap.max } -func (w *writer) write(o interface{}) { - if w.err != nil { +func initialize(t *int, v int) { + if *t > 0 { return } - n, err := fmt.Fprint(w.w, o) - w.n += n - w.err = err + *t = v +} + +func max(t *int, v int) { + if *t >= v { + return + } + + *t = v } func nodeLen(t int, n node) node { - var w int + var w, f int for i, p := range n.parts { switch part := p.(type) { case string: n.len += len(part) w += len(part) + f += len(part) case node: part = nodeLen(t, part) n.parts[i] = part n.len += part.len - if part.len == part.wlen { + if unwrappable(part) { + w += part.len + f += part.len + continue + } + + if part.len == part.wrapLen.max { w += part.len } else { - w += part.wlen0 - if w > n.wlen { - n.wlen = w - } - - if part.wlen > n.wlen { - n.wlen = part.wlen - } - - if n.wlen0 == 0 { - n.wlen0 = w - } - - w = part.wlenLast + w += part.wrapLen.first + initialize(&n.wrapLen.first, w) + max(&n.wrapLen.max, w) + w = part.wrapLen.last } + + f += part.fullWrap.first + initialize(&n.fullWrap.first, f) + max(&n.fullWrap.max, f) + f = part.fullWrap.last case wrapper: if len(part.items) == 0 { continue } - if w > n.wlen { - n.wlen = w + initialize(&n.wrapLen.first, w) + max(&n.wrapLen.max, w) + initialize(&n.fullWrap.first, f) + max(&n.fullWrap.max, f) + w, f = 0, 0 + n.len += (len(part.items) - 1) * len(part.sep) + if part.mode == line { + w += (len(part.items) - 1) * len(part.sep) } - if n.wlen0 == 0 { - n.wlen0 = w - } - - w = 0 - for j, ni := range part.items { - ni = nodeLen(t, ni) - part.items[j] = ni - n.len += ni.len - wni := t + ni.len + len(part.suffix) - if wni > w { - w = wni + for j, item := range part.items { + item = nodeLen(t, item) + part.items[j] = item + n.len += item.len + switch part.mode { + case line: + w += item.len + max(&f, item.len) + default: + wj := t + item.len + len(part.suffix) + max(&w, wj) + fj := t + item.fullWrap.max + max(&f, fj) + fj = t + item.fullWrap.last + len(part.suffix) + max(&f, fj) } } - if len(part.items) > 0 { - n.len += (len(part.items) - 1) * len(part.sep) - } - - w = 0 + max(&n.wrapLen.max, w) + max(&n.fullWrap.max, f) + w, f = 0, 0 } } - if w > n.wlen { - n.wlen = w - } - - if n.wlen0 == 0 { - n.wlen0 = w - } - - n.wlenLast = w + initialize(&n.wrapLen.first, w) + max(&n.wrapLen.max, w) + n.wrapLen.last = w + initialize(&n.fullWrap.first, f) + max(&n.fullWrap.max, f) + n.fullWrap.last = f return n } func wrapNode(t, c0, c1 int, n node) node { - if n.len <= c0 || n.wlen == n.len { + if n.len <= c0 { return n } - if n.len <= c1 && n.len-c0 <= n.wlen { + if n.wrapLen.max >= n.len && n.fullWrap.max >= n.len { + return n + } + + if n.len <= c1 && n.len-c0 <= n.wrapLen.max { return n } n.wrap = true - if n.wlen <= c0 { - return n - } - + cc0, cc1 := c0, c1 for i, p := range n.parts { switch part := p.(type) { case node: - n.parts[i] = wrapNode(t, c0, c1, part) + part = wrapNode(t, cc0, cc1, part) + n.parts[i] = part + if part.wrap { + cc0 -= part.wrapLen.last + cc1 -= part.wrapLen.last + } else { + cc0 -= part.len + cc1 -= part.len + } case wrapper: - for j := range part.items { - part.items[j] = wrapNode( - t, - c0-t, - c1-t, - part.items[j], - ) + if len(part.items) > 0 { + cc0, cc1 = c0, c1 + } + + switch part.mode { + case line: + c0, c1 = c0-t, c1-t + var w int + for j, ni := range part.items { + if w > 0 && w+len(part.sep)+ni.len > c0 { + w = 0 + part.lineWrappers = append( + part.lineWrappers, + j, + ) + } + + if w > 0 { + w += len(part.sep) + } + + w += ni.len + } + + n.parts[i] = part + c0, c1 = c0+t, c1+t + default: + for j := range part.items { + part.items[j] = wrapNode( + t, + c0-t, + c1-t, + part.items[j], + ) + } } } } @@ -132,35 +175,60 @@ func fprint(w *writer, t int, n node) { return } - for i := 0; i < t; i++ { - w.write("\t") - } - for _, p := range n.parts { switch part := p.(type) { case node: - fprint(w, 0, part) + fprint(w, t, part) case wrapper: if len(part.items) == 0 { continue } - if n.wrap { - w.write("\n") - } - - for i, ni := range part.items { - if n.wrap { - fprint(w, t+1, ni) - w.write(part.suffix) - w.write("\n") - } else { - fprint(w, 0, ni) + if !n.wrap { + for i, ni := range part.items { + fprint(w, t, ni) if i < len(part.items)-1 { w.write(part.sep) } } + + continue } + + t++ + switch part.mode { + case line: + var ( + wi int + lineStarted bool + ) + + w.line(t) + for i, ni := range part.items { + if len(part.lineWrappers) > wi && + i == part.lineWrappers[wi] { + wi++ + w.line(t) + lineStarted = false + } + + if lineStarted { + w.write(part.sep) + } + + fprint(w, 0, ni) + lineStarted = true + } + default: + for _, ni := range part.items { + w.line(t) + fprint(w, t, ni) + w.write(part.suffix) + } + } + + t-- + w.line(t) default: w.write(part) } diff --git a/fprint_test.go b/fprint_test.go new file mode 100644 index 0000000..a5508bf --- /dev/null +++ b/fprint_test.go @@ -0,0 +1,206 @@ +package notation + +import ( + "bytes" + "errors" + "testing" +) + +type failingWriter int + +var errTest = errors.New("test") + +func failAfter(n int) *failingWriter { + w := failingWriter(n) + return &w +} + +func (w *failingWriter) Write(p []byte) (int, error) { + *w = failingWriter(int(*w) - len(p)) + if *w >= 0 { + return len(p), nil + } + + return len(p) + int(*w), errTest +} + +func TestFailingWriter(t *testing.T) { + t.Run("single object", func(t *testing.T) { + o := struct{ fooBarBaz int }{42} + w := failAfter(9) + n, err := Fprint(w, o) + if n == 9 && err == errTest { + return + } + + if n != 9 { + t.Fatalf("failed to writ the expected bytes; expected: 9, written: %d", n) + } + + if err != errTest { + t.Fatalf("failed to receive the right error; expected: %v, received: %v", errTest, err) + } + }) + + t.Run("multiple objects, fail first", func(t *testing.T) { + o := struct{ fooBarBaz int }{42} + w := failAfter(9) + n, err := Fprint(w, o, o) + if n == 9 && err == errTest { + return + } + + if n != 9 { + t.Fatalf("failed to writ the expected bytes; expected: 9, written: %d", n) + } + + if err != errTest { + t.Fatalf("failed to receive the right error; expected: %v, received: %v", errTest, err) + } + }) + + t.Run("multiple objects, fail second", func(t *testing.T) { + o := struct{ fooBarBaz int }{42} + w := failAfter(18) + n, err := Fprint(w, o, o) + if n == 9 && err == errTest { + return + } + + if n != 18 { + t.Fatalf("failed to writ the expected bytes; expected: 9, written: %d", n) + } + + if err != errTest { + t.Fatalf("failed to receive the right error; expected: %v, received: %v", errTest, err) + } + }) +} + +func TestFprint(t *testing.T) { + t.Run("Fprint", func(t *testing.T) { + const expect = `{fooBarBaz: 42}` + var b bytes.Buffer + o := struct{ fooBarBaz int }{42} + defer withEnv(t, "TABWIDTH=0", "LINEWIDTH=0", "LINEWIDTH1=0")() + n, err := Fprint(&b, o) + if err != nil { + t.Fatal(err) + } + + if n != len(expect) { + t.Fatalf("invalid write length; expected: %d, got: %d", len(expect), n) + } + + if b.String() != expect { + t.Fatalf("invalid output; expected: %s, got: %s", expect, b.String()) + } + }) + + t.Run("Fprintw", func(t *testing.T) { + const expect = `{ + fooBarBaz: 42, +}` + + var b bytes.Buffer + o := struct{ fooBarBaz int }{42} + defer withEnv(t, "TABWIDTH=0", "LINEWIDTH=0", "LINEWIDTH1=0")() + n, err := Fprintw(&b, o) + if err != nil { + t.Fatal(err) + } + + if n != len(expect) { + t.Fatalf("invalid write length; expected: %d, got: %d", len(expect), n) + } + + if b.String() != expect { + t.Fatalf("invalid output; expected: %s, got: %s", expect, b.String()) + } + }) + + t.Run("Fprintt", func(t *testing.T) { + const expect = `struct{fooBarBaz int}{fooBarBaz: 42}` + var b bytes.Buffer + o := struct{ fooBarBaz int }{42} + defer withEnv(t, "TABWIDTH=0", "LINEWIDTH=0", "LINEWIDTH1=0")() + n, err := Fprintt(&b, o) + if err != nil { + t.Fatal(err) + } + + if n != len(expect) { + t.Fatalf("invalid write length; expected: %d, got: %d", len(expect), n) + } + + if b.String() != expect { + t.Fatalf("invalid output; expected: %s, got: %s", expect, b.String()) + } + }) + + t.Run("Fprintwt", func(t *testing.T) { + const expect = `struct{ + fooBarBaz int +}{ + fooBarBaz: 42, +}` + + var b bytes.Buffer + o := struct{ fooBarBaz int }{42} + defer withEnv(t, "TABWIDTH=0", "LINEWIDTH=0", "LINEWIDTH1=0")() + n, err := Fprintwt(&b, o) + if err != nil { + t.Fatal(err) + } + + if n != len(expect) { + t.Fatalf("invalid write length; expected: %d, got: %d", len(expect), n) + } + + if b.String() != expect { + t.Fatalf("invalid output; expected: %s, got: %s", expect, b.String()) + } + }) + + t.Run("Fprintv", func(t *testing.T) { + const expect = `struct{fooBarBaz int}{fooBarBaz: int(42)}` + var b bytes.Buffer + o := struct{ fooBarBaz int }{42} + defer withEnv(t, "TABWIDTH=0", "LINEWIDTH=0", "LINEWIDTH1=0")() + n, err := Fprintv(&b, o) + if err != nil { + t.Fatal(err) + } + + if n != len(expect) { + t.Fatalf("invalid write length; expected: %d, got: %d", len(expect), n) + } + + if b.String() != expect { + t.Fatalf("invalid output; expected: %s, got: %s", expect, b.String()) + } + }) + + t.Run("Fprintv", func(t *testing.T) { + const expect = `struct{ + fooBarBaz int +}{ + fooBarBaz: int(42), +}` + var b bytes.Buffer + o := struct{ fooBarBaz int }{42} + defer withEnv(t, "TABWIDTH=0", "LINEWIDTH=0", "LINEWIDTH1=0")() + n, err := Fprintwv(&b, o) + if err != nil { + t.Fatal(err) + } + + if n != len(expect) { + t.Fatalf("invalid write length; expected: %d, got: %d", len(expect), n) + } + + if b.String() != expect { + t.Fatalf("invalid output; expected: %s, got: %s", expect, b.String()) + } + }) +} diff --git a/notation.go b/notation.go index 6426162..a22cafd 100644 --- a/notation.go +++ b/notation.go @@ -2,6 +2,7 @@ package notation import ( "bytes" + "fmt" "io" "os" "reflect" @@ -18,17 +19,63 @@ const ( types skipTypes allTypes + randomMaps ) -type node struct { - len, wlen, wlen0, wlenLast int - wrap bool - parts []interface{} +type wrapLen struct { + first, max, last int } +type node struct { + len int + wrapLen wrapLen + fullWrap wrapLen + wrap bool + parts []interface{} +} + +type wrapMode int + +const ( + block wrapMode = iota + line +) + type wrapper struct { - sep, suffix string - items []node + mode wrapMode + sep, suffix string + items []node + lineWrappers []int +} + +type writer struct { + w io.Writer + n int + err error +} + +func (n node) String() string { + var b bytes.Buffer + w := &writer{w: &b} + fprint(w, 0, n) + return b.String() +} + +func (w *writer) write(o interface{}) { + if w.err != nil { + return + } + + n, err := fmt.Fprint(w.w, o) + w.n += n + w.err = err +} + +func (w *writer) line(t int) { + w.write("\n") + for i := 0; i < t; i++ { + w.write("\t") + } } func nodeOf(parts ...interface{}) node { @@ -55,8 +102,13 @@ func config(name string, dflt int) int { func fprintValues(w io.Writer, o opts, v []interface{}) (int, error) { tab := config("TABWIDTH", 8) - cols0 := config("LINEWIDTH", 80-8) - cols1 := config("LINEWIDTH1", (cols0+8)*3/2-8) + cols0 := config("LINEWIDTH", 80-tab) + cols1 := config("LINEWIDTH1", (cols0+tab)*3/2-tab) + sortMaps := config("MAPSORT", 1) + if sortMaps == 0 { + o |= randomMaps + } + wr := &writer{w: w} for i, vi := range v { if wr.err != nil { diff --git a/reflect.go b/reflect.go index 30d5ba8..0b3a502 100644 --- a/reflect.go +++ b/reflect.go @@ -46,34 +46,51 @@ func reflectPrimitive(o opts, r reflect.Value, v interface{}, suppressType ...st return nodeOf(tn, "(", s, ")") } -func reflectNil(o opts, r reflect.Value) node { +func reflectNil(o opts, groupUnnamedType bool, r reflect.Value) node { if _, _, a := withType(o); !a { return nodeOf("nil") } - return nodeOf(reflectType(r.Type()), "(nil)") + rt := r.Type() + if groupUnnamedType && rt.Name() == "" { + return nodeOf("(", reflectType(rt), ")(nil)") + } + + return nodeOf(reflectType(rt), "(nil)") } func reflectItems(o opts, prefix string, r reflect.Value) node { - items := wrapper{sep: ", ", suffix: ","} - itemOpts := o | skipTypes - for i := 0; i < r.Len(); i++ { - items.items = append( - items.items, - reflectValue(itemOpts, r.Index(i)), - ) + typ := r.Type() + var items wrapper + if typ.Elem().Name() == "uint8" { + items = wrapper{sep: " ", mode: line} + for i := 0; i < r.Len(); i++ { + items.items = append( + items.items, + nodeOf(fmt.Sprintf("%02x", r.Index(i).Uint())), + ) + } + } else { + items = wrapper{sep: ", ", suffix: ","} + itemOpts := o | skipTypes + for i := 0; i < r.Len(); i++ { + items.items = append( + items.items, + reflectValue(itemOpts, r.Index(i)), + ) + } } if _, t, _ := withType(o); !t { return nodeOf(prefix, "{", items, "}") } - return nodeOf(reflectType(r.Type()), "{", items, "}") + return nodeOf(reflectType(typ), "{", items, "}") } func reflectHidden(o opts, hidden string, r reflect.Value) node { if r.IsNil() { - return reflectNil(o, r) + return reflectNil(o, true, r) } if _, t, _ := withType(o); !t { @@ -97,7 +114,7 @@ func reflectFunc(o opts, r reflect.Value) node { func reflectInterface(o opts, r reflect.Value) node { if r.IsNil() { - return reflectNil(o, r) + return reflectNil(o, false, r) } e := reflectValue(o, r.Elem()) @@ -115,7 +132,7 @@ func reflectInterface(o opts, r reflect.Value) node { func reflectMap(o opts, r reflect.Value) node { if r.IsNil() { - return reflectNil(o, r) + return reflectNil(o, true, r) } var ( @@ -140,7 +157,10 @@ func reflectMap(o opts, r reflect.Value) node { sn[skey] = nk } - sort.Strings(skeys) + if o&randomMaps == 0 { + sort.Strings(skeys) + } + for _, skey := range skeys { items.items = append( items.items, @@ -161,7 +181,7 @@ func reflectMap(o opts, r reflect.Value) node { func reflectPointer(o opts, r reflect.Value) node { if r.IsNil() { - return reflectNil(o, r) + return reflectNil(o, true, r) } e := reflectValue(o, r.Elem()) @@ -174,7 +194,7 @@ func reflectPointer(o opts, r reflect.Value) node { func reflectList(o opts, r reflect.Value) node { if r.IsNil() { - return reflectNil(o, r) + return reflectNil(o, true, r) } return reflectItems(o, "[]", r) @@ -221,7 +241,7 @@ func reflectString(o opts, r reflect.Value) node { } func reflectStruct(o opts, r reflect.Value) node { - wr := wrapper{sep: ", "} + wr := wrapper{sep: ", ", suffix: ","} fieldOpts := o | skipTypes rt := r.Type() @@ -249,7 +269,7 @@ func reflectStruct(o opts, r reflect.Value) node { func reflectUnsafePointer(o opts, r reflect.Value) node { if r.IsNil() { - return reflectNil(o, r) + return reflectNil(o, false, r) } if _, _, a := withType(o); !a { @@ -298,11 +318,9 @@ func reflectValue(o opts, r reflect.Value) node { return reflectList(o, r) case reflect.String: return reflectString(o, r) - case reflect.Struct: - return reflectStruct(o, r) case reflect.UnsafePointer: return reflectUnsafePointer(o, r) default: - return nodeOf("") + return reflectStruct(o, r) } } diff --git a/reflecttype.go b/reflecttype.go index b37fbb3..f798512 100644 --- a/reflecttype.go +++ b/reflecttype.go @@ -1,13 +1,17 @@ package notation -import ( - "reflect" -) +import "reflect" func reflectFuncBaseType(t reflect.Type) node { + isVariadic := t.IsVariadic() args := func(num func() int, typ func(int) reflect.Type) []node { var t []node for i := 0; i < num(); i++ { + if i == num()-1 && isVariadic { + t = append(t, nodeOf("...", reflectType(typ(i).Elem()))) + continue + } + t = append(t, reflectType(typ(i))) } @@ -21,14 +25,14 @@ func reflectFuncBaseType(t reflect.Type) node { if len(in) == 1 { n.parts = append(n.parts, in[0]) } else if len(in) > 1 { - n.parts = append(n.parts, wrapper{sep: ", ", items: in}) + n.parts = append(n.parts, wrapper{sep: ", ", suffix: ",", items: in}) } n.parts = append(n.parts, ")") if len(out) == 1 { n.parts = append(n.parts, " ", out[0]) } else if len(out) > 1 { - n.parts = append(n.parts, " (", wrapper{sep: ", ", items: out}, ")") + n.parts = append(n.parts, " (", wrapper{sep: ", ", suffix: ",", items: out}, ")") } return n @@ -103,7 +107,12 @@ func reflectStructType(t reflect.Type) node { func reflectType(t reflect.Type) node { if t.Name() != "" { - return nodeOf(t.Name()) + name := t.Name() + if name == "uint8" { + name = "byte" + } + + return nodeOf(name) } switch t.Kind() { @@ -121,9 +130,7 @@ func reflectType(t reflect.Type) node { return reflectPointerType(t) case reflect.Slice: return reflectListType(t) - case reflect.Struct: - return reflectStructType(t) default: - return nodeOf("") + return reflectStructType(t) } } diff --git a/sprint_test.go b/sprint_test.go index 35b0505..8dc5e56 100644 --- a/sprint_test.go +++ b/sprint_test.go @@ -1,32 +1,85 @@ package notation import ( + "fmt" + "strings" "testing" "unsafe" ) -func TestSprint(t *testing.T) { - type ( - myBool bool - myInt int - myFloat float64 - myComplex complex64 - myArray [3]int - myChannel chan int - myFunction func(int, int) int - myMap map[int]int - myPointer *int - myList []int - myString string - myStruct struct{ field interface{} } - myUnsafePointer unsafe.Pointer - ) +type test struct { + title string + value interface{} + expect string +} - for _, test := range []struct { - title string - value interface{} - expect string - }{ +type tests []test + +type ( + myBool bool + myInt int + myFloat float64 + myComplex complex64 + myArray [3]int + myChannel chan int + myFunction func(int, int) int + myMap map[int]int + myPointer *int + myList []int + myString string + myStruct struct{ field interface{} } + myUnsafePointer unsafe.Pointer +) + +func (test test) run(t *testing.T, sprint func(...interface{}) string) { + t.Run(test.title, func(t *testing.T) { + s := sprint(test.value) + if s != test.expect { + t.Fatalf("expected: %s, got: %s", test.expect, s) + } + }) +} + +func (tests tests) run(t *testing.T, sprint func(...interface{}) string) { + logEnv := func(name string, dflt int) { + t.Logf("%s=%d", name, config(name, dflt)) + } + + logEnv("TABWIDTH", 8) + logEnv("LINEWIDTH", 80-8) + logEnv("LINEWIDTH1", 80*3/2-8) + + for _, ti := range tests { + ti.run(t, sprint) + } +} + +func (t tests) expect(expect map[string]string) tests { + var set tests + for _, test := range t { + if expect, doSet := expect[test.title]; doSet { + test.expect = expect + } + + set = append(set, test) + } + + return set +} + +func expectedMaxWidth(t []test) int { + var w int + for _, test := range t { + if len(test.expect) > w { + w = len(test.expect) + } + } + + return w +} + +func defaultSet() tests { + return []test{ {"nil", nil, "nil"}, {"false", false, "false"}, {"true", true, "true"}, @@ -71,6 +124,8 @@ func TestSprint(t *testing.T) { {"custom list", myList{1, 2, 3}, "[]{1, 2, 3}"}, {"nil list", struct{ l []int }{}, "{l: nil}"}, {"nil custom list", struct{ l myList }{}, "{l: nil}"}, + {"long item", []string{"foobarbazqux"}, "[]{\"foobarbazqux\"}"}, + {"long subitem", []struct{ foo string }{{foo: "foobarbazqux"}}, "[]{{foo: \"foobarbazqux\"}}"}, {"string", "\\\"\b\f\n\r\t\vfoo", "\"\\\\\\\"\\b\\f\\n\\r\\t\\vfoo\""}, {"custom string", myString("\\\"\b\f\n\r\t\vfoo"), "\"\\\\\\\"\\b\\f\\n\\r\\t\\vfoo\""}, {"structure", struct{ foo int }{42}, "{foo: 42}"}, @@ -78,179 +133,822 @@ func TestSprint(t *testing.T) { {"unsafe pointer", unsafe.Pointer(&struct{}{}), "pointer"}, {"custom unsafe pointer", myUnsafePointer(&struct{}{}), "pointer"}, {"unsafe pointer type", struct{ p unsafe.Pointer }{}, "{p: nil}"}, - } { - t.Run(test.title, func(t *testing.T) { - s := Sprint(test.value) - if s != test.expect { - t.Fatalf("expected: %s, got: %s", test.expect, s) - } - }) } } +func (t tests) expectTypes() tests { + return t.expect(map[string]string{ + "custom false": "myBool(false)", + "custom true": "myBool(true)", + "custom int": "myInt(42)", + "uint": "uint(42)", + "byte": "byte(42)", + "round float": "float64(42)", + "custom round float": "myFloat(42)", + "float with fraction": "float64(1.8)", + "custom float with fraction": "myFloat(1.8)", + "complex": "complex128(2+3i)", + "custom complex": "myComplex(2+3i)", + "imaginary": "complex128(0+3i)", + "custom imaginary": "myComplex(0+3i)", + "array": "[3]int{1, 2, 3}", + "custom array": "myArray{1, 2, 3}", + "channel": "chan int", + "custom channel": "myChannel", + "nil channel": "struct{c chan int}{c: nil}", + "nil custom channel": "struct{c myChannel}{c: nil}", + "receive channel": "struct{c <-chan int}{c: chan}", + "send channel": "struct{c chan<- int}{c: chan}", + "function with args": "func(int, int) int", + "custom function with args": "myFunction", + "function with multiple return args": "func(int, int) (int, int)", + "nil function": "struct{f func(int) int}{f: nil}", + "interface type": "struct{i interface{foo()}}{i: nil}", + "map": "map[int]int{24: 42}", + "custom map": "myMap{24: 42}", + "nil map": "struct{m map[int]int}{m: nil}", + "nil custom map": "struct{m myMap}{m: nil}", + "pointer": "*struct{}{}", + "custom pointer": "*myStruct{field: nil}", + "nil pointer": "struct{p *int}{p: nil}", + "nil custom pointer": "struct{p myPointer}{p: nil}", + "list": "[]int{1, 2, 3}", + "custom list": "myList{1, 2, 3}", + "nil list": "struct{l []int}{l: nil}", + "nil custom list": "struct{l myList}{l: nil}", + "long item": "[]string{\"foobarbazqux\"}", + "long subitem": "[]struct{foo string}{{foo: \"foobarbazqux\"}}", + "custom string": "myString(\"\\\\\\\"\\b\\f\\n\\r\\t\\vfoo\")", + "structure": "struct{foo int}{foo: 42}", + "custom structure": "myStruct{field: 42}", + "unsafe pointer type": "struct{p Pointer}{p: nil}", + }) +} + +func (t tests) expectVerboseTypes() tests { + return t.expectTypes().expect(map[string]string{ + "false": "bool(false)", + "true": "bool(true)", + "int": "int(42)", + "negative int": "int(-42)", + "array": "[3]int{int(1), int(2), int(3)}", + "custom array": "myArray{int(1), int(2), int(3)}", + "nil channel": "struct{c chan int}{c: (chan int)(nil)}", + "nil custom channel": "struct{c myChannel}{c: myChannel(nil)}", + "receive channel": "struct{c <-chan int}{c: <-chan int}", + "send channel": "struct{c chan<- int}{c: chan<- int}", + "custom function with args": "myFunction", + "function with multiple return args": "func(int, int) (int, int)", + "nil function": "struct{f func(int) int}{f: (func(int) int)(nil)}", + "interface type": "struct{i interface{foo()}}{i: interface{foo()}(nil)}", + "map": "map[int]int{int(24): int(42)}", + "custom map": "myMap{int(24): int(42)}", + "nil map": "struct{m map[int]int}{m: (map[int]int)(nil)}", + "nil custom map": "struct{m myMap}{m: myMap(nil)}", + "custom pointer": "*myStruct{field: interface{}(nil)}", + "nil pointer": "struct{p *int}{p: (*int)(nil)}", + "nil custom pointer": "struct{p myPointer}{p: myPointer(nil)}", + "list": "[]int{int(1), int(2), int(3)}", + "custom list": "myList{int(1), int(2), int(3)}", + "nil list": "struct{l []int}{l: ([]int)(nil)}", + "nil custom list": "struct{l myList}{l: myList(nil)}", + "long item": "[]string{string(\"foobarbazqux\")}", + "long subitem": "[]struct{foo string}{struct{foo string}{foo: string(\"foobarbazqux\")}}", + "string": "string(\"\\\\\\\"\\b\\f\\n\\r\\t\\vfoo\")", + "structure": "struct{foo int}{foo: int(42)}", + "custom structure": "myStruct{field: interface{}(int(42))}", + "unsafe pointer": "Pointer(pointer)", + "custom unsafe pointer": "myUnsafePointer(pointer)", + "unsafe pointer type": "struct{p Pointer}{p: Pointer(nil)}", + }) +} + +func (t tests) expectWrapAll() tests { + return t.expect(map[string]string{ + "array": `[3]{ + 1, + 2, + 3, +}`, + "custom array": `[3]{ + 1, + 2, + 3, +}`, + "nil channel": `{ + c: nil, +}`, + "nil custom channel": `{ + c: nil, +}`, + "receive channel": `{ + c: chan, +}`, + "send channel": `{ + c: chan, +}`, + "nil function": `{ + f: nil, +}`, + "interface type": `{ + i: nil, +}`, + "map": `map{ + 24: 42, +}`, + "custom map": `map{ + 24: 42, +}`, + "nil map": `{ + m: nil, +}`, + "nil custom map": `{ + m: nil, +}`, + "custom pointer": `{ + field: nil, +}`, + "nil pointer": `{ + p: nil, +}`, + "nil custom pointer": `{ + p: nil, +}`, + "list": `[]{ + 1, + 2, + 3, +}`, + "custom list": `[]{ + 1, + 2, + 3, +}`, + "nil list": `{ + l: nil, +}`, + "nil custom list": `{ + l: nil, +}`, + "long item": "[]{\n\t\"foobarbazqux\",\n}", + "long subitem": `[]{ + { + foo: "foobarbazqux", + }, +}`, + "structure": `{ + foo: 42, +}`, + "custom structure": `{ + field: 42, +}`, + "unsafe pointer type": `{ + p: nil, +}`, + }) +} + +func (t tests) expectOnlyLongWrapped() tests { + return t.expect(map[string]string{ + "long item": `[]{ + "foobarbazqux", +}`, + "long subitem": `[]{ + {foo: "foobarbazqux"}, +}`, + }) +} + +func (t tests) expectWrapAllWithTypes() tests { + return t.expectTypes().expect(map[string]string{ + "array": `[3]int{ + 1, + 2, + 3, +}`, + "custom array": `myArray{ + 1, + 2, + 3, +}`, + "nil channel": `struct{ + c chan int +}{ + c: nil, +}`, + "nil custom channel": `struct{ + c myChannel +}{ + c: nil, +}`, + "receive channel": `struct{ + c <-chan int +}{ + c: chan, +}`, + "send channel": `struct{ + c chan<- int +}{ + c: chan, +}`, + "function with args": `func( + int, + int, +) int`, + "function with multiple return args": `func( + int, + int, +) ( + int, + int, +)`, + "nil function": `struct{ + f func(int) int +}{ + f: nil, +}`, + "interface type": `struct{ + i interface{ + foo() + } +}{ + i: nil, +}`, + "map": `map[int]int{ + 24: 42, +}`, + "custom map": `myMap{ + 24: 42, +}`, + "nil map": `struct{ + m map[int]int +}{ + m: nil, +}`, + "nil custom map": `struct{ + m myMap +}{ + m: nil, +}`, + "custom pointer": `*myStruct{ + field: nil, +}`, + "nil pointer": `struct{ + p *int +}{ + p: nil, +}`, + "nil custom pointer": `struct{ + p myPointer +}{ + p: nil, +}`, + "list": `[]int{ + 1, + 2, + 3, +}`, + "custom list": `myList{ + 1, + 2, + 3, +}`, + "nil list": `struct{ + l []int +}{ + l: nil, +}`, + "nil custom list": `struct{ + l myList +}{ + l: nil, +}`, + "long item": `[]string{ + "foobarbazqux", +}`, + "long subitem": `[]struct{ + foo string +}{ + { + foo: "foobarbazqux", + }, +}`, + "custom string": "myString(\n\t\"\\\\\\\"\\b\\f\\n\\r\\t\\vfoo\"\n)", + "structure": `struct{ + foo int +}{ + foo: 42, +}`, + "custom structure": `myStruct{ + field: 42, +}`, + "unsafe pointer type": `struct{ + p Pointer +}{ + p: nil, +}`, + }) +} + +func (t tests) expectOnlyLongWrappedWithTypes() tests { + return t.expectTypes().expect(map[string]string{ + "nil channel": `struct{c chan int}{ + c: nil, +}`, + "nil custom channel": `struct{c myChannel}{ + c: nil, +}`, + "receive channel": `struct{c <-chan int}{ + c: chan, +}`, + "send channel": `struct{c chan<- int}{ + c: chan, +}`, + "nil function": `struct{ + f func(int) int +}{ + f: nil, +}`, + "interface type": `struct{ + i interface{foo()} +}{ + i: nil, +}`, + "nil map": `struct{m map[int]int}{ + m: nil, +}`, + "nil custom map": `struct{m myMap}{ + m: nil, +}`, + "nil pointer": `struct{p *int}{ + p: nil, +}`, + "nil custom pointer": `struct{p myPointer}{ + p: nil, +}`, + "nil list": `struct{l []int}{ + l: nil, +}`, + "nil custom list": `struct{l myList}{ + l: nil, +}`, + "long item": `[]string{ + "foobarbazqux", +}`, + "long subitem": `[]struct{foo string}{ + {foo: "foobarbazqux"}, +}`, + "custom string": "myString(\n\t\"\\\\\\\"\\b\\f\\n\\r\\t\\vfoo\"\n)", + "structure": `struct{foo int}{ + foo: 42, +}`, + "unsafe pointer type": `struct{p Pointer}{ + p: nil, +}`, + }) +} + +func (t tests) expectWrapAllWithVerboseTypes() tests { + return t.expectVerboseTypes().expect(map[string]string{ + "array": `[3]int{ + int(1), + int(2), + int(3), +}`, + "custom array": `myArray{ + int(1), + int(2), + int(3), +}`, + "nil channel": `struct{ + c chan int +}{ + c: (chan int)(nil), +}`, + "nil custom channel": `struct{ + c myChannel +}{ + c: myChannel(nil), +}`, + "receive channel": `struct{ + c <-chan int +}{ + c: <-chan int, +}`, + "send channel": `struct{ + c chan<- int +}{ + c: chan<- int, +}`, + "function with args": `func( + int, + int, +) int`, + "function with multiple return args": `func( + int, + int, +) ( + int, + int, +)`, + "nil function": `struct{ + f func(int) int +}{ + f: (func(int) int)(nil), +}`, + "interface type": `struct{ + i interface{ + foo() + } +}{ + i: interface{ + foo() + }(nil), +}`, + "map": `map[int]int{ + int(24): int(42), +}`, + "custom map": `myMap{ + int(24): int(42), +}`, + "nil map": `struct{ + m map[int]int +}{ + m: (map[int]int)(nil), +}`, + "nil custom map": `struct{ + m myMap +}{ + m: myMap(nil), +}`, + "custom pointer": `*myStruct{ + field: interface{}(nil), +}`, + "nil pointer": `struct{ + p *int +}{ + p: (*int)(nil), +}`, + "nil custom pointer": `struct{ + p myPointer +}{ + p: myPointer(nil), +}`, + "list": `[]int{ + int(1), + int(2), + int(3), +}`, + "custom list": `myList{ + int(1), + int(2), + int(3), +}`, + "nil list": `struct{ + l []int +}{ + l: ([]int)(nil), +}`, + "nil custom list": `struct{ + l myList +}{ + l: myList(nil), +}`, + "long item": `[]string{ + string( + "foobarbazqux" + ), +}`, + "long subitem": `[]struct{ + foo string +}{ + struct{ + foo string + }{ + foo: string( + "foobarbazqux" + ), + }, +}`, + "string": "string(\n\t\"\\\\\\\"\\b\\f\\n\\r\\t\\vfoo\"\n)", + "custom string": "myString(\n\t\"\\\\\\\"\\b\\f\\n\\r\\t\\vfoo\"\n)", + "structure": `struct{ + foo int +}{ + foo: int(42), +}`, + "custom structure": `myStruct{ + field: interface{}( + int(42) + ), +}`, + "unsafe pointer type": `struct{ + p Pointer +}{ + p: Pointer(nil), +}`, + }) +} + +func (t tests) expectOnlyLongWrappedWithVerboseTypes() tests { + return t.expectVerboseTypes().expect(map[string]string{ + "nil channel": `struct{c chan int}{ + c: (chan int)(nil), +}`, + "nil custom channel": `struct{c myChannel}{ + c: myChannel(nil), +}`, + "receive channel": `struct{c <-chan int}{ + c: <-chan int, +}`, + "send channel": `struct{c chan<- int}{ + c: chan<- int, +}`, + "nil function": `struct{f func(int) int}{ + f: (func(int) int)(nil), +}`, + "interface type": `struct{i interface{foo()}}{ + i: interface{foo()}(nil), +}`, + "nil map": `struct{m map[int]int}{ + m: (map[int]int)(nil), +}`, + "long subitem": `[]struct{foo string}{ + struct{foo string}{ + foo: string("foobarbazqux"), + }, +}`, + "nil custom pointer": `struct{p myPointer}{ + p: myPointer(nil), +}`, + "custom structure": `myStruct{ + field: interface{}(int(42)), +}`, + }) +} + +func TestSprint(t *testing.T) { + defaultSet().run(t, Sprint) +} + func TestSprintt(t *testing.T) { - type ( - myBool bool - myInt int - myFloat float64 - myComplex complex64 - myArray [3]int - myChannel chan int - myFunction func(int, int) int - myMap map[int]int - myPointer *int - myList []int - myString string - myStruct struct{ field interface{} } - myUnsafePointer unsafe.Pointer - ) - - for _, test := range []struct { - title string - value interface{} - expect string - }{ - {"nil", nil, "nil"}, - {"false", false, "false"}, - {"true", true, "true"}, - {"custom false", myBool(false), "myBool(false)"}, - {"custom true", myBool(true), "myBool(true)"}, - {"int", 42, "42"}, - {"negative int", -42, "-42"}, - {"custom int", myInt(42), "myInt(42)"}, - {"uint", uint(42), "uint(42)"}, - {"byte", byte(42), "uint8(42)"}, - {"round float", float64(42), "float64(42)"}, - {"custom round float", myFloat(42), "myFloat(42)"}, - {"float with fraction", 1.8, "float64(1.8)"}, - {"custom float with fraction", myFloat(1.8), "myFloat(1.8)"}, - {"complex", 2 + 3i, "complex128(2+3i)"}, - {"custom complex", myComplex(2 + 3i), "myComplex(2+3i)"}, - {"imaginary", 3i, "complex128(0+3i)"}, - {"custom imaginary", myComplex(3i), "myComplex(0+3i)"}, - {"array", [...]int{1, 2, 3}, "[3]int{1, 2, 3}"}, - {"custom array", myArray{1, 2, 3}, "myArray{1, 2, 3}"}, - {"channel", make(chan int), "chan int"}, - {"custom channel", make(myChannel), "myChannel"}, - {"nil channel", struct{ c chan int }{}, "struct{c chan int}{c: nil}"}, - {"nil custom channel", struct{ c myChannel }{}, "struct{c myChannel}{c: nil}"}, - {"receive channel", struct{ c <-chan int }{make(chan int)}, "struct{c <-chan int}{c: chan}"}, - {"send channel", struct{ c chan<- int }{make(chan int)}, "struct{c chan<- int}{c: chan}"}, - {"function", func() {}, "func()"}, - {"function with args", func(int, int) int { return 0 }, "func(int, int) int"}, - {"custom function with args", myFunction(func(int, int) int { return 0 }), "myFunction"}, - {"function with multiple return args", func(int, int) (int, int) { return 0, 0 }, "func(int, int) (int, int)"}, - {"nil function", struct{ f func(int) int }{}, "struct{f func(int) int}{f: nil}"}, - {"interface type", struct{ i interface{ foo() } }{}, "struct{i interface{foo()}}{i: nil}"}, - {"map", map[int]int{24: 42}, "map[int]int{24: 42}"}, - {"custom map", myMap{24: 42}, "myMap{24: 42}"}, - {"nil map", struct{ m map[int]int }{}, "struct{m map[int]int}{m: nil}"}, - {"nil custom map", struct{ m myMap }{}, "struct{m myMap}{m: nil}"}, - {"pointer", &struct{}{}, "*struct{}{}"}, - {"custom pointer", &myStruct{}, "*myStruct{field: nil}"}, - {"nil pointer", struct{ p *int }{}, "struct{p *int}{p: nil}"}, - {"nil custom pointer", struct{ p myPointer }{}, "struct{p myPointer}{p: nil}"}, - {"list", []int{1, 2, 3}, "[]int{1, 2, 3}"}, - {"custom list", myList{1, 2, 3}, "myList{1, 2, 3}"}, - {"nil list", struct{ l []int }{}, "struct{l []int}{l: nil}"}, - {"nil custom list", struct{ l myList }{}, "struct{l myList}{l: nil}"}, - {"string", "\\\"\b\f\n\r\t\vfoo", "\"\\\\\\\"\\b\\f\\n\\r\\t\\vfoo\""}, - {"custom string", myString("\\\"\b\f\n\r\t\vfoo"), "myString(\"\\\\\\\"\\b\\f\\n\\r\\t\\vfoo\")"}, - {"structure", struct{ foo int }{42}, "struct{foo int}{foo: 42}"}, - {"custom structure", myStruct{42}, "myStruct{field: 42}"}, - {"unsafe pointer", unsafe.Pointer(&struct{}{}), "pointer"}, - {"custom unsafe pointer", myUnsafePointer(&struct{}{}), "pointer"}, - {"unsafe pointer type", struct{ p unsafe.Pointer }{}, "struct{p Pointer}{p: nil}"}, - } { - t.Run(test.title, func(t *testing.T) { - s := Sprintt(test.value) - if s != test.expect { - t.Fatalf("expected: %s, got: %s", test.expect, s) - } - }) - } + defaultSet().expectTypes().run(t, Sprintt) } func TestSprintv(t *testing.T) { - type ( - myBool bool - myInt int - myFloat float64 - myComplex complex64 - myArray [3]int - myChannel chan int - myFunction func(int, int) int - myMap map[int]int - myPointer *int - myList []int - myString string - myStruct struct{ field interface{} } - myUnsafePointer unsafe.Pointer - ) + defaultSet().expectTypes().expectVerboseTypes().run(t, Sprintv) +} - for _, test := range []struct { - title string - value interface{} - expect string - }{ - {"nil", nil, "nil"}, - {"false", false, "bool(false)"}, - {"true", true, "bool(true)"}, - {"custom false", myBool(false), "myBool(false)"}, - {"custom true", myBool(true), "myBool(true)"}, - {"int", 42, "int(42)"}, - {"negative int", -42, "int(-42)"}, - {"custom int", myInt(42), "myInt(42)"}, - {"uint", uint(42), "uint(42)"}, - {"byte", byte(42), "uint8(42)"}, - {"round float", float64(42), "float64(42)"}, - {"custom round float", myFloat(42), "myFloat(42)"}, - {"float with fraction", 1.8, "float64(1.8)"}, - {"custom float with fraction", myFloat(1.8), "myFloat(1.8)"}, - {"complex", 2 + 3i, "complex128(2+3i)"}, - {"custom complex", myComplex(2 + 3i), "myComplex(2+3i)"}, - {"imaginary", 3i, "complex128(0+3i)"}, - {"custom imaginary", myComplex(3i), "myComplex(0+3i)"}, - {"array", [...]int{1, 2, 3}, "[3]int{int(1), int(2), int(3)}"}, - {"custom array", myArray{1, 2, 3}, "myArray{int(1), int(2), int(3)}"}, - {"channel", make(chan int), "chan int"}, - {"custom channel", make(myChannel), "myChannel"}, - {"nil channel", struct{ c chan int }{}, "struct{c chan int}{c: chan int(nil)}"}, - {"nil custom channel", struct{ c myChannel }{}, "struct{c myChannel}{c: myChannel(nil)}"}, - {"receive channel", struct{ c <-chan int }{make(chan int)}, "struct{c <-chan int}{c: <-chan int}"}, - {"send channel", struct{ c chan<- int }{make(chan int)}, "struct{c chan<- int}{c: chan<- int}"}, - {"function", func() {}, "func()"}, - {"function with args", func(int, int) int { return 0 }, "func(int, int) int"}, - {"custom function with args", myFunction(func(int, int) int { return 0 }), "myFunction"}, - {"function with multiple return args", func(int, int) (int, int) { return 0, 0 }, "func(int, int) (int, int)"}, - {"nil function", struct{ f func(int) int }{}, "struct{f func(int) int}{f: func(int) int(nil)}"}, - {"interface type", struct{ i interface{ foo() } }{}, "struct{i interface{foo()}}{i: interface{foo()}(nil)}"}, - {"map", map[int]int{24: 42}, "map[int]int{int(24): int(42)}"}, - {"custom map", myMap{24: 42}, "myMap{int(24): int(42)}"}, - {"nil map", struct{ m map[int]int }{}, "struct{m map[int]int}{m: map[int]int(nil)}"}, - {"nil custom map", struct{ m myMap }{}, "struct{m myMap}{m: myMap(nil)}"}, - {"pointer", &struct{}{}, "*struct{}{}"}, - {"custom pointer", &myStruct{}, "*myStruct{field: interface{}(nil)}"}, - {"nil pointer", struct{ p *int }{}, "struct{p *int}{p: *int(nil)}"}, - {"nil custom pointer", struct{ p myPointer }{}, "struct{p myPointer}{p: myPointer(nil)}"}, - {"list", []int{1, 2, 3}, "[]int{int(1), int(2), int(3)}"}, - {"custom list", myList{1, 2, 3}, "myList{int(1), int(2), int(3)}"}, - {"nil list", struct{ l []int }{}, "struct{l []int}{l: []int(nil)}"}, - {"nil custom list", struct{ l myList }{}, "struct{l myList}{l: myList(nil)}"}, - {"string", "\\\"\b\f\n\r\t\vfoo", "string(\"\\\\\\\"\\b\\f\\n\\r\\t\\vfoo\")"}, - {"custom string", myString("\\\"\b\f\n\r\t\vfoo"), "myString(\"\\\\\\\"\\b\\f\\n\\r\\t\\vfoo\")"}, - {"structure", struct{ foo int }{42}, "struct{foo int}{foo: int(42)}"}, - {"custom structure", myStruct{42}, "myStruct{field: interface{}(int(42))}"}, - {"custom structure, nil field", myStruct{}, "myStruct{field: interface{}(nil)}"}, - {"unsafe pointer", unsafe.Pointer(&struct{}{}), "Pointer(pointer)"}, - {"custom unsafe pointer", myUnsafePointer(&struct{}{}), "myUnsafePointer(pointer)"}, - {"unsafe pointer type", struct{ p unsafe.Pointer }{}, "struct{p Pointer}{p: Pointer(nil)}"}, - } { - t.Run(test.title, func(t *testing.T) { - s := Sprintv(test.value) - if s != test.expect { - t.Fatalf("expected: %s, got: %s", test.expect, s) - } - }) +func TestSprintw(t *testing.T) { + t.Run("fit everything on a single line", func(t *testing.T) { + defer withEnv( + t, + "TABWIDTH=8", + fmt.Sprintf("LINEWIDTH=%d", expectedMaxWidth(defaultSet())), + fmt.Sprintf("LINEWIDTH1=%d", expectedMaxWidth(defaultSet())), + )() + + defaultSet().run(t, Sprintw) + }) + + t.Run("wrap everything", func(t *testing.T) { + defer withEnv(t, "TABWIDTH=0", "LINEWIDTH=0", "LINEWIDTH1=0")() + defaultSet().expectWrapAll().run(t, Sprintw) + }) + + t.Run("wrap long expressions only", func(t *testing.T) { + defer withEnv( + t, + "TABWIDTH=2", + fmt.Sprintf("LINEWIDTH=%d", expectedMaxWidth(defaultSet())/2), + fmt.Sprintf("LINEWIDTH1=%d", expectedMaxWidth(defaultSet())/2), + )() + + defaultSet().expectOnlyLongWrapped().run(t, Sprintw) + }) + + t.Run("wrap with tolerance", func(t *testing.T) { + defer withEnv(t, "TABWIDTH=2", "LINEWIDTH=9", "LINEWIDTH1=12")() + tests{{title: "list", value: []int{1, 2, 3}, expect: "[]{1, 2, 3}"}}.run(t, Sprintw) + }) +} + +func TestSprintwt(t *testing.T) { + t.Run("fit everything on a single line", func(t *testing.T) { + defer withEnv( + t, + "TABWIDTH=8", + fmt.Sprintf("LINEWIDTH=%d", expectedMaxWidth(defaultSet().expectTypes())), + fmt.Sprintf("LINEWIDTH1=%d", expectedMaxWidth(defaultSet().expectTypes())), + )() + + defaultSet().expectTypes().run(t, Sprintwt) + }) + + t.Run("wrap everything", func(t *testing.T) { + defer withEnv(t, "TABWIDTH=0", "LINEWIDTH=0", "LINEWIDTH1=0")() + defaultSet().expectWrapAllWithTypes().run(t, Sprintwt) + }) + + t.Run("wrap long expressions only", func(t *testing.T) { + defer withEnv( + t, + "TABWIDTH=2", + fmt.Sprintf("LINEWIDTH=%d", expectedMaxWidth(defaultSet().expectTypes())/2), + fmt.Sprintf("LINEWIDTH1=%d", expectedMaxWidth(defaultSet().expectTypes())/2), + )() + + defaultSet().expectOnlyLongWrappedWithTypes().run(t, Sprintwt) + }) + + t.Run("wrap with tolerance", func(t *testing.T) { + defer withEnv(t, "TABWIDTH=2", "LINEWIDTH=12", "LINEWIDTH1=15")() + tests{{title: "list", value: []int{1, 2, 3}, expect: "[]int{1, 2, 3}"}}.run(t, Sprintwt) + }) +} + +func TestSprintwv(t *testing.T) { + t.Run("fit everything on a single line", func(t *testing.T) { + defer withEnv( + t, + "TABWIDTH=8", + fmt.Sprintf("LINEWIDTH=%d", expectedMaxWidth(defaultSet().expectVerboseTypes())), + fmt.Sprintf("LINEWIDTH1=%d", expectedMaxWidth(defaultSet().expectVerboseTypes())), + )() + + defaultSet().expectVerboseTypes().run(t, Sprintwv) + }) + + t.Run("wrap everything", func(t *testing.T) { + defer withEnv(t, "TABWIDTH=0", "LINEWIDTH=0", "LINEWIDTH1=0")() + defaultSet().expectWrapAllWithVerboseTypes().run(t, Sprintwv) + }) + + t.Run("wrap long expressions only", func(t *testing.T) { + defer withEnv( + t, + "TABWIDTH=2", + fmt.Sprintf("LINEWIDTH=%d", expectedMaxWidth(defaultSet().expectVerboseTypes())/2), + fmt.Sprintf("LINEWIDTH1=%d", expectedMaxWidth(defaultSet().expectVerboseTypes())/2), + )() + + defaultSet().expectOnlyLongWrappedWithVerboseTypes().run(t, Sprintwv) + }) + + t.Run("wrap with tolerance", func(t *testing.T) { + defer withEnv(t, "TABWIDTH=2", "LINEWIDTH=27", "LINEWIDTH1=30")() + tests{{ + title: "list", + value: []int{1, 2, 3}, + expect: "[]int{int(1), int(2), int(3)}", + }}.run(t, Sprintwv) + }) +} + +func TestNoLonger(t *testing.T) { + const expect = `[]{"foobarbaz"}` + o := []string{"foobarbaz"} + defer withEnv(t, "TABWIDTH=8", "LINEWIDTH=9", "LINEWIDTH1=9")() + s := Sprintw(o) + if s != expect { + t.Fatalf("expected: %s, got: %s", expect, s) + } +} + +func TestSprintMultipleObjects(t *testing.T) { + t.Run("single line", func(t *testing.T) { + const expect = "{fooBarBaz: 42} {fooBarBaz: 42}" + o := struct{ fooBarBaz int }{42} + s := Sprint(o, o) + if s != expect { + t.Fatalf("expected: %s, got: %s", expect, s) + } + }) + + t.Run("multiple lines", func(t *testing.T) { + const expect = "{fooBarBaz: 42}\n{fooBarBaz: 42}" + o := struct{ fooBarBaz int }{42} + s := Sprintw(o, o) + if s != expect { + t.Fatalf("expected: %s, got: %s", expect, s) + } + }) +} + +func TestInvalidEasterEgg(t *testing.T) { + defer withEnv(t, "TABWIDTH=foo")() + const expect = "{fooBarBaz: 42}" + o := struct{ fooBarBaz int }{42} + s := Sprintw(o) + if s != expect { + t.Fatalf("expected: %s, got: %s", expect, s) + } +} + +func TestAnonymousFields(t *testing.T) { + type Foo struct{ Bar int } + type Baz struct{ Foo } + o := Baz{Foo: Foo{Bar: 42}} + t.Run("without types", func(t *testing.T) { + const expect = `{Foo: {Bar: 42}}` + s := Sprint(o) + if s != expect { + t.Fatalf("expected: %s, got: %s", expect, s) + } + }) + + t.Run("with types", func(t *testing.T) { + const expect = `Baz{Foo: {Bar: 42}}` + s := Sprintt(o) + if s != expect { + t.Fatalf("expected: %s, got: %s", expect, s) + } + }) + + t.Run("with verbose types", func(t *testing.T) { + const expect = `Baz{Foo: Foo{Bar: int(42)}}` + s := Sprintv(o) + if s != expect { + t.Fatalf("expected: %s, got: %s", expect, s) + } + }) +} + +func TestVariadicFunction(t *testing.T) { + const expect = `func(...int)` + f := func(...int) {} + s := Sprintt(f) + if s != expect { + t.Fatalf("expected: %s, got: %s", expect, s) + } +} + +func TestSortedMap(t *testing.T) { + const testCount = 9 + m := map[int]int{1: 2, 3: 4, 5: 6} + t.Run("sorted", func(t *testing.T) { + const expect = `map{1: 2, 3: 4, 5: 6}` + for i := 0; i < testCount; i++ { + s := Sprint(m) + if s != expect { + t.Fatalf("expected: %s, got: %s", expect, s) + } + } + }) + + t.Run("random", func(t *testing.T) { + defer withEnv(t, "MAPSORT=0")() + for i := 0; i < testCount; i++ { + s := Sprint(m) + for _, entry := range []string{"1: 2", "3: 4", "5: 6"} { + if !strings.Contains(s, entry) { + t.Fatalf("missing entry: %s", entry) + } + } + } + }) +} + +func TestBytes(t *testing.T) { + const expectNotWrapped = `[]{00 01 02 03 04 05 06 07 08 09 0a 0b}` + const expectWrapped = `[]{ + 00 01 02 03 04 05 + 06 07 08 09 0a 0b +}` + + b := []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11} + t.Run("not wrapped", func(t *testing.T) { + s := Sprint(b) + if s != expectNotWrapped { + t.Fatalf("expected: %s, got: %s", expectNotWrapped, s) + } + }) + + t.Run("wrapped", func(t *testing.T) { + defer withEnv( + t, + "TABWIDTH=2", + fmt.Sprintf("LINEWIDTH=%d", len(expectNotWrapped)/2), + fmt.Sprintf("LINEWIDTH1=%d", len(expectNotWrapped)/2), + )() + + s := Sprintw(b) + if s != expectWrapped { + t.Fatalf("expected: %s, got: %s", expectWrapped, s) + } + }) +} + +func TestNonWrapperNodes(t *testing.T) { + const expect = `map[struct{foo int; bar int}]struct{ + foo int + bar int +}{}` + + defer withEnv(t, "TABWIDTH=2", "LINEWIDTH=27", "LINEWIDTH1=30")() + o := map[struct{foo int; bar int}]struct{foo int; bar int}{} + s := Sprintwt(o) + if s != expect { + t.Fatalf("expected: %s, got: %s", expect, s) } }