Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9db13389 authored by Treehugger Robot's avatar Treehugger Robot Committed by Gerrit Code Review
Browse files

Merge "Prefactoring: rewrite section detection smoke tests"

parents cdb84a37 ea71b38b
Loading
Loading
Loading
Loading
+122 −52
Original line number Original line Diff line number Diff line
@@ -14,20 +14,21 @@
 * limitations under the License.
 * limitations under the License.
 */
 */


#include <gmock/gmock.h>
#include <gtest/gtest.h>

#include <fcntl.h>
#include <libgen.h>

#include <android-base/file.h>
#include <android-base/file.h>
#include <android/os/BnDumpstate.h>
#include <android/os/BnDumpstate.h>
#include <android/os/BnDumpstateListener.h>
#include <android/os/BnDumpstateListener.h>
#include <binder/IServiceManager.h>
#include <binder/IServiceManager.h>
#include <binder/ProcessState.h>
#include <binder/ProcessState.h>
#include <cutils/properties.h>
#include <cutils/properties.h>
#include <fcntl.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <libgen.h>
#include <ziparchive/zip_archive.h>
#include <ziparchive/zip_archive.h>


#include <fstream>
#include <regex>

#include "dumpstate.h"
#include "dumpstate.h"


#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
@@ -44,6 +45,11 @@ class DumpstateListener;


namespace {
namespace {


struct SectionInfo {
    std::string name;
    int32_t size_bytes;
};

sp<IDumpstate> GetDumpstateService() {
sp<IDumpstate> GetDumpstateService() {
    return android::interface_cast<IDumpstate>(
    return android::interface_cast<IDumpstate>(
        android::defaultServiceManager()->getService(String16("dumpstate")));
        android::defaultServiceManager()->getService(String16("dumpstate")));
@@ -55,14 +61,79 @@ int OpenForWrite(const std::string& filename) {
                                   S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH));
                                   S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH));
}
}


}  // namespace
void GetEntry(const ZipArchiveHandle archive, const std::string_view entry_name, ZipEntry* data) {
    int32_t e = FindEntry(archive, entry_name, data);
    EXPECT_EQ(e, 0) << ErrorCodeString(e) << " entry name: " << entry_name;
}


struct SectionInfo {
// Extracts the main bugreport txt from the given archive and writes into output_fd.
    std::string name;
void ExtractBugreport(const ZipArchiveHandle* handle, int output_fd) {
    status_t status;
    // Read contents of main_entry.txt which is a single line indicating the name of the zip entry
    int32_t size_bytes;
    // that contains the main bugreport txt.
    int32_t duration_ms;
    ZipEntry main_entry;
};
    GetEntry(*handle, "main_entry.txt", &main_entry);
    std::string bugreport_txt_name;
    bugreport_txt_name.resize(main_entry.uncompressed_length);
    ExtractToMemory(*handle, &main_entry, reinterpret_cast<uint8_t*>(bugreport_txt_name.data()),
                    main_entry.uncompressed_length);

    // Read the main bugreport txt and extract to output_fd.
    ZipEntry entry;
    GetEntry(*handle, bugreport_txt_name, &entry);
    ExtractEntryToFile(*handle, &entry, output_fd);
}

bool IsSectionStart(const std::string& line, std::string* section_name) {
    static const std::regex kSectionStart = std::regex{"DUMP OF SERVICE (.*):"};
    std::smatch match;
    if (std::regex_match(line, match, kSectionStart)) {
        *section_name = match.str(1);
        return true;
    }
    return false;
}

bool IsSectionEnd(const std::string& line) {
    // Not all lines that contain "was the duration of" is a section end, but all section ends do
    // contain "was the duration of". The disambiguation can be done by the caller.
    return (line.find("was the duration of") != std::string::npos);
}

