AutoAnswer: add process test

GitLab: https://git.jami.net/savoirfairelinux/jami-daemon/-/issues/745

Change-Id: I54e5df2ffa44e779f52e5b404898c91d280ac1f6
diff --git a/AutoAnswer/.gitignore b/AutoAnswer/.gitignore
new file mode 100644
index 0000000..0924e88
--- /dev/null
+++ b/AutoAnswer/.gitignore
@@ -0,0 +1,2 @@
+/AutoAnswer
+AutoAnswer*
diff --git a/AutoAnswer/BotChatHandler.cpp b/AutoAnswer/BotChatHandler.cpp
index 6a03179..c877e37 100644
--- a/AutoAnswer/BotChatHandler.cpp
+++ b/AutoAnswer/BotChatHandler.cpp
@@ -72,7 +72,8 @@
 
 BotChatHandler::~BotChatHandler()
 {
-    for (const auto& subject : subjects) {
+    const auto copy = subjects;
+    for (const auto& subject : copy) {
         detach(subject);
     }
 }
diff --git a/AutoAnswer/BotPeerChatSubscriber.cpp b/AutoAnswer/BotPeerChatSubscriber.cpp
index 19760f5..8f2475a 100644
--- a/AutoAnswer/BotPeerChatSubscriber.cpp
+++ b/AutoAnswer/BotPeerChatSubscriber.cpp
@@ -60,6 +60,10 @@
             else if (message->data.at("type") == "text/plain" && message->data.at("body") == input) {
                 sendMsg["type"] = "text/plain";
                 sendMsg["body"] = answer;
+#ifdef __DEBUG__
+                Plog::log(Plog::LogPriority::INFO, TAG, "input " + message->data.at("body"));
+                Plog::log(Plog::LogPriority::INFO, TAG, "ouput " + answer);
+#endif
             }
             if (!sendMsg.empty()) {
                 sendText(message->accountId, message->peerId, sendMsg, message->isSwarm);
@@ -106,6 +110,8 @@
                                                                sendMsg,
                                                                true);
     botAnswer->isSwarm = swarm;
+#ifndef __DEBUG__
     api_->invokeService(api_, "sendTextMessage", botAnswer.get());
+#endif
 }
 } // namespace jami
diff --git a/AutoAnswer/CMakeLists.txt b/AutoAnswer/CMakeLists.txt
index a0db3f1..eb06399 100644
--- a/AutoAnswer/CMakeLists.txt
+++ b/AutoAnswer/CMakeLists.txt
@@ -33,24 +33,39 @@
 
 set(CMAKE_CXX_STANDARD 17)
 set(CMAKE_CXX_STANDARD_REQUIRED True)
-set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT")
-set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MTd")
+
+if(TESTPROCESS)
+    set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /D__DEBUG__ /MD")
+    set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /D__DEBUG__ /MD")
+else()
+    set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MD")
+    set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MD")
+endif()
 
 set(plugin_SRC BotChatHandler.cpp
                BotPeerChatSubscriber.cpp
                PluginPreferenceHandler.cpp
                main.cpp
+               ./../lib/common.cpp
                )
 
 set(plugin_HDR BotChatHandler.h
                BotPeerChatSubscriber.h
                PluginPreferenceHandler.h
                ./../lib/pluglog.h
+               ./../lib/common.h
                )
 
