[PATCH weston v3 1/5] Expanded unit test framework to cover base requirements.

Jon A. Cruz jonc at osg.samsung.com
Tue May 26 16:06:37 PDT 2015


Added simple unit/integration test framework and corresponding
test program.

Added a simple C-based test framework and an example program
that uses it to run through some simple wayland client checks.

Expanded code support in testing framework includes:

* Running tests in forked child processes.
* Filtering of tests by both matches and exclusions.
* Per-suite and per-test setup and tear-down fixtures.
* Unconditional test stopping including skipping.
* Tracing of additional messages for failure logging.
* Works correctly with both debug and release builds (no assert, etc.)
* Randomization of tests.
* Repetition of tests.
* Parsing command-line parameters in test main()'s
* Return of proper exit codes including 77 for skip.
* Break on failure.

    Signed-off-by: Jon A. Cruz <jonc at osg.samsung.com>

Signed-off-by: Jon A. Cruz <jonc at osg.samsung.com>
---
 .gitignore                                 |    3 +
 Makefile.am                                |   91 +-
 tools/Doxyfile                             |  318 ++++++
 tools/devdocs.doxyfile                     |  315 ++++++
 tools/devtools.dox                         |   52 +
 tools/tools.dox                            |   29 +
 tools/tools_arch_new.gv                    |   60 ++
 tools/tools_arch_old.gv                    |   28 +
 tools/waycheck/moretest.c                  |   83 ++
 tools/waycheck/rough_draw.c                |  265 +++++
 tools/waycheck/rough_draw.h                |   60 ++
 tools/waycheck/waycheck.c                  |  416 ++++++++
 tools/waycheck/waycheck.dox                |   29 +
 tools/wayland_fixtures/inc/wtst_fixtures.h |  230 +++++
 tools/wayland_fixtures/src/wtst_fixtures.c |  852 ++++++++++++++++
 tools/zunitc/doc/zunitc.dox                |  138 +++
 tools/zunitc/inc/zunitc/zunitc.h           |  537 ++++++++++
 tools/zunitc/inc/zunitc/zunitc_impl.h      |   87 ++
 tools/zunitc/src/main.c                    |   44 +
 tools/zunitc/src/zuc_base_logger.c         |  382 +++++++
 tools/zunitc/src/zuc_base_logger.h         |   34 +
 tools/zunitc/src/zuc_collector.c           |  321 ++++++
 tools/zunitc/src/zuc_collector.h           |   53 +
 tools/zunitc/src/zuc_context.h             |   55 +
 tools/zunitc/src/zuc_event.h               |   81 ++
 tools/zunitc/src/zuc_event_listener.h      |  167 +++
 tools/zunitc/src/zuc_types.h               |   76 ++
 tools/zunitc/src/zunitc_impl.c             | 1520 ++++++++++++++++++++++++++++
 tools/zunitc/test/fixtures_test.c          |  102 ++
 tools/zunitc/test/zunitc_test.c            |  314 ++++++
 30 files changed, 6741 insertions(+), 1 deletion(-)
 create mode 100644 tools/Doxyfile
 create mode 100644 tools/devdocs.doxyfile
 create mode 100644 tools/devtools.dox
 create mode 100644 tools/tools.dox
 create mode 100644 tools/tools_arch_new.gv
 create mode 100644 tools/tools_arch_old.gv
 create mode 100644 tools/waycheck/moretest.c
 create mode 100644 tools/waycheck/rough_draw.c
 create mode 100644 tools/waycheck/rough_draw.h
 create mode 100644 tools/waycheck/waycheck.c
 create mode 100644 tools/waycheck/waycheck.dox
 create mode 100644 tools/wayland_fixtures/inc/wtst_fixtures.h
 create mode 100644 tools/wayland_fixtures/src/wtst_fixtures.c
 create mode 100644 tools/zunitc/doc/zunitc.dox
 create mode 100644 tools/zunitc/inc/zunitc/zunitc.h
 create mode 100644 tools/zunitc/inc/zunitc/zunitc_impl.h
 create mode 100644 tools/zunitc/src/main.c
 create mode 100644 tools/zunitc/src/zuc_base_logger.c
 create mode 100644 tools/zunitc/src/zuc_base_logger.h
 create mode 100644 tools/zunitc/src/zuc_collector.c
 create mode 100644 tools/zunitc/src/zuc_collector.h
 create mode 100644 tools/zunitc/src/zuc_context.h
 create mode 100644 tools/zunitc/src/zuc_event.h
 create mode 100644 tools/zunitc/src/zuc_event_listener.h
 create mode 100644 tools/zunitc/src/zuc_types.h
 create mode 100644 tools/zunitc/src/zunitc_impl.c
 create mode 100644 tools/zunitc/test/fixtures_test.c
 create mode 100644 tools/zunitc/test/zunitc_test.c

diff --git a/.gitignore b/.gitignore
index 047c386..e0cb805 100644
--- a/.gitignore
+++ b/.gitignore
@@ -98,3 +98,6 @@ weston-drm.7
 weston.ini.5
 
 /tests/weston-ivi.ini
+
+/waycheck
+/zuctest
diff --git a/Makefile.am b/Makefile.am
index e860e0e..d92498a 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -899,7 +899,8 @@ endif
 # Shared utilities
 #
 
-noinst_LTLIBRARIES += libshared.la libshared-cairo.la
+noinst_LTLIBRARIES += libshared.la libshared-cairo.la \
+	libzunitc.la libzunitcmain.la libwayland-fixtures.la
 
 libshared_la_CFLAGS = $(GCC_CFLAGS) $(COMPOSITOR_CFLAGS)
 
@@ -936,6 +937,48 @@ libshared_cairo_la_SOURCES =			\
 	shared/frame.c				\
 	shared/cairo-util.h
 
+libwayland_fixtures_la_SOURCES = \
+	tools/wayland_fixtures/inc/wtst_fixtures.h	\
+	tools/wayland_fixtures/src/wtst_fixtures.c
+
+libwayland_fixtures_la_CFLAGS = \
+	-I${prefix}/include			\
+	-I$(top_srcdir)/tools/wayland_fixtures/inc	\
+	-I$(top_srcdir)/tools/zunitc/inc	\
+	$(GCC_CFLAGS)
+
+libwayland_fixtures_la_LIBADD = \
+	libshared.la
+
+libzunitc_la_SOURCES = \
+	tools/zunitc/inc/zunitc/zunitc.h	\
+	tools/zunitc/inc/zunitc/zunitc_impl.h	\
+	tools/zunitc/src/zuc_base_logger.c	\
+	tools/zunitc/src/zuc_base_logger.h	\
+	tools/zunitc/src/zuc_collector.c	\
+	tools/zunitc/src/zuc_collector.h	\
+	tools/zunitc/src/zuc_context.h		\
+	tools/zunitc/src/zuc_event.h		\
+	tools/zunitc/src/zuc_event_listener.h	\
+	tools/zunitc/src/zuc_types.h		\
+	tools/zunitc/src/zunitc_impl.c
+
+libzunitc_la_CFLAGS = \
+	-I$(top_srcdir)/tools/zunitc/inc	\
+	$(GCC_CFLAGS)
+
+libzunitc_la_LIBADD = \
+	libshared.la
+
+libzunitcmain_la_SOURCES = \
+	tools/zunitc/src/main.c
+
+libzunitcmain_la_CFLAGS = \
+	-I$(top_srcdir)/tools/zunitc/inc	\
+	$(GCC_CFLAGS)
+
+libzunitcmain_la_LIBADD = \
+	libshared.la
 
 #
 # tests subdirectory
@@ -1174,6 +1217,52 @@ setbacklight_CFLAGS = $(AM_CFLAGS) $(SETBACKLIGHT_CFLAGS)
 setbacklight_LDADD = $(SETBACKLIGHT_LIBS)
 endif
 
+# --------------------------------------------------------------------------
+
+all-local: waycheck$(EXEEXT) zuctest$(EXEEXT)
+
+noinst_PROGRAMS += zuctest$(EXEEXT)
+
+zuctest_LDADD =					\
+	libshared.la				\
+	libzunitc.la				\
+	libzunitcmain.la
+
+zuctest_CFLAGS =			\
+	-I$(top_srcdir)/tools/zunitc/inc
+
+zuctest_SOURCES =				\
+	tools/zunitc/test/fixtures_test.c	\
+	tools/zunitc/test/zunitc_test.c
+
+#
+
+noinst_PROGRAMS += waycheck$(EXEEXT)
+
+waycheck_LDADD =			\
+	$(WAYLAND_COMPOSITOR_LIBS)	\
+	$(COMPOSITOR_LIBS)		\
+	libshared.la			\
+	libwayland-fixtures.la		\
+	libzunitc.la			\
+	libzunitcmain.la
+
+waycheck_CFLAGS =				\
+	-I${prefix}/include			\
+	-I$(top_srcdir)/tools/zunitc/inc	\
+	-I$(top_srcdir)/tools/wayland_fixtures/inc	\
+	-I$(top_builddir)/clients		\
+	-I$(top_srcdir)/shared			\
+	$(AM_CFLAGS)
+
+waycheck_SOURCES =				\
+	tools/waycheck/waycheck.c		\
+	tools/waycheck/moretest.c		\
+	tools/waycheck/rough_draw.c		\
+	tools/waycheck/rough_draw.h
+
+# --------------------------------------------------------------------------
+
 EXTRA_DIST += tests/weston-tests-env
 
 BUILT_SOURCES +=				\
