Repo created

This commit is contained in:
Fr4nz D13trich 2025-11-22 13:58:55 +01:00
parent 4af19165ec
commit 68073add76
12458 changed files with 12350765 additions and 2 deletions

View file

@ -0,0 +1,22 @@
project(search_tests_support)
set(
SRC
helpers.cpp
helpers.hpp
test_results_matching.cpp
test_results_matching.hpp
test_search_engine.cpp
test_search_engine.hpp
test_search_request.cpp
test_search_request.hpp
test_with_custom_mwms.hpp
)
omim_add_library(${PROJECT_NAME} ${SRC})
target_link_libraries(${PROJECT_NAME}
generator_tests_support
editor_tests_support
pugixml # osm_editor.hpp -> editor_config.hpp
)

View file

@ -0,0 +1,143 @@
#include "search/search_tests_support/helpers.hpp"
#include "storage/country_info_getter.hpp"
#include "indexer/scales.hpp"
#include "geometry/mercator.hpp"
#include "geometry/rect2d.hpp"
namespace search
{
namespace tests_support
{
using namespace std;
SearchTestBase::SearchTestBase(base::LogLevel logLevel, bool mockCountryInfo)
: m_scopedLog(logLevel)
, m_engine(m_dataSource, {}, mockCountryInfo)
{
SetViewport(mercator::Bounds::FullRect());
if (!mockCountryInfo)
m_engine.InitAffiliations();
}
void SearchTestBase::SetViewport(ms::LatLon const & ll, double radiusM)
{
SetViewport(mercator::MetersToXY(ll.m_lon, ll.m_lat, radiusM));
}
bool SearchTestBase::CategoryMatch(std::string const & query, Rules const & rules, string const & locale /* = "en" */)
{
TestSearchRequest request(m_engine, query, locale, Mode::Everywhere, m_viewport);
request.SetCategorial();
request.Run();
return MatchResults(m_dataSource, rules, request.Results());
}
bool SearchTestBase::ResultsMatch(std::string const & query, Rules const & rules,
std::string const & locale /* = "en" */, Mode mode /* = Mode::Everywhere */)
{
TestSearchRequest request(m_engine, query, locale, mode, m_viewport);
request.Run();
return MatchResults(m_dataSource, rules, request.Results());
}
bool SearchTestBase::OrderedResultsMatch(std::string const & query, Rules const & rules,
std::string const & locale /* = "en" */, Mode mode /* = Mode::Everywhere */)
{
TestSearchRequest request(m_engine, query, locale, mode, m_viewport);
request.Run();
return OrderedResultsMatch(request.Results(), rules);
}
bool SearchTestBase::ResultsMatch(vector<search::Result> const & results, Rules const & rules)
{
return MatchResults(m_dataSource, rules, results);
}
bool SearchTestBase::OrderedResultsMatch(std::vector<Result> const & results, Rules const & rules)
{
if (results.size() != rules.size())
{
LOG(LWARNING, ("Unexpected results number:", results));
return false;
}
for (size_t i = 0; i < results.size(); ++i)
{
if (!ResultMatches(m_dataSource, rules[i], results[i]))
{
LOG(LWARNING, ("Not matched:", rules[i], results[i]));
return false;
}
}
return true;
}
bool SearchTestBase::IsResultMatches(search::Result const & result, Rule const & rule)
{
return tests_support::ResultMatches(m_dataSource, rule, result);
}
bool SearchTestBase::AlternativeMatch(string const & query, vector<Rules> const & rulesList)
{
TestSearchRequest request(m_engine, query, "en", Mode::Everywhere, m_viewport);
request.Run();
return tests_support::AlternativeMatch(m_dataSource, rulesList, request.Results());
}
size_t SearchTestBase::GetResultsNumber(string const & query, string const & locale)
{
TestSearchRequest request(m_engine, query, locale, Mode::Everywhere, m_viewport);
request.Run();
return request.Results().size();
}
SearchParams SearchTestBase::GetDefaultSearchParams(string const & query, string const & locale /* = "en" */) const
{
SearchParams params;
params.m_query = query;
params.m_inputLocale = locale;
params.m_viewport = m_viewport;
params.m_mode = Mode::Everywhere;
params.m_needAddress = true;
params.m_suggestsEnabled = false;
return params;
}
unique_ptr<TestSearchRequest> SearchTestBase::MakeRequest(SearchParams const & params)
{
auto request = make_unique<TestSearchRequest>(m_engine, params);
request->Run();
return request;
}
size_t SearchTestBase::CountFeatures(m2::RectD const & rect)
{
size_t count = 0;
auto counter = [&count](FeatureType const & /* ft */) { ++count; };
m_dataSource.ForEachInRect(counter, rect, scales::GetUpperScale());
return count;
}
void SearchTest::RegisterCountry(string const & name, m2::RectD const & rect)
{
auto & infoGetter = dynamic_cast<storage::CountryInfoGetterForTesting &>(m_engine.GetCountryInfoGetter());
infoGetter.AddCountry(storage::CountryDef(name, rect));
}
void SearchTest::OnMwmBuilt(MwmInfo const & info)
{
switch (info.GetType())
{
case MwmInfo::COUNTRY: RegisterCountry(info.GetCountryName(), info.m_bordersRect); break;
case MwmInfo::WORLD: m_engine.LoadCitiesBoundaries(); break;
case MwmInfo::COASTS: break;
}
}
} // namespace tests_support
} // namespace search

