333524132e0f6a4ccb3a9a7a2e7071af30a1e0f3
[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(StatSize)
118 {
119     write_file("/tmp/test","some nice content to make sure the file system optimizations don't inline this into the inode block");
120
121     I2n::Stat stat("/tmp/test");
122
123     BOOST_CHECK_EQUAL( true, (bool)stat );
124     BOOST_CHECK_EQUAL( true, stat.is_regular_file() );
125
126     BOOST_CHECK_EQUAL( true, ( stat.bytes_on_disk() >= 512 ) );
127
128     unlink("/tmp/test");
129 } // eo StatSize
130
131 BOOST_AUTO_TEST_CASE(StatRecheck)
132 {
133     // just to be sure
134     unlink(".foobar");
135
136     I2n::Stat stat(".foobar");
137     BOOST_CHECK_EQUAL( false, (bool)stat );
138
139     write_file(".foobar","hello world");
140
141     stat.recheck();
142
143     BOOST_CHECK_EQUAL( true, (bool)stat );
144     BOOST_CHECK_EQUAL( true, stat.size() > 0 );
145
146     unlink(".foobar");
147 }
148
149 BOOST_AUTO_TEST_CASE(DirTest1)
150 {
151     typedef std::vector< std::string > StringVector;
152     StringVector names;
153
154     bool res= I2n::get_dir(".",names);
155
156     BOOST_CHECK_EQUAL( true, res );
157     BOOST_CHECK( ! names.empty() );
158
159     StringVector::iterator it = std::find( names.begin(), names.end(), "Makefile");
160     BOOST_CHECK( it != names.end() );
161
162     it = std::find( names.begin(), names.end(), "." );
163     BOOST_CHECK( it == names.end() );
164
165     names= get_dir(".",true);
166     BOOST_CHECK( ! names.empty() );
167
168     for (it= names.begin(); it!=names.end(); ++it)
169     {
170         DOUT("  \"" << *it << "\"");
171         BOOST_CHECK_EQUAL( false, it->empty() );
172     }
173
174     it = std::find( names.begin(), names.end(), "." );
175     BOOST_CHECK( it != names.end() );
176 } // eo DirTest1
177
178
179
180 BOOST_AUTO_TEST_CASE(PathCuts1)
181 {
182     std::string path1("/an/absolute/path");
183
184     BOOST_CHECK_EQUAL( std::string("/an/absolute"), dirname(path1) );
185     BOOST_CHECK_EQUAL( std::string("path"), basename(path1) );
186
187     std::string path2("just.a.name");
188     BOOST_CHECK_EQUAL( std::string("just.a.name"), basename(path2) );
189     BOOST_CHECK_EQUAL( std::string("."), dirname(path2) );
190 } // eo PathCuts1()
191
192
193
194 BOOST_AUTO_TEST_CASE(NormalizePath1)
195 {
196     std::string path;
197
198     path= normalize_path("/a/simple/path/");
199     BOOST_CHECK_EQUAL( std::string("/a/simple/path"), path );
200
201     path= normalize_path("//another///simple/.//path//");
202     BOOST_CHECK_EQUAL( std::string("/another/simple/path"), path );
203
204     path= normalize_path("//..//..//a/dummy///../././simple/././/path//");
205     BOOST_CHECK_EQUAL( std::string("/a/simple/path"), path );
206
207     path= normalize_path("../a/dummy//././..//simple//nice//absolute//.././..//relative/path//");
208     BOOST_CHECK_EQUAL( std::string("../a/simple/relative/path"), path );
209
210     path= normalize_path("../../a/dummy//././..//simple/../nice//absolute//../.x/..//relative/path//");
211     BOOST_CHECK_EQUAL( std::string("../../a/nice/relative/path"), path );
212
213 } // eo NormalizePath1
214
215 BOOST_AUTO_TEST_CASE(NormalizePath2)
216 {
217     std::string path;
218
219     path= normalize_path("/");
220     BOOST_CHECK_EQUAL( std::string("/"), path );
221
222     path= normalize_path("//");
223     BOOST_CHECK_EQUAL( std::string("/"), path );
224
225     path= normalize_path("/.//");
226     BOOST_CHECK_EQUAL( std::string("/"), path );
227
228     path= normalize_path(".");
229     BOOST_CHECK_EQUAL( std::string(""), path );
230
231     path= normalize_path("./");
232     BOOST_CHECK_EQUAL( std::string(""), path );
233
234     path= normalize_path(".///");
235     BOOST_CHECK_EQUAL( std::string(""), path );
236
237     path= normalize_path("/./data/files");
238     BOOST_CHECK_EQUAL( std::string("/data/files"), path );
239
240     path= normalize_path("./data/files/");
241     BOOST_CHECK_EQUAL( std::string("data/files"), path );
242 } // eo NormalizePath2
243
244 BOOST_AUTO_TEST_CASE(TestUserAndGroupStuff1)
245 {
246     User user_root((uid_t)0);
247     BOOST_CHECK_EQUAL( true, user_root.is_valid() );
248     BOOST_CHECK_EQUAL( true, (bool)user_root );
249
250     BOOST_CHECK_EQUAL( std::string("root"), user_root.Name );
251     BOOST_CHECK_EQUAL( (uid_t)0, user_root.Uid );
252     BOOST_CHECK_EQUAL( (gid_t)0, user_root.Gid );
253
254     User user_root2("root");
255     BOOST_CHECK_EQUAL( true, user_root2.is_valid() );
256     BOOST_CHECK_EQUAL( true, (bool)user_root2 );
257
258     BOOST_CHECK_EQUAL( std::string("root"), user_root2.Name );
259     BOOST_CHECK_EQUAL( (uid_t)0, user_root2.Uid );
260     BOOST_CHECK_EQUAL( (gid_t)0, user_root2.Gid );
261
262     Group group_root("root");
263     BOOST_CHECK_EQUAL( true, group_root.is_valid() );
264     BOOST_CHECK_EQUAL( true, (bool)group_root );
265
266     BOOST_CHECK_EQUAL( std::string("root"), group_root.Name );
267     BOOST_CHECK_EQUAL( (gid_t)0, group_root.Gid );
268
269 } // TestUserAndGroupStuff1()
270
271 BOOST_AUTO_TEST_CASE(TestUserAndGroupStuff2)
272 {
273     // Test if is_valid() works correctly
274     User user_root;
275     BOOST_CHECK_EQUAL( false, user_root.is_valid() );
276
277     Group group_root;
278     BOOST_CHECK_EQUAL( false, group_root.is_valid() );
279 }
280
281 BOOST_AUTO_TEST_CASE(TestFileModes1)
282 {
283     std::string path= get_check_file_path("FileModes1");
284
285     write_file(path,"42");
286
287     Stat stat(path, false);
288     BOOST_CHECK_EQUAL( true, stat.is_valid() );
289
290     User user( stat.uid() );
291     Group group( stat.gid() );
292
293     BOOST_CHECK_EQUAL( true, user.is_valid() );
294     BOOST_CHECK_EQUAL( true, group.is_valid() );
295
296     bool res=chown( path, user.Name.c_str(), group.Gid );
297
298     BOOST_CHECK_EQUAL( true, res );
299 } // eo TestFileModes1()
300
301
302
303 BOOST_AUTO_TEST_CASE(TestPidOf1)
304 {
305     using I2n::Daemon::pid_of;
306
307     std::vector< pid_t > pid_list;
308
309     bool res= pid_of("init", pid_list);
310     BOOST_CHECK_EQUAL( true, res);
311     
312     if (pid_list.empty())
313     {
314         res= pid_of("systemd", pid_list);
315         BOOST_CHECK_EQUAL( true, res);
316     }
317     
318     BOOST_CHECK_EQUAL( false, pid_list.empty() );
319
320     std::vector< pid_t >::const_iterator pos1 =
321         std::find( pid_list.begin(), pid_list.end(), 1);
322
323     BOOST_CHECK( pos1 != pid_list.end() );
324 } // eo TestPidOf1()
325
326 BOOST_AUTO_TEST_CASE(TestCopyFileSourceFail)
327 {
328     bool res = copy_file("does not exist", "destination");
329     BOOST_CHECK_EQUAL( false, res );
330 }
331
332 BOOST_AUTO_TEST_CASE(TestCopyFileDestFail)
333 {
334     bool res = copy_file("/etc/HOSTNAME", "/proc/not/writable");
335     BOOST_CHECK_EQUAL( false, res );
336 }
337
338 BOOST_AUTO_TEST_CASE(TestCopyFileOk)
339 {
340     string input = "copy_source";
341     string data = "test";
342
343     long input_size = 0;
344     ofstream finput(input.c_str());
345     if (finput)
346     {
347         finput << data;
348         finput.close();
349         input_size = file_size(input);
350     }
351
352     string output = "copy_dest";
353     long output_size = 0;
354     bool res = copy_file(input, output);
355     if (res)
356     {
357         output_size = file_size(output);
358     }
359     unlink(input);
360     unlink(output);
361
362     bool size_is_zero = output_size == 0 ? true : false;
363
364     BOOST_CHECK_EQUAL( true, res );
365     BOOST_CHECK_EQUAL( input_size, output_size );
366     BOOST_CHECK_EQUAL( false, size_is_zero );
367 }
368
369 BOOST_AUTO_TEST_CASE(TestMkdtemp)
370 {
371     // Create unique directory
372     string unique_dir = I2n::mkdtemp("foobar.XXXXXX");
373     BOOST_REQUIRE(unique_dir.size() > 0);
374
375     // Test if it's really a directory
376     BOOST_CHECK_EQUAL(true, Stat(unique_dir).is_directory());
377
378     // Unlink it
379     BOOST_CHECK_EQUAL(true, rmdir(unique_dir));
380 }
381
382 BOOST_AUTO_TEST_CASE(TestRecursiveDelete)
383 {
384     // Create unique directory
385     string unique_dir = I2n::mkdtemp("foobar.XXXXXX");
386     BOOST_REQUIRE(unique_dir.size() > 0);
387
388     // Test if it's really a directory
389     BOOST_CHECK_EQUAL(true, Stat(unique_dir).is_directory());
390
391     // Create dirs and files below it
392     const std::string sub_dir = unique_dir + "/" + "some_subdir";
393     const std::string some_file = sub_dir + "/" + "some_file";
394     BOOST_CHECK_EQUAL(true, I2n::mkdir(sub_dir));
395     BOOST_CHECK_EQUAL(true, write_file(some_file, "foobar"));
396
397     // Unlink it
398     BOOST_CHECK_EQUAL(true, I2n::recursive_delete(unique_dir));
399     BOOST_CHECK_EQUAL(false, Stat(unique_dir).is_directory());
400 }
401
402 BOOST_AUTO_TEST_CASE(TestRecursiveDeleteKeepParentDir)
403 {
404     // Create unique directory
405     string unique_dir = I2n::mkdtemp("foobar.XXXXXX");
406     BOOST_REQUIRE(unique_dir.size() > 0);
407
408     // Test if it's really a directory
409     BOOST_CHECK_EQUAL(true, Stat(unique_dir).is_directory());
410
411     // Create dirs and files below it
412     const std::string sub_dir = unique_dir + "/" + "some_subdir";
413     const std::string some_file = sub_dir + "/" + "some_file";
414     const std::string other_file = unique_dir + "/" + "other_file";
415
416     BOOST_CHECK_EQUAL(true, I2n::mkdir(sub_dir));
417     BOOST_CHECK_EQUAL(true, write_file(some_file, "foobar"));
418     BOOST_CHECK_EQUAL(true, write_file(other_file, "foobar"));
419
420     // Unlink it
421     BOOST_CHECK_EQUAL(true, I2n::recursive_delete(unique_dir, true));
422
423     // check result
424     BOOST_CHECK_EQUAL(true, Stat(unique_dir).is_directory());
425     BOOST_CHECK_EQUAL(false, Stat(sub_dir).is_directory());
426     BOOST_CHECK_EQUAL(false, Stat(other_file).is_regular_file());
427
428     BOOST_CHECK_EQUAL(true, I2n::rmdir(unique_dir));
429 }
430
431 BOOST_AUTO_TEST_CASE(TestMkdtempBrokenTemplate)
432 {
433     // Broken directory template -> fail
434     string unique_dir = I2n::mkdtemp("foobar.XXX");
435     BOOST_CHECK_EQUAL("", unique_dir);
436 }
437
438 BOOST_AUTO_TEST_CASE(DirectoryCreation)
439 {
440     string dirname = "test_directory";
441
442     I2n::rmdir (dirname);
443
444     BOOST_CHECK_EQUAL(true, I2n::mkdir(dirname));
445     BOOST_CHECK_EQUAL(true, Stat(dirname).is_directory());
446     // Verify default permissions
447     BOOST_CHECK_EQUAL(0700, Stat(dirname).mode());
448
449     BOOST_CHECK_EQUAL(true, I2n::rmdir(dirname));
450
451     // Second call  must fail: Directory already deleted
452     BOOST_CHECK_EQUAL(false, I2n::rmdir(dirname));
453 }
454
455 BOOST_AUTO_TEST_CASE(DirectoryCreatePermission)
456 {
457     string dirname = "test_directory";
458
459     // Save and fix umask
460     mode_t previous_mask = I2n::umask(0);
461
462     I2n::rmdir (dirname);
463     BOOST_CHECK_EQUAL(true, I2n::mkdir(dirname, 0770));
464     BOOST_CHECK_EQUAL(0770, Stat(dirname).mode());
465     I2n::rmdir (dirname);
466
467     // Restore umask
468     I2n::umask(previous_mask);
469 }
470
471 BOOST_AUTO_TEST_CASE(ChangeDirectory)
472 {
473     string current_directory = I2n::getcwd();
474     BOOST_REQUIRE(!current_directory.empty());
475
476     BOOST_CHECK_EQUAL(true, I2n::chdir("/"));
477     BOOST_CHECK_EQUAL("/", I2n::getcwd());
478
479     BOOST_CHECK_EQUAL(true, I2n::chdir(current_directory));
480     BOOST_REQUIRE_EQUAL(current_directory, I2n::getcwd());
481 }
482
483 BOOST_AUTO_TEST_CASE(WriteFileEmptyData)
484 {
485     // just to be sure
486     unlink(".foobar");
487
488     I2n::Stat stat(".foobar");
489     BOOST_CHECK_EQUAL( false, (bool)stat );
490
491     // Call write_file() with empty data. Should still create the file.
492     write_file(".foobar","");
493
494     stat.recheck();
495
496     BOOST_CHECK_EQUAL( true, (bool)stat );
497     BOOST_CHECK_EQUAL( true, stat.size() == 0 );
498
499     unlink(".foobar");
500 }
501
502 BOOST_AUTO_TEST_CASE(FileContentDiffersIsIdentical)
503 {
504     BOOST_CHECK_EQUAL( false, file_content_differs("/etc/passwd", "/etc/passwd") );
505 }
506
507 BOOST_AUTO_TEST_CASE(FileContentDiffersNonExistent)
508 {
509     BOOST_CHECK_EQUAL( true, file_content_differs("/etc/passwd", "abc") );
510     BOOST_CHECK_EQUAL( true, file_content_differs("abc", "/etc/passwd") );
511 }
512
513 BOOST_AUTO_TEST_CASE(FileContentDiffersEqualSize)
514 {
515     const string name1 = "file1", name2 = "file2";
516     {
517         ofstream out1(name1.c_str());
518         out1 << "abc";
519
520         ofstream out2(name2.c_str());
521         out2 << "xyz";
522     }
523
524     bool res = file_content_differs(name1, name2);
525     unlink(name1);
526     unlink(name2);
527
528     BOOST_CHECK_EQUAL( true, res );
529 }
530
531 BOOST_AUTO_TEST_CASE(FreeDiskSpace1)
532 {
533     inpipestream cmd_df("df -B 1 --output=avail /tmp | tail -n 1");
534     string dfstr;
535     if (cmd_df)
536         getline(cmd_df, dfstr);
537
538     long long dfout=-1;
539     string_to<long long>(dfstr,dfout);
540
541     BOOST_CHECK_EQUAL( dfout, get_free_diskspace("/tmp") );
542 }
543
544 BOOST_AUTO_TEST_CASE(FreeDiskSpace2)
545 {
546     BOOST_CHECK_EQUAL( -1, get_free_diskspace("/this/path/is/really/bogus") );
547 }
548
549 BOOST_AUTO_TEST_SUITE_END()