diff options
Diffstat (limited to 'searchlib/src/tests')
47 files changed, 1762 insertions, 1041 deletions
diff --git a/searchlib/src/tests/attribute/bitvector_search_cache/bitvector_search_cache_test.cpp b/searchlib/src/tests/attribute/bitvector_search_cache/bitvector_search_cache_test.cpp index d51ec22a54a..1d66eefaff7 100644 --- a/searchlib/src/tests/attribute/bitvector_search_cache/bitvector_search_cache_test.cpp +++ b/searchlib/src/tests/attribute/bitvector_search_cache/bitvector_search_cache_test.cpp @@ -3,6 +3,7 @@ #include <vespa/vespalib/testkit/test_kit.h> #include <vespa/searchlib/attribute/bitvector_search_cache.h> #include <vespa/searchlib/common/bitvector.h> +#include <vespa/vespalib/util/memoryusage.h> using namespace search; using namespace search::attribute; @@ -31,9 +32,13 @@ struct Fixture { TEST_F("require that bit vectors can be inserted and retrieved", Fixture) { EXPECT_EQUAL(0u, f.cache.size()); + auto old_mem_usage = f.cache.get_memory_usage(); f.cache.insert("foo", f.entry1); f.cache.insert("bar", f.entry2); EXPECT_EQUAL(2u, f.cache.size()); + auto new_mem_usage = f.cache.get_memory_usage(); + EXPECT_LESS(old_mem_usage.usedBytes(), new_mem_usage.usedBytes()); + EXPECT_LESS(old_mem_usage.allocatedBytes(), new_mem_usage.allocatedBytes()); EXPECT_EQUAL(f.entry1, f.cache.find("foo")); EXPECT_EQUAL(f.entry2, f.cache.find("bar")); @@ -43,9 +48,13 @@ TEST_F("require that bit vectors can be inserted and retrieved", Fixture) TEST_F("require that insert() doesn't replace existing bit vector", Fixture) { f.cache.insert("foo", f.entry1); + auto old_mem_usage = f.cache.get_memory_usage(); f.cache.insert("foo", f.entry2); + auto new_mem_usage = f.cache.get_memory_usage(); EXPECT_EQUAL(1u, f.cache.size()); EXPECT_EQUAL(f.entry1, f.cache.find("foo")); + EXPECT_EQUAL(old_mem_usage.usedBytes(), new_mem_usage.usedBytes()); + EXPECT_EQUAL(old_mem_usage.allocatedBytes(), new_mem_usage.allocatedBytes()); } TEST_F("require that cache can be cleared", Fixture) @@ -53,11 +62,15 @@ TEST_F("require that cache can be cleared", Fixture) f.cache.insert("foo", f.entry1); f.cache.insert("bar", f.entry2); EXPECT_EQUAL(2u, f.cache.size()); + auto old_mem_usage = f.cache.get_memory_usage(); f.cache.clear(); + auto new_mem_usage = f.cache.get_memory_usage(); EXPECT_EQUAL(0u, f.cache.size()); EXPECT_TRUE(f.cache.find("foo").get() == nullptr); EXPECT_TRUE(f.cache.find("bar").get() == nullptr); + EXPECT_GREATER(old_mem_usage.usedBytes(), new_mem_usage.usedBytes()); + EXPECT_GREATER(old_mem_usage.allocatedBytes(), new_mem_usage.allocatedBytes()); } TEST_MAIN() { TEST_RUN_ALL(); } diff --git a/searchlib/src/tests/attribute/imported_search_context/imported_search_context_test.cpp b/searchlib/src/tests/attribute/imported_search_context/imported_search_context_test.cpp index 41ec377dece..7c38c322bc8 100644 --- a/searchlib/src/tests/attribute/imported_search_context/imported_search_context_test.cpp +++ b/searchlib/src/tests/attribute/imported_search_context/imported_search_context_test.cpp @@ -508,6 +508,7 @@ assertBitVector(const std::vector<uint32_t> &expDocIds, const BitVector &bitVect TEST_F("Entry is inserted into search cache if bit vector posting list is used", SearchCacheFixture) { EXPECT_EQUAL(0u, f.imported_attr->getSearchCache()->size()); + auto old_mem_usage = f.imported_attr->get_memory_usage(); auto ctx = f.create_context(word_term("5678")); ctx->fetchPostings(queryeval::ExecuteInfo::FULL, true); TermFieldMatchData match; @@ -515,6 +516,9 @@ TEST_F("Entry is inserted into search cache if bit vector posting list is used", TEST_DO(f.assertSearch({3, 5}, *iter)); EXPECT_EQUAL(1u, f.imported_attr->getSearchCache()->size()); + auto new_mem_usage = f.imported_attr->get_memory_usage(); + EXPECT_LESS(old_mem_usage.usedBytes(), new_mem_usage.usedBytes()); + EXPECT_LESS(old_mem_usage.allocatedBytes(), new_mem_usage.allocatedBytes()); auto cacheEntry = f.imported_attr->getSearchCache()->find("5678"); EXPECT_EQUAL(cacheEntry->docIdLimit, f.get_imported_attr()->getNumDocs()); TEST_DO(assertBitVector({3, 5}, *cacheEntry->bitVector)); diff --git a/searchlib/src/tests/attribute/tensorattribute/tensorattribute_test.cpp b/searchlib/src/tests/attribute/tensorattribute/tensorattribute_test.cpp index b1b2235165f..cce72837dad 100644 --- a/searchlib/src/tests/attribute/tensorattribute/tensorattribute_test.cpp +++ b/searchlib/src/tests/attribute/tensorattribute/tensorattribute_test.cpp @@ -210,8 +210,10 @@ public: } void expect_entry(uint32_t exp_docid, const DoubleVector& exp_vector, const EntryVector& entries) const { EXPECT_EQUAL(1u, entries.size()); - EXPECT_EQUAL(exp_docid, entries.back().first); - EXPECT_EQUAL(exp_vector, entries.back().second); + if (entries.size() >= 1u) { + EXPECT_EQUAL(exp_docid, entries.back().first); + EXPECT_EQUAL(exp_vector, entries.back().second); + } } void expect_add(uint32_t exp_docid, const DoubleVector& exp_vector) const { expect_entry(exp_docid, exp_vector, _adds); @@ -329,6 +331,10 @@ public: static search::tensor::DistanceFunctionFactory::UP my_dist_fun = search::tensor::make_distance_function_factory(search::attribute::DistanceMetric::Euclidean, vespalib::eval::CellType::DOUBLE); return *my_dist_fun; } + + uint32_t check_consistency(uint32_t) const noexcept override { + return 0; + } }; class MockNearestNeighborIndexFactory : public NearestNeighborIndexFactory { @@ -1077,8 +1083,22 @@ TEST_F("Populates address space usage in mixed tensor attribute with hnsw index" class DenseTensorAttributeMockIndex : public Fixture { public: DenseTensorAttributeMockIndex() : Fixture(vec_2d_spec, FixtureTraits().mock_hnsw()) {} + void add_vec_a(); }; +void +DenseTensorAttributeMockIndex::add_vec_a() +{ + auto& index = mock_index(); + auto vec_a = vec_2d(3, 5); + auto prepare_result = prepare_set_tensor(1, vec_a); + index.expect_prepare_add(1, {3, 5}); + complete_set_tensor(1, vec_a, std::move(prepare_result)); + assertGetTensor(vec_a, 1); + index.expect_complete_add(1, {3, 5}); + index.clear(); +} + TEST_F("setTensor() updates nearest neighbor index", DenseTensorAttributeMockIndex) { auto& index = f.mock_index(); @@ -1097,15 +1117,7 @@ TEST_F("setTensor() updates nearest neighbor index", DenseTensorAttributeMockInd TEST_F("nearest neighbor index can be updated in two phases", DenseTensorAttributeMockIndex) { auto& index = f.mock_index(); - { - auto vec_a = vec_2d(3, 5); - auto prepare_result = f.prepare_set_tensor(1, vec_a); - index.expect_prepare_add(1, {3, 5}); - f.complete_set_tensor(1, vec_a, std::move(prepare_result)); - f.assertGetTensor(vec_a, 1); - index.expect_complete_add(1, {3, 5}); - } - index.clear(); + f.add_vec_a(); { // Replaces previous value. auto vec_b = vec_2d(7, 9); @@ -1121,15 +1133,7 @@ TEST_F("nearest neighbor index can be updated in two phases", DenseTensorAttribu TEST_F("nearest neighbor index is NOT updated when tensor value is unchanged", DenseTensorAttributeMockIndex) { auto& index = f.mock_index(); - { - auto vec_a = vec_2d(3, 5); - auto prepare_result = f.prepare_set_tensor(1, vec_a); - index.expect_prepare_add(1, {3, 5}); - f.complete_set_tensor(1, vec_a, std::move(prepare_result)); - f.assertGetTensor(vec_a, 1); - index.expect_complete_add(1, {3, 5}); - } - index.clear(); + f.add_vec_a(); { // Replaces previous value with the same value auto vec_b = vec_2d(3, 5); @@ -1139,6 +1143,39 @@ TEST_F("nearest neighbor index is NOT updated when tensor value is unchanged", D f.complete_set_tensor(1, vec_b, std::move(prepare_result)); f.assertGetTensor(vec_b, 1); index.expect_empty_complete_add(); + index.expect_empty_add(); + } +} + +TEST_F("nearest neighbor index is updated when value changes from A to B to A", DenseTensorAttributeMockIndex) +{ + auto& index = f.mock_index(); + f.add_vec_a(); + { + // Prepare replace of A with B + auto vec_b = vec_2d(7, 9); + auto prepare_result_b = f.prepare_set_tensor(1, vec_b); + index.expect_prepare_add(1, {7, 9}); + index.clear(); + // Prepare replace of B with A, but prepare sees original A + auto vec_a = vec_2d(3, 5); + auto prepare_result_a = f.prepare_set_tensor(1, vec_a); + EXPECT_TRUE(prepare_result_a.get() == nullptr); + index.expect_empty_prepare_add(); + index.clear(); + // Complete set B + f.complete_set_tensor(1, vec_b, std::move(prepare_result_b)); + index.expect_remove(1, {3, 5}); + f.assertGetTensor(vec_b, 1); + index.expect_complete_add(1, {7, 9}); + index.expect_empty_add(); + index.clear(); + // Complete set A, no prepare result but tensor cells changed + f.complete_set_tensor(1, vec_a, std::move(prepare_result_a)); + index.expect_remove(1, {7, 9}); + index.expect_empty_complete_add(); + index.expect_add(1, {3, 5}); + f.assertGetTensor(vec_a, 1); } } diff --git a/searchlib/src/tests/features/element_similarity_feature/CMakeLists.txt b/searchlib/src/tests/features/element_similarity_feature/CMakeLists.txt index 748556b0fcd..7703d9be0c6 100644 --- a/searchlib/src/tests/features/element_similarity_feature/CMakeLists.txt +++ b/searchlib/src/tests/features/element_similarity_feature/CMakeLists.txt @@ -6,4 +6,4 @@ vespa_add_executable(searchlib_element_similarity_feature_test_app TEST searchlib searchlib_test ) -vespa_add_test(NAME searchlib_element_similarity_feature_test_app COMMAND searchlib_element_similarity_feature_test_app) +vespa_add_test(NAME searchlib_element_similarity_feature_test_app COMMAND searchlib_element_similarity_feature_test_app COST 50) diff --git a/searchlib/src/tests/features/euclidean_distance/euclidean_distance_test.cpp b/searchlib/src/tests/features/euclidean_distance/euclidean_distance_test.cpp index e7523288e8f..82e227e0d35 100644 --- a/searchlib/src/tests/features/euclidean_distance/euclidean_distance_test.cpp +++ b/searchlib/src/tests/features/euclidean_distance/euclidean_distance_test.cpp @@ -1,5 +1,4 @@ // Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -#include <vespa/vespalib/testkit/test_kit.h> #include <vespa/searchcommon/attribute/config.h> #include <vespa/searchlib/attribute/attributefactory.h> diff --git a/searchlib/src/tests/features/first_phase_rank/CMakeLists.txt b/searchlib/src/tests/features/first_phase_rank/CMakeLists.txt new file mode 100644 index 00000000000..5aa83399d3d --- /dev/null +++ b/searchlib/src/tests/features/first_phase_rank/CMakeLists.txt @@ -0,0 +1,11 @@ +# Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +vespa_add_executable(searchlib_features_first_phase_rank_test_app TEST + SOURCES + first_phase_rank_test.cpp + DEPENDS + searchlib + searchlib_test + GTest::GTest +) +vespa_add_test(NAME searchlib_features_first_phase_rank_test_app COMMAND searchlib_features_first_phase_rank_test_app) diff --git a/searchlib/src/tests/features/first_phase_rank/first_phase_rank_test.cpp b/searchlib/src/tests/features/first_phase_rank/first_phase_rank_test.cpp new file mode 100644 index 00000000000..01ba6c36124 --- /dev/null +++ b/searchlib/src/tests/features/first_phase_rank/first_phase_rank_test.cpp @@ -0,0 +1,143 @@ +// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#include <vespa/searchlib/features/first_phase_rank_feature.h> +#include <vespa/searchlib/features/setup.h> +#include <vespa/searchlib/fef/blueprintfactory.h> +#include <vespa/searchlib/fef/test/dummy_dependency_handler.h> +#include <vespa/searchlib/fef/test/indexenvironment.h> +#include <vespa/searchlib/fef/test/indexenvironmentbuilder.h> +#define ENABLE_GTEST_MIGRATION +#include <vespa/searchlib/test/ft_test_app_base.h> +#include <vespa/vespalib/gtest/gtest.h> + +using search::features::FirstPhaseRankBlueprint; +using search::features::FirstPhaseRankLookup; +using search::features::setup_search_features; +using search::fef::Blueprint; +using search::fef::BlueprintFactory; +using search::fef::ObjectStore; +using search::fef::test::IndexEnvironment; +using search::fef::test::DummyDependencyHandler; +using StringVector = std::vector<vespalib::string>; + +constexpr feature_t unranked = std::numeric_limits<feature_t>::max(); + +struct FirstPhaseRankBlueprintTest : public ::testing::Test { + BlueprintFactory factory; + IndexEnvironment index_env; + + FirstPhaseRankBlueprintTest() + : ::testing::Test(), + factory(), + index_env() + { + setup_search_features(factory); + } + + ~FirstPhaseRankBlueprintTest() override; + + std::shared_ptr<Blueprint> make_blueprint() const { + return factory.createBlueprint("firstPhaseRank"); + } + + void expect_setup_fail(const StringVector& params, const vespalib::string& exp_fail_msg) { + auto blueprint = make_blueprint(); + DummyDependencyHandler deps(*blueprint); + EXPECT_FALSE(blueprint->setup(index_env, params)); + EXPECT_EQ(exp_fail_msg, deps.fail_msg); + } + + std::shared_ptr<Blueprint> expect_setup_succeed(const StringVector& params) { + auto blueprint = make_blueprint(); + DummyDependencyHandler deps(*blueprint); + EXPECT_TRUE(blueprint->setup(index_env, params)); + EXPECT_EQ(0, deps.input.size()); + EXPECT_EQ(StringVector({"score"}), deps.output); + return blueprint; + } +}; + +FirstPhaseRankBlueprintTest::~FirstPhaseRankBlueprintTest() = default; + +TEST_F(FirstPhaseRankBlueprintTest, blueprint_can_be_created_from_factory) +{ + auto bp = make_blueprint(); + EXPECT_TRUE(bp); + EXPECT_TRUE(dynamic_pointer_cast<FirstPhaseRankBlueprint>(bp)); +} + +TEST_F(FirstPhaseRankBlueprintTest, blueprint_setup_fails_when_parameter_list_is_not_empty) +{ + expect_setup_fail({"is"}, + "The parameter list used for setting up rank feature firstPhaseRank is not valid: " + "Expected 0 parameter(s), but got 1"); +} + +TEST_F(FirstPhaseRankBlueprintTest, blueprint_setup_succeeds) +{ + expect_setup_succeed({}); +} + +TEST_F(FirstPhaseRankBlueprintTest, blueprint_can_prepare_shared_state) +{ + auto blueprint = expect_setup_succeed({}); + search::fef::test::QueryEnvironment query_env; + ObjectStore store; + EXPECT_EQ(nullptr, FirstPhaseRankLookup::get_mutable_shared_state(store)); + EXPECT_EQ(nullptr, FirstPhaseRankLookup::get_shared_state(store)); + blueprint->prepareSharedState(query_env, store); + EXPECT_NE(nullptr, FirstPhaseRankLookup::get_mutable_shared_state(store)); + EXPECT_NE(nullptr, FirstPhaseRankLookup::get_shared_state(store)); +} + +TEST_F(FirstPhaseRankBlueprintTest, dump_features) +{ + FtTestAppBase::FT_DUMP_EMPTY(factory, "firstPhaseRank", index_env); +} + +struct FirstPhaseRankExecutorTest : public ::testing::Test { + BlueprintFactory factory; + FtFeatureTest test; + + FirstPhaseRankExecutorTest() + : ::testing::Test(), + factory(), + test(factory, "firstPhaseRank") + { + setup_search_features(factory); + } + ~FirstPhaseRankExecutorTest() override; + void setup(std::vector<std::pair<uint32_t,uint32_t>> ranks) { + EXPECT_TRUE(test.setup()); + auto* lookup = FirstPhaseRankLookup::get_mutable_shared_state(test.getQueryEnv().getObjectStore()); + ASSERT_NE(nullptr, lookup); + for (auto& entry : ranks) { + lookup->add(entry.first, entry.second); + } + } + bool execute(feature_t exp_score, uint32_t docid) { + return test.execute(exp_score, 0.000001, docid); + } +}; + +FirstPhaseRankExecutorTest::~FirstPhaseRankExecutorTest() = default; + +TEST_F(FirstPhaseRankExecutorTest, unranked_docid_gives_huge_output) +{ + setup({}); + EXPECT_TRUE(execute(unranked, 1)); +} + +TEST_F(FirstPhaseRankExecutorTest, ranked_docid_gives_expected_output) +{ + setup({{3, 5}, {7, 4}}); + EXPECT_TRUE(execute(unranked, 2)); + EXPECT_TRUE(execute(5, 3)); + EXPECT_TRUE(execute(unranked, 4)); + EXPECT_TRUE(execute(unranked, 5)); + EXPECT_TRUE(execute(unranked, 6)); + EXPECT_TRUE(execute(4, 7)); + EXPECT_TRUE(execute(unranked, 8)); +} + +GTEST_MAIN_RUN_ALL_TESTS() diff --git a/searchlib/src/tests/features/prod_features_test.cpp b/searchlib/src/tests/features/prod_features_test.cpp index 22105533895..fb00b4ff5e6 100644 --- a/searchlib/src/tests/features/prod_features_test.cpp +++ b/searchlib/src/tests/features/prod_features_test.cpp @@ -33,6 +33,7 @@ #include <vespa/searchlib/features/random_normal_stable_feature.h> #include <vespa/searchlib/features/randomfeature.h> #include <vespa/searchlib/features/rankingexpressionfeature.h> +#include <vespa/searchlib/features/second_phase_feature.h> #include <vespa/searchlib/features/setup.h> #include <vespa/searchlib/features/termfeature.h> #include <vespa/searchlib/features/utils.h> @@ -614,6 +615,32 @@ TEST_F(ProdFeaturesTest, test_first_phase) } } +TEST_F(ProdFeaturesTest, test_second_phase) +{ + { // Test blueprint. + SecondPhaseBlueprint pt; + + EXPECT_TRUE(assertCreateInstance(pt, "secondPhase")); + + FtIndexEnvironment ie; + ie.getProperties().add(indexproperties::rank::SecondPhase::NAME, "random"); + + StringList params, in, out; + FT_SETUP_OK(pt, ie, params, in.add("random"), out.add("score")); + FT_SETUP_FAIL(pt, params.add("foo")); + params.clear(); + + FT_DUMP_EMPTY(_factory, "secondPhase", ie); + } + + { // Test executor. + FtFeatureTest ft(_factory, "secondPhase"); + ft.getIndexEnv().getProperties().add(indexproperties::rank::SecondPhase::NAME, "value(11)"); + ASSERT_TRUE(ft.setup()); + ASSERT_TRUE(ft.execute(11.0f)); + } +} + TEST_F(ProdFeaturesTest, test_foreach) { { // Test blueprint. diff --git a/searchlib/src/tests/fef/phrasesplitter/benchmark.cpp b/searchlib/src/tests/fef/phrasesplitter/benchmark.cpp index 93a5a01262d..6be252380da 100644 --- a/searchlib/src/tests/fef/phrasesplitter/benchmark.cpp +++ b/searchlib/src/tests/fef/phrasesplitter/benchmark.cpp @@ -1,18 +1,16 @@ // Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -#include <vespa/vespalib/testkit/testapp.h> + #include <vespa/searchlib/fef/matchdatalayout.h> #include <vespa/searchlib/fef/phrasesplitter.h> #include <vespa/searchlib/fef/phrase_splitter_query_env.h> #include <vespa/searchlib/fef/test/queryenvironment.h> +#include <vespa/vespalib/util/time.h> #include <iomanip> #include <iostream> -#include <vespa/log/log.h> -LOG_SETUP("phrasesplitter_test"); - namespace search::fef { -class Benchmark : public vespalib::TestApp +class Benchmark { private: vespalib::Timer _timer; @@ -20,12 +18,16 @@ private: void start() { _timer = vespalib::Timer(); } void sample() { _sample = _timer.elapsed(); } - void run(size_t numRuns, size_t numPositions); public: - Benchmark() : _timer(), _sample(0) {} - ~Benchmark() override; - int Main() override; + Benchmark() + : _timer(), + _sample(0) + { + } + ~Benchmark(); + void run(size_t numRuns, size_t numPositions); + vespalib::duration get_sample() const noexcept { return _sample; } }; Benchmark::~Benchmark() = default; @@ -61,28 +63,24 @@ Benchmark::run(size_t numRuns, size_t numPositions) sample(); } +} + int -Benchmark::Main() +main(int argc, char* argv[]) { - - TEST_INIT("benchmark"); - - if (_argc != 3) { + if (argc != 3) { std::cout << "Must specify <numRuns> and <numPositions>" << std::endl; return 0; } - size_t numRuns = strtoull(_argv[1], nullptr, 10); - size_t numPositions = strtoull(_argv[2], nullptr, 10); + size_t numRuns = strtoull(argv[1], nullptr, 10); + size_t numPositions = strtoull(argv[2], nullptr, 10); - run(numRuns, numPositions); + auto app = std::make_unique<search::fef::Benchmark>(); + app->run(numRuns, numPositions); + auto sample = app->get_sample(); - std::cout << "TET: " << vespalib::count_ms(_sample) << " (ms)" << std::endl; - std::cout << "ETPD: " << std::fixed << std::setprecision(10) << (vespalib::count_ns(_sample) / (numRuns * 1000000.0)) << " (ms)" << std::endl; + std::cout << "TET: " << vespalib::count_ms(sample) << " (ms)" << std::endl; + std::cout << "ETPD: " << std::fixed << std::setprecision(10) << (vespalib::count_ns(sample) / (numRuns * 1000000.0)) << " (ms)" << std::endl; - TEST_DONE(); } - -} - -TEST_APPHOOK(search::fef::Benchmark); diff --git a/searchlib/src/tests/fef/properties/CMakeLists.txt b/searchlib/src/tests/fef/properties/CMakeLists.txt index dd1eb83b0c2..d0abf5bd377 100644 --- a/searchlib/src/tests/fef/properties/CMakeLists.txt +++ b/searchlib/src/tests/fef/properties/CMakeLists.txt @@ -4,5 +4,6 @@ vespa_add_executable(searchlib_properties_test_app TEST properties_test.cpp DEPENDS searchlib + GTest::gtest ) vespa_add_test(NAME searchlib_properties_test_app COMMAND searchlib_properties_test_app) diff --git a/searchlib/src/tests/fef/properties/properties_test.cpp b/searchlib/src/tests/fef/properties/properties_test.cpp index c8073739b3e..80a7b64a2e0 100644 --- a/searchlib/src/tests/fef/properties/properties_test.cpp +++ b/searchlib/src/tests/fef/properties/properties_test.cpp @@ -1,7 +1,7 @@ // Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -#include <vespa/vespalib/testkit/test_kit.h> #include <vespa/searchlib/fef/indexproperties.h> #include <vespa/searchlib/fef/properties.h> +#include <vespa/vespalib/gtest/gtest.h> #include <limits> using namespace search::fef; @@ -30,7 +30,8 @@ Properties make_props(std::initializer_list<std::pair<const char *, std::initial return props; } -TEST("require that namespace visitation works") { +TEST(PropertiesTest, require_that_namespace_visitation_works) +{ Properties props = make_props({ {"foo", {"outside"}}, {"foo.a", {"a_value"}}, {"foo.b", {"b_value"}}, @@ -39,20 +40,21 @@ TEST("require that namespace visitation works") { Properties result; CopyVisitor copy_visitor(result); props.visitNamespace("foo", copy_visitor); - EXPECT_EQUAL(2u, result.numKeys()); - EXPECT_EQUAL(result.lookup("a").get(), Property::Value("a_value")); - EXPECT_EQUAL(result.lookup("b").get(), Property::Value("b_value")); + EXPECT_EQ(2u, result.numKeys()); + EXPECT_EQ(result.lookup("a").get(), Property::Value("a_value")); + EXPECT_EQ(result.lookup("b").get(), Property::Value("b_value")); } -TEST("test stuff") { +TEST(PropertiesTest, test_stuff) +{ { // empty lookup result Property p; - EXPECT_EQUAL(p.found(), false); - EXPECT_EQUAL(p.get(), Property::Value("")); - EXPECT_EQUAL(p.get("fb"), Property::Value("fb")); - EXPECT_EQUAL(p.size(), 0u); - EXPECT_EQUAL(p.getAt(0), Property::Value("")); + EXPECT_EQ(p.found(), false); + EXPECT_EQ(p.get(), Property::Value("")); + EXPECT_EQ(p.get("fb"), Property::Value("fb")); + EXPECT_EQ(p.size(), 0u); + EXPECT_EQ(p.getAt(0), Property::Value("")); } { // add / count / remove Properties p = make_props({ {"a", {"a1", "a2", "a3"}}, @@ -61,48 +63,48 @@ TEST("test stuff") { }); const Properties &pc = p; - EXPECT_EQUAL(pc.numKeys(), 3u); - EXPECT_EQUAL(pc.numValues(), 6u); - EXPECT_EQUAL(pc.count("a"), 3u); - EXPECT_EQUAL(pc.count("b"), 2u); - EXPECT_EQUAL(pc.count("c"), 1u); - EXPECT_EQUAL(pc.count("d"), 0u); + EXPECT_EQ(pc.numKeys(), 3u); + EXPECT_EQ(pc.numValues(), 6u); + EXPECT_EQ(pc.count("a"), 3u); + EXPECT_EQ(pc.count("b"), 2u); + EXPECT_EQ(pc.count("c"), 1u); + EXPECT_EQ(pc.count("d"), 0u); p.remove("d"); - EXPECT_EQUAL(pc.numKeys(), 3u); - EXPECT_EQUAL(pc.numValues(), 6u); - EXPECT_EQUAL(pc.count("a"), 3u); - EXPECT_EQUAL(pc.count("b"), 2u); - EXPECT_EQUAL(pc.count("c"), 1u); - EXPECT_EQUAL(pc.count("d"), 0u); + EXPECT_EQ(pc.numKeys(), 3u); + EXPECT_EQ(pc.numValues(), 6u); + EXPECT_EQ(pc.count("a"), 3u); + EXPECT_EQ(pc.count("b"), 2u); + EXPECT_EQ(pc.count("c"), 1u); + EXPECT_EQ(pc.count("d"), 0u); p.remove("c"); - EXPECT_EQUAL(pc.numKeys(), 2u); - EXPECT_EQUAL(pc.numValues(), 5u); - EXPECT_EQUAL(pc.count("a"), 3u); - EXPECT_EQUAL(pc.count("b"), 2u); - EXPECT_EQUAL(pc.count("c"), 0u); - EXPECT_EQUAL(pc.count("d"), 0u); + EXPECT_EQ(pc.numKeys(), 2u); + EXPECT_EQ(pc.numValues(), 5u); + EXPECT_EQ(pc.count("a"), 3u); + EXPECT_EQ(pc.count("b"), 2u); + EXPECT_EQ(pc.count("c"), 0u); + EXPECT_EQ(pc.count("d"), 0u); p.remove("b"); - EXPECT_EQUAL(pc.numKeys(), 1u); - EXPECT_EQUAL(pc.numValues(), 3u); - EXPECT_EQUAL(pc.count("a"), 3u); - EXPECT_EQUAL(pc.count("b"), 0u); - EXPECT_EQUAL(pc.count("c"), 0u); - EXPECT_EQUAL(pc.count("d"), 0u); + EXPECT_EQ(pc.numKeys(), 1u); + EXPECT_EQ(pc.numValues(), 3u); + EXPECT_EQ(pc.count("a"), 3u); + EXPECT_EQ(pc.count("b"), 0u); + EXPECT_EQ(pc.count("c"), 0u); + EXPECT_EQ(pc.count("d"), 0u); p.remove("a"); - EXPECT_EQUAL(pc.numKeys(), 0u); - EXPECT_EQUAL(pc.numValues(), 0u); - EXPECT_EQUAL(pc.count("a"), 0u); - EXPECT_EQUAL(pc.count("b"), 0u); - EXPECT_EQUAL(pc.count("c"), 0u); - EXPECT_EQUAL(pc.count("d"), 0u); + EXPECT_EQ(pc.numKeys(), 0u); + EXPECT_EQ(pc.numValues(), 0u); + EXPECT_EQ(pc.count("a"), 0u); + EXPECT_EQ(pc.count("b"), 0u); + EXPECT_EQ(pc.count("c"), 0u); + EXPECT_EQ(pc.count("d"), 0u); } { // lookup / import / visit / compare / hash Properties p; @@ -114,38 +116,38 @@ TEST("test stuff") { p.add("list", "e1").add("list", "e2").add("list", "e3"); - EXPECT_EQUAL(p.numKeys(), 5u); - EXPECT_EQUAL(p.numValues(), 7u); - - EXPECT_EQUAL(p.lookup("x").found(), true); - EXPECT_EQUAL(p.lookup("a.x").found(), true); - EXPECT_EQUAL(p.lookup("a.b.x").found(), true); - EXPECT_EQUAL(p.lookup("a.b.c.x").found(), true); - EXPECT_EQUAL(p.lookup("list").found(), true); - EXPECT_EQUAL(p.lookup("y").found(), false); - - EXPECT_EQUAL(p.lookup("x").get(), Property::Value("x1")); - EXPECT_EQUAL(p.lookup("a.x").get(), Property::Value("x2")); - EXPECT_EQUAL(p.lookup("a.b.x").get(), Property::Value("x3")); - EXPECT_EQUAL(p.lookup("a.b.c.x").get(), Property::Value("x4")); - EXPECT_EQUAL(p.lookup("list").get(), Property::Value("e1")); - EXPECT_EQUAL(p.lookup("y").get(), Property::Value("")); - - EXPECT_EQUAL(p.lookup("x").get(), Property::Value("x1")); - EXPECT_EQUAL(p.lookup("a", "x").get(), Property::Value("x2")); - EXPECT_EQUAL(p.lookup("a", "b", "x").get(), Property::Value("x3")); - EXPECT_EQUAL(p.lookup("a", "b", "c", "x").get(), Property::Value("x4")); - - EXPECT_EQUAL(p.lookup("x").get("fallback"), Property::Value("x1")); - EXPECT_EQUAL(p.lookup("y").get("fallback"), Property::Value("fallback")); - - EXPECT_EQUAL(p.lookup("y").size(), 0u); - EXPECT_EQUAL(p.lookup("x").size(), 1u); - EXPECT_EQUAL(p.lookup("list").size(), 3u); - EXPECT_EQUAL(p.lookup("list").getAt(0), Property::Value("e1")); - EXPECT_EQUAL(p.lookup("list").getAt(1), Property::Value("e2")); - EXPECT_EQUAL(p.lookup("list").getAt(2), Property::Value("e3")); - EXPECT_EQUAL(p.lookup("list").getAt(3), Property::Value("")); + EXPECT_EQ(p.numKeys(), 5u); + EXPECT_EQ(p.numValues(), 7u); + + EXPECT_EQ(p.lookup("x").found(), true); + EXPECT_EQ(p.lookup("a.x").found(), true); + EXPECT_EQ(p.lookup("a.b.x").found(), true); + EXPECT_EQ(p.lookup("a.b.c.x").found(), true); + EXPECT_EQ(p.lookup("list").found(), true); + EXPECT_EQ(p.lookup("y").found(), false); + + EXPECT_EQ(p.lookup("x").get(), Property::Value("x1")); + EXPECT_EQ(p.lookup("a.x").get(), Property::Value("x2")); + EXPECT_EQ(p.lookup("a.b.x").get(), Property::Value("x3")); + EXPECT_EQ(p.lookup("a.b.c.x").get(), Property::Value("x4")); + EXPECT_EQ(p.lookup("list").get(), Property::Value("e1")); + EXPECT_EQ(p.lookup("y").get(), Property::Value("")); + + EXPECT_EQ(p.lookup("x").get(), Property::Value("x1")); + EXPECT_EQ(p.lookup("a", "x").get(), Property::Value("x2")); + EXPECT_EQ(p.lookup("a", "b", "x").get(), Property::Value("x3")); + EXPECT_EQ(p.lookup("a", "b", "c", "x").get(), Property::Value("x4")); + + EXPECT_EQ(p.lookup("x").get("fallback"), Property::Value("x1")); + EXPECT_EQ(p.lookup("y").get("fallback"), Property::Value("fallback")); + + EXPECT_EQ(p.lookup("y").size(), 0u); + EXPECT_EQ(p.lookup("x").size(), 1u); + EXPECT_EQ(p.lookup("list").size(), 3u); + EXPECT_EQ(p.lookup("list").getAt(0), Property::Value("e1")); + EXPECT_EQ(p.lookup("list").getAt(1), Property::Value("e2")); + EXPECT_EQ(p.lookup("list").getAt(2), Property::Value("e3")); + EXPECT_EQ(p.lookup("list").getAt(3), Property::Value("")); Properties p2; @@ -153,29 +155,29 @@ TEST("test stuff") { p2.add("y", "y1"); p2.add("list", "foo").add("list", "bar"); - EXPECT_EQUAL(p2.numKeys(), 3u); - EXPECT_EQUAL(p2.numValues(), 4u); + EXPECT_EQ(p2.numKeys(), 3u); + EXPECT_EQ(p2.numValues(), 4u); p.import(p2); - EXPECT_EQUAL(p.numKeys(), 6u); - EXPECT_EQUAL(p.numValues(), 7u); + EXPECT_EQ(p.numKeys(), 6u); + EXPECT_EQ(p.numValues(), 7u); - EXPECT_EQUAL(p.lookup("y").size(), 1u); - EXPECT_EQUAL(p.lookup("y").get(), Property::Value("y1")); + EXPECT_EQ(p.lookup("y").size(), 1u); + EXPECT_EQ(p.lookup("y").get(), Property::Value("y1")); - EXPECT_EQUAL(p.lookup("x").size(), 1u); - EXPECT_EQUAL(p.lookup("x").get(), Property::Value("new_x")); + EXPECT_EQ(p.lookup("x").size(), 1u); + EXPECT_EQ(p.lookup("x").get(), Property::Value("new_x")); - EXPECT_EQUAL(p.lookup("z").size(), 0u); + EXPECT_EQ(p.lookup("z").size(), 0u); - EXPECT_EQUAL(p.lookup("a", "x").size(), 1u); - EXPECT_EQUAL(p.lookup("a", "x").get(), Property::Value("x2")); + EXPECT_EQ(p.lookup("a", "x").size(), 1u); + EXPECT_EQ(p.lookup("a", "x").get(), Property::Value("x2")); - EXPECT_EQUAL(p.lookup("list").size(), 2u); - EXPECT_EQUAL(p.lookup("list").getAt(0), Property::Value("foo")); - EXPECT_EQUAL(p.lookup("list").getAt(1), Property::Value("bar")); - EXPECT_EQUAL(p.lookup("list").getAt(2), Property::Value("")); + EXPECT_EQ(p.lookup("list").size(), 2u); + EXPECT_EQ(p.lookup("list").getAt(0), Property::Value("foo")); + EXPECT_EQ(p.lookup("list").getAt(1), Property::Value("bar")); + EXPECT_EQ(p.lookup("list").getAt(2), Property::Value("")); Properties p3; @@ -189,32 +191,32 @@ TEST("test stuff") { CopyVisitor cv(p3); p.visitProperties(cv); - EXPECT_EQUAL(p3.numKeys(), 6u); - EXPECT_EQUAL(p3.numValues(), 7u); + EXPECT_EQ(p3.numKeys(), 6u); + EXPECT_EQ(p3.numValues(), 7u); EXPECT_TRUE(p == p3); EXPECT_TRUE(p3 == p); - EXPECT_EQUAL(p.hashCode(), p3.hashCode()); + EXPECT_EQ(p.hashCode(), p3.hashCode()); p.clear(); - EXPECT_EQUAL(p.numKeys(), 0u); - EXPECT_EQUAL(p.numValues(), 0u); + EXPECT_EQ(p.numKeys(), 0u); + EXPECT_EQ(p.numValues(), 0u); EXPECT_TRUE(!(p == p3)); EXPECT_TRUE(!(p3 == p)); Properties p4; CopyVisitor cv2(p4); p.visitProperties(cv); - EXPECT_EQUAL(p4.numKeys(), 0u); - EXPECT_EQUAL(p4.numValues(), 0u); + EXPECT_EQ(p4.numKeys(), 0u); + EXPECT_EQ(p4.numValues(), 0u); EXPECT_TRUE(p == p4); EXPECT_TRUE(p4 == p); - EXPECT_EQUAL(p.hashCode(), p4.hashCode()); + EXPECT_EQ(p.hashCode(), p4.hashCode()); } { // test index properties known by the framework { // vespa.eval.lazy_expressions - EXPECT_EQUAL(eval::LazyExpressions::NAME, vespalib::string("vespa.eval.lazy_expressions")); + EXPECT_EQ(eval::LazyExpressions::NAME, vespalib::string("vespa.eval.lazy_expressions")); { Properties p; EXPECT_TRUE(eval::LazyExpressions::check(p, true)); @@ -234,201 +236,203 @@ TEST("test stuff") { } } { // vespa.eval.use_fast_forest - EXPECT_EQUAL(eval::UseFastForest::NAME, vespalib::string("vespa.eval.use_fast_forest")); - EXPECT_EQUAL(eval::UseFastForest::DEFAULT_VALUE, false); + EXPECT_EQ(eval::UseFastForest::NAME, vespalib::string("vespa.eval.use_fast_forest")); + EXPECT_EQ(eval::UseFastForest::DEFAULT_VALUE, false); Properties p; - EXPECT_EQUAL(eval::UseFastForest::check(p), false); + EXPECT_EQ(eval::UseFastForest::check(p), false); p.add("vespa.eval.use_fast_forest", "true"); - EXPECT_EQUAL(eval::UseFastForest::check(p), true); + EXPECT_EQ(eval::UseFastForest::check(p), true); } { // vespa.rank.firstphase - EXPECT_EQUAL(rank::FirstPhase::NAME, vespalib::string("vespa.rank.firstphase")); - EXPECT_EQUAL(rank::FirstPhase::DEFAULT_VALUE, vespalib::string("nativeRank")); + EXPECT_EQ(rank::FirstPhase::NAME, vespalib::string("vespa.rank.firstphase")); + EXPECT_EQ(rank::FirstPhase::DEFAULT_VALUE, vespalib::string("nativeRank")); Properties p; - EXPECT_EQUAL(rank::FirstPhase::lookup(p), vespalib::string("nativeRank")); + EXPECT_EQ(rank::FirstPhase::lookup(p), vespalib::string("nativeRank")); p.add("vespa.rank.firstphase", "specialrank"); - EXPECT_EQUAL(rank::FirstPhase::lookup(p), vespalib::string("specialrank")); + EXPECT_EQ(rank::FirstPhase::lookup(p), vespalib::string("specialrank")); } { // vespa.rank.secondphase - EXPECT_EQUAL(rank::SecondPhase::NAME, vespalib::string("vespa.rank.secondphase")); - EXPECT_EQUAL(rank::SecondPhase::DEFAULT_VALUE, vespalib::string("")); + EXPECT_EQ(rank::SecondPhase::NAME, vespalib::string("vespa.rank.secondphase")); + EXPECT_EQ(rank::SecondPhase::DEFAULT_VALUE, vespalib::string("")); Properties p; - EXPECT_EQUAL(rank::SecondPhase::lookup(p), vespalib::string("")); + EXPECT_EQ(rank::SecondPhase::lookup(p), vespalib::string("")); p.add("vespa.rank.secondphase", "specialrank"); - EXPECT_EQUAL(rank::SecondPhase::lookup(p), vespalib::string("specialrank")); + EXPECT_EQ(rank::SecondPhase::lookup(p), vespalib::string("specialrank")); } { // vespa.dump.feature - EXPECT_EQUAL(dump::Feature::NAME, vespalib::string("vespa.dump.feature")); - EXPECT_EQUAL(dump::Feature::DEFAULT_VALUE.size(), 0u); + EXPECT_EQ(dump::Feature::NAME, vespalib::string("vespa.dump.feature")); + EXPECT_EQ(dump::Feature::DEFAULT_VALUE.size(), 0u); Properties p; - EXPECT_EQUAL(dump::Feature::lookup(p).size(), 0u); + EXPECT_EQ(dump::Feature::lookup(p).size(), 0u); p.add("vespa.dump.feature", "foo"); p.add("vespa.dump.feature", "bar"); std::vector<vespalib::string> a = dump::Feature::lookup(p); ASSERT_TRUE(a.size() == 2); - EXPECT_EQUAL(a[0], vespalib::string("foo")); - EXPECT_EQUAL(a[1], vespalib::string("bar")); + EXPECT_EQ(a[0], vespalib::string("foo")); + EXPECT_EQ(a[1], vespalib::string("bar")); } { // vespa.dump.ignoredefaultfeatures - EXPECT_EQUAL(dump::IgnoreDefaultFeatures::NAME, vespalib::string("vespa.dump.ignoredefaultfeatures")); - EXPECT_EQUAL(dump::IgnoreDefaultFeatures::DEFAULT_VALUE, "false"); + EXPECT_EQ(dump::IgnoreDefaultFeatures::NAME, vespalib::string("vespa.dump.ignoredefaultfeatures")); + EXPECT_EQ(dump::IgnoreDefaultFeatures::DEFAULT_VALUE, "false"); Properties p; EXPECT_TRUE(!dump::IgnoreDefaultFeatures::check(p)); p.add("vespa.dump.ignoredefaultfeatures", "true"); EXPECT_TRUE(dump::IgnoreDefaultFeatures::check(p)); } { // vespa.matching.termwise_limit - EXPECT_EQUAL(matching::TermwiseLimit::NAME, vespalib::string("vespa.matching.termwise_limit")); - EXPECT_EQUAL(matching::TermwiseLimit::DEFAULT_VALUE, 1.0); + EXPECT_EQ(matching::TermwiseLimit::NAME, vespalib::string("vespa.matching.termwise_limit")); + EXPECT_EQ(matching::TermwiseLimit::DEFAULT_VALUE, 1.0); Properties p; - EXPECT_EQUAL(matching::TermwiseLimit::lookup(p), 1.0); + EXPECT_EQ(matching::TermwiseLimit::lookup(p), 1.0); p.add("vespa.matching.termwise_limit", "0.05"); - EXPECT_EQUAL(matching::TermwiseLimit::lookup(p), 0.05); + EXPECT_EQ(matching::TermwiseLimit::lookup(p), 0.05); } { // vespa.matching.numthreads - EXPECT_EQUAL(matching::NumThreadsPerSearch::NAME, vespalib::string("vespa.matching.numthreadspersearch")); - EXPECT_EQUAL(matching::NumThreadsPerSearch::DEFAULT_VALUE, std::numeric_limits<uint32_t>::max()); + EXPECT_EQ(matching::NumThreadsPerSearch::NAME, vespalib::string("vespa.matching.numthreadspersearch")); + EXPECT_EQ(matching::NumThreadsPerSearch::DEFAULT_VALUE, std::numeric_limits<uint32_t>::max()); Properties p; - EXPECT_EQUAL(matching::NumThreadsPerSearch::lookup(p), std::numeric_limits<uint32_t>::max()); + EXPECT_EQ(matching::NumThreadsPerSearch::lookup(p), std::numeric_limits<uint32_t>::max()); p.add("vespa.matching.numthreadspersearch", "50"); - EXPECT_EQUAL(matching::NumThreadsPerSearch::lookup(p), 50u); + EXPECT_EQ(matching::NumThreadsPerSearch::lookup(p), 50u); } { // vespa.matching.minhitsperthread - EXPECT_EQUAL(matching::MinHitsPerThread::NAME, vespalib::string("vespa.matching.minhitsperthread")); - EXPECT_EQUAL(matching::MinHitsPerThread::DEFAULT_VALUE, 0u); + EXPECT_EQ(matching::MinHitsPerThread::NAME, vespalib::string("vespa.matching.minhitsperthread")); + EXPECT_EQ(matching::MinHitsPerThread::DEFAULT_VALUE, 0u); Properties p; - EXPECT_EQUAL(matching::MinHitsPerThread::lookup(p), 0u); + EXPECT_EQ(matching::MinHitsPerThread::lookup(p), 0u); p.add("vespa.matching.minhitsperthread", "50"); - EXPECT_EQUAL(matching::MinHitsPerThread::lookup(p), 50u); + EXPECT_EQ(matching::MinHitsPerThread::lookup(p), 50u); } { - EXPECT_EQUAL(matching::NumSearchPartitions::NAME, vespalib::string("vespa.matching.numsearchpartitions")); - EXPECT_EQUAL(matching::NumSearchPartitions::DEFAULT_VALUE, 1u); + EXPECT_EQ(matching::NumSearchPartitions::NAME, vespalib::string("vespa.matching.numsearchpartitions")); + EXPECT_EQ(matching::NumSearchPartitions::DEFAULT_VALUE, 1u); Properties p; - EXPECT_EQUAL(matching::NumSearchPartitions::lookup(p), 1u); + EXPECT_EQ(matching::NumSearchPartitions::lookup(p), 1u); p.add("vespa.matching.numsearchpartitions", "50"); - EXPECT_EQUAL(matching::NumSearchPartitions::lookup(p), 50u); + EXPECT_EQ(matching::NumSearchPartitions::lookup(p), 50u); } { // vespa.matchphase.degradation.attribute - EXPECT_EQUAL(matchphase::DegradationAttribute::NAME, vespalib::string("vespa.matchphase.degradation.attribute")); - EXPECT_EQUAL(matchphase::DegradationAttribute::DEFAULT_VALUE, ""); + EXPECT_EQ(matchphase::DegradationAttribute::NAME, vespalib::string("vespa.matchphase.degradation.attribute")); + EXPECT_EQ(matchphase::DegradationAttribute::DEFAULT_VALUE, ""); Properties p; - EXPECT_EQUAL(matchphase::DegradationAttribute::lookup(p), ""); + EXPECT_EQ(matchphase::DegradationAttribute::lookup(p), ""); p.add("vespa.matchphase.degradation.attribute", "foobar"); - EXPECT_EQUAL(matchphase::DegradationAttribute::lookup(p), "foobar"); + EXPECT_EQ(matchphase::DegradationAttribute::lookup(p), "foobar"); } { // vespa.matchphase.degradation.ascending - EXPECT_EQUAL(matchphase::DegradationAscendingOrder::NAME, vespalib::string("vespa.matchphase.degradation.ascendingorder")); - EXPECT_EQUAL(matchphase::DegradationAscendingOrder::DEFAULT_VALUE, false); + EXPECT_EQ(matchphase::DegradationAscendingOrder::NAME, vespalib::string("vespa.matchphase.degradation.ascendingorder")); + EXPECT_EQ(matchphase::DegradationAscendingOrder::DEFAULT_VALUE, false); Properties p; - EXPECT_EQUAL(matchphase::DegradationAscendingOrder::lookup(p), false); + EXPECT_EQ(matchphase::DegradationAscendingOrder::lookup(p), false); p.add("vespa.matchphase.degradation.ascendingorder", "true"); - EXPECT_EQUAL(matchphase::DegradationAscendingOrder::lookup(p), true); + EXPECT_EQ(matchphase::DegradationAscendingOrder::lookup(p), true); } { // vespa.matchphase.degradation.maxhits - EXPECT_EQUAL(matchphase::DegradationMaxHits::NAME, vespalib::string("vespa.matchphase.degradation.maxhits")); - EXPECT_EQUAL(matchphase::DegradationMaxHits::DEFAULT_VALUE, 0u); + EXPECT_EQ(matchphase::DegradationMaxHits::NAME, vespalib::string("vespa.matchphase.degradation.maxhits")); + EXPECT_EQ(matchphase::DegradationMaxHits::DEFAULT_VALUE, 0u); Properties p; - EXPECT_EQUAL(matchphase::DegradationMaxHits::lookup(p), 0u); + EXPECT_EQ(matchphase::DegradationMaxHits::lookup(p), 0u); p.add("vespa.matchphase.degradation.maxhits", "123789"); - EXPECT_EQUAL(matchphase::DegradationMaxHits::lookup(p), 123789u); + EXPECT_EQ(matchphase::DegradationMaxHits::lookup(p), 123789u); } { // vespa.matchphase.degradation.samplepercentage - EXPECT_EQUAL(matchphase::DegradationSamplePercentage::NAME, vespalib::string("vespa.matchphase.degradation.samplepercentage")); - EXPECT_EQUAL(matchphase::DegradationSamplePercentage::DEFAULT_VALUE, 0.2); + EXPECT_EQ(matchphase::DegradationSamplePercentage::NAME, vespalib::string("vespa.matchphase.degradation.samplepercentage")); + EXPECT_EQ(matchphase::DegradationSamplePercentage::DEFAULT_VALUE, 0.2); Properties p; - EXPECT_EQUAL(matchphase::DegradationSamplePercentage::lookup(p), 0.2); + EXPECT_EQ(matchphase::DegradationSamplePercentage::lookup(p), 0.2); p.add("vespa.matchphase.degradation.samplepercentage", "0.9"); - EXPECT_EQUAL(matchphase::DegradationSamplePercentage::lookup(p), 0.9); + EXPECT_EQ(matchphase::DegradationSamplePercentage::lookup(p), 0.9); } { // vespa.matchphase.degradation.maxfiltercoverage - EXPECT_EQUAL(matchphase::DegradationMaxFilterCoverage::NAME, vespalib::string("vespa.matchphase.degradation.maxfiltercoverage")); - EXPECT_EQUAL(matchphase::DegradationMaxFilterCoverage::DEFAULT_VALUE, 0.2); + EXPECT_EQ(matchphase::DegradationMaxFilterCoverage::NAME, vespalib::string("vespa.matchphase.degradation.maxfiltercoverage")); + EXPECT_EQ(matchphase::DegradationMaxFilterCoverage::DEFAULT_VALUE, 0.2); Properties p; - EXPECT_EQUAL(matchphase::DegradationMaxFilterCoverage::lookup(p), 0.2); + EXPECT_EQ(matchphase::DegradationMaxFilterCoverage::lookup(p), 0.2); p.add("vespa.matchphase.degradation.maxfiltercoverage", "0.076"); - EXPECT_EQUAL(matchphase::DegradationMaxFilterCoverage::lookup(p), 0.076); + EXPECT_EQ(matchphase::DegradationMaxFilterCoverage::lookup(p), 0.076); } { // vespa.matchphase.degradation.postfiltermultiplier - EXPECT_EQUAL(matchphase::DegradationPostFilterMultiplier::NAME, vespalib::string("vespa.matchphase.degradation.postfiltermultiplier")); - EXPECT_EQUAL(matchphase::DegradationPostFilterMultiplier::DEFAULT_VALUE, 1.0); + EXPECT_EQ(matchphase::DegradationPostFilterMultiplier::NAME, vespalib::string("vespa.matchphase.degradation.postfiltermultiplier")); + EXPECT_EQ(matchphase::DegradationPostFilterMultiplier::DEFAULT_VALUE, 1.0); Properties p; - EXPECT_EQUAL(matchphase::DegradationPostFilterMultiplier::lookup(p), 1.0); + EXPECT_EQ(matchphase::DegradationPostFilterMultiplier::lookup(p), 1.0); p.add("vespa.matchphase.degradation.postfiltermultiplier", "0.9"); - EXPECT_EQUAL(matchphase::DegradationPostFilterMultiplier::lookup(p), 0.9); + EXPECT_EQ(matchphase::DegradationPostFilterMultiplier::lookup(p), 0.9); } { // vespa.matchphase.diversity.attribute - EXPECT_EQUAL(matchphase::DiversityAttribute::NAME, vespalib::string("vespa.matchphase.diversity.attribute")); - EXPECT_EQUAL(matchphase::DiversityAttribute::DEFAULT_VALUE, ""); + EXPECT_EQ(matchphase::DiversityAttribute::NAME, vespalib::string("vespa.matchphase.diversity.attribute")); + EXPECT_EQ(matchphase::DiversityAttribute::DEFAULT_VALUE, ""); Properties p; - EXPECT_EQUAL(matchphase::DiversityAttribute::lookup(p), ""); + EXPECT_EQ(matchphase::DiversityAttribute::lookup(p), ""); p.add("vespa.matchphase.diversity.attribute", "foobar"); - EXPECT_EQUAL(matchphase::DiversityAttribute::lookup(p), "foobar"); + EXPECT_EQ(matchphase::DiversityAttribute::lookup(p), "foobar"); } { // vespa.matchphase.diversity.mingroups - EXPECT_EQUAL(matchphase::DiversityMinGroups::NAME, vespalib::string("vespa.matchphase.diversity.mingroups")); - EXPECT_EQUAL(matchphase::DiversityMinGroups::DEFAULT_VALUE, 1u); + EXPECT_EQ(matchphase::DiversityMinGroups::NAME, vespalib::string("vespa.matchphase.diversity.mingroups")); + EXPECT_EQ(matchphase::DiversityMinGroups::DEFAULT_VALUE, 1u); Properties p; - EXPECT_EQUAL(matchphase::DiversityMinGroups::lookup(p), 1u); + EXPECT_EQ(matchphase::DiversityMinGroups::lookup(p), 1u); p.add("vespa.matchphase.diversity.mingroups", "5"); - EXPECT_EQUAL(matchphase::DiversityMinGroups::lookup(p), 5u); + EXPECT_EQ(matchphase::DiversityMinGroups::lookup(p), 5u); } { // vespa.hitcollector.heapsize - EXPECT_EQUAL(hitcollector::HeapSize::NAME, vespalib::string("vespa.hitcollector.heapsize")); - EXPECT_EQUAL(hitcollector::HeapSize::DEFAULT_VALUE, 100u); + EXPECT_EQ(hitcollector::HeapSize::NAME, vespalib::string("vespa.hitcollector.heapsize")); + EXPECT_EQ(hitcollector::HeapSize::DEFAULT_VALUE, 100u); Properties p; - EXPECT_EQUAL(hitcollector::HeapSize::lookup(p), 100u); + EXPECT_EQ(hitcollector::HeapSize::lookup(p), 100u); p.add("vespa.hitcollector.heapsize", "50"); - EXPECT_EQUAL(hitcollector::HeapSize::lookup(p), 50u); + EXPECT_EQ(hitcollector::HeapSize::lookup(p), 50u); } { // vespa.hitcollector.arraysize - EXPECT_EQUAL(hitcollector::ArraySize::NAME, vespalib::string("vespa.hitcollector.arraysize")); - EXPECT_EQUAL(hitcollector::ArraySize::DEFAULT_VALUE, 10000u); + EXPECT_EQ(hitcollector::ArraySize::NAME, vespalib::string("vespa.hitcollector.arraysize")); + EXPECT_EQ(hitcollector::ArraySize::DEFAULT_VALUE, 10000u); Properties p; - EXPECT_EQUAL(hitcollector::ArraySize::lookup(p), 10000u); + EXPECT_EQ(hitcollector::ArraySize::lookup(p), 10000u); p.add("vespa.hitcollector.arraysize", "50"); - EXPECT_EQUAL(hitcollector::ArraySize::lookup(p), 50u); + EXPECT_EQ(hitcollector::ArraySize::lookup(p), 50u); } { // vespa.hitcollector.estimatepoint - EXPECT_EQUAL(hitcollector::EstimatePoint::NAME, vespalib::string("vespa.hitcollector.estimatepoint")); - EXPECT_EQUAL(hitcollector::EstimatePoint::DEFAULT_VALUE, 0xffffffffu); + EXPECT_EQ(hitcollector::EstimatePoint::NAME, vespalib::string("vespa.hitcollector.estimatepoint")); + EXPECT_EQ(hitcollector::EstimatePoint::DEFAULT_VALUE, 0xffffffffu); Properties p; - EXPECT_EQUAL(hitcollector::EstimatePoint::lookup(p), 0xffffffffu); + EXPECT_EQ(hitcollector::EstimatePoint::lookup(p), 0xffffffffu); p.add("vespa.hitcollector.estimatepoint", "50"); - EXPECT_EQUAL(hitcollector::EstimatePoint::lookup(p), 50u); + EXPECT_EQ(hitcollector::EstimatePoint::lookup(p), 50u); } { // vespa.hitcollector.estimatelimit - EXPECT_EQUAL(hitcollector::EstimateLimit::NAME, vespalib::string("vespa.hitcollector.estimatelimit")); - EXPECT_EQUAL(hitcollector::EstimateLimit::DEFAULT_VALUE, 0xffffffffu); + EXPECT_EQ(hitcollector::EstimateLimit::NAME, vespalib::string("vespa.hitcollector.estimatelimit")); + EXPECT_EQ(hitcollector::EstimateLimit::DEFAULT_VALUE, 0xffffffffu); Properties p; - EXPECT_EQUAL(hitcollector::EstimateLimit::lookup(p), 0xffffffffu); + EXPECT_EQ(hitcollector::EstimateLimit::lookup(p), 0xffffffffu); p.add("vespa.hitcollector.estimatelimit", "50"); - EXPECT_EQUAL(hitcollector::EstimateLimit::lookup(p), 50u); + EXPECT_EQ(hitcollector::EstimateLimit::lookup(p), 50u); } { // vespa.hitcollector.rankscoredroplimit - EXPECT_EQUAL(hitcollector::RankScoreDropLimit::NAME, vespalib::string("vespa.hitcollector.rankscoredroplimit")); - search::feature_t got1 = hitcollector::RankScoreDropLimit::DEFAULT_VALUE; - EXPECT_TRUE(got1 != got1); - Properties p; - search::feature_t got2= hitcollector::RankScoreDropLimit::lookup(p); - EXPECT_TRUE(got2 != got2); + EXPECT_EQ(vespalib::string("vespa.hitcollector.rankscoredroplimit"), hitcollector::FirstPhaseRankScoreDropLimit::NAME); + Properties p; + auto got2 = hitcollector::FirstPhaseRankScoreDropLimit::lookup(p); + EXPECT_EQ(std::optional<search::feature_t>(), got2); + got2 = hitcollector::FirstPhaseRankScoreDropLimit::lookup(p, std::nullopt); + EXPECT_EQ(std::optional<search::feature_t>(), got2); + got2 = hitcollector::FirstPhaseRankScoreDropLimit::lookup(p, 4.5); + EXPECT_EQ(std::optional<search::feature_t>(4.5), got2); p.add("vespa.hitcollector.rankscoredroplimit", "-123456789.12345"); - EXPECT_EQUAL(hitcollector::RankScoreDropLimit::lookup(p), -123456789.12345); + EXPECT_EQ(std::optional<search::feature_t>(-123456789.12345), hitcollector::FirstPhaseRankScoreDropLimit::lookup(p)); p.clear().add("vespa.hitcollector.rankscoredroplimit", "123456789.12345"); - EXPECT_EQUAL(hitcollector::RankScoreDropLimit::lookup(p), 123456789.12345); + EXPECT_EQ(std::optional<search::feature_t>(123456789.12345), hitcollector::FirstPhaseRankScoreDropLimit::lookup(p)); } { // vespa.fieldweight. - EXPECT_EQUAL(FieldWeight::BASE_NAME, vespalib::string("vespa.fieldweight.")); - EXPECT_EQUAL(FieldWeight::DEFAULT_VALUE, 100u); + EXPECT_EQ(FieldWeight::BASE_NAME, vespalib::string("vespa.fieldweight.")); + EXPECT_EQ(FieldWeight::DEFAULT_VALUE, 100u); Properties p; - EXPECT_EQUAL(FieldWeight::lookup(p, "foo"), 100u); + EXPECT_EQ(FieldWeight::lookup(p, "foo"), 100u); p.add("vespa.fieldweight.foo", "200"); - EXPECT_EQUAL(FieldWeight::lookup(p, "foo"), 200u); + EXPECT_EQ(FieldWeight::lookup(p, "foo"), 200u); } { // vespa.isfilterfield. - EXPECT_EQUAL(IsFilterField::BASE_NAME, "vespa.isfilterfield."); - EXPECT_EQUAL(IsFilterField::DEFAULT_VALUE, "false"); + EXPECT_EQ(IsFilterField::BASE_NAME, "vespa.isfilterfield."); + EXPECT_EQ(IsFilterField::DEFAULT_VALUE, "false"); Properties p; EXPECT_TRUE(!IsFilterField::check(p, "foo")); p.add("vespa.isfilterfield.foo", "true"); @@ -438,192 +442,206 @@ TEST("test stuff") { EXPECT_TRUE(IsFilterField::check(p, "bar")); } { - EXPECT_EQUAL(mutate::on_match::Attribute::NAME, vespalib::string("vespa.mutate.on_match.attribute")); - EXPECT_EQUAL(mutate::on_match::Attribute::DEFAULT_VALUE, ""); + EXPECT_EQ(mutate::on_match::Attribute::NAME, vespalib::string("vespa.mutate.on_match.attribute")); + EXPECT_EQ(mutate::on_match::Attribute::DEFAULT_VALUE, ""); Properties p; - EXPECT_EQUAL(mutate::on_match::Attribute::lookup(p), ""); + EXPECT_EQ(mutate::on_match::Attribute::lookup(p), ""); p.add("vespa.mutate.on_match.attribute", "foobar"); - EXPECT_EQUAL(mutate::on_match::Attribute::lookup(p), "foobar"); + EXPECT_EQ(mutate::on_match::Attribute::lookup(p), "foobar"); } { - EXPECT_EQUAL(mutate::on_match::Operation::NAME, vespalib::string("vespa.mutate.on_match.operation")); - EXPECT_EQUAL(mutate::on_match::Operation::DEFAULT_VALUE, ""); + EXPECT_EQ(mutate::on_match::Operation::NAME, vespalib::string("vespa.mutate.on_match.operation")); + EXPECT_EQ(mutate::on_match::Operation::DEFAULT_VALUE, ""); Properties p; - EXPECT_EQUAL(mutate::on_match::Operation::lookup(p), ""); + EXPECT_EQ(mutate::on_match::Operation::lookup(p), ""); p.add("vespa.mutate.on_match.operation", "+=1"); - EXPECT_EQUAL(mutate::on_match::Operation::lookup(p), "+=1"); + EXPECT_EQ(mutate::on_match::Operation::lookup(p), "+=1"); } { - EXPECT_EQUAL(mutate::on_first_phase::Attribute::NAME, vespalib::string("vespa.mutate.on_first_phase.attribute")); - EXPECT_EQUAL(mutate::on_first_phase::Attribute::DEFAULT_VALUE, ""); + EXPECT_EQ(mutate::on_first_phase::Attribute::NAME, vespalib::string("vespa.mutate.on_first_phase.attribute")); + EXPECT_EQ(mutate::on_first_phase::Attribute::DEFAULT_VALUE, ""); Properties p; - EXPECT_EQUAL(mutate::on_first_phase::Attribute::lookup(p), ""); + EXPECT_EQ(mutate::on_first_phase::Attribute::lookup(p), ""); p.add("vespa.mutate.on_first_phase.attribute", "foobar"); - EXPECT_EQUAL(mutate::on_first_phase::Attribute::lookup(p), "foobar"); + EXPECT_EQ(mutate::on_first_phase::Attribute::lookup(p), "foobar"); } { - EXPECT_EQUAL(mutate::on_first_phase::Operation::NAME, vespalib::string("vespa.mutate.on_first_phase.operation")); - EXPECT_EQUAL(mutate::on_first_phase::Operation::DEFAULT_VALUE, ""); + EXPECT_EQ(mutate::on_first_phase::Operation::NAME, vespalib::string("vespa.mutate.on_first_phase.operation")); + EXPECT_EQ(mutate::on_first_phase::Operation::DEFAULT_VALUE, ""); Properties p; - EXPECT_EQUAL(mutate::on_first_phase::Operation::lookup(p), ""); + EXPECT_EQ(mutate::on_first_phase::Operation::lookup(p), ""); p.add("vespa.mutate.on_first_phase.operation", "+=1"); - EXPECT_EQUAL(mutate::on_first_phase::Operation::lookup(p), "+=1"); + EXPECT_EQ(mutate::on_first_phase::Operation::lookup(p), "+=1"); } { - EXPECT_EQUAL(mutate::on_second_phase::Attribute::NAME, vespalib::string("vespa.mutate.on_second_phase.attribute")); - EXPECT_EQUAL(mutate::on_second_phase::Attribute::DEFAULT_VALUE, ""); + EXPECT_EQ(mutate::on_second_phase::Attribute::NAME, vespalib::string("vespa.mutate.on_second_phase.attribute")); + EXPECT_EQ(mutate::on_second_phase::Attribute::DEFAULT_VALUE, ""); Properties p; - EXPECT_EQUAL(mutate::on_second_phase::Attribute::lookup(p), ""); + EXPECT_EQ(mutate::on_second_phase::Attribute::lookup(p), ""); p.add("vespa.mutate.on_second_phase.attribute", "foobar"); - EXPECT_EQUAL(mutate::on_second_phase::Attribute::lookup(p), "foobar"); + EXPECT_EQ(mutate::on_second_phase::Attribute::lookup(p), "foobar"); } { - EXPECT_EQUAL(mutate::on_second_phase::Operation::NAME, vespalib::string("vespa.mutate.on_second_phase.operation")); - EXPECT_EQUAL(mutate::on_second_phase::Operation::DEFAULT_VALUE, ""); + EXPECT_EQ(mutate::on_second_phase::Operation::NAME, vespalib::string("vespa.mutate.on_second_phase.operation")); + EXPECT_EQ(mutate::on_second_phase::Operation::DEFAULT_VALUE, ""); Properties p; - EXPECT_EQUAL(mutate::on_second_phase::Operation::lookup(p), ""); + EXPECT_EQ(mutate::on_second_phase::Operation::lookup(p), ""); p.add("vespa.mutate.on_second_phase.operation", "+=1"); - EXPECT_EQUAL(mutate::on_second_phase::Operation::lookup(p), "+=1"); + EXPECT_EQ(mutate::on_second_phase::Operation::lookup(p), "+=1"); } { - EXPECT_EQUAL(mutate::on_summary::Attribute::NAME, vespalib::string("vespa.mutate.on_summary.attribute")); - EXPECT_EQUAL(mutate::on_summary::Attribute::DEFAULT_VALUE, ""); + EXPECT_EQ(mutate::on_summary::Attribute::NAME, vespalib::string("vespa.mutate.on_summary.attribute")); + EXPECT_EQ(mutate::on_summary::Attribute::DEFAULT_VALUE, ""); Properties p; - EXPECT_EQUAL(mutate::on_summary::Attribute::lookup(p), ""); + EXPECT_EQ(mutate::on_summary::Attribute::lookup(p), ""); p.add("vespa.mutate.on_summary.attribute", "foobar"); - EXPECT_EQUAL(mutate::on_summary::Attribute::lookup(p), "foobar"); + EXPECT_EQ(mutate::on_summary::Attribute::lookup(p), "foobar"); } { - EXPECT_EQUAL(mutate::on_summary::Operation::NAME, vespalib::string("vespa.mutate.on_summary.operation")); - EXPECT_EQUAL(mutate::on_summary::Operation::DEFAULT_VALUE, ""); + EXPECT_EQ(mutate::on_summary::Operation::NAME, vespalib::string("vespa.mutate.on_summary.operation")); + EXPECT_EQ(mutate::on_summary::Operation::DEFAULT_VALUE, ""); Properties p; - EXPECT_EQUAL(mutate::on_summary::Operation::lookup(p), ""); + EXPECT_EQ(mutate::on_summary::Operation::lookup(p), ""); p.add("vespa.mutate.on_summary.operation", "+=1"); - EXPECT_EQUAL(mutate::on_summary::Operation::lookup(p), "+=1"); + EXPECT_EQ(mutate::on_summary::Operation::lookup(p), "+=1"); } { - EXPECT_EQUAL(execute::onmatch::Attribute::NAME, vespalib::string("vespa.execute.onmatch.attribute")); - EXPECT_EQUAL(execute::onmatch::Attribute::DEFAULT_VALUE, ""); + EXPECT_EQ(execute::onmatch::Attribute::NAME, vespalib::string("vespa.execute.onmatch.attribute")); + EXPECT_EQ(execute::onmatch::Attribute::DEFAULT_VALUE, ""); Properties p; - EXPECT_EQUAL(execute::onmatch::Attribute::lookup(p), ""); + EXPECT_EQ(execute::onmatch::Attribute::lookup(p), ""); p.add("vespa.execute.onmatch.attribute", "foobar"); - EXPECT_EQUAL(execute::onmatch::Attribute::lookup(p), "foobar"); + EXPECT_EQ(execute::onmatch::Attribute::lookup(p), "foobar"); } { - EXPECT_EQUAL(execute::onmatch::Operation::NAME, vespalib::string("vespa.execute.onmatch.operation")); - EXPECT_EQUAL(execute::onmatch::Operation::DEFAULT_VALUE, ""); + EXPECT_EQ(execute::onmatch::Operation::NAME, vespalib::string("vespa.execute.onmatch.operation")); + EXPECT_EQ(execute::onmatch::Operation::DEFAULT_VALUE, ""); Properties p; - EXPECT_EQUAL(execute::onmatch::Operation::lookup(p), ""); + EXPECT_EQ(execute::onmatch::Operation::lookup(p), ""); p.add("vespa.execute.onmatch.operation", "++"); - EXPECT_EQUAL(execute::onmatch::Operation::lookup(p), "++"); + EXPECT_EQ(execute::onmatch::Operation::lookup(p), "++"); } { - EXPECT_EQUAL(execute::onrerank::Attribute::NAME, vespalib::string("vespa.execute.onrerank.attribute")); - EXPECT_EQUAL(execute::onrerank::Attribute::DEFAULT_VALUE, ""); + EXPECT_EQ(execute::onrerank::Attribute::NAME, vespalib::string("vespa.execute.onrerank.attribute")); + EXPECT_EQ(execute::onrerank::Attribute::DEFAULT_VALUE, ""); Properties p; - EXPECT_EQUAL(execute::onrerank::Attribute::lookup(p), ""); + EXPECT_EQ(execute::onrerank::Attribute::lookup(p), ""); p.add("vespa.execute.onrerank.attribute", "foobar"); - EXPECT_EQUAL(execute::onrerank::Attribute::lookup(p), "foobar"); + EXPECT_EQ(execute::onrerank::Attribute::lookup(p), "foobar"); } { - EXPECT_EQUAL(execute::onrerank::Operation::NAME, vespalib::string("vespa.execute.onrerank.operation")); - EXPECT_EQUAL(execute::onrerank::Operation::DEFAULT_VALUE, ""); + EXPECT_EQ(execute::onrerank::Operation::NAME, vespalib::string("vespa.execute.onrerank.operation")); + EXPECT_EQ(execute::onrerank::Operation::DEFAULT_VALUE, ""); Properties p; - EXPECT_EQUAL(execute::onrerank::Operation::lookup(p), ""); + EXPECT_EQ(execute::onrerank::Operation::lookup(p), ""); p.add("vespa.execute.onrerank.operation", "++"); - EXPECT_EQUAL(execute::onrerank::Operation::lookup(p), "++"); + EXPECT_EQ(execute::onrerank::Operation::lookup(p), "++"); } { - EXPECT_EQUAL(execute::onsummary::Attribute::NAME, vespalib::string("vespa.execute.onsummary.attribute")); - EXPECT_EQUAL(execute::onsummary::Attribute::DEFAULT_VALUE, ""); + EXPECT_EQ(execute::onsummary::Attribute::NAME, vespalib::string("vespa.execute.onsummary.attribute")); + EXPECT_EQ(execute::onsummary::Attribute::DEFAULT_VALUE, ""); Properties p; - EXPECT_EQUAL(execute::onsummary::Attribute::lookup(p), ""); + EXPECT_EQ(execute::onsummary::Attribute::lookup(p), ""); p.add("vespa.execute.onsummary.attribute", "foobar"); - EXPECT_EQUAL(execute::onsummary::Attribute::lookup(p), "foobar"); + EXPECT_EQ(execute::onsummary::Attribute::lookup(p), "foobar"); } { - EXPECT_EQUAL(execute::onsummary::Operation::NAME, vespalib::string("vespa.execute.onsummary.operation")); - EXPECT_EQUAL(execute::onsummary::Operation::DEFAULT_VALUE, ""); + EXPECT_EQ(execute::onsummary::Operation::NAME, vespalib::string("vespa.execute.onsummary.operation")); + EXPECT_EQ(execute::onsummary::Operation::DEFAULT_VALUE, ""); Properties p; - EXPECT_EQUAL(execute::onsummary::Operation::lookup(p), ""); + EXPECT_EQ(execute::onsummary::Operation::lookup(p), ""); p.add("vespa.execute.onsummary.operation", "++"); - EXPECT_EQUAL(execute::onsummary::Operation::lookup(p), "++"); + EXPECT_EQ(execute::onsummary::Operation::lookup(p), "++"); } { - EXPECT_EQUAL(softtimeout::Enabled::NAME, vespalib::string("vespa.softtimeout.enable")); + EXPECT_EQ(softtimeout::Enabled::NAME, vespalib::string("vespa.softtimeout.enable")); EXPECT_TRUE(softtimeout::Enabled::DEFAULT_VALUE); Properties p; p.add(softtimeout::Enabled::NAME, "false"); EXPECT_FALSE(softtimeout::Enabled::lookup(p)); } { - EXPECT_EQUAL(softtimeout::Factor::NAME, vespalib::string("vespa.softtimeout.factor")); - EXPECT_EQUAL(0.5, softtimeout::Factor::DEFAULT_VALUE); + EXPECT_EQ(softtimeout::Factor::NAME, vespalib::string("vespa.softtimeout.factor")); + EXPECT_EQ(0.5, softtimeout::Factor::DEFAULT_VALUE); Properties p; p.add(softtimeout::Factor::NAME, "0.33"); - EXPECT_EQUAL(0.33, softtimeout::Factor::lookup(p)); + EXPECT_EQ(0.33, softtimeout::Factor::lookup(p)); } { - EXPECT_EQUAL(softtimeout::TailCost::NAME, vespalib::string("vespa.softtimeout.tailcost")); - EXPECT_EQUAL(0.1, softtimeout::TailCost::DEFAULT_VALUE); + EXPECT_EQ(softtimeout::TailCost::NAME, vespalib::string("vespa.softtimeout.tailcost")); + EXPECT_EQ(0.1, softtimeout::TailCost::DEFAULT_VALUE); Properties p; p.add(softtimeout::TailCost::NAME, "0.17"); - EXPECT_EQUAL(0.17, softtimeout::TailCost::lookup(p)); + EXPECT_EQ(0.17, softtimeout::TailCost::lookup(p)); } } } -TEST("test attribute type properties") +TEST(PropertiesTest, test_attribute_type_properties) { Properties p; p.add("vespa.type.attribute.foo", "tensor(x[10])"); - EXPECT_EQUAL("tensor(x[10])", type::Attribute::lookup(p, "foo")); - EXPECT_EQUAL("", type::Attribute::lookup(p, "bar")); + EXPECT_EQ("tensor(x[10])", type::Attribute::lookup(p, "foo")); + EXPECT_EQ("", type::Attribute::lookup(p, "bar")); } -TEST("test query feature type properties") +TEST(PropertiesTest, test_query_feature_type_properties) { Properties p; p.add("vespa.type.query.foo", "tensor(x[10])"); - EXPECT_EQUAL("tensor(x[10])", type::QueryFeature::lookup(p, "foo")); - EXPECT_EQUAL("", type::QueryFeature::lookup(p, "bar")); + EXPECT_EQ("tensor(x[10])", type::QueryFeature::lookup(p, "foo")); + EXPECT_EQ("", type::QueryFeature::lookup(p, "bar")); } -TEST("test integer lookup") +TEST(PropertiesTest, test_integer_lookup) { - EXPECT_EQUAL(matching::NumThreadsPerSearch::NAME, vespalib::string("vespa.matching.numthreadspersearch")); - EXPECT_EQUAL(matching::NumThreadsPerSearch::DEFAULT_VALUE, std::numeric_limits<uint32_t>::max()); + EXPECT_EQ(matching::NumThreadsPerSearch::NAME, vespalib::string("vespa.matching.numthreadspersearch")); + EXPECT_EQ(matching::NumThreadsPerSearch::DEFAULT_VALUE, std::numeric_limits<uint32_t>::max()); { Properties p; p.add("vespa.matching.numthreadspersearch", "50"); - EXPECT_EQUAL(matching::NumThreadsPerSearch::lookup(p), 50u); + EXPECT_EQ(matching::NumThreadsPerSearch::lookup(p), 50u); } { Properties p; p.add("vespa.matching.numthreadspersearch", "50 "); - EXPECT_EQUAL(matching::NumThreadsPerSearch::lookup(p), 50u); + EXPECT_EQ(matching::NumThreadsPerSearch::lookup(p), 50u); } { Properties p; p.add("vespa.matching.numthreadspersearch", " 50"); - EXPECT_EQUAL(matching::NumThreadsPerSearch::lookup(p), 50u); + EXPECT_EQ(matching::NumThreadsPerSearch::lookup(p), 50u); } { Properties p; p.add("vespa.matching.numthreadspersearch", " "); - EXPECT_EQUAL(matching::NumThreadsPerSearch::lookup(p), matching::NumThreadsPerSearch::DEFAULT_VALUE); + EXPECT_EQ(matching::NumThreadsPerSearch::lookup(p), matching::NumThreadsPerSearch::DEFAULT_VALUE); } { Properties p; p.add("vespa.matching.numthreadspersearch", "50x"); - EXPECT_EQUAL(matching::NumThreadsPerSearch::lookup(p), 50u); + EXPECT_EQ(matching::NumThreadsPerSearch::lookup(p), 50u); } { Properties p; p.add("vespa.matching.numthreadspersearch", "x"); - EXPECT_EQUAL(matching::NumThreadsPerSearch::lookup(p), matching::NumThreadsPerSearch::DEFAULT_VALUE); + EXPECT_EQ(matching::NumThreadsPerSearch::lookup(p), matching::NumThreadsPerSearch::DEFAULT_VALUE); } } +TEST(PropertiesTest, second_phase_rank_score_drop_limit) +{ + vespalib::stringref name = hitcollector::SecondPhaseRankScoreDropLimit::NAME; + EXPECT_EQ(vespalib::string("vespa.hitcollector.secondphase.rankscoredroplimit"), name); + Properties p; + EXPECT_EQ(std::optional<search::feature_t>(), hitcollector::SecondPhaseRankScoreDropLimit::lookup(p)); + EXPECT_EQ(std::optional<search::feature_t>(4.0), hitcollector::SecondPhaseRankScoreDropLimit::lookup(p, 4.0)); + p.add(name, "-123456789.12345"); + EXPECT_EQ(std::optional<search::feature_t>(-123456789.12345), hitcollector::SecondPhaseRankScoreDropLimit::lookup(p)); + EXPECT_EQ(std::optional<search::feature_t>(-123456789.12345), hitcollector::SecondPhaseRankScoreDropLimit::lookup(p, 4.0)); + p.clear().add(name, "123456789.12345"); + EXPECT_EQ(std::optional<search::feature_t>(123456789.12345), hitcollector::SecondPhaseRankScoreDropLimit::lookup(p)); + EXPECT_EQ(std::optional<search::feature_t>(123456789.12345), hitcollector::SecondPhaseRankScoreDropLimit::lookup(p, 4.0)); +} -TEST_MAIN() { TEST_RUN_ALL(); } +GTEST_MAIN_RUN_ALL_TESTS() diff --git a/searchlib/src/tests/fef/table/CMakeLists.txt b/searchlib/src/tests/fef/table/CMakeLists.txt index 6cc6856e0ce..2353fd50c95 100644 --- a/searchlib/src/tests/fef/table/CMakeLists.txt +++ b/searchlib/src/tests/fef/table/CMakeLists.txt @@ -4,5 +4,6 @@ vespa_add_executable(searchlib_table_test_app TEST table_test.cpp DEPENDS searchlib + GTest::gtest ) vespa_add_test(NAME searchlib_table_test_app COMMAND searchlib_table_test_app) diff --git a/searchlib/src/tests/fef/table/table_test.cpp b/searchlib/src/tests/fef/table/table_test.cpp index b0a47a8bdbc..9c15274c093 100644 --- a/searchlib/src/tests/fef/table/table_test.cpp +++ b/searchlib/src/tests/fef/table/table_test.cpp @@ -1,18 +1,21 @@ // Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -#include <vespa/vespalib/testkit/testapp.h> #include <vespa/searchlib/fef/filetablefactory.h> #include <vespa/searchlib/fef/functiontablefactory.h> #include <vespa/searchlib/fef/tablemanager.h> +#include <vespa/vespalib/gtest/gtest.h> +#include <vespa/vespalib/testkit/test_path.h> #include <fstream> #include <iostream> -namespace search { -namespace fef { +namespace search::fef { -class TableTest : public vespalib::TestApp +class TableTest : public ::testing::Test { -private: +protected: + const std::string _tables1Dir; + const std::string _tables2Dir; + bool assertTable(const Table & act, const Table & exp); bool assertCreateTable(const ITableFactory & tf, const vespalib::string & name, const Table & exp); void testTable(); @@ -20,29 +23,32 @@ private: void testFunctionTableFactory(); void testTableManager(); - const std::string _tables1Dir; - const std::string _tables2Dir; -public: TableTest(); - ~TableTest(); - int Main() override; + ~TableTest() override; }; -TableTest::TableTest() : - vespalib::TestApp(), - _tables1Dir(TEST_PATH("tables1")), - _tables2Dir(TEST_PATH("tables2")) +TableTest::TableTest() + : ::testing::Test(), + _tables1Dir(TEST_PATH("tables1")), + _tables2Dir(TEST_PATH("tables2")) { } -TableTest::~TableTest() {} +TableTest::~TableTest() = default; bool TableTest::assertTable(const Table & act, const Table & exp) { - if (!EXPECT_EQUAL(act.size(), exp.size())) return false; + bool failed = false; + EXPECT_EQ(act.size(), exp.size()) << (failed = true, ""); + if (failed) { + return false; + } for (size_t i = 0; i < act.size(); ++i) { - if (!EXPECT_APPROX(act[i], exp[i], 0.01)) return false; + EXPECT_NEAR(act[i], exp[i], 0.01) << (failed = true, ""); + if (failed) { + return false; + } } return true; } @@ -51,33 +57,35 @@ bool TableTest::assertCreateTable(const ITableFactory & tf, const vespalib::string & name, const Table & exp) { Table::SP t = tf.createTable(name); - if (!EXPECT_TRUE(t.get() != NULL)) return false; + bool failed = false; + EXPECT_TRUE(t.get() != nullptr) << (failed = true, ""); + if (failed) { + return false; + } return assertTable(*t, exp); } -void -TableTest::testTable() +TEST_F(TableTest, table) { Table t; - EXPECT_EQUAL(t.size(), 0u); - EXPECT_EQUAL(t.max(), -std::numeric_limits<double>::max()); + EXPECT_EQ(t.size(), 0u); + EXPECT_EQ(t.max(), -std::numeric_limits<double>::max()); t.add(1).add(2); - EXPECT_EQUAL(t.size(), 2u); - EXPECT_EQUAL(t.max(), 2); - EXPECT_EQUAL(t[0], 1); - EXPECT_EQUAL(t[1], 2); + EXPECT_EQ(t.size(), 2u); + EXPECT_EQ(t.max(), 2); + EXPECT_EQ(t[0], 1); + EXPECT_EQ(t[1], 2); t.add(10); - EXPECT_EQUAL(t.size(), 3u); - EXPECT_EQUAL(t.max(), 10); - EXPECT_EQUAL(t[2], 10); + EXPECT_EQ(t.size(), 3u); + EXPECT_EQ(t.max(), 10); + EXPECT_EQ(t[2], 10); t.add(5); - EXPECT_EQUAL(t.size(), 4u); - EXPECT_EQUAL(t.max(), 10); - EXPECT_EQUAL(t[3], 5); + EXPECT_EQ(t.size(), 4u); + EXPECT_EQ(t.max(), 10); + EXPECT_EQ(t[3], 5); } -void -TableTest::testFileTableFactory() +TEST_F(TableTest, file_table_factory) { { FileTableFactory ftf(_tables1Dir); @@ -90,8 +98,7 @@ TableTest::testFileTableFactory() } } -void -TableTest::testFunctionTableFactory() +TEST_F(TableTest, function_table_factory) { FunctionTableFactory ftf(2); EXPECT_TRUE(assertCreateTable(ftf, "expdecay(400,12)", @@ -117,8 +124,7 @@ TableTest::testFunctionTableFactory() EXPECT_TRUE(ftf.createTable("none)(").get() == NULL); } -void -TableTest::testTableManager() +TEST_F(TableTest, table_manager) { { TableManager tm; @@ -148,20 +154,6 @@ TableTest::testTableManager() } } -int -TableTest::Main() -{ - TEST_INIT("table_test"); - - testTable(); - testFileTableFactory(); - testFunctionTableFactory(); - testTableManager(); - - TEST_DONE(); -} - -} } -TEST_APPHOOK(search::fef::TableTest); +GTEST_MAIN_RUN_ALL_TESTS() diff --git a/searchlib/src/tests/hitcollector/CMakeLists.txt b/searchlib/src/tests/hitcollector/CMakeLists.txt index 5cedbcbd7e6..cc62dd82af4 100644 --- a/searchlib/src/tests/hitcollector/CMakeLists.txt +++ b/searchlib/src/tests/hitcollector/CMakeLists.txt @@ -4,6 +4,7 @@ vespa_add_executable(searchlib_hitcollector_test_app TEST hitcollector_test.cpp DEPENDS searchlib + GTest::gtest ) vespa_add_test(NAME searchlib_hitcollector_test_app COMMAND searchlib_hitcollector_test_app) vespa_add_executable(searchlib_sorted_hit_sequence_test_app TEST @@ -11,5 +12,6 @@ vespa_add_executable(searchlib_sorted_hit_sequence_test_app TEST sorted_hit_sequence_test.cpp DEPENDS searchlib + GTest::gtest ) vespa_add_test(NAME searchlib_sorted_hit_sequence_test_app COMMAND searchlib_sorted_hit_sequence_test_app) diff --git a/searchlib/src/tests/hitcollector/hitcollector_test.cpp b/searchlib/src/tests/hitcollector/hitcollector_test.cpp index e6e38181412..784afea7801 100644 --- a/searchlib/src/tests/hitcollector/hitcollector_test.cpp +++ b/searchlib/src/tests/hitcollector/hitcollector_test.cpp @@ -1,9 +1,9 @@ // Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -#include <vespa/vespalib/testkit/testapp.h> #include <vespa/searchlib/common/bitvector.h> #include <vespa/searchlib/fef/fef.h> #include <vespa/searchlib/queryeval/hitcollector.h> +#include <vespa/vespalib/gtest/gtest.h> #include <vespa/log/log.h> LOG_SETUP("hitcollector_test"); @@ -13,6 +13,8 @@ using namespace search::fef; using namespace search::queryeval; using ScoreMap = std::map<uint32_t, feature_t>; +using DocidVector = std::vector<uint32_t>; +using RankedHitVector = std::vector<RankedHit>; using Ranges = std::pair<Scores, Scores>; @@ -67,11 +69,11 @@ void checkResult(const ResultSet & rs, const std::vector<RankedHit> & exp) if ( ! exp.empty()) { const RankedHit * rh = rs.getArray(); ASSERT_TRUE(rh != nullptr); - ASSERT_EQUAL(rs.getArrayUsed(), exp.size()); + ASSERT_EQ(rs.getArrayUsed(), exp.size()); for (uint32_t i = 0; i < exp.size(); ++i) { - EXPECT_EQUAL(rh[i].getDocId(), exp[i].getDocId()); - EXPECT_EQUAL(rh[i].getRank() + 1.0, exp[i].getRank() + 1.0); + EXPECT_EQ(rh[i].getDocId(), exp[i].getDocId()); + EXPECT_DOUBLE_EQ(rh[i].getRank() + 64.0, exp[i].getRank() + 64.0); } } else { ASSERT_TRUE(rs.getArray() == nullptr); @@ -93,21 +95,24 @@ void checkResult(ResultSet & rs, BitVector * exp) } } -void testAddHit(uint32_t numDocs, uint32_t maxHitsSize) +void testAddHit(uint32_t numDocs, uint32_t maxHitsSize, const vespalib::string& label) { + SCOPED_TRACE(label); LOG(info, "testAddHit: no hits"); - { // no hits + { + SCOPED_TRACE("no hits"); HitCollector hc(numDocs, maxHitsSize); std::vector<RankedHit> expRh; std::unique_ptr<ResultSet> rs = hc.getResultSet(); - TEST_DO(checkResult(*rs, expRh)); - TEST_DO(checkResult(*rs, nullptr)); + checkResult(*rs, expRh); + checkResult(*rs, nullptr); } LOG(info, "testAddHit: only ranked hits"); - { // only ranked hits + { + SCOPED_TRACE("only ranked hits"); HitCollector hc(numDocs, maxHitsSize); std::vector<RankedHit> expRh; @@ -121,12 +126,13 @@ void testAddHit(uint32_t numDocs, uint32_t maxHitsSize) } std::unique_ptr<ResultSet> rs = hc.getResultSet(); - TEST_DO(checkResult(*rs, expRh)); - TEST_DO(checkResult(*rs, nullptr)); + checkResult(*rs, expRh); + checkResult(*rs, nullptr); } LOG(info, "testAddHit: both ranked hits and bit vector hits"); - { // both ranked hits and bit vector hits + { + SCOPED_TRACE("both ranked hits and bitvector hits"); HitCollector hc(numDocs, maxHitsSize); std::vector<RankedHit> expRh; BitVector::UP expBv(BitVector::create(numDocs)); @@ -144,14 +150,15 @@ void testAddHit(uint32_t numDocs, uint32_t maxHitsSize) } std::unique_ptr<ResultSet> rs = hc.getResultSet(); - TEST_DO(checkResult(*rs, expRh)); - TEST_DO(checkResult(*rs, expBv.get())); + checkResult(*rs, expRh); + checkResult(*rs, expBv.get()); } } -TEST("testAddHit") { - TEST_DO(testAddHit(30, 10)); - TEST_DO(testAddHit(400, 10)); // 400/32 = 12 which is bigger than 10. +TEST(HitCollectorTest, testAddHit) +{ + testAddHit(30, 10, "numDocs==30"); + testAddHit(400, 10, "numDocs==400"); // 400/32 = 12 which is bigger than 10. } struct Fixture { @@ -197,14 +204,17 @@ struct DescendingScoreFixture : Fixture { DescendingScoreFixture::~DescendingScoreFixture() = default; -TEST_F("testReRank - empty", Fixture) { - EXPECT_EQUAL(0u, f.reRank()); +TEST(HitCollectorTest, rerank_empty) +{ + Fixture f; + EXPECT_EQ(0u, f.reRank()); } -TEST_F("testReRank - ascending", AscendingScoreFixture) +TEST(HitCollectorTest, rerank_ascending) { + AscendingScoreFixture f; f.addHits(); - EXPECT_EQUAL(5u, f.reRank()); + EXPECT_EQ(5u, f.reRank()); std::vector<RankedHit> expRh; for (uint32_t i = 10; i < 20; ++i) { // 10 last are the best @@ -213,17 +223,18 @@ TEST_F("testReRank - ascending", AscendingScoreFixture) expRh.back()._rankValue = i + 200; // after reranking } } - EXPECT_EQUAL(expRh.size(), 10u); + EXPECT_EQ(expRh.size(), 10u); std::unique_ptr<ResultSet> rs = f.hc.getResultSet(); - TEST_DO(checkResult(*rs, expRh)); - TEST_DO(checkResult(*rs, f.expBv.get())); + checkResult(*rs, expRh); + checkResult(*rs, f.expBv.get()); } -TEST_F("testReRank - descending", DescendingScoreFixture) +TEST(HitCollectorTest, rerank_descending) { + DescendingScoreFixture f; f.addHits(); - EXPECT_EQUAL(5u, f.reRank()); + EXPECT_EQ(5u, f.reRank()); std::vector<RankedHit> expRh; for (uint32_t i = 0; i < 10; ++i) { // 10 first are the best @@ -232,17 +243,18 @@ TEST_F("testReRank - descending", DescendingScoreFixture) expRh.back()._rankValue = i + 200; // after reranking } } - EXPECT_EQUAL(expRh.size(), 10u); + EXPECT_EQ(expRh.size(), 10u); std::unique_ptr<ResultSet> rs = f.hc.getResultSet(); - TEST_DO(checkResult(*rs, expRh)); - TEST_DO(checkResult(*rs, f.expBv.get())); + checkResult(*rs, expRh); + checkResult(*rs, f.expBv.get()); } -TEST_F("testReRank - partial", AscendingScoreFixture) +TEST(HitCollectorTest, rerank_partial) { + AscendingScoreFixture f; f.addHits(); - EXPECT_EQUAL(3u, f.reRank(3)); + EXPECT_EQ(3u, f.reRank(3)); std::vector<RankedHit> expRh; for (uint32_t i = 10; i < 20; ++i) { // 10 last are the best @@ -251,36 +263,39 @@ TEST_F("testReRank - partial", AscendingScoreFixture) expRh.back()._rankValue = i + 200; // after reranking } } - EXPECT_EQUAL(expRh.size(), 10u); + EXPECT_EQ(expRh.size(), 10u); std::unique_ptr<ResultSet> rs = f.hc.getResultSet(); - TEST_DO(checkResult(*rs, expRh)); - TEST_DO(checkResult(*rs, f.expBv.get())); + checkResult(*rs, expRh); + checkResult(*rs, f.expBv.get()); } -TEST_F("require that hits for 2nd phase candidates can be retrieved", DescendingScoreFixture) +TEST(HitCollectorTest, require_that_hits_for_2nd_phase_candidates_can_be_retrieved) { + DescendingScoreFixture f; f.addHits(); std::vector<HitCollector::Hit> scores = extract(f.hc.getSortedHitSequence(5)); - ASSERT_EQUAL(5u, scores.size()); - EXPECT_EQUAL(100, scores[0].second); - EXPECT_EQUAL(99, scores[1].second); - EXPECT_EQUAL(98, scores[2].second); - EXPECT_EQUAL(97, scores[3].second); - EXPECT_EQUAL(96, scores[4].second); + ASSERT_EQ(5u, scores.size()); + EXPECT_EQ(100, scores[0].second); + EXPECT_EQ(99, scores[1].second); + EXPECT_EQ(98, scores[2].second); + EXPECT_EQ(97, scores[3].second); + EXPECT_EQ(96, scores[4].second); } -TEST("require that score ranges can be read and set.") { +TEST(HitCollectorTest, require_that_score_ranges_can_be_read_and_set) +{ std::pair<Scores, Scores> ranges = std::make_pair(Scores(1.0, 2.0), Scores(3.0, 4.0)); HitCollector hc(20, 10); hc.setRanges(ranges); - EXPECT_EQUAL(ranges.first.low, hc.getRanges().first.low); - EXPECT_EQUAL(ranges.first.high, hc.getRanges().first.high); - EXPECT_EQUAL(ranges.second.low, hc.getRanges().second.low); - EXPECT_EQUAL(ranges.second.high, hc.getRanges().second.high); + EXPECT_EQ(ranges.first.low, hc.getRanges().first.low); + EXPECT_EQ(ranges.first.high, hc.getRanges().first.high); + EXPECT_EQ(ranges.second.low, hc.getRanges().second.low); + EXPECT_EQ(ranges.second.high, hc.getRanges().second.high); } -TEST("testNoHitsToReRank") { +TEST(HitCollectorTest, no_hits_to_rerank) +{ uint32_t numDocs = 20; uint32_t maxHitsSize = 10; @@ -299,8 +314,8 @@ TEST("testNoHitsToReRank") { } std::unique_ptr<ResultSet> rs = hc.getResultSet(); - TEST_DO(checkResult(*rs, expRh)); - TEST_DO(checkResult(*rs, nullptr)); + checkResult(*rs, expRh); + checkResult(*rs, nullptr); } } @@ -317,14 +332,15 @@ void testScaling(const std::vector<feature_t> &initScores, PredefinedScorer scorer(std::move(finalScores)); // perform second phase ranking - EXPECT_EQUAL(2u, do_reRank(scorer, hc, 2)); + EXPECT_EQ(2u, do_reRank(scorer, hc, 2)); // check results std::unique_ptr<ResultSet> rs = hc.getResultSet(); - TEST_DO(checkResult(*rs, expected)); + checkResult(*rs, expected); } -TEST("testScaling") { +TEST(HitCollectorTest, scaling) +{ std::vector<feature_t> initScores(5); initScores[0] = 1000; initScores[1] = 2000; @@ -338,7 +354,8 @@ TEST("testScaling") { exp[i]._docId = i; } - { // scale down and adjust down + { + SCOPED_TRACE("scale down and adjust down"); exp[0]._rankValue = 0; // scaled exp[1]._rankValue = 100; // scaled exp[2]._rankValue = 200; // scaled @@ -350,9 +367,10 @@ TEST("testScaling") { finalScores[3] = 300; finalScores[4] = 400; - TEST_DO(testScaling(initScores, std::move(finalScores), exp)); + testScaling(initScores, std::move(finalScores), exp); } - { // scale down and adjust up + { + SCOPED_TRACE("scale down and adjust up"); exp[0]._rankValue = 200; // scaled exp[1]._rankValue = 300; // scaled exp[2]._rankValue = 400; // scaled @@ -364,10 +382,10 @@ TEST("testScaling") { finalScores[3] = 500; finalScores[4] = 600; - TEST_DO(testScaling(initScores, std::move(finalScores), exp)); + testScaling(initScores, std::move(finalScores), exp); } - { // scale up and adjust down - + { + SCOPED_TRACE("scale up and adjust down"); exp[0]._rankValue = -500; // scaled (-500) exp[1]._rankValue = 750; // scaled exp[2]._rankValue = 2000; // scaled @@ -379,9 +397,10 @@ TEST("testScaling") { finalScores[3] = 3250; finalScores[4] = 4500; - TEST_DO(testScaling(initScores, std::move(finalScores), exp)); + testScaling(initScores, std::move(finalScores), exp); } - { // minimal scale (second phase range = 0 (4 - 4) -> 1) + { + SCOPED_TRACE("minimal scale (second phase range = 0 (4 - 4) -> 1)"); exp[0]._rankValue = 1; // scaled exp[1]._rankValue = 2; // scaled exp[2]._rankValue = 3; // scaled @@ -393,9 +412,10 @@ TEST("testScaling") { finalScores[3] = 4; finalScores[4] = 4; - TEST_DO(testScaling(initScores, std::move(finalScores), exp)); + testScaling(initScores, std::move(finalScores), exp); } - { // minimal scale (first phase range = 0 (4000 - 4000) -> 1) + { + SCOPED_TRACE("minimal scale (first phase range = 0 (4000 - 4000) -> 1)"); std::vector<feature_t> is(initScores); is[4] = 4000; exp[0]._rankValue = -299600; // scaled @@ -409,11 +429,12 @@ TEST("testScaling") { finalScores[3] = 400; finalScores[4] = 500; - TEST_DO(testScaling(is, std::move(finalScores), exp)); + testScaling(is, std::move(finalScores), exp); } } -TEST("testOnlyBitVector") { +TEST(HitCollectorTest, only_bitvector) +{ uint32_t numDocs = 20; LOG(info, "testOnlyBitVector: test it"); { @@ -428,8 +449,8 @@ TEST("testOnlyBitVector") { std::unique_ptr<ResultSet> rs = hc.getResultSet(); std::vector<RankedHit> expRh; - TEST_DO(checkResult(*rs, expRh)); // no ranked hits - TEST_DO(checkResult(*rs, expBv.get())); // only bit vector + checkResult(*rs, expRh); // no ranked hits + checkResult(*rs, expBv.get()); // only bit vector } } @@ -443,9 +464,9 @@ struct MergeResultSetFixture { {} }; -TEST_F("require that result set is merged correctly with first phase ranking", - MergeResultSetFixture) +TEST(HitCollectorTest, require_that_result_set_is_merged_correctly_with_first_phase_ranking) { + MergeResultSetFixture f; std::vector<RankedHit> expRh; for (uint32_t i = 0; i < f.numDocs; ++i) { f.hc.addHit(i, i + 1000); @@ -457,7 +478,7 @@ TEST_F("require that result set is merged correctly with first phase ranking", expRh.back()._rankValue = (i < f.numDocs - f.maxHitsSize) ? default_rank_value : i + 1000; } std::unique_ptr<ResultSet> rs = f.hc.getResultSet(); - TEST_DO(checkResult(*rs, expRh)); + checkResult(*rs, expRh); } void @@ -474,9 +495,9 @@ addExpectedHitForMergeTest(const MergeResultSetFixture &f, std::vector<RankedHit } } -TEST_F("require that result set is merged correctly with second phase ranking (document scorer)", - MergeResultSetFixture) +TEST(HitCollectorTest, require_that_result_set_is_merged_correctly_with_second_phase_ranking_using_document_scorer) { + MergeResultSetFixture f; // with second phase ranking that triggers rescoring / scaling BasicScorer scorer(500); // second phase ranking setting score to docId + 500 std::vector<RankedHit> expRh; @@ -484,12 +505,13 @@ TEST_F("require that result set is merged correctly with second phase ranking (d f.hc.addHit(i, i + 1000); addExpectedHitForMergeTest(f, expRh, i); } - EXPECT_EQUAL(f.maxHeapSize, do_reRank(scorer, f.hc, f.maxHeapSize)); + EXPECT_EQ(f.maxHeapSize, do_reRank(scorer, f.hc, f.maxHeapSize)); std::unique_ptr<ResultSet> rs = f.hc.getResultSet(); - TEST_DO(checkResult(*rs, expRh)); + checkResult(*rs, expRh); } -TEST("require that hits can be added out of order") { +TEST(HitCollectorTest, require_that_hits_can_be_added_out_of_order) +{ HitCollector hc(1000, 100); std::vector<RankedHit> expRh; // produce expected result in normal order @@ -503,11 +525,12 @@ TEST("require that hits can be added out of order") { hc.addHit(i, i + 100); } std::unique_ptr<ResultSet> rs = hc.getResultSet(); - TEST_DO(checkResult(*rs, expRh)); - TEST_DO(checkResult(*rs, nullptr)); + checkResult(*rs, expRh); + checkResult(*rs, nullptr); } -TEST("require that hits can be added out of order when passing array limit") { +TEST(HitCollectorTest, require_that_hits_can_be_added_out_of_order_when_passing_array_limit) +{ HitCollector hc(10000, 100); std::vector<RankedHit> expRh; // produce expected result in normal order @@ -525,11 +548,12 @@ TEST("require that hits can be added out of order when passing array limit") { hc.addHit(i, i + 100); } std::unique_ptr<ResultSet> rs = hc.getResultSet(); - TEST_DO(checkResult(*rs, expRh)); - TEST_DO(checkResult(*rs, nullptr)); + checkResult(*rs, expRh); + checkResult(*rs, nullptr); } -TEST("require that hits can be added out of order only after passing array limit") { +TEST(HitCollectorTest, require_that_hits_can_be_added_out_of_order_only_after_passing_array_limit) +{ HitCollector hc(10000, 100); std::vector<RankedHit> expRh; // produce expected result in normal order @@ -548,8 +572,90 @@ TEST("require that hits can be added out of order only after passing array limit hc.addHit(i, i + 100); } std::unique_ptr<ResultSet> rs = hc.getResultSet(); - TEST_DO(checkResult(*rs, expRh)); - TEST_DO(checkResult(*rs, nullptr)); + checkResult(*rs, expRh); + checkResult(*rs, nullptr); +} + +struct RankDropFixture { + uint32_t _docid_limit; + HitCollector _hc; + std::vector<uint32_t> _dropped; + RankDropFixture(uint32_t docid_limit, uint32_t max_hits_size) + : _docid_limit(docid_limit), + _hc(docid_limit, max_hits_size) + { + } + ~RankDropFixture(); + void add(std::vector<RankedHit> hits) { + for (const auto& hit : hits) { + _hc.addHit(hit.getDocId(), hit.getRank()); + } + } + void rerank(ScoreMap score_map, size_t count) { + PredefinedScorer scorer(score_map); + EXPECT_EQ(count, do_reRank(scorer, _hc, count)); + } + std::unique_ptr<BitVector> make_bv(DocidVector docids) { + auto bv = BitVector::create(_docid_limit); + for (auto& docid : docids) { + bv->setBit(docid); + } + return bv; + } + + void setup() { + // Initial 7 hits from first phase + add({{5, 1100},{10, 1200},{11, 1300},{12, 1400},{14, 500},{15, 900},{16,1000}}); + // Rerank two best hits, calculate old and new ranges for reranked + // hits that will cause hits not reranked to later be rescored by + // dividing by 100. + rerank({{11,14},{12,13}}, 2); + } + void check_result(std::optional<double> rank_drop_limit, RankedHitVector exp_array, + std::unique_ptr<BitVector> exp_bv, DocidVector exp_dropped) { + auto rs = _hc.get_result_set(rank_drop_limit, &_dropped); + checkResult(*rs, exp_array); + checkResult(*rs, exp_bv.get()); + EXPECT_EQ(exp_dropped, _dropped); + } +}; + +RankDropFixture::~RankDropFixture() = default; + +TEST(HitCollectorTest, require_that_second_phase_rank_drop_limit_is_enforced) +{ + // Track rank score for all 7 hits from first phase + RankDropFixture f(10000, 10); + f.setup(); + f.check_result(9.0, {{5,11},{10,12},{11,14},{12,13},{16,10}}, + {}, {14, 15}); +} + +TEST(HitCollectorTest, require_that_second_phase_rank_drop_limit_is_enforced_when_docid_vector_is_used) +{ + // Track rank score for 4 best hits from first phase, overflow to docid vector + RankDropFixture f(10000, 4); + f.setup(); + f.check_result(13.0, {{11,14}}, + {}, {5,10,12,14,15,16}); +} + +TEST(HitCollectorTest, require_that_bitvector_is_not_dropped_without_second_phase_rank_drop_limit) +{ + // Track rank score for 4 best hits from first phase, overflow to bitvector + RankDropFixture f(20, 4); + f.setup(); + f.check_result(std::nullopt, {{5,11},{10,12},{11,14},{12,13}}, + f.make_bv({5,10,11,12,14,15,16}), {}); +} + +TEST(HitCollectorTest, require_that_bitvector_is_dropped_with_second_phase_rank_drop_limit) +{ + // Track rank for 4 best hits from first phase, overflow to bitvector + RankDropFixture f(20, 4); + f.setup(); + f.check_result(9.0, {{5,11},{10,12},{11,14},{12,13}}, + {}, {14,15,16}); } -TEST_MAIN() { TEST_RUN_ALL(); } +GTEST_MAIN_RUN_ALL_TESTS() diff --git a/searchlib/src/tests/hitcollector/sorted_hit_sequence_test.cpp b/searchlib/src/tests/hitcollector/sorted_hit_sequence_test.cpp index c1c3a550d9b..4eefa5b5dfa 100644 --- a/searchlib/src/tests/hitcollector/sorted_hit_sequence_test.cpp +++ b/searchlib/src/tests/hitcollector/sorted_hit_sequence_test.cpp @@ -1,7 +1,7 @@ // Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -#include <vespa/vespalib/testkit/test_kit.h> #include <vespa/searchlib/queryeval/sorted_hit_sequence.h> +#include <vespa/vespalib/gtest/gtest.h> using search::queryeval::SortedHitSequence; using Hits = std::vector<SortedHitSequence::Hit>; @@ -10,20 +10,22 @@ using Refs = std::vector<SortedHitSequence::Ref>; Hits hits({{1,10.0},{2,30.0},{3,20.0}}); Refs refs({1,2,0}); -TEST("require that empty hit sequence is empty") { +TEST(SortedHitsSEquenceTest, require_that_empty_hit_sequence_is_empty) +{ EXPECT_TRUE(!SortedHitSequence(nullptr, nullptr, 0).valid()); EXPECT_TRUE(!SortedHitSequence(&hits[0], &refs[0], 0).valid()); } -TEST("require that sorted hit sequence can be iterated") { +TEST(SortedHitsSEquenceTest, require_that_sorted_hit_sequence_can_be_iterated) +{ SortedHitSequence seq(&hits[0], &refs[0], refs.size()); for (const auto &expect: Hits({{2,30.0},{3,20.0},{1,10.0}})) { ASSERT_TRUE(seq.valid()); - EXPECT_EQUAL(expect.first, seq.get().first); - EXPECT_EQUAL(expect.second, seq.get().second); + EXPECT_EQ(expect.first, seq.get().first); + EXPECT_EQ(expect.second, seq.get().second); seq.next(); } EXPECT_TRUE(!seq.valid()); } -TEST_MAIN() { TEST_RUN_ALL(); } +GTEST_MAIN_RUN_ALL_TESTS() diff --git a/searchlib/src/tests/nearsearch/CMakeLists.txt b/searchlib/src/tests/nearsearch/CMakeLists.txt index 4f249380063..8d23b54963e 100644 --- a/searchlib/src/tests/nearsearch/CMakeLists.txt +++ b/searchlib/src/tests/nearsearch/CMakeLists.txt @@ -4,5 +4,6 @@ vespa_add_executable(searchlib_nearsearch_test_app TEST nearsearch_test.cpp DEPENDS searchlib + GTest::gtest ) vespa_add_test(NAME searchlib_nearsearch_test_app COMMAND searchlib_nearsearch_test_app) diff --git a/searchlib/src/tests/nearsearch/nearsearch_test.cpp b/searchlib/src/tests/nearsearch/nearsearch_test.cpp index 4011366c7a1..aa578108b6b 100644 --- a/searchlib/src/tests/nearsearch/nearsearch_test.cpp +++ b/searchlib/src/tests/nearsearch/nearsearch_test.cpp @@ -11,7 +11,7 @@ LOG_SETUP("nearsearch_test"); #include <vespa/searchlib/fef/matchdatalayout.h> #include <vespa/vespalib/util/stringfmt.h> #include <set> -#include <vespa/vespalib/testkit/testapp.h> +#include <vespa/vespalib/gtest/gtest.h> //////////////////////////////////////////////////////////////////////////////// // @@ -108,28 +108,20 @@ MyQuery::~MyQuery() {} // //////////////////////////////////////////////////////////////////////////////// -class Test : public vespalib::TestApp { -private: - bool testNearSearch(MyQuery &query, uint32_t matchId); +class NearSearchTest : public ::testing::Test { +protected: + void testNearSearch(MyQuery &query, uint32_t matchId, const vespalib::string& label); -public: - int Main() override; - void testBasicNear(); - void testRepeatedTerms(); + NearSearchTest(); + ~NearSearchTest() override; }; -int -Test::Main() +NearSearchTest::NearSearchTest() + : ::testing::Test() { - TEST_INIT("nearsearch_test"); - - testBasicNear(); TEST_FLUSH(); - testRepeatedTerms(); TEST_FLUSH(); - - TEST_DONE(); } -TEST_APPHOOK(Test); +NearSearchTest::~NearSearchTest() = default; //////////////////////////////////////////////////////////////////////////////// // @@ -137,84 +129,89 @@ TEST_APPHOOK(Test); // //////////////////////////////////////////////////////////////////////////////// -void -Test::testBasicNear() +TEST_F(NearSearchTest, basic_near) { MyTerm foo(UIntList().add(69), UIntList().add(6).add(11)); for (uint32_t i = 0; i <= 1; ++i) { - TEST_STATE(vespalib::make_string("i = %u", i).c_str()); - TEST_DO(testNearSearch(MyQuery(false, i).addTerm(foo), 69)); - TEST_DO(testNearSearch(MyQuery(true, i).addTerm(foo), 69)); + SCOPED_TRACE(vespalib::make_string("i = %u", i)); + testNearSearch(MyQuery(false, i).addTerm(foo), 69, "near 1"); + testNearSearch(MyQuery(true, i).addTerm(foo), 69, "onear 1"); } MyTerm bar(UIntList().add(68).add(69).add(70), UIntList().add(7).add(10)); - TEST_DO(testNearSearch(MyQuery(false, 0).addTerm(foo).addTerm(bar), 0)); - TEST_DO(testNearSearch(MyQuery(true, 0).addTerm(foo).addTerm(bar), 0)); + testNearSearch(MyQuery(false, 0).addTerm(foo).addTerm(bar), 0, "near 2"); + testNearSearch(MyQuery(true, 0).addTerm(foo).addTerm(bar), 0, "onear 2"); for (uint32_t i = 1; i <= 2; ++i) { - TEST_DO(testNearSearch(MyQuery(false, i).addTerm(foo).addTerm(bar), 69)); - TEST_DO(testNearSearch(MyQuery(true, i).addTerm(foo).addTerm(bar), 69)); + SCOPED_TRACE(vespalib::make_string("i = %u", i)); + testNearSearch(MyQuery(false, i).addTerm(foo).addTerm(bar), 69, "near 3"); + testNearSearch(MyQuery(true, i).addTerm(foo).addTerm(bar), 69, "onear 3"); } MyTerm baz(UIntList().add(69).add(70).add(71), UIntList().add(8).add(9)); for (uint32_t i = 0; i <= 1; ++i) { - TEST_DO(testNearSearch(MyQuery(false, i).addTerm(foo).addTerm(bar).addTerm(baz), 0)); - TEST_DO(testNearSearch(MyQuery(false, i).addTerm(foo).addTerm(baz).addTerm(bar), 0)); - TEST_DO(testNearSearch(MyQuery(false, i).addTerm(bar).addTerm(baz).addTerm(foo), 0)); - TEST_DO(testNearSearch(MyQuery(false, i).addTerm(bar).addTerm(foo).addTerm(baz), 0)); - TEST_DO(testNearSearch(MyQuery(false, i).addTerm(baz).addTerm(foo).addTerm(bar), 0)); - TEST_DO(testNearSearch(MyQuery(false, i).addTerm(baz).addTerm(bar).addTerm(foo), 0)); - TEST_DO(testNearSearch(MyQuery(true, i).addTerm(foo).addTerm(bar).addTerm(baz), 0)); - TEST_DO(testNearSearch(MyQuery(true, i).addTerm(foo).addTerm(baz).addTerm(bar), 0)); - TEST_DO(testNearSearch(MyQuery(true, i).addTerm(bar).addTerm(baz).addTerm(foo), 0)); - TEST_DO(testNearSearch(MyQuery(true, i).addTerm(bar).addTerm(foo).addTerm(baz), 0)); - TEST_DO(testNearSearch(MyQuery(true, i).addTerm(baz).addTerm(foo).addTerm(bar), 0)); - TEST_DO(testNearSearch(MyQuery(true, i).addTerm(baz).addTerm(bar).addTerm(foo), 0)); + SCOPED_TRACE(vespalib::make_string("i = %u", i)); + testNearSearch(MyQuery(false, i).addTerm(foo).addTerm(bar).addTerm(baz), 0, "near 10"); + testNearSearch(MyQuery(false, i).addTerm(foo).addTerm(baz).addTerm(bar), 0, "near 11"); + testNearSearch(MyQuery(false, i).addTerm(bar).addTerm(baz).addTerm(foo), 0, "near 12"); + testNearSearch(MyQuery(false, i).addTerm(bar).addTerm(foo).addTerm(baz), 0, "near 13"); + testNearSearch(MyQuery(false, i).addTerm(baz).addTerm(foo).addTerm(bar), 0, "near 14"); + testNearSearch(MyQuery(false, i).addTerm(baz).addTerm(bar).addTerm(foo), 0, "near 15"); + testNearSearch(MyQuery(true, i).addTerm(foo).addTerm(bar).addTerm(baz), 0, "onear 10"); + testNearSearch(MyQuery(true, i).addTerm(foo).addTerm(baz).addTerm(bar), 0, "onear 11"); + testNearSearch(MyQuery(true, i).addTerm(bar).addTerm(baz).addTerm(foo), 0, "onear 12"); + testNearSearch(MyQuery(true, i).addTerm(bar).addTerm(foo).addTerm(baz), 0, "onear 13"); + testNearSearch(MyQuery(true, i).addTerm(baz).addTerm(foo).addTerm(bar), 0, "onear 14"); + testNearSearch(MyQuery(true, i).addTerm(baz).addTerm(bar).addTerm(foo), 0, "onear 15"); } for (uint32_t i = 2; i <= 3; ++i) { - TEST_DO(testNearSearch(MyQuery(false, i).addTerm(foo).addTerm(bar).addTerm(baz), 69)); - TEST_DO(testNearSearch(MyQuery(false, i).addTerm(foo).addTerm(baz).addTerm(bar), 69)); - TEST_DO(testNearSearch(MyQuery(false, i).addTerm(bar).addTerm(baz).addTerm(foo), 69)); - TEST_DO(testNearSearch(MyQuery(false, i).addTerm(bar).addTerm(foo).addTerm(baz), 69)); - TEST_DO(testNearSearch(MyQuery(false, i).addTerm(baz).addTerm(foo).addTerm(bar), 69)); - TEST_DO(testNearSearch(MyQuery(false, i).addTerm(baz).addTerm(bar).addTerm(foo), 69)); - TEST_DO(testNearSearch(MyQuery(true, i).addTerm(foo).addTerm(bar).addTerm(baz), 69)); - TEST_DO(testNearSearch(MyQuery(true, i).addTerm(foo).addTerm(baz).addTerm(bar), 0)); - TEST_DO(testNearSearch(MyQuery(true, i).addTerm(bar).addTerm(baz).addTerm(foo), 0)); - TEST_DO(testNearSearch(MyQuery(true, i).addTerm(bar).addTerm(foo).addTerm(baz), 0)); - TEST_DO(testNearSearch(MyQuery(true, i).addTerm(baz).addTerm(foo).addTerm(bar), 0)); - TEST_DO(testNearSearch(MyQuery(true, i).addTerm(baz).addTerm(bar).addTerm(foo), 69)); + SCOPED_TRACE(vespalib::make_string("i = %u", i)); + testNearSearch(MyQuery(false, i).addTerm(foo).addTerm(bar).addTerm(baz), 69, "near 20"); + testNearSearch(MyQuery(false, i).addTerm(foo).addTerm(baz).addTerm(bar), 69, "near 21"); + testNearSearch(MyQuery(false, i).addTerm(bar).addTerm(baz).addTerm(foo), 69, "near 22"); + testNearSearch(MyQuery(false, i).addTerm(bar).addTerm(foo).addTerm(baz), 69, "near 23"); + testNearSearch(MyQuery(false, i).addTerm(baz).addTerm(foo).addTerm(bar), 69, "near 24"); + testNearSearch(MyQuery(false, i).addTerm(baz).addTerm(bar).addTerm(foo), 69, "near 25"); + testNearSearch(MyQuery(true, i).addTerm(foo).addTerm(bar).addTerm(baz), 69, "onear 20"); + testNearSearch(MyQuery(true, i).addTerm(foo).addTerm(baz).addTerm(bar), 0, "onear 21"); + testNearSearch(MyQuery(true, i).addTerm(bar).addTerm(baz).addTerm(foo), 0, "onear 22"); + testNearSearch(MyQuery(true, i).addTerm(bar).addTerm(foo).addTerm(baz), 0, "onear 23"); + testNearSearch(MyQuery(true, i).addTerm(baz).addTerm(foo).addTerm(bar), 0, "onear 24"); + testNearSearch(MyQuery(true, i).addTerm(baz).addTerm(bar).addTerm(foo), 69, "onear 25"); } } -void -Test::testRepeatedTerms() + +TEST_F(NearSearchTest, repeated_terms) { MyTerm foo(UIntList().add(69), UIntList().add(1).add(2).add(3)); - TEST_DO(testNearSearch(MyQuery(false, 0).addTerm(foo).addTerm(foo), 69)); - TEST_DO(testNearSearch(MyQuery(true, 0).addTerm(foo).addTerm(foo), 0)); + testNearSearch(MyQuery(false, 0).addTerm(foo).addTerm(foo), 69, "near 50"); + testNearSearch(MyQuery(true, 0).addTerm(foo).addTerm(foo), 0, "onear 50"); for (uint32_t i = 1; i <= 2; ++i) { - TEST_DO(testNearSearch(MyQuery(false, i).addTerm(foo).addTerm(foo), 69)); - TEST_DO(testNearSearch(MyQuery(true, i).addTerm(foo).addTerm(foo), 69)); + SCOPED_TRACE(vespalib::make_string("i = %u", i)); + testNearSearch(MyQuery(false, i).addTerm(foo).addTerm(foo), 69, "near 51"); + testNearSearch(MyQuery(true, i).addTerm(foo).addTerm(foo), 69, "onear 51"); } for (uint32_t i = 0; i <= 1; ++i) { - TEST_DO(testNearSearch(MyQuery(false, i).addTerm(foo).addTerm(foo).addTerm(foo), 69)); - TEST_DO(testNearSearch(MyQuery(true, i).addTerm(foo).addTerm(foo).addTerm(foo), 0)); + SCOPED_TRACE(vespalib::make_string("i = %u", i)); + testNearSearch(MyQuery(false, i).addTerm(foo).addTerm(foo).addTerm(foo), 69, "near 52"); + testNearSearch(MyQuery(true, i).addTerm(foo).addTerm(foo).addTerm(foo), 0, "onear 52"); } for (uint32_t i = 2; i <= 3; ++i) { - TEST_DO(testNearSearch(MyQuery(false, i).addTerm(foo).addTerm(foo).addTerm(foo), 69)); - TEST_DO(testNearSearch(MyQuery(true, i).addTerm(foo).addTerm(foo).addTerm(foo), 69)); + SCOPED_TRACE(vespalib::make_string("i = %u", i)); + testNearSearch(MyQuery(false, i).addTerm(foo).addTerm(foo).addTerm(foo), 69, "near 53"); + testNearSearch(MyQuery(true, i).addTerm(foo).addTerm(foo).addTerm(foo), 69, "onear 53"); } } -bool -Test::testNearSearch(MyQuery &query, uint32_t matchId) +void +NearSearchTest::testNearSearch(MyQuery &query, uint32_t matchId, const vespalib::string& label) { - LOG(info, "testNearSearch(%d)", matchId); + SCOPED_TRACE(vespalib::make_string("%s - %u", label.c_str(), matchId)); search::queryeval::IntermediateBlueprint *near_b = nullptr; if (query.isOrdered()) { near_b = new search::queryeval::ONearBlueprint(query.getWindow()); @@ -240,13 +237,14 @@ Test::testNearSearch(MyQuery &query, uint32_t matchId) if (docId == matchId) { foundMatch = true; } else { - LOG(info, "Document %d matched unexpectedly.", docId); - return false; + FAIL() << "Document " << docId << " matched unexpectedly."; } } if (matchId == 0) { - return EXPECT_TRUE(!foundMatch); + EXPECT_TRUE(!foundMatch); } else { - return EXPECT_TRUE(foundMatch); + EXPECT_TRUE(foundMatch); } } + +GTEST_MAIN_RUN_ALL_TESTS() diff --git a/searchlib/src/tests/query/CMakeLists.txt b/searchlib/src/tests/query/CMakeLists.txt index 0cb6c9413b0..39ef50b1d8c 100644 --- a/searchlib/src/tests/query/CMakeLists.txt +++ b/searchlib/src/tests/query/CMakeLists.txt @@ -18,6 +18,7 @@ vespa_add_executable(searchlib_templatetermvisitor_test_app TEST templatetermvisitor_test.cpp DEPENDS searchlib + GTest::gtest ) vespa_add_test(NAME searchlib_templatetermvisitor_test_app COMMAND searchlib_templatetermvisitor_test_app) vespa_add_executable(searchlib_querybuilder_test_app TEST diff --git a/searchlib/src/tests/query/templatetermvisitor_test.cpp b/searchlib/src/tests/query/templatetermvisitor_test.cpp index b6dd6ceab8d..591aaffcbee 100644 --- a/searchlib/src/tests/query/templatetermvisitor_test.cpp +++ b/searchlib/src/tests/query/templatetermvisitor_test.cpp @@ -1,14 +1,11 @@ // Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. // Unit tests for templatetermvisitor. -#include <vespa/log/log.h> -LOG_SETUP("templatetermvisitor_test"); - #include <vespa/searchlib/query/tree/intermediatenodes.h> #include <vespa/searchlib/query/tree/templatetermvisitor.h> #include <vespa/searchlib/query/tree/simplequery.h> #include <vespa/searchlib/query/tree/termnodes.h> -#include <vespa/vespalib/testkit/testapp.h> +#include <vespa/vespalib/gtest/gtest.h> using namespace search::query; @@ -16,23 +13,6 @@ namespace { class MyVisitor; -class Test : public vespalib::TestApp { - void requireThatAllTermsCanBeVisited(); - -public: - int Main() override; -}; - -int -Test::Main() -{ - TEST_INIT("templatetermvisitor_test"); - - TEST_DO(requireThatAllTermsCanBeVisited()); - - TEST_DONE(); -} - class MyVisitor : public TemplateTermVisitor<MyVisitor, SimpleQueryNodeTypes> { public: @@ -60,7 +40,8 @@ bool checkVisit() { return checkVisit(new T(typename T::Type(), "field", 0, Weight(0))); } -void Test::requireThatAllTermsCanBeVisited() { +TEST(TemplateTermVisitorTest, require_that_all_terms_can_be_visited) +{ EXPECT_TRUE(checkVisit<SimpleNumberTerm>()); EXPECT_TRUE(checkVisit<SimpleLocationTerm>()); EXPECT_TRUE(checkVisit<SimplePrefixTerm>()); @@ -83,4 +64,4 @@ void Test::requireThatAllTermsCanBeVisited() { } // namespace -TEST_APPHOOK(Test); +GTEST_MAIN_RUN_ALL_TESTS() diff --git a/searchlib/src/tests/queryeval/blueprint/CMakeLists.txt b/searchlib/src/tests/queryeval/blueprint/CMakeLists.txt index e46ad1085e3..ef8d974151a 100644 --- a/searchlib/src/tests/queryeval/blueprint/CMakeLists.txt +++ b/searchlib/src/tests/queryeval/blueprint/CMakeLists.txt @@ -11,6 +11,7 @@ vespa_add_executable(searchlib_leaf_blueprints_test_app TEST leaf_blueprints_test.cpp DEPENDS searchlib + GTest::gtest ) vespa_add_test(NAME searchlib_leaf_blueprints_test_app COMMAND searchlib_leaf_blueprints_test_app || diff -u lhs.out rhs.out) vespa_add_executable(searchlib_intermediate_blueprints_test_app TEST diff --git a/searchlib/src/tests/queryeval/blueprint/blueprint_test.cpp b/searchlib/src/tests/queryeval/blueprint/blueprint_test.cpp index 485410e0eba..f7745da174c 100644 --- a/searchlib/src/tests/queryeval/blueprint/blueprint_test.cpp +++ b/searchlib/src/tests/queryeval/blueprint/blueprint_test.cpp @@ -13,7 +13,7 @@ LOG_SETUP("blueprint_test"); using namespace search::queryeval; -using namespace search::fef; +using MatchData = search::fef::MatchData; namespace { @@ -44,9 +44,7 @@ public: } SearchIterator::UP - createIntermediateSearch(MultiSearch::Children subSearches, - MatchData &md) const override - { + createIntermediateSearch(MultiSearch::Children subSearches, MatchData &md) const override { return std::make_unique<MySearch>("or", std::move(subSearches), &md, strict()); } SearchIteratorUP createFilterSearch(FilterConstraint constraint) const override { @@ -63,9 +61,7 @@ class OtherOr : public OrBlueprint private: public: SearchIterator::UP - createIntermediateSearch(MultiSearch::Children subSearches, - MatchData &md) const override - { + createIntermediateSearch(MultiSearch::Children subSearches, MatchData &md) const override { return std::make_unique<MySearch>("or", std::move(subSearches), &md, strict()); } @@ -89,9 +85,7 @@ public: } SearchIterator::UP - createIntermediateSearch(MultiSearch::Children subSearches, - MatchData &md) const override - { + createIntermediateSearch(MultiSearch::Children subSearches, MatchData &md) const override { return std::make_unique<MySearch>("and", std::move(subSearches), &md, strict()); } @@ -106,9 +100,7 @@ class OtherAnd : public AndBlueprint private: public: SearchIterator::UP - createIntermediateSearch(MultiSearch::Children subSearches, - MatchData &md) const override - { + createIntermediateSearch(MultiSearch::Children subSearches, MatchData &md) const override { return std::make_unique<MySearch>("and", std::move(subSearches), &md, strict()); } @@ -121,9 +113,7 @@ class OtherAndNot : public AndNotBlueprint { public: SearchIterator::UP - createIntermediateSearch(MultiSearch::Children subSearches, - MatchData &md) const override - { + createIntermediateSearch(MultiSearch::Children subSearches, MatchData &md) const override { return std::make_unique<MySearch>("andnot", std::move(subSearches), &md, strict()); } @@ -658,6 +648,7 @@ getExpectedBlueprint() " strict_cost: 0\n" " sourceId: 4294967295\n" " docid_limit: 0\n" + " id: 0\n" " strict: false\n" " children: std::vector {\n" " [0]: (anonymous namespace)::MyTerm {\n" @@ -681,6 +672,7 @@ getExpectedBlueprint() " strict_cost: 0\n" " sourceId: 4294967295\n" " docid_limit: 0\n" + " id: 0\n" " strict: false\n" " }\n" " }\n" @@ -714,6 +706,7 @@ getExpectedSlimeBlueprint() { " strict_cost: 0.0," " sourceId: 4294967295," " docid_limit: 0," + " id: 0," " strict: false," " children: {" " '[type]': 'std::vector'," @@ -742,6 +735,7 @@ getExpectedSlimeBlueprint() { " strict_cost: 0.0," " sourceId: 4294967295," " docid_limit: 0," + " id: 0," " strict: false" " }" " }" @@ -852,6 +846,30 @@ TEST("self strict resolving during sort") { } } +void check_ids(Blueprint &bp, const std::vector<uint32_t> &expect) { + std::vector<uint32_t> actual; + bp.each_node_post_order([&](auto &node){ actual.push_back(node.id()); }); + ASSERT_EQUAL(actual.size(), expect.size()); + for (size_t i = 0; i < actual.size(); ++i) { + EXPECT_EQUAL(actual[i], expect[i]); + } +} + +TEST("blueprint node enumeration") { + auto a = std::make_unique<AndBlueprint>(); + a->addChild(std::make_unique<MyLeaf>()); + a->addChild(std::make_unique<MyLeaf>()); + auto b = std::make_unique<AndBlueprint>(); + b->addChild(std::make_unique<MyLeaf>()); + b->addChild(std::make_unique<MyLeaf>()); + auto root = std::make_unique<OrBlueprint>(); + root->addChild(std::move(a)); + root->addChild(std::move(b)); + TEST_DO(check_ids(*root, {0,0,0,0,0,0,0})); + root->enumerate(1); + TEST_DO(check_ids(*root, {3,4,2,6,7,5,1})); +} + TEST_MAIN() { TEST_DEBUG("lhs.out", "rhs.out"); TEST_RUN_ALL(); diff --git a/searchlib/src/tests/queryeval/blueprint/intermediate_blueprints_test.cpp b/searchlib/src/tests/queryeval/blueprint/intermediate_blueprints_test.cpp index bddc9f92111..490f221d1d8 100644 --- a/searchlib/src/tests/queryeval/blueprint/intermediate_blueprints_test.cpp +++ b/searchlib/src/tests/queryeval/blueprint/intermediate_blueprints_test.cpp @@ -27,8 +27,9 @@ LOG_SETUP("blueprint_test"); using namespace search::queryeval; -using namespace search::fef; using namespace search::query; +using search::fef::MatchData; +using search::queryeval::Blueprint; using search::BitVector; using BlueprintVector = std::vector<std::unique_ptr<Blueprint>>; using vespalib::Slime; @@ -575,7 +576,9 @@ void compare(const Blueprint &bp1, const Blueprint &bp2, bool expect_eq) { bp1.asSlime(SlimeInserter(a)); bp2.asSlime(SlimeInserter(b)); if (expect_eq) { - EXPECT_TRUE(vespalib::slime::are_equal(a.get(), b.get(), cmp_hook)); + if(!EXPECT_TRUE(vespalib::slime::are_equal(a.get(), b.get(), cmp_hook))) { + fprintf(stderr, "a: %s\n\nb: %s\n\n", bp1.asString().c_str(), bp2.asString().c_str()); + } } else { EXPECT_FALSE(vespalib::slime::are_equal(a.get(), b.get(), cmp_hook)); } @@ -613,7 +616,6 @@ TEST_F("test SourceBlender below AND partial optimization", SourceBlenderTestFix auto expect = std::make_unique<AndBlueprint>(); addLeafs(*expect, {1,2,3}); - expect->addChild(addLeafsWithSourceId(std::make_unique<SourceBlenderBlueprint>(f.selector_2), {{10, 1}, {20, 2}})); auto blender = std::make_unique<SourceBlenderBlueprint>(f.selector_1); blender->addChild(addLeafsWithSourceId(3, std::make_unique<AndBlueprint>(), {{30, 3}, {300, 3}})); @@ -621,6 +623,8 @@ TEST_F("test SourceBlender below AND partial optimization", SourceBlenderTestFix blender->addChild(addLeafsWithSourceId(1, std::make_unique<AndBlueprint>(), {{10, 1}, {100, 1}, {1000, 1}})); expect->addChild(std::move(blender)); + expect->addChild(addLeafsWithSourceId(std::make_unique<SourceBlenderBlueprint>(f.selector_2), {{10, 1}, {20, 2}})); + optimize_and_compare(std::move(top), std::move(expect)); } @@ -1401,7 +1405,7 @@ TEST("cost for ANDNOT") { TEST("cost for SB") { InvalidSelector sel; - verify_cost(make::SB(sel), 1.3, 1.3); // max + verify_cost(make::SB(sel), 1.3+1.0, 1.3+(1.0-0.8*0.7*0.5)); // max, non_strict+1.0, strict+est } TEST("cost for NEAR") { diff --git a/searchlib/src/tests/queryeval/blueprint/leaf_blueprints_test.cpp b/searchlib/src/tests/queryeval/blueprint/leaf_blueprints_test.cpp index cb5473babbd..ea7f3d8fdc9 100644 --- a/searchlib/src/tests/queryeval/blueprint/leaf_blueprints_test.cpp +++ b/searchlib/src/tests/queryeval/blueprint/leaf_blueprints_test.cpp @@ -1,33 +1,20 @@ // Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -#include <vespa/vespalib/testkit/testapp.h> #include <vespa/searchlib/queryeval/blueprint.h> #include <vespa/searchlib/queryeval/leaf_blueprints.h> #include <vespa/searchlib/fef/matchdata.h> - -#include <vespa/log/log.h> -LOG_SETUP("blueprint_test"); +#include <vespa/vespalib/gtest/gtest.h> using namespace search::queryeval; using namespace search::fef; -class Test : public vespalib::TestApp -{ -public: - void testEmptyBlueprint(); - void testSimpleBlueprint(); - void testFakeBlueprint(); - int Main() override; -}; - -void -Test::testEmptyBlueprint() +TEST(LeafBlueprintsTest, empty_blueprint) { MatchData::UP md(MatchData::makeTestInstance(100, 10)); EmptyBlueprint empty(FieldSpecBase(1, 11)); ASSERT_TRUE(empty.getState().numFields() == 1u); - EXPECT_EQUAL(1u, empty.getState().field(0).getFieldId()); - EXPECT_EQUAL(11u, empty.getState().field(0).getHandle()); + EXPECT_EQ(1u, empty.getState().field(0).getFieldId()); + EXPECT_EQ(11u, empty.getState().field(0).getHandle()); empty.basic_plan(true, 100); empty.fetchPostings(ExecuteInfo::FULL); @@ -36,18 +23,17 @@ Test::testEmptyBlueprint() SimpleResult res; res.search(*search); SimpleResult expect; // empty - EXPECT_EQUAL(res, expect); + EXPECT_EQ(res, expect); } -void -Test::testSimpleBlueprint() +TEST(LeafBlueprintsTest, simple_blueprint) { MatchData::UP md(MatchData::makeTestInstance(100, 10)); SimpleResult a; a.addHit(3).addHit(5).addHit(7); SimpleBlueprint simple(a); simple.tag("tag"); - EXPECT_EQUAL("tag", simple.tag()); + EXPECT_EQ("tag", simple.tag()); simple.basic_plan(true, 100); simple.fetchPostings(ExecuteInfo::FULL); SearchIterator::UP search = simple.createSearch(*md); @@ -56,11 +42,10 @@ Test::testSimpleBlueprint() res.search(*search); SimpleResult expect; expect.addHit(3).addHit(5).addHit(7); - EXPECT_EQUAL(res, expect); + EXPECT_EQ(res, expect); } -void -Test::testFakeBlueprint() +TEST(LeafBlueprintsTest, fake_blueprint) { MatchData::UP md(MatchData::makeTestInstance(100, 10)); FakeResult fake; @@ -76,36 +61,36 @@ Test::testFakeBlueprint() SearchIterator::UP search = orig.createSearch(*md); search->initFullRange(); EXPECT_TRUE(!search->seek(1u)); - EXPECT_EQUAL(10u, search->getDocId()); + EXPECT_EQ(10u, search->getDocId()); { search->unpack(10u); TermFieldMatchData &data = *md->resolveTermField(handle); - EXPECT_EQUAL(fieldId, data.getFieldId()); - EXPECT_EQUAL(10u, data.getDocId()); - EXPECT_EQUAL(10u, data.getDocId()); + EXPECT_EQ(fieldId, data.getFieldId()); + EXPECT_EQ(10u, data.getDocId()); + EXPECT_EQ(10u, data.getDocId()); FieldPositionsIterator itr = data.getIterator(); - EXPECT_EQUAL(50u, itr.getFieldLength()); - EXPECT_EQUAL(2u, itr.size()); + EXPECT_EQ(50u, itr.getFieldLength()); + EXPECT_EQ(2u, itr.size()); ASSERT_TRUE(itr.valid()); - EXPECT_EQUAL(2u, itr.getPosition()); + EXPECT_EQ(2u, itr.getPosition()); itr.next(); ASSERT_TRUE(itr.valid()); - EXPECT_EQUAL(3u, itr.getPosition()); + EXPECT_EQ(3u, itr.getPosition()); itr.next(); EXPECT_TRUE(!itr.valid()); } EXPECT_TRUE(search->seek(25)); - EXPECT_EQUAL(25u, search->getDocId()); + EXPECT_EQ(25u, search->getDocId()); { search->unpack(25u); TermFieldMatchData &data = *md->resolveTermField(handle); - EXPECT_EQUAL(fieldId, data.getFieldId()); - EXPECT_EQUAL(25u, data.getDocId()); + EXPECT_EQ(fieldId, data.getFieldId()); + EXPECT_EQ(25u, data.getDocId()); FieldPositionsIterator itr = data.getIterator(); - EXPECT_EQUAL(10u, itr.getFieldLength()); - EXPECT_EQUAL(1u, itr.size()); + EXPECT_EQ(10u, itr.getFieldLength()); + EXPECT_EQ(1u, itr.size()); ASSERT_TRUE(itr.valid()); - EXPECT_EQUAL(5u, itr.getPosition()); + EXPECT_EQ(5u, itr.getPosition()); itr.next(); EXPECT_TRUE(!itr.valid()); } @@ -113,14 +98,4 @@ Test::testFakeBlueprint() EXPECT_TRUE(search->isAtEnd()); } -int -Test::Main() -{ - TEST_INIT("leaf_blueprints_test"); - testEmptyBlueprint(); - testSimpleBlueprint(); - testFakeBlueprint(); - TEST_DONE(); -} - -TEST_APPHOOK(Test); +GTEST_MAIN_RUN_ALL_TESTS() diff --git a/searchlib/src/tests/queryeval/filter_search/filter_search_test.cpp b/searchlib/src/tests/queryeval/filter_search/filter_search_test.cpp index 16e78f77eec..9fdf1417a92 100644 --- a/searchlib/src/tests/queryeval/filter_search/filter_search_test.cpp +++ b/searchlib/src/tests/queryeval/filter_search/filter_search_test.cpp @@ -356,7 +356,7 @@ DotProductAdapter::~DotProductAdapter() = default; struct ParallelWeakAndAdapter { FieldSpec field; ParallelWeakAndBlueprint blueprint; - ParallelWeakAndAdapter() : field("foo", 3, 7), blueprint(field, 100, 0.0, 1.0) {} + ParallelWeakAndAdapter() : field("foo", 3, 7), blueprint(field, 100, 0.0, 1.0, true) {} void addChild(std::unique_ptr<Blueprint> child) { auto child_field = blueprint.getNextChildField(field); auto term = std::make_unique<LeafProxy>(child_field, std::move(child)); diff --git a/searchlib/src/tests/queryeval/flow/queryeval_flow_test.cpp b/searchlib/src/tests/queryeval/flow/queryeval_flow_test.cpp index 57fddb0a819..d6008136d73 100644 --- a/searchlib/src/tests/queryeval/flow/queryeval_flow_test.cpp +++ b/searchlib/src/tests/queryeval/flow/queryeval_flow_test.cpp @@ -1,6 +1,7 @@ // Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. #include <vespa/searchlib/queryeval/flow.h> +#include <vespa/searchlib/queryeval/flow_tuning.h> #include <vespa/vespalib/gtest/gtest.h> #include <vector> #include <random> @@ -349,6 +350,35 @@ TEST(FlowTest, blender_flow_cost_accumulation_is_max) { } } +double my_non_strict_cost(double est, double adjust) { + return (1.0/adjust) * flow::forced_strict_cost(FlowStats(est, 0.0, est), adjust); +} + +TEST(FlowTest, non_strict_btree_cost) { + for (double est: {0.001, 0.01, 0.1, 0.2, 0.3, 0.5, 0.75, 1.0}) { + auto prev = FlowStats(est, 1.0, est); + auto base = FlowStats(est, flow::non_strict_cost_of_strict_iterator(est, est), est); + auto opt05 = FlowStats(est, my_non_strict_cost(est, 0.5), est); + auto opt02 = FlowStats(est, my_non_strict_cost(est, 0.2), est); + auto opt01 = FlowStats(est, my_non_strict_cost(est, 0.1), est); + auto opt005 = FlowStats(est, my_non_strict_cost(est, 0.05), est); + auto opt003 = FlowStats(est, my_non_strict_cost(est, 0.03), est); + EXPECT_NEAR(strict_crossover(opt05), 0.5, 1e-6); + EXPECT_NEAR(strict_crossover(opt02), 0.2, 1e-6); + EXPECT_NEAR(strict_crossover(opt01), 0.1, 1e-6); + EXPECT_NEAR(strict_crossover(opt005), 0.05, 1e-6); + EXPECT_NEAR(strict_crossover(opt003), 0.03, 1e-6); + fprintf(stderr, "est: %5.3f\n", est); + fprintf(stderr, " prev crossover: %6.4f (cost: %6.4f)\n", strict_crossover(prev), prev.cost); + fprintf(stderr, " base crossover: %6.4f (cost: %6.4f)\n", strict_crossover(base), base.cost); + fprintf(stderr, " 0.5 crossover: %6.4f (cost: %6.4f)\n", strict_crossover(opt05), opt05.cost); + fprintf(stderr, " 0.2 crossover: %6.4f (cost: %6.4f)\n", strict_crossover(opt02), opt02.cost); + fprintf(stderr, " 0.1 crossover: %6.4f (cost: %6.4f)\n", strict_crossover(opt01), opt01.cost); + fprintf(stderr, " 0.05 crossover: %6.4f (cost: %6.4f)\n", strict_crossover(opt005), opt005.cost); + fprintf(stderr, " 0.03 crossover: %6.4f (cost: %6.4f)\n", strict_crossover(opt003), opt003.cost); + } +} + TEST(FlowTest, optimal_and_flow) { for (size_t i = 0; i < loop_cnt; ++i) { for (bool strict: {false, true}) { diff --git a/searchlib/src/tests/queryeval/iterator_benchmark/intermediate_blueprint_factory.cpp b/searchlib/src/tests/queryeval/iterator_benchmark/intermediate_blueprint_factory.cpp index 8591ec1415d..51177850155 100644 --- a/searchlib/src/tests/queryeval/iterator_benchmark/intermediate_blueprint_factory.cpp +++ b/searchlib/src/tests/queryeval/iterator_benchmark/intermediate_blueprint_factory.cpp @@ -2,14 +2,14 @@ #include "intermediate_blueprint_factory.h" #include <vespa/searchlib/queryeval/intermediate_blueprints.h> +#include <vespa/searchlib/attribute/singlenumericattribute.h> #include <iomanip> #include <sstream> namespace search::queryeval::test { -template <typename BlueprintType> char -IntermediateBlueprintFactory<BlueprintType>::child_name(void* blueprint) const +IntermediateBlueprintFactory::child_name(void* blueprint) const { auto itr = _child_names.find(blueprint); if (itr != _child_names.end()) { @@ -18,35 +18,33 @@ IntermediateBlueprintFactory<BlueprintType>::child_name(void* blueprint) const return '?'; } -template <typename BlueprintType> -IntermediateBlueprintFactory<BlueprintType>::IntermediateBlueprintFactory(vespalib::stringref name) +IntermediateBlueprintFactory::IntermediateBlueprintFactory(vespalib::stringref name) : _name(name), _children(), _child_names() { } -template <typename BlueprintType> -IntermediateBlueprintFactory<BlueprintType>::~IntermediateBlueprintFactory() = default; +IntermediateBlueprintFactory::~IntermediateBlueprintFactory() = default; -template <typename BlueprintType> std::unique_ptr<Blueprint> -IntermediateBlueprintFactory<BlueprintType>::make_blueprint() +IntermediateBlueprintFactory::make_blueprint() { - auto res = std::make_unique<BlueprintType>(); + auto res = make_self(); _child_names.clear(); char name = 'A'; + uint32_t source = 1; for (const auto& factory : _children) { auto child = factory->make_blueprint(); _child_names[child.get()] = name++; + child->setSourceId(source++); // ignored by non-source-blender blueprints res->addChild(std::move(child)); } return res; } -template <typename BlueprintType> vespalib::string -IntermediateBlueprintFactory<BlueprintType>::get_name(Blueprint& blueprint) const +IntermediateBlueprintFactory::get_name(Blueprint& blueprint) const { auto* intermediate = blueprint.asIntermediate(); if (intermediate != nullptr) { @@ -69,11 +67,29 @@ IntermediateBlueprintFactory<BlueprintType>::get_name(Blueprint& blueprint) cons return get_class_name(blueprint); } -template class IntermediateBlueprintFactory<AndBlueprint>; +//----------------------------------------------------------------------------- AndBlueprintFactory::AndBlueprintFactory() - : IntermediateBlueprintFactory<AndBlueprint>("AND") + : IntermediateBlueprintFactory("AND") {} +std::unique_ptr<IntermediateBlueprint> +AndBlueprintFactory::make_self() const +{ + return std::make_unique<AndBlueprint>(); +} + +//----------------------------------------------------------------------------- + +SourceBlenderBlueprintFactory::SourceBlenderBlueprintFactory() + : IntermediateBlueprintFactory("SB"), + _selector(250, "my_source_blender", 1000) +{} + +std::unique_ptr<IntermediateBlueprint> +SourceBlenderBlueprintFactory::make_self() const +{ + return std::make_unique<SourceBlenderBlueprint>(_selector); } +} diff --git a/searchlib/src/tests/queryeval/iterator_benchmark/intermediate_blueprint_factory.h b/searchlib/src/tests/queryeval/iterator_benchmark/intermediate_blueprint_factory.h index 6f7fe4f9ee7..c791d866612 100644 --- a/searchlib/src/tests/queryeval/iterator_benchmark/intermediate_blueprint_factory.h +++ b/searchlib/src/tests/queryeval/iterator_benchmark/intermediate_blueprint_factory.h @@ -4,6 +4,7 @@ #include "benchmark_blueprint_factory.h" #include <vespa/searchlib/queryeval/intermediate_blueprints.h> +#include <vespa/searchlib/attribute/fixedsourceselector.h> #include <unordered_map> namespace search::queryeval::test { @@ -11,7 +12,6 @@ namespace search::queryeval::test { /** * Factory that creates an IntermediateBlueprint (of the given type) with children created by the given factories. */ -template <typename BlueprintType> class IntermediateBlueprintFactory : public BenchmarkBlueprintFactory { private: vespalib::string _name; @@ -19,7 +19,8 @@ private: std::unordered_map<void*, char> _child_names; char child_name(void* blueprint) const; - +protected: + virtual std::unique_ptr<IntermediateBlueprint> make_self() const = 0; public: IntermediateBlueprintFactory(vespalib::stringref name); ~IntermediateBlueprintFactory(); @@ -30,10 +31,26 @@ public: vespalib::string get_name(Blueprint& blueprint) const override; }; -class AndBlueprintFactory : public IntermediateBlueprintFactory<AndBlueprint> { +class AndBlueprintFactory : public IntermediateBlueprintFactory { +protected: + std::unique_ptr<IntermediateBlueprint> make_self() const override; public: AndBlueprintFactory(); }; -} +class SourceBlenderBlueprintFactory : public IntermediateBlueprintFactory +{ +private: + FixedSourceSelector _selector; +protected: + std::unique_ptr<IntermediateBlueprint> make_self() const override; +public: + SourceBlenderBlueprintFactory(); + void init_selector(auto f, uint32_t limit) { + for (uint32_t i = 0; i < limit; ++i) { + _selector.setSource(i, f(i)); + } + } +}; +} diff --git a/searchlib/src/tests/queryeval/iterator_benchmark/iterator_benchmark_test.cpp b/searchlib/src/tests/queryeval/iterator_benchmark/iterator_benchmark_test.cpp index f4a1ade8a66..e74fefac70e 100644 --- a/searchlib/src/tests/queryeval/iterator_benchmark/iterator_benchmark_test.cpp +++ b/searchlib/src/tests/queryeval/iterator_benchmark/iterator_benchmark_test.cpp @@ -13,19 +13,31 @@ #include <vector> using namespace search::attribute; -using namespace search::fef; using namespace search::queryeval::test; using namespace search::queryeval; using namespace search; using namespace vespalib; using search::index::Schema; +using search::fef::MatchData; using vespalib::make_string_short::fmt; const vespalib::string field_name = "myfield"; double budget_sec = 1.0; +double estimate_actual_cost(Blueprint &bp, InFlow in_flow) { + if (in_flow.strict()) { + assert(bp.strict()); + return bp.strict_cost(); + } else if (bp.strict()) { + auto stats = FlowStats::from(flow::DefaultAdapter(), &bp); + return flow::forced_strict_cost(stats, in_flow.rate()); + } else { + return bp.cost() * in_flow.rate(); + } +} + enum class PlanningAlgo { Order, Estimate, @@ -236,7 +248,8 @@ strict_search(BenchmarkBlueprintFactory& factory, uint32_t docid_limit, Planning timer.after(); } FlowStats flow(ctx.blueprint->estimate(), ctx.blueprint->cost(), ctx.blueprint->strict_cost()); - return {timer.min_time() * 1000.0, hits + 1, hits, flow, flow.strict_cost, get_class_name(*ctx.iterator), factory.get_name(*ctx.blueprint)}; + double actual_cost = estimate_actual_cost(*ctx.blueprint, InFlow(true)); + return {timer.min_time() * 1000.0, hits + 1, hits, flow, actual_cost, get_class_name(*ctx.iterator), factory.get_name(*ctx.blueprint)}; } template <bool do_unpack> @@ -269,7 +282,7 @@ non_strict_search(BenchmarkBlueprintFactory& factory, uint32_t docid_limit, doub timer.after(); } FlowStats flow(ctx.blueprint->estimate(), ctx.blueprint->cost(), ctx.blueprint->strict_cost()); - double actual_cost = flow.cost * filter_hit_ratio; + double actual_cost = estimate_actual_cost(*ctx.blueprint, InFlow(filter_hit_ratio)); return {timer.min_time() * 1000.0, seeks, hits, flow, actual_cost, get_class_name(*ctx.iterator), factory.get_name(*ctx.blueprint)}; } @@ -291,10 +304,6 @@ benchmark_search(BenchmarkBlueprintFactory& factory, uint32_t docid_limit, bool } } - - - - //----------------------------------------------------------------------------- double est_forced_strict_cost(double estimate, double strict_cost, double rate) { @@ -317,26 +326,26 @@ struct Sample { } }; -double find_crossover(const char *type, const auto &calculate_at, double delta) { +double find_crossover(const char *type, const char *a, const char *b, const auto &calculate_at, double delta) { double min = delta; double max = 1.0; fprintf(stderr, "looking for %s crossover in the range [%g, %g]...\n", type, min, max); auto at_min = calculate_at(min); auto at_max = calculate_at(max); - fprintf(stderr, " before: [%s, %s], after: [%s, %s]\n", - at_min.first.str().c_str(), at_max.first.str().c_str(), - at_min.second.str().c_str(), at_max.second.str().c_str()); - auto best_before = [](auto values) { return (values.first < values.second); }; - if (best_before(at_min) == best_before(at_max)) { + fprintf(stderr, " %s: [%s, %s], %s: [%s, %s]\n", + a, at_min.first.str().c_str(), at_max.first.str().c_str(), + b, at_min.second.str().c_str(), at_max.second.str().c_str()); + auto a_best = [](auto values) { return (values.first < values.second); }; + if (a_best(at_min) == a_best(at_max)) { fprintf(stderr, " NO %s CROSSOVER FOUND\n", type); return 0.0; } while (max > (min + delta)) { double x = (min + max) / 2.0; auto at_x = calculate_at(x); - fprintf(stderr, " best@%g: %s (%s vs %s)\n", x, best_before(at_x) ? "before" : "after", + fprintf(stderr, " best@%g: %s (%s vs %s)\n", x, a_best(at_x) ? a : b, at_x.first.str().c_str(), at_x.second.str().c_str()); - if (best_before(at_min) == best_before(at_x)) { + if (a_best(at_min) == a_best(at_x)) { min = x; at_min = at_x; } else { @@ -409,11 +418,11 @@ void analyze_crossover(BenchmarkBlueprintFactory &fixed, std::function<std::uniq std::vector<double> results; std::vector<const char *> names; names.push_back("time crossover"); - results.push_back(find_crossover("TIME", combine(estimate_AND_time_ms), delta)); + results.push_back(find_crossover("TIME", "before", "after", combine(estimate_AND_time_ms), delta)); names.push_back("cost crossover"); - results.push_back(find_crossover("COST", combine(calculate_AND_cost), delta)); + results.push_back(find_crossover("COST", "before", "after", combine(calculate_AND_cost), delta)); names.push_back("abs_est crossover"); - results.push_back(find_crossover("ABS_EST", combine(first_abs_est), delta)); + results.push_back(find_crossover("ABS_EST", "before", "after", combine(first_abs_est), delta)); sample_at("COST", combine(calculate_AND_cost), results, names); sample_at("TIME", combine(estimate_AND_time_ms), results, names); } @@ -429,21 +438,37 @@ to_string(bool val) void print_result_header() { - std::cout << "| chn | f_ratio | o_ratio | a_ratio | f.est | f.cost | f.scost | hits | seeks | time_ms | act_cost | ns_per_seek | ms_per_act_cost | iterator | blueprint |" << std::endl; + std::cout << "| in_flow | chn | o_ratio | a_ratio | f.est | f.cost | f.act_cost | f.scost | f.act_scost | hits | seeks | time_ms | act_cost | ns_per_seek | ms_per_act_cost | iterator | blueprint |" << std::endl; +} + +std::ostream &operator<<(std::ostream &dst, InFlow in_flow) { + auto old_w = dst.width(); + auto old_p = dst.precision(); + dst << std::setw(7) << std::setprecision(5); + if (in_flow.strict()) { + dst << " STRICT"; + } else { + dst << in_flow.rate(); + } + dst << std::setw(old_w); + dst << std::setprecision(old_p); + return dst; } void -print_result(const BenchmarkResult& res, uint32_t children, double op_hit_ratio, double filter_hit_ratio, uint32_t num_docs) +print_result(const BenchmarkResult& res, uint32_t children, double op_hit_ratio, InFlow in_flow, uint32_t num_docs) { std::cout << std::fixed << std::setprecision(5) - << "| " << std::setw(5) << children - << " | " << std::setw(7) << filter_hit_ratio + << "| " << in_flow + << " | " << std::setw(5) << children << " | " << std::setw(7) << op_hit_ratio << " | " << std::setw(7) << ((double) res.hits / (double) num_docs) << " | " << std::setw(6) << res.flow.estimate << std::setprecision(4) << " | " << std::setw(9) << res.flow.cost + << " | " << std::setw(10) << (res.flow.cost * in_flow.rate()) << " | " << std::setw(7) << res.flow.strict_cost + << " | " << std::setw(11) << (in_flow.strict() ? res.flow.strict_cost : flow::forced_strict_cost(res.flow, in_flow.rate())) << " | " << std::setw(8) << res.hits << " | " << std::setw(8) << res.seeks << std::setprecision(3) @@ -640,7 +665,7 @@ run_benchmark_case(const BenchmarkCaseSetup& setup) if (filter_hit_ratio * setup.filter_crossover_factor <= op_hit_ratio) { auto res = benchmark_search(*factory, setup.num_docs + 1, setup.bcase.strict_context, setup.bcase.force_strict, setup.bcase.unpack_iterator, filter_hit_ratio, PlanningAlgo::Cost); - print_result(res, children, op_hit_ratio, filter_hit_ratio, setup.num_docs); + print_result(res, children, op_hit_ratio, InFlow(setup.bcase.strict_context, filter_hit_ratio), setup.num_docs); result.add(res); } } @@ -681,23 +706,25 @@ run_benchmarks(const BenchmarkSetup& setup) void print_intermediate_blueprint_result_header(size_t children) { + std::cout << "| in_flow"; // This matches the naming scheme in IntermediateBlueprintFactory. char name = 'A'; for (size_t i = 0; i < children; ++i) { - std::cout << "| " << name++ << ".ratio "; + std::cout << " | " << name++ << ".ratio"; } - std::cout << "| flow.cost | flow.scost | flow.est | ratio | hits | seeks | ms_per_cost | time_ms | algo | blueprint |" << std::endl; + std::cout << " | flow.cost | flow.scost | flow.est | ratio | hits | seeks | ms_per_cost | time_ms | algo | blueprint |" << std::endl; } void -print_intermediate_blueprint_result(const BenchmarkResult& res, const std::vector<double>& children_ratios, PlanningAlgo algo, uint32_t num_docs) +print_intermediate_blueprint_result(const BenchmarkResult& res, const std::vector<double>& children_ratios, PlanningAlgo algo, InFlow in_flow, uint32_t num_docs) { - std::cout << std::fixed << std::setprecision(5); + std::cout << std::fixed << std::setprecision(5) + << "| " << in_flow; for (auto ratio : children_ratios) { - std::cout << "| " << std::setw(7) << ratio << " "; + std::cout << " | " << std::setw(7) << ratio; } std::cout << std::setprecision(5) - << "| " << std::setw(10) << res.flow.cost + << " | " << std::setw(10) << res.flow.cost << " | " << std::setw(10) << res.flow.strict_cost << " | " << std::setw(8) << res.flow.estimate << " | " << std::setw(7) << ((double) res.hits / (double) num_docs) @@ -745,9 +772,8 @@ struct BlueprintFactorySetup { BlueprintFactorySetup::~BlueprintFactorySetup() = default; -template <typename IntermediateBlueprintFactoryType> void -run_intermediate_blueprint_benchmark(const BlueprintFactorySetup& a, const BlueprintFactorySetup& b, size_t num_docs) +run_intermediate_blueprint_benchmark(auto factory_factory, std::vector<InFlow> in_flows, const BlueprintFactorySetup& a, const BlueprintFactorySetup& b, size_t num_docs) { print_intermediate_blueprint_result_header(2); double max_speedup = 0.0; @@ -755,26 +781,28 @@ run_intermediate_blueprint_benchmark(const BlueprintFactorySetup& a, const Bluep for (double b_hit_ratio: b.op_hit_ratios) { auto b_factory = b.make_factory_shared(num_docs, b_hit_ratio); for (double a_hit_ratio : a.op_hit_ratios) { - IntermediateBlueprintFactoryType factory; - factory.add_child(a.make_factory(num_docs, a_hit_ratio)); - factory.add_child(b_factory); + auto factory = factory_factory(); + factory->add_child(a.make_factory(num_docs, a_hit_ratio)); + factory->add_child(b_factory); double time_ms_esti = 0.0; - for (auto algo: {PlanningAlgo::Order, PlanningAlgo::Estimate, PlanningAlgo::Cost, - PlanningAlgo::CostForceStrict}) { - auto res = benchmark_search(factory, num_docs + 1, true, false, false, 1.0, algo); - print_intermediate_blueprint_result(res, {a_hit_ratio, b_hit_ratio}, algo, num_docs); - if (algo == PlanningAlgo::Estimate) { - time_ms_esti = res.time_ms; - } - if (algo == PlanningAlgo::CostForceStrict) { - double speedup = time_ms_esti / res.time_ms; - if (speedup > max_speedup) { - max_speedup = speedup; + for (InFlow in_flow: in_flows) { + for (auto algo: {PlanningAlgo::Order, PlanningAlgo::Estimate, PlanningAlgo::Cost, + PlanningAlgo::CostForceStrict}) { + auto res = benchmark_search(*factory, num_docs + 1, in_flow.strict(), false, false, in_flow.rate(), algo); + print_intermediate_blueprint_result(res, {a_hit_ratio, b_hit_ratio}, algo, in_flow, num_docs); + if (algo == PlanningAlgo::Estimate) { + time_ms_esti = res.time_ms; } - if (speedup < min_speedup) { - min_speedup = speedup; + if (algo == PlanningAlgo::CostForceStrict) { + double speedup = time_ms_esti / res.time_ms; + if (speedup > max_speedup) { + max_speedup = speedup; + } + if (speedup < min_speedup) { + min_speedup = speedup; + } + std::cout << "speedup (esti/forc)=" << std::setprecision(4) << speedup << std::endl; } - std::cout << "speedup (esti/forc)=" << std::setprecision(4) << speedup << std::endl; } } } @@ -786,7 +814,19 @@ void run_and_benchmark(const BlueprintFactorySetup& a, const BlueprintFactorySetup& b, size_t num_docs) { std::cout << "AND[A={" << a.to_string() << "},B={" << b.to_string() << "}]" << std::endl; - run_intermediate_blueprint_benchmark<AndBlueprintFactory>(a, b, num_docs); + run_intermediate_blueprint_benchmark([](){ return std::make_unique<AndBlueprintFactory>(); }, {true}, a, b, num_docs); +} + +void +run_source_blender_benchmark(const BlueprintFactorySetup& a, const BlueprintFactorySetup& b, size_t num_docs) +{ + std::cout << "SB[A={" << a.to_string() << "},B={" << b.to_string() << "}]" << std::endl; + auto factory_factory = [&](){ + auto factory = std::make_unique<SourceBlenderBlueprintFactory>(); + factory->init_selector([](uint32_t i){ return (i%10 == 0) ? 1 : 2; }, num_docs + 1); + return factory; + }; + run_intermediate_blueprint_benchmark(factory_factory, {true, 0.75, 0.5, 0.25, 0.1, 0.01, 0.001}, a, b, num_docs); } //------------------------------------------------------------------------------------- @@ -970,16 +1010,40 @@ TEST(IteratorBenchmark, analyze_AND_bitvector_vs_IN) } } +TEST(IteratorBenchmark, analyze_strict_SOURCEBLENDER_memory_and_disk) +{ + for (double small_ratio: {0.001, 0.005, 0.01, 0.05}) { + run_source_blender_benchmark({str_fs, QueryOperator::Term, {small_ratio}}, + {str_index, QueryOperator::Term, {small_ratio * 10}}, + num_docs); + } +} + TEST(IteratorBenchmark, analyze_OR_non_strict_fs) { for (auto or_hit_ratio : {0.01, 0.1, 0.5}) { BenchmarkSetup setup(num_docs, {int32_fs}, {QueryOperator::Or}, {false}, {or_hit_ratio}, {2, 4, 6, 8, 10, 100, 1000}); + //setup.force_strict = true; setup.filter_hit_ratios = gen_ratios(or_hit_ratio, 10.0, 13); run_benchmarks(setup); } } +TEST(IteratorBenchmark, analyze_OR_non_strict_fs_child_est_adjust) +{ + for (auto or_hit_ratio : {0.01, 0.1, 0.5}) { + for (uint32_t children : {2, 4, 6, 8, 10, 100, 1000}) { + double child_est = or_hit_ratio / children; + BenchmarkSetup setup(num_docs, {int32_fs}, {QueryOperator::Or}, {false}, {or_hit_ratio}, + {children}); + //setup.force_strict = true; + setup.filter_hit_ratios = gen_ratios(child_est, 10.0, 13); + run_benchmarks(setup); + } + } +} + TEST(IteratorBenchmark, analyze_OR_non_strict_non_fs) { BenchmarkSetup setup(num_docs, {int32}, {QueryOperator::Or}, {false}, {0.1}, {2, 4, 6, 8, 10}); @@ -1008,6 +1072,22 @@ TEST(IteratorBenchmark, analyze_btree_vs_bitvector_iterators_strict) run_benchmarks(setup); } +TEST(IteratorBenchmark, btree_vs_array_nonstrict_crossover) { + for (double hit_ratio: { 0.001, 0.002, 0.003, 0.004, 0.005, 0.006, 0.007, 0.008, 0.009, + 0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07, 0.08, 0.09, + 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, + 0.91, 0.92, 0.93, 0.94, 0.95, 0.96, 0.97, 0.98, 0.99, 1.0}) + { + auto btree = make_blueprint_factory(int32_array_fs, QueryOperator::Term, num_docs, 0, hit_ratio, 1, false); + auto array = make_blueprint_factory( int32_array, QueryOperator::Term, num_docs, 0, hit_ratio, 1, false); + auto time_ms = [&](auto &bpf, double in_flow) { + return Sample(benchmark_search(bpf, num_docs + 1, false, false, false, in_flow, PlanningAlgo::Cost).time_ms); + }; + auto calculate_at = [&](double in_flow) { return std::make_pair(time_ms(*btree, in_flow), time_ms(*array, in_flow)); }; + fprintf(stderr, "btree/array crossover@%5.3f: %8.6f\n", hit_ratio, find_crossover("TIME", "btree", "array", calculate_at, 0.0001)); + } +} + int main(int argc, char **argv) { ::testing::InitGoogleTest(&argc, argv); int res = RUN_ALL_TESTS(); diff --git a/searchlib/src/tests/queryeval/parallel_weak_and/parallel_weak_and_test.cpp b/searchlib/src/tests/queryeval/parallel_weak_and/parallel_weak_and_test.cpp index 2bd560637d2..1cec376b01c 100644 --- a/searchlib/src/tests/queryeval/parallel_weak_and/parallel_weak_and_test.cpp +++ b/searchlib/src/tests/queryeval/parallel_weak_and/parallel_weak_and_test.cpp @@ -68,8 +68,8 @@ struct TestHeap : public WeakAndHeap { ScoresHistory history; - TestHeap(uint32_t scoresToTrack_) : WeakAndHeap(scoresToTrack_), history() {} - virtual void adjust(score_t *begin, score_t *end) override { + explicit TestHeap(uint32_t scoresToTrack_) : WeakAndHeap(scoresToTrack_), history() {} + void adjust(score_t *begin, score_t *end) override { Scores scores; for (score_t *itr = begin; itr != end; ++itr) { scores.add(*itr); @@ -87,8 +87,8 @@ struct WandTestSpec : public WandSpec TermFieldMatchData rootMatchData; MatchParams matchParams; - WandTestSpec(uint32_t scoresToTrack, uint32_t scoresAdjustFrequency = 1, - score_t scoreThreshold = 0, double thresholdBoostFactor = 1); + explicit WandTestSpec(uint32_t scoresToTrack, uint32_t scoresAdjustFrequency = 1, + score_t scoreThreshold = 0, double thresholdBoostFactor = 1); ~WandTestSpec(); SearchIterator::UP create() { MatchData::UP childrenMatchData = createMatchData(); @@ -114,7 +114,7 @@ WandTestSpec<HeapType>::WandTestSpec(uint32_t scoresToTrack, uint32_t scoresAdju {} template <typename HeapType> -WandTestSpec<HeapType>::~WandTestSpec() {} +WandTestSpec<HeapType>::~WandTestSpec() = default; using WandSpecWithTestHeap = WandTestSpec<TestHeap>; using WandSpecWithRealHeap = WandTestSpec<SharedWeakAndPriorityQueue>; @@ -137,8 +137,8 @@ SimpleResult asSimpleResult(const FakeResult &result) { SimpleResult retval; - for (size_t i = 0; i < result.inspect().size(); ++i) { - retval.addHit(result.inspect()[i].docId); + for (const auto & doc : result.inspect()) { + retval.addHit(doc.docId); } return retval; } @@ -152,26 +152,26 @@ struct WandBlueprintSpec FakeRequestContext requestContext; WandBlueprintSpec &add(const std::string &token, int32_t weight) { - tokens.push_back(std::make_pair(token, weight)); + tokens.emplace_back(token, weight); return *this; } Node::UP createNode(uint32_t scoresToTrack = 100, score_t scoreThreshold = 0, double thresholdBoostFactor = 1) const { - SimpleWandTerm *node = new SimpleWandTerm(tokens.size(), "view", 0, Weight(0), - scoresToTrack, scoreThreshold, thresholdBoostFactor); - for (size_t i = 0; i < tokens.size(); ++i) { - node->addTerm(tokens[i].first, Weight(tokens[i].second)); + auto node = std::make_unique<SimpleWandTerm>(tokens.size(), "view", 0, Weight(0), + scoresToTrack, scoreThreshold, thresholdBoostFactor); + for (const auto & token : tokens) { + node->addTerm(token.first, Weight(token.second)); } - return Node::UP(node); + return node; } Blueprint::UP blueprint(Searchable &searchable, const std::string &field, const search::query::Node &term) const { FieldSpecList fields; fields.add(FieldSpec(field, fieldId, handle)); Blueprint::UP bp = searchable.createBlueprint(requestContext, fields, term); - EXPECT_TRUE(dynamic_cast<ParallelWeakAndBlueprint*>(bp.get()) != 0); + EXPECT_TRUE(dynamic_cast<ParallelWeakAndBlueprint*>(bp.get()) != nullptr); return bp; } @@ -182,7 +182,7 @@ struct WandBlueprintSpec bp->basic_plan(true, docIdLimit); bp->fetchPostings(ExecuteInfo::FULL); SearchIterator::UP sb = bp->createSearch(*md); - EXPECT_TRUE(dynamic_cast<ParallelWeakAndSearch*>(sb.get()) != 0); + EXPECT_TRUE(dynamic_cast<ParallelWeakAndSearch*>(sb.get()) != nullptr); return sb; } @@ -197,7 +197,7 @@ struct WandBlueprintSpec bp->basic_plan(true, docIdLimit); bp->fetchPostings(ExecuteInfo::FULL); SearchIterator::UP sb = bp->createSearch(*md); - EXPECT_TRUE(dynamic_cast<ParallelWeakAndSearch*>(sb.get()) != 0); + EXPECT_TRUE(dynamic_cast<ParallelWeakAndSearch*>(sb.get()) != nullptr); return doSearch(*sb, *md->resolveTermField(handle)); } }; @@ -258,7 +258,7 @@ struct AlgoSameScoreFixture : public FixtureBase struct AlgoScoreThresholdFixture : public FixtureBase { - AlgoScoreThresholdFixture(score_t scoreThreshold) : FixtureBase(3, 1, scoreThreshold) { + explicit AlgoScoreThresholdFixture(score_t scoreThreshold) : FixtureBase(3, 1, scoreThreshold) { spec.leaf(LeafSpec("A", 1).doc(1, 10).doc(2, 30)); spec.leaf(LeafSpec("B", 2).doc(1, 20).doc(3, 40)); prepare(); @@ -267,7 +267,7 @@ struct AlgoScoreThresholdFixture : public FixtureBase struct AlgoLargeScoresFixture : public FixtureBase { - AlgoLargeScoresFixture(score_t scoreThreshold) : FixtureBase(3, 1, scoreThreshold) { + explicit AlgoLargeScoresFixture(score_t scoreThreshold) : FixtureBase(3, 1, scoreThreshold) { spec.leaf(LeafSpec("A", 60000).doc(1, 60000).doc(2, 70000)); spec.leaf(LeafSpec("B", 70000).doc(1, 80000).doc(3, 90000)); prepare(); @@ -276,7 +276,7 @@ struct AlgoLargeScoresFixture : public FixtureBase struct AlgoExhaustPastFixture : public FixtureBase { - AlgoExhaustPastFixture(score_t scoreThreshold) : FixtureBase(3, 1, scoreThreshold) { + explicit AlgoExhaustPastFixture(score_t scoreThreshold) : FixtureBase(3, 1, scoreThreshold) { spec.leaf(LeafSpec("A", 1).doc(1, 20).doc(3, 40).doc(5, 10)); spec.leaf(LeafSpec("B", 1).doc(5, 10)); spec.leaf(LeafSpec("C", 1).doc(5, 10)); @@ -449,11 +449,11 @@ struct BlueprintFixtureBase }; BlueprintFixtureBase::BlueprintFixtureBase() : spec(), searchable() {} -BlueprintFixtureBase::~BlueprintFixtureBase() {} +BlueprintFixtureBase::~BlueprintFixtureBase() = default; struct BlueprintHitsFixture : public BlueprintFixtureBase { - FakeResult createResult(size_t hits) { + static FakeResult createResult(size_t hits) { FakeResult result; for (size_t i = 0; i < hits; ++i) { result.doc(i + 1); @@ -479,7 +479,7 @@ struct BlueprintHitsFixture : public BlueprintFixtureBase struct ThresholdBoostFixture : public FixtureBase { FakeResult result; - ThresholdBoostFixture(double boost) : FixtureBase(1, 1, 800, boost) { + explicit ThresholdBoostFixture(double boost) : FixtureBase(1, 1, 800, boost) { spec.leaf(LeafSpec("A").doc(1, 10)); spec.leaf(LeafSpec("B").doc(2, 20)); spec.leaf(LeafSpec("C").doc(3, 30)); @@ -532,7 +532,7 @@ TEST(ParallelWeakAndTest, require_that_blueprint_picks_up_docid_limit) BlueprintFixture f; Node::UP term = f.spec.createNode(57, 67, 77.7); Blueprint::UP bp = f.blueprint(*term); - const ParallelWeakAndBlueprint * pbp = dynamic_cast<const ParallelWeakAndBlueprint *>(bp.get()); + const auto * pbp = dynamic_cast<const ParallelWeakAndBlueprint *>(bp.get()); EXPECT_EQ(0u, pbp->get_docid_limit()); bp->setDocIdLimit(1000); EXPECT_EQ(1000u, pbp->get_docid_limit()); @@ -543,7 +543,7 @@ TEST(ParallelWeakAndTest, require_that_scores_to_track_score_threshold_and_thres BlueprintFixture f; Node::UP term = f.spec.createNode(57, 67, 77.7); Blueprint::UP bp = f.blueprint(*term); - const ParallelWeakAndBlueprint * pbp = dynamic_cast<const ParallelWeakAndBlueprint *>(bp.get()); + const auto * pbp = dynamic_cast<const ParallelWeakAndBlueprint *>(bp.get()); EXPECT_EQ(57u, pbp->getScores().getScoresToTrack()); EXPECT_EQ(67u, pbp->getScoreThreshold()); EXPECT_EQ(77.7, pbp->getThresholdBoostFactor()); @@ -635,6 +635,7 @@ TEST(ParallelWeakAndTest, require_that_asString_on_blueprint_works) " strict_cost: 0\n" " sourceId: 4294967295\n" " docid_limit: 0\n" + " id: 0\n" " strict: false\n" " _weights: std::vector {\n" " [0]: 5\n" @@ -661,6 +662,7 @@ TEST(ParallelWeakAndTest, require_that_asString_on_blueprint_works) " strict_cost: 0\n" " sourceId: 4294967295\n" " docid_limit: 0\n" + " id: 0\n" " strict: false\n" " }\n" " }\n" @@ -708,7 +710,7 @@ SearchIterator::UP create_wand(bool use_dww, class Verifier : public search::test::DwwIteratorChildrenVerifier { public: - Verifier(bool use_dww) : _use_dww(use_dww) { } + explicit Verifier(bool use_dww) : _use_dww(use_dww) { } private: SearchIterator::UP create(bool strict) const override { MatchParams match_params(_dummy_heap, _dummy_heap.getMinScore(), 1.0, 1); diff --git a/searchlib/src/tests/queryeval/sourceblender/sourceblender_test.cpp b/searchlib/src/tests/queryeval/sourceblender/sourceblender_test.cpp index b84cb02a357..b2a1f6a645a 100644 --- a/searchlib/src/tests/queryeval/sourceblender/sourceblender_test.cpp +++ b/searchlib/src/tests/queryeval/sourceblender/sourceblender_test.cpp @@ -7,15 +7,14 @@ #include <vespa/searchlib/queryeval/leaf_blueprints.h> #define ENABLE_GTEST_MIGRATION #include <vespa/searchlib/test/searchiteratorverifier.h> -#include <vespa/searchlib/common/bitvectoriterator.h> #include <vespa/searchlib/attribute/fixedsourceselector.h> #include <vespa/searchlib/fef/matchdata.h> #include <vespa/vespalib/gtest/gtest.h> using namespace search::queryeval; -using namespace search::fef; using namespace search; using std::make_unique; +using search::fef::MatchData; /** * Proxy search used to verify unpack pattern @@ -27,24 +26,24 @@ private: SimpleResult _unpacked; protected: - virtual void doSeek(uint32_t docid) override { + void doSeek(uint32_t docid) override { _search->seek(docid); setDocId(_search->getDocId()); } - virtual void doUnpack(uint32_t docid) override { + void doUnpack(uint32_t docid) override { _unpacked.addHit(docid); _search->unpack(docid); } public: - UnpackChecker(SearchIterator *search) : _search(search), _unpacked() {} + explicit UnpackChecker(SearchIterator *search) : _search(search), _unpacked() {} const SimpleResult &getUnpacked() const { return _unpacked; } }; class MySelector : public search::FixedSourceSelector { public: - MySelector(int defaultSource) : search::FixedSourceSelector(defaultSource, "fs") { } + explicit MySelector(int defaultSource) : search::FixedSourceSelector(defaultSource, "fs") { } MySelector & set(Source s, uint32_t docId) { setSource(s, docId); return *this; @@ -65,12 +64,12 @@ TEST(SourceBlenderTest, test_strictness) a.addHit(2).addHit(5).addHit(6).addHit(8); b.addHit(3).addHit(5).addHit(6).addHit(7); - MySelector *sel = new MySelector(5); + auto *sel = new MySelector(5); sel->set(2, 1).set(3, 2).set(5, 2).set(7, 1); - SourceBlenderBlueprint *blend_b = new SourceBlenderBlueprint(*sel); - Blueprint::UP a_b(new SimpleBlueprint(a)); - Blueprint::UP b_b(new SimpleBlueprint(b)); + auto *blend_b = new SourceBlenderBlueprint(*sel); + auto a_b = std::make_unique<SimpleBlueprint>(a); + auto b_b = std::make_unique<SimpleBlueprint>(b); a_b->setSourceId(1); b_b->setSourceId(2); blend_b->addChild(std::move(a_b)); @@ -111,16 +110,16 @@ TEST(SourceBlenderTest, test_full_sourceblender_search) c.addHit(4).addHit(11).addHit(21).addHit(32); // these are all handed over to the blender - UnpackChecker *ua = new UnpackChecker(new SimpleSearch(a)); - UnpackChecker *ub = new UnpackChecker(new SimpleSearch(b)); - UnpackChecker *uc = new UnpackChecker(new SimpleSearch(c)); + auto *ua = new UnpackChecker(new SimpleSearch(a)); + auto *ub = new UnpackChecker(new SimpleSearch(b)); + auto *uc = new UnpackChecker(new SimpleSearch(c)); auto sel = make_unique<MySelector>(5); sel->set(2, 1).set(3, 2).set(11, 2).set(21, 3).set(34, 1); SourceBlenderSearch::Children abc; - abc.push_back(SourceBlenderSearch::Child(ua, 1)); - abc.push_back(SourceBlenderSearch::Child(ub, 2)); - abc.push_back(SourceBlenderSearch::Child(uc, 3)); + abc.emplace_back(ua, 1); + abc.emplace_back(ub, 2); + abc.emplace_back(uc, 3); SearchIterator::UP blend(SourceBlenderSearch::create(sel->createIterator(), abc, true)); SimpleResult result; @@ -149,7 +148,7 @@ using search::test::SearchIteratorVerifier; class Verifier : public SearchIteratorVerifier { public: Verifier(); - ~Verifier(); + ~Verifier() override; SearchIterator::UP create(bool strict) const override { return SearchIterator::UP(SourceBlenderSearch::create(_selector.createIterator(), createChildren(strict), @@ -178,7 +177,7 @@ Verifier::Verifier() : _indexes[indexId].push_back(docId); } } -Verifier::~Verifier() {} +Verifier::~Verifier() = default; TEST(SourceBlenderTest, test_that_source_blender_iterator_adheres_to_search_terator_requirements) { diff --git a/searchlib/src/tests/queryeval/sparse_vector_benchmark/sparse_vector_benchmark_test.cpp b/searchlib/src/tests/queryeval/sparse_vector_benchmark/sparse_vector_benchmark_test.cpp index 94ecd8fa539..a7516226daf 100644 --- a/searchlib/src/tests/queryeval/sparse_vector_benchmark/sparse_vector_benchmark_test.cpp +++ b/searchlib/src/tests/queryeval/sparse_vector_benchmark/sparse_vector_benchmark_test.cpp @@ -13,6 +13,7 @@ #include <vespa/searchlib/queryeval/simpleresult.h> #include <vespa/searchlib/queryeval/wand/weak_and_search.h> #include <vespa/searchlib/queryeval/weighted_set_term_search.h> +#include <vespa/searchlib/queryeval/wand/weak_and_heap.h> #include <vespa/vespalib/util/box.h> #include <vespa/vespalib/util/stringfmt.h> @@ -135,7 +136,7 @@ constexpr vespalib::duration max_time = 1000s; //----------------------------------------------------------------------------- struct ChildFactory { - ChildFactory() {} + ChildFactory() = default; virtual std::string name() const = 0; virtual SearchIterator::UP createChild(uint32_t idx, uint32_t limit) const = 0; virtual ~ChildFactory() = default; @@ -190,8 +191,9 @@ struct ModSearchFactory : ChildFactory { //----------------------------------------------------------------------------- struct VespaWandFactory : SparseVectorFactory { + mutable SharedWeakAndPriorityQueue _scores; uint32_t n; - explicit VespaWandFactory(uint32_t n_in) noexcept : n(n_in) {} + explicit VespaWandFactory(uint32_t n_in) : _scores(n_in), n(n_in) {} std::string name() const override { return vespalib::make_string("VespaWand(%u)", n); } @@ -200,7 +202,7 @@ struct VespaWandFactory : SparseVectorFactory { for (size_t i = 0; i < childCnt; ++i) { terms.emplace_back(childFactory.createChild(i, limit), default_weight, limit / (i + 1)); } - return WeakAndSearch::create(terms, n, true); + return WeakAndSearch::create(terms, wand::MatchParams(_scores), n, true); } }; diff --git a/searchlib/src/tests/queryeval/termwise_eval/termwise_eval_test.cpp b/searchlib/src/tests/queryeval/termwise_eval/termwise_eval_test.cpp index 310c6d628e3..4d84dabf834 100644 --- a/searchlib/src/tests/queryeval/termwise_eval/termwise_eval_test.cpp +++ b/searchlib/src/tests/queryeval/termwise_eval/termwise_eval_test.cpp @@ -465,7 +465,7 @@ TEST(TermwiseEvalTest, require_that_termwise_evaluation_can_be_multi_level_but_n child->addChild(UP(new MyBlueprint({3}, true, 3))); my_or.addChild(std::move(child)); for (bool strict: {true, false}) { - my_or.basic_plan(strict, 100); + my_or.null_plan(strict, 100); EXPECT_EQ(my_or.createSearch(*md)->asString(), make_termwise(OR({ TERM({1}, strict), ORz({ TERM({2}, strict), TERM({3}, strict) }, strict) }, diff --git a/searchlib/src/tests/queryeval/weak_and/wand_bench_setup.hpp b/searchlib/src/tests/queryeval/weak_and/wand_bench_setup.hpp index 5e056eb6c0e..457f7133dc1 100644 --- a/searchlib/src/tests/queryeval/weak_and/wand_bench_setup.hpp +++ b/searchlib/src/tests/queryeval/weak_and/wand_bench_setup.hpp @@ -29,20 +29,20 @@ struct Stats { size_t unpackCnt; size_t skippedDocs; size_t skippedHits; - Stats() : hitCnt(0), seekCnt(0), unpackCnt(0), + Stats() noexcept : hitCnt(0), seekCnt(0), unpackCnt(0), skippedDocs(0), skippedHits(0) {} - void hit() { + void hit() noexcept { ++hitCnt; } - void seek(size_t docs, size_t hits) { + void seek(size_t docs, size_t hits) noexcept { ++seekCnt; skippedDocs += docs; skippedHits += hits; } - void unpack() { + void unpack() noexcept { ++unpackCnt; } - void print() { + void print() const { fprintf(stderr, "Stats: hits=%zu, seeks=%zu, unpacks=%zu, skippedDocs=%zu, skippedHits=%zu\n", hitCnt, seekCnt, unpackCnt, skippedDocs, skippedHits); } @@ -77,7 +77,7 @@ struct ModSearch : SearchIterator { } } void doUnpack(uint32_t docid) override { - if (tfmd != NULL) { + if (tfmd != nullptr) { tfmd->reset(docid); search::fef::TermFieldMatchDataPosition pos; pos.setElementWeight(info.getMaxWeight()); @@ -96,40 +96,52 @@ ModSearch::~ModSearch() = default; struct WandFactory { virtual std::string name() const = 0; virtual SearchIterator::UP create(const wand::Terms &terms) = 0; - virtual ~WandFactory() {} + virtual ~WandFactory() = default; }; struct VespaWandFactory : WandFactory { + mutable SharedWeakAndPriorityQueue _scores; uint32_t n; - VespaWandFactory(uint32_t n_in) : n(n_in) {} + explicit VespaWandFactory(uint32_t n_in) noexcept + : _scores(n_in), + n(n_in) + {} ~VespaWandFactory() override; - virtual std::string name() const override { return make_string("VESPA WAND (n=%u)", n); } - virtual SearchIterator::UP create(const wand::Terms &terms) override { - return SearchIterator::UP(WeakAndSearch::create(terms, n, true)); + std::string name() const override { return make_string("VESPA WAND (n=%u)", n); } + SearchIterator::UP create(const wand::Terms &terms) override { + return WeakAndSearch::create(terms, wand::MatchParams(_scores, 1, 1), n, true); } }; VespaWandFactory::~VespaWandFactory() = default; struct VespaArrayWandFactory : WandFactory { + mutable SharedWeakAndPriorityQueue _scores; uint32_t n; - VespaArrayWandFactory(uint32_t n_in) : n(n_in) {} + explicit VespaArrayWandFactory(uint32_t n_in) + : _scores(n_in), + n(n_in) + {} ~VespaArrayWandFactory() override; - virtual std::string name() const override { return make_string("VESPA ARRAY WAND (n=%u)", n); } - virtual SearchIterator::UP create(const wand::Terms &terms) override { - return SearchIterator::UP(WeakAndSearch::createArrayWand(terms, n, true)); + std::string name() const override { return make_string("VESPA ARRAY WAND (n=%u)", n); } + SearchIterator::UP create(const wand::Terms &terms) override { + return WeakAndSearch::createArrayWand(terms, wand::MatchParams(_scores, 1, 1), wand::TermFrequencyScorer(), n, true); } }; VespaArrayWandFactory::~VespaArrayWandFactory() = default; struct VespaHeapWandFactory : WandFactory { + mutable SharedWeakAndPriorityQueue _scores; uint32_t n; - VespaHeapWandFactory(uint32_t n_in) : n(n_in) {} + explicit VespaHeapWandFactory(uint32_t n_in) + : _scores(n_in), + n(n_in) + {} ~VespaHeapWandFactory() override; - virtual std::string name() const override { return make_string("VESPA HEAP WAND (n=%u)", n); } - virtual SearchIterator::UP create(const wand::Terms &terms) override { - return SearchIterator::UP(WeakAndSearch::createHeapWand(terms, n, true)); + std::string name() const override { return make_string("VESPA HEAP WAND (n=%u)", n); } + SearchIterator::UP create(const wand::Terms &terms) override { + return WeakAndSearch::createHeapWand(terms, wand::MatchParams(_scores, 1, 1), wand::TermFrequencyScorer(), n, true); } }; @@ -138,39 +150,39 @@ VespaHeapWandFactory::~VespaHeapWandFactory() = default; struct VespaParallelWandFactory : public WandFactory { SharedWeakAndPriorityQueue scores; TermFieldMatchData rootMatchData; - VespaParallelWandFactory(uint32_t n) : scores(n), rootMatchData() {} + explicit VespaParallelWandFactory(uint32_t n) noexcept : scores(n), rootMatchData() {} ~VespaParallelWandFactory() override; - virtual std::string name() const override { return make_string("VESPA PWAND (n=%u)", scores.getScoresToTrack()); } - virtual SearchIterator::UP create(const wand::Terms &terms) override { - return SearchIterator::UP(ParallelWeakAndSearch::create(terms, + std::string name() const override { return make_string("VESPA PWAND (n=%u)", scores.getScoresToTrack()); } + SearchIterator::UP create(const wand::Terms &terms) override { + return ParallelWeakAndSearch::create(terms, PWMatchParams(scores, 0, 1, 1), - PWRankParams(rootMatchData, MatchData::UP()), true)); + PWRankParams(rootMatchData, {}), true); } }; VespaParallelWandFactory::~VespaParallelWandFactory() = default; struct VespaParallelArrayWandFactory : public VespaParallelWandFactory { - VespaParallelArrayWandFactory(uint32_t n) : VespaParallelWandFactory(n) {} + explicit VespaParallelArrayWandFactory(uint32_t n) noexcept : VespaParallelWandFactory(n) {} ~VespaParallelArrayWandFactory() override; - virtual std::string name() const override { return make_string("VESPA ARRAY PWAND (n=%u)", scores.getScoresToTrack()); } - virtual SearchIterator::UP create(const wand::Terms &terms) override { - return SearchIterator::UP(ParallelWeakAndSearch::createArrayWand(terms, + std::string name() const override { return make_string("VESPA ARRAY PWAND (n=%u)", scores.getScoresToTrack()); } + SearchIterator::UP create(const wand::Terms &terms) override { + return ParallelWeakAndSearch::createArrayWand(terms, PWMatchParams(scores, 0, 1, 1), - PWRankParams(rootMatchData, MatchData::UP()), true)); + PWRankParams(rootMatchData, {}), true); } }; VespaParallelArrayWandFactory::~VespaParallelArrayWandFactory() = default; struct VespaParallelHeapWandFactory : public VespaParallelWandFactory { - VespaParallelHeapWandFactory(uint32_t n) : VespaParallelWandFactory(n) {} + explicit VespaParallelHeapWandFactory(uint32_t n) noexcept : VespaParallelWandFactory(n) {} ~VespaParallelHeapWandFactory() override; - virtual std::string name() const override { return make_string("VESPA HEAP PWAND (n=%u)", scores.getScoresToTrack()); } - virtual SearchIterator::UP create(const wand::Terms &terms) override { - return SearchIterator::UP(ParallelWeakAndSearch::createHeapWand(terms, + std::string name() const override { return make_string("VESPA HEAP PWAND (n=%u)", scores.getScoresToTrack()); } + SearchIterator::UP create(const wand::Terms &terms) override { + return ParallelWeakAndSearch::createHeapWand(terms, PWMatchParams(scores, 0, 1, 1), - PWRankParams(rootMatchData, MatchData::UP()), true)); + PWRankParams(rootMatchData, {}), true); } }; @@ -178,11 +190,11 @@ VespaParallelHeapWandFactory::~VespaParallelHeapWandFactory() = default; struct TermFrequencyRiseWandFactory : WandFactory { uint32_t n; - TermFrequencyRiseWandFactory(uint32_t n_in) : n(n_in) {} + explicit TermFrequencyRiseWandFactory(uint32_t n_in) noexcept : n(n_in) {} ~TermFrequencyRiseWandFactory() override; - virtual std::string name() const override { return make_string("RISE WAND TF (n=%u)", n); } - virtual SearchIterator::UP create(const wand::Terms &terms) override { - return SearchIterator::UP(new rise::TermFrequencyRiseWand(terms, n)); + std::string name() const override { return make_string("RISE WAND TF (n=%u)", n); } + SearchIterator::UP create(const wand::Terms &terms) override { + return std::make_unique<rise::TermFrequencyRiseWand>(terms, n); } }; @@ -190,11 +202,11 @@ TermFrequencyRiseWandFactory::~TermFrequencyRiseWandFactory() = default; struct DotProductRiseWandFactory : WandFactory { uint32_t n; - DotProductRiseWandFactory(uint32_t n_in) : n(n_in) {} + explicit DotProductRiseWandFactory(uint32_t n_in) noexcept : n(n_in) {} ~DotProductRiseWandFactory() override; - virtual std::string name() const override { return make_string("RISE WAND DP (n=%u)", n); } - virtual SearchIterator::UP create(const wand::Terms &terms) override { - return SearchIterator::UP(new rise::DotProductRiseWand(terms, n)); + std::string name() const override { return make_string("RISE WAND DP (n=%u)", n); } + SearchIterator::UP create(const wand::Terms &terms) override { + return std::make_unique<rise::DotProductRiseWand>(terms, n); } }; @@ -204,13 +216,13 @@ struct FilterFactory : WandFactory { WandFactory &factory; Stats stats; uint32_t n; - FilterFactory(WandFactory &f, uint32_t n_in) : factory(f), n(n_in) {} + FilterFactory(WandFactory &f, uint32_t n_in) noexcept : factory(f), n(n_in) {} ~FilterFactory() override; - virtual std::string name() const override { return make_string("Filter (mod=%u) [%s]", n, factory.name().c_str()); } - virtual SearchIterator::UP create(const wand::Terms &terms) override { + std::string name() const override { return make_string("Filter (mod=%u) [%s]", n, factory.name().c_str()); } + SearchIterator::UP create(const wand::Terms &terms) override { AndNotSearch::Children children; children.push_back(factory.create(terms)); - children.emplace_back(new ModSearch(stats, n, search::endDocId, n, NULL)); + children.emplace_back(new ModSearch(stats, n, search::endDocId, n, nullptr)); return AndNotSearch::create(std::move(children), true); } }; @@ -220,8 +232,8 @@ FilterFactory::~FilterFactory() = default; struct Setup { Stats stats; vespalib::duration minTime; - Setup() : stats(), minTime(10000s) {} - virtual ~Setup() {} + Setup() noexcept : stats(), minTime(10000s) {} + virtual ~Setup() = default; virtual std::string name() const = 0; virtual SearchIterator::UP create() = 0; void perform() { @@ -256,10 +268,10 @@ struct WandSetup : Setup { MatchData::UP matchData; WandSetup(WandFactory &f, uint32_t c, uint32_t l) : Setup(), factory(f), childCnt(c), limit(l), weight(100), matchData() {} ~WandSetup() override; - virtual std::string name() const override { + std::string name() const override { return make_string("Wand Setup (terms=%u,docs=%u) [%s]", childCnt, limit, factory.name().c_str()); } - virtual SearchIterator::UP create() override { + SearchIterator::UP create() override { MatchDataLayout layout; std::vector<TermFieldHandle> handles; for (size_t i = 0; i < childCnt; ++i) { diff --git a/searchlib/src/tests/queryeval/weak_and/weak_and_test.cpp b/searchlib/src/tests/queryeval/weak_and/weak_and_test.cpp index 689f9f085d0..4aab66f3cc9 100644 --- a/searchlib/src/tests/queryeval/weak_and/weak_and_test.cpp +++ b/searchlib/src/tests/queryeval/weak_and/weak_and_test.cpp @@ -1,8 +1,7 @@ // Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -#include <vespa/searchlib/queryeval/fake_search.h> #include <vespa/searchlib/queryeval/wand/weak_and_search.h> +#include <vespa/searchlib/queryeval/wand/weak_and_heap.h> #include <vespa/searchlib/queryeval/simpleresult.h> -#include <vespa/searchlib/queryeval/simplesearch.h> #include <vespa/searchlib/queryeval/test/eagerchild.h> #include <vespa/searchlib/queryeval/test/leafspec.h> #include <vespa/searchlib/queryeval/test/wandspec.h> @@ -20,11 +19,13 @@ namespace { struct MyWandSpec : public WandSpec { + SharedWeakAndPriorityQueue scores; uint32_t n; - MyWandSpec(uint32_t n_) : WandSpec(), n(n_) {} + explicit MyWandSpec(uint32_t n_in) : WandSpec(), scores(n_in), n(n_in) {} SearchIterator *create() { - return new TrackedSearch("WAND", getHistory(), WeakAndSearch::create(getTerms(), n, true)); + return new TrackedSearch("WAND", getHistory(), + WeakAndSearch::create(getTerms(), wand::MatchParams(scores, 1, 1), n, true)); } }; @@ -104,7 +105,8 @@ TEST(WeakAndTest, require_that_initial_docid_for_subsearches_are_taken_into_acco wand::Terms terms; terms.push_back(wand::Term(new TrackedSearch("foo", history, new EagerChild(search::endDocId)), 100, 1)); terms.push_back(wand::Term(new TrackedSearch("bar", history, new EagerChild(10)), 100, 2)); - SearchIterator::UP search(new TrackedSearch("WAND", history, WeakAndSearch::create(terms, 2, true))); + SharedWeakAndPriorityQueue scores(2); + auto search = std::make_unique<TrackedSearch>("WAND", history, WeakAndSearch::create(terms, wand::MatchParams(scores), 2, true)); SimpleResult hits; hits.search(*search); EXPECT_EQ(SimpleResult().addHit(10), hits); @@ -114,17 +116,26 @@ TEST(WeakAndTest, require_that_initial_docid_for_subsearches_are_taken_into_acco } class IteratorChildrenVerifier : public search::test::IteratorChildrenVerifier { +public: + IteratorChildrenVerifier(); + ~IteratorChildrenVerifier() override; private: + mutable std::vector<std::unique_ptr<SharedWeakAndPriorityQueue>> _scores; SearchIterator::UP create(bool strict) const override { wand::Terms terms; for (size_t i = 0; i < _num_children; ++i) { terms.emplace_back(createIterator(_split_lists[i], strict).release(), 100, _split_lists[i].size()); } - return SearchIterator::UP(WeakAndSearch::create(terms, -1, strict)); + static constexpr size_t LARGE_ENOUGH_HEAP_FOR_ALL = 10000; + _scores.push_back(std::make_unique<SharedWeakAndPriorityQueue>(LARGE_ENOUGH_HEAP_FOR_ALL)); + return WeakAndSearch::create(terms, wand::MatchParams(*_scores.back(), 1, 1), -1, strict); } }; +IteratorChildrenVerifier::IteratorChildrenVerifier() : _scores() {} +IteratorChildrenVerifier::~IteratorChildrenVerifier() = default; + TEST(WeakAndTest, verify_search_iterator_conformance) { IteratorChildrenVerifier verifier; diff --git a/searchlib/src/tests/queryeval/weak_and/weak_and_test_expensive.cpp b/searchlib/src/tests/queryeval/weak_and/weak_and_test_expensive.cpp index 54bf1e92037..0573404a3b4 100644 --- a/searchlib/src/tests/queryeval/weak_and/weak_and_test_expensive.cpp +++ b/searchlib/src/tests/queryeval/weak_and/weak_and_test_expensive.cpp @@ -16,15 +16,16 @@ void checkWandHits(WandFactory &vespa, WandFactory &rise, uint32_t step, uint32_ s1->initFullRange(); SearchIterator::UP s2 = riseSetup.create(); s2->initFullRange(); - ASSERT_TRUE(dynamic_cast<WeakAndType*>(s1.get()) != 0); - ASSERT_TRUE(dynamic_cast<WeakAndType*>(s2.get()) == 0); - ASSERT_TRUE(dynamic_cast<RiseType*>(s2.get()) != 0); - ASSERT_TRUE(dynamic_cast<RiseType*>(s1.get()) == 0); + ASSERT_TRUE(dynamic_cast<WeakAndType*>(s1.get()) != nullptr); + ASSERT_TRUE(dynamic_cast<WeakAndType*>(s2.get()) == nullptr); + ASSERT_TRUE(dynamic_cast<RiseType*>(s2.get()) != nullptr); + ASSERT_TRUE(dynamic_cast<RiseType*>(s1.get()) == nullptr); s1->seek(1); s2->seek(1); while (!s1->isAtEnd() && !s2->isAtEnd()) { + if (s1->getDocId() != s2->getDocId()) assert(true); ASSERT_EQUAL(s1->getDocId(), s2->getDocId()); if ((filter == 0) || ((s1->getDocId() % filter) != 0)) { s1->unpack(s1->getDocId()); diff --git a/searchlib/src/tests/queryeval/weak_and_scorers/weak_and_scorers_test.cpp b/searchlib/src/tests/queryeval/weak_and_scorers/weak_and_scorers_test.cpp index e1f3f0805d9..8a0bc28f4dd 100644 --- a/searchlib/src/tests/queryeval/weak_and_scorers/weak_and_scorers_test.cpp +++ b/searchlib/src/tests/queryeval/weak_and_scorers/weak_and_scorers_test.cpp @@ -63,4 +63,27 @@ TEST("require that DotProductScorer calculates term score") EXPECT_EQUAL(11u, itr->_unpackDocId); } +TEST("test bm25 idf scorer for wand") +{ + wand::Bm25TermFrequencyScorer scorer(1000000, 1.0); + EXPECT_EQUAL(13410046, scorer.calculateMaxScore(1, 1)); + EXPECT_EQUAL(11464136, scorer.calculateMaxScore(10, 1)); + EXPECT_EQUAL(6907256, scorer.calculateMaxScore(1000, 1)); + EXPECT_EQUAL(4605121, scorer.calculateMaxScore(10000, 1)); + EXPECT_EQUAL(2302581, scorer.calculateMaxScore(100000, 1)); + EXPECT_EQUAL(693147, scorer.calculateMaxScore(500000, 1)); + EXPECT_EQUAL(105360, scorer.calculateMaxScore(900000, 1)); + EXPECT_EQUAL(10050, scorer.calculateMaxScore(990000, 1)); +} + +TEST("test limited range of bm25 idf scorer for wand") +{ + wand::Bm25TermFrequencyScorer scorer08(1000000, 0.8); + wand::Bm25TermFrequencyScorer scorer10(1000000, 1.0); + EXPECT_EQUAL(8207814, scorer08.calculateMaxScore(1000, 1)); + EXPECT_EQUAL(2690049, scorer08.calculateMaxScore(990000, 1)); + EXPECT_EQUAL(6907256, scorer10.calculateMaxScore(1000, 1)); + EXPECT_EQUAL(10050, scorer10.calculateMaxScore(990000, 1)); +} + TEST_MAIN() { TEST_RUN_ALL(); } diff --git a/searchlib/src/tests/ranksetup/CMakeLists.txt b/searchlib/src/tests/ranksetup/CMakeLists.txt index d5eb349a6c7..8dc0ea98835 100644 --- a/searchlib/src/tests/ranksetup/CMakeLists.txt +++ b/searchlib/src/tests/ranksetup/CMakeLists.txt @@ -4,5 +4,6 @@ vespa_add_executable(searchlib_ranksetup_test_app TEST ranksetup_test.cpp DEPENDS searchlib + GTest::gtest ) vespa_add_test(NAME searchlib_ranksetup_test_app COMMAND searchlib_ranksetup_test_app) diff --git a/searchlib/src/tests/ranksetup/ranksetup_test.cpp b/searchlib/src/tests/ranksetup/ranksetup_test.cpp index 53224425a04..a5e7fed5685 100644 --- a/searchlib/src/tests/ranksetup/ranksetup_test.cpp +++ b/searchlib/src/tests/ranksetup/ranksetup_test.cpp @@ -1,7 +1,5 @@ // Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -#include <vespa/vespalib/testkit/testapp.h> -#include <vespa/vespalib/util/stringfmt.h> #include <vespa/searchlib/common/feature.h> #include <vespa/searchlib/attribute/attributeguard.h> @@ -26,6 +24,7 @@ #include <vespa/searchlib/fef/test/rankresult.h> #include <vespa/searchlib/features/rankingexpressionfeature.h> +#include <vespa/searchlib/features/second_phase_feature.h> #include <vespa/searchlib/features/setup.h> #include <vespa/searchlib/features/valuefeature.h> #include <vespa/searchlib/fef/test/plugin/chain.h> @@ -35,6 +34,8 @@ #include <vespa/searchlib/fef/test/plugin/sum.h> #include <vespa/searchlib/fef/test/plugin/cfgvalue.h> #include <vespa/searchlib/fef/test/dummy_dependency_handler.h> +#include <vespa/vespalib/gtest/gtest.h> +#include <vespa/vespalib/util/stringfmt.h> #include <iostream> using namespace search::fef; @@ -219,9 +220,9 @@ FeatureDumper::dump() //----------------------------------------------------------------------------- // RankSetupTest //----------------------------------------------------------------------------- -class RankSetupTest : public vespalib::TestApp +class RankSetupTest : public ::testing::Test { -private: +protected: BlueprintFactory _factory; search::AttributeManager _manager; IndexEnvironment _indexEnv; @@ -229,14 +230,6 @@ private: RankEnvironment _rankEnv; DumpFeatureVisitor _visitor; - void testValueBlueprint(); - void testDoubleBlueprint(); - void testSumBlueprint(); - void testStaticRankBlueprint(); - void testChainBlueprint(); - void testCfgValueBlueprint(); - void testCompilation(); - void testRankSetup(); bool testExecution(const vespalib::string & initRank, feature_t initScore, const vespalib::string & finalRank = "", feature_t finalScore = 0.0f, uint32_t docId = 1); bool testExecution(const RankEnvironment &rankEnv, @@ -248,15 +241,52 @@ private: void checkFeatures(std::map<vespalib::string, feature_t> &exp, std::map<vespalib::string, feature_t> &actual); void testFeatureNormalization(); -public: RankSetupTest(); - ~RankSetupTest(); - int Main() override; + ~RankSetupTest() override; }; +RankSetupTest::RankSetupTest() + : _factory(), + _manager(), + _indexEnv(), + _queryEnv(), + _rankEnv(_factory, _indexEnv, _queryEnv), + _visitor() +{ + // register blueprints + setup_fef_test_plugin(_factory); + _factory.addPrototype(Blueprint::SP(new ValueBlueprint())); + _factory.addPrototype(Blueprint::SP(new RankingExpressionBlueprint())); + _factory.addPrototype(std::make_shared<SecondPhaseBlueprint>()); -void -RankSetupTest::testValueBlueprint() + // setup an original attribute manager with two attributes + search::attribute::Config cfg(search::attribute::BasicType::INT32, + search::attribute::CollectionType::SINGLE); + search::AttributeVector::SP av1 = + search::AttributeFactory::createAttribute("staticrank1", cfg); + search::AttributeVector::SP av2 = + search::AttributeFactory::createAttribute("staticrank2", cfg); + av1->addDocs(5); + av2->addDocs(5); + for (uint32_t i = 0; i < 5; ++i) { + (static_cast<search::IntegerAttribute *>(av1.get()))->update(i, i + 100); + (static_cast<search::IntegerAttribute *>(av2.get()))->update(i, i + 200); + } + av1->commit(); + av2->commit(); + _manager.add(av1); + _manager.add(av2); + + // set the index environment + _queryEnv.setIndexEnv(&_indexEnv); + + // set the manager + _queryEnv.overrideAttributeManager(&_manager); +} + +RankSetupTest::~RankSetupTest() = default; + +TEST_F(RankSetupTest, value_blueprint) { ValueBlueprint prototype; prototype.visitDumpFeatures(_indexEnv, _visitor); @@ -264,22 +294,22 @@ RankSetupTest::testValueBlueprint() Blueprint::UP bp = prototype.createInstance(); DummyDependencyHandler deps(*bp); bp->setName("value"); - EXPECT_EQUAL(bp->getName(), "value"); + EXPECT_EQ(bp->getName(), "value"); std::vector<vespalib::string> params; params.push_back("5.5"); params.push_back("10.5"); EXPECT_TRUE(bp->setup(_indexEnv, params)); - EXPECT_EQUAL(deps.input.size(), 0u); - EXPECT_EQUAL(deps.output.size(), 2u); - EXPECT_EQUAL(deps.output[0], "0"); - EXPECT_EQUAL(deps.output[1], "1"); + EXPECT_EQ(deps.input.size(), 0u); + EXPECT_EQ(deps.output.size(), 2u); + EXPECT_EQ(deps.output[0], "0"); + EXPECT_EQ(deps.output[1], "1"); vespalib::Stash stash; FeatureExecutor &fe = bp->createExecutor(_queryEnv, stash); ValueExecutor * vfe = static_cast<ValueExecutor *>(&fe); - EXPECT_EQUAL(vfe->getValues().size(), 2u); - EXPECT_EQUAL(vfe->getValues()[0], 5.5f); - EXPECT_EQUAL(vfe->getValues()[1], 10.5f); + EXPECT_EQ(vfe->getValues().size(), 2u); + EXPECT_EQ(vfe->getValues()[0], 5.5f); + EXPECT_EQ(vfe->getValues()[1], 10.5f); } { // invalid params Blueprint::UP bp = prototype.createInstance(); @@ -289,8 +319,7 @@ RankSetupTest::testValueBlueprint() } } -void -RankSetupTest::testDoubleBlueprint() +TEST_F(RankSetupTest, double_blueprint) { DoubleBlueprint prototype; prototype.visitDumpFeatures(_indexEnv, _visitor); @@ -301,17 +330,16 @@ RankSetupTest::testDoubleBlueprint() params.push_back("value(5.5).0"); params.push_back("value(10.5).0"); EXPECT_TRUE(bp->setup(_indexEnv, params)); - EXPECT_EQUAL(deps.input.size(), 2u); - EXPECT_EQUAL(deps.input[0], "value(5.5).0"); - EXPECT_EQUAL(deps.input[1], "value(10.5).0"); - EXPECT_EQUAL(deps.output.size(), 2u); - EXPECT_EQUAL(deps.output[0], "0"); - EXPECT_EQUAL(deps.output[1], "1"); + EXPECT_EQ(deps.input.size(), 2u); + EXPECT_EQ(deps.input[0], "value(5.5).0"); + EXPECT_EQ(deps.input[1], "value(10.5).0"); + EXPECT_EQ(deps.output.size(), 2u); + EXPECT_EQ(deps.output[0], "0"); + EXPECT_EQ(deps.output[1], "1"); } } -void -RankSetupTest::testSumBlueprint() +TEST_F(RankSetupTest, sum_blueprint) { SumBlueprint prototype; prototype.visitDumpFeatures(_indexEnv, _visitor); @@ -322,16 +350,15 @@ RankSetupTest::testSumBlueprint() params.push_back("value(5.5, 10.5).0"); params.push_back("value(5.5, 10.5).1"); EXPECT_TRUE(bp->setup(_indexEnv, params)); - EXPECT_EQUAL(deps.input.size(), 2u); - EXPECT_EQUAL(deps.input[0], "value(5.5, 10.5).0"); - EXPECT_EQUAL(deps.input[1], "value(5.5, 10.5).1"); - EXPECT_EQUAL(deps.output.size(), 1u); - EXPECT_EQUAL(deps.output[0], "out"); + EXPECT_EQ(deps.input.size(), 2u); + EXPECT_EQ(deps.input[0], "value(5.5, 10.5).0"); + EXPECT_EQ(deps.input[1], "value(5.5, 10.5).1"); + EXPECT_EQ(deps.output.size(), 1u); + EXPECT_EQ(deps.output[0], "out"); } } -void -RankSetupTest::testStaticRankBlueprint() +TEST_F(RankSetupTest, static_rank_blueprint) { StaticRankBlueprint prototype; { // basic test @@ -340,9 +367,9 @@ RankSetupTest::testStaticRankBlueprint() std::vector<vespalib::string> params; params.push_back("sr1"); EXPECT_TRUE(bp->setup(_indexEnv, params)); - EXPECT_EQUAL(deps.input.size(), 0u); - EXPECT_EQUAL(deps.output.size(), 1u); - EXPECT_EQUAL(deps.output[0], "out"); + EXPECT_EQ(deps.input.size(), 0u); + EXPECT_EQ(deps.output.size(), 1u); + EXPECT_EQ(deps.output[0], "out"); } { // invalid params Blueprint::UP bp = prototype.createInstance(); @@ -355,8 +382,7 @@ RankSetupTest::testStaticRankBlueprint() } } -void -RankSetupTest::testChainBlueprint() +TEST_F(RankSetupTest, chain_blueprint) { ChainBlueprint prototype; { // chaining @@ -367,8 +393,8 @@ RankSetupTest::testChainBlueprint() params.push_back("2"); params.push_back("4"); EXPECT_TRUE(bp->setup(_indexEnv, params)); - EXPECT_EQUAL(deps.input.size(), 1u); - EXPECT_EQUAL(deps.input[0], "chain(basic,1,4)"); + EXPECT_EQ(deps.input.size(), 1u); + EXPECT_EQ(deps.input[0], "chain(basic,1,4)"); } { // leaf node Blueprint::UP bp = prototype.createInstance(); @@ -378,8 +404,8 @@ RankSetupTest::testChainBlueprint() params.push_back("1"); params.push_back("4"); EXPECT_TRUE(bp->setup(_indexEnv, params)); - EXPECT_EQUAL(deps.input.size(), 1u); - EXPECT_EQUAL(deps.input[0], "value(4)"); + EXPECT_EQ(deps.input.size(), 1u); + EXPECT_EQ(deps.input[0], "value(4)"); } { // cycle Blueprint::UP bp = prototype.createInstance(); @@ -389,8 +415,8 @@ RankSetupTest::testChainBlueprint() params.push_back("1"); params.push_back("4"); EXPECT_TRUE(bp->setup(_indexEnv, params)); - EXPECT_EQUAL(deps.input.size(), 1u); - EXPECT_EQUAL(deps.input[0], "chain(cycle,4,4)"); + EXPECT_EQ(deps.input.size(), 1u); + EXPECT_EQ(deps.input[0], "chain(cycle,4,4)"); } { // invalid params Blueprint::UP bp = prototype.createInstance(); @@ -404,8 +430,7 @@ RankSetupTest::testChainBlueprint() } } -void -RankSetupTest::testCfgValueBlueprint() +TEST_F(RankSetupTest, cfg_value_blueprint) { CfgValueBlueprint prototype; IndexEnvironment indexEnv; @@ -421,25 +446,23 @@ RankSetupTest::testCfgValueBlueprint() params.push_back("foo"); EXPECT_TRUE(bp->setup(indexEnv, params)); - EXPECT_EQUAL(deps.input.size(), 0u); - EXPECT_EQUAL(deps.output.size(), 3u); - EXPECT_EQUAL(deps.output[0], "0"); - EXPECT_EQUAL(deps.output[1], "1"); - EXPECT_EQUAL(deps.output[2], "2"); + EXPECT_EQ(deps.input.size(), 0u); + EXPECT_EQ(deps.output.size(), 3u); + EXPECT_EQ(deps.output[0], "0"); + EXPECT_EQ(deps.output[1], "1"); + EXPECT_EQ(deps.output[2], "2"); vespalib::Stash stash; FeatureExecutor &fe = bp->createExecutor(_queryEnv, stash); ValueExecutor *vfe = static_cast<ValueExecutor *>(&fe); - EXPECT_EQUAL(vfe->getValues().size(), 3u); - EXPECT_EQUAL(vfe->getValues()[0], 1.0f); - EXPECT_EQUAL(vfe->getValues()[1], 2.0f); - EXPECT_EQUAL(vfe->getValues()[2], 3.0f); + EXPECT_EQ(vfe->getValues().size(), 3u); + EXPECT_EQ(vfe->getValues()[0], 1.0f); + EXPECT_EQ(vfe->getValues()[1], 2.0f); + EXPECT_EQ(vfe->getValues()[2], 3.0f); } } - -void -RankSetupTest::testCompilation() +TEST_F(RankSetupTest, compilation) { { // unknown blueprint RankSetup rs(_factory, _indexEnv); @@ -498,7 +521,7 @@ RankSetupTest::testCompilation() } } -void RankSetupTest::testRankSetup() +TEST_F(RankSetupTest, rank_setup) { using namespace search::fef::indexproperties; IndexEnvironment env; @@ -524,7 +547,8 @@ void RankSetupTest::testRankSetup() env.getProperties().add(hitcollector::ArraySize::NAME, "60"); env.getProperties().add(hitcollector::EstimatePoint::NAME, "70"); env.getProperties().add(hitcollector::EstimateLimit::NAME, "80"); - env.getProperties().add(hitcollector::RankScoreDropLimit::NAME, "90.5"); + env.getProperties().add(hitcollector::FirstPhaseRankScoreDropLimit::NAME, "90.5"); + env.getProperties().add(hitcollector::SecondPhaseRankScoreDropLimit::NAME, "91.5"); env.getProperties().add(mutate::on_match::Attribute::NAME, "a"); env.getProperties().add(mutate::on_match::Operation::NAME, "+=3"); env.getProperties().add(mutate::on_first_phase::Attribute::NAME, "b"); @@ -541,44 +565,45 @@ void RankSetupTest::testRankSetup() RankSetup rs(_factory, env); EXPECT_FALSE(rs.has_match_features()); rs.configure(); - EXPECT_EQUAL(rs.getFirstPhaseRank(), vespalib::string("firstphase")); - EXPECT_EQUAL(rs.getSecondPhaseRank(), vespalib::string("secondphase")); + EXPECT_EQ(rs.getFirstPhaseRank(), vespalib::string("firstphase")); + EXPECT_EQ(rs.getSecondPhaseRank(), vespalib::string("secondphase")); EXPECT_TRUE(rs.has_match_features()); ASSERT_TRUE(rs.get_match_features().size() == 2); - EXPECT_EQUAL(rs.get_match_features()[0], vespalib::string("match_foo")); - EXPECT_EQUAL(rs.get_match_features()[1], vespalib::string("match_bar")); + EXPECT_EQ(rs.get_match_features()[0], vespalib::string("match_foo")); + EXPECT_EQ(rs.get_match_features()[1], vespalib::string("match_bar")); ASSERT_TRUE(rs.getDumpFeatures().size() == 2); - EXPECT_EQUAL(rs.getDumpFeatures()[0], vespalib::string("foo")); - EXPECT_EQUAL(rs.getDumpFeatures()[1], vespalib::string("bar")); - EXPECT_EQUAL(rs.getNumThreadsPerSearch(), 3u); - EXPECT_EQUAL(rs.getMinHitsPerThread(), 8u); - EXPECT_EQUAL(rs.getDegradationAttribute(), "mystaticrankattr"); - EXPECT_EQUAL(rs.isDegradationOrderAscending(), true); - EXPECT_EQUAL(rs.getDegradationMaxHits(), 12345u); - EXPECT_EQUAL(rs.getDegradationSamplePercentage(), 0.9); - EXPECT_EQUAL(rs.getDegradationMaxFilterCoverage(), 0.19); - EXPECT_EQUAL(rs.getDegradationPostFilterMultiplier(), 0.7); - EXPECT_EQUAL(rs.getDiversityAttribute(), "mycategoryattr"); - EXPECT_EQUAL(rs.getDiversityMinGroups(), 37u); - EXPECT_EQUAL(rs.getDiversityCutoffFactor(), 7.1); - EXPECT_EQUAL(rs.getDiversityCutoffStrategy(), "strict"); - EXPECT_EQUAL(rs.getHeapSize(), 50u); - EXPECT_EQUAL(rs.getArraySize(), 60u); - EXPECT_EQUAL(rs.getEstimatePoint(), 70u); - EXPECT_EQUAL(rs.getEstimateLimit(), 80u); - EXPECT_EQUAL(rs.getRankScoreDropLimit(), 90.5); - EXPECT_EQUAL(rs.getMutateOnMatch()._attribute, "a"); - EXPECT_EQUAL(rs.getMutateOnMatch()._operation, "+=3"); - EXPECT_EQUAL(rs.getMutateOnFirstPhase()._attribute, "b"); - EXPECT_EQUAL(rs.getMutateOnFirstPhase()._operation, "=3"); - EXPECT_EQUAL(rs.getMutateOnSecondPhase()._attribute, "b"); - EXPECT_EQUAL(rs.getMutateOnSecondPhase()._operation, "=7"); - EXPECT_EQUAL(rs.getMutateOnSummary()._attribute, "c"); - EXPECT_EQUAL(rs.getMutateOnSummary()._operation, "-=2"); - EXPECT_EQUAL(rs.get_global_filter_lower_limit(), 0.3); - EXPECT_EQUAL(rs.get_global_filter_upper_limit(), 0.7); - EXPECT_EQUAL(rs.get_target_hits_max_adjustment_factor(), 5.0); - EXPECT_EQUAL(rs.get_fuzzy_matching_algorithm(), vespalib::FuzzyMatchingAlgorithm::DfaImplicit); + EXPECT_EQ(rs.getDumpFeatures()[0], vespalib::string("foo")); + EXPECT_EQ(rs.getDumpFeatures()[1], vespalib::string("bar")); + EXPECT_EQ(rs.getNumThreadsPerSearch(), 3u); + EXPECT_EQ(rs.getMinHitsPerThread(), 8u); + EXPECT_EQ(rs.getDegradationAttribute(), "mystaticrankattr"); + EXPECT_EQ(rs.isDegradationOrderAscending(), true); + EXPECT_EQ(rs.getDegradationMaxHits(), 12345u); + EXPECT_EQ(rs.getDegradationSamplePercentage(), 0.9); + EXPECT_EQ(rs.getDegradationMaxFilterCoverage(), 0.19); + EXPECT_EQ(rs.getDegradationPostFilterMultiplier(), 0.7); + EXPECT_EQ(rs.getDiversityAttribute(), "mycategoryattr"); + EXPECT_EQ(rs.getDiversityMinGroups(), 37u); + EXPECT_EQ(rs.getDiversityCutoffFactor(), 7.1); + EXPECT_EQ(rs.getDiversityCutoffStrategy(), "strict"); + EXPECT_EQ(rs.getHeapSize(), 50u); + EXPECT_EQ(rs.getArraySize(), 60u); + EXPECT_EQ(rs.getEstimatePoint(), 70u); + EXPECT_EQ(rs.getEstimateLimit(), 80u); + EXPECT_EQ(std::optional<feature_t>(90.5), rs.get_first_phase_rank_score_drop_limit()); + EXPECT_EQ(std::optional<feature_t>(91.5), rs.get_second_phase_rank_score_drop_limit()); + EXPECT_EQ(rs.getMutateOnMatch()._attribute, "a"); + EXPECT_EQ(rs.getMutateOnMatch()._operation, "+=3"); + EXPECT_EQ(rs.getMutateOnFirstPhase()._attribute, "b"); + EXPECT_EQ(rs.getMutateOnFirstPhase()._operation, "=3"); + EXPECT_EQ(rs.getMutateOnSecondPhase()._attribute, "b"); + EXPECT_EQ(rs.getMutateOnSecondPhase()._operation, "=7"); + EXPECT_EQ(rs.getMutateOnSummary()._attribute, "c"); + EXPECT_EQ(rs.getMutateOnSummary()._operation, "-=2"); + EXPECT_EQ(rs.get_global_filter_lower_limit(), 0.3); + EXPECT_EQ(rs.get_global_filter_upper_limit(), 0.7); + EXPECT_EQ(rs.get_target_hits_max_adjustment_factor(), 5.0); + EXPECT_EQ(rs.get_fuzzy_matching_algorithm(), vespalib::FuzzyMatchingAlgorithm::DfaImplicit); } bool @@ -603,12 +628,11 @@ RankSetupTest::testExecution(const RankEnvironment &rankEnv, const vespalib::str } RankResult rs = re.execute(docId); ok = ok && (exp == rs); - EXPECT_EQUAL(exp, rs); + EXPECT_EQ(exp, rs); return ok; } -void -RankSetupTest::testExecution() +TEST_F(RankSetupTest, execution) { { // value executor vespalib::string v = FNB().baseName("value").parameter("5.5").parameter("10.5").buildName(); @@ -698,8 +722,7 @@ RankSetupTest::testExecution() } } -void -RankSetupTest::testFeatureDump() +TEST_F(RankSetupTest, feature_dump) { { FeatureDumper dumper(_rankEnv); @@ -721,7 +744,7 @@ RankSetupTest::testFeatureDump() parameter(FNB().baseName("double").parameter("value(8)").buildName()). parameter(FNB().baseName("double").parameter("value(32)").buildName()). buildName(), 80.0f); - EXPECT_EQUAL(exp, dumper.dump()); + EXPECT_EQ(exp, dumper.dump()); } { FeatureDumper dumper(_rankEnv); @@ -731,7 +754,7 @@ RankSetupTest::testFeatureDump() RankResult exp; exp.addScore("value(50)", 50.0f); exp.addScore("value(100)", 100.0f); - EXPECT_EQUAL(exp, dumper.dump()); + EXPECT_EQ(exp, dumper.dump()); } { FeatureDumper dumper(_rankEnv); @@ -739,7 +762,7 @@ RankSetupTest::testFeatureDump() EXPECT_TRUE(dumper.setup()); RankResult exp; exp.addScore(FNB().baseName("rankingExpression").parameter("if(4<2,3,4)").buildName(), 4.0f); - EXPECT_EQUAL(exp, dumper.dump()); + EXPECT_EQ(exp, dumper.dump()); } { @@ -748,7 +771,7 @@ RankSetupTest::testFeatureDump() EXPECT_TRUE(dumper.setup()); RankResult exp; exp.addScore(FNB().baseName("rankingExpression").parameter("if(mysum(value(12),value(10))>2,3,4)").buildName(), 3.0f); - EXPECT_EQUAL(exp, dumper.dump()); + EXPECT_EQ(exp, dumper.dump()); } { // dump features indicated by visitation IndexEnvironment indexEnv; @@ -766,7 +789,7 @@ RankSetupTest::testFeatureDump() RankResult exp; exp.addScore("test_cfgvalue(foo)", 1.0); exp.addScore("test_cfgvalue(bar)", 5.0); - EXPECT_EQUAL(exp, dumper.dump()); + EXPECT_EQ(exp, dumper.dump()); } { // ignore features indicated by visitation IndexEnvironment indexEnv; @@ -785,7 +808,20 @@ RankSetupTest::testFeatureDump() EXPECT_TRUE(dumper.setup()); RankResult exp; exp.addScore("test_cfgvalue(foo)", 1.0); - EXPECT_EQUAL(exp, dumper.dump()); + EXPECT_EQ(exp, dumper.dump()); + } + { // Dump secondPhase feature + IndexEnvironment indexEnv; + indexEnv.getProperties().add(indexproperties::rank::FirstPhase::NAME, "value(2)"); + indexEnv.getProperties().add(indexproperties::rank::SecondPhase::NAME, "value(4)"); + RankEnvironment rankEnv(_factory, indexEnv, _queryEnv); + FeatureDumper dumper(rankEnv); + dumper.configure(); + dumper.addDumpFeature("secondPhase"); + EXPECT_TRUE(dumper.setup()); + RankResult exp; + exp.addScore("secondPhase", 4.0); + EXPECT_EQ(exp, dumper.dump()); } } @@ -793,22 +829,19 @@ void RankSetupTest::checkFeatures(std::map<vespalib::string, feature_t> &exp, std::map<vespalib::string, feature_t> &actual) { using ITR = std::map<vespalib::string, feature_t>::const_iterator; - if (!EXPECT_EQUAL(exp.size(), actual.size())) { - return; - } + ASSERT_EQ(exp.size(), actual.size()); ITR exp_itr = exp.begin(); ITR exp_end = exp.end(); ITR actual_itr = actual.begin(); ITR actual_end = actual.end(); for (; exp_itr != exp_end && actual_itr != actual_end; ++exp_itr, ++actual_itr) { - EXPECT_EQUAL(exp_itr->first, actual_itr->first); - EXPECT_APPROX(exp_itr->second, actual_itr->second, 0.001); + EXPECT_EQ(exp_itr->first, actual_itr->first); + EXPECT_NEAR(exp_itr->second, actual_itr->second, 0.001); } - EXPECT_EQUAL(exp_itr == exp_end, actual_itr == actual_end); + EXPECT_EQ(exp_itr == exp_end, actual_itr == actual_end); } -void -RankSetupTest::testFeatureNormalization() +TEST_F(RankSetupTest, feature_normalization) { BlueprintFactory factory; factory.addPrototype(Blueprint::SP(new ValueBlueprint())); @@ -841,35 +874,39 @@ RankSetupTest::testFeatureNormalization() match_program->setup(*match_data, queryEnv); summaryProgram->setup(*match_data, queryEnv); - EXPECT_APPROX(2.0, Utils::getScoreFeature(*firstPhaseProgram, 1), 0.001); - EXPECT_APPROX(4.0, Utils::getScoreFeature(*secondPhaseProgram, 1), 0.001); + EXPECT_NEAR(2.0, Utils::getScoreFeature(*firstPhaseProgram, 1), 0.001); + EXPECT_NEAR(4.0, Utils::getScoreFeature(*secondPhaseProgram, 1), 0.001); - { // rank seed features + { + SCOPED_TRACE("rank seed features"); std::map<vespalib::string, feature_t> actual = Utils::getSeedFeatures(*summaryProgram, 1); std::map<vespalib::string, feature_t> exp; exp["mysum(value(5),value(5))"] = 10.0; exp["mysum(\"value( 5 )\",\"value( 5 )\")"] = 10.0; - TEST_DO(checkFeatures(exp, actual)); + checkFeatures(exp, actual); } - { // all rank features (1. phase) + { + SCOPED_TRACE("all rank features (1. phase)"); std::map<vespalib::string, feature_t> actual = Utils::getAllFeatures(*firstPhaseProgram, 1); std::map<vespalib::string, feature_t> exp; exp["value(1)"] = 1.0; exp["value(1).0"] = 1.0; exp["mysum(value(1),value(1))"] = 2.0; exp["mysum(value(1),value(1)).out"] = 2.0; - TEST_DO(checkFeatures(exp, actual)); + checkFeatures(exp, actual); } - { // all rank features (2. phase) + { + SCOPED_TRACE("all rank features (2. phase)"); std::map<vespalib::string, feature_t> actual = Utils::getAllFeatures(*secondPhaseProgram, 1); std::map<vespalib::string, feature_t> exp; exp["value(2)"] = 2.0; exp["value(2).0"] = 2.0; exp["mysum(value(2),value(2))"] = 4.0; exp["mysum(value(2),value(2)).out"] = 4.0; - TEST_DO(checkFeatures(exp, actual)); + checkFeatures(exp, actual); } - { // all match features + { + SCOPED_TRACE("all match features"); std::map<vespalib::string, feature_t> actual = Utils::getAllFeatures(*match_program, 1); std::map<vespalib::string, feature_t> exp; exp["value(3)"] = 3.0; @@ -878,9 +915,10 @@ RankSetupTest::testFeatureNormalization() exp["mysum(value(3),value(3)).out"] = 6.0; exp["mysum(\"value( 3 )\",\"value( 3 )\")"] = 6.0; exp["mysum(\"value( 3 )\",\"value( 3 )\").out"] = 6.0; - TEST_DO(checkFeatures(exp, actual)); + checkFeatures(exp, actual); } - { // all rank features (summary) + { + SCOPED_TRACE("all rank features (summary)"); std::map<vespalib::string, feature_t> actual = Utils::getAllFeatures(*summaryProgram, 1); std::map<vespalib::string, feature_t> exp; exp["value(5)"] = 5.0; @@ -889,7 +927,7 @@ RankSetupTest::testFeatureNormalization() exp["mysum(value(5),value(5)).out"] = 10.0; exp["mysum(\"value( 5 )\",\"value( 5 )\")"] = 10.0; exp["mysum(\"value( 5 )\",\"value( 5 )\").out"] = 10.0; - TEST_DO(checkFeatures(exp, actual)); + checkFeatures(exp, actual); } } @@ -900,15 +938,17 @@ RankSetupTest::testFeatureNormalization() RankProgram::UP rankProgram = rankSetup.create_dump_program(); rankProgram->setup(*match_data, queryEnv); - { // dump seed features + { + SCOPED_TRACE("dump seed features"); std::map<vespalib::string, feature_t> actual = Utils::getSeedFeatures(*rankProgram, 1); std::map<vespalib::string, feature_t> exp; exp["mysum(value(10),value(10))"] = 20.0; exp["mysum(\"value( 10 )\",\"value( 10 )\")"] = 20.0; - TEST_DO(checkFeatures(exp, actual)); + checkFeatures(exp, actual); } - { // all dump features + { + SCOPED_TRACE("all dump features"); std::map<vespalib::string, feature_t> actual = Utils::getAllFeatures(*rankProgram, 1); std::map<vespalib::string, feature_t> exp; @@ -921,71 +961,9 @@ RankSetupTest::testFeatureNormalization() exp["mysum(\"value( 10 )\",\"value( 10 )\")"] = 20.0; exp["mysum(\"value( 10 )\",\"value( 10 )\").out"] = 20.0; - TEST_DO(checkFeatures(exp, actual)); + checkFeatures(exp, actual); } } } - -RankSetupTest::RankSetupTest() : - _factory(), - _manager(), - _indexEnv(), - _queryEnv(), - _rankEnv(_factory, _indexEnv, _queryEnv), - _visitor() -{ - // register blueprints - setup_fef_test_plugin(_factory); - _factory.addPrototype(Blueprint::SP(new ValueBlueprint())); - _factory.addPrototype(Blueprint::SP(new RankingExpressionBlueprint())); - - // setup an original attribute manager with two attributes - search::attribute::Config cfg(search::attribute::BasicType::INT32, - search::attribute::CollectionType::SINGLE); - search::AttributeVector::SP av1 = - search::AttributeFactory::createAttribute("staticrank1", cfg); - search::AttributeVector::SP av2 = - search::AttributeFactory::createAttribute("staticrank2", cfg); - av1->addDocs(5); - av2->addDocs(5); - for (uint32_t i = 0; i < 5; ++i) { - (static_cast<search::IntegerAttribute *>(av1.get()))->update(i, i + 100); - (static_cast<search::IntegerAttribute *>(av2.get()))->update(i, i + 200); - } - av1->commit(); - av2->commit(); - _manager.add(av1); - _manager.add(av2); - - // set the index environment - _queryEnv.setIndexEnv(&_indexEnv); - - // set the manager - _queryEnv.overrideAttributeManager(&_manager); -} - -RankSetupTest::~RankSetupTest() {} - -int -RankSetupTest::Main() -{ - TEST_INIT("ranksetup_test"); - - testValueBlueprint(); - testDoubleBlueprint(); - testSumBlueprint(); - testStaticRankBlueprint(); - testChainBlueprint(); - testCfgValueBlueprint(); - - testCompilation(); - testRankSetup(); - testExecution(); - testFeatureDump(); - testFeatureNormalization(); - - TEST_DONE(); -} - -TEST_APPHOOK(RankSetupTest); +GTEST_MAIN_RUN_ALL_TESTS() diff --git a/searchlib/src/tests/sort/CMakeLists.txt b/searchlib/src/tests/sort/CMakeLists.txt index 44ecff7c1a4..9c7b781c232 100644 --- a/searchlib/src/tests/sort/CMakeLists.txt +++ b/searchlib/src/tests/sort/CMakeLists.txt @@ -21,5 +21,6 @@ vespa_add_executable(searchlib_uca_stress_app uca.cpp DEPENDS searchlib + GTest::gtest ) vespa_add_test(NAME searchlib_uca_stress_app COMMAND searchlib_uca_stress_app BENCHMARK) diff --git a/searchlib/src/tests/sort/uca.cpp b/searchlib/src/tests/sort/uca.cpp index d11d230142b..41f6b927990 100644 --- a/searchlib/src/tests/sort/uca.cpp +++ b/searchlib/src/tests/sort/uca.cpp @@ -1,27 +1,17 @@ // Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -#include <vespa/vespalib/testkit/testapp.h> + #include <vespa/searchlib/common/sort.h> #include <vespa/searchlib/common/sortspec.h> +#include <vespa/vespalib/gtest/gtest.h> #include <vespa/vespalib/util/array.h> #include <unicode/ustring.h> #include <unicode/coll.h> #include <fcntl.h> #include <unistd.h> -#include <vespa/log/log.h> -LOG_SETUP("uca_stress"); - using icu::Collator; -class Test : public vespalib::TestApp -{ -public: - int Main() override; - void testFromDat(); -}; - - -void Test::testFromDat() +TEST(UcaStressTest, from_dat) { size_t badnesses = 0; @@ -40,8 +30,6 @@ void Test::testFromDat() int fd = open("sort-blobs.dat", O_RDONLY); char sbuf[4]; - int num=0; - uint32_t atleast = 0; while (read(fd, sbuf, 4) == 4) { @@ -49,20 +37,20 @@ void Test::testFromDat() uint32_t len = 0; int r = read(fd, &len, 4); - EXPECT_EQUAL(4, r); + EXPECT_EQ(4, r); r = read(fd, sbuf, 4); - EXPECT_EQUAL(4, r); - EXPECT_EQUAL(midMark, sbuf); + EXPECT_EQ(4, r); + EXPECT_EQ(midMark, sbuf); if (u16buffer.size() < len) { u16buffer.resize(len); } r = read(fd, &u16buffer[0], len*2); - EXPECT_EQUAL((int)len*2, r); + EXPECT_EQ((int)len*2, r); r = read(fd, sbuf, 4); - EXPECT_EQUAL(4, r); - EXPECT_EQUAL(endMark, sbuf); + EXPECT_EQ(4, r); + EXPECT_EQ(endMark, sbuf); uint32_t wanted = coll->getSortKey(&u16buffer[0], len, NULL, 0); @@ -77,7 +65,7 @@ void Test::testFromDat() for (uint32_t pretend = 1; pretend < wanted+8; ++pretend) { memset(&u8buffer[0], 0x99, u8buffer.size()); uint32_t got = coll->getSortKey(&u16buffer[0], len, &u8buffer[0], pretend); - EXPECT_EQUAL(wanted, got); + EXPECT_EQ(wanted, got); if (u8buffer[pretend+1] != 0x99) { printf("wrote 2 bytes too far: wanted space %d, pretend allocated %d, last good=%02x, bad=%02x %02x\n", @@ -95,24 +83,13 @@ void Test::testFromDat() memset(&u8buffer[0], 0x99, u8buffer.size()); uint32_t got = coll->getSortKey(&u16buffer[0], len, &u8buffer[0], u8buffer.size()); - EXPECT_EQUAL(wanted, got); + EXPECT_EQ(wanted, got); - EXPECT_EQUAL('\0', u8buffer[got-1]); - EXPECT_EQUAL((uint8_t)0x99, u8buffer[got]); - } - if (++num >= 10000) { - TEST_FLUSH(); - num=0; + EXPECT_EQ('\0', u8buffer[got-1]); + EXPECT_EQ((uint8_t)0x99, u8buffer[got]); } } - EXPECT_EQUAL(0u, badnesses); + EXPECT_EQ(0u, badnesses); } -TEST_APPHOOK(Test); - -int Test::Main() -{ - TEST_INIT("uca_stress"); - testFromDat(); - TEST_DONE(); -} +GTEST_MAIN_RUN_ALL_TESTS() diff --git a/searchlib/src/tests/tensor/distance_calculator/distance_calculator_test.cpp b/searchlib/src/tests/tensor/distance_calculator/distance_calculator_test.cpp index 4ffc1fe366e..136878f0ea5 100644 --- a/searchlib/src/tests/tensor/distance_calculator/distance_calculator_test.cpp +++ b/searchlib/src/tests/tensor/distance_calculator/distance_calculator_test.cpp @@ -9,7 +9,6 @@ #include <vespa/searchlib/test/attribute_builder.h> #include <vespa/vespalib/gtest/gtest.h> #include <vespa/vespalib/util/exceptions.h> -#include <iostream> using namespace search::attribute::test; using namespace search::attribute; diff --git a/searchlib/src/tests/tensor/distance_functions/CMakeLists.txt b/searchlib/src/tests/tensor/distance_functions/CMakeLists.txt index e1a54f7883a..92ad9ae2648 100644 --- a/searchlib/src/tests/tensor/distance_functions/CMakeLists.txt +++ b/searchlib/src/tests/tensor/distance_functions/CMakeLists.txt @@ -7,3 +7,10 @@ vespa_add_executable(searchlib_distance_functions_test_app TEST GTest::GTest ) vespa_add_test(NAME searchlib_distance_functions_test_app COMMAND searchlib_distance_functions_test_app) + +vespa_add_executable(searchlib_distance_functions_benchmark_app TEST + SOURCES + distance_functions_benchmark.cpp + DEPENDS + searchlib +) diff --git a/searchlib/src/tests/tensor/distance_functions/distance_functions_benchmark.cpp b/searchlib/src/tests/tensor/distance_functions/distance_functions_benchmark.cpp new file mode 100644 index 00000000000..0cb75075f5e --- /dev/null +++ b/searchlib/src/tests/tensor/distance_functions/distance_functions_benchmark.cpp @@ -0,0 +1,133 @@ +// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +#include <vespa/eval/eval/typed_cells.h> +#include <vespa/searchlib/common/geo_gcd.h> +#include <vespa/searchlib/tensor/distance_functions.h> +#include <vespa/searchlib/tensor/distance_function_factory.h> +#include <vespa/searchlib/tensor/mips_distance_transform.h> +#include <vespa/vespalib/util/benchmark_timer.h> +#include <vespa/vespalib/util/classname.h> + +using namespace search::tensor; +using vespalib::eval::Int8Float; +using vespalib::BFloat16; +using vespalib::eval::TypedCells; +using search::attribute::DistanceMetric; + +size_t npos = std::string::npos; + +double run_calc(size_t iterations, TypedCells b, const BoundDistanceFunction & df) __attribute__((noinline)); +double run_calc_with_limit(size_t iterations, TypedCells b, const BoundDistanceFunction & df) __attribute__((noinline)); + +double +run_calc(size_t iterations, TypedCells b, const BoundDistanceFunction & df) { + vespalib::BenchmarkTimer timer(1.0); + double min_result = std::numeric_limits<double>::max(); + while (timer.has_budget()) { + timer.before(); + for (size_t i(0); i < iterations; i++) { + min_result = std::min(df.calc(b), min_result); + } + timer.after(); + } + printf("%s::calc: Time used = %1.3f, min_result=%3.3f\n", + vespalib::getClassName(df).c_str(), timer.min_time(), min_result); + return min_result; +} + +double +run_calc_with_limit(size_t iterations, TypedCells b, const BoundDistanceFunction & df) { + vespalib::BenchmarkTimer timer(1.0); + double min_result = std::numeric_limits<double>::max(); + while (timer.has_budget()) { + timer.before(); + for (size_t i(0); i < iterations; i++) { + min_result = std::min(df.calc_with_limit(b, std::numeric_limits<double>::max()), min_result); + } + timer.after(); + } + + printf("%s::calc_with_limit: Time used = %1.3f, min_result=%3.3f\n", + vespalib::getClassName(df).c_str(), timer.min_time(), min_result); + return min_result; +} + +template<typename T> +void benchmark(size_t iterations, size_t elems) __attribute__((noinline)); + +template<typename T> +void benchmark(size_t iterations, size_t elems, const DistanceFunctionFactory & df) { + std::vector<T> av, bv; + srandom(7); + av.reserve(elems); + bv.reserve(elems); + for (size_t i(0); i < elems; i++) { + av.push_back(random()%128); + bv.push_back(random()%128); + } + TypedCells a_cells(av), b_cells(bv); + + double calc_result = run_calc(iterations, b_cells, *df.for_query_vector(a_cells)); + double calc_with_limit_result = run_calc_with_limit(iterations, b_cells, *df.for_query_vector(a_cells)); + assert(calc_result == calc_with_limit_result); +} + +template<typename T> +void benchmark(size_t iterations, size_t elems, const std::string & dist_functions) { + if (dist_functions.find("euclid") != npos) { + if constexpr ( ! std::is_same<T, BFloat16>()) { + benchmark<T>(iterations, elems, EuclideanDistanceFunctionFactory<T>()); + } else { + benchmark<BFloat16>(iterations, elems, EuclideanDistanceFunctionFactory<float>()); + } + } + if (dist_functions.find("angular") != npos) { + if constexpr ( ! std::is_same<T, BFloat16>()) { + benchmark<T>(iterations, elems, AngularDistanceFunctionFactory<T>()); + } + } + if (dist_functions.find("prenorm") != npos) { + if constexpr ( ! std::is_same<T, BFloat16>()) { + benchmark<T>(iterations, elems, PrenormalizedAngularDistanceFunctionFactory<T>()); + } + } + if (dist_functions.find("mips") != npos) { + if constexpr ( !std::is_same<T, BFloat16>()) { + benchmark<T>(iterations, elems, MipsDistanceFunctionFactory<T>()); + } + } +} + +void +benchmark(size_t iterations, size_t elems, const std::string & dist_functions, const std::string & data_types) { + if (data_types.find("double") != npos) { + benchmark<double>(iterations, elems, dist_functions); + } + if (data_types.find("float32") != npos) { + benchmark<float>(iterations, elems, dist_functions); + } + if (data_types.find("bfloat16") != npos) { + benchmark<BFloat16>(iterations, elems, dist_functions); + } + if (data_types.find("float8") != npos) { + benchmark<Int8Float>(iterations, elems, dist_functions); + } +} + +int +main(int argc, char *argv[]) { + size_t num_iterations = 10000000; + size_t num_elems = 1024; + std::string dist_functions = "angular euclid prenorm mips"; + std::string data_types = "double float32 bfloat16 float8"; + if (argc > 1) { num_iterations = atol(argv[1]); } + if (argc > 2) { num_elems = atol(argv[2]); } + if (argc > 3) { dist_functions = argv[3]; } + if (argc > 4) { data_types = argv[4]; } + + printf("Benchmarking %ld iterations with vector length %ld with distance functions '%s' for data types '%s'\n", + num_iterations, num_elems, dist_functions.c_str(), data_types.c_str()); + benchmark(num_iterations, num_elems, dist_functions, data_types); + + return 0; +} diff --git a/searchlib/src/tests/tensor/distance_functions/distance_functions_test.cpp b/searchlib/src/tests/tensor/distance_functions/distance_functions_test.cpp index eeae12e1695..c0296548b5a 100644 --- a/searchlib/src/tests/tensor/distance_functions/distance_functions_test.cpp +++ b/searchlib/src/tests/tensor/distance_functions/distance_functions_test.cpp @@ -13,9 +13,11 @@ LOG_SETUP("distance_function_test"); using namespace search::tensor; +using search::attribute::DistanceMetric; +using vespalib::BFloat16; +using vespalib::eval::CellType; using vespalib::eval::Int8Float; using vespalib::eval::TypedCells; -using search::attribute::DistanceMetric; template <typename T> TypedCells t(const std::vector<T> &v) { return TypedCells(v); } @@ -716,6 +718,73 @@ TEST(DistanceFunctionsTest, transformed_mips_growing_norm) EXPECT_GT(-29900.0, f->calc(t(p9d))); } +template <typename FloatType> +void +expect_reference_insertion_vector(FloatType exp_dist, DistanceMetric metric, CellType cell_type) +{ + std::vector<FloatType> lhs{0.0, 1.0}; + std::vector<FloatType> rhs{0.0, 1.0}; + auto factory = make_distance_function_factory(metric, cell_type); + auto func = factory->for_insertion_vector(t(lhs)); + // Updating the insertion vector should be reflected in the calculation. + lhs[0] = 1.0; + lhs[1] = 0.0; + EXPECT_EQ(exp_dist, func->calc(t(rhs))); +} + +template <typename FloatType> +void +expect_not_reference_insertion_vector(FloatType exp_dist, DistanceMetric metric, CellType cell_type) +{ + std::vector<FloatType> lhs{1.0, 0.0}; + std::vector<FloatType> rhs{0.0, 1.0}; + auto factory = make_distance_function_factory(metric, cell_type); + auto func = factory->for_insertion_vector(t(lhs)); + // Updating the insertion vector should NOT be reflected in the calculation, as a copy has been created. + lhs[0] = 0.0; + lhs[1] = 1.0; + EXPECT_EQ(exp_dist, func->calc(t(rhs))); +} + +TEST(DistanceFunctionsTest, angular_can_reference_insertion_vector) +{ + expect_reference_insertion_vector<float>(1.0, DistanceMetric::Angular, CellType::FLOAT); + expect_reference_insertion_vector<double>(1.0, DistanceMetric::Angular, CellType::DOUBLE); + expect_reference_insertion_vector<Int8Float>(1.0, DistanceMetric::Angular, CellType::INT8); + expect_not_reference_insertion_vector<BFloat16>(1.0, DistanceMetric::Angular, CellType::BFLOAT16); +} + +TEST(DistanceFunctionsTest, prenormalized_angular_can_reference_insertion_vector) +{ + expect_reference_insertion_vector<float>(1.0, DistanceMetric::PrenormalizedAngular, CellType::FLOAT); + expect_reference_insertion_vector<double>(1.0, DistanceMetric::PrenormalizedAngular, CellType::DOUBLE); + expect_reference_insertion_vector<Int8Float>(1.0, DistanceMetric::PrenormalizedAngular, CellType::INT8); + expect_not_reference_insertion_vector<BFloat16>(1.0, DistanceMetric::PrenormalizedAngular, CellType::BFLOAT16); +} + +TEST(DistanceFunctionsTest, euclidean_can_reference_insertion_vector) +{ + expect_reference_insertion_vector<float>(2.0, DistanceMetric::Euclidean, CellType::FLOAT); + expect_reference_insertion_vector<double>(2.0, DistanceMetric::Euclidean, CellType::DOUBLE); + expect_reference_insertion_vector<Int8Float>(2.0, DistanceMetric::Euclidean, CellType::INT8); + expect_not_reference_insertion_vector<BFloat16>(2.0, DistanceMetric::Euclidean, CellType::BFLOAT16); +} + +TEST(DistanceFunctionsTest, dotproduct_can_reference_insertion_vector) +{ + expect_reference_insertion_vector<float>(0.0, DistanceMetric::Dotproduct, CellType::FLOAT); + expect_reference_insertion_vector<double>(0.0, DistanceMetric::Dotproduct, CellType::DOUBLE); + expect_reference_insertion_vector<Int8Float>(0.0, DistanceMetric::Dotproduct, CellType::INT8); + expect_not_reference_insertion_vector<BFloat16>(0.0, DistanceMetric::Dotproduct, CellType::BFLOAT16); +} + +TEST(DistanceFunctionsTest, hamming_can_reference_insertion_vector) +{ + expect_reference_insertion_vector<float>(2.0, DistanceMetric::Hamming, CellType::FLOAT); + expect_reference_insertion_vector<double>(2.0, DistanceMetric::Hamming, CellType::DOUBLE); + expect_reference_insertion_vector<Int8Float>(2.0, DistanceMetric::Hamming, CellType::INT8); + expect_not_reference_insertion_vector<BFloat16>(2.0, DistanceMetric::Hamming, CellType::BFLOAT16); +} GTEST_MAIN_RUN_ALL_TESTS() diff --git a/searchlib/src/tests/tensor/hnsw_index/hnsw_index_test.cpp b/searchlib/src/tests/tensor/hnsw_index/hnsw_index_test.cpp index d50677314df..97b88bc787a 100644 --- a/searchlib/src/tests/tensor/hnsw_index/hnsw_index_test.cpp +++ b/searchlib/src/tests/tensor/hnsw_index/hnsw_index_test.cpp @@ -111,7 +111,7 @@ class MyBoundDistanceFunction : public BoundDistanceFunction { std::unique_ptr<BoundDistanceFunction> _real; public: - MyBoundDistanceFunction(std::unique_ptr<BoundDistanceFunction> real) + explicit MyBoundDistanceFunction(std::unique_ptr<BoundDistanceFunction> real) : _real(std::move(real)) { } @@ -147,19 +147,19 @@ class MyDistanceFunctionFactory : public DistanceFunctionFactory { std::unique_ptr<DistanceFunctionFactory> _real; public: - MyDistanceFunctionFactory(std::unique_ptr<DistanceFunctionFactory> real) + explicit MyDistanceFunctionFactory(std::unique_ptr<DistanceFunctionFactory> real) : _real(std::move(real)) { } ~MyDistanceFunctionFactory() override; - std::unique_ptr<BoundDistanceFunction> for_query_vector(TypedCells lhs) override { + std::unique_ptr<BoundDistanceFunction> for_query_vector(TypedCells lhs) const override { EXPECT_FALSE(lhs.non_existing_attribute_value()); return std::make_unique<MyBoundDistanceFunction>(_real->for_query_vector(lhs)); } - std::unique_ptr<BoundDistanceFunction> for_insertion_vector(TypedCells lhs) override { + std::unique_ptr<BoundDistanceFunction> for_insertion_vector(TypedCells lhs) const override { EXPECT_FALSE(lhs.non_existing_attribute_value()); return std::make_unique<MyBoundDistanceFunction>(_real->for_insertion_vector(lhs)); } @@ -936,6 +936,36 @@ TYPED_TEST(HnswIndexTest, search_during_remove) this->expect_top_3_by_docid("{0, 0}", {0, 0}, {7}); } +TYPED_TEST(HnswIndexTest, inconsistent_index) +{ + this->init(false); + this->vectors.clear(); + this->vectors.set(1, {1, 3}).set(2, {7, 1}).set(3, {6, 5}).set(4, {8, 3}).set(5, {10, 3}); + this->add_document(1); + this->add_document(2); + this->add_document(3); + this->add_document(4); + this->add_document(5); + this->expect_entry_point(1, 0); + this->expect_level_0(1, {2, 3}); + this->expect_level_0(2, {1, 3, 4, 5}); + this->expect_level_0(3, {1, 2, 4}); + this->expect_level_0(4, {2, 3, 5}); + this->expect_level_0(5, {2, 4}); + EXPECT_EQ(0, this->index->check_consistency(6)); + // Remove vector for docid 5 but don't update index. + this->vectors.clear(5); + EXPECT_EQ(1, this->index->check_consistency(6)); + /* + * Removing document 2 causes mutual reconnect for nodes [1, 3, 4, 5] + * where nodes 1 and 5 are not previously connected. Distance from + * node 1 to node 5 cannot be calculated due to missing vector. + */ + this->remove_document(2); + // No reconnect for node without vector + this->expect_level_0(5, {4}); +} + using HnswMultiIndexTest = HnswIndexTest<HnswIndex<HnswIndexType::MULTI>>; namespace { diff --git a/searchlib/src/tests/util/token_extractor/token_extractor_test.cpp b/searchlib/src/tests/util/token_extractor/token_extractor_test.cpp index e6944e257e9..5eb42bb8ac4 100644 --- a/searchlib/src/tests/util/token_extractor/token_extractor_test.cpp +++ b/searchlib/src/tests/util/token_extractor/token_extractor_test.cpp @@ -118,7 +118,7 @@ TEST_F(TokenExtractorTest, empty_string) TEST_F(TokenExtractorTest, plain_string) { - EXPECT_EQ((Words{"Plain string"}), process(StringFieldValue("Plain string"))); + EXPECT_EQ((Words{}), process(StringFieldValue("Plain string"))); } TEST_F(TokenExtractorTest, normal_string) |