View file

@ -0,0 +1,101 @@
#pragma once
#include "search/search_tests_support/test_results_matching.hpp"
#include "search/search_tests_support/test_search_engine.hpp"
#include "search/search_tests_support/test_search_request.hpp"
#include "search/search_tests_support/test_with_custom_mwms.hpp"
#include "generator/generator_tests_support/test_feature.hpp"
#include "geometry/rect2d.hpp"
#include <memory>
#include <string>
#include <vector>
namespace search
{
namespace tests_support
{
class SearchTestBase : public TestWithCustomMwms
{
public:
using Rule = std::shared_ptr<MatchingRule>;
using Rules = std::vector<Rule>;
SearchTestBase(base::LogLevel logLevel, bool mockCountryInfo);
inline void SetViewport(m2::RectD const & viewport) { m_viewport = viewport; }
void SetViewport(ms::LatLon const & ll, double radiusM);
bool CategoryMatch(std::string const & query, Rules const & rules, std::string const & locale = "en");
bool ResultsMatch(std::string const & query, Rules const & rules, std::string const & locale = "en",
Mode mode = Mode::Everywhere);
bool OrderedResultsMatch(std::string const & query, Rules const & rules, std::string const & locale = "en",
Mode mode = Mode::Everywhere);
bool ResultsMatch(std::vector<Result> const & results, Rules const & rules);
bool OrderedResultsMatch(std::vector<Result> const & results, Rules const & rules);
bool IsResultMatches(Result const & result, Rule const & rule);
bool AlternativeMatch(std::string const & query, std::vector<Rules> const & rulesList);
size_t GetResultsNumber(std::string const & query, std::string const & locale);
SearchParams GetDefaultSearchParams(std::string const & query, std::string const & locale = "en") const;
std::unique_ptr<TestSearchRequest> MakeRequest(SearchParams const & params);
std::unique_ptr<TestSearchRequest> MakeRequest(std::string const & query, std::string const & locale = "en")
{
return MakeRequest(GetDefaultSearchParams(query, locale));
}
size_t CountFeatures(m2::RectD const & rect);
protected:
base::ScopedLogLevelChanger m_scopedLog;
TestSearchEngine m_engine;
m2::RectD m_viewport;
};
class SearchTest : public SearchTestBase
{
public:
explicit SearchTest(base::LogLevel logLevel = base::LDEBUG) : SearchTestBase(logLevel, true /* mockCountryInfo*/) {}
// Registers country in internal records. Note that physical country file may be absent.
void RegisterCountry(std::string const & name, m2::RectD const & rect);
protected:
void OnMwmBuilt(MwmInfo const & /* info */) override;
};
class TestCafe : public generator::tests_support::TestPOI
{
public:
TestCafe(m2::PointD const & center, std::string const & name, std::string const & lang) : TestPOI(center, name, lang)
{
SetTypes({{"amenity", "cafe"}});
}
explicit TestCafe(m2::PointD const & center) : TestCafe(center, "cafe", "en") {}
};
class TestHotel : public generator::tests_support::TestPOI
{
public:
TestHotel(m2::PointD const & center, std::string const & name, std::string const & lang) : TestPOI(center, name, lang)
{
SetTypes({{"tourism", "hotel"}});
}
explicit TestHotel(m2::PointD const & center) : TestHotel(center, "hotel", "en") {}
};
} // namespace tests_support
} // namespace search

