http://git-wip-us.apache.org/repos/asf/lucy/blob/572d3564/test/Lucy/Test/Search/TestQueryParser.c ---------------------------------------------------------------------- diff --git a/test/Lucy/Test/Search/TestQueryParser.c b/test/Lucy/Test/Search/TestQueryParser.c new file mode 100644 index 0000000..57dcd17 --- /dev/null +++ b/test/Lucy/Test/Search/TestQueryParser.c @@ -0,0 +1,81 @@ +/* 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_TESTLUCY_TESTQUERYPARSER +#define TESTLUCY_USE_SHORT_NAMES +#include "Lucy/Util/ToolSet.h" +#include <string.h> + +#include "Clownfish/TestHarness/TestUtils.h" +#include "Lucy/Test/Search/TestQueryParser.h" +#include "Lucy/Test/TestUtils.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" + +TestQueryParser* +TestQP_new(const char *query_string, Query *tree, Query *expanded, + uint32_t num_hits) { + TestQueryParser *self + = (TestQueryParser*)Class_Make_Obj(TESTQUERYPARSER); + return TestQP_init(self, query_string, tree, expanded, num_hits); +} + +TestQueryParser* +TestQP_init(TestQueryParser *self, const char *query_string, Query *tree, + Query *expanded, uint32_t num_hits) { + TestQueryParserIVARS *const ivars = TestQP_IVARS(self); + ivars->query_string = query_string ? TestUtils_get_str(query_string) : NULL; + ivars->tree = tree ? tree : NULL; + ivars->expanded = expanded ? expanded : NULL; + ivars->num_hits = num_hits; + return self; +} + +void +TestQP_Destroy_IMP(TestQueryParser *self) { + TestQueryParserIVARS *const ivars = TestQP_IVARS(self); + DECREF(ivars->query_string); + DECREF(ivars->tree); + DECREF(ivars->expanded); + SUPER_DESTROY(self, TESTQUERYPARSER); +} + +String* +TestQP_Get_Query_String_IMP(TestQueryParser *self) { + return TestQP_IVARS(self)->query_string; +} + +Query* +TestQP_Get_Tree_IMP(TestQueryParser *self) { + return TestQP_IVARS(self)->tree; +} + +Query* +TestQP_Get_Expanded_IMP(TestQueryParser *self) { + return TestQP_IVARS(self)->expanded; +} + +uint32_t +TestQP_Get_Num_Hits_IMP(TestQueryParser *self) { + return TestQP_IVARS(self)->num_hits; +} + + +
http://git-wip-us.apache.org/repos/asf/lucy/blob/572d3564/test/Lucy/Test/Search/TestQueryParser.cfh ---------------------------------------------------------------------- diff --git a/test/Lucy/Test/Search/TestQueryParser.cfh b/test/Lucy/Test/Search/TestQueryParser.cfh new file mode 100644 index 0000000..4c900d5 --- /dev/null +++ b/test/Lucy/Test/Search/TestQueryParser.cfh @@ -0,0 +1,61 @@ +/* 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. + */ + +parcel TestLucy; + +/** Test case object for QueryParser unit tests. + */ + +class Lucy::Test::Search::TestQueryParser nickname TestQP + inherits Clownfish::Obj { + + String *query_string; + Query *tree; + Query *expanded; + uint32_t num_hits; + + /** Note that unlike most Clownfish constructors, this routine will consume one + * reference count each for `tree`, and `expanded`. + */ + inert incremented TestQueryParser* + new(const char *query_string = NULL, Query *tree = NULL, + Query *expanded = NULL, uint32_t num_hits); + + inert TestQueryParser* + init(TestQueryParser *self, const char *query_string = NULL, + Query *tree = NULL, Query *expanded = NULL, uint32_t num_hits); + + nullable String* + Get_Query_String(TestQueryParser *self); + + nullable Query* + Get_Tree(TestQueryParser *self); + + nullable Query* + Get_Expanded(TestQueryParser *self); + + uint32_t + Get_Num_Hits(TestQueryParser *self); + + public void + Destroy(TestQueryParser *self); +} + +__C__ + +__END_C__ + + http://git-wip-us.apache.org/repos/asf/lucy/blob/572d3564/test/Lucy/Test/Search/TestQueryParserLogic.c ---------------------------------------------------------------------- diff --git a/test/Lucy/Test/Search/TestQueryParserLogic.c b/test/Lucy/Test/Search/TestQueryParserLogic.c new file mode 100644 index 0000000..22104fd --- /dev/null +++ b/test/Lucy/Test/Search/TestQueryParserLogic.c @@ -0,0 +1,976 @@ +/* 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_TESTLUCY_TESTQUERYPARSERLOGIC +#define C_TESTLUCY_TESTQUERYPARSER +#define TESTLUCY_USE_SHORT_NAMES +#include "Lucy/Util/ToolSet.h" +#include <string.h> +#include <stdlib.h> + +#include "Clownfish/TestHarness/TestBatchRunner.h" +#include "Lucy/Test.h" +#include "Lucy/Test/Search/TestQueryParserLogic.h" +#include "Lucy/Test/Search/TestQueryParser.h" +#include "Lucy/Test/TestSchema.h" +#include "Lucy/Test/TestUtils.h" +#include "Lucy/Analysis/Analyzer.h" +#include "Lucy/Document/Doc.h" +#include "Lucy/Index/Indexer.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/MatchAllQuery.h" +#include "Lucy/Search/NOTQuery.h" +#include "Lucy/Search/NoMatchQuery.h" +#include "Lucy/Search/ORQuery.h" +#include "Lucy/Search/RequiredOptionalQuery.h" +#include "Lucy/Store/RAMFolder.h" + +#define make_leaf_query (Query*)TestUtils_make_leaf_query +#define make_not_query (Query*)TestUtils_make_not_query +#define make_poly_query (Query*)TestUtils_make_poly_query + +TestQueryParserLogic* +TestQPLogic_new() { + return (TestQueryParserLogic*)Class_Make_Obj(TESTQUERYPARSERLOGIC); +} + +static TestQueryParser* +logical_test_empty_phrase(uint32_t boolop) { + Query *tree = make_leaf_query(NULL, "\"\""); + UNUSED_VAR(boolop); + return TestQP_new("\"\"", tree, NULL, 0); +} + +static TestQueryParser* +logical_test_empty_parens(uint32_t boolop) { + Query *tree = make_poly_query(boolop, NULL); + return TestQP_new("()", tree, NULL, 0); +} + +static TestQueryParser* +logical_test_nested_empty_parens(uint32_t boolop) { + Query *inner = make_poly_query(boolop, NULL); + Query *tree = make_poly_query(boolop, inner, NULL); + return TestQP_new("(())", tree, NULL, 0); +} + +static TestQueryParser* +logical_test_nested_empty_phrase(uint32_t boolop) { + Query *leaf = make_leaf_query(NULL, "\"\""); + Query *tree = make_poly_query(boolop, leaf, NULL); + return TestQP_new("(\"\")", tree, NULL, 0); +} + +static TestQueryParser* +logical_test_simple_term(uint32_t boolop) { + Query *tree = make_leaf_query(NULL, "b"); + UNUSED_VAR(boolop); + return TestQP_new("b", tree, NULL, 3); +} + +static TestQueryParser* +logical_test_one_nested_term(uint32_t boolop) { + Query *leaf = make_leaf_query(NULL, "a"); + Query *tree = make_poly_query(boolop, leaf, NULL); + return TestQP_new("(a)", tree, NULL, 4); +} + +static TestQueryParser* +logical_test_one_term_phrase(uint32_t boolop) { + Query *tree = make_leaf_query(NULL, "\"a\""); + UNUSED_VAR(boolop); + return TestQP_new("\"a\"", tree, NULL, 4); +} + +static TestQueryParser* +logical_test_two_terms(uint32_t boolop) { + Query *a_leaf = make_leaf_query(NULL, "a"); + Query *b_leaf = make_leaf_query(NULL, "b"); + Query *tree = make_poly_query(boolop, a_leaf, b_leaf, NULL); + uint32_t num_hits = boolop == BOOLOP_OR ? 4 : 3; + return TestQP_new("a b", tree, NULL, num_hits); +} + +static TestQueryParser* +logical_test_two_terms_nested(uint32_t boolop) { + Query *a_leaf = make_leaf_query(NULL, "a"); + Query *b_leaf = make_leaf_query(NULL, "b"); + Query *tree = make_poly_query(boolop, a_leaf, b_leaf, NULL); + uint32_t num_hits = boolop == BOOLOP_OR ? 4 : 3; + return TestQP_new("(a b)", tree, NULL, num_hits); +} + +static TestQueryParser* +logical_test_one_term_one_single_term_phrase(uint32_t boolop) { + Query *a_leaf = make_leaf_query(NULL, "a"); + Query *b_leaf = make_leaf_query(NULL, "\"b\""); + Query *tree = make_poly_query(boolop, a_leaf, b_leaf, NULL); + uint32_t num_hits = boolop == BOOLOP_OR ? 4 : 3; + return TestQP_new("a \"b\"", tree, NULL, num_hits); +} + +static TestQueryParser* +logical_test_two_terms_one_nested(uint32_t boolop) { + Query *a_leaf = make_leaf_query(NULL, "a"); + Query *b_leaf = make_leaf_query(NULL, "b"); + Query *b_tree = make_poly_query(boolop, b_leaf, NULL); + Query *tree = make_poly_query(boolop, a_leaf, b_tree, NULL); + uint32_t num_hits = boolop == BOOLOP_OR ? 4 : 3; + return TestQP_new("a (b)", tree, NULL, num_hits); +} + +static TestQueryParser* +logical_test_one_term_one_nested_single_term_phrase(uint32_t boolop) { + Query *a_leaf = make_leaf_query(NULL, "a"); + Query *b_leaf = make_leaf_query(NULL, "\"b\""); + Query *b_tree = make_poly_query(boolop, b_leaf, NULL); + Query *tree = make_poly_query(boolop, a_leaf, b_tree, NULL); + uint32_t num_hits = boolop == BOOLOP_OR ? 4 : 3; + return TestQP_new("a (\"b\")", tree, NULL, num_hits); +} + +static TestQueryParser* +logical_test_phrase(uint32_t boolop) { + Query *tree = make_leaf_query(NULL, "\"a b\""); + UNUSED_VAR(boolop); + return TestQP_new("\"a b\"", tree, NULL, 3); +} + +static TestQueryParser* +logical_test_nested_phrase(uint32_t boolop) { + Query *leaf = make_leaf_query(NULL, "\"a b\""); + Query *tree = make_poly_query(boolop, leaf, NULL); + return TestQP_new("(\"a b\")", tree, NULL, 3); +} + +static TestQueryParser* +logical_test_three_terms(uint32_t boolop) { + Query *a_leaf = make_leaf_query(NULL, "a"); + Query *b_leaf = make_leaf_query(NULL, "b"); + Query *c_leaf = make_leaf_query(NULL, "c"); + Query *tree = make_poly_query(boolop, a_leaf, b_leaf, + c_leaf, NULL); + uint32_t num_hits = boolop == BOOLOP_OR ? 4 : 2; + return TestQP_new("a b c", tree, NULL, num_hits); +} + +static TestQueryParser* +logical_test_three_terms_two_nested(uint32_t boolop) { + Query *a_leaf = make_leaf_query(NULL, "a"); + Query *b_leaf = make_leaf_query(NULL, "b"); + Query *c_leaf = make_leaf_query(NULL, "c"); + Query *inner_tree = make_poly_query(boolop, b_leaf, c_leaf, NULL); + Query *tree = make_poly_query(boolop, a_leaf, inner_tree, NULL); + uint32_t num_hits = boolop == BOOLOP_OR ? 4 : 2; + return TestQP_new("a (b c)", tree, NULL, num_hits); +} + +static TestQueryParser* +logical_test_one_term_one_phrase(uint32_t boolop) { + Query *a_leaf = make_leaf_query(NULL, "a"); + Query *bc_leaf = make_leaf_query(NULL, "\"b c\""); + Query *tree = make_poly_query(boolop, a_leaf, bc_leaf, NULL); + uint32_t num_hits = boolop == BOOLOP_OR ? 4 : 2; + return TestQP_new("a \"b c\"", tree, NULL, num_hits); +} + +static TestQueryParser* +logical_test_one_term_one_nested_phrase(uint32_t boolop) { + Query *a_leaf = make_leaf_query(NULL, "a"); + Query *bc_leaf = make_leaf_query(NULL, "\"b c\""); + Query *inner_tree = make_poly_query(boolop, bc_leaf, NULL); + Query *tree = make_poly_query(boolop, a_leaf, inner_tree, NULL); + uint32_t num_hits = boolop == BOOLOP_OR ? 4 : 2; + return TestQP_new("a (\"b c\")", tree, NULL, num_hits); +} + +static TestQueryParser* +logical_test_long_phrase(uint32_t boolop) { + Query *tree = make_leaf_query(NULL, "\"a b c\""); + UNUSED_VAR(boolop); + return TestQP_new("\"a b c\"", tree, NULL, 2); +} + +static TestQueryParser* +logical_test_pure_negation(uint32_t boolop) { + Query *leaf = make_leaf_query(NULL, "x"); + Query *tree = make_not_query(leaf); + UNUSED_VAR(boolop); + return TestQP_new("-x", tree, NULL, 0); +} + +static TestQueryParser* +logical_test_double_negative(uint32_t boolop) { + Query *tree = make_leaf_query(NULL, "a"); + UNUSED_VAR(boolop); + return TestQP_new("--a", tree, NULL, 4); +} + +static TestQueryParser* +logical_test_triple_negative(uint32_t boolop) { + Query *leaf = make_leaf_query(NULL, "a"); + Query *tree = make_not_query(leaf); + UNUSED_VAR(boolop); + return TestQP_new("---a", tree, NULL, 0); +} + +// Technically, this should produce an acceptably small result set, but it's +// too difficult to prune -- so QParser_Prune just lops it because it's a +// top-level NOTQuery. +static TestQueryParser* +logical_test_nested_negations(uint32_t boolop) { + Query *query = make_leaf_query(NULL, "a"); + query = make_poly_query(boolop, query, NULL); + query = make_not_query(query); + query = make_poly_query(BOOLOP_AND, query, NULL); + query = make_not_query(query); + return TestQP_new("-(-(a))", query, NULL, 0); +} + +static TestQueryParser* +logical_test_two_terms_one_required(uint32_t boolop) { + Query *a_query = make_leaf_query(NULL, "a"); + Query *b_query = make_leaf_query(NULL, "b"); + Query *tree; + if (boolop == BOOLOP_AND) { + tree = make_poly_query(boolop, a_query, b_query, NULL); + } + else { + tree = (Query*)ReqOptQuery_new(b_query, a_query); + DECREF(b_query); + DECREF(a_query); + } + return TestQP_new("a +b", tree, NULL, 3); +} + +static TestQueryParser* +logical_test_intersection(uint32_t boolop) { + Query *a_query = make_leaf_query(NULL, "a"); + Query *b_query = make_leaf_query(NULL, "b"); + Query *tree = make_poly_query(BOOLOP_AND, a_query, b_query, NULL); + UNUSED_VAR(boolop); + return TestQP_new("a AND b", tree, NULL, 3); +} + +static TestQueryParser* +logical_test_three_way_intersection(uint32_t boolop) { + Query *a_query = make_leaf_query(NULL, "a"); + Query *b_query = make_leaf_query(NULL, "b"); + Query *c_query = make_leaf_query(NULL, "c"); + Query *tree = make_poly_query(BOOLOP_AND, a_query, b_query, + c_query, NULL); + UNUSED_VAR(boolop); + return TestQP_new("a AND b AND c", tree, NULL, 2); +} + +static TestQueryParser* +logical_test_union(uint32_t boolop) { + Query *a_query = make_leaf_query(NULL, "a"); + Query *b_query = make_leaf_query(NULL, "b"); + Query *tree = make_poly_query(BOOLOP_OR, a_query, b_query, NULL); + UNUSED_VAR(boolop); + return TestQP_new("a OR b", tree, NULL, 4); +} + +static TestQueryParser* +logical_test_three_way_union(uint32_t boolop) { + Query *a_query = make_leaf_query(NULL, "a"); + Query *b_query = make_leaf_query(NULL, "b"); + Query *c_query = make_leaf_query(NULL, "c"); + Query *tree = make_poly_query(BOOLOP_OR, a_query, b_query, c_query, NULL); + UNUSED_VAR(boolop); + return TestQP_new("a OR b OR c", tree, NULL, 4); +} + +static TestQueryParser* +logical_test_a_or_plus_b(uint32_t boolop) { + Query *a_query = make_leaf_query(NULL, "a"); + Query *b_query = make_leaf_query(NULL, "b"); + Query *tree = make_poly_query(BOOLOP_OR, a_query, b_query, NULL); + UNUSED_VAR(boolop); + return TestQP_new("a OR +b", tree, NULL, 4); +} + +static TestQueryParser* +logical_test_and_not(uint32_t boolop) { + Query *a_query = make_leaf_query(NULL, "a"); + Query *b_query = make_leaf_query(NULL, "b"); + Query *not_b = make_not_query(b_query); + Query *tree = make_poly_query(BOOLOP_AND, a_query, not_b, NULL); + UNUSED_VAR(boolop); + return TestQP_new("a AND NOT b", tree, NULL, 1); +} + +static TestQueryParser* +logical_test_nested_or(uint32_t boolop) { + Query *a_query = make_leaf_query(NULL, "a"); + Query *b_query = make_leaf_query(NULL, "b"); + Query *c_query = make_leaf_query(NULL, "c"); + Query *nested = make_poly_query(BOOLOP_OR, b_query, c_query, NULL); + Query *tree = make_poly_query(boolop, a_query, nested, NULL); + return TestQP_new("a (b OR c)", tree, NULL, boolop == BOOLOP_OR ? 4 : 3); +} + +static TestQueryParser* +logical_test_and_nested_or(uint32_t boolop) { + Query *a_query = make_leaf_query(NULL, "a"); + Query *b_query = make_leaf_query(NULL, "b"); + Query *c_query = make_leaf_query(NULL, "c"); + Query *nested = make_poly_query(BOOLOP_OR, b_query, c_query, NULL); + Query *tree = make_poly_query(BOOLOP_AND, a_query, nested, NULL); + UNUSED_VAR(boolop); + return TestQP_new("a AND (b OR c)", tree, NULL, 3); +} + +static TestQueryParser* +logical_test_or_nested_or(uint32_t boolop) { + Query *a_query = make_leaf_query(NULL, "a"); + Query *b_query = make_leaf_query(NULL, "b"); + Query *c_query = make_leaf_query(NULL, "c"); + Query *nested = make_poly_query(BOOLOP_OR, b_query, c_query, NULL); + Query *tree = make_poly_query(BOOLOP_OR, a_query, nested, NULL); + UNUSED_VAR(boolop); + return TestQP_new("a OR (b OR c)", tree, NULL, 4); +} + +static TestQueryParser* +logical_test_and_not_nested_or(uint32_t boolop) { + Query *a_query = make_leaf_query(NULL, "a"); + Query *b_query = make_leaf_query(NULL, "b"); + Query *c_query = make_leaf_query(NULL, "c"); + Query *nested = make_poly_query(BOOLOP_OR, b_query, c_query, NULL); + Query *not_nested = make_not_query(nested); + Query *tree = make_poly_query(BOOLOP_AND, a_query, + not_nested, NULL); + UNUSED_VAR(boolop); + return TestQP_new("a AND NOT (b OR c)", tree, NULL, 1); +} + +static TestQueryParser* +logical_test_a_AND_b_AND_c_AND_d(uint32_t boolop) { + Query *a_query = make_leaf_query(NULL, "a"); + Query *b_query = make_leaf_query(NULL, "b"); + Query *c_query = make_leaf_query(NULL, "c"); + Query *d_query = make_leaf_query(NULL, "d"); + Query *tree = make_poly_query(BOOLOP_AND, a_query, b_query, c_query, + d_query, NULL); + UNUSED_VAR(boolop); + return TestQP_new("a AND b AND c AND d", tree, NULL, 1); +} + +static TestQueryParser* +logical_test_a_AND_b_AND_c_OR_d(uint32_t boolop) { + Query *a_query = make_leaf_query(NULL, "a"); + Query *b_query = make_leaf_query(NULL, "b"); + Query *c_query = make_leaf_query(NULL, "c"); + Query *d_query = make_leaf_query(NULL, "d"); + Query *nested = make_poly_query(BOOLOP_AND, a_query, b_query, c_query, + NULL); + Query *tree = make_poly_query(BOOLOP_OR, nested, + d_query, NULL); + UNUSED_VAR(boolop); + return TestQP_new("a AND b AND c OR d", tree, NULL, 2); +} + +static TestQueryParser* +logical_test_a_AND_b_OR_c_AND_d(uint32_t boolop) { + Query *a_query = make_leaf_query(NULL, "a"); + Query *b_query = make_leaf_query(NULL, "b"); + Query *c_query = make_leaf_query(NULL, "c"); + Query *d_query = make_leaf_query(NULL, "d"); + Query *left = make_poly_query(BOOLOP_AND, a_query, b_query, NULL); + Query *right = make_poly_query(BOOLOP_AND, c_query, d_query, NULL); + Query *tree = make_poly_query(BOOLOP_OR, left, right, NULL); + UNUSED_VAR(boolop); + return TestQP_new("a AND b OR c AND d", tree, NULL, 3); +} + +static TestQueryParser* +logical_test_a_AND_b_OR_c_OR_d(uint32_t boolop) { + Query *a_query = make_leaf_query(NULL, "a"); + Query *b_query = make_leaf_query(NULL, "b"); + Query *c_query = make_leaf_query(NULL, "c"); + Query *d_query = make_leaf_query(NULL, "d"); + Query *left = make_poly_query(BOOLOP_AND, a_query, b_query, NULL); + Query *tree = make_poly_query(BOOLOP_OR, left, c_query, d_query, + NULL); + UNUSED_VAR(boolop); + return TestQP_new("a AND b OR c OR d", tree, NULL, 3); +} + +static TestQueryParser* +logical_test_a_OR_b_AND_c_AND_d(uint32_t boolop) { + Query *a_query = make_leaf_query(NULL, "a"); + Query *b_query = make_leaf_query(NULL, "b"); + Query *c_query = make_leaf_query(NULL, "c"); + Query *d_query = make_leaf_query(NULL, "d"); + Query *right = make_poly_query(BOOLOP_AND, b_query, c_query, d_query, + NULL); + Query *tree = make_poly_query(BOOLOP_OR, a_query, right, NULL); + UNUSED_VAR(boolop); + return TestQP_new("a OR b AND c AND d", tree, NULL, 4); +} + +static TestQueryParser* +logical_test_a_OR_b_AND_c_OR_d(uint32_t boolop) { + Query *a_query = make_leaf_query(NULL, "a"); + Query *b_query = make_leaf_query(NULL, "b"); + Query *c_query = make_leaf_query(NULL, "c"); + Query *d_query = make_leaf_query(NULL, "d"); + Query *middle = make_poly_query(BOOLOP_AND, b_query, c_query, NULL); + Query *tree = make_poly_query(BOOLOP_OR, a_query, middle, d_query, + NULL); + UNUSED_VAR(boolop); + return TestQP_new("a OR b AND c OR d", tree, NULL, 4); +} + +static TestQueryParser* +logical_test_a_OR_b_OR_c_AND_d(uint32_t boolop) { + Query *a_query = make_leaf_query(NULL, "a"); + Query *b_query = make_leaf_query(NULL, "b"); + Query *c_query = make_leaf_query(NULL, "c"); + Query *d_query = make_leaf_query(NULL, "d"); + Query *right = make_poly_query(BOOLOP_AND, c_query, d_query, NULL); + Query *tree = make_poly_query(BOOLOP_OR, a_query, b_query, right, + NULL); + UNUSED_VAR(boolop); + return TestQP_new("a OR b OR c AND d", tree, NULL, 4); +} + +static TestQueryParser* +logical_test_a_OR_b_OR_c_OR_d(uint32_t boolop) { + Query *a_query = make_leaf_query(NULL, "a"); + Query *b_query = make_leaf_query(NULL, "b"); + Query *c_query = make_leaf_query(NULL, "c"); + Query *d_query = make_leaf_query(NULL, "d"); + Query *tree = make_poly_query(BOOLOP_OR, a_query, b_query, c_query, + d_query, NULL); + UNUSED_VAR(boolop); + return TestQP_new("a OR b OR c OR d", tree, NULL, 4); +} + +static TestQueryParser* +logical_test_a_AND_b_AND_c_d(uint32_t boolop) { + Query *a_query = make_leaf_query(NULL, "a"); + Query *b_query = make_leaf_query(NULL, "b"); + Query *c_query = make_leaf_query(NULL, "c"); + Query *d_query = make_leaf_query(NULL, "d"); + Query *left = make_poly_query(BOOLOP_AND, a_query, b_query, c_query, + NULL); + Query *tree = make_poly_query(boolop, left, d_query, NULL); + uint32_t num_hits = boolop == BOOLOP_AND ? 1 : 2; + return TestQP_new("a AND b AND c d", tree, NULL, num_hits); +} + +static TestQueryParser* +logical_test_a_AND_b_OR_c_d(uint32_t boolop) { + Query *a_query = make_leaf_query(NULL, "a"); + Query *b_query = make_leaf_query(NULL, "b"); + Query *c_query = make_leaf_query(NULL, "c"); + Query *d_query = make_leaf_query(NULL, "d"); + Query *inner = make_poly_query(BOOLOP_AND, a_query, b_query, NULL); + Query *left = make_poly_query(BOOLOP_OR, inner, c_query, NULL); + Query *tree = make_poly_query(boolop, left, d_query, NULL); + uint32_t num_hits = boolop == BOOLOP_AND ? 1 : 3; + return TestQP_new("a AND b OR c d", tree, NULL, num_hits); +} + +static TestQueryParser* +logical_test_a_OR_b_AND_c_d(uint32_t boolop) { + Query *a_query = make_leaf_query(NULL, "a"); + Query *b_query = make_leaf_query(NULL, "b"); + Query *c_query = make_leaf_query(NULL, "c"); + Query *d_query = make_leaf_query(NULL, "d"); + Query *inner = make_poly_query(BOOLOP_AND, b_query, c_query, NULL); + Query *left = make_poly_query(BOOLOP_OR, a_query, inner, NULL); + Query *tree = make_poly_query(boolop, left, d_query, NULL); + uint32_t num_hits = boolop == BOOLOP_AND ? 1 : 4; + return TestQP_new("a OR b AND c d", tree, NULL, num_hits); +} + +static TestQueryParser* +logical_test_a_OR_b_OR_c_d(uint32_t boolop) { + Query *a_query = make_leaf_query(NULL, "a"); + Query *b_query = make_leaf_query(NULL, "b"); + Query *c_query = make_leaf_query(NULL, "c"); + Query *d_query = make_leaf_query(NULL, "d"); + Query *left = make_poly_query(BOOLOP_OR, a_query, b_query, c_query, + NULL); + Query *tree = make_poly_query(boolop, left, d_query, NULL); + uint32_t num_hits = boolop == BOOLOP_AND ? 1 : 4; + return TestQP_new("a OR b OR c d", tree, NULL, num_hits); +} + +static TestQueryParser* +logical_test_a_AND_b_c_AND_d(uint32_t boolop) { + Query *a_query = make_leaf_query(NULL, "a"); + Query *b_query = make_leaf_query(NULL, "b"); + Query *c_query = make_leaf_query(NULL, "c"); + Query *d_query = make_leaf_query(NULL, "d"); + Query *left = make_poly_query(BOOLOP_AND, a_query, b_query, NULL); + Query *right = make_poly_query(BOOLOP_AND, c_query, d_query, NULL); + Query *tree = make_poly_query(boolop, left, right, NULL); + uint32_t num_hits = boolop == BOOLOP_AND ? 1 : 3; + return TestQP_new("a AND b c AND d", tree, NULL, num_hits); +} + +static TestQueryParser* +logical_test_a_AND_b_c_OR_d(uint32_t boolop) { + Query *a_query = make_leaf_query(NULL, "a"); + Query *b_query = make_leaf_query(NULL, "b"); + Query *c_query = make_leaf_query(NULL, "c"); + Query *d_query = make_leaf_query(NULL, "d"); + Query *left = make_poly_query(BOOLOP_AND, a_query, b_query, NULL); + Query *right = make_poly_query(BOOLOP_OR, c_query, d_query, NULL); + Query *tree = make_poly_query(boolop, left, right, NULL); + uint32_t num_hits = boolop == BOOLOP_AND ? 2 : 3; + return TestQP_new("a AND b c OR d", tree, NULL, num_hits); +} + +static TestQueryParser* +logical_test_a_OR_b_c_AND_d(uint32_t boolop) { + Query *a_query = make_leaf_query(NULL, "a"); + Query *b_query = make_leaf_query(NULL, "b"); + Query *c_query = make_leaf_query(NULL, "c"); + Query *d_query = make_leaf_query(NULL, "d"); + Query *left = make_poly_query(BOOLOP_OR, a_query, b_query, NULL); + Query *right = make_poly_query(BOOLOP_AND, c_query, d_query, NULL); + Query *tree = make_poly_query(boolop, left, right, NULL); + uint32_t num_hits = boolop == BOOLOP_AND ? 1 : 4; + return TestQP_new("a OR b c AND d", tree, NULL, num_hits); +} + +static TestQueryParser* +logical_test_a_OR_b_c_OR_d(uint32_t boolop) { + Query *a_query = make_leaf_query(NULL, "a"); + Query *b_query = make_leaf_query(NULL, "b"); + Query *c_query = make_leaf_query(NULL, "c"); + Query *d_query = make_leaf_query(NULL, "d"); + Query *left = make_poly_query(BOOLOP_OR, a_query, b_query, NULL); + Query *right = make_poly_query(BOOLOP_OR, c_query, d_query, NULL); + Query *tree = make_poly_query(boolop, left, right, NULL); + uint32_t num_hits = boolop == BOOLOP_AND ? 2 : 4; + return TestQP_new("a OR b c OR d", tree, NULL, num_hits); +} + +static TestQueryParser* +logical_test_a_b_AND_c_AND_d(uint32_t boolop) { + Query *a_query = make_leaf_query(NULL, "a"); + Query *b_query = make_leaf_query(NULL, "b"); + Query *c_query = make_leaf_query(NULL, "c"); + Query *d_query = make_leaf_query(NULL, "d"); + Query *right = make_poly_query(BOOLOP_AND, b_query, c_query, d_query, + NULL); + Query *tree = make_poly_query(boolop, a_query, right, NULL); + uint32_t num_hits = boolop == BOOLOP_AND ? 1 : 4; + return TestQP_new("a b AND c AND d", tree, NULL, num_hits); +} + +static TestQueryParser* +logical_test_a_b_AND_c_OR_d(uint32_t boolop) { + Query *a_query = make_leaf_query(NULL, "a"); + Query *b_query = make_leaf_query(NULL, "b"); + Query *c_query = make_leaf_query(NULL, "c"); + Query *d_query = make_leaf_query(NULL, "d"); + Query *inner = make_poly_query(BOOLOP_AND, b_query, c_query, NULL); + Query *right = make_poly_query(BOOLOP_OR, inner, d_query, NULL); + Query *tree = make_poly_query(boolop, a_query, right, NULL); + uint32_t num_hits = boolop == BOOLOP_AND ? 2 : 4; + return TestQP_new("a b AND c OR d", tree, NULL, num_hits); +} + +static TestQueryParser* +logical_test_a_b_OR_c_AND_d(uint32_t boolop) { + Query *a_query = make_leaf_query(NULL, "a"); + Query *b_query = make_leaf_query(NULL, "b"); + Query *c_query = make_leaf_query(NULL, "c"); + Query *d_query = make_leaf_query(NULL, "d"); + Query *inner = make_poly_query(BOOLOP_AND, c_query, d_query, NULL); + Query *right = make_poly_query(BOOLOP_OR, b_query, inner, NULL); + Query *tree = make_poly_query(boolop, a_query, right, NULL); + uint32_t num_hits = boolop == BOOLOP_AND ? 3 : 4; + return TestQP_new("a b OR c AND d", tree, NULL, num_hits); +} + +static TestQueryParser* +logical_test_a_b_OR_c_OR_d(uint32_t boolop) { + Query *a_query = make_leaf_query(NULL, "a"); + Query *b_query = make_leaf_query(NULL, "b"); + Query *c_query = make_leaf_query(NULL, "c"); + Query *d_query = make_leaf_query(NULL, "d"); + Query *right = make_poly_query(BOOLOP_OR, b_query, c_query, d_query, + NULL); + Query *tree = make_poly_query(boolop, a_query, right, NULL); + uint32_t num_hits = boolop == BOOLOP_AND ? 3 : 4; + return TestQP_new("a b OR c OR d", tree, NULL, num_hits); +} + +static TestQueryParser* +logical_test_required_phrase_negated_term(uint32_t boolop) { + Query *bc_query = make_leaf_query(NULL, "\"b c\""); + Query *d_query = make_leaf_query(NULL, "d"); + Query *not_d = make_not_query(d_query); + Query *tree = make_poly_query(BOOLOP_AND, bc_query, not_d, NULL); + UNUSED_VAR(boolop); + return TestQP_new("+\"b c\" -d", tree, NULL, 1); +} + +static TestQueryParser* +logical_test_required_term_optional_phrase(uint32_t boolop) { + Query *ab_query = make_leaf_query(NULL, "\"a b\""); + Query *d_query = make_leaf_query(NULL, "d"); + Query *tree; + if (boolop == BOOLOP_AND) { + tree = make_poly_query(BOOLOP_AND, ab_query, d_query, NULL); + } + else { + tree = (Query*)ReqOptQuery_new(d_query, ab_query); + DECREF(d_query); + DECREF(ab_query); + } + UNUSED_VAR(boolop); + return TestQP_new("\"a b\" +d", tree, NULL, 1); +} + +static TestQueryParser* +logical_test_nested_nest(uint32_t boolop) { + Query *a_query = make_leaf_query(NULL, "a"); + Query *b_query = make_leaf_query(NULL, "b"); + Query *c_query = make_leaf_query(NULL, "c"); + Query *d_query = make_leaf_query(NULL, "d"); + Query *innermost = make_poly_query(BOOLOP_AND, c_query, d_query, NULL); + Query *inner = make_poly_query(BOOLOP_OR, b_query, innermost, NULL); + Query *not_inner = make_not_query(inner); + Query *tree = make_poly_query(BOOLOP_AND, a_query, not_inner, NULL); + UNUSED_VAR(boolop); + return TestQP_new("a AND NOT (b OR (c AND d))", tree, NULL, 1); +} + +static TestQueryParser* +logical_test_field_bool_group(uint32_t boolop) { + Query *b_query = make_leaf_query("content", "b"); + Query *c_query = make_leaf_query("content", "c"); + Query *tree = make_poly_query(boolop, b_query, c_query, NULL); + return TestQP_new("content:(b c)", tree, NULL, + boolop == BOOLOP_OR ? 3 : 2); +} + +static TestQueryParser* +logical_test_field_multi_OR(uint32_t boolop) { + Query *a_query = make_leaf_query("content", "a"); + Query *b_query = make_leaf_query("content", "b"); + Query *c_query = make_leaf_query("content", "c"); + Query *tree = make_poly_query(BOOLOP_OR, a_query, b_query, c_query, NULL); + UNUSED_VAR(boolop); + return TestQP_new("content:(a OR b OR c)", tree, NULL, 4); +} + +static TestQueryParser* +logical_test_field_multi_AND(uint32_t boolop) { + Query *a_query = make_leaf_query("content", "a"); + Query *b_query = make_leaf_query("content", "b"); + Query *c_query = make_leaf_query("content", "c"); + Query *tree = make_poly_query(BOOLOP_AND, a_query, b_query, + c_query, NULL); + UNUSED_VAR(boolop); + return TestQP_new("content:(a AND b AND c)", tree, NULL, 2); +} + +static TestQueryParser* +logical_test_field_phrase(uint32_t boolop) { + Query *tree = make_leaf_query("content", "\"b c\""); + UNUSED_VAR(boolop); + return TestQP_new("content:\"b c\"", tree, NULL, 2); +} + +static TestQueryParser* +prune_test_null_querystring() { + Query *pruned = (Query*)NoMatchQuery_new(); + return TestQP_new(NULL, NULL, pruned, 0); +} + +static TestQueryParser* +prune_test_matchall() { + Query *tree = (Query*)MatchAllQuery_new(); + Query *pruned = (Query*)NoMatchQuery_new(); + return TestQP_new(NULL, tree, pruned, 0); +} + +static TestQueryParser* +prune_test_nomatch() { + Query *tree = (Query*)NoMatchQuery_new(); + Query *pruned = (Query*)NoMatchQuery_new(); + return TestQP_new(NULL, tree, pruned, 0); +} + +static TestQueryParser* +prune_test_optional_not() { + Query *a_leaf = make_leaf_query(NULL, "a"); + Query *b_leaf = make_leaf_query(NULL, "b"); + Query *not_b = make_not_query(b_leaf); + Query *tree = make_poly_query(BOOLOP_OR, (Query*)INCREF(a_leaf), + not_b, NULL); + Query *nomatch = (Query*)NoMatchQuery_new(); + Query *pruned = make_poly_query(BOOLOP_OR, a_leaf, nomatch, NULL); + return TestQP_new(NULL, tree, pruned, 4); +} + +static TestQueryParser* +prune_test_reqopt_optional_not() { + Query *a_leaf = make_leaf_query(NULL, "a"); + Query *b_leaf = make_leaf_query(NULL, "b"); + Query *not_b = make_not_query(b_leaf); + Query *tree = (Query*)ReqOptQuery_new(a_leaf, not_b); + Query *nomatch = (Query*)NoMatchQuery_new(); + Query *pruned = (Query*)ReqOptQuery_new(a_leaf, nomatch); + DECREF(nomatch); + DECREF(not_b); + DECREF(a_leaf); + return TestQP_new(NULL, tree, pruned, 4); +} + +static TestQueryParser* +prune_test_reqopt_required_not() { + Query *a_leaf = make_leaf_query(NULL, "a"); + Query *b_leaf = make_leaf_query(NULL, "b"); + Query *not_a = make_not_query(a_leaf); + Query *tree = (Query*)ReqOptQuery_new(not_a, b_leaf); + Query *nomatch = (Query*)NoMatchQuery_new(); + Query *pruned = (Query*)ReqOptQuery_new(nomatch, b_leaf); + DECREF(nomatch); + DECREF(not_a); + DECREF(b_leaf); + return TestQP_new(NULL, tree, pruned, 0); +} + +static TestQueryParser* +prune_test_not_and_not() { + Query *a_leaf = make_leaf_query(NULL, "a"); + Query *b_leaf = make_leaf_query(NULL, "b"); + Query *not_a = make_not_query(a_leaf); + Query *not_b = make_not_query(b_leaf); + Query *tree = make_poly_query(BOOLOP_AND, not_a, not_b, NULL); + Query *pruned = make_poly_query(BOOLOP_AND, NULL); + return TestQP_new(NULL, tree, pruned, 0); +} + +/***************************************************************************/ + +typedef TestQueryParser* +(*LUCY_TestQPLogic_Logical_Test_t)(uint32_t boolop_sym); + +static LUCY_TestQPLogic_Logical_Test_t logical_test_funcs[] = { + logical_test_empty_phrase, + logical_test_empty_parens, + logical_test_nested_empty_parens, + logical_test_nested_empty_phrase, + logical_test_simple_term, + logical_test_one_nested_term, + logical_test_one_term_phrase, + logical_test_two_terms, + logical_test_two_terms_nested, + logical_test_one_term_one_single_term_phrase, + logical_test_two_terms_one_nested, + logical_test_one_term_one_nested_phrase, + logical_test_phrase, + logical_test_nested_phrase, + logical_test_three_terms, + logical_test_three_terms_two_nested, + logical_test_one_term_one_phrase, + logical_test_one_term_one_nested_single_term_phrase, + logical_test_long_phrase, + logical_test_pure_negation, + logical_test_double_negative, + logical_test_triple_negative, + logical_test_nested_negations, + logical_test_two_terms_one_required, + logical_test_intersection, + logical_test_three_way_intersection, + logical_test_union, + logical_test_three_way_union, + logical_test_a_or_plus_b, + logical_test_and_not, + logical_test_nested_or, + logical_test_and_nested_or, + logical_test_or_nested_or, + logical_test_and_not_nested_or, + logical_test_a_AND_b_AND_c_AND_d, + logical_test_a_AND_b_AND_c_OR_d, + logical_test_a_AND_b_OR_c_AND_d, + logical_test_a_AND_b_OR_c_OR_d, + logical_test_a_OR_b_AND_c_AND_d, + logical_test_a_OR_b_AND_c_OR_d, + logical_test_a_OR_b_OR_c_AND_d, + logical_test_a_OR_b_OR_c_OR_d, + logical_test_a_AND_b_AND_c_d, + logical_test_a_AND_b_OR_c_d, + logical_test_a_OR_b_AND_c_d, + logical_test_a_OR_b_OR_c_d, + logical_test_a_AND_b_c_AND_d, + logical_test_a_AND_b_c_OR_d, + logical_test_a_OR_b_c_AND_d, + logical_test_a_OR_b_c_OR_d, + logical_test_a_b_AND_c_AND_d, + logical_test_a_b_AND_c_OR_d, + logical_test_a_b_OR_c_AND_d, + logical_test_a_b_OR_c_OR_d, + logical_test_required_phrase_negated_term, + logical_test_required_term_optional_phrase, + logical_test_nested_nest, + logical_test_field_phrase, + logical_test_field_bool_group, + logical_test_field_multi_OR, + logical_test_field_multi_AND, + NULL +}; + +typedef TestQueryParser* +(*LUCY_TestQPLogic_Prune_Test_t)(); + +static LUCY_TestQPLogic_Prune_Test_t prune_test_funcs[] = { + prune_test_null_querystring, + prune_test_matchall, + prune_test_nomatch, + prune_test_optional_not, + prune_test_reqopt_optional_not, + prune_test_reqopt_required_not, + prune_test_not_and_not, + NULL +}; + +static Folder* +S_create_index() { + Schema *schema = (Schema*)TestSchema_new(false); + RAMFolder *folder = RAMFolder_new(NULL); + Vector *doc_set = TestUtils_doc_set(); + Indexer *indexer = Indexer_new(schema, (Obj*)folder, NULL, 0); + + String *field = SSTR_WRAP_C("content"); + for (size_t i = 0, max = Vec_Get_Size(doc_set); i < max; i++) { + Doc *doc = Doc_new(NULL, 0); + Doc_Store(doc, field, Vec_Fetch(doc_set, i)); + Indexer_Add_Doc(indexer, doc, 1.0f); + DECREF(doc); + } + + Indexer_Commit(indexer); + + DECREF(doc_set); + DECREF(indexer); + DECREF(schema); + + return (Folder*)folder; +} + +void +TestQPLogic_Run_IMP(TestQueryParserLogic *self, TestBatchRunner *runner) { + TestBatchRunner_Plan(runner, (TestBatch*)self, 258); + + uint32_t i; + Folder *folder = S_create_index(); + IndexSearcher *searcher = IxSearcher_new((Obj*)folder); + QueryParser *or_parser = QParser_new(IxSearcher_Get_Schema(searcher), + NULL, NULL, NULL); + String *AND = SSTR_WRAP_C("AND"); + QueryParser *and_parser = QParser_new(IxSearcher_Get_Schema(searcher), + NULL, AND, NULL); + QParser_Set_Heed_Colons(or_parser, true); + QParser_Set_Heed_Colons(and_parser, true); + + // Run logical tests with default boolop of OR. + for (i = 0; logical_test_funcs[i] != NULL; i++) { + LUCY_TestQPLogic_Logical_Test_t test_func = logical_test_funcs[i]; + TestQueryParser *test_case_obj = test_func(BOOLOP_OR); + TestQueryParserIVARS *test_case = TestQP_IVARS(test_case_obj); + Query *tree = QParser_Tree(or_parser, test_case->query_string); + Query *parsed = QParser_Parse(or_parser, test_case->query_string); + Hits *hits = IxSearcher_Hits(searcher, (Obj*)parsed, 0, 10, NULL); + char *qstr = Str_To_Utf8(test_case->query_string); + + TEST_TRUE(runner, Query_Equals(tree, (Obj*)test_case->tree), + "tree() OR %s", qstr); + TEST_INT_EQ(runner, Hits_Total_Hits(hits), test_case->num_hits, + "hits: OR %s", qstr); + free(qstr); + DECREF(hits); + DECREF(parsed); + DECREF(tree); + DECREF(test_case_obj); + } + + // Run logical tests with default boolop of AND. + for (i = 0; logical_test_funcs[i] != NULL; i++) { + LUCY_TestQPLogic_Logical_Test_t test_func = logical_test_funcs[i]; + TestQueryParser *test_case_obj = test_func(BOOLOP_AND); + TestQueryParserIVARS *test_case = TestQP_IVARS(test_case_obj); + Query *tree = QParser_Tree(and_parser, test_case->query_string); + Query *parsed = QParser_Parse(and_parser, test_case->query_string); + Hits *hits = IxSearcher_Hits(searcher, (Obj*)parsed, 0, 10, NULL); + char *qstr = Str_To_Utf8(test_case->query_string); + + TEST_TRUE(runner, Query_Equals(tree, (Obj*)test_case->tree), + "tree() AND %s", qstr); + TEST_INT_EQ(runner, Hits_Total_Hits(hits), test_case->num_hits, + "hits: AND %s", qstr); + free(qstr); + DECREF(hits); + DECREF(parsed); + DECREF(tree); + DECREF(test_case_obj); + } + + // Run tests for QParser_Prune(). + for (i = 0; prune_test_funcs[i] != NULL; i++) { + LUCY_TestQPLogic_Prune_Test_t test_func = prune_test_funcs[i]; + TestQueryParser *test_case_obj = test_func(); + TestQueryParserIVARS *test_case = TestQP_IVARS(test_case_obj); + String *qstring = test_case->tree + ? Query_To_String(test_case->tree) + : Str_new_from_trusted_utf8("(NULL)", 6); + char *qstr = Str_To_Utf8(qstring); + Query *tree = test_case->tree; + Query *wanted = test_case->expanded; + Query *pruned = QParser_Prune(or_parser, tree); + Query *expanded; + Hits *hits; + + TEST_TRUE(runner, Query_Equals(pruned, (Obj*)wanted), + "prune() %s", qstr); + expanded = QParser_Expand(or_parser, pruned); + hits = IxSearcher_Hits(searcher, (Obj*)expanded, 0, 10, NULL); + TEST_INT_EQ(runner, Hits_Total_Hits(hits), test_case->num_hits, + "hits: %s", qstr); + + free(qstr); + DECREF(hits); + DECREF(expanded); + DECREF(pruned); + DECREF(qstring); + DECREF(test_case_obj); + } + + DECREF(and_parser); + DECREF(or_parser); + DECREF(searcher); + DECREF(folder); +} + http://git-wip-us.apache.org/repos/asf/lucy/blob/572d3564/test/Lucy/Test/Search/TestQueryParserLogic.cfh ---------------------------------------------------------------------- diff --git a/test/Lucy/Test/Search/TestQueryParserLogic.cfh b/test/Lucy/Test/Search/TestQueryParserLogic.cfh new file mode 100644 index 0000000..a0b1a75 --- /dev/null +++ b/test/Lucy/Test/Search/TestQueryParserLogic.cfh @@ -0,0 +1,32 @@ +/* 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. + */ + +parcel TestLucy; + +/** Tests for logical structure of Query objects output by QueryParser. + */ + +class Lucy::Test::Search::TestQueryParserLogic nickname TestQPLogic + inherits Clownfish::TestHarness::TestBatch { + + inert incremented TestQueryParserLogic* + new(); + + void + Run(TestQueryParserLogic *self, TestBatchRunner *runner); +} + + http://git-wip-us.apache.org/repos/asf/lucy/blob/572d3564/test/Lucy/Test/Search/TestQueryParserSyntax.c ---------------------------------------------------------------------- diff --git a/test/Lucy/Test/Search/TestQueryParserSyntax.c b/test/Lucy/Test/Search/TestQueryParserSyntax.c new file mode 100644 index 0000000..1cc4680 --- /dev/null +++ b/test/Lucy/Test/Search/TestQueryParserSyntax.c @@ -0,0 +1,452 @@ +/* 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_TESTLUCY_TESTQUERYPARSERSYNTAX +#define C_TESTLUCY_TESTQUERYPARSER +#define TESTLUCY_USE_SHORT_NAMES +#include "Lucy/Util/ToolSet.h" +#include <string.h> +#include <stdlib.h> + +#include "Clownfish/Boolean.h" +#include "Clownfish/TestHarness/TestBatchRunner.h" +#include "Lucy/Test.h" +#include "Lucy/Test/Search/TestQueryParserSyntax.h" +#include "Lucy/Test/Search/TestQueryParser.h" +#include "Lucy/Test/TestUtils.h" +#include "Lucy/Analysis/PolyAnalyzer.h" +#include "Lucy/Analysis/RegexTokenizer.h" +#include "Lucy/Analysis/SnowballStopFilter.h" +#include "Lucy/Document/Doc.h" +#include "Lucy/Index/Indexer.h" +#include "Lucy/Plan/FullTextType.h" +#include "Lucy/Plan/Schema.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" +#include "Lucy/Store/RAMFolder.h" + +#define make_term_query (Query*)TestUtils_make_term_query +#define make_phrase_query (Query*)TestUtils_make_phrase_query +#define make_leaf_query (Query*)TestUtils_make_leaf_query +#define make_not_query (Query*)TestUtils_make_not_query +#define make_poly_query (Query*)TestUtils_make_poly_query + +TestQueryParserSyntax* +TestQPSyntax_new() { + return (TestQueryParserSyntax*)Class_Make_Obj(TESTQUERYPARSERSYNTAX); +} + +static Folder* +build_index() { + // Plain type. + String *pattern = Str_newf("\\S+"); + RegexTokenizer *tokenizer = RegexTokenizer_new(pattern); + FullTextType *plain = FullTextType_new((Analyzer*)tokenizer); + + // Fancy type. + + String *word_pattern = Str_newf("\\w+"); + RegexTokenizer *word_tokenizer = RegexTokenizer_new(word_pattern); + + Hash *stop_list = Hash_new(0); + Hash_Store_Utf8(stop_list, "x", 1, (Obj*)CFISH_TRUE); + SnowballStopFilter *stop_filter = SnowStop_new(NULL, stop_list); + + Vector *analyzers = Vec_new(0); + Vec_Push(analyzers, (Obj*)word_tokenizer); + Vec_Push(analyzers, (Obj*)stop_filter); + PolyAnalyzer *fancy_analyzer = PolyAnalyzer_new(NULL, analyzers); + + FullTextType *fancy = FullTextType_new((Analyzer*)fancy_analyzer); + + // Schema. + Schema *schema = Schema_new(); + String *plain_str = Str_newf("plain"); + String *fancy_str = Str_newf("fancy"); + Schema_Spec_Field(schema, plain_str, (FieldType*)plain); + Schema_Spec_Field(schema, fancy_str, (FieldType*)fancy); + + // Indexer. + RAMFolder *folder = RAMFolder_new(NULL); + Indexer *indexer = Indexer_new(schema, (Obj*)folder, NULL, 0); + + // Index documents. + Vector *doc_set = TestUtils_doc_set(); + for (size_t i = 0; i < Vec_Get_Size(doc_set); ++i) { + String *content_string = (String*)Vec_Fetch(doc_set, i); + Doc *doc = Doc_new(NULL, 0); + Doc_Store(doc, plain_str, (Obj*)content_string); + Doc_Store(doc, fancy_str, (Obj*)content_string); + Indexer_Add_Doc(indexer, doc, 1.0); + DECREF(doc); + } + Indexer_Commit(indexer); + + // Clean up. + DECREF(doc_set); + DECREF(indexer); + DECREF(fancy_str); + DECREF(plain_str); + DECREF(schema); + DECREF(fancy); + DECREF(fancy_analyzer); + DECREF(analyzers); + DECREF(stop_list); + DECREF(word_pattern); + DECREF(plain); + DECREF(tokenizer); + DECREF(pattern); + + return (Folder*)folder; +} + +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_unmatched_parens() { + Query *tree = make_leaf_query(NULL, "a"); + return TestQP_new(")a)", tree, NULL, 4); +} + +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_unmatched_parens, + syntax_test_escaped_quotes_outside, + syntax_test_escaped_quotes_inside, + syntax_test_identifier_field_name, + syntax_test_double_colon, + NULL +}; + +static void +test_query_parser_syntax(TestBatchRunner *runner) { + if (!RegexTokenizer_is_available()) { + for (uint32_t i = 0; leaf_test_funcs[i] != NULL; i++) { + SKIP(runner, 3, "RegexTokenizer not available"); + } + + for (uint32_t i = 0; syntax_test_funcs[i] != NULL; i++) { + SKIP(runner, 2, "RegexTokenizer not available"); + } + + return; + } + + Folder *index = build_index(); + IndexSearcher *searcher = IxSearcher_new((Obj*)index); + QueryParser *qparser = QParser_new(IxSearcher_Get_Schema(searcher), + NULL, NULL, NULL); + QParser_Set_Heed_Colons(qparser, true); + + for (uint32_t i = 0; leaf_test_funcs[i] != NULL; i++) { + LUCY_TestQPSyntax_Test_t test_func = leaf_test_funcs[i]; + TestQueryParser *test_case = test_func(); + TestQueryParserIVARS *ivars = TestQP_IVARS(test_case); + Query *tree = QParser_Tree(qparser, ivars->query_string); + Query *expanded = QParser_Expand_Leaf(qparser, ivars->tree); + Query *parsed = QParser_Parse(qparser, ivars->query_string); + Hits *hits = IxSearcher_Hits(searcher, (Obj*)parsed, 0, 10, NULL); + char *qstr = Str_To_Utf8(ivars->query_string); + + TEST_TRUE(runner, Query_Equals(tree, (Obj*)ivars->tree), + "tree() %s", qstr); + TEST_TRUE(runner, Query_Equals(expanded, (Obj*)ivars->expanded), + "expand_leaf() %s", qstr); + TEST_INT_EQ(runner, Hits_Total_Hits(hits), ivars->num_hits, + "hits: %s", qstr); + free(qstr); + DECREF(hits); + DECREF(parsed); + DECREF(expanded); + DECREF(tree); + DECREF(test_case); + } + + for (uint32_t i = 0; syntax_test_funcs[i] != NULL; i++) { + LUCY_TestQPSyntax_Test_t test_func = syntax_test_funcs[i]; + TestQueryParser *test_case = test_func(); + TestQueryParserIVARS *ivars = TestQP_IVARS(test_case); + Query *tree = QParser_Tree(qparser, ivars->query_string); + Query *parsed = QParser_Parse(qparser, ivars->query_string); + Hits *hits = IxSearcher_Hits(searcher, (Obj*)parsed, 0, 10, NULL); + char *qstr = Str_To_Utf8(ivars->query_string); + + TEST_TRUE(runner, Query_Equals(tree, (Obj*)ivars->tree), + "tree() %s", qstr); + TEST_INT_EQ(runner, Hits_Total_Hits(hits), ivars->num_hits, + "hits: %s", qstr); + free(qstr); + DECREF(hits); + DECREF(parsed); + DECREF(tree); + DECREF(test_case); + } + + DECREF(searcher); + DECREF(qparser); + DECREF(index); +} + +void +TestQPSyntax_Run_IMP(TestQueryParserSyntax *self, TestBatchRunner *runner) { + TestBatchRunner_Plan(runner, (TestBatch*)self, 68); + test_query_parser_syntax(runner); +} + + http://git-wip-us.apache.org/repos/asf/lucy/blob/572d3564/test/Lucy/Test/Search/TestQueryParserSyntax.cfh ---------------------------------------------------------------------- diff --git a/test/Lucy/Test/Search/TestQueryParserSyntax.cfh b/test/Lucy/Test/Search/TestQueryParserSyntax.cfh new file mode 100644 index 0000000..dee071c --- /dev/null +++ b/test/Lucy/Test/Search/TestQueryParserSyntax.cfh @@ -0,0 +1,32 @@ +/* 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. + */ + +parcel TestLucy; + +/** Tests for logical structure of Query objects output by QueryParser. + */ + +class Lucy::Test::Search::TestQueryParserSyntax nickname TestQPSyntax + inherits Clownfish::TestHarness::TestBatch { + + inert incremented TestQueryParserSyntax* + new(); + + void + Run(TestQueryParserSyntax *self, TestBatchRunner *runner); +} + + http://git-wip-us.apache.org/repos/asf/lucy/blob/572d3564/test/Lucy/Test/Search/TestRangeQuery.c ---------------------------------------------------------------------- diff --git a/test/Lucy/Test/Search/TestRangeQuery.c b/test/Lucy/Test/Search/TestRangeQuery.c new file mode 100644 index 0000000..ab8a9ca --- /dev/null +++ b/test/Lucy/Test/Search/TestRangeQuery.c @@ -0,0 +1,75 @@ +/* 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_TESTLUCY_TESTRANGEQUERY +#define TESTLUCY_USE_SHORT_NAMES +#include "Lucy/Util/ToolSet.h" +#include <math.h> + +#include "Clownfish/TestHarness/TestBatchRunner.h" +#include "Lucy/Test.h" +#include "Lucy/Test/TestUtils.h" +#include "Lucy/Test/Search/TestRangeQuery.h" +#include "Lucy/Search/RangeQuery.h" + +TestRangeQuery* +TestRangeQuery_new() { + return (TestRangeQuery*)Class_Make_Obj(TESTRANGEQUERY); +} + +static void +test_Dump_Load_and_Equals(TestBatchRunner *runner) { + RangeQuery *query + = TestUtils_make_range_query("content", "foo", "phooey", true, true); + RangeQuery *lo_term_differs + = TestUtils_make_range_query("content", "goo", "phooey", true, true); + RangeQuery *hi_term_differs + = TestUtils_make_range_query("content", "foo", "gooey", true, true); + RangeQuery *include_lower_differs + = TestUtils_make_range_query("content", "foo", "phooey", false, true); + RangeQuery *include_upper_differs + = TestUtils_make_range_query("content", "foo", "phooey", true, false); + Obj *dump = (Obj*)RangeQuery_Dump(query); + RangeQuery *clone = (RangeQuery*)RangeQuery_Load(lo_term_differs, dump); + + TEST_FALSE(runner, RangeQuery_Equals(query, (Obj*)lo_term_differs), + "Equals() false with different lower term"); + TEST_FALSE(runner, RangeQuery_Equals(query, (Obj*)hi_term_differs), + "Equals() false with different upper term"); + TEST_FALSE(runner, RangeQuery_Equals(query, (Obj*)include_lower_differs), + "Equals() false with different include_lower"); + TEST_FALSE(runner, RangeQuery_Equals(query, (Obj*)include_upper_differs), + "Equals() false with different include_upper"); + TEST_TRUE(runner, RangeQuery_Equals(query, (Obj*)clone), + "Dump => Load round trip"); + + DECREF(query); + DECREF(lo_term_differs); + DECREF(hi_term_differs); + DECREF(include_lower_differs); + DECREF(include_upper_differs); + DECREF(dump); + DECREF(clone); +} + + +void +TestRangeQuery_Run_IMP(TestRangeQuery *self, TestBatchRunner *runner) { + TestBatchRunner_Plan(runner, (TestBatch*)self, 5); + test_Dump_Load_and_Equals(runner); +} + + http://git-wip-us.apache.org/repos/asf/lucy/blob/572d3564/test/Lucy/Test/Search/TestRangeQuery.cfh ---------------------------------------------------------------------- diff --git a/test/Lucy/Test/Search/TestRangeQuery.cfh b/test/Lucy/Test/Search/TestRangeQuery.cfh new file mode 100644 index 0000000..78c188c --- /dev/null +++ b/test/Lucy/Test/Search/TestRangeQuery.cfh @@ -0,0 +1,29 @@ +/* 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. + */ + +parcel TestLucy; + +class Lucy::Test::Search::TestRangeQuery + inherits Clownfish::TestHarness::TestBatch { + + inert incremented TestRangeQuery* + new(); + + void + Run(TestRangeQuery *self, TestBatchRunner *runner); +} + + http://git-wip-us.apache.org/repos/asf/lucy/blob/572d3564/test/Lucy/Test/Search/TestReqOptQuery.c ---------------------------------------------------------------------- diff --git a/test/Lucy/Test/Search/TestReqOptQuery.c b/test/Lucy/Test/Search/TestReqOptQuery.c new file mode 100644 index 0000000..404e987 --- /dev/null +++ b/test/Lucy/Test/Search/TestReqOptQuery.c @@ -0,0 +1,73 @@ +/* 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_TESTLUCY_TESTREQOPTQUERY +#define TESTLUCY_USE_SHORT_NAMES +#include "Lucy/Util/ToolSet.h" +#include <math.h> + +#include "Clownfish/TestHarness/TestBatchRunner.h" +#include "Lucy/Test.h" +#include "Lucy/Test/TestUtils.h" +#include "Lucy/Test/Search/TestReqOptQuery.h" +#include "Lucy/Search/RequiredOptionalQuery.h" +#include "Lucy/Search/LeafQuery.h" +#include "Lucy/Util/Freezer.h" + +TestReqOptQuery* +TestReqOptQuery_new() { + return (TestReqOptQuery*)Class_Make_Obj(TESTREQOPTQUERY); +} + +static void +test_Dump_Load_and_Equals(TestBatchRunner *runner) { + Query *a_leaf = (Query*)TestUtils_make_leaf_query(NULL, "a"); + Query *b_leaf = (Query*)TestUtils_make_leaf_query(NULL, "b"); + Query *c_leaf = (Query*)TestUtils_make_leaf_query(NULL, "c"); + RequiredOptionalQuery *query = ReqOptQuery_new(a_leaf, b_leaf); + RequiredOptionalQuery *kids_differ = ReqOptQuery_new(a_leaf, c_leaf); + RequiredOptionalQuery *boost_differs = ReqOptQuery_new(a_leaf, b_leaf); + Obj *dump = (Obj*)ReqOptQuery_Dump(query); + RequiredOptionalQuery *clone + = (RequiredOptionalQuery*)Freezer_load(dump); + + TEST_FALSE(runner, ReqOptQuery_Equals(query, (Obj*)kids_differ), + "Different kids spoil Equals"); + TEST_TRUE(runner, ReqOptQuery_Equals(query, (Obj*)boost_differs), + "Equals with identical boosts"); + ReqOptQuery_Set_Boost(boost_differs, 1.5); + TEST_FALSE(runner, ReqOptQuery_Equals(query, (Obj*)boost_differs), + "Different boost spoils Equals"); + TEST_TRUE(runner, ReqOptQuery_Equals(query, (Obj*)clone), + "Dump => Load round trip"); + + DECREF(a_leaf); + DECREF(b_leaf); + DECREF(c_leaf); + DECREF(query); + DECREF(kids_differ); + DECREF(boost_differs); + DECREF(dump); + DECREF(clone); +} + +void +TestReqOptQuery_Run_IMP(TestReqOptQuery *self, TestBatchRunner *runner) { + TestBatchRunner_Plan(runner, (TestBatch*)self, 4); + test_Dump_Load_and_Equals(runner); +} + + http://git-wip-us.apache.org/repos/asf/lucy/blob/572d3564/test/Lucy/Test/Search/TestReqOptQuery.cfh ---------------------------------------------------------------------- diff --git a/test/Lucy/Test/Search/TestReqOptQuery.cfh b/test/Lucy/Test/Search/TestReqOptQuery.cfh new file mode 100644 index 0000000..2df8099 --- /dev/null +++ b/test/Lucy/Test/Search/TestReqOptQuery.cfh @@ -0,0 +1,29 @@ +/* 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. + */ + +parcel TestLucy; + +class Lucy::Test::Search::TestReqOptQuery + inherits Clownfish::TestHarness::TestBatch { + + inert incremented TestReqOptQuery* + new(); + + void + Run(TestReqOptQuery *self, TestBatchRunner *runner); +} + + http://git-wip-us.apache.org/repos/asf/lucy/blob/572d3564/test/Lucy/Test/Search/TestSeriesMatcher.c ---------------------------------------------------------------------- diff --git a/test/Lucy/Test/Search/TestSeriesMatcher.c b/test/Lucy/Test/Search/TestSeriesMatcher.c new file mode 100644 index 0000000..bdd2e78 --- /dev/null +++ b/test/Lucy/Test/Search/TestSeriesMatcher.c @@ -0,0 +1,136 @@ +/* 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_TESTLUCY_TESTSERIESMATCHER +#define TESTLUCY_USE_SHORT_NAMES +#include "Lucy/Util/ToolSet.h" + +#include "Clownfish/TestHarness/TestBatchRunner.h" +#include "Lucy/Test.h" +#include "Lucy/Test/Search/TestSeriesMatcher.h" +#include "Lucy/Search/BitVecMatcher.h" +#include "Lucy/Search/SeriesMatcher.h" + +TestSeriesMatcher* +TestSeriesMatcher_new() { + return (TestSeriesMatcher*)Class_Make_Obj(TESTSERIESMATCHER); +} + +static SeriesMatcher* +S_make_series_matcher(I32Array *doc_ids, I32Array *offsets, int32_t doc_max) { + size_t num_doc_ids = I32Arr_Get_Size(doc_ids); + size_t num_matchers = I32Arr_Get_Size(offsets); + Vector *matchers = Vec_new(num_matchers); + size_t tick = 0; + + // Divvy up doc_ids by segment into BitVectors. + for (size_t i = 0; i < num_matchers; i++) { + int32_t offset = I32Arr_Get(offsets, i); + int32_t max = i == num_matchers - 1 + ? doc_max + 1 + : I32Arr_Get(offsets, i + 1); + BitVector *bit_vec = BitVec_new((size_t)(max - offset)); + while (tick < num_doc_ids) { + int32_t doc_id = I32Arr_Get(doc_ids, tick); + if (doc_id > max) { break; } + else { tick++; } + BitVec_Set(bit_vec, (size_t)(doc_id - offset)); + } + Vec_Push(matchers, (Obj*)BitVecMatcher_new(bit_vec)); + DECREF(bit_vec); + } + + SeriesMatcher *series_matcher = SeriesMatcher_new(matchers, offsets); + DECREF(matchers); + return series_matcher; +} + +static I32Array* +S_generate_match_list(int32_t first, int32_t max, int32_t doc_inc) { + int32_t count = (max - first + doc_inc - 1) / doc_inc; + int32_t *doc_ids = (int32_t*)MALLOCATE((size_t)count * sizeof(int32_t)); + int32_t doc_id = first; + int32_t i = 0; + + for (; doc_id < max; doc_id += doc_inc, i++) { + doc_ids[i] = doc_id; + } + if (i != count) { THROW(ERR, "Screwed up somehow: %i32 %i32", i, count); } + + return I32Arr_new_steal(doc_ids, (size_t)count); +} + +static void +S_do_test_matrix(TestBatchRunner *runner, int32_t doc_max, int32_t first_doc_id, + int32_t doc_inc, int32_t offset_inc) { + I32Array *doc_ids + = S_generate_match_list(first_doc_id, doc_max, doc_inc); + I32Array *offsets + = S_generate_match_list(0, doc_max, offset_inc); + SeriesMatcher *series_matcher + = S_make_series_matcher(doc_ids, offsets, doc_max); + size_t num_in_agreement = 0; + int32_t got; + + while (0 != (got = SeriesMatcher_Next(series_matcher))) { + if (got != I32Arr_Get(doc_ids, num_in_agreement)) { break; } + num_in_agreement++; + } + TEST_UINT_EQ(runner, num_in_agreement, I32Arr_Get_Size(doc_ids), + "doc_max=%d first_doc_id=%d doc_inc=%d offset_inc=%d", + doc_max, first_doc_id, doc_inc, offset_inc); + + DECREF(doc_ids); + DECREF(offsets); + DECREF(series_matcher); +} + +static void +test_matrix(TestBatchRunner *runner) { + int32_t doc_max_nums[] = { 10, 100, 1000, 0 }; + int32_t first_doc_ids[] = { 1, 2, 10, 0 }; + int32_t doc_inc_nums[] = { 20, 13, 9, 4, 2, 0 }; + int32_t offset_inc_nums[] = { 7, 29, 71, 0 }; + int32_t a, b, c, d; + + for (a = 0; doc_max_nums[a] != 0; a++) { + for (b = 0; first_doc_ids[b] != 0; b++) { + for (c = 0; doc_inc_nums[c] != 0; c++) { + for (d = 0; offset_inc_nums[d] != 0; d++) { + int32_t doc_max = doc_max_nums[a]; + int32_t first_doc_id = first_doc_ids[b]; + int32_t doc_inc = doc_inc_nums[c]; + int32_t offset_inc = offset_inc_nums[d]; + if (first_doc_id > doc_max) { + continue; + } + else { + S_do_test_matrix(runner, doc_max, first_doc_id, + doc_inc, offset_inc); + } + } + } + } + } +} + +void +TestSeriesMatcher_Run_IMP(TestSeriesMatcher *self, TestBatchRunner *runner) { + TestBatchRunner_Plan(runner, (TestBatch*)self, 135); + test_matrix(runner); +} + + http://git-wip-us.apache.org/repos/asf/lucy/blob/572d3564/test/Lucy/Test/Search/TestSeriesMatcher.cfh ---------------------------------------------------------------------- diff --git a/test/Lucy/Test/Search/TestSeriesMatcher.cfh b/test/Lucy/Test/Search/TestSeriesMatcher.cfh new file mode 100644 index 0000000..fa89085 --- /dev/null +++ b/test/Lucy/Test/Search/TestSeriesMatcher.cfh @@ -0,0 +1,29 @@ +/* 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. + */ + +parcel TestLucy; + +class Lucy::Test::Search::TestSeriesMatcher + inherits Clownfish::TestHarness::TestBatch { + + inert incremented TestSeriesMatcher* + new(); + + void + Run(TestSeriesMatcher *self, TestBatchRunner *runner); +} + +