Message ID | 1337174425-21531-6-git-send-email-colin.king@canonical.com |
---|---|
State | Accepted |
Headers | show |
On 05/16/2012 09:20 PM, Colin King wrote: > From: Colin Ian King<colin.king@canonical.com> > > Signed-off-by: Colin Ian King<colin.king@canonical.com> Acked-by: Alex Hung <alex.hung@canonical.com>
On Wed, May 16, 2012 at 9:20 PM, Colin King <colin.king@canonical.com> wrote: > From: Colin Ian King <colin.king@canonical.com> > > Signed-off-by: Colin Ian King <colin.king@canonical.com> > --- > src/lib/include/fwts_framework.h | 3 +- > src/lib/include/fwts_log.h | 63 +++++++--- > src/lib/src/Makefile.am | 2 + > src/lib/src/fwts_framework.c | 94 +++++++++++++-- > src/lib/src/fwts_log.c | 239 ++++++++++++++++---------------------- > src/lib/src/fwts_log_json.c | 185 +++++++++++++++++++++++++++++ > src/lib/src/fwts_log_plaintext.c | 195 +++++++++++++++++++++++++++++++ > src/lib/src/fwts_summary.c | 16 ++- > src/lib/src/fwts_tag.c | 2 +- > 9 files changed, 623 insertions(+), 176 deletions(-) > create mode 100644 src/lib/src/fwts_log_json.c > create mode 100644 src/lib/src/fwts_log_plaintext.c > > diff --git a/src/lib/include/fwts_framework.h b/src/lib/include/fwts_framework.h > index 38879f6..fb74253 100644 > --- a/src/lib/include/fwts_framework.h > +++ b/src/lib/include/fwts_framework.h > @@ -26,7 +26,6 @@ > > #include "fwts_log.h" > #include "fwts_list.h" > -#include "fwts_framework.h" > > #define FWTS_FRAMEWORK_MAGIC 0x2af61aec > > @@ -139,6 +138,8 @@ typedef struct { > > int firmware_type; /* Type of firmware */ > int show_progress; /* Show progress while running current test */ > + > + fwts_log_type log_type; /* Output log type, default is plain text ASCII */ > } fwts_framework; > > typedef struct { > diff --git a/src/lib/include/fwts_log.h b/src/lib/include/fwts_log.h > index ab94029..8903bab 100644 > --- a/src/lib/include/fwts_log.h > +++ b/src/lib/include/fwts_log.h > @@ -53,19 +53,43 @@ typedef enum { > LOG_LEVEL_INFO = 0x00000010, > } fwts_log_level; > > +typedef enum { > + LOG_TYPE_NONE = 0x00000000, > + LOG_TYPE_PLAINTEXT = 0x00000001, > + LOG_TYPE_JSON = 0x00000002, > + LOG_TYPE_XML = 0x00000003, > +} fwts_log_type; > + > typedef struct log_t { > unsigned int magic; > FILE *fp; > char *owner; > int line_width; > int line_number; > + struct fwts_log_ops_t *ops; > } fwts_log; > > -fwts_log *fwts_log_open(const char* owner, const char *name, const char *mode); > +typedef struct fwts_log_ops_t { > + int (*vprintf)(fwts_log *log, const fwts_log_field field, const fwts_log_level level, const char *status, const char *label, const char *prefix, const char *fmt, va_list args); > + void (*underline)(fwts_log *log, int ch); > + void (*newline)(fwts_log *log); > + void (*section_begin)(fwts_log *, const char *tag); > + void (*section_end)(fwts_log *); > + void (*open)(fwts_log *); > + void (*close)(fwts_log *); > +} fwts_log_ops; > + > +fwts_log_ops fwts_log_plaintext_ops; > +fwts_log_ops fwts_log_json_ops; > + > +extern fwts_log_field fwts_log_filter; > +extern const char *fwts_log_format; > + > +fwts_log *fwts_log_open(const char* owner, const char *name, const char *mode, fwts_log_type); > int fwts_log_close(fwts_log *log); > -int fwts_log_printf(fwts_log *log, const fwts_log_field field, const fwts_log_level level, const char *fmt, ...) > - __attribute__((format(printf, 4, 5))); > -int fwts_log_vprintf(fwts_log *log, const fwts_log_field field, const fwts_log_level level, const char *fmt, va_list args); > +int fwts_log_printf(fwts_log *log, const fwts_log_field field, const fwts_log_level level, const char *status, const char *label, const char *prefix, const char *fmt, ...) > + __attribute__((format(printf, 7, 8))); > +int fwts_log_vprintf(fwts_log *log, const fwts_log_field field, const fwts_log_level level, const char *status, const char *label, const char *prefix, const char *fmt, va_list args); > void fwts_log_newline(fwts_log *log); > void fwts_log_underline(fwts_log *log, const int ch); > void fwts_log_set_field_filter(const char *str); > @@ -75,47 +99,52 @@ void fwts_log_print_fields(void); > void fwts_log_filter_set_field(const fwts_log_field filter); > void fwts_log_filter_unset_field(const fwts_log_field filter); > int fwts_log_str_to_level(const char *str); > +fwts_log_field fwts_log_str_to_field(const char *text); > char *fwts_log_level_to_str(const fwts_log_level level); > +char *fwts_log_field_to_str(const fwts_log_field field); > +char *fwts_log_field_to_str_full(const fwts_log_field field); > int fwts_log_line_number(fwts_log *log); > void fwts_log_set_line_width(const int width); > +void fwts_log_section_begin(fwts_log *log, const char *name); > +void fwts_log_section_end(fwts_log *log); > > #define fwts_log_result(fw, fmt, args...) \ > - fwts_log_printf(fw->results, LOG_RESULT, LOG_LEVEL_NONE, fmt, ## args) > + fwts_log_printf(fw->results, LOG_RESULT, LOG_LEVEL_NONE, "", "", "", fmt, ## args) > > #define fwts_log_warning(fw, fmt, args...) \ > - fwts_log_printf(fw->results, LOG_WARNING, LOG_LEVEL_NONE, fmt, ## args) > + fwts_log_printf(fw->results, LOG_WARNING, LOG_LEVEL_NONE, "", "", "", fmt, ## args) > > #define fwts_log_warning_verbatum(fw, fmt, args...) \ > - fwts_log_printf(fw->results, LOG_WARNING | LOG_VERBATUM, LOG_LEVEL_NONE, fmt, ## args) > + fwts_log_printf(fw->results, LOG_WARNING | LOG_VERBATUM, LOG_LEVEL_NONE, "", "", "", fmt, ## args) > > #define fwts_log_error(fw, fmt, args...) \ > - fwts_log_printf(fw->results, LOG_ERROR, LOG_LEVEL_NONE, fmt, ## args) > + fwts_log_printf(fw->results, LOG_ERROR, LOG_LEVEL_NONE, "", "", "", fmt, ## args) > > #define fwts_log_error_verbatum(fw, fmt, args...) \ > - fwts_log_printf(fw->results, LOG_ERROR | LOG_VERBATUM, LOG_LEVEL_NONE, fmt, ## args) > + fwts_log_printf(fw->results, LOG_ERROR | LOG_VERBATUM, LOG_LEVEL_NONE, "", "", "", fmt, ## args) > > #define fwts_log_info(fw, fmt, args...) \ > - fwts_log_printf(fw->results, LOG_INFO, LOG_LEVEL_NONE, fmt, ## args) > + fwts_log_printf(fw->results, LOG_INFO, LOG_LEVEL_NONE, "", "", "", fmt, ## args) > > #define fwts_log_info_verbatum(fw, fmt, args...) \ > - fwts_log_printf(fw->results, LOG_INFO | LOG_VERBATUM, LOG_LEVEL_NONE, fmt, ## args) > + fwts_log_printf(fw->results, LOG_INFO | LOG_VERBATUM, LOG_LEVEL_NONE, "", "", "", fmt, ## args) > > #define fwts_log_summary(fw, fmt, args...) \ > - fwts_log_printf(fw->results, LOG_SUMMARY, LOG_LEVEL_NONE, fmt, ## args) > + fwts_log_printf(fw->results, LOG_SUMMARY, LOG_LEVEL_NONE, "", "", "", fmt, ## args) > > #define fwts_log_summary_verbatum(fw, fmt, args...) \ > - fwts_log_printf(fw->results, LOG_SUMMARY | LOG_VERBATUM, LOG_LEVEL_NONE, fmt, ## args) > + fwts_log_printf(fw->results, LOG_SUMMARY | LOG_VERBATUM, LOG_LEVEL_NONE, "", "", "", fmt, ## args) > > #define fwts_log_advice(fw, fmt, args...) \ > - fwts_log_printf(fw->results, LOG_ADVICE, LOG_LEVEL_NONE, fmt, ## args) > + fwts_log_printf(fw->results, LOG_ADVICE, LOG_LEVEL_NONE, "", "", "", fmt, ## args) > > #define fwts_log_heading(fw, fmt, args...) \ > - fwts_log_printf(fw->results, LOG_HEADING, LOG_LEVEL_NONE, fmt, ## args) > + fwts_log_printf(fw->results, LOG_HEADING, LOG_LEVEL_NONE, "", "", "", fmt, ## args) > > #define fwts_log_tag(fw, fmt, args...) \ > - fwts_log_printf(fw->results, LOG_TAG | LOG_VERBATUM, LOG_LEVEL_NONE, fmt, ## args) > + fwts_log_printf(fw->results, LOG_TAG | LOG_VERBATUM, LOG_LEVEL_NONE, "", "", "", fmt, ## args) > > #define fwts_log_nl(fw) \ > - fwts_log_printf(fw->results, LOG_NEWLINE, LOG_LEVEL_NONE, "%s", "") > + fwts_log_printf(fw->results, LOG_NEWLINE, LOG_LEVEL_NONE, "", "", "", "%s", "") > > #endif > diff --git a/src/lib/src/Makefile.am b/src/lib/src/Makefile.am > index 2aac13e..cae1f91 100644 > --- a/src/lib/src/Makefile.am > +++ b/src/lib/src/Makefile.am > @@ -37,6 +37,8 @@ libfwts_la_SOURCES = \ > fwts_klog.c \ > fwts_list.c \ > fwts_log.c \ > + fwts_log_plaintext.c \ > + fwts_log_json.c \ > fwts_memorymap.c \ > fwts_microcode.c \ > fwts_mmap.c \ > diff --git a/src/lib/src/fwts_framework.c b/src/lib/src/fwts_framework.c > index da24b71..bc39b6b 100644 > --- a/src/lib/src/fwts_framework.c > +++ b/src/lib/src/fwts_framework.c > @@ -76,6 +76,7 @@ static fwts_option fwts_framework_options[] = { > { "json-data-path", "j:", 1, "Specify path to fwts json data files - default is /usr/share/fwts." }, > { "lp-tags-log", "", 0, "Output LaunchPad bug tags in results log." }, > { "disassemble-aml", "", 0, "Disassemble AML from DSDT and SSDT tables." }, > + { "log-type", "", 1, "Specify log type (plaintext or json)." }, > { NULL, NULL, 0, NULL } > }; > > @@ -111,6 +112,39 @@ static fwts_framework_setting fwts_framework_settings[] = { > { ID_NAME(FWTS_FRAMEWORK_INFOONLY), "INFO", NULL }, > }; > > +#if 0 > +static const char *fwts_framework_results_to_str(fwts_framework_results result) > +{ > + switch (result) { > + case FWTS_FRAMEWORK_PASSED: > + return "Passed"; > + case FWTS_FRAMEWORK_FAILED: > + return "Failed"; > + case FWTS_FRAMEWORK_FAILED_LOW: > + return "Failed Low"; > + case FWTS_FRAMEWORK_FAILED_HIGH: > + return "Failed High"; > + case FWTS_FRAMEWORK_FAILED_MEDIUM: > + return "Failed Medium"; > + case FWTS_FRAMEWORK_FAILED_CRITICAL: > + return "Failed Critical"; > + case FWTS_FRAMEWORK_WARNING: > + return "Warning"; > + case FWTS_FRAMEWORK_ERROR: > + return "Error"; > + case FWTS_FRAMEWORK_ADVICE: > + return "Advice"; > + case FWTS_FRAMEWORK_SKIPPED: > + return "Skipped"; > + case FWTS_FRAMEWORK_ABORTED: > + return "Aborted"; > + case FWTS_FRAMEWORK_INFOONLY: > + return "Info"; > + default: > + return "Unknown"; > +} > +#endif > + > /* > * fwts_framework_compare_priority() > * used to register tests sorted on run priority > @@ -535,6 +569,7 @@ static int fwts_framework_run_test(fwts_framework *fw, const int num_tests, fwts > > fw->failed_level = 0; > > + fwts_log_section_begin(fw->results, test->name); > fwts_log_set_owner(fw->results, test->name); > > fw->current_minor_test_num = 1; > @@ -591,20 +626,27 @@ static int fwts_framework_run_test(fwts_framework *fw, const int num_tests, fwts > goto done; > } > > + fwts_log_section_begin(fw->results, "subtests"); > for (minor_test = test->ops->minor_tests; > *minor_test->test_func != NULL; > minor_test++, fw->current_minor_test_num++) { > > + fwts_log_section_begin(fw->results, "subtest"); > fw->current_minor_test_name = minor_test->name; > > fwts_results_zero(&fw->minor_tests); > > - if (minor_test->name != NULL) > + if (minor_test->name != NULL) { > + fwts_log_section_begin(fw->results, "subtest_info"); > fwts_log_info(fw, "Test %d of %d: %s", > fw->current_minor_test_num, > test->ops->total_tests, minor_test->name); > + fwts_log_section_end(fw->results); > + } > > + fwts_log_section_begin(fw->results, "subtest_results"); > fwts_framework_minor_test_progress(fw, 0, ""); > + > ret = (*minor_test->test_func)(fw); > > /* Something went horribly wrong, abort all other tests too */ > @@ -625,23 +667,33 @@ static int fwts_framework_run_test(fwts_framework *fw, const int num_tests, fwts > fprintf(stderr, " %-55.55s %s\n", namebuf, > *resbuf ? resbuf : " "); > } > + fwts_log_section_end(fw->results); > fwts_log_nl(fw); > + fwts_log_section_end(fw->results); > } > + fwts_log_section_end(fw->results); > > fwts_framework_summate_results(&fw->total, &fw->current_major_test->results); > > if (test->ops->deinit) > test->ops->deinit(fw); > > - if (fw->flags & FWTS_FRAMEWORK_FLAGS_LP_TAGS_LOG) > + if (fw->flags & FWTS_FRAMEWORK_FLAGS_LP_TAGS_LOG) { > + fwts_log_section_begin(fw->results, "tags"); > fwts_tag_report(fw, LOG_TAG, &fw->test_taglist); > + fwts_log_section_end(fw->results); > + } > > done: > fwts_list_free_items(&fw->test_taglist, free); > > - if (!(test->flags & FWTS_UTILS)) > + if (!(test->flags & FWTS_UTILS)) { > + fwts_log_section_begin(fw->results, "results"); > fwts_framework_test_summary(fw); > + fwts_log_section_end(fw->results); > + } > > + fwts_log_section_end(fw->results); > fwts_log_set_owner(fw->results, "fwts"); > > return FWTS_OK; > @@ -694,6 +746,7 @@ void fwts_framework_log(fwts_framework *fw, > const char *fmt, ...) > { > char buffer[4096]; > + char prefix[256]; > char *str = fwts_framework_get_env(result); > > if (fmt) { > @@ -711,21 +764,24 @@ void fwts_framework_log(fwts_framework *fw, > switch (result) { > case FWTS_FRAMEWORK_ADVICE: > fwts_log_nl(fw); > - fwts_log_printf(fw->results, LOG_RESULT, level, "%s: %s", str, buffer); > + snprintf(prefix, sizeof(prefix), "%s: ", str); > + fwts_log_printf(fw->results, LOG_RESULT, level, str, label, prefix, "%s", buffer); > fwts_log_nl(fw); > break; > case FWTS_FRAMEWORK_FAILED: > fw->failed_level |= level; > fwts_summary_add(fw, fw->current_major_test->name, level, buffer); > - fwts_log_printf(fw->results, LOG_RESULT, level, "%s [%s] %s: Test %d, %s", > - str, fwts_log_level_to_str(level), label, fw->current_minor_test_num, buffer); > + snprintf(prefix, sizeof(prefix), "%s [%s] %s: Test %d, ", > + str, fwts_log_level_to_str(level), label, fw->current_minor_test_num); > + fwts_log_printf(fw->results, LOG_RESULT, level, str, label, prefix, "%s", buffer); > break; > case FWTS_FRAMEWORK_PASSED: > case FWTS_FRAMEWORK_WARNING: > case FWTS_FRAMEWORK_SKIPPED: > case FWTS_FRAMEWORK_ABORTED: > - fwts_log_printf(fw->results, LOG_RESULT, level, "%s: Test %d, %s", > - str, fw->current_minor_test_num, buffer); > + snprintf(prefix, sizeof(prefix), "%s: Test %d, ", > + str, fw->current_minor_test_num); > + fwts_log_printf(fw->results, LOG_RESULT, level, str, label, prefix, "%s", buffer); > break; > case FWTS_FRAMEWORK_INFOONLY: > break; /* no-op */ > @@ -979,6 +1035,16 @@ int fwts_framework_options_handler(fwts_framework *fw, int argc, char * const ar > case 31: /* --disassemble-aml */ > fwts_iasl_disassemble_all_to_file(fw); > return FWTS_COMPLETE; > + case 32: /* --log-type */ > + if (!strcmp(optarg, "plaintext")) > + fw->log_type = LOG_TYPE_PLAINTEXT; > + else if (!strcmp(optarg, "json")) > + fw->log_type = LOG_TYPE_JSON; > + else { > + fprintf(stderr, "--log-type can be either plaintext or json.\n"); > + return FWTS_ERROR; > + } > + break; > } > break; > case 'a': /* --all */ > @@ -1124,8 +1190,9 @@ int fwts_framework_args(const int argc, char **argv) > > /* Results log */ > if ((fw->results = fwts_log_open("fwts", > - fw->results_logname, > - fw->flags & FWTS_FRAMEWORK_FLAGS_FORCE_CLEAN ? "w" : "a")) == NULL) { > + fw->results_logname, > + fw->flags & FWTS_FRAMEWORK_FLAGS_FORCE_CLEAN ? "w" : "a", > + fw->log_type)) == NULL) { > ret = FWTS_ERROR; > fprintf(stderr, "%s: Cannot open results log '%s'.\n", argv[0], fw->results_logname); > goto tidy_close; > @@ -1165,10 +1232,16 @@ int fwts_framework_args(const int argc, char **argv) > fwts_list_len(&tests_to_run), > fw->results_logname); > > + fwts_log_section_begin(fw->results, "heading"); > fwts_framework_heading_info(fw, &tests_to_run); > + fwts_log_section_end(fw->results); > + > + fwts_log_section_begin(fw->results, "tests"); > fwts_framework_tests_run(fw, &tests_to_run); > + fwts_log_section_end(fw->results); > > if (fw->print_summary) { > + fwts_log_section_begin(fw->results, "summary"); > fwts_log_set_owner(fw->results, "summary"); > fwts_log_nl(fw); > if (fw->flags & FWTS_FRAMEWORK_FLAGS_LP_TAGS_LOG) > @@ -1176,6 +1249,7 @@ int fwts_framework_args(const int argc, char **argv) > fwts_framework_total_summary(fw); > fwts_log_nl(fw); > fwts_summary_report(fw, &fwts_framework_test_list); > + fwts_log_section_end(fw->results); > } > > if (fw->flags & FWTS_FRAMEWORK_FLAGS_LP_TAGS) > diff --git a/src/lib/src/fwts_log.c b/src/lib/src/fwts_log.c > index fabfcf5..5331fff 100644 > --- a/src/lib/src/fwts_log.c > +++ b/src/lib/src/fwts_log.c > @@ -32,9 +32,9 @@ > > static int log_line_width = 0; > > -static fwts_log_field fwts_log_filter = ~0; > +fwts_log_field fwts_log_filter = ~0; > > -static char fwts_log_format[256] = ""; > +const char *fwts_log_format = ""; > > /* > * fwts_log_set_line_width() > @@ -59,7 +59,7 @@ int fwts_log_line_number(fwts_log *log) > * fwts_log_field_to_str() > * return string name of log field > */ > -static char *fwts_log_field_to_str(const fwts_log_field field) > +char *fwts_log_field_to_str(const fwts_log_field field) > { > switch (field & LOG_FIELD_MASK) { > case LOG_RESULT: > @@ -90,6 +90,40 @@ static char *fwts_log_field_to_str(const fwts_log_field field) > } > > /* > + * fwts_log_field_to_str_full() > + * return full string name of log field > + */ > +char *fwts_log_field_to_str_full(const fwts_log_field field) > +{ > + switch (field & LOG_FIELD_MASK) { > + case LOG_RESULT: > + return "Result"; > + case LOG_ERROR: > + return "Error"; > + case LOG_WARNING: > + return "Warning"; > + case LOG_DEBUG: > + return "Debug"; > + case LOG_INFO: > + return "Info"; > + case LOG_SUMMARY: > + return "Summary"; > + case LOG_SEPARATOR: > + return "Separator"; > + case LOG_NEWLINE: > + return "Newline"; > + case LOG_ADVICE: > + return "Advice"; > + case LOG_HEADING: > + return "Heading"; > + case LOG_TAG: > + return "Tag"; > + default: > + return "Unknown"; > + } > +} > + > +/* > * fwts_log_str_to_level() > * return log level mapped from the given string > */ > @@ -158,7 +192,7 @@ void fwts_log_print_fields(void) > * fwts_log_str_to_field() > * return log field of a given string, 0 if not matching > */ > -static fwts_log_field fwts_log_str_to_field(const char *text) > +fwts_log_field fwts_log_str_to_field(const char *text) > { > int i; > > @@ -238,79 +272,26 @@ void fwts_log_set_field_filter(const char *str) > */ > void fwts_log_set_format(const char *str) > { > - strncpy(fwts_log_format, str, sizeof(fwts_log_format)-1); > - fwts_log_format[sizeof(fwts_log_format)-1]='\0'; > + fwts_log_format = str; > } > > /* > - * fwts_log_header() > - * format up a tabulated log heading > - */ > -static int fwts_log_header(fwts_log *log, char *buffer, const int len, const fwts_log_field field, const fwts_log_level level) > -{ > - char *ptr; > - int n = 0; > - struct tm tm; > - time_t now; > - > - time(&now); > - localtime_r(&now, &tm); > - > - for (ptr = fwts_log_format; *ptr; ) { > - if (*ptr == '%') { > - ptr++; > - if (!strncmp(ptr, "line", 4)) { > - n += snprintf(buffer + n, len - n, > - "%5.5d", log->line_number); > - ptr += 4; > - } > - if (!strncmp(ptr, "date", 4)) { > - n += snprintf(buffer + n, len - n, > - "%2.2d/%2.2d/%-2.2d", > - tm.tm_mday, tm.tm_mon + 1, (tm.tm_year+1900) % 100); > - ptr += 4; > - } > - if (!strncmp(ptr, "time", 4)) { > - n += snprintf(buffer + n, len - n, > - "%2.2d:%2.2d:%2.2d", > - tm.tm_hour, tm.tm_min, tm.tm_sec); > - ptr += 4; > - } > - if (!strncmp(ptr, "field", 5)) { > - n += snprintf(buffer + n, len - n, "%s", > - fwts_log_field_to_str(field)); > - ptr += 5; > - } > - if (!strncmp(ptr, "level", 5)) { > - n += snprintf(buffer + n, len - n, "%1.1s", > - fwts_log_level_to_str(level)); > - ptr += 5; > - } > - if (!strncmp(ptr,"owner", 5) && log->owner) { > - n += snprintf(buffer + n, len - n, "%-15.15s", log->owner); > - ptr += 5; > - } > - } > - else { > - n += snprintf(buffer+n, len-n, "%c", *ptr); > - ptr++; > - } > - } > - return n; > -} > - > - > -/* > * fwts_log_vprintf() > * printf to a log > */ > -int fwts_log_printf(fwts_log *log, const fwts_log_field field, const fwts_log_level level, const char *fmt, ...) > +int fwts_log_printf(fwts_log *log, > + const fwts_log_field field, > + const fwts_log_level level, > + const char *status, > + const char *label, > + const char *prefix, > + const char *fmt, ...) > { > va_list args; > int ret; > > va_start(args, fmt); > - ret = fwts_log_vprintf(log, field, level, fmt, args); > + ret = fwts_log_vprintf(log, field, level, status, label, prefix, fmt, args); > va_end(args); > > return ret; > @@ -320,53 +301,23 @@ int fwts_log_printf(fwts_log *log, const fwts_log_field field, const fwts_log_le > * fwts_log_vprintf() > * vprintf to a log > */ > -int fwts_log_vprintf(fwts_log *log, const fwts_log_field field, const fwts_log_level level, const char *fmt, va_list args) > +int fwts_log_vprintf(fwts_log *log, > + const fwts_log_field field, > + const fwts_log_level level, > + const char *status, > + const char *label, > + const char *prefix, > + const char *fmt, > + va_list args) > { > - char buffer[4096]; > - int n = 0; > - int len = 0; > - > - fwts_list *lines; > - fwts_list_link *item; > - > - if ((!log) || (log && log->magic != LOG_MAGIC)) > - return 0; > - > if (!((field & LOG_FIELD_MASK) & fwts_log_filter)) > return 0; > > - /* This is a pain, we neen to find out how big the leading log > - message is, so format one up. */ > - n = fwts_log_header(log, buffer, sizeof(buffer), field, level); > - > - vsnprintf(buffer+n, sizeof(buffer)-n, fmt, args); > - > - /* Break text into multi-lines if necessary */ > - if (field & LOG_VERBATUM) > - lines = fwts_list_from_text(buffer+n); > + if (log && log->magic == LOG_MAGIC && > + log->ops && log->ops->underline) > + return log->ops->vprintf(log, field, level, status, label, prefix, fmt, args); > else > - lines = fwts_format_text(buffer+n, log->line_width-n); > - > - len = n; > - > - fwts_list_foreach(item, lines) { > - char *text = fwts_text_list_text(item); > - > - if (!(field & LOG_NO_FIELDS)) { > - /* Re-format up a log heading with current line number which > - may increment with multiple line log messages */ > - fwts_log_header(log, buffer, sizeof(buffer), field, level); > - fwrite(buffer, 1, n, log->fp); > - } > - fwrite(text, 1, strlen(text), log->fp); > - fwrite("\n", 1, 1, log->fp); > - fflush(log->fp); > - log->line_number++; > - len += strlen(text) + 1; > - } > - fwts_text_list_free(lines); > - > - return len; > + return 0; > } > > /* > @@ -375,33 +326,9 @@ int fwts_log_vprintf(fwts_log *log, const fwts_log_field field, const fwts_log_l > */ > void fwts_log_underline(fwts_log *log, const int ch) > { > - int n; > - char *buffer; > - size_t width; > - > - if (!log || (log->magic != LOG_MAGIC)) > - return; > - > - if (!((LOG_SEPARATOR & LOG_FIELD_MASK) & fwts_log_filter)) > - return; > - > - width = log->line_width + 1; > - > - buffer = calloc(1, width); > - if (!buffer) > - return; /* Unlikely, and just abort */ > - > - /* Write in leading optional line prefix */ > - n = fwts_log_header(log, buffer, width, LOG_SEPARATOR, LOG_LEVEL_NONE); > - > - memset(buffer + n, ch, width - n); > - buffer[width - 1] = '\n'; > - > - fwrite(buffer, 1, width, log->fp); > - fflush(log->fp); > - log->line_number++; > - > - free(buffer); > + if (log && log->magic == LOG_MAGIC && > + log->ops && log->ops->underline) > + log->ops->underline(log, ch); > } > > /* > @@ -410,11 +337,9 @@ void fwts_log_underline(fwts_log *log, const int ch) > */ > void fwts_log_newline(fwts_log *log) > { > - if (log && (log->magic == LOG_MAGIC)) { > - fwrite("\n", 1, 1, log->fp); > - fflush(log->fp); > - log->line_number++; > - } > + if (log && log->magic == LOG_MAGIC && > + log->ops && log->ops->underline) > + log->ops->newline(log); > } > > int fwts_log_set_owner(fwts_log *log, const char *owner) > @@ -431,12 +356,26 @@ int fwts_log_set_owner(fwts_log *log, const char *owner) > return FWTS_ERROR; > } > > +void fwts_log_section_begin(fwts_log *log, const char *name) > +{ > + if (log && log->magic == LOG_MAGIC && > + log->ops && log->ops->section_begin) > + log->ops->section_begin(log, name); > +} > + > +void fwts_log_section_end(fwts_log *log) > +{ > + if (log && log->magic == LOG_MAGIC && > + log->ops && log->ops->section_end) > + log->ops->section_end(log); > +} > + > /* > * fwts_log_open() > * open a log file. if name is stderr or stdout, then attach log to these > * streams. > */ > -fwts_log *fwts_log_open(const char *owner, const char *name, const char *mode) > +fwts_log *fwts_log_open(const char *owner, const char *name, const char *mode, fwts_log_type type) > { > fwts_log *newlog; > > @@ -444,6 +383,18 @@ fwts_log *fwts_log_open(const char *owner, const char *name, const char *mode) > return NULL; > > newlog->magic = LOG_MAGIC; > + switch (type) { > + case LOG_TYPE_JSON: > + newlog->ops = &fwts_log_json_ops; > + break; > + case LOG_TYPE_PLAINTEXT: > + newlog->ops = &fwts_log_plaintext_ops; > + break; > + case LOG_TYPE_NONE: > + default: > + newlog->ops = &fwts_log_plaintext_ops; > + break; > + } > > if (owner) { > if ((newlog->owner = calloc(1, strlen(owner)+1)) == NULL) { > @@ -469,6 +420,9 @@ fwts_log *fwts_log_open(const char *owner, const char *name, const char *mode) > newlog->line_width = fwts_tty_width(fileno(newlog->fp), LOG_LINE_WIDTH); > } > > + if (newlog->ops && newlog->ops->open) > + newlog->ops->open(newlog); > + > return newlog; > } > > @@ -479,6 +433,9 @@ fwts_log *fwts_log_open(const char *owner, const char *name, const char *mode) > int fwts_log_close(fwts_log *log) > { > if (log && (log->magic == LOG_MAGIC)) { > + if (log->ops && log->ops->close) > + log->ops->close(log); > + > if (log->fp && (log->fp != stdout && log->fp != stderr)) > fclose(log->fp); > if (log->owner) > diff --git a/src/lib/src/fwts_log_json.c b/src/lib/src/fwts_log_json.c > new file mode 100644 > index 0000000..208847c > --- /dev/null > +++ b/src/lib/src/fwts_log_json.c > @@ -0,0 +1,185 @@ > +/* > + * Copyright (C) 2010-2012 Canonical > + * > + * This program is free software; you can redistribute it and/or > + * modify it under the terms of the GNU General Public License > + * as published by the Free Software Foundation; either version 2 > + * of the License, or (at your option) any later version. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + * > + * You should have received a copy of the GNU General Public License > + * along with this program; if not, write to the Free Software > + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. > + * > + */ > +#define LOG_LINE_WIDTH 100 > + > +#include <stdlib.h> > +#include <stdio.h> > +#include <stdarg.h> > +#include <string.h> > +#include <unistd.h> > +#include <sys/ioctl.h> > +#include <time.h> > + > +#include <json/json.h> > +#include "fwts.h" > + > +#define MAX_JSON_STACK (64) > + > +typedef struct { > + json_object *obj; > + json_object *log; > +} fwts_log_json_stack_t; > + > +static fwts_log_json_stack_t json_stack[MAX_JSON_STACK]; > +static int json_stack_index = 0; > + > +/* > + * fwts_log_vprintf_json() > + * vprintf to a log > + */ > +static int fwts_log_vprintf_json(fwts_log *log, > + const fwts_log_field field, > + const fwts_log_level level, > + const char *status, > + const char *label, > + const char *prefix, > + const char *fmt, > + va_list args) > +{ > + char buffer[4096]; > + struct tm tm; > + time_t now; > + json_object *header; > + json_object *json_log = (json_object*)json_stack[json_stack_index-1].log; > + char *str; > + > + if (!((field & LOG_FIELD_MASK) & fwts_log_filter)) > + return 0; > + > + if (field & (LOG_NEWLINE | LOG_SEPARATOR | LOG_DEBUG)) > + return 0; > + > + time(&now); > + localtime_r(&now, &tm); > + > + header = json_object_new_object(); > + json_object_object_add(header, "line_num", json_object_new_int(log->line_number)); > + snprintf(buffer, sizeof(buffer), "%2.2d/%2.2d/%-2.2d", > + tm.tm_mday, tm.tm_mon + 1, (tm.tm_year+1900) % 100); > + json_object_object_add(header, "date", json_object_new_string(buffer)); > + snprintf(buffer, sizeof(buffer), "%2.2d:%2.2d:%2.2d", > + tm.tm_hour, tm.tm_min, tm.tm_sec); > + json_object_object_add(header, "time", json_object_new_string(buffer)); > + json_object_object_add(header, "field_type", > + json_object_new_string(fwts_log_field_to_str_full(field))); > + > + str = fwts_log_level_to_str(level); > + if (!strcmp(str, " ")) > + str = "None"; > + json_object_object_add(header, "level", > + json_object_new_string(str)); > + > + json_object_object_add(header, "status", json_object_new_string(*status ? status : "None")); > + json_object_object_add(header, "failure_label", json_object_new_string(label && *label ? label : "None")); > + > + /* Redundant really > + json_object_object_add(header, "owner", > + json_object_new_string(log->owner)); > + */ > + vsnprintf(buffer, sizeof(buffer), fmt, args); > + json_object_object_add(header, "log_text", json_object_new_string(buffer)); > + > + json_object_array_add(json_log, header); > + > + log->line_number++; > + > + return 0; > +} > + > +/* > + * fwts_log_underline_json() > + * write an underline across log, using character ch as the underline > + */ > +static void fwts_log_underline_json(fwts_log *log, const int ch) > +{ > + /* No-op for json */ > +} > + > +/* > + * fwts_log_newline() > + * write newline to log > + */ > +static void fwts_log_newline_json(fwts_log *log) > +{ > + /* No-op for json */ > +} > + > +static void fwts_log_section_begin_json(fwts_log *log, const char *name) > +{ > + json_object *json_obj; > + json_object *json_log; > + > + json_obj = json_object_new_object(); > + json_log = json_object_new_array(); > + json_object_object_add(json_obj, name, json_log); > + > + json_stack[json_stack_index].obj = json_obj; > + json_stack[json_stack_index].log = json_log; > + > + if (json_stack_index > 0) > + json_object_array_add(json_stack[json_stack_index-1].log, json_obj); > + > + if (json_stack_index < MAX_JSON_STACK) > + json_stack_index++; > + else { > + fprintf(stderr, "json log stack overflow pushing section %s.\n", name); > + exit(EXIT_FAILURE); > + } > +} > + > +static void fwts_log_section_end_json(fwts_log *log) > +{ > + if (json_stack_index > 0) > + json_stack_index--; > + else { > + fprintf(stderr, "json log stack underflow.\n"); > + exit(EXIT_FAILURE); > + } > +} > + > +static void fwts_log_open_json(fwts_log *log) > +{ > + fwts_log_section_begin_json(log, "fwts"); > +} > + > +static void fwts_log_close_json(fwts_log *log) > +{ > + const char *str; > + size_t len; > + > + fwts_log_section_end_json(log); > + > + str = json_object_to_json_string(json_stack[0].obj); > + len = strlen(str); > + > + fwrite(str, 1, len, log->fp); > + fwrite("\n", 1, 1, log->fp); > + fflush(log->fp); > + json_object_put(json_stack[0].obj); > +} > + > +fwts_log_ops fwts_log_json_ops = { > + .vprintf = fwts_log_vprintf_json, > + .underline = fwts_log_underline_json, > + .newline = fwts_log_newline_json, > + .section_begin = fwts_log_section_begin_json, > + .section_end = fwts_log_section_end_json, > + .open = fwts_log_open_json, > + .close = fwts_log_close_json > +}; > diff --git a/src/lib/src/fwts_log_plaintext.c b/src/lib/src/fwts_log_plaintext.c > new file mode 100644 > index 0000000..44c443f > --- /dev/null > +++ b/src/lib/src/fwts_log_plaintext.c > @@ -0,0 +1,195 @@ > +/* > + * Copyright (C) 2010-2012 Canonical > + * > + * This program is free software; you can redistribute it and/or > + * modify it under the terms of the GNU General Public License > + * as published by the Free Software Foundation; either version 2 > + * of the License, or (at your option) any later version. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + * > + * You should have received a copy of the GNU General Public License > + * along with this program; if not, write to the Free Software > + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. > + * > + */ > +#define LOG_LINE_WIDTH 100 > + > +#include <stdlib.h> > +#include <stdio.h> > +#include <stdarg.h> > +#include <string.h> > +#include <unistd.h> > +#include <sys/ioctl.h> > +#include <time.h> > + > +#include "fwts.h" > + > +/* > + * fwts_log_header_plaintext() > + * format up a tabulated log heading > + */ > +static int fwts_log_header_plaintext(fwts_log *log, > + char *buffer, > + const int len, > + const fwts_log_field field, > + const fwts_log_level level) > +{ > + const char *ptr; > + int n = 0; > + struct tm tm; > + time_t now; > + > + time(&now); > + localtime_r(&now, &tm); > + > + for (ptr = fwts_log_format; *ptr; ) { > + if (*ptr == '%') { > + ptr++; > + if (!strncmp(ptr, "line", 4)) { > + n += snprintf(buffer + n, len - n, > + "%5.5d", log->line_number); > + ptr += 4; > + } > + if (!strncmp(ptr, "date", 4)) { > + n += snprintf(buffer + n, len - n, > + "%2.2d/%2.2d/%-2.2d", > + tm.tm_mday, tm.tm_mon + 1, (tm.tm_year+1900) % 100); > + ptr += 4; > + } > + if (!strncmp(ptr, "time", 4)) { > + n += snprintf(buffer + n, len - n, > + "%2.2d:%2.2d:%2.2d", > + tm.tm_hour, tm.tm_min, tm.tm_sec); > + ptr += 4; > + } > + if (!strncmp(ptr, "field", 5)) { > + n += snprintf(buffer + n, len - n, "%s", > + fwts_log_field_to_str(field)); > + ptr += 5; > + } > + if (!strncmp(ptr, "level", 5)) { > + n += snprintf(buffer + n, len - n, "%1.1s", > + fwts_log_level_to_str(level)); > + ptr += 5; > + } > + if (!strncmp(ptr,"owner", 5) && log->owner) { > + n += snprintf(buffer + n, len - n, "%-15.15s", log->owner); > + ptr += 5; > + } > + } else { > + n += snprintf(buffer+n, len-n, "%c", *ptr); > + ptr++; > + } > + } > + return n; > +} > + > + > +/* > + * fwts_log_vprintf() > + * vprintf to a log > + */ > +static int fwts_log_vprintf_plaintext(fwts_log *log, > + const fwts_log_field field, > + const fwts_log_level level, > + const char *status, /* Ignored */ > + const char *label, /* Ignored */ > + const char *prefix, > + const char *fmt, > + va_list args) > +{ > + char buffer[4096]; > + int n = 0; > + int header_len; > + int len = 0; > + > + fwts_list *lines; > + fwts_list_link *item; > + > + if (!((field & LOG_FIELD_MASK) & fwts_log_filter)) > + return 0; > + > + /* This is a pain, we neen to find out how big the leading log > + message is, so format one up. */ > + n = header_len = fwts_log_header_plaintext(log, buffer, sizeof(buffer), field, level); > + n += snprintf(buffer + n, sizeof(buffer) - n, "%s", prefix); > + n += vsnprintf(buffer + n, sizeof(buffer) - n, fmt, args); > + > + /* Break text into multi-lines if necessary */ > + if (field & LOG_VERBATUM) > + lines = fwts_list_from_text(buffer + header_len); > + else > + lines = fwts_format_text(buffer + header_len, log->line_width - header_len); > + > + len = n; > + > + fwts_list_foreach(item, lines) { > + char *text = fwts_text_list_text(item); > + > + if (!(field & LOG_NO_FIELDS)) { > + /* Re-format up a log heading with current line number which > + may increment with multiple line log messages */ > + fwts_log_header_plaintext(log, buffer, sizeof(buffer), field, level); > + fwrite(buffer, 1, header_len, log->fp); > + } > + fwrite(text, 1, strlen(text), log->fp); > + fwrite("\n", 1, 1, log->fp); > + fflush(log->fp); > + log->line_number++; > + len += strlen(text) + 1; > + } > + fwts_text_list_free(lines); > + > + return len; > +} > + > +/* > + * fwts_log_underline_plaintext() > + * write an underline across log, using character ch as the underline > + */ > +static void fwts_log_underline_plaintext(fwts_log *log, const int ch) > +{ > + int n; > + char *buffer; > + size_t width = log->line_width + 1; > + > + if (!((LOG_SEPARATOR & LOG_FIELD_MASK) & fwts_log_filter)) > + return; > + > + buffer = calloc(1, width); > + if (!buffer) > + return; /* Unlikely, and just abort */ > + > + /* Write in leading optional line prefix */ > + n = fwts_log_header_plaintext(log, buffer, width, LOG_SEPARATOR, LOG_LEVEL_NONE); > + > + memset(buffer + n, ch, width - n); > + buffer[width - 1] = '\n'; > + > + fwrite(buffer, 1, width, log->fp); > + fflush(log->fp); > + log->line_number++; > + > + free(buffer); > +} > + > +/* > + * fwts_log_newline_plaintext() > + * write newline to log > + */ > +static void fwts_log_newline_plaintext(fwts_log *log) > +{ > + fwrite("\n", 1, 1, log->fp); > + fflush(log->fp); > + log->line_number++; > +} > + > +fwts_log_ops fwts_log_plaintext_ops = { > + .vprintf = fwts_log_vprintf_plaintext, > + .underline = fwts_log_underline_plaintext, > + .newline = fwts_log_newline_plaintext > +}; > diff --git a/src/lib/src/fwts_summary.c b/src/lib/src/fwts_summary.c > index 192043d..38d6a79 100644 > --- a/src/lib/src/fwts_summary.c > +++ b/src/lib/src/fwts_summary.c > @@ -210,34 +210,38 @@ int fwts_summary_report(fwts_framework *fw, fwts_list *test_list) > fwts_list_link *item; > > fwts_log_summary(fw, "Test Failure Summary"); > - fwts_log_summary(fw, "===================="); > + fwts_log_underline(fw->results, '='); > fwts_log_nl(fw); > > for (i=0;i<SUMMARY_MAX;i++) { > + fwts_log_section_begin(fw->results, "failure"); > + > if (fwts_summaries[i]->len) { > fwts_list_link *item; > fwts_log_summary(fw, "%s failures: %d", summary_names[i], fwts_summaries[i]->len); > > + fwts_log_section_begin(fw->results, "failures"); > fwts_list_foreach(item, fwts_summaries[i]) { > fwts_summary_item *summary_item = fwts_list_data(fwts_summary_item *,item); > char *lines = fwts_summary_lines(&summary_item->log_lines); > - fwts_log_summary(fw, " %s test, at %d log line%s: %s", > + fwts_log_summary(fw, " %s test, at %d log line%s: %s: %s", > summary_item->test, > fwts_list_len(&summary_item->log_lines), > fwts_list_len(&summary_item->log_lines) > 1 ? "s" : "", > - lines); > - free(lines); > - fwts_log_summary_verbatum(fw, " \"%s\"", > + lines, > summary_item->text); > + free(lines); > } > + fwts_log_section_end(fw->results); > } > else > fwts_log_summary(fw, "%s failures: NONE", summary_names[i]); > > + fwts_log_section_end(fw->results); > fwts_log_nl(fw); > } > > - if (fw->total_run > 0) { > + if (fw->log_type == LOG_TYPE_PLAINTEXT && fw->total_run > 0) { > sorted = fwts_list_new(); > fwts_list_foreach(item, test_list) > fwts_list_add_ordered(sorted, fwts_list_data(fwts_framework_test *,item), fwts_framework_compare_test_name); > diff --git a/src/lib/src/fwts_tag.c b/src/lib/src/fwts_tag.c > index c1ba33d..38d1449 100644 > --- a/src/lib/src/fwts_tag.c > +++ b/src/lib/src/fwts_tag.c > @@ -180,7 +180,7 @@ void fwts_tag_report(fwts_framework *fw, const fwts_log_field field, fwts_list * > if ((taglist != NULL) && (fwts_list_len(taglist) > 0)) { > char *tags = fwts_tag_list_to_str(taglist); > if (tags) { > - fwts_log_printf(fw->results, field | LOG_VERBATUM, LOG_LEVEL_NONE, "Tags: %s", tags); > + fwts_log_printf(fw->results, field | LOG_VERBATUM, LOG_LEVEL_NONE, "", "", "", "Tags: %s", tags); > free(tags); > } > } > -- > 1.7.10 > Acked-by: Keng-Yu Lin <kengyu@canonical.com>
diff --git a/src/lib/include/fwts_framework.h b/src/lib/include/fwts_framework.h index 38879f6..fb74253 100644 --- a/src/lib/include/fwts_framework.h +++ b/src/lib/include/fwts_framework.h @@ -26,7 +26,6 @@ #include "fwts_log.h" #include "fwts_list.h" -#include "fwts_framework.h" #define FWTS_FRAMEWORK_MAGIC 0x2af61aec @@ -139,6 +138,8 @@ typedef struct { int firmware_type; /* Type of firmware */ int show_progress; /* Show progress while running current test */ + + fwts_log_type log_type; /* Output log type, default is plain text ASCII */ } fwts_framework; typedef struct { diff --git a/src/lib/include/fwts_log.h b/src/lib/include/fwts_log.h index ab94029..8903bab 100644 --- a/src/lib/include/fwts_log.h +++ b/src/lib/include/fwts_log.h @@ -53,19 +53,43 @@ typedef enum { LOG_LEVEL_INFO = 0x00000010, } fwts_log_level; +typedef enum { + LOG_TYPE_NONE = 0x00000000, + LOG_TYPE_PLAINTEXT = 0x00000001, + LOG_TYPE_JSON = 0x00000002, + LOG_TYPE_XML = 0x00000003, +} fwts_log_type; + typedef struct log_t { unsigned int magic; FILE *fp; char *owner; int line_width; int line_number; + struct fwts_log_ops_t *ops; } fwts_log; -fwts_log *fwts_log_open(const char* owner, const char *name, const char *mode); +typedef struct fwts_log_ops_t { + int (*vprintf)(fwts_log *log, const fwts_log_field field, const fwts_log_level level, const char *status, const char *label, const char *prefix, const char *fmt, va_list args); + void (*underline)(fwts_log *log, int ch); + void (*newline)(fwts_log *log); + void (*section_begin)(fwts_log *, const char *tag); + void (*section_end)(fwts_log *); + void (*open)(fwts_log *); + void (*close)(fwts_log *); +} fwts_log_ops; + +fwts_log_ops fwts_log_plaintext_ops; +fwts_log_ops fwts_log_json_ops; + +extern fwts_log_field fwts_log_filter; +extern const char *fwts_log_format; + +fwts_log *fwts_log_open(const char* owner, const char *name, const char *mode, fwts_log_type); int fwts_log_close(fwts_log *log); -int fwts_log_printf(fwts_log *log, const fwts_log_field field, const fwts_log_level level, const char *fmt, ...) - __attribute__((format(printf, 4, 5))); -int fwts_log_vprintf(fwts_log *log, const fwts_log_field field, const fwts_log_level level, const char *fmt, va_list args); +int fwts_log_printf(fwts_log *log, const fwts_log_field field, const fwts_log_level level, const char *status, const char *label, const char *prefix, const char *fmt, ...) + __attribute__((format(printf, 7, 8))); +int fwts_log_vprintf(fwts_log *log, const fwts_log_field field, const fwts_log_level level, const char *status, const char *label, const char *prefix, const char *fmt, va_list args); void fwts_log_newline(fwts_log *log); void fwts_log_underline(fwts_log *log, const int ch); void fwts_log_set_field_filter(const char *str); @@ -75,47 +99,52 @@ void fwts_log_print_fields(void); void fwts_log_filter_set_field(const fwts_log_field filter); void fwts_log_filter_unset_field(const fwts_log_field filter); int fwts_log_str_to_level(const char *str); +fwts_log_field fwts_log_str_to_field(const char *text); char *fwts_log_level_to_str(const fwts_log_level level); +char *fwts_log_field_to_str(const fwts_log_field field); +char *fwts_log_field_to_str_full(const fwts_log_field field); int fwts_log_line_number(fwts_log *log); void fwts_log_set_line_width(const int width); +void fwts_log_section_begin(fwts_log *log, const char *name); +void fwts_log_section_end(fwts_log *log); #define fwts_log_result(fw, fmt, args...) \ - fwts_log_printf(fw->results, LOG_RESULT, LOG_LEVEL_NONE, fmt, ## args) + fwts_log_printf(fw->results, LOG_RESULT, LOG_LEVEL_NONE, "", "", "", fmt, ## args) #define fwts_log_warning(fw, fmt, args...) \ - fwts_log_printf(fw->results, LOG_WARNING, LOG_LEVEL_NONE, fmt, ## args) + fwts_log_printf(fw->results, LOG_WARNING, LOG_LEVEL_NONE, "", "", "", fmt, ## args) #define fwts_log_warning_verbatum(fw, fmt, args...) \ - fwts_log_printf(fw->results, LOG_WARNING | LOG_VERBATUM, LOG_LEVEL_NONE, fmt, ## args) + fwts_log_printf(fw->results, LOG_WARNING | LOG_VERBATUM, LOG_LEVEL_NONE, "", "", "", fmt, ## args) #define fwts_log_error(fw, fmt, args...) \ - fwts_log_printf(fw->results, LOG_ERROR, LOG_LEVEL_NONE, fmt, ## args) + fwts_log_printf(fw->results, LOG_ERROR, LOG_LEVEL_NONE, "", "", "", fmt, ## args) #define fwts_log_error_verbatum(fw, fmt, args...) \ - fwts_log_printf(fw->results, LOG_ERROR | LOG_VERBATUM, LOG_LEVEL_NONE, fmt, ## args) + fwts_log_printf(fw->results, LOG_ERROR | LOG_VERBATUM, LOG_LEVEL_NONE, "", "", "", fmt, ## args) #define fwts_log_info(fw, fmt, args...) \ - fwts_log_printf(fw->results, LOG_INFO, LOG_LEVEL_NONE, fmt, ## args) + fwts_log_printf(fw->results, LOG_INFO, LOG_LEVEL_NONE, "", "", "", fmt, ## args) #define fwts_log_info_verbatum(fw, fmt, args...) \ - fwts_log_printf(fw->results, LOG_INFO | LOG_VERBATUM, LOG_LEVEL_NONE, fmt, ## args) + fwts_log_printf(fw->results, LOG_INFO | LOG_VERBATUM, LOG_LEVEL_NONE, "", "", "", fmt, ## args) #define fwts_log_summary(fw, fmt, args...) \ - fwts_log_printf(fw->results, LOG_SUMMARY, LOG_LEVEL_NONE, fmt, ## args) + fwts_log_printf(fw->results, LOG_SUMMARY, LOG_LEVEL_NONE, "", "", "", fmt, ## args) #define fwts_log_summary_verbatum(fw, fmt, args...) \ - fwts_log_printf(fw->results, LOG_SUMMARY | LOG_VERBATUM, LOG_LEVEL_NONE, fmt, ## args) + fwts_log_printf(fw->results, LOG_SUMMARY | LOG_VERBATUM, LOG_LEVEL_NONE, "", "", "", fmt, ## args) #define fwts_log_advice(fw, fmt, args...) \ - fwts_log_printf(fw->results, LOG_ADVICE, LOG_LEVEL_NONE, fmt, ## args) + fwts_log_printf(fw->results, LOG_ADVICE, LOG_LEVEL_NONE, "", "", "", fmt, ## args) #define fwts_log_heading(fw, fmt, args...) \ - fwts_log_printf(fw->results, LOG_HEADING, LOG_LEVEL_NONE, fmt, ## args) + fwts_log_printf(fw->results, LOG_HEADING, LOG_LEVEL_NONE, "", "", "", fmt, ## args) #define fwts_log_tag(fw, fmt, args...) \ - fwts_log_printf(fw->results, LOG_TAG | LOG_VERBATUM, LOG_LEVEL_NONE, fmt, ## args) + fwts_log_printf(fw->results, LOG_TAG | LOG_VERBATUM, LOG_LEVEL_NONE, "", "", "", fmt, ## args) #define fwts_log_nl(fw) \ - fwts_log_printf(fw->results, LOG_NEWLINE, LOG_LEVEL_NONE, "%s", "") + fwts_log_printf(fw->results, LOG_NEWLINE, LOG_LEVEL_NONE, "", "", "", "%s", "") #endif diff --git a/src/lib/src/Makefile.am b/src/lib/src/Makefile.am index 2aac13e..cae1f91 100644 --- a/src/lib/src/Makefile.am +++ b/src/lib/src/Makefile.am @@ -37,6 +37,8 @@ libfwts_la_SOURCES = \ fwts_klog.c \ fwts_list.c \ fwts_log.c \ + fwts_log_plaintext.c \ + fwts_log_json.c \ fwts_memorymap.c \ fwts_microcode.c \ fwts_mmap.c \ diff --git a/src/lib/src/fwts_framework.c b/src/lib/src/fwts_framework.c index da24b71..bc39b6b 100644 --- a/src/lib/src/fwts_framework.c +++ b/src/lib/src/fwts_framework.c @@ -76,6 +76,7 @@ static fwts_option fwts_framework_options[] = { { "json-data-path", "j:", 1, "Specify path to fwts json data files - default is /usr/share/fwts." }, { "lp-tags-log", "", 0, "Output LaunchPad bug tags in results log." }, { "disassemble-aml", "", 0, "Disassemble AML from DSDT and SSDT tables." }, + { "log-type", "", 1, "Specify log type (plaintext or json)." }, { NULL, NULL, 0, NULL } }; @@ -111,6 +112,39 @@ static fwts_framework_setting fwts_framework_settings[] = { { ID_NAME(FWTS_FRAMEWORK_INFOONLY), "INFO", NULL }, }; +#if 0 +static const char *fwts_framework_results_to_str(fwts_framework_results result) +{ + switch (result) { + case FWTS_FRAMEWORK_PASSED: + return "Passed"; + case FWTS_FRAMEWORK_FAILED: + return "Failed"; + case FWTS_FRAMEWORK_FAILED_LOW: + return "Failed Low"; + case FWTS_FRAMEWORK_FAILED_HIGH: + return "Failed High"; + case FWTS_FRAMEWORK_FAILED_MEDIUM: + return "Failed Medium"; + case FWTS_FRAMEWORK_FAILED_CRITICAL: + return "Failed Critical"; + case FWTS_FRAMEWORK_WARNING: + return "Warning"; + case FWTS_FRAMEWORK_ERROR: + return "Error"; + case FWTS_FRAMEWORK_ADVICE: + return "Advice"; + case FWTS_FRAMEWORK_SKIPPED: + return "Skipped"; + case FWTS_FRAMEWORK_ABORTED: + return "Aborted"; + case FWTS_FRAMEWORK_INFOONLY: + return "Info"; + default: + return "Unknown"; +} +#endif + /* * fwts_framework_compare_priority() * used to register tests sorted on run priority @@ -535,6 +569,7 @@ static int fwts_framework_run_test(fwts_framework *fw, const int num_tests, fwts fw->failed_level = 0; + fwts_log_section_begin(fw->results, test->name); fwts_log_set_owner(fw->results, test->name); fw->current_minor_test_num = 1; @@ -591,20 +626,27 @@ static int fwts_framework_run_test(fwts_framework *fw, const int num_tests, fwts goto done; } + fwts_log_section_begin(fw->results, "subtests"); for (minor_test = test->ops->minor_tests; *minor_test->test_func != NULL; minor_test++, fw->current_minor_test_num++) { + fwts_log_section_begin(fw->results, "subtest"); fw->current_minor_test_name = minor_test->name; fwts_results_zero(&fw->minor_tests); - if (minor_test->name != NULL) + if (minor_test->name != NULL) { + fwts_log_section_begin(fw->results, "subtest_info"); fwts_log_info(fw, "Test %d of %d: %s", fw->current_minor_test_num, test->ops->total_tests, minor_test->name); + fwts_log_section_end(fw->results); + } + fwts_log_section_begin(fw->results, "subtest_results"); fwts_framework_minor_test_progress(fw, 0, ""); + ret = (*minor_test->test_func)(fw); /* Something went horribly wrong, abort all other tests too */ @@ -625,23 +667,33 @@ static int fwts_framework_run_test(fwts_framework *fw, const int num_tests, fwts fprintf(stderr, " %-55.55s %s\n", namebuf, *resbuf ? resbuf : " "); } + fwts_log_section_end(fw->results); fwts_log_nl(fw); + fwts_log_section_end(fw->results); } + fwts_log_section_end(fw->results); fwts_framework_summate_results(&fw->total, &fw->current_major_test->results); if (test->ops->deinit) test->ops->deinit(fw); - if (fw->flags & FWTS_FRAMEWORK_FLAGS_LP_TAGS_LOG) + if (fw->flags & FWTS_FRAMEWORK_FLAGS_LP_TAGS_LOG) { + fwts_log_section_begin(fw->results, "tags"); fwts_tag_report(fw, LOG_TAG, &fw->test_taglist); + fwts_log_section_end(fw->results); + } done: fwts_list_free_items(&fw->test_taglist, free); - if (!(test->flags & FWTS_UTILS)) + if (!(test->flags & FWTS_UTILS)) { + fwts_log_section_begin(fw->results, "results"); fwts_framework_test_summary(fw); + fwts_log_section_end(fw->results); + } + fwts_log_section_end(fw->results); fwts_log_set_owner(fw->results, "fwts"); return FWTS_OK; @@ -694,6 +746,7 @@ void fwts_framework_log(fwts_framework *fw, const char *fmt, ...) { char buffer[4096]; + char prefix[256]; char *str = fwts_framework_get_env(result); if (fmt) { @@ -711,21 +764,24 @@ void fwts_framework_log(fwts_framework *fw, switch (result) { case FWTS_FRAMEWORK_ADVICE: fwts_log_nl(fw); - fwts_log_printf(fw->results, LOG_RESULT, level, "%s: %s", str, buffer); + snprintf(prefix, sizeof(prefix), "%s: ", str); + fwts_log_printf(fw->results, LOG_RESULT, level, str, label, prefix, "%s", buffer); fwts_log_nl(fw); break; case FWTS_FRAMEWORK_FAILED: fw->failed_level |= level; fwts_summary_add(fw, fw->current_major_test->name, level, buffer); - fwts_log_printf(fw->results, LOG_RESULT, level, "%s [%s] %s: Test %d, %s", - str, fwts_log_level_to_str(level), label, fw->current_minor_test_num, buffer); + snprintf(prefix, sizeof(prefix), "%s [%s] %s: Test %d, ", + str, fwts_log_level_to_str(level), label, fw->current_minor_test_num); + fwts_log_printf(fw->results, LOG_RESULT, level, str, label, prefix, "%s", buffer); break; case FWTS_FRAMEWORK_PASSED: case FWTS_FRAMEWORK_WARNING: case FWTS_FRAMEWORK_SKIPPED: case FWTS_FRAMEWORK_ABORTED: - fwts_log_printf(fw->results, LOG_RESULT, level, "%s: Test %d, %s", - str, fw->current_minor_test_num, buffer); + snprintf(prefix, sizeof(prefix), "%s: Test %d, ", + str, fw->current_minor_test_num); + fwts_log_printf(fw->results, LOG_RESULT, level, str, label, prefix, "%s", buffer); break; case FWTS_FRAMEWORK_INFOONLY: break; /* no-op */ @@ -979,6 +1035,16 @@ int fwts_framework_options_handler(fwts_framework *fw, int argc, char * const ar case 31: /* --disassemble-aml */ fwts_iasl_disassemble_all_to_file(fw); return FWTS_COMPLETE; + case 32: /* --log-type */ + if (!strcmp(optarg, "plaintext")) + fw->log_type = LOG_TYPE_PLAINTEXT; + else if (!strcmp(optarg, "json")) + fw->log_type = LOG_TYPE_JSON; + else { + fprintf(stderr, "--log-type can be either plaintext or json.\n"); + return FWTS_ERROR; + } + break; } break; case 'a': /* --all */ @@ -1124,8 +1190,9 @@ int fwts_framework_args(const int argc, char **argv) /* Results log */ if ((fw->results = fwts_log_open("fwts", - fw->results_logname, - fw->flags & FWTS_FRAMEWORK_FLAGS_FORCE_CLEAN ? "w" : "a")) == NULL) { + fw->results_logname, + fw->flags & FWTS_FRAMEWORK_FLAGS_FORCE_CLEAN ? "w" : "a", + fw->log_type)) == NULL) { ret = FWTS_ERROR; fprintf(stderr, "%s: Cannot open results log '%s'.\n", argv[0], fw->results_logname); goto tidy_close; @@ -1165,10 +1232,16 @@ int fwts_framework_args(const int argc, char **argv) fwts_list_len(&tests_to_run), fw->results_logname); + fwts_log_section_begin(fw->results, "heading"); fwts_framework_heading_info(fw, &tests_to_run); + fwts_log_section_end(fw->results); + + fwts_log_section_begin(fw->results, "tests"); fwts_framework_tests_run(fw, &tests_to_run); + fwts_log_section_end(fw->results); if (fw->print_summary) { + fwts_log_section_begin(fw->results, "summary"); fwts_log_set_owner(fw->results, "summary"); fwts_log_nl(fw); if (fw->flags & FWTS_FRAMEWORK_FLAGS_LP_TAGS_LOG) @@ -1176,6 +1249,7 @@ int fwts_framework_args(const int argc, char **argv) fwts_framework_total_summary(fw); fwts_log_nl(fw); fwts_summary_report(fw, &fwts_framework_test_list); + fwts_log_section_end(fw->results); } if (fw->flags & FWTS_FRAMEWORK_FLAGS_LP_TAGS) diff --git a/src/lib/src/fwts_log.c b/src/lib/src/fwts_log.c index fabfcf5..5331fff 100644 --- a/src/lib/src/fwts_log.c +++ b/src/lib/src/fwts_log.c @@ -32,9 +32,9 @@ static int log_line_width = 0; -static fwts_log_field fwts_log_filter = ~0; +fwts_log_field fwts_log_filter = ~0; -static char fwts_log_format[256] = ""; +const char *fwts_log_format = ""; /* * fwts_log_set_line_width() @@ -59,7 +59,7 @@ int fwts_log_line_number(fwts_log *log) * fwts_log_field_to_str() * return string name of log field */ -static char *fwts_log_field_to_str(const fwts_log_field field) +char *fwts_log_field_to_str(const fwts_log_field field) { switch (field & LOG_FIELD_MASK) { case LOG_RESULT: @@ -90,6 +90,40 @@ static char *fwts_log_field_to_str(const fwts_log_field field) } /* + * fwts_log_field_to_str_full() + * return full string name of log field + */ +char *fwts_log_field_to_str_full(const fwts_log_field field) +{ + switch (field & LOG_FIELD_MASK) { + case LOG_RESULT: + return "Result"; + case LOG_ERROR: + return "Error"; + case LOG_WARNING: + return "Warning"; + case LOG_DEBUG: + return "Debug"; + case LOG_INFO: + return "Info"; + case LOG_SUMMARY: + return "Summary"; + case LOG_SEPARATOR: + return "Separator"; + case LOG_NEWLINE: + return "Newline"; + case LOG_ADVICE: + return "Advice"; + case LOG_HEADING: + return "Heading"; + case LOG_TAG: + return "Tag"; + default: + return "Unknown"; + } +} + +/* * fwts_log_str_to_level() * return log level mapped from the given string */ @@ -158,7 +192,7 @@ void fwts_log_print_fields(void) * fwts_log_str_to_field() * return log field of a given string, 0 if not matching */ -static fwts_log_field fwts_log_str_to_field(const char *text) +fwts_log_field fwts_log_str_to_field(const char *text) { int i; @@ -238,79 +272,26 @@ void fwts_log_set_field_filter(const char *str) */ void fwts_log_set_format(const char *str) { - strncpy(fwts_log_format, str, sizeof(fwts_log_format)-1); - fwts_log_format[sizeof(fwts_log_format)-1]='\0'; + fwts_log_format = str; } /* - * fwts_log_header() - * format up a tabulated log heading - */ -static int fwts_log_header(fwts_log *log, char *buffer, const int len, const fwts_log_field field, const fwts_log_level level) -{ - char *ptr; - int n = 0; - struct tm tm; - time_t now; - - time(&now); - localtime_r(&now, &tm); - - for (ptr = fwts_log_format; *ptr; ) { - if (*ptr == '%') { - ptr++; - if (!strncmp(ptr, "line", 4)) { - n += snprintf(buffer + n, len - n, - "%5.5d", log->line_number); - ptr += 4; - } - if (!strncmp(ptr, "date", 4)) { - n += snprintf(buffer + n, len - n, - "%2.2d/%2.2d/%-2.2d", - tm.tm_mday, tm.tm_mon + 1, (tm.tm_year+1900) % 100); - ptr += 4; - } - if (!strncmp(ptr, "time", 4)) { - n += snprintf(buffer + n, len - n, - "%2.2d:%2.2d:%2.2d", - tm.tm_hour, tm.tm_min, tm.tm_sec); - ptr += 4; - } - if (!strncmp(ptr, "field", 5)) { - n += snprintf(buffer + n, len - n, "%s", - fwts_log_field_to_str(field)); - ptr += 5; - } - if (!strncmp(ptr, "level", 5)) { - n += snprintf(buffer + n, len - n, "%1.1s", - fwts_log_level_to_str(level)); - ptr += 5; - } - if (!strncmp(ptr,"owner", 5) && log->owner) { - n += snprintf(buffer + n, len - n, "%-15.15s", log->owner); - ptr += 5; - } - } - else { - n += snprintf(buffer+n, len-n, "%c", *ptr); - ptr++; - } - } - return n; -} - - -/* * fwts_log_vprintf() * printf to a log */ -int fwts_log_printf(fwts_log *log, const fwts_log_field field, const fwts_log_level level, const char *fmt, ...) +int fwts_log_printf(fwts_log *log, + const fwts_log_field field, + const fwts_log_level level, + const char *status, + const char *label, + const char *prefix, + const char *fmt, ...) { va_list args; int ret; va_start(args, fmt); - ret = fwts_log_vprintf(log, field, level, fmt, args); + ret = fwts_log_vprintf(log, field, level, status, label, prefix, fmt, args); va_end(args); return ret; @@ -320,53 +301,23 @@ int fwts_log_printf(fwts_log *log, const fwts_log_field field, const fwts_log_le * fwts_log_vprintf() * vprintf to a log */ -int fwts_log_vprintf(fwts_log *log, const fwts_log_field field, const fwts_log_level level, const char *fmt, va_list args) +int fwts_log_vprintf(fwts_log *log, + const fwts_log_field field, + const fwts_log_level level, + const char *status, + const char *label, + const char *prefix, + const char *fmt, + va_list args) { - char buffer[4096]; - int n = 0; - int len = 0; - - fwts_list *lines; - fwts_list_link *item; - - if ((!log) || (log && log->magic != LOG_MAGIC)) - return 0; - if (!((field & LOG_FIELD_MASK) & fwts_log_filter)) return 0; - /* This is a pain, we neen to find out how big the leading log - message is, so format one up. */ - n = fwts_log_header(log, buffer, sizeof(buffer), field, level); - - vsnprintf(buffer+n, sizeof(buffer)-n, fmt, args); - - /* Break text into multi-lines if necessary */ - if (field & LOG_VERBATUM) - lines = fwts_list_from_text(buffer+n); + if (log && log->magic == LOG_MAGIC && + log->ops && log->ops->underline) + return log->ops->vprintf(log, field, level, status, label, prefix, fmt, args); else - lines = fwts_format_text(buffer+n, log->line_width-n); - - len = n; - - fwts_list_foreach(item, lines) { - char *text = fwts_text_list_text(item); - - if (!(field & LOG_NO_FIELDS)) { - /* Re-format up a log heading with current line number which - may increment with multiple line log messages */ - fwts_log_header(log, buffer, sizeof(buffer), field, level); - fwrite(buffer, 1, n, log->fp); - } - fwrite(text, 1, strlen(text), log->fp); - fwrite("\n", 1, 1, log->fp); - fflush(log->fp); - log->line_number++; - len += strlen(text) + 1; - } - fwts_text_list_free(lines); - - return len; + return 0; } /* @@ -375,33 +326,9 @@ int fwts_log_vprintf(fwts_log *log, const fwts_log_field field, const fwts_log_l */ void fwts_log_underline(fwts_log *log, const int ch) { - int n; - char *buffer; - size_t width; - - if (!log || (log->magic != LOG_MAGIC)) - return; - - if (!((LOG_SEPARATOR & LOG_FIELD_MASK) & fwts_log_filter)) - return; - - width = log->line_width + 1; - - buffer = calloc(1, width); - if (!buffer) - return; /* Unlikely, and just abort */ - - /* Write in leading optional line prefix */ - n = fwts_log_header(log, buffer, width, LOG_SEPARATOR, LOG_LEVEL_NONE); - - memset(buffer + n, ch, width - n); - buffer[width - 1] = '\n'; - - fwrite(buffer, 1, width, log->fp); - fflush(log->fp); - log->line_number++; - - free(buffer); + if (log && log->magic == LOG_MAGIC && + log->ops && log->ops->underline) + log->ops->underline(log, ch); } /* @@ -410,11 +337,9 @@ void fwts_log_underline(fwts_log *log, const int ch) */ void fwts_log_newline(fwts_log *log) { - if (log && (log->magic == LOG_MAGIC)) { - fwrite("\n", 1, 1, log->fp); - fflush(log->fp); - log->line_number++; - } + if (log && log->magic == LOG_MAGIC && + log->ops && log->ops->underline) + log->ops->newline(log); } int fwts_log_set_owner(fwts_log *log, const char *owner) @@ -431,12 +356,26 @@ int fwts_log_set_owner(fwts_log *log, const char *owner) return FWTS_ERROR; } +void fwts_log_section_begin(fwts_log *log, const char *name) +{ + if (log && log->magic == LOG_MAGIC && + log->ops && log->ops->section_begin) + log->ops->section_begin(log, name); +} + +void fwts_log_section_end(fwts_log *log) +{ + if (log && log->magic == LOG_MAGIC && + log->ops && log->ops->section_end) + log->ops->section_end(log); +} + /* * fwts_log_open() * open a log file. if name is stderr or stdout, then attach log to these * streams. */ -fwts_log *fwts_log_open(const char *owner, const char *name, const char *mode) +fwts_log *fwts_log_open(const char *owner, const char *name, const char *mode, fwts_log_type type) { fwts_log *newlog; @@ -444,6 +383,18 @@ fwts_log *fwts_log_open(const char *owner, const char *name, const char *mode) return NULL; newlog->magic = LOG_MAGIC; + switch (type) { + case LOG_TYPE_JSON: + newlog->ops = &fwts_log_json_ops; + break; + case LOG_TYPE_PLAINTEXT: + newlog->ops = &fwts_log_plaintext_ops; + break; + case LOG_TYPE_NONE: + default: + newlog->ops = &fwts_log_plaintext_ops; + break; + } if (owner) { if ((newlog->owner = calloc(1, strlen(owner)+1)) == NULL) { @@ -469,6 +420,9 @@ fwts_log *fwts_log_open(const char *owner, const char *name, const char *mode) newlog->line_width = fwts_tty_width(fileno(newlog->fp), LOG_LINE_WIDTH); } + if (newlog->ops && newlog->ops->open) + newlog->ops->open(newlog); + return newlog; } @@ -479,6 +433,9 @@ fwts_log *fwts_log_open(const char *owner, const char *name, const char *mode) int fwts_log_close(fwts_log *log) { if (log && (log->magic == LOG_MAGIC)) { + if (log->ops && log->ops->close) + log->ops->close(log); + if (log->fp && (log->fp != stdout && log->fp != stderr)) fclose(log->fp); if (log->owner) diff --git a/src/lib/src/fwts_log_json.c b/src/lib/src/fwts_log_json.c new file mode 100644 index 0000000..208847c --- /dev/null +++ b/src/lib/src/fwts_log_json.c @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2010-2012 Canonical + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#define LOG_LINE_WIDTH 100 + +#include <stdlib.h> +#include <stdio.h> +#include <stdarg.h> +#include <string.h> +#include <unistd.h> +#include <sys/ioctl.h> +#include <time.h> + +#include <json/json.h> +#include "fwts.h" + +#define MAX_JSON_STACK (64) + +typedef struct { + json_object *obj; + json_object *log; +} fwts_log_json_stack_t; + +static fwts_log_json_stack_t json_stack[MAX_JSON_STACK]; +static int json_stack_index = 0; + +/* + * fwts_log_vprintf_json() + * vprintf to a log + */ +static int fwts_log_vprintf_json(fwts_log *log, + const fwts_log_field field, + const fwts_log_level level, + const char *status, + const char *label, + const char *prefix, + const char *fmt, + va_list args) +{ + char buffer[4096]; + struct tm tm; + time_t now; + json_object *header; + json_object *json_log = (json_object*)json_stack[json_stack_index-1].log; + char *str; + + if (!((field & LOG_FIELD_MASK) & fwts_log_filter)) + return 0; + + if (field & (LOG_NEWLINE | LOG_SEPARATOR | LOG_DEBUG)) + return 0; + + time(&now); + localtime_r(&now, &tm); + + header = json_object_new_object(); + json_object_object_add(header, "line_num", json_object_new_int(log->line_number)); + snprintf(buffer, sizeof(buffer), "%2.2d/%2.2d/%-2.2d", + tm.tm_mday, tm.tm_mon + 1, (tm.tm_year+1900) % 100); + json_object_object_add(header, "date", json_object_new_string(buffer)); + snprintf(buffer, sizeof(buffer), "%2.2d:%2.2d:%2.2d", + tm.tm_hour, tm.tm_min, tm.tm_sec); + json_object_object_add(header, "time", json_object_new_string(buffer)); + json_object_object_add(header, "field_type", + json_object_new_string(fwts_log_field_to_str_full(field))); + + str = fwts_log_level_to_str(level); + if (!strcmp(str, " ")) + str = "None"; + json_object_object_add(header, "level", + json_object_new_string(str)); + + json_object_object_add(header, "status", json_object_new_string(*status ? status : "None")); + json_object_object_add(header, "failure_label", json_object_new_string(label && *label ? label : "None")); + + /* Redundant really + json_object_object_add(header, "owner", + json_object_new_string(log->owner)); + */ + vsnprintf(buffer, sizeof(buffer), fmt, args); + json_object_object_add(header, "log_text", json_object_new_string(buffer)); + + json_object_array_add(json_log, header); + + log->line_number++; + + return 0; +} + +/* + * fwts_log_underline_json() + * write an underline across log, using character ch as the underline + */ +static void fwts_log_underline_json(fwts_log *log, const int ch) +{ + /* No-op for json */ +} + +/* + * fwts_log_newline() + * write newline to log + */ +static void fwts_log_newline_json(fwts_log *log) +{ + /* No-op for json */ +} + +static void fwts_log_section_begin_json(fwts_log *log, const char *name) +{ + json_object *json_obj; + json_object *json_log; + + json_obj = json_object_new_object(); + json_log = json_object_new_array(); + json_object_object_add(json_obj, name, json_log); + + json_stack[json_stack_index].obj = json_obj; + json_stack[json_stack_index].log = json_log; + + if (json_stack_index > 0) + json_object_array_add(json_stack[json_stack_index-1].log, json_obj); + + if (json_stack_index < MAX_JSON_STACK) + json_stack_index++; + else { + fprintf(stderr, "json log stack overflow pushing section %s.\n", name); + exit(EXIT_FAILURE); + } +} + +static void fwts_log_section_end_json(fwts_log *log) +{ + if (json_stack_index > 0) + json_stack_index--; + else { + fprintf(stderr, "json log stack underflow.\n"); + exit(EXIT_FAILURE); + } +} + +static void fwts_log_open_json(fwts_log *log) +{ + fwts_log_section_begin_json(log, "fwts"); +} + +static void fwts_log_close_json(fwts_log *log) +{ + const char *str; + size_t len; + + fwts_log_section_end_json(log); + + str = json_object_to_json_string(json_stack[0].obj); + len = strlen(str); + + fwrite(str, 1, len, log->fp); + fwrite("\n", 1, 1, log->fp); + fflush(log->fp); + json_object_put(json_stack[0].obj); +} + +fwts_log_ops fwts_log_json_ops = { + .vprintf = fwts_log_vprintf_json, + .underline = fwts_log_underline_json, + .newline = fwts_log_newline_json, + .section_begin = fwts_log_section_begin_json, + .section_end = fwts_log_section_end_json, + .open = fwts_log_open_json, + .close = fwts_log_close_json +}; diff --git a/src/lib/src/fwts_log_plaintext.c b/src/lib/src/fwts_log_plaintext.c new file mode 100644 index 0000000..44c443f --- /dev/null +++ b/src/lib/src/fwts_log_plaintext.c @@ -0,0 +1,195 @@ +/* + * Copyright (C) 2010-2012 Canonical + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#define LOG_LINE_WIDTH 100 + +#include <stdlib.h> +#include <stdio.h> +#include <stdarg.h> +#include <string.h> +#include <unistd.h> +#include <sys/ioctl.h> +#include <time.h> + +#include "fwts.h" + +/* + * fwts_log_header_plaintext() + * format up a tabulated log heading + */ +static int fwts_log_header_plaintext(fwts_log *log, + char *buffer, + const int len, + const fwts_log_field field, + const fwts_log_level level) +{ + const char *ptr; + int n = 0; + struct tm tm; + time_t now; + + time(&now); + localtime_r(&now, &tm); + + for (ptr = fwts_log_format; *ptr; ) { + if (*ptr == '%') { + ptr++; + if (!strncmp(ptr, "line", 4)) { + n += snprintf(buffer + n, len - n, + "%5.5d", log->line_number); + ptr += 4; + } + if (!strncmp(ptr, "date", 4)) { + n += snprintf(buffer + n, len - n, + "%2.2d/%2.2d/%-2.2d", + tm.tm_mday, tm.tm_mon + 1, (tm.tm_year+1900) % 100); + ptr += 4; + } + if (!strncmp(ptr, "time", 4)) { + n += snprintf(buffer + n, len - n, + "%2.2d:%2.2d:%2.2d", + tm.tm_hour, tm.tm_min, tm.tm_sec); + ptr += 4; + } + if (!strncmp(ptr, "field", 5)) { + n += snprintf(buffer + n, len - n, "%s", + fwts_log_field_to_str(field)); + ptr += 5; + } + if (!strncmp(ptr, "level", 5)) { + n += snprintf(buffer + n, len - n, "%1.1s", + fwts_log_level_to_str(level)); + ptr += 5; + } + if (!strncmp(ptr,"owner", 5) && log->owner) { + n += snprintf(buffer + n, len - n, "%-15.15s", log->owner); + ptr += 5; + } + } else { + n += snprintf(buffer+n, len-n, "%c", *ptr); + ptr++; + } + } + return n; +} + + +/* + * fwts_log_vprintf() + * vprintf to a log + */ +static int fwts_log_vprintf_plaintext(fwts_log *log, + const fwts_log_field field, + const fwts_log_level level, + const char *status, /* Ignored */ + const char *label, /* Ignored */ + const char *prefix, + const char *fmt, + va_list args) +{ + char buffer[4096]; + int n = 0; + int header_len; + int len = 0; + + fwts_list *lines; + fwts_list_link *item; + + if (!((field & LOG_FIELD_MASK) & fwts_log_filter)) + return 0; + + /* This is a pain, we neen to find out how big the leading log + message is, so format one up. */ + n = header_len = fwts_log_header_plaintext(log, buffer, sizeof(buffer), field, level); + n += snprintf(buffer + n, sizeof(buffer) - n, "%s", prefix); + n += vsnprintf(buffer + n, sizeof(buffer) - n, fmt, args); + + /* Break text into multi-lines if necessary */ + if (field & LOG_VERBATUM) + lines = fwts_list_from_text(buffer + header_len); + else + lines = fwts_format_text(buffer + header_len, log->line_width - header_len); + + len = n; + + fwts_list_foreach(item, lines) { + char *text = fwts_text_list_text(item); + + if (!(field & LOG_NO_FIELDS)) { + /* Re-format up a log heading with current line number which + may increment with multiple line log messages */ + fwts_log_header_plaintext(log, buffer, sizeof(buffer), field, level); + fwrite(buffer, 1, header_len, log->fp); + } + fwrite(text, 1, strlen(text), log->fp); + fwrite("\n", 1, 1, log->fp); + fflush(log->fp); + log->line_number++; + len += strlen(text) + 1; + } + fwts_text_list_free(lines); + + return len; +} + +/* + * fwts_log_underline_plaintext() + * write an underline across log, using character ch as the underline + */ +static void fwts_log_underline_plaintext(fwts_log *log, const int ch) +{ + int n; + char *buffer; + size_t width = log->line_width + 1; + + if (!((LOG_SEPARATOR & LOG_FIELD_MASK) & fwts_log_filter)) + return; + + buffer = calloc(1, width); + if (!buffer) + return; /* Unlikely, and just abort */ + + /* Write in leading optional line prefix */ + n = fwts_log_header_plaintext(log, buffer, width, LOG_SEPARATOR, LOG_LEVEL_NONE); + + memset(buffer + n, ch, width - n); + buffer[width - 1] = '\n'; + + fwrite(buffer, 1, width, log->fp); + fflush(log->fp); + log->line_number++; + + free(buffer); +} + +/* + * fwts_log_newline_plaintext() + * write newline to log + */ +static void fwts_log_newline_plaintext(fwts_log *log) +{ + fwrite("\n", 1, 1, log->fp); + fflush(log->fp); + log->line_number++; +} + +fwts_log_ops fwts_log_plaintext_ops = { + .vprintf = fwts_log_vprintf_plaintext, + .underline = fwts_log_underline_plaintext, + .newline = fwts_log_newline_plaintext +}; diff --git a/src/lib/src/fwts_summary.c b/src/lib/src/fwts_summary.c index 192043d..38d6a79 100644 --- a/src/lib/src/fwts_summary.c +++ b/src/lib/src/fwts_summary.c @@ -210,34 +210,38 @@ int fwts_summary_report(fwts_framework *fw, fwts_list *test_list) fwts_list_link *item; fwts_log_summary(fw, "Test Failure Summary"); - fwts_log_summary(fw, "===================="); + fwts_log_underline(fw->results, '='); fwts_log_nl(fw); for (i=0;i<SUMMARY_MAX;i++) { + fwts_log_section_begin(fw->results, "failure"); + if (fwts_summaries[i]->len) { fwts_list_link *item; fwts_log_summary(fw, "%s failures: %d", summary_names[i], fwts_summaries[i]->len); + fwts_log_section_begin(fw->results, "failures"); fwts_list_foreach(item, fwts_summaries[i]) { fwts_summary_item *summary_item = fwts_list_data(fwts_summary_item *,item); char *lines = fwts_summary_lines(&summary_item->log_lines); - fwts_log_summary(fw, " %s test, at %d log line%s: %s", + fwts_log_summary(fw, " %s test, at %d log line%s: %s: %s", summary_item->test, fwts_list_len(&summary_item->log_lines), fwts_list_len(&summary_item->log_lines) > 1 ? "s" : "", - lines); - free(lines); - fwts_log_summary_verbatum(fw, " \"%s\"", + lines, summary_item->text); + free(lines); } + fwts_log_section_end(fw->results); } else fwts_log_summary(fw, "%s failures: NONE", summary_names[i]); + fwts_log_section_end(fw->results); fwts_log_nl(fw); } - if (fw->total_run > 0) { + if (fw->log_type == LOG_TYPE_PLAINTEXT && fw->total_run > 0) { sorted = fwts_list_new(); fwts_list_foreach(item, test_list) fwts_list_add_ordered(sorted, fwts_list_data(fwts_framework_test *,item), fwts_framework_compare_test_name); diff --git a/src/lib/src/fwts_tag.c b/src/lib/src/fwts_tag.c index c1ba33d..38d1449 100644 --- a/src/lib/src/fwts_tag.c +++ b/src/lib/src/fwts_tag.c @@ -180,7 +180,7 @@ void fwts_tag_report(fwts_framework *fw, const fwts_log_field field, fwts_list * if ((taglist != NULL) && (fwts_list_len(taglist) > 0)) { char *tags = fwts_tag_list_to_str(taglist); if (tags) { - fwts_log_printf(fw->results, field | LOG_VERBATUM, LOG_LEVEL_NONE, "Tags: %s", tags); + fwts_log_printf(fw->results, field | LOG_VERBATUM, LOG_LEVEL_NONE, "", "", "", "Tags: %s", tags); free(tags); } }