Improve testing of  WiFi bootstrapping condition

BUG=23755250,24054100

Change-Id: Ieefbc9f5b23ba4e29a89e43c62fefebd76646bc3
diff --git a/libweave/include/weave/test/mock_task_runner.h b/libweave/include/weave/test/mock_task_runner.h
index 5fee077..0aa3186 100644
--- a/libweave/include/weave/test/mock_task_runner.h
+++ b/libweave/include/weave/test/mock_task_runner.h
@@ -30,6 +30,7 @@
 
   bool RunOnce();
   void Run();
+  void Break();
   base::Clock* GetClock();
 
  private:
@@ -45,6 +46,7 @@
     }
   };
 
+  bool break_{false};
   size_t counter_{0};  // Keeps order of tasks with the same time.
 
   class TestClock;
diff --git a/libweave/src/privet/cloud_delegate.cc b/libweave/src/privet/cloud_delegate.cc
index c7d8c24..cce7994 100644
--- a/libweave/src/privet/cloud_delegate.cc
+++ b/libweave/src/privet/cloud_delegate.cc
@@ -76,6 +76,7 @@
     return true;
   }
 
+  // TODO(vitalybuka): Remove error.
   bool GetName(std::string* name, ErrorPtr* error) const override {
     *name = device_->GetConfig().name();
     return true;
diff --git a/libweave/src/privet/wifi_bootstrap_manager.cc b/libweave/src/privet/wifi_bootstrap_manager.cc
index 5a32419..6dd044d 100644
--- a/libweave/src/privet/wifi_bootstrap_manager.cc
+++ b/libweave/src/privet/wifi_bootstrap_manager.cc
@@ -16,12 +16,6 @@
 namespace weave {
 namespace privet {
 
-namespace {
-const int kConnectTimeoutMinutes = 3;
-const int kBootstrapTimeoutMinutes = 10;
-const int kMonitorTimeoutMinutes = 2;
-}
-
 WifiBootstrapManager::WifiBootstrapManager(
     const std::string& last_configured_ssid,
     const std::string& test_privet_ssid,
@@ -81,7 +75,7 @@
     task_runner_->PostDelayedTask(
         FROM_HERE, base::Bind(&WifiBootstrapManager::OnBootstrapTimeout,
                               tasks_weak_factory_.GetWeakPtr()),
-        base::TimeDelta::FromMinutes(kBootstrapTimeoutMinutes));
+        base::TimeDelta::FromMinutes(10));
   }
   // TODO(vitalybuka): Add SSID probing.
   privet_ssid_ = GenerateSsid();
@@ -104,7 +98,7 @@
   task_runner_->PostDelayedTask(
       FROM_HERE, base::Bind(&WifiBootstrapManager::OnConnectTimeout,
                             tasks_weak_factory_.GetWeakPtr()),
-      base::TimeDelta::FromMinutes(kConnectTimeoutMinutes));
+      base::TimeDelta::FromMinutes(3));
   network_->ConnectToService(ssid, passphrase,
                              base::Bind(&WifiBootstrapManager::OnConnectSuccess,
                                         tasks_weak_factory_.GetWeakPtr(), ssid),
@@ -119,6 +113,19 @@
   // We already have a callback in place with |network_| to update our
   // connectivity state.  See OnConnectivityChange().
   UpdateState(State::kMonitoring);
+
+  if (network_->GetConnectionState() == NetworkState::kConnected) {
+    monitor_until_ = base::Time::Max();
+  } else {
+    if (monitor_until_ == base::Time::Max())
+      monitor_until_ = base::Time::Now() + base::TimeDelta::FromMinutes(2);
+
+    // Schedule timeout timer taking into account already offline time.
+    task_runner_->PostDelayedTask(
+        FROM_HERE, base::Bind(&WifiBootstrapManager::OnMonitorTimeout,
+                              tasks_weak_factory_.GetWeakPtr()),
+        monitor_until_ - base::Time::Now());
+  }
 }
 
 void WifiBootstrapManager::EndMonitoring() {
@@ -234,22 +241,9 @@
   VLOG(3) << "ConnectivityChanged: " << is_connected;
   UpdateConnectionState();
 
-  if (state_ == State::kBootstrapping && is_connected) {
+  if (state_ == State::kMonitoring ||  // Reset monitoring timeout.
+      (state_ == State::kDisabled && is_connected)) {
     StartMonitoring();
-    return;
-  }
-  if (state_ == State::kMonitoring) {
-    if (is_connected) {
-      tasks_weak_factory_.InvalidateWeakPtrs();
-    } else {
-      // Tasks queue may have more than one OnMonitorTimeout enqueued. The
-      // first one could be executed as it would change the state and abort the
-      // rest.
-      task_runner_->PostDelayedTask(
-          FROM_HERE, base::Bind(&WifiBootstrapManager::OnMonitorTimeout,
-                                tasks_weak_factory_.GetWeakPtr()),
-          base::TimeDelta::FromMinutes(kMonitorTimeoutMinutes));
-    }
   }
 }
 
diff --git a/libweave/src/privet/wifi_bootstrap_manager.h b/libweave/src/privet/wifi_bootstrap_manager.h
index f622c77..c0052a0 100644
--- a/libweave/src/privet/wifi_bootstrap_manager.h
+++ b/libweave/src/privet/wifi_bootstrap_manager.h
@@ -101,6 +101,7 @@
   TaskRunner* task_runner_{nullptr};
   Network* network_{nullptr};
   WifiSsidGenerator ssid_generator_;
+  base::Time monitor_until_;
 
   std::vector<StateListener> state_listeners_;
   bool currently_online_{false};
diff --git a/libweave/src/test/mock_task_runner.cc b/libweave/src/test/mock_task_runner.cc
index da9246a..a78b4e1 100644
--- a/libweave/src/test/mock_task_runner.cc
+++ b/libweave/src/test/mock_task_runner.cc
@@ -40,10 +40,15 @@
 }
 
 void MockTaskRunner::Run() {
-  while (RunOnce()) {
+  break_ = false;
+  while (!break_ && RunOnce()) {
   }
 }
 
+void MockTaskRunner::Break() {
+  break_ = true;
+}
+
 base::Clock* MockTaskRunner::GetClock() {
   return test_clock_.get();
 }
diff --git a/libweave/src/weave_unittest.cc b/libweave/src/weave_unittest.cc
index 7b3bbdb..0629dac 100644
--- a/libweave/src/weave_unittest.cc
+++ b/libweave/src/weave_unittest.cc
@@ -221,11 +221,18 @@
 
   void InitNetwork() {
     EXPECT_CALL(network_, AddOnConnectionChangedCallback(_))
-        .WillRepeatedly(Return());
+        .WillRepeatedly(
+            Invoke([this](const Network::OnConnectionChangedCallback& cb) {
+              network_callbacks_.push_back(cb);
+            }));
     EXPECT_CALL(network_, GetConnectionState())
         .WillRepeatedly(Return(NetworkState::kOffline));
-    EXPECT_CALL(network_, EnableAccessPoint(MatchesRegex("DEVICE_NAME.*prv")))
-        .WillOnce(Return());
+  }
+
+  void IgnoreMdns() {
+    EXPECT_CALL(mdns_, GetId()).WillRepeatedly(Return("TEST_ID"));
+    EXPECT_CALL(mdns_, PublishService(_, _, _)).WillRepeatedly(Return());
+    EXPECT_CALL(mdns_, StopPublishing("_privet._tcp")).WillOnce(Return());
   }
 
   void InitMdns() {
@@ -272,12 +279,16 @@
             }));
   }
 