-add_library(${ProjectName} SHARED ${plugin_SRC}
+if(TESTPROCESS)
+    add_executable(${ProjectName} ${plugin_SRC}
                                   ${plugin_HDR}
                                   )
+else()
+    add_library(${ProjectName} SHARED ${plugin_SRC}
+                                      ${plugin_HDR}
+                                      )
+endif()
+
 
 target_include_directories(${ProjectName} PUBLIC ${PROJECT_BINARY_DIR}
                                                  ${PROJECT_SOURCE_DIR}
@@ -58,23 +73,35 @@
                                                  ${DAEMON_SRC}
                                                  ${CONTRIB_PATH}
                                                  ${CONTRIB_PATH}/build/fmt/include
+                                                 ${CONTRIB_PATH}/build/yaml-cpp/include
                                                  )
 target_link_directories(${ProjectName} PUBLIC ${CONTRIB_PATH}
                                               ${CONTRIB_PATH}/build/fmt/msvc/Release
+                                              ${CONTRIB_PATH}/build/yaml-cpp/msvc/Release
                                         )
 
-add_custom_command(
-    TARGET ${ProjectName}
-    PRE_BUILD
-    COMMAND python ${PROJECT_SOURCE_DIR}/../SDK/jplManipulation.py --preassemble --plugin=${ProjectName}
-    COMMENT "Assembling Plugin files"
-)
+target_link_libraries(${ProjectName} PUBLIC libyaml-cppmd)
 
-add_custom_command(
-    TARGET ${ProjectName}
-    POST_BUILD
-    COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_BINARY_DIR}/Release/${ProjectName}.lib ${JPL_DIRECTORY}/lib/${CONTRIB_PLATFORM}
-    COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_BINARY_DIR}/Release/${LIBRARY_FILE_NAME} ${JPL_DIRECTORY}/lib/${CONTRIB_PLATFORM}
-    COMMAND python ${PROJECT_SOURCE_DIR}/../SDK/jplManipulation.py --assemble --plugin=${ProjectName}
-    COMMENT "Generating JPL archive"
-)
+if(TESTPROCESS)
+    add_custom_command(
+        TARGET ${ProjectName}
+        PRE_BUILD
+        COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_SOURCE_DIR}/testPreferences.yml ${PROJECT_BINARY_DIR}/
+    )
+else()
+    add_custom_command(
+        TARGET ${ProjectName}
+        PRE_BUILD
+        COMMAND python ${PROJECT_SOURCE_DIR}/../SDK/jplManipulation.py --preassemble --plugin=${ProjectName}
+        COMMENT "Assembling Plugin files"
+    )
+
+    add_custom_command(
+        TARGET ${ProjectName}
+        POST_BUILD
+        COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_BINARY_DIR}/Release/${ProjectName}.lib ${JPL_DIRECTORY}/lib/${CONTRIB_PLATFORM}
+        COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_BINARY_DIR}/Release/${LIBRARY_FILE_NAME} ${JPL_DIRECTORY}/lib/${CONTRIB_PLATFORM}
+        COMMAND python ${PROJECT_SOURCE_DIR}/../SDK/jplManipulation.py --assemble --plugin=${ProjectName}
+        COMMENT "Generating JPL archive"
+    )
+endif()
diff --git a/AutoAnswer/build.sh b/AutoAnswer/build.sh
index 5af8d21..8b136ec 100755
--- a/AutoAnswer/build.sh
+++ b/AutoAnswer/build.sh
@@ -6,10 +6,10 @@
 EXTRAPATH=''
 # Flags:
 
-# -p: number of processors to use
+# -p: number of processors to use.
 # -c: Runtime plugin cpu/gpu setting.
 # -t: target platform.
-
+# -d: debug program.
 
 if [ -z "${DAEMON}" ]; then
     DAEMON="./../../daemon"
@@ -36,8 +36,11 @@
     echo "Building with ${PLATFORM}"
 fi
 
-while getopts t:c:p OPT; do
+while getopts t:c:p:d OPT; do
   case "$OPT" in
+    d)
+      DEBUG=true
+    ;;
     t)
       PLATFORM="${OPTARG}"
     ;;
@@ -54,34 +57,48 @@
 
 if [ "${PLATFORM}" = "linux-gnu" ] || [ "${PLATFORM}" = "redhat-linux" ] || [ "${PLATFORM}" = "Darwin" ]
 then