View file

@ -0,0 +1,127 @@
#include "search/search_tests_support/test_results_matching.hpp"
#include "generator/generator_tests_support/test_feature.hpp"
#include "indexer/data_source.hpp"
#include "indexer/feature_decl.hpp"
#include <algorithm>
#include <sstream>
using namespace std;
using namespace generator::tests_support;
namespace search
{
namespace tests_support
{
ExactMatchingRule::ExactMatchingRule(MwmSet::MwmId const & mwmId, TestFeature const & feature)
: m_mwmId(mwmId)
, m_feature(feature)
{}
bool ExactMatchingRule::Matches(FeatureType & feature) const
{
if (m_mwmId != feature.GetID().m_mwmId)
return false;
return m_feature.Matches(feature);
}
string ExactMatchingRule::ToString() const
{
ostringstream os;
os << "ExactMatchingRule [ " << DebugPrint(m_mwmId) << ", " << DebugPrint(m_feature) << " ]";
return os.str();
}
AlternativesMatchingRule::AlternativesMatchingRule(vector<shared_ptr<MatchingRule>> && rules)
: m_rules(std::move(rules))
{}
bool AlternativesMatchingRule::Matches(FeatureType & feature) const
{
for (auto const & rule : m_rules)
if (rule->Matches(feature))
return true;
return false;
}
string AlternativesMatchingRule::ToString() const
{
ostringstream os;
os << "OrRule [ ";
for (auto it = m_rules.cbegin(); it != m_rules.cend(); ++it)
{
os << (*it)->ToString();
if (it + 1 != m_rules.cend())
os << " | ";
}
os << " ]";
return os.str();
}
bool MatchResults(DataSource const & dataSource, vector<shared_ptr<MatchingRule>> rules,
vector<search::Result> const & actual)
{
vector<FeatureID> resultIds;
resultIds.reserve(actual.size());
for (auto const & a : actual)
resultIds.push_back(a.GetFeatureID());
sort(resultIds.begin(), resultIds.end());
vector<string> unexpected;
auto removeMatched = [&rules, &unexpected](FeatureType & feature)
{
for (auto it = rules.begin(); it != rules.end(); ++it)
{
if ((*it)->Matches(feature))
{
rules.erase(it);
return;
}
}
unexpected.push_back(feature.DebugString());
};
dataSource.ReadFeatures(removeMatched, resultIds);
if (rules.empty() && unexpected.empty())
return true;
ostringstream os;
os << "Unsatisfied rules:" << endl;
for (auto const & e : rules)
os << " " << DebugPrint(*e) << endl;
os << "Unexpected retrieved features:" << endl;
for (auto const & u : unexpected)
os << " " << u << endl;
LOG(LWARNING, (os.str()));
return false;
}
bool MatchResults(DataSource const & dataSource, vector<shared_ptr<MatchingRule>> rules, search::Results const & actual)
{
vector<search::Result> const results(actual.begin(), actual.end());
return MatchResults(dataSource, rules, results);
}
bool ResultMatches(DataSource const & dataSource, shared_ptr<MatchingRule> rule, search::Result const & result)
{
bool matches = false;
dataSource.ReadFeature([&](FeatureType & ft) { matches = rule->Matches(ft); }, result.GetFeatureID());
return matches;
}
bool AlternativeMatch(DataSource const & dataSource, vector<vector<shared_ptr<MatchingRule>>> rulesList,
std::vector<search::Result> const & results)
{
return any_of(rulesList.begin(), rulesList.end(), [&](vector<shared_ptr<MatchingRule>> const & rules)
{ return MatchResults(dataSource, rules, results); });
}
string DebugPrint(MatchingRule const & rule)
{
return rule.ToString();
}
} // namespace tests_support
} // namespace search

