@@ -36,6 +36,7 @@ int COUNT = 16;
3636
3737static int parse_jobs_count (const char * key , const char * value , struct Args * out );
3838static int parse_iterations (const char * arg );
39+ static int parse_target (const char * value , struct TestFramework * tf );
3940
4041/*
4142 * Main entry point for handling command-line arguments.
@@ -61,6 +62,10 @@ static int parse_arg(const char* key, const char* value, struct TestFramework* t
6162 tf -> args .custom_seed = (!value || strcmp (value , "NULL" ) == 0 ) ? NULL : value ;
6263 return 0 ;
6364 }
65+ /* Test target */
66+ if (strcmp (key , "t" ) == 0 || strcmp (key , "target" ) == 0 ) {
67+ return parse_target (value , tf );
68+ }
6469
6570 /* Unknown key: report just so typos don’t silently pass. */
6671 printf ("Unknown argument '-%s=%s'\n" , key , value );
@@ -75,6 +80,8 @@ static void help(void) {
7580 printf (" -j=<num>, -jobs=<num> Number of parallel worker processes (default: 0 = sequential)\n" );
7681 printf (" -iter=<num>, -iterations=<num> Number of iterations for each test (default: 16)\n" );
7782 printf (" -seed=<hex> Set a specific RNG seed (default: random)\n" );
83+ printf (" -target=<test name>, -t=<name> Run a specific test (can be provided multiple times)\n" );
84+ printf (" -target=<module name>, -t=<module> Run all tests within a specific module (can be provided multiple times)\n" );
7885 printf ("\n" );
7986 printf ("Notes:\n" );
8087 printf (" - All arguments must be provided in the form '-key=value'.\n" );
@@ -116,6 +123,31 @@ static int parse_iterations(const char* arg) {
116123 return 0 ;
117124}
118125
126+ static int parse_target (const char * value , struct TestFramework * tf ) {
127+ TestRef i = {/*idx_module=*/ 0 , /*idx_test=*/ 0 };
128+ if (tf -> args .targets .size >= MAX_ARGS ) {
129+ printf ("Too many -target args (max: %d)\n" , MAX_ARGS );
130+ return -1 ;
131+ }
132+ /* Find test index in the registry */
133+ for (i .group = 0 ; i .group < tf -> num_modules ; i .group ++ ) {
134+ struct TestModule * module = & tf -> registry_modules [i .group ];
135+ int add_all = strcmp (value , module -> name ) == 0 ; /* select all from module */
136+ for (i .idx = 0 ; i .idx < module -> size ; i .idx ++ ) {
137+ if (add_all || strcmp (value , module -> data [i .idx ].name ) == 0 ) {
138+ tf -> args .targets .slots [tf -> args .targets .size ] = i ;
139+ tf -> args .targets .size ++ ;
140+ /* Matched a single test, we're done */
141+ if (!add_all ) return 0 ;
142+ }
143+ }
144+ /* If add_all was true, we added all tests in the module, so return */
145+ if (add_all ) return 0 ;
146+ }
147+ fprintf (stderr , "Error: target not found: '%s'\n" , value );
148+ return -1 ;
149+ }
150+
119151/* Read args; all must be "-key=value" */
120152static int read_args (int argc , char * * argv , int start , struct TestFramework * tf ) {
121153 int i ;
@@ -149,13 +181,10 @@ static void run_test(const struct TestEntry* t) {
149181
150182/* Process tests in sequential order */
151183static int run_sequential (struct TestFramework * tf ) {
152- TestRef ref ;
153- struct TestModule * mdl ;
154- for (ref .group = 0 ; ref .group < tf -> num_modules ; ref .group ++ ) {
155- mdl = & tf -> registry_modules [ref .group ];
156- for (ref .idx = 0 ; ref .idx < mdl -> size ; ref .idx ++ ) {
157- run_test (& mdl -> data [ref .idx ]);
158- }
184+ int it ;
185+ for (it = 0 ; it < tf -> args .targets .size ; it ++ ) {
186+ TestRef * index = & tf -> args .targets .slots [it ];
187+ run_test (& tf -> registry_modules [index -> group ].data [index -> idx ]);
159188 }
160189 return EXIT_SUCCESS ;
161190}
@@ -173,7 +202,7 @@ static int run_concurrent(struct TestFramework* tf) {
173202 /* Loop iterator */
174203 int it ;
175204 /* Loop ref */
176- TestRef ref ;
205+ TestRef * ref ;
177206 /* Launch worker processes */
178207 for (it = 0 ; it < tf -> args .num_processes ; it ++ ) {
179208 pid_t pid ;
@@ -190,9 +219,10 @@ static int run_concurrent(struct TestFramework* tf) {
190219
191220 if (pid == 0 ) {
192221 /* Child worker: run tests assigned via pipe */
222+ TestRef tref ;
193223 close (pipes [it ][1 ]); /* Close write end */
194- while (read (pipes [it ][0 ], & ref , sizeof (ref )) == sizeof (ref )) {
195- run_test (& tf -> registry_modules [ref .group ].data [ref .idx ]);
224+ while (read (pipes [it ][0 ], & tref , sizeof (tref )) == sizeof (tref )) {
225+ run_test (& tf -> registry_modules [tref .group ].data [tref .idx ]);
196226 }
197227 _exit (EXIT_SUCCESS ); /* finish child process */
198228 } else {
@@ -204,15 +234,13 @@ static int run_concurrent(struct TestFramework* tf) {
204234
205235 /* Now that we have all sub-processes, distribute workload in round-robin */
206236 worker_idx = 0 ;
207- for (ref .group = 0 ; ref .group < tf -> num_modules ; ref .group ++ ) {
208- struct TestModule * mdl = & tf -> registry_modules [ref .group ];
209- for (ref .idx = 0 ; ref .idx < mdl -> size ; ref .idx ++ ) {
210- if (write (pipes [worker_idx ][1 ], & ref , sizeof (ref )) == -1 ) {
211- perror ("Error during workload distribution" );
212- return EXIT_FAILURE ;
213- }
214- if (++ worker_idx >= tf -> args .num_processes ) worker_idx = 0 ;
237+ for (it = 0 ; it < tf -> args .targets .size ; it ++ ) {
238+ ref = & tf -> args .targets .slots [it ];
239+ if (write (pipes [worker_idx ][1 ], ref , sizeof (* ref )) == -1 ) {
240+ perror ("Error during workload distribution" );
241+ return EXIT_FAILURE ;
215242 }
243+ if (++ worker_idx >= tf -> args .num_processes ) worker_idx = 0 ;
216244 }
217245
218246 /* Close all pipes to signal workers to exit */
@@ -235,6 +263,7 @@ static int tf_init(struct TestFramework* tf, int argc, char** argv)
235263 /* Initialize command-line options */
236264 tf -> args .num_processes = 0 ;
237265 tf -> args .custom_seed = NULL ;
266+ tf -> args .targets .size = 0 ;
238267
239268 /* Disable buffering for stdout to improve reliability of getting
240269 * diagnostic information. Happens right at the start of main because
@@ -282,11 +311,31 @@ static int tf_run(struct TestFramework* tf) {
282311 /* Initial test time */
283312 int64_t start_time = gettime_i64 (); /* maybe move this after the slots set */
284313
314+ /* Populate targets with all tests if none were explicitly specified */
315+ if (tf -> args .targets .size == 0 ) {
316+ TestRef ref ;
317+ int slot = 0 ;
318+ for (ref .group = 0 ; ref .group < tf -> num_modules ; ref .group ++ ) {
319+ struct TestModule * group = & tf -> registry_modules [ref .group ];
320+ for (ref .idx = 0 ; ref .idx < group -> size ; ref .idx ++ ) {
321+ tf -> args .targets .slots [slot ++ ] = ref ;
322+ if (slot >= MAX_ARGS ) {
323+ fprintf (stderr , "Error: Number of tests (%d) exceeds MAX_ARGS (%d). "
324+ "Increase MAX_ARGS to accommodate all tests.\n" , slot , MAX_ARGS );
325+ return EXIT_FAILURE ;
326+ }
327+ }
328+ }
329+ tf -> args .targets .size = slot ;
330+ }
331+
285332 /* Run test RNG tests (must run before we really initialize the test RNG) */
286333 /* Note: currently, these tests are executed sequentially because there */
287334 /* is really only one test. */
288335 for (t = tf -> registry_no_ctx ; t -> name ; t ++ ) {
289- run_test (t );
336+ if (tf -> args .targets .size == 0 ) { /* future: support filtering */
337+ run_test (t );
338+ }
290339 }
291340
292341 /* Initialize test RNG and library contexts */
0 commit comments