-  void StartDevice() {
+  void InitDefaultExpectations() {
     InitConfigStore();
     InitNetwork();
+    EXPECT_CALL(network_, EnableAccessPoint(MatchesRegex("DEVICE_NAME.*prv")))
+        .WillOnce(Return());
     InitHttpServer();
     InitMdns();
+  }
 
+  void StartDevice() {
     weave::Device::Options options;
     options.xmpp_enabled = false;
 
@@ -311,6 +322,8 @@
   StrictMock<test::MockMdns> mdns_;
   StrictMock<test::MockHttpServer> http_server_;
 
+  std::vector<Network::OnConnectionChangedCallback> network_callbacks_;
+
   weave::Cloud* cloud_{nullptr};
 
   std::unique_ptr<weave::Device> device_;
@@ -331,11 +344,19 @@
                  &network_, nullptr, nullptr);
 }
 
-TEST_F(WeaveTest, Start) {
+class WeaveBasicTest : public WeaveTest {
+ public:
+  void SetUp() override {
+    WeaveTest::SetUp();
+    InitDefaultExpectations();
+  }
+};
+
+TEST_F(WeaveBasicTest, Start) {
   StartDevice();
 }
 
-TEST_F(WeaveTest, Register) {
+TEST_F(WeaveBasicTest, Register) {
   StartDevice();
 
   auto draft = CreateDictionaryValue(kDeviceResource);
@@ -363,4 +384,94 @@
   EXPECT_EQ("DEVICE_ID", cloud_->RegisterDevice("TEST_ID", nullptr));
 }
 
+class WeaveWiFiSetupTest : public WeaveTest {
+ public:
+  void SetUp() override {
+    WeaveTest::SetUp();
+
+    InitConfigStore();
+    InitHttpServer();
+    InitNetwork();
+    IgnoreMdns();
+
+    EXPECT_CALL(network_, GetConnectionState())
+        .WillRepeatedly(Return(NetworkState::kConnected));
+  }
+};
+
+TEST_F(WeaveWiFiSetupTest, StartOnlineNoPrevSsid) {
+  StartDevice();
+
+  // Short disconnect.
+  EXPECT_CALL(network_, GetConnectionState())
+      .WillRepeatedly(Return(NetworkState::kOffline));
+  for (const auto& cb : network_callbacks_) {
+    task_runner_.PostDelayedTask(FROM_HERE, base::Bind(cb, false), {});
+  }
+  EXPECT_CALL(network_, GetConnectionState())
+      .WillRepeatedly(Return(NetworkState::kConnected));
+  for (const auto& cb : network_callbacks_) {
+    task_runner_.PostDelayedTask(FROM_HERE, base::Bind(cb, true),
+                                 base::TimeDelta::FromSeconds(10));
+  }
+  task_runner_.Run();
+
+  // Long disconnect.
+  EXPECT_CALL(network_, GetConnectionState())
+      .WillRepeatedly(Return(NetworkState::kOffline));
+  for (const auto& cb : network_callbacks_) {
+    task_runner_.PostDelayedTask(FROM_HERE, base::Bind(cb, false), {});
+  }
+  auto offline_from = task_runner_.GetClock()->Now();
+  EXPECT_CALL(network_, EnableAccessPoint(MatchesRegex("DEVICE_NAME.*prv")))
+      .WillOnce(InvokeWithoutArgs([this, offline_from]() {
+        EXPECT_GT(task_runner_.GetClock()->Now() - offline_from,
+                  base::TimeDelta::FromMinutes(1));
+      }));
+  task_runner_.Run();
+}
+
+// If device has previously configured WiFi it will run AP for limited time
+// after which it will try to re-connect.
+TEST_F(WeaveWiFiSetupTest, StartOnlineWithPrevSsid) {
+  EXPECT_CALL(config_store_, LoadSettings())
+      .WillRepeatedly(Return(R"({"last_configured_ssid": "TEST_ssid"})"));
+  StartDevice();
+
+  // Long disconnect.
+  EXPECT_CALL(network_, GetConnectionState())
+      .WillRepeatedly(Return(NetworkState::kOffline));
+  for (const auto& cb : network_callbacks_) {
+    task_runner_.PostDelayedTask(FROM_HERE, base::Bind(cb, false), {});
+  }
+
+  for (int i = 0; i < 5; ++i) {
+    auto offline_from = task_runner_.GetClock()->Now();
+    // Temporarily offline mode.
+    EXPECT_CALL(network_, EnableAccessPoint(MatchesRegex("DEVICE_NAME.*prv")))
+        .WillOnce(InvokeWithoutArgs([this, &offline_from]() {
+          EXPECT_GT(task_runner_.GetClock()->Now() - offline_from,
+                    base::TimeDelta::FromMinutes(1));
+          task_runner_.Break();
+        }));
+    task_runner_.Run();
+
+    // Try to reconnect again.
+    offline_from = task_runner_.GetClock()->Now();
+    EXPECT_CALL(network_, DisableAccessPoint())
+        .WillOnce(InvokeWithoutArgs([this, offline_from]() {
+          EXPECT_GT(task_runner_.GetClock()->Now() - offline_from,
+                    base::TimeDelta::FromMinutes(5));
+          task_runner_.Break();
+        }));
+    task_runner_.Run();
+  }
+
+  EXPECT_CALL(network_, GetConnectionState())
+      .WillRepeatedly(Return(NetworkState::kConnected));
+  for (const auto& cb : network_callbacks_)
+    task_runner_.PostDelayedTask(FROM_HERE, base::Bind(cb, true), {});
+  task_runner_.Run();
+}
+
 }  // namespace weave