| //===----------------------------------------------------------------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| /// |
| /// \file |
| /// Tests for mkdtemp |
| /// See: https://pubs.opengroup.org/onlinepubs/9799919799/functions/mkdtemp.html |
| /// |
| //===----------------------------------------------------------------------===// |
| |
| #include "hdr/errno_macros.h" |
| #include "hdr/sys_stat_macros.h" |
| #include "hdr/types/struct_stat.h" |
| #include "hdr/unistd_macros.h" |
| #include "src/__support/CPP/scope.h" |
| #include "src/__support/CPP/string_view.h" |
| #include "src/stdlib/mkdtemp.h" |
| #include "src/string/strdup.h" |
| #include "src/string/strlen.h" |
| #include "src/sys/stat/stat.h" |
| #include "src/unistd/access.h" |
| #include "src/unistd/rmdir.h" |
| #include "test/UnitTest/ErrnoCheckingTest.h" |
| #include "test/UnitTest/ErrnoSetterMatcher.h" |
| #include "test/UnitTest/Test.h" |
| |
| using namespace LIBC_NAMESPACE::testing::ErrnoSetterMatcher; |
| using LIBC_NAMESPACE::cpp::string_view; |
| using LlvmLibcMkdtempTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest; |
| |
| TEST_F(LlvmLibcMkdtempTest, ValidTemplate) { |
| char *tmpl = LIBC_NAMESPACE::strdup(libc_make_test_file_path("tmp_XXXXXX")); |
| ASSERT_NE(tmpl, nullptr); |
| auto cleanup = LIBC_NAMESPACE::cpp::scope_exit([&] { |
| LIBC_NAMESPACE::rmdir(tmpl); |
| ::free(tmpl); |
| }); |
| |
| ASSERT_THAT(LIBC_NAMESPACE::mkdtemp(tmpl), Succeeds(tmpl)); |
| EXPECT_THAT(LIBC_NAMESPACE::access(tmpl, F_OK), Succeeds(0)); |
| |
| struct stat st; |
| ASSERT_THAT(LIBC_NAMESPACE::stat(tmpl, &st), Succeeds(0)); |
| EXPECT_EQ(st.st_mode & S_IFMT, static_cast<mode_t>(S_IFDIR)); |
| EXPECT_EQ(st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO), |
| static_cast<mode_t>(S_IRWXU)); |
| } |
| |
| TEST_F(LlvmLibcMkdtempTest, TemplateModifiedInPlace) { |
| char *tmpl = LIBC_NAMESPACE::strdup(libc_make_test_file_path("tmp_XXXXXX")); |
| ASSERT_NE(tmpl, nullptr); |
| auto cleanup_tmpl = LIBC_NAMESPACE::cpp::scope_exit([&] { |
| LIBC_NAMESPACE::rmdir(tmpl); |
| ::free(tmpl); |
| }); |
| |
| char *orig = LIBC_NAMESPACE::strdup(tmpl); |
| ASSERT_NE(orig, nullptr); |
| auto cleanup_orig = LIBC_NAMESPACE::cpp::scope_exit([&] { ::free(orig); }); |
| |
| size_t len = LIBC_NAMESPACE::strlen(tmpl); |
| ASSERT_THAT(LIBC_NAMESPACE::mkdtemp(tmpl), Succeeds(tmpl)); |
| |
| EXPECT_EQ(string_view(tmpl, len - 6), string_view(orig, len - 6)); |
| EXPECT_NE(string_view(tmpl + len - 6, 6), string_view("XXXXXX")); |
| } |
| |
| TEST_F(LlvmLibcMkdtempTest, AllCharactersInCharset) { |
| char *tmpl = LIBC_NAMESPACE::strdup(libc_make_test_file_path("tmp_XXXXXX")); |
| ASSERT_NE(tmpl, nullptr); |
| auto cleanup = LIBC_NAMESPACE::cpp::scope_exit([&] { |
| LIBC_NAMESPACE::rmdir(tmpl); |
| ::free(tmpl); |
| }); |
| |
| size_t len = LIBC_NAMESPACE::strlen(tmpl); |
| ASSERT_THAT(LIBC_NAMESPACE::mkdtemp(tmpl), Succeeds(tmpl)); |
| |
| // POSIX portable filename character set, sorted by ASCII value. |
| // See |
| // https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/V1_chap03.html#tag_03_265 |
| constexpr string_view CHARSET = "-._0123456789" |
| "ABCDEFGHIJKLMNOPQRSTUVWXYZ" |
| "abcdefghijklmnopqrstuvwxyz"; |
| for (char c : string_view(tmpl + len - 6, 6)) |
| EXPECT_NE(CHARSET.find_first_of(c), string_view::npos); |
| } |
| |
| TEST_F(LlvmLibcMkdtempTest, Uniqueness) { |
| char *tmpl1 = LIBC_NAMESPACE::strdup(libc_make_test_file_path("tmp_XXXXXX")); |
| ASSERT_NE(tmpl1, nullptr); |
| auto cleanup1 = LIBC_NAMESPACE::cpp::scope_exit([&] { |
| LIBC_NAMESPACE::rmdir(tmpl1); |
| ::free(tmpl1); |
| }); |
| |
| char *tmpl2 = LIBC_NAMESPACE::strdup(libc_make_test_file_path("tmp_XXXXXX")); |
| ASSERT_NE(tmpl2, nullptr); |
| auto cleanup2 = LIBC_NAMESPACE::cpp::scope_exit([&] { |
| LIBC_NAMESPACE::rmdir(tmpl2); |
| ::free(tmpl2); |
| }); |
| |
| ASSERT_THAT(LIBC_NAMESPACE::mkdtemp(tmpl1), Succeeds(tmpl1)); |
| ASSERT_THAT(LIBC_NAMESPACE::mkdtemp(tmpl2), Succeeds(tmpl2)); |
| |
| EXPECT_STRNE(tmpl1, tmpl2); |
| } |
| |
| TEST_F(LlvmLibcMkdtempTest, SixXsNoPrefix) { |
| char *tmpl = LIBC_NAMESPACE::strdup(libc_make_test_file_path("XXXXXX")); |
| ASSERT_NE(tmpl, nullptr); |
| auto cleanup = LIBC_NAMESPACE::cpp::scope_exit([&] { |
| LIBC_NAMESPACE::rmdir(tmpl); |
| ::free(tmpl); |
| }); |
| |
| ASSERT_THAT(LIBC_NAMESPACE::mkdtemp(tmpl), Succeeds(tmpl)); |
| EXPECT_THAT(LIBC_NAMESPACE::access(tmpl, F_OK), Succeeds(0)); |
| } |
| |
| TEST_F(LlvmLibcMkdtempTest, MoreThanSixXs) { |
| char *tmpl = |
| LIBC_NAMESPACE::strdup(libc_make_test_file_path("tmp_XXXXXXXXXX")); |
| ASSERT_NE(tmpl, nullptr); |
| auto cleanup_tmpl = LIBC_NAMESPACE::cpp::scope_exit([&] { |
| LIBC_NAMESPACE::rmdir(tmpl); |
| ::free(tmpl); |
| }); |
| |
| char *orig = LIBC_NAMESPACE::strdup(tmpl); |
| ASSERT_NE(orig, nullptr); |
| auto cleanup_orig = LIBC_NAMESPACE::cpp::scope_exit([&] { ::free(orig); }); |
| |
| size_t len = LIBC_NAMESPACE::strlen(tmpl); |
| ASSERT_THAT(LIBC_NAMESPACE::mkdtemp(tmpl), Succeeds(tmpl)); |
| |
| EXPECT_EQ(string_view(tmpl, len - 10), string_view(orig, len - 10)); |
| EXPECT_NE(string_view(tmpl + len - 10, 10), string_view("XXXXXXXXXX")); |
| EXPECT_THAT(LIBC_NAMESPACE::access(tmpl, F_OK), Succeeds(0)); |
| } |
| |
| #if defined(LIBC_ADD_NULL_CHECKS) |
| TEST_F(LlvmLibcMkdtempTest, NullPointer) { |
| ASSERT_DEATH([] { LIBC_NAMESPACE::mkdtemp(nullptr); }, WITH_SIGNAL(-1)); |
| } |
| #endif |
| |
| TEST_F(LlvmLibcMkdtempTest, TemplateTooShort) { |
| char tmpl[] = "XXXXX"; |
| EXPECT_THAT(LIBC_NAMESPACE::mkdtemp(tmpl), Fails<char *>(EINVAL, nullptr)); |
| } |
| |
| TEST_F(LlvmLibcMkdtempTest, DoesNotEndInXs) { |
| char tmpl[] = "tmp_XXXXXY"; |
| EXPECT_THAT(LIBC_NAMESPACE::mkdtemp(tmpl), Fails<char *>(EINVAL, nullptr)); |
| } |
| |
| TEST_F(LlvmLibcMkdtempTest, XsNotAtEnd) { |
| char tmpl[] = "XXXXXXtmp"; |
| EXPECT_THAT(LIBC_NAMESPACE::mkdtemp(tmpl), Fails<char *>(EINVAL, nullptr)); |
| } |
| |
| TEST_F(LlvmLibcMkdtempTest, FiveXsAtEnd) { |
| char tmpl[] = "tmp_XXXXX"; |
| EXPECT_THAT(LIBC_NAMESPACE::mkdtemp(tmpl), Fails<char *>(EINVAL, nullptr)); |
| } |
| |
| TEST_F(LlvmLibcMkdtempTest, EmptyString) { |
| char tmpl[] = ""; |
| EXPECT_THAT(LIBC_NAMESPACE::mkdtemp(tmpl), Fails<char *>(EINVAL, nullptr)); |
| } |
| |
| TEST_F(LlvmLibcMkdtempTest, NonExistentParentDirectory) { |
| char *tmpl = LIBC_NAMESPACE::strdup( |
| libc_make_test_file_path("non_existent_dir/tmp_XXXXXX")); |
| ASSERT_NE(tmpl, nullptr); |
| auto cleanup = LIBC_NAMESPACE::cpp::scope_exit([&] { ::free(tmpl); }); |
| EXPECT_THAT(LIBC_NAMESPACE::mkdtemp(tmpl), Fails<char *>(ENOENT, nullptr)); |
| } |