Vitaly Buka | 4615e0d | 2015-10-14 15:35:12 -0700 | [diff] [blame] | 1 | // Copyright 2015 The Weave Authors. All rights reserved. |
Vitaly Buka | 11b2f23 | 2015-08-20 13:55:41 -0700 | [diff] [blame] | 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 <weave/device.h> |
| 6 | |
| 7 | #include <gmock/gmock.h> |
| 8 | #include <gtest/gtest.h> |
Vitaly Buka | 727f3e6 | 2015-09-25 17:33:43 -0700 | [diff] [blame] | 9 | #include <weave/provider/test/fake_task_runner.h> |
Vitaly Buka | 1e36367 | 2015-09-25 14:01:16 -0700 | [diff] [blame] | 10 | #include <weave/provider/test/mock_bluetooth.h> |
| 11 | #include <weave/provider/test/mock_config_store.h> |
| 12 | #include <weave/provider/test/mock_dns_service_discovery.h> |
| 13 | #include <weave/provider/test/mock_http_client.h> |
| 14 | #include <weave/provider/test/mock_http_server.h> |
| 15 | #include <weave/provider/test/mock_network.h> |
Vitaly Buka | 1e36367 | 2015-09-25 14:01:16 -0700 | [diff] [blame] | 16 | #include <weave/provider/test/mock_wifi.h> |
Vitaly Buka | ef213d7 | 2015-10-07 15:54:58 -0700 | [diff] [blame] | 17 | #include <weave/test/mock_command.h> |
Vitaly Buka | a8ece8f | 2015-10-05 13:30:23 -0700 | [diff] [blame] | 18 | #include <weave/test/mock_device.h> |
Vitaly Buka | 0f6b2ec | 2015-08-20 15:35:19 -0700 | [diff] [blame] | 19 | #include <weave/test/unittest_utils.h> |
Vitaly Buka | 11b2f23 | 2015-08-20 13:55:41 -0700 | [diff] [blame] | 20 | |
Stefan Sauer | 2d16dfa | 2015-09-25 17:08:35 +0200 | [diff] [blame] | 21 | #include "src/bind_lambda.h" |
Vitaly Buka | 11b2f23 | 2015-08-20 13:55:41 -0700 | [diff] [blame] | 22 | |
| 23 | using testing::_; |
Vitaly Buka | 65e1f21 | 2015-11-05 15:54:05 -0800 | [diff] [blame] | 24 | using testing::AtLeast; |
Vitaly Buka | 11b2f23 | 2015-08-20 13:55:41 -0700 | [diff] [blame] | 25 | using testing::AtMost; |
| 26 | using testing::HasSubstr; |
Vitaly Buka | 65e1f21 | 2015-11-05 15:54:05 -0800 | [diff] [blame] | 27 | using testing::InSequence; |
Vitaly Buka | 11b2f23 | 2015-08-20 13:55:41 -0700 | [diff] [blame] | 28 | using testing::Invoke; |
| 29 | using testing::InvokeWithoutArgs; |
| 30 | using testing::MatchesRegex; |
| 31 | using testing::Mock; |
Vitaly Buka | 11b2f23 | 2015-08-20 13:55:41 -0700 | [diff] [blame] | 32 | using testing::Return; |
| 33 | using testing::ReturnRefOfCopy; |
| 34 | using testing::StartsWith; |
| 35 | using testing::StrictMock; |
| 36 | using testing::WithArgs; |
| 37 | |
| 38 | namespace weave { |
| 39 | |
Vitaly Buka | e69e4ee | 2015-10-26 18:30:14 -0700 | [diff] [blame] | 40 | namespace { |
| 41 | |
Vitaly Buka | 1a42e14 | 2015-10-10 18:15:15 -0700 | [diff] [blame] | 42 | using provider::HttpClient; |
| 43 | using provider::Network; |
| 44 | using provider::test::MockHttpClientResponse; |
Vitaly Buka | 0f6b2ec | 2015-08-20 15:35:19 -0700 | [diff] [blame] | 45 | using test::CreateDictionaryValue; |
| 46 | using test::ValueToString; |
Vitaly Buka | 11b2f23 | 2015-08-20 13:55:41 -0700 | [diff] [blame] | 47 | |
Alex Vakulenko | 551a82b | 2015-12-07 14:46:12 -0800 | [diff] [blame] | 48 | const char kTraitDefs[] = R"({ |
| 49 | "trait1": { |
| 50 | "commands": { |
| 51 | "reboot": { |
| 52 | "minimalRole": "user" |
| 53 | }, |
| 54 | "shutdown": { |
| 55 | "minimalRole": "user", |
| 56 | "parameters": {}, |
| 57 | "results": {} |
| 58 | } |
Alex Vakulenko | 2c7740a | 2015-11-30 08:51:29 -0800 | [diff] [blame] | 59 | }, |
Alex Vakulenko | 551a82b | 2015-12-07 14:46:12 -0800 | [diff] [blame] | 60 | "state": { |
| 61 | "firmwareVersion": {"type": "string"} |
| 62 | } |
| 63 | }, |
| 64 | "trait2": { |
| 65 | "state": { |
| 66 | "battery_level": {"type": "integer"} |
Vitaly Buka | 11b2f23 | 2015-08-20 13:55:41 -0700 | [diff] [blame] | 67 | } |
| 68 | } |
| 69 | })"; |
| 70 | |
Vitaly Buka | 11b2f23 | 2015-08-20 13:55:41 -0700 | [diff] [blame] | 71 | const char kDeviceResource[] = R"({ |
Vitaly Buka | 88272d5 | 2015-11-17 17:04:01 -0800 | [diff] [blame] | 72 | "kind": "weave#device", |
Johan Euphrosine | 0b7bb9f | 2015-09-29 01:11:21 -0700 | [diff] [blame] | 73 | "id": "CLOUD_ID", |
Vitaly Buka | 11b2f23 | 2015-08-20 13:55:41 -0700 | [diff] [blame] | 74 | "channel": { |
| 75 | "supportedType": "pull" |
| 76 | }, |
| 77 | "deviceKind": "vendor", |
| 78 | "modelManifestId": "ABCDE", |
| 79 | "systemName": "", |
Vitaly Buka | a05eadb | 2015-09-29 16:38:24 -0700 | [diff] [blame] | 80 | "name": "TEST_NAME", |
Vitaly Buka | 11b2f23 | 2015-08-20 13:55:41 -0700 | [diff] [blame] | 81 | "displayName": "", |
| 82 | "description": "Developer device", |
| 83 | "stateValidationEnabled": true, |
| 84 | "commandDefs":{ |
Alex Vakulenko | 551a82b | 2015-12-07 14:46:12 -0800 | [diff] [blame] | 85 | "trait1": { |
Vitaly Buka | 11b2f23 | 2015-08-20 13:55:41 -0700 | [diff] [blame] | 86 | "reboot": { |
| 87 | "minimalRole": "user", |
Alex Vakulenko | 7e894da | 2015-11-23 11:47:49 -0800 | [diff] [blame] | 88 | "parameters": {"delay": {"type": "integer"}}, |
Vitaly Buka | 11b2f23 | 2015-08-20 13:55:41 -0700 | [diff] [blame] | 89 | "results": {} |
| 90 | }, |
| 91 | "shutdown": { |
| 92 | "minimalRole": "user", |
| 93 | "parameters": {}, |
| 94 | "results": {} |
| 95 | } |
| 96 | } |
| 97 | }, |
| 98 | "state":{ |
Alex Vakulenko | 551a82b | 2015-12-07 14:46:12 -0800 | [diff] [blame] | 99 | "trait1": {"firmwareVersion":"FIRMWARE_VERSION"}, |
| 100 | "trait2": {"battery_level":44} |
| 101 | }, |
| 102 | "traits": { |
| 103 | "trait1": { |
| 104 | "commands": { |
| 105 | "reboot": { |
| 106 | "minimalRole": "user" |
| 107 | }, |
| 108 | "shutdown": { |
| 109 | "minimalRole": "user", |
| 110 | "parameters": {}, |
| 111 | "results": {} |
| 112 | } |
| 113 | }, |
| 114 | "state": { |
| 115 | "firmwareVersion": {"type": "string"} |
Vitaly Buka | 11b2f23 | 2015-08-20 13:55:41 -0700 | [diff] [blame] | 116 | } |
| 117 | }, |
Alex Vakulenko | 551a82b | 2015-12-07 14:46:12 -0800 | [diff] [blame] | 118 | "trait2": { |
| 119 | "state": { |
| 120 | "battery_level": {"type": "integer"} |
| 121 | } |
| 122 | } |
| 123 | }, |
| 124 | "components": { |
| 125 | "myComponent": { |
| 126 | "traits": ["trait1", "trait2"], |
| 127 | "state": { |
| 128 | "trait1": {"firmwareVersion":"FIRMWARE_VERSION"}, |
| 129 | "trait2": {"battery_level":44} |
| 130 | } |
| 131 | } |
Vitaly Buka | 11b2f23 | 2015-08-20 13:55:41 -0700 | [diff] [blame] | 132 | } |
| 133 | })"; |
| 134 | |
| 135 | const char kRegistrationResponse[] = R"({ |
Vitaly Buka | 88272d5 | 2015-11-17 17:04:01 -0800 | [diff] [blame] | 136 | "kind": "weave#registrationTicket", |
Johan Euphrosine | 0b7bb9f | 2015-09-29 01:11:21 -0700 | [diff] [blame] | 137 | "id": "TICKET_ID", |
| 138 | "deviceId": "CLOUD_ID", |
Vitaly Buka | 11b2f23 | 2015-08-20 13:55:41 -0700 | [diff] [blame] | 139 | "oauthClientId": "CLIENT_ID", |
| 140 | "userEmail": "USER@gmail.com", |
| 141 | "creationTimeMs": "1440087183738", |
| 142 | "expirationTimeMs": "1440087423738" |
| 143 | })"; |
| 144 | |
| 145 | const char kRegistrationFinalResponse[] = R"({ |
Vitaly Buka | 88272d5 | 2015-11-17 17:04:01 -0800 | [diff] [blame] | 146 | "kind": "weave#registrationTicket", |
Johan Euphrosine | 0b7bb9f | 2015-09-29 01:11:21 -0700 | [diff] [blame] | 147 | "id": "TICKET_ID", |
| 148 | "deviceId": "CLOUD_ID", |
Vitaly Buka | 11b2f23 | 2015-08-20 13:55:41 -0700 | [diff] [blame] | 149 | "oauthClientId": "CLIENT_ID", |
| 150 | "userEmail": "USER@gmail.com", |
| 151 | "robotAccountEmail": "ROBO@gmail.com", |
| 152 | "robotAccountAuthorizationCode": "AUTH_CODE", |
| 153 | "creationTimeMs": "1440087183738", |
| 154 | "expirationTimeMs": "1440087423738" |
| 155 | })"; |
| 156 | |
| 157 | const char kAuthTokenResponse[] = R"({ |
| 158 | "access_token" : "ACCESS_TOKEN", |
| 159 | "token_type" : "Bearer", |
| 160 | "expires_in" : 3599, |
| 161 | "refresh_token" : "REFRESH_TOKEN" |
| 162 | })"; |
| 163 | |
Vitaly Buka | 3dc2f53 | 2015-09-08 18:01:32 -0700 | [diff] [blame] | 164 | MATCHER_P(MatchTxt, txt, "") { |
| 165 | std::vector<std::string> txt_copy = txt; |
| 166 | std::sort(txt_copy.begin(), txt_copy.end()); |
| 167 | std::vector<std::string> arg_copy = arg; |
| 168 | std::sort(arg_copy.begin(), arg_copy.end()); |
| 169 | return (arg_copy == txt_copy); |
| 170 | } |
| 171 | |
Vitaly Buka | e69e4ee | 2015-10-26 18:30:14 -0700 | [diff] [blame] | 172 | template <class Map> |
| 173 | std::set<typename Map::key_type> GetKeys(const Map& map) { |
| 174 | std::set<typename Map::key_type> result; |
| 175 | for (const auto& pair : map) |
| 176 | result.insert(pair.first); |
| 177 | return result; |
| 178 | } |
| 179 | |
| 180 | } // namespace |
| 181 | |
Vitaly Buka | 11b2f23 | 2015-08-20 13:55:41 -0700 | [diff] [blame] | 182 | class WeaveTest : public ::testing::Test { |
| 183 | protected: |
Vitaly Buka | c5bdd17 | 2015-10-01 19:48:51 -0700 | [diff] [blame] | 184 | void SetUp() override {} |
Vitaly Buka | 11b2f23 | 2015-08-20 13:55:41 -0700 | [diff] [blame] | 185 | |
Vitaly Buka | 1a42e14 | 2015-10-10 18:15:15 -0700 | [diff] [blame] | 186 | void ExpectRequest(HttpClient::Method method, |
Vitaly Buka | 11b2f23 | 2015-08-20 13:55:41 -0700 | [diff] [blame] | 187 | const std::string& url, |
| 188 | const std::string& json_response) { |
Vitaly Buka | 7476342 | 2015-10-11 00:39:52 -0700 | [diff] [blame] | 189 | EXPECT_CALL(http_client_, SendRequest(method, url, _, _, _)) |
Vitaly Buka | 1728037 | 2015-10-10 17:11:47 -0700 | [diff] [blame] | 190 | .WillOnce(WithArgs<4>(Invoke([json_response]( |
Vitaly Buka | 7476342 | 2015-10-11 00:39:52 -0700 | [diff] [blame] | 191 | const HttpClient::SendRequestCallback& callback) { |
Vitaly Buka | 1728037 | 2015-10-10 17:11:47 -0700 | [diff] [blame] | 192 | std::unique_ptr<provider::test::MockHttpClientResponse> response{ |
| 193 | new StrictMock<provider::test::MockHttpClientResponse>}; |
Vitaly Buka | 11b2f23 | 2015-08-20 13:55:41 -0700 | [diff] [blame] | 194 | EXPECT_CALL(*response, GetStatusCode()) |
| 195 | .Times(AtLeast(1)) |
| 196 | .WillRepeatedly(Return(200)); |
| 197 | EXPECT_CALL(*response, GetContentType()) |
| 198 | .Times(AtLeast(1)) |
| 199 | .WillRepeatedly(Return("application/json; charset=utf-8")); |
| 200 | EXPECT_CALL(*response, GetData()) |
Vitaly Buka | 4774df2 | 2015-10-09 12:36:22 -0700 | [diff] [blame] | 201 | .WillRepeatedly(Return(json_response)); |
Vitaly Buka | 7476342 | 2015-10-11 00:39:52 -0700 | [diff] [blame] | 202 | callback.Run(std::move(response), nullptr); |
Vitaly Buka | 1728037 | 2015-10-10 17:11:47 -0700 | [diff] [blame] | 203 | }))); |
Vitaly Buka | 11b2f23 | 2015-08-20 13:55:41 -0700 | [diff] [blame] | 204 | } |
| 205 | |
| 206 | void InitConfigStore() { |
Vitaly Buka | 11b2f23 | 2015-08-20 13:55:41 -0700 | [diff] [blame] | 207 | EXPECT_CALL(config_store_, SaveSettings("")).WillRepeatedly(Return()); |
Vitaly Buka | 11b2f23 | 2015-08-20 13:55:41 -0700 | [diff] [blame] | 208 | } |
| 209 | |
| 210 | void InitNetwork() { |
Vitaly Buka | 3ab6f6e | 2015-09-24 13:16:16 -0700 | [diff] [blame] | 211 | EXPECT_CALL(network_, AddConnectionChangedCallback(_)) |
| 212 | .WillRepeatedly(Invoke( |
Vitaly Buka | 1e36367 | 2015-09-25 14:01:16 -0700 | [diff] [blame] | 213 | [this](const provider::Network::ConnectionChangedCallback& cb) { |
Vitaly Buka | ca365fb | 2015-09-15 17:38:41 -0700 | [diff] [blame] | 214 | network_callbacks_.push_back(cb); |
| 215 | })); |
Vitaly Buka | 11b2f23 | 2015-08-20 13:55:41 -0700 | [diff] [blame] | 216 | EXPECT_CALL(network_, GetConnectionState()) |
Vitaly Buka | 35f317d | 2015-09-27 22:54:39 -0700 | [diff] [blame] | 217 | .WillRepeatedly(Return(Network::State::kOffline)); |
Vitaly Buka | ca365fb | 2015-09-15 17:38:41 -0700 | [diff] [blame] | 218 | } |
| 219 | |
Vitaly Buka | beddc60 | 2015-09-24 15:28:03 -0700 | [diff] [blame] | 220 | void InitDnsSd() { |
Vitaly Buka | beddc60 | 2015-09-24 15:28:03 -0700 | [diff] [blame] | 221 | EXPECT_CALL(dns_sd_, PublishService(_, _, _)).WillRepeatedly(Return()); |
| 222 | EXPECT_CALL(dns_sd_, StopPublishing("_privet._tcp")).WillOnce(Return()); |
Vitaly Buka | 11b2f23 | 2015-08-20 13:55:41 -0700 | [diff] [blame] | 223 | } |
| 224 | |
Vitaly Buka | beddc60 | 2015-09-24 15:28:03 -0700 | [diff] [blame] | 225 | void InitDnsSdPublishing(bool registered, const std::string& flags) { |
Vitaly Buka | 87eb788 | 2015-10-27 22:23:49 -0700 | [diff] [blame] | 226 | std::vector<std::string> txt{ |
| 227 | {"id=TEST_DEVICE_ID"}, {"flags=" + flags}, {"mmid=ABCDE"}, |
| 228 | {"services=developmentBoard"}, {"txtvers=3"}, {"ty=TEST_NAME"}}; |
Vitaly Buka | 11b2f23 | 2015-08-20 13:55:41 -0700 | [diff] [blame] | 229 | if (registered) { |
Johan Euphrosine | 0b7bb9f | 2015-09-29 01:11:21 -0700 | [diff] [blame] | 230 | txt.push_back("gcd_id=CLOUD_ID"); |
Vitaly Buka | 11b2f23 | 2015-08-20 13:55:41 -0700 | [diff] [blame] | 231 | |
| 232 | // During registration device may announce itself twice: |
| 233 | // 1. with GCD ID but not connected (DB) |
| 234 | // 2. with GCD ID and connected (BB) |
Vitaly Buka | beddc60 | 2015-09-24 15:28:03 -0700 | [diff] [blame] | 235 | EXPECT_CALL(dns_sd_, PublishService("_privet._tcp", 11, MatchTxt(txt))) |
Vitaly Buka | 11b2f23 | 2015-08-20 13:55:41 -0700 | [diff] [blame] | 236 | .Times(AtMost(1)) |
| 237 | .WillOnce(Return()); |
| 238 | |
Vitaly Buka | 3dc2f53 | 2015-09-08 18:01:32 -0700 | [diff] [blame] | 239 | txt[1] = "flags=BB"; |
Vitaly Buka | 11b2f23 | 2015-08-20 13:55:41 -0700 | [diff] [blame] | 240 | } |
| 241 | |
Vitaly Buka | beddc60 | 2015-09-24 15:28:03 -0700 | [diff] [blame] | 242 | EXPECT_CALL(dns_sd_, PublishService("_privet._tcp", 11, MatchTxt(txt))) |
Vitaly Buka | bced5af | 2015-10-12 17:42:30 -0700 | [diff] [blame] | 243 | .Times(AtMost(1)) |
Vitaly Buka | 3dc2f53 | 2015-09-08 18:01:32 -0700 | [diff] [blame] | 244 | .WillOnce(Return()); |
Vitaly Buka | 11b2f23 | 2015-08-20 13:55:41 -0700 | [diff] [blame] | 245 | } |
| 246 | |
| 247 | void InitHttpServer() { |
| 248 | EXPECT_CALL(http_server_, GetHttpPort()).WillRepeatedly(Return(11)); |
| 249 | EXPECT_CALL(http_server_, GetHttpsPort()).WillRepeatedly(Return(12)); |
Alex Vakulenko | efee3a2 | 2015-11-17 15:08:38 -0800 | [diff] [blame] | 250 | EXPECT_CALL(http_server_, GetRequestTimeout()) |
| 251 | .WillRepeatedly(Return(base::TimeDelta::Max())); |
Vitaly Buka | 11b2f23 | 2015-08-20 13:55:41 -0700 | [diff] [blame] | 252 | EXPECT_CALL(http_server_, GetHttpsCertificateFingerprint()) |
Vitaly Buka | 138aec4 | 2015-10-08 10:17:48 -0700 | [diff] [blame] | 253 | .WillRepeatedly(Return(std::vector<uint8_t>{1, 2, 3})); |
Vitaly Buka | 1a39c81 | 2015-10-08 21:20:58 -0700 | [diff] [blame] | 254 | EXPECT_CALL(http_server_, AddHttpRequestHandler(_, _)) |
| 255 | .WillRepeatedly(Invoke( |
| 256 | [this](const std::string& path_prefix, |
| 257 | const provider::HttpServer::RequestHandlerCallback& cb) { |
Vitaly Buka | e69e4ee | 2015-10-26 18:30:14 -0700 | [diff] [blame] | 258 | http_handlers_[path_prefix] = cb; |
Vitaly Buka | 1a39c81 | 2015-10-08 21:20:58 -0700 | [diff] [blame] | 259 | })); |
| 260 | EXPECT_CALL(http_server_, AddHttpsRequestHandler(_, _)) |
Vitaly Buka | 01893cc | 2015-10-08 19:58:00 -0700 | [diff] [blame] | 261 | .WillRepeatedly(Invoke( |
| 262 | [this](const std::string& path_prefix, |
| 263 | const provider::HttpServer::RequestHandlerCallback& cb) { |
Vitaly Buka | e69e4ee | 2015-10-26 18:30:14 -0700 | [diff] [blame] | 264 | https_handlers_[path_prefix] = cb; |
Vitaly Buka | 1e36367 | 2015-09-25 14:01:16 -0700 | [diff] [blame] | 265 | })); |
Vitaly Buka | 11b2f23 | 2015-08-20 13:55:41 -0700 | [diff] [blame] | 266 | } |
| 267 | |
Vitaly Buka | ca365fb | 2015-09-15 17:38:41 -0700 | [diff] [blame] | 268 | void InitDefaultExpectations() { |
Vitaly Buka | 11b2f23 | 2015-08-20 13:55:41 -0700 | [diff] [blame] | 269 | InitConfigStore(); |
| 270 | InitNetwork(); |
Vitaly Buka | a05eadb | 2015-09-29 16:38:24 -0700 | [diff] [blame] | 271 | EXPECT_CALL(wifi_, StartAccessPoint(MatchesRegex("TEST_NAME.*prv"))) |
Vitaly Buka | ca365fb | 2015-09-15 17:38:41 -0700 | [diff] [blame] | 272 | .WillOnce(Return()); |
Vitaly Buka | 11b2f23 | 2015-08-20 13:55:41 -0700 | [diff] [blame] | 273 | InitHttpServer(); |
Vitaly Buka | beddc60 | 2015-09-24 15:28:03 -0700 | [diff] [blame] | 274 | InitDnsSd(); |
Vitaly Buka | ca365fb | 2015-09-15 17:38:41 -0700 | [diff] [blame] | 275 | } |
Vitaly Buka | 11b2f23 | 2015-08-20 13:55:41 -0700 | [diff] [blame] | 276 | |
Vitaly Buka | ca365fb | 2015-09-15 17:38:41 -0700 | [diff] [blame] | 277 | void StartDevice() { |
Vitaly Buka | c5bdd17 | 2015-10-01 19:48:51 -0700 | [diff] [blame] | 278 | device_ = weave::Device::Create(&config_store_, &task_runner_, |
| 279 | &http_client_, &network_, &dns_sd_, |
| 280 | &http_server_, &wifi_, &bluetooth_); |
Vitaly Buka | 11b2f23 | 2015-08-20 13:55:41 -0700 | [diff] [blame] | 281 | |
Vitaly Buka | e69e4ee | 2015-10-26 18:30:14 -0700 | [diff] [blame] | 282 | EXPECT_EQ((std::set<std::string>{ |
| 283 | "/privet/info", "/privet/v3/pairing/cancel", |
| 284 | "/privet/v3/pairing/confirm", "/privet/v3/pairing/start"}), |
| 285 | GetKeys(http_handlers_)); |
| 286 | EXPECT_EQ((std::set<std::string>{ |
Alex Vakulenko | efee3a2 | 2015-11-17 15:08:38 -0800 | [diff] [blame] | 287 | "/privet/info", "/privet/v3/auth", |
| 288 | "/privet/v3/checkForUpdates", "/privet/v3/commandDefs", |
Vitaly Buka | e69e4ee | 2015-10-26 18:30:14 -0700 | [diff] [blame] | 289 | "/privet/v3/commands/cancel", "/privet/v3/commands/execute", |
| 290 | "/privet/v3/commands/list", "/privet/v3/commands/status", |
Alex Vakulenko | 551a82b | 2015-12-07 14:46:12 -0800 | [diff] [blame] | 291 | "/privet/v3/components", "/privet/v3/pairing/cancel", |
| 292 | "/privet/v3/pairing/confirm", "/privet/v3/pairing/start", |
| 293 | "/privet/v3/setup/start", "/privet/v3/setup/status", |
| 294 | "/privet/v3/state", "/privet/v3/traits"}), |
Vitaly Buka | e69e4ee | 2015-10-26 18:30:14 -0700 | [diff] [blame] | 295 | GetKeys(https_handlers_)); |
| 296 | |
Alex Vakulenko | 551a82b | 2015-12-07 14:46:12 -0800 | [diff] [blame] | 297 | device_->AddTraitDefinitionsFromJson(kTraitDefs); |
| 298 | EXPECT_TRUE(device_->AddComponent("myComponent", {"trait1", "trait2"}, |
| 299 | nullptr)); |
| 300 | EXPECT_TRUE(device_->SetStatePropertiesFromJson( |
| 301 | "myComponent", R"({"trait2": {"battery_level":44}})", nullptr)); |
Vitaly Buka | 0ac6ca6 | 2015-10-06 19:16:29 -0700 | [diff] [blame] | 302 | |
Vitaly Buka | 11b2f23 | 2015-08-20 13:55:41 -0700 | [diff] [blame] | 303 | task_runner_.Run(); |
| 304 | } |
| 305 | |
Vitaly Buka | 35f317d | 2015-09-27 22:54:39 -0700 | [diff] [blame] | 306 | void NotifyNetworkChanged(provider::Network::State state, |
Vitaly Buka | 1e36367 | 2015-09-25 14:01:16 -0700 | [diff] [blame] | 307 | base::TimeDelta delay) { |
Vitaly Buka | 65e1f21 | 2015-11-05 15:54:05 -0800 | [diff] [blame] | 308 | auto task = [this, state] { |
| 309 | EXPECT_CALL(network_, GetConnectionState()).WillRepeatedly(Return(state)); |
| 310 | for (const auto& cb : network_callbacks_) |
| 311 | cb.Run(); |
| 312 | }; |
| 313 | |
| 314 | task_runner_.PostDelayedTask(FROM_HERE, base::Bind(task), delay); |
Vitaly Buka | 3884ce1 | 2015-09-21 13:48:48 -0700 | [diff] [blame] | 315 | } |
| 316 | |
Vitaly Buka | e69e4ee | 2015-10-26 18:30:14 -0700 | [diff] [blame] | 317 | std::map<std::string, provider::HttpServer::RequestHandlerCallback> |
| 318 | http_handlers_; |
| 319 | std::map<std::string, provider::HttpServer::RequestHandlerCallback> |
| 320 | https_handlers_; |
Vitaly Buka | 11b2f23 | 2015-08-20 13:55:41 -0700 | [diff] [blame] | 321 | |
Vitaly Buka | 1e36367 | 2015-09-25 14:01:16 -0700 | [diff] [blame] | 322 | StrictMock<provider::test::MockConfigStore> config_store_; |
Vitaly Buka | 727f3e6 | 2015-09-25 17:33:43 -0700 | [diff] [blame] | 323 | StrictMock<provider::test::FakeTaskRunner> task_runner_; |
Vitaly Buka | 1e36367 | 2015-09-25 14:01:16 -0700 | [diff] [blame] | 324 | StrictMock<provider::test::MockHttpClient> http_client_; |
| 325 | StrictMock<provider::test::MockNetwork> network_; |
| 326 | StrictMock<provider::test::MockDnsServiceDiscovery> dns_sd_; |
| 327 | StrictMock<provider::test::MockHttpServer> http_server_; |
| 328 | StrictMock<provider::test::MockWifi> wifi_; |
| 329 | StrictMock<provider::test::MockBluetooth> bluetooth_; |
Vitaly Buka | 11b2f23 | 2015-08-20 13:55:41 -0700 | [diff] [blame] | 330 | |
Vitaly Buka | 1e36367 | 2015-09-25 14:01:16 -0700 | [diff] [blame] | 331 | std::vector<provider::Network::ConnectionChangedCallback> network_callbacks_; |
Vitaly Buka | ca365fb | 2015-09-15 17:38:41 -0700 | [diff] [blame] | 332 | |
Vitaly Buka | 11b2f23 | 2015-08-20 13:55:41 -0700 | [diff] [blame] | 333 | std::unique_ptr<weave::Device> device_; |
| 334 | }; |
| 335 | |
Vitaly Buka | ef213d7 | 2015-10-07 15:54:58 -0700 | [diff] [blame] | 336 | TEST_F(WeaveTest, Mocks) { |
Vitaly Buka | a8ece8f | 2015-10-05 13:30:23 -0700 | [diff] [blame] | 337 | // Test checks if mock implements entire interface and mock can be |
| 338 | // instantiated. |
| 339 | test::MockDevice device; |
Vitaly Buka | ef213d7 | 2015-10-07 15:54:58 -0700 | [diff] [blame] | 340 | test::MockCommand command; |
Vitaly Buka | a8ece8f | 2015-10-05 13:30:23 -0700 | [diff] [blame] | 341 | } |
| 342 | |
Vitaly Buka | 11b2f23 | 2015-08-20 13:55:41 -0700 | [diff] [blame] | 343 | TEST_F(WeaveTest, StartMinimal) { |
Vitaly Buka | 11b2f23 | 2015-08-20 13:55:41 -0700 | [diff] [blame] | 344 | InitConfigStore(); |
Vitaly Buka | c5bdd17 | 2015-10-01 19:48:51 -0700 | [diff] [blame] | 345 | device_ = weave::Device::Create(&config_store_, &task_runner_, &http_client_, |
| 346 | &network_, nullptr, nullptr, &wifi_, nullptr); |
Vitaly Buka | b1041e7 | 2015-09-21 15:26:51 -0700 | [diff] [blame] | 347 | } |
| 348 | |
| 349 | TEST_F(WeaveTest, StartNoWifi) { |
| 350 | InitConfigStore(); |
| 351 | InitNetwork(); |
| 352 | InitHttpServer(); |
Vitaly Buka | beddc60 | 2015-09-24 15:28:03 -0700 | [diff] [blame] | 353 | InitDnsSd(); |
| 354 | InitDnsSdPublishing(false, "CB"); |
Vitaly Buka | b1041e7 | 2015-09-21 15:26:51 -0700 | [diff] [blame] | 355 | |
Vitaly Buka | c5bdd17 | 2015-10-01 19:48:51 -0700 | [diff] [blame] | 356 | device_ = weave::Device::Create(&config_store_, &task_runner_, &http_client_, |
| 357 | &network_, &dns_sd_, &http_server_, nullptr, |
| 358 | &bluetooth_); |
Alex Vakulenko | 551a82b | 2015-12-07 14:46:12 -0800 | [diff] [blame] | 359 | device_->AddTraitDefinitionsFromJson(kTraitDefs); |
| 360 | EXPECT_TRUE(device_->AddComponent("myComponent", {"trait1", "trait2"}, |
| 361 | nullptr)); |
Vitaly Buka | b1041e7 | 2015-09-21 15:26:51 -0700 | [diff] [blame] | 362 | |
Vitaly Buka | b1041e7 | 2015-09-21 15:26:51 -0700 | [diff] [blame] | 363 | task_runner_.Run(); |
Vitaly Buka | 11b2f23 | 2015-08-20 13:55:41 -0700 | [diff] [blame] | 364 | } |
| 365 | |
Vitaly Buka | ca365fb | 2015-09-15 17:38:41 -0700 | [diff] [blame] | 366 | class WeaveBasicTest : public WeaveTest { |
| 367 | public: |
| 368 | void SetUp() override { |
| 369 | WeaveTest::SetUp(); |
Vitaly Buka | 3884ce1 | 2015-09-21 13:48:48 -0700 | [diff] [blame] | 370 | |
Vitaly Buka | ca365fb | 2015-09-15 17:38:41 -0700 | [diff] [blame] | 371 | InitDefaultExpectations(); |
Vitaly Buka | beddc60 | 2015-09-24 15:28:03 -0700 | [diff] [blame] | 372 | InitDnsSdPublishing(false, "DB"); |
Vitaly Buka | ca365fb | 2015-09-15 17:38:41 -0700 | [diff] [blame] | 373 | } |
| 374 | }; |
| 375 | |
| 376 | TEST_F(WeaveBasicTest, Start) { |
Vitaly Buka | 11b2f23 | 2015-08-20 13:55:41 -0700 | [diff] [blame] | 377 | StartDevice(); |
| 378 | } |
| 379 | |
Vitaly Buka | ca365fb | 2015-09-15 17:38:41 -0700 | [diff] [blame] | 380 | TEST_F(WeaveBasicTest, Register) { |
Vitaly Buka | 7476342 | 2015-10-11 00:39:52 -0700 | [diff] [blame] | 381 | EXPECT_CALL(network_, OpenSslSocket(_, _, _)).WillRepeatedly(Return()); |
Vitaly Buka | 11b2f23 | 2015-08-20 13:55:41 -0700 | [diff] [blame] | 382 | StartDevice(); |
| 383 | |
| 384 | auto draft = CreateDictionaryValue(kDeviceResource); |
| 385 | auto response = CreateDictionaryValue(kRegistrationResponse); |
| 386 | response->Set("deviceDraft", draft->DeepCopy()); |
Vitaly Buka | 0d7aac8 | 2015-11-16 23:02:24 -0800 | [diff] [blame] | 387 | ExpectRequest(HttpClient::Method::kPatch, |
| 388 | "https://www.googleapis.com/weave/v1/registrationTickets/" |
| 389 | "TICKET_ID?key=TEST_API_KEY", |
| 390 | ValueToString(*response)); |
Vitaly Buka | 11b2f23 | 2015-08-20 13:55:41 -0700 | [diff] [blame] | 391 | |
| 392 | response = CreateDictionaryValue(kRegistrationFinalResponse); |
| 393 | response->Set("deviceDraft", draft->DeepCopy()); |
Vitaly Buka | 0d7aac8 | 2015-11-16 23:02:24 -0800 | [diff] [blame] | 394 | ExpectRequest(HttpClient::Method::kPost, |
| 395 | "https://www.googleapis.com/weave/v1/registrationTickets/" |
| 396 | "TICKET_ID/finalize?key=TEST_API_KEY", |
| 397 | ValueToString(*response)); |
Vitaly Buka | 11b2f23 | 2015-08-20 13:55:41 -0700 | [diff] [blame] | 398 | |
Vitaly Buka | 1a42e14 | 2015-10-10 18:15:15 -0700 | [diff] [blame] | 399 | ExpectRequest(HttpClient::Method::kPost, |
| 400 | "https://accounts.google.com/o/oauth2/token", |
Vitaly Buka | 11b2f23 | 2015-08-20 13:55:41 -0700 | [diff] [blame] | 401 | kAuthTokenResponse); |
| 402 | |
Vitaly Buka | beddc60 | 2015-09-24 15:28:03 -0700 | [diff] [blame] | 403 | InitDnsSdPublishing(true, "DB"); |
Vitaly Buka | 11b2f23 | 2015-08-20 13:55:41 -0700 | [diff] [blame] | 404 | |
Vitaly Buka | 12870bd | 2015-10-08 23:49:39 -0700 | [diff] [blame] | 405 | bool done = false; |
Vitaly Buka | 7476342 | 2015-10-11 00:39:52 -0700 | [diff] [blame] | 406 | device_->Register("TICKET_ID", base::Bind([this, &done](ErrorPtr error) { |
| 407 | EXPECT_FALSE(error); |
Vitaly Buka | 12870bd | 2015-10-08 23:49:39 -0700 | [diff] [blame] | 408 | done = true; |
| 409 | task_runner_.Break(); |
| 410 | EXPECT_EQ("CLOUD_ID", device_->GetSettings().cloud_id); |
Vitaly Buka | 7476342 | 2015-10-11 00:39:52 -0700 | [diff] [blame] | 411 | })); |
Vitaly Buka | 12870bd | 2015-10-08 23:49:39 -0700 | [diff] [blame] | 412 | task_runner_.Run(); |
| 413 | EXPECT_TRUE(done); |
Vitaly Buka | 11b2f23 | 2015-08-20 13:55:41 -0700 | [diff] [blame] | 414 | } |
| 415 | |
Vitaly Buka | ca365fb | 2015-09-15 17:38:41 -0700 | [diff] [blame] | 416 | class WeaveWiFiSetupTest : public WeaveTest { |
| 417 | public: |
| 418 | void SetUp() override { |
| 419 | WeaveTest::SetUp(); |
| 420 | |
| 421 | InitConfigStore(); |
| 422 | InitHttpServer(); |
| 423 | InitNetwork(); |
Vitaly Buka | beddc60 | 2015-09-24 15:28:03 -0700 | [diff] [blame] | 424 | InitDnsSd(); |
Vitaly Buka | ca365fb | 2015-09-15 17:38:41 -0700 | [diff] [blame] | 425 | |
| 426 | EXPECT_CALL(network_, GetConnectionState()) |
Vitaly Buka | efad5b2 | 2015-10-08 10:02:14 -0700 | [diff] [blame] | 427 | .WillRepeatedly(Return(provider::Network::State::kOnline)); |
Vitaly Buka | ca365fb | 2015-09-15 17:38:41 -0700 | [diff] [blame] | 428 | } |
| 429 | }; |
| 430 | |
| 431 | TEST_F(WeaveWiFiSetupTest, StartOnlineNoPrevSsid) { |
| 432 | StartDevice(); |
| 433 | |
| 434 | // Short disconnect. |
Vitaly Buka | 35f317d | 2015-09-27 22:54:39 -0700 | [diff] [blame] | 435 | NotifyNetworkChanged(provider::Network::State::kOffline, {}); |
Vitaly Buka | efad5b2 | 2015-10-08 10:02:14 -0700 | [diff] [blame] | 436 | NotifyNetworkChanged(provider::Network::State::kOnline, |
Vitaly Buka | 3884ce1 | 2015-09-21 13:48:48 -0700 | [diff] [blame] | 437 | base::TimeDelta::FromSeconds(10)); |
Vitaly Buka | ca365fb | 2015-09-15 17:38:41 -0700 | [diff] [blame] | 438 | task_runner_.Run(); |
| 439 | |
| 440 | // Long disconnect. |
Vitaly Buka | 35f317d | 2015-09-27 22:54:39 -0700 | [diff] [blame] | 441 | NotifyNetworkChanged(Network::State::kOffline, {}); |
Vitaly Buka | ca365fb | 2015-09-15 17:38:41 -0700 | [diff] [blame] | 442 | auto offline_from = task_runner_.GetClock()->Now(); |
Vitaly Buka | a05eadb | 2015-09-29 16:38:24 -0700 | [diff] [blame] | 443 | EXPECT_CALL(wifi_, StartAccessPoint(MatchesRegex("TEST_NAME.*prv"))) |
Vitaly Buka | ca365fb | 2015-09-15 17:38:41 -0700 | [diff] [blame] | 444 | .WillOnce(InvokeWithoutArgs([this, offline_from]() { |
| 445 | EXPECT_GT(task_runner_.GetClock()->Now() - offline_from, |
| 446 | base::TimeDelta::FromMinutes(1)); |
Vitaly Buka | caf42bd | 2015-09-16 11:23:23 -0700 | [diff] [blame] | 447 | task_runner_.Break(); |
Vitaly Buka | ca365fb | 2015-09-15 17:38:41 -0700 | [diff] [blame] | 448 | })); |
| 449 | task_runner_.Run(); |
| 450 | } |
| 451 | |
| 452 | // If device has previously configured WiFi it will run AP for limited time |
| 453 | // after which it will try to re-connect. |
| 454 | TEST_F(WeaveWiFiSetupTest, StartOnlineWithPrevSsid) { |
| 455 | EXPECT_CALL(config_store_, LoadSettings()) |
| 456 | .WillRepeatedly(Return(R"({"last_configured_ssid": "TEST_ssid"})")); |
| 457 | StartDevice(); |
| 458 | |
| 459 | // Long disconnect. |
Vitaly Buka | 35f317d | 2015-09-27 22:54:39 -0700 | [diff] [blame] | 460 | NotifyNetworkChanged(Network::State::kOffline, {}); |
Vitaly Buka | ca365fb | 2015-09-15 17:38:41 -0700 | [diff] [blame] | 461 | |
| 462 | for (int i = 0; i < 5; ++i) { |
| 463 | auto offline_from = task_runner_.GetClock()->Now(); |
| 464 | // Temporarily offline mode. |
Vitaly Buka | a05eadb | 2015-09-29 16:38:24 -0700 | [diff] [blame] | 465 | EXPECT_CALL(wifi_, StartAccessPoint(MatchesRegex("TEST_NAME.*prv"))) |
Vitaly Buka | ca365fb | 2015-09-15 17:38:41 -0700 | [diff] [blame] | 466 | .WillOnce(InvokeWithoutArgs([this, &offline_from]() { |
| 467 | EXPECT_GT(task_runner_.GetClock()->Now() - offline_from, |
| 468 | base::TimeDelta::FromMinutes(1)); |
| 469 | task_runner_.Break(); |
| 470 | })); |
| 471 | task_runner_.Run(); |
| 472 | |
| 473 | // Try to reconnect again. |
| 474 | offline_from = task_runner_.GetClock()->Now(); |
Vitaly Buka | 1fd619a | 2015-09-24 11:46:05 -0700 | [diff] [blame] | 475 | EXPECT_CALL(wifi_, StopAccessPoint()) |
Vitaly Buka | ca365fb | 2015-09-15 17:38:41 -0700 | [diff] [blame] | 476 | .WillOnce(InvokeWithoutArgs([this, offline_from]() { |
| 477 | EXPECT_GT(task_runner_.GetClock()->Now() - offline_from, |
| 478 | base::TimeDelta::FromMinutes(5)); |
| 479 | task_runner_.Break(); |
| 480 | })); |
| 481 | task_runner_.Run(); |
| 482 | } |
| 483 | |
Vitaly Buka | efad5b2 | 2015-10-08 10:02:14 -0700 | [diff] [blame] | 484 | NotifyNetworkChanged(Network::State::kOnline, {}); |
Vitaly Buka | ca365fb | 2015-09-15 17:38:41 -0700 | [diff] [blame] | 485 | task_runner_.Run(); |
| 486 | } |
| 487 | |
Vitaly Buka | caf42bd | 2015-09-16 11:23:23 -0700 | [diff] [blame] | 488 | TEST_F(WeaveWiFiSetupTest, StartOfflineWithSsid) { |
| 489 | EXPECT_CALL(config_store_, LoadSettings()) |
| 490 | .WillRepeatedly(Return(R"({"last_configured_ssid": "TEST_ssid"})")); |
| 491 | EXPECT_CALL(network_, GetConnectionState()) |
Vitaly Buka | 35f317d | 2015-09-27 22:54:39 -0700 | [diff] [blame] | 492 | .WillRepeatedly(Return(Network::State::kOffline)); |
Vitaly Buka | caf42bd | 2015-09-16 11:23:23 -0700 | [diff] [blame] | 493 | |
| 494 | auto offline_from = task_runner_.GetClock()->Now(); |
Vitaly Buka | a05eadb | 2015-09-29 16:38:24 -0700 | [diff] [blame] | 495 | EXPECT_CALL(wifi_, StartAccessPoint(MatchesRegex("TEST_NAME.*prv"))) |
Vitaly Buka | caf42bd | 2015-09-16 11:23:23 -0700 | [diff] [blame] | 496 | .WillOnce(InvokeWithoutArgs([this, &offline_from]() { |
| 497 | EXPECT_GT(task_runner_.GetClock()->Now() - offline_from, |
| 498 | base::TimeDelta::FromMinutes(1)); |
| 499 | task_runner_.Break(); |
| 500 | })); |
| 501 | |
| 502 | StartDevice(); |
| 503 | } |
| 504 | |
Vitaly Buka | 65e1f21 | 2015-11-05 15:54:05 -0800 | [diff] [blame] | 505 | TEST_F(WeaveWiFiSetupTest, OfflineLongTimeWithNoSsid) { |
| 506 | EXPECT_CALL(network_, GetConnectionState()) |
| 507 | .WillRepeatedly(Return(Network::State::kOffline)); |
| 508 | NotifyNetworkChanged(provider::Network::State::kOnline, |
| 509 | base::TimeDelta::FromHours(15)); |
| 510 | |
| 511 | { |
| 512 | InSequence s; |
| 513 | auto time_stamp = task_runner_.GetClock()->Now(); |
| 514 | |
| 515 | EXPECT_CALL(wifi_, StartAccessPoint(MatchesRegex("TEST_NAME.*prv"))) |
| 516 | .WillOnce(InvokeWithoutArgs([this, &time_stamp]() { |
| 517 | EXPECT_LE(task_runner_.GetClock()->Now() - time_stamp, |
| 518 | base::TimeDelta::FromMinutes(1)); |
| 519 | time_stamp = task_runner_.GetClock()->Now(); |
| 520 | })); |
| 521 | |
| 522 | EXPECT_CALL(wifi_, StopAccessPoint()) |
| 523 | .WillOnce(InvokeWithoutArgs([this, &time_stamp]() { |
| 524 | EXPECT_GT(task_runner_.GetClock()->Now() - time_stamp, |
| 525 | base::TimeDelta::FromMinutes(5)); |
| 526 | time_stamp = task_runner_.GetClock()->Now(); |
| 527 | task_runner_.Break(); |
| 528 | })); |
| 529 | } |
| 530 | |
| 531 | StartDevice(); |
| 532 | } |
| 533 | |
| 534 | TEST_F(WeaveWiFiSetupTest, OfflineLongTimeWithSsid) { |
| 535 | EXPECT_CALL(config_store_, LoadSettings()) |
| 536 | .WillRepeatedly(Return(R"({"last_configured_ssid": "TEST_ssid"})")); |
| 537 | EXPECT_CALL(network_, GetConnectionState()) |
| 538 | .WillRepeatedly(Return(Network::State::kOffline)); |
| 539 | NotifyNetworkChanged(provider::Network::State::kOnline, |
| 540 | base::TimeDelta::FromHours(15)); |
| 541 | |
| 542 | { |
| 543 | InSequence s; |
| 544 | auto time_stamp = task_runner_.GetClock()->Now(); |
| 545 | for (size_t i = 0; i < 10; ++i) { |
| 546 | EXPECT_CALL(wifi_, StartAccessPoint(MatchesRegex("TEST_NAME.*prv"))) |
| 547 | .WillOnce(InvokeWithoutArgs([this, &time_stamp]() { |
| 548 | EXPECT_GT(task_runner_.GetClock()->Now() - time_stamp, |
| 549 | base::TimeDelta::FromMinutes(1)); |
| 550 | time_stamp = task_runner_.GetClock()->Now(); |
| 551 | })); |
| 552 | |
| 553 | EXPECT_CALL(wifi_, StopAccessPoint()) |
| 554 | .WillOnce(InvokeWithoutArgs([this, &time_stamp]() { |
| 555 | EXPECT_GT(task_runner_.GetClock()->Now() - time_stamp, |
| 556 | base::TimeDelta::FromMinutes(5)); |
| 557 | time_stamp = task_runner_.GetClock()->Now(); |
| 558 | })); |
| 559 | } |
| 560 | |
| 561 | EXPECT_CALL(wifi_, StartAccessPoint(MatchesRegex("TEST_NAME.*prv"))) |
| 562 | .WillOnce(InvokeWithoutArgs([this]() { task_runner_.Break(); })); |
| 563 | } |
| 564 | |
| 565 | StartDevice(); |
| 566 | } |
| 567 | |
Vitaly Buka | 11b2f23 | 2015-08-20 13:55:41 -0700 | [diff] [blame] | 568 | } // namespace weave |