add safeguard against infinite loop with EINTR, even if that case is very unlikely
[libi2ncommon] / test / test_filefunc.cpp
CommitLineData
0e23f538
TJ
1/*
2The software in this package is distributed under the GNU General
3Public License version 2 (with a special exception described below).
4
5A copy of GNU General Public License (GPL) is included in this distribution,
6in the file COPYING.GPL.
7
8As a special exception, if other files instantiate templates or use macros
9or inline functions from this file, or you compile this file and link it
10with other works to produce a work based on this file, this file
11does not by itself cause the resulting work to be covered
12by the GNU General Public License.
13
14However the source code for this file must still be made available
15in accordance with section (3) of the GNU General Public License.
16
17This exception does not invalidate any other reasons why a work based
18on this file might be covered by the GNU General Public License.
19*/
5de0c58a
RP
20/** @file
21 *
22 * tests for the modules "filefunc", "daemonfunc"
23 *
24 * (c) Copyright 2007-2008 by Intra2net AG
5de0c58a
RP
25 */
26
27//#define NOISEDEBUG
28
29#include <string>
30#include <vector>
31#include <list>
9fe0853b 32#include <set>
5de0c58a
RP
33#include <iostream>
34#include <iomanip>
1a2fc4e8 35#include <fstream>
5de0c58a
RP
36#include <sstream>
37#include <algorithm>
38
9fe0853b
TJ
39#define BOOST_TEST_DYN_LINK
40#include <boost/test/unit_test.hpp>
5de0c58a
RP
41
42#include <filefunc.hxx>
90246b4a 43#include <daemonfunc.hpp>
34ed5e5e
GE
44#include <pipestream.hxx>
45#include <stringfunc.hxx>
5de0c58a
RP
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
1a2fc4e8 53using namespace std;
5de0c58a
RP
54using namespace I2n;
55
9fe0853b 56class TestFileFuncFixture
48d97d6c 57{
48d97d6c 58protected:
48d97d6c 59 typedef std::list< std::string > StringList;
48d97d6c
TJ
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
48d97d6c
TJ
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
48d97d6c 86public:
9fe0853b
TJ
87 TestFileFuncFixture()
88 {
89 }
48d97d6c 90
9fe0853b
TJ
91 ~TestFileFuncFixture()
92 {
93 remove_check_files();
94 }
95};
48d97d6c 96
9fe0853b 97BOOST_FIXTURE_TEST_SUITE(TestFileFunc, TestFileFuncFixture)
48d97d6c 98
9fe0853b
TJ
99BOOST_AUTO_TEST_CASE(StatTest1)
100{
263b5807 101 I2n::Stat stat("Makefile");
48d97d6c 102
9fe0853b
TJ
103 BOOST_CHECK_EQUAL( true, (bool)stat );
104 BOOST_CHECK_EQUAL( true, stat.is_regular_file() );
105 BOOST_CHECK_EQUAL( false, stat.is_directory() );
48d97d6c 106
9fe0853b 107 stat= Stat("/dev/null");
48d97d6c 108
9fe0853b
TJ
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
48d97d6c 116
a9f2cccc
GE
117BOOST_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
96fe2dc5
GE
131BOOST_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}
48d97d6c 148
9fe0853b
TJ
149BOOST_AUTO_TEST_CASE(DirTest1)
150{
151 typedef std::vector< std::string > StringVector;
152 StringVector names;
48d97d6c 153
9fe0853b 154 bool res= I2n::get_dir(".",names);
48d97d6c 155
9fe0853b
TJ
156 BOOST_CHECK_EQUAL( true, res );
157 BOOST_CHECK( ! names.empty() );
48d97d6c 158
263b5807 159 StringVector::iterator it = std::find( names.begin(), names.end(), "Makefile");
9fe0853b 160 BOOST_CHECK( it != names.end() );
48d97d6c 161
9fe0853b
TJ
162 it = std::find( names.begin(), names.end(), "." );
163 BOOST_CHECK( it == names.end() );
48d97d6c 164
9fe0853b
TJ
165 names= get_dir(".",true);
166 BOOST_CHECK( ! names.empty() );
48d97d6c 167
9fe0853b
TJ
168 for (it= names.begin(); it!=names.end(); ++it)
169 {
170 DOUT(" \"" << *it << "\"");
171 BOOST_CHECK_EQUAL( false, it->empty() );
172 }
48d97d6c 173
9fe0853b
TJ
174 it = std::find( names.begin(), names.end(), "." );
175 BOOST_CHECK( it != names.end() );
176} // eo DirTest1
48d97d6c
TJ
177
178
179
9fe0853b
TJ
180BOOST_AUTO_TEST_CASE(PathCuts1)
181{
182 std::string path1("/an/absolute/path");
48d97d6c 183
9fe0853b
TJ
184 BOOST_CHECK_EQUAL( std::string("/an/absolute"), dirname(path1) );
185 BOOST_CHECK_EQUAL( std::string("path"), basename(path1) );
48d97d6c 186
9fe0853b
TJ
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()
48d97d6c 191
48d97d6c 192
48d97d6c 193
9fe0853b
TJ
194BOOST_AUTO_TEST_CASE(NormalizePath1)
195{
196 std::string path;
48d97d6c 197
9fe0853b
TJ
198 path= normalize_path("/a/simple/path/");
199 BOOST_CHECK_EQUAL( std::string("/a/simple/path"), path );
48d97d6c 200
9fe0853b
TJ
201 path= normalize_path("//another///simple/.//path//");
202 BOOST_CHECK_EQUAL( std::string("/another/simple/path"), path );
19166500 203
9fe0853b
TJ
204 path= normalize_path("//..//..//a/dummy///../././simple/././/path//");
205 BOOST_CHECK_EQUAL( std::string("/a/simple/path"), path );
19166500 206
9fe0853b
TJ
207 path= normalize_path("../a/dummy//././..//simple//nice//absolute//.././..//relative/path//");
208 BOOST_CHECK_EQUAL( std::string("../a/simple/relative/path"), path );
19166500 209
9fe0853b
TJ
210 path= normalize_path("../../a/dummy//././..//simple/../nice//absolute//../.x/..//relative/path//");
211 BOOST_CHECK_EQUAL( std::string("../../a/nice/relative/path"), path );
19166500 212
9fe0853b 213} // eo NormalizePath1
19166500 214
9fe0853b
TJ
215BOOST_AUTO_TEST_CASE(NormalizePath2)
216{
217 std::string path;
19166500 218
9fe0853b
TJ
219 path= normalize_path("/");
220 BOOST_CHECK_EQUAL( std::string("/"), path );
19166500 221
9fe0853b
TJ
222 path= normalize_path("//");
223 BOOST_CHECK_EQUAL( std::string("/"), path );
48d97d6c 224
9fe0853b
TJ
225 path= normalize_path("/.//");
226 BOOST_CHECK_EQUAL( std::string("/"), path );
48d97d6c 227
9fe0853b
TJ
228 path= normalize_path(".");
229 BOOST_CHECK_EQUAL( std::string(""), path );
48d97d6c 230
9fe0853b
TJ
231 path= normalize_path("./");
232 BOOST_CHECK_EQUAL( std::string(""), path );
48d97d6c 233
9fe0853b
TJ
234 path= normalize_path(".///");
235 BOOST_CHECK_EQUAL( std::string(""), path );
48d97d6c 236
9fe0853b
TJ
237 path= normalize_path("/./data/files");
238 BOOST_CHECK_EQUAL( std::string("/data/files"), path );
48d97d6c 239
9fe0853b
TJ
240 path= normalize_path("./data/files/");
241 BOOST_CHECK_EQUAL( std::string("data/files"), path );
242} // eo NormalizePath2
48d97d6c 243
9fe0853b
TJ
244BOOST_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 );
48d97d6c 249
9fe0853b
TJ
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 );
48d97d6c 253
9fe0853b
TJ
254 User user_root2("root");
255 BOOST_CHECK_EQUAL( true, user_root2.is_valid() );
256 BOOST_CHECK_EQUAL( true, (bool)user_root2 );
48d97d6c 257
9fe0853b
TJ
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 );
48d97d6c 261
9fe0853b
TJ
262 Group group_root("root");
263 BOOST_CHECK_EQUAL( true, group_root.is_valid() );
264 BOOST_CHECK_EQUAL( true, (bool)group_root );
48d97d6c 265
9fe0853b
TJ
266 BOOST_CHECK_EQUAL( std::string("root"), group_root.Name );
267 BOOST_CHECK_EQUAL( (gid_t)0, group_root.Gid );
48d97d6c 268
9fe0853b 269} // TestUserAndGroupStuff1()
48d97d6c 270
9fe0853b
TJ
271BOOST_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() );
48d97d6c 276
9fe0853b
TJ
277 Group group_root;
278 BOOST_CHECK_EQUAL( false, group_root.is_valid() );
279}
48d97d6c 280
9fe0853b
TJ
281BOOST_AUTO_TEST_CASE(TestFileModes1)
282{
283 std::string path= get_check_file_path("FileModes1");
48d97d6c 284
9fe0853b 285 write_file(path,"42");
48d97d6c 286
9fe0853b
TJ
287 Stat stat(path, false);
288 BOOST_CHECK_EQUAL( true, stat.is_valid() );
48d97d6c 289
9fe0853b
TJ
290 User user( stat.uid() );
291 Group group( stat.gid() );
48d97d6c 292
9fe0853b
TJ
293 BOOST_CHECK_EQUAL( true, user.is_valid() );
294 BOOST_CHECK_EQUAL( true, group.is_valid() );
48d97d6c 295
9fe0853b 296 bool res=chown( path, user.Name.c_str(), group.Gid );
48d97d6c 297
9fe0853b
TJ
298 BOOST_CHECK_EQUAL( true, res );
299} // eo TestFileModes1()
48d97d6c 300
48d97d6c 301
48d97d6c 302
9fe0853b
TJ
303BOOST_AUTO_TEST_CASE(TestPidOf1)
304{
305 using I2n::Daemon::pid_of;
48d97d6c 306
9fe0853b 307 std::vector< pid_t > pid_list;
1a2fc4e8 308
9fe0853b 309 bool res= pid_of("init", pid_list);
9fe0853b 310 BOOST_CHECK_EQUAL( true, res);
246bcd58
GE
311
312 if (pid_list.empty())
313 {
314 res= pid_of("systemd", pid_list);
315 BOOST_CHECK_EQUAL( true, res);
316 }
317
9fe0853b 318 BOOST_CHECK_EQUAL( false, pid_list.empty() );
1a2fc4e8 319
9fe0853b
TJ
320 std::vector< pid_t >::const_iterator pos1 =
321 std::find( pid_list.begin(), pid_list.end(), 1);
48d97d6c 322
9fe0853b
TJ
323 BOOST_CHECK( pos1 != pid_list.end() );
324} // eo TestPidOf1()
1a2fc4e8 325
9fe0853b
TJ
326BOOST_AUTO_TEST_CASE(TestCopyFileSourceFail)
327{
328 bool res = copy_file("does not exist", "destination");
329 BOOST_CHECK_EQUAL( false, res );
330}
48d97d6c 331
9fe0853b
TJ
332BOOST_AUTO_TEST_CASE(TestCopyFileDestFail)
333{
334 bool res = copy_file("/etc/HOSTNAME", "/proc/not/writable");
335 BOOST_CHECK_EQUAL( false, res );
336}
5de0c58a 337
9fe0853b
TJ
338BOOST_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
faf8475b
TJ
369BOOST_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
997987a6
TJ
379 BOOST_CHECK_EQUAL(true, rmdir(unique_dir));
380}
381
382BOOST_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
faf8475b 398 BOOST_CHECK_EQUAL(true, I2n::recursive_delete(unique_dir));
997987a6
TJ
399 BOOST_CHECK_EQUAL(false, Stat(unique_dir).is_directory());
400}
401
402BOOST_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));
faf8475b
TJ
429}
430
431BOOST_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
44725532
TJ
438BOOST_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
455BOOST_AUTO_TEST_CASE(DirectoryCreatePermission)
456{
457 string dirname = "test_directory";
458
901e2943
TJ
459 // Save and fix umask
460 mode_t previous_mask = I2n::umask(0);
44725532
TJ
461
462 I2n::rmdir (dirname);
463 BOOST_CHECK_EQUAL(true, I2n::mkdir(dirname, 0770));
901e2943 464 BOOST_CHECK_EQUAL(0770, Stat(dirname).mode());
44725532
TJ
465 I2n::rmdir (dirname);
466
901e2943
TJ
467 // Restore umask
468 I2n::umask(previous_mask);
44725532
TJ
469}
470
471BOOST_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
c5935370
TJ
483BOOST_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
9d1eb8c8
TJ
502BOOST_AUTO_TEST_CASE(FileContentDiffersIsIdentical)
503{
504 BOOST_CHECK_EQUAL( false, file_content_differs("/etc/passwd", "/etc/passwd") );
505}
506
507BOOST_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
513BOOST_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
34ed5e5e
GE
531BOOST_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
544BOOST_AUTO_TEST_CASE(FreeDiskSpace2)
545{
546 BOOST_CHECK_EQUAL( -1, get_free_diskspace("/this/path/is/really/bogus") );
547}
548
9fe0853b 549BOOST_AUTO_TEST_SUITE_END()