Add preprocessor and unit_test modules

This change adds two Pigweed modules: pw_preprocessor and pw_unit_test.
The preprocessor module contains header files providing helpful macros
for the C preprocessor. The unit test module contains a starter
implementation of a unit testing framework for Pigweed.

Change-Id: I46e1a4cae1fd8ce36d7840a2e92f8013fb489cde
diff --git a/.clang-format b/.clang-format
new file mode 100644
index 0000000..0398edc
--- /dev/null
+++ b/.clang-format
@@ -0,0 +1,7 @@
+BasedOnStyle: Google
+BinPackArguments: false
+BinPackParameters: false
+DerivePointerAlignment: false
+PointerAlignment: Left
+AllowShortIfStatementsOnASingleLine: false
+AllowShortLoopsOnASingleLine: false
diff --git a/BUILD.gn b/BUILD.gn
index 437d6e7..8d69e9b 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -15,6 +15,8 @@
 # All Pigweed modules that can be built using gn.
 group("pw_modules") {
   deps = [
+    "$dir_pw_preprocessor",
     "$dir_pw_status",
+    "$dir_pw_unit_test",
   ]
 }
diff --git a/modules.gni b/modules.gni
index d6647d7..be34793 100644
--- a/modules.gni
+++ b/modules.gni
@@ -23,5 +23,7 @@
 }
 
 dir_pw_build = "$dir_pigweed/pw_build"
+dir_pw_preprocessor = "$dir_pigweed/pw_preprocessor"
 dir_pw_status = "$dir_pigweed/pw_status"
 dir_pw_toolchain = "$dir_pigweed/pw_toolchain"
