diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/test.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/test.cc index e81f2564aa..2c19c01741 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/test.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/test.cc @@ -14,6 +14,9 @@ * limitations under the License. */ #include "test.hh" + +#include + #include "libdecomp.hh" vector UnitTest::tests; @@ -21,7 +24,8 @@ vector UnitTest::tests; /// 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. /// \param testNames is the set of names -void UnitTest::run(set &testNames) +/// \return number of failed tests +int UnitTest::run(set &testNames) { int total = 0; @@ -42,6 +46,7 @@ void UnitTest::run(set &testNames) } std::cerr << "==============================" << 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 @@ -75,6 +80,24 @@ void gatherDataTests(const string &dirname,set &testNames,vector } } +/// \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) { bool runUnitTests = true; bool runDataTests = true; @@ -85,7 +108,7 @@ int main(int argc, char **argv) { set dataTestNames; string dirname("../datatests"); string sleighdirname("../../../../../../.."); - if (argc > 0) { + while (argc > 0) { string command(argv[0]); if (command == "-path") { dirname = argv[1]; @@ -109,30 +132,39 @@ int main(int argc, char **argv) { argv += 1; argc -= 1; } - } - if (argc > 0) { - string command(argv[0]); - if (command == "unittests") { + else if (command == "unittests") { runUnitTests = true; runDataTests = false; // Run only unit tests unitTestNames.insert(argv + 1,argv + argc); + break; } else if (command == "datatests") { runUnitTests = false; // Run only data-tests runDataTests = true; dataTestNames.insert(argv + 1,argv + argc); + break; } else { - cout << "USAGE: ghidra_test [-path ] [[unittests|datatests] [testname1 testname2 ...]]" << endl; + cout << "USAGE: ghidra_test [-usesleighenv] [-sleighpath ] [-path ] [[unittests|datatests] [testname1 testname2 ...]]" << endl; + return -1; } } 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) { vector testFiles; gatherDataTests(dirname,dataTestNames,testFiles); cout << endl << endl; - FunctionTestCollection::runTestFiles(testFiles,cout); + int errors = FunctionTestCollection::runTestFiles(testFiles,cout); + failedTests = add_exit_code(failedTests, errors); } + + return failedTests; } diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/test.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/test.hh index 8b762c5b19..a138f511d0 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/test.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/test.hh @@ -54,7 +54,7 @@ struct UnitTest { tests.push_back(this); } - static void run(std::set &testNames); ///< Run all the instantiated tests + static int run(std::set &testNames); ///< Run all the instantiated tests }; diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/testfunction.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/testfunction.cc index 66f8d3fa2e..c136a964b0 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/testfunction.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/testfunction.cc @@ -304,7 +304,7 @@ void FunctionTestCollection::runTests(list &lateStream) /// Run through all XML files in the given list, processing each in turn. /// \param testFiles is the given list of test files /// \param s is the output stream to print results to -void FunctionTestCollection::runTestFiles(const vector &testFiles,ostream &s) +int FunctionTestCollection::runTestFiles(const vector &testFiles,ostream &s) { int4 totalTestsApplied = 0; @@ -344,4 +344,5 @@ void FunctionTestCollection::runTestFiles(const vector &testFiles,ostrea if (iter == failures.end()) break; } } + return totalTestsApplied - totalTestsSucceeded; } diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/testfunction.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/testfunction.hh index 52125b4018..46abd6bce8 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/testfunction.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/testfunction.hh @@ -91,7 +91,7 @@ public: void restoreXml(DocumentStorage &store,const Element *el); ///< Load tests from a \ tag. void restoreXmlOldForm(DocumentStorage &store,const Element *el); ///< Load tests from \ tag. void runTests(list &lateStream); ///< Run the script and perform the tests - static void runTestFiles(const vector &testFiles,ostream &s); ///< Run tests for each listed file + static int runTestFiles(const vector &testFiles,ostream &s); ///< Run tests for each listed file }; #endif