Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mapsme/omim.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYuri Gorshenin <y@maps.me>2016-08-02 14:06:14 +0300
committerYuri Gorshenin <y@maps.me>2016-08-03 13:11:06 +0300
commit0badc084a1043e06e429c7cd66176462c4fa33ab (patch)
tree1460f4d608bbc707e0225c6d7241e66b97774eaa
parent1e6dd75476400e45ab8bf49ba7bd4c7a65cde368 (diff)
[search][ui] Implemented hotels classifier.
-rw-r--r--map/displacement_mode_manager.cpp26
-rw-r--r--map/displacement_mode_manager.hpp38
-rw-r--r--map/framework.cpp47
-rw-r--r--map/framework.hpp7
-rw-r--r--map/map.pro4
-rw-r--r--qt/search_panel.cpp14
-rw-r--r--search/hotels_classifier.cpp28
-rw-r--r--search/hotels_classifier.hpp24
-rw-r--r--search/interactive_search_callback.cpp25
-rw-r--r--search/interactive_search_callback.hpp32
-rw-r--r--search/intermediate_result.cpp6
-rw-r--r--search/result.hpp18
-rw-r--r--search/search.pro4
-rw-r--r--search/search_integration_tests/interactive_search_test.cpp110
-rw-r--r--search/search_integration_tests/search_integration_tests.pro1
-rw-r--r--search/search_tests_support/test_search_request.cpp13
-rw-r--r--search/search_tests_support/test_search_request.hpp7
17 files changed, 370 insertions, 34 deletions
diff --git a/map/displacement_mode_manager.cpp b/map/displacement_mode_manager.cpp
new file mode 100644
index 0000000000..2135274563
--- /dev/null
+++ b/map/displacement_mode_manager.cpp
@@ -0,0 +1,26 @@
+#include "map/displacement_mode_manager.hpp"
+
+DisplacementModeManager::DisplacementModeManager(TCallback && callback)
+ : m_callback(move(callback)), m_mask(0)
+{
+}
+
+void DisplacementModeManager::Set(Slot slot, bool show)
+{
+ lock_guard<mutex> lock(m_mu);
+
+ uint32_t const bit = static_cast<uint32_t>(1) << slot;
+ uint32_t const mask = m_mask;
+ if (show)
+ m_mask = mask | bit;
+ else
+ m_mask = mask & ~bit;
+
+ if (mask == m_mask)
+ return;
+
+ if (m_mask != 0)
+ m_callback(true /* show */);
+ else
+ m_callback(false /* show */);
+}
diff --git a/map/displacement_mode_manager.hpp b/map/displacement_mode_manager.hpp
new file mode 100644
index 0000000000..a86f7846a6
--- /dev/null
+++ b/map/displacement_mode_manager.hpp
@@ -0,0 +1,38 @@
+#pragma once
+
+#include "std/cstdint.hpp"
+#include "std/function.hpp"
+#include "std/mutex.hpp"
+
+// This class should be used as a single point to control whether
+// hotels displacement mode should be activated or not.
+//
+// *NOTE* as the class is thread-safe, the callback should be
+// very-very lightweight and it should be guaranteed that callback or
+// its callees do not call DisplacementModeManager API. Otherwise,
+// deadlock or exception may happen.
+class DisplacementModeManager
+{
+public:
+ using TCallback = function<void(bool show)>;
+
+ enum Slot
+ {
+ SLOT_INTERACTIVE_SEARCH,
+ SLOT_MAP_SELECTION,
+ SLOT_DEBUG,
+ SLOT_COUNT
+ };
+
+ DisplacementModeManager(TCallback && callback);
+
+ void Set(Slot slot, bool show);
+
+private:
+ TCallback m_callback;
+
+ mutex m_mu;
+ uint32_t m_mask;
+
+ static_assert(SLOT_COUNT <= sizeof(m_mask) * CHAR_BIT, "Number of slots is too large");
+};
diff --git a/map/framework.cpp b/map/framework.cpp
index b5f9ed046b..0aad67a9a3 100644
--- a/map/framework.cpp
+++ b/map/framework.cpp
@@ -13,6 +13,7 @@
#include "search/engine.hpp"
#include "search/geometry_utils.hpp"
+#include "search/interactive_search_callback.hpp"
#include "search/intermediate_result.hpp"
#include "search/processor_factory.hpp"
#include "search/result.hpp"
@@ -303,6 +304,10 @@ Framework::Framework()
, m_storage(platform::migrate::NeedMigrate() ? COUNTRIES_OBSOLETE_FILE : COUNTRIES_FILE)
, m_bmManager(*this)
, m_isRenderingEnabled(true)
+ , m_displacementModeManager([this](bool show) {
+ int const mode = show ? dp::displacement::kHotelMode : dp::displacement::kDefaultMode;
+ CallDrapeFunction(bind(&df::DrapeEngine::SetDisplacementMode, _1, mode));
+ })
, m_lastReportedCountry(kInvalidCountryId)
{
m_startBackgroundTime = my::Timer::LocalTime();
@@ -914,9 +919,9 @@ void Framework::PrepareToShutdown()
DestroyDrapeEngine();
}
-void Framework::SetDisplacementMode(int mode)
+void Framework::SetDisplacementMode(DisplacementModeManager::Slot slot, bool show)
{
- CallDrapeFunction(bind(&df::DrapeEngine::SetDisplacementMode, _1, mode));
+ m_displacementModeManager.Set(slot, show);
}
void Framework::SaveViewport()
@@ -1066,26 +1071,30 @@ void Framework::InvalidateRect(m2::RectD const & rect)
void Framework::StartInteractiveSearch(search::SearchParams const & params)
{
- using namespace search;
-
- auto const originalOnResults = params.m_onResults;
+ auto const onResults = params.m_onResults;
m_lastInteractiveSearchParams = params;
m_lastInteractiveSearchParams.SetForceSearch(false);
m_lastInteractiveSearchParams.SetMode(search::Mode::Viewport);
m_lastInteractiveSearchParams.SetSuggestsEnabled(false);
- m_lastInteractiveSearchParams.m_onResults = [this, originalOnResults](Results const & results) {
- if (!results.IsEndMarker())
- {
- GetPlatform().RunOnGuiThread([this, results]() {
- if (IsInteractiveSearchActive())
- FillSearchResultsMarks(results);
- });
- }
- if (originalOnResults)
- originalOnResults(results);
- };
+ m_lastInteractiveSearchParams.m_onResults = search::InteractiveSearchCallback(
+ [this]() {
+ SetDisplacementMode(DisplacementModeManager::SLOT_INTERACTIVE_SEARCH, true /* show */);
+ } /* setMode */,
+ [this, onResults](search::Results const & results) {
+ if (!results.IsEndMarker())
+ {
+ GetPlatform().RunOnGuiThread([this, results]() {
+ if (IsInteractiveSearchActive())
+ FillSearchResultsMarks(results);
+ });
+ }
+
+ if (onResults)
+ onResults(results);
+ } /* onResults */);
+
UpdateUserViewportChanged();
}
@@ -1474,6 +1483,7 @@ void Framework::CancelInteractiveSearch()
if (IsInteractiveSearchActive())
{
m_lastInteractiveSearchParams.Clear();
+ SetDisplacementMode(DisplacementModeManager::SLOT_INTERACTIVE_SEARCH, false /* show */);
CancelQuery(m_lastProcessorHandle);
}
}
@@ -1938,8 +1948,7 @@ void Framework::ActivateMapSelection(bool needAnimation, df::SelectionShape::ESe
CallDrapeFunction(bind(&df::DrapeEngine::SelectObject, _1, selectionType, info.GetMercator(), info.GetID(),
needAnimation));
- SetDisplacementMode(info.IsHotel() ? dp::displacement::kHotelMode
- : dp::displacement::kDefaultMode);
+ SetDisplacementMode(DisplacementModeManager::SLOT_MAP_SELECTION, info.IsHotel() /* show */);
if (m_activateMapSelectionFn)
m_activateMapSelectionFn(info);
@@ -1958,7 +1967,7 @@ void Framework::DeactivateMapSelection(bool notifyUI)
if (somethingWasAlreadySelected)
CallDrapeFunction(bind(&df::DrapeEngine::DeselectObject, _1));
- SetDisplacementMode(dp::displacement::kDefaultMode);
+ SetDisplacementMode(DisplacementModeManager::SLOT_MAP_SELECTION, false /* show */);
}
void Framework::UpdatePlacePageInfoForCurrentSelection()
diff --git a/map/framework.hpp b/map/framework.hpp
index 5f3940c480..1c807b6e10 100644
--- a/map/framework.hpp
+++ b/map/framework.hpp
@@ -4,9 +4,10 @@
#include "map/booking_api.hpp"
#include "map/bookmark.hpp"
#include "map/bookmark_manager.hpp"
-#include "map/place_page_info.hpp"
+#include "map/displacement_mode_manager.hpp"
#include "map/feature_vec_model.hpp"
#include "map/mwm_url.hpp"
+#include "map/place_page_info.hpp"
#include "map/track.hpp"
#include "drape_frontend/gui/skin.hpp"
@@ -381,12 +382,14 @@ public:
void PrepareToShutdown();
- void SetDisplacementMode(int mode);
+ void SetDisplacementMode(DisplacementModeManager::Slot slot, bool show);
private:
void InitCountryInfoGetter();
void InitSearchEngine();
+ DisplacementModeManager m_displacementModeManager;
+
// Last search query params for the interactive search.
search::SearchParams m_lastInteractiveSearchParams;
diff --git a/map/map.pro b/map/map.pro
index bb16d45fbc..582f3744d1 100644
--- a/map/map.pro
+++ b/map/map.pro
@@ -15,6 +15,7 @@ HEADERS += \
booking_api.hpp \
bookmark.hpp \
bookmark_manager.hpp \
+ displacement_mode_manager.hpp \
feature_vec_model.hpp \
framework.hpp \
ge0_parser.hpp \
@@ -37,13 +38,14 @@ SOURCES += \
booking_api.cpp \
bookmark.cpp \
bookmark_manager.cpp \
+ displacement_mode_manager.cpp \
feature_vec_model.cpp \
framework.cpp \
ge0_parser.cpp \
geourl_process.cpp \
gps_track.cpp \
- gps_track_filter.cpp \
gps_track_collection.cpp \
+ gps_track_filter.cpp \
gps_track_storage.cpp \
gps_tracker.cpp \
mwm_url.cpp \
diff --git a/qt/search_panel.cpp b/qt/search_panel.cpp
index 85c6476b65..65095e57b1 100644
--- a/qt/search_panel.cpp
+++ b/qt/search_panel.cpp
@@ -249,12 +249,18 @@ bool SearchPanel::TryDisplacementModeCmd(QString const & str)
if (!isDefaultDisplacementMode && !isHotelDisplacementMode)
return false;
-
+
if (isDefaultDisplacementMode)
- m_pDrawWidget->GetFramework().SetDisplacementMode(dp::displacement::kDefaultMode);
+ {
+ m_pDrawWidget->GetFramework().SetDisplacementMode(DisplacementModeManager::SLOT_DEBUG,
+ false /* show */);
+ }
else if (isHotelDisplacementMode)
- m_pDrawWidget->GetFramework().SetDisplacementMode(dp::displacement::kHotelMode);
-
+ {
+ m_pDrawWidget->GetFramework().SetDisplacementMode(DisplacementModeManager::SLOT_DEBUG,
+ true /* show */);
+ }
+
return true;
}
diff --git a/search/hotels_classifier.cpp b/search/hotels_classifier.cpp
new file mode 100644
index 0000000000..4a21d3cb67
--- /dev/null
+++ b/search/hotels_classifier.cpp
@@ -0,0 +1,28 @@
+#include "search/hotels_classifier.hpp"
+
+#include "search/result.hpp"
+
+namespace search
+{
+void HotelsClassifier::AddBatch(Results const & results)
+{
+ if (results.IsEndMarker())
+ return;
+
+ for (auto const & result : results)
+ {
+ ++m_numResults;
+ if (result.m_metadata.m_isHotel)
+ ++m_numHotels;
+ }
+}
+
+bool HotelsClassifier::IsHotelQuery() const
+{
+ // Threshold used to activate hotels mode. Probably is too strict,
+ // but we don't have statistics now.
+ double const kThreshold = 0.95;
+
+ return m_numResults == 0 ? false : m_numHotels >= kThreshold * m_numResults;
+}
+} // namespace search
diff --git a/search/hotels_classifier.hpp b/search/hotels_classifier.hpp
new file mode 100644
index 0000000000..65a8501208
--- /dev/null
+++ b/search/hotels_classifier.hpp
@@ -0,0 +1,24 @@
+#pragma once
+
+#include "std/cstdint.hpp"
+
+namespace search
+{
+class Results;
+
+// A binary classifier, that can be used in conjunction with search
+// engine to decide whether the majority of results are hotels or not.
+//
+// *NOTE* This class is NOT thread safe.
+class HotelsClassifier
+{
+public:
+ void AddBatch(Results const & results);
+
+ bool IsHotelQuery() const;
+
+private:
+ uint64_t m_numHotels = 0;
+ uint64_t m_numResults = 0;
+};
+} // namespace search
diff --git a/search/interactive_search_callback.cpp b/search/interactive_search_callback.cpp
new file mode 100644
index 0000000000..d7981bab4d
--- /dev/null
+++ b/search/interactive_search_callback.cpp
@@ -0,0 +1,25 @@
+#include "search/interactive_search_callback.hpp"
+
+#include "search/result.hpp"
+
+namespace search
+{
+InteractiveSearchCallback::InteractiveSearchCallback(TSetDisplacementMode && setMode,
+ TOnResults && onResults)
+ : m_setMode(move(setMode)), m_onResults(move(onResults)), m_hotelsModeSet(false)
+{
+}
+
+void InteractiveSearchCallback::operator()(search::Results const & results)
+{
+ m_hotelsClassif.AddBatch(results);
+
+ if (!m_hotelsModeSet && m_hotelsClassif.IsHotelQuery())
+ {
+ m_setMode();
+ m_hotelsModeSet = true;
+ }
+
+ m_onResults(results);
+}
+} // namespace search
diff --git a/search/interactive_search_callback.hpp b/search/interactive_search_callback.hpp
new file mode 100644
index 0000000000..8d42f537a0
--- /dev/null
+++ b/search/interactive_search_callback.hpp
@@ -0,0 +1,32 @@
+#pragma once
+
+#include "search/hotels_classifier.hpp"
+#include "search/params.hpp"
+
+#include "std/function.hpp"
+
+namespace search
+{
+class Results;
+
+// An on-results-callback that should be used for interactive search.
+//
+// *NOTE* the class is NOT thread safe.
+class InteractiveSearchCallback
+{
+public:
+ using TSetDisplacementMode = function<void()>;
+ using TOnResults = search::TOnResults;
+
+ InteractiveSearchCallback(TSetDisplacementMode && setMode, TOnResults && onResults);
+
+ void operator()(search::Results const & results);
+
+private:
+ TSetDisplacementMode m_setMode;
+ TOnResults m_onResults;
+
+ search::HotelsClassifier m_hotelsClassif;
+ bool m_hotelsModeSet;
+};
+} // namespace search
diff --git a/search/intermediate_result.cpp b/search/intermediate_result.cpp
index 0a983e036c..c0652c078c 100644
--- a/search/intermediate_result.cpp
+++ b/search/intermediate_result.cpp
@@ -29,7 +29,8 @@ double const DIST_SAME_STREET = 5000.0;
char const * const kEmptyRatingSymbol = "-";
char const * const kPricingSymbol = "$";
-void ProcessMetadata(FeatureType const & ft, Result::Metadata & meta)
+void ProcessMetadata(FeatureType const & ft, feature::TypesHolder const & types,
+ Result::Metadata & meta)
{
if (meta.m_isInitialized)
return;
@@ -56,6 +57,7 @@ void ProcessMetadata(FeatureType const & ft, Result::Metadata & meta)
bool const isSponsoredHotel = ftypes::IsBookingChecker::Instance()(ft);
meta.m_isSponsoredHotel = isSponsoredHotel;
+ meta.m_isHotel = ftypes::IsHotelChecker::Instance()(ft);
if (isSponsoredHotel)
{
@@ -114,7 +116,7 @@ PreResult2::PreResult2(FeatureType const & f, PreResult1 const * p, m2::PointD c
m_region.SetParams(fileName, center);
m_distance = PointDistance(center, pivot);
- ProcessMetadata(f, m_metadata);
+ ProcessMetadata(f, m_types, m_metadata);
}
PreResult2::PreResult2(double lat, double lon)
diff --git a/search/result.hpp b/search/result.hpp
index b3497bf0c5..e3912d9f31 100644
--- a/search/result.hpp
+++ b/search/result.hpp
@@ -31,13 +31,18 @@ public:
/// Metadata for search results. Considered valid if m_resultType == RESULT_FEATURE.
struct Metadata
{
- string m_cuisine; // Valid only if not empty. Used for restaurants.
- int m_stars = 0; // Valid only if not 0. Used for hotels.
- bool m_isSponsoredHotel = false; // Used for hotels.
- osm::YesNoUnknown m_isOpenNow = osm::Unknown; // Valid for any result.
+ string m_cuisine; // Valid only if not empty. Used for restaurants.
+
+ // Following fields are used for hotels only.
string m_hotelApproximatePricing;
string m_hotelRating;
- /// True if the struct is already assigned or need to be calculated otherwise.
+ int m_stars = 0;
+ bool m_isSponsoredHotel = false;
+ bool m_isHotel = false;
+
+ osm::YesNoUnknown m_isOpenNow = osm::Unknown; // Valid for any result.
+
+ // True if the struct is already assigned or need to be calculated otherwise.
bool m_isInitialized = false;
};
@@ -171,6 +176,9 @@ public:
inline IterT Begin() const { return m_vec.begin(); }
inline IterT End() const { return m_vec.end(); }
+ inline IterT begin() const { return m_vec.begin(); }
+ inline IterT end() const { return m_vec.end(); }
+
inline size_t GetCount() const { return m_vec.size(); }
size_t GetSuggestsCount() const;
diff --git a/search/search.pro b/search/search.pro
index b3612baf6e..3f50c1dadf 100644
--- a/search/search.pro
+++ b/search/search.pro
@@ -25,9 +25,11 @@ HEADERS += \
geocoder_context.hpp \
geometry_cache.hpp \
geometry_utils.hpp \
+ hotels_classifier.hpp \
house_detector.hpp \
house_numbers_matcher.hpp \
house_to_street_table.hpp \
+ interactive_search_callback.hpp \
intermediate_result.hpp \
intersection_result.hpp \
interval_set.hpp \
@@ -82,9 +84,11 @@ SOURCES += \
geocoder_context.cpp \
geometry_cache.cpp \
geometry_utils.cpp \
+ hotels_classifier.cpp \
house_detector.cpp \
house_numbers_matcher.cpp \
house_to_street_table.cpp \
+ interactive_search_callback.cpp \
intermediate_result.cpp \
intersection_result.cpp \
keyword_lang_matcher.cpp \
diff --git a/search/search_integration_tests/interactive_search_test.cpp b/search/search_integration_tests/interactive_search_test.cpp
new file mode 100644
index 0000000000..d7b28ac138
--- /dev/null
+++ b/search/search_integration_tests/interactive_search_test.cpp
@@ -0,0 +1,110 @@
+#include "testing/testing.hpp"
+
+#include "generator/generator_tests_support/test_feature.hpp"
+
+#include "search/interactive_search_callback.hpp"
+#include "search/mode.hpp"
+#include "search/search_integration_tests/helpers.hpp"
+#include "search/search_tests_support/test_results_matching.hpp"
+#include "search/search_tests_support/test_search_request.hpp"
+
+#include "base/macros.hpp"
+
+using namespace generator::tests_support;
+using namespace search::tests_support;
+
+using TRules = vector<shared_ptr<MatchingRule>>;
+
+namespace search
+{
+namespace
+{
+class TestCafe : public TestPOI
+{
+public:
+ TestCafe(m2::PointD const & center) : TestPOI(center, "cafe", "en")
+ {
+ SetTypes({{"amenity", "cafe"}});
+ }
+};
+
+class TestHotel : public TestPOI
+{
+public:
+ TestHotel(m2::PointD const & center) : TestPOI(center, "hotel", "en")
+ {
+ SetTypes({{"tourism", "hotel"}});
+ }
+};
+
+class InteractiveSearchRequest : public TestSearchRequest
+{
+public:
+ InteractiveSearchRequest(TestSearchEngine & engine, string const & query,
+ m2::RectD const & viewport, bool & mode)
+ : TestSearchRequest(
+ engine, query, "en" /* locale */, Mode::Viewport, viewport,
+ bind(&InteractiveSearchRequest::OnStarted, this),
+ InteractiveSearchCallback([&mode]() { mode = true; },
+ bind(&InteractiveSearchRequest::OnResults, this, _1)))
+ {
+ }
+};
+
+class InteractiveSearchTest : public SearchTest
+{
+};
+
+double const kDX[] = {-0.01, 0, 0, 0.01};
+double const kDY[] = {0, -0.01, 0.01, 0};
+
+static_assert(ARRAY_SIZE(kDX) == ARRAY_SIZE(kDY), "Wrong deltas lists");
+
+UNIT_CLASS_TEST(InteractiveSearchTest, Smoke)
+{
+ m2::PointD const cafesPivot(-1, -1);
+ m2::PointD const hotelsPivot(1, 1);
+
+ vector<TestCafe> cafes;
+ for (size_t i = 0; i < ARRAY_SIZE(kDX); ++i)
+ cafes.emplace_back(m2::Shift(cafesPivot, kDX[i], kDY[i]));
+
+ vector<TestHotel> hotels;
+ for (size_t i = 0; i < ARRAY_SIZE(kDX); ++i)
+ hotels.emplace_back(m2::Shift(hotelsPivot, kDX[i], kDY[i]));
+
+ auto const id = BuildCountry("Wonderland", [&](TestMwmBuilder & builder) {
+ for (auto const & cafe : cafes)
+ builder.Add(cafe);
+ for (auto const & hotel : hotels)
+ builder.Add(hotel);
+ });
+
+ {
+ bool mode = false;
+ InteractiveSearchRequest request(
+ m_engine, "cafe", m2::RectD(m2::PointD(-1.5, -1.5), m2::PointD(-0.5, -0.5)), mode);
+ request.Wait();
+
+ TRules const rules = {ExactMatch(id, cafes[0]), ExactMatch(id, cafes[1]),
+ ExactMatch(id, cafes[2]), ExactMatch(id, cafes[3])};
+
+ TEST(!mode, ());
+ TEST(MatchResults(m_engine, rules, request.Results()), ());
+ }
+
+ {
+ bool mode = false;
+ InteractiveSearchRequest request(m_engine, "hotel",
+ m2::RectD(m2::PointD(0.5, 0.5), m2::PointD(1.5, 1.5)), mode);
+ request.Wait();
+
+ TRules const rules = {ExactMatch(id, hotels[0]), ExactMatch(id, hotels[1]),
+ ExactMatch(id, hotels[2]), ExactMatch(id, hotels[3])};
+
+ TEST(mode, ());
+ TEST(MatchResults(m_engine, rules, request.Results()), ());
+ }
+}
+} // namespace
+} // namespace search
diff --git a/search/search_integration_tests/search_integration_tests.pro b/search/search_integration_tests/search_integration_tests.pro
index c764ff9498..b05b0a5645 100644
--- a/search/search_integration_tests/search_integration_tests.pro
+++ b/search/search_integration_tests/search_integration_tests.pro
@@ -21,6 +21,7 @@ SOURCES += \
../../testing/testingmain.cpp \
generate_tests.cpp \
helpers.cpp \
+ interactive_search_test.cpp \
pre_ranker_test.cpp \
processor_test.cpp \
smoke_test.cpp \
diff --git a/search/search_tests_support/test_search_request.cpp b/search/search_tests_support/test_search_request.cpp
index 58bb686c77..1c7f50d3ac 100644
--- a/search/search_tests_support/test_search_request.cpp
+++ b/search/search_tests_support/test_search_request.cpp
@@ -29,6 +29,19 @@ TestSearchRequest::TestSearchRequest(TestSearchEngine & engine, SearchParams par
engine.Search(params, viewport);
}
+TestSearchRequest::TestSearchRequest(TestSearchEngine & engine, string const & query,
+ string const & locale, Mode mode, m2::RectD const & viewport,
+ TOnStarted onStarted, TOnResults onResults)
+{
+ SearchParams params;
+ params.m_query = query;
+ params.m_inputLocale = locale;
+ params.SetMode(mode);
+ params.m_onStarted = move(onStarted);
+ params.m_onResults = move(onResults);
+ engine.Search(params, viewport);
+}
+
void TestSearchRequest::Wait()
{
unique_lock<mutex> lock(m_mu);
diff --git a/search/search_tests_support/test_search_request.hpp b/search/search_tests_support/test_search_request.hpp
index 35c1031ebb..4b86d5a3e2 100644
--- a/search/search_tests_support/test_search_request.hpp
+++ b/search/search_tests_support/test_search_request.hpp
@@ -34,8 +34,13 @@ public:
steady_clock::duration ResponseTime() const;
vector<search::Result> const & Results() const;
-private:
+protected:
+ TestSearchRequest(TestSearchEngine & engine, string const & query, string const & locale,
+ Mode mode, m2::RectD const & viewport, TOnStarted onStarted,
+ TOnResults onResults);
+
void SetUpCallbacks(SearchParams & params);
+
void OnStarted();
void OnResults(search::Results const & results);