blob: a48bbc9e81b6e2e8d30ea02557ad92d48198c95e [file] [log] [blame]
// Copyright 2016 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// How to build and run the tests:
//
// chroot$ cros_run_unit_tests --board=$BOARD --packages arc-setup
//
// Note: only x86 $BOARDs like cyan are supported.
#include "arc/setup/arc_setup_util.h"
#include <fcntl.h>
#include <ifaddrs.h>
#include <inttypes.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <limits>
#include <base/base64.h>
#include <base/bind.h>
#include <base/environment.h>
#include <base/files/file_enumerator.h>
#include <base/files/file_path.h>
#include <base/files/file_util.h>
#include <base/files/scoped_temp_dir.h>
#include <base/rand_util.h>
#include <base/strings/string_util.h>
#include <base/strings/stringprintf.h>
#include <base/time/time.h>
#include <base/timer/elapsed_timer.h>
#include <brillo/file_utils.h>
#include <brillo/files/safe_fd.h>
#include <gtest/gtest.h>
namespace arc {
namespace {
bool FindLineCallback(std::string* out_prop, const std::string& line) {
if (line != "string_to_find")
return false;
*out_prop = "FOUND";
return true;
}
constexpr char kTestProperitesFromFileContent[] =
""
"# begin build properties\n"
"\n"
"ro.build.version.sdk=25\n"
"ro.product.board=board\n"
"ro.build.fingerprint=fingerprint\n";
constexpr char kTestProperitesFromFileContentBad[] =
""
"# begin build properties\n"
"\n"
"ro.build.version.sdk=25\n"
"ro.product.board board\n"; // no '=' separator
} // namespace
TEST(ArcSetupUtil, TestCreateOrTruncate) {
base::ScopedTempDir temp_directory;
ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
// Create a new empty file.
EXPECT_TRUE(CreateOrTruncate(temp_directory.GetPath().Append("file"), 0777));
// Confirm that the mode of the file is 0777.
int mode = 0;
EXPECT_TRUE(base::GetPosixFilePermissions(
temp_directory.GetPath().Append("file"), &mode));
EXPECT_EQ(0777, mode);
// Confirm that the size of the file is 0.
int64_t size = -1;
EXPECT_TRUE(
base::GetFileSize(temp_directory.GetPath().Append("file"), &size));
EXPECT_EQ(0, size);
// Make the file non-empty.
EXPECT_TRUE(
WriteToFile(temp_directory.GetPath().Append("file"), 0777, "abc"));
EXPECT_TRUE(
base::GetFileSize(temp_directory.GetPath().Append("file"), &size));
EXPECT_EQ(3, size);
// Call the API again with a different mode.
EXPECT_TRUE(CreateOrTruncate(temp_directory.GetPath().Append("file"), 0700));
// Confirm that the mode of the file is now 0700.
mode = 0;
EXPECT_TRUE(base::GetPosixFilePermissions(
temp_directory.GetPath().Append("file"), &mode));
EXPECT_EQ(0700, mode);
// Confirm that the size of the file is still 0.
size = -1;
EXPECT_TRUE(
base::GetFileSize(temp_directory.GetPath().Append("file"), &size));
EXPECT_EQ(0, size);
}
TEST(ArcSetupUtil, TestWaitForPaths) {
constexpr base::TimeDelta timeout = base::TimeDelta::FromSeconds(1);
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
base::ScopedTempDir temp_dir2;
ASSERT_TRUE(temp_dir2.CreateUniqueTempDir());
// Confirm that when the first argument is empty, it returns true.
// Also confirm that the third argument can be nullptr.
EXPECT_TRUE(WaitForPaths({}, timeout, nullptr));
// Confirm that the function can handle one path.
base::TimeDelta elapsed;
EXPECT_TRUE(WaitForPaths({temp_dir.GetPath()}, timeout, &elapsed));
EXPECT_GT(elapsed, base::TimeDelta());
// Strictly speaking, WaitForPaths does not guarantee this, but in practice,
// this check passes.
EXPECT_LE(elapsed, timeout);
elapsed = base::TimeDelta();
// Does the same with a nonexistent file.
EXPECT_FALSE(WaitForPaths({temp_dir.GetPath().Append("nonexistent")}, timeout,
&elapsed));
EXPECT_GT(elapsed, timeout);
elapsed = base::TimeDelta();
// Confirm that the function can handle two paths.
EXPECT_TRUE(WaitForPaths({temp_dir.GetPath(), temp_dir2.GetPath()}, timeout,
&elapsed));
EXPECT_GT(elapsed, base::TimeDelta());
EXPECT_LE(elapsed, timeout); // same
elapsed = base::TimeDelta();
EXPECT_FALSE(WaitForPaths(
{temp_dir.GetPath().Append("nonexistent"), temp_dir2.GetPath()}, timeout,
&elapsed));
EXPECT_GT(elapsed, timeout);
elapsed = base::TimeDelta();
EXPECT_FALSE(WaitForPaths(
{temp_dir.GetPath(), temp_dir2.GetPath().Append("nonexistent")}, timeout,
&elapsed));
EXPECT_GT(elapsed, timeout);
elapsed = base::TimeDelta();
EXPECT_FALSE(WaitForPaths({temp_dir.GetPath().Append("nonexistent"),
temp_dir2.GetPath().Append("nonexistent")},
timeout, &elapsed));
EXPECT_GT(elapsed, timeout);
}
TEST(ArcSetupUtil, TestWriteToFile) {
base::ScopedTempDir temp_directory;
ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
// Create a non-empty file.
EXPECT_TRUE(
WriteToFile(temp_directory.GetPath().Append("file"), 0700, "abcde"));
// Confirm that the mode of the file is now 0700.
int mode = 0;
EXPECT_TRUE(base::GetPosixFilePermissions(
temp_directory.GetPath().Append("file"), &mode));
EXPECT_EQ(0700, mode);
// Confirm that the size of the file is still 0.
int64_t size = -1;
EXPECT_TRUE(
base::GetFileSize(temp_directory.GetPath().Append("file"), &size));
EXPECT_EQ(5, size);
// Call the API again with a different mode and content.
EXPECT_TRUE(
WriteToFile(temp_directory.GetPath().Append("file"), 0777, "xyz"));
// Confirm that the mode of the file is now 0700.
mode = 0;
EXPECT_TRUE(base::GetPosixFilePermissions(
temp_directory.GetPath().Append("file"), &mode));
EXPECT_EQ(0777, mode);
// Confirm that the size of the file is still 0.
size = -1;
EXPECT_TRUE(
base::GetFileSize(temp_directory.GetPath().Append("file"), &size));
EXPECT_EQ(3, size);
}
TEST(ArcSetupUtil, TestWriteToFileWithSymlink) {
base::ScopedTempDir temp_directory;
ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
base::ScopedTempDir temp_directory2;
ASSERT_TRUE(temp_directory2.CreateUniqueTempDir());
const base::FilePath symlink = temp_directory.GetPath().Append("symlink");
ASSERT_TRUE(base::CreateSymbolicLink(temp_directory2.GetPath(), symlink));
// WriteToFile should fail when the path points to a symlink.
EXPECT_FALSE(WriteToFile(symlink, 0777, "abc"));
// WriteToFile should also fail when a path component in the middle is a
// symlink.
const base::FilePath path_with_symlink = symlink.Append("not-a-symlink");
EXPECT_FALSE(WriteToFile(path_with_symlink, 0777, "abcde"));
}
TEST(ArcSetupUtil, TestWriteToFileWithFifo) {
base::ScopedTempDir temp_directory;
ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
const base::FilePath fifo = temp_directory.GetPath().Append("fifo");
ASSERT_EQ(0, mkfifo(fifo.value().c_str(), 0700));
// WriteToFile should fail when the path points to a fifo.
EXPECT_FALSE(WriteToFile(fifo, 0777, "abc"));
}
TEST(ArcSetupUtil, TestGetPropertyFromFile) {
base::ScopedTempDir temp_directory;
ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
base::FilePath prop_file = temp_directory.GetPath().Append("test.prop");
// Create a new prop file and read it.
ASSERT_TRUE(WriteToFile(prop_file, 0700, "key=val"));
std::string v;
EXPECT_TRUE(GetPropertyFromFile(prop_file, "key", &v));
EXPECT_EQ("val", v);
EXPECT_FALSE(GetPropertyFromFile(prop_file, "k", &v));
EXPECT_FALSE(GetPropertyFromFile(prop_file, "ke", &v));
EXPECT_FALSE(GetPropertyFromFile(prop_file, "keyX", &v));
// Retry with an empty file.
ASSERT_TRUE(WriteToFile(prop_file, 0700, ""));
EXPECT_FALSE(GetPropertyFromFile(prop_file, "", &v));
EXPECT_FALSE(GetPropertyFromFile(prop_file, "key", &v));
// Retry with a multi-line file.
ASSERT_TRUE(WriteToFile(prop_file, 0700, "k1=v1\nk2=v2\nk3=v3"));
EXPECT_TRUE(GetPropertyFromFile(prop_file, "k1", &v));
EXPECT_EQ("v1", v);
EXPECT_TRUE(GetPropertyFromFile(prop_file, "k2", &v));
EXPECT_EQ("v2", v);
EXPECT_TRUE(GetPropertyFromFile(prop_file, "k3", &v));
EXPECT_EQ("v3", v);
EXPECT_FALSE(GetPropertyFromFile(prop_file, "", &v));
EXPECT_FALSE(GetPropertyFromFile(prop_file, "=", &v));
EXPECT_FALSE(GetPropertyFromFile(prop_file, "1", &v));
EXPECT_FALSE(GetPropertyFromFile(prop_file, "k", &v));
EXPECT_FALSE(GetPropertyFromFile(prop_file, "k4", &v));
}
TEST(ArcSetupUtil, TestGetPropertiesFromFile) {
base::ScopedTempDir temp_directory;
ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
base::FilePath prop_file = temp_directory.GetPath().Append("test.prop");
// Create a new prop file and read it.
ASSERT_TRUE(WriteToFile(prop_file, 0700, kTestProperitesFromFileContent));
std::map<std::string, std::string> properties;
EXPECT_TRUE(GetPropertiesFromFile(prop_file, &properties));
EXPECT_EQ(3U, properties.size());
EXPECT_EQ("25", properties["ro.build.version.sdk"]);
EXPECT_EQ("board", properties["ro.product.board"]);
EXPECT_EQ("fingerprint", properties["ro.build.fingerprint"]);
}
TEST(ArcSetupUtil, TestGetPropertiesFromFileBad) {
base::ScopedTempDir temp_directory;
ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
base::FilePath prop_file = temp_directory.GetPath().Append("test.prop");
// Create a new prop file and read it.
ASSERT_TRUE(WriteToFile(prop_file, 0700, kTestProperitesFromFileContentBad));
std::map<std::string, std::string> properties;
EXPECT_FALSE(GetPropertiesFromFile(prop_file, &properties));
EXPECT_TRUE(properties.empty());
}
TEST(ArcSetupUtil, TestGetFingerprintAndSdkVersionFromPackagesXml) {
base::ScopedTempDir temp_directory;
ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
base::FilePath packages_file =
temp_directory.GetPath().Append("packages.xml");
// Create a new file and read it.
ASSERT_TRUE(WriteToFile(
packages_file, 0700,
"<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
"<packages>\n"
" <version sdkVersion=\"25\" databaseVersion=\"3\" fingerprint=\"f1\">\n"
" <version volumeUuid=\"primary_physical\" "
"sdkVersion=\"25\" databaseVersion=\"25\" fingerprint=\"f2\">\n"
"</packages>"));
std::string fingerprint;
std::string sdk_version;
EXPECT_TRUE(GetFingerprintAndSdkVersionFromPackagesXml(
packages_file, &fingerprint, &sdk_version));
EXPECT_EQ("f1", fingerprint);
EXPECT_EQ("25", sdk_version);
ASSERT_TRUE(WriteToFile(
packages_file, 0700,
// Reverse the order of the version elements.
"<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
"<packages>\n"
" <version volumeUuid=\"primary_physical\" "
"sdkVersion=\"25\" databaseVersion=\"25\" fingerprint=\"f2\">\n"
" <version sdkVersion=\"25\" databaseVersion=\"3\" fingerprint=\"f1\">\n"
"</packages>"));
fingerprint.clear();
sdk_version.clear();
EXPECT_TRUE(GetFingerprintAndSdkVersionFromPackagesXml(
packages_file, &fingerprint, &sdk_version));
EXPECT_EQ("f1", fingerprint);
EXPECT_EQ("25", sdk_version);
// Test invalid <version>s.
ASSERT_TRUE(WriteToFile(
packages_file, 0700,
// "external" version only.
"<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
"<packages>\n"
" <version volumeUuid=\"primary_physical\" "
"sdkVersion=\"25\" databaseVersion=\"25\" fingerprint=\"f2\">\n"
"</packages>"));
EXPECT_FALSE(GetFingerprintAndSdkVersionFromPackagesXml(
packages_file, &fingerprint, &sdk_version));
ASSERT_TRUE(
WriteToFile(packages_file, 0700,
// No sdkVersion.
"<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
"<packages>\n"
" <version databaseVersion=\"3\" fingerprint=\"f1\">\n"
"</packages>"));
EXPECT_FALSE(GetFingerprintAndSdkVersionFromPackagesXml(
packages_file, &fingerprint, &sdk_version));
ASSERT_TRUE(
WriteToFile(packages_file, 0700,
// No databaseVersion.
"<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
"<packages>\n"
" <version sdkVersion=\"25\" fingerprint=\"f1\">\n"
"</packages>"));
EXPECT_FALSE(GetFingerprintAndSdkVersionFromPackagesXml(
packages_file, &fingerprint, &sdk_version));
ASSERT_TRUE(
WriteToFile(packages_file, 0700,
// No fingerprint.
"<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
"<packages>\n"
" <version sdkVersion=\"25\" databaseVersion=\"3\">\n"
"</packages>"));
EXPECT_FALSE(GetFingerprintAndSdkVersionFromPackagesXml(
packages_file, &fingerprint, &sdk_version));
ASSERT_TRUE(WriteToFile(
packages_file, 0700,
// No valid fingerprint.
"<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
"<packages>\n"
" <version sdkVersion=\"25\" databaseVersion=\"3\" fingerprint=\"X>\n"
"</packages>"));
EXPECT_FALSE(GetFingerprintAndSdkVersionFromPackagesXml(
packages_file, &fingerprint, &sdk_version));
ASSERT_TRUE(
WriteToFile(packages_file, 0700,
// No <version> elements.
"<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
"<packages/>\n"));
EXPECT_FALSE(GetFingerprintAndSdkVersionFromPackagesXml(
packages_file, &fingerprint, &sdk_version));
ASSERT_TRUE(WriteToFile(packages_file, 0700,
// Empty file.
""));
EXPECT_FALSE(GetFingerprintAndSdkVersionFromPackagesXml(
packages_file, &fingerprint, &sdk_version));
}
TEST(ArcSetupUtil, TestFindLine) {
base::ScopedTempDir temp_directory;
ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
base::FilePath file = temp_directory.GetPath().Append("test.file");
// Create a new prop file and read it.
ASSERT_TRUE(WriteToFile(file, 0700, "string_to_find"));
std::string v;
EXPECT_TRUE(FindLine(file, base::Bind(&FindLineCallback, &v)));
EXPECT_EQ("FOUND", v);
// Test with multi-line files.
v.clear();
ASSERT_TRUE(WriteToFile(file, 0700, "string_to_find\nline"));
EXPECT_TRUE(FindLine(file, base::Bind(&FindLineCallback, &v)));
EXPECT_EQ("FOUND", v);
v.clear();
ASSERT_TRUE(WriteToFile(file, 0700, "line\nstring_to_find\nline"));
EXPECT_TRUE(FindLine(file, base::Bind(&FindLineCallback, &v)));
EXPECT_EQ("FOUND", v);
v.clear();
ASSERT_TRUE(WriteToFile(file, 0700, "line\nstring_to_find"));
EXPECT_TRUE(FindLine(file, base::Bind(&FindLineCallback, &v)));
EXPECT_EQ("FOUND", v);
v.clear();
ASSERT_TRUE(WriteToFile(file, 0700, "line\nstring_to_find\n"));
EXPECT_TRUE(FindLine(file, base::Bind(&FindLineCallback, &v)));
EXPECT_EQ("FOUND", v);
// Test without the target string.
ASSERT_TRUE(WriteToFile(file, 0700, "string_to_findX"));
EXPECT_FALSE(FindLine(file, base::Bind(&FindLineCallback, &v)));
ASSERT_TRUE(WriteToFile(file, 0700, "string_to_fin"));
EXPECT_FALSE(FindLine(file, base::Bind(&FindLineCallback, &v)));
ASSERT_TRUE(WriteToFile(file, 0700, "string_to_fin\nd"));
EXPECT_FALSE(FindLine(file, base::Bind(&FindLineCallback, &v)));
ASSERT_TRUE(WriteToFile(file, 0700, "s\ntring_to_find"));
EXPECT_FALSE(FindLine(file, base::Bind(&FindLineCallback, &v)));
ASSERT_TRUE(WriteToFile(file, 0700, ""));
EXPECT_FALSE(FindLine(file, base::Bind(&FindLineCallback, &v)));
}
TEST(ArcSetupUtil, TestInstallDirectory) {
base::ScopedTempDir temp_directory;
ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
// Set |temp_directory| to 0707.
EXPECT_TRUE(base::SetPosixFilePermissions(temp_directory.GetPath(), 0707));
// Create a new directory.
EXPECT_TRUE(InstallDirectory(0777, getuid(), getgid(),
temp_directory.GetPath().Append("sub1/sub2")));
// Confirm that the mode for sub2 is 0777.
int mode_sub2 = 0;
EXPECT_TRUE(base::GetPosixFilePermissions(
temp_directory.GetPath().Append("sub1/sub2"), &mode_sub2));
EXPECT_EQ(0777, mode_sub2);
// Confirm that the mode for sub1 is NOT 0777 but the secure default, 0755.
int mode_sub1 = 0;
EXPECT_TRUE(base::GetPosixFilePermissions(
temp_directory.GetPath().Append("sub1"), &mode_sub1));
EXPECT_EQ(0755, mode_sub1);
// Confirm that the existing directory |temp_directory| still has 0707 mode.
int mode = 0;
EXPECT_TRUE(base::GetPosixFilePermissions(temp_directory.GetPath(), &mode));
EXPECT_EQ(0707, mode);
// Call InstallDirectory again with the same path but a different mode, 01700.
EXPECT_TRUE(InstallDirectory(0700 | S_ISVTX, getuid(), getgid(),
temp_directory.GetPath().Append("sub1/sub2")));
// Confirm that the mode for sub2 is now 01700.
struct stat st;
EXPECT_EQ(
0,
stat(temp_directory.GetPath().Append("sub1/sub2").value().c_str(), &st));
EXPECT_EQ(0700 | S_ISVTX, st.st_mode & ~S_IFMT);
mode_sub2 = 0;
EXPECT_TRUE(base::GetPosixFilePermissions(
temp_directory.GetPath().Append("sub1/sub2"), &mode_sub2));
EXPECT_EQ(0700, mode_sub2); // base's function masks the mode with 0777.
// Confirm that the mode for sub1 is still 0755.
mode_sub1 = 0;
EXPECT_TRUE(base::GetPosixFilePermissions(
temp_directory.GetPath().Append("sub1"), &mode_sub1));
EXPECT_EQ(0755, mode_sub1);
// Confirm that the existing directory |temp_directory| still has 0707 mode.
mode = 0;
EXPECT_TRUE(base::GetPosixFilePermissions(temp_directory.GetPath(), &mode));
EXPECT_EQ(0707, mode);
}
TEST(ArcSetupUtil, TestInstallDirectoryWithSymlink) {
base::ScopedTempDir temp_directory;
ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
base::ScopedTempDir temp_directory2;
ASSERT_TRUE(temp_directory2.CreateUniqueTempDir());
const base::FilePath symlink = temp_directory.GetPath().Append("symlink");
ASSERT_TRUE(base::CreateSymbolicLink(temp_directory2.GetPath(), symlink));
// InstallDirectory should fail when the path points to a symlink.
EXPECT_FALSE(InstallDirectory(0777, getuid(), getgid(), symlink));
// InstallDirectory should also fail when a path component in the middle
// is a symlink.
const base::FilePath path_with_symlink = symlink.Append("not-a-symlink");
EXPECT_FALSE(InstallDirectory(0777, getuid(), getgid(), path_with_symlink));
}
TEST(ArcSetupUtil, TestInstallDirectoryWithFifo) {
base::ScopedTempDir temp_directory;
ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
const base::FilePath fifo = temp_directory.GetPath().Append("fifo");
ASSERT_EQ(0, mkfifo(fifo.value().c_str(), 0700));
// InstallDirectory should fail when the path points to a fifo.
EXPECT_FALSE(InstallDirectory(0777, getuid(), getgid(), fifo));
}
TEST(ArcSetupUtil, TestDeleteFilesInDir) {
base::ScopedTempDir directory;
ASSERT_TRUE(directory.CreateUniqueTempDir());
ASSERT_TRUE(brillo::MkdirRecursively(directory.GetPath().Append("arm"), 0755)
.is_valid());
ASSERT_TRUE(
brillo::MkdirRecursively(directory.GetPath().Append("arm64"), 0755)
.is_valid());
ASSERT_TRUE(CreateOrTruncate(
directory.GetPath().Append("arm/system@framework@boot.art"), 0755));
ASSERT_TRUE(CreateOrTruncate(
directory.GetPath().Append("arm64/system@framework@boot.art"), 0755));
EXPECT_TRUE(base::PathExists(
directory.GetPath().Append("arm/system@framework@boot.art")));
EXPECT_TRUE(base::PathExists(
directory.GetPath().Append("arm/system@framework@boot.art")));
EXPECT_TRUE(arc::DeleteFilesInDir(directory.GetPath()));
EXPECT_TRUE(base::PathExists(directory.GetPath().Append("arm")));
EXPECT_TRUE(base::PathExists(directory.GetPath().Append("arm64")));
EXPECT_FALSE(base::PathExists(
directory.GetPath().Append("arm/system@framework@boot.art")));
EXPECT_FALSE(base::PathExists(
directory.GetPath().Append("arm/system@framework@boot.art")));
}
TEST(ArcSetupUtil, TestLaunchAndWait) {
base::ElapsedTimer timer;
// Check that LaunchAndWait actually blocks until sleep returns.
EXPECT_TRUE(LaunchAndWait({"/usr/bin/sleep", "1"}));
EXPECT_LE(1, timer.Elapsed().InSeconds());
EXPECT_FALSE(LaunchAndWait({"/bin/false"}));
EXPECT_FALSE(LaunchAndWait({"/no_such_binary"}));
}
TEST(ArcSetupUtil, TestGenerateFakeSerialNumber) {
// Check that the function always returns 20-character string.
EXPECT_EQ(20U,
GenerateFakeSerialNumber("mytestaccount@gmail.com", "001122aabbcc")
.size());
EXPECT_EQ(20U, GenerateFakeSerialNumber("", "").size());
EXPECT_EQ(20U, GenerateFakeSerialNumber("a", "b").size());
// Check that the function always returns the same ID for the same
// account and hwid_raw.
const std::string id_1 =
GenerateFakeSerialNumber("mytestaccount@gmail.com", "001122aabbcc");
const std::string id_2 =
GenerateFakeSerialNumber("mytestaccount@gmail.com", "001122aabbcc");
EXPECT_EQ(id_1, id_2);
// Generate an ID for a different account but for the same machine.
// Check that the ID is not the same as |id_1|.
const std::string id_3 =
GenerateFakeSerialNumber("mytestaccount2@gmail.com", "001122aabbcc");
EXPECT_NE(id_1, id_3);
// Generate an ID for a different machine but for the same account.
// Check that the ID is not the same as |id_1|.
const std::string id_4 =
GenerateFakeSerialNumber("mytestaccount@gmail.com", "001122aaddcc");
EXPECT_NE(id_1, id_4);
// Check that the function treats '\0' in |salt| properly.
const std::string id_5 =
GenerateFakeSerialNumber("mytestaccount@gmail.com", {'a', '\0', 'b'});
const std::string id_6 =
GenerateFakeSerialNumber("mytestaccount@gmail.com", {'a', '\0', 'c'});
EXPECT_NE(id_5, id_6);
}
TEST(ArcSetupUtil, TestGetArtCompilationOffsetSeed) {
const uint64_t seed1 = GetArtCompilationOffsetSeed("salt1", "build1");
const uint64_t seed2 = GetArtCompilationOffsetSeed("salt2", "build1");
const uint64_t seed3 = GetArtCompilationOffsetSeed("salt1", "build2");
EXPECT_NE(0ULL, seed1);
EXPECT_NE(0ULL, seed2);
EXPECT_NE(0ULL, seed3);
EXPECT_NE(seed1, seed2);
EXPECT_NE(seed2, seed3);
EXPECT_NE(seed3, seed1);
}
TEST(ArcSetupUtil, MoveDirIntoDataOldDir) {
base::ScopedTempDir test_dir;
ASSERT_TRUE(test_dir.CreateUniqueTempDir());
base::FilePath dir = test_dir.GetPath().Append("android-data");
base::FilePath data_old_dir = test_dir.GetPath().Append("android-data-old");
// Create android-data/path/to/file and run MoveDirIntoDataOldDir.
ASSERT_TRUE(brillo::MkdirRecursively(
test_dir.GetPath().Append("android-data/path/to"), 0755)
.is_valid());
ASSERT_TRUE(CreateOrTruncate(
test_dir.GetPath().Append("android-data/path/to/file"), 0755));
EXPECT_TRUE(MoveDirIntoDataOldDir(dir, data_old_dir));
EXPECT_TRUE(base::IsDirectoryEmpty(dir));
// android-data has been cleared.
// Create android-data/path/to/file and run MoveDirIntoDataOldDir again.
ASSERT_TRUE(brillo::MkdirRecursively(
test_dir.GetPath().Append("android-data/path/to"), 0755)
.is_valid());
ASSERT_TRUE(CreateOrTruncate(
test_dir.GetPath().Append("android-data/path/to/file"), 0755));
EXPECT_TRUE(MoveDirIntoDataOldDir(dir, data_old_dir));
EXPECT_TRUE(base::IsDirectoryEmpty(dir));
ASSERT_TRUE(base::DirectoryExists(data_old_dir));
// There should be two temp dirs in android-data-old now.
// Both temp dirs should contain path/to/file.
base::FileEnumerator temp_dir_iter(data_old_dir, false,
base::FileEnumerator::DIRECTORIES);
base::FilePath temp_dir;
int temp_dir_count = 0;
while (!(temp_dir = temp_dir_iter.Next()).empty()) {
EXPECT_TRUE(base::PathExists(temp_dir.Append("path/to/file")));
++temp_dir_count;
}
EXPECT_EQ(2, temp_dir_count);
}
TEST(ArcSetupUtil, MoveDirIntoDataOldDir_AndroidDataDirDoesNotExist) {
base::ScopedTempDir test_dir;
ASSERT_TRUE(test_dir.CreateUniqueTempDir());
base::FilePath dir = test_dir.GetPath().Append("android-data");
base::FilePath data_old_dir = test_dir.GetPath().Append("android-data-old");
EXPECT_TRUE(MoveDirIntoDataOldDir(dir, data_old_dir));
EXPECT_TRUE(base::IsDirectoryEmpty(dir));
EXPECT_TRUE(base::IsDirectoryEmpty(data_old_dir));
}
TEST(ArcSetupUtil, MoveDirIntoDataOldDir_AndroidDataDirIsEmpty) {
base::ScopedTempDir test_dir;
ASSERT_TRUE(test_dir.CreateUniqueTempDir());
base::FilePath dir = test_dir.GetPath().Append("android-data");
base::FilePath data_old_dir = test_dir.GetPath().Append("android-data-old");
ASSERT_TRUE(
brillo::MkdirRecursively(test_dir.GetPath().Append("android-data"), 0755)
.is_valid());
EXPECT_TRUE(MoveDirIntoDataOldDir(dir, data_old_dir));
EXPECT_FALSE(base::DirectoryExists(dir));
base::FileEnumerator temp_dir_iter(data_old_dir, false /* recursive */,
base::FileEnumerator::DIRECTORIES);
base::FilePath temp_dir;
int temp_dir_count = 0;
while (!(temp_dir = temp_dir_iter.Next()).empty()) {
EXPECT_TRUE(base::IsDirectoryEmpty(temp_dir));
++temp_dir_count;
}
EXPECT_EQ(1, temp_dir_count);
}
TEST(ArcSetupUtil, MoveDirIntoDataOldDir_AndroidDataDirIsFile) {
base::ScopedTempDir test_dir;
ASSERT_TRUE(test_dir.CreateUniqueTempDir());
base::FilePath dir = test_dir.GetPath().Append("android-data");
base::FilePath data_old_dir = test_dir.GetPath().Append("android-data-old");
// dir is a file, not a directory.
ASSERT_TRUE(CreateOrTruncate(dir, 0755));
EXPECT_TRUE(MoveDirIntoDataOldDir(dir, data_old_dir));
EXPECT_TRUE(base::PathExists(dir));
EXPECT_TRUE(base::IsDirectoryEmpty(data_old_dir));
}
TEST(ArcSetupUtil, MoveDirIntoDataOldDir_AndroidDataOldIsFile) {
base::ScopedTempDir test_dir;
ASSERT_TRUE(test_dir.CreateUniqueTempDir());
base::FilePath dir = test_dir.GetPath().Append("android-data");
base::FilePath data_old_dir = test_dir.GetPath().Append("android-data-old");
ASSERT_TRUE(brillo::MkdirRecursively(
test_dir.GetPath().Append("android-data/path/to"), 0755)
.is_valid());
ASSERT_TRUE(CreateOrTruncate(
test_dir.GetPath().Append("android-data/path/to/file"), 0755));
// Create a file (not a directory) named android-data-old.
ASSERT_TRUE(
CreateOrTruncate(test_dir.GetPath().Append("android-data-old"), 0755));
// This should remove the file named android-data-old and create
// android-data-old dir instead.
EXPECT_TRUE(MoveDirIntoDataOldDir(dir, data_old_dir));
base::FileEnumerator temp_dir_iter(data_old_dir, false,
base::FileEnumerator::DIRECTORIES);
base::FilePath temp_dir;
int temp_dir_count = 0;
while (!(temp_dir = temp_dir_iter.Next()).empty()) {
EXPECT_TRUE(base::PathExists(temp_dir.Append("path/to/file")));
++temp_dir_count;
}
EXPECT_EQ(1, temp_dir_count);
}
TEST(ArcSetupUtil, MoveDirIntoDataOldDir_DirUnderSymlink) {
base::ScopedTempDir test_dir;
ASSERT_TRUE(test_dir.CreateUniqueTempDir());
const base::FilePath target = test_dir.GetPath().Append("symlink_target");
const base::FilePath test_file = target.Append("android-data/path/to/file");
ASSERT_TRUE(brillo::MkdirRecursively(test_file.DirName(), 0755).is_valid());
ASSERT_TRUE(CreateOrTruncate(test_file, 0755));
base::FilePath data_old_dir =
test_dir.GetPath().Append("old-parent/android-data-old");
ASSERT_TRUE(brillo::MkdirRecursively(data_old_dir, 0755).is_valid());
base::FilePath dir = test_dir.GetPath().Append("data-parent/android-data");
ASSERT_TRUE(base::CreateSymbolicLink(target, dir.DirName()));
EXPECT_FALSE(MoveDirIntoDataOldDir(dir, data_old_dir));
EXPECT_TRUE(base::IsDirectoryEmpty(data_old_dir));
EXPECT_TRUE(base::PathExists(test_file));
}
TEST(ArcSetupUtil, MoveDirIntoDataOldDir_OldDirUnderSymlink) {
base::ScopedTempDir test_dir;
ASSERT_TRUE(test_dir.CreateUniqueTempDir());
base::FilePath dir = test_dir.GetPath().Append("data-parent/android-data");
base::FilePath data_old_dir =
test_dir.GetPath().Append("old-parent/android-data-old");
const base::FilePath target = test_dir.GetPath().Append("symlink_target");
ASSERT_TRUE(brillo::MkdirRecursively(target.Append("android-data-old"), 0755)
.is_valid());
ASSERT_TRUE(base::CreateSymbolicLink(target, data_old_dir.DirName()));
const base::FilePath test_file = dir.Append("path/to/file");
ASSERT_TRUE(brillo::MkdirRecursively(test_file.DirName(), 0755).is_valid());
ASSERT_TRUE(CreateOrTruncate(test_file, 0755));
EXPECT_FALSE(MoveDirIntoDataOldDir(dir, data_old_dir));
EXPECT_TRUE(base::PathExists(dir));
EXPECT_TRUE(base::IsDirectoryEmpty(target.Append("android-data-old")));
}
TEST(ArcSetupUtil, TestGetChromeOsChannelFromFile) {
base::ScopedTempDir temp_directory;
ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
base::FilePath prop_file = temp_directory.GetPath().Append("test.prop");
ASSERT_TRUE(
WriteToFile(prop_file, 0700, "CHROMEOS_RELEASE_TRACK=beta-channel"));
EXPECT_EQ("beta", GetChromeOsChannelFromFile(prop_file));
ASSERT_TRUE(
WriteToFile(prop_file, 0700, "CHROMEOS_RELEASE_TRACK=canary-channel"));
EXPECT_EQ("canary", GetChromeOsChannelFromFile(prop_file));
ASSERT_TRUE(
WriteToFile(prop_file, 0700, "CHROMEOS_RELEASE_TRACK=dev-channel"));
EXPECT_EQ("dev", GetChromeOsChannelFromFile(prop_file));
ASSERT_TRUE(
WriteToFile(prop_file, 0700, "CHROMEOS_RELEASE_TRACK=dogfood-channel"));
EXPECT_EQ("dogfood", GetChromeOsChannelFromFile(prop_file));
ASSERT_TRUE(
WriteToFile(prop_file, 0700, "CHROMEOS_RELEASE_TRACK=stable-channel"));
EXPECT_EQ("stable", GetChromeOsChannelFromFile(prop_file));
ASSERT_TRUE(
WriteToFile(prop_file, 0700, "CHROMEOS_RELEASE_TRACK=testimage-channel"));
EXPECT_EQ("testimage", GetChromeOsChannelFromFile(prop_file));
// Checked "unknown" is returned if no value is set
ASSERT_TRUE(WriteToFile(prop_file, 0700, "CHROMEOS_RELEASE_TRACK="));
EXPECT_EQ("unknown", GetChromeOsChannelFromFile(prop_file));
// Checked "unknown" is returned if some unknown string is set
ASSERT_TRUE(WriteToFile(prop_file, 0700, "CHROMEOS_RELEASE_TRACK=foo_bar"));
EXPECT_EQ("unknown", GetChromeOsChannelFromFile(prop_file));
// Checked "unknown" is returned if CHROMEOS_RELEASE_TRACK is not in the file
ASSERT_TRUE(WriteToFile(prop_file, 0700, " "));
EXPECT_EQ("unknown", GetChromeOsChannelFromFile(prop_file));
// Checked "unknown" is returned if file is not present
EXPECT_EQ("unknown", GetChromeOsChannelFromFile(base::FilePath("foo")));
}
TEST(ArcSetupUtil, TestParseContainerState) {
base::ScopedTempDir temp_directory;
ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
base::FilePath json_file = temp_directory.GetPath().Append("state.json");
const base::FilePath kRootfsPath(
"/opt/google/containers/android/rootfs/root");
constexpr char kJsonTemplate[] = R"json(
{
"ociVersion": "1.0",
"id": "android-container",
"status": "created",
"pid": 4422,
"bundle": "/opt/google/containers/android",
"annotations": {
"org.chromium.run_oci.container_root": "%s"
}
}
)json";
ASSERT_TRUE(WriteToFile(
json_file, 0700,
base::StringPrintf(kJsonTemplate,
temp_directory.GetPath().value().c_str())));
ASSERT_TRUE(brillo::MkdirRecursively(
temp_directory.GetPath().Append("mountpoints"), 0755)
.is_valid());
ASSERT_TRUE(base::CreateSymbolicLink(
kRootfsPath,
temp_directory.GetPath().Append("mountpoints/container-root")));
pid_t container_pid;
base::FilePath rootfs;
EXPECT_TRUE(GetOciContainerState(json_file, &container_pid, &rootfs));
EXPECT_EQ(4422, container_pid);
EXPECT_EQ(kRootfsPath, rootfs);
}
TEST(ArcSetupUtil, TestIsProcessAlive) {
EXPECT_TRUE(IsProcessAlive(getpid()));
// We can reasonably expect that a process with a large enough pid doesn't
// exist.
EXPECT_FALSE(IsProcessAlive(std::numeric_limits<pid_t>::max()));
}
TEST(ArcSetupUtil, TestGetSha1HashOfFiles) {
base::ScopedTempDir temp_directory;
ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
const base::FilePath file1 = temp_directory.GetPath().Append("file1");
const base::FilePath file2 = temp_directory.GetPath().Append("file2");
// Create the files.
EXPECT_TRUE(WriteToFile(file1, 0700, "The quick brown fox "));
EXPECT_TRUE(WriteToFile(file2, 0700, "jumps over the lazy dog"));
// Get the hash of these files.
std::string hash;
EXPECT_TRUE(GetSha1HashOfFiles({file1, file2}, &hash));
// Compare it with the pre-computed value. The value can be obtained with:
// $ echo -n "The quick brown fox jumps over the lazy dog" |
// openssl sha1 -binary | base64
std::string hash_expected;
EXPECT_TRUE(
base::Base64Decode("L9ThxnotKPzthJ7hu3bnORuT6xI=", &hash_expected));
EXPECT_EQ(hash_expected, hash);
// Check that the function can accept an empty input.
EXPECT_TRUE(GetSha1HashOfFiles({}, &hash));
EXPECT_TRUE(
base::Base64Decode("2jmj7l5rSw0yVb/vlWAYkK/YBwk=", &hash_expected));
EXPECT_EQ(hash_expected, hash);
// Check that the function returns false when one of the input files does not
// exist.
const base::FilePath file3 =
temp_directory.GetPath().Append("file3"); // does not exist.
EXPECT_FALSE(GetSha1HashOfFiles({file2, file3}, &hash));
EXPECT_FALSE(GetSha1HashOfFiles({file3, file2}, &hash));
EXPECT_FALSE(GetSha1HashOfFiles({file3}, &hash));
}
TEST(ArcSetupUtil, TestShouldDeleteAndroidData) {
// Shouldn't delete data when no upgrade or downgrade.
EXPECT_FALSE(ShouldDeleteAndroidData(AndroidSdkVersion::ANDROID_M,
AndroidSdkVersion::ANDROID_M));
EXPECT_FALSE(ShouldDeleteAndroidData(AndroidSdkVersion::ANDROID_N_MR1,
AndroidSdkVersion::ANDROID_N_MR1));
EXPECT_FALSE(ShouldDeleteAndroidData(AndroidSdkVersion::ANDROID_P,
AndroidSdkVersion::ANDROID_P));
EXPECT_FALSE(ShouldDeleteAndroidData(AndroidSdkVersion::ANDROID_R,
AndroidSdkVersion::ANDROID_R));
// Shouldn't delete data for initial installation.
EXPECT_FALSE(ShouldDeleteAndroidData(AndroidSdkVersion::ANDROID_M,
AndroidSdkVersion::UNKNOWN));
EXPECT_FALSE(ShouldDeleteAndroidData(AndroidSdkVersion::ANDROID_N_MR1,
AndroidSdkVersion::UNKNOWN));
EXPECT_FALSE(ShouldDeleteAndroidData(AndroidSdkVersion::ANDROID_P,
AndroidSdkVersion::UNKNOWN));
EXPECT_FALSE(ShouldDeleteAndroidData(AndroidSdkVersion::ANDROID_R,
AndroidSdkVersion::UNKNOWN));
// All sorts of downgrades should delete data.
EXPECT_TRUE(ShouldDeleteAndroidData(AndroidSdkVersion::ANDROID_N_MR1,
AndroidSdkVersion::ANDROID_P));
EXPECT_TRUE(ShouldDeleteAndroidData(AndroidSdkVersion::ANDROID_M,
AndroidSdkVersion::ANDROID_N_MR1));
EXPECT_TRUE(ShouldDeleteAndroidData(AndroidSdkVersion::ANDROID_P,
AndroidSdkVersion::ANDROID_R));
// Explicitly delete data when ARC++ is upgraded from M to >= P.
EXPECT_TRUE(ShouldDeleteAndroidData(AndroidSdkVersion::ANDROID_P,
AndroidSdkVersion::ANDROID_M));
// Explicitly delete data when ARC++ is upgraded from N to R.
EXPECT_TRUE(ShouldDeleteAndroidData(AndroidSdkVersion::ANDROID_R,
AndroidSdkVersion::ANDROID_N_MR1));
}
TEST(ArcSetupUtil, TestGetUserId) {
uid_t uid = -1;
gid_t gid = -1;
EXPECT_FALSE(GetUserId("thisuserdoesntexist", &uid, &gid));
EXPECT_TRUE(GetUserId("root", &uid, &gid));
EXPECT_EQ(0, uid);
EXPECT_EQ(0, gid);
EXPECT_TRUE(GetUserId("android-root", &uid, &gid));
EXPECT_EQ(655360, uid);
EXPECT_EQ(655360, gid);
if (USE_ARCVM) {
EXPECT_TRUE(GetUserId("crosvm", &uid, &gid));
EXPECT_NE(655360, uid);
EXPECT_NE(655360, gid);
}
}
TEST(ArcSetupUtil, SafeCopyFile) {
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
const base::FilePath src_file = temp_dir.GetPath().Append("srcfile");
// Create a new source file and write it.
ASSERT_TRUE(WriteToFile(src_file, 0755, "testabc"));
const base::FilePath dest_file =
temp_dir.GetPath().Append("dest").Append("destfile");
ASSERT_TRUE(SafeCopyFile(src_file, brillo::SafeFD::Root().first, dest_file,
brillo::SafeFD::Root().first));
const base::FilePath symlink = temp_dir.GetPath().Append("symlink");
ASSERT_TRUE(base::CreateSymbolicLink(dest_file, symlink));
ASSERT_FALSE(SafeCopyFile(src_file, brillo::SafeFD::Root().first, symlink,
brillo::SafeFD::Root().first));
}
} // namespace arc