Home | History | Annotate | Download | only in nacl_host
      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