| //===----------------------------------------------------------------------===// | |
| // | |
| // 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 mkstemp | |
| /// See: https://pubs.opengroup.org/onlinepubs/9799919799/functions/mkdtemp.html | |
| /// | |
| //===----------------------------------------------------------------------===// | |
| #include "hdr/errno_macros.h" | |
| #include "hdr/fcntl_macros.h" | |
| #include "hdr/signal_macros.h" | |
| #include "src/stdlib/mkstemp.h" | |
| #include "src/string/strdup.h" | |
| #include "src/string/strlen.h" | |
| #include "src/unistd/access.h" | |
| #include "src/unistd/close.h" | |
| #include "src/unistd/read.h" | |
| #include "src/unistd/unlink.h" | |
| #include "src/unistd/write.h" | |
| #include "test/UnitTest/ErrnoCheckingTest.h" | |
| #include "test/UnitTest/Test.h" | |
| using LlvmLibcMkstempTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest; | |
| TEST_F(LlvmLibcMkstempTest, ValidTemplate) { | |
| char *tmpl = LIBC_NAMESPACE::strdup(libc_make_test_file_path("tmp_XXXXXX")); | |
| int fd = LIBC_NAMESPACE::mkstemp(tmpl); | |
| ASSERT_GE(fd, 0); | |
| LIBC_NAMESPACE::close(fd); | |
| LIBC_NAMESPACE::unlink(tmpl); | |
| ::free(tmpl); | |
| } | |
| TEST_F(LlvmLibcMkstempTest, TemplateModifiedInPlace) { | |
| char *tmpl = LIBC_NAMESPACE::strdup(libc_make_test_file_path("tmp_XXXXXX")); | |
| size_t len = LIBC_NAMESPACE::strlen(tmpl); | |
| size_t count = 0; | |
| for (size_t i = len; i > 0 && tmpl[i - 1] == 'X'; i--) | |
| count++; | |
| int fd = LIBC_NAMESPACE::mkstemp(tmpl); | |
| ASSERT_GE(fd, 0); | |
| bool modified = false; | |
| for (size_t i = len - count; i < len; i++) | |
| if (tmpl[i] != 'X') { | |
| modified = true; | |
| break; | |
| } | |
| EXPECT_TRUE(modified); | |
| LIBC_NAMESPACE::close(fd); | |
| LIBC_NAMESPACE::unlink(tmpl); | |
| ::free(tmpl); | |
| } | |
| TEST_F(LlvmLibcMkstempTest, AllCharactersInCharset) { | |
| char *tmpl = LIBC_NAMESPACE::strdup(libc_make_test_file_path("tmp_XXXXXX")); | |
| size_t len = LIBC_NAMESPACE::strlen(tmpl); | |
| size_t count = 0; | |
| for (size_t i = len; i > 0 && tmpl[i - 1] == 'X'; i--) | |
| count++; | |
| int fd = LIBC_NAMESPACE::mkstemp(tmpl); | |
| ASSERT_GE(fd, 0); | |
| // POSIX portable filename character set, sorted by ASCII value. | |
| // See | |
| // https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/V1_chap03.html#tag_03_265 | |
| const char charset[] = "-._0123456789" | |
| "ABCDEFGHIJKLMNOPQRSTUVWXYZ" | |
| "abcdefghijklmnopqrstuvwxyz"; | |
| for (size_t i = len - count; i < len; i++) { | |
| bool found = false; | |
| for (size_t j = 0; j < sizeof(charset) - 1; j++) { | |
| if (tmpl[i] == charset[j]) { | |
| found = true; | |
| break; | |
| } | |
| } | |
| EXPECT_TRUE(found); | |
| } | |
| LIBC_NAMESPACE::close(fd); | |
| LIBC_NAMESPACE::unlink(tmpl); | |
| ::free(tmpl); | |
| } | |
| TEST_F(LlvmLibcMkstempTest, FileOpenedCorrectly) { | |
| char *tmpl = LIBC_NAMESPACE::strdup(libc_make_test_file_path("tmp_XXXXXX")); | |
| int fd = LIBC_NAMESPACE::mkstemp(tmpl); | |
| ASSERT_GE(fd, 0); | |
| // Check for two things: | |
| // 1. The file exists on the disk | |
| EXPECT_EQ(LIBC_NAMESPACE::access(tmpl, F_OK), 0); | |
| // 2. The file can be written to | |
| EXPECT_EQ(LIBC_NAMESPACE::write(fd, "llvm", 4), static_cast<ssize_t>(4)); | |
| LIBC_NAMESPACE::close(fd); | |
| LIBC_NAMESPACE::unlink(tmpl); | |
| ::free(tmpl); | |
| } | |
| TEST_F(LlvmLibcMkstempTest, Uniqueness) { | |
| char *tmpl1 = LIBC_NAMESPACE::strdup(libc_make_test_file_path("tmp_XXXXXX")); | |
| char *tmpl2 = LIBC_NAMESPACE::strdup(libc_make_test_file_path("tmp_XXXXXX")); | |
| int fd1 = LIBC_NAMESPACE::mkstemp(tmpl1); | |
| int fd2 = LIBC_NAMESPACE::mkstemp(tmpl2); | |
| ASSERT_GE(fd1, 0); | |
| ASSERT_GE(fd2, 0); | |
| bool different = false; | |
| for (size_t i = 0; tmpl1[i] != '\0'; i++) | |
| if (tmpl1[i] != tmpl2[i]) { | |
| different = true; | |
| break; | |
| } | |
| EXPECT_TRUE(different); | |
| LIBC_NAMESPACE::close(fd1); | |
| LIBC_NAMESPACE::close(fd2); | |
| LIBC_NAMESPACE::unlink(tmpl1); | |
| LIBC_NAMESPACE::unlink(tmpl2); | |
| ::free(tmpl1); | |
| ::free(tmpl2); | |
| } | |
| TEST_F(LlvmLibcMkstempTest, FileIsEmpty) { | |
| char *tmpl = LIBC_NAMESPACE::strdup(libc_make_test_file_path("tmp_XXXXXX")); | |
| int fd = LIBC_NAMESPACE::mkstemp(tmpl); | |
| ASSERT_GE(fd, 0); | |
| // read should return 0 on empty file | |
| char buf[1]; | |
| EXPECT_EQ(LIBC_NAMESPACE::read(fd, buf, 1), static_cast<ssize_t>(0)); | |
| LIBC_NAMESPACE::close(fd); | |
| LIBC_NAMESPACE::unlink(tmpl); | |
| ::free(tmpl); | |
| } | |
| TEST_F(LlvmLibcMkstempTest, SixXsNoPrefix) { | |
| char *tmpl = LIBC_NAMESPACE::strdup(libc_make_test_file_path("XXXXXX")); | |
| int fd = LIBC_NAMESPACE::mkstemp(tmpl); | |
| ASSERT_GE(fd, 0); | |
| LIBC_NAMESPACE::close(fd); | |
| LIBC_NAMESPACE::unlink(tmpl); | |
| ::free(tmpl); | |
| } | |
| TEST_F(LlvmLibcMkstempTest, MoreThanSixXs) { | |
| char *tmpl = | |
| LIBC_NAMESPACE::strdup(libc_make_test_file_path("tmp_XXXXXXXXXX")); | |
| size_t len = LIBC_NAMESPACE::strlen(tmpl); | |
| size_t count = 0; | |
| for (size_t i = len; i > 0 && tmpl[i - 1] == 'X'; i--) | |
| count++; | |
| int fd = LIBC_NAMESPACE::mkstemp(tmpl); | |
| ASSERT_GE(fd, 0); | |
| bool modified = false; | |
| for (size_t i = len - count; i < len; i++) | |
| if (tmpl[i] != 'X') { | |
| modified = true; | |
| break; | |
| } | |
| EXPECT_TRUE(modified); | |
| EXPECT_EQ(LIBC_NAMESPACE::access(tmpl, F_OK), 0); | |
| LIBC_NAMESPACE::close(fd); | |
| LIBC_NAMESPACE::unlink(tmpl); | |
| ::free(tmpl); | |
| } | |
| #if defined(LIBC_ADD_NULL_CHECKS) | |
| TEST_F(LlvmLibcMkstempTest, NullPointer) { | |
| EXPECT_DEATH([] { LIBC_NAMESPACE::mkstemp(nullptr); }, WITH_SIGNAL(-1)); | |
| } | |
| #endif | |
| TEST_F(LlvmLibcMkstempTest, TemplateTooShort) { | |
| char tmpl[] = "XXXXX"; | |
| int fd = LIBC_NAMESPACE::mkstemp(tmpl); | |
| EXPECT_EQ(fd, -1); | |
| ASSERT_ERRNO_EQ(EINVAL); | |
| } | |
| TEST_F(LlvmLibcMkstempTest, DoesNotEndInXs) { | |
| char tmpl[] = "tmp_XXXXXY"; | |
| int fd = LIBC_NAMESPACE::mkstemp(tmpl); | |
| EXPECT_EQ(fd, -1); | |
| ASSERT_ERRNO_EQ(EINVAL); | |
| } | |
| TEST_F(LlvmLibcMkstempTest, XsNotAtEnd) { | |
| char tmpl[] = "XXXXXXtmp"; | |
| int fd = LIBC_NAMESPACE::mkstemp(tmpl); | |
| EXPECT_EQ(fd, -1); | |
| ASSERT_ERRNO_EQ(EINVAL); | |
| } | |
| TEST_F(LlvmLibcMkstempTest, FiveXsAtEnd) { | |
| char tmpl[] = "tmp_XXXXX"; | |
| int fd = LIBC_NAMESPACE::mkstemp(tmpl); | |
| EXPECT_EQ(fd, -1); | |
| ASSERT_ERRNO_EQ(EINVAL); | |
| } | |
| TEST_F(LlvmLibcMkstempTest, EmptyString) { | |
| char tmpl[] = ""; | |
| int fd = LIBC_NAMESPACE::mkstemp(tmpl); | |
| EXPECT_EQ(fd, -1); | |
| ASSERT_ERRNO_EQ(EINVAL); | |
| } |