libweave: Test for libweave public API.
Only GCD registration is implemented.
Added more mocks of public interfaces.
BUG=brillo:1256
TEST=`FEATURES=test emerge-gizmo libweave buffet`
Change-Id: I70efbee318955e446c0c5c5490dc2d40b526683a
Reviewed-on: https://chromium-review.googlesource.com/294844
Tested-by: Vitaly Buka <vitalybuka@chromium.org>
Reviewed-by: Alex Vakulenko <avakulenko@chromium.org>
Commit-Queue: Vitaly Buka <vitalybuka@chromium.org>
diff --git a/libweave/include/weave/error.h b/libweave/include/weave/error.h
index 344f56f..314f685 100644
--- a/libweave/include/weave/error.h
+++ b/libweave/include/weave/error.h
@@ -19,7 +19,7 @@
using ErrorPtr = std::unique_ptr<Error>;
-class Error final {
+class LIBWEAVE_EXPORT Error final {
public:
~Error() = default;
@@ -36,20 +36,19 @@
// If |error| is not nullptr, creates another instance of Error class,
// initializes it with specified arguments and adds it to the head of
// the error chain pointed to by |error|.
- LIBWEAVE_EXPORT static void AddTo(ErrorPtr* error,
- const tracked_objects::Location& location,
- const std::string& domain,
- const std::string& code,
- const std::string& message);
+ static void AddTo(ErrorPtr* error,
+ const tracked_objects::Location& location,
+ const std::string& domain,
+ const std::string& code,
+ const std::string& message);
// Same as the Error::AddTo above, but allows to pass in a printf-like
// format string and optional parameters to format the error message.
- LIBWEAVE_EXPORT static void AddToPrintf(
- ErrorPtr* error,
- const tracked_objects::Location& location,
- const std::string& domain,
- const std::string& code,
- const char* format,
- ...) PRINTF_FORMAT(5, 6);
+ static void AddToPrintf(ErrorPtr* error,
+ const tracked_objects::Location& location,
+ const std::string& domain,
+ const std::string& code,
+ const char* format,
+ ...) PRINTF_FORMAT(5, 6);
// Clones error with all inner errors.
ErrorPtr Clone() const;
diff --git a/libweave/include/weave/mock_config_store.h b/libweave/include/weave/mock_config_store.h
index 0d03cf2..e82bc66 100644
--- a/libweave/include/weave/mock_config_store.h
+++ b/libweave/include/weave/mock_config_store.h
@@ -12,15 +12,15 @@
#include <gmock/gmock.h>
#include <weave/config_store.h>
-using testing::_;
-using testing::Return;
-
namespace weave {
namespace unittests {
class MockConfigStore : public ConfigStore {
public:
MockConfigStore() {
+ using testing::_;
+ using testing::Return;
+
EXPECT_CALL(*this, LoadDefaults(_)).WillRepeatedly(Return(true));
EXPECT_CALL(*this, LoadSettings()).WillRepeatedly(Return(""));
EXPECT_CALL(*this, SaveSettings(_)).WillRepeatedly(Return());
diff --git a/libweave/include/weave/mock_http_server.h b/libweave/include/weave/mock_http_server.h
new file mode 100644
index 0000000..7cd8355
--- /dev/null
+++ b/libweave/include/weave/mock_http_server.h
@@ -0,0 +1,32 @@
+// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef LIBWEAVE_INCLUDE_WEAVE_MOCK_HTTP_SERVER_H_
+#define LIBWEAVE_INCLUDE_WEAVE_MOCK_HTTP_SERVER_H_
+
+#include <weave/http_server.h>
+
+#include <string>
+#include <vector>
+
+#include <base/callback.h>
+
+namespace weave {
+namespace unittests {
+
+class MockHttpServer : public HttpServer {
+ public:
+ MOCK_METHOD1(AddOnStateChangedCallback, void(const OnStateChangedCallback&));
+ MOCK_METHOD2(AddRequestHandler,
+ void(const std::string&, const OnRequestCallback&));
+
+ MOCK_CONST_METHOD0(GetHttpPort, uint16_t());
+ MOCK_CONST_METHOD0(GetHttpsPort, uint16_t());
+ MOCK_CONST_METHOD0(GetHttpsCertificateFingerprint, std::vector<uint8_t>&());
+};
+
+} // namespace unittests
+} // namespace weave
+
+#endif // LIBWEAVE_INCLUDE_WEAVE_MOCK_HTTP_SERVER_H_
diff --git a/libweave/include/weave/mock_mdns.h b/libweave/include/weave/mock_mdns.h
new file mode 100644
index 0000000..c6106c1
--- /dev/null
+++ b/libweave/include/weave/mock_mdns.h
@@ -0,0 +1,31 @@
+// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef LIBWEAVE_INCLUDE_WEAVE_MOCK_MDNS_H_
+#define LIBWEAVE_INCLUDE_WEAVE_MOCK_MDNS_H_
+
+#include <weave/mdns.h>
+
+#include <map>
+#include <string>
+
+#include <gmock/gmock.h>
+
+namespace weave {
+namespace unittests {
+
+class MockMdns : public Mdns {
+ public:
+ MOCK_METHOD3(PublishService,
+ void(const std::string&,
+ uint16_t,
+ const std::map<std::string, std::string>&));
+ MOCK_METHOD1(StopPublishing, void(const std::string&));
+ MOCK_CONST_METHOD0(GetId, std::string());
+};
+
+} // namespace unittests
+} // namespace weave
+
+#endif // LIBWEAVE_INCLUDE_WEAVE_MOCK_MDNS_H_
diff --git a/libweave/include/weave/mock_network.h b/libweave/include/weave/mock_network.h
new file mode 100644
index 0000000..2d20395
--- /dev/null
+++ b/libweave/include/weave/mock_network.h
@@ -0,0 +1,54 @@
+// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef LIBWEAVE_INCLUDE_WEAVE_MOCK_NETWORK_H_
+#define LIBWEAVE_INCLUDE_WEAVE_MOCK_NETWORK_H_
+
+#include <weave/network.h>
+
+#include <string>
+
+#include <gmock/gmock.h>
+
+namespace weave {
+namespace unittests {
+
+class MockNetwork : public Network {
+ public:
+ MockNetwork() {}
+ ~MockNetwork() override = default;
+
+ MOCK_METHOD1(AddOnConnectionChangedCallback,
+ void(const OnConnectionChangedCallback&));
+ MOCK_METHOD4(ConnectToService,
+ bool(const std::string&,
+ const std::string&,
+ const base::Closure&,
+ ErrorPtr*));
+ MOCK_CONST_METHOD0(GetConnectionState, NetworkState());
+ MOCK_METHOD1(EnableAccessPoint, void(const std::string&));
+ MOCK_METHOD0(DisableAccessPoint, void());
+
+ MOCK_METHOD2(MockOpenSocketBlocking, Stream*(const std::string&, uint16_t));
+ MOCK_METHOD2(MockCreateTlsStream, Stream*(Stream*, const std::string&));
+
+ std::unique_ptr<Stream> OpenSocketBlocking(const std::string& host,
+ uint16_t port) override {
+ return std::unique_ptr<Stream>{MockOpenSocketBlocking(host, port)};
+ }
+
+ void CreateTlsStream(
+ std::unique_ptr<Stream> socket,
+ const std::string& host,
+ const base::Callback<void(std::unique_ptr<Stream>)>& success_callback,
+ const base::Callback<void(const Error*)>& error_callback) override {
+ success_callback.Run(
+ std::unique_ptr<Stream>{MockCreateTlsStream(socket.get(), host)});
+ }
+};
+
+} // namespace unittests
+} // namespace weave
+
+#endif // LIBWEAVE_INCLUDE_WEAVE_MOCK_NETWORK_H_
diff --git a/libweave/include/weave/mock_task_runner.h b/libweave/include/weave/mock_task_runner.h
index d0d06f0..65d9b79 100644
--- a/libweave/include/weave/mock_task_runner.h
+++ b/libweave/include/weave/mock_task_runner.h
@@ -20,45 +20,22 @@
class MockTaskRunner : public TaskRunner {
public:
- MockTaskRunner() {
- test_clock_.SetNow(base::Time::Now());
- using testing::_;
- using testing::Invoke;
- using testing::AnyNumber;
- ON_CALL(*this, PostDelayedTask(_, _, _))
- .WillByDefault(Invoke(this, &MockTaskRunner::SaveTask));
- EXPECT_CALL(*this, PostDelayedTask(_, _, _)).Times(AnyNumber());
- }
- ~MockTaskRunner() override = default;
+ MockTaskRunner();
+ ~MockTaskRunner() override;
MOCK_METHOD3(PostDelayedTask,
void(const tracked_objects::Location&,
const base::Closure&,
base::TimeDelta));
- bool RunOnce() {
- if (queue_.empty())
- return false;
- auto top = queue_.top();
- queue_.pop();
- test_clock_.SetNow(std::max(test_clock_.Now(), top.first.first));
- top.second.Run();
- return true;
- }
-
- void Run() {
- while (RunOnce()) {
- }
- }
-
- base::Clock* GetClock() { return &test_clock_; }
+ bool RunOnce();
+ void Run();
+ base::Clock* GetClock();
private:
void SaveTask(const tracked_objects::Location& from_here,
const base::Closure& task,
- base::TimeDelta delay) {
- queue_.emplace(std::make_pair(test_clock_.Now() + delay, ++counter_), task);
- }
+ base::TimeDelta delay);
using QueueItem = std::pair<std::pair<base::Time, size_t>, base::Closure>;
@@ -70,16 +47,8 @@
size_t counter_{0}; // Keeps order of tasks with the same time.
- class TestClock : public base::Clock {
- public:
- base::Time Now() override { return now_; }
-
- void SetNow(base::Time now) { now_ = now; }
-
- private:
- base::Time now_;
- };
- TestClock test_clock_;
+ class TestClock;
+ std::unique_ptr<TestClock> test_clock_;
std::priority_queue<QueueItem,
std::vector<QueueItem>,
diff --git a/libweave/include/weave/task_runner.h b/libweave/include/weave/task_runner.h
index 66b83ab..845d9f7 100644
--- a/libweave/include/weave/task_runner.h
+++ b/libweave/include/weave/task_runner.h
@@ -11,6 +11,7 @@
#include <base/callback.h>
#include <base/location.h>
+#include <base/time/time.h>
namespace weave {
diff --git a/libweave/include/weave/unittest_utils.h b/libweave/include/weave/unittest_utils.h
index 3cfaeea..fe06ab8 100644
--- a/libweave/include/weave/unittest_utils.h
+++ b/libweave/include/weave/unittest_utils.h
@@ -19,6 +19,8 @@
// are replaced with apostrophes.
std::unique_ptr<base::Value> CreateValue(const std::string& json);
+std::string ValueToString(const base::Value& value);
+
// Helper method to create a JSON dictionary object from a string.
std::unique_ptr<base::DictionaryValue> CreateDictionaryValue(
const std::string& json);
diff --git a/libweave/libweave.gyp b/libweave/libweave.gyp
index e17ac6a..5288b56 100644
--- a/libweave/libweave.gyp
+++ b/libweave/libweave.gyp
@@ -147,6 +147,39 @@
'<@(base_unittests)',
],
},
+ {
+ 'target_name': 'libweave_exports_testrunner',
+ 'type': 'executable',
+ 'variables': {
+ 'deps': [
+ 'libchrome-<(libbase_ver)',
+ ],
+ },
+ 'dependencies': [
+ 'libweave-<(libbase_ver)',
+ 'libweave-test-<(libbase_ver)',
+ ],
+ 'includes': ['../common-mk/common_test.gypi'],
+ 'sources': [
+ '<@(weave_exports_unittest_sources)',
+ ],
+ },
+ {
+ 'target_name': 'libweave_base_exports_testrunner',
+ 'type': 'executable',
+ 'cflags': ['-Wno-format-nonliteral'],
+ 'include_dirs': [
+ '../libweave/external',
+ ],
+ 'dependencies': [
+ 'libweave_base',
+ 'libweave_base-test',
+ ],
+ 'includes': ['../common-mk/common_test.gypi'],
+ 'sources': [
+ '<@(weave_exports_unittest_sources)',
+ ],
+ },
],
}],
],
diff --git a/libweave/libweave.gypi b/libweave/libweave.gypi
index 5543575..c0e752f 100644
--- a/libweave/libweave.gypi
+++ b/libweave/libweave.gypi
@@ -56,6 +56,7 @@
'src/commands/mock_command.cc',
'src/commands/unittest_utils.cc',
'src/mock_http_client.cc',
+ 'src/mock_task_runner.cc',
],
'weave_unittest_sources': [
'external/crypto/p224_spake_unittest.cc',
@@ -89,6 +90,10 @@
'src/string_utils_unittest.cc',
'src/weave_testrunner.cc',
],
+ 'weave_exports_unittest_sources': [
+ 'src/weave_unittest.cc',
+ 'src/weave_testrunner.cc',
+ ],
'base_sources': [
'external/base/bind_helpers.cc',
'external/base/callback_internal.cc',
diff --git a/libweave/src/commands/command_manager_unittest.cc b/libweave/src/commands/command_manager_unittest.cc
index 932414c..00ab9c8 100644
--- a/libweave/src/commands/command_manager_unittest.cc
+++ b/libweave/src/commands/command_manager_unittest.cc
@@ -13,6 +13,8 @@
#include "libweave/src/bind_lambda.h"
#include "libweave/src/commands/unittest_utils.h"
+using testing::Return;
+
namespace weave {
using unittests::CreateDictionaryValue;
diff --git a/libweave/src/commands/unittest_utils.cc b/libweave/src/commands/unittest_utils.cc
index 12a1c02..3148ffe 100644
--- a/libweave/src/commands/unittest_utils.cc
+++ b/libweave/src/commands/unittest_utils.cc
@@ -5,6 +5,7 @@
#include "libweave/src/commands/unittest_utils.h"
#include <base/json/json_reader.h>
+#include <base/json/json_writer.h>
#include <base/logging.h>
namespace weave {
@@ -24,6 +25,13 @@
return value;
}
+std::string ValueToString(const base::Value& value) {
+ std::string json;
+ base::JSONWriter::WriteWithOptions(
+ value, base::JSONWriter::OPTIONS_PRETTY_PRINT, &json);
+ return json;
+}
+
std::unique_ptr<base::DictionaryValue> CreateDictionaryValue(
const std::string& json) {
std::unique_ptr<base::Value> value = CreateValue(json);
diff --git a/libweave/src/config_unittest.cc b/libweave/src/config_unittest.cc
index 794f5cb..7746fbd 100644
--- a/libweave/src/config_unittest.cc
+++ b/libweave/src/config_unittest.cc
@@ -15,6 +15,7 @@
using testing::_;
using testing::Invoke;
+using testing::Return;
namespace weave {
diff --git a/libweave/src/device_registration_info.cc b/libweave/src/device_registration_info.cc
index 62d1ac9..cd62cac 100644
--- a/libweave/src/device_registration_info.cc
+++ b/libweave/src/device_registration_info.cc
@@ -523,6 +523,12 @@
void DeviceRegistrationInfo::GetDeviceInfo(
const CloudRequestCallback& success_callback,
const CloudRequestErrorCallback& error_callback) {
+ ErrorPtr error;
+ if (!VerifyRegistrationCredentials(&error)) {
+ if (!error_callback.is_null())
+ error_callback.Run(error.get());
+ return;
+ }
DoCloudRequest(http::kGet, GetDeviceURL(), nullptr, success_callback,
error_callback);
}
diff --git a/libweave/src/mock_task_runner.cc b/libweave/src/mock_task_runner.cc
new file mode 100644
index 0000000..5052b94
--- /dev/null
+++ b/libweave/src/mock_task_runner.cc
@@ -0,0 +1,58 @@
+// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <weave/mock_task_runner.h>
+
+using testing::_;
+using testing::Invoke;
+using testing::AnyNumber;
+
+namespace weave {
+namespace unittests {
+
+class MockTaskRunner::TestClock : public base::Clock {
+ public:
+ base::Time Now() override { return now_; }
+
+ void SetNow(base::Time now) { now_ = now; }
+
+ private:
+ base::Time now_{base::Time::Now()};
+};
+
+MockTaskRunner::MockTaskRunner() : test_clock_{new TestClock} {
+ ON_CALL(*this, PostDelayedTask(_, _, _))
+ .WillByDefault(Invoke(this, &MockTaskRunner::SaveTask));
+ EXPECT_CALL(*this, PostDelayedTask(_, _, _)).Times(AnyNumber());
+}
+
+MockTaskRunner::~MockTaskRunner() {}
+
+bool MockTaskRunner::RunOnce() {
+ if (queue_.empty())
+ return false;
+ auto top = queue_.top();
+ queue_.pop();
+ test_clock_->SetNow(std::max(test_clock_->Now(), top.first.first));
+ top.second.Run();
+ return true;
+}
+
+void MockTaskRunner::Run() {
+ while (RunOnce()) {
+ }
+}
+
+base::Clock* MockTaskRunner::GetClock() {
+ return test_clock_.get();
+}
+
+void MockTaskRunner::SaveTask(const tracked_objects::Location& from_here,
+ const base::Closure& task,
+ base::TimeDelta delay) {
+ queue_.emplace(std::make_pair(test_clock_->Now() + delay, ++counter_), task);
+}
+
+} // namespace unittests
+} // namespace weave
diff --git a/libweave/src/privet/publisher.cc b/libweave/src/privet/publisher.cc
index 4edb7bc..85576cf 100644
--- a/libweave/src/privet/publisher.cc
+++ b/libweave/src/privet/publisher.cc
@@ -84,10 +84,14 @@
if (!cloud_->GetDescription().empty())
txt_record.emplace("note", cloud_->GetDescription());
+ is_publishing_ = true;
mdns_->PublishService(kPrivetServiceType, port, txt_record);
}
void Publisher::RemoveService() {
+ if (!is_publishing_)
+ return;
+ is_publishing_ = false;
VLOG(1) << "Stopping service publishing.";
mdns_->StopPublishing(kPrivetServiceType);
}
diff --git a/libweave/src/privet/publisher.cc.rej b/libweave/src/privet/publisher.cc.rej
new file mode 100644
index 0000000..6b84196
--- /dev/null
+++ b/libweave/src/privet/publisher.cc.rej
@@ -0,0 +1,16 @@
+diff a/libweave/src/privet/publisher.cc b/libweave/src/privet/publisher.cc (rejected hunks)
+@@ -82,10 +82,14 @@ void Publisher::ExposeService() {
+ if (!cloud_->GetDescription().empty())
+ txt_record.emplace("note", cloud_->GetDescription());
+
++ is_publishing_ = true;
+ mdns_->PublishService(kPrivetServiceId, port, txt_record);
+ }
+
+ void Publisher::RemoveService() {
++ if (!is_publishing_)
++ return;
++ is_publishing_ = false;
+ VLOG(1) << "Stopping service publishing.";
+ mdns_->StopPublishing(kPrivetServiceId);
+ }
diff --git a/libweave/src/privet/publisher.h b/libweave/src/privet/publisher.h
index ddedd05..71d38a8 100644
--- a/libweave/src/privet/publisher.h
+++ b/libweave/src/privet/publisher.h
@@ -41,6 +41,7 @@
void ExposeService();
void RemoveService();
+ bool is_publishing_{false};
Mdns* mdns_{nullptr};
const DeviceDelegate* device_{nullptr};
diff --git a/libweave/src/weave_unittest.cc b/libweave/src/weave_unittest.cc
new file mode 100644
index 0000000..f0bbfa8
--- /dev/null
+++ b/libweave/src/weave_unittest.cc
@@ -0,0 +1,357 @@
+// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <weave/device.h>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <weave/mock_config_store.h>
+#include <weave/mock_http_client.h>
+#include <weave/mock_http_server.h>
+#include <weave/mock_mdns.h>
+#include <weave/mock_network.h>
+#include <weave/mock_task_runner.h>
+#include <weave/unittest_utils.h>
+
+#include "libweave/src/bind_lambda.h"
+
+using testing::_;
+using testing::AtMost;
+using testing::HasSubstr;
+using testing::Invoke;
+using testing::InvokeWithoutArgs;
+using testing::MatchesRegex;
+using testing::Mock;
+using testing::AtLeast;
+using testing::Return;
+using testing::ReturnRefOfCopy;
+using testing::StartsWith;
+using testing::StrictMock;
+using testing::WithArgs;
+
+namespace weave {
+
+using unittests::CreateDictionaryValue;
+using unittests::ValueToString;
+
+const char kCategory[] = "powerd";
+
+const char kBaseCommandDefs[] = R"({
+ "base": {
+ "reboot": {
+ "parameters": {"delay": "integer"},
+ "results": {}
+ },
+ "shutdown": {
+ "parameters": {},
+ "results": {}
+ }
+ }
+})";
+
+const char kCommandDefs[] = R"({
+ "base": {
+ "reboot": {},
+ "shutdown": {}
+ }
+})";
+
+const char kBaseStateDefs[] = R"({
+ "base": {
+ "firmwareVersion": "string",
+ "localDiscoveryEnabled": "boolean",
+ "localAnonymousAccessMaxRole": [ "none", "viewer", "user" ],
+ "localPairingEnabled": "boolean",
+ "network": {
+ "properties": {
+ "name": "string"
+ }
+ }
+ }
+})";
+
+const char kBaseStateDefaults[] = R"({
+ "base": {
+ "firmwareVersion": "",
+ "localDiscoveryEnabled": false,
+ "localAnonymousAccessMaxRole": "none",
+ "localPairingEnabled": false
+ }
+})";
+
+const char kDeviceResource[] = R"({
+ "kind": "clouddevices#device",
+ "id": "DEVICE_ID",
+ "channel": {
+ "supportedType": "pull"
+ },
+ "deviceKind": "vendor",
+ "modelManifestId": "ABCDE",
+ "systemName": "",
+ "name": "DEVICE_NAME",
+ "displayName": "",
+ "description": "Developer device",
+ "stateValidationEnabled": true,
+ "commandDefs":{
+ "base": {
+ "reboot": {
+ "minimalRole": "user",
+ "parameters": {"delay": "integer"},
+ "results": {}
+ },
+ "shutdown": {
+ "minimalRole": "user",
+ "parameters": {},
+ "results": {}
+ }
+ }
+ },
+ "state":{
+ "base":{
+ "firmwareVersion":"FIRMWARE_VERSION",
+ "localAnonymousAccessMaxRole":"viewer",
+ "localDiscoveryEnabled":true,
+ "localPairingEnabled":true,
+ "network":{
+ }
+ },
+ "power": {"battery_level":44}
+ }
+})";
+
+const char kRegistrationResponse[] = R"({
+ "kind": "clouddevices#registrationTicket",
+ "id": "TEST_ID",
+ "deviceId": "DEVICE_ID",
+ "oauthClientId": "CLIENT_ID",
+ "userEmail": "USER@gmail.com",
+ "creationTimeMs": "1440087183738",
+ "expirationTimeMs": "1440087423738"
+})";
+
+const char kRegistrationFinalResponse[] = R"({
+ "kind": "clouddevices#registrationTicket",
+ "id": "TEST_ID",
+ "deviceId": "DEVICE_ID",
+ "oauthClientId": "CLIENT_ID",
+ "userEmail": "USER@gmail.com",
+ "robotAccountEmail": "ROBO@gmail.com",
+ "robotAccountAuthorizationCode": "AUTH_CODE",
+ "creationTimeMs": "1440087183738",
+ "expirationTimeMs": "1440087423738"
+})";
+
+const char kAuthTokenResponse[] = R"({
+ "access_token" : "ACCESS_TOKEN",
+ "token_type" : "Bearer",
+ "expires_in" : 3599,
+ "refresh_token" : "REFRESH_TOKEN"
+})";
+
+const char kStateDefs[] = R"({"power": {"battery_level":"integer"}})";
+
+const char kStateDefaults[] = R"({"power": {"battery_level":44}})";
+
+class WeaveTest : public ::testing::Test {
+ protected:
+ void SetUp() override { device_ = weave::Device::Create(); }
+
+ void ExpectRequest(const std::string& method,
+ const std::string& url,
+ const std::string& json_response) {
+ EXPECT_CALL(http_client_, MockSendRequest(method, url, _, _, _))
+ .WillOnce(InvokeWithoutArgs([json_response]() {
+ unittests::MockHttpClientResponse* response =
+ new StrictMock<unittests::MockHttpClientResponse>;
+ EXPECT_CALL(*response, GetStatusCode())
+ .Times(AtLeast(1))
+ .WillRepeatedly(Return(200));
+ EXPECT_CALL(*response, GetContentType())
+ .Times(AtLeast(1))
+ .WillRepeatedly(Return("application/json; charset=utf-8"));
+ EXPECT_CALL(*response, GetData())
+ .Times(AtLeast(1))
+ .WillRepeatedly(ReturnRefOfCopy(json_response));
+ return response;
+ }));
+ }
+
+ void InitConfigStore() {
+ EXPECT_CALL(config_store_, LoadDefaults(_))
+ .WillOnce(Invoke([](weave::Settings* settings) {
+ settings->api_key = "API_KEY";
+ settings->client_secret = "CLIENT_SECRET";
+ settings->client_id = "CLIENT_ID";
+ settings->firmware_version = "FIRMWARE_VERSION";
+ settings->name = "DEVICE_NAME";
+ settings->model_id = "ABCDE";
+ return true;
+ }));
+ EXPECT_CALL(config_store_, SaveSettings("")).WillRepeatedly(Return());
+
+ EXPECT_CALL(config_store_, LoadBaseCommandDefs())
+ .WillOnce(Return(kBaseCommandDefs));
+
+ EXPECT_CALL(config_store_, LoadCommandDefs())
+ .WillOnce(Return(
+ std::map<std::string, std::string>{{kCategory, kCommandDefs}}));
+
+ EXPECT_CALL(config_store_, LoadBaseStateDefs())
+ .WillOnce(Return(kBaseStateDefs));
+
+ EXPECT_CALL(config_store_, LoadStateDefs())
+ .WillOnce(Return(
+ std::map<std::string, std::string>{{kCategory, kStateDefs}}));
+
+ EXPECT_CALL(config_store_, LoadBaseStateDefaults())
+ .WillOnce(Return(kBaseStateDefaults));
+
+ EXPECT_CALL(config_store_, LoadStateDefaults())
+ .WillOnce(Return(std::vector<std::string>{kStateDefaults}));
+ }
+
+ void InitNetwork() {
+ EXPECT_CALL(network_, AddOnConnectionChangedCallback(_))
+ .WillRepeatedly(Return());
+ EXPECT_CALL(network_, GetConnectionState())
+ .WillRepeatedly(Return(NetworkState::kOffline));
+ EXPECT_CALL(network_, EnableAccessPoint(MatchesRegex("DEVICE_NAME.*prv")))
+ .WillOnce(Return());
+ }
+
+ void InitMdns() {
+ EXPECT_CALL(mdns_, GetId()).WillRepeatedly(Return("TEST_ID"));
+ InitMdnsPublishing(false);
+ EXPECT_CALL(mdns_, StopPublishing("privet")).WillOnce(Return());
+ }
+
+ void InitMdnsPublishing(bool registered) {
+ std::map<std::string, std::string> txt{
+ {"id", "TEST_ID"}, {"flags", "DB"}, {"mmid", "ABCDE"},
+ {"services", "_base"}, {"txtvers", "3"}, {"ty", "DEVICE_NAME"}};
+ if (registered) {
+ txt["gcd_id"] = "DEVICE_ID";
+
+ // During registration device may announce itself twice:
+ // 1. with GCD ID but not connected (DB)
+ // 2. with GCD ID and connected (BB)
+ EXPECT_CALL(mdns_, PublishService("privet", 11, txt))
+ .Times(AtMost(1))
+ .WillOnce(Return());
+
+ txt["flags"] = "BB";
+ }
+
+ EXPECT_CALL(mdns_, PublishService("privet", 11, txt)).WillOnce(Return());
+ }
+
+ void InitHttpServer() {
+ EXPECT_CALL(http_server_, GetHttpPort()).WillRepeatedly(Return(11));
+ EXPECT_CALL(http_server_, GetHttpsPort()).WillRepeatedly(Return(12));
+ EXPECT_CALL(http_server_, GetHttpsCertificateFingerprint())
+ .WillRepeatedly(ReturnRefOfCopy(std::vector<uint8_t>{1, 2, 3}));
+ EXPECT_CALL(http_server_, AddRequestHandler(_, _))
+ .WillRepeatedly(Invoke([this](const std::string& path_prefix,
+ const HttpServer::OnRequestCallback& cb) {
+ http_server_request_cb_.push_back(cb);
+ }));
+ EXPECT_CALL(http_server_, AddOnStateChangedCallback(_))
+ .WillRepeatedly(
+ Invoke([this](const HttpServer::OnStateChangedCallback& cb) {
+ http_server_changed_cb_.push_back(cb);
+ }));
+ }
+
+ void StartDevice() {
+ InitConfigStore();
+ InitNetwork();
+ InitHttpServer();
+ InitMdns();
+
+ weave::Device::Options options;
+ options.xmpp_enabled = false;
+
+ device_->Start(options, &config_store_, &task_runner_, &http_client_,
+ &network_, &mdns_, &http_server_);
+
+ cloud_ = device_->GetCloud();
+ ASSERT_TRUE(cloud_);
+
+ cloud_->GetDeviceInfo(
+ base::Bind(
+ [](const base::DictionaryValue& response) { ADD_FAILURE(); }),
+ base::Bind([](const Error* error) {
+ EXPECT_TRUE(error->HasError("gcd", "device_not_registered"));
+ }));
+
+ for (const auto& cb : http_server_changed_cb_) {
+ cb.Run(http_server_);
+ }
+
+ task_runner_.Run();
+ }
+
+ std::vector<HttpServer::OnStateChangedCallback> http_server_changed_cb_;
+ std::vector<HttpServer::OnRequestCallback> http_server_request_cb_;
+
+ StrictMock<unittests::MockConfigStore> config_store_;
+ StrictMock<unittests::MockTaskRunner> task_runner_;
+ StrictMock<unittests::MockHttpClient> http_client_;
+ StrictMock<unittests::MockNetwork> network_;
+ StrictMock<unittests::MockMdns> mdns_;
+ StrictMock<unittests::MockHttpServer> http_server_;
+
+ weave::Cloud* cloud_{nullptr};
+
+ std::unique_ptr<weave::Device> device_;
+};
+
+TEST_F(WeaveTest, Create) {
+ ASSERT_TRUE(device_.get());
+}
+
+TEST_F(WeaveTest, StartMinimal) {
+ weave::Device::Options options;
+ options.xmpp_enabled = false;
+ options.disable_privet = true;
+ options.disable_security = true;
+
+ InitConfigStore();
+ device_->Start(options, &config_store_, &task_runner_, &http_client_,
+ &network_, nullptr, nullptr);
+}
+
+TEST_F(WeaveTest, Start) {
+ StartDevice();
+}
+
+TEST_F(WeaveTest, Register) {
+ StartDevice();
+
+ auto draft = CreateDictionaryValue(kDeviceResource);
+ auto response = CreateDictionaryValue(kRegistrationResponse);
+ response->Set("deviceDraft", draft->DeepCopy());
+ ExpectRequest(
+ "PATCH",
+ "https://www.googleapis.com/clouddevices/v1/registrationTickets/"
+ "TEST_ID?key=API_KEY",
+ ValueToString(*response));
+
+ response = CreateDictionaryValue(kRegistrationFinalResponse);
+ response->Set("deviceDraft", draft->DeepCopy());
+ ExpectRequest(
+ "POST",
+ "https://www.googleapis.com/clouddevices/v1/registrationTickets/"
+ "TEST_ID/finalize?key=API_KEY",
+ ValueToString(*response));
+
+ ExpectRequest("POST", "https://accounts.google.com/o/oauth2/token",
+ kAuthTokenResponse);
+
+ InitMdnsPublishing(true);
+
+ EXPECT_EQ("DEVICE_ID", cloud_->RegisterDevice("TEST_ID", nullptr));
+}
+
+} // namespace weave