From fd82a86b063a8351daac9891883f74b9556525c7 Mon Sep 17 00:00:00 2001 From: Alexander Duda Date: Fri, 16 Dec 2016 22:26:48 +0100 Subject: [PATCH 01/15] Driver: fix opening serial on Mac OS X tcsetattr returns an error if the speed is not. CLOCAL must be set otherwise usb - serial adapter will never get ready --- src/Driver.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Driver.cpp b/src/Driver.cpp index 000634e..bd819a3 100644 --- a/src/Driver.cpp +++ b/src/Driver.cpp @@ -325,8 +325,12 @@ int Driver::openSerialIO(std::string const& port, int baud_rate) struct termios tio; memset(&tio, 0, sizeof(termios)); - tio.c_cflag = CS8 | CREAD; // data bits = 8bit and enable receiver - tio.c_iflag = IGNBRK; // don't use breaks by default + + tio.c_cflag = (CS8 | CREAD | CLOCAL); // data bits = 8bit, enable reading + // and ignore modem status line + tio.c_iflag = IGNBRK; // don't use breaks by default + cfsetispeed(&tio, B9600); // set baud rate to something + cfsetospeed(&tio, B9600); // otherwise tcsetattr complains on Mac OS // Commit if (tcsetattr(fd, TCSANOW, &tio)!=0) From 33e4bd6dee6b3d7675c715f6c256b443ec99dc07 Mon Sep 17 00:00:00 2001 From: Gabriel Arjones Date: Mon, 13 Mar 2017 14:00:14 -0300 Subject: [PATCH 02/15] Fix base/logging deprecated warning --- manifest.xml | 1 + src/IOStream.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/manifest.xml b/manifest.xml index 40b25a7..929caff 100644 --- a/manifest.xml +++ b/manifest.xml @@ -14,5 +14,6 @@ stable + diff --git a/src/IOStream.cpp b/src/IOStream.cpp index 8539e0f..5214113 100644 --- a/src/IOStream.cpp +++ b/src/IOStream.cpp @@ -1,6 +1,6 @@ #include #include -#include +#include #include #include From c631f92300929e66e4a6c1a7099f51731c1a7a2a Mon Sep 17 00:00:00 2001 From: Sylvain Joyeux Date: Thu, 13 Jul 2017 19:12:01 -0300 Subject: [PATCH 03/15] use the cmake-provided way to resolve -pthread --- src/CMakeLists.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4796aac..1815ab9 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,12 +1,14 @@ find_package(Boost COMPONENTS thread system) include_directories(${Boost_INCLUDE_DIRS}) +find_package(Threads REQUIRED) + rock_library(iodrivers_base SOURCES Driver.cpp Bus.cpp Timeout.cpp IOStream.cpp Exceptions.cpp TCPDriver.cpp IOListener.cpp HEADERS Driver.hpp Bus.hpp Timeout.hpp Status.hpp IOStream.hpp Exceptions.hpp IOListener.hpp TCPDriver.hpp - LIBS ${Boost_THREAD_LIBRARY} ${Boost_SYSTEM_LIBRARY} pthread + LIBS ${Boost_THREAD_LIBRARY} ${Boost_SYSTEM_LIBRARY} ${CMAKE_THREAD_LIBS_INIT} DEPS_PKGCONFIG base-types base-lib) # For backward compatibility only From 16300b73df226d99e0df99982bb1088c18a85bdd Mon Sep 17 00:00:00 2001 From: Sylvain Joyeux Date: Fri, 14 Jul 2017 09:43:28 -0300 Subject: [PATCH 04/15] create a harness to test drivers as blackboxes This uses a stream object (TestStream) that gives direct access to writing and reading data to/from the driver. A driver can be initialized with a TestStream as main stream by using the test:// URI, thus allowing to pass through the driver's own open methods. --- src/CMakeLists.txt | 5 +- src/Driver.cpp | 19 +++- src/Driver.hpp | 7 +- src/Fixture.hpp | 138 +++++++++++++++++++++++++++++ src/Status.hpp | 3 +- src/TestStream.cpp | 51 +++++++++++ src/TestStream.hpp | 43 +++++++++ test/CMakeLists.txt | 3 +- test/suite.cpp | 7 ++ test/{test.cpp => test_Driver.cpp} | 24 ++--- test/test_TestStream.cpp | 92 +++++++++++++++++++ 11 files changed, 374 insertions(+), 18 deletions(-) create mode 100644 src/Fixture.hpp create mode 100644 src/TestStream.cpp create mode 100644 src/TestStream.hpp create mode 100644 test/suite.cpp rename test/{test.cpp => test_Driver.cpp} (97%) create mode 100644 test/test_TestStream.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1815ab9..b51ec7f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -5,9 +5,10 @@ find_package(Threads REQUIRED) rock_library(iodrivers_base SOURCES Driver.cpp Bus.cpp Timeout.cpp IOStream.cpp Exceptions.cpp TCPDriver.cpp - IOListener.cpp + IOListener.cpp TestStream.cpp HEADERS Driver.hpp Bus.hpp Timeout.hpp Status.hpp IOStream.hpp - Exceptions.hpp IOListener.hpp TCPDriver.hpp + Exceptions.hpp IOListener.hpp TCPDriver.hpp TestStream.hpp + Fixture.hpp LIBS ${Boost_THREAD_LIBRARY} ${Boost_SYSTEM_LIBRARY} ${CMAKE_THREAD_LIBS_INIT} DEPS_PKGCONFIG base-types base-lib) diff --git a/src/Driver.cpp b/src/Driver.cpp index a15e59b..6e0ada9 100644 --- a/src/Driver.cpp +++ b/src/Driver.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #ifdef __gnu_linux__ #include @@ -106,6 +107,11 @@ void Driver::setMainStream(IOStream* stream) m_stream = stream; } +IOStream* Driver::getMainStream() const +{ + return m_stream; +} + void Driver::addListener(IOListener* listener) { m_listeners.insert(listener); @@ -124,7 +130,10 @@ void Driver::clear() } Status Driver::getStatus() const -{ return m_stats; } +{ + m_stats.queued_bytes = internal_buffer_size; + return m_stats; +} void Driver::resetStatus() { m_stats = Status(); } @@ -153,8 +162,8 @@ void Driver::openURI(std::string const& uri) // 3 for UDP server // 4 for file (either Unix sockets or named FIFOs) int mode_idx = -1; - char const* modes[5] = { "serial://", "tcp://", "udp://", "udpserver://", "file://" }; - for (int i = 0; i < 5; ++i) + char const* modes[6] = { "serial://", "tcp://", "udp://", "udpserver://", "file://", "test://" }; + for (int i = 0; i < 6; ++i) { if (uri.compare(0, strlen(modes[i]), modes[i]) == 0) { @@ -214,6 +223,10 @@ void Driver::openURI(std::string const& uri) { // file file://path return openFile(device); } + else if (mode_idx == 5) + { // test:// + setMainStream(new TestStream); + } } bool Driver::openSerial(std::string const& port, int baud_rate) diff --git a/src/Driver.hpp b/src/Driver.hpp index 8c79ab7..ef0940c 100644 --- a/src/Driver.hpp +++ b/src/Driver.hpp @@ -80,9 +80,10 @@ class Driver /** The current count of bytes left in \c internal_buffer */ size_t internal_buffer_size; -protected: +public: int const MAX_PACKET_SIZE; +protected: /** The underlying object that gives us access to the actual I/O stream */ IOStream* m_stream; @@ -434,6 +435,10 @@ class Driver */ void setMainStream(IOStream* stream); + /** Gets the main IO stream + */ + IOStream* getMainStream() const; + /** Add a listener stream. The object's ownership is taken by the Driver * object. */ diff --git a/src/Fixture.hpp b/src/Fixture.hpp new file mode 100644 index 0000000..3343c51 --- /dev/null +++ b/src/Fixture.hpp @@ -0,0 +1,138 @@ +#ifndef IODRIVERS_BASE_BOOST_FIXTURE_HPP +#define IODRIVERS_BASE_BOOST_FIXTURE_HPP + +#include +#include + +namespace iodrivers_base +{ + /** A fixture class designed to ease testing of iodrivers_base drivers in + * boost and GTest + * + * It creates a given Driver class, which must be opened with the test:// + * URI. It then provides helper methods to provide access to the underlying + * TestStream. + * + * readPacket/writePacket/pushDataToDevice/readDataFromDevice are then + * available within the test. + * + * In boost-test: + * + * + * BOOST_FIXTURE_TEST(MyTest, Fixture) + * { + * MyDriver.openURI("test://"); + * uint8_t buffer[4] = { 0, 1, 2, 3 }; + * pushDataToDevice(buffer, buffer + 2); + * auto packet = readPacket(); + * // Check that the packet matches the expected extraction + * } + * + * + * In GTest: + * + * + * struct DriverTest : ::testing::Test, iodrivers_base::Fixture + * { + * Fixture() + * { + * // Optional: open here + * // driver.openURI("test://") + * } + * } + * + * TEST_F(DriverTest, it_handles_an_invalid_packet) + * { + * MyDriver.openURI("test://"); + * uint8_t buffer[4] = { 0, 1, 2, 3 }; + * pushDataToDevice(buffer, buffer + 2); + * auto packet = readPacket(); + * // Check that the packet matches the expected extraction + * } + * + */ + template + struct Fixture + { + std::vector packetBuffer; + Driver driver; + + Fixture() + { + packetBuffer.resize(driver.MAX_PACKET_SIZE); + } + + /** Get the underlying TestStream + */ + TestStream* getStream() const + { + return dynamic_cast(driver.getMainStream()); + } + + /** Read a packet from the driver and return it as an std::vector + */ + std::vector readPacket() + { + size_t size = driver.readPacket(packetBuffer.data(), packetBuffer.size()); + std::vector packet; + packet.insert(packet.end(), packetBuffer.begin(), packetBuffer.begin() + size); + return packet; + } + + /** Write data to the driver */ + void writePacket(uint8_t const* buffer, size_t size) + { + driver.writePacket(buffer, size); + } + + /** Push data to the driver "as-if" it was coming from the device + */ + void pushDataToDriver(std::vector const& data) + { + return getStream()->pushDataToDriver(data); + } + + /** Helper method to allow passing any kind of uint8_t range + * + * + * uint8_t packet[] = { 0, 1, 2, 3 }; + * pushDataToDriver(packet, packet + sizeof(packet)); + * + */ + template + void pushDataToDriver(Iterator begin, Iterator end) + { + std::vector buffer(begin, end); + pushDataToDriver(buffer); + } + + /** Read data that the driver sent to the device + */ + std::vector readDataFromDriver() + { + return getStream()->readDataFromDriver(); + } + + /** Return the number of bytes currently queued in the driver's internal + * buffer + * + * This is useful mainly when testing extractPacket + * + * + * TEST_F(DriverTest, KeepsSingleBytes) + * { + * uint8_t data = 0x01; + * pushDataToDriver(&data, &data + 1); + * ASSERT_THROW(TimeoutError, readPacket()); + * ASSERT_EQUAL(1, getQueuedBytes()); + * } + * + */ + unsigned int getQueuedBytes() const + { + return driver.getStatus().queued_bytes; + } + }; +} + +#endif diff --git a/src/Status.hpp b/src/Status.hpp index 66ea1e7..1274d06 100644 --- a/src/Status.hpp +++ b/src/Status.hpp @@ -12,9 +12,10 @@ namespace iodrivers_base { unsigned int tx; //! count of bytes received unsigned int good_rx; //! count of bytes received and accepted unsigned int bad_rx; //! count of bytes received and rejected + unsigned int queued_bytes; //! count of bytes currently queued in the driver's internal buffer Status() - : tx(0), good_rx(0), bad_rx(0) {} + : tx(0), good_rx(0), bad_rx(0), queued_bytes(0) {} }; } diff --git a/src/TestStream.cpp b/src/TestStream.cpp new file mode 100644 index 0000000..4c73695 --- /dev/null +++ b/src/TestStream.cpp @@ -0,0 +1,51 @@ +#include +#include +#include + +using namespace std; +using namespace iodrivers_base; + +/** Push data to the device */ +void TestStream::pushDataToDriver(vector const& data) +{ + to_device.insert(to_device.end(), data.begin(), data.end()); +} + +/** Read all data that the device driver has written since the last + * call to readDataFromDriver + */ +vector TestStream::readDataFromDriver() +{ + vector temp; + temp.swap(from_device); + return temp; +} + +void TestStream::waitRead(base::Time const& timeout) +{ + if (to_device.empty()) + throw TimeoutError(TimeoutError::NONE, "no data in to_device"); +} +void TestStream::waitWrite(base::Time const& timeout) +{ +} + +size_t TestStream::read(uint8_t* buffer, size_t buffer_size) +{ + size_t read_size = min(to_device.size(), buffer_size); + std::memcpy(buffer, to_device.data(), read_size); + to_device.erase(to_device.begin(), to_device.begin() + read_size); + return read_size; +} + +size_t TestStream::write(uint8_t const* buffer, size_t buffer_size) +{ + from_device.insert(from_device.end(), buffer, buffer + buffer_size); + return buffer_size; +} + +void TestStream::clear() +{ + to_device.clear(); +} + diff --git a/src/TestStream.hpp b/src/TestStream.hpp new file mode 100644 index 0000000..3c554c0 --- /dev/null +++ b/src/TestStream.hpp @@ -0,0 +1,43 @@ +#ifndef IODRIVERS_BASE_HPP +#define IODRIVERS_BASE_HPP + +#include +#include + +namespace iodrivers_base +{ + /** A IOStream meant to be used to test iodrivers_base functionality + * from outside + * + * It maintains two vectors, one the "to device" buffer and one the "from + * buffer" device. All communications are synchronous, that is waitRead will + * throw right away if no data is available. + * waitWrite never fails. + */ + + class TestStream : public IOStream + { + std::vector to_device; + std::vector from_device; + + public: + /** Push data to the driver "as-if" it was coming from the device + */ + void pushDataToDriver(std::vector const& data); + + /** Read data that the driver sent to the device + * + * This contains only data sent since the last call to + * readDataFromDriver + */ + std::vector readDataFromDriver(); + + void waitRead(base::Time const& timeout); + void waitWrite(base::Time const& timeout); + size_t read(uint8_t* buffer, size_t buffer_size); + size_t write(uint8_t const* buffer, size_t buffer_size); + void clear(); + }; +} + +#endif diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 791bdb8..9ee2faf 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,4 +1,5 @@ -rock_testsuite(test_suite test.cpp +rock_testsuite(test_suite suite.cpp + test_Driver.cpp test_TestStream.cpp DEPS iodrivers_base) rock_executable(test_tcp_read test_tcp_read.cpp diff --git a/test/suite.cpp b/test/suite.cpp new file mode 100644 index 0000000..43f7749 --- /dev/null +++ b/test/suite.cpp @@ -0,0 +1,7 @@ +// Do NOT add anything to this file +// This header from boost takes ages to compile, so we make sure it is compiled +// only once (here) +#define BOOST_TEST_MAIN +#include + + diff --git a/test/test.cpp b/test/test_Driver.cpp similarity index 97% rename from test/test.cpp rename to test/test_Driver.cpp index ba57529..cb8aa78 100644 --- a/test/test.cpp +++ b/test/test_Driver.cpp @@ -1,10 +1,5 @@ -#define BOOST_TEST_DYN_LINK -#define BOOST_TEST_MAIN -#define BOOST_TEST_MODULE "iodrivers" -#define BOOST_AUTO_TEST_MAIN -#include #include -#include + #include #include #include @@ -37,7 +32,7 @@ class DriverTest : public Driver int setupDriver(Driver& driver) { int pipes[2]; - pipe(pipes); + BOOST_REQUIRE(pipe(pipes) == 0); int rx = pipes[0]; int tx = pipes[1]; @@ -53,6 +48,7 @@ void writeToDriver(Driver& driver, int tx, uint8_t const* data, int size) write(tx, data, size); } +BOOST_AUTO_TEST_SUITE(FileGuardSuite) BOOST_AUTO_TEST_CASE(test_FileGuard) { @@ -64,6 +60,13 @@ BOOST_AUTO_TEST_CASE(test_FileGuard) BOOST_REQUIRE_EQUAL(EBADF, errno); } +BOOST_AUTO_TEST_SUITE_END() + + + + +BOOST_AUTO_TEST_SUITE(DriverSuite) + void common_rx_timeout(DriverTest& test, int tx) { uint8_t buffer[100]; @@ -98,7 +101,7 @@ BOOST_AUTO_TEST_CASE(test_rx_first_byte_timeout) BOOST_REQUIRE_EQUAL(TimeoutError::FIRST_BYTE, e.type); } - write(tx, "a", 1); + BOOST_REQUIRE_EQUAL(write(tx, "a", 1), 1); try { test.readPacket(buffer, 100, 10, 1); @@ -125,7 +128,7 @@ BOOST_AUTO_TEST_CASE(test_open_sets_nonblock) DriverTest test; int pipes[2]; - pipe(pipes); + BOOST_REQUIRE_EQUAL(pipe(pipes), 0); int rx = pipes[0]; int tx = pipes[1]; test.setFileDescriptor(rx, true); @@ -135,7 +138,7 @@ BOOST_AUTO_TEST_CASE(test_open_sets_nonblock) uint8_t buffer[100]; BOOST_REQUIRE_THROW(test.readPacket(buffer, 100, 10), TimeoutError); - write(tx, "a", 1); + BOOST_REQUIRE_EQUAL(1, write(tx, "a", 1)); BOOST_REQUIRE_THROW(test.readPacket(buffer, 100, 10), TimeoutError); } @@ -369,3 +372,4 @@ BOOST_AUTO_TEST_CASE(test_send_from_bidirectional_udp) BOOST_REQUIRE((count == 4) && (memcmp(buffer, msg, count) == 0)); } +BOOST_AUTO_TEST_SUITE_END() diff --git a/test/test_TestStream.cpp b/test/test_TestStream.cpp new file mode 100644 index 0000000..6275886 --- /dev/null +++ b/test/test_TestStream.cpp @@ -0,0 +1,92 @@ +#include + +#include +#include + +using namespace iodrivers_base; +using namespace std; + +BOOST_AUTO_TEST_SUITE(TestStreamSuite) + +struct Driver : iodrivers_base::Driver +{ +public: + Driver() + : iodrivers_base::Driver(100) {} + + int extractPacket(uint8_t const* buffer, size_t size) const + { + return size; + } +}; + +struct Fixture : iodrivers_base::Fixture +{ + Fixture() + { + driver.openURI("test://"); + } +}; + + +BOOST_FIXTURE_TEST_CASE(it_sends_data_to_the_Driver, Fixture) +{ + uint8_t data[] = { 0, 1, 2, 3 }; + pushDataToDriver(data, data + 4); + vector buffer = readPacket(); + BOOST_REQUIRE(buffer == vector(data, data + 4)); +} + +BOOST_FIXTURE_TEST_CASE(it_accumulates_bytes_not_read_by_the_driver, Fixture) +{ + uint8_t data[] = { 0, 1, 2, 3 }; + pushDataToDriver(data, data + 2); + pushDataToDriver(data + 2, data + 4); + vector buffer = readPacket(); + BOOST_REQUIRE(buffer == vector(data, data + 4)); +} + +BOOST_FIXTURE_TEST_CASE(it_does_not_repeat_data_already_read_by_the_Driver, Fixture) +{ + uint8_t data[] = { 0, 1, 2, 3 }; + pushDataToDriver(data, data + 2); + readPacket(); + pushDataToDriver(data + 2, data + 4); + vector buffer = readPacket(); + BOOST_REQUIRE(buffer == vector(data + 2, data + 4)); +} + +BOOST_FIXTURE_TEST_CASE(it_times_out_instantly, Fixture) +{ + BOOST_REQUIRE_THROW(readPacket(), TimeoutError); +} + +BOOST_FIXTURE_TEST_CASE(it_gives_access_to_the_bytes_sent_by_the_driver, Fixture) +{ + uint8_t data[] = { 0, 1, 2, 3 }; + writePacket(data, 4); + std::vector received = readDataFromDriver(); + BOOST_REQUIRE(received == vector(data, data + 4)); +} + +BOOST_FIXTURE_TEST_CASE(it_accumulates_unread_bytes, Fixture) +{ + uint8_t data[] = { 0, 1, 2, 3 }; + writePacket(data, 2); + writePacket(data + 2, 2); + std::vector received = readDataFromDriver(); + BOOST_REQUIRE(received == vector(data, data + 4)); +} + +BOOST_FIXTURE_TEST_CASE(it_does_not_repeat_data_already_read_from_the_device, Fixture) +{ + uint8_t data[] = { 0, 1, 2, 3 }; + writePacket(data, 2); + readDataFromDriver(); + writePacket(data + 2, 2); + std::vector received = readDataFromDriver(); + BOOST_REQUIRE(received == vector(data + 2, data + 4)); +} + +BOOST_AUTO_TEST_SUITE_END() + From 5f45883ed5aeb341a38c6f529c98efa33c45bfb1 Mon Sep 17 00:00:00 2001 From: alcantara Date: Thu, 27 Jul 2017 10:31:57 -0300 Subject: [PATCH 05/15] Implement the MOCK funcionality to be able to send messages that require a reply while testing Drivers The MOCK functionality must be set with IODRIVERS_BASE_MOCK, what puts the driver in mock mode. The expectation tries to mimic the GTEST style and are set with EXPECT_REPLY(expectation,reply). Multiple expecatitons can be set and must be satisfied in the order of creation. If any expecation is not met at the end of the test, the test fails. --- manifest.xml | 2 + src/CMakeLists.txt | 6 +- src/Exceptions.hpp | 20 +++++- src/Fixture.hpp | 97 ++++++++++++++++++++++++++-- src/FixtureBoostTest.hpp | 3 + src/FixtureGtest.hpp | 3 + src/TestStream.cpp | 76 ++++++++++++++++++---- src/TestStream.hpp | 20 +++++- test/CMakeLists.txt | 7 +- test/test_TestStream.cpp | 80 ++++++++++++++++++++++- test/test_TestStreamGtest.cpp | 117 ++++++++++++++++++++++++++++++++++ 11 files changed, 408 insertions(+), 23 deletions(-) create mode 100644 src/FixtureBoostTest.hpp create mode 100644 src/FixtureGtest.hpp create mode 100644 test/test_TestStreamGtest.cpp diff --git a/manifest.xml b/manifest.xml index 929caff..a830b19 100644 --- a/manifest.xml +++ b/manifest.xml @@ -16,4 +16,6 @@ + + diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b51ec7f..d99a9b2 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -5,11 +5,11 @@ find_package(Threads REQUIRED) rock_library(iodrivers_base SOURCES Driver.cpp Bus.cpp Timeout.cpp IOStream.cpp Exceptions.cpp TCPDriver.cpp - IOListener.cpp TestStream.cpp + IOListener.cpp TestStream.cpp HEADERS Driver.hpp Bus.hpp Timeout.hpp Status.hpp IOStream.hpp Exceptions.hpp IOListener.hpp TCPDriver.hpp TestStream.hpp - Fixture.hpp - LIBS ${Boost_THREAD_LIBRARY} ${Boost_SYSTEM_LIBRARY} ${CMAKE_THREAD_LIBS_INIT} + Fixture.hpp FixtureBoostTest.hpp FixtureGtest.hpp + LIBS ${Boost_THREAD_LIBRARY} ${Boost_SYSTEM_LIBRARY} ${CMAKE_THREAD_LIBS_INIT} DEPS_PKGCONFIG base-types base-lib) # For backward compatibility only diff --git a/src/Exceptions.hpp b/src/Exceptions.hpp index 2f4241a..2db190e 100644 --- a/src/Exceptions.hpp +++ b/src/Exceptions.hpp @@ -3,6 +3,7 @@ #include #include +#include namespace iodrivers_base { @@ -31,6 +32,23 @@ struct TimeoutError : std::runtime_error }; } - +class MockContextException : public std::exception +{ + public: + const char * what() const throw() + { + return "IODRIVERS_BASE_MOCK Error: Expectation set outside Mock Context! Please call IODRIVERS_BASE_MOCK() before setting expecations\n"; + } +}; + +class TestEndsWithExpectationsLeftException : public std::exception +{ + public: + const char * what() const throw() + { + return "IODRIVERS_BASE_MOCK Error: Test reached its end without satisfying all expecations.\n"; + } +}; + #endif diff --git a/src/Fixture.hpp b/src/Fixture.hpp index 3343c51..b33a359 100644 --- a/src/Fixture.hpp +++ b/src/Fixture.hpp @@ -3,9 +3,23 @@ #include #include +#include + + +#ifdef IODRIVERS_BASE_FIXTURE_BOOST_FRAMEWORK + #include +#endif + +#ifdef IODRIVERS_BASE_FIXTURE_GTEST_FRAMEWORK + #include +#endif + +#define IODRIVERS_BASE_MOCK() iodrivers_base::Fixture::MockContext __context(this); + namespace iodrivers_base { + /** A fixture class designed to ease testing of iodrivers_base drivers in * boost and GTest * @@ -23,7 +37,7 @@ namespace iodrivers_base * { * MyDriver.openURI("test://"); * uint8_t buffer[4] = { 0, 1, 2, 3 }; - * pushDataToDevice(buffer, buffer + 2); + * pushDataToDriver(buffer, buffer + 2); * auto packet = readPacket(); * // Check that the packet matches the expected extraction * } @@ -39,13 +53,18 @@ namespace iodrivers_base * // Optional: open here * // driver.openURI("test://") * } + * + * virtual void TearDown() + * { + * tearDownMock(); + * } * } * * TEST_F(DriverTest, it_handles_an_invalid_packet) * { * MyDriver.openURI("test://"); * uint8_t buffer[4] = { 0, 1, 2, 3 }; - * pushDataToDevice(buffer, buffer + 2); + * pushDataToDriver(buffer, buffer + 2); * auto packet = readPacket(); * // Check that the packet matches the expected extraction * } @@ -91,7 +110,7 @@ namespace iodrivers_base { return getStream()->pushDataToDriver(data); } - + /** Helper method to allow passing any kind of uint8_t range * * @@ -105,7 +124,7 @@ namespace iodrivers_base std::vector buffer(begin, end); pushDataToDriver(buffer); } - + /** Read data that the driver sent to the device */ std::vector readDataFromDriver() @@ -132,6 +151,76 @@ namespace iodrivers_base { return driver.getStatus().queued_bytes; } + void EXPECT_REPLY(std::vector const& expectation, std::vector const& reply) + { + getStream()->EXPECT_REPLY(expectation,reply); + } + + /** + * Check if the test has any expectation set and throw if positive. + * It should be used to check if the test reached its end without + * any expecation left only + */ + void expectationsIsEmpty() + { + if(!getStream()->expectationsIsEmpty()) + throw TestEndsWithExcepetionsLeftException(); + } + + void enableMockMode() + { + getStream()->enableMockMode(); + } + + void clearExpectations() + { + getStream()->clearExpectations(); + } + + /** + * GTEST FAIL assertion can only be used in void-returning functions. + * Constructors and Destructors are not considered void-returning functions, + * according to the C++ language specification, and so you may not use fatal assertions in them. + * Using a Fatal assertion on these method would leave the object in a partially state. + * I was then decided to use the tear down to finalize the tests in GTEST. + * In BOOST the teardown method can be normally called in the destructor method + */ + void tearDownMock() + { + try + { + expectationsIsEmpty(); + } + catch(TestEndsWithExcepetionsLeftException e) + { + #ifdef IODRIVERS_BASE_FIXTURE_GTEST_FRAMEWORK + ADD_FAILURE() << "IODRIVERS_BASE_MOCK Error: Test reached its end without satisfying all expecations."; + #endif + #ifdef IODRIVERS_BASE_FIXTURE_BOOST_FRAMEWORK + BOOST_ERROR("IODRIVERS_BASE_MOCK Error: Test reached its end without satisfying all expecations."); + #endif + } + } + + class MockContext + { + public: + MockContext() {}; + Fixture* fixture; + MockContext(Fixture* fixture): + fixture(fixture) + { + fixture->enableMockMode(); + } + + ~MockContext() + { + #ifdef IODRIVERS_BASE_FIXTURE_BOOST_FRAMEWORK + fixture->tearDownMock(); + #endif + } + + }; }; } diff --git a/src/FixtureBoostTest.hpp b/src/FixtureBoostTest.hpp new file mode 100644 index 0000000..ccc926e --- /dev/null +++ b/src/FixtureBoostTest.hpp @@ -0,0 +1,3 @@ +#define IODRIVERS_BASE_FIXTURE_BOOST_FRAMEWORK 1 + +#include \ No newline at end of file diff --git a/src/FixtureGtest.hpp b/src/FixtureGtest.hpp new file mode 100644 index 0000000..5ee829c --- /dev/null +++ b/src/FixtureGtest.hpp @@ -0,0 +1,3 @@ +#define IODRIVERS_BASE_FIXTURE_GTEST_FRAMEWORK 1 + +#include \ No newline at end of file diff --git a/src/TestStream.cpp b/src/TestStream.cpp index 4c73695..9f2f40f 100644 --- a/src/TestStream.cpp +++ b/src/TestStream.cpp @@ -5,10 +5,10 @@ using namespace std; using namespace iodrivers_base; -/** Push data to the device */ +/** Push data to the driver */ void TestStream::pushDataToDriver(vector const& data) { - to_device.insert(to_device.end(), data.begin(), data.end()); + to_driver.insert(to_driver.end(), data.begin(), data.end()); } /** Read all data that the device driver has written since the last @@ -17,35 +17,89 @@ void TestStream::pushDataToDriver(vector const& data) vector TestStream::readDataFromDriver() { vector temp; - temp.swap(from_device); + temp.swap(from_driver); return temp; } void TestStream::waitRead(base::Time const& timeout) { - if (to_device.empty()) + if (to_driver.empty()) throw TimeoutError(TimeoutError::NONE, "no data in to_device"); } void TestStream::waitWrite(base::Time const& timeout) { } +void TestStream::EXPECT_REPLY(std::vector const& expectation, std::vector const& reply) +{ + if(!mock_mode) + throw MockContextException(); + std::vector exp; + exp.insert(exp.end(),expectation.begin(),expectation.end()); + std::vector rep = reply; + expectations.push_back(exp); + replies.push_back(reply); +} + + size_t TestStream::read(uint8_t* buffer, size_t buffer_size) { - size_t read_size = min(to_device.size(), buffer_size); - std::memcpy(buffer, to_device.data(), read_size); - to_device.erase(to_device.begin(), to_device.begin() + read_size); - return read_size; + size_t read_size = min(to_driver.size(), buffer_size); + std::memcpy(buffer, to_driver.data(), read_size); + to_driver.erase(to_driver.begin(), to_driver.begin() + read_size); + return read_size; } size_t TestStream::write(uint8_t const* buffer, size_t buffer_size) { - from_device.insert(from_device.end(), buffer, buffer + buffer_size); - return buffer_size; + if(mock_mode) + { + from_driver.clear(); + from_driver.insert(from_driver.end(), buffer, buffer + buffer_size); + if(expectations.size() > 0) + { + if(from_driver == expectations[0]) + { + to_driver.insert(to_driver.end(), replies[0].begin(), replies[0].end()); + expectations.erase(expectations.begin()); + replies.erase(replies.begin()); + } + else + { + expectations.erase(expectations.begin(),expectations.end()); + replies.erase(replies.begin(),replies.end()); + throw std::invalid_argument("Message received doesn't match expectation set"); + } + return buffer_size; + } + else + throw std::runtime_error("Message received without any expecation left."); + } + else + { + from_driver.insert(from_driver.end(), buffer, buffer + buffer_size); + return buffer_size; + } } void TestStream::clear() { - to_device.clear(); + to_driver.clear(); +} + +void TestStream::clearExpectations() +{ + expectations.clear(); + replies.clear(); +} + + +bool TestStream::expectationsIsEmpty() +{ + return(!expectations.size()>0); } +void TestStream::enableMockMode() +{ + mock_mode = true; +} \ No newline at end of file diff --git a/src/TestStream.hpp b/src/TestStream.hpp index 3c554c0..28792d9 100644 --- a/src/TestStream.hpp +++ b/src/TestStream.hpp @@ -17,10 +17,16 @@ namespace iodrivers_base class TestStream : public IOStream { - std::vector to_device; - std::vector from_device; + std::vector to_driver; + std::vector from_driver; + std::vector > expectations; + std::vector > replies; + bool mock_mode; public: + TestStream(): + mock_mode(false){} + /** Push data to the driver "as-if" it was coming from the device */ void pushDataToDriver(std::vector const& data); @@ -32,11 +38,21 @@ namespace iodrivers_base */ std::vector readDataFromDriver(); + /** + * Set a expectation and reply pair and also puts the driver in + * mock mode. Should be used after setting up IODRIVERS_BASE_MOCK(). + * @param expecation is the message that is expected to be sent by the driver + * @param reply reply message to be received by the driver + */ + void EXPECT_REPLY(std::vector const& expectation, std::vector const& reply); void waitRead(base::Time const& timeout); void waitWrite(base::Time const& timeout); size_t read(uint8_t* buffer, size_t buffer_size); size_t write(uint8_t const* buffer, size_t buffer_size); void clear(); + bool expectationsIsEmpty(); + void enableMockMode(); + void clearExpectations(); }; } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 9ee2faf..c554fdb 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,7 +1,12 @@ rock_testsuite(test_suite suite.cpp - test_Driver.cpp test_TestStream.cpp + test_Driver.cpp test_TestStream.cpp DEPS iodrivers_base) +rock_gtest(test_TestStreamGtest + test_TestStreamGtest.cpp + HEADERS + DEPS iodrivers_base) + rock_executable(test_tcp_read test_tcp_read.cpp DEPS iodrivers_base NOINSTALL) diff --git a/test/test_TestStream.cpp b/test/test_TestStream.cpp index 6275886..15394e7 100644 --- a/test/test_TestStream.cpp +++ b/test/test_TestStream.cpp @@ -1,7 +1,8 @@ #include #include -#include +#include +#include using namespace iodrivers_base; using namespace std; @@ -88,5 +89,82 @@ BOOST_FIXTURE_TEST_CASE(it_does_not_repeat_data_already_read_from_the_device, Fi BOOST_REQUIRE(received == vector(data + 2, data + 4)); } +BOOST_FIXTURE_TEST_CASE(it_matches_expecation_with_data_sent_to_device, Fixture) +{ + IODRIVERS_BASE_MOCK(); + uint8_t exp[] = { 0, 1, 2, 3 }; + uint8_t rep[] = { 3, 2, 1, 0 }; + EXPECT_REPLY(vector(exp, exp + 4),vector(rep, rep + 4)); + writePacket(exp,4); + vector received = readPacket(); + BOOST_REQUIRE(received == vector(rep,rep+4)); +} + +BOOST_FIXTURE_TEST_CASE(it_fails_expecation_with_data_sent_to_device, Fixture) +{ + IODRIVERS_BASE_MOCK(); + uint8_t exp[] = { 0, 1, 2, 3 }; + uint8_t msg[] = { 0, 1, 2, 4 }; + uint8_t rep[] = { 3, 2, 1, 0 }; + EXPECT_REPLY(vector(exp, exp + 4),vector(rep, rep + 4)); + BOOST_REQUIRE_THROW(writePacket(msg,4), std::invalid_argument); + +} + +BOOST_FIXTURE_TEST_CASE(it_tries_to_set_expectation_without_calling_mock_context, Fixture) +{ + uint8_t exp[] = { 0, 1, 2, 3 }; + uint8_t rep[] = { 3, 2, 1, 0 }; + BOOST_REQUIRE_THROW(EXPECT_REPLY(vector(exp, exp + 4),vector(rep, rep + 4)), MockContextException); +} + +BOOST_FIXTURE_TEST_CASE(it_matches_more_than_one_expecation, Fixture) +{ + IODRIVERS_BASE_MOCK(); + uint8_t exp1[] = { 0, 1, 2, 3 }; + uint8_t rep1[] = { 3, 2, 1, 0 }; + uint8_t exp2[] = { 0, 1, 2, 3, 4 }; + uint8_t rep2[] = { 4, 3, 2, 1, 0 }; + EXPECT_REPLY(vector(exp1, exp1 + 4),vector(rep1, rep1 + 4)); + EXPECT_REPLY(vector(exp2, exp2 + 5),vector(rep2, rep2 + 5)); + writePacket(exp1,4); + vector received_1 = readPacket(); + BOOST_REQUIRE(received_1 == vector(rep1,rep1+4)); + + writePacket(exp2,5); + vector received_2 = readPacket(); + for(size_t i =0; i(rep2,rep2+5)); +} + +BOOST_FIXTURE_TEST_CASE(it_does_not_matches_all_expecations, Fixture) +{ + IODRIVERS_BASE_MOCK(); + uint8_t exp1[] = { 0, 1, 2, 3 }; + uint8_t rep1[] = { 3, 2, 1, 0 }; + uint8_t exp2[] = { 0, 1, 2, 3, 4 }; + uint8_t rep2[] = { 4, 3, 2, 1, 0 }; + EXPECT_REPLY(vector(exp1, exp1 + 4),vector(rep1, rep1 + 4)); + EXPECT_REPLY(vector(exp2, exp2 + 5),vector(rep2, rep2 + 5)); + writePacket(exp1,4); + vector received_1 = readPacket(); + BOOST_REQUIRE(received_1 == vector(rep1,rep1+4)); + BOOST_REQUIRE_THROW(expectationsIsEmpty(), TestEndsWithExcepetionsLeftException); + clearExpectations(); +} + +BOOST_FIXTURE_TEST_CASE(it_sends_more_messages_than_expecations_set, Fixture) +{ + IODRIVERS_BASE_MOCK(); + uint8_t exp1[] = { 0, 1, 2, 3 }; + uint8_t rep1[] = { 3, 2, 1, 0 }; + uint8_t exp2[] = { 0, 1, 2, 3, 4 }; + EXPECT_REPLY(vector(exp1, exp1 + 4),vector(rep1, rep1 + 4)); + writePacket(exp1,4); + vector received_1 = readPacket(); + BOOST_REQUIRE(received_1 == vector(rep1,rep1+4)); + + BOOST_REQUIRE_THROW(writePacket(exp2,5),std::runtime_error); +} BOOST_AUTO_TEST_SUITE_END() diff --git a/test/test_TestStreamGtest.cpp b/test/test_TestStreamGtest.cpp new file mode 100644 index 0000000..35005ec --- /dev/null +++ b/test/test_TestStreamGtest.cpp @@ -0,0 +1,117 @@ +#include +#include +#include +#include +#include + +using namespace std; + +struct Driver : iodrivers_base::Driver +{ +public: + Driver() + : iodrivers_base::Driver(100) {} + + int extractPacket(uint8_t const* buffer, size_t size) const + { + return size; + } +}; + +struct DriverTest : public ::testing::Test, public iodrivers_base::Fixture +{ + DriverTest() + { + driver.openURI("test://"); + } + + virtual void TearDown() + { + tearDownMock(); + } + +}; + +TEST_F(DriverTest, it_matches_expecation_with_data_sent_to_device) +{ + IODRIVERS_BASE_MOCK(); + uint8_t exp[] = { 0, 1, 2, 3 }; + uint8_t rep[] = { 3, 2, 1, 0 }; + EXPECT_REPLY(vector(exp, exp + 4),vector(rep, rep + 4)); + writePacket(exp,4); + vector received = readPacket(); + ASSERT_EQ(received, vector(rep,rep+4)); +} + +TEST_F(DriverTest, it_fails_expecation_with_data_sent_to_device) +{ + IODRIVERS_BASE_MOCK(); + uint8_t exp[] = { 0, 1, 2, 3 }; + uint8_t msg[] = { 0, 1, 2, 4 }; + uint8_t rep[] = { 3, 2, 1, 0 }; + EXPECT_REPLY(vector(exp, exp + 4),vector(rep, rep + 4)); + EXPECT_THROW(writePacket(msg,4), std::invalid_argument); + +} + +TEST_F(DriverTest, it_tries_to_set_expectation_without_calling_mock_context) +{ + uint8_t exp[] = { 0, 1, 2, 3 }; + uint8_t rep[] = { 3, 2, 1, 0 }; + EXPECT_THROW(EXPECT_REPLY(vector(exp, exp + 4),vector(rep, rep + 4)), MockContextException); +} + +TEST_F(DriverTest, it_matches_expecation_more_than_one_expecation) +{ + IODRIVERS_BASE_MOCK(); + uint8_t exp1[] = { 0, 1, 2, 3 }; + uint8_t rep1[] = { 3, 2, 1, 0 }; + uint8_t exp2[] = { 0, 1, 2, 3, 4 }; + uint8_t rep2[] = { 4, 3, 2, 1, 0 }; + EXPECT_REPLY(vector(exp1, exp1 + 4),vector(rep1, rep1 + 4)); + EXPECT_REPLY(vector(exp2, exp2 + 5),vector(rep2, rep2 + 5)); + writePacket(exp1,4); + vector received_1 = readPacket(); + ASSERT_EQ(received_1, vector(rep1,rep1+4)); + + writePacket(exp2,5); + vector received_2 = readPacket(); + for(size_t i =0; i(rep2,rep2+5)); +} + +TEST_F(DriverTest, it_does_not_matches_all_expecations) +{ + IODRIVERS_BASE_MOCK(); + uint8_t exp1[] = { 0, 1, 2, 3 }; + uint8_t rep1[] = { 3, 2, 1, 0 }; + uint8_t exp2[] = { 0, 1, 2, 3, 4 }; + uint8_t rep2[] = { 4, 3, 2, 1, 0 }; + EXPECT_REPLY(vector(exp1, exp1 + 4),vector(rep1, rep1 + 4)); + EXPECT_REPLY(vector(exp2, exp2 + 5),vector(rep2, rep2 + 5)); + writePacket(exp1,4); + vector received_1 = readPacket(); + ASSERT_EQ(received_1, vector(rep1,rep1+4)); + EXPECT_NONFATAL_FAILURE(tearDownMock(),"IODRIVERS_BASE_MOCK Error: Test reached its end without satisfying all expecations."); + clearExpectations(); +} + +TEST_F(DriverTest ,it_sends_more_messages_than_expecations_set) +{ + IODRIVERS_BASE_MOCK(); + uint8_t exp1[] = { 0, 1, 2, 3 }; + uint8_t rep1[] = { 3, 2, 1, 0 }; + uint8_t exp2[] = { 0, 1, 2, 3, 4 }; + EXPECT_REPLY(vector(exp1, exp1 + 4),vector(rep1, rep1 + 4)); + writePacket(exp1,4); + vector received_1 = readPacket(); + ASSERT_EQ(received_1, vector(rep1,rep1+4)); + + ASSERT_THROW(writePacket(exp2,5),std::runtime_error); +} + +int main(int argc, char **argv) +{ + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} From edf014bdd4f2794f255bc4a310241d7abba36bd9 Mon Sep 17 00:00:00 2001 From: Sylvain Joyeux Date: Thu, 10 Aug 2017 09:52:01 -0300 Subject: [PATCH 06/15] replace MockContext class by test framework specific ones Instead of implement one MockContext class that changes it functionality depending if GTest or BOOST is being used, it was decided to split it in two specific classes to make the code better to understand. --- src/CMakeLists.txt | 6 +- src/Exceptions.hpp | 2 +- src/Fixture.hpp | 73 +++---------------- src/FixtureBoostTest.hpp | 42 ++++++++++- src/FixtureGTest.hpp | 49 +++++++++++++ src/FixtureGtest.hpp | 3 - src/TestStream.cpp | 51 ++++++------- src/TestStream.hpp | 13 ++-- test/CMakeLists.txt | 4 +- test/test_TestStream.cpp | 2 +- ...reamGtest.cpp => test_TestStreamGTest.cpp} | 5 +- 11 files changed, 137 insertions(+), 113 deletions(-) create mode 100644 src/FixtureGTest.hpp delete mode 100644 src/FixtureGtest.hpp rename test/{test_TestStreamGtest.cpp => test_TestStreamGTest.cpp} (94%) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d99a9b2..8cfe367 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -5,11 +5,11 @@ find_package(Threads REQUIRED) rock_library(iodrivers_base SOURCES Driver.cpp Bus.cpp Timeout.cpp IOStream.cpp Exceptions.cpp TCPDriver.cpp - IOListener.cpp TestStream.cpp + IOListener.cpp TestStream.cpp HEADERS Driver.hpp Bus.hpp Timeout.hpp Status.hpp IOStream.hpp Exceptions.hpp IOListener.hpp TCPDriver.hpp TestStream.hpp - Fixture.hpp FixtureBoostTest.hpp FixtureGtest.hpp - LIBS ${Boost_THREAD_LIBRARY} ${Boost_SYSTEM_LIBRARY} ${CMAKE_THREAD_LIBS_INIT} + Fixture.hpp FixtureBoostTest.hpp FixtureGTest.hpp + LIBS ${Boost_THREAD_LIBRARY} ${Boost_SYSTEM_LIBRARY} ${CMAKE_THREAD_LIBS_INIT} DEPS_PKGCONFIG base-types base-lib) # For backward compatibility only diff --git a/src/Exceptions.hpp b/src/Exceptions.hpp index 2db190e..73be98c 100644 --- a/src/Exceptions.hpp +++ b/src/Exceptions.hpp @@ -37,7 +37,7 @@ class MockContextException : public std::exception public: const char * what() const throw() { - return "IODRIVERS_BASE_MOCK Error: Expectation set outside Mock Context! Please call IODRIVERS_BASE_MOCK() before setting expecations\n"; + return "IODRIVERS_BASE_MOCK Error: Expectation set outside Mock Context! Please call IODRIVERS_BASE_MOCK() before setting expectations\n"; } }; diff --git a/src/Fixture.hpp b/src/Fixture.hpp index b33a359..7f03840 100644 --- a/src/Fixture.hpp +++ b/src/Fixture.hpp @@ -5,21 +5,8 @@ #include #include - -#ifdef IODRIVERS_BASE_FIXTURE_BOOST_FRAMEWORK - #include -#endif - -#ifdef IODRIVERS_BASE_FIXTURE_GTEST_FRAMEWORK - #include -#endif - -#define IODRIVERS_BASE_MOCK() iodrivers_base::Fixture::MockContext __context(this); - - namespace iodrivers_base { - /** A fixture class designed to ease testing of iodrivers_base drivers in * boost and GTest * @@ -161,66 +148,24 @@ namespace iodrivers_base * It should be used to check if the test reached its end without * any expecation left only */ - void expectationsIsEmpty() + void validateExpectationsAreEmpty() { - if(!getStream()->expectationsIsEmpty()) - throw TestEndsWithExcepetionsLeftException(); + if(!getStream()->expectationsAreEmpty()) + throw TestEndsWithExpectationsLeftException(); } - - void enableMockMode() + + void setMockMode(bool mode) { - getStream()->enableMockMode(); + getStream()->setMockMode(mode); } - + void clearExpectations() { getStream()->clearExpectations(); } - - /** - * GTEST FAIL assertion can only be used in void-returning functions. - * Constructors and Destructors are not considered void-returning functions, - * according to the C++ language specification, and so you may not use fatal assertions in them. - * Using a Fatal assertion on these method would leave the object in a partially state. - * I was then decided to use the tear down to finalize the tests in GTEST. - * In BOOST the teardown method can be normally called in the destructor method - */ - void tearDownMock() - { - try - { - expectationsIsEmpty(); - } - catch(TestEndsWithExcepetionsLeftException e) - { - #ifdef IODRIVERS_BASE_FIXTURE_GTEST_FRAMEWORK - ADD_FAILURE() << "IODRIVERS_BASE_MOCK Error: Test reached its end without satisfying all expecations."; - #endif - #ifdef IODRIVERS_BASE_FIXTURE_BOOST_FRAMEWORK - BOOST_ERROR("IODRIVERS_BASE_MOCK Error: Test reached its end without satisfying all expecations."); - #endif - } - } - - class MockContext - { - public: - MockContext() {}; - Fixture* fixture; - MockContext(Fixture* fixture): - fixture(fixture) - { - fixture->enableMockMode(); - } - ~MockContext() - { - #ifdef IODRIVERS_BASE_FIXTURE_BOOST_FRAMEWORK - fixture->tearDownMock(); - #endif - } - - }; + class GTestMockContext; + class BoostMockContext; }; } diff --git a/src/FixtureBoostTest.hpp b/src/FixtureBoostTest.hpp index ccc926e..9a936e0 100644 --- a/src/FixtureBoostTest.hpp +++ b/src/FixtureBoostTest.hpp @@ -1,3 +1,41 @@ -#define IODRIVERS_BASE_FIXTURE_BOOST_FRAMEWORK 1 +#ifndef IODRIVERS_BASE_BOOST_HPP +#define IODRIVERS_BASE_BOOST_HPP -#include \ No newline at end of file +#include +#include + +#define IODRIVERS_BASE_MOCK() iodrivers_base::Fixture::BoostMockContext __context(this); + +namespace iodrivers_base { + template + class Fixture::BoostMockContext + { + public: + Fixture* fixture; + BoostMockContext(Fixture* fixture): fixture(fixture) + { + fixture->setMockMode(true); + } + + void tearDown() + { + try + { + fixture->validateExpectationsAreEmpty(); + } + catch(TestEndsWithExpectationsLeftException e) + { + BOOST_ERROR("IODRIVERS_BASE_MOCK Error: Test reached its end without satisfying all expecations."); + } + } + + ~BoostMockContext() + { + tearDown(); + fixture->setMockMode(false); + } + + }; +} + +#endif diff --git a/src/FixtureGTest.hpp b/src/FixtureGTest.hpp new file mode 100644 index 0000000..b24e030 --- /dev/null +++ b/src/FixtureGTest.hpp @@ -0,0 +1,49 @@ +#ifndef IODRIVERS_BASE_GTEST_HPP +#define IODRIVERS_BASE_GTEST_HPP + +#include +#include + +#define IODRIVERS_BASE_MOCK() iodrivers_base::Fixture::GTestMockContext __context(this); + +namespace iodrivers_base { + template + class Fixture::GTestMockContext + { + public: + Fixture* fixture; + GTestMockContext(Fixture* fixture): fixture(fixture) + { + fixture->setMockMode(true); + } + /** + * GTEST FAIL assertion can only be used in void-returning functions. + * Constructors and Destructors are not considered void-returning functions, + * according to the C++ language specification, and so you may not use fatal assertions in them. + * Using a Fatal assertion on these method would leave the object in a partially state. + * I was then decided to use the tear down to finalize the tests in GTEST. + * In BOOST the teardown method can be normally called in the destructor method + */ + void tearDown() + { + try + { + fixture->validateExpectationsAreEmpty(); + } + catch(TestEndsWithExpectationsLeftException e) + { + ADD_FAILURE() << "IODRIVERS_BASE_MOCK Error: Test reached its end without satisfying all expecations."; + } + } + + ~GTestMockContext() + { + fixture->setMockMode(false); + fixture->clearExpectations(); + tearDown(); + } + + }; +} + +#endif diff --git a/src/FixtureGtest.hpp b/src/FixtureGtest.hpp deleted file mode 100644 index 5ee829c..0000000 --- a/src/FixtureGtest.hpp +++ /dev/null @@ -1,3 +0,0 @@ -#define IODRIVERS_BASE_FIXTURE_GTEST_FRAMEWORK 1 - -#include \ No newline at end of file diff --git a/src/TestStream.cpp b/src/TestStream.cpp index 9f2f40f..b1b87e1 100644 --- a/src/TestStream.cpp +++ b/src/TestStream.cpp @@ -34,20 +34,17 @@ void TestStream::EXPECT_REPLY(std::vector const& expectation, std::vect { if(!mock_mode) throw MockContextException(); - std::vector exp; - exp.insert(exp.end(),expectation.begin(),expectation.end()); - std::vector rep = reply; - expectations.push_back(exp); + expectations.push_back(expectation); replies.push_back(reply); } size_t TestStream::read(uint8_t* buffer, size_t buffer_size) { - size_t read_size = min(to_driver.size(), buffer_size); - std::memcpy(buffer, to_driver.data(), read_size); - to_driver.erase(to_driver.begin(), to_driver.begin() + read_size); - return read_size; + size_t read_size = min(to_driver.size(), buffer_size); + std::memcpy(buffer, to_driver.data(), read_size); + to_driver.erase(to_driver.begin(), to_driver.begin() + read_size); + return read_size; } size_t TestStream::write(uint8_t const* buffer, size_t buffer_size) @@ -56,24 +53,22 @@ size_t TestStream::write(uint8_t const* buffer, size_t buffer_size) { from_driver.clear(); from_driver.insert(from_driver.end(), buffer, buffer + buffer_size); - if(expectations.size() > 0) - { - if(from_driver == expectations[0]) - { - to_driver.insert(to_driver.end(), replies[0].begin(), replies[0].end()); - expectations.erase(expectations.begin()); - replies.erase(replies.begin()); - } - else - { - expectations.erase(expectations.begin(),expectations.end()); - replies.erase(replies.begin(),replies.end()); - throw std::invalid_argument("Message received doesn't match expectation set"); - } - return buffer_size; + if(expectations.empty()) + throw std::runtime_error("Message received without any expecation set/left."); + + if(from_driver == expectations.front()) + { + to_driver.insert(to_driver.end(), replies.front().begin(), replies.front().end()); + expectations.pop_front(); + replies.pop_front(); } else - throw std::runtime_error("Message received without any expecation left."); + { + expectations.clear(); + replies.clear(); + throw std::invalid_argument("Message received doesn't the given expectations"); + } + return buffer_size; } else { @@ -94,12 +89,12 @@ void TestStream::clearExpectations() } -bool TestStream::expectationsIsEmpty() +bool TestStream::expectationsAreEmpty() { - return(!expectations.size()>0); + return(expectations.empty()); } -void TestStream::enableMockMode() +void TestStream::setMockMode(bool mode) { - mock_mode = true; + mock_mode = mode; } \ No newline at end of file diff --git a/src/TestStream.hpp b/src/TestStream.hpp index 28792d9..69ea31b 100644 --- a/src/TestStream.hpp +++ b/src/TestStream.hpp @@ -3,6 +3,7 @@ #include #include +#include namespace iodrivers_base { @@ -19,13 +20,13 @@ namespace iodrivers_base { std::vector to_driver; std::vector from_driver; - std::vector > expectations; - std::vector > replies; + std::list > expectations; + std::list > replies; bool mock_mode; public: - TestStream(): - mock_mode(false){} + TestStream(): mock_mode(false) + {} /** Push data to the driver "as-if" it was coming from the device */ @@ -50,8 +51,8 @@ namespace iodrivers_base size_t read(uint8_t* buffer, size_t buffer_size); size_t write(uint8_t const* buffer, size_t buffer_size); void clear(); - bool expectationsIsEmpty(); - void enableMockMode(); + bool expectationsAreEmpty(); + void setMockMode(bool mode); void clearExpectations(); }; } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index c554fdb..9cc92da 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -2,8 +2,8 @@ rock_testsuite(test_suite suite.cpp test_Driver.cpp test_TestStream.cpp DEPS iodrivers_base) -rock_gtest(test_TestStreamGtest - test_TestStreamGtest.cpp +rock_gtest(test_TestStreamGTest + test_TestStreamGTest.cpp HEADERS DEPS iodrivers_base) diff --git a/test/test_TestStream.cpp b/test/test_TestStream.cpp index 15394e7..c68b5f0 100644 --- a/test/test_TestStream.cpp +++ b/test/test_TestStream.cpp @@ -149,7 +149,7 @@ BOOST_FIXTURE_TEST_CASE(it_does_not_matches_all_expecations, Fixture) writePacket(exp1,4); vector received_1 = readPacket(); BOOST_REQUIRE(received_1 == vector(rep1,rep1+4)); - BOOST_REQUIRE_THROW(expectationsIsEmpty(), TestEndsWithExcepetionsLeftException); + BOOST_REQUIRE_THROW(validateExpectationsAreEmpty(), TestEndsWithExpectationsLeftException); clearExpectations(); } diff --git a/test/test_TestStreamGtest.cpp b/test/test_TestStreamGTest.cpp similarity index 94% rename from test/test_TestStreamGtest.cpp rename to test/test_TestStreamGTest.cpp index 35005ec..0090d6a 100644 --- a/test/test_TestStreamGtest.cpp +++ b/test/test_TestStreamGTest.cpp @@ -1,7 +1,7 @@ #include #include #include -#include +#include #include using namespace std; @@ -27,7 +27,6 @@ struct DriverTest : public ::testing::Test, public iodrivers_base::Fixture received_1 = readPacket(); ASSERT_EQ(received_1, vector(rep1,rep1+4)); - EXPECT_NONFATAL_FAILURE(tearDownMock(),"IODRIVERS_BASE_MOCK Error: Test reached its end without satisfying all expecations."); + EXPECT_NONFATAL_FAILURE(__context.tearDown(),"IODRIVERS_BASE_MOCK Error: Test reached its end without satisfying all expecations."); clearExpectations(); } From 21b294738cf59e2721bf8da393e5b5d916ec24cc Mon Sep 17 00:00:00 2001 From: Sylvain Joyeux Date: Thu, 10 Aug 2017 09:52:17 -0300 Subject: [PATCH 07/15] document IODRIVERS_BASE_MOCK --- src/Fixture.hpp | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/Fixture.hpp b/src/Fixture.hpp index 7f03840..3cccdb5 100644 --- a/src/Fixture.hpp +++ b/src/Fixture.hpp @@ -41,10 +41,6 @@ namespace iodrivers_base * // driver.openURI("test://") * } * - * virtual void TearDown() - * { - * tearDownMock(); - * } * } * * TEST_F(DriverTest, it_handles_an_invalid_packet) @@ -56,6 +52,25 @@ namespace iodrivers_base * // Check that the packet matches the expected extraction * } * + * + * Mock Mode: + * To mock the behavior of the device in situations which a reply + * is expected, the mock mode should be set, using IODRIVERS_BASE_MOCK(). + * The expectations are set usint EXPECT_REPLY(expectation, reply) and + * multiple expecations can be set. The mock will check the expecations and + * reply in the order that they were defined and will raise an error if + * any of them is not met. Mock mode is available in both BOOST and GTest + * Frameworks. + * + * + *IODRIVER_BASE_MOCK() + *uint8_t exp[] = { 0, 1, 2, 3 }; + *uint8_t rep[] = { 3, 2, 1, 0 }; + *EXPECT_REPLY(vector(exp, exp + 4), + * vector(rep, rep + 4)); + *writePacket(exp,4); + * + * */ template struct Fixture From 6cd2fe4853ad23e143a292ae373cca20ddcc3a95 Mon Sep 17 00:00:00 2001 From: Sylvain Joyeux Date: Thu, 10 Aug 2017 09:45:45 -0300 Subject: [PATCH 08/15] improve error message on expectation failure --- src/TestStream.cpp | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/TestStream.cpp b/src/TestStream.cpp index b1b87e1..7aa6ab8 100644 --- a/src/TestStream.cpp +++ b/src/TestStream.cpp @@ -1,6 +1,8 @@ #include #include #include +#include +#include using namespace std; using namespace iodrivers_base; @@ -54,7 +56,13 @@ size_t TestStream::write(uint8_t const* buffer, size_t buffer_size) from_driver.clear(); from_driver.insert(from_driver.end(), buffer, buffer + buffer_size); if(expectations.empty()) - throw std::runtime_error("Message received without any expecation set/left."); + { + std::stringstream msg; + msg << "Message received, but there are no expectations left:\n"; + for (std::vector::const_iterator it = from_driver.begin(); it != from_driver.end(); ++it) + msg << " " << setfill('0') << setw(2) << hex << static_cast(*it); + throw std::runtime_error(msg.str()); + } if(from_driver == expectations.front()) { @@ -64,9 +72,19 @@ size_t TestStream::write(uint8_t const* buffer, size_t buffer_size) } else { + std::vector const& expected = expectations.front(); + std::stringstream msg; + msg << "IODRIVERS_BASE_MOCK failure"; + msg << "\nExpected"; + for (std::vector::const_iterator it = expected.begin(); it != expected.end(); ++it) + msg << " " << setfill('0') << setw(2) << hex << static_cast(*it); + msg << "\nBut got "; + for (std::vector::const_iterator it = from_driver.begin(); it != from_driver.end(); ++it) + msg << " " << setfill('0') << setw(2) << hex << static_cast(*it); + expectations.clear(); replies.clear(); - throw std::invalid_argument("Message received doesn't the given expectations"); + throw std::invalid_argument(msg.str()); } return buffer_size; } From 8d774834edd49280746f2178ad67b3dfa8c3bead Mon Sep 17 00:00:00 2001 From: Sylvain Joyeux Date: Thu, 10 Aug 2017 10:31:04 -0300 Subject: [PATCH 09/15] do not assume that the driver class is "Driver" in IODRIVERS_BASE_MOCK() --- src/Fixture.hpp | 2 ++ src/FixtureBoostTest.hpp | 2 +- src/FixtureGTest.hpp | 2 +- test/test_TestStream.cpp | 28 ++++++++++++++++++++++++++++ test/test_TestStreamGTest.cpp | 27 +++++++++++++++++++++++++++ 5 files changed, 59 insertions(+), 2 deletions(-) diff --git a/src/Fixture.hpp b/src/Fixture.hpp index 3cccdb5..eb982b5 100644 --- a/src/Fixture.hpp +++ b/src/Fixture.hpp @@ -75,6 +75,8 @@ namespace iodrivers_base template struct Fixture { + typedef Driver fixture_driver_t; + std::vector packetBuffer; Driver driver; diff --git a/src/FixtureBoostTest.hpp b/src/FixtureBoostTest.hpp index 9a936e0..e44afa5 100644 --- a/src/FixtureBoostTest.hpp +++ b/src/FixtureBoostTest.hpp @@ -4,7 +4,7 @@ #include #include -#define IODRIVERS_BASE_MOCK() iodrivers_base::Fixture::BoostMockContext __context(this); +#define IODRIVERS_BASE_MOCK() iodrivers_base::Fixture::BoostMockContext __context(this); namespace iodrivers_base { template diff --git a/src/FixtureGTest.hpp b/src/FixtureGTest.hpp index b24e030..9c24ea2 100644 --- a/src/FixtureGTest.hpp +++ b/src/FixtureGTest.hpp @@ -4,7 +4,7 @@ #include #include -#define IODRIVERS_BASE_MOCK() iodrivers_base::Fixture::GTestMockContext __context(this); +#define IODRIVERS_BASE_MOCK() iodrivers_base::Fixture::GTestMockContext __context(this); namespace iodrivers_base { template diff --git a/test/test_TestStream.cpp b/test/test_TestStream.cpp index c68b5f0..ec903fd 100644 --- a/test/test_TestStream.cpp +++ b/test/test_TestStream.cpp @@ -166,5 +166,33 @@ BOOST_FIXTURE_TEST_CASE(it_sends_more_messages_than_expecations_set, Fixture) BOOST_REQUIRE_THROW(writePacket(exp2,5),std::runtime_error); } + +struct DriverClassNameDriver : Driver +{ + virtual int extractPacket(uint8_t const* buffer, size_t buffer_length) const + { + return std::min(buffer_length, static_cast(1)); + } +}; + +struct DriverClassNameTestFixture : iodrivers_base::Fixture +{ +}; + +BOOST_FIXTURE_TEST_CASE(the_mock_mode_can_be_used_with_a_driver_class_not_called_Driver, DriverClassNameTestFixture) +{ + IODRIVERS_BASE_MOCK(); + uint8_t exp[] = { 0, 1, 2, 3 }; + uint8_t rep[] = { 3, 2, 1, 0 }; + EXPECT_REPLY(vector(exp, exp + 4),vector(rep, rep + 4)); + writePacket(exp,4); + vector received = readPacket(); + + // This is an indirect test for the type of the driver used in the test. The + // DriverClassNameDriver returns one-byte "packets" while Driver returns the + // whole buffer + BOOST_REQUIRE_EQUAL(1, received.size()); +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/test/test_TestStreamGTest.cpp b/test/test_TestStreamGTest.cpp index 0090d6a..99e9d51 100644 --- a/test/test_TestStreamGTest.cpp +++ b/test/test_TestStreamGTest.cpp @@ -109,6 +109,33 @@ TEST_F(DriverTest ,it_sends_more_messages_than_expecations_set) ASSERT_THROW(writePacket(exp2,5),std::runtime_error); } +struct DriverClassNameDriver : Driver +{ + virtual int extractPacket(uint8_t const* buffer, size_t buffer_length) const + { + return min(buffer_length, static_cast(1)); + } +}; + +struct DriverClassNameTestFixture : ::testing::Test, iodrivers_base::Fixture +{ +}; + +TEST_F(DriverClassNameTestFixture, the_mock_mode_can_be_used_with_a_driver_class_not_called_Driver) +{ + IODRIVERS_BASE_MOCK(); + uint8_t exp[] = { 0, 1, 2, 3 }; + uint8_t rep[] = { 3, 2, 1, 0 }; + EXPECT_REPLY(vector(exp, exp + 4),vector(rep, rep + 4)); + writePacket(exp,4); + vector received = readPacket(); + + // This is an indirect test for the type of the driver used in the test. The + // DriverClassNameDriver returns one-byte "packets" while Driver returns the + // whole buffer + ASSERT_EQ(1, received.size()); +} + int main(int argc, char **argv) { testing::InitGoogleTest(&argc, argv); From 864453171123c995f2c668150e0fecbdf717082a Mon Sep 17 00:00:00 2001 From: Sylvain Joyeux Date: Thu, 10 Aug 2017 10:30:01 -0300 Subject: [PATCH 10/15] give the means to test openURI itself The problem here was that openURI was the one creating the test stream, so the fixture class could not pass data to it. The change is to allow creation of the Note that it also allows to test "lingering data" handling, which may happen when the device sent data either before the device was opened (on HW buffered lines like serial), or between the open and the first read. It also handles situations where some data was not read after the last close. --- src/Driver.cpp | 8 +++++- src/Driver.hpp | 16 +++++++++++ src/Fixture.hpp | 4 ++- test/test_TestStream.cpp | 50 +++++++++++++++++++++++++++++++++++ test/test_TestStreamGTest.cpp | 26 ++++++++++++++++++ 5 files changed, 102 insertions(+), 2 deletions(-) diff --git a/src/Driver.cpp b/src/Driver.cpp index 6e0ada9..b3c4abf 100644 --- a/src/Driver.cpp +++ b/src/Driver.cpp @@ -225,10 +225,16 @@ void Driver::openURI(std::string const& uri) } else if (mode_idx == 5) { // test:// - setMainStream(new TestStream); + if (!dynamic_cast(getMainStream())) + openTestMode(); } } +void Driver::openTestMode() +{ + setMainStream(new TestStream); +} + bool Driver::openSerial(std::string const& port, int baud_rate) { setFileDescriptor(Driver::openSerialIO(port, baud_rate)); diff --git a/src/Driver.hpp b/src/Driver.hpp index ef0940c..af59e05 100644 --- a/src/Driver.hpp +++ b/src/Driver.hpp @@ -439,6 +439,22 @@ class Driver */ IOStream* getMainStream() const; + /** Open the IO in test mode + * + * It is mostly equivalent to openURI("test://"), but is not meant to be + * overloaded, allowing to test openURI: + * + * + * openTestMode() + * // Feed data to the driver + * openURI(); + * + * Moreover, it will always create + * a new test "channel", while openURI will create a new one only if the + * current main stream is not a test stream already. + */ + void openTestMode(); + /** Add a listener stream. The object's ownership is taken by the Driver * object. */ diff --git a/src/Fixture.hpp b/src/Fixture.hpp index eb982b5..1512b5e 100644 --- a/src/Fixture.hpp +++ b/src/Fixture.hpp @@ -87,8 +87,10 @@ namespace iodrivers_base /** Get the underlying TestStream */ - TestStream* getStream() const + TestStream* getStream() { + if (!driver.getMainStream()) + driver.openTestMode(); return dynamic_cast(driver.getMainStream()); } diff --git a/test/test_TestStream.cpp b/test/test_TestStream.cpp index ec903fd..9f2fae3 100644 --- a/test/test_TestStream.cpp +++ b/test/test_TestStream.cpp @@ -15,6 +15,19 @@ struct Driver : iodrivers_base::Driver Driver() : iodrivers_base::Driver(100) {} + std::vector openURIData; + + void openURI(std::string const& uri) + { + iodrivers_base::Driver::openURI(uri); + uint8_t buffer[100]; + try { + size_t packet_size = readPacket(buffer, 100); + openURIData = std::vector(buffer, buffer + packet_size); + } catch (iodrivers_base::TimeoutError) { + } + } + int extractPacket(uint8_t const* buffer, size_t size) const { return size; @@ -29,6 +42,17 @@ struct Fixture : iodrivers_base::Fixture } }; +struct FixtureNoOpen : iodrivers_base::Fixture +{ +}; + +BOOST_FIXTURE_TEST_CASE(it_allows_to_send_data_for_the_benefit_of_openURI_itself, FixtureNoOpen) +{ + uint8_t data[] = { 0, 1, 2, 3 }; + pushDataToDriver(data, data + 4); + driver.openURI("test://"); + BOOST_REQUIRE(driver.openURIData == vector(data, data + 4)); +} BOOST_FIXTURE_TEST_CASE(it_sends_data_to_the_Driver, Fixture) { @@ -194,5 +218,31 @@ BOOST_FIXTURE_TEST_CASE(the_mock_mode_can_be_used_with_a_driver_class_not_called BOOST_REQUIRE_EQUAL(1, received.size()); } +struct openURIMockTestDriver : public Driver +{ + vector open(string const& uri) + { + Driver::openURI(uri); + uint8_t data[4] = { 0, 1, 2, 3 }; + writePacket(data, 4); + + uint8_t read[100]; + size_t packet_size = readPacket(read, 100); + return vector(read, read + packet_size); + } +}; + +struct openURIMockTestFixture : iodrivers_base::Fixture +{ +}; + +BOOST_FIXTURE_TEST_CASE(the_mock_mode_can_be_used_to_test_openURI_itself, openURIMockTestFixture) +{ IODRIVERS_BASE_MOCK(); + uint8_t data[] = { 0, 1, 2, 3 }; + vector packet(data, data + 4); + EXPECT_REPLY(packet, packet); + BOOST_REQUIRE(packet == driver.open("test://")); +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/test/test_TestStreamGTest.cpp b/test/test_TestStreamGTest.cpp index 99e9d51..7f3780d 100644 --- a/test/test_TestStreamGTest.cpp +++ b/test/test_TestStreamGTest.cpp @@ -136,6 +136,32 @@ TEST_F(DriverClassNameTestFixture, the_mock_mode_can_be_used_with_a_driver_class ASSERT_EQ(1, received.size()); } +struct openURIMockTestDriver : public Driver +{ + vector open(string const& uri) + { + Driver::openURI(uri); + uint8_t data[4] = { 0, 1, 2, 3 }; + writePacket(data, 4); + + uint8_t read[100]; + size_t packet_size = readPacket(read, 100); + return vector(read, read + packet_size); + } +}; + +struct openURIMockTestFixture : ::testing::Test, iodrivers_base::Fixture +{ +}; + +TEST_F(openURIMockTestFixture, the_mock_mode_can_be_used_to_test_openURI_itself) +{ IODRIVERS_BASE_MOCK(); + uint8_t data[] = { 0, 1, 2, 3 }; + vector packet(data, data + 4); + EXPECT_REPLY(packet, packet); + ASSERT_EQ(packet, driver.open("test://")); +} + int main(int argc, char **argv) { testing::InitGoogleTest(&argc, argv); From 5f899c6d1f37a91f358c69eb25f862f943575a5a Mon Sep 17 00:00:00 2001 From: Sylvain Joyeux Date: Thu, 10 Aug 2017 10:30:32 -0300 Subject: [PATCH 11/15] test: remove unnecessary std:: qualifications --- test/test_TestStream.cpp | 18 +++++++++--------- test/test_TestStreamGTest.cpp | 4 ++-- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/test/test_TestStream.cpp b/test/test_TestStream.cpp index 9f2fae3..5e643f1 100644 --- a/test/test_TestStream.cpp +++ b/test/test_TestStream.cpp @@ -15,15 +15,15 @@ struct Driver : iodrivers_base::Driver Driver() : iodrivers_base::Driver(100) {} - std::vector openURIData; + vector openURIData; - void openURI(std::string const& uri) + void openURI(string const& uri) { iodrivers_base::Driver::openURI(uri); uint8_t buffer[100]; try { size_t packet_size = readPacket(buffer, 100); - openURIData = std::vector(buffer, buffer + packet_size); + openURIData = vector(buffer, buffer + packet_size); } catch (iodrivers_base::TimeoutError) { } } @@ -90,7 +90,7 @@ BOOST_FIXTURE_TEST_CASE(it_gives_access_to_the_bytes_sent_by_the_driver, Fixture { uint8_t data[] = { 0, 1, 2, 3 }; writePacket(data, 4); - std::vector received = readDataFromDriver(); + vector received = readDataFromDriver(); BOOST_REQUIRE(received == vector(data, data + 4)); } @@ -99,7 +99,7 @@ BOOST_FIXTURE_TEST_CASE(it_accumulates_unread_bytes, Fixture) uint8_t data[] = { 0, 1, 2, 3 }; writePacket(data, 2); writePacket(data + 2, 2); - std::vector received = readDataFromDriver(); + vector received = readDataFromDriver(); BOOST_REQUIRE(received == vector(data, data + 4)); } @@ -109,7 +109,7 @@ BOOST_FIXTURE_TEST_CASE(it_does_not_repeat_data_already_read_from_the_device, Fi writePacket(data, 2); readDataFromDriver(); writePacket(data + 2, 2); - std::vector received = readDataFromDriver(); + vector received = readDataFromDriver(); BOOST_REQUIRE(received == vector(data + 2, data + 4)); } @@ -131,7 +131,7 @@ BOOST_FIXTURE_TEST_CASE(it_fails_expecation_with_data_sent_to_device, Fixture) uint8_t msg[] = { 0, 1, 2, 4 }; uint8_t rep[] = { 3, 2, 1, 0 }; EXPECT_REPLY(vector(exp, exp + 4),vector(rep, rep + 4)); - BOOST_REQUIRE_THROW(writePacket(msg,4), std::invalid_argument); + BOOST_REQUIRE_THROW(writePacket(msg,4), invalid_argument); } @@ -188,14 +188,14 @@ BOOST_FIXTURE_TEST_CASE(it_sends_more_messages_than_expecations_set, Fixture) vector received_1 = readPacket(); BOOST_REQUIRE(received_1 == vector(rep1,rep1+4)); - BOOST_REQUIRE_THROW(writePacket(exp2,5),std::runtime_error); + BOOST_REQUIRE_THROW(writePacket(exp2,5),runtime_error); } struct DriverClassNameDriver : Driver { virtual int extractPacket(uint8_t const* buffer, size_t buffer_length) const { - return std::min(buffer_length, static_cast(1)); + return min(buffer_length, static_cast(1)); } }; diff --git a/test/test_TestStreamGTest.cpp b/test/test_TestStreamGTest.cpp index 7f3780d..a136fbe 100644 --- a/test/test_TestStreamGTest.cpp +++ b/test/test_TestStreamGTest.cpp @@ -49,7 +49,7 @@ TEST_F(DriverTest, it_fails_expecation_with_data_sent_to_device) uint8_t msg[] = { 0, 1, 2, 4 }; uint8_t rep[] = { 3, 2, 1, 0 }; EXPECT_REPLY(vector(exp, exp + 4),vector(rep, rep + 4)); - EXPECT_THROW(writePacket(msg,4), std::invalid_argument); + EXPECT_THROW(writePacket(msg,4), invalid_argument); } @@ -106,7 +106,7 @@ TEST_F(DriverTest ,it_sends_more_messages_than_expecations_set) vector received_1 = readPacket(); ASSERT_EQ(received_1, vector(rep1,rep1+4)); - ASSERT_THROW(writePacket(exp2,5),std::runtime_error); + ASSERT_THROW(writePacket(exp2,5),runtime_error); } struct DriverClassNameDriver : Driver From 60eb49f01eddddedc4ebf701632e79cd30eb8672 Mon Sep 17 00:00:00 2001 From: Sylvain Joyeux Date: Thu, 10 Aug 2017 10:52:54 -0300 Subject: [PATCH 12/15] fix using mock contexts in sequence --- test/test_TestStream.cpp | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/test/test_TestStream.cpp b/test/test_TestStream.cpp index 5e643f1..cece089 100644 --- a/test/test_TestStream.cpp +++ b/test/test_TestStream.cpp @@ -191,6 +191,27 @@ BOOST_FIXTURE_TEST_CASE(it_sends_more_messages_than_expecations_set, Fixture) BOOST_REQUIRE_THROW(writePacket(exp2,5),runtime_error); } +BOOST_FIXTURE_TEST_CASE(mock_modes_can_be_used_in_sequence, Fixture) +{ + { IODRIVERS_BASE_MOCK(); + uint8_t exp[] = { 0, 1, 2, 3 }; + uint8_t rep[] = { 3, 2, 1, 0 }; + EXPECT_REPLY(vector(exp, exp + 4),vector(rep, rep + 4)); + writePacket(exp,4); + vector received = readPacket(); + BOOST_REQUIRE(received == vector(rep,rep+4)); + } + + { IODRIVERS_BASE_MOCK(); + uint8_t exp[] = { 3, 2, 1, 0 }; + uint8_t rep[] = { 0, 1, 2, 3 }; + EXPECT_REPLY(vector(exp, exp + 4),vector(rep, rep + 4)); + writePacket(exp,4); + vector received = readPacket(); + BOOST_REQUIRE(received == vector(rep,rep+4)); + } +} + struct DriverClassNameDriver : Driver { virtual int extractPacket(uint8_t const* buffer, size_t buffer_length) const From 01845efba5db4285f3c03dee74f0ad12daa2ae5c Mon Sep 17 00:00:00 2001 From: Sylvain Joyeux Date: Thu, 10 Aug 2017 11:30:36 -0300 Subject: [PATCH 13/15] fix interaction between the mock mode and raw access --- src/TestStream.cpp | 5 ++- test/test_TestStream.cpp | 48 ++++++++++++++++++++++++ test/test_TestStreamGTest.cpp | 69 +++++++++++++++++++++++++++++++++++ 3 files changed, 120 insertions(+), 2 deletions(-) diff --git a/src/TestStream.cpp b/src/TestStream.cpp index 7aa6ab8..2072549 100644 --- a/src/TestStream.cpp +++ b/src/TestStream.cpp @@ -53,7 +53,6 @@ size_t TestStream::write(uint8_t const* buffer, size_t buffer_size) { if(mock_mode) { - from_driver.clear(); from_driver.insert(from_driver.end(), buffer, buffer + buffer_size); if(expectations.empty()) { @@ -67,6 +66,7 @@ size_t TestStream::write(uint8_t const* buffer, size_t buffer_size) if(from_driver == expectations.front()) { to_driver.insert(to_driver.end(), replies.front().begin(), replies.front().end()); + from_driver.clear(); expectations.pop_front(); replies.pop_front(); } @@ -115,4 +115,5 @@ bool TestStream::expectationsAreEmpty() void TestStream::setMockMode(bool mode) { mock_mode = mode; -} \ No newline at end of file + from_driver.clear(); +} diff --git a/test/test_TestStream.cpp b/test/test_TestStream.cpp index cece089..b6e119c 100644 --- a/test/test_TestStream.cpp +++ b/test/test_TestStream.cpp @@ -212,6 +212,54 @@ BOOST_FIXTURE_TEST_CASE(mock_modes_can_be_used_in_sequence, Fixture) } } +BOOST_FIXTURE_TEST_CASE(mock_modes_can_be_followed_by_raw_write, Fixture) +{ + { IODRIVERS_BASE_MOCK(); + uint8_t exp[] = { 0, 1, 2, 3 }; + uint8_t rep[] = { 3, 2, 1, 0 }; + EXPECT_REPLY(vector(exp, exp + 4),vector(rep, rep + 4)); + writePacket(exp,4); + vector received = readPacket(); + BOOST_REQUIRE(received == vector(rep,rep+4)); + } + + uint8_t rep[] = { 0, 1, 2, 3 }; + pushDataToDriver(vector(rep,rep+4)); + vector received = readPacket(); + BOOST_REQUIRE(received == vector(rep,rep+4)); +} + +BOOST_FIXTURE_TEST_CASE(mock_modes_can_be_followed_by_raw_read, Fixture) +{ + { IODRIVERS_BASE_MOCK(); + uint8_t exp[] = { 0, 1, 2, 3 }; + uint8_t rep[] = { 3, 2, 1, 0 }; + EXPECT_REPLY(vector(exp, exp + 4),vector(rep, rep + 4)); + writePacket(exp,4); + vector received = readPacket(); + BOOST_REQUIRE(received == vector(rep,rep+4)); + } + + uint8_t rep[] = { 0, 1, 2, 3 }; + writePacket(rep, 4); + vector received = readDataFromDriver(); + BOOST_REQUIRE(received == vector(rep,rep+4)); +} + +BOOST_FIXTURE_TEST_CASE(quitting_the_mock_mode_does_not_clear_the_data_unread_by_the_driver, Fixture) +{ + uint8_t rep[] = { 3, 2, 1, 0 }; + + { IODRIVERS_BASE_MOCK(); + uint8_t exp[] = { 0, 1, 2, 3 }; + EXPECT_REPLY(vector(exp, exp + 4),vector(rep, rep + 4)); + writePacket(exp,4); + } + + vector received = readPacket(); + BOOST_REQUIRE(received == vector(rep,rep+4)); +} + struct DriverClassNameDriver : Driver { virtual int extractPacket(uint8_t const* buffer, size_t buffer_length) const diff --git a/test/test_TestStreamGTest.cpp b/test/test_TestStreamGTest.cpp index a136fbe..2bf828d 100644 --- a/test/test_TestStreamGTest.cpp +++ b/test/test_TestStreamGTest.cpp @@ -109,6 +109,75 @@ TEST_F(DriverTest ,it_sends_more_messages_than_expecations_set) ASSERT_THROW(writePacket(exp2,5),runtime_error); } +TEST_F(DriverTest, mock_modes_can_be_used_in_sequence) +{ + { IODRIVERS_BASE_MOCK(); + uint8_t exp[] = { 0, 1, 2, 3 }; + uint8_t rep[] = { 3, 2, 1, 0 }; + EXPECT_REPLY(vector(exp, exp + 4),vector(rep, rep + 4)); + writePacket(exp,4); + vector received = readPacket(); + ASSERT_EQ(received, vector(rep,rep+4)); + } + + { IODRIVERS_BASE_MOCK(); + uint8_t exp[] = { 3, 2, 1, 0 }; + uint8_t rep[] = { 0, 1, 2, 3 }; + EXPECT_REPLY(vector(exp, exp + 4),vector(rep, rep + 4)); + writePacket(exp,4); + vector received = readPacket(); + ASSERT_EQ(received, vector(rep,rep+4)); + } +} + +TEST_F(DriverTest, mock_modes_can_be_followed_by_raw_write) +{ + { IODRIVERS_BASE_MOCK(); + uint8_t exp[] = { 0, 1, 2, 3 }; + uint8_t rep[] = { 3, 2, 1, 0 }; + EXPECT_REPLY(vector(exp, exp + 4),vector(rep, rep + 4)); + writePacket(exp,4); + vector received = readPacket(); + ASSERT_EQ(received, vector(rep,rep+4)); + } + + uint8_t rep[] = { 0, 1, 2, 3 }; + pushDataToDriver(vector(rep,rep+4)); + vector received = readPacket(); + ASSERT_EQ(received, vector(rep,rep+4)); +} + +TEST_F(DriverTest, mock_modes_can_be_followed_by_raw_read) +{ + { IODRIVERS_BASE_MOCK(); + uint8_t exp[] = { 0, 1, 2, 3 }; + uint8_t rep[] = { 3, 2, 1, 0 }; + EXPECT_REPLY(vector(exp, exp + 4),vector(rep, rep + 4)); + writePacket(exp,4); + vector received = readPacket(); + ASSERT_EQ(received, vector(rep,rep+4)); + } + + uint8_t rep[] = { 0, 1, 2, 3 }; + writePacket(rep, 4); + vector received = readDataFromDriver(); + ASSERT_EQ(received, vector(rep,rep+4)); +} + +TEST_F(DriverTest, quitting_the_mock_mode_does_not_clear_the_data_unread_by_the_driver) +{ + uint8_t rep[] = { 3, 2, 1, 0 }; + + { IODRIVERS_BASE_MOCK(); + uint8_t exp[] = { 0, 1, 2, 3 }; + EXPECT_REPLY(vector(exp, exp + 4),vector(rep, rep + 4)); + writePacket(exp,4); + } + + vector received = readPacket(); + ASSERT_EQ(received, vector(rep,rep+4)); +} + struct DriverClassNameDriver : Driver { virtual int extractPacket(uint8_t const* buffer, size_t buffer_length) const From 70a29c16a0722f87de96b65afbfe5945f10b2e14 Mon Sep 17 00:00:00 2001 From: Sylvain Joyeux Date: Thu, 10 Aug 2017 14:34:28 -0300 Subject: [PATCH 14/15] remove newlines at the end of exception messages --- src/Exceptions.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Exceptions.hpp b/src/Exceptions.hpp index 73be98c..8d68611 100644 --- a/src/Exceptions.hpp +++ b/src/Exceptions.hpp @@ -37,7 +37,7 @@ class MockContextException : public std::exception public: const char * what() const throw() { - return "IODRIVERS_BASE_MOCK Error: Expectation set outside Mock Context! Please call IODRIVERS_BASE_MOCK() before setting expectations\n"; + return "IODRIVERS_BASE_MOCK Error: Expectation set outside Mock Context! Please call IODRIVERS_BASE_MOCK() before setting expectations"; } }; @@ -46,7 +46,7 @@ class TestEndsWithExpectationsLeftException : public std::exception public: const char * what() const throw() { - return "IODRIVERS_BASE_MOCK Error: Test reached its end without satisfying all expecations.\n"; + return "IODRIVERS_BASE_MOCK Error: Test reached its end without satisfying all expecations"; } }; From 470283743a25221b5ab3d0f643fcd10535b22628 Mon Sep 17 00:00:00 2001 From: Sylvain Joyeux Date: Thu, 10 Aug 2017 14:34:38 -0300 Subject: [PATCH 15/15] create Fixture::writePacket(vector) for consistency --- src/Fixture.hpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Fixture.hpp b/src/Fixture.hpp index 1512b5e..54deed4 100644 --- a/src/Fixture.hpp +++ b/src/Fixture.hpp @@ -104,6 +104,12 @@ namespace iodrivers_base return packet; } + /** Write data to the driver */ + void writePacket(std::vector const& data) + { + writePacket(data.data(), data.size()); + } + /** Write data to the driver */ void writePacket(uint8_t const* buffer, size_t size) {