-    python3 ./../SDK/jplManipulation.py --preassemble --plugin=${PLUGIN_NAME}
-
     CONTRIB_PLATFORM=${CONTRIB_PLATFORM_CURT}-${PLATFORM}
     OUTPUTFOLDER=${CONTRIB_PLATFORM}
     if [ "${PLATFORM}" = "Darwin" ]; then
       CONTRIB_PLATFORM=${CONTRIB_PLATFORM}${CONTRIB_PLATFORM_EXTRA}
     fi
 
+    if [ ${DEBUG} ]; then
+      OUTPUT="${PLUGIN_NAME}"
+      CLANG_OPTS="-g -fsanitize=address"
+      EXTRA_DEBUG_LIBRARIES="-lyaml-cpp"
+      EXTRA_DEFINES="-D__DEBUG__"
+    else
+      python3 ./../SDK/jplManipulation.py --preassemble --plugin=${PLUGIN_NAME}
+      CLANG_OPTS="-O3 -shared"
+      OUTPUT="build-local/jpl/lib/${OUTPUTFOLDER}/${SO_FILE_NAME}"
+    fi
+
     # Compile
-    clang++ -std=c++17 -shared -fPIC \
+    clang++ -std=c++17 -fPIC ${CLANG_OPTS} \
     -Wl,-rpath,"\${ORIGIN}" \
     -Wall -Wextra \
-    -Wno-unused-variable \
-    -Wno-unused-function \
     -Wno-unused-parameter \
+    ${EXTRA_DEFINES} \
     -I"." \
     -I"${DAEMON_SRC}" \
     -I"${CONTRIB_PATH}/${CONTRIB_PLATFORM}/include" \
     -I"${PLUGINS_LIB}" \
+    ./../lib/common.cpp \
     PluginPreferenceHandler.cpp \
     BotChatHandler.cpp \
     BotPeerChatSubscriber.cpp \
     main.cpp \
     -L"${CONTRIB_PATH}/${CONTRIB_PLATFORM}/lib/" \
-    -o "build-local/jpl/lib/${OUTPUTFOLDER}/${SO_FILE_NAME}"
+    ${EXTRA_DEBUG_LIBRARIES} \
+    -o "${OUTPUT}"
 
     if [ "${PLATFORM}" = "Darwin" ]; then
-      install_name_tool -id "@loader_path/${SO_FILE_NAME}" "build-local/jpl/lib/${OUTPUTFOLDER}/${SO_FILE_NAME}"
+      if [ ! ${DEBUG} ]; then
+          install_name_tool -id "@loader_path/${PLUGIN_NAME}" "${OUTPUT}"
+      else
+          install_name_tool -id "@loader_path/${SO_FILE_NAME}" "${OUTPUT}"
+      fi
     fi
 
     if [ -n "${APPLE_SIGN_CERTIFICATE}" ]; then
@@ -199,8 +216,6 @@
         -Wl,-Bsymbolic,-rpath,"\${ORIGIN}" \
         -shared \
         -Wall -Wextra \
-        -Wno-unused-variable \
-        -Wno-unused-function \
         -Wno-unused-parameter \
         -I"." \
         -I"${DAEMON_SRC}" \
@@ -223,4 +238,6 @@
     done
 fi
 
-python3 ./../SDK/jplManipulation.py --assemble --plugin=${PLUGIN_NAME} --distribution=${PLATFORM} --extraPath=${EXTRAPATH}
+if [ ! ${DEBUG} ]; then
+  python3 ./../SDK/jplManipulation.py --assemble --plugin=${PLUGIN_NAME} --distribution=${PLATFORM} --extraPath=${EXTRAPATH}
+fi
diff --git a/AutoAnswer/main.cpp b/AutoAnswer/main.cpp
index ff4d180..dfed25f 100644
--- a/AutoAnswer/main.cpp
+++ b/AutoAnswer/main.cpp
@@ -26,6 +26,13 @@
 #include "plugin/jamiplugin.h"
 #include "BotChatHandler.h"
 
