Bug: 63863444 Test: manual - create gtests for CommandSection and Procrank Parser following instructions in the README.md of incidentd and incident_helper on how to run them. Change-Id: I099808fd13bf9ed9a564b122f1126b1691a83291
254 lines
7.6 KiB
C++
254 lines
7.6 KiB
C++
// Copyright (C) 2017 The Android Open Source Project
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
#define LOG_TAG "incidentd"
|
|
|
|
#include "FdBuffer.h"
|
|
|
|
#include <android-base/file.h>
|
|
#include <android-base/test_utils.h>
|
|
#include <gmock/gmock.h>
|
|
#include <gtest/gtest.h>
|
|
#include <signal.h>
|
|
#include <string.h>
|
|
|
|
const int READ_TIMEOUT = 5 * 1000;
|
|
const int BUFFER_SIZE = 16 * 1024;
|
|
const int QUICK_TIMEOUT_MS = 100;
|
|
const std::string HEAD = "[OK]";
|
|
|
|
using namespace android;
|
|
using namespace android::base;
|
|
using ::testing::StrEq;
|
|
using ::testing::Test;
|
|
using ::testing::internal::CaptureStdout;
|
|
using ::testing::internal::GetCapturedStdout;
|
|
|
|
class FdBufferTest : public Test {
|
|
public:
|
|
virtual void SetUp() override {
|
|
ASSERT_NE(tf.fd, -1);
|
|
ASSERT_NE(p2cPipe.init(), -1);
|
|
ASSERT_NE(c2pPipe.init(), -1);
|
|
}
|
|
|
|
void AssertBufferReadSuccessful(size_t expected) {
|
|
EXPECT_EQ(buffer.size(), expected);
|
|
EXPECT_FALSE(buffer.timedOut());
|
|
EXPECT_FALSE(buffer.truncated());
|
|
}
|
|
|
|
void AssertBufferContent(const char* expected) {
|
|
ReportRequestSet requests;
|
|
requests.setMainFd(STDOUT_FILENO);
|
|
|
|
CaptureStdout();
|
|
ASSERT_EQ(NO_ERROR, buffer.write(&requests));
|
|
EXPECT_THAT(GetCapturedStdout(), StrEq(expected));
|
|
}
|
|
|
|
bool DoDataStream(int rFd, int wFd) {
|
|
char buf[BUFFER_SIZE];
|
|
ssize_t nRead;
|
|
while ((nRead = read(rFd, buf, BUFFER_SIZE)) > 0) {
|
|
ssize_t nWritten = 0;
|
|
while (nWritten < nRead) {
|
|
ssize_t amt = write(wFd, buf + nWritten, nRead - nWritten);
|
|
if (amt < 0) {
|
|
return false;
|
|
}
|
|
nWritten += amt;
|
|
}
|
|
}
|
|
return nRead == 0;
|
|
}
|
|
|
|
protected:
|
|
FdBuffer buffer;
|
|
TemporaryFile tf;
|
|
Fpipe p2cPipe;
|
|
Fpipe c2pPipe;
|
|
|
|
const std::string kTestPath = GetExecutableDirectory();
|
|
const std::string kTestDataPath = kTestPath + "/testdata/";
|
|
};
|
|
|
|
TEST_F(FdBufferTest, ReadAndWrite) {
|
|
std::string testdata = "FdBuffer test string";
|
|
ASSERT_TRUE(WriteStringToFile(testdata, tf.path, false));
|
|
ASSERT_EQ(NO_ERROR, buffer.read(tf.fd, READ_TIMEOUT));
|
|
AssertBufferReadSuccessful(testdata.size());
|
|
AssertBufferContent(testdata.c_str());
|
|
}
|
|
|
|
TEST_F(FdBufferTest, ReadTimeout) {
|
|
int pid = fork();
|
|
ASSERT_TRUE(pid != -1);
|
|
|
|
if (pid == 0) {
|
|
close(c2pPipe.readFd());
|
|
while(true) {
|
|
write(c2pPipe.writeFd(), "poo", 3);
|
|
sleep(1);
|
|
}
|
|
_exit(EXIT_FAILURE);
|
|
} else {
|
|
close(c2pPipe.writeFd());
|
|
|
|
status_t status = buffer.read(c2pPipe.readFd(), QUICK_TIMEOUT_MS);
|
|
ASSERT_EQ(NO_ERROR, status);
|
|
EXPECT_TRUE(buffer.timedOut());
|
|
|
|
kill(pid, SIGKILL); // reap the child process
|
|
}
|
|
}
|
|
|
|
TEST_F(FdBufferTest, ReadInStreamAndWrite) {
|
|
std::string testdata = "simply test read in stream";
|
|
std::string expected = HEAD + testdata;
|
|
ASSERT_TRUE(WriteStringToFile(testdata, tf.path, false));
|
|
|
|
int pid = fork();
|
|
ASSERT_TRUE(pid != -1);
|
|
|
|
if (pid == 0) {
|
|
close(p2cPipe.writeFd());
|
|
close(c2pPipe.readFd());
|
|
ASSERT_TRUE(WriteStringToFd(HEAD, c2pPipe.writeFd()));
|
|
ASSERT_TRUE(DoDataStream(p2cPipe.readFd(), c2pPipe.writeFd()));
|
|
close(p2cPipe.readFd());
|
|
close(c2pPipe.writeFd());
|
|
// Must exit here otherwise the child process will continue executing the test binary.
|
|
_exit(EXIT_SUCCESS);
|
|
} else {
|
|
close(p2cPipe.readFd());
|
|
close(c2pPipe.writeFd());
|
|
|
|
ASSERT_EQ(NO_ERROR, buffer.readProcessedDataInStream(tf.fd,
|
|
p2cPipe.writeFd(), c2pPipe.readFd(), READ_TIMEOUT));
|
|
AssertBufferReadSuccessful(HEAD.size() + testdata.size());
|
|
AssertBufferContent(expected.c_str());
|
|
wait(&pid);
|
|
}
|
|
}
|
|
|
|
TEST_F(FdBufferTest, ReadInStreamAndWriteAllAtOnce) {
|
|
std::string testdata = "child process flushes only after all data are read.";
|
|
std::string expected = HEAD + testdata;
|
|
ASSERT_TRUE(WriteStringToFile(testdata, tf.path, false));
|
|
|
|
int pid = fork();
|
|
ASSERT_TRUE(pid != -1);
|
|
|
|
if (pid == 0) {
|
|
close(p2cPipe.writeFd());
|
|
close(c2pPipe.readFd());
|
|
std::string data;
|
|
// wait for read finishes then write.
|
|
ASSERT_TRUE(ReadFdToString(p2cPipe.readFd(), &data));
|
|
data = HEAD + data;
|
|
ASSERT_TRUE(WriteStringToFd(data, c2pPipe.writeFd()));
|
|
close(p2cPipe.readFd());
|
|
close(c2pPipe.writeFd());
|
|
// Must exit here otherwise the child process will continue executing the test binary.
|
|
_exit(EXIT_SUCCESS);
|
|
} else {
|
|
close(p2cPipe.readFd());
|
|
close(c2pPipe.writeFd());
|
|
|
|
ASSERT_EQ(NO_ERROR, buffer.readProcessedDataInStream(tf.fd,
|
|
p2cPipe.writeFd(), c2pPipe.readFd(), READ_TIMEOUT));
|
|
AssertBufferReadSuccessful(HEAD.size() + testdata.size());
|
|
AssertBufferContent(expected.c_str());
|
|
wait(&pid);
|
|
}
|
|
}
|
|
|
|
TEST_F(FdBufferTest, ReadInStreamEmpty) {
|
|
ASSERT_TRUE(WriteStringToFile("", tf.path, false));
|
|
|
|
int pid = fork();
|
|
ASSERT_TRUE(pid != -1);
|
|
|
|
if (pid == 0) {
|
|
close(p2cPipe.writeFd());
|
|
close(c2pPipe.readFd());
|
|
ASSERT_TRUE(DoDataStream(p2cPipe.readFd(), c2pPipe.writeFd()));
|
|
close(p2cPipe.readFd());
|
|
close(c2pPipe.writeFd());
|
|
_exit(EXIT_SUCCESS);
|
|
} else {
|
|
close(p2cPipe.readFd());
|
|
close(c2pPipe.writeFd());
|
|
|
|
ASSERT_EQ(NO_ERROR, buffer.readProcessedDataInStream(tf.fd,
|
|
p2cPipe.writeFd(), c2pPipe.readFd(), READ_TIMEOUT));
|
|
AssertBufferReadSuccessful(0);
|
|
AssertBufferContent("");
|
|
wait(&pid);
|
|
}
|
|
}
|
|
|
|
TEST_F(FdBufferTest, ReadInStreamMoreThan4MB) {
|
|
const std::string testFile = kTestDataPath + "morethan4MB.txt";
|
|
int fd = open(testFile.c_str(), O_RDONLY);
|
|
ASSERT_NE(fd, -1);
|
|
int pid = fork();
|
|
ASSERT_TRUE(pid != -1);
|
|
|
|
if (pid == 0) {
|
|
close(p2cPipe.writeFd());
|
|
close(c2pPipe.readFd());
|
|
ASSERT_TRUE(DoDataStream(p2cPipe.readFd(), c2pPipe.writeFd()));
|
|
close(p2cPipe.readFd());
|
|
close(c2pPipe.writeFd());
|
|
_exit(EXIT_SUCCESS);
|
|
} else {
|
|
close(p2cPipe.readFd());
|
|
close(c2pPipe.writeFd());
|
|
|
|
ASSERT_EQ(NO_ERROR, buffer.readProcessedDataInStream(fd,
|
|
p2cPipe.writeFd(), c2pPipe.readFd(), READ_TIMEOUT));
|
|
EXPECT_EQ(buffer.size(), (size_t) (4 * 1024 * 1024));
|
|
EXPECT_FALSE(buffer.timedOut());
|
|
EXPECT_TRUE(buffer.truncated());
|
|
wait(&pid);
|
|
}
|
|
}
|
|
|
|
TEST_F(FdBufferTest, ReadInStreamTimeOut) {
|
|
std::string testdata = "timeout test";
|
|
ASSERT_TRUE(WriteStringToFile(testdata, tf.path, false));
|
|
|
|
int pid = fork();
|
|
ASSERT_TRUE(pid != -1);
|
|
|
|
if (pid == 0) {
|
|
close(p2cPipe.writeFd());
|
|
close(c2pPipe.readFd());
|
|
while (true) {
|
|
sleep(1);
|
|
}
|
|
_exit(EXIT_FAILURE);
|
|
} else {
|
|
close(p2cPipe.readFd());
|
|
close(c2pPipe.writeFd());
|
|
|
|
ASSERT_EQ(NO_ERROR, buffer.readProcessedDataInStream(tf.fd,
|
|
p2cPipe.writeFd(), c2pPipe.readFd(), QUICK_TIMEOUT_MS));
|
|
EXPECT_TRUE(buffer.timedOut());
|
|
kill(pid, SIGKILL); // reap the child process
|
|
}
|
|
}
|