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:
SetUp()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 
AssertBufferReadSuccessful(size_t expected)45     void AssertBufferReadSuccessful(size_t expected) {
46         EXPECT_EQ(buffer.size(), expected);
47         EXPECT_FALSE(buffer.timedOut());
48         EXPECT_FALSE(buffer.truncated());
49     }
50 
AssertBufferContent(const char * expected)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 
DoDataStream(const unique_fd & rFd,const unique_fd & wFd)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 
TEST_F(FdBufferTest,ReadAndWrite)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 
TEST_F(FdBufferTest,IterateEmpty)94 TEST_F(FdBufferTest, IterateEmpty) {
95     sp<ProtoReader> reader = buffer.data()->read();
96     EXPECT_FALSE(reader->hasNext());
97 }
98 
TEST_F(FdBufferTest,ReadAndIterate)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 
TEST_F(FdBufferTest,Move)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 
TEST_F(FdBufferTest,ReadTimeout)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 
TEST_F(FdBufferTest,ReadInStreamAndWrite)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 
TEST_F(FdBufferTest,ReadInStreamAndWriteAllAtOnce)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 
TEST_F(FdBufferTest,ReadInStreamEmpty)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 
TEST_F(FdBufferTest,ReadInStreamMoreThan4MBWithMove)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 
TEST_F(FdBufferTest,ReadInStreamMoreThan4MBWithNext)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 
TEST_F(FdBufferTest,ReadInStreamTimeOut)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