1// Copyright (c) 2012 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 "chrome/test/ppapi/ppapi_test.h"
6
7#include "base/command_line.h"
8#include "base/file_util.h"
9#include "base/logging.h"
10#include "base/path_service.h"
11#include "base/strings/string_util.h"
12#include "base/strings/stringprintf.h"
13#include "base/test/test_timeouts.h"
14#include "build/build_config.h"
15#include "chrome/browser/chrome_notification_types.h"
16#include "chrome/browser/content_settings/host_content_settings_map.h"
17#include "chrome/browser/infobars/confirm_infobar_delegate.h"
18#include "chrome/browser/infobars/infobar.h"
19#include "chrome/browser/profiles/profile.h"
20#include "chrome/browser/ui/browser.h"
21#include "chrome/browser/ui/browser_tabstrip.h"
22#include "chrome/browser/ui/tabs/tab_strip_model.h"
23#include "chrome/common/chrome_paths.h"
24#include "chrome/common/chrome_switches.h"
25#include "chrome/test/base/ui_test_utils.h"
26#include "content/public/browser/dom_operation_notification_details.h"
27#include "content/public/browser/notification_service.h"
28#include "content/public/browser/notification_types.h"
29#include "content/public/browser/web_contents.h"
30#include "content/public/common/content_paths.h"
31#include "content/public/common/content_switches.h"
32#include "content/public/test/browser_test_utils.h"
33#include "content/public/test/test_renderer_host.h"
34#include "net/base/net_util.h"
35#include "net/base/test_data_directory.h"
36#include "ppapi/shared_impl/ppapi_switches.h"
37#include "ui/gl/gl_switches.h"
38
39#if defined(OS_WIN) && defined(USE_ASH)
40#include "base/win/windows_version.h"
41#endif
42
43using content::DomOperationNotificationDetails;
44using content::RenderViewHost;
45
46namespace {
47
48// Platform-specific filename relative to the chrome executable.
49#if defined(OS_WIN)
50const wchar_t library_name[] = L"ppapi_tests.dll";
51#elif defined(OS_MACOSX)
52const char library_name[] = "ppapi_tests.plugin";
53#elif defined(OS_POSIX)
54const char library_name[] = "libppapi_tests.so";
55#endif
56
57}  // namespace
58
59PPAPITestMessageHandler::PPAPITestMessageHandler() {
60}
61
62TestMessageHandler::MessageResponse PPAPITestMessageHandler::HandleMessage(
63    const std::string& json) {
64 std::string trimmed;
65 TrimString(json, "\"", &trimmed);
66 if (trimmed == "...") {
67   return CONTINUE;
68 } else {
69   message_ = trimmed;
70   return DONE;
71 }
72}
73
74void PPAPITestMessageHandler::Reset() {
75  TestMessageHandler::Reset();
76  message_.clear();
77}
78
79PPAPITestBase::InfoBarObserver::InfoBarObserver() {
80  registrar_.Add(this, chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_ADDED,
81                 content::NotificationService::AllSources());
82}
83
84PPAPITestBase::InfoBarObserver::~InfoBarObserver() {
85  EXPECT_EQ(0u, expected_infobars_.size()) << "Missing an expected infobar";
86}
87
88void PPAPITestBase::InfoBarObserver::Observe(
89    int type,
90    const content::NotificationSource& source,
91    const content::NotificationDetails& details) {
92  ASSERT_EQ(chrome::NOTIFICATION_TAB_CONTENTS_INFOBAR_ADDED, type);
93  InfoBarDelegate* infobar =
94      content::Details<InfoBarAddedDetails>(details).ptr();
95  ConfirmInfoBarDelegate* confirm_infobar_delegate =
96      infobar->AsConfirmInfoBarDelegate();
97  ASSERT_TRUE(confirm_infobar_delegate);
98
99  ASSERT_FALSE(expected_infobars_.empty()) << "Unexpected infobar";
100  if (expected_infobars_.front())
101    confirm_infobar_delegate->Accept();
102  else
103    confirm_infobar_delegate->Cancel();
104  expected_infobars_.pop_front();
105
106  // TODO(bauerb): We should close the infobar.
107}
108
109void PPAPITestBase::InfoBarObserver::ExpectInfoBarAndAccept(
110    bool should_accept) {
111  expected_infobars_.push_back(should_accept);
112}
113
114PPAPITestBase::PPAPITestBase() {
115}
116
117void PPAPITestBase::SetUpCommandLine(CommandLine* command_line) {
118  // The test sends us the result via a cookie.
119  command_line->AppendSwitch(switches::kEnableFileCookies);
120
121  // Some stuff is hung off of the testing interface which is not enabled
122  // by default.
123  command_line->AppendSwitch(switches::kEnablePepperTesting);
124
125  // Smooth scrolling confuses the scrollbar test.
126  command_line->AppendSwitch(switches::kDisableSmoothScrolling);
127
128  // For TestRequestOSFileHandle.
129  command_line->AppendSwitch(switches::kUnlimitedStorage);
130  command_line->AppendSwitchASCII(switches::kAllowNaClFileHandleAPI,
131                                  "127.0.0.1");
132}
133
134void PPAPITestBase::SetUpOnMainThread() {
135  // Always allow access to the PPAPI broker.
136  browser()->profile()->GetHostContentSettingsMap()->SetDefaultContentSetting(
137      CONTENT_SETTINGS_TYPE_PPAPI_BROKER, CONTENT_SETTING_ALLOW);
138}
139
140GURL PPAPITestBase::GetTestFileUrl(const std::string& test_case) {
141  base::FilePath test_path;
142  EXPECT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &test_path));
143  test_path = test_path.Append(FILE_PATH_LITERAL("ppapi"));
144  test_path = test_path.Append(FILE_PATH_LITERAL("tests"));
145  test_path = test_path.Append(FILE_PATH_LITERAL("test_case.html"));
146
147  // Sanity check the file name.
148  EXPECT_TRUE(base::PathExists(test_path));
149
150  GURL test_url = net::FilePathToFileURL(test_path);
151
152  GURL::Replacements replacements;
153  std::string query = BuildQuery(std::string(), test_case);
154  replacements.SetQuery(query.c_str(), url_parse::Component(0, query.size()));
155  return test_url.ReplaceComponents(replacements);
156}
157
158void PPAPITestBase::RunTest(const std::string& test_case) {
159  GURL url = GetTestFileUrl(test_case);
160  RunTestURL(url);
161}
162
163void PPAPITestBase::RunTestAndReload(const std::string& test_case) {
164  GURL url = GetTestFileUrl(test_case);
165  RunTestURL(url);
166  // If that passed, we simply run the test again, which navigates again.
167  RunTestURL(url);
168}
169
170void PPAPITestBase::RunTestViaHTTP(const std::string& test_case) {
171  base::FilePath document_root;
172  ASSERT_TRUE(ui_test_utils::GetRelativeBuildDirectory(&document_root));
173  base::FilePath http_document_root;
174  ASSERT_TRUE(ui_test_utils::GetRelativeBuildDirectory(&http_document_root));
175  net::SpawnedTestServer http_server(net::SpawnedTestServer::TYPE_HTTP,
176                                     net::SpawnedTestServer::kLocalhost,
177                                     document_root);
178  ASSERT_TRUE(http_server.Start());
179  RunTestURL(GetTestURL(http_server, test_case, std::string()));
180}
181
182void PPAPITestBase::RunTestWithSSLServer(const std::string& test_case) {
183  base::FilePath http_document_root;
184  ASSERT_TRUE(ui_test_utils::GetRelativeBuildDirectory(&http_document_root));
185  net::SpawnedTestServer http_server(net::SpawnedTestServer::TYPE_HTTP,
186                                     net::SpawnedTestServer::kLocalhost,
187                                     http_document_root);
188  net::SpawnedTestServer ssl_server(net::SpawnedTestServer::TYPE_HTTPS,
189                                    net::BaseTestServer::SSLOptions(),
190                                    http_document_root);
191  // Start the servers in parallel.
192  ASSERT_TRUE(http_server.StartInBackground());
193  ASSERT_TRUE(ssl_server.StartInBackground());
194  // Wait until they are both finished before continuing.
195  ASSERT_TRUE(http_server.BlockUntilStarted());
196  ASSERT_TRUE(ssl_server.BlockUntilStarted());
197
198  uint16_t port = ssl_server.host_port_pair().port();
199  RunTestURL(GetTestURL(http_server,
200                        test_case,
201                        base::StringPrintf("ssl_server_port=%d", port)));
202}
203
204void PPAPITestBase::RunTestWithWebSocketServer(const std::string& test_case) {
205  base::FilePath http_document_root;
206  ASSERT_TRUE(ui_test_utils::GetRelativeBuildDirectory(&http_document_root));
207  net::SpawnedTestServer http_server(net::SpawnedTestServer::TYPE_HTTP,
208                                     net::SpawnedTestServer::kLocalhost,
209                                     http_document_root);
210  net::SpawnedTestServer ws_server(net::SpawnedTestServer::TYPE_WS,
211                                   net::SpawnedTestServer::kLocalhost,
212                                   net::GetWebSocketTestDataDirectory());
213  // Start the servers in parallel.
214  ASSERT_TRUE(http_server.StartInBackground());
215  ASSERT_TRUE(ws_server.StartInBackground());
216  // Wait until they are both finished before continuing.
217  ASSERT_TRUE(http_server.BlockUntilStarted());
218  ASSERT_TRUE(ws_server.BlockUntilStarted());
219
220  std::string host = ws_server.host_port_pair().HostForURL();
221  uint16_t port = ws_server.host_port_pair().port();
222  RunTestURL(GetTestURL(http_server,
223                        test_case,
224                        base::StringPrintf(
225                            "websocket_host=%s&websocket_port=%d",
226                            host.c_str(),
227                            port)));
228}
229
230void PPAPITestBase::RunTestIfAudioOutputAvailable(
231    const std::string& test_case) {
232  RunTest(test_case);
233}
234
235void PPAPITestBase::RunTestViaHTTPIfAudioOutputAvailable(
236    const std::string& test_case) {
237  RunTestViaHTTP(test_case);
238}
239
240std::string PPAPITestBase::StripPrefixes(const std::string& test_name) {
241  const char* const prefixes[] = {
242      "FAILS_", "FLAKY_", "DISABLED_", "SLOW_" };
243  for (size_t i = 0; i < sizeof(prefixes)/sizeof(prefixes[0]); ++i)
244    if (test_name.find(prefixes[i]) == 0)
245      return test_name.substr(strlen(prefixes[i]));
246  return test_name;
247}
248
249void PPAPITestBase::RunTestURL(const GURL& test_url) {
250#if defined(OS_WIN) && defined(USE_ASH)
251  // PPAPITests are broken in Ash browser tests (http://crbug.com/263548).
252  if (base::win::GetVersion() >= base::win::VERSION_WIN8) {
253    LOG(WARNING) << "PPAPITests are disabled for Ash browser tests.";
254    return;
255  }
256#endif
257
258  // See comment above TestingInstance in ppapi/test/testing_instance.h.
259  // Basically it sends messages using the DOM automation controller. The
260  // value of "..." means it's still working and we should continue to wait,
261  // any other value indicates completion (in this case it will start with
262  // "PASS" or "FAIL"). This keeps us from timing out on waits for long tests.
263  PPAPITestMessageHandler handler;
264  JavascriptTestObserver observer(
265      browser()->tab_strip_model()->GetActiveWebContents()->GetRenderViewHost(),
266      &handler);
267
268  ui_test_utils::NavigateToURL(browser(), test_url);
269
270  ASSERT_TRUE(observer.Run()) << handler.error_message();
271  EXPECT_STREQ("PASS", handler.message().c_str());
272}
273
274GURL PPAPITestBase::GetTestURL(
275    const net::SpawnedTestServer& http_server,
276    const std::string& test_case,
277    const std::string& extra_params) {
278  std::string query = BuildQuery("files/test_case.html?", test_case);
279  if (!extra_params.empty())
280    query = base::StringPrintf("%s&%s", query.c_str(), extra_params.c_str());
281
282  return http_server.GetURL(query);
283}
284
285PPAPITest::PPAPITest() : in_process_(true) {
286}
287
288void PPAPITest::SetUpCommandLine(CommandLine* command_line) {
289  PPAPITestBase::SetUpCommandLine(command_line);
290
291  // Append the switch to register the pepper plugin.
292  // library name = <out dir>/<test_name>.<library_extension>
293  // MIME type = application/x-ppapi-<test_name>
294  base::FilePath plugin_dir;
295  EXPECT_TRUE(PathService::Get(base::DIR_MODULE, &plugin_dir));
296
297  base::FilePath plugin_lib = plugin_dir.Append(library_name);
298  EXPECT_TRUE(base::PathExists(plugin_lib));
299  base::FilePath::StringType pepper_plugin = plugin_lib.value();
300  pepper_plugin.append(FILE_PATH_LITERAL(";application/x-ppapi-tests"));
301  command_line->AppendSwitchNative(switches::kRegisterPepperPlugins,
302                                   pepper_plugin);
303  command_line->AppendSwitchASCII(switches::kAllowNaClSocketAPI, "127.0.0.1");
304
305  if (in_process_)
306    command_line->AppendSwitch(switches::kPpapiInProcess);
307}
308
309std::string PPAPITest::BuildQuery(const std::string& base,
310                                  const std::string& test_case){
311  return base::StringPrintf("%stestcase=%s", base.c_str(), test_case.c_str());
312}
313
314OutOfProcessPPAPITest::OutOfProcessPPAPITest() {
315  in_process_ = false;
316}
317
318void OutOfProcessPPAPITest::SetUpCommandLine(CommandLine* command_line) {
319  PPAPITest::SetUpCommandLine(command_line);
320  command_line->AppendSwitch(switches::kUseFakeDeviceForMediaStream);
321  command_line->AppendSwitch(switches::kUseFakeUIForMediaStream);
322}
323
324void PPAPINaClTest::SetUpCommandLine(CommandLine* command_line) {
325  PPAPITestBase::SetUpCommandLine(command_line);
326
327  base::FilePath plugin_lib;
328  EXPECT_TRUE(PathService::Get(chrome::FILE_NACL_PLUGIN, &plugin_lib));
329  EXPECT_TRUE(base::PathExists(plugin_lib));
330
331  // Enable running NaCl outside of the store.
332  command_line->AppendSwitch(switches::kEnableNaCl);
333  command_line->AppendSwitch(switches::kEnablePnacl);
334  command_line->AppendSwitchASCII(switches::kAllowNaClSocketAPI, "127.0.0.1");
335  command_line->AppendSwitch(switches::kUseFakeDeviceForMediaStream);
336  command_line->AppendSwitch(switches::kUseFakeUIForMediaStream);
337}
338
339// Append the correct mode and testcase string
340std::string PPAPINaClNewlibTest::BuildQuery(const std::string& base,
341                                            const std::string& test_case) {
342  return base::StringPrintf("%smode=nacl_newlib&testcase=%s", base.c_str(),
343                            test_case.c_str());
344}
345
346// Append the correct mode and testcase string
347std::string PPAPINaClGLibcTest::BuildQuery(const std::string& base,
348                                           const std::string& test_case) {
349  return base::StringPrintf("%smode=nacl_glibc&testcase=%s", base.c_str(),
350                            test_case.c_str());
351}
352
353// Append the correct mode and testcase string
354std::string PPAPINaClPNaClTest::BuildQuery(const std::string& base,
355                                           const std::string& test_case) {
356  return base::StringPrintf("%smode=nacl_pnacl&testcase=%s", base.c_str(),
357                            test_case.c_str());
358}
359
360void PPAPINaClTestDisallowedSockets::SetUpCommandLine(
361    CommandLine* command_line) {
362  PPAPITestBase::SetUpCommandLine(command_line);
363
364  base::FilePath plugin_lib;
365  EXPECT_TRUE(PathService::Get(chrome::FILE_NACL_PLUGIN, &plugin_lib));
366  EXPECT_TRUE(base::PathExists(plugin_lib));
367
368  // Enable running NaCl outside of the store.
369  command_line->AppendSwitch(switches::kEnableNaCl);
370  command_line->AppendSwitch(switches::kEnablePnacl);
371}
372
373// Append the correct mode and testcase string
374std::string PPAPINaClTestDisallowedSockets::BuildQuery(
375    const std::string& base,
376    const std::string& test_case) {
377  return base::StringPrintf("%smode=nacl_newlib&testcase=%s", base.c_str(),
378                            test_case.c_str());
379}
380
381void PPAPIBrokerInfoBarTest::SetUpOnMainThread() {
382  // The default content setting for the PPAPI broker is ASK. We purposefully
383  // don't call PPAPITestBase::SetUpOnMainThread() to keep it that way.
384}
385