blob: 94b0ba1dc05d58d00e484c6c5c9e0bfb18f61246 [file] [log] [blame]
#include "quiche/common/lifetime_tracking.h"
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "absl/strings/str_cat.h"
#include "quiche/common/platform/api/quiche_logging.h"
#include "quiche/common/platform/api/quiche_test.h"
namespace quiche {
namespace test {
struct ComposedTrackable {
LifetimeTrackable trackable;
};
struct InheritedTrackable : LifetimeTrackable {};
enum class TrackableType {
kComposed,
kInherited,
};
// Used by ::testing::PrintToStringParamName().
std::string PrintToString(const TrackableType& type) {
switch (type) {
case TrackableType::kComposed:
return "Composed";
case TrackableType::kInherited:
return "Inherited";
default:
QUICHE_LOG(FATAL) << "Unknown TrackableType: " << static_cast<int>(type);
}
}
class LifetimeTrackingTest : public QuicheTestWithParam<TrackableType> {
protected:
LifetimeTrackingTest() {
if (GetParam() == TrackableType::kComposed) {
composed_trackable_ = std::make_unique<ComposedTrackable>();
} else {
inherited_trackable_ = std::make_unique<InheritedTrackable>();
}
}
// Returns the trackable object. Must be called before FreeTrackable.
LifetimeTrackable& GetTrackable() {
if (composed_trackable_ != nullptr) {
return composed_trackable_->trackable;
} else {
return *inherited_trackable_;
}
}
// Returns a trackable.info_.
const std::shared_ptr<LifetimeInfo>& GetLifetimeInfoFromTrackable(
LifetimeTrackable& trackable) {
return trackable.info_;
}
const std::shared_ptr<LifetimeInfo>& GetLifetimeInfoFromTrackable() {
return GetLifetimeInfoFromTrackable(GetTrackable());
}
void FreeTrackable() {
composed_trackable_ = nullptr;
inherited_trackable_ = nullptr;
}
std::unique_ptr<ComposedTrackable> composed_trackable_;
std::unique_ptr<InheritedTrackable> inherited_trackable_;
};
TEST_P(LifetimeTrackingTest, TrackableButNeverTracked) {
EXPECT_EQ(GetLifetimeInfoFromTrackable(), nullptr);
}
TEST_P(LifetimeTrackingTest, SingleTrackerQueryLiveness) {
LifetimeTracker tracker = GetTrackable().NewTracker();
EXPECT_FALSE(tracker.IsTrackedObjectDead());
EXPECT_THAT(absl::StrCat(tracker),
testing::HasSubstr("Tracked object is alive"));
FreeTrackable();
EXPECT_TRUE(tracker.IsTrackedObjectDead());
EXPECT_THAT(absl::StrCat(tracker),
testing::HasSubstr("Tracked object has died"));
}
TEST_P(LifetimeTrackingTest, MultiTrackersQueryLiveness) {
LifetimeTracker tracker1 = GetTrackable().NewTracker();
LifetimeTracker tracker2 = GetTrackable().NewTracker();
LifetimeTracker tracker3 = tracker2;
LifetimeTracker tracker4 = std::move(tracker3);
LifetimeTracker tracker5(std::move(tracker4));
LifetimeTrackable another_trackable;
LifetimeTracker tracker6 = another_trackable.NewTracker();
LifetimeTracker tracker7 = another_trackable.NewTracker();
tracker6 = tracker2;
tracker7 = std::move(tracker2);
EXPECT_FALSE(tracker1.IsTrackedObjectDead());
EXPECT_FALSE(
tracker2.IsTrackedObjectDead()); // NOLINT(bugprone-use-after-move)
EXPECT_FALSE(
tracker3.IsTrackedObjectDead()); // NOLINT(bugprone-use-after-move)
EXPECT_FALSE(
tracker4.IsTrackedObjectDead()); // NOLINT(bugprone-use-after-move)
EXPECT_FALSE(tracker5.IsTrackedObjectDead());
EXPECT_FALSE(tracker6.IsTrackedObjectDead());
EXPECT_FALSE(tracker7.IsTrackedObjectDead());
FreeTrackable();
EXPECT_TRUE(tracker1.IsTrackedObjectDead());
EXPECT_TRUE(
tracker2.IsTrackedObjectDead()); // NOLINT(bugprone-use-after-move)
EXPECT_TRUE(
tracker3.IsTrackedObjectDead()); // NOLINT(bugprone-use-after-move)
EXPECT_TRUE(
tracker4.IsTrackedObjectDead()); // NOLINT(bugprone-use-after-move)
EXPECT_TRUE(tracker5.IsTrackedObjectDead());
EXPECT_TRUE(tracker6.IsTrackedObjectDead());
EXPECT_TRUE(tracker7.IsTrackedObjectDead());
}
TEST_P(LifetimeTrackingTest, CopyTrackableIsNoop) {
LifetimeTracker tracker = GetTrackable().NewTracker();
const LifetimeInfo* info = GetLifetimeInfoFromTrackable().get();
EXPECT_NE(info, nullptr);
LifetimeTrackable trackable2(GetTrackable());
EXPECT_EQ(GetLifetimeInfoFromTrackable(trackable2), nullptr);
LifetimeTrackable trackable3;
trackable3 = GetTrackable();
EXPECT_EQ(GetLifetimeInfoFromTrackable(trackable3), nullptr);
EXPECT_EQ(GetLifetimeInfoFromTrackable().get(), info);
}
TEST_P(LifetimeTrackingTest, MoveTrackableIsNoop) {
LifetimeTracker tracker = GetTrackable().NewTracker();
const LifetimeInfo* info = GetLifetimeInfoFromTrackable().get();
EXPECT_NE(info, nullptr);
LifetimeTrackable trackable2(std::move(GetTrackable()));
EXPECT_EQ(GetLifetimeInfoFromTrackable(trackable2), nullptr);
LifetimeTrackable trackable3;
trackable3 = std::move(GetTrackable());
EXPECT_EQ(GetLifetimeInfoFromTrackable(trackable3), nullptr);
EXPECT_EQ(GetLifetimeInfoFromTrackable().get(), info);
}
TEST_P(LifetimeTrackingTest, ObjectDiedDueToVectorRealloc) {
if (GetParam() == TrackableType::kComposed) {
return;
}
std::vector<InheritedTrackable> trackables;
// Append 1 element to the vector and keep track of its life.
InheritedTrackable& trackable = trackables.emplace_back();
LifetimeTracker tracker = trackable.NewTracker();
EXPECT_FALSE(tracker.IsTrackedObjectDead());
// Append 1000 more elements to the vector, |trackable| should be destroyed by
// vector realloc.
for (int i = 0; i < 1000; ++i) {
trackables.emplace_back();
}
// Accessing |trackable| is a use-after-free.
EXPECT_TRUE(tracker.IsTrackedObjectDead());
}
INSTANTIATE_TEST_SUITE_P(Tests, LifetimeTrackingTest,
testing::Values(TrackableType::kComposed,
TrackableType::kInherited),
testing::PrintToStringParamName());
} // namespace test
} // namespace quiche