diff --git a/src/utils/customnetworkreply.cpp b/src/utils/customnetworkreply.cpp new file mode 100644 index 0000000..b717496 --- /dev/null +++ b/src/utils/customnetworkreply.cpp @@ -0,0 +1,62 @@ +#include "customnetworkreply.h" + +CustomNetworkReply::CustomNetworkReply(QObject *parent) + : QNetworkReply(parent), content(nullptr), offset(0) { +} + +CustomNetworkReply::~CustomNetworkReply() { +} + +void CustomNetworkReply::setHttpStatusCode(int code, const QByteArray &statusText) { + setAttribute(QNetworkRequest::HttpStatusCodeAttribute, code); + if (statusText.isNull()) + return; + + setAttribute(QNetworkRequest::HttpReasonPhraseAttribute, statusText); +} + +void CustomNetworkReply::setHeader(QNetworkRequest::KnownHeaders header, const QVariant &value) { + QNetworkReply::setHeader(header, value); +} + +void CustomNetworkReply::setContentType(const QByteArray &contentType) { + setHeader(QNetworkRequest::ContentTypeHeader, contentType); +} + +void CustomNetworkReply::setContent(const QString &content) { + setContent(content.toUtf8()); +} + +void CustomNetworkReply::setContent(const QByteArray &content) { + this->content = content; + offset = 0; + + open(ReadOnly | Unbuffered); + setHeader(QNetworkRequest::ContentLengthHeader, QVariant(content.size())); + + QTimer::singleShot(0, this, SIGNAL(readyRead())); + QTimer::singleShot(0, this, SIGNAL(finished())); +} + +void CustomNetworkReply::abort() { + // No operation. +} + +qint64 CustomNetworkReply::bytesAvailable() const { + return content.size() - offset + QIODevice::bytesAvailable(); +} + +bool CustomNetworkReply::isSequential() const { + return true; +} + +qint64 CustomNetworkReply::readData(char *data, qint64 maxSize) { + if (offset >= content.size()) + return -1; + + qint64 number = qMin(maxSize, content.size() - offset); + memcpy(data, content.constData() + offset, number); + offset += number; + + return number; +} diff --git a/src/utils/customnetworkreply.h b/src/utils/customnetworkreply.h new file mode 100644 index 0000000..419aa10 --- /dev/null +++ b/src/utils/customnetworkreply.h @@ -0,0 +1,93 @@ +#ifndef INSTANT_TRANSLATOR_CUSTOMNETWORKREPLY_H +#define INSTANT_TRANSLATOR_CUSTOMNETWORKREPLY_H + +#include +#include +#include +#include + +/** + * Custom QNetworkReply class. + */ +class CustomNetworkReply : public QNetworkReply { +Q_OBJECT + +public: + /** + * Constructs an object with parent object parent. + * @param parent Parent of an object may be viewed as the object's owner. + */ + explicit CustomNetworkReply(QObject *parent = nullptr); + + /** + * Destructor. + */ + ~CustomNetworkReply() override; + + /** + * + * @param code + * @param statusText + */ + void setHttpStatusCode(int code, const QByteArray &statusText = QByteArray()); + + /** + * Sets the value of the known header header to be + * value, overriding any previously set headers. + * @param header Header. + * @param value Value. + */ + void setHeader(QNetworkRequest::KnownHeaders header, const QVariant &value); + + /** + * Set content type, e.g., "text/html". + * @param contentType Content type. + */ + void setContentType(const QByteArray &contentType); + + /** + * Set content. + * @param content Content. + */ + void setContent(const QString &content); + + /** + * Set content. + * @param content Content. + */ + void setContent(const QByteArray &content); + + /** + * Aborts the operation immediately and close + * down any network connections still open. + */ + void abort() override; + + /** + * Returns the number of bytes that are available for reading. + * @return Bytes that are available for reading. + */ + qint64 bytesAvailable() const override; + + /** + * Returns true if this device is sequential; otherwise returns false. + * @return Returns true if this device is sequential. + */ + bool isSequential() const override; + +protected: + /** + * Reads up to maxSize bytes from the device into data, and + * returns the number of bytes read or -1 if an error occurred. + * @param data Data. + * @param maxSize Max size. + * @return returns the number of bytes read or -1 if an error occurred. + */ + qint64 readData(char *data, qint64 maxSize) override; + +private: + QByteArray content; + qint64 offset; +}; + +#endif //INSTANT_TRANSLATOR_CUSTOMNETWORKREPLY_H diff --git a/src/utils/requestmanager.cpp b/src/utils/requestmanager.cpp index 07b8cfe..e99056c 100644 --- a/src/utils/requestmanager.cpp +++ b/src/utils/requestmanager.cpp @@ -4,6 +4,10 @@ RequestManager::RequestManager(QObject *parent) : QObject(parent), manager(new QNetworkAccessManager) { } +RequestManager::RequestManager(QObject *parent, QNetworkAccessManager *manager) + : QObject(parent), manager(manager) { +} + RequestManager::~RequestManager() { delete manager; } diff --git a/src/utils/requestmanager.h b/src/utils/requestmanager.h index 4951ec9..66f7ac8 100644 --- a/src/utils/requestmanager.h +++ b/src/utils/requestmanager.h @@ -15,6 +15,13 @@ Q_OBJECT */ explicit RequestManager(QObject *parent = nullptr); + /** + * Constructs an object with parent object parent. + * @param parent Parent of an object may be viewed as the object's owner. + * @param manager Network access manager. + */ + RequestManager(QObject *parent, QNetworkAccessManager *manager); + /** * Destructor. */ diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 4e8229c..6fc6e38 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,3 +1,13 @@ +set(CMAKE_CXX_STANDARD 11) + +# Find includes in corresponding build directories. +set(CMAKE_INCLUDE_CURRENT_DIR ON) +# Instruct CMake to run moc automatically when needed. +set(CMAKE_AUTOMOC ON) +# Instruct CMake to run uic automatically when needed. +set(CMAKE_AUTOUIC ON) +# Add a compiler flag. +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall") # Find the Qt libraries. find_package(Qt5Widgets REQUIRED) @@ -23,15 +33,20 @@ include_directories("${PROJECT_SOURCE_DIR}/src") # Source files. set(SOURCE_FILES - language_test.cpp + utils/language_test.cpp ${PROJECT_SOURCE_DIR}/src/utils/language.cpp - ${PROJECT_SOURCE_DIR}/src/utils/language.h) + ${PROJECT_SOURCE_DIR}/src/utils/language.h + utils/requestmanager_test.cpp + ${PROJECT_SOURCE_DIR}/src/utils/requestmanager.cpp + ${PROJECT_SOURCE_DIR}/src/utils/requestmanager.h + ${PROJECT_SOURCE_DIR}/src/utils/customnetworkreply.cpp + ${PROJECT_SOURCE_DIR}/src/utils/customnetworkreply.h) # Tell CMake to create the executable. add_executable(unit_tests ${SOURCE_FILES} ${RESOURCE_FILES_ADDED} ${UI_HEADERS}) # Use the Widgets module from Qt5. -target_link_libraries(unit_tests ${QT5_LIBRARIES} gtest_main) +target_link_libraries(unit_tests ${QT5_LIBRARIES} gtest_main gmock_main) # Add tests. add_test( diff --git a/tests/language_test.cpp b/tests/language_test.cpp deleted file mode 100644 index 5dffbcf..0000000 --- a/tests/language_test.cpp +++ /dev/null @@ -1,8 +0,0 @@ -#include -#include "utils/language.h" - -TEST(Language, getCode) -{ - Language language; - ASSERT_EQ("lt", language.getCode("Lithuanian")); -} diff --git a/tests/utils/language_test.cpp b/tests/utils/language_test.cpp new file mode 100644 index 0000000..1d1ed93 --- /dev/null +++ b/tests/utils/language_test.cpp @@ -0,0 +1,35 @@ +#include +#include +#include "utils/language.h" + +TEST(LanguageTests, getCode_ConvertLanguageToName_True) { + Language language; + ASSERT_EQ("lt", language.getCode("Lithuanian")); +} + +TEST(LanguageTests, getName_ConvertCodeToName_True) { + Language language; + ASSERT_EQ("Lithuanian", language.getName("lt")); +} + +TEST(LanguageTests, getLanguages_ConvertCodesToLanguages_True) { + Language language; + QStringList codesList; + codesList.append("lt"); + codesList.append("en"); + QStringList languagesList = language.getLanguages(codesList); + ASSERT_EQ(2, languagesList.size()); + ASSERT_EQ("Lithuanian", languagesList.at(0)); + ASSERT_EQ("English", languagesList.at(1)); +} + +TEST(LanguageTests, getCodes_ConvertLanguagesToCodes_True) { + Language language; + QStringList languagesList; + languagesList.append("Lithuanian"); + languagesList.append("English"); + QStringList codesList = language.getCodes(languagesList); + ASSERT_EQ(2, languagesList.size()); + ASSERT_EQ("lt", codesList.at(0)); + ASSERT_EQ("en", codesList.at(1)); +} diff --git a/tests/utils/requestmanager_test.cpp b/tests/utils/requestmanager_test.cpp new file mode 100644 index 0000000..375f4f0 --- /dev/null +++ b/tests/utils/requestmanager_test.cpp @@ -0,0 +1,84 @@ +#include +#include +#include +#include +#include +#include +#include "utils/requestmanager.h" +#include "utils/customnetworkreply.h" + +class MockQNetworkAccessManager : public QNetworkAccessManager { +public: + explicit MockQNetworkAccessManager(QObject *parent = nullptr) : QNetworkAccessManager(parent) {} + +protected: + QNetworkReply *createRequest(Operation op, const QNetworkRequest &originalReq, + QIODevice *outgoingData = Q_NULLPTR) override { + CustomNetworkReply *reply = new CustomNetworkReply(); + reply->setHttpStatusCode(200, "OK"); + reply->setContentType("application/json"); + reply->setContent(QString("{\"data\":{\"translations\":[{\"translatedText\":\"Hello\"}]}}")); + + return reply; + } +}; + +TEST(RequestManagerTests, postRequest_SendPostRequest_True) { + int argc = 0; + char **argv = 0; + QCoreApplication app(argc, argv); + + // Format Json object. + QJsonObject postJsonObject; + postJsonObject["source"] = "lt"; + postJsonObject["target"] = "en"; + postJsonObject["format"] = "text"; + postJsonObject["q"] = "Labas"; + + QJsonDocument postJsonDocument(postJsonObject); + + // Make request. + QUrl url("https://randomurl.com/"); + QNetworkRequest request(url); + request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + + // Send request. + MockQNetworkAccessManager *manager = new MockQNetworkAccessManager(); + + RequestManager requestManager(nullptr, manager); + requestManager.postRequest(request, postJsonDocument.toJson()); + + // Test. + QString expected = "{\"data\":{\"translations\":[{\"translatedText\":\"Hello\"}]}}"; + ASSERT_EQ(expected, requestManager.getReply()); +} + +TEST(RequestManagerTests, getRequest_SendGetRequest_True) { + int argc = 0; + char **argv = 0; + QCoreApplication app(argc, argv); + + // Format Json object. + QJsonObject postJsonObject; + postJsonObject["source"] = "lt"; + postJsonObject["target"] = "en"; + postJsonObject["format"] = "text"; + postJsonObject["q"] = "Labas"; + + QJsonDocument postJsonDocument(postJsonObject); + + // Make request. + QUrl url("https://randomurl.com/"); + QNetworkRequest request(url); + request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + + // Send request. + MockQNetworkAccessManager *manager = new MockQNetworkAccessManager(); + + RequestManager requestManager(nullptr, manager); + requestManager.getRequest(request); + + // Test. + QString expected = "{\"data\":{\"translations\":[{\"translatedText\":\"Hello\"}]}}"; + ASSERT_EQ(expected, requestManager.getReply()); +}