Преглед на файлове

Merge branch 'feature/#52-only-store-new-commands-in-history' of shimatta/shellmatta into develop

fix #52
shimatta преди 3 години
родител
ревизия
a9b8dcb504

+ 1 - 1
.vscode/launch.json

@@ -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": [],

+ 187 - 2
README.md

@@ -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].

+ 1 - 1
api/shellmatta.h

@@ -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

+ 1 - 1
example/main.c

@@ -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

+ 32 - 13
makefile

@@ -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++
@@ -45,14 +48,16 @@ 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
 
-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
@@ -68,7 +73,7 @@ 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
@@ -95,7 +100,6 @@ unittest: $(UNITTEST_TARGET)
 #	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
 	-rm -rf $(OBJ_DIR)test/unittest/report/*
@@ -104,8 +108,13 @@ unittest: $(UNITTEST_TARGET)
 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)
-	gcovr --html-details --output $(OBJ_DIR)test/integrationtest/report/report.html output/src -f src -f api -d
+
+	#	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
@@ -126,7 +135,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)
 
@@ -144,10 +153,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 $(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)
-	@$(CPP) -c $(TESTFLAGS) $(DEPEND) -o $@  $(subst $(OBJ_DIR), ,$(@:%.o=%.cpp))
-	$(CPP) -c $(TESTFLAGS) -o $@  $(subst $(OBJ_DIR), ,$(@:%.o=%.cpp))
+	@$(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)

+ 1 - 1
src/shellmatta.c

@@ -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

+ 1 - 1
src/shellmatta_autocomplete.c

@@ -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

+ 1 - 1
src/shellmatta_autocomplete.h

@@ -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

+ 2 - 2
src/shellmatta_escape.c

@@ -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);

+ 1 - 1
src/shellmatta_escape.h

@@ -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

+ 56 - 7
src/shellmatta_history.c

@@ -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
@@ -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;
 }
 

+ 1 - 1
src/shellmatta_history.h

@@ -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

+ 1 - 1
src/shellmatta_opt.c

@@ -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

+ 1 - 1
src/shellmatta_opt.h

@@ -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

+ 1 - 1
src/shellmatta_utils.c

@@ -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

+ 1 - 1
src/shellmatta_utils.h

@@ -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

Файловите разлики са ограничени, защото са твърде много
+ 6643 - 0
test/framework/fff.h


+ 14 - 1
test/integrationtest/test_integration.cpp

@@ -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"
@@ -562,4 +576,3 @@ TEST_CASE( "shellmatta configure delimiter" ) {
     CHECK( write_length == strlen(dummyData));
     REQUIRE( strcmp(dummyData, write_data) == 0);
 }
-

+ 1 - 1
test/integrationtest/test_integration_busy.cpp

@@ -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

+ 1 - 1
test/integrationtest/test_integration_continue.cpp

@@ -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

+ 495 - 0
test/integrationtest/test_integration_history.cpp

@@ -0,0 +1,495 @@
+/*
+ * 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));
+
+shellmatta_cmd_t cmd1 = {(char*)"cmd1", (char*)"1", NULL, NULL, cmdFct1, NULL};
+shellmatta_cmd_t cmd2 = {(char*)"cmd2", (char*)"2", NULL, NULL, cmdFct2, NULL};
+shellmatta_cmd_t cmd3 = {(char*)"cmd3", (char*)"3", NULL, NULL, cmdFct3, NULL};
+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];
+
+        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};
+
+        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};
+
+        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]);
+            }
+        }
+    }
+}

+ 1 - 1
test/integrationtest/test_integration_opt.cpp

@@ -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

+ 1 - 1
test/integrationtest/test_integration_optLong.cpp

@@ -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

+ 4 - 0
test/integrationtest/test_main.cpp

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