1 : /*
2 : * This file is part of MIN Test Framework. Copyright © 2008 Nokia Corporation
3 : * and/or its subsidiary(-ies).
4 : * Contact: Sampo Saaristo
5 : * Contact e-mail: DG.MIN-Support@nokia.com
6 : *
7 : * This program is free software: you can redistribute it and/or modify it
8 : * under the terms of the GNU General Public License as published by the Free
9 : * Software Foundation, version 2 of the License.
10 : *
11 : * This program is distributed in the hope that it will be useful, but WITHOUT
12 : * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 : * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 : * more details. You should have received a copy of the GNU General Public
15 : * License along with this program. If not, see
16 : * <http://www.gnu.org/licenses/>.
17 : */
18 :
19 : /**
20 : * @file cli.c
21 : * @version 0.1
22 : * @brief This file contains implementation of commandline interface
23 : * plugin for MIN
24 : */
25 :
26 : /* ------------------------------------------------------------------------- */
27 : /* INCLUDE FILES */
28 : #include <string.h>
29 : #include <stdlib.h>
30 : #include <unistd.h>
31 : #include <pthread.h>
32 :
33 : #include <cli.h>
34 : #include <min_system_logger.h>
35 : #include <min_plugin_interface.h>
36 : #include <min_text.h>
37 : #include <min_logger.h>
38 : #include <tllib.h>
39 :
40 : /* ------------------------------------------------------------------------- */
41 : /* EXTERNAL DATA STRUCTURES */
42 : /* None */
43 :
44 : /* ------------------------------------------------------------------------- */
45 : /* EXTERNAL GLOBAL VARIABLES */
46 : /* None */
47 :
48 : /* ------------------------------------------------------------------------- */
49 : /* EXTERNAL FUNCTION PROTOTYPES */
50 : /* None */
51 :
52 :
53 : /* ------------------------------------------------------------------------- */
54 : /* GLOBAL VARIABLES */
55 : /* None */
56 :
57 : /* ------------------------------------------------------------------------- */
58 : /* LOCAL GLOBAL VARIABLES */
59 : DLList *available_modules = INITPTR;
60 : DLList *case_list_ = INITPTR;
61 : DLList *executed_case_list_ = INITPTR;
62 : cli_opts cliopts;
63 : unsigned ready_module_count_ = 0;
64 : unsigned available_case_count_ = 0;
65 : unsigned case_result_count_ = 0;
66 : unsigned exit_ = 0;
67 : eapiOut_t min_clbk_;
68 : /* ------------------------------------------------------------------------- */
69 : /* FORWARD DECLARATIONS */
70 : /* ------------------------------------------------------------------------- */
71 : struct ExecutedTestCase;
72 :
73 : /* ------------------------------------------------------------------------- */
74 : /* LOCAL CONSTANTS AND MACROS */
75 : /* ------------------------------------------------------------------------- */
76 : /* None */
77 :
78 : /* ------------------------------------------------------------------------- */
79 : /* LOCAL FUNCTION PROTOTYPES */
80 : /* ------------------------------------------------------------------------- */
81 : /** Search a case by id from the case_list_, used with dl_list_find()
82 : * @param a pointer to DLListIterator
83 : * @param b pointer to search key
84 : * @return 0 if found, greater or less than zero if not
85 : */
86 : LOCAL int _find_case_by_id (const void *a, const void *b);
87 : /* ------------------------------------------------------------------------- */
88 : /** Search a module by id from the available_modules, used with dl_list_find()
89 : * @param a pointer to DLListIterator
90 : * @param b pointer to search key
91 : * @return 0 if found, greater or less than zero if not
92 : */
93 : LOCAL int _find_mod_by_id (const void *a, const void *b);
94 : /* ------------------------------------------------------------------------- */
95 : /** Get the ExecutedTestCase struture by test run identifier
96 : * @param testrunid search key
97 : * @return pointer to ExecutedTestCase matching the id or INITPTR
98 : */
99 : LOCAL struct ExecutedTestCase *get_executed_tcase_with_runid (long testrunid);
100 : /* ------------------------------------------------------------------------- */
101 : /** Engine calls this for each module it is configured with
102 : * @param modulename name of the module
103 : * @param moduleid module id
104 : */
105 : LOCAL void pl_new_module (char *modulename, unsigned moduleid);
106 : /* ------------------------------------------------------------------------- */
107 : /** Engine calls this when module adding fails
108 : * @param modulename name of the module
109 : */
110 : LOCAL void pl_no_module (char *modulename);
111 : /* ------------------------------------------------------------------------- */
112 : /** Engine calls this when all the cases for module are reported
113 : * @param moduleid module id
114 : */
115 : LOCAL void pl_module_ready (unsigned moduleid);
116 : /* ------------------------------------------------------------------------- */
117 : /** Engine calls this for each new test case
118 : * @param moduleid id of the module this test case belongs to
119 : * @param caseid id of the test case
120 : * @param casetitle test case title
121 : */
122 : LOCAL void pl_new_case (unsigned moduleid, unsigned caseid, char *casetitle);
123 : /* ------------------------------------------------------------------------- */
124 : /** Engine calls this when test case has been started
125 : * @param moduleid id of the module this test case belongs to
126 : * @param caseid id of the test case
127 : * @param testrunid identifier for the test run
128 : */
129 : LOCAL void pl_case_started (unsigned moduleid,
130 : unsigned caseid,
131 : long testrunid);
132 : /* ------------------------------------------------------------------------- */
133 : /** Engine calls this when it when test case has finnished
134 : * @param testrunid identifier for the test run
135 : * @param result test case result
136 : * @param desc test result description
137 : * @param starttime starting timestamp
138 : * @param endtime time the test case has finnished
139 : */
140 : LOCAL void pl_case_result (long testrunid, int result, char *desc,
141 : long starttime, long endtime);
142 : /* ------------------------------------------------------------------------- */
143 : /** Engine calls this when it when test case has sent print data
144 : * @param testrunid identifier for the test run
145 : * @param message the test case message
146 : */
147 : LOCAL void pl_msg_print (long testrunid, char *message);
148 : /* ------------------------------------------------------------------------- */
149 : /** Engine calls this when it when test case/module sends error message
150 : * @param error the message
151 : */
152 : LOCAL void pl_error_report (char *error);
153 : /* ------------------------------------------------------------------------- */
154 : /** Function that checks if we have ran all the cases and can finnish
155 : */
156 : LOCAL int all_done();
157 : /* ------------------------------------------------------------------------- */
158 : LOCAL void test_module_info(char *libname);
159 : /* ------------------------------------------------------------------------- */
160 : LOCAL char *patch_path (char *p);
161 : /* ------------------------------------------------------------------------- */
162 : /** Displays information of test module library
163 : * @param libname the test module name coming from commandline
164 : */
165 : LOCAL void test_module_info(char *libname);
166 : /* ------------------------------------------------------------------------- */
167 : /* MODULE DATA STRUCTURES */
168 : /** Test module */
169 : typedef struct {
170 : unsigned moduleid_;
171 : Text *modulename_;
172 : } CLIModuleData;
173 : /** Test case */
174 : typedef struct {
175 : unsigned moduleid_;
176 : unsigned caseid_;
177 : Text *casetitle_;
178 : CLIModuleData *module_;
179 : } CLICaseData;
180 : /** Executed test case */
181 : struct ExecutedTestCase {
182 : long runid_;
183 : #define TCASE_STATUS_INVALID 0
184 : #define TCASE_STATUS_ONGOING 1
185 : #define TCASE_STATUS_FINNISHED 2
186 : unsigned status_;
187 : int result_;
188 : Text *resultdesc_;
189 : long starttime_;
190 : long endtime_;
191 : CLICaseData *case_;
192 : };
193 :
194 :
195 : /* ------------------------------------------------------------------------- */
196 : /* ==================== LOCAL FUNCTIONS ==================================== */
197 : /* ------------------------------------------------------------------------- */
198 : LOCAL int _find_case_by_id (const void *a, const void *b)
199 610 : {
200 610 : CLICaseData * tmp1 = (CLICaseData*)a;
201 610 : unsigned * tmp2 = (unsigned*)b;
202 :
203 610 : if (tmp1->caseid_==(*tmp2)) return 0;
204 544 : else return -1;
205 : }
206 : /* ------------------------------------------------------------------------- */
207 : LOCAL int _find_mod_by_id (const void *a, const void *b)
208 66 : {
209 66 : CLIModuleData * tmp1 = (CLIModuleData*)a;
210 66 : unsigned * tmp2 = (unsigned*)b;
211 :
212 66 : if (tmp1->moduleid_==(*tmp2)) return 0;
213 0 : else return -1;
214 : }
215 : /* ------------------------------------------------------------------------- */
216 : LOCAL struct ExecutedTestCase *get_executed_tcase_with_runid (long testrunid)
217 127 : {
218 : DLListIterator it;
219 : struct ExecutedTestCase *etc;
220 :
221 127 : for (it = dl_list_head (executed_case_list_);
222 1146 : it != INITPTR;
223 892 : it = dl_list_next (it)) {
224 1019 : etc = (struct ExecutedTestCase *)dl_list_data (it);
225 1019 : if (etc->runid_ == testrunid)
226 127 : return etc;
227 : }
228 :
229 0 : return INITPTR;
230 : }
231 : /* ------------------------------------------------------------------------- */
232 : LOCAL void pl_new_module (char *modulename, unsigned moduleid)
233 14 : {
234 14 : CLIModuleData *cld = INITPTR;
235 :
236 14 : if (available_modules == INITPTR) available_modules = dl_list_create();
237 :
238 : /* add new module to list */
239 14 : cld = NEW(CLIModuleData);
240 14 : cld->moduleid_ = moduleid;
241 14 : cld->modulename_ = tx_create(modulename);
242 14 : dl_list_add (available_modules, (void*)cld);
243 :
244 : return;
245 : }
246 : /* ------------------------------------------------------------------------- */
247 : LOCAL void pl_no_module (char *modulename)
248 0 : {
249 :
250 0 : fprintf (stderr, "Module %s has not been loaded", modulename);
251 0 : }
252 : /* ------------------------------------------------------------------------- */
253 : LOCAL void pl_module_ready (unsigned moduleid)
254 14 : {
255 : DLListIterator it;
256 : CLICaseData *c;
257 : CLIModuleData *m;
258 14 : ready_module_count_ ++;
259 :
260 14 : if (cliopts.display_info_) {
261 0 : it = dl_list_find (dl_list_head (available_modules),
262 : dl_list_tail (available_modules),
263 : _find_mod_by_id,
264 : (void *)&moduleid);
265 0 : if (it == DLListNULLIterator) {
266 0 : fprintf (stderr, "No module with id %d",
267 : moduleid);
268 0 : return;
269 : }
270 0 : m = dl_list_data (it);
271 0 : test_module_info (tx_share_buf (m->modulename_));
272 : }
273 102 : for (it = dl_list_head (case_list_); it != INITPTR;
274 74 : it = dl_list_next (it)) {
275 74 : c = (CLICaseData*)dl_list_data(it);
276 74 : if (c->moduleid_ == moduleid) {
277 66 : if (cliopts.display_info_)
278 0 : printf ("%s\n",
279 : tx_share_buf (c->casetitle_));
280 66 : else if (min_clbk_.start_case)
281 66 : min_clbk_.start_case
282 : (c->moduleid_,
283 : c->caseid_,
284 : 1);
285 : }
286 : }
287 :
288 : }
289 : /* ------------------------------------------------------------------------- */
290 : LOCAL void pl_new_case (unsigned moduleid, unsigned caseid, char *casetitle)
291 66 : {
292 66 : CLICaseData *ccd = INITPTR;
293 : DLListIterator it;
294 :
295 :
296 : /* add new case to some list */
297 66 : if (case_list_==INITPTR) case_list_ = dl_list_create();
298 :
299 66 : it = dl_list_find (dl_list_head (available_modules),
300 : dl_list_tail (available_modules),
301 : _find_mod_by_id,
302 : (void *)&moduleid);
303 66 : if (it == DLListNULLIterator) {
304 0 : fprintf (stderr, "No module with id %d found for case %s\n",
305 : moduleid, casetitle);
306 0 : return;
307 : }
308 :
309 66 : available_case_count_++;
310 :
311 66 : ccd = NEW(CLICaseData);
312 66 : ccd->moduleid_ = moduleid;
313 66 : ccd->caseid_ = caseid;
314 66 : ccd->casetitle_ = tx_create(casetitle);
315 66 : ccd->module_ = dl_list_data (it);
316 :
317 66 : dl_list_add (case_list_,(void*)ccd);
318 : }
319 : /* ------------------------------------------------------------------------- */
320 : LOCAL void pl_case_started (unsigned moduleid,
321 : unsigned caseid,
322 : long testrunid)
323 66 : {
324 66 : struct ExecutedTestCase *tmp = INITPTR;
325 66 : DLListIterator it = DLListNULLIterator;
326 66 : DLListIterator begin = DLListNULLIterator;
327 : CLICaseData *ccd;
328 :
329 : /* Case has been started, add it to the executed cases list and set
330 : * its status to ongoing.
331 : */
332 66 : if (executed_case_list_==INITPTR) {
333 8 : executed_case_list_ = dl_list_create();
334 : }
335 :
336 : /* Running test case details, also pointer to test case details should
337 : be added here.
338 : */
339 66 : tmp = NEW2(struct ExecutedTestCase,1);
340 66 : tmp->status_ = 1;
341 66 : tmp->result_ = -1;
342 66 : tmp->resultdesc_ = INITPTR;
343 66 : tmp->runid_ = testrunid;
344 66 : tmp->starttime_ = 0;
345 66 : tmp->endtime_ = 0;
346 :
347 66 : begin = dl_list_head (case_list_);
348 : do {
349 66 : it = dl_list_find (begin,
350 : dl_list_tail (case_list_),
351 : _find_case_by_id,
352 : (void *)&caseid);
353 66 : if (it==DLListNULLIterator) break;
354 66 : ccd = (CLICaseData*)dl_list_data(it);
355 66 : if (ccd == INITPTR) break;
356 66 : if (ccd->moduleid_!=moduleid) begin = dl_list_next(it);
357 66 : else break;
358 0 : } while (it != DLListNULLIterator);
359 66 : if (it == DLListNULLIterator) {
360 0 : DELETE (tmp);
361 : }
362 66 : tmp->case_ = dl_list_data(it);
363 :
364 : /* add to list */
365 66 : dl_list_add (executed_case_list_,(void*)tmp);
366 66 : }
367 : /* ------------------------------------------------------------------------- */
368 : LOCAL void pl_case_result (long testrunid, int result, char *desc,
369 66 : long starttime, long endtime){
370 :
371 : struct ExecutedTestCase *etc;
372 66 : Text *txt = tx_create(" ");
373 66 : struct tm *timeinfo = INITPTR;
374 : char end_time [20];
375 :
376 66 : etc = get_executed_tcase_with_runid (testrunid);
377 66 : if (etc == INITPTR) {
378 0 : fprintf (stderr, "No test found with run id %ld", testrunid);
379 0 : return;
380 : }
381 66 : if (etc->status_ != TCASE_STATUS_ONGOING) {
382 0 : fprintf (stderr, "Test result for a case with status "
383 : "other than ogoing");
384 : }
385 66 : etc->status_ = TCASE_STATUS_FINNISHED;
386 66 : etc->resultdesc_ = tx_create (desc);
387 66 : etc->result_ = result;
388 66 : etc->starttime_ = starttime;
389 66 : etc->endtime_ = endtime;
390 :
391 66 : memset (end_time, 0x0, 20);
392 66 : timeinfo = localtime ((time_t *) & (etc->endtime_));
393 66 : strftime (end_time, 20, "%I:%M:%S",
394 : timeinfo);
395 66 : tx_c_prepend (txt, end_time);
396 66 : switch (result) {
397 : case TP_TIMEOUTED:
398 0 : tx_c_append (txt, "Timeouted ");
399 0 : break;
400 : case TP_CRASHED:
401 0 : tx_c_append (txt, "Crashed ");
402 0 : break;
403 : case TP_PASSED:
404 58 : tx_c_append (txt, "Passed ");
405 58 : break;
406 : case TP_FAILED:
407 8 : tx_c_append (txt, "Failed ");
408 8 : break;
409 : case TP_NC:
410 0 : tx_c_append (txt, "Incomplete ");
411 0 : break;
412 : case TP_LEAVE:
413 0 : tx_c_append (txt, "Leave ");
414 0 : break;
415 : default:
416 0 : tx_c_append (txt, "<Unknown> ");
417 : break;
418 : }
419 66 : tx_c_append (txt, tx_share_buf (etc->case_->module_->modulename_));
420 66 : if (strchr (tx_share_buf (etc->case_->module_->modulename_), '.') ==
421 : NULL)
422 4 : tx_c_append (txt, ".so");
423 66 : tx_c_append (txt, ":\"");
424 66 : tx_c_append (txt, tx_share_buf (etc->case_->casetitle_));
425 66 : tx_c_append (txt, "\" - ");
426 66 : tx_c_append (txt, desc);
427 :
428 66 : printf ("%s\n", tx_share_buf (txt));
429 :
430 66 : tx_destroy (&txt);
431 66 : case_result_count_ ++;
432 : }
433 : /* ------------------------------------------------------------------------- */
434 : LOCAL void pl_msg_print (long testrunid, char *message)
435 61 : {
436 : struct ExecutedTestCase *etc;
437 :
438 61 : etc = get_executed_tcase_with_runid (testrunid);
439 :
440 61 : if (etc == INITPTR)
441 0 : printf (" message from unknown test : %s\n", message);
442 : else
443 61 : printf (" message from \"%s\": %s\n",
444 : tx_share_buf (etc->case_->casetitle_),
445 : message);
446 : return;
447 : }
448 : /* ------------------------------------------------------------------------- */
449 2 : LOCAL void pl_error_report (char *error) {
450 2 : fprintf (stderr, "%s\n", error);
451 2 : }
452 : /* ------------------------------------------------------------------------- */
453 : LOCAL int all_done()
454 2146 : {
455 2146 : if (ready_module_count_ == dl_list_size (available_modules) &&
456 : (cliopts.display_info_ == 1 ||
457 : available_case_count_ == case_result_count_))
458 : {
459 8 : return 1;
460 : }
461 :
462 2138 : return 0;
463 : }
464 : /* ------------------------------------------------------------------------- */
465 : LOCAL char *patch_path (char *p)
466 0 : {
467 : Text *path, *cwd;
468 : char *retval;
469 :
470 0 : path = tx_create (p);
471 0 : if (*p != '/') { /* relative path have to prepend with pwd */
472 0 : cwd = tx_create (getenv ("PWD"));
473 0 : tx_c_append (cwd, "/");
474 0 : tx_append (cwd, path);
475 0 : retval = tx_get_buf (cwd);
476 0 : tx_destroy (&cwd);
477 : } else
478 0 : retval = tx_get_buf (path);
479 0 : tx_destroy (&path);
480 :
481 0 : return retval;
482 : }
483 : /* ------------------------------------------------------------------------- */
484 : LOCAL void test_module_info(char *libname)
485 0 : {
486 : test_libl_t tlibl;
487 : int ret;
488 : static int first = 1;
489 : char *fullpath;
490 :
491 0 : fullpath = patch_path (libname);
492 :
493 0 : ret = tl_open (&tlibl, fullpath);
494 :
495 0 : if( (ret != ENOERR) && (tlibl.test_library_==INITPTR) ) {
496 0 : printf("Error when looking for test module %s\n",libname);
497 0 : DELETE (fullpath);
498 0 : return;
499 : }
500 0 : if (first) {
501 0 : printf ("Module Type:\tModule Version:\t\tBuild date:\n");
502 0 : first = 0;
503 : }
504 0 : printf ("%-11s\t%4d\t\t\t%s %s\n",
505 : tlibl.type_,
506 : tlibl.version_,
507 : tlibl.date_,
508 : tlibl.time_);
509 0 : printf ("Cases:\n");
510 0 : tl_close (&tlibl);
511 0 : DELETE (fullpath);
512 :
513 0 : return;
514 : }
515 :
516 : /* ------------------------------------------------------------------------- */
517 : /* ======================== FUNCTIONS ====================================== */
518 : /* ------------------------------------------------------------------------- */
519 : /** Plugin attach function, sets the callbacks.
520 : * @param out_callback callbacks called by the engine
521 : * @param in_callback callbacks towards the engine
522 : */
523 : void pl_attach_plugin (eapiIn_t **out_callback, eapiOut_t *in_callback)
524 22 : {
525 : /* Binds the callbacks */
526 22 : memcpy (&min_clbk_,in_callback,sizeof(eapiOut_t));
527 :
528 22 : (*out_callback)->case_result = pl_case_result;
529 22 : (*out_callback)->case_started = pl_case_started;
530 22 : (*out_callback)->case_paused = NULL;
531 22 : (*out_callback)->case_resumed = NULL;
532 22 : (*out_callback)->module_prints = pl_msg_print;
533 22 : (*out_callback)->new_module = pl_new_module;
534 22 : (*out_callback)->no_module = pl_no_module;
535 22 : (*out_callback)->module_ready = pl_module_ready;
536 22 : (*out_callback)->new_case = pl_new_case;
537 22 : (*out_callback)->error_report = pl_error_report;
538 :
539 : return;
540 : }
541 : /* ------------------------------------------------------------------------- */
542 : /** Plugin open function
543 : * @param arg not used
544 : */
545 : void pl_open_plugin (void *opts)
546 8 : {
547 8 : memcpy (&cliopts, (cli_opts*)opts, sizeof (cli_opts));
548 8 : if (min_clbk_.min_open) min_clbk_.min_open ();
549 2154 : while (!all_done()) {
550 2138 : usleep (50000);
551 : }
552 8 : if (min_clbk_.min_close) min_clbk_.min_close ();
553 :
554 :
555 : return;
556 : }
557 : /* ------------------------------------------------------------------------- */
|