Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
tag: release-2.0.4
Fetching contributors…

Cannot retrieve contributors at this time

313 lines (268 sloc) 8.933 kb
#include "tut.h"
#include "MessageChannel.h"
#include <cstring>
#include <cstdio>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
using namespace Passenger;
using namespace std;
namespace tut {
struct MessageChannelTest {
MessageChannel reader, writer;
int p[2];
MessageChannelTest() {
if (pipe(p) != 0) {
throw SystemException("Cannot create a pipe", errno);
}
reader = MessageChannel(p[0]);
writer = MessageChannel(p[1]);
}
~MessageChannelTest() {
close(p[0]);
close(p[1]);
}
};
DEFINE_TEST_GROUP(MessageChannelTest);
TEST_METHOD(1) {
// read() should be able to parse a message constructed by write(name, ...).
vector<string> args;
writer.write("hello", "world", "!", NULL);
ensure("End of file has not been reached", reader.read(args));
ensure_equals("read() returns the same number of arguments as passed to write()", args.size(), 3u);
ensure_equals(args[0], "hello");
ensure_equals(args[1], "world");
ensure_equals(args[2], "!");
}
TEST_METHOD(2) {
// read() should be able to parse a message constructed by write(list).
list<string> input;
vector<string> output;
input.push_back("hello");
input.push_back("world");
input.push_back("!");
writer.write(input);
ensure("End of file has not been reached", reader.read(output));
ensure_equals("read() returns the same number of arguments as passed to write()", input.size(), output.size());
list<string>::const_iterator it;
vector<string>::const_iterator it2;
for (it = input.begin(), it2 = output.begin(); it != input.end(); it++, it2++) {
ensure_equals(*it, *it2);
}
}
TEST_METHOD(3) {
// write() should be able to properly serialize arguments that contain whitespace.
vector<string> args;
writer.write("hello", "world with whitespaces", "!!!", NULL);
ensure("End of file has not been reached", reader.read(args));
ensure_equals(args[1], "world with whitespaces");
}
TEST_METHOD(4) {
// read() should be able to read messages constructed by the Ruby implementation.
// write() should be able to construct messages that can be read by the Ruby implementation.
// Multiple read() and write() calls should work (i.e. the MessageChannel should have stream properties).
// End of file should be properly detected.
int p1[2], p2[2];
pid_t pid;
pipe(p1);
pipe(p2);
pid = fork();
if (pid == 0) {
close(p[0]);
close(p[1]);
dup2(p1[0], 0);
dup2(p2[1], 1);
close(p1[0]);
close(p1[1]);
close(p2[0]);
close(p2[1]);
execlp("ruby", "ruby", "./stub/message_channel.rb", NULL);
perror("Cannot execute ruby");
_exit(1);
} else {
MessageChannel input(p1[1]);
MessageChannel output(p2[0]);
close(p1[0]);
close(p2[1]);
input.write("hello", "my beautiful", "world", NULL);
input.write("you have", "not enough", "minerals", NULL);
input.close();
vector<string> message1, message2, message3;
ensure("End of stream has not been reached (1)", output.read(message1));
ensure("End of stream has not been reached (2)", output.read(message2));
ensure("End of file has been reached", !output.read(message3));
output.close();
waitpid(pid, NULL, 0);
ensure_equals("First message is correctly transformed by the mock object",
message1.size(), 4u);
ensure_equals(message1[0], "hello");
ensure_equals(message1[1], "my beautiful");
ensure_equals(message1[2], "world");
ensure_equals(message1[3], "!!");
ensure_equals("Second message is correctly transformed by the mock object",
message2.size(), 4u);
ensure_equals(message2[0], "you have");
ensure_equals(message2[1], "not enough");
ensure_equals(message2[2], "minerals");
ensure_equals(message2[3], "??");
}
}
TEST_METHOD(6) {
// write(name) should generate a correct message even if there are no additional arguments.
writer.write("hello", NULL);
vector<string> args;
reader.read(args);
ensure_equals(args.size(), 1u);
ensure_equals(args[0], "hello");
}
TEST_METHOD(7) {
// writeFileDescriptor() and receiveFileDescriptor() should work.
int s[2], my_pipe[2], fd;
socketpair(AF_UNIX, SOCK_STREAM, 0, s);
MessageChannel channel1(s[0]);
MessageChannel channel2(s[1]);
pipe(my_pipe);
channel1.writeFileDescriptor(my_pipe[1]);
fd = channel2.readFileDescriptor();
char buf[5];
write(fd, "hello", 5);
close(fd);
read(my_pipe[0], buf, 5);
ensure(memcmp(buf, "hello", 5) == 0);
close(s[0]);
close(s[1]);
close(my_pipe[0]);
close(my_pipe[1]);
}
TEST_METHOD(8) {
// write() should be able to construct a message that consists of only an empty string.
// read() should be able to read a message that consists of only an empty string.
vector<string> args;
writer.write("", NULL);
reader.read(args);
ensure_equals(args.size(), 1u);
ensure_equals(args[0], "");
}
TEST_METHOD(9) {
// readScalar() should be able to read messages constructed by writeScalar().
// This also tests readRaw()/writeRaw() because readScalar()/writeScalar() uses
// them internally.
writer.writeScalar("hello\n\r world!!!");
writer.writeScalar(" and this is a second message");
string output;
ensure("End of stream has not been reached (1)", reader.readScalar(output));
ensure_equals(output, "hello\n\r world!!!");
ensure("End of stream has not been reached (2)", reader.readScalar(output));
ensure_equals(output, " and this is a second message");
}
TEST_METHOD(10) {
// writeScalar() should be able to produce messages that are compatible with the Ruby implementation.
// readScalar() should be able to read messages produced by the Ruby implementation.
int p1[2], p2[2];
pid_t pid;
pipe(p1);
pipe(p2);
pid = fork();
if (pid == 0) {
close(p[0]);
close(p[1]);
dup2(p1[0], 0);
dup2(p2[1], 1);
close(p1[0]);
close(p1[1]);
close(p2[0]);
close(p2[1]);
execlp("ruby", "ruby", "./stub/message_channel_2.rb", NULL);
perror("Cannot execute ruby");
_exit(1);
} else {
MessageChannel reader(p2[0]);
MessageChannel writer(p1[1]);
string output;
close(p1[0]);
close(p2[1]);
writer.writeScalar("hello world\n!\r!");
ensure("End of file has not yet been reached (1)", reader.readScalar(output));
ensure_equals(output, "hello world\n!\r!!!");
writer.writeScalar("");
ensure("End of file has not yet been reached (2)", reader.readScalar(output));
ensure_equals(output, "??");
writer.close();
ensure("End of file has been reached", !reader.readScalar(output));
reader.close();
waitpid(pid, NULL, 0);
}
}
TEST_METHOD(11) {
// If we send a lot of different messages (including file descriptor passing),
// and the other side sends the same stuff back to us, then MessageChannel
// should be able to read them all, if done in the correct order.
// writeScalar() should be able to produce messages that are compatible with the Ruby implementation.
// readScalar() should be able to read messages produced by the Ruby implementation.
int fd[2];
pid_t pid;
socketpair(AF_UNIX, SOCK_STREAM, 0, fd);
pid = fork();
if (pid == 0) {
close(p[0]);
close(p[1]);
dup2(fd[0], 3);
close(fd[0]);
close(fd[1]);
execlp("ruby", "ruby", "./stub/message_channel_3.rb", NULL);
perror("Cannot execute ruby");
_exit(1);
} else {
MessageChannel channel(fd[1]);
close(fd[0]);
vector<string> args;
string output;
int tmp[2];
channel.write("hello ", "my!", "world", NULL);
ensure("End of file has not yet been reached", channel.read(args));
ensure_equals(args.size(), 3u);
ensure_equals(args[0], "hello ");
ensure_equals(args[1], "my!");
ensure_equals(args[2], "world");
channel.writeScalar("testing 123");
ensure("End of file has not yet been reached", channel.readScalar(output));
ensure_equals(output, "testing 123");
pipe(tmp);
close(tmp[0]);
channel.writeFileDescriptor(tmp[1]);
close(tmp[1]);
int x = channel.readFileDescriptor();
close(x);
channel.write("the end", NULL);
ensure("End of file has not yet been reached", channel.read(args));
ensure_equals(args.size(), 1u);
ensure_equals(args[0], "the end");
ensure("End of file has been reached", !channel.read(args));
channel.close();
waitpid(pid, NULL, 0);
}
}
TEST_METHOD(12) {
// readScalar()/writeScalar() should be able to handle arbitrary binary data.
string data;
FILE *f = fopen("stub/garbage3.dat", "r");
while (!feof(f)) {
char buf[1024 * 32];
size_t ret = fread(buf, 1, sizeof(buf), f);
data.append(buf, ret);
}
fclose(f);
pid_t pid = fork();
if (pid == 0) {
reader.close();
writer.writeScalar(data);
_exit(0);
} else {
writer.close();
string result;
reader.readScalar(result);
ensure_equals(result, data);
waitpid(pid, NULL, 0);
}
}
}
Jump to Line
Something went wrong with that request. Please try again.