~aleteoryx/muditaos

fbc28f4cfe572157599d4b168e3a7027f1c6cc1f — Tomasz Langowski 5 years ago b661aa0
[EGD-5322] Add send message test (long sms, special characters)

Add send message test (long sms, special characters)

[EGD-5322] Add send message test (long sms, special characters)

Add send message test (long sms, special characters)
M module-services/service-cellular/ServiceCellular.cpp => module-services/service-cellular/ServiceCellular.cpp +8 -12
@@ 1903,18 1903,14 @@ SMSRecord ServiceCellular::createSMSRecord(const UTF8 &decodedMessage,

bool ServiceCellular::dbAddSMSRecord(const SMSRecord &record)
{
    auto query = std::make_unique<db::query::SMSAdd>(record);
    query->setQueryListener(db::QueryCallback::fromFunction([this](auto response) {
        auto result = dynamic_cast<db::query::SMSAddResult *>(response);
        if (result == nullptr || !result->result) {
            return false;
        }

        onSMSReceived();
        return true;
    }));
    const auto [succeed, _] = DBServiceAPI::GetQuery(this, db::Interface::Name::SMS, std::move(query));
    return succeed;
    return DBServiceAPI::AddSMS(this, record, db::QueryCallback::fromFunction([this](auto response) {
                                    auto result = dynamic_cast<db::query::SMSAddResult *>(response);
                                    if (result == nullptr || !result->result) {
                                        return false;
                                    }
                                    onSMSReceived();
                                    return true;
                                }));
}

void ServiceCellular::onSMSReceived()

M module-services/service-db/DBServiceAPI.cpp => module-services/service-db/DBServiceAPI.cpp +9 -1
@@ 1,4 1,4 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "service-db/DBServiceAPI.hpp"


@@ 363,3 363,11 @@ auto DBServiceAPI::DBBackup(sys::Service *serv, std::string backupPath) -> bool
    LOG_ERROR("DBBackup error, return code: %s", c_str(ret.first));
    return false;
}

bool DBServiceAPI::AddSMS(sys::Service *serv, const SMSRecord &record, std::unique_ptr<db::QueryListener> &&listener)
{
    auto query = std::make_unique<db::query::SMSAdd>(record);
    query->setQueryListener(std::move(listener));
    const auto [succeed, _] = DBServiceAPI::GetQuery(serv, db::Interface::Name::SMS, std::move(query));
    return succeed;
}

M module-services/service-db/service-db/DBServiceAPI.hpp => module-services/service-db/service-db/DBServiceAPI.hpp +13 -1
@@ 1,4 1,4 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 14,6 14,8 @@
#include <PhoneNumber.hpp>
#include <Service/Message.hpp>
#include <utf8/UTF8.hpp>
#include <module-db/queries/messages/sms/QuerySMSAdd.hpp>
#include <module-db/Interface/SMSRecord.hpp>

#include <cstdint>
#include <memory>


@@ 121,4 123,14 @@ class DBServiceAPI
    static auto GetCountryCodeByMCC(sys::Service *serv, uint32_t mcc) -> uint32_t;

    static auto DBBackup(sys::Service *serv, std::string backupPath) -> bool;

    /**
     * @brief Add sms via DBService interface
     *
     * @param serv - calling service
     * @param record - sms record data
     * @param listener - query listener to obtain and handle query result
     * @return true if adding sms operation succeed, otherwise false
     */
    static bool AddSMS(sys::Service *serv, const SMSRecord &record, std::unique_ptr<db::QueryListener> &&listener);
};

M module-services/service-desktop/endpoints/developerMode/DeveloperModeHelper.cpp => module-services/service-desktop/endpoints/developerMode/DeveloperModeHelper.cpp +55 -0
@@ 16,6 16,9 @@
#include <service-appmgr/Actions.hpp>
#include <messages/AppMessage.hpp>

#include <service-db/DBServiceAPI.hpp>
#include <time/time_conversion.hpp>

namespace parserFSM
{
    class Context;


@@ 74,6 77,18 @@ auto DeveloperModeHelper::processPutRequest(Context &context) -> sys::ReturnCode
        requestSimChange(simSelected);
        MessageHandler::putToSendQueue(context.createSimpleResponse());
    }
    else if (body[json::developerMode::smsCommand].is_string()) {
        if (body[json::developerMode::smsCommand].string_value() == json::developerMode::smsAdd) {
            SMSType smsType = static_cast<SMSType>(context.getBody()[json::messages::type].int_value());
            if (smsType == SMSType::DRAFT || smsType == SMSType::QUEUED || smsType == SMSType::FAILED) {
                return prepareSMS(context);
            }
            else {
                context.setResponseStatus(http::Code::NotAcceptable);
                MessageHandler::putToSendQueue(context.createSimpleResponse());
            }
        }
    }
    else {
        context.setResponseStatus(http::Code::BadRequest);
        MessageHandler::putToSendQueue(context.createSimpleResponse());


@@ 181,3 196,43 @@ void DeveloperModeHelper::requestSimChange(const int simSelected)
    }
    CellularServiceAPI::SetSimCard(ownerServicePtr, sim);
}

