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