...

Source file src/gotest.tools/v3/fs/path.go

Documentation: gotest.tools/v3/fs

     1  package fs
     2  
     3  import (
     4  	"bytes"
     5  	"io"
     6  	"os"
     7  
     8  	"gotest.tools/v3/assert"
     9  )
    10  
    11  // resourcePath is an adaptor for resources so they can be used as a Path
    12  // with PathOps.
    13  type resourcePath struct{}
    14  
    15  func (p *resourcePath) Path() string {
    16  	return "manifest: not a filesystem path"
    17  }
    18  
    19  func (p *resourcePath) Remove() {}
    20  
    21  type filePath struct {
    22  	resourcePath
    23  	file *file
    24  }
    25  
    26  func (p *filePath) SetContent(content io.ReadCloser) {
    27  	p.file.content = content
    28  }
    29  
    30  func (p *filePath) SetUID(uid uint32) {
    31  	p.file.uid = uid
    32  }
    33  
    34  func (p *filePath) SetGID(gid uint32) {
    35  	p.file.gid = gid
    36  }
    37  
    38  type directoryPath struct {
    39  	resourcePath
    40  	directory *directory
    41  }
    42  
    43  func (p *directoryPath) SetUID(uid uint32) {
    44  	p.directory.uid = uid
    45  }
    46  
    47  func (p *directoryPath) SetGID(gid uint32) {
    48  	p.directory.gid = gid
    49  }
    50  
    51  func (p *directoryPath) AddSymlink(path, target string) error {
    52  	p.directory.items[path] = &symlink{
    53  		resource: newResource(defaultSymlinkMode),
    54  		target:   target,
    55  	}
    56  	return nil
    57  }
    58  
    59  func (p *directoryPath) AddFile(path string, ops ...PathOp) error {
    60  	newFile := &file{resource: newResource(0)}
    61  	p.directory.items[path] = newFile
    62  	exp := &filePath{file: newFile}
    63  	return applyPathOps(exp, ops)
    64  }
    65  
    66  func (p *directoryPath) AddGlobFiles(glob string, ops ...PathOp) error {
    67  	newFile := &file{resource: newResource(0)}
    68  	newFilePath := &filePath{file: newFile}
    69  	p.directory.filepathGlobs[glob] = newFilePath
    70  	return applyPathOps(newFilePath, ops)
    71  }
    72  
    73  func (p *directoryPath) AddDirectory(path string, ops ...PathOp) error {
    74  	newDir := newDirectoryWithDefaults()
    75  	p.directory.items[path] = newDir
    76  	exp := &directoryPath{directory: newDir}
    77  	return applyPathOps(exp, ops)
    78  }
    79  
    80  // Expected returns a [Manifest] with a directory structured created by ops. The
    81  // [PathOp] operations are applied to the manifest as expectations of the
    82  // filesystem structure and properties.
    83  func Expected(t assert.TestingT, ops ...PathOp) Manifest {
    84  	if ht, ok := t.(helperT); ok {
    85  		ht.Helper()
    86  	}
    87  
    88  	newDir := newDirectoryWithDefaults()
    89  	e := &directoryPath{directory: newDir}
    90  	assert.NilError(t, applyPathOps(e, ops))
    91  	return Manifest{root: newDir}
    92  }
    93  
    94  func newDirectoryWithDefaults() *directory {
    95  	return &directory{
    96  		resource:      newResource(defaultRootDirMode),
    97  		items:         make(map[string]dirEntry),
    98  		filepathGlobs: make(map[string]*filePath),
    99  	}
   100  }
   101  
   102  func newResource(mode os.FileMode) resource {
   103  	return resource{
   104  		mode: mode,
   105  		uid:  currentUID(),
   106  		gid:  currentGID(),
   107  	}
   108  }
   109  
   110  func currentUID() uint32 {
   111  	return normalizeID(os.Getuid())
   112  }
   113  
   114  func currentGID() uint32 {
   115  	return normalizeID(os.Getgid())
   116  }
   117  
   118  func normalizeID(id int) uint32 {
   119  	// ids will be -1 on windows
   120  	if id < 0 {
   121  		return 0
   122  	}
   123  	return uint32(id)
   124  }
   125  
   126  var anyFileContent = io.NopCloser(bytes.NewReader(nil))
   127  
   128  // MatchAnyFileContent is a [PathOp] that updates a [Manifest] so that the file
   129  // at path may contain any content.
   130  func MatchAnyFileContent(path Path) error {
   131  	if m, ok := path.(*filePath); ok {
   132  		m.SetContent(anyFileContent)
   133  	}
   134  	return nil
   135  }
   136  
   137  // MatchContentIgnoreCarriageReturn is a [PathOp] that ignores cariage return
   138  // discrepancies.
   139  func MatchContentIgnoreCarriageReturn(path Path) error {
   140  	if m, ok := path.(*filePath); ok {
   141  		m.file.ignoreCariageReturn = true
   142  	}
   143  	return nil
   144  }
   145  
   146  const anyFile = "*"
   147  
   148  // MatchExtraFiles is a [PathOp] that updates a [Manifest] to allow a directory
   149  // to contain unspecified files.
   150  func MatchExtraFiles(path Path) error {
   151  	if m, ok := path.(*directoryPath); ok {
   152  		return m.AddFile(anyFile)
   153  	}
   154  	return nil
   155  }
   156  
   157  // CompareResult is the result of comparison.
   158  //
   159  // See [gotest.tools/v3/assert/cmp.StringResult] for a convenient implementation of
   160  // this interface.
   161  type CompareResult interface {
   162  	Success() bool
   163  	FailureMessage() string
   164  }
   165  
   166  // MatchFileContent is a [PathOp] that updates a [Manifest] to use the provided
   167  // function to determine if a file's content matches the expectation.
   168  func MatchFileContent(f func([]byte) CompareResult) PathOp {
   169  	return func(path Path) error {
   170  		if m, ok := path.(*filePath); ok {
   171  			m.file.compareContentFunc = f
   172  		}
   173  		return nil
   174  	}
   175  }
   176  
   177  // MatchFilesWithGlob is a [PathOp] that updates a [Manifest] to match files using
   178  // glob pattern, and check them using the ops.
   179  func MatchFilesWithGlob(glob string, ops ...PathOp) PathOp {
   180  	return func(path Path) error {
   181  		if m, ok := path.(*directoryPath); ok {
   182  			return m.AddGlobFiles(glob, ops...)
   183  		}
   184  		return nil
   185  	}
   186  }
   187  
   188  // anyFileMode is represented by uint32_max
   189  const anyFileMode os.FileMode = 4294967295
   190  
   191  // MatchAnyFileMode is a [PathOp] that updates a [Manifest] so that the resource at path
   192  // will match any file mode.
   193  func MatchAnyFileMode(path Path) error {
   194  	if m, ok := path.(manifestResource); ok {
   195  		m.SetMode(anyFileMode)
   196  	}
   197  	return nil
   198  }
   199  

View as plain text