first check-in of feisty meow codebase. many things broken still due to recent
[feisty_meow.git] / octopi / library / tests_sockets / test_bcast_spocket.cpp
1 /*
2 * Name   : test_bcast_spocket
3 * Author : Chris Koeritz
4 * Purpose: This is the "main" program for our sockets tester.  It parses command line
5 * parameters and starts up the tester class.
6 **
7 * Copyright (c) 2001-$now By Author.  This program is free software; you can  *
8 * redistribute it and/or modify it under the terms of the GNU General Public  *
9 * License as published by the Free Software Foundation; either version 2 of   *
10 * the License or (at your option) any later version.  This is online at:      *
11 *     http://www.fsf.org/copyleft/gpl.html                                    *
12 * Please send any updates to: fred@gruntose.com                               *
13 */
14
15 #include "bcast_spocketer.h"
16
17 #include <application/command_line.h>
18 #include <application/hoople_main.h>
19 #include <basis/byte_array.h>
20 #include <basis/astring.h>
21 #include <loggers/program_wide_logger.h>
22 #include <processes/launch_process.h>
23 #include <structures/static_memory_gremlin.h>
24 #include <sockets/internet_address.h>
25 #include <unit_test/unit_base.h>
26 #include <timely/time_control.h>
27
28 #include <stdio.h>
29
30 using namespace application;
31 using namespace basis;
32 using namespace filesystem;
33 using namespace loggers;
34 using namespace mathematics;
35 using namespace processes;
36 using namespace sockets;
37 using namespace structures;
38 using namespace textual;
39 using namespace timely;
40 using namespace unit_test;
41
42 #define LOG(to_print) EMERGENCY_LOG(program_wide_logger().get(), astring(to_print))
43
44 const int INITIAL_DELAY = 1;  // number of seconds before starting sends.
45
46 typedef abyte ip_address_holder[4];
47
48 class test_bcast_spocket : public virtual unit_base, public virtual application_shell
49 {
50 public:
51   test_bcast_spocket() {}
52   DEFINE_CLASS_NAME("test_bcast_spocket");
53   virtual int execute();
54   bool parse_address(const astring &input, ip_address_holder &ip_address);
55 };
56
57 //hmmm: extract this to function in sockets.
58 bool test_bcast_spocket::parse_address(const astring &input, ip_address_holder &ip_address)
59 {
60   int index = 0;  // current storage position in our array.
61   int current_byte = 0;
62   const char *last_period = 0;  // helps check for non-empty numbers.
63   bool got_digit = false;
64   for (const char *address = input.s(); *address; address++) {
65     if ( (*address <= '9') && (*address >= '0') ) {
66       current_byte *= 10;  // shift over.
67       current_byte += *address - '0';  // add in current character.
68       got_digit = true;
69     } else if (*address == '.') {
70       got_digit = false;
71       if (last_period + 1 == address) {
72         LOG("The IP address entry has an empty digit.  Exiting.");
73         return false;
74       }
75       last_period = address;  // set our last period location.
76       if (current_byte > 255) {
77         LOG("The IP address entry has an illegal abyte.  Exiting.");
78         return false;
79       }
80       ip_address[index] = abyte(current_byte);  // store current byte.
81       current_byte = 0;  // reset.
82       index++;  // next place in array.
83       if (index > 3) break;
84         // stop if there are too many periods, but keep accumulated address.
85     } else {
86       LOG("The IP address entry has illegal characters.  Exiting.");
87       return false;
88     }
89   }
90   // catch the case where we ran out of chars before adding the last byte.
91   if ( (index == 3) && got_digit) {
92     if (current_byte > 255) {
93       LOG("The IP address entry has an illegal abyte.  Exiting.");
94       return false;
95     }
96     ip_address[index] = current_byte;
97   } else if (index < 4) {
98     LOG("The IP address entry is too short.  Exiting.");
99     return false;
100   }
101   return true;
102 }
103
104 int test_bcast_spocket::execute()
105 {
106   FUNCDEF("execute");
107   ip_address_holder ip_address;  // accumulates the source address.
108   ip_address_holder dest_addr;  // where to send stuff to.
109   int rcv_port = 0;
110   int send_port = 0;
111   int send_size = 0;
112   int send_count = 0;
113
114   const char *DEFAULT_HOST = "127.0.0.1";
115   const int DEFAULT_PORT = 12342;
116   const int DEFAULT_SEND_SIZE = 1008;
117   const int DEFAULT_SEND_COUNT = 10;
118
119   if (_global_argc < 7) {
120     if (_global_argc > 1) {
121       LOG("\
122 This program takes six command line arguments to begin operation.\n\
123 These arguments (in order) are:\n\
124 \tIP address for src\tIn the form w.x.y.z\n\
125 \tIP address for dest\tIn the form w.x.y.z\n\
126 \tReceive Port number\tAs a short integer\n\
127 \tSending Port number\tAs a short integer\n\
128 \tSend size\t\tThe size of the data to exchange.\n\
129 \tSend count\t\tThe number of \"packets\" to exchange.\n\
130 Note: it is expected that the testers have equal send sizes; this\n\
131 allows the receiver to know when it's gotten all the data that's\n\
132 expected during a cycle.");
133       return 1;  // bail if they provided anything; otherwise we test.
134     } else {
135       parse_address(DEFAULT_HOST, ip_address);
136       parse_address(DEFAULT_HOST, dest_addr);
137       rcv_port = DEFAULT_PORT;
138       send_port = DEFAULT_PORT + 1;
139       send_size = DEFAULT_SEND_SIZE;
140       send_count = DEFAULT_SEND_COUNT;
141     }
142   }
143
144   // only parse the parameters if we got enough from the user; otherwise we accept our
145   // defaults to do a simple test run.
146   if (_global_argc >= 7) {
147
148     if (!parse_address(_global_argv[1], ip_address)) {
149       LOG("failed to parse source address.");
150       return 9283;
151     }
152
153     LOG(a_sprintf("\tParsed a source of: \"%d.%d.%d.%d\".",
154         (int)ip_address[0], (int)ip_address[1], (int)ip_address[2],
155         (int)ip_address[3]));
156
157     if (!parse_address(_global_argv[2], dest_addr)) {
158       LOG("failed to parse dest address.");
159       return 9283;
160     }
161
162     LOG(a_sprintf("\tParsed a destination of: \"%d.%d.%d.%d\".",
163         (int)dest_addr[0], (int)dest_addr[1], (int)dest_addr[2],
164         (int)dest_addr[3]));
165
166     // parse the third parameter: the port.
167     if (sscanf(_global_argv[3], "%d", &rcv_port) < 1) {
168       LOG("The port entry is malformed.  Exiting.");
169       return 3;
170     }
171     LOG(a_sprintf("\tGot a receive port of %d.", rcv_port));
172
173     // parse the fourth parameter: the port.
174     if (sscanf(_global_argv[4], "%d", &send_port) < 1) {
175       LOG("The port entry is malformed.  Exiting.");
176       return 3;
177     }
178     LOG(a_sprintf("\tGot a send port of %d.", send_port));
179
180     // parse the fifth parameter: the size of the sends.
181     if (sscanf(_global_argv[5], "%d", &send_size) < 1) {
182       LOG("The send size entry is malformed.  Exiting.");
183       return 5;
184     }
185     LOG(a_sprintf("\tGot a send size of %d.", send_size));
186
187     // parse the sixth parameter: the number of sends.
188     if (sscanf(_global_argv[6], "%d", &send_count) < 1) {
189       LOG("The send count entry is malformed.  Exiting.");
190       return 5;
191     }
192     LOG(a_sprintf("\tGot a send count of %d.", send_count));
193   }
194
195   if (_global_argc == 1) {
196     // launch a paired duplicate of our test so we can chat.
197     launch_process zingit;
198     un_int kidnum;
199     un_int result = zingit.run(_global_argv[0],
200         astring(DEFAULT_HOST) + " " +  DEFAULT_HOST + " "
201         /* we have reversed the send and receive ports. */
202         + a_sprintf("%d", DEFAULT_PORT + 1) + " " + a_sprintf("%d", DEFAULT_PORT)
203         + " " + a_sprintf("%d", DEFAULT_SEND_SIZE) + " " + a_sprintf("%d", DEFAULT_SEND_COUNT),
204         launch_process::RETURN_IMMEDIATELY, kidnum);
205     ASSERT_EQUAL(result, 0, "launching paired process should start successfully");
206   }
207
208   // package our parameters in a form the tester likes.
209   internet_address to_pass(byte_array(4, ip_address), "", rcv_port);
210   internet_address dest(byte_array(4, dest_addr), "", send_port);
211
212   // now, construct our tester object.
213   broadcast_spocket_tester tester(to_pass);
214
215   // choose the appropriate action based on our role.
216   bool outcome = tester.connect();
217   if (!outcome) {
218     LOG(astring("Failed to connect on the tester."));
219     return 10;
220   }
221
222   LOG(a_sprintf("you now have %d seconds; get other side ready.",
223       INITIAL_DELAY));
224   time_control::sleep_ms(INITIAL_DELAY * SECOND_ms);
225   LOG("starting test");
226
227   // so, we're connected.  try sending the test packages out.
228   testing_statistics stats;  // to be filled by the tester.
229   outcome = tester.perform_test(dest, send_size, send_count * 2, stats);
230     // multiply send_count since we count each side as one.
231   if (!outcome) {
232     LOG("Failed out of send_data; maybe other side terminated.");
233   }
234
235   stats.total_runs /= 2;  // cut down to the real number of tests.
236
237   if (!stats.total_runs)
238     stats.total_runs = 1;
239
240   // now report on the stats that we get from the data sending.
241   LOG(a_sprintf("Report for %d completed test cycles.", stats.total_runs));
242   LOG("");
243   LOG("\t\tsend stats\t\treceive stats");
244   LOG("\t\t----------\t\t-------------");
245   LOG(a_sprintf("bytes\t\t%d\t\t\t%d", stats.bytes_sent,
246       stats.bytes_received));
247   LOG(a_sprintf("time\t\t%d\t\t\t%d", stats.send_time, stats.receive_time));
248   LOG(a_sprintf("avg. bytes\t%d\t\t\t%d", stats.bytes_sent
249       / stats.total_runs / 2, stats.bytes_received / stats.total_runs / 2));
250   LOG("");
251   LOG(a_sprintf("round trip time: %d ms", stats.round_trip_time));
252 //hmmm: use the bandwidth measurer object!!!
253   double bandwidth = double(stats.bytes_sent + stats.bytes_received)
254       / stats.round_trip_time / 1024.0 * 1000.0;
255   LOG(a_sprintf("bandwidth overall: %f K/s", bandwidth));
256
257   if (_global_argc == 1) return final_report();
258   else return 0;  // no unit test report for non-top-level process
259 }
260
261 HOOPLE_MAIN(test_bcast_spocket, );
262