+#ifdef __DEBUG__
+#include <common.h>
+#include <assert.h>
+#include <yaml-cpp/yaml.h>
+#include <fstream>
+#endif
+
 #ifdef WIN32
 #define EXPORT_PLUGIN __declspec(dllexport)
 #else
@@ -80,3 +87,85 @@
     return pluginExit;
 }
 }
+
+#ifdef __DEBUG__
+
+int
+main ()
+{
+    std::cout << "********************************" << std::endl;
+    std::cout << "**  AutoAnswer Debug Version  **" << std::endl;
+    std::cout << "********************************" << std::endl;
+    std::cout << "Version " << AutoAnswer_VERSION_MAJOR << "." << AutoAnswer_VERSION_MINOR << "."
+              << AutoAnswer_VERSION_PATCH << std::endl << std::endl;
+
+
+#ifdef _WIN32
+    std::ifstream file = std::ifstream(string_utils::to_wstring("testPreferences.yml"));
+#else
+    std::ifstream file = std::ifstream("testPreferences.yml", std::ios_base::in);
+#endif
+
+    assert(file.is_open());
+    YAML::Node node = YAML::Load(file);
+
+    assert(node.IsMap());
+    std::map<std::string, std::map<std::string, std::string>> preferences;
+    preferences["default"] = {};
+    for (const auto& kv : node) {
+        preferences["default"][kv.first.as<std::string>()] = kv.second.as<std::string>();
+        std::cout << "Key: " << kv.first.as<std::string>() << "; Value: " << kv.second.as<std::string>() << std::endl;
+    }
+
+    std::string dataPath = "tester";
+
+    auto fmpPluginPreferenceHandler
+        = std::make_unique<jami::PluginPreferenceHandler>(nullptr, std::move(preferences), dataPath);
+
+    auto fmpBotChatHandler
+        = std::make_unique<jami::BotChatHandler>(nullptr,
+                                                    std::move(dataPath),
+                                                    fmpPluginPreferenceHandler.get());
+
+    auto subject = std::make_shared<jami::PublishObservable<jami::pluginMessagePtr>>();
+    std::pair<std::string, std::string> subjectConnection("origin", "destiny");
+    fmpBotChatHandler->notifyChatSubject(subjectConnection, subject);
+
+    // Only test for swarm
+
+    // Valid Sender, Receiver, direction and message
+    std::cout << "Test 1" << std::endl << "Should print input/output" << std::endl;
+    std::map<std::string, std::string> sendMsg = {{"type", "text/plain"}, {"body", preferences["default"]["inText"]}};
+    jami::pluginMessagePtr jamiMsg = std::make_shared<JamiMessage>("origin",
+                                                                   "destiny",
+                                                                   true,
+                                                                   sendMsg,
+                                                                   false);
+    jamiMsg->isSwarm = true;
+    subject->publish(jamiMsg);
+
+
+    // Valid Sender, Receiver and message but not direction
+    std::cout << "Test 2" << std::endl << "Should NOT print input/output" << std::endl;
+    jamiMsg.reset(new JamiMessage("origin",
+                                  "destiny",
+                                  false,
+                                  sendMsg,
+                                  false));
+    jamiMsg->isSwarm = true;
+    subject->publish(jamiMsg);
+
+    // Invalid Sender, Receiver, direction and message
+    std::cout << "Test 3" << std::endl << "Should NOT print input/output" << std::endl;
+    sendMsg["body"] = preferences["default"]["invalid"];
+    jamiMsg.reset(new JamiMessage("destiny",
+                                  "origin",
+                                  true,
+                                  sendMsg,
+                                  false));
+    jamiMsg->isSwarm = true;
+    subject->publish(jamiMsg);
+
+    return 0;
+}
+#endif
diff --git a/AutoAnswer/package.json b/AutoAnswer/package.json
index 3161df3..a1dee2f 100644
--- a/AutoAnswer/package.json
+++ b/AutoAnswer/package.json
@@ -3,9 +3,12 @@
     "version": "2.0.0",
     "extractLibs": false,
     "deps": [
-        "fmt"
+        "fmt",
+        "yaml-cpp"
     ],
