config: error on invalid configuration

Since the default led priority is no longer 'Blink', the priority now
has to be explicitly defined when using either group priority or led
priority.

If a configuration does not define the priority, the configuration is
invalid and in the yaml case, phosphor-led-manager should fail to build,
in the json case, the process should exit due to the configuration
error.

The config validation has been extracted into it's own file and made
separate from json config parsing.

So every config will go through the same validation even if its been
created via yaml.

Change-Id: Ifda65942b0768d6c0d3b25076f7a1236b46b3d9f
Signed-off-by: Alexander Hansen <alexander.hansen@9elements.com>
diff --git a/test/config/led-group-config-malformed.json b/test/config/led-group-config-malformed.json
deleted file mode 100644
index 3e7d668..0000000
--- a/test/config/led-group-config-malformed.json
+++ /dev/null
@@ -1,40 +0,0 @@
-{
-    "leds": [
-        {
-            "group": "bmc_booted"
-            "members": [
-                {
-                    "Name": "heartbeat",
-                    "Action": "On"
-                }
-            ]
-        },
-        {
-            "group": "power_on",
-            "members": [
-                {
-                    "Name": "power",
-                    "Action": "On",
-                    "Priority": "On"
-                }
-            ]
-        },
-        {
-            "group": "enclosure_identify",
-            "members": [
-                {
-                    "Name": "front_id",
-                    "Action": "Blink",
-                    "DutyOn": 50,
-                    "Period": 1000
-                },
-                {
-                    "Name": "rear_id",
-                    "Action": "Blink",
-                    "DutyOn": 50,
-                    "Period": 1000
-                }
-            ]
-        }
-    ]
-}
\ No newline at end of file
diff --git a/test/config/led-group-config.json b/test/config/led-group-config.json
index 0a4ccb9..f2ce5d7 100644
--- a/test/config/led-group-config.json
+++ b/test/config/led-group-config.json
@@ -5,7 +5,8 @@
             "members": [
                 {
                     "Name": "heartbeat",
-                    "Action": "On"
+                    "Action": "On",
+                    "Priority": "Blink"
                 }
             ]
         },
@@ -36,13 +37,15 @@
                     "Name": "front_id",
                     "Action": "Blink",
                     "DutyOn": 50,
-                    "Period": 1000
+                    "Period": 1000,
+                    "Priority": "Blink"
                 },
                 {
                     "Name": "rear_id",
                     "Action": "Blink",
                     "DutyOn": 50,
-                    "Period": 1000
+                    "Period": 1000,
+                    "Priority": "Blink"
                 }
             ]
         }
diff --git a/test/config/test-group-priority.yaml b/test/config/test-group-priority.yaml
index ca5005f..1acfbb6 100644
--- a/test/config/test-group-priority.yaml
+++ b/test/config/test-group-priority.yaml
@@ -1,10 +1,13 @@
 group1:
+    Priority: 1
     led1:
         Action: "On"
 group2:
+    Priority: 2
     led1:
         Action: "Off"
 group3:
+    Priority: 3
     led1:
         Action: "Blink"
         DutyOn: 50
diff --git a/test/meson.build b/test/meson.build
index 010f7f4..4aa6632 100644
--- a/test/meson.build
+++ b/test/meson.build
@@ -26,6 +26,7 @@
 
 test_sources = [
   '../manager/manager.cpp',
+  '../manager/config-validator.cpp',
   '../utils.cpp',
 ]
 
@@ -50,6 +51,7 @@
   'utest-group-priority.cpp',
   'utest-led-yaml-group-priority.cpp',
   'utest-led-yaml-led-priority.cpp',
