github.com/grafana/pyroscope@v1.18.0/cmd/pyroscope/main_test.go (about) 1 package main 2 3 import ( 4 "flag" 5 "os" 6 "strings" 7 "testing" 8 9 "github.com/prometheus/client_golang/prometheus" 10 "github.com/stretchr/testify/assert" 11 "github.com/stretchr/testify/require" 12 13 "github.com/grafana/pyroscope/pkg/cfg" 14 "github.com/grafana/pyroscope/pkg/test" 15 ) 16 17 func TestFlagParsing(t *testing.T) { 18 for name, tc := range map[string]struct { 19 arguments []string 20 stdoutMessage string // string that must be included in stdout 21 stderrMessage string // string that must be included in stderr 22 stdoutExcluded string // string that must NOT be included in stdout 23 stderrExcluded string // string that must NOT be included in stderr 24 }{ 25 "help-short": { 26 arguments: []string{"-h"}, 27 stdoutMessage: "Usage of", // Usage must be on stdout, not stderr. 28 stderrExcluded: "Usage of", 29 }, 30 "help": { 31 arguments: []string{"-help"}, 32 stdoutMessage: "Usage of", // Usage must be on stdout, not stderr. 33 stderrExcluded: "Usage of", 34 }, 35 "help-all": { 36 arguments: []string{"-help-all"}, 37 stdoutMessage: "Usage of", // Usage must be on stdout, not stderr. 38 stderrExcluded: "Usage of", 39 }, 40 "user visible module listing": { 41 arguments: []string{"-modules"}, 42 stdoutMessage: "ingester *\n", 43 stderrExcluded: "ingester\n", 44 }, 45 "version": { 46 arguments: []string{"-version"}, 47 stdoutMessage: "pyroscope, version", 48 stderrExcluded: "pyroscope, version", 49 }, 50 "unknown flag": { 51 arguments: []string{"-unknown.flag"}, 52 stderrMessage: "Run with -help to get a list of available parameters", 53 stdoutExcluded: "Usage of", // No usage description on unknown flag. 54 stderrExcluded: "Usage of", 55 }, 56 } { 57 t.Run(name, func(t *testing.T) { 58 _ = os.Setenv("TARGET", "ingester") 59 oldDefaultRegistry := prometheus.DefaultRegisterer 60 defer func() { 61 prometheus.DefaultRegisterer = oldDefaultRegistry 62 }() 63 // We need to reset the default registry to avoid 64 // "duplicate metrics collector registration attempted" errors. 65 prometheus.DefaultRegisterer = prometheus.NewRegistry() 66 testSingle(t, tc.arguments, tc.stdoutMessage, tc.stderrMessage, tc.stdoutExcluded, tc.stderrExcluded) 67 }) 68 } 69 } 70 71 func TestHelp(t *testing.T) { 72 for _, tc := range []struct { 73 name string 74 arg string 75 filename string 76 }{ 77 { 78 name: "basic", 79 arg: "-h", 80 filename: "help.txt.tmpl", 81 }, 82 { 83 name: "all", 84 arg: "-help-all", 85 filename: "help-all.txt.tmpl", 86 }, 87 } { 88 t.Run(tc.name, func(t *testing.T) { 89 oldArgs, oldStdout, oldStderr, oldCmdLine := os.Args, os.Stdout, os.Stderr, flag.CommandLine 90 restored := false 91 restoreIfNeeded := func() { 92 if restored { 93 return 94 } 95 96 os.Stdout = oldStdout 97 os.Stderr = oldStderr 98 os.Args = oldArgs 99 flag.CommandLine = oldCmdLine 100 restored = true 101 } 102 103 oldDefaultRegistry := prometheus.DefaultRegisterer 104 defer func() { 105 prometheus.DefaultRegisterer = oldDefaultRegistry 106 }() 107 // We need to reset the default registry to avoid 108 // "duplicate metrics collector registration attempted" errors. 109 prometheus.DefaultRegisterer = prometheus.NewRegistry() 110 111 co := test.CaptureOutput(t) 112 113 const cmd = "./pyroscope" 114 os.Args = []string{cmd, tc.arg} 115 116 // reset default flags 117 flag.CommandLine = flag.NewFlagSet(os.Args[0], flag.ExitOnError) 118 119 main() 120 121 stdout, stderr := co.Done() 122 123 // Restore stdout and stderr before reporting errors to make them visible. 124 restoreIfNeeded() 125 126 expected, err := os.ReadFile(tc.filename) 127 require.NoError(t, err) 128 assert.Equalf(t, string(expected), stdout, "%s %s output changed; try `make reference-help`", cmd, tc.arg) 129 assert.Empty(t, stderr) 130 }) 131 } 132 } 133 134 func testSingle(t *testing.T, arguments []string, stdoutMessage, stderrMessage, stdoutExcluded, stderrExcluded string) { 135 t.Helper() 136 oldArgs, oldStdout, oldStderr, oldTestMode := os.Args, os.Stdout, os.Stderr, cfg.GetTestMode() 137 restored := false 138 cfg.SetTestMode(true) 139 restoreIfNeeded := func() { 140 if restored { 141 return 142 } 143 os.Args = oldArgs 144 os.Stdout = oldStdout 145 os.Stderr = oldStderr 146 cfg.SetTestMode(oldTestMode) 147 restored = true 148 } 149 150 arguments = append([]string{"./pyroscope"}, arguments...) 151 152 os.Args = arguments 153 co := test.CaptureOutput(t) 154 155 // reset default flags 156 flag.CommandLine = flag.NewFlagSet(arguments[0], flag.ExitOnError) 157 158 main() 159 160 stdout, stderr := co.Done() 161 162 // Restore stdout and stderr before reporting errors to make them visible. 163 restoreIfNeeded() 164 if !strings.Contains(stdout, stdoutMessage) { 165 t.Errorf("Expected on stdout: %q, stdout: %s\n", stdoutMessage, stdout) 166 } 167 if !strings.Contains(stderr, stderrMessage) { 168 t.Errorf("Expected on stderr: %q, stderr: %s\n", stderrMessage, stderr) 169 } 170 if len(stdoutExcluded) > 0 && strings.Contains(stdout, stdoutExcluded) { 171 t.Errorf("Unexpected output on stdout: %q, stdout: %s\n", stdoutExcluded, stdout) 172 } 173 if len(stderrExcluded) > 0 && strings.Contains(stderr, stderrExcluded) { 174 t.Errorf("Unexpected output on stderr: %q, stderr: %s\n", stderrExcluded, stderr) 175 } 176 }