blob: a944f37e7f4ea5c4e54a3bfa84ef9e26706825da [file] [log] [blame]
Jae Hoon Kim0ae8fe12019-06-26 14:32:50 -07001//
2// Copyright (C) 2019 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/libcurl_http_fetcher.h"
18
19#include <string>
20
21#include <brillo/message_loops/fake_message_loop.h>
Amin Hassanid3d84212019-08-17 00:27:44 -070022#include <gmock/gmock.h>
Jae Hoon Kim0ae8fe12019-06-26 14:32:50 -070023#include <gtest/gtest.h>
24
25#include "update_engine/common/fake_hardware.h"
Amin Hassanid3d84212019-08-17 00:27:44 -070026#include "update_engine/mock_libcurl_http_fetcher.h"
Jae Hoon Kim0ae8fe12019-06-26 14:32:50 -070027
28using std::string;
29
30namespace chromeos_update_engine {
31
32namespace {
Kelvin Zhangf0c73d42021-09-07 10:34:26 -070033
Jae Hoon Kim0ae8fe12019-06-26 14:32:50 -070034constexpr char kHeaderName[] = "X-Goog-Test-Header";
Jae Hoon Kim0ae8fe12019-06-26 14:32:50 -070035
36class LibcurlHttpFetcherTest : public ::testing::Test {
37 protected:
38 void SetUp() override {
39 loop_.SetAsCurrent();
40 fake_hardware_.SetIsOfficialBuild(true);
41 fake_hardware_.SetIsOOBEEnabled(false);
42 }
43
44 brillo::FakeMessageLoop loop_{nullptr};
45 FakeHardware fake_hardware_;
Kelvin Zhangc7a1d1f2022-07-29 13:36:29 -070046 MockLibcurlHttpFetcher libcurl_fetcher_{&fake_hardware_};
Xiaochu Liub5ba7972019-07-11 09:51:06 -070047 UnresolvedHostStateMachine state_machine_;
Jae Hoon Kim0ae8fe12019-06-26 14:32:50 -070048};
49
Kelvin Zhangf0c73d42021-09-07 10:34:26 -070050} // namespace
51
Jae Hoon Kim0ae8fe12019-06-26 14:32:50 -070052TEST_F(LibcurlHttpFetcherTest, GetEmptyHeaderValueTest) {
53 const string header_value = "";
54 string actual_header_value;
55 libcurl_fetcher_.SetHeader(kHeaderName, header_value);
56 EXPECT_TRUE(libcurl_fetcher_.GetHeader(kHeaderName, &actual_header_value));
57 EXPECT_EQ("", actual_header_value);
58}
59
60TEST_F(LibcurlHttpFetcherTest, GetHeaderTest) {
61 const string header_value = "This-is-value 123";
62 string actual_header_value;
63 libcurl_fetcher_.SetHeader(kHeaderName, header_value);
64 EXPECT_TRUE(libcurl_fetcher_.GetHeader(kHeaderName, &actual_header_value));
65 EXPECT_EQ(header_value, actual_header_value);
66}
67
68TEST_F(LibcurlHttpFetcherTest, GetNonExistentHeaderValueTest) {
69 string actual_header_value;
70 // Skip |SetHeaader()| call.
71 EXPECT_FALSE(libcurl_fetcher_.GetHeader(kHeaderName, &actual_header_value));
72 // Even after a failed |GetHeaderValue()|, enforce that the passed pointer to
73 // modifiable string was cleared to be empty.
74 EXPECT_EQ("", actual_header_value);
75}
76
77TEST_F(LibcurlHttpFetcherTest, GetHeaderEdgeCaseTest) {
78 const string header_value = "\a\b\t\v\f\r\\ edge:-case: \a\b\t\v\f\r\\";
79 string actual_header_value;
80 libcurl_fetcher_.SetHeader(kHeaderName, header_value);
81 EXPECT_TRUE(libcurl_fetcher_.GetHeader(kHeaderName, &actual_header_value));
82 EXPECT_EQ(header_value, actual_header_value);
83}
84
Xiaochu Liub5ba7972019-07-11 09:51:06 -070085TEST_F(LibcurlHttpFetcherTest, InvalidURLTest) {
86 int no_network_max_retries = 1;
87 libcurl_fetcher_.set_no_network_max_retries(no_network_max_retries);
88
Amin Hassanid3d84212019-08-17 00:27:44 -070089 libcurl_fetcher_.BeginTransfer("not-a-URL");
Xiaochu Liub5ba7972019-07-11 09:51:06 -070090 while (loop_.PendingTasks()) {
91 loop_.RunOnce(true);
92 }
93
94 EXPECT_EQ(libcurl_fetcher_.get_no_network_max_retries(),
95 no_network_max_retries);
96}
97
Amin Hassanid3d84212019-08-17 00:27:44 -070098TEST_F(LibcurlHttpFetcherTest, CouldNotResolveHostTest) {
Xiaochu Liub5ba7972019-07-11 09:51:06 -070099 int no_network_max_retries = 1;
100 libcurl_fetcher_.set_no_network_max_retries(no_network_max_retries);
101
Amin Hassanid3d84212019-08-17 00:27:44 -0700102 libcurl_fetcher_.BeginTransfer("https://An-uNres0lvable-uRl.invalid");
103
Tianjie934b8472020-06-24 23:10:49 -0700104 // It's slower on Android that libcurl handle may not finish within 1 cycle.
105 // Will need to wait for more cycles until it finishes. Original test didn't
106 // correctly handle when we need to re-watch libcurl fds.
107 while (loop_.PendingTasks() &&
108 libcurl_fetcher_.GetAuxiliaryErrorCode() == ErrorCode::kSuccess) {
109 loop_.RunOnce(true);
110 }
Amin Hassanice99ee72020-10-07 22:24:33 -0700111
Amin Hassanid3d84212019-08-17 00:27:44 -0700112 EXPECT_EQ(libcurl_fetcher_.GetAuxiliaryErrorCode(),
113 ErrorCode::kUnresolvedHostError);
114
115 while (loop_.PendingTasks()) {
116 loop_.RunOnce(true);
117 }
118 // The auxilary error code should've have been changed.
119 EXPECT_EQ(libcurl_fetcher_.GetAuxiliaryErrorCode(),
120 ErrorCode::kUnresolvedHostError);
121
122 // If libcurl fails to resolve the name, we call res_init() to reload
123 // resolv.conf and retry exactly once more. See crbug.com/982813 for details.
124 EXPECT_EQ(libcurl_fetcher_.get_no_network_max_retries(),
125 no_network_max_retries + 1);
126}
127
128TEST_F(LibcurlHttpFetcherTest, HostResolvedTest) {
129 int no_network_max_retries = 2;
130 libcurl_fetcher_.set_no_network_max_retries(no_network_max_retries);
131
Xiaochu Liub5ba7972019-07-11 09:51:06 -0700132 // This test actually sends request to internet but according to
133 // https://tools.ietf.org/html/rfc2606#section-2, .invalid domain names are
134 // reserved and sure to be invalid. Ideally we should mock libcurl or
135 // reorganize LibcurlHttpFetcher so the part that sends request can be mocked
136 // easily.
137 // TODO(xiaochu) Refactor LibcurlHttpFetcher (and its relates) so it's
138 // easier to mock the part that depends on internet connectivity.
139 libcurl_fetcher_.BeginTransfer("https://An-uNres0lvable-uRl.invalid");
Amin Hassanid3d84212019-08-17 00:27:44 -0700140
Tianjie934b8472020-06-24 23:10:49 -0700141 // It's slower on Android that libcurl handle may not finish within 1 cycle.
142 // Will need to wait for more cycles until it finishes. Original test didn't
143 // correctly handle when we need to re-watch libcurl fds.
144 while (loop_.PendingTasks() &&
145 libcurl_fetcher_.GetAuxiliaryErrorCode() == ErrorCode::kSuccess) {
146 loop_.RunOnce(true);
147 }
Amin Hassanice99ee72020-10-07 22:24:33 -0700148
Amin Hassanid3d84212019-08-17 00:27:44 -0700149 EXPECT_EQ(libcurl_fetcher_.GetAuxiliaryErrorCode(),
150 ErrorCode::kUnresolvedHostError);
151
152 // The second time, it will resolve, with error code 200 but we set the
153 // download size be smaller than the transfer size so it will retry again.
154 EXPECT_CALL(libcurl_fetcher_, GetHttpResponseCode())
155 .WillOnce(testing::Invoke(
156 [this]() { libcurl_fetcher_.http_response_code_ = 200; }))
157 .WillRepeatedly(testing::Invoke(
158 [this]() { libcurl_fetcher_.http_response_code_ = 0; }));
159 libcurl_fetcher_.transfer_size_ = 10;
160
Tianjie934b8472020-06-24 23:10:49 -0700161 // It's slower on Android that libcurl handle may not finish within 1 cycle.
162 // Will need to wait for more cycles until it finishes. Original test didn't
163 // correctly handle when we need to re-watch libcurl fds.
164 while (loop_.PendingTasks() && libcurl_fetcher_.GetAuxiliaryErrorCode() ==
165 ErrorCode::kUnresolvedHostError) {
166 loop_.RunOnce(true);
167 }
Amin Hassanice99ee72020-10-07 22:24:33 -0700168
Amin Hassanid3d84212019-08-17 00:27:44 -0700169 EXPECT_EQ(libcurl_fetcher_.GetAuxiliaryErrorCode(),
170 ErrorCode::kUnresolvedHostRecovered);
171
Xiaochu Liub5ba7972019-07-11 09:51:06 -0700172 while (loop_.PendingTasks()) {
173 loop_.RunOnce(true);
174 }
Amin Hassanid3d84212019-08-17 00:27:44 -0700175 // The auxilary error code should not have been changed.
176 EXPECT_EQ(libcurl_fetcher_.GetAuxiliaryErrorCode(),
177 ErrorCode::kUnresolvedHostRecovered);
Xiaochu Liub5ba7972019-07-11 09:51:06 -0700178
179 // If libcurl fails to resolve the name, we call res_init() to reload
180 // resolv.conf and retry exactly once more. See crbug.com/982813 for details.
181 EXPECT_EQ(libcurl_fetcher_.get_no_network_max_retries(),
182 no_network_max_retries + 1);
183}
184
185TEST_F(LibcurlHttpFetcherTest, HttpFetcherStateMachineRetryFailedTest) {
186 state_machine_.UpdateState(true);
187 state_machine_.UpdateState(true);
Amin Hassanid3d84212019-08-17 00:27:44 -0700188 EXPECT_EQ(state_machine_.GetState(),
Xiaochu Liub5ba7972019-07-11 09:51:06 -0700189 UnresolvedHostStateMachine::State::kNotRetry);
190}
191
192TEST_F(LibcurlHttpFetcherTest, HttpFetcherStateMachineRetrySucceedTest) {
193 state_machine_.UpdateState(true);
194 state_machine_.UpdateState(false);
Amin Hassanid3d84212019-08-17 00:27:44 -0700195 EXPECT_EQ(state_machine_.GetState(),
Xiaochu Liub5ba7972019-07-11 09:51:06 -0700196 UnresolvedHostStateMachine::State::kRetriedSuccess);
197}
198
199TEST_F(LibcurlHttpFetcherTest, HttpFetcherStateMachineNoRetryTest) {
200 state_machine_.UpdateState(false);
201 state_machine_.UpdateState(false);
Amin Hassanid3d84212019-08-17 00:27:44 -0700202 EXPECT_EQ(state_machine_.GetState(),
Xiaochu Liub5ba7972019-07-11 09:51:06 -0700203 UnresolvedHostStateMachine::State::kInit);
204}
205
Jae Hoon Kim0ae8fe12019-06-26 14:32:50 -0700206} // namespace chromeos_update_engine