+  'utest-config-validator.cpp'
 ]
 if get_option('persistent-led-asserted').allowed()
   test_sources += [
diff --git a/test/utest-config-validator.cpp b/test/utest-config-validator.cpp
new file mode 100644
index 0000000..1eda517
--- /dev/null
+++ b/test/utest-config-validator.cpp
@@ -0,0 +1,143 @@
+#include "config-validator.hpp"
+#include "grouplayout.hpp"
+#include "ledlayout.hpp"
+
+#include <gtest/gtest.h>
+
+using namespace phosphor::led;
+
+static void assertValidationException(const GroupMap& ledMap,
+                                      error::ConfigValidationError err)
+{
+    try
+    {
+        validateConfigV1(ledMap);
+        ASSERT_FALSE(true);
+    }
+    catch (ConfigValidationException& e)
+    {
+        ASSERT_EQ(e.reason, err);
+    }
+}
+
+TEST(validateConfig, testGoodPathLedPriority)
+{
+    ActionSet group1ActionSet = {
+        {"led1", Layout::Action::On, 0, 0, Layout::Action::On},
+        {"led2", Layout::Action::On, 0, 0, Layout::Action::Blink},
+    };
+    Layout::GroupLayout group1 = {0, group1ActionSet};
+    GroupMap ledMap = {{"group1", group1}};
+
+    validateConfigV1(ledMap);
+}
+
+TEST(validateConfig, testGoodPathGroupPriority)
+{
+    ActionSet group1ActionSet = {
+        {"led1", Layout::Action::On, 0, 0, std::nullopt},
+        {"led2", Layout::Action::On, 0, 0, std::nullopt},
+    };
+    ActionSet group2ActionSet = {
+        {"led1", Layout::Action::On, 0, 0, std::nullopt},
+        {"led2", Layout::Action::On, 0, 0, std::nullopt},
+    };
+    Layout::GroupLayout group1 = {1, group1ActionSet};
+    Layout::GroupLayout group2 = {2, group1ActionSet};
+    GroupMap ledMap = {
+        {"group1", group1},
+        {"group2", group2},
+    };
+
+    validateConfigV1(ledMap);
+}
+
+TEST(validateConfig, testLedPriorityMismatch)
+{
+    ActionSet group1ActionSet = {
+        {"led1", Layout::Action::On, 0, 0, Layout::Action::On},
+    };
+    ActionSet group2ActionSet = {
+        {"led1", Layout::Action::On, 0, 0, Layout::Action::Off},
+    };
+    Layout::GroupLayout group1 = {0, group1ActionSet};
+    Layout::GroupLayout group2 = {0, group2ActionSet};
+    GroupMap ledMap = {
+        {"group1", group1},
+        {"group2", group2},
+    };
+
+    assertValidationException(ledMap, error::LedPriorityMismatch);
+}
+
+TEST(validateConfig, testMissingLedPriority)
+{
+    ActionSet group1ActionSet = {
+        {"led1", Layout::Action::On, 0, 0, Layout::Action::On},
+    };
+    ActionSet group2ActionSet = {
+        {"led1", Layout::Action::On, 0, 0, std::nullopt},
+    };
+    Layout::GroupLayout group1 = {0, group1ActionSet};
+    Layout::GroupLayout group2 = {0, group2ActionSet};
+    GroupMap ledMap = {
+        {"group1", group1},
+        {"group2", group2},
+    };
+
+    assertValidationException(ledMap, error::MissingLedPriority);
+}
+
+TEST(validateConfig, testMixedLedAndGroupPriority)
+{
+    ActionSet group1ActionSet = {
+        {"led1", Layout::Action::On, 0, 0, Layout::Action::On},
+    };
+    ActionSet group2ActionSet = {
+        {"led1", Layout::Action::On, 0, 0, Layout::Action::On},
+    };
+    Layout::GroupLayout group1 = {0, group1ActionSet};
+    Layout::GroupLayout group2 = {1, group2ActionSet};
+    GroupMap ledMap = {
+        {"group1", group1},
+        {"group2", group2},
+    };
+
+    assertValidationException(ledMap, error::MixedLedAndGroupPriority);
+}
+
+TEST(validateConfig, testInvalidGroupPriority)
+{
+    ActionSet group1ActionSet = {
+        {"led1", Layout::Action::On, 0, 0, std::nullopt},
+    };
+    ActionSet group2ActionSet = {
+        {"led1", Layout::Action::On, 0, 0, std::nullopt},
+    };
+    Layout::GroupLayout group1 = {0, group1ActionSet};
+    Layout::GroupLayout group2 = {1, group2ActionSet};
+    GroupMap ledMap = {
+        {"group1", group1},
+        {"group2", group2},
+    };
+
+    assertValidationException(ledMap, error::InvalidGroupPriority);
+}
+
+TEST(validateConfig, testDuplicateGroupPriority)
+{
+    ActionSet group1ActionSet = {
+        {"led1", Layout::Action::On, 0, 0, std::nullopt},
+    };
+    ActionSet group2ActionSet = {
+        {"led1", Layout::Action::On, 0, 0, std::nullopt},
+    };
+    Layout::GroupLayout group1 = {1, group1ActionSet};
+    Layout::GroupLayout group2 = {1, group2ActionSet};
+    GroupMap ledMap = {
+        {"group1", group1},
+        {"group2", group2},
+    };
+
+    assertValidationException(ledMap, error::DuplicateGroupPriority);
+}
diff --git a/test/utest-led-json.cpp b/test/utest-led-json.cpp
index fa9161b..318c9bc 100644
--- a/test/utest-led-json.cpp
+++ b/test/utest-led-json.cpp
@@ -29,7 +29,7 @@
         ASSERT_EQ(group.action, phosphor::led::Layout::Action::On);
         ASSERT_EQ(group.dutyOn, 50);
         ASSERT_EQ(group.period, 0);
-        ASSERT_EQ(group.priority, std::nullopt);
+        ASSERT_EQ(group.priority, phosphor::led::Layout::Action::Blink);
     }
 
     for (const auto& group : powerOnActions)
