/* * Copyright 2024 The Android Open Source Project * * 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 * * http://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 #include #include #include #include namespace android::test { namespace { struct Counter { constexpr auto increment_fn() { return [this] { ++value_; }; } auto increment_finalizer() { return ftl::Finalizer([this] { ++value_; }); } [[nodiscard]] constexpr auto value() const -> int { return value_; } private: int value_ = 0; }; struct CounterPair { constexpr auto increment_first_fn() { return first.increment_fn(); } constexpr auto increment_second_fn() { return second.increment_fn(); } [[nodiscard]] constexpr auto values() const -> std::pair { return {first.value(), second.value()}; } private: Counter first; Counter second; }; } // namespace TEST(Finalizer, DefaultConstructionAndNoOpDestructionWhenPolymorphicType) { ftl::FinalizerStd finalizer1; ftl::FinalizerFtl finalizer2; ftl::FinalizerFtl1 finalizer3; ftl::FinalizerFtl2 finalizer4; ftl::FinalizerFtl3 finalizer5; } TEST(Finalizer, InvokesTheFunctionOnDestruction) { Counter counter; { const auto finalizer = counter.increment_finalizer(); EXPECT_EQ(counter.value(), 0); } EXPECT_EQ(counter.value(), 1); } TEST(Finalizer, InvocationCanBeCanceled) { Counter counter; { auto finalizer = counter.increment_finalizer(); EXPECT_EQ(counter.value(), 0); finalizer.cancel(); EXPECT_EQ(counter.value(), 0); } EXPECT_EQ(counter.value(), 0); } TEST(Finalizer, InvokesTheFunctionOnce) { Counter counter; { auto finalizer = counter.increment_finalizer(); EXPECT_EQ(counter.value(), 0); finalizer(); EXPECT_EQ(counter.value(), 1); finalizer(); EXPECT_EQ(counter.value(), 1); } EXPECT_EQ(counter.value(), 1); } TEST(Finalizer, SelfInvocationIsAllowedAndANoOp) { Counter counter; ftl::FinalizerStd finalizer; finalizer = ftl::Finalizer([&]() { counter.increment_fn()(); finalizer(); // recursive invocation should do nothing. }); EXPECT_EQ(counter.value(), 0); finalizer(); EXPECT_EQ(counter.value(), 1); } TEST(Finalizer, MoveConstruction) { Counter counter; { ftl::FinalizerStd outer_finalizer = counter.increment_finalizer(); EXPECT_EQ(counter.value(), 0); { ftl::FinalizerStd inner_finalizer = std::move(outer_finalizer); static_assert(std::is_same_v); EXPECT_EQ(counter.value(), 0); } EXPECT_EQ(counter.value(), 1); } EXPECT_EQ(counter.value(), 1); } TEST(Finalizer, MoveConstructionWithImplicitConversion) { Counter counter; { auto outer_finalizer = counter.increment_finalizer(); EXPECT_EQ(counter.value(), 0); { ftl::FinalizerStd inner_finalizer = std::move(outer_finalizer); static_assert(!std::is_same_v); EXPECT_EQ(counter.value(), 0); } EXPECT_EQ(counter.value(), 1); } EXPECT_EQ(counter.value(), 1); } TEST(Finalizer, MoveAssignment) { CounterPair pair; { ftl::FinalizerStd outer_finalizer = ftl::Finalizer(pair.increment_first_fn()); EXPECT_EQ(pair.values(), std::make_pair(0, 0)); { ftl::FinalizerStd inner_finalizer = ftl::Finalizer(pair.increment_second_fn()); static_assert(std::is_same_v); EXPECT_EQ(pair.values(), std::make_pair(0, 0)); inner_finalizer = std::move(outer_finalizer); EXPECT_EQ(pair.values(), std::make_pair(0, 1)); } EXPECT_EQ(pair.values(), std::make_pair(1, 1)); } EXPECT_EQ(pair.values(), std::make_pair(1, 1)); } TEST(Finalizer, MoveAssignmentWithImplicitConversion) { CounterPair pair; { auto outer_finalizer = ftl::Finalizer(pair.increment_first_fn()); EXPECT_EQ(pair.values(), std::make_pair(0, 0)); { ftl::FinalizerStd inner_finalizer = ftl::Finalizer(pair.increment_second_fn()); static_assert(!std::is_same_v); EXPECT_EQ(pair.values(), std::make_pair(0, 0)); inner_finalizer = std::move(outer_finalizer); EXPECT_EQ(pair.values(), std::make_pair(0, 1)); } EXPECT_EQ(pair.values(), std::make_pair(1, 1)); } EXPECT_EQ(pair.values(), std::make_pair(1, 1)); } TEST(Finalizer, NullifiesTheFunctionWhenInvokedIfPossible) { auto shared = std::make_shared(0); std::weak_ptr weak = shared; int count = 0; { auto lambda = [capture = std::move(shared)]() {}; auto finalizer = ftl::Finalizer(std::move(lambda)); EXPECT_FALSE(weak.expired()); // A lambda is not nullable. Invoking the finalizer cannot destroy it to destroy the lambda's // capture. finalizer(); EXPECT_FALSE(weak.expired()); } // The lambda is only destroyed when the finalizer instance is destroyed. EXPECT_TRUE(weak.expired()); shared = std::make_shared(0); weak = shared; { auto lambda = [capture = std::move(shared)]() {}; auto finalizer = ftl::FinalizerStd(std::move(lambda)); EXPECT_FALSE(weak.expired()); // Since std::function is used, and is nullable, invoking the finalizer will destroy the // contained function, which will destroy the lambda's capture. finalizer(); EXPECT_TRUE(weak.expired()); } } } // namespace android::test