1 /***************************************************************************
2 copyright : (C) by 2003-2004 Stefano Barbato
3 email : stefano@codesink.org
5 $Id: cutee.cxx,v 1.2 2008-10-07 11:47:37 tat Exp $
6 ***************************************************************************/
8 /***************************************************************************
10 * This program is free software; you can redistribute it and/or modify *
11 * it under the terms of the GNU General Public License as published by *
12 * the Free Software Foundation; either version 2 of the License, or *
13 * (at your option) any later version. *
15 ***************************************************************************/
26 #define DEFAULT_RUNNER_EXT __FILE__
27 #define RUNTEST_NAME "runtest"
28 #define CPP_EXT " cc cxx cpp c++ CC CXX CPP C++ "
32 // c++ unit testing easy environment
34 "cutee, C++ Unit Testing Easy Environment, Ver. " CUTEE_VERSION "\n\
35 Copyright (c) 2003-2004 by Stefano Barbato - All rights reserved.\n\
36 Usage: cutee [options]... testfile(s)\n\
37 -m generates runtest source code\n\
38 -o specify the output filename\n\
39 -p generates autocutee.mk to include in your Makefile\n\
40 -k generates autocutee.mk to include in your Makefile.am\n\n";
42 typedef list<string> StringList;
44 #define die(msg) do { do_die_if(1, msg, __LINE__); } while(0);
45 #define die_if(a, msg) do { do_die_if(a, msg, __LINE__); } while(0);
46 void do_die_if(int b, const string& msg, int line)
50 cerr << "(" << line << ") " << msg << endl;
53 #define _( code ) of << code << endl
64 string ifile, ofile, ext;
70 : ext(DEFAULT_RUNNER_EXT),mode(MODE_RUNTEST)
73 void parse(int argc, char **argv)
76 mode = MODE_RUNTEST; // default
77 while((ret=getopt(argc, argv, "mo:pk")) != -1)
88 mode = MODE_AUTOMAKEFILE;
100 die_if( (argc - optind) != 1 || ofile.empty(), g_usage);
101 for(; optind < argc; optind++)
102 ifile = argv[optind];
105 die_if( (argc - optind) != 0 || ofile.empty(), g_usage);
107 case MODE_AUTOMAKEFILE:
108 die_if( (argc - optind) < 1 || ofile.empty(), g_usage);
109 for(i = 0; optind < argc; i++, optind++)
110 ifList.push_back(argv[optind]);
113 die_if( (argc - optind) < 1 || ofile.empty(), g_usage);
114 for(i = 0; optind < argc; i++, optind++)
115 ifList.push_back(argv[optind]);
122 class String2WordList
127 typedef list<string> WordList;
128 typedef list<string>::iterator iterator;
129 typedef list<string>::const_iterator const_iterator;
131 String2WordList(const string& s)
138 inline bool isWordChar(char c) const
140 return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
141 (c >= '0' && c <= '9') || (c == '_');
146 string::const_iterator beg = mS.begin(), end = mS.end();
147 for( ; beg != end; ++beg)
154 mList.push_back(word);
155 word.erase(word.begin(), word.end());
159 mList.push_back(word);
161 iterator begin() { return mList.begin(); }
162 iterator end() { return mList.end(); }
163 const_iterator begin() const { return mList.begin(); }
164 const_iterator end() const { return mList.end(); }
169 GenMain(const string& ofqn)
175 ofstream of(mFqn.c_str());
176 die_if(!of.is_open(),
177 string("Unable to open output file: ") + mFqn);
179 _( "#include <iostream>" );
180 _( "#include <iomanip>" );
181 _( "#include \"cutee.h\"" );
182 _( "using namespace std;" );
183 _( "using namespace cutee;" );
185 _( "// static vars initialization" );
186 _( "CuteeTest* TestList::list[MAX_TEST_COUNT];" );
187 _( "int TestList::list_idx = 0;" );
189 _( "int main(int argc, char **argv) " );
191 _( "\tRunner r(argc, argv);" );
201 GenRunTest(const string& ifqn, const string& ofqn)
202 : m_ifqn(ifqn), mFqn(ofqn)
205 void writeRunTestClass()
207 ofstream of(mFqn.c_str());
208 die_if(!of.is_open(),
209 string("Unable to open output file: ") + mFqn);
210 string cn = m_className, fqcn;
214 StringList::const_iterator
215 beg = m_nsList.begin(),
216 end = m_nsList.end();
217 for(; beg != end; ++beg)
221 _( "#include \"cutee.h\"" );
222 _( "#include \"" << m_ifqn << "\"" );
223 _( "using namespace cutee;" );
224 _( "struct run_" << cn << ": public " << fqcn);
230 StringList::const_iterator
231 beg = m_fnList.begin(),
232 end = m_fnList.end();
233 for(; beg != end; ++beg)
234 _( " run_" << *beg << "();" );
237 _( " uint count() { return " << m_fnList.size() << "; }" );
239 _("static struct "<<cn<<"_add_to_list: public cutee::TestList");
241 _( " " << cn << "_add_to_list()" );
243 _( " list[list_idx++] = new run_" << cn << ";" );
245 _( "} " << cn << "_addit;" );
250 START, GET_NAMESPACE_NAME, GET_CLASS_NAME,
251 WAITING_FUNCTION, GET_FUNCTION_NAME
253 ifstream inf(m_ifqn.c_str());
254 die_if(!inf.is_open(),
255 string("Unable to open input file: ") + m_ifqn);
259 while(getline(inf, line))
262 String2WordList s2wl(line);
263 String2WordList::iterator
264 beg = s2wl.begin(), end = s2wl.end();
272 if(w == "namespace" && prev != "using")
273 state = GET_NAMESPACE_NAME;
274 else if(w == "TEST_CLASS")
275 state = GET_CLASS_NAME;
277 case GET_NAMESPACE_NAME:
278 m_nsList.push_back(w);
283 state = WAITING_FUNCTION;
285 case WAITING_FUNCTION:
286 if(w == "TEST_FUNCTION")
287 state = GET_FUNCTION_NAME;
289 case GET_FUNCTION_NAME:
290 m_fnList.push_back(w);
291 state = WAITING_FUNCTION;
294 die("Unknown state");
299 return !m_className.empty();
302 string m_ifqn, mFqn, m_className;
303 StringList m_nsList, m_fnList;
308 GenMakefile(const StringList& ifList, const string& ofqn, const string& ext)
309 : mFqn(ofqn), mExt(ext), mIfList(ifList)
314 string cc_exts = CPP_EXT;
315 ofstream of(mFqn.c_str());
316 die_if(!of.is_open(),
317 string("Unable to open output file: ") + mFqn);
319 _( "# cutee autogen: begin" );
320 _( "CUTEE=./cutee" );
322 of << "object_files=";
323 StringList::const_iterator
324 beg = mIfList.begin(),
326 for(; beg != end; ++beg)
328 GenRunTest grt(*beg, "");
329 if(grt.parseInputFile())
330 of << stripExt(*beg) << "." << stripExt(mExt)
332 else if(cc_exts.find(" " + getExt(*beg) + " ") != string::npos)
333 of << stripExt(*beg) << "." << "o" << " ";
337 beg = mIfList.begin(), end = mIfList.end();
338 for(; beg != end; ++beg)
341 GenRunTest grt(*beg, "");
342 if(grt.parseInputFile())
344 string dst = stripExt(*beg) + "." + mExt;
345 of << dst << ": " << *beg << endl;
346 of << "\t$(CUTEE) -o " << dst << " " << *beg
354 _( RUNTEST_NAME ".cxx: cutee" );
355 _( "\t$(CUTEE) -m -o "RUNTEST_NAME".cxx" );
357 _( RUNTEST_NAME": autocutee.mk " RUNTEST_NAME ".o $(object_files)");
358 _( "\t$(CXX) $(CXXFLAGS) $(LDFLAGS) -o "RUNTEST_NAME" "RUNTEST_NAME".o $(object_files)");
359 _( "\t./"RUNTEST_NAME );
361 _( "# cutee autogen: end ");
364 string getExt(const string& fqn)
366 string::size_type idx = fqn.find_last_of('.');
367 if(idx != string::npos)
368 return string(fqn, ++idx);
372 string stripExt(const string& fqn)
374 string::size_type idx = fqn.find_last_of('.');
375 if(idx != string::npos)
376 return string(fqn, 0, idx);
384 struct GenAutomakefile
386 GenAutomakefile(const StringList& ifList, const string& ofqn, const string& ext)
387 : mFqn(ofqn), mExt(ext), mIfList(ifList)
392 ofstream of(mFqn.c_str());
393 die_if(!of.is_open(),
394 string("Unable to open output file: ") + mFqn);
396 _( "# cutee autogen: begin" );
397 _( "CUTEE=./cutee" );
400 StringList::const_iterator
401 beg = mIfList.begin(),
403 for(; beg != end; ++beg)
405 GenRunTest grt(*beg, "dummy");
406 if(grt.parseInputFile())
407 of << stripPath(stripExt(*beg)) << "." << mExt << " ";
412 _( "%.cutee.cxx: $(srcdir)/%.h" );
413 _( "\t$(CUTEE) -o $@ $<");
417 _( RUNTEST_NAME ".cxx: cutee" );
418 _( "\t$(CUTEE) -m -o $@" );
421 _( RUNTEST_NAME "-clean:");
422 _( "\trm -f autocutee.mk cutee *.o *.cutee.cxx "RUNTEST_NAME" "RUNTEST_NAME".cxx");
423 _( "\ttouch autocutee.mk");
426 _( "EXTRA_PROGRAMS="RUNTEST_NAME );
427 _( RUNTEST_NAME "_SOURCES="RUNTEST_NAME".cxx $(test_files) $(t_runners)");
428 _( RUNTEST_NAME"_DEPENDENCIES=cutee autocutee.mk" );
429 _( "# cutee autogen: end ");
432 string stripPath(const string& fqn)
434 string::size_type idx = fqn.find_last_of('/');
435 if(idx != string::npos)
436 return string(fqn, ++idx);
440 string stripExt(const string& fqn)
442 string::size_type idx = fqn.find_last_of('.');
443 if(idx != string::npos)
444 return string(fqn, 0, idx);
454 int main(int argc, char** argv)
457 clo.parse(argc,argv);
460 GenAutomakefile* pGamk;
467 // i'm using new/delete to avoid a warning (jump to case
468 // label crosses initialization of ...) that throw an
469 // internal compiler error using gcc 3.2(linux)
470 pGrt = new GenRunTest(clo.ifile, clo.ofile);
471 if(pGrt->parseInputFile())
472 pGrt->writeRunTestClass();
476 pGm = new GenMain(clo.ofile);
480 case MODE_AUTOMAKEFILE:
481 pGamk = new GenAutomakefile(clo.ifList, clo.ofile, clo.ext);
482 pGamk->writeMakefile();
486 pGmk = new GenMakefile(clo.ifList, clo.ofile, clo.ext);
487 pGmk->writeMakefile();