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,28 @@
project(platform_tests)
set(SRC
country_file_tests.cpp
distance_tests.cpp
duration_tests.cpp
downloader_tests/downloader_test.cpp
downloader_utils_tests.cpp
get_text_by_id_tests.cpp
glaze_test.cpp
jansson_test.cpp
language_test.cpp
local_country_file_tests.cpp
location_test.cpp
measurement_tests.cpp
platform_test.cpp
meta_config_tests.cpp
utm_mgrs_utils_tests.cpp
)
omim_add_test(${PROJECT_NAME} ${SRC} REQUIRE_QT REQUIRE_SERVER)
target_link_libraries(${PROJECT_NAME}
platform_tests_support
platform
cppjansson
glaze::glaze
)

View file

@ -0,0 +1,33 @@
#include "testing/testing.hpp"
#include "defines.hpp"
#include "platform/country_file.hpp"
#include "platform/mwm_version.hpp"
#include <string>
namespace platform
{
UNIT_TEST(CountryFile_Smoke)
{
{
CountryFile cf("One");
TEST_EQUAL("One", cf.GetName(), ());
auto const mapFileName = cf.GetFileName(MapFileType::Map);
TEST_EQUAL("One" DATA_FILE_EXTENSION, mapFileName, ());
TEST_EQUAL(0, cf.GetRemoteSize(), ());
}
{
CountryFile cf("Three", 666, "xxxSHAxxx");
TEST_EQUAL("Three", cf.GetName(), ());
auto const mapFileName = cf.GetFileName(MapFileType::Map);
TEST_EQUAL("Three" DATA_FILE_EXTENSION, mapFileName, ());
TEST_EQUAL(666, cf.GetRemoteSize(), ());
TEST_EQUAL("xxxSHAxxx", cf.GetSha1(), ());
}
}
} // namespace platform

View file

