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/browser/nacl_host/nacl_file_host.h" 6 7 #include "base/basictypes.h" 8 #include "base/files/file_path.h" 9 #include "base/files/scoped_temp_dir.h" 10 #include "base/run_loop.h" 11 #include "base/test/scoped_path_override.h" 12 #include "base/threading/sequenced_worker_pool.h" 13 #include "chrome/browser/nacl_host/nacl_browser.h" 14 #include "components/nacl/common/nacl_browser_delegate.h" 15 #include "components/nacl/common/pnacl_types.h" 16 #include "content/public/browser/browser_thread.h" 17 #include "content/public/test/test_browser_thread_bundle.h" 18 #include "testing/gtest/include/gtest/gtest.h" 19 20 using nacl_file_host::PnaclCanOpenFile; 21 using nacl_file_host::EnsurePnaclInstalled; 22 23 class TestNaClBrowserDelegate : public NaClBrowserDelegate { 24 public: 25 26 TestNaClBrowserDelegate() : should_pnacl_install_succeed_(false) { } 27 28 virtual void ShowNaClInfobar(int render_process_id, 29 int render_view_id, 30 int error_id) OVERRIDE { 31 } 32 33 virtual bool DialogsAreSuppressed() OVERRIDE { 34 return false; 35 } 36 37 virtual bool GetCacheDirectory(base::FilePath* cache_dir) OVERRIDE { 38 return false; 39 } 40 41 virtual bool GetPluginDirectory(base::FilePath* plugin_dir) OVERRIDE { 42 return false; 43 } 44 45 virtual bool GetPnaclDirectory(base::FilePath* pnacl_dir) OVERRIDE { 46 *pnacl_dir = pnacl_path_; 47 return true; 48 } 49 50 virtual bool GetUserDirectory(base::FilePath* user_dir) OVERRIDE { 51 return false; 52 } 53 54 virtual std::string GetVersionString() const OVERRIDE { 55 return std::string(); 56 } 57 58 virtual ppapi::host::HostFactory* CreatePpapiHostFactory( 59 content::BrowserPpapiHost* ppapi_host) OVERRIDE { 60 return NULL; 61 } 62 63 virtual void TryInstallPnacl( 64 const base::Callback<void(bool)>& installed) OVERRIDE { 65 installed.Run(should_pnacl_install_succeed_); 66 } 67 68 void SetPnaclDirectory(const base::FilePath& pnacl_dir) { 69 pnacl_path_ = pnacl_dir; 70 } 71 72 // Indicate if we should mock the PNaCl install as succeeding 73 // or failing for the next test. 74 void SetShouldPnaclInstallSucceed(bool succeed) { 75 should_pnacl_install_succeed_ = succeed; 76 } 77 78 private: 79 base::FilePath pnacl_path_; 80 bool should_pnacl_install_succeed_; 81 }; 82 83 class NaClFileHostTest : public testing::Test { 84 protected: 85 NaClFileHostTest(); 86 virtual ~NaClFileHostTest(); 87 88 virtual void SetUp() OVERRIDE { 89 nacl_browser_delegate_ = new TestNaClBrowserDelegate; 90 NaClBrowser::SetDelegate(nacl_browser_delegate_); 91 } 92 93 virtual void TearDown() OVERRIDE { 94 NaClBrowser::SetDelegate(NULL); // This deletes nacl_browser_delegate_ 95 } 96 97 TestNaClBrowserDelegate* nacl_browser_delegate() { 98 return nacl_browser_delegate_; 99 } 100 101 bool install_success() { return install_success_; } 102 size_t install_call_count() { 103 return std::count(events_.begin(), events_.end(), INSTALL_DONE); 104 } 105 size_t progress_call_count() { 106 return std::count(events_.begin(), events_.end(), INSTALL_PROGRESS); 107 } 108 bool events_in_correct_order() { 109 // INSTALL_DONE should be the last thing. 110 // The rest should be progress events. 111 size_t size = events_.size(); 112 return size > 0 && events_[size - 1] == INSTALL_DONE 113 && progress_call_count() == (size - 1); 114 } 115 116 public: // Allow classes to bind these callback methods. 117 void CallbackInstall(bool success) { 118 install_success_ = success; 119 events_.push_back(INSTALL_DONE); 120 } 121 122 void CallbackProgress(const nacl::PnaclInstallProgress& p) { 123 // Check that the first event has an unknown total. 124 if (events_.size() == 0) { 125 EXPECT_FALSE(nacl::PnaclInstallProgress::progress_known(p)); 126 } 127 events_.push_back(INSTALL_PROGRESS); 128 // TODO(jvoung): be able to check that current_progress 129 // goes up monotonically and hits total_progress at the end, 130 // when we actually get real progress events. 131 } 132 133 private: 134 enum EventType { 135 INSTALL_DONE, 136 INSTALL_PROGRESS 137 }; 138 TestNaClBrowserDelegate* nacl_browser_delegate_; 139 bool install_success_; 140 std::vector<EventType> events_; 141 content::TestBrowserThreadBundle thread_bundle_; 142 DISALLOW_COPY_AND_ASSIGN(NaClFileHostTest); 143 }; 144 145 NaClFileHostTest::NaClFileHostTest() 146 : nacl_browser_delegate_(NULL), 147 install_success_(false), 148 thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP) { 149 } 150 151 NaClFileHostTest::~NaClFileHostTest() { 152 } 153 154 // Try to pass a few funny filenames with a dummy PNaCl directory set. 155 TEST_F(NaClFileHostTest, TestFilenamesWithPnaclPath) { 156 base::ScopedTempDir scoped_tmp_dir; 157 ASSERT_TRUE(scoped_tmp_dir.CreateUniqueTempDir()); 158 159 base::FilePath kTestPnaclPath = scoped_tmp_dir.path(); 160 161 nacl_browser_delegate()->SetPnaclDirectory(kTestPnaclPath); 162 ASSERT_TRUE(NaClBrowser::GetDelegate()->GetPnaclDirectory(&kTestPnaclPath)); 163 164 // Check allowed strings, and check that the expected prefix is added. 165 base::FilePath out_path; 166 EXPECT_TRUE(PnaclCanOpenFile("pnacl_json", &out_path)); 167 base::FilePath expected_path = kTestPnaclPath.Append( 168 FILE_PATH_LITERAL("pnacl_public_pnacl_json")); 169 EXPECT_EQ(expected_path, out_path); 170 171 EXPECT_TRUE(PnaclCanOpenFile("x86_32_llc", &out_path)); 172 expected_path = kTestPnaclPath.Append( 173 FILE_PATH_LITERAL("pnacl_public_x86_32_llc")); 174 EXPECT_EQ(expected_path, out_path); 175 176 // Check character ranges. 177 EXPECT_FALSE(PnaclCanOpenFile(".xchars", &out_path)); 178 EXPECT_FALSE(PnaclCanOpenFile("/xchars", &out_path)); 179 EXPECT_FALSE(PnaclCanOpenFile("x/chars", &out_path)); 180 EXPECT_FALSE(PnaclCanOpenFile("\\xchars", &out_path)); 181 EXPECT_FALSE(PnaclCanOpenFile("x\\chars", &out_path)); 182 EXPECT_FALSE(PnaclCanOpenFile("$xchars", &out_path)); 183 EXPECT_FALSE(PnaclCanOpenFile("%xchars", &out_path)); 184 EXPECT_FALSE(PnaclCanOpenFile("CAPS", &out_path)); 185 const char non_ascii[] = "\xff\xfe"; 186 EXPECT_FALSE(PnaclCanOpenFile(non_ascii, &out_path)); 187 188 // Check file length restriction. 189 EXPECT_FALSE(PnaclCanOpenFile("thisstringisactuallywaaaaaaaaaaaaaaaaaaaaaaaa" 190 "toolongwaytoolongwaaaaayyyyytoooooooooooooooo" 191 "looooooooong", 192 &out_path)); 193 194 // Other bad files. 195 EXPECT_FALSE(PnaclCanOpenFile(std::string(), &out_path)); 196 EXPECT_FALSE(PnaclCanOpenFile(".", &out_path)); 197 EXPECT_FALSE(PnaclCanOpenFile("..", &out_path)); 198 #if defined(OS_WIN) 199 EXPECT_FALSE(PnaclCanOpenFile("..\\llc", &out_path)); 200 EXPECT_FALSE(PnaclCanOpenFile("%SystemRoot%", &out_path)); 201 EXPECT_FALSE(PnaclCanOpenFile("%SystemRoot%\\explorer.exe", &out_path)); 202 #else 203 EXPECT_FALSE(PnaclCanOpenFile("../llc", &out_path)); 204 EXPECT_FALSE(PnaclCanOpenFile("/bin/sh", &out_path)); 205 EXPECT_FALSE(PnaclCanOpenFile("$HOME", &out_path)); 206 EXPECT_FALSE(PnaclCanOpenFile("$HOME/.bashrc", &out_path)); 207 #endif 208 } 209 210 // Test that callbacks return success when PNaCl looks like it is 211 // already installed. No intermediate progress events are expected. 212 TEST_F(NaClFileHostTest, TestEnsureInstalledAlreadyInstalled) { 213 base::ScopedTempDir scoped_tmp_dir; 214 ASSERT_TRUE(scoped_tmp_dir.CreateUniqueTempDir()); 215 216 base::FilePath kTestPnaclPath = scoped_tmp_dir.path(); 217 nacl_browser_delegate()->SetPnaclDirectory(kTestPnaclPath); 218 ASSERT_TRUE(NaClBrowser::GetDelegate()->GetPnaclDirectory(&kTestPnaclPath)); 219 220 EnsurePnaclInstalled( 221 base::Bind(&NaClFileHostTest::CallbackInstall, base::Unretained(this)), 222 base::Bind(&NaClFileHostTest::CallbackProgress, base::Unretained(this))); 223 content::BrowserThread::GetBlockingPool()->FlushForTesting(); 224 base::RunLoop().RunUntilIdle(); 225 226 EXPECT_TRUE(install_success()); 227 EXPECT_EQ(1u, install_call_count()); 228 EXPECT_EQ(0u, progress_call_count()); 229 EXPECT_TRUE(events_in_correct_order()); 230 } 231 232 // Test that final callback is called with success, when PNaCl does not 233 // look like it's already installed, but mock installer says success. 234 // Also check that intermediate progress events are called. 235 TEST_F(NaClFileHostTest, TestEnsureInstalledNotInstalledSuccess) { 236 base::FilePath kTestPnaclPath; 237 nacl_browser_delegate()->SetPnaclDirectory(kTestPnaclPath); 238 nacl_browser_delegate()->SetShouldPnaclInstallSucceed(true); 239 240 EnsurePnaclInstalled( 241 base::Bind(&NaClFileHostTest::CallbackInstall, base::Unretained(this)), 242 base::Bind(&NaClFileHostTest::CallbackProgress, base::Unretained(this))); 243 content::BrowserThread::GetBlockingPool()->FlushForTesting(); 244 base::RunLoop().RunUntilIdle(); 245 246 EXPECT_TRUE(install_success()); 247 EXPECT_EQ(1u, install_call_count()); 248 EXPECT_GE(progress_call_count(), 1u); 249 EXPECT_TRUE(events_in_correct_order()); 250 } 251 252 // Test that final callback is called with error, when PNaCl does not look 253 // like it's already installed, but mock installer says error. 254 // Also check that intermediate progress events are called. 255 TEST_F(NaClFileHostTest, TestEnsureInstalledNotInstalledError) { 256 base::FilePath kTestPnaclPath; 257 nacl_browser_delegate()->SetPnaclDirectory(kTestPnaclPath); 258 nacl_browser_delegate()->SetShouldPnaclInstallSucceed(false); 259 260 EnsurePnaclInstalled( 261 base::Bind(&NaClFileHostTest::CallbackInstall, base::Unretained(this)), 262 base::Bind(&NaClFileHostTest::CallbackProgress, base::Unretained(this))); 263 content::BrowserThread::GetBlockingPool()->FlushForTesting(); 264 base::RunLoop().RunUntilIdle(); 265 266 EXPECT_FALSE(install_success()); 267 EXPECT_EQ(1u, install_call_count()); 268 EXPECT_GE(progress_call_count(), 1u); 269 EXPECT_TRUE(events_in_correct_order()); 270 } 271