Files
resiprocate/tfm/CppTestSelector.cxx
Andrey Semashev 06f72f71f3 Fix usage of <cctype> functions.
Functions defined in <cctype> have undefined behavior if a negative
value is passed on input. Input values must always be converted to
unsigned integers.

Also, added missing includes.
2025-09-13 23:36:02 +03:00

295 lines
6.5 KiB
C++

#include "CppTestSelector.hxx"
#include <ctype.h>
#include <cstring>
#include <iostream>
#include <regex>
using namespace std;
CommandLineSelector::CommandLineSelector(int argc, char** argv) : mArgNumTimes(0)
{
// Needs to be reworked for many selections and number of repeats
for(int i = 1; i < argc; i++)
{
std::string userInput = argv[i];
if(isdigit(static_cast< unsigned char >(*userInput.c_str())))
{
int res = atoi(userInput.c_str());
if(res < 0)
continue;
if(i + 1 < argc)
{
mArgResult.push_back(res);
}
else
{
mArgNumTimes = res;
}
}
else
{
mPattern = userInput;
}
}
if(mArgNumTimes == 0 && (mArgResult.size() > 0 || mPattern.size() > 0) )
{
mArgNumTimes = 1;
}
}
void CommandLineSelector::DisplayTestCases(std::vector<std::string>& names)
{
int i;
int count = (int) names.size();
std::cout << std::endl;
for(i=0; i< count; i++)
std::cout << i << " " << names[i] << std::endl;
std::cout << i << " Cancel" << std::endl;
std::cout << std::endl;
}
void CommandLineSelector::DisplayTestCases2(std::vector<std::string>& names)
{
int i;
size_t off = 0;
size_t off1 = 0;
size_t off2 = 0;
const char *sep = "::";
const char *indent0 = " ";
const char *indent1 = " ";
const char *indent2 = " ... ";
const char *indent;
int count = (int) names.size();
std::cout << std::endl;
for(i=0; i< count; i++)
{
off = 0;
indent = indent0;
if((off1 = names[i].find(sep, 0)) >= 0)
{
if((off2 = names[i].find(sep, off1+strlen(sep))) >= 0) //child test
{
off = off2+strlen(sep);
indent = indent2;
}
else
{
off = off1+strlen(sep);
indent = indent1;
}
}
std::cout << i << indent << names[i].c_str()+off << std::endl;
}
std::cout << i << indent0 << "Cancel" << std::endl;
std::cout << std::endl;
}
void CommandLineSelector::ReadLineInput(std::vector<int> &result, int maxSelection, int &numTimes)
{
int res = 0;
int repeat = 1;
std::string userInput;
std::string repeatStr;
//long index = 0;
numTimes = 1;
while(true)
{
std::cout << std::endl;
std::cout << "Enter a test number (or pattern) followed by number of repetitions: ";
std::cin >> userInput;
std::cin >> repeatStr;
if(isdigit(static_cast< unsigned char >(*userInput.c_str())))
{
res = atoi(userInput.c_str());
if(res == maxSelection)
{
break; // user wants to cancel
}
if((res < 0) || (res >= maxSelection))
{
break;
}
result.push_back(res);
}
else
{
mPattern = userInput;
}
// see if user specified number of repetitions
if((repeat = atoi(repeatStr.c_str())) > 0)
{
numTimes = repeat;
break;
}
}
}
void CommandLineSelector::GetUserSelection(std::vector<std::string>& names, std::vector<int> &result, int &numTimes)
{
if(names.size() == 0)
{
std::cout << std::endl << "No tests specified" << std::endl;
return;
}
if(mArgNumTimes > 0)
{
result = mArgResult;
numTimes = mArgNumTimes;
}
else
{
DisplayTestCases2(names);
ReadLineInput(result, (int)names.size(), numTimes);
}
if(mPattern.size() == 0)
{
return;
}
size_t startFrom = 0;
std::wstring prefix1 = L">=";
std::wstring prefix2 = L">";
if(std::equal(prefix1.begin(), prefix1.end(), mPattern.begin()))
{
string pattern = mPattern;
pattern = std::regex_replace(pattern, std::regex(">="), "");
startFrom = atoi(pattern.c_str());
}
else if(std::equal(prefix2.begin(), prefix2.end(), mPattern.begin()))
{
string pattern = mPattern;
pattern = std::regex_replace(pattern, std::regex(">"), "");
startFrom = atoi(pattern.c_str());
if(startFrom > 0)
{
startFrom++;
}
}
if(startFrom > 0 && startFrom < names.size())
{
// Match by > or >=
for(size_t i = startFrom; i < names.size(); i++)
{
result.push_back((int)i);
}
return;
}
bool matchedTopLevel = false;
for(unsigned int i = 0; i < names.size(); i++)
{
// Match by pattern
string name = names[i];
bool topLevel = (name.find_first_of("::") == (name.find_last_of("::") - 1));
if(name.find(mPattern) != std::string::npos)
{
if(topLevel)
{
matchedTopLevel = true;
}
if(matchedTopLevel && topLevel)
{
result.push_back(i);
}
else if(matchedTopLevel == false && topLevel == false)
{
result.push_back(i);
}
}
else
{
matchedTopLevel = false;
}
}
}
CppTestSelector::CppTestSelector(void)
{
}
CppTestSelector::~CppTestSelector(void)
{
}
// may throw a memory exception if push fails
void CppTestSelector::GetTestCases(CppUnit::Test *test, std::vector<std::string>& names)
{
int childCount = 0;
names.push_back (test->getName());
if ((childCount = test->getChildTestCount()) == 0)
return;
for (int i = 0; i < childCount; i++)
GetTestCases (test->getChildTestAt (i), names);
}
// return number of selected tests or suites
// may throw memory exception
int CppTestSelector::SelectTests(CppUnit::Test *suite, CppUnit::TestRunner &testRunner, UISelector &uiSelector, int &numTimes)
{
std::vector<std::string> testNames;
std::vector<int> selectedTests;
size_t i = 0;
numTimes = 1;
GetTestCases(suite, testNames);
uiSelector.GetUserSelection(testNames, selectedTests, numTimes);
CppUnit::Test *testToRun = NULL;
for(i = 0; i< selectedTests.size(); i++)
{
try
{
testToRun = suite->findTest(testNames[selectedTests[i]]);
if(testToRun)
{
testRunner.addTest(testToRun);
}
}
catch (...)
{
//std::cout << "test not found: " << testNames[selectedTests[i]] << std::endl;
// test not found, but this should never happen
// because the test names were found from the given suite
}
}
return (int) i;
}