public
Description: Phusion Passenger (mod_rails)
Homepage: http://www.modrails.com/
Clone URL: git://github.com/FooBarWidget/passenger.git
Click here to lend your support to: passenger and make a donation at www.pledgie.com !
mod_rails is more usable now though it still crashes randomly.
The message channel protocol also had to be changed in order to fix some 
semantic ambiguities.
FooBarWidget (author)
Thu Jan 31 12:37:28 -0800 2008
commit  af29aeaa1d4bf975824b14fe7577168055045d01
tree    13c83296ba7c9cb58f58554897e84ab769f21703
parent  040201494bc0c798e543c0b2e1287ad15062ffa3
...
29
30
31
 
 
 
 
 
32
33
34
...
29
30
31
32
33
34
35
36
37
38
39
0
@@ -29,6 +29,11 @@ public:
0
     closeWriter();
0
   }
0
   
0
+ void detachCommunicationChannels() {
0
+ reader = -1;
0
+ writer = -1;
0
+ }
0
+
0
   string getAppRoot() const {
0
     return appRoot;
0
   }
...
 
1
2
3
...
5
6
7
8
9
10
11
12
13
14
 
15
16
 
 
 
17
18
 
19
20
21
...
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
...
135
136
137
 
138
139
140
...
155
156
157
 
158
159
160
161
162
163
 
164
165
166
...
174
175
176
 
177
178
 
179
180
181
182
183
184
 
185
186
187
188
189
190
191
 
 
192
193
194
...
208
209
210
211
 
 
212
213
214
 
 
 
215
216
217
...
224
225
226
 
 
227
228
 
 
 
 
 
 
 
 
229
...
1
2
3
4
...
6
7
8
 
 
 
 
 
 
 
9
10
11
12
13
14
15
16
17
18
19
20
...
28
29
30
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31
32
33
...
116
117
118
119
120
121
122
...
137
138
139
140
141
142
143
144
145
146
147
148
149
150
...
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
 
 
177
178
179
180
181
...
195
196
197
 
198
199
200
201
202
203
204
205
206
207
208
...
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
0
@@ -1,3 +1,4 @@
0
+#include <string>
0
 #include <poll.h>
0
 #include <errno.h>
0
 #include <unistd.h>
0
@@ -5,17 +6,15 @@
0
 #define APR_WANT_BYTEFUNC
0
 #include <apr_want.h>
0
 
0
-#define DEBUG
0
-#ifdef DEBUG
0
- #include <cstdarg>
0
- #include <cstdio>
0
- #include <apr_strings.h>
0
-#endif
0
-
0
+#include "Utils.h"
0
 #include "DispatcherBucket.h"
0
 
0
+using namespace std;
0
+using namespace Passenger;
0
+
0
 static apr_status_t dispatcher_bucket_read(apr_bucket *b, const char **str, apr_size_t *len, apr_read_type_e block);
0
 static void dispatcher_bucket_destroy(void *data);
0
+static apr_status_t dispatcher_bucket_pool_cleaner(void *d);
0
 
0
 static const apr_bucket_type_t bucket_type_dispatcher = {
0
   "Dispatcher",
0
@@ -29,24 +28,6 @@ static const apr_bucket_type_t bucket_type_dispatcher = {
0
 };
0
 
0
 
0
-#ifdef DEBUG
0
-static void
0
-debug(const char *format, ...) {
0
- va_list ap;
0
- char message[1024];
0
-
0
- va_start(ap, format);
0
- int size = apr_vsnprintf(message, sizeof(message), format, ap);
0
- FILE *f = fopen("/dev/pts/2", "w");
0
- if (f != NULL) {
0
- fwrite(message, 1, size, f);
0
- fclose(f);
0
- }
0
- va_end(ap);
0
-}
0
-#endif
0
-
0
-
0
 class DispatcherBucket {
0
 private:
0
   apr_status_t
0
@@ -135,6 +116,7 @@ private:
0
   }
0
   
0
 public:
0
+ ApplicationPtr app;
0
   int pipe;
0
   apr_interval_time_t timeout;
0
   bool closed;
0
@@ -155,12 +137,14 @@ public:
0
     
0
     result = read_chunk_size(chunk_size, current_timeout);
0
     if (result == APR_EOF || (result == APR_SUCCESS && chunk_size == 0)) {
0
+ P_DEBUG("DispatcherBucket " << this << ": EOF");
0
       b = apr_bucket_immortal_make(b, "", 0);
0
       *str = (const char *) b->data;
0
       close(pipe);
0
       closed = true;
0
       return APR_SUCCESS;
0
     } else if (result != APR_SUCCESS) {
0
+ P_DEBUG("DispatcherBucket " << this << ": APR error " << result);
0
       return result;
0
     }
0
 
0
@@ -174,21 +158,24 @@ public:
0
       *str = chunk;
0
       *len = chunk_size;
0
       APR_BUCKET_INSERT_AFTER(b, dup_bucket(b->list));
0
+ P_DEBUG("DispatcherBucket " << this << ": read (" << string(*str, *len) << ")");
0
       return APR_SUCCESS;
0
     } else if (result == APR_EOF) {
0
+ P_DEBUG("DispatcherBucket " << this << ": EOF");
0
       b = apr_bucket_immortal_make(b, "", 0);
0
       *str = (const char *) b->data;
0
       close(pipe);
0
       closed = true;
0
       return APR_SUCCESS;
0
     } else {
0
+ P_DEBUG("DispatcherBucket " << this << ": APR error " << result);
0
       return result;
0
     }
