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,20 @@
project(generator_tests_support)
set(SRC
routing_helpers.cpp
routing_helpers.hpp
test_feature.cpp
test_feature.hpp
test_generator.cpp
test_generator.hpp
test_mwm_builder.cpp
test_mwm_builder.hpp
test_with_classificator.cpp
test_with_classificator.hpp
test_with_custom_mwms.cpp
test_with_custom_mwms.hpp
)
omim_add_library(${PROJECT_NAME} ${SRC})
target_link_libraries(${PROJECT_NAME} generator)

View file

@ -0,0 +1,105 @@
#include "generator/generator_tests_support/routing_helpers.hpp"
#include "testing/testing.hpp"
#include "generator/gen_mwm_info.hpp"
#include "routing/traffic_stash.hpp"
#include "routing_common/car_model.hpp"
#include "coding/file_writer.hpp"
#include "base/assert.hpp"
#include "base/geo_object_id.hpp"
#include "base/string_utils.hpp"
#include <utility>
namespace generator
{
void ReEncodeOsmIdsToFeatureIdsMapping(std::string const & mappingContent, std::string const & outputFilePath)
{
strings::SimpleTokenizer lineIter(mappingContent, "\n\r" /* line delimiters */);
std::vector<std::pair<base::GeoObjectId, uint32_t>> osmIdsToFeatureIds;
for (; lineIter; ++lineIter)
{
auto const & line = *lineIter;
strings::SimpleTokenizer idIter(line, ", \t" /* id delimiters */);
uint64_t osmId = 0;
TEST(idIter, ());
TEST(strings::to_uint(*idIter, osmId), ("Cannot convert to uint64_t:", *idIter));
TEST(idIter, ("Wrong feature ids to osm ids mapping."));
++idIter;
uint32_t featureId = 0;
TEST(idIter, ());
TEST(strings::to_uint(*idIter, featureId), ("Cannot convert to uint:", *idIter));
osmIdsToFeatureIds.emplace_back(base::MakeOsmWay(osmId), featureId);
++idIter;
TEST(!idIter, ());
}
FileWriter osm2ftWriter(outputFilePath);
rw::WriteVectorOfPOD(osm2ftWriter, osmIdsToFeatureIds);
}
} // namespace generator
namespace routing
{
void TestGeometryLoader::Load(uint32_t featureId, RoadGeometry & road)
{
auto const it = m_roads.find(featureId);
if (it == m_roads.cend())
return;
road = it->second;
}
void TestGeometryLoader::AddRoad(uint32_t featureId, bool oneWay, float speed, RoadGeometry::Points const & points)
{
auto const it = m_roads.find(featureId);
CHECK(it == m_roads.end(), ("Already contains feature", featureId));
m_roads[featureId] = RoadGeometry(oneWay, speed, speed, points);
m_roads[featureId].SetPassThroughAllowedForTests(true);
}
void TestGeometryLoader::SetPassThroughAllowed(uint32_t featureId, bool passThroughAllowed)
{
auto const it = m_roads.find(featureId);
CHECK(it != m_roads.end(), ("No feature", featureId));
m_roads[featureId].SetPassThroughAllowedForTests(passThroughAllowed);
}
std::shared_ptr<EdgeEstimator> CreateEstimatorForCar(std::shared_ptr<TrafficStash> trafficStash)
{
auto const carModel = CarModelFactory({}).GetVehicleModel();
return EdgeEstimator::Create(VehicleType::Car, *carModel, trafficStash, nullptr /* DataSource */,
nullptr /* NumMwmIds */);
}
std::shared_ptr<EdgeEstimator> CreateEstimatorForCar(traffic::TrafficCache const & trafficCache)
{
auto numMwmIds = std::make_shared<NumMwmIds>();
auto stash = std::make_shared<TrafficStash>(trafficCache, numMwmIds);
return CreateEstimatorForCar(stash);
}
Joint MakeJoint(std::vector<RoadPoint> const & points)
{
Joint joint;
for (auto const & point : points)
joint.AddPoint(point);
return joint;
}
std::unique_ptr<IndexGraph> BuildIndexGraph(std::unique_ptr<TestGeometryLoader> geometryLoader,
std::shared_ptr<EdgeEstimator> estimator, std::vector<Joint> const & joints)
{
auto graph = std::make_unique<IndexGraph>(std::make_shared<Geometry>(std::move(geometryLoader)), estimator);
graph->Import(joints);
return graph;
}
} // namespace routing

View file

@ -0,0 +1,56 @@
#pragma once
#include "routing/edge_estimator.hpp"
#include "routing/geometry.hpp"
#include "routing/index_graph.hpp"
#include "routing/joint.hpp"
#include <cstdint>
#include <memory>
#include <string>
#include <unordered_map>
#include <vector>
namespace generator
{
/// \brief Generates a binary file by |mappingContent| with mapping from osm ids to feature ids.
/// \param mappingContent a string with lines with mapping from osm id to feature id (one to one).
/// For example
/// 10, 1,
/// 20, 2
/// 30, 3,
/// 40, 4
/// \param outputFilePath full path to an output file where the mapping is saved.
void ReEncodeOsmIdsToFeatureIdsMapping(std::string const & mappingContent, std::string const & outputFilePath);
} // namespace generator
namespace traffic
{
class TrafficCache;
}
namespace routing
{
class TestGeometryLoader : public GeometryLoader
{
public:
// GeometryLoader overrides:
void Load(uint32_t featureId, routing::RoadGeometry & road) override;
void AddRoad(uint32_t featureId, bool oneWay, float speed, routing::RoadGeometry::Points const & points);
void SetPassThroughAllowed(uint32_t featureId, bool passThroughAllowed);
private:
std::unordered_map<uint32_t, RoadGeometry> m_roads;
};
std::shared_ptr<EdgeEstimator> CreateEstimatorForCar(traffic::TrafficCache const & trafficCache);
std::shared_ptr<EdgeEstimator> CreateEstimatorForCar(std::shared_ptr<TrafficStash> trafficStash);
Joint MakeJoint(std::vector<routing::RoadPoint> const & points);
std::unique_ptr<IndexGraph> BuildIndexGraph(std::unique_ptr<TestGeometryLoader> geometryLoader,
std::shared_ptr<EdgeEstimator> estimator,
std::vector<Joint> const & joints);
} // namespace routing

View file

