blob: 5474f482b76bed54bdc4a1ee9379141e927e8b44 [file] [log] [blame]
/*
* Copyright (C) 2012 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 <base/files/file_enumerator.h>
#include <base/files/file_util.h>
#include <base/files/scoped_temp_dir.h>
#include <base/strings/stringprintf.h>
#include <brillo/syslog_logging.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "udev_collector.h"
using base::FilePath;
namespace {
// Dummy log config file name.
const char kLogConfigFileName[] = "log_config_file";
// Dummy directory for storing device coredumps.
const char kDevCoredumpDirectory[] = "devcoredump";
// A bunch of random rules to put into the dummy log config file.
const char kLogConfigFileContents[] =
"crash_reporter-udev-collection-change-card0-drm=echo change card0 drm\n"
"crash_reporter-udev-collection-add-state0-cpu=echo change state0 cpu\n"
"crash_reporter-udev-collection-devcoredump-iwlwifi=echo devcoredump\n"
"cros_installer=echo not for udev";
const char kCrashLogFilePattern[] = "*.log.gz";
const char kDevCoredumpFilePattern[] = "*.devcore";
// Dummy content for device coredump data file.
const char kDevCoredumpDataContents[] = "coredump";
// Content for failing device's uevent file.
const char kFailingDeviceUeventContents[] = "DRIVER=iwlwifi\n";
void CountCrash() {}
bool s_consent_given = true;
bool IsMetrics() {
return s_consent_given;
}
// Returns the number of files found in the given path that matches the
// specified file name pattern.
int GetNumFiles(const FilePath& path, const std::string& file_pattern) {
base::FileEnumerator enumerator(path, false, base::FileEnumerator::FILES,
file_pattern);
int num_files = 0;
for (FilePath file_path = enumerator.Next();
!file_path.value().empty();
file_path = enumerator.Next()) {
num_files++;
}
return num_files;
}
} // namespace
class UdevCollectorMock : public UdevCollector {
public:
MOCK_METHOD0(SetUpDBus, void());
};
class UdevCollectorTest : public ::testing::Test {
protected:
base::ScopedTempDir temp_dir_generator_;
void HandleCrash(const std::string &udev_event) {
collector_.HandleCrash(udev_event);
}
void GenerateDevCoredump(const std::string& device_name) {
// Generate coredump data file.
ASSERT_TRUE(CreateDirectory(
FilePath(base::StringPrintf("%s/%s",
collector_.dev_coredump_directory_.c_str(),
device_name.c_str()))));
FilePath data_path =
FilePath(base::StringPrintf("%s/%s/data",
collector_.dev_coredump_directory_.c_str(),
device_name.c_str()));
ASSERT_EQ(strlen(kDevCoredumpDataContents),
base::WriteFile(data_path,
kDevCoredumpDataContents,
strlen(kDevCoredumpDataContents)));
// Generate uevent file for failing device.
ASSERT_TRUE(CreateDirectory(
FilePath(base::StringPrintf("%s/%s/failing_device",
collector_.dev_coredump_directory_.c_str(),
device_name.c_str()))));
FilePath uevent_path =
FilePath(base::StringPrintf("%s/%s/failing_device/uevent",
collector_.dev_coredump_directory_.c_str(),
device_name.c_str()));
ASSERT_EQ(strlen(kFailingDeviceUeventContents),
base::WriteFile(uevent_path,
kFailingDeviceUeventContents,
strlen(kFailingDeviceUeventContents)));
}
private:
void SetUp() override {
s_consent_given = true;
EXPECT_CALL(collector_, SetUpDBus()).WillRepeatedly(testing::Return());
collector_.Initialize(CountCrash, IsMetrics);
ASSERT_TRUE(temp_dir_generator_.CreateUniqueTempDir());
FilePath log_config_path =
temp_dir_generator_.path().Append(kLogConfigFileName);
collector_.log_config_path_ = log_config_path;
collector_.ForceCrashDirectory(temp_dir_generator_.path());
FilePath dev_coredump_path =
temp_dir_generator_.path().Append(kDevCoredumpDirectory);
collector_.dev_coredump_directory_ = dev_coredump_path.value();
// Write to a dummy log config file.
ASSERT_EQ(strlen(kLogConfigFileContents),
base::WriteFile(log_config_path,
kLogConfigFileContents,
strlen(kLogConfigFileContents)));
brillo::ClearLog();
}
UdevCollectorMock collector_;
};
TEST_F(UdevCollectorTest, TestNoConsent) {
s_consent_given = false;
HandleCrash("ACTION=change:KERNEL=card0:SUBSYSTEM=drm");
EXPECT_EQ(0, GetNumFiles(temp_dir_generator_.path(), kCrashLogFilePattern));
}
TEST_F(UdevCollectorTest, TestNoMatch) {
// No rule should match this.
HandleCrash("ACTION=change:KERNEL=foo:SUBSYSTEM=bar");
EXPECT_EQ(0, GetNumFiles(temp_dir_generator_.path(), kCrashLogFilePattern));
}
TEST_F(UdevCollectorTest, TestMatches) {
// Try multiple udev events in sequence. The number of log files generated
// should increase.
HandleCrash("ACTION=change:KERNEL=card0:SUBSYSTEM=drm");
EXPECT_EQ(1, GetNumFiles(temp_dir_generator_.path(), kCrashLogFilePattern));
HandleCrash("ACTION=add:KERNEL=state0:SUBSYSTEM=cpu");
EXPECT_EQ(2, GetNumFiles(temp_dir_generator_.path(), kCrashLogFilePattern));
}
TEST_F(UdevCollectorTest, TestDevCoredump) {
GenerateDevCoredump("devcd0");
HandleCrash("ACTION=add:KERNEL_NUMBER=0:SUBSYSTEM=devcoredump");
EXPECT_EQ(1, GetNumFiles(temp_dir_generator_.path(),
kDevCoredumpFilePattern));
GenerateDevCoredump("devcd1");
HandleCrash("ACTION=add:KERNEL_NUMBER=1:SUBSYSTEM=devcoredump");
EXPECT_EQ(2, GetNumFiles(temp_dir_generator_.path(),
kDevCoredumpFilePattern));
}
// TODO(sque, crosbug.com/32238) - test wildcard cases, multiple identical udev
// events.