YAC 3.21.0
Yet Another Coupler
Loading...
Searching...
No Matches
test_io_utils.c
Go to the documentation of this file.
1// Copyright (c) 2026 The YAC Authors
2//
3// SPDX-License-Identifier: BSD-3-Clause
4
5#include <stdio.h>
6#include <stdlib.h>
7#include <string.h>
8#include <sys/stat.h>
9#include <unistd.h>
10
11#include "tests.h"
12#include "io_utils.h"
13#include "utils_core.h"
14
21static void utest_file_exists(char const * tmp_base_path);
22static void utest_file_contains(char const * tmp_base_path);
23static void utest_file_path_join(void);
24static void utest_remove_directory(char const * tmp_base_path);
25
26int main(int argc, char ** argv) {
27
28 if (argc != 2) {
29 PUT_ERR("wrong number of arguments\n");
30 return TEST_EXIT_CODE;
31 }
32
33 utest_file_exists(argv[1]);
34 utest_file_contains(argv[1]);
35 utest_file_path_join();
36 utest_remove_directory(argv[1]);
37
38 return TEST_EXIT_CODE;
39}
40
41static char * utest_make_tmp_path(
42 char const * tmp_base_path, char const * name_prefix) {
43
44 // "XXXXXX" is a template for mkstemp/mkdtemp to create a unique
45 // temporary file/directory
46 return yac_file_path_join(tmp_base_path, name_prefix, "XXXXXX");
47}
48
52static char * utest_make_tmp_dir(
53 char const * tmp_base_path, char const * name_prefix) {
54
55 char * path = utest_make_tmp_path(tmp_base_path, name_prefix);
56
57 path = mkdtemp(path);
58
60 path != NULL, "mkdtemp failed for tmp_base_path=\"%s\", name_prefix=\"%s\"",
61 tmp_base_path, name_prefix);
62
63 return path;
64}
65
68static char * utest_write_tmp_file(
69 char const * tmp_base_path, char const * content) {
70
71 char * path = utest_make_tmp_path(tmp_base_path, "test_io_utils_");
72
73 YAC_ASSERT_F(path != NULL, "failed to create temporary path");
74
75 int fd = mkstemp(path);
76
77 YAC_ASSERT_F(fd != -1, "mkstemp failed for path \"%s\"", path);
78
79 size_t len = strlen(content);
80
81 int status = write(fd, content, len);
82 YAC_ASSERT_F(status == (int)len, "short write for path \"%s\"", path);
83
84 close(fd);
85
86 return path;
87}
88
89static void utest_file_exists(char const * tmp_base_path) {
90
91 { // test non-existent file
92
93 // create a temporary file and then remove it to ensure it does not exist
94 char * path = utest_make_tmp_path(tmp_base_path, "no_such_file_");
95
96 {
97 int fd = mkstemp(path);
98 YAC_ASSERT(fd != -1, "mkstemp failed");
99 close(fd);
100 unlink(path);
101 }
102
103 if (yac_file_exists(path))
104 PUT_ERR("yac_file_exists: expected 0 for non-existent file\n");
105
106 free(path);
107 }
108
109 { // test existing file
110 char * path = utest_write_tmp_file(tmp_base_path, "hello\n");
111
112 if (!yac_file_exists(path))
113 PUT_ERR("yac_file_exists: expected 1 for existing file\n");
114
115 unlink(path);
116 free(path);
117 }
118}
119
120static void utest_file_contains(char const * tmp_base_path) {
121
122 { // test file containing the needle
123 char * path = utest_write_tmp_file(
124 tmp_base_path, "first line\nneedle_token here\nlast line\n");
125
126 if (!yac_file_contains(path, "needle_token"))
127 PUT_ERR("yac_file_contains: expected 1 when needle is present\n");
128
129 unlink(path);
130 free(path);
131 }
132
133 { // test file not containing the needle
134 char * path = utest_write_tmp_file(
135 tmp_base_path, "first line\nsecond line\nthird line\n");
136
137 if (yac_file_contains(path, "needle_token"))
138 PUT_ERR("yac_file_contains: expected 0 when needle is absent\n");
139
140 unlink(path);
141 free(path);
142 }
143}
144
145static void utest_file_path_join(void) {
146
147 { // test joining two simple path components
148 char * path = yac_file_path_join("base", "file.txt", NULL);
149
150 if (strcmp(path, "base/file.txt"))
151 PUT_ERR(
152 "yac_file_path_join: unexpected join result for base and file.txt\n");
153
154 free(path);
155 }
156
157 { // test joining a path with a leading slash and a file name
158 char * path = yac_file_path_join("/tmp", "abc", NULL);
159
160 if (strcmp(path, "/tmp/abc"))
161 PUT_ERR("yac_file_path_join: unexpected join result for /tmp and abc\n");
162
163 free(path);
164 }
165
166 { // test joining a path with a trailing slash and a file name
167 char * path = yac_file_path_join("root/", "child", NULL);
168
169 if (strcmp(path, "root//child"))
170 PUT_ERR(
171 "yac_file_path_join: unexpected join result for root/ and child\n");
172
173 free(path);
174 }
175
176 { // test joining path components with extension
177 char * path = yac_file_path_join("base", "file", ".txt");
178
179 if (strcmp(path, "base/file.txt"))
180 PUT_ERR(
181 "yac_file_path_join: unexpected join result for base, file, .txt\n");
182
183 free(path);
184 }
185
186 { // test joining path components with empty extension
187 char * path = yac_file_path_join("base", "file", "");
188
189 if (strcmp(path, "base/file"))
190 PUT_ERR(
191 "yac_file_path_join: unexpected join result for base, file, \"\"\n");
192
193 free(path);
194 }
195}
196
197static void utest_remove_directory(char const * tmp_base_path) {
198
199 { // test empty directory
200
201 // create a temporary directory
202 char * dir = utest_make_tmp_dir(tmp_base_path, "test_io_utils_dir_");
203
205
206 if (yac_file_exists(dir))
207 PUT_ERR(
208 "yac_remove_directory: empty dir still exists after removal\n");
209
210 free(dir);
211 }
212
213 { // test directory with a file
214 char * dir = utest_make_tmp_dir(tmp_base_path, "test_io_utils_dir_");
215 char * file_path = utest_write_tmp_file(dir, "content\n");
216
218
219 if (yac_file_exists(dir))
220 PUT_ERR(
221 "yac_remove_directory: empty dir still exists after removal\n");
222
223 free(dir);
224 free(file_path);
225 }
226
227 { // test directory with a subdirectory and a file
228 char * dir = utest_make_tmp_dir(tmp_base_path, "test_io_utils_dir_");
229 char * subdir_path = utest_make_tmp_dir(dir, "subdir_");
230 char * file_path = utest_write_tmp_file(subdir_path, "nested content\n");
231
233
234 if (yac_file_exists(dir))
235 PUT_ERR(
236 "yac_remove_directory: dir with subdir still exists after removal\n");
237
238 free(dir);
239 free(subdir_path);
240 free(file_path);
241 }
242
243 { // test more complex directory structure with multiple levels of
244 // subdirectories and files:
245 // dir/
246 // ├── subdir1/
247 // │ ├── file1.txt
248 // │ └── subsubdir1/
249 // │ └── file2.txt
250 // └── subdir2/
251 // ├── file3.txt
252 // └── file4.txt
253 char * dir = utest_make_tmp_dir(tmp_base_path, "test_io_utils_dir_");
254 char * subdir1 = utest_make_tmp_dir(dir, "subdir1_");
255 char * file1 = utest_write_tmp_file(subdir1, "file1 content\n");
256 char * subsubdir1 = utest_make_tmp_dir(subdir1, "subsubdir1_");
257 char * file2 = utest_write_tmp_file(subsubdir1, "file2 content\n");
258 char * subdir2 = utest_make_tmp_dir(dir, "subdir2_");
259 char * file3 = utest_write_tmp_file(subdir2, "file3 content\n");
260 char * file4 = utest_write_tmp_file(subdir2, "file4 content\n");
261
263
264 if (yac_file_exists(dir))
265 PUT_ERR(
266 "yac_remove_directory: complex dir structure still exists after removal\n");
267
268 free(dir);
269 free(subdir1);
270 free(file1);
271 free(subsubdir1);
272 free(file2);
273 free(subdir2);
274 free(file3);
275 free(file4);
276 }
277}
#define YAC_ASSERT(exp, msg)
char * yac_file_path_join(char const *path_prefix, char const *path_suffix, char const *extension)
Join two path components with a '/' and optional extension.
Definition io_utils.c:426
void yac_remove_directory(char const *path)
Remove a directory and its contents recursively.
Definition io_utils.c:500
int yac_file_contains(char const *path, char const *needle)
Check whether a file contains a specific string.
Definition io_utils.c:399
int yac_file_exists(const char *filename)
Check whether a file exists.
Definition io_utils.c:394
#define TEST_EXIT_CODE
Definition tests.h:15
#define PUT_ERR(string)
Definition tests.h:10
#define YAC_ASSERT_F(exp, format,...)
Definition yac_assert.h:39