device_status_collector_browsertest.cc revision 2a99a7e74a7f215066514fe81d2bfa6639d9eddd
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 "chrome/browser/chromeos/policy/device_status_collector.h" 6 7#include "base/environment.h" 8#include "base/logging.h" 9#include "base/memory/scoped_ptr.h" 10#include "base/message_loop.h" 11#include "base/prefs/pref_service.h" 12#include "base/prefs/testing_pref_service.h" 13#include "base/threading/sequenced_worker_pool.h" 14#include "chrome/browser/chromeos/settings/cros_settings.h" 15#include "chrome/browser/chromeos/settings/cros_settings_names.h" 16#include "chrome/browser/chromeos/settings/cros_settings_provider.h" 17#include "chrome/browser/chromeos/settings/stub_cros_settings_provider.h" 18#include "chrome/browser/chromeos/system/mock_statistics_provider.h" 19#include "chrome/browser/chromeos/system/statistics_provider.h" 20#include "chrome/browser/policy/cloud/proto/device_management_backend.pb.h" 21#include "chrome/common/pref_names.h" 22#include "content/public/browser/browser_thread.h" 23#include "content/public/test/test_browser_thread.h" 24#include "testing/gmock/include/gmock/gmock.h" 25#include "testing/gtest/include/gtest/gtest.h" 26 27using ::testing::DoAll; 28using ::testing::NotNull; 29using ::testing::Return; 30using ::testing::SetArgPointee; 31using ::testing::_; 32using base::Time; 33using base::TimeDelta; 34 35namespace em = enterprise_management; 36 37namespace { 38 39const int64 kMillisecondsPerDay = Time::kMicrosecondsPerDay / 1000; 40 41scoped_ptr<content::Geoposition> mock_position_to_return_next; 42 43void SetMockPositionToReturnNext(const content::Geoposition &position) { 44 mock_position_to_return_next.reset(new content::Geoposition(position)); 45} 46 47void MockPositionUpdateRequester( 48 const content::GeolocationUpdateCallback& callback) { 49 if (!mock_position_to_return_next.get()) 50 return; 51 52 // If the fix is invalid, the DeviceStatusCollector will immediately request 53 // another update when it receives the callback. This is desirable and safe in 54 // real life where geolocation updates arrive asynchronously. In this testing 55 // harness, the callback is invoked synchronously upon request, leading to a 56 // request-callback loop. The loop is broken by returning the mock position 57 // only once. 58 scoped_ptr<content::Geoposition> position( 59 mock_position_to_return_next.release()); 60 callback.Run(*position); 61} 62 63class TestingDeviceStatusCollector : public policy::DeviceStatusCollector { 64 public: 65 TestingDeviceStatusCollector( 66 PrefService* local_state, 67 chromeos::system::StatisticsProvider* provider) 68 : policy::DeviceStatusCollector(local_state, 69 provider, 70 &MockPositionUpdateRequester) { 71 // Set the baseline time to a fixed value (1 AM) to prevent test flakiness 72 // due to a single activity period spanning two days. 73 SetBaselineTime(Time::Now().LocalMidnight() + TimeDelta::FromHours(1)); 74 } 75 76 void Simulate(IdleState* states, int len) { 77 for (int i = 0; i < len; i++) 78 IdleStateCallback(states[i]); 79 } 80 81 void set_max_stored_past_activity_days(unsigned int value) { 82 max_stored_past_activity_days_ = value; 83 } 84 85 void set_max_stored_future_activity_days(unsigned int value) { 86 max_stored_future_activity_days_ = value; 87 } 88 89 // Reset the baseline time. 90 void SetBaselineTime(Time time) { 91 baseline_time_ = time; 92 baseline_offset_periods_ = 0; 93 } 94 95 protected: 96 virtual void CheckIdleState() OVERRIDE { 97 // This should never be called in testing, as it results in a dbus call. 98 ADD_FAILURE(); 99 } 100 101 // Each time this is called, returns a time that is a fixed increment 102 // later than the previous time. 103 virtual Time GetCurrentTime() OVERRIDE { 104 int poll_interval = policy::DeviceStatusCollector::kIdlePollIntervalSeconds; 105 return baseline_time_ + 106 TimeDelta::FromSeconds(poll_interval * baseline_offset_periods_++); 107 } 108 109 private: 110 // Baseline time for the fake times returned from GetCurrentTime(). 111 Time baseline_time_; 112 113 // The number of simulated periods since the baseline time. 114 int baseline_offset_periods_; 115}; 116 117// Return the total number of active milliseconds contained in a device 118// status report. 119int64 GetActiveMilliseconds(em::DeviceStatusReportRequest& status) { 120 int64 active_milliseconds = 0; 121 for (int i = 0; i < status.active_period_size(); i++) { 122 active_milliseconds += status.active_period(i).active_duration(); 123 } 124 return active_milliseconds; 125} 126 127} // namespace 128 129namespace policy { 130 131// Though it is a unit test, this test is linked with browser_tests so that it 132// runs in a separate process. The intention is to avoid overriding the timezone 133// environment variable for other tests. 134class DeviceStatusCollectorTest : public testing::Test { 135 public: 136 DeviceStatusCollectorTest() 137 : message_loop_(MessageLoop::TYPE_UI), 138 ui_thread_(content::BrowserThread::UI, &message_loop_), 139 file_thread_(content::BrowserThread::FILE, &message_loop_), 140 io_thread_(content::BrowserThread::IO, &message_loop_) { 141 // Run this test with a well-known timezone so that Time::LocalMidnight() 142 // returns the same values on all machines. 143 scoped_ptr<base::Environment> env(base::Environment::Create()); 144 env->SetVar("TZ", "UTC"); 145 146 TestingDeviceStatusCollector::RegisterPrefs(prefs_.registry()); 147 148 EXPECT_CALL(statistics_provider_, GetMachineStatistic(_, NotNull())) 149 .WillRepeatedly(Return(false)); 150 151 // Remove the real DeviceSettingsProvider and replace it with a stub. 152 cros_settings_ = chromeos::CrosSettings::Get(); 153 device_settings_provider_ = 154 cros_settings_->GetProvider(chromeos::kReportDeviceVersionInfo); 155 EXPECT_TRUE(device_settings_provider_ != NULL); 156 EXPECT_TRUE( 157 cros_settings_->RemoveSettingsProvider(device_settings_provider_)); 158 cros_settings_->AddSettingsProvider(&stub_settings_provider_); 159 160 RestartStatusCollector(); 161 } 162 163 virtual ~DeviceStatusCollectorTest() { 164 // Finish pending tasks. 165 content::BrowserThread::GetBlockingPool()->FlushForTesting(); 166 message_loop_.RunUntilIdle(); 167 168 // Restore the real DeviceSettingsProvider. 169 EXPECT_TRUE( 170 cros_settings_->RemoveSettingsProvider(&stub_settings_provider_)); 171 cros_settings_->AddSettingsProvider(device_settings_provider_); 172 } 173 174 void RestartStatusCollector() { 175 status_collector_.reset( 176 new TestingDeviceStatusCollector(&prefs_, &statistics_provider_)); 177 } 178 179 void GetStatus() { 180 status_.Clear(); 181 status_collector_->GetDeviceStatus(&status_); 182 } 183 184 void CheckThatNoLocationIsReported() { 185 GetStatus(); 186 EXPECT_FALSE(status_.has_device_location()); 187 } 188 189 void CheckThatAValidLocationIsReported() { 190 // Checks that a location is being reported which matches the valid fix 191 // set using SetMockPositionToReturnNext(). 192 GetStatus(); 193 EXPECT_TRUE(status_.has_device_location()); 194 em::DeviceLocation location = status_.device_location(); 195 if (location.has_error_code()) 196 EXPECT_EQ(em::DeviceLocation::ERROR_CODE_NONE, location.error_code()); 197 EXPECT_TRUE(location.has_latitude()); 198 EXPECT_TRUE(location.has_longitude()); 199 EXPECT_TRUE(location.has_accuracy()); 200 EXPECT_TRUE(location.has_timestamp()); 201 EXPECT_FALSE(location.has_altitude()); 202 EXPECT_FALSE(location.has_altitude_accuracy()); 203 EXPECT_FALSE(location.has_heading()); 204 EXPECT_FALSE(location.has_speed()); 205 EXPECT_FALSE(location.has_error_message()); 206 EXPECT_DOUBLE_EQ(4.3, location.latitude()); 207 EXPECT_DOUBLE_EQ(-7.8, location.longitude()); 208 EXPECT_DOUBLE_EQ(3., location.accuracy()); 209 // Check that the timestamp is not older than ten minutes. 210 EXPECT_TRUE(Time::Now() - Time::FromDoubleT(location.timestamp() / 1000.) < 211 TimeDelta::FromMinutes(10)); 212 } 213 214 void CheckThatALocationErrorIsReported() { 215 GetStatus(); 216 EXPECT_TRUE(status_.has_device_location()); 217 em::DeviceLocation location = status_.device_location(); 218 EXPECT_TRUE(location.has_error_code()); 219 EXPECT_EQ(em::DeviceLocation::ERROR_CODE_POSITION_UNAVAILABLE, 220 location.error_code()); 221 } 222 223 protected: 224 // Convenience method. 225 int64 ActivePeriodMilliseconds() { 226 return policy::DeviceStatusCollector::kIdlePollIntervalSeconds * 1000; 227 } 228 229 MessageLoop message_loop_; 230 content::TestBrowserThread ui_thread_; 231 content::TestBrowserThread file_thread_; 232 content::TestBrowserThread io_thread_; 233 234 TestingPrefServiceSimple prefs_; 235 chromeos::system::MockStatisticsProvider statistics_provider_; 236 scoped_ptr<TestingDeviceStatusCollector> status_collector_; 237 em::DeviceStatusReportRequest status_; 238 chromeos::CrosSettings* cros_settings_; 239 chromeos::CrosSettingsProvider* device_settings_provider_; 240 chromeos::StubCrosSettingsProvider stub_settings_provider_; 241}; 242 243TEST_F(DeviceStatusCollectorTest, AllIdle) { 244 IdleState test_states[] = { 245 IDLE_STATE_IDLE, 246 IDLE_STATE_IDLE, 247 IDLE_STATE_IDLE 248 }; 249 cros_settings_->SetBoolean(chromeos::kReportDeviceActivityTimes, true); 250 251 // Test reporting with no data. 252 GetStatus(); 253 EXPECT_EQ(0, status_.active_period_size()); 254 EXPECT_EQ(0, GetActiveMilliseconds(status_)); 255 256 // Test reporting with a single idle sample. 257 status_collector_->Simulate(test_states, 1); 258 GetStatus(); 259 EXPECT_EQ(0, status_.active_period_size()); 260 EXPECT_EQ(0, GetActiveMilliseconds(status_)); 261 262 // Test reporting with multiple consecutive idle samples. 263 status_collector_->Simulate(test_states, 264 sizeof(test_states) / sizeof(IdleState)); 265 GetStatus(); 266 EXPECT_EQ(0, status_.active_period_size()); 267 EXPECT_EQ(0, GetActiveMilliseconds(status_)); 268} 269 270TEST_F(DeviceStatusCollectorTest, AllActive) { 271 IdleState test_states[] = { 272 IDLE_STATE_ACTIVE, 273 IDLE_STATE_ACTIVE, 274 IDLE_STATE_ACTIVE 275 }; 276 cros_settings_->SetBoolean(chromeos::kReportDeviceActivityTimes, true); 277 278 // Test a single active sample. 279 status_collector_->Simulate(test_states, 1); 280 GetStatus(); 281 EXPECT_EQ(1, status_.active_period_size()); 282 EXPECT_EQ(1 * ActivePeriodMilliseconds(), GetActiveMilliseconds(status_)); 283 status_.clear_active_period(); // Clear the result protobuf. 284 285 // Test multiple consecutive active samples. 286 status_collector_->Simulate(test_states, 287 sizeof(test_states) / sizeof(IdleState)); 288 GetStatus(); 289 EXPECT_EQ(1, status_.active_period_size()); 290 EXPECT_EQ(4 * ActivePeriodMilliseconds(), GetActiveMilliseconds(status_)); 291} 292 293TEST_F(DeviceStatusCollectorTest, MixedStates) { 294 IdleState test_states[] = { 295 IDLE_STATE_ACTIVE, 296 IDLE_STATE_IDLE, 297 IDLE_STATE_ACTIVE, 298 IDLE_STATE_ACTIVE, 299 IDLE_STATE_IDLE, 300 IDLE_STATE_IDLE, 301 IDLE_STATE_ACTIVE 302 }; 303 cros_settings_->SetBoolean(chromeos::kReportDeviceActivityTimes, true); 304 status_collector_->Simulate(test_states, 305 sizeof(test_states) / sizeof(IdleState)); 306 GetStatus(); 307 EXPECT_EQ(4 * ActivePeriodMilliseconds(), GetActiveMilliseconds(status_)); 308} 309 310TEST_F(DeviceStatusCollectorTest, StateKeptInPref) { 311 IdleState test_states[] = { 312 IDLE_STATE_ACTIVE, 313 IDLE_STATE_IDLE, 314 IDLE_STATE_ACTIVE, 315 IDLE_STATE_ACTIVE, 316 IDLE_STATE_IDLE, 317 IDLE_STATE_IDLE 318 }; 319 cros_settings_->SetBoolean(chromeos::kReportDeviceActivityTimes, true); 320 status_collector_->Simulate(test_states, 321 sizeof(test_states) / sizeof(IdleState)); 322 323 // Process the list a second time after restarting the collector. It should be 324 // able to count the active periods found by the original collector, because 325 // the results are stored in a pref. 326 RestartStatusCollector(); 327 status_collector_->Simulate(test_states, 328 sizeof(test_states) / sizeof(IdleState)); 329 330 GetStatus(); 331 EXPECT_EQ(6 * ActivePeriodMilliseconds(), GetActiveMilliseconds(status_)); 332} 333 334TEST_F(DeviceStatusCollectorTest, Times) { 335 IdleState test_states[] = { 336 IDLE_STATE_ACTIVE, 337 IDLE_STATE_IDLE, 338 IDLE_STATE_ACTIVE, 339 IDLE_STATE_ACTIVE, 340 IDLE_STATE_IDLE, 341 IDLE_STATE_IDLE 342 }; 343 cros_settings_->SetBoolean(chromeos::kReportDeviceActivityTimes, true); 344 status_collector_->Simulate(test_states, 345 sizeof(test_states) / sizeof(IdleState)); 346 GetStatus(); 347 EXPECT_EQ(3 * ActivePeriodMilliseconds(), GetActiveMilliseconds(status_)); 348} 349 350TEST_F(DeviceStatusCollectorTest, MaxStoredPeriods) { 351 IdleState test_states[] = { 352 IDLE_STATE_ACTIVE, 353 IDLE_STATE_IDLE 354 }; 355 const int kMaxDays = 10; 356 357 cros_settings_->SetBoolean(chromeos::kReportDeviceActivityTimes, true); 358 status_collector_->set_max_stored_past_activity_days(kMaxDays - 1); 359 status_collector_->set_max_stored_future_activity_days(1); 360 Time baseline = Time::Now().LocalMidnight(); 361 362 // Simulate 12 active periods. 363 for (int i = 0; i < kMaxDays + 2; i++) { 364 status_collector_->Simulate(test_states, 365 sizeof(test_states) / sizeof(IdleState)); 366 // Advance the simulated clock by a day. 367 baseline += TimeDelta::FromDays(1); 368 status_collector_->SetBaselineTime(baseline); 369 } 370 371 // Check that we don't exceed the max number of periods. 372 GetStatus(); 373 EXPECT_EQ(kMaxDays - 1, status_.active_period_size()); 374 375 // Simulate some future times. 376 for (int i = 0; i < kMaxDays + 2; i++) { 377 status_collector_->Simulate(test_states, 378 sizeof(test_states) / sizeof(IdleState)); 379 // Advance the simulated clock by a day. 380 baseline += TimeDelta::FromDays(1); 381 status_collector_->SetBaselineTime(baseline); 382 } 383 // Set the clock back so the previous simulated times are in the future. 384 baseline -= TimeDelta::FromDays(20); 385 status_collector_->SetBaselineTime(baseline); 386 387 // Collect one more data point to trigger pruning. 388 status_collector_->Simulate(test_states, 1); 389 390 // Check that we don't exceed the max number of periods. 391 status_.clear_active_period(); 392 GetStatus(); 393 EXPECT_LT(status_.active_period_size(), kMaxDays); 394} 395 396TEST_F(DeviceStatusCollectorTest, ActivityTimesDisabledByDefault) { 397 // If the pref for collecting device activity times isn't explicitly turned 398 // on, no data on activity times should be reported. 399 400 IdleState test_states[] = { 401 IDLE_STATE_ACTIVE, 402 IDLE_STATE_ACTIVE, 403 IDLE_STATE_ACTIVE 404 }; 405 status_collector_->Simulate(test_states, 406 sizeof(test_states) / sizeof(IdleState)); 407 GetStatus(); 408 EXPECT_EQ(0, status_.active_period_size()); 409 EXPECT_EQ(0, GetActiveMilliseconds(status_)); 410} 411 412TEST_F(DeviceStatusCollectorTest, ActivityCrossingMidnight) { 413 IdleState test_states[] = { 414 IDLE_STATE_ACTIVE 415 }; 416 cros_settings_->SetBoolean(chromeos::kReportDeviceActivityTimes, true); 417 418 // Set the baseline time to 10 seconds after midnight. 419 status_collector_->SetBaselineTime( 420 Time::Now().LocalMidnight() + TimeDelta::FromSeconds(10)); 421 422 status_collector_->Simulate(test_states, 1); 423 GetStatus(); 424 ASSERT_EQ(2, status_.active_period_size()); 425 426 em::ActiveTimePeriod period0 = status_.active_period(0); 427 em::ActiveTimePeriod period1 = status_.active_period(1); 428 EXPECT_EQ(ActivePeriodMilliseconds() - 10000, period0.active_duration()); 429 EXPECT_EQ(10000, period1.active_duration()); 430 431 em::TimePeriod time_period0 = period0.time_period(); 432 em::TimePeriod time_period1 = period1.time_period(); 433 434 EXPECT_EQ(time_period0.end_timestamp(), time_period1.start_timestamp()); 435 436 // Ensure that the start and end times for the period are a day apart. 437 EXPECT_EQ(time_period0.end_timestamp() - time_period0.start_timestamp(), 438 kMillisecondsPerDay); 439 EXPECT_EQ(time_period1.end_timestamp() - time_period1.start_timestamp(), 440 kMillisecondsPerDay); 441} 442 443TEST_F(DeviceStatusCollectorTest, ActivityTimesKeptUntilSubmittedSuccessfully) { 444 IdleState test_states[] = { 445 IDLE_STATE_ACTIVE, 446 IDLE_STATE_ACTIVE, 447 }; 448 cros_settings_->SetBoolean(chromeos::kReportDeviceActivityTimes, true); 449 450 status_collector_->Simulate(test_states, 2); 451 GetStatus(); 452 EXPECT_EQ(2 * ActivePeriodMilliseconds(), GetActiveMilliseconds(status_)); 453 em::DeviceStatusReportRequest first_status(status_); 454 455 // The collector returns the same status again. 456 GetStatus(); 457 EXPECT_EQ(first_status.SerializeAsString(), status_.SerializeAsString()); 458 459 // After indicating a successful submit, the submitted status gets cleared, 460 // but what got collected meanwhile sticks around. 461 status_collector_->Simulate(test_states, 1); 462 status_collector_->OnSubmittedSuccessfully(); 463 GetStatus(); 464 EXPECT_EQ(ActivePeriodMilliseconds(), GetActiveMilliseconds(status_)); 465} 466 467TEST_F(DeviceStatusCollectorTest, DevSwitchBootMode) { 468 // Test that boot mode data is not reported if the pref is not turned on. 469 EXPECT_CALL(statistics_provider_, 470 GetMachineStatistic("devsw_boot", NotNull())) 471 .WillRepeatedly(DoAll(SetArgPointee<1>("0"), Return(true))); 472 GetStatus(); 473 EXPECT_FALSE(status_.has_boot_mode()); 474 475 // Turn the pref on, and check that the status is reported iff the 476 // statistics provider returns valid data. 477 cros_settings_->SetBoolean(chromeos::kReportDeviceBootMode, true); 478 479 EXPECT_CALL(statistics_provider_, 480 GetMachineStatistic("devsw_boot", NotNull())) 481 .WillOnce(DoAll(SetArgPointee<1>("(error)"), Return(true))); 482 GetStatus(); 483 EXPECT_FALSE(status_.has_boot_mode()); 484 485 EXPECT_CALL(statistics_provider_, 486 GetMachineStatistic("devsw_boot", NotNull())) 487 .WillOnce(DoAll(SetArgPointee<1>(" "), Return(true))); 488 GetStatus(); 489 EXPECT_FALSE(status_.has_boot_mode()); 490 491 EXPECT_CALL(statistics_provider_, 492 GetMachineStatistic("devsw_boot", NotNull())) 493 .WillOnce(DoAll(SetArgPointee<1>("0"), Return(true))); 494 GetStatus(); 495 EXPECT_EQ("Verified", status_.boot_mode()); 496 497 EXPECT_CALL(statistics_provider_, 498 GetMachineStatistic("devsw_boot", NotNull())) 499 .WillOnce(DoAll(SetArgPointee<1>("1"), Return(true))); 500 GetStatus(); 501 EXPECT_EQ("Dev", status_.boot_mode()); 502} 503 504TEST_F(DeviceStatusCollectorTest, VersionInfo) { 505 // When the pref to collect this data is not enabled, expect that none of 506 // the fields are present in the protobuf. 507 GetStatus(); 508 EXPECT_FALSE(status_.has_browser_version()); 509 EXPECT_FALSE(status_.has_os_version()); 510 EXPECT_FALSE(status_.has_firmware_version()); 511 512 cros_settings_->SetBoolean(chromeos::kReportDeviceVersionInfo, true); 513 GetStatus(); 514 EXPECT_TRUE(status_.has_browser_version()); 515 EXPECT_TRUE(status_.has_os_version()); 516 EXPECT_TRUE(status_.has_firmware_version()); 517 518 // Check that the browser version is not empty. OS version & firmware 519 // don't have any reasonable values inside the unit test, so those 520 // aren't checked. 521 EXPECT_NE("", status_.browser_version()); 522} 523 524TEST_F(DeviceStatusCollectorTest, Location) { 525 content::Geoposition valid_fix; 526 valid_fix.latitude = 4.3; 527 valid_fix.longitude = -7.8; 528 valid_fix.accuracy = 3.; 529 valid_fix.timestamp = Time::Now(); 530 531 content::Geoposition invalid_fix; 532 invalid_fix.error_code = 533 content::Geoposition::ERROR_CODE_POSITION_UNAVAILABLE; 534 invalid_fix.timestamp = Time::Now(); 535 536 // Check that when device location reporting is disabled, no location is 537 // reported. 538 SetMockPositionToReturnNext(valid_fix); 539 CheckThatNoLocationIsReported(); 540 541 // Check that when device location reporting is enabled and a valid fix is 542 // available, the location is reported and is stored in local state. 543 SetMockPositionToReturnNext(valid_fix); 544 cros_settings_->SetBoolean(chromeos::kReportDeviceLocation, true); 545 EXPECT_FALSE(prefs_.GetDictionary(prefs::kDeviceLocation)->empty()); 546 CheckThatAValidLocationIsReported(); 547 548 // Restart the status collector. Check that the last known location has been 549 // retrieved from local state without requesting a geolocation update. 550 SetMockPositionToReturnNext(valid_fix); 551 RestartStatusCollector(); 552 CheckThatAValidLocationIsReported(); 553 EXPECT_TRUE(mock_position_to_return_next.get()); 554 555 // Check that after disabling location reporting again, the last known 556 // location has been cleared from local state and is no longer reported. 557 SetMockPositionToReturnNext(valid_fix); 558 cros_settings_->SetBoolean(chromeos::kReportDeviceLocation, false); 559 // Allow the new pref to propagate to the status collector. 560 message_loop_.RunUntilIdle(); 561 EXPECT_TRUE(prefs_.GetDictionary(prefs::kDeviceLocation)->empty()); 562 CheckThatNoLocationIsReported(); 563 564 // Check that after enabling location reporting again, an error is reported 565 // if no valid fix is available. 566 SetMockPositionToReturnNext(invalid_fix); 567 cros_settings_->SetBoolean(chromeos::kReportDeviceLocation, true); 568 // Allow the new pref to propagate to the status collector. 569 message_loop_.RunUntilIdle(); 570 CheckThatALocationErrorIsReported(); 571} 572 573} // namespace policy 574