...

Source file src/github.com/google/pprof/internal/proftest/proftest.go

Documentation: github.com/google/pprof/internal/proftest

     1  // Copyright 2014 Google Inc. All Rights Reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // Package proftest provides some utility routines to test other
    16  // packages related to profiles.
    17  package proftest
    18  
    19  import (
    20  	"encoding/json"
    21  	"flag"
    22  	"fmt"
    23  	"io"
    24  	"os"
    25  	"os/exec"
    26  	"regexp"
    27  	"testing"
    28  
    29  	_ "embed" // For embedding profiles needed by tests and benchmarks
    30  )
    31  
    32  var flagLargeProfile = flag.String("large_profile", "", "The name of a file that contains a profile to use in benchmarks. If empty, a profile of a synthetic program is used.")
    33  
    34  // Diff compares two byte arrays using the diff tool to highlight the
    35  // differences. It is meant for testing purposes to display the
    36  // differences between expected and actual output.
    37  func Diff(b1, b2 []byte) (data []byte, err error) {
    38  	f1, err := os.CreateTemp("", "proto_test")
    39  	if err != nil {
    40  		return nil, err
    41  	}
    42  	defer os.Remove(f1.Name())
    43  	defer f1.Close()
    44  
    45  	f2, err := os.CreateTemp("", "proto_test")
    46  	if err != nil {
    47  		return nil, err
    48  	}
    49  	defer os.Remove(f2.Name())
    50  	defer f2.Close()
    51  
    52  	f1.Write(b1)
    53  	f2.Write(b2)
    54  
    55  	data, err = exec.Command("diff", "-u", f1.Name(), f2.Name()).CombinedOutput()
    56  	if len(data) > 0 {
    57  		// diff exits with a non-zero status when the files don't match.
    58  		// Ignore that failure as long as we get output.
    59  		err = nil
    60  	}
    61  	if err != nil {
    62  		data = []byte(fmt.Sprintf("diff failed: %v\nb1: %q\nb2: %q\n", err, b1, b2))
    63  		err = nil
    64  	}
    65  	return
    66  }
    67  
    68  // EncodeJSON encodes a value into a byte array. This is intended for
    69  // testing purposes.
    70  func EncodeJSON(x interface{}) []byte {
    71  	data, err := json.MarshalIndent(x, "", "    ")
    72  	if err != nil {
    73  		panic(err)
    74  	}
    75  	data = append(data, '\n')
    76  	return data
    77  }
    78  
    79  // TestUI implements the plugin.UI interface, triggering test failures
    80  // if more than Ignore errors not matching AllowRx are printed.
    81  // Also tracks the number of times the error matches AllowRx in
    82  // NumAllowRxMatches.
    83  type TestUI struct {
    84  	T                 testing.TB
    85  	Ignore            int
    86  	AllowRx           string
    87  	NumAllowRxMatches int
    88  	Input             []string
    89  	index             int
    90  }
    91  
    92  // ReadLine returns no input, as no input is expected during testing.
    93  func (ui *TestUI) ReadLine(_ string) (string, error) {
    94  	if ui.index >= len(ui.Input) {
    95  		return "", io.EOF
    96  	}
    97  	input := ui.Input[ui.index]
    98  	ui.index++
    99  	if input == "**error**" {
   100  		return "", fmt.Errorf("error: %s", input)
   101  	}
   102  	return input, nil
   103  }
   104  
   105  // Print messages are discarded by the test UI.
   106  func (ui *TestUI) Print(args ...interface{}) {
   107  }
   108  
   109  // PrintErr messages may trigger an error failure. A fixed number of
   110  // error messages are permitted when appropriate.
   111  func (ui *TestUI) PrintErr(args ...interface{}) {
   112  	if ui.AllowRx != "" {
   113  		if matched, err := regexp.MatchString(ui.AllowRx, fmt.Sprint(args...)); matched || err != nil {
   114  			if err != nil {
   115  				ui.T.Errorf("failed to match against regex %q: %v", ui.AllowRx, err)
   116  			}
   117  			ui.NumAllowRxMatches++
   118  			return
   119  		}
   120  	}
   121  	if ui.Ignore > 0 {
   122  		ui.Ignore--
   123  		return
   124  	}
   125  	// Stringify arguments with fmt.Sprint() to match what default UI
   126  	// implementation does. Without this Error() calls fmt.Sprintln() which
   127  	// _always_ adds spaces between arguments, unlike fmt.Sprint() which only
   128  	// adds them between arguments if neither is string.
   129  	ui.T.Error("unexpected error: " + fmt.Sprint(args...))
   130  }
   131  
   132  // IsTerminal indicates if the UI is an interactive terminal.
   133  func (ui *TestUI) IsTerminal() bool {
   134  	return false
   135  }
   136  
   137  // WantBrowser indicates whether a browser should be opened with the -http option.
   138  func (ui *TestUI) WantBrowser() bool {
   139  	return false
   140  }
   141  
   142  // SetAutoComplete is not supported by the test UI.
   143  func (ui *TestUI) SetAutoComplete(_ func(string) string) {
   144  }
   145  
   146  // LargeProfile returns a large profile that may be useful in benchmarks.
   147  //
   148  // If the flag --large_profile is set, the contents of the file
   149  // named by the flag are returned. Otherwise an embedded profile (~1.2MB)
   150  // for a synthetic program is returned.
   151  func LargeProfile(tb testing.TB) []byte {
   152  	tb.Helper()
   153  	if f := *flagLargeProfile; f != "" {
   154  		// Use custom profile.
   155  		data, err := os.ReadFile(f)
   156  		if err != nil {
   157  			tb.Fatalf("custom profile file: %v\n", err)
   158  		}
   159  		return data
   160  	}
   161  
   162  	return largeProfileData
   163  }
   164  
   165  //go:embed testdata/large.cpu
   166  var largeProfileData []byte
   167  

View as plain text