+dir_pw_unit_test = "$dir_pigweed/pw_unit_test"
diff --git a/pw_build/README.md b/pw_build/README.md
index 3879b11..8b56bbf 100644
--- a/pw_build/README.md
+++ b/pw_build/README.md
@@ -1 +1 @@
-# pw\_build: Definitions for Pigweed's build system.
+# pw\_build: Definitions for Pigweed's build system
diff --git a/pw_preprocessor/BUILD.gn b/pw_preprocessor/BUILD.gn
new file mode 100644
index 0000000..ccc89ad
--- /dev/null
+++ b/pw_preprocessor/BUILD.gn
@@ -0,0 +1,88 @@
+# Copyright 2019 The Pigweed Authors
+#
+# Licensed 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
+#
+#     https://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.
+
+config("default_config") {
+  include_dirs = [ "public" ]
+}
+
+source_set("pw_preprocessor") {
+  public_configs = [
+    "$dir_pw_build:pw_default_cpp",
+    ":default_config",
+  ]
+  public = [
+    "public/pw_preprocessor/boolean.h",
+    "public/pw_preprocessor/compiler.h",
+    "public/pw_preprocessor/concat.h",
+    "public/pw_preprocessor/macro_arg_count.h",
+    "public/pw_preprocessor/util.h",
+  ]
+  sources = public
+}
+
+group("pw_preprocessor_tests") {
+  deps = [
+    ":boolean_test",
+    ":concat_test",
+    ":macro_arg_count_test",
+    ":util_test",
+  ]
+}
+
+group("pw_preprocessor_tests_linux") {
+  deps = [
+    ":pw_preprocessor_tests($dir_pw_toolchain:x86_linux_o2)",
+  ]
+}
+
+# TODO(frolv): Change these to special unit test executables.
+executable("boolean_test") {
+  deps = [
+    ":pw_preprocessor",
+    "$dir_pw_unit_test:main",
+  ]
+  sources = [
+    "boolean_test.cc",
+  ]
+}
+
+executable("concat_test") {
+  deps = [
+    ":pw_preprocessor",
+    "$dir_pw_unit_test:main",
+  ]
+  sources = [
+    "concat_test.cc",
+  ]
+}
+
+executable("macro_arg_count_test") {
+  deps = [
+    ":pw_preprocessor",
+    "$dir_pw_unit_test:main",
+  ]
+  sources = [
+    "macro_arg_count_test.cc",
+  ]
+}
+
+executable("util_test") {
+  deps = [
+    ":pw_preprocessor",
+    "$dir_pw_unit_test:main",
+  ]
+  sources = [
+    "util_test.cc",
+  ]
+}
diff --git a/pw_preprocessor/README.md b/pw_preprocessor/README.md
new file mode 100644
index 0000000..d564809
--- /dev/null
+++ b/pw_preprocessor/README.md
@@ -0,0 +1,4 @@
+# pw\_preprocessor: Useful C preprocessor macros
+
+The pw\_preprocessor module provides several helpful preprocessor macros for use
+in C and C++ code.
diff --git a/pw_preprocessor/boolean_test.cc b/pw_preprocessor/boolean_test.cc
new file mode 100644
index 0000000..ab8ada3
--- /dev/null
+++ b/pw_preprocessor/boolean_test.cc
@@ -0,0 +1,81 @@
+// Copyright 2019 The Pigweed Authors
+//
+// Licensed 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
+//
+//     https://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.
+//
+// All of these tests are static asserts. If the test compiles, it has already
+// passed. The TEST functions are used for organization only.
+#include "pw_preprocessor/boolean.h"
+
+#include "pw_unit_test/framework.h"
+
+namespace pw {
+namespace {
+
+#define ONE 1
+#define ZERO() 0
+
+TEST(BooleanMacros, And) {
+  static_assert(PW_AND(ZERO(), 0) == 0);
+  static_assert(PW_AND(ZERO(), ONE) == 0);
+  static_assert(PW_AND(1, 0) == 0);
+  static_assert(PW_AND(ONE, PW_NOT(ZERO())) == 1);
+}
+
+TEST(BooleanMacros, Or) {
+  static_assert(PW_OR(ZERO(), 0) == 0);
+  static_assert(PW_OR(ZERO(), ONE) == 1);
+  static_assert(PW_OR(1, 0) == 1);
+  static_assert(PW_OR(ONE, PW_NOT(ZERO())) == 1);
+}
+
+TEST(BooleanMacros, Not) {
+  static_assert(PW_NOT(0) == 1);
+  static_assert(PW_NOT(1) == 0);
+  static_assert(PW_NOT(ONE) == 0);
+  static_assert(PW_NOT(ZERO()) == 1);
+}
+
+TEST(BooleanMacros, Xor) {
+  static_assert(PW_XOR(ZERO(), 0) == 0);
+  static_assert(PW_XOR(ZERO(), ONE) == 1);
+  static_assert(PW_XOR(1, 0) == 1);
+  static_assert(PW_XOR(ONE, PW_NOT(ZERO())) == 0);
+}
+
+TEST(BooleanMacros, Nand) {
+  static_assert(PW_NAND(ZERO(), 0) == 1);
+  static_assert(PW_NAND(ZERO(), ONE) == 1);
+  static_assert(PW_NAND(1, 0) == 1);
+  static_assert(PW_NAND(ONE, PW_NOT(ZERO())) == 0);
+}
+
+TEST(BooleanMacros, Nor) {
+  static_assert(PW_NOR(ZERO(), 0) == 1);
+  static_assert(PW_NOR(ZERO(), ONE) == 0);
+  static_assert(PW_NOR(1, 0) == 0);
+  static_assert(PW_NOR(ONE, PW_NOT(ZERO())) == 0);
+}
+
+TEST(BooleanMacros, Xnor) {
+  static_assert(PW_XNOR(ZERO(), 0) == 1);
+  static_assert(PW_XNOR(ZERO(), ONE) == 0);
+  static_assert(PW_XNOR(1, 0) == 0);
+  static_assert(PW_XNOR(ONE, PW_NOT(ZERO())) == 1);
+}
+
+TEST(BooleanMacros, Nested) {
+  static_assert(PW_AND(1, PW_AND(PW_OR(ZERO(), ONE), PW_XOR(ONE, 0))) == 1);
+}
+
+}  // namespace
+}  // namespace pw
diff --git a/pw_preprocessor/concat_test.cc b/pw_preprocessor/concat_test.cc
new file mode 100644
index 0000000..cd3af3a
--- /dev/null
+++ b/pw_preprocessor/concat_test.cc
@@ -0,0 +1,86 @@
+// Copyright 2019 The Pigweed Authors
+//
+// Licensed 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
+//
+//     https://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 "pw_preprocessor/concat.h"
+
+#include "pw_preprocessor/util.h"
+#include "pw_unit_test/framework.h"
+
+namespace pw {
+namespace {
+
+TEST(Concat, WithoutMacroExpansions) {
+  static_assert(PW_CONCAT() 9000 PW_CONCAT() == 9000);
+  static_assert(PW_CONCAT(1, 2) == 12);
+  static_assert(PW_CONCAT(1, 2, 3, 4) == 1234);
+  static_assert(PW_CONCAT(1, 2, 3, 4, 5) == 12345);
+  static_assert(PW_CONCAT(1, 2, 3, 4, 5, 6) == 123456);
+  static_assert(PW_CONCAT(1, 2, 3, 4, 5, 6, 7) == 1234567);
+  static_assert(PW_CONCAT(1, 2, 3, 4, 5, 6, 7, 8) == 12345678);
+
+  static_assert(PW_CONCAT(0x, 3, 4, 5, 6, 7, 8, llu) == 0x345678llu);
+  static_assert(PW_CONCAT(0x, 3, 4, 5, 6, 7, 8, 9, llu) == 0x3456789llu);
+  static_assert(PW_CONCAT(0x, 3, 4, 5, 6, 7, 8, 9, A, llu) == 0x3456789Allu);
+  static_assert(PW_CONCAT(0x, 3, 4, 5, 6, 7, 8, 9, A, B, llu) ==
+                0x3456789ABllu);
+  static_assert(PW_CONCAT(0x, 3, 4, 5, 6, 7, 8, 9, A, B, C, llu) ==
+                0x3456789ABCllu);
+  static_assert(PW_CONCAT(0x, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, llu) ==
+                0x3456789ABCDllu);
+  static_assert(PW_CONCAT(0x, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, llu) ==
+                0x3456789ABCDEllu);
+  static_assert(PW_CONCAT(0x, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F, llu) ==
+                0x3456789ABCDEFllu);
+  static_assert(PW_CONCAT(0x, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F, F, llu) ==
+                0x3456789ABCDEFFllu);
+}
+
+// Macros which expand out to test that PW_CONCAT properly expands them.
+#define _PW_TEST_SECTION(x) "section: " x
+#define _PW_TEST_NUMBER 123
+#define _PW_OUTER_MACRO(section_name) \
+  _PW_TEST_SECTION(PW_STRINGIFY(PW_CONCAT(section_name, _PW_TEST_NUMBER)))
+
+#define _PW_WHOSE() my
+#define _PW_WHAT var
+
+TEST(Concat, WithMacroExpansions) {
+  static_assert(PW_CONCAT(_PW_TEST_NUMBER, 5) == 1235);
+
+  int my_var;
+  EXPECT_EQ(&PW_CONCAT(_PW_WHOSE(), _, _PW_WHAT), &my_var);
+
+  EXPECT_STREQ(_PW_OUTER_MACRO(what is up), "section: what is up123");
+}
+
+// Test concatenation up to the maximum supported length.
+TEST(Concat, MaximumConcatenationLength) {
+  constexpr int value31_234567890123456789012345678901 = 1337;
+  constexpr int value32_2345678901234567890123456789012 = 101010;
+
+  // clang-format off
+  static_assert(PW_CONCAT(value31_, 2, 3, 4, 5, 6, 7, 8, 9, 0,
+                                 1, 2, 3, 4, 5, 6, 7, 8, 9, 0,
+                                 1, 2, 3, 4, 5, 6, 7, 8, 9, 0,
+                                 1) == 1337);
+
+  static_assert(PW_CONCAT(value32_, 2, 3, 4, 5, 6, 7, 8, 9, 0,
+                                 1, 2, 3, 4, 5, 6, 7, 8, 9, 0,
+                                 1, 2, 3, 4, 5, 6, 7, 8, 9, 0,
+                                 1, 2) == 101010);
+  // clang-format on
+}
+
+}  // namespace
+}  // namespace pw
diff --git a/pw_preprocessor/docs.rst b/pw_preprocessor/docs.rst
new file mode 100644
index 0000000..37ddbf6
--- /dev/null
+++ b/pw_preprocessor/docs.rst
@@ -0,0 +1,63 @@
+.. _chapter-preprocessor:
+
+.. default-domain:: cpp
+
+.. highlight:: sh
+
+------------
+Preprocessor
+------------
+The preprocessor module provides various helpful preprocessor macros.
+
+Compatibility
+=============
+C and C++
+
+Dependencies
+============
+This module has no dependencies.
+
+Headers
+=======
+The preprocessor module provides several headers.
+
+pw_preprocessor/boolean.h
+-------------------------
+Defines macros for boolean logic on literal 1s and 0s. This is useful for
+situations when a literal is needed to build the name of a function or macro.
+
+pw_preprocessor/compiler.h
+--------------------------
+Macros for compiler-specific features, such as attributes or builtins.
+
+pw_preprocessor/concat.h
+------------------------
+Defines the ``PW_CONCAT(...)`` macro, which expands its arguments if they are
+macros and token pastes the results. This can be used for building names of
+classes, variables, macros, etc.
+
+pw_preprocessor/macro_arg_count.h
+---------------------------------
+Defines the ``PW_ARG_COUNT(...)`` macro, which counts the number of arguments it
+was passed. It can be invoked directly or with ``__VA_ARGS__`` in another macro.
+``PW_ARG_COUNT(...)``  evaluates to a literal of the number of arguments which
+can be used directly or concatenated to build other names. Unlike many common
+implementations, this macro correctly evaluates to ``0`` when it is invoked
+without arguments.
+
+This header also defines ``PW_HAS_ARGS(...)`` and ``PW_HAS_NO_ARGS(...)``,
+which evaluate to ``1`` or ``0`` depending on whether they are invoked with
+arguments.
+
+pw_preprocessor/util.h
+----------------------
+General purpose, useful macros.
+
+* ``PW_ARRAY_SIZE(array)`` -- calculates the size of a C array
+* ``PW_UNUSED(value)`` -- silences "unused variable" compiler warnings
+* ``PW_STRINGIFY(...)`` -- expands its arguments as macros and converts them to
+  a string literal
+* ``PW_EXTERN_C`` -- declares a name to be ``extern "C"`` in C++; expands to
+  nothing in C
+* ``PW_EXTERN_C_START`` / ``PW_EXTERN_C_END`` -- declares an ``extern "C" { }``
+  block in C++; expands to nothing in C
diff --git a/pw_preprocessor/macro_arg_count_test.cc b/pw_preprocessor/macro_arg_count_test.cc
new file mode 100644
index 0000000..3b483b9
--- /dev/null
+++ b/pw_preprocessor/macro_arg_count_test.cc
@@ -0,0 +1,255 @@
+// Copyright 2019 The Pigweed Authors
+//
+// Licensed 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
+//
+//     https://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.
+//
+// All of these tests are static asserts. If the test compiles, it has already
+// passed. The TEST functions are used for organization only.
+
+#include "pw_preprocessor/macro_arg_count.h"
+
+#include "pw_unit_test/framework.h"
+
+namespace pw {
+namespace {
+
+TEST(HasArgs, WithoutArguments) {
+  static_assert(PW_HAS_ARGS() == 0);
+  static_assert(PW_HAS_ARGS(/**/) == 0);
+  static_assert(PW_HAS_ARGS(/* uhm, hi */) == 0);
+
+  // Test how the macro handles whitespace and comments.
+  // clang-format off
+  static_assert(PW_HAS_ARGS(     ) == 0);  // NOLINT
+  static_assert(PW_HAS_ARGS(
+      ) == 0);  // NOLINT
+  static_assert(PW_HAS_ARGS(
+      // wow
+      // This is a comment.
+      ) == 0);  // NOLINT
+  // clang-format on
+
+  static_assert(PW_HAS_NO_ARGS() == 1);
+  static_assert(PW_HAS_NO_ARGS(/* hello */) == 1);
+  static_assert(PW_HAS_NO_ARGS(
+                    // hello
+                    /* goodbye */) == 1);
+}
+
+TEST(HasArgs, WithArguments) {
+  static_assert(PW_HAS_ARGS(()) == 1);
+  static_assert(PW_HAS_ARGS(0) == 1);
+  static_assert(PW_HAS_ARGS(, ) == 1);  // NOLINT
+  static_assert(PW_HAS_ARGS(a, b, c) == 1);
+  static_assert(PW_HAS_ARGS(PW_HAS_ARGS) == 1);
+  static_assert(PW_HAS_ARGS(PW_HAS_ARGS()) == 1);
+
+  static_assert(PW_HAS_NO_ARGS(0) == 0);
+  static_assert(PW_HAS_NO_ARGS(, ) == 0);  // NOLINT
+  static_assert(PW_HAS_NO_ARGS(a, b, c) == 0);
+  static_assert(PW_HAS_NO_ARGS(PW_HAS_ARGS) == 0);
+  static_assert(PW_HAS_NO_ARGS(PW_HAS_ARGS()) == 0);
+}
+
+constexpr int TestFunc(int arg, ...) { return arg; }
+
+#define CALL_FUNCTION(arg, ...) TestFunc(arg PW_COMMA_ARGS(__VA_ARGS__))
+
+template <typename T, typename... Args>
+constexpr T TemplateArgCount() {
+  return sizeof...(Args);
+}
+
+#define COUNT_ARGS_TEMPLATE(...) \
+  TemplateArgCount<int PW_COMMA_ARGS(__VA_ARGS__)>()
+
+TEST(CommaVarargs, NoArguments) {
+  static_assert(TestFunc(0 PW_COMMA_ARGS()) == 0);
+  static_assert(TestFunc(1 /* whoa */ PW_COMMA_ARGS(
+                    /* this macro */) /* is cool! */) == 1);
+
+  static_assert(TemplateArgCount<int PW_COMMA_ARGS()>() == 0);
+  static_assert(TemplateArgCount<int PW_COMMA_ARGS(/* nothing */)>() == 0);
+
+  static_assert(CALL_FUNCTION(2) == 2);
+  static_assert(CALL_FUNCTION(3, ) == 3);
+  static_assert(CALL_FUNCTION(4, /* nothing */) == 4);
+
+  static_assert(COUNT_ARGS_TEMPLATE() == 0);
+  static_assert(COUNT_ARGS_TEMPLATE(/* nothing */) == 0);
+}
+
+TEST(CommaVarargs, WithArguments) {
+  static_assert(TestFunc(0 PW_COMMA_ARGS(1)) == 0);
+  static_assert(TestFunc(1 PW_COMMA_ARGS(1, 2)) == 1);
+  static_assert(TestFunc(2 PW_COMMA_ARGS(1, 2, "three")) == 2);
+
+  static_assert(TemplateArgCount<int PW_COMMA_ARGS(bool)>() == 1);
+  static_assert(TemplateArgCount<int PW_COMMA_ARGS(char, const char*)>() == 2);
+  static_assert(TemplateArgCount<int PW_COMMA_ARGS(int, char, const char*)>() ==
+                3);
+
+  static_assert(CALL_FUNCTION(3) == 3);
+  static_assert(CALL_FUNCTION(4, ) == 4);
+  static_assert(CALL_FUNCTION(5, /* nothing */) == 5);
+
+  static_assert(COUNT_ARGS_TEMPLATE(int) == 1);
+  static_assert(COUNT_ARGS_TEMPLATE(int, int) == 2);
+  static_assert(COUNT_ARGS_TEMPLATE(int, int, int) == 3);
+}
+
+TEST(CountArgs, Zero) {
+  static_assert(PW_ARG_COUNT() == 0);
+  static_assert(PW_ARG_COUNT(/**/) == 0);
+  static_assert(PW_ARG_COUNT(/* uhm, hi */) == 0);
+
+  // clang-format off
+  static_assert(PW_ARG_COUNT(     ) == 0);  // NOLINT
+  static_assert(PW_ARG_COUNT(
+      ) == 0);  // NOLINT
+  static_assert(PW_ARG_COUNT(
+      // wow
+      // This is a comment.
+      ) == 0);  // NOLINT
+  // clang-format on
+}
+
+TEST(CountArgs, Commas) {
+  // clang-format off
+  static_assert(PW_ARG_COUNT(,) == 2);    // NOLINT
+  static_assert(PW_ARG_COUNT(,,) == 3);   // NOLINT
+  static_assert(PW_ARG_COUNT(,,,) == 4);  // NOLINT
+  // clang-format on
+  static_assert(PW_ARG_COUNT(, ) == 2);      // NOLINT
+  static_assert(PW_ARG_COUNT(, , ) == 3);    // NOLINT
+  static_assert(PW_ARG_COUNT(, , , ) == 4);  // NOLINT
+}
+
+TEST(CountArgs, Parentheses) {
+  static_assert(PW_ARG_COUNT(()) == 1);
+  static_assert(PW_ARG_COUNT((1, 2, 3, 4)) == 1);
+  static_assert(PW_ARG_COUNT((1, 2, 3), (1, 2, 3, 4)) == 2);
+  static_assert(PW_ARG_COUNT((), ()) == 2);
+  static_assert(PW_ARG_COUNT((-), (o)) == 2);
+  static_assert(PW_ARG_COUNT((, , (, , ), ), (123, 4)) == 2);  // NOLINT
+  static_assert(PW_ARG_COUNT(1, (2, 3, 4), (<5, 6>)) == 3);
+}
+
+#define SOME_VARIADIC_MACRO(...) PW_ARG_COUNT(__VA_ARGS__)
+
+#define ANOTHER_VARIADIC_MACRO(arg, ...) SOME_VARIADIC_MACRO(__VA_ARGS__)
+
+#define ALWAYS_ONE_ARG(...) SOME_VARIADIC_MACRO((__VA_ARGS__))
+
+TEST(CountArgs, NestedMacros) {
+  static_assert(SOME_VARIADIC_MACRO() == 0);
+  static_assert(SOME_VARIADIC_MACRO(X1) == 1);
+  static_assert(SOME_VARIADIC_MACRO(X1, X2) == 2);
+  static_assert(SOME_VARIADIC_MACRO(X1, X2, X3) == 3);
+  static_assert(SOME_VARIADIC_MACRO(X1, X2, X3, X4) == 4);
+  static_assert(SOME_VARIADIC_MACRO(X1, X2, X3, X4, X5) == 5);
+
+  static_assert(ANOTHER_VARIADIC_MACRO() == 0);
+  static_assert(ANOTHER_VARIADIC_MACRO(X0) == 0);
+  static_assert(ANOTHER_VARIADIC_MACRO(X0, X1) == 1);
+  static_assert(ANOTHER_VARIADIC_MACRO(X0, X1, X2) == 2);
+  static_assert(ANOTHER_VARIADIC_MACRO(X0, X1, X2, X3) == 3);
+  static_assert(ANOTHER_VARIADIC_MACRO(X0, X1, X2, X3, X4) == 4);
+  static_assert(ANOTHER_VARIADIC_MACRO(X0, X1, X2, X3, X4, X5) == 5);
+
+  static_assert(ALWAYS_ONE_ARG() == 1);
+  static_assert(ALWAYS_ONE_ARG(X0) == 1);
+  static_assert(ALWAYS_ONE_ARG(X0, X1) == 1);
+  static_assert(ALWAYS_ONE_ARG(X0, X1, X2) == 1);
+  static_assert(ALWAYS_ONE_ARG(X0, X1, X2, X3) == 1);
+  static_assert(ALWAYS_ONE_ARG(X0, X1, X2, X3, X4) == 1);
+  static_assert(ALWAYS_ONE_ARG(X0, X1, X2, X3, X4, X5) == 1);
+}
+
+/* Tests all supported arg counts. This test was generated by the following
+   Python 3 code:
+for i in range(64 + 1):
+  args = [f'X{x}' for x in range(1, i + 1)]
+  print(f'  static_assert(PW_ARG_COUNT({", ".join(args)}) == {i})  // NOLINT')
+*/
+TEST(CountArgs, AllSupported) {
+  // clang-format off
+  static_assert(PW_ARG_COUNT() == 0);  // NOLINT
+  static_assert(PW_ARG_COUNT(X1) == 1);  // NOLINT
+  static_assert(PW_ARG_COUNT(X1, X2) == 2);  // NOLINT
+  static_assert(PW_ARG_COUNT(X1, X2, X3) == 3);  // NOLINT
+  static_assert(PW_ARG_COUNT(X1, X2, X3, X4) == 4);  // NOLINT
+  static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5) == 5);  // NOLINT
+  static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6) == 6);  // NOLINT
+  static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7) == 7);  // NOLINT
+  static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8) == 8);  // NOLINT
+  static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9) == 9);  // NOLINT
+  static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10) == 10);  // NOLINT
+  static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11) == 11);  // NOLINT
+  static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12) == 12);  // NOLINT
+  static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13) == 13);  // NOLINT
+  static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14) == 14);  // NOLINT
+  static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15) == 15);  // NOLINT
+  static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16) == 16);  // NOLINT
+  static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17) == 17);  // NOLINT
+  static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18) == 18);  // NOLINT
+  static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19) == 19);  // NOLINT
+  static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20) == 20);  // NOLINT
+  static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21) == 21);  // NOLINT
+  static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22) == 22);  // NOLINT
+  static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23) == 23);  // NOLINT
+  static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24) == 24);  // NOLINT
+  static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25) == 25);  // NOLINT
+  static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26) == 26);  // NOLINT
+  static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27) == 27);  // NOLINT
+  static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28) == 28);  // NOLINT
+  static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, X29) == 29);  // NOLINT
+  static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, X29, X30) == 30);  // NOLINT
+  static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, X29, X30, X31) == 31);  // NOLINT
+  static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, X29, X30, X31, X32) == 32);  // NOLINT
+  static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, X29, X30, X31, X32, X33) == 33);  // NOLINT
+  static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, X29, X30, X31, X32, X33, X34) == 34);  // NOLINT
+  static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, X29, X30, X31, X32, X33, X34, X35) == 35);  // NOLINT
+  static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, X29, X30, X31, X32, X33, X34, X35, X36) == 36);  // NOLINT
+  static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, X29, X30, X31, X32, X33, X34, X35, X36, X37) == 37);  // NOLINT
+  static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, X29, X30, X31, X32, X33, X34, X35, X36, X37, X38) == 38);  // NOLINT
+  static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, X29, X30, X31, X32, X33, X34, X35, X36, X37, X38, X39) == 39);  // NOLINT
+  static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, X29, X30, X31, X32, X33, X34, X35, X36, X37, X38, X39, X40) == 40);  // NOLINT
+  static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, X29, X30, X31, X32, X33, X34, X35, X36, X37, X38, X39, X40, X41) == 41);  // NOLINT
+  static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, X29, X30, X31, X32, X33, X34, X35, X36, X37, X38, X39, X40, X41, X42) == 42);  // NOLINT
+  static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, X29, X30, X31, X32, X33, X34, X35, X36, X37, X38, X39, X40, X41, X42, X43) == 43);  // NOLINT
+  static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, X29, X30, X31, X32, X33, X34, X35, X36, X37, X38, X39, X40, X41, X42, X43, X44) == 44);  // NOLINT
+  static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, X29, X30, X31, X32, X33, X34, X35, X36, X37, X38, X39, X40, X41, X42, X43, X44, X45) == 45);  // NOLINT
+  static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, X29, X30, X31, X32, X33, X34, X35, X36, X37, X38, X39, X40, X41, X42, X43, X44, X45, X46) == 46);  // NOLINT
+  static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, X29, X30, X31, X32, X33, X34, X35, X36, X37, X38, X39, X40, X41, X42, X43, X44, X45, X46, X47) == 47);  // NOLINT
+  static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, X29, X30, X31, X32, X33, X34, X35, X36, X37, X38, X39, X40, X41, X42, X43, X44, X45, X46, X47, X48) == 48);  // NOLINT
+  static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, X29, X30, X31, X32, X33, X34, X35, X36, X37, X38, X39, X40, X41, X42, X43, X44, X45, X46, X47, X48, X49) == 49);  // NOLINT
+  static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, X29, X30, X31, X32, X33, X34, X35, X36, X37, X38, X39, X40, X41, X42, X43, X44, X45, X46, X47, X48, X49, X50) == 50);  // NOLINT
+  static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, X29, X30, X31, X32, X33, X34, X35, X36, X37, X38, X39, X40, X41, X42, X43, X44, X45, X46, X47, X48, X49, X50, X51) == 51);  // NOLINT
+  static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, X29, X30, X31, X32, X33, X34, X35, X36, X37, X38, X39, X40, X41, X42, X43, X44, X45, X46, X47, X48, X49, X50, X51, X52) == 52);  // NOLINT
+  static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, X29, X30, X31, X32, X33, X34, X35, X36, X37, X38, X39, X40, X41, X42, X43, X44, X45, X46, X47, X48, X49, X50, X51, X52, X53) == 53);  // NOLINT
+  static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, X29, X30, X31, X32, X33, X34, X35, X36, X37, X38, X39, X40, X41, X42, X43, X44, X45, X46, X47, X48, X49, X50, X51, X52, X53, X54) == 54);  // NOLINT
+  static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, X29, X30, X31, X32, X33, X34, X35, X36, X37, X38, X39, X40, X41, X42, X43, X44, X45, X46, X47, X48, X49, X50, X51, X52, X53, X54, X55) == 55);  // NOLINT
+  static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, X29, X30, X31, X32, X33, X34, X35, X36, X37, X38, X39, X40, X41, X42, X43, X44, X45, X46, X47, X48, X49, X50, X51, X52, X53, X54, X55, X56) == 56);  // NOLINT
+  static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, X29, X30, X31, X32, X33, X34, X35, X36, X37, X38, X39, X40, X41, X42, X43, X44, X45, X46, X47, X48, X49, X50, X51, X52, X53, X54, X55, X56, X57) == 57);  // NOLINT
+  static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, X29, X30, X31, X32, X33, X34, X35, X36, X37, X38, X39, X40, X41, X42, X43, X44, X45, X46, X47, X48, X49, X50, X51, X52, X53, X54, X55, X56, X57, X58) == 58);  // NOLINT
+  static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, X29, X30, X31, X32, X33, X34, X35, X36, X37, X38, X39, X40, X41, X42, X43, X44, X45, X46, X47, X48, X49, X50, X51, X52, X53, X54, X55, X56, X57, X58, X59) == 59);  // NOLINT
+  static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, X29, X30, X31, X32, X33, X34, X35, X36, X37, X38, X39, X40, X41, X42, X43, X44, X45, X46, X47, X48, X49, X50, X51, X52, X53, X54, X55, X56, X57, X58, X59, X60) == 60);  // NOLINT
+  static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, X29, X30, X31, X32, X33, X34, X35, X36, X37, X38, X39, X40, X41, X42, X43, X44, X45, X46, X47, X48, X49, X50, X51, X52, X53, X54, X55, X56, X57, X58, X59, X60, X61) == 61);  // NOLINT
+  static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, X29, X30, X31, X32, X33, X34, X35, X36, X37, X38, X39, X40, X41, X42, X43, X44, X45, X46, X47, X48, X49, X50, X51, X52, X53, X54, X55, X56, X57, X58, X59, X60, X61, X62) == 62);  // NOLINT
+  static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, X29, X30, X31, X32, X33, X34, X35, X36, X37, X38, X39, X40, X41, X42, X43, X44, X45, X46, X47, X48, X49, X50, X51, X52, X53, X54, X55, X56, X57, X58, X59, X60, X61, X62, X63) == 63);  // NOLINT
+  static_assert(PW_ARG_COUNT(X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, X29, X30, X31, X32, X33, X34, X35, X36, X37, X38, X39, X40, X41, X42, X43, X44, X45, X46, X47, X48, X49, X50, X51, X52, X53, X54, X55, X56, X57, X58, X59, X60, X61, X62, X63, X64) == 64);  // NOLINT
+  // clang-format on
+}
+
+}  // namespace
+}  // namespace pw
diff --git a/pw_preprocessor/public/pw_preprocessor/boolean.h b/pw_preprocessor/public/pw_preprocessor/boolean.h
new file mode 100644
index 0000000..cb2af18
--- /dev/null
+++ b/pw_preprocessor/public/pw_preprocessor/boolean.h
@@ -0,0 +1,74 @@
+// Copyright 2019 The Pigweed Authors
+//
+// Licensed 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
+//
+//     https://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.
+
+#pragma once
+
+// Preprocessor boolean operation macros that evaluate to 0 or 1.
+//
+// These macros perform boolean operations in the C preprocessor that evaluate
+// to a literal 1 or 0. They can be used for a few purposes:
+//
+//   - Generate other macros that evaluate to a 1 or 0, instead of a
+//     parenthesized boolean expression.
+//   - Ensure that the operands are defined and evaluate to 1 or 0 themselves.
+//   - Write macros that conditionally use other macros by token pasting the
+//     resulting 1 or 0 to form a new macro name.
+//
+// These macros should not be used outside of macro definitions. Use normal C
+// operators (&&, ||, !, ==, !=) instead. For example, to check whether two
+// flags are set, the C operators are the best choice:
+//
+//   #if RELEASE && OPTIMIZED
+//
+// However, there are cases when a literal 0 or 1 is required. For example:
+//
+//   #define SELECT_ALGORITHM() PW_CONCAT(ALGO_, PW_AND(RELEASE, OPTIMIZED))
+//
+// SELECT_ALGORITHM evaluates to ALGO_0 or ALGO_1, depending on whether RELEASE
+// and OPTIMIZED are set to 1.
+
+// Boolean AND of two preprocessor expressions that evaluate to 0 or 1.
+#define PW_AND(a, b) _PW_AND(a, b)      // Expand the macro an extra time to
+#define _PW_AND(a, b) _PW_AND_##a##b()  // allow macro substitution to occur.
+#define _PW_AND_00() 0
+#define _PW_AND_01() 0
+#define _PW_AND_10() 0
+#define _PW_AND_11() 1
+
+// Boolean OR of two preprocessor expressions that evaluate to 0 or 1.
+#define PW_OR(a, b) _PW_OR(a, b)
+#define _PW_OR(a, b) _PW_OR_##a##b()
+#define _PW_OR_00() 0
+#define _PW_OR_01() 1
+#define _PW_OR_10() 1
+#define _PW_OR_11() 1
+
+// Boolean NOT of a preprocessor expression that evaluates to 0 or 1.
+#define PW_NOT(value) _PW_NOT(value)
+#define _PW_NOT(value) _PW_NOT_##value()
+#define _PW_NOT_0() 1
+#define _PW_NOT_1() 0
+
+// Boolean XOR of two preprocessor expressions that evaluate to 0 or 1.
+#define PW_XOR(a, b) _PW_XOR(a, b)
+#define _PW_XOR(a, b) _PW_XOR_##a##b()
+#define _PW_XOR_00() 0
+#define _PW_XOR_01() 1
+#define _PW_XOR_10() 1
+#define _PW_XOR_11() 0
+
+// Boolean NAND, NOR, and XNOR of expressions that evaluate to 0 or 1.
+#define PW_NAND(a, b) PW_NOT(PW_AND(a, b))
+#define PW_NOR(a, b) PW_NOT(PW_OR(a, b))
+#define PW_XNOR(a, b) PW_NOT(PW_XOR(a, b))
diff --git a/pw_preprocessor/public/pw_preprocessor/compiler.h b/pw_preprocessor/public/pw_preprocessor/compiler.h
new file mode 100644
index 0000000..eb1e433
--- /dev/null
+++ b/pw_preprocessor/public/pw_preprocessor/compiler.h
@@ -0,0 +1,60 @@
+// Copyright 2019 The Pigweed Authors
+//
+// Licensed 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
+//
+//     https://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.
+//
+// Preprocessor macros that wrap compiler-specific features.
+// This file is used by both C++ and C code.
+#pragma once
+
+// Marks a struct or class as packed.
+#define PW_PACKED(declaration) declaration __attribute__((packed))
+
+// Marks a function or object as used, ensuring code for it is generated.
+#define PW_USED __attribute__((used))
+
+// Prevents generation of a prologue or epilogue for a function. This is
+// helpful when implementing the function in assembly.
+#define PW_NO_PROLOGUE __attribute__((naked))
+
+// Marks that a function declaration takes a printf-style format string and
+// variadic arguments. This allows the compiler to perform check the validity of
+// the format string and arguments. This macro must only be on the function
+// declaration, not the definition.
+//
+// The format_index is index of the format string parameter and parameter_index
+// is the starting index of the variadic arguments. Indices start at 1. For C++
+// class member functions, add one to the index to account for the implicit this
+// parameter.
+//
+// This example shows a function where the format string is argument 2 and the
+// varargs start at argument 3.
+//
+//   int PrintfStyleFunction(char* buffer,
+//                           const char* fmt, ...) PW_PRINTF_FORMAT(2,3);
+//
+//   int PrintfStyleFunction(char* buffer, const char* fmt, ...) {
+//     ... implementation here ...
+//   }
+//
+#define PW_PRINTF_FORMAT(format_index, parameter_index) \
+  __attribute__((format(printf, format_index, parameter_index)))
+
+// Places a variable in the specified linker section and directs the compiler
+// to keep the variable, even if it is not used. Depending on the linker
+// options, the linker may still remove this section if it is not declared in
+// the linker script and marked KEEP.
+#if __APPLE__
+#define PW_KEEP_IN_SECTION(name) __attribute__((section("__DATA," name), used))
+#else
+#define PW_KEEP_IN_SECTION(name) __attribute__((section(name), used))
+#endif  // __APPLE__
diff --git a/pw_preprocessor/public/pw_preprocessor/concat.h b/pw_preprocessor/public/pw_preprocessor/concat.h
new file mode 100644
index 0000000..7b6f135
--- /dev/null
+++ b/pw_preprocessor/public/pw_preprocessor/concat.h
@@ -0,0 +1,66 @@
+// Copyright 2019 The Pigweed Authors
+//
+// Licensed 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
+//
+//     https://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
+
+#pragma once
+
+#include "pw_preprocessor/macro_arg_count.h"
+
+// Expands macros and concatenates the results using preprocessor ##
+// concatentation. Supports up to 32 arguments.
+#define PW_CONCAT(...) _PW_CONCAT_IMPL1(PW_ARG_COUNT(__VA_ARGS__), __VA_ARGS__)
+
+// Expand the macro to allow PW_ARG_COUNT and any caller-provided macros to be
+// evaluated before concatenating the tokens.
+#define _PW_CONCAT_IMPL1(count, ...) _PW_CONCAT_IMPL2(count, __VA_ARGS__)
+#define _PW_CONCAT_IMPL2(count, ...) _PW_CONCAT_##count(__VA_ARGS__)
+
+// clang-format off
+/* This macro implementation was generated with the following Python 3 code:
+for i in range(32 + 1):
+  args = [f'a{x}' for x in range(1, i + 1)]
+  print(f'#define _PW_CONCAT_{i}({", ".join(args)}) {"##".join(args)}  // NOLINT')
+*/
+
+#define _PW_CONCAT_0()   // NOLINT
+#define _PW_CONCAT_1(a1) a1  // NOLINT
+#define _PW_CONCAT_2(a1, a2) a1##a2  // NOLINT
+#define _PW_CONCAT_3(a1, a2, a3) a1##a2##a3  // NOLINT
+#define _PW_CONCAT_4(a1, a2, a3, a4) a1##a2##a3##a4  // NOLINT
+#define _PW_CONCAT_5(a1, a2, a3, a4, a5) a1##a2##a3##a4##a5  // NOLINT
+#define _PW_CONCAT_6(a1, a2, a3, a4, a5, a6) a1##a2##a3##a4##a5##a6  // NOLINT
+#define _PW_CONCAT_7(a1, a2, a3, a4, a5, a6, a7) a1##a2##a3##a4##a5##a6##a7  // NOLINT
+#define _PW_CONCAT_8(a1, a2, a3, a4, a5, a6, a7, a8) a1##a2##a3##a4##a5##a6##a7##a8  // NOLINT
+#define _PW_CONCAT_9(a1, a2, a3, a4, a5, a6, a7, a8, a9) a1##a2##a3##a4##a5##a6##a7##a8##a9  // NOLINT
+#define _PW_CONCAT_10(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) a1##a2##a3##a4##a5##a6##a7##a8##a9##a10  // NOLINT
+#define _PW_CONCAT_11(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11) a1##a2##a3##a4##a5##a6##a7##a8##a9##a10##a11  // NOLINT
+#define _PW_CONCAT_12(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12) a1##a2##a3##a4##a5##a6##a7##a8##a9##a10##a11##a12  // NOLINT
+#define _PW_CONCAT_13(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13) a1##a2##a3##a4##a5##a6##a7##a8##a9##a10##a11##a12##a13  // NOLINT
+#define _PW_CONCAT_14(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14) a1##a2##a3##a4##a5##a6##a7##a8##a9##a10##a11##a12##a13##a14  // NOLINT
+#define _PW_CONCAT_15(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15) a1##a2##a3##a4##a5##a6##a7##a8##a9##a10##a11##a12##a13##a14##a15  // NOLINT
+#define _PW_CONCAT_16(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16) a1##a2##a3##a4##a5##a6##a7##a8##a9##a10##a11##a12##a13##a14##a15##a16  // NOLINT
+#define _PW_CONCAT_17(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17) a1##a2##a3##a4##a5##a6##a7##a8##a9##a10##a11##a12##a13##a14##a15##a16##a17  // NOLINT
+#define _PW_CONCAT_18(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18) a1##a2##a3##a4##a5##a6##a7##a8##a9##a10##a11##a12##a13##a14##a15##a16##a17##a18  // NOLINT
+#define _PW_CONCAT_19(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19) a1##a2##a3##a4##a5##a6##a7##a8##a9##a10##a11##a12##a13##a14##a15##a16##a17##a18##a19  // NOLINT
+#define _PW_CONCAT_20(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) a1##a2##a3##a4##a5##a6##a7##a8##a9##a10##a11##a12##a13##a14##a15##a16##a17##a18##a19##a20  // NOLINT
+#define _PW_CONCAT_21(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21) a1##a2##a3##a4##a5##a6##a7##a8##a9##a10##a11##a12##a13##a14##a15##a16##a17##a18##a19##a20##a21  // NOLINT
+#define _PW_CONCAT_22(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22) a1##a2##a3##a4##a5##a6##a7##a8##a9##a10##a11##a12##a13##a14##a15##a16##a17##a18##a19##a20##a21##a22  // NOLINT
+#define _PW_CONCAT_23(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23) a1##a2##a3##a4##a5##a6##a7##a8##a9##a10##a11##a12##a13##a14##a15##a16##a17##a18##a19##a20##a21##a22##a23  // NOLINT
+#define _PW_CONCAT_24(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24) a1##a2##a3##a4##a5##a6##a7##a8##a9##a10##a11##a12##a13##a14##a15##a16##a17##a18##a19##a20##a21##a22##a23##a24  // NOLINT
+#define _PW_CONCAT_25(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25) a1##a2##a3##a4##a5##a6##a7##a8##a9##a10##a11##a12##a13##a14##a15##a16##a17##a18##a19##a20##a21##a22##a23##a24##a25  // NOLINT
+#define _PW_CONCAT_26(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26) a1##a2##a3##a4##a5##a6##a7##a8##a9##a10##a11##a12##a13##a14##a15##a16##a17##a18##a19##a20##a21##a22##a23##a24##a25##a26  // NOLINT
+#define _PW_CONCAT_27(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27) a1##a2##a3##a4##a5##a6##a7##a8##a9##a10##a11##a12##a13##a14##a15##a16##a17##a18##a19##a20##a21##a22##a23##a24##a25##a26##a27  // NOLINT
+#define _PW_CONCAT_28(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28) a1##a2##a3##a4##a5##a6##a7##a8##a9##a10##a11##a12##a13##a14##a15##a16##a17##a18##a19##a20##a21##a22##a23##a24##a25##a26##a27##a28  // NOLINT
+#define _PW_CONCAT_29(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29) a1##a2##a3##a4##a5##a6##a7##a8##a9##a10##a11##a12##a13##a14##a15##a16##a17##a18##a19##a20##a21##a22##a23##a24##a25##a26##a27##a28##a29  // NOLINT
+#define _PW_CONCAT_30(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30) a1##a2##a3##a4##a5##a6##a7##a8##a9##a10##a11##a12##a13##a14##a15##a16##a17##a18##a19##a20##a21##a22##a23##a24##a25##a26##a27##a28##a29##a30  // NOLINT
+#define _PW_CONCAT_31(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31) a1##a2##a3##a4##a5##a6##a7##a8##a9##a10##a11##a12##a13##a14##a15##a16##a17##a18##a19##a20##a21##a22##a23##a24##a25##a26##a27##a28##a29##a30##a31  // NOLINT
+#define _PW_CONCAT_32(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32) a1##a2##a3##a4##a5##a6##a7##a8##a9##a10##a11##a12##a13##a14##a15##a16##a17##a18##a19##a20##a21##a22##a23##a24##a25##a26##a27##a28##a29##a30##a31##a32  // NOLINT
diff --git a/pw_preprocessor/public/pw_preprocessor/macro_arg_count.h b/pw_preprocessor/public/pw_preprocessor/macro_arg_count.h
new file mode 100644
index 0000000..e55ff0c
--- /dev/null
+++ b/pw_preprocessor/public/pw_preprocessor/macro_arg_count.h
@@ -0,0 +1,124 @@
+// Copyright 2019 The Pigweed Authors
+//
+// Licensed 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
+//
+//     https://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.
+//
+// Macros for counting the number of arguments passed to a variadic
+// function-like macro.
+#pragma once
+
+#include "pw_preprocessor/boolean.h"
+
+// PW_ARG_COUNT counts the number of arguments it was called with. It evalulates
+// to an integer literal in the range 0 to 64. Counting more than 64 arguments
+// is not currently supported.
+//
+// PW_ARG_COUNT is most commonly used to count __VA_ARGS__ in a variadic macro.
+// For example, the following code counts the number of arguments passed to a
+// logging macro:
+//
+/*   #define LOG_INFO(format, ...) {                             \
+         static const int kArgCount = PW_ARG_COUNT(__VA_ARGS__); \
+         SendLog(kArgCount, format, ##__VA_ARGS__);              \
+       }
+*/
+// clang-format off
+#define PW_ARG_COUNT(...)                            \
+  _PW_ARG_COUNT_IMPL(__VA_ARGS__,                    \
+                     64, 63, 62, 61, 60, 59, 58, 57, \
+                     56, 55, 54, 53, 52, 51, 50, 49, \
+                     48, 47, 46, 45, 44, 43, 42, 41, \
+                     40, 39, 38, 37, 36, 35, 34, 33, \
+                     32, 31, 30, 29, 28, 27, 26, 25, \
+                     24, 23, 22, 21, 20, 19, 18, 17, \
+                     16, 15, 14, 13, 12, 11, 10,  9, \
+                      8,  7,  6,  5, 4,  3,  2,  PW_HAS_ARGS(__VA_ARGS__))
+
+// Expands to 1 if one or more arguments are provided, 0 otherwise.
+#define PW_HAS_ARGS(...) PW_NOT(PW_HAS_NO_ARGS(__VA_ARGS__))
+
+// Expands to 0 if one or more arguments are provided, 1 otherwise. This
+// approach is from Jens Gustedt's blog:
+//   https://gustedt.wordpress.com/2010/06/08/detect-empty-macro-arguments/
+//
+// Normally, with a standard-compliant C preprocessor, it's impossible to tell
+// whether a variadic macro was called with no arguments or with one argument.
+// A macro invoked with no arguments is actually passed one empty argument.
+//
+// This macro works by checking for the presense of a comma in four situations.
+// These situations give the following information about __VA_ARGS__:
+//
+//   1. It is two or more variadic arguments.
+//   2. It expands to one argument surrounded by parentheses.
+//   3. It is a function-like macro that produces a comma when invoked.
+//   4. It does not interfere with calling a macro when placed between it and
+//      parentheses.
+//
+// If a comma is not present in 1, 2, 3, but is present in 4, then __VA_ARGS__
+// is empty. For this case (0001), and only this case, a corresponding macro
+// that expands to a comma is defined. The presence of this comma determines
+// whether any arguments were passed in.
+//
+// C++20 introduces __VA_OPT__, which would greatly simplify this macro.
+#define PW_HAS_NO_ARGS(...)                                            \
+  _PW_HAS_NO_ARGS(_PW_HAS_COMMA(__VA_ARGS__),                          \
+                  _PW_HAS_COMMA(_PW_MAKE_COMMA_IF_CALLED __VA_ARGS__), \
+                  _PW_HAS_COMMA(__VA_ARGS__()),                        \
+                  _PW_HAS_COMMA(_PW_MAKE_COMMA_IF_CALLED __VA_ARGS__()))
+
+#define _PW_HAS_COMMA(...)                                           \
+  _PW_ARG_COUNT_IMPL(__VA_ARGS__,                                    \
+                     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \
+                     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \
+                     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \
+                     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0)
+
+#define _PW_ARG_COUNT_IMPL(a64, a63, a62, a61, a60, a59, a58, a57, \
+                           a56, a55, a54, a53, a52, a51, a50, a49, \
+                           a48, a47, a46, a45, a44, a43, a42, a41, \
+                           a40, a39, a38, a37, a36, a35, a34, a33, \
+                           a32, a31, a30, a29, a28, a27, a26, a25, \
+                           a24, a23, a22, a21, a20, a19, a18, a17, \
+                           a16, a15, a14, a13, a12, a11, a10, a09, \
+                           a08, a07, a06, a05, a04, a03, a02, a01, \
+                           count, ...)                             \
+  count
+
+// clang-format on
+#define _PW_HAS_NO_ARGS(a1, a2, a3, a4) \
+  _PW_HAS_COMMA(_PW_PASTE_RESULTS(a1, a2, a3, a4))
+#define _PW_PASTE_RESULTS(a1, a2, a3, a4) _PW_HAS_COMMA_CASE_##a1##a2##a3##a4
+#define _PW_HAS_COMMA_CASE_0001 ,
+#define _PW_MAKE_COMMA_IF_CALLED(...) ,
+
+// Expands to a comma followed by __VA_ARGS__, if __VA_ARGS__ is non-empty.
+// Otherwise, expands to nothing. This is useful when calling a function with
+// __VA_ARGS__, since it removes the extra comma when no arguments are
+// provided.
+//
+// This is a more flexible, standard-compliant version of ##__VA_ARGS__. Unlike
+// ##__VA_ARGS__, this can be used to eliminate an unwanted comma when
+// __VA_ARGS__ expands to an empty argument because an outer macro was called
+// with __VA_ARGS__ instead of ##__VA_ARGS__.
+//
+// This can be used to call variadic functions or provide variadic template
+// parameters from a macro. For example:
+//
+//  #define MY_PRINTF(fmt, ...) MY_PRINTF_IMPL(fmt, __VA_ARGS__)
+//  #define MY_PRINTF_IMPL(fmt, ...) printf(fmt PW_COMMA_ARGS(__VA_ARGS__))
+//
+#define PW_COMMA_ARGS(...) _PW_COMMA_ARGS(PW_HAS_ARGS(__VA_ARGS__), __VA_ARGS__)
+
+#define _PW_COMMA_ARGS(has_args, ...) _PW_COMMA_ARGS_X(has_args, __VA_ARGS__)
+#define _PW_COMMA_ARGS_X(has_args, ...) _PW_COMMA_ARGS_##has_args(__VA_ARGS__)
+#define _PW_COMMA_ARGS_0(...)                // no args, no comma
+#define _PW_COMMA_ARGS_1(...) , __VA_ARGS__  // comma, followed by args
diff --git a/pw_preprocessor/public/pw_preprocessor/util.h b/pw_preprocessor/public/pw_preprocessor/util.h
new file mode 100644
index 0000000..11cd158
--- /dev/null
+++ b/pw_preprocessor/public/pw_preprocessor/util.h
@@ -0,0 +1,57 @@
+// Copyright 2019 The Pigweed Authors
+//
+// Licensed 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
+//
+//     https://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.
+//
+// Small, general preprocessor macros for C and C++ code.
+#pragma once
+
+// Returns the number of elements in a C array.
+#define PW_ARRAY_SIZE(array) (sizeof(array) / sizeof(*array))
+
+// Prevents unused value compiler warnings.
+#define PW_UNUSED(value) ((void)sizeof(value))
+
+// Returns a string literal of the arguments after expanding macros.
+#define PW_STRINGIFY(...) _PW_STRINGIFY(__VA_ARGS__)
+#define _PW_STRINGIFY(...) #__VA_ARGS__
+
+#ifdef __cplusplus
+
+// Macro for inline extern "C" declarations. The following will compile
+// correctly for C and C++:
+//
+//   PW_EXTERN_C ThisFunctionHasCLinkage(void);
+//
+#define PW_EXTERN_C extern "C"
+
+// Macros for opening and closing an extern "C" block. This avoids the need for
+// an #ifdef __cplusplus check around the extern "C" { and closing }. Example:
+//
+//   PW_EXTERN_C_START
+//
+//   void FunctionDeclarationForCppAndC(void);
+//
+//   void AnotherFunctionDeclarationForCppAndC(int, char);
+//
+//   PW_EXTERN_C_END
+//
+#define PW_EXTERN_C_START extern "C" {
+#define PW_EXTERN_C_END }  // extern "C"
+
+#else  // extern "C" is removed from C code
+
+#define PW_EXTERN_C
+#define PW_EXTERN_C_START
+#define PW_EXTERN_C_END
+
+#endif  // __cplusplus
diff --git a/pw_preprocessor/util_test.cc b/pw_preprocessor/util_test.cc
new file mode 100644
index 0000000..0ce027f
--- /dev/null
+++ b/pw_preprocessor/util_test.cc
@@ -0,0 +1,66 @@
+// Copyright 2019 The Pigweed Authors
+//
+// Licensed 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
+//
+//     https://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 "pw_preprocessor/util.h"
+
+#include <cstdint>
+
+#include "pw_unit_test/framework.h"
+
+namespace pw {
+namespace {
+
+TEST(Macros, ArraySize) {
+  uint32_t hello_there[123];
+  static_assert(PW_ARRAY_SIZE(hello_there) == 123);
+
+  char characters[500];
+  static_assert(PW_ARRAY_SIZE(characters) == 500);
+  static_assert(PW_ARRAY_SIZE("2345") == 5);
+
+  struct Object {
+    int a;
+    uint64_t array[2048];
+  };
+  Object objects[404];
+
+  static_assert(PW_ARRAY_SIZE(objects) == 404);
+  static_assert(PW_ARRAY_SIZE(Object::array) == 2048);
+  static_assert(PW_ARRAY_SIZE(objects[1].array) == 2048);
+}
+
+TEST(Macros, UnusedVariable) {
+  int this_is_not_used = 12;
+  PW_UNUSED(this_is_not_used);
+
+  volatile void* volatile wow;
+  wow = nullptr;
+  PW_UNUSED(wow);
+}
+
+#define HELLO hello
+#define WORLD WORLD_IMPL()
+#define WORLD_IMPL() WORLD !
+
+TEST(Macros, Stringify) {
+  EXPECT_STREQ("", PW_STRINGIFY());
+  EXPECT_STREQ("> _ <", PW_STRINGIFY(> _ <));
+  EXPECT_STREQ("hello WORLD !", PW_STRINGIFY(HELLO WORLD));
+  EXPECT_STREQ("hello, WORLD !", PW_STRINGIFY(HELLO, WORLD));
+  EXPECT_STREQ("a, b, c, hello, WORLD ! 2",
+               PW_STRINGIFY(a, b, c, HELLO, WORLD 2));
+}
+
+}  // namespace
+}  // namespace pw
diff --git a/pw_status/README.md b/pw_status/README.md
index 4261b75..2f3c97a 100644
--- a/pw_status/README.md
+++ b/pw_status/README.md
@@ -1 +1 @@
-# pw\_status: Pigweed error codes.
+# pw\_status: Pigweed error codes
diff --git a/pw_toolchain/BUILD.gn b/pw_toolchain/BUILD.gn
index c0743ce..3b63d01 100644
--- a/pw_toolchain/BUILD.gn
+++ b/pw_toolchain/BUILD.gn
@@ -13,6 +13,7 @@
 # the License.
 
 import("arm_gcc.gni")
