mirror of
				https://github.com/Telecominfraproject/wlan-cloud-lib-poco.git
				synced 2025-10-30 18:17:50 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			237 lines
		
	
	
		
			7.6 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			237 lines
		
	
	
		
			7.6 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| //
 | |
| // DataDrivenTestCaller.h
 | |
| //
 | |
| // $Id: //poco/Main/CppUnit/include/CppUnit/DataDrivenTestCaller.h#3 $
 | |
| //
 | |
| 
 | |
| 
 | |
| #ifndef CppUnit_DataDrivenTestCaller_INCLUDED
 | |
| #define CppUnit_DataDrivenTestCaller_INCLUDED
 | |
| 
 | |
| 
 | |
| #include "CppUnit/CppUnit.h"
 | |
| #include "CppUnit/TestCase.h"
 | |
| #include "CppUnit/TestSuite.h"
 | |
| #include "CppUnit/CppUnitException.h"
 | |
| #include <iostream>
 | |
| #include <fstream>
 | |
| #include "Poco/File.h"
 | |
| #include "Poco/Path.h"
 | |
| #include "Poco/Exception.h"
 | |
| #include <algorithm>
 | |
| #include <functional>
 | |
| #include <map>
 | |
| 
 | |
| 
 | |
| namespace CppUnit {
 | |
| 
 | |
| /*
 | |
|  * A data driven test case defines the fixture to run a single test with varying input streams. 
 | |
|  * A data driven test is assumed to take an input stream and write its result to an output stream
 | |
|  * which can then be compared to a reference output.
 | |
|  * To do data driven testing
 | |
|  * 1) implement a subclass of TestCase
 | |
|  * 2) define instance variables that store the state of the fixture
 | |
|  * 3) initialize the fixture state by overriding setUp
 | |
|  * 4) clean-up after a test by overriding tearDown.
 | |
|  *
 | |
|  * Each test runs in its own fixture so there
 | |
|  * can be no side effects among test runs.
 | |
|  *
 | |
|  * Each test method must implement the following method signature:
 | |
|  * 
 | |
|  *     void dotest(std::istream& in, std::ostream& out)
 | |
|  *
 | |
|  * Add the test in the suite() method of your test case with the CppUnit_addDataTest macro:
 | |
|  *     
 | |
|  *     CppUnit::TestSuite* pSuite = new CppUnit::TestSuite("WebWidgetsTest");
 | |
|  *     CppUnit_addDataTest(pSuite, WebWidgetsTest, doTest, "tests/", "temp/");
 | |
|  *
 | |
|  * CppUnit_addDataTest expects 5 input parameters:
 | |
|  *  - the first is always a pointer to the suite where the tests are added
 | |
|  *  - the second parameter is the name of your test class
 | |
|  *  - the third parameter is the test method
 | |
|  *  - the fourth parameter contains the directory where the tests are stored. Note that
 | |
|  *  we assume a certain directory structure inside the tests folder. 
 | |
|  *  Input files are stored in the subfolder tests/data, the reference results are stored
 | |
|  *  in the subfolder tests/results. The matching of reference to input data happens automatically via the file
 | |
|  *  base name. For each input a separate test is inserted with the base name as the name of the test.
 | |
|  *  - the last parameter defines where we should generate temporary files
 | |
|  * 
 | |
|  * Note that the test framework guarantees that input and output stream for the test
 | |
|  * method are valid and open. It will also perform the comparision (i.e. does the produced output matches
 | |
|  * the reference output) and close the input and outout streams.
 | |
|  *
 | |
|  */
 | |
| template <class Fixture>
 | |
| class DataDrivenTestCaller: public TestCase
 | |
| {
 | |
| 	REFERENCEOBJECT (DataDrivenTestCaller)
 | |
| 
 | |
| 	typedef void (Fixture::*TestMethod)(std::istream& in, std::ostream& out);
 | |
| 
 | |
| public:
 | |
| 	DataDrivenTestCaller(const std::string& name, TestMethod test, const Poco::File& in, const Poco::File& out, const Poco::File& expectedResult):
 | |
| 		TestCase(name), 
 | |
| 		_test(test),
 | |
| 		_fixture(new Fixture(name)),
 | |
| 		_in(in),
 | |
| 		_out(out),
 | |
| 		_result(expectedResult)
 | |
| 	{
 | |
| 	}
 | |
| 
 | |
| protected:
 | |
| 	void runTest()
 | |
| 	{
 | |
| 		std::ifstream in(_in.path().c_str(), std::ios::binary);
 | |
| 		if (!in)
 | |
| 			throw Poco::FileNotFoundException("Failed to open file " + _in.path());
 | |
| 
 | |
| 		std::ofstream out(_out.path().c_str(), std::ios::binary);
 | |
| 		if (!out)
 | |
| 			throw Poco::CreateFileException("Failed to open file for writing " + _out.path());
 | |
| 		try
 | |
| 		{
 | |
| 			(_fixture.get()->*_test)(in, out);
 | |
| 		}
 | |
| 		catch(Poco::Exception&)
 | |
| 		{
 | |
| 			// maybe an exception is wanted
 | |
| 			// compare with ref file
 | |
| 			if (_result.exists() && _result.getSize() > 0)
 | |
| 			{
 | |
| 				in.close();
 | |
| 				out.close();
 | |
| 				throw;
 | |
| 			}
 | |
| 		}
 | |
| 		in.close();
 | |
| 		out.close();
 | |
| 		// now compare 
 | |
| 		std::ifstream inRes(_out.path().c_str(), std::ios::binary);
 | |
| 		std::ifstream refRes(_result.path().c_str(), std::ios::binary);
 | |
| 		if (!inRes || !refRes)
 | |
| 			throw Poco::FileNotFoundException("Can't compare with reference file. Reference result missing or file not created " + _out.path());
 | |
| 		while (!inRes.eof())
 | |
| 		{
 | |
| 			int c = inRes.get();
 | |
| 			int c2 = refRes.get();
 | |
| 			if (c != c2)
 | |
| 				throw Poco::DataException("Created file does not match reference file" + _out.path());
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	void setUp()
 | |
| 	{
 | |
| 		_fixture.get()->setUp();
 | |
| 	}
 | |
| 
 | |
| 	void tearDown()
 | |
| 	{
 | |
| 		_fixture.get()->tearDown();
 | |
| 	}
 | |
| 
 | |
| public:
 | |
| 	static void addDirectoryTest(TestSuite* pSuite, typename DataDrivenTestCaller::TestMethod method, const std::string& rootDir, const Poco::Path& tempD)
 | |
| 		/// rootDir contains the name of the root directory (not a path). We will start searching for the root dir in the
 | |
| 		/// current directory and go up until we are at the root level
 | |
| 		/// The root dir must contain a subfolder /data and /results. tempDir will be used to store temporary files
 | |
| 	{
 | |
| 		Poco::Path dir(rootDir);
 | |
| 		std::string cwd(Poco::Path::current());
 | |
| 		Poco::Path cwdPath(cwd);
 | |
| 		Poco::Path p(cwdPath, dir);
 | |
| 		p.makeFile();
 | |
| 		bool found = false;
 | |
| 		while (!found && p.depth() > 0)
 | |
| 		{
 | |
| 			Poco::File f(p);
 | |
| 			if (f.exists())
 | |
| 				found = true;
 | |
| 			else
 | |
| 				p.popDirectory();
 | |
| 		}
 | |
| 		if (!found)
 | |
| 			return;
 | |
| 
 | |
| 		Poco::Path data(p, "data");
 | |
| 		data.makeDirectory();
 | |
| 		Poco::File dataDir(data);
 | |
| 		if (!dataDir.exists())
 | |
| 			return;
 | |
| 		Poco::Path results(p, "results");
 | |
| 		results.makeDirectory();
 | |
| 		Poco::File resDir(results);
 | |
| 		if (!resDir.exists())
 | |
| 			return;
 | |
| 		Poco::Path temp(tempD);
 | |
| 		Poco::File tempDir(temp);
 | |
| 		temp.makeDirectory();
 | |
| 		tempDir.createDirectories();
 | |
| 		std::vector<std::string> inputFiles;
 | |
| 		dataDir.list(inputFiles);
 | |
| 		std::vector<std::string> referenceFiles;
 | |
| 		resDir.list(referenceFiles);
 | |
| 		std::sort(inputFiles.begin(), inputFiles.end(), std::less<std::string>());
 | |
| 		std::sort(referenceFiles.begin(), referenceFiles.end(), std::less<std::string>());
 | |
| 		typedef std::pair<Poco::File, Poco::File> InputRef;
 | |
| 		std::map<std::string, InputRef> pairs;
 | |
| 		// now map input to reference via basename
 | |
| 		std::vector<std::string>::const_iterator itIn = inputFiles.begin();
 | |
| 		std::vector<std::string>::const_iterator itInEnd = inputFiles.end();
 | |
| 		for (; itIn != itInEnd; ++itIn)
 | |
| 		{
 | |
| 			std::cout << *itIn << std::endl;
 | |
| 			Poco::Path inTmp(data, *itIn);
 | |
| 			Poco::File inFTmp(inTmp);
 | |
| 			if (!inTmp.getBaseName().empty())
 | |
| 				pairs.insert(std::make_pair(inTmp.getBaseName(), std::make_pair(inFTmp, Poco::File())));
 | |
| 		}
 | |
| 
 | |
| 		std::vector<std::string>::const_iterator itRef = referenceFiles.begin();
 | |
| 		std::vector<std::string>::const_iterator itRefEnd = referenceFiles.end();
 | |
| 		for (; itRef != itRefEnd; ++itRef)
 | |
| 		{
 | |
| 			Poco::Path refTmp(results, *itRef);
 | |
| 			Poco::File refFTmp(refTmp);
 | |
| 			std::map<std::string, InputRef>::iterator it = pairs.find(refTmp.getBaseName());
 | |
| 			if (it != pairs.end())
 | |
| 				it->second.second = refFTmp;
 | |
| 		}
 | |
| 
 | |
| 		std::map<std::string, InputRef>::const_iterator itP = pairs.begin();
 | |
| 		std::map<std::string, InputRef>::const_iterator itPEnd = pairs.end();
 | |
| 		for (; itP != itPEnd; ++itP)
 | |
| 		{
 | |
| 			std::string fileName;
 | |
| 			
 | |
| 			if (!itP->second.second.path().empty())
 | |
| 			{
 | |
| 				Poco::Path refTmp(itP->second.second.path());
 | |
| 				fileName = refTmp.getFileName();
 | |
| 			}
 | |
| 			else
 | |
| 				fileName = itP->first + ".tmp";
 | |
| 			Poco::Path outTmp(temp, fileName);
 | |
| 			Poco::File outFTmp(outTmp);
 | |
| 			pSuite->addTest(new DataDrivenTestCaller(itP->first, method, itP->second.first, outFTmp, itP->second.second));
 | |
| 		}
 | |
| 	}
 | |
| private:
 | |
| 	TestMethod             _test;
 | |
| 	std::auto_ptr<Fixture> _fixture;
 | |
| 	Poco::File             _in;
 | |
| 	Poco::File             _out;
 | |
| 	Poco::File             _result;
 | |
| };
 | |
| 
 | |
| 
 | |
| } // namespace CppUnit
 | |
| 
 | |
| 
 | |
| #define CppUnit_addDataTest(suite, cls, mth, rootDir, tempDir) \
 | |
| 	CppUnit::DataDrivenTestCaller<cls>::addDirectoryTest(suite, &cls::mth, rootDir, tempDir)
 | |
| 
 | |
| #endif // DataDrivenTestCaller_INCLUDED
 | 
