diff --git a/android/jni/com/mapswithme/country/country_helper.cpp b/android/jni/com/mapswithme/country/country_helper.cpp index 6e9f6796a3..248fc86244 100644 --- a/android/jni/com/mapswithme/country/country_helper.cpp +++ b/android/jni/com/mapswithme/country/country_helper.cpp @@ -8,7 +8,7 @@ namespace storage_utils storage::CountryTree & GetTree() { return frm()->GetCountryTree(); } storage::ActiveMapsLayout::TGroup ToGroup(int group) { return static_cast(group); } - storage::TMapOptions ToOptions(int options) {return static_cast(options); } + TMapOptions ToOptions(int options) { return static_cast(options); } jlongArray ToArray(JNIEnv * env, storage::LocalAndRemoteSizeT const & size) { jlongArray result = env->NewLongArray(2); diff --git a/android/jni/com/mapswithme/country/country_helper.hpp b/android/jni/com/mapswithme/country/country_helper.hpp index e6636e7417..f02772d613 100644 --- a/android/jni/com/mapswithme/country/country_helper.hpp +++ b/android/jni/com/mapswithme/country/country_helper.hpp @@ -12,6 +12,6 @@ namespace storage_utils storage::CountryTree & GetTree(); storage::ActiveMapsLayout::TGroup ToGroup(int group); - storage::TMapOptions ToOptions(int options); + TMapOptions ToOptions(int options); jlongArray ToArray(JNIEnv * env, storage::LocalAndRemoteSizeT const & sizes); } diff --git a/android/jni/com/mapswithme/maps/Framework.hpp b/android/jni/com/mapswithme/maps/Framework.hpp index 505e56bc37..e27ba812b4 100644 --- a/android/jni/com/mapswithme/maps/Framework.hpp +++ b/android/jni/com/mapswithme/maps/Framework.hpp @@ -6,6 +6,8 @@ #include "../../../../../search/result.hpp" +#include "../../../../../platform/country_defines.hpp" + #include "../../../../../geometry/avg_vector.hpp" #include "../../../../../base/deferred_task.hpp" @@ -174,8 +176,9 @@ namespace android storage::ActiveMapsLayout::TGroup const & newGroup, int newPosition); virtual void CountryStatusChanged(storage::ActiveMapsLayout::TGroup const & group, int position, storage::TStatus const & oldStatus, storage::TStatus const & newStatus); - virtual void CountryOptionsChanged(storage::ActiveMapsLayout::TGroup const & group, int position, - storage::TMapOptions const & oldOpt, storage::TMapOptions const & newOpt); + virtual void CountryOptionsChanged(storage::ActiveMapsLayout::TGroup const & group, + int position, TMapOptions const & oldOpt, + TMapOptions const & newOpt); virtual void DownloadingProgressUpdate(storage::ActiveMapsLayout::TGroup const & group, int position, storage::LocalAndRemoteSizeT const & progress); }; diff --git a/coding/coding_tests/file_utils_test.cpp b/coding/coding_tests/file_utils_test.cpp index d5c8ca565c..bba7856b39 100644 --- a/coding/coding_tests/file_utils_test.cpp +++ b/coding/coding_tests/file_utils_test.cpp @@ -38,3 +38,24 @@ UNIT_TEST(FileName_Smoke) my::GetNameWithoutExt(name); TEST_EQUAL(name, "test", ()); } + +UNIT_TEST(FileName_GetDirectory) +{ +// TODO (@gorshenin): implement a Clean() method to clean file path +// (remove redundant separators, correctly collapse dots, dot-dots, etc.). +#if !defined(OMIM_OS_WINDOWS) + TEST_EQUAL("/tmp", my::GetDirectory("/tmp/evil\\file"), ()); + TEST_EQUAL(".", my::GetDirectory("evil\\file"), ()); + + TEST_EQUAL("/", my::GetDirectory("/somefile.txt"), ()); + + TEST_EQUAL("/", my::GetDirectory("////somefile"), ()); + TEST_EQUAL("a/b", my::GetDirectory("a/b///somefile.txt"), ()); + + TEST_EQUAL("/a/b", my::GetDirectory("/a/b/c"), ()); + TEST_EQUAL("/a/b", my::GetDirectory("/a/b/c.txt"), ()); + + TEST_EQUAL(".", my::GetDirectory("somefile.txt"), ()); + TEST_EQUAL(".", my::GetDirectory("somefile"), ()); +#endif // !defined(OMIM_OS_WINDOWS) +} diff --git a/coding/file_name_utils.cpp b/coding/file_name_utils.cpp index 9533705ada..3750c3673c 100644 --- a/coding/file_name_utils.cpp +++ b/coding/file_name_utils.cpp @@ -1,9 +1,7 @@ #include "coding/file_name_utils.hpp" - namespace my { - void GetNameWithoutExt(string & name) { string::size_type const i = name.rfind('.'); @@ -24,6 +22,19 @@ void GetNameFromFullPath(string & name) name = name.substr(i+1); } +string GetDirectory(string const & name) +{ + string const sep = GetNativeSeparator(); + size_t const sepSize = sep.size(); + + string::size_type i = name.rfind(sep); + if (i == string::npos) + return "."; + while (i > sepSize && (name.substr(i - sepSize, sepSize) == sep)) + i -= sepSize; + return i == 0 ? sep : name.substr(0, i); +} + string GetNativeSeparator() { #ifdef OMIM_OS_WINDOWS @@ -63,5 +74,4 @@ string JoinFoldersToPath(const vector & folders, const string & file) result += file; return result; } - -} +} // namespace my diff --git a/coding/file_name_utils.hpp b/coding/file_name_utils.hpp index 91f38fda77..ddef2af4a9 100644 --- a/coding/file_name_utils.hpp +++ b/coding/file_name_utils.hpp @@ -13,6 +13,11 @@ namespace my /// Get file name from full path. void GetNameFromFullPath(string & name); + /// Returns all but last components of the path. After dropping the last + /// component, all trailing slashes are removed, unless the result is a + /// root directory. If the argument is a single component, returns ".". + string GetDirectory(string const & path); + /// Get folder separator for specific platform string GetNativeSeparator(); diff --git a/iphone/Maps/ActiveMapsVC.mm b/iphone/Maps/ActiveMapsVC.mm index adba16cadc..0ac86b236a 100644 --- a/iphone/Maps/ActiveMapsVC.mm +++ b/iphone/Maps/ActiveMapsVC.mm @@ -128,7 +128,7 @@ extern NSString * const MapsStatusChangedNotification; return [NSString stringWithUTF8String:self.mapsLayout.GetCountryName(self.selectedGroup, self.selectedPosition).c_str()]; } -- (uint64_t)selectedMapSizeWithOptions:(storage::TMapOptions)options +- (uint64_t)selectedMapSizeWithOptions:(TMapOptions)options { return self.mapsLayout.GetCountrySize(self.selectedGroup, self.selectedPosition, options).second; } diff --git a/iphone/Maps/Classes/CustomAlert/DownloadTransitMapsAlert/MWMDownloadTransitMapAlert.mm b/iphone/Maps/Classes/CustomAlert/DownloadTransitMapsAlert/MWMDownloadTransitMapAlert.mm index e99e4383fa..567183be8d 100644 --- a/iphone/Maps/Classes/CustomAlert/DownloadTransitMapsAlert/MWMDownloadTransitMapAlert.mm +++ b/iphone/Maps/Classes/CustomAlert/DownloadTransitMapsAlert/MWMDownloadTransitMapAlert.mm @@ -33,10 +33,12 @@ extern UIColor * const kActiveDownloaderViewColor; MWMDownloadTransitMapAlert *alert = [[[NSBundle mainBundle] loadNibNamed:kDownloadTransitMapAlertNibName owner:self options:nil] firstObject]; ActiveMapsLayout& layout = GetFramework().GetCountryTree().GetActiveMapLayout(); alert.countryLabel.text = [NSString stringWithUTF8String:layout.GetFormatedCountryName(index).c_str()]; - alert.sizeLabel.text = [NSString stringWithFormat:@"%@ %@", @(layout.GetCountrySize(index, storage::TMapOptions::EMapWithCarRouting).second/(1024 * 1024)), L(@"mb")]; - alert.downloaderBlock = ^{ - layout.DownloadMap(index, storage::TMapOptions::EMapWithCarRouting); - }; + alert.sizeLabel.text = [NSString + stringWithFormat:@"%@ %@", + @(layout.GetCountrySize(index, TMapOptions::EMapWithCarRouting).second / + (1024 * 1024)), + L(@"mb")]; + alert.downloaderBlock = ^{ layout.DownloadMap(index, TMapOptions::EMapWithCarRouting); }; [alert configure]; return alert; } diff --git a/iphone/Maps/Classes/MapCell.h b/iphone/Maps/Classes/MapCell.h index ae2c3d1d35..5630cb06f3 100644 --- a/iphone/Maps/Classes/MapCell.h +++ b/iphone/Maps/Classes/MapCell.h @@ -4,6 +4,7 @@ #import "BadgeView.h" #include "../../storage/storage_defines.hpp" +#include "../../platform/country_defines.hpp" using namespace storage; diff --git a/iphone/Maps/Classes/MapViewController.mm b/iphone/Maps/Classes/MapViewController.mm index 1b9c2dc497..17bc886935 100644 --- a/iphone/Maps/Classes/MapViewController.mm +++ b/iphone/Maps/Classes/MapViewController.mm @@ -676,7 +676,7 @@ typedef NS_ENUM(NSUInteger, UserTouchesAction) LocalAndRemoteSizeT sizes = layout.GetRemoteCountrySizes(idx); uint64_t sizeToDownload = sizes.first; TMapOptions options = static_cast(opt); - if(options & TMapOptions::ECarRouting) + if(HasOptions(options, TMapOptions::ECarRouting)) sizeToDownload += sizes.second; NSString * name = [NSString stringWithUTF8String:layout.GetCountryName(idx).c_str()]; diff --git a/iphone/Maps/CountryTreeVC.mm b/iphone/Maps/CountryTreeVC.mm index a192c7e890..ede8d89437 100644 --- a/iphone/Maps/CountryTreeVC.mm +++ b/iphone/Maps/CountryTreeVC.mm @@ -135,7 +135,7 @@ extern NSString * const MapsStatusChangedNotification; return [NSString stringWithUTF8String:self.tree.GetChildName(self.selectedPosition).c_str()]; } -- (uint64_t)selectedMapSizeWithOptions:(storage::TMapOptions)options +- (uint64_t)selectedMapSizeWithOptions:(TMapOptions)options { return self.tree.GetLeafSize(self.selectedPosition, options).second; } diff --git a/map/active_maps_layout.cpp b/map/active_maps_layout.cpp index e4fce36dd1..8dc200ded3 100644 --- a/map/active_maps_layout.cpp +++ b/map/active_maps_layout.cpp @@ -647,13 +647,13 @@ void ActiveMapsLayout::NotifyOptionsChanged(TGroup const & group, int position, TMapOptions ActiveMapsLayout::ValidOptionsForDownload(TMapOptions const & options) { - return options | TMapOptions::EMap; + return SetOptions(options, TMapOptions::EMap); } TMapOptions ActiveMapsLayout::ValidOptionsForDelete(TMapOptions const & options) { - if (options & TMapOptions::EMap) - return options | TMapOptions::ECarRouting; + if (HasOptions(options, TMapOptions::EMap)) + return SetOptions(options, TMapOptions::ECarRouting); return options; } diff --git a/map/active_maps_layout.hpp b/map/active_maps_layout.hpp index 8c3ae584ac..27c1e101e4 100644 --- a/map/active_maps_layout.hpp +++ b/map/active_maps_layout.hpp @@ -3,6 +3,8 @@ #include "storage/storage_defines.hpp" #include "storage/index.hpp" +#include "platform/country_defines.hpp" + #include "base/buffer_vector.hpp" #include "std/string.hpp" diff --git a/map/country_status_display.cpp b/map/country_status_display.cpp index 0fdf2fd697..bcad44fdfb 100644 --- a/map/country_status_display.cpp +++ b/map/country_status_display.cpp @@ -449,7 +449,7 @@ void CountryStatusDisplay::OnButtonClicked(gui::Element const * button) TMapOptions options = TMapOptions::EMap; if (button == m_secondaryButton.get()) - options |= TMapOptions::ECarRouting; + options = SetOptions(options, TMapOptions::ECarRouting); ASSERT(m_downloadCallback, ()); int opt = static_cast(options); diff --git a/map/country_status_display.hpp b/map/country_status_display.hpp index e7aced902b..c9ab39e62a 100644 --- a/map/country_status_display.hpp +++ b/map/country_status_display.hpp @@ -3,7 +3,7 @@ #include "map/active_maps_layout.hpp" #include "gui/element.hpp" -#include "storage/storage_defines.hpp" +#include "platform/country_defines.hpp" #ifdef OMIM_OS_ANDROID #include "../base/mutex.hpp" @@ -70,7 +70,9 @@ private: virtual void CountryStatusChanged(storage::ActiveMapsLayout::TGroup const & group, int position, storage::TStatus const & oldStatus, storage::TStatus const & newStatus); virtual void CountryOptionsChanged(storage::ActiveMapsLayout::TGroup const & group, int position, - storage::TMapOptions const & oldOpt, storage::TMapOptions const & newOpt){} + TMapOptions const & oldOpt, TMapOptions const & newOpt) + { + } virtual void DownloadingProgressUpdate(storage::ActiveMapsLayout::TGroup const & group, int position, storage::LocalAndRemoteSizeT const & progress); diff --git a/map/framework.cpp b/map/framework.cpp index 88fb96885f..f6c80d25e0 100644 --- a/map/framework.cpp +++ b/map/framework.cpp @@ -396,20 +396,20 @@ double Framework::GetVisualScale() const void Framework::DeleteCountry(TIndex const & index, TMapOptions opt) { - if (opt & TMapOptions::EMap) + if (HasOptions(opt, TMapOptions::EMap)) opt = TMapOptions::EMapWithCarRouting; if (!m_storage.DeleteFromDownloader(index)) { CountryFile const & file = m_storage.CountryByIndex(index).GetFile(); - if (opt & TMapOptions::EMap) + if (HasOptions(opt, TMapOptions::EMap)) { if (m_model.DeleteMap(file.GetFileWithExt(TMapOptions::EMap))) InvalidateRect(GetCountryBounds(file.GetFileWithoutExt()), true); } - if (opt & TMapOptions::ECarRouting) + if (HasOptions(opt, TMapOptions::ECarRouting)) m_routingSession.DeleteIndexFile(file.GetFileWithExt(TMapOptions::ECarRouting)); m_storage.NotifyStatusChanged(index); @@ -470,7 +470,7 @@ void Framework::ShowCountry(TIndex const & index) void Framework::UpdateAfterDownload(string const & fileName, TMapOptions opt) { - if (opt & TMapOptions::EMap) + if (HasOptions(opt, TMapOptions::EMap)) { // Delete old (splitted) map files, if any. char const * arr[] = { "Japan", "Brazil" }; @@ -496,7 +496,7 @@ void Framework::UpdateAfterDownload(string const & fileName, TMapOptions opt) } // Replace routing file. - if (opt & TMapOptions::ECarRouting) + if (HasOptions(opt, TMapOptions::ECarRouting)) { string routingName = fileName + ROUTING_FILE_EXTENSION; m_routingSession.DeleteIndexFile(routingName); diff --git a/map/framework.hpp b/map/framework.hpp index 0959f6d9d9..d42279b2b8 100644 --- a/map/framework.hpp +++ b/map/framework.hpp @@ -33,6 +33,7 @@ #include "storage/storage.hpp" +#include "platform/country_defines.hpp" #include "platform/location.hpp" #ifndef USE_DRAPE @@ -135,7 +136,7 @@ protected: /// This function is called by m_storage to notify that country downloading is finished. /// @param[in] file Country file name (without extensions). - void UpdateAfterDownload(string const & file, storage::TMapOptions opt); + void UpdateAfterDownload(string const & file, TMapOptions opt); //my::Timer m_timer; inline double ElapsedSeconds() const @@ -211,9 +212,9 @@ public: /// @name This functions is used by Downloader UI. //@{ /// options - flags that signal about parts of map that must be deleted - void DeleteCountry(storage::TIndex const & index, storage::TMapOptions opt); + void DeleteCountry(storage::TIndex const & index, TMapOptions opt); /// options - flags that signal about parts of map that must be downloaded - void DownloadCountry(storage::TIndex const & index, storage::TMapOptions opt); + void DownloadCountry(storage::TIndex const & index, TMapOptions opt); storage::TStatus GetCountryStatus(storage::TIndex const & index) const; string GetCountryName(storage::TIndex const & index) const; diff --git a/platform/country_defines.cpp b/platform/country_defines.cpp index fa5a5ec31f..5a3ab9e8f5 100644 --- a/platform/country_defines.cpp +++ b/platform/country_defines.cpp @@ -2,13 +2,23 @@ #include "base/assert.hpp" +bool HasOptions(TMapOptions options, TMapOptions bits) +{ + return (static_cast(options) & static_cast(bits)) == static_cast(bits); +} + +TMapOptions SetOptions(TMapOptions options, TMapOptions bits) +{ + return static_cast(static_cast(options) | static_cast(bits)); +} + string DebugPrint(TMapOptions options) { switch (options) { case TMapOptions::ENothing: return "Nothing"; - case TMapOptions::EMapOnly: + case TMapOptions::EMap: return "MapOnly"; case TMapOptions::ECarRouting: return "CarRouting"; diff --git a/platform/country_defines.hpp b/platform/country_defines.hpp index 57ba90de80..11e6f97842 100644 --- a/platform/country_defines.hpp +++ b/platform/country_defines.hpp @@ -1,15 +1,17 @@ #pragma once #include "std/string.hpp" -#include "3party/enum_flags.hpp" -ENUM_FLAGS(TMapOptions) -enum class TMapOptions +enum class TMapOptions : uint8_t { ENothing = 0x0, - EMapOnly = 0x1, + EMap = 0x1, ECarRouting = 0x2, EMapWithCarRouting = 0x3 }; +bool HasOptions(TMapOptions options, TMapOptions bits); + +TMapOptions SetOptions(TMapOptions options, TMapOptions bits); + string DebugPrint(TMapOptions options); diff --git a/platform/country_file.cpp b/platform/country_file.cpp index 2055e5cb14..8fffb2a52d 100644 --- a/platform/country_file.cpp +++ b/platform/country_file.cpp @@ -4,6 +4,8 @@ #include "base/assert.hpp" #include "std/sstream.hpp" +namespace platform +{ CountryFile::CountryFile() : m_mapSize(0), m_routingSize(0) {} CountryFile::CountryFile(string const & name) : m_name(name), m_mapSize(0), m_routingSize(0) {} @@ -14,7 +16,7 @@ string CountryFile::GetNameWithExt(TMapOptions file) const { switch (file) { - case TMapOptions::EMapOnly: + case TMapOptions::EMap: return m_name + DATA_FILE_EXTENSION; case TMapOptions::ECarRouting: return m_name + DATA_FILE_EXTENSION + ROUTING_FILE_EXTENSION; @@ -33,9 +35,9 @@ void CountryFile::SetRemoteSizes(uint32_t mapSize, uint32_t routingSize) uint32_t CountryFile::GetRemoteSize(TMapOptions filesMask) const { uint32_t size = 0; - if (filesMask & TMapOptions::EMapOnly) + if (HasOptions(filesMask, TMapOptions::EMap)) size += m_mapSize; - if (filesMask & TMapOptions::ECarRouting) + if (HasOptions(filesMask, TMapOptions::ECarRouting)) size += m_routingSize; return size; } @@ -46,3 +48,4 @@ string DebugPrint(CountryFile const & file) os << "CountryFile [" << file.m_name << "]"; return os.str(); } +} // namespace platform diff --git a/platform/country_file.hpp b/platform/country_file.hpp index 342ad8b4fc..e23a81e85e 100644 --- a/platform/country_file.hpp +++ b/platform/country_file.hpp @@ -3,9 +3,11 @@ #include "platform/country_defines.hpp" #include "std/string.hpp" +namespace platform +{ // This class represents a country file name and sizes of // corresponding map files on a server, which should correspond to an -// entry in counties.txt file. Also, this class can be used to +// entry in countries.txt file. Also, this class can be used to // represent a hand-made-country name. Instances of this class don't // represent paths to disk files. class CountryFile @@ -34,3 +36,4 @@ private: }; string DebugPrint(CountryFile const & file); +} // namespace platform diff --git a/platform/local_country_file.cpp b/platform/local_country_file.cpp index 621a4e7a3f..48c9f92da8 100644 --- a/platform/local_country_file.cpp +++ b/platform/local_country_file.cpp @@ -8,6 +8,8 @@ #include "std/sstream.hpp" +namespace platform +{ LocalCountryFile::LocalCountryFile() : m_version(0), m_files(TMapOptions::ENothing), m_mapSize(0), m_routingSize() { @@ -32,21 +34,23 @@ void LocalCountryFile::SyncWithDisk() Platform & platform = GetPlatform(); - string const mapPath = GetPath(TMapOptions::EMapOnly); - if (platform.GetFileSizeByName(mapPath, m_mapSize)) - m_files = m_files | TMapOptions::EMapOnly; + if (platform.GetFileSizeByName(GetPath(TMapOptions::EMap), m_mapSize)) + m_files = SetOptions(m_files, TMapOptions::EMap); string const routingPath = GetPath(TMapOptions::ECarRouting); if (platform.GetFileSizeByName(routingPath, m_routingSize)) - m_files = m_files | TMapOptions::ECarRouting; + m_files = SetOptions(m_files, TMapOptions::ECarRouting); } void LocalCountryFile::DeleteFromDisk() { - for (TMapOptions file : {TMapOptions::EMapOnly, TMapOptions::ECarRouting}) + for (TMapOptions file : {TMapOptions::EMap, TMapOptions::ECarRouting}) { if (OnDisk(file)) - my::DeleteFileX(GetPath(file)); + { + bool const deleted = my::DeleteFileX(GetPath(file)); + ASSERT(deleted, (file, "from", *this, "wasn't deleted from disk.")); + } } } @@ -58,9 +62,9 @@ string LocalCountryFile::GetPath(TMapOptions file) const uint32_t LocalCountryFile::GetSize(TMapOptions filesMask) const { uint64_t size64 = 0; - if (filesMask & TMapOptions::EMapOnly) + if (HasOptions(filesMask, TMapOptions::EMap)) size64 += m_mapSize; - if (filesMask & TMapOptions::ECarRouting) + if (HasOptions(filesMask, TMapOptions::ECarRouting)) size64 += m_routingSize; uint32_t const size32 = static_cast(size64); ASSERT_EQUAL(size32, size64, ()); @@ -73,12 +77,17 @@ bool LocalCountryFile::operator<(LocalCountryFile const & rhs) const return m_countryFile < rhs.m_countryFile; if (m_version != rhs.m_version) return m_version < rhs.m_version; - return m_files < rhs.m_files; + if (m_directory != rhs.m_directory) + return m_directory < rhs.m_directory; + if (m_files != rhs.m_files) + return m_files < rhs.m_files; + return false; } bool LocalCountryFile::operator==(LocalCountryFile const & rhs) const { - return m_countryFile == rhs.m_countryFile && m_version == rhs.m_version && m_files == rhs.m_files; + return m_directory == rhs.m_directory && m_countryFile == rhs.m_countryFile && + m_version == rhs.m_version && m_files == rhs.m_files; } // static @@ -97,3 +106,4 @@ string DebugPrint(LocalCountryFile const & file) << file.m_version << ", " << DebugPrint(file.m_files) << "]"; return os.str(); } +} // namespace platform diff --git a/platform/local_country_file.hpp b/platform/local_country_file.hpp index 87570bdb06..c73975f772 100644 --- a/platform/local_country_file.hpp +++ b/platform/local_country_file.hpp @@ -6,6 +6,8 @@ #include "std/string.hpp" #include "std/vector.hpp" +namespace platform +{ // This class represents a path to disk files corresponding to some // country region. class LocalCountryFile @@ -71,3 +73,4 @@ private: }; string DebugPrint(LocalCountryFile const & file); +} // namespace platform diff --git a/platform/local_country_file_utils.cpp b/platform/local_country_file_utils.cpp index e01b5c69bd..cba1ac3dbb 100644 --- a/platform/local_country_file_utils.cpp +++ b/platform/local_country_file_utils.cpp @@ -2,17 +2,71 @@ #include "platform/platform.hpp" #include "coding/file_name_utils.hpp" - +#include "coding/file_writer.hpp" +#include "base/string_utils.hpp" +#include "base/logging.hpp" #include "std/algorithm.hpp" #include "std/cctype.hpp" -namespace local_country_file_utils +namespace platform { namespace { size_t const kMaxTimestampLength = 18; + +bool IsSpecialFile(string const & file) { return file == "." || file == ".."; } } // namespace +void CleanupMapsDirectory() +{ + Platform & platform = GetPlatform(); + + string const mapsDir = platform.WritableDir(); + + // Remove partially downloaded maps. + { + Platform::FilesList files; + string const regexp = "\\" DATA_FILE_EXTENSION "\\.(downloading2?$|resume2?$)"; + platform.GetFilesByRegExp(mapsDir, regexp, files); + for (string const & file : files) + FileWriter::DeleteFileX(file); + } + + // Find and remove Brazil and Japan maps. + vector localFiles; + FindAllLocalMaps(localFiles); + for (LocalCountryFile & localFile : localFiles) + { + CountryFile const countryFile = localFile.GetCountryFile(); + if (countryFile.GetNameWithoutExt() == "Japan" || countryFile.GetNameWithoutExt() == "Brazil") + { + localFile.SyncWithDisk(); + localFile.DeleteFromDisk(); + } + } + + // Try to delete empty folders. + Platform::FilesList subdirs; + Platform::GetFilesByType(mapsDir, Platform::FILE_TYPE_DIRECTORY, subdirs); + for (string const & subdir : subdirs) + { + int64_t version; + if (ParseVersion(subdir, version)) + { + vector files; + string const subdirPath = my::JoinFoldersToPath(mapsDir, subdir); + platform.GetFilesByType(subdirPath, + Platform::FILE_TYPE_REGULAR | Platform::FILE_TYPE_DIRECTORY, files); + if (all_of(files.begin(), files.end(), &IsSpecialFile)) + { + Platform::EError ret = Platform::RmDir(subdirPath); + ASSERT_EQUAL(Platform::ERR_OK, ret, + ("Can't remove empty directory:", subdirPath, "error:", ret)); + } + } + } +} + void FindAllLocalMapsInDirectory(string const & directory, int64_t version, vector & localFiles) { @@ -22,11 +76,10 @@ void FindAllLocalMapsInDirectory(string const & directory, int64_t version, platform.GetFilesByRegExp(directory, ".*\\" DATA_FILE_EXTENSION "$", files); for (string const & file : files) { + // Remove DATA_FILE_EXTENSION and use base name as a country file name. string name = file; my::GetNameWithoutExt(name); - - CountryFile const countryFile(name); - localFiles.emplace_back(directory, countryFile, version); + localFiles.emplace_back(directory, CountryFile(name), version); } } @@ -34,7 +87,13 @@ void FindAllLocalMaps(vector & localFiles) { vector allFiles; Platform & platform = GetPlatform(); - for (string const & directory : {platform.ResourcesDir(), platform.WritableDir()}) + vector baseDirectories = { + platform.ResourcesDir(), platform.WritableDir(), + }; + sort(baseDirectories.begin(), baseDirectories.end()); + baseDirectories.erase(unique(baseDirectories.begin(), baseDirectories.end()), + baseDirectories.end()); + for (string const & directory : baseDirectories) { FindAllLocalMapsInDirectory(directory, 0 /* version */, allFiles); @@ -47,12 +106,35 @@ void FindAllLocalMaps(vector & localFiles) FindAllLocalMapsInDirectory(my::JoinFoldersToPath(directory, subdir), version, allFiles); } } -#ifdef OMIM_OS_ANDROID +#if defined(OMIM_OS_ANDROID) // On Android World and WorldCoasts can be stored in alternative /Android/obb/ path. - FindAllLocalMapsInDirectory("/Android/obb/", 0 /* version */, allFiles); -#endif - sort(allFiles.begin(), allFiles.end()); - allFiles.erase(unique(allFiles.begin(), allFiles.end()), allFiles.end()); + for (string const & file : {WORLD_FILE_NAME, WORLD_COASTS_FILE_NAME}) + { + bool found = false; + for (LocalCountryFile const & localFile : allFiles) + { + if (localFile.GetCountryFile().GetNameWithoutExt() == file) + { + found = true; + break; + } + } + if (!found) + { + try + { + ModelReaderPtr reader = platform.GetReader(file + DATA_FILE_EXTENSION); + allFiles.emplace_back(my::GetDirectory(reader.GetName()), CountryFile(file), + 0 /* version */); + } + catch (FileAbsentException const & e) + { + LOG(LERROR, ("Can't find map file for", file, ".")); + } + } + } +#endif // defined(OMIM_OS_ANDROID) + localFiles.insert(localFiles.end(), allFiles.begin(), allFiles.end()); } @@ -60,14 +142,41 @@ bool ParseVersion(string const & s, int64_t & version) { if (s.empty() || s.size() > kMaxTimestampLength) return false; - if (!all_of(s.begin(), s.end(), [](char c) -> bool - { - return isdigit(c); - })) - return false; - version = 0; - for (char c : s) - version = version * 10 + c - '0'; + int64_t v = 0; + for (char const c : s) + { + if (!isdigit(c)) + return false; + v = v * 10 + c - '0'; + } + version = v; return true; } -} // namespace local_country_file_utils + +shared_ptr PreparePlaceForCountryFiles(CountryFile const & countryFile, + int64_t version) +{ + Platform & platform = GetPlatform(); + if (version == 0) + return make_shared(platform.WritableDir(), countryFile, version); + string const directory = + my::JoinFoldersToPath(platform.WritableDir(), strings::to_string(version)); + switch (platform.MkDir(directory)) + { + case Platform::ERR_OK: + return make_shared(directory, countryFile, version); + case Platform::ERR_FILE_ALREADY_EXISTS: + { + Platform::EFileType type; + if (Platform::GetFileType(directory, type) != Platform::ERR_OK || + type != Platform::FILE_TYPE_DIRECTORY) + { + return shared_ptr(); + } + return make_shared(directory, countryFile, version); + } + default: + return shared_ptr(); + }; +} +} // namespace platform diff --git a/platform/local_country_file_utils.hpp b/platform/local_country_file_utils.hpp index 74b7775375..2b9b852c17 100644 --- a/platform/local_country_file_utils.hpp +++ b/platform/local_country_file_utils.hpp @@ -2,14 +2,47 @@ #include "platform/local_country_file.hpp" +#include "std/shared_ptr.hpp" #include "std/utility.hpp" #include "std/vector.hpp" -namespace local_country_file_utils +namespace platform { +// Removes partially downloaded maps, empty directories and old +// (format v1) maps. Also, removes old (splitted) Japan and Brazil +// maps. +void CleanupMapsDirectory(); + +// Finds all local map files in a directory. Version of these files is +// passed as an argument. void FindAllLocalMapsInDirectory(string const & directory, int64_t version, vector & localFiles); + +// Finds all local map files in resources and writable +// directory. Also, for Android, checks /Android/obb directory. +// Directories should have the following structure: +// +// dir/*.mwm -- map files, base name should correspond to countries.txt, +// -- version is assumed to be zero (unknown). +// dir/*.mwm.routing -- routing files for corresponding map files, +// -- version is assumed to be zero (unknown). +// dir/[0-9]+/*.mwm -- map files, base name should correspond to countries.txt, +// -- version is assumed to be the name of a directory. +// dir/[0-9]{1,18}/*.mwm.routing -- routing file for corresponding map files, +// -- version is assumed to be the name of a directory. +// +// We can't derive version of a map file from its header because +// currently header stores format version + individual mwm's file +// generation timestamp, not timestamp mentioned in countries.txt. void FindAllLocalMaps(vector & localFiles); +// Tries to parse a version from a string of size not longer than 18 +// symbols and representing an unsigned decimal number. Leading zeroes +// are allowed. bool ParseVersion(string const & s, int64_t & version); -} // namespace local_country_file_utils + +// When version is zero, uses writable directory, otherwise, creates +// directory with name equal to decimal representation of version. +shared_ptr PreparePlaceForCountryFiles(CountryFile const & countryFile, + int64_t version); +} // namespace platform diff --git a/platform/platform.cpp b/platform/platform.cpp index e9c2841a2b..76d0fe2c38 100644 --- a/platform/platform.cpp +++ b/platform/platform.cpp @@ -20,6 +20,8 @@ Platform::EError Platform::ErrnoToError() return ERR_ACCESS_FAILED; case ENOTEMPTY: return ERR_DIRECTORY_NOT_EMPTY; + case EEXIST: + return ERR_FILE_ALREADY_EXISTS; default: return ERR_UNKNOWN; } diff --git a/platform/platform.hpp b/platform/platform.hpp index 28698ed1c2..91f86740d7 100644 --- a/platform/platform.hpp +++ b/platform/platform.hpp @@ -25,6 +25,7 @@ public: ERR_FILE_DOES_NOT_EXIST, ERR_ACCESS_FAILED, ERR_DIRECTORY_NOT_EMPTY, + ERR_FILE_ALREADY_EXISTS, ERR_UNKNOWN }; diff --git a/platform/platform_qt.cpp b/platform/platform_qt.cpp index 5d4e185dde..004c273446 100644 --- a/platform/platform_qt.cpp +++ b/platform/platform_qt.cpp @@ -59,6 +59,8 @@ int Platform::VideoMemoryLimit() const Platform::EError Platform::MkDir(string const & dirName) const { + if (QDir().exists(dirName.c_str())) + return Platform::ERR_FILE_ALREADY_EXISTS; if(!QDir().mkdir(dirName.c_str())) { LOG(LWARNING, ("Can't create directory: ", dirName)); diff --git a/platform/platform_tests/country_file_tests.cpp b/platform/platform_tests/country_file_tests.cpp index b160876a5b..136aceede6 100644 --- a/platform/platform_tests/country_file_tests.cpp +++ b/platform/platform_tests/country_file_tests.cpp @@ -3,24 +3,26 @@ #include "defines.hpp" #include "platform/country_file.hpp" +namespace platform +{ UNIT_TEST(CountryFile_Smoke) { CountryFile countryFile("TestCountry"); TEST_EQUAL("TestCountry", countryFile.GetNameWithoutExt(), ()); - TEST_EQUAL("TestCountry" DATA_FILE_EXTENSION, countryFile.GetNameWithExt(TMapOptions::EMapOnly), - ()); + TEST_EQUAL("TestCountry" DATA_FILE_EXTENSION, countryFile.GetNameWithExt(TMapOptions::EMap), ()); TEST_EQUAL("TestCountry" DATA_FILE_EXTENSION ROUTING_FILE_EXTENSION, countryFile.GetNameWithExt(TMapOptions::ECarRouting), ()); TEST_EQUAL(0, countryFile.GetRemoteSize(TMapOptions::ENothing), ()); - TEST_EQUAL(0, countryFile.GetRemoteSize(TMapOptions::EMapOnly), ()); + TEST_EQUAL(0, countryFile.GetRemoteSize(TMapOptions::EMap), ()); TEST_EQUAL(0, countryFile.GetRemoteSize(TMapOptions::ECarRouting), ()); TEST_EQUAL(0, countryFile.GetRemoteSize(TMapOptions::EMapWithCarRouting), ()); countryFile.SetRemoteSizes(1 /* mapSize */, 2 /* routingSize */); TEST_EQUAL(0, countryFile.GetRemoteSize(TMapOptions::ENothing), ()); - TEST_EQUAL(1, countryFile.GetRemoteSize(TMapOptions::EMapOnly), ()); + TEST_EQUAL(1, countryFile.GetRemoteSize(TMapOptions::EMap), ()); TEST_EQUAL(2, countryFile.GetRemoteSize(TMapOptions::ECarRouting), ()); TEST_EQUAL(3, countryFile.GetRemoteSize(TMapOptions::EMapWithCarRouting), ()); } +} // namespace platform diff --git a/platform/platform_tests/local_country_file_tests.cpp b/platform/platform_tests/local_country_file_tests.cpp index 0ae32e93e5..5594a4e63f 100644 --- a/platform/platform_tests/local_country_file_tests.cpp +++ b/platform/platform_tests/local_country_file_tests.cpp @@ -16,22 +16,73 @@ #include "std/bind.hpp" #include "std/set.hpp" -using namespace local_country_file_utils; - +namespace platform +{ namespace { -void CreateTestDir(string const & testDir) +template +bool Contains(vector const & v, T const & t) { - Platform & platform = GetPlatform(); - TEST(!Platform::IsFileExistsByFullPath(testDir), - ("Please, remove", testDir, "before running the test.")); - TEST_EQUAL(Platform::ERR_OK, platform.MkDir(testDir), ("Can't create directory", testDir)); + return find(v.begin(), v.end(), t) != v.end(); } +class ScopedTestDir +{ +public: + ScopedTestDir(string const & path) : m_path(path), m_reset(false) + { + Platform & platform = GetPlatform(); + Platform::EError ret = platform.MkDir(m_path); + switch (ret) + { + case Platform::ERR_OK: + break; + case Platform::ERR_FILE_ALREADY_EXISTS: + Platform::EFileType type; + TEST_EQUAL(Platform::ERR_OK, Platform::GetFileType(m_path, type), ()); + TEST_EQUAL(Platform::FILE_TYPE_DIRECTORY, type, ()); + break; + default: + CHECK(false, ("Can't create directory:", m_path, "error:", ret)); + break; + } + } + + ~ScopedTestDir() + { + if (m_reset) + return; + + Platform::EError ret = Platform::RmDir(m_path); + switch (ret) + { + case Platform::ERR_OK: + break; + case Platform::ERR_FILE_DOES_NOT_EXIST: + LOG(LWARNING, (m_path, "was deleted before destruction of ScopedTestDir.")); + break; + case Platform::ERR_DIRECTORY_NOT_EMPTY: + LOG(LWARNING, ("There are files in", m_path)); + break; + default: + LOG(LWARNING, ("Platform::RmDir() error:", ret)); + break; + } + } + + inline void Reset() { m_reset = true; } + + inline string const GetPath() const { return m_path; } + +private: + string const m_path; + bool m_reset; + + DISALLOW_COPY_AND_MOVE(ScopedTestDir); +}; + void CreateTestFile(string const & testFile, string const & contents) { - TEST(!Platform::IsFileExistsByFullPath(testFile), - ("Please, remove", testFile, "before running the test.")); { FileWriter writer(testFile); writer.Write(contents.data(), contents.size()); @@ -59,6 +110,7 @@ UNIT_TEST(LocalCountryFile_ParseVersion) TEST_EQUAL(version, 999999999999999999, ()); TEST(!ParseVersion("1000000000000000000", version), ()); + TEST(!ParseVersion("00000000000000000000000000000000123", version), ()); TEST(!ParseVersion("", version), ()); TEST(!ParseVersion("150309 ", version), ()); @@ -75,8 +127,7 @@ UNIT_TEST(LocalCountryFile_Smoke) LocalCountryFile localFile("/test-dir", countryFile, 150309); - TEST_EQUAL("/test-dir/TestCountry" DATA_FILE_EXTENSION, localFile.GetPath(TMapOptions::EMapOnly), - ()); + TEST_EQUAL("/test-dir/TestCountry" DATA_FILE_EXTENSION, localFile.GetPath(TMapOptions::EMap), ()); TEST_EQUAL("/test-dir/TestCountry" DATA_FILE_EXTENSION ROUTING_FILE_EXTENSION, localFile.GetPath(TMapOptions::ECarRouting), ()); @@ -86,14 +137,14 @@ UNIT_TEST(LocalCountryFile_Smoke) // Any statement is true about elements of an empty set. TEST(localFile.OnDisk(TMapOptions::ENothing), ()); - TEST(!localFile.OnDisk(TMapOptions::EMapOnly), ()); + TEST(!localFile.OnDisk(TMapOptions::EMap), ()); TEST(!localFile.OnDisk(TMapOptions::ECarRouting), ()); TEST(!localFile.OnDisk(TMapOptions::EMapWithCarRouting), ()); TEST_EQUAL("/test-dir", localFile.GetDirectory(), ()); TEST_EQUAL(0, localFile.GetSize(TMapOptions::ENothing), ()); - TEST_EQUAL(0, localFile.GetSize(TMapOptions::EMapOnly), ()); + TEST_EQUAL(0, localFile.GetSize(TMapOptions::EMap), ()); TEST_EQUAL(0, localFile.GetSize(TMapOptions::ECarRouting), ()); TEST_EQUAL(0, localFile.GetSize(TMapOptions::EMapWithCarRouting), ()); @@ -109,31 +160,31 @@ UNIT_TEST(LocalCountryFile_DiskFiles) CountryFile countryFile("TestCountry"); countryFile.SetRemoteSizes(1 /* mapSize */, 2 /* routingSize */); - string const testMapFile = my::JoinFoldersToPath( - platform.WritableDir(), countryFile.GetNameWithExt(TMapOptions::EMapOnly)); + string const testMapFile = + my::JoinFoldersToPath(platform.WritableDir(), countryFile.GetNameWithExt(TMapOptions::EMap)); string const testRoutingFile = my::JoinFoldersToPath( platform.WritableDir(), countryFile.GetNameWithExt(TMapOptions::ECarRouting)); LocalCountryFile localFile(platform.WritableDir(), countryFile, 0 /* version */); - TEST(!localFile.OnDisk(TMapOptions::EMapOnly), ()); + TEST(!localFile.OnDisk(TMapOptions::EMap), ()); TEST(!localFile.OnDisk(TMapOptions::ECarRouting), ()); TEST(!localFile.OnDisk(TMapOptions::EMapWithCarRouting), ()); CreateTestFile(testMapFile, "map"); localFile.SyncWithDisk(); - TEST(localFile.OnDisk(TMapOptions::EMapOnly), ()); + TEST(localFile.OnDisk(TMapOptions::EMap), ()); TEST(!localFile.OnDisk(TMapOptions::ECarRouting), ()); TEST(!localFile.OnDisk(TMapOptions::EMapWithCarRouting), ()); - TEST_EQUAL(3, localFile.GetSize(TMapOptions::EMapOnly), ()); + TEST_EQUAL(3, localFile.GetSize(TMapOptions::EMap), ()); CreateTestFile(testRoutingFile, "routing"); localFile.SyncWithDisk(); - TEST(localFile.OnDisk(TMapOptions::EMapOnly), ()); + TEST(localFile.OnDisk(TMapOptions::EMap), ()); TEST(localFile.OnDisk(TMapOptions::ECarRouting), ()); TEST(localFile.OnDisk(TMapOptions::EMapWithCarRouting), ()); - TEST_EQUAL(3, localFile.GetSize(TMapOptions::EMapOnly), ()); + TEST_EQUAL(3, localFile.GetSize(TMapOptions::EMap), ()); TEST_EQUAL(7, localFile.GetSize(TMapOptions::ECarRouting), ()); TEST_EQUAL(10, localFile.GetSize(TMapOptions::EMapWithCarRouting), ()); @@ -144,6 +195,52 @@ UNIT_TEST(LocalCountryFile_DiskFiles) ("Routing file", testRoutingFile, "wasn't deleted by LocalCountryFile.")); } +UNIT_TEST(LocalCountryFile_DirectoryCleanup) +{ + Platform & platform = GetPlatform(); + string const mapsDir = platform.WritableDir(); + + CountryFile japanFile("Japan"); + CountryFile brazilFile("Brazil"); + CountryFile irelandFile("Ireland"); + + ScopedTestDir testDir1(my::JoinFoldersToPath(mapsDir, "1")); + LocalCountryFile japanLocalFile(testDir1.GetPath(), japanFile, 1 /* version */); + CreateTestFile(japanLocalFile.GetPath(TMapOptions::EMap), "Japan"); + + ScopedTestDir testDir2(my::JoinFoldersToPath(mapsDir, "2")); + LocalCountryFile brazilLocalFile(testDir2.GetPath(), brazilFile, 2 /* version */); + CreateTestFile(brazilLocalFile.GetPath(TMapOptions::EMap), "Brazil"); + + ScopedTestDir testDir3(my::JoinFoldersToPath(mapsDir, "3")); + + LocalCountryFile irelandLocalFile(testDir2.GetPath(), irelandFile, 2 /* version */); + CreateTestFile(irelandLocalFile.GetPath(TMapOptions::EMap), "Ireland"); + + vector localFiles; + FindAllLocalMaps(localFiles); + TEST(Contains(localFiles, japanLocalFile), (japanLocalFile)); + TEST(Contains(localFiles, brazilLocalFile), (brazilLocalFile)); + TEST(Contains(localFiles, irelandLocalFile), (irelandLocalFile)); + + CleanupMapsDirectory(); + + japanLocalFile.SyncWithDisk(); + TEST_EQUAL(TMapOptions::ENothing, japanLocalFile.GetFiles(), ()); + TEST(!Platform::IsFileExistsByFullPath(testDir1.GetPath()), ("Empty directory wasn't removed.")); + testDir1.Reset(); + + brazilLocalFile.SyncWithDisk(); + TEST_EQUAL(TMapOptions::ENothing, brazilLocalFile.GetFiles(), ()); + + irelandLocalFile.SyncWithDisk(); + TEST_EQUAL(TMapOptions::EMap, irelandLocalFile.GetFiles(), ()); + irelandLocalFile.DeleteFromDisk(); + + TEST(!Platform::IsFileExistsByFullPath(testDir3.GetPath()), ("Empty directory wasn't removed.")); + testDir3.Reset(); +} + // Creates test-dir and following files: // * test-dir/Ireland.mwm // * test-dir/Netherlands.mwm @@ -158,41 +255,39 @@ UNIT_TEST(LocalCountryFile_DirectoryLookup) Platform & platform = GetPlatform(); - string const testDir = my::JoinFoldersToPath(platform.WritableDir(), "test-dir"); - CreateTestDir(testDir); - MY_SCOPE_GUARD(removeTestDir, bind(&Platform::RmDir, testDir)); + ScopedTestDir testDir(my::JoinFoldersToPath(platform.WritableDir(), "test-dir")); string const testIrelandMapFile = - my::JoinFoldersToPath(testDir, irelandFile.GetNameWithExt(TMapOptions::EMapOnly)); + my::JoinFoldersToPath(testDir.GetPath(), irelandFile.GetNameWithExt(TMapOptions::EMap)); CreateTestFile(testIrelandMapFile, "Ireland-map"); MY_SCOPE_GUARD(removeTestIrelandMapFile, bind(&FileWriter::DeleteFileX, testIrelandMapFile)); string const testNetherlandsMapFile = - my::JoinFoldersToPath(testDir, netherlandsFile.GetNameWithExt(TMapOptions::EMapOnly)); + my::JoinFoldersToPath(testDir.GetPath(), netherlandsFile.GetNameWithExt(TMapOptions::EMap)); CreateTestFile(testNetherlandsMapFile, "Netherlands-map"); MY_SCOPE_GUARD(removeTestNetherlandsMapFile, bind(&FileWriter::DeleteFileX, testNetherlandsMapFile)); - string const testNetherlandsRoutingFile = - my::JoinFoldersToPath(testDir, netherlandsFile.GetNameWithExt(TMapOptions::ECarRouting)); + string const testNetherlandsRoutingFile = my::JoinFoldersToPath( + testDir.GetPath(), netherlandsFile.GetNameWithExt(TMapOptions::ECarRouting)); CreateTestFile(testNetherlandsRoutingFile, "Netherlands-routing"); MY_SCOPE_GUARD(removeTestNetherlandsRoutingFile, bind(&FileWriter::DeleteFileX, testNetherlandsRoutingFile)); vector localFiles; - FindAllLocalMapsInDirectory(testDir, 150309, localFiles); + FindAllLocalMapsInDirectory(testDir.GetPath(), 150309, localFiles); sort(localFiles.begin(), localFiles.end()); for (LocalCountryFile & localFile : localFiles) localFile.SyncWithDisk(); - LocalCountryFile expectedIrelandFile(testDir, irelandFile, 150309); - expectedIrelandFile.m_files = TMapOptions::EMapOnly; + LocalCountryFile expectedIrelandFile(testDir.GetPath(), irelandFile, 150309); + expectedIrelandFile.m_files = TMapOptions::EMap; - LocalCountryFile expectedNetherlandsFile(testDir, netherlandsFile, 150309); + LocalCountryFile expectedNetherlandsFile(testDir.GetPath(), netherlandsFile, 150309); expectedNetherlandsFile.m_files = TMapOptions::EMapWithCarRouting; vector expectedLocalFiles = {expectedIrelandFile, expectedNetherlandsFile}; - + sort(expectedLocalFiles.begin(), expectedLocalFiles.end()); TEST_EQUAL(expectedLocalFiles, localFiles, ()); } @@ -206,18 +301,15 @@ UNIT_TEST(LocalCountryFile_AllLocalFilesLookup) Platform & platform = GetPlatform(); - string const testDir = my::JoinFoldersToPath(platform.WritableDir(), "010101"); - CreateTestDir(testDir); - MY_SCOPE_GUARD(removeTestDir, bind(&Platform::RmDir, testDir)); + ScopedTestDir testDir(my::JoinFoldersToPath(platform.WritableDir(), "010101")); string const testItalyMapFile = - my::JoinFoldersToPath(testDir, italyFile.GetNameWithExt(TMapOptions::EMapOnly)); + my::JoinFoldersToPath(testDir.GetPath(), italyFile.GetNameWithExt(TMapOptions::EMap)); CreateTestFile(testItalyMapFile, "Italy-map"); MY_SCOPE_GUARD(remoteTestItalyMapFile, bind(&FileWriter::DeleteFileX, testItalyMapFile)); vector localFiles; FindAllLocalMaps(localFiles); - LOG(LINFO, (localFiles)); multiset localFilesSet(localFiles.begin(), localFiles.end()); LocalCountryFile expectedWorldFile(platform.WritableDir(), CountryFile(WORLD_FILE_NAME), @@ -228,6 +320,35 @@ UNIT_TEST(LocalCountryFile_AllLocalFilesLookup) CountryFile(WORLD_COASTS_FILE_NAME), 0 /* version */); TEST_EQUAL(1, localFilesSet.count(expectedWorldCoastsFile), ()); - LocalCountryFile expectedItalyFile(testDir, italyFile, 10101); + LocalCountryFile expectedItalyFile(testDir.GetPath(), italyFile, 10101); TEST_EQUAL(1, localFilesSet.count(expectedItalyFile), ()); } + +UNIT_TEST(LocalCountryFile_PreparePlaceForCountryFiles) +{ + Platform & platform = GetPlatform(); + + CountryFile italyFile("Italy"); + LocalCountryFile expectedItalyFile(platform.WritableDir(), italyFile, 0 /* version */); + shared_ptr italyLocalFile = + PreparePlaceForCountryFiles(italyFile, 0 /* version */); + TEST(italyLocalFile.get(), ()); + TEST_EQUAL(expectedItalyFile, *italyLocalFile, ()); + + ScopedTestDir directoryForV1(my::JoinFoldersToPath(platform.WritableDir(), "1")); + + CountryFile germanyFile("Germany"); + LocalCountryFile expectedGermanyFile(directoryForV1.GetPath(), germanyFile, 1 /* version */); + shared_ptr germanyLocalFile = + PreparePlaceForCountryFiles(germanyFile, 1 /* version */); + TEST(germanyLocalFile.get(), ()); + TEST_EQUAL(expectedGermanyFile, *germanyLocalFile, ()); + + CountryFile franceFile("France"); + LocalCountryFile expectedFranceFile(directoryForV1.GetPath(), franceFile, 1 /* version */); + shared_ptr franceLocalFile = + PreparePlaceForCountryFiles(franceFile, 1 /* version */); + TEST(franceLocalFile.get(), ()); + TEST_EQUAL(expectedFranceFile, *franceLocalFile, ()); +} +} // namespace platform diff --git a/qt/draw_widget.cpp b/qt/draw_widget.cpp index 6a7f94772d..92b8aca160 100644 --- a/qt/draw_widget.cpp +++ b/qt/draw_widget.cpp @@ -99,7 +99,7 @@ namespace qt if (opt == -1) layout.RetryDownloading(idx); else - layout.DownloadMap(idx, static_cast(opt)); + layout.DownloadMap(idx, static_cast(opt)); }); m_framework->SetRouteBuildingListener([] (routing::IRouter::ResultCode, vector const &) diff --git a/qt/update_dialog.cpp b/qt/update_dialog.cpp index 9afa8b8172..e94964a90c 100644 --- a/qt/update_dialog.cpp +++ b/qt/update_dialog.cpp @@ -150,10 +150,10 @@ namespace qt QAbstractButton * res = ask.clickedButton(); if (res == btns[0]) - m_framework.DownloadCountry(countryIndex, storage::TMapOptions::EMapWithCarRouting); + m_framework.DownloadCountry(countryIndex, TMapOptions::EMapWithCarRouting); if (res == btns[1]) - m_framework.DeleteCountry(countryIndex, storage::TMapOptions::EMapWithCarRouting); + m_framework.DeleteCountry(countryIndex, TMapOptions::EMapWithCarRouting); } break; @@ -167,18 +167,18 @@ namespace qt ask.setDefaultButton(QMessageBox::No); if (ask.exec() == QMessageBox::Yes) - m_framework.DeleteCountry(countryIndex, storage::TMapOptions::EMapWithCarRouting); + m_framework.DeleteCountry(countryIndex, TMapOptions::EMapWithCarRouting); } break; case TStatus::ENotDownloaded: case TStatus::EDownloadFailed: - m_framework.DownloadCountry(countryIndex, storage::TMapOptions::EMapWithCarRouting); + m_framework.DownloadCountry(countryIndex, TMapOptions::EMapWithCarRouting); break; case TStatus::EInQueue: case TStatus::EDownloading: - m_framework.DeleteCountry(countryIndex, storage::TMapOptions::EMapWithCarRouting); + m_framework.DeleteCountry(countryIndex, TMapOptions::EMapWithCarRouting); break; default: @@ -247,7 +247,7 @@ namespace qt QString statusString; LocalAndRemoteSizeT size(0, 0); - storage::TMapOptions const options = storage::TMapOptions::EMapWithCarRouting; + TMapOptions const options = TMapOptions::EMapWithCarRouting; Storage const & st = GetStorage(); switch (m_framework.GetCountryStatus(index)) diff --git a/storage/country.cpp b/storage/country.cpp index 022a4f06fc..3e39eb674a 100644 --- a/storage/country.cpp +++ b/storage/country.cpp @@ -41,13 +41,15 @@ uint32_t CountryFile::GetRemoteSize(TMapOptions opt) const { switch (opt) { - case TMapOptions::EMap: return m_mapSize; - case TMapOptions::ECarRouting: return m_routingSize; - case TMapOptions::EMapWithCarRouting : return m_mapSize + m_routingSize; + case TMapOptions::ENothing: + return 0; + case TMapOptions::EMap: + return m_mapSize; + case TMapOptions::ECarRouting: + return m_routingSize; + case TMapOptions::EMapWithCarRouting: + return m_mapSize + m_routingSize; } - - ASSERT(false, ()); - return 0; } /* diff --git a/storage/country.hpp b/storage/country.hpp index a5ce677a4c..0140722634 100644 --- a/storage/country.hpp +++ b/storage/country.hpp @@ -4,6 +4,8 @@ #include "storage/simple_tree.hpp" #include "storage/country_decl.hpp" +#include "platform/country_defines.hpp" + #include "defines.hpp" #include "geometry/rect2d.hpp" diff --git a/storage/storage.cpp b/storage/storage.cpp index 3d432e69c4..bad306c873 100644 --- a/storage/storage.cpp +++ b/storage/storage.cpp @@ -62,8 +62,8 @@ namespace storage // Don't queue files with 0-size on server (empty or absent). // Downloader has lots of assertions about it. If car routing was // requested for downloading, try to download it first. - if ((m_init & TMapOptions::ECarRouting) && - (m_pFile->GetRemoteSize(TMapOptions::ECarRouting) > 0)) + if (HasOptions(m_init, TMapOptions::ECarRouting) && + m_pFile->GetRemoteSize(TMapOptions::ECarRouting) > 0) { m_current = TMapOptions::ECarRouting; } @@ -78,10 +78,10 @@ namespace storage TMapOptions const arr[] = { TMapOptions::EMap, TMapOptions::ECarRouting }; for (size_t i = 0; i < ARRAY_SIZE(arr); ++i) { - if (opt & arr[i] && !(m_init & arr[i])) + if (HasOptions(opt, arr[i]) && !HasOptions(m_init, arr[i])) { - m_init |= arr[i]; - m_left |= arr[i]; + m_init = SetOptions(m_init, arr[i]); + m_left = SetOptions(m_left, arr[i]); } } } @@ -114,9 +114,9 @@ namespace storage ASSERT(m_pFile->GetFileSize(TMapOptions::ECarRouting) == 0, ()); #endif - if (m_init & TMapOptions::ECarRouting) + if (HasOptions(m_init, TMapOptions::ECarRouting)) { - ASSERT(m_init & TMapOptions::EMap, ()); + ASSERT(HasOptions(m_init, TMapOptions::EMap), ()); if (currentStatus == TStatus::EOnDisk) { @@ -141,7 +141,7 @@ namespace storage TMapOptions const arr[] = { TMapOptions::EMap, TMapOptions::ECarRouting }; for (size_t i = 0; i < ARRAY_SIZE(arr); ++i) { - if (m_init & arr[i]) + if (HasOptions(m_init, arr[i])) { res.first += m_pFile->GetFileSize(arr[i]); res.second += m_pFile->GetRemoteSize(arr[i]); @@ -157,7 +157,7 @@ namespace storage TMapOptions const arr[] = { TMapOptions::EMap, TMapOptions::ECarRouting }; for (size_t i = 0; i < ARRAY_SIZE(arr); ++i) { - if (m_init & arr[i]) + if (HasOptions(m_init, arr[i])) res += m_pFile->GetRemoteSize(arr[i]); } @@ -311,7 +311,7 @@ namespace storage string const fName = QueuedCountry(*this, index, TMapOptions::ECarRouting).GetFileName(); Platform const & pl = GetPlatform(); if (pl.IsFileExistsByFullPath(pl.WritablePathForFile(fName))) - options |= TMapOptions::ECarRouting; + options = SetOptions(options, TMapOptions::ECarRouting); } } @@ -474,6 +474,7 @@ namespace storage string optionName; switch (cnt.GetInitOptions()) { + case TMapOptions::ENothing: optionName = "Nothing"; break; case TMapOptions::EMap: optionName = "Map"; break; case TMapOptions::ECarRouting: optionName = "CarRouting"; break; case TMapOptions::EMapWithCarRouting: optionName = "MapWithCarRouting"; break; diff --git a/storage/storage_defines.hpp b/storage/storage_defines.hpp index 9cea845c27..5da65a105c 100644 --- a/storage/storage_defines.hpp +++ b/storage/storage_defines.hpp @@ -1,7 +1,5 @@ #pragma once -#include "3party/enum_flags.hpp" - #include "std/stdint.hpp" #include "std/string.hpp" #include "std/utility.hpp" @@ -23,13 +21,5 @@ namespace storage string DebugPrint(TStatus status); - ENUM_FLAGS(TMapOptions) - enum class TMapOptions - { - EMap = 0x1, - ECarRouting = 0x2, - EMapWithCarRouting = 0x3 - }; - typedef pair LocalAndRemoteSizeT; } diff --git a/storage/storage_tests/storage_tests.cpp b/storage/storage_tests/storage_tests.cpp index 5ce8f52b68..60d795d4c7 100644 --- a/storage/storage_tests/storage_tests.cpp +++ b/storage/storage_tests/storage_tests.cpp @@ -190,9 +190,9 @@ void OnCountryDownloaded(string const & mapFileName, TMapOptions files) string const localMapFile = my::JoinFoldersToPath(platform.WritableDir(), mapFileName); string const localRoutingFile = localMapFile + ROUTING_FILE_EXTENSION; - if (files & TMapOptions::EMap) + if (HasOptions(files, TMapOptions::EMap)) CHECK(my::RenameFileX(localMapFile + READY_FILE_EXTENSION, localMapFile), ()); - if (files & TMapOptions::ECarRouting) + if (HasOptions(files, TMapOptions::ECarRouting)) CHECK(my::RenameFileX(localRoutingFile + READY_FILE_EXTENSION, localRoutingFile), ()); }