diff --git a/rt_manipulators_lib/include/hardware.hpp b/rt_manipulators_lib/include/hardware.hpp index 4ae387f..0a5c788 100644 --- a/rt_manipulators_lib/include/hardware.hpp +++ b/rt_manipulators_lib/include/hardware.hpp @@ -34,6 +34,7 @@ using JointName = std::string; class Hardware { public: explicit Hardware(const std::string device_name); + explicit Hardware(std::unique_ptr comm); ~Hardware(); bool load_config_file(const std::string& config_yaml); bool connect(const int baudrate = 3000000); diff --git a/rt_manipulators_lib/include/hardware_communicator.hpp b/rt_manipulators_lib/include/hardware_communicator.hpp index 36d6e24..47baf28 100644 --- a/rt_manipulators_lib/include/hardware_communicator.hpp +++ b/rt_manipulators_lib/include/hardware_communicator.hpp @@ -39,33 +39,38 @@ using GroupSyncWrite = dynamixel::GroupSyncWrite; class Communicator{ public: explicit Communicator(const std::string device_name); - ~Communicator(); - bool is_connected(); - bool connect(const int baudrate = 3000000); - void disconnect(); - void make_sync_read_group(const group_name_t & group_name, const dxl_address_t & start_address, - const dxl_data_length_t & data_length); - void make_sync_write_group(const group_name_t & group_name, const dxl_address_t & start_address, - const dxl_data_length_t & data_length); - bool append_id_to_sync_read_group(const group_name_t & group_name, const dxl_id_t & id); - bool append_id_to_sync_write_group(const group_name_t & group_name, const dxl_id_t & id, + virtual ~Communicator(); + virtual bool is_connected(); + virtual bool connect(const int baudrate = 3000000); + virtual void disconnect(); + virtual void make_sync_read_group( + const group_name_t & group_name, const dxl_address_t & start_address, + const dxl_data_length_t & data_length); + virtual void make_sync_write_group( + const group_name_t & group_name, const dxl_address_t & start_address, + const dxl_data_length_t & data_length); + virtual bool append_id_to_sync_read_group(const group_name_t & group_name, const dxl_id_t & id); + virtual bool append_id_to_sync_write_group(const group_name_t & group_name, const dxl_id_t & id, std::vector & init_data); - bool send_sync_read_packet(const group_name_t & group_name); - bool send_sync_write_packet(const group_name_t & group_name); - bool get_sync_read_data(const group_name_t & group_name, const dxl_id_t id, + virtual bool send_sync_read_packet(const group_name_t & group_name); + virtual bool send_sync_write_packet(const group_name_t & group_name); + virtual bool get_sync_read_data(const group_name_t & group_name, const dxl_id_t id, const dxl_address_t & address, const dxl_data_length_t & length, dxl_double_word_t & read_data); - bool set_sync_write_data(const group_name_t & group_name, const dxl_id_t id, + virtual bool set_sync_write_data(const group_name_t & group_name, const dxl_id_t id, std::vector & write_data); - bool write_byte_data(const dxl_id_t & id, const dxl_address_t & address, + virtual bool write_byte_data(const dxl_id_t & id, const dxl_address_t & address, const dxl_byte_t & write_data); - bool write_word_data(const dxl_id_t & id, const dxl_address_t & address, + virtual bool write_word_data(const dxl_id_t & id, const dxl_address_t & address, const dxl_word_t & write_data); - bool write_double_word_data(const dxl_id_t & id, const dxl_address_t & address, - const dxl_double_word_t & write_data); - bool read_byte_data(const dxl_id_t & id, const dxl_address_t & address, dxl_byte_t & read_data); - bool read_word_data(const dxl_id_t & id, const dxl_address_t & address, dxl_word_t & read_data); - bool read_double_word_data(const dxl_id_t & id, const dxl_address_t & address, + virtual bool write_double_word_data( + const dxl_id_t & id, const dxl_address_t & address, + const dxl_double_word_t & write_data); + virtual bool read_byte_data( + const dxl_id_t & id, const dxl_address_t & address, dxl_byte_t & read_data); + virtual bool read_word_data( + const dxl_id_t & id, const dxl_address_t & address, dxl_word_t & read_data); + virtual bool read_double_word_data(const dxl_id_t & id, const dxl_address_t & address, dxl_double_word_t & read_data); private: diff --git a/rt_manipulators_lib/src/hardware.cpp b/rt_manipulators_lib/src/hardware.cpp index c870973..89f5e62 100644 --- a/rt_manipulators_lib/src/hardware.cpp +++ b/rt_manipulators_lib/src/hardware.cpp @@ -23,7 +23,12 @@ namespace rt_manipulators_cpp { Hardware::Hardware(const std::string device_name) : thread_enable_(false) { - comm_ = std::make_shared(device_name); + comm_ = std::make_unique(device_name); +} + +Hardware::Hardware(std::unique_ptr comm) : + thread_enable_(false) { + comm_ = std::move(comm); } Hardware::~Hardware() { diff --git a/rt_manipulators_lib/test/CMakeLists.txt b/rt_manipulators_lib/test/CMakeLists.txt index 25e5a80..bade229 100644 --- a/rt_manipulators_lib/test/CMakeLists.txt +++ b/rt_manipulators_lib/test/CMakeLists.txt @@ -22,7 +22,19 @@ set(list_tests test_dynamixel_x test_dynamixel_xh test_dynamixel_p + test_hardware ) + +# Download FakeIt +Set(FETCHCONTENT_QUIET FALSE) +include(FetchContent) +FetchContent_Declare( + fakeit + GIT_REPOSITORY https://github.com/eranpeer/FakeIt + GIT_TAG 2.4.0 + GIT_PROGRESS TRUE) +FetchContent_MakeAvailable(fakeit) + foreach(test_executable IN LISTS list_tests) message("${test_executable}") add_executable(${test_executable} @@ -33,5 +45,8 @@ foreach(test_executable IN LISTS list_tests) GTest::Main rt_manipulators_cpp ) + target_include_directories(${test_executable} PRIVATE + ${fakeit_SOURCE_DIR}/single_header/gtest + ) gtest_discover_tests(${test_executable}) endforeach() diff --git a/rt_manipulators_lib/test/test_hardware.cpp b/rt_manipulators_lib/test/test_hardware.cpp new file mode 100644 index 0000000..9ca299c --- /dev/null +++ b/rt_manipulators_lib/test/test_hardware.cpp @@ -0,0 +1,86 @@ +// Copyright 2023 RT 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 + +#include "fakeit.hpp" +#include "gtest/gtest.h" +#include "rt_manipulators_cpp/hardware.hpp" +#include "rt_manipulators_cpp/hardware_communicator.hpp" + +using fakeit::Mock; +using fakeit::Verify; +using fakeit::When; + +Mock create_comm_mock(void) { + Mock mock; + When(Method(mock, is_connected)).AlwaysReturn(true); + When(Method(mock, connect)).AlwaysReturn(true); + When(Method(mock, disconnect)).AlwaysReturn(); + When(Method(mock, make_sync_write_group)).AlwaysReturn(); + When(Method(mock, make_sync_read_group)).AlwaysReturn(); + When(Method(mock, append_id_to_sync_write_group)).AlwaysReturn(true); + When(Method(mock, append_id_to_sync_read_group)).AlwaysReturn(true); + When(Method(mock, send_sync_read_packet)).AlwaysReturn(true); + When(Method(mock, send_sync_write_packet)).AlwaysReturn(true); + When(Method(mock, get_sync_read_data)).AlwaysReturn(true); + When(Method(mock, set_sync_write_data)).AlwaysReturn(true); + When(Method(mock, write_byte_data)).AlwaysReturn(true); + When(Method(mock, write_word_data)).AlwaysReturn(true); + When(Method(mock, write_double_word_data)).AlwaysReturn(true); + When(Method(mock, read_byte_data)).AlwaysReturn(true); + When(Method(mock, read_word_data)).AlwaysReturn(true); + When(Method(mock, read_double_word_data)).AlwaysReturn(true); + + return mock; +} + +TEST(HardwareTest, load_config_file) { + // Expect the load_config_file method to be called twice and return true and false respectively. + auto mock = create_comm_mock(); + + rt_manipulators_cpp::Hardware hardware( + std::unique_ptr(&mock.get())); + + EXPECT_TRUE(hardware.load_config_file("../config/ok_has_dynamixel_name.yaml")); + EXPECT_FALSE(hardware.load_config_file("../config/ng_has_same_joints.yaml")); +} + +TEST(HardwareTest, connect) { + // Expect the connect method to be called twice and return true and false respectively. + auto mock = create_comm_mock(); + When(Method(mock, connect)).Return(true, false); // Return true then false. + + rt_manipulators_cpp::Hardware hardware( + std::unique_ptr(&mock.get())); + + EXPECT_TRUE(hardware.connect()); + EXPECT_FALSE(hardware.connect()); +} + +TEST(HardwareTest, disconnect) { + // Expect the disconnect method to be called once and never. + auto mock = create_comm_mock(); + When(Method(mock, is_connected)).Return(false).AlwaysReturn(true); // Return false then true. + When(Method(mock, disconnect)).AlwaysReturn(); + + rt_manipulators_cpp::Hardware hardware( + std::unique_ptr(&mock.get())); + + hardware.disconnect(); + Verify(Method(mock, disconnect)).Never(); + + hardware.disconnect(); + Verify(Method(mock, disconnect)).Once(); +}