Home | History | Annotate | Download | only in webstore
      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 
      5 #include <string>
      6 
      7 #include "base/basictypes.h"
      8 #include "base/bind.h"
      9 #include "base/command_line.h"
     10 #include "base/memory/scoped_ptr.h"
     11 #include "base/run_loop.h"
     12 #include "base/strings/utf_string_conversions.h"
     13 #include "chrome/browser/profiles/profile_manager.h"
     14 #include "chrome/browser/ui/app_list/search/chrome_search_result.h"
     15 #include "chrome/browser/ui/app_list/search/webstore/webstore_provider.h"
     16 #include "chrome/common/chrome_switches.h"
     17 #include "chrome/test/base/in_process_browser_test.h"
     18 #include "content/public/browser/browser_thread.h"
     19 #include "net/test/embedded_test_server/embedded_test_server.h"
     20 #include "net/test/embedded_test_server/http_request.h"
     21 #include "net/test/embedded_test_server/http_response.h"
     22 
     23 using content::BrowserThread;
     24 using net::test_server::BasicHttpResponse;
     25 using net::test_server::HttpRequest;
     26 using net::test_server::HttpResponse;
     27 using net::test_server::EmbeddedTestServer;
     28 
     29 namespace app_list {
     30 namespace test {
     31 namespace {
     32 
     33 // Mock results.
     34 const char kOneResult[] = "{"
     35     "\"search_url\": \"http://host/search\","
     36     "\"results\":["
     37       "{"
     38         "\"id\": \"app1_id\","
     39         "\"localized_name\": \"app1 name\","
     40         "\"icon_url\": \"http://host/icon\""
     41       "}"
     42     "]}";
     43 
     44 const char kThreeResults[] = "{"
     45     "\"search_url\": \"http://host/search\","
     46     "\"results\":["
     47       "{"
     48         "\"id\": \"app1_id\","
     49         "\"localized_name\": \"one\","
     50         "\"icon_url\": \"http://host/icon\""
     51       "},"
     52       "{"
     53         "\"id\": \"app2_id\","
     54         "\"localized_name\": \"two\","
     55         "\"icon_url\": \"http://host/icon\""
     56       "},"
     57       "{"
     58         "\"id\": \"app3_id\","
     59         "\"localized_name\": \"three\","
     60         "\"icon_url\": \"http://host/icon\""
     61       "}"
     62     "]}";
     63 
     64 }  // namespace
     65 
     66 class WebstoreProviderTest : public InProcessBrowserTest {
     67  public:
     68   WebstoreProviderTest() {}
     69   virtual ~WebstoreProviderTest() {}
     70 
     71   // InProcessBrowserTest overrides:
     72   virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
     73     command_line->AppendSwitchASCII(switches::kForceFieldTrials,
     74                                     "LauncherUseWebstoreSearch/Enable/");
     75   }
     76 
     77   virtual void SetUpOnMainThread() OVERRIDE {
     78     test_server_.reset(new EmbeddedTestServer);
     79 
     80     ASSERT_TRUE(test_server_->InitializeAndWaitUntilReady());
     81     test_server_->RegisterRequestHandler(
     82         base::Bind(&WebstoreProviderTest::HandleRequest,
     83                    base::Unretained(this)));
     84     CommandLine::ForCurrentProcess()->AppendSwitchASCII(
     85         switches::kAppsGalleryURL, test_server_->base_url().spec());
     86 
     87     webstore_provider_.reset(new WebstoreProvider(
     88         ProfileManager::GetActiveUserProfile(), NULL));
     89     webstore_provider_->set_webstore_search_fetched_callback(
     90         base::Bind(&WebstoreProviderTest::OnSearchResultsFetched,
     91                    base::Unretained(this)));
     92     // TODO(mukai): add test cases for throttling.
     93     webstore_provider_->set_use_throttling(false);
     94   }
     95 
     96   virtual void CleanUpOnMainThread() OVERRIDE {
     97     EXPECT_TRUE(test_server_->ShutdownAndWaitUntilComplete());
     98     test_server_.reset();
     99   }
    100 
    101   std::string RunQuery(const std::string& query,
    102                        const std::string& mock_server_response) {
    103     webstore_provider_->Start(base::UTF8ToUTF16(query));
    104 
    105     if (webstore_provider_->webstore_search_ && !mock_server_response.empty()) {
    106       mock_server_response_ = mock_server_response;
    107 
    108       DCHECK(!run_loop_);
    109       run_loop_.reset(new base::RunLoop);
    110       run_loop_->Run();
    111       run_loop_.reset();
    112 
    113       mock_server_response_.clear();
    114     }
    115 
    116     webstore_provider_->Stop();
    117     return GetResults();
    118   }
    119 
    120   std::string GetResults() const {
    121     std::string results;
    122     for (SearchProvider::Results::const_iterator it =
    123              webstore_provider_->results().begin();
    124          it != webstore_provider_->results().end();
    125          ++it) {
    126       if (!results.empty())
    127         results += ',';
    128       results += base::UTF16ToUTF8((*it)->title());
    129     }
    130     return results;
    131   }
    132 
    133   WebstoreProvider* webstore_provider() { return webstore_provider_.get(); }
    134 
    135  private:
    136   scoped_ptr<HttpResponse> HandleRequest(const HttpRequest& request) {
    137     scoped_ptr<BasicHttpResponse> response(new BasicHttpResponse);
    138 
    139     if (request.relative_url.find("/jsonsearch?") != std::string::npos) {
    140       if (mock_server_response_ == "404") {
    141         response->set_code(net::HTTP_NOT_FOUND);
    142       } else if (mock_server_response_ == "500") {
    143         response->set_code(net::HTTP_INTERNAL_SERVER_ERROR);
    144       } else {
    145         response->set_code(net::HTTP_OK);
    146         response->set_content(mock_server_response_);
    147       }
    148     }
    149 
    150     return response.PassAs<HttpResponse>();
    151   }
    152 
    153   void OnSearchResultsFetched() {
    154     if (run_loop_)
    155       run_loop_->Quit();
    156   }
    157 
    158   scoped_ptr<EmbeddedTestServer> test_server_;
    159   scoped_ptr<base::RunLoop> run_loop_;
    160 
    161   std::string mock_server_response_;
    162 
    163   scoped_ptr<WebstoreProvider> webstore_provider_;
    164 
    165   DISALLOW_COPY_AND_ASSIGN(WebstoreProviderTest);
    166 };
    167 
    168 // Flaky on CrOS and Windows: http://crbug.com/246136.
    169 // TODO(erg): linux_aura bringup: http://crbug.com/163931
    170 #if defined(OS_WIN) || defined(OS_LINUX)
    171 #define MAYBE_Basic DISABLED_Basic
    172 #else
    173 #define MAYBE_Basic Basic
    174 #endif
    175 IN_PROC_BROWSER_TEST_F(WebstoreProviderTest, MAYBE_Basic) {
    176   struct {
    177     const char* query;
    178     const char* mock_server_response;
    179     const char* expected_results_content;
    180   } kTestCases[] = {
    181     // "Search in web store" result with query text itself is used for
    182     // synchronous placeholder, bad server response etc.
    183     {"synchronous", "", "synchronous" },
    184     {"404", "404", "404" },
    185     {"500", "500", "500" },
    186     {"bad json", "invalid json", "bad json" },
    187     // Good results.
    188     {"1 result", kOneResult, "app1 name" },
    189     {"3 result", kThreeResults, "one,two,three" },
    190   };
    191 
    192   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTestCases); ++i) {
    193     EXPECT_EQ(kTestCases[i].expected_results_content,
    194               RunQuery(kTestCases[i].query,
    195                        kTestCases[i].mock_server_response))
    196         << "Case " << i << ": q=" << kTestCases[i].query;
    197   }
    198 }
    199 
    200 IN_PROC_BROWSER_TEST_F(WebstoreProviderTest, NoSearchForSensitiveData) {
    201   // None of the following input strings should be accepted because they may
    202   // contain private data.
    203   const char* inputs[] = {
    204     // file: scheme is bad.
    205     "file://filename",
    206     "FILE://filename",
    207     // URLs with usernames, ports, queries or refs are bad.
    208     "http://username:password@hostname/",
    209     "http://www.example.com:1000",
    210     "http://foo:1000",
    211     "http://hostname/?query=q",
    212     "http://hostname/path#ref",
    213     // A https URL with path is bad.
    214     "https://hostname/path",
    215   };
    216 
    217   for (size_t i = 0; i < arraysize(inputs); ++i)
    218     EXPECT_EQ("", RunQuery(inputs[i], kOneResult));
    219 }
    220 
    221 IN_PROC_BROWSER_TEST_F(WebstoreProviderTest, NoSearchForShortQueries) {
    222   EXPECT_EQ("", RunQuery("a", kOneResult));
    223   EXPECT_EQ("", RunQuery("ab", kOneResult));
    224   EXPECT_EQ("app1 name", RunQuery("abc", kOneResult));
    225 }
    226 
    227 // Flaky on CrOS and Windows: http://crbug.com/246136.
    228 #if defined(OS_WIN) || defined(OS_CHROMEOS)
    229 #define MAYBE_SearchCache DISABLED_SearchCache
    230 #else
    231 #define MAYBE_SearchCache SearchCache
    232 #endif
    233 IN_PROC_BROWSER_TEST_F(WebstoreProviderTest, MAYBE_SearchCache) {
    234   EXPECT_EQ("app1 name", RunQuery("foo", kOneResult));
    235 
    236   // No result is provided but the provider gets the result from the cache.
    237   EXPECT_EQ("app1 name", RunQuery("foo", ""));
    238 }
    239 
    240 }  // namespace test
    241 }  // namespace app_list
    242