regulators: Create Chassis class

Create C++ class that implements the 'chassis' element from the JSON
config file.  See chassis.md for more information.

Signed-off-by: Shawn McCarney <shawnmm@us.ibm.com>
Change-Id: I00544ad00fb810bc5d25896c83f2d7dd1221937d
diff --git a/phosphor-regulators/src/chassis.hpp b/phosphor-regulators/src/chassis.hpp
new file mode 100644
index 0000000..d9eec81
--- /dev/null
+++ b/phosphor-regulators/src/chassis.hpp
@@ -0,0 +1,117 @@
+/**
+ * Copyright © 2020 IBM Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include "device.hpp"
+
+#include <memory>
+#include <stdexcept>
+#include <string>
+#include <utility>
+#include <vector>
+
+namespace phosphor::power::regulators
+{
+
+/**
+ * @class Chassis
+ *
+ * A chassis within the system.
+ *
+ * Chassis are large enclosures that can be independently powered off and on by
+ * the BMC.  Small and mid-sized systems may contain a single chassis.  In a
+ * large rack-mounted system, each drawer may correspond to a chassis.
+ *
+ * A C++ Chassis object only needs to be created if the physical chassis
+ * contains regulators that need to be configured or monitored.
+ */
+class Chassis
+{
+  public:
+    // Specify which compiler-generated methods we want
+    Chassis() = delete;
+    Chassis(const Chassis&) = delete;
+    Chassis(Chassis&&) = delete;
+    Chassis& operator=(const Chassis&) = delete;
+    Chassis& operator=(Chassis&&) = delete;
+    ~Chassis() = default;
+
+    /**
+     * Constructor.
+     *
+     * Throws an exception if any of the input parameters are invalid.
+     *
+     * @param number Chassis number within the system.  Chassis numbers start at
+     *               1 because chassis 0 represents the entire system.
+     * @param devices Devices within this chassis, if any.  The vector should
+     *                contain regulator devices and any related devices required
+     *                to perform regulator operations.
+     */
+    explicit Chassis(unsigned int number,
+                     std::vector<std::unique_ptr<Device>> devices =
+                         std::vector<std::unique_ptr<Device>>{}) :
+        number{number},
+        devices{std::move(devices)}
+    {
+        if (number < 1)
+        {
+            throw std::invalid_argument{"Invalid chassis number: " +
+                                        std::to_string(number)};
+        }
+    }
+
+    /**
+     * Returns the devices within this chassis, if any.
+     *
+     * The vector contains regulator devices and any related devices
+     * required to perform regulator operations.
+     *
+     * @return devices in chassis
+     */
+    const std::vector<std::unique_ptr<Device>>& getDevices() const
+    {
+        return devices;
+    }
+
+    /**
+     * Returns the chassis number within the system.
+     *
+     * @return chassis number
+     */
+    unsigned int getNumber() const
+    {
+        return number;
+    }
+
+  private:
+    /**
+     * Chassis number within the system.
+     *
+     * Chassis numbers start at 1 because chassis 0 represents the entire
+     * system.
+     */
+    const unsigned int number{};
+
+    /**
+     * Devices within this chassis, if any.
+     *
+     * The vector contains regulator devices and any related devices
+     * required to perform regulator operations.
+     */
+    std::vector<std::unique_ptr<Device>> devices{};
+};
+
+} // namespace phosphor::power::regulators
diff --git a/phosphor-regulators/test/chassis_tests.cpp b/phosphor-regulators/test/chassis_tests.cpp
new file mode 100644
index 0000000..707fff5
--- /dev/null
+++ b/phosphor-regulators/test/chassis_tests.cpp
@@ -0,0 +1,104 @@
+/**
+ * Copyright © 2020 IBM Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "chassis.hpp"
+#include "device.hpp"
+#include "i2c_interface.hpp"
+#include "test_utils.hpp"
+
+#include <memory>
+#include <stdexcept>
+#include <utility>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+using namespace phosphor::power::regulators;
+using namespace phosphor::power::regulators::test_utils;
+
+TEST(ChassisTests, Constructor)
+{
+    // Test where works: Only required parameters are specified
+    {
+        Chassis chassis{2};
+        EXPECT_EQ(chassis.getNumber(), 2);
+        EXPECT_EQ(chassis.getDevices().size(), 0);
+    }
+
+    // Test where works: All parameters are specified
+    {
+        // Create vector of Device objects
+        std::vector<std::unique_ptr<Device>> devices{};
+        devices.push_back(std::make_unique<Device>(
+            "vdd_reg1", true, "/system/chassis/motherboard/reg1",
+            std::move(createI2CInterface())));
+        devices.push_back(std::make_unique<Device>(
+            "vdd_reg2", true, "/system/chassis/motherboard/reg2",
+            std::move(createI2CInterface())));
+
+        // Create Chassis
+        Chassis chassis{1, std::move(devices)};
+        EXPECT_EQ(chassis.getNumber(), 1);
+        EXPECT_EQ(chassis.getDevices().size(), 2);
+    }
+
+    // Test where fails: Invalid chassis number < 1
+    try
+    {
+        Chassis chassis{0};
+        ADD_FAILURE() << "Should not have reached this line.";
+    }
+    catch (const std::invalid_argument& e)
+    {
+        EXPECT_STREQ(e.what(), "Invalid chassis number: 0");
+    }
+    catch (...)
+    {
+        ADD_FAILURE() << "Should not have caught exception.";
+    }
+}
+
+TEST(ChassisTests, GetDevices)
+{
+    // Test where no devices were specified in constructor
+    {
+        Chassis chassis{2};
+        EXPECT_EQ(chassis.getDevices().size(), 0);
+    }
+
+    // Test where devices were specified in constructor
+    {
+        // Create vector of Device objects
+        std::vector<std::unique_ptr<Device>> devices{};
+        devices.push_back(std::make_unique<Device>(
+            "vdd_reg1", true, "/system/chassis/motherboard/reg1",
+            std::move(createI2CInterface())));
+        devices.push_back(std::make_unique<Device>(
+            "vdd_reg2", true, "/system/chassis/motherboard/reg2",
+            std::move(createI2CInterface())));
+
+        // Create Chassis
+        Chassis chassis{1, std::move(devices)};
+        EXPECT_EQ(chassis.getDevices().size(), 2);
+        EXPECT_EQ(chassis.getDevices()[0]->getID(), "vdd_reg1");
+        EXPECT_EQ(chassis.getDevices()[1]->getID(), "vdd_reg2");
+    }
+}
+
+TEST(ChassisTests, GetNumber)
+{
+    Chassis chassis{3};
+    EXPECT_EQ(chassis.getNumber(), 3);
+}
diff --git a/phosphor-regulators/test/device_tests.cpp b/phosphor-regulators/test/device_tests.cpp
index 955e6d8..2ae4d6f 100644
--- a/phosphor-regulators/test/device_tests.cpp
+++ b/phosphor-regulators/test/device_tests.cpp
@@ -20,6 +20,7 @@
 #include "mock_action.hpp"
 #include "presence_detection.hpp"
 #include "rail.hpp"
+#include "test_utils.hpp"
 
 #include <memory>
 #include <optional>
@@ -29,18 +30,7 @@
 #include <gtest/gtest.h>
 
 using namespace phosphor::power::regulators;
-
-/**
- * Create an I2CInterface object with hard-coded bus and address values.
- *
- * @return I2CInterface object wrapped in a unique_ptr
- */
-std::unique_ptr<i2c::I2CInterface> createI2CInterface()
-{
-    std::unique_ptr<i2c::I2CInterface> i2cInterface =
-        i2c::create(1, 0x70, i2c::I2CInterface::InitialState::CLOSED);
-    return i2cInterface;
-}
+using namespace phosphor::power::regulators::test_utils;
 
 TEST(DeviceTests, Constructor)
 {
diff --git a/phosphor-regulators/test/meson.build b/phosphor-regulators/test/meson.build
index 5042cd3..0c2ba50 100644
--- a/phosphor-regulators/test/meson.build
+++ b/phosphor-regulators/test/meson.build
@@ -4,6 +4,7 @@
 )
 
 phosphor_regulators_tests_source_files = [
+    'chassis_tests.cpp',
     'configuration_tests.cpp',
     'device_tests.cpp',
     'id_map_tests.cpp',
diff --git a/phosphor-regulators/test/test_utils.hpp b/phosphor-regulators/test/test_utils.hpp
new file mode 100644
index 0000000..cbb322a
--- /dev/null
+++ b/phosphor-regulators/test/test_utils.hpp
@@ -0,0 +1,33 @@
+/**
+ * Copyright © 2020 IBM Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "i2c_interface.hpp"
+
+#include <memory>
+
+namespace phosphor::power::regulators::test_utils
+{
+
+/**
+ * Create an I2CInterface object with hard-coded bus and address values.
+ *
+ * @return I2CInterface object wrapped in a unique_ptr
+ */
+inline std::unique_ptr<i2c::I2CInterface> createI2CInterface()
+{
+    return i2c::create(1, 0x70, i2c::I2CInterface::InitialState::CLOSED);
+}
+
+} // namespace phosphor::power::regulators::test_utils