simplified default directory in traversal test
[feisty_meow.git] / nucleus / library / tests_filesystem / test_directory_tree.cpp
1 /*
2 *  Name   : test_directory_tree
3 *  Author : Chris Koeritz
4 *  Purpose:
5 *    Tests the directory_tree object on some well-known directories.
6 **
7 * Copyright (c) 2001-$now By Author.  This program is free software; you can
8 * redistribute it and/or modify it under the terms of the GNU General Public
9 * License as published by the Free Software Foundation; either version 2 of
10 * the License or (at your option) any later version.  This is online at:
11 *     http://www.fsf.org/copyleft/gpl.html
12 * Please send any updates to: fred@gruntose.com
13 */
14
15 #include <application/hoople_main.h>
16 #include <basis/functions.h>
17 #include <basis/guards.h>
18 #include <filesystem/directory_tree.h>
19 #include <filesystem/filename.h>
20 #include <filesystem/filename_list.h>
21 #include <loggers/critical_events.h>
22 #include <loggers/program_wide_logger.h>
23 #include <processes/launch_process.h>
24 #include <structures/static_memory_gremlin.h>
25 #include <structures/string_array.h>
26 #include <textual/string_manipulation.h>
27 #include <unit_test/unit_base.h>
28
29 using namespace application;
30 using namespace basis;
31 using namespace mathematics;
32 using namespace filesystem;
33 using namespace loggers;
34 using namespace processes;
35 using namespace structures;
36 using namespace textual;
37 using namespace timely;
38 using namespace unit_test;
39
40 const bool JUST_SIZES = false;
41   // determines if we'll only compare file size and time.
42
43 #define DEBUG_TEST_DIRECTORY_TREE
44   // uncomment if you want noisy logging.
45
46 #define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger::get(), s)
47
48 class test_directory_tree : public virtual unit_base, virtual public application_shell
49 {
50 public:
51   test_directory_tree() : application_shell() {}
52   DEFINE_CLASS_NAME("test_directory_tree");
53   int execute();
54 };
55
56 int test_directory_tree::execute()
57 {
58   FUNCDEF("execute");
59
60 /* old approach.
61   astring path = "/usr/lib";
62 #ifdef __WIN32__
63   // default path for windoze uses an area that should always exist.
64   path = environment::get("COMMONPROGRAMFILES");
65 #endif
66 */
67
68 /*
69 hmmm: this test shows that our algorithms are poor; the tree traversal on feisty meow apex
70 should not take as long as it does.
71 get some timing around this, comparing it with other linux tools, like ls -R and find.
72 see if we have some ugly bottlenecks, or where they are, and fix them.
73 */
74
75   // new approach for where to look is at feisty meow itself.
76   // some type of introspection to be sure.
77   astring path = environment::get("FEISTY_MEOW_APEX");
78
79   // process the command line parameters, which are optionally a directory name and
80   // a pattern to use when scanning.
81   if (_global_argc >= 2)
82     path = _global_argv[1];
83
84   astring pattern = "*";
85   if (_global_argc >= 3)
86     pattern = _global_argv[2];
87
88   {
89 #ifdef DEBUG_TEST_DIRECTORY_TREE
90     log(astring("Scanning directory tree at \"") + path + "\"");
91     log(astring("Using pattern-match \"") + pattern + "\"");
92 #endif
93
94     directory_tree dir(path, pattern.s());
95     ASSERT_TRUE(dir.good(), "directory_tree construction should succeed and be readable.");
96
97     dir_tree_iterator *ted = dir.start(directory_tree::prefix);
98       // create our iterator to do a prefix traversal.
99
100     int depth;  // current depth in tree.
101     filename curr;  // the current path the iterator is at.
102     string_array files;  // the filenames held at the iterator.
103
104     while (directory_tree::current(*ted, curr, files)) {
105       // we have a good directory to show.
106       directory_tree::depth(*ted, depth);
107 #ifdef DEBUG_TEST_DIRECTORY_TREE
108       log(string_manipulation::indentation(depth * 2) + astring("[") + curr.raw() + "]");
109 #endif
110
111       astring names;
112       for (int i = 0; i < files.length(); i++) names += files[i] + " ";
113       if (names.length()) {
114         astring split;
115         string_manipulation::split_lines(names, split, depth * 2 + 2);
116 #ifdef DEBUG_TEST_DIRECTORY_TREE
117         log(split);
118 #endif
119       }
120
121       // go to the next place.
122       directory_tree::next(*ted);
123     }
124
125     directory_tree::throw_out(ted);
126   }
127
128   {
129     // second test group.  seek operation.
130 //scan the directory, create some temporary directories and junk filenames
131 //therein, then seek to that location.
132
133   }
134
135   {
136     // third test group.  tree comparison operation.
137 //    log(astring("Self-comparing directory tree at \"") + path + "\"");
138 //    log(astring("Using pattern-match \"") + pattern + "\"");
139
140 //    LOG("reading tree 1.");
141     directory_tree dir(path, pattern.s());
142     ASSERT_TRUE(dir.good(), "the directory should be readable for self-compare");
143
144     // now read a copy of the tree also.
145 //    LOG("reading tree 2.");
146     directory_tree dir2(path, pattern.s());
147     ASSERT_TRUE(dir2.good(), "the directory should read the second time fine too");
148
149     LOG("comparing the two trees.");
150     filename_list diffs;
151     directory_tree::compare_trees(dir, dir2, diffs, file_info::EQUAL_CHECKSUM_TIMESTAMP_FILESIZE);
152 LOG(diffs.text_form());
153
154     ASSERT_FALSE(diffs.elements(), "there should be no differences comparing identical dirs");
155   }
156
157   {
158     // fourth test: see if the calculate function works.
159 //    log(astring("Calculating sums for tree at \"") + path + "\"");
160 //    log(astring("Using pattern-match \"") + pattern + "\"");
161
162 //    LOG("reading tree 1.");
163     directory_tree dir(path, pattern.s());
164     ASSERT_TRUE(dir.good(), "the directory should be readable for checksums");
165
166     // now read a copy of the tree also.
167 //    LOG("reading tree 2.");
168     directory_tree dir2(path, pattern.s());
169     ASSERT_TRUE(dir2.good(), "checksummer should be able to read second time also");
170
171 //    LOG("calculating checksums for tree 1.");
172     ASSERT_TRUE(dir.calculate(JUST_SIZES), "the first checksummer tree can be calculated");
173
174 //    LOG("calculating checksums for tree 2.");
175     ASSERT_TRUE(dir2.calculate(JUST_SIZES), "the second checksummer tree can be calculated");
176
177 //    LOG("comparing the two trees.");
178     filename_list diffs;
179     directory_tree::compare_trees(dir, dir2, diffs, file_info::EQUAL_CHECKSUM_TIMESTAMP_FILESIZE);
180 //LOG(diffs.text_form());
181
182     ASSERT_FALSE(diffs.elements(), "no checksummer differences should be seen for identical directories");
183   }
184
185   {
186     // fifth test: see if the packing works.
187 //    log(astring("Reading tree for packing at \"") + path + "\"");
188 //    log(astring("Using pattern-match \"") + pattern + "\"");
189
190 //    LOG("reading tree.");
191     directory_tree dir(path, pattern.s());
192     ASSERT_TRUE(dir.good(), "packer directory should be read");
193
194 //    LOG("calculating checksums for tree.");
195     ASSERT_TRUE(dir.calculate(JUST_SIZES), "the first packer tree can be calculated");
196
197     byte_array packed_form;
198     int size_packed = dir.packed_size();
199     dir.pack(packed_form);
200 //LOG(a_sprintf("tree became %d abyte array", packed_form.length()));
201     ASSERT_EQUAL(size_packed, packed_form.length(), "packed size should be right");
202
203     directory_tree dir2;
204     ASSERT_TRUE(dir2.unpack(packed_form), "second tree can be unpacked from the first");
205
206 //    LOG("comparing the two trees.");
207     filename_list diffs;
208     directory_tree::compare_trees(dir, dir2, diffs, file_info::EQUAL_CHECKSUM_TIMESTAMP_FILESIZE);
209 //LOG(diffs.text_form());
210
211     ASSERT_FALSE(diffs.elements(), "identical directories should stay same after packing");
212
213     directory_tree::compare_trees(dir2, dir, diffs, file_info::EQUAL_CHECKSUM_TIMESTAMP_FILESIZE);
214     ASSERT_FALSE(diffs.elements(), "no differences for reverse compare identical dirs");
215   }
216
217   {
218     // sixth test: see if the make_directories function works.
219 LOG("reading tree to recreate");
220     directory_tree dir(path, pattern.s());
221     ASSERT_TRUE(dir.good(), "makedirs test directory reading");
222     filename tmpdir(environment::get("FEISTY_MEOW_GENERATED_STORE") + "/zz_balfazzaral");
223     LOG(astring("will write to tmp in ") + tmpdir);
224     basis::outcome result = dir.make_directories(tmpdir.raw());
225     ASSERT_EQUAL(result.value(), common::OKAY, "makedirs should succeed");
226     
227 LOG("what happened with that?  did it work?  merely rhetorical, since we need more code here.");
228
229 //hmmm: compare the directories with what we expect to be made;
230 //      do a dirtree iterator on the path, and make sure each of those exists in the target place.
231
232
233     // clean up the output directory.
234 //this won't do it; it's a directory!
235 //    bool worked = tmpdir.recursive_unlink();    
236 //    ASSERT_TRUE(worked, "removing temporary files after test");
237
238 //hmmm: plug in real recursive delete here instead.
239 basis::un_int kid;
240 launch_process::run("/bin/rm", astring("-rf ") + tmpdir.raw(), launch_process::AWAIT_APP_EXIT, kid);
241 ASSERT_FALSE(kid, "removing temporary files after test");
242
243   }
244
245
246 // nth test:
247 // combine the results of the second test with a comparison like in the
248 // third test.  delete all of those temporary files that were added.
249 // rescan tree. make sure that a tree containing the temporaries
250 // when compared with the current post-deletion tree produces a list
251 // that contains all the temporary files and directories.
252
253
254 //hmmm: more tests!
255
256   return final_report();
257 }
258
259 HOOPLE_MAIN(test_directory_tree, )
260
261