0
   }
0
 };
0
 
0
-extern "C" apr_bucket *
0
-dispatcher_bucket_create(apr_pool_t *pool, int pipe, apr_interval_time_t timeout, apr_bucket_alloc_t *list) {
0
+apr_bucket *
0
+dispatcher_bucket_create(apr_pool_t *pool, ApplicationPtr app, apr_interval_time_t timeout, apr_bucket_alloc_t *list) {
0
   apr_bucket *b;
0
   DispatcherBucket *data;
0
 
0
@@ -208,10 +195,14 @@ dispatcher_bucket_create(apr_pool_t *pool, int pipe, apr_interval_time_t timeout
0
     apr_bucket_free(b);
0
     return NULL;
0
   }
0
- data->pipe = pipe;
0
+ data->app = app;
0
+ data->pipe = app->getReader();
0
   data->timeout = timeout;
0
   data->closed = false;
0
   b->data = data;
0
+ apr_pool_cleanup_register(pool, data, dispatcher_bucket_pool_cleaner, apr_pool_cleanup_null);
0
+
0
+ P_DEBUG("DispatcherBucket " << data << " created.");
0
   return b;
0
 }
0
 
0
@@ -224,6 +215,16 @@ static void
0
 dispatcher_bucket_destroy(void *d) {
0
   DispatcherBucket *data = (DispatcherBucket *) d;
0
   if (!data->closed) {
0
+ P_DEBUG("Closing file descriptor for DispatcherBucket " << d);
0
+ data->closed = true;
0
     close(data->pipe);
0
   }
0
+ data->app = ApplicationPtr();
0
+ P_DEBUG("DispatcherBucket " << d << " destroyed.");
0
+}
0
+
0
+static apr_status_t
0
+dispatcher_bucket_pool_cleaner(void *d) {
0
+ dispatcher_bucket_destroy(d);
0
+ return APR_SUCCESS;
0
 }
...
3
4
5
 
6
7
8
9
10
11
12
13
14
15
 
 
16
17
...
3
4
5
6
7
 
 
 
 
 
 
 
 
 
8
9
10
11
0
@@ -3,15 +3,9 @@
0
 
0
 #include <apr_pools.h>
0
 #include <apr_buckets.h>
0
+#include "Application.h"
0
 
0
-#ifdef __cplusplus
0
-extern "C" {
0
-#endif
0
-
0
-apr_bucket *dispatcher_bucket_create(apr_pool_t *pool, int pipe, apr_interval_time_t timeout, apr_bucket_alloc_t *list);
0
-
0
-#ifdef __cplusplus
0
-}
0
-#endif
0
+apr_bucket *dispatcher_bucket_create(apr_pool_t *pool, Passenger::ApplicationPtr app,
0
+ apr_interval_time_t timeout, apr_bucket_alloc_t *list);
0
 
0
 #endif /* _DISPATCHER_BUCKET_H_ */
...
1
2
3
4
5
 
 
 
6
7
8
9
10
 
11
12
13
 
14
15
 
 
 
 
 
 
 
16
17
 
 
 
18
19
20
...
1
2
3
 
 
4
5
6
7
8
9
10
 
11
12
13
 
14
15
 
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
0
@@ -1,20 +1,30 @@
0
 APXS=apxs2
0
 APACHECTL=apache2ctl
0
 CXX=g++
0
-CXXFLAGS=-Wall -fPIC -g `pkg-config --cflags apr-util-1`
0
-OBJECTS=DispatcherBucket.o
0
+CXXFLAGS=-Wall -fPIC -g -DPASSENGER_DEBUG `pkg-config --cflags apr-1 apr-util-1` `$(APXS) -q CFLAGS` -I`$(APXS) -q INCLUDEDIR`
0
+OBJECTS=Configuration.o Hooks.o DispatcherBucket.o Utils.o
0
+LIBTOOL_OBJECTS=Configuration.o,Hooks.o,DispatcherBucket.o,Utils.o
0
 
0
 .PHONY: all install clean start restart stop
0
 
0
 all: $(OBJECTS)
0
- CC=g++ $(APXS) -c mod_rails.c -Wc,DispatcherBucket.o -Wl,-lstdc++
0
+ $(APXS) -c mod_rails.c -Wc,$(LIBTOOL_OBJECTS) -Wl,-lstdc++
0
 
0
 install: $(OBJECTS)
0
- CC=g++ $(APXS) -i -c mod_rails.c -Wc,DispatcherBucket.o -Wl,-lstdc++
0
+ $(APXS) -i -c mod_rails.c -Wc,$(LIBTOOL_OBJECTS) -Wl,-lstdc++
0
 
0
-DispatcherBucket.o: DispatcherBucket.cpp
0
+Configuration.o: Configuration.cpp
0
+ $(CXX) $(CXXFLAGS) -c Configuration.cpp
0
+
0
+Hooks.o: Hooks.cpp Hooks.h Types.h DispatcherBucket.h ApplicationPool.h SpawnManager.h Exceptions.h Application.h MessageChannel.h Utils.h
0
+ $(CXX) $(CXXFLAGS) -c Hooks.cpp
0
+
0
+DispatcherBucket.o: DispatcherBucket.h DispatcherBucket.cpp Utils.h
0
   $(CXX) $(CXXFLAGS) -c DispatcherBucket.cpp
0
 
0
+Utils.o: Utils.cpp Utils.h
0
+ $(CXX) $(CXXFLAGS) -c Utils.cpp
0
+
0
 clean:
0
   rm -rf *.o mod_rails.lo mod_rails.slo mod_rails.la .libs
0
 
...
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
 
 
 
59
60
61
62
63
64
 
65
66
67
...
168
169
170
171
172
173
174
...
43
44
45
 
46
47
48
49
50
51
 
 
 
 
 
 
52
53
54
55
56
 
 
 
 
57
58
59
60
...
161
162
163
 
164
165
166
0
@@ -43,25 +43,18 @@ public:
0
     list<string>::const_iterator it;
0
     string data;
0
     uint16_t dataSize = 0;
0
- unsigned int i = 0;
0
     string::size_type written;
0
     int ret;
0
 
0
     for (it = args.begin(); it != args.end(); it++) {
0
       dataSize += it->size() + 1;
0
     }
0
- if (!args.empty()) {
0
- dataSize--;
0
- data.reserve(dataSize + sizeof(dataSize));
0
- dataSize = htons(dataSize);
0
- data.append((const char *) &dataSize, sizeof(dataSize));
0
- }
0
+ data.reserve(dataSize + sizeof(dataSize));
0
+ dataSize = htons(dataSize);
0
+ data.append((const char *) &dataSize, sizeof(dataSize));
0
     for (it = args.begin(); it != args.end(); it++) {
0
       data.append(*it);
0
- if (i != args.size() - 1) {
0
- data.append(1, DELIMITER);
0
- }
0
- i++;
0
+ data.append(1, DELIMITER);
0
     }
0
     
0
     written = 0;
0
@@ -168,7 +161,6 @@ public:
0
         args.push_back(const_buffer.substr(start, pos - start));
0
         start = pos + 1;
0
       }
