Compare commits

...

24 Commits

Author SHA1 Message Date
shimatta
28e82c65de Merge branch 'develop' of shimatta/shellmatta into master 2021-01-24 20:18:59 +01:00
shimatta
4d542f973b Merge branch 'feature/#55-help-command-shall-only-print-help-and-usage-as-derailled-output' of shimatta/shellmatta into develop 2021-01-24 20:15:22 +01:00
prozessorkern
c1f42d8239 checking for buffer overflow in insertChars function 2021-01-24 20:10:43 +01:00
prozessorkern
a5bd5c57ab added detailed help function 2021-01-24 19:46:12 +01:00
prozessorkern
e3c35bd0d5 improved doxygen documentation 2021-01-24 01:10:44 +01:00
prozessorkern
eb524436ce improved style and fixed some cppcheck findings 2021-01-24 00:10:43 +01:00
shimatta
a9b8dcb504 Merge branch 'feature/#52-only-store-new-commands-in-history' of shimatta/shellmatta into develop
fix #52
2021-01-23 23:40:31 +01:00
prozessorkern
c7f238c005 fixed comments 2021-01-23 23:38:47 +01:00
prozessorkern
0b0a52d898 fixed the history buffer when there is an overflow
added test cases
2021-01-23 23:35:32 +01:00
prozessorkern
e6b45952b3 added tests for shellmatta_history and improved documentation
added content to the readme
added fff for function faking
fixed coverage reports of the integrationtest
added testscenarios to test the history buffer
2021-01-22 23:13:20 +01:00
prozessorkern
438e4c4d76 fix dirty flag 2021-01-20 21:22:23 +01:00
prozessorkern
ded69bc1f4 make mario happy 2021-01-20 21:16:53 +01:00
prozessorkern
88c33895f6 added check if the current entered command matches the last command in the history buffer - if the command is already stored it will not be stored again 2021-01-20 21:13:01 +01:00
prozessorkern
ac6ffb9602 fixed findings from static analysis 2020-12-02 15:47:23 +01:00
shimatta
6cfd157408 Merge branch '#50_Add_unittest_for_shellmatta_opt_peekNextHunk' of shimatta/shellmatta into develop 2020-12-02 15:39:53 +01:00
S.Hentges
68ec0ab3dc refactor testcases, delete doubling testcases 2020-11-11 21:25:05 +01:00
S.Hentges
e495346d44 Add unittest for otp_peekNextHunk 2020-10-31 15:16:16 +01:00
sebastian
ca293841e8 Merge branch '#48_Add_unittest_for_shellmatte_utils_removeChars' of shimatta/shellmatta into develop 2020-10-30 22:32:08 +01:00
prozessorkern
f5f9c62493 tried to improve the documentation of the function utils_removeChars + made it more defensive + fixed the testcases 2020-06-02 18:40:24 +02:00
S.Hentges
2060cd61eb Expand unittest for utils_removeChars 2020-05-28 22:39:53 +02:00
S.Hentges
15c846a9f1 Fix identation 2020-05-18 21:14:47 +02:00
S.Hentges
0ae7e24470 Revert "Fix identation"
This reverts commit 0ebbcc602c.
2020-05-18 21:10:26 +02:00
S.Hentges
0ebbcc602c Fix identation 2020-05-17 20:15:40 +02:00
S.Hentges
536643a462 Add Unittest for test_utils_removeChars.cpp 2020-05-17 20:09:58 +02:00
31 changed files with 8376 additions and 273 deletions

2
.vscode/launch.json vendored
View File

@@ -9,7 +9,7 @@
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/output/example/example",
"args": ["/dev/pts/4"],
"args": ["/dev/pts/2"],
"stopAtEntry": false,
"cwd": "${workspaceFolder}",
"environment": [],

189
README.md
View File

