1
// Copyright 2013, 2014 Canonical Ltd.
2
// Licensed under the LGPLv3, see LICENCE file for details.
12
jc "github.com/juju/testing/checkers"
13
gc "gopkg.in/check.v1"
16
// Entry represents a filesystem entity that can be created; and whose
17
// correctness can be verified.
18
type Entry interface {
20
// GetPath returns the slash-separated relative path that this
24
// Create causes the entry to be created, relative to basePath. It returns
25
// a copy of the receiver.
26
Create(c *gc.C, basePath string) Entry
28
// Check checks that the entry exists, relative to basePath, and matches
29
// the entry that would be created by Create. It returns a copy of the
31
Check(c *gc.C, basePath string) Entry
41
// Entries supplies convenience methods on Entry slices.
44
// Paths returns the slash-separated path of every entry.
45
func (e Entries) Paths() []string {
46
result := make([]string, len(e))
47
for i, entry := range e {
48
result[i] = entry.GetPath()
53
// Create creates every entry relative to basePath and returns a copy of itself.
54
func (e Entries) Create(c *gc.C, basePath string) Entries {
55
result := make([]Entry, len(e))
56
for i, entry := range e {
57
result[i] = entry.Create(c, basePath)
62
// Check checks every entry relative to basePath and returns a copy of itself.
63
func (e Entries) Check(c *gc.C, basePath string) Entries {
64
result := make([]Entry, len(e))
65
for i, entry := range e {
66
result[i] = entry.Check(c, basePath)
71
// AsRemoveds returns a slice of Removed entries whose paths correspond to
73
func (e Entries) AsRemoveds() Entries {
74
result := make([]Entry, len(e))
75
for i, entry := range e {
76
result[i] = Removed{entry.GetPath()}
81
// join joins a slash-separated path to a filesystem basePath.
82
func join(basePath, path string) string {
83
return filepath.Join(basePath, filepath.FromSlash(path))
86
// Dir is an Entry that allows directories to be created and verified. The
87
// Path field should use "/" as the path separator.
93
func (d Dir) GetPath() string {
97
func (d Dir) Create(c *gc.C, basePath string) Entry {
98
path := join(basePath, d.Path)
99
err := os.MkdirAll(path, d.Perm)
100
c.Assert(err, gc.IsNil)
101
err = os.Chmod(path, d.Perm)
102
c.Assert(err, gc.IsNil)
106
func (d Dir) Check(c *gc.C, basePath string) Entry {
107
path := join(basePath, d.Path)
108
fileInfo, err := os.Lstat(path)
109
comment := gc.Commentf("dir %q", path)
110
if !c.Check(err, gc.IsNil, comment) {
113
// Skip until we implement proper permissions checking
114
if runtime.GOOS != "windows" {
115
c.Check(fileInfo.Mode()&os.ModePerm, gc.Equals, d.Perm, comment)
117
c.Check(fileInfo.Mode()&os.ModeType, gc.Equals, os.ModeDir, comment)
121
// File is an Entry that allows plain files to be created and verified. The
122
// Path field should use "/" as the path separator.
129
func (f File) GetPath() string {
133
func (f File) Create(c *gc.C, basePath string) Entry {
134
path := join(basePath, f.Path)
135
err := ioutil.WriteFile(path, []byte(f.Data), f.Perm)
136
c.Assert(err, gc.IsNil)
137
err = os.Chmod(path, f.Perm)
138
c.Assert(err, gc.IsNil)
142
func (f File) Check(c *gc.C, basePath string) Entry {
143
path := join(basePath, f.Path)
144
fileInfo, err := os.Lstat(path)
145
comment := gc.Commentf("file %q", path)
146
if !c.Check(err, gc.IsNil, comment) {
149
// Skip until we implement proper permissions checking
150
if runtime.GOOS != "windows" {
151
mode := fileInfo.Mode()
152
c.Check(mode&os.ModeType, gc.Equals, os.FileMode(0), comment)
153
c.Check(mode&os.ModePerm, gc.Equals, f.Perm, comment)
155
data, err := ioutil.ReadFile(path)
156
c.Check(err, gc.IsNil, comment)
157
c.Check(string(data), gc.Equals, f.Data, comment)
161
// Symlink is an Entry that allows symlinks to be created and verified. The
162
// Path field should use "/" as the path separator.
163
type Symlink struct {
168
func (s Symlink) GetPath() string {
172
func (s Symlink) Create(c *gc.C, basePath string) Entry {
173
err := os.Symlink(s.Link, join(basePath, s.Path))
174
c.Assert(err, gc.IsNil)
178
func (s Symlink) Check(c *gc.C, basePath string) Entry {
179
path := join(basePath, s.Path)
180
comment := gc.Commentf("symlink %q", path)
181
link, err := os.Readlink(path)
182
c.Check(err, gc.IsNil, comment)
183
c.Check(link, gc.Equals, s.Link, comment)
187
// Removed is an Entry that indicates the absence of any entry. The Path
188
// field should use "/" as the path separator.
189
type Removed struct {
193
func (r Removed) GetPath() string {
197
func (r Removed) Create(c *gc.C, basePath string) Entry {
198
err := os.RemoveAll(join(basePath, r.Path))
199
c.Assert(err, gc.IsNil)
203
func (r Removed) Check(c *gc.C, basePath string) Entry {
204
path := join(basePath, r.Path)
205
_, err := os.Lstat(path)
206
// isNotExist allows us to handle the following case:
207
// File{"foo", ...}.Create(...)
208
// Removed{"foo/bar"}.Check(...)
209
// ...where os.IsNotExist would not work.
210
c.Check(err, jc.Satisfies, isNotExist, gc.Commentf("removed %q", path))