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