@ -0,0 +1,484 @@
#include "generator/generator_tests_support/test_feature.hpp"
#include "generator/feature_builder.hpp"
#include "editor/osm_editor.hpp"
#include "indexer/classificator.hpp"
#include "indexer/editable_map_object.hpp"
#include "indexer/feature.hpp"
#include "indexer/feature_decl.hpp"
#include "indexer/feature_meta.hpp"
#include "indexer/mwm_set.hpp"
#include "coding/string_utf8_multilang.hpp"
#include "base/assert.hpp"
#include "base/stl_helpers.hpp"
#include "base/string_utils.hpp"
#include <atomic>
#include <sstream>
namespace generator
{
namespace tests_support
{
using namespace feature;
using std::ostringstream, std::string, std::string_view, std::vector;
namespace
{
uint64_t GenUniqueId()
{
static std::atomic<uint64_t> id;
return id.fetch_add(1);
}
vector<m2::PointD> MakePoly(m2::RectD const & rect)
{
return {rect.LeftBottom(), rect.RightBottom(), rect.RightTop(), rect.LeftTop(), rect.LeftBottom()};
}
StringUtf8Multilang MakeName(string const & name, string const & lang)
{
StringUtf8Multilang res;
res.AddString(lang, name);
// Names used for search depend on locale. Fill default name because we need to run tests with
// different locales. If you do not need default name to be filled use
// TestFeature::TestFeature(StringUtf8Multilang const & name).
res.AddString("default", name);
return res;
}
} // namespace
// TestFeature -------------------------------------------------------------------------------------
TestFeature::TestFeature() : m_id(GenUniqueId()), m_center(0, 0), m_type(Type::Unknown)
{
Init();
}
TestFeature::TestFeature(StringUtf8Multilang name)
: m_id(GenUniqueId())
, m_center(0, 0)
, m_type(Type::Unknown)
, m_names(std::move(name))
{
Init();
}
TestFeature::TestFeature(m2::PointD const & center, StringUtf8Multilang name)
: m_id(GenUniqueId())
, m_center(center)
, m_type(Type::Point)
, m_names(std::move(name))
{
Init();
}
TestFeature::TestFeature(m2::RectD const & boundary, StringUtf8Multilang name)
: TestFeature(MakePoly(boundary), std::move(name), Type::Area)
{
Init();
}
TestFeature::TestFeature(vector<m2::PointD> geometry, StringUtf8Multilang name, Type type)
: m_id(GenUniqueId())
, m_center(0, 0)
, m_geometry(std::move(geometry))
, m_type(type)
, m_names(std::move(name))
{
ASSERT(!m_geometry.empty(), ());
Init();
}
void TestFeature::Init()
{
m_metadata.Set(Metadata::FMD_TEST_ID, strings::to_string(m_id));
}
bool TestFeature::Matches(FeatureType & feature) const
{
auto const sv = feature.GetMetadata(feature::Metadata::FMD_TEST_ID);
if (sv.empty())
return false;
uint64_t id;
CHECK(strings::to_uint(sv, id), (sv));
return id == m_id;
}
void TestFeature::Serialize(FeatureBuilder & fb) const
{
using feature::Metadata;
// Iterate [1, FMD_COUNT). Absent types don't matter here.
size_t i = 1;
auto constexpr count = static_cast<size_t>(Metadata::EType::FMD_COUNT);
for (; i < count; ++i)
{
auto const type = static_cast<Metadata::EType>(i);
if (m_metadata.Has(type))
fb.GetMetadata().Set(type, string(m_metadata.Get(type)));
}
if (!m_geometry.empty())
fb.AssignPoints(m_geometry);
switch (m_type)
{
case Type::Point:
{
fb.SetCenter(m_center);
break;
}
case Type::Line:
{
fb.SetLinear();
break;
}
case Type::Area:
{
fb.SetArea();
break;
}
case Type::Unknown: break;
}
m_names.ForEach([&](int8_t langCode, string_view name)
{
if (!name.empty())
fb.SetName(langCode, name);
});
if (!m_postcode.empty())
fb.GetParams().SetPostcode(m_postcode);
}
// TestPlace -------------------------------------------------------------------------------------
TestPlace::TestPlace(m2::PointD const & center, string const & name, string const & lang, uint32_t type,
uint8_t rank /* = 0 */)
: TestFeature(center, MakeName(name, lang))
, m_type(type)
, m_rank(rank)
{}
TestPlace::TestPlace(m2::PointD const & center, StringUtf8Multilang const & name, uint32_t type, uint8_t rank)
: TestFeature(center, name)
, m_type(type)
, m_rank(rank)
{}
TestPlace::TestPlace(std::vector<m2::PointD> const & boundary, std::string const & name, std::string const & lang,
uint32_t type, uint8_t rank)
: TestFeature(boundary, MakeName(name, lang), Type::Area)
, m_type(type)
, m_rank(rank)
{}
void TestPlace::Serialize(FeatureBuilder & fb) const
{
TestFeature::Serialize(fb);
fb.AddType(m_type);
fb.GetParams().rank = m_rank;
}
string TestPlace::ToDebugString() const
{
ostringstream os;
os << "TestPlace { " << DebugPrint(m_names) << ", " << DebugPrint(m_center) << ", "
<< classif().GetReadableObjectName(m_type) << ", " << int(m_rank) << " }";
return os.str();
}
void TestPlace::SetType(base::StringIL const & e)
{
m_type = classif().GetTypeByPath(e);
}
TestCountry::TestCountry(m2::PointD const & center, std::string const & name, std::string const & lang)
: TestPlace(center, name, lang, classif().GetTypeByPath({"place", "country"}), 170 /* rank */)
{
// Default rank for Country with population ~ 1.0E7
}
TestState::TestState(m2::PointD const & center, string const & name, string const & lang)
: TestPlace(center, name, lang, classif().GetTypeByPath({"place", "state"}))
{}
TestSea::TestSea(m2::PointD const & center, std::string const & name, std::string const & lang)
: TestPlace(center, name, lang, classif().GetTypeByPath({"place", "sea"}))
{}
uint32_t TestCity::GetCityType()
{
return classif().GetTypeByPath({"place", "city"});
}
TestCity::TestCity(m2::PointD const & center, string const & name, string const & lang, uint8_t rank)
: TestPlace(center, name, lang, GetCityType(), rank)
{}
TestCity::TestCity(m2::PointD const & center, StringUtf8Multilang const & name, uint8_t rank)
: TestPlace(center, name, GetCityType(), rank)
{}
TestCity::TestCity(vector<m2::PointD> const & boundary, string const & name, string const & lang, uint8_t rank)
: TestPlace(boundary, name, lang, GetCityType(), rank)
{}
TestVillage::TestVillage(m2::PointD const & center, string const & name, string const & lang, uint8_t rank)
: TestPlace(center, name, lang, classif().GetTypeByPath({"place", "village"}), rank)
{}
TestSuburb::TestSuburb(m2::PointD const & center, string const & name, string const & lang)
: TestPlace(center, name, lang, classif().GetTypeByPath({"place", "suburb"}))
{}
// TestStreet --------------------------------------------------------------------------------------
TestStreet::TestStreet(vector<m2::PointD> const & points, string const & name, string const & lang)
: TestFeature(points, MakeName(name, lang), Type::Line)
{
SetType({"highway", "living_street"});
}
TestStreet::TestStreet(vector<m2::PointD> const & points, StringUtf8Multilang const & name)
: TestFeature(points, name, Type::Line)
{
SetType({"highway", "living_street"});
}
void TestStreet::SetType(base::StringIL const & e)
{
m_highwayType = classif().GetTypeByPath(e);
}
void TestStreet::Serialize(FeatureBuilder & fb) const
{
TestFeature::Serialize(fb);
fb.SetType(m_highwayType);
fb.GetParams().ref = m_roadNumber;
}
string TestStreet::ToDebugString() const
{
ostringstream os;
os << "TestStreet [" << DebugPrint(m_names) << ", " << ::DebugPrint(m_geometry) << "]";
return os.str();
}
// TestSquare --------------------------------------------------------------------------------------
TestSquare::TestSquare(m2::RectD const & rect, string const & name, string const & lang)
: TestFeature(rect, MakeName(name, lang))
{}
void TestSquare::Serialize(FeatureBuilder & fb) const
{
TestFeature::Serialize(fb);
auto const & classificator = classif();
fb.SetType(classificator.GetTypeByPath({"place", "square"}));
}
string TestSquare::ToDebugString() const
{
ostringstream os;
os << "TestSquare [" << DebugPrint(m_names) << "]";
return os.str();
}
// TestPOI -----------------------------------------------------------------------------------------
TestPOI::TestPOI(m2::PointD const & center, string const & name, string const & lang)
: TestFeature(center, MakeName(name, lang))
{
SetTypes({{"railway", "station"}});
}
// static
std::pair<TestPOI, FeatureID> TestPOI::AddWithEditor(osm::Editor & editor, MwmSet::MwmId const & mwmId,
string const & enName, m2::PointD const & pt)
{
TestPOI poi(pt, enName, "en");
osm::EditableMapObject emo;
editor.CreatePoint(classif().GetTypeByPath({"shop", "bakery"}), pt, mwmId, emo);
StringUtf8Multilang names;
names.AddString(StringUtf8Multilang::GetLangIndex("en"), enName);
emo.SetName(names);
emo.SetTestId(poi.GetId());
editor.SaveEditedFeature(emo);
return {poi, emo.GetID()};
}
void TestPOI::Serialize(FeatureBuilder & fb) const
{
TestFeature::Serialize(fb);
for (uint32_t t : m_types)
fb.AddType(t);
auto & params = fb.GetParams();
if (!m_houseNumber.empty())
params.AddHouseNumber(m_houseNumber);
if (!m_streetName.empty())
params.SetStreet(m_streetName);
}
string TestPOI::ToDebugString() const
{
ostringstream os;
os << "TestPOI [" << DebugPrint(m_names) << ", " << DebugPrint(m_center);
if (!m_houseNumber.empty())
os << ", " << m_houseNumber;
if (!m_streetName.empty())
os << ", " << m_streetName;
os << "]";
return os.str();
}
TypesHolder TestPOI::GetTypes() const
{
TypesHolder types;
types.Assign(m_types.begin(), m_types.end());
return types;
}
void TestPOI::SetTypes(std::initializer_list<base::StringIL> const & types)
{
m_types.clear();
auto const & c = classif();
for (auto const & e : types)
m_types.push_back(c.GetTypeByPath(e));
}
// TestMultilingualPOI -----------------------------------------------------------------------------
TestMultilingualPOI::TestMultilingualPOI(m2::PointD const & center, string const & defaultName,
std::map<string, string> const & multilingualNames)
: TestPOI(center, defaultName, "default")
, m_multilingualNames(multilingualNames)
{}
void TestMultilingualPOI::Serialize(FeatureBuilder & fb) const
{
TestPOI::Serialize(fb);
for (auto const & kv : m_multilingualNames)
CHECK(fb.AddName(kv.first, kv.second), ("Can't set feature name:", kv.second, "(", kv.first, ")"));
}
string TestMultilingualPOI::ToDebugString() const
{
ostringstream os;
os << "TestPOI [" << DebugPrint(m_names) << ", ";
for (auto const & kv : m_multilingualNames)
os << "( " << kv.second << ", " << kv.first << "), ";
os << DebugPrint(m_center);
if (!m_houseNumber.empty())
os << ", " << m_houseNumber;
if (!m_streetName.empty())
os << ", " << m_streetName;
os << "]";
return os.str();
}
// TestBuilding ------------------------------------------------------------------------------------
TestBuilding::TestBuilding(m2::PointD const & center, string const & name, string const & houseNumber,
string const & lang)
: TestFeature(center, MakeName(name, lang))
, m_houseNumber(houseNumber)
{}
TestBuilding::TestBuilding(m2::PointD const & center, string const & name, string const & houseNumber,
string_view street, string const & lang)
: TestFeature(center, MakeName(name, lang))
, m_houseNumber(houseNumber)
{
m_addr.Set(AddressData::Type::Street, street);
}
TestBuilding::TestBuilding(m2::RectD const & boundary, string const & name, string const & houseNumber,
string_view street, string const & lang)
: TestFeature(boundary, MakeName(name, lang))
, m_houseNumber(houseNumber)
{
m_addr.Set(AddressData::Type::Street, street);
}
void TestBuilding::Serialize(FeatureBuilder & fb) const
{
TestFeature::Serialize(fb);
auto & params = fb.GetParams();
if (!m_houseNumber.empty())
params.AddHouseNumber(m_houseNumber);
params.SetAddress(AddressData(m_addr));
if (m_type == 0)
fb.AddType(classif().GetTypeByPath({"building"}));
else
fb.AddType(m_type);
}
string TestBuilding::ToDebugString() const
{
ostringstream os;
os << "TestBuilding [" << DebugPrint(m_names) << ", " << m_houseNumber << ", " << DebugPrint(m_center) << "]";
return os.str();
}
// TestPark ----------------------------------------------------------------------------------------
TestPark::TestPark(vector<m2::PointD> const & boundary, string const & name, string const & lang)
: TestFeature(boundary, MakeName(name, lang), Type::Area)
{}
void TestPark::Serialize(FeatureBuilder & fb) const
{
TestFeature::Serialize(fb);
auto const & classificator = classif();
fb.AddType(classificator.GetTypeByPath({"leisure", "park"}));
}
string TestPark::ToDebugString() const
{
ostringstream os;
os << "TestPark [" << DebugPrint(m_names) << ", "
<< "]";
return os.str();
}
// TestRoad ----------------------------------------------------------------------------------------
TestRoad::TestRoad(vector<m2::PointD> const & points, string const & name, string const & lang)
: TestFeature(points, MakeName(name, lang), Type::Line)
{}
void TestRoad::Serialize(FeatureBuilder & fb) const
{
TestFeature::Serialize(fb);
auto const & classificator = classif();
fb.AddType(classificator.GetTypeByPath({"highway", "road"}));
}
string TestRoad::ToDebugString() const
{
ostringstream os;
os << "TestRoad [" << DebugPrint(m_names) << "]";
return os.str();
}
// Functions ---------------------------------------------------------------------------------------
string DebugPrint(TestFeature const & feature)
{
return feature.ToDebugString();
}
} // namespace tests_support
} // namespace generator

