1 #include "filesystem_test.h"
4 #include <pbd/gstdio_compat.h>
12 #include <sys/utime.h>
17 #include <glibmm/miscutils.h>
18 #include <glibmm/fileutils.h>
19 #include <glibmm/convert.h>
20 #include <glibmm/timer.h>
22 #include "pbd/file_utils.h"
23 #include "pbd/pathexpand.h"
25 #include "test_common.h"
30 CPPUNIT_TEST_SUITE_REGISTRATION (FilesystemTest);
38 PwdReset(const string& new_pwd)
39 : m_old_pwd(Glib::get_current_dir()) {
40 CPPUNIT_ASSERT (g_chdir (new_pwd.c_str()) == 0);
45 CPPUNIT_ASSERT (g_chdir (m_old_pwd.c_str()) == 0);
57 FilesystemTest::testPathIsWithin ()
59 #ifndef PLATFORM_WINDOWS
60 string output_path = test_output_directory ("testPathIsWithin");
61 PwdReset pwd_reset(output_path);
63 CPPUNIT_ASSERT (g_mkdir_with_parents ("foo/bar/baz", 0755) == 0);
65 CPPUNIT_ASSERT (PBD::path_is_within (Glib::build_filename(output_path, "foo/bar/baz"), Glib::build_filename(output_path, "foo/bar/baz")));
66 CPPUNIT_ASSERT (PBD::path_is_within (Glib::build_filename(output_path, "foo/bar"), Glib::build_filename(output_path, "foo/bar/baz")));
67 CPPUNIT_ASSERT (PBD::path_is_within (Glib::build_filename(output_path, "foo"), Glib::build_filename(output_path, "foo/bar/baz")));
68 CPPUNIT_ASSERT (PBD::path_is_within (Glib::build_filename(output_path, "foo/bar"), Glib::build_filename(output_path, "foo/bar/baz")));
69 CPPUNIT_ASSERT (PBD::path_is_within (Glib::build_filename(output_path, "foo/bar"), Glib::build_filename(output_path, "foo/bar")));
71 CPPUNIT_ASSERT (PBD::path_is_within (Glib::build_filename(output_path, "foo/bar/baz"), Glib::build_filename(output_path, "frobozz")) == false);
73 int const r = symlink ("bar", "foo/jim");
74 CPPUNIT_ASSERT (r == 0);
76 CPPUNIT_ASSERT (PBD::path_is_within (Glib::build_filename(output_path, "foo/jim/baz"), Glib::build_filename(output_path, "foo/bar/baz")));
77 CPPUNIT_ASSERT (PBD::path_is_within (Glib::build_filename(output_path, "foo/jim"), Glib::build_filename(output_path, "foo/bar/baz")));
78 CPPUNIT_ASSERT (PBD::path_is_within (Glib::build_filename(output_path, "foo"), Glib::build_filename(output_path, "foo/bar/baz")));
79 CPPUNIT_ASSERT (PBD::path_is_within (Glib::build_filename(output_path, "foo/jim"), Glib::build_filename(output_path, "foo/bar/baz")));
80 CPPUNIT_ASSERT (PBD::path_is_within (Glib::build_filename(output_path, "foo/jim"), Glib::build_filename(output_path, "foo/bar")));
82 CPPUNIT_ASSERT (PBD::path_is_within (Glib::build_filename(output_path, "foo/jim/baz"), Glib::build_filename(output_path, "frobozz")) == false);
87 FilesystemTest::testCopyFileASCIIFilename ()
90 CPPUNIT_ASSERT (find_file (test_search_path (), "RosegardenPatchFile.xml", testdata_path));
92 string output_path = test_output_directory ("CopyFile");
94 output_path = Glib::build_filename (output_path, "RosegardenPatchFile.xml");
97 cerr << "CopyFile test output path: " << output_path << endl;
99 CPPUNIT_ASSERT (PBD::copy_file (testdata_path, output_path));
103 FilesystemTest::testCopyFileUTF8Filename ()
105 vector<string> i18n_files;
107 Searchpath i18n_path(test_search_path());
108 i18n_path.add_subdirectory_to_paths("i18n_test");
110 PBD::find_files_matching_pattern (i18n_files, i18n_path, "*.tst");
112 CPPUNIT_ASSERT (i18n_files.size() == 8);
115 cerr << "Copying " << i18n_files.size() << " test files from: "
116 << i18n_path.to_string () << endl;
118 string output_dir = test_output_directory ("CopyFile");
120 for (vector<string>::iterator i = i18n_files.begin(); i != i18n_files.end(); ++i) {
121 string input_path = *i;
122 string output_file = Glib::path_get_basename(*i);
123 string output_path = Glib::build_filename (output_dir, output_file);
125 cerr << "Copying test file: " << input_path
126 << " To " << output_path << endl;
128 CPPUNIT_ASSERT (PBD::copy_file (input_path, output_path));
133 FilesystemTest::testOpenFileUTF8Filename ()
135 vector<string> i18n_files;
137 Searchpath i18n_path (test_search_path ());
138 i18n_path.add_subdirectory_to_paths ("i18n_test");
140 PBD::find_files_matching_pattern (i18n_files, i18n_path, "*.tst");
142 CPPUNIT_ASSERT (i18n_files.size () == 8);
145 cerr << "Opening " << i18n_files.size ()
146 << " test files from: " << i18n_path.to_string () << endl;
148 // check that g_open will successfully open all the test files
149 for (vector<string>::iterator i = i18n_files.begin (); i != i18n_files.end ();
151 string input_path = *i;
153 cerr << "Opening file: " << input_path << " with g_open" << endl;
155 int fdgo = g_open (input_path.c_str(), O_RDONLY, 0444);
157 CPPUNIT_ASSERT (fdgo != -1);
164 #ifdef PLATFORM_WINDOWS
165 // This test is here to prove and remind us that using Glib::locale_from_utf8
166 // to convert a utf-8 encoded file path for use with ::open will not work
167 // for all file paths.
169 // It may be possible to convert a string that is utf-8 encoded that will not
170 // work with ::open(on windows) to a string that will work with ::open using
171 // Glib::locale_from_utf8 string if all the characters that are contained
172 // in the utf-8 string can be found/mapped in the system code page.
174 // European locales that only have a small amount of extra characters with
175 // accents/umlauts I'm guessing will be more likely succeed but CJK locales
176 // will almost certainly fail
178 bool conversion_failed = false;
180 for (vector<string>::iterator i = i18n_files.begin (); i != i18n_files.end ();
182 string input_path = *i;
183 cerr << "Opening file: " << input_path << " with locale_from_utf8 and ::open "
185 string converted_input_path;
189 // this will fail for utf8 that contains characters that aren't
190 // representable in the system code page
191 converted_input_path = Glib::locale_from_utf8 (input_path);
192 // conversion succeeded so we expect ::open to be successful if the
193 // current C library locale is the same as the system locale, which
194 // it should be as we haven't changed it.
195 fdo = ::open (converted_input_path.c_str (), O_RDONLY, 0444);
196 CPPUNIT_ASSERT (fdo != -1);
198 if (converted_input_path != input_path) {
199 cerr << "Character set conversion succeeded and strings differ for input "
200 "string: " << input_path << endl;
201 // file path must have contained non-ASCII characters that were mapped
202 // from the system code page so we would expect the original
203 // utf-8 file path to fail with ::open
204 int fd2 = ::open (input_path.c_str (), O_RDONLY, 0444);
205 CPPUNIT_ASSERT (fd2 == -1);
208 } catch (const Glib::ConvertError& err) {
209 cerr << "Character set conversion failed: " << err.what () << endl;
210 // I am confident that on Windows with the test data that no locale will
211 // have a system code page containing all the characters required
212 // and conversion will fail for at least one of the filenames
213 conversion_failed = true;
214 // CPPUNIT_ASSERT (err.code() == ?);
216 // conversion failed so we expect the original utf-8 string to fail
217 // with ::open on Windows as the file path will not exist
218 fdo = ::open (input_path.c_str (), O_RDONLY, 0444);
219 CPPUNIT_ASSERT (fdo == -1);
226 // we expect at least one conversion failure with the filename test data
227 CPPUNIT_ASSERT (conversion_failed);
232 FilesystemTest::testFindFilesMatchingPattern ()
234 vector<string> patch_files;
236 PBD::find_files_matching_pattern (patch_files, test_search_path (), "*PatchFile*");
238 CPPUNIT_ASSERT(test_search_path ().size() == 1);
240 CPPUNIT_ASSERT(patch_files.size() == 2);
244 create_test_directory (std::string test_dir)
246 vector<string> test_files;
247 vector<string> i18n_files;
249 Searchpath spath(test_search_path());
250 PBD::get_files (test_files, spath);
252 spath.add_subdirectory_to_paths("i18n_test");
254 PBD::get_files (i18n_files, spath);
256 string output_dir = test_output_directory (test_dir);
258 CPPUNIT_ASSERT (test_search_path().size () != 0);
260 string test_dir_path = test_search_path()[0];
263 cerr << "Copying " << test_files.size() << " test files from: "
264 << test_dir_path << " to " << output_dir << endl;
266 CPPUNIT_ASSERT (test_files.size() != 0);
268 PBD::copy_files (test_dir_path, output_dir);
270 vector<string> copied_files;
272 PBD::get_files (copied_files, output_dir);
274 CPPUNIT_ASSERT (copied_files.size() == test_files.size());
276 string subdir_path = Glib::build_filename (output_dir, "subdir");
278 CPPUNIT_ASSERT (g_mkdir_with_parents (subdir_path.c_str(), 0755) == 0);
281 cerr << "Copying " << i18n_files.size() << " i18n test files to: "
282 << subdir_path << endl;
284 for (vector<string>::iterator i = i18n_files.begin(); i != i18n_files.end(); ++i) {
285 string input_filepath = *i;
286 string output_filename = Glib::path_get_basename(*i);
287 string output_filepath = Glib::build_filename (subdir_path, output_filename);
289 CPPUNIT_ASSERT (PBD::copy_file (input_filepath, output_filepath));
292 copied_files.clear();
293 PBD::get_files (copied_files, subdir_path);
295 CPPUNIT_ASSERT (copied_files.size() == i18n_files.size());
301 FilesystemTest::testClearDirectory ()
303 string output_dir_path = create_test_directory ("ClearDirectory");
305 vector<string> files_in_output_dir;
307 PBD::get_paths (files_in_output_dir, output_dir_path, true, true);
309 size_t removed_file_size = 0;
310 vector<string> removed_files;
312 CPPUNIT_ASSERT (PBD::clear_directory (output_dir_path, &removed_file_size, &removed_files) ==0);
314 cerr << "Removed " << removed_files.size() << " files of total size: "
315 << removed_file_size << endl;
317 CPPUNIT_ASSERT (removed_files.size () == files_in_output_dir.size ());
319 string subdir_path = Glib::build_filename (output_dir_path, "subdir");
321 // make sure the directory structure is still there
322 CPPUNIT_ASSERT (Glib::file_test (subdir_path, Glib::FILE_TEST_IS_DIR));
326 FilesystemTest::testRemoveDirectory ()
328 string output_dir_path = create_test_directory ("RemoveDirectory");
330 vector<string> files_in_output_dir;
332 PBD::get_paths (files_in_output_dir, output_dir_path, false, true);
334 CPPUNIT_ASSERT (files_in_output_dir.size () != 0);
336 PBD::remove_directory (output_dir_path);
338 CPPUNIT_ASSERT (!Glib::file_test (output_dir_path, Glib::FILE_TEST_EXISTS));
342 FilesystemTest::testCanonicalPath ()
344 #ifndef PLATFORM_WINDOWS
345 string top_dir = test_output_directory ("testCanonicalPath");
346 PwdReset pwd_reset(top_dir);
348 string pwd = Glib::get_current_dir ();
350 CPPUNIT_ASSERT (!pwd.empty());
351 CPPUNIT_ASSERT (pwd == top_dir);
353 CPPUNIT_ASSERT (g_mkdir ("gtk2_ardour", 0755) == 0);
354 CPPUNIT_ASSERT (g_mkdir_with_parents ("libs/pbd/test", 0755) == 0);
356 const char* relative_path = "./gtk2_ardour/../libs/pbd/test";
357 string canonical_path = PBD::canonical_path (relative_path);
358 // no expansion expected in this case
359 string expanded_path = PBD::path_expand (relative_path);
360 string expected_path = top_dir + string("/libs/pbd/test");
362 CPPUNIT_ASSERT (canonical_path == expected_path);
363 CPPUNIT_ASSERT (expanded_path == expected_path);
368 FilesystemTest::testTouchFile ()
370 const string filename = "touch.me";
372 const string test_dir = test_output_directory ("testTouchFile");
373 const string touch_file_path = Glib::build_filename (test_dir, filename);
375 CPPUNIT_ASSERT (touch_file(touch_file_path));
377 CPPUNIT_ASSERT (Glib::file_test (touch_file_path, Glib::FILE_TEST_EXISTS));
381 FilesystemTest::testStatFile ()
383 const string filename1 = "touch.me";
384 const string filename2 = "touch.me.2";
386 const string test_dir = test_output_directory ("testStatFile");
388 const string path1 = Glib::build_filename (test_dir, filename1);
389 const string path2 = Glib::build_filename (test_dir, filename2);
391 CPPUNIT_ASSERT (touch_file(path1));
393 Glib::usleep (2000000);
395 CPPUNIT_ASSERT (touch_file(path2));
400 CPPUNIT_ASSERT (g_stat (path1.c_str(), &gsb1) == 0);
401 CPPUNIT_ASSERT (g_stat (path2.c_str(), &gsb2) == 0);
404 cerr << "StatFile: " << path1 << " access time: " << gsb1.st_atime << endl;
405 cerr << "StatFile: " << path1 << " modification time: " << gsb1.st_mtime << endl;
406 cerr << "StatFile: " << path2 << " access time: " << gsb2.st_atime << endl;
407 cerr << "StatFile: " << path2 << " modification time: " << gsb2.st_mtime << endl;
409 CPPUNIT_ASSERT (gsb1.st_atime == gsb1.st_mtime);
410 CPPUNIT_ASSERT (gsb2.st_atime == gsb2.st_mtime);
412 // at least access time works on windows(or at least on ntfs)
413 CPPUNIT_ASSERT (gsb1.st_atime < gsb2.st_atime);
414 CPPUNIT_ASSERT (gsb1.st_mtime < gsb2.st_mtime);
418 tbuf.actime = gsb1.st_atime;
419 tbuf.modtime = gsb1.st_mtime;
421 // update the file access/modification times to be the same
422 CPPUNIT_ASSERT (g_utime (path2.c_str(), &tbuf) == 0);
424 CPPUNIT_ASSERT (g_stat (path2.c_str(), &gsb2) == 0);
427 cerr << "StatFile: " << path1 << " access time: " << gsb1.st_atime << endl;
428 cerr << "StatFile: " << path1 << " modification time: " << gsb1.st_mtime << endl;
429 cerr << "StatFile: " << path2 << " access time: " << gsb2.st_atime << endl;
430 cerr << "StatFile: " << path2 << " modification time: " << gsb2.st_mtime << endl;
432 CPPUNIT_ASSERT (gsb1.st_atime == gsb2.st_atime);
433 CPPUNIT_ASSERT (gsb1.st_mtime == gsb2.st_mtime);