Skip to content

Commit 590d4e8

Browse files
committed
Add temp_make and temp_del
1 parent 7dfd898 commit 590d4e8

20 files changed

+649
-6
lines changed

CHANGELOG.md

+5
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ This project adheres to [Semantic Versioning](http://semver.org/).
66

77
## [Unreleased]
88

9+
### Added
10+
11+
- Handling temporary directories with `temp_make()` and `temp_del()`
12+
913
### Fixed
1014

1115
- Function comments listing path transformation variables incorrectly
@@ -19,4 +23,5 @@ This project adheres to [Semantic Versioning](http://semver.org/).
1923
`assert_file_not_exist`
2024
- `npm` support
2125

26+
2227
[Unreleased]: https://github.com/ztombol/bats-file/compare/v0.1.0...HEAD

README.md

+130-4
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,21 @@
55
[![Build Status](https://travis-ci.org/ztombol/bats-file.svg?branch=master)](https://travis-ci.org/ztombol/bats-file)
66

77
`bats-file` is a helper library providing common filesystem related
8-
assertions for [Bats][bats].
8+
assertions and helpers for [Bats][bats].
99

1010
Assertions are functions that perform a test and output relevant
1111
information on failure to help debugging. They return 1 on failure and 0
1212
otherwise. Output, [formatted][bats-support-output] for readability, is
1313
sent to the standard error to make assertions usable outside of `@test`
1414
blocks too.
1515

16+
Features:
17+
- [assertions](#usage)
18+
- [temporary directory handling](#working-with-temporary-directories)
19+
1620
Dependencies:
17-
- [`bats-support`][bats-support] - output formatting
21+
- [`bats-support`][bats-support] - output formatting, function call
22+
restriction
1823

1924
See the [shared documentation][bats-docs] to learn how to install and
2025
load this library.
@@ -60,6 +65,123 @@ path : /path/to/existing-file
6065
```
6166
6267
68+
## Working with temporary directories
69+
70+
When testing code that manipulates the filesystem, it is good practice
71+
to run tests in clean, throw-away environments to ensure correctness and
72+
reproducibility. Therefore, this library includes convenient functions
73+
to create and destroy temporary directories.
74+
75+
76+
### `temp_make`
77+
78+
Create a temporary directory for the current test in `BATS_TMPDIR`. The
79+
directory is guaranteed to be unique and its name is derived from the
80+
test's filename and number for easy identification.
81+
82+
```
83+
<test-filename>-<test-number>-<random-string>
84+
```
85+
86+
This information is only available in `setup`, `@test` and `teardown`,
87+
thus the function must be called from one of these locations.
88+
89+
The path of the directory is displayed on the standard output and is
90+
meant to be captured into a variable.
91+
92+
```bash
93+
setup() {
94+
TEST_TEMP_DIR="$(temp_make)"
95+
}
96+
```
97+
98+
For example, for the first test in `sample.bats`, this snippet creates a
99+
directory named `sample.bats-1-XXXXXXXXXX`, where each trailing `X` is a
100+
random alphanumeric character.
101+
102+
If the directory cannot be created, the function fails and displays an
103+
error message on the standard error.
104+
105+
```
106+
-- ERROR: temp_make --
107+
mktemp: failed to create directory via template ‘/etc/samle.bats-1-XXXXXXXXXX’: Permission denied
108+
--
109+
```
110+
111+
#### Directory name prefix
112+
113+
The directory name can be prefixed with an arbitrary string using the `--prefix
114+
<prefix>` option (`-p <prefix>` for short).
115+
116+
```bash
117+
setup() {
118+
TEST_TEMP_DIR="$(temp_make --prefix 'myapp-')"
119+
}
120+
```
121+
122+
Following the previous example, this will create a directory named
123+
`myapp-sample.bats-1-XXXXXXXXXX`. This can be used to group temporary
124+
directories.
125+
126+
Generally speaking, the directory name is of the following form.
127+
128+
```
129+
<prefix><test-filename>-<test-number>-<random-string>
130+
```
131+
132+
133+
### `temp_del`
134+
135+
Delete a temporary directory, typically created with `temp_make`.
136+
137+
```bash
138+
teardown() {
139+
temp_del "$TEST_TEMP_DIR"
140+
}
141+
```
142+
143+
If the directory cannot be deleted, the function fails and displays an
144+
error message on the standard error.
145+
146+
```
147+
-- ERROR: temp_del --
148+
rm: cannot remove '/etc/samle.bats-1-04RUVmBP7x': No such file or directory
149+
--
150+
```
151+
152+
_**Note:** Actually, this function can be used to delete any file or
153+
directory. However, it is most useful in deleting temporary directories
154+
created with `temp_make`, hence the naming._
155+
156+
#### Preserve directory
157+
158+
During development, it is useful to peak into temporary directories
159+
post-mortem to see what the tested code has done.
160+
161+
When `BATSLIB_TEMP_PRESERVE` is set to 1, the function succeeds but the
162+
directory is not deleted.
163+
164+
```bash
165+
$ BATSLIB_TEMP_PRESERVE=1 bats sample.bats
166+
```
167+
168+
#### Preserve directory on failure
169+
170+
During debugging, it is useful to preserve the temporary directories of
171+
failing tests.
172+
173+
When `BATSLIB_TEMP_PRESERVE_ON_FAILURE` is set to 1, the function
174+
succeeds but the directory is not deleted if the test has failed.
175+
176+
```bash
177+
$ BATSLIB_TEMP_PRESERVE_ON_FAILURE=1 bats sample.bats
178+
```
179+
180+
The outcome of a test is only known in `teardown`, therefore this
181+
feature can be used only when `temp_del` is called from that location.
182+
Otherwise and error is displayed on the standard error.
183+
184+
63185
## Transforming displayed paths
64186

65187
Sometimes paths can be long and tiresome to parse to the human eye. To
@@ -75,21 +197,25 @@ ${path/$BATSLIB_FILE_PATH_REM/$BATSLIB_FILE_PATH_ADD}
75197

76198
The longest match of the pattern `BATSLIB_FILE_PATH_REM` is replaced
77199
with `BATSLIB_FILE_PATH_ADD`. To anchor the pattern to the beginning or
78-
the end, prepend a `#` or `%`, respectively.
200+
the end, prepend `#` or `%`, respectively.
79201

80202
For example, the following example hides the path of the temporary
81203
directory where the test takes place.
82204

83205
```bash
84206
setup {
85-
TEST_TEMP_DIR='/tmp/bats-app-temp-dir'
207+
TEST_TEMP_DIR="$(temp_make)"
86208
BATSLIB_FILE_PATH_REM="#${TEST_TEMP_DIR}"
87209
BATSLIB_FILE_PATH_ADD='<temp>'
88210
}
89211

90212
@test 'assert_file_exist()' {
91213
assert_file_exist "${TEST_TEMP_DIR}/path/to/non-existent-file"
92214
}
215+
216+
teardown() {
217+
temp_del "$TEST_TEMP_DIR"
218+
}
93219
```
94220

95221
On failure, only the relevant part of the path is shown.

load.bash

+1
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
source "$(dirname "${BASH_SOURCE[0]}")/src/file.bash"
2+
source "$(dirname "${BASH_SOURCE[0]}")/src/temp.bash"

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,6 @@
33
"version": "0.1.0",
44
"private": true,
55
"peerDependencies": {
6-
"bats-support": "git+https://github.com/ztombol/bats-support.git#v0.2.0"
6+
"bats-support": "git+https://github.com/ztombol/bats-support.git#v0.3.0"
77
}
88
}

src/file.bash

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#
2-
# bats-file - Common filesystem assertions for Bats
2+
# bats-file - Common filesystem assertions and helpers for Bats
33
#
44
# Written in 2016 by Zoltan Tombol <zoltan dot tombol at gmail dot com>
55
#

src/temp.bash

+178
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
#
2+
# bats-file - Common filesystem assertions and helpers for Bats
3+
#
4+
# Written in 2016 by Zoltan Tombol <zoltan dot tombol at gmail dot com>
5+
#
6+
# To the extent possible under law, the author(s) have dedicated all
7+
# copyright and related and neighboring rights to this software to the
8+
# public domain worldwide. This software is distributed without any
9+
# warranty.
10+
#
11+
# You should have received a copy of the CC0 Public Domain Dedication
12+
# along with this software. If not, see
13+
# <http://creativecommons.org/publicdomain/zero/1.0/>.
14+
#
15+
16+
#
17+
# temp.bash
18+
# ---------
19+
#
20+
# Functions for handling temporary directories.
21+
#
22+
23+
# Create a temporary directory for the current test in `BATS_TMPDIR`,
24+
# and display its path on the standard output.
25+
#
26+
# The directory name is derived from the test's filename and number, and
27+
# a random string for uniqueness.
28+
#
29+
# <test-filename>-<test-number>-<random-string>
30+
#
31+
# When `--prefix <prefix>' is specified, `<prefix>' is prepended to the
32+
# directory name.
33+
#
34+
# <prefix><test-filename>-<test-number>-<random-string>
35+
#
36+
# Must be called from `setup', `@test' or `teardown'.
37+
#
38+
# Example:
39+
#
40+
# setup() {
41+
# TEST_TEMP_DIR="$(temp_make --prefix 'myapp-')"
42+
# }
43+
#
44+
# teardown() {
45+
# temp_del "$TEST_TEMP_DIR"
46+
# }
47+
#
48+
# Globals:
49+
# BATS_TEST_NAME
50+
# BATS_TEST_FILENAME
51+
# BATS_TEST_NUMBER
52+
# BATS_TMPDIR
53+
# Arguments:
54+
# none
55+
# Options:
56+
# -p, --prefix <prefix> - prefix the directory name with `<prefix>'
57+
# Returns:
58+
# 0 - on success
59+
# 1 - otherwise
60+
# Outputs:
61+
# STDOUT - path of temporary directory
62+
# STDERR - error messages
63+
temp_make() {
64+
# Check caller.
65+
if ! ( batslib_is_caller --indirect 'setup' \
66+
|| batslib_is_caller --indirect "$BATS_TEST_NAME" \
67+
|| batslib_is_caller --indirect 'teardown' )
68+
then
69+
echo "Must be called from \`setup', \`@test' or \`teardown'" \
70+
| batslib_decorate 'ERROR: temp_make' \
71+
| fail
72+
return $?
73+
fi
74+
75+
# Handle options.
76+
local prefix=''
77+
78+
while (( $# > 0 )); do
79+
case "$1" in
80+
-p|--prefix)
81+
if (( $# < 2 )); then
82+
echo "\`--prefix' requires an argument" \
83+
| batslib_decorate 'ERROR: temp_make' \
84+
| fail
85+
return $?
86+
fi
87+
prefix="$2"
88+
shift 2
89+
;;
90+
--) shift; break ;;
91+
*) break ;;
92+
esac
93+
done
94+
95+
# Create directory.
96+
local template="$prefix"
97+
template+="${BATS_TEST_FILENAME##*/}"
98+
template+="-${BATS_TEST_NUMBER}"
99+
template+='-XXXXXXXXXX'
100+
101+
local path
102+
path="$(mktemp --directory --tmpdir="$BATS_TMPDIR" -- "$template" 2>&1)"
103+
if (( $? )); then
104+
echo "$path" \
105+
| batslib_decorate 'ERROR: temp_make' \
106+
| fail
107+
return $?
108+
fi
109+
110+
echo "$path"
111+
}
112+
113+
# Delete a temporary directory, typically created with `temp_make', and
114+
# its contents.
115+
#
116+
# Note: Actually, this function can be used to delete any file or
117+
# directory. However, it is most useful in deleting temporary
118+
# directories created with `temp_make', hence the naming.
119+
#
120+
# For development and debugging, deletion can be prevented using
121+
# environment variables.
122+
#
123+
# When `BATSLIB_TEMP_PRESERVE' is set to 1, the function succeeds but
124+
# the directory is not deleted.
125+
#
126+
# When `BATSLIB_TEMP_PRESERVE_ON_FAILURE' is set to 1 and `temp_del' is
127+
# called, directly or indirectly, from `teardown', the function succeeds
128+
# but the directory is not deleted if the test has failed.
129+
#
130+
# Example:
131+
#
132+
# setup() {
133+
# TEST_TEMP_DIR="$(temp_make --prefix 'myapp-')"
134+
# }
135+
#
136+
# teardown() {
137+
# temp_del "$TEST_TEMP_DIR"
138+
# }
139+
#
140+
# Globals:
141+
# BATSLIB_TEMP_PRESERVE
142+
# BATSLIB_TEMP_PRESERVE_ON_FAILURE
143+
# BATS_TEST_COMPLETED
144+
# Arguments:
145+
# $1 - path of directory
146+
# Returns:
147+
# 0 - on success
148+
# 1 - otherwise
149+
# Outputs:
150+
# STDERR - error messages
151+
temp_del() {
152+
local -r path="$1"
153+
154+
# Environment variables.
155+
if [[ $BATSLIB_TEMP_PRESERVE == '1' ]]; then
156+
return 0
157+
elif [[ $BATSLIB_TEMP_PRESERVE_ON_FAILURE == '1' ]]; then
158+
# Check caller.
159+
if ! batslib_is_caller --indirect 'teardown'; then
160+
echo "Must be called from \`teardown' when using \`BATSLIB_TEMP_PRESERVE_ON_FAILURE'" \
161+
| batslib_decorate 'ERROR: temp_del' \
162+
| fail
163+
return $?
164+
fi
165+
166+
(( BATS_TEST_COMPLETED != 1 )) && return 0
167+
fi
168+
169+
# Delete directory.
170+
local result
171+
result="$(rm -r -- "$path" 2>&1)"
172+
if (( $? )); then
173+
echo "$result" \
174+
| batslib_decorate 'ERROR: temp_del' \
175+
| fail
176+
return $?
177+
fi
178+
}

0 commit comments

Comments
 (0)