View file

@ -0,0 +1,254 @@
#pragma once
#include "indexer/feature_data.hpp"
#include "indexer/feature_decl.hpp"
#include "indexer/feature_meta.hpp"
#include "indexer/mwm_set.hpp"
#include "coding/string_utf8_multilang.hpp"
#include "geometry/point2d.hpp"
#include "geometry/rect2d.hpp"
#include <map>
#include <string>
#include <vector>
class FeatureType;
namespace feature
{
class FeatureBuilder;
}
namespace osm
{
class Editor;
}
namespace generator
{
namespace tests_support
{
class TestFeature
{
public:
virtual ~TestFeature() = default;
bool Matches(FeatureType & feature) const;
void SetPostcode(std::string const & postcode) { m_postcode = postcode; }
uint64_t GetId() const { return m_id; }
std::string_view GetName(std::string_view lang) const
{
std::string_view res;
if (m_names.GetString(lang, res))
return res;
return {};
}
m2::PointD const & GetCenter() const { return m_center; }
feature::Metadata & GetMetadata() { return m_metadata; }
virtual void Serialize(feature::FeatureBuilder & fb) const;
virtual std::string ToDebugString() const = 0;
protected:
enum class Type
{
Point,
Line,
Area,
Unknown
};
TestFeature();
explicit TestFeature(StringUtf8Multilang name);
TestFeature(m2::PointD const & center, StringUtf8Multilang name);
TestFeature(m2::RectD const & boundary, StringUtf8Multilang name);
TestFeature(std::vector<m2::PointD> geometry, StringUtf8Multilang name, Type type);
uint64_t const m_id;
m2::PointD const m_center;
std::vector<m2::PointD> const m_geometry;
Type const m_type;
StringUtf8Multilang m_names;
std::string m_postcode;
feature::Metadata m_metadata;
private:
void Init();
};
class TestPlace : public TestFeature
{
public:
TestPlace(m2::PointD const & center, std::string const & name, std::string const & lang, uint32_t type,
uint8_t rank = 0);
TestPlace(m2::PointD const & center, StringUtf8Multilang const & name, uint32_t type, uint8_t rank);
TestPlace(std::vector<m2::PointD> const & boundary, std::string const & name, std::string const & lang, uint32_t type,
uint8_t rank);
// TestFeature overrides:
void Serialize(feature::FeatureBuilder & fb) const override;
std::string ToDebugString() const override;
void SetType(base::StringIL const & e);
protected:
uint32_t m_type;
uint8_t const m_rank;
};
class TestCountry : public TestPlace
{
public:
TestCountry(m2::PointD const & center, std::string const & name, std::string const & lang);
};
class TestState : public TestPlace
{
public:
TestState(m2::PointD const & center, std::string const & name, std::string const & lang);
};
// A feature that is big enough for World.mwm but is not a locality.
class TestSea : public TestPlace
{
public:
TestSea(m2::PointD const & center, std::string const & name, std::string const & lang);
};
class TestCity : public TestPlace
{
static uint32_t GetCityType();
public:
TestCity(m2::PointD const & center, std::string const & name, std::string const & lang, uint8_t rank);
TestCity(m2::PointD const & center, StringUtf8Multilang const & name, uint8_t rank);
TestCity(std::vector<m2::PointD> const & boundary, std::string const & name, std::string const & lang, uint8_t rank);
};
class TestVillage : public TestPlace
{
public:
TestVillage(m2::PointD const & center, std::string const & name, std::string const & lang, uint8_t rank);
};
class TestSuburb : public TestPlace
{
public:
TestSuburb(m2::PointD const & center, std::string const & name, std::string const & lang);
};
class TestStreet : public TestFeature
{
public:
TestStreet(std::vector<m2::PointD> const & points, std::string const & name, std::string const & lang);
TestStreet(std::vector<m2::PointD> const & points, StringUtf8Multilang const & name);
void SetType(base::StringIL const & e);
void SetRoadNumber(std::string const & roadNumber) { m_roadNumber = roadNumber; }
// TestFeature overrides:
void Serialize(feature::FeatureBuilder & fb) const override;
std::string ToDebugString() const override;
private:
uint32_t m_highwayType;
std::string m_roadNumber;
};
class TestSquare : public TestFeature
{
public:
TestSquare(m2::RectD const & rect, std::string const & name, std::string const & lang);
// TestFeature overrides:
void Serialize(feature::FeatureBuilder & fb) const override;
std::string ToDebugString() const override;
};
class TestPOI : public TestFeature
{
public:
TestPOI(m2::PointD const & center, std::string const & name, std::string const & lang);
static std::pair<TestPOI, FeatureID> AddWithEditor(osm::Editor & editor, MwmSet::MwmId const & mwmId,
std::string const & enName, m2::PointD const & pt);
// TestFeature overrides:
void Serialize(feature::FeatureBuilder & fb) const override;
std::string ToDebugString() const override;
feature::TypesHolder GetTypes() const;
void SetHouseNumber(std::string const & houseNumber) { m_houseNumber = houseNumber; }
void SetStreetName(std::string_view name) { m_streetName = name; }
void SetTypes(std::initializer_list<uint32_t> const & types) { m_types.assign(types); }
void SetTypes(std::initializer_list<base::StringIL> const & types);
protected:
std::string m_houseNumber;
std::string m_streetName;
std::vector<uint32_t> m_types;
};
class TestMultilingualPOI : public TestPOI
{
public:
TestMultilingualPOI(m2::PointD const & center, std::string const & defaultName,
std::map<std::string, std::string> const & multilingualNames);
// TestFeature overrides:
void Serialize(feature::FeatureBuilder & fb) const override;
std::string ToDebugString() const override;
private:
std::map<std::string, std::string> m_multilingualNames;
};
class TestBuilding : public TestFeature
{
public:
TestBuilding(m2::PointD const & center, std::string const & name, std::string const & houseNumber,
std::string const & lang);
TestBuilding(m2::PointD const & center, std::string const & name, std::string const & houseNumber,
std::string_view street, std::string const & lang);
TestBuilding(m2::RectD const & boundary, std::string const & name, std::string const & houseNumber,
std::string_view street, std::string const & lang);
// TestFeature overrides:
void Serialize(feature::FeatureBuilder & fb) const override;
std::string ToDebugString() const override;
void SetType(uint32_t type) { m_type = type; }
void SetPlace(std::string_view place) { m_addr.Set(feature::AddressData::Type::Place, place); }
private:
std::string m_houseNumber;
feature::AddressData m_addr;
uint32_t m_type = 0;
};
class TestPark : public TestFeature
{
public:
TestPark(std::vector<m2::PointD> const & boundary, std::string const & name, std::string const & lang);
// TestFeature overrides:
void Serialize(feature::FeatureBuilder & fb) const override;
std::string ToDebugString() const override;
};
class TestRoad : public TestFeature
{
public:
TestRoad(std::vector<m2::PointD> const & points, std::string const & name, std::string const & lang);
// TestFeature overrides:
void Serialize(feature::FeatureBuilder & fb) const override;
std::string ToDebugString() const override;
};
std::string DebugPrint(TestFeature const & feature);
} // namespace tests_support
} // namespace generator

