1 module during.tests.api;
2 
3 import during;
4 import during.tests.base;
5 
6 version (D_Exceptions) import std.exception : assertThrown;
7 import std.range;
8 
9 @("Submission variants")
10 unittest
11 {
12     Uring io;
13     auto res = io.setup();
14     assert(res >= 0, "Error initializing IO");
15 
16     SubmissionEntry entry;
17     entry.opcode = Operation.NOP;
18     entry.user_data = 1;
19 
20     struct MyOp
21     {
22         Operation opcode = Operation.NOP;
23         ulong user_data;
24     }
25 
26     // chain operations
27     io
28         .put(entry)
29         .put(MyOp(Operation.NOP, 2))
30         .putWith!((ref SubmissionEntry e) { e.prepNop(); e.user_data = 42; })
31         .submit(1); // submit operations and wait for at least 1 completed
32 
33     // check completions
34     assert(!io.empty);
35     assert(io.front.user_data == 1);
36     io.popFront();
37     io.wait(2);
38     assert(io.front.user_data == 2);
39     io.popFront();
40     assert(!io.empty);
41     assert(io.front.user_data == 42);
42     io.popFront();
43     assert(io.empty);
44 }
45 
46 @("Limits")
47 unittest
48 {
49     struct Nop { Operation opcode = Operation.NOP; }
50 
51     Uring io;
52     auto res = io.setup(16);
53     assert(res >= 0, "Error initializing IO");
54     assert(io.empty);
55     assert(!io.length);
56     assert(!io.full);
57     assert(io.capacity == 16);
58     assert(io.params.sq_entries == 16);
59     assert(io.params.cq_entries == 32);
60 
61     // fill submission queue
62     foreach (_; 0..16) io.put(Nop());
63     assert(io.capacity == 0);
64     assert(io.full);
65     assert(io.empty);
66     assert(!io.length);
67     assert(!io.overflow);
68     assert(!io.dropped);
69     version(D_Exceptions) assertThrown!Throwable(io.put(Nop()));
70 
71     io.submit(0); // submit them all
72     assert(io.capacity == 16);
73     assert(!io.full);
74     assert(!io.dropped);
75 
76     io.wait(10);
77     assert(io.capacity == 16);
78     assert(!io.full);
79     assert(!io.dropped);
80     assert(!io.empty);
81     assert(io.length == 16);
82     assert(!io.overflow);
83     io.popFront();
84     assert(io.length == 15);
85 
86     // fill up completion queue
87     foreach (_; 0..16) io.put(Nop());
88     io.submit(16); // submit them and wait for all
89     assert(io.length == 31);
90     assert(!io.overflow); // still no overflow
91 
92     // cause overflow
93     foreach (_; 0..2) io.put(Nop());
94     immutable sub = io.submit(2);
95     assert(io.length == 32);
96     if ((io.params.features & SetupFeatures.NODROP) == 0)
97     {
98         assert(io.overflow == 1);
99 
100         io.drop(32);
101         assert(io.empty);
102         assert(io.length == 0);
103     }
104     else
105     {
106         // Linux 5.5 has NODROP feature
107         assert(!io.overflow);
108         io.drop(32);
109         io.wait(1);
110         assert(!io.empty);
111         assert(io.length == 1);
112         io.drop(1);
113     }
114 
115     // put there another batch
116     foreach (_; 0..16) io.put(Nop());
117     io.submit(0);
118     io.wait(16);
119     assert(io.length == 16);
120     if ((io.params.features & SetupFeatures.NODROP) == 0) assert(io.overflow == 1);
121     else assert(!io.overflow);
122 }
123 
124 @("Range interface")
125 unittest
126 {
127     import std.algorithm : copy, equal, map;
128 
129     Uring io;
130     auto res = io.setup(16);
131     assert(res >= 0, "Error initializing IO");
132 
133     struct MyOp { Operation opcode; ulong user_data; }
134     static assert(isOutputRange!(Uring, MyOp));
135 
136     // add 32 entries (in 2 batches as we have capacity only for 16 entries)
137     iota(0, 16).map!(a => MyOp(Operation.NOP, a)).copy(io);
138     assert(io.capacity == 0);
139     assert(io.full);
140     res = io.submit(0); // just submit
141     assert(res == 16);
142     iota(16, 32).map!(a => MyOp(Operation.NOP, a)).copy(io);
143     res = io.submit(32); // submit and wait
144     assert(res == 16);
145     assert(!io.empty);
146     assert(io.length == 32);
147     assert(io.map!(c => c.user_data).equal(iota(0, 32)));
148 }
149 
150 @("sample")
151 unittest
152 {
153     import during;
154     import std.range : drop, iota;
155     import std.algorithm : copy, equal, map;
156 
157     Uring io;
158     auto res = io.setup();
159     assert(res >= 0, "Error initializing IO");
160 
161     SubmissionEntry entry;
162     entry.opcode = Operation.NOP;
163     entry.user_data = 1;
164 
165     // custom operation to allow usage customization
166     struct MyOp { Operation opcode = Operation.NOP; ulong user_data; }
167 
168     // chain operations
169     res = io
170         .put(entry) // whole entry as defined by io_uring
171         .put(MyOp(Operation.NOP, 2)) // custom op that would be filled over submission queue entry
172         .putWith!((ref SubmissionEntry e) // own function to directly fill entry in a queue
173             {
174                 e.prepNop();
175                 e.user_data = 42;
176             })
177         .submit(1); // submit operations and wait for at least 1 completed
178 
179     assert(res == 3); // 3 operations were submitted to the submission queue
180     assert(!io.empty); // at least one operation has been completed
181     assert(io.front.user_data == 1);
182     io.popFront(); // drop it from the completion queue
183 
184     // wait for and drop rest of the operations
185     io.wait(2);
186     io.drop(2);
187 
188     // use range API to post some operations
189     iota(0, 16).map!(a => MyOp(Operation.NOP, a)).copy(io);
190 
191     // submit them and wait for their completion
192     res = io.submit(16);
193     assert(res == 16);
194     assert(io.length == 16); // all operations has completed
195     assert(io.map!(c => c.user_data).equal(iota(0, 16)));
196 }
197 
198 // @("single mmap")
199 // unittest
200 // {
201 //     // 5.4
202 //     version (D_BetterC) errmsg = "Not implemented";
203 //     else throw new Exception("Not implemented");
204 // }
205 
206 // @("nodrop")
207 // unittest
208 // {
209 //     // 5.5
210 //     version (D_BetterC) errmsg = "Not implemented";
211 //     else throw new Exception("Not implemented");
212 // }
213 
214 // @("cqsize")
215 // unittest
216 // {
217 //     // 5.5
218 //     version (D_BetterC) errmsg = "Not implemented";
219 //     else throw new Exception("Not implemented");
220 // }