Add TriggerManager and Trigger objects

Implemented initial version of Trigger and TriggerManager DBus
interfaces. Now DBus user is able to add new Trigger and delete it.
There is no support for other functionality added.

Tested:
 - Built using yocto dependencies with success
 - Unit tests passed
 - Verified manually if Trigger and TriggerManager works as
   expected

Change-Id: Ie68463526ecccc3be67cc7bfaaf9a9aa7326dee6
Signed-off-by: Wludzik, Jozef <jozef.wludzik@intel.com>
diff --git a/meson.build b/meson.build
index 160946f..c40b9f3 100644
--- a/meson.build
+++ b/meson.build
@@ -72,6 +72,7 @@
     '-DTELEMETRY_MAX_REPORTS=' + get_option('max-reports').to_string(),
     '-DTELEMETRY_MAX_READING_PARAMS=' + get_option('max-reading-parameters').to_string(),
     '-DTELEMETRY_MIN_INTERVAL=' + get_option('min-interval').to_string(),
+    '-DTELEMETRY_MAX_TRIGGERS=' + get_option('max-triggers').to_string(),
     language: 'cpp'
 )
 
@@ -86,6 +87,9 @@
         'src/report_manager.cpp',
         'src/sensor.cpp',
         'src/sensor_cache.cpp',
+        'src/trigger.cpp',
+        'src/trigger_factory.cpp',
+        'src/trigger_manager.cpp',
     ],
     dependencies: [
         boost,
diff --git a/meson_options.txt b/meson_options.txt
index 5021eb6..cad7253 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -5,3 +5,5 @@
        description: 'Max number of reading parameters in single report')
 option('min-interval', type: 'integer', min: 1, value: 1000,
        description: 'Minimal value of interval in milliseconds')
+option('max-triggers', type: 'integer', min: 1, value: 10,
+       description: 'Max number of Triggers')
diff --git a/src/interfaces/trigger.hpp b/src/interfaces/trigger.hpp
new file mode 100644
index 0000000..328e581
--- /dev/null
+++ b/src/interfaces/trigger.hpp
@@ -0,0 +1,17 @@
+#pragma once
+
+#include <string>
+
+namespace interfaces
+{
+
+class Trigger
+{
+  public:
+    virtual ~Trigger() = default;
+
+    virtual std::string getName() const = 0;
+    virtual std::string getPath() const = 0;
+};
+
+} // namespace interfaces
diff --git a/src/interfaces/trigger_factory.hpp b/src/interfaces/trigger_factory.hpp
new file mode 100644
index 0000000..91f7f4b
--- /dev/null
+++ b/src/interfaces/trigger_factory.hpp
@@ -0,0 +1,28 @@
+#pragma once
+
+#include "interfaces/trigger.hpp"
+#include "interfaces/trigger_manager.hpp"
+#include "interfaces/trigger_types.hpp"
+
+#include <memory>
+#include <utility>
+
+namespace interfaces
+{
+
+class TriggerFactory
+{
+  public:
+    virtual ~TriggerFactory() = default;
+
+    virtual std::unique_ptr<interfaces::Trigger> make(
+        const std::string& name, bool isDiscrete, bool logToJournal,
+        bool logToRedfish, bool updateReport,
+        const std::vector<
+            std::pair<sdbusplus::message::object_path, std::string>>& sensors,
+        const std::vector<std::string>& reportNames,
+        const TriggerThresholdParams& thresholdParams,
+        interfaces::TriggerManager& triggerManager) const = 0;
+};
+
+} // namespace interfaces
diff --git a/src/interfaces/trigger_manager.hpp b/src/interfaces/trigger_manager.hpp
new file mode 100644
index 0000000..878fd57
--- /dev/null
+++ b/src/interfaces/trigger_manager.hpp
@@ -0,0 +1,16 @@
+#pragma once
+
+#include "interfaces/trigger.hpp"
+
+namespace interfaces
+{
+
+class TriggerManager
+{
+  public:
+    virtual ~TriggerManager() = default;
+
+    virtual void removeTrigger(const Trigger* trigger) = 0;
+};
+
+} // namespace interfaces
diff --git a/src/interfaces/trigger_types.hpp b/src/interfaces/trigger_types.hpp
new file mode 100644
index 0000000..ef61aa6
--- /dev/null
+++ b/src/interfaces/trigger_types.hpp
@@ -0,0 +1,48 @@
+#pragma once
+
+#include <sdbusplus/message/types.hpp>
+
+#include <string>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+namespace discrete
+{
+
+enum class Severity
+{
+    ok = 0,
+    warning,
+    critical
+};
+
+using ThresholdParam = std::tuple<std::string, std::underlying_type_t<Severity>,
+                                  std::variant<double>, uint64_t>;
+} // namespace discrete
+
+namespace numeric
+{
+
+enum class Type
+{
+    lowerCritical = 0,
+    lowerWarning,
+    upperWarning,
+    upperCritical
+};
+
+enum class Direction
+{
+    either = 0,
+    decreasing,
+    increasing
+};
+
+using ThresholdParam = std::tuple<std::underlying_type_t<Type>, uint64_t,
+                                  std::underlying_type_t<Direction>, double>;
+} // namespace numeric
+
+using TriggerThresholdParams =
+    std::variant<std::vector<numeric::ThresholdParam>,
+                 std::vector<discrete::ThresholdParam>>;
diff --git a/src/report_manager.hpp b/src/report_manager.hpp
index 2180d52..a1a9007 100644
--- a/src/report_manager.hpp
+++ b/src/report_manager.hpp
@@ -27,7 +27,6 @@
     ReportManager& operator=(ReportManager&&) = delete;
 
     void removeReport(const interfaces::Report* report) override;
