activity_database_unittest.cc revision a36e5920737c6adbddd3e43b760e5de8431db6e0
1// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include <string> 6 7#include "base/command_line.h" 8#include "base/file_util.h" 9#include "base/files/file_path.h" 10#include "base/files/scoped_temp_dir.h" 11#include "base/run_loop.h" 12#include "base/test/simple_test_clock.h" 13#include "base/time/time.h" 14#include "chrome/browser/extensions/activity_log/activity_action_constants.h" 15#include "chrome/browser/extensions/activity_log/activity_database.h" 16#include "chrome/browser/extensions/activity_log/fullstream_ui_policy.h" 17#include "chrome/browser/extensions/extension_service.h" 18#include "chrome/browser/extensions/test_extension_system.h" 19#include "chrome/common/chrome_constants.h" 20#include "chrome/common/chrome_switches.h" 21#include "chrome/common/extensions/dom_action_types.h" 22#include "chrome/test/base/chrome_render_view_host_test_harness.h" 23#include "chrome/test/base/testing_profile.h" 24#include "content/public/browser/web_contents.h" 25#include "content/public/test/test_browser_thread.h" 26#include "sql/statement.h" 27#include "testing/gtest/include/gtest/gtest.h" 28 29#if defined(OS_CHROMEOS) 30#include "chrome/browser/chromeos/cros/network_library.h" 31#include "chrome/browser/chromeos/login/mock_user_manager.h" 32#include "chrome/browser/chromeos/login/user_manager.h" 33#include "chrome/browser/chromeos/settings/cros_settings.h" 34#include "chrome/browser/chromeos/settings/device_settings_service.h" 35#include "chromeos/chromeos_switches.h" 36#endif 37 38using content::BrowserThread; 39 40namespace constants = activity_log_constants; 41 42namespace extensions { 43 44// A dummy implementation of ActivityDatabase::Delegate, sufficient for 45// the unit tests. 46class ActivityDatabaseTestPolicy : public ActivityDatabase::Delegate { 47 public: 48 ActivityDatabaseTestPolicy() {}; 49 50 static const char kTableName[]; 51 static const char* kTableContentFields[]; 52 static const char* kTableFieldTypes[]; 53 54 virtual void Record(ActivityDatabase* db, scoped_refptr<Action> action); 55 56 protected: 57 virtual bool InitDatabase(sql::Connection* db) OVERRIDE; 58 virtual bool FlushDatabase(sql::Connection*) OVERRIDE; 59 virtual void OnDatabaseFailure() OVERRIDE {} 60 virtual void OnDatabaseClose() OVERRIDE { delete this; } 61 62 std::vector<scoped_refptr<Action> > queue_; 63}; 64 65const char ActivityDatabaseTestPolicy::kTableName[] = "actions"; 66const char* ActivityDatabaseTestPolicy::kTableContentFields[] = { 67 "extension_id", "time", "action_type", "api_name"}; 68const char* ActivityDatabaseTestPolicy::kTableFieldTypes[] = { 69 "LONGVARCHAR NOT NULL", "INTEGER", "INTEGER", "LONGVARCHAR"}; 70 71bool ActivityDatabaseTestPolicy::InitDatabase(sql::Connection* db) { 72 return ActivityDatabase::InitializeTable(db, 73 kTableName, 74 kTableContentFields, 75 kTableFieldTypes, 76 arraysize(kTableContentFields)); 77} 78 79bool ActivityDatabaseTestPolicy::FlushDatabase(sql::Connection* db) { 80 std::string sql_str = 81 "INSERT INTO " + std::string(kTableName) + 82 " (extension_id, time, action_type, api_name) VALUES (?,?,?,?)"; 83 84 std::vector<scoped_refptr<Action> >::size_type i; 85 for (i = 0; i < queue_.size(); i++) { 86 const Action& action = *queue_[i]; 87 sql::Statement statement(db->GetCachedStatement( 88 sql::StatementID(SQL_FROM_HERE), sql_str.c_str())); 89 statement.BindString(0, action.extension_id()); 90 statement.BindInt64(1, action.time().ToInternalValue()); 91 statement.BindInt(2, static_cast<int>(action.action_type())); 92 statement.BindString(3, action.api_name()); 93 if (!statement.Run()) { 94 LOG(ERROR) << "Activity log database I/O failed: " << sql_str; 95 return false; 96 } 97 } 98 99 queue_.clear(); 100 return true; 101} 102 103void ActivityDatabaseTestPolicy::Record(ActivityDatabase* db, 104 scoped_refptr<Action> action) { 105 queue_.push_back(action); 106 db->NotifyAction(); 107} 108 109class ActivityDatabaseTest : public ChromeRenderViewHostTestHarness { 110 protected: 111 virtual void SetUp() OVERRIDE { 112 ChromeRenderViewHostTestHarness::SetUp(); 113#if defined OS_CHROMEOS 114 test_user_manager_.reset(new chromeos::ScopedTestUserManager()); 115#endif 116 CommandLine command_line(CommandLine::NO_PROGRAM); 117 CommandLine::ForCurrentProcess()->AppendSwitch( 118 switches::kEnableExtensionActivityLogTesting); 119 } 120 121 virtual void TearDown() OVERRIDE { 122#if defined OS_CHROMEOS 123 test_user_manager_.reset(); 124#endif 125 ChromeRenderViewHostTestHarness::TearDown(); 126 } 127 128 // Creates a test database and initializes the table schema. 129 ActivityDatabase* OpenDatabase(const base::FilePath& db_file) { 130 db_delegate_ = new ActivityDatabaseTestPolicy(); 131 ActivityDatabase* activity_db = new ActivityDatabase(db_delegate_); 132 activity_db->Init(db_file); 133 CHECK(activity_db->is_db_valid()); 134 return activity_db; 135 } 136 137 scoped_refptr<Action> CreateAction(const base::Time& time, 138 const std::string& api_name) const { 139 scoped_refptr<Action> action( 140 new Action("punky", time, Action::ACTION_API_CALL, api_name)); 141 return action; 142 } 143 144 void Record(ActivityDatabase* db, scoped_refptr<Action> action) { 145 db_delegate_->Record(db, action); 146 } 147 148 int CountActions(sql::Connection* db, const std::string& api_name_pattern) { 149 if (!db->DoesTableExist(ActivityDatabaseTestPolicy::kTableName)) 150 return -1; 151 std::string sql_str = "SELECT COUNT(*) FROM " + 152 std::string(ActivityDatabaseTestPolicy::kTableName) + 153 " WHERE api_name LIKE ?"; 154 sql::Statement statement(db->GetUniqueStatement(sql_str.c_str())); 155 statement.BindString(0, api_name_pattern); 156 if (!statement.Step()) 157 return -1; 158 return statement.ColumnInt(0); 159 } 160 161 private: 162#if defined OS_CHROMEOS 163 chromeos::ScopedStubNetworkLibraryEnabler stub_network_library_enabler_; 164 chromeos::ScopedTestDeviceSettingsService test_device_settings_service_; 165 chromeos::ScopedTestCrosSettings test_cros_settings_; 166 scoped_ptr<chromeos::ScopedTestUserManager> test_user_manager_; 167#endif 168 169 ActivityDatabaseTestPolicy* db_delegate_; 170}; 171 172// Check that the database is initialized properly. 173TEST_F(ActivityDatabaseTest, Init) { 174 base::ScopedTempDir temp_dir; 175 base::FilePath db_file; 176 ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); 177 db_file = temp_dir.path().AppendASCII("ActivityInit.db"); 178 base::DeleteFile(db_file, false); 179 180 ActivityDatabase* activity_db = OpenDatabase(db_file); 181 activity_db->Close(); 182 183 sql::Connection db; 184 ASSERT_TRUE(db.Open(db_file)); 185 ASSERT_TRUE(db.DoesTableExist(ActivityDatabaseTestPolicy::kTableName)); 186 db.Close(); 187} 188 189// Check that actions are recorded in the db. 190TEST_F(ActivityDatabaseTest, RecordAction) { 191 base::ScopedTempDir temp_dir; 192 base::FilePath db_file; 193 ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); 194 db_file = temp_dir.path().AppendASCII("ActivityRecord.db"); 195 base::DeleteFile(db_file, false); 196 197 ActivityDatabase* activity_db = OpenDatabase(db_file); 198 activity_db->SetBatchModeForTesting(false); 199 scoped_refptr<Action> action = CreateAction(base::Time::Now(), "brewster"); 200 Record(activity_db, action); 201 activity_db->Close(); 202 203 sql::Connection db; 204 ASSERT_TRUE(db.Open(db_file)); 205 206 ASSERT_EQ(1, CountActions(&db, "brewster")); 207} 208 209TEST_F(ActivityDatabaseTest, BatchModeOff) { 210 base::ScopedTempDir temp_dir; 211 base::FilePath db_file; 212 ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); 213 db_file = temp_dir.path().AppendASCII("ActivityRecord.db"); 214 base::DeleteFile(db_file, false); 215 216 // Record some actions 217 ActivityDatabase* activity_db = OpenDatabase(db_file); 218 activity_db->SetBatchModeForTesting(false); 219 220 scoped_refptr<Action> action = CreateAction(base::Time::Now(), "brewster"); 221 Record(activity_db, action); 222 ASSERT_EQ(1, CountActions(&activity_db->db_, "brewster")); 223 224 activity_db->Close(); 225} 226 227TEST_F(ActivityDatabaseTest, BatchModeOn) { 228 base::ScopedTempDir temp_dir; 229 base::FilePath db_file; 230 ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); 231 db_file = temp_dir.path().AppendASCII("ActivityRecord.db"); 232 base::DeleteFile(db_file, false); 233 234 // Record some actions 235 ActivityDatabase* activity_db = OpenDatabase(db_file); 236 activity_db->SetBatchModeForTesting(true); 237 scoped_refptr<Action> action = CreateAction(base::Time::Now(), "brewster"); 238 Record(activity_db, action); 239 ASSERT_EQ(0, CountActions(&activity_db->db_, "brewster")); 240 241 // Artificially trigger and then stop the timer. 242 activity_db->SetTimerForTesting(0); 243 base::MessageLoop::current()->RunUntilIdle(); 244 ASSERT_EQ(1, CountActions(&activity_db->db_, "brewster")); 245 246 activity_db->Close(); 247} 248 249// Check that nothing explodes if the DB isn't initialized. 250TEST_F(ActivityDatabaseTest, InitFailure) { 251 base::ScopedTempDir temp_dir; 252 base::FilePath db_file; 253 ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); 254 db_file = temp_dir.path().AppendASCII("ActivityRecord.db"); 255 base::DeleteFile(db_file, false); 256 257 ActivityDatabaseTestPolicy* delegate = new ActivityDatabaseTestPolicy(); 258 ActivityDatabase* activity_db = new ActivityDatabase(delegate); 259 scoped_refptr<Action> action = new Action( 260 "punky", base::Time::Now(), Action::ACTION_API_CALL, "brewster"); 261 action->mutable_args()->AppendString("woof"); 262 delegate->Record(activity_db, action); 263 activity_db->Close(); 264} 265 266} // namespace extensions 267