1 //
2 // Copyright (C) 2011 The Android Open Source Project
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16
17 #include "update_engine/omaha_response_handler_action.h"
18
19 #include <string>
20
21 #include <base/files/file_util.h>
22 #include <base/files/scoped_temp_dir.h>
23 #include <gtest/gtest.h>
24
25 #include "update_engine/common/constants.h"
26 #include "update_engine/common/platform_constants.h"
27 #include "update_engine/common/test_utils.h"
28 #include "update_engine/common/utils.h"
29 #include "update_engine/fake_system_state.h"
30 #include "update_engine/mock_payload_state.h"
31 #include "update_engine/payload_consumer/payload_constants.h"
32
33 using chromeos_update_engine::test_utils::System;
34 using chromeos_update_engine::test_utils::WriteFileString;
35 using std::string;
36 using testing::Return;
37 using testing::_;
38
39 namespace chromeos_update_engine {
40
41 class OmahaResponseHandlerActionTest : public ::testing::Test {
42 protected:
SetUp()43 void SetUp() override {
44 FakeBootControl* fake_boot_control = fake_system_state_.fake_boot_control();
45 fake_boot_control->SetPartitionDevice(
46 kLegacyPartitionNameKernel, 0, "/dev/sdz2");
47 fake_boot_control->SetPartitionDevice(
48 kLegacyPartitionNameRoot, 0, "/dev/sdz3");
49 fake_boot_control->SetPartitionDevice(
50 kLegacyPartitionNameKernel, 1, "/dev/sdz4");
51 fake_boot_control->SetPartitionDevice(
52 kLegacyPartitionNameRoot, 1, "/dev/sdz5");
53 }
54
55 // Return true iff the OmahaResponseHandlerAction succeeded.
56 // If out is non-null, it's set w/ the response from the action.
57 bool DoTest(const OmahaResponse& in,
58 const string& deadline_file,
59 InstallPlan* out);
60
61 FakeSystemState fake_system_state_;
62 };
63
64 class OmahaResponseHandlerActionProcessorDelegate
65 : public ActionProcessorDelegate {
66 public:
OmahaResponseHandlerActionProcessorDelegate()67 OmahaResponseHandlerActionProcessorDelegate()
68 : code_(ErrorCode::kError),
69 code_set_(false) {}
ActionCompleted(ActionProcessor * processor,AbstractAction * action,ErrorCode code)70 void ActionCompleted(ActionProcessor* processor,
71 AbstractAction* action,
72 ErrorCode code) {
73 if (action->Type() == OmahaResponseHandlerAction::StaticType()) {
74 code_ = code;
75 code_set_ = true;
76 }
77 }
78 ErrorCode code_;
79 bool code_set_;
80 };
81
82 namespace {
83 const char* const kLongName =
84 "very_long_name_and_no_slashes-very_long_name_and_no_slashes"
85 "very_long_name_and_no_slashes-very_long_name_and_no_slashes"
86 "very_long_name_and_no_slashes-very_long_name_and_no_slashes"
87 "very_long_name_and_no_slashes-very_long_name_and_no_slashes"
88 "very_long_name_and_no_slashes-very_long_name_and_no_slashes"
89 "very_long_name_and_no_slashes-very_long_name_and_no_slashes"
90 "very_long_name_and_no_slashes-very_long_name_and_no_slashes"
91 "-the_update_a.b.c.d_DELTA_.tgz";
92 const char* const kBadVersion = "don't update me";
93 } // namespace
94
DoTest(const OmahaResponse & in,const string & test_deadline_file,InstallPlan * out)95 bool OmahaResponseHandlerActionTest::DoTest(
96 const OmahaResponse& in,
97 const string& test_deadline_file,
98 InstallPlan* out) {
99 ActionProcessor processor;
100 OmahaResponseHandlerActionProcessorDelegate delegate;
101 processor.set_delegate(&delegate);
102
103 ObjectFeederAction<OmahaResponse> feeder_action;
104 feeder_action.set_obj(in);
105 if (in.update_exists && in.version != kBadVersion) {
106 EXPECT_CALL(*(fake_system_state_.mock_prefs()),
107 SetString(kPrefsUpdateCheckResponseHash, in.hash))
108 .WillOnce(Return(true));
109
110 int slot = 1 - fake_system_state_.fake_boot_control()->GetCurrentSlot();
111 string key = kPrefsChannelOnSlotPrefix + std::to_string(slot);
112 EXPECT_CALL(*(fake_system_state_.mock_prefs()), SetString(key, testing::_))
113 .WillOnce(Return(true));
114 }
115
116 string current_url = in.payload_urls.size() ? in.payload_urls[0] : "";
117 EXPECT_CALL(*(fake_system_state_.mock_payload_state()), GetCurrentUrl())
118 .WillRepeatedly(Return(current_url));
119
120 OmahaResponseHandlerAction response_handler_action(
121 &fake_system_state_,
122 (test_deadline_file.empty() ?
123 constants::kOmahaResponseDeadlineFile : test_deadline_file));
124 BondActions(&feeder_action, &response_handler_action);
125 ObjectCollectorAction<InstallPlan> collector_action;
126 BondActions(&response_handler_action, &collector_action);
127 processor.EnqueueAction(&feeder_action);
128 processor.EnqueueAction(&response_handler_action);
129 processor.EnqueueAction(&collector_action);
130 processor.StartProcessing();
131 EXPECT_TRUE(!processor.IsRunning())
132 << "Update test to handle non-async actions";
133 if (out)
134 *out = collector_action.object();
135 EXPECT_TRUE(delegate.code_set_);
136 return delegate.code_ == ErrorCode::kSuccess;
137 }
138
TEST_F(OmahaResponseHandlerActionTest,SimpleTest)139 TEST_F(OmahaResponseHandlerActionTest, SimpleTest) {
140 string test_deadline_file;
141 CHECK(utils::MakeTempFile(
142 "omaha_response_handler_action_unittest-XXXXXX",
143 &test_deadline_file, nullptr));
144 ScopedPathUnlinker deadline_unlinker(test_deadline_file);
145 {
146 OmahaResponse in;
147 in.update_exists = true;
148 in.version = "a.b.c.d";
149 in.payload_urls.push_back("http://foo/the_update_a.b.c.d.tgz");
150 in.more_info_url = "http://more/info";
151 in.hash = "HASH+";
152 in.size = 12;
153 in.prompt = false;
154 in.deadline = "20101020";
155 InstallPlan install_plan;
156 EXPECT_TRUE(DoTest(in, test_deadline_file, &install_plan));
157 EXPECT_EQ(in.payload_urls[0], install_plan.download_url);
158 EXPECT_EQ(in.hash, install_plan.payload_hash);
159 EXPECT_EQ(1U, install_plan.target_slot);
160 string deadline;
161 EXPECT_TRUE(utils::ReadFile(test_deadline_file, &deadline));
162 EXPECT_EQ("20101020", deadline);
163 struct stat deadline_stat;
164 EXPECT_EQ(0, stat(test_deadline_file.c_str(), &deadline_stat));
165 EXPECT_EQ(
166 static_cast<mode_t>(S_IFREG | S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH),
167 deadline_stat.st_mode);
168 EXPECT_EQ(in.version, install_plan.version);
169 }
170 {
171 OmahaResponse in;
172 in.update_exists = true;
173 in.version = "a.b.c.d";
174 in.payload_urls.push_back("http://foo/the_update_a.b.c.d.tgz");
175 in.more_info_url = "http://more/info";
176 in.hash = "HASHj+";
177 in.size = 12;
178 in.prompt = true;
179 InstallPlan install_plan;
180 // Set the other slot as current.
181 fake_system_state_.fake_boot_control()->SetCurrentSlot(1);
182 EXPECT_TRUE(DoTest(in, test_deadline_file, &install_plan));
183 EXPECT_EQ(in.payload_urls[0], install_plan.download_url);
184 EXPECT_EQ(in.hash, install_plan.payload_hash);
185 EXPECT_EQ(0U, install_plan.target_slot);
186 string deadline;
187 EXPECT_TRUE(utils::ReadFile(test_deadline_file, &deadline) &&
188 deadline.empty());
189 EXPECT_EQ(in.version, install_plan.version);
190 }
191 {
192 OmahaResponse in;
193 in.update_exists = true;
194 in.version = "a.b.c.d";
195 in.payload_urls.push_back(kLongName);
196 in.more_info_url = "http://more/info";
197 in.hash = "HASHj+";
198 in.size = 12;
199 in.prompt = true;
200 in.deadline = "some-deadline";
201 InstallPlan install_plan;
202 fake_system_state_.fake_boot_control()->SetCurrentSlot(0);
203 EXPECT_TRUE(DoTest(in, test_deadline_file, &install_plan));
204 EXPECT_EQ(in.payload_urls[0], install_plan.download_url);
205 EXPECT_EQ(in.hash, install_plan.payload_hash);
206 EXPECT_EQ(1U, install_plan.target_slot);
207 string deadline;
208 EXPECT_TRUE(utils::ReadFile(test_deadline_file, &deadline));
209 EXPECT_EQ("some-deadline", deadline);
210 EXPECT_EQ(in.version, install_plan.version);
211 }
212 }
213
TEST_F(OmahaResponseHandlerActionTest,NoUpdatesTest)214 TEST_F(OmahaResponseHandlerActionTest, NoUpdatesTest) {
215 OmahaResponse in;
216 in.update_exists = false;
217 InstallPlan install_plan;
218 EXPECT_FALSE(DoTest(in, "", &install_plan));
219 EXPECT_TRUE(install_plan.partitions.empty());
220 }
221
TEST_F(OmahaResponseHandlerActionTest,HashChecksForHttpTest)222 TEST_F(OmahaResponseHandlerActionTest, HashChecksForHttpTest) {
223 OmahaResponse in;
224 in.update_exists = true;
225 in.version = "a.b.c.d";
226 in.payload_urls.push_back("http://test.should/need/hash.checks.signed");
227 in.more_info_url = "http://more/info";
228 in.hash = "HASHj+";
229 in.size = 12;
230 // Hash checks are always skipped for non-official update URLs.
231 EXPECT_CALL(*(fake_system_state_.mock_request_params()),
232 IsUpdateUrlOfficial())
233 .WillRepeatedly(Return(true));
234 InstallPlan install_plan;
235 EXPECT_TRUE(DoTest(in, "", &install_plan));
236 EXPECT_EQ(in.payload_urls[0], install_plan.download_url);
237 EXPECT_EQ(in.hash, install_plan.payload_hash);
238 EXPECT_TRUE(install_plan.hash_checks_mandatory);
239 EXPECT_EQ(in.version, install_plan.version);
240 }
241
TEST_F(OmahaResponseHandlerActionTest,HashChecksForUnofficialUpdateUrl)242 TEST_F(OmahaResponseHandlerActionTest, HashChecksForUnofficialUpdateUrl) {
243 OmahaResponse in;
244 in.update_exists = true;
245 in.version = "a.b.c.d";
246 in.payload_urls.push_back("http://url.normally/needs/hash.checks.signed");
247 in.more_info_url = "http://more/info";
248 in.hash = "HASHj+";
249 in.size = 12;
250 EXPECT_CALL(*(fake_system_state_.mock_request_params()),
251 IsUpdateUrlOfficial())
252 .WillRepeatedly(Return(false));
253 InstallPlan install_plan;
254 EXPECT_TRUE(DoTest(in, "", &install_plan));
255 EXPECT_EQ(in.payload_urls[0], install_plan.download_url);
256 EXPECT_EQ(in.hash, install_plan.payload_hash);
257 EXPECT_FALSE(install_plan.hash_checks_mandatory);
258 EXPECT_EQ(in.version, install_plan.version);
259 }
260
TEST_F(OmahaResponseHandlerActionTest,HashChecksForOfficialUrlUnofficialBuildTest)261 TEST_F(OmahaResponseHandlerActionTest,
262 HashChecksForOfficialUrlUnofficialBuildTest) {
263 // Official URLs for unofficial builds (dev/test images) don't require hash.
264 OmahaResponse in;
265 in.update_exists = true;
266 in.version = "a.b.c.d";
267 in.payload_urls.push_back("http://url.normally/needs/hash.checks.signed");
268 in.more_info_url = "http://more/info";
269 in.hash = "HASHj+";
270 in.size = 12;
271 EXPECT_CALL(*(fake_system_state_.mock_request_params()),
272 IsUpdateUrlOfficial())
273 .WillRepeatedly(Return(true));
274 fake_system_state_.fake_hardware()->SetIsOfficialBuild(false);
275 InstallPlan install_plan;
276 EXPECT_TRUE(DoTest(in, "", &install_plan));
277 EXPECT_EQ(in.payload_urls[0], install_plan.download_url);
278 EXPECT_EQ(in.hash, install_plan.payload_hash);
279 EXPECT_FALSE(install_plan.hash_checks_mandatory);
280 EXPECT_EQ(in.version, install_plan.version);
281 }
282
TEST_F(OmahaResponseHandlerActionTest,HashChecksForHttpsTest)283 TEST_F(OmahaResponseHandlerActionTest, HashChecksForHttpsTest) {
284 OmahaResponse in;
285 in.update_exists = true;
286 in.version = "a.b.c.d";
287 in.payload_urls.push_back("https://test.should.not/need/hash.checks.signed");
288 in.more_info_url = "http://more/info";
289 in.hash = "HASHj+";
290 in.size = 12;
291 EXPECT_CALL(*(fake_system_state_.mock_request_params()),
292 IsUpdateUrlOfficial())
293 .WillRepeatedly(Return(true));
294 InstallPlan install_plan;
295 EXPECT_TRUE(DoTest(in, "", &install_plan));
296 EXPECT_EQ(in.payload_urls[0], install_plan.download_url);
297 EXPECT_EQ(in.hash, install_plan.payload_hash);
298 EXPECT_FALSE(install_plan.hash_checks_mandatory);
299 EXPECT_EQ(in.version, install_plan.version);
300 }
301
TEST_F(OmahaResponseHandlerActionTest,HashChecksForBothHttpAndHttpsTest)302 TEST_F(OmahaResponseHandlerActionTest, HashChecksForBothHttpAndHttpsTest) {
303 OmahaResponse in;
304 in.update_exists = true;
305 in.version = "a.b.c.d";
306 in.payload_urls.push_back("http://test.should.still/need/hash.checks");
307 in.payload_urls.push_back("https://test.should.still/need/hash.checks");
308 in.more_info_url = "http://more/info";
309 in.hash = "HASHj+";
310 in.size = 12;
311 EXPECT_CALL(*(fake_system_state_.mock_request_params()),
312 IsUpdateUrlOfficial())
313 .WillRepeatedly(Return(true));
314 InstallPlan install_plan;
315 EXPECT_TRUE(DoTest(in, "", &install_plan));
316 EXPECT_EQ(in.payload_urls[0], install_plan.download_url);
317 EXPECT_EQ(in.hash, install_plan.payload_hash);
318 EXPECT_TRUE(install_plan.hash_checks_mandatory);
319 EXPECT_EQ(in.version, install_plan.version);
320 }
321
TEST_F(OmahaResponseHandlerActionTest,ChangeToMoreStableChannelTest)322 TEST_F(OmahaResponseHandlerActionTest, ChangeToMoreStableChannelTest) {
323 OmahaResponse in;
324 in.update_exists = true;
325 in.version = "a.b.c.d";
326 in.payload_urls.push_back("https://MoreStableChannelTest");
327 in.more_info_url = "http://more/info";
328 in.hash = "HASHjk";
329 in.size = 15;
330
331 // Create a uniquely named test directory.
332 base::ScopedTempDir tempdir;
333 ASSERT_TRUE(tempdir.CreateUniqueTempDir());
334
335 OmahaRequestParams params(&fake_system_state_);
336 fake_system_state_.fake_hardware()->SetIsOfficialBuild(false);
337 params.set_root(tempdir.path().value());
338 params.set_current_channel("canary-channel");
339 // The ImageProperties in Android uses prefs to store MutableImageProperties.
340 #ifdef __ANDROID__
341 EXPECT_CALL(*fake_system_state_.mock_prefs(), SetString(_, "stable-channel"))
342 .WillOnce(Return(true));
343 EXPECT_CALL(*fake_system_state_.mock_prefs(), SetBoolean(_, true))
344 .WillOnce(Return(true));
345 #endif // __ANDROID__
346 EXPECT_TRUE(params.SetTargetChannel("stable-channel", true, nullptr));
347 params.UpdateDownloadChannel();
348 EXPECT_TRUE(params.to_more_stable_channel());
349 EXPECT_TRUE(params.is_powerwash_allowed());
350
351 fake_system_state_.set_request_params(¶ms);
352 InstallPlan install_plan;
353 EXPECT_TRUE(DoTest(in, "", &install_plan));
354 EXPECT_TRUE(install_plan.powerwash_required);
355 }
356
TEST_F(OmahaResponseHandlerActionTest,ChangeToLessStableChannelTest)357 TEST_F(OmahaResponseHandlerActionTest, ChangeToLessStableChannelTest) {
358 OmahaResponse in;
359 in.update_exists = true;
360 in.version = "a.b.c.d";
361 in.payload_urls.push_back("https://LessStableChannelTest");
362 in.more_info_url = "http://more/info";
363 in.hash = "HASHjk";
364 in.size = 15;
365
366 // Create a uniquely named test directory.
367 base::ScopedTempDir tempdir;
368 ASSERT_TRUE(tempdir.CreateUniqueTempDir());
369
370 OmahaRequestParams params(&fake_system_state_);
371 fake_system_state_.fake_hardware()->SetIsOfficialBuild(false);
372 params.set_root(tempdir.path().value());
373 params.set_current_channel("stable-channel");
374 // The ImageProperties in Android uses prefs to store MutableImageProperties.
375 #ifdef __ANDROID__
376 EXPECT_CALL(*fake_system_state_.mock_prefs(), SetString(_, "canary-channel"))
377 .WillOnce(Return(true));
378 EXPECT_CALL(*fake_system_state_.mock_prefs(), SetBoolean(_, false))
379 .WillOnce(Return(true));
380 #endif // __ANDROID__
381 EXPECT_TRUE(params.SetTargetChannel("canary-channel", false, nullptr));
382 params.UpdateDownloadChannel();
383 EXPECT_FALSE(params.to_more_stable_channel());
384 EXPECT_FALSE(params.is_powerwash_allowed());
385
386 fake_system_state_.set_request_params(¶ms);
387 InstallPlan install_plan;
388 EXPECT_TRUE(DoTest(in, "", &install_plan));
389 EXPECT_FALSE(install_plan.powerwash_required);
390 }
391
TEST_F(OmahaResponseHandlerActionTest,P2PUrlIsUsedAndHashChecksMandatory)392 TEST_F(OmahaResponseHandlerActionTest, P2PUrlIsUsedAndHashChecksMandatory) {
393 OmahaResponse in;
394 in.update_exists = true;
395 in.version = "a.b.c.d";
396 in.payload_urls.push_back("https://would.not/cause/hash/checks");
397 in.more_info_url = "http://more/info";
398 in.hash = "HASHj+";
399 in.size = 12;
400
401 OmahaRequestParams params(&fake_system_state_);
402 // We're using a real OmahaRequestParams object here so we can't mock
403 // IsUpdateUrlOfficial(), but setting the update URL to the AutoUpdate test
404 // server will cause IsUpdateUrlOfficial() to return true.
405 params.set_update_url(constants::kOmahaDefaultAUTestURL);
406 fake_system_state_.set_request_params(¶ms);
407
408 EXPECT_CALL(*fake_system_state_.mock_payload_state(),
409 SetUsingP2PForDownloading(true));
410
411 string p2p_url = "http://9.8.7.6/p2p";
412 EXPECT_CALL(*fake_system_state_.mock_payload_state(), GetP2PUrl())
413 .WillRepeatedly(Return(p2p_url));
414 EXPECT_CALL(*fake_system_state_.mock_payload_state(),
415 GetUsingP2PForDownloading()).WillRepeatedly(Return(true));
416
417 InstallPlan install_plan;
418 EXPECT_TRUE(DoTest(in, "", &install_plan));
419 EXPECT_EQ(in.hash, install_plan.payload_hash);
420 EXPECT_EQ(install_plan.download_url, p2p_url);
421 EXPECT_TRUE(install_plan.hash_checks_mandatory);
422 }
423
424 } // namespace chromeos_update_engine
425