View file

@ -0,0 +1,85 @@
#pragma once
#include "search/result.hpp"
#include "indexer/mwm_set.hpp"
#include <initializer_list>
#include <memory>
#include <string>
#include <utility>
#include <vector>
class FeatureType;
class DataSource;
namespace generator
{
namespace tests_support
{
class TestFeature;
}
} // namespace generator
namespace search
{
namespace tests_support
{
class MatchingRule
{
public:
virtual ~MatchingRule() = default;
virtual bool Matches(FeatureType & feature) const = 0;
virtual std::string ToString() const = 0;
};
class ExactMatchingRule : public MatchingRule
{
public:
ExactMatchingRule(MwmSet::MwmId const & mwmId, generator::tests_support::TestFeature const & feature);
// MatchingRule overrides:
bool Matches(FeatureType & feature) const override;
std::string ToString() const override;
private:
MwmSet::MwmId m_mwmId;
generator::tests_support::TestFeature const & m_feature;
};
class AlternativesMatchingRule : public MatchingRule
{
public:
AlternativesMatchingRule(std::vector<std::shared_ptr<MatchingRule>> && rules);
// MatchingRule overrides:
bool Matches(FeatureType & feature) const override;
std::string ToString() const override;
private:
std::vector<std::shared_ptr<MatchingRule>> m_rules;
};
template <typename... Args>
std::shared_ptr<MatchingRule> ExactMatch(Args &&... args)
{
return std::make_shared<ExactMatchingRule>(std::forward<Args>(args)...);
}
inline std::shared_ptr<MatchingRule> AlternativesMatch(std::vector<std::shared_ptr<MatchingRule>> && rules)
{
return std::make_shared<AlternativesMatchingRule>(std::move(rules));
}
bool MatchResults(DataSource const & dataSource, std::vector<std::shared_ptr<MatchingRule>> rules,
std::vector<search::Result> const & actual);
bool MatchResults(DataSource const & dataSource, std::vector<std::shared_ptr<MatchingRule>> rules,
search::Results const & actual);
bool ResultMatches(DataSource const & dataSource, std::shared_ptr<MatchingRule> rule, search::Result const & result);
bool AlternativeMatch(DataSource const & dataSource, std::vector<std::vector<std::shared_ptr<MatchingRule>>> rules,
std::vector<search::Result> const & results);
std::string DebugPrint(MatchingRule const & rule);
} // namespace tests_support
} // namespace search

View file

@ -0,0 +1,35 @@
#include "search/search_tests_support/test_search_engine.hpp"
#include "indexer/categories_holder.hpp"
#include "storage/storage.hpp"
#include "platform/platform.hpp"
#include <utility>
namespace search
{
namespace tests_support
{
using namespace std;
TestSearchEngine::TestSearchEngine(DataSource & dataSource, Engine::Params const & params, bool mockCountryInfo)
: m_infoGetter(mockCountryInfo ? make_unique<storage::CountryInfoGetterForTesting>()
: storage::CountryInfoReader::CreateCountryInfoGetter(GetPlatform()))
, m_engine(dataSource, GetDefaultCategories(), *m_infoGetter, params)
{}
void TestSearchEngine::InitAffiliations()
{
storage::Storage storage;
m_affiliations = *storage.GetAffiliations();
m_infoGetter->SetAffiliations(&m_affiliations);
}
weak_ptr<ProcessorHandle> TestSearchEngine::Search(SearchParams const & params)
{
return m_engine.Search(params);
}
} // namespace tests_support
} // namespace search

