Repo created
This commit is contained in:
parent
4af19165ec
commit
68073add76
12458 changed files with 12350765 additions and 2 deletions
12
libs/map/mwm_tests/CMakeLists.txt
Normal file
12
libs/map/mwm_tests/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
project(mwm_tests)
|
||||
|
||||
set(SRC
|
||||
multithread_mwm_test.cpp
|
||||
mwm_foreach_test.cpp
|
||||
mwm_index_test.cpp
|
||||
world_map_test.cpp
|
||||
)
|
||||
|
||||
omim_add_test(${PROJECT_NAME} ${SRC})
|
||||
|
||||
target_link_libraries(${PROJECT_NAME} map)
|
||||
98
libs/map/mwm_tests/multithread_mwm_test.cpp
Normal file
98
libs/map/mwm_tests/multithread_mwm_test.cpp
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
#include "testing/testing.hpp"
|
||||
|
||||
#include "map/features_fetcher.hpp"
|
||||
|
||||
#include "indexer/scales.hpp"
|
||||
|
||||
#include "base/macros.hpp"
|
||||
#include "base/thread_pool_computational.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace multithread_mwm_test
|
||||
{
|
||||
using SourceT = FeaturesFetcher;
|
||||
|
||||
class FeaturesLoader
|
||||
{
|
||||
public:
|
||||
explicit FeaturesLoader(SourceT const & src) : m_src(src) {}
|
||||
|
||||
void operator()()
|
||||
{
|
||||
size_t const kCount = 2000;
|
||||
|
||||
for (size_t i = 0; i < kCount; ++i)
|
||||
{
|
||||
m2::RectD const r = GetRandomRect();
|
||||
m_scale = scales::GetScaleLevel(r);
|
||||
|
||||
m_src.ForEachFeature(r, [this](FeatureType & ft)
|
||||
{
|
||||
ft.ParseAllBeforeGeometry();
|
||||
|
||||
(void)ft.GetOuterGeometryStats();
|
||||
(void)ft.GetOuterTrianglesStats();
|
||||
|
||||
// Force load feature. We check asserts here. There is no any other constrains here.
|
||||
CHECK(!ft.IsEmptyGeometry(m_scale), (ft.GetID()));
|
||||
}, m_scale);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
// Get random rect inside m_src.
|
||||
m2::RectD GetRandomRect() const
|
||||
{
|
||||
int const count = std::max(1, rand() % 50);
|
||||
|
||||
int const x = rand() % count;
|
||||
int const y = rand() % count;
|
||||
|
||||
m2::RectD const r = m_src.GetWorldRect();
|
||||
double const sizeX = r.SizeX() / count;
|
||||
double const sizeY = r.SizeY() / count;
|
||||
|
||||
double const minX = r.minX() + x * sizeX;
|
||||
double const minY = r.minY() + y * sizeY;
|
||||
|
||||
return m2::RectD(minX, minY, minX + sizeX, minY + sizeY);
|
||||
}
|
||||
|
||||
SourceT const & m_src;
|
||||
int m_scale = 0;
|
||||
};
|
||||
|
||||
void RunTest(std::string const & file)
|
||||
{
|
||||
SourceT src;
|
||||
src.InitClassificator();
|
||||
|
||||
UNUSED_VALUE(src.RegisterMap(platform::LocalCountryFile::MakeForTesting(file)));
|
||||
|
||||
// Check that country rect is valid and not infinity.
|
||||
m2::RectD const r = src.GetWorldRect();
|
||||
TEST(r.IsValid(), ());
|
||||
|
||||
m2::RectD world(mercator::Bounds::FullRect());
|
||||
world.Inflate(-10.0, -10.0);
|
||||
|
||||
TEST(world.IsRectInside(r), ());
|
||||
|
||||
srand(666);
|
||||
|
||||
size_t const kCount = 20;
|
||||
base::ComputationalThreadPool pool(kCount);
|
||||
|
||||
for (size_t i = 0; i < kCount; ++i)
|
||||
pool.SubmitWork(FeaturesLoader(src));
|
||||
|
||||
pool.WaitingStop();
|
||||
}
|
||||
|
||||
UNIT_TEST(Threading_ForEachFeature)
|
||||
{
|
||||
RunTest("minsk-pass");
|
||||
}
|
||||
|
||||
} // namespace multithread_mwm_test
|
||||
315
libs/map/mwm_tests/mwm_foreach_test.cpp
Normal file
315
libs/map/mwm_tests/mwm_foreach_test.cpp
Normal file
|
|
@ -0,0 +1,315 @@
|
|||
#include "testing/testing.hpp"
|
||||
|
||||
#include "map/features_fetcher.hpp"
|
||||
|
||||
#include "indexer/classificator.hpp"
|
||||
#include "indexer/data_header.hpp"
|
||||
#include "indexer/feature_processor.hpp"
|
||||
#include "indexer/feature_visibility.hpp"
|
||||
#include "indexer/map_style_reader.hpp"
|
||||
#include "indexer/scales.hpp"
|
||||
|
||||
#include "platform/local_country_file_utils.hpp"
|
||||
|
||||
#include "geometry/rect_intersect.hpp"
|
||||
#include "geometry/robust_orientation.hpp"
|
||||
|
||||
#include "base/logging.hpp"
|
||||
#include "base/macros.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace mwm_for_each_test
|
||||
{
|
||||
using Cont = vector<uint32_t>;
|
||||
|
||||
bool IsDrawable(FeatureType & f, int scale)
|
||||
{
|
||||
if (f.IsEmptyGeometry(scale))
|
||||
return false;
|
||||
|
||||
if (feature::IsDrawableForIndex(f, scale))
|
||||
return true;
|
||||
|
||||
// Scale index ends on GetUpperScale(), but the actual visibility goes until GetUpperStyleScale().
|
||||
if (scale != scales::GetUpperScale())
|
||||
return false;
|
||||
|
||||
while (++scale <= scales::GetUpperStyleScale())
|
||||
if (feature::IsDrawableForIndex(f, scale))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
class AccumulatorBase
|
||||
{
|
||||
Cont & m_cont;
|
||||
|
||||
protected:
|
||||
int m_scale;
|
||||
|
||||
bool is_drawable(FeatureType & f) const
|
||||
{
|
||||
f.ParseGeometry(m_scale);
|
||||
f.ParseTriangles(m_scale);
|
||||
// TODO(pastk): need a better / more comprehensive lazy-loading consistency check,
|
||||
// e.g. ATM geometry is checked by its first point only, metadata is not checked at all..
|
||||
TEST_EQUAL(f.DebugString(), f.DebugString(), ());
|
||||
|
||||
return IsDrawable(f, m_scale);
|
||||
}
|
||||
|
||||
void add(FeatureType const & f) const
|
||||
{
|
||||
TEST(f.GetID().IsValid(), ());
|
||||
m_cont.push_back(f.GetID().m_index);
|
||||
}
|
||||
|
||||
void add(FeatureType const &, uint32_t index) const { m_cont.push_back(index); }
|
||||
|
||||
public:
|
||||
AccumulatorBase(int scale, Cont & cont) : m_cont(cont), m_scale(scale) {}
|
||||
|
||||
void operator()(FeatureType & f) const
|
||||
{
|
||||
TEST(is_drawable(f), (m_scale, f.DebugString()));
|
||||
add(f);
|
||||
}
|
||||
};
|
||||
|
||||
class IntersectCheck
|
||||
{
|
||||
m2::RectD m_rect;
|
||||
|
||||
m2::PointD m_prev;
|
||||
bool m_isPrev, m_intersect;
|
||||
|
||||
public:
|
||||
explicit IntersectCheck(m2::RectD const & r) : m_rect(r), m_isPrev(false), m_intersect(false) {}
|
||||
|
||||
void TestPoint(m2::PointD const & p) { m_intersect = m_rect.IsPointInside(p); }
|
||||
|
||||
void operator()(m2::PointD const & pt)
|
||||
{
|
||||
if (m_intersect)
|
||||
return;
|
||||
|
||||
if (m_isPrev)
|
||||
{
|
||||
m2::PointD d1 = m_prev;
|
||||
m2::PointD d2 = pt;
|
||||
m_intersect = m2::Intersect(m_rect, d1, d2);
|
||||
}
|
||||
else
|
||||
m_isPrev = true;
|
||||
|
||||
m_prev = pt;
|
||||
}
|
||||
|
||||
void operator()(m2::PointD const & p1, m2::PointD const & p2, m2::PointD const & p3)
|
||||
{
|
||||
if (m_intersect)
|
||||
return;
|
||||
|
||||
m2::PointD arrP[] = {p1, p2, p3};
|
||||
|
||||
// make right-oriented triangle
|
||||
if (m2::robust::OrientedS(arrP[0], arrP[1], arrP[2]) < 0.0)
|
||||
swap(arrP[1], arrP[2]);
|
||||
|
||||
bool isInside = true;
|
||||
m2::PointD const pt = m_rect.LeftTop();
|
||||
|
||||
for (size_t i = 0; i < 3; ++i)
|
||||
{
|
||||
// intersect edge with rect
|
||||
m_intersect = m2::Intersect(m_rect, arrP[i], arrP[(i + 1) % 3]);
|
||||
if (m_intersect)
|
||||
break;
|
||||
|
||||
if (isInside)
|
||||
{
|
||||
// or check if rect inside triangle (any point of rect)
|
||||
double const s = m2::robust::OrientedS(arrP[i], arrP[(i + 1) % 3], pt);
|
||||
isInside = (s >= 0.0);
|
||||
}
|
||||
}
|
||||
|
||||
m_intersect = m_intersect || isInside;
|
||||
}
|
||||
|
||||
bool IsIntersect() const { return m_intersect; }
|
||||
};
|
||||
|
||||
class AccumulatorEtalon : public AccumulatorBase
|
||||
{
|
||||
typedef AccumulatorBase base_type;
|
||||
|
||||
m2::RectD m_rect;
|
||||
|
||||
bool is_intersect(FeatureType & f) const
|
||||
{
|
||||
IntersectCheck check(m_rect);
|
||||
|
||||
using namespace feature;
|
||||
switch (f.GetGeomType())
|
||||
{
|
||||
case GeomType::Point: check.TestPoint(f.GetCenter()); break;
|
||||
case GeomType::Line: f.ForEachPoint(check, m_scale); break;
|
||||
case GeomType::Area: f.ForEachTriangle(check, m_scale); break;
|
||||
default: CHECK(false, ());
|
||||
}
|
||||
|
||||
return check.IsIntersect();
|
||||
}
|
||||
|
||||
public:
|
||||
AccumulatorEtalon(m2::RectD const & r, int scale, Cont & cont) : base_type(scale, cont), m_rect(r) {}
|
||||
|
||||
void operator()(FeatureType & f, uint32_t index) const
|
||||
{
|
||||
if (is_drawable(f) && is_intersect(f))
|
||||
add(f, index);
|
||||
}
|
||||
};
|
||||
|
||||
// Use this comparator for "sort" and "compare_sequence".
|
||||
struct FeatureIDCmp
|
||||
{
|
||||
int compare(uint32_t r1, uint32_t r2) const
|
||||
{
|
||||
if (r1 < r2)
|
||||
return -1;
|
||||
if (r2 < r1)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
bool operator()(uint32_t r1, uint32_t r2) const { return (compare(r1, r2) == -1); }
|
||||
};
|
||||
|
||||
/// Check that "test" contains all elements from "etalon".
|
||||
template <class TCont, class TCompare>
|
||||
bool compare_sequence(TCont const & etalon, TCont const & test, TCompare comp, size_t & errInd)
|
||||
{
|
||||
auto i1 = etalon.begin();
|
||||
auto i2 = test.begin();
|
||||
while (i1 != etalon.end() && i2 != test.end())
|
||||
{
|
||||
switch (comp.compare(*i1, *i2))
|
||||
{
|
||||
case 0: // equal
|
||||
++i1;
|
||||
++i2;
|
||||
break;
|
||||
case -1: // present in etalon, but missing in test - error
|
||||
{
|
||||
errInd = distance(etalon.begin(), i1);
|
||||
return false;
|
||||
}
|
||||
case 1: // present in test, but missing in etalon - actually it may be ok
|
||||
++i2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i1 != etalon.end())
|
||||
{
|
||||
errInd = distance(etalon.begin(), i1);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
class FindOffset
|
||||
{
|
||||
int m_level;
|
||||
uint32_t m_index;
|
||||
|
||||
public:
|
||||
FindOffset(int level, uint32_t index) : m_level(level), m_index(index) {}
|
||||
|
||||
void operator()(FeatureType & ft, uint32_t index)
|
||||
{
|
||||
if (index == m_index)
|
||||
{
|
||||
TEST(IsDrawable(ft, m_level), ());
|
||||
|
||||
LOG(LINFO, ("Feature index:", index));
|
||||
LOG(LINFO, ("Feature:", ft.DebugString()));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void RunTest(string const & countryFileName)
|
||||
{
|
||||
FeaturesFetcher src1;
|
||||
src1.InitClassificator();
|
||||
|
||||
platform::LocalCountryFile localFile(platform::LocalCountryFile::MakeForTesting(countryFileName));
|
||||
// Clean indexes to prevent mwm and indexes versions mismatch error.
|
||||
platform::CountryIndexes::DeleteFromDisk(localFile);
|
||||
UNUSED_VALUE(src1.RegisterMap(localFile));
|
||||
|
||||
vector<m2::RectD> rects;
|
||||
rects.push_back(src1.GetWorldRect());
|
||||
|
||||
ModelReaderPtr reader = platform::GetCountryReader(localFile, MapFileType::Map);
|
||||
|
||||
while (!rects.empty())
|
||||
{
|
||||
m2::RectD const r = rects.back();
|
||||
rects.pop_back();
|
||||
|
||||
int const scale = max(scales::GetUpperCountryScale(), scales::GetScaleLevel(r));
|
||||
|
||||
Cont v1, v2;
|
||||
{
|
||||
AccumulatorBase acc(scale, v1);
|
||||
src1.ForEachFeature(r, acc, scale);
|
||||
sort(v1.begin(), v1.end(), FeatureIDCmp());
|
||||
}
|
||||
{
|
||||
AccumulatorEtalon acc(r, scale, v2);
|
||||
feature::ForEachFeature(reader, acc);
|
||||
sort(v2.begin(), v2.end(), FeatureIDCmp());
|
||||
}
|
||||
|
||||
size_t const emptyInd = size_t(-1);
|
||||
size_t errInd = emptyInd;
|
||||
if (!compare_sequence(v2, v1, FeatureIDCmp(), errInd))
|
||||
{
|
||||
if (errInd != emptyInd)
|
||||
{
|
||||
FindOffset doFind(scale, v2[errInd]);
|
||||
feature::ForEachFeature(reader, doFind);
|
||||
}
|
||||
|
||||
TEST(false,
|
||||
("Failed for rect:", r, "; Scale level:", scale, "; Etalon size:", v2.size(), "; Index size:", v1.size()));
|
||||
}
|
||||
|
||||
if (!v2.empty() && (scale < scales::GetUpperScale()))
|
||||
{
|
||||
m2::RectD r1, r2;
|
||||
r.DivideByGreaterSize(r1, r2);
|
||||
rects.push_back(r1);
|
||||
rects.push_back(r2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// @todo The concept of this test became invalid after POI filtering when overlap in geometry index.
|
||||
/// Probably, will restore it with a new idea someday. Like build and check separate index without filtering.
|
||||
// UNIT_TEST(ForEach_QueryResults)
|
||||
//{
|
||||
// GetStyleReader().SetCurrentStyle(MapStyleMerged);
|
||||
// RunTest("minsk-pass");
|
||||
// }
|
||||
|
||||
} // namespace mwm_for_each_test
|
||||
75
libs/map/mwm_tests/mwm_index_test.cpp
Normal file
75
libs/map/mwm_tests/mwm_index_test.cpp
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
#include "testing/testing.hpp"
|
||||
|
||||
#include "map/features_fetcher.hpp"
|
||||
|
||||
#include "indexer/classificator_loader.hpp"
|
||||
#include "indexer/scales.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace
|
||||
{
|
||||
class CheckNonEmptyGeometry
|
||||
{
|
||||
public:
|
||||
vector<FeatureID> m_ids;
|
||||
|
||||
void operator()(FeatureID const & id) { m_ids.push_back(id); }
|
||||
|
||||
void operator()(FeatureType & ft)
|
||||
{
|
||||
bool res = false;
|
||||
ft.ForEachPoint([&res](m2::PointD const &) { res = true; }, m_scale);
|
||||
ft.ForEachTriangle([&res](m2::PointD const &, m2::PointD const &, m2::PointD const &) { res = true; }, m_scale);
|
||||
|
||||
TEST(res, (ft.DebugString(), "Scale =", m_scale));
|
||||
}
|
||||
|
||||
void SetScale(int scale)
|
||||
{
|
||||
m_ids.clear();
|
||||
m_scale = scale;
|
||||
}
|
||||
|
||||
private:
|
||||
int m_scale;
|
||||
};
|
||||
|
||||
bool RunTest(string const & countryFileName, int lowS, int highS)
|
||||
{
|
||||
FeaturesFetcher src;
|
||||
auto p = src.RegisterMap(platform::LocalCountryFile::MakeForTesting(countryFileName));
|
||||
if (p.second != MwmSet::RegResult::Success)
|
||||
return false;
|
||||
|
||||
MwmSet::MwmId const & id = p.first;
|
||||
ASSERT(id.IsAlive(), ());
|
||||
|
||||
version::Format const version = id.GetInfo()->m_version.GetFormat();
|
||||
if (version == version::Format::unknownFormat)
|
||||
return false;
|
||||
|
||||
CheckNonEmptyGeometry doCheck;
|
||||
for (int scale = lowS; scale <= highS; ++scale)
|
||||
{
|
||||
doCheck.SetScale(scale);
|
||||
src.ForEachFeatureID(mercator::Bounds::FullRect(), doCheck, scale);
|
||||
src.ReadFeatures(doCheck, doCheck.m_ids);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
UNIT_TEST(ForEachFeatureID_Test)
|
||||
{
|
||||
classificator::Load();
|
||||
|
||||
TEST(RunTest("World", 0, scales::GetUpperWorldScale()), ());
|
||||
TEST(RunTest("WorldCoasts", 0, scales::GetUpperWorldScale()), ());
|
||||
// TEST(RunTest("Belarus", scales::GetUpperWorldScale() + 1, scales::GetUpperStyleScale()), ());
|
||||
TEST(RunTest("minsk-pass", scales::GetUpperWorldScale() + 1, scales::GetUpperStyleScale()), ());
|
||||
}
|
||||
51
libs/map/mwm_tests/world_map_test.cpp
Normal file
51
libs/map/mwm_tests/world_map_test.cpp
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
#include "testing/testing.hpp"
|
||||
|
||||
#include "indexer/classificator.hpp"
|
||||
#include "indexer/classificator_loader.hpp"
|
||||
#include "indexer/data_source.hpp"
|
||||
#include "indexer/feature.hpp"
|
||||
|
||||
#include "coding/string_utf8_multilang.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
UNIT_TEST(World_Capitals)
|
||||
{
|
||||
classificator::Load();
|
||||
auto const capitalType = classif().GetTypeByPath({"place", "city", "capital", "2"});
|
||||
std::set<std::string_view> testCapitals = {"Lisbon", "Warsaw", "Kyiv", "Roseau"};
|
||||
|
||||
platform::LocalCountryFile localFile(platform::LocalCountryFile::MakeForTesting(WORLD_FILE_NAME));
|
||||
|
||||
FrozenDataSource dataSource;
|
||||
auto const res = dataSource.RegisterMap(localFile);
|
||||
TEST_EQUAL(res.second, MwmSet::RegResult::Success, ());
|
||||
|
||||
size_t capitalsCount = 0;
|
||||
|
||||
FeaturesLoaderGuard guard(dataSource, res.first);
|
||||
size_t const count = guard.GetNumFeatures();
|
||||
for (size_t id = 0; id < count; ++id)
|
||||
{
|
||||
auto ft = guard.GetFeatureByIndex(id);
|
||||
if (ft->GetGeomType() != feature::GeomType::Point)
|
||||
continue;
|
||||
|
||||
bool found = false;
|
||||
ft->ForEachType([&found, capitalType](uint32_t t)
|
||||
{
|
||||
if (t == capitalType)
|
||||
found = true;
|
||||
});
|
||||
|
||||
if (found)
|
||||
++capitalsCount;
|
||||
|
||||
std::string_view const name = ft->GetName(StringUtf8Multilang::kEnglishCode);
|
||||
if (testCapitals.count(name) > 0)
|
||||
TEST(found, (name));
|
||||
}
|
||||
|
||||
// Got 225 values from the first launch. May vary slightly ..
|
||||
TEST_GREATER_OR_EQUAL(capitalsCount, 215, ());
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue