blob: 9b70a51cd880ed76a5a2e99ad6c4237bec0b307f [file]
//===----------------------------------------------------------------------===//
//
// 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
/// Unittests for tmpnam
///
//===----------------------------------------------------------------------===//
#include "src/stdio/tmpnam.h"
#include "hdr/stdio_macros.h" // For L_tmpnam
#include "hdr/types/size_t.h"
#include "src/__support/CPP/string.h"
#include "src/__support/CPP/string_view.h"
#include "src/__support/macros/config.h"
#include "test/UnitTest/Test.h"
using LIBC_NAMESPACE::cpp::string;
using LIBC_NAMESPACE::cpp::string_view;
namespace {
constexpr string_view TMPDIR = "/tmp/";
// POSIX portable filename character set.
constexpr string_view ALLOWED = "0123456789"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"_.";
bool only_allowed_chars(string_view sv) {
for (char c : sv) {
if (!ALLOWED.contains(c))
return false;
}
return true;
}
} // namespace
// For caller-supplied buffer the return value should be
// the argument pointer
TEST(LlvmLibcTmpnamTest, NonNullBufferReturnsSamePointer) {
char buf[L_tmpnam];
char *result = LIBC_NAMESPACE::tmpnam(buf);
ASSERT_EQ(result, buf);
}
TEST(LlvmLibcTmpnamTest, NonNullBufferIsNullTerminated) {
char buf[L_tmpnam];
char *result = LIBC_NAMESPACE::tmpnam(buf);
ASSERT_NE(result, static_cast<char *>(nullptr));
string_view sv(result);
ASSERT_EQ(sv.size(), static_cast<size_t>(L_tmpnam - 1));
ASSERT_EQ(sv[L_tmpnam - 1], '\0');
}
TEST(LlvmLibcTmpnamTest, ResultUsesOnlyPortableChars) {
char buf[L_tmpnam];
char *result = LIBC_NAMESPACE::tmpnam(buf);
ASSERT_NE(result, static_cast<char *>(nullptr));
string_view sv(result);
ASSERT_TRUE(sv.starts_with(TMPDIR));
ASSERT_TRUE(only_allowed_chars(sv.substr(TMPDIR.size())));
}
// For null argument, the result lives in an internal static object.
TEST(LlvmLibcTmpnamTest, NullBufferReturnsInternalObject) {
char *result = LIBC_NAMESPACE::tmpnam(nullptr);
ASSERT_NE(result, static_cast<char *>(nullptr));
string_view sv(result);
ASSERT_TRUE(sv.starts_with(TMPDIR));
ASSERT_TRUE(only_allowed_chars(sv.substr(TMPDIR.size())));
}
// Successive calls should produce distinct strings.
TEST(LlvmLibcTmpnamTest, SuccessiveCallsDiffer) {
char a[L_tmpnam];
char b[L_tmpnam];
char *ra = LIBC_NAMESPACE::tmpnam(a);
char *rb = LIBC_NAMESPACE::tmpnam(b);
ASSERT_NE(ra, static_cast<char *>(nullptr));
ASSERT_NE(rb, static_cast<char *>(nullptr));
ASSERT_FALSE(string_view(ra) == string_view(rb));
}
// Two calls with a null argument must return the same
// pointer. The contents, however, are overwritten by
// the second call.
TEST(LlvmLibcTmpnamTest, NullCallsShareObjectButDifferInContent) {
char *first = LIBC_NAMESPACE::tmpnam(nullptr);
ASSERT_NE(first, static_cast<char *>(nullptr));
// Snapshot the first result before it is overwritten.
string snapshot(first);
char *second = LIBC_NAMESPACE::tmpnam(nullptr);
ASSERT_NE(second, static_cast<char *>(nullptr));
ASSERT_EQ(first, second);
string secondstr(second);
// But the generated string changed
ASSERT_FALSE(snapshot == secondstr);
}