Skip to content

Commit 02b1cc9

Browse files
committed
Add even more tests for canonicalize_path
Also fix a bug on macOS where /path/to/file/ didn't give an error.
1 parent e7db3e0 commit 02b1cc9

File tree

2 files changed

+140
-0
lines changed

2 files changed

+140
-0
lines changed

src/file-canonical.cpp

+12
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,18 @@ canonical_path_result canonicalize_path(const char *path) {
6464
}
6565
return canonical_path_result(canonical.string().c_str());
6666
#elif QLJS_HAVE_REALPATH
67+
#if defined(__APPLE__)
68+
{
69+
// macOS' realpath doesn't catch some errors. Catch them with another API.
70+
struct stat s;
71+
int rc = ::stat(path, &s);
72+
if (rc == -1) {
73+
return canonical_path_result::failure(
74+
std::string("failed to canonicalize path ") + path + ": " +
75+
std::strerror(errno));
76+
}
77+
}
78+
#endif
6779
char *allocated_canonical = ::realpath(path, nullptr);
6880
if (!allocated_canonical) {
6981
return canonical_path_result::failure(

test/test-file.cpp

+128
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,12 @@
3434
#include <sys/types.h>
3535
#endif
3636

37+
#if defined(_WIN32)
38+
#define QLJS_FILE_PATH_ALLOWS_FOLLOWING_COMPONENTS 1
39+
#else
40+
#define QLJS_FILE_PATH_ALLOWS_FOLLOWING_COMPONENTS 0
41+
#endif
42+
3743
using ::testing::AnyOf;
3844
using ::testing::HasSubstr;
3945
using ::testing::Not;
@@ -255,6 +261,21 @@ TEST_F(test_file, canonical_path_to_directory_removes_trailing_slash) {
255261
EXPECT_FALSE(ends_with(canonical.path(), "\\"));
256262
}
257263

264+
TEST_F(test_file, canonical_path_to_file_with_trailing_slash_fails) {
265+
std::string temp_dir = this->make_temporary_directory();
266+
write_file(temp_dir + "/file.txt", u8"");
267+
268+
std::string input_path = temp_dir + "/file.txt/";
269+
canonical_path_result canonical = canonicalize_path(input_path);
270+
EXPECT_FALSE(canonical.ok());
271+
std::string error = std::move(canonical).error();
272+
EXPECT_THAT(error, HasSubstr("file.txt"));
273+
EXPECT_THAT(error, AnyOf(HasSubstr("Not a directory"),
274+
// TODO(strager): Improve error message.
275+
HasSubstr("The filename, directory name, or volume "
276+
"label syntax is incorrect")));
277+
}
278+
258279
TEST_F(test_file, canonical_path_to_non_existing_file_fails) {
259280
std::string temp_file_path =
260281
this->make_temporary_directory() + "/does-not-exist.js";
@@ -293,6 +314,113 @@ TEST_F(test_file, canonical_path_removes_dot_components) {
293314
EXPECT_SAME_FILE(canonical.path(), input_path);
294315
}
295316

317+
TEST_F(test_file, canonical_path_removes_trailing_dot_component) {
318+
std::string temp_dir = this->make_temporary_directory();
319+
320+
std::string input_path = temp_dir + "/.";
321+
canonical_path_result canonical = canonicalize_path(input_path);
322+
ASSERT_TRUE(canonical.ok()) << std::move(canonical).error();
323+
324+
EXPECT_FALSE(ends_with(canonical.path(), "/.")) << canonical.path();
325+
EXPECT_FALSE(ends_with(canonical.path(), "\\.")) << canonical.path();
326+
EXPECT_SAME_FILE(canonical.path(), temp_dir);
327+
}
328+
329+
TEST_F(test_file, canonical_path_fails_with_dot_component_after_regular_file) {
330+
std::string temp_dir = this->make_temporary_directory();
331+
write_file(temp_dir + "/just-a-file", u8"");
332+
333+
std::string input_path = temp_dir + "/just-a-file/./something";
334+
canonical_path_result canonical = canonicalize_path(input_path);
335+
EXPECT_FALSE(canonical.ok());
336+
std::string error = std::move(canonical).error();
337+
EXPECT_THAT(error, HasSubstr("just-a-file"));
338+
EXPECT_THAT(error,
339+
AnyOf(HasSubstr("Not a directory"),
340+
HasSubstr("The system cannot find the path specified")));
341+
}
342+
343+
// TODO(strager): This test is wrong if
344+
// QLJS_FILE_PATH_ALLOWS_FOLLOWING_COMPONENTS.
345+
TEST_F(test_file,
346+
canonical_path_fails_with_dot_dot_component_after_regular_file) {
347+
std::string temp_dir = this->make_temporary_directory();
348+
write_file(temp_dir + "/just-a-file", u8"");
349+
write_file(temp_dir + "/other.txt", u8"");
350+
351+
std::string input_path = temp_dir + "/just-a-file/../other.text";
352+
canonical_path_result canonical = canonicalize_path(input_path);
353+
EXPECT_FALSE(canonical.ok());
354+
std::string error = std::move(canonical).error();
355+
EXPECT_THAT(error, HasSubstr("just-a-file"));
356+
EXPECT_THAT(error,
357+
AnyOf(HasSubstr("Not a directory"),
358+
HasSubstr("No such file or directory"),
359+
HasSubstr("The system cannot find the file specified")));
360+
}
361+
362+
#if QLJS_FILE_PATH_ALLOWS_FOLLOWING_COMPONENTS
363+
TEST_F(test_file,
364+
canonical_path_with_component_and_dot_dot_after_regular_file) {
365+
std::string temp_dir = this->make_temporary_directory();
366+
write_file(temp_dir + "/just-a-file", u8"");
367+
368+
std::string input_path = temp_dir + "/just-a-file/fake-subdir/..";
369+
canonical_path_result canonical = canonicalize_path(input_path);
370+
ASSERT_TRUE(canonical.ok()) << std::move(canonical).error();
371+
372+
EXPECT_FALSE(ends_with(canonical.path(), "/..")) << canonical.path();
373+
EXPECT_FALSE(ends_with(canonical.path(), "\\..")) << canonical.path();
374+
EXPECT_THAT(std::string(canonical.path()), Not(HasSubstr("fake-subdir")));
375+
EXPECT_SAME_FILE(canonical.path(), temp_dir + "/just-a-file");
376+
}
377+
#else
378+
TEST_F(test_file,
379+
canonical_path_fails_with_component_and_dot_dot_after_regular_file) {
380+
std::string temp_dir = this->make_temporary_directory();
381+
write_file(temp_dir + "/just-a-file", u8"");
382+
383+
std::string input_path = temp_dir + "/just-a-file/fake-subdir/..";
384+
canonical_path_result canonical = canonicalize_path(input_path);
385+
EXPECT_FALSE(canonical.ok());
386+
std::string error = std::move(canonical).error();
387+
EXPECT_THAT(error, HasSubstr("just-a-file"));
388+
EXPECT_THAT(error,
389+
AnyOf(HasSubstr("Not a directory"),
390+
HasSubstr("The system cannot find the path specified")));
391+
}
392+
#endif
393+
394+
#if QLJS_FILE_PATH_ALLOWS_FOLLOWING_COMPONENTS
395+
TEST_F(test_file, canonical_path_with_dot_after_regular_file) {
396+
std::string temp_dir = this->make_temporary_directory();
397+
write_file(temp_dir + "/just-a-file", u8"");
398+
399+
std::string input_path = temp_dir + "/just-a-file/.";
400+
canonical_path_result canonical = canonicalize_path(input_path);
401+
ASSERT_TRUE(canonical.ok()) << std::move(canonical).error();
402+
403+
EXPECT_FALSE(ends_with(canonical.path(), "/.")) << canonical.path();
404+
EXPECT_FALSE(ends_with(canonical.path(), "\\.")) << canonical.path();
405+
EXPECT_SAME_FILE(canonical.path(), temp_dir + "/just-a-file");
406+
}
407+
#else
408+
TEST_F(test_file,
409+
canonical_path_fails_with_trailing_dot_component_for_regular_file) {
410+
std::string temp_dir = this->make_temporary_directory();
411+
write_file(temp_dir + "/just-a-file", u8"");
412+
413+
std::string input_path = temp_dir + "/just-a-file/.";
414+
canonical_path_result canonical = canonicalize_path(input_path);
415+
EXPECT_FALSE(canonical.ok());
416+
std::string error = std::move(canonical).error();
417+
EXPECT_THAT(error, HasSubstr("just-a-file"));
418+
EXPECT_THAT(error,
419+
AnyOf(HasSubstr("Not a directory"),
420+
HasSubstr("The system cannot find the path specified")));
421+
}
422+
#endif
423+
296424
TEST_F(test_file, canonical_path_removes_redundant_slashes) {
297425
std::string temp_dir = this->make_temporary_directory();
298426
write_file(temp_dir + "/temp.js", u8"");

0 commit comments

Comments
 (0)