| // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <windows.h> |
| #include <stdint.h> |
| #include <wincrypt.h> |
| |
| #include "base/files/file_util.h" |
| #include "base/files/scoped_temp_dir.h" |
| #include "base/stl_util.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "chrome/installer/util/self_cleaning_temp_dir.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace { |
| |
| // Returns a string of 8 characters consisting of the letter 'R' followed by |
| // seven random hex digits. |
| std::string GetRandomFilename() { |
| uint8_t data[4]; |
| HCRYPTPROV crypt_ctx = NULL; |
| |
| // Get four bytes of randomness. Use CAPI rather than the CRT since I've |
| // seen the latter trivially repeat. |
| EXPECT_NE(FALSE, CryptAcquireContext(&crypt_ctx, NULL, NULL, PROV_RSA_FULL, |
| CRYPT_VERIFYCONTEXT)); |
| EXPECT_NE(FALSE, CryptGenRandom(crypt_ctx, base::size(data), &data[0])); |
| EXPECT_NE(FALSE, CryptReleaseContext(crypt_ctx, 0)); |
| |
| // Hexify the value. |
| std::string result(base::HexEncode(&data[0], base::size(data))); |
| EXPECT_EQ(8u, result.size()); |
| |
| // Replace the first digit with the letter 'R' (for "random", get it?). |
| result[0] = 'R'; |
| |
| return result; |
| } |
| |
| } // namespace |
| |
| namespace installer { |
| |
| class SelfCleaningTempDirTest : public testing::Test { |
| }; |
| |
| // Test the implementation of GetTopDirToCreate when given the root of a |
| // volume. |
| TEST_F(SelfCleaningTempDirTest, TopLevel) { |
| base::FilePath base_dir; |
| SelfCleaningTempDir::GetTopDirToCreate(base::FilePath(L"C:\\"), &base_dir); |
| EXPECT_TRUE(base_dir.empty()); |
| } |
| |
| // Test the implementation of GetTopDirToCreate when given a non-existant dir |
| // under the root of a volume. |
| TEST_F(SelfCleaningTempDirTest, TopLevelPlusOne) { |
| base::FilePath base_dir; |
| base::FilePath parent_dir(L"C:\\"); |
| parent_dir = parent_dir.AppendASCII(GetRandomFilename()); |
| SelfCleaningTempDir::GetTopDirToCreate(parent_dir, &base_dir); |
| EXPECT_EQ(parent_dir, base_dir); |
| } |
| |
| // Test that all intermediate dirs are cleaned up if they're empty when |
| // Delete() is called. |
| TEST_F(SelfCleaningTempDirTest, RemoveUnusedOnDelete) { |
| // Make a directory in which we'll work. |
| base::ScopedTempDir work_dir; |
| EXPECT_TRUE(work_dir.CreateUniqueTempDir()); |
| |
| // Make up some path under the temp dir. |
| base::FilePath parent_temp_dir( |
| work_dir.GetPath().Append(L"One").Append(L"Two")); |
| SelfCleaningTempDir temp_dir; |
| EXPECT_TRUE(temp_dir.Initialize(parent_temp_dir, L"Three")); |
| EXPECT_EQ(parent_temp_dir.Append(L"Three"), temp_dir.path()); |
| EXPECT_TRUE(base::DirectoryExists(temp_dir.path())); |
| EXPECT_TRUE(temp_dir.Delete()); |
| EXPECT_FALSE(base::DirectoryExists(parent_temp_dir.Append(L"Three"))); |
| EXPECT_FALSE(base::DirectoryExists(parent_temp_dir)); |
| EXPECT_FALSE(base::DirectoryExists(parent_temp_dir.DirName())); |
| EXPECT_TRUE(base::DirectoryExists(parent_temp_dir.DirName().DirName())); |
| EXPECT_TRUE(work_dir.Delete()); |
| EXPECT_FALSE(base::DirectoryExists(parent_temp_dir.DirName().DirName())); |
| } |
| |
| // Test that two clients can work in the same area. |
| TEST_F(SelfCleaningTempDirTest, TwoClients) { |
| // Make a directory in which we'll work. |
| base::ScopedTempDir work_dir; |
| EXPECT_TRUE(work_dir.CreateUniqueTempDir()); |
| |
| // Make up some path under the temp dir. |
| base::FilePath parent_temp_dir( |
| work_dir.GetPath().Append(L"One").Append(L"Two")); |
| SelfCleaningTempDir temp_dir1; |
| SelfCleaningTempDir temp_dir2; |
| // First client is created. |
| EXPECT_TRUE(temp_dir1.Initialize(parent_temp_dir, L"Three")); |
| // Second client is created in the same space. |
| EXPECT_TRUE(temp_dir2.Initialize(parent_temp_dir, L"Three")); |
| // Both clients are where they are expected. |
| EXPECT_EQ(parent_temp_dir.Append(L"Three"), temp_dir1.path()); |
| EXPECT_EQ(parent_temp_dir.Append(L"Three"), temp_dir2.path()); |
| EXPECT_TRUE(base::DirectoryExists(temp_dir1.path())); |
| EXPECT_TRUE(base::DirectoryExists(temp_dir2.path())); |
| // Second client goes away. |
| EXPECT_TRUE(temp_dir2.Delete()); |
| // The first is now useless. |
| EXPECT_FALSE(base::DirectoryExists(temp_dir1.path())); |
| // But the intermediate dirs are still present |
| EXPECT_TRUE(base::DirectoryExists(parent_temp_dir)); |
| // Now the first goes away. |
| EXPECT_TRUE(temp_dir1.Delete()); |
| // And cleans up after itself. |
| EXPECT_FALSE(base::DirectoryExists(parent_temp_dir.Append(L"Three"))); |
| EXPECT_FALSE(base::DirectoryExists(parent_temp_dir)); |
| EXPECT_FALSE(base::DirectoryExists(parent_temp_dir.DirName())); |
| EXPECT_TRUE(base::DirectoryExists(parent_temp_dir.DirName().DirName())); |
| EXPECT_TRUE(work_dir.Delete()); |
| EXPECT_FALSE(base::DirectoryExists(parent_temp_dir.DirName().DirName())); |
| } |
| |
| // Test that all intermediate dirs are cleaned up if they're empty when the |
| // destructor is called. |
| TEST_F(SelfCleaningTempDirTest, RemoveUnusedOnDestroy) { |
| // Make a directory in which we'll work. |
| base::ScopedTempDir work_dir; |
| EXPECT_TRUE(work_dir.CreateUniqueTempDir()); |
| |
| // Make up some path under the temp dir. |
| base::FilePath parent_temp_dir( |
| work_dir.GetPath().Append(L"One").Append(L"Two")); |
| { |
| SelfCleaningTempDir temp_dir; |
| EXPECT_TRUE(temp_dir.Initialize(parent_temp_dir, L"Three")); |
| EXPECT_EQ(parent_temp_dir.Append(L"Three"), temp_dir.path()); |
| EXPECT_TRUE(base::DirectoryExists(temp_dir.path())); |
| } |
| EXPECT_FALSE(base::DirectoryExists(parent_temp_dir.Append(L"Three"))); |
| EXPECT_FALSE(base::DirectoryExists(parent_temp_dir)); |
| EXPECT_FALSE(base::DirectoryExists(parent_temp_dir.DirName())); |
| EXPECT_TRUE(base::DirectoryExists(parent_temp_dir.DirName().DirName())); |
| EXPECT_TRUE(work_dir.Delete()); |
| EXPECT_FALSE(base::DirectoryExists(parent_temp_dir.DirName().DirName())); |
| } |
| |
| // Test that intermediate dirs are left behind if they're not empty when the |
| // destructor is called. |
| TEST_F(SelfCleaningTempDirTest, LeaveUsedOnDestroy) { |
| static const char kHiHon[] = "hi, hon"; |
| |
| // Make a directory in which we'll work. |
| base::ScopedTempDir work_dir; |
| EXPECT_TRUE(work_dir.CreateUniqueTempDir()); |
| |
| // Make up some path under the temp dir. |
| base::FilePath parent_temp_dir( |
| work_dir.GetPath().Append(L"One").Append(L"Two")); |
| { |
| SelfCleaningTempDir temp_dir; |
| EXPECT_TRUE(temp_dir.Initialize(parent_temp_dir, L"Three")); |
| EXPECT_EQ(parent_temp_dir.Append(L"Three"), temp_dir.path()); |
| EXPECT_TRUE(base::DirectoryExists(temp_dir.path())); |
| // Drop a file somewhere. |
| EXPECT_EQ(static_cast<int>(base::size(kHiHon) - 1), |
| base::WriteFile(parent_temp_dir.AppendASCII(GetRandomFilename()), |
| kHiHon, base::size(kHiHon) - 1)); |
| } |
| EXPECT_FALSE(base::DirectoryExists(parent_temp_dir.Append(L"Three"))); |
| EXPECT_TRUE(base::DirectoryExists(parent_temp_dir)); |
| EXPECT_TRUE(work_dir.Delete()); |
| EXPECT_FALSE(base::DirectoryExists(parent_temp_dir.DirName().DirName())); |
| } |
| |
| } // namespace installer |