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 <android-base/file.h>
21 #include <android-base/test_utils.h>
22 #include <fcntl.h>
23 #include <gtest/gtest.h>
24 #include <signal.h>
25 #include <string.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         EncodedBuffer::iterator it = buffer.data();
54         while (it.hasNext()) {
55             ASSERT_EQ(it.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     EncodedBuffer::iterator it = buffer.data();
96     EXPECT_FALSE(it.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     EncodedBuffer::iterator it = buffer.data();
106     while (it.hasNext()) {
107         EXPECT_EQ(it.next(), (uint8_t)testdata[i++]);
108     }
109 
110     it.rp()->rewind();
111     it.rp()->move(buffer.size());
112     EXPECT_EQ(it.bytesRead(), testdata.size());
113     EXPECT_FALSE(it.hasNext());
114 }
115 
TEST_F(FdBufferTest,ReadTimeout)116 TEST_F(FdBufferTest, ReadTimeout) {
117     int pid = fork();
118     ASSERT_TRUE(pid != -1);
119 
120     if (pid == 0) {
121         c2pPipe.readFd().reset();
122         while (true) {
123             write(c2pPipe.writeFd(), "poo", 3);
124             sleep(1);
125         }
126         _exit(EXIT_FAILURE);
127     } else {
128         c2pPipe.writeFd().reset();
129 
130         status_t status = buffer.read(c2pPipe.readFd().get(), QUICK_TIMEOUT_MS);
131         ASSERT_EQ(NO_ERROR, status);
132         EXPECT_TRUE(buffer.timedOut());
133 
134         kill(pid, SIGKILL);  // reap the child process
135     }
136 }
137 
TEST_F(FdBufferTest,ReadInStreamAndWrite)138 TEST_F(FdBufferTest, ReadInStreamAndWrite) {
139     std::string testdata = "simply test read in stream";
140     std::string expected = HEAD + testdata;
141     ASSERT_TRUE(WriteStringToFile(testdata, tf.path));
142 
143     int pid = fork();
144     ASSERT_TRUE(pid != -1);
145 
146     if (pid == 0) {
147         p2cPipe.writeFd().reset();
148         c2pPipe.readFd().reset();
149         ASSERT_TRUE(WriteStringToFd(HEAD, c2pPipe.writeFd()));
150         ASSERT_TRUE(DoDataStream(p2cPipe.readFd(), c2pPipe.writeFd()));
151         p2cPipe.readFd().reset();
152         c2pPipe.writeFd().reset();
153         // Must exit here otherwise the child process will continue executing the test binary.
154         _exit(EXIT_SUCCESS);
155     } else {
156         p2cPipe.readFd().reset();
157         c2pPipe.writeFd().reset();
158 
159         ASSERT_EQ(NO_ERROR,
160                   buffer.readProcessedDataInStream(tf.fd, std::move(p2cPipe.writeFd()),
161                                                    std::move(c2pPipe.readFd()), READ_TIMEOUT));
162         AssertBufferReadSuccessful(HEAD.size() + testdata.size());
163         AssertBufferContent(expected.c_str());
164         wait(&pid);
165     }
166 }
167 
TEST_F(FdBufferTest,ReadInStreamAndWriteAllAtOnce)168 TEST_F(FdBufferTest, ReadInStreamAndWriteAllAtOnce) {
169     std::string testdata = "child process flushes only after all data are read.";
170     std::string expected = HEAD + testdata;
171     ASSERT_TRUE(WriteStringToFile(testdata, tf.path));
172 
173     int pid = fork();
174     ASSERT_TRUE(pid != -1);
175 
176     if (pid == 0) {
177         p2cPipe.writeFd().reset();
178         c2pPipe.readFd().reset();
179         std::string data;
180         // wait for read finishes then write.
181         ASSERT_TRUE(ReadFdToString(p2cPipe.readFd(), &data));
182         data = HEAD + data;
183         ASSERT_TRUE(WriteStringToFd(data, c2pPipe.writeFd()));
184         p2cPipe.readFd().reset();
185         c2pPipe.writeFd().reset();
186         // Must exit here otherwise the child process will continue executing the test binary.
187         _exit(EXIT_SUCCESS);
188     } else {
189         p2cPipe.readFd().reset();
190         c2pPipe.writeFd().reset();
191 
192         ASSERT_EQ(NO_ERROR,
193                   buffer.readProcessedDataInStream(tf.fd, std::move(p2cPipe.writeFd()),
194                                                    std::move(c2pPipe.readFd()), READ_TIMEOUT));
195         AssertBufferReadSuccessful(HEAD.size() + testdata.size());
196         AssertBufferContent(expected.c_str());
197         wait(&pid);
198     }
199 }
200 
TEST_F(FdBufferTest,ReadInStreamEmpty)201 TEST_F(FdBufferTest, ReadInStreamEmpty) {
202     ASSERT_TRUE(WriteStringToFile("", tf.path));
203 
204     int pid = fork();
205     ASSERT_TRUE(pid != -1);
206 
207     if (pid == 0) {
208         p2cPipe.writeFd().reset();
209         c2pPipe.readFd().reset();
210         ASSERT_TRUE(DoDataStream(p2cPipe.readFd(), c2pPipe.writeFd()));
211         p2cPipe.readFd().reset();
212         c2pPipe.writeFd().reset();
213         _exit(EXIT_SUCCESS);
214     } else {
215         p2cPipe.readFd().reset();
216         c2pPipe.writeFd().reset();
217 
218         ASSERT_EQ(NO_ERROR,
219                   buffer.readProcessedDataInStream(tf.fd, std::move(p2cPipe.writeFd()),
220                                                    std::move(c2pPipe.readFd()), READ_TIMEOUT));
221         AssertBufferReadSuccessful(0);
222         AssertBufferContent("");
223         wait(&pid);
224     }
225 }
226 
TEST_F(FdBufferTest,ReadInStreamMoreThan4MB)227 TEST_F(FdBufferTest, ReadInStreamMoreThan4MB) {
228     const std::string testFile = kTestDataPath + "morethan4MB.txt";
229     size_t fourMB = (size_t)4 * 1024 * 1024;
230     unique_fd fd(open(testFile.c_str(), O_RDONLY | O_CLOEXEC));
231     ASSERT_NE(fd.get(), -1);
232     int pid = fork();
233     ASSERT_TRUE(pid != -1);
234 
235     if (pid == 0) {
236         p2cPipe.writeFd().reset();
237         c2pPipe.readFd().reset();
238         ASSERT_TRUE(DoDataStream(p2cPipe.readFd(), c2pPipe.writeFd()));
239         p2cPipe.readFd().reset();
240         c2pPipe.writeFd().reset();
241         _exit(EXIT_SUCCESS);
242     } else {
243         p2cPipe.readFd().reset();
244         c2pPipe.writeFd().reset();
245 
246         ASSERT_EQ(NO_ERROR,
247                   buffer.readProcessedDataInStream(fd, std::move(p2cPipe.writeFd()),
248                                                    std::move(c2pPipe.readFd()), READ_TIMEOUT));
249         EXPECT_EQ(buffer.size(), fourMB);
250         EXPECT_FALSE(buffer.timedOut());
251         EXPECT_TRUE(buffer.truncated());
252         wait(&pid);
253         EncodedBuffer::iterator it = buffer.data();
254         it.rp()->move(fourMB);
255         EXPECT_EQ(it.bytesRead(), fourMB);
256         EXPECT_FALSE(it.hasNext());
257 
258         it.rp()->rewind();
259         while (it.hasNext()) {
260             char c = 'A' + (it.bytesRead() % 64 / 8);
261             ASSERT_TRUE(it.next() == c);
262         }
263     }
264 }
265 
TEST_F(FdBufferTest,ReadInStreamTimeOut)266 TEST_F(FdBufferTest, ReadInStreamTimeOut) {
267     std::string testdata = "timeout test";
268     ASSERT_TRUE(WriteStringToFile(testdata, tf.path));
269 
270     int pid = fork();
271     ASSERT_TRUE(pid != -1);
272 
273     if (pid == 0) {
274         p2cPipe.writeFd().reset();
275         c2pPipe.readFd().reset();
276         while (true) {
277             sleep(1);
278         }
279         _exit(EXIT_FAILURE);
280     } else {
281         p2cPipe.readFd().reset();
282         c2pPipe.writeFd().reset();
283 
284         ASSERT_EQ(NO_ERROR,
285                   buffer.readProcessedDataInStream(tf.fd, std::move(p2cPipe.writeFd()),
286                                                    std::move(c2pPipe.readFd()), QUICK_TIMEOUT_MS));
287         EXPECT_TRUE(buffer.timedOut());
288         kill(pid, SIGKILL);  // reap the child process
289     }
290 }
291