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