blob: 306fd2073dd3ee0fec71f597ee4c5372816ed999 [file] [edit]
//===----------------------------------------------------------------------===//
//
// 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));
}