Vitaly Buka | 4615e0d | 2015-10-14 15:35:12 -0700 | [diff] [blame] | 1 | // Copyright 2015 The Weave Authors. All rights reserved. |
Alex Vakulenko | be4254b | 2015-06-26 11:34:03 -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 | |
Stefan Sauer | 2d16dfa | 2015-09-25 17:08:35 +0200 | [diff] [blame] | 5 | #include "src/commands/cloud_command_proxy.h" |
Alex Vakulenko | be4254b | 2015-06-26 11:34:03 -0700 | [diff] [blame] | 6 | |
| 7 | #include <memory> |
| 8 | #include <queue> |
| 9 | |
Alex Vakulenko | ebfa60b | 2016-02-01 11:52:30 -0800 | [diff] [blame] | 10 | #include <base/bind.h> |
Alex Vakulenko | be4254b | 2015-06-26 11:34:03 -0700 | [diff] [blame] | 11 | #include <gmock/gmock.h> |
| 12 | #include <gtest/gtest.h> |
Vitaly Buka | 727f3e6 | 2015-09-25 17:33:43 -0700 | [diff] [blame] | 13 | #include <weave/provider/test/fake_task_runner.h> |
Alex Vakulenko | 8a05beb | 2015-11-24 17:13:20 -0800 | [diff] [blame] | 14 | #include <weave/test/unittest_utils.h> |
Alex Vakulenko | be4254b | 2015-06-26 11:34:03 -0700 | [diff] [blame] | 15 | |
Stefan Sauer | 2d16dfa | 2015-09-25 17:08:35 +0200 | [diff] [blame] | 16 | #include "src/commands/command_instance.h" |
Vitaly Buka | 012cd98 | 2016-02-22 17:18:49 -0800 | [diff] [blame] | 17 | #include "src/test/mock_component_manager.h" |
Alex Vakulenko | be4254b | 2015-06-26 11:34:03 -0700 | [diff] [blame] | 18 | |
Vitaly Buka | ff1d186 | 2015-10-07 20:40:36 -0700 | [diff] [blame] | 19 | using testing::_; |
Alex Vakulenko | ebfa60b | 2016-02-01 11:52:30 -0800 | [diff] [blame] | 20 | using testing::AnyNumber; |
Vitaly Buka | ff1d186 | 2015-10-07 20:40:36 -0700 | [diff] [blame] | 21 | using testing::DoAll; |
Alex Vakulenko | be4254b | 2015-06-26 11:34:03 -0700 | [diff] [blame] | 22 | using testing::Invoke; |
| 23 | using testing::Return; |
| 24 | using testing::ReturnPointee; |
Vitaly Buka | ff1d186 | 2015-10-07 20:40:36 -0700 | [diff] [blame] | 25 | using testing::SaveArg; |
Alex Vakulenko | be4254b | 2015-06-26 11:34:03 -0700 | [diff] [blame] | 26 | |
Vitaly Buka | b6f015a | 2015-07-09 14:59:23 -0700 | [diff] [blame] | 27 | namespace weave { |
Alex Vakulenko | be4254b | 2015-06-26 11:34:03 -0700 | [diff] [blame] | 28 | |
Vitaly Buka | 0f6b2ec | 2015-08-20 15:35:19 -0700 | [diff] [blame] | 29 | using test::CreateDictionaryValue; |
| 30 | using test::CreateValue; |
Alex Vakulenko | be4254b | 2015-06-26 11:34:03 -0700 | [diff] [blame] | 31 | |
| 32 | namespace { |
| 33 | |
| 34 | const char kCmdID[] = "abcd"; |
| 35 | |
| 36 | MATCHER_P(MatchJson, str, "") { |
| 37 | return arg.Equals(CreateValue(str).get()); |
| 38 | } |
| 39 | |
| 40 | class MockCloudCommandUpdateInterface : public CloudCommandUpdateInterface { |
| 41 | public: |
Vitaly Buka | 7476342 | 2015-10-11 00:39:52 -0700 | [diff] [blame] | 42 | MOCK_METHOD3(UpdateCommand, |
Vitaly Buka | a647c85 | 2015-07-06 14:51:01 -0700 | [diff] [blame] | 43 | void(const std::string&, |
| 44 | const base::DictionaryValue&, |
Vitaly Buka | 7476342 | 2015-10-11 00:39:52 -0700 | [diff] [blame] | 45 | const DoneCallback&)); |
Alex Vakulenko | be4254b | 2015-06-26 11:34:03 -0700 | [diff] [blame] | 46 | }; |
| 47 | |
Alex Vakulenko | be4254b | 2015-06-26 11:34:03 -0700 | [diff] [blame] | 48 | // Test back-off entry that uses the test clock. |
Vitaly Buka | 0f80f7c | 2015-08-13 00:57:25 -0700 | [diff] [blame] | 49 | class TestBackoffEntry : public BackoffEntry { |
Alex Vakulenko | be4254b | 2015-06-26 11:34:03 -0700 | [diff] [blame] | 50 | public: |
| 51 | TestBackoffEntry(const Policy* const policy, base::Clock* clock) |
Vitaly Buka | 0f80f7c | 2015-08-13 00:57:25 -0700 | [diff] [blame] | 52 | : BackoffEntry{policy}, clock_{clock} { |
Alex Vakulenko | be4254b | 2015-06-26 11:34:03 -0700 | [diff] [blame] | 53 | creation_time_ = clock->Now(); |
| 54 | } |
| 55 | |
| 56 | private: |
Vitaly Buka | 0f80f7c | 2015-08-13 00:57:25 -0700 | [diff] [blame] | 57 | // Override from BackoffEntry to use the custom test clock for |
Alex Vakulenko | be4254b | 2015-06-26 11:34:03 -0700 | [diff] [blame] | 58 | // the backoff calculations. |
| 59 | base::TimeTicks ImplGetTimeNow() const override { |
| 60 | return base::TimeTicks::FromInternalValue(clock_->Now().ToInternalValue()); |
| 61 | } |
| 62 | |
| 63 | base::Clock* clock_; |
| 64 | base::Time creation_time_; |
| 65 | }; |
| 66 | |
Alex Vakulenko | ebfa60b | 2016-02-01 11:52:30 -0800 | [diff] [blame] | 67 | class CloudCommandProxyWrapper : public CloudCommandProxy { |
| 68 | public: |
| 69 | CloudCommandProxyWrapper(CommandInstance* command_instance, |
| 70 | CloudCommandUpdateInterface* cloud_command_updater, |
| 71 | ComponentManager* component_manager, |
| 72 | std::unique_ptr<BackoffEntry> backoff_entry, |
| 73 | provider::TaskRunner* task_runner, |
| 74 | const base::Closure& destruct_callback) |
| 75 | : CloudCommandProxy{command_instance, cloud_command_updater, |
| 76 | component_manager, std::move(backoff_entry), |
| 77 | task_runner}, |
| 78 | destruct_callback_{destruct_callback} {} |
| 79 | |
Vitaly Buka | 5e94dc8 | 2016-03-01 13:03:01 -0800 | [diff] [blame] | 80 | ~CloudCommandProxyWrapper() { destruct_callback_.Run(); } |
Alex Vakulenko | ebfa60b | 2016-02-01 11:52:30 -0800 | [diff] [blame] | 81 | |
| 82 | private: |
| 83 | base::Closure destruct_callback_; |
| 84 | }; |
| 85 | |
Alex Vakulenko | be4254b | 2015-06-26 11:34:03 -0700 | [diff] [blame] | 86 | class CloudCommandProxyTest : public ::testing::Test { |
| 87 | protected: |
| 88 | void SetUp() override { |
Alex Vakulenko | d91d625 | 2015-12-05 17:14:39 -0800 | [diff] [blame] | 89 | // Set up the test ComponentManager. |
Vitaly Buka | 34668e7 | 2015-12-15 14:46:47 -0800 | [diff] [blame] | 90 | auto callback = [this]( |
| 91 | const base::Callback<void(ComponentManager::UpdateID)>& call) { |
Alex Vakulenko | be4254b | 2015-06-26 11:34:03 -0700 | [diff] [blame] | 92 | return callbacks_.Add(call).release(); |
| 93 | }; |
Alex Vakulenko | d91d625 | 2015-12-05 17:14:39 -0800 | [diff] [blame] | 94 | EXPECT_CALL(component_manager_, MockAddServerStateUpdatedCallback(_)) |
Alex Vakulenko | be4254b | 2015-06-26 11:34:03 -0700 | [diff] [blame] | 95 | .WillRepeatedly(Invoke(callback)); |
Alex Vakulenko | d91d625 | 2015-12-05 17:14:39 -0800 | [diff] [blame] | 96 | EXPECT_CALL(component_manager_, GetLastStateChangeId()) |
Alex Vakulenko | be4254b | 2015-06-26 11:34:03 -0700 | [diff] [blame] | 97 | .WillRepeatedly(testing::ReturnPointee(¤t_state_update_id_)); |
| 98 | |
Alex Vakulenko | be4254b | 2015-06-26 11:34:03 -0700 | [diff] [blame] | 99 | CreateCommandInstance(); |
| 100 | } |
| 101 | |
| 102 | void CreateCommandInstance() { |
| 103 | auto command_json = CreateDictionaryValue(R"({ |
| 104 | 'name': 'calc.add', |
| 105 | 'id': 'abcd', |
| 106 | 'parameters': { |
| 107 | 'value1': 10, |
| 108 | 'value2': 20 |
| 109 | } |
| 110 | })"); |
| 111 | CHECK(command_json.get()); |
| 112 | |
Alex Vakulenko | 88f55d8 | 2015-12-03 15:30:27 -0800 | [diff] [blame] | 113 | command_instance_ = CommandInstance::FromJson( |
| 114 | command_json.get(), Command::Origin::kCloud, nullptr, nullptr); |
Alex Vakulenko | be4254b | 2015-06-26 11:34:03 -0700 | [diff] [blame] | 115 | CHECK(command_instance_.get()); |
| 116 | |
| 117 | // Backoff - start at 1s and double with each backoff attempt and no jitter. |
Vitaly Buka | 0f80f7c | 2015-08-13 00:57:25 -0700 | [diff] [blame] | 118 | static const BackoffEntry::Policy policy{0, 1000, 2.0, 0.0, |
| 119 | 20000, -1, false}; |
Alex Vakulenko | be4254b | 2015-06-26 11:34:03 -0700 | [diff] [blame] | 120 | std::unique_ptr<TestBackoffEntry> backoff{ |
Vitaly Buka | 823fdda | 2015-08-13 00:33:00 -0700 | [diff] [blame] | 121 | new TestBackoffEntry{&policy, task_runner_.GetClock()}}; |
Alex Vakulenko | be4254b | 2015-06-26 11:34:03 -0700 | [diff] [blame] | 122 | |
| 123 | // Finally construct the CloudCommandProxy we are going to test here. |
Alex Vakulenko | ebfa60b | 2016-02-01 11:52:30 -0800 | [diff] [blame] | 124 | std::unique_ptr<CloudCommandProxy> proxy{new CloudCommandProxyWrapper{ |
Alex Vakulenko | d91d625 | 2015-12-05 17:14:39 -0800 | [diff] [blame] | 125 | command_instance_.get(), &cloud_updater_, &component_manager_, |
Alex Vakulenko | ebfa60b | 2016-02-01 11:52:30 -0800 | [diff] [blame] | 126 | std::move(backoff), &task_runner_, |
| 127 | base::Bind(&CloudCommandProxyTest::OnProxyDestroyed, |
| 128 | base::Unretained(this))}}; |
Vitaly Buka | 157b16a | 2015-07-31 16:20:48 -0700 | [diff] [blame] | 129 | // CloudCommandProxy::CloudCommandProxy() subscribe itself to weave::Command |
| 130 | // notifications. When weave::Command is being destroyed it sends |
| 131 | // ::OnCommandDestroyed() and CloudCommandProxy deletes itself. |
| 132 | proxy.release(); |
Alex Vakulenko | ebfa60b | 2016-02-01 11:52:30 -0800 | [diff] [blame] | 133 | |
| 134 | EXPECT_CALL(*this, OnProxyDestroyed()).Times(AnyNumber()); |
Alex Vakulenko | be4254b | 2015-06-26 11:34:03 -0700 | [diff] [blame] | 135 | } |
| 136 | |
Alex Vakulenko | ebfa60b | 2016-02-01 11:52:30 -0800 | [diff] [blame] | 137 | MOCK_METHOD0(OnProxyDestroyed, void()); |
| 138 | |
Alex Vakulenko | d91d625 | 2015-12-05 17:14:39 -0800 | [diff] [blame] | 139 | ComponentManager::UpdateID current_state_update_id_{0}; |
| 140 | base::CallbackList<void(ComponentManager::UpdateID)> callbacks_; |
Alex Vakulenko | be4254b | 2015-06-26 11:34:03 -0700 | [diff] [blame] | 141 | testing::StrictMock<MockCloudCommandUpdateInterface> cloud_updater_; |
Vitaly Buka | 012cd98 | 2016-02-22 17:18:49 -0800 | [diff] [blame] | 142 | testing::StrictMock<test::MockComponentManager> component_manager_; |
Vitaly Buka | 727f3e6 | 2015-09-25 17:33:43 -0700 | [diff] [blame] | 143 | testing::StrictMock<provider::test::FakeTaskRunner> task_runner_; |
Alex Vakulenko | be4254b | 2015-06-26 11:34:03 -0700 | [diff] [blame] | 144 | std::queue<base::Closure> task_queue_; |
Alex Vakulenko | be4254b | 2015-06-26 11:34:03 -0700 | [diff] [blame] | 145 | std::unique_ptr<CommandInstance> command_instance_; |
| 146 | }; |
| 147 | |
| 148 | } // anonymous namespace |
| 149 | |
Alex Vakulenko | ebfa60b | 2016-02-01 11:52:30 -0800 | [diff] [blame] | 150 | TEST_F(CloudCommandProxyTest, EnsureDestroyed) { |
| 151 | EXPECT_CALL(*this, OnProxyDestroyed()).Times(1); |
| 152 | command_instance_.reset(); |
| 153 | // Verify that CloudCommandProxy has been destroyed already and not at some |
| 154 | // point during the destruction of CloudCommandProxyTest class. |
| 155 | testing::Mock::VerifyAndClearExpectations(this); |
| 156 | } |
| 157 | |
Alex Vakulenko | be4254b | 2015-06-26 11:34:03 -0700 | [diff] [blame] | 158 | TEST_F(CloudCommandProxyTest, ImmediateUpdate) { |
| 159 | const char expected[] = "{'state':'done'}"; |
Vitaly Buka | 7476342 | 2015-10-11 00:39:52 -0700 | [diff] [blame] | 160 | EXPECT_CALL(cloud_updater_, UpdateCommand(kCmdID, MatchJson(expected), _)); |
Vitaly Buka | 2f54897 | 2015-10-08 19:34:49 -0700 | [diff] [blame] | 161 | command_instance_->Complete({}, nullptr); |
Vitaly Buka | ff1d186 | 2015-10-07 20:40:36 -0700 | [diff] [blame] | 162 | task_runner_.RunOnce(); |
Alex Vakulenko | be4254b | 2015-06-26 11:34:03 -0700 | [diff] [blame] | 163 | } |
| 164 | |
| 165 | TEST_F(CloudCommandProxyTest, DelayedUpdate) { |
| 166 | // Simulate that the current device state has changed. |
| 167 | current_state_update_id_ = 20; |
| 168 | // No command update is expected here. |
Vitaly Buka | 2f54897 | 2015-10-08 19:34:49 -0700 | [diff] [blame] | 169 | command_instance_->Complete({}, nullptr); |
Alex Vakulenko | be4254b | 2015-06-26 11:34:03 -0700 | [diff] [blame] | 170 | // Still no command update here... |
| 171 | callbacks_.Notify(19); |
| 172 | // Now we should get the update... |
| 173 | const char expected[] = "{'state':'done'}"; |
Vitaly Buka | 7476342 | 2015-10-11 00:39:52 -0700 | [diff] [blame] | 174 | EXPECT_CALL(cloud_updater_, UpdateCommand(kCmdID, MatchJson(expected), _)); |
Alex Vakulenko | be4254b | 2015-06-26 11:34:03 -0700 | [diff] [blame] | 175 | callbacks_.Notify(20); |
| 176 | } |
| 177 | |
| 178 | TEST_F(CloudCommandProxyTest, InFlightRequest) { |
| 179 | // SetProgress causes two consecutive updates: |
| 180 | // state=inProgress |
| 181 | // progress={...} |
| 182 | // The first state update is sent immediately, the second should be delayed. |
Vitaly Buka | 7476342 | 2015-10-11 00:39:52 -0700 | [diff] [blame] | 183 | DoneCallback callback; |
Vitaly Buka | ff1d186 | 2015-10-07 20:40:36 -0700 | [diff] [blame] | 184 | EXPECT_CALL( |
| 185 | cloud_updater_, |
| 186 | UpdateCommand( |
| 187 | kCmdID, |
Vitaly Buka | 7476342 | 2015-10-11 00:39:52 -0700 | [diff] [blame] | 188 | MatchJson("{'state':'inProgress', 'progress':{'status':'ready'}}"), |
Vitaly Buka | ff1d186 | 2015-10-07 20:40:36 -0700 | [diff] [blame] | 189 | _)) |
Vitaly Buka | 7476342 | 2015-10-11 00:39:52 -0700 | [diff] [blame] | 190 | .WillOnce(SaveArg<2>(&callback)); |
Vitaly Buka | 4f4e228 | 2015-07-23 17:50:07 -0700 | [diff] [blame] | 191 | EXPECT_TRUE(command_instance_->SetProgress( |
| 192 | *CreateDictionaryValue("{'status': 'ready'}"), nullptr)); |
Alex Vakulenko | be4254b | 2015-06-26 11:34:03 -0700 | [diff] [blame] | 193 | |
Vitaly Buka | ff1d186 | 2015-10-07 20:40:36 -0700 | [diff] [blame] | 194 | task_runner_.RunOnce(); |
Alex Vakulenko | be4254b | 2015-06-26 11:34:03 -0700 | [diff] [blame] | 195 | } |
| 196 | |
| 197 | TEST_F(CloudCommandProxyTest, CombineMultiple) { |
| 198 | // Simulate that the current device state has changed. |
| 199 | current_state_update_id_ = 20; |
| 200 | // SetProgress causes two consecutive updates: |
| 201 | // state=inProgress |
| 202 | // progress={...} |
| 203 | // Both updates will be held until device state is updated. |
Vitaly Buka | 4f4e228 | 2015-07-23 17:50:07 -0700 | [diff] [blame] | 204 | EXPECT_TRUE(command_instance_->SetProgress( |
| 205 | *CreateDictionaryValue("{'status': 'ready'}"), nullptr)); |
Alex Vakulenko | be4254b | 2015-06-26 11:34:03 -0700 | [diff] [blame] | 206 | |
| 207 | // Now simulate the device state updated. Both updates should come in one |
| 208 | // request. |
| 209 | const char expected[] = R"({ |
| 210 | 'progress': {'status':'ready'}, |
| 211 | 'state':'inProgress' |
| 212 | })"; |
Vitaly Buka | 7476342 | 2015-10-11 00:39:52 -0700 | [diff] [blame] | 213 | EXPECT_CALL(cloud_updater_, UpdateCommand(kCmdID, MatchJson(expected), _)); |
Alex Vakulenko | be4254b | 2015-06-26 11:34:03 -0700 | [diff] [blame] | 214 | callbacks_.Notify(20); |
| 215 | } |
| 216 | |
| 217 | TEST_F(CloudCommandProxyTest, RetryFailed) { |
Vitaly Buka | 7476342 | 2015-10-11 00:39:52 -0700 | [diff] [blame] | 218 | DoneCallback callback; |
Vitaly Buka | ff1d186 | 2015-10-07 20:40:36 -0700 | [diff] [blame] | 219 | |
| 220 | const char expect[] = |
| 221 | "{'state':'inProgress', 'progress': {'status': 'ready'}}"; |
Vitaly Buka | 7476342 | 2015-10-11 00:39:52 -0700 | [diff] [blame] | 222 | EXPECT_CALL(cloud_updater_, UpdateCommand(kCmdID, MatchJson(expect), _)) |
Vitaly Buka | ff1d186 | 2015-10-07 20:40:36 -0700 | [diff] [blame] | 223 | .Times(3) |
Vitaly Buka | 7476342 | 2015-10-11 00:39:52 -0700 | [diff] [blame] | 224 | .WillRepeatedly(SaveArg<2>(&callback)); |
Vitaly Buka | ff1d186 | 2015-10-07 20:40:36 -0700 | [diff] [blame] | 225 | auto started = task_runner_.GetClock()->Now(); |
Vitaly Buka | 4f4e228 | 2015-07-23 17:50:07 -0700 | [diff] [blame] | 226 | EXPECT_TRUE(command_instance_->SetProgress( |
| 227 | *CreateDictionaryValue("{'status': 'ready'}"), nullptr)); |
Vitaly Buka | ff1d186 | 2015-10-07 20:40:36 -0700 | [diff] [blame] | 228 | task_runner_.Run(); |
Vitaly Buka | 7476342 | 2015-10-11 00:39:52 -0700 | [diff] [blame] | 229 | ErrorPtr error; |
Vitaly Buka | 48a8669 | 2016-01-21 17:15:58 -0800 | [diff] [blame] | 230 | Error::AddTo(&error, FROM_HERE, "TEST", "TEST"); |
Vitaly Buka | 7476342 | 2015-10-11 00:39:52 -0700 | [diff] [blame] | 231 | callback.Run(error->Clone()); |
Vitaly Buka | ff1d186 | 2015-10-07 20:40:36 -0700 | [diff] [blame] | 232 | task_runner_.Run(); |
| 233 | EXPECT_GE(task_runner_.GetClock()->Now() - started, |
| 234 | base::TimeDelta::FromSecondsD(0.9)); |
Alex Vakulenko | be4254b | 2015-06-26 11:34:03 -0700 | [diff] [blame] | 235 | |
Vitaly Buka | 7476342 | 2015-10-11 00:39:52 -0700 | [diff] [blame] | 236 | callback.Run(error->Clone()); |
Vitaly Buka | ff1d186 | 2015-10-07 20:40:36 -0700 | [diff] [blame] | 237 | task_runner_.Run(); |
| 238 | EXPECT_GE(task_runner_.GetClock()->Now() - started, |
| 239 | base::TimeDelta::FromSecondsD(2.9)); |
Alex Vakulenko | be4254b | 2015-06-26 11:34:03 -0700 | [diff] [blame] | 240 | |
Vitaly Buka | 7476342 | 2015-10-11 00:39:52 -0700 | [diff] [blame] | 241 | callback.Run(nullptr); |
Vitaly Buka | ff1d186 | 2015-10-07 20:40:36 -0700 | [diff] [blame] | 242 | task_runner_.Run(); |
| 243 | EXPECT_GE(task_runner_.GetClock()->Now() - started, |
| 244 | base::TimeDelta::FromSecondsD(2.9)); |
Alex Vakulenko | be4254b | 2015-06-26 11:34:03 -0700 | [diff] [blame] | 245 | } |
| 246 | |
| 247 | TEST_F(CloudCommandProxyTest, GateOnStateUpdates) { |
| 248 | current_state_update_id_ = 20; |
Vitaly Buka | 4f4e228 | 2015-07-23 17:50:07 -0700 | [diff] [blame] | 249 | EXPECT_TRUE(command_instance_->SetProgress( |
| 250 | *CreateDictionaryValue("{'status': 'ready'}"), nullptr)); |
Alex Vakulenko | be4254b | 2015-06-26 11:34:03 -0700 | [diff] [blame] | 251 | current_state_update_id_ = 21; |
Vitaly Buka | 4f4e228 | 2015-07-23 17:50:07 -0700 | [diff] [blame] | 252 | EXPECT_TRUE(command_instance_->SetProgress( |
| 253 | *CreateDictionaryValue("{'status': 'busy'}"), nullptr)); |
Alex Vakulenko | be4254b | 2015-06-26 11:34:03 -0700 | [diff] [blame] | 254 | current_state_update_id_ = 22; |
Vitaly Buka | 2f54897 | 2015-10-08 19:34:49 -0700 | [diff] [blame] | 255 | command_instance_->Complete({}, nullptr); |
Alex Vakulenko | be4254b | 2015-06-26 11:34:03 -0700 | [diff] [blame] | 256 | |
| 257 | // Device state #20 updated. |
Vitaly Buka | 7476342 | 2015-10-11 00:39:52 -0700 | [diff] [blame] | 258 | DoneCallback callback; |
Alex Vakulenko | be4254b | 2015-06-26 11:34:03 -0700 | [diff] [blame] | 259 | const char expect1[] = R"({ |
| 260 | 'progress': {'status':'ready'}, |
| 261 | 'state':'inProgress' |
| 262 | })"; |
Vitaly Buka | 7476342 | 2015-10-11 00:39:52 -0700 | [diff] [blame] | 263 | EXPECT_CALL(cloud_updater_, UpdateCommand(kCmdID, MatchJson(expect1), _)) |
| 264 | .WillOnce(SaveArg<2>(&callback)); |
Alex Vakulenko | be4254b | 2015-06-26 11:34:03 -0700 | [diff] [blame] | 265 | callbacks_.Notify(20); |
Vitaly Buka | 7476342 | 2015-10-11 00:39:52 -0700 | [diff] [blame] | 266 | callback.Run(nullptr); |
Alex Vakulenko | be4254b | 2015-06-26 11:34:03 -0700 | [diff] [blame] | 267 | |
| 268 | // Device state #21 updated. |
| 269 | const char expect2[] = "{'progress': {'status':'busy'}}"; |
Vitaly Buka | 7476342 | 2015-10-11 00:39:52 -0700 | [diff] [blame] | 270 | EXPECT_CALL(cloud_updater_, UpdateCommand(kCmdID, MatchJson(expect2), _)) |
| 271 | .WillOnce(SaveArg<2>(&callback)); |
Alex Vakulenko | be4254b | 2015-06-26 11:34:03 -0700 | [diff] [blame] | 272 | callbacks_.Notify(21); |
| 273 | |
| 274 | // Device state #22 updated. Nothing happens here since the previous command |
| 275 | // update request hasn't completed yet. |
| 276 | callbacks_.Notify(22); |
| 277 | |
| 278 | // Now the command update is complete, send out the patch that happened after |
| 279 | // the state #22 was updated. |
| 280 | const char expect3[] = "{'state': 'done'}"; |
Vitaly Buka | 7476342 | 2015-10-11 00:39:52 -0700 | [diff] [blame] | 281 | EXPECT_CALL(cloud_updater_, UpdateCommand(kCmdID, MatchJson(expect3), _)) |
| 282 | .WillOnce(SaveArg<2>(&callback)); |
| 283 | callback.Run(nullptr); |
Alex Vakulenko | be4254b | 2015-06-26 11:34:03 -0700 | [diff] [blame] | 284 | } |
| 285 | |
| 286 | TEST_F(CloudCommandProxyTest, CombineSomeStates) { |
| 287 | current_state_update_id_ = 20; |
Vitaly Buka | 4f4e228 | 2015-07-23 17:50:07 -0700 | [diff] [blame] | 288 | EXPECT_TRUE(command_instance_->SetProgress( |
| 289 | *CreateDictionaryValue("{'status': 'ready'}"), nullptr)); |
Alex Vakulenko | be4254b | 2015-06-26 11:34:03 -0700 | [diff] [blame] | 290 | current_state_update_id_ = 21; |
Vitaly Buka | 4f4e228 | 2015-07-23 17:50:07 -0700 | [diff] [blame] | 291 | EXPECT_TRUE(command_instance_->SetProgress( |
| 292 | *CreateDictionaryValue("{'status': 'busy'}"), nullptr)); |
Alex Vakulenko | be4254b | 2015-06-26 11:34:03 -0700 | [diff] [blame] | 293 | current_state_update_id_ = 22; |
Vitaly Buka | 2f54897 | 2015-10-08 19:34:49 -0700 | [diff] [blame] | 294 | command_instance_->Complete({}, nullptr); |
Alex Vakulenko | be4254b | 2015-06-26 11:34:03 -0700 | [diff] [blame] | 295 | |
| 296 | // Device state 20-21 updated. |
Vitaly Buka | 7476342 | 2015-10-11 00:39:52 -0700 | [diff] [blame] | 297 | DoneCallback callback; |
Alex Vakulenko | be4254b | 2015-06-26 11:34:03 -0700 | [diff] [blame] | 298 | const char expect1[] = R"({ |
| 299 | 'progress': {'status':'busy'}, |
| 300 | 'state':'inProgress' |
| 301 | })"; |
Vitaly Buka | 7476342 | 2015-10-11 00:39:52 -0700 | [diff] [blame] | 302 | EXPECT_CALL(cloud_updater_, UpdateCommand(kCmdID, MatchJson(expect1), _)) |
| 303 | .WillOnce(SaveArg<2>(&callback)); |
Alex Vakulenko | be4254b | 2015-06-26 11:34:03 -0700 | [diff] [blame] | 304 | callbacks_.Notify(21); |
Vitaly Buka | 7476342 | 2015-10-11 00:39:52 -0700 | [diff] [blame] | 305 | callback.Run(nullptr); |
Alex Vakulenko | be4254b | 2015-06-26 11:34:03 -0700 | [diff] [blame] | 306 | |
| 307 | // Device state #22 updated. |
| 308 | const char expect2[] = "{'state': 'done'}"; |
Vitaly Buka | 7476342 | 2015-10-11 00:39:52 -0700 | [diff] [blame] | 309 | EXPECT_CALL(cloud_updater_, UpdateCommand(kCmdID, MatchJson(expect2), _)) |
| 310 | .WillOnce(SaveArg<2>(&callback)); |
Alex Vakulenko | be4254b | 2015-06-26 11:34:03 -0700 | [diff] [blame] | 311 | callbacks_.Notify(22); |
Vitaly Buka | 7476342 | 2015-10-11 00:39:52 -0700 | [diff] [blame] | 312 | callback.Run(nullptr); |
Alex Vakulenko | be4254b | 2015-06-26 11:34:03 -0700 | [diff] [blame] | 313 | } |
| 314 | |
| 315 | TEST_F(CloudCommandProxyTest, CombineAllStates) { |
| 316 | current_state_update_id_ = 20; |
Vitaly Buka | 4f4e228 | 2015-07-23 17:50:07 -0700 | [diff] [blame] | 317 | EXPECT_TRUE(command_instance_->SetProgress( |
| 318 | *CreateDictionaryValue("{'status': 'ready'}"), nullptr)); |
Alex Vakulenko | be4254b | 2015-06-26 11:34:03 -0700 | [diff] [blame] | 319 | current_state_update_id_ = 21; |
Vitaly Buka | 4f4e228 | 2015-07-23 17:50:07 -0700 | [diff] [blame] | 320 | EXPECT_TRUE(command_instance_->SetProgress( |
| 321 | *CreateDictionaryValue("{'status': 'busy'}"), nullptr)); |
Alex Vakulenko | be4254b | 2015-06-26 11:34:03 -0700 | [diff] [blame] | 322 | current_state_update_id_ = 22; |
Vitaly Buka | 2f54897 | 2015-10-08 19:34:49 -0700 | [diff] [blame] | 323 | command_instance_->Complete({}, nullptr); |
Alex Vakulenko | be4254b | 2015-06-26 11:34:03 -0700 | [diff] [blame] | 324 | |
| 325 | // Device state 30 updated. |
| 326 | const char expected[] = R"({ |
| 327 | 'progress': {'status':'busy'}, |
| 328 | 'state':'done' |
| 329 | })"; |
Vitaly Buka | 7476342 | 2015-10-11 00:39:52 -0700 | [diff] [blame] | 330 | EXPECT_CALL(cloud_updater_, UpdateCommand(kCmdID, MatchJson(expected), _)); |
Alex Vakulenko | be4254b | 2015-06-26 11:34:03 -0700 | [diff] [blame] | 331 | callbacks_.Notify(30); |
| 332 | } |
| 333 | |
| 334 | TEST_F(CloudCommandProxyTest, CoalesceUpdates) { |
| 335 | current_state_update_id_ = 20; |
Vitaly Buka | 4f4e228 | 2015-07-23 17:50:07 -0700 | [diff] [blame] | 336 | EXPECT_TRUE(command_instance_->SetProgress( |
| 337 | *CreateDictionaryValue("{'status': 'ready'}"), nullptr)); |
| 338 | EXPECT_TRUE(command_instance_->SetProgress( |
| 339 | *CreateDictionaryValue("{'status': 'busy'}"), nullptr)); |
| 340 | EXPECT_TRUE(command_instance_->SetProgress( |
| 341 | *CreateDictionaryValue("{'status': 'finished'}"), nullptr)); |
Vitaly Buka | 2f54897 | 2015-10-08 19:34:49 -0700 | [diff] [blame] | 342 | EXPECT_TRUE(command_instance_->Complete(*CreateDictionaryValue("{'sum': 30}"), |
| 343 | nullptr)); |
Alex Vakulenko | be4254b | 2015-06-26 11:34:03 -0700 | [diff] [blame] | 344 | |
| 345 | const char expected[] = R"({ |
| 346 | 'progress': {'status':'finished'}, |
| 347 | 'results': {'sum':30}, |
| 348 | 'state':'done' |
| 349 | })"; |
Vitaly Buka | 7476342 | 2015-10-11 00:39:52 -0700 | [diff] [blame] | 350 | EXPECT_CALL(cloud_updater_, UpdateCommand(kCmdID, MatchJson(expected), _)); |
Alex Vakulenko | be4254b | 2015-06-26 11:34:03 -0700 | [diff] [blame] | 351 | callbacks_.Notify(30); |
| 352 | } |
| 353 | |
| 354 | TEST_F(CloudCommandProxyTest, EmptyStateChangeQueue) { |
| 355 | // Assume the device state update queue was empty and was at update ID 20. |
| 356 | current_state_update_id_ = 20; |
| 357 | |
| 358 | // Recreate the command instance and proxy with the new state change queue. |
| 359 | CreateCommandInstance(); |
| 360 | |
| 361 | // Empty queue will immediately call back with the state change notification. |
| 362 | callbacks_.Notify(20); |
| 363 | |
| 364 | // As soon as we change the command, the update to the server should be sent. |
| 365 | const char expected[] = "{'state':'done'}"; |
Vitaly Buka | 7476342 | 2015-10-11 00:39:52 -0700 | [diff] [blame] | 366 | EXPECT_CALL(cloud_updater_, UpdateCommand(kCmdID, MatchJson(expected), _)); |
Vitaly Buka | 2f54897 | 2015-10-08 19:34:49 -0700 | [diff] [blame] | 367 | command_instance_->Complete({}, nullptr); |
Vitaly Buka | ff1d186 | 2015-10-07 20:40:36 -0700 | [diff] [blame] | 368 | task_runner_.RunOnce(); |
Alex Vakulenko | be4254b | 2015-06-26 11:34:03 -0700 | [diff] [blame] | 369 | } |
| 370 | |
| 371 | TEST_F(CloudCommandProxyTest, NonEmptyStateChangeQueue) { |
| 372 | // Assume the device state update queue was NOT empty when the command |
| 373 | // instance was created. |
| 374 | current_state_update_id_ = 20; |
| 375 | |
| 376 | // Recreate the command instance and proxy with the new state change queue. |
| 377 | CreateCommandInstance(); |
| 378 | |
| 379 | // No command updates right now. |
Vitaly Buka | 2f54897 | 2015-10-08 19:34:49 -0700 | [diff] [blame] | 380 | command_instance_->Complete({}, nullptr); |
Alex Vakulenko | be4254b | 2015-06-26 11:34:03 -0700 | [diff] [blame] | 381 | |
| 382 | // Only when the state #20 is published we should update the command |
| 383 | const char expected[] = "{'state':'done'}"; |
Vitaly Buka | 7476342 | 2015-10-11 00:39:52 -0700 | [diff] [blame] | 384 | EXPECT_CALL(cloud_updater_, UpdateCommand(kCmdID, MatchJson(expected), _)); |
Alex Vakulenko | be4254b | 2015-06-26 11:34:03 -0700 | [diff] [blame] | 385 | callbacks_.Notify(20); |
| 386 | } |
| 387 | |
Vitaly Buka | b6f015a | 2015-07-09 14:59:23 -0700 | [diff] [blame] | 388 | } // namespace weave |