// Copyright 2024 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 inspector_test

import (
	"go/ast"
	"iter"
	"slices"
	"testing"

	"golang.org/x/tools/go/ast/inspector"
)

// TestPreorderSeq checks PreorderSeq against Preorder.
func TestPreorderSeq(t *testing.T) {
	inspect := inspector.New(netFiles)

	nodeFilter := []ast.Node{(*ast.FuncDecl)(nil), (*ast.FuncLit)(nil)}

	// reference implementation
	var want []ast.Node
	inspect.Preorder(nodeFilter, func(n ast.Node) {
		want = append(want, n)
	})

	// Check entire sequence.
	got := slices.Collect(inspect.PreorderSeq(nodeFilter...))
	compare(t, got, want)

	// Check that break works.
	got = firstN(10, inspect.PreorderSeq(nodeFilter...))
	compare(t, got, want[:10])
}

// TestAll checks All against Preorder.
func TestAll(t *testing.T) {
	inspect := inspector.New(netFiles)

	// reference implementation
	var want []*ast.CallExpr
	inspect.Preorder([]ast.Node{(*ast.CallExpr)(nil)}, func(n ast.Node) {
		want = append(want, n.(*ast.CallExpr))
	})

	// Check entire sequence.
	got := slices.Collect(inspector.All[*ast.CallExpr](inspect))
	compare(t, got, want)

	// Check that break works.
	got = firstN(10, inspector.All[*ast.CallExpr](inspect))
	compare(t, got, want[:10])
}

// firstN(n, seq), returns a slice of up to n elements of seq.
func firstN[T any](n int, seq iter.Seq[T]) (res []T) {
	for x := range seq {
		res = append(res, x)
		if len(res) == n {
			break
		}
	}
	return res
}

// BenchmarkAllCalls is like BenchmarkInspectCalls,
// but using the single-type filtering iterator, All.
// (The iterator adds about 5-15%.)
func BenchmarkAllCalls(b *testing.B) {
	inspect := inspector.New(netFiles)
	b.ResetTimer()

	// Measure marginal cost of traversal.
	var ncalls int
	for range b.N {
		for range inspector.All[*ast.CallExpr](inspect) {
			ncalls++
		}
	}
}
