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

Commit 4c2d6637 authored by Felipe Leme's avatar Felipe Leme
Browse files

Added initial tests for dumpstate.

BUG: 31807540
Test: mmm -j32 frameworks/native/cmds/dumpstate/ && adb push ${ANDROID_PRODUCT_OUT}/data/nativetest/dumpstate_test* /data/nativetest && adb shell /data/nativetest/dumpstate_test/dumpstate_test

Change-Id: If5497784052b8d13d7c856f9400dbcd8c2015d05
parent c7fe8fe5
Loading
Loading
Loading
Loading
+67 −13
Original line number Diff line number Diff line
LOCAL_PATH:= $(call my-dir)

# ================#
# Common settings #
# ================#
# ZipArchive support, the order matters here to get all symbols.
COMMON_ZIP_LIBRARIES := libziparchive libz libcrypto_static

# TODO: ideally the tests should depend on a shared dumpstate library, but currently libdumpstate
# is used to define the device-specific HAL library. Instead, both dumpstate and dumpstate_test
# shares a lot of common settings
COMMON_LOCAL_CFLAGS := \
       -Wall -Werror -Wno-missing-field-initializers -Wno-unused-variable -Wunused-parameter
COMMON_SRC_FILES := \
        utils.cpp
COMMON_SHARED_LIBRARIES := \
        libbase \
        libcutils \
        libhardware_legacy \
        liblog \
        libselinux

# ==========#
# dumpstate #
# ==========#
include $(CLEAR_VARS)

ifdef BOARD_WLAN_DEVICE
LOCAL_CFLAGS := -DFWDUMP_$(BOARD_WLAN_DEVICE)
endif

LOCAL_SRC_FILES := \
        dumpstate.cpp \
        utils.cpp
LOCAL_SRC_FILES := $(COMMON_SRC_FILES) \
        dumpstate.cpp

LOCAL_MODULE := dumpstate

LOCAL_SHARED_LIBRARIES := \
        libbase \
        libcutils \
        libhardware_legacy \
        liblog \
        libselinux
LOCAL_SHARED_LIBRARIES := $(COMMON_SHARED_LIBRARIES)

# ZipArchive support, the order matters here to get all symbols.
ZIP_LIBRARIES := libziparchive libz libcrypto_static
LOCAL_STATIC_LIBRARIES := $(COMMON_ZIP_LIBRARIES)

LOCAL_STATIC_LIBRARIES := $(ZIP_LIBRARIES)
LOCAL_HAL_STATIC_LIBRARIES := libdumpstate
LOCAL_CFLAGS += -Wall -Werror -Wno-unused-parameter

LOCAL_CFLAGS += $(COMMON_LOCAL_CFLAGS)

LOCAL_INIT_RC := dumpstate.rc

include $(BUILD_EXECUTABLE)

# ===============#
# dumpstate_test #
# ===============#
include $(CLEAR_VARS)

LOCAL_MODULE := dumpstate_test

LOCAL_MODULE_TAGS := tests

LOCAL_CFLAGS := $(COMMON_LOCAL_CFLAGS)

LOCAL_SRC_FILES := $(COMMON_SRC_FILES) \
        tests/dumpstate_test.cpp

LOCAL_STATIC_LIBRARIES := $(COMMON_ZIP_LIBRARIES) \
        libgmock

LOCAL_SHARED_LIBRARIES := $(COMMON_SHARED_LIBRARIES)

include $(BUILD_NATIVE_TEST)

# =======================#
# dumpstate_test_fixture #
# =======================#
include $(CLEAR_VARS)

LOCAL_MODULE := dumpstate_test_fixture

LOCAL_MODULE_TAGS := tests

LOCAL_CFLAGS := $(COMMON_LOCAL_CFLAGS)

LOCAL_SRC_FILES := \
        tests/dumpstate_test_fixture.cpp

include $(BUILD_NATIVE_TEST)
+9 −18
Original line number Diff line number Diff line
@@ -62,7 +62,6 @@ static const char *dump_traces_path = NULL;
static std::string args;

// TODO: variables below should be part of dumpstate object
static std::string buildType;
static time_t now;
static std::unique_ptr<ZipWriter> zip_writer;
static std::set<std::string> mount_points;
@@ -127,14 +126,6 @@ static const std::string ZIP_ROOT_DIR = "FS";
static constexpr char PROPERTY_EXTRA_OPTIONS[] = "dumpstate.options";
static constexpr char PROPERTY_LAST_ID[] = "dumpstate.last_id";

bool Dumpstate::IsUserBuild() {
    return "user" == buildType;
}

bool Dumpstate::IsDryRun() {
    return dryRun_;
}

/* gets the tombstone data, according to the bugreport type: if zipped, gets all tombstones;
 * otherwise, gets just those modified in the last half an hour. */
