blob: f58bc129be7f9d9391b9d161896f8922de8758f3 [file] [log] [blame]
// Copyright 2015 The Cobalt Authors. All Rights Reserved.
//
// 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 "cobalt/storage/storage_manager.h"
#include <memory>
#include <string>
#include "base/bind.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/path_service.h"
#include "base/synchronization/waitable_event.h"
#include "base/threading/platform_thread.h"
#include "cobalt/base/cobalt_paths.h"
#include "cobalt/storage/savegame_fake.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using ::testing::_;
using ::testing::InvokeWithoutArgs;
using ::testing::NotNull;
using ::testing::Eq;
namespace cobalt {
namespace storage {
namespace {
// Used to be able to intercept QueueFlush().
class MockStorageManager : public StorageManager {
public:
MockStorageManager(
std::unique_ptr<StorageManager::UpgradeHandler> upgrade_handler,
const Options& options)
: StorageManager(std::move(upgrade_handler), options) {}
#ifndef GMOCK_NO_MOVE_MOCK
MOCK_METHOD1(QueueFlush, void(base::OnceClosure callback));
#endif
};
class MockUpgradeHandler : public StorageManager::UpgradeHandler {
public:
MOCK_METHOD3(OnUpgrade, void(StorageManager*, const char*, int));
};
class CallbackWaiter {
public:
CallbackWaiter()
: was_called_event_(base::WaitableEvent::ResetPolicy::MANUAL,
base::WaitableEvent::InitialState::NOT_SIGNALED) {}
virtual ~CallbackWaiter() {}
bool TimedWait() {
return was_called_event_.TimedWait(base::TimeDelta::FromSeconds(5));
}
bool IsSignaled() { return was_called_event_.IsSignaled(); }
protected:
void Signal() { was_called_event_.Signal(); }
private:
base::WaitableEvent was_called_event_;
DISALLOW_COPY_AND_ASSIGN(CallbackWaiter);
};
class FlushWaiter : public CallbackWaiter {
public:
FlushWaiter() {}
void OnFlushDone() { Signal(); }
private:
DISALLOW_COPY_AND_ASSIGN(FlushWaiter);
};
class MemoryStoreWaiter : public CallbackWaiter {
public:
MemoryStoreWaiter() {}
void OnMemoryStore(MemoryStore* memory_store) {
Signal();
}
private:
DISALLOW_COPY_AND_ASSIGN(MemoryStoreWaiter);
};
class ReadOnlyMemoryStoreWaiter : public CallbackWaiter {
public:
ReadOnlyMemoryStoreWaiter() {}
void OnMemoryStore(const MemoryStore& memory_store) {
Signal();
}
private:
DISALLOW_COPY_AND_ASSIGN(ReadOnlyMemoryStoreWaiter);
};
void FlushCallback(MemoryStore* memory_store) {
EXPECT_NE(memory_store, nullptr);
}
} // namespace
class StorageManagerTest : public ::testing::Test {
protected:
StorageManagerTest() : message_loop_(base::MessageLoop::TYPE_DEFAULT) {}
~StorageManagerTest() { storage_manager_.reset(NULL); }
template <typename StorageManagerType>
void Init(bool delete_savegame = true,
const Savegame::ByteVector* initial_data = NULL) {
// Destroy the current one first. We can't have two VFSs with the same name
// concurrently.
storage_manager_.reset(NULL);
std::unique_ptr<StorageManager::UpgradeHandler> upgrade_handler(
new MockUpgradeHandler());
StorageManager::Options options;
options.savegame_options.delete_on_destruction = delete_savegame;
options.savegame_options.factory = &SavegameFake::Create;
if (initial_data) {
options.savegame_options.test_initial_data = *initial_data;
}
storage_manager_.reset(
new StorageManagerType(std::move(upgrade_handler), options));
}
template <typename StorageManagerType>
void FinishIO() {
storage_manager_->FinishIO();
}
base::MessageLoop message_loop_;
std::unique_ptr<StorageManager> storage_manager_;
};
TEST_F(StorageManagerTest, WithMemoryStore) {
Init<StorageManager>();
MemoryStoreWaiter waiter;
storage_manager_->WithMemoryStore(
base::Bind(&MemoryStoreWaiter::OnMemoryStore, base::Unretained(&waiter)));
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(waiter.TimedWait());
}
TEST_F(StorageManagerTest, WithReadOnlyMemoryStore) {
Init<StorageManager>();
ReadOnlyMemoryStoreWaiter waiter;
storage_manager_->WithReadOnlyMemoryStore(base::Bind(
&ReadOnlyMemoryStoreWaiter::OnMemoryStore, base::Unretained(&waiter)));
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(waiter.TimedWait());
}
TEST_F(StorageManagerTest, FlushNow) {
// Ensure the Flush callback is called.
Init<StorageManager>();
storage_manager_->WithMemoryStore(base::Bind(&FlushCallback));
base::RunLoop().RunUntilIdle();
FlushWaiter waiter;
storage_manager_->FlushNow(
base::Bind(&FlushWaiter::OnFlushDone, base::Unretained(&waiter)));
EXPECT_TRUE(waiter.TimedWait());
}
#ifndef GMOCK_NO_MOVE_MOCK
TEST_F(StorageManagerTest, FlushNowWithFlushOnChange) {
// Test that the Flush callback is called exactly once, despite calling both
// FlushOnChange() and FlushNow().
Init<MockStorageManager>();
storage_manager_->WithMemoryStore(base::Bind(&FlushCallback));
base::RunLoop().RunUntilIdle();
FlushWaiter waiter;
MockStorageManager& storage_manager =
*dynamic_cast<MockStorageManager*>(storage_manager_.get());
// When QueueFlush() is called, have it also call FlushWaiter::OnFlushDone().
ON_CALL(storage_manager, QueueFlush(_))
.WillByDefault(InvokeWithoutArgs(&waiter, &FlushWaiter::OnFlushDone));
EXPECT_CALL(storage_manager, QueueFlush(_)).Times(1);
storage_manager_->FlushOnChange();
storage_manager_->FlushNow(
base::Bind(&FlushWaiter::OnFlushDone, base::Unretained(&waiter)));
base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(3000));
EXPECT_TRUE(waiter.IsSignaled());
}
TEST_F(StorageManagerTest, FlushOnChange) {
// Test that the Flush callback is called exactly once, despite calling
// FlushOnChange() multiple times.
Init<MockStorageManager>();
storage_manager_->WithMemoryStore(base::Bind(&FlushCallback));
base::RunLoop().RunUntilIdle();
FlushWaiter waiter;
MockStorageManager& storage_manager =
*dynamic_cast<MockStorageManager*>(storage_manager_.get());
// When QueueFlush() is called, have it also call FlushWaiter::OnFlushDone().
// We will wait for this in TimedWait().
ON_CALL(storage_manager, QueueFlush(_))
.WillByDefault(InvokeWithoutArgs(&waiter, &FlushWaiter::OnFlushDone));
EXPECT_CALL(storage_manager, QueueFlush(_)).Times(1);
for (int i = 0; i < 10; ++i) {
storage_manager_->FlushOnChange();
}
base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(3000));
EXPECT_TRUE(waiter.TimedWait());
}
TEST_F(StorageManagerTest, FlushOnChangeMaxDelay) {
// Test that the Flush callback is called once from hitting the max delay when
// there are constant calls to FlushOnChange().
Init<MockStorageManager>();
storage_manager_->WithMemoryStore(base::Bind(&FlushCallback));
base::RunLoop().RunUntilIdle();
FlushWaiter waiter;
MockStorageManager& storage_manager =
*dynamic_cast<MockStorageManager*>(storage_manager_.get());
// When QueueFlush() is called, have it also call FlushWaiter::OnFlushDone().
ON_CALL(storage_manager, QueueFlush(_))
.WillByDefault(InvokeWithoutArgs(&waiter, &FlushWaiter::OnFlushDone));
EXPECT_CALL(storage_manager, QueueFlush(_)).Times(1);
for (int i = 0; i < 30; ++i) {
base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100));
storage_manager_->FlushOnChange();
}
EXPECT_TRUE(waiter.IsSignaled());
}
TEST_F(StorageManagerTest, FlushOnShutdown) {
// Test that pending flushes are completed on shutdown.
Init<MockStorageManager>();
storage_manager_->WithMemoryStore(base::Bind(&FlushCallback));
base::RunLoop().RunUntilIdle();
FlushWaiter waiter;
MockStorageManager& storage_manager =
*dynamic_cast<MockStorageManager*>(storage_manager_.get());
// When QueueFlush() is called, have it also call FlushWaiter::OnFlushDone().
ON_CALL(storage_manager, QueueFlush(_))
.WillByDefault(InvokeWithoutArgs(&waiter, &FlushWaiter::OnFlushDone));
EXPECT_CALL(storage_manager, QueueFlush(_)).Times(1);
storage_manager_->FlushOnChange();
FinishIO<StorageManager>();
storage_manager_.reset();
EXPECT_TRUE(waiter.IsSignaled());
}
#endif
TEST_F(StorageManagerTest, Upgrade) {
Savegame::ByteVector initial_data;
initial_data.push_back('U');
initial_data.push_back('P');
initial_data.push_back('G');
initial_data.push_back('0');
Init<StorageManager>(true, &initial_data);
// We expect a call to the upgrade handler when it reads this data.
MockUpgradeHandler& upgrade_handler =
*dynamic_cast<MockUpgradeHandler*>(storage_manager_->upgrade_handler());
EXPECT_CALL(upgrade_handler,
OnUpgrade(Eq(storage_manager_.get()), NotNull(), Eq(4)))
.Times(1);
FlushWaiter waiter;
storage_manager_->FlushNow(
base::Bind(&FlushWaiter::OnFlushDone, base::Unretained(&waiter)));
EXPECT_TRUE(waiter.TimedWait());
base::RunLoop().RunUntilIdle();
}
} // namespace storage
} // namespace cobalt