You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
334 lines
9.5 KiB
334 lines
9.5 KiB
// Copyright 2019 The Pigweed Authors
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
|
// use this file except in compliance with the License. You may obtain a copy of
|
|
// the License at
|
|
//
|
|
// https://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
// License for the specific language governing permissions and limitations under
|
|
// the License.
|
|
|
|
// Include all of the provided headers, even if they aren't tested.
|
|
#include <algorithm>
|
|
#include <array>
|
|
#include <cinttypes>
|
|
#include <cmath>
|
|
#include <cstdarg>
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <cstdio>
|
|
#include <cstring>
|
|
#include <initializer_list>
|
|
#include <iterator>
|
|
#include <limits>
|
|
#include <new>
|
|
#include <string_view>
|
|
#include <type_traits>
|
|
#include <utility>
|
|
|
|
#include "pw_preprocessor/compiler.h"
|
|
|
|
namespace {
|
|
|
|
// In order to test this file without dependencies on the C++ standard library,
|
|
// this file needs to be fully isolated from the regular Pigweed testing
|
|
// infrastructure. pw_unit_test's dependencies do compile with the C++ standard
|
|
// library, which could conflict with pw_minimal_cpp_stdlib.
|
|
//
|
|
// SimpleTest provides the basic features of pw_unit_test without dependencies.
|
|
class SimpleTest {
|
|
public:
|
|
virtual ~SimpleTest() = default;
|
|
|
|
static bool RunAllTests() {
|
|
for (SimpleTest* test = all_tests; test != nullptr; test = test->next_) {
|
|
test->Run();
|
|
if (!test->passed_) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
protected:
|
|
SimpleTest() : next_(all_tests) { all_tests = this; }
|
|
|
|
void RecordTestFailure() { passed_ = false; }
|
|
|
|
private:
|
|
virtual void Run() = 0;
|
|
|
|
static SimpleTest* all_tests;
|
|
|
|
bool passed_ = true;
|
|
SimpleTest* next_;
|
|
};
|
|
|
|
SimpleTest* SimpleTest::all_tests = nullptr;
|
|
|
|
#define EXPECT_EQ(lhs, rhs) \
|
|
do { \
|
|
if ((lhs) != (rhs)) { \
|
|
RecordTestFailure(); \
|
|
} \
|
|
} while (0)
|
|
|
|
#define EXPECT_TRUE(expr) EXPECT_EQ(true, expr)
|
|
#define EXPECT_FALSE(expr) EXPECT_EQ(false, expr)
|
|
#define EXPECT_STREQ(lhs, rhs) EXPECT_EQ(std::strcmp((lhs), (rhs)), 0)
|
|
|
|
#define TEST(suite, name) \
|
|
class SimpleTest_##suite##_##name : public SimpleTest { \
|
|
void Run() override; \
|
|
} test_##suite##_##name; \
|
|
\
|
|
void SimpleTest_##suite##_##name::Run()
|
|
|
|
TEST(Algorithm, Basic) {
|
|
static_assert(std::min(1, 2) == 1);
|
|
static_assert(std::max(1, 2) == 2);
|
|
|
|
EXPECT_EQ(std::forward<int>(2), 2);
|
|
}
|
|
|
|
TEST(Algorithm, Copy) {
|
|
constexpr size_t kCopyOffset = 1;
|
|
std::array<int, 3> foo{3, 2, 1};
|
|
std::array<int, 5> bar{0};
|
|
|
|
// Ensure zero-element iterator doesn't modify the destination object when
|
|
// copied.
|
|
int temp = foo[0];
|
|
std::copy(foo.end(), foo.end(), bar.begin());
|
|
EXPECT_EQ(foo[0], temp);
|
|
|
|
// Copy a single element.
|
|
std::array<int, 1> one{-101};
|
|
std::copy(one.begin(), one.end(), foo.begin());
|
|
EXPECT_EQ(foo[0], -101);
|
|
|
|
auto copy_end = std::copy(foo.begin(), foo.end(), bar.begin() + kCopyOffset);
|
|
// Verify the iterator points to the end of the copied region.
|
|
EXPECT_EQ(copy_end, bar.begin() + foo.size() + kCopyOffset);
|
|
|
|
// Verify all the values were properly copied from foo to bar.
|
|
{
|
|
size_t i = 0;
|
|
for (auto it = bar.begin() + kCopyOffset; it != copy_end; ++it) {
|
|
EXPECT_EQ(*it, foo[i++]);
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST(Algorithm, Find) {
|
|
std::array<int, 5> foo{3, 2, 1, 42, 17};
|
|
// Ensure a value in the middle of the array is properly found.
|
|
EXPECT_EQ(*std::find(std::begin(foo), std::end(foo), 42), 42);
|
|
|
|
// Ensure the iterator returned by find() matches the expected location of the
|
|
// element.
|
|
EXPECT_EQ(std::find(std::begin(foo), std::end(foo), 42), std::begin(foo) + 3);
|
|
|
|
// Ensure an element at the beginning of an array is found.
|
|
EXPECT_EQ(*std::find(std::begin(foo), std::end(foo), 3), foo[0]);
|
|
|
|
// Ensure an element at the end of an array is found.
|
|
EXPECT_EQ(*std::find(std::begin(foo), std::end(foo), 17),
|
|
foo[foo.size() - 1]);
|
|
}
|
|
|
|
TEST(Algorithm, NotFound) {
|
|
std::array<int, 3> foo{3, 2, 1};
|
|
|
|
// Ensure that if an element is not found, an iterator matching foo.end() is
|
|
// returned.
|
|
EXPECT_EQ(std::find(std::begin(foo), std::end(foo), -99), std::end(foo));
|
|
|
|
// Ensure that a zero-element iterator range returns the end iterator passed
|
|
// to std::find().
|
|
EXPECT_EQ(std::find(std::end(foo), std::end(foo), 3), std::end(foo));
|
|
}
|
|
|
|
TEST(Array, Basic) {
|
|
constexpr std::array<int, 4> array{0, 1, 2, 3};
|
|
|
|
static_assert(array[2] == 2);
|
|
|
|
for (int i = 0; i < static_cast<int>(array.size()); ++i) {
|
|
EXPECT_EQ(i, array[i]);
|
|
}
|
|
}
|
|
|
|
TEST(Cmath, Basic) PW_NO_SANITIZE("float-divide-by-zero") {
|
|
EXPECT_EQ(std::abs(-1), 1);
|
|
EXPECT_EQ(std::abs(1), 1);
|
|
|
|
// Although Clang/LLVM do not fully support __STDC_IEC_559__, they do have the
|
|
// necessary IEEE 754 support for floating point division by zero.
|
|
EXPECT_TRUE(std::isfinite(1.0));
|
|
EXPECT_FALSE(std::isfinite(1.0 / 0.0));
|
|
|
|
EXPECT_FALSE(std::isnan(1.0));
|
|
EXPECT_TRUE(std::isnan(0.0 / 0.0));
|
|
|
|
EXPECT_FALSE(std::signbit(1.0));
|
|
EXPECT_TRUE(std::signbit(-1.0));
|
|
}
|
|
|
|
TEST(Cstddef, Basic) {
|
|
using std::byte;
|
|
byte foo = byte{12};
|
|
EXPECT_EQ(foo, byte{12});
|
|
}
|
|
|
|
TEST(Iterator, Basic) {
|
|
std::array<int, 3> foo{3, 2, 1};
|
|
|
|
EXPECT_EQ(std::data(foo), foo.data());
|
|
EXPECT_EQ(std::size(foo), foo.size());
|
|
|
|
EXPECT_EQ(*std::begin(foo), foo[0]);
|
|
EXPECT_EQ(std::end(foo), std::begin(foo) + foo.size());
|
|
|
|
foo.fill(99);
|
|
EXPECT_EQ(foo[0], 99);
|
|
EXPECT_EQ(foo[1], 99);
|
|
EXPECT_EQ(foo[2], 99);
|
|
}
|
|
|
|
template <typename T>
|
|
int SumFromInitializerList(std::initializer_list<T> values) {
|
|
int sum = 0;
|
|
for (auto value : values) {
|
|
sum += value;
|
|
}
|
|
return sum;
|
|
}
|
|
TEST(InitializerList, Empty) {
|
|
std::initializer_list<int> mt;
|
|
EXPECT_EQ(0, SumFromInitializerList(mt));
|
|
|
|
EXPECT_EQ(0, SumFromInitializerList<float>({}));
|
|
}
|
|
|
|
TEST(InitializerList, Declared) {
|
|
std::initializer_list<char> list{'\3', '\3', '\4'};
|
|
EXPECT_EQ(10, SumFromInitializerList(list));
|
|
}
|
|
|
|
TEST(InitializerList, Inline) {
|
|
EXPECT_EQ(42, SumFromInitializerList<long>({42}));
|
|
EXPECT_EQ(2, SumFromInitializerList<bool>({true, false, true}));
|
|
EXPECT_EQ(15, SumFromInitializerList({1, 2, 3, 4, 5}));
|
|
}
|
|
|
|
TEST(Limits, Basic) {
|
|
static_assert(std::numeric_limits<unsigned char>::is_specialized);
|
|
static_assert(std::numeric_limits<unsigned char>::is_integer);
|
|
static_assert(std::numeric_limits<unsigned char>::min() == 0u);
|
|
static_assert(std::numeric_limits<unsigned char>::max() == 255u);
|
|
|
|
static_assert(std::numeric_limits<signed char>::is_specialized);
|
|
static_assert(std::numeric_limits<signed char>::is_integer);
|
|
static_assert(std::numeric_limits<signed char>::min() == -128);
|
|
static_assert(std::numeric_limits<signed char>::max() == 127);
|
|
|
|
// Assume 64-bit long long
|
|
static_assert(std::numeric_limits<long long>::is_specialized);
|
|
static_assert(std::numeric_limits<long long>::is_integer);
|
|
static_assert(std::numeric_limits<long long>::min() ==
|
|
(-9223372036854775807ll - 1));
|
|
static_assert(std::numeric_limits<long long>::max() == 9223372036854775807ll);
|
|
|
|
static_assert(std::numeric_limits<unsigned long long>::is_specialized);
|
|
static_assert(std::numeric_limits<unsigned long long>::is_integer);
|
|
static_assert(std::numeric_limits<unsigned long long>::min() == 0u);
|
|
static_assert(std::numeric_limits<unsigned long long>::max() ==
|
|
18446744073709551615ull);
|
|
}
|
|
|
|
TEST(New, PlacementNew) {
|
|
alignas(sizeof(int)) unsigned char value[sizeof(int)];
|
|
new (value) int(1234);
|
|
|
|
int int_value;
|
|
std::memcpy(&int_value, value, sizeof(int_value));
|
|
EXPECT_EQ(1234, int_value);
|
|
}
|
|
|
|
TEST(New, Launder) {
|
|
unsigned char value[4];
|
|
int* int_ptr = std::launder(reinterpret_cast<int*>(value));
|
|
EXPECT_EQ(static_cast<void*>(int_ptr), static_cast<void*>(value));
|
|
}
|
|
|
|
TEST(StringView, Basic) {
|
|
constexpr std::string_view value("1234567890");
|
|
static_assert(value.size() == 10);
|
|
static_assert(value[1] == '2');
|
|
|
|
char buffer[] = "!!!!!";
|
|
constexpr size_t buffer_size = sizeof(buffer) - 1; // always keep the \0
|
|
|
|
value.copy(buffer, buffer_size, 10);
|
|
EXPECT_STREQ(buffer, "!!!!!");
|
|
|
|
value.copy(buffer, buffer_size, 9);
|
|
EXPECT_STREQ(buffer, "0!!!!");
|
|
|
|
value.copy(buffer, buffer_size, 2);
|
|
EXPECT_STREQ(buffer, "34567");
|
|
|
|
value.copy(buffer, buffer_size);
|
|
EXPECT_STREQ(buffer, "12345");
|
|
}
|
|
|
|
TEST(TypeTraits, Basic) {
|
|
static_assert(std::is_integral_v<bool>);
|
|
static_assert(!std::is_integral_v<float>);
|
|
|
|
static_assert(std::is_floating_point_v<float>);
|
|
static_assert(!std::is_floating_point_v<bool>);
|
|
|
|
static_assert(std::is_same_v<float, float>);
|
|
static_assert(!std::is_same_v<char, unsigned char>);
|
|
}
|
|
|
|
struct MoveTester {
|
|
MoveTester(int value) : magic_value(value), moved(false) {}
|
|
|
|
MoveTester(const MoveTester&) = default;
|
|
|
|
MoveTester(MoveTester&& other) : magic_value(other.magic_value), moved(true) {
|
|
other.magic_value = 0xffff;
|
|
}
|
|
|
|
int magic_value;
|
|
bool moved;
|
|
};
|
|
|
|
TEST(Utility, Move) {
|
|
MoveTester test(123);
|
|
|
|
MoveTester copied(test);
|
|
EXPECT_EQ(copied.magic_value, 123);
|
|
EXPECT_FALSE(copied.moved);
|
|
|
|
MoveTester moved(std::move(copied));
|
|
EXPECT_EQ(123, moved.magic_value);
|
|
EXPECT_EQ(0xffff, copied.magic_value);
|
|
EXPECT_TRUE(moved.moved);
|
|
}
|
|
|
|
} // namespace
|
|
|
|
namespace pw::minimal_cpp_stdlib {
|
|
|
|
bool RunAllTests() { return SimpleTest::RunAllTests(); }
|
|
|
|
} // namespace pw::minimal_cpp_stdlib
|