Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(978)

Side by Side Diff: tests/standalone/io/raw_synchronous_socket_test.dart

Issue 2814773004: Various fixes for sync socket implementation. (Closed)
Patch Set: Various fixes for sync socket implementation. Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « sdk/lib/io/sync_socket.dart ('k') | tests/standalone/standalone.status » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file.
4
5 import "dart:async";
6 import "dart:io";
7 import "dart:isolate";
8 import "dart:math";
9
10 import "package:async_helper/async_helper.dart";
11 import "package:expect/expect.dart";
12
13 const String LOOPBACK_IP_V4_STRING = "127.0.0.1";
14
15 void testArguments() {
16 Expect.throws(() => RawSynchronousSocket.connectSync(null, 0));
17 Expect.throws(
18 () => RawSynchronousSocket.connectSync(LOOPBACK_IP_V4_STRING, null));
19 Expect.throws(
20 () => RawSynchronousSocket.connectSync(LOOPBACK_IP_V4_STRING, 65536));
21 Expect.throws(
22 () => RawSynchronousSocket.connectSync(LOOPBACK_IP_V4_STRING, -1));
23 Expect.throws(() =>
24 RawSynchronousSocket.connectSync(LOOPBACK_IP_V4_STRING, 0, backlog: -1));
25 }
26
27 // The connection attempt happens on the main Dart thread and the OS timeout can
28 // be arbitrarily long, causing timeout issues on the build bots. This isn't an
29 // issue with the async sockets since the lookup for a connect call happens on
30 // the IO service thread.
31 /*
32 void testInvalidConnect() {
33 // Connect to an unknown DNS name.
34 try {
35 var socket = RawSynchronousSocket.connectSync("ko.faar.__hest__", 0);
36 Expect.fail("Failure expected");
37 } catch (e) {
38 Expect.isTrue(e is SocketException);
39 }
40
41 // Connect to an unavaliable IP-address.
42 try {
43 var socket = RawSynchronousSocket.connectSync("1.2.3.4", 0);
44 Expect.fail("Failure expected");
45 } catch (e) {
46 Expect.isTrue(e is SocketException);
47 }
48 }
49 */
50
51 void testSimpleConnect() {
52 asyncStart();
53 RawServerSocket.bind(InternetAddress.LOOPBACK_IP_V4, 0).then((server) {
54 var socket =
55 RawSynchronousSocket.connectSync(LOOPBACK_IP_V4_STRING, server.port);
56 server.listen((serverSocket) {
57 Expect.equals(socket.address, serverSocket.remoteAddress);
58 Expect.equals(socket.port, serverSocket.remotePort);
59 Expect.equals(socket.remoteAddress, server.address);
60 Expect.equals(socket.remotePort, server.port);
61 socket.closeSync();
62 server.close();
63 asyncEnd();
64 });
65 });
66 }
67
68 void testServerListenAfterConnect() {
69 asyncStart();
70 RawServerSocket.bind(InternetAddress.LOOPBACK_IP_V4, 0).then((server) {
71 Expect.isTrue(server.port > 0);
72 var client =
73 RawSynchronousSocket.connectSync(LOOPBACK_IP_V4_STRING, server.port);
74 server.listen((socket) {
75 client.closeSync();
76 server.close();
77 socket.close();
78 asyncEnd();
79 });
80 });
81 }
82
83 const messageSize = 1000;
84 // Configuration fields for the EchoServer.
85 enum EchoServerTypes {
86 // Max accumulated connections to server before close. Defaults to 1.
87 CONNECTION_COUNT,
88 // Sets the range of the fields to check in the list generated by
89 // createTestData().
90 OFFSET_END,
91 OFFSET_START,
92 // The port used to communicate with an isolate.
93 ISOLATE_SEND_PORT,
94 // The port of the newly created echo server.
95 SERVER_PORT
96 }
97
98 List<int> createTestData() {
99 return new List<int>.generate(messageSize, (index) => index & 0xff);
100 }
101
102 // Consumes data generated by a test and compares it against the original test
103 // data. The optional fields, start and end, are used to compare against
104 // segments of the original test data list. In other words, data.length == (end
105 // - start).
106 void verifyTestData(List<int> data, [int start = 0, int end]) {
107 assert(data != null);
108 List<int> expected = createTestData();
109 if (end == null) {
110 end = data.length;
111 }
112 end = min(messageSize, end);
113 Expect.equals(end - start, data.length);
114 for (int i = 0; i < (end - start); i++) {
115 Expect.equals(expected[start + i], data[i]);
116 }
117 }
118
119 // The echo server is spawned in a new isolate and is used to test various
120 // synchronous read/write operations by echoing any data received back to the
121 // sender. The server should shutdown automatically after a specified number of
122 // socket disconnections (default: 1).
123 Future echoServer(var sendPort) async {
124 RawServerSocket.bind(InternetAddress.LOOPBACK_IP_V4, 0).then((server) async {
125 ReceivePort receivePort = new ReceivePort();
126 Map response = {
127 EchoServerTypes.ISOLATE_SEND_PORT: receivePort.sendPort,
128 EchoServerTypes.SERVER_PORT: server.port
129 };
130 sendPort.send(response);
131 Map limits = await receivePort.first;
132 int start = limits[EchoServerTypes.OFFSET_START];
133 int end = limits[EchoServerTypes.OFFSET_END];
134 int length = end - start;
135 int connection_count = limits[EchoServerTypes.CONNECTION_COUNT] ?? 1;
136 int connections = 0;
137 sendPort = limits[EchoServerTypes.ISOLATE_SEND_PORT];
138 server.listen((client) {
139 int bytesRead = 0;
140 int bytesWritten = 0;
141 bool closedEventReceived = false;
142 List<int> data = new List<int>(length);
143 client.writeEventsEnabled = false;
144 client.listen((event) {
145 switch (event) {
146 case RawSocketEvent.READ:
147 Expect.isTrue(bytesWritten == 0);
148 Expect.isTrue(client.available() > 0);
149 var buffer = client.read(client.available());
150 data.setRange(bytesRead, bytesRead + buffer.length, buffer);
151 bytesRead += buffer.length;
152 // Once we've read all the data, we can echo it back. Otherwise,
153 // keep waiting for more bytes.
154 if (bytesRead >= length) {
155 verifyTestData(data, start, end);
156 client.writeEventsEnabled = true;
157 }
158 break;
159 case RawSocketEvent.WRITE:
160 Expect.isFalse(client.writeEventsEnabled);
161 bytesWritten +=
162 client.write(data, bytesWritten, data.length - bytesWritten);
163 if (bytesWritten < length) {
164 client.writeEventsEnabled = true;
165 } else if (bytesWritten == length) {
166 // Close the socket for writing from the server since we're done
167 // writing to this socket. The connection is closed completely
168 // after the client closes the socket for reading from the server.
169 client.shutdown(SocketDirection.SEND);
170 }
171 break;
172 case RawSocketEvent.READ_CLOSED:
173 client.close();
174 break;
175 case RawSocketEvent.CLOSED:
176 Expect.isFalse(closedEventReceived);
177 closedEventReceived = true;
178 break;
179 default:
180 throw "Unexpected event $event";
181 }
182 }, onDone: () {
183 Expect.isTrue(closedEventReceived);
184 connections++;
185 if (connections >= connection_count) {
186 server.close();
187 }
188 });
189 }, onDone: () {
190 // Let the client know we're shutting down then kill the isolate.
191 sendPort.send(null);
192 kill();
193 });
194 });
195 }
196
197 Future testSimpleReadWrite({bool dropReads}) async {
198 asyncStart();
199 // This test creates a server and a client connects. The client writes data
200 // to the socket and the server echos it back. The client confirms the data it
201 // reads is the same as the data sent, then closes the socket, resulting in
202 // the closing of the server, which responds on receivePort with null to
203 // specify the echo server isolate is about to be killed. If an error occurs
204 // in the echo server, the exception and stack trace are sent to receivePort,
205 // which prints the exception and stack trace before eventually throwing an
206 // error.
207 ReceivePort receivePort = new ReceivePort();
208 Isolate echo = await Isolate.spawn(echoServer, receivePort.sendPort);
209
210 Map response = await receivePort.first;
211 SendPort sendPort = response[EchoServerTypes.ISOLATE_SEND_PORT];
212 int serverInternetPort = response[EchoServerTypes.SERVER_PORT];
213
214 receivePort = new ReceivePort();
215 echo.addErrorListener(receivePort.sendPort);
216
217 Map limits = {
218 EchoServerTypes.OFFSET_START: 0,
219 EchoServerTypes.OFFSET_END: messageSize,
220 EchoServerTypes.ISOLATE_SEND_PORT: receivePort.sendPort
221 };
222 sendPort.send(limits);
223
224 try {
225 var socket = RawSynchronousSocket.connectSync(
226 LOOPBACK_IP_V4_STRING, serverInternetPort);
227 List<int> data = createTestData();
228 socket.writeFromSync(data);
229 List<int> result = socket.readSync(data.length);
230 verifyTestData(result);
231 socket.shutdown(SocketDirection.SEND);
232 socket.closeSync();
233 } catch (e, stack) {
234 print("Echo test failed in the client");
235 rethrow;
236 }
237 // Wait for the server to shutdown before finishing the test.
238 var result = await receivePort.first;
239 if (result != null) {
240 throw "Echo test failed in server!\nError: ${result[0]}\nStack trace:" +
241 " ${result[1]}";
242 }
243 asyncEnd();
244 }
245
246 Future testPartialRead() async {
247 asyncStart();
248 // This test is based on testSimpleReadWrite, but instead of reading the
249 // entire echoed message at once, it reads it in two calls to readIntoSync.
250 ReceivePort receivePort = new ReceivePort();
251 Isolate echo = await Isolate.spawn(echoServer, receivePort.sendPort);
252
253 Map response = await receivePort.first;
254 SendPort sendPort = response[EchoServerTypes.ISOLATE_SEND_PORT];
255 int serverInternetPort = response[EchoServerTypes.SERVER_PORT];
256 List<int> data = createTestData();
257
258 receivePort = new ReceivePort();
259 echo.addErrorListener(receivePort.sendPort);
260
261 Map limits = {
262 EchoServerTypes.OFFSET_START: 0,
263 EchoServerTypes.OFFSET_END: 1000,
264 EchoServerTypes.ISOLATE_SEND_PORT: receivePort.sendPort
265 };
266 sendPort.send(limits);
267
268 try {
269 var socket = RawSynchronousSocket.connectSync(
270 LOOPBACK_IP_V4_STRING, serverInternetPort);
271 int half_length = (data.length / 2).toInt();
272
273 // Send the full data list to the server.
274 socket.writeFromSync(data);
275 List<int> result = new List<int>(data.length);
276
277 // Read half at a time and check that there's still more bytes available.
278 socket.readIntoSync(result, 0, half_length);
279 verifyTestData(result.sublist(0, half_length), 0, half_length);
280 Expect.isTrue(socket.available() == (data.length - half_length));
281
282 // Read the second half and verify again.
283 socket.readIntoSync(result, half_length);
284 verifyTestData(result);
285 Expect.isTrue(socket.available() == 0);
286
287 socket.closeSync();
288 } catch (e, stack) {
289 print("Echo test failed in the client.");
290 rethrow;
291 }
292 // Wait for the server to shutdown before finishing the test.
293 var result = await receivePort.first;
294 if (result != null) {
295 throw "Echo test failed in server!\nError: ${result[0]}\nStack trace:" +
296 " ${result[1]}";
297 }
298 asyncEnd();
299 }
300
301 Future testPartialWrite() async {
302 asyncStart();
303 // This test is based on testSimpleReadWrite, but instead of writing the
304 // entire data buffer at once, it writes different parts of the buffer over
305 // multiple calls to writeFromSync.
306 ReceivePort receivePort = new ReceivePort();
307 Isolate echo = await Isolate.spawn(echoServer, receivePort.sendPort);
308
309 Map response = await receivePort.first;
310 List<int> data = createTestData();
311 SendPort sendPort = response[EchoServerTypes.ISOLATE_SEND_PORT];
312 int startOffset = 32;
313 int endOffset = (data.length / 2).toInt();
314 int serverInternetPort = response[EchoServerTypes.SERVER_PORT];
315
316 receivePort = new ReceivePort();
317 echo.addErrorListener(receivePort.sendPort);
318
319 Map limits = {
320 EchoServerTypes.OFFSET_START: startOffset,
321 EchoServerTypes.OFFSET_END: endOffset,
322 EchoServerTypes.ISOLATE_SEND_PORT: receivePort.sendPort
323 };
324 sendPort.send(limits);
325 try {
326 var socket = RawSynchronousSocket.connectSync(
327 LOOPBACK_IP_V4_STRING, serverInternetPort);
328 List<int> data = createTestData();
329
330 // Write a subset of data to the server.
331 socket.writeFromSync(data, startOffset, endOffset);
332
333 // Grab the response and verify it's correct.
334 List<int> result = new List<int>(endOffset - startOffset);
335 socket.readIntoSync(result);
336
337 Expect.equals(result.length, endOffset - startOffset);
338 verifyTestData(result, startOffset, endOffset);
339 socket.closeSync();
340 } catch (e, stack) {
341 print("Echo test failed in the client.");
342 rethrow;
343 }
344
345 // Wait for the server to shutdown before finishing the test.
346 var result = await receivePort.first;
347 if (result != null) {
348 throw "Echo test failed in server!\nError: ${result[0]}\nStack trace:" +
349 " ${result[1]}";
350 }
351 asyncEnd();
352 }
353
354 Future testShutdown() async {
355 asyncStart();
356 // This test creates a server and a client connects. The client then tries to
357 // perform various operations after being shutdown in a specific direction, to
358 // ensure reads or writes cannot be performed if the socket has been shutdown
359 // for reading or writing.
360 ReceivePort receivePort = new ReceivePort();
361 Isolate echo = await Isolate.spawn(echoServer, receivePort.sendPort);
362
363 Map response = await receivePort.first;
364 SendPort sendPort = response[EchoServerTypes.ISOLATE_SEND_PORT];
365 int serverInternetPort = response[EchoServerTypes.SERVER_PORT];
366 List<int> data = createTestData();
367
368 receivePort = new ReceivePort();
369 echo.addErrorListener(receivePort.sendPort);
370
371 Map limits = {
372 EchoServerTypes.OFFSET_START: 0,
373 EchoServerTypes.OFFSET_END: data.length,
374 EchoServerTypes.ISOLATE_SEND_PORT: receivePort.sendPort,
375 // Tell the server to shutdown after 3 sockets disconnect.
376 EchoServerTypes.CONNECTION_COUNT: 3
377 };
378 sendPort.send(limits);
379
380 try {
381 var socket = RawSynchronousSocket.connectSync(
382 LOOPBACK_IP_V4_STRING, serverInternetPort);
383
384 // Close from both directions. Shouldn't be able to read/write to the
385 // socket.
386 socket.shutdown(SocketDirection.BOTH);
387 Expect.throws(
388 () => socket.writeFromSync(data), (e) => e is SocketException);
389 Expect.throws(
390 () => socket.readSync(data.length), (e) => e is SocketException);
391 socket.closeSync();
392
393 // Close the socket for reading, do a write, and see if we can get any
394 // response from the server (we shouldn't be able to).
395 socket = RawSynchronousSocket.connectSync(
396 LOOPBACK_IP_V4_STRING, serverInternetPort);
397 socket.shutdown(SocketDirection.RECEIVE);
398 socket.writeFromSync(data);
399 // Throws exception when the socket is closed for RECEIVE.
400 Expect.throws(
401 () => socket.readSync(data.length), (e) => e is SocketException);
402 Expect.isTrue(socket.available() == 0);
403 socket.closeSync();
404
405 // Close the socket for writing and try to do a write. This should cause an
406 // OSError to be throw as the pipe is closed for writing.
407 socket = RawSynchronousSocket.connectSync(
408 LOOPBACK_IP_V4_STRING, serverInternetPort);
409 socket.shutdown(SocketDirection.SEND);
410 Expect.throws(
411 () => socket.writeFromSync(data), (e) => e is SocketException);
412 socket.closeSync();
413 } catch (e, stack) {
414 print("Echo test failed in client.");
415 rethrow;
416 }
417 // Wait for the server to shutdown before finishing the test.
418 var result = await receivePort.first;
419 if (result != null) {
420 throw "Echo test failed in server!\nError: ${result[0]}\nStack trace:" +
421 " ${result[1]}";
422 }
423 asyncEnd();
424 }
425
426 Future testInvalidReadWriteOperations() {
427 asyncStart();
428 RawServerSocket.bind(InternetAddress.LOOPBACK_IP_V4, 0).then((server) {
429 server.listen((socket) {});
430 List<int> data = createTestData();
431 var socket =
432 RawSynchronousSocket.connectSync(LOOPBACK_IP_V4_STRING, server.port);
433
434 // Invalid writeFromSync invocations
435 Expect.throws(() => socket.writeFromSync(data, data.length + 1),
436 (e) => e is RangeError);
437 Expect.throws(() => socket.writeFromSync(data, 0, data.length + 1),
438 (e) => e is RangeError);
439 Expect.throws(
440 () => socket.writeFromSync(data, 1, 0), (e) => e is RangeError);
441 Expect.throws(
442 () => socket.writeFromSync(data, null), (e) => e is ArgumentError);
443
444 // Invalid readIntoSync invocations
445 List<int> buffer = new List<int>(10);
446 Expect.throws(() => socket.readIntoSync(buffer, buffer.length + 1),
447 (e) => e is RangeError);
448 Expect.throws(() => socket.readIntoSync(buffer, 0, buffer.length + 1),
449 (e) => e is RangeError);
450 Expect.throws(
451 () => socket.readIntoSync(buffer, 1, 0), (e) => e is RangeError);
452 Expect.throws(
453 () => socket.readIntoSync(buffer, null), (e) => e is ArgumentError);
454
455 // Invalid readSync invocation
456 Expect.throws(() => socket.readSync(-1), (e) => e is ArgumentError);
457
458 server.close();
459 socket.closeSync();
460 asyncEnd();
461 });
462 }
463
464 void testClosedError() {
465 asyncStart();
466 RawServerSocket.bind(InternetAddress.LOOPBACK_IP_V4, 0).then((server) {
467 server.listen((socket) {
468 socket.close();
469 });
470 var socket =
471 RawSynchronousSocket.connectSync(LOOPBACK_IP_V4_STRING, server.port);
472 server.close();
473 socket.closeSync();
474 Expect.throws(() => socket.remotePort, (e) => e is SocketException);
475 Expect.throws(() => socket.remoteAddress, (e) => e is SocketException);
476 asyncEnd();
477 });
478 }
479
480 main() async {
481 asyncStart();
482 testArguments();
483 // testInvalidConnect(); Long timeout for bad lookups, so disable for bots.
484 await testShutdown();
485 testSimpleConnect();
486 testServerListenAfterConnect();
487 await testSimpleReadWrite();
488 await testPartialRead();
489 await testPartialWrite();
490 testInvalidReadWriteOperations();
491 testClosedError();
492 asyncEnd();
493 }
OLDNEW
« no previous file with comments | « sdk/lib/io/sync_socket.dart ('k') | tests/standalone/standalone.status » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698