@@ -54,14 +54,14 @@
             ASSERT_EQ(group.action, phosphor::led::Layout::Action::Blink);
             ASSERT_EQ(group.dutyOn, 50);
             ASSERT_EQ(group.period, 1000);
-            ASSERT_EQ(group.priority, std::nullopt);
+            ASSERT_EQ(group.priority, phosphor::led::Layout::Action::Blink);
         }
         else if (group.name == "rear_id")
         {
             ASSERT_EQ(group.action, phosphor::led::Layout::Action::Blink);
             ASSERT_EQ(group.dutyOn, 50);
             ASSERT_EQ(group.period, 1000);
-            ASSERT_EQ(group.priority, std::nullopt);
+            ASSERT_EQ(group.priority, phosphor::led::Layout::Action::Blink);
         }
         else
         {
@@ -69,35 +69,3 @@
         }
     }
 }
-
-TEST(loadJsonConfig, testBadPath)
-{
-    static constexpr auto jsonPath = "config/led-group-config-malformed.json";
-    ASSERT_THROW(loadJsonConfig(jsonPath), std::exception);
-}
-
-TEST(validatePriority, testGoodPriority)
-{
-    PriorityMap priorityMap{};
-    validatePriority("heartbeat", phosphor::led::Layout::Action::Blink,
-                     priorityMap);
-    validatePriority("power", phosphor::led::Layout::Action::On, priorityMap);
-
-    ASSERT_EQ(priorityMap.at("heartbeat"),
-              phosphor::led::Layout::Action::Blink);
-    ASSERT_EQ(priorityMap.at("power"), phosphor::led::Layout::Action::On);
-}
-
-TEST(validatePriority, testBadPriority)
-{
-    PriorityMap priorityMap{};
-    validatePriority("heartbeat", phosphor::led::Layout::Action::Blink,
-                     priorityMap);
-
-    ASSERT_EQ(priorityMap.at("heartbeat"),
-              phosphor::led::Layout::Action::Blink);
-    ASSERT_THROW(validatePriority("heartbeat",
-                                  phosphor::led::Layout::Action::On,
-                                  priorityMap),
-                 std::runtime_error);
-}
diff --git a/test/utest-led-yaml-group-priority.cpp b/test/utest-led-yaml-group-priority.cpp
index e48a199..b257c17 100644
--- a/test/utest-led-yaml-group-priority.cpp
+++ b/test/utest-led-yaml-group-priority.cpp
@@ -26,13 +26,14 @@
 
     phosphor::led::Layout::GroupLayout group = systemLedMap.at(groupPath);
 
-    EXPECT_EQ(group.priority, 0);
+    EXPECT_EQ(group.priority, 1);
     EXPECT_EQ(group.actionSet.size(), 1);
 
     for (auto& led : group.actionSet)
     {
         EXPECT_EQ(led.name, "led1");
         EXPECT_EQ(led.action, Action::On);
+        EXPECT_EQ(led.priority, std::nullopt);
     }
 }
 
@@ -43,13 +44,14 @@
 
     phosphor::led::Layout::GroupLayout group = systemLedMap.at(groupPath);
 
-    EXPECT_EQ(group.priority, 0);
+    EXPECT_EQ(group.priority, 2);
     EXPECT_EQ(group.actionSet.size(), 1);
 
     for (auto& led : group.actionSet)
     {
         EXPECT_EQ(led.name, "led1");
         EXPECT_EQ(led.action, Action::Off);
+        EXPECT_EQ(led.priority, std::nullopt);
     }
 }
 
@@ -60,7 +62,7 @@
 
     phosphor::led::Layout::GroupLayout group = systemLedMap.at(groupPath);
 
-    EXPECT_EQ(group.priority, 0);
+    EXPECT_EQ(group.priority, 3);
     EXPECT_EQ(group.actionSet.size(), 1);
 
     for (auto& led : group.actionSet)
@@ -69,6 +71,7 @@
         EXPECT_EQ(led.action, Action::Blink);
         EXPECT_EQ(led.dutyOn, 50);
         EXPECT_EQ(led.period, 1000);
+        EXPECT_EQ(led.priority, std::nullopt);
     }
 }
 
@@ -88,11 +91,13 @@
         if (led.name == "led1")
         {
             EXPECT_EQ(led.action, Action::On);
+            EXPECT_EQ(led.priority, std::nullopt);
             found++;
         }
         if (led.name == "led2")
         {
             EXPECT_EQ(led.action, Action::Off);
+            EXPECT_EQ(led.priority, std::nullopt);
             found++;
         }
     }