|  | /* | 
|  | * 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. |