diff --git a/firmware/controllers/actuators/boost_control.cpp b/firmware/controllers/actuators/boost_control.cpp index 90097d910b..9352cd463e 100644 --- a/firmware/controllers/actuators/boost_control.cpp +++ b/firmware/controllers/actuators/boost_control.cpp @@ -31,7 +31,7 @@ static boostOpenLoop_Map3D_t boostMapOpen; static boostOpenLoop_Map3D_t boostMapClosed; static SimplePwm boostPwmControl("boost"); -void BoostController::init(SimplePwm* pwm, const ValueProvider3D* openLoopMap, const ValueProvider3D* closedLoopTargetMap, pid_s* pidParams) { +void BoostController::init(IPwm* pwm, const ValueProvider3D* openLoopMap, const ValueProvider3D* closedLoopTargetMap, pid_s* pidParams) { m_pwm = pwm; m_openLoopMap = openLoopMap; m_closedLoopTargetMap = closedLoopTargetMap; diff --git a/firmware/controllers/actuators/boost_control.h b/firmware/controllers/actuators/boost_control.h index cee1ba2f7d..72281d2411 100644 --- a/firmware/controllers/actuators/boost_control.h +++ b/firmware/controllers/actuators/boost_control.h @@ -11,13 +11,13 @@ #include "closed_loop_controller.h" #include "pid.h" -class SimplePwm; +class IPwm; class BoostController : public ClosedLoopController { public: DECLARE_ENGINE_PTR; - void init(SimplePwm* pmw, const ValueProvider3D* openLoopMap, const ValueProvider3D* closedLoopTargetMap, pid_s* pidParams); + void init(IPwm* pmw, const ValueProvider3D* openLoopMap, const ValueProvider3D* closedLoopTargetMap, pid_s* pidParams); void update(); // Called when the configuration may have changed. Controller will @@ -39,7 +39,7 @@ private: const ValueProvider3D* m_openLoopMap = nullptr; const ValueProvider3D* m_closedLoopTargetMap = nullptr; - SimplePwm* m_pwm = nullptr; + IPwm* m_pwm = nullptr; }; void startBoostPin(); diff --git a/firmware/controllers/actuators/gppwm/gppwm_channel.cpp b/firmware/controllers/actuators/gppwm/gppwm_channel.cpp index 6023f41d12..fd5fe5c1f5 100644 --- a/firmware/controllers/actuators/gppwm/gppwm_channel.cpp +++ b/firmware/controllers/actuators/gppwm/gppwm_channel.cpp @@ -59,7 +59,7 @@ void GppwmChannel::setOutput(float result) { } } -void GppwmChannel::init(bool usePwm, SimplePwm* pwm, OutputPin* outputPin, const ValueProvider3D* table, const gppwm_channel* config) { +void GppwmChannel::init(bool usePwm, IPwm* pwm, OutputPin* outputPin, const ValueProvider3D* table, const gppwm_channel* config) { m_usePwm = usePwm; m_pwm = pwm; m_output = outputPin; diff --git a/firmware/controllers/actuators/gppwm/gppwm_channel.h b/firmware/controllers/actuators/gppwm/gppwm_channel.h index ad863aba3b..db34a689b2 100644 --- a/firmware/controllers/actuators/gppwm/gppwm_channel.h +++ b/firmware/controllers/actuators/gppwm/gppwm_channel.h @@ -6,14 +6,14 @@ struct gppwm_channel; class OutputPin; -class SimplePwm; +class IPwm; class ValueProvider3D; class GppwmChannel { public: DECLARE_ENGINE_PTR; - void init(bool usePwm, SimplePwm* pwm, OutputPin* outputPin, const ValueProvider3D* table, const gppwm_channel* config); + void init(bool usePwm, IPwm* pwm, OutputPin* outputPin, const ValueProvider3D* table, const gppwm_channel* config); float update(); percent_t getOutput() const; void setOutput(float result); @@ -25,7 +25,7 @@ private: // Configuration fields const gppwm_channel* m_config = nullptr; bool m_usePwm = false; - SimplePwm* m_pwm = nullptr; + IPwm* m_pwm = nullptr; OutputPin* m_output = nullptr; const ValueProvider3D* m_table = nullptr; }; diff --git a/firmware/controllers/system/dc_motor.h b/firmware/controllers/system/dc_motor.h index 8d0f66488e..e04d4a9467 100644 --- a/firmware/controllers/system/dc_motor.h +++ b/firmware/controllers/system/dc_motor.h @@ -71,9 +71,9 @@ public: }; private: - IPwm* m_enable; - IPwm* m_dir1; - IPwm* m_dir2; + IPwm* m_enable = nullptr; + IPwm* m_dir1 = nullptr; + IPwm* m_dir2 = nullptr; OutputPin* const m_disable; float m_value = 0; diff --git a/unit_tests/mocks.h b/unit_tests/mocks.h index 3e5a9f8be5..0a9c50f065 100644 --- a/unit_tests/mocks.h +++ b/unit_tests/mocks.h @@ -42,7 +42,7 @@ public: MOCK_METHOD(float, getValue, (float xColumn, float yRow), (const, override)); }; -class MockPwm : public SimplePwm { +class MockPwm : public IPwm { public: MOCK_METHOD(void, setSimplePwmDutyCycle, (float dutyCycle), (override)); }; diff --git a/unit_tests/tests/test_dc_motor.cpp b/unit_tests/tests/test_dc_motor.cpp new file mode 100644 index 0000000000..3af162ad60 --- /dev/null +++ b/unit_tests/tests/test_dc_motor.cpp @@ -0,0 +1,126 @@ +#include "dc_motor.h" + +#include "mocks.h" + +using ::testing::InSequence; +using ::testing::NiceMock; +using ::testing::StrictMock; + +TEST(DcMotor, Disable) { + StrictMock dpin; + + EXPECT_CALL(dpin, setValue(1)) + .Times(2); // happens twice - once for initial disable, once for set(0) + + TwoPinDcMotor dut(dpin); +} + +TEST(DcMotor, Disable2) { + StrictMock dpin; + + EXPECT_CALL(dpin, setValue(1)).Times(4); + + TwoPinDcMotor dut(dpin); + + dut.disable(); +} + +TEST(DcMotor, Enable) { + StrictMock dpin; + + { + InSequence is; + + // Construction disables + EXPECT_CALL(dpin, setValue(1)).Times(2); + + // Then enable + EXPECT_CALL(dpin, setValue(0)); + } + + TwoPinDcMotor dut(dpin); + dut.enable(); +} + +TEST(DcMotor, SetUnconfigured) { + StrictMock dpin; + EXPECT_CALL(dpin, setValue(1)).Times(3); + + TwoPinDcMotor dut(dpin); + EXPECT_FLOAT_EQ(dut.get(), 0); + EXPECT_NO_THROW(dut.set(0.5f)); + + // Readback should work even without configuration + EXPECT_FLOAT_EQ(dut.get(), 0.5f); +} + +TEST(DcMotor, PwmEnablePinModePositive) { + NiceMock dpin; + TwoPinDcMotor dut(dpin); + dut.setType(TwoPinDcMotor::ControlType::PwmEnablePin); + + MockPwm enable; + MockPwm dir1; + MockPwm dir2; + + EXPECT_CALL(enable, setSimplePwmDutyCycle(0.5f)); + EXPECT_CALL(dir1, setSimplePwmDutyCycle(1)); + EXPECT_CALL(dir2, setSimplePwmDutyCycle(0)); + + dut.configure(enable, dir1, dir2); + dut.set(0.5f); +} + +TEST(DcMotor, PwmEnablePinModeNegative) { + NiceMock dpin; + TwoPinDcMotor dut(dpin); + dut.setType(TwoPinDcMotor::ControlType::PwmEnablePin); + + MockPwm enable; + MockPwm dir1; + MockPwm dir2; + + + EXPECT_CALL(enable, setSimplePwmDutyCycle(0.5f)); + EXPECT_CALL(dir1, setSimplePwmDutyCycle(0)); + EXPECT_CALL(dir2, setSimplePwmDutyCycle(1)); + + dut.configure(enable, dir1, dir2); + dut.set(-0.5f); +} + +TEST(DcMotor, PwmDirectionPinsModePositive) { + NiceMock dpin; + TwoPinDcMotor dut(dpin); + dut.setType(TwoPinDcMotor::ControlType::PwmDirectionPins); + + MockPwm enable; + MockPwm dir1; + MockPwm dir2; + + + EXPECT_CALL(enable, setSimplePwmDutyCycle(1)); + EXPECT_CALL(dir1, setSimplePwmDutyCycle(0.5f)); + EXPECT_CALL(dir2, setSimplePwmDutyCycle(0)); + + dut.configure(enable, dir1, dir2); + dut.set(0.5f); +} + +TEST(DcMotor, PwmDirectionPinsModeNegative) { + NiceMock dpin; + TwoPinDcMotor dut(dpin); + dut.setType(TwoPinDcMotor::ControlType::PwmDirectionPins); + + MockPwm enable; + MockPwm dir1; + MockPwm dir2; + + + EXPECT_CALL(enable, setSimplePwmDutyCycle(1)); + EXPECT_CALL(dir1, setSimplePwmDutyCycle(0)); + EXPECT_CALL(dir2, setSimplePwmDutyCycle(0.5f)); + + dut.configure(enable, dir1, dir2); + dut.set(-0.5f); +} diff --git a/unit_tests/tests/test_gppwm.cpp b/unit_tests/tests/test_gppwm.cpp index f337a1bb5d..1a7d3ecfd3 100644 --- a/unit_tests/tests/test_gppwm.cpp +++ b/unit_tests/tests/test_gppwm.cpp @@ -7,13 +7,14 @@ #include "mocks.h" using ::testing::InSequence; +using ::testing::StrictMock; TEST(GpPwm, OutputWithPwm) { GppwmChannel ch; gppwm_channel cfg; - MockPwm pwm; + StrictMock pwm; // Shouldn't throw with no config EXPECT_NO_THROW(ch.setOutput(10)); diff --git a/unit_tests/tests/tests.mk b/unit_tests/tests/tests.mk index cb32a93306..3c07cf1d37 100644 --- a/unit_tests/tests/tests.mk +++ b/unit_tests/tests/tests.mk @@ -38,6 +38,7 @@ TESTS_SRC_CPP = \ tests/test_idle_controller.cpp \ tests/test_issue_898.cpp \ tests/test_etb.cpp \ + tests/test_dc_motor.cpp \ tests/test_fan_control.cpp \ tests/test_vvt.cpp \ tests/test_launch.cpp \