io-test.c++ (6873B)
1 // Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors 2 // Licensed under the MIT License: 3 // 4 // Permission is hereby granted, free of charge, to any person obtaining a copy 5 // of this software and associated documentation files (the "Software"), to deal 6 // in the Software without restriction, including without limitation the rights 7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 // copies of the Software, and to permit persons to whom the Software is 9 // furnished to do so, subject to the following conditions: 10 // 11 // The above copyright notice and this permission notice shall be included in 12 // all copies or substantial portions of the Software. 13 // 14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 // THE SOFTWARE. 21 22 #ifndef _GNU_SOURCE 23 #define _GNU_SOURCE 24 #endif 25 26 #include "io.h" 27 #include "debug.h" 28 #include "miniposix.h" 29 #include <kj/compat/gtest.h> 30 31 namespace kj { 32 namespace { 33 34 TEST(Io, WriteVec) { 35 // Check that writing an array of arrays works even when some of the arrays are empty. (This 36 // used to not work in some cases.) 37 38 int fds[2]; 39 KJ_SYSCALL(miniposix::pipe(fds)); 40 41 FdInputStream in((AutoCloseFd(fds[0]))); 42 FdOutputStream out((AutoCloseFd(fds[1]))); 43 44 ArrayPtr<const byte> pieces[5] = { 45 arrayPtr(implicitCast<const byte*>(nullptr), 0), 46 arrayPtr(reinterpret_cast<const byte*>("foo"), 3), 47 arrayPtr(implicitCast<const byte*>(nullptr), 0), 48 arrayPtr(reinterpret_cast<const byte*>("bar"), 3), 49 arrayPtr(implicitCast<const byte*>(nullptr), 0) 50 }; 51 52 out.write(pieces); 53 54 char buf[7]; 55 in.read(buf, 6); 56 buf[6] = '\0'; 57 58 EXPECT_STREQ("foobar", buf); 59 } 60 61 KJ_TEST("stringify AutoCloseFd") { 62 int fds[2]; 63 KJ_SYSCALL(miniposix::pipe(fds)); 64 AutoCloseFd in(fds[0]), out(fds[1]); 65 66 KJ_EXPECT(kj::str(in) == kj::str(fds[0]), in, fds[0]); 67 } 68 69 KJ_TEST("VectorOutputStream") { 70 VectorOutputStream output(16); 71 auto buf = output.getWriteBuffer(); 72 KJ_ASSERT(buf.size() == 16); 73 74 for (auto i: kj::indices(buf)) { 75 buf[i] = 'a' + i; 76 } 77 78 output.write(buf.begin(), 4); 79 KJ_ASSERT(output.getArray().begin() == buf.begin()); 80 KJ_ASSERT(output.getArray().size() == 4); 81 82 auto buf2 = output.getWriteBuffer(); 83 KJ_ASSERT(buf2.end() == buf.end()); 84 KJ_ASSERT(buf2.size() == 12); 85 86 output.write(buf2.begin(), buf2.size()); 87 KJ_ASSERT(output.getArray().begin() == buf.begin()); 88 KJ_ASSERT(output.getArray().size() == 16); 89 90 auto buf3 = output.getWriteBuffer(); 91 KJ_ASSERT(buf3.size() == 16); 92 KJ_ASSERT(output.getArray().begin() != buf.begin()); 93 KJ_ASSERT(output.getArray().end() == buf3.begin()); 94 KJ_ASSERT(kj::str(output.getArray().asChars()) == "abcdefghijklmnop"); 95 96 byte junk[24]; 97 for (auto i: kj::indices(junk)) { 98 junk[i] = 'A' + i; 99 } 100 101 output.write(junk, 4); 102 KJ_ASSERT(output.getArray().begin() != buf.begin()); 103 KJ_ASSERT(output.getArray().end() == buf3.begin() + 4); 104 KJ_ASSERT(kj::str(output.getArray().asChars()) == "abcdefghijklmnopABCD"); 105 106 output.write(junk + 4, 20); 107 // (We can't assert output.getArray().begin() != buf.begin() because the memory allocator could 108 // legitimately have allocated a new array in the same space.) 109 KJ_ASSERT(output.getArray().end() != buf3.begin() + 24); 110 KJ_ASSERT(kj::str(output.getArray().asChars()) == "abcdefghijklmnopABCDEFGHIJKLMNOPQRSTUVWX"); 111 112 KJ_ASSERT(output.getWriteBuffer().size() == 24); 113 KJ_ASSERT(output.getWriteBuffer().begin() == output.getArray().begin() + 40); 114 } 115 116 class MockInputStream: public InputStream { 117 public: 118 MockInputStream(kj::ArrayPtr<const byte> bytes, size_t blockSize) 119 : bytes(bytes), blockSize(blockSize) {} 120 121 size_t tryRead(void* buffer, size_t minBytes, size_t maxBytes) override { 122 // Clamp max read to blockSize. 123 size_t n = kj::min(blockSize, maxBytes); 124 125 // Unless that's less than minBytes -- in which case, use minBytes. 126 n = kj::max(n, minBytes); 127 128 // But also don't read more data than we have. 129 n = kj::min(n, bytes.size()); 130 131 memcpy(buffer, bytes.begin(), n); 132 bytes = bytes.slice(n, bytes.size()); 133 return n; 134 } 135 136 private: 137 kj::ArrayPtr<const byte> bytes; 138 size_t blockSize; 139 }; 140 141 KJ_TEST("InputStream::readAllText() / readAllBytes()") { 142 auto bigText = strArray(kj::repeat("foo bar baz"_kj, 12345), ","); 143 size_t inputSizes[] = { 0, 1, 256, 4096, 8191, 8192, 8193, 10000, bigText.size() }; 144 size_t blockSizes[] = { 1, 4, 256, 4096, 8192, bigText.size() }; 145 uint64_t limits[] = { 146 0, 1, 256, 147 bigText.size() / 2, 148 bigText.size() - 1, 149 bigText.size(), 150 bigText.size() + 1, 151 kj::maxValue 152 }; 153 154 for (size_t inputSize: inputSizes) { 155 for (size_t blockSize: blockSizes) { 156 for (uint64_t limit: limits) { 157 KJ_CONTEXT(inputSize, blockSize, limit); 158 auto textSlice = bigText.asBytes().slice(0, inputSize); 159 auto readAllText = [&]() { 160 MockInputStream input(textSlice, blockSize); 161 return input.readAllText(limit); 162 }; 163 auto readAllBytes = [&]() { 164 MockInputStream input(textSlice, blockSize); 165 return input.readAllBytes(limit); 166 }; 167 if (limit > inputSize) { 168 KJ_EXPECT(readAllText().asBytes() == textSlice); 169 KJ_EXPECT(readAllBytes() == textSlice); 170 } else { 171 KJ_EXPECT_THROW_MESSAGE("Reached limit before EOF.", readAllText()); 172 KJ_EXPECT_THROW_MESSAGE("Reached limit before EOF.", readAllBytes()); 173 } 174 } 175 } 176 } 177 } 178 179 KJ_TEST("ArrayOutputStream::write() does not assume adjacent write buffer is its own") { 180 // Previously, if ArrayOutputStream::write(src, size) saw that `src` equaled its fill position, it 181 // would assume that the write was already in its buffer. This assumption was buggy if the write 182 // buffer was directly adjacent in memory to the ArrayOutputStream's buffer, and the 183 // ArrayOutputStream was full (i.e., its fill position was one-past-the-end). 184 // 185 // VectorOutputStream also suffered a similar bug, but it is much harder to test, since it 186 // performs its own allocation. 187 188 kj::byte buffer[10] = { 0 }; 189 190 ArrayOutputStream output(arrayPtr(buffer, buffer + 5)); 191 192 // Succeeds and fills the ArrayOutputStream. 193 output.write(buffer + 5, 5); 194 195 // Previously this threw an inscrutable "size <= array.end() - fillPos" requirement failure. 196 KJ_EXPECT_THROW_MESSAGE( 197 "backing array was not large enough for the data written", 198 output.write(buffer + 5, 5)); 199 } 200 201 } // namespace 202 } // namespace kj