// 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 #include #include #include "pw_sync/interrupt_spin_lock.h" #include "pw_sync/lock_annotations.h" #include "pw_sync/mutex.h" #include "pw_unit_test/framework.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: explicit constexpr CustomType(int z) : x_(z), y_(-z) {} 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 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 trivial; EXPECT_TRUE(trivial.acquire()->yes()); } TEST(InlineBorrowableTest, TestCustomTypeInPlace1Arg) { InlineBorrowable custom(std::in_place, 1); EXPECT_EQ(custom.acquire()->data(), std::make_pair(1, -1)); } TEST(InlineBorrowableTest, TestCustomTypeInPlace1ArgLValue) { int x = 1; InlineBorrowable custom(std::in_place, x); EXPECT_EQ(custom.acquire()->data(), std::make_pair(x, -x)); } TEST(InlineBorrowableTest, TestCustomTypeInPlace2Arg) { InlineBorrowable custom(std::in_place, 1, 2); EXPECT_EQ(custom.acquire()->data(), std::make_pair(1, 2)); } TEST(InlineBorrowableTest, TestCustomTypeFromTuple) { InlineBorrowable custom(std::make_tuple(1, 2)); EXPECT_EQ(custom.acquire()->data(), std::make_pair(1, 2)); } TEST(InlineBorrowableTest, TestCustomTypeFromFactory) { InlineBorrowable custom([] { return CustomType(1, 2); }); EXPECT_EQ(custom.acquire()->data(), std::make_pair(1, 2)); } TEST(InlineBorrowableTest, TestCustomTypeFromMutableFactory) { int i = 0; auto factory = [&i]() mutable { i++; return CustomType(1, 2); }; InlineBorrowable custom(factory); EXPECT_EQ(custom.acquire()->data(), std::make_pair(1, 2)); } TEST(InlineBorrowableTest, TestTrivialTypeWithInterruptSpinLock) { InlineBorrowable trivial_interrupt_safe; EXPECT_TRUE(trivial_interrupt_safe.acquire()->yes()); } TEST(InlineBorrowableTest, TestCustomTypeWithInterruptSpinLock) { InlineBorrowable custom_interrupt_safe( std::in_place, 1, 2); EXPECT_EQ(custom_interrupt_safe.acquire()->data(), std::make_pair(1, 2)); } TEST(InlineBorrowableTest, TestCustomTypeWithCustomMutexFromTuple) { InlineBorrowable 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 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; InlineBorrowable 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 aggregate{std::in_place, 1, 2}; EXPECT_EQ(aggregate.acquire()->a, 1); EXPECT_EQ(aggregate.acquire()->b, 2); } TEST(InlineBorrowableTest, TestStructAggregateInitializationFromFactory) { InlineBorrowable 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 aggregate(factory); EXPECT_EQ(aggregate.acquire()->a, 1); EXPECT_EQ(aggregate.acquire()->b, 2); } struct ReferenceTypes { ReferenceTypes(const int& a, int& b, BorrowedPointer&& c) : in(a), out(b), borrowed(std::move(c)) {} const int& in; int& out; BorrowedPointer borrowed; // move-only type }; class InlineBorrowableReferenceTypesTest : public ::testing::Test { protected: int input_ = 1; int output_ = 2; InlineBorrowable borrowable_{std::in_place, 3}; void Validate(BorrowedPointer&& 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 references( std::in_place, input_, output_, borrowable_.acquire()); Validate(references.acquire()); } TEST_F(InlineBorrowableReferenceTypesTest, TestFromTuple) { InlineBorrowable references( std::forward_as_tuple(input_, output_, borrowable_.acquire())); Validate(references.acquire()); } TEST_F(InlineBorrowableReferenceTypesTest, TestFromFactory) { InlineBorrowable references( [&] { return ReferenceTypes(input_, output_, borrowable_.acquire()); }); Validate(references.acquire()); } } // namespace } // namespace pw::sync