File: filetesting.go

package info (click to toggle)
golang-github-juju-testing 0.0~git20170608.2fe0e88-3
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 464 kB
  • sloc: makefile: 6
file content (212 lines) | stat: -rw-r--r-- 5,441 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
// Copyright 2013, 2014 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.

package filetesting

import (
	"io/ioutil"
	"os"
	"path/filepath"
	"runtime"

	jc "github.com/juju/testing/checkers"
	gc "gopkg.in/check.v1"
)

// Entry represents a filesystem entity that can be created; and whose
// correctness can be verified.
type Entry interface {

	// GetPath returns the slash-separated relative path that this
	// entry represents.
	GetPath() string

	// Create causes the entry to be created, relative to basePath. It returns
	// a copy of the receiver.
	Create(c *gc.C, basePath string) Entry

	// Check checks that the entry exists, relative to basePath, and matches
	// the entry that would be created by Create. It returns a copy of the
	// receiver.
	Check(c *gc.C, basePath string) Entry
}

var (
	_ Entry = Dir{}
	_ Entry = File{}
	_ Entry = Symlink{}
	_ Entry = Removed{}
)

// Entries supplies convenience methods on Entry slices.
type Entries []Entry

// Paths returns the slash-separated path of every entry.
func (e Entries) Paths() []string {
	result := make([]string, len(e))
	for i, entry := range e {
		result[i] = entry.GetPath()
	}
	return result
}

// Create creates every entry relative to basePath and returns a copy of itself.
func (e Entries) Create(c *gc.C, basePath string) Entries {
	result := make([]Entry, len(e))
	for i, entry := range e {
		result[i] = entry.Create(c, basePath)
	}
	return result
}

// Check checks every entry relative to basePath and returns a copy of itself.
func (e Entries) Check(c *gc.C, basePath string) Entries {
	result := make([]Entry, len(e))
	for i, entry := range e {
		result[i] = entry.Check(c, basePath)
	}
	return result
}

// AsRemoveds returns a slice of Removed entries whose paths correspond to
// those in e.
func (e Entries) AsRemoveds() Entries {
	result := make([]Entry, len(e))
	for i, entry := range e {
		result[i] = Removed{entry.GetPath()}
	}
	return result
}

// join joins a slash-separated path to a filesystem basePath.
func join(basePath, path string) string {
	return filepath.Join(basePath, filepath.FromSlash(path))
}

// Dir is an Entry that allows directories to be created and verified. The
// Path field should use "/" as the path separator.
type Dir struct {
	Path string
	Perm os.FileMode
}

func (d Dir) GetPath() string {
	return d.Path
}

func (d Dir) Create(c *gc.C, basePath string) Entry {
	path := join(basePath, d.Path)
	err := os.MkdirAll(path, d.Perm)
	c.Assert(err, gc.IsNil)
	err = os.Chmod(path, d.Perm)
	c.Assert(err, gc.IsNil)
	return d
}

func (d Dir) Check(c *gc.C, basePath string) Entry {
	path := join(basePath, d.Path)
	fileInfo, err := os.Lstat(path)
	comment := gc.Commentf("dir %q", path)
	if !c.Check(err, gc.IsNil, comment) {
		return d
	}
	// Skip until we implement proper permissions checking
	if runtime.GOOS != "windows" {
		c.Check(fileInfo.Mode()&os.ModePerm, gc.Equals, d.Perm, comment)
	}
	c.Check(fileInfo.Mode()&os.ModeType, gc.Equals, os.ModeDir, comment)
	return d
}

// File is an Entry that allows plain files to be created and verified. The
// Path field should use "/" as the path separator.
type File struct {
	Path string
	Data string
	Perm os.FileMode
}

func (f File) GetPath() string {
	return f.Path
}

func (f File) Create(c *gc.C, basePath string) Entry {
	path := join(basePath, f.Path)
	err := ioutil.WriteFile(path, []byte(f.Data), f.Perm)
	c.Assert(err, gc.IsNil)
	err = os.Chmod(path, f.Perm)
	c.Assert(err, gc.IsNil)
	return f
}

func (f File) Check(c *gc.C, basePath string) Entry {
	path := join(basePath, f.Path)
	fileInfo, err := os.Lstat(path)
	comment := gc.Commentf("file %q", path)
	if !c.Check(err, gc.IsNil, comment) {
		return f
	}
	// Skip until we implement proper permissions checking
	if runtime.GOOS != "windows" {
		mode := fileInfo.Mode()
		c.Check(mode&os.ModeType, gc.Equals, os.FileMode(0), comment)
		c.Check(mode&os.ModePerm, gc.Equals, f.Perm, comment)
	}
	data, err := ioutil.ReadFile(path)
	c.Check(err, gc.IsNil, comment)
	c.Check(string(data), gc.Equals, f.Data, comment)
	return f
}

// Symlink is an Entry that allows symlinks to be created and verified. The
// Path field should use "/" as the path separator.
type Symlink struct {
	Path string
	Link string
}

func (s Symlink) GetPath() string {
	return s.Path
}

func (s Symlink) Create(c *gc.C, basePath string) Entry {
	err := os.Symlink(s.Link, join(basePath, s.Path))
	c.Assert(err, gc.IsNil)
	return s
}

func (s Symlink) Check(c *gc.C, basePath string) Entry {
	path := join(basePath, s.Path)
	comment := gc.Commentf("symlink %q", path)
	link, err := os.Readlink(path)
	c.Check(err, gc.IsNil, comment)
	c.Check(link, gc.Equals, s.Link, comment)
	return s
}

// Removed is an Entry that indicates the absence of any entry. The Path
// field should use "/" as the path separator.
type Removed struct {
	Path string
}

func (r Removed) GetPath() string {
	return r.Path
}

func (r Removed) Create(c *gc.C, basePath string) Entry {
	err := os.RemoveAll(join(basePath, r.Path))
	c.Assert(err, gc.IsNil)
	return r
}

func (r Removed) Check(c *gc.C, basePath string) Entry {
	path := join(basePath, r.Path)
	_, err := os.Lstat(path)
	// isNotExist allows us to handle the following case:
	//  File{"foo", ...}.Create(...)
	//  Removed{"foo/bar"}.Check(...)
	// ...where os.IsNotExist would not work.
	c.Check(err, jc.Satisfies, isNotExist, gc.Commentf("removed %q", path))
	return r
}