auto DeveloperModeHelper::smsRecordFromJson(json11::Json msgJson) -> SMSRecord
{
    auto record = SMSRecord();

    record.type = static_cast<SMSType>(msgJson[json::messages::type].int_value());
    record.date = utils::time::getCurrentTimestamp().getTime();
    utils::PhoneNumber phoneNumber(msgJson[json::messages::phoneNumber].string_value());
    record.number = phoneNumber.getView();
    record.body   = UTF8(msgJson[json::messages::messageBody].string_value());
    return record;
}

auto DeveloperModeHelper::prepareSMS(Context &context) -> sys::ReturnCodes
{
    SMSRecord record = smsRecordFromJson(context.getBody());

    LOG_INFO("Adding sms of type %d to database", static_cast<int>(record.type));
    auto listener = std::make_unique<db::EndpointListener>(
        [=](db::QueryResult *result, Context context) {
            bool res = false;
            if (auto SMSAddResult = dynamic_cast<db::query::SMSAddResult *>(result)) {
                context.setResponseStatus(SMSAddResult->result ? http::Code::OK : http::Code::InternalServerError);
                MessageHandler::putToSendQueue(context.createSimpleResponse());
                LOG_INFO("Adding sms of type %d to database - %s",
                         static_cast<int>(record.type),
                         SMSAddResult->result ? "OK" : "NOK");
                res = true;
            }
            else {
                context.setResponseStatus(http::Code::InternalServerError);
                MessageHandler::putToSendQueue(context.createSimpleResponse());
            }
            return res;
        },
        context);

    DBServiceAPI::AddSMS(ownerServicePtr, record, std::move(listener));
    return sys::ReturnCodes::Success;
}

M module-services/service-desktop/endpoints/developerMode/DeveloperModeHelper.hpp => module-services/service-desktop/endpoints/developerMode/DeveloperModeHelper.hpp +7 -2
@@ 1,4 1,4 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once


@@ 10,6 10,7 @@
#include <Service/Service.hpp>
#include <bsp/keyboard/key_codes.hpp>
#include <input/InputEvent.hpp>
#include <module-db/Interface/SMSRecord.hpp>

namespace sys
{


@@ 26,6 27,8 @@ namespace parserFSM
        void sendKeypress(bsp::KeyCodes keyCode, gui::InputEvent::State state);

        void requestSimChange(const int simSelected);
        auto smsRecordFromJson(json11::Json msgJson) -> SMSRecord;
        auto prepareSMS(Context &context) -> sys::ReturnCodes;

      public:
        DeveloperModeHelper(sys::Service *_ownerServicePtr) : ownerServicePtr(_ownerServicePtr){};


@@ 46,9 49,11 @@ namespace parserFSM
        inline constexpr auto btOn          = "on";
        inline constexpr auto btCommand     = "btCommand";
        inline constexpr auto changeSim     = "changeSim";
        inline constexpr auto smsCommand    = "smsCommand";
        inline constexpr auto getInfo       = "getInfo";

        /// values for getInfo cmd
        inline constexpr auto simStateInfo = "simState";
        /// values for smsCommand
        inline constexpr auto smsAdd = "smsAdd";
    }
} // namespace parserFSM

M test/harness/utils.py => test/harness/utils.py +139 -27
@@ 1,4 1,4 @@
# Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
# Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
# For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

import time


@@ 99,6 99,105 @@ keymap = {
    "caps": key_codes["*"],
}

