/* * Copyright (c) 2021 - 2024 Stefan Strobel * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ /** * @file test_integration_history.cpp * @brief integration test implementation for the history buffer of the shellmatta * @author Stefan Strobel */ #include "test/framework/catch.hpp" extern "C" { #include "test/framework/fff.h" #include "shellmatta.h" } #include FAKE_VALUE_FUNC(shellmatta_retCode_t, writeFct, const char *, uint32_t) FAKE_VALUE_FUNC(shellmatta_retCode_t, cmdFct1, shellmatta_handle_t, const char *, uint32_t) FAKE_VALUE_FUNC(shellmatta_retCode_t, cmdFct2, shellmatta_handle_t, const char *, uint32_t) FAKE_VALUE_FUNC(shellmatta_retCode_t, cmdFct3, shellmatta_handle_t, const char *, uint32_t) FAKE_VALUE_FUNC(shellmatta_retCode_t, cmdFct4, shellmatta_handle_t, const char *, uint32_t) /* List of fakes */ #define FFF_FAKES_LIST(FAKE) \ FAKE(writeFct) \ FAKE(cmdFct1) \ FAKE(cmdFct2) \ FAKE(cmdFct3) \ FAKE(cmdFct4) #define CHECK_FOR_COMMAND(hist, idx) \ CHECK(writeFct_fake.call_count == ((hist) + 1u)); \ CHECK(0 == memcmp(writeFct_fake.arg0_history[(hist)], commandSequence[(idx)], strlen(commandSequence[(idx)]) - 1)); \ CHECK((strlen(commandSequence[(idx)]) - 1) == writeFct_fake.arg1_history[(hist)]); #define BUTTON_UP "\x1b" "[A" #define BUTTON_DOWN "\x1b" "[B" #define PROCESS_INPUT(input) \ CHECK(SHELLMATTA_OK == shellmatta_processData(handle, (char*)(input), sizeof((input)) - 1u)); static shellmatta_cmd_t cmd1 = {(char*)"cmd1", (char*)"1", NULL, NULL, cmdFct1, NULL}; static shellmatta_cmd_t cmd2 = {(char*)"cmd2", (char*)"2", NULL, NULL, cmdFct2, NULL}; static shellmatta_cmd_t cmd3 = {(char*)"cmd3", (char*)"3", NULL, NULL, cmdFct3, NULL}; static shellmatta_cmd_t cmd4 = {(char*)"cmd4", (char*)"4", NULL, NULL, cmdFct4, NULL}; char *commandSequence[] = { (char*)"foo\r", (char*)"bar\r", (char*)"cmd1\r", (char*)"2\r", (char*)"4\r", (char*)"cmd3\r" }; #define CMD_SEQ_LEN (sizeof(commandSequence) / sizeof(commandSequence[0])) SCENARIO("Test the history buffer with a fixed sequence of commands in there") { GIVEN("An initialized Shellmatta instance with some commands already in the history buffer") { shellmatta_instance_t inst; shellmatta_handle_t handle; char buffer[1024u]; char historyBuffer[1024u]; FFF_FAKES_LIST(RESET_FAKE) CHECK(SHELLMATTA_OK == shellmatta_doInit( &inst, &handle, buffer, sizeof(buffer), historyBuffer, sizeof(historyBuffer), "shellmatta->", NULL, writeFct)); CHECK(SHELLMATTA_OK == shellmatta_addCmd(handle, &cmd1)); CHECK(SHELLMATTA_OK == shellmatta_addCmd(handle, &cmd2)); CHECK(SHELLMATTA_OK == shellmatta_addCmd(handle, &cmd3)); CHECK(SHELLMATTA_OK == shellmatta_addCmd(handle, &cmd4)); for(uint32_t i = 0u; i < CMD_SEQ_LEN; i++) { CHECK(SHELLMATTA_OK == shellmatta_processData(handle, commandSequence[i], strlen(commandSequence[i]))); } WHEN("The up button is pressed") { THEN("The shellmatta prints the most recent command") { FFF_FAKES_LIST(RESET_FAKE) PROCESS_INPUT(BUTTON_UP) CHECK(writeFct_fake.call_count == 2); CHECK(0 == memcmp(writeFct_fake.arg0_history[0], "\x1b" "[K", 3)); CHECK(3 == writeFct_fake.arg1_history[0]); CHECK(0 == memcmp(writeFct_fake.arg0_history[1], commandSequence[CMD_SEQ_LEN - 1], strlen(commandSequence[CMD_SEQ_LEN - 1]) - 1)); CHECK((strlen(commandSequence[CMD_SEQ_LEN - 1]) - 1) == writeFct_fake.arg1_history[1]); for(uint32_t i = CMD_SEQ_LEN - 1; i > 0; i--) { FFF_FAKES_LIST(RESET_FAKE) PROCESS_INPUT(BUTTON_UP) CHECK(writeFct_fake.call_count == 3); CHECK(0 == memcmp(writeFct_fake.arg0_history[1], "\x1b" "[K", 3)); CHECK(3 == writeFct_fake.arg1_history[1]); CHECK(0 == memcmp(writeFct_fake.arg0_history[2], commandSequence[i - 1u], strlen(commandSequence[i - 1u]) - 1)); CHECK((strlen(commandSequence[i - 1u]) - 1) == writeFct_fake.arg1_history[2]); } } } WHEN("The history buffer it at the oldest command yet") { for(uint32_t i = CMD_SEQ_LEN; i > 0; i--) { PROCESS_INPUT(BUTTON_UP) } AND_WHEN("The up button is pressed again") { THEN("The output is deleted and the oldest command is printed again") { for(uint32_t i = 0u; i < 64; i++) { FFF_FAKES_LIST(RESET_FAKE) PROCESS_INPUT(BUTTON_UP) CHECK_FOR_COMMAND(2u, 0u) } } } AND_WHEN("The down button is pressed") { THEN("On each button press one newer command is printed") { for(uint32_t i = 1; i < CMD_SEQ_LEN; i++) { FFF_FAKES_LIST(RESET_FAKE) PROCESS_INPUT(BUTTON_DOWN) CHECK(0 == memcmp(writeFct_fake.arg0_history[1], "\x1b" "[K", 3)); CHECK(3 == writeFct_fake.arg1_history[1]); CHECK_FOR_COMMAND(2u, i) } } } } WHEN("The user pushes the up and down button alternately") { THEN("The output shall be updated with the correct command or not updated at all when at the end") { FFF_FAKES_LIST(RESET_FAKE) PROCESS_INPUT(BUTTON_UP) CHECK_FOR_COMMAND(1u, CMD_SEQ_LEN - 1u) FFF_FAKES_LIST(RESET_FAKE) PROCESS_INPUT(BUTTON_UP) CHECK_FOR_COMMAND(2u, CMD_SEQ_LEN - 2u) FFF_FAKES_LIST(RESET_FAKE) PROCESS_INPUT(BUTTON_DOWN) CHECK_FOR_COMMAND(2u, CMD_SEQ_LEN - 1u) FFF_FAKES_LIST(RESET_FAKE) PROCESS_INPUT(BUTTON_DOWN) CHECK(writeFct_fake.call_count == 0u); FFF_FAKES_LIST(RESET_FAKE) PROCESS_INPUT(BUTTON_DOWN) CHECK(writeFct_fake.call_count == 0u); FFF_FAKES_LIST(RESET_FAKE) PROCESS_INPUT(BUTTON_UP) CHECK_FOR_COMMAND(2u, CMD_SEQ_LEN - 2u) FFF_FAKES_LIST(RESET_FAKE) PROCESS_INPUT(BUTTON_UP) CHECK_FOR_COMMAND(2u, CMD_SEQ_LEN - 3u) FFF_FAKES_LIST(RESET_FAKE) PROCESS_INPUT(BUTTON_UP) CHECK_FOR_COMMAND(2u, CMD_SEQ_LEN - 4u) FFF_FAKES_LIST(RESET_FAKE) PROCESS_INPUT(BUTTON_UP) CHECK_FOR_COMMAND(2u, CMD_SEQ_LEN - 5u) FFF_FAKES_LIST(RESET_FAKE) PROCESS_INPUT(BUTTON_UP) CHECK_FOR_COMMAND(2u, CMD_SEQ_LEN - 6u) FFF_FAKES_LIST(RESET_FAKE) PROCESS_INPUT(BUTTON_UP) CHECK_FOR_COMMAND(2u, CMD_SEQ_LEN - 6u) /* back down again */ FFF_FAKES_LIST(RESET_FAKE) PROCESS_INPUT(BUTTON_DOWN) CHECK_FOR_COMMAND(2u, CMD_SEQ_LEN - 5u) FFF_FAKES_LIST(RESET_FAKE) PROCESS_INPUT(BUTTON_DOWN) CHECK_FOR_COMMAND(2u, CMD_SEQ_LEN - 4u) FFF_FAKES_LIST(RESET_FAKE) PROCESS_INPUT(BUTTON_DOWN) CHECK_FOR_COMMAND(2u, CMD_SEQ_LEN - 3u) FFF_FAKES_LIST(RESET_FAKE) PROCESS_INPUT(BUTTON_DOWN) CHECK_FOR_COMMAND(2u, CMD_SEQ_LEN - 2u) FFF_FAKES_LIST(RESET_FAKE) PROCESS_INPUT(BUTTON_DOWN) CHECK_FOR_COMMAND(2u, CMD_SEQ_LEN - 1u) /* end of the buffer - shellmatta shall not update */ FFF_FAKES_LIST(RESET_FAKE) PROCESS_INPUT(BUTTON_DOWN) CHECK(writeFct_fake.call_count == 0u); FFF_FAKES_LIST(RESET_FAKE) PROCESS_INPUT(BUTTON_DOWN) CHECK(writeFct_fake.call_count == 0u); FFF_FAKES_LIST(RESET_FAKE) PROCESS_INPUT(BUTTON_UP) CHECK_FOR_COMMAND(2u, CMD_SEQ_LEN - 2u) } } } } SCENARIO("Test how the history buffer handles more commands than fits inside the buffer") { GIVEN("An initialized Shellmatta instance with some commands already in the history buffer") { shellmatta_instance_t inst; shellmatta_handle_t handle; char buffer[1024u]; char historyBuffer[16u] = {0}; FFF_FAKES_LIST(RESET_FAKE) CHECK(SHELLMATTA_OK == shellmatta_doInit( &inst, &handle, buffer, sizeof(buffer), historyBuffer, sizeof(historyBuffer), "shellmatta->", NULL, writeFct)); CHECK(SHELLMATTA_OK == shellmatta_addCmd(handle, &cmd1)); CHECK(SHELLMATTA_OK == shellmatta_addCmd(handle, &cmd2)); CHECK(SHELLMATTA_OK == shellmatta_addCmd(handle, &cmd3)); CHECK(SHELLMATTA_OK == shellmatta_addCmd(handle, &cmd4)); for(uint32_t i = 0u; i < CMD_SEQ_LEN; i++) { CHECK(SHELLMATTA_OK == shellmatta_processData(handle, commandSequence[i], strlen(commandSequence[i]))); } WHEN("The user pushes the up and down button alternately") { THEN("The output shall be updated with the correct commands that did fit into the buffer") { FFF_FAKES_LIST(RESET_FAKE) PROCESS_INPUT(BUTTON_UP) CHECK_FOR_COMMAND(1u, CMD_SEQ_LEN - 1u) FFF_FAKES_LIST(RESET_FAKE) PROCESS_INPUT(BUTTON_UP) CHECK_FOR_COMMAND(2u, CMD_SEQ_LEN - 2u) FFF_FAKES_LIST(RESET_FAKE) PROCESS_INPUT(BUTTON_UP) CHECK_FOR_COMMAND(2u, CMD_SEQ_LEN - 3u) FFF_FAKES_LIST(RESET_FAKE) PROCESS_INPUT(BUTTON_UP) CHECK_FOR_COMMAND(2u, CMD_SEQ_LEN - 4u) FFF_FAKES_LIST(RESET_FAKE) PROCESS_INPUT(BUTTON_UP) CHECK_FOR_COMMAND(2u, CMD_SEQ_LEN - 4u) FFF_FAKES_LIST(RESET_FAKE) PROCESS_INPUT(BUTTON_DOWN) CHECK_FOR_COMMAND(2u, CMD_SEQ_LEN - 3u) FFF_FAKES_LIST(RESET_FAKE) PROCESS_INPUT(BUTTON_DOWN) CHECK_FOR_COMMAND(2u, CMD_SEQ_LEN - 2u) FFF_FAKES_LIST(RESET_FAKE) PROCESS_INPUT(BUTTON_UP) CHECK_FOR_COMMAND(2u, CMD_SEQ_LEN - 3u) FFF_FAKES_LIST(RESET_FAKE) PROCESS_INPUT(BUTTON_DOWN) CHECK_FOR_COMMAND(2u, CMD_SEQ_LEN - 2u) FFF_FAKES_LIST(RESET_FAKE) PROCESS_INPUT(BUTTON_DOWN) CHECK_FOR_COMMAND(2u, CMD_SEQ_LEN - 1u) } } WHEN("A command dowes not fit into the history buffer") { FFF_FAKES_LIST(RESET_FAKE) PROCESS_INPUT("This is a very long command input\r") THEN("The input is not stored") { FFF_FAKES_LIST(RESET_FAKE) PROCESS_INPUT(BUTTON_UP) CHECK_FOR_COMMAND(1u, CMD_SEQ_LEN - 1u) FFF_FAKES_LIST(RESET_FAKE) PROCESS_INPUT(BUTTON_UP) CHECK_FOR_COMMAND(2u, CMD_SEQ_LEN - 2u) FFF_FAKES_LIST(RESET_FAKE) PROCESS_INPUT(BUTTON_UP) CHECK_FOR_COMMAND(2u, CMD_SEQ_LEN - 3u) FFF_FAKES_LIST(RESET_FAKE) PROCESS_INPUT(BUTTON_UP) CHECK_FOR_COMMAND(2u, CMD_SEQ_LEN - 4u) FFF_FAKES_LIST(RESET_FAKE) PROCESS_INPUT(BUTTON_UP) CHECK_FOR_COMMAND(2u, CMD_SEQ_LEN - 4u) } } } } SCENARIO("Test if the history buffer stores changes done during navigating") { GIVEN("An initialized Shellmatta instance with some commands already in the history buffer") { shellmatta_instance_t inst; shellmatta_handle_t handle; char buffer[1024u]; char historyBuffer[16u] = {0}; FFF_FAKES_LIST(RESET_FAKE) CHECK(SHELLMATTA_OK == shellmatta_doInit( &inst, &handle, buffer, sizeof(buffer), historyBuffer, sizeof(historyBuffer), "shellmatta->", NULL, writeFct)); CHECK(SHELLMATTA_OK == shellmatta_addCmd(handle, &cmd1)); CHECK(SHELLMATTA_OK == shellmatta_addCmd(handle, &cmd2)); CHECK(SHELLMATTA_OK == shellmatta_addCmd(handle, &cmd3)); CHECK(SHELLMATTA_OK == shellmatta_addCmd(handle, &cmd4)); for(uint32_t i = 0u; i < CMD_SEQ_LEN; i++) { CHECK(SHELLMATTA_OK == shellmatta_processData(handle, commandSequence[i], strlen(commandSequence[i]))); } WHEN("The user pushes the up and down button alternately and inputs data in between") { THEN("The output shall be updated with the correct commands and the input shall be stored") { FFF_FAKES_LIST(RESET_FAKE) PROCESS_INPUT(BUTTON_UP) CHECK_FOR_COMMAND(1u, CMD_SEQ_LEN - 1u) FFF_FAKES_LIST(RESET_FAKE) PROCESS_INPUT(BUTTON_UP) CHECK_FOR_COMMAND(2u, CMD_SEQ_LEN - 2u) PROCESS_INPUT("\b123456") FFF_FAKES_LIST(RESET_FAKE) PROCESS_INPUT(BUTTON_UP) CHECK_FOR_COMMAND(2u, CMD_SEQ_LEN - 3u) FFF_FAKES_LIST(RESET_FAKE) PROCESS_INPUT(BUTTON_DOWN) CHECK_FOR_COMMAND(2u, CMD_SEQ_LEN - 2u) FFF_FAKES_LIST(RESET_FAKE) PROCESS_INPUT(BUTTON_DOWN) CHECK_FOR_COMMAND(2u, CMD_SEQ_LEN - 1u) FFF_FAKES_LIST(RESET_FAKE) PROCESS_INPUT(BUTTON_DOWN) CHECK(writeFct_fake.call_count == 3); CHECK(0 == memcmp(writeFct_fake.arg0_history[2], "123456", 6)); CHECK(6 == writeFct_fake.arg1_history[2]); PROCESS_INPUT("\r") FFF_FAKES_LIST(RESET_FAKE) PROCESS_INPUT(BUTTON_UP) CHECK(writeFct_fake.call_count == 2); CHECK(0 == memcmp(writeFct_fake.arg0_history[1], "123456", 6)); CHECK(6 == writeFct_fake.arg1_history[1]); FFF_FAKES_LIST(RESET_FAKE) PROCESS_INPUT(BUTTON_UP) CHECK_FOR_COMMAND(2u, CMD_SEQ_LEN - 1u) PROCESS_INPUT("\x03" "12345678\r") FFF_FAKES_LIST(RESET_FAKE) PROCESS_INPUT(BUTTON_UP) CHECK(writeFct_fake.call_count == 2); CHECK(0 == memcmp(writeFct_fake.arg0_history[1], "12345678", 8)); CHECK(8 == writeFct_fake.arg1_history[1]); PROCESS_INPUT("\r") FFF_FAKES_LIST(RESET_FAKE) PROCESS_INPUT(BUTTON_UP) CHECK(writeFct_fake.call_count == 2); CHECK(0 == memcmp(writeFct_fake.arg0_history[1], "12345678", 8)); CHECK(8 == writeFct_fake.arg1_history[1]); FFF_FAKES_LIST(RESET_FAKE) PROCESS_INPUT(BUTTON_UP) CHECK(writeFct_fake.call_count == 3); CHECK(0 == memcmp(writeFct_fake.arg0_history[2], "123456", 6)); CHECK(6 == writeFct_fake.arg1_history[2]); /* check if the compare gets it when the new command is exactly one character longer than the stored */ PROCESS_INPUT("\x03" "123456789\r") FFF_FAKES_LIST(RESET_FAKE) PROCESS_INPUT(BUTTON_UP) CHECK(writeFct_fake.call_count == 2); CHECK(0 == memcmp(writeFct_fake.arg0_history[1], "123456789", 9)); CHECK(9 == writeFct_fake.arg1_history[1]); FFF_FAKES_LIST(RESET_FAKE) PROCESS_INPUT(BUTTON_UP) CHECK(writeFct_fake.call_count == 3); CHECK(0 == memcmp(writeFct_fake.arg0_history[2], "123456789", 9)); CHECK(9 == writeFct_fake.arg1_history[2]); /* check if the compare gets it when the last command ist longer than the new one */ PROCESS_INPUT("\x03" "12345678\r") FFF_FAKES_LIST(RESET_FAKE) PROCESS_INPUT(BUTTON_UP) CHECK(writeFct_fake.call_count == 2); CHECK(0 == memcmp(writeFct_fake.arg0_history[1], "12345678", 8)); CHECK(8 == writeFct_fake.arg1_history[1]); /* check what happens when there is a \0 in the buffer */ PROCESS_INPUT("\x03" "1234" "\0" "678\r") FFF_FAKES_LIST(RESET_FAKE) PROCESS_INPUT(BUTTON_UP) CHECK(writeFct_fake.call_count == 2); CHECK(0 == memcmp(writeFct_fake.arg0_history[1], "1234", 4)); CHECK(4 == writeFct_fake.arg1_history[1]); /* check what happens when there is a \0 in the buffer */ PROCESS_INPUT("\x03" "1234" "\0" "888\r") FFF_FAKES_LIST(RESET_FAKE) PROCESS_INPUT(BUTTON_UP) CHECK(writeFct_fake.call_count == 2); CHECK(0 == memcmp(writeFct_fake.arg0_history[1], "1234", 4)); CHECK(4 == writeFct_fake.arg1_history[1]); FFF_FAKES_LIST(RESET_FAKE) PROCESS_INPUT(BUTTON_UP) CHECK(writeFct_fake.call_count == 3); CHECK(0 == memcmp(writeFct_fake.arg0_history[2], "888", 3)); CHECK(3 == writeFct_fake.arg1_history[2]); } } } }