sandboxed_unpacker_unittest.cc revision 90dce4d38c5ff5333bea97d859d4e484e27edf0c
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 "base/file_util.h" 6#include "base/files/scoped_temp_dir.h" 7#include "base/memory/ref_counted.h" 8#include "base/message_loop.h" 9#include "base/path_service.h" 10#include "base/string_util.h" 11#include "base/values.h" 12#include "chrome/browser/extensions/sandboxed_unpacker.h" 13#include "chrome/common/chrome_paths.h" 14#include "chrome/common/extensions/extension.h" 15#include "chrome/common/extensions/unpacker.h" 16#include "content/public/test/test_browser_thread.h" 17#include "extensions/common/constants.h" 18#include "testing/gmock/include/gmock/gmock.h" 19#include "testing/gtest/include/gtest/gtest.h" 20#include "third_party/skia/include/core/SkBitmap.h" 21 22using content::BrowserThread; 23using testing::_; 24using testing::Invoke; 25 26namespace { 27 28void OnUnpackSuccess(const base::FilePath& temp_dir, 29 const base::FilePath& extension_root, 30 const DictionaryValue* original_manifest, 31 const extensions::Extension* extension) { 32 // Don't delete temp_dir here, we need to do some post op checking. 33} 34 35} // namespace 36 37namespace extensions { 38 39class MockSandboxedUnpackerClient : public SandboxedUnpackerClient { 40 public: 41 MOCK_METHOD4(OnUnpackSuccess, 42 void(const base::FilePath& temp_dir, 43 const base::FilePath& extension_root, 44 const DictionaryValue* original_manifest, 45 const Extension* extension)); 46 47 MOCK_METHOD1(OnUnpackFailure, 48 void(const string16& error)); 49 50 void DelegateToFake() { 51 ON_CALL(*this, OnUnpackSuccess(_, _, _, _)) 52 .WillByDefault(Invoke(::OnUnpackSuccess)); 53 } 54 55 protected: 56 virtual ~MockSandboxedUnpackerClient() {} 57}; 58 59class SandboxedUnpackerTest : public testing::Test { 60 public: 61 virtual void SetUp() { 62 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); 63 ASSERT_TRUE(extensions_dir_.CreateUniqueTempDir()); 64 file_thread_.reset(new content::TestBrowserThread(BrowserThread::FILE, 65 &loop_)); 66 // It will delete itself. 67 client_ = new MockSandboxedUnpackerClient; 68 client_->DelegateToFake(); 69 } 70 71 virtual void TearDown() { 72 // Need to destruct SandboxedUnpacker before the message loop since 73 // it posts a task to it. 74 sandboxed_unpacker_ = NULL; 75 loop_.RunUntilIdle(); 76 } 77 78 void SetupUnpacker(const std::string& crx_name) { 79 base::FilePath original_path; 80 ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &original_path)); 81 original_path = original_path.AppendASCII("extensions") 82 .AppendASCII("unpacker") 83 .AppendASCII(crx_name); 84 ASSERT_TRUE(file_util::PathExists(original_path)) << original_path.value(); 85 86 // Try bots won't let us write into DIR_TEST_DATA, so we have to write the 87 // CRX to the temp directory, and create a subdirectory into which to 88 // unpack it. 89 base::FilePath crx_path = temp_dir_.path().AppendASCII(crx_name); 90 ASSERT_TRUE(file_util::CopyFile(original_path, crx_path)) << 91 "Original path: " << original_path.value() << 92 ", Crx path: " << crx_path.value(); 93 94 unpacker_.reset(new Unpacker( 95 crx_path, std::string(), Manifest::INTERNAL, Extension::NO_FLAGS)); 96 97 // Build a temp area where the extension will be unpacked. 98 temp_path_ = 99 temp_dir_.path().AppendASCII("sandboxed_unpacker_test_Temp"); 100 ASSERT_TRUE(file_util::CreateDirectory(temp_path_)); 101 102 sandboxed_unpacker_ = 103 new SandboxedUnpacker( 104 crx_path, false, Manifest::INTERNAL, Extension::NO_FLAGS, 105 extensions_dir_.path(), 106 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE), 107 client_); 108 109 EXPECT_TRUE(PrepareUnpackerEnv()); 110 } 111 112 bool PrepareUnpackerEnv() { 113 sandboxed_unpacker_->extension_root_ = 114 temp_dir_.path().AppendASCII(extension_filenames::kTempExtensionName); 115 116 if (!sandboxed_unpacker_->temp_dir_.Set(temp_dir_.path())) 117 return false; 118 sandboxed_unpacker_->public_key_ = 119 "ocnapchkplbmjmpfehjocmjnipfmogkh"; 120 return true; 121 } 122 123 void OnUnpackSucceeded() { 124 sandboxed_unpacker_->OnUnpackExtensionSucceeded( 125 *unpacker_->parsed_manifest()); 126 } 127 128 base::FilePath GetInstallPath() { 129 return temp_dir_.path().AppendASCII( 130 extension_filenames::kTempExtensionName); 131 } 132 133 bool TempFilesRemoved() { 134 // Check that temporary files were cleaned up. 135 int files_and_dirs = file_util::FileEnumerator::DIRECTORIES | 136 file_util::FileEnumerator::FILES; 137 138 file_util::FileEnumerator temp_iterator( 139 temp_path_, 140 true, // recursive 141 files_and_dirs 142 ); 143 int items_not_removed = 0; 144 base::FilePath item_in_temp; 145 item_in_temp = temp_iterator.Next(); 146 while (!item_in_temp.value().empty()) { 147 items_not_removed++; 148 EXPECT_STREQ(FILE_PATH_LITERAL(""), item_in_temp.value().c_str()) 149 << "File was not removed on success."; 150 item_in_temp = temp_iterator.Next(); 151 } 152 return (items_not_removed == 0); 153 } 154 155 protected: 156 base::ScopedTempDir temp_dir_; 157 base::ScopedTempDir extensions_dir_; 158 base::FilePath temp_path_; 159 MockSandboxedUnpackerClient* client_; 160 scoped_ptr<Unpacker> unpacker_; 161 scoped_refptr<SandboxedUnpacker> sandboxed_unpacker_; 162 base::MessageLoop loop_; 163 scoped_ptr<content::TestBrowserThread> file_thread_; 164}; 165 166TEST_F(SandboxedUnpackerTest, NoCatalogsSuccess) { 167 EXPECT_CALL(*client_, OnUnpackSuccess(_, _, _, _)); 168 EXPECT_CALL(*client_, OnUnpackFailure(_)).Times(0); 169 170 SetupUnpacker("no_l10n.crx"); 171 ASSERT_TRUE(unpacker_->Run()); 172 ASSERT_TRUE(unpacker_->DumpImagesToFile()); 173 ASSERT_TRUE(unpacker_->DumpMessageCatalogsToFile()); 174 175 // Check that there is no _locales folder. 176 base::FilePath install_path = 177 GetInstallPath().Append(kLocaleFolder); 178 EXPECT_FALSE(file_util::PathExists(install_path)); 179 180 OnUnpackSucceeded(); 181 182 // Check that there still is no _locales folder. 183 EXPECT_FALSE(file_util::PathExists(install_path)); 184 185 ASSERT_TRUE(TempFilesRemoved()); 186} 187 188TEST_F(SandboxedUnpackerTest, WithCatalogsSuccess) { 189 EXPECT_CALL(*client_, OnUnpackSuccess(_, _, _, _)); 190 EXPECT_CALL(*client_, OnUnpackFailure(_)).Times(0); 191 192 SetupUnpacker("good_l10n.crx"); 193 ASSERT_TRUE(unpacker_->Run()); 194 ASSERT_TRUE(unpacker_->DumpImagesToFile()); 195 ASSERT_TRUE(unpacker_->DumpMessageCatalogsToFile()); 196 197 // Set timestamp on _locales/en_US/messages.json into the past. 198 base::FilePath messages_file; 199 messages_file = GetInstallPath().Append(kLocaleFolder) 200 .AppendASCII("en_US") 201 .Append(kMessagesFilename); 202 base::PlatformFileInfo old_info; 203 EXPECT_TRUE(file_util::GetFileInfo(messages_file, &old_info)); 204 base::Time old_time = 205 old_info.last_modified - base::TimeDelta::FromSeconds(2); 206 EXPECT_TRUE(file_util::SetLastModifiedTime(messages_file, old_time)); 207 // Refresh old_info, just to be sure. 208 EXPECT_TRUE(file_util::GetFileInfo(messages_file, &old_info)); 209 210 OnUnpackSucceeded(); 211 212 // Check that there is newer _locales/en_US/messages.json file. 213 base::PlatformFileInfo new_info; 214 EXPECT_TRUE(file_util::GetFileInfo(messages_file, &new_info)); 215 216 EXPECT_TRUE(new_info.last_modified > old_info.last_modified); 217 218 ASSERT_TRUE(TempFilesRemoved()); 219} 220 221} // namespace extensions 222