blob: cd4cd3d1056554d7ee8e2536add48b6c00a62c7d [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "platform/loader/fetch/ResourceLoadScheduler.h"
#include "platform/loader/testing/MockFetchContext.h"
#include "platform/runtime_enabled_features.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace blink {
namespace {
class MockClient final : public GarbageCollectedFinalized<MockClient>,
public ResourceLoadSchedulerClient {
USING_GARBAGE_COLLECTED_MIXIN(MockClient);
public:
~MockClient() {}
void Run() override {
EXPECT_FALSE(was_ran_);
was_ran_ = true;
}
bool WasRan() { return was_ran_; }
DEFINE_INLINE_VIRTUAL_TRACE() { ResourceLoadSchedulerClient::Trace(visitor); }
private:
bool was_ran_ = false;
};
class ResourceLoadSchedulerTest : public ::testing::Test {
public:
void SetUp() override {
DCHECK(RuntimeEnabledFeatures::ResourceLoadSchedulerEnabled());
scheduler_ = ResourceLoadScheduler::Create(
MockFetchContext::Create(MockFetchContext::kShouldNotLoadNewResource));
scheduler()->SetOutstandingLimitForTesting(1);
}
void TearDown() override {
scheduler()->Shutdown();
}
ResourceLoadScheduler* scheduler() { return scheduler_; }
private:
Persistent<ResourceLoadScheduler> scheduler_;
};
TEST_F(ResourceLoadSchedulerTest, Bypass) {
// A request that disallows throttling should be ran synchronously.
MockClient* client1 = new MockClient;
ResourceLoadScheduler::ClientId id1 = ResourceLoadScheduler::kInvalidClientId;
scheduler()->Request(
client1, ResourceLoadScheduler::ThrottleOption::kCanNotBeThrottled, &id1);
EXPECT_NE(ResourceLoadScheduler::kInvalidClientId, id1);
EXPECT_TRUE(client1->WasRan());
// Another request that disallows throttling also should be ran even it makes
// the outstanding number reaches to the limit.
MockClient* client2 = new MockClient;
ResourceLoadScheduler::ClientId id2 = ResourceLoadScheduler::kInvalidClientId;
scheduler()->Request(
client2, ResourceLoadScheduler::ThrottleOption::kCanNotBeThrottled, &id2);
EXPECT_NE(ResourceLoadScheduler::kInvalidClientId, id2);
EXPECT_TRUE(client2->WasRan());
// Call Release() with different options just in case.
EXPECT_TRUE(scheduler()->Release(
id1, ResourceLoadScheduler::ReleaseOption::kReleaseOnly));
EXPECT_TRUE(scheduler()->Release(
id2, ResourceLoadScheduler::ReleaseOption::kReleaseAndSchedule));
// Should not succeed to call with the same ID twice.
EXPECT_FALSE(scheduler()->Release(
id1, ResourceLoadScheduler::ReleaseOption::kReleaseOnly));
// Should not succeed to call with the invalid ID or unused ID.
EXPECT_FALSE(
scheduler()->Release(ResourceLoadScheduler::kInvalidClientId,
ResourceLoadScheduler::ReleaseOption::kReleaseOnly));
EXPECT_FALSE(
scheduler()->Release(static_cast<ResourceLoadScheduler::ClientId>(774),
ResourceLoadScheduler::ReleaseOption::kReleaseOnly));
}
TEST_F(ResourceLoadSchedulerTest, Throttled) {
// The first request should be ran synchronously.
MockClient* client1 = new MockClient;
ResourceLoadScheduler::ClientId id1 = ResourceLoadScheduler::kInvalidClientId;
scheduler()->Request(
client1, ResourceLoadScheduler::ThrottleOption::kCanBeThrottled, &id1);
EXPECT_NE(ResourceLoadScheduler::kInvalidClientId, id1);
EXPECT_TRUE(client1->WasRan());
// Another request should be throttled until the first request calls Release.
MockClient* client2 = new MockClient;
ResourceLoadScheduler::ClientId id2 = ResourceLoadScheduler::kInvalidClientId;
scheduler()->Request(
client2, ResourceLoadScheduler::ThrottleOption::kCanBeThrottled, &id2);
EXPECT_NE(ResourceLoadScheduler::kInvalidClientId, id2);
EXPECT_FALSE(client2->WasRan());
// Two more requests.
MockClient* client3 = new MockClient;
ResourceLoadScheduler::ClientId id3 = ResourceLoadScheduler::kInvalidClientId;
scheduler()->Request(
client3, ResourceLoadScheduler::ThrottleOption::kCanBeThrottled, &id3);
EXPECT_NE(ResourceLoadScheduler::kInvalidClientId, id3);
EXPECT_FALSE(client3->WasRan());
MockClient* client4 = new MockClient;
ResourceLoadScheduler::ClientId id4 = ResourceLoadScheduler::kInvalidClientId;
scheduler()->Request(
client4, ResourceLoadScheduler::ThrottleOption::kCanBeThrottled, &id4);
EXPECT_NE(ResourceLoadScheduler::kInvalidClientId, id4);
EXPECT_FALSE(client4->WasRan());
// Call Release() to run the second request.
EXPECT_TRUE(scheduler()->Release(
id1, ResourceLoadScheduler::ReleaseOption::kReleaseAndSchedule));
EXPECT_TRUE(client2->WasRan());
// Call Release() with kReleaseOnly should not run the third and the fourth
// requests.
EXPECT_TRUE(scheduler()->Release(
id2, ResourceLoadScheduler::ReleaseOption::kReleaseOnly));
EXPECT_FALSE(client3->WasRan());
EXPECT_FALSE(client4->WasRan());
// Should be able to call Release() for a client that hasn't run yet. This
// should run another scheduling to run the fourth request.
EXPECT_TRUE(scheduler()->Release(
id3, ResourceLoadScheduler::ReleaseOption::kReleaseAndSchedule));
EXPECT_TRUE(client4->WasRan());
}
TEST_F(ResourceLoadSchedulerTest, Unthrottle) {
// Push three requests.
MockClient* client1 = new MockClient;
ResourceLoadScheduler::ClientId id1 = ResourceLoadScheduler::kInvalidClientId;
scheduler()->Request(
client1, ResourceLoadScheduler::ThrottleOption::kCanBeThrottled, &id1);
EXPECT_NE(ResourceLoadScheduler::kInvalidClientId, id1);
EXPECT_TRUE(client1->WasRan());
MockClient* client2 = new MockClient;
ResourceLoadScheduler::ClientId id2 = ResourceLoadScheduler::kInvalidClientId;
scheduler()->Request(
client2, ResourceLoadScheduler::ThrottleOption::kCanBeThrottled, &id2);
EXPECT_NE(ResourceLoadScheduler::kInvalidClientId, id2);
EXPECT_FALSE(client2->WasRan());
MockClient* client3 = new MockClient;
ResourceLoadScheduler::ClientId id3 = ResourceLoadScheduler::kInvalidClientId;
scheduler()->Request(
client3, ResourceLoadScheduler::ThrottleOption::kCanBeThrottled, &id3);
EXPECT_NE(ResourceLoadScheduler::kInvalidClientId, id3);
EXPECT_FALSE(client3->WasRan());
// Allows to pass all requests.
scheduler()->SetOutstandingLimitForTesting(3);
EXPECT_TRUE(client2->WasRan());
EXPECT_TRUE(client3->WasRan());
// Release all.
EXPECT_TRUE(scheduler()->Release(
id3, ResourceLoadScheduler::ReleaseOption::kReleaseOnly));
EXPECT_TRUE(scheduler()->Release(
id2, ResourceLoadScheduler::ReleaseOption::kReleaseOnly));
EXPECT_TRUE(scheduler()->Release(
id1, ResourceLoadScheduler::ReleaseOption::kReleaseOnly));
}
TEST_F(ResourceLoadSchedulerTest, Stopped) {
// Push three requests.
MockClient* client1 = new MockClient;
ResourceLoadScheduler::ClientId id1 = ResourceLoadScheduler::kInvalidClientId;
scheduler()->Request(
client1, ResourceLoadScheduler::ThrottleOption::kCanBeThrottled, &id1);
EXPECT_NE(ResourceLoadScheduler::kInvalidClientId, id1);
EXPECT_TRUE(client1->WasRan());
MockClient* client2 = new MockClient;
ResourceLoadScheduler::ClientId id2 = ResourceLoadScheduler::kInvalidClientId;
scheduler()->Request(
client2, ResourceLoadScheduler::ThrottleOption::kCanBeThrottled, &id2);
EXPECT_NE(ResourceLoadScheduler::kInvalidClientId, id2);
EXPECT_FALSE(client2->WasRan());
MockClient* client3 = new MockClient;
ResourceLoadScheduler::ClientId id3 = ResourceLoadScheduler::kInvalidClientId;
scheduler()->Request(
client3, ResourceLoadScheduler::ThrottleOption::kCanBeThrottled, &id3);
EXPECT_NE(ResourceLoadScheduler::kInvalidClientId, id3);
EXPECT_FALSE(client3->WasRan());
// Setting outstanding_limit_ to 0 in ThrottlingState::kStopped, prevents
// further requests.
scheduler()->SetOutstandingLimitForTesting(0);
EXPECT_FALSE(client2->WasRan());
EXPECT_FALSE(client3->WasRan());
// Calling Release() still does not run the second request.
EXPECT_TRUE(scheduler()->Release(
id1, ResourceLoadScheduler::ReleaseOption::kReleaseAndSchedule));
EXPECT_FALSE(client2->WasRan());
EXPECT_FALSE(client3->WasRan());
// Release all.
EXPECT_TRUE(scheduler()->Release(
id3, ResourceLoadScheduler::ReleaseOption::kReleaseOnly));
EXPECT_TRUE(scheduler()->Release(
id2, ResourceLoadScheduler::ReleaseOption::kReleaseOnly));
}
} // namespace
} // namespace blink