// Copyright 2021 The Pigweed Authors // // 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 // // https://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 "pw_digital_io/digital_io.h" #include "pw_status/status.h" #include "pw_unit_test/framework.h" namespace pw::digital_io { namespace { // The base class should be compact. static_assert(sizeof(DigitalIoOptional) <= 2 * sizeof(void*), "DigitalIo should be no larger than two pointers (vtable pointer " "& packed members)"); // Skeleton implementations to test DigitalIo methods. class TestDigitalInterrupt : public DigitalInterrupt { public: TestDigitalInterrupt() = default; private: Status DoEnable(bool) override { return OkStatus(); } Status DoSetInterruptHandler(InterruptTrigger, InterruptHandler&&) override { return OkStatus(); } Status DoEnableInterruptHandler(bool) override { return OkStatus(); } }; class TestDigitalIn : public DigitalIn { public: TestDigitalIn() : state_(State::kInactive) {} private: Status DoEnable(bool) override { return OkStatus(); } Result DoGetState() override { return state_; } const State state_; }; class TestDigitalInInterrupt : public DigitalInInterrupt { public: TestDigitalInInterrupt() : state_(State::kInactive) {} private: Status DoEnable(bool) override { return OkStatus(); } Result DoGetState() override { return state_; } Status DoSetInterruptHandler(InterruptTrigger, InterruptHandler&&) override { return OkStatus(); } Status DoEnableInterruptHandler(bool) override { return OkStatus(); } const State state_; }; class TestDigitalOut : public DigitalOut { public: TestDigitalOut() {} private: Status DoEnable(bool) override { return OkStatus(); } Status DoSetState(State) override { return OkStatus(); } }; class TestDigitalOutInterrupt : public DigitalOutInterrupt { public: TestDigitalOutInterrupt() {} private: Status DoEnable(bool) override { return OkStatus(); } Status DoSetState(State) override { return OkStatus(); } Status DoSetInterruptHandler(InterruptTrigger, InterruptHandler&&) override { return OkStatus(); } Status DoEnableInterruptHandler(bool) override { return OkStatus(); } }; class TestDigitalInOut : public DigitalInOut { public: TestDigitalInOut() : state_(State::kInactive) {} private: Status DoEnable(bool) override { return OkStatus(); } Result DoGetState() override { return state_; } Status DoSetState(State state) override { state_ = state; return OkStatus(); } State state_; }; class TestDigitalInOutInterrupt : public DigitalInOutInterrupt { public: TestDigitalInOutInterrupt() : state_(State::kInactive) {} private: Status DoEnable(bool) override { return OkStatus(); } Result DoGetState() override { return state_; } Status DoSetState(State state) override { state_ = state; return OkStatus(); } Status DoSetInterruptHandler(InterruptTrigger, InterruptHandler&&) override { return OkStatus(); } Status DoEnableInterruptHandler(bool) override { return OkStatus(); } State state_; }; // Test conversions between different interfaces. static_assert(!std::is_convertible()); static_assert(!std::is_convertible()); static_assert( !std::is_convertible()); static_assert( !std::is_convertible()); static_assert( !std::is_convertible()); static_assert(!std::is_convertible()); static_assert(!std::is_convertible()); static_assert(!std::is_convertible()); static_assert(!std::is_convertible()); static_assert(std::is_convertible()); static_assert(!std::is_convertible()); static_assert(std::is_convertible()); static_assert( !std::is_convertible()); static_assert(!std::is_convertible()); static_assert(!std::is_convertible()); static_assert(!std::is_convertible()); static_assert(!std::is_convertible()); static_assert(!std::is_convertible()); static_assert(std::is_convertible()); static_assert( std::is_convertible()); static_assert( !std::is_convertible()); static_assert(std::is_convertible()); static_assert(std::is_convertible()); static_assert(!std::is_convertible()); static_assert(!std::is_convertible()); static_assert(!std::is_convertible()); static_assert(std::is_convertible()); static_assert(std::is_convertible()); static_assert( std::is_convertible()); static_assert( std::is_convertible()); static_assert( std::is_convertible()); void FakeInterruptHandler(State) {} template void TestInput(Line& line) { ASSERT_EQ(OkStatus(), line.Enable()); auto state_result = line.GetState(); ASSERT_EQ(OkStatus(), state_result.status()); ASSERT_EQ(State::kInactive, state_result.value()); auto active_result = line.IsStateActive(); ASSERT_EQ(OkStatus(), active_result.status()); ASSERT_EQ(false, active_result.value()); ASSERT_EQ(OkStatus(), line.Disable()); } template void TestOutput(Line& line) { ASSERT_EQ(OkStatus(), line.Enable()); ASSERT_EQ(OkStatus(), line.SetState(State::kActive)); ASSERT_EQ(OkStatus(), line.SetState(State::kInactive)); ASSERT_EQ(OkStatus(), line.SetStateActive()); ASSERT_EQ(OkStatus(), line.SetStateInactive()); ASSERT_EQ(OkStatus(), line.Disable()); } template void TestOutputReadback(Line& line) { ASSERT_EQ(OkStatus(), line.Enable()); ASSERT_EQ(OkStatus(), line.SetState(State::kActive)); auto state_result = line.GetState(); ASSERT_EQ(OkStatus(), state_result.status()); ASSERT_EQ(State::kActive, state_result.value()); ASSERT_EQ(OkStatus(), line.SetState(State::kInactive)); state_result = line.GetState(); ASSERT_EQ(OkStatus(), state_result.status()); ASSERT_EQ(State::kInactive, state_result.value()); ASSERT_EQ(OkStatus(), line.SetStateActive()); auto active_result = line.IsStateActive(); ASSERT_EQ(OkStatus(), active_result.status()); ASSERT_EQ(true, active_result.value()); ASSERT_EQ(OkStatus(), line.SetStateInactive()); active_result = line.IsStateActive(); ASSERT_EQ(OkStatus(), active_result.status()); ASSERT_EQ(false, active_result.value()); ASSERT_EQ(OkStatus(), line.Disable()); } template void TestInterrupt(Line& line) { ASSERT_EQ(OkStatus(), line.Enable()); ASSERT_EQ(OkStatus(), line.SetInterruptHandler(InterruptTrigger::kBothEdges, FakeInterruptHandler)); ASSERT_EQ(OkStatus(), line.EnableInterruptHandler()); ASSERT_EQ(OkStatus(), line.EnableInterruptHandler()); ASSERT_EQ(OkStatus(), line.DisableInterruptHandler()); ASSERT_EQ(OkStatus(), line.ClearInterruptHandler()); ASSERT_EQ(OkStatus(), line.Disable()); } TEST(Digital, Interrupt) { TestDigitalInterrupt line; DigitalIoOptional& optional_line = line; ASSERT_EQ(false, optional_line.provides_input()); ASSERT_EQ(false, optional_line.provides_output()); ASSERT_EQ(true, optional_line.provides_interrupt()); TestInterrupt(line); TestInterrupt(optional_line); } TEST(Digital, In) { TestDigitalIn line; DigitalIoOptional& optional_line = line; ASSERT_EQ(true, optional_line.provides_input()); ASSERT_EQ(false, optional_line.provides_output()); ASSERT_EQ(false, optional_line.provides_interrupt()); TestInput(line); TestInput(optional_line); } TEST(Digital, InInterrupt) { TestDigitalInInterrupt line; DigitalIoOptional& optional_line = line; ASSERT_EQ(true, optional_line.provides_input()); ASSERT_EQ(false, optional_line.provides_output()); ASSERT_EQ(true, optional_line.provides_interrupt()); TestInput(line); TestInterrupt(line); TestInput(optional_line); TestInterrupt(optional_line); } TEST(Digital, Out) { TestDigitalOut line; DigitalIoOptional& optional_line = line; ASSERT_EQ(false, optional_line.provides_input()); ASSERT_EQ(true, optional_line.provides_output()); ASSERT_EQ(false, optional_line.provides_interrupt()); TestOutput(line); TestOutput(optional_line); } TEST(Digital, OutInterrupt) { TestDigitalOutInterrupt line; DigitalIoOptional& optional_line = line; ASSERT_EQ(false, optional_line.provides_input()); ASSERT_EQ(true, optional_line.provides_output()); ASSERT_EQ(true, optional_line.provides_interrupt()); TestOutput(line); TestInterrupt(line); TestOutput(optional_line); TestInterrupt(optional_line); } TEST(Digital, InOut) { TestDigitalInOut line; DigitalIoOptional& optional_line = line; ASSERT_EQ(true, optional_line.provides_input()); ASSERT_EQ(true, optional_line.provides_output()); ASSERT_EQ(false, optional_line.provides_interrupt()); TestInput(line); TestOutput(line); TestOutputReadback(line); TestInput(optional_line); TestOutput(optional_line); TestOutputReadback(optional_line); } TEST(DigitalIo, InOutInterrupt) { TestDigitalInOutInterrupt line; DigitalIoOptional& optional_line = line; ASSERT_EQ(true, optional_line.provides_input()); ASSERT_EQ(true, optional_line.provides_output()); ASSERT_EQ(true, optional_line.provides_interrupt()); TestInput(line); TestOutput(line); TestOutputReadback(line); TestInterrupt(line); TestInput(optional_line); TestOutput(optional_line); TestOutputReadback(optional_line); TestInterrupt(optional_line); } } // namespace } // namespace pw::digital_io