// Copyright 2024 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_thread/deprecated_or_new_thread_function.h" #include "pw_unit_test/framework.h" namespace { using ::pw::Function; using ::pw::thread::DeprecatedFnPtrAndArg; using ::pw::thread::DeprecatedOrNewThreadFn; TEST(DeprecatedOrNewThreadFunction, CallInvokesLambda) { int call_count = 0; DeprecatedOrNewThreadFn fn; fn = Function([&call_count]() { ++call_count; }); EXPECT_EQ(call_count, 0); fn(); EXPECT_EQ(call_count, 1); } TEST(DeprecatedOrNewThreadFunction, CallMovedInvokesLambda) { int call_count = 0; DeprecatedOrNewThreadFn fn; fn = Function([&call_count]() { ++call_count; }); DeprecatedOrNewThreadFn fn2; fn2 = std::move(fn); EXPECT_EQ(call_count, 0); fn2(); EXPECT_EQ(call_count, 1); } class DestroyCounter { public: DestroyCounter(int& counter) : counter_(&counter) {} DestroyCounter(const DestroyCounter&) = delete; DestroyCounter& operator=(const DestroyCounter&) = delete; DestroyCounter(DestroyCounter&& other) : counter_(other.counter_) { other.counter_ = nullptr; } DestroyCounter& operator=(DestroyCounter&& other) { CounterIncrement(); counter_ = other.counter_; other.counter_ = nullptr; return *this; } ~DestroyCounter() { CounterIncrement(); } private: void CounterIncrement() { if (counter_ != nullptr) { ++(*counter_); } } int* counter_; }; TEST(DeprecatedOrNewThreadFunction, DestructionInvokesLambdasDestructor) { int counter = 0; { DeprecatedOrNewThreadFn fn; fn = Function([_unused = DestroyCounter(counter)]() {}); EXPECT_EQ(counter, 0); } EXPECT_EQ(counter, 1); } TEST(DeprecatedOrNewThreadFunction, DestructionMovedInvokesLambdasDestructor) { int counter = 0; { DeprecatedOrNewThreadFn fn; { fn = Function([_unused = DestroyCounter(counter)]() {}); DeprecatedOrNewThreadFn scoped_fn; scoped_fn = std::move(fn); EXPECT_EQ(counter, 0); } EXPECT_EQ(counter, 1); } EXPECT_EQ(counter, 1); } TEST(DeprecatedOrNewThreadFunction, NullptrAssignmentInvokesLambdasDestructor) { int counter = 0; { DeprecatedOrNewThreadFn fn; fn = Function([_unused = DestroyCounter(counter)]() {}); EXPECT_EQ(counter, 0); fn = nullptr; EXPECT_EQ(counter, 1); } EXPECT_EQ(counter, 1); } void increment(void* int_to_increment) { int& value = *static_cast(int_to_increment); ++value; } TEST(DeprecatedOrNewThreadFunction, CallInvokesFnPtrWithArg) { int call_count = 0; DeprecatedOrNewThreadFn fn; fn = DeprecatedFnPtrAndArg{increment, static_cast(&call_count)}; EXPECT_EQ(call_count, 0); fn(); EXPECT_EQ(call_count, 1); } TEST(DeprecatedOrNewThreadFunction, CallMovedInvokesFnPtrWithArg) { int call_count = 0; DeprecatedOrNewThreadFn fn; fn = DeprecatedFnPtrAndArg{increment, static_cast(&call_count)}; DeprecatedOrNewThreadFn fn2; fn2 = std::move(fn); EXPECT_EQ(call_count, 0); fn2(); EXPECT_EQ(call_count, 1); } } // namespace