// Copyright 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package stack_test

import (
	"bytes"
	"strings"
	"testing"

	"golang.org/x/exp/jsonrpc2/internal/stack"
)

func TestProcess(t *testing.T) {
	for _, test := range []struct{ name, input, expect string }{{
		name:   `empty`,
		input:  ``,
		expect: ``,
	}, {
		name:  `no_frame`,
		input: `goroutine 1 [running]:`,
		expect: `
[running]: $1

1 goroutines, 1 unique
`,
	}, {
		name: `one_frame`,
		input: `
goroutine 1 [running]:
package.function(args)
	file.go:10
`,
		expect: `
[running]: $1
file.go:10: function

1 goroutines, 1 unique
`,
	}, {
		name: `one_call`,
		input: `
goroutine 1 [running]:
package1.functionA(args)
	file1.go:10
package2.functionB(args)
	file2.go:20
package3.functionC(args)
	file3.go:30
`,
		expect: `
[running]: $1
file1.go:10: functionA
file2.go:20: functionB
file3.go:30: functionC

1 goroutines, 1 unique
`,
	}, {
		name: `two_call`,
		input: `
goroutine 1 [running]:
package1.functionA(args)
	file1.go:10
goroutine 2 [running]:
package2.functionB(args)
	file2.go:20
`,
		expect: `
[running]: $1
file1.go:10: functionA

[running]: $2
file2.go:20: functionB

2 goroutines, 2 unique
`,
	}, {
		name: `merge_call`,
		input: `
goroutine 1 [running]:
package1.functionA(args)
	file1.go:10
goroutine 2 [running]:
package1.functionA(args)
	file1.go:10
`,
		expect: `
[running]: $1, $2
file1.go:10: functionA

2 goroutines, 1 unique
`,
	}, {
		name: `alternating_call`,
		input: `
goroutine 1 [running]:
package1.functionA(args)
	file1.go:10
goroutine 2 [running]:
package2.functionB(args)
	file2.go:20
goroutine 3 [running]:
package1.functionA(args)
	file1.go:10
goroutine 4 [running]:
package2.functionB(args)
	file2.go:20
goroutine 5 [running]:
package1.functionA(args)
	file1.go:10
goroutine 6 [running]:
package2.functionB(args)
	file2.go:20
`,
		expect: `
[running]: $1, $3, $5
file1.go:10: functionA

[running]: $2, $4, $6
file2.go:20: functionB

6 goroutines, 2 unique
`,
	}, {
		name: `sort_calls`,
		input: `
goroutine 1 [running]:
package3.functionC(args)
	file3.go:30
goroutine 2 [running]:
package2.functionB(args)
	file2.go:20
goroutine 3 [running]:
package1.functionA(args)
	file1.go:10
`,
		expect: `
[running]: $3
file1.go:10: functionA

[running]: $2
file2.go:20: functionB

[running]: $1
file3.go:30: functionC

3 goroutines, 3 unique
`,
	}, {
		name: `real_single`,
		input: `
panic: oops

goroutine 53 [running]:
golang.org/x/tools/internal/jsonrpc2_test.testHandler.func1(0x1240c20, 0xc000013350, 0xc0000133b0, 0x1240ca0, 0xc00002ab00, 0x3, 0x3)
	/work/tools/internal/jsonrpc2/jsonrpc2_test.go:160 +0x74c
golang.org/x/tools/internal/jsonrpc2.(*Conn).Run(0xc000204330, 0x1240c20, 0xc000204270, 0x1209570, 0xc000212120, 0x1242700)
	/work/tools/internal/jsonrpc2/jsonrpc2.go:187 +0x777
golang.org/x/tools/internal/jsonrpc2_test.run.func1(0x123ebe0, 0xc000206018, 0x123ec20, 0xc000206010, 0xc0002080a0, 0xc000204330, 0x1240c20, 0xc000204270, 0xc000212120)
	/work/tools/internal/jsonrpc2/jsonrpc2_test.go:131 +0xe2
created by golang.org/x/tools/internal/jsonrpc2_test.run
	/work/tools/internal/jsonrpc2/jsonrpc2_test.go:121 +0x263
FAIL    golang.org/x/tools/internal/jsonrpc2    0.252s
FAIL
`,
		expect: `
panic: oops

[running]: $53
/work/tools/internal/jsonrpc2/jsonrpc2_test.go:160: testHandler.func1
/work/tools/internal/jsonrpc2/jsonrpc2.go:187:      (*Conn).Run
/work/tools/internal/jsonrpc2/jsonrpc2_test.go:131: run.func1
/work/tools/internal/jsonrpc2/jsonrpc2_test.go:121: run

1 goroutines, 1 unique

FAIL    golang.org/x/tools/internal/jsonrpc2    0.252s
FAIL
`,
	}} {
		t.Run(test.name, func(t *testing.T) {
			buf := &bytes.Buffer{}
			stack.Process(buf, strings.NewReader(test.input))
			expect := strings.TrimSpace(test.expect)
			got := strings.TrimSpace(buf.String())
			if got != expect {
				t.Errorf("got:\n%s\nexpect:\n%s", got, expect)
			}
		})
	}
}
