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