92cf1eb4e4681f02c124d547f3df5a8d66e7aa15
[feisty_meow.git] / nucleus / library / tests_filesystem / test_filename.cpp
1 /*
2 *  Name   : test_filename
3 *  Author : Chris Koeritz
4 **
5 * Copyright (c) 1993-$now By Author.  This program is free software; you can  *
6 * redistribute it and/or modify it under the terms of the GNU General Public  *
7 * License as published by the Free Software Foundation; either version 2 of   *
8 * the License or (at your option) any later version.  This is online at:      *
9 *     http://www.fsf.org/copyleft/gpl.html                                    *
10 * Please send any updates to: fred@gruntose.com                               *
11 */
12
13 #define DEBUG_FILENAME_TEST
14
15 #include <application/hoople_main.h>
16 #include <basis/functions.h>
17 #include <basis/guards.h>
18 #include <basis/astring.h>
19 #include <configuration/application_configuration.h>
20 #include <loggers/critical_events.h>
21 #include <loggers/logging_macros.h>
22 #include <loggers/program_wide_logger.h>
23 #include <filesystem/filename.h>
24 #include <structures/static_memory_gremlin.h>
25 #include <structures/string_array.h>
26 #include <textual/parser_bits.h>
27 #include <unit_test/unit_base.h>
28
29 using namespace application;
30 using namespace basis;
31 using namespace configuration;
32 using namespace mathematics;
33 using namespace filesystem;
34 using namespace loggers;
35 using namespace structures;
36 using namespace textual;
37 using namespace timely;
38 using namespace unit_test;
39
40 #define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger::get(), s)
41
42 class test_filename : virtual public unit_base, public virtual application_shell
43 {
44 public:
45   test_filename() : application_shell() {}
46   DEFINE_CLASS_NAME("test_filename");
47   virtual int execute();
48   void clean_sequel(astring &sequel);
49   astring virtual_root();
50   void dump_string_array(const astring &title, const string_array &to_dump);
51   bool verify_equal_string_array(const astring &group, const string_array &exemplar, const string_array &acolyte);
52   bool prepare_string_arrays_for_filenames(const astring &common_bit, const astring &group,
53       bool &exemplar_rooted, string_array &exemplar_pieces, bool &acolyte_rooted,
54       string_array &acolyte_pieces);
55 };
56
57 void test_filename::clean_sequel(astring &sequel)
58 { sequel.replace_all('\\', '/'); }
59
60 astring test_filename::virtual_root()
61 {
62   astring virt_root = application_configuration::virtual_unix_root();
63   if (!!virt_root && !filename::separator(virt_root[virt_root.length() - 1])) {
64     // this is not terminated with a slash, which is possible for dosdows.
65     // we'll make it a reliable directory component by adding a slash.
66     virt_root += astring("/");
67   }
68   return virt_root;
69 }
70
71 void test_filename::dump_string_array(const astring &title, const string_array &to_dump)
72 {
73   FUNCDEF("dump_string_array");
74   LOG(title);
75   for (int i = 0; i < to_dump.length(); i++) {
76     LOG(a_sprintf("%d: '", i) + to_dump[i] + "'");
77   }
78 }
79
80 /*
81   due to some difference in behavior between the platforms, we need to turn
82   rooted paths that work perfectly find on unix systems into a bizarre messed
83   up c drive version for windows (based on the virtual unix system in place,
84   although only cygwin is currently supported).  this assumes the virtual root
85   is available...  we accomplish our testing a platform invariant way by by
86   simulating the same operations filename does, but using our exemplar paths
87   as the starting point.
88 */
89
90 bool test_filename::verify_equal_string_array(const astring &group, const string_array &exemplar, const string_array &acolyte)
91 {
92   FUNCDEF("verify_equal_string_array");
93
94 //temp debug
95 dump_string_array("exemplar", exemplar);
96 dump_string_array("acolyte", acolyte);
97
98   // doing some extra assertions in here, to complain about what went wrong, but then we still need to return a success value.
99   ASSERT_EQUAL(exemplar.length(), acolyte.length(), group + "the list was the wrong length");
100   if (exemplar.length() != acolyte.length()) { return false; }
101
102   for (int indy = 0; indy < exemplar.length(); indy++) {
103     bool success = acolyte[indy].equal_to(exemplar[indy]);
104     ASSERT_TRUE(success, group + a_sprintf("piece %d did not match: ", indy) + "'" 
105         + acolyte[indy] + "' vs expected '" + exemplar[indy] + "'");
106     if (!success) { return false; }  // fail fast.
107   }
108   return true;
109 }
110
111 /*
112   helper method constructs string arrays for the filename with common_bit as
113   the portion after the root ('/').  the exemplar array is generated
114   independently from the acolyte string array to ensure that it is correctly
115   constructed (with a virtual root and then a non-rooted chunk).
116 */
117 bool test_filename::prepare_string_arrays_for_filenames(const astring &common_bit, const astring &group,
118     bool &exemplar_rooted, string_array &exemplar_pieces,
119     bool &acolyte_rooted, string_array &acolyte_pieces)
120 {
121   FUNCDEF("prepare_string_arrays_for_filenames")
122   bool to_return = true;  // success until we learn otherwise.
123
124   // generate the acolyte, which will be tested again, very straightforwardly.
125   // it is a non-rooted string, so we just slap the virtual root in front.
126   filename acolyte_fn(virtual_root() + common_bit);
127   acolyte_fn.separate(acolyte_rooted, acolyte_pieces);
128
129   // generate the exemplar without allowing filename to operate on the whole
130   // string.  we get the virtual root first and operate on it as a filename,
131   // then we slap on the unrooted portion to get the unmanipulated form.
132   filename(virtual_root()).separate(exemplar_rooted, exemplar_pieces);
133   {
134     string_array common_pieces;
135     bool common_rooted;
136     filename(common_bit).separate(common_rooted, common_pieces);
137     ASSERT_FALSE(common_rooted, group + "the common_rooted value is erreonous");
138     if (common_rooted) { to_return = false; }
139     // conjoin the rooty pieces with the common bits, hopefully hitting both platforms' sweet spots.
140     exemplar_pieces += common_pieces;
141   }
142
143   return to_return;
144 }
145
146 int test_filename::execute()
147 {
148   FUNCDEF("execute")
149   {
150     // first test group.
151     filename gorgeola("");
152     ASSERT_FALSE(gorgeola.exists(), "an empty filename should not exist");
153   }
154
155
156   {
157     // second test group.
158
159     astring GROUP = "testing separate() ";
160     astring common_bit = "omega/ralph/turkey/buzzard.txt";
161     string_array turkey_pieces;
162     bool turkey_rooted;
163     string_array exemplar_pieces;
164     bool exemplar_rooted;
165     bool worked = test_filename::prepare_string_arrays_for_filenames(common_bit, GROUP,
166         exemplar_rooted, exemplar_pieces, turkey_rooted, turkey_pieces);
167
168     ASSERT_EQUAL(turkey_rooted, exemplar_rooted, GROUP + "the turkey_rooted value is erreonous.");
169     ASSERT_TRUE(verify_equal_string_array(GROUP, exemplar_pieces, turkey_pieces), "the turkey array differs from exemplar");
170   }
171
172   {
173     // third test group.
174     astring GROUP = "third: test compare_prefix ";
175     filename turkey(virtual_root() + "omega/ralph/turkey/buzzard.txt");
176     filename murpin1(virtual_root() + "omega");
177     filename murpin2(virtual_root() + "omega/ralph");
178     filename murpin3(virtual_root() + "omega/ralph/turkey");
179     filename murpin4(virtual_root() + "omega/ralph/turkey/buzzard.txt");
180     filename murpin_x1("ralph/turkey/buzzard.txt");
181     filename murpin_x2(virtual_root() + "omega/ralph/turkey/buzzard.txt2");
182     filename murpin_x3(virtual_root() + "omega/turkey/buzzard.txt");
183     filename murpin_x4(virtual_root() + "omega/ralph/turkey/b0/buzzard.txt");
184     filename murpin_x5("moomega/ralph/turkey");
185
186     astring sequel;
187     ASSERT_TRUE(murpin1.compare_prefix(turkey, sequel), GROUP + "first should match but didn't");
188     clean_sequel(sequel);
189     ASSERT_TRUE(sequel.equal_to("ralph/turkey/buzzard.txt"), GROUP + "first sequel was wrong");
190     ASSERT_TRUE(murpin2.compare_prefix(turkey, sequel), GROUP + "second should match but didn't");
191     clean_sequel(sequel);
192     ASSERT_TRUE(sequel.equal_to("turkey/buzzard.txt"), GROUP + "second sequel was wrong");
193     ASSERT_TRUE(murpin3.compare_prefix(turkey, sequel), GROUP + "third should match but didn't");
194     clean_sequel(sequel);
195     ASSERT_TRUE(sequel.equal_to("buzzard.txt"), GROUP + "third sequel was wrong");
196     ASSERT_TRUE(murpin4.compare_prefix(turkey, sequel), GROUP + "fourth should match but didn't");
197     ASSERT_FALSE(sequel.t(), GROUP + "fourth had a sequel but shouldn't");
198
199     ASSERT_FALSE(murpin_x1.compare_prefix(turkey, sequel),
200         GROUP + "x-first should not match but did");
201     ASSERT_FALSE(sequel.t(),
202         GROUP + "x-first had a sequel but shouldn't");
203     ASSERT_FALSE(murpin_x2.compare_prefix(turkey, sequel),
204         GROUP + "x-second should not match but did");
205     ASSERT_FALSE(sequel.t(),
206         GROUP + "x-second had a sequel but shouldn't");
207     ASSERT_FALSE(murpin_x3.compare_prefix(turkey, sequel),
208         GROUP + "x-third should not match but did");
209     ASSERT_FALSE(sequel.t(),
210         GROUP + "x-third had a sequel but shouldn't");
211     ASSERT_FALSE(murpin_x4.compare_prefix(turkey, sequel),
212         GROUP + "x-fourth should not match but did");
213     ASSERT_FALSE(sequel.t(),
214         GROUP + "x-fourth had a sequel but shouldn't");
215     ASSERT_FALSE(murpin_x5.compare_prefix(turkey, sequel),
216         GROUP + "x-fifth should not match but did");
217     ASSERT_FALSE(sequel.t(),
218         GROUP + "x-fifth had a sequel but shouldn't");
219
220     // check that the functions returning no sequel are still correct.
221     ASSERT_TRUE(murpin1.compare_prefix(turkey), GROUP + "the two versions differed!");
222     ASSERT_FALSE(murpin_x1.compare_prefix(turkey), GROUP + "x-the two versions differed!");
223   }
224
225   {
226     // fourth test group.
227     astring GROUP = "fourth: test compare_suffix ";
228     filename turkey(virtual_root() + "omega/ralph/turkey/buzzard.txt");
229     filename murpin1("turkey\\buzzard.txt");
230     filename murpin2("turkey/buzzard.txt");
231     filename murpin3("ralph/turkey/buzzard.txt");
232     filename murpin4("omega/ralph/turkey/buzzard.txt");
233     filename murpin5(virtual_root() + "omega/ralph/turkey/buzzard.txt");
234
235     ASSERT_TRUE(murpin1.compare_suffix(turkey), GROUP + "compare 1 failed");
236     ASSERT_TRUE(murpin2.compare_suffix(turkey), GROUP + "compare 2 failed");
237     ASSERT_TRUE(murpin3.compare_suffix(turkey), GROUP + "compare 3 failed");
238     ASSERT_TRUE(murpin4.compare_suffix(turkey), GROUP + "compare 4 failed");
239     ASSERT_TRUE(murpin5.compare_suffix(turkey), GROUP + "compare 5 failed");
240
241     ASSERT_FALSE(turkey.compare_suffix(murpin1), GROUP + "compare x.1 failed");
242   }
243
244   {
245     // fifth test group.
246     // tests out the canonicalization method on any parameters given on
247     // the command line, including the program name.
248     astring GROUP = "fifth: canonicalize command-line paths ";
249 //    log(GROUP, ALWAYS_PRINT);
250     for (int i = 0; i < application::_global_argc; i++) {
251       filename canony(application::_global_argv[i]);
252 //      log(a_sprintf("parm %d:\n\tfrom \"%s\"\n\t  to \"%s\"", i, application::_global_argv[i],
253 //           canony.raw().s()), ALWAYS_PRINT);
254
255 //hmmm: the above wasn't really a test so much as a look at what we did.
256 //      we should run the canonicalizer against a set of known paths so we can know what to
257 //      expect.
258
259     }
260   }
261
262   {
263     // sixth test group.
264     astring GROUP = "sixth: testing pop and push ";
265     // test dossy paths.
266     filename test1("c:/flug/blumen/klemper/smooden");
267 //log(astring("base=") + test1.basename(), ALWAYS_PRINT);
268     ASSERT_EQUAL(test1.basename(), astring("smooden"), GROUP + "basename 1 failed");
269 //log(astring("got past basename 1 test that was failing."));
270     ASSERT_EQUAL(test1.dirname(), filename("c:/flug/blumen/klemper"),
271         GROUP + "d-dirname 1 failed");
272 //log(astring("got past a test or so after that."));
273     filename test2 = test1;
274     astring popped = test2.pop();
275     ASSERT_EQUAL(popped, astring("smooden"), GROUP + "dpop 1 return failed");
276     ASSERT_EQUAL(test2, filename("c:/flug/blumen/klemper"), GROUP + "dpop 1 failed");
277     test2.pop();
278     test2.pop();
279     ASSERT_EQUAL(test2, filename("c:/flug"), GROUP + "dpop 2 failed");
280     popped = test2.pop();
281     ASSERT_EQUAL(popped, astring("flug"), GROUP + "dpop 1 return failed");
282     ASSERT_EQUAL(test2, filename("c:/"), GROUP + "dpop 3 failed");
283     test2.pop();
284     ASSERT_EQUAL(test2, filename("c:/"), GROUP + "dpop 3 failed");
285     test2.push("flug");
286     test2.push("blumen");
287     test2.push("klemper");
288     ASSERT_EQUAL(test2, filename("c:/flug/blumen/klemper"), GROUP + "dpush 1 failed");
289     // test unix paths.
290     filename test3(virtual_root() + "flug/blumen/klemper/smooden");
291     ASSERT_EQUAL(test3.basename(), astring("smooden"), GROUP + "basename 1 failed");
292     ASSERT_EQUAL(test3.dirname(), filename(virtual_root() + "flug/blumen/klemper"),
293         GROUP + "u-dirname 1 failed");
294     filename test4 = test3;
295     popped = test4.pop();
296     ASSERT_EQUAL(popped, astring("smooden"), GROUP + "upop 1 return failed");
297     ASSERT_EQUAL(test4, filename(virtual_root() + "flug/blumen/klemper"), GROUP + "upop 1 failed");
298     test4.pop();
299     test4.pop();
300     ASSERT_EQUAL(test4, filename(virtual_root() + "flug"), GROUP + "upop 2 failed");
301     popped = test4.pop();
302     ASSERT_EQUAL(popped, astring("flug"), GROUP + "upop 2 return failed");
303     ASSERT_EQUAL(test4, filename(virtual_root()), GROUP + "upop 3 failed");
304     test4.pop();
305     filename special_popped = filename(virtual_root());
306     special_popped.pop();
307     ASSERT_EQUAL(test4, special_popped, GROUP + "upop 4 failed");
308     test4 = filename(virtual_root());
309     test4.push("flug");
310     test4.push("blumen");
311     test4.push("klemper");
312     ASSERT_EQUAL(test4, filename(virtual_root() + "flug/blumen/klemper"), GROUP + "upush 1 failed");
313   }
314   {
315     // seventh test group.
316     astring GROUP = "seventh: testing pack and unpack ";
317     filename test1(virtual_root() + "usr/local/athabasca");
318     byte_array packed;
319     int size_guess = test1.packed_size();
320     test1.pack(packed);
321     ASSERT_EQUAL(size_guess, packed.length(), GROUP + "packed_size 1 failed");
322     filename test2;
323     ASSERT_TRUE(test2.unpack(packed), GROUP + "unpack 1 failed");
324     ASSERT_EQUAL(test2, test1, GROUP + "packed contents differ, 1 failed");
325   }
326 #ifdef __WIN32__
327   {
328     // eighth test group is only for windows side.
329     astring GROUP = "eighth: cygwin and msys paths ";
330     filename test1("/cygdrive/q/marbles");
331     ASSERT_EQUAL(test1, astring("q:/marbles"), GROUP + "test 1 failed");
332     filename test2("/cygdrive/r");
333     ASSERT_EQUAL(test2, astring("r:/"), GROUP + "test 2 failed");
334     filename test3("/cygdrive/r/");
335     ASSERT_EQUAL(test3, astring("r:/"), GROUP + "test 3 failed");
336     // this is a broken pattern, which we don't expect to resolve to a drive.
337     filename test4("/cygdrive//");
338     ASSERT_EQUAL(test4, virtual_root() + "cygdrive", GROUP + "test 4 failed");
339     // another broken pattern.
340     filename test5("/cygdrive/");
341     ASSERT_EQUAL(test5, virtual_root() + "cygdrive", GROUP + "test 5 failed");
342     // and one more.  not great tests, but whatever.
343     filename test6("/cygdrive");
344     ASSERT_EQUAL(test6, virtual_root() + "cygdrive", GROUP + "test 6 failed");
345     filename test7(virtual_root() + "klaunspendle");
346     ASSERT_EQUAL(test7, astring(virtual_root() + "klaunspendle"), GROUP + "test 7 failed");
347     filename test8("z:/klaunspendle");
348     ASSERT_EQUAL(test8, astring("z:/klaunspendle"), GROUP + "test 8 failed");
349
350     filename test10("/q/borkage");
351     ASSERT_EQUAL(test10, astring("q:/borkage"), GROUP + "test 10 failed");
352     filename test11("/q/r");
353     ASSERT_EQUAL(test11, astring("q:/r"), GROUP + "test 11 failed");
354     filename test12("/q/r/");
355     ASSERT_EQUAL(test12, astring("q:/r"), GROUP + "test 12 failed");
356     filename test13("/q/r/x");
357     ASSERT_EQUAL(test13, astring("q:/r/x"), GROUP + "test 13 failed");
358     filename test14("/r/");
359     ASSERT_EQUAL(test14, astring("r:/"), GROUP + "test 14 failed");
360     filename test15("/r");
361     ASSERT_EQUAL(test15, astring("r:/"), GROUP + "test 15 failed");
362
363     bool ex_rooted, ac_rooted;
364     string_array exemplar, acolyte;
365     ASSERT_TRUE(prepare_string_arrays_for_filenames(astring(""), GROUP,
366         ex_rooted, exemplar, ac_rooted, acolyte), GROUP + "test 16 failed prep");
367     ASSERT_TRUE(verify_equal_string_array(GROUP, exemplar, acolyte), GROUP + "test 16 failed compare");
368
369     filename test17("r/");
370     ASSERT_EQUAL(test17, astring("r/"), GROUP + "test 17 failed");
371     filename test18(virtual_root() + "kr/soop");
372     ASSERT_EQUAL(test18, astring(virtual_root() + "kr/soop"), GROUP + "test 18 failed");
373   }
374 #endif
375
376   return final_report();
377 }
378
379 HOOPLE_MAIN(test_filename, )
380