-    static bool verifyScanPeriod(const uint64_t scanPeriod);
 
   private:
     std::unique_ptr<interfaces::ReportFactory> reportFactory;
diff --git a/src/telemetry.hpp b/src/telemetry.hpp
index 4cc138e..20749e4 100644
--- a/src/telemetry.hpp
+++ b/src/telemetry.hpp
@@ -4,6 +4,8 @@
 #include "report_factory.hpp"
 #include "report_manager.hpp"
 #include "sensor_cache.hpp"
+#include "trigger_factory.hpp"
+#include "trigger_manager.hpp"
 
 #include <sdbusplus/asio/connection.hpp>
 #include <sdbusplus/asio/object_server.hpp>
@@ -19,10 +21,13 @@
                       std::make_unique<PersistentJsonStorage>(
                           interfaces::JsonStorage::DirectoryPath(
                               "/var/lib/telemetry/Reports")),
-                      objServer)
+                      objServer),
+        triggerManager(std::make_unique<TriggerFactory>(bus, objServer),
+                       objServer)
     {}
 
   private:
     std::shared_ptr<sdbusplus::asio::object_server> objServer;
     ReportManager reportManager;
+    TriggerManager triggerManager;
 };
diff --git a/src/trigger.cpp b/src/trigger.cpp
new file mode 100644
index 0000000..4152dd1
--- /dev/null
+++ b/src/trigger.cpp
@@ -0,0 +1,60 @@
+#include "trigger.hpp"
+
+#include "interfaces/types.hpp"
+
+Trigger::Trigger(
+    boost::asio::io_context& ioc,
+    const std::shared_ptr<sdbusplus::asio::object_server>& objServer,
+    const std::string& nameIn, const bool isDiscrete, const bool logToJournal,
+    const bool logToRedfish, const bool updateReport,
+    const std::vector<std::pair<sdbusplus::message::object_path, std::string>>&
+        sensorsIn,
+    const std::vector<std::string>& reportNamesIn,
+    const TriggerThresholdParams& thresholdParamsIn,
+    interfaces::TriggerManager& triggerManager) :
+    name(nameIn),
+    path(triggerDir + name), persistent(false), sensors(sensorsIn),
+    reportNames(reportNamesIn), thresholdParams(thresholdParamsIn)
+{
+    deleteIface = objServer->add_unique_interface(
+        path, deleteIfaceName, [this, &ioc, &triggerManager](auto& dbusIface) {
+            dbusIface.register_method("Delete", [this, &ioc, &triggerManager] {
+                boost::asio::post(ioc, [this, &triggerManager] {
+                    triggerManager.removeTrigger(this);
+                });
+            });
+        });
+
+    triggerIface = objServer->add_unique_interface(
+        path, triggerIfaceName,
+        [this, isDiscrete, logToJournal, logToRedfish,
+         updateReport](auto& dbusIface) {
+            dbusIface.register_property_r(
+                "Persistent", persistent,
+                sdbusplus::vtable::property_::emits_change,
+                [](const auto& x) { return x; });
+            dbusIface.register_property_r(
+                "Thresholds", thresholdParams,
+                sdbusplus::vtable::property_::emits_change,
+                [](const auto& x) { return x; });
+            dbusIface.register_property_r(
+                "Sensors", sensors, sdbusplus::vtable::property_::emits_change,
+                [](const auto& x) { return x; });
+            dbusIface.register_property_r(
+                "ReportNames", reportNames,
+                sdbusplus::vtable::property_::emits_change,
+                [](const auto& x) { return x; });
+            dbusIface.register_property_r("Discrete", isDiscrete,
+                                          sdbusplus::vtable::property_::const_,
+                                          [](const auto& x) { return x; });
+            dbusIface.register_property_r("LogToJournal", logToJournal,
+                                          sdbusplus::vtable::property_::const_,
+                                          [](const auto& x) { return x; });
+            dbusIface.register_property_r("LogToRedfish", logToRedfish,
+                                          sdbusplus::vtable::property_::const_,
+                                          [](const auto& x) { return x; });
+            dbusIface.register_property_r("UpdateReport", updateReport,
+                                          sdbusplus::vtable::property_::const_,
+                                          [](const auto& x) { return x; });
+        });
+}
diff --git a/src/trigger.hpp b/src/trigger.hpp
new file mode 100644
index 0000000..5092b6d
--- /dev/null
+++ b/src/trigger.hpp
@@ -0,0 +1,60 @@
+#pragma once
+
+#include "interfaces/trigger.hpp"
+#include "interfaces/trigger_manager.hpp"
+#include "interfaces/trigger_types.hpp"
+
+#include <boost/asio/io_context.hpp>
+#include <sdbusplus/asio/object_server.hpp>
+
+#include <memory>
+
+class Trigger : public interfaces::Trigger
+{
+  public:
+    Trigger(
+        boost::asio::io_context& ioc,
+        const std::shared_ptr<sdbusplus::asio::object_server>& objServer,
+        const std::string& name, const bool isDiscrete, const bool logToJournal,
+        const bool logToRedfish, const bool updateReport,
+        const std::vector<
+            std::pair<sdbusplus::message::object_path, std::string>>& sensorsIn,
+        const std::vector<std::string>& reportNames,
+        const TriggerThresholdParams& thresholds,
+        interfaces::TriggerManager& triggerManager);
+
+    Trigger(const Trigger&) = delete;
+    Trigger(Trigger&&) = delete;
+    Trigger& operator=(const Trigger&) = delete;
+    Trigger& operator=(Trigger&&) = delete;
+
+    std::string getName() const override
+    {
+        return name;
+    }
+
+    std::string getPath() const override
+    {
+        return path;
+    }
+
+  private:
+    const std::string name;
+    const std::string path;
+    bool persistent;
+    std::vector<std::pair<sdbusplus::message::object_path, std::string>>
+        sensors;
+    std::vector<std::string> reportNames;
+    TriggerThresholdParams thresholdParams;
+
+    std::unique_ptr<sdbusplus::asio::dbus_interface> deleteIface;
+    std::unique_ptr<sdbusplus::asio::dbus_interface> triggerIface;
+
+  public:
+    static constexpr const char* triggerIfaceName =
+        "xyz.openbmc_project.Telemetry.Trigger";
+    static constexpr const char* triggerDir =
+        "/xyz/openbmc_project/Telemetry/Triggers/";
+    static constexpr const char* deleteIfaceName =
+        "xyz.openbmc_project.Object.Delete";
+};
diff --git a/src/trigger_factory.cpp b/src/trigger_factory.cpp
new file mode 100644
index 0000000..0a45dec
--- /dev/null
+++ b/src/trigger_factory.cpp
@@ -0,0 +1,25 @@
+#include "trigger_factory.hpp"
+
+#include "trigger.hpp"
+
+TriggerFactory::TriggerFactory(
+    std::shared_ptr<sdbusplus::asio::connection> bus,
+    std::shared_ptr<sdbusplus::asio::object_server> objServer) :
+    bus(std::move(bus)),
+    objServer(std::move(objServer))
+{}
+
+std::unique_ptr<interfaces::Trigger> TriggerFactory::make(
+    const std::string& name, bool isDiscrete, bool logToJournal,
+    bool logToRedfish, bool updateReport,
+    const std::vector<std::pair<sdbusplus::message::object_path, std::string>>&
+        sensors,
+    const std::vector<std::string>& reportNames,
+    const TriggerThresholdParams& thresholdParams,
+    interfaces::TriggerManager& reportManager) const
+{
+    return std::make_unique<Trigger>(bus->get_io_context(), objServer, name,
+                                     isDiscrete, logToJournal, logToRedfish,
+                                     updateReport, sensors, reportNames,
+                                     thresholdParams, reportManager);
+}
diff --git a/src/trigger_factory.hpp b/src/trigger_factory.hpp
new file mode 100644
index 0000000..60a9fb4
--- /dev/null
+++ b/src/trigger_factory.hpp
@@ -0,0 +1,25 @@
+#pragma once
+
+#include "interfaces/trigger_factory.hpp"
+
+#include <sdbusplus/asio/object_server.hpp>
+
+class TriggerFactory : public interfaces::TriggerFactory
+{
+  public:
+    TriggerFactory(std::shared_ptr<sdbusplus::asio::connection> bus,
+                   std::shared_ptr<sdbusplus::asio::object_server> objServer);
+
+    std::unique_ptr<interfaces::Trigger> make(
+        const std::string& name, bool isDiscrete, bool logToJournal,
+        bool logToRedfish, bool updateReport,
+        const std::vector<
+            std::pair<sdbusplus::message::object_path, std::string>>& sensors,
+        const std::vector<std::string>& reportNames,
+        const TriggerThresholdParams& thresholdParams,
+        interfaces::TriggerManager& triggerManager) const override;
+
+  private:
+    std::shared_ptr<sdbusplus::asio::connection> bus;
+    std::shared_ptr<sdbusplus::asio::object_server> objServer;
+};
diff --git a/src/trigger_manager.cpp b/src/trigger_manager.cpp
new file mode 100644
index 0000000..cc164e4
--- /dev/null
+++ b/src/trigger_manager.cpp
@@ -0,0 +1,50 @@
+#include "trigger_manager.hpp"
+
+TriggerManager::TriggerManager(
+    std::unique_ptr<interfaces::TriggerFactory> triggerFactoryIn,
+    const std::shared_ptr<sdbusplus::asio::object_server>& objServer) :
+    triggerFactory(std::move(triggerFactoryIn))
+{
+    managerIface = objServer->add_unique_interface(
+        triggerManagerPath, triggerManagerIfaceName, [this](auto& iface) {
+            iface.register_method(
+                "AddTrigger",
+                [this](
+                    const std::string& name, bool isDiscrete, bool logToJournal,
+                    bool logToRedfish, bool updateReport,
+                    const std::vector<std::pair<sdbusplus::message::object_path,
+                                                std::string>>& sensors,
+                    const std::vector<std::string>& reportNames,
+                    const TriggerThresholdParams& thresholds) {
+                    if (triggers.size() >= maxTriggers)
+                    {
+                        throw sdbusplus::exception::SdBusError(
+                            static_cast<int>(std::errc::too_many_files_open),
+                            "Reached maximal trigger count");
+                    }
+
+                    for (const auto& trigger : triggers)
+                    {
+                        if (trigger->getName() == name)
+                        {
+                            throw sdbusplus::exception::SdBusError(
+                                static_cast<int>(std::errc::file_exists),
+                                "Duplicate trigger");
+                        }
+                    }
+
+                    triggers.emplace_back(triggerFactory->make(
+                        name, isDiscrete, logToJournal, logToRedfish,
+                        updateReport, sensors, reportNames, thresholds, *this));
+                    return triggers.back()->getPath();
+                });
+        });
+}
+
+void TriggerManager::removeTrigger(const interfaces::Trigger* trigger)
+{
+    triggers.erase(
+        std::remove_if(triggers.begin(), triggers.end(),
+                       [trigger](const auto& x) { return trigger == x.get(); }),
+        triggers.end());
+}
diff --git a/src/trigger_manager.hpp b/src/trigger_manager.hpp
new file mode 100644
index 0000000..b5b4a22
--- /dev/null
+++ b/src/trigger_manager.hpp
@@ -0,0 +1,36 @@
+#pragma once
+
+#include "interfaces/trigger_factory.hpp"
+#include "interfaces/trigger_manager.hpp"
+
+#include <sdbusplus/asio/object_server.hpp>
+
+#include <memory>
+#include <vector>
+
+class TriggerManager : public interfaces::TriggerManager
+{
+  public:
+    TriggerManager(
+        std::unique_ptr<interfaces::TriggerFactory> triggerFactory,
+        const std::shared_ptr<sdbusplus::asio::object_server>& objServer);
+
+    TriggerManager(TriggerManager&) = delete;
+    TriggerManager(TriggerManager&&) = delete;
+    TriggerManager& operator=(TriggerManager&) = delete;
+    TriggerManager& operator=(TriggerManager&&) = delete;
+
+    void removeTrigger(const interfaces::Trigger* trigger) override;
+
+  private:
+    std::unique_ptr<interfaces::TriggerFactory> triggerFactory;
+    std::unique_ptr<sdbusplus::asio::dbus_interface> managerIface;
+    std::vector<std::unique_ptr<interfaces::Trigger>> triggers;
+
+  public:
+    static constexpr size_t maxTriggers{TELEMETRY_MAX_TRIGGERS};
+    static constexpr const char* triggerManagerIfaceName =
+        "xyz.openbmc_project.Telemetry.TriggerManager";
+    static constexpr const char* triggerManagerPath =
+        "/xyz/openbmc_project/Telemetry/Triggers";
+};
diff --git a/tests/meson.build b/tests/meson.build
index 54d79e2..509d67b 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -30,6 +30,8 @@
             '../src/report_manager.cpp',
             '../src/sensor.cpp',
             '../src/sensor_cache.cpp',