@@ -1,3 +1,188 @@
# shellmatta
# Shellmatta
A tiny and flexible shell implementation to be used on embedded devices.
A tiny and flexible shell implementation to be used on embedded devices.
## Name
The name `shellmatta` is the combination of `shell` and `shimatta`.
What the hell is `shimatta` you might ask.
...well if you really wanna know you might reach out to these nerds that are
running domains like [shimatta.net](https://git.shimatta.net) or
[shimatta.de](https://git.shimatta.de).
Do not pretend i didn't warn you.
## Intention
The intention is to enable a software project of nearly any size to have a
runtime command line interface to simplify debugging or
configuration/calibration of any kind of device.
The `shellmatta` is designed to fit in most tiny microcontroller.
It is based on a simple character based interface and can work with for example
network sockets or simple uarts.
Some features are removable at build time to save ressources on really tiny
platforms.
## Features
The `shellmatta` piled up some features over time:
1. history buffer (configurable)
2. auto complete
3. heredoc like interface to pass multiline data
4. option parser (getopt like)
## Documentation
Besides this readme most documentation is integrated directly in the sourcecode
as doxygen parsable comments.
To build the doxygen documentation just run `make doc`.
The html and latex documentation will be exported to `output/doc`
## Integration into your project
The basic integration into a softwareproject is quite easy.
1. add all *.c files from the `src` to your build
2. include the `api/shellmatta.h` file
3. implement a write function to output the data from the shell
4. initialize the shell providing static buffers
5. implement and add commands
6. pass data into the shellmatta and watch the magic happen
7. static constant command list (if you do not like dynamic lists)
Code example:
```
#include "shellmatta.h"
#include <unistd.h>
shellmatta_retCode_t writeFct(const char* data, uint32_t length)
{
write(1, data, length);
return SHELLMATTA_OK;
}
static shellmatta_retCode_t exampleCmdFct(shellmatta_handle_t handle, const char *arguments, uint32_t length)
{
shellmatta_retCode_t ret;
char option;
static const shellmatta_opt_long_t options[] =
{
{"version", 'v', SHELLMATTA_OPT_ARG_NONE},
{NULL, '\0', SHELLMATTA_OPT_ARG_NONE}
};
ret = shellmatta_opt_long(handle, options, &option, NULL, NULL);
while(SHELLMATTA_OK == ret)
{
switch(option)
{
case 'v':
shellmatta_printf(handle, "This should represent the version of this command");
break;
default:
shellmatta_printf(handle, "Unknown option: %c\r\n", option);
break;
}
ret = shellmatta_opt_long(handle, options, &option, NULL, NULL);
}
(void)arguments;
(void)length;
return SHELLMATTA_OK;
}
shellmatta_cmd_t exampleCmd = { "example", /**< command name */
"e", /**< command alias */
"example command", /**< command help */
"example [options]\n" /**< command usage text */
"\t-v, --version - print the version of the command",
exampleCmdFct, /**< command function */
NULL}; /**< intenally used */
int main(void)
{
static char buffer[1024]; /**< memory for inptu buffer */
static char historyBuffer[4096]; /**< memory for history buffer */
static shellmatta_instance_t instance; /**< instance variable */
shellmatta_handle_t handle; /**< handle used for accessing */
/** -# initialize the shellmatta instance */
shellmatta_doInit( &instance,
&handle,
buffer,
sizeof(buffer),
historyBuffer,
sizeof(historyBuffer),
"shellmatta->", /**< prompt text */
NULL, /**< optional static command list */
writeFct); /**< write function */
/** -# add the command - one command can only be added to one instance */
shellmatta_addCmd(handle, &exampleCmd);
/** -# ready to put some data in there */
shellmatta_processData(handle, "example --version\r", 18u);
return 0;
}
```
## Compile time configuration
There are some defines you can use to change the behaviour of the shellmatta:
| Define | Description |
| -------------------------- | ---------------------------------------------- |
| SHELLMATTA_STRIP_PRINTF | removes stdio dependencies to reduce footprint |
| SHELLMATTA_HELP_COMMAND | string to overwrite the help command name |
| SHELLMATTA_HELP_ALIAS | string to overwrite the help command alias |
| SHELLMATTA_HELP_HELP_TEXT | string to overwrite the help command help |
| SHELLMATTA_HELP_USAGE_TEXT | string to overwrite the help command usage |
## Example
There is a quite confusing example in this repo to show and test some features.
To build it just rum `make example`.
The binary will be built to `output/example/example`
It requires a serial device as parameter to run e.g. `/dev/tty0`
To be able to play around a bit you can create a local serial loopback using
socat.
```
socat -d -d pty,raw,echo=0 pty,raw,echo=0
```
This will create two serial devices which are connected with a loopback.
The device numbers in this example might change on your system.
You can use one of them starting the example e.g.
```
./output/example/example /dev/pts2
```
And use the other one to connect using the terminal tool of your choice e.g.
```
minicom -D /dev/pts3
```
## Running tests
There are some tests implemented using catch2 and the function fake framework.
To run the tests just do:
```
make test
```
To be able to build the coverage report you need an installation of
(gcovr)[https://pypi.org/project/gcovr].

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2019 Stefan Strobel <stefan.strobel@shimatta.net>
* Copyright (c) 2019 - 2021 Stefan Strobel <stefan.strobel@shimatta.net>
*
* 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
@@ -13,7 +13,7 @@
*/
/**
* @addtogroup shellmatta_api
* @addtogroup shellmatta_api Shellmatta API description
* @{
*/
@@ -109,7 +109,7 @@ typedef struct shellmatta_cmd
char *cmd; /**< command name */
char *cmdAlias; /**< command alias */
char *helpText; /**< help text to print in "help" command */
char *usageText; /**< usage text to print on parameter error */
char *usageText; /**< usage text - printed on "help cmd" */
shellmatta_cmdFct_t cmdFct; /**< pointer to the cmd callack function */
struct shellmatta_cmd *next; /**< pointer to next command or NULL */
} shellmatta_cmd_t;

View File

@@ -830,7 +830,8 @@ WARN_LOGFILE =
# Note: If this tag is empty the current directory is searched.
INPUT = src \
api
api \
doc
# This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
@@ -2474,7 +2475,7 @@ DIAFILE_DIRS =
# generate a warning when it encounters a \startuml command in this case and
# will not generate output for the diagram.
PLANTUML_JAR_PATH =
PLANTUML_JAR_PATH = /usr/bin
# When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a
# configuration file for plantuml.

11
doc/main.dox Normal file
View File

@@ -0,0 +1,11 @@
/**
@mainpage
This is the entry to the doxygen documentation of the shellmatta library.
Please find the documenation here:
@subpage shellmatta
*/

35
doc/shellmatta.dox Normal file
View File

@@ -0,0 +1,35 @@
/**
@page shellmatta Shellmatta
The shellmatta is a tiny shell implementation to be integrated in all kinds
of software projects to add a debug and configuration interface.
Please take a look at the README.md file for some information that might
not be included here.
@section shellmatta_api_section Shellmatta api description
The complete api of the shellmatta is included in the file api/shellmatta.h.
The api description can be found here:
@subpage shellmatta_api
This is how the classic usage looks like:
@startuml
App -> Shellmatta: shellmatta_doInit()
loop for every command
App -> Shellmatta: shellmatta_addCmd(command)
end
loop until finished
IO -> Shellmatta: shellmatta_processData(data)
Shellmatta -> App: call command function
App -> Shellmatta: shellmatta_printf(output data)
Shellmatta -> IO: write(data)
end
@enduml
*/

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2019 Stefan Strobel <stefan.strobel@shimatta.net>
* Copyright (c) 2019 - 2021 Stefan Strobel <stefan.strobel@shimatta.net>
*
* 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

View File

@@ -1,5 +1,5 @@
#
# Copyright (c) 2019 Stefan Strobel <stefan.strobel@shimatta.net>
# Copyright (c) 2019 - 2021 Stefan Strobel <stefan.strobel@shimatta.net>
#
# 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
@@ -7,6 +7,9 @@
#
OBJ_DIR := output/
INTEGRATIONTEST_CPP_OBJ_DIR := $(OBJ_DIR)test/integrationtest/
INTEGRATIONTEST_C_OBJ_DIR := $(INTEGRATIONTEST_CPP_OBJ_DIR)
UNITTEST_OBJ_DIR := $(OBJ_DIR)test/unittest/
CC := gcc
CPP := g++
@@ -23,6 +26,7 @@ INCLUDES := api .
UNITTEST_SOURCES := test/unittest/test_main.cpp \
test/unittest/shellmatta_opt/test_opt_findNextHunk.cpp \
test/unittest/shellmatta_opt/test_opt_peekNextHunk.cpp \
test/unittest/shellmatta_utils/test_utils_writeEcho.cpp \
test/unittest/shellmatta_utils/test_utils_shellItoa.cpp \
test/unittest/shellmatta_utils/test_utils_saveCursorPos.cpp \
@@ -33,6 +37,7 @@ UNITTEST_SOURCES := test/unittest/test_main.cpp
test/unittest/shellmatta_utils/test_utils_clearInput.cpp \
test/unittest/shellmatta_utils/test_utils_insertChars.cpp \
test/unittest/shellmatta_utils/test_utils_terminateInput.cpp \
test/unittest/shellmatta_utils/test_utils_removeChars.cpp \
test/unittest/shellmatta_autocomplete/test_autocomplete_run.cpp \
test/unittest/shellmatta_escape/test_escape_processArrowKeys.cpp \
test/unittest/shellmatta_history/test_appendHistoryByte.cpp \
@@ -43,14 +48,17 @@ INTEGRATIONTEST_SOURCES := test/integrationtest/test_main.cpp
test/integrationtest/test_integration_opt.cpp \
test/integrationtest/test_integration_optLong.cpp \
test/integrationtest/test_integration_continue.cpp \
test/integrationtest/test_integration_busy.cpp
test/integrationtest/test_integration_busy.cpp \
test/integrationtest/test_integration_history.cpp \
test/integrationtest/test_integration_help.cpp
UNITTEST_CPPOBJ := $(patsubst %.cpp,$(OBJ_DIR)%.o,$(UNITTEST_SOURCES))
UNITTEST_CPPOBJ := $(patsubst %.cpp,$(UNITTEST_OBJ_DIR)%.o,$(UNITTEST_SOURCES))
INTEGRATIONTEST_CPPOBJ := $(patsubst %.cpp,$(OBJ_DIR)%.o,$(INTEGRATIONTEST_SOURCES))
INTEGRATIONTEST_CPPOBJ := $(patsubst %.cpp,$(INTEGRATIONTEST_CPP_OBJ_DIR)%.o,$(INTEGRATIONTEST_SOURCES))
INTEGRATIONTEST_COBJ := $(patsubst %.c,$(INTEGRATIONTEST_CPP_OBJ_DIR)%.o,$(SOURCES))
CFLAGS := $(INCLUDES:%=-I%) -g -Wall -Werror -Wextra -pedantic -DSHELLMATTA_HELP_ALIAS=\"?\"
TESTFLAGS := $(INCLUDES:%=-I%) -g -Wall -Werror -Wextra -fprofile-arcs -ftest-coverage -pedantic
CFLAGS := $(INCLUDES:%=-I%) -g -Wall -Werror -Wextra -pedantic -DSHELLMATTA_HELP_ALIAS=\(char*\)\"?\"
TESTFLAGS := $(CFLAGS) -fprofile-arcs -ftest-coverage
TESTLFLAGS := -fprofile-arcs -Wl,--allow-multiple-definition
DEPEND = -MT $@ -MF "$(@:%.o=%.d)" -MG -MM
@@ -66,11 +74,13 @@ UNITTEST_TARGET := $(OBJ_DIR)test/unittest/unittest
INTEGRATIONTEST_TARGET := $(OBJ_DIR)test/integrationtest/integrationtest
OBJ := $(COBJ) $(EXAMPLE_COBJ) $(UNITTEST_CPPOBJ) $(INTEGRATIONTEST_CPPOBJ)
OBJ := $(COBJ) $(EXAMPLE_COBJ) $(UNITTEST_CPPOBJ) $(INTEGRATIONTEST_CPPOBJ) $(INTEGRATIONTEST_COBJ)
DEPS := $(OBJ:%.o=%.d)
export
.PHONY: help cppcheck doc clean
help:
@echo Shellmatta help
@echo -----------------------------------------------
@@ -90,24 +100,25 @@ cppcheck:
unittest: $(UNITTEST_TARGET)
- @mkdir -p output/test/unittest/report
@echo running test:
@# remove coverage from former run
# remove coverage from former run
@-find . -name "*.gcda" -type f -delete
-$(UNITTEST_TARGET)
@#gcov -o output/test/unittest $(UNITTEST_CPPOBJ) -r src
@# remove report from former run
# remove report from former run
-rm -rf $(OBJ_DIR)test/unittest/report/*
gcovr --html-details --output $(OBJ_DIR)test/unittest/report/report.html output/test/unittest -f src -f api
@#-rm *.gcov
gcovr --html-details --output $(OBJ_DIR)test/unittest/report/report.html output/test/unittest -f src -f api -d
integrationtest: $(INTEGRATIONTEST_TARGET)
- @mkdir -p output/test/integrationtest/report
@echo running test:
# remove coverage from former run
# @-find . -name "*.gcda" -type f -delete
-$(INTEGRATIONTEST_TARGET)
#gcov -o output/test $(TEST_CPPOBJ) -r
gcovr --html-details --output $(OBJ_DIR)test/integrationtest/report/report.html output/src -f src -f api
#-rm *.gcov
# remove report from former run
# -rm -rf $(OBJ_DIR)test/unittest/report/*
gcovr --html-details --output $(OBJ_DIR)test/integrationtest/report/report.html output/test/integrationtest -f src -f api -d
example: $(EXAMPLE_TARGET)
@echo building example
@@ -127,7 +138,7 @@ $(UNITTEST_TARGET): $(UNITTEST_CPPOBJ)
- @mkdir -p $(@D)
$(CPP) $(TESTLFLAGS) $(LIB_PATH) -o $@ $^ $(LIBS)
$(INTEGRATIONTEST_TARGET): $(INTEGRATIONTEST_CPPOBJ) $(COBJ)
$(INTEGRATIONTEST_TARGET): $(INTEGRATIONTEST_CPPOBJ) $(INTEGRATIONTEST_COBJ)
- @mkdir -p $(@D)
$(CPP) $(TESTLFLAGS) $(LIB_PATH) -o $@ $^ $(LIBS)
@@ -145,10 +156,20 @@ $(EXAMPLE_COBJ):
@$(CC) -c $(CFLAGS) $(DEPEND) -o $@ $(subst $(OBJ_DIR), ,$(@:%.o=%.c))
$(CC) -c $(CFLAGS) -o $@ $(subst $(OBJ_DIR), ,$(@:%.o=%.c))
$(UNITTEST_CPPOBJ) $(INTEGRATIONTEST_CPPOBJ):
$(UNITTEST_CPPOBJ):
- @mkdir -p $(@D)
@$(CPP) -c $(TESTFLAGS) $(DEPEND) -o $@ $(subst $(OBJ_DIR), ,$(@:%.o=%.cpp))
$(CPP) -c $(TESTFLAGS) -o $@ $(subst $(OBJ_DIR), ,$(@:%.o=%.cpp))
@$(CPP) -c $(TESTFLAGS) $(DEPEND) -o $@ $(subst $(UNITTEST_OBJ_DIR), ,$(@:%.o=%.cpp))
$(CPP) -c $(TESTFLAGS) -o $@ $(subst $(UNITTEST_OBJ_DIR), ,$(@:%.o=%.cpp))
$(INTEGRATIONTEST_CPPOBJ):
- @mkdir -p $(@D)
@$(CPP) -c $(TESTFLAGS) $(DEPEND) -o $@ $(subst $(INTEGRATIONTEST_CPP_OBJ_DIR), ,$(@:%.o=%.cpp))
$(CPP) -c $(TESTFLAGS) -o $@ $(subst $(INTEGRATIONTEST_CPP_OBJ_DIR), ,$(@:%.o=%.cpp))
$(INTEGRATIONTEST_COBJ):
- @mkdir -p $(@D)
@$(CC) -c $(TESTFLAGS) $(DEPEND) -o $@ $(subst $(INTEGRATIONTEST_C_OBJ_DIR), ,$(@:%.o=%.c))
$(CC) -c $(TESTFLAGS) -o $@ $(subst $(INTEGRATIONTEST_C_OBJ_DIR), ,$(@:%.o=%.c))
%.o: %.cpp
- @mkdir -p $(@D)

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2019 Stefan Strobel <stefan.strobel@shimatta.net>
* Copyright (c) 2019 - 2021 Stefan Strobel <stefan.strobel@shimatta.net>
*
* 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
@@ -193,8 +193,8 @@ shellmatta_retCode_t shellmatta_addCmd(shellmatta_handle_t handle, shellmatta_cm
shellmatta_cmd_t **prevCmd;
bool cmdPlaced = false;
shellmatta_retCode_t ret = SHELLMATTA_OK;
int cmdDiff = 0;
int aliasDiff = 0;
int cmdDiff;
int aliasDiff;
/** -# check parameters for plausibility */
if( (NULL != inst)
@@ -203,8 +203,8 @@ shellmatta_retCode_t shellmatta_addCmd(shellmatta_handle_t handle, shellmatta_cm
&& (NULL != cmd)
&& (NULL != cmd->cmd))
{
tempCmd = inst->cmdList;
prevCmd = &inst->cmdList;
tempCmd = inst->cmdList;
prevCmd = &inst->cmdList;
/** -# register first command as list entry */
if (NULL == tempCmd)
{
@@ -216,7 +216,7 @@ shellmatta_retCode_t shellmatta_addCmd(shellmatta_handle_t handle, shellmatta_cm
{
while ((false == cmdPlaced) && (SHELLMATTA_OK == ret))
{
cmdDiff = strcmp(tempCmd->cmd, cmd->cmd);
cmdDiff = strcmp(tempCmd->cmd, cmd->cmd);
if( (NULL != cmd->cmdAlias)
&& (NULL != tempCmd->cmdAlias))
{
@@ -232,7 +232,7 @@ shellmatta_retCode_t shellmatta_addCmd(shellmatta_handle_t handle, shellmatta_cm
{
ret = SHELLMATTA_DUPLICATE;
}
else if(0 < cmdDiff)
else if(cmdDiff > 0)
{
cmd->next = tempCmd;
*prevCmd = cmd;
@@ -289,9 +289,8 @@ shellmatta_retCode_t shellmatta_removeCmd(shellmatta_handle_t handle, shellmatta
while(NULL != tempCmd)
{
/** -# compare command strings to find the command to delete */
if (0 == strcmp( tempCmd->cmd,
cmd->cmd)
&& (strlen(tempCmd->cmd) == strlen(cmd->cmd)))
if (0 == strcmp(tempCmd->cmd, cmd->cmd)
&& (strlen(tempCmd->cmd) == strlen(cmd->cmd)))
{
/** -# first command removed */
if(NULL == prevCmd)
@@ -344,7 +343,7 @@ shellmatta_retCode_t shellmatta_configure( shellmatta_handle_t handle,
/** -# check parameters for plausibility */
if( (NULL != inst)
&& (SHELLMATTA_MAGIC == inst->magic)
&& ((mode == SHELLMATTA_MODE_INSERT) || (mode == SHELLMATTA_MODE_OVERWRITE)))
&& ((SHELLMATTA_MODE_INSERT == mode) || (SHELLMATTA_MODE_OVERWRITE == mode)))
{
inst->mode = mode;
inst->echoEnabled = echoEnabled;
@@ -427,6 +426,10 @@ shellmatta_retCode_t shellmatta_processData(shellmatta_handle_t handle,
utils_terminateInput(inst);
}
}
else
{
/* nothing to do here - continue parsing the command */
}
/** -# process byte wise */
for (; (inst->byteCounter < size) && (NULL == inst->busyCmd); inst->byteCounter++)
@@ -597,15 +600,11 @@ shellmatta_retCode_t shellmatta_processData(shellmatta_handle_t handle,
while (NULL != cmd)
{
/** -# compare command and alias string and length */
if ( ((0 == strncmp( inst->buffer,
cmd->cmd,
cmdLen))
&& (cmdLen == strlen(cmd->cmd)))
if ( ((cmdLen == strlen(cmd->cmd))
&& (0 == strncmp(inst->buffer, cmd->cmd, cmdLen)))
|| ((NULL != cmd->cmdAlias)
&& ((0 == strncmp( inst->buffer,
cmd->cmdAlias,
cmdLen))
&& (cmdLen == strlen(cmd->cmdAlias)))))
&& (cmdLen == strlen(cmd->cmdAlias))
&& (0 == strncmp(inst->buffer, cmd->cmdAlias, cmdLen))))
{
utils_writeEcho(inst, "\r\n", 2u);
shellmatta_opt_init(inst, cmdLen + 1u);
@@ -639,7 +638,7 @@ shellmatta_retCode_t shellmatta_processData(shellmatta_handle_t handle,
}
}
if ((cmdExecuted == 0u) && (inst->inputCount > 0))
if ((0u == cmdExecuted) && (inst->inputCount > 0))
{
inst->write("\r\nCommand: ", 11u);
inst->write(inst->buffer, inst->inputCount);
@@ -819,4 +818,3 @@ shellmatta_retCode_t shellmatta_printf( shellmatta_handle_t handle,
#endif
/** @} */

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2019 Stefan Strobel <stefan.strobel@shimatta.net>
* Copyright (c) 2019 - 2021 Stefan Strobel <stefan.strobel@shimatta.net>
*
* 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
@@ -34,16 +34,16 @@ void autocomplete_run(shellmatta_instance_t *inst)
shellmatta_cmd_t *cmd = inst->cmdList;
char *tempCmd = NULL;
uint32_t minLen = 0u;
uint32_t sizeDiff = 0u;
bool exactMatch = true;
uint32_t printedLen = 0u;
uint32_t tempCursor = 0u;
uint32_t sizeDiff;
uint32_t tempCursor;
/** -# increase the tab counter to print all matching commands next time */
inst->tabCounter++;
/** -# on douple tab show all matching commands */
if (1u < inst->tabCounter)
if (inst->tabCounter > 1u)
{
inst->tabCounter = 0u;
@@ -52,7 +52,7 @@ void autocomplete_run(shellmatta_instance_t *inst)
{
/** -# check if command matches the input */
if( (strlen(cmd->cmd) >= inst->cursor)
&& (0u == memcmp(cmd->cmd, inst->buffer, inst->cursor)))
&& (0 == strncmp(cmd->cmd, inst->buffer, inst->cursor)))
{
/** -# add newline on first find */
if(0u == printedLen)
@@ -67,7 +67,7 @@ void autocomplete_run(shellmatta_instance_t *inst)
/** -# check if command alias matches the input */
if( (NULL != cmd->cmdAlias)
&& (strlen(cmd->cmdAlias) >= inst->cursor)
&& (0u == memcmp(cmd->cmdAlias, inst->buffer, inst->cursor)))
&& (0 == strncmp(cmd->cmdAlias, inst->buffer, inst->cursor)))
{
/** -# add newline on first find */
if(0u == printedLen)
@@ -100,7 +100,7 @@ void autocomplete_run(shellmatta_instance_t *inst)
{
/** -# check if command matches the input */
if( (strlen(cmd->cmd) >= inst->cursor)
&& (0u == memcmp(cmd->cmd, inst->buffer, inst->cursor)))
&& (0 == strncmp(cmd->cmd, inst->buffer, inst->cursor)))
{
/** -# store first match */
if(NULL == tempCmd)
@@ -126,7 +126,7 @@ void autocomplete_run(shellmatta_instance_t *inst)
/** -# check if command Alias matches the input */
if( (NULL != cmd->cmdAlias)
&& (strlen(cmd->cmdAlias) >= inst->cursor)
&& (0u == memcmp(cmd->cmdAlias, inst->buffer, inst->cursor)))
&& (0 == strncmp(cmd->cmdAlias, inst->buffer, inst->cursor)))
{
/** -# store first match */
if(NULL == tempCmd)

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2019 Stefan Strobel <stefan.strobel@shimatta.net>
* Copyright (c) 2019 - 2021 Stefan Strobel <stefan.strobel@shimatta.net>
*
* 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
@@ -13,7 +13,7 @@
*/
/**
* @addtogroup shellmatta_api
* @addtogroup shellmatta_autocomplete
* @{
*/
#ifndef _SHELLMATTA_AUTOCOMPLETE_H_

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2019 Stefan Strobel <stefan.strobel@shimatta.net>
* Copyright (c) 2019 - 2021 Stefan Strobel <stefan.strobel@shimatta.net>
*
* 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
@@ -52,7 +52,7 @@ shellmatta_retCode_t escape_processArrowKeys(shellmatta_instance_t *inst)
break;
case 'B': /* arrow down */
/*! -# ignore the key if the history buffer points to the last entry */
if((inst->historyRead != inst->historyEnd))
{
history_storeCmd(inst);

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2019 Stefan Strobel <stefan.strobel@shimatta.net>
* Copyright (c) 2019 - 2021 Stefan Strobel <stefan.strobel@shimatta.net>
*
* 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

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2019 Stefan Strobel <stefan.strobel@shimatta.net>
* Copyright (c) 2019 - 2021 Stefan Strobel <stefan.strobel@shimatta.net>
*
* 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
@@ -38,7 +38,7 @@ static void appendHistoryByte(shellmatta_instance_t *inst, char byte)
/** -# append the byte */
inst->historyBuffer[inst->historyEnd] = byte;
/** -# check if the we overwrite an existing stored command */
/** -# check if we overwrite an existing stored command */
if(inst->historyEnd == inst->historyStart)
{
/** -# move the start pointer to the next termination (0) */
@@ -84,6 +84,55 @@ static bool getHistoryByte(shellmatta_instance_t *inst, char *byte)
return ret;
}
/**
* @brief compares the current buffer to the last command in the history buffer
* @param[in] inst pointer to a shellmatta instance
* @return true: current command is identical to the last one in the history buffer
*/
static bool compareLastCommand(shellmatta_instance_t *inst)
{
bool ret = false;
uint32_t i;
uint32_t cnt;
/** -# check if there is anything in the buffer */
if(inst->historyStart != inst->historyEnd)
{
i = inst->historyEnd;
cnt = 0u;
ret = true;
while((true == ret) && (cnt < inst->inputCount))
{
/** -# terminate compare on first mismatch */
if((inst->historyBuffer[i] != inst->buffer[cnt]) || (0u == inst->historyBuffer[i]))
{
ret = false;
}
if(0u == i)
{
i = (inst->historyBufferSize - 1u);
}
else
{
i --;
}
cnt ++;
}
/*! -# check if we are at the end of the command in the buffer - there has to be a terminating 0 */
if(0u != inst->historyBuffer[i])
{
ret = false;
}
}
return ret;
}
/**
* @brief navigates in the history buffer by the given number of commands
* @param[in, out] inst pointer to a shellmatta instance
@@ -92,8 +141,8 @@ static bool getHistoryByte(shellmatta_instance_t *inst, char *byte)
*/
bool history_navigate(shellmatta_instance_t *inst, int32_t cnt)
{
bool ret = true;
uint32_t tempReadIdx = 0u;
bool ret = true;
uint32_t tempReadIdx;
while((cnt > 0) && (true == ret))
{
@@ -106,7 +155,7 @@ bool history_navigate(shellmatta_instance_t *inst, int32_t cnt)
inst->historyRead ++;
if(inst->historyRead >= inst->historyBufferSize)
{
inst->historyRead = 0u;
inst->historyRead -= inst->historyBufferSize;
}
if( (inst->historyRead != inst->historyEnd)
@@ -165,7 +214,7 @@ bool history_navigate(shellmatta_instance_t *inst, int32_t cnt)
/**
* @brief stores the current command from the instances buffer into the
* history buffer
* @param[in] inst pointer to a shellmatta instance
* @param[in] inst pointer to a shellmatta instance
*/
void history_storeCmd(shellmatta_instance_t *inst)
{
@@ -174,8 +223,9 @@ void history_storeCmd(shellmatta_instance_t *inst)
/** -# check if we have enough room for the command in the history buffer
* and there is a new command to be stored */
if( (inst->historyBufferSize > inst->inputCount)
&& (0u != inst->inputCount)
&& (true == inst->dirty))
&& (0u != inst->inputCount)
&& (true == inst->dirty)
&& (true != compareLastCommand(inst)))
{
/** -# append the command termination */
appendHistoryByte(inst, 0u);
@@ -187,7 +237,6 @@ void history_storeCmd(shellmatta_instance_t *inst)
}
}
/** -# remove the dirty flag - everything is nice and saved */
inst->dirty = false;
}
@@ -210,7 +259,7 @@ void history_restoreCmd(shellmatta_instance_t *inst)
utils_clearInput(inst);
anythingToRestore = true;
}
while((ret == true) && (byte != 0u))
while((true == ret) && (0u != byte))
{
inst->buffer[inst->inputCount] = byte;
inst->inputCount ++;

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2019 Stefan Strobel <stefan.strobel@shimatta.net>
* Copyright (c) 2019 - 2021 Stefan Strobel <stefan.strobel@shimatta.net>
*
* 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
@@ -13,7 +13,7 @@
*/
/**
* @addtogroup shellmatta_api
* @addtogroup shellmatta_history
* @{
*/
#ifndef _SHELLMATTA_HISTORY_H_

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2019 Stefan Strobel <stefan.strobel@shimatta.net>
* Copyright (c) 2019 - 2021 Stefan Strobel <stefan.strobel@shimatta.net>
*
* 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
@@ -51,7 +51,7 @@ static shellmatta_retCode_t findNextHunk(shellmatta_instance_t *inst)
&& (((' ' != inst->buffer[newOffset]) && ('\0' != inst->buffer[newOffset])) || '\0' != quotation))
{
/** -# check for new quotation */
if((('\'' == inst->buffer[newOffset]) || ('"' == inst->buffer[newOffset])) && (quotation == '\0'))
if((('\'' == inst->buffer[newOffset]) || ('"' == inst->buffer[newOffset])) && ('\0' == quotation))
{
quotation = inst->buffer[newOffset];
exeptionOffset ++;
@@ -211,7 +211,7 @@ static shellmatta_retCode_t parseLongOpt( shellmatta_instance_t *inst,
}
}
/** -# check for correct syntax for long options */
else if((3u <= inst->optionParser.len) && ('-' == buffer[0u]) && ('-' == buffer[1u]))
else if((inst->optionParser.len >= 3u) && ('-' == buffer[0u]) && ('-' == buffer[1u]))
{
/** -# search for long option in option list */
for(i = 0u; ('\0' != longOptions[i].paramShort) && ('\0' == *option); i ++)

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2019 Stefan Strobel <stefan.strobel@shimatta.net>
* Copyright (c) 2019 - 2021 Stefan Strobel <stefan.strobel@shimatta.net>
*
* 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
@@ -22,18 +22,6 @@
#include "shellmatta.h"
#include <stdint.h>
shellmatta_retCode_t shellmatta_opt( shellmatta_handle_t handle,
const char *optionString,
char *option,
char **argument,
uint32_t *argLen);
shellmatta_retCode_t shellmatta_opt_long( shellmatta_handle_t handle,
const shellmatta_opt_long_t *longOptions,
char *option,
char **argument,
uint32_t *argLen);
void shellmatta_opt_init( shellmatta_instance_t *inst,
uint32_t argStart);

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2019 Stefan Strobel <stefan.strobel@shimatta.net>
* Copyright (c) 2019 - 2021 Stefan Strobel <stefan.strobel@shimatta.net>
*
* 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
@@ -51,25 +51,25 @@ uint32_t utils_shellItoa(int32_t value, char *buffer, uint32_t base)
char tempBuffer[34u];
uint32_t i;
uint32_t bufferIdx = 0u;
char digitValue;
int8_t digitValue;
/** -# check the base for plausibility */
if((2 <= base) && (16 >= base))
if((base >= 2) && (base <= 16))
{
/** -# check for sign */
if(0 > value)
if(value < 0)
{
value = value * (-1);
buffer[0u] = '-';
bufferIdx += 1u;
value = value * (-1);
buffer[0u] = '-';
bufferIdx += 1u;
}
/** -# loop through all digits in reverse order */
i = 0u;
do
{
digitValue = (char) (value % base);
tempBuffer[i] = (digitValue < 10) ? ('0' + digitValue) : (('A' - 10) + digitValue);
digitValue = (int8_t) (value % base);
tempBuffer[i] = (digitValue < 10) ? ('0' + digitValue) : ('A' + (digitValue - 10));
value /= base;
i ++;
}while(value > 0);
@@ -91,7 +91,7 @@ uint32_t utils_shellItoa(int32_t value, char *buffer, uint32_t base)
*/
void utils_saveCursorPos(shellmatta_instance_t *inst)
{
utils_writeEcho(inst, "\x1b[s", 3u);
utils_writeEcho(inst, "\x1b" "[s", 3u);
}
/**
@@ -100,7 +100,7 @@ void utils_saveCursorPos(shellmatta_instance_t *inst)
*/
void utils_restoreCursorPos(shellmatta_instance_t *inst)
{
utils_writeEcho(inst, "\x1b[u", 3u);
utils_writeEcho(inst, "\x1b" "[u", 3u);
}
/**
@@ -110,7 +110,7 @@ void utils_restoreCursorPos(shellmatta_instance_t *inst)
*/
void utils_eraseLine(shellmatta_instance_t *inst)
{
utils_writeEcho(inst, "\x1b[K", 3u);
utils_writeEcho(inst, "\x1b" "[K", 3u);
}
/**
@@ -162,46 +162,51 @@ void utils_forwardCursor(shellmatta_instance_t *inst, uint32_t length)
* @param[in] inst pointer to shellmatta instance
* @param[in] data pointer to the data to be inserted
* @param[in] length length of the data to be inserted
* @todo this function shall check buffer overflows
*/
void utils_insertChars( shellmatta_instance_t *inst,
char *data,
uint32_t length)
{
if(0u != length)
uint32_t tmpLength = length;
/** -# limit the length to the space left in the buffer */
if((inst->inputCount + tmpLength) > inst->bufferSize)
{
tmpLength = inst->bufferSize - inst->inputCount;
}
if(0u != tmpLength)
{
/** -# check if we have to move chars in the buffer */
if( (inst->cursor != inst->inputCount)
&& (SHELLMATTA_MODE_INSERT == inst->mode))
{
/** -# move the existing chars */
for ( uint32_t i = inst->inputCount;
i > inst->cursor;
i --)
for (uint32_t i = inst->inputCount; i > inst->cursor; i --)
{
inst->buffer[i + length - 1] = inst->buffer[i - 1];
inst->buffer[i + tmpLength - 1] = inst->buffer[i - 1];
}
/** -# store and print the new chars */
memcpy(&(inst->buffer[inst->cursor]), data, length);
utils_writeEcho(inst, data, length);
memcpy(&(inst->buffer[inst->cursor]), data, tmpLength);
utils_writeEcho(inst, data, tmpLength);
/** -# print the other chars and restore the cursor to this position */
utils_eraseLine(inst);
utils_saveCursorPos(inst);
utils_writeEcho( inst,
&(inst->buffer[inst->cursor + length]),
&(inst->buffer[inst->cursor + tmpLength]),
inst->inputCount - inst->cursor);
utils_restoreCursorPos(inst);
inst->cursor += length;
inst->inputCount += length;
inst->cursor += tmpLength;
inst->inputCount += tmpLength;
}
/** -# overwrite - if the cursor reaches the end of the input it is pushed further */
else
{
memcpy(&(inst->buffer[inst->cursor]), data, length);
utils_writeEcho(inst, data, length);
inst->cursor += length;
memcpy(&(inst->buffer[inst->cursor]), data, tmpLength);
utils_writeEcho(inst, data, tmpLength);
inst->cursor += tmpLength;
if(inst->cursor > inst->inputCount)
{
inst->inputCount = inst->cursor;
@@ -215,13 +220,14 @@ void utils_insertChars( shellmatta_instance_t *inst,
* position
* @param[in] inst pointer to a shellmatta instance
* @param[in] length number of characters to remove
* @param[in] backspace remove characters left of the cursor
* @param[in] backspace true ==> remove characters left of the cursor
* false ==> remove characters right of the cursor
*/
void utils_removeChars( shellmatta_instance_t *inst,
uint32_t length,
bool backspace)
{
if(0u != length)
if((0u != length) && (inst->inputCount >= inst->cursor))
{
/** -# rewind the cursor in case of backspace */
if(true == backspace)
@@ -266,84 +272,162 @@ void utils_clearInput(shellmatta_instance_t *inst)
inst->dirty = false;
}
/**
* @brief prints the usage information of one passed command
* @param[in] inst handle shellmatta instance handle
* @param[in] cmd pointer to a command structure to print
* @return #SHELLMATTA_OK
* #SHELLMATTA_ERROR
*/
static shellmatta_retCode_t printUsage(const shellmatta_instance_t *inst, const shellmatta_cmd_t *cmd)
{
shellmatta_retCode_t ret = SHELLMATTA_OK;
/** -# write the command and alias if configured */
SHELLMATTA_RET(ret, inst->write("Help for command: ", 18u));
SHELLMATTA_RET(ret, inst->write(cmd->cmd, strlen(cmd->cmd)));
if((NULL != cmd->cmdAlias) && (0u != strlen(cmd->cmdAlias)))
{
SHELLMATTA_RET(ret, inst->write(" (", 2u));
SHELLMATTA_RET(ret, inst->write(cmd->cmdAlias, strlen(cmd->cmdAlias)));
SHELLMATTA_RET(ret, inst->write(")", 1u));
}
/** -# write the help text if configured */
if((NULL != cmd->helpText) && (0u != strlen(cmd->helpText)))
{
SHELLMATTA_RET(ret, inst->write("\r\n\r\n", 4u));
SHELLMATTA_RET(ret, inst->write(cmd->helpText, strlen(cmd->helpText)));
}
/** -# write the usage text if configured */
if((NULL != cmd->usageText) && (0u != strlen(cmd->usageText)))
{
SHELLMATTA_RET(ret, inst->write("\r\n\r\nUsage: \r\n", 13u));
SHELLMATTA_RET(ret, inst->write(cmd->usageText, strlen(cmd->usageText)));
}
SHELLMATTA_RET(ret, inst->write("\r\n", 2u));
return ret;
}
/**
* @brief prints all possible commands with description and usage
* @param[in] handle handle shellmatta instance handle
* @param[in] arguments not used here
* @param[in] length not used here
* @param[in] arguments arguments containing a command name or alias
* @param[in] length length of the arguments
* @return #SHELLMATTA_OK
* #SHELLMATTA_ERROR (buffer overflow)
*/
static shellmatta_retCode_t helpCmdFct(shellmatta_handle_t handle, const char *arguments, uint32_t length)
static shellmatta_retCode_t helpCmdFct(const shellmatta_handle_t handle, const char *arguments, uint32_t length)
{
shellmatta_retCode_t ret = SHELLMATTA_OK;
shellmatta_instance_t *inst = (shellmatta_instance_t*) handle;
shellmatta_cmd_t *cmd = inst->cmdList;
size_t maxCmdLen = 0u;
size_t maxCmdAliasLen = 0u;
size_t maxCmdHelpLen = 0u;
size_t cmdLen = 0u;
size_t cmdAliasLen = 0u;
size_t cmdHelpLen = 0u;
uint32_t tabCnt = 0u;
static const char tabBuffer[] = { ' ', ' ', ' ', ' ',
' ', ' ', ' ', ' ',
' ', ' ', ' ', ' ',
' ', ' ', ' ', ' '};
shellmatta_retCode_t ret = SHELLMATTA_OK;
const shellmatta_instance_t *inst = (const shellmatta_instance_t*) handle;
shellmatta_cmd_t *cmd = NULL;
size_t maxCmdLen = 0u;
size_t maxCmdAliasLen = 0u;
size_t cmdLen = 0u;
const char *subCmd = NULL;
size_t cmdAliasLen;
uint32_t tabCnt;
uint32_t i;
static const char tabBuffer[] = { ' ', ' ', ' ', ' ',
' ', ' ', ' ', ' ',
' ', ' ', ' ', ' ',
' ', ' ', ' ', ' '};
/** -# loop through all commands to determine the lengths of each cmd */
while(NULL != cmd)
/** -# check if help is called with a command - find first space */
for(i = 1u; i < length; i ++)
{
maxCmdLen = SHELLMATTA_MAX(maxCmdLen, strlen(cmd->cmd));
if(NULL != cmd->cmdAlias)
if(' ' == arguments[i - 1])
{
maxCmdAliasLen = SHELLMATTA_MAX(maxCmdAliasLen, strlen(cmd->cmdAlias));
subCmd = &(arguments[i]);
/** -# determine subcommand length*/
cmdLen = 0u;
while( ((i + cmdLen) < length)
&& (' ' != arguments[i + cmdLen])
&& ('\r' != arguments[i + cmdLen])
&& ('\n' != arguments[i + cmdLen])
&& ('\0' != arguments[i + cmdLen]))
{
cmdLen ++;
}
break;
}
if(NULL != cmd->helpText)
{
maxCmdHelpLen = SHELLMATTA_MAX(maxCmdHelpLen, strlen(cmd->helpText));
}
cmd = cmd->next;
}
/** -# loop through all commands and print all possible information */
cmd = inst->cmdList;
while(NULL != cmd)
/* print detailled help */
if(NULL != subCmd)
{
/** -# determine the length of each field to add padding */
cmdLen = strlen(cmd->cmd);
cmdAliasLen = (NULL != cmd->cmdAlias) ? strlen(cmd->cmdAlias) : 0u;
cmdHelpLen = (NULL != cmd->helpText) ? strlen(cmd->helpText) : 0u;
cmd = inst->cmdList;
inst->write(cmd->cmd, strlen(cmd->cmd));
tabCnt = (maxCmdLen - cmdLen) + 2u;
SHELLMATTA_PRINT_BUFFER(tabBuffer, tabCnt, inst->write);
if(NULL != cmd->cmdAlias)
/** -# search for a matching command */
while (NULL != cmd)
{
inst->write(cmd->cmdAlias, cmdAliasLen);
}
tabCnt = (maxCmdAliasLen - cmdAliasLen) + 2u;
SHELLMATTA_PRINT_BUFFER(tabBuffer, tabCnt, inst->write);
/** -# compare command and alias string and length */
if ( ((cmdLen == strlen(cmd->cmd))
&& (0 == strncmp(subCmd, cmd->cmd, cmdLen)))
|| ((NULL != cmd->cmdAlias)
&& (cmdLen == strlen(cmd->cmdAlias))
&& (0 == strncmp(subCmd, cmd->cmdAlias, cmdLen))))
{
SHELLMATTA_RET(ret, printUsage(inst, cmd));
break;
}
if(NULL != cmd->helpText)
{
inst->write(cmd->helpText, cmdHelpLen);
cmd = cmd->next;
}
tabCnt = (maxCmdHelpLen - cmdHelpLen) + 2u;
SHELLMATTA_PRINT_BUFFER(tabBuffer, tabCnt, inst->write);
if(NULL != cmd->usageText)
{
inst->write(cmd->usageText, strlen(cmd->usageText));
}
inst->write("\r\n", 2u);
cmd = cmd->next;
}
(void)arguments;
(void)length;
/** -# print help list if no sub cmd was found */
if(NULL == cmd)
{
/** -# loop through all commands to determine the lengths of each cmd */
cmd = inst->cmdList;
while(NULL != cmd)
{
maxCmdLen = SHELLMATTA_MAX(maxCmdLen, strlen(cmd->cmd));
if(NULL != cmd->cmdAlias)
{
maxCmdAliasLen = SHELLMATTA_MAX(maxCmdAliasLen, strlen(cmd->cmdAlias));
}
cmd = cmd->next;
}
/** -# loop through all commands and print all possible information */
cmd = inst->cmdList;
while(NULL != cmd)
{
/** -# determine the length of each field to add padding */
cmdLen = strlen(cmd->cmd);
cmdAliasLen = (NULL != cmd->cmdAlias) ? strlen(cmd->cmdAlias) : 0u;
SHELLMATTA_RET(ret, inst->write(cmd->cmd, strlen(cmd->cmd)));
tabCnt = (maxCmdLen - cmdLen) + 2u;
/** -# add padding if there is anything to be printed afterwards */
if( ((NULL != cmd->cmdAlias) && (0u != strlen(cmd->cmdAlias)))
|| ((NULL != cmd->helpText) && (0u != strlen(cmd->helpText))))
{
SHELLMATTA_PRINT_BUFFER(tabBuffer, tabCnt, inst->write);
}
if((NULL != cmd->cmdAlias) && (0u != strlen(cmd->cmdAlias)))
{
SHELLMATTA_RET(ret, inst->write(cmd->cmdAlias, cmdAliasLen));
}
tabCnt = (maxCmdAliasLen - cmdAliasLen) + 2u;
if((NULL != cmd->helpText) && (0u != strlen(cmd->helpText)))
{
SHELLMATTA_PRINT_BUFFER(tabBuffer, tabCnt, inst->write);
SHELLMATTA_RET(ret, inst->write(cmd->helpText, strlen(cmd->helpText)));
}
SHELLMATTA_RET(ret, inst->write("\r\n", 2u));
cmd = cmd->next;
}
}
return ret;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2019 Stefan Strobel <stefan.strobel@shimatta.net>
* Copyright (c) 2019 - 2021 Stefan Strobel <stefan.strobel@shimatta.net>
*
* 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
@@ -36,6 +36,13 @@
*/
#define SHELLMATTA_MAX(a,b) (((a) < (b)) ? (b) : (a))
/**
* @brief sums up #shellmatta_retCode_t
* @param[in] ret return variable
* @param[in] expression expression with the return value
*/
#define SHELLMATTA_RET(ret, expression) (ret) = (SHELLMATTA_OK != (expression)) ? SHELLMATTA_ERROR : (ret)
/**
* @brief calls fct with cnt bytes from buffer (to print cnt same bytes)
* @param[in] buffer buffer to send (shall contain the same char)
@@ -76,16 +83,21 @@ extern const shellmatta_cmd_t helpCmd;
* e.g. use _-DSHELLMATTA_HELP_ALIAS=\"?\"_ as compile option to change the alias to ?
*/
#ifndef SHELLMATTA_HELP_COMMAND
#define SHELLMATTA_HELP_COMMAND (char*)"help" /**< help command */
/** \brief help command */
#define SHELLMATTA_HELP_COMMAND (char*)"help"
#endif
#ifndef SHELLMATTA_HELP_ALIAS
#define SHELLMATTA_HELP_ALIAS (char*)"h" /**< help command alias */
/** \brief help command alias */
#define SHELLMATTA_HELP_ALIAS (char*)"h"
#endif
#ifndef SHELLMATTA_HELP_HELP_TEXT
#define SHELLMATTA_HELP_HELP_TEXT (char*)"Print this help text" /**< help command help text */
/** \brief help command help text */
#define SHELLMATTA_HELP_HELP_TEXT (char*)"help [command] - print help or usage information"
#endif
#ifndef SHELLMATTA_HELP_USAGE_TEXT
#define SHELLMATTA_HELP_USAGE_TEXT (char*)"help" /**< help command usage text */
/** \brief help command usage text */
#define SHELLMATTA_HELP_USAGE_TEXT (char*) "help [command]\r\n" \
"\tcommand: optional command name or alias to print detailled help for"
#endif
/**
* @}

6643
test/framework/fff.h Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,3 +1,17 @@
/*
* Copyright (c) 2021 Stefan Strobel <stefan.strobel@shimatta.net>
*
* 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.cpp
* @brief integration test implementation for some general functions
* @author Stefan Strobel <stefan.strobel@shimatta.net>
*/
#include "test/framework/catch.hpp"
extern "C" {
#include "shellmatta.h"
@@ -74,71 +88,6 @@ TEST_CASE( "shellmatta empty function" ) {
}
TEST_CASE( "shellmatta help function" ) {
shellmatta_instance_t inst;
shellmatta_handle_t handle;
char buffer[1024];
char historyBuffer[1024];
char *dummyData = (char*)"?\r\n"
"doSomething do Function does something use me, please\r\n"
"empty \r\n"
"help ? Print this help text help\r\n"
"\r\nshellmatta->";
shellmatta_doInit( &inst,
&handle,
buffer,
sizeof(buffer),
historyBuffer,
sizeof(historyBuffer),
"shellmatta->",
NULL,
writeFct);
shellmatta_addCmd(handle, &emptyCmd);
shellmatta_addCmd(handle, &doSomethingCmd);
write_callCnt = 0u;
memset(write_data, 0, sizeof(write_data));
write_length = 0u;
shellmatta_processData(handle, (char*)"?\r", 2);
CHECK( write_length == strlen(dummyData));
CHECK( strcmp(dummyData, write_data) == 0);
write_callCnt = 0u;
memset(write_data, 0, sizeof(write_data));
write_length = 0u;
dummyData = (char*)"? 564 321 56 465 46\r\n"
"doSomething do Function does something use me, please\r\n"
"empty \r\n"
"help ? Print this help text help\r\n"
"\r\nshellmatta->";
shellmatta_processData(handle, (char*)"? 564 321 56 465 46\r", 20);
CHECK( write_length == strlen(dummyData));
CHECK( strcmp(dummyData, write_data) == 0);
write_callCnt = 0u;
memset(write_data, 0, sizeof(write_data));
write_length = 0u;
dummyData = (char*)"?r\r\n"
"Command: ?r not found"
"\r\nshellmatta->";
shellmatta_processData(handle, (char*)"?r\r", 3);
CHECK( write_length == 39u);
REQUIRE( strcmp(dummyData, write_data) == 0);
}
TEST_CASE( "shellmatta heredoc test" ) {
shellmatta_instance_t inst;
@@ -214,8 +163,8 @@ TEST_CASE( "shellmatta remove function" ) {
char buffer[1024];
char historyBuffer[1024];
char *dummyData = (char*)"?\r\n"
"doSomething do Function does something use me, please\r\n"
"help ? Print this help text help\r\n"
"doSomething do Function does something\r\n"
"help ? help [command] - print help or usage information\r\n"
"\r\nshellmatta->";
shellmatta_doInit( &inst,
@@ -235,7 +184,7 @@ TEST_CASE( "shellmatta remove function" ) {
shellmatta_processData(handle, (char*)"?\r", 2);
CHECK( write_length == 123u);
CHECK( write_length == strlen(dummyData));
CHECK( strcmp(dummyData, write_data) == 0);
@@ -244,13 +193,13 @@ TEST_CASE( "shellmatta remove function" ) {
write_length = 0u;
dummyData = (char*)"? 564 321 56 465 46\r\n"
"doSomething do Function does something use me, please\r\n"
"help ? Print this help text help\r\n"
"doSomething do Function does something\r\n"
"help ? help [command] - print help or usage information\r\n"
"\r\nshellmatta->";
shellmatta_processData(handle, (char*)"? 564 321 56 465 46\r", 20);
CHECK( write_length == 141u);
CHECK( write_length == strlen(dummyData));
CHECK( strcmp(dummyData, write_data) == 0);
write_callCnt = 0u;
@@ -261,10 +210,10 @@ TEST_CASE( "shellmatta remove function" ) {
shellmatta_processData(handle, (char*)"? 564 321 56 465 46\r", 20);
dummyData = (char*)"? 564 321 56 465 46\r\n"
"help ? Print this help text help\r\n"
"help ? help [command] - print help or usage information\r\n"
"\r\nshellmatta->";
CHECK( write_length == 72u);
CHECK( write_length == strlen(dummyData));
REQUIRE( strcmp(dummyData, write_data) == 0);
}
@@ -275,8 +224,8 @@ TEST_CASE( "shellmatta reset no prompt" ) {
char buffer[1024];
char historyBuffer[1024];
char *dummyData = (char*)"dkfg hdlsfkgh ldksfjhg lkdjfsh glkd?\r\n"
"doSomething do Function does something use me, please\r\n"
"help ? Print this help text help\r\n"
"doSomething do Function does something\r\n"
"help ? help [command] - print help or usage information\r\n"
"\r\nshellmatta->";
shellmatta_doInit( &inst,
@@ -310,8 +259,8 @@ TEST_CASE( "shellmatta reset with prompt" ) {
char historyBuffer[1024];
char *dummyData = (char*)"dkfg hdlsfkgh ldksfjhg lkdjfsh glkd"
"\r\nshellmatta->?\r\n"
"doSomething do Function does something use me, please\r\n"
"help ? Print this help text help\r\n"
"doSomething do Function does something\r\n"
"help ? help [command] - print help or usage information\r\n"
"\r\nshellmatta->";
shellmatta_doInit( &inst,
@@ -344,8 +293,8 @@ TEST_CASE( "shellmatta reset no prompt history buffer" ) {
char buffer[1024];
char historyBuffer[1024];
char *dummyData = (char*)"?\r\n"
"doSomething do Function does something use me, please\r\n"
"help ? Print this help text help\r\n"
"doSomething do Function does something\r\n"
"help ? help [command] - print help or usage information\r\n"
"\r\nshellmatta->";
shellmatta_doInit( &inst,
@@ -384,8 +333,8 @@ TEST_CASE( "shellmatta reset no prompt heredoc" ) {
char buffer[1024];
char historyBuffer[1024];
char *dummyData = (char*)"?\r\n"
"doSomething do Function does something use me, please\r\n"
"help ? Print this help text help\r\n"
"doSomething do Function does something\r\n"
"help ? help [command] - print help or usage information\r\n"
"\r\nshellmatta->";
shellmatta_doInit( &inst,
@@ -425,12 +374,12 @@ TEST_CASE( "shellmatta configure disable echo" ) {
char buffer[1024];
char historyBuffer[1024];
char *dummyData = (char*)"help this is some dummy Text\r\n"
"doSomething do Function does something use me, please\r\n"
"help ? Print this help text help\r\n"
"doSomething do Function does something\r\n"
"help ? help [command] - print help or usage information\r\n"
"\r\nshellmatta->";
char *dummyData2 = (char*)"doSomething do Function does something use me, please\r\n"
"help ? Print this help text help\r\n"
char *dummyData2 = (char*)"doSomething do Function does something\r\n"
"help ? help [command] - print help or usage information\r\n"
"\r\nshellmatta->";
shellmatta_doInit( &inst,
@@ -562,4 +511,3 @@ TEST_CASE( "shellmatta configure delimiter" ) {
CHECK( write_length == strlen(dummyData));
REQUIRE( strcmp(dummyData, write_data) == 0);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2019 Stefan Strobel <stefan.strobel@shimatta.net>
* Copyright (c) 2019 - 2021 Stefan Strobel <stefan.strobel@shimatta.net>
*
* 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

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2019 Stefan Strobel <stefan.strobel@shimatta.net>
* Copyright (c) 2019 - 2021 Stefan Strobel <stefan.strobel@shimatta.net>
*
* 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

View File

@@ -0,0 +1,296 @@
/*
* Copyright (c) 2021 Stefan Strobel <stefan.strobel@shimatta.net>
*
* 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_help.cpp
* @brief integration test implementation for the help command of the shellmatta
* @author Stefan Strobel <stefan.strobel@shimatta.net>
*/
#include "test/framework/catch.hpp"
extern "C" {
#include "test/framework/fff.h"
#include "shellmatta.h"
}
#include <string.h>
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)
static char fakeWriteData[1024];
static uint32_t fakeWriteLength;
static shellmatta_retCode_t writeFct_customFake(const char* data, uint32_t length)
{
while((length > 0) && (fakeWriteLength < sizeof(fakeWriteData)))
{
fakeWriteData[fakeWriteLength] = *data;
data ++;
length --;
fakeWriteLength ++;
}
return SHELLMATTA_OK;
}
/* List of fakes */
#define FFF_FAKES_LIST(FAKE) \
FAKE(writeFct) \
FAKE(cmdFct1) \
FAKE(cmdFct2) \
FAKE(cmdFct3)
#define PROCESS_INPUT(input) \
CHECK(SHELLMATTA_OK == shellmatta_processData(handle, (char*)(input), sizeof((input)) - 1u));
static shellmatta_cmd_t cmd1 = {(char*)"cmd1", (char*)"1", (char*)"cmd1 [options]", (char*)"cmd1 usage\r\n--option, -o: option", cmdFct1, NULL};
static shellmatta_cmd_t cmd2 = {(char*)"cmd2", NULL, NULL, NULL, cmdFct2, NULL};
static shellmatta_cmd_t cmd3 = {(char*)"cmd3", (char*)"", (char*)"", (char*)"", cmdFct3, NULL};
SCENARIO("Test the help function")
{
GIVEN("An initialized and empty Shellmatte instance")
{
shellmatta_instance_t inst;
shellmatta_handle_t handle;
char buffer[1024u];
char historyBuffer[1024u];
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));
WHEN("The user hits help")
{
FFF_FAKES_LIST(RESET_FAKE)
fakeWriteLength = 0u;
memset(fakeWriteData, 0, sizeof(fakeWriteData));
shellmatta_write_t writeCustomFakeSeq[1] = {writeFct_customFake};
SET_CUSTOM_FAKE_SEQ(writeFct, writeCustomFakeSeq, 1)
PROCESS_INPUT("help\r\n")
THEN("The shellmatta prints the help text")
{
static const char * response = (char*) "help\r\n"
"cmd1 1 cmd1 [options]\r\n"
"cmd2\r\n"
"cmd3\r\n"
"help ? help [command] - print help or usage information\r\n\r\n"
"shellmatta->";
CHECK(writeFct_fake.call_count == 23);
CHECK(strlen(response) == fakeWriteLength);
CHECK(0 == strcmp(response, fakeWriteData));
}
}
WHEN("The user hits ?")
{
FFF_FAKES_LIST(RESET_FAKE)
fakeWriteLength = 0u;
memset(fakeWriteData, 0, sizeof(fakeWriteData));
shellmatta_write_t writeCustomFakeSeq[1] = {writeFct_customFake};
SET_CUSTOM_FAKE_SEQ(writeFct, writeCustomFakeSeq, 1)
PROCESS_INPUT("?\r\n")
THEN("The shellmatta prints the help text")
{
static const char * response = (char*) "?\r\n"
"cmd1 1 cmd1 [options]\r\n"
"cmd2\r\n"
"cmd3\r\n"
"help ? help [command] - print help or usage information\r\n\r\n"
"shellmatta->";
CHECK(writeFct_fake.call_count == 20);
CHECK(strlen(response) == fakeWriteLength);
CHECK(0 == strcmp(response, fakeWriteData));
}
}
}
}
SCENARIO("Test if the help command prints the usage correctly")
{
GIVEN("An initialized and empty Shellmatte instance")
{
shellmatta_instance_t inst;
shellmatta_handle_t handle;
char buffer[1024u];
char historyBuffer[1024u];
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));
WHEN("The user requests usage information from a valid command")
{
FFF_FAKES_LIST(RESET_FAKE)
fakeWriteLength = 0u;
memset(fakeWriteData, 0, sizeof(fakeWriteData));
shellmatta_write_t writeCustomFakeSeq[1] = {writeFct_customFake};
SET_CUSTOM_FAKE_SEQ(writeFct, writeCustomFakeSeq, 1)
PROCESS_INPUT("help cmd1\r\n")
THEN("The shellmatta prints the help text")
{
static const char * response = (char*) "help cmd1\r\n"
"Help for command: cmd1 (1)\r\n\r\n"
"cmd1 [options]\r\n\r\n"
"Usage: \r\n"
"cmd1 usage\r\n--option, -o: option\r\n"
"\r\nshellmatta->";
CHECK(writeFct_fake.call_count == 22);
CHECK(strlen(response) == fakeWriteLength);
CHECK(0 == strcmp(response, fakeWriteData));
}
}
WHEN("The user requests usage information from a valid command using its alias")
{
FFF_FAKES_LIST(RESET_FAKE)
fakeWriteLength = 0u;
memset(fakeWriteData, 0, sizeof(fakeWriteData));
shellmatta_write_t writeCustomFakeSeq[1] = {writeFct_customFake};
SET_CUSTOM_FAKE_SEQ(writeFct, writeCustomFakeSeq, 1)
PROCESS_INPUT("help 1\r\n")
THEN("The shellmatta prints the help text")
{
static const char * response = (char*) "help 1\r\n"
"Help for command: cmd1 (1)\r\n\r\n"
"cmd1 [options]\r\n\r\n"
"Usage: \r\n"
"cmd1 usage\r\n--option, -o: option\r\n"
"\r\nshellmatta->";
CHECK(writeFct_fake.call_count == 19);
CHECK(strlen(response) == fakeWriteLength);
CHECK(0 == strcmp(response, fakeWriteData));
}
}
WHEN("The user requests usage information from an empty command")
{
FFF_FAKES_LIST(RESET_FAKE)
fakeWriteLength = 0u;
memset(fakeWriteData, 0, sizeof(fakeWriteData));
shellmatta_write_t writeCustomFakeSeq[1] = {writeFct_customFake};
SET_CUSTOM_FAKE_SEQ(writeFct, writeCustomFakeSeq, 1)
PROCESS_INPUT("help cmd2\r\n")
THEN("The shellmatta prints the help text - without alias, help and usage text")
{
static const char * response = (char*) "help cmd2\r\n"
"Help for command: cmd2\r\n"
"\r\nshellmatta->";
CHECK(writeFct_fake.call_count == 15);
CHECK(strlen(response) == fakeWriteLength);
CHECK(0 == strcmp(response, fakeWriteData));
}
}
WHEN("The user requests usage information from an empty stringed command")
{
FFF_FAKES_LIST(RESET_FAKE)
fakeWriteLength = 0u;
memset(fakeWriteData, 0, sizeof(fakeWriteData));
shellmatta_write_t writeCustomFakeSeq[1] = {writeFct_customFake};
SET_CUSTOM_FAKE_SEQ(writeFct, writeCustomFakeSeq, 1)
PROCESS_INPUT("help cmd3\r\n")
THEN("The shellmatta prints the help text - without alias, help and usage text")
{
static const char * response = (char*) "help cmd3\r\n"
"Help for command: cmd3\r\n"
"\r\nshellmatta->";
CHECK(writeFct_fake.call_count == 15);
CHECK(strlen(response) == fakeWriteLength);
CHECK(0 == strcmp(response, fakeWriteData));
}
}
WHEN("The user adds additional arguments to the help command")
{
FFF_FAKES_LIST(RESET_FAKE)
fakeWriteLength = 0u;
memset(fakeWriteData, 0, sizeof(fakeWriteData));
shellmatta_write_t writeCustomFakeSeq[1] = {writeFct_customFake};
SET_CUSTOM_FAKE_SEQ(writeFct, writeCustomFakeSeq, 1)
PROCESS_INPUT("help cmd2 foo bar meow this is nonsense\r\n")
THEN("The shellmatta ignores the superflous arguments")
{
static const char * response = (char*) "help cmd2 foo bar meow this is nonsense\r\n"
"Help for command: cmd2\r\n"
"\r\nshellmatta->";
CHECK(writeFct_fake.call_count == 45);
CHECK(strlen(response) == fakeWriteLength);
CHECK(0 == strcmp(response, fakeWriteData));
}
}
WHEN("The user requests help of a nonexisting command")
{
FFF_FAKES_LIST(RESET_FAKE)
fakeWriteLength = 0u;
memset(fakeWriteData, 0, sizeof(fakeWriteData));
shellmatta_write_t writeCustomFakeSeq[1] = {writeFct_customFake};
SET_CUSTOM_FAKE_SEQ(writeFct, writeCustomFakeSeq, 1)
PROCESS_INPUT("help cmd4\r\n")
THEN("The shellmatta prints the help list")
{
static const char * response = (char*) "help cmd4\r\n"
"cmd1 1 cmd1 [options]\r\n"
"cmd2\r\n"
"cmd3\r\n"
"help ? help [command] - print help or usage information\r\n\r\n"
"shellmatta->";
CHECK(writeFct_fake.call_count == 28);
CHECK(strlen(response) == fakeWriteLength);
CHECK(0 == strcmp(response, fakeWriteData));
}
}
}
}

View File

@@ -0,0 +1,501 @@
/*
* Copyright (c) 2021 Stefan Strobel <stefan.strobel@shimatta.net>
*
* 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 <stefan.strobel@shimatta.net>
*/
#include "test/framework/catch.hpp"
extern "C" {
#include "test/framework/fff.h"
#include "shellmatta.h"
}
#include <string.h>
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 Shellmatte 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 Shellmatte 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 Shellmatte 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]);
}
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2019 Stefan Strobel <stefan.strobel@shimatta.net>
* Copyright (c) 2019 - 2021 Stefan Strobel <stefan.strobel@shimatta.net>
*
* 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

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2019 Stefan Strobel <stefan.strobel@shimatta.net>
* Copyright (c) 2019 - 2021 Stefan Strobel <stefan.strobel@shimatta.net>
*
* 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

View File

@@ -4,4 +4,8 @@
#define CATCH_CONFIG_MAIN
#include "test/framework/catch.hpp"
extern "C" {
#include "test/framework/fff.h"
DEFINE_FFF_GLOBALS
}

View File

@@ -0,0 +1,72 @@
#include "test/framework/catch.hpp"
#include "src/shellmatta_opt.c"
#include <string.h>
TEST_CASE( "shellmatta_opt peekNextHunk next char without space" ) {
char ret = 0;
shellmatta_instance_t inst;
char *dummyData = (char*) "Welcome... to Jurassic Park.";
char buffer[1024u];
memcpy(buffer, dummyData, strlen(dummyData));
inst.buffer = buffer;
inst.bufferSize = sizeof(buffer);
inst.inputCount = 28u;
inst.optionParser.nextOffset = 11u;
ret = peekNextHunk(&inst);
CHECK( ret == 't' );
}
TEST_CASE( "shellmatta_opt peekNextHunk next char with space" ) {
char ret = 0;
shellmatta_instance_t inst;
char *dummyData = (char*) "Welcome... to Jurassic Park.\0";
char buffer[1024u];
memcpy(buffer, dummyData, strlen(dummyData));
inst.buffer = buffer;
inst.bufferSize = sizeof(buffer);
inst.inputCount = 28u;
inst.optionParser.nextOffset = 13u;
ret = peekNextHunk(&inst);
CHECK( ret == 'J' );
}
TEST_CASE( "shellmatta_opt peekNextHunk next hunk escape and space" ) {
char ret = 0;
shellmatta_instance_t inst;
char *dummyData = (char*) "Welcome... to Jurassic Park.\0 Remind me to thank John for a lovely weekend.";
char buffer[1024u];
uint16_t stringsize = 77u;
memcpy(buffer, dummyData, stringsize);
inst.buffer = buffer;
inst.bufferSize = sizeof(buffer);
inst.inputCount = stringsize;
inst.optionParser.nextOffset = 28u;
ret = peekNextHunk(&inst);
CHECK( ret == 'R' );
}
TEST_CASE( "shellmatta_opt peekNextHunk next char with spaces" ) {
char ret = 0;
shellmatta_instance_t inst;
char *dummyData = (char*) "Welcome... to Jurassic Park.\0 Remind me to thank John for a lovely weekend.";
char buffer[1024u];
uint16_t stringsize = 77u;
memcpy(buffer, dummyData, stringsize);
inst.buffer = buffer;
inst.bufferSize = sizeof(buffer);
inst.inputCount = stringsize;
inst.optionParser.nextOffset = 36u;
ret = peekNextHunk(&inst);
CHECK( ret == 'm' );
}

View File

@@ -123,3 +123,58 @@ TEST_CASE( "shellmatta_insertChars 0 length" ) {
CHECK( memcmp("\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", write_data, sizeof(write_data) ) == 0u );
REQUIRE( memcmp("abcdefghij\0\0\0\0\0\0\0\0\0", buffer, sizeof(buffer)) == 0);
}
TEST_CASE( "shellmatta_insertChars buffer full" ) {
shellmatta_instance_t inst;
char buffer[20] = "abcdefghij\0\0\0\0\0\0\0\0\0";
memset(&inst, 0, sizeof(inst));
inst.buffer = buffer;
inst.bufferSize = 20;
inst.cursor = 8;
inst.inputCount = 10;
inst.echoEnabled = true;
inst.write = writeFct;
write_callCnt = 0u;
memset(write_data, 0, sizeof(write_data));
write_idx = 0u;
utils_insertChars(&inst, (char*)"blksdflsd kfjlk", 10u);
CHECK( inst.cursor == 18u );
CHECK( inst.inputCount == 20u );
CHECK( write_callCnt == 5u );
CHECK( memcmp("blksdflsd ", write_data, 10u ) == 0u );
REQUIRE( memcmp("abcdefghblksdflsd ij", buffer, sizeof(buffer)) == 0);
}
TEST_CASE( "shellmatta_insertChars buffer overflow by 1" ) {
shellmatta_instance_t inst;
char buffer[20] = "abcdefghij\0\0\0\0\0\0\0\0\0";
memset(&inst, 0, sizeof(inst));
inst.buffer = buffer;
inst.bufferSize = 20;
inst.cursor = 8;
inst.inputCount = 10;
inst.echoEnabled = true;
inst.write = writeFct;
write_callCnt = 0u;
memset(write_data, 0, sizeof(write_data));
write_idx = 0u;
utils_insertChars(&inst, (char*)"blksdflsd kfjlk", 11u);
CHECK( inst.cursor == 18u );
CHECK( inst.inputCount == 20u );
CHECK( write_callCnt == 5u );
CHECK( memcmp("blksdflsd ", write_data, 10u ) == 0u );
REQUIRE( memcmp("abcdefghblksdflsd ij", buffer, sizeof(buffer)) == 0);
}

View File

@@ -0,0 +1,200 @@
#include "test/framework/catch.hpp"
#include "src/shellmatta_utils.h"
#include <string.h>
static uint32_t write_callCnt = 0u;
static char write_data[20];
static uint32_t write_idx;
static shellmatta_retCode_t writeFct(const char* data, uint32_t length)
{
write_callCnt ++;
strncpy(&write_data[write_idx], data, length);
write_idx += length;
return SHELLMATTA_OK;
}
TEST_CASE("shellmatta_utils_removeChars_nothing_removed"){
shellmatta_instance_t inst;
memset(&inst, 0, sizeof(inst));
uint32_t length = 0u;
bool backspace = true;
inst.write = writeFct;
write_callCnt = 0u;
memset(write_data, 0, sizeof(write_data));
write_idx = 0u;
inst.cursor = 20u;
inst.inputCount = 20u;
char buffer[20] = "abcdefghijklmnopqr";
inst.buffer = buffer;
inst.bufferSize = 20u;
utils_removeChars( &inst, length, backspace);
CHECK( inst.cursor == 20u);
CHECK( inst.inputCount == 20);
REQUIRE(strncmp("abcdefghijklmnopqr", buffer, sizeof(buffer)) == 0);
}
TEST_CASE("shellmatta_utils_removeChars_backspace_false"){
shellmatta_instance_t inst;
char buffer[20] = "abcdefghijklmnopqr";
memset(&inst, 0, sizeof(inst));
uint32_t length = 5u;
bool backspace = false;
inst.write = writeFct;
write_callCnt = 0u;
memset(write_data, 0, sizeof(write_data));
write_idx = 0u;
inst.cursor = 20;
inst.inputCount = 20u;
inst.buffer = buffer;
inst.bufferSize = 20u;
utils_removeChars(&inst, length, backspace);
CHECK( inst.cursor == 20u);
CHECK( inst.inputCount == 20);
REQUIRE(strncmp("abcdefghijklmnopqr", buffer, sizeof(buffer)) == 0);
}
TEST_CASE("shellmatta_utils_removeChars_remove_five"){
shellmatta_instance_t inst;
char buffer[20] = "abcdefghijklmnopqr";
memset(&inst, 0, sizeof(inst));
inst.write = writeFct;
write_callCnt = 0u;
memset(write_data, 0, sizeof(write_data));
write_idx = 0u;
uint32_t length = 5u;
bool backspace = true;
inst.cursor = 10u;
inst.inputCount = 20u;
inst.bufferSize = 20u;
inst.buffer = buffer;
utils_removeChars(&inst, length, backspace);
CHECK( inst.cursor == 5u);
CHECK( inst.inputCount == 15u);
REQUIRE(strncmp("abcdeklmnopqr", buffer, sizeof(buffer)) == 0);
}
TEST_CASE("shellmatta_utils_removeChars_length_greater_than_CursorPos"){
shellmatta_instance_t inst;
char buffer[20] = "abcdefghijklmnopqr";
memset(&inst, 0, sizeof(inst));
inst.write = writeFct;
write_callCnt = 0u;
memset(write_data, 0, sizeof(write_data));
write_idx = 0u;
uint32_t length = 15u;
bool backspace = true;
inst.cursor = 10u;
inst.inputCount = 20u;
inst.bufferSize = 20u;
inst.buffer = buffer;
utils_removeChars(&inst, length, backspace);
CHECK( inst.cursor == 0u);
CHECK( inst.inputCount == 10u);
REQUIRE(strncmp("klmnopqr", buffer, sizeof(buffer)) == 0);
}
TEST_CASE("shellmatta_utils_removeChars_remove_chars_in_the_middle_of_the_buffer_backspace_false"){
shellmatta_instance_t inst;
char buffer[20] = "abcdefghijklmnopqr";
memset(&inst, 0, sizeof(inst));
inst.write = writeFct;
write_callCnt = 0u;
memset(write_data, 0, sizeof(write_data));
write_idx = 0u;
uint32_t length = 5u;
bool backspace = false;
inst.cursor = 10u;
inst.inputCount = 20u;
inst.bufferSize = 20u;
inst.buffer = buffer;
utils_removeChars(&inst, length, backspace);
CHECK( inst.cursor == 10u);
CHECK( inst.inputCount == 15u);
REQUIRE(strncmp("abcdefghijpqr", buffer, sizeof(buffer)) == 0);
}
TEST_CASE("shellmatta_utils_removeChars_remove_more_chars_in_middle_of_buffer_than_are_present_backspace_false"){
shellmatta_instance_t inst;
char buffer[20] = "abcdefghijklmnopqr";
memset(&inst, 0, sizeof(inst));
inst.write = writeFct;
write_callCnt = 0u;
memset(write_data, 0, sizeof(write_data));
write_idx = 0u;
uint32_t length = 15u;
bool backspace = false;
inst.cursor = 10u;
inst.inputCount = 20u;
inst.bufferSize = 20u;
inst.buffer = buffer;
utils_removeChars(&inst, length, backspace);
CHECK( inst.cursor == 10u);
CHECK( inst.inputCount == 10u);
REQUIRE(strncmp("abcdefghij", buffer, 10u) == 0);
}
TEST_CASE("shellmatta_utils_removeChars_curser_outside_buffer"){
shellmatta_instance_t inst;
char buffer[20] = "abcdefghijklmnopqr";
memset(&inst, 0, sizeof(inst));
inst.write = writeFct;
write_callCnt = 0u;
memset(write_data, 0, sizeof(write_data));
write_idx = 0u;
uint32_t length = 15u;
bool backspace = false;
inst.cursor = 21u;
inst.inputCount = 20u;
inst.bufferSize = 20u;
inst.buffer = buffer;
utils_removeChars(&inst, length, backspace);
CHECK( inst.cursor == 21u);
CHECK( inst.inputCount == 20u);
REQUIRE(strncmp("abcdefghijklmnopqr", buffer, sizeof(buffer)) == 0);
}