bmc: provide disabled json support

Provide support such that a json configuration file can define a firmware
handler and its behavior.  This is not yet enabled.

Signed-off-by: Patrick Venture <venture@google.com>
Change-Id: If21f4e97951b3bb91a8afc53bee493d8600a04b7
diff --git a/bmc/test/Makefile.am b/bmc/test/Makefile.am
index feaee78..a4d79f8 100644
--- a/bmc/test/Makefile.am
+++ b/bmc/test/Makefile.am
@@ -41,7 +41,8 @@
 	firmware_state_updatestarted_unittest \
 	firmware_state_updatecompleted_unittest \
 	firmware_state_notyetstarted_tarball_unittest \
-	firmware_multiplebundle_unittest
+	firmware_multiplebundle_unittest \
+	firmware_json_unittest
 
 TESTS = $(check_PROGRAMS)
 
@@ -104,3 +105,6 @@
 
 firmware_multiplebundle_unittest_SOURCES = firmware_multiplebundle_unittest.cpp
 firmware_multiplebundle_unittest_LDADD = $(top_builddir)/bmc/libfirmwareblob_common.la
+
+firmware_json_unittest_SOURCES = firmware_json_unittest.cpp
+firmware_json_unittest_LDADD = $(top_builddir)/bmc/libfirmwareblob_common.la
diff --git a/bmc/test/firmware_json_unittest.cpp b/bmc/test/firmware_json_unittest.cpp
new file mode 100644
index 0000000..704243b
--- /dev/null
+++ b/bmc/test/firmware_json_unittest.cpp
@@ -0,0 +1,429 @@
+#include "buildjson.hpp"
+
+#include <nlohmann/json.hpp>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+namespace ipmi_flash
+{
+namespace
+{
+using ::testing::IsEmpty;
+
+using json = nlohmann::json;
+
+TEST(FirmwareJsonTest, InvalidHandlerType)
+{
+    auto j2 = R"(
+        [{
+            "blob" : "/flash/image",
+            "handler" : {
+                "type" : "unsupported",
+                "path" : "/run/initramfs/bmc-image"
+            },
+            "actions" : {
+                "preparation" : {
+                    "type" : "systemd",
+                    "unit" : "phosphor-ipmi-flash-bmc-prepare.target"
+                },
+                "verification" : {
+                    "type" : "fileSystemdVerify",
+                    "unit" : "phosphor-ipmi-flash-bmc-verify.target",
+                    "path" : "/tmp/bmc.verify"
+                },
+                "update" : {
+                    "type" : "reboot"
+                }
+            }
+         }]
+    )"_json;
+
+    EXPECT_THAT(buildHandlerFromJson(j2), IsEmpty());
+}
+
+TEST(FirmwareJsonTest, InvalidPreparationType)
+{
+    auto j2 = R"(
+        [{
+            "blob" : "/flash/image",
+            "handler" : {
+                "type" : "file",
+                "path" : "/run/initramfs/bmc-image"
+            },
+            "actions" : {
+                "preparation" : {
+                    "type" : "superfun",
+                    "unit" : "phosphor-ipmi-flash-bmc-prepare.target"
+                },
+                "verification" : {
+                    "type" : "fileSystemdVerify",
+                    "unit" : "phosphor-ipmi-flash-bmc-verify.target",
+                    "path" : "/tmp/bmc.verify"
+                },
+                "update" : {
+                    "type" : "reboot"
+                }
+            }
+         }]
+    )"_json;
+
+    EXPECT_THAT(buildHandlerFromJson(j2), IsEmpty());
+}
+
+TEST(FirmwareJsonTest, InvalidVerificationType)
+{
+    auto j2 = R"(
+        [{
+            "blob" : "/flash/image",
+            "handler" : {
+                "type" : "file",
+                "path" : "/run/initramfs/bmc-image"
+            },
+            "actions" : {
+                "preparation" : {
+                    "type" : "systemd",
+                    "unit" : "phosphor-ipmi-flash-bmc-prepare.target"
+                },
+                "verification" : {
+                    "type" : "funtimes",
+                    "unit" : "phosphor-ipmi-flash-bmc-verify.target",
+                    "path" : "/tmp/bmc.verify"
+                },
+                "update" : {
+                    "type" : "reboot"
+                }
+            }
+         }]
+    )"_json;
+
+    EXPECT_THAT(buildHandlerFromJson(j2), IsEmpty());
+}
+
+TEST(FirmwareJsonTest, InvalidUpdateType)
+{
+    auto j2 = R"(
+        [{
+            "blob" : "/flash/image",
+            "handler" : {
+                "type" : "file",
+                "path" : "/run/initramfs/bmc-image"
+            },
+            "actions" : {
+                "preparation" : {
+                    "type" : "systemd",
+                    "unit" : "phosphor-ipmi-flash-bmc-prepare.target"
+                },
+                "verification" : {
+                    "type" : "fileSystemdVerify",
+                    "unit" : "phosphor-ipmi-flash-bmc-verify.target",
+                    "path" : "/tmp/bmc.verify"
+                },
+                "update" : {
+                    "type" : "systemd"
+                }
+            }
+         }]
+    )"_json;
+
+    EXPECT_THAT(buildHandlerFromJson(j2), IsEmpty());
+}
+
+TEST(FirmwareJsonTest, MissingHandler)
+{
+    auto j2 = R"(
+        [{
+            "blob" : "/flash/image",
+            "actions" : {
+                "preparation" : {
+                    "type" : "systemd",
+                    "unit" : "phosphor-ipmi-flash-bmc-prepare.target"
+                },
+                "verification" : {
+                    "type" : "fileSystemdVerify",
+                    "unit" : "phosphor-ipmi-flash-bmc-verify.target",
+                    "path" : "/tmp/bmc.verify"
+                },
+                "update" : {
+                    "type" : "reboot"
+                }
+            }
+        }]
+    )"_json;
+
+    EXPECT_THAT(buildHandlerFromJson(j2), IsEmpty());
+}
+
+TEST(FirmwareJsonTest, MissingActions)
+{
+    auto j2 = R"(
+        [{
+            "blob" : "/flash/image",
+            "handler" : {
+                "type" : "file",
+                "path" : "/run/initramfs/bmc-image"
+            }
+        }]
+    )"_json;
+
+    EXPECT_THAT(buildHandlerFromJson(j2), IsEmpty());
+}
+
+TEST(FirmwareJsonTest, MissingActionPreparation)
+{
+    auto j2 = R"(
+        [{
+            "blob" : "/flash/image",
+            "handler" : {
+                "type" : "file",
+                "path" : "/run/initramfs/bmc-image"
+            },
+            "actions" : {
+                "verification" : {
+                    "type" : "fileSystemdVerify",
+                    "unit" : "phosphor-ipmi-flash-bmc-verify.target",
+                    "path" : "/tmp/bmc.verify"
+                },
+                "update" : {
+                    "type" : "reboot"
+                }
+            }
+        }]
+    )"_json;
+
+    EXPECT_THAT(buildHandlerFromJson(j2), IsEmpty());
+}
+
+TEST(FirmwareJsonTest, MissingActionVerification)
+{
+    auto j2 = R"(
+        [{
+            "blob" : "/flash/image",
+            "handler" : {
+                "type" : "file",
+                "path" : "/run/initramfs/bmc-image"
+            },
+            "actions" : {
+                "preparation" : {
+                    "type" : "systemd",
+                    "unit" : "phosphor-ipmi-flash-bmc-prepare.target"
+                },
+                "update" : {
+                    "type" : "reboot"
+                }
+            }
+        }]
+    )"_json;
+
+    EXPECT_THAT(buildHandlerFromJson(j2), IsEmpty());
+}
+
+TEST(FirmwareJsonTest, MissingActionUpdate)
+{
+    auto j2 = R"(
+        [{
+            "blob" : "/flash/image",
+            "handler" : {
+                "type" : "file",
+                "path" : "/run/initramfs/bmc-image"
+            },
+            "actions" : {
+                "preparation" : {
+                    "type" : "systemd",
+                    "unit" : "phosphor-ipmi-flash-bmc-prepare.target"
+                },
+                "verification" : {
+                    "type" : "fileSystemdVerify",
+                    "unit" : "phosphor-ipmi-flash-bmc-verify.target",
+                    "path" : "/tmp/bmc.verify"
+                }
+            }
+        }]
+    )"_json;
+
+    EXPECT_THAT(buildHandlerFromJson(j2), IsEmpty());
+}
+
+TEST(FirmwareJsonTest, TwoConfigsOneInvalidReturnsValid)
+{
+    auto j2 = R"(
+        [{
+            "blob" : "/flash/image",
+            "actions" : {
+                "preparation" : {
+                    "type" : "systemd",
+                    "unit" : "phosphor-ipmi-flash-bmc-prepare.target"
+                },
+                "verification" : {
+                    "type" : "fileSystemdVerify",
+                    "unit" : "phosphor-ipmi-flash-bmc-verify.target",
+                    "path" : "/tmp/bmc.verify"
+                },
+                "update" : {
+                    "type" : "reboot"
+                }
+            }
+         },
+         {
+            "blob" : "/flash/image2",
+            "handler" : {
+                "type" : "file",
+                "path" : "/run/initramfs/bmc-image"
+            },
+            "actions" : {
+                "preparation" : {
+                    "type" : "systemd",
+                    "unit" : "phosphor-ipmi-flash-bmc-prepare.target"
+                },
+                "verification" : {
+                    "type" : "fileSystemdVerify",
+                    "unit" : "phosphor-ipmi-flash-bmc-verify.target",
+                    "path" : "/tmp/bmc.verify"
+                },
+                "update" : {
+                    "type" : "reboot"
+                }
+            }
+        }]
+    )"_json;
+
+    auto h = buildHandlerFromJson(j2);
+    EXPECT_EQ(h[0].blobId, "/flash/image2");
+    EXPECT_EQ(h.size(), 1);
+}
+
+/*
+ * TODO: It may be worth individually using builders per type, and testing
+ * those.
+ *
+ * TODO: Only allow unique handler blob paths (tested at a higher level).
+ */
+
+TEST(FirmwareJsonTest, VerifySystemdWithReboot)
+{
+    auto j2 = R"(
+        [{
+            "blob" : "/flash/image",
+            "handler" : {
+                "type" : "file",
+                "path" : "/run/initramfs/bmc-image"
+            },
+            "actions" : {
+                "preparation" : {
+                    "type" : "systemd",
+                    "unit" : "phosphor-ipmi-flash-bmc-prepare.target"
+                },
+                "verification" : {
+                    "type" : "fileSystemdVerify",
+                    "unit" : "phosphor-ipmi-flash-bmc-verify.target",
+                    "path" : "/tmp/bmc.verify"
+                },
+                "update" : {
+                    "type" : "reboot"
+                }
+            }
+         }]
+    )"_json;
+
+    auto h = buildHandlerFromJson(j2);
+    EXPECT_EQ(h[0].blobId, "/flash/image");
+    EXPECT_FALSE(h[0].handler == nullptr);
+    EXPECT_FALSE(h[0].actions == nullptr);
+    EXPECT_FALSE(h[0].actions->preparation == nullptr);
+    EXPECT_FALSE(h[0].actions->verification == nullptr);
+    EXPECT_FALSE(h[0].actions->update == nullptr);
+}
+
+TEST(FirmwareJsonTest, VerifyMultipleHandlersReturned)
+{
+    auto j2 = R"(
+        [{
+            "blob" : "/flash/image",
+            "handler" : {
+                "type" : "file",
+                "path" : "/run/initramfs/bmc-image"
+            },
+            "actions" : {
+                "preparation" : {
+                    "type" : "systemd",
+                    "unit" : "phosphor-ipmi-flash-bmc-prepare.target"
+                },
+                "verification" : {
+                    "type" : "fileSystemdVerify",
+                    "unit" : "phosphor-ipmi-flash-bmc-verify.target",
+                    "path" : "/tmp/bmc.verify"
+                },
+                "update" : {
+                    "type" : "systemd",
+                    "unit" : "phosphor-ipmi-flash-bmc-update.target"
+                }
+            }
+        },
+        {
+            "blob" : "/flash/bios",
+            "handler" : {
+                "type" : "file",
+                "path" : "/run/initramfs/bmc-image"
+            },
+            "actions" : {
+                "preparation" : {
+                    "type" : "systemd",
+                    "unit" : "phosphor-ipmi-flash-bmc-prepare.target"
+                },
+                "verification" : {
+                    "type" : "fileSystemdVerify",
+                    "unit" : "phosphor-ipmi-flash-bmc-verify.target",
+                    "path" : "/tmp/bmc.verify"
+                },
+                "update" : {
+                    "type" : "systemd",
+                    "unit" : "phosphor-ipmi-flash-bmc-update.target"
+                }
+            }
+        }]
+    )"_json;
+
+    auto h = buildHandlerFromJson(j2);
+    EXPECT_EQ(h.size(), 2);
+    EXPECT_EQ(h[0].blobId, "/flash/image");
+    EXPECT_EQ(h[1].blobId, "/flash/bios");
+}
+
+TEST(FirmwareJsonTest, VerifyValidSingleNonReboot)
+{
+    auto j2 = R"(
+        [{
+            "blob" : "/flash/image",
+            "handler" : {
+                "type" : "file",
+                "path" : "/run/initramfs/bmc-image"
+            },
+            "actions" : {
+                "preparation" : {
+                    "type" : "systemd",
+                    "unit" : "phosphor-ipmi-flash-bmc-prepare.target"
+                },
+                "verification" : {
+                    "type" : "fileSystemdVerify",
+                    "unit" : "phosphor-ipmi-flash-bmc-verify.target",
+                    "path" : "/tmp/bmc.verify"
+                },
+                "update" : {
+                    "type" : "systemd",
+                    "unit" : "phosphor-ipmi-flash-bmc-update.target"
+                }
+            }
+         }]
+    )"_json;
+
+    auto h = buildHandlerFromJson(j2);
+    EXPECT_EQ(h[0].blobId, "/flash/image");
+    EXPECT_FALSE(h[0].handler == nullptr);
+    EXPECT_FALSE(h[0].actions == nullptr);
+    EXPECT_FALSE(h[0].actions->preparation == nullptr);
+    EXPECT_FALSE(h[0].actions->verification == nullptr);
+    EXPECT_FALSE(h[0].actions->update == nullptr);
+}
+
+} // namespace
+} // namespace ipmi_flash