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
diff --git a/tools/bit/main.cpp b/tools/bit/main.cpp
index 860094ae..082ccf3 100644
--- a/tools/bit/main.cpp
+++ b/tools/bit/main.cpp
@@ -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 @@
{
}
+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 @@
}
// 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 @@
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 @@
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 @@
}
}
+ // 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 @@
// 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 @@
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 @@
// 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++) {
diff --git a/tools/bit/make.cpp b/tools/bit/make.cpp
index 6270913..df64a80 100644
--- a/tools/bit/make.cpp
+++ b/tools/bit/make.cpp
@@ -51,6 +51,18 @@
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(),
diff --git a/tools/bit/make.h b/tools/bit/make.h
index db0b69f..785912a 100644
--- a/tools/bit/make.h
+++ b/tools/bit/make.h
@@ -29,6 +29,8 @@
vector<string> classes;
vector<string> paths;
vector<string> installed;
+
+ bool HasClass(const string& cl);
};
/**
diff --git a/tools/bit/util.cpp b/tools/bit/util.cpp
index a502a9d..63399d6 100644
--- a/tools/bit/util.cpp
+++ b/tools/bit/util.cpp
@@ -254,4 +254,42 @@
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);
+ }
+}
diff --git a/tools/bit/util.h b/tools/bit/util.h
index 718f147..7ccdab1 100644
--- a/tools/bit/util.h
+++ b/tools/bit/util.h
@@ -79,5 +79,10 @@
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