@ -0,0 +1,365 @@
#include "testing/testing.hpp"
#include "platform/distance.hpp"
#include "platform/measurement_utils.hpp"
#include "platform/settings.hpp"
#include <algorithm>
namespace platform
{
std::string MakeDistanceStr(std::string value, Distance::Units unit)
{
static Locale const loc = GetCurrentLocale();
constexpr char kHardCodedGroupingSeparator = ',';
if (auto found = value.find(kHardCodedGroupingSeparator); found != std::string::npos)
value.replace(found, 1, loc.m_groupingSeparator);
constexpr char kHardCodedDecimalSeparator = '.';
if (auto found = value.find(kHardCodedDecimalSeparator); found != std::string::npos)
value.replace(found, 1, loc.m_decimalSeparator);
return value.append(kNarrowNonBreakingSpace).append(DebugPrint(unit));
}
struct ScopedSettings
{
/// Saves/restores previous units and sets new units for a scope.
explicit ScopedSettings(measurement_utils::Units newUnits) : m_oldUnits(measurement_utils::Units::Metric)
{
m_wasSet = settings::Get(settings::kMeasurementUnits, m_oldUnits);
settings::Set(settings::kMeasurementUnits, newUnits);
}
~ScopedSettings()
{
if (m_wasSet)
settings::Set(settings::kMeasurementUnits, m_oldUnits);
else
settings::Delete(settings::kMeasurementUnits);
}
bool m_wasSet;
measurement_utils::Units m_oldUnits;
};
UNIT_TEST(Distance_InititalDistance)
{
Distance const d;
TEST(!d.IsValid(), ());
TEST_ALMOST_EQUAL_ULPS(d.GetDistance(), -1.0, ());
TEST_EQUAL(d.GetDistanceString(), "", ());
TEST_EQUAL(d.ToString(), "", ());
}
UNIT_TEST(Distance_CreateFormatted)
{
using enum Distance::Units;
{
ScopedSettings const guard(measurement_utils::Units::Metric);
Distance const d = Distance::CreateFormatted(100);
TEST_EQUAL(d.GetUnits(), Meters, ());
TEST_ALMOST_EQUAL_ULPS(d.GetDistance(), 100.0, ());
TEST_EQUAL(d.GetDistanceString(), "100", ());
TEST_EQUAL(d.ToString(), MakeDistanceStr("100", Meters), ());
}
{
ScopedSettings const guard(measurement_utils::Units::Imperial);
Distance const d = Distance::CreateFormatted(100);
TEST_EQUAL(d.GetUnits(), Feet, ());
TEST_ALMOST_EQUAL_ULPS(d.GetDistance(), 330.0, ());
TEST_EQUAL(d.GetDistanceString(), "330", ());
TEST_EQUAL(d.ToString(), MakeDistanceStr("330", Feet), ());
}
}
UNIT_TEST(Distance_CreateAltitudeFormatted)
{
using enum Distance::Units;
{
ScopedSettings const guard(measurement_utils::Units::Metric);
TEST_EQUAL(Distance::FormatAltitude(5), MakeDistanceStr("5", Meters), ());
TEST_EQUAL(Distance::FormatAltitude(-8849), MakeDistanceStr("-8849", Meters), ());
TEST_EQUAL(Distance::FormatAltitude(12345), MakeDistanceStr("12,345", Meters), ());
}
{
ScopedSettings const guard(measurement_utils::Units::Imperial);
TEST_EQUAL(Distance::FormatAltitude(10000), MakeDistanceStr("32,808", Feet), ());
}
}
UNIT_TEST(Distance_IsLowUnits)
{
using enum Distance::Units;
TEST_EQUAL(Distance(0.0, Meters).IsLowUnits(), true, ());
TEST_EQUAL(Distance(0.0, Feet).IsLowUnits(), true, ());
TEST_EQUAL(Distance(0.0, Kilometers).IsLowUnits(), false, ());
TEST_EQUAL(Distance(0.0, Miles).IsLowUnits(), false, ());
}
UNIT_TEST(Distance_IsHighUnits)
{
using enum Distance::Units;
TEST_EQUAL(Distance(0.0, Meters).IsHighUnits(), false, ());
TEST_EQUAL(Distance(0.0, Feet).IsHighUnits(), false, ());
TEST_EQUAL(Distance(0.0, Kilometers).IsHighUnits(), true, ());
TEST_EQUAL(Distance(0.0, Miles).IsHighUnits(), true, ());
}
UNIT_TEST(Distance_To)
{
struct TestData
{
double initialDistance;
Distance::Units initialUnits;
Distance::Units to;
double newDistance;
Distance::Units newUnits;
};
using enum Distance::Units;
// clang-format off
TestData constexpr testData[] = {
{0.1, Meters, Feet, 0, Feet},
{0.3, Meters, Feet, 1, Feet},
{0.3048, Meters, Feet, 1, Feet},
{0.4573, Meters, Feet, 2, Feet},
{0.9, Meters, Feet, 3, Feet},
{3, Meters, Feet, 10, Feet},
{30.17, Meters, Feet, 99, Feet},
{30.33, Meters, Feet, 100, Feet},
{30.49, Meters, Feet, 100, Feet},
{33.5, Meters, Feet, 110, Feet},
{302, Meters, Feet, 990, Feet},
{304.7, Meters, Feet, 0.2, Miles},
{304.8, Meters, Feet, 0.2, Miles},
{402.3, Meters, Feet, 0.2, Miles},
{402.4, Meters, Feet, 0.3, Miles},
{482.8, Meters, Feet, 0.3, Miles},
{1609.3, Meters, Feet, 1.0, Miles},
{1610, Meters, Feet, 1.0, Miles},
{1770, Meters, Feet, 1.1, Miles},
{15933, Meters, Feet, 9.9, Miles},
{16093, Meters, Feet, 10, Miles},
{16093.5, Meters, Feet, 10, Miles},
{16898.464, Meters, Feet, 11, Miles},
{16898.113, Meters, Kilometers, 17, Kilometers},
{302, Meters, Miles, 990, Feet},
{994, Meters, Kilometers, 990, Meters},
{995, Meters, Kilometers, 1.0, Kilometers},
{0.1, Kilometers, Meters, 100, Meters},
{0.3, Kilometers, Kilometers, 300, Meters},
{12, Kilometers, Feet, 7.5, Miles},
{0.1, Kilometers, Feet, 330, Feet},
{110, Feet, Meters, 34, Meters},
{1100, Feet, Kilometers, 340, Meters},
{1100, Feet, Meters, 340, Meters},
{1100, Feet, Miles, 0.2, Miles},
{0.2, Miles, Meters, 320, Meters},
{11, Miles, Meters, 18, Kilometers},
{11, Miles, Kilometers, 18, Kilometers},
{0.1, Miles, Feet, 530, Feet},
};
// clang-format on
for (TestData const & data : testData)
{
Distance const formatted = Distance(data.initialDistance, data.initialUnits).To(data.to).GetFormattedDistance();
TEST_ALMOST_EQUAL_ULPS(formatted.GetDistance(), data.newDistance, (data.initialDistance));
TEST_EQUAL(formatted.GetUnits(), data.newUnits, ());
}
}
UNIT_TEST(Distance_ToPlatformUnitsFormatted)
{
using enum Distance::Units;
{
ScopedSettings const guard(measurement_utils::Units::Metric);
Distance d{11, Feet};
Distance newDistance = d.ToPlatformUnitsFormatted();
TEST_EQUAL(newDistance.GetUnits(), Meters, (d.ToString()));
TEST_ALMOST_EQUAL_ULPS(newDistance.GetDistance(), 3.0, (d.ToString()));
TEST_EQUAL(newDistance.GetDistanceString(), "3", (d.ToString()));
TEST_EQUAL(newDistance.ToString(), MakeDistanceStr("3", Meters), (d.ToString()));
d = Distance{11, Kilometers};
newDistance = d.ToPlatformUnitsFormatted();
TEST_EQUAL(newDistance.GetUnits(), Kilometers, (d.ToString()));
TEST_ALMOST_EQUAL_ULPS(newDistance.GetDistance(), 11.0, (d.ToString()));
TEST_EQUAL(newDistance.GetDistanceString(), "11", (d.ToString()));
TEST_EQUAL(newDistance.ToString(), MakeDistanceStr("11", Kilometers), (d.ToString()));
}
{
ScopedSettings const guard(measurement_utils::Units::Imperial);
Distance d{11, Feet};
Distance newDistance = d.ToPlatformUnitsFormatted();
TEST_EQUAL(newDistance.GetUnits(), Feet, (d.ToString()));
TEST_ALMOST_EQUAL_ULPS(newDistance.GetDistance(), 11.0, (d.ToString()));
TEST_EQUAL(newDistance.GetDistanceString(), "11", (d.ToString()));
TEST_EQUAL(newDistance.ToString(), MakeDistanceStr("11", Feet), (d.ToString()));
d = Distance{11, Kilometers};
newDistance = d.ToPlatformUnitsFormatted();
TEST_EQUAL(newDistance.GetUnits(), Miles, (d.ToString()));
TEST_ALMOST_EQUAL_ULPS(newDistance.GetDistance(), 6.8, (d.ToString()));
TEST_EQUAL(newDistance.GetDistanceString(), "6.8", (d.ToString()));
TEST_EQUAL(newDistance.ToString(), MakeDistanceStr("6.8", Miles), (d.ToString()));
}
}
UNIT_TEST(Distance_GetUnits)
{
using enum Distance::Units;
TEST_EQUAL(Distance(1234).GetUnits(), Meters, ());
TEST_EQUAL(Distance(1234, Kilometers).GetUnits(), Kilometers, ());
TEST_EQUAL(Distance(1234, Feet).GetUnits(), Feet, ());
TEST_EQUAL(Distance(1234, Miles).GetUnits(), Miles, ());
}
UNIT_TEST(Distance_GetUnitsString)
{
using enum Distance::Units;
TEST_EQUAL(Distance(1234).GetUnitsString(), "m", ());
TEST_EQUAL(Distance(1234, Meters).GetUnitsString(), "m", ());
TEST_EQUAL(Distance(1234, Kilometers).GetUnitsString(), "km", ());
TEST_EQUAL(Distance(1234, Feet).GetUnitsString(), "ft", ());
TEST_EQUAL(Distance(1234, Miles).GetUnitsString(), "mi", ());
}
UNIT_TEST(Distance_FormattedDistance)
{
struct TestData
{
Distance distance;
double formattedDistance;
Distance::Units formattedUnits;
std::string formattedDistanceStringInUsLocale;
};
using enum Distance::Units;
// clang-format off
TestData const testData[] = {
// From Meters to Meters
{Distance(0, Meters), 0, Meters, "0"},
{Distance(0.3, Meters), 0, Meters, "0"},
{Distance(0.9, Meters), 1, Meters, "1"},
{Distance(1, Meters), 1, Meters, "1"},
{Distance(1.234, Meters), 1, Meters, "1"},
{Distance(9.99, Meters), 10, Meters, "10"},
{Distance(10.01, Meters), 10, Meters, "10"},
{Distance(10.4, Meters), 10, Meters, "10"},
{Distance(10.5, Meters), 11, Meters, "11"},
{Distance(10.51, Meters), 11, Meters, "11"},
{Distance(64.2, Meters), 64, Meters, "64"},
{Distance(99, Meters), 99, Meters, "99"},
{Distance(100, Meters), 100, Meters, "100"},
{Distance(101, Meters), 100, Meters, "100"},
{Distance(109, Meters), 110, Meters, "110"},
{Distance(991, Meters), 990, Meters, "990"},
// From Kilometers to Kilometers
{Distance(0, Kilometers), 0, Meters, "0"},
{Distance(0.3, Kilometers), 300, Meters, "300"},
{Distance(1.234, Kilometers), 1.2, Kilometers, "1.2"},
{Distance(10, Kilometers), 10, Kilometers, "10"},
{Distance(11, Kilometers), 11, Kilometers, "11"},
{Distance(54, Kilometers), 54, Kilometers, "54"},
{Distance(99.99, Kilometers), 100, Kilometers, "100"},
{Distance(100.01, Kilometers), 100, Kilometers, "100"},
{Distance(115, Kilometers), 115, Kilometers, "115"},
{Distance(999, Kilometers), 999, Kilometers, "999"},
{Distance(1000, Kilometers), 1000, Kilometers, "1000"},
{Distance(1049.99, Kilometers), 1050, Kilometers, "1050"},
{Distance(1050, Kilometers), 1050, Kilometers, "1050"},
{Distance(1050.01, Kilometers), 1050, Kilometers, "1050"},
{Distance(1234, Kilometers), 1234, Kilometers, "1234"},
{Distance(12345, Kilometers), 12345, Kilometers, "12,345"},
// From Feet to Feet
{Distance(0, Feet), 0, Feet, "0"},
{Distance(1, Feet), 1, Feet, "1"},
{Distance(9.99, Feet), 10, Feet, "10"},
{Distance(10.01, Feet), 10, Feet, "10"},
{Distance(95, Feet), 95, Feet, "95"},
{Distance(125, Feet), 130, Feet, "130"},
{Distance(991, Feet), 990, Feet, "990"},
// From Miles to Miles
{Distance(0, Miles), 0, Feet, "0"},
{Distance(0.1, Miles), 530, Feet, "530"},
{Distance(1, Miles), 1.0, Miles, "1.0"},
{Distance(1.234, Miles), 1.2, Miles, "1.2"},
{Distance(9.99, Miles), 10, Miles, "10"},
{Distance(10.01, Miles), 10, Miles, "10"},
{Distance(11, Miles), 11, Miles, "11"},
{Distance(54, Miles), 54, Miles, "54"},
{Distance(145, Miles), 145, Miles, "145"},
{Distance(999, Miles), 999, Miles, "999"},
{Distance(1149.99, Miles), 1150, Miles, "1150"},
{Distance(1150, Miles), 1150, Miles, "1150"},
{Distance(1150.01, Miles), 1150, Miles, "1150"},
{Distance(12345.0, Miles), 12345, Miles, "12,345"},
// From Meters to Kilometers
{Distance(999, Meters), 1.0, Kilometers, "1.0"},
{Distance(1000, Meters), 1.0, Kilometers, "1.0"},
{Distance(1001, Meters), 1.0, Kilometers, "1.0"},
{Distance(1100, Meters), 1.1, Kilometers, "1.1"},
{Distance(1140, Meters), 1.1, Kilometers, "1.1"},
{Distance(1151, Meters), 1.2, Kilometers, "1.2"},
{Distance(1500, Meters), 1.5, Kilometers, "1.5"},
{Distance(1549.9, Meters), 1.5, Kilometers, "1.5"},
{Distance(1550, Meters), 1.6, Kilometers, "1.6"},
{Distance(1551, Meters), 1.6, Kilometers, "1.6"},
{Distance(9949, Meters), 9.9, Kilometers, "9.9"},
{Distance(9992, Meters), 10, Kilometers, "10"},
{Distance(10000, Meters), 10, Kilometers, "10"},
{Distance(10499.9, Meters), 10, Kilometers, "10"},
{Distance(10501, Meters), 11, Kilometers, "11"},
{Distance(101'001, Meters), 101, Kilometers, "101"},
{Distance(101'999, Meters), 102, Kilometers, "102"},
{Distance(287'386, Meters), 287, Kilometers, "287"},
// From Feet to Miles
{Distance(999, Feet), 0.2, Miles, "0.2"},
{Distance(1000, Feet), 0.2, Miles, "0.2"},
{Distance(1150, Feet), 0.2, Miles, "0.2"},
{Distance(5280, Feet), 1.0, Miles, "1.0"},
{Distance(7920, Feet), 1.5, Miles, "1.5"},
{Distance(10560, Feet), 2.0, Miles, "2.0"},
{Distance(100'000, Feet), 19, Miles, "19"},
{Distance(285'120, Feet), 54, Miles, "54"},
{Distance(633'547, Feet), 120, Miles, "120"},
{Distance(633'600, Feet), 120, Miles, "120"},
{Distance(633'653, Feet), 120, Miles, "120"},
{Distance(999'999, Feet), 189, Miles, "189"},
};
// clang-format on
for (auto const & [distance, formattedDistance, formattedUnits, formattedDistanceStringInUsLocale] : testData)
{
Distance const formatted = distance.GetFormattedDistance();
// Run two times to verify that nothing breaks after second format
for (auto const & d : {formatted, formatted.GetFormattedDistance()})
{
TEST_ALMOST_EQUAL_ULPS(d.GetDistance(), formattedDistance, (distance));
TEST_EQUAL(d.GetUnits(), formattedUnits, (distance));
auto const formattedString = MakeDistanceStr(formattedDistanceStringInUsLocale, formattedUnits);
TEST_EQUAL(d.ToString(), formattedString, (distance));
}
}
}
} // namespace platform

View file

@ -0,0 +1,613 @@
#include "testing/testing.hpp"
#include "platform/chunks_download_strategy.hpp"
#include "platform/http_request.hpp"
#include "platform/platform.hpp"
#include "coding/file_reader.hpp"
#include "coding/file_writer.hpp"
#include "coding/internal/file_data.hpp"
#include "base/logging.hpp"
#include "base/std_serialization.hpp"
#include <QtCore/QCoreApplication>
#include <functional>
#include <memory>
#include <vector>
#include "defines.hpp"
namespace downloader_test
{
using namespace downloader;
using namespace std::placeholders;
using std::bind, std::string, std::vector;
char constexpr kTestUrl1[] = "http://localhost:34568/unit_tests/1.txt";
char constexpr kTestUrl404[] = "http://localhost:34568/unit_tests/notexisting_unittest";
char constexpr kTestUrlBigFile[] = "http://localhost:34568/unit_tests/47kb.file";
// Should match file size in tools/python/ResponseProvider.py
int constexpr kBigFileSize = 47684;
class DownloadObserver
{
bool m_progressWasCalled;
// Chunked downloads can return one status per chunk (thread).
vector<DownloadStatus> m_statuses;
// Interrupt download after this number of chunks
int m_chunksToFail;
base::ScopedLogLevelChanger const m_debugLogLevel;
public:
DownloadObserver() : m_chunksToFail(-1), m_debugLogLevel(LDEBUG) { Reset(); }
void CancelDownloadOnGivenChunk(int chunksToFail) { m_chunksToFail = chunksToFail; }
void Reset()
{
m_progressWasCalled = false;
m_statuses.clear();
}
void TestOk()
{
TEST_NOT_EQUAL(0, m_statuses.size(), ("Observer was not called."));
TEST(m_progressWasCalled, ("Download progress wasn't called"));
for (auto const & status : m_statuses)
TEST_EQUAL(status, DownloadStatus::Completed, ());
}
void TestFailed()
{
TEST_NOT_EQUAL(0, m_statuses.size(), ("Observer was not called."));
for (auto const & status : m_statuses)
TEST_EQUAL(status, DownloadStatus::Failed, ());
}
void TestFileNotFound()
{
TEST_NOT_EQUAL(0, m_statuses.size(), ("Observer was not called."));
for (auto const & status : m_statuses)
TEST_EQUAL(status, DownloadStatus::FileNotFound, ());
}
void OnDownloadProgress(HttpRequest & request)
{
m_progressWasCalled = true;
TEST_EQUAL(request.GetStatus(), DownloadStatus::InProgress, ());
// Cancel download if needed
if (m_chunksToFail != -1)
{
--m_chunksToFail;
if (m_chunksToFail == 0)
{
m_chunksToFail = -1;
LOG(LINFO, ("Download canceled"));
QCoreApplication::quit();
}
}
}
virtual void OnDownloadFinish(HttpRequest & request)
{
auto const status = request.GetStatus();
m_statuses.emplace_back(status);
TEST(status != DownloadStatus::InProgress, ());
QCoreApplication::quit();
}
};
struct CancelDownload
{
void OnProgress(HttpRequest & request)
{
TEST_GREATER(request.GetData().size(), 0, ());
delete &request;
QCoreApplication::quit();
}
void OnFinish(HttpRequest &) { TEST(false, ("Should be never called")); }
};
struct DeleteOnFinish
{
void OnProgress(HttpRequest & request) { TEST_GREATER(request.GetData().size(), 0, ()); }
void OnFinish(HttpRequest & request)
{
delete &request;
QCoreApplication::quit();
}
};
UNIT_TEST(DownloaderSimpleGet)
{
DownloadObserver observer;
auto const MakeRequest = [&observer](string const & url)
{
return HttpRequest::Get(url, bind(&DownloadObserver::OnDownloadFinish, &observer, _1),
bind(&DownloadObserver::OnDownloadProgress, &observer, _1));
};
{
// simple success case
std::unique_ptr<HttpRequest> const request{MakeRequest(kTestUrl1)};
// wait until download is finished
QCoreApplication::exec();
observer.TestOk();
TEST_EQUAL(request->GetData(), "Test1", ());
}
observer.Reset();
{
// We DO NOT SUPPORT redirects to avoid data corruption when downloading mwm files
std::unique_ptr<HttpRequest> const request{MakeRequest("http://localhost:34568/unit_tests/permanent")};
QCoreApplication::exec();
observer.TestFailed();
TEST_EQUAL(request->GetData().size(), 0, (request->GetData()));
}
observer.Reset();
{
// fail case 404
std::unique_ptr<HttpRequest> const request{MakeRequest(kTestUrl404)};
QCoreApplication::exec();
observer.TestFileNotFound();
TEST_EQUAL(request->GetData().size(), 0, (request->GetData()));
}
observer.Reset();
{
// fail case not existing host
std::unique_ptr<HttpRequest> const request{MakeRequest("http://not-valid-host123532.ath.cx")};
QCoreApplication::exec();
observer.TestFailed();
TEST_EQUAL(request->GetData().size(), 0, (request->GetData()));
}
{
// cancel download in the middle of the progress
CancelDownload canceler;
// should be deleted in canceler
HttpRequest::Get(kTestUrlBigFile, bind(&CancelDownload::OnFinish, &canceler, _1),
bind(&CancelDownload::OnProgress, &canceler, _1));
QCoreApplication::exec();
}
observer.Reset();
{
// https success case
std::unique_ptr<HttpRequest> const request{MakeRequest("https://github.com")};
// wait until download is finished
QCoreApplication::exec();
observer.TestOk();
TEST_GREATER(request->GetData().size(), 0, ());
}
{
// Delete request at the end of successful download
DeleteOnFinish deleter;
// should be deleted in deleter on finish
HttpRequest::Get(kTestUrl1, bind(&DeleteOnFinish::OnFinish, &deleter, _1),
bind(&DeleteOnFinish::OnProgress, &deleter, _1));
QCoreApplication::exec();
}
}
// TODO: This test sometimes fails on CI. Reasons are unknown.
#ifndef OMIM_OS_MAC
UNIT_TEST(DownloaderSimplePost)
{
// simple success case
string const postData = "{\"jsonKey\":\"jsonValue\"}";
DownloadObserver observer;
std::unique_ptr<HttpRequest> const request{HttpRequest::PostJson(
"http://localhost:34568/unit_tests/post.php", postData, bind(&DownloadObserver::OnDownloadFinish, &observer, _1),
bind(&DownloadObserver::OnDownloadProgress, &observer, _1))};
// wait until download is finished
QCoreApplication::exec();
observer.TestOk();
TEST_EQUAL(request->GetData(), postData, ());
}
#endif
UNIT_TEST(ChunksDownloadStrategy)
{
vector<string> const servers = {"UrlOfServer1", "UrlOfServer2", "UrlOfServer3"};
typedef std::pair<int64_t, int64_t> RangeT;
RangeT const R1{0, 249}, R2{250, 499}, R3{500, 749}, R4{750, 800};
int64_t constexpr kFileSize = 800;
int64_t constexpr kChunkSize = 250;
ChunksDownloadStrategy strategy(servers);
strategy.InitChunks(kFileSize, kChunkSize);
string s1;
RangeT r1;
TEST_EQUAL(strategy.NextChunk(s1, r1), ChunksDownloadStrategy::ENextChunk, ());
string s2;
RangeT r2;
TEST_EQUAL(strategy.NextChunk(s2, r2), ChunksDownloadStrategy::ENextChunk, ());
string s3;
RangeT r3;
TEST_EQUAL(strategy.NextChunk(s3, r3), ChunksDownloadStrategy::ENextChunk, ());
string sEmpty;
RangeT rEmpty;
TEST_EQUAL(strategy.NextChunk(sEmpty, rEmpty), ChunksDownloadStrategy::ENoFreeServers, ());
TEST_EQUAL(strategy.NextChunk(sEmpty, rEmpty), ChunksDownloadStrategy::ENoFreeServers, ());
TEST(s1 != s2 && s2 != s3 && s3 != s1, (s1, s2, s3));
TEST(r1 != r2 && r2 != r3 && r3 != r1, (r1, r2, r3));
TEST(r1 == R1 || r1 == R2 || r1 == R3 || r1 == R4, (r1));
TEST(r2 == R1 || r2 == R2 || r2 == R3 || r2 == R4, (r2));
TEST(r3 == R1 || r3 == R2 || r3 == R3 || r3 == R4, (r3));
strategy.ChunkFinished(true, r1);
string s4;
RangeT r4;
TEST_EQUAL(strategy.NextChunk(s4, r4), ChunksDownloadStrategy::ENextChunk, ());
TEST_EQUAL(s4, s1, ());
TEST(r4 != r1 && r4 != r2 && r4 != r3, (r4));
TEST_EQUAL(strategy.NextChunk(sEmpty, rEmpty), ChunksDownloadStrategy::ENoFreeServers, ());
TEST_EQUAL(strategy.NextChunk(sEmpty, rEmpty), ChunksDownloadStrategy::ENoFreeServers, ());
strategy.ChunkFinished(false, r2);
TEST_EQUAL(strategy.NextChunk(sEmpty, rEmpty), ChunksDownloadStrategy::ENoFreeServers, ());
strategy.ChunkFinished(true, r4);
string s5;
RangeT r5;
TEST_EQUAL(strategy.NextChunk(s5, r5), ChunksDownloadStrategy::ENextChunk, ());
TEST_EQUAL(s5, s4, (s5, s4));
TEST_EQUAL(r5, r2, ());
TEST_EQUAL(strategy.NextChunk(sEmpty, rEmpty), ChunksDownloadStrategy::ENoFreeServers, ());
TEST_EQUAL(strategy.NextChunk(sEmpty, rEmpty), ChunksDownloadStrategy::ENoFreeServers, ());
strategy.ChunkFinished(true, r5);
// 3rd is still alive here
TEST_EQUAL(strategy.NextChunk(sEmpty, rEmpty), ChunksDownloadStrategy::ENoFreeServers, ());
TEST_EQUAL(strategy.NextChunk(sEmpty, rEmpty), ChunksDownloadStrategy::ENoFreeServers, ());
strategy.ChunkFinished(true, r3);
TEST_EQUAL(strategy.NextChunk(sEmpty, rEmpty), ChunksDownloadStrategy::EDownloadSucceeded, ());
TEST_EQUAL(strategy.NextChunk(sEmpty, rEmpty), ChunksDownloadStrategy::EDownloadSucceeded, ());
}
UNIT_TEST(ChunksDownloadStrategyFAIL)
{
vector<string> const servers = {"UrlOfServer1", "UrlOfServer2"};
typedef std::pair<int64_t, int64_t> RangeT;
int64_t constexpr kFileSize = 800;
int64_t constexpr kChunkSize = 250;
ChunksDownloadStrategy strategy(servers);
strategy.InitChunks(kFileSize, kChunkSize);
string s1;
RangeT r1;
TEST_EQUAL(strategy.NextChunk(s1, r1), ChunksDownloadStrategy::ENextChunk, ());
string s2;
RangeT r2;
TEST_EQUAL(strategy.NextChunk(s2, r2), ChunksDownloadStrategy::ENextChunk, ());
TEST_EQUAL(strategy.NextChunk(s2, r2), ChunksDownloadStrategy::ENoFreeServers, ());
strategy.ChunkFinished(false, r1);
TEST_EQUAL(strategy.NextChunk(s2, r2), ChunksDownloadStrategy::ENoFreeServers, ());
strategy.ChunkFinished(false, r2);
TEST_EQUAL(strategy.NextChunk(s2, r2), ChunksDownloadStrategy::EDownloadFailed, ());
}
UNIT_TEST(ChunksDownloadStrategyDynamicChunks)
{
vector<string> const servers = {"UrlOfServer1", "UrlOfServer2"};
typedef std::pair<int64_t, int64_t> RangeT;
string url;
ChunksDownloadStrategy strategy(servers);
// Small 1MB file - one chunk
strategy.InitChunks(1024 * 1024, 0);
RangeT const R11{0, 1024 * 1024 - 1};
RangeT r1;
TEST_EQUAL(strategy.NextChunk(url, r1), ChunksDownloadStrategy::ENextChunk, ());
RangeT rEmpty;
TEST_EQUAL(strategy.NextChunk(url, rEmpty), ChunksDownloadStrategy::ENoFreeServers, ());
TEST(r1 == R11, (r1));
// Small 1MB+1b file - 2 chunks
strategy = ChunksDownloadStrategy(servers);
strategy.InitChunks(1024 * 1024 + 1, 0);
RangeT const R21{0, 1024 * 1024 - 1}, R22{1024 * 1024, 1024 * 1024};
TEST_EQUAL(strategy.NextChunk(url, r1), ChunksDownloadStrategy::ENextChunk, ());
RangeT r2;
TEST_EQUAL(strategy.NextChunk(url, r2), ChunksDownloadStrategy::ENextChunk, ());
TEST_EQUAL(strategy.NextChunk(url, rEmpty), ChunksDownloadStrategy::ENoFreeServers, ());
TEST(r1 == R21 && r2 == R22, (r1, r2));
// Big 200MB file - 5MB chunks
strategy = ChunksDownloadStrategy(servers);
strategy.InitChunks(200 * 1024 * 1024, 0);
RangeT const R31{0, 5 * 1024 * 1024 - 1}, R32{5 * 1024 * 1024, 2 * 5 * 1024 * 1024 - 1};
TEST_EQUAL(strategy.NextChunk(url, r1), ChunksDownloadStrategy::ENextChunk, ());
TEST_EQUAL(strategy.NextChunk(url, r2), ChunksDownloadStrategy::ENextChunk, ());
TEST(r1 == R31 && r2 == R32, (r1, r2));
}
namespace
{
string ReadFileAsString(string const & file)
{
try
{
FileReader f(file);
string s;
f.ReadAsString(s);
return s;
}
catch (FileReader::Exception const &)
{
TEST(false, ("File ", file, " should exist"));
return {};
}
} // namespace
void FinishDownloadSuccess(string const & file)
{
TEST(base::DeleteFileX(file), ("Result file should present on success"));
uint64_t size;
TEST(!base::GetFileSize(file + DOWNLOADING_FILE_EXTENSION, size), ("No downloading file on success"));
TEST(!base::GetFileSize(file + RESUME_FILE_EXTENSION, size), ("No resume file on success"));
}
void FinishDownloadFail(string const & file)
{
uint64_t size;
TEST(!base::GetFileSize(file, size), ("No result file on fail"));
(void)base::DeleteFileX(file + DOWNLOADING_FILE_EXTENSION);
TEST(base::DeleteFileX(file + RESUME_FILE_EXTENSION), ("Resume file should present on fail"));
}
void DeleteTempDownloadFiles()
{
// Remove data from previously failed files.
// Get regexp like this: (\.downloading3$|\.resume3$)
string const regexp = "(\\" RESUME_FILE_EXTENSION "$|\\" DOWNLOADING_FILE_EXTENSION "$)";
Platform::FilesList files;
Platform::GetFilesByRegExp(".", regexp, files);
for (Platform::FilesList::iterator it = files.begin(); it != files.end(); ++it)
FileWriter::DeleteFileX(*it);
}
} // namespace
UNIT_TEST(DownloadChunks)
{
string const kFileName = "some_downloader_test_file";
// remove data from previously failed files
DeleteTempDownloadFiles();
vector<string> urls = {kTestUrl1, kTestUrl1};
int64_t fileSize = 5;
DownloadObserver observer;
auto const MakeRequest = [&](int64_t chunkSize)
{
return HttpRequest::GetFile(urls, kFileName, fileSize, bind(&DownloadObserver::OnDownloadFinish, &observer, _1),
bind(&DownloadObserver::OnDownloadProgress, &observer, _1), chunkSize);
};
{
// should use only one thread
std::unique_ptr<HttpRequest> const request{MakeRequest(512 * 1024)};
// wait until download is finished
QCoreApplication::exec();
observer.TestOk();
TEST_EQUAL(request->GetData(), kFileName, ());
TEST_EQUAL(ReadFileAsString(kFileName), "Test1", ());
FinishDownloadSuccess(kFileName);
}
observer.Reset();
urls = {kTestUrlBigFile, kTestUrlBigFile, kTestUrlBigFile};
fileSize = 5;
{
// 3 threads - fail, because of invalid size
[[maybe_unused]] std::unique_ptr<HttpRequest> const request{MakeRequest(2048)};
// wait until download is finished
QCoreApplication::exec();
observer.TestFailed();
FinishDownloadFail(kFileName);
}
observer.Reset();
urls = {kTestUrlBigFile, kTestUrlBigFile, kTestUrlBigFile};
fileSize = kBigFileSize;
{
// 3 threads - succeeded
[[maybe_unused]] std::unique_ptr<HttpRequest> const request{MakeRequest(2048)};
// wait until download is finished
QCoreApplication::exec();
observer.TestOk();
FinishDownloadSuccess(kFileName);
}
observer.Reset();
urls = {kTestUrlBigFile, kTestUrl1, kTestUrl404};
fileSize = kBigFileSize;
{
// 3 threads with only one valid url - succeeded
[[maybe_unused]] std::unique_ptr<HttpRequest> const request{MakeRequest(2048)};
// wait until download is finished
QCoreApplication::exec();
observer.TestOk();
FinishDownloadSuccess(kFileName);
}
observer.Reset();
urls = {kTestUrlBigFile, kTestUrlBigFile};
fileSize = 12345;
{
// 2 threads and all points to file with invalid size - fail
[[maybe_unused]] std::unique_ptr<HttpRequest> const request{MakeRequest(2048)};
// wait until download is finished
QCoreApplication::exec();
observer.TestFailed();
FinishDownloadFail(kFileName);
}
}
namespace
{
int64_t constexpr beg1 = 123, end1 = 1230, beg2 = 44000, end2 = 47683;
struct ResumeChecker
{
size_t m_counter;
ResumeChecker() : m_counter(0) {}
void OnProgress(HttpRequest & request)
{
if (m_counter == 0)
{
TEST_EQUAL(request.GetProgress().m_bytesDownloaded, beg2, ());
TEST_EQUAL(request.GetProgress().m_bytesTotal, kBigFileSize, ());
}
else if (m_counter == 1)
{
TEST_EQUAL(request.GetProgress().m_bytesDownloaded, kBigFileSize, ());
TEST_EQUAL(request.GetProgress().m_bytesTotal, kBigFileSize, ());
}
else
{
TEST(false, ("Progress should be called exactly 2 times"));
}
++m_counter;
}
void OnFinish(HttpRequest & request)
{
TEST_EQUAL(request.GetStatus(), DownloadStatus::Completed, ());
QCoreApplication::exit();
}
};
} // namespace
UNIT_TEST(DownloadResumeChunks)
{
string const FILENAME = "some_test_filename_12345";
string const RESUME_FILENAME = FILENAME + RESUME_FILE_EXTENSION;
string const DOWNLOADING_FILENAME = FILENAME + DOWNLOADING_FILE_EXTENSION;
// remove data from previously failed files
DeleteTempDownloadFiles();
vector<string> urls = {kTestUrlBigFile};
// 1st step - download full file
{
DownloadObserver observer;
std::unique_ptr<HttpRequest> const request(
HttpRequest::GetFile(urls, FILENAME, kBigFileSize, bind(&DownloadObserver::OnDownloadFinish, &observer, _1),
bind(&DownloadObserver::OnDownloadProgress, &observer, _1)));
QCoreApplication::exec();
observer.TestOk();
uint64_t size;
TEST(!base::GetFileSize(RESUME_FILENAME, size), ("No resume file on success"));
}
// 2nd step - mark some file blocks as not downloaded
{
// to substitute temporary not fully downloaded file
TEST(base::RenameFileX(FILENAME, DOWNLOADING_FILENAME), ());
FileWriter f(DOWNLOADING_FILENAME, FileWriter::OP_WRITE_EXISTING);
f.Seek(beg1);
char b1[end1 - beg1 + 1] = {0};
f.Write(b1, ARRAY_SIZE(b1));
f.Seek(beg2);
char b2[end2 - beg2 + 1] = {0};
f.Write(b2, ARRAY_SIZE(b2));
ChunksDownloadStrategy strategy((vector<string>()));
strategy.AddChunk(std::make_pair(int64_t(0), beg1 - 1), ChunksDownloadStrategy::CHUNK_COMPLETE);
strategy.AddChunk(std::make_pair(beg1, end1), ChunksDownloadStrategy::CHUNK_FREE);
strategy.AddChunk(std::make_pair(end1 + 1, beg2 - 1), ChunksDownloadStrategy::CHUNK_COMPLETE);
strategy.AddChunk(std::make_pair(beg2, end2), ChunksDownloadStrategy::CHUNK_FREE);
strategy.SaveChunks(kBigFileSize, RESUME_FILENAME);
}
// 3rd step - check that resume works
{
ResumeChecker checker;
std::unique_ptr<HttpRequest> const request(HttpRequest::GetFile(urls, FILENAME, kBigFileSize,
bind(&ResumeChecker::OnFinish, &checker, _1),
bind(&ResumeChecker::OnProgress, &checker, _1)));
QCoreApplication::exec();
FinishDownloadSuccess(FILENAME);
}
}
// Unit test with forcible canceling of http request
UNIT_TEST(DownloadResumeChunksWithCancel)
{
string const FILENAME = "some_test_filename_12345";
// remove data from previously failed files
DeleteTempDownloadFiles();
vector<string> urls = {kTestUrlBigFile};
DownloadObserver observer;
int arrCancelChunks[] = {1, 3, 10, 15, 20, 0};
for (size_t i = 0; i < ARRAY_SIZE(arrCancelChunks); ++i)
{
if (arrCancelChunks[i] > 0)
observer.CancelDownloadOnGivenChunk(arrCancelChunks[i]);
std::unique_ptr<HttpRequest> const request(
HttpRequest::GetFile(urls, FILENAME, kBigFileSize, bind(&DownloadObserver::OnDownloadFinish, &observer, _1),
bind(&DownloadObserver::OnDownloadProgress, &observer, _1), 1024, false));
QCoreApplication::exec();
}
observer.TestOk();
FinishDownloadSuccess(FILENAME);
}
} // namespace downloader_test

View file

@ -0,0 +1,125 @@
#include "testing/testing.hpp"
#include "platform/downloader_utils.hpp"
#include "platform/local_country_file_utils.hpp"
#include "platform/mwm_version.hpp"
#include "platform/platform.hpp"
#include "platform/servers_list.hpp"
#include "base/file_name_utils.hpp"
UNIT_TEST(Downloader_GetFilePathByUrl)
{
{
std::string const mwmName = "Luna";
std::string const fileName = platform::GetFileName(mwmName, MapFileType::Map);
int64_t const dataVersion = version::FOR_TESTING_MWM1;
int64_t const diffVersion = 0;
MapFileType const fileType = MapFileType::Map;
auto const path = platform::GetFileDownloadPath(dataVersion, mwmName, fileType);
auto const url = downloader::GetFileDownloadUrl(fileName, dataVersion, diffVersion);
auto const resultPath = downloader::GetFilePathByUrl(url);
TEST_EQUAL(path, resultPath, ());
}
{
std::string const mwmName = "Luna";
std::string const fileName = platform::GetFileName(mwmName, MapFileType::Diff);
int64_t const dataVersion = version::FOR_TESTING_MWM2;
int64_t const diffVersion = version::FOR_TESTING_MWM1;
MapFileType const fileType = MapFileType::Diff;
auto const path = platform::GetFileDownloadPath(dataVersion, mwmName, fileType);
auto const url = downloader::GetFileDownloadUrl(fileName, dataVersion, diffVersion);
auto const resultPath = downloader::GetFilePathByUrl(url);
TEST_EQUAL(path, resultPath, ());
}
TEST_EQUAL(downloader::GetFilePathByUrl("/maps/220314/Belarus_Brest Region.mwm"),
base::JoinPath(GetPlatform().WritableDir(), "220314/Belarus_Brest Region.mwm.ready"), ());
}
UNIT_TEST(Downloader_IsUrlSupported)
{
std::string const mwmName = "Luna";
std::string fileName = platform::GetFileName(mwmName, MapFileType::Map);
int64_t dataVersion = version::FOR_TESTING_MWM1;
int64_t diffVersion = 0;
auto url = downloader::GetFileDownloadUrl(fileName, dataVersion, diffVersion);
TEST(downloader::IsUrlSupported(url), ());
TEST(downloader::IsUrlSupported("maps/991215/Luna.mwm"), ());
TEST(downloader::IsUrlSupported("maps/0/Luna.mwm"), ());
TEST(!downloader::IsUrlSupported("maps/x/Luna.mwm"), ());
TEST(!downloader::IsUrlSupported("macarena/0/Luna.mwm"), ());
TEST(!downloader::IsUrlSupported("/hack/maps/0/Luna.mwm"), ());
TEST(!downloader::IsUrlSupported("0/Luna.mwm"), ());
TEST(!downloader::IsUrlSupported("maps/0/Luna"), ());
TEST(!downloader::IsUrlSupported("0/Luna.mwm"), ());
TEST(!downloader::IsUrlSupported("Luna.mwm"), ());
TEST(!downloader::IsUrlSupported("Luna"), ());
fileName = platform::GetFileName(mwmName, MapFileType::Diff);
diffVersion = version::FOR_TESTING_MWM1;
url = downloader::GetFileDownloadUrl(fileName, dataVersion, diffVersion);
TEST(downloader::IsUrlSupported(url), ());
TEST(downloader::IsUrlSupported("diffs/991215/991215/Luna.mwmdiff"), ());
TEST(downloader::IsUrlSupported("diffs/0/0/Luna.mwmdiff"), ());
TEST(!downloader::IsUrlSupported("diffs/x/0/Luna.mwmdiff"), ());
TEST(!downloader::IsUrlSupported("diffs/0/x/Luna.mwmdiff"), ());
TEST(!downloader::IsUrlSupported("diffs/x/x/Luna.mwmdiff"), ());
TEST(!downloader::IsUrlSupported("beefs/0/0/Luna.mwmdiff"), ());
TEST(!downloader::IsUrlSupported("diffs/0/0/Luna.mwmdiff.f"), ());
TEST(!downloader::IsUrlSupported("maps/diffs/0/0/Luna.mwmdiff"), ());
TEST(!downloader::IsUrlSupported("diffs/0/0/Luna"), ());
TEST(!downloader::IsUrlSupported("0/0/Luna.mwmdiff"), ());
TEST(!downloader::IsUrlSupported("diffs/0/Luna.mwmdiff"), ());
TEST(!downloader::IsUrlSupported("diffs/0"), ());
TEST(!downloader::IsUrlSupported("diffs/0/Luna.mwmdiff"), ());
TEST(!downloader::IsUrlSupported("diffs/Luna.mwmdiff"), ());
TEST(!downloader::IsUrlSupported("Luna.mwmdiff"), ());
TEST(!downloader::IsUrlSupported("Luna"), ());
}
UNIT_TEST(Downloader_ParseMetaConfig)
{
std::optional<downloader::MetaConfig> cfg;
TEST((cfg = downloader::ParseMetaConfig(R"([ "https://url1/", "https://url2/" ])")), ());
TEST_EQUAL(cfg->m_serversList.size(), 2, ());
TEST_EQUAL(cfg->m_serversList[0], "https://url1/", ());
TEST_EQUAL(cfg->m_serversList[1], "https://url2/", ());
TEST((cfg = downloader::ParseMetaConfig(R"(
{
"servers": [ "https://url1/", "https://url2/" ],
"settings": {
"DonateUrl": "value1",
"NY": "value2",
"key3": "value3"
}
}
)")),
());
TEST_EQUAL(cfg->m_serversList.size(), 2, ());
TEST_EQUAL(cfg->m_serversList[0], "https://url1/", ());
TEST_EQUAL(cfg->m_serversList[1], "https://url2/", ());
TEST_EQUAL(cfg->m_settings.size(), 2, ()); // "key3" is ignored
TEST_EQUAL(cfg->m_settings["DonateUrl"], "value1", ());
TEST_EQUAL(cfg->m_settings["NY"], "value2", ());
TEST(!downloader::ParseMetaConfig(R"(broken json)"), ());
TEST(!downloader::ParseMetaConfig(R"([])"), ());
TEST(!downloader::ParseMetaConfig(R"({})"), ());
TEST(!downloader::ParseMetaConfig(R"({"no_servers": "invalid"})"), ());
TEST(!downloader::ParseMetaConfig(R"({"servers": "invalid"})"), ());
}

View file

@ -0,0 +1,118 @@
#include "testing/testing.hpp"
#include "platform/duration.hpp"
#include <chrono>
namespace platform
{
using std::chrono::duration_cast, std::chrono::seconds, std::chrono::minutes, std::chrono::hours, std::chrono::days;
struct TestData
{
struct Duration
{
days m_days;
hours m_hours;
minutes m_minutes;
seconds m_seconds;
std::string result;
Duration(long days, long hours, long minutes, long seconds, std::string const & result)
: m_days(days)
, m_hours(hours)
, m_minutes(minutes)
, m_seconds(seconds)
, result(result)
{}
long Seconds() const
{
return (duration_cast<seconds>(m_days) + duration_cast<seconds>(m_hours) + duration_cast<seconds>(m_minutes) +
m_seconds)
.count();
}
};
Locale m_locale;
std::vector<Duration> m_duration;
constexpr TestData(Locale locale, std::vector<Duration> duration) : m_locale(locale), m_duration(duration) {}
};
Locale GetLocale(std::string const & language)
{
Locale locale;
locale.m_language = language;
return locale;
}
/*
Localized string cannot be retrieved from the app target bundle during the tests execution
and the platform::GetLocalizedString will return the same string as the input ("minute", "hour" etc).
This is why the expectation strings are not explicit.
*/
auto const m = Duration::GetUnitsString(Duration::Units::Minutes);
auto const h = Duration::GetUnitsString(Duration::Units::Hours);
auto const d = Duration::GetUnitsString(Duration::Units::Days);
UNIT_TEST(Duration_AllUnits)
{
TestData const testData[] = {
{GetLocale("en"),
{{0, 0, 0, 0, "0" + m},
{0, 0, 0, 30, "0" + m},
{0, 0, 0, 59, "0" + m},
{0, 0, 1, 0, "1" + m},
{0, 0, 1, 59, "1" + m},
{0, 0, 60, 0, "1" + h},
{0, 0, 123, 0, "2" + h + kNonBreakingSpace + "3" + m},
{0, 3, 0, 0, "3" + h},
{0, 24, 0, 0, "1" + d},
{4, 0, 0, 0, "4" + d},
{1, 2, 3, 0, "1" + d + kNonBreakingSpace + "2" + h + kNonBreakingSpace + "3" + m},
{1, 0, 15, 0, "1" + d + kNonBreakingSpace + "15" + m},
{0, 15, 1, 0, "15" + h + kNonBreakingSpace + "1" + m},
{1, 15, 0, 0, "1" + d + kNonBreakingSpace + "15" + h},
{15, 0, 10, 0, "15" + d + kNonBreakingSpace + "10" + m},
{15, 15, 15, 0, "15" + d + kNonBreakingSpace + "15" + h + kNonBreakingSpace + "15" + m}}},
};
for (auto const & data : testData)
{
for (auto const & dataDuration : data.m_duration)
{
auto const duration = Duration(dataDuration.Seconds());
auto durationStr = duration.GetLocalizedString(
{Duration::Units::Days, Duration::Units::Hours, Duration::Units::Minutes}, data.m_locale);
TEST_EQUAL(durationStr, dataDuration.result, ());
}
}
}
UNIT_TEST(Duration_Localization)
{
TestData const testData[] = {
// en
{GetLocale("en"), {{1, 2, 3, 0, "1" + d + kNonBreakingSpace + "2" + h + kNonBreakingSpace + "3" + m}}},
// ru (narrow spacing between number and unit)
{GetLocale("ru"),
{{1, 2, 3, 0,
"1" + kNarrowNonBreakingSpace + d + kNonBreakingSpace + "2" + kNarrowNonBreakingSpace + h + kNonBreakingSpace +
"3" + kNarrowNonBreakingSpace + m}}},
// zh (no spacings)
{GetLocale("zh"), {{1, 2, 3, 0, "1" + d + "2" + h + "3" + m}}}};
for (auto const & data : testData)
{
for (auto const & duration : data.m_duration)
{
auto const durationStr =
Duration(duration.Seconds())
.GetLocalizedString({Duration::Units::Days, Duration::Units::Hours, Duration::Units::Minutes},
data.m_locale);
TEST_EQUAL(durationStr, duration.result, ());
}
}
}
} // namespace platform

View file

@ -0,0 +1,120 @@
#include "testing/testing.hpp"
#include "platform/get_text_by_id.hpp"
#include <string>
UNIT_TEST(GetTextByIdEnglishTest)
{
std::string const shortJson =
"\
{\
\"make_a_slight_right_turn\":\"Make a slight right turn.\",\
\"in_900_meters\":\"In nine hundred meters.\",\
\"then\":\"Then.\",\
\"in_1_mile\":\"In one mile.\"\
}";
auto getEnglish = platform::ForTestingGetTextByIdFactory(shortJson, "en");
TEST_EQUAL((*getEnglish)("make_a_slight_right_turn"), "Make a slight right turn.", ());
TEST_EQUAL((*getEnglish)("in_900_meters"), "In nine hundred meters.", ());
TEST_EQUAL((*getEnglish)("then"), "Then.", ());
TEST_EQUAL((*getEnglish)("in_1_mile"), "In one mile.", ());
TEST_EQUAL((*getEnglish)("some_nonexistent_key"), "", ());
TEST_EQUAL((*getEnglish)("some_nonexistent_key"), "", ());
TEST_EQUAL((*getEnglish)(""), "", ());
TEST_EQUAL((*getEnglish)(" "), "", ());
}
UNIT_TEST(GetTextByIdRussianTest)
{
std::string const shortJson =
"\
{\
\"in_800_meters\":\"Через восемьсот метров.\",\
\"make_a_slight_right_turn\":\"Держитесь правее.\",\
\"take_the_6_exit\":\"Сверните на шестой съезд.\",\
\"in_1_mile\":\"Через одну милю.\"\
}";
auto getRussian = platform::ForTestingGetTextByIdFactory(shortJson, "ru");
TEST_EQUAL((*getRussian)("in_800_meters"), "Через восемьсот метров.", ());
TEST_EQUAL((*getRussian)("make_a_slight_right_turn"), "Держитесь правее.", ());
TEST_EQUAL((*getRussian)("take_the_6_exit"), "Сверните на шестой съезд.", ());
TEST_EQUAL((*getRussian)("in_1_mile"), "Через одну милю.", ());
TEST_EQUAL((*getRussian)("some_nonexistent_key"), "", ());
TEST_EQUAL((*getRussian)("some_nonexistent_key"), "", ());
TEST_EQUAL((*getRussian)(""), "", ());
TEST_EQUAL((*getRussian)(" "), "", ());
}
UNIT_TEST(GetTextByIdKoreanTest)
{
std::string const shortJson =
"\
{\
\"in_700_meters\":\"700 미터 앞\",\
\"make_a_right_turn\":\"우회전입니다.\",\
\"take_the_5_exit\":\"다섯 번째 출구입니다.\",\
\"in_5000_feet\":\"5000피트 앞\"\
}";
auto getKorean = platform::ForTestingGetTextByIdFactory(shortJson, "ko");
TEST_EQUAL((*getKorean)("in_700_meters"), "700 미터 앞", ());
TEST_EQUAL((*getKorean)("make_a_right_turn"), "우회전입니다.", ());
TEST_EQUAL((*getKorean)("take_the_5_exit"), "다섯 번째 출구입니다.", ());
TEST_EQUAL((*getKorean)("in_5000_feet"), "5000피트 앞", ());
TEST_EQUAL((*getKorean)("some_nonexistent_key"), "", ());
TEST_EQUAL((*getKorean)("some_nonexistent_key"), "", ());
TEST_EQUAL((*getKorean)(""), "", ());
TEST_EQUAL((*getKorean)(" "), "", ());
}
UNIT_TEST(GetTextByIdArabicTest)
{
std::string const shortJson =
"\
{\
\"in_1_kilometer\":\"بعد كيلو متر واحدٍ\",\
\"leave_the_roundabout\":\"اخرج من الطريق الدوار\",\
\"take_the_3_exit\":\"اسلك المخرج الثالث\",\
\"in_4000_feet\":\"بعد 4000 قدم\"\
}";
auto getArabic = platform::ForTestingGetTextByIdFactory(shortJson, "ar");
TEST_EQUAL((*getArabic)("in_1_kilometer"), "بعد كيلو متر واحدٍ", ());
TEST_EQUAL((*getArabic)("leave_the_roundabout"), "اخرج من الطريق الدوار", ());
TEST_EQUAL((*getArabic)("take_the_3_exit"), "اسلك المخرج الثالث", ());
TEST_EQUAL((*getArabic)("in_4000_feet"), "بعد 4000 قدم", ());
TEST_EQUAL((*getArabic)("some_nonexistent_key"), "", ());
TEST_EQUAL((*getArabic)("some_nonexistent_key"), "", ());
TEST_EQUAL((*getArabic)(""), "", ());
TEST_EQUAL((*getArabic)(" "), "", ());
}
UNIT_TEST(GetTextByIdFrenchTest)
{
std::string const shortJson =
"\
{\
\"in_1_5_kilometers\":\"Dans un virgule cinq kilomètre.\",\
\"enter_the_roundabout\":\"Prenez le rond-point.\",\
\"take_the_2_exit\":\"Prenez la deuxième sortie.\",\
\"in_3500_feet\":\"Dans trois mille cinq cents pieds.\"\
}";
auto getFrench = platform::ForTestingGetTextByIdFactory(shortJson, "fr");
TEST_EQUAL((*getFrench)("in_1_5_kilometers"), "Dans un virgule cinq kilomètre.", ());
TEST_EQUAL((*getFrench)("enter_the_roundabout"), "Prenez le rond-point.", ());
TEST_EQUAL((*getFrench)("take_the_2_exit"), "Prenez la deuxième sortie.", ());
TEST_EQUAL((*getFrench)("in_3500_feet"), "Dans trois mille cinq cents pieds.", ());
TEST_EQUAL((*getFrench)("some_nonexistent_key"), "", ());
TEST_EQUAL((*getFrench)("some_nonexistent_key"), "", ());
TEST_EQUAL((*getFrench)(""), "", ());
TEST_EQUAL((*getFrench)(" "), "", ());
}

View file

@ -0,0 +1,90 @@
#include "testing/testing.hpp"
#include "glaze/json.hpp"
#include <optional>
#include <string>
enum class LocationSource
{
GPS,
Network,
Unknown
};
// If enumeration should be represented as string instead of integer in JSON,
// then enum should be declared in the global namespace.
template <>
struct glz::meta<LocationSource>
{
using enum LocationSource;
static constexpr auto value = glz::enumerate(GPS, Network, Unknown);
};
// For our test macros.
std::string DebugPrint(LocationSource source)
{
return std::string{glz::get_enum_name(source)};
}
namespace glaze_test
{
struct Location
{
LocationSource source;
double latitude;
double longitude;
double accuracy;
std::optional<double> altitude;
std::optional<double> altitudeAccuracy;
};
struct AccessToken
{
Location location;
std::string accessToken;
};
UNIT_TEST(Glaze_Smoke)
{
std::string_view constexpr fullJsonWithComments = R"({
"location": {
"source": "GPS", // Where the location data comes from.
"latitude": 47.3345141,
"longitude": 8.5312839,
"accuracy": 22,
"altitude": 100.5,
"altitudeAccuracy": 5.0
},
/* Access token for authentication
* This token is used to access protected resources.
*/
"accessToken": "2:vC65Xv0mxMtsNVf4:hY5YSIkuFfnAU77z"
})";
AccessToken token;
auto error = glz::read_jsonc(token, fullJsonWithComments);
TEST(!error, (glz::format_error(error, fullJsonWithComments)));
TEST_EQUAL(token.location.source, LocationSource::GPS, ());
TEST_EQUAL(token.location.latitude, 47.3345141, ());
TEST_EQUAL(token.location.longitude, 8.5312839, ());
TEST_EQUAL(token.location.accuracy, 22.0, ());
TEST_EQUAL(token.location.altitude, 100.5, ());
TEST_EQUAL(token.location.altitudeAccuracy, 5.0, ());
TEST_EQUAL(token.accessToken, "2:vC65Xv0mxMtsNVf4:hY5YSIkuFfnAU77z", ());
std::string_view constexpr partialJson =
R"({"location":{"source":"Network","latitude":47.3345141,"longitude":8.5312839,"accuracy":22},"accessToken":""})";
token.location.source = LocationSource::Network;
token.location.altitude = {};
token.location.altitudeAccuracy = {};
token.accessToken = {};
std::string buffer;
error = glz::write_json(token, buffer);
TEST(!error, (glz::format_error(error, "Failed to write JSON")));
TEST_EQUAL(buffer, partialJson, ());
}
} // namespace glaze_test

View file

@ -0,0 +1,45 @@
#include "testing/testing.hpp"
#include "cppjansson/cppjansson.hpp"
UNIT_TEST(Jansson_Smoke)
{
char * savedLocale = setlocale(LC_NUMERIC, "C");
// char const * str = "{\"location\":{\"latitude\":47.383333,\"longitude\":8.533333,"
// "\"accuracy\":18000.0},\"access_token\":\"2:6aOjM2IAoPMaweWN:txhu5LpkRkLVb3u3\"}";
char const * str =
"{\"location\":{\"latitude\":47.3345141,\"longitude\":8.5312839,"
"\"accuracy\":22.0},\"access_token\":\"2:vC65Xv0mxMtsNVf4:hY5YSIkuFfnAU77z\"}";
base::Json root(str);
TEST(json_is_object(root.get()), ());
json_t * location = json_object_get(root.get(), "location");
TEST(json_is_object(location), ());
json_t * lat = json_object_get(location, "latitude");
TEST(json_is_real(lat), ());
TEST_ALMOST_EQUAL_ULPS(json_real_value(lat), 47.3345141, ());
json_t * lon = json_object_get(location, "longitude");
TEST(json_is_real(lon), ());
TEST_ALMOST_EQUAL_ULPS(json_real_value(lon), 8.5312839, ());
json_t * acc = json_object_get(location, "accuracy");
TEST(json_is_real(acc), ());
TEST_ALMOST_EQUAL_ULPS(json_real_value(acc), 22.0, ());
bool wasException = false;
try
{
base::Json invalid("{asd]");
}
catch (base::Json::Exception const &)
{
wasException = true;
}
TEST(wasException, ());
setlocale(LC_NUMERIC, savedLocale);
}

View file

@ -0,0 +1,33 @@
#include "testing/testing.hpp"
#include "platform/preferred_languages.hpp"
#include "base/logging.hpp"
#include <cstddef>
#include <string>
UNIT_TEST(LangNormalize_Smoke)
{
char const * arr1[] = {"en", "en-GB", "zh", "es-SP", "zh-penyn", "en-US", "ru_RU", "es"};
char const * arr2[] = {"en", "en", "zh", "es", "zh", "en", "ru", "es"};
static_assert(ARRAY_SIZE(arr1) == ARRAY_SIZE(arr2), "");
for (size_t i = 0; i < ARRAY_SIZE(arr1); ++i)
TEST_EQUAL(arr2[i], languages::Normalize(arr1[i]), ());
}
UNIT_TEST(PrefLanguages_Smoke)
{
std::string s = languages::GetPreferred();
TEST(!s.empty(), ());
LOG(LINFO, ("Preferred langs:", s));
s = languages::GetCurrentOrig();
TEST(!s.empty(), ());
LOG(LINFO, ("Current original lang:", s));
s = languages::GetCurrentNorm();
TEST(!s.empty(), ());
LOG(LINFO, ("Current normalized lang:", s));
}

View file

@ -0,0 +1,349 @@
#include "testing/testing.hpp"
#include "platform/country_file.hpp"
#include "platform/local_country_file.hpp"
#include "platform/local_country_file_utils.hpp"
#include "platform/mwm_version.hpp"
#include "platform/platform.hpp"
#include "platform/platform_tests_support/scoped_dir.hpp"
#include "platform/platform_tests_support/scoped_file.hpp"
#include "platform/settings.hpp"
#include "coding/file_writer.hpp"
#include "coding/internal/file_data.hpp"
#include "base/file_name_utils.hpp"
#include "base/logging.hpp"
#include "base/scope_guard.hpp"
#include "defines.hpp"
#include <algorithm>
#include <functional>
#include <set>
#include <string>
namespace local_country_file_tests
{
using platform::CountryFile;
using platform::LocalCountryFile;
using platform::tests_support::ScopedDir;
using platform::tests_support::ScopedFile;
// Checks that all unsigned numbers less than 10 ^ 18 can be parsed as
// a timestamp.
UNIT_TEST(LocalCountryFile_ParseVersion)
{
using namespace platform;
int64_t version = 0;
TEST(ParseVersion("1", version), ());
TEST_EQUAL(version, 1, ());
TEST(ParseVersion("141010", version), ());
TEST_EQUAL(version, 141010, ());
TEST(ParseVersion("150309", version), ());
TEST_EQUAL(version, 150309, ());
TEST(!ParseVersion("2111225", version), ()); // too many digits
TEST(!ParseVersion("00000000000000000000000000000000123", version), ());
TEST(!ParseVersion("", version), ());
TEST(!ParseVersion("150309 ", version), ());
TEST(!ParseVersion(" 150309", version), ());
TEST(!ParseVersion("-150309", version), ());
TEST(!ParseVersion("just string", version), ());
}
// Checks basic functionality of LocalCountryFile.
UNIT_TEST(LocalCountryFile_Smoke)
{
CountryFile countryFile("TestCountry", 1 /* size */, "sha1");
LocalCountryFile localFile("/test-dir", countryFile, 150309);
TEST_EQUAL("/test-dir/TestCountry" DATA_FILE_EXTENSION, localFile.GetPath(MapFileType::Map), ());
// Not synced with disk yet.
TEST(!localFile.HasFiles(), ());
TEST(!localFile.OnDisk(MapFileType::Map), ());
TEST(!localFile.OnDisk(MapFileType::Diff), ());
TEST_EQUAL("/test-dir", localFile.GetDirectory(), ());
TEST_EQUAL(0, localFile.GetSize(MapFileType::Map), ());
TEST_EQUAL(0, localFile.GetSize(MapFileType::Diff), ());
TEST_EQUAL(150309, localFile.GetVersion(), ());
}
// Creates test country map file and checks sync-with-disk functionality.
UNIT_TEST(LocalCountryFile_DiskFiles)
{
Platform & platform = GetPlatform();
CountryFile countryFile("TestCountry", 1 /* size */, "sha1");
for (int64_t version : {1, 150312})
{
LocalCountryFile localFile(platform.WritableDir(), countryFile, version);
TEST(!localFile.OnDisk(MapFileType::Map), ());
TEST(!localFile.OnDisk(MapFileType::Diff), ());
std::string const mapFileName = countryFile.GetFileName(MapFileType::Map);
std::string const mapFileContents("map");
ScopedFile testMapFile(mapFileName, mapFileContents);
localFile.SyncWithDisk();
TEST(localFile.OnDisk(MapFileType::Map), ());
TEST(!localFile.OnDisk(MapFileType::Diff), ());
TEST_EQUAL(mapFileContents.size(), localFile.GetSize(MapFileType::Map), ());
localFile.SyncWithDisk();
TEST(localFile.OnDisk(MapFileType::Map), ());
TEST_EQUAL(mapFileContents.size(), localFile.GetSize(MapFileType::Map), ());
localFile.DeleteFromDisk(MapFileType::Map);
TEST(!testMapFile.Exists(), (testMapFile, "wasn't deleted by LocalCountryFile."));
testMapFile.Reset();
}
}
UNIT_TEST(LocalCountryFile_CleanupMapFiles)
{
Platform & platform = GetPlatform();
std::string const mapsDir = platform.WritableDir();
// Two fake directories for test country files and indexes.
ScopedDir dir3("3");
ScopedDir dir4("4");
ScopedDir absentCountryIndexesDir(dir4, "Absent");
ScopedDir irelandIndexesDir(dir4, "Ireland");
CountryFile irelandFile("Ireland");
LocalCountryFile irelandLocalFile(dir4.GetFullPath(), irelandFile, 4 /* version */);
ScopedFile irelandMapFile(dir4, irelandFile, MapFileType::Map);
// Check FindAllLocalMaps()
std::vector<LocalCountryFile> localFiles;
FindAllLocalMapsAndCleanup(4 /* latestVersion */, localFiles);
TEST(base::IsExist(localFiles, irelandLocalFile), (irelandLocalFile, localFiles));
irelandLocalFile.SyncWithDisk();
TEST(irelandLocalFile.OnDisk(MapFileType::Map), ());
irelandLocalFile.DeleteFromDisk(MapFileType::Map);
TEST(!irelandMapFile.Exists(), (irelandMapFile));
irelandMapFile.Reset();
TEST(!dir3.Exists(), ("Empty directory", dir3, "wasn't removed."));
dir3.Reset();
TEST(dir4.Exists(), ());
// Useless CountryIndexes::DeleteFromDisk calls are removed.
// TEST(!absentCountryIndexesDir.Exists(), ("Indexes for absent country weren't deleted."));
// absentCountryIndexesDir.Reset();
TEST(irelandIndexesDir.Exists(), ());
}
UNIT_TEST(LocalCountryFile_CleanupPartiallyDownloadedFiles)
{
ScopedDir dataDir("101008");
auto const DataFilePath = [&dataDir](char const * file) { return dataDir.GetRelativePath() + "/" + file; };
ScopedDir oldDir("101009");
ScopedDir latestDir("101010");
ScopedFile toBeDeleted[] = {
{DataFilePath("Ireland.mwm.ready"), ScopedFile::Mode::Create},
{DataFilePath("Netherlands.mwm.routing.downloading2"), ScopedFile::Mode::Create},
{DataFilePath("Germany.mwm.ready3"), ScopedFile::Mode::Create},
{DataFilePath("UK_England.mwm.resume4"), ScopedFile::Mode::Create},
{base::JoinPath(oldDir.GetRelativePath(), "Russia_Central.mwm.downloading"), ScopedFile::Mode::Create}};
ScopedFile toBeKept[] = {
{DataFilePath("Italy.mwm"), ScopedFile::Mode::Create},
{DataFilePath("Spain.mwm"), ScopedFile::Mode::Create},
{DataFilePath("Spain.mwm.routing"), ScopedFile::Mode::Create},
{base::JoinPath(latestDir.GetRelativePath(), "Russia_Southern.mwm.downloading"), ScopedFile::Mode::Create}};
platform::CleanupMapsDirectory(101010 /* latestVersion */);
for (ScopedFile & file : toBeDeleted)
{
TEST(!file.Exists(), (file));
file.Reset();
}
TEST(!oldDir.Exists(), (oldDir));
oldDir.Reset();
for (ScopedFile & file : toBeKept)
TEST(file.Exists(), (file));
TEST(latestDir.Exists(), (latestDir));
TEST(dataDir.Exists(), (dataDir));
}
// Creates test-dir and following files:
// * test-dir/Ireland.mwm
// * test-dir/Netherlands.mwm
// * test-dir/Netherlands.mwm.routing
// After that, checks that FindAllLocalMapsInDirectory() correctly finds all created files.
UNIT_TEST(LocalCountryFile_DirectoryLookup)
{
// This tests creates a map file for Ireland and map + routing files
// for Netherlands in a test directory.
CountryFile const irelandFile("Ireland");
CountryFile const netherlandsFile("Netherlands");
ScopedDir testDir("test-dir");
ScopedFile testIrelandMapFile(testDir, irelandFile, MapFileType::Map);
ScopedFile testNetherlandsMapFile(testDir, netherlandsFile, MapFileType::Map);
std::vector<LocalCountryFile> localFiles;
FindAllLocalMapsInDirectoryAndCleanup(testDir.GetFullPath(), 150309 /* version */, -1 /* latestVersion */,
localFiles);
std::sort(localFiles.begin(), localFiles.end());
for (LocalCountryFile & localFile : localFiles)
localFile.SyncWithDisk();
LocalCountryFile expectedIrelandFile(testDir.GetFullPath(), irelandFile, 150309);
expectedIrelandFile.SyncWithDisk();
LocalCountryFile expectedNetherlandsFile(testDir.GetFullPath(), netherlandsFile, 150309);
expectedNetherlandsFile.SyncWithDisk();
std::vector expectedLocalFiles = {expectedIrelandFile, expectedNetherlandsFile};
std::sort(expectedLocalFiles.begin(), expectedLocalFiles.end());
TEST_EQUAL(expectedLocalFiles, localFiles, ());
}
// Creates directory 010101 and 010101/Italy.mwm file. After that,
// checks that this file will be recognized as a map file for Italy
// with version 010101. Also, checks that World.mwm and
// WorldCoasts.mwm exist in writable dir.
UNIT_TEST(LocalCountryFile_AllLocalFilesLookup)
{
CountryFile const italyFile("Italy");
ScopedDir testDir("10101");
settings::Delete("LastMigration");
ScopedFile testItalyMapFile(testDir, italyFile, MapFileType::Map);
std::vector<LocalCountryFile> localFiles;
FindAllLocalMapsAndCleanup(10101 /* latestVersion */, localFiles);
std::multiset<LocalCountryFile> localFilesSet(localFiles.begin(), localFiles.end());
bool worldFound = false;
bool worldCoastsFound = false;
for (auto const & file : localFiles)
{
// With the new concepts, World mwm files have valid version.
if (file.GetCountryName() == WORLD_FILE_NAME)
{
worldFound = true;
TEST_NOT_EQUAL(0, file.GetVersion(), (file));
}
if (file.GetCountryName() == WORLD_COASTS_FILE_NAME)
{
worldCoastsFound = true;
TEST_NOT_EQUAL(0, file.GetVersion(), (file));
}
}
TEST(worldFound, ());
TEST(worldCoastsFound, ());
LocalCountryFile expectedItalyFile(testDir.GetFullPath(), italyFile, 10101);
TEST_EQUAL(1, localFilesSet.count(expectedItalyFile), (localFiles));
}
UNIT_TEST(LocalCountryFile_PreparePlaceForCountryFiles)
{
Platform & platform = GetPlatform();
CountryFile italyFile("Italy");
LocalCountryFile expectedItalyFile(platform.WritableDir(), italyFile, 0 /* version */);
auto italyLocalFile = PreparePlaceForCountryFiles(0 /* version */, italyFile);
TEST(italyLocalFile.get(), ());
TEST_EQUAL(expectedItalyFile, *italyLocalFile, ());
ScopedDir directoryForV1("1");
CountryFile germanyFile("Germany");
LocalCountryFile expectedGermanyFile(directoryForV1.GetFullPath(), germanyFile, 1 /* version */);
auto germanyLocalFile = PreparePlaceForCountryFiles(1 /* version */, germanyFile);
TEST(germanyLocalFile.get(), ());
TEST_EQUAL(expectedGermanyFile, *germanyLocalFile, ());
CountryFile franceFile("France");
LocalCountryFile expectedFranceFile(directoryForV1.GetFullPath(), franceFile, 1 /* version */);
auto franceLocalFile = PreparePlaceForCountryFiles(1 /* version */, franceFile);
TEST(franceLocalFile.get(), ());
TEST_EQUAL(expectedFranceFile, *franceLocalFile, ());
}
UNIT_TEST(LocalCountryFile_CountryIndexes)
{
using platform::CountryIndexes;
ScopedDir testDir("101010");
CountryFile germanyFile("Germany");
LocalCountryFile germanyLocalFile(testDir.GetFullPath(), germanyFile, 101010 /* version */);
TEST_EQUAL(base::JoinPath(germanyLocalFile.GetDirectory(), germanyFile.GetName()),
CountryIndexes::IndexesDir(germanyLocalFile), ());
CountryIndexes::PreparePlaceOnDisk(germanyLocalFile);
auto const bitsPath = CountryIndexes::GetPath(germanyLocalFile, CountryIndexes::Index::Bits);
TEST(!Platform::IsFileExistsByFullPath(bitsPath), (bitsPath));
{
FileWriter writer(bitsPath);
std::string const contents = "bits index";
writer.Write(contents.data(), contents.size());
}
TEST(Platform::IsFileExistsByFullPath(bitsPath), (bitsPath));
TEST(CountryIndexes::DeleteFromDisk(germanyLocalFile), ("Can't delete indexes for:", germanyLocalFile));
TEST(!Platform::IsFileExistsByFullPath(bitsPath), (bitsPath));
}
UNIT_TEST(LocalCountryFile_DoNotDeleteUserFiles)
{
using platform::CountryIndexes;
base::ScopedLogLevelChanger const criticalLogLevel(LCRITICAL);
ScopedDir testDir("101010");
CountryFile germanyFile("Germany");
LocalCountryFile germanyLocalFile(testDir.GetFullPath(), germanyFile, 101010 /* version */);
CountryIndexes::PreparePlaceOnDisk(germanyLocalFile);
auto const userFilePath = base::JoinPath(CountryIndexes::IndexesDir(germanyLocalFile), "user-data.txt");
{
FileWriter writer(userFilePath);
std::string const data = "user data";
writer.Write(data.data(), data.size());
}
TEST(!CountryIndexes::DeleteFromDisk(germanyLocalFile), ("Indexes dir should not be deleted for:", germanyLocalFile));
TEST(base::DeleteFileX(userFilePath), ("Can't delete test file:", userFilePath));
TEST(CountryIndexes::DeleteFromDisk(germanyLocalFile), ("Can't delete indexes for:", germanyLocalFile));
}
UNIT_TEST(LocalCountryFile_MakeTemporary)
{
auto const path = GetPlatform().WritablePathForFile("minsk-pass" DATA_FILE_EXTENSION);
LocalCountryFile file = LocalCountryFile::MakeTemporary(path);
TEST_EQUAL(file.GetPath(MapFileType::Map), path, ());
}
} // namespace local_country_file_tests

View file

@ -0,0 +1,39 @@
#include "testing/testing.hpp"
#include "platform/location.hpp"
UNIT_TEST(IsLatValid)
{
TEST(location::IsLatValid(35.), ());
TEST(location::IsLatValid(-35.), ());
TEST(!location::IsLatValid(0.), ());
TEST(!location::IsLatValid(100.), ());
TEST(!location::IsLatValid(-99.), ());
}
UNIT_TEST(IsLonValid)
{
TEST(location::IsLonValid(135.), ());
TEST(location::IsLonValid(-35.), ());
TEST(!location::IsLonValid(0.), ());
TEST(!location::IsLonValid(200.), ());
TEST(!location::IsLonValid(-199.), ());
}
UNIT_TEST(AngleToBearing)
{
TEST_ALMOST_EQUAL_ULPS(location::AngleToBearing(0.), 90., ());
TEST_ALMOST_EQUAL_ULPS(location::AngleToBearing(30.), 60., ());
TEST_ALMOST_EQUAL_ULPS(location::AngleToBearing(100.), 350., ());
TEST_ALMOST_EQUAL_ULPS(location::AngleToBearing(370.), 80., ());
TEST_ALMOST_EQUAL_ULPS(location::AngleToBearing(-370.), 100., ());
}
UNIT_TEST(BearingToAngle)
{
TEST_ALMOST_EQUAL_ULPS(location::BearingToAngle(0.), 90., ());
TEST_ALMOST_EQUAL_ULPS(location::BearingToAngle(30.), 60., ());
TEST_ALMOST_EQUAL_ULPS(location::BearingToAngle(100.), 350., ());
TEST_ALMOST_EQUAL_ULPS(location::BearingToAngle(370.), 80., ());
TEST_ALMOST_EQUAL_ULPS(location::AngleToBearing(-370.), 100., ());
}

View file

@ -0,0 +1,183 @@
#include "testing/testing.hpp"
#include "platform/measurement_utils.hpp"
#include "base/math.hpp"
#include <iostream>
#include <string>
using namespace measurement_utils;
using namespace platform;
std::string AddGroupingSeparators(std::string const & valueString, std::string const & groupingSeparator)
{
std::string out(valueString);
if (out.size() > 4 && !groupingSeparator.empty())
for (int pos = out.size() - 3; pos > 0; pos -= 3)
out.insert(pos, groupingSeparator);
return out;
}
UNIT_TEST(LatLonToDMS_Origin)
{
TEST_EQUAL(FormatLatLonAsDMS(0, 0, false), "00°0000″ 00°0000″", ());
TEST_EQUAL(FormatLatLonAsDMS(0, 0, false, 3), "00°0000″ 00°0000″", ());
}
UNIT_TEST(LatLonToDMS_Rounding)
{
// Here and after data is from Wiki: http://bit.ly/datafotformatingtest
// Boston
TEST_EQUAL(FormatLatLonAsDMS(42.358056, -71.063611, false, 0), "42°2129″N 71°0349″W", ());
// Minsk
TEST_EQUAL(FormatLatLonAsDMS(53.916667, 27.55, false, 0), "53°5500″N 27°3300″E", ());
// Rio
TEST_EQUAL(FormatLatLonAsDMS(-22.908333, -43.196389, false, 0), "22°5430″S 43°1147″W", ());
}
UNIT_TEST(LatLonToDMS_NoRounding)
{
// Paris
TEST_EQUAL(FormatLatLonAsDMS(48.8567, 2.3508, false, 2), "48°5124.12″N 02°2102.88″E", ());
// Vatican
TEST_EQUAL(FormatLatLonAsDMS(41.904, 12.453, false, 2), "41°5414.4″N 12°2710.8″E", ());
TEST_EQUAL(FormatLatLonAsDMS(21.981112, -159.371112, false, 2), "21°5852″N 159°2216″W", ());
}
UNIT_TEST(FormatOsmLink)
{
// Zero point
TEST_EQUAL(FormatOsmLink(0, 0, 5), "https://osm.org/go/wAAAA-?m", ());
// Eifel tower
TEST_EQUAL(FormatOsmLink(48.85825, 2.29450, 15), "https://osm.org/go/0BOdUs9e--?m", ());
// Buenos Aires
TEST_EQUAL(FormatOsmLink(-34.6061, -58.4360, 10), "https://osm.org/go/Mnx6SB?m", ());
// Formally, lat = -90 and lat = 90 are the same for OSM links, but Mercator is valid until 85.
auto link = FormatOsmLink(-90, -180, 10);
TEST(link == "https://osm.org/go/AAAAAA?m" || link == "https://osm.org/go/~~~~~~?m", (link));
link = FormatOsmLink(90, 180, 10);
TEST(link == "https://osm.org/go/AAAAAA?m" || link == "https://osm.org/go/~~~~~~?m", (link));
}
UNIT_TEST(FormatSpeedNumeric)
{
TEST_EQUAL(FormatSpeedNumeric(10, Units::Metric), "36", ());
TEST_EQUAL(FormatSpeedNumeric(1, Units::Metric), "4", ());
TEST_EQUAL(FormatSpeedNumeric(10, Units::Imperial), "22", ());
TEST_EQUAL(FormatSpeedNumeric(1, Units::Imperial), "2", ());
}
UNIT_TEST(OSMDistanceToMetersString)
{
TEST_EQUAL(OSMDistanceToMetersString(""), "", ());
TEST_EQUAL(OSMDistanceToMetersString("INF"), "", ());
TEST_EQUAL(OSMDistanceToMetersString("NAN"), "", ());
TEST_EQUAL(OSMDistanceToMetersString("not a number"), "", ());
TEST_EQUAL(OSMDistanceToMetersString("10й"), "10", ());
TEST_EQUAL(OSMDistanceToMetersString("0"), "0", ());
TEST_EQUAL(OSMDistanceToMetersString("0.0"), "0", ());
TEST_EQUAL(OSMDistanceToMetersString("0.0000000"), "0", ());
TEST_EQUAL(OSMDistanceToMetersString("22.5"), "22.5", ());
TEST_EQUAL(OSMDistanceToMetersString("+21.5"), "21.5", ());
TEST_EQUAL(OSMDistanceToMetersString("1e+2"), "100", ());
TEST_EQUAL(OSMDistanceToMetersString("5 метров"), "5", ());
TEST_EQUAL(OSMDistanceToMetersString(" 4.4 "), "4.4", ());
TEST_EQUAL(OSMDistanceToMetersString("8-15"), "15", ());
TEST_EQUAL(OSMDistanceToMetersString("8-15 ft"), "4.57", ());
TEST_EQUAL(OSMDistanceToMetersString("8-й километр"), "8", ());
// Do not support lists for distance values.
TEST_EQUAL(OSMDistanceToMetersString("8;9;10"), "", ());
TEST_EQUAL(OSMDistanceToMetersString("8;9;10 meters"), "", ());
TEST_EQUAL(OSMDistanceToMetersString("8;9;10 km"), "", ());
TEST_EQUAL(OSMDistanceToMetersString("10;20!111"), "", ());
TEST_EQUAL(OSMDistanceToMetersString("10;20\""), "", ());
TEST_EQUAL(OSMDistanceToMetersString("-100.3"), "-100.3", ());
TEST_EQUAL(OSMDistanceToMetersString("99.0000000"), "99", ());
TEST_EQUAL(OSMDistanceToMetersString("8900.000023"), "8900", ());
TEST_EQUAL(OSMDistanceToMetersString("-300.9999"), "-301", ());
TEST_EQUAL(OSMDistanceToMetersString("-300.9"), "-300.9", ());
TEST_EQUAL(OSMDistanceToMetersString("15 m"), "15", ());
TEST_EQUAL(OSMDistanceToMetersString("15.9 m"), "15.9", ());
TEST_EQUAL(OSMDistanceToMetersString("15.9m"), "15.9", ());
TEST_EQUAL(OSMDistanceToMetersString("3000 ft"), "914.4", ());
TEST_EQUAL(OSMDistanceToMetersString("3000ft"), "914.4", ());
TEST_EQUAL(OSMDistanceToMetersString("100 feet"), "30.48", ());
TEST_EQUAL(OSMDistanceToMetersString("100feet"), "30.48", ());
TEST_EQUAL(OSMDistanceToMetersString("3 nmi"), "5556", ());
TEST_EQUAL(OSMDistanceToMetersString("3 mi"), "4828.03", ());
TEST_EQUAL(OSMDistanceToMetersString("3.3 km"), "3300", ());
TEST_EQUAL(OSMDistanceToMetersString("105'"), "32", ());
TEST_EQUAL(OSMDistanceToMetersString("11'"), "3.35", ());
TEST_EQUAL(OSMDistanceToMetersString("11 3\""), "11", ());
TEST_EQUAL(OSMDistanceToMetersString("11 3'"), "11", ());
TEST_EQUAL(OSMDistanceToMetersString("11\"'"), "0.28", ());
TEST_EQUAL(OSMDistanceToMetersString("11'\""), "3.35", ());
TEST_EQUAL(OSMDistanceToMetersString("11'4\""), "3.45", ());
TEST_EQUAL(OSMDistanceToMetersString("11\""), "0.28", ());
TEST_EQUAL(OSMDistanceToMetersString("100 \""), "100", ());
TEST_EQUAL(OSMDistanceToMetersString("0", false), "", ());
TEST_EQUAL(OSMDistanceToMetersString("-15", false), "", ());
TEST_EQUAL(OSMDistanceToMetersString("15.12345", false, 1), "15.1", ());
TEST_EQUAL(OSMDistanceToMetersString("15.123", false, 4), "15.123", ());
TEST_EQUAL(OSMDistanceToMetersString("15.654321", true, 1), "15.7", ());
}
UNIT_TEST(UnitsConversion)
{
double const kEps = 1e-5;
TEST(AlmostEqualAbs(MilesToMeters(MetersToMiles(1000.0)), 1000.0, kEps), ());
TEST(AlmostEqualAbs(MilesToMeters(1.0), 1609.344, kEps), ());
TEST(AlmostEqualAbs(MiphToKmph(KmphToMiph(100.0)), 100.0, kEps), ());
TEST(AlmostEqualAbs(MiphToKmph(100.0), 160.9344, kEps), (MiphToKmph(100.0)));
TEST(AlmostEqualAbs(ToSpeedKmPH(100.0, Units::Imperial), 160.9344, kEps), ());
TEST(AlmostEqualAbs(ToSpeedKmPH(100.0, Units::Metric), 100.0, kEps), ());
TEST(AlmostEqualAbs(KmphToMps(3.6), 1.0, kEps), ());
TEST(AlmostEqualAbs(MpsToKmph(1.0), 3.6, kEps), ());
}
UNIT_TEST(ToStringPrecisionLocale)
{
double d1 = 9.8;
int pr1 = 1;
double d2 = 12345.0;
int pr2 = 0;
std::string d2String("12345");
struct TestData
{
std::string localeName;
std::string d1String;
};
TestData testData[] = {// Locale name , Decimal
{"en_US.UTF-8", "9.8"},
{"es_ES.UTF-8", "9,8"},
{"fr_FR.UTF-8", "9,8"},
{"ru_RU.UTF-8", "9,8"}};
for (TestData const & data : testData)
{
Locale loc;
if (!GetLocale(data.localeName, loc))
{
std::cout << "Locale '" << data.localeName << "' not found!! Skipping test..." << std::endl;
continue;
}
TEST_EQUAL(measurement_utils::ToStringPrecisionLocale(loc, d1, pr1), data.d1String, ());
TEST_EQUAL(measurement_utils::ToStringPrecisionLocale(loc, d2, pr2),
AddGroupingSeparators(d2String, loc.m_groupingSeparator), ());
}
}

View file

@ -0,0 +1,62 @@
#include "testing/testing.hpp"
#include "platform/servers_list.hpp"
#include "cppjansson/cppjansson.hpp"
using namespace downloader;
UNIT_TEST(MetaConfig_JSONParser_OldFormat)
{
std::string oldFormatJson = R"(["http://url1", "http://url2", "http://url3"])";
auto result = ParseMetaConfig(oldFormatJson);
TEST(result.has_value(), ());
TEST_EQUAL(result->m_serversList.size(), 3, ());
TEST_EQUAL(result->m_serversList[0], "http://url1", ());
TEST_EQUAL(result->m_serversList[1], "http://url2", ());
TEST_EQUAL(result->m_serversList[2], "http://url3", ());
TEST(result->m_settings.empty(), ());
}
UNIT_TEST(MetaConfig_JSONParser_InvalidJSON)
{
std::string invalidJson = R"({"servers": ["http://url1", "http://url2")";
auto result = ParseMetaConfig(invalidJson);
TEST(!result.has_value(), ());
}
UNIT_TEST(MetaConfig_JSONParser_EmptyServersList)
{
std::string emptyServersJson = R"({"servers": []})";
auto result = ParseMetaConfig(emptyServersJson);
TEST(!result.has_value(), ());
}
UNIT_TEST(MetaConfig_JSONParser_NewFormatWithoutProducts)
{
std::string newFormatJson = R"({
"servers": ["http://url1", "http://url2"],
"settings": {
"DonateUrl": "value1",
"key2": "value2"
}
})";
auto result = ParseMetaConfig(newFormatJson);
TEST(result.has_value(), ());
TEST_EQUAL(result->m_serversList.size(), 2, ());
TEST_EQUAL(result->m_serversList[0], "http://url1", ());
TEST_EQUAL(result->m_serversList[1], "http://url2", ());
TEST_EQUAL(result->m_settings.size(), 1, ());
TEST_EQUAL(result->m_settings["DonateUrl"], "value1", ());
}
UNIT_TEST(MetaConfig_JSONParser_MissingServersKey)
{
std::string missingServersJson = R"({
"settings": {
"key1": "value1"
}
})";
auto result = ParseMetaConfig(missingServersJson);
TEST(!result.has_value(), ("JSON shouldn't be parsed without 'servers' key"));
}

View file

@ -0,0 +1,334 @@
#include "testing/testing.hpp"
#include "platform/mwm_version.hpp"
#include "platform/platform.hpp"
#include "platform/platform_tests_support/scoped_file.hpp"
#include "coding/file_writer.hpp"
#include "coding/internal/file_data.hpp"
#include "base/file_name_utils.hpp"
#include "base/logging.hpp"
#include "base/scope_guard.hpp"
#include "base/stl_helpers.hpp"
#include <functional>
#include <initializer_list>
#include <set>
#include <string>
#include <utility>
#include <boost/regex.hpp>
#include "defines.hpp"
namespace
{
char const * TEST_FILE_NAME = "some_temporary_unit_test_file.tmp";
void CheckFilesPresence(std::string const & baseDir, unsigned typeMask,
std::initializer_list<std::pair<std::string, size_t>> const & files)
{
Platform::TFilesWithType fwts;
Platform::GetFilesByType(baseDir, typeMask, fwts);
std::multiset<std::string> filesSet;
for (auto const & fwt : fwts)
filesSet.insert(fwt.first);
for (auto const & file : files)
TEST_EQUAL(filesSet.count(file.first), file.second, (file.first, file.second));
}
} // namespace
UNIT_TEST(WritableDir)
{
std::string const path = GetPlatform().WritableDir() + TEST_FILE_NAME;
try
{
base::FileData f(path, base::FileData::Op::WRITE_TRUNCATE);
}
catch (Writer::OpenException const &)
{
LOG(LCRITICAL, ("Can't create file", path));
return;
}
base::DeleteFileX(path);
}
UNIT_TEST(WritablePathForFile)
{
Platform & pl = GetPlatform();
std::string const p1 = base::JoinPath(pl.WritableDir(), TEST_FILE_NAME);
std::string const p2 = pl.WritablePathForFile(TEST_FILE_NAME);
TEST_EQUAL(p1, p2, ());
}
UNIT_TEST(GetReader)
{
char const * NON_EXISTING_FILE = "mgbwuerhsnmbui45efhdbn34.tmp";
char const * arr[] = {"symbols/mdpi/light/symbols.sdf", "classificator.txt", "minsk-pass.mwm"};
Platform & p = GetPlatform();
for (size_t i = 0; i < ARRAY_SIZE(arr); ++i)
{
ReaderPtr<Reader> r = p.GetReader(arr[i]);
TEST_GREATER(r.Size(), 0, ("File should exist!"));
}
bool wasException = false;
try
{
ReaderPtr<Reader> r = p.GetReader(NON_EXISTING_FILE);
}
catch (FileAbsentException const &)
{
wasException = true;
}
TEST_EQUAL(wasException, true, ());
}
UNIT_TEST(GetFilesInDir_Smoke)
{
Platform & pl = GetPlatform();
Platform::FilesList files1, files2;
std::string const dir = pl.ResourcesDir();
pl.GetFilesByExt(dir, DATA_FILE_EXTENSION, files1);
TEST_GREATER(files1.size(), 0, (dir, "folder should contain some data files"));
TEST(base::IsExist(files1, "minsk-pass.mwm"), ());
pl.GetFilesByRegExp(dir, boost::regex(".*\\" + DATA_FILE_EXTENSION + "$"), files2);
TEST_EQUAL(files1, files2, ());
files1.clear();
pl.GetFilesByExt(dir, ".dsa", files1);
TEST_EQUAL(files1.size(), 0, ());
}
UNIT_TEST(DirsRoutines)
{
std::string const baseDir = GetPlatform().WritableDir();
std::string const testDir = base::JoinPath(baseDir, "test-dir");
std::string const testFile = base::JoinPath(testDir, "test-file");
TEST(!Platform::IsFileExistsByFullPath(testDir), ());
TEST_EQUAL(Platform::MkDir(testDir), Platform::ERR_OK, ());
TEST(Platform::IsFileExistsByFullPath(testDir), ());
TEST(Platform::IsDirectoryEmpty(testDir), ());
{
FileWriter writer(testFile);
}
TEST(!Platform::IsDirectoryEmpty(testDir), ());
FileWriter::DeleteFileX(testFile);
TEST_EQUAL(Platform::RmDir(testDir), Platform::ERR_OK, ());
TEST(!Platform::IsFileExistsByFullPath(testDir), ());
}
UNIT_TEST(GetFilesByType)
{
std::string const kTestDirBaseName = "test-dir";
std::string const kTestFileBaseName = "test-file";
std::string const baseDir = GetPlatform().WritableDir();
std::string const testDir = base::JoinPath(baseDir, kTestDirBaseName);
TEST_EQUAL(Platform::MkDir(testDir), Platform::ERR_OK, ());
SCOPE_GUARD(removeTestDir, bind(&Platform::RmDir, testDir));
std::string const testFile = base::JoinPath(baseDir, kTestFileBaseName);
TEST(!Platform::IsFileExistsByFullPath(testFile), ());
{
FileWriter writer(testFile);
}
TEST(Platform::IsFileExistsByFullPath(testFile), ());
SCOPE_GUARD(removeTestFile, bind(FileWriter::DeleteFileX, testFile));
CheckFilesPresence(baseDir, Platform::EFileType::Directory,
{{
kTestDirBaseName, 1 /* present */
},
{
kTestFileBaseName, 0 /* not present */
}});
CheckFilesPresence(baseDir, Platform::EFileType::Regular,
{{
kTestDirBaseName, 0 /* not present */
},
{
kTestFileBaseName, 1 /* present */
}});
CheckFilesPresence(baseDir, Platform::EFileType::Directory | Platform::EFileType::Regular,
{{
kTestDirBaseName, 1 /* present */
},
{
kTestFileBaseName, 1 /* present */
}});
}
UNIT_TEST(GetFileSize)
{
Platform & pl = GetPlatform();
uint64_t size = 123141;
TEST(!pl.GetFileSizeByName("adsmngfuwrbfyfwe", size), ());
TEST(!pl.IsFileExistsByFullPath("adsmngfuwrbfyfwe"), ());
char const kContent[] = "HOHOHO";
size_t const kSize = ARRAY_SIZE(kContent);
std::string const fileName = pl.WritablePathForFile(TEST_FILE_NAME);
{
FileWriter testFile(fileName);
testFile.Write(kContent, kSize);
}
size = 0;
TEST(Platform::GetFileSizeByFullPath(fileName, size), ());
TEST_EQUAL(size, kSize, ());
FileWriter::DeleteFileX(fileName);
TEST(!pl.IsFileExistsByFullPath(fileName), ());
{
FileWriter testFile(fileName);
testFile.Write(kContent, kSize);
}
size = 0;
TEST(pl.GetFileSizeByName(TEST_FILE_NAME, size), ());
TEST_EQUAL(size, kSize, ());
FileWriter::DeleteFileX(fileName);
}
UNIT_TEST(CpuCores)
{
int const coresNum = GetPlatform().CpuCores();
TEST_GREATER(coresNum, 0, ());
TEST_LESS_OR_EQUAL(coresNum, 128, ());
}
UNIT_TEST(GetWritableStorageStatus)
{
TEST_EQUAL(Platform::STORAGE_OK, GetPlatform().GetWritableStorageStatus(100000), ());
TEST_EQUAL(Platform::NOT_ENOUGH_SPACE, GetPlatform().GetWritableStorageStatus(uint64_t(-1)), ());
}
UNIT_TEST(RmDirRecursively)
{
std::string const testDir1 = base::JoinPath(GetPlatform().WritableDir(), "test_dir1");
TEST_EQUAL(Platform::MkDir(testDir1), Platform::ERR_OK, ());
SCOPE_GUARD(removeTestDir1, bind(&Platform::RmDir, testDir1));
std::string const testDir2 = base::JoinPath(testDir1, "test_dir2");
TEST_EQUAL(Platform::MkDir(testDir2), Platform::ERR_OK, ());
SCOPE_GUARD(removeTestDir2, bind(&Platform::RmDir, testDir2));
std::string const filePath = base::JoinPath(testDir2, "test_file");
{
FileWriter testFile(filePath);
testFile.Write("HOHOHO", 6);
}
SCOPE_GUARD(removeTestFile, bind(&base::DeleteFileX, filePath));
TEST(Platform::IsFileExistsByFullPath(filePath), ());
TEST(Platform::IsFileExistsByFullPath(testDir1), ());
TEST(Platform::IsFileExistsByFullPath(testDir2), ());
TEST_EQUAL(Platform::ERR_DIRECTORY_NOT_EMPTY, Platform::RmDir(testDir1), ());
TEST(Platform::IsFileExistsByFullPath(filePath), ());
TEST(Platform::IsFileExistsByFullPath(testDir1), ());
TEST(Platform::IsFileExistsByFullPath(testDir2), ());
TEST(Platform::RmDirRecursively(testDir1), ());
TEST(!Platform::IsFileExistsByFullPath(filePath), ());
TEST(!Platform::IsFileExistsByFullPath(testDir1), ());
TEST(!Platform::IsFileExistsByFullPath(testDir2), ());
}
UNIT_TEST(MkDirRecursively)
{
using namespace platform::tests_support;
auto const writablePath = GetPlatform().WritableDir();
auto const workPath = base::JoinPath(writablePath, "MkDirRecursively");
auto const resetDir = [](std::string const & path)
{
if (Platform::IsFileExistsByFullPath(path) && !Platform::RmDirRecursively(path))
return false;
return Platform::MkDirChecked(path);
};
CHECK(resetDir(workPath), ());
auto const path = base::JoinPath(workPath, "test1", "test2", "test3");
TEST(Platform::MkDirRecursively(path), ());
TEST(Platform::IsFileExistsByFullPath(path), ());
TEST(Platform::IsDirectory(path), ());
CHECK(resetDir(workPath), ());
auto const filePath = base::JoinPath(workPath, "test1");
SCOPE_GUARD(removeTestFile, bind(&base::DeleteFileX, filePath));
{
FileWriter testFile(filePath);
}
TEST(!Platform::MkDirRecursively(path), ());
TEST(!Platform::IsFileExistsByFullPath(path), ());
CHECK(Platform::RmDirRecursively(workPath), ());
}
UNIT_TEST(Platform_ThreadRunner)
{
{
Platform::ThreadRunner m_runner;
bool called = false;
GetPlatform().RunTask(Platform::Thread::File, [&called]
{
called = true;
testing::Notify();
});
testing::Wait();
TEST(called, ());
}
GetPlatform().RunTask(Platform::Thread::File, []
{
TEST(false, ("The task must not be posted when thread runner is dead. "
"But app must not be crashed. It is normal behaviour during destruction"));
});
}
UNIT_TEST(GetFileCreationTime_GetFileModificationTime)
{
auto const now = std::time(nullptr);
std::string_view constexpr kContent{"HOHOHO"};
std::string const fileName = GetPlatform().WritablePathForFile(TEST_FILE_NAME);
{
FileWriter testFile(fileName);
testFile.Write(kContent.data(), kContent.size());
}
SCOPE_GUARD(removeTestFile, bind(&base::DeleteFileX, fileName));
auto const creationTime = Platform::GetFileCreationTime(fileName);
TEST_GREATER_OR_EQUAL(creationTime, now, ());
{
FileWriter testFile(fileName);
testFile.Write(kContent.data(), kContent.size());
}
auto const modificationTime = Platform::GetFileModificationTime(fileName);
TEST_GREATER_OR_EQUAL(modificationTime, creationTime, ());
}

View file

@ -0,0 +1,69 @@
#include "testing/testing.hpp"
#include "platform/utm_mgrs_utils.hpp"
#include <string>
using namespace utm_mgrs_utils;
UNIT_TEST(FormatUTM)
{
TEST_EQUAL(FormatUTM(42.0, -93.0), "15T 500000 4649776", ());
TEST_EQUAL(FormatUTM(31.77597, 35.23406), "36R 711554 3517777", ());
TEST_EQUAL(FormatUTM(-34.81449, -58.54016), "21H 359135 6146448", ());
TEST_EQUAL(FormatUTM(36.2361322, -115.0820944), "11S 672349 4011845", ());
TEST_EQUAL(FormatUTM(50.77535, 6.08389), "32U 294409 5628898", ());
TEST_EQUAL(FormatUTM(40.71435, -74.00597), "18T 583960 4507523", ());
TEST_EQUAL(FormatUTM(-41.28646, 174.77624), "60G 313784 5427057", ());
TEST_EQUAL(FormatUTM(-33.92487, 18.42406), "34H 261878 6243186", ());
TEST_EQUAL(FormatUTM(-32.89018, -68.84405), "19H 514586 6360877", ());
TEST_EQUAL(FormatUTM(64.83778, -147.71639), "6W 466013 7190568", ());
TEST_EQUAL(FormatUTM(56.79680, -5.00601), "30V 377486 6296562", ());
TEST_EQUAL(FormatUTM(84, -5.00601), "30X 476594 9328501", ());
TEST_EQUAL(FormatUTM(84.644103, 3.000009), "", ()); // Latitude limit exceeded.
TEST_EQUAL(FormatUTM(12.016469, 188.0), "", ());
}
UNIT_TEST(FormatMGRS)
{
TEST_EQUAL(FormatMGRS(42.0, -93.0, 5), "15T WG 00000 49776", ());
TEST_EQUAL(FormatMGRS(31.77597, 35.23406, 5), "36R YA 11554 17776", ());
TEST_EQUAL(FormatMGRS(-34.81449, -58.54016, 5), "21H UB 59135 46447", ());
TEST_EQUAL(FormatMGRS(36.2361322, -115.0820944, 5), "11S PA 72349 11844", ());
TEST_EQUAL(FormatMGRS(36.2361322, -115.0820944, 4), "11S PA 7234 1184", ());
TEST_EQUAL(FormatMGRS(36.2361322, -115.0820944, 3), "11S PA 723 118", ());
TEST_EQUAL(FormatMGRS(36.2361322, -115.0820944, 2), "11S PA 72 11", ());
TEST_EQUAL(FormatMGRS(36.2361322, -115.0820944, 1), "11S PA 7 1", ());
TEST_EQUAL(FormatMGRS(84.644103, 3.000009, 5), "", ()); // Some converters generate string "Z AB 31142 05767"
TEST_EQUAL(FormatMGRS(-81.016469, 8.745519, 5), "", ()); // Some converters generate string "B BX 51947 87732"
TEST_EQUAL(FormatMGRS(12.016469, 188.0, 2), "", ());
// Test data from https://s3.amazonaws.com/mgrs.io/mgrsToGeo_WE.txt
TEST_EQUAL(FormatMGRS(0.000005, 0.304981, 5), "31N BA 00000 00000", ());
TEST_EQUAL(FormatMGRS(4.518520, 48.296629, 5), "39N TF 00000 00000", ());
TEST_EQUAL(FormatMGRS(9.045438, 50.090125, 5), "39P VL 00000 00000", ());
TEST_EQUAL(FormatMGRS(63.115494, 91.017708, 5), "46V DR 00000 00000", ());
TEST_EQUAL(FormatMGRS(40.626644, 137.364687, 5), "53T QF 00000 00000", ());
TEST_EQUAL(FormatMGRS(0.000005, -4.797048, 5), "30N UF 00000 00000", ());
TEST_EQUAL(FormatMGRS(0.000005, -0.592324, 5), "30N YF 67993 00000", ());
TEST_EQUAL(FormatMGRS(0.000005, 6.592333, 5), "32N KF 32007 00000", ());
TEST_EQUAL(FormatMGRS(0.000005, 54.592333, 5), "40N BF 32007 00000", ());
TEST_EQUAL(FormatMGRS(76.463950, 58.675211, 5), "40X EK 43763 87577", ());
TEST_EQUAL(FormatMGRS(-49.578078, 85.150396, 5), "45F UF 66291 06635", ());
TEST_EQUAL(FormatMGRS(-45.089793, 132.812340, 5), "53G LL 27848 04746", ());
TEST_EQUAL(FormatMGRS(4.514602, 138.603457, 5), "54N TK 34069 99447", ());
TEST_EQUAL(FormatMGRS(-67.547521, -142.303616, 5), "07D DF 44443 06997", ());
TEST_EQUAL(FormatMGRS(-62.908852, -154.887008, 5), "05E ML 04130 23160", ());
TEST_EQUAL(FormatMGRS(4.514602, -144.603447, 5), "06N YK 65931 99447", ());
TEST_EQUAL(FormatMGRS(4.514602, -137.396543, 5), "08N KK 34069 99447", ());
TEST_EQUAL(FormatMGRS(9.028532, -144.637149, 5), "06P YQ 59760 98847", ());
TEST_EQUAL(FormatMGRS(54.109208, -60.059588, 5), "20U PE 92211 99669", ());
TEST_EQUAL(FormatMGRS(54.109208, -53.940397, 5), "22U CE 07789 99669", ());
TEST_EQUAL(FormatMGRS(-45.089793, -53.187660, 5), "22G CR 27848 04746", ());
TEST_EQUAL(FormatMGRS(-31.596034, -18.161583, 5), "27J YF 69322 00842", ());
TEST_EQUAL(FormatMGRS(-22.559756, -11.111475, 5), "29K KR 82882 03678", ());
TEST_EQUAL(FormatMGRS(0.000005, 0.592333, 5), "31N BA 32007 00000", ());
}