+            '../src/trigger.cpp',
+            '../src/trigger_manager.cpp',
             'src/dbus_environment.cpp',
             'src/main.cpp',
             'src/stubs/dbus_sensor_object.cpp',
@@ -41,6 +43,8 @@
             'src/test_sensor.cpp',
             'src/test_sensor_cache.cpp',
             'src/test_transform.cpp',
+            'src/test_trigger.cpp',
+            'src/test_trigger_manager.cpp',
             'src/test_unique_call.cpp',
             'src/utils/generate_unique_mock_id.cpp',
         ],
diff --git a/tests/src/mocks/report_mock.hpp b/tests/src/mocks/report_mock.hpp
index 2f6ffac..f3eafba 100644
--- a/tests/src/mocks/report_mock.hpp
+++ b/tests/src/mocks/report_mock.hpp
@@ -7,17 +7,12 @@
 class ReportMock : public interfaces::Report
 {
   public:
-    ReportMock(std::string reportName)
+    ReportMock(std::string name)
     {
         using namespace testing;
 
-        ON_CALL(*this, getName).WillByDefault([reportName] {
-            return reportName;
-        });
-        ON_CALL(*this, getPath).WillByDefault([reportName] {
-            return "/" + reportName;
-        });
-
+        ON_CALL(*this, getName).WillByDefault([name] { return name; });
+        ON_CALL(*this, getPath).WillByDefault([name] { return "/" + name; });
         EXPECT_CALL(*this, Die).Times(AnyNumber());
     }
 
diff --git a/tests/src/mocks/trigger_factory_mock.hpp b/tests/src/mocks/trigger_factory_mock.hpp
new file mode 100644
index 0000000..b714a44
--- /dev/null
+++ b/tests/src/mocks/trigger_factory_mock.hpp
@@ -0,0 +1,51 @@
+#pragma once
+
+#include "interfaces/trigger_factory.hpp"
+#include "mocks/trigger_mock.hpp"
+#include "params/trigger_params.hpp"
+
+#include <gmock/gmock.h>
+
+class TriggerFactoryMock : public interfaces::TriggerFactory
+{
+  public:
+    TriggerFactoryMock()
+    {
+        using namespace testing;
+
+        ON_CALL(*this, make(_, _, _, _, _, _, _, _, _))
+            .WillByDefault(WithArgs<0>(Invoke([](const std::string& name) {
+                return std::make_unique<NiceMock<TriggerMock>>(name);
+            })));
+    }
+
+    MOCK_METHOD(std::unique_ptr<interfaces::Trigger>, make,
+                (const std::string& name, bool isDiscrete, bool logToJournal,
+                 bool logToRedfish, bool updateReport,
+                 (const std::vector<std::pair<sdbusplus::message::object_path,
+                                              std::string>>& sensors),
+                 const std::vector<std::string>& reportNames,
+                 const TriggerThresholdParams& thresholds,
+                 interfaces::TriggerManager& triggerManager),
+                (const, override));
+
+    auto& expectMake(
+        std::optional<std::reference_wrapper<const TriggerParams>> paramsOpt,
+        const testing::Matcher<interfaces::TriggerManager&>& tm)
+    {
+        if (paramsOpt)
+        {
+            const TriggerParams& params = *paramsOpt;
+            return EXPECT_CALL(
+                *this, make(params.name(), params.isDiscrete(),
+                            params.logToJournal(), params.logToRedfish(),
+                            params.updateReport(), params.sensors(),
+                            params.reportNames(), params.thresholds(), tm));
+        }
+        else
+        {
+            using testing::_;
+            return EXPECT_CALL(*this, make(_, _, _, _, _, _, _, _, tm));
+        }
+    }
+};
diff --git a/tests/src/mocks/trigger_manager_mock.hpp b/tests/src/mocks/trigger_manager_mock.hpp
new file mode 100644
index 0000000..9377d2a
--- /dev/null
+++ b/tests/src/mocks/trigger_manager_mock.hpp
@@ -0,0 +1,12 @@
+#pragma once
+
+#include "interfaces/trigger_manager.hpp"
+
+#include <gmock/gmock.h>
+
+class TriggerManagerMock : public interfaces::TriggerManager
+{
+  public:
+    MOCK_METHOD(void, removeTrigger, (const interfaces::Trigger* trigger),
+                (override));
+};
diff --git a/tests/src/mocks/trigger_mock.hpp b/tests/src/mocks/trigger_mock.hpp
new file mode 100644
index 0000000..d8eddd2
--- /dev/null
+++ b/tests/src/mocks/trigger_mock.hpp
@@ -0,0 +1,27 @@
+#pragma once
+
+#include "interfaces/trigger.hpp"
+
+#include <gmock/gmock.h>
+
+class TriggerMock : public interfaces::Trigger
+{
+  public:
+    TriggerMock(std::string name)
+    {
+        using namespace testing;
+
+        ON_CALL(*this, getName).WillByDefault([name] { return name; });
+        ON_CALL(*this, getPath).WillByDefault([name] { return "/" + name; });
+        EXPECT_CALL(*this, Die).Times(AnyNumber());
+    }
+
+    virtual ~TriggerMock()
+    {
+        Die();
+    }
+
+    MOCK_METHOD(std::string, getName, (), (const, override));
+    MOCK_METHOD(std::string, getPath, (), (const, override));
+    MOCK_METHOD(void, Die, ());
+};
diff --git a/tests/src/params/trigger_params.hpp b/tests/src/params/trigger_params.hpp
new file mode 100644
index 0000000..340f9b3
--- /dev/null
+++ b/tests/src/params/trigger_params.hpp
@@ -0,0 +1,67 @@
+#pragma once
+
+#include "interfaces/trigger_types.hpp"
+
+#include <utility>
+
+class TriggerParams
+{
+  public:
+    TriggerParams& name(std::string val)
+    {
+        nameProperty = std::move(val);
+        return *this;
+    }
+
+    const std::string& name() const
+    {
+        return nameProperty;
+    }
+
+    bool isDiscrete() const
+    {
+        return discreteProperty;
+    }
+
+    bool logToJournal() const
+    {
+        return logToJournalProperty;
+    }
+
+    bool logToRedfish() const
+    {
+        return logToRedfishProperty;
+    }
+
+    bool updateReport() const
+    {
+        return updateReportProperty;
+    }
+
+    const std::vector<std::pair<sdbusplus::message::object_path, std::string>>&
+        sensors() const
+    {
+        return sensorsProperty;
+    }
+
+    const std::vector<std::string>& reportNames() const
+    {
+        return reportNamesProperty;
+    }
+
+    const TriggerThresholdParams& thresholds() const
+    {
+        return thresholdsProperty;
+    }
+
+  private:
+    std::string nameProperty = "Trigger1";
+    bool discreteProperty = false;
+    bool logToJournalProperty = false;
+    bool logToRedfishProperty = false;
+    bool updateReportProperty = false;
+    std::vector<std::pair<sdbusplus::message::object_path, std::string>>
+        sensorsProperty = {};
+    std::vector<std::string> reportNamesProperty = {};
+    TriggerThresholdParams thresholdsProperty = {};
+};
diff --git a/tests/src/test_trigger.cpp b/tests/src/test_trigger.cpp
new file mode 100644
index 0000000..0f2d752
--- /dev/null
+++ b/tests/src/test_trigger.cpp
@@ -0,0 +1,91 @@
+#include "dbus_environment.hpp"
+#include "helpers.hpp"
+#include "mocks/trigger_manager_mock.hpp"
+#include "params/trigger_params.hpp"
+#include "trigger.hpp"
+#include "utils/set_exception.hpp"
+
+using namespace testing;
+using namespace std::literals::string_literals;
+
+class TestTrigger : public Test
+{
+  public:
+    TriggerParams triggerParams;
+
+    std::unique_ptr<TriggerManagerMock> triggerManagerMockPtr =
+        std::make_unique<NiceMock<TriggerManagerMock>>();
+    std::unique_ptr<Trigger> sut;
+
+    void SetUp() override
+    {
+        sut = std::make_unique<Trigger>(
+            DbusEnvironment::getIoc(), DbusEnvironment::getObjServer(),
+            triggerParams.name(), triggerParams.isDiscrete(),
+            triggerParams.logToJournal(), triggerParams.logToRedfish(),
+            triggerParams.updateReport(), triggerParams.sensors(),
+            triggerParams.reportNames(), triggerParams.thresholds(),
+            *triggerManagerMockPtr);
+    }
+
+    template <class T>
+    static T getProperty(const std::string& path, const std::string& property)
+    {
+        std::promise<T> propertyPromise;
+        sdbusplus::asio::getProperty<T>(
+            *DbusEnvironment::getBus(), DbusEnvironment::serviceName(), path,
+            Trigger::triggerIfaceName, property,
+            [&propertyPromise](boost::system::error_code) {
+                utils::setException(propertyPromise, "GetProperty failed");
+            },
+            [&propertyPromise](T t) { propertyPromise.set_value(t); });
+        return DbusEnvironment::waitForFuture(propertyPromise.get_future());
+    }
+
+    boost::system::error_code deleteTrigger(const std::string& path)
+    {
+        std::promise<boost::system::error_code> methodPromise;
+        DbusEnvironment::getBus()->async_method_call(
+            [&methodPromise](boost::system::error_code ec) {
+                methodPromise.set_value(ec);
+            },
+            DbusEnvironment::serviceName(), path, Trigger::deleteIfaceName,
+            "Delete");
+        return DbusEnvironment::waitForFuture(methodPromise.get_future());
+    }
+};
+
+TEST_F(TestTrigger, checkIfPropertiesAreSet)
+{
+    EXPECT_THAT(getProperty<bool>(sut->getPath(), "Discrete"),
+                Eq(triggerParams.isDiscrete()));
+    EXPECT_THAT(getProperty<bool>(sut->getPath(), "LogToJournal"),
+                Eq(triggerParams.logToJournal()));
+    EXPECT_THAT(getProperty<bool>(sut->getPath(), "LogToRedfish"),
+                Eq(triggerParams.logToRedfish()));
+    EXPECT_THAT(getProperty<bool>(sut->getPath(), "UpdateReport"),
+                Eq(triggerParams.updateReport()));
+    EXPECT_THAT((getProperty<std::vector<
+                     std::pair<sdbusplus::message::object_path, std::string>>>(
+                    sut->getPath(), "Sensors")),
+                Eq(triggerParams.sensors()));
+    EXPECT_THAT(
+        getProperty<std::vector<std::string>>(sut->getPath(), "ReportNames"),
+        Eq(triggerParams.reportNames()));
+    EXPECT_THAT(
+        getProperty<TriggerThresholdParams>(sut->getPath(), "Thresholds"),
+        Eq(triggerParams.thresholds()));
+}
+
+TEST_F(TestTrigger, deleteTrigger)
+{
+    EXPECT_CALL(*triggerManagerMockPtr, removeTrigger(sut.get()));
+    auto ec = deleteTrigger(sut->getPath());
+    EXPECT_THAT(ec, Eq(boost::system::errc::success));
+}
+
+TEST_F(TestTrigger, deletingNonExistingTriggerReturnInvalidRequestDescriptor)
+{
+    auto ec = deleteTrigger(Trigger::triggerDir + "NonExisting"s);
+    EXPECT_THAT(ec.value(), Eq(EBADR));
+}
diff --git a/tests/src/test_trigger_manager.cpp b/tests/src/test_trigger_manager.cpp
new file mode 100644
index 0000000..71314fa
--- /dev/null
+++ b/tests/src/test_trigger_manager.cpp
@@ -0,0 +1,127 @@
+#include "dbus_environment.hpp"
+#include "helpers.hpp"
+#include "mocks/trigger_factory_mock.hpp"
+#include "mocks/trigger_mock.hpp"
+#include "params/trigger_params.hpp"
+#include "trigger_manager.hpp"
+
+using namespace testing;
+
+class TestTriggerManager : public Test
+{
+  public:
+    std::pair<boost::system::error_code, std::string>
+        addTrigger(const TriggerParams& params)
+    {
+        std::promise<std::pair<boost::system::error_code, std::string>>
+            addTriggerPromise;
+        DbusEnvironment::getBus()->async_method_call(
+            [&addTriggerPromise](boost::system::error_code ec,
+                                 const std::string& path) {
+                addTriggerPromise.set_value({ec, path});
+            },
+            DbusEnvironment::serviceName(), TriggerManager::triggerManagerPath,
+            TriggerManager::triggerManagerIfaceName, "AddTrigger",
+            params.name(), params.isDiscrete(), params.logToJournal(),
+            params.logToRedfish(), params.updateReport(), params.sensors(),
+            params.reportNames(), params.thresholds());
+        return DbusEnvironment::waitForFuture(addTriggerPromise.get_future());
+    }
+
+    TriggerParams triggerParams;
+    std::unique_ptr<TriggerFactoryMock> triggerFactoryMockPtr =
+        std::make_unique<NiceMock<TriggerFactoryMock>>();
+    TriggerFactoryMock& triggerFactoryMock = *triggerFactoryMockPtr;
+    std::unique_ptr<TriggerMock> triggerMockPtr =
+        std::make_unique<NiceMock<TriggerMock>>(triggerParams.name());
+    TriggerMock& triggerMock = *triggerMockPtr;
+    std::unique_ptr<TriggerManager> sut = std::make_unique<TriggerManager>(
+        std::move(triggerFactoryMockPtr),
+        std::move(DbusEnvironment::getObjServer()));
+    MockFunction<void(std::string)> checkPoint;
+};
+
+TEST_F(TestTriggerManager, addTrigger)
+{
+    triggerFactoryMock.expectMake(triggerParams, Ref(*sut))
+        .WillOnce(Return(ByMove(std::move(triggerMockPtr))));
+
+    auto [ec, path] = addTrigger(triggerParams);
+    EXPECT_THAT(ec.value(), Eq(boost::system::errc::success));
+    EXPECT_THAT(path, Eq(triggerMock.getPath()));
+}
+
+TEST_F(TestTriggerManager, failToAddTriggerTwice)
+{
+    triggerFactoryMock.expectMake(triggerParams, Ref(*sut))
+        .WillOnce(Return(ByMove(std::move(triggerMockPtr))));
+
+    addTrigger(triggerParams);
+
+    auto [ec, path] = addTrigger(triggerParams);
+    EXPECT_THAT(ec.value(), Eq(boost::system::errc::file_exists));
+    EXPECT_THAT(path, Eq(std::string()));
+}
+
+TEST_F(TestTriggerManager, failToAddTriggerWhenMaxTriggerIsReached)
+{
+    triggerFactoryMock.expectMake(std::nullopt, Ref(*sut))
+        .Times(TriggerManager::maxTriggers);
+
+    for (size_t i = 0; i < TriggerManager::maxTriggers; i++)
+    {
+        triggerParams.name(triggerParams.name() + std::to_string(i));
+
+        auto [ec, path] = addTrigger(triggerParams);
+        EXPECT_THAT(ec.value(), Eq(boost::system::errc::success));
+    }
+
+    triggerParams.name(triggerParams.name() +
+                       std::to_string(TriggerManager::maxTriggers));
+    auto [ec, path] = addTrigger(triggerParams);
+    EXPECT_THAT(ec.value(), Eq(boost::system::errc::too_many_files_open));
+    EXPECT_THAT(path, Eq(std::string()));
+}
+
+TEST_F(TestTriggerManager, removeTrigger)
+{
+    {
+        InSequence seq;
+        triggerFactoryMock.expectMake(triggerParams, Ref(*sut))
+            .WillOnce(Return(ByMove(std::move(triggerMockPtr))));
+        EXPECT_CALL(triggerMock, Die());
+        EXPECT_CALL(checkPoint, Call("end"));
+    }
+
+    addTrigger(triggerParams);
+    sut->removeTrigger(&triggerMock);
+    checkPoint.Call("end");
+}
+
+TEST_F(TestTriggerManager, removingTriggerThatIsNotInContainerHasNoEffect)
+{
+    {
+        InSequence seq;
+        EXPECT_CALL(checkPoint, Call("end"));
+        EXPECT_CALL(triggerMock, Die());
+    }
+
+    sut->removeTrigger(&triggerMock);
+    checkPoint.Call("end");
+}
+
+TEST_F(TestTriggerManager, removingSameTriggerTwiceHasNoSideEffect)
+{
+    {
+        InSequence seq;
+        triggerFactoryMock.expectMake(triggerParams, Ref(*sut))
+            .WillOnce(Return(ByMove(std::move(triggerMockPtr))));
+        EXPECT_CALL(triggerMock, Die());
+        EXPECT_CALL(checkPoint, Call("end"));
+    }
+
+    addTrigger(triggerParams);
+    sut->removeTrigger(&triggerMock);
+    sut->removeTrigger(&triggerMock);
+    checkPoint.Call("end");
+}