aboutsummaryrefslogtreecommitdiff
path: root/libc/test
diff options
context:
space:
mode:
Diffstat (limited to 'libc/test')
-rw-r--r--libc/test/integration/src/pthread/CMakeLists.txt17
-rw-r--r--libc/test/integration/src/pthread/pthread_barrier_test.cpp117
-rw-r--r--libc/test/integration/src/stdlib/gpu/malloc_stress.cpp28
-rw-r--r--libc/test/shared/CMakeLists.txt10
-rw-r--r--libc/test/shared/shared_math_test.cpp44
-rw-r--r--libc/test/src/__support/FPUtil/comparison_operations_test.cpp27
-rw-r--r--libc/test/src/math/generic/CMakeLists.txt1
-rw-r--r--libc/test/src/math/smoke/CMakeLists.txt13
-rw-r--r--libc/test/src/math/smoke/fabsbf16_test.cpp14
-rw-r--r--libc/test/src/sched/CMakeLists.txt13
-rw-r--r--libc/test/src/sched/getcpu_test.cpp30
-rw-r--r--libc/test/src/wchar/CMakeLists.txt62
-rw-r--r--libc/test/src/wchar/mbsinit_test.cpp33
-rw-r--r--libc/test/src/wchar/mbsnrtowcs_test.cpp212
-rw-r--r--libc/test/src/wchar/mbsrtowcs_test.cpp185
-rw-r--r--libc/test/src/wchar/mbstowcs_test.cpp171
16 files changed, 940 insertions, 37 deletions
diff --git a/libc/test/integration/src/pthread/CMakeLists.txt b/libc/test/integration/src/pthread/CMakeLists.txt
index 0bdd99c..251b009 100644
--- a/libc/test/integration/src/pthread/CMakeLists.txt
+++ b/libc/test/integration/src/pthread/CMakeLists.txt
@@ -19,6 +19,23 @@ add_integration_test(
)
add_integration_test(
+ pthread_barrier_test
+ SUITE
+ libc-pthread-integration-tests
+ SRCS
+ pthread_barrier_test.cpp
+ DEPENDS
+ libc.include.pthread
+ libc.src.errno.errno
+ libc.src.pthread.pthread_barrier_destroy
+ libc.src.pthread.pthread_barrier_wait
+ libc.src.pthread.pthread_barrier_init
+ libc.src.pthread.pthread_create
+ libc.src.pthread.pthread_join
+ libc.src.stdio.printf
+)
+
+add_integration_test(
pthread_rwlock_test
SUITE
libc-pthread-integration-tests
diff --git a/libc/test/integration/src/pthread/pthread_barrier_test.cpp b/libc/test/integration/src/pthread/pthread_barrier_test.cpp
new file mode 100644
index 0000000..c8e1104
--- /dev/null
+++ b/libc/test/integration/src/pthread/pthread_barrier_test.cpp
@@ -0,0 +1,117 @@
+//===-- Tests for pthread_barrier_t ---------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "src/pthread/pthread_barrier_destroy.h"
+#include "src/pthread/pthread_barrier_init.h"
+#include "src/pthread/pthread_barrier_wait.h"
+
+#include "src/__support/CPP/atomic.h"
+#include "src/pthread/pthread_create.h"
+#include "src/pthread/pthread_join.h"
+#include "src/pthread/pthread_mutex_destroy.h"
+#include "src/pthread/pthread_mutex_init.h"
+#include "src/pthread/pthread_mutex_lock.h"
+#include "src/pthread/pthread_mutex_unlock.h"
+#include "src/string/memset.h"
+
+#include "test/IntegrationTest/test.h"
+
+#include <pthread.h>
+
+pthread_barrier_t barrier;
+LIBC_NAMESPACE::cpp::Atomic<int> counter;
+
+void *increment_counter_and_wait(void *args) {
+ counter.fetch_add(1);
+ return reinterpret_cast<void *>(
+ LIBC_NAMESPACE::pthread_barrier_wait(&barrier));
+}
+
+void single_use_barrier_test(int num_threads) {
+ counter.set(0);
+ // create n - 1 ADDITIONAL threads since the current thread will also wait at
+ // the barrier
+ pthread_t threads[num_threads - 1];
+ LIBC_NAMESPACE::memset(&barrier, 0, sizeof(pthread_barrier_t));
+ ASSERT_EQ(
+ LIBC_NAMESPACE::pthread_barrier_init(&barrier, nullptr, num_threads), 0);
+
+ for (int i = 0; i < num_threads - 1; ++i)
+ LIBC_NAMESPACE::pthread_create(&threads[i], nullptr,
+ increment_counter_and_wait, nullptr);
+
+ uintptr_t return_val_sum =
+ reinterpret_cast<uintptr_t>(increment_counter_and_wait(nullptr));
+ ASSERT_EQ(counter.load(), num_threads);
+
+ // verify only one thread got the PTHREAD_BARRIER_SERIAL_THREAD return value
+ for (int i = 0; i < num_threads - 1; ++i) {
+ void *ret;
+ LIBC_NAMESPACE::pthread_join(threads[i], &ret);
+ if (reinterpret_cast<uintptr_t>(ret) ==
+ static_cast<uintptr_t>(PTHREAD_BARRIER_SERIAL_THREAD)) {
+ return_val_sum += reinterpret_cast<uintptr_t>(ret);
+ } else {
+ ASSERT_EQ(ret, 0);
+ }
+ }
+ ASSERT_EQ(return_val_sum,
+ static_cast<uintptr_t>(PTHREAD_BARRIER_SERIAL_THREAD));
+
+ LIBC_NAMESPACE::pthread_barrier_destroy(&barrier);
+}
+
+void reused_barrier_test() {
+ counter.set(0);
+ const int NUM_THREADS = 30;
+ const int REPEAT = 20;
+ pthread_t threads[NUM_THREADS - 1]; // subtract 1 for main thread
+ LIBC_NAMESPACE::memset(&barrier, 0, sizeof(pthread_barrier_t));
+ ASSERT_EQ(
+ LIBC_NAMESPACE::pthread_barrier_init(&barrier, nullptr, NUM_THREADS), 0);
+
+ for (int i = 0; i < REPEAT; ++i) {
+ for (int j = 0; j < NUM_THREADS - 1; ++j)
+ LIBC_NAMESPACE::pthread_create(&threads[j], nullptr,
+ increment_counter_and_wait, nullptr);
+
+ uintptr_t return_val_sum =
+ reinterpret_cast<uintptr_t>(increment_counter_and_wait(nullptr));
+ ASSERT_EQ(counter.load(), NUM_THREADS * (i + 1));
+
+ // verify only one thread got the PTHREAD_BARRIER_SERIAL_THREAD return value
+ for (int i = 0; i < NUM_THREADS - 1; ++i) {
+ void *ret;
+ LIBC_NAMESPACE::pthread_join(threads[i], &ret);
+ if (reinterpret_cast<uintptr_t>(ret) ==
+ static_cast<uintptr_t>(PTHREAD_BARRIER_SERIAL_THREAD)) {
+ return_val_sum += reinterpret_cast<uintptr_t>(ret);
+ } else {
+ ASSERT_EQ(ret, 0);
+ }
+ }
+ ASSERT_EQ(return_val_sum,
+ static_cast<uintptr_t>(PTHREAD_BARRIER_SERIAL_THREAD));
+ }
+
+ LIBC_NAMESPACE::pthread_barrier_destroy(&barrier);
+}
+
+void *barrier_wait(void *in) {
+ return reinterpret_cast<void *>(
+ LIBC_NAMESPACE::pthread_barrier_wait(&barrier));
+}
+
+TEST_MAIN() {
+ // don't create any additional threads; only use main thread
+ single_use_barrier_test(1);
+
+ single_use_barrier_test(30);
+ reused_barrier_test();
+ return 0;
+}
diff --git a/libc/test/integration/src/stdlib/gpu/malloc_stress.cpp b/libc/test/integration/src/stdlib/gpu/malloc_stress.cpp
index 77479f8..4c540a8 100644
--- a/libc/test/integration/src/stdlib/gpu/malloc_stress.cpp
+++ b/libc/test/integration/src/stdlib/gpu/malloc_stress.cpp
@@ -14,6 +14,20 @@
using namespace LIBC_NAMESPACE;
+static inline uint32_t entropy() {
+ return (static_cast<uint32_t>(gpu::processor_clock()) ^
+ (gpu::get_thread_id_x() * 0x632be59b) ^
+ (gpu::get_block_id_x() * 0x85157af5)) *
+ 0x9e3779bb;
+}
+
+static inline uint32_t xorshift32(uint32_t &state) {
+ state ^= state << 13;
+ state ^= state >> 17;
+ state ^= state << 5;
+ return state * 0x9e3779bb;
+}
+
static inline void use(uint8_t *ptr, uint32_t size) {
EXPECT_NE(ptr, nullptr);
for (int i = 0; i < size; ++i)
@@ -34,5 +48,19 @@ TEST_MAIN(int, char **, char **) {
for (int i = 0; i < 256; ++i)
free(ptrs[i]);
+
+ uint32_t state = entropy();
+ for (int i = 0; i < 1024; ++i) {
+ if (xorshift32(state) % 2) {
+ uint64_t size = xorshift32(state) % 256 + 16;
+ uint64_t *ptr = reinterpret_cast<uint64_t *>(malloc(size));
+ *ptr = gpu::get_thread_id();
+
+ EXPECT_EQ(*ptr, gpu::get_thread_id());
+ ASSERT_TRUE(ptr);
+ ASSERT_TRUE(__builtin_is_aligned(ptr, 16));
+ free(ptr);
+ }
+ }
return 0;
}
diff --git a/libc/test/shared/CMakeLists.txt b/libc/test/shared/CMakeLists.txt
index 89b607d..4ed32d4 100644
--- a/libc/test/shared/CMakeLists.txt
+++ b/libc/test/shared/CMakeLists.txt
@@ -12,6 +12,16 @@ add_fp_unittest(
libc.src.__support.math.acosf16
libc.src.__support.math.acoshf
libc.src.__support.math.acoshf16
+ libc.src.__support.math.acospif16
+ libc.src.__support.math.asin
+ libc.src.__support.math.asinf
+ libc.src.__support.math.asinf16
+ libc.src.__support.math.asinhf
+ libc.src.__support.math.asinhf16
+ libc.src.__support.math.atan
+ libc.src.__support.math.atan2
+ libc.src.__support.math.atanf
+ libc.src.__support.math.atanf16
libc.src.__support.math.erff
libc.src.__support.math.exp
libc.src.__support.math.exp10
diff --git a/libc/test/shared/shared_math_test.cpp b/libc/test/shared/shared_math_test.cpp
index 8d3cebd..cd72df4 100644
--- a/libc/test/shared/shared_math_test.cpp
+++ b/libc/test/shared/shared_math_test.cpp
@@ -8,64 +8,78 @@
#include "shared/math.h"
#include "test/UnitTest/FPMatcher.h"
+#include "test/UnitTest/Test.h"
#ifdef LIBC_TYPES_HAS_FLOAT16
TEST(LlvmLibcSharedMathTest, AllFloat16) {
int exponent;
- EXPECT_FP_EQ(0x0p+0f, LIBC_NAMESPACE::shared::acoshf16(1.0f));
+ EXPECT_FP_EQ(0x0p+0f16, LIBC_NAMESPACE::shared::acoshf16(1.0f16));
+ EXPECT_FP_EQ(0x0p+0f16, LIBC_NAMESPACE::shared::acospif16(1.0f16));
+ EXPECT_FP_EQ(0x0p+0f16, LIBC_NAMESPACE::shared::asinf16(0.0f16));
+ EXPECT_FP_EQ(0x0p+0f16, LIBC_NAMESPACE::shared::asinhf16(0.0f16));
+ EXPECT_FP_EQ(0x0p+0f16, LIBC_NAMESPACE::shared::atanf16(0.0f16));
EXPECT_FP_EQ(0x1p+0f16, LIBC_NAMESPACE::shared::exp10f16(0.0f16));
EXPECT_FP_EQ(0x1p+0f16, LIBC_NAMESPACE::shared::expf16(0.0f16));
- ASSERT_FP_EQ(float16(8 << 5), LIBC_NAMESPACE::shared::ldexpf16(float(8), 5));
+ ASSERT_FP_EQ(float16(8 << 5), LIBC_NAMESPACE::shared::ldexpf16(8.0f16, 5));
ASSERT_FP_EQ(float16(-1 * (8 << 5)),
- LIBC_NAMESPACE::shared::ldexpf16(float(-8), 5));
+ LIBC_NAMESPACE::shared::ldexpf16(-8.0f16, 5));
- EXPECT_FP_EQ_ALL_ROUNDING(0.75f16,
- LIBC_NAMESPACE::shared::frexpf16(24.0f, &exponent));
+ EXPECT_FP_EQ_ALL_ROUNDING(
+ 0.75f16, LIBC_NAMESPACE::shared::frexpf16(24.0f16, &exponent));
EXPECT_EQ(exponent, 5);
EXPECT_FP_EQ(0x1.921fb6p+0f16, LIBC_NAMESPACE::shared::acosf16(0.0f16));
}
-#endif
+#endif // LIBC_TYPES_HAS_FLOAT16
TEST(LlvmLibcSharedMathTest, AllFloat) {
int exponent;
EXPECT_FP_EQ(0x1.921fb6p+0, LIBC_NAMESPACE::shared::acosf(0.0f));
+ EXPECT_FP_EQ(0x0p+0f, LIBC_NAMESPACE::shared::acoshf(1.0f));
+ EXPECT_FP_EQ(0x0p+0f, LIBC_NAMESPACE::shared::asinf(0.0f));
+ EXPECT_FP_EQ(0x0p+0f, LIBC_NAMESPACE::shared::asinhf(0.0f));
+ EXPECT_FP_EQ(0x0p+0f, LIBC_NAMESPACE::shared::atanf(0.0f));
+ EXPECT_FP_EQ(0x0p+0f, LIBC_NAMESPACE::shared::erff(0.0f));
EXPECT_FP_EQ(0x1p+0f, LIBC_NAMESPACE::shared::exp10f(0.0f));
EXPECT_FP_EQ(0x1p+0f, LIBC_NAMESPACE::shared::expf(0.0f));
- EXPECT_FP_EQ(0x0p+0f, LIBC_NAMESPACE::shared::erff(0.0f));
- EXPECT_FP_EQ(0x0p+0f, LIBC_NAMESPACE::shared::acoshf(1.0f));
EXPECT_FP_EQ_ALL_ROUNDING(0.75f,
LIBC_NAMESPACE::shared::frexpf(24.0f, &exponent));
EXPECT_EQ(exponent, 5);
- ASSERT_FP_EQ(float(8 << 5), LIBC_NAMESPACE::shared::ldexpf(float(8), 5));
- ASSERT_FP_EQ(float(-1 * (8 << 5)),
- LIBC_NAMESPACE::shared::ldexpf(float(-8), 5));
+ ASSERT_FP_EQ(float(8 << 5), LIBC_NAMESPACE::shared::ldexpf(8.0f, 5));
+ ASSERT_FP_EQ(float(-1 * (8 << 5)), LIBC_NAMESPACE::shared::ldexpf(-8.0f, 5));
}
TEST(LlvmLibcSharedMathTest, AllDouble) {
EXPECT_FP_EQ(0x1.921fb54442d18p+0, LIBC_NAMESPACE::shared::acos(0.0));
+ EXPECT_FP_EQ(0x0p+0, LIBC_NAMESPACE::shared::asin(0.0));
+ EXPECT_FP_EQ(0x0p+0, LIBC_NAMESPACE::shared::atan(0.0));
+ EXPECT_FP_EQ(0x0p+0, LIBC_NAMESPACE::shared::atan2(0.0, 0.0));
EXPECT_FP_EQ(0x1p+0, LIBC_NAMESPACE::shared::exp(0.0));
EXPECT_FP_EQ(0x1p+0, LIBC_NAMESPACE::shared::exp10(0.0));
}
+#ifdef LIBC_TYPES_HAS_FLOAT128
+
TEST(LlvmLibcSharedMathTest, AllFloat128) {
int exponent;
- EXPECT_FP_EQ_ALL_ROUNDING(
- float128(0.75), LIBC_NAMESPACE::shared::frexpf128(24.0f, &exponent));
+ EXPECT_FP_EQ_ALL_ROUNDING(float128(0.75), LIBC_NAMESPACE::shared::frexpf128(
+ float128(24), &exponent));
EXPECT_EQ(exponent, 5);
ASSERT_FP_EQ(float128(8 << 5),
- LIBC_NAMESPACE::shared::ldexpf128(float(8), 5));
+ LIBC_NAMESPACE::shared::ldexpf128(float128(8), 5));
ASSERT_FP_EQ(float128(-1 * (8 << 5)),
- LIBC_NAMESPACE::shared::ldexpf128(float(-8), 5));
+ LIBC_NAMESPACE::shared::ldexpf128(float128(-8), 5));
}
+
+#endif // LIBC_TYPES_HAS_FLOAT128
diff --git a/libc/test/src/__support/FPUtil/comparison_operations_test.cpp b/libc/test/src/__support/FPUtil/comparison_operations_test.cpp
index 04a3321..05b8f68 100644
--- a/libc/test/src/__support/FPUtil/comparison_operations_test.cpp
+++ b/libc/test/src/__support/FPUtil/comparison_operations_test.cpp
@@ -25,28 +25,15 @@ template <typename T>
class ComparisonOperationsTest : public LIBC_NAMESPACE::testing::FEnvSafeTest {
DECLARE_SPECIAL_CONSTANTS(T)
- // TODO: Make these constexpr once quick_get_round is made constexpr.
- T normal1;
- T neg_normal1;
- T normal2;
- T small;
- T neg_small;
- T large;
- T neg_large;
+ static constexpr T normal1 = T(3.14);
+ static constexpr T neg_normal1 = T(-3.14);
+ static constexpr T normal2 = T(2.71);
+ static constexpr T small = T(0.1);
+ static constexpr T neg_small = T(-0.1);
+ static constexpr T large = T(10000.0);
+ static constexpr T neg_large = T(-10000.0);
public:
- void SetUp() override {
- with_fenv_preserved([this]() {
- normal1 = T(3.14);
- neg_normal1 = T(-3.14);
- normal2 = T(2.71);
- small = T(0.1);
- neg_small = T(-0.1);
- large = T(10000.0);
- neg_large = T(-10000.0);
- });
- }
-
void test_equals() {
EXPECT_TRUE(equals(neg_zero, neg_zero));
EXPECT_TRUE(equals(zero, neg_zero));
diff --git a/libc/test/src/math/generic/CMakeLists.txt b/libc/test/src/math/generic/CMakeLists.txt
index 1fe7801..a9d54d6 100644
--- a/libc/test/src/math/generic/CMakeLists.txt
+++ b/libc/test/src/math/generic/CMakeLists.txt
@@ -30,4 +30,3 @@ add_fp_unittest(
DEPENDS
libc.src.math.generic.ceill
)
-
diff --git a/libc/test/src/math/smoke/CMakeLists.txt b/libc/test/src/math/smoke/CMakeLists.txt
index ec4c09c..40b7a342 100644
--- a/libc/test/src/math/smoke/CMakeLists.txt
+++ b/libc/test/src/math/smoke/CMakeLists.txt
@@ -221,6 +221,19 @@ add_fp_unittest(
)
add_fp_unittest(
+ fabsbf16_test
+ SUITE
+ libc-math-smoke-tests
+ SRCS
+ fabsbf16_test.cpp
+ HDRS
+ FAbsTest.h
+ DEPENDS
+ libc.src.__support.FPUtil.bfloat16
+ libc.src.math.fabsbf16
+)
+
+add_fp_unittest(
fadd_test
SUITE
libc-math-smoke-tests
diff --git a/libc/test/src/math/smoke/fabsbf16_test.cpp b/libc/test/src/math/smoke/fabsbf16_test.cpp
new file mode 100644
index 0000000..611050a
--- /dev/null
+++ b/libc/test/src/math/smoke/fabsbf16_test.cpp
@@ -0,0 +1,14 @@
+//===-- Unittests for fabsbf16 --------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "FAbsTest.h"
+
+#include "src/__support/FPUtil/bfloat16.h"
+#include "src/math/fabsbf16.h"
+
+LIST_FABS_TESTS(bfloat16, LIBC_NAMESPACE::fabsbf16)
diff --git a/libc/test/src/sched/CMakeLists.txt b/libc/test/src/sched/CMakeLists.txt
index 9dda4ea..54c6d1d 100644
--- a/libc/test/src/sched/CMakeLists.txt
+++ b/libc/test/src/sched/CMakeLists.txt
@@ -41,6 +41,19 @@ add_libc_unittest(
)
add_libc_unittest(
+ getcpu_test
+ SUITE
+ libc_sched_unittests
+ SRCS
+ getcpu_test.cpp
+ DEPENDS
+ libc.include.sched
+ libc.src.errno.errno
+ libc.src.sched.getcpu
+ libc.test.UnitTest.ErrnoCheckingTest
+)
+
+add_libc_unittest(
scheduler_test
SUITE
libc_sched_unittests
diff --git a/libc/test/src/sched/getcpu_test.cpp b/libc/test/src/sched/getcpu_test.cpp
new file mode 100644
index 0000000..fc4ada8
--- /dev/null
+++ b/libc/test/src/sched/getcpu_test.cpp
@@ -0,0 +1,30 @@
+//===-- Unittests for getcpu ----------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "src/__support/OSUtil/syscall.h"
+#include "src/__support/libc_errno.h"
+#include "src/sched/getcpu.h"
+#include "test/UnitTest/ErrnoCheckingTest.h"
+#include "test/UnitTest/ErrnoSetterMatcher.h"
+
+using LlvmLibcSchedGetCpuTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
+
+TEST_F(LlvmLibcSchedGetCpuTest, SmokeTest) {
+ unsigned int current_cpu;
+ unsigned int current_node;
+ using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Succeeds;
+ ASSERT_THAT(LIBC_NAMESPACE::getcpu(&current_cpu, &current_node), Succeeds(0));
+}
+
+TEST_F(LlvmLibcSchedGetCpuTest, BadPointer) {
+ unsigned int current_cpu;
+ unsigned int *current_node = reinterpret_cast<unsigned int *>(-1);
+ using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Fails;
+ ASSERT_THAT(LIBC_NAMESPACE::getcpu(&current_cpu, current_node),
+ Fails(EFAULT));
+}
diff --git a/libc/test/src/wchar/CMakeLists.txt b/libc/test/src/wchar/CMakeLists.txt
index f420ecc..d1a0684 100644
--- a/libc/test/src/wchar/CMakeLists.txt
+++ b/libc/test/src/wchar/CMakeLists.txt
@@ -76,6 +76,19 @@ add_libc_test(
)
add_libc_test(
+ mbstowcs_test
+ SUITE
+ libc_wchar_unittests
+ SRCS
+ mbstowcs_test.cpp
+ DEPENDS
+ libc.src.__support.libc_errno
+ libc.src.wchar.mbstowcs
+ libc.hdr.types.wchar_t
+ libc.test.UnitTest.ErrnoCheckingTest
+)
+
+add_libc_test(
mblen_test
SUITE
libc_wchar_unittests
@@ -88,6 +101,22 @@ add_libc_test(
)
add_libc_test(
+ mbsrtowcs_test
+ SUITE
+ libc_wchar_unittests
+ SRCS
+ mbsrtowcs_test.cpp
+ DEPENDS
+ libc.src.__support.libc_errno
+ libc.src.__support.wchar.mbstate
+ libc.src.string.memset
+ libc.src.wchar.mbsrtowcs
+ libc.hdr.types.mbstate_t
+ libc.hdr.types.wchar_t
+ libc.test.UnitTest.ErrnoCheckingTest
+)
+
+add_libc_test(
mbrlen_test
SUITE
libc_wchar_unittests
@@ -97,12 +126,43 @@ add_libc_test(
libc.src.__support.libc_errno
libc.src.__support.wchar.mbstate
libc.src.string.memset
- libc.src.wchar.mbrlen
+ libc.src.wchar.mbsrlen
libc.hdr.types.mbstate_t
+ libc.hdr.types.wchar_t
libc.test.UnitTest.ErrnoCheckingTest
)
add_libc_test(
+ mbsnrtowcs_test
+ SUITE
+ libc_wchar_unittests
+ SRCS
+ mbsnrtowcs_test.cpp
+ DEPENDS
+ libc.src.__support.libc_errno
+ libc.src.__support.wchar.mbstate
+ libc.src.string.memset
+ libc.src.wchar.mbsnrtowcs
+ libc.hdr.types.mbstate_t
+ libc.hdr.types.wchar_t
+ libc.test.UnitTest.ErrnoCheckingTest
+)
+
+add_libc_test(
+ mbsinit_test
+ SUITE
+ libc_wchar_unittests
+ SRCS
+ mbsinit_test.cpp
+ DEPENDS
+ libc.src.string.memset
+ libc.src.wchar.mbsinit
+ libc.src.wchar.mbrtowc
+ libc.hdr.types.mbstate_t
+ libc.hdr.types.wchar_t
+)
+
+add_libc_test(
wctob_test
SUITE
libc_wchar_unittests
diff --git a/libc/test/src/wchar/mbsinit_test.cpp b/libc/test/src/wchar/mbsinit_test.cpp
new file mode 100644
index 0000000..ecb48aa
--- /dev/null
+++ b/libc/test/src/wchar/mbsinit_test.cpp
@@ -0,0 +1,33 @@
+//===-- Unittests for mbsinit ---------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "hdr/types/wchar_t.h"
+#include "src/string/memset.h"
+#include "src/wchar/mbrtowc.h"
+#include "src/wchar/mbsinit.h"
+#include "test/UnitTest/Test.h"
+
+TEST(LlvmLibcMBSInitTest, EmptyState) {
+ mbstate_t ps;
+ LIBC_NAMESPACE::memset(&ps, 0, sizeof(mbstate_t));
+ ASSERT_NE(LIBC_NAMESPACE::mbsinit(&ps), 0);
+ ASSERT_NE(LIBC_NAMESPACE::mbsinit(nullptr), 0);
+}
+
+TEST(LlvmLibcMBSInitTest, ConversionTest) {
+ const char *src = "\xf0\x9f\xa4\xa3"; // 4 byte emoji
+ wchar_t dest[2];
+ mbstate_t ps;
+ LIBC_NAMESPACE::memset(&ps, 0, sizeof(mbstate_t));
+
+ ASSERT_NE(LIBC_NAMESPACE::mbsinit(&ps), 0);
+ LIBC_NAMESPACE::mbrtowc(dest, src, 2, &ps); // partial conversion
+ ASSERT_EQ(LIBC_NAMESPACE::mbsinit(&ps), 0);
+ LIBC_NAMESPACE::mbrtowc(dest, src + 2, 2, &ps); // complete conversion
+ ASSERT_NE(LIBC_NAMESPACE::mbsinit(&ps), 0); // state should be reset now
+}
diff --git a/libc/test/src/wchar/mbsnrtowcs_test.cpp b/libc/test/src/wchar/mbsnrtowcs_test.cpp
new file mode 100644
index 0000000..a3de68f
--- /dev/null
+++ b/libc/test/src/wchar/mbsnrtowcs_test.cpp
@@ -0,0 +1,212 @@
+//===-- Unittests for mbsetowcs -------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "hdr/types/mbstate_t.h"
+#include "hdr/types/wchar_t.h"
+#include "src/__support/libc_errno.h"
+#include "src/__support/macros/null_check.h"
+#include "src/__support/wchar/mbstate.h"
+#include "src/string/memset.h"
+#include "src/wchar/mbsnrtowcs.h"
+#include "test/UnitTest/ErrnoCheckingTest.h"
+#include "test/UnitTest/Test.h"
+
+using LlvmLibcMBSNRToWCSTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
+
+TEST_F(LlvmLibcMBSNRToWCSTest, OneByteOneChar) {
+ const char *ch = "A";
+ const char *original = ch;
+ wchar_t dest[2];
+ mbstate_t mb;
+ LIBC_NAMESPACE::memset(&mb, 0, sizeof(mbstate_t));
+ size_t n = LIBC_NAMESPACE::mbsnrtowcs(dest, &ch, 1, 1, &mb);
+ ASSERT_EQ(static_cast<char>(*dest), 'A');
+ ASSERT_EQ(static_cast<int>(n), 1);
+ // Should point to null terminator now
+ ASSERT_EQ(ch, original + 1);
+ ASSERT_ERRNO_SUCCESS();
+
+ n = LIBC_NAMESPACE::mbsnrtowcs(dest + 1, &ch, 1, 1, &mb);
+ ASSERT_EQ(static_cast<char>(dest[1]), '\0');
+ // Should not include null terminator
+ ASSERT_EQ(static_cast<int>(n), 0);
+ // Should now be a nullptr
+ ASSERT_EQ(ch, nullptr);
+ ASSERT_ERRNO_SUCCESS();
+}
+
+TEST_F(LlvmLibcMBSNRToWCSTest, FourByteOneChar) {
+ const char *src = "\xf0\x9f\x98\xb9"; // laughing cat emoji 😹
+ const char *original = src;
+ wchar_t dest[2];
+ mbstate_t mb;
+ LIBC_NAMESPACE::memset(&mb, 0, sizeof(mbstate_t));
+ // Not enough bytes for the full character
+ size_t n = LIBC_NAMESPACE::mbsnrtowcs(dest, &src, 3, 2, &mb);
+ ASSERT_ERRNO_SUCCESS();
+ ASSERT_EQ(static_cast<int>(n), 0);
+ ASSERT_EQ(src, original + 3);
+ // Needs 2 more bytes (last byte of cat + null terminator)
+ n = LIBC_NAMESPACE::mbsnrtowcs(dest, &src, 2, 2, &mb);
+ ASSERT_ERRNO_SUCCESS();
+ // Does not include null terminator
+ ASSERT_EQ(static_cast<int>(n), 1);
+ ASSERT_EQ(src, nullptr);
+ ASSERT_EQ(static_cast<int>(dest[0]), 128569);
+ ASSERT_TRUE(dest[1] == L'\0');
+}
+
+TEST_F(LlvmLibcMBSNRToWCSTest, MixedNumberOfBytes) {
+ // 'A', sigma symbol 'Σ', recycling symbol '♻', laughing cat emoji '😹'
+ const char *src = "A\xce\xa3\xe2\x99\xbb\xf0\x9f\x98\xb9";
+ const char *original = src;
+ wchar_t dest[5];
+ mbstate_t mb;
+ LIBC_NAMESPACE::memset(&mb, 0, sizeof(mbstate_t));
+
+ // Read 'A'
+ size_t n = LIBC_NAMESPACE::mbsnrtowcs(dest, &src, 1, 1, &mb);
+ ASSERT_ERRNO_SUCCESS();
+ ASSERT_EQ(static_cast<char>(dest[0]), 'A');
+ ASSERT_EQ(static_cast<int>(n), 1);
+ ASSERT_EQ(src, original + 1);
+
+ // Read sigma 'Σ'
+ n = LIBC_NAMESPACE::mbsnrtowcs(dest + 1, &src, 2, 1, &mb);
+ ASSERT_ERRNO_SUCCESS();
+ ASSERT_EQ(static_cast<int>(dest[1]), 931);
+ ASSERT_EQ(static_cast<int>(n), 1);
+ ASSERT_EQ(src, original + 3);
+
+ // Read recycling 'â™»'
+ n = LIBC_NAMESPACE::mbsnrtowcs(dest + 2, &src, 2, 5, &mb);
+ ASSERT_ERRNO_SUCCESS();
+ ASSERT_EQ(static_cast<int>(n), 0);
+ ASSERT_EQ(src, original + 5);
+ n = LIBC_NAMESPACE::mbsnrtowcs(dest + 2, &src, 1, 1, &mb);
+ ASSERT_ERRNO_SUCCESS();
+ ASSERT_EQ(static_cast<int>(n), 1);
+ ASSERT_EQ(src, original + 6);
+ ASSERT_EQ(static_cast<int>(dest[2]), 9851);
+
+ // Read laughing cat emoji '😹'
+ n = LIBC_NAMESPACE::mbsnrtowcs(dest + 3, &src, 4, 5, &mb);
+ ASSERT_ERRNO_SUCCESS();
+ ASSERT_EQ(static_cast<int>(n), 1);
+ ASSERT_EQ(src, original + 10);
+ ASSERT_EQ(static_cast<int>(dest[3]), 128569);
+
+ n = LIBC_NAMESPACE::mbsnrtowcs(dest + 4, &src, 4, 4, nullptr);
+ ASSERT_TRUE(dest[4] == L'\0');
+ ASSERT_ERRNO_SUCCESS();
+ // Should not count null terminator in number
+ ASSERT_EQ(static_cast<int>(n), 0);
+ // Should now be a nullptr
+ ASSERT_EQ(src, nullptr);
+}
+
+TEST_F(LlvmLibcMBSNRToWCSTest, ReadLessThanStringLength) {
+ // Four laughing cat emojis "😹😹😹😹"
+ const char *src =
+ "\xf0\x9f\x98\xb9\xf0\x9f\x98\xb9\xf0\x9f\x98\xb9\xf0\x9f\x98\xb9";
+ const char *original = src;
+ wchar_t dest[5] = {L'a', L'b', L'c', L'd', L'e'};
+ size_t n = LIBC_NAMESPACE::mbsnrtowcs(dest, &src, 100, 3, nullptr);
+ ASSERT_ERRNO_SUCCESS();
+ // Should have read 3 emojis
+ ASSERT_EQ(static_cast<int>(n), 3);
+ ASSERT_EQ(static_cast<int>(dest[0]), 128569);
+ ASSERT_EQ(static_cast<int>(dest[1]), 128569);
+ ASSERT_EQ(static_cast<int>(dest[2]), 128569);
+ ASSERT_TRUE(dest[3] == L'd');
+ ASSERT_TRUE(dest[4] == L'e');
+ // Read three laughing cat emojis, 12 bytes
+ ASSERT_EQ(src, original + 12);
+}
+
+TEST_F(LlvmLibcMBSNRToWCSTest, InvalidFirstByte) {
+ // 0x80 is invalid first byte of mb character
+ const char *src =
+ "\x80\x9f\x98\xb9\xf0\x9f\x98\xb9\xf0\x9f\x98\xb9\xf0\x9f\x98\xb9";
+ const char *original = src;
+ wchar_t dest[3];
+ size_t n = LIBC_NAMESPACE::mbsnrtowcs(dest, &src, 88, 88, nullptr);
+ // Should return error and set errno
+ ASSERT_EQ(static_cast<int>(n), -1);
+ ASSERT_ERRNO_EQ(EILSEQ);
+ // Should not update pointer
+ ASSERT_EQ(src, original);
+}
+
+TEST_F(LlvmLibcMBSNRToWCSTest, InvalidMiddleByte) {
+ // The 7th byte is invalid for a 4 byte character
+ const char *src =
+ "\xf0\x9f\x98\xb9\xf0\x9f\xf0\xb9\xf0\x9f\x98\xb9\xf0\x9f\x98\xb9";
+ const char *original = src;
+ wchar_t dest[3];
+ mbstate_t mb;
+ LIBC_NAMESPACE::memset(&mb, 0, sizeof(mbstate_t));
+ // Successfully read one character and first byte of the second character
+ size_t n = LIBC_NAMESPACE::mbsnrtowcs(dest, &src, 5, 88, &mb);
+ ASSERT_EQ(static_cast<int>(n), 1);
+ ASSERT_ERRNO_SUCCESS();
+ ASSERT_EQ(src, original + 5);
+ ASSERT_EQ(static_cast<int>(dest[0]), 128569);
+
+ n = LIBC_NAMESPACE::mbsnrtowcs(dest + 1, &src, 5, 88, &mb);
+ // Should return error, set errno, and not update the pointer
+ ASSERT_EQ(static_cast<int>(n), -1);
+ ASSERT_ERRNO_EQ(EILSEQ);
+ ASSERT_EQ(src, original + 5);
+}
+
+TEST_F(LlvmLibcMBSNRToWCSTest, NullDestination) {
+ // Four laughing cat emojis "😹😹😹😹"
+ const char *src =
+ "\xf0\x9f\x98\xb9\xf0\x9f\x98\xb9\xf0\x9f\x98\xb9\xf0\x9f\x98\xb9";
+ const char *original = src;
+ size_t n = LIBC_NAMESPACE::mbsnrtowcs(nullptr, &src, 88, 88, nullptr);
+ ASSERT_ERRNO_SUCCESS();
+ // Null destination should ignore len and read till end of string
+ ASSERT_EQ(static_cast<int>(n), 4);
+ // It should also not change the src pointer
+ ASSERT_EQ(src, original);
+}
+
+TEST_F(LlvmLibcMBSNRToWCSTest, ErrnoChecks) {
+ // Two laughing cat emojis and invalid 3rd mb char (3rd byte of it)
+ const char *src =
+ "\xf0\x9f\x98\xb9\xf0\x9f\x98\xb9\xf0\x9f\xf0\xb9\xf0\x9f\x98\xb9";
+ const char *original = src;
+ wchar_t dest[5];
+ mbstate_t mb;
+ LIBC_NAMESPACE::memset(&mb, 0, sizeof(mbstate_t));
+ // First two bytes are valid --> should not set errno
+ size_t n = LIBC_NAMESPACE::mbsnrtowcs(dest, &src, 80, 2, &mb);
+ ASSERT_ERRNO_SUCCESS();
+ ASSERT_EQ(static_cast<int>(n), 2);
+ ASSERT_EQ(static_cast<int>(dest[0]), 128569);
+ ASSERT_EQ(static_cast<int>(dest[1]), 128569);
+ ASSERT_EQ(src, original + 8);
+
+ // Trying to read the 3rd byte should set errno
+ n = LIBC_NAMESPACE::mbsnrtowcs(dest + 2, &src, 4, 2, &mb);
+ ASSERT_ERRNO_EQ(EILSEQ);
+ ASSERT_EQ(static_cast<int>(n), -1);
+ // Should not move the pointer
+ ASSERT_EQ(src, original + 8);
+}
+
+#if defined(LIBC_ADD_NULL_CHECKS)
+TEST(LlvmLibcMBSNRToWCSTest, NullptrCrash) {
+ // Passing in a nullptr should crash the program.
+ EXPECT_DEATH(
+ [] { LIBC_NAMESPACE::mbsnrtowcs(nullptr, nullptr, 1, 1, nullptr); },
+ WITH_SIGNAL(-1));
+}
+#endif // LIBC_ADD_NULL_CHECKS
diff --git a/libc/test/src/wchar/mbsrtowcs_test.cpp b/libc/test/src/wchar/mbsrtowcs_test.cpp
new file mode 100644
index 0000000..59efc0d
--- /dev/null
+++ b/libc/test/src/wchar/mbsrtowcs_test.cpp
@@ -0,0 +1,185 @@
+//===-- Unittests for mbsetowcs -------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "hdr/types/mbstate_t.h"
+#include "hdr/types/wchar_t.h"
+#include "src/__support/libc_errno.h"
+#include "src/__support/macros/null_check.h"
+#include "src/__support/wchar/mbstate.h"
+#include "src/string/memset.h"
+#include "src/wchar/mbsrtowcs.h"
+#include "test/UnitTest/ErrnoCheckingTest.h"
+#include "test/UnitTest/Test.h"
+
+using LlvmLibcMBSRToWCSTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
+
+TEST_F(LlvmLibcMBSRToWCSTest, OneByteOneChar) {
+ const char *ch = "A";
+ const char *original = ch;
+ wchar_t dest[2];
+ mbstate_t mb;
+ LIBC_NAMESPACE::memset(&mb, 0, sizeof(mbstate_t));
+ size_t n = LIBC_NAMESPACE::mbsrtowcs(dest, &ch, 1, &mb);
+ ASSERT_EQ(static_cast<char>(*dest), 'A');
+ ASSERT_EQ(static_cast<int>(n), 1);
+ // Should point to null terminator now
+ ASSERT_EQ(ch, original + 1);
+ ASSERT_ERRNO_SUCCESS();
+
+ n = LIBC_NAMESPACE::mbsrtowcs(dest + 1, &ch, 1, &mb);
+ ASSERT_EQ(static_cast<char>(dest[1]), '\0');
+ // Should not include null terminator
+ ASSERT_EQ(static_cast<int>(n), 0);
+ // Should now be a nullptr
+ ASSERT_EQ(ch, nullptr);
+ ASSERT_ERRNO_SUCCESS();
+}
+
+TEST_F(LlvmLibcMBSRToWCSTest, FourByteOneChar) {
+ const char *src = "\xf0\x9f\x98\xb9"; // laughing cat emoji 😹
+ wchar_t dest[2];
+ mbstate_t mb;
+ LIBC_NAMESPACE::memset(&mb, 0, sizeof(mbstate_t));
+ size_t n = LIBC_NAMESPACE::mbsrtowcs(dest, &src, 2, &mb);
+ ASSERT_ERRNO_SUCCESS();
+ ASSERT_EQ(static_cast<int>(dest[0]), 128569);
+ ASSERT_TRUE(dest[1] == L'\0');
+ // Should not count null terminator in number
+ ASSERT_EQ(static_cast<int>(n), 1);
+ // Should now be a nullptr
+ ASSERT_EQ(src, nullptr);
+}
+
+TEST_F(LlvmLibcMBSRToWCSTest, MultiByteTwoCharacters) {
+ // Two laughing cat emojis "😹😹"
+ const char *src = "\xf0\x9f\x98\xb9\xf0\x9f\x98\xb9";
+ wchar_t dest[3];
+ mbstate_t mb;
+ LIBC_NAMESPACE::memset(&mb, 0, sizeof(mbstate_t));
+ size_t n = LIBC_NAMESPACE::mbsrtowcs(dest, &src, 3, &mb);
+ ASSERT_ERRNO_SUCCESS();
+ ASSERT_EQ(static_cast<int>(dest[0]), 128569);
+ ASSERT_EQ(static_cast<int>(dest[1]), 128569);
+ ASSERT_TRUE(dest[2] == L'\0');
+ // Should not count null terminator in number
+ ASSERT_EQ(static_cast<int>(n), 2);
+ // Should now be a nullptr
+ ASSERT_EQ(src, nullptr);
+}
+
+TEST_F(LlvmLibcMBSRToWCSTest, MixedNumberOfBytes) {
+ // 'A', sigma symbol 'Σ', recycling symbol '♻', laughing cat emoji '😹'
+ const char *src = "A\xce\xa3\xe2\x99\xbb\xf0\x9f\x98\xb9";
+ const char *original = src;
+ wchar_t dest[5];
+ size_t n = LIBC_NAMESPACE::mbsrtowcs(dest, &src, 4, nullptr);
+ ASSERT_ERRNO_SUCCESS();
+ ASSERT_EQ(static_cast<char>(dest[0]), 'A');
+ ASSERT_EQ(static_cast<int>(dest[1]), 931);
+ ASSERT_EQ(static_cast<int>(dest[2]), 9851);
+ ASSERT_EQ(static_cast<int>(dest[3]), 128569);
+ // Should point to null terminator (byte at 10th index)
+ ASSERT_EQ(src, original + 10);
+ ASSERT_EQ(static_cast<int>(n), 4);
+ n = LIBC_NAMESPACE::mbsrtowcs(dest + 4, &src, 4, nullptr);
+ ASSERT_TRUE(dest[4] == L'\0');
+ ASSERT_ERRNO_SUCCESS();
+ // Should not count null terminator in number
+ ASSERT_EQ(static_cast<int>(n), 0);
+ // Should now be a nullptr
+ ASSERT_EQ(src, nullptr);
+}
+
+TEST_F(LlvmLibcMBSRToWCSTest, ReadLessThanStringLength) {
+ // Four laughing cat emojis "😹😹😹😹"
+ const char *src =
+ "\xf0\x9f\x98\xb9\xf0\x9f\x98\xb9\xf0\x9f\x98\xb9\xf0\x9f\x98\xb9";
+ const char *original = src;
+ wchar_t dest[5] = {L'a', L'b', L'c', L'd', L'e'};
+ size_t n = LIBC_NAMESPACE::mbsrtowcs(dest, &src, 3, nullptr);
+ ASSERT_ERRNO_SUCCESS();
+ // Should have read 3 emojis
+ ASSERT_EQ(static_cast<int>(n), 3);
+ ASSERT_EQ(static_cast<int>(dest[0]), 128569);
+ ASSERT_EQ(static_cast<int>(dest[1]), 128569);
+ ASSERT_EQ(static_cast<int>(dest[2]), 128569);
+ ASSERT_TRUE(dest[3] == L'd');
+ ASSERT_TRUE(dest[4] == L'e');
+ // Read three laughing cat emojis, 12 bytes
+ ASSERT_EQ(src, original + 12);
+}
+
+TEST_F(LlvmLibcMBSRToWCSTest, InvalidFirstByte) {
+ // 0x80 is invalid first byte of mb character
+ const char *src =
+ "\x80\x9f\x98\xb9\xf0\x9f\x98\xb9\xf0\x9f\x98\xb9\xf0\x9f\x98\xb9";
+ const char *original = src;
+ wchar_t dest[3];
+ size_t n = LIBC_NAMESPACE::mbsrtowcs(dest, &src, 3, nullptr);
+ // Should return error and set errno
+ ASSERT_EQ(static_cast<int>(n), -1);
+ ASSERT_ERRNO_EQ(EILSEQ);
+ // Should not update pointer
+ ASSERT_EQ(src, original);
+}
+
+TEST_F(LlvmLibcMBSRToWCSTest, InvalidMiddleByte) {
+ // The 7th byte is invalid for a 4 byte character
+ const char *src =
+ "\xf0\x9f\x98\xb9\xf0\x9f\xf0\xb9\xf0\x9f\x98\xb9\xf0\x9f\x98\xb9";
+ const char *original = src;
+ wchar_t dest[3];
+ size_t n = LIBC_NAMESPACE::mbsrtowcs(dest, &src, 5, nullptr);
+ // Should return error, set errno, and not update the pointer
+ ASSERT_EQ(static_cast<int>(n), -1);
+ ASSERT_ERRNO_EQ(EILSEQ);
+ ASSERT_EQ(src, original);
+}
+
+TEST_F(LlvmLibcMBSRToWCSTest, NullDestination) {
+ // Four laughing cat emojis "😹😹😹😹"
+ const char *src =
+ "\xf0\x9f\x98\xb9\xf0\x9f\x98\xb9\xf0\x9f\x98\xb9\xf0\x9f\x98\xb9";
+ const char *original = src;
+ size_t n = LIBC_NAMESPACE::mbsrtowcs(nullptr, &src, 2, nullptr);
+ ASSERT_ERRNO_SUCCESS();
+ // Null destination should ignore len and read till end of string
+ ASSERT_EQ(static_cast<int>(n), 4);
+ // It should also not change the src pointer
+ ASSERT_EQ(src, original);
+}
+
+TEST_F(LlvmLibcMBSRToWCSTest, ErrnoChecks) {
+ // Two laughing cat emojis and invalid 3rd mb char (3rd byte of it)
+ const char *src =
+ "\xf0\x9f\x98\xb9\xf0\x9f\x98\xb9\xf0\x9f\xf0\xb9\xf0\x9f\x98\xb9";
+ const char *original = src;
+ wchar_t dest[5];
+ // First two bytes are valid --> should not set errno
+ size_t n = LIBC_NAMESPACE::mbsrtowcs(dest, &src, 2, nullptr);
+ ASSERT_ERRNO_SUCCESS();
+ ASSERT_EQ(static_cast<int>(n), 2);
+ ASSERT_EQ(static_cast<int>(dest[0]), 128569);
+ ASSERT_EQ(static_cast<int>(dest[1]), 128569);
+ ASSERT_EQ(src, original + 8);
+
+ // Trying to read the 3rd byte should set errno
+ n = LIBC_NAMESPACE::mbsrtowcs(dest, &src, 2, nullptr);
+ ASSERT_ERRNO_EQ(EILSEQ);
+ ASSERT_EQ(static_cast<int>(n), -1);
+ // Should not move the pointer
+ ASSERT_EQ(src, original + 8);
+}
+
+#if defined(LIBC_ADD_NULL_CHECKS)
+TEST(LlvmLibcMBSRToWCSTest, NullptrCrash) {
+ // Passing in a nullptr should crash the program.
+ EXPECT_DEATH([] { LIBC_NAMESPACE::mbsrtowcs(nullptr, nullptr, 1, nullptr); },
+ WITH_SIGNAL(-1));
+}
+#endif // LIBC_ADD_NULL_CHECKS
diff --git a/libc/test/src/wchar/mbstowcs_test.cpp b/libc/test/src/wchar/mbstowcs_test.cpp
new file mode 100644
index 0000000..f0396e0
--- /dev/null
+++ b/libc/test/src/wchar/mbstowcs_test.cpp
@@ -0,0 +1,171 @@
+//===-- Unittests for mbstowcs --------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "hdr/types/wchar_t.h"
+#include "src/__support/libc_errno.h"
+#include "src/__support/macros/null_check.h"
+#include "src/wchar/mbstowcs.h"
+#include "test/UnitTest/ErrnoCheckingTest.h"
+#include "test/UnitTest/Test.h"
+
+using LlvmLibcMBSToWCSTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
+
+TEST_F(LlvmLibcMBSToWCSTest, OneByteOneChar) {
+ const char *ch = "A";
+ const char *original = ch;
+ wchar_t dest[2];
+ size_t n = LIBC_NAMESPACE::mbstowcs(dest, ch, 1);
+ ASSERT_EQ(static_cast<char>(*dest), 'A');
+ ASSERT_EQ(static_cast<int>(n), 1);
+ // Making sure the pointer is not getting updated
+ ASSERT_EQ(ch, original);
+ ASSERT_ERRNO_SUCCESS();
+
+ n = LIBC_NAMESPACE::mbstowcs(dest + 1, ch + 1, 1);
+ ASSERT_EQ(static_cast<char>(dest[1]), '\0');
+ // Should not include null terminator
+ ASSERT_EQ(static_cast<int>(n), 0);
+ // Making sure the pointer is not getting updated
+ ASSERT_EQ(ch, original);
+ ASSERT_ERRNO_SUCCESS();
+}
+
+TEST_F(LlvmLibcMBSToWCSTest, FourByteOneChar) {
+ const char *src = "\xf0\x9f\x98\xb9"; // laughing cat emoji 😹
+ const char *original = src;
+ wchar_t dest[2];
+ size_t n = LIBC_NAMESPACE::mbstowcs(dest, src, 2);
+ ASSERT_ERRNO_SUCCESS();
+ ASSERT_EQ(static_cast<int>(dest[0]), 128569);
+ ASSERT_TRUE(dest[1] == L'\0');
+ // Should not count null terminator in number
+ ASSERT_EQ(static_cast<int>(n), 1);
+ // Making sure the pointer is not getting updated
+ ASSERT_EQ(src, original);
+}
+
+TEST_F(LlvmLibcMBSToWCSTest, MultiByteTwoCharacters) {
+ // Two laughing cat emojis "😹😹"
+ const char *src = "\xf0\x9f\x98\xb9\xf0\x9f\x98\xb9";
+ const char *original = src;
+ wchar_t dest[3];
+ size_t n = LIBC_NAMESPACE::mbstowcs(dest, src, 3);
+ ASSERT_ERRNO_SUCCESS();
+ ASSERT_EQ(static_cast<int>(dest[0]), 128569);
+ ASSERT_EQ(static_cast<int>(dest[1]), 128569);
+ ASSERT_TRUE(dest[2] == L'\0');
+ // Should not count null terminator in number
+ ASSERT_EQ(static_cast<int>(n), 2);
+ // Making sure the pointer is not getting updated
+ ASSERT_EQ(src, original);
+}
+
+TEST_F(LlvmLibcMBSToWCSTest, MixedNumberOfBytes) {
+ // 'A', sigma symbol 'Σ', recycling symbol '♻', laughing cat emoji '😹'
+ const char *src = "A\xce\xa3\xe2\x99\xbb\xf0\x9f\x98\xb9";
+ const char *original = src;
+ wchar_t dest[5];
+ size_t n = LIBC_NAMESPACE::mbstowcs(dest, src, 5);
+ ASSERT_ERRNO_SUCCESS();
+ ASSERT_EQ(static_cast<char>(dest[0]), 'A');
+ ASSERT_EQ(static_cast<int>(dest[1]), 931);
+ ASSERT_EQ(static_cast<int>(dest[2]), 9851);
+ ASSERT_EQ(static_cast<int>(dest[3]), 128569);
+ ASSERT_TRUE(dest[4] == L'\0');
+ // Should not count null terminator in number
+ ASSERT_EQ(static_cast<int>(n), 4);
+ // Making sure the pointer is not getting updated
+ ASSERT_EQ(src, original);
+}
+
+TEST_F(LlvmLibcMBSToWCSTest, ReadLessThanStringLength) {
+ // Four laughing cat emojis "😹😹😹😹"
+ const char *src =
+ "\xf0\x9f\x98\xb9\xf0\x9f\x98\xb9\xf0\x9f\x98\xb9\xf0\x9f\x98\xb9";
+ const char *original = src;
+ wchar_t dest[5] = {L'a', L'b', L'c', L'd', L'e'};
+ size_t n = LIBC_NAMESPACE::mbstowcs(dest, src, 3);
+ ASSERT_ERRNO_SUCCESS();
+ // Should have read 3 emojis
+ ASSERT_EQ(static_cast<int>(n), 3);
+ ASSERT_EQ(static_cast<int>(dest[0]), 128569);
+ ASSERT_EQ(static_cast<int>(dest[1]), 128569);
+ ASSERT_EQ(static_cast<int>(dest[2]), 128569);
+ ASSERT_TRUE(dest[3] == L'd');
+ ASSERT_TRUE(dest[4] == L'e');
+ // Making sure the pointer is not getting updated
+ ASSERT_EQ(src, original);
+}
+
+TEST_F(LlvmLibcMBSToWCSTest, InvalidFirstByte) {
+ // 0x80 is invalid first byte of mb character
+ const char *src =
+ "\x80\x9f\x98\xb9\xf0\x9f\x98\xb9\xf0\x9f\x98\xb9\xf0\x9f\x98\xb9";
+ wchar_t dest[3];
+ size_t n = LIBC_NAMESPACE::mbstowcs(dest, src, 3);
+ // Should return error and set errno
+ ASSERT_EQ(static_cast<int>(n), -1);
+ ASSERT_ERRNO_EQ(EILSEQ);
+}
+
+TEST_F(LlvmLibcMBSToWCSTest, InvalidMiddleByte) {
+ // The 7th byte is invalid for a 4 byte character
+ const char *src =
+ "\xf0\x9f\x98\xb9\xf0\x9f\xf0\xb9\xf0\x9f\x98\xb9\xf0\x9f\x98\xb9";
+ const char *original = src;
+ wchar_t dest[3];
+ size_t n = LIBC_NAMESPACE::mbstowcs(dest, src, 5);
+ // Should return error and set errno
+ ASSERT_EQ(static_cast<int>(n), -1);
+ ASSERT_ERRNO_EQ(EILSEQ);
+ // Making sure the pointer is not getting updated
+ ASSERT_EQ(src, original);
+}
+
+TEST_F(LlvmLibcMBSToWCSTest, NullDestination) {
+ // Four laughing cat emojis "😹😹😹😹"
+ const char *src =
+ "\xf0\x9f\x98\xb9\xf0\x9f\x98\xb9\xf0\x9f\x98\xb9\xf0\x9f\x98\xb9";
+ const char *original = src;
+ size_t n = LIBC_NAMESPACE::mbstowcs(nullptr, src, 2);
+ ASSERT_ERRNO_SUCCESS();
+ // Null destination should ignore len and read till end of string
+ ASSERT_EQ(static_cast<int>(n), 4);
+ // Making sure the pointer is not getting updated
+ ASSERT_EQ(src, original);
+}
+
+TEST_F(LlvmLibcMBSToWCSTest, ErrnoChecks) {
+ // Two laughing cat emojis and invalid 3rd mb char (3rd byte of it)
+ const char *src =
+ "\xf0\x9f\x98\xb9\xf0\x9f\x98\xb9\xf0\x9f\xf0\xb9\xf0\x9f\x98\xb9";
+ const char *original = src;
+ wchar_t dest[5];
+ // First two bytes are valid --> should not set errno
+ size_t n = LIBC_NAMESPACE::mbstowcs(dest, src, 2);
+ ASSERT_ERRNO_SUCCESS();
+ ASSERT_EQ(static_cast<int>(n), 2);
+ ASSERT_EQ(static_cast<int>(dest[0]), 128569);
+ ASSERT_EQ(static_cast<int>(dest[1]), 128569);
+ // Making sure the pointer is not getting updated
+ ASSERT_EQ(src, original);
+ // Trying to read the 3rd byte should set errno
+ n = LIBC_NAMESPACE::mbstowcs(dest, src + 2, 2);
+ ASSERT_ERRNO_EQ(EILSEQ);
+ ASSERT_EQ(static_cast<int>(n), -1);
+ // Making sure the pointer is not getting updated
+ ASSERT_EQ(src, original);
+}
+
+#if defined(LIBC_ADD_NULL_CHECKS)
+TEST(LlvmLibcMBSToWCSTest, NullptrCrash) {
+ // Passing in a nullptr should crash the program.
+ EXPECT_DEATH([] { LIBC_NAMESPACE::mbstowcs(nullptr, nullptr, 1); },
+ WITH_SIGNAL(-1));
+}
+#endif // LIBC_ADD_NULL_CHECKS