From ac07ad67d964572ffb10f188a4602a447f654f93 Mon Sep 17 00:00:00 2001 From: Markus Kauppila 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 | 394 +++++------------------------- 5 files changed, 426 insertions(+), 333 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 27b4941ff..ffd0928fa 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 160be93e1..dae393798 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 + +// 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 000000000..bc76285c9 --- /dev/null +++ b/test/test-automation/xml.c @@ -0,0 +1,254 @@ +/* + Copyright (C) 2011 Markus Kauppila + + 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 +//#include +#include +//#include +#include + +#include + +#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(""); + + 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", "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", "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 000000000..b222f9dec --- /dev/null +++ b/test/test-automation/xml.h @@ -0,0 +1,80 @@ +/* + Copyright (C) 2011 Markus Kauppila + + 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 e1f04026f..8411b0666 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 #include @@ -30,281 +32,6 @@ #include #include -/* \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 - * - * \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); - } -} - -/*! - * Initializes the XML-logger for creating test reports in XML. - * - * \return Error code. \todo - */ -int -XMLInit(LogOutputFp logOutputFp, Priority priority) -{ - OutputFp = logOutputFp; - loggingPriority = priority; - - //! make "doctype" work with priority level? - OutputFp(""); - XMLStartTag(DEFAULT, "testlog"); -} - -/*! - * 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; - } -} - -/*! - * 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) -{ - // 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, ""); - } - - - 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; -} - -/*! - * 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) -{ - 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) -{ - /* - 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, ...) -{ - 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. @@ -312,33 +39,76 @@ XMLTag(Priority priority, const char *tag, const char *fmt, ...) * \return Possible error value (\todo) */ int -LogGenericOutput(char *message) +LogGenericOutput(const char *message) { - int depth = nestingDepth; + /* + int depth = indentDepth; while(depth--) { fprintf(stderr, " "); } + */ fprintf(stderr, "%s\n", message); + fflush(stderr); } +void +RunStarted(LogOutputFp outputFn, const char *runnerParameters, time_t eventTime) +{ + XMLOpenDocument("testlog", outputFn); + XMLOpenElement("parameters"); + XMLAddContent(runnerParameters); + XMLCloseElement("parameters"); +} -/*! Quick Dummy functions for testing non-xml output. \todo put to proper place*/ -int DummyInit(LogOutputFp output, Priority priority) { - return 0; +void +RunEnded(time_t endTime, time_t totalRuntime) +{ + XMLCloseDocument(); } -int DummyCleanUp() { - return 0; + +void +SuiteStarted(const char *suiteName, time_t eventTime) +{ + XMLOpenElement("suite"); + + XMLOpenElement("eventTime"); + //XMLAddContent(evenTime); + XMLCloseElement("eventTime"); } -int DummyStartTag(Priority priority, const char *tag) { - return 0; + +void +SuiteEnded(int testsPassed, int testsFailed, int testsSkipped, + double endTime, time_t totalRuntime) +{ + XMLCloseElement("suite"); } -int DummyEndTag(Priority priority, const char *tag) { - return 0; + +void +TestStarted(const char *testName, const char *testDescription, time_t startTime) +{ + } -int DummyTag(Priority priority, const char *tag, const char *fmt, ...) { - return 0; + +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) +{ + } @@ -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; }