Define pw_test_group template

This change adds a GN template to the pw_unit_test module which defines
a group of unit tests. The template creates a generated_file target with
JSON metadata for the unit tests and dependencies within the test group.
These metadata files are intended to be parsed by the test runner script
to run unit tests outside of GN.

A pw_test_group target is added to each of the existing modules which
have unit tests.

Change-Id: I363b937da365d0a3482a979feace18ecba1807a9
diff --git a/BUILD.gn b/BUILD.gn
index bd5f9e6..be74d9d 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -29,6 +29,9 @@
 # Targets for all module unit test groups.
 group("pw_module_tests") {
   deps = [
-    "$dir_pw_preprocessor:unit_tests",
+    "$dir_pw_preprocessor:tests",
+    "$dir_pw_span:tests",
+    "$dir_pw_status:tests",
+    "$dir_pw_string:tests",
   ]
 }
diff --git a/pw_preprocessor/BUILD.gn b/pw_preprocessor/BUILD.gn
index 112ab73..4bd8742 100644
--- a/pw_preprocessor/BUILD.gn
+++ b/pw_preprocessor/BUILD.gn
@@ -41,8 +41,8 @@
 }
 
 # All pw_preprocessor test binaries.
-group("unit_tests") {
-  deps = [
+pw_test_group("tests") {
+  tests = [
     ":boolean_test",
     ":concat_test",
     ":macro_arg_count_test",
diff --git a/pw_span/BUILD.gn b/pw_span/BUILD.gn
index 35b8692..52ed013 100644
--- a/pw_span/BUILD.gn
+++ b/pw_span/BUILD.gn
@@ -29,6 +29,10 @@
   sources = public
 }
 
+pw_test_group("tests") {
+  tests = [ ":test" ]
+}
+
 pw_test("test") {
   deps = [
     ":pw_span",
diff --git a/pw_status/BUILD.gn b/pw_status/BUILD.gn
index 1659341..c358931 100644
--- a/pw_status/BUILD.gn
+++ b/pw_status/BUILD.gn
@@ -30,6 +30,13 @@
   sources = [ "status.cc" ] + public
 }
 
+pw_test_group("tests") {
+  tests = [
+    ":status_test",
+    ":status_with_size_test",
+  ]
+}
+
 pw_test("status_test") {
   deps = [
     ":pw_status",
diff --git a/pw_string/BUILD.gn b/pw_string/BUILD.gn
index 3674b89..1b8c52a 100644
--- a/pw_string/BUILD.gn
+++ b/pw_string/BUILD.gn
@@ -40,6 +40,19 @@
   ]
 }
 
+pw_test_group("tests") {
+  tests = [
+    ":format_test",
+    ":to_string_test",
+    ":type_to_string_test",
+  ]
+  group_deps = [
+    "$dir_pw_preprocessor:tests",
+    "$dir_pw_span:tests",
+    "$dir_pw_status:tests",
+  ]
+}
+
 pw_test("format_test") {
   deps = [
     ":pw_string",
diff --git a/pw_unit_test/test.gni b/pw_unit_test/test.gni
index f52b1b2..2f5654a 100644
--- a/pw_unit_test/test.gni
+++ b/pw_unit_test/test.gni
@@ -16,8 +16,10 @@
 import("$dir_pw_build/python_script.gni")
 
 # Creates an executable target for a unit test.
-# Additionally, outputs a file containing unit test metadata in JSON format for
-# the test runner script.
+#
+# If the pw_unit_test_create_run_targets variable is set to true, this template
+# also creates a "${test_name}_run" target which runs the unit test executable
+# after building it.
 #
 # This template accepts all of the regular "executable" target args.
 template("pw_test") {
@@ -27,44 +29,19 @@
   # shadowing the one in this scope.
   _test_target_name = target_name
 
-  # Metadata for the test runner script. This is a dummy target which doesn't
-  # require or produce anything; it simply exists to define the unit test
-  # metadata.
-  _metadata_group_target = _test_target_name + "_pw_metadata_group"
-  group(_metadata_group_target) {
+  pw_executable(_test_target_name) {
+    # Metadata for this test when used as part of a pw_test_group target.
     metadata = {
-      test_metadata = [
+      tests = [
         {
+          type = "test"
           test_name = _test_target_name
+          test_directory = rebase_path(target_out_dir, root_build_dir)
         },
       ]
     }
-  }
 
-  # Output file for the unit test metadata. Reads metadata from the dummy group
-  # target and outputs to a JSON file.
-  _metadata_file_target = _test_target_name + "_pw_test_metadata"
-  generated_file(_metadata_file_target) {
-    outputs = [
-      "$target_out_dir/$_test_target_name.meta.json",
-    ]
-    data_keys = [ "test_metadata" ]
-    output_conversion = "json"
-
-    deps = [
-      ":$_metadata_group_target",
-    ]
-  }
-
-  # Actual executable file for the unit test. Depends on the metadata output
-  # file in order to generate it as well.
-  pw_executable(_test_target_name) {
-    forward_variables_from(invoker, "*")
-
-    if (!defined(deps)) {
-      deps = []
-    }
-    deps += [ ":$_metadata_file_target" ]
+    forward_variables_from(invoker, "*", [ "metadata" ])
   }
 
   if (pw_unit_test_create_run_targets) {
@@ -82,3 +59,58 @@
     }
   }
 }
+
+# Defines a related collection of unit tests.
+#
+# pw_test_group targets output a JSON metadata file for the Pigweed test runner.
+#
+# Args:
+#  tests: List of pw_test targets for each of the tests in the group.
+#  group_deps: Optional pw_test_group targets on which this group depends.
+template("pw_test_group") {
+  if (defined(invoker.group_deps)) {
+    # If the group specified any other group dependencies, create a metadata
+    # entry for each of them indicating that they are another group and a group
+    # target to collect that metadata.
+    _group_deps = []
+    foreach(dep, invoker.group_deps) {
+      _group_deps += [
+        {
+          type = "dep"
+          group = get_path_info(dep, "abspath")
+        },
+      ]
+    }
+
+    _metadata_group_target = "${target_name}_pw_test_group_metadata"
+    group(_metadata_group_target) {
+      metadata = {
+        group_deps = _group_deps
+
+        # Metadata from the group's own unit test targets is forwarded through
+        # the group dependencies group. This entry is listed as a "walk_key" in
+        # the generated file so that only test targets' metadata (not group
+        # targets) appear in the output.
+        propagate_metadata_from = invoker.tests
+      }
+      deps = invoker.tests + invoker.group_deps
+    }
+
+    _test_group_deps = [ ":$_metadata_group_target" ]
+  } else {
+    _test_group_deps = invoker.tests
+  }
+
+  generated_file(target_name) {
+    outputs = [
+      "$target_out_dir/$target_name.utmeta.json",
+    ]
+    data_keys = [
+      "group_deps",
+      "tests",
+    ]
+    walk_keys = [ "propagate_metadata_from" ]
+    output_conversion = "json"
+    deps = _test_group_deps
+  }
+}