1#include <iostream>
2#include <platform/dirutils.h>
3#include <cstdlib>
4#include <list>
5#include <string>
6#include <cerrno>
7#include <cstring>
8#include <sys/stat.h>
9#include <sys/types.h>
10#include <stdio.h>
11
12#ifdef WIN32
13#include <windows.h>
14#include <direct.h>
15static bool CreateDirectory(const std::string &dir) {
16    if (_mkdir(dir.c_str()) != 0) {
17        return false;
18    }
19    return true;
20}
21
22#define PATH_SEPARATOR "\\"
23
24#else
25static bool CreateDirectory(const std::string &dir) {
26   if (mkdir(dir.c_str(), S_IXUSR | S_IWUSR | S_IRUSR) != 0) {
27      return false;
28   }
29   return true;
30}
31
32#define PATH_SEPARATOR "/"
33
34#endif
35
36#undef NDEBUG
37#include <cassert>
38
39int exit_value = EXIT_SUCCESS;
40
41static void expect(const bool exp, const bool val) {
42    if (exp != val) {
43        std::cerr << "Expected " << exp << " got [" << val << "]" << std::endl;
44        exit_value = EXIT_FAILURE;
45    }
46}
47
48static void expect(const std::string &exp, const std::string &val) {
49   if (exp != val) {
50      std::cerr << "Expected " << exp << " got [" << val << "]" << std::endl;
51      exit_value = EXIT_FAILURE;
52   }
53}
54
55static void expect(size_t size, const std::vector<std::string> &vec) {
56   if (vec.size() != size) {
57      std::cerr << "Expected vector of " << size
58                << " elements got [" << vec.size() << "]" << std::endl;
59      exit_value = EXIT_FAILURE;
60   }
61}
62
63static void contains(const std::string &val, const std::vector<std::string> &vec) {
64   std::vector<std::string>::const_iterator ii;
65   for (ii = vec.begin(); ii != vec.end(); ++ii) {
66      if (val == *ii) {
67         return ;
68      }
69   }
70
71   std::cerr << "Expected vector to contain [" << val << "]" << std::endl;
72   for (ii = vec.begin(); ii != vec.end(); ++ii) {
73      std::cerr << "  -> " << *ii << std::endl;
74   }
75   std::cerr << std::endl;
76   exit_value = EXIT_FAILURE;
77}
78
79static std::list<std::string> vfs;
80
81static bool exists(const std::string &path) {
82   struct stat st;
83   return stat(path.c_str(), &st) == 0;
84}
85
86static void testDirname(void) {
87   // Check the simple case
88   expect("foo", CouchbaseDirectoryUtilities::dirname("foo\\bar"));
89   expect("foo", CouchbaseDirectoryUtilities::dirname("foo/bar"));
90
91   // Make sure that we remove double an empty chunk
92   expect("foo", CouchbaseDirectoryUtilities::dirname("foo\\\\bar"));
93   expect("foo", CouchbaseDirectoryUtilities::dirname("foo//bar"));
94
95   // Make sure that we handle the case without a directory
96   expect(".", CouchbaseDirectoryUtilities::dirname("bar"));
97   expect(".", CouchbaseDirectoryUtilities::dirname(""));
98
99   // Absolute directories
100   expect("\\", CouchbaseDirectoryUtilities::dirname("\\bar"));
101   expect("\\", CouchbaseDirectoryUtilities::dirname("\\\\bar"));
102   expect("/", CouchbaseDirectoryUtilities::dirname("/bar"));
103   expect("/", CouchbaseDirectoryUtilities::dirname("//bar"));
104
105   // Test that we work with multiple directories
106   expect("1/2/3/4/5", CouchbaseDirectoryUtilities::dirname("1/2/3/4/5/6"));
107   expect("1\\2\\3\\4\\5", CouchbaseDirectoryUtilities::dirname("1\\2\\3\\4\\5\\6"));
108   expect("1/2\\4/5", CouchbaseDirectoryUtilities::dirname("1/2\\4/5\\6"));
109}
110
111static void testBasename(void) {
112   expect("bar", CouchbaseDirectoryUtilities::basename("foo\\bar"));
113   expect("bar", CouchbaseDirectoryUtilities::basename("foo/bar"));
114   expect("bar", CouchbaseDirectoryUtilities::basename("foo\\\\bar"));
115   expect("bar", CouchbaseDirectoryUtilities::basename("foo//bar"));
116   expect("bar", CouchbaseDirectoryUtilities::basename("bar"));
117   expect("", CouchbaseDirectoryUtilities::basename(""));
118   expect("bar", CouchbaseDirectoryUtilities::basename("\\bar"));
119   expect("bar", CouchbaseDirectoryUtilities::basename("\\\\bar"));
120   expect("bar", CouchbaseDirectoryUtilities::basename("/bar"));
121   expect("bar", CouchbaseDirectoryUtilities::basename("//bar"));
122   expect("6", CouchbaseDirectoryUtilities::basename("1/2/3/4/5/6"));
123   expect("6", CouchbaseDirectoryUtilities::basename("1\\2\\3\\4\\5\\6"));
124   expect("6", CouchbaseDirectoryUtilities::basename("1/2\\4/5\\6"));
125}
126
127static void testFindFilesWithPrefix(void) {
128   using namespace CouchbaseDirectoryUtilities;
129
130   std::vector<std::string> vec = findFilesWithPrefix("fs");
131   expect(1, vec);
132   contains("." PATH_SEPARATOR "fs", vec);
133
134   vec = findFilesWithPrefix("fs", "d");
135   expect(3, vec);
136   contains("fs" PATH_SEPARATOR "d1", vec);
137   contains("fs" PATH_SEPARATOR "d2", vec);
138   contains("fs" PATH_SEPARATOR "d3", vec);
139   vec = findFilesWithPrefix("fs", "1");
140   expect(1, vec);
141   contains("fs" PATH_SEPARATOR "1", vec);
142
143   vec = findFilesWithPrefix("fs", "");
144   expect(vfs.size() - 1, vec);
145
146}
147
148static void testFindFilesContaining(void) {
149   using namespace CouchbaseDirectoryUtilities;
150   std::vector<std::string> vec = findFilesContaining("fs", "");
151   expect(vfs.size() - 1, vec);
152
153   vec = findFilesContaining("fs", "2");
154   expect(7, vec);
155   contains("fs" PATH_SEPARATOR "d2", vec);
156   contains("fs" PATH_SEPARATOR "e2", vec);
157   contains("fs" PATH_SEPARATOR "f2c", vec);
158   contains("fs" PATH_SEPARATOR "g2", vec);
159   contains("fs" PATH_SEPARATOR "2", vec);
160   contains("fs" PATH_SEPARATOR "2c", vec);
161   contains("fs" PATH_SEPARATOR "2d", vec);
162}
163
164static void testRemove(void) {
165   fclose(fopen("test-file", "w"));
166   if (!CouchbaseDirectoryUtilities::rmrf("test-file")) {
167      std::cerr << "expected to delete existing file" << std::endl;
168   }
169   if (CouchbaseDirectoryUtilities::rmrf("test-file")) {
170      std::cerr << "Didn't expected to delete non-existing file" << std::endl;
171   }
172
173   if (!CouchbaseDirectoryUtilities::rmrf("fs")) {
174      std::cerr << "Expected to nuke the entire fs directory recursively" << std::endl;
175   }
176}
177
178static void testIsDirectory(void) {
179    using namespace CouchbaseDirectoryUtilities;
180#ifdef WIN32
181    expect(true, isDirectory("c:\\"));
182#else
183    expect(true, isDirectory("/"));
184#endif
185    expect(true, isDirectory("."));
186    expect(false, isDirectory("/it/would/suck/if/this/exists"));
187    FILE *fp = fopen("isDirectoryTest", "w");
188    if (fp == NULL) {
189        std::cerr << "Failed to create test file" << std::endl;
190        exit_value = EXIT_FAILURE;
191    } else {
192        using namespace std;
193        fclose(fp);
194        expect(false, isDirectory("isDirectoryTest"));
195        remove("isDirectoryTest");
196    }
197}
198
199static void testIsFile(void) {
200   using namespace CouchbaseDirectoryUtilities;
201   expect(false, isFile("."));
202   FILE* fp = fopen("plainfile", "w");
203   if (fp == nullptr) {
204      std::cerr << "Failed to create test file" << std::endl;
205      exit_value = EXIT_FAILURE;
206   } else {
207      fclose(fp);
208      expect(true, isFile("plainfile"));
209      rmrf("plainfile");
210   }
211}
212
213static void testMkdirp(void) {
214    using namespace CouchbaseDirectoryUtilities;
215
216#ifndef WIN32
217    expect(false, mkdirp("/it/would/suck/if/I/could/create/this"));
218#endif
219    expect(true, mkdirp("."));
220    expect(true, mkdirp("/"));
221    expect(true, mkdirp("foo/bar"));
222    expect(true, isDirectory("foo/bar"));
223    rmrf("foo");
224}
225
226static void testGetCurrentDirectory() {
227   try {
228      auto cwd = CouchbaseDirectoryUtilities::getcwd();
229      // I can't really determine the correct value here, but it shouldn't be
230      // empty ;-)
231      if (cwd.empty()) {
232         std::cerr << "FAIL: cwd should not be an empty string" << std::endl;
233         exit(EXIT_FAILURE);
234      }
235   } catch (const std::exception& ex) {
236      std::cerr << "FAIL: " << ex.what() << std::endl;
237      exit(EXIT_FAILURE);
238   }
239}
240
241static void testCbMktemp() {
242   auto filename = CouchbaseDirectoryUtilities::mktemp("foo");
243   if (filename.empty()) {
244      std::cerr << "FAIL: Expected to create tempfile without mask"
245                << std::endl;
246      exit(EXIT_FAILURE);
247   }
248
249   if (!CouchbaseDirectoryUtilities::isFile(filename)) {
250      std::cerr << "FAIL: Expected mktemp to create file" << std::endl;
251      exit(EXIT_FAILURE);
252   }
253
254   if (!CouchbaseDirectoryUtilities::rmrf(filename)) {
255      std::cerr << "FAIL: failed to remove temporary file" << std::endl;
256      exit(EXIT_FAILURE);
257   }
258
259   filename = CouchbaseDirectoryUtilities::mktemp("barXXXXXX");
260   if (filename.empty()) {
261      std::cerr << "FAIL: Expected to create tempfile with mask"
262                << std::endl;
263      exit(EXIT_FAILURE);
264   }
265
266   if (!CouchbaseDirectoryUtilities::isFile(filename)) {
267      std::cerr << "FAIL: Expected mktemp to create file" << std::endl;
268      exit(EXIT_FAILURE);
269   }
270
271   if (!CouchbaseDirectoryUtilities::rmrf(filename)) {
272      std::cerr << "FAIL: failed to remove temporary file" << std::endl;
273      exit(EXIT_FAILURE);
274   }
275}
276
277int main(int argc, char **argv)
278{
279   testDirname();
280   testBasename();
281
282   vfs.push_back("fs");
283   vfs.push_back("fs/d1");
284   vfs.push_back("fs/d2");
285   vfs.push_back("fs/e2");
286   vfs.push_back("fs/f2c");
287   vfs.push_back("fs/g2");
288   vfs.push_back("fs/d3");
289   vfs.push_back("fs/1");
290   vfs.push_back("fs/2");
291   vfs.push_back("fs/2c");
292   vfs.push_back("fs/2d");
293   vfs.push_back("fs/3");
294
295   std::list<std::string>::iterator ii;
296   for (ii = vfs.begin(); ii != vfs.end(); ++ii) {
297      if (!CreateDirectory(*ii)) {
298         if (!exists(*ii)) {
299            std::cerr << "Fatal: failed to setup test directory: "
300                      << *ii << std::endl;
301            exit(EXIT_FAILURE);
302         }
303      }
304   }
305
306   testFindFilesWithPrefix();
307   testFindFilesContaining();
308   testRemove();
309
310   testIsDirectory();
311   testIsFile();
312   testMkdirp();
313   testGetCurrentDirectory();
314   testCbMktemp();
315
316   return exit_value;
317}
318