View file

@ -0,0 +1,160 @@
#include "test_generator.hpp"
#include "generator/borders.hpp"
#include "generator/camera_info_collector.hpp"
#include "generator/feature_sorter.hpp"
#include "generator/osm_source.hpp"
#include "generator/raw_generator.hpp"
#include "generator/maxspeeds_builder.hpp"
#include "generator/restriction_generator.hpp"
#include "generator/road_access_generator.hpp"
#include "generator/routing_index_generator.hpp"
#include "generator/search_index_builder.hpp"
#include "indexer/classificator_loader.hpp"
#include "indexer/features_offsets_table.hpp"
#include "indexer/index_builder.hpp"
#include "indexer/map_style_reader.hpp"
#include "platform/platform.hpp"
namespace generator
{
namespace tests_support
{
char const * TestRawGenerator::kWikidataFilename = "wiki_urls.csv";
bool MakeFakeBordersFile(std::string const & intemediatePath, std::string const & filename)
{
auto const borderPath = base::JoinPath(intemediatePath, BORDERS_DIR);
auto & platform = GetPlatform();
auto const code = platform.MkDir(borderPath);
if (code != Platform::EError::ERR_OK && code != Platform::EError::ERR_FILE_ALREADY_EXISTS)
return false;
std::vector<m2::PointD> points = {{-180.0, -90.0}, {180.0, -90.0}, {180.0, 90.0}, {-180.0, 90.0}, {-180.0, -90.0}};
borders::DumpBorderToPolyFile(borderPath, filename, {m2::RegionD{std::move(points)}});
return true;
}
TestRawGenerator::TestRawGenerator()
{
GetStyleReader().SetCurrentStyle(MapStyleMerged);
classificator::Load();
SetupTmpFolder("./raw_generator");
}
TestRawGenerator::~TestRawGenerator()
{
UNUSED_VALUE(Platform::RmDirRecursively(GetTmpPath()));
}
void TestRawGenerator::SetupTmpFolder(std::string const & tmpPath)
{
m_genInfo.m_cacheDir = m_genInfo.m_intermediateDir = tmpPath;
UNUSED_VALUE(Platform::RmDirRecursively(tmpPath));
CHECK(Platform::MkDirChecked(tmpPath), ());
}
void TestRawGenerator::BuildFB(std::string const & osmFilePath, std::string const & mwmName,
bool makeWorld /* = false */)
{
m_genInfo.m_nodeStorageType = feature::GenerateInfo::NodeStorageType::Index;
m_genInfo.m_osmFileName = osmFilePath;
m_genInfo.m_osmFileType = feature::GenerateInfo::OsmSourceType::XML;
CHECK(GenerateIntermediateData(m_genInfo), ());
// CHECK(MakeFakeBordersFile(GetTmpPath(), mwmName), ());
m_genInfo.m_tmpDir = m_genInfo.m_targetDir = GetTmpPath();
m_genInfo.m_fileName = mwmName;
m_genInfo.m_idToWikidataFilename = m_genInfo.GetIntermediateFileName(kWikidataFilename);
m_genInfo.m_citiesBoundariesFilename = GetCitiesBoundariesPath();
RawGenerator rawGenerator(m_genInfo);
rawGenerator.ForceReloadCache();
if (makeWorld)
rawGenerator.GenerateWorld(false /* cutBordersByWater */);
rawGenerator.GenerateCountries(true /* isTests */);
CHECK(rawGenerator.Execute(), ("Error generating", mwmName));
}
void TestRawGenerator::BuildFeatures(std::string const & mwmName)
{
using namespace feature;
auto const type = IsWorld(mwmName) ? DataHeader::MapType::World : DataHeader::MapType::Country;
CHECK(GenerateFinalFeatures(m_genInfo, mwmName, type), ());
std::string const mwmPath = GetMwmPath(mwmName);
CHECK(BuildOffsetsTable(mwmPath), ());
CHECK(indexer::BuildIndexFromDataFile(mwmPath, mwmPath), ());
}
void TestRawGenerator::BuildSearch(std::string const & mwmName)
{
CHECK(indexer::BuildSearchIndexFromDataFile(mwmName, m_genInfo, true /* forceRebuild */, 1 /* threadsCount */), ());
if (IsWorld(mwmName))
{
generator::OsmIdToBoundariesTable table;
CHECK(generator::DeserializeBoundariesTable(GetCitiesBoundariesPath(), table), ());
CHECK(generator::BuildCitiesBoundaries(GetMwmPath(mwmName), table), ());
}
}
void TestRawGenerator::BuildRouting(std::string const & mwmName, std::string const & countryName)
{
using namespace routing_builder;
CountryParentNameGetterFn const parentGetter = [&countryName](std::string const & name)
{ return (name != countryName ? countryName : std::string()); };
std::string const filePath = GetMwmPath(mwmName);
std::string const osmToFeatureFilename = filePath + OSM2FEATURE_FILE_EXTENSION;
routing_builder::BuildRoutingIndex(filePath, countryName, parentGetter);
auto osm2feature = routing::CreateWay2FeatureMapper(filePath, osmToFeatureFilename);
BuildRoadAccessInfo(filePath, m_genInfo.GetIntermediateFileName(ROAD_ACCESS_FILENAME), *osm2feature);
BuildCamerasInfo(filePath, m_genInfo.GetIntermediateFileName(CAMERAS_TO_WAYS_FILENAME), osmToFeatureFilename);
auto routingGraph = CreateIndexGraph(filePath, countryName, parentGetter);
CHECK(routingGraph, ());
BuildRoadRestrictions(*routingGraph, filePath, m_genInfo.GetIntermediateFileName(RESTRICTIONS_FILENAME),
osmToFeatureFilename);
BuildMaxspeedsSection(routingGraph.get(), filePath, osmToFeatureFilename,
m_genInfo.GetIntermediateFileName(MAXSPEEDS_FILENAME));
}
routing::FeatureIdToOsmId TestRawGenerator::LoadFID2OsmID(std::string const & mwmName)
{
routing::FeatureIdToOsmId ids;
routing::ParseWaysFeatureIdToOsmIdMapping(GetMwmPath(mwmName) + OSM2FEATURE_FILE_EXTENSION, ids);
return ids;
}
std::string TestRawGenerator::GetMwmPath(std::string const & mwmName) const
{
return m_genInfo.GetTargetFileName(mwmName, DATA_FILE_EXTENSION);
}
std::string TestRawGenerator::GetCitiesBoundariesPath() const
{
return m_genInfo.GetTmpFileName(CITIES_BOUNDARIES_FILE_TAG, ".bin");
}
bool TestRawGenerator::IsWorld(std::string const & mwmName) const
{
return (mwmName == WORLD_FILE_NAME);
}
} // namespace tests_support
} // namespace generator

