1fb51f8f126403bfa3b00a41648e485349c5fda8
[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(TestRecursiveDeleteSymlinkedDirs1)
432 {
433     // Create unique directory
434     string unique_dir = I2n::mkdtemp("foobar.XXXXXX");
435     BOOST_REQUIRE(unique_dir.size() > 0);
436
437     // Test if it's really a directory
438     BOOST_CHECK_EQUAL(true, Stat(unique_dir).is_directory());
439
440     // Create dirs and files below it
441     const std::string sub_dir = unique_dir + "/" + "some_subdir";
442     const std::string some_file = sub_dir + "/" + "some_file";
443     const std::string dir_symlink_within_unique_dir = unique_dir + "/" + "dir_symlink_within_unique_dir";
444     BOOST_CHECK_EQUAL(true, I2n::mkdir(sub_dir));
445     BOOST_CHECK_EQUAL(true, write_file(some_file, "foobar"));
446     BOOST_CHECK_EQUAL(true, symlink(sub_dir, dir_symlink_within_unique_dir));
447
448     // Unlink it
449     BOOST_CHECK_EQUAL(true, I2n::recursive_delete(unique_dir));
450     BOOST_CHECK_EQUAL(false, Stat(unique_dir).is_directory());
451 }
452
453 BOOST_AUTO_TEST_CASE(TestRecursiveDeleteSymlinkedDirs2)
454 {
455     // Create unique directory
456     string unique_dir = I2n::mkdtemp("foobar.XXXXXX");
457     BOOST_REQUIRE(unique_dir.size() > 0);
458
459     // Test if it's really a directory
460     BOOST_CHECK_EQUAL(true, Stat(unique_dir).is_directory());
461
462     // Create another unique directory
463     string other_unique_dir = I2n::mkdtemp("foobar_other.XXXXXX");
464     BOOST_REQUIRE(other_unique_dir.size() > 0);
465
466     // Test if it's really a directory
467     BOOST_CHECK_EQUAL(true, Stat(other_unique_dir).is_directory());
468
469     const std::string other_file = other_unique_dir + "/" + "other_file";
470     BOOST_CHECK_EQUAL(true, write_file(other_file, "foobar"));
471
472     // Create dirs and files below it
473     const std::string sub_dir = unique_dir + "/" + "some_subdir";
474     const std::string some_file = sub_dir + "/" + "some_file";
475     const std::string dir_symlink_to_outside_unique_dir = unique_dir + "/" + "dir_symlink_to_outside_unique_dir";
476     BOOST_CHECK_EQUAL(true, I2n::mkdir(sub_dir));
477     BOOST_CHECK_EQUAL(true, write_file(some_file, "foobar"));
478     BOOST_CHECK_EQUAL(true, symlink(other_unique_dir, dir_symlink_to_outside_unique_dir));
479
480     // Unlink it
481     BOOST_CHECK_EQUAL(true, I2n::recursive_delete(unique_dir));
482     BOOST_CHECK_EQUAL(false, Stat(unique_dir).is_directory());
483
484     // other unique dir must still be there
485     BOOST_CHECK_EQUAL(true, Stat(other_unique_dir).is_directory());
486     BOOST_CHECK_EQUAL(true, Stat(other_file).is_regular_file());
487
488     // finally delete the other unique dir
489     BOOST_CHECK_EQUAL(true, I2n::recursive_delete(other_unique_dir));
490     BOOST_CHECK_EQUAL(false, Stat(other_unique_dir).is_directory());
491 }
492
493 BOOST_AUTO_TEST_CASE(TestMkdtempBrokenTemplate)
494 {
495     // Broken directory template -> fail
496     string unique_dir = I2n::mkdtemp("foobar.XXX");
497     BOOST_CHECK_EQUAL("", unique_dir);
498 }
499
500 BOOST_AUTO_TEST_CASE(DirectoryCreation)
501 {
502     string dirname = "test_directory";
503
504     I2n::rmdir (dirname);
505
506     BOOST_CHECK_EQUAL(true, I2n::mkdir(dirname));
507     BOOST_CHECK_EQUAL(true, Stat(dirname).is_directory());
508     // Verify default permissions
509     BOOST_CHECK_EQUAL(0700, Stat(dirname).mode());
510
511     BOOST_CHECK_EQUAL(true, I2n::rmdir(dirname));
512
513     // Second call  must fail: Directory already deleted
514     BOOST_CHECK_EQUAL(false, I2n::rmdir(dirname));
515 }
516
517 BOOST_AUTO_TEST_CASE(DirectoryCreatePermission)
518 {
519     string dirname = "test_directory";
520
521     // Save and fix umask
522     mode_t previous_mask = I2n::umask(0);
523
524     I2n::rmdir (dirname);
525     BOOST_CHECK_EQUAL(true, I2n::mkdir(dirname, 0770));
526     BOOST_CHECK_EQUAL(0770, Stat(dirname).mode());
527     I2n::rmdir (dirname);
528
529     // Restore umask
530     I2n::umask(previous_mask);
531 }
532
533 BOOST_AUTO_TEST_CASE(ChangeDirectory)
534 {
535     string current_directory = I2n::getcwd();
536     BOOST_REQUIRE(!current_directory.empty());
537
538     BOOST_CHECK_EQUAL(true, I2n::chdir("/"));
539     BOOST_CHECK_EQUAL("/", I2n::getcwd());
540
541     BOOST_CHECK_EQUAL(true, I2n::chdir(current_directory));
542     BOOST_REQUIRE_EQUAL(current_directory, I2n::getcwd());
543 }
544
545 BOOST_AUTO_TEST_CASE(WriteFileEmptyData)
546 {
547     // just to be sure
548     unlink(".foobar");
549
550     I2n::Stat stat(".foobar");
551     BOOST_CHECK_EQUAL( false, (bool)stat );
552
553     // Call write_file() with empty data. Should still create the file.
554     write_file(".foobar","");
555
556     stat.recheck();
557
558     BOOST_CHECK_EQUAL( true, (bool)stat );
559     BOOST_CHECK_EQUAL( true, stat.size() == 0 );
560
561     unlink(".foobar");
562 }
563
564 BOOST_AUTO_TEST_CASE(FileContentDiffersIsIdentical)
565 {
566     BOOST_CHECK_EQUAL( false, file_content_differs("/etc/passwd", "/etc/passwd") );
567 }
568
569 BOOST_AUTO_TEST_CASE(FileContentDiffersNonExistent)
570 {
571     BOOST_CHECK_EQUAL( true, file_content_differs("/etc/passwd", "abc") );
572     BOOST_CHECK_EQUAL( true, file_content_differs("abc", "/etc/passwd") );
573 }
574
575 BOOST_AUTO_TEST_CASE(FileContentDiffersEqualSize)
576 {
577     const string name1 = "file1", name2 = "file2";
578     {
579         ofstream out1(name1.c_str());
580         out1 << "abc";
581
582         ofstream out2(name2.c_str());
583         out2 << "xyz";
584     }
585
586     bool res = file_content_differs(name1, name2);
587     unlink(name1);
588     unlink(name2);
589
590     BOOST_CHECK_EQUAL( true, res );
591 }
592
593 BOOST_AUTO_TEST_CASE(FreeDiskSpace1)
594 {
595     long long dfout;
596     long long get_free_diskspace_tmp;
597
598     // we need a partition to do this test. we can't create our own because that would require root
599     // so we use /tmp and retry it a few times in case of an error
600     int retries = 5;
601     while (retries > 0)
602     {
603         string dfstr=capture_exec("df -P -B 1 /tmp | tail -n 1 | sed \"s/ \\+/\\\\n/g\" | sed -n 4,4p");
604
605         dfout=-1;
606         string_to<long long>(dfstr,dfout);
607
608         get_free_diskspace_tmp=get_free_diskspace("/tmp");
609         
610         if ( dfout == get_free_diskspace_tmp )
611             break;
612         
613         retries--;
614     }
615
616     // do the check again with BOOST_CHECK_EQUAL to show it in the unit test output
617     BOOST_CHECK_EQUAL( dfout, get_free_diskspace_tmp );
618 }
619
620 BOOST_AUTO_TEST_CASE(FreeDiskSpace2)
621 {
622     BOOST_CHECK_EQUAL( -1, get_free_diskspace("/this/path/is/really/bogus") );
623 }
624
625 BOOST_AUTO_TEST_CASE(TestDu)
626 {
627     // Create unique directory
628     string unique_dir = I2n::mkdtemp("foobar.XXXXXX");
629     BOOST_REQUIRE(unique_dir.size() > 0);
630
631     // Test if it's really a directory
632     BOOST_CHECK_EQUAL(true, Stat(unique_dir).is_directory());
633
634     // Create dirs and files below it
635     const std::string sub_dir = unique_dir + "/" + "some_subdir";
636     const std::string some_file = sub_dir + "/" + "some_file";
637     BOOST_CHECK_EQUAL(true, I2n::mkdir(sub_dir));
638     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"));
639
640     string dustr=capture_exec(string("du --block-size=1 --sum ")+unique_dir+" | cut -f 1");
641     long long duout=-1;
642     string_to<long long>(dustr,duout);
643
644     long long first_du=du(unique_dir);
645
646     BOOST_CHECK_EQUAL( duout, first_du );
647
648     // create hardlinks
649     string cmd;
650     cmd=string("ln ")+some_file+" hardlink1";
651     system(cmd.c_str());
652
653     cmd=string("ln ")+some_file+" hardlink2";
654     system(cmd.c_str());
655
656     cmd=string("ln ")+some_file+" hardlink3";
657     system(cmd.c_str());
658
659     long long du_with_hardlinks=du(unique_dir);
660
661     BOOST_CHECK_EQUAL( first_du , du_with_hardlinks );
662
663     // Unlink it
664     BOOST_CHECK_EQUAL(true, I2n::recursive_delete(unique_dir));
665     BOOST_CHECK_EQUAL(false, Stat(unique_dir).is_directory());
666
667     // remove links
668     unlink("hardlink1");
669     unlink("hardlink2");
670     unlink("hardlink3");
671 }
672
673
674
675 BOOST_AUTO_TEST_SUITE_END()