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  }