~aleteoryx/muditaos

3eb630b02dc28175cfae387f207c2f3210e1ee09 — Przemyslaw Brudny 4 years ago 226e9e4
[EGD-6534] TextFixedSize crash on char removal fix

Fixed TextFixedSize character removal crash on single line
space case. Added UT to cover it and other missing TextFixed
sized drawLines cases.
M enabled_unittests => enabled_unittests +2 -0
@@ 225,6 225,8 @@ TESTS_LIST["catch2-gui-text"]="
    TextLineCursor - navigation without scroll;
    TextLineCursor - navigation with scroll;
    TextLineCursor - addition and deletion with scroll;
    TextFixedSize drawLines;
    TextFixedSize remove Char;
"
#---------
TESTS_LIST["catch2-PowerManager"]="

M module-gui/gui/widgets/Lines.cpp => module-gui/gui/widgets/Lines.cpp +25 -8
@@ 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 "Lines.hpp"


@@ 80,20 80,37 @@ namespace gui
            auto textLine =
                gui::TextLine(drawCursor, w, initHeight, underLine, UnderlineDrawMode::WholeLine, underLinePadding);

            if (textLine.height() > 0 && initHeight != textLine.height()) {
            if ((textLine.height() > 0) && initHeight != textLine.height()) {
                initHeight = textLine.height();
            }

            if (lineYPosition + initHeight > h) {
                stopCondition = LinesDrawStop::OutOfSpace;
                addToInvisibleLines(std::move(textLine));
            if (!previousLinesStart.empty() && (textLine.length() == 0) && textLine.getLineEnd()) {
                stopCondition = LinesDrawStop::OutOfText;
                break;
            }

            if (lineYPosition + initHeight > h) {
                if ((textLine.length() == 0) && textLine.getLineEnd()) {
                    stopCondition = LinesDrawStop::OutOfText;
                    break;
                }
                else {
                    stopCondition = LinesDrawStop::OutOfSpace;
                    addToInvisibleLines(std::move(textLine));
                    break;
                }
            }

            if (lines.size() >= linesCount) {
                stopCondition = LinesDrawStop::OutOfSpace;
                addToInvisibleLines(std::move(textLine));
                break;
                if ((textLine.length() == 0) && textLine.getLineEnd()) {
                    stopCondition = LinesDrawStop::OutOfText;
                    break;
                }
                else {
                    stopCondition = LinesDrawStop::OutOfSpace;
                    addToInvisibleLines(std::move(textLine));
                    break;
                }
            }

            emplace(std::move(textLine));

M module-gui/gui/widgets/Text.cpp => module-gui/gui/widgets/Text.cpp +6 -2
@@ 206,8 206,12 @@ namespace gui
    void Text::setFont(const UTF8 &fontName)
    {
        RawFont *newFont = FontManager::getInstance().getFont(fontName);
        format.setFont(newFont);
        buildCursor();

        if (format.getFont() != newFont) {
            format.setFont(newFont);
            buildCursor();
            drawLines();
        }
    }

    void Text::setFont(RawFont *font)

M module-gui/test/test-catch-text/CMakeLists.txt => module-gui/test/test-catch-text/CMakeLists.txt +1 -0
@@ 8,6 8,7 @@ add_catch2_executable(
                ../mock/multi-line-string.cpp
                ../mock/InitializedFontManager.cpp
                test-gui-Text.cpp
                test-gui-TextFixedSize.cpp
                test-gui-TextBlock.cpp
                test-gui-TextBlockCursor.cpp
                test-gui-TextDocument.cpp

A module-gui/test/test-catch-text/test-gui-TextFixedSize.cpp => module-gui/test/test-catch-text/test-gui-TextFixedSize.cpp +197 -0
@@ 0,0 1,197 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include <catch2/catch.hpp>

#include <module-gui/gui/widgets/TextFixedSize.hpp>
#include <module-gui/test/mock/InitializedFontManager.hpp>

namespace gui
{
    class TestTextFixedSize : public TextFixedSize
    {
      public:
        TestTextFixedSize() : TextFixedSize(nullptr, 0, 0, 0, 0){};

        [[nodiscard]] unsigned int linesSize()
        {
            return lines->get().size();
        }

        void drawLines() override
        {
            TextFixedSize::drawLines();
        }

        [[nodiscard]] auto lineGet(unsigned int nr)
        {
            auto line = lines->get().begin();

            if (nr >= lines->size()) {
                nr = lines->size() - 1;
            }

            std::advance(line, nr);
            return line;
        }

        auto removeNCharacters(unsigned int n)
        {
            for (unsigned int i = 0; i < n; i++) {
                removeChar();
            }
        }
    };
} // namespace gui

TEST_CASE("TextFixedSize drawLines")
{
    using namespace gui;
    mockup::fontManager();

    std::string testString1 = "Test \n";
    std::string testString2 = "Long \n";
    std::string testString3 = "Long \n";
    std::string testString4 = "Long \n";
    std::string testString5 = "Line \n";
    std::string testString6 = "End";

    std::string testStringLongLine = testString1 + testString2 + testString3 + testString4 + testString5 + testString6;

    SECTION("Empty text for space to draw 4 lines")
    {
        auto text = TestTextFixedSize();
        text.setSize(20, 120);

        text.setFont(style::window::font::medium);
        text.drawLines();

        REQUIRE(text.linesSize() == 4);
    }

    SECTION("Empty text for space to draw 4 lines but lines count limited to 2")
    {
        auto text = TestTextFixedSize();
        text.setSize(20, 120);

        text.setFont(style::window::font::medium);
        text.setLines(2);
        text.drawLines();

        REQUIRE(text.linesSize() == 2);
    }

    SECTION("Text for space to draw 4 lines - cursor moved to beginning")
    {
        auto text = TestTextFixedSize();
        text.setSize(200, 120);

        text.setCursorStartPosition(gui::CursorStartPosition::DocumentBegin);
        text.setFont(style::window::font::medium);
        text.setText(testStringLongLine);

        REQUIRE(text.lineGet(0)->getText(0) == testString1);
        REQUIRE(text.lineGet(1)->getText(0) == testString2);
        REQUIRE(text.lineGet(2)->getText(0) == testString3);
        REQUIRE(text.lineGet(3)->getText(0) == testString4);

        // There should be added extra invisible line.
        REQUIRE(text.linesSize() == 5);

        REQUIRE(text.lineGet(4)->getText(0) == testString5);
        REQUIRE_FALSE(text.lineGet(4)->isVisible());
    }

    SECTION("Text for space to draw 4 lines - cursor moved to end")
    {
        auto text = TestTextFixedSize();
        text.setSize(200, 120);

        text.setFont(style::window::font::medium);
        text.setText(testStringLongLine);

        REQUIRE(text.lineGet(0)->getText(0) == testString3);
        REQUIRE(text.lineGet(1)->getText(0) == testString4);
        REQUIRE(text.lineGet(2)->getText(0) == testString5);
        REQUIRE(text.lineGet(3)->getText(0) == testString6);
        REQUIRE(text.linesSize() == 4);
    }

    SECTION("Text for space to draw 4 lines but lines count limited to 2 - cursor moved to beginning")
    {
        auto text = TestTextFixedSize();
        text.setSize(200, 120);

        text.setCursorStartPosition(gui::CursorStartPosition::DocumentBegin);
        text.setFont(style::window::font::medium);
        text.setLines(2);
        text.setText(testStringLongLine);

        REQUIRE(text.lineGet(0)->getText(0) == testString1);
        REQUIRE(text.lineGet(1)->getText(0) == testString2);

        // There should be added extra invisible line.
        REQUIRE(text.linesSize() == 3);

        REQUIRE(text.lineGet(2)->getText(0) == testString3);
        REQUIRE_FALSE(text.lineGet(4)->isVisible());
    }

    SECTION("Text for space to draw 4 lines but lines count limited to 2 - cursor moved to end")
    {
        auto text = TestTextFixedSize();
        text.setSize(200, 120);

        text.setFont(style::window::font::medium);
        text.setLines(2);
        text.setText(testStringLongLine);

        REQUIRE(text.lineGet(0)->getText(0) == testString5);
        REQUIRE(text.lineGet(1)->getText(0) == testString6);
        REQUIRE(text.linesSize() == 2);
    }
}

TEST_CASE("TextFixedSize remove Char")
{
    using namespace gui;
    mockup::fontManager();

    std::string testString1 = "Test Line Line Line";
    std::string testString2 = "End";

    std::string testStringLongLine = testString1 + testString2;

    SECTION("Remove char from last line on size limited space")
    {
        auto text = TestTextFixedSize();
        text.setSize(210, 30);

        text.setFont(style::window::font::medium);
        text.setText(testStringLongLine);

        REQUIRE(text.lineGet(0)->getText(0) == testString2);
        REQUIRE(text.linesSize() == 1);

        text.removeNCharacters(testString2.size());
        REQUIRE(text.lineGet(0)->getText(0) == testString1);
        REQUIRE(text.linesSize() == 1);
    }

    SECTION("Remove char from last line on line limited space")
    {
        auto text = TestTextFixedSize();
        text.setSize(210, 120);

        text.setFont(style::window::font::medium);
        text.setLines(1);
        text.setText(testStringLongLine);

        REQUIRE(text.lineGet(0)->getText(0) == testString2);
        REQUIRE(text.linesSize() == 1);

        text.removeNCharacters(testString2.size());
        REQUIRE(text.lineGet(0)->getText(0) == testString1);
        REQUIRE(text.linesSize() == 1);
    }
}