• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/test/base/chrome_render_view_host_test_harness.h"
22 #include "chrome/test/base/testing_profile.h"
23 #include "content/public/browser/web_contents.h"
24 #include "content/public/test/test_browser_thread.h"
25 #include "extensions/common/dom_action_types.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/login/users/mock_user_manager.h"
31 #include "chrome/browser/chromeos/login/users/user_manager.h"
32 #include "chrome/browser/chromeos/settings/cros_settings.h"
33 #include "chrome/browser/chromeos/settings/device_settings_service.h"
34 #include "chromeos/chromeos_switches.h"
35 #endif
36 
37 using content::BrowserThread;
38 
39 namespace constants = activity_log_constants;
40 
41 namespace extensions {
42 
43 // A dummy implementation of ActivityDatabase::Delegate, sufficient for
44 // the unit tests.
45 class ActivityDatabaseTestPolicy : public ActivityDatabase::Delegate {
46  public:
ActivityDatabaseTestPolicy()47   ActivityDatabaseTestPolicy() {};
48 
49   static const char kTableName[];
50   static const char* kTableContentFields[];
51   static const char* kTableFieldTypes[];
52 
53   virtual void Record(ActivityDatabase* db, scoped_refptr<Action> action);
54 
55  protected:
56   virtual bool InitDatabase(sql::Connection* db) OVERRIDE;
57   virtual bool FlushDatabase(sql::Connection*) OVERRIDE;
OnDatabaseFailure()58   virtual void OnDatabaseFailure() OVERRIDE {}
OnDatabaseClose()59   virtual void OnDatabaseClose() OVERRIDE { delete this; }
60 
61   std::vector<scoped_refptr<Action> > queue_;
62 };
63 
64 const char ActivityDatabaseTestPolicy::kTableName[] = "actions";
65 const char* ActivityDatabaseTestPolicy::kTableContentFields[] = {
66     "extension_id", "time", "action_type", "api_name"};
67 const char* ActivityDatabaseTestPolicy::kTableFieldTypes[] = {
68     "LONGVARCHAR NOT NULL", "INTEGER", "INTEGER", "LONGVARCHAR"};
69 
InitDatabase(sql::Connection * db)70 bool ActivityDatabaseTestPolicy::InitDatabase(sql::Connection* db) {
71   return ActivityDatabase::InitializeTable(db,
72                                            kTableName,
73                                            kTableContentFields,
74                                            kTableFieldTypes,
75                                            arraysize(kTableContentFields));
76 }
77 
FlushDatabase(sql::Connection * db)78 bool ActivityDatabaseTestPolicy::FlushDatabase(sql::Connection* db) {
79   std::string sql_str =
80       "INSERT INTO " + std::string(kTableName) +
81       " (extension_id, time, action_type, api_name) VALUES (?,?,?,?)";
82 
83   std::vector<scoped_refptr<Action> >::size_type i;
84   for (i = 0; i < queue_.size(); i++) {
85     const Action& action = *queue_[i];
86     sql::Statement statement(db->GetCachedStatement(
87         sql::StatementID(SQL_FROM_HERE), sql_str.c_str()));
88     statement.BindString(0, action.extension_id());
89     statement.BindInt64(1, action.time().ToInternalValue());
90     statement.BindInt(2, static_cast<int>(action.action_type()));
91     statement.BindString(3, action.api_name());
92     if (!statement.Run()) {
93       LOG(ERROR) << "Activity log database I/O failed: " << sql_str;
94       return false;
95     }
96   }
97 
98   queue_.clear();
99   return true;
100 }
101 
Record(ActivityDatabase * db,scoped_refptr<Action> action)102 void ActivityDatabaseTestPolicy::Record(ActivityDatabase* db,
103                                         scoped_refptr<Action> action) {
104   queue_.push_back(action);
105   db->AdviseFlush(queue_.size());
106 }
107 
108 class ActivityDatabaseTest : public ChromeRenderViewHostTestHarness {
109  protected:
SetUp()110   virtual void SetUp() OVERRIDE {
111     ChromeRenderViewHostTestHarness::SetUp();
112 #if defined OS_CHROMEOS
113     test_user_manager_.reset(new chromeos::ScopedTestUserManager());
114 #endif
115     CommandLine command_line(CommandLine::NO_PROGRAM);
116     CommandLine::ForCurrentProcess()->AppendSwitch(
117         switches::kEnableExtensionActivityLogTesting);
118   }
119 
TearDown()120   virtual void TearDown() OVERRIDE {
121 #if defined OS_CHROMEOS
122     test_user_manager_.reset();
123 #endif
124     ChromeRenderViewHostTestHarness::TearDown();
125   }
126 
127   // Creates a test database and initializes the table schema.
OpenDatabase(const base::FilePath & db_file)128   ActivityDatabase* OpenDatabase(const base::FilePath& db_file) {
129     db_delegate_ = new ActivityDatabaseTestPolicy();
130     ActivityDatabase* activity_db = new ActivityDatabase(db_delegate_);
131     activity_db->Init(db_file);
132     CHECK(activity_db->is_db_valid());
133     return activity_db;
134   }
135 
CreateAction(const base::Time & time,const std::string & api_name) const136   scoped_refptr<Action> CreateAction(const base::Time& time,
137                                      const std::string& api_name) const {
138     scoped_refptr<Action> action(
139         new Action("punky", time, Action::ACTION_API_CALL, api_name));
140     return action;
141   }
142 
Record(ActivityDatabase * db,scoped_refptr<Action> action)143   void Record(ActivityDatabase* db, scoped_refptr<Action> action) {
144     db_delegate_->Record(db, action);
145   }
146 
CountActions(sql::Connection * db,const std::string & api_name_pattern)147   int CountActions(sql::Connection* db, const std::string& api_name_pattern) {
148     if (!db->DoesTableExist(ActivityDatabaseTestPolicy::kTableName))
149       return -1;
150     std::string sql_str = "SELECT COUNT(*) FROM " +
151                           std::string(ActivityDatabaseTestPolicy::kTableName) +
152                           " WHERE api_name LIKE ?";
153     sql::Statement statement(db->GetCachedStatement(
154         sql::StatementID(SQL_FROM_HERE), 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::ScopedTestDeviceSettingsService test_device_settings_service_;
164   chromeos::ScopedTestCrosSettings test_cros_settings_;
165   scoped_ptr<chromeos::ScopedTestUserManager> test_user_manager_;
166 #endif
167 
168   ActivityDatabaseTestPolicy* db_delegate_;
169 };
170 
171 // Check that the database is initialized properly.
TEST_F(ActivityDatabaseTest,Init)172 TEST_F(ActivityDatabaseTest, Init) {
173   base::ScopedTempDir temp_dir;
174   base::FilePath db_file;
175   ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
176   db_file = temp_dir.path().AppendASCII("ActivityInit.db");
177   base::DeleteFile(db_file, false);
178 
179   ActivityDatabase* activity_db = OpenDatabase(db_file);
180   activity_db->Close();
181 
182   sql::Connection db;
183   ASSERT_TRUE(db.Open(db_file));
184   ASSERT_TRUE(db.DoesTableExist(ActivityDatabaseTestPolicy::kTableName));
185   db.Close();
186 }
187 
188 // Check that actions are recorded in the db.
TEST_F(ActivityDatabaseTest,RecordAction)189 TEST_F(ActivityDatabaseTest, RecordAction) {
190   base::ScopedTempDir temp_dir;
191   base::FilePath db_file;
192   ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
193   db_file = temp_dir.path().AppendASCII("ActivityRecord.db");
194   base::DeleteFile(db_file, false);
195 
196   ActivityDatabase* activity_db = OpenDatabase(db_file);
197   activity_db->SetBatchModeForTesting(false);
198   scoped_refptr<Action> action = CreateAction(base::Time::Now(), "brewster");
199   Record(activity_db, action);
200   activity_db->Close();
201 
202   sql::Connection db;
203   ASSERT_TRUE(db.Open(db_file));
204 
205   ASSERT_EQ(1, CountActions(&db, "brewster"));
206 }
207 
TEST_F(ActivityDatabaseTest,BatchModeOff)208 TEST_F(ActivityDatabaseTest, BatchModeOff) {
209   base::ScopedTempDir temp_dir;
210   base::FilePath db_file;
211   ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
212   db_file = temp_dir.path().AppendASCII("ActivityRecord.db");
213   base::DeleteFile(db_file, false);
214 
215   // Record some actions
216   ActivityDatabase* activity_db = OpenDatabase(db_file);
217   activity_db->SetBatchModeForTesting(false);
218 
219   scoped_refptr<Action> action = CreateAction(base::Time::Now(), "brewster");
220   Record(activity_db, action);
221   ASSERT_EQ(1, CountActions(&activity_db->db_, "brewster"));
222 
223   activity_db->Close();
224 }
225 
TEST_F(ActivityDatabaseTest,BatchModeOn)226 TEST_F(ActivityDatabaseTest, BatchModeOn) {
227   base::ScopedTempDir temp_dir;
228   base::FilePath db_file;
229   ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
230   db_file = temp_dir.path().AppendASCII("ActivityRecord.db");
231   base::DeleteFile(db_file, false);
232 
233   // Record some actions
234   ActivityDatabase* activity_db = OpenDatabase(db_file);
235   activity_db->SetBatchModeForTesting(true);
236   scoped_refptr<Action> action = CreateAction(base::Time::Now(), "brewster");
237   Record(activity_db, action);
238   ASSERT_EQ(0, CountActions(&activity_db->db_, "brewster"));
239 
240   // Artificially trigger and then stop the timer.
241   activity_db->SetTimerForTesting(0);
242   base::MessageLoop::current()->RunUntilIdle();
243   ASSERT_EQ(1, CountActions(&activity_db->db_, "brewster"));
244 
245   activity_db->Close();
246 }
247 
TEST_F(ActivityDatabaseTest,BatchModeFlush)248 TEST_F(ActivityDatabaseTest, BatchModeFlush) {
249   base::ScopedTempDir temp_dir;
250   base::FilePath db_file;
251   ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
252   db_file = temp_dir.path().AppendASCII("ActivityFlush.db");
253   base::DeleteFile(db_file, false);
254 
255   // Record some actions
256   ActivityDatabase* activity_db = OpenDatabase(db_file);
257   activity_db->SetBatchModeForTesting(true);
258   scoped_refptr<Action> action = CreateAction(base::Time::Now(), "brewster");
259   Record(activity_db, action);
260   ASSERT_EQ(0, CountActions(&activity_db->db_, "brewster"));
261 
262   // Request an immediate database flush.
263   activity_db->AdviseFlush(ActivityDatabase::kFlushImmediately);
264   ASSERT_EQ(1, CountActions(&activity_db->db_, "brewster"));
265 
266   activity_db->Close();
267 }
268 
269 // Check that nothing explodes if the DB isn't initialized.
TEST_F(ActivityDatabaseTest,InitFailure)270 TEST_F(ActivityDatabaseTest, InitFailure) {
271   base::ScopedTempDir temp_dir;
272   base::FilePath db_file;
273   ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
274   db_file = temp_dir.path().AppendASCII("ActivityRecord.db");
275   base::DeleteFile(db_file, false);
276 
277   ActivityDatabaseTestPolicy* delegate = new ActivityDatabaseTestPolicy();
278   ActivityDatabase* activity_db = new ActivityDatabase(delegate);
279   scoped_refptr<Action> action = new Action(
280       "punky", base::Time::Now(), Action::ACTION_API_CALL, "brewster");
281   action->mutable_args()->AppendString("woof");
282   delegate->Record(activity_db, action);
283   activity_db->Close();
284 }
285 
286 }  // namespace extensions
287