| //===----------------------------------------------------------------------===// |
| // |
| // 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); |
| } |