#include #include #include #include "defines.h" #include "main.h" #include "utils.h" #include "eval.h" #include "list.h" #include "edit.h" #ifdef ENABLE_PROVE /* * Run powwow --prove to get TAP output * * To use with prove, create a script powwow.t that contains powwow --prove * * Then run: * * prove powwow.t * * Or without a wrapper .t: * * prove --exec '' ./src/powwow :: --prove */ int _is_proving = 0; int num_tests = 0; #define PROVE_TTY_OUTPUT_LENGTH 40000 char prove_tty_output[PROVE_TTY_OUTPUT_LENGTH]; #define TAP_STRING_LENGTH 400 char tap_string[TAP_STRING_LENGTH]; void prove_save_tty_output(const char *s) { strncat(prove_tty_output, s, PROVE_TTY_OUTPUT_LENGTH - strlen(prove_tty_output) - 1); } int is_proving() { return _is_proving; } static void t_clear_tty_output() { strcpy(prove_tty_output, ""); } static const char *t_get_tty_output() { return prove_tty_output; } static void tap_putchar(const char c) { putchar(c); } static void tap_printf(const char *format, ...) { va_list va_args; va_start(va_args, format); vprintf(format, va_args); va_end(va_args); } static void tap_ok(const char *description) { tap_printf("ok %d - %s\n", ++num_tests, description); } static void tap_not_ok(const char *description) { tap_printf("not ok %d - %s\n", ++num_tests, description); } static void done_testing() { tap_printf("1..%d\n", num_tests); } char *tap_stringf(const char *format, ...) { va_list va_args; va_start(va_args, format); vsnprintf(tap_string, TAP_STRING_LENGTH, format, va_args); va_end(va_args); return tap_string; } static void todo(const char *description) { tap_not_ok(tap_stringf("# TODO %s", description)); } static void note(const char *description) { int offset = 0; char last_c = '\n'; while (description[offset] != 0) { if (last_c == '\n') { tap_printf("# "); } last_c = description[offset++]; tap_putchar(last_c); } tap_putchar('\n'); } static void note_tty_output() { note(tap_stringf("error = %d", error)); note("--- begin tty output ---"); note(t_get_tty_output()); note("--- end tty output ---"); } static void t_reset_errors() { error = 0; } static void t_simulate_user_input(const char *original_line) { char line[BUFSIZE]; // send in a copy since some function will modify input strcpy(line, original_line); note(tap_stringf("Simulating input: %s", line)); clear_line(0); t_reset_errors(); edlen = strlen(line); strcpy(edbuf, line); parse_user_input(line, 1); } static void t_simulate_output(const char *original_line) { char line[BUFSIZE]; // send in a copy since some function will modify input strcpy(line, original_line); note(tap_stringf("Simulating output: %s", line)); t_reset_errors(); smart_print(line, 1); } static void C_ok(int arg, const char *description) { if (arg) { tap_ok(description); } else { tap_not_ok(description); } } static void C_is(const char *input, const char *match, const char *description) { if (strcmp(input, match) == 0) { tap_ok(description); } else { tap_not_ok(description); note(tap_stringf( "C_is FAILED\n" " got: '%s'\n" " expected: '%s'\n", input, match)); } } static void C_contains(const char *input, const char *match, const char *description) { if (strstr(input, match)) { tap_ok(description); } else { tap_not_ok(description); note(tap_stringf( "C_contains FAILED\n" " got: '%s'\n" " expected: '%s'\n", input, match)); } } static void C_output_from_user_input_contains(char *user_input, const char *output_contains, const char *description) { t_clear_tty_output(); t_simulate_user_input(user_input); C_contains(t_get_tty_output(), output_contains, description); note_tty_output(); } static void C_output_from_user_input_is(char *user_input, const char *output_is, const char *description) { t_clear_tty_output(); t_simulate_user_input(user_input); C_is(t_get_tty_output(), output_is, description); note_tty_output(); } static void C_simulated_output_contains(char *original_output, const char *output_contains, const char *description) { t_clear_tty_output(); t_simulate_output(original_output); C_contains(t_get_tty_output(), output_contains, description); note_tty_output(); } static void C_simulated_output_is(char *original_output, const char *output_is, const char *description) { t_clear_tty_output(); t_simulate_output(original_output); C_is(t_get_tty_output(), output_is, description); note_tty_output(); } static void C_edit_from_user_input_is(char *user_input, const char *output_is, const char *description) { t_clear_tty_output(); strcpy(inserted_next, ""); t_simulate_user_input(user_input); C_is(inserted_next, output_is, description); note_tty_output(); } static void t_start_new_test() { /* this should init the globals again so everything is clean */ t_reset_errors(); t_simulate_user_input("#reset all"); t_clear_tty_output(); } static void do_test(void (*func)(), const char *name) { note(tap_stringf("-=-=-=-=- Setting up for test %s -=-=-=-=-\n", name)); t_start_new_test(); (*func)(); note(tap_stringf("-=-=-=-=- End test %s -=-=-=-=-\n", name)); } static void TEST_tap() { C_ok(1, "Should ok"); return; C_ok(0, "Should not ok"); } static void TEST_mark_syntax_error() { C_output_from_user_input_contains( "#mark foo=bar", "#invalid attribute syntax", "#mark - syntax error"); C_output_from_user_input_is( "#mark =bar", "#mark must be non-empty\n", "#mark - empty error"); C_output_from_user_input_is( "#mark foo&$foo=bar", "#mark two wildcards (& or $) may not be next to eachother\n", "#mark - two consecutive wildcards"); } static void TEST_mark_reset_step1() { C_output_from_user_input_is( "#mark foo=bold", "#new mark: foo=bold \n", "#mark - new mark is created"); C_simulated_output_is( "The word foo should be bold", "The word \x1b[1mfoo\x1b[0m should be bold", "#mark - foo should be bold"); C_output_from_user_input_is( "#mark", "#the following marker is defined:\n" "#mark foo=bold \n", "#mark - can list marks"); } static void TEST_mark_reset_step2() { C_output_from_user_input_is( "#mark", "#no markers are defined.\n", "#mark - there should be no marks defined after a reset"); C_simulated_output_is( "The word foo should not be bold", "The word foo should not be bold", "#mark - foo should be not be bold because of test reset"); } static void TEST_mark() { C_output_from_user_input_is( "#mark match rest &=underline", "#new mark: match rest &=underline \n", "#mark - new mark is created with pattern"); C_output_from_user_input_is( "#mark ^bar=underline", "#new mark: ^bar=underline \n", "#mark - new mark is created with anchor"); C_simulated_output_is( "bar - The word bar should be underlined at the beginning", "\x1b[4mbar\x1b[0m - The word bar should be underlined at the beginning", "#mark - ^bar should be underlined"); todo("The below test should fail when mbeg is fixed!"); // need to see if this is intended, mbeg suggests it should only match // at beginning of the line C_simulated_output_is( "The word bar should be not be underlined if not at the beginning (mbeg - THIS IS BROKEN?)", //"The word bar should be not be underlined if not at the beginning", "The word \x1b[4mbar\x1b[0m should be not be underlined if not at the beginning (mbeg - THIS IS BROKEN?)", "#mark - other bar should NOT be underlined"); todo("The above test should fail when mbeg is fixed!"); C_output_from_user_input_is( "#mark foo=bold", "#new mark: foo=bold \n", "#mark - new mark is created"); C_edit_from_user_input_is( "#mark foo", "#mark foo=bold ", "#mark - mark can be edited"); C_simulated_output_is( "The word foo should be bold", "The word \x1b[1mfoo\x1b[0m should be bold", "#mark - foo should be bold"); C_simulated_output_is( "After match rest all should be underlined and foo should not be bold", "After \x1b[4mmatch rest all should be underlined and foo should not be bold\x1b[0m", "#mark - match rest should be underlined but foo is not bold, matches seem to be greedy"); C_simulated_output_is( "The word foo and foo should be bold", "The word \x1b[1mfoo\x1b[0m and \x1b[1mfoo\x1b[0m should be bold", "#mark - multiple foo should be bold"); C_output_from_user_input_is( "#mark foo=", "#deleting mark: foo=bold \n", "#mark - mark is deleted"); C_output_from_user_input_is( "#mark ^bar=", "#deleting mark: ^bar=underline \n", "#mark - mark is deleted"); C_output_from_user_input_is( "#mark match rest &=", "#deleting mark: match rest &=underline \n", "#mark - mark is deleted"); C_simulated_output_is( "The word foo should not be bold", "The word foo should not be bold", "#mark - foo should not be bold"); C_output_from_user_input_is( "#mark", "#no markers are defined.\n", "#mark - all marks deleted"); } static void TEST_substitute_syntax_error() { C_output_from_user_input_is( "#substitute = ", "#substitute must be non-empty\n", "#substitute - syntax error"); } static void TEST_substitute() { C_output_from_user_input_contains( "#substitute foo=bar", "#new substitution: foo=bar\n", "#substitute - new substitute is created"); C_edit_from_user_input_is( "#substitute foo", "#substitute foo=bar", "#substitute - substitute can be edited"); C_simulated_output_is( "The word foo should be bar", "The word bar should be bar", "#substitute - foo should be bar"); C_output_from_user_input_is( "#substitute foo=", "#deleting substitution: foo=bar\n", "#substitute - substitute is deleted"); C_simulated_output_is( "The word foo should not be bar", "The word foo should not be bar", "#substitute - foo should not be bar"); } void prove() { _is_proving = 1; int looping = 2; while (looping--) { do_test(TEST_tap, "TAP output"); do_test(TEST_mark_syntax_error, "#mark syntax"); do_test(TEST_mark_reset_step1, "#mark reset step 1"); do_test(TEST_mark_reset_step2, "#mark reset step 2"); do_test(TEST_mark, "#mark usage"); do_test(TEST_substitute_syntax_error, "#substitute syntax error"); do_test(TEST_substitute, "#substitute usage"); } done_testing(); _is_proving = 0; } #else void prove() { printf("Prove is disabled\n"); } #endif