OLD | NEW |
1 // Copyright (c) 2009 The Chromium OS Authors. All rights reserved. | 1 // Copyright (c) 2009 The Chromium OS Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include <unistd.h> | 5 #include <unistd.h> |
6 | 6 |
7 #include <string> | 7 #include <string> |
| 8 #include <utility> |
8 #include <vector> | 9 #include <vector> |
9 | 10 |
10 #include "base/logging.h" | 11 #include "base/logging.h" |
11 #include "base/scoped_ptr.h" | 12 #include "base/scoped_ptr.h" |
12 #include "base/string_util.h" | 13 #include "base/string_util.h" |
13 #include "glib.h" | 14 #include "glib.h" |
14 #include "gtest/gtest.h" | 15 #include "gtest/gtest.h" |
15 #include "update_engine/libcurl_http_fetcher.h" | 16 #include "update_engine/libcurl_http_fetcher.h" |
16 #include "update_engine/mock_http_fetcher.h" | 17 #include "update_engine/mock_http_fetcher.h" |
| 18 #include "update_engine/multi_http_fetcher.h" |
17 | 19 |
| 20 using std::make_pair; |
18 using std::string; | 21 using std::string; |
19 using std::vector; | 22 using std::vector; |
20 | 23 |
21 namespace chromeos_update_engine { | 24 namespace chromeos_update_engine { |
22 | 25 |
23 namespace { | 26 namespace { |
24 // WARNING, if you update this, you must also update test_http_server.py | 27 // WARNING, if you update these, you must also update test_http_server.py |
25 const char* const kServerPort = "8080"; | 28 const char* const kServerPort = "8080"; |
| 29 const int kBigSize = 100000; |
26 string LocalServerUrlForPath(const string& path) { | 30 string LocalServerUrlForPath(const string& path) { |
27 return string("http://127.0.0.1:") + kServerPort + path; | 31 return string("http://127.0.0.1:") + kServerPort + path; |
28 } | 32 } |
29 } | 33 } |
30 | 34 |
31 template <typename T> | 35 template <typename T> |
32 class HttpFetcherTest : public ::testing::Test { | 36 class HttpFetcherTest : public ::testing::Test { |
33 public: | 37 public: |
34 HttpFetcher* NewLargeFetcher() = 0; | 38 HttpFetcher* NewLargeFetcher() = 0; |
35 HttpFetcher* NewSmallFetcher() = 0; | 39 HttpFetcher* NewSmallFetcher() = 0; |
36 string BigUrl() const = 0; | 40 string BigUrl() const = 0; |
37 string SmallUrl() const = 0; | 41 string SmallUrl() const = 0; |
38 bool IsMock() const = 0; | 42 bool IsMock() const = 0; |
| 43 bool IsMulti() const = 0; |
39 }; | 44 }; |
40 | 45 |
41 class NullHttpServer { | 46 class NullHttpServer { |
42 public: | 47 public: |
43 NullHttpServer() : started_(true) {} | 48 NullHttpServer() : started_(true) {} |
44 ~NullHttpServer() {} | 49 ~NullHttpServer() {} |
45 bool started_; | 50 bool started_; |
46 }; | 51 }; |
47 | 52 |
48 | 53 |
49 template <> | 54 template <> |
50 class HttpFetcherTest<MockHttpFetcher> : public ::testing::Test { | 55 class HttpFetcherTest<MockHttpFetcher> : public ::testing::Test { |
51 public: | 56 public: |
52 HttpFetcher* NewLargeFetcher() { | 57 HttpFetcher* NewLargeFetcher() { |
53 vector<char> big_data(1000000); | 58 vector<char> big_data(1000000); |
54 return new MockHttpFetcher(big_data.data(), big_data.size()); | 59 return new MockHttpFetcher(big_data.data(), big_data.size()); |
55 } | 60 } |
56 HttpFetcher* NewSmallFetcher() { | 61 HttpFetcher* NewSmallFetcher() { |
57 return new MockHttpFetcher("x", 1); | 62 return new MockHttpFetcher("x", 1); |
58 } | 63 } |
59 string BigUrl() const { | 64 string BigUrl() const { |
60 return "unused://unused"; | 65 return "unused://unused"; |
61 } | 66 } |
62 string SmallUrl() const { | 67 string SmallUrl() const { |
63 return "unused://unused"; | 68 return "unused://unused"; |
64 } | 69 } |
65 bool IsMock() const { return true; } | 70 bool IsMock() const { return true; } |
| 71 bool IsMulti() const { return false; } |
66 typedef NullHttpServer HttpServer; | 72 typedef NullHttpServer HttpServer; |
67 void IgnoreServerAborting(HttpServer* server) const {} | 73 void IgnoreServerAborting(HttpServer* server) const {} |
68 }; | 74 }; |
69 | 75 |
70 class PythonHttpServer { | 76 class PythonHttpServer { |
71 public: | 77 public: |
72 PythonHttpServer() { | 78 PythonHttpServer() { |
73 char *argv[2] = {strdup("./test_http_server"), NULL}; | 79 char *argv[2] = {strdup("./test_http_server"), NULL}; |
74 GError *err; | 80 GError *err; |
75 started_ = false; | 81 started_ = false; |
(...skipping 18 matching lines...) Expand all Loading... |
94 LOG(INFO) << "done running wget to start"; | 100 LOG(INFO) << "done running wget to start"; |
95 usleep(10 * 1000); // 10 ms | 101 usleep(10 * 1000); // 10 ms |
96 tries--; | 102 tries--; |
97 if (tries == 0) { | 103 if (tries == 0) { |
98 LOG(ERROR) << "Unable to start server."; | 104 LOG(ERROR) << "Unable to start server."; |
99 started_ = false; | 105 started_ = false; |
100 break; | 106 break; |
101 } | 107 } |
102 } | 108 } |
103 free(argv[0]); | 109 free(argv[0]); |
| 110 LOG(INFO) << "gdb attach now!"; |
104 return; | 111 return; |
105 } | 112 } |
106 ~PythonHttpServer() { | 113 ~PythonHttpServer() { |
107 if (!started_) | 114 if (!started_) |
108 return; | 115 return; |
109 // request that the server exit itself | 116 // request that the server exit itself |
110 LOG(INFO) << "running wget to exit"; | 117 LOG(INFO) << "running wget to exit"; |
111 int rc = system((string("wget -t 1 --output-document=/dev/null ") + | 118 int rc = system((string("wget -t 1 --output-document=/dev/null ") + |
112 LocalServerUrlForPath("/quitquitquit")).c_str()); | 119 LocalServerUrlForPath("/quitquitquit")).c_str()); |
113 LOG(INFO) << "done running wget to exit"; | 120 LOG(INFO) << "done running wget to exit"; |
114 if (validate_quit_) | 121 if (validate_quit_) |
115 EXPECT_EQ(0, rc); | 122 EXPECT_EQ(0, rc); |
116 waitpid(pid_, NULL, 0); | 123 waitpid(pid_, NULL, 0); |
117 } | 124 } |
118 GPid pid_; | 125 GPid pid_; |
119 bool started_; | 126 bool started_; |
120 bool validate_quit_; | 127 bool validate_quit_; |
121 }; | 128 }; |
122 | 129 |
123 template <> | 130 template <> |
124 class HttpFetcherTest<LibcurlHttpFetcher> : public ::testing::Test { | 131 class HttpFetcherTest<LibcurlHttpFetcher> : public ::testing::Test { |
125 public: | 132 public: |
126 HttpFetcher* NewLargeFetcher() { | 133 virtual HttpFetcher* NewLargeFetcher() { |
127 LibcurlHttpFetcher *ret = new LibcurlHttpFetcher; | 134 LibcurlHttpFetcher *ret = new LibcurlHttpFetcher; |
128 // Speed up test execution. | 135 // Speed up test execution. |
129 ret->set_idle_seconds(1); | 136 ret->set_idle_seconds(1); |
130 ret->set_retry_seconds(1); | 137 ret->set_retry_seconds(1); |
131 return ret; | 138 return ret; |
132 } | 139 } |
133 HttpFetcher* NewSmallFetcher() { | 140 HttpFetcher* NewSmallFetcher() { |
134 return NewLargeFetcher(); | 141 return NewLargeFetcher(); |
135 } | 142 } |
136 string BigUrl() const { | 143 string BigUrl() const { |
137 return LocalServerUrlForPath("/big"); | 144 return LocalServerUrlForPath("/big"); |
138 } | 145 } |
139 string SmallUrl() const { | 146 string SmallUrl() const { |
140 return LocalServerUrlForPath("/foo"); | 147 return LocalServerUrlForPath("/foo"); |
141 } | 148 } |
142 bool IsMock() const { return false; } | 149 bool IsMock() const { return false; } |
| 150 bool IsMulti() const { return false; } |
143 typedef PythonHttpServer HttpServer; | 151 typedef PythonHttpServer HttpServer; |
144 void IgnoreServerAborting(HttpServer* server) const { | 152 void IgnoreServerAborting(HttpServer* server) const { |
145 PythonHttpServer *pyserver = reinterpret_cast<PythonHttpServer*>(server); | 153 PythonHttpServer *pyserver = reinterpret_cast<PythonHttpServer*>(server); |
146 pyserver->validate_quit_ = false; | 154 pyserver->validate_quit_ = false; |
147 } | 155 } |
148 }; | 156 }; |
149 | 157 |
150 typedef ::testing::Types<LibcurlHttpFetcher, MockHttpFetcher> | 158 template <> |
151 HttpFetcherTestTypes; | 159 class HttpFetcherTest<MultiHttpFetcher<LibcurlHttpFetcher> > |
| 160 : public HttpFetcherTest<LibcurlHttpFetcher> { |
| 161 public: |
| 162 HttpFetcher* NewLargeFetcher() { |
| 163 MultiHttpFetcher<LibcurlHttpFetcher> *ret = |
| 164 new MultiHttpFetcher<LibcurlHttpFetcher>; |
| 165 MultiHttpFetcher<LibcurlHttpFetcher>::RangesVect |
| 166 ranges(1, make_pair(0, -1)); |
| 167 ret->set_ranges(ranges); |
| 168 // Speed up test execution. |
| 169 ret->set_idle_seconds(1); |
| 170 ret->set_retry_seconds(1); |
| 171 return ret; |
| 172 } |
| 173 bool IsMulti() const { return true; } |
| 174 }; |
| 175 |
| 176 typedef ::testing::Types<LibcurlHttpFetcher, |
| 177 MockHttpFetcher, |
| 178 MultiHttpFetcher<LibcurlHttpFetcher> > |
| 179 HttpFetcherTestTypes; |
152 TYPED_TEST_CASE(HttpFetcherTest, HttpFetcherTestTypes); | 180 TYPED_TEST_CASE(HttpFetcherTest, HttpFetcherTestTypes); |
153 | 181 |
154 namespace { | 182 namespace { |
155 class HttpFetcherTestDelegate : public HttpFetcherDelegate { | 183 class HttpFetcherTestDelegate : public HttpFetcherDelegate { |
156 public: | 184 public: |
157 virtual void ReceivedBytes(HttpFetcher* fetcher, | 185 virtual void ReceivedBytes(HttpFetcher* fetcher, |
158 const char* bytes, int length) { | 186 const char* bytes, int length) { |
159 char str[length + 1]; | 187 char str[length + 1]; |
160 memset(str, 0, length + 1); | 188 memset(str, 0, length + 1); |
161 memcpy(str, bytes, length); | 189 memcpy(str, bytes, length); |
(...skipping 164 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
326 ASSERT_TRUE(server.started_); | 354 ASSERT_TRUE(server.started_); |
327 GSource* timeout_source_; | 355 GSource* timeout_source_; |
328 timeout_source_ = g_timeout_source_new(0); // ms | 356 timeout_source_ = g_timeout_source_new(0); // ms |
329 g_source_set_callback(timeout_source_, AbortingTimeoutCallback, &delegate, | 357 g_source_set_callback(timeout_source_, AbortingTimeoutCallback, &delegate, |
330 NULL); | 358 NULL); |
331 g_source_attach(timeout_source_, NULL); | 359 g_source_attach(timeout_source_, NULL); |
332 fetcher->BeginTransfer(this->BigUrl()); | 360 fetcher->BeginTransfer(this->BigUrl()); |
333 | 361 |
334 g_main_loop_run(loop); | 362 g_main_loop_run(loop); |
335 g_source_destroy(timeout_source_); | 363 g_source_destroy(timeout_source_); |
336 EXPECT_EQ(0, fetcher->http_response_code()); | |
337 } | 364 } |
338 g_main_loop_unref(loop); | 365 g_main_loop_unref(loop); |
339 } | 366 } |
340 | 367 |
341 namespace { | 368 namespace { |
342 class FlakyHttpFetcherTestDelegate : public HttpFetcherDelegate { | 369 class FlakyHttpFetcherTestDelegate : public HttpFetcherDelegate { |
343 public: | 370 public: |
344 virtual void ReceivedBytes(HttpFetcher* fetcher, | 371 virtual void ReceivedBytes(HttpFetcher* fetcher, |
345 const char* bytes, int length) { | 372 const char* bytes, int length) { |
346 data.append(bytes, length); | 373 data.append(bytes, length); |
(...skipping 196 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
543 ASSERT_TRUE(server.started_); | 570 ASSERT_TRUE(server.started_); |
544 string url; | 571 string url; |
545 for (int r = 0; r < LibcurlHttpFetcher::kMaxRedirects + 1; r++) { | 572 for (int r = 0; r < LibcurlHttpFetcher::kMaxRedirects + 1; r++) { |
546 url += base::StringPrintf("/redirect/%d", | 573 url += base::StringPrintf("/redirect/%d", |
547 kRedirectCodes[r % arraysize(kRedirectCodes)]); | 574 kRedirectCodes[r % arraysize(kRedirectCodes)]); |
548 } | 575 } |
549 url += "/medium"; | 576 url += "/medium"; |
550 RedirectTest(false, url, this->NewLargeFetcher()); | 577 RedirectTest(false, url, this->NewLargeFetcher()); |
551 } | 578 } |
552 | 579 |
| 580 namespace { |
| 581 class MultiHttpFetcherTestDelegate : public HttpFetcherDelegate { |
| 582 public: |
| 583 MultiHttpFetcherTestDelegate(int expected_response_code) |
| 584 : expected_response_code_(expected_response_code) {} |
| 585 virtual void ReceivedBytes(HttpFetcher* fetcher, |
| 586 const char* bytes, int length) { |
| 587 data.append(bytes, length); |
| 588 } |
| 589 virtual void TransferComplete(HttpFetcher* fetcher, bool successful) { |
| 590 EXPECT_EQ(expected_response_code_ != 0, successful); |
| 591 if (expected_response_code_ != 0) |
| 592 EXPECT_EQ(expected_response_code_, fetcher->http_response_code()); |
| 593 g_main_loop_quit(loop_); |
| 594 } |
| 595 int expected_response_code_; |
| 596 string data; |
| 597 GMainLoop* loop_; |
| 598 }; |
| 599 |
| 600 void MultiTest(HttpFetcher* fetcher_in, |
| 601 const string& url, |
| 602 const MultiHttpFetcher<LibcurlHttpFetcher>::RangesVect& ranges, |
| 603 const string& expected_prefix, |
| 604 off_t expected_size, |
| 605 int expected_response_code) { |
| 606 GMainLoop* loop = g_main_loop_new(g_main_context_default(), FALSE); |
| 607 { |
| 608 MultiHttpFetcherTestDelegate delegate(expected_response_code); |
| 609 delegate.loop_ = loop; |
| 610 scoped_ptr<HttpFetcher> fetcher(fetcher_in); |
| 611 MultiHttpFetcher<LibcurlHttpFetcher>* multi_fetcher = |
| 612 dynamic_cast<MultiHttpFetcher<LibcurlHttpFetcher>*>(fetcher.get()); |
| 613 ASSERT_TRUE(multi_fetcher); |
| 614 multi_fetcher->set_ranges(ranges); |
| 615 fetcher->set_delegate(&delegate); |
| 616 |
| 617 StartTransferArgs start_xfer_args = {fetcher.get(), url}; |
| 618 |
| 619 g_timeout_add(0, StartTransfer, &start_xfer_args); |
| 620 g_main_loop_run(loop); |
| 621 |
| 622 EXPECT_EQ(expected_size, delegate.data.size()); |
| 623 EXPECT_EQ(expected_prefix, |
| 624 string(delegate.data.data(), expected_prefix.size())); |
| 625 } |
| 626 g_main_loop_unref(loop); |
| 627 } |
| 628 } // namespace {} |
| 629 |
| 630 TYPED_TEST(HttpFetcherTest, MultiHttpFetcherSimplTest) { |
| 631 if (!this->IsMulti()) |
| 632 return; |
| 633 typename TestFixture::HttpServer server; |
| 634 ASSERT_TRUE(server.started_); |
| 635 |
| 636 MultiHttpFetcher<LibcurlHttpFetcher>::RangesVect ranges; |
| 637 ranges.push_back(make_pair(0, 25)); |
| 638 ranges.push_back(make_pair(99, -1)); |
| 639 MultiTest(this->NewLargeFetcher(), |
| 640 this->BigUrl(), |
| 641 ranges, |
| 642 "abcdefghijabcdefghijabcdejabcdefghijabcdef", |
| 643 kBigSize - (99 - 25), |
| 644 206); |
| 645 } |
| 646 |
| 647 TYPED_TEST(HttpFetcherTest, MultiHttpFetcherLengthLimitTest) { |
| 648 if (!this->IsMulti()) |
| 649 return; |
| 650 typename TestFixture::HttpServer server; |
| 651 ASSERT_TRUE(server.started_); |
| 652 |
| 653 MultiHttpFetcher<LibcurlHttpFetcher>::RangesVect ranges; |
| 654 ranges.push_back(make_pair(0, 24)); |
| 655 MultiTest(this->NewLargeFetcher(), |
| 656 this->BigUrl(), |
| 657 ranges, |
| 658 "abcdefghijabcdefghijabcd", |
| 659 24, |
| 660 200); |
| 661 } |
| 662 |
| 663 TYPED_TEST(HttpFetcherTest, MultiHttpFetcherMultiEndTest) { |
| 664 if (!this->IsMulti()) |
| 665 return; |
| 666 typename TestFixture::HttpServer server; |
| 667 ASSERT_TRUE(server.started_); |
| 668 |
| 669 MultiHttpFetcher<LibcurlHttpFetcher>::RangesVect ranges; |
| 670 ranges.push_back(make_pair(kBigSize - 2, -1)); |
| 671 ranges.push_back(make_pair(kBigSize - 3, -1)); |
| 672 MultiTest(this->NewLargeFetcher(), |
| 673 this->BigUrl(), |
| 674 ranges, |
| 675 "ijhij", |
| 676 5, |
| 677 206); |
| 678 } |
| 679 |
| 680 TYPED_TEST(HttpFetcherTest, MultiHttpFetcherInsufficientTest) { |
| 681 if (!this->IsMulti()) |
| 682 return; |
| 683 typename TestFixture::HttpServer server; |
| 684 ASSERT_TRUE(server.started_); |
| 685 |
| 686 MultiHttpFetcher<LibcurlHttpFetcher>::RangesVect ranges; |
| 687 ranges.push_back(make_pair(kBigSize - 2, 4)); |
| 688 for (int i = 0; i < 2; ++i) { |
| 689 MultiTest(this->NewLargeFetcher(), |
| 690 this->BigUrl(), |
| 691 ranges, |
| 692 "ij", |
| 693 2, |
| 694 0); |
| 695 ranges.push_back(make_pair(0, 5)); |
| 696 } |
| 697 } |
| 698 |
553 } // namespace chromeos_update_engine | 699 } // namespace chromeos_update_engine |
OLD | NEW |