| OLD | NEW |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium 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 "base/bind.h" | 5 #include "base/bind.h" |
| 6 #include "base/bind_helpers.h" | 6 #include "base/bind_helpers.h" |
| 7 #include "base/files/file_path.h" | 7 #include "base/files/file_path.h" |
| 8 #include "base/files/file_util.h" | 8 #include "base/files/file_util.h" |
| 9 #include "base/location.h" | 9 #include "base/location.h" |
| 10 #include "base/memory/ref_counted.h" | 10 #include "base/memory/ref_counted.h" |
| (...skipping 119 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 130 | 130 |
| 131 using std::string; | 131 using std::string; |
| 132 | 132 |
| 133 class UpdateClientTest : public testing::Test { | 133 class UpdateClientTest : public testing::Test { |
| 134 public: | 134 public: |
| 135 UpdateClientTest(); | 135 UpdateClientTest(); |
| 136 ~UpdateClientTest() override; | 136 ~UpdateClientTest() override; |
| 137 | 137 |
| 138 protected: | 138 protected: |
| 139 void RunThreads(); | 139 void RunThreads(); |
| 140 void StopWorkerPool(); | |
| 141 | 140 |
| 142 // Returns the full path to a test file. | 141 // Returns the full path to a test file. |
| 143 static base::FilePath TestFilePath(const char* file); | 142 static base::FilePath TestFilePath(const char* file); |
| 144 | 143 |
| 145 scoped_refptr<update_client::Configurator> config() { return config_; } | 144 scoped_refptr<update_client::Configurator> config() { return config_; } |
| 146 | 145 |
| 147 base::Closure quit_closure() { return quit_closure_; } | 146 base::Closure quit_closure() { return quit_closure_; } |
| 148 | 147 |
| 149 private: | 148 private: |
| 150 static const int kNumWorkerThreads_ = 2; | 149 static const int kNumWorkerThreads_ = 2; |
| (...skipping 20 matching lines...) Expand all Loading... |
| 171 message_loop_.task_runner()); | 170 message_loop_.task_runner()); |
| 172 } | 171 } |
| 173 | 172 |
| 174 UpdateClientTest::~UpdateClientTest() { | 173 UpdateClientTest::~UpdateClientTest() { |
| 175 } | 174 } |
| 176 | 175 |
| 177 void UpdateClientTest::RunThreads() { | 176 void UpdateClientTest::RunThreads() { |
| 178 runloop_.Run(); | 177 runloop_.Run(); |
| 179 } | 178 } |
| 180 | 179 |
| 181 void UpdateClientTest::StopWorkerPool() { | |
| 182 worker_pool_->pool()->Shutdown(); | |
| 183 } | |
| 184 | |
| 185 base::FilePath UpdateClientTest::TestFilePath(const char* file) { | 180 base::FilePath UpdateClientTest::TestFilePath(const char* file) { |
| 186 base::FilePath path; | 181 base::FilePath path; |
| 187 PathService::Get(base::DIR_SOURCE_ROOT, &path); | 182 PathService::Get(base::DIR_SOURCE_ROOT, &path); |
| 188 return path.AppendASCII("components") | 183 return path.AppendASCII("components") |
| 189 .AppendASCII("test") | 184 .AppendASCII("test") |
| 190 .AppendASCII("data") | 185 .AppendASCII("data") |
| 191 .AppendASCII("update_client") | 186 .AppendASCII("update_client") |
| 192 .AppendASCII(file); | 187 .AppendASCII(file); |
| 193 } | 188 } |
| 194 | 189 |
| (...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 280 std::vector<std::string> ids; | 275 std::vector<std::string> ids; |
| 281 ids.push_back(std::string("jebgalgnebhfojomionfpkfelancnnkf")); | 276 ids.push_back(std::string("jebgalgnebhfojomionfpkfelancnnkf")); |
| 282 | 277 |
| 283 update_client->Update( | 278 update_client->Update( |
| 284 ids, base::Bind(&DataCallbackFake::Callback), | 279 ids, base::Bind(&DataCallbackFake::Callback), |
| 285 base::Bind(&CompletionCallbackFake::Callback, quit_closure())); | 280 base::Bind(&CompletionCallbackFake::Callback, quit_closure())); |
| 286 | 281 |
| 287 RunThreads(); | 282 RunThreads(); |
| 288 | 283 |
| 289 update_client->RemoveObserver(&observer); | 284 update_client->RemoveObserver(&observer); |
| 290 | |
| 291 StopWorkerPool(); | |
| 292 } | 285 } |
| 293 | 286 |
| 294 // Tests the scenario where two CRXs are checked for updates. On CRX has | 287 // Tests the scenario where two CRXs are checked for updates. On CRX has |
| 295 // an update, the other CRX does not. | 288 // an update, the other CRX does not. |
| 296 TEST_F(UpdateClientTest, TwoCrxUpdateNoUpdate) { | 289 TEST_F(UpdateClientTest, TwoCrxUpdateNoUpdate) { |
| 297 class DataCallbackFake { | 290 class DataCallbackFake { |
| 298 public: | 291 public: |
| 299 static void Callback(const std::vector<std::string>& ids, | 292 static void Callback(const std::vector<std::string>& ids, |
| 300 std::vector<CrxComponent>* components) { | 293 std::vector<CrxComponent>* components) { |
| 301 CrxComponent crx1; | 294 CrxComponent crx1; |
| (...skipping 161 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 463 ids.push_back(std::string("jebgalgnebhfojomionfpkfelancnnkf")); | 456 ids.push_back(std::string("jebgalgnebhfojomionfpkfelancnnkf")); |
| 464 ids.push_back(std::string("abagagagagagagagagagagagagagagag")); | 457 ids.push_back(std::string("abagagagagagagagagagagagagagagag")); |
| 465 | 458 |
| 466 update_client->Update( | 459 update_client->Update( |
| 467 ids, base::Bind(&DataCallbackFake::Callback), | 460 ids, base::Bind(&DataCallbackFake::Callback), |
| 468 base::Bind(&CompletionCallbackFake::Callback, quit_closure())); | 461 base::Bind(&CompletionCallbackFake::Callback, quit_closure())); |
| 469 | 462 |
| 470 RunThreads(); | 463 RunThreads(); |
| 471 | 464 |
| 472 update_client->RemoveObserver(&observer); | 465 update_client->RemoveObserver(&observer); |
| 473 | |
| 474 StopWorkerPool(); | |
| 475 } | 466 } |
| 476 | 467 |
| 477 // Tests the update check for two CRXs scenario. Both CRXs have updates. | 468 // Tests the update check for two CRXs scenario. Both CRXs have updates. |
| 478 TEST_F(UpdateClientTest, TwoCrxUpdate) { | 469 TEST_F(UpdateClientTest, TwoCrxUpdate) { |
| 479 class DataCallbackFake { | 470 class DataCallbackFake { |
| 480 public: | 471 public: |
| 481 static void Callback(const std::vector<std::string>& ids, | 472 static void Callback(const std::vector<std::string>& ids, |
| 482 std::vector<CrxComponent>* components) { | 473 std::vector<CrxComponent>* components) { |
| 483 CrxComponent crx1; | 474 CrxComponent crx1; |
| 484 crx1.name = "test_jebg"; | 475 crx1.name = "test_jebg"; |
| (...skipping 216 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 701 ids.push_back(std::string("jebgalgnebhfojomionfpkfelancnnkf")); | 692 ids.push_back(std::string("jebgalgnebhfojomionfpkfelancnnkf")); |
| 702 ids.push_back(std::string("ihfokbkgjpifnbbojhneepfflplebdkc")); | 693 ids.push_back(std::string("ihfokbkgjpifnbbojhneepfflplebdkc")); |
| 703 | 694 |
| 704 update_client->Update( | 695 update_client->Update( |
| 705 ids, base::Bind(&DataCallbackFake::Callback), | 696 ids, base::Bind(&DataCallbackFake::Callback), |
| 706 base::Bind(&CompletionCallbackFake::Callback, quit_closure())); | 697 base::Bind(&CompletionCallbackFake::Callback, quit_closure())); |
| 707 | 698 |
| 708 RunThreads(); | 699 RunThreads(); |
| 709 | 700 |
| 710 update_client->RemoveObserver(&observer); | 701 update_client->RemoveObserver(&observer); |
| 711 | |
| 712 StopWorkerPool(); | |
| 713 } | 702 } |
| 714 | 703 |
| 715 // Tests the scenario where there is a download timeout for the first | 704 // Tests the scenario where there is a download timeout for the first |
| 716 // CRX. The update for the first CRX fails. The update client waits before | 705 // CRX. The update for the first CRX fails. The update client waits before |
| 717 // attempting the update for the second CRX. This update succeeds. | 706 // attempting the update for the second CRX. This update succeeds. |
| 718 TEST_F(UpdateClientTest, TwoCrxUpdateDownloadTimeout) { | 707 TEST_F(UpdateClientTest, TwoCrxUpdateDownloadTimeout) { |
| 719 class DataCallbackFake { | 708 class DataCallbackFake { |
| 720 public: | 709 public: |
| 721 static void Callback(const std::vector<std::string>& ids, | 710 static void Callback(const std::vector<std::string>& ids, |
| 722 std::vector<CrxComponent>* components) { | 711 std::vector<CrxComponent>* components) { |
| (...skipping 216 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 939 ids.push_back(std::string("jebgalgnebhfojomionfpkfelancnnkf")); | 928 ids.push_back(std::string("jebgalgnebhfojomionfpkfelancnnkf")); |
| 940 ids.push_back(std::string("ihfokbkgjpifnbbojhneepfflplebdkc")); | 929 ids.push_back(std::string("ihfokbkgjpifnbbojhneepfflplebdkc")); |
| 941 | 930 |
| 942 update_client->Update( | 931 update_client->Update( |
| 943 ids, base::Bind(&DataCallbackFake::Callback), | 932 ids, base::Bind(&DataCallbackFake::Callback), |
| 944 base::Bind(&CompletionCallbackFake::Callback, quit_closure())); | 933 base::Bind(&CompletionCallbackFake::Callback, quit_closure())); |
| 945 | 934 |
| 946 RunThreads(); | 935 RunThreads(); |
| 947 | 936 |
| 948 update_client->RemoveObserver(&observer); | 937 update_client->RemoveObserver(&observer); |
| 949 | |
| 950 StopWorkerPool(); | |
| 951 } | 938 } |
| 952 | 939 |
| 953 // Tests the differential update scenario for one CRX. | 940 // Tests the differential update scenario for one CRX. |
| 954 TEST_F(UpdateClientTest, OneCrxDiffUpdate) { | 941 TEST_F(UpdateClientTest, OneCrxDiffUpdate) { |
| 955 class DataCallbackFake { | 942 class DataCallbackFake { |
| 956 public: | 943 public: |
| 957 static void Callback(const std::vector<std::string>& ids, | 944 static void Callback(const std::vector<std::string>& ids, |
| 958 std::vector<CrxComponent>* components) { | 945 std::vector<CrxComponent>* components) { |
| 959 static int num_calls = 0; | 946 static int num_calls = 0; |
| 960 | 947 |
| (...skipping 244 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1205 | 1192 |
| 1206 { | 1193 { |
| 1207 base::RunLoop runloop; | 1194 base::RunLoop runloop; |
| 1208 update_client->Update( | 1195 update_client->Update( |
| 1209 ids, base::Bind(&DataCallbackFake::Callback), | 1196 ids, base::Bind(&DataCallbackFake::Callback), |
| 1210 base::Bind(&CompletionCallbackFake::Callback, runloop.QuitClosure())); | 1197 base::Bind(&CompletionCallbackFake::Callback, runloop.QuitClosure())); |
| 1211 runloop.Run(); | 1198 runloop.Run(); |
| 1212 } | 1199 } |
| 1213 | 1200 |
| 1214 update_client->RemoveObserver(&observer); | 1201 update_client->RemoveObserver(&observer); |
| 1215 | |
| 1216 StopWorkerPool(); | |
| 1217 } | 1202 } |
| 1218 | 1203 |
| 1219 // Tests the update scenario for one CRX where the CRX installer returns | 1204 // Tests the update scenario for one CRX where the CRX installer returns |
| 1220 // an error. | 1205 // an error. |
| 1221 TEST_F(UpdateClientTest, OneCrxInstallError) { | 1206 TEST_F(UpdateClientTest, OneCrxInstallError) { |
| 1222 class MockInstaller : public CrxInstaller { | 1207 class MockInstaller : public CrxInstaller { |
| 1223 public: | 1208 public: |
| 1224 MOCK_METHOD1(OnUpdateError, void(int error)); | 1209 MOCK_METHOD1(OnUpdateError, void(int error)); |
| 1225 MOCK_METHOD2(Install, | 1210 MOCK_METHOD2(Install, |
| 1226 bool(const base::DictionaryValue& manifest, | 1211 bool(const base::DictionaryValue& manifest, |
| (...skipping 172 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1399 std::vector<std::string> ids; | 1384 std::vector<std::string> ids; |
| 1400 ids.push_back(std::string("jebgalgnebhfojomionfpkfelancnnkf")); | 1385 ids.push_back(std::string("jebgalgnebhfojomionfpkfelancnnkf")); |
| 1401 | 1386 |
| 1402 update_client->Update( | 1387 update_client->Update( |
| 1403 ids, base::Bind(&DataCallbackFake::Callback), | 1388 ids, base::Bind(&DataCallbackFake::Callback), |
| 1404 base::Bind(&CompletionCallbackFake::Callback, quit_closure())); | 1389 base::Bind(&CompletionCallbackFake::Callback, quit_closure())); |
| 1405 | 1390 |
| 1406 RunThreads(); | 1391 RunThreads(); |
| 1407 | 1392 |
| 1408 update_client->RemoveObserver(&observer); | 1393 update_client->RemoveObserver(&observer); |
| 1409 | |
| 1410 StopWorkerPool(); | |
| 1411 } | 1394 } |
| 1412 | 1395 |
| 1413 // Tests the fallback from differential to full update scenario for one CRX. | 1396 // Tests the fallback from differential to full update scenario for one CRX. |
| 1414 TEST_F(UpdateClientTest, OneCrxDiffUpdateFailsFullUpdateSucceeds) { | 1397 TEST_F(UpdateClientTest, OneCrxDiffUpdateFailsFullUpdateSucceeds) { |
| 1415 class DataCallbackFake { | 1398 class DataCallbackFake { |
| 1416 public: | 1399 public: |
| 1417 static void Callback(const std::vector<std::string>& ids, | 1400 static void Callback(const std::vector<std::string>& ids, |
| 1418 std::vector<CrxComponent>* components) { | 1401 std::vector<CrxComponent>* components) { |
| 1419 static int num_calls = 0; | 1402 static int num_calls = 0; |
| 1420 | 1403 |
| (...skipping 263 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1684 | 1667 |
| 1685 { | 1668 { |
| 1686 base::RunLoop runloop; | 1669 base::RunLoop runloop; |
| 1687 update_client->Update( | 1670 update_client->Update( |
| 1688 ids, base::Bind(&DataCallbackFake::Callback), | 1671 ids, base::Bind(&DataCallbackFake::Callback), |
| 1689 base::Bind(&CompletionCallbackFake::Callback, runloop.QuitClosure())); | 1672 base::Bind(&CompletionCallbackFake::Callback, runloop.QuitClosure())); |
| 1690 runloop.Run(); | 1673 runloop.Run(); |
| 1691 } | 1674 } |
| 1692 | 1675 |
| 1693 update_client->RemoveObserver(&observer); | 1676 update_client->RemoveObserver(&observer); |
| 1694 | |
| 1695 StopWorkerPool(); | |
| 1696 } | 1677 } |
| 1697 | 1678 |
| 1698 // Tests the queuing of update checks. In this scenario, two update checks are | 1679 // Tests the queuing of update checks. In this scenario, two update checks are |
| 1699 // done for one CRX. The second update check call is queued up and will run | 1680 // done for one CRX. The second update check call is queued up and will run |
| 1700 // after the first check has completed. The CRX has no updates. | 1681 // after the first check has completed. The CRX has no updates. |
| 1701 TEST_F(UpdateClientTest, OneCrxNoUpdateQueuedCall) { | 1682 TEST_F(UpdateClientTest, OneCrxNoUpdateQueuedCall) { |
| 1702 class DataCallbackFake { | 1683 class DataCallbackFake { |
| 1703 public: | 1684 public: |
| 1704 static void Callback(const std::vector<std::string>& ids, | 1685 static void Callback(const std::vector<std::string>& ids, |
| 1705 std::vector<CrxComponent>* components) { | 1686 std::vector<CrxComponent>* components) { |
| (...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1790 update_client->Update( | 1771 update_client->Update( |
| 1791 ids, base::Bind(&DataCallbackFake::Callback), | 1772 ids, base::Bind(&DataCallbackFake::Callback), |
| 1792 base::Bind(&CompletionCallbackFake::Callback, quit_closure())); | 1773 base::Bind(&CompletionCallbackFake::Callback, quit_closure())); |
| 1793 update_client->Update( | 1774 update_client->Update( |
| 1794 ids, base::Bind(&DataCallbackFake::Callback), | 1775 ids, base::Bind(&DataCallbackFake::Callback), |
| 1795 base::Bind(&CompletionCallbackFake::Callback, quit_closure())); | 1776 base::Bind(&CompletionCallbackFake::Callback, quit_closure())); |
| 1796 | 1777 |
| 1797 RunThreads(); | 1778 RunThreads(); |
| 1798 | 1779 |
| 1799 update_client->RemoveObserver(&observer); | 1780 update_client->RemoveObserver(&observer); |
| 1800 | |
| 1801 StopWorkerPool(); | |
| 1802 } | 1781 } |
| 1803 | 1782 |
| 1804 // Tests the install of one CRX. | 1783 // Tests the install of one CRX. |
| 1805 TEST_F(UpdateClientTest, OneCrxInstall) { | 1784 TEST_F(UpdateClientTest, OneCrxInstall) { |
| 1806 class DataCallbackFake { | 1785 class DataCallbackFake { |
| 1807 public: | 1786 public: |
| 1808 static void Callback(const std::vector<std::string>& ids, | 1787 static void Callback(const std::vector<std::string>& ids, |
| 1809 std::vector<CrxComponent>* components) { | 1788 std::vector<CrxComponent>* components) { |
| 1810 CrxComponent crx; | 1789 CrxComponent crx; |
| 1811 crx.name = "test_jebg"; | 1790 crx.name = "test_jebg"; |
| (...skipping 151 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1963 update_client->AddObserver(&observer); | 1942 update_client->AddObserver(&observer); |
| 1964 | 1943 |
| 1965 update_client->Install( | 1944 update_client->Install( |
| 1966 std::string("jebgalgnebhfojomionfpkfelancnnkf"), | 1945 std::string("jebgalgnebhfojomionfpkfelancnnkf"), |
| 1967 base::Bind(&DataCallbackFake::Callback), | 1946 base::Bind(&DataCallbackFake::Callback), |
| 1968 base::Bind(&CompletionCallbackFake::Callback, quit_closure())); | 1947 base::Bind(&CompletionCallbackFake::Callback, quit_closure())); |
| 1969 | 1948 |
| 1970 RunThreads(); | 1949 RunThreads(); |
| 1971 | 1950 |
| 1972 update_client->RemoveObserver(&observer); | 1951 update_client->RemoveObserver(&observer); |
| 1973 | |
| 1974 StopWorkerPool(); | |
| 1975 } | 1952 } |
| 1976 | 1953 |
| 1977 // Tests that overlapping installs of the same CRX result in an error. | 1954 // Tests that overlapping installs of the same CRX result in an error. |
| 1978 TEST_F(UpdateClientTest, ConcurrentInstallSameCRX) { | 1955 TEST_F(UpdateClientTest, ConcurrentInstallSameCRX) { |
| 1979 class DataCallbackFake { | 1956 class DataCallbackFake { |
| 1980 public: | 1957 public: |
| 1981 static void Callback(const std::vector<std::string>& ids, | 1958 static void Callback(const std::vector<std::string>& ids, |
| 1982 std::vector<CrxComponent>* components) { | 1959 std::vector<CrxComponent>* components) { |
| 1983 CrxComponent crx; | 1960 CrxComponent crx; |
| 1984 crx.name = "test_jebg"; | 1961 crx.name = "test_jebg"; |
| (...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2077 base::Bind(&CompletionCallbackFake::Callback, quit_closure())); | 2054 base::Bind(&CompletionCallbackFake::Callback, quit_closure())); |
| 2078 | 2055 |
| 2079 update_client->Install( | 2056 update_client->Install( |
| 2080 std::string("jebgalgnebhfojomionfpkfelancnnkf"), | 2057 std::string("jebgalgnebhfojomionfpkfelancnnkf"), |
| 2081 base::Bind(&DataCallbackFake::Callback), | 2058 base::Bind(&DataCallbackFake::Callback), |
| 2082 base::Bind(&CompletionCallbackFake::Callback, quit_closure())); | 2059 base::Bind(&CompletionCallbackFake::Callback, quit_closure())); |
| 2083 | 2060 |
| 2084 RunThreads(); | 2061 RunThreads(); |
| 2085 | 2062 |
| 2086 update_client->RemoveObserver(&observer); | 2063 update_client->RemoveObserver(&observer); |
| 2087 | |
| 2088 StopWorkerPool(); | |
| 2089 } | 2064 } |
| 2090 | 2065 |
| 2091 // Make sure that we don't get any crashes when trying to update an empty list | 2066 // Make sure that we don't get any crashes when trying to update an empty list |
| 2092 // of ids. | 2067 // of ids. |
| 2093 TEST_F(UpdateClientTest, EmptyIdList) { | 2068 TEST_F(UpdateClientTest, EmptyIdList) { |
| 2094 class DataCallbackFake { | 2069 class DataCallbackFake { |
| 2095 public: | 2070 public: |
| 2096 static void Callback(const std::vector<std::string>& ids, | 2071 static void Callback(const std::vector<std::string>& ids, |
| 2097 std::vector<CrxComponent>* components) {} | 2072 std::vector<CrxComponent>* components) {} |
| 2098 }; | 2073 }; |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2137 scoped_refptr<UpdateClient> update_client(new UpdateClientImpl( | 2112 scoped_refptr<UpdateClient> update_client(new UpdateClientImpl( |
| 2138 config(), make_scoped_ptr(new FakePingManagerImpl(*config())), | 2113 config(), make_scoped_ptr(new FakePingManagerImpl(*config())), |
| 2139 &FakeUpdateChecker::Create, &FakeCrxDownloader::Create)); | 2114 &FakeUpdateChecker::Create, &FakeCrxDownloader::Create)); |
| 2140 | 2115 |
| 2141 std::vector<std::string> empty_id_list; | 2116 std::vector<std::string> empty_id_list; |
| 2142 base::RunLoop runloop; | 2117 base::RunLoop runloop; |
| 2143 update_client->Update( | 2118 update_client->Update( |
| 2144 empty_id_list, base::Bind(&DataCallbackFake::Callback), | 2119 empty_id_list, base::Bind(&DataCallbackFake::Callback), |
| 2145 base::Bind(&CompletionCallbackFake::Callback, runloop.QuitClosure())); | 2120 base::Bind(&CompletionCallbackFake::Callback, runloop.QuitClosure())); |
| 2146 runloop.Run(); | 2121 runloop.Run(); |
| 2147 | |
| 2148 StopWorkerPool(); | |
| 2149 } | 2122 } |
| 2150 | 2123 |
| 2151 } // namespace update_client | 2124 } // namespace update_client |
| OLD | NEW |