special_chars_keymap = {
    U'.': "",
    U',': "d",
    U'\'': "dd",
    U'?': "ddd",
    U'!': "dddd",
    U'"': "ddddd",
    U'-': "dddddd",
    U'(': "s",
    U')': "sd",
    U'@': "sdd",
    U'/': "sddd",
    U':': "sdddd",
    U'_': "sddddd",
    U';': "sdddddd",
    U'␤': "ss",
    U'+': "ssd",
    U'&': "ssdd",
    U'%': "ssddd",
    U'*': "ssdddd",
    U'<': "ssddddd",
    U'>': "ssdddddd",
    U'=': "sss",
    U'£': "sssd",
    U'€': "sssdd",
    U'$': "sssddd",
    U'[': "sssdddd",
    U']': "sssddddd",
    U'{': "sssdddddd",
    U'}': "ssss",
    U'\'': "ssssd",
    U'^': "ssssdd",
    U'~': "ssssddd",
    U'`': "ssssdddd",
    U'į': "ssssddddd",
    U'§': "ssssdddddd",
    U'…': "sssss",
    U'#': "sssssd",
    U'|': "sssssdd",
    U'÷': "sssssddd",
    U'·': "sssssdddd",
    U'°': "sssssddddd",
    U'¿': "sssssdddddd",
    U'¡': "ssssss",
    U'ą': "ssssssd",
    U'à': "ssssssdd",
    U'á': "ssssssddd",
    U'ä': "ssssssdddd",
    U'â': "ssssssddddd",
    U'ć': "ssssssdddddd",
    U'ç': "sssssss",
    U'ę': "sssssssd",
    U'é': "sssssssdd",
    U'è': "sssssssddd",
    U'ê': "sssssssdddd",
    U'ë': "sssssssddddd",
    U'î': "sssssssdddddd",
    U'ï': "ssssssss",
    U'í': "ssssssssd",
    U'ł': "ssssssssdd",
    U'ń': "ssssssssddd",
    U'ñ': "ssssssssdddd",
    U'ó': "ssssssssddddd",
    U'ô': "ssssssssdddddd",
    U'ö': "sssssssss",
    U'ś': "sssssssssd",
    U'û': "sssssssssdd",
    U'ú': "sssssssssddd",
    U'ù': "sssssssssdddd",
    U'ü': "sssssssssddddd",
    U'ÿ': "sssssssssdddddd",
    U'ż': "ssssssssss",
    U'ź': "ssssssssssd",
    U'ß': "ssssssssssdd"
}

emojis_keymap = {
    U'😁': "",
    U'😂': "d",
    U'😃': "dd",
    U'😄': "ddd",
    U'😅': "dddd",
    U'😆': "ddddd",
    U'😉': "dddddd",
    U'😊': "s",
    U'😋': "sd",
    U'😌': "sdd",
    U'😍': "sddd",
    U'😏': "sdddd",
    U'😒': "sddddd",
    U'😓': "sdddddd",
    U'😔': "ss",
    U'😖': "ssd",
    U'😘': "ssdd",
    U'😚': "ssddd",
    U'😜': "ssdddd",
    U'😝': "ssddddd",
    U'😼': "ssdddddd"
}

def send_keystoke(keypath, connection):
    for key in keypath:


@@ 112,34 211,47 @@ last_char = '\0'
def send_char(char: str, connection):
    global last_char
    key_type = Keytype.short_press
    if char.isdigit():
        key_type = Keytype.long_press
        if last_char is char:
            print("repeated key!")
    if char.isascii():
        if char.isdigit():
            key_type = Keytype.long_press
            if last_char is char:
                print("repeated key!")
                connection.send_key_code(key_codes["right"])
            connection.send_key_code(int(char), key_type)
            connection.send_key_code(key_codes["right"])
        connection.send_key_code(int(char), key_type)
        connection.send_key_code(key_codes["right"])
        last_char = char
    elif char.islower():
        tmp = char.upper()
        # toggle to lowercase mode
        connection.send_key_code(key_codes["*"], key_type)
        if last_char is keymap[tmp][0]:
            print("repeated key!")
            connection.send_key_code(key_codes["right"], key_type)
        for key in keymap[tmp]:
            connection.send_key_code(int(key), key_type)
        last_char = keymap[tmp][0]
        # toggle to uppercase mode
        connection.send_key_code(key_codes["*"], key_type)
        connection.send_key_code(key_codes["*"], key_type)
            last_char = char
        elif char.islower():
            tmp = char.upper()
            # toggle to lowercase mode
            connection.send_key_code(key_codes["*"], key_type)
            if last_char is keymap[tmp][0]:
                print("repeated key!")
                connection.send_key_code(key_codes["right"], key_type)
            for key in keymap[tmp]:
                connection.send_key_code(int(key), key_type)
            last_char = keymap[tmp][0]
            # toggle to uppercase mode
            connection.send_key_code(key_codes["*"], key_type)
            connection.send_key_code(key_codes["*"], key_type)
        else:
            if last_char is keymap[char][0]:
                print("repeated key!")
                connection.send_key_code(key_codes["right"], key_type)
            for key in keymap[char]:
                connection.send_key_code(int(key), key_type)
            last_char = keymap[char][0]
    else:
        if last_char is keymap[char][0]:
            print("repeated key!")
            connection.send_key_code(key_codes["right"], key_type)
        for key in keymap[char]:
            connection.send_key_code(int(key), key_type)
        last_char = keymap[char][0]
        connection.send_key_code(key_codes["*"], Keytype.long_press)
        if char in special_chars_keymap:
            for key in special_chars_keymap[char]:
                connection.send_key_code(ord(key), key_type)
        elif char in emojis_keymap:
            connection.send_key_code(key_codes["fnLeft"], Keytype.short_press)
            for key in emojis_keymap[char]:
                connection.send_key_code(ord(key), key_type)
        else:
            print("Not supported char {} !!!".format(char))
        connection.send_key_code(key_codes["enter"], key_type)


