You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mynewt.apache.org by st...@apache.org on 2016/09/29 01:34:11 UTC

[04/49] incubator-mynewt-core git commit: directory re-org

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/6a7432f4/sys/stats/pkg.yml
----------------------------------------------------------------------
diff --git a/sys/stats/pkg.yml b/sys/stats/pkg.yml
index bde92c8..67c95fd 100644
--- a/sys/stats/pkg.yml
+++ b/sys/stats/pkg.yml
@@ -25,10 +25,10 @@ pkg.keywords:
     - statistics
 
 pkg.deps:
-    - libs/os
+    - kernel/os
     - libs/util
 pkg.deps.STATS_CLI:
-    - libs/shell
+    - sys/shell
 pkg.req_apis.STATS_NEWTMGR:
     - newtmgr
 

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/6a7432f4/sys/sysinit/pkg.yml
----------------------------------------------------------------------
diff --git a/sys/sysinit/pkg.yml b/sys/sysinit/pkg.yml
index e728ccf..e6f8225 100644
--- a/sys/sysinit/pkg.yml
+++ b/sys/sysinit/pkg.yml
@@ -25,7 +25,7 @@ pkg.keywords:
     - init
 
 pkg.deps:
-    - libs/os
+    - kernel/os
     - libs/util
 
 pkg.syscfg_defs:

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/6a7432f4/test/crash_test/include/crash_test/crash_test.h
----------------------------------------------------------------------
diff --git a/test/crash_test/include/crash_test/crash_test.h b/test/crash_test/include/crash_test/crash_test.h
new file mode 100644
index 0000000..73135fd
--- /dev/null
+++ b/test/crash_test/include/crash_test/crash_test.h
@@ -0,0 +1,27 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+#ifndef __CRASH_TEST_H__
+#define __CRASH_TEST_H__
+
+/*
+ * Adds the crash commands to your shell/newtmgr.
+ */
+int crash_test_init(void);
+
+#endif /* __CRASH_TEST_H__ */

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/6a7432f4/test/crash_test/pkg.yml
----------------------------------------------------------------------
diff --git a/test/crash_test/pkg.yml b/test/crash_test/pkg.yml
new file mode 100644
index 0000000..75afa8c
--- /dev/null
+++ b/test/crash_test/pkg.yml
@@ -0,0 +1,42 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#  http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+pkg.name: test/crash_test
+pkg.description: Generate different kinds of faults
+pkg.homepage: "http://mynewt.apache.org/"
+pkg.keywords:
+
+pkg.deps.CRASH_TEST_CLI:
+    - sys/shell
+pkg.req_apis.CRASH_TEST_CLI:
+    - console
+pkg.req_apis.CRASH_TEST_NEWTMGR:
+    - newtmgr
+
+pkg.deps.CRASH_TEST_NEWTMGR:
+    - libs/newtmgr
+    - encoding/json
+
+pkg.syscfg_defs:
+    CRASH_TEST_CLI:
+        description: 'TBD'
+        value: 1
+    CRASH_TEST_NEWTMGR:
+        description: 'TBD'
+        value: 1

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/6a7432f4/test/crash_test/src/crash_cli.c
----------------------------------------------------------------------
diff --git a/test/crash_test/src/crash_cli.c b/test/crash_test/src/crash_cli.c
new file mode 100644
index 0000000..4d49bef
--- /dev/null
+++ b/test/crash_test/src/crash_cli.c
@@ -0,0 +1,49 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "syscfg/syscfg.h"
+
+#if MYNEWT_VAL(CRASH_TEST_CLI)
+#include <inttypes.h>
+#include <os/os.h>
+#include <console/console.h>
+#include <shell/shell.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "crash_test/crash_test.h"
+#include "crash_test_priv.h"
+
+static int crash_cli_cmd(int argc, char **argv);
+struct shell_cmd crash_cmd_struct = {
+    .sc_cmd = "crash",
+    .sc_cmd_func = crash_cli_cmd
+};
+
+static int
+crash_cli_cmd(int argc, char **argv)
+{
+    if (argc >= 2 && crash_device(argv[1]) == 0) {
+        return 0;
+    }
+    console_printf("Usage crash [div0|jump0|ref0|assert]\n");
+    return 0;
+}
+
+#endif /* MYNEWT_VAL(CRASH_TEST_CLI) */

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/6a7432f4/test/crash_test/src/crash_nmgr.c
----------------------------------------------------------------------
diff --git a/test/crash_test/src/crash_nmgr.c b/test/crash_test/src/crash_nmgr.c
new file mode 100644
index 0000000..d9bf4b0
--- /dev/null
+++ b/test/crash_test/src/crash_nmgr.c
@@ -0,0 +1,75 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "syscfg/syscfg.h"
+
+#if MYNEWT_VAL(CRASH_TEST_NEWTMGR)
+
+#include <string.h>
+
+#include "newtmgr/newtmgr.h"
+#include "json/json.h"
+#include "console/console.h"
+
+#include "crash_test/crash_test.h"
+#include "crash_test_priv.h"
+
+static int crash_test_nmgr_write(struct nmgr_jbuf *);
+
+static const struct nmgr_handler crash_test_nmgr_handler[] = {
+    [0] = { crash_test_nmgr_write, crash_test_nmgr_write }
+};
+
+struct nmgr_group crash_test_nmgr_group = {
+    .ng_handlers = (struct nmgr_handler *)crash_test_nmgr_handler,
+    .ng_handlers_count = 1,
+    .ng_group_id = NMGR_GROUP_ID_CRASH
+};
+
+static int
+crash_test_nmgr_write(struct nmgr_jbuf *njb)
+{
+    char tmp_str[64];
+    const struct json_attr_t attr[2] = {
+        [0] = {
+            .attribute = "t",
+            .type = t_string,
+            .addr.string = tmp_str,
+            .len = sizeof(tmp_str)
+        },
+        [1] = {
+            .attribute = NULL
+        }
+    };
+    int rc;
+
+    rc = json_read_object(&njb->njb_buf, attr);
+    if (rc) {
+        rc = NMGR_ERR_EINVAL;
+    } else {
+        rc = crash_device(tmp_str);
+        if (rc) {
+            rc = NMGR_ERR_EINVAL;
+        }
+    }
+    nmgr_jbuf_setoerr(njb, rc);
+    return 0;
+}
+
+#endif /* MYNEWT_VAL(CRASH_TEST_NEWTMGR) */

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/6a7432f4/test/crash_test/src/crash_test.c
----------------------------------------------------------------------
diff --git a/test/crash_test/src/crash_test.c b/test/crash_test/src/crash_test.c
new file mode 100644
index 0000000..a6e2ea2
--- /dev/null
+++ b/test/crash_test/src/crash_test.c
@@ -0,0 +1,72 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+#include <inttypes.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+#include "syscfg/syscfg.h"
+#include "os/os.h"
+#include "console/console.h"
+
+#include "crash_test/crash_test.h"
+#include "crash_test_priv.h"
+
+#if MYNEWT_VAL(CRASH_TEST_CLI)
+#include "shell/shell.h"
+#endif
+#if MYNEWT_VAL(CRASH_TEST_NEWTMGR)
+#include "newtmgr/newtmgr.h"
+#endif
+
+int
+crash_device(char *how)
+{
+    volatile int val1, val2, val3;
+
+    if (!strcmp(how, "div0")) {
+
+        val1 = 42;
+        val2 = 0;
+
+        val3 = val1 / val2;
+        console_printf("42/0 = %d\n", val3);
+    } else if (!strcmp(how, "jump0")) {
+        ((void (*)(void))0)();
+    } else if (!strcmp(how, "ref0")) {
+        val1 = *(int *)0;
+    } else if (!strcmp(how, "assert")) {
+        assert(0);
+    } else {
+        return -1;
+    }
+    return 0;
+}
+
+int
+crash_test_init(void)
+{
+#if MYNEWT_VAL(CRASH_TEST_CLI)
+    shell_cmd_register(&crash_cmd_struct);
+#endif
+#if MYNEWT_VAL(CRASH_TEST_NEWTMGR)
+    nmgr_group_register(&crash_test_nmgr_group);
+#endif
+    return 0;
+}

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/6a7432f4/test/crash_test/src/crash_test_priv.h
----------------------------------------------------------------------
diff --git a/test/crash_test/src/crash_test_priv.h b/test/crash_test/src/crash_test_priv.h
new file mode 100644
index 0000000..09dc5a3
--- /dev/null
+++ b/test/crash_test/src/crash_test_priv.h
@@ -0,0 +1,31 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+#ifndef __CRASH_TEST_PRIV_H__
+#define __CRASH_TEST_PRIV_H__
+
+#if MYNEWT_VAL(CRASH_TEST_CLI)
+extern struct shell_cmd crash_cmd_struct;
+#endif
+#if MYNEWT_VAL(CRASH_TEST_NEWTMGR)
+extern struct nmgr_group crash_test_nmgr_group;
+#endif
+
+int crash_device(char *how);
+
+#endif /* __CRASH_TEST_PRIV_H__ */

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/6a7432f4/test/flash_test/include/flash_test/flash_test.h
----------------------------------------------------------------------
diff --git a/test/flash_test/include/flash_test/flash_test.h b/test/flash_test/include/flash_test/flash_test.h
new file mode 100644
index 0000000..f38f48c
--- /dev/null
+++ b/test/flash_test/include/flash_test/flash_test.h
@@ -0,0 +1,27 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+#ifndef __FLASH_TEST_H__ 
+#define __FLASH_TEST_H__
+
+/**
+ * adds the flash test commands to your shell */
+int
+flash_test_init(void);
+
+#endif /* __FLASH_TEST_H__ */

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/6a7432f4/test/flash_test/pkg.yml
----------------------------------------------------------------------
diff --git a/test/flash_test/pkg.yml b/test/flash_test/pkg.yml
new file mode 100644
index 0000000..365705c
--- /dev/null
+++ b/test/flash_test/pkg.yml
@@ -0,0 +1,30 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+# 
+#  http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+pkg.name: test/flash_test
+pkg.description: flash HAL tester
+pkg.homepage: "http://mynewt.apache.org/"
+pkg.keywords:
+
+pkg.deps:
+    - kernel/os
+    - libs/util
+    - hw/hal
+pkg.req_apis:
+    - console

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/6a7432f4/test/flash_test/src/flash_test/flash_test.c
----------------------------------------------------------------------
diff --git a/test/flash_test/src/flash_test/flash_test.c b/test/flash_test/src/flash_test/flash_test.c
new file mode 100644
index 0000000..980cbba
--- /dev/null
+++ b/test/flash_test/src/flash_test/flash_test.c
@@ -0,0 +1,155 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+#include <inttypes.h>
+#include <os/os.h>
+#include <console/console.h>
+#include <flash_test/flash_test.h>
+#include <hal/hal_bsp.h>
+#include <hal/hal_flash.h>
+#include <hal/hal_flash_int.h>
+#include <shell/shell.h>
+#include <stdio.h>
+#include <string.h>
+
+static int flash_cli_cmd(int argc, char **argv);
+static struct shell_cmd flash_cmd_struct = {
+    .sc_cmd = "flash",
+    .sc_cmd_func = flash_cli_cmd
+};
+
+static int
+flash_cli_cmd(int argc, char **argv)
+{
+    const struct hal_flash *hf;
+    uint32_t off = 0;
+    uint32_t sz = 1;
+    int sec_cnt;
+    int i;
+    int soff;
+    char *eptr;
+    char tmp_buf[8];
+    char pr_str[80];
+
+    hf = bsp_flash_dev(0);
+    if (!hf) {
+        console_printf("No flash device present\n");
+        return 0;
+    }
+    if (argc == 1) {
+        /*
+         * print status
+         */
+        console_printf("Flash at 0x%lx size 0x%lx with %d sectors,"
+          "alignment req %d bytes\n", 
+                (long unsigned int) hf->hf_base_addr, 
+                (long unsigned int) hf->hf_size,
+                hf->hf_sector_cnt, 
+                hf->hf_align);
+        sec_cnt = hf->hf_sector_cnt;
+        if (sec_cnt > 32) {
+            sec_cnt = 32;
+        }
+        for (i = 0; i < sec_cnt; i++) {
+            console_printf("  %d: %lx\n", i, 
+                    (long unsigned int) hal_flash_sector_size(hf, i));
+        }
+        if (sec_cnt != hf->hf_sector_cnt) {
+            console_printf("...  %d: %lx\n", hf->hf_sector_cnt - 1,
+              (long unsigned int) hal_flash_sector_size(hf, hf->hf_sector_cnt - 1));
+        }
+        return 0;
+    }
+    if (argc > 2) {
+        off = strtoul(argv[2], &eptr, 0);
+        if (*eptr != '\0') {
+            console_printf("Invalid offset %s\n", argv[2]);
+            goto err;
+        }
+    }
+    if (argc > 3) {
+        sz = strtoul(argv[3], &eptr, 0);
+        if (*eptr != '\0') {
+            console_printf("Invalid size %s\n", argv[3]);
+            goto err;
+        }
+    }
+    if (!strcmp(argv[1], "erase")) {
+        console_printf("Erase 0x%lx + %lx\n", 
+                (long unsigned int) off, (long unsigned int) sz);
+
+        if (hal_flash_erase(0, off, sz)) {
+            console_printf("Flash erase failed\n");
+        }
+        console_printf("Done!\n");
+    } else if (!strcmp(argv[1], "read")) {
+        console_printf("Read 0x%lx + %lx\n", 
+                (long unsigned int) off, (long unsigned int) sz);
+        sz += off;
+        while (off < sz) {
+            sec_cnt = min(sizeof(tmp_buf), sz - off);
+            if (hal_flash_read(0, off, tmp_buf, sec_cnt)) {
+                console_printf("flash read failure at %lx\n", 
+                        (long unsigned int) off);
+                break;
+            }
+            for (i = 0, soff = 0; i < sec_cnt; i++) {
+                soff += snprintf(pr_str + soff, sizeof(pr_str) - soff,
+                  "0x%02x ", tmp_buf[i] & 0xff);
+            }
+            console_printf("  0x%lx: %s\n", 
+                    (long unsigned int) off, pr_str);
+            off += sec_cnt;
+        }
+    } else if (!strcmp(argv[1], "write")) {
+        console_printf("Write 0x%lx + %lx\n", 
+                (long unsigned int) off, (long unsigned int) sz);
+
+        sz += off;
+        for (i = 0; i < sizeof(tmp_buf); i++) {
+            tmp_buf[i] = i + 1;
+        }
+
+        while (off < sz) {
+            sec_cnt = min(sizeof(tmp_buf), sz - off);
+            if (hal_flash_write(0, off, tmp_buf, sec_cnt)) {
+                console_printf("flash write failure at %lx\n", 
+                        (long unsigned int) off);
+            }
+            off += sec_cnt;
+        }
+        console_printf("Done!\n");
+    } else if ( !strcmp(argv[1], "?") || !strcmp(argv[1], "help"))
+    {
+        console_printf("Commands Available\n");
+        console_printf("flash -- dumps sector map \n");
+        console_printf("flash read <offset> <size> -- reads bytes from flash \n");
+        console_printf("flash write <offset>  <size>  -- writes incrementing data pattern 0-8 to flash \n");
+        console_printf("flash erase <offset> <size> -- erases flash \n");
+    }
+    return 0;
+err:
+    return -1;
+}
+
+
+int
+flash_test_init(void) {
+    shell_cmd_register(&flash_cmd_struct);
+    return 0;
+}

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/6a7432f4/test/testreport/include/testreport/testreport.h
----------------------------------------------------------------------
diff --git a/test/testreport/include/testreport/testreport.h b/test/testreport/include/testreport/testreport.h
new file mode 100644
index 0000000..7a0d3e4
--- /dev/null
+++ b/test/testreport/include/testreport/testreport.h
@@ -0,0 +1,34 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef H_TESTREPORT_
+#define H_TESTREPORT_
+
+struct nffs_area_desc;
+
+struct tr_config {
+    const char *tc_base_path;
+    const struct nffs_area_desc *tc_area_descs;
+};
+
+extern struct tr_config tr_config;
+
+int tr_init(void);
+
+#endif

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/6a7432f4/test/testreport/pkg.yml
----------------------------------------------------------------------
diff --git a/test/testreport/pkg.yml b/test/testreport/pkg.yml
new file mode 100644
index 0000000..42e8482
--- /dev/null
+++ b/test/testreport/pkg.yml
@@ -0,0 +1,30 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+# 
+#  http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+pkg.name: test/testreport
+pkg.description: Library for recording unit test results to flash.
+pkg.author: "Apache Mynewt <de...@mynewt.incubator.apache.org>"
+pkg.homepage: "http://mynewt.apache.org/"
+pkg.keywords:
+    - unit
+    - test
+
+pkg.deps:
+    - fs/nffs
+    - test/testutil

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/6a7432f4/test/testreport/src/arch/cortex_m4/io.c
----------------------------------------------------------------------
diff --git a/test/testreport/src/arch/cortex_m4/io.c b/test/testreport/src/arch/cortex_m4/io.c
new file mode 100644
index 0000000..fd27585
--- /dev/null
+++ b/test/testreport/src/arch/cortex_m4/io.c
@@ -0,0 +1,91 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <stddef.h>
+#include "nffs/nffs.h"
+#include "nffs/nffsutil.h"
+#include "testreport_priv.h"
+
+int
+tr_io_write(const char *path, const void *contents, size_t len)
+{
+    int rc;
+
+    rc = nffsutil_write_file(path, contents, len);
+    if (rc != 0) {
+        return -1;
+    }
+
+    return 0;
+}
+
+int
+tr_io_mkdir(const char *path)
+{
+    int rc;
+
+    rc = nffs_mkdir(path);
+    if (rc != 0 && rc != NFFS_EEXIST) {
+        return -1;
+    }
+
+    return 0;
+}
+
+int
+tr_io_rmdir(const char *path)
+{
+    int rc;
+
+    rc = nffs_unlink(path);
+    if (rc != 0 && rc != NFFS_ENOENT) {
+        return -1;
+    }
+
+    return 0;
+}
+
+int
+tr_io_read(const char *path, void *out_data, size_t len, size_t *out_len)
+{
+    uint32_t u32;
+    int rc;
+
+    rc = nffsutil_read_file(path, 0, len, out_data, &u32);
+    if (rc != 0) {
+        return -1;
+    }
+
+    *out_len = u32;
+
+    return 0;
+}
+
+int
+tr_io_delete(const char *path)
+{
+    int rc;
+
+    rc = nffs_unlink(path);
+    if (rc != 0 && rc != NFFS_ENOENT) {
+        return -1;
+    }
+
+    return 0;
+}

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/6a7432f4/test/testreport/src/arch/sim/io.c
----------------------------------------------------------------------
diff --git a/test/testreport/src/arch/sim/io.c b/test/testreport/src/arch/sim/io.c
new file mode 100644
index 0000000..a5233e5
--- /dev/null
+++ b/test/testreport/src/arch/sim/io.c
@@ -0,0 +1,140 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include "testreport_priv.h"
+
+static char tr_io_buf[1024];
+
+int
+tr_io_write(const char *path, const void *contents, size_t len)
+{
+    FILE *fp;
+    int rc;
+
+    fp = NULL;
+
+    fp = fopen(path, "w+");
+    if (fp == NULL) {
+        rc = -1;
+        goto done;
+    }
+
+    if (contents != NULL && len > 0) {
+        rc = fwrite(contents, len, 1, fp);
+        if (rc != 1) {
+            rc = -1;
+            goto done;
+        }
+    }
+
+    rc = 0;
+
+done:
+    if (fp != NULL) {
+        fclose(fp);
+    }
+
+    return rc;
+}
+
+int
+tr_io_mkdir(const char *path)
+{
+    int rc;
+
+    rc = mkdir(path, 0755);
+    if (rc == -1 && errno != EEXIST) {
+        return -1;
+    }
+
+    return 0;
+}
+
+/* XXX security risk, not portable, blah blah blah */
+int
+tr_io_rmdir(const char *path)
+{
+    int rc; 
+
+    rc = snprintf(tr_io_buf, sizeof tr_io_buf,
+                  "rm -rf '%s'", path);
+    if (rc >= sizeof tr_io_buf) {
+        return -1;
+    }
+
+    rc = system(tr_io_buf);
+    if (rc != 0) {
+        return rc;
+    }
+
+    return 0;
+}
+
+int
+tr_io_read(const char *path, void *out_data, size_t len, size_t *out_len)
+{
+    FILE *fp;
+    uint8_t *dst;
+    int rc;
+    int i;
+
+    fp = NULL;
+
+    fp = fopen(path, "rb");
+    if (fp == NULL) {
+        rc = -1;
+        goto done;
+    }
+
+    dst = out_data;
+    for (i = 0; i < len; i++) {
+        rc = getc(fp);
+        if (rc == EOF) {
+            rc = -1;
+            goto done;
+        }
+
+        dst[i] = rc;
+    }
+
+    *out_len = i;
+    rc = 0;
+
+done:
+    if (fp != NULL) {
+        fclose(fp);
+    }
+
+    return rc;
+}
+
+int
+tr_io_delete(const char *path)
+{
+    int rc;
+
+    rc = remove(path);
+
+    return rc;
+}

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/6a7432f4/test/testreport/src/results.c
----------------------------------------------------------------------
diff --git a/test/testreport/src/results.c b/test/testreport/src/results.c
new file mode 100644
index 0000000..77b26c2
--- /dev/null
+++ b/test/testreport/src/results.c
@@ -0,0 +1,190 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <stdio.h>
+#include "testutil/testutil.h"
+#include "testreport/testreport.h"
+#include "testreport_priv.h"
+
+#define TU_REPORT_META_DIR          ".meta"
+#define TU_REPORT_STATUS_FILENAME   "status"
+
+static char tr_report_buf[1024];
+
+int
+tr_report_rmdir_results(void)
+{
+    if (tr_config.tc_base_path == NULL) {
+        return 0;
+    }
+
+    return tr_io_rmdir(tr_config.tc_base_path);
+}
+
+int
+tr_report_mkdir_results(void)
+{
+    int rc;
+
+    if (tr_config.tc_base_path == NULL) {
+        return 0;
+    }
+
+    rc = snprintf(tr_report_buf, sizeof tr_report_buf,
+                  "%s", tr_config.tc_base_path);
+    if (rc >= sizeof tr_report_buf) {
+        return -1;
+    }
+    return tr_io_mkdir(tr_report_buf);
+}
+
+int
+tr_report_mkdir_meta(void)
+{
+    int rc;
+
+    if (tr_config.tc_base_path == NULL) {
+        return 0;
+    }
+
+    rc = snprintf(tr_report_buf, sizeof tr_report_buf,
+                  "%s/" TU_REPORT_META_DIR, tr_config.tc_base_path);
+    if (rc >= sizeof tr_report_buf) {
+        return -1;
+    }
+    return tr_io_mkdir(tr_report_buf);
+}
+
+int
+tr_report_mkdir_suite(void)
+{
+    int rc;
+
+    if (tr_config.tc_base_path == NULL) {
+        return 0;
+    }
+
+    rc = snprintf(tr_report_buf, sizeof tr_report_buf,
+                  "%s/%s", tr_config.tc_base_path,
+                  tu_suite_name);
+    if (rc >= sizeof tr_report_buf) {
+        return -1;
+    }
+
+    rc = tr_io_mkdir(tr_report_buf);
+    if (rc != 0) {
+        return rc;
+    }
+
+    return 0;
+}
+
+int
+tr_report_mkdir_case(void)
+{
+    int rc;
+
+    if (tr_config.tc_base_path == NULL) {
+        return 0;
+    }
+
+    rc = snprintf(tr_report_buf, sizeof tr_report_buf,
+                  "%s/%s/%s", tr_config.tc_base_path,
+                  tu_suite_name, tu_case_name);
+    if (rc >= sizeof tr_report_buf) {
+        return -1;
+    }
+
+    rc = tr_io_mkdir(tr_report_buf);
+    if (rc != 0) {
+        return rc;
+    }
+
+    return 0;
+}
+
+int
+tr_report_write_file(const char *filename, const uint8_t *data,
+                     size_t data_len)
+{
+    int rc;
+
+    if (tr_config.tc_base_path == NULL) {
+        return 0;
+    }
+
+    rc = snprintf(tr_report_buf, sizeof tr_report_buf,
+                  "%s/%s/%s/%s", tr_config.tc_base_path,
+                  tu_suite_name, tu_case_name, filename);
+    if (rc >= sizeof tr_report_buf) {
+        return -1;
+    }
+
+    rc = tr_io_write(tr_report_buf, data, data_len);
+    if (rc != 0) {
+        return rc;
+    }
+
+    return 0;
+}
+
+int
+tr_report_read_status(void)
+{
+    size_t bytes_read;
+    int rc;
+
+    rc = snprintf(tr_report_buf, sizeof tr_report_buf,
+                  "%s/%s/%s", tr_config.tc_base_path,
+                  TU_REPORT_META_DIR, TU_REPORT_STATUS_FILENAME);
+    if (rc >= sizeof tr_report_buf) {
+        return -1;
+    }
+
+    rc = tr_io_read(tr_report_buf, &tu_first_idx, sizeof tu_first_idx,
+                    &bytes_read);
+    if (rc != 0 || bytes_read != sizeof tu_first_idx) {
+        return -1;
+    }
+
+    tr_io_delete(tr_report_buf);
+
+    return 0;
+}
+
+int
+tr_report_write_status(void)
+{
+    int rc;
+
+    rc = snprintf(tr_report_buf, sizeof tr_report_buf,
+                  "%s/%s/%s", tr_config.tc_base_path,
+                  TU_REPORT_META_DIR, TU_REPORT_STATUS_FILENAME);
+    if (rc >= sizeof tr_report_buf) {
+        return -1;
+    }
+
+    rc = tr_io_write(tr_report_buf, &tu_first_idx, sizeof tu_first_idx);
+    if (rc != 0) {
+        return -1;
+    }
+
+    return 0;
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/6a7432f4/test/testreport/src/testreport.c
----------------------------------------------------------------------
diff --git a/test/testreport/src/testreport.c b/test/testreport/src/testreport.c
new file mode 100644
index 0000000..c29221b
--- /dev/null
+++ b/test/testreport/src/testreport.c
@@ -0,0 +1,131 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include "hal/hal_flash.h"
+#include "fs/fs.h"
+#include "nffs/nffs.h"
+#include "testutil/testutil.h"
+#include "testreport/testreport.h"
+#include "testreport_priv.h"
+
+struct tr_config tr_config;
+
+static int tr_case_fail_idx;
+
+static void
+tr_case_init(void *unused)
+{
+    int rc;
+
+    rc = tr_results_mkdir_case();
+    assert(rc == 0);
+}
+
+static void
+tr_case_fail(char *msg, int msg_len, void *unused)
+{
+    char filename[14];
+    int rc;
+
+    rc = snprintf(filename, sizeof filename, "fail-%04d.txt",
+                  tr_case_fail_idx);
+    assert(rc < sizeof filename);
+
+    rc = tr_results_write_file(filename, (uint8_t *)msg, msg_len);
+    assert(rc == 0);
+
+    tr_case_fail_idx++;
+}
+
+static void
+tr_case_pass(char *msg, int msg_len, void *unused)
+{
+    int rc;
+
+    rc = tr_results_write_file("pass.txt", (uint8_t *)msg, msg_len);
+    assert(rc == 0);
+}
+
+static void
+tr_suite_init(void *unused)
+{
+    int rc;
+
+    rc = tr_results_mkdir_suite();
+    assert(rc == 0);
+}
+
+static void
+tr_restart(void *unused)
+{
+    tr_results_write_status();
+}
+
+int
+tr_init(void)
+{
+    int rc;
+
+    if (tr_config.tc_base_path != NULL) {
+        if (tr_config.tc_area_descs != NULL) {
+            rc = hal_flash_init();
+            if (rc != 0) {
+                return -1;
+            }
+
+            rc = nffs_init();
+            if (rc != 0) {
+                return -1;
+            }
+
+            rc = nffs_detect(tr_config.tc_area_descs);
+            if (rc == FS_ECORRUPT) {
+                rc = nffs_format(tr_config.tc_area_descs);
+            }
+            if (rc != 0) {
+                return -1;
+            }
+        }
+
+        rc = tr_results_read_status();
+        if (rc != 0) {
+            tr_results_rmdir_results();
+        }
+
+        rc = tr_results_mkdir_results();
+        if (rc != 0) {
+            return -1;
+        }
+
+        rc = tr_results_mkdir_meta();
+        if (rc != 0) {
+            return -1;
+        }
+    }
+
+    tu_config.tc_case_init_cb = tr_case_init;
+    tu_config.tc_case_fail_cb = tr_case_fail;
+    tu_config.tc_case_pass_cb = tr_case_pass;
+    tu_config.tc_suite_init_cb = tr_suite_init;
+    tu_config.tc_restart_cb = tr_restart;
+
+    return 0;
+}

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/6a7432f4/test/testreport/src/testreport_priv.h
----------------------------------------------------------------------
diff --git a/test/testreport/src/testreport_priv.h b/test/testreport/src/testreport_priv.h
new file mode 100644
index 0000000..88576b6
--- /dev/null
+++ b/test/testreport/src/testreport_priv.h
@@ -0,0 +1,48 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef H_TESTREPORT_PRIV_
+#define H_TESTREPORT_PRIV_
+
+#include <stddef.h>
+#include <inttypes.h>
+
+int tr_results_mkdir_results(void);
+int tr_results_rmdir_results(void);
+
+int tr_results_mkdir_meta(void);
+
+int tr_results_mkdir_suite(void);
+int tr_results_mkdir_case(void);
+
+int tr_results_write_file(const char *filename, const uint8_t *data,
+                          size_t data_len);
+
+int tr_results_read_status(void);
+int tr_results_write_status(void);
+
+int tr_io_read(const char *path, void *out_data, size_t len, size_t *out_len);
+int tr_io_write(const char *path, const void *contents, size_t len);
+
+int tr_io_delete(const char *path);
+
+int tr_io_mkdir(const char *path);
+int tr_io_rmdir(const char *path);
+
+#endif

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/6a7432f4/test/testutil/design.txt
----------------------------------------------------------------------
diff --git a/test/testutil/design.txt b/test/testutil/design.txt
new file mode 100644
index 0000000..a1dfd9b
--- /dev/null
+++ b/test/testutil/design.txt
@@ -0,0 +1,476 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+# 
+#  http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+****** TEST AUTOMATION
+
+*** HISTORY
+Rev.    Date            Changes
+2       2015/09/07      Addition of tu_restart(); clarification of exported
+                            preprocessor symbols.
+1       2015/08/28
+
+
+*** SUMMARY
+
+Automated testing can be done in either of two ways:
+    1. Testing a set of packages on embedded hardware or a simulated
+       environment.
+    2. Testing an individual package in a simulated environment.
+
+
+***** PACKAGE SET TESTING
+
+Automated testing of package sets consists of two components:
+    1. "PC software" which administers tests and manages results.
+    2. Test images which run on the units under test (UUTs).
+
+
+**** PC SOFTWARE
+
+The PC software will need to perform the following tasks:
+    1. Connect to UUT (e.g., via JTAG and OpenOCD) (if embedded).
+    2. Upload test image (if embedded).
+    3. Run test image.
+    4. Read test results from UUT.
+    5. Generate test report.
+
+If any of these tasks fail, a fatal error would need to be logged to the test
+report.
+
+The details of the PC software are TBD, and are not discussed further here.
+
+
+**** TEST IMAGES
+
+Test images are build using the stack tool.  The user will need to create a
+separate stack tool target for each platform under test.  In addition, there
+will be a single stack tool project which contains code to call the appropriate
+test code.  Each test image typically consists of the following:
+    1. The test cases from all relevant packages.
+    2. Some "glue code" from a special test project.
+
+
+*** PACKAGE TEST CASES
+
+A package may optionally contain a set of test cases.  Test cases are not
+normally compiled and linked when a package is built; they are only included
+when the "test" identity is specified.  All of a
+package's test code goes in its 'src/test' directory.  For example, the ffs
+package's test code is located in the following directory:
+
+    libs/ffs/src/test/
+
+This directory contains the source and header files that implement the ffs test
+code.
+
+The test code has access to all the header files in the following directories:
+    * src
+    * src/arch/<target-arch>
+    * include
+    * src/test
+    * src/test/arch/<target-arch>
+    * include directories of all package dependencies
+
+Package test code typically depends on the testutil package, described later in
+this document.  If a package's test code uses testutil, then the package itself
+needs to have testutil in its dependency list.
+
+Some test cases or test initialization code may be platform-specific.  In such
+cases, the platform-specific function definitions are placed in arch
+subdirectories within the package test directory.
+
+When test code is built (i.e., when the "test" identity is specified), the
+stack tool defines the "TEST" macro.  This macro is defined during complilation
+of all C source files in all projects and packages..
+
+
+***** TESTUTIL
+
+The testutil package is a test framework that provides facilities for
+specifying test cases and recording test results.  
+
+
+*** TEST STRUCTURE
+
+Tests are structures according to the following hierarchy:
+
+                [test]
+               /      \
+        [suite]        [suite]
+       /       \      /       \
+     [case] [case]  [case] [case]
+
+
+I.e., a test consists of test suites, and a test suite consits of test cases.
+
+The test code uses testutil to define test suites and test cases.
+
+
+*** TESTUTIL EXAMPLE
+
+The following example demonstrates how to create a simple test suite.
+
+    TEST_CASE(test_addition)
+    {
+        int sum;
+
+        sum = 5 + 10;
+        TEST_ASSERT(sum == 15, "actual value: %d", sum);
+    }
+
+    TEST_CASE(test_times_0)
+    {
+        TEST_ASSERT(3 * 0 == 0);
+        TEST_ASSERT(4 * 0 == 0);
+        TEST_ASSERT(712 * 0 == 0);
+    }
+
+    TEST_SUITE(test_suite_arithmetic)
+    {
+        test_addition();
+        test_times_0();
+    }
+
+The test suite would then be executed via a call to test_suite_arithmetic().
+
+
+*** TEST RESULTS
+
+The testutil package optionally writes test results to a file system residing
+on the UUT.  The contents of the file system can then be retrieved from the UUT
+by the PC software.  When run in an embedded environment, the results are
+written to an ffs file system.  When run in a simulated environment, the
+results are written to the native file system.
+
+Results directory structure:
+    /
+    \u2514\u2500\u2500 test-results
+        \u251c\u2500\u2500 <test-suite-1>
+        |   \u251c\u2500\u2500 <test-case-1>
+        |   \u2502�� \u251c\u2500\u2500 <result-1>.txt
+        |   \u2502�� \u251c\u2500\u2500 [<result-2>.txt]
+        |   \u2502�� \u251c\u2500\u2500 [...]
+        |   \u2502�� \u2514\u2500\u2500 [other-data-files]
+        |   \u251c\u2500\u2500 <test-case-2>
+        |   \u2502�� \u251c\u2500\u2500 <result-1>.txt
+        |   \u2502�� \u251c\u2500\u2500 [<result-2>.txt]
+        |   \u2502�� \u251c\u2500\u2500 [...]
+        |   \u2502�� \u2514\u2500\u2500 [other-data-files]
+        |   \u2514\u2500\u2500 [...]
+        \u251c\u2500\u2500 <test-suite-2>
+        |   \u2514\u2500\u2500 [...]
+        \u2514\u2500\u2500 [...]
+
+The name of the top-level directory is configurable.  This document uses
+'test-results' in its examples.
+
+Each test case directory contains a set of result files, and optionally
+contains data files containing information collected during the test.  Result
+files are named according to one of the following templates:
+
+    * pass.txt
+    * fail-xxxx.txt
+
+Where "xxxx" is a numeric index automatically generated by testutil.  If a test
+passes, its results directory is populated with a single 'pass' file and no
+'fail' files.  If a test fails, its results directory is populated with one or
+more 'fail' files and no 'pass' files.  A result file (pass or fail) optionally
+contains a text string.
+
+In addition, a results directory can contains data files containing information
+collected during the test.  For example, a test case may record timing
+information or statistics.  Test case code specifies the name and contents of a
+data file when it writes one.  Data files can be written during a test case
+regardless of the test outcome.  There are no restrictions on the size or
+contents of data files, but their filenames should be distinct from the test
+result naming scheme described above.  There is no limit to the number of data
+files that a particular test case can write.
+
+Returning to the earlier arithmetic test suite example, suppose the test suite
+completed with the following results:
+
+    * test_addition: 1 assertion success, 0 assertion failures
+    * test_times_0:  1 assertion success, 2 assertion failures
+
+Such a test run would produce the following directory tree:
+
+    /
+    \u2514\u2500\u2500 test-results
+        \u2514\u2500\u2500 test_suite_arithmetic
+            \u251c\u2500\u2500 test_addition
+            \u2502�� \u2514\u2500\u2500 pass.txt
+            \u2514\u2500\u2500 test_times_0
+                \u251c\u2500\u2500 fail-0000.txt
+                \u2514\u2500\u2500 fail-0001.txt
+
+Each 'fail' file would contain a string describing the failure.
+
+
+*** TESTUTIL API
+
+struct tu_config {
+    /** If nonzero, test results are printed to stdout. */
+    int tc_print_results;
+
+    /**
+     * Name of the base results directory (e.g., "test-results").  If null,
+     * results are not written to disk.
+     */
+    const char *tc_results_path;
+
+    /**
+     * Array of flash areas where an ffs file system should be located.  Unused
+     * if run in a simulated environment.  This array must be terminated with a
+     * zero-length area.
+     */
+    const struct ffs_area_desc *tc_area_descs;
+};
+extern struct tu_config tu_config;
+
+Description:
+    The global tu_config struct contains all the testutil package's settings.
+    The struct must be populated before tu_init() is called.
+
+-
+
+int tu_init(void)
+
+Description:
+    Initializes the test framework according to the contents of the tu_config
+    struct.  If run in an embedded environment and the
+    tu_config.tc_results_path and tu_config.tc_area_descs fields are non-null,
+    this function ensures an ffs file system is present within the specified
+    flash areas.  If testutil is using an ffs file system, and no valid ffs
+    file system is detected in the specified region of flash, the region is
+    formatted with a new file system.  This function must be called before any
+    tests are run.
+
+    If, immediately prior to a call to tu_init(), the UUT was restarted via a
+    call to tu_restart(), this function skips all the test cases that have
+    already been executed.  See tu_restart() for details.
+
+    Returns 0 on success; nonzero on failure.
+
+-
+
+TEST_ASSERT(expression)
+TEST_ASSERT(expression, fail_msg, ...)
+
+Description:
+    Asserts that the specified condition is true.  If the expression is true,
+    nothing gets reported.  The expression argument is mandatory; the rest are
+    optional.  The fail_msg argument is a printf format string which specifies
+    how the remaining arguments are parsed.
+
+    If the expression is false, testutil reports the following message:
+
+        |<file>:<line-number>| failed assertion: <expression>
+        <fail_msg>
+
+    If no fail_msg is specified, only the first line gets reported.
+
+    The test case proceeds as normal after this macro is invoked.
+
+Example:
+    TEST_ASSERT(num_blocks == 1024,
+                "expected: 1024 blocks; got: %d", num_blocks);
+
+    On failure, the above assertion would result in a report like the
+    following:
+
+        |ffs_test.c:426| failed assertion: num_blocks == 1024
+        expected: 1024 blocks; got 12
+
+-
+
+TEST_ASSERT_FATAL(expression)
+TEST_ASSERT_FATAL(expression, fail_msg, ...)
+
+Description:
+    These are identical to their TEST_ASSERT counterparts, except that a
+    failure causes the current test case to be aborted.
+
+-
+
+TEST_PASS(msg, ...)
+
+Description:
+    Reports a success result for the current test.  This function is not
+    normally needed, as all successful tests automatically write an empty pass
+    result at completion.  This function is only needed when the success result
+    report should contain text.  The msg argument is a printf format string
+    which specifies how the remaining arguments are parsed.  The result file
+    produced by this function contains the following text:
+
+        |<file>:<line-number>| manual pass
+        <msg>
+
+    After this function is called, the remainder of the test case is not
+    executed.  If the current test has already failed when this macro is
+    invoked, the macro has no effect.
+
+-
+
+testutil_write_data(const char *filename, const uint8_t *data, size_t data_len)
+
+Description:
+    Writes a data file to the current test results directory.  The file's
+    contents are fully specified by the data and data_len arguments.  If a file
+    with the specified name already exists, it is overwritten.  After this
+    function is called, the test case proceeds as normal.
+
+    Data does not get written to stdout; it only gets written to disk.  This
+    function has no effect if testutil is configured to not write results to
+    disk.
+
+-
+
+TEST_CASE(test_case_name) { /* test case code */ }
+
+Description:
+    Defines a test case function with the following type:
+
+        int test_case_name(void)
+
+    The return value is 0 if the test case passed; nonzero if it failed.
+    Generally, the return code is not used.
+
+
+-
+
+TEST_SUITE(test_suite_name) { /* test suite code */ }
+
+Description:
+    Defines a test suite function with the following type:
+
+        int test_suite_name(void)
+
+    The return value is 0 if the test suite passed; nonzero if it failed.
+    Generally, the return code is not used.
+
+-
+
+void tu_restart(void)
+
+Description:
+    This function is used when a system reset is necessary to proceed with
+    testing.  For example, the OS is designed to run forever once started, so a
+    test which creates several OS tasks and then starts the OS has no means of
+    completing.  This function, when called from such a test, gracefully ends
+    the current test case and proceeds to the next test case.
+
+    The particulars of this function depend on whether it is called from a
+    simulated environment.  In a simulated environment, this function uses a
+    longjmp() call to break out of the current test case.
+
+    In a non-simulated environment, this function performs the following
+    procedure:
+
+        1. Record the current test status to the file system.
+        2. Perform a software reset of the UUT.
+        3. Read test status and delete it from the file system.
+        4. Skip all previously completed test cases.
+
+    If this function is called before any results have been reported for the
+    current test case, a "pass" result gets reported.
+
+    Note: after this function performs a software reset, the UUT may not be
+    fully initialized, depending on the test environment.  For example, if the
+    UUT has been set up to run the test image from RAM via JTAG, variables in
+    the .data section will not get properly initialized by the software reset.
+    Proper initialization in this case would require that data be re-read from
+    the image file via an additional run of the JTAG script.
+
+-
+
+ASSERT_IF_TEST(cond)
+
+Description:
+    The effect of this macro depends on whether the TEST macro is defined:
+
+        If defined:     expands to assert().
+        If not defined: expands to nothing.
+
+    This macro is not intended for use within test cases.  Instead, it should
+    be used in normal non-test code to perform computationally-expensive
+    checks.  In non-test builds, the checks do not get performed.
+
+
+***** INDIVIDUAL PACKAGE TESTING
+
+Testing individual packages is performed via the "target test" stack tool
+command.  When testing is done in this manner, the stack tool automatically
+runs the test code on the host machine.  There is no need for an extra stack
+project for producing a test image.
+
+Testing a package in this manner requires a special target that is specific to
+the package.  The target specifies the package to test via the "pkg"
+configuration variable.  The target must not specify a project, or the test
+command will fail.
+
+Below is an example target called testffs that tests the ffs package:
+
+    testffs
+            compiler: sim
+            compiler_def: debug
+            pkg: libs/ffs
+            bsp: hw/bsp/native
+            arch: sim
+            name: testffs
+
+The target specifies the "native" bsp.  This is necessary so that the ffs test
+code can link with the appropriate implementations of the hal functions that it
+depends on.
+
+The test is run by specifying the following stack arguments:
+
+    target test testffs
+
+When the "target test" command is used, the PKG_TEST macro is defined during
+compilation of the package under test.  This is in addition to the TEST macro,
+which is defined during compilation of all packages.
+
+
+***** ISSUES / QUESTIONS
+
+* For individual package tests, the requirement for a separate target for each
+  package may become unwieldy.  Perhaps the stack tool should be modified to
+  allow arbitrary key-value pairs to be specified on the command line.  That
+  way, the target's "pkg" variable could be unset, and the user would specify
+  the package to test when he executes the test command.  This would allow the
+  same target to be used for all packages, e.g.,
+
+        target test testtarget pkg=libs/ffs
+        target test testtarget pkg=libs/os
+
+* Most test frameworks define a lot of extra assertions (e.g., assert_equal(),
+  assert_not_null(), assert_false(), etc).  These don't seem particularly
+  useful to me, so I did not add them.  Typing TEST_ASSERT(x == 5) is no more
+  difficult than TEST_ASSERT_EQUAL(x, 5).  The benefit of the more specific
+  assertions is that they can automatically produce more helpful failure
+  messages.  For example, if TEST_ASSERT_EQUAL(x, 5) fails, the failure message
+  can contain the actual value of x in most cases, whereas a plain
+  TEST_ASSERT() won't automatically log that information.  This didn't seem
+  useful enough to me to justify growing the interface, but I am interested in
+  hearing what others think.
+
+* During testing on embedded devices, the PC software will need some
+  notification when testing is complete, so that it knows when to retrieve the
+  test results.  The testutil package should expose some state that the PC
+  software can poll for this purpose.

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/6a7432f4/test/testutil/include/testutil/testutil.h
----------------------------------------------------------------------
diff --git a/test/testutil/include/testutil/testutil.h b/test/testutil/include/testutil/testutil.h
new file mode 100644
index 0000000..de61394
--- /dev/null
+++ b/test/testutil/include/testutil/testutil.h
@@ -0,0 +1,174 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef H_TESTUTIL_
+#define H_TESTUTIL_
+
+#include <inttypes.h>
+#include <setjmp.h>
+
+#include "syscfg/syscfg.h"
+
+/*****************************************************************************
+ * Public declarations                                                       *
+ *****************************************************************************/
+
+typedef void tu_case_init_fn_t(void *arg);
+typedef void tu_case_report_fn_t(char *msg, int msg_len, void *arg);
+typedef void tu_suite_init_fn_t(void *arg);
+typedef void tu_restart_fn_t(void *arg);
+
+struct tu_config {
+    int tc_print_results;
+    int tc_system_assert;
+
+    tu_case_init_fn_t *tc_case_init_cb;
+    void *tc_case_init_arg;
+
+    tu_case_report_fn_t *tc_case_fail_cb;
+    void *tc_case_fail_arg;
+
+    tu_case_report_fn_t *tc_case_pass_cb;
+    void *tc_case_pass_arg;
+
+    tu_suite_init_fn_t *tc_suite_init_cb;
+    void *tc_suite_init_arg;
+
+    tu_restart_fn_t *tc_restart_cb;
+    void *tc_restart_arg;
+};
+
+extern struct tu_config tu_config;
+extern const char *tu_suite_name;
+extern const char *tu_case_name;
+extern int tu_first_idx;
+
+typedef void tu_post_test_fn_t(void *arg);
+
+void tu_suite_set_post_test_cb(tu_post_test_fn_t *cb, void *cb_arg);
+int tu_parse_args(int argc, char **argv);
+int tu_init(void);
+void tu_restart(void);
+
+/*****************************************************************************
+ * Private declarations                                                      *
+ *****************************************************************************/
+
+void tu_suite_complete(void);
+void tu_suite_init(const char *name);
+
+void tu_case_init(const char *name);
+void tu_case_complete(void);
+void tu_case_fail_assert(int fatal, const char *file, int line,
+                         const char *expr, const char *format, ...);
+void tu_case_write_pass_auto(void);
+void tu_case_pass_manual(const char *file, int line,
+                         const char *format, ...);
+void tu_case_post_test(void);
+
+extern int tu_any_failed;
+extern int tu_suite_failed;
+extern int tu_case_reported;
+extern int tu_case_failed;
+extern int tu_case_idx;
+extern jmp_buf tu_case_jb;
+
+#define TEST_SUITE(suite_name)                                                \
+    static void TEST_SUITE_##suite_name(void);                                \
+                                                                              \
+    int                                                                       \
+    suite_name(void)                                                          \
+    {                                                                         \
+        tu_suite_init(#suite_name);                                           \
+        TEST_SUITE_##suite_name();                                            \
+        tu_suite_complete();                                                  \
+                                                                              \
+        return tu_suite_failed;                                               \
+    }                                                                         \
+                                                                              \
+    static void                                                               \
+    TEST_SUITE_##suite_name(void)
+
+/* for creating multiple files with test cases all belonging to the same
+ * suite */
+#define TEST_CASE_DECL(case_name)  int case_name(void);
+
+#define TEST_CASE(case_name)                                                  \
+    static void TEST_CASE_##case_name(void);                                  \
+                                                                              \
+    int                                                                       \
+    case_name(void)                                                           \
+    {                                                                         \
+        if (tu_case_idx >= tu_first_idx) {                                    \
+            tu_case_init(#case_name);                                         \
+                                                                              \
+            if (setjmp(tu_case_jb) == 0) {                                    \
+                TEST_CASE_##case_name();                                      \
+                tu_case_post_test();                                          \
+                tu_case_write_pass_auto();                                    \
+            }                                                                 \
+        }                                                                     \
+                                                                              \
+        tu_case_complete();                                                   \
+                                                                              \
+        return tu_case_failed;                                                \
+    }                                                                         \
+                                                                              \
+    static void                                                               \
+    TEST_CASE_##case_name(void)
+
+#define FIRST_AUX(first, ...) first
+#define FIRST(...) FIRST_AUX(__VA_ARGS__, _)
+
+#define NUM(...) ARG10(__VA_ARGS__, N, N, N, N, N, N, N, N, 1, _)
+#define ARG10(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, ...) a10
+
+#define REST_OR_0(...) REST_OR_0_AUX(NUM(__VA_ARGS__), __VA_ARGS__)
+#define REST_OR_0_AUX(qty, ...) REST_OR_0_AUX_INNER(qty, __VA_ARGS__)
+#define REST_OR_0_AUX_INNER(qty, ...) REST_OR_0_AUX_##qty(__VA_ARGS__)
+#define REST_OR_0_AUX_1(first) 0
+#define REST_OR_0_AUX_N(first, ...) __VA_ARGS__
+
+#define XSTR(s) STR(s)
+#define STR(s) #s
+
+#define TEST_ASSERT_FULL(fatal, expr, ...) do                                 \
+{                                                                             \
+    if (!(expr)) {                                                            \
+        tu_case_fail_assert((fatal), __FILE__, __LINE__, XSTR(expr),          \
+                            __VA_ARGS__);                                     \
+    }                                                                         \
+} while (0)
+
+#define TEST_ASSERT(...)                                                      \
+    TEST_ASSERT_FULL(0, FIRST(__VA_ARGS__), REST_OR_0(__VA_ARGS__))
+
+#define TEST_ASSERT_FATAL(...)                                                \
+    TEST_ASSERT_FULL(1, FIRST(__VA_ARGS__), REST_OR_0(__VA_ARGS__))
+
+#define TEST_PASS(...)                                                        \
+    tu_case_pass_manual(__FILE__, __LINE__, __VA_ARGS__);
+
+#if MYNEWT_VAL(TEST)
+#define ASSERT_IF_TEST(expr) assert(expr)
+#else
+#define ASSERT_IF_TEST(expr)
+#endif
+
+#endif

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/6a7432f4/test/testutil/pkg.yml
----------------------------------------------------------------------
diff --git a/test/testutil/pkg.yml b/test/testutil/pkg.yml
new file mode 100644
index 0000000..be8274e
--- /dev/null
+++ b/test/testutil/pkg.yml
@@ -0,0 +1,30 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+# 
+#  http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+pkg.name: test/testutil
+pkg.description: Support library for implementing unit tests.
+pkg.author: "Apache Mynewt <de...@mynewt.incubator.apache.org>"
+pkg.homepage: "http://mynewt.apache.org/"
+pkg.keywords:
+    - unit
+    - test
+
+pkg.deps:
+    - hw/hal
+    - kernel/os

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/6a7432f4/test/testutil/src/arch/cortex_m4/testutil_arch_arm.c
----------------------------------------------------------------------
diff --git a/test/testutil/src/arch/cortex_m4/testutil_arch_arm.c b/test/testutil/src/arch/cortex_m4/testutil_arch_arm.c
new file mode 100644
index 0000000..52ace63
--- /dev/null
+++ b/test/testutil/src/arch/cortex_m4/testutil_arch_arm.c
@@ -0,0 +1,27 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "hal/hal_system.h"
+#include "testutil_priv.h"
+
+void
+tu_arch_restart(void)
+{
+    system_reset();
+}

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/6a7432f4/test/testutil/src/arch/sim/testutil_arch_sim.c
----------------------------------------------------------------------
diff --git a/test/testutil/src/arch/sim/testutil_arch_sim.c b/test/testutil/src/arch/sim/testutil_arch_sim.c
new file mode 100644
index 0000000..2d90501
--- /dev/null
+++ b/test/testutil/src/arch/sim/testutil_arch_sim.c
@@ -0,0 +1,30 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "os/os.h"
+#include "os/os_arch.h"
+#include "os/os_test.h"
+#include "testutil_priv.h"
+
+void
+tu_arch_restart(void)
+{
+    os_arch_os_stop();
+    tu_case_abort();
+}

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/6a7432f4/test/testutil/src/arch/sim/tu_args.c
----------------------------------------------------------------------
diff --git a/test/testutil/src/arch/sim/tu_args.c b/test/testutil/src/arch/sim/tu_args.c
new file mode 100644
index 0000000..75a2900
--- /dev/null
+++ b/test/testutil/src/arch/sim/tu_args.c
@@ -0,0 +1,23 @@
+#include <errno.h>
+#include <unistd.h>
+
+#include "testutil/testutil.h"
+
+int
+tu_parse_args(int argc, char **argv)
+{
+    int ch;
+
+    while ((ch = getopt(argc, argv, "s")) != -1) {
+        switch (ch) {
+        case 's':
+            tu_config.tc_system_assert = 1;
+            break;
+
+        default:
+            return EINVAL;
+        }
+    }
+
+    return 0;
+}

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/6a7432f4/test/testutil/src/case.c
----------------------------------------------------------------------
diff --git a/test/testutil/src/case.c b/test/testutil/src/case.c
new file mode 100644
index 0000000..614b142
--- /dev/null
+++ b/test/testutil/src/case.c
@@ -0,0 +1,255 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include "testutil/testutil.h"
+#include "testutil_priv.h"
+
+jmp_buf tu_case_jb;
+int tu_case_reported;
+int tu_case_failed;
+int tu_case_idx;
+
+const char *tu_case_name;
+tu_post_test_fn_t *tu_case_post_test_cb;
+void *tu_case_post_test_cb_arg;
+
+#define TU_CASE_BUF_SZ      1024
+
+static char tu_case_buf[TU_CASE_BUF_SZ];
+static int tu_case_buf_len;
+
+void
+tu_case_abort(void)
+{
+    tu_case_write_pass_auto();
+    longjmp(tu_case_jb, 1);
+}
+
+static int
+tu_case_vappend_buf(const char *format, va_list ap)
+{
+    char *dst;
+    int maxlen;
+    int rc;
+
+    dst = tu_case_buf + tu_case_buf_len;
+    maxlen = TU_CASE_BUF_SZ - tu_case_buf_len;
+
+    rc = vsnprintf(dst, maxlen, format, ap);
+    tu_case_buf_len += rc;
+    if (tu_case_buf_len >= TU_CASE_BUF_SZ) {
+        tu_case_buf_len = TU_CASE_BUF_SZ - 1;
+        return -1;
+    }
+
+    return 0;
+}
+
+static int
+tu_case_append_buf(const char *format, ...)
+{
+    va_list ap;
+    int rc;
+
+    va_start(ap, format);
+    rc = tu_case_vappend_buf(format, ap);
+    va_end(ap);
+
+    return rc;
+}
+
+static void
+tu_case_set_name(const char *name)
+{
+    tu_case_name = name;
+}
+
+void
+tu_case_init(const char *name)
+{
+    tu_case_reported = 0;
+    tu_case_failed = 0;
+
+    tu_case_set_name(name);
+
+    if (tu_config.tc_case_init_cb != NULL) {
+        tu_config.tc_case_init_cb(tu_config.tc_case_init_arg);
+    }
+}
+
+void
+tu_case_complete(void)
+{
+    tu_case_idx++;
+}
+
+void
+tu_case_post_test(void)
+{
+    if (tu_case_post_test_cb != NULL) {
+        tu_case_post_test_cb(tu_case_post_test_cb_arg);
+    }
+}
+
+static void
+tu_case_write_fail_buf(void)
+{
+    tu_case_reported = 1;
+    tu_case_failed = 1;
+    tu_suite_failed = 1;
+    tu_any_failed = 1;
+
+    if (tu_config.tc_print_results) {
+        printf("[FAIL] %s/%s %s", tu_suite_name, tu_case_name, tu_case_buf);
+        fflush(stdout);
+    }
+
+    if (tu_config.tc_case_fail_cb != NULL) {
+        tu_config.tc_case_fail_cb(tu_case_buf, tu_case_buf_len,
+                                  tu_config.tc_case_fail_arg);
+    }
+}
+
+static void
+tu_case_append_file_info(const char *file, int line)
+{
+    int rc;
+
+    rc = tu_case_append_buf("|%s:%d| ", file, line);
+    assert(rc == 0);
+}
+
+static void
+tu_case_append_assert_msg(const char *expr)
+{
+    int rc;
+
+    rc = tu_case_append_buf("failed assertion: %s", expr);
+    assert(rc == 0);
+}
+
+static void
+tu_case_write_pass_buf(void)
+{
+    if (tu_config.tc_print_results) {
+        printf("[pass] %s/%s\n", tu_suite_name, tu_case_name);
+        if (tu_case_buf_len > 0) {
+            printf("%s", tu_case_buf);
+        }
+        fflush(stdout);
+    }
+
+    tu_case_reported = 1;
+
+    if (tu_config.tc_case_pass_cb != NULL) {
+        tu_config.tc_case_pass_cb(tu_case_buf, tu_case_buf_len,
+                                  tu_config.tc_case_pass_arg);
+    }
+}
+
+static void
+tu_case_append_manual_pass_msg(void)
+{
+    int rc;
+
+    rc = tu_case_append_buf("manual pass");
+    assert(rc == 0);
+}
+
+void
+tu_case_write_pass_auto(void)
+{
+    if (!tu_case_reported) {
+        tu_case_buf_len = 0;
+        tu_case_write_pass_buf();
+    }
+}
+
+void
+tu_case_fail_assert(int fatal, const char *file, int line,
+                    const char *expr, const char *format, ...)
+{
+    va_list ap;
+    int rc;
+
+    if (tu_config.tc_system_assert) {
+        assert(0);
+    }
+
+    tu_case_buf_len = 0;
+
+    tu_case_append_file_info(file, line);
+    tu_case_append_assert_msg(expr);
+
+    if (format != NULL) {
+        rc = tu_case_append_buf("\n");
+        assert(rc == 0);
+
+        va_start(ap, format);
+        rc = tu_case_vappend_buf(format, ap);
+        assert(rc == 0);
+        va_end(ap);
+    }
+
+    rc = tu_case_append_buf("\n");
+    assert(rc == 0);
+
+    tu_case_write_fail_buf();
+
+    if (fatal) {
+        tu_case_abort();
+    }
+}
+
+void
+tu_case_pass_manual(const char *file, int line, const char *format, ...)
+{
+    va_list ap;
+    int rc;
+
+    if (tu_case_reported) {
+        return;
+    }
+
+    tu_case_buf_len = 0;
+
+    tu_case_append_file_info(file, line);
+    tu_case_append_manual_pass_msg();
+
+    if (format != NULL) {
+        rc = tu_case_append_buf("\n");
+        assert(rc == 0);
+
+        va_start(ap, format);
+        rc = tu_case_vappend_buf(format, ap);
+        assert(rc == 0);
+        va_end(ap);
+    }
+
+    rc = tu_case_append_buf("\n");
+    assert(rc == 0);
+
+    tu_case_write_pass_buf();
+
+    tu_case_abort();
+}

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/6a7432f4/test/testutil/src/suite.c
----------------------------------------------------------------------
diff --git a/test/testutil/src/suite.c b/test/testutil/src/suite.c
new file mode 100644
index 0000000..93ca639
--- /dev/null
+++ b/test/testutil/src/suite.c
@@ -0,0 +1,67 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <assert.h>
+#include "testutil/testutil.h"
+#include "testutil_priv.h"
+
+const char *tu_suite_name = 0;
+int tu_suite_failed = 0;
+
+static void
+tu_suite_set_name(const char *name)
+{
+    tu_suite_name = name;
+}
+
+/**
+ * Configures a callback that gets executed at the end of each test case in the
+ * current suite.  This is useful when there are some checks that should be
+ * performed at the end of each test (e.g., verify no memory leaks).  This
+ * callback is cleared when the current suite completes.
+ *
+ * @param cb                    The callback to execute at the end of each test
+ *                                  case.
+ * @param cb_arg                An optional argument that gets passed to the
+ *                                  callback.
+ */
+void
+tu_suite_set_post_test_cb(tu_post_test_fn_t *cb, void *cb_arg)
+{
+    tu_case_post_test_cb = cb;
+    tu_case_post_test_cb_arg = cb_arg;
+}
+
+void
+tu_suite_complete(void)
+{
+    tu_suite_set_post_test_cb(NULL, NULL);
+}
+
+void
+tu_suite_init(const char *name)
+{
+    tu_suite_failed = 0;
+
+    tu_suite_set_name(name);
+
+    if (tu_config.tc_suite_init_cb != NULL) {
+        tu_config.tc_suite_init_cb(tu_config.tc_suite_init_arg);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/6a7432f4/test/testutil/src/testutil.c
----------------------------------------------------------------------
diff --git a/test/testutil/src/testutil.c b/test/testutil/src/testutil.c
new file mode 100644
index 0000000..032a32c
--- /dev/null
+++ b/test/testutil/src/testutil.c
@@ -0,0 +1,50 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <assert.h>
+#include "os/os.h"
+#include "hal/hal_flash.h"
+#include "testutil/testutil.h"
+#include "testutil_priv.h"
+
+struct tu_config tu_config;
+int tu_any_failed;
+int tu_first_idx;
+
+int
+tu_init(void)
+{
+    os_init();
+
+    return 0;
+}
+
+void
+tu_restart(void)
+{
+    tu_case_write_pass_auto();
+
+    tu_first_idx = tu_case_idx + 1;
+
+    if (tu_config.tc_restart_cb != NULL) {
+        tu_config.tc_restart_cb(tu_config.tc_restart_arg);
+    }
+
+    tu_arch_restart();
+}

http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/6a7432f4/test/testutil/src/testutil_priv.h
----------------------------------------------------------------------
diff --git a/test/testutil/src/testutil_priv.h b/test/testutil/src/testutil_priv.h
new file mode 100644
index 0000000..3f8cb2d
--- /dev/null
+++ b/test/testutil/src/testutil_priv.h
@@ -0,0 +1,34 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef H_TESTUTIL_PRIV_
+#define H_TESTUTIL_PRIV_
+
+#include <stddef.h>
+#include <inttypes.h>
+
+#include "testutil/testutil.h"
+
+void tu_arch_restart(void);
+void tu_case_abort(void);
+
+extern tu_post_test_fn_t *tu_case_post_test_cb;
+extern void *tu_case_post_test_cb_arg;
+
+#endif