blob: 4f5c2258db0ea5cbea2ccfbeef958a375bd1e7c2 [file] [log] [blame]
/*
* 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 <memory>
#include <type_traits>
#include <utility>
#include <ftl/finalizer.h>
#include <gtest/gtest.h>
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<int, int> {
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<decltype(inner_finalizer), decltype(outer_finalizer)>);
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<decltype(inner_finalizer), decltype(outer_finalizer)>);
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<decltype(inner_finalizer), decltype(outer_finalizer)>);
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<decltype(inner_finalizer), decltype(outer_finalizer)>);
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<int>(0);
std::weak_ptr<int> 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<int>(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