@@ -36,6 +36,7 @@ int COUNT = 16;
36
36
37
37
static int parse_jobs_count (const char * key , const char * value , struct Args * out );
38
38
static int parse_iterations (const char * arg );
39
+ static int parse_target (const char * value , struct TestFramework * tf );
39
40
40
41
/*
41
42
* 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
61
62
tf -> args .custom_seed = (!value || strcmp (value , "NULL" ) == 0 ) ? NULL : value ;
62
63
return 0 ;
63
64
}
65
+ /* Test target */
66
+ if (strcmp (key , "t" ) == 0 || strcmp (key , "target" ) == 0 ) {
67
+ return parse_target (value , tf );
68
+ }
64
69
65
70
/* Unknown key: report just so typos don’t silently pass. */
66
71
printf ("Unknown argument '-%s=%s'\n" , key , value );
@@ -75,6 +80,8 @@ static void help(void) {
75
80
printf (" -j=<num>, -jobs=<num> Number of parallel worker processes (default: 0 = sequential)\n" );
76
81
printf (" -iter=<num>, -iterations=<num> Number of iterations for each test (default: 16)\n" );
77
82
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" );
78
85
printf ("\n" );
79
86
printf ("Notes:\n" );
80
87
printf (" - All arguments must be provided in the form '-key=value'.\n" );
@@ -116,6 +123,31 @@ static int parse_iterations(const char* arg) {
116
123
return 0 ;
117
124
}
118
125
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
+ fprintf (stderr , "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
+
119
151
/* Read args; all must be "-key=value" */
120
152
static int read_args (int argc , char * * argv , int start , struct TestFramework * tf ) {
121
153
int i ;
@@ -149,13 +181,10 @@ static void run_test(const struct TestEntry* t) {
149
181
150
182
/* Process tests in sequential order */
151
183
static 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 ]);
159
188
}
160
189
return EXIT_SUCCESS ;
161
190
}
@@ -173,7 +202,7 @@ static int run_concurrent(struct TestFramework* tf) {
173
202
/* Loop iterator */
174
203
int it ;
175
204
/* Loop ref */
176
- TestRef ref ;
205
+ TestRef * ref ;
177
206
/* Launch worker processes */
178
207
for (it = 0 ; it < tf -> args .num_processes ; it ++ ) {
179
208
pid_t pid ;
@@ -190,9 +219,10 @@ static int run_concurrent(struct TestFramework* tf) {
190
219
191
220
if (pid == 0 ) {
192
221
/* Child worker: run tests assigned via pipe */
222
+ TestRef tref ;
193
223
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 ]);
196
226
}
197
227
_exit (EXIT_SUCCESS ); /* finish child process */
198
228
} else {
@@ -204,15 +234,13 @@ static int run_concurrent(struct TestFramework* tf) {
204
234
205
235
/* Now that we have all sub-processes, distribute workload in round-robin */
206
236
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 ;
215
242
}
243
+ if (++ worker_idx >= tf -> args .num_processes ) worker_idx = 0 ;
216
244
}
217
245
218
246
/* Close all pipes to signal workers to exit */
@@ -235,6 +263,7 @@ static int tf_init(struct TestFramework* tf, int argc, char** argv)
235
263
/* Initialize command-line options */
236
264
tf -> args .num_processes = 0 ;
237
265
tf -> args .custom_seed = NULL ;
266
+ tf -> args .targets .size = 0 ;
238
267
239
268
/* Disable buffering for stdout to improve reliability of getting
240
269
* diagnostic information. Happens right at the start of main because
@@ -282,11 +311,31 @@ static int tf_run(struct TestFramework* tf) {
282
311
/* Initial test time */
283
312
int64_t start_time = gettime_i64 (); /* maybe move this after the slots set */
284
313
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
+
285
332
/* Run test RNG tests (must run before we really initialize the test RNG) */
286
333
/* Note: currently, these tests are executed sequentially because there */
287
334
/* is really only one test. */
288
335
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
+ }
290
339
}
291
340
292
341
/* Initialize test RNG and library contexts */
0 commit comments