Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include <fstream> | |
| 6 | |
| 5 #include "base/file_util.h" | 7 #include "base/file_util.h" |
| 6 #include "base/path_service.h" | 8 #include "base/path_service.h" |
| 7 #include "base/ref_counted.h" | 9 #include "base/ref_counted.h" |
| 8 #include "base/scoped_temp_dir.h" | 10 #include "base/scoped_temp_dir.h" |
| 9 #include "base/string_util.h" | 11 #include "base/string_util.h" |
| 12 #include "base/utf_string_conversions.h" | |
| 10 #include "chrome/browser/extensions/sandboxed_extension_unpacker.h" | 13 #include "chrome/browser/extensions/sandboxed_extension_unpacker.h" |
| 11 #include "chrome/common/chrome_paths.h" | 14 #include "chrome/common/chrome_paths.h" |
| 12 #include "chrome/common/extensions/extension.h" | 15 #include "chrome/common/extensions/extension.h" |
| 13 #include "chrome/common/extensions/extension_constants.h" | 16 #include "chrome/common/extensions/extension_constants.h" |
| 14 #include "chrome/common/extensions/extension_unpacker.h" | 17 #include "chrome/common/extensions/extension_unpacker.h" |
| 15 #include "testing/gmock/include/gmock/gmock.h" | 18 #include "testing/gmock/include/gmock/gmock.h" |
| 16 #include "testing/gtest/include/gtest/gtest.h" | 19 #include "testing/gtest/include/gtest/gtest.h" |
| 17 #include "third_party/skia/include/core/SkBitmap.h" | 20 #include "third_party/skia/include/core/SkBitmap.h" |
| 18 | 21 |
| 19 namespace errors = extension_manifest_errors; | 22 namespace errors = extension_manifest_errors; |
| 20 namespace keys = extension_manifest_keys; | 23 namespace keys = extension_manifest_keys; |
| 21 | 24 |
| 22 using testing::_; | 25 using testing::_; |
| 23 using testing::Invoke; | 26 using testing::Invoke; |
| 24 | 27 |
| 28 namespace { | |
| 29 | |
| 30 // Create a non-empty file. | |
| 31 void CreateTextFile(const FilePath& filename) { | |
| 32 std::ofstream file; | |
| 33 file.open(WideToUTF8(filename.ToWStringHack()).c_str()); | |
|
Erik does not do reviews
2011/01/14 22:34:44
This looks problematic - potential filename incomp
Sam Kerner (Chrome)
2011/01/18 19:26:45
Done.
| |
| 34 ASSERT_TRUE(file.is_open()); | |
| 35 file << "I am a text file."; | |
| 36 file.close(); | |
| 37 } | |
| 38 | |
| 39 // Clear a scoped_temp_dir if it has any contents. | |
| 40 void ClearScopedTempDir(ScopedTempDir& scoped_temp_dir) { | |
| 41 if (scoped_temp_dir.IsValid()) { | |
|
Erik does not do reviews
2011/01/14 22:34:44
nit: remove {}
Sam Kerner (Chrome)
2011/01/18 19:26:45
Done.
| |
| 42 EXPECT_TRUE(scoped_temp_dir.Delete()); | |
| 43 } | |
| 44 } | |
| 45 | |
| 25 void OnUnpackSuccess(const FilePath& temp_dir, | 46 void OnUnpackSuccess(const FilePath& temp_dir, |
| 26 const FilePath& extension_root, | 47 const FilePath& extension_root, |
| 27 const Extension* extension) { | 48 const Extension* extension) { |
| 28 // Don't delete temp_dir here, we need to do some post op checking. | 49 // Don't delete temp_dir here, we need to do some post op checking. |
| 29 } | 50 } |
| 30 | 51 |
| 52 } | |
| 53 | |
| 31 class MockSandboxedExtensionUnpackerClient | 54 class MockSandboxedExtensionUnpackerClient |
| 32 : public SandboxedExtensionUnpackerClient { | 55 : public SandboxedExtensionUnpackerClient { |
| 33 public: | 56 public: |
| 34 virtual ~MockSandboxedExtensionUnpackerClient() {} | 57 virtual ~MockSandboxedExtensionUnpackerClient() {} |
| 35 | 58 |
| 36 MOCK_METHOD3(OnUnpackSuccess, | 59 MOCK_METHOD3(OnUnpackSuccess, |
| 37 void(const FilePath& temp_dir, | 60 void(const FilePath& temp_dir, |
| 38 const FilePath& extension_root, | 61 const FilePath& extension_root, |
| 39 const Extension* extension)); | 62 const Extension* extension)); |
| 40 | 63 |
| (...skipping 30 matching lines...) Expand all Loading... | |
| 71 ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &original_path)); | 94 ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &original_path)); |
| 72 original_path = original_path.AppendASCII("extensions") | 95 original_path = original_path.AppendASCII("extensions") |
| 73 .AppendASCII("unpacker") | 96 .AppendASCII("unpacker") |
| 74 .AppendASCII(crx_name); | 97 .AppendASCII(crx_name); |
| 75 ASSERT_TRUE(file_util::PathExists(original_path)) << original_path.value(); | 98 ASSERT_TRUE(file_util::PathExists(original_path)) << original_path.value(); |
| 76 | 99 |
| 77 // Try bots won't let us write into DIR_TEST_DATA, so we have to create | 100 // Try bots won't let us write into DIR_TEST_DATA, so we have to create |
| 78 // a temp folder to play in. | 101 // a temp folder to play in. |
| 79 ASSERT_TRUE(PathService::Get(base::DIR_TEMP, &install_dir_)); | 102 ASSERT_TRUE(PathService::Get(base::DIR_TEMP, &install_dir_)); |
| 80 install_dir_ = | 103 install_dir_ = |
| 81 install_dir_.AppendASCII("sandboxed_extension_unpacker_test"); | 104 install_dir_.AppendASCII("sandboxed_extension_unpacker_test"); |
| 82 file_util::Delete(install_dir_, true); | 105 file_util::Delete(install_dir_, true); |
| 83 file_util::CreateDirectory(install_dir_); | 106 file_util::CreateDirectory(install_dir_); |
| 84 | 107 |
| 85 FilePath crx_path = install_dir_.AppendASCII(crx_name); | 108 FilePath crx_path = install_dir_.AppendASCII(crx_name); |
| 86 ASSERT_TRUE(file_util::CopyFile(original_path, crx_path)) << | 109 ASSERT_TRUE(file_util::CopyFile(original_path, crx_path)) << |
| 87 "Original path: " << original_path.value() << | 110 "Original path: " << original_path.value() << |
| 88 ", Crx path: " << crx_path.value(); | 111 ", Crx path: " << crx_path.value(); |
| 89 | 112 |
| 90 unpacker_.reset(new ExtensionUnpacker(crx_path)); | 113 unpacker_.reset(new ExtensionUnpacker(crx_path)); |
| 91 | 114 |
| 92 | |
| 93 // Build a temp area where the extension will be unpacked. | 115 // Build a temp area where the extension will be unpacked. |
| 94 ASSERT_TRUE(PathService::Get(base::DIR_TEMP, &temp_dir_)); | 116 ASSERT_TRUE(PathService::Get(base::DIR_TEMP, &temp_dir_)); |
| 95 temp_dir_ = temp_dir_.AppendASCII("sandboxed_extension_unpacker_test_Temp"); | 117 temp_dir_ = temp_dir_.AppendASCII("sandboxed_extension_unpacker_test_Temp"); |
| 96 file_util::CreateDirectory(temp_dir_); | 118 ASSERT_TRUE(file_util::CreateDirectory(temp_dir_)); |
| 119 | |
| 120 //PathService::Override(chrome::DIR_USER_DATA_TEMP, temp_dir_); | |
|
Erik does not do reviews
2011/01/14 22:34:44
remove unused code
Sam Kerner (Chrome)
2011/01/18 19:26:45
Done.
| |
| 97 | 121 |
| 98 sandboxed_unpacker_ = | 122 sandboxed_unpacker_ = |
| 99 new SandboxedExtensionUnpacker(crx_path, temp_dir_, NULL, client_); | 123 new SandboxedExtensionUnpacker(crx_path, NULL, client_); |
| 124 | |
| 100 // Hack since SandboxedExtensionUnpacker gets its background thread id from | 125 // Hack since SandboxedExtensionUnpacker gets its background thread id from |
| 101 // the Start call, but we don't call it here. | 126 // the Start call, but we don't call it here. |
| 102 sandboxed_unpacker_->thread_identifier_ = BrowserThread::FILE; | 127 sandboxed_unpacker_->thread_identifier_ = BrowserThread::FILE; |
| 103 EXPECT_TRUE(PrepareUnpackerEnv()); | |
| 104 } | 128 } |
| 105 | 129 |
| 106 bool PrepareUnpackerEnv() { | 130 bool PrepareUnpackerEnv() { |
| 107 sandboxed_unpacker_->extension_root_ = | 131 sandboxed_unpacker_->extension_root_ = |
| 108 install_dir_.AppendASCII(extension_filenames::kTempExtensionName); | 132 install_dir_.AppendASCII(extension_filenames::kTempExtensionName); |
| 109 | 133 |
| 110 if (!sandboxed_unpacker_->temp_dir_.Set(install_dir_)) | 134 if (!sandboxed_unpacker_->temp_dir_.Set(install_dir_)) |
| 111 return false; | 135 return false; |
| 112 sandboxed_unpacker_->public_key_ = | 136 sandboxed_unpacker_->public_key_ = |
| 113 "ocnapchkplbmjmpfehjocmjnipfmogkh"; | 137 "ocnapchkplbmjmpfehjocmjnipfmogkh"; |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 152 FilePath temp_dir_; | 176 FilePath temp_dir_; |
| 153 MockSandboxedExtensionUnpackerClient* client_; | 177 MockSandboxedExtensionUnpackerClient* client_; |
| 154 scoped_ptr<ExtensionUnpacker> unpacker_; | 178 scoped_ptr<ExtensionUnpacker> unpacker_; |
| 155 scoped_refptr<SandboxedExtensionUnpacker> sandboxed_unpacker_; | 179 scoped_refptr<SandboxedExtensionUnpacker> sandboxed_unpacker_; |
| 156 MessageLoop loop_; | 180 MessageLoop loop_; |
| 157 scoped_ptr<BrowserThread> file_thread_; | 181 scoped_ptr<BrowserThread> file_thread_; |
| 158 }; | 182 }; |
| 159 | 183 |
| 160 TEST_F(SandboxedExtensionUnpackerTest, NoCatalogsSuccess) { | 184 TEST_F(SandboxedExtensionUnpackerTest, NoCatalogsSuccess) { |
| 161 EXPECT_CALL(*client_, OnUnpackSuccess(_, _, _)); | 185 EXPECT_CALL(*client_, OnUnpackSuccess(_, _, _)); |
| 186 EXPECT_CALL(*client_, OnUnpackFailure(_)).Times(0); | |
| 162 | 187 |
| 163 SetupUnpacker("no_l10n.crx"); | 188 SetupUnpacker("no_l10n.crx"); |
| 189 PrepareUnpackerEnv(); | |
|
Erik does not do reviews
2011/01/14 22:34:44
shouldn't this have EXPECT or ASSERT on it? (and o
Sam Kerner (Chrome)
2011/01/18 19:26:45
Yes. Done.
| |
| 164 ASSERT_TRUE(unpacker_->Run()); | 190 ASSERT_TRUE(unpacker_->Run()); |
| 165 ASSERT_TRUE(unpacker_->DumpImagesToFile()); | 191 ASSERT_TRUE(unpacker_->DumpImagesToFile()); |
| 166 ASSERT_TRUE(unpacker_->DumpMessageCatalogsToFile()); | 192 ASSERT_TRUE(unpacker_->DumpMessageCatalogsToFile()); |
| 167 | |
| 168 // Check that there is no _locales folder. | 193 // Check that there is no _locales folder. |
| 169 FilePath install_path = | 194 FilePath install_path = |
| 170 GetInstallPath().Append(Extension::kLocaleFolder); | 195 GetInstallPath().Append(Extension::kLocaleFolder); |
| 171 EXPECT_FALSE(file_util::PathExists(install_path)); | 196 EXPECT_FALSE(file_util::PathExists(install_path)); |
| 172 | |
| 173 OnUnpackSucceeded(); | 197 OnUnpackSucceeded(); |
| 174 | 198 |
| 175 // Check that there still is no _locales folder. | 199 // Check that there still is no _locales folder. |
| 176 EXPECT_FALSE(file_util::PathExists(install_path)); | 200 EXPECT_FALSE(file_util::PathExists(install_path)); |
| 177 | 201 |
| 178 ASSERT_TRUE(TempFilesRemoved()); | 202 ASSERT_TRUE(TempFilesRemoved()); |
| 179 } | 203 } |
| 180 | 204 |
| 181 TEST_F(SandboxedExtensionUnpackerTest, WithCatalogsSuccess) { | 205 TEST_F(SandboxedExtensionUnpackerTest, WithCatalogsSuccess) { |
| 182 EXPECT_CALL(*client_, OnUnpackSuccess(_, _, _)); | 206 EXPECT_CALL(*client_, OnUnpackSuccess(_, _, _)); |
| 207 EXPECT_CALL(*client_, OnUnpackFailure(_)).Times(0); | |
| 183 | 208 |
| 184 SetupUnpacker("good_l10n.crx"); | 209 SetupUnpacker("good_l10n.crx"); |
| 210 PrepareUnpackerEnv(); | |
| 185 ASSERT_TRUE(unpacker_->Run()); | 211 ASSERT_TRUE(unpacker_->Run()); |
| 186 ASSERT_TRUE(unpacker_->DumpImagesToFile()); | 212 ASSERT_TRUE(unpacker_->DumpImagesToFile()); |
| 187 ASSERT_TRUE(unpacker_->DumpMessageCatalogsToFile()); | 213 ASSERT_TRUE(unpacker_->DumpMessageCatalogsToFile()); |
| 188 | 214 |
| 189 // Set timestamp on _locales/en_US/messages.json into the past. | 215 // Set timestamp on _locales/en_US/messages.json into the past. |
| 190 FilePath messages_file; | 216 FilePath messages_file; |
| 191 messages_file = GetInstallPath().Append(Extension::kLocaleFolder) | 217 messages_file = GetInstallPath().Append(Extension::kLocaleFolder) |
| 192 .AppendASCII("en_US") | 218 .AppendASCII("en_US") |
| 193 .Append(Extension::kMessagesFilename); | 219 .Append(Extension::kMessagesFilename); |
| 194 base::PlatformFileInfo old_info; | 220 base::PlatformFileInfo old_info; |
| 195 EXPECT_TRUE(file_util::GetFileInfo(messages_file, &old_info)); | 221 EXPECT_TRUE(file_util::GetFileInfo(messages_file, &old_info)); |
| 196 base::Time old_time = | 222 base::Time old_time = |
| 197 old_info.last_modified - base::TimeDelta::FromSeconds(2); | 223 old_info.last_modified - base::TimeDelta::FromSeconds(2); |
| 198 EXPECT_TRUE(file_util::SetLastModifiedTime(messages_file, old_time)); | 224 EXPECT_TRUE(file_util::SetLastModifiedTime(messages_file, old_time)); |
| 199 // Refresh old_info, just to be sure. | 225 // Refresh old_info, just to be sure. |
| 200 EXPECT_TRUE(file_util::GetFileInfo(messages_file, &old_info)); | 226 EXPECT_TRUE(file_util::GetFileInfo(messages_file, &old_info)); |
| 201 | 227 |
| 202 OnUnpackSucceeded(); | 228 OnUnpackSucceeded(); |
| 203 | 229 |
| 204 // Check that there is newer _locales/en_US/messages.json file. | 230 // Check that there is newer _locales/en_US/messages.json file. |
| 205 base::PlatformFileInfo new_info; | 231 base::PlatformFileInfo new_info; |
| 206 EXPECT_TRUE(file_util::GetFileInfo(messages_file, &new_info)); | 232 EXPECT_TRUE(file_util::GetFileInfo(messages_file, &new_info)); |
| 207 | 233 |
| 208 EXPECT_TRUE(new_info.last_modified > old_info.last_modified); | 234 EXPECT_TRUE(new_info.last_modified > old_info.last_modified); |
| 209 | 235 |
| 210 ASSERT_TRUE(TempFilesRemoved()); | 236 ASSERT_TRUE(TempFilesRemoved()); |
| 211 } | 237 } |
| 238 | |
| 239 TEST_F(SandboxedExtensionUnpackerTest, CreateTempDirectory) { | |
|
Erik does not do reviews
2011/01/14 22:34:44
If we ditch the multiple dir support for now, is t
Sam Kerner (Chrome)
2011/01/18 19:26:45
No, but it could be used to test error handling of
| |
| 240 // To be sure overriding path keys does not change the paths seen | |
| 241 // by existing code, use some path keys that do not exist outside | |
| 242 // this test. | |
| 243 enum { | |
| 244 PATH_START = chrome::PATH_END+1, | |
| 245 TEST_DIR_WORKS, | |
| 246 TEST_DIR_CANT_GET, | |
| 247 TEST_DIR_CANT_WRITE, | |
| 248 PATH_END | |
| 249 }; | |
| 250 | |
| 251 // Create a fake temp path. | |
| 252 ScopedTempDir fake_user_temp_dir; | |
| 253 ASSERT_TRUE(fake_user_temp_dir.CreateUniqueTempDir()); | |
| 254 | |
| 255 // |dir_works| will work for unpacking. | |
| 256 FilePath dir_works(fake_user_temp_dir.path()); | |
| 257 | |
| 258 // Needed because path service code runs AbsolutePath() on all values | |
| 259 // it returns. This changes the path on the mac trybots, and we need | |
| 260 // to compare this path to the result from PathService::Get() below. | |
| 261 ASSERT_TRUE(file_util::AbsolutePath(&dir_works)); | |
| 262 | |
| 263 // To make directory creation fail, use a base path which is a file. | |
| 264 FilePath dir_cant_write(fake_user_temp_dir.path().AppendASCII("cant_write")); | |
| 265 CreateTextFile(dir_cant_write); | |
| 266 | |
| 267 // Set up test paths: | |
| 268 PathService::Override(TEST_DIR_WORKS, dir_works); | |
| 269 PathService::Override(TEST_DIR_CANT_GET, FilePath()); | |
| 270 PathService::Override(TEST_DIR_CANT_WRITE, dir_cant_write); | |
| 271 | |
| 272 // Test that our path keys give paths with the desired properties: | |
| 273 FilePath scratch; | |
| 274 ASSERT_TRUE(PathService::Get(TEST_DIR_WORKS, &scratch)); | |
| 275 ASSERT_EQ(dir_works.value(), scratch.value()); | |
| 276 | |
| 277 ASSERT_FALSE(PathService::Get(TEST_DIR_CANT_GET, &scratch)); | |
| 278 | |
| 279 ASSERT_TRUE(PathService::Get(TEST_DIR_CANT_WRITE, &scratch)); | |
| 280 ASSERT_EQ(dir_cant_write.value(), scratch.value()); | |
| 281 ASSERT_FALSE(file_util::CreateDirectory( | |
| 282 dir_cant_write.AppendASCII("cant_make_dir_in_a_file"))); | |
| 283 | |
| 284 SetupUnpacker("good_l10n.crx"); | |
| 285 | |
| 286 // Test that a valid, writable path is read and used. | |
| 287 const int kWorkingDir[] = { | |
| 288 TEST_DIR_WORKS | |
| 289 }; | |
| 290 // Clear |temp_dir_|, so that we can test that it was set. | |
| 291 ClearScopedTempDir(sandboxed_unpacker_->temp_dir_); | |
| 292 EXPECT_CALL(*client_, OnUnpackFailure(_)).Times(0); | |
| 293 ASSERT_TRUE(sandboxed_unpacker_->CreateTempDirectory( | |
| 294 kWorkingDir, arraysize(kWorkingDir))); | |
| 295 ASSERT_EQ(dir_works.value(), | |
| 296 sandboxed_unpacker_->temp_dir_.path().DirName().value()); | |
| 297 | |
| 298 // Test that two unusable paths both fail. | |
| 299 const int kFailingDir[] = { | |
| 300 TEST_DIR_CANT_GET, | |
| 301 TEST_DIR_CANT_WRITE | |
| 302 }; | |
| 303 ClearScopedTempDir(sandboxed_unpacker_->temp_dir_); | |
| 304 EXPECT_CALL(*client_, OnUnpackFailure(_)).Times(1); | |
| 305 ASSERT_FALSE(sandboxed_unpacker_->CreateTempDirectory( | |
| 306 kFailingDir, arraysize(kFailingDir))); | |
| 307 | |
| 308 // If at first you don't succeed, try try again. | |
| 309 const int kFallback[] = { | |
| 310 TEST_DIR_CANT_WRITE, | |
| 311 TEST_DIR_CANT_GET, | |
| 312 TEST_DIR_WORKS | |
| 313 }; | |
| 314 ClearScopedTempDir(sandboxed_unpacker_->temp_dir_); | |
| 315 EXPECT_CALL(*client_, OnUnpackFailure(_)).Times(0); | |
| 316 ASSERT_TRUE( | |
| 317 sandboxed_unpacker_->CreateTempDirectory( | |
| 318 kFallback, arraysize(kFallback))); | |
| 319 ASSERT_EQ( | |
| 320 dir_works.value(), | |
| 321 sandboxed_unpacker_->temp_dir_.path().DirName().value()); | |
| 322 } | |
| OLD | NEW |