Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Driver: fix opening serial on Mac OS X #16

Open
wants to merge 21 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
fd82a86
Driver: fix opening serial on Mac OS X
Dec 16, 2016
33e4bd6
Fix base/logging deprecated warning
g-arjones Mar 13, 2017
d962897
Merge pull request #17 from Brazilian-Institute-of-Robotics/fix_depre…
jmachowinski Mar 14, 2017
c631f92
use the cmake-provided way to resolve -pthread
doudou Jul 13, 2017
12a1234
Merge pull request #18 from rock-core/cmake_pthread_resolution
doudou Jul 14, 2017
16300b7
create a harness to test drivers as blackboxes
doudou Jul 14, 2017
dbdbdbf
Merge pull request #19 from rock-core/driver_test_harness
doudou Jul 14, 2017
5f45883
Implement the MOCK funcionality to be able to send messages that requ…
alcantara09 Jul 27, 2017
edf014b
replace MockContext class by test framework specific ones
doudou Aug 10, 2017
21b2947
document IODRIVERS_BASE_MOCK
doudou Aug 10, 2017
d5c8bf8
Merge pull request #20 from ThirteenLtda/iodrivers_base_mock
doudou Aug 10, 2017
6cd2fe4
improve error message on expectation failure
doudou Aug 10, 2017
8d77483
do not assume that the driver class is "Driver" in IODRIVERS_BASE_MOCK()
doudou Aug 10, 2017
8644531
give the means to test openURI itself
doudou Aug 10, 2017
5f899c6
test: remove unnecessary std:: qualifications
doudou Aug 10, 2017
60eb49f
fix using mock contexts in sequence
doudou Aug 10, 2017
01845ef
fix interaction between the mock mode and raw access
doudou Aug 10, 2017
70a29c1
remove newlines at the end of exception messages
doudou Aug 10, 2017
4702837
create Fixture::writePacket(vector) for consistency
doudou Aug 10, 2017
07ef979
Merge pull request #21 from rock-core/bugfixes_mock
alcantara09 Sep 1, 2017
838bf7b
Merge remote-tracking branch 'autobuild/macosx'
D-Alex Feb 1, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions manifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,8 @@
<tags>stable</tags>

<depend package="base/types" />
<depend package="base/logging" />
<depend package="boost" />
<test_depend package="libgtest-dev" />
<test_depend package="google-mock" />
</package>
9 changes: 6 additions & 3 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
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
IOListener.cpp TestStream.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
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}
DEPS_PKGCONFIG base-types base-lib)

# For backward compatibility only
Expand Down
33 changes: 28 additions & 5 deletions src/Driver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include <boost/lexical_cast.hpp>
#include <iodrivers_base/IOStream.hpp>
#include <iodrivers_base/IOListener.hpp>
#include <iodrivers_base/TestStream.hpp>

#ifdef __gnu_linux__
#include <linux/serial.h>
Expand Down Expand Up @@ -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);
Expand All @@ -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(); }

Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -214,6 +223,16 @@ void Driver::openURI(std::string const& uri)
{ // file file://path
return openFile(device);
}
else if (mode_idx == 5)
{ // test://
if (!dynamic_cast<TestStream*>(getMainStream()))
openTestMode();
}
}

void Driver::openTestMode()
{
setMainStream(new TestStream);
}

bool Driver::openSerial(std::string const& port, int baud_rate)
Expand Down Expand Up @@ -372,8 +391,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)
Expand Down
23 changes: 22 additions & 1 deletion src/Driver.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -434,6 +435,26 @@ class Driver
*/
void setMainStream(IOStream* stream);

/** Gets the main IO stream
*/
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:
*
* <code>
* 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.
*/
Expand Down
20 changes: 19 additions & 1 deletion src/Exceptions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#include <string>
#include <stdexcept>
#include <exception>

namespace iodrivers_base
{
Expand Down Expand Up @@ -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 expectations";
}
};

class TestEndsWithExpectationsLeftException : public std::exception
{
public:
const char * what() const throw()
{
return "IODRIVERS_BASE_MOCK Error: Test reached its end without satisfying all expecations";
}
};

