Fixes CLI argument parsing

Accept more than one option for specifying test paths.

Support non-zero exit code when tests fail

Return a non-zero exit code (saturated to max value 255, even if more
than 255 tests fail) indicating number of failed tests. This is helpful
when running in a script (like in CI) to detect failures instead of
detecting failures through manual visual inspection of output text.
This commit is contained in:
Eric Kilmer 2022-05-24 19:53:10 -04:00 committed by caheckman
parent 0241b2b97e
commit cd09ea0c4a
4 changed files with 46 additions and 13 deletions

View file

@ -14,6 +14,9 @@
* limitations under the License. * limitations under the License.
*/ */
#include "test.hh" #include "test.hh"
#include <algorithm>
#include "libdecomp.hh" #include "libdecomp.hh"
vector<UnitTest *> UnitTest::tests; vector<UnitTest *> UnitTest::tests;
@ -21,7 +24,8 @@ vector<UnitTest *> UnitTest::tests;
/// Run all the tests unless a non-empty set of names is passed in. /// Run all the tests unless a non-empty set of names is passed in.
/// In which case, only the named tests in the set are run. /// In which case, only the named tests in the set are run.
/// \param testNames is the set of names /// \param testNames is the set of names
void UnitTest::run(set<string> &testNames) /// \return number of failed tests
int UnitTest::run(set<string> &testNames)
{ {
int total = 0; int total = 0;
@ -42,6 +46,7 @@ void UnitTest::run(set<string> &testNames)
} }
std::cerr << "==============================" << std::endl; std::cerr << "==============================" << std::endl;
std::cerr << passed << "/" << total << " tests passed." << std::endl; std::cerr << passed << "/" << total << " tests passed." << std::endl;
return total - passed;
} }
/// Create list of the absolute path of all tests to be run /// Create list of the absolute path of all tests to be run
@ -75,6 +80,24 @@ void gatherDataTests(const string &dirname,set<string> &testNames,vector<string>
} }
} }
/// \brief This function performs a saturating add on two numbers where the
/// result is to be used as an exit code for a CLI application.
///
/// \param current The current return code
/// \param add A number to add to the current return code
/// \return A number that can be used as an exit code up to 255.
int add_exit_code(int current, int add) {
const int CLAMP = 255;
int ret = current + add;
if (current < 0 || // Sanity checks
current > CLAMP ||
ret < current || // Can only happen due to overflow
ret > CLAMP) { // Check clamp value
ret = CLAMP; // Set to max exit code
}
return ret;
}
int main(int argc, char **argv) { int main(int argc, char **argv) {
bool runUnitTests = true; bool runUnitTests = true;
bool runDataTests = true; bool runDataTests = true;
@ -85,7 +108,7 @@ int main(int argc, char **argv) {
set<string> dataTestNames; set<string> dataTestNames;
string dirname("../datatests"); string dirname("../datatests");
string sleighdirname("../../../../../../.."); string sleighdirname("../../../../../../..");
if (argc > 0) { while (argc > 0) {
string command(argv[0]); string command(argv[0]);
if (command == "-path") { if (command == "-path") {
dirname = argv[1]; dirname = argv[1];
@ -109,30 +132,39 @@ int main(int argc, char **argv) {
argv += 1; argv += 1;
argc -= 1; argc -= 1;
} }
} else if (command == "unittests") {
if (argc > 0) {
string command(argv[0]);
if (command == "unittests") {
runUnitTests = true; runUnitTests = true;
runDataTests = false; // Run only unit tests runDataTests = false; // Run only unit tests
unitTestNames.insert(argv + 1,argv + argc); unitTestNames.insert(argv + 1,argv + argc);
break;
} }
else if (command == "datatests") { else if (command == "datatests") {
runUnitTests = false; // Run only data-tests runUnitTests = false; // Run only data-tests
runDataTests = true; runDataTests = true;
dataTestNames.insert(argv + 1,argv + argc); dataTestNames.insert(argv + 1,argv + argc);
break;
} }
else { else {
cout << "USAGE: ghidra_test [-path <datatestdir>] [[unittests|datatests] [testname1 testname2 ...]]" << endl; cout << "USAGE: ghidra_test [-usesleighenv] [-sleighpath <sleighdir>] [-path <datatestdir>] [[unittests|datatests] [testname1 testname2 ...]]" << endl;
return -1;
} }
} }
startDecompilerLibrary(sleighdirname.c_str()); startDecompilerLibrary(sleighdirname.c_str());
if (runUnitTests)
UnitTest::run(unitTestNames); // Keep track of failed tests as return code to indicate failures, clamped at
// max exit code value in add_exit_code
int failedTests = 0;
if (runUnitTests) {
int errors = UnitTest::run(unitTestNames);
failedTests = add_exit_code(failedTests, errors);
}
if (runDataTests) { if (runDataTests) {
vector<string> testFiles; vector<string> testFiles;
gatherDataTests(dirname,dataTestNames,testFiles); gatherDataTests(dirname,dataTestNames,testFiles);
cout << endl << endl; cout << endl << endl;
FunctionTestCollection::runTestFiles(testFiles,cout); int errors = FunctionTestCollection::runTestFiles(testFiles,cout);
failedTests = add_exit_code(failedTests, errors);
} }
return failedTests;
} }

View file

@ -54,7 +54,7 @@ struct UnitTest {
tests.push_back(this); tests.push_back(this);
} }
static void run(std::set<std::string> &testNames); ///< Run all the instantiated tests static int run(std::set<std::string> &testNames); ///< Run all the instantiated tests
}; };

View file

@ -304,7 +304,7 @@ void FunctionTestCollection::runTests(list<string> &lateStream)
/// Run through all XML files in the given list, processing each in turn. /// Run through all XML files in the given list, processing each in turn.
/// \param testFiles is the given list of test files /// \param testFiles is the given list of test files
/// \param s is the output stream to print results to /// \param s is the output stream to print results to
void FunctionTestCollection::runTestFiles(const vector<string> &testFiles,ostream &s) int FunctionTestCollection::runTestFiles(const vector<string> &testFiles,ostream &s)
{ {
int4 totalTestsApplied = 0; int4 totalTestsApplied = 0;
@ -344,4 +344,5 @@ void FunctionTestCollection::runTestFiles(const vector<string> &testFiles,ostrea
if (iter == failures.end()) break; if (iter == failures.end()) break;
} }
} }
return totalTestsApplied - totalTestsSucceeded;
} }

View file

@ -91,7 +91,7 @@ public:
void restoreXml(DocumentStorage &store,const Element *el); ///< Load tests from a \<decompilertest> tag. void restoreXml(DocumentStorage &store,const Element *el); ///< Load tests from a \<decompilertest> tag.
void restoreXmlOldForm(DocumentStorage &store,const Element *el); ///< Load tests from \<binaryimage> tag. void restoreXmlOldForm(DocumentStorage &store,const Element *el); ///< Load tests from \<binaryimage> tag.
void runTests(list<string> &lateStream); ///< Run the script and perform the tests void runTests(list<string> &lateStream); ///< Run the script and perform the tests
static void runTestFiles(const vector<string> &testFiles,ostream &s); ///< Run tests for each listed file static int runTestFiles(const vector<string> &testFiles,ostream &s); ///< Run tests for each listed file
}; };
#endif #endif