blob: 550995a15e0c579ed5523a75ac601c0a1a2f04dc [file] [log] [blame]
// Copyright (C) 2015 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "android/base/system/System.h"
#include "android/base/EintrWrapper.h"
#include "android/base/files/PathUtils.h"
#include "android/base/Log.h"
#include "android/base/testing/TestSystem.h"
#include "android/base/testing/TestTempDir.h"
#include <gtest/gtest.h>
#include <fstream>
#include <iostream>
#include <string>
#include <fcntl.h>
#include <unistd.h>
#define ARRAYLEN(x) (sizeof(x)/sizeof(x[0]))
namespace android {
namespace base {
static void make_subfile(const std::string& dir, const char* file) {
std::string path = dir;
path.append("/");
path.append(file);
int fd = ::open(path.c_str(), O_WRONLY|O_CREAT, 0755);
EXPECT_GE(fd, 0) << "Path: " << path.c_str();
::close(fd);
}
TEST(System, get) {
System* sys1 = System::get();
EXPECT_TRUE(sys1);
System* sys2 = System::get();
EXPECT_EQ(sys1, sys2);
}
TEST(System, getProgramDirectory) {
std::string dir = System::get()->getProgramDirectory();
EXPECT_FALSE(dir.empty());
LOG(INFO) << "Program directory: [" << dir.c_str() << "]";
}
TEST(System, getHomeDirectory) {
std::string dir = System::get()->getHomeDirectory();
EXPECT_FALSE(dir.empty());
LOG(INFO) << "Home directory: [" << dir.c_str() << "]";
}
TEST(System, getAppDataDirectory) {
std::string dir = System::get()->getAppDataDirectory();
#if defined(__linux__)
EXPECT_TRUE(dir.empty());
#else
// Mac OS X, Microsoft Windows
EXPECT_FALSE(dir.empty());
#endif // __linux__
LOG(INFO) << "AppData directory: [" << dir.c_str() << "]";
}
TEST(System, getCurrentDirectory) {
std::string dir = System::get()->getCurrentDirectory();
EXPECT_FALSE(dir.empty());
LOG(INFO) << "Current directory: [" << dir.c_str() << "]";
}
// Tests case where program directory == launcher directory (QEMU1)
TEST(TestSystem, getDirectory) {
const char kLauncherDir[] = "/foo/bar";
const char kHomeDir[] = "/mama/papa";
#if defined(__linux__)
const char *kAppDataDir = "";
#else
// Mac OS X, Microsoft Windows
const char kAppDataDir[] = "/lala/kaka";
#endif // __linux__
TestSystem testSys(kLauncherDir, 32, kHomeDir, kAppDataDir);
std::string ldir = System::get()->getLauncherDirectory();
EXPECT_STREQ(kLauncherDir, ldir.c_str());
std::string pdir = System::get()->getProgramDirectory();
EXPECT_STREQ(kLauncherDir, pdir.c_str());
std::string hdir = System::get()->getHomeDirectory();
EXPECT_STREQ(kHomeDir, hdir.c_str());
std::string adir = System::get()->getAppDataDirectory();
#if defined(__linux__)
EXPECT_TRUE(adir.empty());
#else
// Mac OS X, Microsoft Windows
EXPECT_STREQ(kAppDataDir, adir.c_str());
#endif // __linux__
}
// Tests case where program directory is a subdirectory of launcher directory
// (QEMU2)
TEST(TestSystem, getDirectoryProgramDir) {
const char kLauncherDir[] = "/foo/bar";
const char kProgramDir[] = "qemu/os-arch";
TestSystem testSys(kLauncherDir, 32, "/home", "/app");
testSys.setProgramSubDir(kProgramDir);
std::string ldir = System::get()->getLauncherDirectory();
EXPECT_STREQ(kLauncherDir, ldir.c_str());
std::string pdir = System::get()->getProgramDirectory();
#ifdef _WIN32
EXPECT_STREQ("/foo/bar\\qemu/os-arch", pdir.c_str());
#else
EXPECT_STREQ("/foo/bar/qemu/os-arch", pdir.c_str());
#endif
}
TEST(System, getHostBitness) {
int hostBitness = System::get()->getHostBitness();
LOG(INFO) << "Host bitness: " << hostBitness;
EXPECT_TRUE(hostBitness == 32 || hostBitness == 64);
{
TestSystem sysTest("/foo", 32);
EXPECT_EQ(32, System::get()->getHostBitness());
}
{
TestSystem sysTest("/foo64", 64);
EXPECT_EQ(64, System::get()->getHostBitness());
}
}
TEST(System, getProgramBitness) {
const int kExpected = (sizeof(void*) == 8) ? 64 : 32;
EXPECT_EQ(kExpected, System::get()->getProgramBitness());
}
TEST(System, scandDirEntries) {
static const char* const kExpected[] = {
"fifth", "first", "fourth", "second", "sixth", "third"
};
static const char* const kInput[] = {
"first", "second", "third", "fourth", "fifth", "sixth"
};
const size_t kCount = ARRAYLEN(kInput);
TestTempDir myDir("scanDirEntries");
for (size_t n = 0; n < kCount; ++n) {
make_subfile(myDir.path(), kInput[n]);
}
std::vector<std::string> entries =
System::get()->scanDirEntries(myDir.path());
EXPECT_EQ(kCount, entries.size());
for (size_t n = 0; n < kCount; ++n) {
EXPECT_STREQ(kExpected[n], entries[n].c_str()) << "#" << n;
}
}
TEST(System, envGetAndSet) {
System* sys = System::get();
const char kVarName[] = "FOO_BAR_TESTING_STUFF";
const char kVarValue[] = "SomethingCompletelyRandomForYou!";
EXPECT_FALSE(sys->envTest(kVarName));
EXPECT_STREQ("", sys->envGet(kVarName).c_str());
sys->envSet(kVarName, kVarValue);
EXPECT_TRUE(sys->envTest(kVarName));
EXPECT_STREQ(kVarValue, sys->envGet(kVarName).c_str());
sys->envSet(kVarName, nullptr);
EXPECT_FALSE(sys->envTest(kVarName));
EXPECT_STREQ("", sys->envGet(kVarName).c_str());
}
TEST(System, pathIsDir) {
TestSystem sys("/bin", 32);
EXPECT_FALSE(sys.pathIsDir("foo"));
EXPECT_FALSE(sys.pathIsDir("foo/"));
#ifdef _WIN32
EXPECT_FALSE(sys.pathIsDir("foo\\"));
#endif
sys.getTempRoot()->makeSubDir("foo");
EXPECT_TRUE(sys.pathIsDir("foo"));
EXPECT_TRUE(sys.pathIsDir("foo/"));
#ifdef _WIN32
EXPECT_TRUE(sys.pathIsDir("foo\\"));
#endif
}
TEST(System, pathOperations) {
System* sys = System::get();
TestTempDir tempDir("path_opts");
std::string fooPath = tempDir.path();
fooPath += "/foo";
System::FileSize fileSize;
EXPECT_FALSE(sys->pathExists(fooPath));
EXPECT_FALSE(sys->pathIsFile(fooPath));
EXPECT_FALSE(sys->pathIsDir(fooPath));
EXPECT_FALSE(sys->pathCanRead(fooPath));
EXPECT_FALSE(sys->pathCanWrite(fooPath));
EXPECT_FALSE(sys->pathCanExec(fooPath));
EXPECT_FALSE(sys->pathFileSize(fooPath, &fileSize));
make_subfile(tempDir.path(), "foo");
EXPECT_TRUE(sys->pathExists(fooPath));
EXPECT_TRUE(sys->pathIsFile(fooPath));
EXPECT_FALSE(sys->pathIsDir(fooPath));
// NOTE: Windows doesn't have 'execute' permission bits.
// Any readable file can be executed. Also any writable file
// is readable.
EXPECT_FALSE(HANDLE_EINTR(chmod(fooPath.c_str(), S_IRUSR|S_IWUSR|S_IXUSR)));
EXPECT_TRUE(sys->pathCanRead(fooPath));
EXPECT_TRUE(sys->pathCanWrite(fooPath));
EXPECT_TRUE(sys->pathCanExec(fooPath));
EXPECT_FALSE(HANDLE_EINTR(chmod(fooPath.c_str(), S_IRUSR)));
EXPECT_TRUE(sys->pathCanRead(fooPath));
EXPECT_FALSE(sys->pathCanWrite(fooPath));
#ifdef _WIN32
EXPECT_TRUE(sys->pathCanExec(fooPath));
#else
EXPECT_FALSE(sys->pathCanExec(fooPath));
#endif
EXPECT_FALSE(HANDLE_EINTR(chmod(fooPath.c_str(), S_IWUSR)));
#ifdef _WIN32
EXPECT_TRUE(sys->pathCanRead(fooPath));
EXPECT_TRUE(sys->pathCanWrite(fooPath));
EXPECT_TRUE(sys->pathCanExec(fooPath));
#else
EXPECT_FALSE(sys->pathCanRead(fooPath));
EXPECT_TRUE(sys->pathCanWrite(fooPath));
EXPECT_FALSE(sys->pathCanExec(fooPath));
#endif
EXPECT_FALSE(HANDLE_EINTR(chmod(fooPath.c_str(), S_IXUSR)));
#ifdef _WIN32
EXPECT_TRUE(sys->pathCanRead(fooPath));
#else
EXPECT_FALSE(sys->pathCanRead(fooPath));
#endif
EXPECT_FALSE(sys->pathCanWrite(fooPath));
EXPECT_TRUE(sys->pathCanExec(fooPath));
EXPECT_FALSE(HANDLE_EINTR(chmod(fooPath.c_str(), S_IRUSR|S_IWUSR)));
EXPECT_TRUE(sys->pathCanRead(fooPath));
EXPECT_TRUE(sys->pathCanWrite(fooPath));
#ifdef _WIN32
EXPECT_TRUE(sys->pathCanExec(fooPath));
#else
EXPECT_FALSE(sys->pathCanExec(fooPath));
#endif
EXPECT_FALSE(HANDLE_EINTR(chmod(fooPath.c_str(), S_IRUSR|S_IXUSR)));
EXPECT_TRUE(sys->pathCanRead(fooPath));
EXPECT_FALSE(sys->pathCanWrite(fooPath));
EXPECT_TRUE(sys->pathCanExec(fooPath));
EXPECT_FALSE(HANDLE_EINTR(chmod(fooPath.c_str(), S_IWUSR|S_IXUSR)));
#ifdef _WIN32
EXPECT_TRUE(sys->pathCanRead(fooPath));
#else
EXPECT_FALSE(sys->pathCanRead(fooPath));
#endif
EXPECT_TRUE(sys->pathCanWrite(fooPath));
EXPECT_TRUE(sys->pathCanExec(fooPath));
EXPECT_FALSE(sys->pathFileSize(fooPath, nullptr));
EXPECT_TRUE(sys->pathFileSize(fooPath, &fileSize));
EXPECT_EQ(0, fileSize);
std::ofstream fooFile(fooPath);
ASSERT_TRUE(bool(fooFile));
fooFile << "Some non-zero data";
fooFile.close();
EXPECT_TRUE(sys->pathFileSize(fooPath, &fileSize));
EXPECT_LT(0, fileSize);
// Test file deletion
EXPECT_TRUE(sys->deleteFile(fooPath));
EXPECT_FALSE(sys->pathFileSize(fooPath, &fileSize));
}
TEST(System, scanDirEntriesWithFullPaths) {
static const char* const kExpected[] = {
"fifth", "first", "fourth", "second", "sixth", "third"
};
static const char* const kInput[] = {
"first", "second", "third", "fourth", "fifth", "sixth"
};
const size_t kCount = ARRAYLEN(kInput);
TestTempDir myDir("scanDirEntriesFull");
for (size_t n = 0; n < kCount; ++n) {
make_subfile(myDir.path(), kInput[n]);
}
std::vector<std::string> entries =
System::get()->scanDirEntries(myDir.path(), true);
EXPECT_EQ(kCount, entries.size());
for (size_t n = 0; n < kCount; ++n) {
std::string expected(myDir.path());
expected = PathUtils::addTrailingDirSeparator(expected);
expected += kExpected[n];
EXPECT_STREQ(expected.c_str(), entries[n].c_str()) << "#" << n;
}
}
TEST(System, isRemoteSession) {
std::string sessionType;
bool isRemote = System::get()->isRemoteSession(&sessionType);
if (isRemote) {
LOG(INFO) << "Remote session type [" << sessionType.c_str() << "]";
} else {
LOG(INFO) << "Local session type";
}
}
TEST(System, addLibrarySearchDir) {
TestSystem testSys("/foo/bar", 32);
TestTempDir* testDir = testSys.getTempRoot();
ASSERT_TRUE(testDir->makeSubDir("lib"));
testSys.addLibrarySearchDir("lib");
}
TEST(System, findBundledExecutable) {
#ifdef _WIN32
static const char kProgramFile[] = "myprogram.exe";
#else
static const char kProgramFile[] = "myprogram";
#endif
TestSystem testSys("/foo", System::kProgramBitness);
TestTempDir* testDir = testSys.getTempRoot();
ASSERT_TRUE(testDir->makeSubDir("foo"));
std::vector<std::string> pathList;
pathList.push_back(std::string("foo"));
pathList.push_back(std::string(System::kBinSubDir));
ASSERT_TRUE(testDir->makeSubDir(PathUtils::recompose(pathList).c_str()));
pathList.push_back(std::string(kProgramFile));
std::string programPath = PathUtils::recompose(pathList);
make_subfile(testDir->path(), programPath.c_str());
std::string path = testSys.findBundledExecutable("myprogram");
std::string expectedPath("/");
expectedPath += programPath;
EXPECT_STREQ(expectedPath.c_str(), path.c_str());
path = testSys.findBundledExecutable("otherprogram");
EXPECT_FALSE(path.size());
}
TEST(System, getProcessTimes) {
const System::Times times1 = System::get()->getProcessTimes();
const System::Times times2 = System::get()->getProcessTimes();
ASSERT_GE(times2.userMs, times1.userMs);
ASSERT_GE(times2.systemMs, times1.systemMs);
}
TEST(System, getUnixTime) {
const time_t curTime = time(NULL);
const time_t time1 = System::get()->getUnixTime();
const time_t time2 = System::get()->getUnixTime();
ASSERT_GE(time1, curTime);
ASSERT_GE(time2, time1);
}
TEST(System, runCommandTrue) {
#ifndef _WIN32
std::vector<std::string> cmd = {"ls"};
#else
// 'ver' is a builtin command, so all you need is a sane cmd.exe
std::vector<std::string> cmd = {"cmd.exe", "/C", "ver"};
#endif
EXPECT_TRUE(System::get()->runCommand(cmd));
EXPECT_TRUE(System::get()->runCommand(cmd, RunOptions::WaitForCompletion));
System::Pid pid = 0;
System::ProcessExitCode exitCode = 666;
EXPECT_TRUE(System::get()->runCommand(cmd, RunOptions::WaitForCompletion,
System::kInfinite, &exitCode, &pid));
EXPECT_EQ(0, exitCode);
EXPECT_GT(pid, 0);
}
TEST(System, runCommandTimeout) {
#ifndef _WIN32
std::vector<std::string> cmd = {"sleep", "0.1"};
#else
// 2 Attempts give us a delay of 1 second.
// 'ping' is not an internal cmd.exe command, but seems always being shipped
// on recent windows boxes and wine.
std::vector<std::string> cmd = {"ping", "-n", "2", "127.0.0.1"};
#endif
EXPECT_FALSE(
System::get()->runCommand(cmd, RunOptions::WaitForCompletion, 1));
System::Pid pid = 0;
System::ProcessExitCode exitCode = 666;
#ifdef _WIN32
// Make sure we don't wait for too long here, where we expect to succeed.
cmd = {"cmd.exe", "/C", "ver"};
#endif
EXPECT_TRUE(System::get()->runCommand(cmd, RunOptions::WaitForCompletion,
System::kInfinite, &exitCode, &pid));
EXPECT_EQ(0, exitCode);
EXPECT_GT(pid, 0);
}
TEST(System, DISABLED_runCommandWithOutput) {
std::vector<std::string> cmd = {"echo", "hello"};
System::Pid pid = 666;
System::ProcessExitCode exitCode = 0;
std::string outputFile = PathUtils::recompose(
{System::get()->getTempDir(), std::string("test.txt")});
EXPECT_TRUE(System::get()->runCommand(
cmd, RunOptions::WaitForCompletion | RunOptions::DumpOutputToFile,
System::kInfinite, &exitCode, &pid, outputFile));
EXPECT_TRUE(System::get()->pathExists(outputFile));
EXPECT_TRUE(System::get()->pathIsFile(outputFile));
EXPECT_TRUE(System::get()->pathCanRead(outputFile));
std::ifstream file(outputFile);
EXPECT_TRUE(file.is_open());
if (file.is_open()) {
std::string line;
EXPECT_TRUE(static_cast<bool>(std::getline(file, line)));
// An idosyncrasy of cmd.exe's "echo" command - we add spaces between
// arguments, and that space gets captured by echo. This is only a
// problem with echo itself.
EXPECT_TRUE(!strncmp("hello", line.c_str(), 5));
file.close();
}
EXPECT_EQ(0, std::remove(outputFile.c_str()));
}
} // namespace base
} // namespace android