golang.org/x/tools@v0.21.0/internal/testfiles/testfiles.go (about)

     1  // Copyright 2024 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 testfiles provides utilities for writing Go tests with files
     6  // in testdata.
     7  package testfiles
     8  
     9  import (
    10  	"io"
    11  	"io/fs"
    12  	"os"
    13  	"path/filepath"
    14  	"strings"
    15  	"testing"
    16  
    17  	"golang.org/x/tools/txtar"
    18  )
    19  
    20  // CopyDirToTmp copies dir to a temporary test directory using
    21  // CopyTestFiles and returns the path to the test directory.
    22  func CopyDirToTmp(t testing.TB, srcdir string) string {
    23  	dst := t.TempDir()
    24  	if err := CopyFS(dst, os.DirFS(srcdir)); err != nil {
    25  		t.Fatal(err)
    26  	}
    27  	return dst
    28  }
    29  
    30  // CopyFS copies the files and directories in src to a
    31  // destination directory dst. Paths to files and directories
    32  // ending in a ".test" extension have the ".test" extension
    33  // removed. This allows tests to hide files whose names have
    34  // special meaning, such as "go.mod" files or "testdata" directories
    35  // from the go command, or ill-formed Go source files from gofmt.
    36  //
    37  // For example if we copy the directory testdata:
    38  //
    39  //	testdata/
    40  //	    go.mod.test
    41  //	    a/a.go
    42  //	    b/b.go
    43  //
    44  // The resulting files will be:
    45  //
    46  //	dst/
    47  //	    go.mod
    48  //	    a/a.go
    49  //	    b/b.go
    50  func CopyFS(dstdir string, src fs.FS) error {
    51  	if err := copyFS(dstdir, src); err != nil {
    52  		return err
    53  	}
    54  
    55  	// Collect ".test" paths in lexical order.
    56  	var rename []string
    57  	err := fs.WalkDir(os.DirFS(dstdir), ".", func(path string, d fs.DirEntry, err error) error {
    58  		if err != nil {
    59  			return err
    60  		}
    61  		if strings.HasSuffix(path, ".test") {
    62  			rename = append(rename, path)
    63  		}
    64  		return nil
    65  	})
    66  	if err != nil {
    67  		return err
    68  	}
    69  
    70  	// Rename the .test paths in reverse lexical order, e.g.
    71  	// in d.test/a.test renames a.test to d.test/a then d.test to d.
    72  	for i := len(rename) - 1; i >= 0; i-- {
    73  		oldpath := filepath.Join(dstdir, rename[i])
    74  		newpath := strings.TrimSuffix(oldpath, ".test")
    75  		if err != os.Rename(oldpath, newpath) {
    76  			return err
    77  		}
    78  	}
    79  	return nil
    80  }
    81  
    82  // Copy the files in src to dst.
    83  // Use os.CopyFS when 1.23 can be used in x/tools.
    84  func copyFS(dstdir string, src fs.FS) error {
    85  	return fs.WalkDir(src, ".", func(path string, d fs.DirEntry, err error) error {
    86  		if err != nil {
    87  			return err
    88  		}
    89  		newpath := filepath.Join(dstdir, path)
    90  		if d.IsDir() {
    91  			return os.MkdirAll(newpath, 0777)
    92  		}
    93  		r, err := src.Open(path)
    94  		if err != nil {
    95  			return err
    96  		}
    97  		defer r.Close()
    98  
    99  		w, err := os.Create(newpath)
   100  		if err != nil {
   101  			return err
   102  		}
   103  		defer w.Close()
   104  		_, err = io.Copy(w, r)
   105  		return err
   106  	})
   107  }
   108  
   109  // ExtractTxtar writes each archive file to the corresponding location beneath dir.
   110  //
   111  // TODO(adonovan): move this to txtar package, we need it all the time (#61386).
   112  func ExtractTxtar(dstdir string, ar *txtar.Archive) error {
   113  	for _, file := range ar.Files {
   114  		name := filepath.Join(dstdir, file.Name)
   115  		if err := os.MkdirAll(filepath.Dir(name), 0777); err != nil {
   116  			return err
   117  		}
   118  		if err := os.WriteFile(name, file.Data, 0666); err != nil {
   119  			return err
   120  		}
   121  	}
   122  	return nil
   123  }
   124  
   125  // ExtractTxtarToTmp read a txtar archive on a given path,
   126  // extracts it to a temporary directory, and returns the
   127  // temporary directory.
   128  func ExtractTxtarToTmp(t testing.TB, archive string) string {
   129  	ar, err := txtar.ParseFile(archive)
   130  	if err != nil {
   131  		t.Fatal(err)
   132  	}
   133  
   134  	dir := t.TempDir()
   135  	err = ExtractTxtar(dir, ar)
   136  	if err != nil {
   137  		t.Fatal(err)
   138  	}
   139  	return dir
   140  }