+import("x86_linux_gcc.gni")
 
 # Creates a series of toolchain targets with common compiler options.
 #
@@ -120,3 +121,14 @@
     },
   ]
 }
+
+generate_toolchains("linux") {
+  toolchain_template = "x86_gcc_toolchain"
+
+  toolchains = [
+    {
+      toolchain_name = "x86_linux_o2"
+      additional_cflags = [ "-O2" ]
+    },
+  ]
+}
diff --git a/pw_toolchain/README.md b/pw_toolchain/README.md
index e73f45d..8017671 100644
--- a/pw_toolchain/README.md
+++ b/pw_toolchain/README.md
@@ -1 +1 @@
-# pw\_toolchain: Pigweed's standard build toolchains.
+# pw\_toolchain: Pigweed's standard build toolchains
diff --git a/pw_toolchain/x86_linux_gcc.gni b/pw_toolchain/x86_linux_gcc.gni
new file mode 100644
index 0000000..fcb4926
--- /dev/null
+++ b/pw_toolchain/x86_linux_gcc.gni
@@ -0,0 +1,116 @@
+# Copyright 2019 The Pigweed Authors
+#
+# Licensed 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
+#
+#     https://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.
+
+# Generates a host Linux gcc toolchain for a specific target.
+#
+# Args:
+#   toolchain_cflags: Additional C/C++ compiler flags for the target.
+#   toolchain_ldflags: Additional linker flags for the target.
+template("x86_gcc_toolchain") {
+  _toolchain_cflags = ""
+  if (defined(invoker.toolchain_cflags)) {
+    foreach(flag, invoker.toolchain_cflags) {
+      _toolchain_cflags += " " + flag
+    }
+  }
+
+  _toolchain_ldflags = ""
+  if (defined(invoker.toolchain_ldflags)) {
+    foreach(flag, invoker.toolchain_ldflags) {
+      _toolchain_ldflags += " " + flag
+    }
+  }
+
+  _ar = "ar"
+  _cc = "gcc"
+  _cxx = "g++"
+
+  toolchain(target_name) {
+    tool("asm") {
+      depfile = "{{output}}.d"
+      command = "$_cc -MMD -MF $depfile $_toolchain_cflags {{defines}} {{include_dirs}} {{asmflags}} -c {{source}} -o {{output}}"
+      depsformat = "gcc"
+      description = "as {{output}}"
+      outputs = [
+        "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o",
+      ]
+    }
+
+    tool("cc") {
+      depfile = "{{output}}.d"
+      command = "$_cc -MMD -MF $depfile $_toolchain_cflags {{defines}} {{include_dirs}} {{cflags}} {{cflags_c}} -c {{source}} -o {{output}}"
+      depsformat = "gcc"
+      description = "cc {{output}}"
+      outputs = [
+        "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o",
+      ]
+    }
+
+    tool("cxx") {
+      depfile = "{{output}}.d"
+      command = "$_cxx -MMD -MF $depfile $_toolchain_cflags {{defines}} {{include_dirs}} {{cflags}} {{cflags_cc}} -c {{source}} -o {{output}}"
+      depsformat = "gcc"
+      description = "c++ {{output}}"
+      outputs = [
+        "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o",
+      ]
+    }
+
+    tool("alink") {
+      command = "rm -f {{output}} && $_ar rcs {{output}} {{inputs}}"
+      description = "ar {{target_output_name}}{{output_extension}}"
+      outputs = [
+        "{{target_out_dir}}/{{target_output_name}}{{output_extension}}",
+      ]
+      default_output_extension = ".a"
+    }
+
+    lib_switch = "-l"
+    lib_dir_switch = "-L"
+
+    _link_outfile = "{{output_dir}}/{{target_output_name}}{{output_extension}}"
+    _link_mapfile = "{{output_dir}}/{{target_output_name}}.map"
+    _link_command = "$_cxx {{ldflags}} -Wl,--gc-sections $_toolchain_cflags $_toolchain_ldflags -Wl,--start-group {{inputs}} -Wl,--end-group {{libs}} -Wl,-Map=$_link_mapfile -o $_link_outfile"
+
+    tool("link") {
+      command = _link_command
+      description = "ld $_link_outfile"
+      outputs = [
+        _link_outfile,
+      ]
+      default_output_dir = "{{target_out_dir}}"
+      default_output_extension = ""
+    }
+
+    tool("solink") {
+      command = _link_command + " -shared"
+      description = "ld -shared $_link_outfile"
+      outputs = [
+        _link_outfile,
+      ]
+      default_output_dir = "{{target_out_dir}}"
+      default_output_extension = ".so"
+    }
+
+    tool("stamp") {
+      command = "touch {{output}}"
+      description = "stamp {{output}}"
+    }
+
+    tool("copy") {
+      command = "cp -af {{source}} {{output}}"
+      description = "cp {{source}} {{output}}"
+    }
+  }
+}
diff --git a/pw_unit_test/BUILD.gn b/pw_unit_test/BUILD.gn
new file mode 100644
index 0000000..864de75
--- /dev/null
+++ b/pw_unit_test/BUILD.gn
@@ -0,0 +1,81 @@
+# Copyright 2019 The Pigweed Authors
+#
+# Licensed 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
+#
+#     https://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.
+
+import("$dir_pw_build/pw_executable.gni")
+
+config("default_config") {
+  include_dirs = [ "public" ]
+}
+
+# pw_unit_test core library.
+source_set("pw_unit_test") {
+  public_configs = [
+    "$dir_pw_build:pw_default_cpp",
+    ":default_config",
+  ]
+  public_deps = [
+    "$dir_pw_preprocessor",
+  ]
+  public = [
+    "public/gtest/gtest.h",
+    "public/pw_unit_test/event_handler.h",
+    "public/pw_unit_test/framework.h",
+  ]
+  sources = [ "framework.cc" ] + public
+}
+
+# Library providing an event handler which outputs human-readable text.
+source_set("simple_printing_event_handler") {
+  public_deps = [
+    ":pw_unit_test",
+    "$dir_pw_preprocessor",
+  ]
+  public = [
+    "public/pw_unit_test/simple_printing_event_handler.h",
+  ]
+  sources = [ "simple_printing_event_handler.cc" ] + public
+}
+
+# Library providing a standard desktop main function for the pw_unit_test
+# framework. Unit test files can link against this library to build runnable
+# unit test executables.
+source_set("main") {
+  public_deps = [
+    ":pw_unit_test",
+  ]
+  deps = [
+    ":simple_printing_event_handler",
+  ]
+  sources = [
+    "main.cc",
+  ]
+}
+
+# TODO(frolv): These targets are temporarily here until unit test support is
+# integrated into the build system.
+pw_executable("framework_test") {
+  deps = [
+    ":main",
+    ":pw_unit_test",
+  ]
+  sources = [
+    "framework_test.cc",
+  ]
+}
+
+group("framework_test_linux") {
+  deps = [
+    ":framework_test($dir_pw_toolchain:x86_linux_o2)",
+  ]
+}
diff --git a/pw_unit_test/README.md b/pw_unit_test/README.md
new file mode 100644
index 0000000..713fad1
--- /dev/null
+++ b/pw_unit_test/README.md
@@ -0,0 +1,6 @@
+# pw\_unit\_test: Lightweight C++ unit testing framework
+
+The pw\_unit\_test module contains the code for *Pigweed Test*, a
+[Googletest](https://github.com/google/googletest/blob/master/googletest/docs/primer.md)-compatible
+unit testing framework that runs on anything from bare-metal microcontrollers
+to large desktop operating systems.
diff --git a/pw_unit_test/framework.cc b/pw_unit_test/framework.cc
new file mode 100644
index 0000000..24fbb7c
--- /dev/null
+++ b/pw_unit_test/framework.cc
@@ -0,0 +1,112 @@
+// Copyright 2019 The Pigweed Authors
+//
+// Licensed 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
+//
+//     https://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 "pw_unit_test/framework.h"
+
+namespace pw::unit_test {
+
+void RegisterEventHandler(EventHandler* event_handler) {
+  internal::Framework::Get().RegisterEventHandler(event_handler);
+}
+
+namespace internal {
+
+// Singleton instance of the unit test framework class.
+Framework Framework::framework_;
+
+// Linked list of all test cases in the test executable. This is static as it is
+// populated using static initialization.
+TestInfo* Framework::tests_ = nullptr;
+
+void Framework::RegisterTest(TestInfo* test) {
+  // Append the test case to the end of the test list.
+  TestInfo** pos = &tests_;
+  for (; *pos != nullptr; pos = &(*pos)->next) {
+  }
+  *pos = test;
+}
+
+int Framework::RunAllTests() {
+  for (TestInfo* test = tests_; test != nullptr; test = test->next) {
+    test->run();
+  }
+  return exit_status_;
+}
+
+void Framework::StartTest(Test* test) {
+  current_test_ = test;
+  current_result_ = TestResult::kSuccess;
+
+  if (event_handler_ == nullptr) {
+    return;
+  }
+
+  const TestInfo* info = test->pigweed_test_info_;
+  TestCase test_case = {
+      .suite_name = info->test_suite_name,
+      .test_name = info->test_name,
+      .file_name = info->file_name,
+  };
+
+  event_handler_->TestCaseStart(test_case);
+}
+
+void Framework::EndTest(Test* test) {
+  current_test_ = nullptr;
+
+  if (event_handler_ == nullptr) {
+    return;
+  }
+
+  const TestInfo* info = test->pigweed_test_info_;
+  TestCase test_case = {
+      .suite_name = info->test_suite_name,
+      .test_name = info->test_name,
+      .file_name = info->file_name,
+  };
+
+  event_handler_->TestCaseEnd(test_case, current_result_);
+}
+
+void Framework::ExpectationResult(const char* expression,
+                                  int line,
+                                  bool success) {
+  if (!success) {
+    current_result_ = TestResult::kFailure;
+    exit_status_ = 1;
+  }
+
+  if (event_handler_ == nullptr) {
+    return;
+  }
+
+  const TestInfo* info = current_test_->pigweed_test_info_;
+  TestCase test_case = {
+      .suite_name = info->test_suite_name,
+      .test_name = info->test_name,
+      .file_name = info->file_name,
+  };
+
+  TestExpectation expectation = {
+      .expression = expression,
+      .line_number = line,
+      .success = success,
+  };
+
+  event_handler_->TestCaseExpect(test_case, expectation);
+}
+
+}  // namespace internal
+
+}  // namespace pw::unit_test
diff --git a/pw_unit_test/framework_test.cc b/pw_unit_test/framework_test.cc
new file mode 100644
index 0000000..300a7cd
--- /dev/null
+++ b/pw_unit_test/framework_test.cc
@@ -0,0 +1,155 @@
+// Copyright 2019 The Pigweed Authors
+//
+// Licensed 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
+//
+//     https://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 "pw_unit_test/framework.h"
+
+#include <cstring>
+
+namespace pw {
+namespace {
+
+TEST(PigweedTest, ExpectBool) {
+  EXPECT_TRUE(true);
+  EXPECT_FALSE(false);
+
+  EXPECT_TRUE(1);
+  EXPECT_TRUE(1203492);
+  EXPECT_TRUE(-1);
+  EXPECT_TRUE(0.1f);
+
+  EXPECT_FALSE(0);
+  EXPECT_FALSE(0.0f);
+  EXPECT_FALSE(-0.0f);
+}
+
+TEST(PigweedTest, ExpectBasicComparisons) {
+  EXPECT_EQ(1, 1 + 0);
+  ASSERT_EQ(1, 1 + 0);
+
+  EXPECT_EQ(0.0f, -0.0f);
+  ASSERT_EQ(0.0f, -0.0f);
+
+  EXPECT_NE(-1, 0);
+  ASSERT_NE(-1, 0);
+
+  EXPECT_GT(2, 1);
+  ASSERT_GT(3, 0);
+
+  EXPECT_GE(1, 1);
+  ASSERT_GE(3, 0);
+
+  EXPECT_LT(0, 1);
+  ASSERT_LT(-2, 1209);
+
+  EXPECT_LE(-1, 0);
+  ASSERT_LE(-2, -2);
+}
+
+TEST(PigweedTest, ExpectStringEquality) {
+  EXPECT_STREQ("", "");
+  EXPECT_STREQ("Yes", "Yes");
+
+  char no[] = {'N', 'o', '\0'};
+  ASSERT_STREQ("No", no);
+
+  EXPECT_STRNE("NO", "no");
+  ASSERT_STRNE("yes", no);
+}
+
+class NonCopyable {
+ public:
+  NonCopyable(int value) : value_(value) {}
+
+  NonCopyable(const NonCopyable&) = delete;
+  NonCopyable& operator=(const NonCopyable&) = delete;
+
+  bool operator==(const NonCopyable& rhs) const { return value_ == rhs.value_; }
+  bool operator!=(const NonCopyable& rhs) const { return value_ != rhs.value_; }
+
+  operator bool() const { return value_ > 0; }
+
+ private:
+  const int value_;
+};
+
+TEST(PigweedTest, NonCopyableType) {
+  EXPECT_TRUE(NonCopyable(6));
+  EXPECT_FALSE(NonCopyable(-1));
+
+  const NonCopyable this_one(100);
+  EXPECT_EQ(this_one, this_one);
+  EXPECT_TRUE(this_one);
+
+  EXPECT_EQ(NonCopyable(5), NonCopyable(5));
+  EXPECT_NE(NonCopyable(5), NonCopyable(6));
+}
+
+bool Increment(int* i) {
+  (*i)++;
+  return true;
+}
+
+TEST(PigweedTest, MacroArgumentsOnlyAreEvaluatedOnce) {
+  int i = 1;
+
+  EXPECT_TRUE(Increment(&i));
+  EXPECT_EQ(i, 2);
+  ASSERT_TRUE(Increment(&i));
+  EXPECT_EQ(i, 3);
+
+  EXPECT_EQ(0x600dbeef, [&i]() {
+    i += 1;
+    return 0x600dbeef;
+  }());
+
+  EXPECT_EQ(i, 4);
+}
+
+class FixtureTest : public ::testing::Test {
+ public:
+  FixtureTest() : string_("hello world") {}
+
+  bool ReturnTrue() { return true; }
+  int StringLength() { return std::strlen(string_); }
+
+ protected:
+  const char* string_;
+};
+
+TEST_F(FixtureTest, CustomFixture) {
+  EXPECT_TRUE(ReturnTrue());
+  EXPECT_EQ(StringLength(), 11);
+}
+
+class PigweedTestFixture : public ::testing::Test {
+ protected:
+  PigweedTestFixture() : cool_number_(35) {}
+
+  int cool_number_;
+};
+
+TEST_F(PigweedTestFixture, TheNumberIs35) {
+  EXPECT_EQ(cool_number_, 35);
+  cool_number_ += 1;
+  EXPECT_EQ(cool_number_, 36);
+}
+
+TEST_F(PigweedTestFixture, YupTheNumberIs35) {
+  EXPECT_EQ(cool_number_, 35);
+  cool_number_ *= 100;
+  EXPECT_EQ(cool_number_, 3500);
+}
+
+}  // namespace
+}  // namespace pw
diff --git a/pw_unit_test/main.cc b/pw_unit_test/main.cc
new file mode 100644
index 0000000..e4465bb
--- /dev/null
+++ b/pw_unit_test/main.cc
@@ -0,0 +1,27 @@
+// Copyright 2019 The Pigweed Authors
+//
+// Licensed 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
+//
+//     https://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 <cstdio>
+
+#include "pw_unit_test/framework.h"
+#include "pw_unit_test/simple_printing_event_handler.h"
+
+// Default main function for the pw_unit_test library. Prints all test results
+// to stdout using a SimplePrintingEventHandler.
+int main() {
+  pw::unit_test::SimplePrintingEventHandler handler(
+      [](const char* s) { return std::printf("%s", s); });
+  pw::unit_test::RegisterEventHandler(&handler);
+  return RUN_ALL_TESTS();
+}
diff --git a/pw_unit_test/public/gtest/gtest.h b/pw_unit_test/public/gtest/gtest.h
new file mode 100644
index 0000000..46e0f65
--- /dev/null
+++ b/pw_unit_test/public/gtest/gtest.h
@@ -0,0 +1,20 @@
+// Copyright 2019 The Pigweed Authors
+//
+// Licensed 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
+//
+//     https://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.
+//
+// pw_unit_test exposes a gTest-compatible interface. This file is provided for
+// existing projects which #include "gtest/gtest.h" to allow them to compile
+// using pw_unit_test as a backend.
+#pragma once
+
+#include "pw_unit_test/framework.h"
diff --git a/pw_unit_test/public/pw_unit_test/event_handler.h b/pw_unit_test/public/pw_unit_test/event_handler.h
new file mode 100644
index 0000000..8a8338b
--- /dev/null
+++ b/pw_unit_test/public/pw_unit_test/event_handler.h
@@ -0,0 +1,100 @@
+// Copyright 2019 The Pigweed Authors
+//
+// Licensed 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
+//
+//     https://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.
+
+#pragma once
+
+namespace pw::unit_test {
+
+// This file defines the interface through which the pw_unit_test framework
+// sends its test data as it runs unit tests. A program wanting to process test
+// events must define a class implementing the EventHandler interface and
+// register it with the framework. When RUN_ALL_TESTS() is called, pw_unit_test
+// will notify the handler of various events which occur in the test process.
+//
+// For example, consider a file containing the following test definitions:
+//
+//   TEST(MyTestSuite, MyFirstCase) {
+//     EXPECT_TRUE(true);
+//   }
+//
+//   TEST(MyTestSuite, MySecondCase) {
+//     EXPECT_TRUE(false);
+//   }
+//
+// In this file, there is one test suite consisting of two test cases.
+//
+// When pw_unit_test starts running the first test case, it dispatches a
+// TestCaseStart event to the event handler. It then runs the body of the test,
+// sequentially checking each assertion within. After each assertion, a
+// TestCaseExpect event is sent to the event handler with the assertion's
+// result. In this case, there is only one, which passes successfully (as
+// `true`, is in fact, true). Finally, after the test is finished, a TestCaseEnd
+// event is dispatched with the overall result of the test case.
+//
+// pw_unit_test then runs MySecondCase, still within the same test suite. The
+// sequence of events dispatched is the same, except that this TestCaseExpect
+// event is marked as a failure. The result passed alongside the TestCaseEnd
+// event also indicates that the test case did not complete successfully.
+
+// The result of a complete test run.
+enum class TestResult {
+  kSuccess = 0,
+  kFailure = 1,
+};
+
+struct TestCase {
+  // Name of the test suite to which this test case belongs.
+  const char* suite_name;
+
+  // Name of the test case.
+  const char* test_name;
+
+  // Path to the file in which the test case is defined.
+  const char* file_name;
+};
+
+struct TestExpectation {
+  // The expression which was run.
+  const char* expression;
+
+  // Line number at which the expectation is located.
+  int line_number;
+
+  // Whether the expectation succeeded.
+  bool success;
+};
+
+// An event handler is responsible for collecting and processing the results of
+// a unit test run. Its interface is called by the unit test framework as tests
+// are executed and various test events occur.
+class EventHandler {
+ public:
+  // Called when a new test case is started.
+  virtual void TestCaseStart(const TestCase& test_case) = 0;
+
+  // Called when a test case completes. The overall result of the test case is
+  // provided.
+  virtual void TestCaseEnd(const TestCase& test_case, TestResult result) = 0;
+
+  // Called after each expect/assert statement within a test case with the
+  // result of the expectation.
+  virtual void TestCaseExpect(const TestCase& test_case,
+                              const TestExpectation& expectation) = 0;
+};
+
+// Sets the event handler for a test run. Must be called before RUN_ALL_TESTS()
+// to receive test output.
+void RegisterEventHandler(EventHandler* event_handler);
+
+}  // namespace pw::unit_test
diff --git a/pw_unit_test/public/pw_unit_test/framework.h b/pw_unit_test/public/pw_unit_test/framework.h
new file mode 100644
index 0000000..111b892
--- /dev/null
+++ b/pw_unit_test/public/pw_unit_test/framework.h
@@ -0,0 +1,345 @@
+// Copyright 2019 The Pigweed Authors
+//
+// Licensed 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
+//
+//     https://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.
+
+#pragma once
+
+#include <cstddef>
+#include <cstdint>
+#include <cstring>
+#include <new>
+
+#include "pw_preprocessor/concat.h"
+#include "pw_preprocessor/util.h"
+#include "pw_unit_test/event_handler.h"
+
+#define PW_TEST(test_suite_name, test_name) \
+  _PW_TEST(test_suite_name, test_name, ::pw::unit_test::Test)
+
+// TEST() is a pretty generic macro name which could conflict with other code.
+// If PW_TEST_DONT_DEFINE_TEST is set, don't alias PW_TEST to TEST.
+// GTEST_DONT_DEFINE_TEST is also accepted for compatibility.
+#if !PW_TEST_DONT_DEFINE_TEST && !GTEST_DONT_DEFINE_TEST
+#define TEST PW_TEST
+#endif
+
+#define TEST_F(test_fixture, test_name) \
+  _PW_TEST(test_fixture, test_name, test_fixture)
+
+#define EXPECT_EQ(lhs, rhs) _PW_TEST_OP(_PW_TEST_EXPECT, lhs, rhs, ==)
+#define EXPECT_NE(lhs, rhs) _PW_TEST_OP(_PW_TEST_EXPECT, lhs, rhs, !=)
+#define EXPECT_GT(lhs, rhs) _PW_TEST_OP(_PW_TEST_EXPECT, lhs, rhs, >)
+#define EXPECT_GE(lhs, rhs) _PW_TEST_OP(_PW_TEST_EXPECT, lhs, rhs, >=)
+#define EXPECT_LT(lhs, rhs) _PW_TEST_OP(_PW_TEST_EXPECT, lhs, rhs, <)
+#define EXPECT_LE(lhs, rhs) _PW_TEST_OP(_PW_TEST_EXPECT, lhs, rhs, <=)
+#define EXPECT_TRUE(expr) _PW_TEST_TRUE(_PW_TEST_EXPECT, expr)
+#define EXPECT_FALSE(expr) _PW_TEST_FALSE(_PW_TEST_EXPECT, expr)
+#define EXPECT_STREQ(lhs, rhs) _PW_TEST_STREQ(_PW_TEST_EXPECT, lhs, rhs)
+#define EXPECT_STRNE(lhs, rhs) _PW_TEST_STRNE(_PW_TEST_EXPECT, lhs, rhs)
+
+#define ASSERT_EQ(lhs, rhs) _PW_TEST_OP(_PW_TEST_ASSERT, lhs, rhs, ==)
+#define ASSERT_NE(lhs, rhs) _PW_TEST_OP(_PW_TEST_ASSERT, lhs, rhs, !=)
+#define ASSERT_GT(lhs, rhs) _PW_TEST_OP(_PW_TEST_ASSERT, lhs, rhs, >)
+#define ASSERT_GE(lhs, rhs) _PW_TEST_OP(_PW_TEST_ASSERT, lhs, rhs, >=)
+#define ASSERT_LT(lhs, rhs) _PW_TEST_OP(_PW_TEST_ASSERT, lhs, rhs, <)
+#define ASSERT_LE(lhs, rhs) _PW_TEST_OP(_PW_TEST_ASSERT, lhs, rhs, <=)
+#define ASSERT_TRUE(expr) _PW_TEST_TRUE(_PW_TEST_ASSERT, expr)
+#define ASSERT_FALSE(expr) _PW_TEST_FALSE(_PW_TEST_ASSERT, expr)
+#define ASSERT_STREQ(lhs, rhs) _PW_TEST_STREQ(_PW_TEST_ASSERT, lhs, rhs)
+#define ASSERT_STRNE(lhs, rhs) _PW_TEST_STRNE(_PW_TEST_ASSERT, lhs, rhs)
+
+// pw_unit_test framework entry point. Runs every registered test case and
+// dispatches the results through the event handler. Returns a status of zero
+// if all tests passed, or nonzero if there were any failures.
+// This is compatible with Googletest.
+//
+// In order to receive test output, an event handler must be registered before
+// this is called:
+//
+//   int main() {
+//     MyEventHandler handler;
+//     pw::unit_test::RegisterEventHandler(&handler);
+//     return RUN_ALL_TESTS();
+//   }
+//
+#define RUN_ALL_TESTS() \
+  ::pw::unit_test::internal::Framework::Get().RunAllTests()
+
+namespace pw::unit_test {
+
+class Test;
+
+namespace internal {
+
+struct TestInfo;
+
+// Singleton test framework class responsible for managing and running test
+// cases. This implementation is internal to Pigweed test; free functions
+// wrapping its functionality are exposed as the public interface.
+class Framework {
+ public:
+  constexpr Framework()
+      : current_test_(nullptr),
+        current_result_(TestResult::kSuccess),
+        exit_status_(0),
+        event_handler_(nullptr),
+        memory_pool_() {}
+
+  static Framework& Get() { return framework_; }
+
+  // Registers a single test case with the framework. The framework owns the
+  // registered unit test. Called during static initialization.
+  void RegisterTest(TestInfo* test);
+
+  // Sets the handler to which the framework dispatches test events. During a
+  // test run, the framework owns the event handler.
+  void RegisterEventHandler(EventHandler* event_handler) {
+    event_handler_ = event_handler;
+  }
+
+  // Runs all registered test cases, returning a status of 0 if all succeeded or
+  // nonzero if there were any failures. Test events that occur during the run
+  // are sent to the registered event handler, if any.
+  int RunAllTests();
+
+  // Constructs an instance of a unit test class and runs the test.
+  //
+  // Tests are constructed within a static memory pool at run time instead of
+  // being statically allocated to avoid blowing up the size of the test binary
+  // in cases where users have large test fixtures (e.g. containing buffers)
+  // reused many times. Instead, only a small, fixed-size TestInfo struct is
+  // statically allocated per test case, with a run() function that references
+  // this method instantiated for its test class.
+  template <typename TestInstance>
+  static void CreateAndRunTest() {
+    // TODO(frolv): Update the assert message with the name of the config option
+    // for memory pool size once it is configurable.
+    static_assert(
+        sizeof(TestInstance) <= sizeof(memory_pool_),
+        "The test memory pool is too small for this test. Either increase "
+        "kTestMemoryPoolSizeBytes or decrease the size of your test fixture.");
+
+    Framework& framework = Get();
+
+    // Construct the test object within the static memory pool.
+    TestInstance* test_instance = new (&framework.memory_pool_) TestInstance;
+
+    framework.StartTest(test_instance);
+    test_instance->PigweedTestRun();
+    framework.EndTest(test_instance);
+
+    // Manually call the destructor as it is not called automatically for
+    // objects constructed using placement new.
+    test_instance->~TestInstance();
+  }
+
+  // Runs an expectation function for the currently active test case.
+  template <typename Expectation, typename Lhs, typename Rhs>
+  bool CurrentTestExpect(Expectation expectation,
+                         const Lhs& lhs,
+                         const Rhs& rhs,
+                         const char* expression,
+                         int line) {
+    bool result = expectation(lhs, rhs);
+    ExpectationResult(expression, line, result);
+    return result;
+  }
+
+ private:
+  // Dispatches an event indicating that a test started running.
+  void StartTest(Test* test);
+
+  // Dispatches an event indicating that a test finished running.
+  void EndTest(Test* test);
+
+  // Dispatches an event indicating the result of an expectation.
+  void ExpectationResult(const char* expression, int line, bool success);
+
+  // Singleton instance of the framework class.
+  static Framework framework_;
+
+  // Linked list of all registered test cases. This is static as it tests are
+  // registered using static initialization.
+  static TestInfo* tests_;
+
+  // The current test case which is running.
+  Test* current_test_;
+
+  // Overall result of the current test case (pass/fail).
+  TestResult current_result_;
+
+  // Program exit status returned by RunAllTests for Googletest compatibility.
+  int exit_status_;
+
+  // Handler to which to dispatch test events.
+  EventHandler* event_handler_;
+
+  // Memory region in which to construct test case classes as they are run.
+  // TODO(frolv): Make the memory pool size configurable.
+  static constexpr size_t kTestMemoryPoolSizeBytes = 8192;
+  std::aligned_storage_t<kTestMemoryPoolSizeBytes, alignof(std::max_align_t)>
+      memory_pool_;
+};
+
+// Information about a single test case, including a pointer to a function which
+// constructs and runs the test class. These are statically allocated instead of
+// the test classes, as test classes can be very large.
+struct TestInfo {
+  TestInfo(const char* const test_suite_name,
+           const char* const test_name,
+           const char* const file_name,
+           void (&run)())
+      : test_suite_name(test_suite_name),
+        test_name(test_name),
+        file_name(file_name),
+        run(run) {
+    Framework::Get().RegisterTest(this);
+  }
+
+  // Name of the suite to which the test case belongs.
+  const char* const test_suite_name;
+
+  // Name of the test case itself.
+  const char* const test_name;
+
+  // Path to the file in which the test case is located.
+  const char* const file_name;
+
+  // Function which runs the test case. Refers to Framework::CreateAndRunTest
+  // instantiated for the test case's class.
+  void (&run)();
+
+  // TestInfo structs are registered with the test framework and stored as a
+  // linked list.
+  TestInfo* next = nullptr;
+};
+
+}  // namespace internal
+
+// Base class for all test cases or custom test fixtures.
+// Every unit test created using the TEST or TEST_F macro defines a class that
+// inherits from this (or a subclass of this).
+//
+// For example, given the following test definition:
+//
+//   TEST(MyTest, SaysHello) {
+//     ASSERT_STREQ(SayHello(), "Hello, world!");
+//   }
+//
+// A new class is defined for the test, e.g. MyTest_SaysHello_Test. This class
+// inherits from the Test class and implements its PigweedTestBody function with
+// the block provided to the TEST macro.
+class Test {
+ public:
+  // Runs the unit test. Currently, this simply executes the test body, but it
+  // could be expanded to perform more bookkeeping operations.
+  void PigweedTestRun() { PigweedTestBody(); }
+
+ protected:
+  // Called by subclasses' constructors with their TestInfo instances.
+  void PigweedSetTestInfo(const internal::TestInfo* test_info) {
+    pigweed_test_info_ = test_info;
+  }
+
+ private:
+  friend class internal::Framework;
+
+  // Pointer to the TestInfo struct statically allocated for the test case.
+  const internal::TestInfo* pigweed_test_info_;
+
+  // The user-provided body of the test case. Populated by the TEST macro.
+  virtual void PigweedTestBody() = 0;
+};
+
+}  // namespace pw::unit_test
+
+#define _PW_TEST_CLASS_NAME(test_suite_name, test_name) \
+  PW_CONCAT(test_suite_name, _, test_name, _Test)
+
+#define _PW_TEST(test_suite_name, test_name, parent_class)         \
+  static_assert(sizeof(PW_STRINGIFY(test_suite_name)) > 1,         \
+                "test_suite_name must not be empty");              \
+  static_assert(sizeof(PW_STRINGIFY(test_name)) > 1,               \
+                "test_name must not be empty");                    \
+                                                                   \
+  class _PW_TEST_CLASS_NAME(test_suite_name, test_name)            \
+      : public parent_class {                                      \
+   public:                                                         \
+    _PW_TEST_CLASS_NAME(test_suite_name, test_name)() {            \
+      PigweedSetTestInfo(&test_info_);                             \
+    }                                                              \
+                                                                   \
+   private:                                                        \
+    void PigweedTestBody() override;                               \
+    static ::pw::unit_test::internal::TestInfo test_info_;         \
+  };                                                               \
+                                                                   \
+  ::pw::unit_test::internal::TestInfo                              \
+      _PW_TEST_CLASS_NAME(test_suite_name, test_name)::test_info_( \
+          PW_STRINGIFY(test_suite_name),                           \
+          PW_STRINGIFY(test_name),                                 \
+          __FILE__,                                                \
+          &::pw::unit_test::internal::Framework::CreateAndRunTest< \
+              _PW_TEST_CLASS_NAME(test_suite_name, test_name)>);   \
+                                                                   \
+  void _PW_TEST_CLASS_NAME(test_suite_name, test_name)::PigweedTestBody()
+
+#define _PW_TEST_EXPECT(lhs, rhs, expectation, expectation_string) \
+  ::pw::unit_test::internal::Framework::Get().CurrentTestExpect(   \
+      expectation,                                                 \
+      (lhs),                                                       \
+      (rhs),                                                       \
+      #lhs " " expectation_string " " #rhs,                        \
+      __LINE__)
+
+#define _PW_TEST_ASSERT(lhs, rhs, expectation, expectation_string)   \
+  if (!_PW_TEST_EXPECT(lhs, rhs, expectation, expectation_string)) { \
+    return;                                                          \
+  }
+
+#define _PW_TEST_OP(expect_or_assert, lhs, rhs, op) \
+  expect_or_assert(                                 \
+      lhs, rhs, [](const auto& l, const auto& r) { return l op r; }, #op)
+
+#define _PW_TEST_TRUE(expect_or_assert, expr)                              \
+  expect_or_assert(                                                        \
+      expr,                                                                \
+      true,                                                                \
+      [](const auto& arg, const auto&) { return static_cast<bool>(arg); }, \
+      "is")
+
+#define _PW_TEST_FALSE(expect_or_assert, expr)                              \
+  expect_or_assert(                                                         \
+      expr,                                                                 \
+      false,                                                                \
+      [](const auto& arg, const auto&) { return !static_cast<bool>(arg); }, \
+      "is")
+
+#define _PW_TEST_STREQ(expect_or_assert, lhs, rhs)                         \
+  expect_or_assert(                                                        \
+      lhs,                                                                 \
+      rhs,                                                                 \
+      [](const auto& l, const auto& r) { return std::strcmp(l, r) == 0; }, \
+      "equals")
+
+#define _PW_TEST_STRNE(expect_or_assert, lhs, rhs)                         \
+  expect_or_assert(                                                        \
+      lhs,                                                                 \
+      rhs,                                                                 \
+      [](const auto& l, const auto& r) { return std::strcmp(l, r) != 0; }, \
+      "does not equal")
+
+// Alias Test as ::testing::Test for Googletest compatibility.
+namespace testing {
+using Test = ::pw::unit_test::Test;
+}  // namespace testing
diff --git a/pw_unit_test/public/pw_unit_test/simple_printing_event_handler.h b/pw_unit_test/public/pw_unit_test/simple_printing_event_handler.h
new file mode 100644
index 0000000..25c1568
--- /dev/null
+++ b/pw_unit_test/public/pw_unit_test/simple_printing_event_handler.h
@@ -0,0 +1,57 @@
+// Copyright 2019 The Pigweed Authors
+//
+// Licensed 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
+//
+//     https://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.
+
+#pragma once
+
+#include <cstddef>
+
+#include "pw_preprocessor/compiler.h"
+#include "pw_unit_test/event_handler.h"
+
+namespace pw::unit_test {
+
+// An event handler implementation which produces human-readable test output.
+//
+// Example output:
+//
+//   >>> Running MyTestSuite.TestCase1
+//   [SUCCESS] 128 <= 129
+//   [FAILURE] 'a' == 'b'
+//     at ../path/to/my/file_test.cc:4831
+//   <<< Test MyTestSuite.TestCase1 failed
+//
+class SimplePrintingEventHandler : public EventHandler {
+ public:
+  using WriteFunction = int (*)(const char*);
+
+  // Instantiates an event handler with a function to which to output results.
+  // If verbose is set, information for successful tests is written as well as
+  // failures.
+  SimplePrintingEventHandler(WriteFunction write_function, bool verbose = false)
+      : write_(write_function), verbose_(verbose) {}
+
+  void TestCaseStart(const TestCase& test_case) override;
+  void TestCaseEnd(const TestCase& test_case, TestResult result) override;
+  void TestCaseExpect(const TestCase& test_case,
+                      const TestExpectation& expectation) override;
+
+ private:
+  int WriteAndFlush(const char* format, ...) PW_PRINTF_FORMAT(2, 3);
+
+  WriteFunction write_;
+  bool verbose_;
+  char buffer_[512];
+};
+
+}  // namespace pw::unit_test
diff --git a/pw_unit_test/simple_printing_event_handler.cc b/pw_unit_test/simple_printing_event_handler.cc
new file mode 100644
index 0000000..1ae5d29
--- /dev/null
+++ b/pw_unit_test/simple_printing_event_handler.cc
@@ -0,0 +1,55 @@
+// Copyright 2019 The Pigweed Authors
+//
+// Licensed 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
+//
+//     https://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 "pw_unit_test/simple_printing_event_handler.h"
+
+#include <cstdarg>
+#include <cstdio>
+
+namespace pw::unit_test {
+
+void SimplePrintingEventHandler::TestCaseStart(const TestCase& test_case) {
+  WriteAndFlush(
+      ">>> Running %s.%s\n", test_case.suite_name, test_case.test_name);
+}
+
+void SimplePrintingEventHandler::TestCaseEnd(const TestCase& test_case,
+                                             TestResult result) {
+  const char* status = result == TestResult::kSuccess ? "succeeded" : "failed";
+  WriteAndFlush(
+      "<<< Test %s.%s %s\n", test_case.suite_name, test_case.test_name, status);
+}
+
+void SimplePrintingEventHandler::TestCaseExpect(
+    const TestCase& test_case, const TestExpectation& expectation) {
+  if (!verbose_ && expectation.success) {
+    return;
+  }
+
+  const char* result = expectation.success ? "SUCCESS" : "FAILURE";
+  WriteAndFlush("[%s] %s\n", result, expectation.expression);
+  WriteAndFlush("  at %s:%d\n", test_case.file_name, expectation.line_number);
+}
+
+int SimplePrintingEventHandler::WriteAndFlush(const char* format, ...) {
+  va_list args;
+
+  va_start(args, format);
+  std::vsnprintf(buffer_, sizeof(buffer_), format, args);
+  va_end(args);
+
+  return write_(buffer_);
+}
+
+}  // namespace pw::unit_test