View file

@ -0,0 +1,64 @@
#pragma once
#include "generator/feature_builder.hpp"
#include "generator/generate_info.hpp"
#include "generator/routing_helpers.hpp"
#include "indexer/data_source.hpp"
namespace generator
{
namespace tests_support
{
bool MakeFakeBordersFile(std::string const & intemediatePath, std::string const & fileName);
class TestRawGenerator
{
feature::GenerateInfo m_genInfo;
std::string const & GetTmpPath() const { return m_genInfo.m_cacheDir; }
public:
TestRawGenerator();
~TestRawGenerator();
void SetupTmpFolder(std::string const & tmpPath);
void BuildFB(std::string const & osmFilePath, std::string const & mwmName, bool makeWorld = false);
void BuildFeatures(std::string const & mwmName);
void BuildSearch(std::string const & mwmName);
void BuildRouting(std::string const & mwmName, std::string const & countryName);
routing::FeatureIdToOsmId LoadFID2OsmID(std::string const & mwmName);
template <class FnT>
void ForEachFB(std::string const & mwmName, FnT && fn)
{
using namespace feature;
ForEachFeatureRawFormat(m_genInfo.GetTmpFileName(mwmName), [&fn](FeatureBuilder const & fb, uint64_t) { fn(fb); });
}
template <class FnT>
void ForEachFeature(std::string const & mwmName, FnT && fn)
{
FrozenDataSource dataSource;
auto const res = dataSource.RegisterMap(platform::LocalCountryFile::MakeTemporary(GetMwmPath(mwmName)));
CHECK_EQUAL(res.second, MwmSet::RegResult::Success, ());
FeaturesLoaderGuard guard(dataSource, res.first);
uint32_t const count = guard.GetNumFeatures();
for (uint32_t id = 0; id < count; ++id)
fn(guard.GetFeatureByIndex(id));
}
std::string GetMwmPath(std::string const & mwmName) const;
std::string GetCitiesBoundariesPath() const;
feature::GenerateInfo & GetGenInfo() { return m_genInfo; }
bool IsWorld(std::string const & mwmName) const;
static char const * kWikidataFilename;
};
} // namespace tests_support
} // namespace generator

