blob: a6260275c42354f3c268b43a474aec42dd4c7b10 [file] [log] [blame]
// Copyright 2021 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 "pw_sync/inline_borrowable.h"
#include <array>
#include <chrono>
#include <tuple>
#include "gtest/gtest.h"
#include "pw_sync/interrupt_spin_lock.h"
#include "pw_sync/lock_annotations.h"
#include "pw_sync/mutex.h"
namespace pw::sync {
namespace {
using namespace std::chrono_literals;
// A trivial type that is copyable and movable.
struct TrivialType {
bool yes() const { return true; }
};
// A custom type that is neither copyable nor movable.
class CustomType {
public:
constexpr CustomType(int x, int y) : x_(x), y_(y) {}
CustomType(const CustomType&) = delete;
CustomType& operator=(const CustomType&) = delete;
CustomType(CustomType&&) = delete;
CustomType&& operator=(CustomType&&) = delete;
std::pair<int, int> data() const { return std::make_pair(x_, y_); }
private:
int x_, y_;
};
// A custom lockable interface.
class PW_LOCKABLE("VirtualCustomLocakble") VirtualCustomLockable {
public:
virtual ~VirtualCustomLockable() {}
virtual void lock() PW_EXCLUSIVE_LOCK_FUNCTION() = 0;
virtual void unlock() PW_UNLOCK_FUNCTION() = 0;
};
// A custom mutex type that requires a constructor parameter.
class PW_LOCKABLE("VirtualCustomMutex") VirtualCustomMutex
: public VirtualCustomLockable {
public:
explicit VirtualCustomMutex(int id) : mutex_{}, id_{id} {}
void lock() override PW_EXCLUSIVE_LOCK_FUNCTION() { mutex_.lock(); }
void unlock() override PW_UNLOCK_FUNCTION() { mutex_.unlock(); }
int id() const { return id_; }
private:
pw::sync::Mutex mutex_;
int id_;
};
TEST(InlineBorrowableTest, TestTrivialType) {
InlineBorrowable<TrivialType> trivial;
EXPECT_TRUE(trivial.acquire()->yes());
}
TEST(InlineBorrowableTest, TestCustomTypeInPlace) {
InlineBorrowable<CustomType> custom(std::in_place, 1, 2);
EXPECT_EQ(custom.acquire()->data(), std::make_pair(1, 2));
}
TEST(InlineBorrowableTest, TestCustomTypeFromTuple) {
InlineBorrowable<CustomType> custom(std::make_tuple(1, 2));
EXPECT_EQ(custom.acquire()->data(), std::make_pair(1, 2));
}
TEST(InlineBorrowableTest, TestCustomTypeFromFactory) {
InlineBorrowable<CustomType> custom([] { return CustomType(1, 2); });
EXPECT_EQ(custom.acquire()->data(), std::make_pair(1, 2));
}
TEST(InlineBorrowableTest, TestTrivialTypeWithInterruptSpinLock) {
InlineBorrowable<TrivialType, VirtualInterruptSpinLock>
trivial_interrupt_safe;
EXPECT_TRUE(trivial_interrupt_safe.acquire()->yes());
}
TEST(InlineBorrowableTest, TestCustomTypeWithInterruptSpinLock) {
InlineBorrowable<CustomType, VirtualInterruptSpinLock> custom_interrupt_safe(
std::in_place, 1, 2);
EXPECT_EQ(custom_interrupt_safe.acquire()->data(), std::make_pair(1, 2));
}
TEST(InlineBorrowableTest, TestCustomTypeWithCustomMutexFromTuple) {
InlineBorrowable<CustomType, VirtualCustomMutex, VirtualCustomLockable>
custom_mutex(std::make_tuple(1, 2), std::make_tuple(42));
EXPECT_EQ(custom_mutex.acquire()->data(), std::make_pair(1, 2));
}
TEST(InlineBorrowableTest, TestCustomTypeWithCustomMutexFromFactory) {
InlineBorrowable<CustomType, VirtualCustomMutex, VirtualCustomLockable>
custom_mutex([] { return CustomType(1, 2); },
[] { return VirtualCustomMutex(42); });
EXPECT_EQ(custom_mutex.acquire()->data(), std::make_pair(1, 2));
}
TEST(InlineBorrowableTest, TestArrayAggregateInitializationInPlace) {
using ArrayAggregate = std::array<int, 2>;
InlineBorrowable<ArrayAggregate> aggregate{std::in_place, 1, 2};
EXPECT_EQ((*aggregate.acquire())[0], 1);
EXPECT_EQ((*aggregate.acquire())[1], 2);
}
struct StructAggregate {
int a;
int b;
};
TEST(InlineBorrowableTest, TestStructAggregateInitializationInPlace) {
InlineBorrowable<StructAggregate> aggregate{std::in_place, 1, 2};
EXPECT_EQ(aggregate.acquire()->a, 1);
EXPECT_EQ(aggregate.acquire()->b, 2);
}
TEST(InlineBorrowableTest, TestStructAggregateInitializationFromFactory) {
InlineBorrowable<StructAggregate> aggregate(
[]() -> StructAggregate { return {.a = 1, .b = 2}; });
EXPECT_EQ(aggregate.acquire()->a, 1);
EXPECT_EQ(aggregate.acquire()->b, 2);
}
TEST(InlineBorrowableTest,
TestStructAggregateInitializationFromMutableFactory) {
int i = 0;
auto factory = [&i]() mutable -> StructAggregate {
i++;
return {.a = 1, .b = 2};
};
InlineBorrowable<StructAggregate> aggregate(factory);
EXPECT_EQ(aggregate.acquire()->a, 1);
EXPECT_EQ(aggregate.acquire()->b, 2);
}
struct ReferenceTypes {
ReferenceTypes(const int& a, int& b, BorrowedPointer<int>&& c)
: in(a), out(b), borrowed(std::move(c)) {}
const int& in;
int& out;
BorrowedPointer<int> borrowed; // move-only type
};
class InlineBorrowableReferenceTypesTest : public ::testing::Test {
protected:
int input_ = 1;
int output_ = 2;
InlineBorrowable<int> borrowable_{std::in_place, 3};
void Validate(BorrowedPointer<ReferenceTypes>&& references) {
EXPECT_EQ(references->in, 1);
EXPECT_EQ(references->out, 2);
EXPECT_EQ(*references->borrowed, 3);
references->out = -2;
EXPECT_EQ(output_, -2);
}
};
TEST_F(InlineBorrowableReferenceTypesTest, TestInPlace) {
InlineBorrowable<ReferenceTypes> references(
std::in_place, input_, output_, borrowable_.acquire());
Validate(references.acquire());
}
TEST_F(InlineBorrowableReferenceTypesTest, TestFromTuple) {
InlineBorrowable<ReferenceTypes> references(
std::forward_as_tuple(input_, output_, borrowable_.acquire()));
Validate(references.acquire());
}
TEST_F(InlineBorrowableReferenceTypesTest, TestFromFactory) {
InlineBorrowable<ReferenceTypes> references(
[&] { return ReferenceTypes(input_, output_, borrowable_.acquire()); });
Validate(references.acquire());
}
} // namespace
} // namespace pw::sync