feisty meow concerns codebase 2.140
test_filename.cpp
Go to the documentation of this file.
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
16#include <basis/functions.h>
17#include <basis/guards.h>
18#include <basis/astring.h>
23#include <filesystem/filename.h>
26#include <textual/parser_bits.h>
27#include <unit_test/unit_base.h>
28
29using namespace application;
30using namespace basis;
31using namespace configuration;
32using namespace mathematics;
33using namespace filesystem;
34using namespace loggers;
35using namespace structures;
36using namespace textual;
37using namespace timely;
38using namespace unit_test;
39
40#define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger::get(), s)
41
42class test_filename : virtual public unit_base, public virtual application_shell
43{
44public:
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
57void test_filename::clean_sequel(astring &sequel)
58{ sequel.replace_all('\\', '/'); }
59
60astring test_filename::virtual_root()
61{
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
71void 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
90bool 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
95dump_string_array("exemplar", exemplar);
96dump_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*/
117bool 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
146int 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++) {
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
379HOOPLE_MAIN(test_filename, )
380
The application_shell is a base object for console programs.
virtual int execute()=0
< retrieves the command line from the /proc hierarchy on linux.
application_shell()
constructs an application_shell to serve as the root of the program.
a_sprintf is a specialization of astring that provides printf style support.
Definition astring.h:440
int length() const
Returns the current reported length of the allocated C array.
Definition array.h:115
Provides a dynamically resizable ASCII character string.
Definition astring.h:35
bool t() const
t() is a shortcut for the string being "true", as in non-empty.
Definition astring.h:97
bool equal_to(const char *that) const
returns true if "that" is equal to this.
Definition astring.cpp:159
bool replace_all(char to_replace, char new_char)
changes all occurrences of "to_replace" with "new_char".
Definition astring.cpp:932
int length() const
Returns the current length of the string.
Definition astring.cpp:132
A very common template for a dynamic array of bytes.
Definition byte_array.h:36
static const basis::astring & virtual_unix_root()
returns the path to the unix root, which may be simulated.
Provides operations commonly needed on file names.
Definition filename.h:64
static bool separator(char is_it)
returns true if the character "is_it" in question is a separator.
Definition filename.cpp:118
virtual bool unpack(basis::byte_array &packed_form)
Restores the packable from the "packed_form".
Definition filename.cpp:471
void separate(bool &rooted, structures::string_array &pieces) const
breaks the filename into its component parts.
Definition filename.cpp:482
void push(const basis::astring &to_push)
pushes a new filename onto the current pathname.
Definition filename.cpp:152
basis::astring pop()
removes the deepest component of the pathname.
Definition filename.cpp:138
An array of strings with some additional helpful methods.
bool equal_to(const equalizable &to_compare) const
Compares this string array for equality with "to_compare".
#define DEFINE_CLASS_NAME(objname)
Defines the name of a class by providing a couple standard methods.
Definition enhance_cpp.h:42
#define FUNCDEF(func_in)
FUNCDEF sets the name of a function (and plugs it into the callstack).
Definition enhance_cpp.h:54
Provides macros that implement the 'main' program of an application.
#define HOOPLE_MAIN(obj_name, obj_args)
options that should work for most unix and linux apps.
Definition hoople_main.h:61
Implements an application lock to ensure only one is running at once.
char ** _global_argv
The guards collection helps in testing preconditions and reporting errors.
Definition array.h:30
A platform independent way to obtain the timestamp of a file.
A logger that sends to the console screen using the standard output device.
An extension to floating point primitives providing approximate equality.
Definition averager.h:21
A dynamic container class that holds any kind of object via pointers.
Definition amorph.h:55
#include <time.h>
Useful support functions for unit testing, especially within hoople.
Definition unit_base.cpp:35
#define LOG(s)
#define ASSERT_EQUAL(a, b, test_name)
Definition unit_base.h:38
#define ASSERT_TRUE(a, test_name)
Definition unit_base.h:46
#define ASSERT_FALSE(a, test_name)
Definition unit_base.h:50