pw_unit_test: Support ASSERT/EXPECT in test ctor

Make current_test_ a pointer to the TestInfo, and set it before
constructing the test instance. This allows EXPECT/ASSERT statements to
be used in fixture constructors and destructors.

Change-Id: Ib84db91acc080ba103cdb543b4d764b192b398f7
diff --git a/pw_unit_test/framework.cc b/pw_unit_test/framework.cc
index 5c2ba05..1a27eda 100644
--- a/pw_unit_test/framework.cc
+++ b/pw_unit_test/framework.cc
@@ -23,16 +23,6 @@
 }
 
 namespace internal {
-namespace {
-
-bool TestIsEnabled(const TestInfo& test) {
-  constexpr size_t kStringSize = sizeof("DISABLED_") - 1;
-  return std::strncmp("DISABLED_", test.test_case.test_name, kStringSize) !=
-             0 &&
-         std::strncmp("DISABLED_", test.test_case.suite_name, kStringSize) != 0;
-}
-
-}  // namespace
 
 // Singleton instance of the unit test framework class.
 Framework Framework::framework_;
@@ -41,12 +31,18 @@
 // 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) {
+void Framework::RegisterTest(TestInfo* new_test) {
+  // If the test list is empty, set new_test as the first test.
+  if (tests_ == nullptr) {
+    tests_ = new_test;
+    return;
   }
-  *pos = test;
+
+  // Append the test case to the end of the test list.
+  TestInfo* info = tests_;
+  for (; info->next() != nullptr; info = info->next()) {
+  }
+  info->set_next(new_test);
 }
 
 int Framework::RunAllTests() {
@@ -56,11 +52,11 @@
   if (event_handler_ != nullptr) {
     event_handler_->RunAllTestsStart();
   }
-  for (TestInfo* test = tests_; test != nullptr; test = test->next) {
-    if (TestIsEnabled(*test)) {
+  for (const TestInfo* test = tests_; test != nullptr; test = test->next()) {
+    if (test->enabled()) {
       test->run();
     } else {
-      event_handler_->TestCaseDisabled(test->test_case);
+      event_handler_->TestCaseDisabled(test->test_case());
     }
   }
   if (event_handler_ != nullptr) {
@@ -69,18 +65,16 @@
   return exit_status_;
 }
 
-void Framework::StartTest(Test* test) {
-  current_test_ = test;
+void Framework::StartTest(const TestInfo& test) {
+  current_test_ = &test;
   current_result_ = TestResult::kSuccess;
 
-  if (event_handler_ == nullptr) {
-    return;
+  if (event_handler_ != nullptr) {
+    event_handler_->TestCaseStart(test.test_case());
   }
-  event_handler_->TestCaseStart(test->pigweed_test_info_->test_case);
 }
 
-void Framework::EndTest(Test* test) {
-  current_test_ = nullptr;
+void Framework::EndCurrentTest() {
   switch (current_result_) {
     case TestResult::kSuccess:
       run_tests_summary_.passed_tests++;
@@ -90,12 +84,11 @@
       break;
   }
 
-  if (event_handler_ == nullptr) {
-    return;
+  if (event_handler_ != nullptr) {
+    event_handler_->TestCaseEnd(current_test_->test_case(), current_result_);
   }
 
-  event_handler_->TestCaseEnd(test->pigweed_test_info_->test_case,
-                              current_result_);
+  current_test_ = nullptr;
 }
 
 void Framework::ExpectationResult(const char* expression,
@@ -118,10 +111,14 @@
       .success = success,
   };
 
-  event_handler_->TestCaseExpect(current_test_->pigweed_test_info_->test_case,
-                                 expectation);
+  event_handler_->TestCaseExpect(current_test_->test_case(), expectation);
+}
+
+bool TestInfo::enabled() const {
+  constexpr size_t kStringSize = sizeof("DISABLED_") - 1;
+  return std::strncmp("DISABLED_", test_case().test_name, kStringSize) != 0 &&
+         std::strncmp("DISABLED_", test_case().suite_name, kStringSize) != 0;
 }
 
 }  // namespace internal
-
 }  // namespace pw::unit_test
diff --git a/pw_unit_test/framework_test.cc b/pw_unit_test/framework_test.cc
index 2d570ee..10a0ada 100644
--- a/pw_unit_test/framework_test.cc
+++ b/pw_unit_test/framework_test.cc
@@ -162,5 +162,16 @@
   EXPECT_EQ(cool_number_, 3500);
 }
 
+class Expectations : public ::testing::Test {
+ protected:
+  Expectations() : cool_number_(3) { ASSERT_EQ(cool_number_, 3); }
+
+  ~Expectations() { ASSERT_EQ(cool_number_, 14159); }
+
+  int cool_number_;
+};
+
+TEST_F(Expectations, SetCoolNumber) { cool_number_ = 14159; }
+
 }  // namespace
 }  // namespace pw
diff --git a/pw_unit_test/public/pw_unit_test/framework.h b/pw_unit_test/public/pw_unit_test/framework.h
index a74304b..6588cc5 100644
--- a/pw_unit_test/public/pw_unit_test/framework.h
+++ b/pw_unit_test/public/pw_unit_test/framework.h
@@ -143,7 +143,7 @@
 
 namespace internal {
 
-struct TestInfo;
+class TestInfo;
 
 // Singleton test framework class responsible for managing and running test
 // cases. This implementation is internal to Pigweed test; free functions
@@ -184,7 +184,7 @@
   // statically allocated per test case, with a run() function that references
   // this method instantiated for its test class.
   template <typename TestInstance>
-  static void CreateAndRunTest() {
+  static void CreateAndRunTest(const TestInfo& test_info) {
     // TODO(frolv): Update the assert message with the name of the config option
     // for memory pool size once it is configurable.
     static_assert(
@@ -193,17 +193,18 @@
         "kTestMemoryPoolSizeBytes or decrease the size of your test fixture.");
 
     Framework& framework = Get();
+    framework.StartTest(test_info);
 
-    // Construct the test object within the static memory pool.
+    // Construct the test object within the static memory pool. The StartTest
+    // function has already been called by the TestInfo at this point.
     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();
+
+    framework.EndCurrentTest();
   }
 
   // Runs an expectation function for the currently active test case.
@@ -245,11 +246,11 @@
                          bool success);
 
  private:
-  // Dispatches an event indicating that a test started running.
-  void StartTest(Test* test);
+  // Sets current_test_ and dispatches an event indicating that a test started.
+  void StartTest(const TestInfo& test);
 
-  // Dispatches an event indicating that a test finished running.
-  void EndTest(Test* test);
+  // Dispatches event indicating that a test finished and clears current_test_.
+  void EndCurrentTest();
 
   // Singleton instance of the framework class.
   static Framework framework_;
@@ -259,7 +260,7 @@
   static TestInfo* tests_;
 
   // The current test case which is running.
-  Test* current_test_;
+  const TestInfo* current_test_;
 
   // Overall result of the current test case (pass/fail).
   TestResult current_result_;
@@ -283,30 +284,41 @@
 // 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 {
+class TestInfo {
+ public:
   TestInfo(const char* const test_suite_name,
            const char* const test_name,
            const char* const file_name,
-           void (*run)())
-      : test_case{
+           void (*run)(const TestInfo&))
+      : test_case_{
         .suite_name = test_suite_name,
         .test_name = test_name,
         .file_name = file_name,
-       }, run(run) {
+       }, run_(run) {
     Framework::Get().RegisterTest(this);
   }
 
   // The name of the suite to which the test case belongs, the name of the test
   // case itself, and the path to the file in which the test case is located.
-  TestCase test_case;
+  const TestCase& test_case() const { return test_case_; }
+
+  bool enabled() const;
+
+  void run() const { run_(*this); }
+
+  TestInfo* next() const { return next_; }
+  void set_next(TestInfo* next) { next_ = next; }
+
+ private:
+  TestCase test_case_;
 
   // Function which runs the test case. Refers to Framework::CreateAndRunTest
   // instantiated for the test case's class.
-  void (*run)();
+  void (*run_)(const TestInfo&);
 
   // TestInfo structs are registered with the test framework and stored as a
   // linked list.
-  TestInfo* next = nullptr;
+  TestInfo* next_ = nullptr;
 };
 
 }  // namespace internal
@@ -332,18 +344,9 @@
 
   virtual ~Test() = default;
 
- 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;
 };
@@ -354,32 +357,26 @@
 #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) final      \
-      : 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)>);   \
-                                                                   \
+#define _PW_TEST(test_suite_name, test_name, parent_class)              \
+  static_assert(sizeof(#test_suite_name) > 1,                           \
+                "test_suite_name must not be empty");                   \
+  static_assert(sizeof(#test_name) > 1, "test_name must not be empty"); \
+                                                                        \
+  class _PW_TEST_CLASS_NAME(test_suite_name, test_name) final           \
+      : public parent_class {                                           \
+   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_(      \
+          #test_suite_name,                                             \
+          #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) \