Skip to content

Add support for using ephemeral ports #131

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

Merged
merged 2 commits into from
Dec 24, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
8 changes: 8 additions & 0 deletions include/cucumber-cpp/internal/connectors/wire/WireServer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,14 @@ class SocketServer {
*/
void listen(const port_type port);

/**
* Port number that this server is currently listening on.
*
* @throw boost::system::system_error when not listening on any TCP port or
* the port cannot be determined.
*/
port_type listenPort() const;

/**
* Accept one connection
*/
Expand Down
5 changes: 5 additions & 0 deletions src/connectors/wire/WireServer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ void SocketServer::listen(const port_type port) {
acceptor.listen(1);
}

SocketServer::port_type SocketServer::listenPort() const {
const tcp::endpoint ep(acceptor.local_endpoint());
return ep.port();
}

void SocketServer::acceptOnce() {
tcp::iostream stream;
acceptor.accept(*stream.rdbuf());
Expand Down
4 changes: 2 additions & 2 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ void acceptWireProtocol(int port, bool verbose) {
SocketServer server(&protocolHandler);
server.listen(port);
if (verbose)
std::clog << "Listening on port " << port << std::endl;
std::clog << "Listening on port " << server.listenPort() << std::endl;
server.acceptOnce();
}

Expand All @@ -26,7 +26,7 @@ int main(int argc, char **argv) {
optionDescription.add_options()
("help,h", "help for cucumber-cpp")
("verbose,v", "verbose output")
("port,p", value<int>(), "listening port of wireserver")
("port,p", value<int>(), "listening port of wireserver, use '0' (zero) to select an ephemeral port")
;
boost::program_options::variables_map optionVariableMap;
boost::program_options::store(boost::program_options::parse_command_line(argc, argv, optionDescription), optionVariableMap);
Expand Down
11 changes: 5 additions & 6 deletions tests/integration/WireServerTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,13 @@ class MockProtocolHandler : public ProtocolHandler {
class SocketServerTest : public Test {

protected:
static const unsigned short PORT = 54321;
StrictMock<MockProtocolHandler> protocolHandler;
SocketServer *server;
thread *serverThread;

virtual void SetUp() {
server = new SocketServer(&protocolHandler);
server->listen(PORT);
server->listen(0);
serverThread = new thread(bind(&SocketServer::acceptOnce, server));
}

Expand All @@ -79,7 +78,7 @@ class SocketServerTest : public Test {

TEST_F(SocketServerTest, exitsOnFirstConnectionClosed) {
// given
tcp::iostream client(tcp::endpoint(tcp::v4(), PORT));
tcp::iostream client(tcp::endpoint(tcp::v4(), server->listenPort()));
ASSERT_THAT(client, IsConnected());

// when
Expand All @@ -91,11 +90,11 @@ TEST_F(SocketServerTest, exitsOnFirstConnectionClosed) {

TEST_F(SocketServerTest, moreThanOneClientCanConnect) {
// given
tcp::iostream client1(tcp::endpoint(tcp::v4(), PORT));
tcp::iostream client1(tcp::endpoint(tcp::v4(), server->listenPort()));
ASSERT_THAT(client1, IsConnected());

// when
tcp::iostream client2(tcp::endpoint(tcp::v4(), PORT));
tcp::iostream client2(tcp::endpoint(tcp::v4(), server->listenPort()));

//then
ASSERT_THAT(client2, IsConnected());
Expand All @@ -110,7 +109,7 @@ TEST_F(SocketServerTest, receiveAndSendsSingleLineMassages) {
}

// given
tcp::iostream client(tcp::endpoint(tcp::v4(), PORT));
tcp::iostream client(tcp::endpoint(tcp::v4(), server->listenPort()));
ASSERT_THAT(client, IsConnected());

// when
Expand Down
2 changes: 2 additions & 0 deletions travis.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@ BOOST=build/examples/Calc/BoostCalculatorSteps
if [ -f $GTEST ]; then
$GTEST >/dev/null &
cucumber examples/Calc
wait
Copy link
Contributor Author

@muggenhor muggenhor Dec 15, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Without this it's possible for the step definition executable to still be running when cucumber is finished. Keeping the port in use, if timing is working against us just enough then this can result in an inability to open a new listening socket on that port for the BOOST test case below.

Note that a similar race condition still exists here: the step definition executable will not necessarily have opened the TCP socket for listening by the time that cucumber attempts to connect to it. An approach that I'm using in our CI system is to run with --verbose and wait for the Listening on ... message before starting cucumber. That's more difficult to script in a shell script than the Python that I'm using though. (I'm using Python instead of Ruby because most of our CI system is either shell scripts or Python).

fi
if [ -f $BOOST ]; then
$BOOST >/dev/null &
cucumber examples/Calc
wait
fi