View file

@ -0,0 +1,190 @@
#include "generator/generator_tests_support/test_mwm_builder.hpp"
#include "generator/centers_table_builder.hpp"
#include "generator/cities_ids_builder.hpp"
#include "generator/feature_builder.hpp"
#include "generator/feature_generator.hpp"
#include "generator/feature_merger.hpp"
#include "generator/feature_sorter.hpp"
#include "generator/generator_tests_support/test_feature.hpp"
#include "generator/search_index_builder.hpp"
#include "indexer/city_boundary.hpp"
#include "indexer/data_header.hpp"
#include "indexer/feature_meta.hpp"
#include "indexer/features_offsets_table.hpp"
#include "indexer/ftypes_matcher.hpp"
#include "indexer/index_builder.hpp"
#include "indexer/rank_table.hpp"
#include "storage/country_info_getter.hpp"
#include "platform/local_country_file.hpp"
#include "coding/internal/file_data.hpp"
#include "base/macros.hpp"
#include "base/string_utils.hpp"
#include "defines.hpp"
namespace generator
{
namespace tests_support
{
using namespace feature;
using std::string, std::vector;
namespace
{
bool WriteRegionDataForTests(string const & path, vector<string> const & languages)
{
try
{
FilesContainerW writer(path, FileWriter::OP_WRITE_EXISTING);
RegionData regionData;
regionData.SetLanguages(languages);
auto w = writer.GetWriter(REGION_INFO_FILE_TAG);
regionData.Serialize(*w);
}
catch (Writer::Exception const & e)
{
LOG(LERROR, ("Error writing file:", e.Msg()));
return false;
}
return true;
}
} // namespace
TestMwmBuilder::TestMwmBuilder(platform::LocalCountryFile & file, DataHeader::MapType type, uint32_t version)
: m_file(file)
, m_type(type)
, m_collector(std::make_unique<FeaturesCollector>(m_file.GetPath(MapFileType::Map) + EXTENSION_TMP))
, m_version(version)
{}
TestMwmBuilder::~TestMwmBuilder()
{
if (m_collector)
Finish();
}
void TestMwmBuilder::Add(TestFeature const & feature)
{
FeatureBuilder fb;
feature.Serialize(fb);
CHECK(Add(fb), (fb));
}
void TestMwmBuilder::AddSafe(TestFeature const & feature)
{
FeatureBuilder fb;
feature.Serialize(fb);
(void)Add(fb);
}
bool TestMwmBuilder::Add(FeatureBuilder & fb)
{
CHECK(m_collector, ("It's not possible to add features after call to Finish()."));
switch (m_type)
{
case DataHeader::MapType::Country:
if (!feature::PreprocessForCountryMap(fb))
return false;
break;
case DataHeader::MapType::World:
if (!feature::PreprocessForWorldMap(fb))
return false;
break;
case DataHeader::MapType::WorldCoasts: CHECK(false, ("Coasts are not supported in test builder")); break;
}
auto const & isCityTownOrVillage = ftypes::IsCityTownOrVillageChecker::Instance();
if (isCityTownOrVillage(fb.GetTypes()) && fb.GetGeomType() == GeomType::Area)
{
auto const & metadata = fb.GetMetadata();
uint64_t testId;
CHECK(strings::to_uint(metadata.Get(Metadata::FMD_TEST_ID), testId), ());
m_boundariesTable.Append(testId, indexer::CityBoundary(fb.GetOuterGeometry()));
fb.SetCenter(fb.GetGeometryCenter());
}
if (!fb.RemoveInvalidTypes() || !fb.PreSerializeAndRemoveUselessNamesForIntermediate())
{
LOG(LWARNING, ("No types."));
return false;
}
m_collector->Collect(fb);
return true;
}
void TestMwmBuilder::SetPostcodesData(string const & postcodesPath, indexer::PostcodePointsDatasetType postcodesType,
std::shared_ptr<storage::CountryInfoGetter> const & countryInfoGetter)
{
m_postcodesPath = postcodesPath;
m_postcodesType = postcodesType;
m_postcodesCountryInfoGetter = countryInfoGetter;
}
void TestMwmBuilder::SetMwmLanguages(vector<string> const & languages)
{
m_languages = languages;
}
void TestMwmBuilder::Finish()
{
CHECK(m_collector, ("Finish() already was called."));
string const tmpFilePath = m_collector->GetFilePath();
m_collector.reset();
GenerateInfo info;
info.m_targetDir = m_file.GetDirectory();
info.m_tmpDir = m_file.GetDirectory();
info.m_intermediateDir = m_file.GetDirectory();
info.m_versionDate = static_cast<uint32_t>(base::YYMMDDToSecondsSinceEpoch(m_version));
CHECK(GenerateFinalFeatures(info, m_file.GetCountryFile().GetName(), m_type), ("Can't sort features."));
CHECK(base::DeleteFileX(tmpFilePath), ());
string const path = m_file.GetPath(MapFileType::Map);
UNUSED_VALUE(base::DeleteFileX(path + OSM2FEATURE_FILE_EXTENSION));
CHECK(BuildOffsetsTable(path), ("Can't build feature offsets table."));
CHECK(indexer::BuildIndexFromDataFile(path, path), ("Can't build geometry index."));
CHECK(indexer::BuildSearchIndexFromDataFile(m_file.GetCountryName(), info, true /* forceRebuild */,
1 /* threadsCount */),
("Can't build search index."));
if (!m_postcodesPath.empty() && m_postcodesCountryInfoGetter)
{
CHECK(indexer::BuildPostcodePointsWithInfoGetter(m_file.GetDirectory(), m_file.GetCountryName(), m_postcodesType,
m_postcodesPath, true /* forceRebuild */,
*m_postcodesCountryInfoGetter),
("Can't build postcodes section."));
}
UNUSED_VALUE(base::DeleteFileX(path + TEMP_ADDR_EXTENSION));
if (m_type == DataHeader::MapType::World)
{
CHECK(generator::BuildCitiesBoundariesForTesting(path, m_boundariesTable), ());
CHECK(generator::BuildCitiesIdsForTesting(path), ());
}
CHECK(indexer::BuildCentersTableFromDataFile(path, true /* forceRebuild */), ("Can't build centers table."));
CHECK(search::SearchRankTableBuilder::CreateIfNotExists(path), ());
if (!m_languages.empty())
CHECK(WriteRegionDataForTests(path, m_languages), ());
m_file.SyncWithDisk();
}
} // namespace tests_support
} // namespace generator

