~aleteoryx/muditaos

31df1ce788ec1fec020880441fc0b62ed0920280 — Piotr Tański 5 years ago f9facb4
[EGD-4921] Add next harness test cases

Test harness extended with additional tests:
- Making a call to a specific phone number.
- Calling back to the last number from the call log.
- Searching for SMSes with a phone number filter.
M changelog.md => changelog.md +1 -0
@@ 11,6 11,7 @@
* Add Bluetooth virtual audio device.
* Add custom repeat window for the alarm application
* `[PowerManagement]` Add CPU frequency shift mechanism.
* `[tests]` Extended the automated tests with new test cases: making a call, answering a call.

### Fixed


M module-db/Interface/SMSRecord.cpp => module-db/Interface/SMSRecord.cpp +25 -1
@@ 380,13 380,37 @@ std::unique_ptr<db::QueryResult> SMSRecordInterface::getByContactIDQuery(const s
std::unique_ptr<db::QueryResult> SMSRecordInterface::getByTextQuery(const std::shared_ptr<db::Query> &query)
{
    const auto localQuery = static_cast<const db::query::SMSGetByText *>(query.get());
    auto smsVector        = smsDB->sms.getByText(localQuery->text);
    auto smsVector        = getByText(localQuery->getText(), localQuery->getPhoneNumber());
    auto recordVector     = std::vector<SMSRecord>(smsVector.begin(), smsVector.end());

    auto response = std::make_unique<db::query::SMSGetByTextResult>(recordVector);
    response->setRequestQuery(query);
    return response;
}

std::vector<SMSRecord> SMSRecordInterface::getByText(const std::string &text,
                                                     const std::optional<utils::PhoneNumber::View> &phoneNumberFilter)
{
    if (phoneNumberFilter.has_value()) {
        return getByTextAndPhoneNumber(text, phoneNumberFilter.value());
    }
    const auto &records = smsDB->sms.getByText(text);
    return std::vector<SMSRecord>(records.begin(), records.end());
}

std::vector<SMSRecord> SMSRecordInterface::getByTextAndPhoneNumber(const std::string &text,
                                                                   const utils::PhoneNumber::View &phoneNumber)
{
    ThreadRecordInterface threadsInterface{smsDB, contactsDB};
    const auto &thread = threadsInterface.GetByNumber(phoneNumber);
    if (!thread.isValid()) {
        return {};
    }

    const auto &smsRecords = smsDB->sms.getByText(text, thread.ID);
    return std::vector<SMSRecord>(smsRecords.begin(), smsRecords.end());
}

std::unique_ptr<db::QueryResult> SMSRecordInterface::getCountQuery(const std::shared_ptr<db::Query> &query)
{
    auto response = std::make_unique<db::query::SMSGetCountResult>(smsDB->sms.count());

M module-db/Interface/SMSRecord.hpp => module-db/Interface/SMSRecord.hpp +5 -0
@@ 77,6 77,11 @@ class SMSRecordInterface : public RecordInterface<SMSRecord, SMSRecordField>
    SmsDB *smsDB                        = nullptr;
    ContactsDB *contactsDB              = nullptr;

    std::vector<SMSRecord> getByText(const std::string &text,
                                     const std::optional<utils::PhoneNumber::View> &phoneNumberFilter);
    std::vector<SMSRecord> getByTextAndPhoneNumber(const std::string &text,
                                                   const utils::PhoneNumber::View &phoneNumber);

    static void UpdateThreadSummary(ThreadRecord &threadToUpdate, const SMSRecord &rec);
    std::unique_ptr<db::query::SMSSearchByTypeResult> runQueryImpl(const db::query::SMSSearchByType *query);
    std::unique_ptr<db::QueryResult> getByIDQuery(const std::shared_ptr<db::Query> &query);

M module-db/Tables/SMSTable.cpp => module-db/Tables/SMSTable.cpp +24 -0
@@ 234,6 234,30 @@ std::vector<SMSTableRow> SMSTable::getByText(std::string text)
    return ret;
}

std::vector<SMSTableRow> SMSTable::getByText(std::string text, uint32_t threadId)
{
    auto retQuery =
        db->query("SELECT *, INSTR(body,'%s') pos FROM sms WHERE pos > 0 AND thread_id=%u;", text.c_str(), threadId);
    if ((retQuery == nullptr) || (retQuery->getRowCount() == 0)) {
        return {};
    }

    std::vector<SMSTableRow> ret;
    do {
        ret.push_back(SMSTableRow{
            (*retQuery)[0].getUInt32(),                       // ID
            (*retQuery)[1].getUInt32(),                       // threadID
            (*retQuery)[2].getUInt32(),                       // contactID
            (*retQuery)[3].getUInt32(),                       // date
            (*retQuery)[4].getUInt32(),                       // dateSent
            (*retQuery)[5].getUInt32(),                       // errorCode
            (*retQuery)[6].getString(),                       // body
            static_cast<SMSType>((*retQuery)[7].getUInt32()), // type
        });
    } while (retQuery->nextRow());
    return ret;
}

std::vector<SMSTableRow> SMSTable::getLimitOffset(uint32_t offset, uint32_t limit)
{
    auto retQuery = db->query("SELECT * from sms ORDER BY date DESC LIMIT %lu OFFSET %lu;", limit, offset);

M module-db/Tables/SMSTable.hpp => module-db/Tables/SMSTable.hpp +1 -0
@@ 49,6 49,7 @@ class SMSTable : public Table<SMSTableRow, SMSTableFields>
    uint32_t countByFieldId(const char *field, uint32_t id) override final;
    std::vector<SMSTableRow> getByContactId(uint32_t contactId);
    std::vector<SMSTableRow> getByText(std::string text);
    std::vector<SMSTableRow> getByText(std::string text, uint32_t threadId);
    std::vector<SMSTableRow> getByThreadId(uint32_t threadId, uint32_t offset, uint32_t limit);
    std::vector<SMSTableRow> getByThreadIdWithoutDraftWithEmptyInput(uint32_t threadId,
                                                                     uint32_t offset,

M module-db/queries/messages/sms/QuerySMSGetByText.cpp => module-db/queries/messages/sms/QuerySMSGetByText.cpp +16 -0
@@ 8,6 8,21 @@ namespace db::query
    SMSGetByText::SMSGetByText(std::string text) : Query(Query::Type::Read), text(std::move(text))
    {}

    void SMSGetByText::filterByPhoneNumber(const utils::PhoneNumber::View &number) noexcept
    {
        phoneNumber = number;
    }

    auto SMSGetByText::getPhoneNumber() const noexcept -> const std::optional<utils::PhoneNumber::View> &
    {
        return phoneNumber;
    }

    auto SMSGetByText::getText() const noexcept -> const std::string &
    {
        return text;
    }

    auto SMSGetByText::debugInfo() const -> std::string
    {
        return "SMSGetByText";


@@ 15,6 30,7 @@ namespace db::query

    SMSGetByTextResult::SMSGetByTextResult(std::vector<SMSRecord> result) : result(std::move(result))
    {}

    auto SMSGetByTextResult::getResults() const -> std::vector<SMSRecord>
    {
        return result;

M module-db/queries/messages/sms/QuerySMSGetByText.hpp => module-db/queries/messages/sms/QuerySMSGetByText.hpp +9 -2
@@ 14,9 14,16 @@ namespace db::query
    class SMSGetByText : public Query
    {
      public:
        SMSGetByText(std::string text);
        std::string text;
        explicit SMSGetByText(std::string text);

        void filterByPhoneNumber(const utils::PhoneNumber::View &number) noexcept;
        [[nodiscard]] auto getPhoneNumber() const noexcept -> const std::optional<utils::PhoneNumber::View> &;
        [[nodiscard]] auto getText() const noexcept -> const std::string &;
        [[nodiscard]] auto debugInfo() const -> std::string override;

      private:
        std::string text;
        std::optional<utils::PhoneNumber::View> phoneNumber;
    };

    class SMSGetByTextResult : public QueryResult

M module-services/service-db/ServiceDB.cpp => module-services/service-db/ServiceDB.cpp +1 -1
@@ 541,7 541,7 @@ sys::ReturnCodes ServiceDB::InitHandler()
    smsDB           = std::make_unique<SmsDB>((purefs::dir::getUserDiskPath() / "sms.db").c_str());
    alarmsDB        = std::make_unique<AlarmsDB>((purefs::dir::getUserDiskPath() / "alarms.db").c_str());
    notesDB         = std::make_unique<NotesDB>((purefs::dir::getUserDiskPath() / "notes.db").c_str());
    calllogDB       = std::make_unique<CalllogDB>((purefs::dir::getUserDiskPath() / "callog.db").c_str());
    calllogDB       = std::make_unique<CalllogDB>((purefs::dir::getUserDiskPath() / "calllog.db").c_str());
    countryCodesDB  = std::make_unique<CountryCodesDB>("country-codes.db");
    notificationsDB = std::make_unique<NotificationsDB>((purefs::dir::getUserDiskPath() / "notifications.db").c_str());
    eventsDB        = std::make_unique<EventsDB>((purefs::dir::getUserDiskPath() / "events.db").c_str());

M module-services/service-desktop/endpoints/messages/MessageHelper.cpp => module-services/service-desktop/endpoints/messages/MessageHelper.cpp +5 -0
@@ 198,6 198,11 @@ auto MessageHelper::requestSMS(Context &context) -> sys::ReturnCodes
    else if (context.getBody()[json::messages::messageBody].string_value().empty() == false) {
        auto query =
            std::make_unique<db::query::SMSGetByText>(context.getBody()[json::messages::messageBody].string_value());
        if (const auto filterByNumber = !context.getBody()[json::messages::phoneNumber].string_value().empty();
            filterByNumber) {
            utils::PhoneNumber number{context.getBody()[json::messages::phoneNumber].string_value()};
            query->filterByPhoneNumber(number.getView());
        }

        auto listener = std::make_unique<db::EndpointListener>(
            [=](db::QueryResult *result, Context context) {

M test/harness/utils.py => test/harness/utils.py +1 -1
@@ 10,7 10,7 @@ from harness.interface.defs import key_codes

# assuming that the harness is actually in the menu
application_keypath = {
    "phone": [
    "calllog": [
        "enter"
    ],
    "contacts": [

A test/pytest/test_call_back.py => test/pytest/test_call_back.py +38 -0
@@ 0,0 1,38 @@
# Copyright (c) 2017-2020, 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


def get_calllog_count(harness):
    body = {"count": True}
    return harness.endpoint_request("calllog", "get", body)["body"]["count"]


@pytest.mark.rt1051
@pytest.mark.usefixtures("phone_unlocked")
def test_call(harness, call_duration):
    count_before = get_calllog_count(harness)

    # enter menu
    harness.connection.send_key(key_codes["enter"])
    harness.open_application("calllog")
    if harness.connection.get_window_name() != "ApplicationCallLog":
        time.sleep(2)
        assert harness.connection.get_window_name() == "ApplicationCallLog"

    # call
    harness.connection.send_key(key_codes["fnLeft"])
    time.sleep(call_duration)
    # hang up
    harness.connection.send_key(key_codes["fnRight"])
    count_after = get_calllog_count(harness)

    for _ in range(3):
        harness.connection.send_key(key_codes["fnRight"])
        time.sleep(1)

    assert count_before + 1 == count_after
    time.sleep(2)  # needed to restore after call

A test/pytest/test_search_sms.py => test/pytest/test_search_sms.py +13 -0
@@ 0,0 1,13 @@
# Copyright (c) 2017-2020, 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

@pytest.mark.rt1051
@pytest.mark.usefixtures("phone_unlocked")
def test_search_sms(harness, sms_text, phone_number):
    body = {"messageBody": sms_text, "phoneNumber": str(phone_number)}
    messages = harness.endpoint_request("messages", "get", body)["body"]
    assert len(messages) != 0

M test/pytest/test_send_message.py => test/pytest/test_send_message.py +5 -5
@@ 6,15 6,15 @@ import pytest
from harness.interface.defs import key_codes


def get_message_by_text(harness, message: str):
    body = {"messageBody": message}
def get_message_by_text(harness, message: str, phone_number: str):
    body = {"messageBody": message, "phoneNumber": phone_number}
    return harness.endpoint_request("messages", "get", body)["body"]


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

    # enter menu
    harness.connection.send_key(key_codes["enter"])


@@ 35,11 35,11 @@ def test_send_message(harness, phone_number, sms_text):
    harness.connection.send_key(key_codes["enter"])

    # go back to main screen
    for i in range(3):
    for _ in range(3):
        harness.connection.send_key(key_codes["fnRight"])

    time.sleep(2)
    new_messages = get_message_by_text(harness, sms_text.upper())
    new_messages = get_message_by_text(harness, sms_text.upper(), str(phone_number))

    diff = [i for i in messages + new_messages if i not in messages or i not in new_messages]
    assert len(diff) == 1

R test/send_message.py => test/search_sms.py +9 -40
@@ 11,33 11,12 @@ from harness.interface.defs import key_codes, endpoint, method
from harness.interface.error import TestError, Error


def send_message(harness, phone_number: str, message: str):
def search_sms(harness, message: str, phone_number: str):
    @harness.with_phone_unlocked
    def send(connection):
        # enter menu
        connection.send_key(key_codes["enter"])
        harness.open_application("messages")
        if harness.connection.get_window_name() != "ApplicationMessages":
            time.sleep(2)
            if harness.connection.get_window_name() != "ApplicationMessages":
                print("Application didn't switch, exiting...")
                exit(1)

        # create new message
        connection.send_key(key_codes["left"])
        # enter phone number
        harness.send_number(phone_number)
        # move down to message body
        connection.send_key(key_codes["down"])
        # write a message
        harness.send_text(message)
        # send
        connection.send_key(key_codes["enter"])


def get_message_by_text(harness, message: str):
    body = {"messageBody": message}
    return harness.endpoint_request("messages", "get", body)["body"]
    def do_it(connection):
        body = {"messageBody": message, "phoneNumber": phone_number}
        messages = harness.endpoint_request("messages", "get", body)["body"]
        print(f'Found {len(messages)} messages')


def main():


@@ 45,10 24,9 @@ def main():
        log.warning("Port name not passed, trying port name filename from simulator...")
        try:
            file = open("/tmp/purephone_pts_name", "r")
        except FileNotFoundError as err:
        except FileNotFoundError:
            raise TestError(Error.PORT_FILE_NOT_FOUND)


        port_name = file.readline()
        if port_name.isascii():
            log.debug("found {} entry!".format(port_name))


@@ 59,19 37,10 @@ def main():
        port_name = sys.argv[1]

    harness = Harness(port_name)
    message = str(sys.argv[3])
    messages = get_message_by_text(harness, message.upper())

    send_message(harness, str(sys.argv[2]), message)
    time.sleep(2)
    new_messages = get_message_by_text(harness, message.upper())
    message = str(sys.argv[2])
    phone_number = str(sys.argv[3])

    diff = [i for i in messages + new_messages if i not in messages or i not in new_messages]
    if len(diff) != 1 or diff[0]["type"] != 0x08:  # 0x08 - SMSType::OUTBOX
        log.error("sending error!")
        raise TestError(Error.TEST_FAILED)
    else:
        log.info("sending success!")
    search_sms(harness, message, phone_number)


if __name__ == "__main__":