blob: 624064f3a0cc4cc8c671e4333a6abaf52b820060 [file] [log] [blame]
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/interest_group/data_decoder_manager.h"
#include <memory>
#include <optional>
#include "base/test/task_environment.h"
#include "base/test/test_future.h"
#include "base/time/time.h"
#include "services/data_decoder/public/cpp/data_decoder.h"
#include "services/data_decoder/public/cpp/test_support/in_process_data_decoder.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
#include "url/origin.h"
namespace content {
namespace {
class DataDecoderManagerTest : public testing::Test {
public:
DataDecoderManagerTest() = default;
~DataDecoderManagerTest() override = default;
protected:
// Tries to decode CBOR using `data_decoder`, and checks the result, to
// validate the DataDecoder works.
void ValidateDecoder(data_decoder::DataDecoder& data_decoder) {
base::test::TestFuture<data_decoder::DataDecoder::ValueOrError> future;
// Try to decode the CBOR string value "test".
data_decoder.ParseCbor({0x64, 0x74, 0x65, 0x73, 0x74},
future.GetCallback());
const auto& result = future.Get();
ASSERT_TRUE(result.has_value());
EXPECT_EQ(result.value(), "test");
}
const url::Origin kOrigin1 =
url::Origin::Create(GURL("https://origin1.test"));
const url::Origin kOrigin2 =
url::Origin::Create(GURL("https://origin2.test"));
base::TimeDelta kTinyTime = base::Milliseconds(1);
base::test::TaskEnvironment task_environment_{
base::test::TaskEnvironment::TimeSource::MOCK_TIME};
data_decoder::test::InProcessDataDecoder in_process_data_decoder_;
DataDecoderManager manager_;
};
// Destroy a Handle, and make sure it times out as expected.
TEST_F(DataDecoderManagerTest, CreateAndDestroyHandle) {
// Try a number of different delays before destroying the Handle, to make sure
// the idle timer starts when the Handle is destroyed.
for (base::TimeDelta delay :
{base::Seconds(0), DataDecoderManager::kIdleTimeout - kTinyTime,
DataDecoderManager::kIdleTimeout, base::Hours(1)}) {
auto handle = manager_.GetHandle(kOrigin1, kOrigin2);
EXPECT_EQ(manager_.NumDecodersForTesting(), 1u);
EXPECT_EQ(manager_.GetHandleCountForTesting(kOrigin1, kOrigin2), 1u);
ValidateDecoder(handle->data_decoder());
EXPECT_EQ(manager_.NumDecodersForTesting(), 1u);
EXPECT_EQ(manager_.GetHandleCountForTesting(kOrigin1, kOrigin2), 1u);
// No matter when the Handle is destroyed, the underlying DataDecoder should
// be kept alive for `kIdleTimeout`.
task_environment_.FastForwardBy(delay);
ValidateDecoder(handle->data_decoder());
EXPECT_EQ(manager_.NumDecodersForTesting(), 1u);
EXPECT_EQ(manager_.GetHandleCountForTesting(kOrigin1, kOrigin2), 1u);
// Destroying the Handle shouldn't destroy the DataDecoder.
handle.reset();
EXPECT_EQ(manager_.NumDecodersForTesting(), 1u);
EXPECT_EQ(manager_.GetHandleCountForTesting(kOrigin1, kOrigin2), 0u);
// Wait until just before the idle timeout, and check that the DataDecoder
// still exists.
task_environment_.FastForwardBy(DataDecoderManager::kIdleTimeout -
kTinyTime);
EXPECT_EQ(manager_.NumDecodersForTesting(), 1u);
EXPECT_EQ(manager_.GetHandleCountForTesting(kOrigin1, kOrigin2), 0u);
// Check that the handle times out correctly.
task_environment_.FastForwardBy(kTinyTime);
EXPECT_EQ(manager_.NumDecodersForTesting(), 0u);
EXPECT_EQ(manager_.GetHandleCountForTesting(kOrigin1, kOrigin2),
std::nullopt);
}
}
// Check multiple Handles for a single DataDecoder.
TEST_F(DataDecoderManagerTest, MultipleHandles) {
auto handle1 = manager_.GetHandle(kOrigin1, kOrigin2);
EXPECT_EQ(manager_.NumDecodersForTesting(), 1u);
EXPECT_EQ(manager_.GetHandleCountForTesting(kOrigin1, kOrigin2), 1u);
ValidateDecoder(handle1->data_decoder());
// Create a second Handle, check that it gets the same DataDecoder.
auto handle2 = manager_.GetHandle(kOrigin1, kOrigin2);
EXPECT_EQ(manager_.NumDecodersForTesting(), 1u);
EXPECT_EQ(manager_.GetHandleCountForTesting(kOrigin1, kOrigin2), 2u);
EXPECT_EQ(&handle1->data_decoder(), &handle2->data_decoder());
ValidateDecoder(handle2->data_decoder());
// Destroy one handle. The decoder should still exist and not be timed out
// after `kIdleTimeout`.
handle1.reset();
EXPECT_EQ(manager_.NumDecodersForTesting(), 1u);
EXPECT_EQ(manager_.GetHandleCountForTesting(kOrigin1, kOrigin2), 1u);
ValidateDecoder(handle2->data_decoder());
task_environment_.FastForwardBy(DataDecoderManager::kIdleTimeout);
EXPECT_EQ(manager_.NumDecodersForTesting(), 1u);
EXPECT_EQ(manager_.GetHandleCountForTesting(kOrigin1, kOrigin2), 1u);
ValidateDecoder(handle2->data_decoder());
// Grab a raw pointer to the decoder (which consumers shouldn't be doing),
// delete the last Handle, and wait until just before the DataDecoder times
// out.
auto* raw_decoder = &handle2->data_decoder();
handle2.reset();
task_environment_.FastForwardBy(DataDecoderManager::kIdleTimeout - kTinyTime);
EXPECT_EQ(manager_.NumDecodersForTesting(), 1u);
EXPECT_EQ(manager_.GetHandleCountForTesting(kOrigin1, kOrigin2), 0u);
// Get another DataDecoder, which should return a Handle to the same object as
// before.
auto handle3 = manager_.GetHandle(kOrigin1, kOrigin2);
EXPECT_EQ(&handle3->data_decoder(), raw_decoder);
EXPECT_EQ(manager_.NumDecodersForTesting(), 1u);
EXPECT_EQ(manager_.GetHandleCountForTesting(kOrigin1, kOrigin2), 1u);
ValidateDecoder(handle3->data_decoder());
// Clear `raw_decoder`, as it's no longer needed.
raw_decoder = nullptr;
// Decoder should not be destroyed after `kIdleTimeout`, since there's still a
// live Handle.
task_environment_.FastForwardBy(DataDecoderManager::kIdleTimeout);
EXPECT_EQ(manager_.NumDecodersForTesting(), 1u);
EXPECT_EQ(manager_.GetHandleCountForTesting(kOrigin1, kOrigin2), 1u);
ValidateDecoder(handle3->data_decoder());
// Destroy the last Handle, and make sure the underlying DataDecoder is
// destroyed after `kIdleTimeout`.
handle3.reset();
task_environment_.FastForwardBy(DataDecoderManager::kIdleTimeout - kTinyTime);
EXPECT_EQ(manager_.NumDecodersForTesting(), 1u);
EXPECT_EQ(manager_.GetHandleCountForTesting(kOrigin1, kOrigin2), 0u);
task_environment_.FastForwardBy(kTinyTime);
EXPECT_EQ(manager_.NumDecodersForTesting(), 0u);
EXPECT_EQ(manager_.GetHandleCountForTesting(kOrigin1, kOrigin2),
std::nullopt);
}
// Create 4 different DataDecoders at once. Make sure they're all backed by
// different DataDecoders. Destroy all Handles at once, and check that the
// underlying DataDecoders are all destroyed together as well.
TEST_F(DataDecoderManagerTest, MultipleDataDecoders) {
auto handle1 = manager_.GetHandle(kOrigin1, kOrigin1);
EXPECT_EQ(manager_.NumDecodersForTesting(), 1u);
EXPECT_EQ(manager_.GetHandleCountForTesting(kOrigin1, kOrigin1), 1u);
ValidateDecoder(handle1->data_decoder());
auto handle2 = manager_.GetHandle(kOrigin1, kOrigin2);
EXPECT_NE(&handle1->data_decoder(), &handle2->data_decoder());
EXPECT_EQ(manager_.NumDecodersForTesting(), 2u);
EXPECT_EQ(manager_.GetHandleCountForTesting(kOrigin1, kOrigin1), 1u);
EXPECT_EQ(manager_.GetHandleCountForTesting(kOrigin1, kOrigin2), 1u);
ValidateDecoder(handle2->data_decoder());
auto handle3 = manager_.GetHandle(kOrigin2, kOrigin1);
EXPECT_NE(&handle1->data_decoder(), &handle3->data_decoder());
EXPECT_NE(&handle2->data_decoder(), &handle3->data_decoder());
EXPECT_EQ(manager_.NumDecodersForTesting(), 3u);
EXPECT_EQ(manager_.GetHandleCountForTesting(kOrigin1, kOrigin1), 1u);
EXPECT_EQ(manager_.GetHandleCountForTesting(kOrigin1, kOrigin2), 1u);
EXPECT_EQ(manager_.GetHandleCountForTesting(kOrigin2, kOrigin1), 1u);
ValidateDecoder(handle3->data_decoder());
auto handle4 = manager_.GetHandle(kOrigin2, kOrigin2);
EXPECT_NE(&handle1->data_decoder(), &handle4->data_decoder());
EXPECT_NE(&handle2->data_decoder(), &handle4->data_decoder());
EXPECT_NE(&handle3->data_decoder(), &handle4->data_decoder());
EXPECT_EQ(manager_.NumDecodersForTesting(), 4u);
EXPECT_EQ(manager_.GetHandleCountForTesting(kOrigin1, kOrigin1), 1u);
EXPECT_EQ(manager_.GetHandleCountForTesting(kOrigin1, kOrigin2), 1u);
EXPECT_EQ(manager_.GetHandleCountForTesting(kOrigin2, kOrigin1), 1u);
EXPECT_EQ(manager_.GetHandleCountForTesting(kOrigin2, kOrigin2), 1u);
ValidateDecoder(handle4->data_decoder());
// Create duplicates of each Handle, make sure the correct Handle is reused.
auto handle1_2 = manager_.GetHandle(kOrigin1, kOrigin1);
EXPECT_EQ(&handle1->data_decoder(), &handle1_2->data_decoder());
EXPECT_EQ(manager_.NumDecodersForTesting(), 4u);
EXPECT_EQ(manager_.GetHandleCountForTesting(kOrigin1, kOrigin1), 2u);
EXPECT_EQ(manager_.GetHandleCountForTesting(kOrigin1, kOrigin2), 1u);
EXPECT_EQ(manager_.GetHandleCountForTesting(kOrigin2, kOrigin1), 1u);
EXPECT_EQ(manager_.GetHandleCountForTesting(kOrigin2, kOrigin2), 1u);
auto handle2_2 = manager_.GetHandle(kOrigin1, kOrigin2);
EXPECT_EQ(&handle2->data_decoder(), &handle2_2->data_decoder());
EXPECT_EQ(manager_.NumDecodersForTesting(), 4u);
EXPECT_EQ(manager_.GetHandleCountForTesting(kOrigin1, kOrigin1), 2u);
EXPECT_EQ(manager_.GetHandleCountForTesting(kOrigin1, kOrigin2), 2u);
EXPECT_EQ(manager_.GetHandleCountForTesting(kOrigin2, kOrigin1), 1u);
EXPECT_EQ(manager_.GetHandleCountForTesting(kOrigin2, kOrigin2), 1u);
auto handle3_2 = manager_.GetHandle(kOrigin2, kOrigin1);
EXPECT_EQ(&handle3->data_decoder(), &handle3_2->data_decoder());
EXPECT_EQ(manager_.NumDecodersForTesting(), 4u);
EXPECT_EQ(manager_.GetHandleCountForTesting(kOrigin1, kOrigin1), 2u);
EXPECT_EQ(manager_.GetHandleCountForTesting(kOrigin1, kOrigin2), 2u);
EXPECT_EQ(manager_.GetHandleCountForTesting(kOrigin2, kOrigin1), 2u);
EXPECT_EQ(manager_.GetHandleCountForTesting(kOrigin2, kOrigin2), 1u);
auto handle4_2 = manager_.GetHandle(kOrigin2, kOrigin2);
EXPECT_EQ(&handle4->data_decoder(), &handle4_2->data_decoder());
EXPECT_EQ(manager_.NumDecodersForTesting(), 4u);
EXPECT_EQ(manager_.GetHandleCountForTesting(kOrigin1, kOrigin1), 2u);
EXPECT_EQ(manager_.GetHandleCountForTesting(kOrigin1, kOrigin2), 2u);
EXPECT_EQ(manager_.GetHandleCountForTesting(kOrigin2, kOrigin1), 2u);
EXPECT_EQ(manager_.GetHandleCountForTesting(kOrigin2, kOrigin2), 2u);
// Tear down all Handles.
handle1.reset();
handle1_2.reset();
EXPECT_EQ(manager_.NumDecodersForTesting(), 4u);
EXPECT_EQ(manager_.GetHandleCountForTesting(kOrigin1, kOrigin1), 0u);
EXPECT_EQ(manager_.GetHandleCountForTesting(kOrigin1, kOrigin2), 2u);
EXPECT_EQ(manager_.GetHandleCountForTesting(kOrigin2, kOrigin1), 2u);
EXPECT_EQ(manager_.GetHandleCountForTesting(kOrigin2, kOrigin2), 2u);
handle2.reset();
handle2_2.reset();
EXPECT_EQ(manager_.NumDecodersForTesting(), 4u);
EXPECT_EQ(manager_.GetHandleCountForTesting(kOrigin1, kOrigin1), 0u);
EXPECT_EQ(manager_.GetHandleCountForTesting(kOrigin1, kOrigin2), 0u);
EXPECT_EQ(manager_.GetHandleCountForTesting(kOrigin2, kOrigin1), 2u);
EXPECT_EQ(manager_.GetHandleCountForTesting(kOrigin2, kOrigin2), 2u);
handle3.reset();
handle3_2.reset();
EXPECT_EQ(manager_.NumDecodersForTesting(), 4u);
EXPECT_EQ(manager_.GetHandleCountForTesting(kOrigin1, kOrigin1), 0u);
EXPECT_EQ(manager_.GetHandleCountForTesting(kOrigin1, kOrigin2), 0u);
EXPECT_EQ(manager_.GetHandleCountForTesting(kOrigin2, kOrigin1), 0u);
EXPECT_EQ(manager_.GetHandleCountForTesting(kOrigin2, kOrigin2), 2u);
handle4.reset();
handle4_2.reset();
EXPECT_EQ(manager_.NumDecodersForTesting(), 4u);
EXPECT_EQ(manager_.GetHandleCountForTesting(kOrigin1, kOrigin1), 0u);
EXPECT_EQ(manager_.GetHandleCountForTesting(kOrigin1, kOrigin2), 0u);
EXPECT_EQ(manager_.GetHandleCountForTesting(kOrigin2, kOrigin1), 0u);
EXPECT_EQ(manager_.GetHandleCountForTesting(kOrigin2, kOrigin2), 0u);
// Wait until just before `kIdleTimeout`. No DataDecoders should be destroyed.
task_environment_.FastForwardBy(DataDecoderManager::kIdleTimeout - kTinyTime);
EXPECT_EQ(manager_.NumDecodersForTesting(), 4u);
// Wait until timeout. All DataDecoders should be timed out at once.
task_environment_.FastForwardBy(kTinyTime);
EXPECT_EQ(manager_.NumDecodersForTesting(), 0u);
EXPECT_EQ(manager_.GetHandleCountForTesting(kOrigin1, kOrigin1),
std::nullopt);
EXPECT_EQ(manager_.GetHandleCountForTesting(kOrigin1, kOrigin2),
std::nullopt);
EXPECT_EQ(manager_.GetHandleCountForTesting(kOrigin2, kOrigin1),
std::nullopt);
EXPECT_EQ(manager_.GetHandleCountForTesting(kOrigin2, kOrigin2),
std::nullopt);
}
// Create 2 Handles, destroy one immediately, then destroy the other some time
// later, but while the timer for the other one is pending. Finally, check when
// the DataDecoders are actually destroyed.
TEST_F(DataDecoderManagerTest, OverlappingCleanupTimers) {
const base::TimeDelta kHalfTimeout = DataDecoderManager::kIdleTimeout / 2;
auto handle1 = manager_.GetHandle(kOrigin1, kOrigin1);
auto handle2 = manager_.GetHandle(kOrigin1, kOrigin2);
EXPECT_EQ(manager_.NumDecodersForTesting(), 2u);
EXPECT_EQ(manager_.GetHandleCountForTesting(kOrigin1, kOrigin1), 1u);
EXPECT_EQ(manager_.GetHandleCountForTesting(kOrigin1, kOrigin2), 1u);
handle1.reset();
EXPECT_EQ(manager_.NumDecodersForTesting(), 2u);
EXPECT_EQ(manager_.GetHandleCountForTesting(kOrigin1, kOrigin1), 0u);
EXPECT_EQ(manager_.GetHandleCountForTesting(kOrigin1, kOrigin2), 1u);
// Wait until half the timeout has passed before destroying `handle2`. Both
// DataDecoders should still exist, but have no Handles.
task_environment_.FastForwardBy(kHalfTimeout);
handle2.reset();
EXPECT_EQ(manager_.NumDecodersForTesting(), 2u);
EXPECT_EQ(manager_.GetHandleCountForTesting(kOrigin1, kOrigin1), 0u);
EXPECT_EQ(manager_.GetHandleCountForTesting(kOrigin1, kOrigin2), 0u);
// Wait until just before `kIdleTimeout` from when `handle` was destroyed. No
// DataDecoders should be destroyed.
task_environment_.FastForwardBy(DataDecoderManager::kIdleTimeout -
kHalfTimeout - kTinyTime);
EXPECT_EQ(manager_.NumDecodersForTesting(), 2u);
// At exactly the timeout from when `handle1` was destroyed, its DataDecoder
// should be destroyed. `handle2's` DataDecoder should still exist.
task_environment_.FastForwardBy(kTinyTime);
EXPECT_EQ(manager_.NumDecodersForTesting(), 1u);
EXPECT_EQ(manager_.GetHandleCountForTesting(kOrigin1, kOrigin1),
std::nullopt);
EXPECT_EQ(manager_.GetHandleCountForTesting(kOrigin1, kOrigin2), 0u);
// Due to the cleanup task throttling mechanism, the other DataDecoder will
// only be destroyed after an additional `kIdleTimeout` has passed from when
// the other one was timed out.
task_environment_.FastForwardBy(DataDecoderManager::kIdleTimeout - kTinyTime);
EXPECT_EQ(manager_.NumDecodersForTesting(), 1u);
task_environment_.FastForwardBy(kTinyTime);
EXPECT_EQ(manager_.NumDecodersForTesting(), 0u);
EXPECT_EQ(manager_.GetHandleCountForTesting(kOrigin1, kOrigin1),
std::nullopt);
EXPECT_EQ(manager_.GetHandleCountForTesting(kOrigin1, kOrigin2),
std::nullopt);
}
} // namespace
} // namespace content