View file

@ -0,0 +1,66 @@
#pragma once
#include "generator/cities_boundaries_builder.hpp"
#include "generator/postcode_points_builder.hpp"
#include "indexer/data_header.hpp"
#include "base/timer.hpp"
#include <memory>
#include <string>
#include <vector>
namespace feature
{
class FeaturesCollector;
class FeatureBuilder;
} // namespace feature
namespace platform
{
class LocalCountryFile;
}
namespace storage
{
class CountryInfoGetter;
}
namespace generator
{
namespace tests_support
{
class TestFeature;
class TestMwmBuilder
{
public:
TestMwmBuilder(platform::LocalCountryFile & file, feature::DataHeader::MapType type,
uint32_t version = base::GenerateYYMMDD(base::SecondsSinceEpoch()));
~TestMwmBuilder();
void Add(TestFeature const & feature);
void AddSafe(TestFeature const & feature);
bool Add(feature::FeatureBuilder & fb);
void SetPostcodesData(std::string const & postcodesPath, indexer::PostcodePointsDatasetType postcodesType,
std::shared_ptr<storage::CountryInfoGetter> const & countryInfoGetter);
void SetMwmLanguages(std::vector<std::string> const & languages);
void Finish();
private:
platform::LocalCountryFile & m_file;
feature::DataHeader::MapType m_type;
std::vector<std::string> m_languages;
std::unique_ptr<feature::FeaturesCollector> m_collector;
TestIdToBoundariesTable m_boundariesTable;
std::shared_ptr<storage::CountryInfoGetter> m_postcodesCountryInfoGetter;
std::string m_postcodesPath;
indexer::PostcodePointsDatasetType m_postcodesType;
uint32_t m_version = 0;
};
} // namespace tests_support
} // namespace generator

