| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256 |
- // Copyright (c) Tailscale Inc & AUTHORS
- // SPDX-License-Identifier: BSD-3-Clause
- package tstest
- import (
- "strings"
- "testing"
- "github.com/google/go-cmp/cmp"
- )
- func TestPrintGoroutines(t *testing.T) {
- tests := []struct {
- name string
- in string
- want string
- }{
- {
- name: "empty",
- in: "goroutine profile: total 0\n",
- want: "goroutine profile: total 0",
- },
- {
- name: "single goroutine",
- in: `goroutine profile: total 1
- 1 @ 0x47bc0e 0x458e57 0x847587 0x483da1
- # 0x847586 database/sql.(*DB).connectionOpener+0x86 database/sql/sql.go:1261
- `,
- want: `goroutine profile: total 1
- 1 @ 0x47bc0e 0x458e57 0x847587 0x483da1
- # 0x847586 database/sql.(*DB).connectionOpener+0x86 database/sql/sql.go:1261
- `,
- },
- {
- name: "multiple goroutines sorted",
- in: `goroutine profile: total 14
- 7 @ 0x47bc0e 0x413705 0x4132b2 0x10fda4d 0x483da1
- # 0x10fda4c github.com/user/pkg.RoutineA+0x16c pkg/a.go:443
- 7 @ 0x47bc0e 0x458e57 0x754927 0x483da1
- # 0x754926 net/http.(*persistConn).writeLoop+0xe6 net/http/transport.go:2596
- `,
- want: `goroutine profile: total 14
- 7 @ 0x47bc0e 0x413705 0x4132b2 0x10fda4d 0x483da1
- # 0x10fda4c github.com/user/pkg.RoutineA+0x16c pkg/a.go:443
- 7 @ 0x47bc0e 0x458e57 0x754927 0x483da1
- # 0x754926 net/http.(*persistConn).writeLoop+0xe6 net/http/transport.go:2596
- `,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- got := string(printGoroutines(parseGoroutines([]byte(tt.in))))
- if got != tt.want {
- t.Errorf("printGoroutines() = %q, want %q, diff:\n%s", got, tt.want, cmp.Diff(tt.want, got))
- }
- })
- }
- }
- func TestDiffPprofGoroutines(t *testing.T) {
- tests := []struct {
- name string
- x, y string
- want string
- }{
- {
- name: "no difference",
- x: `goroutine profile: total 1
- 1 @ 0x47bc0e 0x458e57 0x847587 0x483da1
- # 0x847586 database/sql.(*DB).connectionOpener+0x86 database/sql/sql.go:1261`,
- y: `goroutine profile: total 1
- 1 @ 0x47bc0e 0x458e57 0x847587 0x483da1
- # 0x847586 database/sql.(*DB).connectionOpener+0x86 database/sql/sql.go:1261
- `,
- want: "",
- },
- {
- name: "different counts",
- x: `goroutine profile: total 1
- 1 @ 0x47bc0e 0x458e57 0x847587 0x483da1
- # 0x847586 database/sql.(*DB).connectionOpener+0x86 database/sql/sql.go:1261
- `,
- y: `goroutine profile: total 2
- 2 @ 0x47bc0e 0x458e57 0x847587 0x483da1
- # 0x847586 database/sql.(*DB).connectionOpener+0x86 database/sql/sql.go:1261
- `,
- want: `- goroutine profile: total 1
- + goroutine profile: total 2
- - 1 @ 0x47bc0e 0x458e57 0x847587 0x483da1
- + 2 @ 0x47bc0e 0x458e57 0x847587 0x483da1
- # 0x847586 database/sql.(*DB).connectionOpener+0x86 database/sql/sql.go:1261
- `,
- },
- {
- name: "new goroutine",
- x: `goroutine profile: total 1
- 1 @ 0x47bc0e 0x458e57 0x847587 0x483da1
- # 0x847586 database/sql.(*DB).connectionOpener+0x86 database/sql/sql.go:1261
- `,
- y: `goroutine profile: total 2
- 1 @ 0x47bc0e 0x458e57 0x847587 0x483da1
- # 0x847586 database/sql.(*DB).connectionOpener+0x86 database/sql/sql.go:1261
- 1 @ 0x47bc0e 0x458e57 0x754927 0x483da1
- # 0x754926 net/http.(*persistConn).writeLoop+0xe6 net/http/transport.go:2596
- `,
- want: `- goroutine profile: total 1
- + goroutine profile: total 2
- + 1 @ 0x47bc0e 0x458e57 0x754927 0x483da1
- + # 0x754926 net/http.(*persistConn).writeLoop+0xe6 net/http/transport.go:2596
- `,
- },
- {
- name: "removed goroutine",
- x: `goroutine profile: total 2
- 1 @ 0x47bc0e 0x458e57 0x847587 0x483da1
- # 0x847586 database/sql.(*DB).connectionOpener+0x86 database/sql/sql.go:1261
- 1 @ 0x47bc0e 0x458e57 0x754927 0x483da1
- # 0x754926 net/http.(*persistConn).writeLoop+0xe6 net/http/transport.go:2596
- `,
- y: `goroutine profile: total 1
- 1 @ 0x47bc0e 0x458e57 0x847587 0x483da1
- # 0x847586 database/sql.(*DB).connectionOpener+0x86 database/sql/sql.go:1261
- `,
- want: `- goroutine profile: total 2
- + goroutine profile: total 1
- - 1 @ 0x47bc0e 0x458e57 0x754927 0x483da1
- - # 0x754926 net/http.(*persistConn).writeLoop+0xe6 net/http/transport.go:2596
- `,
- },
- {
- name: "removed many goroutine",
- x: `goroutine profile: total 2
- 1 @ 0x47bc0e 0x458e57 0x847587 0x483da1
- # 0x847586 database/sql.(*DB).connectionOpener+0x86 database/sql/sql.go:1261
- 1 @ 0x47bc0e 0x458e57 0x754927 0x483da1
- # 0x754926 net/http.(*persistConn).writeLoop+0xe6 net/http/transport.go:2596
- `,
- y: `goroutine profile: total 0`,
- want: `- goroutine profile: total 2
- + goroutine profile: total 0
- - 1 @ 0x47bc0e 0x458e57 0x754927 0x483da1
- - # 0x754926 net/http.(*persistConn).writeLoop+0xe6 net/http/transport.go:2596
- - 1 @ 0x47bc0e 0x458e57 0x847587 0x483da1
- - # 0x847586 database/sql.(*DB).connectionOpener+0x86 database/sql/sql.go:1261
- `,
- },
- {
- name: "invalid input x",
- x: "invalid",
- y: "goroutine profile: total 0\n",
- want: "- invalid\n+ goroutine profile: total 0\n",
- },
- {
- name: "invalid input y",
- x: "goroutine profile: total 0\n",
- y: "invalid",
- want: "- goroutine profile: total 0\n+ invalid\n",
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- got := diffGoroutines(
- parseGoroutines([]byte(tt.x)),
- parseGoroutines([]byte(tt.y)),
- )
- if got != tt.want {
- t.Errorf("diffPprofGoroutines() diff:\ngot:\n%s\nwant:\n%s\ndiff (-want +got):\n%s", got, tt.want, cmp.Diff(tt.want, got))
- }
- })
- }
- }
- func TestParseGoroutines(t *testing.T) {
- tests := []struct {
- name string
- in string
- wantHeader string
- wantCount int
- }{
- {
- name: "empty profile",
- in: "goroutine profile: total 0\n",
- wantHeader: "goroutine profile: total 0",
- wantCount: 0,
- },
- {
- name: "single goroutine",
- in: `goroutine profile: total 1
- 1 @ 0x47bc0e 0x458e57 0x847587 0x483da1
- # 0x847586 database/sql.(*DB).connectionOpener+0x86 database/sql/sql.go:1261
- `,
- wantHeader: "goroutine profile: total 1",
- wantCount: 1,
- },
- {
- name: "multiple goroutines",
- in: `goroutine profile: total 14
- 7 @ 0x47bc0e 0x413705 0x4132b2 0x10fda4d 0x483da1
- # 0x10fda4c github.com/user/pkg.RoutineA+0x16c pkg/a.go:443
- 7 @ 0x47bc0e 0x458e57 0x754927 0x483da1
- # 0x754926 net/http.(*persistConn).writeLoop+0xe6 net/http/transport.go:2596
- `,
- wantHeader: "goroutine profile: total 14",
- wantCount: 2,
- },
- {
- name: "invalid format",
- in: "invalid",
- wantHeader: "invalid",
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- g := parseGoroutines([]byte(tt.in))
- if got := string(g.head); got != tt.wantHeader {
- t.Errorf("parseGoroutines() header = %q, want %q", got, tt.wantHeader)
- }
- if got := len(g.goroutines); got != tt.wantCount {
- t.Errorf("parseGoroutines() goroutine count = %d, want %d", got, tt.wantCount)
- }
- // Verify that the sort field is correctly reversed
- for _, g := range g.goroutines {
- original := strings.Fields(string(g.header))
- sorted := strings.Fields(string(g.sort))
- if len(original) != len(sorted) {
- t.Errorf("sort field has different number of words: got %d, want %d", len(sorted), len(original))
- continue
- }
- for i := 0; i < len(original); i++ {
- if original[i] != sorted[len(sorted)-1-i] {
- t.Errorf("sort field word mismatch at position %d: got %q, want %q", i, sorted[len(sorted)-1-i], original[i])
- }
- }
- }
- })
- }
- }
|