Repo created
This commit is contained in:
parent
4af19165ec
commit
68073add76
12458 changed files with 12350765 additions and 2 deletions
13
libs/map/style_tests/CMakeLists.txt
Normal file
13
libs/map/style_tests/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
project(style_tests)
|
||||
|
||||
set(SRC
|
||||
classificator_tests.cpp
|
||||
dashes_test.cpp
|
||||
helpers.hpp
|
||||
style_symbols_consistency_test.cpp
|
||||
)
|
||||
|
||||
omim_add_test(${PROJECT_NAME} ${SRC})
|
||||
|
||||
target_link_libraries(${PROJECT_NAME} indexer)
|
||||
|
||||
402
libs/map/style_tests/classificator_tests.cpp
Normal file
402
libs/map/style_tests/classificator_tests.cpp
Normal file
|
|
@ -0,0 +1,402 @@
|
|||
#include "helpers.hpp"
|
||||
#include "testing/testing.hpp"
|
||||
|
||||
#include "indexer/classificator.hpp"
|
||||
#include "indexer/feature_data.hpp"
|
||||
#include "indexer/feature_visibility.hpp"
|
||||
|
||||
#include "base/logging.hpp"
|
||||
#include "base/stl_helpers.hpp"
|
||||
|
||||
namespace classificator_tests
|
||||
{
|
||||
using namespace std;
|
||||
|
||||
class DoCheckConsistency
|
||||
{
|
||||
Classificator const & m_c;
|
||||
|
||||
public:
|
||||
explicit DoCheckConsistency(Classificator const & c) : m_c(c) {}
|
||||
void operator()(ClassifObject const * p, uint32_t type) const
|
||||
{
|
||||
if (p->IsDrawableAny() && !m_c.IsTypeValid(type))
|
||||
TEST(false, ("Inconsistency type", type, m_c.GetFullObjectName(type)));
|
||||
}
|
||||
};
|
||||
|
||||
UNIT_TEST(Classificator_CheckConsistency)
|
||||
{
|
||||
styles::RunForEveryMapStyle([](MapStyle)
|
||||
{
|
||||
Classificator const & c = classif();
|
||||
|
||||
DoCheckConsistency doCheck(c);
|
||||
c.ForEachTree(doCheck);
|
||||
});
|
||||
}
|
||||
|
||||
using namespace feature;
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
class DoCheckStyles
|
||||
{
|
||||
Classificator const & m_c;
|
||||
GeomType m_geomType;
|
||||
int m_rules;
|
||||
|
||||
public:
|
||||
DoCheckStyles(Classificator const & c, GeomType geomType, int rules) : m_c(c), m_geomType(geomType), m_rules(rules) {}
|
||||
|
||||
void operator()(ClassifObject const * p, uint32_t type) const
|
||||
{
|
||||
// We can't put TEST here, should check output manually. Or rewrite test in more sophisticated way.
|
||||
// - place=county/region are non-drawable
|
||||
// - historic=citywalls is not a Point
|
||||
// - waterway=* [tunnel] are non-drawable
|
||||
// - some highway,waterway are Point
|
||||
|
||||
if (p->IsDrawableAny())
|
||||
{
|
||||
TypesHolder holder(m_geomType);
|
||||
holder.Add(type);
|
||||
|
||||
pair<int, int> const range = GetDrawableScaleRangeForRules(holder, m_rules);
|
||||
if (range.first == -1 || range.second == -1)
|
||||
LOG(LWARNING, ("No styles:", m_c.GetFullObjectName(type)));
|
||||
}
|
||||
else if (ftype::GetLevel(type) > 1)
|
||||
LOG(LWARNING, ("Type without any rules:", m_c.GetFullObjectName(type)));
|
||||
}
|
||||
};
|
||||
|
||||
void ForEachObject(Classificator const & c, vector<string_view> const & path, GeomType geomType, int rules)
|
||||
{
|
||||
uint32_t const type = c.GetTypeByPath(path);
|
||||
ClassifObject const * pObj = c.GetObject(type);
|
||||
|
||||
DoCheckStyles doCheck(c, geomType, rules);
|
||||
doCheck(pObj, type);
|
||||
pObj->ForEachObjectInTree(doCheck, type);
|
||||
}
|
||||
|
||||
void ForEachObject(Classificator const & c, string const & name, GeomType geomType, int rules)
|
||||
{
|
||||
ForEachObject(c, strings::Tokenize(name, "-"), geomType, rules);
|
||||
}
|
||||
|
||||
void CheckPointStyles(Classificator const & c, string const & name)
|
||||
{
|
||||
ForEachObject(c, name, GeomType::Point, RULE_CAPTION | RULE_SYMBOL);
|
||||
}
|
||||
|
||||
void CheckLineStyles(Classificator const & c, string const & name)
|
||||
{
|
||||
ForEachObject(c, name, GeomType::Line, RULE_PATH_TEXT);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
UNIT_TEST(Classificator_DrawingRules)
|
||||
{
|
||||
styles::RunForEveryMapStyle([](MapStyle style)
|
||||
{
|
||||
if (style != MapStyle::MapStyleDefaultLight && style != MapStyle::MapStyleDefaultDark)
|
||||
return;
|
||||
|
||||
Classificator const & c = classif();
|
||||
|
||||
LOG(LINFO, ("--------------- Point styles ---------------"));
|
||||
CheckPointStyles(c, "amenity");
|
||||
CheckPointStyles(c, "historic");
|
||||
CheckPointStyles(c, "office");
|
||||
CheckPointStyles(c, "place");
|
||||
CheckPointStyles(c, "shop");
|
||||
CheckPointStyles(c, "sport");
|
||||
CheckPointStyles(c, "tourism");
|
||||
CheckPointStyles(c, "highway-bus_stop");
|
||||
CheckPointStyles(c, "highway-motorway_junction");
|
||||
CheckPointStyles(c, "railway-station");
|
||||
CheckPointStyles(c, "railway-tram_stop");
|
||||
CheckPointStyles(c, "railway-halt");
|
||||
|
||||
LOG(LINFO, ("--------------- Linear styles ---------------"));
|
||||
CheckLineStyles(c, "highway");
|
||||
CheckLineStyles(c, "waterway");
|
||||
// CheckLineStyles(c, "railway");
|
||||
});
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
pair<int, int> GetMinMax(int level, vector<uint32_t> const & types, drule::TypeT ruleType)
|
||||
{
|
||||
pair<int, int> res(numeric_limits<int>::max(), numeric_limits<int>::min());
|
||||
|
||||
drule::KeysT keys;
|
||||
feature::GetDrawRule(types, level, feature::GeomType::Area, keys);
|
||||
|
||||
for (size_t i = 0; i < keys.size(); ++i)
|
||||
{
|
||||
if (keys[i].m_type != ruleType)
|
||||
continue;
|
||||
|
||||
if (keys[i].m_priority < res.first)
|
||||
res.first = keys[i].m_priority;
|
||||
if (keys[i].m_priority > res.second)
|
||||
res.second = keys[i].m_priority;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
string CombineArrT(base::StringIL const & arrT)
|
||||
{
|
||||
string result;
|
||||
for (auto it = arrT.begin(); it != arrT.end(); ++it)
|
||||
{
|
||||
if (it != arrT.begin())
|
||||
result.append("-");
|
||||
result.append(*it);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void CheckPriority(vector<base::StringIL> const & arrT, vector<size_t> const & arrI, drule::TypeT ruleType)
|
||||
{
|
||||
Classificator const & c = classif();
|
||||
vector<vector<uint32_t>> types;
|
||||
vector<vector<string>> typesInfo;
|
||||
|
||||
styles::RunForEveryMapStyle([&](MapStyle)
|
||||
{
|
||||
types.clear();
|
||||
typesInfo.clear();
|
||||
|
||||
size_t ind = 0;
|
||||
for (size_t i = 0; i < arrI.size(); ++i)
|
||||
{
|
||||
types.push_back(vector<uint32_t>());
|
||||
types.back().reserve(arrI[i]);
|
||||
typesInfo.push_back(vector<string>());
|
||||
typesInfo.back().reserve(arrI[i]);
|
||||
|
||||
for (size_t j = 0; j < arrI[i]; ++j)
|
||||
{
|
||||
types.back().push_back(c.GetTypeByPath(arrT[ind]));
|
||||
typesInfo.back().push_back(CombineArrT(arrT[ind]));
|
||||
++ind;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_EQUAL(ind, arrT.size(), ());
|
||||
|
||||
for (int level = scales::GetUpperWorldScale() + 1; level <= scales::GetUpperStyleScale(); ++level)
|
||||
{
|
||||
pair<int, int> minmax(numeric_limits<int>::max(), numeric_limits<int>::min());
|
||||
vector<string> minmaxInfo;
|
||||
for (size_t i = 0; i < types.size(); ++i)
|
||||
{
|
||||
pair<int, int> const mm = GetMinMax(level, types[i], ruleType);
|
||||
TEST_LESS(minmax.second, mm.first,
|
||||
("Priority bug on zoom", level, "group", i, ":", minmaxInfo, minmax.first, minmax.second, "vs",
|
||||
typesInfo[i], mm.first, mm.second));
|
||||
minmax = mm;
|
||||
minmaxInfo = typesInfo[i];
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// Check area drawing priority according to the types order below (from downmost to upmost).
|
||||
// If someone disagrees with this order, please refer to VNG :)
|
||||
// natural-coastline
|
||||
// place-island = natural-land
|
||||
// natural-scrub,heath,grassland = landuse-grass,farmland,forest
|
||||
// natural-water,lake = landuse-basin
|
||||
|
||||
UNIT_TEST(Classificator_AreaPriority)
|
||||
{
|
||||
CheckPriority(
|
||||
{// 0
|
||||
{"natural", "coastline"},
|
||||
// 1
|
||||
{"place", "island"},
|
||||
{"natural", "land"},
|
||||
// 2
|
||||
{"natural", "scrub"},
|
||||
{"natural", "heath"},
|
||||
{"natural", "grassland"},
|
||||
{"landuse", "grass"},
|
||||
{"landuse", "farmland"},
|
||||
{"landuse", "forest"},
|
||||
// ?
|
||||
//{"leisure", "park"}, {"leisure", "garden"}, - maybe next time (too tricky to do it now)
|
||||
// 3
|
||||
{"natural", "water"},
|
||||
{"natural", "water", "lake"},
|
||||
{"landuse", "basin"}},
|
||||
{1, 2, 6, 3}, drule::area);
|
||||
|
||||
CheckPriority(
|
||||
{
|
||||
// ? - linear waterways @todo: add ability to compare different drule types (areas vs lines)
|
||||
//{"waterway", "river"}, {"waterway", "stream"}, {"natural", "strait"}, {"waterway", "ditch"},
|
||||
// 0 - water areas
|
||||
{"natural", "water"},
|
||||
{"landuse", "reservoir"},
|
||||
{"natural", "water", "river"},
|
||||
{"waterway", "dock"},
|
||||
// ? - hatching fills @todo: absent in vehicle style, need to test main style only
|
||||
//{"leisure", "nature_reserve"}, {"boundary", "national_park"}, {"landuse", "military"},
|
||||
// 1 - above-water features
|
||||
{"man_made", "pier"},
|
||||
{"man_made", "breakwater"},
|
||||
{"waterway", "dam"},
|
||||
},
|
||||
{4, 3}, drule::area);
|
||||
|
||||
CheckPriority(
|
||||
{
|
||||
// 0
|
||||
{"leisure", "park"},
|
||||
// 1
|
||||
{"leisure", "pitch"},
|
||||
{"leisure", "playground"},
|
||||
{"sport", "multi"},
|
||||
},
|
||||
{1, 3}, drule::area);
|
||||
}
|
||||
|
||||
UNIT_TEST(Classificator_PoiPriority)
|
||||
{
|
||||
{
|
||||
CheckPriority(
|
||||
{
|
||||
// 1
|
||||
{"amenity", "drinking_water"},
|
||||
// 2
|
||||
{"tourism", "camp_site"},
|
||||
// 3
|
||||
{"tourism", "wilderness_hut"},
|
||||
// 4
|
||||
{"tourism", "alpine_hut"},
|
||||
},
|
||||
{1, 1, 1, 1}, drule::symbol);
|
||||
}
|
||||
}
|
||||
|
||||
UNIT_TEST(Classificator_MultipleTypesPoiPriority)
|
||||
{
|
||||
{
|
||||
CheckPriority(
|
||||
{// 1
|
||||
{"amenity", "atm"},
|
||||
// 2
|
||||
{"amenity", "bank"}},
|
||||
{1, 1}, drule::symbol);
|
||||
}
|
||||
|
||||
{
|
||||
CheckPriority(
|
||||
{
|
||||
// 1
|
||||
{"amenity", "bench"},
|
||||
{"amenity", "shelter"},
|
||||
// 2
|
||||
{"highway", "bus_stop"},
|
||||
{"amenity", "bus_station"},
|
||||
{"railway", "station"},
|
||||
{"railway", "halt"},
|
||||
{"railway", "tram_stop"},
|
||||
},
|
||||
{2, 5}, drule::symbol);
|
||||
}
|
||||
|
||||
/// @todo Check that all of sport=* icons priority is bigger than all of pitch, sports_center, recreation_ground.
|
||||
|
||||
{
|
||||
CheckPriority(
|
||||
{// 1
|
||||
{"leisure", "pitch"},
|
||||
// 2
|
||||
{"sport", "yoga"}},
|
||||
{1, 1}, drule::symbol);
|
||||
}
|
||||
|
||||
{
|
||||
CheckPriority(
|
||||
{// 1
|
||||
{"leisure", "sports_centre"},
|
||||
// 2
|
||||
{"sport", "shooting"}},
|
||||
{1, 1}, drule::symbol);
|
||||
}
|
||||
|
||||
{
|
||||
CheckPriority(
|
||||
{// 1
|
||||
{"landuse", "recreation_ground"},
|
||||
// 2
|
||||
{"sport", "multi"}},
|
||||
{1, 1}, drule::symbol);
|
||||
}
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
struct RangeEntry
|
||||
{
|
||||
uint32_t m_type;
|
||||
std::pair<int, int> m_range;
|
||||
|
||||
// From C++ 20.
|
||||
// auto operator<=>(RangeEntry const &) const = default;
|
||||
bool operator!=(RangeEntry const & rhs) const { return m_type != rhs.m_type || m_range != rhs.m_range; }
|
||||
|
||||
friend std::string DebugPrint(RangeEntry const & e)
|
||||
{
|
||||
std::ostringstream ss;
|
||||
ss << classif().GetReadableObjectName(e.m_type) << "; (" << e.m_range.first << "," << e.m_range.second << ")";
|
||||
return ss.str();
|
||||
}
|
||||
};
|
||||
} // namespace
|
||||
|
||||
UNIT_TEST(Classificator_HighwayZoom_AcrossStyles)
|
||||
{
|
||||
std::array<std::vector<RangeEntry>, MapStyleCount> scales;
|
||||
|
||||
styles::RunForEveryMapStyle([&scales](MapStyle style)
|
||||
{
|
||||
auto const & cl = classif();
|
||||
uint32_t const type = cl.GetTypeByPath({"highway"});
|
||||
ClassifObject const * pObj = cl.GetObject(type);
|
||||
|
||||
pObj->ForEachObjectInTree([&scales, style](ClassifObject const *, uint32_t type)
|
||||
{
|
||||
TypesHolder holder(GeomType::Line);
|
||||
holder.Add(type);
|
||||
scales[style].push_back({type, GetDrawableScaleRangeForRules(holder, RULE_LINE)});
|
||||
}, type);
|
||||
});
|
||||
|
||||
for (size_t iStyle = 1; iStyle < MapStyleCount; ++iStyle)
|
||||
{
|
||||
if (iStyle == MapStyleMerged)
|
||||
continue;
|
||||
|
||||
// Don't put TEST, only diagnostic logs. In general, Clear and Vehical visibility styles are different
|
||||
// for highways like: footway, path, steps, cycleway, bridleway, track, service.
|
||||
TEST_EQUAL(scales[0].size(), scales[iStyle].size(), (iStyle));
|
||||
for (size_t j = 0; j < scales[0].size(); ++j)
|
||||
if (scales[0][j] != scales[iStyle][j])
|
||||
LOG(LWARNING, (scales[0][j], scales[iStyle][j]));
|
||||
}
|
||||
}
|
||||
} // namespace classificator_tests
|
||||
38
libs/map/style_tests/dashes_test.cpp
Normal file
38
libs/map/style_tests/dashes_test.cpp
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
#include "helpers.hpp"
|
||||
#include "testing/testing.hpp"
|
||||
|
||||
#include "drape/stipple_pen_resource.hpp"
|
||||
#include "drape_frontend/visual_params.hpp"
|
||||
#include "indexer/classificator_loader.hpp"
|
||||
#include "indexer/drawing_rules.hpp"
|
||||
#include "indexer/drules_include.hpp"
|
||||
|
||||
UNIT_TEST(Test_Dashes)
|
||||
{
|
||||
styles::RunForEveryMapStyle([](MapStyle)
|
||||
{
|
||||
drule::rules().ForEachRule([](drule::BaseRule const * rule)
|
||||
{
|
||||
LineRuleProto const * const line = rule->GetLine();
|
||||
if (nullptr == line || !line->has_dashdot())
|
||||
return;
|
||||
|
||||
DashDotProto const & dd = line->dashdot();
|
||||
|
||||
int const n = dd.dd_size();
|
||||
if (n > 0)
|
||||
{
|
||||
TEST_GREATER_OR_EQUAL(n, 2, ());
|
||||
TEST_LESS_OR_EQUAL(n, 4, ());
|
||||
for (int i = 0; i < n; ++i)
|
||||
{
|
||||
double const value = dd.dd(i);
|
||||
TEST_GREATER_OR_EQUAL(value, 0.0, ());
|
||||
}
|
||||
|
||||
double const patternLength = (dd.dd(0) + dd.dd(1)) * df::kMaxVisualScale;
|
||||
TEST_LESS_OR_EQUAL(patternLength, dp::kMaxStipplePenLength, (dd.dd(0), dd.dd(1)));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
31
libs/map/style_tests/helpers.hpp
Normal file
31
libs/map/style_tests/helpers.hpp
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
#pragma once
|
||||
|
||||
#include "indexer/classificator_loader.hpp"
|
||||
#include "indexer/map_style.hpp"
|
||||
#include "indexer/map_style_reader.hpp"
|
||||
|
||||
#include "base/logging.hpp"
|
||||
|
||||
namespace styles
|
||||
{
|
||||
template <class TFn>
|
||||
void RunForEveryMapStyle(TFn && fn)
|
||||
{
|
||||
auto & reader = GetStyleReader();
|
||||
for (size_t s = 0; s < MapStyleCount; ++s)
|
||||
{
|
||||
MapStyle const mapStyle = static_cast<MapStyle>(s);
|
||||
if (mapStyle != MapStyle::MapStyleMerged)
|
||||
{
|
||||
reader.SetCurrentStyle(mapStyle);
|
||||
classificator::Load();
|
||||
LOG(LINFO, ("Test with map style", mapStyle));
|
||||
fn(mapStyle);
|
||||
}
|
||||
}
|
||||
|
||||
// Restore default style.
|
||||
reader.SetCurrentStyle(kDefaultMapStyle);
|
||||
classificator::Load();
|
||||
}
|
||||
} // namespace styles
|
||||
97
libs/map/style_tests/style_symbols_consistency_test.cpp
Normal file
97
libs/map/style_tests/style_symbols_consistency_test.cpp
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
#include "testing/testing.hpp"
|
||||
|
||||
#include "map/style_tests/helpers.hpp"
|
||||
|
||||
#include "indexer/classificator_loader.hpp"
|
||||
#include "indexer/drawing_rules.hpp"
|
||||
#include "indexer/drules_include.hpp"
|
||||
#include "indexer/map_style_reader.hpp"
|
||||
|
||||
#include "platform/platform.hpp"
|
||||
|
||||
#include "base/logging.hpp"
|
||||
|
||||
#include "coding/parse_xml.hpp"
|
||||
#include "coding/reader.hpp"
|
||||
|
||||
#include <cstring> // std::strcmp
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace style_symbols_consistency_tests
|
||||
{
|
||||
typedef std::set<std::string> StringSet;
|
||||
|
||||
class SdfParsingDispatcher
|
||||
{
|
||||
public:
|
||||
explicit SdfParsingDispatcher(StringSet & symbols) : m_symbols(symbols) {}
|
||||
|
||||
bool Push(char const *) { return true; }
|
||||
void Pop(char const *) {}
|
||||
void CharData(std::string const &) {}
|
||||
void AddAttr(char const * attribute, char const * value)
|
||||
{
|
||||
if (0 == std::strcmp(attribute, "name"))
|
||||
m_symbols.insert(value);
|
||||
}
|
||||
|
||||
private:
|
||||
StringSet & m_symbols;
|
||||
};
|
||||
|
||||
StringSet GetSymbolsSetFromDrawingRule()
|
||||
{
|
||||
StringSet symbols;
|
||||
drule::rules().ForEachRule([&symbols](drule::BaseRule const * rule)
|
||||
{
|
||||
SymbolRuleProto const * symbol = rule->GetSymbol();
|
||||
if (symbol && !symbol->name().empty())
|
||||
symbols.insert(symbol->name());
|
||||
});
|
||||
return symbols;
|
||||
}
|
||||
|
||||
StringSet GetSymbolsSetFromResourcesFile(std::string_view density)
|
||||
{
|
||||
StringSet symbols;
|
||||
SdfParsingDispatcher dispatcher(symbols);
|
||||
ReaderPtr<Reader> reader = GetStyleReader().GetResourceReader("symbols.sdf", density);
|
||||
ReaderSource<ReaderPtr<Reader>> source(reader);
|
||||
ParseXML(source, dispatcher);
|
||||
return symbols;
|
||||
}
|
||||
|
||||
// Tests that all symbols specified in drawing rules have corresponding symbols in resources
|
||||
UNIT_TEST(Test_SymbolsConsistency)
|
||||
{
|
||||
bool res = true;
|
||||
|
||||
std::string_view constexpr densities[] = {"mdpi", "hdpi", "xhdpi", "xxhdpi", "xxxhdpi", "6plus"};
|
||||
|
||||
styles::RunForEveryMapStyle([&](MapStyle mapStyle)
|
||||
{
|
||||
StringSet const drawingRuleSymbols = GetSymbolsSetFromDrawingRule();
|
||||
|
||||
for (std::string_view density : densities)
|
||||
{
|
||||
StringSet const resourceStyles = GetSymbolsSetFromResourcesFile(density);
|
||||
|
||||
std::vector<std::string> missed;
|
||||
std::set_difference(drawingRuleSymbols.begin(), drawingRuleSymbols.end(), resourceStyles.begin(),
|
||||
resourceStyles.end(), back_inserter(missed));
|
||||
|
||||
if (!missed.empty())
|
||||
{
|
||||
// We are interested in all set of bugs, therefore we do not stop test here but
|
||||
// continue it just keeping in res that test failed.
|
||||
LOG(LINFO, ("Symbols mismatch: style", mapStyle, ", density", density, ", missed", missed));
|
||||
res = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
TEST(res, ());
|
||||
}
|
||||
} // namespace style_symbols_consistency_tests
|
||||
Loading…
Add table
Add a link
Reference in a new issue