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/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"; } }; diff --git a/src/Fixture.hpp b/src/Fixture.hpp index 3cccdb5..54deed4 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; @@ -85,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()); } @@ -100,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) { 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/src/TestStream.cpp b/src/TestStream.cpp index b1b87e1..2072549 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; @@ -51,22 +53,38 @@ 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()) - 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()) { to_driver.insert(to_driver.end(), replies.front().begin(), replies.front().end()); + from_driver.clear(); expectations.pop_front(); replies.pop_front(); } 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; } @@ -97,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 c68b5f0..b6e119c 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) {} + vector openURIData; + + void openURI(string const& uri) + { + iodrivers_base::Driver::openURI(uri); + uint8_t buffer[100]; + try { + size_t packet_size = readPacket(buffer, 100); + openURIData = 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) { @@ -66,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)); } @@ -75,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)); } @@ -85,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)); } @@ -107,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); } @@ -164,7 +188,130 @@ 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); +} + +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)); + } +} + +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 + { + return 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()); +} + +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 0090d6a..2bf828d 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,129 @@ 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); +} + +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 + { + 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()); +} + +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)