1 /*******************************************************************************
2 
3     Copyright:
4         Copyright (c) 2017 dunnhumby Germany GmbH.
5         All rights reserved.
6 
7     License:
8         Boost Software License Version 1.0. See LICENSE_BOOST.txt for details.
9         Alternatively, this file may be distributed under the terms of the Tango
10         3-Clause BSD License (see LICENSE_BSD.txt for details).
11 
12 *******************************************************************************/
13 
14 module ocean.io.select.protocol.task.TaskSelectTransceiver_test;
15 
16 import ocean.io.select.protocol.task.TaskSelectTransceiver;
17 
18 import ocean.meta.types.Qualifiers;
19 import ocean.stdc.posix.fcntl: O_NONBLOCK;
20 import core.sys.posix.unistd: write, close;
21 import ocean.sys.ErrnoException;
22 import ocean.io.device.IODevice;
23 import ocean.io.select.protocol.generic.ErrnoIOException;
24 import ocean.io.select.client.model.ISelectClient;
25 import ocean.task.Scheduler;
26 import ocean.task.Task;
27 import ocean.core.Test;
28 
29 extern (C) private int pipe2(ref int[2] fd, int flags);
30 
31 unittest
32 {
33     // This test uses a pipe as an I/O device.
34     int[2] pipefd;
35 
36     if (pipe2(pipefd, O_NONBLOCK))
37         throw (new ErrnoException).useGlobalErrno("pipe2");
38 
39     // This TaskSelectTransceiver subclass uses a tiny 3-bytes buffer to test
40     // the reading loop.
41     static class TestTaskSelectTransceiver: TaskSelectTransceiver
42     {
43         this ( IODevice iodev )
44         {
45             super(iodev, new IOWarning(iodev), new IOError(iodev), 3);
46         }
47 
48         ~this ( ) { .close(this.iodev.fileHandle); }
49     }
50 
51     // Create an input-only task select transceiver for the reading end of the
52     // pipe.
53     scope intst = new TestTaskSelectTransceiver(new class IODevice
54     {
55         Handle fileHandle ( ) { return cast(Handle)pipefd[0]; }
56         override ssize_t write ( const(void)[] src ) { assert(false); }
57     });
58 
59     // Create an output-only task select transceiver for the writing end of the
60     // pipe.
61     scope outtst = new TestTaskSelectTransceiver(new class IODevice
62     {
63         Handle fileHandle ( ) { return cast(Handle)pipefd[1]; }
64         override ssize_t read ( void[] dst ) { assert(false); }
65         override ssize_t write ( const(void)[] src )
66         {
67             return .write(pipefd[1], src.ptr, src.length);
68         }
69     });
70 
71     initScheduler(SchedulerConfiguration.init);
72 
73     static immutable outstr = "Hello World!";
74     char[outstr.length] instr;
75 
76     // Start a task that writes the test string to the pipe.
77     theScheduler.schedule(new class Task
78     {
79         override void run     ( ) { outtst.write(outstr); }
80         override void recycle ( ) { outtst.select_client.unregister(); }
81     });
82 
83     // Start a task that reads the test string from the pipe.
84     theScheduler.schedule(new class Task
85     {
86         override void run ( )
87         {
88             // Read only "Hello " to test readv().
89             static immutable hello = "Hello ".length;
90             intst.read(instr[0 .. hello]);
91             auto world = instr[hello .. $];
92 
93             // Read "World!". The input buffer size makes only 3 characters
94             // arrive at once.
95             intst.readConsume(
96                 (void[] data)
97                 {
98                     world[0 .. data.length] = cast(char[])data;
99                     world = world[data.length .. $];
100                     return data.length + !!world.length;
101                 }
102             );
103         }
104 
105         override void recycle ( ) { intst.select_client.unregister(); }
106     });
107 
108     theScheduler.eventLoop();
109 
110     test!("==")(instr, outstr);
111 }