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