blob: 09b472681fdf6c503fbdced226fd845ab7bd1428 [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 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);
}