blob: f05af2b79e7c2cbdfe80a4ba23d691e54b7b6391 [file] [log] [blame]
// Copyright 2010-2015, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <algorithm>
#include <string>
#include <vector>
#include "base/clock_mock.h"
#include "base/port.h"
#include "base/util.h"
#include "config/config.pb.h"
#include "config/config_handler.h"
#include "converter/converter_mock.h"
#include "engine/mock_converter_engine.h"
#include "engine/mock_data_engine_factory.h"
#include "engine/user_data_manager_mock.h"
#include "session/commands.pb.h"
#include "session/generic_storage_manager.h"
#include "session/session_handler.h"
#include "session/session_handler_test_util.h"
#include "testing/base/public/googletest.h"
#include "testing/base/public/gunit.h"
#include "usage_stats/usage_stats.h"
#include "usage_stats/usage_stats_testing_util.h"
DECLARE_int32(max_session_size);
DECLARE_int32(create_session_min_interval);
DECLARE_int32(last_command_timeout);
DECLARE_int32(last_create_session_timeout);
namespace mozc {
using mozc::session::testing::CreateSession;
using mozc::session::testing::CleanUp;
using mozc::session::testing::IsGoodSession;
using mozc::session::testing::SessionHandlerTestBase;
class SessionHandlerTest : public SessionHandlerTestBase {
protected:
virtual void SetUp() {
SessionHandlerTestBase::SetUp();
Util::SetClockHandler(NULL);
GenericStorageManagerFactory::SetGenericStorageManager(NULL);
}
virtual void TearDown() {
GenericStorageManagerFactory::SetGenericStorageManager(NULL);
Util::SetClockHandler(NULL);
SessionHandlerTestBase::TearDown();
}
};
TEST_F(SessionHandlerTest, MaxSessionSizeTest) {
uint32 expected_session_created_num = 0;
const int32 interval_time = FLAGS_create_session_min_interval = 10; // 10 sec
ClockMock clock(1000, 0);
Util::SetClockHandler(&clock);
// The oldest item is remvoed
const size_t session_size = 3;
FLAGS_max_session_size = static_cast<int32>(session_size);
{
scoped_ptr<EngineInterface> engine(MockDataEngineFactory::Create());
SessionHandler handler(engine.get());
// Create session_size + 1 sessions
vector<uint64> ids;
for (size_t i = 0; i <= session_size; ++i) {
uint64 id = 0;
EXPECT_TRUE(CreateSession(&handler, &id));
++expected_session_created_num;
EXPECT_COUNT_STATS("SessionCreated", expected_session_created_num);
ids.push_back(id);
clock.PutClockForward(interval_time, 0);
}
for (int i = static_cast<int>(ids.size() - 1); i >= 0; --i) {
if (i > 0) { // this id is alive
EXPECT_TRUE(IsGoodSession(&handler, ids[i]));
} else { // the first id shuold be removed
EXPECT_FALSE(IsGoodSession(&handler, ids[i]));
}
}
}
FLAGS_max_session_size = static_cast<int32>(session_size);
{
scoped_ptr<EngineInterface> engine(MockDataEngineFactory::Create());
SessionHandler handler(engine.get());
// Create session_size sessions
vector<uint64> ids;
for (size_t i = 0; i < session_size; ++i) {
uint64 id = 0;
EXPECT_TRUE(CreateSession(&handler, &id));
++expected_session_created_num;
EXPECT_COUNT_STATS("SessionCreated", expected_session_created_num);
ids.push_back(id);
clock.PutClockForward(interval_time, 0);
}
random_shuffle(ids.begin(), ids.end());
const uint64 oldest_id = ids[0];
for (size_t i = 0; i < session_size; ++i) {
EXPECT_TRUE(IsGoodSession(&handler, ids[i]));
}
// Create new session
uint64 id = 0;
EXPECT_TRUE(CreateSession(&handler, &id));
++expected_session_created_num;
EXPECT_COUNT_STATS("SessionCreated", expected_session_created_num);
// the oldest id no longer exists
EXPECT_FALSE(IsGoodSession(&handler, oldest_id));
}
}
TEST_F(SessionHandlerTest, CreateSessionMinInterval) {
const int32 interval_time = FLAGS_create_session_min_interval = 10; // 10 sec
ClockMock clock(1000, 0);
Util::SetClockHandler(&clock);
scoped_ptr<EngineInterface> engine(MockDataEngineFactory::Create());
SessionHandler handler(engine.get());
uint64 id = 0;
EXPECT_TRUE(CreateSession(&handler, &id));
EXPECT_FALSE(CreateSession(&handler, &id));
clock.PutClockForward(interval_time - 1, 0);
EXPECT_FALSE(CreateSession(&handler, &id));
clock.PutClockForward(1, 0);
EXPECT_TRUE(CreateSession(&handler, &id));
}
TEST_F(SessionHandlerTest, LastCreateSessionTimeout) {
const int32 timeout = FLAGS_last_create_session_timeout = 10; // 10 sec
ClockMock clock(1000, 0);
Util::SetClockHandler(&clock);
scoped_ptr<EngineInterface> engine(MockDataEngineFactory::Create());
SessionHandler handler(engine.get());
uint64 id = 0;
EXPECT_TRUE(CreateSession(&handler, &id));
clock.PutClockForward(timeout, 0);
EXPECT_TRUE(CleanUp(&handler, id));
// the session is removed by server
EXPECT_FALSE(IsGoodSession(&handler, id));
}
TEST_F(SessionHandlerTest, LastCommandTimeout) {
const int32 timeout = FLAGS_last_command_timeout = 10; // 10 sec
ClockMock clock(1000, 0);
Util::SetClockHandler(&clock);
scoped_ptr<EngineInterface> engine(MockDataEngineFactory::Create());
SessionHandler handler(engine.get());
uint64 id = 0;
EXPECT_TRUE(CreateSession(&handler, &id));
EXPECT_TRUE(CleanUp(&handler, id));
EXPECT_TRUE(IsGoodSession(&handler, id));
clock.PutClockForward(timeout, 0);
EXPECT_TRUE(CleanUp(&handler, id));
EXPECT_FALSE(IsGoodSession(&handler, id));
}
TEST_F(SessionHandlerTest, ShutdownTest) {
scoped_ptr<EngineInterface> engine(MockDataEngineFactory::Create());
SessionHandler handler(engine.get());
uint64 session_id = 0;
EXPECT_TRUE(CreateSession(&handler, &session_id));
{
commands::Command command;
commands::Input *input = command.mutable_input();
input->set_id(session_id);
input->set_type(commands::Input::SHUTDOWN);
// EvalCommand returns false since the session no longer exists.
EXPECT_FALSE(handler.EvalCommand(&command));
EXPECT_EQ(session_id, command.output().id());
}
{ // Any command should be rejected after shutdown.
commands::Command command;
commands::Input *input = command.mutable_input();
input->set_id(session_id);
input->set_type(commands::Input::NO_OPERATION);
EXPECT_FALSE(handler.EvalCommand(&command));
}
EXPECT_COUNT_STATS("ShutDown", 1);
// CreateSession and Shutdown.
EXPECT_COUNT_STATS("SessionAllEvent", 2);
}
TEST_F(SessionHandlerTest, ClearHistoryTest) {
scoped_ptr<EngineInterface> engine(MockDataEngineFactory::Create());
SessionHandler handler(engine.get());
uint64 session_id = 0;
EXPECT_TRUE(CreateSession(&handler, &session_id));
{
commands::Command command;
commands::Input *input = command.mutable_input();
input->set_id(session_id);
input->set_type(commands::Input::CLEAR_USER_HISTORY);
EXPECT_TRUE(handler.EvalCommand(&command));
EXPECT_EQ(session_id, command.output().id());
EXPECT_COUNT_STATS("ClearUserHistory", 1);
}
{
commands::Command command;
commands::Input *input = command.mutable_input();
input->set_id(session_id);
input->set_type(commands::Input::CLEAR_USER_PREDICTION);
EXPECT_TRUE(handler.EvalCommand(&command));
EXPECT_EQ(session_id, command.output().id());
EXPECT_COUNT_STATS("ClearUserPrediction", 1);
}
{
commands::Command command;
commands::Input *input = command.mutable_input();
input->set_id(session_id);
input->set_type(commands::Input::CLEAR_UNUSED_USER_PREDICTION);
EXPECT_TRUE(handler.EvalCommand(&command));
EXPECT_EQ(session_id, command.output().id());
EXPECT_COUNT_STATS("ClearUnusedUserPrediction", 1);
}
// CreateSession and Clear{History|UserPrediction|UnusedUserPrediction}.
EXPECT_COUNT_STATS("SessionAllEvent", 4);
}
TEST_F(SessionHandlerTest, ElapsedTimeTest) {
scoped_ptr<EngineInterface> engine(MockDataEngineFactory::Create());
SessionHandler handler(engine.get());
uint64 id = 0;
ClockMock clock(1000, 0);
Util::SetClockHandler(&clock);
EXPECT_TRUE(CreateSession(&handler, &id));
EXPECT_TIMING_STATS("ElapsedTimeUSec", 0, 1, 0, 0);
}
TEST_F(SessionHandlerTest, ConfigTest) {
config::Config config;
config::ConfigHandler::GetStoredConfig(&config);
config.set_incognito_mode(false);
config::ConfigHandler::SetConfig(config);
scoped_ptr<EngineInterface> engine(MockDataEngineFactory::Create());
SessionHandler handler(engine.get());
uint64 session_id = 0;
EXPECT_TRUE(CreateSession(&handler, &session_id));
{
commands::Command command;
commands::Input *input = command.mutable_input();
input->set_id(session_id);
input->set_type(commands::Input::GET_CONFIG);
EXPECT_TRUE(handler.EvalCommand(&command));
EXPECT_EQ(command.input().id(), command.output().id());
EXPECT_FALSE(command.output().config().incognito_mode());
}
{
commands::Command command;
commands::Input *input = command.mutable_input();
input->set_id(session_id);
input->set_type(commands::Input::SET_CONFIG);
config.set_incognito_mode(true);
input->mutable_config()->CopyFrom(config);
EXPECT_TRUE(handler.EvalCommand(&command));
EXPECT_EQ(command.input().id(), command.output().id());
EXPECT_TRUE(command.output().config().incognito_mode());
config::ConfigHandler::GetStoredConfig(&config);
EXPECT_TRUE(config.incognito_mode());
}
EXPECT_COUNT_STATS("SetConfig", 1);
// CreateSession, GetConfig and SetConfig.
EXPECT_COUNT_STATS("SessionAllEvent", 3);
}
TEST_F(SessionHandlerTest, VerifySyncIsCalled) {
// Tests if sync is called for the following input commands.
commands::Input::CommandType command_types[] = {
commands::Input::DELETE_SESSION,
commands::Input::CLEANUP,
};
for (size_t i = 0; i < arraysize(command_types); ++i) {
MockConverterEngine engine;
// Set the mock user data manager to the converter mock created above. This
// user_data_manager_mock is owned by the converter mock inside the engine
// instance.
UserDataManagerMock *user_data_mgr_mock = new UserDataManagerMock();
engine.SetUserDataManager(user_data_mgr_mock);
// Set up a session handler and a input command.
SessionHandler handler(&engine);
commands::Command command;
command.mutable_input()->set_id(0);
command.mutable_input()->set_type(command_types[i]);
// Check if Sync() is called after evaluating the command.
EXPECT_EQ(0, user_data_mgr_mock->GetFunctionCallCount("Sync"));
handler.EvalCommand(&command);
EXPECT_EQ(1, user_data_mgr_mock->GetFunctionCallCount("Sync"));
}
}
const char *kStorageTestData[] = {
"angel", "bishop", "chariot", "dragon",
};
class MockStorage : public GenericStorageInterface {
public:
int insert_count;
int clear_count;
const char **insert_expect;
MockStorage() : insert_count(0), clear_count(0) {}
virtual ~MockStorage() {}
virtual bool Insert(const string &key, const char *value) {
EXPECT_EQ(string(insert_expect[insert_count]), key);
EXPECT_EQ(string(insert_expect[insert_count]), string(value));
++insert_count;
return true;
}
virtual const char *Lookup(const string &key) {
return NULL;
}
virtual bool GetAllValues(vector<string> *values) {
values->clear();
for (size_t i = 0; i < arraysize(kStorageTestData); ++i) {
values->push_back(kStorageTestData[i]);
}
return true;
}
virtual bool Clear() {
++clear_count;
return true;
}
void SetInsertExpect(const char **expect) {
insert_expect = expect;
}
};
class MockStorageManager : public GenericStorageManagerInterface {
public:
virtual GenericStorageInterface *GetStorage(
commands::GenericStorageEntry::StorageType storage_type) {
return storage;
}
void SetStorage(MockStorage *newStorage) {
storage = newStorage;
}
private:
MockStorage *storage;
};
// Tests basic behavior of InsertToStorage and ReadAllFromStorage methods.
TEST_F(SessionHandlerTest, StorageTest) {
// Inject mock objects.
MockStorageManager storageManager;
GenericStorageManagerFactory::SetGenericStorageManager(&storageManager);
scoped_ptr<EngineInterface> engine(MockDataEngineFactory::Create());
SessionHandler handler(engine.get());
{
// InsertToStorage
MockStorage mock_storage;
mock_storage.SetInsertExpect(kStorageTestData);
storageManager.SetStorage(&mock_storage);
commands::Command command;
command.mutable_input()->set_type(commands::Input::INSERT_TO_STORAGE);
commands::GenericStorageEntry *storage_entry =
command.mutable_input()->mutable_storage_entry();
storage_entry->set_type(commands::GenericStorageEntry::SYMBOL_HISTORY);
storage_entry->mutable_key()->assign("dummy key");
for (size_t i = 0; i < arraysize(kStorageTestData); ++i) {
storage_entry->mutable_value()->Add()->assign(kStorageTestData[i]);
}
EXPECT_TRUE(handler.InsertToStorage(&command));
EXPECT_EQ(arraysize(kStorageTestData), mock_storage.insert_count);
}
{
// ReadAllFromStorage
MockStorage mock_storage;
storageManager.SetStorage(&mock_storage);
commands::Command command;
command.mutable_input()->set_type(commands::Input::READ_ALL_FROM_STORAGE);
commands::GenericStorageEntry *storage_entry =
command.mutable_input()->mutable_storage_entry();
storage_entry->set_type(commands::GenericStorageEntry::EMOTICON_HISTORY);
EXPECT_TRUE(handler.ReadAllFromStorage(&command));
EXPECT_EQ(
commands::GenericStorageEntry::EMOTICON_HISTORY,
command.output().storage_entry().type());
EXPECT_EQ(
arraysize(kStorageTestData),
command.output().storage_entry().value().size());
}
{
// Clear
MockStorage mock_storage;
storageManager.SetStorage(&mock_storage);
commands::Command command;
command.mutable_input()->set_type(commands::Input::CLEAR_STORAGE);
commands::GenericStorageEntry *storage_entry =
command.mutable_input()->mutable_storage_entry();
storage_entry->set_type(commands::GenericStorageEntry::EMOTICON_HISTORY);
EXPECT_TRUE(handler.ClearStorage(&command));
EXPECT_EQ(
commands::GenericStorageEntry::EMOTICON_HISTORY,
command.output().storage_entry().type());
EXPECT_EQ(1, mock_storage.clear_count);
}
}
TEST_F(SessionHandlerTest, EmojiUsageStatsTest) {
scoped_ptr<EngineInterface> engine(MockDataEngineFactory::Create());
SessionHandler handler(engine.get());
commands::Command command;
command.mutable_input()->set_type(commands::Input::INSERT_TO_STORAGE);
commands::GenericStorageEntry *storage_entry =
command.mutable_input()->mutable_storage_entry();
storage_entry->set_type(commands::GenericStorageEntry::EMOJI_HISTORY);
storage_entry->mutable_key()->assign("dummy key");
// Carrier emoji "BLACK SUN WITH RAYS"
storage_entry->mutable_value()->Clear();
storage_entry->mutable_value()->Add()->assign("\xF3\xBE\x80\x80");
EXPECT_TRUE(handler.EvalCommand(&command));
EXPECT_COUNT_STATS("CommitCarrierEmoji", 1);
EXPECT_COUNT_STATS("CommitUnicodeEmoji", 0);
storage_entry->mutable_value()->Clear();
// Carrier emoji "BLACK SUN WITH RAYS"
storage_entry->mutable_value()->Add()->assign("\xF3\xBE\x80\x80");
// Carrier emoji "GOOGLE"
storage_entry->mutable_value()->Add()->assign("\xF3\xBE\xBA\xA0");
// Unicode emoji "BLACK SUN WITH RAYS"
storage_entry->mutable_value()->Add()->assign("\xE2\x98\x80");
// Unicode emoji "RABBIT FACE"
storage_entry->mutable_value()->Add()->assign("\xF0\x9F\x90\xB0");
EXPECT_TRUE(handler.EvalCommand(&command));
EXPECT_COUNT_STATS("CommitCarrierEmoji", 3);
EXPECT_COUNT_STATS("CommitUnicodeEmoji", 2);
}
} // namespace mozc