From ac07ad67d964572ffb10f188a4602a447f654f93 Mon Sep 17 00:00:00 2001 From: Markus Kauppila <markus.kauppila@gmail.com> Date: Tue, 21 Jun 2011 19:31:46 +0300 Subject: [PATCH] Creating flexible logging system which supports XML and plain text. Work under progress. --- test/test-automation/Makefile.am | 5 + test/test-automation/logger.h | 26 ++- test/test-automation/xml.c | 254 +++++++++++++++++++++ test/test-automation/xml.h | 80 +++++++ test/test-automation/xml_logger.c | 364 ++++-------------------------- 5 files changed, 411 insertions(+), 318 deletions(-) create mode 100644 test/test-automation/xml.c create mode 100644 test/test-automation/xml.h diff --git a/test/test-automation/Makefile.am b/test/test-automation/Makefile.am index 27b4941f..ffd0928f 100644 --- a/test/test-automation/Makefile.am +++ b/test/test-automation/Makefile.am @@ -7,6 +7,11 @@ runner_SOURCES = runner.c SDL_test.c runner_CLAGS = -W -Wall -Wextra -g `sdl-config --cflags` -DSDL_NO_COMPAT runner_LDFLAGS = `sdl-config --libs` +bin_PROGRAMS = logger +logger_SOURCES = xml_logger.c xml.c +logger_CLAGS = -W -Wall -Wextra -g `sdl-config --cflags` -DSDL_NO_COMPAT +logger_LDFLAGS = `sdl-config --libs` + install: install-tests install-tests: -mkdir tests diff --git a/test/test-automation/logger.h b/test/test-automation/logger.h index 160be93e..dae39379 100644 --- a/test/test-automation/logger.h +++ b/test/test-automation/logger.h @@ -21,6 +21,30 @@ #ifndef _LOGGER_H #define _LOGGER_H -// Put function pointers here +#include <time.h> + +// Function pointer to function which handles to output +typedef int (*LogOutputFp)(const char *); + +/*! + * Generic logger interface + * + */ +void RunStarted(LogOutputFp outputFn, const char *runnerParameters, time_t eventTime); +void RunEnded(time_t endTime, time_t totalRuntime); + +void SuiteStarted(const char *suiteName, time_t eventTime); +void SuiteEnded(int testsPassed, int testsFailed, int testsSkipped, + double endTime, time_t totalRuntime); + +void TestStarted(const char *testName, const char *testDescription, time_t startTime); +void TestEnded(const char *testName, const char *testDescription, int testResult, + int numAsserts, time_t endTime, time_t totalRuntime); + +void Assert(const char *assertName, int assertResult, const char *assertMessage, + time_t eventTime); + +void Log(const char *logMessage, time_t eventTime); + #endif diff --git a/test/test-automation/xml.c b/test/test-automation/xml.c new file mode 100644 index 00000000..bc76285c --- /dev/null +++ b/test/test-automation/xml.c @@ -0,0 +1,254 @@ +/* + Copyright (C) 2011 Markus Kauppila <markus.kauppila@gmail.com> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + + +#include <stdio.h> +//#include <stdlib.h> +#include <string.h> +//#include <stdarg.h> +#include <assert.h> + +#include <SDL/SDL.h> + +#include "xml.h" + +/*! Points the function which handles the output */ +static LogOutputFp logger = 0; + +/*! + * Defines structure used for "counting" open XML-tags + */ +typedef struct TagList { + const char *tag; + struct TagList *next; +} TagList; + +static TagList *openTags = NULL; + +/*! + * Prepend the open tags list + * + * \return On error returns non-zero value, otherwise zero will returned + */ +static int +AddOpenTag(const char *tag) +{ + TagList *openTag = SDL_malloc(sizeof(TagList)); + if(openTag == NULL) { + return 1; + } + memset(openTag, 0, sizeof(TagList)); + + openTag->tag = tag; // Should be fine without malloc? + openTag->next = openTags; + + openTags = openTag; + + return 0; +} + +/*! + * Removes the first tag from the open tag list + * + * \return On error returns non-zero value, otherwise zero will returned + */ +static int +RemoveOpenTag(const char *tag) +{ + if(openTags == NULL) { + return 1; + } + + int retVal = 0; + + // Tag should always be the same as previously opened tag + // It prevents opening and ending tag mismatch + if(SDL_strcmp(openTags->tag, tag) == 0) { + TagList *openTag = openTags; + openTags = openTags->next; + + free(openTag); + } else { + printf("Debug | RemoveOpenTag(): open/end tag mismatch"); + retVal = 1; + } + + return retVal; +} + +/*! + * Debug function. Prints the contents of the open tags list. + */ +static void +PrintOpenTags() +{ + printf("\nOpen tags:\n"); + + TagList *openTag = NULL; + for(openTag = openTags; openTag; openTag = openTag->next) { + printf("\ttag: %s\n", openTag->tag); + } +} + +/* +=================== + + XML + +=================== +*/ + +static int has_open_element = 0; + +void +XMLOpenDocument(const char *rootTag, LogOutputFp log) +{ + assert(log != NULL); + logger = log; + + logger("<?xml version=\"1.0\" encoding=\"utf-8\" ?>"); + + size_t size = SDL_strlen(rootTag) + 3 + 1; /* one extra for '\0', '<' and '>' */ + char *buffer = SDL_malloc(size); + snprintf(buffer, size, "%s%s%s", "<", rootTag, ">"); + logger(buffer); + SDL_free(buffer); + + // add open tag + AddOpenTag(rootTag); +} + +void +XMLCloseDocument() { + // Close the open tags with proper nesting + TagList *openTag = openTags; + while(openTag) { + TagList *temp = openTag->next; + + size_t size = SDL_strlen(openTag->tag) + 4 + 1; /* one extra for '\0', '<', '/' and '>' */ + char *buffer = SDL_malloc(size); + snprintf(buffer, size, "%s%s%s", "</", openTag->tag, ">"); + logger(buffer); + SDL_free(buffer); + + RemoveOpenTag(openTag->tag); + + openTag = temp; + } +} + +static const char *currentTag = NULL; + +void +XMLOpenElement(const char *tag) +{ + size_t size = SDL_strlen(tag) + 2 + 1; /* one extra for '\0', '<' */ + char *buffer = SDL_malloc(size); + snprintf(buffer, size, "%s%s%s", "<", tag, ">"); + logger(buffer); + SDL_free(buffer); + + currentTag = tag; + + has_open_element = 1; + + AddOpenTag(tag); +} + + +void +XMLOpenElementWithAttribute(const char *tag, Attribute attribute) +{ + size_t size = SDL_strlen(tag) + 2 + 1; /* one extra for '\0', '<' */ + char *buffer = SDL_malloc(size); + + snprintf(buffer, size, "%s%s", "<", tag); + logger(buffer); + SDL_free(buffer); + + currentTag = tag; + + has_open_element = 1; + + AddOpenTag(tag); +} + +//! \todo make this static and remove from interface? +void +XMLAddAttribute(const char *attribute, const char *value) +{ + // Requires open element + if(has_open_element == 0) { + return ; + } + size_t attributeSize = SDL_strlen(attribute); + size_t valueSize = SDL_strlen(value); + + size_t size = 1 + attributeSize + 3 + valueSize + 1; + char *buffer = SDL_malloc(size); // 1 for '=' + snprintf(buffer, size, " %s%s\"%s\"", attribute, "=", value); + logger(buffer); + SDL_free(buffer); +} + +void +XMLAddContent(const char *content) +{ + size_t size = SDL_strlen(content) + 1 + 1; + char *buffer = SDL_malloc(size); + snprintf(buffer, size, "%s", content); + logger(buffer); + SDL_free(buffer);} + +void +XMLCloseElement(const char *tag) +{ + // Close the open tags with proper nesting. Closes tags until it finds + // the given tag which is the last tag that will be closed + TagList *openTag = openTags; + while(openTag) { + TagList *temp = openTag->next; + + size_t size = SDL_strlen(openTag->tag) + 4 + 1; /* one extra for '\0', '<', '/' and '>' */ + char *buffer = SDL_malloc(size); + snprintf(buffer, size, "%s%s%s", "</", openTag->tag, ">"); + logger(buffer); + SDL_free(buffer); + + const int openTagSize = SDL_strlen(openTag->tag); + const int tagSize = SDL_strlen(tag); + const int compSize = (openTagSize > tagSize) ? openTagSize : tagSize; + + int breakOut = 0; + if(SDL_strncmp(openTag->tag, tag, compSize) == 0) { + breakOut = 1; + } + + RemoveOpenTag(openTag->tag); + + openTag = temp; + + if(breakOut) { + break; + } + } + + has_open_element = 0; +} diff --git a/test/test-automation/xml.h b/test/test-automation/xml.h new file mode 100644 index 00000000..b222f9de --- /dev/null +++ b/test/test-automation/xml.h @@ -0,0 +1,80 @@ +/* + Copyright (C) 2011 Markus Kauppila <markus.kauppila@gmail.com> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef _XML_H +#define _XML_H + +#include "logger.h" + +typedef struct Attribute { + const char *attribute; + const char *value; +} Attribute; + + +/*! + * Opens XML document. + * Creates header and start tag for root element. + * + * \param rootTag Root tag for the XML document + */ +void XMLOpenDocument(const char *rootTag, LogOutputFp log); + +/*! + * Closes the XML-document. + * Creates end tag for root element and closes other open elements + * with correct nesting. + */ +void XMLCloseDocument(); + +/*! + * Opens XML-element. + * + * \param tag Element to be opened + */ +void XMLOpenElement(const char *tag); + +/*! + * Add attribute to currently open element. + * + * \param attribute Name of the attribute + * \param value Value of the given attribute + */ +void XMLAddAttribute(const char *attribute, const char *value); + +/*! + * Add content to currently open element. + * + * \param content Content for the currently open element + */ +void XMLAddContent(const char *content); + +/*! + * Closes previously opened element. + * Enforces proper nesting by not allowing end elements haphazardly. + * + * Closes all the opened elements until the given element/tag is found + * + * \param tag Element to close + */ +void XMLCloseElement(const char *tag); + +#endif + diff --git a/test/test-automation/xml_logger.c b/test/test-automation/xml_logger.c index e1f04026..8411b066 100644 --- a/test/test-automation/xml_logger.c +++ b/test/test-automation/xml_logger.c @@ -23,6 +23,8 @@ #include "logger.h" +#include "xml.h" + #include <SDL/SDL.h> #include <stdio.h> @@ -30,315 +32,83 @@ #include <string.h> #include <stdarg.h> -/* \todo - * - Make XML (and relevant comparisons) case-insensitive - */ - -static int xml_enabled = 1; - -static int loggingPriority = 0; -static int nestingDepth = 0; - -/*! Definitions of log priorities */ -typedef enum Priority { - VERBOSE, - DEFAULT, -} Priority; - -/*! Function pointer definitions. \todo Move to logger.h */ -typedef int (*LogOutputFp)(char *); - -typedef int (*LogInitFp)(LogOutputFp, Priority); -typedef int (*LogCleanUptFp)(void); - -typedef int (*StartTagFp)(Priority, const char *); -typedef int (*EndTagFp)(Priority, const char *); -typedef int (*TagFp)(Priority, const char *, const char *, ...); - - -/*! Function pointer to output function */ -static LogOutputFp OutputFp = NULL; - - -/*! Definitions for tag styles used in Tagify() */ -#define TAG_START 0x00000001 -#define TAG_END 0x00000002 -#define TAG_BOTH (TAG_START & TAG_END) - -/*! Function prototypes \todo move to xml_logger.h */ -int XMLStartTag(Priority priority, const char *tag); -int XMLEndTag(Priority priority, const char *tag); - -int LogGenericOutput(char *message); - - /*! - * Defines structure used for "counting" open XML-tags - */ -typedef struct TagList { - const char *tag; - struct TagList *next; -} TagList; - -static TagList *openTags = NULL; - -/*! - * Prepend the open tags list + * Prints the given message to stderr. Function adds nesting + * to the output. * - * \return On error returns non-zero value, otherwise zero will returned + * \return Possible error value (\todo) */ -static int -AddOpenTag(const char *tag) +int +LogGenericOutput(const char *message) { - TagList *openTag = SDL_malloc(sizeof(TagList)); - if(openTag == NULL) { - return 1; + /* + int depth = indentDepth; + while(depth--) { + fprintf(stderr, " "); } - memset(openTag, 0, sizeof(TagList)); - - openTag->tag = tag; // Should be fine without malloc? - openTag->next = openTags; - - openTags = openTag; + */ - return 0; + fprintf(stderr, "%s\n", message); + fflush(stderr); } -/*! - * Removes the first tag from the open tag list - * - * \return On error returns non-zero value, otherwise zero will returned - */ -static int -RemoveOpenTag(const char *tag) +void +RunStarted(LogOutputFp outputFn, const char *runnerParameters, time_t eventTime) { - if(openTags == NULL) { - return 1; - } - - int retVal = 0; + XMLOpenDocument("testlog", outputFn); - // Tag should always be the same as previously opened tag - // It prevents opening and ending tag mismatch - if(SDL_strcmp(openTags->tag, tag) == 0) { - TagList *openTag = openTags; - openTags = openTags->next; - - free(openTag); - } else { - printf("Debug | RemoveOpenTag(): open/end tag mismatch"); - retVal = 1; - } - - return retVal; + XMLOpenElement("parameters"); + XMLAddContent(runnerParameters); + XMLCloseElement("parameters"); } -/*! - * Debug function. Prints the contents of the open tags list. - */ -static void -PrintOpenTags() +void +RunEnded(time_t endTime, time_t totalRuntime) { - printf("\nOpen tags:\n"); - - TagList *openTag = NULL; - for(openTag = openTags; openTag; openTag = openTag->next) { - printf("\ttag: %s\n", openTag->tag); - } + XMLCloseDocument(); } -/*! - * Initializes the XML-logger for creating test reports in XML. - * - * \return Error code. \todo - */ -int -XMLInit(LogOutputFp logOutputFp, Priority priority) +void +SuiteStarted(const char *suiteName, time_t eventTime) { - OutputFp = logOutputFp; - loggingPriority = priority; - - //! make "doctype" work with priority level? - OutputFp("<?xml version=\"1.0\" encoding=\"utf-8\" ?>"); - XMLStartTag(DEFAULT, "testlog"); -} + XMLOpenElement("suite"); -/*! - * Cleans up the logger and closes all open XML-tags - * - * \return Error code. \todo - */ -int -XMLCleanUp() -{ - // Close the open tags - TagList *openTag = openTags; - while(openTag) { - TagList *temp = openTag->next; - XMLEndTag(DEFAULT, openTag->tag); - openTag = temp; - } + XMLOpenElement("eventTime"); + //XMLAddContent(evenTime); + XMLCloseElement("eventTime"); } -/*! - * Forms a valid XML-tag based on the given parameters - * - * \param tag XML-tag to create - * \param tagStyle Do start or end tag, or both. - * \param message text content of the tags - * - * \return Well-formed XML tag - */ -static char * -Tagify(const char *tag, const int tagStyle, const char *message) +void +SuiteEnded(int testsPassed, int testsFailed, int testsSkipped, + double endTime, time_t totalRuntime) { - // buffer simplifies the creation of the string - const int bufferSize = 1024; - char buffer[bufferSize]; - memset(buffer, 0, bufferSize); - - if(tagStyle & TAG_START) { - strcat(buffer, "<"); - strcat(buffer, tag); - strcat(buffer, ">"); - } - - if(message) { - strcat(buffer, message); - } - - if(tagStyle & TAG_END) { - strcat(buffer, "</"); - strcat(buffer, tag); - strcat(buffer, ">"); - } - - - const int size = SDL_strlen(buffer) + 1; - char *newTag = SDL_malloc(size * sizeof(char)); - memset(newTag, 0, size * sizeof(char)); - memcpy(newTag, buffer, size); - - return newTag; + XMLCloseElement("suite"); } -/*! - * Creates and outputs an start tag - * - * \param priority Priority of the tag - * \param tag Tag for outputting - * - * \return Error code. Non-zero on failure. Zero on success - */ -int -XMLStartTag(Priority priority, const char *tag) +void +TestStarted(const char *testName, const char *testDescription, time_t startTime) { - if(priority < loggingPriority) { - return 1; - } - - AddOpenTag(tag); - char *newTag = Tagify(tag, TAG_START, NULL); - OutputFp(newTag); - SDL_free(newTag); - nestingDepth++; } -/*! - * Creates and outputs an end tag - * - * \param priority Priority of the tag - * \param tag Tag for outputting - * - * \return Error code. Non-zero on failure. Zero on success - */ -int -XMLEndTag(Priority priority, const char *tag) +void +TestEnded(const char *testName, const char *testDescription, int testResult, + int numAsserts, time_t endTime, time_t totalRuntime) { - /* - Do it before priority check, so incorrect usage of - priorities won't mess it up (?) - */ - nestingDepth--; - - if(priority < loggingPriority) { - return 1; - } - - RemoveOpenTag(tag); - char *newTag = Tagify(tag, TAG_END, NULL); - OutputFp(newTag); - SDL_free(newTag); } -/*! - * Creates an XML-tag including start and end tags and text content - * between them. - * - * \param priority Priority of the tag - * \param tag Tag for outputting - * \param fmt Text content of tag as variadic parameter list - * - * \return Error code. Non-zero on failure. Zero on success - */ -int -XMLTag(Priority priority, const char *tag, const char *fmt, ...) +void +Assert(const char *assertName, int assertResult, const char *assertMessage, + time_t eventTime) { - if(priority < loggingPriority) { - return 1; - } - - const int bufferSize = 1024; - char buffer[bufferSize]; - memset(buffer, 0, bufferSize); - - va_list list; - va_start(list, fmt); - vsnprintf(buffer, bufferSize, fmt, list); - va_end(list); - char *newTag = Tagify(tag, TAG_BOTH, buffer); - //LogGenericOutput(newTag); - OutputFp(newTag); - SDL_free(newTag); } -/*! - * Prints the given message to stderr. Function adds nesting - * to the output. - * - * \return Possible error value (\todo) - */ -int -LogGenericOutput(char *message) +void +Log(const char *logMessage, time_t eventTime) { - int depth = nestingDepth; - while(depth--) { - fprintf(stderr, " "); - } - - fprintf(stderr, "%s\n", message); -} - - -/*! Quick Dummy functions for testing non-xml output. \todo put to proper place*/ -int DummyInit(LogOutputFp output, Priority priority) { - return 0; -} -int DummyCleanUp() { - return 0; -} -int DummyStartTag(Priority priority, const char *tag) { - return 0; -} -int DummyEndTag(Priority priority, const char *tag) { - return 0; -} -int DummyTag(Priority priority, const char *tag, const char *fmt, ...) { - return 0; } @@ -348,50 +118,10 @@ int DummyTag(Priority priority, const char *tag, const char *fmt, ...) { int main(int argc, char *argv[]) { - LogInitFp LogInit = NULL; - LogCleanUptFp LogCleanUp = NULL; - StartTagFp StartTag = NULL; - EndTagFp EndTag = NULL; - TagFp Tag = NULL; - - if(xml_enabled) { - // set logger functions to XML - LogInit = XMLInit; - LogCleanUp = XMLCleanUp; - - StartTag = XMLStartTag; - EndTag = XMLEndTag; - Tag = XMLTag; - } else { - // When no XML-output is desired, dummy functions are used - LogInit = DummyInit; - LogCleanUp = DummyCleanUp; - - StartTag = DummyStartTag; - EndTag = DummyEndTag; - Tag = DummyTag; - } - - LogInit(LogGenericOutput, VERBOSE); - - StartTag(DEFAULT, "hello"); - StartTag(DEFAULT, "world"); - EndTag(DEFAULT, "world"); - //EndTag(DEFAULT, "hello"); - - LogCleanUp(); - -#if 0 - XMLStartTag("log"); - XMLStartTag("suite"); - XMLStartTag("test"); - - XMLEndTag("test"); - XMLEndTag("suite"); - - - PrintOpenTags(); -#endif + RunStarted(LogGenericOutput, "All the data from harness", 0); + SuiteStarted("Suite data here", 0); + SuiteEnded(0, 0, 0, 0.0f, 0); + RunEnded(0, 0); return 0; } -- 2.18.1