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 33using chromeos_update_engine::test_utils::System; 34using chromeos_update_engine::test_utils::WriteFileString; 35using std::string; 36using testing::Return; 37using testing::_; 38 39namespace chromeos_update_engine { 40 41class OmahaResponseHandlerActionTest : public ::testing::Test { 42 protected: 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 64class OmahaResponseHandlerActionProcessorDelegate 65 : public ActionProcessorDelegate { 66 public: 67 OmahaResponseHandlerActionProcessorDelegate() 68 : code_(ErrorCode::kError), 69 code_set_(false) {} 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 82namespace { 83const 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"; 92const char* const kBadVersion = "don't update me"; 93} // namespace 94 95bool 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 139TEST_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 214TEST_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 222TEST_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 242TEST_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 261TEST_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 283TEST_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 302TEST_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 322TEST_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 357TEST_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 392TEST_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