// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary // source code may only be used and distributed under the Widevine License // Agreement. #include "file_store.h" #include #include #include #include #include #include #include "cdm_random.h" #include "test_vectors.h" namespace wvutil { namespace { constexpr char kTestFilename[] = "sample.txt"; constexpr char kTestIdentifier1[] = "some_identifier"; constexpr char kTestIdentifier2[] = "some_other_identifier"; constexpr int kNoError = 0; constexpr int kEntryDoesNotExist = ENOENT; bool StartsWith(const std::string& haystack, const std::string& needle) { if (needle.empty()) return true; if (haystack.size() < needle.size()) return false; return haystack.find(needle) == 0; } } // namespace class FileTest : public testing::Test { protected: FileTest() {} void TearDown() override { RemoveTestDir(); } void RemoveTestDir() { ASSERT_TRUE(file_system_.Remove(wvcdm::test_vectors::kTestDir)) << "Failed to update test directory: " << wvcdm::test_vectors::kTestDir; } std::string PathJoin(const std::string& base_path, const std::string& add_path) { if (base_path.empty()) return add_path; std::string path = base_path; if (path.back() != '/') { path.push_back('/'); } path.append(add_path); return path; } FileSystem file_system_; }; TEST_F(FileTest, FileExists) { int errno_value = -1; EXPECT_TRUE(file_system_.Exists(wvcdm::test_vectors::kExistentFile)) << "path = " << wvcdm::test_vectors::kExistentFile; EXPECT_TRUE( file_system_.Exists(wvcdm::test_vectors::kExistentFile, &errno_value)) << "path = " << wvcdm::test_vectors::kExistentFile; EXPECT_EQ(kNoError, errno_value); } TEST_F(FileTest, FileDoesNotExist) { int errno_value = -1; EXPECT_FALSE(file_system_.Exists(wvcdm::test_vectors::kNonExistentFile)) << "path = " << wvcdm::test_vectors::kNonExistentFile; EXPECT_FALSE( file_system_.Exists(wvcdm::test_vectors::kNonExistentFile, &errno_value)) << "path = " << wvcdm::test_vectors::kNonExistentFile; EXPECT_EQ(kEntryDoesNotExist, errno_value); } TEST_F(FileTest, DirectoryExists) { int errno_value = -1; EXPECT_TRUE(file_system_.Exists(wvcdm::test_vectors::kExistentDir)) << "path = " << wvcdm::test_vectors::kExistentDir; EXPECT_TRUE( file_system_.Exists(wvcdm::test_vectors::kExistentDir, &errno_value)) << "path = " << wvcdm::test_vectors::kExistentDir; EXPECT_EQ(kNoError, errno_value); } TEST_F(FileTest, DirectoryDoesNotExist) { int errno_value = -1; EXPECT_FALSE(file_system_.Exists(wvcdm::test_vectors::kNonExistentDir)) << "path = " << wvcdm::test_vectors::kNonExistentDir; EXPECT_FALSE( file_system_.Exists(wvcdm::test_vectors::kNonExistentDir, &errno_value)) << "path = " << wvcdm::test_vectors::kNonExistentDir; EXPECT_EQ(kEntryDoesNotExist, errno_value); } TEST_F(FileTest, RemoveDir) { EXPECT_TRUE(file_system_.Remove(wvcdm::test_vectors::kTestDir)) << "path = " << wvcdm::test_vectors::kTestDir; EXPECT_FALSE(file_system_.Exists(wvcdm::test_vectors::kTestDir)) << "path = " << wvcdm::test_vectors::kTestDir; } TEST_F(FileTest, OpenFile) { const std::string path = PathJoin(wvcdm::test_vectors::kTestDir, kTestFilename); EXPECT_TRUE(file_system_.Remove(path)) << "path = " << path; std::unique_ptr file = file_system_.Open(path, FileSystem::kCreate); ASSERT_TRUE(file) << "Failed to create file: " << path; EXPECT_TRUE(file_system_.Exists(path)) << "path = " << path; } TEST_F(FileTest, RemoveDirAndFile) { const std::string path = PathJoin(wvcdm::test_vectors::kTestDir, kTestFilename); std::unique_ptr file = file_system_.Open(path, FileSystem::kCreate); ASSERT_TRUE(file) << "Failed to create file: " << path; EXPECT_TRUE(file_system_.Exists(path)) << "path = " << path; EXPECT_TRUE(file_system_.Remove(path)) << "path = " << path; EXPECT_FALSE(file_system_.Exists(path)) << "path = " << path; file = file_system_.Open(path, FileSystem::kCreate); ASSERT_TRUE(file) << "Failed to create file: " << path; EXPECT_TRUE(file_system_.Exists(path)) << "path = " << path; ASSERT_NO_FATAL_FAILURE(RemoveTestDir()); EXPECT_FALSE(file_system_.Exists(wvcdm::test_vectors::kTestDir)) << "path = " << path; EXPECT_FALSE(file_system_.Exists(path)) << "path = " << path; } TEST_F(FileTest, RemoveWildcardFiles) { const std::string path1 = PathJoin(wvcdm::test_vectors::kTestDir, "first.txt"); const std::string path2 = PathJoin(wvcdm::test_vectors::kTestDir, "second.txt"); const std::string wildcard_path = PathJoin(wvcdm::test_vectors::kTestDir, "*.txt"); std::unique_ptr file = file_system_.Open(path1, FileSystem::kCreate); ASSERT_TRUE(file) << "Failed to create file (1): " << path1; file = file_system_.Open(path2, FileSystem::kCreate); ASSERT_TRUE(file) << "Failed to create file (2): " << path2; EXPECT_TRUE(file_system_.Exists(path1)) << "path = " << path1; EXPECT_TRUE(file_system_.Exists(path2)) << "path = " << path2; EXPECT_TRUE(file_system_.Remove(wildcard_path)) << "wildcard_path = " << wildcard_path; EXPECT_FALSE(file_system_.Exists(path1)) << "path = " << path1; EXPECT_FALSE(file_system_.Exists(path2)) << "path = " << path2; } TEST_F(FileTest, FileSize) { const std::string path = PathJoin(wvcdm::test_vectors::kTestDir, kTestFilename); file_system_.Remove(path); constexpr size_t kDataSize = 600; const std::string write_data = CdmRandom::RandomData(kDataSize); ASSERT_EQ(write_data.size(), kDataSize); std::unique_ptr file = file_system_.Open(path, FileSystem::kCreate); ASSERT_TRUE(file) << "Failed to create file: " << path; EXPECT_EQ(file->Write(write_data.data(), write_data.size()), write_data.size()); file.reset(); // Close file. EXPECT_TRUE(file_system_.Exists(path)) << "path = " << path; EXPECT_EQ(static_cast(kDataSize), file_system_.FileSize(path)) << "path = " << path; } TEST_F(FileTest, WriteReadBinaryFile) { const std::string path = PathJoin(wvcdm::test_vectors::kTestDir, kTestFilename); file_system_.Remove(path); constexpr size_t kDataSize = 600; const std::string write_data = CdmRandom::RandomData(kDataSize); ASSERT_EQ(write_data.size(), kDataSize); std::unique_ptr file = file_system_.Open(path, FileSystem::kCreate); ASSERT_TRUE(file) << "Failed to create file: " << path; constexpr ssize_t kExpectedFileSizeResult = static_cast(kDataSize); EXPECT_EQ(file->Write(write_data.data(), write_data.size()), kExpectedFileSizeResult) << "path = " << path; file.reset(); // Close file. EXPECT_TRUE(file_system_.Exists(path)) << "path = " << path; file = file_system_.Open(path, FileSystem::kReadOnly); ASSERT_TRUE(file) << "Failed to re-open file: " << path; std::string read_data(kDataSize, '\0'); ; ASSERT_EQ(file->Read(&read_data[0], read_data.size()), kExpectedFileSizeResult) << "path = " << path; EXPECT_EQ(write_data, read_data); } TEST_F(FileTest, ListFiles) { const std::string kTxtFilename1 = "data.txt"; const std::string kTxtFilename2 = "other.txt"; const std::string kBinFilename = "sample.bin"; const std::string dir_path = wvcdm::test_vectors::kTestDir; const std::string path1 = PathJoin(dir_path, kTxtFilename1); const std::string path2 = PathJoin(dir_path, kTxtFilename2); const std::string path3 = PathJoin(dir_path, kBinFilename); // Create files. std::unique_ptr file = file_system_.Open(path1, FileSystem::kCreate); ASSERT_TRUE(file) << "Failed to create file (1): " << path1; file = file_system_.Open(path2, FileSystem::kCreate); ASSERT_TRUE(file) << "Failed to create file (2): " << path2; file = file_system_.Open(path3, FileSystem::kCreate); ASSERT_TRUE(file) << "Failed to create file (3): " << path3; file.reset(); // Close file EXPECT_TRUE(file_system_.Exists(path1)); EXPECT_TRUE(file_system_.Exists(path2)); EXPECT_TRUE(file_system_.Exists(path3)); std::vector names; ASSERT_TRUE(file_system_.List(dir_path, &names)) << "dir_path = " << dir_path; size_t expected_file_count = 3; EXPECT_EQ(names.size(), expected_file_count); // Should find the three files. Order not important. EXPECT_THAT(names, ::testing::UnorderedElementsAre( kTxtFilename1, kTxtFilename2, kBinFilename)); // Remove .txt files. const std::string txt_wildcard_path = PathJoin(dir_path, "*.txt"); EXPECT_TRUE(file_system_.Remove(txt_wildcard_path)) << "txt_wildcard_path = " << txt_wildcard_path; EXPECT_TRUE(file_system_.List(dir_path, &names)) << "dir_path = " << dir_path; expected_file_count = 1; ASSERT_EQ(names.size(), expected_file_count); EXPECT_EQ(names.front(), kBinFilename); const std::string bin_wildcard_path = PathJoin(dir_path, "*.bin"); EXPECT_TRUE(file_system_.Remove(bin_wildcard_path)) << "bin_wildcard_path = " << bin_wildcard_path; // All files should be removed, but listing should still succeed. EXPECT_TRUE(file_system_.List(dir_path, &names)) << "dir_path = " << dir_path; expected_file_count = 0; EXPECT_EQ(expected_file_count, names.size()); } TEST_F(FileTest, ListFiles_NotAPath) { const std::string kTestFilename = "zzz.txt"; const std::string dir_path = wvcdm::test_vectors::kTestDir; const std::string file_path = PathJoin(dir_path, kTestFilename); std::unique_ptr file = file_system_.Open(file_path, FileSystem::kCreate); ASSERT_TRUE(file) << "Failed to create file: " << kTestFilename; file.reset(); // Close file std::vector names; // Ask for non-existent path. EXPECT_FALSE(file_system_.List(file_path, &names)); } TEST_F(FileTest, ListFiles_NullParameter) { const std::string dir_path = wvcdm::test_vectors::kTestDir; const std::string path = PathJoin(dir_path, kTestFilename); std::unique_ptr file = file_system_.Open(path, FileSystem::kCreate); ASSERT_TRUE(file) << "Failed to create file: " << path; file.reset(); // Close file. // Valid path, but no way to return names. EXPECT_FALSE(file_system_.List(dir_path, nullptr)); } // On certain platforms, the FileSystem may perform special // name translations on certificate filenames which make them behave // differently from non-certificate filenames. TEST_F(FileTest, CreateGlobalCertificates) { ASSERT_TRUE(file_system_.IsGlobal()) << "Test case requires global file system"; const std::string dir_path = wvcdm::test_vectors::kTestDir; // Clear directory const std::string all_file_wildcard_path = PathJoin(dir_path, "*"); file_system_.Remove(all_file_wildcard_path); // Ensure directory is empty. std::vector names; if (file_system_.List(dir_path, &names)) { constexpr size_t kZero = 0; ASSERT_EQ(kZero, names.size()) << "Test requires empty directory"; } const std::string certificate_path = PathJoin(dir_path, kCertificateFileName); const std::string legacy_certificate_path = PathJoin(dir_path, kLegacyCertificateFileName); // Create certificates and verify that they exist std::unique_ptr file = file_system_.Open(certificate_path, FileSystem::kCreate); ASSERT_TRUE(file) << "Failed to create certificate file: " << certificate_path; file = file_system_.Open(legacy_certificate_path, FileSystem::kCreate); ASSERT_TRUE(file) << "Failed to create legacy certificate file: " << legacy_certificate_path; file.reset(); // Close file. EXPECT_TRUE(file_system_.Exists(certificate_path)) << "certificate_path = " << certificate_path; EXPECT_TRUE(file_system_.Exists(legacy_certificate_path)) << "legacy_certificate_path = " << legacy_certificate_path; ASSERT_TRUE(file_system_.List(dir_path, &names)); // Should find two files. Order not important. constexpr size_t kExpectedCount = 2; EXPECT_EQ(kExpectedCount, names.size()); EXPECT_THAT(names, ::testing::UnorderedElementsAre( kCertificateFileName, kLegacyCertificateFileName)); } // On certain platforms, the FileSystem may perform special // name translations on certificate filenames which make them behave // differently from non-certificate filenames. TEST_F(FileTest, CreateCertificates) { ASSERT_TRUE(file_system_.IsGlobal()) << "Test case requires starting with a global file system"; const std::string dir_path = wvcdm::test_vectors::kTestDir; // Clear directory const std::string all_file_wildcard_path = PathJoin(dir_path, "*"); file_system_.Remove(all_file_wildcard_path); // Ensure directory is empty. std::vector names; if (file_system_.List(dir_path, &names)) { constexpr size_t kZero = 0; ASSERT_EQ(kZero, names.size()) << "Test requires empty directory"; } const std::string certificate_path = PathJoin(dir_path, kCertificateFileName); const std::string legacy_certificate_path = PathJoin(dir_path, kLegacyCertificateFileName); // Create Global certificates. std::unique_ptr file = file_system_.Open(certificate_path, FileSystem::kCreate); ASSERT_TRUE(file) << "Failed to create global certificate: " << certificate_path; file = file_system_.Open(legacy_certificate_path, FileSystem::kCreate); ASSERT_TRUE(file) << "Failed to create global legacy certificate: " << legacy_certificate_path; file.reset(); // Close file. EXPECT_TRUE(file_system_.Exists(certificate_path)) << "Global certificate: " << certificate_path; EXPECT_TRUE(file_system_.Exists(legacy_certificate_path)) << "Global legacy certificate: " << legacy_certificate_path; // Switch to first identifier. file_system_.set_identifier(kTestIdentifier1); ASSERT_FALSE(file_system_.IsGlobal()) << "identifier = " << kTestIdentifier1; // Global certificates should not be visible once identifier has been // specified. EXPECT_FALSE(file_system_.Exists(certificate_path)) << kTestIdentifier1 << " certificate: " << certificate_path; EXPECT_FALSE(file_system_.Exists(legacy_certificate_path)) << kTestIdentifier1 << " legacy certificate: " << legacy_certificate_path; // Create certificates with first identifier file = file_system_.Open(certificate_path, FileSystem::kCreate); ASSERT_TRUE(file) << "Failed to create " << kTestIdentifier1 << " certificate: " << certificate_path; file = file_system_.Open(legacy_certificate_path, FileSystem::kCreate); ASSERT_TRUE(file) << "Failed to create " << kTestIdentifier1 << " legacy certificate: " << legacy_certificate_path; file.reset(); // Close file. // Verify they now exist. EXPECT_TRUE(file_system_.Exists(certificate_path)) << kTestIdentifier1 << " certificate: " << certificate_path; EXPECT_TRUE(file_system_.Exists(legacy_certificate_path)) << kTestIdentifier1 << " legacy certificate: " << legacy_certificate_path; // Switch to second identifier. file_system_.set_identifier(kTestIdentifier2); ASSERT_FALSE(file_system_.IsGlobal()) << "identifier = " << kTestIdentifier2; // Global and first identifier certificates should not be // visible. EXPECT_FALSE(file_system_.Exists(certificate_path)) << kTestIdentifier2 << " certificate: " << certificate_path; EXPECT_FALSE(file_system_.Exists(legacy_certificate_path)) << kTestIdentifier2 << " legacy certificate: " << legacy_certificate_path; // Create certificates with second identifier file = file_system_.Open(certificate_path, FileSystem::kCreate); ASSERT_TRUE(file) << "Failed to create " << kTestIdentifier2 << " certificate: " << certificate_path; file = file_system_.Open(legacy_certificate_path, FileSystem::kCreate); ASSERT_TRUE(file) << "Failed to create " << kTestIdentifier2 << " legacy certificate: " << legacy_certificate_path; file.reset(); // Close file. // Verify they now exist. EXPECT_TRUE(file_system_.Exists(certificate_path)) << kTestIdentifier2 << " certificate: " << certificate_path; EXPECT_TRUE(file_system_.Exists(legacy_certificate_path)) << kTestIdentifier2 << " legacy certificate: " << legacy_certificate_path; // FileSystem::List is expected to still return all certificate files // (both global and scoped). ASSERT_TRUE(file_system_.List(dir_path, &names)) << "dir_path = " << dir_path; // Should find six files. Order not important. constexpr size_t kExpectedTotalCertCount = 6; ASSERT_EQ(names.size(), kExpectedTotalCertCount); bool is_global_certificate_present = false; bool is_global_legacy_certificate_present = false; size_t certificate_count = 0; size_t legacy_certificate_count = 0; for (const auto& filename : names) { if (filename == kLegacyCertificateFileName) { is_global_legacy_certificate_present = true; } else if (filename == kCertificateFileName) { is_global_certificate_present = true; } else if (StartsWith(filename, kScopedCertificateFilenamePrefix)) { certificate_count++; } else if (StartsWith(filename, kLegacyScopedCertificateFilenamePrefix)) { legacy_certificate_count++; } else { ADD_FAILURE() << "Unexpected filename: " << filename; } } constexpr size_t kExpectedScopedCertCount = 2; EXPECT_EQ(certificate_count, kExpectedScopedCertCount) << "Missing certificates"; EXPECT_EQ(legacy_certificate_count, kExpectedScopedCertCount) << "Missing legacy certificates"; EXPECT_TRUE(is_global_certificate_present) << "Missing global certificate: " << kCertificateFileName; EXPECT_TRUE(is_global_legacy_certificate_present) << "Missing legacy global certificate: " << kLegacyCertificateFileName; } // On certain platforms, the FileSystem may perform special // name translations on certificate file names which make them behave // differently from non-certificate file names. TEST_F(FileTest, RemoveCertificates) { ASSERT_TRUE(file_system_.IsGlobal()) << "Test case requires starting with a global file system"; const std::string dir_path = wvcdm::test_vectors::kTestDir; // Clear directory const std::string all_file_wildcard_path = PathJoin(dir_path, "*"); file_system_.Remove(all_file_wildcard_path); // Ensure directory is empty. std::vector names; if (file_system_.List(dir_path, &names)) { constexpr size_t kZero = 0; ASSERT_EQ(kZero, names.size()) << "Test requires empty directory"; } const std::string certificate_path = PathJoin(dir_path, kCertificateFileName); const std::string legacy_certificate_path = PathJoin(dir_path, kLegacyCertificateFileName); // Create Global certificates. std::unique_ptr file = file_system_.Open(certificate_path, FileSystem::kCreate); ASSERT_TRUE(file) << "Failed to create global certificate: " << certificate_path; file = file_system_.Open(legacy_certificate_path, FileSystem::kCreate); ASSERT_TRUE(file) << "Failed to create global legacy certificate: " << legacy_certificate_path; file.reset(); // Close file. // Switch to first identifier. file_system_.set_identifier(kTestIdentifier1); ASSERT_FALSE(file_system_.IsGlobal()) << "identifier = " << kTestIdentifier1; // Create certificates with first identifier file = file_system_.Open(certificate_path, FileSystem::kCreate); ASSERT_TRUE(file) << "Failed to create " << kTestIdentifier1 << " certificate: " << certificate_path; file = file_system_.Open(legacy_certificate_path, FileSystem::kCreate); ASSERT_TRUE(file) << "Failed to create " << kTestIdentifier1 << " legacy certificate: " << legacy_certificate_path; file.reset(); // Close file. // Switch to second identifier. file_system_.set_identifier(kTestIdentifier2); ASSERT_FALSE(file_system_.IsGlobal()) << "identifier = " << kTestIdentifier2; // Create certificates with second identifier file = file_system_.Open(certificate_path, FileSystem::kCreate); ASSERT_TRUE(file) << "Failed to create " << kTestIdentifier2 << " certificate: " << certificate_path; file = file_system_.Open(legacy_certificate_path, FileSystem::kCreate); ASSERT_TRUE(file) << "Failed to create " << kTestIdentifier2 << " legacy certificate: " << legacy_certificate_path; file.reset(); // Close file. // FileSystem::List is expected to still return all certificate files // (both global and scoped). ASSERT_TRUE(file_system_.List(dir_path, &names)) << "dir_path = " << dir_path; // Should find six files. Order not important. constexpr size_t kExpectedTotalCertCount = 6; ASSERT_EQ(names.size(), kExpectedTotalCertCount); std::set removed_certs; // Remove all even number listed files for (size_t i = 0; i < names.size(); ++i) { if ((i % 2) != 0) continue; const std::string& cert_filename = names[i]; const std::string cert_path = PathJoin(dir_path, cert_filename); ASSERT_TRUE(file_system_.Remove(cert_path)) << "Failed to remove cert: " << cert_path; removed_certs.insert(cert_filename); } // Verify that they have been removed for (const std::string& cert_filename : names) { const std::string cert_path = PathJoin(dir_path, cert_filename); if (removed_certs.find(cert_filename) == removed_certs.end()) { // Ensure still exists. ASSERT_TRUE(file_system_.Exists(cert_path)) << "Cert missing: " << cert_filename; } else { ASSERT_FALSE(file_system_.Exists(cert_path)) << "Cert not removed: " << cert_filename; } } // Remove all remaining. for (const std::string& cert_filename : names) { if (removed_certs.find(cert_filename) != removed_certs.end()) continue; const std::string cert_path = PathJoin(dir_path, cert_filename); ASSERT_TRUE(file_system_.Remove(cert_path)) << "Failed to remove cert: " << cert_path; } // Verify that all have been removed for (const std::string& cert_filename : names) { const std::string cert_path = PathJoin(dir_path, cert_filename); EXPECT_FALSE(file_system_.Exists(cert_path)) << "Cert not removed: " << cert_filename; } } } // namespace wvutil