package tv.dyndns.kishibe.qmaclone.server.database;
import static org.hamcrest.Matchers.greaterThan;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import tv.dyndns.kishibe.qmaclone.client.game.ProblemGenre;
import tv.dyndns.kishibe.qmaclone.client.game.ProblemType;
import tv.dyndns.kishibe.qmaclone.client.game.RandomFlag;
import tv.dyndns.kishibe.qmaclone.client.packet.PacketProblem;
import tv.dyndns.kishibe.qmaclone.server.testing.QMACloneTestEnv;
import tv.dyndns.kishibe.qmaclone.server.util.IntArray;
import tv.dyndns.kishibe.qmaclone.server.util.Normalizer;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.guiceberry.junit4.GuiceBerryRule;
import com.google.inject.Inject;
@RunWith(JUnit4.class)
public class FullTextSearchTest {
@Rule
public final GuiceBerryRule rule = new GuiceBerryRule(QMACloneTestEnv.class);
@Inject
private Database database;
@Inject
private FullTextSearch fullTextSearch;
@Test
public final void testEscapeQuery() {
assertEquals("\\\\", FullTextSearch.escapeQuery("\\"));
assertEquals("\\\\post", FullTextSearch.escapeQuery("\\post"));
assertEquals("pre\\\\", FullTextSearch.escapeQuery("pre\\"));
assertEquals("pre\\\\post", FullTextSearch.escapeQuery("pre\\post"));
assertEquals("\\+", FullTextSearch.escapeQuery("+"));
assertEquals("\\+post", FullTextSearch.escapeQuery("+post"));
assertEquals("pre\\+", FullTextSearch.escapeQuery("pre+"));
assertEquals("pre\\+post", FullTextSearch.escapeQuery("pre+post"));
assertEquals("\\-", FullTextSearch.escapeQuery("-"));
assertEquals("\\-post", FullTextSearch.escapeQuery("-post"));
assertEquals("pre\\-", FullTextSearch.escapeQuery("pre-"));
assertEquals("pre\\-post", FullTextSearch.escapeQuery("pre-post"));
assertEquals("\\&&", FullTextSearch.escapeQuery("&&"));
assertEquals("\\&&post", FullTextSearch.escapeQuery("&&post"));
assertEquals("pre\\&&", FullTextSearch.escapeQuery("pre&&"));
assertEquals("pre\\&&post", FullTextSearch.escapeQuery("pre&&post"));
assertEquals("\\||", FullTextSearch.escapeQuery("||"));
assertEquals("\\||post", FullTextSearch.escapeQuery("||post"));
assertEquals("pre\\||", FullTextSearch.escapeQuery("pre||"));
assertEquals("pre\\||post", FullTextSearch.escapeQuery("pre||post"));
assertEquals("\\!", FullTextSearch.escapeQuery("!"));
assertEquals("\\!post", FullTextSearch.escapeQuery("!post"));
assertEquals("pre\\!", FullTextSearch.escapeQuery("pre!"));
assertEquals("pre\\!post", FullTextSearch.escapeQuery("pre!post"));
assertEquals("\\(", FullTextSearch.escapeQuery("("));
assertEquals("\\(post", FullTextSearch.escapeQuery("(post"));
assertEquals("pre\\(", FullTextSearch.escapeQuery("pre("));
assertEquals("pre\\(post", FullTextSearch.escapeQuery("pre(post"));
assertEquals("\\)", FullTextSearch.escapeQuery(")"));
assertEquals("\\)post", FullTextSearch.escapeQuery(")post"));
assertEquals("pre\\)", FullTextSearch.escapeQuery("pre)"));
assertEquals("pre\\)post", FullTextSearch.escapeQuery("pre)post"));
assertEquals("\\{", FullTextSearch.escapeQuery("{"));
assertEquals("\\{post", FullTextSearch.escapeQuery("{post"));
assertEquals("pre\\{", FullTextSearch.escapeQuery("pre{"));
assertEquals("pre\\{post", FullTextSearch.escapeQuery("pre{post"));
assertEquals("\\}", FullTextSearch.escapeQuery("}"));
assertEquals("\\}post", FullTextSearch.escapeQuery("}post"));
assertEquals("pre\\}", FullTextSearch.escapeQuery("pre}"));
assertEquals("pre\\}post", FullTextSearch.escapeQuery("pre}post"));
assertEquals("\\[", FullTextSearch.escapeQuery("["));
assertEquals("\\[post", FullTextSearch.escapeQuery("[post"));
assertEquals("pre\\[", FullTextSearch.escapeQuery("pre["));
assertEquals("pre\\[post", FullTextSearch.escapeQuery("pre[post"));
assertEquals("\\]", FullTextSearch.escapeQuery("]"));
assertEquals("\\]post", FullTextSearch.escapeQuery("]post"));
assertEquals("pre\\]", FullTextSearch.escapeQuery("pre]"));
assertEquals("pre\\]post", FullTextSearch.escapeQuery("pre]post"));
assertEquals("\\^", FullTextSearch.escapeQuery("^"));
assertEquals("\\^post", FullTextSearch.escapeQuery("^post"));
assertEquals("pre\\^", FullTextSearch.escapeQuery("pre^"));
assertEquals("pre\\^post", FullTextSearch.escapeQuery("pre^post"));
assertEquals("\\\"", FullTextSearch.escapeQuery("\""));
assertEquals("\\\"post", FullTextSearch.escapeQuery("\"post"));
assertEquals("pre\\\"", FullTextSearch.escapeQuery("pre\""));
assertEquals("pre\\\"post", FullTextSearch.escapeQuery("pre\"post"));
assertEquals("\\~", FullTextSearch.escapeQuery("~"));
assertEquals("\\~post", FullTextSearch.escapeQuery("~post"));
assertEquals("pre\\~", FullTextSearch.escapeQuery("pre~"));
assertEquals("pre\\~post", FullTextSearch.escapeQuery("pre~post"));
assertEquals("\\*", FullTextSearch.escapeQuery("*"));
assertEquals("\\*post", FullTextSearch.escapeQuery("*post"));
assertEquals("pre\\*", FullTextSearch.escapeQuery("pre*"));
assertEquals("pre\\*post", FullTextSearch.escapeQuery("pre*post"));
assertEquals("\\?", FullTextSearch.escapeQuery("?"));
assertEquals("\\?post", FullTextSearch.escapeQuery("?post"));
assertEquals("pre\\?", FullTextSearch.escapeQuery("pre?"));
assertEquals("pre\\?post", FullTextSearch.escapeQuery("pre?post"));
assertEquals("\\:", FullTextSearch.escapeQuery(":"));
assertEquals("\\:post", FullTextSearch.escapeQuery(":post"));
assertEquals("pre\\:", FullTextSearch.escapeQuery("pre:"));
assertEquals("pre\\:post", FullTextSearch.escapeQuery("pre:post"));
}
private static final boolean contains(String sentence, String query) {
sentence = Normalizer.normalize(sentence);
query = Normalizer.normalize(query);
return sentence.contains(query);
}
private void assertSearchProblemsForThemeModeReturnsProblemContainsQueryString(
String... queryStrings) throws Exception {
IntArray problemIds = fullTextSearch.searchProblemsForThemeMode(ImmutableList
.copyOf(queryStrings));
assertNotNull(problemIds);
assertFalse(problemIds.isEmpty());
for (PacketProblem problem : database.getProblem(problemIds.asList())) {
assertNotNull(problem);
boolean contained = false;
for (String queryString : queryStrings) {
if (contained == contains(problem.getSearchDocument(), queryString)) {
break;
}
}
assertFalse(
"searchQuery=\"" + problem.getSearchDocument() + "\" queryString="
+ Arrays.deepToString(queryStrings), contained);
}
}
private void assertSearchProblemsForThemeModeReturnsNoProblems(String... queryStrings)
throws Exception {
IntArray problemIds = fullTextSearch.searchProblemsForThemeMode(ImmutableList
.copyOf(queryStrings));
assertNotNull(problemIds);
assertTrue(problemIds.isEmpty());
}
@Test
public void searchProblemsForThemeModeShouldWorkWithOneLetter() throws Exception {
assertSearchProblemsForThemeModeReturnsProblemContainsQueryString("A");
}
@Test
public void searchProblemsForThemeModeShouldWorkWithTwoLetters() throws Exception {
assertSearchProblemsForThemeModeReturnsProblemContainsQueryString("KA");
}
@Test
public void searchProblemsForThemeModeShouldWorkWithAlphabetLetters() throws Exception {
assertSearchProblemsForThemeModeReturnsProblemContainsQueryString("KAITO");
}
@Test
public void searchProblemsForThemeModeShouldWorkWithJapaneseWord() throws Exception {
assertSearchProblemsForThemeModeReturnsProblemContainsQueryString("ファッション");
}
@Test
public void searchProblemsForThemeModeShouldWorkWithBugTrack359_1() throws Exception {
// BugTrack-QMAClone/359 - QMAClone wiki
// http://kishibe.dyndns.tv/qmaclone/wiki/wiki.cgi?page=BugTrack%2DQMAClone%2F359
assertSearchProblemsForThemeModeReturnsProblemContainsQueryString("Intel");
}
@Test
public void searchProblemsForThemeModeShouldWorkWithBugTrack600() throws Exception {
// BugTrack-QMAClone/600 - QMAClone wiki
// http://kishibe.dyndns.tv/qmaclone/wiki/wiki.cgi?page=BugTrack%2DQMAClone%2F600
assertSearchProblemsForThemeModeReturnsProblemContainsQueryString("『ナイン』");
}
@Test
public void searchProblemsForThemeModeShouldWorkWithBugTrack359_2() throws Exception {
// BugTrack-QMAClone/359 - QMAClone wiki
// http://kishibe.dyndns.tv/qmaclone/wiki/wiki.cgi?page=BugTrack%2DQMAClone%2F359
assertSearchProblemsForThemeModeReturnsNoProblems("Corel");
}
@Test
public final void searchProblemsForThemeModeShouldWorkWithMark() throws Exception {
assertSearchProblemsForThemeModeReturnsProblemContainsQueryString("「AIR」");
assertSearchProblemsForThemeModeReturnsProblemContainsQueryString("『AIR』");
assertSearchProblemsForThemeModeReturnsProblemContainsQueryString("けいおん!");
assertSearchProblemsForThemeModeReturnsNoProblems("鏡音リン(");
assertSearchProblemsForThemeModeReturnsNoProblems("鏡音レン)");
assertSearchProblemsForThemeModeReturnsNoProblems("*MEIKO");
assertSearchProblemsForThemeModeReturnsNoProblems("がくぽ[");
assertSearchProblemsForThemeModeReturnsNoProblems("機動戦士]");
assertSearchProblemsForThemeModeReturnsNoProblems("宇宙戦艦{");
assertSearchProblemsForThemeModeReturnsNoProblems("アンパンマン}");
}
@Test
public void searchProblemsForThemeModeShouldWorkWithMultipleString() throws Exception {
assertSearchProblemsForThemeModeReturnsProblemContainsQueryString("カプコン", "逆転裁判", "ロックマン",
"鬼武者", "アレスの翼", "クローバースタジ", "モンスターハンタ", "魔界村", "『大神』", "ブレスオブファイ", "ファイナルファイ", "ビューティフルジ",
"バイオハザード", "デビルメイクライ", "天地を喰らう", "ソンソン", "戦国BASARA", "ジャスティス学園", "ストリートファイ", "ポケットファイタ",
"プロ野球?殺人事", "プロギア", "ころしあむ", "ロストワールド", "岡本吉起", "パワードギア", "『パワーストーン", "バトルサーキット",
"クイズなないろ", "QUIZなないろ", "QUIZなないろ", "虹色町の奇跡", "虎への道", "デッドライジング", "エグゼドエグゼス", "クイズ殿様の野望",
"19XX", "ガチャフォース", "ギガウイング", "GODHAND", "三上真司", "ストライダー飛竜", "サイバーボッツ", "CAPCOM", "1943改",
"ルースターズ", "ドンプル", "ひげ丸", "クイズ三国志", "サイドアーム", "戦場の狼", "戦いの挽歌", "超鋼戦紀", "キカイオー", "バルガス",
"必殺無頼拳", "プロギアの嵐", "ンパイアハンター", "パイアセイヴァー", "魔界島", "逆転検事");
assertSearchProblemsForThemeModeReturnsProblemContainsQueryString("ジャンヌ", "セイントテール");
assertSearchProblemsForThemeModeReturnsProblemContainsQueryString("マリみて", "マリア様がみてる");
assertSearchProblemsForThemeModeReturnsProblemContainsQueryString("マリみて", "マリア様がみてる");
}
@Test
public void searchProblemsForThemeModeShouldWorkWithAndQuery() throws Exception {
IntArray problemIds;
problemIds = fullTextSearch.searchProblemsForThemeMode(ImmutableList.of("機動 ナデシコ"));
assertNotNull(problemIds);
assertFalse(problemIds.isEmpty());
for (PacketProblem problem : database.getProblem(problemIds.asList())) {
final String searchQuery = problem.getSearchDocument();
assertTrue(contains(searchQuery, "機動"));
assertTrue(contains(searchQuery, "ナデシコ"));
}
problemIds = fullTextSearch.searchProblemsForThemeMode(ImmutableList.of("KAITO MEIKO"));
assertNotNull(problemIds);
assertFalse(problemIds.isEmpty());
for (PacketProblem problem : database.getProblem(problemIds.asList())) {
final String searchQuery = problem.getSearchDocument();
assertTrue(contains(searchQuery, "KAITO"));
assertTrue(contains(searchQuery, "MEIKO"));
}
}
@Test
public final void testGetMinimumProblemsForThemeModeNotOperator() throws Exception {
List<String> queries = new ArrayList<String>();
IntArray problemIds;
queries.clear();
queries.add("ローマ -神聖");
problemIds = fullTextSearch.searchProblemsForThemeMode(queries);
assertNotNull(problemIds);
assertFalse(problemIds.isEmpty());
for (PacketProblem problem : database.getProblem(problemIds.asList())) {
String sentence = problem.sentence;
assertTrue(contains(sentence, "ローマ"));
assertFalse("「" + sentence + "」に「神聖」が含まれています", contains(sentence, "神聖"));
}
}
@Test
public void searchProblemsForThemeModeShouldWorkWithGenre() throws Exception {
IntArray problemIds = fullTextSearch.searchProblemsForThemeMode(ImmutableList
.of("四国 ジャンル:アニメ&ゲーム"));
assertFalse(problemIds.isEmpty());
for (PacketProblem problem : database.getProblem(problemIds.asList())) {
String sentence = problem.sentence;
assertTrue(contains(sentence, "四国"));
assertSame(ProblemGenre.Anige, problem.genre);
}
}
@Test
public void searchProblemsForThemeModeShouldWorkWithGenreAndNotOperator() throws Exception {
IntArray problemIds = fullTextSearch.searchProblemsForThemeMode(ImmutableList
.of("四国 -ジャンル:アニメ&ゲーム"));
assertFalse(problemIds.isEmpty());
for (PacketProblem problem : database.getProblem(problemIds.asList())) {
String sentence = problem.sentence;
assertTrue(contains(sentence, "四国"));
assertNotSame(ProblemGenre.Anige, problem.genre);
}
}
@Test
public void searchProblemsForThemeModeShouldWorkWithType() throws Exception {
IntArray problemIds = fullTextSearch.searchProblemsForThemeMode(ImmutableList.of("四国 問題形式:○×"));
assertFalse(problemIds.isEmpty());
for (PacketProblem problem : database.getProblem(problemIds.asList())) {
String sentence = problem.sentence;
assertTrue(contains(sentence, "四国"));
assertSame(ProblemType.Marubatsu, problem.type);
}
}
@Test
public void searchProblemsForThemeModeShouldWorkWithTypeAndNotOperator() throws Exception {
IntArray problemIds = fullTextSearch
.searchProblemsForThemeMode(ImmutableList.of("四国 -問題形式:○×"));
assertFalse(problemIds.isEmpty());
for (PacketProblem problem : database.getProblem(problemIds.asList())) {
String sentence = problem.sentence;
assertTrue(contains(sentence, "四国"));
assertNotSame(ProblemType.Marubatsu, problem.type);
}
}
@Test
public void searchProblemsForThemeModeShouldWorkWithCreator() throws Exception {
IntArray problemIds = fullTextSearch.searchProblemsForThemeMode(ImmutableList
.of("ヨーロッパ 問題作成者:FUWAWA"));
assertFalse(problemIds.isEmpty());
for (PacketProblem problem : database.getProblem(problemIds.asList())) {
String sentence = problem.sentence;
assertTrue(contains(sentence, "ヨーロッパ"));
assertTrue(contains(problem.creator, "FUWAWA"));
}
}
@Test
public void searchProblemsForThemeModeShouldWorkWithCreatorAndNotOperator() throws Exception {
IntArray problemIds = fullTextSearch.searchProblemsForThemeMode(ImmutableList
.of("ヨーロッパ -問題作成者:FUWAWA"));
assertFalse(problemIds.isEmpty());
for (PacketProblem problem : database.getProblem(problemIds.asList())) {
String sentence = problem.sentence;
assertTrue(contains(sentence, "ヨーロッパ"));
assertFalse(contains(problem.creator, "FUWAWA"));
}
}
@Test
public void searchProblemsForThemeModeShouldWorkWithRandomFlag() throws Exception {
IntArray problemIds = fullTextSearch.searchProblemsForThemeMode(ImmutableList
.of("ヨーロッパ ランダム:1"));
assertFalse(problemIds.isEmpty());
for (PacketProblem problem : database.getProblem(problemIds.asList())) {
String sentence = problem.sentence;
assertTrue(contains(sentence, "ヨーロッパ"));
assertSame(RandomFlag.Random1, problem.randomFlag);
}
}
@Test
public void searchProblemsForThemeModeShouldWorkWithRandomFlagAndNotOperator() throws Exception {
IntArray problemIds = fullTextSearch.searchProblemsForThemeMode(ImmutableList
.of("ヨーロッパ -ランダム:1"));
assertFalse(problemIds.isEmpty());
for (PacketProblem problem : database.getProblem(problemIds.asList())) {
String sentence = problem.sentence;
assertTrue(contains(sentence, "ヨーロッパ"));
assertNotSame(RandomFlag.Random1, problem.randomFlag);
}
}
@Test
public void searchProblemsForThemeModeShouldWorkWithGenreSubGenreWord() throws Exception {
IntArray problemIds = fullTextSearch.searchProblemsForThemeMode(ImmutableList.of(
"ジャンル:アニメ&ゲーム ランダム:1 なのは", "ジャンル:アニメ&ゲーム ランダム:1 まどか"));
assertFalse(problemIds.isEmpty());
int numberOfNanoha = 0;
int numberOfMadoka = 0;
for (PacketProblem problem : database.getProblem(problemIds.asList())) {
String sentence = problem.sentence;
assertTrue("sentence=" + sentence, sentence.contains("なのは") || sentence.contains("まどか"));
if (sentence.contains("なのは")) {
++numberOfNanoha;
}
if (sentence.contains("まどか")) {
++numberOfMadoka;
}
}
assertThat(numberOfNanoha, greaterThan(0));
assertThat(numberOfMadoka, greaterThan(0));
}
@Test
public void searchProblemsForThemeModeShouldWorkWithWordAndProblemType() throws Exception {
IntArray problemIds = fullTextSearch.searchProblemsForThemeMode(ImmutableList
.of("どっち? 問題形式:○×"));
assertFalse(problemIds.isEmpty());
for (PacketProblem problem : database.getProblem(problemIds.asList())) {
String sentence = problem.sentence;
assertTrue(contains(sentence, "どっち?"));
assertEquals(ProblemType.Marubatsu, problem.type);
}
}
@Test
public void searchProblemsForThemeModeShouldReceiveManyQueries() throws Exception {
List<String> queryStrings = Lists.newArrayList();
for (int i = 0; i < 10000; ++i) {
queryStrings.add(String.valueOf(i));
}
fullTextSearch.searchProblemsForThemeMode(queryStrings);
}
@Test
public void searchProblemsForThemeModeShouldSupportOrQuery() throws Exception {
List<Integer> problemIds = fullTextSearch.searchProblem("巨人 OR 阪神", null, false,
ImmutableSet.<ProblemGenre> of(), ImmutableSet.<ProblemType> of(),
ImmutableSet.<RandomFlag> of());
assertFalse(problemIds.isEmpty());
boolean hasGiants = false;
boolean hasTigers = false;
for (PacketProblem problem : database.getProblem(problemIds)) {
String document = problem.getSearchDocument();
boolean giants = document.contains("巨人");
boolean tigers = document.contains("阪神");
assertTrue("巨人または阪神が見つかりませんでした: " + document, giants | tigers);
hasGiants |= giants;
hasTigers |= tigers;
}
assertTrue(hasGiants);
assertTrue(hasTigers);
}
}