summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTor Egge <tegge@vespa.ai>2024-06-03 13:11:45 +0200
committerGitHub <noreply@github.com>2024-06-03 13:11:45 +0200
commit86974425545fe47432cc9c9ffb7cb376c86ec2a5 (patch)
tree1f17e36dd10e5e0f19266d55d6d320bae5f16d0d
parent46b7e3a8771e8c654f39b96d26210b099910ef56 (diff)
parentc169c719aa2b8f9b9fd86e08d53ea024108776c9 (diff)
Merge pull request #31340 from vespa-engine/toregge/rewrite-match-loop-communicator-test-to-gtestv8.351.13
Rewrite match loop communicator unit test to gtest.
-rw-r--r--searchcore/src/tests/proton/matching/match_loop_communicator/CMakeLists.txt1
-rw-r--r--searchcore/src/tests/proton/matching/match_loop_communicator/match_loop_communicator_test.cpp277
-rw-r--r--searchlib/src/vespa/searchlib/queryeval/scores.h3
3 files changed, 179 insertions, 102 deletions
diff --git a/searchcore/src/tests/proton/matching/match_loop_communicator/CMakeLists.txt b/searchcore/src/tests/proton/matching/match_loop_communicator/CMakeLists.txt
index b545023ce97..b5b71836581 100644
--- a/searchcore/src/tests/proton/matching/match_loop_communicator/CMakeLists.txt
+++ b/searchcore/src/tests/proton/matching/match_loop_communicator/CMakeLists.txt
@@ -4,5 +4,6 @@ vespa_add_executable(searchcore_match_loop_communicator_test_app TEST
match_loop_communicator_test.cpp
DEPENDS
searchcore_matching
+ GTest::gtest
)
vespa_add_test(NAME searchcore_match_loop_communicator_test_app COMMAND searchcore_match_loop_communicator_test_app)
diff --git a/searchcore/src/tests/proton/matching/match_loop_communicator/match_loop_communicator_test.cpp b/searchcore/src/tests/proton/matching/match_loop_communicator/match_loop_communicator_test.cpp
index 5994385b4aa..759c4cefafe 100644
--- a/searchcore/src/tests/proton/matching/match_loop_communicator/match_loop_communicator_test.cpp
+++ b/searchcore/src/tests/proton/matching/match_loop_communicator/match_loop_communicator_test.cpp
@@ -1,8 +1,8 @@
// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-#include <vespa/vespalib/test/insertion_operators.h>
-#include <vespa/vespalib/testkit/test_kit.h>
#include <vespa/searchcore/proton/matching/match_loop_communicator.h>
#include <vespa/searchlib/features/first_phase_rank_lookup.h>
+#include <vespa/vespalib/gtest/gtest.h>
+#include <vespa/vespalib/test/nexus.h>
#include <algorithm>
using namespace proton::matching;
@@ -16,6 +16,15 @@ using TaggedHit = MatchLoopCommunicator::TaggedHit;
using TaggedHits = MatchLoopCommunicator::TaggedHits;
using search::features::FirstPhaseRankLookup;
using search::queryeval::SortedHitSequence;
+using vespalib::test::Nexus;
+
+namespace search::queryeval {
+
+void PrintTo(const Scores& scores, std::ostream* os) {
+ *os << "{" << scores.low << "," << scores.high << "}";
+}
+
+}
std::vector<Hit> hit_vec(std::vector<Hit> list) { return list; }
@@ -30,6 +39,13 @@ Hits makeScores(size_t id) {
return {};
}
+Hits make_first_scores(size_t id, size_t size) {
+ auto result = makeScores(id);
+ EXPECT_LE(size, result.size());
+ result.resize(size);
+ return result;
+}
+
std::tuple<size_t,Hits,RangePair> second_phase(MatchLoopCommunicator &com, const Hits &hits, size_t thread_id, double delta = 0.0) {
std::vector<uint32_t> refs;
for (size_t i = 0; i < hits.size(); ++i) {
@@ -63,25 +79,6 @@ size_t my_work_size(MatchLoopCommunicator &com, const Hits &hits, size_t thread_
return work_size;
}
-void equal(size_t count, const Hits & a, const Hits & b) {
- EXPECT_EQUAL(count, b.size());
- for (size_t i(0); i < count; i++) {
- EXPECT_EQUAL(a[i].first, b[i].first);
- EXPECT_EQUAL(a[i].second , b[i].second);
- }
-}
-
-void equal_range(const Range &a, const Range &b) {
- EXPECT_EQUAL(a.isValid(), b.isValid());
- EXPECT_EQUAL(a.low, b.low);
- EXPECT_EQUAL(a.high, b.high);
-}
-
-void equal_ranges(const RangePair &a, const RangePair &b) {
- TEST_DO(equal_range(a.first, b.first));
- TEST_DO(equal_range(a.second, b.second));
-}
-
struct EveryOdd : public search::queryeval::IDiversifier {
bool accepted(uint32_t docId) override {
return docId & 0x01;
@@ -92,122 +89,196 @@ struct None : public search::queryeval::IDiversifier {
bool accepted(uint32_t) override { return false; }
};
-TEST_F("require that selectBest gives appropriate results for single thread", MatchLoopCommunicator(num_threads, 3)) {
- TEST_DO(equal(2u, hit_vec({{1, 5}, {2, 4}}), selectBest(f1, hit_vec({{1, 5}, {2, 4}}), thread_id)));
- TEST_DO(equal(3u, hit_vec({{1, 5}, {2, 4}, {3, 3}}), selectBest(f1, hit_vec({{1, 5}, {2, 4}, {3, 3}}), thread_id)));
- TEST_DO(equal(3u, hit_vec({{1, 5}, {2, 4}, {3, 3}}), selectBest(f1, hit_vec({{1, 5}, {2, 4}, {3, 3}, {4, 2}}), thread_id)));
+TEST(MatchLoopCommunicatorTest, require_that_selectBest_gives_appropriate_results_for_single_thread)
+{
+ constexpr size_t num_threads = 1;
+ constexpr size_t thread_id = 0;
+ MatchLoopCommunicator f1(num_threads, 3);
+ EXPECT_EQ(hit_vec({{1, 5}, {2, 4}}), selectBest(f1, hit_vec({{1, 5}, {2, 4}}), thread_id));
+ EXPECT_EQ(hit_vec({{1, 5}, {2, 4}, {3, 3}}), selectBest(f1, hit_vec({{1, 5}, {2, 4}, {3, 3}}), thread_id));
+ EXPECT_EQ(hit_vec({{1, 5}, {2, 4}, {3, 3}}), selectBest(f1, hit_vec({{1, 5}, {2, 4}, {3, 3}, {4, 2}}), thread_id));
}
-TEST_F("require that selectBest gives appropriate results for single thread with filter",
- MatchLoopCommunicator(num_threads, 3, std::make_unique<EveryOdd>(), nullptr))
+TEST(MatchLoopCommunicatorTest, require_that_selectBest_gives_appropriate_results_for_single_thread_with_filter)
{
- TEST_DO(equal(1u, hit_vec({{1, 5}}), selectBest(f1, hit_vec({{1, 5}, {2, 4}}), thread_id)));
- TEST_DO(equal(2u, hit_vec({{1, 5}, {3, 3}}), selectBest(f1, hit_vec({{1, 5}, {2, 4}, {3, 3}}), thread_id)));
- TEST_DO(equal(3u, hit_vec({{1, 5}, {3, 3}, {5, 1}}), selectBest(f1, hit_vec({{1, 5}, {2, 4}, {3, 3}, {4, 2}, {5, 1}, {6, 0}}), thread_id)));
+ constexpr size_t num_threads = 1;
+ constexpr size_t thread_id = 0;
+ MatchLoopCommunicator f1(num_threads, 3, std::make_unique<EveryOdd>(), nullptr);
+ EXPECT_EQ(hit_vec({{1, 5}}), selectBest(f1, hit_vec({{1, 5}, {2, 4}}), thread_id));
+ EXPECT_EQ(hit_vec({{1, 5}, {3, 3}}), selectBest(f1, hit_vec({{1, 5}, {2, 4}, {3, 3}}), thread_id));
+ EXPECT_EQ(hit_vec({{1, 5}, {3, 3}, {5, 1}}), selectBest(f1, hit_vec({{1, 5}, {2, 4}, {3, 3}, {4, 2}, {5, 1}, {6, 0}}), thread_id));
}
-TEST_MT_F("require that selectBest works with no hits", 10, MatchLoopCommunicator(num_threads, 10)) {
- EXPECT_TRUE(selectBest(f1, hit_vec({}), thread_id).empty());
+TEST(MatchLoopCommunicatorTest, require_that_selectBest_works_with_no_hits)
+{
+ constexpr size_t num_threads = 10;
+ MatchLoopCommunicator f1(num_threads, 10);
+ auto task = [&f1](Nexus& ctx) {
+ EXPECT_TRUE(selectBest(f1, hit_vec({}), ctx.thread_id()).empty());
+ };
+ Nexus::run(num_threads, task);
}
-TEST_MT_F("require that selectBest works with too many hits from all threads", 5, MatchLoopCommunicator(num_threads, 13)) {
- if (thread_id < 3) {
- TEST_DO(equal(3u, makeScores(thread_id), selectBest(f1, makeScores(thread_id), thread_id)));
- } else {
- TEST_DO(equal(2u, makeScores(thread_id), selectBest(f1, makeScores(thread_id), thread_id)));
- }
+TEST(MatchLoopCommunicatorTest, require_that_selectBest_works_with_too_many_hits_from_all_threads)
+{
+ constexpr size_t num_threads = 5;
+ MatchLoopCommunicator f1(num_threads, 13);
+ auto task = [&f1](Nexus& ctx) {
+ auto thread_id = ctx.thread_id();
+ if (thread_id < 3) {
+ EXPECT_EQ(make_first_scores(thread_id, 3), selectBest(f1, makeScores(thread_id), thread_id));
+ } else {
+ EXPECT_EQ(make_first_scores(thread_id, 2), selectBest(f1, makeScores(thread_id), thread_id));
+ }
+ };
+ Nexus::run(num_threads, task);
}
-TEST_MT_F("require that selectBest works with some exhausted threads", 5, MatchLoopCommunicator(num_threads, 22)) {
- if (thread_id < 2) {
- TEST_DO(equal(5u, makeScores(thread_id), selectBest(f1, makeScores(thread_id), thread_id)));
- } else {
- TEST_DO(equal(4u, makeScores(thread_id), selectBest(f1, makeScores(thread_id), thread_id)));
- }
+TEST(MatchLoopCommunicatorTest, require_that_selectBest_works_with_some_exhausted_threads)
+{
+ constexpr size_t num_threads = 5;
+ MatchLoopCommunicator f1(num_threads, 22);
+ auto task = [&f1](Nexus& ctx) {
+ auto thread_id = ctx.thread_id();
+ if (thread_id < 2) {
+ EXPECT_EQ(makeScores(thread_id), selectBest(f1, makeScores(thread_id), thread_id));
+ } else {
+ EXPECT_EQ(make_first_scores(thread_id, 4), selectBest(f1, makeScores(thread_id), thread_id));
+ }
+ };
+ Nexus::run(num_threads, task);
}
-TEST_MT_F("require that selectBest can select all hits from all threads", 5, MatchLoopCommunicator(num_threads, 100)) {
- EXPECT_EQUAL(5u, selectBest(f1, makeScores(thread_id), thread_id).size());
+TEST(MatchLoopCommunicatorTest, require_that_selectBest_can_select_all_hits_from_all_threads)
+{
+ constexpr size_t num_threads = 5;
+ MatchLoopCommunicator f1(num_threads, 100);
+ auto task = [&f1](Nexus& ctx) {
+ auto thread_id = ctx.thread_id();
+ EXPECT_EQ(5u, selectBest(f1, makeScores(thread_id), thread_id).size());
+ };
+ Nexus::run(num_threads, task);
}
-TEST_MT_F("require that selectBest works with some empty threads", 10, MatchLoopCommunicator(num_threads, 7)) {
- if (thread_id < 2) {
- TEST_DO(equal(2u, makeScores(thread_id), selectBest(f1, makeScores(thread_id), thread_id)));
- } else if (thread_id < 5) {
- TEST_DO(equal(1u, makeScores(thread_id), selectBest(f1, makeScores(thread_id), thread_id)));
- } else {
- EXPECT_TRUE(selectBest(f1, makeScores(thread_id), thread_id).empty());
- }
+TEST(MatchLoopCommunicatorTest, require_that_selectBest_works_with_some_empty_threads)
+{
+ constexpr size_t num_threads = 5;
+ MatchLoopCommunicator f1(num_threads, 7);
+ auto task = [&f1](Nexus& ctx) {
+ auto thread_id = ctx.thread_id();
+ if (thread_id < 2) {
+ EXPECT_EQ(make_first_scores(thread_id, 2), selectBest(f1, makeScores(thread_id), thread_id));
+ } else if (thread_id < 5) {
+ EXPECT_EQ(make_first_scores(thread_id, 1), selectBest(f1, makeScores(thread_id), thread_id));
+ } else {
+ EXPECT_TRUE(selectBest(f1, makeScores(thread_id), thread_id).empty());
+ }
+ };
+ Nexus::run(num_threads, task);
}
-TEST_F("require that rangeCover works with a single thread", MatchLoopCommunicator(num_threads, 5)) {
+TEST(MatchLoopCommunicatorTest, require_that_rangeCover_works_with_a_single_thread)
+{
+ constexpr size_t num_threads = 1;
+ constexpr size_t thread_id = 0;
+ MatchLoopCommunicator f1(num_threads, 5);
RangePair res = rangeCover(f1, hit_vec({{1, 7.5}, {2, 1.5}}), thread_id, 10);
- TEST_DO(equal_ranges(RangePair({1.5, 7.5}, {11.5, 17.5}), res));
+ EXPECT_EQ(RangePair({1.5, 7.5}, {11.5, 17.5}), res);
}
-TEST_MT_F("require that rangeCover works with multiple threads", 5, MatchLoopCommunicator(num_threads, 10)) {
- RangePair res = rangeCover(f1, hit_vec({{thread_id * 100 + 1, 100.0 + thread_id}, {thread_id * 100 + 2, 100.0 - thread_id}}), thread_id, 10);
- TEST_DO(equal_ranges(RangePair({96.0, 104.0}, {106.0, 114.0}), res));
+TEST(MatchLoopCommunicatorTest, require_that_rangeCover_works_with_multiple_threads)
+{
+ constexpr size_t num_threads = 5;
+ MatchLoopCommunicator f1(num_threads, 10);
+ auto task = [&f1](Nexus& ctx) {
+ auto thread_id = ctx.thread_id();
+ RangePair res = rangeCover(f1, hit_vec({{thread_id * 100 + 1, 100.0 + thread_id}, {thread_id * 100 + 2, 100.0 - thread_id}}), thread_id, 10);
+ EXPECT_EQ(RangePair({96.0, 104.0}, {106.0, 114.0}), res);
+ };
+ Nexus::run(num_threads, task);
}
-TEST_MT_F("require that rangeCover works with no hits", 10, MatchLoopCommunicator(num_threads, 5)) {
- RangePair res = rangeCover(f1, hit_vec({}), thread_id, 10);
- TEST_DO(equal_ranges(RangePair({}, {}), res));
+TEST(MatchLoopCommunicatorTest, require_that_rangeCover_works_with_no_hits)
+{
+ constexpr size_t num_threads = 10;
+ MatchLoopCommunicator f1(num_threads, 5);
+ auto task = [&f1](Nexus& ctx) {
+ auto thread_id = ctx.thread_id();
+ RangePair res = rangeCover(f1, hit_vec({}), thread_id, 10);
+ EXPECT_EQ(RangePair({}, {}), res);
+ };
+ Nexus::run(num_threads, task);
}
-TEST_FFF("require that hits dropped due to lack of diversity affects range cover result",
- MatchLoopCommunicator(num_threads, 3),
- MatchLoopCommunicator(num_threads, 3, std::make_unique<EveryOdd>(), nullptr),
- MatchLoopCommunicator(num_threads, 3, std::make_unique<None>(), nullptr))
+TEST(MatchLoopCommunicatorTest, require_that_hits_dropped_due_to_lack_of_diversity_affects_range_cover_result)
{
+ constexpr size_t num_threads = 1;
+ constexpr size_t thread_id = 0;
+ MatchLoopCommunicator f1(num_threads, 3);
+ MatchLoopCommunicator f2(num_threads, 3, std::make_unique<EveryOdd>(), nullptr);
+ MatchLoopCommunicator f3(num_threads, 3, std::make_unique<None>(), nullptr);
auto hits_in = hit_vec({{1, 5}, {2, 4}, {3, 3}, {4, 2}, {5, 1}});
auto [my_work1, hits1, ranges1] = second_phase(f1, hits_in, thread_id, 10);
auto [my_work2, hits2, ranges2] = second_phase(f2, hits_in, thread_id, 10);
auto [my_work3, hits3, ranges3] = second_phase(f3, hits_in, thread_id, 10);
- EXPECT_EQUAL(my_work1, 3u);
- EXPECT_EQUAL(my_work2, 3u);
- EXPECT_EQUAL(my_work3, 0u);
+ EXPECT_EQ(my_work1, 3u);
+ EXPECT_EQ(my_work2, 3u);
+ EXPECT_EQ(my_work3, 0u);
- TEST_DO(equal(3u, hit_vec({{1, 15}, {2, 14}, {3, 13}}), hits1));
- TEST_DO(equal(3u, hit_vec({{1, 15}, {3, 13}, {5, 11}}), hits2));
- TEST_DO(equal(0u, hit_vec({}), hits3));
+ EXPECT_EQ(hit_vec({{1, 15}, {2, 14}, {3, 13}}), hits1);
+ EXPECT_EQ(hit_vec({{1, 15}, {3, 13}, {5, 11}}), hits2);
+ EXPECT_EQ(hit_vec({}), hits3);
- TEST_DO(equal_ranges(RangePair({3,5},{13,15}), ranges1));
- TEST_DO(equal_ranges(RangePair({4,5},{11,15}), ranges2)); // best dropped: 4
+ EXPECT_EQ(RangePair({3,5},{13,15}), ranges1);
+ EXPECT_EQ(RangePair({4,5},{11,15}), ranges2); // best dropped: 4
// note that the 'drops all hits due to diversity' case will
// trigger much of the same code path as dropping second phase
// ranking due to hard doom.
- TEST_DO(equal_ranges(RangePair({},{}), ranges3));
+ EXPECT_EQ(RangePair({},{}), ranges3);
}
-TEST_MT_F("require that estimate_match_frequency will count hits and docs across threads", 4, MatchLoopCommunicator(num_threads, 5)) {
- double freq = (0.0/10.0 + 1.0/11.0 + 2.0/12.0 + 3.0/13.0) / 4.0;
- EXPECT_APPROX(freq, f1.estimate_match_frequency(Matches(thread_id, thread_id + 10)), 0.00001);
+TEST(MatchLoopCommunicatorTest, require_that_estimate_match_frequency_will_count_hits_and_docs_across_threads)
+{
+ constexpr size_t num_threads = 4;
+ MatchLoopCommunicator f1(num_threads, 5);
+ auto task = [&f1](Nexus& ctx) {
+ auto thread_id = ctx.thread_id();
+ double freq = (0.0/10.0 + 1.0/11.0 + 2.0/12.0 + 3.0/13.0) / 4.0;
+ EXPECT_NEAR(freq, f1.estimate_match_frequency(Matches(thread_id, thread_id + 10)), 0.00001);
+ };
+ Nexus::run(num_threads, task);
}
-TEST_MT_F("require that second phase work is evenly distributed among search threads", 5, MatchLoopCommunicator(num_threads, 20)) {
- size_t num_hits = thread_id * 5;
- size_t docid = thread_id * 100;
- double score = thread_id * 100.0;
- Hits my_hits;
- for(size_t i = 0; i < num_hits; ++i) {
- my_hits.emplace_back(++docid, score);
- score -= 1.0;
- }
- auto [my_work, best_hits, ranges] = second_phase(f1, my_hits, thread_id, 1000.0);
- EXPECT_EQUAL(my_work, 4u);
- TEST_DO(equal_ranges(RangePair({381,400},{1381,1400}), ranges));
- if (thread_id == 4) {
- for (auto &hit: my_hits) {
- hit.second += 1000.0;
- }
- TEST_DO(equal(num_hits, my_hits, best_hits));
- } else {
- EXPECT_TRUE(best_hits.empty());
- }
+TEST(MatchLoopCommunicatorTest, require_that_second_phase_work_is_evenly_distributed_among_search_threads)
+{
+ constexpr size_t num_threads = 5;
+ MatchLoopCommunicator f1(num_threads, 20);
+ auto task = [&f1](Nexus& ctx) {
+ auto thread_id = ctx.thread_id();
+ size_t num_hits = thread_id * 5;
+ size_t docid = thread_id * 100;
+ double score = thread_id * 100.0;
+ Hits my_hits;
+ for(size_t i = 0; i < num_hits; ++i) {
+ my_hits.emplace_back(++docid, score);
+ score -= 1.0;
+ }
+ auto [my_work, best_hits, ranges] = second_phase(f1, my_hits, thread_id, 1000.0);
+ EXPECT_EQ(my_work, 4u);
+ EXPECT_EQ(RangePair({381,400},{1381,1400}), ranges);
+ if (thread_id == 4) {
+ for (auto &hit: my_hits) {
+ hit.second += 1000.0;
+ }
+ EXPECT_EQ(my_hits, best_hits);
+ } else {
+ EXPECT_TRUE(best_hits.empty());
+ }
+ };
+ Nexus::run(num_threads, task);
}
namespace {
@@ -226,8 +297,10 @@ using FeatureVec = std::vector<search::feature_t>;
}
-TEST("require that first phase rank lookup is populated")
+TEST(MatchLoopCommunicatorTest, require_that_first_phase_rank_lookup_is_populated)
{
+ constexpr size_t num_threads = 1;
+ constexpr size_t thread_id = 0;
FirstPhaseRankLookup l1;
FirstPhaseRankLookup l2;
MatchLoopCommunicator f1(num_threads, 3, {}, &l1);
@@ -235,8 +308,8 @@ TEST("require that first phase rank lookup is populated")
auto hits_in = hit_vec({{21, 5}, {22, 4}, {23, 3}, {24, 2}, {25, 1}});
auto res1 = second_phase(f1, hits_in, thread_id, 10);
auto res2 = second_phase(f2, hits_in, thread_id, 10);
- EXPECT_EQUAL(FeatureVec({1, 2, 3, unranked, unranked}), extract_ranks(l1));
- EXPECT_EQUAL(FeatureVec({1, unranked, 3, unranked, 5}), extract_ranks(l2));
+ EXPECT_EQ(FeatureVec({1, 2, 3, unranked, unranked}), extract_ranks(l1));
+ EXPECT_EQ(FeatureVec({1, unranked, 3, unranked, 5}), extract_ranks(l2));
}
-TEST_MAIN() { TEST_RUN_ALL(); }
+GTEST_MAIN_RUN_ALL_TESTS()
diff --git a/searchlib/src/vespa/searchlib/queryeval/scores.h b/searchlib/src/vespa/searchlib/queryeval/scores.h
index 19976a2831b..83b1ba9d406 100644
--- a/searchlib/src/vespa/searchlib/queryeval/scores.h
+++ b/searchlib/src/vespa/searchlib/queryeval/scores.h
@@ -24,6 +24,9 @@ struct Scores {
high = score;
}
}
+ bool operator==(const Scores& rhs) const {
+ return low == rhs.low && high == rhs.high;
+ }
};
}