View file

@ -0,0 +1,17 @@
#include "generator/generator_tests_support/test_with_classificator.hpp"
#include "indexer/classificator_loader.hpp"
#include "indexer/map_style.hpp"
#include "indexer/map_style_reader.hpp"
namespace generator
{
namespace tests_support
{
TestWithClassificator::TestWithClassificator()
{
GetStyleReader().SetCurrentStyle(MapStyleMerged);
classificator::Load();
}
} // namespace tests_support
} // namespace generator

View file

@ -0,0 +1,14 @@
#pragma once
namespace generator
{
namespace tests_support
{
class TestWithClassificator
{
public:
TestWithClassificator();
virtual ~TestWithClassificator() = default;
};
} // namespace tests_support
} // namespace generator

View file

@ -0,0 +1,86 @@
#include "generator/generator_tests_support/test_with_custom_mwms.hpp"
#include "storage/country_info_getter.hpp"
#include "platform/local_country_file_utils.hpp"
#include "base/stl_helpers.hpp"
#include "base/timer.hpp"
namespace generator
{
namespace tests_support
{
using namespace platform;
TestWithCustomMwms::TestWithCustomMwms()
{
m_version = base::GenerateYYMMDD(base::SecondsSinceEpoch());
}
TestWithCustomMwms::~TestWithCustomMwms()
{
m_dataSource.ClearCache();
for (auto const & file : m_files)
Cleanup(file);
}
// static
void TestWithCustomMwms::Cleanup(LocalCountryFile const & file)
{
CountryIndexes::DeleteFromDisk(file);
file.DeleteFromDisk(MapFileType::Map);
}
void TestWithCustomMwms::DeregisterMap(std::string const & name)
{
auto const file = CountryFile(name);
auto it = base::FindIf(m_files, [&file](LocalCountryFile const & f) { return f.GetCountryFile() == file; });
if (it == m_files.end())
return;
m_dataSource.DeregisterMap(file);
Cleanup(*it);
m_files.erase(it);
}
template <class FnT>
void TestWithCustomMwms::RegisterLocalMapsImpl(FnT && check)
{
std::vector<LocalCountryFile> localFiles;
FindAllLocalMapsAndCleanup(std::numeric_limits<int64_t>::max() /* latestVersion */, localFiles);
for (auto const & file : localFiles)
{
// Always load World.mwm, important for search.
auto const & name = file.GetCountryName();
if (name != WORLD_FILE_NAME && !check(name))
continue;
auto const res = m_dataSource.RegisterMap(file);
if (res.second == MwmSet::RegResult::Success)
{
auto const & info = res.first.GetInfo();
OnMwmBuilt(*info);
}
else
CHECK_EQUAL(res.second, MwmSet::RegResult::VersionAlreadyExists, ());
}
}
void TestWithCustomMwms::RegisterLocalMapsInViewport(m2::RectD const & viewport)
{
auto const countriesInfo = storage::CountryInfoReader::CreateCountryInfoGetter(GetPlatform());
RegisterLocalMapsImpl([&](std::string const & name)
{ return countriesInfo->GetLimitRectForLeaf(name).IsIntersect(viewport); });
}
void TestWithCustomMwms::RegisterLocalMapsByPrefix(std::string const & prefix)
{
RegisterLocalMapsImpl([&](std::string const & name) { return name.starts_with(prefix); });
}
} // namespace tests_support
} // namespace generator

View file

@ -0,0 +1,90 @@
#pragma once
#include "generator/generator_tests_support/test_mwm_builder.hpp"
#include "generator/generator_tests_support/test_with_classificator.hpp"
#include "editor/editable_data_source.hpp"
#include "indexer/data_header.hpp"
#include "indexer/mwm_set.hpp"
#include "platform/country_file.hpp"
#include "platform/local_country_file.hpp"
#include "platform/platform.hpp"
#include "base/assert.hpp"
#include "base/stl_helpers.hpp"
#include <string>
#include <utility>
#include <vector>
namespace generator
{
namespace tests_support
{
class TestWithCustomMwms : public TestWithClassificator
{
public:
TestWithCustomMwms();
~TestWithCustomMwms() override;
// Creates a physical country file on a disk, which will be removed
// at the end of the test. |fn| is a delegate that accepts a single
// argument - TestMwmBuilder and adds all necessary features to the
// country file.
template <typename BuildFn>
MwmSet::MwmId BuildMwm(std::string name, feature::DataHeader::MapType type, BuildFn && fn)
{
m_files.emplace_back(GetPlatform().WritableDir(), platform::CountryFile(std::move(name)), 0 /* version */);
auto & file = m_files.back();
Cleanup(file);
{
generator::tests_support::TestMwmBuilder builder(file, type, m_version);
fn(builder);
}
auto result = m_dataSource.RegisterMap(file);
CHECK_EQUAL(result.second, MwmSet::RegResult::Success, ());
auto id = result.first;
auto const info = id.GetInfo();
CHECK(info.get(), ());
OnMwmBuilt(*info);
return id;
}
void DeregisterMap(std::string const & name);
template <typename BuildFn>
MwmSet::MwmId BuildWorld(BuildFn && fn)
{
return BuildMwm("testWorld", feature::DataHeader::MapType::World, fn);
}
template <typename BuildFn>
MwmSet::MwmId BuildCountry(std::string_view name, BuildFn && fn)
{
return BuildMwm(std::string(name), feature::DataHeader::MapType::Country, fn);
}
void SetMwmVersion(uint32_t version) { m_version = version; }
void RegisterLocalMapsInViewport(m2::RectD const & viewport);
void RegisterLocalMapsByPrefix(std::string const & prefix);
protected:
template <class FnT>
void RegisterLocalMapsImpl(FnT && check);
static void Cleanup(platform::LocalCountryFile const & file);
virtual void OnMwmBuilt(MwmInfo const & /* info */) {}
EditableDataSource m_dataSource;
std::vector<platform::LocalCountryFile> m_files;
uint32_t m_version = 0;
};
} // namespace tests_support
} // namespace generator