blob: dd2efd00af7a6edbf6f3c8d2f7c82d1c535368a2 [file] [log] [blame]
/* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define C_LUCY_TESTQUERYPARSERSYNTAX
#define C_LUCY_TESTQUERYPARSER
#include "Lucy/Util/ToolSet.h"
#include <stdarg.h>
#include <string.h>
#include "Lucy/Test.h"
#include "Lucy/Test/Search/TestQueryParserSyntax.h"
#include "Lucy/Test/Search/TestQueryParser.h"
#include "Lucy/Test/TestUtils.h"
#include "Lucy/Search/Hits.h"
#include "Lucy/Search/IndexSearcher.h"
#include "Lucy/Search/QueryParser.h"
#include "Lucy/Search/TermQuery.h"
#include "Lucy/Search/PhraseQuery.h"
#include "Lucy/Search/LeafQuery.h"
#include "Lucy/Search/ANDQuery.h"
#include "Lucy/Search/NOTQuery.h"
#include "Lucy/Search/ORQuery.h"
#include "Lucy/Store/Folder.h"
#define make_term_query (Query*)lucy_TestUtils_make_term_query
#define make_phrase_query (Query*)lucy_TestUtils_make_phrase_query
#define make_leaf_query (Query*)lucy_TestUtils_make_leaf_query
#define make_not_query (Query*)lucy_TestUtils_make_not_query
#define make_poly_query (Query*)lucy_TestUtils_make_poly_query
static TestQueryParser*
leaf_test_simple_term() {
Query *tree = make_leaf_query(NULL, "a");
Query *plain_q = make_term_query("plain", "a");
Query *fancy_q = make_term_query("fancy", "a");
Query *expanded = make_poly_query(BOOLOP_OR, fancy_q, plain_q, NULL);
return TestQP_new("a", tree, expanded, 4);
}
static TestQueryParser*
leaf_test_simple_phrase() {
Query *tree = make_leaf_query(NULL, "\"a b\"");
Query *plain_q = make_phrase_query("plain", "a", "b", NULL);
Query *fancy_q = make_phrase_query("fancy", "a", "b", NULL);
Query *expanded = make_poly_query(BOOLOP_OR, fancy_q, plain_q, NULL);
return TestQP_new("\"a b\"", tree, expanded, 3);
}
static TestQueryParser*
leaf_test_unclosed_quote() {
Query *tree = make_leaf_query(NULL, "\"a b");
Query *plain_q = make_phrase_query("plain", "a", "b", NULL);
Query *fancy_q = make_phrase_query("fancy", "a", "b", NULL);
Query *expanded = make_poly_query(BOOLOP_OR, fancy_q, plain_q, NULL);
return TestQP_new("\"a b", tree, expanded, 3);
}
static TestQueryParser*
leaf_test_escaped_quotes_inside() {
Query *tree = make_leaf_query(NULL, "\"\\\"a b\\\"\"");
Query *plain_q = make_phrase_query("plain", "\"a", "b\"", NULL);
Query *fancy_q = make_phrase_query("fancy", "a", "b", NULL);
Query *expanded = make_poly_query(BOOLOP_OR, fancy_q, plain_q, NULL);
return TestQP_new("\"\\\"a b\\\"\"", tree, expanded, 3);
}
static TestQueryParser*
leaf_test_escaped_quotes_outside() {
Query *tree = make_leaf_query(NULL, "\\\"a");
Query *plain_q = make_term_query("plain", "\"a");
Query *fancy_q = make_term_query("fancy", "a");
Query *expanded = make_poly_query(BOOLOP_OR, fancy_q, plain_q, NULL);
return TestQP_new("\\\"a", tree, expanded, 4);
}
static TestQueryParser*
leaf_test_single_term_phrase() {
Query *tree = make_leaf_query(NULL, "\"a\"");
Query *plain_q = make_phrase_query("plain", "a", NULL);
Query *fancy_q = make_phrase_query("fancy", "a", NULL);
Query *expanded = make_poly_query(BOOLOP_OR, fancy_q, plain_q, NULL);
return TestQP_new("\"a\"", tree, expanded, 4);
}
static TestQueryParser*
leaf_test_longer_phrase() {
Query *tree = make_leaf_query(NULL, "\"a b c\"");
Query *plain_q = make_phrase_query("plain", "a", "b", "c", NULL);
Query *fancy_q = make_phrase_query("fancy", "a", "b", "c", NULL);
Query *expanded = make_poly_query(BOOLOP_OR, fancy_q, plain_q, NULL);
return TestQP_new("\"a b c\"", tree, expanded, 2);
}
static TestQueryParser*
leaf_test_empty_phrase() {
Query *tree = make_leaf_query(NULL, "\"\"");
Query *plain_q = make_phrase_query("plain", NULL);
Query *fancy_q = make_phrase_query("fancy", NULL);
Query *expanded = make_poly_query(BOOLOP_OR, fancy_q, plain_q, NULL);
return TestQP_new("\"\"", tree, expanded, 0);
}
static TestQueryParser*
leaf_test_phrase_with_stopwords() {
Query *tree = make_leaf_query(NULL, "\"x a\"");
Query *plain_q = make_phrase_query("plain", "x", "a", NULL);
Query *fancy_q = make_phrase_query("fancy", "a", NULL);
Query *expanded = make_poly_query(BOOLOP_OR, fancy_q, plain_q, NULL);
return TestQP_new("\"x a\"", tree, expanded, 4);
}
static TestQueryParser*
leaf_test_different_tokenization() {
Query *tree = make_leaf_query(NULL, "a.b");
Query *plain_q = make_term_query("plain", "a.b");
Query *fancy_q = make_phrase_query("fancy", "a", "b", NULL);
Query *expanded = make_poly_query(BOOLOP_OR, fancy_q, plain_q, NULL);
return TestQP_new("a.b", tree, expanded, 3);
}
static TestQueryParser*
leaf_test_http() {
char address[] = "http://www.foo.com/bar.html";
Query *tree = make_leaf_query(NULL, address);
Query *plain_q = make_term_query("plain", address);
Query *fancy_q = make_phrase_query("fancy", "http", "www", "foo",
"com", "bar", "html", NULL);
Query *expanded = make_poly_query(BOOLOP_OR, fancy_q, plain_q, NULL);
return TestQP_new(address, tree, expanded, 0);
}
static TestQueryParser*
leaf_test_field() {
Query *tree = make_leaf_query("plain", "b");
Query *expanded = make_term_query("plain", "b");
return TestQP_new("plain:b", tree, expanded, 3);
}
static TestQueryParser*
leaf_test_unrecognized_field() {
Query *tree = make_leaf_query("bogusfield", "b");
Query *expanded = make_term_query("bogusfield", "b");
return TestQP_new("bogusfield:b", tree, expanded, 0);
}
static TestQueryParser*
leaf_test_unescape_colons() {
Query *tree = make_leaf_query("plain", "a\\:b");
Query *expanded = make_term_query("plain", "a:b");
return TestQP_new("plain:a\\:b", tree, expanded, 0);
}
static TestQueryParser*
syntax_test_minus_plus() {
Query *leaf = make_leaf_query(NULL, "a");
Query *tree = make_not_query(leaf);
return TestQP_new("-+a", tree, NULL, 0);
}
static TestQueryParser*
syntax_test_plus_minus() {
// Not a perfect result, but then it's not a good query string.
Query *leaf = make_leaf_query(NULL, "a");
Query *tree = make_not_query(leaf);
return TestQP_new("+-a", tree, NULL, 0);
}
static TestQueryParser*
syntax_test_minus_minus() {
// Not a perfect result, but then it's not a good query string.
Query *tree = make_leaf_query(NULL, "a");
return TestQP_new("--a", tree, NULL, 4);
}
static TestQueryParser*
syntax_test_not_minus() {
Query *tree = make_leaf_query(NULL, "a");
return TestQP_new("NOT -a", tree, NULL, 4);
}
static TestQueryParser*
syntax_test_not_plus() {
// Not a perfect result, but then it's not a good query string.
Query *leaf = make_leaf_query(NULL, "a");
Query *tree = make_not_query(leaf);
return TestQP_new("NOT +a", tree, NULL, 0);
}
static TestQueryParser*
syntax_test_padded_plus() {
Query *plus = make_leaf_query(NULL, "+");
Query *a = make_leaf_query(NULL, "a");
Query *tree = make_poly_query(BOOLOP_OR, plus, a, NULL);
return TestQP_new("+ a", tree, NULL, 4);
}
static TestQueryParser*
syntax_test_padded_minus() {
Query *minus = make_leaf_query(NULL, "-");
Query *a = make_leaf_query(NULL, "a");
Query *tree = make_poly_query(BOOLOP_OR, minus, a, NULL);
return TestQP_new("- a", tree, NULL, 4);
}
static TestQueryParser*
syntax_test_unclosed_parens() {
// Not a perfect result, but then it's not a good query string.
Query *inner = make_poly_query(BOOLOP_OR, NULL);
Query *tree = make_poly_query(BOOLOP_OR, inner, NULL);
return TestQP_new("((", tree, NULL, 0);
}
static TestQueryParser*
syntax_test_escaped_quotes_outside() {
Query *tree = make_leaf_query(NULL, "\\\"a\\\"");
return TestQP_new("\\\"a\\\"", tree, NULL, 4);
}
static TestQueryParser*
syntax_test_escaped_quotes_inside() {
Query *tree = make_leaf_query(NULL, "\"\\\"a\\\"\"");
return TestQP_new("\"\\\"a\\\"\"", tree, NULL, 4);
}
static TestQueryParser*
syntax_test_identifier_field_name() {
// Field names must be identifiers, i.e. they cannot start with a number.
Query *tree = make_leaf_query(NULL, "10:30");
return TestQP_new("10:30", tree, NULL, 0);
}
static TestQueryParser*
syntax_test_double_colon() {
Query *tree = make_leaf_query(NULL, "PHP::Interpreter");
return TestQP_new("PHP::Interpreter", tree, NULL, 0);
}
/***************************************************************************/
typedef TestQueryParser*
(*lucy_TestQPSyntax_test_t)();
static lucy_TestQPSyntax_test_t leaf_test_funcs[] = {
leaf_test_simple_term,
leaf_test_simple_phrase,
leaf_test_unclosed_quote,
leaf_test_escaped_quotes_inside,
leaf_test_escaped_quotes_outside,
leaf_test_single_term_phrase,
leaf_test_longer_phrase,
leaf_test_empty_phrase,
leaf_test_different_tokenization,
leaf_test_phrase_with_stopwords,
leaf_test_http,
leaf_test_field,
leaf_test_unrecognized_field,
leaf_test_unescape_colons,
NULL
};
static lucy_TestQPSyntax_test_t syntax_test_funcs[] = {
syntax_test_minus_plus,
syntax_test_plus_minus,
syntax_test_minus_minus,
syntax_test_not_minus,
syntax_test_not_plus,
syntax_test_padded_plus,
syntax_test_padded_minus,
syntax_test_unclosed_parens,
syntax_test_escaped_quotes_outside,
syntax_test_escaped_quotes_inside,
syntax_test_identifier_field_name,
syntax_test_double_colon,
NULL
};
void
TestQPSyntax_run_tests(Folder *index) {
uint32_t i;
TestBatch *batch = TestBatch_new(66);
IndexSearcher *searcher = IxSearcher_new((Obj*)index);
QueryParser *qparser = QParser_new(IxSearcher_Get_Schema(searcher),
NULL, NULL, NULL);
QParser_Set_Heed_Colons(qparser, true);
TestBatch_Plan(batch);
for (i = 0; leaf_test_funcs[i] != NULL; i++) {
lucy_TestQPSyntax_test_t test_func = leaf_test_funcs[i];
TestQueryParser *test_case = test_func();
Query *tree = QParser_Tree(qparser, test_case->query_string);
Query *expanded = QParser_Expand_Leaf(qparser, test_case->tree);
Query *parsed = QParser_Parse(qparser, test_case->query_string);
Hits *hits = IxSearcher_Hits(searcher, (Obj*)parsed, 0, 10, NULL);
TEST_TRUE(batch, Query_Equals(tree, (Obj*)test_case->tree),
"tree() %s", (char*)CB_Get_Ptr8(test_case->query_string));
TEST_TRUE(batch, Query_Equals(expanded, (Obj*)test_case->expanded),
"expand_leaf() %s", (char*)CB_Get_Ptr8(test_case->query_string));
TEST_INT_EQ(batch, Hits_Total_Hits(hits), test_case->num_hits,
"hits: %s", (char*)CB_Get_Ptr8(test_case->query_string));
DECREF(hits);
DECREF(parsed);
DECREF(expanded);
DECREF(tree);
DECREF(test_case);
}
for (i = 0; syntax_test_funcs[i] != NULL; i++) {
lucy_TestQPSyntax_test_t test_func = syntax_test_funcs[i];
TestQueryParser *test_case = test_func();
Query *tree = QParser_Tree(qparser, test_case->query_string);
Query *parsed = QParser_Parse(qparser, test_case->query_string);
Hits *hits = IxSearcher_Hits(searcher, (Obj*)parsed, 0, 10, NULL);
TEST_TRUE(batch, Query_Equals(tree, (Obj*)test_case->tree),
"tree() %s", (char*)CB_Get_Ptr8(test_case->query_string));
TEST_INT_EQ(batch, Hits_Total_Hits(hits), test_case->num_hits,
"hits: %s", (char*)CB_Get_Ptr8(test_case->query_string));
DECREF(hits);
DECREF(parsed);
DECREF(tree);
DECREF(test_case);
}
DECREF(batch);
DECREF(searcher);
DECREF(qparser);
}