From 83d889098db7253a8d8bec7940a93724ea724449 Mon Sep 17 00:00:00 2001 From: Vadim ProductEngine Date: Mon, 12 Sep 2011 17:21:07 +0300 Subject: STORM-1577 WIP Forgot to add the unit test. --- indra/newview/tests/lltranslate_test.cpp | 344 +++++++++++++++++++++++++++++++ 1 file changed, 344 insertions(+) create mode 100644 indra/newview/tests/lltranslate_test.cpp (limited to 'indra/newview/tests') diff --git a/indra/newview/tests/lltranslate_test.cpp b/indra/newview/tests/lltranslate_test.cpp new file mode 100644 index 0000000000..0f3429b7b3 --- /dev/null +++ b/indra/newview/tests/lltranslate_test.cpp @@ -0,0 +1,344 @@ +/** + * @file lltranslate_test.cpp + * + * $LicenseInfo:firstyear=2011&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2011, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" + +#include "../test/lltut.h" +#include "../lltranslate.h" +#include "../llversioninfo.h" +#include "../llviewercontrol.h" + +#include "llbufferstream.h" +#include "lltrans.h" +#include "llui.h" + +static const std::string GOOGLE_VALID_RESPONSE1 = +"{\ + \"data\": {\ + \"translations\": [\ + {\ + \"translatedText\": \"привет\",\ + \"detectedSourceLanguage\": \"es\"\ + }\ + ]\ + }\ +}"; + +static const std::string GOOGLE_VALID_RESPONSE2 = +"{\ + \"data\": {\ + \"translations\": [\ + {\ + \"translatedText\": \"привет\"\ + }\ + ]\ + }\ +}\ +"; + +static const std::string GOOGLE_VALID_RESPONSE3 = +"{\ + \"error\": {\ + \"errors\": [\ + {\ + \"domain\": \"global\",\ + \"reason\": \"invalid\",\ + \"message\": \"Invalid Value\"\ + }\ + ],\ + \"code\": 400,\ + \"message\": \"Invalid Value\"\ + }\ +}"; + +static const std::string BING_VALID_RESPONSE1 = +"Привет"; + +static const std::string BING_VALID_RESPONSE2 = +"

Argument Exception

Method: Translate()

Parameter:

\ +

Message: 'from' must be a valid language

\ +

message id=3743.V2_Rest.Translate.58E8454F

"; + +static const std::string BING_VALID_RESPONSE3 = +"

Argument Exception

Method: Translate()

\ +

Parameter: appId

Message: Invalid appId \nParameter name: appId

\ +

message id=3737.V2_Rest.Translate.56016759

"; + +namespace tut +{ + class translate_test + { + protected: + void test_translation( + LLTranslationAPIHandler& handler, + int status, const std::string& resp, + const std::string& exp_trans, const std::string& exp_lang, const std::string& exp_err) + { + std::string translation, detected_lang, err_msg; + bool rc = handler.parseResponse(status, resp, translation, detected_lang, err_msg); + ensure_equals("rc", rc, (status == 200)); + ensure_equals("err_msg", err_msg, exp_err); + ensure_equals("translation", translation, exp_trans); + ensure_equals("detected_lang", detected_lang, exp_lang); + } + + LLGoogleTranslationHandler mGoogle; + LLBingTranslarionHandler mBing; + }; + + typedef test_group translate_test_group_t; + typedef translate_test_group_t::object translate_test_object_t; + tut::translate_test_group_t tut_translate("LLTranslate"); + + template<> template<> + void translate_test_object_t::test<1>() + { + test_translation(mGoogle, 200, GOOGLE_VALID_RESPONSE1, "привет", "es", ""); + } + + template<> template<> + void translate_test_object_t::test<2>() + { + test_translation(mGoogle, 200, GOOGLE_VALID_RESPONSE2, "привет", "", ""); + } + + template<> template<> + void translate_test_object_t::test<3>() + { + test_translation(mGoogle, 400, GOOGLE_VALID_RESPONSE3, "", "", "Invalid Value"); + } + + template<> template<> + void translate_test_object_t::test<4>() + { + test_translation(mGoogle, 400, + "", + "", "", "* Line 1, Column 1\n Syntax error: value, object or array expected.\n"); + } + + template<> template<> + void translate_test_object_t::test<5>() + { + test_translation(mGoogle, 400, + "[]", + "", "", ""); + } + + template<> template<> + void translate_test_object_t::test<6>() + { + test_translation(mGoogle, 400, + "{\"oops\": \"invalid\"}", + "", "", ""); + } + + template<> template<> + void translate_test_object_t::test<7>() + { + test_translation(mGoogle, 400, + "{\"oops\": \"invalid\"}", + "", "", ""); + } + + template<> template<> + void translate_test_object_t::test<8>() + { + test_translation(mGoogle, 400, + "{\"data\": { \"translations\": [ {} ] }}", + "", "", ""); + } + + template<> template<> + void translate_test_object_t::test<9>() + { + test_translation(mGoogle, 400, + "{\"data\": { \"translations\": [ { \"translatedTextZZZ\": \"привет\", \"detectedSourceLanguageZZZ\": \"es\" } ] }}", + "", "", ""); + } + + template<> template<> + void translate_test_object_t::test<10>() + { + test_translation(mBing, 200, BING_VALID_RESPONSE1, "Привет", "", ""); + } + + template<> template<> + void translate_test_object_t::test<11>() + { + test_translation(mBing, 400, BING_VALID_RESPONSE2, "", "", "'from' must be a valid language"); + } + + template<> template<> + void translate_test_object_t::test<12>() + { + test_translation(mBing, 400, BING_VALID_RESPONSE3, "", "", "Invalid appId\nParameter name: appId"); + } + + template<> template<> + void translate_test_object_t::test<13>() + { + test_translation(mBing, 200, + "Привет", + "Привет", "", ""); + } + + template<> template<> + void translate_test_object_t::test<14>() + { + test_translation(mBing, 200, + "Привет", + "Привет", "", ""); + } + + template<> template<> + void translate_test_object_t::test<15>() + { + test_translation(mBing, 200, + "Привет", + "Привет", "", ""); + } + + template<> template<> + void translate_test_object_t::test<16>() + { + test_translation(mBing, 400, + "Message: some error

", + "", "", "some error"); + } + + template<> template<> + void translate_test_object_t::test<17>() + { + test_translation(mBing, 400, + "Message: some error", + "", "", "some error"); + } + + template<> template<> + void translate_test_object_t::test<18>() + { + test_translation(mBing, 400, + "some error

", + "", "", "some error"); + } + + template<> template<> + void translate_test_object_t::test<19>() + { + test_translation(mBing, 400, + "some error", + "", "", "some error"); + } + + template<> template<> + void translate_test_object_t::test<20>() + { + std::string url; + mBing.getTranslateURL(url, "en", "es", "hi"); + ensure_equals("bing URL", url, + "http://api.microsofttranslator.com/v2/Http.svc/Translate?appId=dummy&text=hi&to=es&from=en"); + } + + template<> template<> + void translate_test_object_t::test<21>() + { + std::string url; + mBing.getTranslateURL(url, "", "es", "hi"); + ensure_equals("bing URL", url, + "http://api.microsofttranslator.com/v2/Http.svc/Translate?appId=dummy&text=hi&to=es"); + } + + template<> template<> + void translate_test_object_t::test<22>() + { + std::string url; + mGoogle.getTranslateURL(url, "en", "es", "hi"); + ensure_equals("google URL", url, + "https://www.googleapis.com/language/translate/v2?key=dummy&q=hi&target=es&source=en"); + } + + template<> template<> + void translate_test_object_t::test<23>() + { + std::string url; + mGoogle.getTranslateURL(url, "", "es", "hi"); + ensure_equals("google URL", url, + "https://www.googleapis.com/language/translate/v2?key=dummy&q=hi&target=es"); + } +} + +//== Misc stubs =============================================================== +LLControlGroup gSavedSettings("test"); + +std::string LLUI::getLanguage() { return "en"; } +std::string LLTrans::getString(const std::string &xml_desc, const LLStringUtil::format_map_t& args) { return "dummy"; } + +LLControlGroup::LLControlGroup(const std::string& name) : LLInstanceTracker(name) {} +std::string LLControlGroup::getString(const std::string& name) { return "dummy"; } +LLControlGroup::~LLControlGroup() {} + +namespace boost { + void intrusive_ptr_add_ref(LLCurl::Responder*) {} + void intrusive_ptr_release(LLCurl::Responder*) {} +} + +LLCurl::Responder::Responder() {} +void LLCurl::Responder::completedHeader(U32, std::string const&, LLSD const&) {} +void LLCurl::Responder::completedRaw(U32, const std::string&, const LLChannelDescriptors&, const LLIOPipe::buffer_ptr_t& buffer) {} +void LLCurl::Responder::completed(U32, std::string const&, LLSD const&) {} +void LLCurl::Responder::error(U32, std::string const&) {} +void LLCurl::Responder::errorWithContent(U32, std::string const&, LLSD const&) {} +void LLCurl::Responder::result(LLSD const&) {} +LLCurl::Responder::~Responder() {} + +void LLHTTPClient::get(const std::string&, const LLSD&, ResponderPtr, const LLSD&, const F32) {} +void LLHTTPClient::get(const std::string&, boost::intrusive_ptr, const LLSD&, const F32) {} + +LLBufferStream::LLBufferStream(const LLChannelDescriptors& channels, LLBufferArray* buffer) : mStreamBuf(channels, buffer) {} +LLBufferStream::~LLBufferStream() {} + +LLBufferStreamBuf::LLBufferStreamBuf(const LLChannelDescriptors&, LLBufferArray*) {} +#if( LL_WINDOWS || __GNUC__ > 2) +LLBufferStreamBuf::pos_type LLBufferStreamBuf::seekoff( + off_type off, + std::ios::seekdir way, + std::ios::openmode which) +#else +streampos LLBufferStreamBuf::seekoff( + streamoff off, + std::ios::seekdir way, + std::ios::openmode which) +#endif +{ return 0; } +int LLBufferStreamBuf::sync() {return 0;} +int LLBufferStreamBuf::underflow() {return 0;} +int LLBufferStreamBuf::overflow(int) {return 0;} +LLBufferStreamBuf::~LLBufferStreamBuf() {} + +S32 LLVersionInfo::getBuild() { return 0; } +const std::string& LLVersionInfo::getChannel() {static std::string dummy; return dummy;} +S32 LLVersionInfo::getMajor() { return 0; } +S32 LLVersionInfo::getMinor() { return 0; } +S32 LLVersionInfo::getPatch() { return 0; } -- cgit v1.3 From 10b7c8d07f0c68613e29acecc334ea2d345d8ca3 Mon Sep 17 00:00:00 2001 From: Vadim ProductEngine Date: Thu, 15 Sep 2011 14:55:32 +0300 Subject: STORM-1577 WIP Trying to fix a Windows compiler error in unit tests. --- indra/newview/tests/lltranslate_test.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'indra/newview/tests') diff --git a/indra/newview/tests/lltranslate_test.cpp b/indra/newview/tests/lltranslate_test.cpp index 0f3429b7b3..da11b8394a 100644 --- a/indra/newview/tests/lltranslate_test.cpp +++ b/indra/newview/tests/lltranslate_test.cpp @@ -316,7 +316,8 @@ LLCurl::Responder::~Responder() {} void LLHTTPClient::get(const std::string&, const LLSD&, ResponderPtr, const LLSD&, const F32) {} void LLHTTPClient::get(const std::string&, boost::intrusive_ptr, const LLSD&, const F32) {} -LLBufferStream::LLBufferStream(const LLChannelDescriptors& channels, LLBufferArray* buffer) : mStreamBuf(channels, buffer) {} +LLBufferStream::LLBufferStream(const LLChannelDescriptors& channels, LLBufferArray* buffer) +: std::iostream(&mStreamBuf), mStreamBuf(channels, buffer) {} LLBufferStream::~LLBufferStream() {} LLBufferStreamBuf::LLBufferStreamBuf(const LLChannelDescriptors&, LLBufferArray*) {} -- cgit v1.3 From 1474f8c9a288dbc475ae8c482ee9961a39e94c0a Mon Sep 17 00:00:00 2001 From: Vadim ProductEngine Date: Fri, 16 Sep 2011 00:38:17 +0300 Subject: STORM-1577 WIP Addressing review feedback. * Don't enable the "Verify" button if use just moves cursor in the API key input field. * Fixed copy&paste error in unit tests. * Fixed a typo: LLBingTranslarionHandler * Added Doxygen comments to lltranslate.h. --- indra/newview/llfloatertranslationsettings.cpp | 12 ++- indra/newview/lltranslate.cpp | 12 +-- indra/newview/lltranslate.h | 109 ++++++++++++++++++++++++- indra/newview/tests/lltranslate_test.cpp | 4 +- 4 files changed, 123 insertions(+), 14 deletions(-) (limited to 'indra/newview/tests') diff --git a/indra/newview/llfloatertranslationsettings.cpp b/indra/newview/llfloatertranslationsettings.cpp index ac3e7ac8fa..ac4514b438 100644 --- a/indra/newview/llfloatertranslationsettings.cpp +++ b/indra/newview/llfloatertranslationsettings.cpp @@ -253,14 +253,18 @@ void LLFloaterTranslationSettings::onEditorFocused(LLFocusableElement* control) void LLFloaterTranslationSettings::onBingKeyEdited() { - mBingAPIKeyEditor->setTentative(FALSE); - setBingVerified(false, false); + if (mBingAPIKeyEditor->isDirty()) + { + setBingVerified(false, false); + } } void LLFloaterTranslationSettings::onGoogleKeyEdited() { - mGoogleAPIKeyEditor->setTentative(FALSE); - setGoogleVerified(false, false); + if (mGoogleAPIKeyEditor->isDirty()) + { + setGoogleVerified(false, false); + } } void LLFloaterTranslationSettings::onBtnBingVerify() diff --git a/indra/newview/lltranslate.cpp b/indra/newview/lltranslate.cpp index 7b99c20a58..7eb54271f4 100644 --- a/indra/newview/lltranslate.cpp +++ b/indra/newview/lltranslate.cpp @@ -117,6 +117,8 @@ bool LLGoogleTranslationHandler::parseTranslation( std::string& translation, std::string& detected_lang) { + // JsonCpp is prone to aborting the program on failed assertions, + // so be super-careful and verify the response format. const Json::Value& data = root.get("data", 0); if (!data.isObject() || !data.isMember("translations")) { @@ -147,7 +149,7 @@ std::string LLGoogleTranslationHandler::getAPIKey() } // virtual -void LLBingTranslarionHandler::getTranslateURL( +void LLBingTranslationHandler::getTranslateURL( std::string &url, const std::string &from_lang, const std::string &to_lang, @@ -162,7 +164,7 @@ void LLBingTranslarionHandler::getTranslateURL( } // virtual -void LLBingTranslarionHandler::getKeyVerificationURL( +void LLBingTranslationHandler::getKeyVerificationURL( std::string& url, const std::string& key) const { @@ -171,7 +173,7 @@ void LLBingTranslarionHandler::getKeyVerificationURL( } // virtual -bool LLBingTranslarionHandler::parseResponse( +bool LLBingTranslationHandler::parseResponse( int& status, const std::string& body, std::string& translation, @@ -217,7 +219,7 @@ bool LLBingTranslarionHandler::parseResponse( } // static -std::string LLBingTranslarionHandler::getAPIKey() +std::string LLBingTranslationHandler::getAPIKey() { return gSavedSettings.getString("BingTranslateAPIKey"); } @@ -347,7 +349,7 @@ const LLTranslationAPIHandler& LLTranslate::getPreferredHandler() const LLTranslationAPIHandler& LLTranslate::getHandler(EService service) { static LLGoogleTranslationHandler google; - static LLBingTranslarionHandler bing; + static LLBingTranslationHandler bing; if (service == SERVICE_GOOGLE) { diff --git a/indra/newview/lltranslate.h b/indra/newview/lltranslate.h index 672a56af8b..c2330daa81 100644 --- a/indra/newview/lltranslate.h +++ b/indra/newview/lltranslate.h @@ -35,19 +35,53 @@ namespace Json class Value; } +/** + * Handler of an HTTP machine translation service. + * + * Derived classes know the service URL + * and how to parse the translation result. + */ class LLTranslationAPIHandler { public: + /** + * Get URL for translation of the given string. + * + * Sending HTTP GET request to the URL will initiate translation. + * + * @param[out] url Place holder for the result. + * @param from_lang Source language. Leave empty for auto-detection. + * @param to_lang Target language. + * @param text Text to translate. + */ virtual void getTranslateURL( std::string &url, const std::string &from_lang, const std::string &to_lang, const std::string &text) const = 0; + /** + * Get URL to verify the given API key. + * + * Sending request to the URL verifies the key. + * Positive HTTP response (code 200) means that the key is valid. + * + * @param[out] url Place holder for the URL. + * @param[in] key Key to verify. + */ virtual void getKeyVerificationURL( std::string &url, const std::string &key) const = 0; + /** + * Parse translation response. + * + * @param[in,out] status HTTP status. May be modified while parsing. + * @param body Response text. + * @param[out] translation Translated text. + * @param[out] detected_lang Detected source language. May be empty. + * @param[out] err_msg Error message (in case of error). + */ virtual bool parseResponse( int& status, const std::string& body, @@ -61,6 +95,7 @@ protected: static const int STATUS_OK = 200; }; +/// Google Translate v2 API handler. class LLGoogleTranslationHandler : public LLTranslationAPIHandler { LOG_CLASS(LLGoogleTranslationHandler); @@ -93,9 +128,10 @@ private: static std::string getAPIKey(); }; -class LLBingTranslarionHandler : public LLTranslationAPIHandler +/// Microsoft Translator v2 API handler. +class LLBingTranslationHandler : public LLTranslationAPIHandler { - LOG_CLASS(LLBingTranslarionHandler); + LOG_CLASS(LLBingTranslationHandler); public: /*virtual*/ void getTranslateURL( @@ -116,7 +152,18 @@ private: static std::string getAPIKey(); }; - +/** + * Entry point for machine translation services. + * + * Basically, to translate a string, we need to know the URL + * of a translation service, have a valid API for the service + * and be given the target language. + * + * Callers specify the string to translate and the target language, + * LLTranslate takes care of the rest. + * + * API keys for translation are taken from saved settings. + */ class LLTranslate { LOG_CLASS(LLTranslate); @@ -128,9 +175,23 @@ public : SERVICE_GOOGLE, } EService; + /** + * Subclasses are supposed to handle translation results (e.g. show them in chat) + */ class TranslationReceiver: public LLHTTPClient::Responder { public: + + /** + * Using mHandler, parse incoming response. + * + * Calls either handleResponse() or handleFailure() + * depending on the HTTP status code and parsing success. + * + * @see handleResponse() + * @see handleFailure() + * @see mHandler + */ /*virtual*/ void completedRaw( U32 http_status, const std::string& reason, @@ -140,9 +201,13 @@ public : protected: friend class LLTranslate; + /// Remember source and target languages for subclasses to be able to filter inappropriate results. TranslationReceiver(const std::string& from_lang, const std::string& to_lang); + /// Override point to handle successful translation. virtual void handleResponse(const std::string &translation, const std::string &recognized_lang) = 0; + + /// Override point to handle unsuccessful translation. virtual void handleFailure(int status, const std::string& err_msg) = 0; std::string mFromLang; @@ -150,18 +215,41 @@ public : const LLTranslationAPIHandler& mHandler; }; + /** + * Subclasses are supposed to handle API key verification result. + */ class KeyVerificationReceiver: public LLHTTPClient::Responder { public: EService getService() const; protected: + /** + * Save the translation service the key belongs to. + * + * Subclasses need to know it. + * + * @see getService() + */ KeyVerificationReceiver(EService service); + + /** + * Parse verification response. + * + * Calls setVerificationStatus() with the verification status, + * which is true if HTTP status code is 200. + * + * @see setVerificationStatus() + */ /*virtual*/ void completedRaw( U32 http_status, const std::string& reason, const LLChannelDescriptors& channels, const LLIOPipe::buffer_ptr_t& buffer); + + /** + * Override point for subclasses to handle key verification status. + */ virtual void setVerificationStatus(bool ok) = 0; EService mService; @@ -170,7 +258,22 @@ public : typedef boost::intrusive_ptr TranslationReceiverPtr; typedef boost::intrusive_ptr KeyVerificationReceiverPtr; + /** + * Translate given text. + * + * @param receiver Object to pass translation result to. + * @param from_lang Source language. Leave empty for auto-detection. + * @param to_lang Target language. + * @param mesg Text to translate. + */ static void translateMessage(TranslationReceiverPtr &receiver, const std::string &from_lang, const std::string &to_lang, const std::string &mesg); + + /** + * Verify given API key of a translation service. + * + * @param receiver Object to pass verification result to. + * @param key Key to verify. + */ static void verifyKey(KeyVerificationReceiverPtr& receiver, const std::string& key); static std::string getTranslateLanguage(); diff --git a/indra/newview/tests/lltranslate_test.cpp b/indra/newview/tests/lltranslate_test.cpp index da11b8394a..10e37fae97 100644 --- a/indra/newview/tests/lltranslate_test.cpp +++ b/indra/newview/tests/lltranslate_test.cpp @@ -105,7 +105,7 @@ namespace tut } LLGoogleTranslationHandler mGoogle; - LLBingTranslarionHandler mBing; + LLBingTranslationHandler mBing; }; typedef test_group translate_test_group_t; @@ -158,7 +158,7 @@ namespace tut void translate_test_object_t::test<7>() { test_translation(mGoogle, 400, - "{\"oops\": \"invalid\"}", + "{\"data\": {}}", "", "", ""); } -- cgit v1.3