blob: a9103446fad6da8810d16bb1b34763a2e8dd6f0f [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 "dictionary/user_dictionary_storage.h"
#include <string>
#include <vector>
#include "base/file_stream.h"
#include "base/file_util.h"
#include "base/logging.h"
#include "base/mmap.h"
#include "base/number_util.h"
#include "base/protobuf/unknown_field_set.h"
#include "base/system_util.h"
#include "base/util.h"
#include "dictionary/user_dictionary_importer.h"
#include "dictionary/user_dictionary_util.h"
#include "testing/base/public/googletest.h"
#include "testing/base/public/gunit.h"
#include "testing/base/public/testing_util.h"
DECLARE_string(test_tmpdir);
namespace mozc {
using mozc::protobuf::UnknownField;
using mozc::protobuf::UnknownFieldSet;
using mozc::testing::SerializeUnknownFieldSetAsString;
using user_dictionary::UserDictionary;
namespace {
string GenRandomString(int size) {
string result;
const size_t len = Util::Random(size) + 1;
for (int i = 0; i < len; ++i) {
const char32 l =
static_cast<char32>(Util::Random(static_cast<int>('~' - ' ')) + ' ');
Util::UCS4ToUTF8Append(l, &result);
}
return result;
}
} // namespace
class UserDictionaryStorageTest : public ::testing::Test {
protected:
virtual void SetUp() {
backup_user_profile_directory_ = SystemUtil::GetUserProfileDirectory();
SystemUtil::SetUserProfileDirectory(FLAGS_test_tmpdir);
FileUtil::Unlink(GetUserDictionaryFile());
}
virtual void TearDown() {
FileUtil::Unlink(GetUserDictionaryFile());
SystemUtil::SetUserProfileDirectory(backup_user_profile_directory_);
}
static string GetUserDictionaryFile() {
return FileUtil::JoinPath(FLAGS_test_tmpdir, "test.db");
}
private:
string backup_user_profile_directory_;
};
TEST_F(UserDictionaryStorageTest, FileTest) {
UserDictionaryStorage storage(GetUserDictionaryFile());
EXPECT_EQ(storage.filename(), GetUserDictionaryFile());
EXPECT_FALSE(storage.Exists());
}
TEST_F(UserDictionaryStorageTest, LockTest) {
UserDictionaryStorage storage1(GetUserDictionaryFile());
UserDictionaryStorage storage2(GetUserDictionaryFile());
EXPECT_FALSE(storage1.Save());
EXPECT_FALSE(storage2.Save());
EXPECT_TRUE(storage1.Lock());
EXPECT_FALSE(storage2.Lock());
EXPECT_TRUE(storage1.Save());
EXPECT_FALSE(storage2.Save());
EXPECT_TRUE(storage1.UnLock());
EXPECT_FALSE(storage1.Save());
EXPECT_FALSE(storage2.Save());
EXPECT_TRUE(storage2.Lock());
EXPECT_FALSE(storage1.Save());
EXPECT_TRUE(storage2.Save());
}
TEST_F(UserDictionaryStorageTest, BasicOperationsTest) {
UserDictionaryStorage storage(GetUserDictionaryFile());
EXPECT_FALSE(storage.Load());
const size_t kDictionariesSize = 3;
uint64 id[kDictionariesSize];
const size_t dict_size = storage.dictionaries_size();
for (size_t i = 0; i < kDictionariesSize; ++i) {
EXPECT_TRUE(storage.CreateDictionary(
"test" + NumberUtil::SimpleItoa(static_cast<uint32>(i)),
&id[i]));
EXPECT_EQ(i + 1 + dict_size, storage.dictionaries_size());
}
for (size_t i = 0; i < kDictionariesSize; ++i) {
EXPECT_EQ(i + dict_size, storage.GetUserDictionaryIndex(id[i]));
EXPECT_EQ(-1, storage.GetUserDictionaryIndex(id[i] + 1));
}
for (size_t i = 0; i < kDictionariesSize; ++i) {
EXPECT_EQ(storage.mutable_dictionaries(i + dict_size),
storage.GetUserDictionary(id[i]));
EXPECT_EQ(NULL, storage.GetUserDictionary(id[i] + 1));
}
// empty
EXPECT_FALSE(storage.RenameDictionary(id[0], ""));
// duplicated
uint64 tmp_id = 0;
EXPECT_FALSE(storage.CreateDictionary("test0", &tmp_id));
EXPECT_EQ(UserDictionaryStorage::DUPLICATED_DICTIONARY_NAME,
storage.GetLastError());
// invalid id
EXPECT_FALSE(storage.RenameDictionary(0, ""));
// duplicated
EXPECT_FALSE(storage.RenameDictionary(id[0], "test1"));
EXPECT_EQ(UserDictionaryStorage::DUPLICATED_DICTIONARY_NAME,
storage.GetLastError());
// no name
EXPECT_TRUE(storage.RenameDictionary(id[0], "test0"));
EXPECT_TRUE(storage.RenameDictionary(id[0], "renamed0"));
EXPECT_EQ("renamed0", storage.GetUserDictionary(id[0])->name());
// invalid id
EXPECT_FALSE(storage.DeleteDictionary(0));
EXPECT_TRUE(storage.DeleteDictionary(id[1]));
EXPECT_EQ(kDictionariesSize + dict_size - 1, storage.dictionaries_size());
}
TEST_F(UserDictionaryStorageTest, DeleteTest) {
UserDictionaryStorage storage(GetUserDictionaryFile());
EXPECT_FALSE(storage.Load());
// repeat 10 times
for (int i = 0; i < 10; ++i) {
storage.Clear();
vector<uint64> ids(100);
for (size_t i = 0; i < ids.size(); ++i) {
EXPECT_TRUE(storage.CreateDictionary(
"test" + NumberUtil::SimpleItoa(static_cast<uint32>(i)),
&ids[i]));
}
vector<uint64> alive;
for (size_t i = 0; i < ids.size(); ++i) {
if (Util::Random(3) == 0) { // 33%
EXPECT_TRUE(storage.DeleteDictionary(ids[i]));
continue;
}
alive.push_back(ids[i]);
}
EXPECT_EQ(alive.size(), storage.dictionaries_size());
for (size_t i = 0; i < alive.size(); ++i) {
EXPECT_EQ(alive[i], storage.dictionaries(i).id());
}
}
}
TEST_F(UserDictionaryStorageTest, ExportTest) {
UserDictionaryStorage storage(GetUserDictionaryFile());
uint64 id = 0;
EXPECT_TRUE(storage.CreateDictionary("test", &id));
UserDictionaryStorage::UserDictionary *dic =
storage.GetUserDictionary(id);
for (size_t i = 0; i < 1000; ++i) {
UserDictionaryStorage::UserDictionaryEntry *entry = dic->add_entries();
const string prefix = NumberUtil::SimpleItoa(static_cast<uint32>(i));
// set empty fields randomly
entry->set_key(prefix + "key");
entry->set_value(prefix + "value");
// "名詞"
entry->set_pos(UserDictionary::NOUN);
entry->set_comment(prefix + "comment");
}
const string export_file = FileUtil::JoinPath(FLAGS_test_tmpdir,
"export.txt");
EXPECT_FALSE(storage.ExportDictionary(id + 1, export_file));
EXPECT_TRUE(storage.ExportDictionary(id, export_file));
string file_string;
// Copy whole contents of the file into |file_string|.
{
InputFileStream ifs(export_file.c_str());
CHECK(ifs);
ifs.seekg(0, std::ios::end);
file_string.resize(ifs.tellg());
ifs.seekg(0, std::ios::beg);
ifs.read(&file_string[0], file_string.size());
ifs.close();
}
UserDictionaryImporter::StringTextLineIterator iter(file_string);
UserDictionaryStorage::UserDictionary dic2;
EXPECT_EQ(UserDictionaryImporter::IMPORT_NO_ERROR,
UserDictionaryImporter::ImportFromTextLineIterator(
UserDictionaryImporter::MOZC,
&iter, &dic2));
dic2.set_id(id);
dic2.set_name("test");
EXPECT_EQ(dic2.DebugString(), dic->DebugString());
}
TEST_F(UserDictionaryStorageTest, SerializeTest) {
// repeat 20 times
for (int i = 0; i < 20; ++i) {
FileUtil::Unlink(GetUserDictionaryFile());
UserDictionaryStorage storage1(GetUserDictionaryFile());
{
EXPECT_FALSE(storage1.Load());
const size_t dic_size = Util::Random(50) + 1;
for (size_t i = 0; i < dic_size; ++i) {
uint64 id = 0;
EXPECT_TRUE(
storage1.CreateDictionary(
"test" + NumberUtil::SimpleItoa(static_cast<uint32>(i)), &id));
const size_t entry_size = Util::Random(100) + 1;
for (size_t j = 0; j < entry_size; ++j) {
UserDictionaryStorage::UserDictionary *dic =
storage1.mutable_dictionaries(i);
UserDictionaryStorage::UserDictionaryEntry *entry =
dic->add_entries();
entry->set_key(GenRandomString(10));
entry->set_value(GenRandomString(10));
entry->set_pos(UserDictionary::NOUN);
entry->set_comment(GenRandomString(10));
}
}
EXPECT_TRUE(storage1.Lock());
EXPECT_TRUE(storage1.Save());
EXPECT_TRUE(storage1.UnLock());
}
UserDictionaryStorage storage2(GetUserDictionaryFile());
{
EXPECT_TRUE(storage2.Load());
}
EXPECT_EQ(storage1.DebugString(), storage2.DebugString());
}
}
TEST_F(UserDictionaryStorageTest, GetUserDictionaryIdTest) {
UserDictionaryStorage storage(GetUserDictionaryFile());
EXPECT_FALSE(storage.Load());
const size_t kDictionariesSize = 3;
uint64 id[kDictionariesSize];
EXPECT_TRUE(storage.CreateDictionary("testA", &id[0]));
EXPECT_TRUE(storage.CreateDictionary("testB", &id[1]));
uint64 ret_id[kDictionariesSize];
EXPECT_TRUE(storage.GetUserDictionaryId("testA", &ret_id[0]));
EXPECT_TRUE(storage.GetUserDictionaryId("testB", &ret_id[1]));
EXPECT_FALSE(storage.GetUserDictionaryId("testC", &ret_id[2]));
EXPECT_EQ(ret_id[0], id[0]);
EXPECT_EQ(ret_id[1], id[1]);
}
TEST_F(UserDictionaryStorageTest, ConvertSyncDictionariesToNormalDictionaries) {
// "名詞"
const UserDictionary::PosType kPos = UserDictionary::NOUN;
const struct TestData {
bool is_sync_dictionary;
bool is_removed_dictionary;
bool has_normal_entry;
bool has_removed_entry;
string dictionary_name;
} test_data[] = {
{ false, false, false, false, "non-sync dictionary (empty)" },
{ false, false, true, false, "non-sync dictionary (normal entry)" },
{ true, false, false, false, "sync dictionary (empty)" },
{ true, false, false, true, "sync dictionary (removed entry)" },
{ true, false, true, false, "sync dictionary (normal entry)" },
{ true, false, true, true, "sync dictionary (normal & removed entries)" },
{ true, true, false, false, "removed sync dictionary (empty)" },
{ true, true, false, true, "removed sync dictionary (removed entry)" },
{ true, true, true, false, "removed sync dictionary (normal entry)" },
{ true, true, true, true,
"removed sync dictionary (normal & removed entries)" },
{ true, false, true, false,
UserDictionaryStorage::default_sync_dictionary_name() },
};
UserDictionaryStorage storage(GetUserDictionaryFile());
EXPECT_FALSE(storage.Load())
<< "At first, we expect there is not user dictionary file.";
EXPECT_FALSE(storage.ConvertSyncDictionariesToNormalDictionaries())
<< "No sync dictionary available.";
for (size_t i = 0; i < arraysize(test_data); ++i) {
SCOPED_TRACE(Util::StringPrintf("add %d", static_cast<int>(i)));
const TestData &data = test_data[i];
CHECK(data.is_sync_dictionary ||
!(data.is_removed_dictionary || data.has_removed_entry))
<< "Non-sync dictionary should NOT have removed dictionary / entry.";
uint64 dict_id = 0;
ASSERT_TRUE(storage.CreateDictionary(data.dictionary_name, &dict_id));
UserDictionaryStorage::UserDictionary *dict =
storage.mutable_dictionaries(storage.GetUserDictionaryIndex(dict_id));
dict->set_syncable(data.is_sync_dictionary);
dict->set_removed(data.is_removed_dictionary);
if (data.has_normal_entry) {
UserDictionaryStorage::UserDictionaryEntry *entry = dict->add_entries();
entry->set_key("normal");
entry->set_value("normal entry");
entry->set_pos(kPos);
}
if (data.has_removed_entry) {
UserDictionaryStorage::UserDictionaryEntry *entry = dict->add_entries();
entry->set_key("removed");
entry->set_value("removed entry");
entry->set_pos(kPos);
entry->set_removed(true);
}
}
EXPECT_EQ(9, UserDictionaryStorage::CountSyncableDictionaries(storage));
ASSERT_TRUE(storage.ConvertSyncDictionariesToNormalDictionaries());
// "同期用辞書"
const char *kDictionaryNameConvertedFromSyncableDictionary =
"\xE5\x90\x8C\xE6\x9C\x9F\xE7\x94\xA8\xE8\xBE\x9E\xE6\x9B\xB8";
const struct ExpectedData {
bool has_normal_entry;
string dictionary_name;
} expected_data[] = {
{ false, "non-sync dictionary (empty)" },
{ true, "non-sync dictionary (normal entry)" },
{ true, "sync dictionary (normal entry)" },
{ true, "sync dictionary (normal & removed entries)" },
{ true, kDictionaryNameConvertedFromSyncableDictionary },
};
EXPECT_EQ(0, UserDictionaryStorage::CountSyncableDictionaries(storage));
ASSERT_EQ(arraysize(expected_data), storage.dictionaries_size());
for (size_t i = 0; i < arraysize(expected_data); ++i) {
SCOPED_TRACE(Util::StringPrintf("verify %d", static_cast<int>(i)));
const ExpectedData &expected = expected_data[i];
const UserDictionaryStorage::UserDictionary &dict = storage.dictionaries(i);
EXPECT_EQ(expected.dictionary_name, dict.name());
EXPECT_FALSE(dict.syncable());
EXPECT_FALSE(dict.removed());
if (expected.has_normal_entry) {
ASSERT_EQ(1, dict.entries_size());
EXPECT_EQ("normal", dict.entries(0).key());
} else {
EXPECT_EQ(0, dict.entries_size());
}
}
// Test duplicated dictionary name.
storage.Clear();
{
uint64 dict_id = 0;
storage.CreateDictionary(
UserDictionaryStorage::default_sync_dictionary_name(), &dict_id);
storage.CreateDictionary(
kDictionaryNameConvertedFromSyncableDictionary, &dict_id);
ASSERT_EQ(2, storage.dictionaries_size());
UserDictionaryStorage::UserDictionary *dict;
dict = storage.mutable_dictionaries(0);
dict->set_syncable(true);
dict->add_entries()->set_key("0");
dict = storage.mutable_dictionaries(1);
dict->set_syncable(false);
dict->add_entries()->set_key("1");
}
ASSERT_TRUE(storage.ConvertSyncDictionariesToNormalDictionaries());
EXPECT_EQ(0, UserDictionaryStorage::CountSyncableDictionaries(storage));
EXPECT_EQ(2, storage.dictionaries_size());
EXPECT_EQ(Util::StringPrintf("%s_1",
kDictionaryNameConvertedFromSyncableDictionary),
storage.dictionaries(0).name());
EXPECT_EQ(kDictionaryNameConvertedFromSyncableDictionary,
storage.dictionaries(1).name());
}
TEST_F(UserDictionaryStorageTest, AddToAutoRegisteredDictionary) {
{
UserDictionaryStorage storage(GetUserDictionaryFile());
EXPECT_EQ(0, storage.dictionaries_size());
EXPECT_TRUE(storage.AddToAutoRegisteredDictionary(
"key1", "value1", UserDictionary::NOUN));
EXPECT_EQ(1, storage.dictionaries_size());
EXPECT_EQ(1, storage.dictionaries(0).entries_size());
const UserDictionaryStorage::UserDictionaryEntry &entry1 =
storage.dictionaries(0).entries(0);
EXPECT_EQ("key1", entry1.key());
EXPECT_EQ("value1", entry1.value());
EXPECT_EQ(UserDictionary::NOUN, entry1.pos());
EXPECT_TRUE(entry1.auto_registered());
EXPECT_TRUE(storage.AddToAutoRegisteredDictionary(
"key2", "value2", UserDictionary::NOUN));
EXPECT_EQ(1, storage.dictionaries_size());
EXPECT_EQ(2, storage.dictionaries(0).entries_size());
const UserDictionaryStorage::UserDictionaryEntry &entry2 =
storage.dictionaries(0).entries(1);
EXPECT_EQ("key2", entry2.key());
EXPECT_EQ("value2", entry2.value());
EXPECT_EQ(UserDictionary::NOUN, entry2.pos());
EXPECT_TRUE(entry1.auto_registered());
}
{
FileUtil::Unlink(GetUserDictionaryFile());
UserDictionaryStorage storage(GetUserDictionaryFile());
storage.Lock();
// Already locked.
EXPECT_FALSE(storage.AddToAutoRegisteredDictionary(
"key", "value", UserDictionary::NOUN));
}
}
#ifndef OS_ANDROID
// This is a test to check if the data stored by the new binary can be read
// by an older binary, considering the Dev -> Stable converting users.
// We can remove when the new Stable binary which supports reading new format
// is spread enough.
TEST_F(UserDictionaryStorageTest, BackwardCompatibilityTest) {
{
UserDictionaryStorage storage(GetUserDictionaryFile());
// Add dummy entry.
{
UserDictionary *dictionary = storage.add_dictionaries();
UserDictionary::Entry *entry = dictionary->add_entries();
entry->set_key("key");
entry->set_value("value");
entry->set_comment("comment");
entry->set_pos(UserDictionary::NOUN);
}
ASSERT_TRUE(storage.Lock());
ASSERT_TRUE(storage.Save());
ASSERT_TRUE(storage.UnLock());
}
// Make sure that the deprecated field is filled (in string).
UserDictionaryStorage storage(GetUserDictionaryFile());
ASSERT_TRUE(storage.LoadWithoutMigration());
ASSERT_EQ(1, storage.dictionaries_size());
const UserDictionary &dictionary = storage.dictionaries(0);
ASSERT_EQ(1, dictionary.entries_size());
const UserDictionary::Entry &entry = dictionary.entries(0);
const UnknownFieldSet &unknown_field_set = entry.unknown_fields();
ASSERT_EQ(1, unknown_field_set.field_count());
const UnknownField &unknown_field = unknown_field_set.field(0);
EXPECT_EQ(3, unknown_field.number());
EXPECT_EQ(UnknownField::TYPE_LENGTH_DELIMITED, unknown_field.type());
EXPECT_EQ("\xE5\x90\x8D\xE8\xA9\x9E", unknown_field.length_delimited());
}
#endif
TEST_F(UserDictionaryStorageTest, Export) {
const int kDummyDictionaryId = 10;
const string kPath = FileUtil::JoinPath(FLAGS_test_tmpdir, "exported_file");
{
UserDictionaryStorage storage(GetUserDictionaryFile());
{
UserDictionary *dictionary = storage.add_dictionaries();
dictionary->set_id(kDummyDictionaryId);
UserDictionary::Entry *entry = dictionary->add_entries();
entry->set_key("key");
entry->set_value("value");
entry->set_pos(UserDictionary::NOUN);
entry->set_comment("comment");
}
storage.ExportDictionary(kDummyDictionaryId, kPath);
}
Mmap mapped_data;
ASSERT_TRUE(mapped_data.Open(kPath.c_str()));
// Make sure the exported format, especially that the pos is exported in
// Japanese.
// "key value 名詞 comment" separted by a tab character.
#ifdef OS_WIN
EXPECT_EQ("key\tvalue\t\xE5\x90\x8D\xE8\xA9\x9E\tcomment\r\n",
string(mapped_data.begin(), mapped_data.size()));
#else
EXPECT_EQ("key\tvalue\t\xE5\x90\x8D\xE8\xA9\x9E\tcomment\n",
string(mapped_data.begin(), mapped_data.size()));
#endif // OS_WIN
}
namespace {
enum SerializedPosType {
NEW_ENUM_POS,
LEGACY_STRING_POS,
LEGACY_ENUM_POS,
};
void AddTestDummyUserDictionary(
SerializedPosType serialized_pos_type, UnknownFieldSet *storage) {
UnknownFieldSet dictionary;
for (int i = UserDictionary::PosType_MIN;
i <= UserDictionary::PosType_MAX; ++i) {
UnknownFieldSet entry;
entry.AddLengthDelimited(1, Util::StringPrintf("key%d", i));
entry.AddLengthDelimited(2, Util::StringPrintf("value%d", i));
switch (serialized_pos_type) {
case NEW_ENUM_POS:
entry.AddVarint(5, i);
break;
case LEGACY_STRING_POS:
entry.AddLengthDelimited(
3,
UserDictionaryUtil::GetStringPosType(
static_cast<UserDictionary::PosType>(i)));
break;
case LEGACY_ENUM_POS:
entry.AddVarint(3, i);
break;
default:
LOG(FATAL) << "Unknown serialized pos type: " << serialized_pos_type;
}
entry.AddLengthDelimited(4, Util::StringPrintf("comment%d", i));
dictionary.AddLengthDelimited(
4, SerializeUnknownFieldSetAsString(entry));
}
storage->AddLengthDelimited(
2, SerializeUnknownFieldSetAsString(dictionary));
}
void WriteToFile(const string &value, const string &filepath) {
OutputFileStream stream(filepath.c_str(), ios::out|ios::binary|ios::trunc);
stream.write(value.data(), value.size());
}
} // namespace
class UserDictionaryStorageMigrationTest
: public UserDictionaryStorageTest,
public ::testing::WithParamInterface<SerializedPosType> {
};
TEST_P(UserDictionaryStorageMigrationTest, Load) {
{
// Create serialized user dictionary data directly.
UnknownFieldSet storage;
AddTestDummyUserDictionary(GetParam(), &storage);
WriteToFile(SerializeUnknownFieldSetAsString(storage),
GetUserDictionaryFile());
}
UserDictionaryStorage storage(GetUserDictionaryFile());
ASSERT_TRUE(storage.Load());
ASSERT_EQ(1, storage.dictionaries_size()) << storage.Utf8DebugString();
const UserDictionary &dictionary = storage.dictionaries(0);
ASSERT_EQ(UserDictionary::PosType_MAX - UserDictionary::PosType_MIN + 1,
dictionary.entries_size())
<< dictionary.Utf8DebugString();
for (int i = 0; i < dictionary.entries_size(); ++i) {
const int id = UserDictionary::PosType_MIN + i;
const UserDictionary::Entry &entry = dictionary.entries(i);
EXPECT_EQ(Util::StringPrintf("key%d", id), entry.key())
<< entry.Utf8DebugString();
EXPECT_EQ(Util::StringPrintf("value%d", id), entry.value())
<< entry.Utf8DebugString();
EXPECT_EQ(id, entry.pos()) << entry.Utf8DebugString();
EXPECT_EQ(Util::StringPrintf("comment%d", id), entry.comment())
<< entry.Utf8DebugString();
}
}
TEST_P(UserDictionaryStorageMigrationTest, LoadWithoutMigration) {
{
// Create serialized user dictionary data directly.
UnknownFieldSet storage;
AddTestDummyUserDictionary(GetParam(), &storage);
WriteToFile(SerializeUnknownFieldSetAsString(storage),
GetUserDictionaryFile());
}
UserDictionaryStorage storage(GetUserDictionaryFile());
ASSERT_TRUE(storage.LoadWithoutMigration());
ASSERT_EQ(1, storage.dictionaries_size()) << storage.Utf8DebugString();
const UserDictionary &dictionary = storage.dictionaries(0);
ASSERT_EQ(UserDictionary::PosType_MAX - UserDictionary::PosType_MIN + 1,
dictionary.entries_size())
<< dictionary.Utf8DebugString();
for (int i = 0; i < dictionary.entries_size(); ++i) {
const int id = UserDictionary::PosType_MIN + i;
const UserDictionary::Entry &entry = dictionary.entries(i);
EXPECT_EQ(Util::StringPrintf("key%d", id), entry.key())
<< entry.Utf8DebugString();
EXPECT_EQ(Util::StringPrintf("value%d", id), entry.value())
<< entry.Utf8DebugString();
switch (GetParam()) {
case NEW_ENUM_POS:
ASSERT_TRUE(entry.has_pos());
EXPECT_EQ(id, entry.pos());
break;
case LEGACY_STRING_POS: {
const UnknownFieldSet &unknown_field_set = entry.unknown_fields();
ASSERT_EQ(1, unknown_field_set.field_count());
const UnknownField &unknown_field = unknown_field_set.field(0);
EXPECT_EQ(UnknownField::TYPE_LENGTH_DELIMITED, unknown_field.type());
EXPECT_EQ(3, unknown_field.number());
EXPECT_EQ(
UserDictionaryUtil::GetStringPosType(
static_cast<UserDictionary::PosType>(id)),
unknown_field.length_delimited());
break;
}
case LEGACY_ENUM_POS: {
const UnknownFieldSet &unknown_field_set = entry.unknown_fields();
ASSERT_EQ(1, unknown_field_set.field_count());
const UnknownField &unknown_field = unknown_field_set.field(0);
EXPECT_EQ(UnknownField::TYPE_VARINT, unknown_field.type());
EXPECT_EQ(3, unknown_field.number());
EXPECT_EQ(id, unknown_field.varint());
break;
}
default:
LOG(FATAL) << "Unknown serialized pos type: " << GetParam();
}
EXPECT_EQ(Util::StringPrintf("comment%d", id), entry.comment())
<< entry.Utf8DebugString();
}
}
INSTANTIATE_TEST_CASE_P(
UserDictionaryStorageProtoMigration,
UserDictionaryStorageMigrationTest,
::testing::Values(NEW_ENUM_POS, LEGACY_STRING_POS, LEGACY_ENUM_POS));
} // namespace mozc