static void get_tombstone_fds(tombstone_data_t data[NUM_TOMBSTONES]) {
@@ -155,7 +146,7 @@ static void get_tombstone_fds(tombstone_data_t data[NUM_TOMBSTONES]) {
}

// for_each_pid() callback to get mount info about a process.
void do_mountinfo(int pid, const char *name) {
void do_mountinfo(int pid, const char* name __attribute__((unused))) {
    char path[PATH_MAX];

    // Gets the the content of the /proc/PID/ns/mnt link, so only unique mount points
@@ -426,7 +417,7 @@ static bool skip_not_stat(const char *path) {
    return strcmp(path + len - sizeof(stat) + 1, stat); /* .../stat? */
}

static bool skip_none(const char *path) {
static bool skip_none(const char* path __attribute__((unused))) {
    return false;
}

@@ -704,7 +695,7 @@ void print_header(const std::string& version) {

    build = android::base::GetProperty("ro.build.display.id", "(unknown)");
    fingerprint = android::base::GetProperty("ro.build.fingerprint", "(unknown)");
    buildType = android::base::GetProperty("ro.build.type", "(unknown)");
    ds.buildType_ = android::base::GetProperty("ro.build.type", "(unknown)");
    radio = android::base::GetProperty("gsm.version.baseband", "(unknown)");
    bootloader = android::base::GetProperty("ro.bootloader", "(unknown)");
    network = android::base::GetProperty("gsm.operator.alpha", "(unknown)");
@@ -727,7 +718,7 @@ void print_header(const std::string& version) {
    printf("Command line: %s\n", strtok(cmdline_buf, "\n"));
    printf("Bugreport format version: %s\n", version.c_str());
    printf("Dumpstate info: id=%lu pid=%d dryRun=%d args=%s extraOptions=%s\n", ds.id_, getpid(),
           ds.dryRun_, args.c_str(), extraOptions.c_str());
           ds.IsDryRun(), args.c_str(), extraOptions.c_str());
    printf("\n");
}

@@ -805,7 +796,7 @@ bool add_zip_entry(const std::string& entry_name, const std::string& entry_path)
}

/* adds a file to the existing zipped bugreport */
static int _add_file_from_fd(const char *title, const char *path, int fd) {
static int _add_file_from_fd(const char* title __attribute__((unused)), const char* path, int fd) {
    return add_zip_entry_from_fd(ZIP_ROOT_DIR + path, fd) ? 0 : 1;
}

@@ -861,7 +852,8 @@ static void dump_iptables() {
    RunCommand("IP6TABLE RAW", {"ip6tables", "-t", "raw", "-L", "-nvx"});
}

static void dumpstate(const std::string& screenshot_path, const std::string& version) {
static void dumpstate(const std::string& screenshot_path,
                      const std::string& version __attribute__((unused))) {
    DurationReporter durationReporter("DUMPSTATE");
    unsigned long timeout;

@@ -1193,7 +1185,7 @@ static void wake_lock_releaser() {
    }
}

static void sig_handler(int signo) {
static void sig_handler(int signo __attribute__((unused))) {
    wake_lock_releaser();
    _exit(EXIT_FAILURE);
}
@@ -1319,8 +1311,7 @@ int main(int argc, char *argv[]) {
        register_sig_handler();
    }

    ds.dryRun_ = android::base::GetBoolProperty("dumpstate.dry_run", false);
    if (ds.dryRun_) {
    if (ds.IsDryRun()) {
        MYLOGI("Running on dry-run mode (to disable it, call 'setprop dumpstate.dry_run false')\n");
    }

+8 −3
Original line number Diff line number Diff line
@@ -202,6 +202,8 @@ static const int WEIGHT_TOTAL = 6500;
 * that are spread accross utils.cpp and dumpstate.cpp will be moved to it.
 */
class Dumpstate {
    friend class DumpstateTest;

  public:
    static Dumpstate& GetInstance();

@@ -274,15 +276,18 @@ class Dumpstate {
    // When set, defines a socket file-descriptor use to report progress to bugreportz.
    int controlSocketFd_ = -1;

    // Whether this is a dry run.
    bool dryRun_;
    // Build type (such as 'user' or 'eng').
    std::string buildType_;

    // Full path of the directory where the bugreport files will be written;
    std::string bugreportDir_;

  private:
    // Whether this is a dry run.
    bool dryRun_;

    // Used by GetInstance() only.
    Dumpstate();
    Dumpstate(bool dryRun = false);
};

// for_each_pid_func = void (*)(int, const char*);
+143 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2016 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.
 */

#include "dumpstate.h"

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

#include <libgen.h>
#include <unistd.h>

#include <android-base/file.h>

using ::testing::EndsWith;
using ::testing::IsEmpty;
using ::testing::StrEq;
using ::testing::StartsWith;
using ::testing::Test;
using ::testing::internal::CaptureStderr;
using ::testing::internal::CaptureStdout;
using ::testing::internal::GetCapturedStderr;
using ::testing::internal::GetCapturedStdout;

// Not used on test cases yet...
void dumpstate_board(void) {
}

class DumpstateTest : public Test {
  public:
    void SetUp() {
        SetDryRun(false);
    }

    // Runs a command and capture `stdout` and `stderr`.
    int RunCommand(const std::string& title, const std::vector<std::string>& fullCommand,
                   const CommandOptions& options = CommandOptions::DEFAULT) {
        CaptureStdout();
        CaptureStderr();
        int status = ds_.RunCommand(title, fullCommand, options);
        out = GetCapturedStdout();
        err = GetCapturedStderr();
        return status;
    }

    // `stdout` and `stderr` from the last command ran.
    std::string out, err;

    std::string testPath = dirname(android::base::GetExecutablePath().c_str());
    std::string simpleBin = testPath + "/../dumpstate_test_fixture/dumpstate_test_fixture";

    void SetDryRun(bool dryRun) {
        ds_.dryRun_ = dryRun;
    }

  private:
    Dumpstate& ds_ = Dumpstate::GetInstance();
};

TEST_F(DumpstateTest, RunCommandNoArgs) {
    EXPECT_EQ(-1, RunCommand("", {}));
}

TEST_F(DumpstateTest, RunCommandNoTitle) {
    EXPECT_EQ(0, RunCommand("", {simpleBin}));
    EXPECT_THAT(out, StrEq("stdout\n"));
    EXPECT_THAT(err, StrEq("stderr\n"));
}

TEST_F(DumpstateTest, RunCommandWithTitle) {
    EXPECT_EQ(0, RunCommand("I AM GROOT", {simpleBin}));
    EXPECT_THAT(err, StrEq("stderr\n"));
    // We don't know the exact duration, so we check the prefix and suffix
    EXPECT_THAT(out, StartsWith("------ I AM GROOT (" + simpleBin + ") ------\nstdout\n------"));
    EXPECT_THAT(out, EndsWith("s was the duration of 'I AM GROOT' ------\n"));
}

TEST_F(DumpstateTest, RunCommandRedirectStderr) {
    EXPECT_EQ(
        0, RunCommand("", {simpleBin}, CommandOptions::WithTimeout(10).RedirectStderr().Build()));
    EXPECT_THAT(out, IsEmpty());
    EXPECT_THAT(err, StrEq("stderr\nstdout\n"));
}

TEST_F(DumpstateTest, RunCommandWithOneArg) {
    EXPECT_EQ(0, RunCommand("", {simpleBin, "one"}));
    EXPECT_THAT(err, IsEmpty());
    EXPECT_THAT(out, StrEq("one\n"));
}

TEST_F(DumpstateTest, RunCommandWithNoArgs) {
    EXPECT_EQ(0, RunCommand("", {simpleBin, "one", "is", "the", "loniest", "number"}));
    EXPECT_THAT(err, IsEmpty());
    EXPECT_THAT(out, StrEq("one is the loniest number\n"));
}

TEST_F(DumpstateTest, RunCommandDryRun) {
    SetDryRun(true);
    EXPECT_EQ(0, RunCommand("I AM GROOT", {simpleBin}));
    // We don't know the exact duration, so we check the prefix and suffix
    EXPECT_THAT(out, StartsWith("------ I AM GROOT (" + simpleBin +
                                ") ------\n\t(skipped on dry run)\n------"));
    EXPECT_THAT(out, EndsWith("s was the duration of 'I AM GROOT' ------\n"));
    EXPECT_THAT(err, IsEmpty());
}

TEST_F(DumpstateTest, RunCommandDryRunNoTitle) {
    SetDryRun(true);
    EXPECT_EQ(0, RunCommand("", {simpleBin}));
    EXPECT_THAT(out, IsEmpty());
    EXPECT_THAT(err, IsEmpty());
}

TEST_F(DumpstateTest, RunCommandDryRunAlways) {
    SetDryRun(true);
    EXPECT_EQ(0, RunCommand("", {simpleBin}, CommandOptions::WithTimeout(10).Always().Build()));
    EXPECT_THAT(out, StrEq("stdout\n"));
    EXPECT_THAT(err, StrEq("stderr\n"));
}

// TODO: add test for other scenarios:
// - AsRoot()
// - DropRoot
// - WithLoggingMessage()
// - command does not exist (invalid path)
// - command times out
// - command exits with a different value
// - command is killed before timed out
// - test progress

// TODO: test DumpFile()
+44 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2016 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.
 */

#include <stdio.h>

/*
 * Binary used to on RunCommand tests.
 *
 * Usage:
 *
 * - It returns 0 unless documented otherwise.
 * - Without any arguments, prints `stdout\n` on `stdout` and `stderr\n` on `stderr`.
 * - With arguments, prints all arguments on `stdout` separated by ` `.
 */
int main(int argc, char* const argv[]) {
    if (argc > 1) {
        for (int i = 1; i < argc; i++) {
            fprintf(stdout, "%s", argv[i]);
            if (i == argc - 1) {
                fprintf(stdout, "\n");
            } else {
                fprintf(stdout, " ");
            }
        }
        return 0;
    }
    fprintf(stdout, "stdout\n");
    fprintf(stderr, "stderr\n");

    return 0;
}
Loading