Rename get_dir_size() to get_dir_count()
[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(__FILE__);
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     string test_dir = dirname(__FILE__);
155     bool res= I2n::get_dir(test_dir,names);
156
157     BOOST_CHECK_EQUAL( true, res );
158     BOOST_CHECK( ! names.empty() );
159     BOOST_CHECK_EQUAL(I2n::get_dir_count(test_dir), names.size());
160
161     BOOST_MESSAGE("Looking for " << basename(__FILE__) << " in " << test_dir);
162     StringVector::iterator it = std::find( names.begin(), names.end(), basename(__FILE__));
163     BOOST_CHECK( it != names.end() );
164
165     it = std::find( names.begin(), names.end(), "." );
166     BOOST_CHECK( it == names.end() );
167
168     names= get_dir(test_dir,true);
169     BOOST_CHECK( ! names.empty() );
170     BOOST_CHECK_EQUAL(I2n::get_dir_count(test_dir, true), names.size());
171
172     for (it= names.begin(); it!=names.end(); ++it)
173     {
174         DOUT("  \"" << *it << "\"");
175         BOOST_CHECK_EQUAL( false, it->empty() );
176     }
177
178     it = std::find( names.begin(), names.end(), "." );
179     BOOST_CHECK( it != names.end() );
180 } // eo DirTest1
181
182
183
184 BOOST_AUTO_TEST_CASE(PathCuts1)
185 {
186     std::string path1("/an/absolute/path");
187
188     BOOST_CHECK_EQUAL( std::string("/an/absolute"), dirname(path1) );
189     BOOST_CHECK_EQUAL( std::string("path"), basename(path1) );
190
191     std::string path2("just.a.name");
192     BOOST_CHECK_EQUAL( std::string("just.a.name"), basename(path2) );
193     BOOST_CHECK_EQUAL( std::string("."), dirname(path2) );
194 } // eo PathCuts1()
195
196
197
198 BOOST_AUTO_TEST_CASE(NormalizePath1)
199 {
200     std::string path;
201
202     path= normalize_path("/a/simple/path/");
203     BOOST_CHECK_EQUAL( std::string("/a/simple/path"), path );
204
205     path= normalize_path("//another///simple/.//path//");
206     BOOST_CHECK_EQUAL( std::string("/another/simple/path"), path );
207
208     path= normalize_path("//..//..//a/dummy///../././simple/././/path//");
209     BOOST_CHECK_EQUAL( std::string("/a/simple/path"), path );
210
211     path= normalize_path("../a/dummy//././..//simple//nice//absolute//.././..//relative/path//");
212     BOOST_CHECK_EQUAL( std::string("../a/simple/relative/path"), path );
213
214     path= normalize_path("../../a/dummy//././..//simple/../nice//absolute//../.x/..//relative/path//");
215     BOOST_CHECK_EQUAL( std::string("../../a/nice/relative/path"), path );
216
217 } // eo NormalizePath1
218
219 BOOST_AUTO_TEST_CASE(NormalizePath2)
220 {
221     std::string path;
222
223     path= normalize_path("/");
224     BOOST_CHECK_EQUAL( std::string("/"), path );
225
226     path= normalize_path("//");
227     BOOST_CHECK_EQUAL( std::string("/"), path );
228
229     path= normalize_path("/.//");
230     BOOST_CHECK_EQUAL( std::string("/"), path );
231
232     path= normalize_path(".");
233     BOOST_CHECK_EQUAL( std::string(""), path );
234
235     path= normalize_path("./");
236     BOOST_CHECK_EQUAL( std::string(""), path );
237
238     path= normalize_path(".///");
239     BOOST_CHECK_EQUAL( std::string(""), path );
240
241     path= normalize_path("/./data/files");
242     BOOST_CHECK_EQUAL( std::string("/data/files"), path );
243
244     path= normalize_path("./data/files/");
245     BOOST_CHECK_EQUAL( std::string("data/files"), path );
246 } // eo NormalizePath2
247
248 BOOST_AUTO_TEST_CASE(TestUserAndGroupStuff1)
249 {
250     User user_root((uid_t)0);
251     BOOST_CHECK_EQUAL( true, user_root.is_valid() );
252     BOOST_CHECK_EQUAL( true, (bool)user_root );
253
254     BOOST_CHECK_EQUAL( std::string("root"), user_root.Name );
255     BOOST_CHECK_EQUAL( (uid_t)0, user_root.Uid );
256     BOOST_CHECK_EQUAL( (gid_t)0, user_root.Gid );
257
258     User user_root2("root");
259     BOOST_CHECK_EQUAL( true, user_root2.is_valid() );
260     BOOST_CHECK_EQUAL( true, (bool)user_root2 );
261
262     BOOST_CHECK_EQUAL( std::string("root"), user_root2.Name );
263     BOOST_CHECK_EQUAL( (uid_t)0, user_root2.Uid );
264     BOOST_CHECK_EQUAL( (gid_t)0, user_root2.Gid );
265
266     Group group_root("root");
267     BOOST_CHECK_EQUAL( true, group_root.is_valid() );
268     BOOST_CHECK_EQUAL( true, (bool)group_root );
269
270     BOOST_CHECK_EQUAL( std::string("root"), group_root.Name );
271     BOOST_CHECK_EQUAL( (gid_t)0, group_root.Gid );
272
273 } // TestUserAndGroupStuff1()
274
275 BOOST_AUTO_TEST_CASE(TestUserAndGroupStuff2)
276 {
277     // Test if is_valid() works correctly
278     User user_root;
279     BOOST_CHECK_EQUAL( false, user_root.is_valid() );
280
281     Group group_root;
282     BOOST_CHECK_EQUAL( false, group_root.is_valid() );
283 }
284
285 BOOST_AUTO_TEST_CASE(TestFileModes1)
286 {
287     std::string path= get_check_file_path("FileModes1");
288
289     write_file(path,"42");
290
291     Stat stat(path, false);
292     BOOST_CHECK_EQUAL( true, stat.is_valid() );
293
294     User user( stat.uid() );
295     Group group( stat.gid() );
296
297     BOOST_CHECK_EQUAL( true, user.is_valid() );
298     BOOST_CHECK_EQUAL( true, group.is_valid() );
299
300     bool res=chown( path, user.Name.c_str(), group.Gid );
301
302     BOOST_CHECK_EQUAL( true, res );
303 } // eo TestFileModes1()
304
305
306
307 BOOST_AUTO_TEST_CASE(TestPidOf1)
308 {
309     using I2n::Daemon::pid_of;
310
311     std::vector< pid_t > pid_list;
312
313     bool res= pid_of("init", pid_list);
314     BOOST_CHECK_EQUAL( true, res);
315     
316     if (pid_list.empty())
317     {
318         res= pid_of("systemd", pid_list);
319         BOOST_CHECK_EQUAL( true, res);
320     }
321     
322     BOOST_CHECK_EQUAL( false, pid_list.empty() );
323
324     std::vector< pid_t >::const_iterator pos1 =
325         std::find( pid_list.begin(), pid_list.end(), 1);
326
327     BOOST_CHECK( pos1 != pid_list.end() );
328 } // eo TestPidOf1()
329
330 BOOST_AUTO_TEST_CASE(TestCopyFileSourceFail)
331 {
332     bool res = copy_file("does not exist", "destination");
333     BOOST_CHECK_EQUAL( false, res );
334 }
335
336 BOOST_AUTO_TEST_CASE(TestCopyFileDestFail)
337 {
338     bool res = copy_file("/etc/HOSTNAME", "/proc/not/writable");
339     BOOST_CHECK_EQUAL( false, res );
340 }
341
342 BOOST_AUTO_TEST_CASE(TestCopyFileOk)
343 {
344     string input = "copy_source";
345     string data = "test";
346
347     long input_size = 0;
348     ofstream finput(input.c_str());
349     if (finput)
350     {
351         finput << data;
352         finput.close();
353         input_size = file_size(input);
354     }
355
356     string output = "copy_dest";
357     long output_size = 0;
358     bool res = copy_file(input, output);
359     if (res)
360     {
361         output_size = file_size(output);
362     }
363     unlink(input);
364     unlink(output);
365
366     bool size_is_zero = output_size == 0 ? true : false;
367
368     BOOST_CHECK_EQUAL( true, res );
369     BOOST_CHECK_EQUAL( input_size, output_size );
370     BOOST_CHECK_EQUAL( false, size_is_zero );
371 }
372
373 BOOST_AUTO_TEST_CASE(TestMkdtemp)
374 {
375     // Create unique directory
376     string unique_dir = I2n::mkdtemp("foobar.XXXXXX");
377     BOOST_REQUIRE(unique_dir.size() > 0);
378
379     // Test if it's really a directory
380     BOOST_CHECK_EQUAL(true, Stat(unique_dir).is_directory());
381
382     // Unlink it
383     BOOST_CHECK_EQUAL(true, rmdir(unique_dir));
384 }
385
386 BOOST_AUTO_TEST_CASE(TestRecursiveDelete)
387 {
388     // Create unique directory
389     string unique_dir = I2n::mkdtemp("foobar.XXXXXX");
390     BOOST_REQUIRE(unique_dir.size() > 0);
391
392     // Test if it's really a directory
393     BOOST_CHECK_EQUAL(true, Stat(unique_dir).is_directory());
394
395     // Create dirs and files below it
396     const std::string sub_dir = unique_dir + "/" + "some_subdir";
397     const std::string some_file = sub_dir + "/" + "some_file";
398     BOOST_CHECK_EQUAL(true, I2n::mkdir(sub_dir));
399     BOOST_CHECK_EQUAL(true, write_file(some_file, "foobar"));
400
401     // Unlink it
402     BOOST_CHECK_EQUAL(true, I2n::recursive_delete(unique_dir));
403     BOOST_CHECK_EQUAL(false, Stat(unique_dir).is_directory());
404 }
405
406 BOOST_AUTO_TEST_CASE(TestRecursiveDeleteKeepParentDir)
407 {
408     // Create unique directory
409     string unique_dir = I2n::mkdtemp("foobar.XXXXXX");
410     BOOST_REQUIRE(unique_dir.size() > 0);
411
412     // Test if it's really a directory
413     BOOST_CHECK_EQUAL(true, Stat(unique_dir).is_directory());
414
415     // Create dirs and files below it
416     const std::string sub_dir = unique_dir + "/" + "some_subdir";
417     const std::string some_file = sub_dir + "/" + "some_file";
418     const std::string other_file = unique_dir + "/" + "other_file";
419
420     BOOST_CHECK_EQUAL(true, I2n::mkdir(sub_dir));
421     BOOST_CHECK_EQUAL(true, write_file(some_file, "foobar"));
422     BOOST_CHECK_EQUAL(true, write_file(other_file, "foobar"));
423
424     // Unlink it
425     BOOST_CHECK_EQUAL(true, I2n::recursive_delete(unique_dir, true));
426
427     // check result
428     BOOST_CHECK_EQUAL(true, Stat(unique_dir).is_directory());
429     BOOST_CHECK_EQUAL(false, Stat(sub_dir).is_directory());
430     BOOST_CHECK_EQUAL(false, Stat(other_file).is_regular_file());
431
432     BOOST_CHECK_EQUAL(true, I2n::rmdir(unique_dir));
433 }
434
435 BOOST_AUTO_TEST_CASE(TestRecursiveDeleteSymlinkedDirs1)
436 {
437     // Create unique directory
438     string unique_dir = I2n::mkdtemp("foobar.XXXXXX");
439     BOOST_REQUIRE(unique_dir.size() > 0);
440
441     // Test if it's really a directory
442     BOOST_CHECK_EQUAL(true, Stat(unique_dir).is_directory());
443
444     // Create dirs and files below it
445     const std::string sub_dir = unique_dir + "/" + "some_subdir";
446     const std::string some_file = sub_dir + "/" + "some_file";
447     const std::string dir_symlink_within_unique_dir = unique_dir + "/" + "dir_symlink_within_unique_dir";
448     BOOST_CHECK_EQUAL(true, I2n::mkdir(sub_dir));
449     BOOST_CHECK_EQUAL(true, write_file(some_file, "foobar"));
450     BOOST_CHECK_EQUAL(true, symlink(sub_dir, dir_symlink_within_unique_dir));
451
452     // Unlink it
453     BOOST_CHECK_EQUAL(true, I2n::recursive_delete(unique_dir));
454     BOOST_CHECK_EQUAL(false, Stat(unique_dir).is_directory());
455 }
456
457 BOOST_AUTO_TEST_CASE(TestRecursiveDeleteSymlinkedDirs2)
458 {
459     // Create unique directory
460     string unique_dir = I2n::mkdtemp("foobar.XXXXXX");
461     BOOST_REQUIRE(unique_dir.size() > 0);
462
463     // Test if it's really a directory
464     BOOST_CHECK_EQUAL(true, Stat(unique_dir).is_directory());
465
466     // Create another unique directory
467     string other_unique_dir = I2n::mkdtemp("foobar_other.XXXXXX");
468     BOOST_REQUIRE(other_unique_dir.size() > 0);
469
470     // Test if it's really a directory
471     BOOST_CHECK_EQUAL(true, Stat(other_unique_dir).is_directory());
472
473     const std::string other_file = other_unique_dir + "/" + "other_file";
474     BOOST_CHECK_EQUAL(true, write_file(other_file, "foobar"));
475
476     // Create dirs and files below it
477     const std::string sub_dir = unique_dir + "/" + "some_subdir";
478     const std::string some_file = sub_dir + "/" + "some_file";
479     const std::string dir_symlink_to_outside_unique_dir = unique_dir + "/" + "dir_symlink_to_outside_unique_dir";
480     BOOST_CHECK_EQUAL(true, I2n::mkdir(sub_dir));
481     BOOST_CHECK_EQUAL(true, write_file(some_file, "foobar"));
482     BOOST_CHECK_EQUAL(true, symlink(other_unique_dir, dir_symlink_to_outside_unique_dir));
483
484     // Unlink it
485     BOOST_CHECK_EQUAL(true, I2n::recursive_delete(unique_dir));
486     BOOST_CHECK_EQUAL(false, Stat(unique_dir).is_directory());
487
488     // other unique dir must still be there
489     BOOST_CHECK_EQUAL(true, Stat(other_unique_dir).is_directory());
490     BOOST_CHECK_EQUAL(true, Stat(other_file).is_regular_file());
491
492     // finally delete the other unique dir
493     BOOST_CHECK_EQUAL(true, I2n::recursive_delete(other_unique_dir));
494     BOOST_CHECK_EQUAL(false, Stat(other_unique_dir).is_directory());
495 }
496
497 BOOST_AUTO_TEST_CASE(TestMkdtempBrokenTemplate)
498 {
499     // Broken directory template -> fail
500     string unique_dir = I2n::mkdtemp("foobar.XXX");
501     BOOST_CHECK_EQUAL("", unique_dir);
502 }
503
504 BOOST_AUTO_TEST_CASE(DirectoryCreation)
505 {
506     string dirname = "test_directory";
507
508     I2n::rmdir (dirname);
509
510     BOOST_CHECK_EQUAL(true, I2n::mkdir(dirname));
511     BOOST_CHECK_EQUAL(true, Stat(dirname).is_directory());
512     // Verify default permissions
513     BOOST_CHECK_EQUAL(0700, Stat(dirname).mode());
514
515     BOOST_CHECK_EQUAL(true, I2n::rmdir(dirname));
516
517     // Second call  must fail: Directory already deleted
518     BOOST_CHECK_EQUAL(false, I2n::rmdir(dirname));
519 }
520
521 BOOST_AUTO_TEST_CASE(DirectoryCreatePermission)
522 {
523     string dirname = "test_directory";
524
525     // Save and fix umask
526     mode_t previous_mask = I2n::umask(0);
527
528     I2n::rmdir (dirname);
529     BOOST_CHECK_EQUAL(true, I2n::mkdir(dirname, 0770));
530     BOOST_CHECK_EQUAL(0770, Stat(dirname).mode());
531     I2n::rmdir (dirname);
532
533     // Restore umask
534     I2n::umask(previous_mask);
535 }
536
537 BOOST_AUTO_TEST_CASE(ChangeDirectory)
538 {
539     string current_directory = I2n::getcwd();
540     BOOST_REQUIRE(!current_directory.empty());
541
542     BOOST_CHECK_EQUAL(true, I2n::chdir("/"));
543     BOOST_CHECK_EQUAL("/", I2n::getcwd());
544
545     BOOST_CHECK_EQUAL(true, I2n::chdir(current_directory));
546     BOOST_REQUIRE_EQUAL(current_directory, I2n::getcwd());
547 }
548
549 BOOST_AUTO_TEST_CASE(WriteFileEmptyData)
550 {
551     // just to be sure
552     unlink(".foobar");
553
554     I2n::Stat stat(".foobar");
555     BOOST_CHECK_EQUAL( false, (bool)stat );
556
557     // Call write_file() with empty data. Should still create the file.
558     write_file(".foobar","");
559
560     stat.recheck();
561
562     BOOST_CHECK_EQUAL( true, (bool)stat );
563     BOOST_CHECK_EQUAL( true, stat.size() == 0 );
564
565     unlink(".foobar");
566 }
567
568 BOOST_AUTO_TEST_CASE(FileContentDiffersIsIdentical)
569 {
570     BOOST_CHECK_EQUAL( false, file_content_differs("/etc/passwd", "/etc/passwd") );
571 }
572
573 BOOST_AUTO_TEST_CASE(FileContentDiffersNonExistent)
574 {
575     BOOST_CHECK_EQUAL( true, file_content_differs("/etc/passwd", "abc") );
576     BOOST_CHECK_EQUAL( true, file_content_differs("abc", "/etc/passwd") );
577 }
578
579 BOOST_AUTO_TEST_CASE(FileContentDiffersEqualSize)
580 {
581     const string name1 = "file1", name2 = "file2";
582     {
583         ofstream out1(name1.c_str());
584         out1 << "abc";
585
586         ofstream out2(name2.c_str());
587         out2 << "xyz";
588     }
589
590     bool res = file_content_differs(name1, name2);
591     unlink(name1);
592     unlink(name2);
593
594     BOOST_CHECK_EQUAL( true, res );
595 }
596
597 BOOST_AUTO_TEST_CASE(FreeDiskSpace1)
598 {
599     long long dfout;
600     long long get_free_diskspace_tmp;
601
602     // we need a partition to do this test. we can't create our own because that would require root
603     // so we use /tmp and retry it a few times in case of an error
604     int retries = 5;
605     while (retries > 0)
606     {
607         string dfstr=capture_exec("df -P -B 1 /tmp | tail -n 1 | sed \"s/ \\+/\\\\n/g\" | sed -n 4,4p");
608
609         dfout=-1;
610         string_to<long long>(dfstr,dfout);
611
612         get_free_diskspace_tmp=get_free_diskspace("/tmp");
613         
614         if ( dfout == get_free_diskspace_tmp )
615             break;
616         
617         retries--;
618     }
619
620     // do the check again with BOOST_CHECK_EQUAL to show it in the unit test output
621     BOOST_CHECK_EQUAL( dfout, get_free_diskspace_tmp );
622 }
623
624 BOOST_AUTO_TEST_CASE(FreeDiskSpace2)
625 {
626     BOOST_CHECK_EQUAL( -1, get_free_diskspace("/this/path/is/really/bogus") );
627 }
628
629 BOOST_AUTO_TEST_CASE(TestDu)
630 {
631     // Create unique directory
632     string unique_dir = I2n::mkdtemp("foobar.XXXXXX");
633     BOOST_REQUIRE(unique_dir.size() > 0);
634
635     // Test if it's really a directory
636     BOOST_CHECK_EQUAL(true, Stat(unique_dir).is_directory());
637
638     // Create dirs and files below it
639     const std::string sub_dir = unique_dir + "/" + "some_subdir";
640     const std::string some_file = sub_dir + "/" + "some_file";
641     BOOST_CHECK_EQUAL(true, I2n::mkdir(sub_dir));
642     BOOST_CHECK_EQUAL(true, write_file(some_file, "foobar and some more data, even more that Tom would like to see in one line without linebreak but I still want to have it all here"));
643
644     string dustr=capture_exec(string("du --block-size=1 --sum ")+unique_dir+" | cut -f 1");
645     long long duout=-1;
646     string_to<long long>(dustr,duout);
647
648     long long first_du=du(unique_dir);
649
650     BOOST_CHECK_EQUAL( duout, first_du );
651
652     // create hardlinks
653     string cmd;
654     cmd=string("ln ")+some_file+" hardlink1";
655     system(cmd.c_str());
656
657     cmd=string("ln ")+some_file+" hardlink2";
658     system(cmd.c_str());
659
660     cmd=string("ln ")+some_file+" hardlink3";
661     system(cmd.c_str());
662
663     long long du_with_hardlinks=du(unique_dir);
664
665     BOOST_CHECK_EQUAL( first_du , du_with_hardlinks );
666
667     // Unlink it
668     BOOST_CHECK_EQUAL(true, I2n::recursive_delete(unique_dir));
669     BOOST_CHECK_EQUAL(false, Stat(unique_dir).is_directory());
670
671     // remove links
672     unlink("hardlink1");
673     unlink("hardlink2");
674     unlink("hardlink3");
675 }
676
677
678
679 BOOST_AUTO_TEST_SUITE_END()