1 /*
2 * Copyright (c) 2019 Peter Bigot Consulting, LLC
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 /* Constants, data types, and functions that support testing the Zephyr FS API.
8 *
9 * Includes:
10 *
11 * * A data type that supports building and modifying absolute paths
12 * without worrying about snprintf or buffer overflow;
13 * * Functions to write known content into files, and to verify that
14 * content when reading the file.
15 * * A data type that is used to describe file system contents, with
16 * functions to create the file system and verify file system
17 * content against the build command description.
18 */
19
20 #ifndef _ZEPHYR_TESTS_SUBSYS_FS_LITTLEFS_TESTFS_UTIL_H_
21 #define _ZEPHYR_TESTS_SUBSYS_FS_LITTLEFS_TESTFS_UTIL_H_
22
23 #include <stdarg.h>
24 #include <string.h>
25 #include <zephyr/types.h>
26 #include <zephyr/fs/fs.h>
27
28 /** Maximum length of a path supported by the test infrastructure. */
29 #define TESTFS_PATH_MAX 127
30
31 /** Structure holding an absolute file system path. */
32 struct testfs_path {
33 /* Storage for a maximal path plus NUL */
34 char path[TESTFS_PATH_MAX + 1];
35
36 /* Pointer to the NUL character marking end of string. */
37 char *eos;
38 };
39
40 /** A properly cast terminator for variadic arguments to testfs_path
41 * functions.
42 */
43 #define TESTFS_PATH_END ((const char *)NULL)
44
45 #define TESTFS_BUFFER_SIZE 64
46
47 /** Initialize the file system path within a mount point.
48 *
49 * Creates an absolute path that begins with the mount point, then
50 * extends it with an arbitrary number of path elements as with
51 * testfs_path_extend().
52 *
53 * @param pp a pointer to the path structure.
54 *
55 * @param mp pointer to the mount point. A null pointer may be passed
56 * to create the root path without a mount point.
57 *
58 * @param... as with testfs_path_extend()
59 *
60 * @return a pointer to the start of the path.
61 */
62 const char *testfs_path_init(struct testfs_path *pp,
63 const struct fs_mount_t *mp,
64 ...);
65
66 /** Extend/modify an existing file system path.
67 *
68 * Given an absolute path this function extends it with additional
69 * path elements. A forward slash is added between each element.
70 *
71 * If ".." is passed the last element of the path is removed.
72 *
73 * The final argument must be a const char * null pointer such as @ref
74 * TESTFS_PATH_END.
75 *
76 * If adding an element would exceed the maximum allowed path length
77 * extension stops, and the path existing to that point is returned.
78 *
79 * @param pp a pointer to the path structure.
80 *
81 * @param... a sequence of const char * pointers terminating with a
82 * null pointer.
83 *
84 * @return a pointer to the start of the path.
85 */
86 const char *testfs_path_extend(struct testfs_path *pp,
87 ...);
88
testfs_path_copy(struct testfs_path * dp,const struct testfs_path * sp)89 static inline struct testfs_path *testfs_path_copy(struct testfs_path *dp,
90 const struct testfs_path *sp)
91 {
92 size_t len = sp->eos - sp->path;
93
94 memcpy(dp->path, sp->path, len + 1);
95 dp->eos = dp->path + len;
96
97 return dp;
98 }
99
100 /** Write a sequence of constant data to the file.
101 *
102 * Writes are generated in blocks up to TESTFS_BUFFER_SIZE in length.
103 *
104 * @param fp pointer to the file to write
105 *
106 * @param value value of the bytes to write
107 *
108 * @param len number of octets to write
109 *
110 * @return number of octets written, or a negative error code.
111 */
112 int testfs_write_constant(struct fs_file_t *fp,
113 uint8_t value,
114 unsigned int len);
115
116 /** Verify that the file contains a sequence of constant data.
117 *
118 * Reads are performed in blocks up to TESTFS_BUFFER_SIZE in length.
119 *
120 * @param fp pointer to the file to read
121 *
122 * @param value the expected value of the constant data.
123 *
124 * @param len the number of times the constant value should appear
125 *
126 * @return the number of times the constant value did appear before
127 * len was reached or a mismatch occurred, or a negative error on file
128 * read failure.
129 */
130 int testfs_verify_constant(struct fs_file_t *fp,
131 uint8_t value,
132 unsigned int len);
133
134 /** Write an increasing sequence of bytes to the file.
135 *
136 * Writes are generated in blocks up to TESTFS_BUFFER_SIZE in length.
137 *
138 * @param fp pointer to the file to write
139 *
140 * @param value value of the first byte to write
141 *
142 * @param len number of octets to write
143 *
144 * @return number of octets written, or a negative error code.
145 */
146 int testfs_write_incrementing(struct fs_file_t *fp,
147 uint8_t value,
148 unsigned int len);
149
150 /** Verify that the file contains a sequence of increasing data.
151 *
152 * Reads are performed in blocks up to TESTFS_BUFFER_SIZE in length.
153 *
154 * @param fp pointer to the file to read
155 *
156 * @param value the expected value of the first byte of data.
157 *
158 * @param len the number of successive increasing values expected
159 *
160 * @return the number of times the expected value did appear before
161 * len was reached or a mismatch occurred, or a negative error on file
162 * read failure.
163 */
164 int testfs_verify_incrementing(struct fs_file_t *fp,
165 uint8_t value,
166 unsigned int len);
167
168 /** Structure used to describe a filesystem layout. */
169 struct testfs_bcmd {
170 enum fs_dir_entry_type type;
171 const char *name;
172 uint32_t size;
173 uint8_t value;
174 bool matched;
175 };
176
177 /* Specify that a directory named _n is to be created, and all entries
178 * up to the next matching EXIT_DIR command are to be created within
179 * that directory.
180 */
181 #define TESTFS_BCMD_ENTER_DIR(_n) { \
182 .type = FS_DIR_ENTRY_DIR, \
183 .name = _n, \
184 }
185
186 /* Specify that a file named _n is to be created, with _sz bytes of
187 * content that starts with _val and increments with each byte.
188 */
189 #define TESTFS_BCMD_FILE(_n, _val, _sz) { \
190 .type = FS_DIR_ENTRY_FILE, \
191 .name = _n, \
192 .size = _sz, \
193 .value = _val, \
194 }
195
196 /* Specify that the content of the previous matching ENTER_DIR is
197 * complete and subsequent entries should be created in the parent
198 * directory.
199 */
200 #define TESTFS_BCMD_EXIT_DIR(_n) { \
201 .type = FS_DIR_ENTRY_DIR, \
202 }
203
204 /* Specify that all build commands have been provided. */
205 #define TESTFS_BCMD_END() { \
206 }
207
208 #define TESTFS_BCMD_IS_ENTER_DIR(cp) (((cp)->type == FS_DIR_ENTRY_DIR) \
209 && ((cp)->name != NULL))
210 #define TESTFS_BCMD_IS_EXIT_DIR(cp) (((cp)->type == FS_DIR_ENTRY_DIR) \
211 && ((cp)->name == NULL))
212 #define TESTFS_BCMD_IS_FILE(cp) (((cp)->type == FS_DIR_ENTRY_FILE) \
213 && ((cp)->name != NULL))
214 #define TESTFS_BCMD_IS_END(cp) (((cp)->type == FS_DIR_ENTRY_FILE) \
215 && ((cp)->name == NULL))
216
217 /** Create a file system hierarchy.
218 *
219 * If an error is returned the
220 *
221 * @param root a path to the directory in which the hierarchy will be
222 * created. If an error is returned the content of this may identify
223 * where the problem occurred.
224 *
225 * @param cp pointer to a sequence of commands that specify the
226 * directory layout.
227 *
228 * @return Zero after successfully building the hierarchy, or a
229 * negative errno code.
230 */
231 int testfs_build(struct testfs_path *root,
232 const struct testfs_bcmd *cp);
233
234 /** Get the end range pointer for a sequence of build commands.
235 *
236 * @param cp a command within the range
237 *
238 * @return a pointer to the END command in the range.
239 */
testfs_bcmd_end(struct testfs_bcmd * cp)240 static inline struct testfs_bcmd *testfs_bcmd_end(struct testfs_bcmd *cp)
241 {
242 while (!TESTFS_BCMD_IS_END(cp)) {
243 ++cp;
244 }
245 return cp;
246 }
247
248 /** Verify file system contents against build commands.
249 *
250 * The specified directory is opened and its contents recursively
251 * compared against the build commands for name, size, and content.
252 * Build command record entries that are that are matched will have
253 * their matched flag set; unmatched entries will have a cleared
254 * matched flag.
255 *
256 * @param pp the path to a directory in the file system.
257 *
258 * @param scp the first build command relevant to the directory.
259 *
260 * @param ecp the exclusive last entry in the sequence of build
261 * commands relevant to the directory.
262 *
263 * @return the number of file system entries found that did not match
264 * build command content, or a negative error code on compare or file
265 * system failures.
266 */
267 int testfs_bcmd_verify_layout(struct testfs_path *pp,
268 struct testfs_bcmd *scp,
269 struct testfs_bcmd *ecp);
270
271 /** Search a build command range for a match to an entry.
272 *
273 * A match is identified if the directory entry type, name, and size
274 * match between the build command and the file status. The content
275 * of the file is not verified.
276 *
277 * @param statp pointer to a file system entry
278 *
279 * @param scp pointer to the first build command associated with the
280 * hierarchy level of the entry.
281 *
282 * @param ecp pointer to the exclusive last entry in the build command
283 * range.
284 *
285 * @return a pointer to a build command that matches in type and name,
286 * or a null pointer if no match is found at the top level.
287 */
288 struct testfs_bcmd *testfs_bcmd_find(struct fs_dirent *statp,
289 struct testfs_bcmd *scp,
290 struct testfs_bcmd *ecp);
291
292 /** Find the exit dir command that balances the enter dir command.
293 *
294 * @param cp pointer to an ENTER_DIR command.
295 *
296 * @param ecp pointer to the exclusive range end of this level of the
297 * hierarchy.
298 *
299 * @return a pointer to the paired EXIT_DIR command.
300 */
301 struct testfs_bcmd *testfs_bcmd_exitdir(struct testfs_bcmd *cp,
302 struct testfs_bcmd *ecp);
303
304 #endif /* _ZEPHYR_TESTS_SUBSYS_FS_LITTLEFS_TESTFS_UTIL_H_ */
305