0
- args.push_back(const_buffer.substr(start));
0
     }
0
     return true;
0
   }
...
32
33
34
35
 
36
37
 
38
39
40
...
42
43
44
45
 
 
 
 
 
 
 
 
 
 
46
47
48
49
50
 
 
 
 
 
51
52
53
...
64
65
66
 
 
 
67
68
69
...
32
33
34
 
35
36
37
38
39
40
41
...
43
44
45
 
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
...
79
80
81
82
83
84
85
86
87
0
@@ -32,9 +32,10 @@ private:
0
   pid_t pid;
0
 
0
 public:
0
- SpawnManager(const string &spawnManagerCommand, const string &rubyCommand = "ruby") {
0
+ SpawnManager(const string &spawnManagerCommand, const string &logFile = "", const string &rubyCommand = "ruby") {
0
     int fds[2];
0
     char fd_string[20];
0
+ FILE *logFileHandle;
0
     
0
     if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) == -1) {
0
       throw SystemException("Cannot create a Unix socket", errno);
0
@@ -42,12 +43,26 @@ public:
0
     if (apr_snprintf(fd_string, sizeof(fd_string), "%d", fds[1]) <= 0) {
0
       throw MemoryException();
0
     }
0
-
0
+ if (!logFile.empty()) {
0
+ logFileHandle = fopen(logFile.c_str(), "a");
0
+ if (logFileHandle == NULL) {
0
+ string message("Cannot open log file '");
0
+ message.append(logFile);
0
+ message.append("' for writing.");
0
+ throw IOException(message);
0
+ }
0
+ }
0
+
0
     pid = fork();
0
     if (pid == 0) {
0
       // TODO: redirect stderr to a log file
0
       pid = fork();
0
       if (pid == 0) {
0
+ if (!logFile.empty()) {
0
+ dup2(fileno(logFileHandle), STDOUT_FILENO);
0
+ dup2(fileno(logFileHandle), STDERR_FILENO);
0
+ fclose(logFileHandle);
0
+ }
0
         close(fds[0]);
0
         execlp(rubyCommand.c_str(), rubyCommand.c_str(), spawnManagerCommand.c_str(), fd_string, NULL);
0
         fprintf(stderr, "Unable to run ruby: %s\n", strerror(errno));
0
@@ -64,6 +79,9 @@ public:
0
       throw SystemException("Unable to fork a process", errno);
0
     } else {
0
       close(fds[1]);
0
+ if (!logFile.empty()) {
0
+ fclose(logFileHandle);
0
+ }
0
       waitpid(pid, NULL, 0);
0
       channel = MessageChannel(fds[0]);
0
     }
...
1
2
3
4
5
6
7
8
9
10
 
 
 
 
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
 
 
 
 
 
 
29
...
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
 
 
 
 
 
 
 
 
 
6
7
 
 
 
 
 
 
8
9
10
11
12
13
14
0
@@ -1,29 +1,14 @@
0
-#include "ap_config.h"
0
-#include "apr_lib.h"
0
-#include "apr_strings.h"
0
-#include "httpd.h"
0
-#include "http_config.h"
0
-#include "http_core.h"
0
-#include "http_request.h"
0
-#include "http_log.h"
0
-#include "http_protocol.h"
0
-#include "mpm_common.h"
0
+#include <httpd.h>
0
+#include <http_config.h>
0
+#include "Configuration.h"
0
+#include "Hooks.h"
0
 
0
-module AP_MODULE_DECLARE_DATA rails_module;
0
-#define MOD_RAILS_VERSION "1.0.0"
0
-#define INSIDE_MOD_RAILS
0
-#include "types.h"
0
-#include "utils.c"
0
-#include "config.c"
0
-#include "hooks.c"
0
-
0
-/* Dispatch list for API hooks */
0
 module AP_MODULE_DECLARE_DATA rails_module = {
0
   STANDARD20_MODULE_STUFF,
0
- create_dir_config, /* create per-dir config structs */
0
- merge_dir_config, /* merge per-dir config structs */
0
- create_server_config, /* create per-server config structs */
0
- merge_server_config, /* merge per-server config structs */
0
- mod_rails_cmds, /* table of config file commands */
0
- mod_rails_register_hooks, /* register hooks */
0
+ passenger_config_create_dir, /* create per-dir config structs */
0
+ passenger_config_merge_dir, /* merge per-dir config structs */
0
+ passenger_config_create_server, /* create per-server config structs */
0
+ passenger_config_merge_server, /* merge per-server config structs */
0
+ passenger_commands, /* table of config file commands */
0
+ passenger_register_hooks, /* register hooks */
0
 };
...
27
28
29
30
 
31
32
33
...
27
28
29
 
30
31
32
33
0
@@ -27,7 +27,7 @@ class Application
0
   # Return the Ruby on Rails version that the application requires, or nil
0
   # if it doesn't require a particular version.
0
   def self.get_framework_version(app_root)
0
- File.read("#{app_root}/environment.rb") =~ /^[^#]*RAILS_GEM_VERSION\s+=\s+'([\d.]+)'/
0
+ File.read("#{app_root}/config/environment.rb") =~ /^[^#]*RAILS_GEM_VERSION\s+=\s+'([\d.]+)'/
0
     return $1
0
   end
0
 
...
53
54
55
56
57
58
 
 
 
 
 
 
 
 
 
 
 
59
60
61
...
75
76
77
78
79
80
 
 
 
81
82
83
...
53
54
55
 
 
 
56
57
58
59
60
61
62
63
64
65
66
67
68
69
...
83
84
85
 
 
 
86
87
88
89
90
91
0
@@ -53,9 +53,17 @@ class MessageChannel
0
       buffer << @io.readpartial(chunk_size - buffer.size)
0
     end
0
     
0
- message = buffer.split(DELIMITER)
0
- if buffer[chunk_size - 1] == DELIMITER[0]
0
- message << ""
0
+ message = []
0
+ offset = 0
0
+ delimiter_pos = buffer.index(DELIMITER, offset)
0
+ while !delimiter_pos.nil?
0
+ if delimiter_pos == 0
0
+ message << ""
0
+ else
0
+ message << buffer[offset .. delimiter_pos - 1]
0
+ end
0
+ offset = delimiter_pos + 1
0
+ delimiter_pos = buffer.index(DELIMITER, offset)
0
     end
0
     return message
0
   rescue EOFError
0
@@ -75,9 +83,9 @@ class MessageChannel
0
       check_argument(arg)
0
     end
0
     
0
- message = "#{name}"
0
- if !args.empty?
0
- message << DELIMITER << args.join(DELIMITER)
0
+ message = "#{name}#{DELIMITER}"
0
+ args.each do |arg|
0
+ message << arg.to_s << DELIMITER
0
     end
0
     @io.write([message.size].pack('n') << message)
0
     @io.flush
...
 
1
2
3
...
27
28
29
30
31
32
 
 
 
 
33
34
35
...
50
51
52
 
 
 
53
54
55
56
 
 
 
 
 
 
57
58
 
59
60
61
62
63
64
 
 
65
66
67
...
1
2
3
4
...
28
29
30
 
 
 
31
32
33
34
35
36
37
...
52
53
54
55
56
57
58
 
 
 
59
60
61
62
63
64
65
 
66
67
68
69
70
71
72
73
74
75
76
77
0
@@ -1,3 +1,4 @@
0
+require 'mod_rails/message_channel'
0
 # NOTE: we make use of pipes instead of Unix sockets, because
0
 # experimentation has shown that pipes are slightly faster.
0
 module ModRails # :nodoc:
0
@@ -27,9 +28,10 @@ class RequestHandler
0
     end
0
   end
0
 
0
- def initialize(reader_pipe, writer_pipe)
0
- @reader = reader_pipe
0
- @writer = writer_pipe
0
+ def initialize(reader, writer)
0
+ @reader = reader
0
+ @writer = writer
0
+ @reader_channel = MessageChannel.new(reader)
0
     @previous_signal_handlers = {}
0
   end
0
   
0
@@ -50,18 +52,26 @@ class RequestHandler
0
   end
0
   
0
   def process_next_request
0
+ content = "hello <b>world</b>!<br>\n"
0
+
0
+ STDERR.puts "--- #{$$} BEGIN"
0
     done = false
0
- chunk = read_chunk
0
- while !chunk.nil?
0
- chunk = read_chunk
0
+ while !done
0
+ header, value = @reader_channel.read
0
+ if !header.empty?
0
+ content << "<tt>#{header} = #{value}</tt><br>\n"
0
+ end
0
+ done = header.empty?
0
     end
0
- content = "hello <b>world</b>!"
0
+
0
     write_chunk("Status: 200 OK\r\n")
0
     write_chunk("Content-Type: text/html\r\n")
0
     write_chunk("Content-Length: #{content.size}\r\n")
0
     write_chunk("\r\n")
0
     write_chunk(content)
0
     write_chunk("")
0
+ STDERR.puts "--- #{$$} END"
0
+ STDERR.flush
0
   end
0
 
0
 private
...
36
37
38
 
39
40
41
...
36
37
38
39
40
41
42
0
@@ -36,6 +36,7 @@ class SpawnManager
0
 
0
   def spawn_application(app_root, username = nil)
0
     framework_version = Application.get_framework_version(app_root)
0
+ spawner = nil
0
     @lock.synchronize do
0
       spawner = @spawners[framework_version]
0
       if !spawner
...
32
33
34
35
 
36
37
 
38
39
40
...
32
33
34
 
35
36
37
38
39
40
41
0
@@ -32,9 +32,10 @@ private
0
   
0
   def print_exception(current_location, exception)
0
     STDERR.puts("** Exception #{exception.class} in #{current_location} " <<
0
- "(#{exception}):\n" <<
0
+ "(#{exception}) (process #{$$}):\n" <<
0
       "\tfrom " <<
0
       exception.backtrace.join("\n\tfrom "))
0
+ STDERR.flush
0
   end
0
 end
0
 
...
35
36
37
38
 
39
40
41
...
51
52
53
54
 
55
56
57
...
98
99
100
101
102
103
 
 
 
 
104
105
106
107
 
 
108
109
110
111
112
113
 
 
114
115
116
...
148
149
150
 
 
 
 
 
 
 
 
 
 
 
151
...
35
36
37
 
38
39
40
41
...
51
52
53
 
54
55
56
57
...
98
99
100
 
 
 
101
102
103
104
105
106
107
 
108
109
110
111
112
113
114
 
115
116
117
118
119
...
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
0
@@ -35,7 +35,7 @@ namespace tut {
0
     vector<string> args;
0
     
0
     writer.write("hello", "world", "!", NULL);
0
- ensure("end-of-file has not been reached", reader.read(args));
0
+ ensure("End of file has not been reached", reader.read(args));
0
     ensure_equals("read() returns the same number of arguments as passed to write()", args.size(), 3u);
0
     ensure_equals(args[0], "hello");
0
     ensure_equals(args[1], "world");
0
@@ -51,7 +51,7 @@ namespace tut {
0
     input.push_back("world");
0
     input.push_back("!");
0
     writer.write(input);
0
- ensure("end-of-file has not been reached", reader.read(output));
0
+ ensure("End of file has not been reached", reader.read(output));
0
     ensure_equals("read() returns the same number of arguments as passed to write()", input.size(), output.size());
0
     
0
     list<string>::const_iterator it;
0
@@ -98,19 +98,22 @@ namespace tut {
0
       input.write("you have", "not enough", "minerals", NULL);
0
       input.close();
0
       
0
- vector<string> message1, message2;
0
- ensure("End of stream has not been reached", output.read(message1));
0
- ensure("End of stream has not been reached", output.read(message2));
0
+ vector<string> message1, message2, message3;
0
+ ensure("End of stream has not been reached (1)", output.read(message1));
0
+ ensure("End of stream has not been reached (2)", output.read(message2));
0
+ ensure("End of file has been reached", !output.read(message3));
0
       output.close();
0
       waitpid(pid, NULL, 0);
0
       
0
- ensure_equals(message1.size(), 4u);
0
+ ensure_equals("First message is correctly transformed by the mock object",
0
+ message1.size(), 4u);
0
       ensure_equals(message1[0], "hello");
0
       ensure_equals(message1[1], "my beautiful");
0
       ensure_equals(message1[2], "world");
0
       ensure_equals(message1[3], "!!");
0
       
0
- ensure_equals(message2.size(), 4u);
0
+ ensure_equals("Second message is correctly transformed by the mock object",
0
+ message2.size(), 4u);
0
       ensure_equals(message2[0], "you have");
0
       ensure_equals(message2[1], "not enough");
0
       ensure_equals(message2[2], "minerals");
0
@@ -148,4 +151,15 @@ namespace tut {
0
     close(my_pipe[0]);
0
     close(my_pipe[1]);
0
   }
0
+
0
+ TEST_METHOD(8) {
0
+ // write() should be able to construct a message that consists of only an empty string.
0
+ // read() should be able to read a message that consists of only an empty string.
0
+ vector<string> args;
0
+
0
+ writer.write("", NULL);
0
+ reader.read(args);
0
+ ensure_equals(args.size(), 1u);
0
+ ensure_equals(args[0], "");
0
+ }
0
 }
...
62
63
64
 
 
 
 
 
 
 
 
 
 
 
65
66
67
...
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
0
@@ -62,6 +62,17 @@ class MessageChannelTest < Test::Unit::TestCase
0
       end
0
     )
0
   end
0
+
0
+ def test_empty_arguments
0
+ with_pipe_channel do
0
+ @writer.write("hello", "", "world")
0
+ @writer.write("")
0
+ @writer.write(nil, "foo")
0
+ assert_equal(["hello", "", "world"], @reader.read)
0
+ assert_equal([""], @reader.read)
0
+ assert_equal(["", "foo"], @reader.read)
0
+ end
0
+ end
0
 
0
 private
0
   def with_pipe_channel

Comments

    No one has commented yet.