golang.org/x/tools/gopls@v0.15.3/internal/test/integration/misc/prompt_test.go (about)

     1  // Copyright 2023 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package misc
     6  
     7  import (
     8  	"fmt"
     9  	"os"
    10  	"path/filepath"
    11  	"regexp"
    12  	"testing"
    13  
    14  	"golang.org/x/tools/gopls/internal/protocol"
    15  	"golang.org/x/tools/gopls/internal/protocol/command"
    16  	"golang.org/x/tools/gopls/internal/server"
    17  	. "golang.org/x/tools/gopls/internal/test/integration"
    18  )
    19  
    20  // Test that gopls prompts for telemetry only when it is supposed to.
    21  func TestTelemetryPrompt_Conditions(t *testing.T) {
    22  	const src = `
    23  -- go.mod --
    24  module mod.com
    25  
    26  go 1.12
    27  -- main.go --
    28  package main
    29  
    30  func main() {
    31  }
    32  `
    33  
    34  	for _, enabled := range []bool{true, false} {
    35  		t.Run(fmt.Sprintf("telemetryPrompt=%v", enabled), func(t *testing.T) {
    36  			for _, initialMode := range []string{"", "local", "off", "on"} {
    37  				t.Run(fmt.Sprintf("initial_mode=%s", initialMode), func(t *testing.T) {
    38  					modeFile := filepath.Join(t.TempDir(), "mode")
    39  					if initialMode != "" {
    40  						if err := os.WriteFile(modeFile, []byte(initialMode), 0666); err != nil {
    41  							t.Fatal(err)
    42  						}
    43  					}
    44  					WithOptions(
    45  						Modes(Default), // no need to run this in all modes
    46  						EnvVars{
    47  							server.GoplsConfigDirEnvvar:        t.TempDir(),
    48  							server.FakeTelemetryModefileEnvvar: modeFile,
    49  						},
    50  						Settings{
    51  							"telemetryPrompt": enabled,
    52  						},
    53  					).Run(t, src, func(t *testing.T, env *Env) {
    54  						wantPrompt := enabled && (initialMode == "" || initialMode == "local")
    55  						expectation := ShownMessageRequest(".*Would you like to enable Go telemetry?")
    56  						if !wantPrompt {
    57  							expectation = Not(expectation)
    58  						}
    59  						env.OnceMet(
    60  							CompletedWork(server.TelemetryPromptWorkTitle, 1, true),
    61  							expectation,
    62  						)
    63  					})
    64  				})
    65  			}
    66  		})
    67  	}
    68  }
    69  
    70  // Test that responding to the telemetry prompt results in the expected state.
    71  func TestTelemetryPrompt_Response(t *testing.T) {
    72  	const src = `
    73  -- go.mod --
    74  module mod.com
    75  
    76  go 1.12
    77  -- main.go --
    78  package main
    79  
    80  func main() {
    81  }
    82  `
    83  
    84  	tests := []struct {
    85  		name     string // subtest name
    86  		response string // response to choose for the telemetry dialog
    87  		wantMode string // resulting telemetry mode
    88  		wantMsg  string // substring contained in the follow-up popup (if empty, no popup is expected)
    89  	}{
    90  		{"yes", server.TelemetryYes, "on", "uploading is now enabled"},
    91  		{"no", server.TelemetryNo, "", ""},
    92  		{"empty", "", "", ""},
    93  	}
    94  	for _, test := range tests {
    95  		t.Run(test.name, func(t *testing.T) {
    96  			modeFile := filepath.Join(t.TempDir(), "mode")
    97  			msgRE := regexp.MustCompile(".*Would you like to enable Go telemetry?")
    98  			respond := func(m *protocol.ShowMessageRequestParams) (*protocol.MessageActionItem, error) {
    99  				if msgRE.MatchString(m.Message) {
   100  					for _, item := range m.Actions {
   101  						if item.Title == test.response {
   102  							return &item, nil
   103  						}
   104  					}
   105  					if test.response != "" {
   106  						t.Errorf("action item %q not found", test.response)
   107  					}
   108  				}
   109  				return nil, nil
   110  			}
   111  			WithOptions(
   112  				Modes(Default), // no need to run this in all modes
   113  				EnvVars{
   114  					server.GoplsConfigDirEnvvar:        t.TempDir(),
   115  					server.FakeTelemetryModefileEnvvar: modeFile,
   116  				},
   117  				Settings{
   118  					"telemetryPrompt": true,
   119  				},
   120  				MessageResponder(respond),
   121  			).Run(t, src, func(t *testing.T, env *Env) {
   122  				var postConditions []Expectation
   123  				if test.wantMsg != "" {
   124  					postConditions = append(postConditions, ShownMessage(test.wantMsg))
   125  				}
   126  				env.OnceMet(
   127  					CompletedWork(server.TelemetryPromptWorkTitle, 1, true),
   128  					postConditions...,
   129  				)
   130  				gotMode := ""
   131  				if contents, err := os.ReadFile(modeFile); err == nil {
   132  					gotMode = string(contents)
   133  				} else if !os.IsNotExist(err) {
   134  					t.Fatal(err)
   135  				}
   136  				if gotMode != test.wantMode {
   137  					t.Errorf("after prompt, mode=%s, want %s", gotMode, test.wantMode)
   138  				}
   139  			})
   140  		})
   141  	}
   142  }
   143  
   144  // Test that we stop asking about telemetry after the user ignores the question
   145  // 5 times.
   146  func TestTelemetryPrompt_GivingUp(t *testing.T) {
   147  	const src = `
   148  -- go.mod --
   149  module mod.com
   150  
   151  go 1.12
   152  -- main.go --
   153  package main
   154  
   155  func main() {
   156  }
   157  `
   158  
   159  	// For this test, we want to share state across gopls sessions.
   160  	modeFile := filepath.Join(t.TempDir(), "mode")
   161  	configDir := t.TempDir()
   162  
   163  	const maxPrompts = 5 // internal prompt limit defined by gopls
   164  
   165  	for i := 0; i < maxPrompts+1; i++ {
   166  		WithOptions(
   167  			Modes(Default), // no need to run this in all modes
   168  			EnvVars{
   169  				server.GoplsConfigDirEnvvar:        configDir,
   170  				server.FakeTelemetryModefileEnvvar: modeFile,
   171  			},
   172  			Settings{
   173  				"telemetryPrompt": true,
   174  			},
   175  		).Run(t, src, func(t *testing.T, env *Env) {
   176  			wantPrompt := i < maxPrompts
   177  			expectation := ShownMessageRequest(".*Would you like to enable Go telemetry?")
   178  			if !wantPrompt {
   179  				expectation = Not(expectation)
   180  			}
   181  			env.OnceMet(
   182  				CompletedWork(server.TelemetryPromptWorkTitle, 1, true),
   183  				expectation,
   184  			)
   185  		})
   186  	}
   187  }
   188  
   189  // Test that gopls prompts for telemetry only when it is supposed to.
   190  func TestTelemetryPrompt_Conditions2(t *testing.T) {
   191  	const src = `
   192  -- go.mod --
   193  module mod.com
   194  
   195  go 1.12
   196  -- main.go --
   197  package main
   198  
   199  func main() {
   200  }
   201  `
   202  	modeFile := filepath.Join(t.TempDir(), "mode")
   203  	WithOptions(
   204  		Modes(Default), // no need to run this in all modes
   205  		EnvVars{
   206  			server.GoplsConfigDirEnvvar:        t.TempDir(),
   207  			server.FakeTelemetryModefileEnvvar: modeFile,
   208  		},
   209  		Settings{
   210  			// off because we are testing
   211  			// if we can trigger the prompt with command.
   212  			"telemetryPrompt": false,
   213  		},
   214  	).Run(t, src, func(t *testing.T, env *Env) {
   215  		cmd, err := command.NewMaybePromptForTelemetryCommand("prompt")
   216  		if err != nil {
   217  			t.Fatal(err)
   218  		}
   219  		var result error
   220  		env.ExecuteCommand(&protocol.ExecuteCommandParams{
   221  			Command: cmd.Command,
   222  		}, &result)
   223  		if result != nil {
   224  			t.Fatal(err)
   225  		}
   226  		expectation := ShownMessageRequest(".*Would you like to enable Go telemetry?")
   227  		env.OnceMet(
   228  			CompletedWork(server.TelemetryPromptWorkTitle, 2, true),
   229  			expectation,
   230  		)
   231  	})
   232  }