128 changes: 85 additions & 43 deletions libc/test/src/string/memmove_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,64 +8,106 @@

#include "src/__support/CPP/ArrayRef.h"
#include "src/string/memmove.h"
#include "utils/UnitTest/MemoryMatcher.h"
#include "utils/UnitTest/Test.h"

class LlvmLibcMemmoveTest : public __llvm_libc::testing::Test {
public:
void check_memmove(void *dst, const void *src, size_t count,
const unsigned char *str,
const __llvm_libc::cpp::ArrayRef<unsigned char> expected) {
void *result = __llvm_libc::memmove(dst, src, count);
// Making sure the pointer returned is same with `dst`.
EXPECT_EQ(result, dst);
// `expected` is designed according to `str`.
// `dst` and `src` might be part of `str`.
// Making sure `str` is same with `expected`.
for (size_t i = 0; i < expected.size(); ++i)
EXPECT_EQ(str[i], expected[i]);
}
};
using __llvm_libc::cpp::Array;
using __llvm_libc::cpp::ArrayRef;
using __llvm_libc::cpp::MutableArrayRef;

TEST_F(LlvmLibcMemmoveTest, MoveZeroByte) {
unsigned char dst[] = {'a', 'b'};
const unsigned char src[] = {'y', 'z'};
const unsigned char expected[] = {'a', 'b'};
check_memmove(dst, src, 0, dst, expected);
TEST(LlvmLibcMemmoveTest, MoveZeroByte) {
char Buffer[] = {'a', 'b', 'y', 'z'};
const char Expected[] = {'a', 'b', 'y', 'z'};
void *const Dst = Buffer;
void *const Ret = __llvm_libc::memmove(Dst, Buffer + 2, 0);
EXPECT_EQ(Ret, Dst);
EXPECT_MEM_EQ(Buffer, Expected);
}

TEST_F(LlvmLibcMemmoveTest, OverlapThatDstAndSrcPointToSameAddress) {
unsigned char str[] = {'a', 'b'};
const unsigned char expected[] = {'a', 'b'};
check_memmove(str, str, 1, str, expected);
TEST(LlvmLibcMemmoveTest, DstAndSrcPointToSameAddress) {
char Buffer[] = {'a', 'b'};
const char Expected[] = {'a', 'b'};
void *const Dst = Buffer;
void *const Ret = __llvm_libc::memmove(Dst, Buffer, 1);
EXPECT_EQ(Ret, Dst);
EXPECT_MEM_EQ(Buffer, Expected);
}

TEST_F(LlvmLibcMemmoveTest, OverlapThatDstStartsBeforeSrc) {
TEST(LlvmLibcMemmoveTest, DstStartsBeforeSrc) {
// Set boundary at beginning and end for not overstepping when
// copy forward or backward.
unsigned char str[] = {'z', 'a', 'b', 'c', 'z'};
const unsigned char expected[] = {'z', 'b', 'c', 'c', 'z'};
// `dst` is `&str[1]`.
check_memmove(&str[1], &str[2], 2, str, expected);
char Buffer[] = {'z', 'a', 'b', 'c', 'z'};
const char Expected[] = {'z', 'b', 'c', 'c', 'z'};
void *const Dst = Buffer + 1;
void *const Ret = __llvm_libc::memmove(Dst, Buffer + 2, 2);
EXPECT_EQ(Ret, Dst);
EXPECT_MEM_EQ(Buffer, Expected);
}

TEST_F(LlvmLibcMemmoveTest, OverlapThatDstStartsAfterSrc) {
unsigned char str[] = {'z', 'a', 'b', 'c', 'z'};
const unsigned char expected[] = {'z', 'a', 'a', 'b', 'z'};
check_memmove(&str[2], &str[1], 2, str, expected);
TEST(LlvmLibcMemmoveTest, DstStartsAfterSrc) {
char Buffer[] = {'z', 'a', 'b', 'c', 'z'};
const char Expected[] = {'z', 'a', 'a', 'b', 'z'};
void *const Dst = Buffer + 2;
void *const Ret = __llvm_libc::memmove(Dst, Buffer + 1, 2);
EXPECT_EQ(Ret, Dst);
EXPECT_MEM_EQ(Buffer, Expected);
}

// e.g. `dst` follow `src`.
// e.g. `Dst` follow `src`.
// str: [abcdefghij]
// [__src_____]
// [_____dst__]
TEST_F(LlvmLibcMemmoveTest, SrcFollowDst) {
unsigned char str[] = {'z', 'a', 'b', 'z'};
const unsigned char expected[] = {'z', 'b', 'b', 'z'};
check_memmove(&str[1], &str[2], 1, str, expected);
// [_____Dst__]
TEST(LlvmLibcMemmoveTest, SrcFollowDst) {
char Buffer[] = {'z', 'a', 'b', 'z'};
const char Expected[] = {'z', 'b', 'b', 'z'};
void *const Dst = Buffer + 1;
void *const Ret = __llvm_libc::memmove(Dst, Buffer + 2, 1);
EXPECT_EQ(Ret, Dst);
EXPECT_MEM_EQ(Buffer, Expected);
}

TEST(LlvmLibcMemmoveTest, DstFollowSrc) {
char Buffer[] = {'z', 'a', 'b', 'z'};
const char Expected[] = {'z', 'a', 'a', 'z'};
void *const Dst = Buffer + 2;
void *const Ret = __llvm_libc::memmove(Dst, Buffer + 1, 1);
EXPECT_EQ(Ret, Dst);
EXPECT_MEM_EQ(Buffer, Expected);
}

TEST_F(LlvmLibcMemmoveTest, DstFollowSrc) {
unsigned char str[] = {'z', 'a', 'b', 'z'};
const unsigned char expected[] = {'z', 'a', 'a', 'z'};
check_memmove(&str[2], &str[1], 1, str, expected);
static constexpr int kMaxSize = 512;

char GetRandomChar() {
static constexpr const uint64_t A = 1103515245;
static constexpr const uint64_t C = 12345;
static constexpr const uint64_t M = 1ULL << 31;
static uint64_t Seed = 123456789;
Seed = (A * Seed + C) % M;
return Seed;
}

void Randomize(MutableArrayRef<char> Buffer) {
for (auto &current : Buffer)
current = GetRandomChar();
}

TEST(LlvmLibcMemmoveTest, Thorough) {
using LargeBuffer = Array<char, 3 * kMaxSize>;
LargeBuffer GroundTruth;
Randomize(GroundTruth);
for (int Size = 0; Size < kMaxSize; ++Size) {
for (int Offset = -Size; Offset < Size; ++Offset) {
LargeBuffer Buffer = GroundTruth;
LargeBuffer Expected = GroundTruth;
size_t DstOffset = kMaxSize;
size_t SrcOffset = kMaxSize + Offset;
for (int I = 0; I < Size; ++I)
Expected[DstOffset + I] = GroundTruth[SrcOffset + I];
void *const Dst = Buffer.data() + DstOffset;
void *const Ret =
__llvm_libc::memmove(Dst, Buffer.data() + SrcOffset, Size);
EXPECT_EQ(Ret, Dst);
EXPECT_MEM_EQ(Buffer, Expected);
}
}
}
13 changes: 13 additions & 0 deletions libc/utils/UnitTest/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,16 @@ add_dependencies(
libc.src.__support.CPP.standalone_cpp
libc.src.__support.FPUtil.fputil
)

add_library(
LibcMemoryHelpers
MemoryMatcher.h
MemoryMatcher.cpp
)
target_include_directories(LibcMemoryHelpers PUBLIC ${LIBC_SOURCE_DIR})
target_link_libraries(LibcMemoryHelpers LibcUnitTest)
add_dependencies(
LibcMemoryHelpers
LibcUnitTest
libc.src.__support.CPP.standalone_cpp
)
46 changes: 46 additions & 0 deletions libc/utils/UnitTest/MemoryMatcher.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
//===-- MemoryMatcher.cpp ---------------------------------------*- C++ -*-===//
//
// 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 "MemoryMatcher.h"

namespace __llvm_libc {
namespace memory {
namespace testing {

bool MemoryMatcher::match(MemoryView actualValue) {
actual = actualValue;
return expected.equals(actual);
}

void display(testutils::StreamWrapper &Stream, char C) {
const auto print = [&Stream](unsigned char I) {
Stream << static_cast<char>(I < 10 ? '0' + I : 'A' + I - 10);
};
print(static_cast<unsigned char>(C) / 16);
print(static_cast<unsigned char>(C) & 15);
}

void display(testutils::StreamWrapper &Stream, MemoryView View) {
for (auto C : View) {
Stream << ' ';
display(Stream, C);
}
}

void MemoryMatcher::explainError(testutils::StreamWrapper &Stream) {
Stream << "expected :";
display(Stream, expected);
Stream << '\n';
Stream << "actual :";
display(Stream, actual);
Stream << '\n';
}

} // namespace testing
} // namespace memory
} // namespace __llvm_libc
41 changes: 41 additions & 0 deletions libc/utils/UnitTest/MemoryMatcher.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
//===-- MemoryMatcher.h -----------------------------------------*- C++ -*-===//
//
// 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
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_LIBC_UTILS_UNITTEST_MEMORY_MATCHER_H
#define LLVM_LIBC_UTILS_UNITTEST_MEMORY_MATCHER_H

#include "src/__support/CPP/ArrayRef.h"

#include "utils/UnitTest/Test.h"

namespace __llvm_libc {
namespace memory {
namespace testing {

using MemoryView = __llvm_libc::cpp::ArrayRef<char>;

class MemoryMatcher : public __llvm_libc::testing::Matcher<MemoryView> {
MemoryView expected;
MemoryView actual;

public:
MemoryMatcher(MemoryView expectedValue) : expected(expectedValue) {}

bool match(MemoryView actualValue);

void explainError(testutils::StreamWrapper &stream) override;
};

} // namespace testing
} // namespace memory
} // namespace __llvm_libc

#define EXPECT_MEM_EQ(expected, actual) \
EXPECT_THAT(actual, __llvm_libc::memory::testing::MemoryMatcher(expected))

#endif // LLVM_LIBC_UTILS_UNITTEST_MEMORY_MATCHER_H