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

Commit 68fa070d authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Added initial tests for dumpstate."

parents 4308a78a 4c2d6637
Loading
Loading
Loading
Loading
+67 −13
Original line number Original line Diff line number Diff line
LOCAL_PATH:= $(call my-dir)
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)
include $(CLEAR_VARS)


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


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


LOCAL_MODULE := dumpstate
LOCAL_MODULE := dumpstate


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


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


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

LOCAL_CFLAGS += $(COMMON_LOCAL_CFLAGS)

LOCAL_INIT_RC := dumpstate.rc
LOCAL_INIT_RC := dumpstate.rc


include $(BUILD_EXECUTABLE)
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 Original line Diff line number Diff line
@@ -62,7 +62,6 @@ static const char *dump_traces_path = NULL;
static std::string args;
static std::string args;


// TODO: variables below should be part of dumpstate object
// TODO: variables below should be part of dumpstate object
static std::string buildType;
static time_t now;
static time_t now;
static std::unique_ptr<ZipWriter> zip_writer;
static std::unique_ptr<ZipWriter> zip_writer;
static std::set<std::string> mount_points;
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_EXTRA_OPTIONS[] = "dumpstate.options";
static constexpr char PROPERTY_LAST_ID[] = "dumpstate.last_id";
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;
/* 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. */
 * otherwise, gets just those modified in the last half an hour. */
static void get_tombstone_fds(tombstone_data_t data[NUM_TOMBSTONES]) {
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.
// 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];
    char path[PATH_MAX];


    // Gets the the content of the /proc/PID/ns/mnt link, so only unique mount points
    // 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? */
    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;
    return false;
}
}


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


    build = android::base::GetProperty("ro.build.display.id", "(unknown)");
    build = android::base::GetProperty("ro.build.display.id", "(unknown)");
    fingerprint = android::base::GetProperty("ro.build.fingerprint", "(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)");
    radio = android::base::GetProperty("gsm.version.baseband", "(unknown)");
    bootloader = android::base::GetProperty("ro.bootloader", "(unknown)");
    bootloader = android::base::GetProperty("ro.bootloader", "(unknown)");
    network = android::base::GetProperty("gsm.operator.alpha", "(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("Command line: %s\n", strtok(cmdline_buf, "\n"));
    printf("Bugreport format version: %s\n", version.c_str());
    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(),
    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");
    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 */
/* 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;
    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"});
    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");
    DurationReporter durationReporter("DUMPSTATE");
    unsigned long timeout;
    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();
    wake_lock_releaser();
    _exit(EXIT_FAILURE);
    _exit(EXIT_FAILURE);
}
}
@@ -1319,8 +1311,7 @@ int main(int argc, char *argv[]) {
        register_sig_handler();
        register_sig_handler();
    }
    }


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


+8 −3
Original line number Original line 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.
 * that are spread accross utils.cpp and dumpstate.cpp will be moved to it.
 */
 */
class Dumpstate {
class Dumpstate {
    friend class DumpstateTest;

  public:
  public:
    static Dumpstate& GetInstance();
    static Dumpstate& GetInstance();


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


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


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


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

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


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