1/***********************************************************************
2 * Copyright (c) 2009, Secure Endpoints Inc.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 *   notice, this list of conditions and the following disclaimer.
11 *
12 * - Redistributions in binary form must reproduce the above copyright
13 *   notice, this list of conditions and the following disclaimer in
14 *   the documentation and/or other materials provided with the
15 *   distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
20 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
21 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
22 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
28 * OF THE POSSIBILITY OF SUCH DAMAGE.
29 *
30 **********************************************************************/
31
32#include <stdio.h>
33#include <stdlib.h>
34#include <stdarg.h>
35#include <direct.h>
36#include <errno.h>
37#include <io.h>
38#include <fcntl.h>
39#include <sys/stat.h>
40#include <string.h>
41#include "dirent.h"
42
43/* Note that we create a known directory structure in a subdirectory
44   of the current directory to run our tests. */
45
46#define TESTDIR "dirent-test-dir"
47
48const char * dir_entries[] = {
49    "A",
50    "B",
51    "C",
52    "CAA",
53    "CAAA",
54    "CABBBB",
55    "CAABBB.txt",
56    "A filename with spaces"
57};
58
59const char * entries_begin_with_C[] = {
60    "C",
61    "CAA",
62    "CAAA",
63    "CABBBB",
64    "CAABBB.txt"
65};
66
67const char * entries_end_with_A[] = {
68    "A",
69    "CAA",
70    "CAAA"
71};
72
73const int n_dir_entries = sizeof(dir_entries)/sizeof(dir_entries[0]);
74
75int teardown_test(void);
76
77void fail_test(const char * reason, ...)
78{
79    va_list args;
80
81    va_start(args, reason);
82    vfprintf(stderr, reason, args);
83    va_end(args);
84
85    fprintf(stderr, " : errno = %d (%s)\n", errno, strerror(errno));
86    teardown_test();
87    abort();
88}
89
90void fail_test_nf(const char * format, ...)
91{
92    va_list args;
93
94    fprintf(stderr, "FAIL:");
95
96    va_start(args, format);
97    vfprintf(stderr, format, args);
98    va_end(args);
99
100    fprintf(stderr, " : errno = %d (%s)\n", errno, strerror(errno));
101}
102
103int touch(const char * filename)
104{
105    int fd;
106
107    fd = _open(filename, _O_CREAT, _S_IREAD| _S_IWRITE);
108
109    if (fd == -1)
110        return -1;
111
112    return _close(fd);
113}
114
115int setup_test(void)
116{
117    int i;
118
119    fprintf(stderr, "Creating test directory %s ...\n", TESTDIR);
120
121    if (_mkdir(TESTDIR))
122        fail_test("Can't create test directory \"" TESTDIR "\"");
123
124    if (_chdir(TESTDIR))
125        fail_test("Can't change to test directory");
126
127    for (i=0; i < n_dir_entries; i++) {
128        if (touch(dir_entries[i]))
129            fail_test("Can't create test file '%s'", dir_entries[i]);
130    }
131
132    fprintf(stderr, "Done with test setup.\n");
133
134    return 0;
135}
136
137int teardown_test(void)
138{
139    char dirname[_MAX_PATH];
140    size_t len;
141    int i;
142
143    printf ("Begin cleanup...\n");
144
145    if (_getcwd(dirname, sizeof(dirname)/sizeof(char)) != NULL &&
146
147        (len = strlen(dirname)) > sizeof(TESTDIR)/sizeof(char) &&
148
149        !strcmp(dirname + len + 1 - sizeof(TESTDIR)/sizeof(char), TESTDIR)) {
150
151        /* fallthrough */
152
153    } else {
154        /* did we create the directory? */
155
156        if (!_rmdir( TESTDIR )) {
157            fprintf(stderr, "Removed test directory\n");
158            return 0;
159        } else {
160            if (errno == ENOTEMPTY) {
161                if (_chdir(TESTDIR)) {
162                    fprintf(stderr, "Can't change to test directory. Aborting cleanup.\n");
163                    return -1;
164                } else {
165                    /* fallthrough */
166                }
167            } else {
168                return -1;
169            }
170        }
171    }
172
173    fprintf(stderr, "Cleaning up test directory %s ...\n", TESTDIR);
174
175    for (i=0; i < n_dir_entries; i++) {
176        if (_unlink(dir_entries[i])) {
177            /* if the test setup failed, we expect this to happen for
178               at least some files */
179        }
180    }
181
182    if (_chdir("..")) {
183        fprintf(stderr, "Can't escape test directory. Giving in.\n");
184        return -1;
185    }
186
187    if (_rmdir( TESTDIR )) {
188        fprintf(stderr, "Can't remove test directory.\n");
189        return -1;
190    }
191
192    printf("Cleaned up test directory\n");
193    return 0;
194}
195
196int check_list(const char * filespec, const char ** list, int n, int expect_dot_and_dotdot)
197{
198    DIR * d;
199    struct dirent * e;
200    int n_found = 0;
201    int i;
202    int rv = 0;
203    int retry = 1;
204
205    d = opendir(filespec);
206    if (d == NULL) {
207        fail_test_nf("opendir failed for [%s]", filespec);
208        return -1;
209    }
210
211    printf("Checking filespec [%s]... ", filespec);
212
213 retry:
214    while ((e = readdir(d)) != NULL) {
215        n_found ++;
216
217        if (expect_dot_and_dotdot &&
218            (!strcmp(e->d_name, ".") ||
219             !strcmp(e->d_name, "..")))
220            continue;
221
222        for (i=0; i < n; i++) {
223            if (!strcmp(list[i], e->d_name))
224                break;
225        }
226
227        if (i == n) {
228            fail_test_nf("Found unexpected entry [%s]", e->d_name);
229            rv = -1;
230        }
231    }
232
233    if (n_found != n) {
234        fail_test_nf("Unexpected number of entries [%d].  Expected %d", n_found, n);
235        rv = -1;
236    }
237
238    if (retry) {
239        retry = 0;
240        n_found = 0;
241
242        rewinddir(d);
243        goto retry;
244    }
245
246    if (closedir(d)) {
247        fail_test_nf("closedir() failed");
248    }
249
250    printf("done\n");
251
252    return rv;
253}
254
255int run_tests()
256{
257    /* assumes that the test directory has been set up and we have
258       changed into the test directory. */
259
260    check_list("*", dir_entries, n_dir_entries + 2, 1);
261    check_list("*.*", dir_entries, n_dir_entries + 2, 1);
262    check_list("C*", entries_begin_with_C, sizeof(entries_begin_with_C)/sizeof(entries_begin_with_C[0]), 0);
263    check_list("*A", entries_end_with_A, sizeof(entries_end_with_A)/sizeof(entries_end_with_A[0]), 0);
264
265    return 0;
266}
267
268int main(int argc, char ** argv)
269{
270    if (setup_test())
271        return 1;
272
273    run_tests();
274
275    teardown_test();
276
277    return 0;
278}
279