~nskaggs/+junk/xenial-test

« back to all changes in this revision

Viewing changes to src/github.com/juju/testing/filetesting/filetesting.go

  • Committer: Nicholas Skaggs
  • Date: 2016-10-24 20:56:05 UTC
  • Revision ID: nicholas.skaggs@canonical.com-20161024205605-z8lta0uvuhtxwzwl
Initi with beta15

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Copyright 2013, 2014 Canonical Ltd.
 
2
// Licensed under the LGPLv3, see LICENCE file for details.
 
3
 
 
4
package filetesting
 
5
 
 
6
import (
 
7
        "io/ioutil"
 
8
        "os"
 
9
        "path/filepath"
 
10
        "runtime"
 
11
 
 
12
        jc "github.com/juju/testing/checkers"
 
13
        gc "gopkg.in/check.v1"
 
14
)
 
15
 
 
16
// Entry represents a filesystem entity that can be created; and whose
 
17
// correctness can be verified.
 
18
type Entry interface {
 
19
 
 
20
        // GetPath returns the slash-separated relative path that this
 
21
        // entry represents.
 
22
        GetPath() string
 
23
 
 
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
 
27
 
 
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
 
30
        // receiver.
 
31
        Check(c *gc.C, basePath string) Entry
 
32
}
 
33
 
 
34
var (
 
35
        _ Entry = Dir{}
 
36
        _ Entry = File{}
 
37
        _ Entry = Symlink{}
 
38
        _ Entry = Removed{}
 
39
)
 
40
 
 
41
// Entries supplies convenience methods on Entry slices.
 
42
type Entries []Entry
 
43
 
 
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()
 
49
        }
 
50
        return result
 
51
}
 
52
 
 
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)
 
58
        }
 
59
        return result
 
60
}
 
61
 
 
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)
 
67
        }
 
68
        return result
 
69
}
 
70
 
 
71
// AsRemoveds returns a slice of Removed entries whose paths correspond to
 
72
// those in e.
 
73
func (e Entries) AsRemoveds() Entries {
 
74
        result := make([]Entry, len(e))
 
75
        for i, entry := range e {
 
76
                result[i] = Removed{entry.GetPath()}
 
77
        }
 
78
        return result
 
79
}
 
80
 
 
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))
 
84
}
 
85
 
 
86
// Dir is an Entry that allows directories to be created and verified. The
 
87
// Path field should use "/" as the path separator.
 
88
type Dir struct {
 
89
        Path string
 
90
        Perm os.FileMode
 
91
}
 
92
 
 
93
func (d Dir) GetPath() string {
 
94
        return d.Path
 
95
}
 
96
 
 
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)
 
103
        return d
 
104
}
 
105
 
 
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) {
 
111
                return d
 
112
        }
 
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)
 
116
        }
 
117
        c.Check(fileInfo.Mode()&os.ModeType, gc.Equals, os.ModeDir, comment)
 
118
        return d
 
119
}
 
120
 
 
121
// File is an Entry that allows plain files to be created and verified. The
 
122
// Path field should use "/" as the path separator.
 
123
type File struct {
 
124
        Path string
 
125
        Data string
 
126
        Perm os.FileMode
 
127
}
 
128
 
 
129
func (f File) GetPath() string {
 
130
        return f.Path
 
131
}
 
132
 
 
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)
 
139
        return f
 
140
}
 
141
 
 
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) {
 
147
                return f
 
148
        }
 
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)
 
154
        }
 
155
        data, err := ioutil.ReadFile(path)
 
156
        c.Check(err, gc.IsNil, comment)
 
157
        c.Check(string(data), gc.Equals, f.Data, comment)
 
158
        return f
 
159
}
 
160
 
 
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 {
 
164
        Path string
 
165
        Link string
 
166
}
 
167
 
 
168
func (s Symlink) GetPath() string {
 
169
        return s.Path
 
170
}
 
171
 
 
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)
 
175
        return s
 
176
}
 
177
 
 
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)
 
184
        return s
 
185
}
 
186
 
 
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 {
 
190
        Path string
 
191
}
 
192
 
 
193
func (r Removed) GetPath() string {
 
194
        return r.Path
 
195
}
 
196
 
 
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)
 
200
        return r
 
201
}
 
202
 
 
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))
 
211
        return r
 
212
}