first check-in of feisty meow codebase. many things broken still due to recent
[feisty_meow.git] / octopi / library / tests_sockets / bcast_spocketer.cpp
1 /*****************************************************************************\
2 *                                                                             *
3 *  Name   : broadcast_spocket_tester                                          *
4 *  Author : Chris Koeritz                                                     *
5 *                                                                             *
6 *******************************************************************************
7 * Copyright (c) 2000-$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 <basis/astring.h>
18 #include <basis/byte_array.h>
19 #include <basis/functions.h>
20 #include <loggers/critical_events.h>
21 #include <loggers/program_wide_logger.h>
22 #include <mathematics/chaos.h>
23 #include <sockets/internet_address.h>
24 #include <sockets/raw_socket.h>
25 #include <sockets/spocket.h>
26 #include <sockets/tcpip_definitions.h>
27 #include <sockets/tcpip_stack.h>
28 #include <timely/time_control.h>
29 #include <timely/time_stamp.h>
30 #include <unit_test/unit_base.h>
31
32 #include <errno.h>
33
34 //using namespace application;
35 using namespace basis;
36 //using namespace filesystem;
37 using namespace loggers;
38 using namespace mathematics;
39 using namespace sockets;
40 using namespace structures;
41 //using namespace textual;
42 using namespace timely;
43 using namespace unit_test;
44
45 #define LOG(to_print) EMERGENCY_LOG(program_wide_logger().get(), astring(to_print))
46
47 const int MAXIMUM_WINSOCK_MTU = 1500;
48   // the largest chunk of bytes we can receive at once.
49
50 const int MAXIMUM_TRANSFER_WAIT = 40 * SECOND_ms;
51   // the longest amount of time we wait in trying to receive data.
52
53 static abyte receive_buffer[MAXIMUM_WINSOCK_MTU + 1];
54   // used for dumping received data into.
55
56 //#define DEBUG_SPOCKET_TESTER
57   // uncomment for noisy version.
58
59 broadcast_spocket_tester::broadcast_spocket_tester
60     (const internet_address &where, bool unicast)
61 : _where(new internet_address(where)),
62   _stack(new tcpip_stack),
63   _socket(NIL),
64   _raw(new raw_socket),
65   _ucast(unicast)
66 {
67 }
68
69 broadcast_spocket_tester::~broadcast_spocket_tester()
70 {
71   WHACK(_socket);
72   WHACK(_stack);
73   WHACK(_where);
74   WHACK(_raw);
75 }
76
77 bool broadcast_spocket_tester::connect()
78 {
79   spocket::sock_types type = spocket::BROADCAST;
80   if (_ucast) type = spocket::UNICAST;
81   if (!_socket) {
82     _socket = new spocket(*_where, type);
83   }
84   outcome ret = _socket->connect();
85   return ret == spocket::OKAY;
86 }
87
88 bool broadcast_spocket_tester::do_a_send(const internet_address &where_to,
89     abyte *buffer, int size, testing_statistics &stats)
90 {
91   time_stamp start_time;
92   int len_sent;
93   time_stamp when_to_leave(MAXIMUM_TRANSFER_WAIT);
94   outcome worked;
95
96 #ifdef DEBUG_SPOCKET_TESTER
97   LOG(a_sprintf("into do a send with %d bytes", size));
98 #endif
99
100   while (time_stamp() < when_to_leave) {
101     worked = _socket->send_to(where_to, buffer, size, len_sent);
102     if (worked == spocket::NONE_READY) {
103       time_control::sleep_ms(20);
104       continue;
105     } else if (worked == spocket::PARTIAL) {
106 //danger danger if we get wrong info.
107       buffer += len_sent;
108       size -= len_sent;
109       time_control::sleep_ms(20);
110       continue;
111     } else break;
112   }
113 #ifdef DEBUG_SPOCKET_TESTER
114   LOG("got out of loop");
115 #endif
116
117   stats.send_time += int(time_stamp().value() - start_time.value());
118
119   if ( (worked != spocket::OKAY) || !len_sent) {
120     LOG("No data went out on the socket.");
121     return false;
122   }
123   if (len_sent != size) {
124     LOG(a_sprintf("The full chunk didn't get sent out: %d bytes instead of %d",
125         len_sent, size));
126 //more bad news.  what do we do about getting the rest out?
127     return false;
128   }
129   stats.bytes_sent += len_sent;
130
131   time_stamp end_time;
132 //  int time_taken = int(end_time.value() - start_time.value());
133
134   return true;
135 }
136
137 bool broadcast_spocket_tester::do_a_receive(int size_expected,
138     testing_statistics &stats)
139 {
140   time_stamp start_time;
141
142 #ifdef DEBUG_SPOCKET_TESTER
143   LOG("into do a rcv");
144 #endif
145
146   time_stamp when_to_leave(MAXIMUM_TRANSFER_WAIT);
147   int full_length = 0;
148   while ( (full_length < size_expected) && (time_stamp() < when_to_leave) ) {
149     time_stamp start_of_receive;
150     int len = MAXIMUM_WINSOCK_MTU;
151     internet_address where_from;
152     outcome ret = _socket->receive_from(receive_buffer, len, where_from);
153 ///LOG(astring("recvfrom outcome is ") + spocket::outcome_name(ret));
154     if (ret != spocket::OKAY) {
155       if (ret == spocket::NONE_READY) {
156         time_control::sleep_ms(20);
157         continue;
158       } else break;
159     }
160     // reset our time if we've gotten good data.
161     if (ret == spocket::OKAY)
162       when_to_leave.reset(MAXIMUM_TRANSFER_WAIT);
163
164     int receive_duration = int(time_stamp().value()
165         - start_of_receive.value());
166     stats.receive_time += receive_duration;
167
168 #ifdef DEBUG_SPOCKET_TESTER
169     LOG(a_sprintf("did recv, len=%d", len));
170 #endif
171
172     if (!len) {
173       LOG("Our socket has been disconnected.");
174       return false;
175     } else if (len < 0) {
176       if (errno == SOCK_EWOULDBLOCK) continue;  // no data.
177       LOG(astring("The receive failed with an error ")
178           + critical_events::system_error_text(errno));
179       return false;
180     }
181     full_length += len;
182     stats.bytes_received += len;
183   }
184
185   if (full_length != size_expected)
186     LOG(a_sprintf("Did not get the full size expected (wanted %d and "
187         "got %d bytes).", size_expected, full_length));
188
189   return true;
190 }
191
192 bool broadcast_spocket_tester::perform_test(const internet_address &dest,
193     int size, int count, testing_statistics &stats)
194 {
195 #ifdef DEBUG_SPOCKET_TESTER
196   LOG("into perf test");
197 #endif
198
199   // the statics are used to generate our random buffer for sending.
200   static abyte garbage_buffer[MAXIMUM_WINSOCK_MTU + 1];
201   static bool garbage_initialized = false;
202   chaos randomizer;
203
204   // if our static buffer full of random stuff was never initialized, we do
205   // so now.  this supports efficiently re-using the tester if desired.
206   if (!garbage_initialized) {
207     // note the less than or equal; we know we have one more byte to fill.
208     for (int i = 0; i <= MAXIMUM_WINSOCK_MTU; i++)
209       garbage_buffer[i] = randomizer.inclusive(0, 255);
210     garbage_initialized = true;
211   }
212
213   // reset the statistical package.
214   stats.total_runs = 0;
215   stats.send_time = 0;
216   stats.receive_time = 0;
217   stats.bytes_sent = 0;
218   stats.bytes_received = 0;
219
220   // check that they aren't trying to do too big of a send.
221   if (size > MAXIMUM_WINSOCK_MTU) {
222     LOG("The size is over our limit.  To fix this, edit the "
223         "send_data function.");
224     return false;
225   }
226
227   // check that our socket is usable.
228   if (!_socket) {
229     LOG("One cannot send data on an uninitialized tester!");
230     return false;
231   }
232
233   int runs_completed = 0;
234     // counts up how many times we've done our test cycle.
235
236   while (runs_completed < count) {
237 #ifdef DEBUG_SPOCKET_TESTER
238     LOG(a_sprintf("iter %d", runs_completed));
239 #endif
240     // we're doing the client side routine here.
241     time_stamp trip_start;
242 #ifdef DEBUG_SPOCKET_TESTER
243     LOG("client about to send");
244 #endif
245     if (!do_a_send(dest, garbage_buffer, size, stats)) {
246       LOG("We failed on a send.  Now quitting.");
247       return false;
248     }
249 #ifdef DEBUG_SPOCKET_TESTER
250     LOG("client about to rcv");
251 #endif
252     if (!do_a_receive(size, stats)) {
253       LOG("We failed on a receive.  Now quitting.");
254       return false;
255     }
256     stats.round_trip_time += int(time_stamp().value() - trip_start.value());
257
258     runs_completed++;  // finished a run.
259     stats.total_runs++;  // count it in the overall stats too.
260     if ( !(runs_completed % 10) )
261       LOG(a_sprintf("Completed test #%d.", runs_completed));
262   }
263
264   return true;
265 }
266