Creating flexible logging system which supports XML and plain
text. Work under progress.
--- a/test/test-automation/Makefile.am Sun Jun 19 19:52:35 2011 +0300
+++ b/test/test-automation/Makefile.am Tue Jun 21 19:31:46 2011 +0300
@@ -7,6 +7,11 @@
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
--- a/test/test-automation/logger.h Sun Jun 19 19:52:35 2011 +0300
+++ b/test/test-automation/logger.h Tue Jun 21 19:31:46 2011 +0300
@@ -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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/test-automation/xml.c Tue Jun 21 19:31:46 2011 +0300
@@ -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;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/test-automation/xml.h Tue Jun 21 19:31:46 2011 +0300
@@ -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
+
--- a/test/test-automation/xml_logger.c Sun Jun 19 19:52:35 2011 +0300
+++ b/test/test-automation/xml_logger.c Tue Jun 21 19:31:46 2011 +0300
@@ -23,6 +23,8 @@
#include "logger.h"
+#include "xml.h"
+
#include <SDL/SDL.h>
#include <stdio.h>
@@ -30,281 +32,6 @@
#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
- *
- * \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("<?xml version=\"1.0\" encoding=\"utf-8\" ?>");
- 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, "</");
- 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;
-}
-
-/*!
- * 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 @@
* \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");
+}
+
+void
+RunEnded(time_t endTime, time_t totalRuntime)
+{
+ XMLCloseDocument();
+}
+
+void
+SuiteStarted(const char *suiteName, time_t eventTime)
+{
+ XMLOpenElement("suite");
+
+ XMLOpenElement("eventTime");
+ //XMLAddContent(evenTime);
+ XMLCloseElement("eventTime");
}
-
+void
+SuiteEnded(int testsPassed, int testsFailed, int testsSkipped,
+ double endTime, time_t totalRuntime)
+{
+ XMLCloseElement("suite");
+}
-/*! 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;
+void
+TestStarted(const char *testName, const char *testDescription, time_t startTime)
+{
+
}
-int DummyStartTag(Priority priority, const char *tag) {
- return 0;
+
+void
+TestEnded(const char *testName, const char *testDescription, int testResult,
+ int numAsserts, time_t endTime, time_t totalRuntime)
+{
+
}
-int DummyEndTag(Priority priority, const char *tag) {
- return 0;
+
+void
+Assert(const char *assertName, int assertResult, const char *assertMessage,
+ time_t eventTime)
+{
+
}
-int DummyTag(Priority priority, const char *tag, const char *fmt, ...) {
- return 0;
+
+void
+Log(const char *logMessage, time_t eventTime)
+{
+
}
@@ -348,50 +118,10 @@
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;
}