#endif

197 changes: 197 additions & 0 deletions src/Fixture.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
#ifndef IODRIVERS_BASE_BOOST_FIXTURE_HPP
#define IODRIVERS_BASE_BOOST_FIXTURE_HPP

#include <iodrivers_base/TestStream.hpp>
#include <vector>
#include <iodrivers_base/Exceptions.hpp>

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:
*
* <code>
* BOOST_FIXTURE_TEST(MyTest, Fixture<MyDriver>)
* {
* MyDriver.openURI("test://");
* uint8_t buffer[4] = { 0, 1, 2, 3 };
* pushDataToDriver(buffer, buffer + 2);
* auto packet = readPacket();
* // Check that the packet matches the expected extraction
* }
* </code>
*
* In GTest:
*
* <code>
* struct DriverTest : ::testing::Test, iodrivers_base::Fixture<MyDriver>
* {
* 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 };
* pushDataToDriver(buffer, buffer + 2);
* auto packet = readPacket();
* // Check that the packet matches the expected extraction
* }
* </code>
*
* 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.
*
*<code>
*IODRIVER_BASE_MOCK()
*uint8_t exp[] = { 0, 1, 2, 3 };
*uint8_t rep[] = { 3, 2, 1, 0 };
*EXPECT_REPLY(vector<uint8_t>(exp, exp + 4),
* vector<uint8_t>(rep, rep + 4));
*writePacket(exp,4);
*
*<code>
*/
template<typename Driver>
struct Fixture
{
typedef Driver fixture_driver_t;

std::vector<uint8_t> packetBuffer;
Driver driver;

Fixture()
{
packetBuffer.resize(driver.MAX_PACKET_SIZE);
}

/** Get the underlying TestStream
*/
TestStream* getStream()
{
if (!driver.getMainStream())
driver.openTestMode();
return dynamic_cast<TestStream*>(driver.getMainStream());
}

/** Read a packet from the driver and return it as an std::vector
*/
std::vector<uint8_t> readPacket()
{
size_t size = driver.readPacket(packetBuffer.data(), packetBuffer.size());
std::vector<uint8_t> packet;
packet.insert(packet.end(), packetBuffer.begin(), packetBuffer.begin() + size);
return packet;
}

/** Write data to the driver */
void writePacket(std::vector<uint8_t> const& data)
{
writePacket(data.data(), data.size());
}

/** 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<uint8_t> const& data)
{
return getStream()->pushDataToDriver(data);
}

/** Helper method to allow passing any kind of uint8_t range
*
* <code>
* uint8_t packet[] = { 0, 1, 2, 3 };
* pushDataToDriver(packet, packet + sizeof(packet));
* </code>
*/
template<typename Iterator>
void pushDataToDriver(Iterator begin, Iterator end)
{
std::vector<uint8_t> buffer(begin, end);
pushDataToDriver(buffer);
}

/** Read data that the driver sent to the device
*/
std::vector<uint8_t> readDataFromDriver()
{
return getStream()->readDataFromDriver();
}

/** Return the number of bytes currently queued in the driver's internal
* buffer
*
* This is useful mainly when testing extractPacket
*
* <code>
* TEST_F(DriverTest, KeepsSingleBytes)
* {
* uint8_t data = 0x01;
* pushDataToDriver(&data, &data + 1);
* ASSERT_THROW(TimeoutError, readPacket());
* ASSERT_EQUAL(1, getQueuedBytes());
* }
* </code>
*/
unsigned int getQueuedBytes() const
{
return driver.getStatus().queued_bytes;
}
void EXPECT_REPLY(std::vector<uint8_t> const& expectation, std::vector<uint8_t> 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 validateExpectationsAreEmpty()
{
if(!getStream()->expectationsAreEmpty())
throw TestEndsWithExpectationsLeftException();
}

void setMockMode(bool mode)
{
getStream()->setMockMode(mode);
}

void clearExpectations()
{
getStream()->clearExpectations();
}

class GTestMockContext;
class BoostMockContext;
};
}

#endif
Loading