#include <algorithm>
#include <cstring>
#include <list>
#include <string>
#include <vector>
#include "PrefixMatch.hpp"
#include "DictGroup.hpp"
#include "MarisaDict.hpp"
#include "TestUtilsUTF8.hpp"
#include "TextDictTestBase.hpp"
namespace opencc {
class PrefixMatchTest : public TextDictTestBase {
protected:
PrefixMatchTest()
: marisaDict(MarisaDict::NewFromDict(*textDict)), fileName("dict.ocd2") {
marisaDict->opencc::SerializableDict::SerializeToFile(fileName);
}
virtual ~PrefixMatchTest() {
std::remove(fileName.c_str());
}
const MarisaDictPtr marisaDict;
const std::string fileName;
};
TEST_F(PrefixMatchTest, SingleDictFastPathMatchesTablePath) {
std::list<DictPtr> dicts = { marisaDict };
DictPtr dictGroup(new DictGroup(dicts));
PrefixMatch pmFast(dictGroup);
std::list<DictPtr> dictsMulti = { marisaDict, textDict };
DictPtr dictGroupMulti(new DictGroup(dictsMulti));
PrefixMatch pmTable(dictGroupMulti);
const std::vector<std::string> testQueries = {
"清華", "清華大", "清華大學", "清", "積羽沉舟", "nowhere"
};
for (const std::string& query : testQueries) {
PrefixMatch::Match mFast = pmFast.MatchPrefix(query.c_str(), query.length());
PrefixMatch::Match mTable = pmTable.MatchPrefix(query.c_str(), query.length());
EXPECT_EQ(mFast.matched, mTable.matched);
if (mFast.matched) {
EXPECT_EQ(mFast.keyLength, mTable.keyLength);
EXPECT_EQ(*mFast.key, *mTable.key);
EXPECT_EQ(*mFast.value, *mTable.value);
}
}
}
TEST_F(PrefixMatchTest, MarisaLazyLoadFastPathDoesNotReconstruct) {
MarisaDictPtr lazyDict = SerializableDict::NewFromFile<MarisaDict>(fileName);
EXPECT_FALSE(lazyDict->IsLexiconReconstructed());
PrefixMatch pm(lazyDict);
PrefixMatch::Match m = pm.MatchPrefix("清華", 6);
EXPECT_TRUE(m.matched);
EXPECT_EQ(6, m.keyLength); EXPECT_EQ("清華", *m.key);
EXPECT_EQ(utf8("Tsinghua"), *m.value);
EXPECT_FALSE(lazyDict->IsLexiconReconstructed());
}
TEST_F(PrefixMatchTest, FastPathSelectsLongestMatch) {
PrefixMatch pm(marisaDict);
const std::string query = utf8("清華大學校園");
PrefixMatch::Match m = pm.MatchPrefix(query.c_str(), query.length());
EXPECT_TRUE(m.matched);
EXPECT_EQ(utf8("清華大學"), *m.key);
EXPECT_EQ(utf8("TsinghuaUniversity"), *m.value);
EXPECT_EQ(m.key->length(), m.keyLength);
}
TEST_F(PrefixMatchTest, MatchPrefixViewFastPathReturnsViews) {
PrefixMatch pm(marisaDict);
const std::string query = utf8("清華大學校園");
PrefixMatchView v = pm.MatchPrefixView(query.c_str(), query.length());
EXPECT_TRUE(v.matched);
EXPECT_EQ(utf8("清華大學"), std::string(v.key));
EXPECT_EQ(utf8("TsinghuaUniversity"), std::string(v.value));
EXPECT_EQ(v.key.size(), v.keyLength);
EXPECT_EQ(query.data(), v.key.data());
}
TEST_F(PrefixMatchTest, MatchPrefixViewTablePathReturnsViews) {
std::list<DictPtr> dictsMulti = {marisaDict, textDict};
DictPtr dictGroupMulti(new DictGroup(dictsMulti));
PrefixMatch pm(dictGroupMulti);
const std::string query = utf8("清華大學校園");
PrefixMatchView v = pm.MatchPrefixView(query.c_str(), query.length());
EXPECT_TRUE(v.matched);
EXPECT_EQ(utf8("清華大學"), std::string(v.key));
EXPECT_EQ(utf8("TsinghuaUniversity"), std::string(v.value));
EXPECT_EQ(v.key.size(), v.keyLength);
}
TEST_F(PrefixMatchTest, ShortCircuitGroupPrefersEarlierShorterMatch) {
LexiconPtr firstLexicon(new Lexicon);
firstLexicon->Add(DictEntryFactory::New(utf8("意大利"), utf8("義大利")));
firstLexicon->Sort();
DictPtr firstDict(new TextDict(firstLexicon));
LexiconPtr secondLexicon(new Lexicon);
secondLexicon->Add(DictEntryFactory::New(utf8("意大利面"), utf8("義大利麵")));
secondLexicon->Sort();
DictPtr secondDict(new TextDict(secondLexicon));
DictPtr dictGroup(new DictGroup(
std::list<DictPtr>{firstDict, secondDict},
DictGroupMatchPolicy::ShortCircuit));
PrefixMatch pm(dictGroup);
const std::string query = utf8("意大利面");
PrefixMatch::Match m = pm.MatchPrefix(query.c_str(), query.length());
EXPECT_TRUE(m.matched);
EXPECT_EQ(utf8("意大利"), *m.key);
EXPECT_EQ(utf8("義大利"), *m.value);
}
TEST_F(PrefixMatchTest, UnionGroupPrefersLaterLongerMatch) {
LexiconPtr firstLexicon(new Lexicon);
firstLexicon->Add(DictEntryFactory::New(utf8("意大利"), utf8("義大利")));
firstLexicon->Sort();
DictPtr firstDict(new TextDict(firstLexicon));
LexiconPtr secondLexicon(new Lexicon);
secondLexicon->Add(DictEntryFactory::New(utf8("意大利面"), utf8("義大利麵")));
secondLexicon->Sort();
DictPtr secondDict(new TextDict(secondLexicon));
DictPtr dictGroup(
new UnionDictGroup(std::list<DictPtr>{firstDict, secondDict}));
PrefixMatch pm(dictGroup);
const std::string query = utf8("意大利面");
PrefixMatch::Match m = pm.MatchPrefix(query.c_str(), query.length());
EXPECT_TRUE(m.matched);
EXPECT_EQ(utf8("意大利面"), *m.key);
EXPECT_EQ(utf8("義大利麵"), *m.value);
}
TEST_F(PrefixMatchTest, UnionGroupPreservesNestedShortCircuitBoundary) {
LexiconPtr firstLexicon(new Lexicon);
firstLexicon->Add(DictEntryFactory::New(utf8("意"), "nested-first"));
firstLexicon->Sort();
DictPtr firstDict(new TextDict(firstLexicon));
LexiconPtr hiddenLexicon(new Lexicon);
hiddenLexicon->Add(DictEntryFactory::New(utf8("意大利面"), "hidden"));
hiddenLexicon->Sort();
DictPtr hiddenDict(new TextDict(hiddenLexicon));
LexiconPtr rootLexicon(new Lexicon);
rootLexicon->Add(DictEntryFactory::New(utf8("意大利"), "root-second"));
rootLexicon->Sort();
DictPtr rootDict(new TextDict(rootLexicon));
DictPtr nestedGroup(
new DictGroup(std::list<DictPtr>{firstDict, hiddenDict}));
DictPtr rootGroup(
new UnionDictGroup(std::list<DictPtr>{nestedGroup, rootDict}));
PrefixMatch pm(rootGroup);
const std::string query = utf8("意大利面");
PrefixMatch::Match m = pm.MatchPrefix(query.c_str(), query.length());
EXPECT_TRUE(m.matched);
EXPECT_EQ(utf8("意大利"), *m.key);
EXPECT_EQ("root-second", *m.value);
}
}