View file

@ -0,0 +1,40 @@
#pragma once
#include "search/engine.hpp"
#include "storage/country_info_getter.hpp"
#include "storage/storage_defines.hpp"
#include <memory>
#include <string>
class DataSource;
namespace search
{
struct SearchParams;
namespace tests_support
{
class TestSearchEngine
{
public:
TestSearchEngine(DataSource & dataSource, Engine::Params const & params, bool mockCountryInfo = false);
void InitAffiliations();
void SetLocale(std::string const & locale) { m_engine.SetLocale(locale); }
void LoadCitiesBoundaries() { m_engine.LoadCitiesBoundaries(); }
std::weak_ptr<ProcessorHandle> Search(SearchParams const & params);
storage::CountryInfoGetter & GetCountryInfoGetter() { return *m_infoGetter; }
private:
storage::Affiliations m_affiliations;
std::unique_ptr<storage::CountryInfoGetter> m_infoGetter;
Engine m_engine;
};
} // namespace tests_support
} // namespace search

View file

@ -0,0 +1,135 @@
#include "search/search_tests_support/test_search_request.hpp"
#include "search/search_tests_support/test_search_engine.hpp"
#include "base/assert.hpp"
#include <functional>
namespace search
{
namespace tests_support
{
using namespace std::chrono;
using namespace std;
TestSearchRequest::TestSearchRequest(TestSearchEngine & engine, string const & query, string const & locale, Mode mode,
m2::RectD const & viewport)
: m_engine(engine)
{
m_params.m_query = query;
m_params.m_inputLocale = locale;
m_params.m_viewport = viewport;
m_params.m_mode = mode;
m_params.m_useDebugInfo = true;
SetUpCallbacks();
SetUpResultParams();
}
TestSearchRequest::TestSearchRequest(TestSearchEngine & engine, SearchParams const & params)
: m_engine(engine)
, m_params(params)
{
m_params.m_useDebugInfo = true;
SetUpCallbacks();
}
TestSearchRequest::TestSearchRequest(TestSearchEngine & engine, string const & query, string const & locale, Mode mode,
m2::RectD const & viewport, SearchParams::OnStarted const & onStarted,
SearchParams::OnResults const & onResults)
: m_engine(engine)
{
m_params.m_query = query;
m_params.m_inputLocale = locale;
m_params.m_viewport = viewport;
m_params.m_mode = mode;
m_params.m_onStarted = onStarted;
m_params.m_onResults = onResults;
m_params.m_useDebugInfo = true;
SetUpResultParams();
}
void TestSearchRequest::Run()
{
Start();
Wait();
}
TestSearchRequest::TimeDurationT TestSearchRequest::ResponseTime() const
{
lock_guard<mutex> lock(m_mu);
CHECK(m_done, ("This function may be called only when request is processed."));
return m_endTime - m_startTime;
}
vector<search::Result> const & TestSearchRequest::Results() const
{
lock_guard<mutex> lock(m_mu);
CHECK(m_done, ("This function may be called only when request is processed."));
return m_results;
}
void TestSearchRequest::Start()
{
m_engine.Search(m_params);
}
void TestSearchRequest::Wait()
{
unique_lock<mutex> lock(m_mu);
m_cv.wait(lock, [this]() { return m_done; });
}
void TestSearchRequest::SetUpCallbacks()
{
m_params.m_onStarted = bind(&TestSearchRequest::OnStarted, this);
m_params.m_onResults = bind(&TestSearchRequest::OnResults, this, placeholders::_1);
}
void TestSearchRequest::SetUpResultParams()
{
switch (m_params.m_mode)
{
case Mode::Everywhere:
m_params.m_needAddress = true;
m_params.m_suggestsEnabled = false;
m_params.m_needHighlighting = true;
break;
case Mode::Viewport: // fallthrough
case Mode::Downloader: // fallthrough
case Mode::Bookmarks:
m_params.m_needAddress = false;
m_params.m_suggestsEnabled = false;
m_params.m_needHighlighting = false;
break;
case Mode::Count: CHECK(false, ()); break;
}
}
void TestSearchRequest::OnStarted()
{
lock_guard<mutex> lock(m_mu);
m_startTime = m_timer.TimeElapsed();
}
void TestSearchRequest::OnResults(search::Results const & results)
{
lock_guard<mutex> lock(m_mu);
m_results.assign(results.begin(), results.end());
if (results.IsEndMarker())
{
m_done = true;
m_endTime = m_timer.TimeElapsed();
m_cv.notify_one();
}
}
void TestSearchRequest::SetCustomOnResults(SearchParams::OnResults const & onResults)
{
m_params.m_onResults = onResults;
}
} // namespace tests_support
} // namespace search

