snapshot for rework
diff --git a/Android.mk b/Android.mk
new file mode 100644
index 0000000..4132f27
--- /dev/null
+++ b/Android.mk
@@ -0,0 +1,28 @@
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE_TAGS := tests
+LOCAL_SRC_FILES:= \
+	src/bundle.c \
+	src/debug.c \
+	src/json-format.c \
+	src/json-test.c \
+	src/text-utils.c \
+	src/uuid.c \
+    src/lava-wrapper.c
+LOCAL_MODULE := lava-wrapper 
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES:= \
+	src/bundle.c \
+	src/debug.c \
+	src/json-format.c \
+	src/json-test.c \
+	src/text-utils.c \
+	src/uuid.c \
+    src/lava-wrapper.c
+LOCAL_MODULE := lava-wrapper 
+LOCAL_MODULE_TAGS := tests
+LOCAL_CFLAGS += -g
+include $(BUILD_HOST_EXECUTABLE)
diff --git a/bundle.h b/bundle.h
new file mode 100644
index 0000000..8f0f9f5
--- /dev/null
+++ b/bundle.h
@@ -0,0 +1,129 @@
+#ifndef BUNDLE_H
+#define BUNDLE_H
+
+/**
+ * Utility functions for printing Linaro Dashboard Bundles.
+ * See http://linaro-dashboard-bundle.readthedocs.org/en/latest/schema/examples.html
+ * for example bundles.
+ **/
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <sys/time.h>
+#include <time.h>
+
+/**
+ * Print the header of a bundle object.
+ *
+ * Must be called in pair with bundle_print_footer().
+ *
+ * Once called you may call bundle_print_test_run_header().
+ **/
+void
+bundle_print_header(
+    FILE *bundle_stream,
+    unsigned flags,
+    const char *format
+    );
+
+
+/**
+ * Print the footer of a bundle object.
+ *
+ * Terminates a complete bundle. You cannot call any other bundle_print_*
+ * functions on the same stream after that or the bundle will be incorrect
+ * JSON.
+ **/
+void
+bundle_print_footer(
+    FILE *bundle_stream,
+    unsigned flags
+    );
+
+
+/**
+ * Prints the header of a test run object.
+ *
+ * Must be called in pair with bundle_print_test_run_footer().
+ *
+ * Once called you may call bundle_print_test_result_header().
+ **/
+void
+bundle_print_test_run_header(
+    FILE *bundle_stream,
+    unsigned flags,
+    bool comma,
+    const char *analyzer_assigned_uuid,
+    time_t analyzer_assigned_date,
+    bool time_check_performed,
+    const char *test_id
+    );
+
+
+/**
+ * Prints the footer of a test run object.
+ *
+ * Must be called in pair with bundle_print_test_run_header().
+ *
+ * Once called you may call bundle_print_test_result_header().
+ **/
+void
+bundle_print_test_run_footer(
+    FILE *bundle_stream,
+    unsigned flags
+    );
+
+/**
+ * Prints the header of a test result object.
+ *
+ * Must be called in pair with bundle_print_test_result_footer().
+ *
+ * The initial call in the current test_run must pass comma=false, all
+ * subsequent calls must pass true there.
+ *
+ * When called you may call bundle_print_test_result_message() (any number of times)
+ **/
+void
+bundle_print_test_result_header(
+    FILE *bundle_stream,
+    unsigned flags,
+    bool comma,
+    const char *test_case_id,
+    time_t timestamp
+    );
+
+
+/**
+ * Prints a (fragment of) test result message.
+ *
+ * May be called between bundle_print_test_result_header() and
+ * bundle_print_test_result_footer().
+ *
+ * May be called multiple times. Each time the 'message' is directly
+ * concatenated to any previous messages.
+ **/
+void
+bundle_print_test_result_message(
+    FILE *bundle_stream,
+    unsigned flags,
+    const char *message
+    );
+
+
+/**
+ * Prints the footer of a test result.
+ *
+ * Must be called in pair with bundle_print_test_result_header().
+ **/
+void
+bundle_print_test_result_footer(
+    FILE *bundle_stream,
+    unsigned flags,
+    const char *result,
+    const char *log_filename,
+    int log_lineno,
+    struct timeval duration
+    );
+
+
+#endif
diff --git a/debug.h b/debug.h
new file mode 100644
index 0000000..ffa7327
--- /dev/null
+++ b/debug.h
@@ -0,0 +1,22 @@
+#ifndef DEBUG_H
+#define DEBUG_H
+
+#include <stdio.h>
+
+/**
+ * Enable debuging and redirect all debug messages to the specified stream
+ **/
+void
+debug_enable(
+    FILE *stream
+    );
+
+/**
+ * Emit a printf-like message on the debug channel
+ **/
+void
+debug_log(
+    const char *format, ...
+    ) __attribute__((format(printf, 1, 2)));
+
+#endif
diff --git a/dummy-BufferQueue_test b/dummy-BufferQueue_test
new file mode 100755
index 0000000..9929e1b
--- /dev/null
+++ b/dummy-BufferQueue_test
@@ -0,0 +1,34 @@
+#!/bin/sh
+cat << DUMP
+[==========] Running 12 tests from 1 test case.
+[----------] Global test environment set-up.
+[----------] 12 tests from TestBufferQueue
+[ RUN      ] TestBufferQueue.testInvalidBuffer
+[       OK ] TestBufferQueue.testInvalidBuffer
+[ RUN      ] TestBufferQueue.testMuteSolo
+[       OK ] TestBufferQueue.testMuteSolo
+[ RUN      ] TestBufferQueue.testSeek
+[       OK ] TestBufferQueue.testSeek
+[ RUN      ] TestBufferQueue.testValidBuffer
+[       OK ] TestBufferQueue.testValidBuffer
+[ RUN      ] TestBufferQueue.testEnqueueMaxBuffer
+[       OK ] TestBufferQueue.testEnqueueMaxBuffer
+[ RUN      ] TestBufferQueue.testEnqueueExtraBuffer
+[       OK ] TestBufferQueue.testEnqueueExtraBuffer
+[ RUN      ] TestBufferQueue.testEnqueueAtStopped
+[       OK ] TestBufferQueue.testEnqueueAtStopped
+[ RUN      ] TestBufferQueue.testEnqueueAtPaused
+[       OK ] TestBufferQueue.testEnqueueAtPaused
+[ RUN      ] TestBufferQueue.testClearQueue
+[       OK ] TestBufferQueue.testClearQueue
+[ RUN      ] TestBufferQueue.testStateTransitionEmptyQueue
+[       OK ] TestBufferQueue.testStateTransitionEmptyQueue
+[ RUN      ] TestBufferQueue.testStateTransitionNonEmptyQueue
+[       OK ] TestBufferQueue.testStateTransitionNonEmptyQueue
+[ RUN      ] TestBufferQueue.testStatePlayBuffer
+[       OK ] TestBufferQueue.testStatePlayBuffer
+[----------] Global test environment tear-down
+[==========] 12 tests from 1 test case ran.
+[  PASSED  ] 12 tests.
+DUMP
+
diff --git a/dummy-SurfaceTexture_test b/dummy-SurfaceTexture_test
new file mode 100755
index 0000000..b260b90
--- /dev/null
+++ b/dummy-SurfaceTexture_test
@@ -0,0 +1,98 @@
+#!/bin/sh
+cat << DUMP
+Running main() from gtest_main.cc
+[==========] Running 44 tests from 6 test cases.
+[----------] Global test environment set-up.
+[----------] 4 tests from SurfaceTest
+[ RUN      ] SurfaceTest.QueuesToWindowComposerIsTrueWhenVisible
+[       OK ] SurfaceTest.QueuesToWindowComposerIsTrueWhenVisible
+[ RUN      ] SurfaceTest.QueuesToWindowComposerIsTrueWhenPurgatorized
+[       OK ] SurfaceTest.QueuesToWindowComposerIsTrueWhenPurgatorized
+[ RUN      ] SurfaceTest.ScreenshotsOfProtectedBuffersSucceed
+[       OK ] SurfaceTest.ScreenshotsOfProtectedBuffersSucceed
+[ RUN      ] SurfaceTest.ConcreteTypeIsSurface
+[       OK ] SurfaceTest.ConcreteTypeIsSurface
+[----------] 23 tests from SurfaceTextureClientTest
+[ RUN      ] SurfaceTextureClientTest.GetISurfaceTextureIsNotNull
+[       OK ] SurfaceTextureClientTest.GetISurfaceTextureIsNotNull
+[ RUN      ] SurfaceTextureClientTest.QueuesToWindowCompositorIsFalse
+[       OK ] SurfaceTextureClientTest.QueuesToWindowCompositorIsFalse
+[ RUN      ] SurfaceTextureClientTest.ConcreteTypeIsSurfaceTextureClient
+[       OK ] SurfaceTextureClientTest.ConcreteTypeIsSurfaceTextureClient
+[ RUN      ] SurfaceTextureClientTest.EglCreateWindowSurfaceSucceeds
+[       OK ] SurfaceTextureClientTest.EglCreateWindowSurfaceSucceeds
+[ RUN      ] SurfaceTextureClientTest.BufferGeometryInvalidSizesFail
+[       OK ] SurfaceTextureClientTest.BufferGeometryInvalidSizesFail
+[ RUN      ] SurfaceTextureClientTest.DefaultGeometryValues
+[       OK ] SurfaceTextureClientTest.DefaultGeometryValues
+[ RUN      ] SurfaceTextureClientTest.BufferGeometryCanBeSet
+[       OK ] SurfaceTextureClientTest.BufferGeometryCanBeSet
+[ RUN      ] SurfaceTextureClientTest.BufferGeometryDefaultSizeSetFormat
+[       OK ] SurfaceTextureClientTest.BufferGeometryDefaultSizeSetFormat
+[ RUN      ] SurfaceTextureClientTest.BufferGeometrySetSizeDefaultFormat
+[       OK ] SurfaceTextureClientTest.BufferGeometrySetSizeDefaultFormat
+[ RUN      ] SurfaceTextureClientTest.BufferGeometrySizeCanBeUnset
+[       OK ] SurfaceTextureClientTest.BufferGeometrySizeCanBeUnset
+[ RUN      ] SurfaceTextureClientTest.BufferGeometrySizeCanBeChangedWithoutFormat
+[       OK ] SurfaceTextureClientTest.BufferGeometrySizeCanBeChangedWithoutFormat
+[ RUN      ] SurfaceTextureClientTest.SurfaceTextureSetDefaultSize
+[       OK ] SurfaceTextureClientTest.SurfaceTextureSetDefaultSize
+[ RUN      ] SurfaceTextureClientTest.SurfaceTextureSetDefaultSizeAfterDequeue
+[       OK ] SurfaceTextureClientTest.SurfaceTextureSetDefaultSizeAfterDequeue
+[ RUN      ] SurfaceTextureClientTest.SurfaceTextureSetDefaultSizeVsGeometry
+[       OK ] SurfaceTextureClientTest.SurfaceTextureSetDefaultSizeVsGeometry
+[ RUN      ] SurfaceTextureClientTest.SurfaceTextureTooManyUpdateTexImage
+[       OK ] SurfaceTextureClientTest.SurfaceTextureTooManyUpdateTexImage
+[ RUN      ] SurfaceTextureClientTest.SurfaceTextureSyncModeSlowRetire
+[       OK ] SurfaceTextureClientTest.SurfaceTextureSyncModeSlowRetire
+[ RUN      ] SurfaceTextureClientTest.SurfaceTextureSyncModeFastRetire
+[       OK ] SurfaceTextureClientTest.SurfaceTextureSyncModeFastRetire
+[ RUN      ] SurfaceTextureClientTest.SurfaceTextureSyncModeDQQR
+[       OK ] SurfaceTextureClientTest.SurfaceTextureSyncModeDQQR
+[ RUN      ] SurfaceTextureClientTest.SurfaceTextureSyncModeMinUndequeued
+[       OK ] SurfaceTextureClientTest.SurfaceTextureSyncModeMinUndequeued
+[ RUN      ] SurfaceTextureClientTest.GetTransformMatrixReturnsVerticalFlip
+[       OK ] SurfaceTextureClientTest.GetTransformMatrixReturnsVerticalFlip
+[ RUN      ] SurfaceTextureClientTest.GetTransformMatrixSucceedsAfterFreeingBuffers
+[       OK ] SurfaceTextureClientTest.GetTransformMatrixSucceedsAfterFreeingBuffers
+[ RUN      ] SurfaceTextureClientTest.GetTransformMatrixSucceedsAfterFreeingBuffersWithCrop
+[       OK ] SurfaceTextureClientTest.GetTransformMatrixSucceedsAfterFreeingBuffersWithCrop
+[ RUN      ] SurfaceTextureClientTest.QueryFormatAfterSettingWorks
+[       OK ] SurfaceTextureClientTest.QueryFormatAfterSettingWorks
+[----------] 8 tests from SurfaceTextureGLTest
+[ RUN      ] SurfaceTextureGLTest.TexturingFromCpuFilledYV12BufferNpot
+pixel check failure: g(184 isn't 127) b(171 isn't 255)
+frameworks/base/libs/gui/tests/SurfaceTexture_test.cpp:584: Failure
+Value of: checkPixel( 0, 0, 255, 127, 255, 255)
+  Actual: false
+Expected: true
+pixel check failure: r(4 isn't 0) g(75 isn't 133) b(87 isn't 0)
+frameworks/base/libs/gui/tests/SurfaceTexture_test.cpp:585: Failure
+Value of: checkPixel(63, 0, 0, 133, 0, 255)
+  Actual: false
+Expected: true
+pixel check failure: r(3 isn't 0) g(87 isn't 133) b(22 isn't 0)
+frameworks/base/libs/gui/tests/SurfaceTexture_test.cpp:586: Failure
+Value of: checkPixel(63, 65, 0, 133, 0, 255)
+  Actual: false
+Expected: true
+pixel check failure: g(172 isn't 127) b(236 isn't 255)
+frameworks/base/libs/gui/tests/SurfaceTexture_test.cpp:587: Failure
+Value of: checkPixel( 0, 65, 255, 127, 255, 255)
+  Actual: false
+Expected: true
+pixel check failure: b(82 isn't 118)
+frameworks/base/libs/gui/tests/SurfaceTexture_test.cpp:592: Failure
+Value of: checkPixel( 7, 31, 155, 0, 118, 255)
+  Actual: false
+Expected: true
+pixel check failure: r(103 isn't 107) g(44 isn't 24) b(0 isn't 87)
+frameworks/base/libs/gui/tests/SurfaceTexture_test.cpp:593: Failure
+Value of: checkPixel(31, 9, 107, 24, 87, 255)
+  Actual: false
+Expected: true
+*********************************************************************
+ASSERT EXIT: In file: vendor/st-ericsson/multimedia/linux/mali400/driver/./src/ump/arch_hwmem/ump_frontend_hwmem.c  function: ump_mapped_pointer_release()   line: 166
+Error in mapping pointer (not mapped)
+DUMP
+exit 130
diff --git a/dummy-dalvik-vm-unit-tests b/dummy-dalvik-vm-unit-tests
new file mode 100755
index 0000000..cc5fd1f
--- /dev/null
+++ b/dummy-dalvik-vm-unit-tests
@@ -0,0 +1,17 @@
+#!/bin/sh
+cat << DUMP
+[==========] Running 4 tests from 1 test case.
+[----------] Global test environment set-up.
+[----------] 4 tests from dvmHumanReadableDescriptor
+[ RUN      ] dvmHumanReadableDescriptor.ArrayReferences
+[       OK ] dvmHumanReadableDescriptor.ArrayReferences
+[ RUN      ] dvmHumanReadableDescriptor.ScalarReferences
+[       OK ] dvmHumanReadableDescriptor.ScalarReferences
+[ RUN      ] dvmHumanReadableDescriptor.PrimitiveArrays
+[       OK ] dvmHumanReadableDescriptor.PrimitiveArrays
+[ RUN      ] dvmHumanReadableDescriptor.PrimitiveScalars
+[       OK ] dvmHumanReadableDescriptor.PrimitiveScalars
+[----------] Global test environment tear-down
+[==========] 4 tests from 1 test case ran.
+[  PASSED  ] 4 tests.
+DUMP
diff --git a/dummy-gtest_repeat_test b/dummy-gtest_repeat_test
new file mode 100755
index 0000000..21c49d9
--- /dev/null
+++ b/dummy-gtest_repeat_test
@@ -0,0 +1,1036 @@
+#!/bin/sh
+cat << DUMP
+[==========] Running 13 tests from 3 test cases.
+[----------] Global test environment set-up.
+[----------] 1 test from BarDeathTest
+[ RUN      ] BarDeathTest.ThreadSafeAndFast
+
+[WARNING] external/gtest/src/../src/gtest-death-test.cc:789:: Death tests use fork(), which is unsafe particularly in a threaded context. For this test, Google Test couldn't detect the number of threads.
+[       OK ] BarDeathTest.ThreadSafeAndFast (229 ms)
+[----------] 1 test from BarDeathTest (229 ms total)
+
+[----------] 2 tests from FooTest
+[ RUN      ] FooTest.ShouldFail
+external/gtest/test/gtest_repeat_test.cc:96: Failure
+Value of: 1
+Expected: 0
+Expected failure.
+[  FAILED  ] FooTest.ShouldFail (0 ms)
+[ RUN      ] FooTest.ShouldPass
+[       OK ] FooTest.ShouldPass (0 ms)
+[----------] 2 tests from FooTest (0 ms total)
+
+[----------] 10 tests from MyParamSequence/MyParamTest
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/0
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/0 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/1
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/1 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/2
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/2 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/3
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/3 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/4
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/4 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/5
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/5 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/6
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/6 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/7
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/7 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/8
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/8 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/9
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/9 (0 ms)
+[----------] 10 tests from MyParamSequence/MyParamTest (0 ms total)
+
+[----------] Global test environment tear-down
+[==========] 13 tests from 3 test cases ran. (231 ms total)
+[  PASSED  ] 12 tests.
+[  FAILED  ] 1 test, listed below:
+[  FAILED  ] FooTest.ShouldFail
+
+ 1 FAILED TEST
+[==========] Running 13 tests from 3 test cases.
+[----------] Global test environment set-up.
+[----------] 1 test from BarDeathTest
+[ RUN      ] BarDeathTest.ThreadSafeAndFast
+
+[WARNING] external/gtest/src/../src/gtest-death-test.cc:789:: Death tests use fork(), which is unsafe particularly in a threaded context. For this test, Google Test couldn't detect the number of threads.
+[       OK ] BarDeathTest.ThreadSafeAndFast (215 ms)
+[----------] 1 test from BarDeathTest (215 ms total)
+
+[----------] 2 tests from FooTest
+[ RUN      ] FooTest.ShouldFail
+external/gtest/test/gtest_repeat_test.cc:96: Failure
+Value of: 1
+Expected: 0
+Expected failure.
+[  FAILED  ] FooTest.ShouldFail (0 ms)
+[ RUN      ] FooTest.ShouldPass
+[       OK ] FooTest.ShouldPass (0 ms)
+[----------] 2 tests from FooTest (0 ms total)
+
+[----------] 10 tests from MyParamSequence/MyParamTest
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/0
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/0 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/1
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/1 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/2
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/2 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/3
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/3 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/4
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/4 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/5
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/5 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/6
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/6 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/7
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/7 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/8
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/8 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/9
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/9 (0 ms)
+[----------] 10 tests from MyParamSequence/MyParamTest (1 ms total)
+
+[----------] Global test environment tear-down
+[==========] 13 tests from 3 test cases ran. (216 ms total)
+[  PASSED  ] 12 tests.
+[  FAILED  ] 1 test, listed below:
+[  FAILED  ] FooTest.ShouldFail
+
+ 1 FAILED TEST
+
+Repeating all tests (iteration 1) . . .
+
+[==========] Running 13 tests from 3 test cases.
+[----------] Global test environment set-up.
+[----------] 1 test from BarDeathTest
+[ RUN      ] BarDeathTest.ThreadSafeAndFast
+
+[WARNING] external/gtest/src/../src/gtest-death-test.cc:789:: Death tests use fork(), which is unsafe particularly in a threaded context. For this test, Google Test couldn't detect the number of threads.
+[       OK ] BarDeathTest.ThreadSafeAndFast (219 ms)
+[----------] 1 test from BarDeathTest (219 ms total)
+
+[----------] 2 tests from FooTest
+[ RUN      ] FooTest.ShouldFail
+external/gtest/test/gtest_repeat_test.cc:96: Failure
+Value of: 1
+Expected: 0
+Expected failure.
+[  FAILED  ] FooTest.ShouldFail (0 ms)
+[ RUN      ] FooTest.ShouldPass
+[       OK ] FooTest.ShouldPass (0 ms)
+[----------] 2 tests from FooTest (0 ms total)
+
+[----------] 10 tests from MyParamSequence/MyParamTest
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/0
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/0 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/1
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/1 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/2
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/2 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/3
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/3 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/4
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/4 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/5
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/5 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/6
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/6 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/7
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/7 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/8
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/8 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/9
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/9 (0 ms)
+[----------] 10 tests from MyParamSequence/MyParamTest (0 ms total)
+
+[----------] Global test environment tear-down
+[==========] 13 tests from 3 test cases ran. (220 ms total)
+[  PASSED  ] 12 tests.
+[  FAILED  ] 1 test, listed below:
+[  FAILED  ] FooTest.ShouldFail
+
+ 1 FAILED TEST
+
+Repeating all tests (iteration 2) . . .
+
+[==========] Running 13 tests from 3 test cases.
+[----------] Global test environment set-up.
+[----------] 1 test from BarDeathTest
+[ RUN      ] BarDeathTest.ThreadSafeAndFast
+
+[WARNING] external/gtest/src/../src/gtest-death-test.cc:789:: Death tests use fork(), which is unsafe particularly in a threaded context. For this test, Google Test couldn't detect the number of threads.
+[       OK ] BarDeathTest.ThreadSafeAndFast (205 ms)
+[----------] 1 test from BarDeathTest (205 ms total)
+
+[----------] 2 tests from FooTest
+[ RUN      ] FooTest.ShouldFail
+external/gtest/test/gtest_repeat_test.cc:96: Failure
+Value of: 1
+Expected: 0
+Expected failure.
+[  FAILED  ] FooTest.ShouldFail (0 ms)
+[ RUN      ] FooTest.ShouldPass
+[       OK ] FooTest.ShouldPass (0 ms)
+[----------] 2 tests from FooTest (0 ms total)
+
+[----------] 10 tests from MyParamSequence/MyParamTest
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/0
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/0 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/1
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/1 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/2
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/2 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/3
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/3 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/4
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/4 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/5
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/5 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/6
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/6 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/7
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/7 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/8
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/8 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/9
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/9 (0 ms)
+[----------] 10 tests from MyParamSequence/MyParamTest (1 ms total)
+
+[----------] Global test environment tear-down
+[==========] 13 tests from 3 test cases ran. (206 ms total)
+[  PASSED  ] 12 tests.
+[  FAILED  ] 1 test, listed below:
+[  FAILED  ] FooTest.ShouldFail
+
+ 1 FAILED TEST
+
+Repeating all tests (iteration 3) . . .
+
+[==========] Running 13 tests from 3 test cases.
+[----------] Global test environment set-up.
+[----------] 1 test from BarDeathTest
+[ RUN      ] BarDeathTest.ThreadSafeAndFast
+
+[WARNING] external/gtest/src/../src/gtest-death-test.cc:789:: Death tests use fork(), which is unsafe particularly in a threaded context. For this test, Google Test couldn't detect the number of threads.
+[       OK ] BarDeathTest.ThreadSafeAndFast (215 ms)
+[----------] 1 test from BarDeathTest (215 ms total)
+
+[----------] 2 tests from FooTest
+[ RUN      ] FooTest.ShouldFail
+external/gtest/test/gtest_repeat_test.cc:96: Failure
+Value of: 1
+Expected: 0
+Expected failure.
+[  FAILED  ] FooTest.ShouldFail (0 ms)
+[ RUN      ] FooTest.ShouldPass
+[       OK ] FooTest.ShouldPass (0 ms)
+[----------] 2 tests from FooTest (0 ms total)
+
+[----------] 10 tests from MyParamSequence/MyParamTest
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/0
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/0 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/1
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/1 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/2
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/2 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/3
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/3 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/4
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/4 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/5
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/5 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/6
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/6 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/7
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/7 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/8
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/8 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/9
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/9 (0 ms)
+[----------] 10 tests from MyParamSequence/MyParamTest (1 ms total)
+
+[----------] Global test environment tear-down
+[==========] 13 tests from 3 test cases ran. (216 ms total)
+[  PASSED  ] 12 tests.
+[  FAILED  ] 1 test, listed below:
+[  FAILED  ] FooTest.ShouldFail
+
+ 1 FAILED TEST
+
+Repeating all tests (iteration 4) . . .
+
+[==========] Running 13 tests from 3 test cases.
+[----------] Global test environment set-up.
+[----------] 1 test from BarDeathTest
+[ RUN      ] BarDeathTest.ThreadSafeAndFast
+
+[WARNING] external/gtest/src/../src/gtest-death-test.cc:789:: Death tests use fork(), which is unsafe particularly in a threaded context. For this test, Google Test couldn't detect the number of threads.
+[       OK ] BarDeathTest.ThreadSafeAndFast (227 ms)
+[----------] 1 test from BarDeathTest (227 ms total)
+
+[----------] 2 tests from FooTest
+[ RUN      ] FooTest.ShouldFail
+external/gtest/test/gtest_repeat_test.cc:96: Failure
+Value of: 1
+Expected: 0
+Expected failure.
+[  FAILED  ] FooTest.ShouldFail (0 ms)
+[ RUN      ] FooTest.ShouldPass
+[       OK ] FooTest.ShouldPass (0 ms)
+[----------] 2 tests from FooTest (0 ms total)
+
+[----------] 10 tests from MyParamSequence/MyParamTest
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/0
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/0 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/1
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/1 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/2
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/2 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/3
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/3 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/4
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/4 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/5
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/5 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/6
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/6 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/7
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/7 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/8
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/8 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/9
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/9 (0 ms)
+[----------] 10 tests from MyParamSequence/MyParamTest (0 ms total)
+
+[----------] Global test environment tear-down
+[==========] 13 tests from 3 test cases ran. (227 ms total)
+[  PASSED  ] 12 tests.
+[  FAILED  ] 1 test, listed below:
+[  FAILED  ] FooTest.ShouldFail
+
+ 1 FAILED TEST
+
+Repeating all tests (iteration 5) . . .
+
+[==========] Running 13 tests from 3 test cases.
+[----------] Global test environment set-up.
+[----------] 1 test from BarDeathTest
+[ RUN      ] BarDeathTest.ThreadSafeAndFast
+
+[WARNING] external/gtest/src/../src/gtest-death-test.cc:789:: Death tests use fork(), which is unsafe particularly in a threaded context. For this test, Google Test couldn't detect the number of threads.
+[       OK ] BarDeathTest.ThreadSafeAndFast (225 ms)
+[----------] 1 test from BarDeathTest (225 ms total)
+
+[----------] 2 tests from FooTest
+[ RUN      ] FooTest.ShouldFail
+external/gtest/test/gtest_repeat_test.cc:96: Failure
+Value of: 1
+Expected: 0
+Expected failure.
+[  FAILED  ] FooTest.ShouldFail (0 ms)
+[ RUN      ] FooTest.ShouldPass
+[       OK ] FooTest.ShouldPass (0 ms)
+[----------] 2 tests from FooTest (0 ms total)
+
+[----------] 10 tests from MyParamSequence/MyParamTest
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/0
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/0 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/1
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/1 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/2
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/2 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/3
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/3 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/4
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/4 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/5
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/5 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/6
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/6 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/7
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/7 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/8
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/8 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/9
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/9 (0 ms)
+[----------] 10 tests from MyParamSequence/MyParamTest (0 ms total)
+
+[----------] Global test environment tear-down
+[==========] 13 tests from 3 test cases ran. (225 ms total)
+[  PASSED  ] 12 tests.
+[  FAILED  ] 1 test, listed below:
+[  FAILED  ] FooTest.ShouldFail
+
+ 1 FAILED TEST
+
+Repeating all tests (iteration 1) . . .
+
+Note: Google Test filter = None
+[==========] Running 0 tests from 0 test cases.
+[==========] 0 tests from 0 test cases ran. (0 ms total)
+[  PASSED  ] 0 tests.
+
+Repeating all tests (iteration 2) . . .
+
+Note: Google Test filter = None
+[==========] Running 0 tests from 0 test cases.
+[==========] 0 tests from 0 test cases ran. (0 ms total)
+[  PASSED  ] 0 tests.
+
+Repeating all tests (iteration 1) . . .
+
+Note: Google Test filter = None
+[==========] Running 0 tests from 0 test cases.
+[==========] 0 tests from 0 test cases ran. (0 ms total)
+[  PASSED  ] 0 tests.
+
+Repeating all tests (iteration 2) . . .
+
+Note: Google Test filter = None
+[==========] Running 0 tests from 0 test cases.
+[==========] 0 tests from 0 test cases ran. (0 ms total)
+[  PASSED  ] 0 tests.
+
+Repeating all tests (iteration 3) . . .
+
+Note: Google Test filter = None
+[==========] Running 0 tests from 0 test cases.
+[==========] 0 tests from 0 test cases ran. (0 ms total)
+[  PASSED  ] 0 tests.
+
+Repeating all tests (iteration 1) . . .
+
+Note: Google Test filter = *-*ShouldFail
+[==========] Running 12 tests from 3 test cases.
+[----------] Global test environment set-up.
+[----------] 1 test from BarDeathTest
+[ RUN      ] BarDeathTest.ThreadSafeAndFast
+
+[WARNING] external/gtest/src/../src/gtest-death-test.cc:789:: Death tests use fork(), which is unsafe particularly in a threaded context. For this test, Google Test couldn't detect the number of threads.
+[       OK ] BarDeathTest.ThreadSafeAndFast (215 ms)
+[----------] 1 test from BarDeathTest (215 ms total)
+
+[----------] 1 test from FooTest
+[ RUN      ] FooTest.ShouldPass
+[       OK ] FooTest.ShouldPass (0 ms)
+[----------] 1 test from FooTest (0 ms total)
+
+[----------] 10 tests from MyParamSequence/MyParamTest
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/0
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/0 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/1
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/1 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/2
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/2 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/3
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/3 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/4
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/4 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/5
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/5 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/6
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/6 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/7
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/7 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/8
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/8 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/9
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/9 (0 ms)
+[----------] 10 tests from MyParamSequence/MyParamTest (0 ms total)
+
+[----------] Global test environment tear-down
+[==========] 12 tests from 3 test cases ran. (215 ms total)
+[  PASSED  ] 12 tests.
+
+Repeating all tests (iteration 2) . . .
+
+Note: Google Test filter = *-*ShouldFail
+[==========] Running 12 tests from 3 test cases.
+[----------] Global test environment set-up.
+[----------] 1 test from BarDeathTest
+[ RUN      ] BarDeathTest.ThreadSafeAndFast
+
+[WARNING] external/gtest/src/../src/gtest-death-test.cc:789:: Death tests use fork(), which is unsafe particularly in a threaded context. For this test, Google Test couldn't detect the number of threads.
+[       OK ] BarDeathTest.ThreadSafeAndFast (233 ms)
+[----------] 1 test from BarDeathTest (233 ms total)
+
+[----------] 1 test from FooTest
+[ RUN      ] FooTest.ShouldPass
+[       OK ] FooTest.ShouldPass (0 ms)
+[----------] 1 test from FooTest (0 ms total)
+
+[----------] 10 tests from MyParamSequence/MyParamTest
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/0
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/0 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/1
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/1 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/2
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/2 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/3
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/3 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/4
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/4 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/5
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/5 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/6
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/6 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/7
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/7 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/8
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/8 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/9
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/9 (0 ms)
+[----------] 10 tests from MyParamSequence/MyParamTest (0 ms total)
+
+[----------] Global test environment tear-down
+[==========] 12 tests from 3 test cases ran. (233 ms total)
+[  PASSED  ] 12 tests.
+
+Repeating all tests (iteration 3) . . .
+
+Note: Google Test filter = *-*ShouldFail
+[==========] Running 12 tests from 3 test cases.
+[----------] Global test environment set-up.
+[----------] 1 test from BarDeathTest
+[ RUN      ] BarDeathTest.ThreadSafeAndFast
+
+[WARNING] external/gtest/src/../src/gtest-death-test.cc:789:: Death tests use fork(), which is unsafe particularly in a threaded context. For this test, Google Test couldn't detect the number of threads.
+[       OK ] BarDeathTest.ThreadSafeAndFast (238 ms)
+[----------] 1 test from BarDeathTest (238 ms total)
+
+[----------] 1 test from FooTest
+[ RUN      ] FooTest.ShouldPass
+[       OK ] FooTest.ShouldPass (0 ms)
+[----------] 1 test from FooTest (0 ms total)
+
+[----------] 10 tests from MyParamSequence/MyParamTest
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/0
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/0 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/1
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/1 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/2
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/2 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/3
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/3 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/4
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/4 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/5
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/5 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/6
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/6 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/7
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/7 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/8
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/8 (0 ms)
+[ RUN      ] MyParamSequence/MyParamTest.ShouldPass/9
+[       OK ] MyParamSequence/MyParamTest.ShouldPass/9 (0 ms)
+[----------] 10 tests from MyParamSequence/MyParamTest (0 ms total)
+
+[----------] Global test environment tear-down
+[==========] 12 tests from 3 test cases ran. (239 ms total)
+[  PASSED  ] 12 tests.
+
+Repeating all tests (iteration 1) . . .
+
+Note: Google Test filter = *ShouldFail
+[==========] Running 1 test from 1 test case.
+[----------] Global test environment set-up.
+[----------] 1 test from FooTest
+[ RUN      ] FooTest.ShouldFail
+external/gtest/test/gtest_repeat_test.cc:96: Failure
+Value of: 1
+Expected: 0
+Expected failure.
+[  FAILED  ] FooTest.ShouldFail (1 ms)
+[----------] 1 test from FooTest (1 ms total)
+
+[----------] Global test environment tear-down
+[==========] 1 test from 1 test case ran. (1 ms total)
+[  PASSED  ] 0 tests.
+[  FAILED  ] 1 test, listed below:
+[  FAILED  ] FooTest.ShouldFail
+
+ 1 FAILED TEST
+
+Repeating all tests (iteration 2) . . .
+
+Note: Google Test filter = *ShouldFail
+[==========] Running 1 test from 1 test case.
+[----------] Global test environment set-up.
+[----------] 1 test from FooTest
+[ RUN      ] FooTest.ShouldFail
+external/gtest/test/gtest_repeat_test.cc:96: Failure
+Value of: 1
+Expected: 0
+Expected failure.
+[  FAILED  ] FooTest.ShouldFail (0 ms)
+[----------] 1 test from FooTest (0 ms total)
+
+[----------] Global test environment tear-down
+[==========] 1 test from 1 test case ran. (0 ms total)
+[  PASSED  ] 0 tests.
+[  FAILED  ] 1 test, listed below:
+[  FAILED  ] FooTest.ShouldFail
+
+ 1 FAILED TEST
+
+Repeating all tests (iteration 3) . . .
+
+Note: Google Test filter = *ShouldFail
+[==========] Running 1 test from 1 test case.
+[----------] Global test environment set-up.
+[----------] 1 test from FooTest
+[ RUN      ] FooTest.ShouldFail
+external/gtest/test/gtest_repeat_test.cc:96: Failure
+Value of: 1
+Expected: 0
+Expected failure.
+[  FAILED  ] FooTest.ShouldFail (0 ms)
+[----------] 1 test from FooTest (1 ms total)
+
+[----------] Global test environment tear-down
+[==========] 1 test from 1 test case ran. (1 ms total)
+[  PASSED  ] 0 tests.
+[  FAILED  ] 1 test, listed below:
+[  FAILED  ] FooTest.ShouldFail
+
+ 1 FAILED TEST
+
+Repeating all tests (iteration 4) . . .
+
+Note: Google Test filter = *ShouldFail
+[==========] Running 1 test from 1 test case.
+[----------] Global test environment set-up.
+[----------] 1 test from FooTest
+[ RUN      ] FooTest.ShouldFail
+external/gtest/test/gtest_repeat_test.cc:96: Failure
+Value of: 1
+Expected: 0
+Expected failure.
+[  FAILED  ] FooTest.ShouldFail (0 ms)
+[----------] 1 test from FooTest (0 ms total)
+
+[----------] Global test environment tear-down
+[==========] 1 test from 1 test case ran. (0 ms total)
+[  PASSED  ] 0 tests.
+[  FAILED  ] 1 test, listed below:
+[  FAILED  ] FooTest.ShouldFail
+
+ 1 FAILED TEST
+PASS
+root@android:/data/nativetest # 
+external/gtest/test/gtest_repeat_test.cc:96: Failure
+Value of: 1
+Expected: 0
+Expected failure.
+[  FAILED  ] FooTest.ShouldFail (0 ms)
+[----------] 1 test from FooTest (0 ms total)
+
+[----------] Global test environment tear-down
+[==========] 1 test from 1 test case ran. (0 ms total)
+[  PASSED  ] 0 tests.
+[  FAILED  ] 1 test, listed below:
+[  FAILED  ] FooTest.ShouldFail
+
+ 1 FAILED TEST
+
+Repeating all tests (iteration 3) . . .
+
+Note: Google Test filter = *ShouldFail
+[==========] Running 1 test from 1 test case.
+[----------] Global test environment set-up.
+[----------] 1 test from FooTest
+[ RUN      ] FooTest.ShouldFail
+external/gtest/test/gtest_repeat_test.cc:96: Failure
+Value of: 1
+Expected: 0
+Expected failure.
+[  FAILED  ] FooTest.ShouldFail (0 ms)
+[----------] 1 test from FooTest (1 ms total)
+
+[----------] Global test environment tear-down
+[==========] 1 test from 1 test case ran. (1 ms total)
+[  PASSED  ] 0 tests.
+[  FAILED  ] 1 test, listed below:
+[  FAILED  ] FooTest.ShouldFail
+
+ 1 FAILED TEST
+
+Repeating all tests (iteration 4) . . .
+
+Note: Google Test filter = *ShouldFail
+[==========] Running 1 test from 1 test case.
+[----------] Global test environment set-up.
+[----------] 1 test from FooTest
+[ RUN      ] FooTest.ShouldFail
+external/gtest/test/gtest_repeat_test.cc:96: Failure
+Value of: 1
+Expected: 0
+Expected failure.
+[  FAILED  ] FooTest.ShouldFail (0 ms)
+[----------] 1 test from FooTest (0 ms total)
+
+[----------] Global test environment tear-down
+[==========] 1 test from 1 test case ran. (0 ms total)
+[  PASSED  ] 0 tests.
+[  FAILED  ] 1 test, listed below:
+[  FAILED  ] FooTest.ShouldFail
+
+ 1 FAILED TEST
+PASS
+root@android:/data/nativetest # 
+[  PASSED  ] 0 tests.
+[  FAILED  ] 1 test, listed below:
+[  FAILED  ] FooTest.ShouldFail
+
+ 1 FAILED TEST
+
+Repeating all tests (iteration 2) . . .
+
+Note: Google Test filter = *ShouldFail
+[==========] Running 1 test from 1 test case.
+[----------] Global test environment set-up.
+[----------] 1 test from FooTest
+[ RUN      ] FooTest.ShouldFail
+external/gtest/test/gtest_repeat_test.cc:96: Failure
+Value of: 1
+Expected: 0
+Expected failure.
+[  FAILED  ] FooTest.ShouldFail (0 ms)
+[----------] 1 test from FooTest (0 ms total)
+
+[----------] Global test environment tear-down
+[==========] 1 test from 1 test case ran. (0 ms total)
+[  PASSED  ] 0 tests.
+[  FAILED  ] 1 test, listed below:
+[  FAILED  ] FooTest.ShouldFail
+
+ 1 FAILED TEST
+
+Repeating all tests (iteration 3) . . .
+
+Note: Google Test filter = *ShouldFail
+[==========] Running 1 test from 1 test case.
+[----------] Global test environment set-up.
+[----------] 1 test from FooTest
+[ RUN      ] FooTest.ShouldFail
+external/gtest/test/gtest_repeat_test.cc:96: Failure
+Value of: 1
+Expected: 0
+Expected failure.
+[  FAILED  ] FooTest.ShouldFail (0 ms)
+[----------] 1 test from FooTest (1 ms total)
+
+[----------] Global test environment tear-down
+[==========] 1 test from 1 test case ran. (1 ms total)
+[  PASSED  ] 0 tests.
+[  FAILED  ] 1 test, listed below:
+[  FAILED  ] FooTest.ShouldFail
+
+ 1 FAILED TEST
+
+Repeating all tests (iteration 4) . . .
+
+Note: Google Test filter = *ShouldFail
+[==========] Running 1 test from 1 test case.
+[----------] Global test environment set-up.
+[----------] 1 test from FooTest
+[ RUN      ] FooTest.ShouldFail
+external/gtest/test/gtest_repeat_test.cc:96: Failure
+Value of: 1
+Expected: 0
+Expected failure.
+[  FAILED  ] FooTest.ShouldFail (0 ms)
+[----------] 1 test from FooTest (0 ms total)
+
+[----------] Global test environment tear-down
+[==========] 1 test from 1 test case ran. (0 ms total)
+[  PASSED  ] 0 tests.
+[  FAILED  ] 1 test, listed below:
+[  FAILED  ] FooTest.ShouldFail
+
+ 1 FAILED TEST
+PASS
+root@android:/data/nativetest # 
+external/gtest/test/gtest_repeat_test.cc:96: Failure
+Value of: 1
+Expected: 0
+Expected failure.
+[  FAILED  ] FooTest.ShouldFail (0 ms)
+[----------] 1 test from FooTest (0 ms total)
+
+[----------] Global test environment tear-down
+[==========] 1 test from 1 test case ran. (0 ms total)
+[  PASSED  ] 0 tests.
+[  FAILED  ] 1 test, listed below:
+[  FAILED  ] FooTest.ShouldFail
+
+ 1 FAILED TEST
+
+Repeating all tests (iteration 3) . . .
+
+Note: Google Test filter = *ShouldFail
+[==========] Running 1 test from 1 test case.
+[----------] Global test environment set-up.
+[----------] 1 test from FooTest
+[ RUN      ] FooTest.ShouldFail
+external/gtest/test/gtest_repeat_test.cc:96: Failure
+Value of: 1
+Expected: 0
+Expected failure.
+[  FAILED  ] FooTest.ShouldFail (0 ms)
+[----------] 1 test from FooTest (1 ms total)
+
+[----------] Global test environment tear-down
+[==========] 1 test from 1 test case ran. (1 ms total)
+[  PASSED  ] 0 tests.
+[  FAILED  ] 1 test, listed below:
+[  FAILED  ] FooTest.ShouldFail
+
+ 1 FAILED TEST
+
+Repeating all tests (iteration 4) . . .
+
+Note: Google Test filter = *ShouldFail
+[==========] Running 1 test from 1 test case.
+[----------] Global test environment set-up.
+[----------] 1 test from FooTest
+[ RUN      ] FooTest.ShouldFail
+external/gtest/test/gtest_repeat_test.cc:96: Failure
+Value of: 1
+Expected: 0
+Expected failure.
+[  FAILED  ] FooTest.ShouldFail (0 ms)
+[----------] 1 test from FooTest (0 ms total)
+
+[----------] Global test environment tear-down
+[==========] 1 test from 1 test case ran. (0 ms total)
+[  PASSED  ] 0 tests.
+[  FAILED  ] 1 test, listed below:
+[  FAILED  ] FooTest.ShouldFail
+
+ 1 FAILED TEST
+PASS
+root@android:/data/nativetest # 
+
+
+
+
+
+
+
+
+
+
+
+
+
+[  PASSED  ] 0 tests.
+[  FAILED  ] 1 test, listed below:
+[  FAILED  ] FooTest.ShouldFail
+
+ 1 FAILED TEST
+
+Repeating all tests (iteration 2) . . .
+
+Note: Google Test filter = *ShouldFail
+[==========] Running 1 test from 1 test case.
+[----------] Global test environment set-up.
+[----------] 1 test from FooTest
+[ RUN      ] FooTest.ShouldFail
+external/gtest/test/gtest_repeat_test.cc:96: Failure
+Value of: 1
+Expected: 0
+Expected failure.
+[  FAILED  ] FooTest.ShouldFail (0 ms)
+[----------] 1 test from FooTest (0 ms total)
+
+[----------] Global test environment tear-down
+[==========] 1 test from 1 test case ran. (0 ms total)
+[  PASSED  ] 0 tests.
+[  FAILED  ] 1 test, listed below:
+[  FAILED  ] FooTest.ShouldFail
+
+ 1 FAILED TEST
+
+Repeating all tests (iteration 3) . . .
+
+Note: Google Test filter = *ShouldFail
+[==========] Running 1 test from 1 test case.
+[----------] Global test environment set-up.
+[----------] 1 test from FooTest
+[ RUN      ] FooTest.ShouldFail
+external/gtest/test/gtest_repeat_test.cc:96: Failure
+Value of: 1
+Expected: 0
+Expected failure.
+[  FAILED  ] FooTest.ShouldFail (0 ms)
+[----------] 1 test from FooTest (1 ms total)
+
+[----------] Global test environment tear-down
+[==========] 1 test from 1 test case ran. (1 ms total)
+[  PASSED  ] 0 tests.
+[  FAILED  ] 1 test, listed below:
+[  FAILED  ] FooTest.ShouldFail
+
+ 1 FAILED TEST
+
+Repeating all tests (iteration 4) . . .
+
+Note: Google Test filter = *ShouldFail
+[==========] Running 1 test from 1 test case.
+[----------] Global test environment set-up.
+[----------] 1 test from FooTest
+[ RUN      ] FooTest.ShouldFail
+external/gtest/test/gtest_repeat_test.cc:96: Failure
+Value of: 1
+Expected: 0
+Expected failure.
+[  FAILED  ] FooTest.ShouldFail (0 ms)
+[----------] 1 test from FooTest (0 ms total)
+
+[----------] Global test environment tear-down
+[==========] 1 test from 1 test case ran. (0 ms total)
+[  PASSED  ] 0 tests.
+[  FAILED  ] 1 test, listed below:
+[  FAILED  ] FooTest.ShouldFail
+
+ 1 FAILED TEST
+PASS
+root@android:/data/nativetest # 
+external/gtest/test/gtest_repeat_test.cc:96: Failure
+Value of: 1
+Expected: 0
+Expected failure.
+[  FAILED  ] FooTest.ShouldFail (0 ms)
+[----------] 1 test from FooTest (0 ms total)
+
+[----------] Global test environment tear-down
+[==========] 1 test from 1 test case ran. (0 ms total)
+[  PASSED  ] 0 tests.
+[  FAILED  ] 1 test, listed below:
+[  FAILED  ] FooTest.ShouldFail
+
+ 1 FAILED TEST
+
+Repeating all tests (iteration 3) . . .
+
+Note: Google Test filter = *ShouldFail
+[==========] Running 1 test from 1 test case.
+[----------] Global test environment set-up.
+[----------] 1 test from FooTest
+[ RUN      ] FooTest.ShouldFail
+external/gtest/test/gtest_repeat_test.cc:96: Failure
+Value of: 1
+Expected: 0
+Expected failure.
+[  FAILED  ] FooTest.ShouldFail (0 ms)
+[----------] 1 test from FooTest (1 ms total)
+
+[----------] Global test environment tear-down
+[==========] 1 test from 1 test case ran. (1 ms total)
+[  PASSED  ] 0 tests.
+[  FAILED  ] 1 test, listed below:
+[  FAILED  ] FooTest.ShouldFail
+
+ 1 FAILED TEST
+
+Repeating all tests (iteration 4) . . .
+
+Note: Google Test filter = *ShouldFail
+[==========] Running 1 test from 1 test case.
+[----------] Global test environment set-up.
+[----------] 1 test from FooTest
+[ RUN      ] FooTest.ShouldFail
+external/gtest/test/gtest_repeat_test.cc:96: Failure
+Value of: 1
+Expected: 0
+Expected failure.
+[  FAILED  ] FooTest.ShouldFail (0 ms)
+[----------] 1 test from FooTest (0 ms total)
+
+[----------] Global test environment tear-down
+[==========] 1 test from 1 test case ran. (0 ms total)
+[  PASSED  ] 0 tests.
+[  FAILED  ] 1 test, listed below:
+[  FAILED  ] FooTest.ShouldFail
+
+ 1 FAILED TEST
+PASS
+root@android:/data/nativetest # 
+[  PASSED  ] 0 tests.
+[  FAILED  ] 1 test, listed below:
+[  FAILED  ] FooTest.ShouldFail
+
+ 1 FAILED TEST
+
+Repeating all tests (iteration 2) . . .
+
+Note: Google Test filter = *ShouldFail
+[==========] Running 1 test from 1 test case.
+[----------] Global test environment set-up.
+[----------] 1 test from FooTest
+[ RUN      ] FooTest.ShouldFail
+external/gtest/test/gtest_repeat_test.cc:96: Failure
+Value of: 1
+Expected: 0
+Expected failure.
+[  FAILED  ] FooTest.ShouldFail (0 ms)
+[----------] 1 test from FooTest (0 ms total)
+
+[----------] Global test environment tear-down
+[==========] 1 test from 1 test case ran. (0 ms total)
+[  PASSED  ] 0 tests.
+[  FAILED  ] 1 test, listed below:
+[  FAILED  ] FooTest.ShouldFail
+
+ 1 FAILED TEST
+
+Repeating all tests (iteration 3) . . .
+
+Note: Google Test filter = *ShouldFail
+[==========] Running 1 test from 1 test case.
+[----------] Global test environment set-up.
+[----------] 1 test from FooTest
+[ RUN      ] FooTest.ShouldFail
+external/gtest/test/gtest_repeat_test.cc:96: Failure
+Value of: 1
+Expected: 0
+Expected failure.
+[  FAILED  ] FooTest.ShouldFail (0 ms)
+[----------] 1 test from FooTest (1 ms total)
+
+[----------] Global test environment tear-down
+[==========] 1 test from 1 test case ran. (1 ms total)
+[  PASSED  ] 0 tests.
+[  FAILED  ] 1 test, listed below:
+[  FAILED  ] FooTest.ShouldFail
+
+ 1 FAILED TEST
+
+Repeating all tests (iteration 4) . . .
+
+Note: Google Test filter = *ShouldFail
+[==========] Running 1 test from 1 test case.
+[----------] Global test environment set-up.
+[----------] 1 test from FooTest
+[ RUN      ] FooTest.ShouldFail
+external/gtest/test/gtest_repeat_test.cc:96: Failure
+Value of: 1
+Expected: 0
+Expected failure.
+[  FAILED  ] FooTest.ShouldFail (0 ms)
+[----------] 1 test from FooTest (0 ms total)
+
+[----------] Global test environment tear-down
+[==========] 1 test from 1 test case ran. (0 ms total)
+[  PASSED  ] 0 tests.
+[  FAILED  ] 1 test, listed below:
+[  FAILED  ] FooTest.ShouldFail
+
+ 1 FAILED TEST
+PASS
+DUMP
diff --git a/dummy-libopenslestest b/dummy-libopenslestest
new file mode 100755
index 0000000..e3940c0
--- /dev/null
+++ b/dummy-libopenslestest
@@ -0,0 +1,41 @@
+#!/bin/sh
+cat << DUMP
+[==========] Running 2 tests from 1 test case.
+[----------] Global test environment set-up.
+[----------] 2 tests from MimeUri
+[ RUN      ] MimeUri.testPlayAbsPath
+        PrefetchEventCallback: received event 3
+        PrefetchEventCallback: Error while prefetching data, exiting
+system/media/wilhelm/tests/mimeUri_test.cpp:84: Failure
+Value of: false
+  Actual: false
+Expected: true
+Error: Failed to prefetch data in time, exiting
+system/media/wilhelm/tests/mimeUri_test.cpp:211: Failure
+Value of: false
+  Actual: false
+Expected: true
+[  FAILED  ] MimeUri.testPlayAbsPath
+[ RUN      ] MimeUri.testPlayfilePath
+        PrefetchEventCallback: received event 3
+        PrefetchEventCallback: Error while prefetching data, exiting
+system/media/wilhelm/tests/mimeUri_test.cpp:84: Failure
+Value of: false
+  Actual: false
+Expected: true
+Error: Failed to prefetch data in time, exiting
+system/media/wilhelm/tests/mimeUri_test.cpp:211: Failure
+Value of: false
+  Actual: false
+Expected: true
+[  FAILED  ] MimeUri.testPlayfilePath
+[----------] Global test environment tear-down
+[==========] 2 tests from 1 test case ran.
+[  PASSED  ] 0 tests.
+[  FAILED  ] 2 tests, listed below:
+[  FAILED  ] MimeUri.testPlayAbsPath
+[  FAILED  ] MimeUri.testPlayfilePath
+
+ 2 FAILED TESTS
+DUMP
+exit 1
diff --git a/json-format.h b/json-format.h
new file mode 100644
index 0000000..ded38a6
--- /dev/null
+++ b/json-format.h
@@ -0,0 +1,83 @@
+#ifndef JSON_FORMAT_H
+#define JSON_FORMAT_H
+
+#include <stdio.h>
+
+/* Token for JSON pieces.
+ *
+ * Used as simple command language to write JSON documents */
+enum json_token {
+    /* End of commands marker */
+    JSON_END,
+    /* Prints '{' */
+    JSON_DICT_START,
+    /* Prints '}' */
+    JSON_DICT_END,
+    /* Prints '[' */
+    JSON_ARRAY_START,
+    /* Prints ']' */
+    JSON_ARRAY_END,
+    /* Prints ',' */
+    JSON_COMMA,
+    /* Prints ':' */
+    JSON_COLON,
+    /* Prints '\n' but only in human readable mode */
+    JSON_NEWLINE,
+    /* Prints ' ' but only in human readable mode */
+    JSON_SPACE,
+    /* Consumes (int) and prints some indent but only in human readable mode */
+    JSON_INDENT,
+    /* Prints 'null' */
+    JSON_NULL,
+    /* Consumes (bool) and prints either 'true' or 'false' */
+    JSON_BOOLEAN,
+    /* Consumes (int) and prints it as a number */
+    JSON_NUMBER_INT,
+    /* Consumes (const char *) and prints it as a number */
+    JSON_NUMBER_STRING,
+    /* Consumes (const char *) and prints it as UTF-8 string */
+    JSON_STRING,
+    /* Just as JSON_STRING but without the final double quotes */
+    JSON_STRING_START,
+    /* Just as JSON_STRING but without both initial and final double qoutes */
+    JSON_STRING_NEXT,
+    /* Consumes (time_t) and formats that as JSON string with strftime() format
+     * of "%FT%TZ". This gives nice international timestmaps such as
+     * "2012-08-13T21:15:45Z" where 'T' separates date (YYYY-MM-DD) from time
+     * (HH:MM:SS) and 'Z' indicates the UTC timezone */
+    JSON_TIME_STAMP,
+    /* Consumes (struct timeval) and formats that as JSON string with printf()
+     * "%ds %dus" where the first part contains the number of seconds and the
+     * second part, number of microseconds. This format is used by LAVA to
+     * express duration */
+    JSON_TIME_DURATION,
+    /* Just as JSON_STRING but without the initial double quotes */
+    JSON_STRING_END,
+};
+
+
+/**
+ * Flag suitable for json_format(), causes the output to be generally human readable
+ **/
+#define JSON_READABLE   1
+
+/**
+ * Flag suitable for json_format(), causes a newline to be printed on JSON_END
+ **/
+#define JSON_EOL        2
+
+
+/**
+ * Render a sequence of JSON tokens on to the specified json_stream.
+ * The list must be terminated by JSON_END (zero).
+ *
+ * There are no checks for semantically valid output.
+ **/
+void
+__attribute__((sentinel))
+json_format(
+    FILE *stream,
+    unsigned flags, ...
+    );
+
+#endif
diff --git a/json-test.h b/json-test.h
new file mode 100644
index 0000000..eebcd03
--- /dev/null
+++ b/json-test.h
@@ -0,0 +1,6 @@
+#ifndef JSON_TEST_H
+#define JSON_TEST_H
+
+void json_test_all();
+
+#endif
diff --git a/src/bundle.c b/src/bundle.c
new file mode 100644
index 0000000..a89ea6a
--- /dev/null
+++ b/src/bundle.c
@@ -0,0 +1,203 @@
+#include "bundle.h"
+
+#include "json-format.h"
+
+
+void
+bundle_print_header(
+    FILE *bundle_stream,
+    unsigned flags,
+    const char *format
+    )
+{
+    /* This should print something like:
+     *
+     * <indent 0>{
+     * <indent 1>"format": $format,
+     * <indent 1>"test_runs": [
+     */
+    json_format(bundle_stream, flags,
+        /* Open the bundle */
+        JSON_DICT_START,
+        /* Add the format field */
+        JSON_NEWLINE, JSON_INDENT, 1,
+            JSON_STRING, "format", JSON_COLON, JSON_SPACE,
+            JSON_STRING, format, JSON_COMMA,
+        /* Add the test run field */
+        JSON_NEWLINE, JSON_INDENT, 1,
+            JSON_STRING, "test_runs", JSON_COLON, JSON_SPACE,
+            JSON_ARRAY_START,
+    NULL);
+}
+
+
+void
+bundle_print_footer(
+    FILE *bundle_stream,
+    unsigned flags
+    )
+{
+    /* This should print something like:
+     *
+     * <indent 1>]<newline>
+     * <indent 0>}<newline>
+     */
+    json_format(bundle_stream, flags | JSON_EOL,
+        JSON_NEWLINE, JSON_INDENT, 1, JSON_ARRAY_END,
+        JSON_NEWLINE, JSON_DICT_END,
+    NULL);
+}
+
+
+void
+bundle_print_test_run_header(
+    FILE *bundle_stream,
+    unsigned flags,
+    bool comma,
+    const char *analyzer_assigned_uuid,
+    time_t analyzer_assigned_date,
+    bool time_check_performed,
+    const char *test_id
+    )
+{
+    /* This should print something like:
+     *
+     * ,
+     */
+    if (comma)
+        json_format(bundle_stream, flags, JSON_COMMA, JSON_SPACE, NULL);
+    /* This should print something like:
+     *
+     * <indent 2>{
+     * <indent 3>"analyzer_assigned_uuid": $analyzer_assigned_uuid,
+     * <indent 3>"analyzer_assigned_date": $analyzer_assigned_date,
+     * <indent 3>"time_check_performed": false,
+     * <indent 3>"test_id": $test_id,
+     * <indent 3>"test_results": [
+     */
+    json_format(bundle_stream, flags,
+        JSON_NEWLINE, JSON_INDENT, 2, JSON_DICT_START,
+        JSON_NEWLINE, JSON_INDENT, 3,
+            JSON_STRING, "analyzer_assigned_uuid", JSON_COLON, JSON_SPACE,
+            JSON_STRING, analyzer_assigned_uuid, JSON_COMMA,
+        JSON_NEWLINE, JSON_INDENT, 3,
+            JSON_STRING, "analyzer_assigned_date", JSON_COLON, JSON_SPACE,
+            JSON_TIME_STAMP, analyzer_assigned_date, JSON_COMMA,
+        JSON_NEWLINE, JSON_INDENT, 3,
+            JSON_STRING, "time_check_performed", JSON_COLON, JSON_SPACE,
+            JSON_BOOLEAN, time_check_performed, JSON_COMMA, JSON_SPACE,
+        JSON_NEWLINE, JSON_INDENT, 3,
+            JSON_STRING, "test_id", JSON_COLON, JSON_SPACE,
+            JSON_STRING, test_id, JSON_COMMA,
+        JSON_NEWLINE, JSON_INDENT, 3,
+            JSON_STRING, "test_results", JSON_COLON, JSON_SPACE,
+            JSON_ARRAY_START,
+    NULL);
+}
+
+
+void
+bundle_print_test_run_footer(
+    FILE *bundle_stream,
+    unsigned flags
+    )
+{
+    /* This should print something like:
+     *
+     * <indent 3>]
+     * <indent 2>}
+     */
+    json_format(bundle_stream, flags,
+        JSON_NEWLINE, JSON_INDENT, 3, JSON_ARRAY_END,
+        JSON_NEWLINE, JSON_INDENT, 2, JSON_DICT_END,
+    NULL);
+}
+
+
+void
+bundle_print_test_result_header(
+    FILE *bundle_stream,
+    unsigned flags,
+    bool comma,
+    const char *test_case_id,
+    time_t timestamp
+    )
+{
+    if (comma)
+        json_format(bundle_stream, flags, JSON_COMMA, JSON_SPACE, NULL);
+    /* This should print something like:
+     *
+     * <indent 4>{
+     * <indent 5>"test_case_id": $test_case_id,
+     * <indent 5>"timestamp": $timestamp,
+     * <indent 5>"message": "
+     *
+     * note: the message value (string) is _not_ terminated
+     */
+    json_format(bundle_stream, flags,
+        JSON_NEWLINE, JSON_INDENT, 4, JSON_DICT_START,
+        JSON_NEWLINE, JSON_INDENT, 5,
+            JSON_STRING, "test_case_id", JSON_COLON, JSON_SPACE,
+            JSON_STRING, test_case_id, JSON_COMMA,
+        JSON_NEWLINE, JSON_INDENT, 5,
+            JSON_STRING, "timestamp", JSON_COLON, JSON_SPACE,
+            JSON_TIME_STAMP, timestamp, JSON_COMMA,
+        JSON_NEWLINE, JSON_INDENT, 5,
+            JSON_STRING, "message", JSON_COLON, JSON_SPACE,
+            JSON_STRING_START, "",
+    NULL);
+}
+
+
+
+void
+bundle_print_test_result_message(
+    FILE *bundle_stream,
+    unsigned flags,
+    const char *message
+    )
+{
+    json_format(bundle_stream, flags,
+        JSON_STRING_NEXT, message,
+        JSON_STRING_NEXT, "\n", NULL);
+}
+
+
+void
+bundle_print_test_result_footer(
+    FILE *bundle_stream,
+    unsigned flags,
+    const char *result,
+    const char *log_filename,
+    int log_lineno,
+    struct timeval duration
+    )
+{
+    /* This should print something like:
+     *
+     * ",
+     * <indent 5>"result": $result,
+     * <indent 5>"duration": $duration,
+     * <indent 5>"log_filename": $log_filename,
+     * <indent 5>"log_lineno": $log_lineno,
+     * <indent 4>}
+     *
+     * note: the first part terminates the quote left open by bundle_print_test_result_header()
+     */
+    json_format(bundle_stream, flags,
+        JSON_STRING_END, "", JSON_COMMA,
+        JSON_NEWLINE, JSON_INDENT, 5,
+            JSON_STRING, "result", JSON_COLON, JSON_SPACE,
+            JSON_STRING, result, JSON_COMMA,
+        JSON_NEWLINE, JSON_INDENT, 5,
+            JSON_STRING, "duration", JSON_COLON, JSON_SPACE,
+            JSON_TIME_DURATION, duration, JSON_COMMA,
+        JSON_NEWLINE, JSON_INDENT, 5,
+            JSON_STRING, "log_filename", JSON_COLON, JSON_SPACE,
+            JSON_STRING, log_filename, JSON_COMMA,
+        JSON_NEWLINE, JSON_INDENT, 5,
+            JSON_STRING, "log_lineno", JSON_COLON, JSON_SPACE,
+            JSON_NUMBER_INT, log_lineno,
+        JSON_NEWLINE, JSON_INDENT, 4, JSON_DICT_END,
+    NULL);
+}
diff --git a/src/debug.c b/src/debug.c
new file mode 100644
index 0000000..9ba4ec4
--- /dev/null
+++ b/src/debug.c
@@ -0,0 +1,35 @@
+#include "debug.h"
+
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdio.h>
+
+
+static bool debug_enabled = false;
+static FILE *debug_stream = NULL;
+
+
+void
+debug_enable(
+    FILE *stream
+    )
+{
+    debug_stream = stream;
+    debug_enabled = true;
+}
+
+
+void
+__attribute__((format(printf, 1, 2)))
+debug_log(
+    const char *format, ...
+    )
+{
+    va_list ap;
+    va_start(ap, format);
+    if (debug_enabled) {
+        fprintf(debug_stream, "LAVA> ");
+        vfprintf(debug_stream, format, ap);
+    }
+    va_end(ap);
+}
diff --git a/src/json-format.c b/src/json-format.c
new file mode 100644
index 0000000..1661a13
--- /dev/null
+++ b/src/json-format.c
@@ -0,0 +1,200 @@
+#include "json-format.h"
+
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <time.h>
+
+
+static
+void
+json_string(FILE *stream, const char *value) {
+    char c;
+    /* TODO: verify the string is valid UTF-8 */
+    while ((c = *value++)) {
+        switch (c) {
+            case '"':
+                fputc('\\', stream);
+                fputc('"', stream);
+                break;
+            case '\\':
+                fputc('\\', stream);
+                fputc('\\', stream);
+                break;
+#if 0
+            case '/':
+                fputc('\\', stream);
+                fputc('/', stream);
+                break;
+#endif
+            case '\b':
+                fputc('\\', stream);
+                fputc('b', stream);
+                break;
+            case '\f':
+                fputc('\\', stream);
+                fputc('f', stream);
+                break;
+            case '\n':
+                fputc('\\', stream);
+                fputc('n', stream);
+                break;
+            case '\r':
+                fputc('\\', stream);
+                fputc('r', stream);
+                break;
+            case '\t':
+                fputc('\\', stream);
+                fputc('t', stream);
+                break;
+            default:
+                fputc(c, stream);
+                break;
+        }
+    }
+}
+
+
+void
+__attribute__((sentinel))
+json_format(FILE *stream, unsigned flags, ...)
+{
+    va_list ap;
+    enum json_token token;
+    va_start(ap, flags);
+    do {
+        token = va_arg(ap, enum json_token);
+        switch (token) {
+            case JSON_END:
+                if (JSON_EOL & flags)
+                    fputc('\n', stream);
+                break;
+            case JSON_DICT_START:
+                fputc('{', stream);
+                break;
+            case JSON_DICT_END:
+                fputc('}', stream);
+                break;
+            case JSON_ARRAY_START:
+                fputc('[', stream);
+                break;
+            case JSON_ARRAY_END:
+                fputc(']', stream);
+                break;
+            case JSON_COMMA:
+                fputc(',', stream);
+                break;
+            case JSON_COLON:
+                fputc(':', stream);
+                break;
+            case JSON_NEWLINE:
+                if (JSON_READABLE & flags)
+                    fputc('\n', stream);
+                break;
+            case JSON_SPACE:
+                if (JSON_READABLE & flags)
+                    fputc(' ', stream);
+                break;
+            case JSON_INDENT:
+                {
+                    int depth;
+                    depth = va_arg(ap, int);
+                    if (JSON_READABLE & flags)
+                        while (depth-- > 0)
+                            fputs("    ", stream);
+                }
+                break;
+            case JSON_NULL:
+                fputs("null", stream);
+                break;
+            case JSON_BOOLEAN:
+                {
+                    bool value;
+                    value = va_arg(ap, int); /* _Bool is promoted to int when passed to ... */
+                    fputs(value ? "true" : "false", stream);
+                }
+                break;
+            case JSON_NUMBER_INT:
+                {
+                    int value;
+                    value = va_arg(ap, int);
+                    fprintf(stream, "%d", value);
+                }
+                break;
+            case JSON_NUMBER_STRING:
+                {
+                    const char *value;
+                    value = va_arg(ap, const char *);
+                    /* TODO: verify the string is a valid number */
+                    fputs(value, stream);
+                }
+                break;
+            case JSON_STRING:
+                {
+                    const char *value;
+                    value = va_arg(ap, const char *);
+                    fputc('"', stream);
+                    json_string(stream, value);
+                    fputc('"', stream);
+                }
+                break;
+            case JSON_STRING_START:
+                {
+                    const char *value;
+                    value = va_arg(ap, const char *);
+                    fputc('"', stream);
+                    json_string(stream, value);
+                }
+                break;
+            case JSON_STRING_NEXT:
+                {
+                    const char *value;
+                    value = va_arg(ap, const char *);
+                    json_string(stream, value);
+                }
+                break;
+            case JSON_STRING_END:
+                {
+                    const char *value;
+                    value = va_arg(ap, const char *);
+                    json_string(stream, value);
+                    fputc('"', stream);
+                }
+                break;
+            case JSON_TIME_STAMP:
+                {
+                    time_t value;
+                    struct tm tm;
+                    char buf[128];
+                    value = va_arg(ap, time_t);
+                    gmtime_r(&value, &tm);
+                    strftime(buf, sizeof buf, "%FT%TZ", &tm);
+                    fputc('"', stream);
+                    json_string(stream, buf);
+                    fputc('"', stream);
+                }
+                break;
+            case JSON_TIME_DURATION:
+                {
+                    struct timeval value;
+                    const int SECONDS_IN_DAY = 60 * 60 * 24;
+                    char buf[128];
+                    value = va_arg(ap, struct timeval);
+                    snprintf(buf, sizeof buf, "%0ldd %0lds %0ldus",
+                             (long)(value.tv_sec) / SECONDS_IN_DAY,
+                             (long)(value.tv_sec) % SECONDS_IN_DAY,
+                             (long)value.tv_usec);
+                    fputc('"', stream);
+                    json_string(stream, buf);
+                    fputc('"', stream);
+                }
+                break;
+            default:
+                abort();
+                break;
+        }
+    } while (token != JSON_END);
+    va_end(ap);
+}
diff --git a/src/json-test.c b/src/json-test.c
new file mode 100644
index 0000000..014e549
--- /dev/null
+++ b/src/json-test.c
@@ -0,0 +1,111 @@
+#include "json-test.h"
+
+#include "json-format.h"
+
+#include <limits.h>
+#include <stdbool.h>
+#include <stdio.h>
+
+#define MSG_INFO    "[==========] "
+#define MSG_DEBUG   "[----------] "
+
+
+#define TEST_HEADER() printf(MSG_INFO "Output of: %s\n", __FUNCTION__)
+#define TEST_NEXT() printf("\n" MSG_DEBUG "Additional line of output\n")
+#define TEST_FOOTER() printf("\n" MSG_DEBUG "(end of test case)\n")
+
+
+static void test_nothing() {
+    TEST_HEADER();
+    json_format(stdout, 0, NULL);
+    TEST_FOOTER();
+}
+
+
+static void test_dict() {
+    TEST_HEADER();
+    json_format(stdout, 0, JSON_DICT_START, JSON_COLON, JSON_DICT_END, NULL);
+    TEST_FOOTER();
+}
+
+
+static void test_array() {
+    TEST_HEADER();
+    json_format(stdout, 0, JSON_ARRAY_START, JSON_COMMA, JSON_ARRAY_END, NULL);
+    TEST_FOOTER();
+}
+
+
+static void test_null() {
+    TEST_HEADER();
+    json_format(stdout, 0, JSON_NULL, NULL);
+    TEST_FOOTER();
+}
+
+
+static void test_bool() {
+    TEST_HEADER();
+    json_format(stdout, 0, JSON_BOOLEAN, true, NULL);
+    TEST_NEXT();
+    json_format(stdout, 0, JSON_BOOLEAN, false, NULL);
+    TEST_FOOTER();
+}
+
+
+static void test_int() {
+    TEST_HEADER();
+    json_format(stdout, 0, JSON_NUMBER_INT, 0, NULL);
+    TEST_NEXT();
+    json_format(stdout, 0, JSON_NUMBER_INT, INT_MAX, NULL);
+    TEST_NEXT();
+    json_format(stdout, 0, JSON_NUMBER_INT, INT_MIN, NULL);
+    TEST_FOOTER();
+}
+
+
+static void test_int_str() {
+    TEST_HEADER();
+    json_format(stdout, 0, JSON_NUMBER_STRING, "0", NULL);
+    TEST_NEXT();
+    json_format(stdout, 0, JSON_NUMBER_STRING, "15.2", NULL);
+    TEST_FOOTER();
+}
+
+
+static void test_str() {
+    TEST_HEADER();
+    json_format(stdout, 0, JSON_STRING, "hello world", NULL);
+    TEST_NEXT();
+    json_format(stdout, 0, JSON_STRING, "double quote:   \"", NULL);
+    TEST_NEXT();
+    json_format(stdout, 0, JSON_STRING, "forward slash:  \\", NULL);
+    TEST_NEXT();
+    json_format(stdout, 0, JSON_STRING, "backspace:      \b", NULL);
+    TEST_NEXT();
+    json_format(stdout, 0, JSON_STRING, "form feed:      \f", NULL);
+    TEST_NEXT();
+    json_format(stdout, 0, JSON_STRING, "new line:       \n", NULL);
+    TEST_NEXT();
+    json_format(stdout, 0, JSON_STRING, "carriage return:\r", NULL);
+    TEST_NEXT();
+    json_format(stdout, 0, JSON_STRING, "tab:            \t", NULL);
+    TEST_NEXT();
+    json_format(stdout, 0, JSON_STRING, "single quote:   ' (should be UNQUOTED)", NULL);
+    TEST_NEXT();
+    json_format(stdout, 0, JSON_STRING, "", NULL);
+    TEST_NEXT();
+    json_format(stdout, 0, JSON_STRING, "0", NULL);
+    TEST_FOOTER();
+}
+
+
+void json_test_all() {
+    test_nothing();
+    test_dict();
+    test_array();
+    test_null();
+    test_bool();
+    test_int();
+    test_int_str();
+    test_str();
+}
diff --git a/src/lava-wrapper.c b/src/lava-wrapper.c
new file mode 100644
index 0000000..cbb8ab9
--- /dev/null
+++ b/src/lava-wrapper.c
@@ -0,0 +1,354 @@
+/**
+ * Copyright (c) Zygmunt Krynicki <zygmunt.krynicki@linaro.org> 2012
+ **/
+
+#include <assert.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "bundle.h"
+#include "debug.h"
+#include "json-format.h"
+#include "json-test.h"
+#include "text-utils.h"
+#include "uuid.h"
+
+/** Patterns used to discover gtest-like output */
+#define GTEST_LOG1_PATTERN      "[==========] "
+#define GTEST_LOG2_PATTERN      "[----------] "
+#define GTEST_RUN_PATTERN       "[ RUN      ] "
+#define GTEST_OK_PATTERN        "[       OK ] "
+#define GTEST_PASSED_PATTERN    "[  PASSED  ] "
+#define GTEST_FAILED_PATTERN    "[  FAILED  ] "
+
+enum gtest_line_type {
+    GTEST_LINE_LOG1,
+    GTEST_LINE_LOG2,
+    GTEST_LINE_RUN,
+    GTEST_LINE_OK,
+    GTEST_LINE_PASSED,
+    GTEST_LINE_FAILED,
+    GTEST_LINE_OTHER
+};
+
+
+static
+void
+process_output(
+    FILE *bundle_stream,
+    FILE *child_stream,
+    unsigned flags
+    )
+{
+    char *line = NULL;
+    size_t line_capacity = 0;
+    char *line_sans_pattern = NULL;
+    int lineno = 0;
+    enum gtest_line_type line_type;
+
+    int test_run_cnt = 0;
+    char *previous_test_id = NULL;
+    size_t previous_test_id_capacity = 0;
+
+    int test_result_cnt = 0;
+    bool test_result_open = false;
+    const char *test_result_result = "unknown";
+    bool test_result_ready = false;
+    struct timeval test_result_start = { .tv_sec = 0, .tv_usec = 0};
+
+    struct timeval line_timestamp;
+    do {
+        /* Read a line from the child stream */
+        if (text_readline(&line, &line_capacity, child_stream) != 0) {
+            perror("text_readline()");
+            abort();
+        }
+        lineno += 1;
+        /* Measure the time, we may need it in several spots below */
+        gettimeofday(&line_timestamp, NULL);
+        /* We need to classify each line, there are a number of options:
+         * 0) status lines (either '=' or '-')
+         * 1) a 'RUN' line is where we start to run a test case
+         * 2) a 'OK' line is when the test passes
+         * 3) a 'FAILED' line is when the test has failed (but it must be
+         *    paired with 'RUN' line just directly before.
+         * 4) other lines, arbitrary messages generated by the the case
+         * 5) a summary line like 'PASSED' or FAILED (we ignore those) */
+        if (strstr(line, GTEST_LOG1_PATTERN)) {
+            line_type = GTEST_LINE_LOG1;
+            line_sans_pattern = line + strlen(GTEST_LOG1_PATTERN);
+        } else if (strstr(line, GTEST_LOG2_PATTERN)) {
+            line_type = GTEST_LINE_LOG2;
+            line_sans_pattern = line + strlen(GTEST_LOG2_PATTERN);
+        } else if (strstr(line, GTEST_RUN_PATTERN)) {
+            line_type = GTEST_LINE_RUN;
+            line_sans_pattern = line + strlen(GTEST_RUN_PATTERN);
+        } else if (strstr(line, GTEST_OK_PATTERN)) {
+            line_type = GTEST_LINE_OK;
+            line_sans_pattern = line + strlen(GTEST_OK_PATTERN);
+        } else if (strstr(line, GTEST_PASSED_PATTERN)) {
+            line_type = GTEST_LINE_PASSED;
+            line_sans_pattern = line + strlen(GTEST_PASSED_PATTERN);
+        } else if (strstr(line, GTEST_FAILED_PATTERN)) {
+            line_type = GTEST_LINE_FAILED;
+            line_sans_pattern = line + strlen(GTEST_FAILED_PATTERN);
+        } else {
+            line_type = GTEST_LINE_OTHER;
+            line_sans_pattern = line;
+        }
+        /* Now that we know what kind of line we have we can run our simple state
+         * machine to react to the data */
+        switch (line_type) {
+            case GTEST_LINE_LOG1:
+            case GTEST_LINE_LOG2:
+            case GTEST_LINE_PASSED:
+                debug_log("message from gtest: %s\n", line_sans_pattern);
+                break;
+            case GTEST_LINE_FAILED:
+                /* FAILED line is printed when a test case fails
+                 * and we need to keep track of that. Sadly, it is
+                 * also printed as a 'summary' line at the end of a test run.
+                 *
+                 * To differentiate them we will treat any FAILED line that was
+                 * printed after a RUN line was seen (which opens a test case)
+                 * as the terminator */
+                if (test_result_open) {
+                    debug_log("test case finished unsuccessfully\n");
+                    /* This test case has failed */
+                    test_result_result = "fail";
+                    /* And we have enough data to print the test case footer */
+                    test_result_ready = true;
+                } else
+                    debug_log("message from gtest: %s\n", line_sans_pattern);
+                break;
+            case GTEST_LINE_RUN:
+                {
+                    char *dot;
+                    char *test_case_id;
+                    char *test_id;
+                    /* We need to terminate previous test case that has probably
+                     * crashed bad enough not to print the failure record */
+                    if (test_result_open) {
+                        struct timeval duration;
+                        duration.tv_sec = line_timestamp.tv_sec - test_result_start.tv_sec;
+                        duration.tv_usec = line_timestamp.tv_usec - test_result_start.tv_usec;
+                        debug_log("closing previous test result\n");
+                        bundle_print_test_result_footer(
+                            bundle_stream, flags,
+                            test_result_result, "<stdout>", lineno, duration);
+                        test_result_ready = false;
+                        test_result_open = false;
+                    }
+                    /* Parse the RUN line. Try to find the test case id (it
+                     * should be right after the dot component that separates
+                     * test id from test case id */
+                    dot = strchr(line_sans_pattern, '.');
+                    if (dot != NULL) {
+                        /* The test case has a dot component. We can use that to
+                         * discover the test id and test case id. Let's just split
+                         * it there by modifying the dot to a nul character */
+                        *dot = 0;
+                        test_id = line_sans_pattern;
+                        test_case_id = dot + 1;
+                    } else {
+                        /* For some reason the test case did not have the dot that
+                         * otherwise separates test id from test case id. */
+                        test_id = "unknown-gtest-based-test";
+                        test_case_id = line_sans_pattern;
+                    }
+                    /* We may need to print the test run header.
+                     *
+                     * This can happen when we see a change in test_id or when
+                     * we need the initial header for the very first test case. */
+                    if (previous_test_id == NULL || strcmp(previous_test_id, test_id) != 0) {
+                        char analyzer_assigned_uuid[UUID_ASCII_LEN + 1];
+                        const bool time_check_performed = false;
+                        /* Close the previous test run if it is open */
+                        if (test_run_cnt > 0) {
+                            debug_log("closing previous test run\n");
+                            bundle_print_test_run_footer(bundle_stream, flags);
+                        }
+                        /* Copy the old test_id so that we can keep comparing it */
+                        if (text_copy(&previous_test_id,
+                                       &previous_test_id_capacity, test_id) != 0) {
+                            perror("text_copy()");
+                            abort();
+                        }
+                        /* Generate an UUID */
+                        if (uuid_gen(analyzer_assigned_uuid) != 0) {
+                            perror("uuid_gen()");
+                            abort();
+                        }
+                        debug_log("generated analyzer_assigned_uuid: %s\n", analyzer_assigned_uuid);
+                        /* Open another test run */
+                        debug_log("starting new test run for test_id: %s\n", test_id);
+                        bundle_print_test_run_header(
+                            bundle_stream, flags, test_run_cnt > 0,
+                            analyzer_assigned_uuid, line_timestamp.tv_sec,
+                            time_check_performed, test_id);
+                        /* Keep track of the number of test runs we have printed */
+                        test_run_cnt += 1;
+                        /* Reset the number of test cases we've printed in this test run */
+                        test_result_cnt = 0;
+                    }
+                    debug_log("starting new test result for test_case_id: %s\n", test_case_id);
+                    test_result_start = line_timestamp;
+                    /* Print the header of the test result */
+                    bundle_print_test_result_header(
+                        bundle_stream, flags, test_result_cnt > 0, test_case_id,
+                        test_result_start.tv_sec);
+                    /* Keep track of subsequent test cases in one test run */
+                    test_result_cnt += 1;
+                    /* Keep track of output state */
+                    test_result_open = true;
+                    /* Reset result to unknown */
+                    test_result_result = "unknown";
+                }
+                break;
+            case GTEST_LINE_OK:
+                debug_log("test case finished successfully\n");
+                /* This test case has passed */
+                test_result_result = "pass";
+                /* And we have enough data to print the test case footer */
+                test_result_ready = true;
+                break;
+            case GTEST_LINE_OTHER:
+                /* Once we've seen a RUN line that opens a test case
+                 * we want to capture any output as test case message. */
+                if (test_result_open) {
+                    debug_log("message from test: %s\n", line_sans_pattern);
+                    bundle_print_test_result_message(
+                        bundle_stream, flags,
+                        line_sans_pattern);
+                } else
+                    debug_log("message from gtest: %s\n", line_sans_pattern);
+                break;
+            default:
+                abort();
+        }
+        /* Write out the test result once we have everything */
+        if (test_result_ready) {
+            struct timeval duration;
+            duration.tv_sec = line_timestamp.tv_sec - test_result_start.tv_sec;
+            duration.tv_usec = line_timestamp.tv_usec - test_result_start.tv_usec;
+            debug_log("closing previous test result\n");
+            bundle_print_test_result_footer(
+                bundle_stream, flags,
+                test_result_result, "<stdout>", lineno, duration);
+            test_result_ready = false;
+            test_result_open = false;
+        }
+    } while (!feof(child_stream));
+    /* We may need to terminate the final test result */
+    if (test_result_open) {
+        struct timeval duration;
+        duration.tv_sec = line_timestamp.tv_sec - test_result_start.tv_sec;
+        duration.tv_usec = line_timestamp.tv_usec - test_result_start.tv_usec;
+        debug_log("closing previous (final) test result\n");
+        bundle_print_test_result_footer(
+            bundle_stream, flags,
+            test_result_result, "<stdout>", lineno, duration);
+    }
+    /* We may need to terminate the final test run */
+    if (test_run_cnt > 0) {
+        debug_log("closing previous (final) test run\n");
+        bundle_print_test_run_footer(bundle_stream, flags);
+    }
+    /* Reclaim line cache */
+    if (line)
+        free(line);
+    /* Reclaim memory used to keep the previous test_id */
+    if (previous_test_id)
+        free(previous_test_id);
+}
+
+
+static void
+run_test_prog(
+    const char *test_prog,
+    const char *bundle_name,
+    unsigned flags
+    )
+{
+    FILE *child_stream;
+    FILE *bundle_stream;
+    /* Open the bundle file */
+    bundle_stream = fopen(bundle_name, "wt");
+    if (bundle_stream == NULL) {
+        perror("fopen()");
+        abort();
+    }
+    /* Open the child process */
+    child_stream = popen(test_prog, "r");
+    if (child_stream == NULL) {
+        perror("popen()");
+        abort();
+    }
+    /* Write the bundle header */
+    debug_log("Saving bundle to %s\n", bundle_name);
+    bundle_print_header(bundle_stream, flags, "Dashboard Bundle Format 1.3");
+    /* Process the output data */
+    process_output(bundle_stream, child_stream, flags);
+    /* Terminate the test process */
+    pclose(child_stream);
+    /* Write the bundle footer */
+    bundle_print_footer(bundle_stream, flags);
+}
+
+
+static void show_usage() {
+    printf("Usage: android-lava-wrapper [-o BUNDLE] [-r] [-d] <executable>\n"
+           "       android-lava-wrapper -t\n"
+           "\n"
+           "The first form runs the specified executable and parses\n"
+           "the output as a gtest-based test.\n"
+           "-r creates a human-readable bundle\n"
+           "-o sets the name of the bundle, by default it is bundle.json\n"
+           "-d enables debugging\n"
+           "\n"
+           "The second form runs the internal self-test\n");
+}
+
+
+int main(int argc, char *argv[]) {
+    int opt;
+    unsigned flags = 0;
+    const char *bundle_pathname = "bundle.json";
+    while ((opt = getopt(argc, argv, "drto:")) != -1) {
+        switch (opt) {
+            case 'd':
+                debug_enable(stderr);
+                break;
+            case 'r':
+                flags |= JSON_READABLE;
+                break;
+            case 't':
+                json_test_all();
+                return 0;
+            case 'o':
+                bundle_pathname = optarg;
+                break;
+            default:
+                show_usage();
+                printf("\nUnsupported option: %c\n", optopt);
+                return 1;
+        }
+    }
+    if (optind >= argc) {
+        show_usage();
+        printf("\nYou have to pass the pathname of the executable to run\n");
+        return 1;
+    } else {
+        run_test_prog(argv[optind], bundle_pathname, flags);
+        return 0;
+    }
+}
diff --git a/src/text-utils.c b/src/text-utils.c
new file mode 100644
index 0000000..a832f3f
--- /dev/null
+++ b/src/text-utils.c
@@ -0,0 +1,122 @@
+#include "text-utils.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "debug.h"
+
+
+int
+text_readline(
+    char **buffer_ptr,
+    size_t *capacity_ptr,
+    FILE *stream
+    )
+{
+    int c;
+    size_t size = 0;
+    char *buffer;
+    size_t capacity;
+    int error;
+    /* Ensure the caller passed line and capacity pointers */
+    if (buffer_ptr == NULL || capacity_ptr == NULL)
+        return EINVAL;
+    buffer = *buffer_ptr;
+    capacity = *capacity_ptr;
+    do {
+        text_grow_buffer(&buffer, &capacity, size + 1);
+        /* Do the IO, read one byte at a time */
+        c = fgetc(stream);
+        if (c != EOF && c != '\n') {
+            buffer[size++] = (char)c;
+        } else {
+            buffer[size++] = 0;
+            break;
+        }
+    } while (1);
+    error = 0;
+out:
+    /* Copy back the line and capacity */
+    *buffer_ptr = buffer;
+    *capacity_ptr = capacity;
+    return error;
+}
+
+
+int
+text_copy(
+    char **buffer_ptr,
+    size_t *capacity_ptr,
+    const char *text
+    )
+{
+    size_t text_len;
+    int error;
+    /* Ensure the caller passed line and capacity pointers */
+    if (buffer_ptr == NULL || capacity_ptr == NULL)
+        return EINVAL;
+    if (text == NULL)
+        return EINVAL;
+    text_len = strlen(text);
+    error = text_grow_buffer(buffer_ptr, capacity_ptr, text_len + 1);
+    if (error != 0)
+        return error;
+    strncpy(*buffer_ptr, text, *capacity_ptr);
+    return 0;
+}
+
+
+/**
+ * Find a good reallocation size for the current size
+ **/
+static
+size_t
+next_best_capacity(size_t size) {
+    const size_t good_sizes[] = {128, 512, 4 * 1024, 16 * 1024};
+    size_t i;
+    /* Try to use one of the good sizes */
+    for (i=0; i<sizeof(good_sizes) / sizeof(*good_sizes); ++i)
+        if (size < good_sizes[i])
+            return good_sizes[i];
+    /* Or just double the current size */
+    return size * 2;
+}
+
+
+int
+text_grow_buffer(
+    char **buffer_ptr,
+    size_t *capacity_ptr,
+    size_t min_capacity
+    )
+{
+    char *buffer, *new_buffer;
+    size_t capacity, new_capacity;
+    /* Ensure the caller passed line and capacity pointers */
+    if (buffer_ptr == NULL || capacity_ptr == NULL)
+        return EINVAL;
+    /* Load current buffer pointer and size */
+    buffer = *buffer_ptr;
+    capacity = *capacity_ptr;
+    /* Keep growing the buffer as required */
+    while (capacity <= min_capacity) {
+        new_capacity = next_best_capacity(min_capacity);
+        debug_log("Resizing text buffer from %zd to %zd bytes\n",
+                  capacity, new_capacity);
+        new_buffer = realloc(buffer, new_capacity);
+        if (new_buffer != NULL) {
+            debug_log("Resize complete, old buffer %p, new buffer %p\n",
+                      buffer, new_buffer);
+            buffer = new_buffer;
+            capacity = new_capacity;
+        } else {
+            debug_log("Unable to resize line buffer!\n");
+            return ENOMEM;
+        }
+    }
+    /* Copy back the new buffer pointer and new capacity */
+    *buffer_ptr = buffer;
+    *capacity_ptr = capacity;
+    return 0;
+}
diff --git a/src/uuid.c b/src/uuid.c
new file mode 100644
index 0000000..cae0ee5
--- /dev/null
+++ b/src/uuid.c
@@ -0,0 +1,29 @@
+#include "uuid.h"
+
+#include <errno.h>
+#include <stdio.h>
+
+int
+uuid_gen(char *buf) {
+   FILE *stream = NULL;
+   int retval;
+   size_t num_read;
+
+   stream = fopen(UUID_PATHNAME, "rt");
+   if (!stream) {
+       retval = errno;
+       goto out;
+   }
+   num_read = fread(buf, 1, UUID_ASCII_LEN, stream);
+   if (num_read != UUID_ASCII_LEN) {
+       retval = errno ? errno : EIO;
+       goto out;
+   } else {
+       buf[UUID_ASCII_LEN] = 0;
+       retval = 0;
+   }
+out:
+   if (stream != NULL)
+       fclose(stream);
+   return retval;
+}
diff --git a/text-utils.h b/text-utils.h
new file mode 100644
index 0000000..cf20b8d
--- /dev/null
+++ b/text-utils.h
@@ -0,0 +1,51 @@
+#ifndef TEXT_UTILS_H
+#define TEXT_UTILS_H
+
+#include <stdio.h>
+
+/**
+ * Read one line from stream.
+ *
+ * This function allocates the memory on demand, reusing any previous
+ * buffers if possible. The buffer data is kept in two variables that must
+ * be provided by the caller.
+ *
+ * Uses text_grow_buffer() and can fail the same way.
+ **/
+int
+text_readline(
+    char **buffer_ptr,
+    size_t *capacity_ptr,
+    FILE *stream
+    );
+
+
+/**
+ * Copy specified text into the specified buffer.
+ *
+ * Uses text_grow_buffer() and can fail the same way.
+ **/
+int
+text_copy(
+    char **buffer_ptr,
+    size_t *capacity_ptr,
+    const char *text
+    );
+
+
+/**
+ * Grow the text buffer so that it can hold at lest the specified required
+ * capacity of bytes.
+ *
+ * Returns 0 on success. On out-of-memory error the old line buffer and
+ * capacity are left intact and it is expected that the caller will free
+ * that buffer.
+ **/
+int
+text_grow_buffer(
+    char **buffer_ptr,
+    size_t *capacity_ptr,
+    size_t min_capacity
+    );
+
+#endif
diff --git a/uuid.h b/uuid.h
new file mode 100644
index 0000000..8ca4d3d
--- /dev/null
+++ b/uuid.h
@@ -0,0 +1,24 @@
+#ifndef UUID_H
+#define UUID_H
+
+/**
+ * Pathname of a special file that generates UUID on read
+ **/
+#define UUID_PATHNAME "/proc/sys/kernel/random/uuid"
+
+/**
+ * Number of bytes, not including the final nul byte, required to store an UUID in text form
+ **/
+#define UUID_ASCII_LEN 36
+
+/**
+ * Generate a random UUID by reading from UUID_PATHNAME
+ *
+ * The buffer must have at least UUID_ASCII_LEN + 1 bytes.
+ * Returns 0 on success.
+ **/
+
+int
+uuid_gen(char *buf);
+
+#endif