X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fpbd%2Ftest%2Ffilesystem_test.cc;h=1eaba5496873af78d3b9b97bbd5f1c7636cba7bd;hb=e44212321e436ee12bffb76361202b5efc83a172;hp=781e2bdafed222c4d4c725353da52ca14d0eb401;hpb=aed58d6f39f77d9d0426e86d4e6cd6b81ebadee6;p=ardour.git diff --git a/libs/pbd/test/filesystem_test.cc b/libs/pbd/test/filesystem_test.cc index 781e2bdafe..1eaba54968 100644 --- a/libs/pbd/test/filesystem_test.cc +++ b/libs/pbd/test/filesystem_test.cc @@ -1,15 +1,26 @@ #include "filesystem_test.h" #include -#include +#include #include #include +#include + +#ifdef COMPILER_MSVC +#include +#else +#include +#endif + #include #include +#include +#include #include "pbd/file_utils.h" +#include "pbd/pathexpand.h" #include "test_common.h" @@ -18,33 +29,57 @@ using namespace PBD; CPPUNIT_TEST_SUITE_REGISTRATION (FilesystemTest); +namespace { + +class PwdReset +{ +public: + + PwdReset(const string& new_pwd) + : m_old_pwd(Glib::get_current_dir()) { + CPPUNIT_ASSERT (g_chdir (new_pwd.c_str()) == 0); + } + + ~PwdReset() + { + CPPUNIT_ASSERT (g_chdir (m_old_pwd.c_str()) == 0); + } + +private: + + string m_old_pwd; + +}; + +} // anon + void FilesystemTest::testPathIsWithin () { #ifndef PLATFORM_WINDOWS string output_path = test_output_directory ("testPathIsWithin"); - CPPUNIT_ASSERT (g_chdir (output_path.c_str()) == 0); + PwdReset pwd_reset(output_path); CPPUNIT_ASSERT (g_mkdir_with_parents ("foo/bar/baz", 0755) == 0); - CPPUNIT_ASSERT (PBD::path_is_within ("foo/bar/baz", "foo/bar/baz")); - CPPUNIT_ASSERT (PBD::path_is_within ("foo/bar", "foo/bar/baz")); - CPPUNIT_ASSERT (PBD::path_is_within ("foo", "foo/bar/baz")); - CPPUNIT_ASSERT (PBD::path_is_within ("foo/bar", "foo/bar/baz")); - CPPUNIT_ASSERT (PBD::path_is_within ("foo/bar", "foo/bar")); + CPPUNIT_ASSERT (PBD::path_is_within (Glib::build_filename(output_path, "foo/bar/baz"), Glib::build_filename(output_path, "foo/bar/baz"))); + CPPUNIT_ASSERT (PBD::path_is_within (Glib::build_filename(output_path, "foo/bar"), Glib::build_filename(output_path, "foo/bar/baz"))); + CPPUNIT_ASSERT (PBD::path_is_within (Glib::build_filename(output_path, "foo"), Glib::build_filename(output_path, "foo/bar/baz"))); + CPPUNIT_ASSERT (PBD::path_is_within (Glib::build_filename(output_path, "foo/bar"), Glib::build_filename(output_path, "foo/bar/baz"))); + CPPUNIT_ASSERT (PBD::path_is_within (Glib::build_filename(output_path, "foo/bar"), Glib::build_filename(output_path, "foo/bar"))); - CPPUNIT_ASSERT (PBD::path_is_within ("foo/bar/baz", "frobozz") == false); + CPPUNIT_ASSERT (PBD::path_is_within (Glib::build_filename(output_path, "foo/bar/baz"), Glib::build_filename(output_path, "frobozz")) == false); int const r = symlink ("bar", "foo/jim"); CPPUNIT_ASSERT (r == 0); - CPPUNIT_ASSERT (PBD::path_is_within ("foo/bar/baz", "foo/bar/baz")); - CPPUNIT_ASSERT (PBD::path_is_within ("foo/bar", "foo/bar/baz")); - CPPUNIT_ASSERT (PBD::path_is_within ("foo", "foo/bar/baz")); - CPPUNIT_ASSERT (PBD::path_is_within ("foo/bar", "foo/bar/baz")); - CPPUNIT_ASSERT (PBD::path_is_within ("foo/bar", "foo/bar")); + CPPUNIT_ASSERT (PBD::path_is_within (Glib::build_filename(output_path, "foo/jim/baz"), Glib::build_filename(output_path, "foo/bar/baz"))); + CPPUNIT_ASSERT (PBD::path_is_within (Glib::build_filename(output_path, "foo/jim"), Glib::build_filename(output_path, "foo/bar/baz"))); + CPPUNIT_ASSERT (PBD::path_is_within (Glib::build_filename(output_path, "foo"), Glib::build_filename(output_path, "foo/bar/baz"))); + CPPUNIT_ASSERT (PBD::path_is_within (Glib::build_filename(output_path, "foo/jim"), Glib::build_filename(output_path, "foo/bar/baz"))); + CPPUNIT_ASSERT (PBD::path_is_within (Glib::build_filename(output_path, "foo/jim"), Glib::build_filename(output_path, "foo/bar"))); - CPPUNIT_ASSERT (PBD::path_is_within ("foo/jim/baz", "frobozz") == false); + CPPUNIT_ASSERT (PBD::path_is_within (Glib::build_filename(output_path, "foo/jim/baz"), Glib::build_filename(output_path, "frobozz")) == false); #endif } @@ -74,15 +109,18 @@ FilesystemTest::testCopyFileUTF8Filename () PBD::find_files_matching_pattern (i18n_files, i18n_path, "*.tst"); + CPPUNIT_ASSERT (i18n_files.size() == 8); + cerr << endl; cerr << "Copying " << i18n_files.size() << " test files from: " << i18n_path.to_string () << endl; + string output_dir = test_output_directory ("CopyFile"); + for (vector::iterator i = i18n_files.begin(); i != i18n_files.end(); ++i) { string input_path = *i; string output_file = Glib::path_get_basename(*i); - string output_path = test_output_directory ("CopyFile"); - output_path = Glib::build_filename (output_path, output_file); + string output_path = Glib::build_filename (output_dir, output_file); cerr << "Copying test file: " << input_path << " To " << output_path << endl; @@ -91,6 +129,105 @@ FilesystemTest::testCopyFileUTF8Filename () } } +void +FilesystemTest::testOpenFileUTF8Filename () +{ + vector i18n_files; + + Searchpath i18n_path (test_search_path ()); + i18n_path.add_subdirectory_to_paths ("i18n_test"); + + PBD::find_files_matching_pattern (i18n_files, i18n_path, "*.tst"); + + CPPUNIT_ASSERT (i18n_files.size () == 8); + + cerr << endl; + cerr << "Opening " << i18n_files.size () + << " test files from: " << i18n_path.to_string () << endl; + + // check that g_open will successfully open all the test files + for (vector::iterator i = i18n_files.begin (); i != i18n_files.end (); + ++i) { + string input_path = *i; + + cerr << "Opening file: " << input_path << " with g_open" << endl; + + int fdgo = g_open (input_path.c_str(), O_RDONLY, 0444); + + CPPUNIT_ASSERT (fdgo != -1); + + if (fdgo >= 0) { + ::close (fdgo); + } + } + +#ifdef PLATFORM_WINDOWS + // This test is here to prove and remind us that using Glib::locale_from_utf8 + // to convert a utf-8 encoded file path for use with ::open will not work + // for all file paths. + // + // It may be possible to convert a string that is utf-8 encoded that will not + // work with ::open(on windows) to a string that will work with ::open using + // Glib::locale_from_utf8 string if all the characters that are contained + // in the utf-8 string can be found/mapped in the system code page. + // + // European locales that only have a small amount of extra characters with + // accents/umlauts I'm guessing will be more likely succeed but CJK locales + // will almost certainly fail + + bool conversion_failed = false; + + for (vector::iterator i = i18n_files.begin (); i != i18n_files.end (); + ++i) { + string input_path = *i; + cerr << "Opening file: " << input_path << " with locale_from_utf8 and ::open " + << endl; + string converted_input_path; + int fdo; + + try { + // this will fail for utf8 that contains characters that aren't + // representable in the system code page + converted_input_path = Glib::locale_from_utf8 (input_path); + // conversion succeeded so we expect ::open to be successful if the + // current C library locale is the same as the system locale, which + // it should be as we haven't changed it. + fdo = ::open (converted_input_path.c_str (), O_RDONLY, 0444); + CPPUNIT_ASSERT (fdo != -1); + + if (converted_input_path != input_path) { + cerr << "Character set conversion succeeded and strings differ for input " + "string: " << input_path << endl; + // file path must have contained non-ASCII characters that were mapped + // from the system code page so we would expect the original + // utf-8 file path to fail with ::open + int fd2 = ::open (input_path.c_str (), O_RDONLY, 0444); + CPPUNIT_ASSERT (fd2 == -1); + } + + } catch (const Glib::ConvertError& err) { + cerr << "Character set conversion failed: " << err.what () << endl; + // I am confident that on Windows with the test data that no locale will + // have a system code page containing all the characters required + // and conversion will fail for at least one of the filenames + conversion_failed = true; + // CPPUNIT_ASSERT (err.code() == ?); + + // conversion failed so we expect the original utf-8 string to fail + // with ::open on Windows as the file path will not exist + fdo = ::open (input_path.c_str (), O_RDONLY, 0444); + CPPUNIT_ASSERT (fdo == -1); + } + + if (fdo >= 0) { + ::close (fdo); + } + } + // we expect at least one conversion failure with the filename test data + CPPUNIT_ASSERT (conversion_failed); +#endif +} + void FilesystemTest::testFindFilesMatchingPattern () { @@ -126,6 +263,8 @@ create_test_directory (std::string test_dir) cerr << "Copying " << test_files.size() << " test files from: " << test_dir_path << " to " << output_dir << endl; + CPPUNIT_ASSERT (test_files.size() != 0); + PBD::copy_files (test_dir_path, output_dir); vector copied_files; @@ -205,3 +344,98 @@ FilesystemTest::testRemoveDirectory () CPPUNIT_ASSERT (files_in_output_dir.size () == 0); } + +void +FilesystemTest::testCanonicalPath () +{ +#ifndef PLATFORM_WINDOWS + string top_dir = test_output_directory ("testCanonicalPath"); + PwdReset pwd_reset(top_dir); + + string pwd = Glib::get_current_dir (); + + CPPUNIT_ASSERT (!pwd.empty()); + CPPUNIT_ASSERT (pwd == top_dir); + + CPPUNIT_ASSERT (g_mkdir ("gtk2_ardour", 0755) == 0); + CPPUNIT_ASSERT (g_mkdir_with_parents ("libs/pbd/test", 0755) == 0); + + const char* relative_path = "./gtk2_ardour/../libs/pbd/test"; + string canonical_path = PBD::canonical_path (relative_path); + // no expansion expected in this case + string expanded_path = PBD::path_expand (relative_path); + string expected_path = top_dir + string("/libs/pbd/test"); + + CPPUNIT_ASSERT (canonical_path == expected_path); + CPPUNIT_ASSERT (expanded_path == expected_path); +#endif +} + +void +FilesystemTest::testTouchFile () +{ + const string filename = "touch.me"; + + const string test_dir = test_output_directory ("testTouchFile"); + const string touch_file_path = Glib::build_filename (test_dir, filename); + + CPPUNIT_ASSERT (touch_file(touch_file_path)); + + CPPUNIT_ASSERT (Glib::file_test (touch_file_path, Glib::FILE_TEST_EXISTS)); +} + +void +FilesystemTest::testStatFile () +{ + const string filename1 = "touch.me"; + const string filename2 = "touch.me.2"; + + const string test_dir = test_output_directory ("testStatFile"); + + const string path1 = Glib::build_filename (test_dir, filename1); + const string path2 = Glib::build_filename (test_dir, filename2); + + CPPUNIT_ASSERT (touch_file(path1)); + + Glib::usleep (2000000); + + CPPUNIT_ASSERT (touch_file(path2)); + + GStatBuf gsb1; + GStatBuf gsb2; + + CPPUNIT_ASSERT (g_stat (path1.c_str(), &gsb1) == 0); + CPPUNIT_ASSERT (g_stat (path2.c_str(), &gsb2) == 0); + + cerr << endl; + cerr << "StatFile: " << path1 << " access time: " << gsb1.st_atime << endl; + cerr << "StatFile: " << path1 << " modification time: " << gsb1.st_mtime << endl; + cerr << "StatFile: " << path2 << " access time: " << gsb2.st_atime << endl; + cerr << "StatFile: " << path2 << " modification time: " << gsb2.st_mtime << endl; + + CPPUNIT_ASSERT (gsb1.st_atime == gsb1.st_mtime); + CPPUNIT_ASSERT (gsb2.st_atime == gsb2.st_mtime); + + // at least access time works on windows(or at least on ntfs) + CPPUNIT_ASSERT (gsb1.st_atime < gsb2.st_atime); + CPPUNIT_ASSERT (gsb1.st_mtime < gsb2.st_mtime); + + struct utimbuf tbuf; + + tbuf.actime = gsb1.st_atime; + tbuf.modtime = gsb1.st_mtime; + + // update the file access/modification times to be the same + CPPUNIT_ASSERT (g_utime (path2.c_str(), &tbuf) == 0); + + CPPUNIT_ASSERT (g_stat (path2.c_str(), &gsb2) == 0); + + cerr << endl; + cerr << "StatFile: " << path1 << " access time: " << gsb1.st_atime << endl; + cerr << "StatFile: " << path1 << " modification time: " << gsb1.st_mtime << endl; + cerr << "StatFile: " << path2 << " access time: " << gsb2.st_atime << endl; + cerr << "StatFile: " << path2 << " modification time: " << gsb2.st_mtime << endl; + + CPPUNIT_ASSERT (gsb1.st_atime == gsb2.st_atime); + CPPUNIT_ASSERT (gsb1.st_mtime == gsb2.st_mtime); +}