File: README.md

package info (click to toggle)
golang-github-twpayne-go-vfs 4.1.0-1.1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 212 kB
  • sloc: makefile: 30
file content (159 lines) | stat: -rw-r--r-- 6,059 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
# go-vfs

[![PkgGoDev](https://pkg.go.dev/badge/github.com/twpayne/go-vfs)](https://pkg.go.dev/github.com/twpayne/go-vfs)
[![Report Card](https://goreportcard.com/badge/github.com/twpayne/go-vfs)](https://goreportcard.com/report/github.com/twpayne/go-vfs)

Package `vfs` provides an abstraction of the `os` and `io` packages that is easy
to test.

## Key features

* File system abstraction layer for commonly-used `os` and `io` functions from
  the standard library.

* Powerful and easy-to-use declarative testing framework, `vfst`. You declare
  the desired state of the filesystem after your code has run, and `vfst` tests
  that the filesystem matches that state. For a quick tour of `vfst`'s features,
  see [the examples in the
  documentation](https://godoc.org/github.com/twpayne/go-vfs/vfst#pkg-examples).

* Compatibility with
  [`github.com/spf13/afero`](https://github.com/spf13/afero) and
  [`github.com/src-d/go-billy`](https://github.com/src-d/go-billy).

## Quick start

`vfs` provides implementations of the `FS` interface:

```go
// An FS is an abstraction over commonly-used functions in the os and io
// packages.
type FS interface {
    Chmod(name string, mode fs.FileMode) error
    Chown(name string, uid, git int) error
    Chtimes(name string, atime, mtime time.Time) error
    Create(name string) (*os.File, error)
    Glob(pattern string) ([]string, error)
    Lchown(name string, uid, git int) error
    Link(oldname, newname string) error
    Lstat(name string) (fs.FileInfo, error)
    Mkdir(name string, perm fs.FileMode) error
    Open(name string) (fs.File, error)
    OpenFile(name string, flag int, perm fs.FileMode) (*os.File, error)
    PathSeparator() rune
    RawPath(name string) (string, error)
    ReadDir(dirname string) ([]fs.DirEntry, error)
    ReadFile(filename string) ([]byte, error)
    Readlink(name string) (string, error)
    Remove(name string) error
    RemoveAll(name string) error
    Rename(oldpath, newpath string) error
    Stat(name string) (fs.FileInfo, error)
    Symlink(oldname, newname string) error
    Truncate(name string, size int64) error
    WriteFile(filename string, data []byte, perm fs.FileMode) error
}
```

To use `vfs`, you write your code to use the `FS` interface, and then use
`vfst` to test it.

`vfs` also provides functions `MkdirAll` (equivalent to `os.MkdirAll`),
`Contains` (an improved `filepath.HasPrefix`), and `Walk` (equivalent to
`filepath.Walk`) that operate on an `FS`.

The implementations of `FS` provided are:

* `OSFS` which calls the underlying `os` and `io` functions directly.

* `PathFS` which transforms all paths to provide a poor-man's `chroot`.

* `ReadOnlyFS` which prevents modification of the underlying FS.

* `TestFS` which assists running tests on a real filesystem but in a temporary
  directory that is easily cleaned up. It uses `OSFS` under the hood.

Example usage:

```go
// writeConfigFile is the function we're going to test. It can make arbitrary
// changes to the filesystem through fileSystem.
func writeConfigFile(fileSystem vfs.FS) error {
    return fileSystem.WriteFile("/home/user/app.conf", []byte(`app config`), 0644)
}

// TestWriteConfigFile is our test function.
func TestWriteConfigFile(t *testing.T) {
    // Create and populate an temporary directory with a home directory.
    fileSystem, cleanup, err := vfst.NewTestFS(map[string]interface{}{
        "/home/user/.bashrc": "# contents of user's .bashrc\n",
    })

    // Check that the directory was populated successfully.
    if err != nil {
        t.Fatalf("vfsTest.NewTestFS(_) == _, _, %v, want _, _, <nil>", err)
    }

    // Ensure that the temporary directory is removed.
    defer cleanup()

    // Call the function we want to test.
    if err := writeConfigFile(fileSystem); err != nil {
        t.Error(err)
    }

    // Check properties of the filesystem after our function has modified it.
    vfst.RunTest(t, fileSystem, "app_conf",
        vfst.PathTest("/home/user/app.conf",
            vfst.TestModeIsRegular,
            vfst.TestModePerm(0644),
            vfst.TestContentsString("app config"),
        ),
    )
}
```

## `github.com/spf13/afero` compatibility

There is a compatibility shim for
[`github.com/spf13/afero`](https://github.com/spf13/afero) in
[`github.com/twpayne/go-vfsafero`](https://github.com/twpayne/go-vfsafero). This
allows you to use `vfst` to test existing code that uses
[`afero.FS`](https://godoc.org/github.com/spf13/afero#Fs). See [the
documentation](https://godoc.org/github.com/twpayne/go-vfsafero) for an example.

## `github.com/src-d/go-billy` compatibility

There is a compatibility shim for
[`github.com/src-d/go-billy`](https://github.com/src-d/go-billy) in
[`github.com/twpayne/go-vfsbilly`](https://github.com/twpayne/go-vfsbilly). This
allows you to use `vfst` to test existing code that uses
[`billy.Filesystem`](https://godoc.org/github.com/src-d/go-billy#Filesystem).
See [the documentation](https://godoc.org/github.com/twpayne/go-vfsbilly) for an
example.

## Motivation

`vfs` was inspired by
[`github.com/spf13/afero`](https://github.com/spf13/afero). So, why not use
`afero`?

* `afero` has several critical bugs in its in-memory mock filesystem
  implementation `MemMapFs`, to the point that it is unusable for non-trivial
  test cases. `vfs` does not attempt to implement an in-memory mock filesystem,
  and instead only provides a thin layer around the standard library's `os` and
  `io` packages, and as such should have fewer bugs.

* `afero` does not support creating or reading symbolic links, and its
  `LstatIfPossible` interface is clumsy to use as it is not part of the
  `afero.Fs` interface. `vfs` provides out-of-the-box support for symbolic links
  with all methods in the `FS` interface.

* `afero` has been effectively abandoned by its author, and a "friendly fork"
  ([`github.com/absfs/afero`](https://github.com/absfs/afero)) has not seen much
  activity. `vfs`, by providing much less functionality than `afero`, should be
  smaller and easier to maintain.

## License

MIT