// Copyright 2023 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 interp_test

import (
	"path/filepath"
	"reflect"
	"strings"
	"testing"

	"golang.org/x/tools/internal/testenv"
)

func TestIssue69298(t *testing.T) {
	testenv.NeedsGo1Point(t, 23)

	goroot := makeGoroot(t)
	run(t, filepath.Join("testdata", "fixedbugs", "issue69298.go"), goroot)
}

func TestRangeFunc(t *testing.T) {
	testenv.NeedsGo1Point(t, 23)

	goroot := makeGoroot(t)
	out := run(t, filepath.Join("testdata", "rangefunc.go"), goroot)

	// Check the output of the tests.
	const (
		RERR_DONE      = "Saw expected panic: yield function called after range loop exit"
		RERR_MISSING   = "Saw expected panic: iterator call did not preserve panic"
		RERR_EXHAUSTED = RERR_DONE // ssa does not distinguish. Same message as RERR_DONE.

		CERR_DONE      = "Saw expected panic: checked rangefunc error: loop iteration after body done"
		CERR_EXHAUSTED = "Saw expected panic: checked rangefunc error: loop iteration after iterator exit"
		CERR_MISSING   = "Saw expected panic: checked rangefunc error: loop iterator swallowed panic"

		panickyIterMsg = "Saw expected panic: Panicky iterator panicking"
	)
	expected := map[string][]string{
		// rangefunc.go
		"TestCheck":                           {"i = 45", CERR_DONE},
		"TestCooperativeBadOfSliceIndex":      {RERR_EXHAUSTED, "i = 36"},
		"TestCooperativeBadOfSliceIndexCheck": {CERR_EXHAUSTED, "i = 36"},
		"TestTrickyIterAll":                   {"i = 36", RERR_EXHAUSTED},
		"TestTrickyIterOne":                   {"i = 1", RERR_EXHAUSTED},
		"TestTrickyIterZero":                  {"i = 0", RERR_EXHAUSTED},
		"TestTrickyIterZeroCheck":             {"i = 0", CERR_EXHAUSTED},
		"TestTrickyIterEcho": {
			"first loop i=0",
			"first loop i=1",
			"first loop i=3",
			"first loop i=6",
			"i = 10",
			"second loop i=0",
			RERR_EXHAUSTED,
			"end i=0",
		},
		"TestTrickyIterEcho2": {
			"k=0,x=1,i=0",
			"k=0,x=2,i=1",
			"k=0,x=3,i=3",
			"k=0,x=4,i=6",
			"i = 10",
			"k=1,x=1,i=0",
			RERR_EXHAUSTED,
			"end i=1",
		},
		"TestBreak1":                {"[1 2 -1 1 2 -2 1 2 -3]"},
		"TestBreak2":                {"[1 2 -1 1 2 -2 1 2 -3]"},
		"TestContinue":              {"[-1 1 2 -2 1 2 -3 1 2 -4]"},
		"TestBreak3":                {"[100 10 2 4 200 10 2 4 20 2 4 300 10 2 4 20 2 4 30]"},
		"TestBreak1BadA":            {"[1 2 -1 1 2 -2 1 2 -3]", RERR_DONE},
		"TestBreak1BadB":            {"[1 2]", RERR_DONE},
		"TestMultiCont0":            {"[1000 10 2 4 2000]"},
		"TestMultiCont1":            {"[1000 10 2 4]", RERR_DONE},
		"TestMultiCont2":            {"[1000 10 2 4]", RERR_DONE},
		"TestMultiCont3":            {"[1000 10 2 4]", RERR_DONE},
		"TestMultiBreak0":           {"[1000 10 2 4]", RERR_DONE},
		"TestMultiBreak1":           {"[1000 10 2 4]", RERR_DONE},
		"TestMultiBreak2":           {"[1000 10 2 4]", RERR_DONE},
		"TestMultiBreak3":           {"[1000 10 2 4]", RERR_DONE},
		"TestPanickyIterator1":      {panickyIterMsg},
		"TestPanickyIterator1Check": {panickyIterMsg},
		"TestPanickyIterator2":      {RERR_MISSING},
		"TestPanickyIterator2Check": {CERR_MISSING},
		"TestPanickyIterator3":      {"[100 10 1 2 200 10 1 2]"},
		"TestPanickyIterator3Check": {"[100 10 1 2 200 10 1 2]"},
		"TestPanickyIterator4":      {RERR_MISSING},
		"TestPanickyIterator4Check": {CERR_MISSING},
		"TestVeryBad1":              {"[1 10]"},
		"TestVeryBad2":              {"[1 10]"},
		"TestVeryBadCheck":          {"[1 10]"},
		"TestOk":                    {"[1 10]"},
		"TestBreak1BadDefer":        {RERR_DONE, "[1 2 -1 1 2 -2 1 2 -3 -30 -20 -10]"},
		"TestReturns":               {"[-1 1 2 -10]", "[-1 1 2 -10]", RERR_DONE, "[-1 1 2 -10]", RERR_DONE},
		"TestGotoA":                 {"testGotoA1[-1 1 2 -2 1 2 -3 1 2 -4 -30 -20 -10]", "testGotoA2[-1 1 2 -2 1 2 -3 1 2 -4 -30 -20 -10]", RERR_DONE, "testGotoA3[-1 1 2 -10]", RERR_DONE},
		"TestGotoB":                 {"testGotoB1[-1 1 2 999 -10]", "testGotoB2[-1 1 2 -10]", RERR_DONE, "testGotoB3[-1 1 2 -10]", RERR_DONE},
		"TestPanicReturns": {
			"Got expected 'f return'",
			"Got expected 'g return'",
			"Got expected 'h return'",
			"Got expected 'k return'",
			"Got expected 'j return'",
			"Got expected 'm return'",
			"Got expected 'n return and n closure return'",
		},
	}
	got := make(map[string][]string)
	for ln := range strings.SplitSeq(out, "\n") {
		if n, m, ok := strings.Cut(ln, " \t "); ok {
			got[n] = append(got[n], m)
		}
	}
	for n, es := range expected {
		if gs := got[n]; !reflect.DeepEqual(es, gs) {
			t.Errorf("Output of test %s did not match expected output %v. got %v", n, es, gs)
		}
	}
	for n, gs := range got {
		if expected[n] == nil {
			t.Errorf("No expected output for test %s. got %v", n, gs)
		}
	}
}
