blob: 64c6a85d9090be5b42fdc5cea358cd2bd7619242 [file] [log] [blame]
// Copyright 2019 The Fuchsia 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 "src/ledger/bin/app/page_availability_manager.h"
#include <fuchsia/ledger/cpp/fidl.h>
#include <lib/callback/set_when_called.h>
#include <map>
#include <random>
#include <vector>
#include "gtest/gtest.h"
#include "src/ledger/bin/testing/test_with_environment.h"
namespace ledger {
namespace {
using PageAvailabilityManagerTest = TestWithEnvironment;
TEST_F(PageAvailabilityManagerTest, PageAvailableByDefault) {
storage::PageId page_id = std::string(::fuchsia::ledger::PAGE_ID_SIZE, '4');
bool on_empty_called;
bool on_available_called;
PageAvailabilityManager page_availability_manager;
page_availability_manager.set_on_empty(
callback::SetWhenCalled(&on_empty_called));
page_availability_manager.OnPageAvailable(
page_id, callback::SetWhenCalled(&on_available_called));
EXPECT_TRUE(page_availability_manager.IsEmpty());
EXPECT_TRUE(on_available_called);
EXPECT_FALSE(on_empty_called);
}
TEST_F(PageAvailabilityManagerTest, SingleBusyPage) {
storage::PageId page_id = std::string(::fuchsia::ledger::PAGE_ID_SIZE, '4');
bool on_empty_called;
bool on_available_called;
PageAvailabilityManager page_availability_manager;
page_availability_manager.set_on_empty(
callback::SetWhenCalled(&on_empty_called));
page_availability_manager.MarkPageBusy(page_id);
page_availability_manager.OnPageAvailable(
page_id, callback::SetWhenCalled(&on_available_called));
EXPECT_FALSE(page_availability_manager.IsEmpty());
EXPECT_FALSE(on_available_called);
EXPECT_FALSE(on_empty_called);
}
TEST_F(PageAvailabilityManagerTest, MultiplePages) {
auto bit_generator = environment_.random()->NewBitGenerator<size_t>();
size_t page_count = std::uniform_int_distribution(2u, 20u)(bit_generator);
// The number of callbacks per page to be registered with the
// PageAvailabilityManager.
int dimension_count = std::uniform_int_distribution(2, 10)(bit_generator);
bool page_available_called[page_count][dimension_count];
std::vector<storage::PageId> page_ids;
std::map<storage::PageId, size_t> indices;
for (uint8_t i = 0; i < page_count; i++) {
storage::PageId page_id =
std::string(::fuchsia::ledger::PAGE_ID_SIZE, i + 'a');
page_ids.push_back(page_id);
indices[page_id] = i;
}
bool on_empty_called;
PageAvailabilityManager page_availability_manager;
page_availability_manager.set_on_empty(
callback::SetWhenCalled(&on_empty_called));
EXPECT_TRUE(page_availability_manager.IsEmpty());
EXPECT_FALSE(on_empty_called);
// In a random order, mark all the pages busy.
std::vector<storage::PageId> remaining_page_ids = page_ids;
std::shuffle(remaining_page_ids.begin(), remaining_page_ids.end(),
bit_generator);
while (!remaining_page_ids.empty()) {
auto page_id = remaining_page_ids.back();
page_availability_manager.MarkPageBusy(page_id);
remaining_page_ids.pop_back();
}
EXPECT_FALSE(on_empty_called);
// As many times as the number of callbacks to be registered for a page...
for (int dimension = 0; dimension < dimension_count; dimension++) {
// ... register a single callback for each page (in a random page ordering)
remaining_page_ids = page_ids;
std::shuffle(remaining_page_ids.begin(), remaining_page_ids.end(),
bit_generator);
while (!remaining_page_ids.empty()) {
auto page_id = remaining_page_ids.back();
page_availability_manager.OnPageAvailable(
page_id, callback::SetWhenCalled(
&page_available_called[indices[page_id]][dimension]));
EXPECT_FALSE(page_available_called[indices[page_id]][dimension]);
remaining_page_ids.pop_back();
}
}
// In a random order, mark all the pages available and verify that each page's
// callbacks are called.
remaining_page_ids = page_ids;
std::shuffle(remaining_page_ids.begin(), remaining_page_ids.end(),
bit_generator);
while (!remaining_page_ids.empty()) {
EXPECT_FALSE(on_empty_called);
auto page_id = remaining_page_ids.back();
page_availability_manager.MarkPageAvailable(page_id);
for (int dimension = 0; dimension < dimension_count; dimension++) {
EXPECT_TRUE(page_available_called[indices[page_id]][dimension]);
}
remaining_page_ids.pop_back();
}
EXPECT_TRUE(on_empty_called);
}
TEST_F(PageAvailabilityManagerTest, PageAvailabilityManagerReusable) {
storage::PageId first_page_id =
std::string(::fuchsia::ledger::PAGE_ID_SIZE, '8');
storage::PageId second_page_id =
std::string(::fuchsia::ledger::PAGE_ID_SIZE, '9');
bool on_empty_called;
bool first_on_available_called;
bool second_on_available_called;
PageAvailabilityManager page_availability_manager;
page_availability_manager.set_on_empty(
callback::SetWhenCalled(&on_empty_called));
page_availability_manager.MarkPageBusy(first_page_id);
page_availability_manager.OnPageAvailable(
first_page_id, callback::SetWhenCalled(&first_on_available_called));
EXPECT_FALSE(page_availability_manager.IsEmpty());
EXPECT_FALSE(first_on_available_called);
EXPECT_FALSE(on_empty_called);
page_availability_manager.MarkPageBusy(second_page_id);
page_availability_manager.OnPageAvailable(
second_page_id, callback::SetWhenCalled(&second_on_available_called));
EXPECT_FALSE(page_availability_manager.IsEmpty());
EXPECT_FALSE(first_on_available_called);
EXPECT_FALSE(second_on_available_called);
EXPECT_FALSE(on_empty_called);
page_availability_manager.MarkPageAvailable(second_page_id);
page_availability_manager.MarkPageAvailable(first_page_id);
EXPECT_TRUE(page_availability_manager.IsEmpty());
EXPECT_TRUE(first_on_available_called);
EXPECT_TRUE(second_on_available_called);
EXPECT_TRUE(on_empty_called);
on_empty_called = false;
first_on_available_called = false;
second_on_available_called = false;
page_availability_manager.MarkPageBusy(second_page_id);
page_availability_manager.MarkPageBusy(first_page_id);
page_availability_manager.OnPageAvailable(
second_page_id, callback::SetWhenCalled(&second_on_available_called));
page_availability_manager.OnPageAvailable(
first_page_id, callback::SetWhenCalled(&first_on_available_called));
EXPECT_FALSE(page_availability_manager.IsEmpty());
EXPECT_FALSE(first_on_available_called);
EXPECT_FALSE(second_on_available_called);
EXPECT_FALSE(on_empty_called);
page_availability_manager.MarkPageAvailable(first_page_id);
page_availability_manager.MarkPageAvailable(second_page_id);
EXPECT_TRUE(page_availability_manager.IsEmpty());
EXPECT_TRUE(first_on_available_called);
EXPECT_TRUE(second_on_available_called);
EXPECT_TRUE(on_empty_called);
}
TEST_F(PageAvailabilityManagerTest, CallbacksNotCalledOnDestruction) {
storage::PageId first_page_id =
std::string(::fuchsia::ledger::PAGE_ID_SIZE, '8');
storage::PageId second_page_id =
std::string(::fuchsia::ledger::PAGE_ID_SIZE, '9');
bool on_empty_called;
bool first_on_available_called;
bool second_on_available_called;
auto page_availability_manager = std::make_unique<PageAvailabilityManager>();
page_availability_manager->set_on_empty(
callback::SetWhenCalled(&on_empty_called));
page_availability_manager->MarkPageBusy(first_page_id);
page_availability_manager->OnPageAvailable(
first_page_id, callback::SetWhenCalled(&first_on_available_called));
EXPECT_FALSE(page_availability_manager->IsEmpty());
EXPECT_FALSE(first_on_available_called);
EXPECT_FALSE(on_empty_called);
page_availability_manager->MarkPageBusy(second_page_id);
page_availability_manager->OnPageAvailable(
second_page_id, callback::SetWhenCalled(&second_on_available_called));
EXPECT_FALSE(page_availability_manager->IsEmpty());
EXPECT_FALSE(first_on_available_called);
EXPECT_FALSE(second_on_available_called);
EXPECT_FALSE(on_empty_called);
page_availability_manager.reset();
EXPECT_FALSE(first_on_available_called);
EXPECT_FALSE(second_on_available_called);
EXPECT_FALSE(on_empty_called);
}
} // namespace
} // namespace ledger