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 // }