Skip to content

Commit 0e8cbce

Browse files
Merge pull request #102 from dreamer-coding/add_ini_parser
2 parents 17af088 + 3ad846b commit 0e8cbce

File tree

5 files changed

+225
-0
lines changed

5 files changed

+225
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ The Pizza Test CLI provides an efficient way to run and manage tests directly fr
7676
| `sort` | Sort tests by specified criteria. | Allows sorting in ascending or descending order. |
7777
| `shuffle` | Shuffle tests with optional parameters. | Includes options for specifying a seed or shuffle criteria. |
7878
| `color=<mode>` | Set color mode (enable, disable, auto). | Enhances readability in supported terminals. |
79+
| `config=<file>` | Specify a configuration file (must be pizza_test.ini). | Allows loading custom settings for test execution. |
7980
| `theme=<name>` | Set the theme (pizza, catch, doctest, etc.). | Customizes the appearance of test output. |
8081
| `verbose=<level>` | Set verbosity level (plain, ci, doge). | Adjusts the level of detail in test output. |
8182
| `timeout=<seconds>` | Set a timeout for test execution. | Ensures tests do not exceed the specified duration, helping to identify long-running tests. |

code/logic/common.c

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ static void _show_help(void) {
7676
pizza_io_printf("{cyan} sort Sort tests by specified criteria{reset}\n");
7777
pizza_io_printf("{cyan} shuffle Shuffle tests with optional parameters{reset}\n");
7878
pizza_io_printf("{cyan} color=<mode> Set color mode (enable, disable, auto){reset}\n");
79+
pizza_io_printf("{cyan} config=<file> Specify a configuration file (must be pizza_test.ini){reset}\n");
7980
pizza_io_printf("{cyan} theme=<name> Set the theme (fossil, catch, doctest, etc.){reset}\n");
8081
pizza_io_printf("{cyan} verbose=<level> Set verbosity level (plain, ci, doge){reset}\n");
8182
pizza_io_printf("{cyan} timeout=<seconds> Set the timeout for commands (default: 60 seconds){reset}\n");
@@ -343,6 +344,26 @@ fossil_pizza_pallet_t fossil_pizza_pallet_create(int argc, char** argv) {
343344
if (i + 1 < argc && pizza_io_cstr_compare(argv[i + 1], "--help") == 0) {
344345
_show_subhelp_color();
345346
}
347+
} else if (strncmp(argv[i], "config=", 7) == 0) {
348+
const char* config_file = argv[i] + 7;
349+
const char* basename = strrchr(config_file, '/');
350+
if (!basename) {
351+
basename = config_file; // No '/' found, use the entire filename
352+
} else {
353+
basename++; // Skip the '/'
354+
}
355+
356+
if (pizza_io_cstr_compare(basename, "pizza_test.ini") == 0) {
357+
pallet.config_file = config_file;
358+
} else {
359+
pizza_io_printf("{red}Error: Invalid configuration file name. Must be 'pizza_test.ini'.{reset}\n");
360+
exit(EXIT_FAILURE);
361+
}
362+
} else if (pizza_io_cstr_compare(argv[i], "config") == 0) {
363+
if (i + 1 < argc && pizza_io_cstr_compare(argv[i + 1], "--help") == 0) {
364+
_show_help();
365+
}
366+
346367
} else if (strncmp(argv[i], "theme=", 6) == 0) {
347368
const char* theme_str = argv[i] + 6;
348369
if (pizza_io_cstr_compare(theme_str, "fossil") == 0) {
@@ -404,6 +425,169 @@ fossil_pizza_pallet_t fossil_pizza_pallet_create(int argc, char** argv) {
404425
return pallet;
405426
}
406427

428+
// *****************************************************************************
429+
// INI Parser
430+
// *****************************************************************************
431+
432+
int fossil_pizza_ini_parse(const char *filename, fossil_pizza_pallet_t *pallet) {
433+
pizza_io_printf("{yellow}Warning: INI parser is experimental and in development.{reset}\n");
434+
const char *basename = strrchr(filename, '/');
435+
if (!basename) {
436+
basename = filename; // No '/' found, use the entire filename
437+
} else {
438+
basename++; // Skip the '/'
439+
}
440+
441+
if (pizza_io_cstr_compare(basename, "pizza_test.ini") != 0) {
442+
fprintf(stderr, "Error: INI file must be named 'pizza_test.ini'.\n");
443+
return -1;
444+
}
445+
446+
FILE *file = fopen(filename, "r");
447+
if (!file) {
448+
fprintf(stderr, "Error: Unable to open INI file: %s\n", basename);
449+
return -1;
450+
}
451+
452+
char line[256];
453+
char section[64] = "";
454+
while (fgets(line, sizeof(line), file)) {
455+
// Trim whitespace
456+
char *start = line;
457+
while (isspace((unsigned char)*start)) start++;
458+
char *end = start + strlen(start) - 1;
459+
while (end > start && isspace((unsigned char)*end)) *end-- = '\0';
460+
461+
// Skip comments and empty lines
462+
if (*start == ';' || *start == '#' || *start == '\0') continue;
463+
464+
// Handle section headers
465+
if (*start == '[') {
466+
char *close = strchr(start, ']');
467+
if (close) {
468+
*close = '\0';
469+
strncpy(section, start + 1, sizeof(section) - 1);
470+
section[sizeof(section) - 1] = '\0';
471+
}
472+
continue;
473+
}
474+
475+
// Handle key-value pairs
476+
char *equals = strchr(start, '=');
477+
if (equals) {
478+
*equals = '\0';
479+
char *key = start;
480+
char *value = equals + 1;
481+
482+
// Trim whitespace around key and value
483+
while (isspace((unsigned char)*key)) key++;
484+
end = key + strlen(key) - 1;
485+
while (end > key && isspace((unsigned char)*end)) *end-- = '\0';
486+
487+
while (isspace((unsigned char)*value)) value++;
488+
end = value + strlen(value) - 1;
489+
while (end > value && isspace((unsigned char)*end)) *end-- = '\0';
490+
491+
// Handle inline comments
492+
char *comment = strchr(value, ';');
493+
if (!comment) comment = strchr(value, '#');
494+
if (comment) *comment = '\0';
495+
496+
// Trim whitespace again after removing comments
497+
end = value + strlen(value) - 1;
498+
while (end > value && isspace((unsigned char)*end)) *end-- = '\0';
499+
500+
// Populate the pallet structure based on the section and key
501+
if (pizza_io_cstr_compare(section, "general") == 0) {
502+
if (pizza_io_cstr_compare(key, "theme") == 0) {
503+
if (pizza_io_cstr_compare(value, "fossil") == 0) {
504+
pallet->theme = PIZZA_THEME_FOSSIL;
505+
} else if (pizza_io_cstr_compare(value, "catch") == 0) {
506+
pallet->theme = PIZZA_THEME_CATCH;
507+
} else if (pizza_io_cstr_compare(value, "doctest") == 0) {
508+
pallet->theme = PIZZA_THEME_DOCTEST;
509+
} else if (pizza_io_cstr_compare(value, "cpputest") == 0) {
510+
pallet->theme = PIZZA_THEME_CPPUTEST;
511+
} else if (pizza_io_cstr_compare(value, "tap") == 0) {
512+
pallet->theme = PIZZA_THEME_TAP;
513+
} else if (pizza_io_cstr_compare(value, "gtest") == 0) {
514+
pallet->theme = PIZZA_THEME_GOOGLETEST;
515+
} else if (pizza_io_cstr_compare(value, "unity") == 0) {
516+
pallet->theme = PIZZA_THEME_UNITY;
517+
}
518+
} else if (pizza_io_cstr_compare(key, "verbose") == 0) {
519+
if (pizza_io_cstr_compare(value, "plain") == 0) {
520+
pallet->verbose = PIZZA_VERBOSE_PLAIN;
521+
} else if (pizza_io_cstr_compare(value, "ci") == 0) {
522+
pallet->verbose = PIZZA_VERBOSE_CI;
523+
} else if (pizza_io_cstr_compare(value, "doge") == 0) {
524+
pallet->verbose = PIZZA_VERBOSE_DOGE;
525+
}
526+
}
527+
} else if (pizza_io_cstr_compare(section, "test") == 0) {
528+
if (pizza_io_cstr_compare(key, "run.fail_fast") == 0) {
529+
pallet->run.fail_fast = atoi(value);
530+
} else if (pizza_io_cstr_compare(key, "run.only") == 0) {
531+
pallet->run.only = pizza_io_cstr_dup(value);
532+
} else if (pizza_io_cstr_compare(key, "run.repeat") == 0) {
533+
pallet->run.repeat = atoi(value);
534+
} else if (pizza_io_cstr_compare(key, "filter.test_name") == 0) {
535+
pallet->filter.test_name = pizza_io_cstr_dup(value);
536+
} else if (pizza_io_cstr_compare(key, "filter.suite_name") == 0) {
537+
pallet->filter.suite_name = pizza_io_cstr_dup(value);
538+
} else if (pizza_io_cstr_compare(key, "filter.tag") == 0) {
539+
int is_valid_tag = 0;
540+
for (int i = 0; VALID_TAGS[i] != null; i++) {
541+
if (pizza_io_cstr_compare(value, VALID_TAGS[i]) == 0) {
542+
is_valid_tag = 1;
543+
break;
544+
}
545+
}
546+
if (is_valid_tag) {
547+
pallet->filter.tag = pizza_io_cstr_dup(value);
548+
} else {
549+
fprintf(stderr, "Error: Invalid tag '%s'.\n", value);
550+
fclose(file);
551+
return -1;
552+
}
553+
} else if (pizza_io_cstr_compare(key, "sort.by") == 0) {
554+
int is_valid_criteria = 0;
555+
for (int i = 0; VALID_CRITERIA[i] != null; i++) {
556+
if (pizza_io_cstr_compare(value, VALID_CRITERIA[i]) == 0) {
557+
is_valid_criteria = 1;
558+
break;
559+
}
560+
}
561+
if (is_valid_criteria) {
562+
pallet->sort.by = pizza_io_cstr_dup(value);
563+
} else {
564+
fprintf(stderr, "Error: Invalid sort criteria '%s'.\n", value);
565+
fclose(file);
566+
return -1;
567+
}
568+
} else if (pizza_io_cstr_compare(key, "sort.order") == 0) {
569+
pallet->sort.order = pizza_io_cstr_dup(value);
570+
} else if (pizza_io_cstr_compare(key, "shuffle.seed") == 0) {
571+
pallet->shuffle.seed = pizza_io_cstr_dup(value);
572+
} else if (pizza_io_cstr_compare(key, "shuffle.count") == 0) {
573+
pallet->shuffle.count = atoi(value);
574+
} else if (pizza_io_cstr_compare(key, "shuffle.by") == 0) {
575+
pallet->shuffle.by = pizza_io_cstr_dup(value);
576+
}
577+
} else if (pizza_io_cstr_compare(section, "mock") == 0) {
578+
// Add mock-related parsing logic here
579+
} else if (pizza_io_cstr_compare(section, "mark") == 0) {
580+
// Add mark-related parsing logic here
581+
} else if (pizza_io_cstr_compare(section, "sanity") == 0) {
582+
// Add sanity-related parsing logic here
583+
}
584+
}
585+
}
586+
587+
fclose(file);
588+
return 0;
589+
}
590+
407591
// *****************************************************************************
408592
// Host information
409593
// *****************************************************************************

code/logic/fossil/pizza/common.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,7 @@ typedef enum {
295295

296296
typedef struct {
297297
int dry_run; // Flag for dry run mode
298+
const char* config_file; // Path to the configuration file
298299
struct {
299300
int fail_fast; // Flag for --fail-fast
300301
const char* only; // Value for --only
@@ -346,6 +347,25 @@ extern fossil_pizza_cli_verbose_t G_PIZZA_VERBOSE;
346347
*/
347348
fossil_pizza_pallet_t fossil_pizza_pallet_create(int argc, char** argv);
348349

350+
// *****************************************************************************
351+
// INI Parser
352+
// *****************************************************************************
353+
354+
// INI Parser Implementation
355+
356+
/**
357+
* @brief Parses an INI file and populates the provided pallet structure.
358+
*
359+
* This function reads an INI file and extracts key-value pairs to populate
360+
* the fossil_pizza_pallet_t structure. It assumes a simple INI format with
361+
* sections and key-value pairs.
362+
*
363+
* @param filename The path to the INI file.
364+
* @param pallet Pointer to the pallet structure to populate.
365+
* @return 0 on success, or a negative error code on failure.
366+
*/
367+
int fossil_pizza_ini_parse(const char *filename, fossil_pizza_pallet_t *pallet);
368+
349369
// *****************************************************************************
350370
// Host information
351371
// *****************************************************************************

code/logic/test.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,13 @@ int fossil_pizza_start(fossil_pizza_engine_t* engine, int argc, char** argv) {
4444
engine->pallet = fossil_pizza_pallet_create(argc, argv);
4545
pizza_sys_memory_set(&engine->score, 0, sizeof(engine->score));
4646

47+
// Parse configuration file if specified
48+
const char* config_file = engine->pallet.config_file;
49+
if (config_file && fossil_pizza_ini_parse(config_file, &engine->pallet) != FOSSIL_PIZZA_SUCCESS) {
50+
pizza_io_printf("Error: Failed to parse configuration file: %s\n", config_file);
51+
return FOSSIL_PIZZA_FAILURE;
52+
}
53+
4754
return FOSSIL_PIZZA_SUCCESS;
4855
}
4956

code/tests/pizza_test.ini

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# This file is used to configure the test settings for Pizza Test.
2+
[general]
3+
theme=fossil
4+
verbose=ci
5+
6+
[test]
7+
run.fail_fast=1
8+
run.repeat=3
9+
10+
sort.by=result
11+
shuffle.by=result
12+
13+
# sections for mock, sanity, and mark are in the works. PRs are welcome.

0 commit comments

Comments
 (0)