-
Notifications
You must be signed in to change notification settings - Fork 71
/
Copy pathherctest.c
4269 lines (3612 loc) · 180 KB
/
herctest.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
herctest.c, run test cases and analyze results
Copyright 2018 Stephen R. Orso.
Distributed under the Boost Software License, Version 1.0. See
accompanying file BOOST_LICENSE_1_0.txt or a copy at
http://www.boost.org/LICENSE_1_0.txt)
The getopt_long() function and the declarations, definitions, and
macros needed to compile and use getopt_long on Windows are
Copyright (c) 2002 Todd C. Miller <[email protected]>, used
by permission, and Copyright (c) 2000 The NetBSD Foundation, Inc.,
used in accordance with the terms of the license agreement. See the
following for the original source and the complete text of the
license agreement.
https://github.com/freebsd/freebsd/blob/master/include/getopt.h
https://github.com/freebsd/freebsd/blob/master/lib/libc/stdlib/getopt.c
The permission notice and license agreement are reproduced in full
just before the definition of the function getopt_long(), which is
located at the end of this compilation unit.
Code needed to compile under Windows is derived from BSD getopt.h,
which is Copyright (c) 1989, 1993 The Regents of the University of
California, and used under the terms of its BSD 3-clause license
agreement. See the following for the original source and the
complete text of the license agreement.
https://github.com/freebsd/freebsd/blob/master/include/glob.h
*/
/* ********************************************************************
herctest.c, run test cases and analyze results
Function/Operation -
* Using command line options and defaults, assemble a suite of test
scripts, run Hercules using those scripts, and analyze the results.
Summary results are written to an output file that lists each test
case that was run and the number of tests in each case that passed
and failed.
* A single test script may include multiple test cases, and each test
case may include multiple tests.
* Hercules is executed to run all test scripts, with output captured
to a log file for analysis by the Hercules REXX script redtest.rexx.
Platfrom-specific techniques are used to execute Hercules and
redtest.rexx as child processes.
* Errors in command line options or the absence of requested or
required files cancel the execution of Hercules and redtest.rexx
Entry Points -
* One, main(), invoked using the standard c program interface (argc
and argv).
* All functions and global data are declared static and are not
exposed to the outside world. This program declares no Application
Programming Interface beyond main( int argc, char ** argv ).
* The command line option -? displays short help, and --help displays
more detailed help.
Input -
* Command line parameters that identify:
- the test scripts to be run,
- the directory containing the test scripts and the redtest.rexx
result analysis tool,
- any Hercules loadable modules to be loaded,
- the location of the Hercules executable,
- the test script repeat count,
- the timeout adjustment factor, and
- the file name of the work files used.
Refer to the short and long help for specifics of the command line
parameters.
* One or more test scripts containing test cases and the expected
results from each of those test cases. Test scripts default to
an extension of ".tst" although other extensions may be specified
in command line options.
* If run from the tests subdirectory of a Hercules build directory,
no command line parameters are required, and if none are provided,
all test scripts in that directory will be run.
Output -
* This program creates one file, default name allTests.testin, that is
used as a Hercules Run Commands (RC) file. This file contains a
set of Herules `script` commands that run the required test scripts.
* This program executes Hercules with console output redirected to
a file, default name alltests.log. This file contains output
generated by Hercules as a result of the test scripts. The log file
analysis program redtest.rexx uses this file as input.
* This program executes redtest.rexx, a REXX script that analyzes
the Hercules output generated by the test scripts and creates a
file allTests.txt that summarizes results one line per test case.
* The file name allTests may be changed as desired using the -w
command line option. The extensions .testin, .out, and .txt may
not be changed by command line option.
* Return codes document at a summary level success or failure of the
entire process.
* Error messages are issued on stderr if there are errors in command
line parameters, required files are missing, or child processes
(Hercules, redtest.rexx) fail.
* If a command line option error is detected, short help is displayed
at exit.
External References -
* The core logic of this program uses POSIX functions that have been
defined since ca. 2001. Testing suggests that this program compiles
and executes correctly on Windows 7 and newer, macOS High Sierra,
assorted recent Linux distributions, and FreeBSD 11.1.
* When compiled on Windows, wrapper functions are included for
selected POSIX functions that have not been implemented in Windows.
* On UNIX-like systems including OS X/macOS, FreeBSD, and Linux,
Hercules and redtest.rexx are spawned as child processes using
fork/execvp. Functions freopen and dup2 are used to redirect
stdin, stdout, and stderr as needed.
* On Windows systems, CreateProcess is used to spawn Hercules and
redtest.rexx. CreateFile is used to to redirect stdin, stdout, and
stderr as needed.
Exits -
* To the host system with return code zero when all functions have
completed successfully.
* To the host system with a non-zero return code when a) command line
parameters failed validation, b) required modules are missing, or
c) child processes Hercules or redtest.rexx failed.
Return Codes -
0 - All tasks completed successfully, and all tests passed.
1-239 - All tasks completed successfully, but one or more tests
failed.
240 - More than 239 tests failed. The exact count is unavailable.
241 - Error in command line options or arguments, including missing
files or directories specified on the command line.
242 - Error setting defaults for unspecified command line options,
including missing default files or directories.
243 - Specified or default test scripts missing (absolute path used
as argument to -f command line option) or missing from the test
scripts directory (relative path).
244 - Loadable module not present in modpath directory.
245 - Failed to create the allTests.testin RC file for Hercules
execution.
246 - Failed to build the argument list for Hercules execution.
247 - Failed to spawn the Hercules child process.
248 - Failed to build the argument list for redtest.rexx execution.
249 - Failed to spawn the child process for redtes.rexx.
Portability -
* This program does not use a configure step and does not include
config.h from the Hercueles build (CMake or autotools). Four
strategies are used to enable platform-independent compilation and
execution:
- Core logic is written using POSIX-defined functions and does not
have platform-specific tests or sensitivities.
- Where Windows does not have a POSIX interface but does have an
interface that can be co-opted to produce a matching result, a
wrapper function prefixed "win_" is written and a preprocessor
macro defines either the native POSIX function or the Windows
wrapper function. See GET_REALPATH, GLOB, and GLOBFREE for
examples of this.
- Where Windows and UNIX-like systems have approaches too
different to reconcile using a wrapper function, a herctest
function is versioned based on platform. See get_exepath(),
has_extension(), and run_child_process() for examples of this
approach.
- Where Windows has no equivalent function, permissively-licensed
source from FreeBSD is included in this module modified as
needed to operate as needed in Windows. See getopt() and
getopt_long() for the example of this.
* Selected platform differences, for example the possibility of a
GNU libtool executable directory '.libs' or the valid characters
for globbing, are indicated by #defines of difference-specific
macros rather than testing the platform name in main line code.
Notes -
* This program has been demonstrated to fail on Debian 5.4 (Lenny,
14-Feb-2009) because of a floating point issue that affects the
validation of the -t parameter. This is under investigation, but
not as an urgent issue.
*/
/* ********************************************************************
Standard includes, as required by the host system.
*/
#if defined(_WIN32) /* includes required for Windows */
# include <windows.h> /* required Windows header */
# include <sdkddkver.h> /* for Windows version testing, needed */
/* ...to deal with the deprecation of */
/* ...the char * _pgmptr extern. see */
/* ...Get_exepath() for details. */
#include <io.h> /* required for _access() */
#endif
/* includes required on all systems */
#include <stdlib.h>
#include <stdio.h>
#include <errno.h> /* required for errno constants */
#include <stdarg.h> /* for va_start/va_arg/va_end, used in */
/* ...concat_strings() */
#include <string.h>
#include <sys/stat.h> /* for stat() function */
#include <time.h> /* for timestamp conversion functions, */
/* ...used to include timestamps in the */
/* ...allTests.testin RC file. */
#if !defined(_WIN32) /* includes needed on UNIX-like systems */
/* ...Which includes OS X/macOS */
# include <unistd.h>
# include <errno.h> /* errno externs and macros */
# include <getopt.h> /* system-provided getopt_long() */
# include <libgen.h> /* basename(), dirname() functions */
# include <dirent.h> /* scandir functions */
# include <glob.h> /* file name globbing */
# include <sys/utsname.h> /* uname(), to set platform= */
# include <sys/wait.h> /* wait(), await hercules/redtest exec */
# if defined(__APPLE__)
# include <mach-o/dyld.h> /* for _NSGetExecutablePath */
# endif
#endif
/* ********************************************************************
Define macros for file name lengths. Function concat_strings()
uses MAX_PATH as a sanity check on the result length of the strings
being concatenated.
*/
#ifndef MAX_PATH
#ifdef PATH_MAX
#define MAX_PATH PATH_MAX
#else
#define MAX_PATH 4096
#endif
#endif
/* ********************************************************************
Define the following to enable inclusion of the dump_globals()
function. It may be called from anywhere for diagnostic purposes.
*/
// #define DUMP_GLOBALS
/* ********************************************************************
Define defaults for various strings and constants
*/
#define HERC_RC_EXT ".testin" /* hercules rc file extension */
#define HERC_LOG_EXT ".out" /* hercules console file extension */
#define REDTEST_LOG_EXT ".txt" /* test results file extension */
#define TESTS_SUBDIR "tests" /* subdirectory containing scripts */
#define FNAME_WORKFILES "allTests" /* default filename for workfiles */
#define CONCAT_RES_LEN 128 /* Initial alloc for concatenation */
#define CONCAT_MAX_ARGS 50 /* max strings to concatenate */
#define CONCAT_TOO_LONG "**Too Long**" /* diagnostic for overmax */
#define CMDLINE_ALLOC 256 /* Initial/additional alloc for */
/* Windows cmdline including '\0' */
/* for CreateProcess function */
#define WIN_GLOB_PATH_ALLOC 128 /* Initial/add'l alloc for string */
/* array when globbing on Windows */
#define TEST_SCRIPT_EXT "tst" /* test script extension (-e) */
#define PTRSIZE_NAME "ptrsize" /* var part of "-v ptrsize=" opt */
#define CONFIG_H_NAME "config.h" /* file name of Hecules config file */
#define PTRSIZE_SOURCE "SIZEOF_SIZE_T" /* config #define for ptrsize */
#define PLATFORM_NAME "platform" /* var part of "-v platform=" opt */
#define QUIET_NAME "quiet" /* test for -v quiet */
#define REDTEST_REXX "redtest.rexx" /* name of reduction script */
/* POSIX Portable File Name */
/* character set. */
#define POSIX_FN_CHARS "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._-"
/* ********************************************************************
The following macros define the initial allocations for the arrays
that hold the sundry strings used by this program. The array
structures are defined with zero array elements, and the routines
that manipulate the structures will increase the array allocation by
the amounts below when more room is needed to add a string.
*/
#define PROCARG_COUNT 64 /* Initial Hercules argument count */
#define LOADMOD_COUNT 64 /* Initial load module name alloc */
#define TEST_SCRIPT_COUNT 64 /* Initial test script name alloc */
#define VARVAL_COUNT 64 /* Initial var=val pair alloc */
/* ********************************************************************
Sundry platform-specific strings and constants
*/
#if defined(_WIN32) /* **** Windows systems **** */
# define LOADMOD_SUFFIX ".dll"
# define HERCULES_EXE "hercules.exe"
# define PATH_SEPC '\\'
# define PATH_SEPS "\\"
# define WILDCARD_DIR "\\*"
# define GLOBBING_CHARS "*?"
# define PATH_SEPS_AMBI "\\/" /* either path separator */
# define PLATFORM_VALUE "Windows"
# define REXX_EXE "cmd.exe"
# define REXX_EXE_FLAGS "/c" /* flags for REXX executable */
# define ENCAPSULATE_REXX_REDTEST /* encapsulate in dbl quotes */
# if defined(WINVER) && WINVER >= _WIN32_WINNT_WIN7
# define USE_GET_PGMPTR /* Win 7 or better, use _get_pgmptr */
/* instead of deprecated _pgmptr */
# endif
#else /* **** non-Windows systems **** */
# define LOADMOD_SUFFIX ".so"
# define HERCULES_EXE "hercules"
# define PATH_SEPC '/'
# define PATH_SEPS "/"
# define WILDCARD_DIR "/*"
# define GLOBBING_CHARS "*?["
# define REXX_EXE "rexx"
# undef PATH_SEPS_AMBI /* only one kind of path separator */
# undef PLATFORM_VALUE /* determined at runtime */
# undef REXX_EXE_FLAGS /* no REXX flags for UNIX-like CLI */
# undef ENCAPSULATE_REXX_REDTEST /* do not encapsulate in dbl quotes */
# if !defined(__APPLE__)
# define LIBTOOL_SUBDIR ".libs" /* libtool is UNIX-like only */
# endif
#endif
/* ********************************************************************
Define access() function based on host system. The Windows
_access() function is pretty much identical to the POSIX.1-2001
access() function, but they have slightly different names. Windows
also fails to define helpful macros for the access type.
*/
#if defined(_WIN32)
# define W_OK 0x02
# define R_OK 0x04
# define ACCESS( path, acc_type ) _access( path, acc_type )
#else
# define ACCESS( path, acc_type ) access( path, acc_type )
#endif
/* ********************************************************************
Define absolute path function based on host system. The native
realpath() function available on UNIX-like systems is sufficient,
and it's easy enough to code an equivalent Windows function.
*/
#if defined(_WIN32)
# define GET_REALPATH( relpath ) win_realpath( relpath )
#else
# define GET_REALPATH( relpath ) realpath( relpath, NULL )
#endif
/* ********************************************************************
Define glob() function based on host system. The native glob()
function available on UNIX-like systems is sufficient, so there is
no need for a wrapper. A Windows function that implements enough
of glob() for this program is also easy enough to code.
*/
#if defined(_WIN32)
# define GLOB( pattern, flags, errfunc, result ) \
win_glob( pattern, flags, errfunc, result)
# define GLOBFREE( result ) win_globfree( result )
#else
# define GLOB( pattern, flags, errfunc, result ) \
glob( pattern, flags, errfunc, result)
# define GLOBFREE( result ) globfree( result )
#endif
/* ********************************************************************
typedef/struct definition for an array of strings. This program
uses four arrays of strings that are allocated and expanded as
needed: load module names, test script names, variable=value pairs,
and the argv array passed to fork/exec. Because each has the same
structure, we can do a typedef and use the same routines to manage
each.
*/
struct stringctl_s
{
char ** string; /* array of strings */
int alloc; /* current allocaction of string elements */
int index; /* index of last used string, -1 if empty */
int next_alloc; /* number of element to create or add */
/* when array needs to be expanded */
char * desc; /* text description of array contents */
};
typedef struct stringctl_s stringctl_t;
/* ********************************************************************
Forward declarations for all functions defined in this compilation
unit.
*/
/* Main line functions */
int main( /* main line processing */
int, /* command line argument count */
char ** ); /* c-style arguments */
static int process_args( /* process command line arguments */
int, /* ..count of arguments */
char ** ); /* ..array of argument pointers */
/* rc = nr. of errors detected */
static int set_defaults( /* set defaults for unspecified options */
char ** ); /* argv array, needed for exe path */
static int validate_arg_scripts_array(); /* validate test scripts */
/* rc = nr of non-existant test scripts */
static int validate_loadables(); /* validate load modules in modpath */
/* rc = nr of non-existant loadable modules */
static int build_test_script_rc_file(); /* create Hercules RC file */
/* no return codes (yet). */
static int build_hercules_arglist(); /* build Hercules arg list */
/* no return codes */
static int build_redtest_arglist(); /* build redtest.rexx arg list */
/* no return codes */
static int run_child_process( /* run child procs, wait for completion */
const char *, /* descriptive name of child process */
const char *, /* name of stdout/stderr redir file */
int, /* if 1, enclose args in double quotes */
int * /* location to store child process rc */
); /* ...used only by Windows version */
/* rc = 0 means normal process completion */
/* Utility functions */
static void dump_globals(); /* dump global vars to stdout for debug */
static int addto_string_array( /* Add elementn to string array */
stringctl_t *, /* ..array to receive new element */
char * ); /* ..malloc'd string to be added */
/* rc always zero (0) (for now) */
static char * concat_strings( /* Concatenate strings */
char *, /* ..first string of list of strings */
...); /* ..more strings, NULL at end of list */
/* rc = pointer to malloc'd result string */
/* argument validation and global table update routines */
static void add_args_script( /* Add name to test script arg array */
char *, /* ..file name to be added */
const char * ); /* ..name of option being processed */
static void add_load_module( /* Add module to loadable module array */
char *, /* ..name of loadable module to be added */
const char * ); /* ..name of option being processed */
static void add_test_script( /* Add file name to test script array */
char *, /* ..file name of test script to be added */
const char * ); /* ..name of option being processed */
static int add_varval_pair( /* Add a var=val pair to var=val array */
char *, /* ..variable=value pair to be added */
const char *, /* ..desc. of option being processed */
int ); /* ..if 1, allow setting of platform */
/* rc -1 if invalid var=val pair, else zero */
static int check_arg_dir( /* check a directory argument, set pointer */
/* ..used for -d, -p, -w directories */
char *, /* ..directory to be validated */
char **, /* ..pointer to directory pointer */
const char * ); /* ..name of option being processed */
/* rc = 0 if dir valid, 1 otherwise */
static int check_arg_hercexe( /* check hercules exe path, set ptr */
char *, /* ..executabel path to be validated */
char **, /* ..pointer to executable path pointer */
const char * ); /* ..name of option being processed */
/* rc = 0 if path valid, 1 otherwise */
static char * get_dirname( /* get directory portion of path name */
const char * ); /* path name to have lasts bit excised */
/* NULL returned if unable */
static int get_exepath( /* get full path of current executable */
const char * ); /* ..argv[ 0 ], used for UNX-like systems */
/* NULL returned if unable to determine */
static int get_ptrsize( ); /* scan config.h to determine ptrsize=, */
/* add to varval array. */
/* rc = 0 if added, 1 otherwise */
static int has_extension( /* return true if filename has ext. */
char * ); /* ..path name to be tested for extension */
/* rc = 1 path has extension, 0 otherwise */
static void long_help(); /* display long help text --help */
static void short_help(); /* display short help */
/* ******************************************************************** */
#if defined(_WIN32) /* ***** declarations etc for Windows wrappers **** */
/* ********************************************************************
Declarations and macro definitions needed to compile getopt_long()
under Windows. This code is taken from FreeBSD source files
getopt_long.c and getopt.h and is used under the terms of the
license granted by the authors. The copyright notice for this
code is in the beginning of this compilation unit and the complete
copyright and license terms are reproduced below.
Forward function declaration and structure definitions for Windows
compilation of getopt_long.
*/
/* globals from BSD getopt.c */
static int opterr = 1; /* if error message should be printed */
static int optind = 1; /* index into parent argv vector */
static int optopt = '?'; /* character checked for validity */
static int optreset; /* reset getopt */
static char * optarg; /* argument associated with option */
/* struct options from getopt.h */
struct option {
const char *name; /* name of long option */
int has_arg; /* whether option takes an argument, one of */
/* no_argument, required_argument, optional_argument */
int *flag; /* if !NULL, set *flag to val when option found */
int val; /* if flag not NULL, value to set *flag to; */
/* ...else return value */
};
/* Forward declaration of getopt_long(). */
static int getopt_long( int , /* count of arguments on command line */
char * const *, /* Array of pointers to argument strings */
const char *, /* string of allowed short options */
const struct option *, /* structure of allowed long options */
int * ); /* pointer to found long option or -1 */
/* disable error functions from getopt */
#define warnx(...)
/* other defines from BSD getopt.h */
#define no_argument 0
#define required_argument 1
#define optional_argument 2
/* ************ end of content subject to getopt copyright ************ */
/* ********************************************************************
Definitions adapted from glob.h and needed to compile this program
under Windows. This code is adapted from FreeBSD source file
glob.h and is adapted under the terms of the copyright holder. The
copyright notice for this code is in the beginning of this
compilation unit.
*/
typedef struct {
size_t gl_pathc; /* Count of total paths so far. */
size_t gl_offs; /* Reserved at beginning of gl_pathv (not impl.)*/
char **gl_pathv; /* List of paths matching pattern. */
} glob_t;
/* * end of content derived from material subject to getopt copyright * */
/* ******************************************************************** */
#endif /* ***** end of declarations etc for Windows wrappers ***** */
/* ******************************************************************** */
#if defined(_WIN32)
/* Windows values for stat fst.mode executable. Windows does not have */
/* group and other executable flags on the base file system, we shall */
/* not get into access tokens for a command line argument test, and */
/* Windows has deprecated S_IXUSR in favor of S_IEXEC. So we code */
/* premeptively for its disappearance. */
# if !defined(S_IXUSR)
# define S_IXUSR S_IEXEC
# endif
#define S_IXGRP 0
#define S_IXOTH 0
/* Windows values for stat fst.mode read. See above... */
#define S_IRGRP 0
#define S_IROTH 0
/* Macros for Windows tests of stat structure from stat() function. */
/* Windows fails to define them for us. */
#define S_ISDIR(st_mode) (((st_mode) & _S_IFMT) == _S_IFDIR) /* directory */
#define S_ISREG(st_mode) (((st_mode) & _S_IFMT) == _S_IFREG) /* regular file */
/* forward declarations for Windows wrappers to enable portability */
static char * win_realpath( /* get absolute path of named file */
const char * ); /* argv[ 0 ], used for UNX-like */
/* NULL returned if invalid/nonexist. file */
static int win_glob( /* limited function glob() for Windows */
const char *, /* pattern to be matched on a glob call */
int, /* processing flags (not implemented) */
int (*)(const char *, int), /* error function (not impl.) */
glob_t * ); /* glob_t struct with returned paths */
/* rc=0 if all OK */
static void win_globfree( /* free storage in glob_t structure */
glob_t * ); /* glob_t structure to be free'd */
/* no return value */
#endif
/* ******************************************************************** */
/* ********************************************************************
Global variable declarations. These are limited to strings and
values that will be used to generate the Hercules command line
*/
/* directories and absolute paths */
static char * dir_test_scripts = NULL; /* test scripts directory */
static char * dir_modpath = NULL; /* loadable module directory */
static char * exe_hercules = NULL; /* Hercules executable full path */
static char * dir_hercules = NULL; /* directory containing the above */
static char * rexx_redtest = NULL; /* redtest.rexx script full path */
static char * dir_herctest = NULL; /* executable dir for this program */
static char * name_herctest = NULL; /* name of this executable */
/* filename for herctest work files, default AllTests */
static char * fname_workfiles = NULL;
/* workfile file names with extensions, all relative to CWD */
static char * herc_rc_file; /* rc file name */
static char * herc_log; /* hercules exec log */
static char * redtest_log; /* redtest exec log */
/* String arrays for arguments to -f, load modules, test scripts, */
/* var=value pairs, and arguments to be passed to Hercules during */
/* fork/exec. The initialization values NULL, 0, -1 and the array */
/* description indicate an array with no elements and no room for more */
/* elements; this triggers an initial allocation on first add. */
/* All but the procargs array are arrays of pointers to malloc'd */
/* strings, and free'ing the array means first free'ing each string */
/* included in the array. */
static stringctl_t args_scripts = { NULL, 0, -1, TEST_SCRIPT_COUNT, "-f argument" };
static stringctl_t loadmod = { NULL, 0, -1, LOADMOD_COUNT, "load module" };
static stringctl_t test_scripts = { NULL, 0, -1, TEST_SCRIPT_COUNT, "test script" };
static stringctl_t varval = { NULL, 0, -1, VARVAL_COUNT, "var=val pair" };
/* The procargs array will contain the c-style parameter list used to */
/* invoke Hercules and redtest.rexx on UNIX-like systems, and from */
/* which the Windows command line used to start Hercules is built. All */
/* strings pointed to by this array are either constants or are copies */
/* of pointers from the above arrays. This array can be free'd just by */
/* free'ing the array itself. The strings will be free'd as needed */
/* when the above arrays are free'd. */
static stringctl_t procargs = { NULL, 0, -1, PROCARG_COUNT, "child args" };
static char * test_script_ext = NULL; /* default test script extension */
/* ...*WITHOUT* the period */
/* other flags to hercules or redtest.rexx */
static int rep_count = 1; /* test script repetition count */
/* timeout adjustment factor */
static float timeout_factor = 1.0f; /* test script t/o adj. factor */
static float timeout_limit = 14.3f; /* maximum t/o adj. factor */
static char timeout_factor_s[ 10 ]; /* t/o factor in string form */
static int no_exit = 0; /* 1 = omit "exit" at end of RC */
static int redtest_quiet = 0; /* 1 = pass quiet to redtest */
/* Global flags used by this program */
static int ptrsize_found = 0; /* cmd included -v ptrsize if true */
/* ********************************************************************
Main function.
Do the following:
1. Edit and process the command line arguments, and build the global
data structures needed to turn those arguments into Hercules and
redtest.rexx command line arguments.
2. For command line options not specified, establish default options
consistent with help displays for this program.
3. Ensure all files required to run Hercules to run
the test scripts and and run redtest.rexx to analyze the results exist.
4. Create an input run commands file (.rc) for Hercules and run
Hercules with that command file.
5. Run redtest.rexx to analyze the output log from Hercules.
*/
int main( int argc, char ** argv )
{
int ec; /* error code returned by many functions */
int child_rc; /* return code from child process */
int rc = 0; /* return code from mainline */
int i; /* sundry loop control */
#if defined(DUMP_GLOBALS)
char * msg;
/* Dump the command line arguments */
msg = "Cmdline args:";
for ( i = 0; i < argc; i++ )
{
fprintf( stdout, "%s %3d. %p -> \"%s\"\n", msg, i, argv[ i ], argv[ i ] );
msg = " ";
}
fprintf( stdout, "\n" );
#endif
test_script_ext = strdup( TEST_SCRIPT_EXT ); /* default test */
/* ...script extension *WITHOUT* period */
/* get dir containing this executable */
ec = get_exepath( argv[ 0 ] );
if ( ec ) /* able to determine executable path? */
/* ..no, error message, continue */
fprintf( stderr, "Unable to determine executable path, rc=%d, for \"%s\"\n",
ec, argv[ 0 ] );
/* Calculate the maximum timeout expansion factor. The following */
/* expression must match the #define for MAX_RUNTEST_FACTOR in */
/* Hercules hconsts.h */
timeout_limit = (float) (((4.0 * 1024.0 * 1024.0 * 1024.0) - 1.0)
/ 1000000.0 /* (usecs) */
/ 300 ); /* MAX_RUNTEST_DUR */
timeout_limit = ((float) ((int) (timeout_limit * 10 + 0.5))) / 10;
/* Process command line options and arguments */
if ( ( ec = process_args( argc, argv ) ) )
rc = 241;
/* Set defaults for unspecified opts */
else if ( ( ec = set_defaults( argv ) ) )
rc = 242;
/* ensure test scripts exist */
else if ( ( ec = validate_arg_scripts_array() ) )
rc = 243;
/* ensure loadable modules exist */
else if ( ( ec = validate_loadables() ) )
rc = 244;
/* build the Hercules RC run commands file */
else if ( ( ec = build_test_script_rc_file() ) )
rc = 245;
/* build the Hercules argument list */
else if ( ( ec = build_hercules_arglist() ) )
rc = 246;
/* Run Hercules */
else if ( ( ec = run_child_process( "Hercules",
herc_log, 1, &child_rc ) ) )
rc = 247;
/* build the redtest.rexx argument list */
else if ( ( ec = build_redtest_arglist() ) )
rc = 248;
/* run redtest.rexx to analyze Hercules output. If redtest.rexx */
/* spawns successfully, run_child_process will return zero, and */
/* we will use the return code from the child process as the */
/* herctest return code. */
else if ( ( ec = run_child_process( "Redtest.rexx",
redtest_log, 0, &child_rc ) ) )
rc = 249;
/* No errors detected in herctest processing. Use redtest.rexx */
/* return code as the herctest return code. */
else
rc = child_rc;
#if defined(DUMP_GLOBALS)
dump_globals();
#endif
/* free all array elements and the root arrays. This is not */
/* strictly necessary because the next step is return to shell, */
/* which will free everything. But if one is running a memory */
/* analysis tool such as Valgrind, free'ing everything really */
/* helps in understanding whether memory leaks are an issue. */
for ( i = 0; i <= varval.index; i++ )
free( varval.string[ i ] );
free( varval.string );
varval.string = NULL;
for ( i = 0; i <= loadmod.index; i++ )
free( loadmod.string[ i ] );
free( loadmod.string );
loadmod.string = NULL;
for ( i = 0; i <= args_scripts.index; i++ )
free( args_scripts.string[ i ] );
free( args_scripts.string );
args_scripts.string = NULL;
for ( i = 0; i <= test_scripts.index; i++ )
free( test_scripts.string[ i ] );
free( test_scripts.string );
test_scripts.string = NULL;
free( procargs.string );
procargs.string = NULL;
free( dir_test_scripts );
free( dir_modpath );
free( exe_hercules );
free( rexx_redtest );
free( dir_hercules );
free( test_script_ext );
free( fname_workfiles );
free( dir_herctest );
free( name_herctest );
dir_test_scripts = NULL;
dir_modpath = NULL;
exe_hercules = NULL;
rexx_redtest = NULL;
dir_hercules = NULL;
test_script_ext = NULL;
fname_workfiles = NULL;
dir_herctest = NULL;
name_herctest = NULL;
free( herc_rc_file );
free( herc_log );
free( redtest_log );
herc_rc_file = NULL;
herc_log = NULL;
redtest_log = NULL;
return rc;
}
/* ********************************************************************
Function process_args
Validate command line options and arguments and populate the global
variables based on the options and arguments provided. Default
values are not processed here; defaults are set after all command
line arguments have been processed. The interactions between
options, for example the test script directory and test script file
names, cannot be addressed until after all options and arguments
have been validated.
*/
int process_args( int argc, char * argv[] )
{
/* short (single-dash) command line options */
static const char * options_allowed = ":d:e:f:h:l:p:qr:t:v:w:x";
/* long (single-dash) command line options. All have short */
/* equivalents except --helplong. */
static const struct option longopts[] = {
{ "testdir", required_argument, NULL, 'd' },
{ "testext", required_argument, NULL, 'e' },
{ "testname", required_argument, NULL, 'f' },
{ "hercdir", required_argument, NULL, 'h' },
{ "loadmod", required_argument, NULL, 'l' },
{ "modpath", required_argument, NULL, 'p' },
{ "quiet", no_argument, NULL, 'q' },
{ "testreps", required_argument, NULL, 'r' },
{ "timeout", required_argument, NULL, 't' },
{ "var", required_argument, NULL, 'v' },
{ "workdir", required_argument, NULL, 'w' },
{ "noexit", no_argument, NULL, 'x' },
{ "help", no_argument, NULL, 2 },
{ "helplong", no_argument, NULL, 1 },
{ NULL, 0, NULL, 0 }
};
int opt; /* current short option being processed */
int longopts_index = -1; /* index of identified long option */
int display_help = 0; /* was help requested, >0 == TRUE */
int arg_error_count = 0; /* count of errors related to args */
int error_count = 0; /* count of errors related to options */
char * msg; /* text of message to be issued */
char * repcount_arg_end; /* pointer to end of valid part of rep */
char * timeout_arg_end; /* pointer to end of valid part of t/o */
char * option_name; /* string containing current option */
char long_option[ 12 ] = /* base string for long option display */
{'-', '-' }; /* ..starts with "--" */
char short_option[3] = /* base string for short opt display */
{ '-', '?', '\0' }; /* ..starts with "-" */
/* flags for options that may have only one argument and may be */
/* specified only once. */
int have_testscript_dir = 0; /* -d/--testdir: test script dir */
int have_modpath_dir = 0; /* -p/--modpath: loadable mod'l dir */
int have_fname_workfiles = 0; /* -w/--workdir: working directory */
int have_hercexe_path = 0; /* -h/--hercdir: executable path */
int have_repcount = 0; /* -r/--testreps: test repeat count */
int have_timeout = 0; /* -t/--timeout: t/o adjustment */
opterr = 0; /* we will handle error messages */
while ( (opt = getopt_long( argc, argv, options_allowed, longopts, &longopts_index)) != -1 )
{
/* get a pointer to the option name (e.g., -v, --hercdir for */
/* use in diagnostic messages. */
if ( longopts_index >= 1 ) /* Valid long option coded? */
{ /* ..yes, append it to "--" */
long_option[ 2 ] = '\0'; /* trunc. name to "--" and add */
strcat( long_option, longopts[ longopts_index ].name);
option_name = long_option;
}
else if ( (char) opt == '?' ) /* invalid option coded? */
if ( (char) optopt == '\0' ) /* Invalid long ("--")? */
option_name = argv[ optind - 1 ]; /* yes, use argv[] */
else
{ /* ..no, invalid short option */
short_option[ 1 ] = (char) optopt; /* copy option char */
option_name = short_option + 1; /* into "-?" string */
}
else /* valid short option... */
{
short_option[ 1 ] = (char) opt; /* copy option char into */
option_name = short_option; /* "-?" string */
}
if ( optarg && *optarg == '-' ) /* is arg really an option? */
{
optopt = opt; /* ..yes, save option without arg */
opt = (char) ':'; /* indicate missing option */
optind--; /* back up to next option */
}
/* Edit each option and its argument(s) if the option supports */
/* arguments. This switch also has cases for missing argument. */
switch( opt )
{
/* -------------------------------------------------------- */
/* -d Test script directory */
/* Used to construct absolute path names of test */
/* scripts, and passed via DEFSYM to all test scripts. */
case 'd':
if ( have_testscript_dir++ )
{
fprintf( stdout, "%s option may be specified only once\n", option_name );
error_count++;
}
else if ( optind < argc && *argv[ optind ] != '-' )
{
fprintf( stdout, "%s option may not have more than one argument\n", option_name );
error_count++;
}
else if ( check_arg_dir( optarg, &dir_test_scripts, option_name) )
{
fprintf( stdout, "Incrementing error count on -d\n" );
error_count++;
}
break;
/* -------------------------------------------------------- */
/* -e Default extension for test script filenames */
/* Appended to each test script file name that lacks */
/* an extension. */
case 'e':
test_script_ext =
strdup( optarg[ 0 ] == '.' ? optarg + 1 : optarg );
break;
/* -------------------------------------------------------- */
/* -f Specify a test script file name. */
/* One or more file names follow the option, and */
/* multiple -f options are accepted. These file names */
/* are used to build the RC file used to run Hercuse. */
case 'f':
while ( 1 )
{
add_args_script( optarg, option_name );
if ( ( optind >= argc ) || ( *argv[ optind ] == (char) '-' ) )
break;
else
optarg = argv[ optind++ ];
}
break;
/* -------------------------------------------------------- */
/* -h Hercules executable path */
/* Used for CreateProcess (Windows) or fork/exec */
/* (UNIX-like systems). Also used as the default for */
/* the loadable module path (-p). */
case 'h':
if ( have_hercexe_path++ )
{
fprintf( stdout, "%s option may be specified only once\n", option_name );
error_count++;
}
else if ( optind < argc && *argv[ optind ] != '-' )
{
fprintf( stdout, "%s option may not have more than one argument\n", option_name );
error_count++;
}
else if ( check_arg_hercexe( optarg, &exe_hercules, option_name ) )
{
fprintf( stdout, "Incrementing error count on -h\n" );
error_count++;
}
else
dir_hercules = get_dirname( exe_hercules );