/* The software in this package is distributed under the GNU General Public License version 2 (with a special exception described below). A copy of GNU General Public License (GPL) is included in this distribution, in the file COPYING.GPL. As a special exception, if other files instantiate templates or use macros or inline functions from this file, or you compile this file and link it with other works to produce a work based on this file, this file does not by itself cause the resulting work to be covered by the GNU General Public License. However the source code for this file must still be made available in accordance with section (3) of the GNU General Public License. This exception does not invalidate any other reasons why a work based on this file might be covered by the GNU General Public License. */ /** @file * * tests for the modules "filefunc", "daemonfunc" * * (c) Copyright 2007-2008 by Intra2net AG */ //#define NOISEDEBUG #include #include #include #include #include #include #include #include #include #define BOOST_TEST_DYN_LINK #include #include #include #include #include #ifdef NOISEDEBUG #define DOUT(msg) std::cout << msg << std::endl #else #define DOUT(msg) do {} while (0) #endif using namespace std; using namespace I2n; class TestFileFuncFixture { protected: typedef std::list< std::string > StringList; std::set used_check_files; std::string get_check_file_path(std::string tag) { std::string result; result= "__unittest__" + tag + ".dat"; used_check_files.insert(result); return result; } // eo get_check_file_path void remove_check_files() { for (std::set::iterator it= used_check_files.begin(); it != used_check_files.end(); ++it) { std::string filepath(*it); if (path_exists(filepath)) { unlink(filepath); } //TODO } used_check_files.clear(); } // eo remove_check_files public: TestFileFuncFixture() { } ~TestFileFuncFixture() { remove_check_files(); } }; BOOST_FIXTURE_TEST_SUITE(TestFileFunc, TestFileFuncFixture) BOOST_AUTO_TEST_CASE(StatTest1) { I2n::Stat stat(__FILE__); BOOST_CHECK_EQUAL( true, (bool)stat ); BOOST_CHECK_EQUAL( true, stat.is_regular_file() ); BOOST_CHECK_EQUAL( false, stat.is_directory() ); stat= Stat("/dev/null"); BOOST_CHECK_EQUAL( true, (bool)stat ); BOOST_CHECK_EQUAL( false, stat.is_regular_file() ); BOOST_CHECK_EQUAL( false, stat.is_directory() ); BOOST_CHECK_EQUAL( true, stat.is_device() ); BOOST_CHECK_EQUAL( true, stat.is_character_device() ); BOOST_CHECK_EQUAL( false, stat.is_block_device() ); } // eo StatTest1 BOOST_AUTO_TEST_CASE(StatSize) { write_file("/tmp/test","some nice content to make sure the file system optimizations don't inline this into the inode block"); I2n::Stat stat("/tmp/test"); BOOST_CHECK_EQUAL( true, (bool)stat ); BOOST_CHECK_EQUAL( true, stat.is_regular_file() ); BOOST_CHECK_EQUAL( true, ( stat.bytes_on_disk() >= 512 ) ); unlink("/tmp/test"); } // eo StatSize BOOST_AUTO_TEST_CASE(StatRecheck) { // just to be sure unlink(".foobar"); I2n::Stat stat(".foobar"); BOOST_CHECK_EQUAL( false, (bool)stat ); write_file(".foobar","hello world"); stat.recheck(); BOOST_CHECK_EQUAL( true, (bool)stat ); BOOST_CHECK_EQUAL( true, stat.size() > 0 ); unlink(".foobar"); } BOOST_AUTO_TEST_CASE(DirTest1) { typedef std::vector< std::string > StringVector; StringVector names; string test_dir = dirname(__FILE__); bool res= I2n::get_dir(test_dir,names); BOOST_CHECK_EQUAL( true, res ); BOOST_CHECK( ! names.empty() ); BOOST_MESSAGE("Looking for " << basename(__FILE__) << " in " << test_dir); StringVector::iterator it = std::find( names.begin(), names.end(), basename(__FILE__)); BOOST_CHECK( it != names.end() ); it = std::find( names.begin(), names.end(), "." ); BOOST_CHECK( it == names.end() ); names= get_dir(test_dir,true); BOOST_CHECK( ! names.empty() ); for (it= names.begin(); it!=names.end(); ++it) { DOUT(" \"" << *it << "\""); BOOST_CHECK_EQUAL( false, it->empty() ); } it = std::find( names.begin(), names.end(), "." ); BOOST_CHECK( it != names.end() ); } // eo DirTest1 BOOST_AUTO_TEST_CASE(PathCuts1) { std::string path1("/an/absolute/path"); BOOST_CHECK_EQUAL( std::string("/an/absolute"), dirname(path1) ); BOOST_CHECK_EQUAL( std::string("path"), basename(path1) ); std::string path2("just.a.name"); BOOST_CHECK_EQUAL( std::string("just.a.name"), basename(path2) ); BOOST_CHECK_EQUAL( std::string("."), dirname(path2) ); } // eo PathCuts1() BOOST_AUTO_TEST_CASE(NormalizePath1) { std::string path; path= normalize_path("/a/simple/path/"); BOOST_CHECK_EQUAL( std::string("/a/simple/path"), path ); path= normalize_path("//another///simple/.//path//"); BOOST_CHECK_EQUAL( std::string("/another/simple/path"), path ); path= normalize_path("//..//..//a/dummy///../././simple/././/path//"); BOOST_CHECK_EQUAL( std::string("/a/simple/path"), path ); path= normalize_path("../a/dummy//././..//simple//nice//absolute//.././..//relative/path//"); BOOST_CHECK_EQUAL( std::string("../a/simple/relative/path"), path ); path= normalize_path("../../a/dummy//././..//simple/../nice//absolute//../.x/..//relative/path//"); BOOST_CHECK_EQUAL( std::string("../../a/nice/relative/path"), path ); } // eo NormalizePath1 BOOST_AUTO_TEST_CASE(NormalizePath2) { std::string path; path= normalize_path("/"); BOOST_CHECK_EQUAL( std::string("/"), path ); path= normalize_path("//"); BOOST_CHECK_EQUAL( std::string("/"), path ); path= normalize_path("/.//"); BOOST_CHECK_EQUAL( std::string("/"), path ); path= normalize_path("."); BOOST_CHECK_EQUAL( std::string(""), path ); path= normalize_path("./"); BOOST_CHECK_EQUAL( std::string(""), path ); path= normalize_path(".///"); BOOST_CHECK_EQUAL( std::string(""), path ); path= normalize_path("/./data/files"); BOOST_CHECK_EQUAL( std::string("/data/files"), path ); path= normalize_path("./data/files/"); BOOST_CHECK_EQUAL( std::string("data/files"), path ); } // eo NormalizePath2 BOOST_AUTO_TEST_CASE(TestUserAndGroupStuff1) { User user_root((uid_t)0); BOOST_CHECK_EQUAL( true, user_root.is_valid() ); BOOST_CHECK_EQUAL( true, (bool)user_root ); BOOST_CHECK_EQUAL( std::string("root"), user_root.Name ); BOOST_CHECK_EQUAL( (uid_t)0, user_root.Uid ); BOOST_CHECK_EQUAL( (gid_t)0, user_root.Gid ); User user_root2("root"); BOOST_CHECK_EQUAL( true, user_root2.is_valid() ); BOOST_CHECK_EQUAL( true, (bool)user_root2 ); BOOST_CHECK_EQUAL( std::string("root"), user_root2.Name ); BOOST_CHECK_EQUAL( (uid_t)0, user_root2.Uid ); BOOST_CHECK_EQUAL( (gid_t)0, user_root2.Gid ); Group group_root("root"); BOOST_CHECK_EQUAL( true, group_root.is_valid() ); BOOST_CHECK_EQUAL( true, (bool)group_root ); BOOST_CHECK_EQUAL( std::string("root"), group_root.Name ); BOOST_CHECK_EQUAL( (gid_t)0, group_root.Gid ); } // TestUserAndGroupStuff1() BOOST_AUTO_TEST_CASE(TestUserAndGroupStuff2) { // Test if is_valid() works correctly User user_root; BOOST_CHECK_EQUAL( false, user_root.is_valid() ); Group group_root; BOOST_CHECK_EQUAL( false, group_root.is_valid() ); } BOOST_AUTO_TEST_CASE(TestFileModes1) { std::string path= get_check_file_path("FileModes1"); write_file(path,"42"); Stat stat(path, false); BOOST_CHECK_EQUAL( true, stat.is_valid() ); User user( stat.uid() ); Group group( stat.gid() ); BOOST_CHECK_EQUAL( true, user.is_valid() ); BOOST_CHECK_EQUAL( true, group.is_valid() ); bool res=chown( path, user.Name.c_str(), group.Gid ); BOOST_CHECK_EQUAL( true, res ); } // eo TestFileModes1() BOOST_AUTO_TEST_CASE(TestPidOf1) { using I2n::Daemon::pid_of; std::vector< pid_t > pid_list; bool res= pid_of("init", pid_list); BOOST_CHECK_EQUAL( true, res); if (pid_list.empty()) { res= pid_of("systemd", pid_list); BOOST_CHECK_EQUAL( true, res); } BOOST_CHECK_EQUAL( false, pid_list.empty() ); std::vector< pid_t >::const_iterator pos1 = std::find( pid_list.begin(), pid_list.end(), 1); BOOST_CHECK( pos1 != pid_list.end() ); } // eo TestPidOf1() BOOST_AUTO_TEST_CASE(TestCopyFileSourceFail) { bool res = copy_file("does not exist", "destination"); BOOST_CHECK_EQUAL( false, res ); } BOOST_AUTO_TEST_CASE(TestCopyFileDestFail) { bool res = copy_file("/etc/HOSTNAME", "/proc/not/writable"); BOOST_CHECK_EQUAL( false, res ); } BOOST_AUTO_TEST_CASE(TestCopyFileOk) { string input = "copy_source"; string data = "test"; long input_size = 0; ofstream finput(input.c_str()); if (finput) { finput << data; finput.close(); input_size = file_size(input); } string output = "copy_dest"; long output_size = 0; bool res = copy_file(input, output); if (res) { output_size = file_size(output); } unlink(input); unlink(output); bool size_is_zero = output_size == 0 ? true : false; BOOST_CHECK_EQUAL( true, res ); BOOST_CHECK_EQUAL( input_size, output_size ); BOOST_CHECK_EQUAL( false, size_is_zero ); } BOOST_AUTO_TEST_CASE(TestMkdtemp) { // Create unique directory string unique_dir = I2n::mkdtemp("foobar.XXXXXX"); BOOST_REQUIRE(unique_dir.size() > 0); // Test if it's really a directory BOOST_CHECK_EQUAL(true, Stat(unique_dir).is_directory()); // Unlink it BOOST_CHECK_EQUAL(true, rmdir(unique_dir)); } BOOST_AUTO_TEST_CASE(TestRecursiveDelete) { // Create unique directory string unique_dir = I2n::mkdtemp("foobar.XXXXXX"); BOOST_REQUIRE(unique_dir.size() > 0); // Test if it's really a directory BOOST_CHECK_EQUAL(true, Stat(unique_dir).is_directory()); // Create dirs and files below it const std::string sub_dir = unique_dir + "/" + "some_subdir"; const std::string some_file = sub_dir + "/" + "some_file"; BOOST_CHECK_EQUAL(true, I2n::mkdir(sub_dir)); BOOST_CHECK_EQUAL(true, write_file(some_file, "foobar")); // Unlink it BOOST_CHECK_EQUAL(true, I2n::recursive_delete(unique_dir)); BOOST_CHECK_EQUAL(false, Stat(unique_dir).is_directory()); } BOOST_AUTO_TEST_CASE(TestRecursiveDeleteKeepParentDir) { // Create unique directory string unique_dir = I2n::mkdtemp("foobar.XXXXXX"); BOOST_REQUIRE(unique_dir.size() > 0); // Test if it's really a directory BOOST_CHECK_EQUAL(true, Stat(unique_dir).is_directory()); // Create dirs and files below it const std::string sub_dir = unique_dir + "/" + "some_subdir"; const std::string some_file = sub_dir + "/" + "some_file"; const std::string other_file = unique_dir + "/" + "other_file"; BOOST_CHECK_EQUAL(true, I2n::mkdir(sub_dir)); BOOST_CHECK_EQUAL(true, write_file(some_file, "foobar")); BOOST_CHECK_EQUAL(true, write_file(other_file, "foobar")); // Unlink it BOOST_CHECK_EQUAL(true, I2n::recursive_delete(unique_dir, true)); // check result BOOST_CHECK_EQUAL(true, Stat(unique_dir).is_directory()); BOOST_CHECK_EQUAL(false, Stat(sub_dir).is_directory()); BOOST_CHECK_EQUAL(false, Stat(other_file).is_regular_file()); BOOST_CHECK_EQUAL(true, I2n::rmdir(unique_dir)); } BOOST_AUTO_TEST_CASE(TestRecursiveDeleteSymlinkedDirs1) { // Create unique directory string unique_dir = I2n::mkdtemp("foobar.XXXXXX"); BOOST_REQUIRE(unique_dir.size() > 0); // Test if it's really a directory BOOST_CHECK_EQUAL(true, Stat(unique_dir).is_directory()); // Create dirs and files below it const std::string sub_dir = unique_dir + "/" + "some_subdir"; const std::string some_file = sub_dir + "/" + "some_file"; const std::string dir_symlink_within_unique_dir = unique_dir + "/" + "dir_symlink_within_unique_dir"; BOOST_CHECK_EQUAL(true, I2n::mkdir(sub_dir)); BOOST_CHECK_EQUAL(true, write_file(some_file, "foobar")); BOOST_CHECK_EQUAL(true, symlink(sub_dir, dir_symlink_within_unique_dir)); // Unlink it BOOST_CHECK_EQUAL(true, I2n::recursive_delete(unique_dir)); BOOST_CHECK_EQUAL(false, Stat(unique_dir).is_directory()); } BOOST_AUTO_TEST_CASE(TestRecursiveDeleteSymlinkedDirs2) { // Create unique directory string unique_dir = I2n::mkdtemp("foobar.XXXXXX"); BOOST_REQUIRE(unique_dir.size() > 0); // Test if it's really a directory BOOST_CHECK_EQUAL(true, Stat(unique_dir).is_directory()); // Create another unique directory string other_unique_dir = I2n::mkdtemp("foobar_other.XXXXXX"); BOOST_REQUIRE(other_unique_dir.size() > 0); // Test if it's really a directory BOOST_CHECK_EQUAL(true, Stat(other_unique_dir).is_directory()); const std::string other_file = other_unique_dir + "/" + "other_file"; BOOST_CHECK_EQUAL(true, write_file(other_file, "foobar")); // Create dirs and files below it const std::string sub_dir = unique_dir + "/" + "some_subdir"; const std::string some_file = sub_dir + "/" + "some_file"; const std::string dir_symlink_to_outside_unique_dir = unique_dir + "/" + "dir_symlink_to_outside_unique_dir"; BOOST_CHECK_EQUAL(true, I2n::mkdir(sub_dir)); BOOST_CHECK_EQUAL(true, write_file(some_file, "foobar")); BOOST_CHECK_EQUAL(true, symlink(other_unique_dir, dir_symlink_to_outside_unique_dir)); // Unlink it BOOST_CHECK_EQUAL(true, I2n::recursive_delete(unique_dir)); BOOST_CHECK_EQUAL(false, Stat(unique_dir).is_directory()); // other unique dir must still be there BOOST_CHECK_EQUAL(true, Stat(other_unique_dir).is_directory()); BOOST_CHECK_EQUAL(true, Stat(other_file).is_regular_file()); // finally delete the other unique dir BOOST_CHECK_EQUAL(true, I2n::recursive_delete(other_unique_dir)); BOOST_CHECK_EQUAL(false, Stat(other_unique_dir).is_directory()); } BOOST_AUTO_TEST_CASE(TestMkdtempBrokenTemplate) { // Broken directory template -> fail string unique_dir = I2n::mkdtemp("foobar.XXX"); BOOST_CHECK_EQUAL("", unique_dir); } BOOST_AUTO_TEST_CASE(DirectoryCreation) { string dirname = "test_directory"; I2n::rmdir (dirname); BOOST_CHECK_EQUAL(true, I2n::mkdir(dirname)); BOOST_CHECK_EQUAL(true, Stat(dirname).is_directory()); // Verify default permissions BOOST_CHECK_EQUAL(0700, Stat(dirname).mode()); BOOST_CHECK_EQUAL(true, I2n::rmdir(dirname)); // Second call must fail: Directory already deleted BOOST_CHECK_EQUAL(false, I2n::rmdir(dirname)); } BOOST_AUTO_TEST_CASE(DirectoryCreatePermission) { string dirname = "test_directory"; // Save and fix umask mode_t previous_mask = I2n::umask(0); I2n::rmdir (dirname); BOOST_CHECK_EQUAL(true, I2n::mkdir(dirname, 0770)); BOOST_CHECK_EQUAL(0770, Stat(dirname).mode()); I2n::rmdir (dirname); // Restore umask I2n::umask(previous_mask); } BOOST_AUTO_TEST_CASE(ChangeDirectory) { string current_directory = I2n::getcwd(); BOOST_REQUIRE(!current_directory.empty()); BOOST_CHECK_EQUAL(true, I2n::chdir("/")); BOOST_CHECK_EQUAL("/", I2n::getcwd()); BOOST_CHECK_EQUAL(true, I2n::chdir(current_directory)); BOOST_REQUIRE_EQUAL(current_directory, I2n::getcwd()); } BOOST_AUTO_TEST_CASE(WriteFileEmptyData) { // just to be sure unlink(".foobar"); I2n::Stat stat(".foobar"); BOOST_CHECK_EQUAL( false, (bool)stat ); // Call write_file() with empty data. Should still create the file. write_file(".foobar",""); stat.recheck(); BOOST_CHECK_EQUAL( true, (bool)stat ); BOOST_CHECK_EQUAL( true, stat.size() == 0 ); unlink(".foobar"); } BOOST_AUTO_TEST_CASE(FileContentDiffersIsIdentical) { BOOST_CHECK_EQUAL( false, file_content_differs("/etc/passwd", "/etc/passwd") ); } BOOST_AUTO_TEST_CASE(FileContentDiffersNonExistent) { BOOST_CHECK_EQUAL( true, file_content_differs("/etc/passwd", "abc") ); BOOST_CHECK_EQUAL( true, file_content_differs("abc", "/etc/passwd") ); } BOOST_AUTO_TEST_CASE(FileContentDiffersEqualSize) { const string name1 = "file1", name2 = "file2"; { ofstream out1(name1.c_str()); out1 << "abc"; ofstream out2(name2.c_str()); out2 << "xyz"; } bool res = file_content_differs(name1, name2); unlink(name1); unlink(name2); BOOST_CHECK_EQUAL( true, res ); } BOOST_AUTO_TEST_CASE(FreeDiskSpace1) { long long dfout; long long get_free_diskspace_tmp; // we need a partition to do this test. we can't create our own because that would require root // so we use /tmp and retry it a few times in case of an error int retries = 5; while (retries > 0) { string dfstr=capture_exec("df -P -B 1 /tmp | tail -n 1 | sed \"s/ \\+/\\\\n/g\" | sed -n 4,4p"); dfout=-1; string_to(dfstr,dfout); get_free_diskspace_tmp=get_free_diskspace("/tmp"); if ( dfout == get_free_diskspace_tmp ) break; retries--; } // do the check again with BOOST_CHECK_EQUAL to show it in the unit test output BOOST_CHECK_EQUAL( dfout, get_free_diskspace_tmp ); } BOOST_AUTO_TEST_CASE(FreeDiskSpace2) { BOOST_CHECK_EQUAL( -1, get_free_diskspace("/this/path/is/really/bogus") ); } BOOST_AUTO_TEST_CASE(TestDu) { // Create unique directory string unique_dir = I2n::mkdtemp("foobar.XXXXXX"); BOOST_REQUIRE(unique_dir.size() > 0); // Test if it's really a directory BOOST_CHECK_EQUAL(true, Stat(unique_dir).is_directory()); // Create dirs and files below it const std::string sub_dir = unique_dir + "/" + "some_subdir"; const std::string some_file = sub_dir + "/" + "some_file"; BOOST_CHECK_EQUAL(true, I2n::mkdir(sub_dir)); 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")); string dustr=capture_exec(string("du --block-size=1 --sum ")+unique_dir+" | cut -f 1"); long long duout=-1; string_to(dustr,duout); long long first_du=du(unique_dir); BOOST_CHECK_EQUAL( duout, first_du ); // create hardlinks string cmd; cmd=string("ln ")+some_file+" hardlink1"; system(cmd.c_str()); cmd=string("ln ")+some_file+" hardlink2"; system(cmd.c_str()); cmd=string("ln ")+some_file+" hardlink3"; system(cmd.c_str()); long long du_with_hardlinks=du(unique_dir); BOOST_CHECK_EQUAL( first_du , du_with_hardlinks ); // Unlink it BOOST_CHECK_EQUAL(true, I2n::recursive_delete(unique_dir)); BOOST_CHECK_EQUAL(false, Stat(unique_dir).is_directory()); // remove links unlink("hardlink1"); unlink("hardlink2"); unlink("hardlink3"); } BOOST_AUTO_TEST_SUITE_END()