#include "filesystem_test.h"
#include <glib.h>
-#include <glib/gstdio.h>
+#include <pbd/gstdio_compat.h>
#include <unistd.h>
#include <stdlib.h>
+#include <fcntl.h>
+
+#ifdef COMPILER_MSVC
+#include <sys/utime.h>
+#else
+#include <utime.h>
+#endif
+
#include <glibmm/miscutils.h>
#include <glibmm/fileutils.h>
+#include <glibmm/convert.h>
+#include <glibmm/timer.h>
#include "pbd/file_utils.h"
+#include "pbd/pathexpand.h"
#include "test_common.h"
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
}
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<string>::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;
}
}
+void
+FilesystemTest::testOpenFileUTF8Filename ()
+{
+ vector<string> 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<string>::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<string>::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 ()
{
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<string> copied_files;
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);
+}