valfaudes.cpp
Go to the documentation of this file.
1/** valfaudes.cpp Utility validate test cases */
2
3/* FAU Discrete Event Systems Library (libfaudes)
4
5 Copyright (C) 2025 Thomas Moor
6 Exclusive copyright is granted to Klaus Schmidt
7
8 This library is free software; you can redistribute it and/or
9 modify it under the terms of the GNU Lesser General Public
10 License as published by the Free Software Foundation; either
11 version 2.1 of the License, or (at your option) any later version.
12
13 This library is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public
19 License along with this library; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
21
22#include "corefaudes.h"
23
24using namespace faudes;
25
26// mini help
27void usage(const std::string& msg="") {
28 if (msg != "") {
29 std::cerr << "valfaudes: error: " << msg << std::endl;
30 std::cerr << std::endl;
31 std::flush(std::cerr);
32 std::flush(std::cout);
33 exit(1);
34 }
35 std::cerr << "valfaudes --- run and validate testcases (" << faudes::VersionString() << ")" << std::endl;
36 std::cerr << std::endl;
37 std::cerr << "usage:" << std::endl;
38 std::cerr << " valfaudes [-v|q] [-t <tmp>] <main-in>" << std::endl;
39 std::cerr << std::endl;
40 std::cerr << "with:" << std::endl;
41 std::cerr << " <main-in> specify '.prot' or a '.flx' input" << std::endl;
42 std::cerr << std::endl;
43 std::cerr << "options:" << std::endl;
44 std::cerr << " -t <tmp> temp dir for extracting/validating flx packages" << std::endl;
45 std::cerr << std::endl;
46 std::cerr << "note: this tool is meant to facilitate the libFAUDES build process and" << std::endl;
47 std::cerr << "relies on std libFAUDES folder layout" << std::endl;
48 std::cerr << std::endl;
49 exit(0);
50}
51
52// config
53bool mOptV=false;
54bool mOptQ=false;
55std::string mLibFaudes=".";
56std::string mLuaFaudes;
57std::string mArgFile;
58std::string mProtFile;
59std::string mTestCase;
60std::string mTestType;
61std::string mTestPath;
62std::string mFlxFile;
63std::string mBinFile;
64std::string mLuaFile;
65std::string mPyFile;
66std::string mTmpProtFile;
67std::string mTmpDir;
68std::string mPython="";
69std::string mPythonPath="";
70
71// helper: exe suffix
72#ifdef FAUDES_POSIX
73std::string exesfx(void) { return "";}
74#endif
75#ifdef FAUDES_WINDOWS
76std::string exesfx(void) { return ".exe";}
77#endif
78#ifdef FAUDES_GENERIC
79std::string exesfx(void) { return "";}
80#endif
81
82// helper: run system command with return code)
83int runshell(const std::string& command) {
84 std::string cmd=command;
85#ifdef FAUDES_POSIX
86 if(!mOptV)
87 cmd = cmd + " > /dev/null 2>&1 ";
88#endif
89#ifdef FAUDES_WINDOWS
90 cmd=faudes_extpath(cmd);
91 if(!mOptV)
92 cmd = cmd + " > NUL 2>&1";
93#endif
94 if(!mOptQ)
95 std::cout << "valfaudes: running: \"" << cmd << "\"" << std::endl;
96 std::flush(std::cout);
97 std::flush(std::cerr);
98 int res= std::system(cmd.c_str());
99 return res;
100}
101
102// helper: run faudes executable (0<>success)
103int runfexec(const std::string& command, const std::string& arguments="") {
104 std::string cmd=command;
105#ifdef FAUDES_POSIX
106 if(cmd.size()>0)
107 if((cmd.at(0)!='/') && (cmd.at(0)!='.'))
108 cmd= "." + faudes_pathsep() + cmd;
109 if(!arguments.empty())
110 cmd += " " + arguments;
111#endif
112#ifdef FAUDES_WINDOWS
113 cmd=faudes_extpath(cmd);
114 if(!arguments.empty())
115 cmd += " " + arguments;
116#endif
117 return runshell(cmd);
118}
119
120// helper: run system executable (o<>success)
121int runsexec(const std::string& command, const std::string& arguments="") {
122 std::string cmd=command;
123#ifdef FAUDES_POSIX
124 if(!arguments.empty())
125 cmd += " " + arguments;
126#endif
127#ifdef FAUDES_WINDOWS
128 cmd=faudes_extpath(cmd);
129 if(!arguments.empty())
130 cmd += " " + arguments;
131#endif
132 return runshell(cmd);
133}
134
135// helper: diff (0<>match)
136int rundiff(const std::string& file1, const std::string& file2) {
137 std::string cmd;
138#ifdef FAUDES_POSIX
139 cmd= "diff -w --context=3 --show-function-line=mark " + file1 + " " + file2;
140#endif
141#ifdef FAUDES_WINDOWS
142 cmd= "fc /W " + faudes_extpath(file1) + " " + faudes_extpath(file2);
143#endif
144
145 if(!mOptQ)
146 std::cout << "valfaudes: running: \"" << cmd << "\"" << std::endl;
147 std::flush(std::cout);
148 std::flush(std::cerr);
149 return std::system(cmd.c_str());
150}
151
152
153// helper: find luafaudes (true <> success)
154bool findlua(void) {
155 // is it right here?
156 mLuaFaudes="luafaudes"+exesfx();
157 if(FileExists(mLuaFaudes)) return true;
158 // is it in bin?
160 if(FileExists(mLuaFaudes)) return true;
161 // is it in bin one up?
163 if(FileExists(mLuaFaudes)) return true;
164 // is it in bin two up?
166 if(FileExists(mLuaFaudes)) return true;
167 // is it in bin three up?
169 if(FileExists(mLuaFaudes)) return true;
170 // were lost
171 if(!mOptQ) {
172 std::cout << "valfaudes: could not find luafaudes ( last try \"" << mLuaFaudes <<"\""
173 << "from \"" << faudes_getwd() <<"\""<<std::endl;
174 }
175 return false;
176}
177
178// helper: find python (true <> success)
179bool findpython(void) {
180 bool res=false;
181#ifdef FAUDES_POSIX
182 if(runshell("python3 --version")==0) {
183 mPython="python3";
184 res=true;
185 } else {
186 if(runshell("python --version")==0) {
187 mPython="python";
188 res=true;
189 }
190 }
191#endif
192#ifdef FAUDES_WINDOWS
193 if(runshell("python3 --version")==0) {
194 mPython="python3";
195 res=true;
196 } else {
197 if(runshell("py.exe --version")==0) {
198 mPython="py.exe";
199 res=true;
200 }
201 }
202#endif
203 // we are lost
204 if(!res) {
205 if(!mOptQ) {
206 std::cout << "valfaudes: could not find python" <<std::endl;
207 }
208 return false;
209 }
210 // test for faudes module
211 if(FileExists("faudes.py")) {
212 if(!mOptQ) {
213 std::cout << "valfaudes: using faudes module in current directory" <<std::endl;
214 }
215 return true;
216 }
217 // test for faudes module
218 if(FileExists("../../pybindings/obj/faudes.py")) {
219 if(!mOptQ) {
220 std::cout << "valfaudes: using faudes module from pybindings plug-in" <<std::endl;
221 }
222 mPythonPath="../../pybindings/obj";
223 return true;
224 }
225 // were lost
226 if(!mOptQ) {
227 std::cout << "valfaudes: failed to locate faudes module" <<std::endl;
228 }
229 return false;
230}
231
232
233// runner
234int main(int argc, char *argv[]) {
235
236 // primitive command line parser
237 for(int i=1; i<argc; i++) {
238 std::string option(argv[i]);
239 // option: help
240 if((option=="-?") || (option=="--help")) {
241 usage();
242 continue;
243 }
244 // option: verb
245 if((option=="-v") || (option=="--verbose")) {
246 mOptV=true;
247 mOptQ=false;
248 continue;
249 }
250 // option: verb
251 if((option=="-q") || (option=="--quiet")) {
252 mOptV=false;
253 mOptQ=true;
254 continue;
255 }
256 // option: tmp dir
257 if((option=="-t") || (option=="--temp")) {
258 if(++i>argc)
259 usage("cannot read temp dir (-t)");
260 mTmpDir=argv[i];
261 continue;
262 }
263 // option: unknown
264 if(option.c_str()[0]=='-') {
265 usage("unknown option "+ option);
266 continue;
267 }
268 // argument #1 input file
269 if(mArgFile.empty()) {
270 mArgFile=argv[i];
271 continue;
272 }
273 // fail
274 usage("no more than one argument must be specified" );
275 }
276
277 // fail on no input
278 if(mArgFile.empty())
279 usage("no input file specified");
280
281 //report
282 if(!mOptQ)
283 std::cout << "valfaudes: input file: \"" << mArgFile <<"\"" << std::endl;
284
285 // derive config: test type
287 std::string sfx=ExtractSuffix(mArgFile);
288 // case a) its a .prot
289 if(sfx=="prot") {
292 size_t seppos=mTestCase.find_last_of('_');
293 if(seppos==std::string::npos) {
294 usage("could not figure test type (no seperator '_' in '.prot' file)");
295 }
296 mTestType=mTestCase.substr(seppos+1);
297 if(ToLowerCase(mTestType)=="cpp") {
298 mBinFile=mTestCase.substr(0,seppos);
299 }
300 if(ToLowerCase(mTestType)=="lua") {
302 mLuaFile.at(seppos)='.';
303 }
304 if(ToLowerCase(mTestType)=="py") {
306 mPyFile.at(seppos)='.';
307 }
308 mTmpProtFile= "tmp_" + mTestCase + ".prot";
309 mTestCase = mTestCase.substr(0,seppos);
310 }
311 // case b) its an .flx
312 if(sfx=="flx") {
313 mTestType="flx";
317 }
318 // failed test type
319 if(mTestType.empty()) {
320 usage("could not figure test type");
321 }
322
323 // derive config: working dir (for .prot files)
324 if(sfx=="prot") {
325 size_t datapos=mTestPath.rfind("data");
326 if(datapos==std::string::npos) {
327 usage("could not figure working dir (path must end with 'data' [a])");
328 }
329 if(datapos!=mTestPath.size()-5) {
330 usage("could not figure working dir (path must end with 'data' [b])");
331 }
332 if(datapos==0) {
333 usage("could not figure working dir (layout mismatch)");
334 }
335 mTestPath=mTestPath.substr(0,datapos-1);
337 }
338
339 //report
340 if(!mOptQ) {
341 std::cout << "valfaudes:" << std::endl;
342 std::cout << " test case: \"" << mTestCase <<"\"" << std::endl;
343 std::cout << " test working dir: \"" << mTestPath <<"\"" << std::endl;
344 if(!mBinFile.empty())
345 std::cout << " exeutable: \"" << mBinFile <<"\"" << std::endl;
346 if(!mLuaFile.empty())
347 std::cout << " lua script: \"" << mLuaFile <<"\"" << std::endl;
348 if(!mPyFile.empty())
349 std::cout << " python script: \"" << mPyFile <<"\"" << std::endl;
350 if(!mFlxFile.empty())
351 std::cout << " flx file: \"" << mFlxFile <<"\"" << std::endl;
352 }
353
354 // result code (0 for ok)
355 int testok=-1;
356
357 // is there nothing to test?
358 if(mBinFile.empty() && mLuaFile.empty() && mFlxFile.empty() && mPyFile.empty()) {
359 std::cout << "valfaudes: error: nothing we can validate" << std::endl;
360 return 1;
361 }
362
363 // change working dir
364 std::string pwd=faudes_getwd();
365 if(pwd.empty()) {
366 usage("could not figure current working dir");
367 }
368 int cdok=faudes_chdir(mTestPath);
369 if(cdok!=0) {
370 usage("could change to test working dir");
371 }
372
373 // cpp tutorials
374 if(!mBinFile.empty()) {
375 testok=runfexec(mBinFile);
376 }
377
378 // lua tutorials
379 if(!mLuaFile.empty()) {
380#ifndef FAUDES_PLUGIN_LUABINDINGS
381 std::cout << "valfaudes: no luabindings, silently skipping test case" << std::endl;
382 testok=0;
383#else
384 if(!findlua()) {
385 usage("could not find luafaudes");
386 }
387 std::string arg=mLuaFile;
388 if(mOptV)
389 arg= "-d " + arg;
390 testok=runfexec(mLuaFaudes,arg);
391#endif
392 }
393
394 // python tutorials
395 if(!mPyFile.empty()) {
396#ifndef FAUDES_PLUGIN_PYBINDINGS
397 std::cout << "valfaudes: no pybindings, silently skipping test case" << std::endl;
398 testok=0;
399#else
400 if(!findpython()) {
401 usage("could not find python");
402 }
403 if(mPythonPath=="") {
404 testok=runsexec(mPython,mPyFile);
405 } else {
406 std::string args= "-c ";
407 args += "\"";
408 args += "import sys; sys.path.append('" + mPythonPath +"'); ";
409 args += "import faudes; faudes.FAUDES_TEST_NAME='" + mPyFile +"'; ";
410 args += " exec(open('" + mPyFile + "').read()); ";
411 args += "\"";
412 testok=runsexec(mPython,args);
413 }
414#endif
415 }
416
417
418 // flx extensions
419 if(!mFlxFile.empty()) {
420 // alt: have absolut paths
421 std::string args;
422 args+= "-tbin ../bin";
423 args+= " -t";
424 args+= " ../"+mArgFile+" .";
425 testok=runfexec("../bin/flxinstall",args);
426 }
427
428 // go back to original dir
429 int pwdok=faudes_chdir(pwd);
430 if(pwdok!=0) {
431 usage("could nod change back working dir");
432 }
433
434 // fail if no tests run
435 if(testok!=0) {
436 usage("test failed to run");
437 }
438
439 // flx is done here
440 if(!mFlxFile.empty()) {
441 return testok;
442 }
443
444 // fail if no protocol found
446 usage("no test results (expected at \""+mTmpProtFile+"\")");
447 }
448
449 // do diff
451 if(!mOptQ) {
452 if(testok==0)
453 std::cout << "valfaudes: no differences detected: test passed" << std::endl;
454 else
455 std::cout << "valfaudes: diff returncode \"" << testok << "\": test failed" << std::endl;
456 }
457
458 return (testok==0 ? 0 : 1);
459}
int main()
int faudes_chdir(const std::string &nwd)
std::string faudes_extpath(const std::string &rPath)
const std::string & faudes_pathsep(void)
std::string faudes_getwd(void)
std::string VersionString()
std::string ExtractDirectory(const std::string &rFullPath)
std::string PrependPath(const std::string &rLeft, const std::string &rRight)
std::string ExtractFilename(const std::string &rFullPath)
std::string ToLowerCase(const std::string &rString)
std::string ExtractBasename(const std::string &rFullPath)
bool FileExists(const std::string &rFilename)
std::string ExtractSuffix(const std::string &rFullPath)
std::string mTestType
Definition valfaudes.cpp:60
std::string exesfx(void)
Definition valfaudes.cpp:79
std::string mPythonPath
Definition valfaudes.cpp:69
std::string mLuaFaudes
Definition valfaudes.cpp:56
int runfexec(const std::string &command, const std::string &arguments="")
std::string mProtFile
Definition valfaudes.cpp:58
std::string mFlxFile
Definition valfaudes.cpp:62
std::string mPyFile
Definition valfaudes.cpp:65
bool mOptV
Definition valfaudes.cpp:53
std::string mArgFile
Definition valfaudes.cpp:57
void usage(const std::string &msg="")
Definition valfaudes.cpp:27
int runsexec(const std::string &command, const std::string &arguments="")
std::string mPython
Definition valfaudes.cpp:68
std::string mBinFile
Definition valfaudes.cpp:63
std::string mTmpDir
Definition valfaudes.cpp:67
std::string mLuaFile
Definition valfaudes.cpp:64
bool mOptQ
Definition valfaudes.cpp:54
int rundiff(const std::string &file1, const std::string &file2)
std::string mTmpProtFile
Definition valfaudes.cpp:66
std::string mTestCase
Definition valfaudes.cpp:59
std::string mTestPath
Definition valfaudes.cpp:61
std::string mLibFaudes
Definition valfaudes.cpp:55
int runshell(const std::string &command)
Definition valfaudes.cpp:83
bool findpython(void)
bool findlua(void)

libFAUDES 2.34d --- 2026.03.11 --- c++ api documentaion by doxygen