// 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 bench

import (
	"context"
	"flag"
	"fmt"
	"testing"
	"time"

	"golang.org/x/tools/gopls/internal/cache"
	"golang.org/x/tools/gopls/internal/hooks"
	"golang.org/x/tools/gopls/internal/lsprpc"
	"golang.org/x/tools/gopls/internal/test/integration/fake"
	"golang.org/x/tools/internal/jsonrpc2"
	"golang.org/x/tools/internal/jsonrpc2/servertest"
)

// github.com/pilosa/pilosa is a repository that has historically caused
// significant memory problems for Gopls. We use it for a simple stress test
// that types arbitrarily in a file with lots of dependents.

var pilosaPath = flag.String("pilosa_path", "", "Path to a directory containing "+
	"github.com/pilosa/pilosa, for stress testing. Do not set this unless you "+
	"know what you're doing!")

func TestPilosaStress(t *testing.T) {
	// TODO(rfindley): revisit this test and make it is hermetic: it should check
	// out pilosa into a directory.
	//
	// Note: This stress test has not been run recently, and may no longer
	// function properly.
	if *pilosaPath == "" {
		t.Skip("-pilosa_path not configured")
	}

	sandbox, err := fake.NewSandbox(&fake.SandboxConfig{
		Workdir: *pilosaPath,
		GOPROXY: "https://proxy.golang.org",
	})
	if err != nil {
		t.Fatal(err)
	}

	server := lsprpc.NewStreamServer(cache.New(nil), false, hooks.Options)
	ts := servertest.NewPipeServer(server, jsonrpc2.NewRawStream)
	ctx := context.Background()

	const skipApplyEdits = false
	editor, err := fake.NewEditor(sandbox, fake.EditorConfig{}).Connect(ctx, ts, fake.ClientHooks{}, skipApplyEdits)
	if err != nil {
		t.Fatal(err)
	}

	files := []string{
		"cmd.go",
		"internal/private.pb.go",
		"roaring/roaring.go",
		"roaring/roaring_internal_test.go",
		"server/handler_test.go",
	}
	for _, file := range files {
		if err := editor.OpenFile(ctx, file); err != nil {
			t.Fatal(err)
		}
	}
	ctx, cancel := context.WithTimeout(ctx, 10*time.Minute)
	defer cancel()

	i := 1
	// MagicNumber is an identifier that occurs in roaring.go. Just change it
	// arbitrarily.
	if err := editor.RegexpReplace(ctx, "roaring/roaring.go", "MagicNumber", fmt.Sprintf("MagicNumber%d", 1)); err != nil {
		t.Fatal(err)
	}
	for {
		select {
		case <-ctx.Done():
			return
		default:
		}
		if err := editor.RegexpReplace(ctx, "roaring/roaring.go", fmt.Sprintf("MagicNumber%d", i), fmt.Sprintf("MagicNumber%d", i+1)); err != nil {
			t.Fatal(err)
		}
		// Simulate (very fast) typing.
		//
		// Typing 80 wpm ~150ms per keystroke.
		time.Sleep(150 * time.Millisecond)
		i++
	}
}