// Extracts the zipped bugreport and identifies the sections.
void ParseSections(const std::string& zip_path, std::vector<SectionInfo>* sections) {
    // Open the archive
    ZipArchiveHandle handle;
    ASSERT_EQ(OpenArchive(zip_path.c_str(), &handle), 0);

    // Extract the main entry to a temp file
    TemporaryFile tmp_binary;
    ASSERT_NE(-1, tmp_binary.fd);
    ExtractBugreport(&handle, tmp_binary.fd);

    // Read line by line and identify sections
    std::ifstream ifs(tmp_binary.path, std::ifstream::in);
    std::string line;
    int section_bytes = 0;
    std::string current_section_name;
    while (std::getline(ifs, line)) {
        std::string section_name;
        if (IsSectionStart(line, &section_name)) {
            section_bytes = 0;
            current_section_name = section_name;
        } else if (IsSectionEnd(line)) {
            if (!current_section_name.empty()) {
                sections->push_back({current_section_name, section_bytes});
            }
            current_section_name = "";
        } else if (!current_section_name.empty()) {
            section_bytes += line.length();
        }
    }

    CloseArchive(handle);
}

}  // namespace


/**
/**
 * Listens to bugreport progress and updates the user by writing the progress to STDOUT. All the
 * Listens to bugreport progress and updates the user by writing the progress to STDOUT. All the
@@ -107,11 +178,11 @@ class DumpstateListener : public BnDumpstateListener {
        return binder::Status::ok();
        return binder::Status::ok();
    }
    }


    binder::Status onSectionComplete(const ::std::string& name, int32_t status, int32_t size_bytes,
    binder::Status onSectionComplete(const ::std::string& name, int32_t, int32_t size_bytes,
                                     int32_t duration_ms) override {
                                     int32_t) override {
        std::lock_guard<std::mutex> lock(lock_);
        std::lock_guard<std::mutex> lock(lock_);
        if (sections_.get() != nullptr) {
        if (sections_.get() != nullptr) {
            sections_->push_back({name, status, size_bytes, duration_ms});
            sections_->push_back({name, size_bytes});
        }
        }
        return binder::Status::ok();
        return binder::Status::ok();
    }
    }
@@ -208,29 +279,30 @@ class ZippedBugReportContentsTest : public Test {


    void FileExists(const char* filename, uint32_t minsize, uint32_t maxsize) {
    void FileExists(const char* filename, uint32_t minsize, uint32_t maxsize) {
        ZipEntry entry;
        ZipEntry entry;
        EXPECT_EQ(FindEntry(handle, filename, &entry), 0);
        GetEntry(handle, filename, &entry);
        EXPECT_GT(entry.uncompressed_length, minsize);
        EXPECT_GT(entry.uncompressed_length, minsize);
        EXPECT_LT(entry.uncompressed_length, maxsize);
        EXPECT_LT(entry.uncompressed_length, maxsize);
    }
    }
};
};


TEST_F(ZippedBugReportContentsTest, ContainsMainEntry) {
TEST_F(ZippedBugReportContentsTest, ContainsMainEntry) {
    ZipEntry mainEntryLoc;
    ZipEntry main_entry;
    // contains main entry name file
    // contains main entry name file
    EXPECT_EQ(FindEntry(handle, "main_entry.txt", &mainEntryLoc), 0);
    GetEntry(handle, "main_entry.txt", &main_entry);


    char* buf = new char[mainEntryLoc.uncompressed_length];
    std::string bugreport_txt_name;
    ExtractToMemory(handle, &mainEntryLoc, (uint8_t*)buf, mainEntryLoc.uncompressed_length);
    bugreport_txt_name.resize(main_entry.uncompressed_length);
    delete[] buf;
    ExtractToMemory(handle, &main_entry, reinterpret_cast<uint8_t*>(bugreport_txt_name.data()),
                    main_entry.uncompressed_length);


    // contains main entry file
    // contains main entry file
    FileExists(buf, 1000000U, 50000000U);
    FileExists(bugreport_txt_name.c_str(), 1000000U, 50000000U);
}
}


TEST_F(ZippedBugReportContentsTest, ContainsVersion) {
TEST_F(ZippedBugReportContentsTest, ContainsVersion) {
    ZipEntry entry;
    ZipEntry entry;
    // contains main entry name file
    // contains main entry name file
    EXPECT_EQ(FindEntry(handle, "version.txt", &entry), 0);
    GetEntry(handle, "version.txt", &entry);


    char* buf = new char[entry.uncompressed_length + 1];
    char* buf = new char[entry.uncompressed_length + 1];
    ExtractToMemory(handle, &entry, (uint8_t*)buf, entry.uncompressed_length);
    ExtractToMemory(handle, &entry, (uint8_t*)buf, entry.uncompressed_length);
@@ -244,6 +316,10 @@ TEST_F(ZippedBugReportContentsTest, ContainsBoardSpecificFiles) {
    FileExists("dumpstate_board.txt", 100000U, 1000000U);
    FileExists("dumpstate_board.txt", 100000U, 1000000U);
}
}


TEST_F(ZippedBugReportContentsTest, ContainsProtoFile) {
    FileExists("proto/activity.proto", 100000U, 1000000U);
}

// Spot check on some files pulled from the file system
// Spot check on some files pulled from the file system
TEST_F(ZippedBugReportContentsTest, ContainsSomeFileSystemFiles) {
TEST_F(ZippedBugReportContentsTest, ContainsSomeFileSystemFiles) {
    // FS/proc/*/mountinfo size > 0
    // FS/proc/*/mountinfo size > 0
