61 TestData::TestData(
const String& Name,
66 : TestName(Name), Results(Result), FunctionName(FuncName), FileName(File), LineNumber(Line)
70 : TestName( Node.attribute(
"TestName").as_string() ),
72 FunctionName( Node.attribute(
"FunctionName").as_string() ),
73 FileName( Node.attribute(
"FileName").as_string() ),
74 LineNumber( Node.attribute(
"LineNumber").as_uint() )
82 std::stringstream Snippet;
83 Snippet <<
"<TestData "
84 <<
"TestName=\"" <<
TestName <<
"\" "
87 <<
"FileName=\"" <<
FileName <<
"\" "
95 for(CoreTestGroup::iterator Iter=TestGroups.begin(); Iter!=TestGroups.end(); ++Iter)
96 { cout << Iter->first << std::endl; }
103 size_t LastSpace=Line.rfind(
' ');
128 LongestNameLength(0),
129 DoSubProcessTest(false),
130 DoAutomaticTest(false),
131 DoInteractiveTest(false),
136 : TestOutput(OtherGroup.TestOutput.str()),
137 TestError(OtherGroup.TestError.str()),
140 DoSubProcessTest(OtherGroup.DoSubProcessTest),
141 DoAutomaticTest(OtherGroup.DoAutomaticTest),
142 DoInteractiveTest(OtherGroup.DoInteractiveTest),
144 { insert(OtherGroup.begin(),OtherGroup.end()); }
162 TestOutput << std::endl <<
"<AutomaticTestOutput><![CDATA[" << std::endl;
163 TestError << std::endl <<
"<AutomaticTestError><![CDATA[" << std::endl;
170 }
catch(exception& e){
171 TestError <<
"Caught an unhandled exception while doing RunAutomaticTests()." << endl
172 <<
"Message: " << e.what() << endl;
175 TestOutput << std::endl <<
"]]></AutomaticTestOutput>" << std::endl;
176 TestError << std::endl <<
"]]></AutomaticTestError>" << std::endl;
181 TestOutput << std::endl <<
"<InteractiveTestOutput><![CDATA[" << std::endl;
182 TestError << std::endl <<
"<InteractiveTestError><![CDATA[" << std::endl;
189 }
catch(exception& e){
190 TestError <<
"Caught an unhandled exception while doing RunInteractiveTests()." << endl
191 <<
"Message: " << e.what() << endl;
194 TestOutput << std::endl <<
"]]></InteractiveTestOutput>" << std::endl;
195 TestError << std::endl <<
"]]></InteractiveTestError>" << std::endl;
230 {
throw std::invalid_argument(
"Invalid Test Name, contains one or more space character ( ), TestName: \"" + CurrentTest.
TestName +
"\""); }
232 {
throw std::invalid_argument(
"Invalid Test Name, contains one or more double quote (\") character(s), TestName: \"" + CurrentTest.
TestName +
"\""); }
234 if(this->
Name().length())
236 if(this->
Name().npos != this->
Name().find(
" "))
237 {
throw std::invalid_argument(
"Invalid UnitTestGroup Name, contains one or more space character ( ), name: \"" + this->
Name() +
"\""); }
238 if(this->
Name().npos != this->
Name().find(
"\""))
239 {
throw std::invalid_argument(
"Invalid UnitTestGroup Name, contains one or more double quote (\"), name: \"" + this->
Name() +
"\""); }
243 TestDataStorage::iterator PreExisting = this->find(CurrentTest.
TestName);
244 if(this->end()!=PreExisting)
249 if (PreExisting->Results <= CurrentTest.
Results)
251 this->erase(PreExisting);
252 this->insert(CurrentTest);
257 this->erase(PreExisting);
258 this->insert(CurrentTest);
262 if (PreExisting->Results >= CurrentTest.
Results)
264 this->erase(PreExisting);
265 this->insert(CurrentTest);
273 this->insert(CurrentTest);
295 insert(rhs.begin(),rhs.end());
305 throw std::invalid_argument(
306 String(
"UnitTestGroup::AddTestsFromXML can only handle XML but was passed an empty file. Expected results from ")
311 if(
String(
"UnitTestGroup")==Node.name())
313 for(pugi::xml_node::iterator Iter = Node.begin(); Iter!=Node.end(); Iter++)
315 String CurrentName(Iter->name());
316 if(
String(
"TestData")==CurrentName)
318 else if(
String(
"UnitTestOutput")==CurrentName)
324 else if(
String(
"UnitTestError")==CurrentName)
326 String Text(Iter->text().as_string());
328 {
TestError << std::endl << Text << std::endl; }
332 throw std::invalid_argument(
333 String(
"UnitTestGroup::AddTestsFromXML Invalid subelement passed from ")
341 throw std::invalid_argument(
342 String(
"UnitTestGroup::AddTestsFromXML can only handle XML with \"UnitTestGroup\" as root element. Expected results from ")
350 String Results(
"\n<UnitTestGroup>");
351 for (iterator Iter=this->begin(); Iter!=this->end(); Iter++)
352 { Results +=
"\n " + Iter->GetAsXML(); }
353 Results +=
"\n<UnitTestOutput>";
355 Results +=
"\n</UnitTestOutput>";
356 Results +=
"\n<UnitTestError>";
358 Results +=
"\n</UnitTestError>";
359 Results +=
"\n</UnitTestGroup>";
365 std::vector<unsigned int> TestCounts;
366 TestCounts.insert(TestCounts.end(),1+(
unsigned int)
NotApplicable, 0);
368 if(FullOutput && HeaderOutput)
377 for (TestDataStorage::iterator Iter=this->begin(); Iter!=this->end(); Iter++)
385 if(Iter->FileName.length())
386 { Output <<
" File: " << Iter->FileName; }
387 if(Iter->FunctionName.length())
388 { Output <<
" Function: " << Iter->FunctionName; }
390 { Output <<
" Line: " << Iter->LineNumber; }
391 if(Iter->FileName.length()==0 && Iter->FunctionName.length() == 0 && Iter->LineNumber==0)
392 { Output <<
" No Metadata available able issue, use TEST to capture"; }
398 if (Iter->Results && Iter->FileName.length() && Iter->FunctionName.length() && Iter->LineNumber)
400 Error << Iter->FileName <<
":" << Iter->LineNumber
402 <<
" in function " << Iter->FunctionName << endl;
405 TestCounts.at((
unsigned int)Iter->Results)++;
410 Output << std::endl <<
" Results Summary:" << std::endl;
411 for(
unsigned int c=0; c<TestCounts.size();++c)
414 Output <<
" " << ResultName <<
MakePadding(ResultName,16) << TestCounts.at(c) << std::endl;
420 if(FullOutput &&
TestError.str().size()>5 )
421 { Error <<
"Errors: " <<
TestError.str(); }
434 }
catch(exception& e){
435 TestError <<
"Caught an unhandled exception while adding results for " << TestName << endl
436 <<
"Message: " << e.what() << endl;
446 #endif // \ _testdata_cpp
String LaunchSubProcessTest(const String &Argument=String(""))
Tests should use this to launch things that need sheltering from segfaults and similar faults...
Mezzanine::String GetExecutableName()
Get the command/executable name used to invoke this at the command prompt.
void LaunchAutomaticTest()
This launches all the automated tests on the derived class if the flag is set to run them otherwise i...
String MakePadding(String Leader, unsigned int Column)
Creates some blank spaces, useful for controlling the vertical location of console text...
virtual void Test(bool TestCondition, const String &TestName, TestResult IfFalse=Testing::Failed, TestResult IfTrue=Testing::Success, const String &FuncName="", const String &File="", Mezzanine::Whole Line=0)
Interpret Boolean value as a test result. Also Prepends the name of the current test, as returned by Name() + "::", to ease test scoping.
std::streambuf * CoutStreamBuf
Used to store the buffer connected to the stdout while it is being redirected.
Used to aplly RAII to Stdout and STDERR buffers/streams.
Mezzanine::String TestResultToString(TestResult Convertable)
This converts A test result enum value into a String matching the identifier name.
virtual bool HasAutomaticTests() const
Used only to report skipped tests.
std::map< Mezzanine::String, UnitTestGroup * > CoreTestGroup
A group of testnames and the Actual test that implement those test(s).
virtual void ShouldRunAutomaticTests()
Sets a flag that indicatesz that is the process that should run this subprocess.
This is the default behavior, because it is presumed failures should be visible so they can be fixed...
Just Overwrite, useful if the test was defaulted to a low value.
void CaptureOutputBuffers()
This will direct any output that would have gone to an external process via cout to TestOutput Instea...
String rtrim(const String &t)
Take the whitespace off the end of a String.
This is not even a kind of failure, This is used to when referencing a test, so if this winds up comi...
Test was simply not ran at the behest of the user.
void LaunchInteractiveTest()
This launches all the interactice tests on the derived class if the flag is set to run them otherwise...
unsigned int LongestNameLength
Some basic variable for tracking simple statistics.
Mezzanine::String FileName
The File The test happened in.
virtual void RunSubprocessTest(const Mezzanine::String &Arg)
Does nothing by default, tests which need to run code in a subprocess should override this...
Mezzanine::Whole LineNumber
What line in the file this test occurred when the test was compiled.
bool DoSubProcessTest
Set to false if subprocess tests should not be executed. True if they should.
virtual void RunTests()
This will call RunAutomaticTests based on the values passed.
bool DoInteractiveTest
Sets the flag to run interactive tests.
std::streambuf * CerrStreamBuf
Used to store the buffer connected to the stderr while it is being redirected.
bool operator<(const TestData &Rhs) const
Used to sort TestData in std::std and other sorted containers, by TestName.
A single group of tests, suitable for being all the tests of a small subsystem or single class...
Mezzanine::String GetSubSubProcessArgument()
If a test needs to pass a string to a subsubprocess it will get stored here.
int PrintList(CoreTestGroup &TestGroups)
Print all the groups that exist in a given CoreTestGroup.
OverWriteResults
This is used to control the behavior of the function UnitTestGroup::AddTestResult() ...
The information about a test and how to easily find it in the filesystem.
TestResult StringToTestResult(Mezzanine::String Text)
Roughly convert a String to a TestResult.
TestResult Results
How did the test turn out.
virtual void ShouldRunSubProcessTests()
Sets a flag that indicatesz that is the process that should run this subprocess.
bool DoAutomaticTest
Set the flag to run automatic tests.
TestResult
Return values from tests.
Normal exit all tests skipped or better.
String GetCommandResults(String Command)
Execute a command in a process, piping its standard output to a file.
String GetAsXML() const
Get the Whole UnitTestGroup as a valid XML document.
This is a supplementary running a test, if a results already exists, skip writing anything...
const UnitTestGroup & operator+=(const UnitTestGroup &rhs)
Add all the items in another UnitTestGroup to this one.
void RestoreOutputBuffers()
This will direct any error messages that would have gone to an external process via cerr to TestOutpu...
virtual void ShouldRunInteractiveTests()
Sets a flag that indicatesz that is the process that should run this subprocess.
The bulk of the engine components go in this namspace.
unsigned long Whole
Whole is an unsigned integer, it will be at least 32bits in size.
virtual bool HasInteractiveTests() const
Used only to report skipped tests.
TestData StringToTestData(Mezzanine::String Line)
Trim the whitespace from a line of text and try to interpret the remains as TestResults and a testnam...
UnitTestGroup()
Default constructor.
virtual Mezzanine::String Name()
Get Name of this UnitTestGroup.
TestData(const String &Name="", TestResult Result=Testing::Success, const String &FuncName="", const String &File="", Mezzanine::Whole Line=0)
Create a TestData.
virtual void RunAutomaticTests()
This should be overloaded to run all tests that do require not user interaction.
virtual void DisplayResults(std::ostream &Output=std::cout, std::ostream &Error=std::cerr, bool Summary=true, bool FullOutput=true, bool HeaderOutput=true)
Print the results or save them to a file.
std::stringstream TestError
A destination for errors.
Mezzanine::String FunctionName
The function the test was called from.
std::stringstream TestOutput
A destination for all normal ouput in the tests.
String GetAsXML() const
Return a snippet of xml describing this TestData.
std::string String
A datatype used to a series of characters.
void AddTestsFromXML(pugi::xml_node Node)
Create and add all the tests in a given piece of parsed xml.
void AddTestResult(TestData CurrentTest, OverWriteResults Behavior=OverWriteIfLessSuccessful)
Its expected that tests will be inserted using this.
Mezzanine::String TestName
The name of a given test.
virtual bool HasSubprocessTest() const
If this returns false then the test suite treats it like any other test, if true then it enables some...
Overwrite only if the result is better than the old result.
virtual void RunInteractiveTests()
This should be overloaded to run all tests require user interaction.