blob: 4a940c7281b6e02696c28f25981099b7886b38a0 [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
/// Unittests for fgetws
///
//===----------------------------------------------------------------------===//
#include "hdr/errno_macros.h"
#include "hdr/stdint_proxy.h"
#include "hdr/types/wint_t.h"
#include "src/stdio/fclose.h"
#include "src/stdio/ferror.h"
#include "src/stdio/fopen.h"
#include "src/stdio/fwrite.h"
#include "src/wchar/fgetws.h"
#include "src/wchar/fwide.h"
#include "src/wchar/wcscmp.h"
#include "test/UnitTest/ErrnoCheckingTest.h"
#include "test/UnitTest/Test.h"
using LlvmLibcFgetwsTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
// TODO: Refactor these tests to use standard wide-character string comparison
// assert macros (e.g., EXPECT_STREQ for wchar_t) once they are supported
// natively by the LLVM-libc test framework, instead of calling wcscmp.
TEST_F(LlvmLibcFgetwsTest, ReadWideString) {
const auto FILENAME =
libc_make_test_file_path(APPEND_LIBC_TEST("fgetws_string.test"));
::FILE *file = LIBC_NAMESPACE::fopen(FILENAME, "w");
ASSERT_FALSE(file == nullptr);
#if WCHAR_MAX > 0xFFFF
// Write UTF-8 bytes for: "Hello, ¢€𐍈 world!\n"
constexpr unsigned char CONTENT[] = {
'H', 'e', 'l', 'l', 'o', ',', ' ', 0xC2, 0xA2, 0xE2, 0x82, 0xAC,
0xF0, 0x90, 0x8D, 0x88, ' ', 'w', 'o', 'r', 'l', 'd', '!', '\n'};
constexpr const wchar_t *EXPECTED_STR = L"Hello, ¢€𐍈 world!\n";
#else
// Write UTF-8 bytes for: "Hello, ¢ world!\n"
constexpr unsigned char CONTENT[] = {'H', 'e', 'l', 'l', 'o', ',',
' ', 0xC2, 0xA2, ' ', 'w', 'o',
'r', 'l', 'd', '!', '\n'};
constexpr const wchar_t *EXPECTED_STR = L"Hello, ¢ world!\n";
#endif
ASSERT_EQ(LIBC_NAMESPACE::fwrite(CONTENT, 1, sizeof(CONTENT), file),
sizeof(CONTENT));
ASSERT_EQ(LIBC_NAMESPACE::fclose(file), 0);
// Open for reading
file = LIBC_NAMESPACE::fopen(FILENAME, "r");
ASSERT_FALSE(file == nullptr);
wchar_t buffer[50] = {0};
wchar_t *result = LIBC_NAMESPACE::fgetws(buffer, 50, file);
ASSERT_FALSE(result == nullptr);
EXPECT_EQ(result, buffer);
EXPECT_EQ(LIBC_NAMESPACE::wcscmp(buffer, EXPECTED_STR), 0);
ASSERT_EQ(LIBC_NAMESPACE::fclose(file), 0);
}
TEST_F(LlvmLibcFgetwsTest, ReadBounded) {
const auto FILENAME =
libc_make_test_file_path(APPEND_LIBC_TEST("fgetws_bounded.test"));
::FILE *file = LIBC_NAMESPACE::fopen(FILENAME, "w");
ASSERT_FALSE(file == nullptr);
// Write "1234567890"
constexpr char CONTENT[] = "1234567890";
ASSERT_EQ(LIBC_NAMESPACE::fwrite(CONTENT, 1, sizeof(CONTENT) - 1, file),
sizeof(CONTENT) - 1);
ASSERT_EQ(LIBC_NAMESPACE::fclose(file), 0);
// Open for reading
file = LIBC_NAMESPACE::fopen(FILENAME, "r");
ASSERT_FALSE(file == nullptr);
wchar_t buffer[10] = {0};
// Read bounded by count = 5 (4 chars + null terminator)
wchar_t *result = LIBC_NAMESPACE::fgetws(buffer, 5, file);
ASSERT_FALSE(result == nullptr);
EXPECT_EQ(result, buffer);
EXPECT_EQ(LIBC_NAMESPACE::wcscmp(buffer, L"1234"), 0);
// Read bounded by count = 1 (writes only null terminator)
wchar_t buffer_one[5] = {L'x', L'y', L'z', L'\0'};
wchar_t *result_one = LIBC_NAMESPACE::fgetws(buffer_one, 1, file);
ASSERT_FALSE(result_one == nullptr);
EXPECT_EQ(result_one, buffer_one);
EXPECT_EQ(static_cast<wint_t>(buffer_one[0]), static_cast<wint_t>(L'\0'));
EXPECT_EQ(static_cast<wint_t>(buffer_one[1]),
static_cast<wint_t>(L'y')); // untouched
ASSERT_EQ(LIBC_NAMESPACE::fclose(file), 0);
}
TEST_F(LlvmLibcFgetwsTest, NewlineStops) {
const auto FILENAME =
libc_make_test_file_path(APPEND_LIBC_TEST("fgetws_newline.test"));
::FILE *file = LIBC_NAMESPACE::fopen(FILENAME, "w");
ASSERT_FALSE(file == nullptr);
// Write "abc\ndef"
constexpr char CONTENT[] = "abc\ndef";
ASSERT_EQ(LIBC_NAMESPACE::fwrite(CONTENT, 1, sizeof(CONTENT) - 1, file),
sizeof(CONTENT) - 1);
ASSERT_EQ(LIBC_NAMESPACE::fclose(file), 0);
// Open for reading
file = LIBC_NAMESPACE::fopen(FILENAME, "r");
ASSERT_FALSE(file == nullptr);
wchar_t buffer[20] = {0};
wchar_t *result = LIBC_NAMESPACE::fgetws(buffer, 10, file);
ASSERT_FALSE(result == nullptr);
EXPECT_EQ(result, buffer);
EXPECT_EQ(LIBC_NAMESPACE::wcscmp(buffer, L"abc\n"), 0);
ASSERT_EQ(LIBC_NAMESPACE::fclose(file), 0);
}
TEST_F(LlvmLibcFgetwsTest, InvalidStream) {
const auto FILENAME =
libc_make_test_file_path(APPEND_LIBC_TEST("fgetws_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 write-only mode
file = LIBC_NAMESPACE::fopen(FILENAME, "w");
ASSERT_FALSE(file == nullptr);
// Try to read from write-only stream
wchar_t buffer[10] = {0};
ASSERT_ERRNO_SUCCESS();
EXPECT_EQ(LIBC_NAMESPACE::fgetws(buffer, 5, file),
static_cast<wchar_t *>(nullptr));
ASSERT_ERRNO_EQ(EBADF);
EXPECT_NE(LIBC_NAMESPACE::ferror(file), 0);
ASSERT_EQ(LIBC_NAMESPACE::fclose(file), 0);
}
TEST_F(LlvmLibcFgetwsTest, EncodingErrorEILSEQ) {
const auto FILENAME =
libc_make_test_file_path(APPEND_LIBC_TEST("fgetws_eilseq.test"));
::FILE *file = LIBC_NAMESPACE::fopen(FILENAME, "w");
ASSERT_FALSE(file == nullptr);
// Write an invalid UTF-8 sequence: 0x80 (stray continuation byte)
constexpr unsigned char CONTENT[] = {0x80};
ASSERT_EQ(LIBC_NAMESPACE::fwrite(CONTENT, 1, sizeof(CONTENT), file),
sizeof(CONTENT));
ASSERT_EQ(LIBC_NAMESPACE::fclose(file), 0);
// Open for reading
file = LIBC_NAMESPACE::fopen(FILENAME, "r");
ASSERT_FALSE(file == nullptr);
// Reading invalid sequence should fail with EILSEQ
wchar_t buffer[10] = {0};
ASSERT_ERRNO_SUCCESS();
EXPECT_EQ(LIBC_NAMESPACE::fgetws(buffer, 5, file),
static_cast<wchar_t *>(nullptr));
ASSERT_ERRNO_EQ(EILSEQ);
EXPECT_NE(LIBC_NAMESPACE::ferror(file), 0);
ASSERT_EQ(LIBC_NAMESPACE::fclose(file), 0);
}
TEST_F(LlvmLibcFgetwsTest, ByteModeFailure) {
const auto FILENAME =
libc_make_test_file_path(APPEND_LIBC_TEST("fgetws_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);
// Read wide string should fail and set errno to EINVAL
wchar_t buffer[10] = {0};
EXPECT_EQ(LIBC_NAMESPACE::fgetws(buffer, 5, file),
static_cast<wchar_t *>(nullptr));
ASSERT_ERRNO_EQ(EINVAL);
EXPECT_NE(LIBC_NAMESPACE::ferror(file), 0);
ASSERT_EQ(LIBC_NAMESPACE::fclose(file), 0);
}