| package fs |
| |
| import ( |
| "bytes" |
| "io" |
| "io/ioutil" |
| "os" |
| |
| "gotest.tools/assert" |
| ) |
| |
| // resourcePath is an adaptor for resources so they can be used as a Path |
| // with PathOps. |
| type resourcePath struct{} |
| |
| func (p *resourcePath) Path() string { |
| return "manifest: not a filesystem path" |
| } |
| |
| func (p *resourcePath) Remove() {} |
| |
| type filePath struct { |
| resourcePath |
| file *file |
| } |
| |
| func (p *filePath) SetContent(content io.ReadCloser) { |
| p.file.content = content |
| } |
| |
| func (p *filePath) SetUID(uid uint32) { |
| p.file.uid = uid |
| } |
| |
| func (p *filePath) SetGID(gid uint32) { |
| p.file.gid = gid |
| } |
| |
| type directoryPath struct { |
| resourcePath |
| directory *directory |
| } |
| |
| func (p *directoryPath) SetUID(uid uint32) { |
| p.directory.uid = uid |
| } |
| |
| func (p *directoryPath) SetGID(gid uint32) { |
| p.directory.gid = gid |
| } |
| |
| func (p *directoryPath) AddSymlink(path, target string) error { |
| p.directory.items[path] = &symlink{ |
| resource: newResource(defaultSymlinkMode), |
| target: target, |
| } |
| return nil |
| } |
| |
| func (p *directoryPath) AddFile(path string, ops ...PathOp) error { |
| newFile := &file{resource: newResource(0)} |
| p.directory.items[path] = newFile |
| exp := &filePath{file: newFile} |
| return applyPathOps(exp, ops) |
| } |
| |
| func (p *directoryPath) AddDirectory(path string, ops ...PathOp) error { |
| newDir := newDirectoryWithDefaults() |
| p.directory.items[path] = newDir |
| exp := &directoryPath{directory: newDir} |
| return applyPathOps(exp, ops) |
| } |
| |
| // Expected returns a Manifest with a directory structured created by ops. The |
| // PathOp operations are applied to the manifest as expectations of the |
| // filesystem structure and properties. |
| func Expected(t assert.TestingT, ops ...PathOp) Manifest { |
| if ht, ok := t.(helperT); ok { |
| ht.Helper() |
| } |
| |
| newDir := newDirectoryWithDefaults() |
| e := &directoryPath{directory: newDir} |
| assert.NilError(t, applyPathOps(e, ops)) |
| return Manifest{root: newDir} |
| } |
| |
| func newDirectoryWithDefaults() *directory { |
| return &directory{ |
| resource: newResource(defaultRootDirMode), |
| items: make(map[string]dirEntry), |
| } |
| } |
| |
| func newResource(mode os.FileMode) resource { |
| return resource{ |
| mode: mode, |
| uid: currentUID(), |
| gid: currentGID(), |
| } |
| } |
| |
| func currentUID() uint32 { |
| return normalizeID(os.Getuid()) |
| } |
| |
| func currentGID() uint32 { |
| return normalizeID(os.Getgid()) |
| } |
| |
| func normalizeID(id int) uint32 { |
| // ids will be -1 on windows |
| if id < 0 { |
| return 0 |
| } |
| return uint32(id) |
| } |
| |
| var anyFileContent = ioutil.NopCloser(bytes.NewReader(nil)) |
| |
| // MatchAnyFileContent is a PathOp that updates a Manifest so that the file |
| // at path may contain any content. |
| func MatchAnyFileContent(path Path) error { |
| if m, ok := path.(*filePath); ok { |
| m.SetContent(anyFileContent) |
| } |
| return nil |
| } |
| |
| const anyFile = "*" |
| |
| // MatchExtraFiles is a PathOp that updates a Manifest to allow a directory |
| // to contain unspecified files. |
| func MatchExtraFiles(path Path) error { |
| if m, ok := path.(*directoryPath); ok { |
| m.AddFile(anyFile) |
| } |
| return nil |
| } |
| |
| // anyFileMode is represented by uint32_max |
| const anyFileMode os.FileMode = 4294967295 |
| |
| // MatchAnyFileMode is a PathOp that updates a Manifest so that the resource at path |
| // will match any file mode. |
| func MatchAnyFileMode(path Path) error { |
| if m, ok := path.(manifestResource); ok { |
| m.SetMode(anyFileMode) |
| } |
| return nil |
| } |