diff --git a/tools/Doxyfile b/tools/Doxyfile
new file mode 100644
index 0000000..e42cdbb
--- /dev/null
+++ b/tools/Doxyfile
@@ -0,0 +1,318 @@
+# Doxyfile 1.8.8
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+DOXYFILE_ENCODING      = UTF-8
+PROJECT_NAME           = "Tools"
+PROJECT_NUMBER         =
+PROJECT_BRIEF          =
+PROJECT_LOGO           =
+OUTPUT_DIRECTORY       = docs
+CREATE_SUBDIRS         = NO
+ALLOW_UNICODE_NAMES    = NO
+OUTPUT_LANGUAGE        = English
+BRIEF_MEMBER_DESC      = YES
+REPEAT_BRIEF           = YES
+ABBREVIATE_BRIEF       =
+ALWAYS_DETAILED_SEC    = NO
+INLINE_INHERITED_MEMB  = NO
+FULL_PATH_NAMES        = YES
+STRIP_FROM_PATH        =
+STRIP_FROM_INC_PATH    =
+SHORT_NAMES            = NO
+JAVADOC_AUTOBRIEF      = YES
+QT_AUTOBRIEF           = NO
+MULTILINE_CPP_IS_BRIEF = NO
+INHERIT_DOCS           = YES
+SEPARATE_MEMBER_PAGES  = NO
+TAB_SIZE               = 4
+ALIASES                =
+TCL_SUBST              =
+OPTIMIZE_OUTPUT_FOR_C  = YES
+OPTIMIZE_OUTPUT_JAVA   = NO
+OPTIMIZE_FOR_FORTRAN   = NO
+OPTIMIZE_OUTPUT_VHDL   = NO
+EXTENSION_MAPPING      =
+MARKDOWN_SUPPORT       = YES
+AUTOLINK_SUPPORT       = YES
+BUILTIN_STL_SUPPORT    = NO
+CPP_CLI_SUPPORT        = NO
+SIP_SUPPORT            = NO
+IDL_PROPERTY_SUPPORT   = YES
+DISTRIBUTE_GROUP_DOC   = NO
+SUBGROUPING            = YES
+INLINE_GROUPED_CLASSES = NO
+INLINE_SIMPLE_STRUCTS  = NO
+TYPEDEF_HIDES_STRUCT   = NO
+LOOKUP_CACHE_SIZE      = 0
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+EXTRACT_ALL            = NO
+EXTRACT_PRIVATE        = NO
+EXTRACT_PACKAGE        = NO
+EXTRACT_STATIC         = NO
+EXTRACT_LOCAL_CLASSES  = YES
+EXTRACT_LOCAL_METHODS  = NO
+EXTRACT_ANON_NSPACES   = NO
+HIDE_UNDOC_MEMBERS     = NO
+HIDE_UNDOC_CLASSES     = NO
+HIDE_FRIEND_COMPOUNDS  = NO
+HIDE_IN_BODY_DOCS      = NO
+INTERNAL_DOCS          = NO
+CASE_SENSE_NAMES       = YES
+HIDE_SCOPE_NAMES       = NO
+SHOW_INCLUDE_FILES     = YES
+SHOW_GROUPED_MEMB_INC  = NO
+FORCE_LOCAL_INCLUDES   = NO
+INLINE_INFO            = YES
+SORT_MEMBER_DOCS       = YES
+SORT_BRIEF_DOCS        = NO
+SORT_MEMBERS_CTORS_1ST = NO
+SORT_GROUP_NAMES       = NO
+SORT_BY_SCOPE_NAME     = NO
+STRICT_PROTO_MATCHING  = NO
+GENERATE_TODOLIST      = YES
+GENERATE_TESTLIST      = YES
+GENERATE_BUGLIST       = YES
+GENERATE_DEPRECATEDLIST= YES
+ENABLED_SECTIONS       =
+MAX_INITIALIZER_LINES  = 30
+SHOW_USED_FILES        = YES
+SHOW_FILES             = YES
+SHOW_NAMESPACES        = YES
+FILE_VERSION_FILTER    =
+LAYOUT_FILE            =
+CITE_BIB_FILES         =
+#---------------------------------------------------------------------------
+# Configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+QUIET                  = NO
+WARNINGS               = YES
+WARN_IF_UNDOCUMENTED   = YES
+WARN_IF_DOC_ERROR      = YES
+WARN_NO_PARAMDOC       = NO
+WARN_FORMAT            = "$file:$line: $text"
+WARN_LOGFILE           =
+#---------------------------------------------------------------------------
+# Configuration options related to the input files
+#---------------------------------------------------------------------------
+INPUT                  = \
+		       tools.dox \
+		       zunitc/doc/zunitc.dox \
+		       zunitc/inc/zunitc/zunitc.h \
+		       waycheck/waycheck.dox \
+		       waycheck/wtst_fixtures.h \
+		       waycheck/rough_draw.h
+INPUT_ENCODING         = UTF-8
+FILE_PATTERNS          =
+RECURSIVE              = NO
+EXCLUDE                =
+EXCLUDE_SYMLINKS       = NO
+EXCLUDE_PATTERNS       =
+EXCLUDE_SYMBOLS        =
+EXAMPLE_PATH           =
+EXAMPLE_PATTERNS       =
+EXAMPLE_RECURSIVE      = NO
+IMAGE_PATH             =
+INPUT_FILTER           =
+FILTER_PATTERNS        =
+FILTER_SOURCE_FILES    = NO
+FILTER_SOURCE_PATTERNS =
+USE_MDFILE_AS_MAINPAGE =
+#---------------------------------------------------------------------------
+# Configuration options related to source browsing
+#---------------------------------------------------------------------------
+SOURCE_BROWSER         = NO
+INLINE_SOURCES         = NO
+STRIP_CODE_COMMENTS    = YES
+REFERENCED_BY_RELATION = NO
+REFERENCES_RELATION    = NO
+REFERENCES_LINK_SOURCE = YES
+SOURCE_TOOLTIPS        = YES
+USE_HTAGS              = NO
+VERBATIM_HEADERS       = YES
+CLANG_ASSISTED_PARSING = NO
+CLANG_OPTIONS          =
+#---------------------------------------------------------------------------
+# Configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+ALPHABETICAL_INDEX     = YES
+COLS_IN_ALPHA_INDEX    = 5
+IGNORE_PREFIX          =
+#---------------------------------------------------------------------------
+# Configuration options related to the HTML output
+#---------------------------------------------------------------------------
+GENERATE_HTML          = YES
+HTML_OUTPUT            = html
+HTML_FILE_EXTENSION    = .html
+HTML_HEADER            =
+HTML_FOOTER            =
+HTML_STYLESHEET        =
+HTML_EXTRA_STYLESHEET  =
+HTML_EXTRA_FILES       =
+HTML_COLORSTYLE_HUE    = 220
+HTML_COLORSTYLE_SAT    = 100
+HTML_COLORSTYLE_GAMMA  = 80
+HTML_TIMESTAMP         = YES
+HTML_DYNAMIC_SECTIONS  = NO
+HTML_INDEX_NUM_ENTRIES = 100
+GENERATE_DOCSET        = NO
+DOCSET_FEEDNAME        = "Doxygen generated docs"
+DOCSET_BUNDLE_ID       = org.doxygen.Project
+DOCSET_PUBLISHER_ID    = org.doxygen.Publisher
+DOCSET_PUBLISHER_NAME  = Publisher
+GENERATE_HTMLHELP      = NO
+CHM_FILE               =
+HHC_LOCATION           =
+GENERATE_CHI           = NO
+CHM_INDEX_ENCODING     =
+BINARY_TOC             = NO
+TOC_EXPAND             = NO
+GENERATE_QHP           = NO
+QCH_FILE               =
+QHP_NAMESPACE          = org.doxygen.Project
+QHP_VIRTUAL_FOLDER     = doc
+QHP_CUST_FILTER_NAME   =
+QHP_CUST_FILTER_ATTRS  =
+QHP_SECT_FILTER_ATTRS  =
+QHG_LOCATION           =
+GENERATE_ECLIPSEHELP   = NO
+ECLIPSE_DOC_ID         = org.doxygen.Project
+DISABLE_INDEX          = NO
+GENERATE_TREEVIEW      = NO
+ENUM_VALUES_PER_LINE   = 4
+TREEVIEW_WIDTH         = 250
+EXT_LINKS_IN_WINDOW    = NO
+FORMULA_FONTSIZE       = 10
+FORMULA_TRANSPARENT    = YES
+USE_MATHJAX            = NO
+MATHJAX_FORMAT         = HTML-CSS
+MATHJAX_RELPATH        = http://cdn.mathjax.org/mathjax/latest
+MATHJAX_EXTENSIONS     =
+MATHJAX_CODEFILE       =
+SEARCHENGINE           = YES
+SERVER_BASED_SEARCH    = NO
+EXTERNAL_SEARCH        = NO
+SEARCHENGINE_URL       =
+SEARCHDATA_FILE        = searchdata.xml
+EXTERNAL_SEARCH_ID     =
+EXTRA_SEARCH_MAPPINGS  =
+#---------------------------------------------------------------------------
+# Configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+GENERATE_LATEX         = YES
+LATEX_OUTPUT           = latex
+LATEX_CMD_NAME         = latex
+MAKEINDEX_CMD_NAME     = makeindex
+COMPACT_LATEX          = NO
+PAPER_TYPE             = a4
+EXTRA_PACKAGES         =
+LATEX_HEADER           =
+LATEX_FOOTER           =
+LATEX_EXTRA_FILES      =
+PDF_HYPERLINKS         = YES
+USE_PDFLATEX           = YES
+LATEX_BATCHMODE        = NO
+LATEX_HIDE_INDICES     = NO
+LATEX_SOURCE_CODE      = NO
+LATEX_BIB_STYLE        = plain
+#---------------------------------------------------------------------------
+# Configuration options related to the RTF output
+#---------------------------------------------------------------------------
+GENERATE_RTF           = NO
+RTF_OUTPUT             = rtf
+COMPACT_RTF            = NO
+RTF_HYPERLINKS         = NO
+RTF_STYLESHEET_FILE    =
+RTF_EXTENSIONS_FILE    =
+#---------------------------------------------------------------------------
+# Configuration options related to the man page output
+#---------------------------------------------------------------------------
+GENERATE_MAN           = NO
+MAN_OUTPUT             = man
+MAN_EXTENSION          = .3
+MAN_SUBDIR             =
+MAN_LINKS              = NO
+#---------------------------------------------------------------------------
+# Configuration options related to the XML output
+#---------------------------------------------------------------------------
+GENERATE_XML           = NO
+XML_OUTPUT             = xml
+XML_PROGRAMLISTING     = YES
+#---------------------------------------------------------------------------
+# Configuration options related to the DOCBOOK output
+#---------------------------------------------------------------------------
+GENERATE_DOCBOOK       = NO
+DOCBOOK_OUTPUT         = docbook
+DOCBOOK_PROGRAMLISTING = NO
+#---------------------------------------------------------------------------
+# Configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+GENERATE_AUTOGEN_DEF   = NO
+#---------------------------------------------------------------------------
+# Configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+GENERATE_PERLMOD       = NO
+PERLMOD_LATEX          = NO
+PERLMOD_PRETTY         = YES
+PERLMOD_MAKEVAR_PREFIX =
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+ENABLE_PREPROCESSING   = YES
+MACRO_EXPANSION        = NO
+EXPAND_ONLY_PREDEF     = NO
+SEARCH_INCLUDES        = YES
+INCLUDE_PATH           =
+INCLUDE_FILE_PATTERNS  =
+PREDEFINED             =
+EXPAND_AS_DEFINED      =
+SKIP_FUNCTION_MACROS   = YES
+#---------------------------------------------------------------------------
+# Configuration options related to external references
+#---------------------------------------------------------------------------
+TAGFILES               =
+GENERATE_TAGFILE       =
+ALLEXTERNALS           = NO
+EXTERNAL_GROUPS        = YES
+EXTERNAL_PAGES         = YES
+PERL_PATH              = /usr/bin/perl
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+CLASS_DIAGRAMS         = YES
+MSCGEN_PATH            =
+DIA_PATH               =
+HIDE_UNDOC_RELATIONS   = YES
+HAVE_DOT               = YES
+DOT_NUM_THREADS        = 0
+DOT_FONTNAME           = Helvetica
+DOT_FONTSIZE           = 10
+DOT_FONTPATH           =
+CLASS_GRAPH            = YES
+COLLABORATION_GRAPH    = YES
+GROUP_GRAPHS           = YES
+UML_LOOK               = NO
+UML_LIMIT_NUM_FIELDS   = 10
+TEMPLATE_RELATIONS     = NO
+INCLUDE_GRAPH          = YES
+INCLUDED_BY_GRAPH      = YES
+CALL_GRAPH             = NO
+CALLER_GRAPH           = NO
+GRAPHICAL_HIERARCHY    = YES
+DIRECTORY_GRAPH        = YES
+DOT_IMAGE_FORMAT       = png
+INTERACTIVE_SVG        = NO
+DOT_PATH               =
+DOTFILE_DIRS           =
+MSCFILE_DIRS           =
+DIAFILE_DIRS           =
+PLANTUML_JAR_PATH      =
+DOT_GRAPH_MAX_NODES    = 50
+MAX_DOT_GRAPH_DEPTH    = 0
+DOT_TRANSPARENT        = NO
+DOT_MULTI_TARGETS      = NO
+GENERATE_LEGEND        = YES
+DOT_CLEANUP            = YES
diff --git a/tools/devdocs.doxyfile b/tools/devdocs.doxyfile
new file mode 100644
index 0000000..41aa836
--- /dev/null
+++ b/tools/devdocs.doxyfile
@@ -0,0 +1,315 @@
+# Doxyfile 1.8.8
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+DOXYFILE_ENCODING      = UTF-8
+PROJECT_NAME           = "Tool Internals"
+PROJECT_NUMBER         =
+PROJECT_BRIEF          =
+PROJECT_LOGO           =
+OUTPUT_DIRECTORY       = devdocs
+CREATE_SUBDIRS         = NO
+ALLOW_UNICODE_NAMES    = NO
+OUTPUT_LANGUAGE        = English
+BRIEF_MEMBER_DESC      = YES
+REPEAT_BRIEF           = YES
+ABBREVIATE_BRIEF       =
+ALWAYS_DETAILED_SEC    = NO
+INLINE_INHERITED_MEMB  = NO
+FULL_PATH_NAMES        = YES
+STRIP_FROM_PATH        =
+STRIP_FROM_INC_PATH    =
+SHORT_NAMES            = NO
+JAVADOC_AUTOBRIEF      = YES
+QT_AUTOBRIEF           = NO
+MULTILINE_CPP_IS_BRIEF = NO
+INHERIT_DOCS           = YES
+SEPARATE_MEMBER_PAGES  = NO
+TAB_SIZE               = 4
+ALIASES                =
+TCL_SUBST              =
+OPTIMIZE_OUTPUT_FOR_C  = YES
+OPTIMIZE_OUTPUT_JAVA   = NO
+OPTIMIZE_FOR_FORTRAN   = NO
+OPTIMIZE_OUTPUT_VHDL   = NO
+EXTENSION_MAPPING      =
+MARKDOWN_SUPPORT       = YES
+AUTOLINK_SUPPORT       = YES
+BUILTIN_STL_SUPPORT    = NO
+CPP_CLI_SUPPORT        = NO
+SIP_SUPPORT            = NO
+IDL_PROPERTY_SUPPORT   = YES
+DISTRIBUTE_GROUP_DOC   = NO
+SUBGROUPING            = YES
+INLINE_GROUPED_CLASSES = NO
+INLINE_SIMPLE_STRUCTS  = NO
+TYPEDEF_HIDES_STRUCT   = NO
+LOOKUP_CACHE_SIZE      = 0
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+EXTRACT_ALL            = YES
+EXTRACT_PRIVATE        = NO
+EXTRACT_PACKAGE        = NO
+EXTRACT_STATIC         = NO
+EXTRACT_LOCAL_CLASSES  = YES
+EXTRACT_LOCAL_METHODS  = NO
+EXTRACT_ANON_NSPACES   = NO
+HIDE_UNDOC_MEMBERS     = NO
+HIDE_UNDOC_CLASSES     = NO
+HIDE_FRIEND_COMPOUNDS  = NO
+HIDE_IN_BODY_DOCS      = NO
+INTERNAL_DOCS          = NO
+CASE_SENSE_NAMES       = YES
+HIDE_SCOPE_NAMES       = NO
+SHOW_INCLUDE_FILES     = YES
+SHOW_GROUPED_MEMB_INC  = NO
+FORCE_LOCAL_INCLUDES   = NO
+INLINE_INFO            = YES
+SORT_MEMBER_DOCS       = YES
+SORT_BRIEF_DOCS        = NO
+SORT_MEMBERS_CTORS_1ST = NO
+SORT_GROUP_NAMES       = NO
+SORT_BY_SCOPE_NAME     = NO
+STRICT_PROTO_MATCHING  = NO
+GENERATE_TODOLIST      = YES
+GENERATE_TESTLIST      = YES
+GENERATE_BUGLIST       = YES
+GENERATE_DEPRECATEDLIST= YES
+ENABLED_SECTIONS       =
+MAX_INITIALIZER_LINES  = 30
+SHOW_USED_FILES        = YES
+SHOW_FILES             = YES
+SHOW_NAMESPACES        = YES
+FILE_VERSION_FILTER    =
+LAYOUT_FILE            =
+CITE_BIB_FILES         =
+#---------------------------------------------------------------------------
+# Configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+QUIET                  = NO
+WARNINGS               = YES
+WARN_IF_UNDOCUMENTED   = YES
+WARN_IF_DOC_ERROR      = YES
+WARN_NO_PARAMDOC       = NO
+WARN_FORMAT            = "$file:$line: $text"
+WARN_LOGFILE           =
+#---------------------------------------------------------------------------
+# Configuration options related to the input files
+#---------------------------------------------------------------------------
+INPUT                  = \
+		       devtools.dox \
+		       zunitc \
+		       waycheck
+INPUT_ENCODING         = UTF-8
+FILE_PATTERNS          =
+RECURSIVE              = YES
+EXCLUDE                =
+EXCLUDE_SYMLINKS       = NO
+EXCLUDE_PATTERNS       =
+EXCLUDE_SYMBOLS        =
+EXAMPLE_PATH           =
+EXAMPLE_PATTERNS       =
+EXAMPLE_RECURSIVE      = NO
+IMAGE_PATH             =
+INPUT_FILTER           =
+FILTER_PATTERNS        =
+FILTER_SOURCE_FILES    = NO
+FILTER_SOURCE_PATTERNS =
+USE_MDFILE_AS_MAINPAGE =
+#---------------------------------------------------------------------------
+# Configuration options related to source browsing
+#---------------------------------------------------------------------------
+SOURCE_BROWSER         = NO
+INLINE_SOURCES         = NO
+STRIP_CODE_COMMENTS    = YES
+REFERENCED_BY_RELATION = NO
+REFERENCES_RELATION    = NO
+REFERENCES_LINK_SOURCE = YES
+SOURCE_TOOLTIPS        = YES
+USE_HTAGS              = NO
+VERBATIM_HEADERS       = YES
+CLANG_ASSISTED_PARSING = NO
+CLANG_OPTIONS          =
+#---------------------------------------------------------------------------
+# Configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+ALPHABETICAL_INDEX     = YES
+COLS_IN_ALPHA_INDEX    = 5
+IGNORE_PREFIX          =
+#---------------------------------------------------------------------------
+# Configuration options related to the HTML output
+#---------------------------------------------------------------------------
+GENERATE_HTML          = YES
+HTML_OUTPUT            = html
+HTML_FILE_EXTENSION    = .html
+HTML_HEADER            =
+HTML_FOOTER            =
+HTML_STYLESHEET        =
+HTML_EXTRA_STYLESHEET  =
+HTML_EXTRA_FILES       =
+HTML_COLORSTYLE_HUE    = 220
+HTML_COLORSTYLE_SAT    = 100
+HTML_COLORSTYLE_GAMMA  = 80
+HTML_TIMESTAMP         = YES
+HTML_DYNAMIC_SECTIONS  = NO
+HTML_INDEX_NUM_ENTRIES = 100
+GENERATE_DOCSET        = NO
+DOCSET_FEEDNAME        = "Doxygen generated docs"
+DOCSET_BUNDLE_ID       = org.doxygen.Project
+DOCSET_PUBLISHER_ID    = org.doxygen.Publisher
+DOCSET_PUBLISHER_NAME  = Publisher
+GENERATE_HTMLHELP      = NO
+CHM_FILE               =
+HHC_LOCATION           =
+GENERATE_CHI           = NO
+CHM_INDEX_ENCODING     =
+BINARY_TOC             = NO
+TOC_EXPAND             = NO
+GENERATE_QHP           = NO
+QCH_FILE               =
+QHP_NAMESPACE          = org.doxygen.Project
+QHP_VIRTUAL_FOLDER     = doc
+QHP_CUST_FILTER_NAME   =
+QHP_CUST_FILTER_ATTRS  =
+QHP_SECT_FILTER_ATTRS  =
+QHG_LOCATION           =
+GENERATE_ECLIPSEHELP   = NO
+ECLIPSE_DOC_ID         = org.doxygen.Project
+DISABLE_INDEX          = NO
+GENERATE_TREEVIEW      = NO
+ENUM_VALUES_PER_LINE   = 4
+TREEVIEW_WIDTH         = 250
+EXT_LINKS_IN_WINDOW    = NO
+FORMULA_FONTSIZE       = 10
+FORMULA_TRANSPARENT    = YES
+USE_MATHJAX            = NO
+MATHJAX_FORMAT         = HTML-CSS
+MATHJAX_RELPATH        = http://cdn.mathjax.org/mathjax/latest
+MATHJAX_EXTENSIONS     =
+MATHJAX_CODEFILE       =
+SEARCHENGINE           = YES
+SERVER_BASED_SEARCH    = NO
+EXTERNAL_SEARCH        = NO
+SEARCHENGINE_URL       =
+SEARCHDATA_FILE        = searchdata.xml
+EXTERNAL_SEARCH_ID     =
+EXTRA_SEARCH_MAPPINGS  =
+#---------------------------------------------------------------------------
+# Configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+GENERATE_LATEX         = YES
+LATEX_OUTPUT           = latex
+LATEX_CMD_NAME         = latex
+MAKEINDEX_CMD_NAME     = makeindex
+COMPACT_LATEX          = NO
+PAPER_TYPE             = a4
+EXTRA_PACKAGES         =
+LATEX_HEADER           =
+LATEX_FOOTER           =
+LATEX_EXTRA_FILES      =
+PDF_HYPERLINKS         = YES
+USE_PDFLATEX           = YES
+LATEX_BATCHMODE        = NO
+LATEX_HIDE_INDICES     = NO
+LATEX_SOURCE_CODE      = NO
+LATEX_BIB_STYLE        = plain
+#---------------------------------------------------------------------------
+# Configuration options related to the RTF output
+#---------------------------------------------------------------------------
+GENERATE_RTF           = NO
+RTF_OUTPUT             = rtf
+COMPACT_RTF            = NO
+RTF_HYPERLINKS         = NO
+RTF_STYLESHEET_FILE    =
+RTF_EXTENSIONS_FILE    =
+#---------------------------------------------------------------------------
+# Configuration options related to the man page output
+#---------------------------------------------------------------------------
+GENERATE_MAN           = NO
+MAN_OUTPUT             = man
+MAN_EXTENSION          = .3
+MAN_SUBDIR             =
+MAN_LINKS              = NO
+#---------------------------------------------------------------------------
+# Configuration options related to the XML output
+#---------------------------------------------------------------------------
+GENERATE_XML           = NO
+XML_OUTPUT             = xml
+XML_PROGRAMLISTING     = YES
+#---------------------------------------------------------------------------
+# Configuration options related to the DOCBOOK output
+#---------------------------------------------------------------------------
+GENERATE_DOCBOOK       = NO
+DOCBOOK_OUTPUT         = docbook
+DOCBOOK_PROGRAMLISTING = NO
+#---------------------------------------------------------------------------
+# Configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+GENERATE_AUTOGEN_DEF   = NO
+#---------------------------------------------------------------------------
+# Configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+GENERATE_PERLMOD       = NO
+PERLMOD_LATEX          = NO
+PERLMOD_PRETTY         = YES
+PERLMOD_MAKEVAR_PREFIX =
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+ENABLE_PREPROCESSING   = YES
+MACRO_EXPANSION        = NO
+EXPAND_ONLY_PREDEF     = NO
+SEARCH_INCLUDES        = YES
+INCLUDE_PATH           =
+INCLUDE_FILE_PATTERNS  =
+PREDEFINED             =
+EXPAND_AS_DEFINED      =
+SKIP_FUNCTION_MACROS   = YES
+#---------------------------------------------------------------------------
+# Configuration options related to external references
+#---------------------------------------------------------------------------
+TAGFILES               =
+GENERATE_TAGFILE       =
+ALLEXTERNALS           = NO
+EXTERNAL_GROUPS        = YES
+EXTERNAL_PAGES         = YES
+PERL_PATH              = /usr/bin/perl
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+CLASS_DIAGRAMS         = YES
+MSCGEN_PATH            =
+DIA_PATH               =
+HIDE_UNDOC_RELATIONS   = YES
+HAVE_DOT               = YES
+DOT_NUM_THREADS        = 0
+DOT_FONTNAME           = Helvetica
+DOT_FONTSIZE           = 10
+DOT_FONTPATH           =
+CLASS_GRAPH            = YES
+COLLABORATION_GRAPH    = YES
+GROUP_GRAPHS           = YES
+UML_LOOK               = NO
+UML_LIMIT_NUM_FIELDS   = 10
+TEMPLATE_RELATIONS     = NO
+INCLUDE_GRAPH          = YES
+INCLUDED_BY_GRAPH      = YES
+CALL_GRAPH             = NO
+CALLER_GRAPH           = NO
+GRAPHICAL_HIERARCHY    = YES
+DIRECTORY_GRAPH        = YES
+DOT_IMAGE_FORMAT       = png
+INTERACTIVE_SVG        = NO
+DOT_PATH               =
+DOTFILE_DIRS           = .
+MSCFILE_DIRS           =
+DIAFILE_DIRS           =
+PLANTUML_JAR_PATH      =
+DOT_GRAPH_MAX_NODES    = 50
+MAX_DOT_GRAPH_DEPTH    = 0
+DOT_TRANSPARENT        = NO
+DOT_MULTI_TARGETS      = NO
+GENERATE_LEGEND        = YES
+DOT_CLEANUP            = YES
diff --git a/tools/devtools.dox b/tools/devtools.dox
new file mode 100644
index 0000000..c1ec9fb
--- /dev/null
+++ b/tools/devtools.dox
@@ -0,0 +1,52 @@
+/*
+ * Copyright © 2015 Samsung Electronics Co., Ltd
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of the copyright holders not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.  The copyright holders make
+ * no representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+ * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/**
+ at mainpage
+
+- @ref zunitc - Simple test framework
+
+- @ref waycheck - Simple integration/acceptance test tool
+
+ at section tools_overview Overview
+
+The tools area currently consists of two sub-projects (@ref zunitc and
+ at ref waycheck) that are refined from the prior single weston/tests source
+folder.
+
+ at subsection tools_overview_old Old Code Organization
+
+The original 'tests' folder contained basic weston testing with an
+integrated test runner framework. Over time things progressed to the
+stage where splitting apart into discrete layers was warranted.
+
+ at dotfile tools_arch_old.gv "Original test code organization"
+
+ at subsection tools_overview_new New Code Organization
+
+The test code that is not weston-specific gets split out to a separate
+folder and/or folders. Then an additional testing tool 'waycheck' that is
+compositor-agnostic is added.
+
+ at dotfile tools_arch_new.gv "Refactored test code organization"
+
+*/
diff --git a/tools/tools.dox b/tools/tools.dox
new file mode 100644
index 0000000..e4f5524
--- /dev/null
+++ b/tools/tools.dox
@@ -0,0 +1,29 @@
+/*
+ * Copyright © 2015 Samsung Electronics Co., Ltd
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of the copyright holders not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.  The copyright holders make
+ * no representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+ * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/**
+ at mainpage
+
+- @ref zunitc - Simple test framework
+
+- @ref waycheck - Simple integration/acceptance test tool
+*/
diff --git a/tools/tools_arch_new.gv b/tools/tools_arch_new.gv
new file mode 100644
index 0000000..fe8b6c7
--- /dev/null
+++ b/tools/tools_arch_new.gv
@@ -0,0 +1,60 @@
+digraph toolarch_new {
+    rankdir = "TB";
+
+    node[shape=record]
+
+    subgraph cluster_0 {
+        label = "./tests";
+
+        keyboard_test_c [label = "{keyboard-test.c|tests\l}"]
+        text_test_c [label = "{text-test.c|tests\l}"]
+        vertex_clip_test_c [label = "{vertex-clip-test.c|tests\l}"]
+
+	spacer [shape = point, style = invis]
+
+        weston_test_client_helper [label = "{weston-test-client-helper.h/.c|Weston test protocol\l}"]
+
+        weston_test_c [label = "{weston-test.c|Extension protocol\nimplementation}"]
+    }
+
+    subgraph cluster_1 {
+        label = "./tools/waycheck";
+
+	waycheck [label = "{waycheck.c| \n \n }"]
+    }
+
+    subgraph cluster_2 {
+        label = "./tools/wayland_fixtures";
+
+	wtst_fixtures [label = "{wtst_fixtures.h/c|Wayland tracking structs\lWayland callbacks\l}"]
+    }
+
+    subgraph cluster_3 {
+        label = "./tools/zunitc";
+
+	zunitc [label = "{zunitc|Test definition macros\lTest running functions\lTest reporting functions\lTest run lifecycle\l}"]
+    }
+
+    keyboard_test_c -> weston_test_client_helper
+    keyboard_test_c -> wtst_fixtures
+    keyboard_test_c -> zunitc
+    vertex_clip_test_c -> zunitc
+    text_test_c -> weston_test_client_helper
+    text_test_c -> wtst_fixtures
+    text_test_c -> zunitc
+
+    waycheck -> wtst_fixtures
+    waycheck -> zunitc
+
+    wtst_fixtures -> zunitc
+
+    edge [style = dashed, arrowhead = open]
+    weston_test_client_helper -> weston_test_c
+
+    edge [style = invis]
+    weston_test_client_helper -> zunitc
+
+    text_test_c -> spacer
+    keyboard_test_c -> spacer
+    spacer -> weston_test_client_helper
+}
diff --git a/tools/tools_arch_old.gv b/tools/tools_arch_old.gv
new file mode 100644
index 0000000..b746121
--- /dev/null
+++ b/tools/tools_arch_old.gv
@@ -0,0 +1,28 @@
+digraph toolarch_old {
+    rankdir = "TB";
+
+    node[shape = record]
+
+    subgraph cluster_0 {
+        label = "./tests";
+
+        keyboard_test_c [label = "{keyboard-test.c|tests\l}"]
+        text_test_c [label = "{text-test.c|tests\l}"]
+        vertex_clip_test_c [label = "{vertex-clip-test.c|tests\l}"]
+
+        weston_test_client_helper [label = "{weston-test-client-helper.h/.c|Wayland tracking structs\lWeston test protocol\lWayland callbacks\lTest run lifecycle\l}"]
+
+        weston_test_c [label = "{weston-test.c|Extension protocol\nimplementation}"]
+        weston_test_runner [label = "{weston-test-runner.h/.c|Test definition macros\lTest running functions\lTest reporting functions\lTest run lifecycle\l}"]
+    }
+
+    weston_test_client_helper -> weston_test_runner
+    keyboard_test_c -> weston_test_client_helper
+    keyboard_test_c -> weston_test_runner
+    vertex_clip_test_c -> weston_test_runner
+    text_test_c -> weston_test_client_helper
+    text_test_c -> weston_test_runner
+
+    edge [style = dashed, arrowhead = open]
+    weston_test_client_helper -> weston_test_c
+}
diff --git a/tools/waycheck/moretest.c b/tools/waycheck/moretest.c
new file mode 100644
index 0000000..80ee5b4
--- /dev/null
+++ b/tools/waycheck/moretest.c
@@ -0,0 +1,83 @@
+/*
+ * Copyright © 2015 Samsung Electronics Co., Ltd
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of the copyright holders not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.  The copyright holders make
+ * no representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+ * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "config.h"
+
+#include "zunitc/zunitc.h"
+
+/* #define ENABLE_FAIL_TESTS */
+
+ZUC_TEST(more, good_Versions)
+{
+	ZUC_EXPECT_TRUE(1);
+	ZUC_EXPECT_TRUE(-1);
+	ZUC_EXPECT_TRUE(!0);
+
+	ZUC_EXPECT_FALSE(0);
+	ZUC_EXPECT_FALSE(!-1);
+	ZUC_EXPECT_FALSE(!1);
+
+	ZUC_EXPECT_EQ(4 + 0, 2 + 2);
+
+	ZUC_EXPECT_NE(5 - 1, 2 + 3);
+
+	ZUC_EXPECT_GE(4 - 0, 2 + 2);
+
+	ZUC_EXPECT_GT(4 - 9, 2 - 13);
+
+	ZUC_EXPECT_LE(4 + 3, 2 + 5);
+
+	ZUC_EXPECT_LT(4 + 9, 2 + 12);
+}
+
+#ifdef ENABLE_FAIL_TESTS
+ZUC_TEST(more, fail_Versions)
+{
+	ZUC_EXPECT_FALSE(zuc_has_failure());
+	ZUC_EXPECT_FALSE(zuc_has_fatal_failure());
+	ZUC_EXPECT_FALSE(zuc_has_nonfatal_failure());
+
+	ZUC_EXPECT_TRUE(0);
+	ZUC_EXPECT_TRUE(!-1);
+	ZUC_EXPECT_TRUE(!1);
+
+	ZUC_EXPECT_FALSE(1);
+	ZUC_EXPECT_FALSE(-1);
+	ZUC_EXPECT_FALSE(!0);
+
+	ZUC_EXPECT_EQ(4 + 0, 2 + 3);
+
+	ZUC_EXPECT_NE(5 - 1, 2 + 2);
+
+	ZUC_EXPECT_GE(4 - 0, 2 + 3);
+
+	ZUC_EXPECT_GT(4 - 9, 2 + 3);
+
+	ZUC_EXPECT_LE(4 + 3, 2 + 3);
+
+	ZUC_EXPECT_LT(4 + 9, 2 + 3);
+
+	ZUC_EXPECT_TRUE(zuc_has_failure());
+	ZUC_EXPECT_FALSE(zuc_has_fatal_failure());
+	ZUC_EXPECT_TRUE(zuc_has_nonfatal_failure());
+}
+#endif
diff --git a/tools/waycheck/rough_draw.c b/tools/waycheck/rough_draw.c
new file mode 100644
index 0000000..ed02a33
--- /dev/null
+++ b/tools/waycheck/rough_draw.c
@@ -0,0 +1,265 @@
+/*
+ * Copyright © 2015 Samsung Electronics Co., Ltd
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of the copyright holders not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.  The copyright holders make
+ * no representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+ * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stddef.h>
+#include <string.h>
+
+#include "rough_draw.h"
+
+#define ARRAY_LENGTH(a) ((int) (sizeof (a) / sizeof (a)[0]))
+
+/**
+ * Very simple way to track colors.
+ */
+struct colorval {
+	float r;
+	float g;
+	float b;
+	float a;
+	enum pixel_form form;
+	int used;
+	uint8_t raw[4];
+};
+
+static void populate_color(struct colorval *color, enum pixel_form form)
+{
+	memset(color->raw, 0, sizeof(color->raw));
+	color->form = form;
+	switch (form) {
+	case PXL_ARGB32:
+		color->used = 4;
+		color->raw[3] = (0xff * color->a) + 0.5;
+		color->raw[2] = (0xff * color->r) + 0.5;
+		color->raw[1] = (0xff * color->g) + 0.5;
+		color->raw[0] = (0xff * color->b) + 0.5;
+		break;
+	case PXL_RGB565:
+		color->used = 2;
+		color->raw[1] = (uint8_t)((0x1f * color->r) + 0.5) << 3;
+		color->raw[0] = (0x3f * color->g) + 0.5;
+		color->raw[1] |= 0x07 & (color->raw[0] >> 3);
+		color->raw[0] <<= 5;
+		color->raw[0] |= (uint8_t)((0x1f * color->b) + 0.5);
+		break;
+	default:
+		color->form = PXL_NA;
+		/* fall-through */
+	case PXL_NA:
+		color->used = 0;
+	}
+}
+
+static void fill_rect(uint8_t *raw, struct colorval *color,
+		      int width, int height, int stride)
+{
+	int y = 0;
+	for (y = 0; y < height; ++y) {
+		int x = 0;
+		uint8_t *ptr = raw + (y * stride);
+		for (x = 0; x < width; ++x) {
+			memcpy(ptr, &color->raw[0], color->used);
+			ptr += color->used;
+		}
+	}
+}
+
+static void blend(struct colorval *dest,
+		  struct colorval const *val1, struct colorval const *val2,
+		  double factor)
+{
+	dest->r = (val1->r * (1.0 - factor)) + (val2->r * factor);
+	dest->g = (val1->g * (1.0 - factor)) + (val2->g * factor);
+	dest->b = (val1->b * (1.0 - factor)) + (val2->b * factor);
+	dest->a = (val1->a * (1.0 - factor)) + (val2->a * factor);
+	populate_color(dest, val1->form);
+}
+
+static void hgrad_rect(uint8_t *buf,
+		       struct colorval const *val1, struct colorval const *val2,
+		       int width, int height, int stride)
+{
+	int x = 0;
+	int y = 0;
+	int pw = val1->used;
+	struct colorval color = {.a=1.0};
+	double dwidth = (double)width;
+	for (x = 0; x < width; ++x) {
+		uint8_t *ptr = buf + x * pw;
+		blend(&color, val1, val2, x / dwidth);
+		for (y = 0; y < height; ++y) {
+			memcpy(ptr, &color.raw[0], color.used);
+			ptr += stride;
+		}
+	}
+}
+
+/*
+ * Approximates common color bars with black at 16 and white at 235.
+ */
+static const double lvblk = (16 + 0.00 * (235 - 16)) / 255.0;
+static const double lvwht = (16 + 1.00 * (235 - 16)) / 255.0;
+static const double lvupr = (16 + 0.75 * (235 - 16)) / 255.0;
+static const double lvmid = (16 + 0.40 * (235 - 16)) / 255.0;
+static const double lvb15 = (16 + 0.15 * (235 - 16)) / 255.0;
+static const double lvb_2 = (16 - 0.02 * (235 - 16)) / 255.0;
+static const double lvb02 = (16 + 0.02 * (235 - 16)) / 255.0;
+static const double lvb04 = (16 + 0.04 * (235 - 16)) / 255.0;
+
+void rd_draw_bars(uint8_t *buf, enum pixel_form form, int stride,
+		  int width, int height)
+{
+	int i;
+	int y0, y1, y2;
+	int h1, h2;
+	int w1, w2, w3, w4, hw;
+	int xr;
+	int pw;
+	int xoff;
+	double bar_span;
+	int bar_count;
+	struct colorval bars[] = {
+		{.r=lvmid, .g=lvmid, .b=lvmid, .a=1.0},
+		{.r=lvupr, .g=lvupr, .b=lvupr, .a=1.0},
+		{.r=lvupr, .g=lvupr, .b=lvblk, .a=1.0},
+		{.r=lvblk, .g=lvupr, .b=lvupr, .a=1.0},
+		{.r=lvblk, .g=lvupr, .b=lvblk, .a=1.0},
+		{.r=lvupr, .g=lvblk, .b=lvupr, .a=1.0},
+		{.r=lvupr, .g=lvblk, .b=lvblk, .a=1.0},
+		{.r=lvblk, .g=lvblk, .b=lvupr, .a=1.0},
+		{.r=lvmid, .g=lvmid, .b=lvmid, .a=1.0},
+		{.r=lvb15, .g=lvb15, .b=lvb15, .a=1.0},
+		{.r=lvwht, .g=lvwht, .b=lvwht, .a=1.0},
+		{.r=lvblk, .g=lvblk, .b=lvblk, .a=1.0},
+		{.r=lvb_2, .g=lvb_2, .b=lvb_2, .a=1.0},
+		{.r=lvb02, .g=lvb02, .b=lvb02, .a=1.0},
+		{.r=lvb04, .g=lvb04, .b=lvb04, .a=1.0},
+	};
+
+	if (!buf)
+		return;
+
+	for (i = 0; i < ARRAY_LENGTH(bars); ++i)
+		populate_color(&bars[i], form);
+
+	pw = bars[0].used;
+	y0 = ((height * 1.75) / 3);
+	y1 = ((height * 2) / 3);
+	y2 = ((height * 3) / 4);
+	h1 = y1 - y0;
+	h2 = height - y2;
+	w1 = width / 8;
+	w2 = (width * 0.75) / 7;
+	xr = pw * ((width * 7) / 8);
+
+	/* left gray: */
+	fill_rect(buf, &bars[0], w1, y0, stride);
+	fill_rect(buf + (y0 * stride), &bars[3], w1, h1, stride);
+	fill_rect(buf + (y1 * stride), &bars[2], w1, h1, stride);
+	fill_rect(buf + (y2 * stride), &bars[9], w1, h2, stride);
+
+	/* right gray */
+	fill_rect(buf + xr, &bars[0], w1, y0, stride);
+	fill_rect(buf + xr + (y0 * stride), &bars[7], w1, h1, stride);
+	fill_rect(buf + xr + (y1 * stride), &bars[6], w1, h1, stride);
+	fill_rect(buf + xr + (y2 * stride), &bars[9], w1, h2, stride);
+
+	xoff = w1 * pw;
+	fill_rect(buf + xoff + (y0 * stride), &bars[10], w2, h1, stride);
+	fill_rect(buf + xoff + (y1 * stride), &bars[11], w2, h1, stride);
+
+	bar_span = (width * 0.75);
+	bar_count = 7;
+	for (i = 0; i < bar_count; ++i)
+	{
+		int ww = (bar_span * (i + 1)) / bar_count;
+		xoff = (bar_span * i) / bar_count;
+		ww -= xoff;
+		xoff += w1;
+		fill_rect(buf + xoff * pw, &bars[i + 1], ww, y0, stride);
+	}
+
+
+	hw = width - (w1 + w1 + w2);
+	/* Gray horiz: */
+	xoff = w1 + w2;
+	fill_rect(buf + xoff * pw + (y0 * stride), &bars[1], hw, h1, stride);
+	hgrad_rect(buf + xoff * pw + (y1 * stride), &bars[11], &bars[10],
+		   hw, h1, stride);
+
+	/* note - horizontal positions of lower bars are not simple values */
+
+	xoff = (width * 0.85625) / 3;
+	w3 = (width * 0.64375) / 3;
+	fill_rect(buf + w1 * pw + (y2 * stride), &bars[11], width - (w1 * 2),
+		  h2, stride);
+	fill_rect(buf + xoff * pw + (y2 * stride), &bars[10], w3,
+		  h2, stride);
+
+	w4 = (width * 0.10625) / 3;
+	xoff = (width * 1.765625) / 3;
+	fill_rect(buf + xoff * pw + (y2 * stride), &bars[12], w4, h2, stride);
+
+	xoff = (width * 1.98125) / 3;
+	fill_rect(buf + xoff * pw + (y2 * stride), &bars[13], w4, h2, stride);
+
+	xoff = (width * 2.196875) / 3;
+	fill_rect(buf + xoff * pw + (y2 * stride), &bars[14], w4, h2, stride);
+}
+
+void rd_draw_crosshairs(uint8_t *buf, enum pixel_form form, int stride,
+			int width, int height)
+{
+	int pw;
+	int x = (width + 1) / 2;
+	int y = (height + 1) / 2;
+	struct colorval fg = {.r=0, .g=0, .b=0, .a=0.5};
+	struct colorval bg = {.r=0.5, .g=0.5, .b=0.5, .a=0.5};
+
+	if (!buf)
+		return;
+
+	populate_color(&fg, form);
+	populate_color(&bg, form);
+	pw = fg.used;
+
+	fill_rect(buf + ((y - 1) * stride), &bg, width, 3, stride);
+	fill_rect(buf + (y * stride), &fg, width, 1, stride);
+
+	fill_rect(buf + ((y - 2) * stride), &bg, width / 3, 5, stride);
+	fill_rect(buf + ((y - 1) * stride), &fg, width / 3, 3, stride);
+	fill_rect(buf + ((y - 2) * stride + (width * 2 / 3) * pw), &bg,
+		  width / 3, 5, stride);
+	fill_rect(buf + ((y - 1) * stride + (width * 2 / 3) * pw), &fg,
+		  width / 3, 3, stride);
+
+
+	fill_rect(buf + (x - 1) * pw, &bg, 3, height, stride);
+	fill_rect(buf + x * pw, &fg, 1, height, stride);
+
+	fill_rect(buf + (x - 2) * pw, &bg, 5, height / 3, stride);
+	fill_rect(buf + (x - 1) * pw, &fg, 3, height / 3, stride);
+
+	fill_rect(buf + (x - 2) * pw + ((height * 2 / 3) * stride), &bg,
+		  5, height / 3, stride);
+	fill_rect(buf + (x - 1) * pw + ((height * 2 / 3) * stride), &fg,
+		  3, height / 3, stride);
+}
diff --git a/tools/waycheck/rough_draw.h b/tools/waycheck/rough_draw.h
new file mode 100644
index 0000000..4bb2ce6
--- /dev/null
+++ b/tools/waycheck/rough_draw.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright © 2015 Samsung Electronics Co., Ltd
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of the copyright holders not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.  The copyright holders make
+ * no representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+ * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef ROUGH_DRAW_H
+#define ROUGH_DRAW_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+
+
+/**
+ * Possible memory formats.
+ * Very short list for now
+ */
+enum pixel_form {
+	PXL_NA = 0,
+	PXL_ARGB32,
+	PXL_RGB565
+};
+
+/**
+ * Draws a simple color-bars test pattern to the specified buffer.
+ */
+void rd_draw_bars(uint8_t *buf, enum pixel_form form, int stride,
+		  int width, int height);
+
+/**
+ * Draws a simple cross-hairs image suitable for a cursor to the specified
+ * buffer.
+ */
+void rd_draw_crosshairs(uint8_t *buf, enum pixel_form form, int stride,
+			int width, int height);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* ROUGH_DRAW_H */
diff --git a/tools/waycheck/waycheck.c b/tools/waycheck/waycheck.c
new file mode 100644
index 0000000..49d32ae
--- /dev/null
+++ b/tools/waycheck/waycheck.c
@@ -0,0 +1,416 @@
+/*
+ * Copyright © 2015 Samsung Electronics Co., Ltd
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of the copyright holders not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.  The copyright holders make
+ * no representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+ * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdbool.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <sys/mman.h>
+
+#include <linux/input.h>
+#include <wayland-client.h>
+
+
+#include "os-compatibility.h"
+#include "rough_draw.h"
+#include "shared/config-parser.h"
+#include "shared/zalloc.h"
+#include "wtst_fixtures.h"
+#include "zunitc/zunitc.h"
+
+#define ARRAY_LENGTH(a) ((int) (sizeof (a) / sizeof (a)[0]))
+
+#define ARGB32_SIZE 4
+
+static const uint32_t PIXEL_FORMAT = WL_SHM_FORMAT_ARGB8888;
+
+static void *setup_test_config(void *data)
+{
+	struct wtst_ctx *ctx = wtst_ctx_create();
+	ZUC_EXPECT_TRUE(ctx != NULL);
+	if (zuc_has_failure())
+		ZUC_MARK_FATAL("Failure during fixture setup.");
+	return ctx;
+}
+
+static void cleanup_test_config(void *data)
+{
+	struct wtst_ctx *ctx = data;
+	ZUC_ASSERT_TRUE(ctx != NULL);
+	wtst_ctx_destroy(ctx);
+}
+
+static struct zuc_fixture base_test_f = {
+	.set_up = setup_test_config,
+	.tear_down = cleanup_test_config
+};
+
+/**
+ * Baseline test to see if a connection can be made to the Wayland server.
+ */
+ZUC_TEST(base_test, can_connect_display)
+{
+	struct wl_display *display = wl_display_connect(NULL);
+
+	ZUC_EXPECT_TRUE(display != NULL);
+
+	if (display) {
+		wl_display_disconnect(display);
+		display = NULL;
+	}
+}
+
+/* A very simple case that only exercises the test fixture. */
+ZUC_TEST(base_test, hello_wayland)
+{
+	struct wtst_ctx *ctx = wtst_ctx_create();
+	ZUC_ASSERT_TRUE(ctx != NULL);
+
+	wtst_ctx_destroy(ctx);
+}
+
+ZUC_TEST_F(base_test_f, advertises_required)
+{
+	int num_required = 0;
+	int i = 0;
+	char const *required[] = {
+		"wl_compositor",
+		"wl_data_device_manager",
+		"wl_output",
+		"wl_seat",
+		"wl_shell",
+		"wl_shm",
+		"wl_subcompositor",
+	};
+	struct wtst_ctx *ctx = data;
+
+	num_required = sizeof(required) / sizeof(required[0]);
+	for (i = 0; i < num_required; i++) {
+		ZUC_TRACEPOINT("Check %s\n", required[i]);
+		ZUC_EXPECT_TRUE(wtst_is_global_advertised(ctx, required[i]));
+	}
+}
+
+typedef void (*button_cb)(uint32_t serial, uint32_t time,
+			  uint32_t button, uint32_t state);
+
+struct surface_extras
+{
+	void (*button_cb)(uint32_t serial, uint32_t time,
+			  uint32_t button, uint32_t state);
+
+	uint8_t *membuf;
+	struct wl_buffer *buffer;
+	struct wl_surface *cursor;
+	int32_t hit_x;
+	int32_t hit_y;
+};
+
+static void setup_surface_extras(struct wtst_surface *wsurf)
+{
+	ZUC_ASSERT_TRUE(wsurf != NULL);
+	ZUC_EXPECT_TRUE(wsurf->data == NULL);
+	struct surface_extras *extras = zalloc(sizeof(*extras));
+	ZUC_ASSERT_TRUE(extras != NULL);
+	wsurf->data = extras;
+}
+
+/**
+ * @todo refactor to be more genericly helpful then move this along with
+ * the corresponding helper functions to fixtures lib
+ */
+struct wtst_shm_pool {
+	struct wl_shm_pool *pool;
+
+	int fd;
+	uint8_t *membuf;
+	size_t flen;
+	size_t used;
+};
+
+/* Simple testing helpers: */
+static uint8_t *wtst_shm_pool_get_membuf(struct wtst_shm_pool *pool);
+static struct wl_shm_pool *wtst_shm_pool_get_pool(struct wtst_shm_pool *pool);
+static void wtst_shm_pool_consume(struct wtst_shm_pool *pool, size_t mem_used);
+
+static struct wtst_shm_pool *wtst_create_mempool(struct wl_shm *shm,
+					  int fd, size_t flen);
+
+
+static void setup_cursor(struct wtst_shm_pool *pool,
+			 unsigned int width,
+			 unsigned int height,
+			 int32_t hit_x, int32_t hit_y,
+			 struct wl_compositor *compositor,
+			 struct surface_extras *extras);
+
+static void set_button_cb(struct wtst_shell_surface *shell_surface,
+			  button_cb callback);
+
+static const struct wl_pointer_listener pointer_listener;
+
+static bool keep_alive = true;
+
+static void handle_button(uint32_t serial, uint32_t time,
+			  uint32_t button, uint32_t state)
+{
+	keep_alive = false;
+}
+
+ZUC_TEST(base_test, DISABLED_sleeper)
+{
+/* Simple disabled test */
+}
+
+ZUC_TEST_F(base_test_f, simple_setup)
+{
+	struct wtst_ctx *ctx = data;
+	size_t needed = 0;
+	int anon = 0;
+	const int surf_width = 355;
+	const int surf_height = 200;
+	const int cursor_size = 45;
+	const int mid_hit = cursor_size / 2;
+	struct wtst_shm_pool *pool = NULL;
+	struct wl_buffer *buffer = NULL;
+	struct wtst_shell_surface *sh_surface = NULL;
+
+	keep_alive = false; /* Don't run the main loop for normal testing */
+
+	wtst_set_dump_pointer_events(WTST_DBG_POINTER_ALL
+				     & ~WTST_DBG_POINTER_MOTION);
+
+	needed = ARGB32_SIZE * surf_width * surf_height;
+	needed += ARGB32_SIZE * cursor_size * cursor_size;
+
+	anon = os_create_anonymous_file(needed);
+	buffer = NULL;
+	pool = wtst_create_mempool(ctx->shm, anon, needed);
+	if (!pool) {
+		close(anon);
+		ZUC_ASSERT_TRUE(pool != NULL);
+	} else {
+		uint8_t *mem = wtst_shm_pool_get_membuf(pool);
+		rd_draw_bars(mem, PXL_ARGB32, surf_width * ARGB32_SIZE,
+			     surf_width, surf_height);
+
+		struct wl_shm_pool *shpool =
+			wtst_shm_pool_get_pool(pool);
+		buffer = wl_shm_pool_create_buffer(shpool,
+						 0,
+						 surf_width,
+						 surf_height,
+						 surf_width * ARGB32_SIZE,
+						 PIXEL_FORMAT);
+		wtst_shm_pool_consume(pool,
+				      surf_width * surf_height * ARGB32_SIZE);
+		ZUC_EXPECT_TRUE(buffer != NULL);
+	}
+
+	sh_surface = wtst_create_shell_surface(ctx->compositor, ctx->shell);
+	setup_surface_extras(sh_surface->surface);
+
+	if (buffer) {
+		wl_surface_attach(sh_surface->surface->wl_surface, buffer,
+				  0, 0);
+		wl_surface_commit(sh_surface->surface->wl_surface);
+	}
+
+	setup_cursor(pool, cursor_size, cursor_size, mid_hit, mid_hit,
+		     ctx->compositor, sh_surface->surface->data);
+
+	set_button_cb(sh_surface, handle_button);
+	wtst_pointer_add_listener(ctx->input->pointer,
+				  &pointer_listener, ctx->input->pointer);
+
+	while (keep_alive) {
+		if (wl_display_dispatch(ctx->display) < 0)
+			keep_alive = false;
+	}
+
+	wl_buffer_destroy(buffer);
+}
+
+uint8_t *wtst_shm_pool_get_membuf(struct wtst_shm_pool *pool)
+{
+	ZUC_EXPECT_TRUE(pool != NULL);
+	return (pool) ? pool->membuf : NULL;
+}
+
+struct wl_shm_pool *wtst_shm_pool_get_pool(struct wtst_shm_pool *pool)
+{
+	ZUC_EXPECT_TRUE(pool != NULL);
+	return (pool) ? pool->pool : NULL;
+}
+
+static void wtst_shm_pool_consume(struct wtst_shm_pool *pool, size_t mem_used)
+{
+	ZUC_ASSERT_TRUE(pool != NULL);
+	pool->used += mem_used;
+}
+
+struct wtst_shm_pool *wtst_create_mempool(struct wl_shm *shm,
+					  int fd, size_t flen)
+{
+	struct wtst_shm_pool *wtst_pool = zalloc(sizeof(struct wtst_shm_pool));
+	ZUC_EXPECT_TRUE(wtst_pool != NULL);
+	if (!wtst_pool)
+		return NULL;
+
+	wtst_pool->fd = fd;
+	wtst_pool->flen = flen;
+	wtst_pool->membuf = mmap(NULL, flen, PROT_READ | PROT_WRITE,
+				 MAP_SHARED, fd, 0);
+	ZUC_EXPECT_TRUE(wtst_pool->membuf != MAP_FAILED);
+	if (wtst_pool->membuf == MAP_FAILED) {
+		printf("%s:%d: error: unable to memmap.\n", __FILE__, __LINE__);
+	} else {
+		wtst_pool->pool = wl_shm_create_pool(shm, fd, flen);
+		ZUC_EXPECT_TRUE(wtst_pool->pool != NULL);
+		if (!wtst_pool->pool) {
+			printf("%s:%d: error: Unable to create pool.\n",
+			       __FILE__, __LINE__);
+			munmap(wtst_pool->membuf, flen);
+		}
+	}
+
+	if (!wtst_pool->pool) {
+		free(wtst_pool);
+		wtst_pool = NULL;
+	}
+
+	return wtst_pool;
+}
+
+void set_button_cb(struct wtst_shell_surface *shell_surface, button_cb callback)
+{
+	struct wtst_surface *wsurf = NULL;
+	ZUC_ASSERT_TRUE(shell_surface != NULL);
+	ZUC_ASSERT_TRUE(shell_surface->surface != NULL);
+	ZUC_ASSERT_TRUE(shell_surface->surface->wl_surface != NULL);
+
+	wsurf = shell_surface->surface;
+	ZUC_ASSERT_TRUE(wsurf != NULL);
+	ZUC_ASSERT_TRUE(wsurf->data != NULL);
+
+	((struct surface_extras *)wsurf->data)->button_cb = callback;
+}
+
+void setup_cursor(struct wtst_shm_pool *pool,
+		  unsigned int width, unsigned int height,
+		  int32_t hit_x, int32_t hit_y,
+		  struct wl_compositor *compositor,
+		  struct surface_extras *extras)
+{
+	int32_t stride = width * ARGB32_SIZE;
+	ZUC_ASSERT_TRUE(extras != NULL);
+	extras->hit_x = hit_x;
+	extras->hit_y = hit_y;
+
+	extras->cursor = wl_compositor_create_surface(compositor);
+	ZUC_EXPECT_TRUE(extras->cursor != NULL);
+	if (!extras->cursor)
+		return;
+
+	extras->membuf = pool->membuf;
+	extras->membuf += pool->used / sizeof(*extras->membuf);
+	rd_draw_crosshairs(extras->membuf, PXL_ARGB32, width * ARGB32_SIZE,
+			   width, height);
+
+	extras->buffer = wl_shm_pool_create_buffer(pool->pool,
+						   pool->used,
+						   width,
+						   height,
+						   stride,
+						   PIXEL_FORMAT);
+	pool->used += width * height * ARGB32_SIZE;
+	ZUC_EXPECT_TRUE(extras->buffer != NULL);
+
+	if (extras->buffer) {
+		rd_draw_crosshairs(extras->membuf, PXL_ARGB32,
+				   width * ARGB32_SIZE,
+				   width, height);
+	}
+}
+
+
+static void foo_enter(void *data,
+		      struct wl_pointer *wl_pointer,
+		      uint32_t serial, struct wl_surface *surface,
+		      wl_fixed_t surface_x, wl_fixed_t surface_y)
+{
+	struct wtst_pointer *pointer = data;
+	if (pointer && pointer->focus && pointer->focus->data) {
+		struct surface_extras *extras = pointer->focus->data;
+		wl_surface_attach(extras->cursor, extras->buffer, 0, 0);
+		wl_surface_commit(extras->cursor);
+		wl_pointer_set_cursor(pointer->wl_pointer, serial,
+				      extras->cursor,
+				      extras->hit_x, extras->hit_y);
+	}
+}
+
+static void foo_leave(void *data,
+		      struct wl_pointer *wl_pointer, uint32_t serial,
+		      struct wl_surface *wl_surface)
+{
+}
+
+static void foo_motion(void *data,
+		       struct wl_pointer *wl_pointer, uint32_t time,
+		       wl_fixed_t surface_x, wl_fixed_t surface_y)
+{
+}
+
+static void foo_button(void *data,
+		       struct wl_pointer *wl_pointer, uint32_t serial,
+		       uint32_t time, uint32_t button, uint32_t state)
+{
+	struct wtst_pointer *pointer = data;
+	if (pointer && pointer->focus && pointer->focus->data) {
+		struct surface_extras *extras = pointer->focus->data;
+		if (extras->button_cb)
+			extras->button_cb(serial, time, button, state);
+	}
+}
+
+static void foo_axis(void *data,
+			 struct wl_pointer *wl_pointer, uint32_t time,
+			 uint32_t axis, wl_fixed_t value)
+{
+}
+
+static const struct wl_pointer_listener pointer_listener = {
+	.enter = foo_enter,
+	.leave = foo_leave,
+	.motion = foo_motion,
+	.button = foo_button,
+	.axis = foo_axis
+};
diff --git a/tools/waycheck/waycheck.dox b/tools/waycheck/waycheck.dox
new file mode 100644
index 0000000..66ae65b
--- /dev/null
+++ b/tools/waycheck/waycheck.dox
@@ -0,0 +1,29 @@
+/*
+ * Copyright © 2015 Samsung Electronics Co., Ltd
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of the copyright holders not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.  The copyright holders make
+ * no representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+ * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/**
+ at page waycheck
+
+A simple integration/acceptance tool to exercise Wayland compositors.
+
+Tests use @ref zunitc for their infrastructure, and most include use of a common wtst_ctx test fixture.
+*/
\ No newline at end of file
diff --git a/tools/wayland_fixtures/inc/wtst_fixtures.h b/tools/wayland_fixtures/inc/wtst_fixtures.h
new file mode 100644
index 0000000..bf95603
--- /dev/null
+++ b/tools/wayland_fixtures/inc/wtst_fixtures.h
@@ -0,0 +1,230 @@
+/*
+ * Copyright © 2015 Samsung Electronics Co., Ltd
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of the copyright holders not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.  The copyright holders make
+ * no representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+ * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef WTST_FIXTURES_H
+#define WTST_FIXTURES_H
+
+/**
+ * @file
+ * Common helpers for Wayland tests.
+ */
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <wayland-client.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Test fixture that holds the basics for simple Wayland client testing.
+ *
+ * @see wtst_ctx_create()
+ * @see wtst_ctx_destroy()
+ */
+struct wtst_ctx
+{
+	/** The display the client is connected to. */
+	struct wl_display *display;
+
+	/** The registry the client is connected to. */
+	struct wl_registry *registry;
+
+	/** Main compositor as advertised and updated by the registry. */
+	struct wl_compositor *compositor;
+
+	/**
+	 * Main shared memory object as advertised and updated by the
+	 * registry.
+	 */
+	struct wl_shm *shm;
+
+// -------------------------------------
+
+	/** The seat that is actually used for input events. */
+	struct wtst_input *input;
+
+	/**
+	 * Server can have more wl_seats. We need keep them all until we
+	 * find the one that we need. After that, the others
+	 * will be destroyed, so this list will have the length of 1.
+	 * If some day in the future we will need the other seats,
+	 * we can just keep them here.
+	 */
+	struct wl_list inputs;
+
+	struct wtst_output *output;
+
+	struct wtst_surface *surface;
+
+	bool has_argb;
+
+	bool has_wl_drm;
+
+// -------------------------------------
+
+	/** Main shell as advertised and updated by the registry. */
+	struct wl_shell *shell;
+
+	/** Listener registered with the registry. */
+	struct wl_registry_listener *reg_listener;
+
+	/** Context for internal implementation details. */
+	struct wtst_ctx_private *private;
+};
+
+struct wtst_global {
+	uint32_t name;
+	char *interface;
+	uint32_t version;
+	struct wl_list link;
+};
+
+struct wtst_input {
+	struct wl_seat *wl_seat;
+	struct wtst_pointer *pointer;
+	struct wtst_keyboard *keyboard;
+	struct wtst_touch *touch;
+	char *seat_name;
+	enum wl_seat_capability caps;
+	struct wl_list link;
+};
+
+struct wtst_pointer {
+	struct wl_pointer *wl_pointer;
+	struct wtst_surface *focus;
+	int x;
+	int y;
+	uint32_t button;
+	uint32_t state;
+	struct wtst_pointer_private *private;
+};
+
+struct wtst_keyboard {
+	struct wl_keyboard *wl_keyboard;
+	struct wtst_surface *focus;
+	uint32_t key;
+	uint32_t state;
+	uint32_t mods_depressed;
+	uint32_t mods_latched;
+	uint32_t mods_locked;
+	uint32_t group;
+	struct {
+		int rate;
+		int delay;
+	} repeat_info;
+};
+
+struct wtst_touch {
+	struct wl_touch *wl_touch;
+	int down_x;
+	int down_y;
+	int x;
+	int y;
+	int id;
+	int up_id; /* id of last wl_touch.up event */
+	int frame_no;
+	int cancel_no;
+};
+
+struct wtst_output {
+	struct wl_output *wl_output;
+	int x;
+	int y;
+	int width;
+	int height;
+	int scale;
+	bool initialized;
+};
+
+struct wtst_surface {
+	struct wl_surface *wl_surface;
+	struct wl_buffer *wl_buffer;
+	struct wtst_output *output;
+	int x;
+	int y;
+	int width;
+	int height;
+	void *data;
+};
+
+struct wtst_shell_surface {
+	struct wl_shell_surface *wl_shell_surface;
+	struct wtst_surface *surface; /**< surface that this is wrapping. */
+};
+
+enum wtst_dbgevents_pointer {
+	WTST_DBG_POINTER_NONE = 0x00,
+	WTST_DBG_POINTER_ENTER = 0x01,
+	WTST_DBG_POINTER_LEAVE = 0x02,
+	WTST_DBG_POINTER_MOTION = 0x04,
+	WTST_DBG_POINTER_BUTTON = 0x08,
+	WTST_DBG_POINTER_AXIS = 0x10,
+	WTST_DBG_POINTER_ALL = 0x1f
+};
+
+/**
+ * Creates an instance of the test fixture.
+ *
+ * @return a pointer to a test fixture upon success, NULL otherwise.
+ * @see wtst_ctx_destroy()
+ */
+struct wtst_ctx *wtst_ctx_create(void);
+
+/**
+ * Destroys a test fixture.
+ *
+ * @param ctx pointer to a test fixture to destroy.
+ * It should have previously been created via wtst_ctx_create().
+ * @see wtst_ctx_create()
+ */
+void wtst_ctx_destroy(struct wtst_ctx *ctx);
+
+/**
+ * Determines of an interface has been advertized by the Wayland registry
+ * as being present.
+ *
+ * @param ctx the test fixture in use.
+ * @param interface the name of the interface to query.
+ * @return true if the interface is supported, false otherwise.
+ */
+int wtst_is_global_advertised(struct wtst_ctx *ctx, char const *interface);
+
+int wtst_pointer_add_listener(struct wtst_pointer *pointer,
+			      const struct wl_pointer_listener *listener,
+			      void *data);
+
+void wtst_set_dump_pointer_events(enum wtst_dbgevents_pointer mask);
+
+/**
+ * Creates a wl_shell_surface and packages it in a wtst_shell_surface wrapper.
+ */
+struct wtst_shell_surface *wtst_create_shell_surface(
+	struct wl_compositor *compositor, struct wl_shell *shell);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* WTST_FIXTURES_H */
diff --git a/tools/wayland_fixtures/src/wtst_fixtures.c b/tools/wayland_fixtures/src/wtst_fixtures.c
new file mode 100644
index 0000000..a1dbade
--- /dev/null
+++ b/tools/wayland_fixtures/src/wtst_fixtures.c
@@ -0,0 +1,852 @@
+/*
+ * Copyright © 2015 Samsung Electronics Co., Ltd
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of the copyright holders not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.  The copyright holders make
+ * no representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+ * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "wtst_fixtures.h"
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "shared/zalloc.h"
+#include "zunitc/zunitc.h"
+
+struct wtst_ctx_private {
+
+	/** List of all known global advertised by the registry. */
+	struct wl_list global_list;
+
+	/** Current main compositor name advertised by the registry. */
+	uint32_t compositor_name;
+
+	/** Current shared memory object name advertised by the registry. */
+	uint32_t shm_name;
+
+	/** Current shell name advertised by the registry. */
+	uint32_t shell_name;
+};
+
+/**
+ * Find the test-seat and set it in client while destroying other inputs.
+ * @todo probably need to defer input selection to higher layers and not
+ * hardcode to "test-seat" here.
+ */
+static void wtst_ctx_set_input(struct wtst_ctx *ctx);
+
+static void input_update_devices(struct wtst_input *input);
+
+static int logle = 0;
+
+static enum wtst_dbgevents_pointer g_pointer_dbg_mask = WTST_DBG_POINTER_ALL;
+
+void wtst_set_dump_pointer_events(enum wtst_dbgevents_pointer mask)
+{
+	g_pointer_dbg_mask = mask;
+}
+
+static void input_destroy(struct wtst_input *inp)
+{
+	wl_list_remove(&inp->link);
+	wl_seat_destroy(inp->wl_seat);
+	free(inp);
+}
+
+struct wtst_pointer_private
+{
+	void *data;
+	const struct wl_pointer_listener *listener;
+	struct wl_list link;
+};
+
+static void output_handle_geometry(void *data,
+				   struct wl_output *wl_output,
+				   int x, int y,
+				   int physical_width, int physical_height,
+				   int subpixel,
+				   const char *make, const char *model,
+				   int32_t transform)
+{
+	struct wtst_output *output = data;
+
+	output->x = x;
+	output->y = y;
+}
+
+static void output_handle_mode(void *data,
+			       struct wl_output *wl_output,
+			       uint32_t flags,
+			       int width, int height,
+			       int refresh)
+{
+	struct wtst_output *output = data;
+
+	if (flags & WL_OUTPUT_MODE_CURRENT) {
+		output->width = width;
+		output->height = height;
+	}
+}
+
+static void output_handle_scale(void *data,
+				struct wl_output *wl_output,
+				int scale)
+{
+	struct wtst_output *output = data;
+
+	output->scale = scale;
+}
+
+static void output_handle_done(void *data,
+			       struct wl_output *wl_output)
+{
+	struct wtst_output *output = data;
+
+	output->initialized = true;
+}
+
+static void seat_handle_capabilities(void *data, struct wl_seat *seat,
+				     enum wl_seat_capability caps)
+{
+	struct wtst_input *input = data;
+
+	input->caps = caps;
+
+	/* we will create/update the devices only with the right (test) seat.
+	 * If we haven't discovered which seat is the test seat, just
+	 * store capabilities and bail out */
+	if(input->seat_name && strcmp(input->seat_name, "test-seat") == 0)
+		input_update_devices(input);
+
+	fprintf(stderr, "test-client: got seat %p capabilities: %x\n",
+		input, caps);
+}
+
+static void seat_handle_name(void *data, struct wl_seat *seat, const char *name)
+{
+	struct wtst_input *input = data;
+
+	input->seat_name = strdup(name);
+	ZUC_ASSERT_TRUE(input->seat_name != NULL);
+
+	fprintf(stderr, "test-client: got seat %p name: \'%s\'\n",
+		input, name);
+}
+
+static void shell_surface_ping(void *data,
+			       struct wl_shell_surface *shell_surface,
+			       uint32_t serial)
+{
+    wl_shell_surface_pong(shell_surface, serial);
+}
+
+static void shell_surface_configure(void *data,
+				    struct wl_shell_surface *shell_surface,
+				    uint32_t edges,
+				    int32_t width, int32_t height)
+{
+}
+
+static void pointer_handle_enter(void *data, struct wl_pointer *wl_pointer,
+				 uint32_t serial, struct wl_surface *wl_surface,
+				 wl_fixed_t x, wl_fixed_t y)
+{
+	struct wtst_pointer *pointer = data;
+	struct wtst_pointer_private *entry = NULL;
+	struct wtst_pointer_private *tmp = NULL;
+
+	pointer->focus = wl_surface_get_user_data(wl_surface);
+	pointer->x = wl_fixed_to_int(x);
+	pointer->y = wl_fixed_to_int(y);
+
+	if (g_pointer_dbg_mask & WTST_DBG_POINTER_ENTER)
+		fprintf(stderr, "test-client: "
+			"got pointer enter %d %d, surface %p\n",
+			pointer->x, pointer->y, pointer->focus);
+
+	wl_list_for_each_safe(entry, tmp, &pointer->private->link, link)
+		if (entry->listener && entry->listener->enter)
+			entry->listener->enter(entry->data, wl_pointer,
+					       serial, wl_surface, x, y);
+}
+
+static void pointer_handle_leave(void *data, struct wl_pointer *wl_pointer,
+				 uint32_t serial, struct wl_surface *wl_surface)
+{
+	struct wtst_pointer *pointer = data;
+	struct wtst_pointer_private *entry = NULL;
+	struct wtst_pointer_private *tmp = NULL;
+
+	pointer->focus = NULL;
+
+	if (g_pointer_dbg_mask & WTST_DBG_POINTER_LEAVE)
+		fprintf(stderr, "test-client: "
+			"got pointer leave, surface %p\n",
+			wl_surface_get_user_data(wl_surface));
+
+	wl_list_for_each_safe(entry, tmp, &pointer->private->link, link)
+		if (entry->listener && entry->listener->leave)
+			entry->listener->leave(entry->data, wl_pointer,
+					       serial, wl_surface);
+}
+
+static void pointer_handle_motion(void *data, struct wl_pointer *wl_pointer,
+				  uint32_t time, wl_fixed_t x, wl_fixed_t y)
+{
+	struct wtst_pointer *pointer = data;
+	struct wtst_pointer_private *entry = NULL;
+	struct wtst_pointer_private *tmp = NULL;
+
+	pointer->x = wl_fixed_to_int(x);
+	pointer->y = wl_fixed_to_int(y);
+
+	if (g_pointer_dbg_mask & WTST_DBG_POINTER_MOTION)
+		fprintf(stderr, "test-client: "
+			"got pointer motion %d %d\n",
+			pointer->x, pointer->y);
+
+	wl_list_for_each_safe(entry, tmp, &pointer->private->link, link)
+		if (entry->listener && entry->listener->motion)
+			entry->listener->motion(entry->data, wl_pointer,
+			   time, x, y);
+}
+
+static void pointer_handle_button(void *data, struct wl_pointer *wl_pointer,
+				  uint32_t serial, uint32_t time,
+				  uint32_t button, uint32_t state)
+{
+	struct wtst_pointer *pointer = data;
+	struct wtst_pointer_private *entry = NULL;
+	struct wtst_pointer_private *tmp = NULL;
+
+	pointer->button = button;
+	pointer->state = state;
+
+	if (g_pointer_dbg_mask & WTST_DBG_POINTER_BUTTON)
+		fprintf(stderr, "test-client: "
+			"got pointer button %u %u\n",
+			button, state);
+
+	wl_list_for_each_safe(entry, tmp, &pointer->private->link, link)
+		if (entry->listener && entry->listener->button)
+			entry->listener->button(entry->data, wl_pointer,
+						serial, time, button, state);
+}
+
+static void pointer_handle_axis(void *data, struct wl_pointer *wl_pointer,
+				uint32_t time, uint32_t axis, wl_fixed_t value)
+{
+	struct wtst_pointer *pointer = data;
+	struct wtst_pointer_private *entry = NULL;
+	struct wtst_pointer_private *tmp = NULL;
+
+	fprintf(stderr, "test-client: got pointer axis %u %f\n",
+		axis, wl_fixed_to_double(value));
+
+	wl_list_for_each_safe(entry, tmp, &pointer->private->link, link)
+		if (entry->listener && entry->listener->axis)
+			entry->listener->axis(entry->data, wl_pointer,
+					      time, axis, value);
+}
+
+static void keyboard_handle_keymap(void *data, struct wl_keyboard *wl_keyboard,
+				   uint32_t format, int fd, uint32_t size)
+{
+	close(fd);
+
+	fprintf(stderr, "test-client: got keyboard keymap\n");
+}
+
+static void keyboard_handle_enter(void *data, struct wl_keyboard *wl_keyboard,
+				  uint32_t serial,
+				  struct wl_surface *wl_surface,
+				  struct wl_array *keys)
+{
+	struct wtst_keyboard *keyboard = data;
+
+	keyboard->focus = wl_surface_get_user_data(wl_surface);
+
+	fprintf(stderr, "test-client: got keyboard enter, surface %p\n",
+		keyboard->focus);
+}
+
+static void keyboard_handle_leave(void *data, struct wl_keyboard *wl_keyboard,
+				  uint32_t serial,
+				  struct wl_surface *wl_surface)
+{
+	struct wtst_keyboard *keyboard = data;
+
+	keyboard->focus = NULL;
+
+	fprintf(stderr, "test-client: got keyboard leave, surface %p\n",
+		wl_surface_get_user_data(wl_surface));
+}
+
+static void keyboard_handle_key(void *data, struct wl_keyboard *wl_keyboard,
+				uint32_t serial, uint32_t time, uint32_t key,
+				uint32_t state)
+{
+	struct wtst_keyboard *keyboard = data;
+
+	keyboard->key = key;
+	keyboard->state = state;
+
+	fprintf(stderr, "test-client: got keyboard key %u %u\n", key, state);
+}
+
+static void keyboard_handle_modifiers(void *data,
+				      struct wl_keyboard *wl_keyboard,
+				      uint32_t serial, uint32_t mods_depressed,
+				      uint32_t mods_latched,
+				      uint32_t mods_locked,
+				      uint32_t group)
+{
+	struct wtst_keyboard *keyboard = data;
+
+	keyboard->mods_depressed = mods_depressed;
+	keyboard->mods_latched = mods_latched;
+	keyboard->mods_locked = mods_locked;
+	keyboard->group = group;
+
+	fprintf(stderr, "test-client: got keyboard modifiers %u %u %u %u\n",
+		mods_depressed, mods_latched, mods_locked, group);
+}
+
+static void keyboard_handle_repeat_info(void *data,
+					struct wl_keyboard *wl_keyboard,
+					int32_t rate, int32_t delay)
+{
+	struct wtst_keyboard *keyboard = data;
+
+	keyboard->repeat_info.rate = rate;
+	keyboard->repeat_info.delay = delay;
+
+	fprintf(stderr, "test-client: got keyboard repeat_info %d %d\n",
+		rate, delay);
+}
+
+static void touch_handle_down(void *data, struct wl_touch *wl_touch,
+			      uint32_t serial, uint32_t time,
+			      struct wl_surface *surface,
+			      int32_t id, wl_fixed_t x_w, wl_fixed_t y_w)
+{
+	struct wtst_touch *touch = data;
+
+	touch->down_x = wl_fixed_to_int(x_w);
+	touch->down_y = wl_fixed_to_int(y_w);
+	touch->id = id;
+
+	fprintf(stderr, "test-client: got touch down %d %d, surf: %p, id: %d\n",
+		touch->down_x, touch->down_y, surface, id);
+}
+
+static void touch_handle_up(void *data, struct wl_touch *wl_touch,
+			    uint32_t serial, uint32_t time, int32_t id)
+{
+	struct wtst_touch *touch = data;
+	touch->up_id = id;
+
+	fprintf(stderr, "test-client: got touch up, id: %d\n", id);
+}
+
+static void touch_handle_motion(void *data, struct wl_touch *wl_touch,
+				uint32_t time, int32_t id,
+				wl_fixed_t x_w, wl_fixed_t y_w)
+{
+	struct wtst_touch *touch = data;
+	touch->x = wl_fixed_to_int(x_w);
+	touch->y = wl_fixed_to_int(y_w);
+
+	fprintf(stderr, "test-client: got touch motion, %d %d, id: %d\n",
+		touch->x, touch->y, id);
+}
+
+static void touch_handle_frame(void *data, struct wl_touch *wl_touch)
+{
+	struct wtst_touch *touch = data;
+
+	++touch->frame_no;
+
+	fprintf(stderr, "test-client: got touch frame (%d)\n", touch->frame_no);
+}
+
+static void touch_handle_cancel(void *data, struct wl_touch *wl_touch)
+{
+	struct wtst_touch *touch = data;
+
+	++touch->cancel_no;
+
+	fprintf(stderr, "test-client: got touch cancel (%d)\n",
+		touch->cancel_no);
+}
+
+static void surface_handle_enter(void *data, struct wl_surface *wl_surface,
+				 struct wl_output *output)
+{
+	struct wtst_surface *surface = data;
+
+	surface->output = wl_output_get_user_data(output);
+
+	fprintf(stderr, "test-client: got surface enter output %p\n",
+		surface->output);
+}
+
+static void surface_handle_leave(void *data, struct wl_surface *wl_surface,
+				 struct wl_output *output)
+{
+	struct wtst_surface *surface = data;
+
+	surface->output = NULL;
+
+	fprintf(stderr, "test-client: got surface leave output %p\n",
+		wl_output_get_user_data(output));
+}
+
+static const struct wl_shell_surface_listener
+shell_surface_listener = {
+	.ping = shell_surface_ping,
+	.configure = shell_surface_configure,
+};
+
+static const struct wl_output_listener output_listener = {
+	output_handle_geometry,
+	output_handle_mode,
+	output_handle_done,
+	output_handle_scale,
+};
+
+static const struct wl_seat_listener seat_listener = {
+	seat_handle_capabilities,
+	seat_handle_name,
+};
+
+static const struct wl_pointer_listener pointer_listener = {
+	pointer_handle_enter,
+	pointer_handle_leave,
+	pointer_handle_motion,
+	pointer_handle_button,
+	pointer_handle_axis,
+};
+
+static const struct wl_keyboard_listener keyboard_listener = {
+	keyboard_handle_keymap,
+	keyboard_handle_enter,
+	keyboard_handle_leave,
+	keyboard_handle_key,
+	keyboard_handle_modifiers,
+	keyboard_handle_repeat_info
+};
+
+static const struct wl_touch_listener touch_listener = {
+	touch_handle_down,
+	touch_handle_up,
+	touch_handle_motion,
+	touch_handle_frame,
+	touch_handle_cancel,
+};
+
+static const struct wl_surface_listener surface_listener = {
+	surface_handle_enter,
+	surface_handle_leave
+};
+
+static void add_global_advert(struct wtst_ctx *ctx,
+			      uint32_t name,
+			      const char *interface,
+			      uint32_t version)
+{
+	if (!ctx || !ctx->private)
+		return;
+
+	/* See if we've got this interface already */
+	struct wtst_global *curr = NULL;
+	struct wtst_global *tmp = NULL;
+	wl_list_for_each(tmp, &ctx->private->global_list, link)
+		if (strcmp(tmp->interface, interface) == 0) {
+			curr = tmp;
+			break;
+		}
+
+	if (curr) {
+		/* These are not supposed to change. */
+		ZUC_EXPECT_EQ(curr->name, name);
+		ZUC_EXPECT_EQ(curr->version, version);
+	} else {
+		struct wtst_global *glbl = zalloc(sizeof(*glbl));
+		ZUC_ASSERT_TRUE(glbl != NULL);
+		glbl->name = name;
+		glbl->interface = strdup(interface);
+		glbl->version = version;
+		wl_list_insert(&ctx->private->global_list, &glbl->link);
+	}
+}
+
+static void reg_global(void *data,
+		       struct wl_registry *registry,
+		       uint32_t name,
+		       const char *interface,
+		       uint32_t version)
+{
+	struct wtst_ctx *ctx = (struct wtst_ctx *)data;
+	struct wtst_input *input = NULL;
+
+	if (logle > 1) {
+		printf("reg_global ++: %2d   %s  v%d\n",
+		       name, interface, version);
+	}
+
+	ZUC_EXPECT_EQ(0, strcmp(wl_compositor_interface.name,
+				"wl_compositor"));
+
+	add_global_advert(ctx, name, interface, version);
+
+	if (strcmp(interface, wl_compositor_interface.name) == 0) {
+		ctx->compositor = wl_registry_bind(registry, name,
+						   &wl_compositor_interface,
+						   version);
+		ctx->private->compositor_name = name;
+	} else if (strcmp(interface, wl_seat_interface.name) == 0) {
+		input = zalloc(sizeof(*input));
+		ZUC_ASSERT_TRUE(input != NULL);
+		input->wl_seat = wl_registry_bind(registry, name,
+						  &wl_seat_interface, version);
+		wl_seat_add_listener(input->wl_seat, &seat_listener, input);
+		wl_list_insert(&ctx->inputs, &input->link);
+	} else if (strcmp(interface, wl_shm_interface.name) == 0) {
+		ZUC_EXPECT_TRUE(ctx->shm == NULL);
+		if (ctx->shm)
+			wl_shm_destroy(ctx->shm);
+		ctx->shm = wl_registry_bind(registry, name,
+					    &wl_shm_interface, version);
+		ctx->private->shm_name = name;
+	} else if (strcmp(interface, wl_output_interface.name) == 0) {
+		struct wtst_output *output = zalloc(sizeof(*output));
+		ZUC_ASSERT_TRUE(output != NULL);
+		output->wl_output = wl_registry_bind(registry, name,
+						     &wl_output_interface,
+						     version);
+		wl_output_add_listener(output->wl_output,
+				       &output_listener, output);
+		ctx->output = output;
+	} else if (strcmp(interface, wl_shell_interface.name) == 0) {
+		ZUC_EXPECT_TRUE(ctx->shell == NULL);
+		if (ctx->shell)
+			wl_shell_destroy(ctx->shell);
+		ctx->shell = wl_registry_bind(registry, name,
+					      &wl_shell_interface, version);
+		ctx->private->shell_name = name;
+	} else if (strcmp(interface, "wl_drm") == 0) {
+		ctx->has_wl_drm = true;
+	}
+}
+
+static void reg_global_remove(void *data,
+			      struct wl_registry *registry,
+			      uint32_t name)
+{
+	struct wtst_ctx *ctx = (struct wtst_ctx *)data;
+	if (logle > 1)
+		printf("reg_global --: %2d\n", name);
+	if (name == ctx->private->compositor_name) {
+		if (ctx->compositor)
+			wl_compositor_destroy(ctx->compositor);
+		ctx->compositor = NULL;
+		ctx->private->compositor_name = 0;
+	} else if (name == ctx->private->shm_name) {
+		if (ctx->shm)
+			wl_shm_destroy(ctx->shm);
+		ctx->shm = NULL;
+		ctx->private->shm_name = 0;
+	} else if (name == ctx->private->shell_name) {
+		if (ctx->shell)
+			wl_shell_destroy(ctx->shell);
+		ctx->shell = NULL;
+		ctx->private->shell_name = 0;
+	}
+}
+
+static struct wtst_ctx_private *wtst_ctx_private_create(void)
+{
+	struct wtst_ctx_private *private =
+		zalloc(sizeof(struct wtst_ctx_private));
+	ZUC_EXPECT_TRUE(private != NULL);
+	if (private) {
+		wl_list_init(&private->global_list);
+	}
+
+	return private;
+}
+
+static void wtst_ctx_private_destroy(struct wtst_ctx_private *private)
+{
+	if (!private)
+		return;
+
+	if (!wl_list_empty(&private->global_list)) {
+		struct wtst_global *entry = NULL;
+		struct wtst_global *tmp = NULL;
+		wl_list_for_each_reverse_safe(entry, tmp,
+					      &private->global_list, link) {
+			wl_list_remove(&entry->link);
+			free(entry);
+		}
+	}
+
+	free(private);
+}
+
+struct wtst_ctx *wtst_ctx_create(void)
+{
+	struct wtst_ctx *ctx = zalloc(sizeof(*ctx));
+	if (!ctx) {
+		ZUC_FAIL("Unable to allocate context.");
+	} else {
+		wl_list_init(&ctx->inputs);
+		ctx->reg_listener = zalloc(sizeof(*(ctx->reg_listener)));
+		ZUC_EXPECT_TRUE(ctx->reg_listener != NULL);
+
+		/* connect to display. */
+		ctx->display = wl_display_connect(NULL);
+		if (!ctx->display) {
+			ZUC_FAIL("Unable to connect to display");
+			free(ctx->reg_listener);
+			free(ctx);
+			ctx = NULL;
+		} else {
+			ctx->private = wtst_ctx_private_create();
+
+			/* setup registry so we can bind to interfaces. */
+			ctx->registry = wl_display_get_registry(ctx->display);
+			ctx->reg_listener->global = reg_global;
+			ctx->reg_listener->global_remove = reg_global_remove;
+			wl_registry_add_listener(ctx->registry,
+						 ctx->reg_listener,
+						 ctx);
+
+			/* this roundtrip makes sure we have all globals
+			 * and we have bound to them: */
+			wl_display_roundtrip(ctx->display);
+
+			/* this roundtrip makes sure we got all wl_shm
+			 * format and wl_seat.* events: */
+			wl_display_roundtrip(ctx->display);
+
+			/* find the right input for us */
+			wtst_ctx_set_input(ctx);
+
+			/* must have an output */
+			ZUC_EXPECT_TRUE(ctx->output != NULL);
+
+			/* the output must be initialized */
+			if (ctx->output)
+				ZUC_EXPECT_TRUE(ctx->output->initialized);
+
+			/* must have seat set */
+			ZUC_EXPECT_TRUE(ctx->input != NULL);
+		}
+	}
+
+	return ctx;
+}
+
+void wtst_ctx_destroy(struct wtst_ctx *ctx)
+{
+	if (ctx) {
+		if (ctx->registry)
+			wl_registry_destroy(ctx->registry);
+		if (ctx->reg_listener)
+			free(ctx->reg_listener);
+		if (ctx->shell)
+			wl_shell_destroy(ctx->shell);
+		if (ctx->shm)
+			wl_shm_destroy(ctx->shm);
+		if (ctx->compositor)
+			wl_compositor_destroy(ctx->compositor);
+		if (ctx->display)
+			wl_display_disconnect(ctx->display);
+		wtst_ctx_private_destroy(ctx->private);
+		free(ctx);
+	}
+}
+
+static struct wtst_pointer *wtst_pointer_create(void)
+{
+	struct wtst_pointer *pointer = zalloc(sizeof(*pointer));
+	ZUC_EXPECT_TRUE(pointer != NULL);
+	if (pointer) {
+		pointer->private = zalloc(sizeof(struct wtst_pointer_private));
+		ZUC_EXPECT_TRUE(pointer->private != NULL);
+		wl_list_init(&pointer->private->link);
+	}
+	return pointer;
+}
+
+int wtst_pointer_add_listener(struct wtst_pointer *pointer,
+			      const struct wl_pointer_listener *listener,
+			      void *data)
+{
+	struct wtst_pointer_private *priv =
+		zalloc(sizeof(struct wtst_pointer_private));
+	ZUC_EXPECT_TRUE(priv != NULL);
+	priv->data = data;
+	priv->listener = listener;
+	wl_list_insert(&pointer->private->link, &priv->link);
+	return 0;
+}
+
+void input_update_devices(struct wtst_input *input)
+{
+	struct wl_seat *seat = input->wl_seat;
+	enum wl_seat_capability caps = input->caps;
+
+	if ((caps & WL_SEAT_CAPABILITY_POINTER) && !input->pointer) {
+		struct wtst_pointer *pointer = wtst_pointer_create();
+		pointer->wl_pointer = wl_seat_get_pointer(seat);
+		wl_pointer_set_user_data(pointer->wl_pointer, pointer);
+		wl_pointer_add_listener(pointer->wl_pointer, &pointer_listener,
+					pointer);
+		input->pointer = pointer;
+	} else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && input->pointer) {
+		wl_pointer_destroy(input->pointer->wl_pointer);
+		free(input->pointer);
+		input->pointer = NULL;
+	}
+
+	if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !input->keyboard) {
+		struct wtst_keyboard *keyboard = zalloc(sizeof(*keyboard));
+		ZUC_ASSERT_TRUE(keyboard != NULL);
+		keyboard->wl_keyboard = wl_seat_get_keyboard(seat);
+		wl_keyboard_set_user_data(keyboard->wl_keyboard, keyboard);
+		wl_keyboard_add_listener(keyboard->wl_keyboard,
+					 &keyboard_listener, keyboard);
+		input->keyboard = keyboard;
+	} else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && input->keyboard) {
+		wl_keyboard_destroy(input->keyboard->wl_keyboard);
+		free(input->keyboard);
+		input->keyboard = NULL;
+	}
+
+	if ((caps & WL_SEAT_CAPABILITY_TOUCH) && !input->touch) {
+		struct wtst_touch *touch = zalloc(sizeof(*touch));
+		ZUC_ASSERT_TRUE(touch != NULL);
+		touch->wl_touch = wl_seat_get_touch(seat);
+		wl_touch_set_user_data(touch->wl_touch, touch);
+		wl_touch_add_listener(touch->wl_touch, &touch_listener,
+					 touch);
+		input->touch = touch;
+	} else if (!(caps & WL_SEAT_CAPABILITY_TOUCH) && input->touch) {
+		wl_touch_destroy(input->touch->wl_touch);
+		free(input->touch);
+		input->touch = NULL;
+	}
+}
+
+void wtst_ctx_set_input(struct wtst_ctx *ctx)
+{
+	/* For now prune all but first. Later support looking for named seats */
+	struct wtst_input *inp, *inptmp;
+	wl_list_for_each_safe(inp, inptmp, &ctx->inputs, link) {
+		ZUC_ASSERT_TRUE(inp->seat_name && "BUG: input with no name");
+		if (!ctx->input) {
+			ctx->input = inp;
+			input_update_devices(inp);
+		} else {
+			input_destroy(inp);
+		}
+	}
+
+	/* we keep only one input */
+	ZUC_ASSERT_EQ(1, wl_list_length(&ctx->inputs));
+}
+
+int wtst_is_global_advertised(struct wtst_ctx *ctx, char const *interface)
+{
+	int found = 0;
+	if (ctx && ctx->private && interface) {
+		struct wtst_global *tmp = NULL;
+		wl_list_for_each(tmp, &ctx->private->global_list, link)
+			if (strcmp(interface, tmp->interface) == 0) {
+				found = true;
+				break;
+			}
+	}
+	return found;
+}
+
+static struct wtst_surface *wrap_wl_surface(struct wl_surface *wl_surface)
+{
+	struct wtst_surface *wsurf = NULL;
+	ZUC_EXPECT_TRUE(wl_surface != NULL);
+	if (wl_surface) {
+		wsurf = zalloc(sizeof(*wsurf));
+		ZUC_EXPECT_TRUE(wsurf != NULL);
+		wl_surface_set_user_data(wl_surface, wsurf);
+		if (wsurf) {
+			wsurf->wl_surface = wl_surface;
+			wl_surface_add_listener(wl_surface, &surface_listener,
+						wsurf);
+		}
+	}
+	return wsurf;
+}
+
+struct wtst_shell_surface *wtst_create_shell_surface(
+	struct wl_compositor *compositor,
+	struct wl_shell *shell)
+{
+	struct wtst_shell_surface *wss =
+		zalloc(sizeof(struct wtst_shell_surface));
+	ZUC_EXPECT_TRUE(wss != NULL);
+	if (wss) {
+		struct wl_surface *wl_surface =
+			wl_compositor_create_surface(compositor);
+		ZUC_EXPECT_TRUE(wl_surface != NULL);
+		wss->surface = wrap_wl_surface(wl_surface);
+	}
+
+	if (wss && wss->surface) {
+		wss->wl_shell_surface =
+			wl_shell_get_shell_surface(shell,
+						   wss->surface->wl_surface);
+		ZUC_EXPECT_TRUE(wss->wl_shell_surface != NULL);
+		if (wss->wl_shell_surface) {
+			wl_shell_surface_add_listener(wss->wl_shell_surface,
+						      &shell_surface_listener,
+						      wss);
+			wl_shell_surface_set_toplevel(wss->wl_shell_surface);
+			wl_shell_surface_set_user_data(wss->wl_shell_surface,
+						       wss);
+		}
+	}
+
+	if (wss && (!wss->surface || !wss->wl_shell_surface)) {
+		if (wss->surface) {
+			struct wl_surface *wl_surface =
+				wss->surface->wl_surface;
+			free(wss->surface);
+			wl_surface_destroy(wl_surface);
+		}
+		free(wss);
+		wss = NULL;
+	}
+
+	return wss;
+}
diff --git a/tools/zunitc/doc/zunitc.dox b/tools/zunitc/doc/zunitc.dox
new file mode 100644
index 0000000..4595d7d
--- /dev/null
+++ b/tools/zunitc/doc/zunitc.dox
@@ -0,0 +1,138 @@
+/*
+ * Copyright © 2015 Samsung Electronics Co., Ltd
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of the copyright holders not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.  The copyright holders make
+ * no representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+ * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/**
+ at page zunitc zunitc
+
+- @ref zunitc_overview
+- @ref zunitc_execution
+  - @ref zunitc_execution_commandline
+  - @ref zunitc_execution_matching
+  - @ref zunitc_execution_wildcards
+  - @ref zunitc_execution_repeat
+  - @ref zunitc_execution_randomize
+- @ref zunitc_fixtures
+- @ref zunitc_functions
+
+ at section zunitc_overview Overview
+
+A simple test framework in plain C suitable for basic unit and integration testing.
+
+The main rationale for creating this framework was to have a simple to use testing
+framework with tests implemented in C using common patterns and under a
+compatible license. The structure of the test code and macro use is intended to
+follow common patterns established by frameworks such as Boost Test and Google Test.
+
+
+To get started, one or more tests should be defined via ZUC_TEST() and/or
+ZUC_TEST_F(), which set up automatic test registration via gcc extensions.
+To actually execute tests, ZUC_RUN_TESTS() should be called.
+
+
+Tests can use several ZUC_EXPECT_* or ZUC_ASSERT_* checks to validate
+conditions. The ZUC_EXPECT_* ones upon failure will mark the current test
+as failing, but allow execution to continue. On the other hand, the
+ZUC_ASSERT_* tests will mark the current test as failed and then immediately
+terminate the test.
+
+The set of non-fatal test checks are
+
+- ZUC_EXPECT_TRUE()
+- ZUC_EXPECT_FALSE()
+- ZUC_EXPECT_EQ()
+- ZUC_EXPECT_NE()
+- ZUC_EXPECT_LT()
+- ZUC_EXPECT_LE()
+- ZUC_EXPECT_GT()
+- ZUC_EXPECT_GE()
+
+The set of fatal test checks are
+
+- ZUC_ASSERT_TRUE()
+- ZUC_ASSERT_FALSE()
+- ZUC_ASSERT_EQ()
+- ZUC_ASSERT_NE()
+- ZUC_ASSERT_LT()
+- ZUC_ASSERT_LE()
+- ZUC_ASSERT_GT()
+- ZUC_ASSERT_GE()
+
+Unconditional test values for logging and termination are
+- ZUC_SKIP()
+- ZUC_FAIL()
+- ZUC_FATAL()
+
+Unconditional message logging for failure cases only is
+- ZUC_TRACEPOINT()
+
+ at section zunitc_execution Controlling Execution
+
+To control execution, the various zuc_set_* functions can be called before invoking ZUC_RUN_TESTS(). 
+
+ at subsection zunitc_execution_commandline Commandline Parameters
+
+The current implementation defers processing of command-line parameters to the main application hosting the testing. It is possible that a helper to process certain parameters may be added.
+
+ at subsection zunitc_execution_matching Matching Patterns for Tests
+
+The function zuc_set_filter() can be used to specify a pattern for matching or excluding tests from a run. The general form is
+ match1[:match2[:match3..n]][:-exclude1[:exclude2[:exclude3..n]]]
+
+ at subsection zunitc_execution_wildcards Matching Wildcards
+
+Wildcards can be used in the match/exclude patterns and recognize the following two special characters:
+- '*' matches any number of characters including zero.
+- '?' matches any single character.
+
+Calling zuc_list_tests() after zuc_set_filter() can be done to show the effects of the matching without needing to actually run tests.
+
+ at subsection zunitc_execution_repeat Repeating Tests
+
+Setting the repeat count higher than 1 ( via zuc_set_repeat() ) will cause the tests to be executed several times in a row. This can be useful for stress testing, checking for leaks, etc.
+
+ at subsection zunitc_execution_randomize Randomizing Tests
+
+Test ordering can be randomized by setting a non-zero positive value to zuc_set_random(). Setting it to 1 will cause the framework to pick a random seed based on the time. A value greater than 1 will be taken as a random seed itself. And setting it to 0 will disable randomization and allow the test to be executed in their natural ordering.
+
+ at section zunitc_fixtures Fixtures
+
+Per-suite and per-test setup and teardown fixtures can be implemented by defining an instance of struct zuc_fixture and using it as the first parameter to ZUC_TEST_F().
+
+ at section zunitc_functions Functions
+
+- zuc_get_fixture_data()
+- ZUC_TEST()
+- ZUC_TEST_F()
+- ZUC_RUN_TESTS()
+- zuc_cleanup()
+- zuc_list_tests()
+- zuc_set_filter()
+- zuc_set_random()
+- zuc_set_spawn()
+- zuc_set_output_tap()
+- zuc_set_output_junit()
+- zuc_has_skip()
+- zuc_has_failure()
+- zuc_has_fatal_failure()
+- zuc_has_nonfatal_failure()
+
+*/
diff --git a/tools/zunitc/inc/zunitc/zunitc.h b/tools/zunitc/inc/zunitc/zunitc.h
new file mode 100644
index 0000000..869b46a
--- /dev/null
+++ b/tools/zunitc/inc/zunitc/zunitc.h
@@ -0,0 +1,537 @@
+/*
+ * Copyright © 2015 Samsung Electronics Co., Ltd
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of the copyright holders not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.  The copyright holders make
+ * no representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+ * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef Z_UNIT_C_H
+#define Z_UNIT_C_H
+
+#include <stdarg.h>
+#include <stdbool.h>
+
+#include "zunitc/zunitc_impl.h"
+
+/**
+ * @file
+ * Simple unit test framework declarations.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @page zunitc
+ */
+
+/**
+ * Structure to use when defining a test fixture.
+ * @note likely pending refactoring as use cases are refined.
+ */
+struct zuc_fixture {
+	/**
+	 * Initial optional seed data to pass to setup functions and/or tests.
+	 */
+	const void *data;
+
+	/**
+	 * Per-suite setup called before invoking any of the tests
+	 * contained in the suite.
+	 *
+	 * @return a pointer to test data, or NULL.
+	 */
+	void *(*set_up_test_case)(const void *data);
+
+	/**
+	 * Per-suite tear-down called after invoking all of the tests
+	 * contained in the suite.
+	 *
+	 * @param data pointer returned from the setup function.
+	 */
+	void (*tear_down_test_case)(void *data);
+
+	/**
+	 * Setup called before running each of the tests in the suite.
+	 *
+	 * @param data optional data from suite setup, or NULL.
+	 * @return a pointer to test data, or NULL.
+	 */
+	void *(*set_up)(void *data);
+
+	/**
+	 * Tear-down called after running each of the tests in the suite.
+	 *
+	 * @param data pointer returned from the setup function.
+	 */
+	void (*tear_down)(void *data);
+};
+
+/**
+ * Process exit code to mark skipped tests, consistent with
+ * automake tests.
+ */
+#define ZUC_EXIT_SKIP 77
+
+/**
+ * Initializes the test framework and consumes any known command-line
+ * parameters from the list.
+ * The exception is 'h/help' which will be left in place for follow-up
+ * processing by the hosting app if so desired.
+ *
+ * @param argc poitner to argc value to read and possibly change.
+ * @param argv array of parameter pointers to read and possibly change.
+ * @param help_flagged if non-NULL will be set to true if the user
+ * specifies the help flag (and framework help has been output).
+ * @return EXIT_SUCCESS upon success setting or help, EXIT_FAILURE otherwise.
+ */
+int zuc_initialize(int *argc, char *argv[], bool *help_flagged);
+
+/**
+ * Runs all tests that have been registered.
+ * Expected return values include EXIT_FAILURE if any errors or failures
+ * have occurred, ZUC_EXIT_SKIP if no failures have occurred but at least
+ * one test reported skipped, otherwise EXIT_SUCCESS if nothing of note
+ * was recorded.
+ *
+ * @return expected exit status - normally EXIT_SUCCESS, ZUC_EXIT_SKIP,
+ * or EXIT_FAILURE.
+ */
+#define ZUC_RUN_TESTS() \
+	zucimpl_run_tests()
+
+/**
+ * Clears the test system in preparation for application shutdown.
+ */
+void zuc_cleanup(void);
+
+/**
+ * Displays all known tests.
+ * The list returned is affected to any filtering in place.
+ *
+ * @see zuc_set_filter()
+ */
+void zuc_list_tests(void);
+
+#if !__GNUC__
+#error Framework currently requires gcc or compatible compiler.
+#endif
+
+
+/**
+ * Sets the filter string to use for tests.
+ * The format is a series of patterns separated by a colon, with wildcards
+ * and an optional flag for negative matching. For wildcards, the '*'
+ * character will match any sequence and the '?' character will match any
+ * single character.
+ * The '-' character at the start of a pattern marks the end of the
+ * patterns required to match and the begining of patterns that names
+ * must not match.
+ *
+ * @param filter the filter string to apply to tests.
+ */
+void zuc_set_filter(const char *filter);
+
+/**
+ * Trigger specific failure/signal upon test failures; useful when
+ * running under a debugger.
+ *
+ * @param break_on_failure true to cause a break when tests fail, false to
+ * allow normal operation upon failures.
+ */
+void zuc_set_break_on_failure(bool break_on_failure);
+
+/**
+ * Sets the number of times to repeat the tests.
+ * Any number higher than 1 will cause the tests to be repeated the
+ * specified number of times.
+ *
+ * @param repeat number of times to repeat the tests.
+ */
+void zuc_set_repeat(int repeat);
+
+/**
+ * Randomizes the order in which tests are executed.
+ * A value of 0 (the default) means tests are executed in their natural
+ * ordering. A value of 1 will pick a random seed based on the time to
+ * use for running tests in a pseudo-random order. A value greater than 1
+ * will be used directly for the initial seed.
+ *
+ * If the tests are also repeated, the seed will be incremented for each
+ * subsequent run.
+ *
+ * @param random 0|1|seed value.
+ * @see zuc_set_repeat()
+ */
+void zuc_set_random(int random);
+
+/**
+ * Controls whether or not to run the tests as forked child processes.
+ *
+ * @param spawn true to spawn each test in a forked child process,
+ * false to run tests directly.
+ */
+void zuc_set_spawn(bool spawn);
+
+/**
+ * Defines a test case that can be registered to run.
+ */
+#define ZUC_TEST(tcase, test) \
+	static void zuctest_##tcase##_##test(void); \
+	\
+	const struct zuc_registration zzz_##tcase##_##test \
+	__attribute__ ((section ("zuc_tsect"))) = \
+	{ \
+		#tcase, #test, 0,		\
+		zuctest_##tcase##_##test,	\
+		0				\
+	}; \
+	\
+	static void zuctest_##tcase##_##test(void)
+
+/**
+ * Defines a test case that can be registered to run along with setup/teardown
+ * support per-test and/or per test case.
+ *
+ * @note likely pending refactoring as use cases are refined.
+ */
+#define ZUC_TEST_F(tcase, test) \
+	static void zuctest_##tcase##_##test(void *data); \
+	\
+	const struct zuc_registration zzz_##tcase##_##test \
+	__attribute__ ((section ("zuc_tsect"))) = \
+	{ \
+		#tcase, #test, &tcase,		\
+		0,				\
+		zuctest_##tcase##_##test	\
+	}; \
+	\
+	static void zuctest_##tcase##_##test(void *data)
+
+
+/**
+ * Returns true if the currently executing test has encounted any skips.
+ *
+ * @return true if there is currently a test executing and it has
+ * encounted any skips.
+ * @see zuc_has_failure
+ * @see zuc_has_fatal_failure
+ * @see zuc_has_nonfatal_failure
+ */
+bool zuc_has_skip(void);
+
+/**
+ * Returns true if the currently executing test has encounted any failures.
+ *
+ * @return true if there is currently a test executing and it has
+ * encounted any failures.
+ * @see zuc_has_skip
+ * @see zuc_has_fatal_failure
+ * @see zuc_has_nonfatal_failure
+ */
+bool zuc_has_failure(void);
+
+/**
+ * Returns true if the currently executing test has encounted fatal failures.
+ *
+ * @return true if there is currently a test executing and it has
+ * encounted fatal failures.
+ * @see zuc_has_skip
+ * @see zuc_has_failure
+ * @see zuc_has_nonfatal_failure
+ */
+bool zuc_has_fatal_failure(void);
+
+/**
+ * Returns true if the currently executing test has encounted any
+ * non-fatal failures.
+ *
+ * @return true if there is currently a test executing and it has
+ * encounted non-fatal failures.
+ * @see zuc_has_skip
+ * @see zuc_has_failure
+ * @see zuc_has_fatal_failure
+ */
+bool zuc_has_nonfatal_failure(void);
+
+/**
+ * Terminates the current test without marking it as failed.
+ *
+ * @param message the message to log as to why the test has been skipped.
+ */
+#define ZUC_SKIP(message) \
+	do { \
+		zucimpl_terminate(__FILE__, __LINE__, false, false, #message); \
+		return; \
+	} \
+	while (0)
+
+/**
+ * Marks the current test as failed, but continues execution.
+ *
+ * @param message the message to log as to why the test has failed.
+ */
+#define ZUC_FAIL(message) \
+	zucimpl_terminate(__FILE__, __LINE__, true, false, #message)
+
+/**
+ * Terminates the current test and marks it as failed.
+ *
+ * @param message the message to log as to why the test has failed.
+ */
+#define ZUC_FATAL(message) \
+	do { \
+		zucimpl_terminate(__FILE__, __LINE__, true, true, #message); \
+		return; \
+	} \
+	while (0)
+
+/**
+ * Marks the current test as failed with a fatal issue, but does not
+ * immediately return from the current function. ZUC_FATAL() is normally
+ * preferred, but when further cleanup is needed, or the current function
+ * needs to return a value, this macro may be required.
+ *
+ * @param message the message to log as to why the test has failed.
+ * @see ZUC_FATAL()
+ */
+#define ZUC_MARK_FATAL(message) \
+	do { \
+		zucimpl_terminate(__FILE__, __LINE__, true, true, #message); \
+	} \
+	while (0)
+
+/**
+ * Creates a message that will be processed in the case of failure.
+ * If the test encounters any failures (fatal or non-fatal) then these
+ * messages are included in output. Otherwise they are discarded at the
+ * end of the test run.
+ *
+ * @param message the format string style message.
+ */
+#define ZUC_TRACEPOINT(message, ...) \
+	zucimpl_tracepoint(__FILE__, __LINE__, message, ##__VA_ARGS__);
+
+/**
+ * Verfies that the specified expression is true and marks the test as failed
+ * if it is not.
+ *
+ * @param condition the expression that is expected to be true.
+ * @note it is far better to use a more specific check when possible
+ * (e.g. ZUC_EXPECT_EQ(), ZUC_EXPECT_NE(), etc.)
+ */
+#define ZUC_EXPECT_TRUE(condition) \
+	zucimpl_expect_pred2(__FILE__, __LINE__, ZUC_OP_TRUE, false, \
+			     (condition), 0, #condition, "")
+
+/**
+ * Verfies that the specified expression is false and marks the test as failed
+ * if it is not.
+ *
+ * @param condition the expression that is expected to be false.
+ * @note it is far better to use a more specific check when possible
+ * (e.g. ZUC_EXPECT_EQ(), ZUC_EXPECT_NE(), etc.)
+ */
+#define ZUC_EXPECT_FALSE(condition) \
+	zucimpl_expect_pred2(__FILE__, __LINE__, ZUC_OP_FALSE, false, \
+			     (condition), 0, #condition, "")
+
+/**
+ * Verfies that the values of the specified expressions match and marks
+ * the test as failed if they do not.
+ *
+ * @param expected the value the result should hold.
+ * @param actual the actual value seen in testing.
+ */
+#define ZUC_EXPECT_EQ(expected, actual) \
+	zucimpl_expect_pred2(__FILE__, __LINE__, ZUC_OP_EQ, false, \
+			     (expected), (actual), #expected, #actual);
+
+/**
+ * Verfies that the values of the specified expressions differ and marks
+ * the test as failed if they do not.
+ *
+ * @param expected the value the result should not hold.
+ * @param actual the actual value seen in testing.
+ */
+#define ZUC_EXPECT_NE(expected, actual) \
+	zucimpl_expect_pred2(__FILE__, __LINE__, ZUC_OP_NE, false, \
+			     (expected), (actual), #expected, #actual);
+
+/**
+ * Verifies that the value of the first expression is less than the value
+ * of the second expression and marks the test as failed if it is not.
+ *
+ * @param lhs the expression whose value should be lesser than the other.
+ * @param rhs the expression whose value should be greater than the other.
+ */
+#define ZUC_EXPECT_LT(lhs, rhs) \
+	zucimpl_expect_pred2(__FILE__, __LINE__, ZUC_OP_LT, false, \
+			     (lhs), (rhs), #lhs, #rhs);
+
+/**
+ * Verifies that the value of the first expression is less than or equal
+ * to the value of the second expression and marks the test as failed if
+ * it is not.
+ *
+ * @param lhs the expression whose value should be lesser than or equal to
+ * the other.
+ * @param rhs the expression whose value should be greater than or equal to
+ * the other.
+ */
+#define ZUC_EXPECT_LE(lhs, rhs) \
+	zucimpl_expect_pred2(__FILE__, __LINE__, ZUC_OP_LE, false, \
+			     (lhs), (rhs), #lhs, #rhs);
+
+/**
+ * Verifies that the value of the first expression is greater than the
+ * value of the second expression and marks the test as failed if it is not.
+ *
+ * @param lhs the expression whose value should be greater than the other.
+ * @param rhs the expression whose value should be lesser than the other.
+ */
+#define ZUC_EXPECT_GT(lhs, rhs) \
+	zucimpl_expect_pred2(__FILE__, __LINE__, ZUC_OP_GT, false, \
+			     (lhs), (rhs), #lhs, #rhs);
+
+/**
+ * Verifies that the value of the first expression is greater than or equal
+ * to the value of the second expression and marks the test as failed if
+ * it is not.
+ *
+ * @param lhs the expression whose value should be greater than or equal to
+ * the other.
+ * @param rhs the expression whose value should be lesser than or equal to
+ * the other.
+ */
+#define ZUC_EXPECT_GE(lhs, rhs) \
+	zucimpl_expect_pred2(__FILE__, __LINE__, ZUC_OP_GE, false, \
+			     (lhs), (rhs), #lhs, #rhs);
+
+
+/**
+ * Internal use macro for ASSERT implementation.
+ * Should not be used directly in code.
+ */
+#define ZUCIMPL_ASSERT(opcode, lhs, rhs) \
+	do { \
+		if (zucimpl_expect_pred2(__FILE__, __LINE__, \
+					 (opcode), true, \
+					 (lhs), (rhs), #lhs, #rhs)) { \
+			return; \
+		} \
+	} \
+	while (0)
+
+
+/**
+ * Verfies that the specified expression is true, marks the test as failed
+ * and terminates the test if it is not.
+ *
+ * @param condition the expression that is expected to be true.
+ * @note it is far better to use a more specific check when possible
+ * (e.g. ZUC_EXPECT_EQ(), ZUC_EXPECT_NE(), etc.)
+ */
+#define ZUC_ASSERT_TRUE(condition) \
+	ZUCIMPL_ASSERT(ZUC_OP_TRUE, condition, 0)
+
+/**
+ * Verfies that the specified expression is false, marks the test as
+ * failed and terminates the test if it is not.
+ *
+ * @param condition the expression that is expected to be false.
+ * @note it is far better to use a more specific check when possible
+ * (e.g. ZUC_EXPECT_EQ(), ZUC_EXPECT_NE(), etc.)
+ */
+#define ZUC_ASSERT_FALSE(condition) \
+	ZUCIMPL_ASSERT(ZUC_OP_FALSE, condition, 0)
+
+/**
+ * Verfies that the values of the specified expressions match, marks the
+ * test as failed and terminates the test if they do not.
+ *
+ * @param expected the value the result should hold.
+ * @param actual the actual value seen in testing.
+ */
+#define ZUC_ASSERT_EQ(expected, actual) \
+	ZUCIMPL_ASSERT(ZUC_OP_EQ, expected, actual)
+
+/**
+ * Verfies that the values of the specified expressions differ, marks the
+ * test as failed and terminates the test if they do not.
+ *
+ * @param expected the value the result should not hold.
+ * @param actual the actual value seen in testing.
+ */
+#define ZUC_ASSERT_NE(expected, actual) \
+	ZUCIMPL_ASSERT(ZUC_OP_NE, expected, actual)
+
+/**
+ * Verifies that the value of the first expression is less than the value
+ * of the second expression, marks the test as failed and terminates the
+ * test if it is not.
+ *
+ * @param lhs the expression whose value should be lesser than the other.
+ * @param rhs the expression whose value should be greater than the other.
+ */
+#define ZUC_ASSERT_LT(lhs, rhs) \
+	ZUCIMPL_ASSERT(ZUC_OP_LT, lhs, rhs)
+
+/**
+ * Verifies that the value of the first expression is less than or equal
+ * to the value of the second expression, marks the test as failed and
+ * terminates the test if it is not.
+ *
+ * @param lhs the expression whose value should be lesser than or equal to
+ * the other.
+ * @param rhs the expression whose value should be greater than or equal to
+ * the other.
+ */
+#define ZUC_ASSERT_LE(lhs, rhs) \
+	ZUCIMPL_ASSERT(ZUC_OP_LE, lhs, rhs)
+
+/**
+ * Verifies that the value of the first expression is greater than the
+ * value of the second expression, marks the test as failed and terminates
+ * the test if it is not.
+ *
+ * @param lhs the expression whose value should be greater than the other.
+ * @param rhs the expression whose value should be lesser than the other.
+ */
+#define ZUC_ASSERT_GT(lhs, rhs) \
+	ZUCIMPL_ASSERT(ZUC_OP_GT, lhs, rhs)
+
+/**
+ * Verifies that the value of the first expression is greater than or equal
+ * to the value of the second expression, marks the test as failed and
+ * terminates the test if it is not.
+ *
+ * @param lhs the expression whose value should be greater than or equal to
+ * the other.
+ * @param rhs the expression whose value should be lesser than or equal to
+ * the other.
+ */
+#define ZUC_ASSERT_GE(lhs, rhs) \
+	ZUCIMPL_ASSERT(ZUC_OP_GE, lhs, rhs)
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* Z_UNIT_C_H */
diff --git a/tools/zunitc/inc/zunitc/zunitc_impl.h b/tools/zunitc/inc/zunitc/zunitc_impl.h
new file mode 100644
index 0000000..46253d0
--- /dev/null
+++ b/tools/zunitc/inc/zunitc/zunitc_impl.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright © 2015 Samsung Electronics Co., Ltd
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of the copyright holders not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.  The copyright holders make
+ * no representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+ * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef Z_UNIT_C_IMPL_H
+#define Z_UNIT_C_IMPL_H
+
+/**
+ * @file
+ * Internal details to bridge the public API - should not be used
+ * directly in user code.
+ */
+
+#include <stdbool.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum zuc_check_op
+{
+	ZUC_OP_TRUE,
+	ZUC_OP_FALSE,
+	ZUC_OP_EQ,
+	ZUC_OP_NE,
+	ZUC_OP_GE,
+	ZUC_OP_GT,
+	ZUC_OP_LE,
+	ZUC_OP_LT,
+	ZUC_OP_TERMINATE,
+	ZUC_OP_TRACEPOINT
+};
+
+typedef void (*zucimpl_test_fn)(void);
+
+typedef void (*zucimpl_test_fn_f)(void *);
+
+/**
+ * Internal use structure for automatic test case registration.
+ * Should not be used directly in code.
+ */
+struct zuc_registration {
+	const char *tcase;		/**< Name of the test case. */
+	const char *test;		/**< Name of the specific test. */
+	const struct zuc_fixture* fxt;	/**< Optional fixture for test/case. */
+	zucimpl_test_fn fn;		/**< function implementing base test. */
+	zucimpl_test_fn_f fn_f;	/**< function implementing test with
+					   fixture. */
+} __attribute__ ((aligned (32)));
+
+
+int zucimpl_run_tests(void);
+
+void zucimpl_terminate(char const *file, int line,
+		       bool fail, bool fatal, const char *msg);
+
+int zucimpl_tracepoint(char const *file, int line, const char *fmt, ...)
+	__attribute__ ((format (printf, 3, 4)));
+
+int zucimpl_expect_pred2(char const *file, int line,
+			 enum zuc_check_op, bool fatal,
+			 int lhs, int rhs,
+			 const char *lhs_str, const char* rhs_str);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* Z_UNIT_C_IMPL_H */
diff --git a/tools/zunitc/src/main.c b/tools/zunitc/src/main.c
new file mode 100644
index 0000000..7049db0
--- /dev/null
+++ b/tools/zunitc/src/main.c
@@ -0,0 +1,44 @@
+/*
+ * Copyright © 2015 Samsung Electronics Co., Ltd
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of the copyright holders not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.  The copyright holders make
+ * no representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+ * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * Common main() for test programs.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+
+#include "zunitc/zunitc.h"
+
+int main( int argc, char* argv[] )
+{
+	bool helped = false;
+	int rc = zuc_initialize(&argc, argv, &helped);
+
+	if ((rc == EXIT_SUCCESS) && !helped) {
+		rc = ZUC_RUN_TESTS();
+	}
+
+	zuc_cleanup();
+	return rc;
+}
diff --git a/tools/zunitc/src/zuc_base_logger.c b/tools/zunitc/src/zuc_base_logger.c
new file mode 100644
index 0000000..2ac2e01
--- /dev/null
+++ b/tools/zunitc/src/zuc_base_logger.c
@@ -0,0 +1,382 @@
+/*
+ * Copyright © 2015 Samsung Electronics Co., Ltd
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of the copyright holders not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.  The copyright holders make
+ * no representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+ * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "zuc_base_logger.h"
+
+#include <memory.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include "zuc_event_listener.h"
+#include "zuc_types.h"
+
+#include "shared/zalloc.h"
+
+/* a few sequences for rudimentary ANSI graphics. */
+#define CSI_GRN "\x1b[0;32m"
+#define CSI_RED "\x1b[0;31m"
+#define CSI_YLW "\x1b[0;33m"
+#define CSI_RST "\x1b[m"
+
+/**
+ * Logical mappings of style levels.
+ */
+enum style_level {
+	STYLE_GOOD,
+	STYLE_WARN,
+	STYLE_BAD
+};
+
+/**
+ * Structure for internal context.
+ */
+struct base_data {
+	bool use_color;
+};
+
+/**
+ * Prints a formatted string with optional ANSI coloring.
+ *
+ * @param use_color true to colorize the output, false to output normally.
+ * @param slevel the logical type to color for.
+ * @param fmt the format string to print with.
+ */
+static void styled_printf(bool use_color, enum style_level slevel,
+			  const char *fmt, ...);
+
+static void destroy(void *data);
+static void pre_run(void *data,
+		    int pass_count,
+		    int pass_num,
+		    int seed,
+		    const char *filter);
+static void run_started(void *data,
+			int live_case_count,
+			int live_test_count,
+			int disabled_count);
+static void run_ended(void *data,
+		      int case_count,
+		      struct zuc_case **cases,
+		      int live_case_count,
+		      int live_test_count,
+		      int total_passed,
+		      int total_failed,
+		      int total_disabled,
+		      long total_elapsed);
+static void case_started(void *data,
+			 struct zuc_case *test_case,
+			 int live_test_count,
+			 int disabled_count);
+static void case_ended(void *data,
+		       struct zuc_case *test_case);
+static void test_started(void *data,
+			 struct zuc_test *test);
+static void test_ended(void *data,
+		       struct zuc_test *test);
+static void check_triggered(void *data,
+			    char const *file,
+			    int line,
+			    enum zuc_fail_state state,
+			    enum zuc_check_op op,
+			    int val1,
+			    int val2,
+			    const char *expr1,
+			    const char *expr2);
+
+struct zuc_event_listener * zuc_base_logger_create(void)
+{
+	struct zuc_event_listener *listener =
+		zalloc(sizeof(struct zuc_event_listener));
+
+	listener->data = zalloc(sizeof(struct base_data));
+	listener->destroy = destroy;
+	listener->pre_run = pre_run;
+	listener->run_started = run_started;
+	listener->run_ended = run_ended;
+	listener->case_started = case_started;
+	listener->case_ended = case_ended;
+	listener->test_started = test_started;
+	listener->test_ended = test_ended;
+	listener->check_triggered = check_triggered;
+
+	return listener;
+}
+
+void styled_printf(bool use_color, enum style_level slevel,
+		   const char *fmt, ...)
+{
+	va_list argp;
+
+	if (use_color)
+		switch (slevel) {
+		case STYLE_GOOD:
+			printf(CSI_GRN);
+			break;
+		case STYLE_WARN:
+			printf(CSI_YLW);
+			break;
+		case STYLE_BAD:
+			printf(CSI_RED);
+			break;
+		default:
+			break;
+		}
+
+	va_start(argp, fmt);
+	vprintf(fmt, argp);
+	va_end(argp);
+
+	if (use_color)
+		printf(CSI_RST);
+}
+
+void destroy(void *data)
+{
+	free(data);
+}
+
+void pre_run(void *data,
+	     int pass_count,
+	     int pass_num,
+	     int seed,
+	     const char *filter)
+{
+	struct base_data *bdata = data;
+
+	bdata->use_color = isatty(fileno(stdout))
+		&& getenv("TERM") && strcmp(getenv("TERM"), "dumb");
+
+	if (pass_count > 1)
+		printf("\nRepeating all tests (iteration %d) . . .\n\n",
+		       pass_num);
+
+	if (filter && filter[0])
+		styled_printf(bdata->use_color, STYLE_WARN,
+			      "Note: test filter = %s\n",
+			      filter);
+
+	if (seed > 0)
+		styled_printf(bdata->use_color, STYLE_WARN,
+			      "Note: Randomizing tests' orders"
+			      " with a seed of %u .\n",
+			      seed);
+}
+
+void run_started(void *data,
+		 int live_case_count,
+		 int live_test_count,
+		 int disabled_count)
+{
+	struct base_data *bdata = data;
+
+	styled_printf(bdata->use_color, STYLE_GOOD, "[==========]");
+	printf(" Running %d %s from %d test %s.\n",
+	       live_test_count,
+	       (live_test_count == 1) ? "test" : "tests",
+	       live_case_count,
+	       (live_case_count == 1) ? "case" : "cases");
+}
+
+void run_ended(void *data,
+	       int case_count,
+	       struct zuc_case **cases,
+	       int live_case_count,
+	       int live_test_count,
+	       int total_passed,
+	       int total_failed,
+	       int total_disabled,
+	       long total_elapsed)
+{
+	struct base_data *bdata = data;
+	styled_printf(bdata->use_color, STYLE_GOOD, "[==========]");
+	printf(" %d %s from %d test %s ran. (%ld ms)\n",
+	       live_test_count,
+	       (live_test_count == 1) ? "test" : "tests",
+	       live_case_count,
+	       (live_case_count == 1) ? "case" : "cases",
+	       total_elapsed);
+
+	if (total_passed) {
+		styled_printf(bdata->use_color, STYLE_GOOD, "[  PASSED  ]");
+		printf(" %d %s.\n", total_passed,
+		       (total_passed == 1) ? "test" : "tests");
+	}
+
+	if (total_failed) {
+		int case_num;
+		styled_printf(bdata->use_color, STYLE_BAD, "[  FAILED  ]");
+		printf(" %d %s, listed below:\n",
+		       total_failed, (total_failed == 1) ? "test" : "tests");
+
+		for (case_num = 0; case_num < case_count; ++case_num) {
+			int i;
+			for (i = 0; i < cases[case_num]->test_count; ++i) {
+				struct zuc_test *curr =
+					cases[case_num]->tests[i];
+				if (curr->failed || curr->fatal) {
+					styled_printf(bdata->use_color,
+						      STYLE_BAD,
+						      "[  FAILED  ]");
+					printf(" %s.%s\n",
+					       cases[case_num]->name,
+					       curr->name);
+				}
+			}
+		}
+	}
+
+	if (total_failed || total_disabled)
+		printf("\n");
+
+	if (total_failed)
+		printf(" %d FAILED %s\n",
+		       total_failed,
+		       (total_failed == 1) ? "TEST" : "TESTS");
+
+	if (total_disabled)
+		styled_printf(bdata->use_color, STYLE_WARN,
+			      "  YOU HAVE %d DISABLED %s\n",
+			      total_disabled,
+			      (total_disabled == 1) ? "TEST" : "TESTS");
+}
+
+void case_started(void *data,
+		  struct zuc_case *test_case,
+		  int live_test_count,
+		  int disabled_count)
+{
+	struct base_data *bdata = data;
+	styled_printf(bdata->use_color, STYLE_GOOD, "[----------]");
+	printf(" %d %s from %s.\n",
+	       live_test_count,
+	       (live_test_count == 1) ? "test" : "tests",
+	       test_case->name);
+
+}
+
+void case_ended(void *data,
+		struct zuc_case *test_case)
+{
+	struct base_data *bdata = data;
+	styled_printf(bdata->use_color, STYLE_GOOD, "[----------]");
+	printf(" %d %s from %s (%ld ms)\n",
+	       test_case->test_count,
+	       (test_case->test_count == 1) ? "test" : "tests",
+	       test_case->name,
+	       test_case->elapsed);
+	printf("\n");
+}
+
+void test_started(void *data,
+		  struct zuc_test *test)
+{
+	struct base_data *bdata = data;
+	styled_printf(bdata->use_color, STYLE_GOOD, "[ RUN      ]");
+	printf(" %s.%s\n", test->test_case->name, test->name);
+}
+
+void test_ended(void *data,
+		struct zuc_test *test)
+{
+	struct base_data *bdata = data;
+	if (test->failed || test->fatal) {
+		styled_printf(bdata->use_color, STYLE_BAD, "[  FAILED  ]");
+		printf(" %s.%s (%ld ms)\n",
+		       test->test_case->name, test->name, test->elapsed);
+	} else {
+		styled_printf(bdata->use_color, STYLE_GOOD, "[       OK ]");
+		printf(" %s.%s (%ld ms)\n",
+		       test->test_case->name, test->name, test->elapsed);
+	}
+}
+
+const char *zuc_get_opstr(enum zuc_check_op op)
+{
+	switch (op) {
+	case ZUC_OP_EQ:
+		return "=";
+		break;
+	case ZUC_OP_NE:
+		return "!=";
+		break;
+	case ZUC_OP_GE:
+		return ">=";
+		break;
+	case ZUC_OP_GT:
+		return ">";
+		break;
+	case ZUC_OP_LE:
+		return "<=";
+		break;
+	case ZUC_OP_LT:
+		return "<";
+		break;
+	default:
+		return "???";
+	}
+}
+
+void check_triggered(void *data,
+		     char const *file,
+		     int line,
+		     enum zuc_fail_state state,
+		     enum zuc_check_op op,
+		     int val1,
+		     int val2,
+		     const char *expr1,
+		     const char *expr2)
+{
+	switch (op) {
+	case ZUC_OP_TRUE:
+		printf("%s:%d: error: Value of: %s\n", file, line, expr1);
+		printf("  Actual: false\n");
+		printf("Expected: true\n");
+		break;
+	case ZUC_OP_FALSE:
+		printf("%s:%d: error: Value of: %s\n", file, line, expr1);
+		printf("  Actual: true\n");
+		printf("Expected: false\n");
+		break;
+	case ZUC_OP_EQ:
+		printf("%s:%d: error: Value of: %s\n", file, line, expr2);
+		printf("  Actual: %d\n", val2);
+		printf("Expected: %s\n", expr1);
+		printf("Which is: %d\n", val1);
+		break;
+	case ZUC_OP_TERMINATE: {
+		char const *level = (val1 == 0) ? "error"
+			: (val1 == 1) ? "warning"
+			: "note";
+		printf("%s:%d: %s: %s\n", file, line, level, expr1);
+		break;
+	}
+	case ZUC_OP_TRACEPOINT:
+		printf("%s:%d: note: %s\n", file, line, expr1);
+		break;
+	default:
+		printf("%s:%d: error: ", file, line);
+		printf("Expected: (%s) %s (%s), actual: %d vs %d\n",
+		       expr1, zuc_get_opstr(op), expr2, val1, val2);
+	}
+}
diff --git a/tools/zunitc/src/zuc_base_logger.h b/tools/zunitc/src/zuc_base_logger.h
new file mode 100644
index 0000000..809d616
--- /dev/null
+++ b/tools/zunitc/src/zuc_base_logger.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright © 2015 Samsung Electronics Co., Ltd
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of the copyright holders not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.  The copyright holders make
+ * no representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+ * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef ZUC_BASE_LOGGER_H
+#define ZUC_BASE_LOGGER_H
+
+struct zuc_event_listener;
+
+/**
+ * Creates a new logger that outputs data to console in the default
+ * format.
+ */
+struct zuc_event_listener * zuc_base_logger_create(void);
+
+#endif /* ZUC_BASE_LOGGER_H */
diff --git a/tools/zunitc/src/zuc_collector.c b/tools/zunitc/src/zuc_collector.c
new file mode 100644
index 0000000..3a95390
--- /dev/null
+++ b/tools/zunitc/src/zuc_collector.c
@@ -0,0 +1,321 @@
+/*
+ * Copyright © 2015 Samsung Electronics Co., Ltd
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of the copyright holders not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.  The copyright holders make
+ * no representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+ * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "zuc_collector.h"
+
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "shared/zalloc.h"
+#include "zuc_event_listener.h"
+#include "zunitc/zunitc_impl.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+/**
+ * @file
+ * General handling of collecting events during testing to pass back to
+ * main tracking of fork()'d tests.
+ *
+ * @note implementation of zuc_process_message() is included here so that
+ * all child->parent IPC is in a single source file for easier maintenance
+ * and updating.
+ */
+
+/**
+ * Internal data struct for processing.
+ */
+struct collector_data
+{
+	int *fd;		/**< file descriptor to output to. */
+	struct zuc_test *test;	/**< current test, or NULL. */
+};
+
+/**
+ * Stores an int32_t into the given buffer.
+ *
+ * @param ptr the buffer to store to.
+ * @param val the value to store.
+ * @return a pointer to the position in the buffer after the stored value.
+ */
+static char *pack_int32(char *ptr, int32_t val);
+
+/**
+ * Extracts a int32_t from the given buffer.
+ *
+ * @param ptr the buffer to extract from.
+ * @param val the value to set.
+ * @return a pointer to the position in the buffer after the extracted
+ * value.
+ */
+static char const *unpack_int32(char const *ptr, int32_t *val);
+
+/**
+ * Extracts a length-prefixed string from the given buffer.
+ *
+ * @param ptr the buffer to extract from.
+ * @param str the value to set.
+ * @return a pointer to the position in the buffer after the extracted
+ * value.
+ */
+static char const *unpack_string(char const *ptr, char **str);
+
+/**
+ * Extracts an event from the given buffer.
+ *
+ * @param ptr the buffer to extract from.
+ * @param len the length of the given buffer.
+ * @return an event that was packed in the buffer
+ */
+static struct zuc_event *unpack_event(char const *ptr, int32_t len);
+
+/**
+ * Handles an event by either attaching it directly or sending it over IPC
+ * as needed.
+ */
+static void store_event(struct collector_data *cdata,
+			enum zuc_event_type event_type,
+			char const *file,
+			int line,
+			enum zuc_fail_state state,
+			enum zuc_check_op op,
+			int val1,
+			int val2,
+			const char *expr1,
+			const char *expr2);
+
+static void destroy(void *data);
+static void test_started(void *data, struct zuc_test *test);
+static void test_ended(void *data, struct zuc_test *test);
+static void check_triggered(void *data,
+			    char const *file,
+			    int line,
+			    enum zuc_fail_state state,
+			    enum zuc_check_op op,
+			    int val1,
+			    int val2,
+			    const char *expr1,
+			    const char *expr2);
+static void collect_event(void *data,
+			  char const *file,
+			  int line,
+			  const char *expr1);
+
+struct zuc_event_listener * zuc_collector_create(int *pipe_fd)
+{
+	struct zuc_event_listener *listener =
+		zalloc(sizeof(struct zuc_event_listener));
+
+	listener->data = zalloc(sizeof(struct collector_data));
+	((struct collector_data *)listener->data)->fd = pipe_fd;
+	listener->destroy = destroy;
+	listener->test_started = test_started;
+	listener->test_ended = test_ended;
+	listener->check_triggered = check_triggered;
+	listener->collect_event = collect_event;
+
+	return listener;
+}
+
+char *pack_int32(char *ptr, int32_t val)
+{
+	memcpy(ptr, &val, sizeof(val));
+	return ptr + sizeof(val);
+}
+
+void destroy(void *data)
+{
+	free(data);
+}
+
+void test_started(void *data, struct zuc_test *test)
+{
+	struct collector_data *cdata = data;
+	cdata->test = test;
+}
+
+void test_ended(void *data, struct zuc_test *test)
+{
+	struct collector_data *cdata = data;
+	cdata->test = NULL;
+}
+
+void check_triggered(void *data,
+		     char const *file,
+		     int line,
+		     enum zuc_fail_state state,
+		     enum zuc_check_op op,
+		     int val1,
+		     int val2,
+		     const char *expr1,
+		     const char *expr2)
+{
+	struct collector_data *cdata = data;
+	if (op != ZUC_OP_TRACEPOINT)
+		store_event(cdata, ZUC_EVENT_IMMEDIATE, file, line, state, op,
+			      val1, val2, expr1, expr2);
+}
+
+void collect_event(void *data,
+		   char const *file,
+		   int line,
+		   const char *expr1)
+{
+	struct collector_data *cdata = data;
+	store_event(cdata, ZUC_EVENT_DEFERRED, file, line, ZUC_CHECK_OK,
+		    ZUC_OP_TRACEPOINT,
+		    0, 0, expr1, "");
+}
+
+void store_event(struct collector_data *cdata,
+		 enum zuc_event_type event_type,
+		 char const *file,
+		 int line,
+		 enum zuc_fail_state state,
+		 enum zuc_check_op op,
+		 int val1,
+		 int val2,
+		 const char *expr1,
+		 const char *expr2)
+{
+	struct zuc_event *event = zalloc(sizeof(*event));
+	event->file = strdup(file);
+	event->line = line;
+	event->state = state;
+	event->op = op;
+	event->val1 = val1;
+	event->val2 = val2;
+	event->expr1 = strdup(expr1);
+	event->expr2 = strdup(expr2);
+
+	zuc_attach_event(cdata->test, event, event_type, false);
+
+	if (*cdata->fd == -1) {
+	} else {
+		/* Need to pass it back */
+		int sent;
+		int count;
+		int expr1_len = strlen(expr1);
+		int expr2_len = strlen(expr2);
+		int file_len = strlen(file);
+		int len = (4 * 10) + file_len + expr1_len + expr2_len;
+		char *buf = zalloc(len);
+
+		char *ptr = pack_int32(buf, len - 4);
+		ptr = pack_int32(ptr, event_type);
+		ptr = pack_int32(ptr, file_len);
+		memcpy(ptr, file, file_len);
+		ptr += file_len;
+		ptr = pack_int32(ptr, line);
+		ptr = pack_int32(ptr, state);
+		ptr = pack_int32(ptr, op);
+		ptr = pack_int32(ptr, val1);
+		ptr = pack_int32(ptr, val2);
+		ptr = pack_int32(ptr, expr1_len);
+		if (expr1_len) {
+			memcpy(ptr, expr1, expr1_len);
+			ptr += expr1_len;
+		}
+		ptr = pack_int32(ptr, expr2_len);
+		if (expr2_len) {
+			memcpy(ptr, expr2, expr2_len);
+			ptr += expr2_len;
+		}
+
+
+		sent = 0;
+		while (sent < len) {
+			count = write(*cdata->fd, buf, len);
+			if (count == -1)
+				break;
+			sent += count;
+		}
+
+		free(buf);
+	}
+}
+
+char const *unpack_int32(char const *ptr, int32_t *val)
+{
+	memcpy(val, ptr, sizeof(*val));
+	return ptr + sizeof(*val);
+}
+
+char const *unpack_string(char const *ptr, char **str)
+{
+	int32_t len = 0;
+	ptr = unpack_int32(ptr, &len);
+	*str = zalloc(len + 1);
+	if (len)
+		memcpy(*str, ptr, len);
+	ptr += len;
+	return ptr;
+}
+
+struct zuc_event *unpack_event(char const *ptr, int32_t len)
+{
+	int32_t val = 0;
+	struct zuc_event *evt = zalloc(sizeof(*evt));
+	char const *tmp = unpack_string(ptr, &evt->file);
+	tmp = unpack_int32(tmp, &evt->line);
+
+	tmp = unpack_int32(tmp, &val);
+	evt->state = val;
+	tmp = unpack_int32(tmp, &val);
+	evt->op = val;
+
+	tmp = unpack_int32(tmp, &evt->val1);
+	tmp = unpack_int32(tmp, &evt->val2);
+
+	tmp = unpack_string(tmp, &evt->expr1);
+	tmp = unpack_string(tmp, &evt->expr2);
+
+	return evt;
+}
+
+int zuc_process_message(struct zuc_test *test, int fd)
+{
+	char buf[4] = {};
+	int got = read(fd, buf, 4);
+	if (got == 4) {
+		enum zuc_event_type event_type = ZUC_EVENT_IMMEDIATE;
+		int32_t val = 0;
+		int32_t len = 0;
+		const char *tmp = NULL;
+		char *raw = NULL;
+		unpack_int32(buf, &len);
+		raw = zalloc(len);
+		got = read(fd, raw, len);
+
+		tmp = unpack_int32(raw, &val);
+		event_type = val;
+
+		struct zuc_event *evt = unpack_event(tmp, len - (tmp - raw));
+		zuc_attach_event(test, evt, event_type, true);
+		free(raw);
+	}
+	return got;
+}
diff --git a/tools/zunitc/src/zuc_collector.h b/tools/zunitc/src/zuc_collector.h
new file mode 100644
index 0000000..bbd8b73
--- /dev/null
+++ b/tools/zunitc/src/zuc_collector.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright © 2015 Samsung Electronics Co., Ltd
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of the copyright holders not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.  The copyright holders make
+ * no representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+ * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef ZUC_COLLECTOR_H
+#define ZUC_COLLECTOR_H
+
+struct zuc_event_listener;
+struct zuc_test;
+
+/**
+ * Creates a new instance of an even collector that will attatch events
+ * to the current test directly or via connection from child to parent.
+ *
+ * @param pipe_fd pointer to the file descriptor to use for communication if
+ * needed. If the value is -1 the events will be attached directly to the
+ * current test. Otherwise events will be passed back via IPC over this
+ * pipe with the expectation that the payload will be handled in the parent
+ * process via zuc_process_message().
+ * @return a new collector intance.
+ * @see zuc_process_message()
+ */
+struct zuc_event_listener * zuc_collector_create(int *pipe_fd);
+
+/**
+ * Reads events from the given pipe and processes them.
+ *
+ * @param test the currently active test to attache events for.
+ * @param pipe_fd the file descriptor of the pipe to read from.
+ * @return a positive value if a message was received, 0 if the end has
+ * been reached and -1 if an error has occurred.
+ */
+int zuc_process_message(struct zuc_test *test, int pipe_fd);
+
+#endif /* ZUC_COLLECTOR_H */
diff --git a/tools/zunitc/src/zuc_context.h b/tools/zunitc/src/zuc_context.h
new file mode 100644
index 0000000..6a4fb41
--- /dev/null
+++ b/tools/zunitc/src/zuc_context.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright © 2015 Samsung Electronics Co., Ltd
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of the copyright holders not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.  The copyright holders make
+ * no representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+ * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef ZUC_CONTEXT_H
+#define ZUC_CONTEXT_H
+
+#include "zuc_types.h"
+
+struct zuc_slinked;
+
+/**
+ * Internal context for processing.
+ * Collecting data members here minimizes use of globals.
+ */
+struct zuc_context {
+	int case_count;
+	struct zuc_case **cases;
+
+	bool fatal;
+	int repeat;
+	int random;
+	unsigned int seed;
+	bool spawn;
+	bool break_on_failure;
+	bool output_tap;
+	bool output_junit;
+	int fds[2];
+	char *filter;
+
+	struct zuc_slinked *listeners;
+
+	struct zuc_case *curr_case;
+	struct zuc_test *curr_test;
+};
+
+#endif /* ZUC_CONTEXT_H */
diff --git a/tools/zunitc/src/zuc_event.h b/tools/zunitc/src/zuc_event.h
new file mode 100644
index 0000000..d2b9fb2
--- /dev/null
+++ b/tools/zunitc/src/zuc_event.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright © 2015 Samsung Electronics Co., Ltd
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of the copyright holders not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.  The copyright holders make
+ * no representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+ * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef ZUC_EVENT_H
+#define ZUC_EVENT_H
+
+#include <stdint.h>
+
+#include "zunitc/zunitc_impl.h"
+
+/**
+ *
+ */
+enum zuc_event_type
+{
+	ZUC_EVENT_IMMEDIATE,
+	ZUC_EVENT_DEFERRED
+};
+
+/**
+ * Status enum for posted events.
+ */
+enum zuc_fail_state
+{
+	ZUC_CHECK_OK, /**< no problem. */
+	ZUC_CHECK_SKIP, /**< runtime skip directive encountered. */
+	ZUC_CHECK_FAIL, /**< non-fatal check fails. */
+	ZUC_CHECK_FATAL, /**< fatal assertion/check fails. */
+	ZUC_CHECK_ERROR /**< internal level problem. */
+};
+
+/**
+ * Record of an event that occurs during testing such as assert macro
+ * failures.
+ */
+struct zuc_event
+{
+	char *file;
+	int32_t line;
+	enum zuc_fail_state state;
+	enum zuc_check_op op;
+	int32_t val1;
+	int32_t val2;
+	char *expr1;
+	char *expr2;
+
+	struct zuc_event *next;
+};
+
+/**
+ * Attaches an event to the specified test.
+ *
+ * @param test the test to attach to.
+ * @param event the event to attach.
+ * @param event_type of event (immediate or deferred) to attach.
+ * @param transferred true if the event has been processed elsewhere and
+ * is being transferred for storage, false otherwise.
+ */
+void zuc_attach_event(struct zuc_test *test, struct zuc_event *event,
+		      enum zuc_event_type event_type, bool transferred);
+
+#endif /* ZUC_EVENT_H */
diff --git a/tools/zunitc/src/zuc_event_listener.h b/tools/zunitc/src/zuc_event_listener.h
new file mode 100644
index 0000000..6b16376
--- /dev/null
+++ b/tools/zunitc/src/zuc_event_listener.h
@@ -0,0 +1,167 @@
+/*
+ * Copyright © 2015 Samsung Electronics Co., Ltd
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of the copyright holders not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.  The copyright holders make
+ * no representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+ * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef ZUC_EVENT_HANDLER_H
+#define ZUC_EVENT_HANDLER_H
+
+#include "zuc_context.h"
+#include "zuc_event.h"
+
+struct zuc_test;
+struct zuc_case;
+
+/**
+ * Interface to allow components to process testing events as they occur.
+ *
+ * Event listeners that will stream output as testing progresses are often
+ * named "*_logger" whereas those that produce their output upon test run
+ * completion are named "*_reporter".
+ */
+struct zuc_event_listener {
+	/**
+	 * User data pointer.
+	 */
+	void *data;
+
+	/**
+	 * Destructor.
+	 * @param data the user data associated with this instance.
+	 */
+	void (*destroy)(void *data);
+
+	/**
+	 * Handler for simple pre-run state.
+	 *
+	 * @param pass_count total number of expected test passes.
+	 * @param pass_num current pass iteration number.
+	 * @param seed random seed being used, or 0 for no randomization.
+	 * @param filter filter string used for tests, or NULL/blank for none.
+	 */
+	void (*pre_run)(void *data,
+			int pass_count,
+			int pass_num,
+			int seed,
+			const char *filter);
+
+	/**
+	 * Handler for test runs starting.
+	 *
+	 * @param data the user data associated with this instance.
+	 * @param live_case_count number of active test cases in this run.
+	 * @param live_test_count number of active tests in this run.
+	 * @param disabled_count number of disabled tests in this run.
+	 */
+	void (*run_started)(void *data,
+			    int live_case_count,
+			    int live_test_count,
+			    int disabled_count);
+
+	/**
+	 * Handler for test runs ending.
+	 *
+	 * @param data the user data associated with this instance.
+	 */
+	void (*run_ended)(void *data,
+			  int case_count,
+			  struct zuc_case** cases,
+			  int live_case_count,
+			  int live_test_count,
+			  int total_passed,
+			  int total_failed,
+			  int total_disabled,
+			  long total_elapsed);
+
+	/**
+	 * Handler for test case starting.
+	 *
+	 * @param data the user data associated with this instance.
+	 */
+	void (*case_started)(void *data,
+			     struct zuc_case *test_case,
+			     int live_test_count,
+			     int disabled_count);
+
+	/**
+	 * Handler for test case ending.
+	 *
+	 * @param data the user data associated with this instance.
+	 */
+	void (*case_ended)(void *data,
+			   struct zuc_case *test_case);
+
+	/**
+	 * Handler for test starting.
+	 *
+	 * @param data the user data associated with this instance.
+	 */
+	void (*test_started)(void *data,
+			     struct zuc_test *test);
+
+	/**
+	 * Handler for test ending.
+	 *
+	 * @param data the user data associated with this instance.
+	 */
+	void (*test_ended)(void *data,
+			   struct zuc_test *test);
+
+	/**
+	 * Handler for disabled test notification.
+	 *
+	 * @param data the user data associated with this instance.
+	 */
+	void (*test_disabled)(void *data,
+			      struct zuc_test *test);
+
+	/**
+	 * Handler for check/assertion fired due to failure, warning, etc.
+	 *
+	 * @param data the user data associated with this instance.
+	 */
+	void (*check_triggered)(void *data,
+				char const *file,
+				int line,
+				enum zuc_fail_state state,
+				enum zuc_check_op op,
+				int val1,
+				int val2,
+				const char *expr1,
+				const char *expr2);
+
+	/**
+	 * Handler for tracepoints and such that may be displayed later.
+	 *
+	 * @param data the user data associated with this instance.
+	 */
+	void (*collect_event)(void *data,
+			      char const *file,
+			      int line,
+			      const char *expr1);
+};
+
+/**
+ * Registers an event listener instance to be called.
+ */
+void zuc_add_event_listener(struct zuc_event_listener *event_listener);
+
+
+#endif /* ZUC_EVENT_HANDLER_H */
diff --git a/tools/zunitc/src/zuc_types.h b/tools/zunitc/src/zuc_types.h
new file mode 100644
index 0000000..2b66852
--- /dev/null
+++ b/tools/zunitc/src/zuc_types.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright © 2015 Samsung Electronics Co., Ltd
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of the copyright holders not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.  The copyright holders make
+ * no representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+ * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef ZUC_TYPES_H
+#define ZUC_TYPES_H
+
+#include "zunitc/zunitc_impl.h"
+
+struct zuc_case;
+
+/**
+ * Represents a specific test.
+ */
+struct zuc_test
+{
+	int order;
+	struct zuc_case *test_case;
+	zucimpl_test_fn fn;
+	zucimpl_test_fn_f fn_f;
+	char *name;
+	int disabled;
+	int skipped;
+	int failed;
+	int fatal;
+	long elapsed;
+	struct zuc_event *events;
+	struct zuc_event *deferred;
+};
+
+/**
+ * Represents a test case that can hold a collection of tests.
+ */
+struct zuc_case
+{
+	int order;
+	char *name;
+	const struct zuc_fixture* fxt;
+	int disabled;
+	int skipped;
+	int failed;
+	int fatal;
+	int passed;
+	long elapsed;
+	int test_count;
+	struct zuc_test **tests;
+};
+
+/**
+ * Returns a human-readable version of the comparison opcode.
+ *
+ * @param op the opcode to get a string version of.
+ * @return a human-readable string of the opcode.
+ * (This value should not be freed)
+ */
+const char *zuc_get_opstr(enum zuc_check_op op);
+
+#endif /* ZUC_TYPES_H */
diff --git a/tools/zunitc/src/zunitc_impl.c b/tools/zunitc/src/zunitc_impl.c
new file mode 100644
index 0000000..fecd7a6
--- /dev/null
+++ b/tools/zunitc/src/zunitc_impl.c
@@ -0,0 +1,1520 @@
+/*
+ * Copyright © 2015 Samsung Electronics Co., Ltd
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of the copyright holders not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.  The copyright holders make
+ * no representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+ * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "config.h"
+
+#include <fnmatch.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "zunitc/zunitc_impl.h"
+#include "zunitc/zunitc.h"
+
+#include "zuc_base_logger.h"
+#include "zuc_collector.h"
+#include "zuc_context.h"
+#include "zuc_event_listener.h"
+
+#include "shared/config-parser.h"
+#include "shared/zalloc.h"
+
+#define ARRAY_LENGTH(a) ((int) (sizeof (a) / sizeof (a)[0]))
+
+/*
+ * If CLOCK_MONOTONIC is present on the system it will give us reliable
+ * results under certain edge conditions that normally require manual
+ * admin actions to trigger. If not, CLOCK_REALTIME is a reasonable
+ * fallback.
+ */
+#if _POSIX_MONOTONIC_CLOCK
+static const clockid_t TARGET_TIMER = CLOCK_MONOTONIC;
+#else
+static const clockid_t TARGET_TIMER = CLOCK_REALTIME;
+#endif
+
+static char const DISABLED_PREFIX[] = "DISABLED_";
+
+#define MS_PER_SEC 1000L
+#define NANO_PER_MS 1000000L
+
+/**
+ * Simple single-linked list structure.
+ */
+struct zuc_slinked {
+	void *data;
+	struct zuc_slinked *next;
+};
+
+static struct zuc_context g_ctx = {
+	.case_count = 0,
+	.cases = NULL,
+
+	.fatal = false,
+	.repeat = 0,
+	.random = 0,
+	.spawn = true,
+	.break_on_failure = false,
+	.fds = {-1, -1},
+
+	.listeners = NULL,
+
+	.curr_case = NULL,
+	.curr_test = NULL,
+};
+
+
+static void initialize(void);
+static void register_tests(void);
+static void free_test_case(struct zuc_case *test_case);
+static struct zuc_test *create_test(int order,
+				    zucimpl_test_fn fn, zucimpl_test_fn_f fn_f,
+				    char const *case_name,
+				    char const *test_name,
+				    struct zuc_case *parent);
+static void free_test(struct zuc_test *test);
+static void free_events(struct zuc_event **events);
+static void migrate_deferred_events(struct zuc_test *test, bool transferred);
+
+static int compare_regs(const void *lhs, const void *rhs);
+static int compare_case_order(const void *lhs, const void *rhs);
+static int compare_test_order(const void *lhs, const void *rhs);
+static void order_cases(int count, struct zuc_case **cases);
+static void shuffle_cases(int count, struct zuc_case **cases,
+			  unsigned int seed);
+static void filter_cases(int *count, struct zuc_case **cases,
+			 char const *filter);
+static unsigned int get_seed_from_time(void);
+
+/**
+ * A very simple matching that is compatible with the algorithm used in
+ * Google Test.
+ *
+ * @param wildcard sequence of '?', '*' or normal characters to match.
+ * @param str string to check for matching.
+ * @return true if the wildcard matches the entire string, false otherwise.
+ */
+static bool wildcard_matches(char const *wildcard, char const *str);
+
+static bool test_has_skip(struct zuc_test *test);
+static bool test_has_failure(struct zuc_test *test);
+static bool test_has_fatal_failure(struct zuc_test *test);
+static bool test_has_nonfatal_failure(struct zuc_test *test);
+static void reset_test_values(struct zuc_case **cases, int case_count);
+static void mark_failed(struct zuc_test *test, enum zuc_fail_state state);
+static void mark_single_failed(struct zuc_test *test,
+			       enum zuc_fail_state state);
+static int run_single_pass(void);
+static void run_single_case(struct zuc_case *test_case);
+static void run_single_test(struct zuc_test *test,
+			    const struct zuc_fixture *fxt,
+			    void *case_data,
+			    bool spawn);
+static void spawn_test(struct zuc_test *test, void *test_data,
+		       void (*cleanup_fn)(void *data), void *cleanup_data);
+
+typedef int (*comp_pred2)(int lhs, int rhs);
+
+static comp_pred2 get_pred2(enum zuc_check_op op);
+
+/* for outputting results */
+static void dispatch_pre_run(struct zuc_context *ctx,
+			     int pass_count,
+			     int pass_num,
+			     int seed,
+			     const char *filter);
+static void dispatch_run_started(struct zuc_context *ctx,
+				 int live_case_count,
+				 int live_test_count,
+				 int disabled_count);
+static void dispatch_run_ended(struct zuc_context *ctx,
+			       int live_case_count,
+			       int live_test_count,
+			       int total_passed,
+			       int total_failed,
+			       int total_disabled,
+			       long total_elapsed);
+static void dispatch_case_started(struct zuc_context *ctx,
+				  struct zuc_case *test_case,
+				  int live_test_count,
+				  int disabled_count);
+static void dispatch_case_ended(struct zuc_context *ctx,
+				struct zuc_case *test_case);
+static void dispatch_test_started(struct zuc_context *ctx,
+				  struct zuc_test *test);
+static void dispatch_test_ended(struct zuc_context *ctx,
+				  struct zuc_test *test);
+static void dispatch_test_disabled(struct zuc_context *ctx,
+				   struct zuc_test *test);
+static void dispatch_check_triggered(struct zuc_context *ctx,
+				     char const *file,
+				     int line,
+				     enum zuc_fail_state state,
+				     enum zuc_check_op op,
+				     int val1,
+				     int val2,
+				     const char *expr1,
+				     const char *expr2);
+static void dispatch_collect_event(struct zuc_context *ctx,
+				   char const *file,
+				   int line,
+				   const char *expr1);
+
+
+bool zuc_has_skip(void)
+{
+	bool skip = g_ctx.curr_test ?
+		test_has_skip(g_ctx.curr_test) : false;
+	return skip;
+}
+
+bool zuc_has_failure(void)
+{
+	bool failure = g_ctx.curr_test ?
+		test_has_failure(g_ctx.curr_test) : false;
+	return failure;
+}
+
+bool zuc_has_fatal_failure(void)
+{
+	bool failure = g_ctx.curr_test ?
+		test_has_fatal_failure(g_ctx.curr_test) : false;
+	return failure;
+}
+
+bool zuc_has_nonfatal_failure(void)
+{
+	bool failure = g_ctx.curr_test ?
+		test_has_nonfatal_failure(g_ctx.curr_test) : false;
+	return failure;
+}
+
+void zuc_set_filter(const char *filter)
+{
+	g_ctx.filter = strdup(filter);
+}
+
+void zuc_set_repeat(int repeat)
+{
+	g_ctx.repeat = repeat;
+}
+
+void zuc_set_random(int random)
+{
+	g_ctx.random = random;
+}
+
+void zuc_set_spawn(bool spawn)
+{
+	g_ctx.spawn = spawn;
+}
+
+void zuc_set_break_on_failure(bool break_on_failure)
+{
+	g_ctx.break_on_failure = break_on_failure;
+}
+
+int zuc_initialize(int *argc, char *argv[], bool *help_flagged)
+{
+	int rc = EXIT_FAILURE;
+	int opt_help = 0;
+	int opt_nofork = 0;
+	int opt_list = 0;
+	int opt_repeat = 0;
+	int opt_random = 0;
+	int opt_break_on_failure = 0;
+	char *opt_filter = NULL;
+
+	char *help_param = NULL;
+	int argc_in = *argc;
+
+	const struct weston_option options[] = {
+		{ WESTON_OPTION_BOOLEAN, "zuc-nofork", 0, &opt_nofork },
+		{ WESTON_OPTION_BOOLEAN, "zuc-list-tests", 0, &opt_list },
+		{ WESTON_OPTION_INTEGER, "zuc-repeat", 0, &opt_repeat },
+		{ WESTON_OPTION_INTEGER, "zuc-random", 0, &opt_random },
+		{ WESTON_OPTION_BOOLEAN, "zuc-break-on-failure", 0,
+		  &opt_break_on_failure },
+		{ WESTON_OPTION_STRING, "zuc-filter", 0, &opt_filter },
+	};
+
+	initialize();
+	if (g_ctx.fatal)
+		return EXIT_FAILURE;
+
+	if (help_flagged)
+		*help_flagged = false;
+
+	{
+		/* Help param will be a special case and need restoring. */
+		int i = 0;
+		char **argv_in = NULL;
+		const struct weston_option help_options[] = {
+			{ WESTON_OPTION_BOOLEAN, "help", 'h', &opt_help },
+		};
+		argv_in = zalloc(sizeof(char *) * argc_in);
+		if (!argv_in) {
+			printf("%s:%d: error: alloc failed.\n",
+			       __FILE__, __LINE__);
+			return EXIT_FAILURE;
+		}
+		for (i = 0; i < argc_in; ++i)
+			argv_in[i] = argv[i];
+
+		parse_options(help_options, ARRAY_LENGTH(help_options),
+			      argc, argv);
+		if (*argc < argc_in) {
+			for (i = 1; (i < argc_in) && !help_param; ++i) {
+				bool found = false;
+				int j = 0;
+				for (j = 0; (j < *argc) && !found; ++j)
+					found = (argv[j] == argv_in[i]);
+
+				if (!found)
+					help_param = argv_in[i];
+			}
+		}
+		free(argv_in);
+	}
+
+	parse_options(options, ARRAY_LENGTH(options), argc, argv);
+
+	if (help_param && (*argc < argc_in))
+		argv[(*argc)++] = help_param;
+
+	if (opt_filter) {
+		zuc_set_filter(opt_filter);
+		free(opt_filter);
+	}
+
+	if (opt_help) {
+		printf("Usage: %s [OPTIONS]\n"
+		       "  --zuc-break-on-failure\n"
+		       "  --zuc-filter=FILTER\n"
+		       "  --zuc-list-tests\n"
+		       "  --zuc-nofork\n"
+		       "  --zuc-random=N            [0|1|<seed number>]\n"
+		       "  --zuc-repeat=N\n"
+		       "  --help\n",
+		       argv[0]);
+		if (help_flagged)
+			*help_flagged = true;
+		rc = EXIT_SUCCESS;
+	} else if (opt_list) {
+		zuc_list_tests();
+		rc = EXIT_FAILURE;
+	} else {
+		zuc_set_repeat(opt_repeat);
+		zuc_set_random(opt_random);
+		zuc_set_spawn(!opt_nofork);
+		zuc_set_break_on_failure(opt_break_on_failure);
+		rc = EXIT_SUCCESS;
+	}
+
+	return rc;
+}
+
+void free_test_case(struct zuc_case *test_case)
+{
+	int i;
+	free(test_case->name);
+	for (i = test_case->test_count - 1; i >= 0; --i) {
+		free_test(test_case->tests[i]);
+		test_case->tests[i] = NULL;
+	}
+	free(test_case->tests);
+	free(test_case);
+}
+
+struct zuc_test *create_test(int order,
+			     zucimpl_test_fn fn, zucimpl_test_fn_f fn_f,
+			     char const *case_name,
+			     char const *test_name,
+			     struct zuc_case *parent)
+{
+	struct zuc_test *test = zalloc(sizeof(struct zuc_test));
+	ZUC_EXPECT_TRUE(test != NULL);
+	if (!test)
+		return NULL;
+	test->order = order;
+	test->fn = fn;
+	test->fn_f = fn_f;
+	test->name = strdup(test_name);
+	if ((!fn && !fn_f) ||
+	    (strncmp(DISABLED_PREFIX,
+		     test_name, sizeof(DISABLED_PREFIX) - 1) == 0))
+		test->disabled = 1;
+
+	test->test_case = parent;
+
+	return test;
+}
+
+void free_test(struct zuc_test *test)
+{
+	free(test->name);
+	free_events(&test->events);
+	free_events(&test->deferred);
+	free(test);
+}
+
+void free_events(struct zuc_event **events)
+{
+	struct zuc_event *evt = *events;
+	*events = NULL;
+	while (evt) {
+		struct zuc_event *old = evt;
+		evt = evt->next;
+		free(old->file);
+		free(old->expr1);
+		free(old->expr2);
+		free(old);
+	}
+}
+
+void zuc_attach_event(struct zuc_test *test, struct zuc_event *event,
+		      enum zuc_event_type event_type, bool transferred)
+{
+	if (!test) {
+		/*
+		 * consider adding events directly to the case.
+		 * would be for use during per-suite setup and teardown.
+		 */
+		printf("%s:%d: error: No current test.\n", __FILE__, __LINE__);
+	} else if (event_type == ZUC_EVENT_DEFERRED) {
+		if (test->deferred) {
+			struct zuc_event *curr = test->deferred;
+			while (curr->next)
+				curr = curr->next;
+			curr->next = event;
+		} else {
+			test->deferred = event;
+		}
+	} else {
+		if (test)
+			migrate_deferred_events(test, transferred);
+
+		if (test->events) {
+			struct zuc_event *curr = test->events;
+			while (curr->next)
+				curr = curr->next;
+			curr->next = event;
+		} else {
+			test->events = event;
+		}
+		mark_failed(test, event->state);
+	}
+}
+
+void migrate_deferred_events(struct zuc_test *test, bool transferred)
+{
+	struct zuc_event *evt = test->deferred;
+	if (!evt)
+		return;
+
+	test->deferred = NULL;
+	if (test->events) {
+		struct zuc_event *old = test->events;
+		while (old->next)
+			old = old->next;
+		old->next = evt;
+	} else {
+		test->events = evt;
+	}
+	while (evt && !transferred) {
+		dispatch_check_triggered(&g_ctx,
+					 evt->file, evt->line,
+					 evt->state, evt->op,
+					 evt->val1, evt->val2,
+					 evt->expr1, evt->expr2);
+		evt = evt->next;
+	}
+}
+
+void zuc_add_event_listener(struct zuc_event_listener *event_listener)
+{
+	if (!event_listener) /* ensure null entries are not added */
+		return;
+
+	if (!g_ctx.listeners) {
+		g_ctx.listeners = zalloc(sizeof(struct zuc_slinked));
+		ZUC_ASSERT_TRUE(g_ctx.listeners != NULL);
+		g_ctx.listeners->data = event_listener;
+	} else {
+		struct zuc_slinked *curr = g_ctx.listeners;
+		while (curr->next)
+			curr = curr->next;
+		curr->next = zalloc(sizeof(struct zuc_slinked));
+		ZUC_ASSERT_TRUE(curr->next != NULL);
+		curr->next->data = event_listener;
+	}
+}
+
+
+void initialize(void)
+{
+	static bool init = false;
+	if (init)
+		return;
+
+	init = true;
+	register_tests();
+	if (g_ctx.fatal)
+		return;
+
+	if (g_ctx.random > 1) {
+		g_ctx.seed = g_ctx.random;
+	} else if (g_ctx.random == 1) {
+		g_ctx.seed = get_seed_from_time();
+	}
+
+	if (g_ctx.listeners == NULL) {
+		zuc_add_event_listener(zuc_collector_create(&(g_ctx.fds[1])));
+		zuc_add_event_listener(zuc_base_logger_create());
+	}
+
+	if (g_ctx.case_count) {
+		order_cases(g_ctx.case_count, g_ctx.cases);
+		if (g_ctx.filter && g_ctx.filter[0])
+			filter_cases(&g_ctx.case_count, g_ctx.cases,
+				     g_ctx.filter);
+	}
+}
+
+void zuc_cleanup(void)
+{
+	int i;
+	free(g_ctx.filter);
+	g_ctx.filter = 0;
+	for (i = 0; i < 2; ++i)
+		if (g_ctx.fds[i] != -1) {
+			close(g_ctx.fds[i]);
+			g_ctx.fds[i] = -1;
+		}
+
+	if (g_ctx.listeners) {
+		struct zuc_slinked *curr = g_ctx.listeners;
+		while (curr) {
+			struct zuc_slinked *old = curr;
+			struct zuc_event_listener *listener = curr->data;
+			if (listener->destroy)
+				listener->destroy(listener->data);
+			free(listener);
+			curr = curr->next;
+			free(old);
+		}
+		g_ctx.listeners = NULL;
+	}
+
+	for (i = g_ctx.case_count - 1; i >= 0; --i) {
+		free_test_case(g_ctx.cases[i]);
+		g_ctx.cases[i] = NULL;
+	}
+	free(g_ctx.cases);
+	g_ctx.cases = NULL;
+}
+
+/* gcc-specific markers for auto test case registration: */
+extern const struct zuc_registration __start_zuc_tsect;
+extern const struct zuc_registration __stop_zuc_tsect;
+
+void register_tests(void)
+{
+	size_t case_count = 0;
+	size_t count = &__stop_zuc_tsect - &__start_zuc_tsect;
+	size_t i;
+	int idx = 0;
+	const char *last_name = NULL;
+	void **array = zalloc(sizeof(void *) * count);
+	ZUC_ASSERT_TRUE(array != NULL);
+	for (i = 0; i < count; ++i)
+		array[i] = (void *)(&__start_zuc_tsect + i);
+
+	qsort(array, count, sizeof(array[0]), compare_regs);
+
+	/* Count transitions to get number of test cases. */
+	last_name = NULL;
+	for (i = 0; i < count; ++i) {
+		const struct zuc_registration *reg =
+			(const struct zuc_registration *)array[i];
+		if (!last_name || (strcmp(last_name, reg->tcase))) {
+			last_name = reg->tcase;
+			case_count++;
+		}
+	}
+
+	/* Create test case data items. */
+	struct zuc_case **case_array =
+		zalloc(sizeof(struct zuc_case *) * case_count);
+	ZUC_ASSERT_TRUE(case_array != NULL);
+	struct zuc_case *last_case = NULL;
+	size_t case_num = 0;
+	for (i = 0; i < count; ++i) {
+		const struct zuc_registration *reg =
+			(const struct zuc_registration *)array[i];
+		if (!last_case || (strcmp(last_case->name, reg->tcase))) {
+			last_case = zalloc(sizeof(struct zuc_case));
+			ZUC_ASSERT_TRUE(last_case != NULL);
+			last_case->order = count;
+			last_case->name = strdup(reg->tcase);
+			last_case->fxt = reg->fxt;
+			last_case->test_count = i;
+			if (case_num > 0) {
+				int tcount = i
+					- case_array[case_num - 1]->test_count;
+				case_array[case_num - 1]->test_count = tcount;
+			}
+			case_array[case_num++] = last_case;
+		}
+	}
+	case_array[case_count - 1]->test_count = count
+		- case_array[case_count - 1]->test_count;
+
+	/* Reserve space for tests per test case. */
+	for (case_num = 0; case_num < case_count; ++case_num) {
+		case_array[case_num]->tests =
+			zalloc(case_array[case_num]->test_count
+			       * sizeof(struct zuc_test *));
+		ZUC_ASSERT_TRUE(case_array[case_num]->tests != NULL);
+	}
+
+	last_name = NULL;
+	case_num = -1;
+	for (i = 0; i < count; ++i) {
+		const struct zuc_registration *reg =
+			(const struct zuc_registration *)array[i];
+		int order = count - (1 + (reg - &__start_zuc_tsect));
+
+		if (!last_name || (strcmp(last_name, reg->tcase))) {
+			last_name = reg->tcase;
+			case_num++;
+			idx = 0;
+		}
+		if (order < case_array[case_num]->order)
+			case_array[case_num]->order = order;
+		case_array[case_num]->tests[idx] =
+			create_test(order, reg->fn, reg->fn_f,
+				    reg->tcase, reg->test,
+				    case_array[case_num]);
+
+		if (case_array[case_num]->fxt != reg->fxt)
+			printf("%s:%d: error: Mismatched fixtures for '%s'\n",
+			       __FILE__, __LINE__, case_array[case_num]->name);
+
+		idx++;
+	}
+	free(array);
+
+	g_ctx.case_count = case_count;
+	g_ctx.cases = case_array;
+}
+
+int compare_regs(const void *lhs, const void *rhs)
+{
+	int rc = strcmp((*(struct zuc_registration **)lhs)->tcase,
+		      (*(struct zuc_registration **)rhs)->tcase);
+	if (rc == 0)
+		rc = strcmp((*(struct zuc_registration **)lhs)->test,
+		      (*(struct zuc_registration **)rhs)->test);
+
+	return rc;
+}
+
+int compare_case_order(const void *lhs, const void *rhs)
+{
+	return (*(struct zuc_case **)lhs)->order
+		- (*(struct zuc_case **)rhs)->order;
+}
+
+int compare_test_order(const void *lhs, const void *rhs)
+{
+	return (*(struct zuc_test **)lhs)->order
+		- (*(struct zuc_test **)rhs)->order;
+}
+
+void order_cases(int count, struct zuc_case **cases)
+{
+	int i;
+	qsort(cases, count, sizeof(*cases), compare_case_order);
+	for (i = 0; i < count; ++i) {
+		qsort(cases[i]->tests, cases[i]->test_count,
+		      sizeof(*cases[i]->tests), compare_test_order);
+	}
+}
+
+void shuffle_cases(int count, struct zuc_case **cases,
+		   unsigned int seed)
+{
+	int i;
+	unsigned int rseed = seed;
+	for (i = 0; i < count; ++i) {
+		int j;
+		for (j = cases[i]->test_count - 1; j > 0 ; --j) {
+			int val = rand_r(&rseed);
+			int b = ((val / (double)RAND_MAX) * j + 0.5);
+			if (j != b) {
+				struct zuc_test *tmp = cases[i]->tests[j];
+				cases[i]->tests[j] = cases[i]->tests[b];
+				cases[i]->tests[b] = tmp;
+			}
+		}
+	}
+
+	for (i = count - 1; i > 0; --i) {
+		int val = rand_r(&rseed);
+		int j = ((val / (double)RAND_MAX) * i + 0.5);
+
+		if (i != j) {
+			struct zuc_case *tmp = cases[i];
+			cases[i] = cases[j];
+			cases[j] = tmp;
+		}
+	}
+}
+
+static char** segment_str(char *str)
+{
+	int count = 1;
+	char **parts = NULL;
+	int i = 0;
+	for (i = 0; str[i]; ++i)
+		if (str[i] == ':')
+			count++;
+	parts = zalloc(sizeof(char*) * (count + 1));
+	ZUC_EXPECT_TRUE(parts != NULL);
+	if (parts) {
+		char *saved = NULL;
+		char *tok = strtok_r(str, ":", &saved);
+		i = 0;
+		parts[i++] = tok;
+		while (tok) {
+			tok = strtok_r(NULL, ":", &saved);
+			parts[i++] = tok;
+		}
+	}
+	return parts;
+}
+
+bool wildcard_matches(char const *wildcard, char const *str)
+{
+	switch (*wildcard) {
+	case '\0':
+		return !*str;
+	case '?':
+		return *str && wildcard_matches(wildcard + 1, str + 1);
+	case '*':
+		return (*str && wildcard_matches(wildcard, str + 1))
+			|| wildcard_matches(wildcard + 1, str);
+	default:
+		return (*wildcard == *str)
+			&& wildcard_matches(wildcard + 1, str + 1);
+	};
+}
+
+void filter_cases(int *count, struct zuc_case **cases, char const *filter)
+{
+	int i = 0;
+	int j = 0;
+	int num_pos = 0;
+	int negative = -1;
+	char *buf = strdup(filter);
+	char **parts = segment_str(buf);
+
+	for (i = 0; parts[i]; ++i) {
+		if (parts[i][0] == '-') {
+			parts[i]++;
+			negative = i;
+			break;
+		}
+		num_pos++;
+	}
+
+	for (i = 0; i < *count; ++i) {
+		/* Walk backwards for easier pruning. */
+		for (j = cases[i]->test_count - 1; j >= 0; --j) {
+			int x;
+			bool keep = num_pos == 0;
+			char *name = NULL;
+			struct zuc_test *test = cases[i]->tests[j];
+			if (asprintf(&name, "%s.%s", cases[i]->name,
+				     test->name) < 0) {
+				printf("%s:%d: error: %d\n", __FILE__, __LINE__,
+				       errno);
+				exit(EXIT_FAILURE);
+			}
+			for (x = 0; (x < num_pos) && !keep; ++x)
+				keep = wildcard_matches(parts[x], name);
+			if (keep && (negative >= 0))
+				for (x = negative; parts[x] && keep; ++x)
+					keep &= !wildcard_matches(parts[x],
+								  name);
+			if (!keep) {
+				int w;
+				free_test(test);
+				for (w = j + 1; w < cases[i]->test_count; w++)
+					cases[i]->tests[w - 1] =
+						cases[i]->tests[w];
+				cases[i]->test_count--;
+			}
+
+			free(name);
+		}
+	}
+	free(parts);
+	parts = NULL;
+	free(buf);
+	buf = NULL;
+
+	/* Prune any cases with no more tests. */
+	for (i = *count - 1; i >= 0; --i) {
+		if (cases[i]->test_count < 1) {
+			free_test_case(cases[i]);
+			for (j = i + 1; j < *count; ++j)
+				cases[j - 1] = cases[j];
+			cases[*count - 1] = NULL;
+			(*count)--;
+		}
+	}
+}
+
+unsigned int get_seed_from_time(void)
+{
+	time_t sec = time(NULL);
+	unsigned int seed = (unsigned int) sec % 100000;
+	if (seed < 2)
+		seed = 2;
+
+	return seed;
+}
+
+void zuc_list_tests(void)
+{
+	int i;
+	int j;
+	initialize();
+	if (g_ctx.fatal)
+		return;
+	for (i = 0; i < g_ctx.case_count; ++i) {
+		printf("%s.\n", g_ctx.cases[i]->name);
+		for (j = 0; j < g_ctx.cases[i]->test_count; ++j) {
+			printf("  %s\n", g_ctx.cases[i]->tests[j]->name);
+		}
+	}
+}
+
+int zucimpl_run_tests(void)
+{
+	int rc = EXIT_SUCCESS;
+	int i;
+	int limit = g_ctx.repeat > 0 ? g_ctx.repeat : 1;
+
+	initialize();
+	if (g_ctx.fatal)
+		return EXIT_FAILURE;
+
+	if (g_ctx.case_count < 1) {
+		printf("%s:%d: error: Setup error: test tree is empty\n",
+		       __FILE__, __LINE__);
+		rc = EXIT_FAILURE;
+	}
+
+	for (i = 0; (i < limit) && (g_ctx.case_count > 0); ++i) {
+		int pass_code = EXIT_SUCCESS;
+		dispatch_pre_run(&g_ctx, limit, i + 1,
+				 (g_ctx.random > 0) ? g_ctx.seed : 0,
+				 g_ctx.filter);
+
+		order_cases(g_ctx.case_count, g_ctx.cases);
+		if (g_ctx.random > 0)
+			shuffle_cases(g_ctx.case_count, g_ctx.cases,
+				      g_ctx.seed);
+
+		pass_code = run_single_pass();
+		if (pass_code == EXIT_FAILURE)
+			rc = EXIT_FAILURE;
+		else if ((pass_code == ZUC_EXIT_SKIP) && (rc == EXIT_SUCCESS))
+			rc = ZUC_EXIT_SKIP;
+
+		g_ctx.seed++;
+	}
+
+	return rc;
+}
+
+void reset_test_values(struct zuc_case **cases, int case_count)
+{
+	int i;
+	for (i = 0; i < case_count; ++i) {
+		int j;
+		cases[i]->disabled = 0;
+		cases[i]->skipped = 0;
+		cases[i]->failed = 0;
+		cases[i]->fatal = 0;
+		cases[i]->passed = 0;
+		cases[i]->elapsed = 0;
+		for (j = 0; j < cases[i]->test_count; ++j) {
+			struct zuc_test *test = cases[i]->tests[j];
+			if (test->disabled)
+				cases[i]->disabled++;
+			test->skipped = 0;
+			test->failed = 0;
+			test->fatal = 0;
+			test->elapsed = 0;
+
+			free_events(&test->events);
+			free_events(&test->deferred);
+		}
+	}
+}
+
+int run_single_pass(void)
+{
+	long total_elapsed = 0;
+	int total_passed = 0;
+	int total_failed = 0;
+	int total_skipped = 0;
+	int live_case_count = 0;
+	int live_test_count = 0;
+	int disabled_test_count = 0;
+	int i;
+
+	reset_test_values(g_ctx.cases, g_ctx.case_count);
+	for (i = 0; i <  g_ctx.case_count; ++i) {
+		int live = g_ctx.cases[i]->test_count
+			- g_ctx.cases[i]->disabled;
+		if (live) {
+			live_test_count += live;
+			live_case_count++;
+		}
+		if (g_ctx.cases[i]->disabled)
+			disabled_test_count++;
+	}
+
+	dispatch_run_started(&g_ctx, live_case_count, live_test_count,
+			     disabled_test_count);
+
+	for (i = 0; i <  g_ctx.case_count; ++i) {
+		run_single_case(g_ctx.cases[i]);
+		total_failed += g_ctx.cases[i]->test_count
+			- (g_ctx.cases[i]->passed + g_ctx.cases[i]->disabled);
+		total_passed += g_ctx.cases[i]->passed;
+		total_elapsed += g_ctx.cases[i]->elapsed;
+		total_skipped += g_ctx.cases[i]->skipped;
+	}
+
+	dispatch_run_ended(&g_ctx, live_case_count, live_test_count,
+			   total_passed, total_failed, disabled_test_count,
+			   total_elapsed);
+
+	if (total_failed)
+		return EXIT_FAILURE;
+	else if (total_skipped)
+		return ZUC_EXIT_SKIP;
+	else
+		return EXIT_SUCCESS;
+}
+
+void run_single_case(struct zuc_case *test_case)
+{
+	int count_live = test_case->test_count - test_case->disabled;
+	g_ctx.curr_case = test_case;
+	if (count_live) {
+		int i = 0;
+		const struct zuc_fixture *fxt = test_case->fxt;
+		void *case_data = fxt ? (void *)fxt->data : NULL;
+
+		dispatch_case_started(&g_ctx, test_case,
+				      count_live, test_case->disabled);
+
+		if (fxt && fxt->set_up_test_case)
+			case_data = fxt->set_up_test_case(fxt->data);
+
+		for (i = 0; i < test_case->test_count; ++i) {
+			struct zuc_test *curr = test_case->tests[i];
+			if (curr->disabled) {
+				dispatch_test_disabled(&g_ctx, curr);
+			} else {
+				run_single_test(curr, fxt, case_data,
+						g_ctx.spawn);
+				if (curr->skipped)
+					test_case->skipped++;
+				if (curr->failed)
+					test_case->failed++;
+				if (curr->fatal)
+					test_case->fatal++;
+				if (!curr->failed && !curr->fatal)
+					test_case->passed++;
+				test_case->elapsed += curr->elapsed;
+			}
+		}
+
+		if (fxt && fxt->tear_down_test_case)
+			fxt->tear_down_test_case(case_data);
+
+		dispatch_case_ended(&g_ctx, test_case);
+	}
+	g_ctx.curr_case = NULL;
+}
+
+void run_single_test(struct zuc_test *test,
+		     const struct zuc_fixture *fxt,
+		     void *case_data,
+		     bool spawn)
+{
+	long elapsed = 0;
+	struct timespec begin;
+	struct timespec end;
+	void *test_data = NULL;
+	void *cleanup_data = NULL;
+	void (*cleanup_fn)(void *data) = NULL;
+	memset(&begin, 0, sizeof(begin));
+	memset(&end, 0, sizeof(end));
+
+	g_ctx.curr_test = test;
+	dispatch_test_started(&g_ctx, test);
+
+	cleanup_fn = fxt ? fxt->tear_down : NULL;
+	cleanup_data = NULL;
+
+	if (fxt && fxt->set_up) {
+		test_data = fxt->set_up(case_data);
+		cleanup_data = test_data;
+	} else {
+		test_data = case_data;
+	}
+
+	clock_gettime(TARGET_TIMER, &begin);
+
+	/* Need to re-check these, as fixtures might have changed test state. */
+	if (!test->fatal && !test->skipped) {
+		if (spawn) {
+			spawn_test(test, test_data,
+				   cleanup_fn, cleanup_data);
+		} else {
+			if (test->fn_f)
+				test->fn_f(test_data);
+			else
+				test->fn();
+		}
+	}
+
+	clock_gettime(TARGET_TIMER, &end);
+
+	elapsed = (end.tv_sec - begin.tv_sec) * MS_PER_SEC;
+	if (end.tv_sec != begin.tv_sec) {
+		elapsed -= (begin.tv_nsec) / NANO_PER_MS;
+		elapsed += (end.tv_nsec) / NANO_PER_MS;
+	} else {
+		elapsed += (end.tv_nsec - begin.tv_nsec) / NANO_PER_MS;
+	}
+	test->elapsed = elapsed;
+
+	if (cleanup_fn)
+		cleanup_fn(cleanup_data);
+
+	if (test->deferred) {
+		if (test_has_failure(test))
+			migrate_deferred_events(test, false);
+		else
+			free_events(&test->deferred);
+	}
+
+	dispatch_test_ended(&g_ctx, test);
+
+	g_ctx.curr_test = NULL;
+}
+
+void spawn_test(struct zuc_test *test, void *test_data,
+		void (*cleanup_fn)(void *data), void *cleanup_data)
+{
+	pid_t pid = -1;
+
+	if (!test || (!test->fn && !test->fn_f))
+		return;
+
+	if (pipe2(g_ctx.fds, O_CLOEXEC)) {
+		printf("%s:%d: error: Unable to create pipe: %d\n",
+		       __FILE__, __LINE__, errno);
+		mark_failed(test, ZUC_CHECK_ERROR);
+		return;
+	}
+
+	fflush(NULL); /* important. avoid duplication of output */
+	pid = fork();
+	switch (pid) {
+	case -1: /* Error forking */
+		printf("%s:%d: error: Problem with fork: %d\n",
+		       __FILE__, __LINE__, errno);
+		mark_failed(test, ZUC_CHECK_ERROR);
+		close(g_ctx.fds[0]);
+		g_ctx.fds[0] = -1;
+		close(g_ctx.fds[1]);
+		g_ctx.fds[1] = -1;
+		break;
+	case 0: { /* child */
+		int rc = EXIT_SUCCESS;
+		close(g_ctx.fds[0]);
+		g_ctx.fds[0] = -1;
+
+		if (test->fn_f)
+			test->fn_f(test_data);
+		else
+			test->fn();
+
+		if (test_has_failure(test))
+			rc = EXIT_FAILURE;
+		else if (test_has_skip(test))
+			rc = ZUC_EXIT_SKIP;
+
+		/* Avoid confusing memory tools like valgrind */
+		if (cleanup_fn)
+			cleanup_fn(cleanup_data);
+
+		zuc_cleanup();
+		exit(rc);
+		break;
+	}
+	default: { /* parent */
+		ssize_t rc = 0;
+		siginfo_t info = {};
+
+		close(g_ctx.fds[1]);
+		g_ctx.fds[1] = -1;
+
+		do {
+			rc = zuc_process_message(g_ctx.curr_test,
+						 g_ctx.fds[0]);
+		} while (rc > 0);
+		close(g_ctx.fds[0]);
+		g_ctx.fds[0] = -1;
+
+		if (waitid(P_ALL, 0, &info, WEXITED)) {
+			printf("%s:%d: error: waitid failed. (%d)\n",
+			       __FILE__, __LINE__, errno);
+			mark_failed(test, ZUC_CHECK_ERROR);
+		} else {
+			switch (info.si_code) {
+			case CLD_EXITED: {
+				int exit_code = info.si_status;
+				switch(exit_code) {
+				case EXIT_SUCCESS:
+					break;
+				case ZUC_EXIT_SKIP:
+					if (!test_has_skip(g_ctx.curr_test) &&
+					    !test_has_failure(g_ctx.curr_test))
+						ZUC_SKIP("Child exited SKIP");
+					break;
+				default:
+					/* unexpected failure */
+					if (!test_has_failure(g_ctx.curr_test))
+						ZUC_EXPECT_EQ(0, exit_code);
+				}
+				break;
+			}
+			case CLD_KILLED:
+			case CLD_DUMPED:
+				printf("%s:%d: error: signaled: %d\n",
+				       __FILE__, __LINE__, info.si_status);
+				mark_failed(test, ZUC_CHECK_ERROR);
+				break;
+			}
+		}
+	}
+	}
+}
+
+bool test_has_skip(struct zuc_test *test)
+{
+	return test->skipped;
+}
+
+bool test_has_failure(struct zuc_test *test)
+{
+	return test->fatal || test->failed;
+}
+
+bool test_has_fatal_failure(struct zuc_test *test)
+{
+	return test->fatal;
+}
+
+bool test_has_nonfatal_failure(struct zuc_test *test)
+{
+	return test->failed;
+}
+
+void mark_failed(struct zuc_test *test, enum zuc_fail_state state)
+{
+	if (!test && g_ctx.curr_test)
+		test = g_ctx.curr_test;
+
+	if (test) {
+		mark_single_failed(test, state);
+	} else if (g_ctx.curr_case) {
+		/* In setup or tear-down of test suite */
+		int i;
+		for (i = 0; i < g_ctx.curr_case->test_count; ++i)
+			mark_single_failed(g_ctx.curr_case->tests[i], state);
+	}
+	if ((state == ZUC_CHECK_FATAL) || (state == ZUC_CHECK_ERROR))
+		g_ctx.fatal = true;
+}
+
+void mark_single_failed(struct zuc_test *test, enum zuc_fail_state state)
+{
+	switch (state) {
+	case ZUC_CHECK_OK:
+		/* no internal state to change */
+		break;
+	case ZUC_CHECK_SKIP:
+		if (test)
+			test->skipped = 1;
+		break;
+	case ZUC_CHECK_FAIL:
+		if (test)
+			test->failed = 1;
+		break;
+	case ZUC_CHECK_ERROR:
+	case ZUC_CHECK_FATAL:
+		if (test)
+			test->fatal = 1;
+		break;
+	}
+
+	if (g_ctx.break_on_failure)
+		raise(SIGABRT);
+
+}
+
+int zucimpl_tracepoint(char const *file, int line, char const *fmt, ...)
+{
+	int rc = -1;
+	va_list argp;
+	char *msg = NULL;
+
+
+	va_start(argp, fmt);
+	rc = vasprintf(&msg, fmt, argp);
+	if (rc == -1) {
+		msg = NULL;
+	}
+	va_end(argp);
+
+	dispatch_collect_event(&g_ctx,
+			       file, line,
+			       msg);
+
+	free(msg);
+
+	return rc;
+}
+
+void zucimpl_terminate(char const *file, int line,
+		       bool fail, bool fatal, const char *msg)
+{
+	enum zuc_fail_state state = ZUC_CHECK_SKIP;
+	int level = 2;
+	if (fail && fatal) {
+		state =  ZUC_CHECK_FATAL;
+		level = 0;
+	} else if (fail && !fatal) {
+		state = ZUC_CHECK_FAIL;
+		level = 0;
+	}
+
+	mark_failed(g_ctx.curr_test, state);
+
+	if ((state != ZUC_CHECK_OK) && g_ctx.curr_test)
+		migrate_deferred_events(g_ctx.curr_test, false);
+
+	dispatch_check_triggered(&g_ctx,
+				 file, line,
+				 state,
+				 ZUC_OP_TERMINATE,
+				 level, 0,
+				 msg, "");
+}
+
+int zucimpl_expect_pred2(char const *file, int line,
+			 enum zuc_check_op op, bool fatal,
+			 int lhs, int rhs,
+			 const char *lhs_str, const char* rhs_str)
+{
+	enum zuc_fail_state state = fatal ?  ZUC_CHECK_FATAL : ZUC_CHECK_FAIL;
+	comp_pred2 pred = get_pred2(op);
+	int failed = !pred(lhs, rhs);
+	if (failed) {
+		mark_failed(g_ctx.curr_test, state);
+
+		if (g_ctx.curr_test)
+			migrate_deferred_events(g_ctx.curr_test, false);
+
+		dispatch_check_triggered(&g_ctx,
+					 file, line,
+					 fatal ? ZUC_CHECK_FATAL
+					 : ZUC_CHECK_FAIL,
+					 op,
+					 lhs, rhs,
+					 lhs_str, rhs_str);
+	}
+	return failed;
+}
+
+void dispatch_pre_run(struct zuc_context *ctx,
+		      int pass_count,
+		      int pass_num,
+		      int seed,
+		      const char *filter)
+{
+	struct zuc_slinked *curr;
+	for (curr = ctx->listeners; curr; curr = curr->next) {
+		struct zuc_event_listener *listener = curr->data;
+		if (listener->pre_run)
+			listener->pre_run(listener->data,
+					  pass_count,
+					  pass_num,
+					  seed,
+					  filter);
+	}
+}
+
+void dispatch_run_started(struct zuc_context *ctx,
+			  int live_case_count,
+			  int live_test_count,
+			  int disabled_count)
+{
+	struct zuc_slinked *curr;
+	for (curr = ctx->listeners; curr; curr = curr->next) {
+		struct zuc_event_listener *listener = curr->data;
+		if (listener->run_started)
+			listener->run_started(listener->data,
+					      live_case_count,
+					      live_test_count,
+					      disabled_count);
+	}
+}
+
+void dispatch_run_ended(struct zuc_context *ctx,
+			int live_case_count,
+			int live_test_count,
+			int total_passed,
+			int total_failed,
+			int total_disabled,
+			long total_elapsed)
+{
+	struct zuc_slinked *curr;
+	for (curr = ctx->listeners; curr; curr = curr->next) {
+		struct zuc_event_listener *listener = curr->data;
+		if (listener->run_ended)
+			listener->run_ended(listener->data,
+					    ctx->case_count,
+					    ctx->cases,
+					    live_case_count,
+					    live_test_count,
+					    total_passed,
+					    total_failed,
+					    total_disabled,
+					    total_elapsed);
+	}
+}
+
+void dispatch_case_started(struct zuc_context *ctx,
+			   struct zuc_case *test_case,
+			   int live_test_count,
+			   int disabled_count)
+{
+	struct zuc_slinked *curr;
+	for (curr = ctx->listeners; curr; curr = curr->next) {
+		struct zuc_event_listener *listener = curr->data;
+		if (listener->case_started)
+			listener->case_started(listener->data,
+					       test_case,
+					       live_test_count,
+					       disabled_count);
+	}
+}
+
+void dispatch_case_ended(struct zuc_context *ctx,
+			 struct zuc_case *test_case)
+{
+	struct zuc_slinked *curr;
+	for (curr = ctx->listeners; curr; curr = curr->next) {
+		struct zuc_event_listener *listener = curr->data;
+		if (listener->case_ended)
+			listener->case_ended(listener->data, test_case);
+	}
+}
+
+void dispatch_test_started(struct zuc_context *ctx,
+			   struct zuc_test *test)
+{
+	struct zuc_slinked *curr;
+	for (curr = ctx->listeners; curr; curr = curr->next) {
+		struct zuc_event_listener *listener = curr->data;
+		if (listener->test_started)
+			listener->test_started(listener->data, test);
+	}
+}
+
+void dispatch_test_ended(struct zuc_context *ctx,
+			 struct zuc_test *test)
+{
+	struct zuc_slinked *curr;
+	for (curr = ctx->listeners; curr; curr = curr->next) {
+		struct zuc_event_listener *listener = curr->data;
+		if (listener->test_ended)
+			listener->test_ended(listener->data, test);
+	}
+}
+
+void dispatch_test_disabled(struct zuc_context *ctx,
+			    struct zuc_test *test)
+{
+	struct zuc_slinked *curr;
+	for (curr = ctx->listeners; curr; curr = curr->next) {
+		struct zuc_event_listener *listener = curr->data;
+		if (listener->test_disabled)
+			listener->test_disabled(listener->data, test);
+	}
+}
+
+void dispatch_check_triggered(struct zuc_context *ctx,
+			      char const *file,
+			      int line,
+			      enum zuc_fail_state state,
+			      enum zuc_check_op op,
+			      int val1,
+			      int val2,
+			      const char *expr1,
+			      const char *expr2)
+{
+	struct zuc_slinked *curr;
+	for (curr = ctx->listeners; curr; curr = curr->next) {
+		struct zuc_event_listener *listener = curr->data;
+		if (listener->check_triggered)
+			listener->check_triggered(listener->data,
+						  file, line,
+						  state, op,
+						  val1, val2,
+						  expr1, expr2);
+	}
+}
+
+void dispatch_collect_event(struct zuc_context *ctx,
+			    char const *file,
+			    int line,
+			    const char *expr1)
+{
+	struct zuc_slinked *curr;
+	for (curr = ctx->listeners; curr; curr = curr->next) {
+		struct zuc_event_listener *listener = curr->data;
+		if (listener->collect_event)
+			listener->collect_event(listener->data,
+						file, line, expr1);
+	}
+}
+
+static int pred2_unknown(int lhs, int rhs)
+{
+	return 0;
+}
+
+static int pred2_true(int lhs, int rhs)
+{
+	return lhs;
+}
+
+static int pred2_false(int lhs, int rhs)
+{
+	return !lhs;
+}
+
+static int pred2_eq(int lhs, int rhs)
+{
+	return lhs == rhs;
+}
+
+static int pred2_ne(int lhs, int rhs)
+{
+	return lhs != rhs;
+}
+
+static int pred2_ge(int lhs, int rhs)
+{
+	return lhs >= rhs;
+}
+
+static int pred2_gt(int lhs, int rhs)
+{
+	return lhs > rhs;
+}
+
+static int pred2_le(int lhs, int rhs)
+{
+	return lhs <= rhs;
+}
+
+static int pred2_lt(int lhs, int rhs)
+{
+	return lhs < rhs;
+}
+
+comp_pred2 get_pred2(enum zuc_check_op op)
+{
+	switch (op) {
+	case ZUC_OP_TRUE:
+		return pred2_true;
+		break;
+	case ZUC_OP_FALSE:
+		return pred2_false;
+		break;
+	case ZUC_OP_EQ:
+		return pred2_eq;
+		break;
+	case ZUC_OP_NE:
+		return pred2_ne;
+		break;
+	case ZUC_OP_GE:
+		return pred2_ge;
+		break;
+	case ZUC_OP_GT:
+		return pred2_gt;
+		break;
+	case ZUC_OP_LE:
+		return pred2_le;
+		break;
+	case ZUC_OP_LT:
+		return pred2_lt;
+		break;
+	default:
+		return pred2_unknown;
+	}
+}
diff --git a/tools/zunitc/test/fixtures_test.c b/tools/zunitc/test/fixtures_test.c
new file mode 100644
index 0000000..7a79d49
--- /dev/null
+++ b/tools/zunitc/test/fixtures_test.c
@@ -0,0 +1,102 @@
+/*
+ * Copyright © 2015 Samsung Electronics Co., Ltd
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of the copyright holders not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.  The copyright holders make
+ * no representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+ * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/**
+ * Tests of fixtures.
+ */
+
+#include "config.h"
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+
+#include "zunitc/zunitc.h"
+
+
+
+/* Use a simple string for a simplistic fixture */
+static struct zuc_fixture fixture_minimal = {
+	.data = "for all good men to",
+};
+
+ZUC_TEST_F(fixture_minimal, just_as_is)
+{
+	const char *str = data;
+	ZUC_ASSERT_TRUE(str != NULL);
+
+	ZUC_ASSERT_EQ(0, strcmp("for all good men to", str));
+}
+
+/*
+ * Not important what or how data is manipulated, just that this function
+ * does something non-transparent to it.
+ */
+static void *setup_test_config(void *data)
+{
+	int i;
+	const char *str = data;
+	char *upper = NULL;
+	ZUC_EXPECT_TRUE(data != NULL);
+	if (str) {
+		upper = strdup(str);
+		ZUC_EXPECT_TRUE(upper != NULL);
+		if (upper) {
+			for (i = 0; upper[i]; ++i)
+				upper[i] = (char)toupper(upper[i]);
+		}
+	}
+
+	return upper;
+}
+
+static void teardown_test_config(void *data)
+{
+	ZUC_ASSERT_TRUE(data != NULL);
+	free(data);
+}
+
+static struct zuc_fixture fixture_data0 = {
+	.data = "Now is the time",
+	.set_up = setup_test_config,
+	.tear_down = teardown_test_config
+};
+
+ZUC_TEST_F(fixture_data0, base)
+{
+	const char *str = data;
+	ZUC_ASSERT_TRUE(str != NULL);
+
+	ZUC_ASSERT_EQ(0, strcmp("NOW IS THE TIME", str));
+}
+
+/* Use the same fixture for a second test. */
+ZUC_TEST_F(fixture_data0, no_lower)
+{
+	int i;
+	const char *str = data;
+	ZUC_ASSERT_TRUE(str != NULL);
+
+	for (i = 0; str[i]; ++i)
+		ZUC_ASSERT_EQ(0, islower(str[i]));
+}
diff --git a/tools/zunitc/test/zunitc_test.c b/tools/zunitc/test/zunitc_test.c
new file mode 100644
index 0000000..434026b
--- /dev/null
+++ b/tools/zunitc/test/zunitc_test.c
@@ -0,0 +1,314 @@
+/*
+ * Copyright © 2015 Samsung Electronics Co., Ltd
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of the copyright holders not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.  The copyright holders make
+ * no representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+ * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * A simple file to show tests being setup and run.
+ */
+
+#include "config.h"
+
+#include <stddef.h>
+
+#include "zunitc/zunitc.h"
+
+/* #define ENABLE_FAIL_TESTS */
+/* #define ENABLE_SKIP_TESTS */
+
+ZUC_TEST(base_test, math_is_sane)
+{
+	ZUC_EXPECT_EQ(4, 2 + 2);
+}
+
+ZUC_TEST(base_test, math_is_hard)
+{
+	ZUC_TRACEPOINT("Tracepoint here.");
+
+	ZUC_TRACEPOINT("Checking %d", 4);
+
+#ifdef ENABLE_FAIL_TESTS
+
+	ZUC_EXPECT_EQ(5, 2 + 2);
+	ZUC_TRACEPOINT("flip %1.3f", 3.1415927);
+
+	ZUC_EXPECT_EQ(7, 9);
+#endif
+}
+
+#ifdef ENABLE_FAIL_TESTS
+ZUC_TEST(base_test, tracepoint_after_assert)
+{
+	ZUC_TRACEPOINT("Should be seen in output");
+
+	ZUC_ASSERT_EQ(5, 2 + 2);
+
+	ZUC_TRACEPOINT("Should NOT be seen in output");
+}
+#endif
+
+ZUC_TEST(base_test, math_is_more_hard)
+{
+#ifdef ENABLE_FAIL_TESTS
+	ZUC_ASSERT_EQ(5, 2 + 2);
+	ZUC_ASSERT_EQ(7, 9);
+#endif
+}
+
+ZUC_TEST(base_test, time_counted)
+{
+	ZUC_TRACEPOINT("Never seen");
+
+	ZUC_TRACEPOINT("Sleepy Time %d", 10000 * 5);
+	ZUC_EXPECT_EQ(0, usleep(10000 * 5)); /* 50ms to show up in reporting */
+}
+
+ZUC_TEST(other_test, math_monkey)
+{
+	ZUC_EXPECT_TRUE(1);
+	ZUC_EXPECT_TRUE(3);
+	ZUC_EXPECT_FALSE(0);
+
+	ZUC_ASSERT_TRUE(1);
+	ZUC_ASSERT_TRUE(3);
+	ZUC_ASSERT_FALSE(0);
+
+	ZUC_EXPECT_EQ(5, 2 + 3);
+	ZUC_ASSERT_EQ(5, 2 + 3);
+
+	int b = 9;
+	ZUC_EXPECT_NE(1, 2);
+	ZUC_EXPECT_NE(b, b + 2);
+
+	ZUC_ASSERT_NE(1, 2);
+	ZUC_ASSERT_NE(b, b + 1);
+
+	ZUC_EXPECT_LT(1, 2);
+	ZUC_ASSERT_LT(1, 3);
+
+	ZUC_EXPECT_LE(1, 2);
+	ZUC_ASSERT_LE(1, 3);
+
+	ZUC_EXPECT_LE(1, 1);
+	ZUC_ASSERT_LE(1, 1);
+
+	ZUC_EXPECT_GT(2, 1);
+	ZUC_ASSERT_GT(3, 1);
+
+	ZUC_EXPECT_GE(1, 1);
+	ZUC_ASSERT_GE(1, 1);
+
+	ZUC_EXPECT_GE(2, 1);
+	ZUC_ASSERT_GE(3, 1);
+}
+
+
+static void force_non_fatal_failure(void)
+{
+#ifdef ENABLE_FAIL_TESTS
+	bool expected_to_fail_here = true;
+	ZUC_EXPECT_FALSE(expected_to_fail_here);
+#endif
+}
+
+static void force_fatal_failure(void)
+{
+#ifdef ENABLE_FAIL_TESTS
+	bool expected_to_fail_here = true;
+	ZUC_ASSERT_FALSE(expected_to_fail_here);
+
+	ZUC_FATAL("Should never reach here");
+	ZUC_ASSERT_NE(1, 1);
+#endif
+}
+
+#ifdef ENABLE_FAIL_TESTS
+ZUC_TEST(infrastructure, fail_keeps_testing)
+{
+	ZUC_FAIL("Time to fail testing");
+
+	ZUC_FATAL("Should always reach here");
+	ZUC_ASSERT_NE(1, 1); /* in case ZUC_FATAL doesn't work. */
+}
+#endif
+
+#ifdef ENABLE_FAIL_TESTS
+ZUC_TEST(infrastructure, fatal_stops_test)
+{
+	ZUC_FATAL("Time to kill testing");
+
+	ZUC_FATAL("Should never reach here");
+	ZUC_ASSERT_NE(1, 1); /* in case ZUC_FATAL doesn't work. */
+}
+#endif
+
+#ifdef ENABLE_SKIP_TESTS
+ZUC_TEST(infrastructure, skip_stops_test)
+{
+	ZUC_SKIP("Time to skip testing");
+
+	ZUC_FATAL("Should never reach here");
+	ZUC_ASSERT_NE(1, 1); /* in case ZUC_FATAL doesn't work. */
+}
+#endif
+
+struct fixture_data {
+	int case_counter;
+	int test_counter;
+};
+
+static struct fixture_data fixture_info = {0, 0};
+
+static void *complex_test_set_up_case(const void *data)
+{
+	fixture_info.case_counter++;
+	return &fixture_info;
+}
+
+static void complex_test_tear_down_case(void *data)
+{
+	ZUC_EXPECT_TRUE(&fixture_info == data);
+	fixture_info.case_counter--;
+}
+
+static void *complex_test_set_up(void *data)
+{
+	fixture_info.test_counter = fixture_info.case_counter;
+	return &fixture_info;
+}
+
+static void complex_test_tear_down(void *data)
+{
+	ZUC_ASSERT_EQ(1, fixture_info.case_counter);
+}
+
+struct zuc_fixture complex_test = {
+	.set_up = complex_test_set_up,
+	.tear_down = complex_test_tear_down,
+	.set_up_test_case = complex_test_set_up_case,
+	.tear_down_test_case = complex_test_tear_down_case
+};
+
+/*
+ * Note that these next cases all try to modify the test_counter member,
+ * but the fixture should reset that.
+*/
+
+ZUC_TEST_F(complex_test, bases_cenario)
+{
+	struct fixture_data *fdata = data;
+	ZUC_ASSERT_TRUE(fdata != NULL);
+
+	ZUC_EXPECT_EQ(4, 3 + 1);
+	ZUC_EXPECT_EQ(1, fdata->case_counter);
+	ZUC_EXPECT_EQ(1, fdata->test_counter);
+	fdata->test_counter++;
+	ZUC_EXPECT_EQ(2, fdata->test_counter);
+}
+
+ZUC_TEST_F(complex_test, something)
+{
+	struct fixture_data *fdata = data;
+	ZUC_ASSERT_TRUE(fdata != NULL);
+
+	ZUC_EXPECT_EQ(4, 3 + 1);
+	ZUC_EXPECT_EQ(1, fdata->case_counter);
+	ZUC_EXPECT_EQ(1, fdata->test_counter);
+	fdata->test_counter++;
+	ZUC_EXPECT_EQ(2, fdata->test_counter);
+}
+
+ZUC_TEST_F(complex_test, else_here)
+{
+	struct fixture_data *fdata = data;
+	ZUC_ASSERT_TRUE(fdata != NULL);
+
+	ZUC_EXPECT_EQ(4, 3 + 1);
+	ZUC_EXPECT_EQ(1, fdata->case_counter);
+	ZUC_EXPECT_EQ(1, fdata->test_counter);
+	fdata->test_counter++;
+	ZUC_EXPECT_EQ(2, fdata->test_counter);
+}
+
+ZUC_TEST(more, DISABLED_not_run)
+{
+	ZUC_ASSERT_EQ(1, 2);
+}
+
+ZUC_TEST(more, failure_states)
+{
+#ifdef ENABLE_FAIL_TESTS
+	bool expected_to_fail_here = true;
+#endif
+
+	ZUC_EXPECT_FALSE(zuc_has_failure());
+	ZUC_EXPECT_FALSE(zuc_has_nonfatal_failure());
+	ZUC_EXPECT_FALSE(zuc_has_fatal_failure());
+
+#ifdef ENABLE_FAIL_TESTS
+	ZUC_EXPECT_FALSE(expected_to_fail_here); /* should fail */
+
+	ZUC_EXPECT_TRUE(zuc_has_failure());
+	ZUC_EXPECT_TRUE(zuc_has_nonfatal_failure());
+	ZUC_EXPECT_FALSE(zuc_has_fatal_failure());
+#endif
+}
+
+ZUC_TEST(more, failure_sub_nonfatal)
+{
+	ZUC_EXPECT_FALSE(zuc_has_failure());
+	ZUC_EXPECT_FALSE(zuc_has_nonfatal_failure());
+	ZUC_EXPECT_FALSE(zuc_has_fatal_failure());
+
+	force_non_fatal_failure();
+
+#ifdef ENABLE_FAIL_TESTS
+	ZUC_EXPECT_TRUE(zuc_has_failure());
+	ZUC_EXPECT_TRUE(zuc_has_nonfatal_failure());
+	ZUC_EXPECT_FALSE(zuc_has_fatal_failure());
+#endif
+}
+
+ZUC_TEST(more, failure_sub_fatal)
+{
+	ZUC_EXPECT_FALSE(zuc_has_failure());
+	ZUC_EXPECT_FALSE(zuc_has_nonfatal_failure());
+	ZUC_EXPECT_FALSE(zuc_has_fatal_failure());
+
+	force_fatal_failure();
+
+#ifdef ENABLE_FAIL_TESTS
+	ZUC_EXPECT_TRUE(zuc_has_failure());
+	ZUC_EXPECT_FALSE(zuc_has_nonfatal_failure());
+	ZUC_EXPECT_TRUE(zuc_has_fatal_failure());
+#endif
+}
+
+ZUC_TEST(base_test, later)
+{
+	/* an additional test for the same case but later in source */
+	ZUC_EXPECT_EQ(3, 5 - 2);
+}
+
+ZUC_TEST(base_test, zed)
+{
+	/* an additional test for the same case but later in source */
+	ZUC_EXPECT_EQ(3, 5 - 2);
+}
-- 
2.1.0



More information about the wayland-devel mailing list