def send_number(number: str, connection):

M test/pytest/test_send_message.py => test/pytest/test_send_message.py +77 -13
@@ 1,9 1,10 @@
# Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
# Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
# For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
import time
import pytest

from harness.interface.defs import key_codes, SMSType
from harness.interface.CDCSerial import Keytype


def get_message_by_text(harness, message: str, phone_number: str):


@@ 11,17 12,38 @@ def get_message_by_text(harness, message: str, phone_number: str):
    return harness.endpoint_request("messages", "get", body)["body"]


# default sms type is draft
def prepare_sms(harness, message: str, phone_number: str, sms_type: int = 1):
    body = {"smsCommand": "smsAdd", "messageBody": message, "phoneNumber": phone_number, "type": sms_type}
    return harness.endpoint_request("developerMode", "put", body)


def compare_messages(old_messages, new_messages):
    diff_messages = []

    for message in new_messages:
        if message not in old_messages:
            diff_messages.append(message)

    assert len(diff_messages) == 1
    assert SMSType(diff_messages[0]["type"]) == SMSType.OUTBOX


def enter_messages_menu(harness):
    harness.connection.send_key_code(key_codes["enter"])
    harness.open_application("messages")
    if harness.connection.get_window_name() != "ApplicationMessages":
        time.sleep(2)
        assert harness.connection.get_window_name() == "ApplicationMessages"


@pytest.mark.rt1051
@pytest.mark.usefixtures("phone_unlocked")
def test_send_message(harness, phone_number, sms_text):
    old_messages = get_message_by_text(harness, sms_text, str(phone_number))

    # enter menu
    harness.connection.send_key_code(key_codes["enter"])
    harness.open_application("messages")
    if harness.connection.get_application_name() != "ApplicationMessages":
        time.sleep(2)
        assert harness.connection.get_application_name() == "ApplicationMessages"
    enter_messages_menu(harness)

    # create new message
    harness.connection.send_key_code(key_codes["left"])


@@ 35,16 57,58 @@ def test_send_message(harness, phone_number, sms_text):
    harness.connection.send_key_code(key_codes["enter"])
    # go back to main screen
    for _ in range(3):
        time.sleep(1.2) # it take horrendous amount of time to go back to thread view
        time.sleep(1.2)  # it take horrendous amount of time to go back to thread view
        harness.connection.send_key_code(key_codes["fnRight"])

    time.sleep(3)

    new_messages = get_message_by_text(harness, sms_text, str(phone_number))
    diff_messages = []
    compare_messages(old_messages, new_messages)

    for message in new_messages:
        if message not in old_messages:
            diff_messages.append(message)

    assert len(diff_messages) == 1
    assert SMSType(diff_messages[0]["type"]) == SMSType.OUTBOX
@pytest.mark.rt1051
@pytest.mark.usefixtures("phone_unlocked")
def test_send_prepared_message(harness, phone_number, sms_text):
    old_messages = get_message_by_text(harness, sms_text, str(phone_number))

    # prepare sms and send it (add sms to sending queue)
    prepare_sms(harness, sms_text, str(phone_number), 0x10)
    # time to send message
    time.sleep(3)
    # check whether message was sent
    new_messages = get_message_by_text(harness, sms_text, str(phone_number))
    compare_messages(old_messages, new_messages)


testdata = [
    "Ala1Ma śżżń 😚",
    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
    "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA😚"
]


@pytest.mark.rt1051
@pytest.mark.usefixtures("phone_unlocked")
@pytest.mark.parametrize("sms_text", testdata, ids=["special_characters", "long_text"])
def test_send_prepared_draft_message(harness, phone_number, sms_text):
    old_messages = get_message_by_text(harness, sms_text, str(phone_number))

    # prepare sms and send it (add sms to sending queue)
    prepare_sms(harness, sms_text, str(phone_number), SMSType.DRAFT.value)

    # enter menu
    enter_messages_menu(harness)

    # enter thread with draft
    harness.connection.send_key_code(key_codes["enter"])
    # send
    harness.connection.send_key_code(key_codes["enter"])
    # go back to main screen
    harness.connection.send_key_code(key_codes["fnRight"], Keytype.long_press)
    # wait for sms to send
    time.sleep(3)

    # check whether message was sent
    new_messages = get_message_by_text(harness, sms_text, str(phone_number))
    compare_messages(old_messages, new_messages)