1 // Copyright (C) 2017 The Android Open Source Project 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 #define DEBUG false 15 #include "Log.h" 16 17 #include "FdBuffer.h" 18 #include "incidentd_util.h" 19 20 #include <fcntl.h> 21 #include <signal.h> 22 #include <string.h> 23 24 #include <android-base/file.h> 25 #include <gtest/gtest.h> 26 27 using namespace android; 28 using namespace android::base; 29 using namespace android::os::incidentd; 30 using ::testing::Test; 31 32 const int READ_TIMEOUT = 5 * 1000; 33 const int BUFFER_SIZE = 16 * 1024; 34 const int QUICK_TIMEOUT_MS = 100; 35 const std::string HEAD = "[OK]"; 36 37 class FdBufferTest : public Test { 38 public: 39 virtual void SetUp() override { 40 ASSERT_NE(tf.fd, -1); 41 ASSERT_NE(p2cPipe.init(), -1); 42 ASSERT_NE(c2pPipe.init(), -1); 43 } 44 45 void AssertBufferReadSuccessful(size_t expected) { 46 EXPECT_EQ(buffer.size(), expected); 47 EXPECT_FALSE(buffer.timedOut()); 48 EXPECT_FALSE(buffer.truncated()); 49 } 50 51 void AssertBufferContent(const char* expected) { 52 int i = 0; 53 sp<ProtoReader> reader = buffer.data()->read(); 54 while (reader->hasNext()) { 55 ASSERT_EQ(reader->next(), expected[i++]); 56 } 57 EXPECT_EQ(expected[i], '\0'); 58 } 59 60 bool DoDataStream(const unique_fd& rFd, const unique_fd& wFd) { 61 char buf[BUFFER_SIZE]; 62 ssize_t nRead; 63 while ((nRead = read(rFd.get(), buf, BUFFER_SIZE)) > 0) { 64 ssize_t nWritten = 0; 65 while (nWritten < nRead) { 66 ssize_t amt = write(wFd.get(), buf + nWritten, nRead - nWritten); 67 if (amt < 0) { 68 return false; 69 } 70 nWritten += amt; 71 } 72 } 73 return nRead == 0; 74 } 75 76 protected: 77 FdBuffer buffer; 78 TemporaryFile tf; 79 Fpipe p2cPipe; 80 Fpipe c2pPipe; 81 82 const std::string kTestPath = GetExecutableDirectory(); 83 const std::string kTestDataPath = kTestPath + "/testdata/"; 84 }; 85 86 TEST_F(FdBufferTest, ReadAndWrite) { 87 std::string testdata = "FdBuffer test string"; 88 ASSERT_TRUE(WriteStringToFile(testdata, tf.path)); 89 ASSERT_EQ(NO_ERROR, buffer.read(tf.fd, READ_TIMEOUT)); 90 AssertBufferReadSuccessful(testdata.size()); 91 AssertBufferContent(testdata.c_str()); 92 } 93 94 TEST_F(FdBufferTest, IterateEmpty) { 95 sp<ProtoReader> reader = buffer.data()->read(); 96 EXPECT_FALSE(reader->hasNext()); 97 } 98 99 TEST_F(FdBufferTest, ReadAndIterate) { 100 std::string testdata = "FdBuffer test string"; 101 ASSERT_TRUE(WriteStringToFile(testdata, tf.path)); 102 ASSERT_EQ(NO_ERROR, buffer.read(tf.fd, READ_TIMEOUT)); 103 104 int i = 0; 105 sp<ProtoReader> reader = buffer.data()->read(); 106 107 while (reader->hasNext()) { 108 EXPECT_EQ(reader->next(), (uint8_t)testdata[i++]); 109 } 110 } 111 112 TEST_F(FdBufferTest, Move) { 113 std::string testdata = "FdBuffer test string"; 114 ASSERT_TRUE(WriteStringToFile(testdata, tf.path)); 115 ASSERT_EQ(NO_ERROR, buffer.read(tf.fd, READ_TIMEOUT)); 116 117 sp<ProtoReader> reader = buffer.data()->read(); 118 reader->move(buffer.size()); 119 120 EXPECT_EQ(reader->bytesRead(), testdata.size()); 121 EXPECT_FALSE(reader->hasNext()); 122 } 123 124 TEST_F(FdBufferTest, ReadTimeout) { 125 int pid = fork(); 126 ASSERT_TRUE(pid != -1); 127 128 if (pid == 0) { 129 c2pPipe.readFd().reset(); 130 while (true) { 131 write(c2pPipe.writeFd(), "poo", 3); 132 sleep(1); 133 } 134 _exit(EXIT_FAILURE); 135 } else { 136 c2pPipe.writeFd().reset(); 137 138 status_t status = buffer.read(c2pPipe.readFd().get(), QUICK_TIMEOUT_MS); 139 ASSERT_EQ(NO_ERROR, status); 140 EXPECT_TRUE(buffer.timedOut()); 141 142 kill(pid, SIGKILL); // reap the child process 143 } 144 } 145 146 TEST_F(FdBufferTest, ReadInStreamAndWrite) { 147 std::string testdata = "simply test read in stream"; 148 std::string expected = HEAD + testdata; 149 ASSERT_TRUE(WriteStringToFile(testdata, tf.path)); 150 151 int pid = fork(); 152 ASSERT_TRUE(pid != -1); 153 154 if (pid == 0) { 155 p2cPipe.writeFd().reset(); 156 c2pPipe.readFd().reset(); 157 ASSERT_TRUE(WriteStringToFd(HEAD, c2pPipe.writeFd())); 158 ASSERT_TRUE(DoDataStream(p2cPipe.readFd(), c2pPipe.writeFd())); 159 p2cPipe.readFd().reset(); 160 c2pPipe.writeFd().reset(); 161 // Must exit here otherwise the child process will continue executing the test binary. 162 _exit(EXIT_SUCCESS); 163 } else { 164 p2cPipe.readFd().reset(); 165 c2pPipe.writeFd().reset(); 166 167 ASSERT_EQ(NO_ERROR, 168 buffer.readProcessedDataInStream(tf.fd, std::move(p2cPipe.writeFd()), 169 std::move(c2pPipe.readFd()), READ_TIMEOUT)); 170 AssertBufferReadSuccessful(HEAD.size() + testdata.size()); 171 AssertBufferContent(expected.c_str()); 172 wait(&pid); 173 } 174 } 175 176 TEST_F(FdBufferTest, ReadInStreamAndWriteAllAtOnce) { 177 std::string testdata = "child process flushes only after all data are read."; 178 std::string expected = HEAD + testdata; 179 ASSERT_TRUE(WriteStringToFile(testdata, tf.path)); 180 181 int pid = fork(); 182 ASSERT_TRUE(pid != -1); 183 184 if (pid == 0) { 185 p2cPipe.writeFd().reset(); 186 c2pPipe.readFd().reset(); 187 std::string data; 188 // wait for read finishes then write. 189 ASSERT_TRUE(ReadFdToString(p2cPipe.readFd(), &data)); 190 data = HEAD + data; 191 ASSERT_TRUE(WriteStringToFd(data, c2pPipe.writeFd())); 192 p2cPipe.readFd().reset(); 193 c2pPipe.writeFd().reset(); 194 // Must exit here otherwise the child process will continue executing the test binary. 195 _exit(EXIT_SUCCESS); 196 } else { 197 p2cPipe.readFd().reset(); 198 c2pPipe.writeFd().reset(); 199 200 ASSERT_EQ(NO_ERROR, 201 buffer.readProcessedDataInStream(tf.fd, std::move(p2cPipe.writeFd()), 202 std::move(c2pPipe.readFd()), READ_TIMEOUT)); 203 AssertBufferReadSuccessful(HEAD.size() + testdata.size()); 204 AssertBufferContent(expected.c_str()); 205 wait(&pid); 206 } 207 } 208 209 TEST_F(FdBufferTest, ReadInStreamEmpty) { 210 ASSERT_TRUE(WriteStringToFile("", tf.path)); 211 212 int pid = fork(); 213 ASSERT_TRUE(pid != -1); 214 215 if (pid == 0) { 216 p2cPipe.writeFd().reset(); 217 c2pPipe.readFd().reset(); 218 ASSERT_TRUE(DoDataStream(p2cPipe.readFd(), c2pPipe.writeFd())); 219 p2cPipe.readFd().reset(); 220 c2pPipe.writeFd().reset(); 221 _exit(EXIT_SUCCESS); 222 } else { 223 p2cPipe.readFd().reset(); 224 c2pPipe.writeFd().reset(); 225 226 ASSERT_EQ(NO_ERROR, 227 buffer.readProcessedDataInStream(tf.fd, std::move(p2cPipe.writeFd()), 228 std::move(c2pPipe.readFd()), READ_TIMEOUT)); 229 AssertBufferReadSuccessful(0); 230 AssertBufferContent(""); 231 wait(&pid); 232 } 233 } 234 235 TEST_F(FdBufferTest, ReadInStreamMoreThan4MBWithMove) { 236 const std::string testFile = kTestDataPath + "morethan96MB.txt"; 237 size_t ninetySixMB = (size_t)96 * 1024 * 1024; 238 unique_fd fd(open(testFile.c_str(), O_RDONLY | O_CLOEXEC)); 239 ASSERT_NE(fd.get(), -1); 240 int pid = fork(); 241 ASSERT_TRUE(pid != -1); 242 243 if (pid == 0) { 244 p2cPipe.writeFd().reset(); 245 c2pPipe.readFd().reset(); 246 ASSERT_TRUE(DoDataStream(p2cPipe.readFd(), c2pPipe.writeFd())); 247 p2cPipe.readFd().reset(); 248 c2pPipe.writeFd().reset(); 249 _exit(EXIT_SUCCESS); 250 } else { 251 p2cPipe.readFd().reset(); 252 c2pPipe.writeFd().reset(); 253 254 ASSERT_EQ(NO_ERROR, 255 buffer.readProcessedDataInStream(fd, std::move(p2cPipe.writeFd()), 256 std::move(c2pPipe.readFd()), READ_TIMEOUT)); 257 EXPECT_EQ(buffer.size(), ninetySixMB); 258 EXPECT_FALSE(buffer.timedOut()); 259 EXPECT_TRUE(buffer.truncated()); 260 wait(&pid); 261 sp<ProtoReader> reader = buffer.data()->read(); 262 reader->move(ninetySixMB); 263 264 EXPECT_EQ(reader->bytesRead(), ninetySixMB); 265 EXPECT_FALSE(reader->hasNext()); 266 } 267 } 268 269 TEST_F(FdBufferTest, ReadInStreamMoreThan4MBWithNext) { 270 const std::string testFile = kTestDataPath + "morethan96MB.txt"; 271 size_t ninetySixMB = (size_t)96 * 1024 * 1024; 272 unique_fd fd(open(testFile.c_str(), O_RDONLY | O_CLOEXEC)); 273 ASSERT_NE(fd.get(), -1); 274 int pid = fork(); 275 ASSERT_TRUE(pid != -1); 276 277 if (pid == 0) { 278 p2cPipe.writeFd().reset(); 279 c2pPipe.readFd().reset(); 280 ASSERT_TRUE(DoDataStream(p2cPipe.readFd(), c2pPipe.writeFd())); 281 p2cPipe.readFd().reset(); 282 c2pPipe.writeFd().reset(); 283 _exit(EXIT_SUCCESS); 284 } else { 285 p2cPipe.readFd().reset(); 286 c2pPipe.writeFd().reset(); 287 288 ASSERT_EQ(NO_ERROR, 289 buffer.readProcessedDataInStream(fd, std::move(p2cPipe.writeFd()), 290 std::move(c2pPipe.readFd()), READ_TIMEOUT)); 291 EXPECT_EQ(buffer.size(), ninetySixMB); 292 EXPECT_FALSE(buffer.timedOut()); 293 EXPECT_TRUE(buffer.truncated()); 294 wait(&pid); 295 sp<ProtoReader> reader = buffer.data()->read(); 296 297 while (reader->hasNext()) { 298 char c = 'A' + (reader->bytesRead() % 64 / 8); 299 ASSERT_TRUE(reader->next() == c); 300 } 301 } 302 } 303 304 TEST_F(FdBufferTest, ReadInStreamTimeOut) { 305 std::string testdata = "timeout test"; 306 ASSERT_TRUE(WriteStringToFile(testdata, tf.path)); 307 308 int pid = fork(); 309 ASSERT_TRUE(pid != -1); 310 311 if (pid == 0) { 312 p2cPipe.writeFd().reset(); 313 c2pPipe.readFd().reset(); 314 while (true) { 315 sleep(1); 316 } 317 _exit(EXIT_FAILURE); 318 } else { 319 p2cPipe.readFd().reset(); 320 c2pPipe.writeFd().reset(); 321 322 ASSERT_EQ(NO_ERROR, 323 buffer.readProcessedDataInStream(tf.fd, std::move(p2cPipe.writeFd()), 324 std::move(c2pPipe.readFd()), QUICK_TIMEOUT_MS)); 325 EXPECT_TRUE(buffer.timedOut()); 326 kill(pid, SIGKILL); // reap the child process 327 } 328 } 329