Make bit able to run gtest native tests.
The output parsing isn't ideal, so these are a bit more spammy than I'd like, but at least they build, install and run without the manual glop. Test: bit incidentd_test Change-Id: I3c34a4ebbf661f612b4b0f8b4e05cade8669b5a6
This commit is contained in:
@@ -33,6 +33,8 @@
|
||||
|
||||
using namespace std;
|
||||
|
||||
#define NATIVE_TESTS "NATIVE_TESTS"
|
||||
|
||||
/**
|
||||
* An entry from the command line for something that will be built, installed,
|
||||
* and/or tested.
|
||||
@@ -132,6 +134,32 @@ InstallApk::InstallApk(const string& filename, bool always)
|
||||
{
|
||||
}
|
||||
|
||||
struct PushedFile
|
||||
{
|
||||
TrackedFile file;
|
||||
string dest;
|
||||
|
||||
PushedFile();
|
||||
PushedFile(const PushedFile& that);
|
||||
PushedFile(const string& filename, const string& dest);
|
||||
~PushedFile() {};
|
||||
};
|
||||
|
||||
PushedFile::PushedFile()
|
||||
{
|
||||
}
|
||||
|
||||
PushedFile::PushedFile(const PushedFile& that)
|
||||
:file(that.file),
|
||||
dest(that.dest)
|
||||
{
|
||||
}
|
||||
|
||||
PushedFile::PushedFile(const string& f, const string& d)
|
||||
:file(f),
|
||||
dest(d)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Record for an test that is going to be launched.
|
||||
@@ -658,12 +686,14 @@ run_phases(vector<Target*> targets, const Options& options)
|
||||
}
|
||||
|
||||
// Figure out whether we need to sync the system and which apks to install
|
||||
string systemPath = buildOut + "/target/product/" + buildDevice + "/system/";
|
||||
string dataPath = buildOut + "/target/product/" + buildDevice + "/data/";
|
||||
string deviceTargetPath = buildOut + "/target/product/" + buildDevice;
|
||||
string systemPath = deviceTargetPath + "/system/";
|
||||
string dataPath = deviceTargetPath + "/data/";
|
||||
bool syncSystem = false;
|
||||
bool alwaysSyncSystem = false;
|
||||
vector<string> systemFiles;
|
||||
vector<InstallApk> installApks;
|
||||
vector<PushedFile> pushedFiles;
|
||||
for (size_t i=0; i<targets.size(); i++) {
|
||||
Target* target = targets[i];
|
||||
if (target->install) {
|
||||
@@ -687,6 +717,11 @@ run_phases(vector<Target*> targets, const Options& options)
|
||||
installApks.push_back(InstallApk(file, !target->build));
|
||||
continue;
|
||||
}
|
||||
// If it's a native test module, push it.
|
||||
if (target->module.HasClass(NATIVE_TESTS) && starts_with(file, dataPath)) {
|
||||
string installedPath(file.c_str() + deviceTargetPath.length());
|
||||
pushedFiles.push_back(PushedFile(file, installedPath));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -701,6 +736,13 @@ run_phases(vector<Target*> targets, const Options& options)
|
||||
printf(" %s\n", systemFiles[i].c_str());
|
||||
}
|
||||
}
|
||||
if (pushedFiles.size() > 0){
|
||||
print_info("Files to push:");
|
||||
for (size_t i=0; i<pushedFiles.size(); i++) {
|
||||
printf(" %s\n", pushedFiles[i].file.filename.c_str());
|
||||
printf(" --> %s\n", pushedFiles[i].dest.c_str());
|
||||
}
|
||||
}
|
||||
if (installApks.size() > 0){
|
||||
print_info("APKs to install:");
|
||||
for (size_t i=0; i<installApks.size(); i++) {
|
||||
@@ -784,6 +826,25 @@ run_phases(vector<Target*> targets, const Options& options)
|
||||
}
|
||||
}
|
||||
|
||||
// Push files
|
||||
if (pushedFiles.size() > 0) {
|
||||
print_status("Pushing files");
|
||||
for (size_t i=0; i<pushedFiles.size(); i++) {
|
||||
const PushedFile& pushed = pushedFiles[i];
|
||||
string dir = dirname(pushed.dest);
|
||||
if (dir.length() == 0 || dir == "/") {
|
||||
// This isn't really a file inside the data directory. Just skip it.
|
||||
continue;
|
||||
}
|
||||
// TODO: if (!apk.file.fileInfo.exists || apk.file.HasChanged())
|
||||
err = run_adb("shell", "mkdir", "-p", dir.c_str(), NULL);
|
||||
check_error(err);
|
||||
err = run_adb("push", pushed.file.filename.c_str(), pushed.dest.c_str());
|
||||
check_error(err);
|
||||
// pushed.installed = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Install APKs
|
||||
if (installApks.size() > 0) {
|
||||
print_status("Installing APKs");
|
||||
@@ -804,6 +865,74 @@ run_phases(vector<Target*> targets, const Options& options)
|
||||
// Actions
|
||||
//
|
||||
|
||||
// Whether there have been any tests run, so we can print a summary.
|
||||
bool testsRun = false;
|
||||
|
||||
// Run the native tests.
|
||||
// TODO: We don't have a good way of running these and capturing the output of
|
||||
// them live. It'll take some work. On the other hand, if they're gtest tests,
|
||||
// the output of gtest is not completely insane like the text output of the
|
||||
// instrumentation tests. So for now, we'll just live with that.
|
||||
for (size_t i=0; i<targets.size(); i++) {
|
||||
Target* target = targets[i];
|
||||
if (target->test && target->module.HasClass(NATIVE_TESTS)) {
|
||||
// We don't have a clear signal from the build system which of the installed
|
||||
// files is actually the test, so we guess by looking for one with the same
|
||||
// leaf name as the module that is executable.
|
||||
for (size_t j=0; j<target->module.installed.size(); j++) {
|
||||
string filename = target->module.installed[j];
|
||||
if (!starts_with(filename, dataPath)) {
|
||||
// Native tests go into the data directory.
|
||||
continue;
|
||||
}
|
||||
if (leafname(filename) != target->module.name) {
|
||||
// This isn't the test executable.
|
||||
continue;
|
||||
}
|
||||
if (!is_executable(filename)) {
|
||||
continue;
|
||||
}
|
||||
string installedPath(filename.c_str() + deviceTargetPath.length());
|
||||
printf("the magic one is: %s\n", filename.c_str());
|
||||
printf(" and it's installed at: %s\n", installedPath.c_str());
|
||||
|
||||
// Convert bit-style actions to gtest test filter arguments
|
||||
if (target->actions.size() > 0) {
|
||||
testsRun = true;
|
||||
target->testActionCount++;
|
||||
bool runAll = false;
|
||||
string filterArg("--gtest_filter=");
|
||||
for (size_t k=0; k<target->actions.size(); k++) {
|
||||
string actionString = target->actions[k];
|
||||
if (actionString == "*") {
|
||||
runAll = true;
|
||||
} else {
|
||||
filterArg += actionString;
|
||||
if (k != target->actions.size()-1) {
|
||||
// We would otherwise have to worry about this condition
|
||||
// being true, and appending an extra ':', but we know that
|
||||
// if the extra action is "*", then we'll just run all and
|
||||
// won't use filterArg anyway, so just keep this condition
|
||||
// simple.
|
||||
filterArg += ':';
|
||||
}
|
||||
}
|
||||
}
|
||||
if (runAll) {
|
||||
err = run_adb("shell", installedPath.c_str(), NULL);
|
||||
} else {
|
||||
err = run_adb("shell", installedPath.c_str(), filterArg.c_str(), NULL);
|
||||
}
|
||||
if (err == 0) {
|
||||
target->testPassCount++;
|
||||
} else {
|
||||
target->testFailCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Inspect the apks, and figure out what is an activity and what needs a test runner
|
||||
bool printedInspecting = false;
|
||||
vector<TestAction> testActions;
|
||||
@@ -872,6 +1001,7 @@ run_phases(vector<Target*> targets, const Options& options)
|
||||
TestResults testResults;
|
||||
if (testActions.size() > 0) {
|
||||
print_status("Running tests");
|
||||
testsRun = true;
|
||||
for (size_t i=0; i<testActions.size(); i++) {
|
||||
TestAction& action = testActions[i];
|
||||
testResults.SetCurrentAction(&action);
|
||||
@@ -969,7 +1099,7 @@ run_phases(vector<Target*> targets, const Options& options)
|
||||
|
||||
// Tests
|
||||
bool hasErrors = false;
|
||||
if (testActions.size() > 0) {
|
||||
if (testsRun) {
|
||||
printf("%sRan tests:%s\n", g_escapeBold, g_escapeEndColor);
|
||||
size_t maxNameLength = 0;
|
||||
for (size_t i=0; i<targets.size(); i++) {
|
||||
|
||||
@@ -51,6 +51,18 @@ make_cache_filename(const string& outDir)
|
||||
return filename + "/.bit_cache";
|
||||
}
|
||||
|
||||
bool
|
||||
Module::HasClass(const string& cl)
|
||||
{
|
||||
for (vector<string>::const_iterator c = classes.begin(); c != classes.end(); c++) {
|
||||
if (*c == cl) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
BuildVars::BuildVars(const string& outDir, const string& buildProduct,
|
||||
const string& buildVariant, const string& buildType)
|
||||
:m_filename(),
|
||||
|
||||
@@ -29,6 +29,8 @@ struct Module
|
||||
vector<string> classes;
|
||||
vector<string> paths;
|
||||
vector<string> installed;
|
||||
|
||||
bool HasClass(const string& cl);
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -254,4 +254,42 @@ read_file(const string& filename)
|
||||
return result;
|
||||
}
|
||||
|
||||
bool
|
||||
is_executable(const string& filename)
|
||||
{
|
||||
int err;
|
||||
struct stat st;
|
||||
|
||||
err = stat(filename.c_str(), &st);
|
||||
if (err != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (st.st_mode & S_IXUSR) != 0;
|
||||
}
|
||||
|
||||
string
|
||||
dirname(const string& filename)
|
||||
{
|
||||
size_t slash = filename.rfind('/');
|
||||
if (slash == string::npos) {
|
||||
return "";
|
||||
} else if (slash == 0) {
|
||||
return "/";
|
||||
} else {
|
||||
return string(filename, 0, slash);
|
||||
}
|
||||
}
|
||||
|
||||
string
|
||||
leafname(const string& filename)
|
||||
{
|
||||
size_t slash = filename.rfind('/');
|
||||
if (slash == string::npos) {
|
||||
return filename;
|
||||
} else if (slash == filename.length() - 1) {
|
||||
return "";
|
||||
} else {
|
||||
return string(filename, slash + 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,5 +79,10 @@ void split_lines(vector<string>* result, const string& str);
|
||||
|
||||
string read_file(const string& filename);
|
||||
|
||||
bool is_executable(const string& filename);
|
||||
|
||||
string dirname(const string& filename);
|
||||
string leafname(const string& filename);
|
||||
|
||||
#endif // UTIL_H
|
||||
|
||||
|
||||
Reference in New Issue
Block a user