blob: 80b1a5b7bd3af1544091c663c2f6340359d06ffc [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 fputwc
///
//===----------------------------------------------------------------------===//
#include "hdr/errno_macros.h"
#include "hdr/stdint_proxy.h"
#include "hdr/wchar_macros.h" // For WEOF
#include "src/stdio/fclose.h"
#include "src/stdio/ferror.h"
#include "src/stdio/fopen.h"
#include "src/stdio/fread.h"
#include "src/stdio/fwrite.h"
#include "src/wchar/fputwc.h"
#include "src/wchar/fwide.h"
#include "test/UnitTest/ErrnoCheckingTest.h"
#include "test/UnitTest/Test.h"
using LlvmLibcFputwcTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
TEST_F(LlvmLibcFputwcTest, WriteASCII) {
const auto FILENAME =
libc_make_test_file_path(APPEND_LIBC_TEST("fputwc_ascii.test"));
::FILE *file = LIBC_NAMESPACE::fopen(FILENAME, "w");
ASSERT_FALSE(file == nullptr);
// Write 'a', 'b', 'c'
EXPECT_EQ(LIBC_NAMESPACE::fputwc(L'a', file), static_cast<wint_t>(L'a'));
EXPECT_EQ(LIBC_NAMESPACE::fputwc(L'b', file), static_cast<wint_t>(L'b'));
EXPECT_EQ(LIBC_NAMESPACE::fputwc(L'c', file), static_cast<wint_t>(L'c'));
ASSERT_EQ(LIBC_NAMESPACE::fclose(file), 0);
// Open again to read raw bytes
file = LIBC_NAMESPACE::fopen(FILENAME, "r");
ASSERT_FALSE(file == nullptr);
char buffer[10] = {0};
ASSERT_EQ(LIBC_NAMESPACE::fread(buffer, 1, 3, file), size_t(3));
EXPECT_STREQ(buffer, "abc");
ASSERT_EQ(LIBC_NAMESPACE::fclose(file), 0);
}
TEST_F(LlvmLibcFputwcTest, WriteUtf8) {
const auto FILENAME =
libc_make_test_file_path(APPEND_LIBC_TEST("fputwc_utf8.test"));
::FILE *file = LIBC_NAMESPACE::fopen(FILENAME, "w");
ASSERT_FALSE(file == nullptr);
// 1-byte character: 'a' (L'a', 0x61) -> UTF-8: 0x61
EXPECT_EQ(LIBC_NAMESPACE::fputwc(L'a', file), static_cast<wint_t>(L'a'));
// 2-byte character: '¢' (L'¢', 0xA2) -> UTF-8: 0xC2 0xA2
EXPECT_EQ(LIBC_NAMESPACE::fputwc(L'¢', file), static_cast<wint_t>(L'¢'));
#if WCHAR_MAX > 0xFFFF
// 3-byte character: '€' (L'€', 0x20AC) -> UTF-8: 0xE2 0x82 0xAC
EXPECT_EQ(LIBC_NAMESPACE::fputwc(L'€', file), static_cast<wint_t>(L'€'));
// 4-byte character: '𐍈' (L'𐍈', 0x10348) -> UTF-8: 0xF0 0x90 0x8D 0x88
EXPECT_EQ(LIBC_NAMESPACE::fputwc(L'𐍈', file), static_cast<wint_t>(L'𐍈'));
#endif
ASSERT_EQ(LIBC_NAMESPACE::fclose(file), 0);
// Open again to read raw bytes
file = LIBC_NAMESPACE::fopen(FILENAME, "r");
ASSERT_FALSE(file == nullptr);
unsigned char buffer[15] = {0};
ASSERT_EQ(LIBC_NAMESPACE::fread(buffer, 1, 10, file), size_t(10));
// Verify 1-byte
EXPECT_EQ(buffer[0], static_cast<unsigned char>(0x61));
// Verify 2-byte
EXPECT_EQ(buffer[1], static_cast<unsigned char>(0xC2));
EXPECT_EQ(buffer[2], static_cast<unsigned char>(0xA2));
#if WCHAR_MAX > 0xFFFF
// Verify 3-byte
EXPECT_EQ(buffer[3], static_cast<unsigned char>(0xE2));
EXPECT_EQ(buffer[4], static_cast<unsigned char>(0x82));
EXPECT_EQ(buffer[5], static_cast<unsigned char>(0xAC));
// Verify 4-byte
EXPECT_EQ(buffer[6], static_cast<unsigned char>(0xF0));
EXPECT_EQ(buffer[7], static_cast<unsigned char>(0x90));
EXPECT_EQ(buffer[8], static_cast<unsigned char>(0x8D));
EXPECT_EQ(buffer[9], static_cast<unsigned char>(0x88));
#endif
ASSERT_EQ(LIBC_NAMESPACE::fclose(file), 0);
}
// For the character to be outside the unicode range it also needs to be outside
// the UTF-16 range.
#if WCHAR_MAX > 0xFFFF
TEST_F(LlvmLibcFputwcTest, EncodingErrorEILSEQ) {
const auto FILENAME =
libc_make_test_file_path(APPEND_LIBC_TEST("fputwc_eilseq.test"));
::FILE *file = LIBC_NAMESPACE::fopen(FILENAME, "w");
ASSERT_FALSE(file == nullptr);
// Try to write an invalid wide character point (outside Unicode range)
ASSERT_ERRNO_SUCCESS();
EXPECT_EQ(LIBC_NAMESPACE::fputwc(static_cast<wchar_t>(0x110000), file),
static_cast<wint_t>(WEOF));
ASSERT_ERRNO_EQ(EILSEQ);
EXPECT_NE(LIBC_NAMESPACE::ferror(file), 0);
ASSERT_EQ(LIBC_NAMESPACE::fclose(file), 0);
}
#endif
TEST_F(LlvmLibcFputwcTest, InvalidStream) {
const auto FILENAME =
libc_make_test_file_path(APPEND_LIBC_TEST("fputwc_invalid.test"));
// Create the file first
::FILE *file = LIBC_NAMESPACE::fopen(FILENAME, "w");
ASSERT_FALSE(file == nullptr);
ASSERT_EQ(LIBC_NAMESPACE::fclose(file), 0);
// Open in read-only mode
file = LIBC_NAMESPACE::fopen(FILENAME, "r");
ASSERT_FALSE(file == nullptr);
// Try to write to read-only file
ASSERT_ERRNO_SUCCESS();
EXPECT_EQ(LIBC_NAMESPACE::fputwc(L'x', file), static_cast<wint_t>(WEOF));
ASSERT_ERRNO_EQ(EBADF);
ASSERT_EQ(LIBC_NAMESPACE::fclose(file), 0);
}
TEST_F(LlvmLibcFputwcTest, ByteModeFailure) {
const auto FILENAME =
libc_make_test_file_path(APPEND_LIBC_TEST("fputwc_bytemode.test"));
::FILE *file = LIBC_NAMESPACE::fopen(FILENAME, "w");
ASSERT_FALSE(file == nullptr);
// Orient to byte mode
EXPECT_LT(LIBC_NAMESPACE::fwide(file, -1), 0);
// Writing wide char should fail and set errno to EINVAL
EXPECT_EQ(LIBC_NAMESPACE::fputwc(L'a', file), static_cast<wint_t>(WEOF));
ASSERT_ERRNO_EQ(EINVAL);
ASSERT_EQ(LIBC_NAMESPACE::fclose(file), 0);
}