code-intelligence.com/cifuzz@v0.40.0/internal/testutil/testutil.go (about)

     1  package testutil
     2  
     3  import (
     4  	"io"
     5  	"io/fs"
     6  	"log"
     7  	"os"
     8  	"path/filepath"
     9  	"runtime"
    10  	"sync"
    11  	"testing"
    12  
    13  	"github.com/stretchr/testify/require"
    14  
    15  	"code-intelligence.com/cifuzz/internal/installer"
    16  	"code-intelligence.com/cifuzz/util/envutil"
    17  	"code-intelligence.com/cifuzz/util/fileutil"
    18  )
    19  
    20  var ChdirMutex sync.Mutex
    21  
    22  // RegisterTestDeps ensures that the test calling this function is rerun (despite caching) if any of the files and
    23  // directories (and their recursive contents) under the provided paths change.
    24  func RegisterTestDeps(path ...string) {
    25  	// Workaround for https://github.com/golang/go/issues/53053
    26  	// Explicitly stat all data dirs and files so that the Go test runner picks up the data dependency and knows how to
    27  	// rerun the test if the data dir contents change. Without this explicit recursive walk, changes to files in
    28  	// subdirectories aren't picked up automatically.
    29  	for _, p := range path {
    30  		err := filepath.Walk(p, func(path string, info fs.FileInfo, err error) error {
    31  			if err != nil {
    32  				return err
    33  			}
    34  			_, err = os.Stat(path)
    35  			return err
    36  		})
    37  		if err != nil {
    38  			panic(err)
    39  		}
    40  	}
    41  }
    42  
    43  // RegisterTestDepOnCIFuzz registers test dependencies on the cifuzz
    44  // executable and all its dependencies. Go doesn't recognize those
    45  // dependencies on its own in tests which build and execute cifuzz as an
    46  // external command.
    47  func RegisterTestDepOnCIFuzz() {
    48  	var deps []string
    49  	_, b, _, _ := runtime.Caller(0)
    50  	// Note: The number of levels we go up here has to be adjusted if
    51  	// this source file is moved.
    52  	basepath := filepath.Dir(filepath.Dir(filepath.Dir(b)))
    53  	for _, dep := range installer.Deps {
    54  		deps = append(deps, filepath.Join(basepath, dep))
    55  	}
    56  	RegisterTestDeps(deps...)
    57  }
    58  
    59  // ChdirToTempDir creates and changes the working directory to new tmp dir
    60  func ChdirToTempDir(prefix string) (tempDir string, cleanup func()) { //nolint:nonamedreturns
    61  	ChdirMutex.Lock()
    62  	oldWd, err := os.Getwd()
    63  	if err != nil {
    64  		log.Printf("Failed to get current working directory: %+v", err)
    65  		os.Exit(1)
    66  	}
    67  
    68  	testTempDir, err := os.MkdirTemp("", prefix)
    69  	if err != nil {
    70  		log.Printf("Failed to create temp dir for tests: %+v", err)
    71  		os.Exit(1)
    72  	}
    73  
    74  	err = os.Chdir(testTempDir)
    75  	if err != nil {
    76  		log.Printf("Failed to change working dir for tests: %+v", err)
    77  		fileutil.Cleanup(testTempDir)
    78  		os.Exit(1)
    79  	}
    80  
    81  	cleanup = func() {
    82  		err = os.Chdir(oldWd)
    83  		if err != nil {
    84  			log.Printf("Failed to change working directory back to %s: %+v", oldWd, err)
    85  			os.Exit(1)
    86  		}
    87  		ChdirMutex.Unlock()
    88  		fileutil.Cleanup(testTempDir)
    89  	}
    90  
    91  	return testTempDir, cleanup
    92  }
    93  
    94  // CheckOutput checks that the strings are contained in the reader output
    95  func CheckOutput(t *testing.T, r io.Reader, s ...string) {
    96  	output, err := io.ReadAll(r)
    97  	require.NoError(t, err)
    98  	for _, str := range s {
    99  		require.Contains(t, string(output), str)
   100  	}
   101  }
   102  
   103  // MkdirTemp wraps os.MkdirTemp and makes sure that errors are checked and
   104  // directories will be deleted
   105  func MkdirTemp(t *testing.T, dir, pattern string) string {
   106  	tempDir, err := os.MkdirTemp(dir, pattern)
   107  	require.NoError(t, err)
   108  	t.Cleanup(func() { fileutil.Cleanup(tempDir) })
   109  	return tempDir
   110  }
   111  
   112  // RepoRoot returns the path pointing to the root of the cifuzz project
   113  func RepoRoot(t *testing.T) string {
   114  	_, b, _, _ := runtime.Caller(0)
   115  	// Note: The number of levels we go up here has to be adjusted if
   116  	// this source file is moved.
   117  	basepath := filepath.Dir(filepath.Dir(filepath.Dir(b)))
   118  	return basepath
   119  }
   120  
   121  // SetupCoverage creates a directory for coverage data and sets the
   122  // needed environment variable
   123  func SetupCoverage(t *testing.T, env []string, subdir string) []string {
   124  	t.Helper()
   125  	covDir := filepath.Join(RepoRoot(t), "coverage", subdir)
   126  	err := os.MkdirAll(covDir, 0755)
   127  	require.NoError(t, err)
   128  	env, err = envutil.Setenv(env, "GOCOVERDIR", covDir)
   129  	require.NoError(t, err)
   130  	return env
   131  }