Files
oemcrypto/util/test/file_store_unittest.cpp
2025-04-02 09:49:07 -07:00

571 lines
22 KiB
C++

// 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 <errno.h>
#include <set>
#include <string>
#include <vector>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#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 = 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 = 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 = 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 = 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<ssize_t>(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 = file_system_.Open(path, FileSystem::kCreate);
ASSERT_TRUE(file) << "Failed to create file: " << path;
constexpr ssize_t kExpectedFileSizeResult = static_cast<ssize_t>(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 = 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<std::string> 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 =
file_system_.Open(file_path, FileSystem::kCreate);
ASSERT_TRUE(file) << "Failed to create file: " << kTestFilename;
file.reset(); // Close file
std::vector<std::string> 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 = 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<std::string> 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 =
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<std::string> 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 =
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<std::string> 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 =
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<std::string> 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