blob: 342baa98956744c2d1bc0fc918399e94512e5533 [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 "storage/louds/louds_trie.h"
#include <limits>
#include <string>
#include "base/port.h"
#include "storage/louds/key_expansion_table.h"
#include "storage/louds/louds_trie_builder.h"
#include "testing/base/public/gunit.h"
// undef max macro if defined.
#ifdef max
#undef max
#endif // max
namespace mozc {
namespace storage {
namespace louds {
namespace {
class LoudsTrieTest : public ::testing::Test {
};
class TestCallback : public LoudsTrie::Callback {
public:
TestCallback() : current_index_(0), limit_(numeric_limits<size_t>::max()) {
}
void AddExpectation(
const string &s, size_t len, int id, ResultType result_type) {
expectation_list_.push_back(Expectation());
Expectation &expectation = expectation_list_.back();
expectation.s = s;
expectation.len = len;
expectation.id = id;
expectation.result_type = result_type;
}
virtual ResultType Run(const char *s, size_t len, int id) {
if (current_index_ >= expectation_list_.size()) {
ADD_FAILURE() << "Too many invocations: " << expectation_list_.size()
<< " vs " << current_index_;
// Quit the callback.
return SEARCH_DONE;
}
const Expectation &expectation = expectation_list_[current_index_];
EXPECT_EQ(expectation.s, string(s, len)) << current_index_;
EXPECT_EQ(expectation.len, len) << current_index_;
EXPECT_EQ(expectation.id, id) << current_index_;
++current_index_;
return expectation.result_type;
}
size_t num_invoked() const { return current_index_; }
private:
struct Expectation {
string s;
size_t len;
int id;
ResultType result_type;
};
vector<Expectation> expectation_list_;
size_t current_index_;
size_t limit_;
DISALLOW_COPY_AND_ASSIGN(TestCallback);
};
TEST_F(LoudsTrieTest, ExactSearch) {
LoudsTrieBuilder builder;
builder.Add("a");
builder.Add("abc");
builder.Add("abcd");
builder.Add("ae");
builder.Add("aecd");
builder.Add("b");
builder.Add("bcx");
builder.Build();
LoudsTrie trie;
trie.Open(reinterpret_cast<const uint8 *>(builder.image().data()));
EXPECT_EQ(builder.GetId("a"), trie.ExactSearch("a"));
EXPECT_EQ(builder.GetId("abc"), trie.ExactSearch("abc"));
EXPECT_EQ(builder.GetId("abcd"), trie.ExactSearch("abcd"));
EXPECT_EQ(builder.GetId("ae"), trie.ExactSearch("ae"));
EXPECT_EQ(builder.GetId("aecd"), trie.ExactSearch("aecd"));
EXPECT_EQ(builder.GetId("b"), trie.ExactSearch("b"));
EXPECT_EQ(builder.GetId("bcx"), trie.ExactSearch("bcx"));
EXPECT_EQ(-1, trie.ExactSearch(""));
EXPECT_EQ(-1, trie.ExactSearch("ab"));
EXPECT_EQ(-1, trie.ExactSearch("aa"));
EXPECT_EQ(-1, trie.ExactSearch("aec"));
EXPECT_EQ(-1, trie.ExactSearch("aecx"));
EXPECT_EQ(-1, trie.ExactSearch("aecdf"));
EXPECT_EQ(-1, trie.ExactSearch("abcdefghi"));
EXPECT_EQ(-1, trie.ExactSearch("bc"));
EXPECT_EQ(-1, trie.ExactSearch("bca"));
EXPECT_EQ(-1, trie.ExactSearch("bcxyz"));
trie.Close();
}
TEST_F(LoudsTrieTest, PrefixSearch) {
LoudsTrieBuilder builder;
builder.Add("aa");
builder.Add("ab");
builder.Add("abc");
builder.Add("abcd");
builder.Add("abd");
builder.Add("ebd");
builder.Add("\x01\xFF");
builder.Add("\x01\xFF\xFF");
builder.Build();
LoudsTrie trie;
trie.Open(reinterpret_cast<const uint8 *>(builder.image().data()));
{
TestCallback callback;
callback.AddExpectation(
"ab", 2, builder.GetId("ab"), LoudsTrie::Callback::SEARCH_CONTINUE);
callback.AddExpectation(
"abc", 3, builder.GetId("abc"), LoudsTrie::Callback::SEARCH_CONTINUE);
trie.PrefixSearch("abc", &callback);
EXPECT_EQ(2, callback.num_invoked());
}
{
TestCallback callback;
callback.AddExpectation(
"ab", 2, builder.GetId("ab"), LoudsTrie::Callback::SEARCH_CONTINUE);
trie.PrefixSearch("abxxxxxxx", &callback);
EXPECT_EQ(1, callback.num_invoked());
}
{
// Make sure that non-ascii characters can be found, too.
TestCallback callback;
callback.AddExpectation("\x01\xFF", 2, builder.GetId("\x01\xFF"),
LoudsTrie::Callback::SEARCH_CONTINUE);
callback.AddExpectation("\x01\xFF\xFF", 3, builder.GetId("\x01\xFF\xFF"),
LoudsTrie::Callback::SEARCH_CONTINUE);
trie.PrefixSearch("\x01\xFF\xFF", &callback);
EXPECT_EQ(2, callback.num_invoked());
}
trie.Close();
}
TEST_F(LoudsTrieTest, PrefixSearchWithLimit) {
LoudsTrieBuilder builder;
builder.Add("aa");
builder.Add("ab");
builder.Add("abc");
builder.Add("abcd");
builder.Add("abcde");
builder.Add("abcdef");
builder.Add("abd");
builder.Add("ebd");
builder.Build();
LoudsTrie trie;
trie.Open(reinterpret_cast<const uint8 *>(builder.image().data()));
{
TestCallback callback;
callback.AddExpectation("ab", 2, builder.GetId("ab"),
LoudsTrie::Callback::SEARCH_CONTINUE);
callback.AddExpectation("abc", 3, builder.GetId("abc"),
LoudsTrie::Callback::SEARCH_DONE);
trie.PrefixSearch("abcdef", &callback);
EXPECT_EQ(2, callback.num_invoked());
}
{
TestCallback callback;
callback.AddExpectation("ab", 2, builder.GetId("ab"),
LoudsTrie::Callback::SEARCH_DONE);
trie.PrefixSearch("abdxxx", &callback);
EXPECT_EQ(1, callback.num_invoked());
}
trie.Close();
}
TEST_F(LoudsTrieTest, PrefixSearchWithKeyExpansion) {
LoudsTrieBuilder builder;
builder.Add("abc");
builder.Add("adc");
builder.Add("cbc");
builder.Add("ddc");
builder.Build();
LoudsTrie trie;
trie.Open(reinterpret_cast<const uint8 *>(builder.image().data()));
KeyExpansionTable key_expansion_table;
key_expansion_table.Add('b', "d");
{
TestCallback callback;
callback.AddExpectation("abc", 3, builder.GetId("abc"),
LoudsTrie::Callback::SEARCH_CONTINUE);
callback.AddExpectation("adc", 3, builder.GetId("adc"),
LoudsTrie::Callback::SEARCH_CONTINUE);
trie.PrefixSearchWithKeyExpansion("abc", key_expansion_table, &callback);
EXPECT_EQ(2, callback.num_invoked());
}
{
TestCallback callback;
callback.AddExpectation("adc", 3, builder.GetId("adc"),
LoudsTrie::Callback::SEARCH_CONTINUE);
trie.PrefixSearchWithKeyExpansion("adc", key_expansion_table, &callback);
EXPECT_EQ(1, callback.num_invoked());
}
trie.Close();
}
TEST_F(LoudsTrieTest, PrefixSearchCulling) {
LoudsTrieBuilder builder;
builder.Add("a");
builder.Add("ab");
builder.Add("abc");
builder.Add("abcd");
builder.Add("ae");
builder.Add("aec");
builder.Add("aecd");
builder.Build();
LoudsTrie trie;
trie.Open(reinterpret_cast<const uint8 *>(builder.image().data()));
KeyExpansionTable key_expansion_table;
key_expansion_table.Add('b', "e");
{
TestCallback callback;
callback.AddExpectation("a", 1, builder.GetId("a"),
LoudsTrie::Callback::SEARCH_CONTINUE);
callback.AddExpectation("ab", 2, builder.GetId("ab"),
LoudsTrie::Callback::SEARCH_CONTINUE);
callback.AddExpectation("abc", 3, builder.GetId("abc"),
LoudsTrie::Callback::SEARCH_CULL);
// No callback for abcd, but ones for ae... should be found.
callback.AddExpectation("ae", 2, builder.GetId("ae"),
LoudsTrie::Callback::SEARCH_CONTINUE);
callback.AddExpectation("aec", 3, builder.GetId("aec"),
LoudsTrie::Callback::SEARCH_CONTINUE);
callback.AddExpectation("aecd", 4, builder.GetId("aecd"),
LoudsTrie::Callback::SEARCH_CONTINUE);
trie.PrefixSearchWithKeyExpansion("abcd", key_expansion_table, &callback);
EXPECT_EQ(6, callback.num_invoked());
}
trie.Close();
}
TEST_F(LoudsTrieTest, PredictiveSearch) {
LoudsTrieBuilder builder;
builder.Add("aa");
builder.Add("ab");
builder.Add("abc");
builder.Add("abcd");
builder.Add("abcde");
builder.Add("abcdef");
builder.Add("abcea");
builder.Add("abcef");
builder.Add("abd");
builder.Add("ebd");
builder.Add("\x01\xFF");
builder.Add("\x01\xFF\xFF");
builder.Build();
LoudsTrie trie;
trie.Open(reinterpret_cast<const uint8 *>(builder.image().data()));
{
TestCallback callback;
callback.AddExpectation("abc", 3, builder.GetId("abc"),
LoudsTrie::Callback::SEARCH_CONTINUE);
callback.AddExpectation("abcd", 4, builder.GetId("abcd"),
LoudsTrie::Callback::SEARCH_CONTINUE);
callback.AddExpectation("abcde", 5, builder.GetId("abcde"),
LoudsTrie::Callback::SEARCH_CONTINUE);
callback.AddExpectation("abcdef", 6, builder.GetId("abcdef"),
LoudsTrie::Callback::SEARCH_CONTINUE);
callback.AddExpectation("abcea", 5, builder.GetId("abcea"),
LoudsTrie::Callback::SEARCH_CONTINUE);
callback.AddExpectation("abcef", 5, builder.GetId("abcef"),
LoudsTrie::Callback::SEARCH_CONTINUE);
trie.PredictiveSearch("abc", &callback);
EXPECT_EQ(6, callback.num_invoked());
}
{
TestCallback callback;
callback.AddExpectation("aa", 2, builder.GetId("aa"),
LoudsTrie::Callback::SEARCH_CONTINUE);
callback.AddExpectation("ab", 2, builder.GetId("ab"),
LoudsTrie::Callback::SEARCH_CONTINUE);
callback.AddExpectation("abc", 3, builder.GetId("abc"),
LoudsTrie::Callback::SEARCH_CONTINUE);
callback.AddExpectation("abcd", 4, builder.GetId("abcd"),
LoudsTrie::Callback::SEARCH_CONTINUE);
callback.AddExpectation("abcde", 5, builder.GetId("abcde"),
LoudsTrie::Callback::SEARCH_CONTINUE);
callback.AddExpectation("abcdef", 6, builder.GetId("abcdef"),
LoudsTrie::Callback::SEARCH_CONTINUE);
callback.AddExpectation("abcea", 5, builder.GetId("abcea"),
LoudsTrie::Callback::SEARCH_CONTINUE);
callback.AddExpectation("abcef", 5, builder.GetId("abcef"),
LoudsTrie::Callback::SEARCH_CONTINUE);
callback.AddExpectation("abd", 3, builder.GetId("abd"),
LoudsTrie::Callback::SEARCH_CONTINUE);
trie.PredictiveSearch("a", &callback);
EXPECT_EQ(9, callback.num_invoked());
}
{
// Make sure non-ascii characters can be found.
TestCallback callback;
callback.AddExpectation("\x01\xFF", 2, builder.GetId("\x01\xFF"),
LoudsTrie::Callback::SEARCH_CONTINUE);
callback.AddExpectation("\x01\xFF\xFF", 3, builder.GetId("\x01\xFF\xFF"),
LoudsTrie::Callback::SEARCH_CONTINUE);
trie.PredictiveSearch("\x01", &callback);
EXPECT_EQ(2, callback.num_invoked());
}
trie.Close();
}
TEST_F(LoudsTrieTest, PredictiveSearchWithLimit) {
LoudsTrieBuilder builder;
builder.Add("aa");
builder.Add("ab");
builder.Add("abc");
builder.Add("abcd");
builder.Add("abcde");
builder.Add("abcdef");
builder.Add("abcea");
builder.Add("abcef");
builder.Add("abd");
builder.Add("ebd");
builder.Build();
LoudsTrie trie;
trie.Open(reinterpret_cast<const uint8 *>(builder.image().data()));
{
TestCallback callback;
callback.AddExpectation("abc", 3, builder.GetId("abc"),
LoudsTrie::Callback::SEARCH_CONTINUE);
callback.AddExpectation("abcd", 4, builder.GetId("abcd"),
LoudsTrie::Callback::SEARCH_DONE);
trie.PredictiveSearch("abc", &callback);
EXPECT_EQ(2, callback.num_invoked());
}
{
TestCallback callback;
callback.AddExpectation("aa", 2, builder.GetId("aa"),
LoudsTrie::Callback::SEARCH_CONTINUE);
callback.AddExpectation("ab", 2, builder.GetId("ab"),
LoudsTrie::Callback::SEARCH_CONTINUE);
callback.AddExpectation("abc", 3, builder.GetId("abc"),
LoudsTrie::Callback::SEARCH_DONE);
trie.PredictiveSearch("a", &callback);
EXPECT_EQ(3, callback.num_invoked());
}
trie.Close();
}
TEST_F(LoudsTrieTest, PredictiveSearchWithKeyExpansion) {
LoudsTrieBuilder builder;
builder.Add("abc");
builder.Add("adc");
builder.Add("cbc");
builder.Add("ddc");
builder.Build();
LoudsTrie trie;
trie.Open(reinterpret_cast<const uint8 *>(builder.image().data()));
KeyExpansionTable key_expansion_table;
key_expansion_table.Add('b', "d");
{
TestCallback callback;
callback.AddExpectation("abc", 3, builder.GetId("abc"),
LoudsTrie::Callback::SEARCH_CONTINUE);
callback.AddExpectation("adc", 3, builder.GetId("adc"),
LoudsTrie::Callback::SEARCH_CONTINUE);
trie.PredictiveSearchWithKeyExpansion(
"ab", key_expansion_table, &callback);
EXPECT_EQ(2, callback.num_invoked());
}
{
TestCallback callback;
callback.AddExpectation("adc", 3, builder.GetId("adc"),
LoudsTrie::Callback::SEARCH_CONTINUE);
trie.PredictiveSearchWithKeyExpansion(
"ad", key_expansion_table, &callback);
EXPECT_EQ(1, callback.num_invoked());
}
trie.Close();
}
TEST_F(LoudsTrieTest, PredictiveSearchCulling) {
LoudsTrieBuilder builder;
builder.Add("a");
builder.Add("ab");
builder.Add("abc");
builder.Add("abcd");
builder.Add("ae");
builder.Add("aec");
builder.Add("aecd");
builder.Build();
LoudsTrie trie;
trie.Open(reinterpret_cast<const uint8 *>(builder.image().data()));
{
TestCallback callback;
callback.AddExpectation("a", 1, builder.GetId("a"),
LoudsTrie::Callback::SEARCH_CONTINUE);
callback.AddExpectation("ab", 2, builder.GetId("ab"),
LoudsTrie::Callback::SEARCH_CONTINUE);
callback.AddExpectation("abc", 3, builder.GetId("abc"),
LoudsTrie::Callback::SEARCH_CULL);
// No callback for abcd.
callback.AddExpectation("ae", 2, builder.GetId("ae"),
LoudsTrie::Callback::SEARCH_CONTINUE);
callback.AddExpectation("aec", 3, builder.GetId("aec"),
LoudsTrie::Callback::SEARCH_CONTINUE);
callback.AddExpectation("aecd", 4, builder.GetId("aecd"),
LoudsTrie::Callback::SEARCH_CONTINUE);
trie.PredictiveSearch("a", &callback);
EXPECT_EQ(6, callback.num_invoked());
}
trie.Close();
}
TEST_F(LoudsTrieTest, PredictiveSearchCulling2) {
LoudsTrieBuilder builder;
builder.Add("abc");
builder.Add("abcd");
builder.Add("abcde");
builder.Add("abxy");
builder.Add("abxyz");
builder.Build();
LoudsTrie trie;
trie.Open(reinterpret_cast<const uint8 *>(builder.image().data()));
{
TestCallback callback;
callback.AddExpectation("abc", 3, builder.GetId("abc"),
LoudsTrie::Callback::SEARCH_CULL);
callback.AddExpectation("abxy", 4, builder.GetId("abxy"),
LoudsTrie::Callback::SEARCH_CONTINUE);
callback.AddExpectation("abxyz", 5, builder.GetId("abxyz"),
LoudsTrie::Callback::SEARCH_CONTINUE);
trie.PredictiveSearch("a", &callback);
EXPECT_EQ(3, callback.num_invoked());
}
trie.Close();
}
TEST_F(LoudsTrieTest, Reverse) {
LoudsTrieBuilder builder;
builder.Add("aa");
builder.Add("ab");
builder.Add("abc");
builder.Add("abcd");
builder.Add("abcde");
builder.Add("abcdef");
builder.Add("abcea");
builder.Add("abcef");
builder.Add("abd");
builder.Add("ebd");
builder.Build();
LoudsTrie trie;
trie.Open(reinterpret_cast<const uint8 *>(builder.image().data()));
char buffer[LoudsTrie::kMaxDepth + 1];
EXPECT_STREQ("aa", trie.Reverse(builder.GetId("aa"), buffer));
EXPECT_STREQ("ab", trie.Reverse(builder.GetId("ab"), buffer));
EXPECT_STREQ("abc", trie.Reverse(builder.GetId("abc"), buffer));
EXPECT_STREQ("abcd", trie.Reverse(builder.GetId("abcd"), buffer));
EXPECT_STREQ("abcde", trie.Reverse(builder.GetId("abcde"), buffer));
EXPECT_STREQ("abcdef", trie.Reverse(builder.GetId("abcdef"), buffer));
EXPECT_STREQ("abcea", trie.Reverse(builder.GetId("abcea"), buffer));
EXPECT_STREQ("abcef", trie.Reverse(builder.GetId("abcef"), buffer));
EXPECT_STREQ("abd", trie.Reverse(builder.GetId("abd"), buffer));
EXPECT_STREQ("ebd", trie.Reverse(builder.GetId("ebd"), buffer));
EXPECT_STREQ("", trie.Reverse(-1, buffer));
trie.Close();
}
} // namespace
} // namespace louds
} // namespace storage
} // namespace mozc