View file

@ -0,0 +1,75 @@
#pragma once
#include "geometry/rect2d.hpp"
#include "search/result.hpp"
#include "search/search_params.hpp"
#include "base/timer.hpp"
#include <chrono>
#include <condition_variable>
#include <mutex>
#include <string>
#include <vector>
namespace search
{
namespace tests_support
{
class TestSearchEngine;
// This class wraps a search query to a search engine. Note that the
// last token will be handled as a complete token iff it will be
// followed by a space in a query.
class TestSearchRequest
{
public:
TestSearchRequest(TestSearchEngine & engine, std::string const & query, std::string const & locale, Mode mode,
m2::RectD const & viewport);
TestSearchRequest(TestSearchEngine & engine, SearchParams const & params);
void SetCategorial() { m_params.m_categorialRequest = true; }
// Initiates the search and waits for it to finish.
void Run();
// Initiates asynchronous search.
void Start();
// Waits for the search to finish.
void Wait();
// Call these functions only after call to Wait().
using TimeDurationT = base::Timer::DurationT;
TimeDurationT ResponseTime() const;
std::vector<search::Result> const & Results() const;
protected:
TestSearchRequest(TestSearchEngine & engine, std::string const & query, std::string const & locale, Mode mode,
m2::RectD const & viewport, SearchParams::OnStarted const & onStarted,
SearchParams::OnResults const & onResults);
void SetUpCallbacks();
void SetUpResultParams();
void OnStarted();
void OnResults(search::Results const & results);
// Overrides the default onResults callback.
void SetCustomOnResults(SearchParams::OnResults const & onResults);
std::condition_variable m_cv;
mutable std::mutex m_mu;
std::vector<search::Result> m_results;
bool m_done = false;
base::Timer m_timer;
TimeDurationT m_startTime, m_endTime;
TestSearchEngine & m_engine;
SearchParams m_params;
};
} // namespace tests_support
} // namespace search

View file

@ -0,0 +1,38 @@
#pragma once
#include "editor/editable_data_source.hpp"
#include "editor/editor_tests_support/helpers.hpp"
#include "generator/generator_tests_support/test_with_custom_mwms.hpp"
#include "search/editor_delegate.hpp"
#include "indexer/feature.hpp"
#include "base/assert.hpp"
#include <memory>
#include <utility>
namespace search
{
namespace tests_support
{
class TestWithCustomMwms : public generator::tests_support::TestWithCustomMwms
{
public:
TestWithCustomMwms() { editor::tests_support::SetUpEditorForTesting(std::make_unique<EditorDelegate>(m_dataSource)); }
~TestWithCustomMwms() override { editor::tests_support::TearDownEditorForTesting(); }
template <typename EditorFn>
void EditFeature(FeatureID const & id, EditorFn && fn)
{
FeaturesLoaderGuard loader(m_dataSource, id.m_mwmId);
auto ft = loader.GetFeatureByIndex(id.m_index);
CHECK(ft, ());
editor::tests_support::EditFeature(*ft, std::forward<EditorFn>(fn));
}
};
} // namespace tests_support
} // namespace search