diff --git a/android/jni/com/mapswithme/maps/MWMApplication.cpp b/android/jni/com/mapswithme/maps/MWMApplication.cpp index 72721da3d1..2345a85c81 100644 --- a/android/jni/com/mapswithme/maps/MWMApplication.cpp +++ b/android/jni/com/mapswithme/maps/MWMApplication.cpp @@ -17,6 +17,7 @@ #include "../../../../../map/dialog_settings.hpp" #include "../../../../../platform/settings.hpp" +#include "../../../../../platform/preferred_languages.hpp" extern "C" @@ -147,13 +148,16 @@ extern "C" guides::GuideInfo info; if (g_framework->NativeFramework()->GetGuideInfo(storage::toNative(index), info)) { - const jclass giClazz = env->FindClass("com/mapswithme/maps/guides/GuideInfo"); - const jmethodID constrId = env->GetMethodID(giClazz, "", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"); + const jclass giClass = env->FindClass("com/mapswithme/maps/guides/GuideInfo"); + const jmethodID methodID = env->GetMethodID(giClass, + "", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"); - return env->NewObject(giClazz, constrId, - jni::ToJavaString(env, info.m_appName), - jni::ToJavaString(env, info.m_appId), - jni::ToJavaString(env, info.m_appUrl)); + string const lang = languages::CurrentLanguage(); + return env->NewObject(giClass, methodID, + jni::ToJavaString(env, info.GetAppID()), + jni::ToJavaString(env, info.GetURL()), + jni::ToJavaString(env, info.GetAdTitle(lang)), + jni::ToJavaString(env, info.GetAdMessage(lang))); } return 0; diff --git a/android/jni/com/mapswithme/platform/Language.cpp b/android/jni/com/mapswithme/platform/Language.cpp index 897e033b92..55a491e570 100644 --- a/android/jni/com/mapswithme/platform/Language.cpp +++ b/android/jni/com/mapswithme/platform/Language.cpp @@ -5,7 +5,8 @@ #include "../../../../../std/string.hpp" -#define DEFAULT_LANG "en" + +static char const * DEFAULT_LANG = "en"; /// This function is called from native c++ code string GetAndroidSystemLanguage() @@ -18,27 +19,24 @@ string GetAndroidSystemLanguage() } jclass localeClass = env->FindClass("java/util/Locale"); - ASSERT(localeClass, ("Can't find java class java/util/Locale")); + ASSERT(localeClass, ()); jmethodID localeGetDefaultId = env->GetStaticMethodID(localeClass, "getDefault", "()Ljava/util/Locale;"); - ASSERT(localeGetDefaultId, ("Can't find static java/util/Locale.getDefault() method")); + ASSERT(localeGetDefaultId, ()); jobject localeInstance = env->CallStaticObjectMethod(localeClass, localeGetDefaultId); - ASSERT(localeInstance, ("Locale.getDefault() returned NULL")); + ASSERT(localeInstance, ()); jmethodID localeGetLanguageId = env->GetMethodID(localeClass, "getLanguage", "()Ljava/lang/String;"); - ASSERT(localeGetLanguageId, ("Can't find java/util/Locale.getLanguage() method")); + ASSERT(localeGetLanguageId, ()); jstring langString = (jstring)env->CallObjectMethod(localeInstance, localeGetLanguageId); - ASSERT(langString, ("Locale.getLanguage() returned NULL")); + ASSERT(langString, ()); - char const * langUtf8 = env->GetStringUTFChars(langString, 0); - string result(DEFAULT_LANG); - if (langUtf8 != 0) - { - result = langUtf8; - env->ReleaseStringUTFChars(langString, langUtf8); - } + string res = jni::ToNativeString(env, langString); + if (res.empty()) + res = DEFAULT_LANG; - return result; + LOG(LDEBUG, ("System language:", res)); + return res; } diff --git a/android/src/com/mapswithme/maps/MWMApplication.java b/android/src/com/mapswithme/maps/MWMApplication.java index 8e49873b8b..b387432f0d 100644 --- a/android/src/com/mapswithme/maps/MWMApplication.java +++ b/android/src/com/mapswithme/maps/MWMApplication.java @@ -91,12 +91,10 @@ public class MWMApplication extends android.app.Application implements MapStorag if (Utils.hasAnyGoogleStoreInstalled()) { final GuideInfo info = getGuideInfoForIndex(idx); - if (info != null && !GuidesUtils.isGuideInstalled(info.getAppId(), this)) + if (info != null && !GuidesUtils.isGuideInstalled(info.mAppId, this)) { final Notifier notifier = new Notifier(this); - notifier.placeGuideAvailable(info.getAppName(), - info.getAppId(), - getMapStorage().countryName(idx)); + notifier.placeGuideAvailable(info.mAppId, info.mTitle, info.mMessage); } } } diff --git a/android/src/com/mapswithme/maps/background/Notifier.java b/android/src/com/mapswithme/maps/background/Notifier.java index 763c68d081..b1039b7e46 100644 --- a/android/src/com/mapswithme/maps/background/Notifier.java +++ b/android/src/com/mapswithme/maps/background/Notifier.java @@ -86,12 +86,8 @@ public class Notifier .setSmallIcon(R.drawable.ic_notification); } - public void placeGuideAvailable(String guideName, String packageName, String country) + public void placeGuideAvailable(String packageName, String title, String content) { - // TODO: Add string resources - final String title = mContext.getString(R.string.noti_guide_title, country); - final String content = mContext.getString(R.string.noti_guide_content, guideName); - final PendingIntent pi = PendingIntent .getActivity(mContext, 0, GuidesUtils.getGoogleStoreIntentForPackage(packageName), 0); diff --git a/android/src/com/mapswithme/maps/guides/GuideInfo.java b/android/src/com/mapswithme/maps/guides/GuideInfo.java index eca4cebca7..ec39d9451e 100644 --- a/android/src/com/mapswithme/maps/guides/GuideInfo.java +++ b/android/src/com/mapswithme/maps/guides/GuideInfo.java @@ -2,29 +2,16 @@ package com.mapswithme.maps.guides; public class GuideInfo { - private final String mAppName; - private final String mAppId; - private final String mAppUrl; + public final String mAppId; + public final String mAppUrl; + public final String mTitle; + public final String mMessage; - public GuideInfo(String appName, String appId, String appUrl) + public GuideInfo(String appId, String appUrl, String title, String message) { - this.mAppName = appName; this.mAppId = appId; this.mAppUrl = appUrl; - } - - public String getAppName() - { - return mAppName; - } - - public String getAppId() - { - return mAppId; - } - - public String getAppUrl() - { - return mAppUrl; + this.mTitle = title; + this.mMessage = message; } } diff --git a/iphone/Maps/Classes/MapsAppDelegate.mm b/iphone/Maps/Classes/MapsAppDelegate.mm index 4a291660bf..af26c1bf97 100644 --- a/iphone/Maps/Classes/MapsAppDelegate.mm +++ b/iphone/Maps/Classes/MapsAppDelegate.mm @@ -8,9 +8,13 @@ #include #include "Framework.h" + #include "../../../storage/storage.hpp" + #include "../../../platform/settings.hpp" #include "../../../platform/platform.hpp" +#include "../../../platform/preferred_languages.hpp" + #define NOTIFICATION_ALERT_VIEW_TAG 665 @@ -200,7 +204,7 @@ void InitLocalizedStrings() if ([[dict objectForKey:@"Proposal"] isEqual:@"OpenGuides"]) { self.lastGuidesUrl = [dict objectForKey:@"GuideUrl"]; - UIAlertView * view = [[UIAlertView alloc] initWithTitle:@"Guide With Me" message:@"Get GuideWithMe, free travel guide on UK" delegate:self cancelButtonTitle:NSLocalizedString(@"cancel", nil) otherButtonTitles:NSLocalizedString(@"get_it_now", nil), nil]; + UIAlertView * view = [[UIAlertView alloc] initWithTitle:[dict objectForKey:@"GuideTitle"] message:[dict objectForKey:@"GuideMessage"] delegate:self cancelButtonTitle:NSLocalizedString(@"cancel", nil) otherButtonTitles:NSLocalizedString(@"get_it_now", nil), nil]; view.tag = NOTIFICATION_ALERT_VIEW_TAG; [view show]; [view release]; @@ -339,23 +343,35 @@ void InitLocalizedStrings() { } --(BOOL) ShowNotificationWithGuideInfo:(guides::GuideInfo &)guide +-(BOOL) ShowNotificationWithGuideInfo:(guides::GuideInfo const &)guide { - guides::GuidesManager guidesManager = GetFramework().GetGuidesManager(); - bool wasAdvertised = guidesManager.WasAdvertised(guide.m_appId); - if (wasAdvertised || [[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:[NSString stringWithUTF8String:guide.m_appId.c_str()]]]) + guides::GuidesManager & guidesManager = GetFramework().GetGuidesManager(); + string const appID = guide.GetAppID(); + + if (guidesManager.WasAdvertised(appID) || + [[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:[NSString stringWithUTF8String:appID.c_str()]]]) return NO; + UILocalNotification * notification = [[UILocalNotification alloc] init]; notification.fireDate = [NSDate dateWithTimeIntervalSinceNow:0]; notification.repeatInterval = 0; notification.timeZone = [NSTimeZone defaultTimeZone]; notification.soundName = UILocalNotificationDefaultSoundName; - notification.alertBody = @"Get GuideWithMe, free travel guide on UK"; - notification.userInfo = @{@"Proposal" : @"OpenGuides", @"GuideUrl" : [NSString stringWithUTF8String:guide.m_appUrl.c_str()], @"GuideName" : [NSString stringWithUTF8String:guide.m_appName.c_str()]}; + string const lang = languages::CurrentLanguage(); + NSString * message = [NSString stringWithUTF8String:guide.GetAdMessage(lang).c_str()]; + notification.alertBody = message; + notification.userInfo = @{ + @"Proposal" : @"OpenGuides", + @"GuideUrl" : [NSString stringWithUTF8String:guide.GetURL().c_str()], + @"GuideTitle" : [NSString stringWithUTF8String:guide.GetAdTitle(lang).c_str()], + @"GuideMessage" : message + }; + [[UIApplication sharedApplication] scheduleLocalNotification:notification]; [notification release]; - guidesManager.SetWasAdvertised(guide.m_appId); + + guidesManager.SetWasAdvertised(appID); return YES; } diff --git a/map/framework.cpp b/map/framework.cpp index 6e72dd865a..4b6c02f393 100644 --- a/map/framework.cpp +++ b/map/framework.cpp @@ -1723,15 +1723,16 @@ bool Framework::GetGuideInfo(storage::TIndex const & index, guides::GuideInfo & m_storage.GetGuideManager().GetGuideInfo(m_storage.CountryFileName(index), info)); } -void Framework::GetGuidesInfosWithDownloadedMaps(vector & guides) +void Framework::GetGuidesInfosWithDownloadedMaps(vector & guides) const { - vector maps; + vector maps; GetLocalMaps(maps); + for (size_t i = 0; i < maps.size(); ++i) { guides::GuideInfo tmp; my::GetNameWithoutExt(maps[i]); - if (GetGuidesManager().GetGuideInfo(maps[i], tmp)) + if (m_storage.GetGuideManager().GetGuideInfo(maps[i], tmp)) guides.push_back(tmp); } } diff --git a/map/framework.hpp b/map/framework.hpp index c5a55f069a..9dbf98f2b6 100644 --- a/map/framework.hpp +++ b/map/framework.hpp @@ -55,8 +55,6 @@ class CountryStatusDisplay; class BenchmarkEngine; - - /// Uncomment line to make fixed position settings and /// build version for screenshots. //#define FIXED_LOCATION @@ -481,7 +479,7 @@ public: /// @name Guides //@{ public: - guides::GuidesManager & GetGuidesManager(); + guides::GuidesManager & GetGuidesManager() { return m_storage.GetGuideManager(); } bool GetGuideInfo(storage::TIndex const & index, guides::GuideInfo & info) const; void GetGuidesInfosWithDownloadedMaps(vector & guides) const; //@} diff --git a/storage/guides.cpp b/storage/guides.cpp index cfe03ee04f..c96cdec24b 100644 --- a/storage/guides.cpp +++ b/storage/guides.cpp @@ -2,6 +2,7 @@ #include "../../coding/file_writer.hpp" #include "../../coding/file_reader.hpp" +#include "../../coding/internal/file_data.hpp" #include "../../platform/platform.hpp" #include "../../platform/settings.hpp" @@ -16,14 +17,73 @@ #include "../../3party/jansson/myjansson.hpp" +namespace +{ char const * GUIDE_UPDATE_TIME_KEY = "GuideUpdateTime"; -char const * GUIDE_ADVERTISE_KEY "GuideAdvertised:"; +char const * GUIDE_ADVERTISE_KEY = "GuideAdvertised:"; int const GUIDE_UPDATE_PERIOD = 60 * 60 * 24; - +} namespace guides { +string GetStringImpl(json_t * j) +{ + char const * s = json_string_value(j); + return (s ? s : ""); +} + +string GuideInfo::GetString(char const * key) const +{ + return GetStringImpl(json_object_get(m_obj.get(), key)); +} + +string GuideInfo::GetAdForLang(string const & lang, char const * key) const +{ + json_t * jKey = json_object_get(m_obj.get(), key); + ASSERT(jKey, (key)); + + string s = GetStringImpl(json_object_get(jKey, lang.c_str())); + if (s.empty()) + { + s = GetStringImpl(json_object_get(jKey, "en")); + ASSERT(!s.empty(), ()); + } + return s; +} + +string GuideInfo::GetURL() const +{ + return GetString("url"); +} + +string GuideInfo::GetAppID() const +{ + return GetString("appId"); +} + +string GuideInfo::GetAdTitle(string const & lang) const +{ + return GetAdForLang(lang, "adTitles"); +} + +string GuideInfo::GetAdMessage(string const & lang) const +{ + return GetAdForLang(lang, "adMessages"); +} + +bool GuideInfo::IsValid() const +{ + return (!GetURL().empty() && !GetAppID().empty()); +} + +string DebugPrint(GuideInfo const & r) +{ + ostringstream ss; + ss << "URL: " << r.GetURL() << "; ID: " << r.GetAppID(); + return ss.str(); +} + bool GuidesManager::RestoreFromFile() { try @@ -58,11 +118,10 @@ void GuidesManager::UpdateGuidesData() } } -bool GuidesManager::GetGuideInfo(string const & countryId, GuideInfo & appInfo) const +bool GuidesManager::GetGuideInfo(string const & id, GuideInfo & appInfo) const { - map::const_iterator const it = m_countryToInfoMapping.find(countryId); - - if (it != m_countryToInfoMapping.end()) + MapT::const_iterator const it = m_file2Info.find(id); + if (it != m_file2Info.end()) { appInfo = it->second; return true; @@ -80,13 +139,35 @@ string GuidesManager::GetDataFileName() const return OMIM_OS_NAME "-" GUIDES_DATA_FILE_SUFFIX; } +string GuidesManager::GetDataFileFullPath() const +{ + return GetPlatform().WritableDir() + GetDataFileName(); +} + void GuidesManager::OnFinish(downloader::HttpRequest & request) { if (request.Status() == downloader::HttpRequest::ECompleted) { string const & data = request.Data(); - if (ValidateAndParseGuidesData(data)) - SaveToFile(); + if (ValidateAndParseGuidesData(data) && !m_file2Info.empty()) + { + // Save only if file was parsed successfully and info exists! + + string const path = GetDataFileFullPath(); + try + { + FileWriter writer(path); + writer.Write(data.c_str(), data.size()); + } + catch (Writer::Exception const & ex) + { + LOG(LWARNING, (ex.Msg())); + + // Delete file in case of error + // (app will get the default one from bundle on start). + (void)my::DeleteFileX(path); + } + } else LOG(LWARNING, ("Request data is invalid:", request.Data())); } @@ -98,79 +179,55 @@ void GuidesManager::OnFinish(downloader::HttpRequest & request) bool GuidesManager::ValidateAndParseGuidesData(string const & jsonData) { + typedef my::Json::Exception ParseException; + try { my::Json root(jsonData.c_str()); - void * iter = json_object_iter(root.get()); + void * iter = json_object_iter(root.get()); - map temp; + MapT temp; while (iter) { - char const * key = json_object_iter_key(iter); - json_t * value = json_object_get(root.get(), key); - - GuideInfo info; - info.m_appId = json_string_value(json_object_get(value, "appId")); - info.m_appName = json_string_value(json_object_get(value, "name")); - info.m_appUrl = json_string_value(json_object_get(value, "url")); - temp[key] = info; + json_t * entry = json_object_iter_value(iter); + if (entry) + { + GuideInfo info(entry); + if (info.IsValid()) + { + json_t * keys = json_object_get(entry, "keys"); + for (size_t i = 0; i < json_array_size(keys); ++i) + { + char const * key = json_string_value(json_array_get(keys, i)); + if (key) + temp[key] = info; + } + } + } iter = json_object_iter_next(root.get(), iter); } - m_countryToInfoMapping.swap(temp); + m_file2Info.swap(temp); return true; } - catch (my::Json::Exception const &) + catch (ParseException const & ex) { - LOG(LWARNING, ("Failded to parse guides data.")); + LOG(LWARNING, ("Failed to parse guides data:", ex.Msg())); return false; } } -bool GuidesManager::WasAdvertised(string const & countryId) +bool GuidesManager::WasAdvertised(string const & appID) { bool flag = false; - Settings::Get(GUIDE_ADVERTISE_KEY + countryId, flag); + Settings::Get(GUIDE_ADVERTISE_KEY + appID, flag); return flag; } -void GuidesManager::SetWasAdvertised(string const & countryId) +void GuidesManager::SetWasAdvertised(string const & appID) { - Settings::Set(GUIDE_ADVERTISE_KEY + countryId, true); -} - -void GuidesManager::SaveToFile() const -{ - string const path = GetPlatform().WritableDir() + GetDataFileName(); - FileWriter writer(path); - - char braces[] = { '{', '}' }; - - writer.Write(&braces[0], 1); - - if (!m_countryToInfoMapping.empty()) - { - bool isFirst = true; - map::const_iterator it; - for (it = m_countryToInfoMapping.begin(); it != m_countryToInfoMapping.end(); ++it) - { - ostringstream node; - node << (isFirst ? "" : " ,"); - isFirst = false; - - GuideInfo info = it->second; - node << "\"" + it->first + "\": {" - << "\"name\": \"" << info.m_appName << "\", " - << "\"url\": \"" << info.m_appUrl << "\", " - << "\"appId\": \"" << info.m_appId << "\"}"; - - string const nodeStr = node.str(); - writer.Write(nodeStr.data(), nodeStr.size()); - } - } - - writer.Write(&braces[1], 1); + Settings::Set(GUIDE_ADVERTISE_KEY + appID, true); } } diff --git a/storage/guides.hpp b/storage/guides.hpp index 8d76f94869..6f83968395 100644 --- a/storage/guides.hpp +++ b/storage/guides.hpp @@ -1,54 +1,72 @@ #pragma once +#include "../platform/http_request.hpp" + #include "../std/string.hpp" #include "../std/map.hpp" #include "../std/scoped_ptr.hpp" +#include "../std/noncopyable.hpp" -#include "../platform/http_request.hpp" +#include "../../3party/jansson/jansson_handle.hpp" namespace guides { -struct GuideInfo +class GuideInfo { - GuideInfo() {} + my::JsonHandle m_obj; - GuideInfo(string const & appName, string const & appUrl, string const & appId) - : m_appName(appName), m_appUrl(appUrl), m_appId(appId) - {} + string GetString(char const * key) const; + string GetAdForLang(string const & lang, char const * key) const; - string m_appName; - string m_appUrl; - string m_appId; +public: + GuideInfo(json_struct_t * obj = 0) : m_obj(obj) {} - bool operator == (GuideInfo const & other) const - { - return (m_appName == other.m_appName - && m_appId == other.m_appId - && m_appUrl == other.m_appUrl); - } + string GetURL() const; + string GetAppID() const; + string GetAdTitle(string const & lang) const; + string GetAdMessage(string const & lang) const; + + bool operator == (GuideInfo const & r) const { return (m_obj.get() == r.m_obj.get()); } + + bool IsValid() const; }; -class GuidesManager +string DebugPrint(GuideInfo const & r); + + +class GuidesManager : private noncopyable { /// @name Guides managment //@{ public: void UpdateGuidesData(); bool RestoreFromFile(); - void SaveToFile() const; - bool GetGuideInfo(string const & countryId, GuideInfo & appInfo) const; + + /// @param[in] id MWM file name without extension as a key. + bool GetGuideInfo(string const & id, GuideInfo & appInfo) const; + + /// @param[in] appID Guide app package id. + //@{ + bool WasAdvertised(string const & appID); + void SetWasAdvertised(string const & appID); + //@} + bool ValidateAndParseGuidesData(string const & jsonData); - bool WasAdvertised(string const & countryId); - void SetWasAdvertised(string const & countryId); + + /// Public visibility for unit tests only! + string GetDataFileFullPath() const; private: void OnFinish(downloader::HttpRequest & request); - string GetDataFileName() const; string GetGuidesDataUrl() const; + string GetDataFileName() const; + + /// Map from mwm file name (key) to guide info. + typedef map MapT; + MapT m_file2Info; - map m_countryToInfoMapping; scoped_ptr m_httpRequest; //@} }; diff --git a/storage/storage_tests/guides_tests.cpp b/storage/storage_tests/guides_tests.cpp index aae48337da..c8ea290045 100644 --- a/storage/storage_tests/guides_tests.cpp +++ b/storage/storage_tests/guides_tests.cpp @@ -2,112 +2,191 @@ #include "../guides.hpp" -UNIT_TEST(ValidTest) -{ - guides::GuidesManager manager; - string str = "{\"Guernsey\":{\"name\": \"UK Travel Guide with Me\",\"url\": \"https://itunes.apple.com/app/uk-travel-guide-with-me/id687855665\",\"appId\": \"com.guideswithme.uk\"}}"; +#include "../../platform/platform.hpp" - TEST(!manager.ValidateAndParseGuidesData("invalidtest"), ("")); - TEST(manager.ValidateAndParseGuidesData("{}"), ("")); +#include "../../coding/file_writer.hpp" +#include "../../coding/file_reader.hpp" - TEST(manager.ValidateAndParseGuidesData(str),("Has 'Guernsey'")); -} -UNIT_TEST(ParseDataTest) -{ - guides::GuidesManager manager; - string str = "{\"Guernsey\":{\"name\": \"UK Travel Guide with Me\",\"url\": \"https://itunes.apple.com/app/uk-travel-guide-with-me/id687855665\",\"appId\": \"com.guideswithme.uk\"}}"; - string key = "Guernsey"; - guides::GuideInfo info; - - manager.ValidateAndParseGuidesData("{}"); - TEST(!manager.GetGuideInfo(key, info), ("Empty data set")); - - manager.ValidateAndParseGuidesData(str); - TEST(manager.GetGuideInfo(key, info), ("Has info for Guernsey")); - TEST(!manager.GetGuideInfo("Minsk", info), ("Has not info for Minsk")); -} - -UNIT_TEST(CorrectlyParseData) +UNIT_TEST(Guides_SmokeTest) { guides::GuidesManager manager; guides::GuideInfo info; - string strLondonIsle = "{\"London\": {\"name\": \"UK Travel Guide with Me\",\"url\": \"https://itunes.apple.com/app/uk-travel-guide-with-me/id687855665\",\"appId\": \"com.guideswithme.uk\"},\"Isle of Man\": {\"name\": \"UK Travel Guide with Me\",\"url\": \"https://play.google.com/store/apps/details?id=com.guidewithme.uk\",\"appId\": \"com.guideswithme.uk\"}}"; - string validKeys[] = {"London", "Isle of Man"}; - string invalidKeys[] = {"Minsk", "Blabla"}; + string str = "{ \"UK\": {" + "\"url\": \"https://itunes.apple.com/app/uk-travel-guide-with-me/id687855665\"," + "\"appId\": \"com.guideswithme.uk\"," + "\"adTitles\": { \"en\": \"UK title\", \"ru\": \"ВБ заголовок\" }," + "\"adMessages\": { \"en\": \"UK message\", \"ru\": \"ВБ сообщение\" }," + "\"keys\": [ \"Guernsey\", \"Mercy\" ]" + "} }"; - TEST(manager.ValidateAndParseGuidesData(strLondonIsle), ("MUST BE PARSED")); - for (int i = 0; i < ARRAY_SIZE(validKeys); ++i) + TEST(!manager.ValidateAndParseGuidesData("invalidtest"), ()); + TEST(manager.ValidateAndParseGuidesData("{}"), ()); + TEST(!manager.GetGuideInfo("Guernsey", info), ()); + + TEST(manager.ValidateAndParseGuidesData(str), ()); + TEST(manager.GetGuideInfo("Guernsey", info), ()); + TEST(info.IsValid(), ()); + + TEST_EQUAL(info.GetAdTitle("en"), "UK title", ()); + TEST_EQUAL(info.GetAdMessage("en"), "UK message", ()); + TEST_EQUAL(info.GetAdTitle("ru"), "ВБ заголовок", ()); + TEST_EQUAL(info.GetAdMessage("ru"), "ВБ сообщение", ()); + TEST_EQUAL(info.GetAdTitle("zh"), "UK title", ()); + TEST_EQUAL(info.GetAdMessage("zh"), "UK message", ()); + + guides::GuideInfo info1; + TEST(manager.GetGuideInfo("Mercy", info1), ()); + TEST(info1.IsValid(), ()); + TEST_EQUAL(info, info1, ()); + + TEST(!manager.GetGuideInfo("Minsk", info), ()); +} + +UNIT_TEST(Guides_CorrectlyParseData) +{ + guides::GuidesManager manager; + guides::GuideInfo info; + + string strLondonIsle = "{ \"UK_1\": {" + "\"url\": \"https://itunes.apple.com/app/uk-travel-guide-with-me/id687855665\"," + "\"appId\": \"com.guideswithme.uk\"," + "\"adTitles\": { \"en\": \"UK title\", \"ru\": \"ВБ заголовок\" }," + "\"adMessages\": { \"en\": \"UK message\", \"ru\": \"ВБ сообщение\" }," + "\"keys\": [ \"London\" ]" + "}, \"UK_2\": {" + "\"url\": \"https://play.google.com/store/apps/details?id=com.guidewithme.uk\"," + "\"appId\": \"com.guideswithme.uk\"," + "\"adTitles\": { \"en\": \"UK title\", \"ru\": \"ВБ заголовок\" }," + "\"adMessages\": { \"en\": \"UK message\", \"ru\": \"ВБ сообщение\" }," + "\"keys\": [ \"Isle of Man\" ]" + "} }"; + + string validKeys[] = { "London", "Isle of Man" }; + string invalidKeys[] = { "london", "Lond", "Isle", "Man" }; + + TEST(manager.ValidateAndParseGuidesData(strLondonIsle), ()); + for (size_t i = 0; i < ARRAY_SIZE(validKeys); ++i) { - string key = validKeys[i]; - TEST(manager.GetGuideInfo(key, info), ("Has info for:", key)); + TEST(manager.GetGuideInfo(validKeys[i], info), (i)); + TEST(info.IsValid(), ()); } - for (int i = 0; i < ARRAY_SIZE(invalidKeys); ++i) - { - string key = invalidKeys[i]; - TEST(!manager.GetGuideInfo(key, info), ("Has no info for:", key)); - } + for (size_t i = 0; i < ARRAY_SIZE(invalidKeys); ++i) + TEST(!manager.GetGuideInfo(invalidKeys[i], info), (i)); - guides::GuideInfo guidesArray [] = + char const * guidesArray[][2] = { - guides::GuideInfo("UK Travel Guide with Me", "https://itunes.apple.com/app/uk-travel-guide-with-me/id687855665", "com.guideswithme.uk"), - guides::GuideInfo("UK Travel Guide with Me", "https://play.google.com/store/apps/details?id=com.guidewithme.uk", "com.guideswithme.uk") + { "https://itunes.apple.com/app/uk-travel-guide-with-me/id687855665", "com.guideswithme.uk" }, + { "https://play.google.com/store/apps/details?id=com.guidewithme.uk", "com.guideswithme.uk" } }; - for (int i = 0; i < ARRAY_SIZE(validKeys); ++i) + + for (size_t i = 0; i < ARRAY_SIZE(validKeys); ++i) { - string key = validKeys[i]; - manager.GetGuideInfo(key, info); - TEST((info == guidesArray[i]), (i)); + TEST(manager.GetGuideInfo(validKeys[i], info), ()); + TEST(info.IsValid(), ()); + TEST_EQUAL(info.GetURL(), guidesArray[i][0], (i)); + TEST_EQUAL(info.GetAppID(), guidesArray[i][1], (i)); } } -UNIT_TEST(ComplexNames) +UNIT_TEST(Guides_ComplexNames) { guides::GuidesManager manager; guides::GuideInfo info; - string strLondonIsle = "{\"Côte_d'Ivoire\": {\"name\": \"Côte_d'Ivoire Travel Guide with Me\",\"url\": \"https://itunes.apple.com/app/uk-travel-guide-with-me/id687855665\",\"appId\": \"com.guideswithme.uk\"},\"Беларусь\": {\"name\": \"UK Travel Guide with Me\",\"url\": \"https://play.google.com/store/apps/details?id=com.guidewithme.uk\",\"appId\": \"com.guideswithme.uk\"}}"; - string validKeys[] = {"Côte_d'Ivoire", "Беларусь"}; - string invalidKeys[] = {"Не Белурусь", "Côte_d'IvoireCôte_d'IvoireCôte_d'Ivoire"}; + string strLondonIsle = "{\"Côte_d'Ivoire\": {" + "\"url\": \"https://itunes.apple.com/app/uk-travel-guide-with-me/id687855665\"," + "\"appId\": \"com.guideswithme.uk\"," + "\"adTitles\": { \"en\": \"Côte_d'Ivoire title\", \"ru\": \"КДВар заголовок\" }," + "\"adMessages\": { \"en\": \"Côte_d'Ivoire message\", \"ru\": \"КДВар сообщение\" }," + "\"keys\": [ \"Côte_d'Ivoire\" ]" + "}, \"Беларусь\": {" + "\"url\": \"https://play.google.com/store/apps/details?id=com.guidewithme.uk\"," + "\"appId\": \"com.guideswithme.uk\"," + "\"adTitles\": { \"en\": \"Belarus title\", \"ru\": \"РБ заголовок\" }," + "\"adMessages\": { \"en\": \"Belarus message\", \"ru\": \"РБ сообщение\" }," + "\"keys\": [ \"Беларусь\" ]" + "} }"; - TEST(manager.ValidateAndParseGuidesData(strLondonIsle), ("MUST BE PARSED")); - for (int i = 0; i < ARRAY_SIZE(validKeys); ++i) + string validKeys[] = { "Côte_d'Ivoire", "Беларусь" }; + string invalidKeys[] = { "Не Беларусь", "Côte_d'IvoireCôte_d'IvoireCôte_d'Ivoire" }; + + TEST(manager.ValidateAndParseGuidesData(strLondonIsle), ()); + for (size_t i = 0; i < ARRAY_SIZE(validKeys); ++i) { - string key = validKeys[i]; - TEST(manager.GetGuideInfo(key, info), ("Has info for:", key)); + TEST(manager.GetGuideInfo(validKeys[i], info), (i)); + TEST(info.IsValid(), ()); } - for (int i = 0; i < ARRAY_SIZE(invalidKeys); ++i) - { - string key = invalidKeys[i]; - TEST(!manager.GetGuideInfo(key, info), ("Has no info for:", key)); - } + for (size_t i = 0; i < ARRAY_SIZE(invalidKeys); ++i) + TEST(!manager.GetGuideInfo(invalidKeys[i], info), (i)); } -UNIT_TEST(SaveRestoreFromFile) +UNIT_TEST(Guides_SaveRestoreFromFile) { - guides::GuidesManager manager; guides::GuideInfo info; - string strLondonIsle = "{\"London\": {\"name\": \"UK Travel Guide with Me\",\"url\": \"https://itunes.apple.com/app/uk-travel-guide-with-me/id687855665\",\"appId\": \"com.guideswithme.uk\"},\"Isle of Man\": {\"name\": \"UK Travel Guide with Me\",\"url\": \"https://play.google.com/store/apps/details?id=com.guidewithme.uk\",\"appId\": \"com.guideswithme.uk\"}}"; - manager.ValidateAndParseGuidesData(strLondonIsle); - manager.SaveToFile(); + string strLondonIsle = "{ \"UK_1\": {" + "\"url\": \"https://itunes.apple.com/app/uk-travel-guide-with-me/id687855665\"," + "\"appId\": \"com.guideswithme.uk\"," + "\"adTitles\": { \"en\": \"UK title\", \"ru\": \"ВБ заголовок\" }," + "\"adMessages\": { \"en\": \"UK message\", \"ru\": \"ВБ сообщение\" }," + "\"keys\": [ \"London\" ]" + "}, \"UK_2\": {" + "\"url\": \"https://play.google.com/store/apps/details?id=com.guidewithme.uk\"," + "\"appId\": \"com.guideswithme.uk\"," + "\"adTitles\": { \"en\": \"UK title\", \"ru\": \"ВБ заголовок\" }," + "\"adMessages\": { \"en\": \"UK message\", \"ru\": \"ВБ сообщение\" }," + "\"keys\": [ \"Isle of Man\" ]" + "} }"; - TEST(manager.RestoreFromFile(), ("File must exist.")); + TEST(manager.ValidateAndParseGuidesData(strLondonIsle), ()); - string validKeys[] = {"London", "Isle of Man"}; - guides::GuideInfo guidesArray [] = + string const path = manager.GetDataFileFullPath(); { - guides::GuideInfo("UK Travel Guide with Me", "https://itunes.apple.com/app/uk-travel-guide-with-me/id687855665", "com.guideswithme.uk"), - guides::GuideInfo("UK Travel Guide with Me", "https://play.google.com/store/apps/details?id=com.guidewithme.uk", "com.guideswithme.uk") + FileWriter writer(path); + writer.Write(strLondonIsle.c_str(), strLondonIsle.size()); + } + + TEST(manager.RestoreFromFile(), ()); + + string validKeys[] = { "London", "Isle of Man" }; + char const * guidesArray [][2] = + { + { "https://itunes.apple.com/app/uk-travel-guide-with-me/id687855665", "com.guideswithme.uk" }, + { "https://play.google.com/store/apps/details?id=com.guidewithme.uk", "com.guideswithme.uk" } }; - for (int i = 0; i < ARRAY_SIZE(validKeys); ++i) + + for (size_t i = 0; i < ARRAY_SIZE(validKeys); ++i) { - string key = validKeys[i]; - manager.GetGuideInfo(key, info); - TEST((info == guidesArray[i]), (i)); + TEST(manager.GetGuideInfo(validKeys[i], info), (i)); + TEST(info.IsValid(), ()); + TEST_EQUAL(info.GetURL(), guidesArray[i][0], (i)); + TEST_EQUAL(info.GetAppID(), guidesArray[i][1], (i)); + } + + FileWriter::DeleteFileX(path); +} + +UNIT_TEST(Guides_CheckDataFiles) +{ + string const path = GetPlatform().WritableDir(); + string arr[] = { "android-guides.json", "ios-guides.json" }; + + guides::GuidesManager manager; + guides::GuideInfo info; + + for (size_t i = 0; i < ARRAY_SIZE(arr); ++i) + { + FileReader reader(path + arr[i]); + string str; + reader.ReadAsString(str); + + TEST(manager.ValidateAndParseGuidesData(str), ()); + TEST(manager.GetGuideInfo("UK_England", info), ()); + TEST(info.IsValid(), ()); } }