-    "defines": [],
+    "defines": [
+        "TESTPROCESS=False"
+    ],
     "custom_scripts": {
         "pre_build": [
             "mkdir msvc"
diff --git a/AutoAnswer/testPreferences.yml b/AutoAnswer/testPreferences.yml
new file mode 100644
index 0000000..5b0283d
--- /dev/null
+++ b/AutoAnswer/testPreferences.yml
@@ -0,0 +1,3 @@
+answer: "Occupied"
+inText: "Trigger"
+invalid: "invalid message"
diff --git a/GreenScreen/pluginProcessor.cpp b/GreenScreen/pluginProcessor.cpp
index 737ebfe..52b7d17 100644
--- a/GreenScreen/pluginProcessor.cpp
+++ b/GreenScreen/pluginProcessor.cpp
@@ -29,24 +29,7 @@
 #include <pluglog.h>
 
 #ifdef WIN32
-#include <WTypes.h>
-
-namespace string_utils {
-std::wstring
-to_wstring(const std::string& str) {
-    int codePage = CP_UTF8;
-    int srcLength = (int) str.length();
-    int requiredSize = MultiByteToWideChar(codePage, 0, str.c_str(), srcLength, nullptr, 0);
-    if (!requiredSize) {
-        throw std::runtime_error("Can't convert string to wstring");
-    }
-    std::wstring result((size_t) requiredSize, 0);
-    if (!MultiByteToWideChar(codePage, 0, str.c_str(), srcLength, &(*result.begin()), requiredSize)) {
-        throw std::runtime_error("Can't convert string to wstring");
-    }
-    return result;
-}
-} // namespace string_utils
+#include <common.h>
 #endif
 
 const char sep = separator();
diff --git a/lib/common.cpp b/lib/common.cpp
new file mode 100644
index 0000000..2047278
--- /dev/null
+++ b/lib/common.cpp
@@ -0,0 +1,43 @@
+/**
+ *  Copyright (C) 2022 Savoir-faire Linux Inc.
+ *
+ *  Author: Aline Gondim Santos <aline.gondimsantos@savoirfairelinux.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+ */
+
+#include "common.h"
+
+#ifdef WIN32
+#include <WTypes.h>
+#include <stdexcept>
+
+namespace string_utils {
+std::wstring
+to_wstring(const std::string& str) {
+    int codePage = CP_UTF8;
+    int srcLength = (int) str.length();
+    int requiredSize = MultiByteToWideChar(codePage, 0, str.c_str(), srcLength, nullptr, 0);
+    if (!requiredSize) {
+        throw std::runtime_error("Can't convert string to wstring");
+    }
+    std::wstring result((size_t) requiredSize, 0);
+    if (!MultiByteToWideChar(codePage, 0, str.c_str(), srcLength, &(*result.begin()), requiredSize)) {
+        throw std::runtime_error("Can't convert string to wstring");
+    }
+    return result;
+}
+} // namespace string_utils
+#endif // WIN32
diff --git a/lib/common.h b/lib/common.h
new file mode 100644
index 0000000..f0543d0
--- /dev/null
+++ b/lib/common.h
@@ -0,0 +1,32 @@
+/**
+ *  Copyright (C) 2022 Savoir-faire Linux Inc.
+ *
+ *  Author: Aline Gondim Santos <aline.gondimsantos@savoirfairelinux.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+ */
+
+#ifndef COMMON_H
+#define COMMON_H
+
+#ifdef WIN32
+
+#include <string>
+
+namespace string_utils {
+std::wstring to_wstring(const std::string& str);
+} // namespace string_utils
+#endif // WIN32
+#endif // COMMON_H