Files
android/libwvdrmengine/cdm/util/test/file_store_unittest.cpp
Alex Dale 39c12039cb Patch Android FileSystem::List for non-existing directory.
[ Merge of http://go/wvgerrit/210651 ]

The Android FileSystem implementation for List() would return an error
if the directory does not exist.  This creates an issue for the case
where the CDM attempts to list offline licenses after clearing all
data.  This typically won't effect a regular user, it causes
integration tests which re-provision to fail.

Bug: 372105842
Test: file_store_unittest on Oriole
Change-Id: I121b52ab95e36249ae5b196e987bc950a278131f
2024-11-21 14:41:20 -08: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