crx_downloader_unittest.cc 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405
  1. // Copyright 2013 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "components/update_client/crx_downloader.h"
  5. #include <utility>
  6. #include "base/bind.h"
  7. #include "base/files/file_path.h"
  8. #include "base/files/file_util.h"
  9. #include "base/memory/ref_counted.h"
  10. #include "base/path_service.h"
  11. #include "base/run_loop.h"
  12. #include "base/test/bind.h"
  13. #include "base/test/task_environment.h"
  14. #include "base/threading/thread_task_runner_handle.h"
  15. #include "build/build_config.h"
  16. #include "components/update_client/crx_downloader_factory.h"
  17. #include "components/update_client/net/network_chromium.h"
  18. #include "components/update_client/update_client_errors.h"
  19. #include "components/update_client/utils.h"
  20. #include "net/base/net_errors.h"
  21. #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
  22. #include "services/network/public/mojom/url_response_head.mojom.h"
  23. #include "services/network/test/test_url_loader_factory.h"
  24. #include "testing/gtest/include/gtest/gtest.h"
  25. using base::ContentsEqual;
  26. namespace update_client {
  27. namespace {
  28. const char kTestFileName[] = "jebgalgnebhfojomionfpkfelancnnkf.crx";
  29. const char hash_jebg[] =
  30. "7ab32f071cd9b5ef8e0d7913be161f532d98b3e9fa284a7cd8059c3409ce0498";
  31. base::FilePath MakeTestFilePath(const char* file) {
  32. base::FilePath path;
  33. base::PathService::Get(base::DIR_SOURCE_ROOT, &path);
  34. return path.AppendASCII("components/test/data/update_client")
  35. .AppendASCII(file);
  36. }
  37. } // namespace
  38. class CrxDownloaderTest : public testing::Test {
  39. public:
  40. CrxDownloaderTest();
  41. ~CrxDownloaderTest() override;
  42. // Overrides from testing::Test.
  43. void SetUp() override;
  44. void TearDown() override;
  45. void Quit();
  46. void RunThreads();
  47. void RunThreadsUntilIdle();
  48. void DownloadComplete(const CrxDownloader::Result& result);
  49. void DownloadProgress(int64_t downloaded_bytes, int64_t total_bytes);
  50. int GetInterceptorCount() { return interceptor_count_; }
  51. void AddResponse(const GURL& url,
  52. const base::FilePath& file_path,
  53. int net_error);
  54. protected:
  55. scoped_refptr<CrxDownloader> crx_downloader_;
  56. network::TestURLLoaderFactory test_url_loader_factory_;
  57. CrxDownloader::DownloadCallback callback_;
  58. CrxDownloader::ProgressCallback progress_callback_;
  59. int num_download_complete_calls_ = 0;
  60. CrxDownloader::Result download_complete_result_ = {};
  61. // These members are updated by DownloadProgress.
  62. int num_progress_calls_ = 0;
  63. int64_t downloaded_bytes_ = -1;
  64. int64_t total_bytes_ = -1;
  65. // Accumulates the number of loads triggered.
  66. int interceptor_count_ = 0;
  67. private:
  68. base::test::TaskEnvironment task_environment_;
  69. scoped_refptr<network::SharedURLLoaderFactory>
  70. test_shared_url_loader_factory_;
  71. base::OnceClosure quit_closure_;
  72. };
  73. CrxDownloaderTest::CrxDownloaderTest()
  74. : callback_(base::BindOnce(&CrxDownloaderTest::DownloadComplete,
  75. base::Unretained(this))),
  76. progress_callback_(
  77. base::BindRepeating(&CrxDownloaderTest::DownloadProgress,
  78. base::Unretained(this))),
  79. task_environment_(base::test::TaskEnvironment::MainThreadType::IO),
  80. test_shared_url_loader_factory_(
  81. base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
  82. &test_url_loader_factory_)) {}
  83. CrxDownloaderTest::~CrxDownloaderTest() = default;
  84. void CrxDownloaderTest::SetUp() {
  85. // Do not use the background downloader in these tests.
  86. crx_downloader_ =
  87. MakeCrxDownloaderFactory(
  88. base::MakeRefCounted<NetworkFetcherChromiumFactory>(
  89. test_shared_url_loader_factory_,
  90. base::BindRepeating([](const GURL& url) { return false; })))
  91. ->MakeCrxDownloader(false);
  92. crx_downloader_->set_progress_callback(progress_callback_);
  93. test_url_loader_factory_.SetInterceptor(base::BindLambdaForTesting(
  94. [&](const network::ResourceRequest& request) { interceptor_count_++; }));
  95. }
  96. void CrxDownloaderTest::TearDown() {
  97. crx_downloader_ = nullptr;
  98. }
  99. void CrxDownloaderTest::Quit() {
  100. if (!quit_closure_.is_null())
  101. std::move(quit_closure_).Run();
  102. }
  103. void CrxDownloaderTest::DownloadComplete(const CrxDownloader::Result& result) {
  104. ++num_download_complete_calls_;
  105. download_complete_result_ = result;
  106. Quit();
  107. }
  108. void CrxDownloaderTest::DownloadProgress(int64_t downloaded_bytes,
  109. int64_t total_bytes) {
  110. if (downloaded_bytes != -1 && total_bytes != -1)
  111. DCHECK_LE(downloaded_bytes, total_bytes);
  112. downloaded_bytes_ = downloaded_bytes;
  113. total_bytes_ = total_bytes;
  114. ++num_progress_calls_;
  115. }
  116. void CrxDownloaderTest::AddResponse(const GURL& url,
  117. const base::FilePath& file_path,
  118. int net_error) {
  119. if (net_error == net::OK) {
  120. std::string data;
  121. EXPECT_TRUE(base::ReadFileToString(file_path, &data));
  122. auto head = network::mojom::URLResponseHead::New();
  123. head->content_length = data.size();
  124. network::URLLoaderCompletionStatus status(net_error);
  125. status.decoded_body_length = data.size();
  126. test_url_loader_factory_.AddResponse(url, std::move(head), data, status);
  127. return;
  128. }
  129. EXPECT_NE(net_error, net::OK);
  130. test_url_loader_factory_.AddResponse(
  131. url, network::mojom::URLResponseHead::New(), std::string(),
  132. network::URLLoaderCompletionStatus(net_error));
  133. }
  134. void CrxDownloaderTest::RunThreads() {
  135. base::RunLoop runloop;
  136. quit_closure_ = runloop.QuitClosure();
  137. runloop.Run();
  138. // Since some tests need to drain currently enqueued tasks such as network
  139. // intercepts on the IO thread, run the threads until they are
  140. // idle. The component updater service won't loop again until the loop count
  141. // is set and the service is started.
  142. RunThreadsUntilIdle();
  143. }
  144. // TODO(crbug.com/1104691): rewrite the tests to not use RunUntilIdle().
  145. void CrxDownloaderTest::RunThreadsUntilIdle() {
  146. task_environment_.RunUntilIdle();
  147. base::RunLoop().RunUntilIdle();
  148. }
  149. // Tests that starting a download without a url results in an error.
  150. TEST_F(CrxDownloaderTest, NoUrl) {
  151. std::vector<GURL> urls;
  152. crx_downloader_->StartDownload(urls, std::string("abcd"),
  153. std::move(callback_));
  154. RunThreadsUntilIdle();
  155. EXPECT_EQ(1, num_download_complete_calls_);
  156. EXPECT_EQ(static_cast<int>(CrxDownloaderError::NO_URL),
  157. download_complete_result_.error);
  158. EXPECT_TRUE(download_complete_result_.response.empty());
  159. EXPECT_EQ(0, num_progress_calls_);
  160. }
  161. // Tests that starting a download without providing a hash results in an error.
  162. TEST_F(CrxDownloaderTest, NoHash) {
  163. std::vector<GURL> urls(1, GURL("http://somehost/somefile"));
  164. crx_downloader_->StartDownload(urls, std::string(), std::move(callback_));
  165. RunThreadsUntilIdle();
  166. EXPECT_EQ(1, num_download_complete_calls_);
  167. EXPECT_EQ(static_cast<int>(CrxDownloaderError::NO_HASH),
  168. download_complete_result_.error);
  169. EXPECT_TRUE(download_complete_result_.response.empty());
  170. EXPECT_EQ(0, num_progress_calls_);
  171. }
  172. // Tests that downloading from one url is successful.
  173. TEST_F(CrxDownloaderTest, OneUrl) {
  174. const GURL expected_crx_url =
  175. GURL("http://localhost/download/jebgalgnebhfojomionfpkfelancnnkf.crx");
  176. const base::FilePath test_file(MakeTestFilePath(kTestFileName));
  177. AddResponse(expected_crx_url, test_file, net::OK);
  178. crx_downloader_->StartDownloadFromUrl(
  179. expected_crx_url, std::string(hash_jebg), std::move(callback_));
  180. RunThreads();
  181. EXPECT_EQ(1, GetInterceptorCount());
  182. EXPECT_EQ(1, num_download_complete_calls_);
  183. EXPECT_EQ(0, download_complete_result_.error);
  184. EXPECT_TRUE(ContentsEqual(download_complete_result_.response, test_file));
  185. EXPECT_TRUE(
  186. DeleteFileAndEmptyParentDirectory(download_complete_result_.response));
  187. EXPECT_LE(1, num_progress_calls_);
  188. EXPECT_EQ(total_bytes_, 1015);
  189. }
  190. // Tests that downloading from one url fails if the actual hash of the file
  191. // does not match the expected hash.
  192. TEST_F(CrxDownloaderTest, OneUrlBadHash) {
  193. const GURL expected_crx_url =
  194. GURL("http://localhost/download/jebgalgnebhfojomionfpkfelancnnkf.crx");
  195. const base::FilePath test_file(MakeTestFilePath(kTestFileName));
  196. AddResponse(expected_crx_url, test_file, net::OK);
  197. crx_downloader_->StartDownloadFromUrl(
  198. expected_crx_url,
  199. std::string(
  200. "813c59747e139a608b3b5fc49633affc6db574373f309f156ea6d27229c0b3f9"),
  201. std::move(callback_));
  202. RunThreads();
  203. EXPECT_EQ(1, GetInterceptorCount());
  204. EXPECT_EQ(1, num_download_complete_calls_);
  205. EXPECT_EQ(static_cast<int>(CrxDownloaderError::BAD_HASH),
  206. download_complete_result_.error);
  207. EXPECT_TRUE(download_complete_result_.response.empty());
  208. EXPECT_LE(1, num_progress_calls_);
  209. }
  210. // Tests that specifying two urls has no side effects. Expect a successful
  211. // download, and only one download request be made.
  212. TEST_F(CrxDownloaderTest, TwoUrls) {
  213. const GURL expected_crx_url =
  214. GURL("http://localhost/download/jebgalgnebhfojomionfpkfelancnnkf.crx");
  215. const base::FilePath test_file(MakeTestFilePath(kTestFileName));
  216. AddResponse(expected_crx_url, test_file, net::OK);
  217. std::vector<GURL> urls;
  218. urls.push_back(expected_crx_url);
  219. urls.push_back(expected_crx_url);
  220. crx_downloader_->StartDownload(urls, std::string(hash_jebg),
  221. std::move(callback_));
  222. RunThreads();
  223. EXPECT_EQ(1, GetInterceptorCount());
  224. EXPECT_EQ(1, num_download_complete_calls_);
  225. EXPECT_EQ(0, download_complete_result_.error);
  226. EXPECT_TRUE(ContentsEqual(download_complete_result_.response, test_file));
  227. EXPECT_TRUE(
  228. DeleteFileAndEmptyParentDirectory(download_complete_result_.response));
  229. EXPECT_LE(1, num_progress_calls_);
  230. }
  231. // Tests that the fallback to a valid url is successful.
  232. TEST_F(CrxDownloaderTest, TwoUrls_FirstInvalid) {
  233. const GURL expected_crx_url =
  234. GURL("http://localhost/download/jebgalgnebhfojomionfpkfelancnnkf.crx");
  235. const GURL no_file_url =
  236. GURL("http://localhost/download/ihfokbkgjpifnbbojhneepfflplebdkc.crx");
  237. const base::FilePath test_file(MakeTestFilePath(kTestFileName));
  238. AddResponse(expected_crx_url, test_file, net::OK);
  239. AddResponse(no_file_url, base::FilePath(), net::ERR_FILE_NOT_FOUND);
  240. std::vector<GURL> urls;
  241. urls.push_back(no_file_url);
  242. urls.push_back(expected_crx_url);
  243. crx_downloader_->StartDownload(urls, std::string(hash_jebg),
  244. std::move(callback_));
  245. RunThreads();
  246. EXPECT_EQ(2, GetInterceptorCount());
  247. EXPECT_EQ(1, num_download_complete_calls_);
  248. EXPECT_EQ(0, download_complete_result_.error);
  249. EXPECT_TRUE(ContentsEqual(download_complete_result_.response, test_file));
  250. EXPECT_TRUE(
  251. DeleteFileAndEmptyParentDirectory(download_complete_result_.response));
  252. // Expect at least some progress reported by the loader.
  253. EXPECT_LE(1, num_progress_calls_);
  254. const auto download_metrics = crx_downloader_->download_metrics();
  255. ASSERT_EQ(2u, download_metrics.size());
  256. EXPECT_EQ(no_file_url, download_metrics[0].url);
  257. EXPECT_EQ(net::ERR_FILE_NOT_FOUND, download_metrics[0].error);
  258. EXPECT_EQ(-1, download_metrics[0].downloaded_bytes);
  259. EXPECT_EQ(-1, download_metrics[0].total_bytes);
  260. EXPECT_EQ(expected_crx_url, download_metrics[1].url);
  261. EXPECT_EQ(0, download_metrics[1].error);
  262. EXPECT_EQ(1015, download_metrics[1].downloaded_bytes);
  263. EXPECT_EQ(1015, download_metrics[1].total_bytes);
  264. }
  265. // Tests that the download succeeds if the first url is correct and the
  266. // second bad url does not have a side-effect.
  267. TEST_F(CrxDownloaderTest, TwoUrls_SecondInvalid) {
  268. const GURL expected_crx_url =
  269. GURL("http://localhost/download/jebgalgnebhfojomionfpkfelancnnkf.crx");
  270. const GURL no_file_url =
  271. GURL("http://localhost/download/ihfokbkgjpifnbbojhneepfflplebdkc.crx");
  272. const base::FilePath test_file(MakeTestFilePath(kTestFileName));
  273. AddResponse(expected_crx_url, test_file, net::OK);
  274. AddResponse(no_file_url, base::FilePath(), net::ERR_FILE_NOT_FOUND);
  275. std::vector<GURL> urls;
  276. urls.push_back(expected_crx_url);
  277. urls.push_back(no_file_url);
  278. crx_downloader_->StartDownload(urls, std::string(hash_jebg),
  279. std::move(callback_));
  280. RunThreads();
  281. EXPECT_EQ(1, GetInterceptorCount());
  282. EXPECT_EQ(1, num_download_complete_calls_);
  283. EXPECT_EQ(0, download_complete_result_.error);
  284. EXPECT_TRUE(ContentsEqual(download_complete_result_.response, test_file));
  285. EXPECT_TRUE(
  286. DeleteFileAndEmptyParentDirectory(download_complete_result_.response));
  287. EXPECT_LE(1, num_progress_calls_);
  288. EXPECT_EQ(1u, crx_downloader_->download_metrics().size());
  289. }
  290. // Tests that the download fails if both urls don't serve content.
  291. TEST_F(CrxDownloaderTest, TwoUrls_BothInvalid) {
  292. const GURL expected_crx_url =
  293. GURL("http://localhost/download/jebgalgnebhfojomionfpkfelancnnkf.crx");
  294. AddResponse(expected_crx_url, base::FilePath(), net::ERR_FILE_NOT_FOUND);
  295. std::vector<GURL> urls;
  296. urls.push_back(expected_crx_url);
  297. urls.push_back(expected_crx_url);
  298. crx_downloader_->StartDownload(urls, std::string(hash_jebg),
  299. std::move(callback_));
  300. RunThreads();
  301. EXPECT_EQ(2, GetInterceptorCount());
  302. EXPECT_EQ(1, num_download_complete_calls_);
  303. EXPECT_NE(0, download_complete_result_.error);
  304. EXPECT_TRUE(download_complete_result_.response.empty());
  305. const auto download_metrics = crx_downloader_->download_metrics();
  306. ASSERT_EQ(2u, download_metrics.size());
  307. EXPECT_EQ(expected_crx_url, download_metrics[0].url);
  308. EXPECT_EQ(net::ERR_FILE_NOT_FOUND, download_metrics[0].error);
  309. EXPECT_EQ(-1, download_metrics[0].downloaded_bytes);
  310. EXPECT_EQ(-1, download_metrics[0].total_bytes);
  311. EXPECT_EQ(expected_crx_url, download_metrics[1].url);
  312. EXPECT_EQ(net::ERR_FILE_NOT_FOUND, download_metrics[1].error);
  313. EXPECT_EQ(-1, download_metrics[1].downloaded_bytes);
  314. EXPECT_EQ(-1, download_metrics[1].total_bytes);
  315. }
  316. } // namespace update_client