@@ -258,6 +334,11 @@ TEST_F(ZippedBugReportContentsTest, ContainsSomeFileSystemFiles) {
 */
 */
class BugreportSectionTest : public Test {
class BugreportSectionTest : public Test {
  public:
  public:
    static void SetUpTestCase() {
        ParseSections(ZippedBugreportGenerationTest::getZipFilePath(),
                      ZippedBugreportGenerationTest::sections.get());
    }

    int numMatches(const std::string& substring) {
    int numMatches(const std::string& substring) {
        int matches = 0;
        int matches = 0;
        for (auto const& section : *ZippedBugreportGenerationTest::sections) {
        for (auto const& section : *ZippedBugreportGenerationTest::sections) {
@@ -267,10 +348,11 @@ class BugreportSectionTest : public Test {
        }
        }
        return matches;
        return matches;
    }
    }

    void SectionExists(const std::string& sectionName, int minsize) {
    void SectionExists(const std::string& sectionName, int minsize) {
        for (auto const& section : *ZippedBugreportGenerationTest::sections) {
        for (auto const& section : *ZippedBugreportGenerationTest::sections) {
            if (sectionName == section.name) {
            if (sectionName == section.name) {
                EXPECT_GE(section.size_bytes, minsize);
                EXPECT_GE(section.size_bytes, minsize) << " for section:" << sectionName;
                return;
                return;
            }
            }
        }
        }
@@ -278,71 +360,59 @@ class BugreportSectionTest : public Test {
    }
    }
};
};


// Test all sections are generated without timeouts or errors
TEST_F(BugreportSectionTest, GeneratedWithoutErrors) {
    for (auto const& section : *ZippedBugreportGenerationTest::sections) {
        EXPECT_EQ(section.status, 0) << section.name << " failed with status " << section.status;
    }
}

TEST_F(BugreportSectionTest, Atleast3CriticalDumpsysSectionsGenerated) {
TEST_F(BugreportSectionTest, Atleast3CriticalDumpsysSectionsGenerated) {
    int numSections = numMatches("DUMPSYS CRITICAL");
    int numSections = numMatches("CRITICAL");
    EXPECT_GE(numSections, 3);
    EXPECT_GE(numSections, 3);
}
}


TEST_F(BugreportSectionTest, Atleast2HighDumpsysSectionsGenerated) {
TEST_F(BugreportSectionTest, Atleast2HighDumpsysSectionsGenerated) {
    int numSections = numMatches("DUMPSYS HIGH");
    int numSections = numMatches("HIGH");
    EXPECT_GE(numSections, 2);
    EXPECT_GE(numSections, 2);
}
}


TEST_F(BugreportSectionTest, Atleast50NormalDumpsysSectionsGenerated) {
TEST_F(BugreportSectionTest, Atleast50NormalDumpsysSectionsGenerated) {
    int allSections = numMatches("DUMPSYS");
    int allSections = ZippedBugreportGenerationTest::sections->size();
    int criticalSections = numMatches("DUMPSYS CRITICAL");
    int criticalSections = numMatches("CRITICAL");
    int highSections = numMatches("DUMPSYS HIGH");
    int highSections = numMatches("HIGH");
    int normalSections = allSections - criticalSections - highSections;
    int normalSections = allSections - criticalSections - highSections;


    EXPECT_GE(normalSections, 50) << "Total sections less than 50 (Critical:" << criticalSections
    EXPECT_GE(normalSections, 50) << "Total sections less than 50 (Critical:" << criticalSections
                                  << "High:" << highSections << "Normal:" << normalSections << ")";
                                  << "High:" << highSections << "Normal:" << normalSections << ")";
}
}


TEST_F(BugreportSectionTest, Atleast1ProtoDumpsysSectionGenerated) {
    int numSections = numMatches("proto/");
    EXPECT_GE(numSections, 1);
}

// Test if some critical sections are being generated.
// Test if some critical sections are being generated.
TEST_F(BugreportSectionTest, CriticalSurfaceFlingerSectionGenerated) {
TEST_F(BugreportSectionTest, CriticalSurfaceFlingerSectionGenerated) {
    SectionExists("DUMPSYS CRITICAL - SurfaceFlinger", /* bytes= */ 10000);
    SectionExists("CRITICAL SurfaceFlinger", /* bytes= */ 10000);
}
}


TEST_F(BugreportSectionTest, ActivitySectionsGenerated) {
TEST_F(BugreportSectionTest, ActivitySectionsGenerated) {
    SectionExists("DUMPSYS CRITICAL - activity", /* bytes= */ 5000);
    SectionExists("CRITICAL activity", /* bytes= */ 5000);
    SectionExists("DUMPSYS - activity", /* bytes= */ 10000);
    SectionExists("activity", /* bytes= */ 10000);
}
}


TEST_F(BugreportSectionTest, CpuinfoSectionGenerated) {
TEST_F(BugreportSectionTest, CpuinfoSectionGenerated) {
    SectionExists("DUMPSYS CRITICAL - cpuinfo", /* bytes= */ 1000);
    SectionExists("CRITICAL cpuinfo", /* bytes= */ 1000);
}
}


TEST_F(BugreportSectionTest, WindowSectionGenerated) {
TEST_F(BugreportSectionTest, WindowSectionGenerated) {
    SectionExists("DUMPSYS CRITICAL - window", /* bytes= */ 20000);
    SectionExists("CRITICAL window", /* bytes= */ 20000);
}
}


TEST_F(BugreportSectionTest, ConnectivitySectionsGenerated) {
TEST_F(BugreportSectionTest, ConnectivitySectionsGenerated) {
    SectionExists("DUMPSYS HIGH - connectivity", /* bytes= */ 5000);
    SectionExists("HIGH connectivity", /* bytes= */ 3000);
    SectionExists("DUMPSYS - connectivity", /* bytes= */ 5000);
    SectionExists("connectivity", /* bytes= */ 5000);
}
}


TEST_F(BugreportSectionTest, MeminfoSectionGenerated) {
TEST_F(BugreportSectionTest, MeminfoSectionGenerated) {
    SectionExists("DUMPSYS HIGH - meminfo", /* bytes= */ 100000);
    SectionExists("HIGH meminfo", /* bytes= */ 100000);
}
}


TEST_F(BugreportSectionTest, BatteryStatsSectionGenerated) {
TEST_F(BugreportSectionTest, BatteryStatsSectionGenerated) {
    SectionExists("DUMPSYS - batterystats", /* bytes= */ 1000);
    SectionExists("batterystats", /* bytes= */ 1000);
}
}


TEST_F(BugreportSectionTest, WifiSectionGenerated) {
TEST_F(BugreportSectionTest, WifiSectionGenerated) {
    SectionExists("DUMPSYS - wifi", /* bytes= */ 100000);
    SectionExists("wifi", /* bytes= */ 100000);
}
}


class DumpstateBinderTest : public Test {
class DumpstateBinderTest : public Test {