fd1efb04a45fd63cb2ecac57d66d33c486d2a863
[libi2ncommon] / test / test_filefunc.cpp
1 /*
2 The software in this package is distributed under the GNU General
3 Public License version 2 (with a special exception described below).
4
5 A copy of GNU General Public License (GPL) is included in this distribution,
6 in the file COPYING.GPL.
7
8 As a special exception, if other files instantiate templates or use macros
9 or inline functions from this file, or you compile this file and link it
10 with other works to produce a work based on this file, this file
11 does not by itself cause the resulting work to be covered
12 by the GNU General Public License.
13
14 However the source code for this file must still be made available
15 in accordance with section (3) of the GNU General Public License.
16
17 This exception does not invalidate any other reasons why a work based
18 on this file might be covered by the GNU General Public License.
19 */
20 /** @file
21  *
22  * tests for the modules "filefunc", "daemonfunc"
23  *
24  * (c) Copyright 2007-2008 by Intra2net AG
25  */
26
27 //#define NOISEDEBUG
28
29 #include <string>
30 #include <vector>
31 #include <list>
32 #include <set>
33 #include <iostream>
34 #include <iomanip>
35 #include <fstream>
36 #include <sstream>
37 #include <algorithm>
38
39 #define BOOST_TEST_DYN_LINK
40 #include <boost/test/unit_test.hpp>
41
42 #include <filefunc.hxx>
43 #include <daemonfunc.hpp>
44 #include <pipestream.hxx>
45 #include <stringfunc.hxx>
46
47 #ifdef NOISEDEBUG
48 #define DOUT(msg) std::cout << msg << std::endl
49 #else
50 #define DOUT(msg) do {} while (0)
51 #endif
52
53 using namespace std;
54 using namespace I2n;
55
56 class TestFileFuncFixture
57 {
58 protected:
59    typedef std::list< std::string > StringList;
60    std::set<std::string>  used_check_files;
61
62    std::string get_check_file_path(std::string tag)
63    {
64       std::string result;
65       result= "__unittest__" + tag + ".dat";
66       used_check_files.insert(result);
67       return result;
68    } // eo get_check_file_path
69
70    void remove_check_files()
71    {
72       for (std::set<std::string>::iterator it= used_check_files.begin();
73             it != used_check_files.end();
74             ++it)
75       {
76          std::string filepath(*it);
77          if (path_exists(filepath))
78          {
79             unlink(filepath);
80          }
81          //TODO
82       }
83       used_check_files.clear();
84    } // eo remove_check_files
85
86 public:
87     TestFileFuncFixture()
88     {
89     }
90
91     ~TestFileFuncFixture()
92     {
93         remove_check_files();
94     }
95 };
96
97 BOOST_FIXTURE_TEST_SUITE(TestFileFunc, TestFileFuncFixture)
98
99 BOOST_AUTO_TEST_CASE(StatTest1)
100 {
101     I2n::Stat stat("Makefile");
102
103     BOOST_CHECK_EQUAL( true, (bool)stat );
104     BOOST_CHECK_EQUAL( true, stat.is_regular_file() );
105     BOOST_CHECK_EQUAL( false, stat.is_directory() );
106
107     stat= Stat("/dev/null");
108
109     BOOST_CHECK_EQUAL( true, (bool)stat );
110     BOOST_CHECK_EQUAL( false, stat.is_regular_file() );
111     BOOST_CHECK_EQUAL( false, stat.is_directory() );
112     BOOST_CHECK_EQUAL( true, stat.is_device() );
113     BOOST_CHECK_EQUAL( true, stat.is_character_device() );
114     BOOST_CHECK_EQUAL( false, stat.is_block_device() );
115 } // eo StatTest1
116
117 BOOST_AUTO_TEST_CASE(StatRecheck)
118 {
119     // just to be sure
120     unlink(".foobar");
121
122     I2n::Stat stat(".foobar");
123     BOOST_CHECK_EQUAL( false, (bool)stat );
124
125     write_file(".foobar","hello world");
126
127     stat.recheck();
128
129     BOOST_CHECK_EQUAL( true, (bool)stat );
130     BOOST_CHECK_EQUAL( true, stat.size() > 0 );
131
132     unlink(".foobar");
133 }
134
135 BOOST_AUTO_TEST_CASE(DirTest1)
136 {
137     typedef std::vector< std::string > StringVector;
138     StringVector names;
139
140     bool res= I2n::get_dir(".",names);
141
142     BOOST_CHECK_EQUAL( true, res );
143     BOOST_CHECK( ! names.empty() );
144
145     StringVector::iterator it = std::find( names.begin(), names.end(), "Makefile");
146     BOOST_CHECK( it != names.end() );
147
148     it = std::find( names.begin(), names.end(), "." );
149     BOOST_CHECK( it == names.end() );
150
151     names= get_dir(".",true);
152     BOOST_CHECK( ! names.empty() );
153
154     for (it= names.begin(); it!=names.end(); ++it)
155     {
156         DOUT("  \"" << *it << "\"");
157         BOOST_CHECK_EQUAL( false, it->empty() );
158     }
159
160     it = std::find( names.begin(), names.end(), "." );
161     BOOST_CHECK( it != names.end() );
162 } // eo DirTest1
163
164
165
166 BOOST_AUTO_TEST_CASE(PathCuts1)
167 {
168     std::string path1("/an/absolute/path");
169
170     BOOST_CHECK_EQUAL( std::string("/an/absolute"), dirname(path1) );
171     BOOST_CHECK_EQUAL( std::string("path"), basename(path1) );
172
173     std::string path2("just.a.name");
174     BOOST_CHECK_EQUAL( std::string("just.a.name"), basename(path2) );
175     BOOST_CHECK_EQUAL( std::string("."), dirname(path2) );
176 } // eo PathCuts1()
177
178
179
180 BOOST_AUTO_TEST_CASE(NormalizePath1)
181 {
182     std::string path;
183
184     path= normalize_path("/a/simple/path/");
185     BOOST_CHECK_EQUAL( std::string("/a/simple/path"), path );
186
187     path= normalize_path("//another///simple/.//path//");
188     BOOST_CHECK_EQUAL( std::string("/another/simple/path"), path );
189
190     path= normalize_path("//..//..//a/dummy///../././simple/././/path//");
191     BOOST_CHECK_EQUAL( std::string("/a/simple/path"), path );
192
193     path= normalize_path("../a/dummy//././..//simple//nice//absolute//.././..//relative/path//");
194     BOOST_CHECK_EQUAL( std::string("../a/simple/relative/path"), path );
195
196     path= normalize_path("../../a/dummy//././..//simple/../nice//absolute//../.x/..//relative/path//");
197     BOOST_CHECK_EQUAL( std::string("../../a/nice/relative/path"), path );
198
199 } // eo NormalizePath1
200
201 BOOST_AUTO_TEST_CASE(NormalizePath2)
202 {
203     std::string path;
204
205     path= normalize_path("/");
206     BOOST_CHECK_EQUAL( std::string("/"), path );
207
208     path= normalize_path("//");
209     BOOST_CHECK_EQUAL( std::string("/"), path );
210
211     path= normalize_path("/.//");
212     BOOST_CHECK_EQUAL( std::string("/"), path );
213
214     path= normalize_path(".");
215     BOOST_CHECK_EQUAL( std::string(""), path );
216
217     path= normalize_path("./");
218     BOOST_CHECK_EQUAL( std::string(""), path );
219
220     path= normalize_path(".///");
221     BOOST_CHECK_EQUAL( std::string(""), path );
222
223     path= normalize_path("/./data/files");
224     BOOST_CHECK_EQUAL( std::string("/data/files"), path );
225
226     path= normalize_path("./data/files/");
227     BOOST_CHECK_EQUAL( std::string("data/files"), path );
228 } // eo NormalizePath2
229
230 BOOST_AUTO_TEST_CASE(TestUserAndGroupStuff1)
231 {
232     User user_root((uid_t)0);
233     BOOST_CHECK_EQUAL( true, user_root.is_valid() );
234     BOOST_CHECK_EQUAL( true, (bool)user_root );
235
236     BOOST_CHECK_EQUAL( std::string("root"), user_root.Name );
237     BOOST_CHECK_EQUAL( (uid_t)0, user_root.Uid );
238     BOOST_CHECK_EQUAL( (gid_t)0, user_root.Gid );
239
240     User user_root2("root");
241     BOOST_CHECK_EQUAL( true, user_root2.is_valid() );
242     BOOST_CHECK_EQUAL( true, (bool)user_root2 );
243
244     BOOST_CHECK_EQUAL( std::string("root"), user_root2.Name );
245     BOOST_CHECK_EQUAL( (uid_t)0, user_root2.Uid );
246     BOOST_CHECK_EQUAL( (gid_t)0, user_root2.Gid );
247
248     Group group_root("root");
249     BOOST_CHECK_EQUAL( true, group_root.is_valid() );
250     BOOST_CHECK_EQUAL( true, (bool)group_root );
251
252     BOOST_CHECK_EQUAL( std::string("root"), group_root.Name );
253     BOOST_CHECK_EQUAL( (gid_t)0, group_root.Gid );
254
255 } // TestUserAndGroupStuff1()
256
257 BOOST_AUTO_TEST_CASE(TestUserAndGroupStuff2)
258 {
259     // Test if is_valid() works correctly
260     User user_root;
261     BOOST_CHECK_EQUAL( false, user_root.is_valid() );
262
263     Group group_root;
264     BOOST_CHECK_EQUAL( false, group_root.is_valid() );
265 }
266
267 BOOST_AUTO_TEST_CASE(TestFileModes1)
268 {
269     std::string path= get_check_file_path("FileModes1");
270
271     write_file(path,"42");
272
273     Stat stat(path, false);
274     BOOST_CHECK_EQUAL( true, stat.is_valid() );
275
276     User user( stat.uid() );
277     Group group( stat.gid() );
278
279     BOOST_CHECK_EQUAL( true, user.is_valid() );
280     BOOST_CHECK_EQUAL( true, group.is_valid() );
281
282     bool res=chown( path, user.Name.c_str(), group.Gid );
283
284     BOOST_CHECK_EQUAL( true, res );
285 } // eo TestFileModes1()
286
287
288
289 BOOST_AUTO_TEST_CASE(TestPidOf1)
290 {
291     using I2n::Daemon::pid_of;
292
293     std::vector< pid_t > pid_list;
294
295     bool res= pid_of("init", pid_list);
296     BOOST_CHECK_EQUAL( true, res);
297     
298     if (pid_list.empty())
299     {
300         res= pid_of("systemd", pid_list);
301         BOOST_CHECK_EQUAL( true, res);
302     }
303     
304     BOOST_CHECK_EQUAL( false, pid_list.empty() );
305
306     std::vector< pid_t >::const_iterator pos1 =
307         std::find( pid_list.begin(), pid_list.end(), 1);
308
309     BOOST_CHECK( pos1 != pid_list.end() );
310 } // eo TestPidOf1()
311
312 BOOST_AUTO_TEST_CASE(TestCopyFileSourceFail)
313 {
314     bool res = copy_file("does not exist", "destination");
315     BOOST_CHECK_EQUAL( false, res );
316 }
317
318 BOOST_AUTO_TEST_CASE(TestCopyFileDestFail)
319 {
320     bool res = copy_file("/etc/HOSTNAME", "/proc/not/writable");
321     BOOST_CHECK_EQUAL( false, res );
322 }
323
324 BOOST_AUTO_TEST_CASE(TestCopyFileOk)
325 {
326     string input = "copy_source";
327     string data = "test";
328
329     long input_size = 0;
330     ofstream finput(input.c_str());
331     if (finput)
332     {
333         finput << data;
334         finput.close();
335         input_size = file_size(input);
336     }
337
338     string output = "copy_dest";
339     long output_size = 0;
340     bool res = copy_file(input, output);
341     if (res)
342     {
343         output_size = file_size(output);
344     }
345     unlink(input);
346     unlink(output);
347
348     bool size_is_zero = output_size == 0 ? true : false;
349
350     BOOST_CHECK_EQUAL( true, res );
351     BOOST_CHECK_EQUAL( input_size, output_size );
352     BOOST_CHECK_EQUAL( false, size_is_zero );
353 }
354
355 BOOST_AUTO_TEST_CASE(TestMkdtemp)
356 {
357     // Create unique directory
358     string unique_dir = I2n::mkdtemp("foobar.XXXXXX");
359     BOOST_REQUIRE(unique_dir.size() > 0);
360
361     // Test if it's really a directory
362     BOOST_CHECK_EQUAL(true, Stat(unique_dir).is_directory());
363
364     // Unlink it
365     BOOST_CHECK_EQUAL(true, rmdir(unique_dir));
366 }
367
368 BOOST_AUTO_TEST_CASE(TestRecursiveDelete)
369 {
370     // Create unique directory
371     string unique_dir = I2n::mkdtemp("foobar.XXXXXX");
372     BOOST_REQUIRE(unique_dir.size() > 0);
373
374     // Test if it's really a directory
375     BOOST_CHECK_EQUAL(true, Stat(unique_dir).is_directory());
376
377     // Create dirs and files below it
378     const std::string sub_dir = unique_dir + "/" + "some_subdir";
379     const std::string some_file = sub_dir + "/" + "some_file";
380     BOOST_CHECK_EQUAL(true, I2n::mkdir(sub_dir));
381     BOOST_CHECK_EQUAL(true, write_file(some_file, "foobar"));
382
383     // Unlink it
384     BOOST_CHECK_EQUAL(true, I2n::recursive_delete(unique_dir));
385     BOOST_CHECK_EQUAL(false, Stat(unique_dir).is_directory());
386 }
387
388 BOOST_AUTO_TEST_CASE(TestRecursiveDeleteKeepParentDir)
389 {
390     // Create unique directory
391     string unique_dir = I2n::mkdtemp("foobar.XXXXXX");
392     BOOST_REQUIRE(unique_dir.size() > 0);
393
394     // Test if it's really a directory
395     BOOST_CHECK_EQUAL(true, Stat(unique_dir).is_directory());
396
397     // Create dirs and files below it
398     const std::string sub_dir = unique_dir + "/" + "some_subdir";
399     const std::string some_file = sub_dir + "/" + "some_file";
400     const std::string other_file = unique_dir + "/" + "other_file";
401
402     BOOST_CHECK_EQUAL(true, I2n::mkdir(sub_dir));
403     BOOST_CHECK_EQUAL(true, write_file(some_file, "foobar"));
404     BOOST_CHECK_EQUAL(true, write_file(other_file, "foobar"));
405
406     // Unlink it
407     BOOST_CHECK_EQUAL(true, I2n::recursive_delete(unique_dir, true));
408
409     // check result
410     BOOST_CHECK_EQUAL(true, Stat(unique_dir).is_directory());
411     BOOST_CHECK_EQUAL(false, Stat(sub_dir).is_directory());
412     BOOST_CHECK_EQUAL(false, Stat(other_file).is_regular_file());
413
414     BOOST_CHECK_EQUAL(true, I2n::rmdir(unique_dir));
415 }
416
417 BOOST_AUTO_TEST_CASE(TestMkdtempBrokenTemplate)
418 {
419     // Broken directory template -> fail
420     string unique_dir = I2n::mkdtemp("foobar.XXX");
421     BOOST_CHECK_EQUAL("", unique_dir);
422 }
423
424 BOOST_AUTO_TEST_CASE(DirectoryCreation)
425 {
426     string dirname = "test_directory";
427
428     I2n::rmdir (dirname);
429
430     BOOST_CHECK_EQUAL(true, I2n::mkdir(dirname));
431     BOOST_CHECK_EQUAL(true, Stat(dirname).is_directory());
432     // Verify default permissions
433     BOOST_CHECK_EQUAL(0700, Stat(dirname).mode());
434
435     BOOST_CHECK_EQUAL(true, I2n::rmdir(dirname));
436
437     // Second call  must fail: Directory already deleted
438     BOOST_CHECK_EQUAL(false, I2n::rmdir(dirname));
439 }
440
441 BOOST_AUTO_TEST_CASE(DirectoryCreatePermission)
442 {
443     string dirname = "test_directory";
444
445     // Save and fix umask
446     mode_t previous_mask = I2n::umask(0);
447
448     I2n::rmdir (dirname);
449     BOOST_CHECK_EQUAL(true, I2n::mkdir(dirname, 0770));
450     BOOST_CHECK_EQUAL(0770, Stat(dirname).mode());
451     I2n::rmdir (dirname);
452
453     // Restore umask
454     I2n::umask(previous_mask);
455 }
456
457 BOOST_AUTO_TEST_CASE(ChangeDirectory)
458 {
459     string current_directory = I2n::getcwd();
460     BOOST_REQUIRE(!current_directory.empty());
461
462     BOOST_CHECK_EQUAL(true, I2n::chdir("/"));
463     BOOST_CHECK_EQUAL("/", I2n::getcwd());
464
465     BOOST_CHECK_EQUAL(true, I2n::chdir(current_directory));
466     BOOST_REQUIRE_EQUAL(current_directory, I2n::getcwd());
467 }
468
469 BOOST_AUTO_TEST_CASE(WriteFileEmptyData)
470 {
471     // just to be sure
472     unlink(".foobar");
473
474     I2n::Stat stat(".foobar");
475     BOOST_CHECK_EQUAL( false, (bool)stat );
476
477     // Call write_file() with empty data. Should still create the file.
478     write_file(".foobar","");
479
480     stat.recheck();
481
482     BOOST_CHECK_EQUAL( true, (bool)stat );
483     BOOST_CHECK_EQUAL( true, stat.size() == 0 );
484
485     unlink(".foobar");
486 }
487
488 BOOST_AUTO_TEST_CASE(FileContentDiffersIsIdentical)
489 {
490     BOOST_CHECK_EQUAL( false, file_content_differs("/etc/passwd", "/etc/passwd") );
491 }
492
493 BOOST_AUTO_TEST_CASE(FileContentDiffersNonExistent)
494 {
495     BOOST_CHECK_EQUAL( true, file_content_differs("/etc/passwd", "abc") );
496     BOOST_CHECK_EQUAL( true, file_content_differs("abc", "/etc/passwd") );
497 }
498
499 BOOST_AUTO_TEST_CASE(FileContentDiffersEqualSize)
500 {
501     const string name1 = "file1", name2 = "file2";
502     {
503         ofstream out1(name1.c_str());
504         out1 << "abc";
505
506         ofstream out2(name2.c_str());
507         out2 << "xyz";
508     }
509
510     bool res = file_content_differs(name1, name2);
511     unlink(name1);
512     unlink(name2);
513
514     BOOST_CHECK_EQUAL( true, res );
515 }
516
517 BOOST_AUTO_TEST_CASE(FreeDiskSpace1)
518 {
519     inpipestream cmd_df("df -B 1 --output=avail /tmp | tail -n 1");
520     string dfstr;
521     if (cmd_df)
522         getline(cmd_df, dfstr);
523
524     long long dfout=-1;
525     string_to<long long>(dfstr,dfout);
526
527     BOOST_CHECK_EQUAL( dfout, get_free_diskspace("/tmp") );
528 }
529
530 BOOST_AUTO_TEST_CASE(FreeDiskSpace2)
531 {
532     BOOST_CHECK_EQUAL( -1, get_free_diskspace("/this/path/is/really/bogus") );
533 }
534
535 BOOST_AUTO_TEST_SUITE_END()