[UnitTests] Added UnitTests for MetricEvents in tracing (#32477)

* [UnitTests] Added UnitTests for MetricEvents in tracing

- Added unittests to validate behavior MetricEvent and related macros

* Restyler fixes

* Incorporated review feedback

* Incorporated review feedback2

* Restyler fixes2
diff --git a/src/tracing/tests/BUILD.gn b/src/tracing/tests/BUILD.gn
index 6f3ef63..8f28b56 100644
--- a/src/tracing/tests/BUILD.gn
+++ b/src/tracing/tests/BUILD.gn
@@ -23,13 +23,16 @@
   chip_test_suite_using_nltest("tests") {
     output_name = "libTracingTests"
 
-    test_sources = [ "TestTracing.cpp" ]
-    sources = []
+    test_sources = [
+      "TestMetricEvents.cpp",
+      "TestTracing.cpp",
+    ]
 
     public_deps = [
       "${chip_root}/src/lib/support:testing_nlunit",
       "${chip_root}/src/platform",
       "${chip_root}/src/tracing",
+      "${chip_root}/src/tracing:macros",
       "${nlunit_test_root}:nlunit-test",
     ]
   }
diff --git a/src/tracing/tests/TestMetricEvents.cpp b/src/tracing/tests/TestMetricEvents.cpp
new file mode 100644
index 0000000..c52a5fc
--- /dev/null
+++ b/src/tracing/tests/TestMetricEvents.cpp
@@ -0,0 +1,348 @@
+/*
+ *    Copyright (c) 2024 Project CHIP 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
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+#include <lib/support/UnitTestRegistration.h>
+#include <tracing/backend.h>
+#include <tracing/metric_event.h>
+
+#include <nlunit-test.h>
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+using namespace chip;
+using namespace chip::Tracing;
+
+namespace chip {
+namespace Tracing {
+
+static bool operator==(const MetricEvent & lhs, const MetricEvent & rhs)
+{
+    if (&lhs == &rhs)
+    {
+        return true;
+    }
+
+    if (lhs.type() == rhs.type() && std::string(lhs.key()) == std::string(rhs.key()) && lhs.ValueType() == rhs.ValueType())
+    {
+        switch (lhs.ValueType())
+        {
+        case MetricEvent::Value::Type::kInt32:
+            return lhs.ValueInt32() == rhs.ValueInt32();
+
+        case MetricEvent::Value::Type::kUInt32:
+            return lhs.ValueUInt32() == rhs.ValueUInt32();
+
+        case MetricEvent::Value::Type::kChipErrorCode:
+            return lhs.ValueErrorCode() == rhs.ValueErrorCode();
+
+        case MetricEvent::Value::Type::kUndefined:
+            return true;
+        }
+    }
+    return false;
+}
+} // namespace Tracing
+} // namespace chip
+
+namespace {
+
+// This keeps a log of all received trace items
+class MetricEventBackend : public Backend
+{
+public:
+    MetricEventBackend() {}
+    const std::vector<MetricEvent> & GetMetricEvents() const { return mMetricEvents; }
+
+    // Implementation
+    virtual void LogMetricEvent(const MetricEvent & event) { mMetricEvents.push_back(event); }
+
+private:
+    std::vector<MetricEvent> mMetricEvents;
+};
+
+void TestBasicMetricEvent(nlTestSuite * inSuite, void * inContext)
+{
+
+    {
+        MetricEvent event(MetricEvent::Type::kInstantEvent, "instant_event");
+        NL_TEST_ASSERT(inSuite, event.type() == MetricEvent::Type::kInstantEvent);
+        NL_TEST_ASSERT(inSuite, std::string(event.key()) == std::string("instant_event"));
+        NL_TEST_ASSERT(inSuite, event.ValueType() == MetricEvent::Value::Type::kUndefined);
+    }
+
+    {
+        MetricEvent event(MetricEvent::Type::kBeginEvent, "begin_event");
+        NL_TEST_ASSERT(inSuite, event.type() == MetricEvent::Type::kBeginEvent);
+        NL_TEST_ASSERT(inSuite, std::string(event.key()) == std::string("begin_event"));
+        NL_TEST_ASSERT(inSuite, event.ValueType() == MetricEvent::Value::Type::kUndefined);
+    }
+
+    {
+        MetricEvent event(MetricEvent::Type::kEndEvent, "end_event");
+        NL_TEST_ASSERT(inSuite, event.type() == MetricEvent::Type::kEndEvent);
+        NL_TEST_ASSERT(inSuite, std::string(event.key()) == std::string("end_event"));
+        NL_TEST_ASSERT(inSuite, event.ValueType() == MetricEvent::Value::Type::kUndefined);
+    }
+
+    {
+        MetricEvent event(MetricEvent::Type::kEndEvent, "end_event_with_int32_value", int32_t(42));
+        NL_TEST_ASSERT(inSuite, event.type() == MetricEvent::Type::kEndEvent);
+        NL_TEST_ASSERT(inSuite, std::string(event.key()) == std::string("end_event_with_int32_value"));
+        NL_TEST_ASSERT(inSuite, event.ValueType() == MetricEvent::Value::Type::kInt32);
+        NL_TEST_ASSERT(inSuite, event.ValueInt32() == 42);
+    }
+
+    {
+        MetricEvent event(MetricEvent::Type::kEndEvent, "end_event_with_uint32_value", uint32_t(42));
+        NL_TEST_ASSERT(inSuite, event.type() == MetricEvent::Type::kEndEvent);
+        NL_TEST_ASSERT(inSuite, std::string(event.key()) == std::string("end_event_with_uint32_value"));
+        NL_TEST_ASSERT(inSuite, event.ValueType() == MetricEvent::Value::Type::kUInt32);
+        NL_TEST_ASSERT(inSuite, event.ValueUInt32() == 42u);
+    }
+
+    {
+        MetricEvent event(MetricEvent::Type::kEndEvent, "end_event_with_error_value", CHIP_ERROR_BUSY);
+        NL_TEST_ASSERT(inSuite, event.type() == MetricEvent::Type::kEndEvent);
+        NL_TEST_ASSERT(inSuite, std::string(event.key()) == std::string("end_event_with_error_value"));
+        NL_TEST_ASSERT(inSuite, event.ValueType() == MetricEvent::Value::Type::kChipErrorCode);
+        NL_TEST_ASSERT(inSuite, chip::ChipError(event.ValueErrorCode()) == CHIP_ERROR_BUSY);
+    }
+}
+
+void TestInstantMetricEvent(nlTestSuite * inSuite, void * inContext)
+{
+    MetricEventBackend backend;
+
+    {
+        ScopedRegistration scope(backend);
+
+        MATTER_LOG_METRIC("event1");
+        MATTER_LOG_METRIC("event2");
+        MATTER_LOG_METRIC("event3");
+    }
+
+    std::vector<MetricEvent> expected = {
+        MetricEvent(MetricEvent::Type::kInstantEvent, "event1"),
+        MetricEvent(MetricEvent::Type::kInstantEvent, "event2"),
+        MetricEvent(MetricEvent::Type::kInstantEvent, "event3"),
+    };
+
+    NL_TEST_ASSERT(inSuite, backend.GetMetricEvents().size() == expected.size());
+    NL_TEST_ASSERT(
+        inSuite, std::equal(backend.GetMetricEvents().begin(), backend.GetMetricEvents().end(), expected.begin(), expected.end()));
+}
+
+void TestBeginEndMetricEvent(nlTestSuite * inSuite, void * inContext)
+{
+    MetricEventBackend backend1;
+    MetricEventBackend backend2;
+
+    {
+        ScopedRegistration scope1(backend1);
+        {
+
+            MATTER_LOG_METRIC_BEGIN("event1");
+            MATTER_LOG_METRIC_BEGIN("event2");
+            MATTER_LOG_METRIC_END("event2", 53);
+            MATTER_LOG_METRIC_END("event1");
+        }
+
+        std::vector<MetricEvent> expected1 = {
+            MetricEvent(MetricEvent::Type::kBeginEvent, "event1"),
+            MetricEvent(MetricEvent::Type::kBeginEvent, "event2"),
+            MetricEvent(MetricEvent::Type::kEndEvent, "event2", 53),
+            MetricEvent(MetricEvent::Type::kEndEvent, "event1"),
+        };
+
+        NL_TEST_ASSERT(inSuite, backend1.GetMetricEvents().size() == expected1.size());
+        NL_TEST_ASSERT(
+            inSuite,
+            std::equal(backend1.GetMetricEvents().begin(), backend1.GetMetricEvents().end(), expected1.begin(), expected1.end()));
+
+        {
+            ScopedRegistration scope2(backend2);
+
+            MATTER_LOG_METRIC_BEGIN("event1");
+            MATTER_LOG_METRIC_BEGIN("event2");
+            MATTER_LOG_METRIC_BEGIN("event3");
+            MATTER_LOG_METRIC_BEGIN("event4");
+            MATTER_LOG_METRIC_END("event3", CHIP_ERROR_UNKNOWN_KEY_TYPE);
+            MATTER_LOG_METRIC_END("event1", 91u);
+            MATTER_LOG_METRIC_END("event2", 53);
+            MATTER_LOG_METRIC_END("event4");
+        }
+
+        std::vector<MetricEvent> expected2 = {
+            MetricEvent(MetricEvent::Type::kBeginEvent, "event1"),
+            MetricEvent(MetricEvent::Type::kBeginEvent, "event2"),
+            MetricEvent(MetricEvent::Type::kBeginEvent, "event3"),
+            MetricEvent(MetricEvent::Type::kBeginEvent, "event4"),
+            MetricEvent(MetricEvent::Type::kEndEvent, "event3", CHIP_ERROR_UNKNOWN_KEY_TYPE),
+            MetricEvent(MetricEvent::Type::kEndEvent, "event1", 91u),
+            MetricEvent(MetricEvent::Type::kEndEvent, "event2", 53),
+            MetricEvent(MetricEvent::Type::kEndEvent, "event4"),
+        };
+
+        NL_TEST_ASSERT(inSuite, backend2.GetMetricEvents().size() == expected2.size());
+        NL_TEST_ASSERT(
+            inSuite,
+            std::equal(backend2.GetMetricEvents().begin(), backend2.GetMetricEvents().end(), expected2.begin(), expected2.end()));
+    }
+}
+
+void TestScopedMetricEvent(nlTestSuite * inSuite, void * inContext)
+{
+    MetricEventBackend backend1;
+    MetricEventBackend backend2;
+    MetricEventBackend backend3;
+    chip::ChipError err1 = CHIP_NO_ERROR;
+    chip::ChipError err2 = CHIP_NO_ERROR;
+    chip::ChipError err3 = CHIP_NO_ERROR;
+    chip::ChipError err4 = CHIP_NO_ERROR;
+
+    {
+        ScopedRegistration scope1(backend1);
+        {
+            MATTER_LOG_METRIC_SCOPE("event1", err1);
+            err1 = CHIP_ERROR_BUSY;
+            {
+                ScopedRegistration scope2(backend2);
+                MATTER_LOG_METRIC_SCOPE("event2", err2);
+                err2 = CHIP_ERROR_BAD_REQUEST;
+
+                {
+                    ScopedRegistration scope3(backend3);
+                    MATTER_LOG_METRIC_SCOPE("event3", err3);
+                    err3 = CHIP_ERROR_EVENT_ID_FOUND;
+                }
+                {
+                    MATTER_LOG_METRIC_SCOPE("event4", err4);
+                    err4 = CHIP_ERROR_BUFFER_TOO_SMALL;
+                }
+            }
+        }
+
+        std::vector<MetricEvent> expected1 = {
+            MetricEvent(MetricEvent::Type::kBeginEvent, "event1"),
+            MetricEvent(MetricEvent::Type::kBeginEvent, "event2"),
+            MetricEvent(MetricEvent::Type::kBeginEvent, "event3"),
+            MetricEvent(MetricEvent::Type::kEndEvent, "event3", CHIP_ERROR_EVENT_ID_FOUND),
+            MetricEvent(MetricEvent::Type::kBeginEvent, "event4"),
+            MetricEvent(MetricEvent::Type::kEndEvent, "event4", CHIP_ERROR_BUFFER_TOO_SMALL),
+            MetricEvent(MetricEvent::Type::kEndEvent, "event2", CHIP_ERROR_BAD_REQUEST),
+            MetricEvent(MetricEvent::Type::kEndEvent, "event1", CHIP_ERROR_BUSY),
+        };
+
+        NL_TEST_ASSERT(inSuite, backend1.GetMetricEvents().size() == expected1.size());
+        NL_TEST_ASSERT(
+            inSuite,
+            std::equal(backend1.GetMetricEvents().begin(), backend1.GetMetricEvents().end(), expected1.begin(), expected1.end()));
+
+        std::vector<MetricEvent> expected2 = {
+            MetricEvent(MetricEvent::Type::kBeginEvent, "event2"),
+            MetricEvent(MetricEvent::Type::kBeginEvent, "event3"),
+            MetricEvent(MetricEvent::Type::kEndEvent, "event3", CHIP_ERROR_EVENT_ID_FOUND),
+            MetricEvent(MetricEvent::Type::kBeginEvent, "event4"),
+            MetricEvent(MetricEvent::Type::kEndEvent, "event4", CHIP_ERROR_BUFFER_TOO_SMALL),
+            MetricEvent(MetricEvent::Type::kEndEvent, "event2", CHIP_ERROR_BAD_REQUEST),
+        };
+
+        NL_TEST_ASSERT(inSuite, backend2.GetMetricEvents().size() == expected2.size());
+        NL_TEST_ASSERT(
+            inSuite,
+            std::equal(backend2.GetMetricEvents().begin(), backend2.GetMetricEvents().end(), expected2.begin(), expected2.end()));
+
+        std::vector<MetricEvent> expected3 = {
+            MetricEvent(MetricEvent::Type::kBeginEvent, "event3"),
+            MetricEvent(MetricEvent::Type::kEndEvent, "event3", CHIP_ERROR_EVENT_ID_FOUND),
+        };
+
+        NL_TEST_ASSERT(inSuite, backend3.GetMetricEvents().size() == expected3.size());
+        NL_TEST_ASSERT(
+            inSuite,
+            std::equal(backend3.GetMetricEvents().begin(), backend3.GetMetricEvents().end(), expected3.begin(), expected3.end()));
+    }
+}
+
+static int DoubleOf(int input)
+{
+    return input * 2;
+}
+
+void TestVerifyOrExitWithMetric(nlTestSuite * inSuite, void * inContext)
+{
+    MetricEventBackend backend;
+    ScopedRegistration scope(backend);
+    chip::ChipError err = CHIP_NO_ERROR;
+
+    VerifyOrExitWithMetric("event0", DoubleOf(2) == 4, err = CHIP_ERROR_BAD_REQUEST);
+    VerifyOrExitWithMetric("event1", DoubleOf(3) == 9, err = CHIP_ERROR_INCORRECT_STATE);
+
+exit:
+    std::vector<MetricEvent> expected = {
+        MetricEvent(MetricEvent::Type::kInstantEvent, "event1", CHIP_ERROR_INCORRECT_STATE),
+    };
+
+    NL_TEST_ASSERT(inSuite, err == CHIP_ERROR_INCORRECT_STATE);
+    NL_TEST_ASSERT(inSuite, backend.GetMetricEvents().size() == expected.size());
+    NL_TEST_ASSERT(
+        inSuite, std::equal(backend.GetMetricEvents().begin(), backend.GetMetricEvents().end(), expected.begin(), expected.end()));
+}
+
+void TestSuccessOrExitWithMetric(nlTestSuite * inSuite, void * inContext)
+{
+    MetricEventBackend backend;
+    ScopedRegistration scope(backend);
+    chip::ChipError err = CHIP_NO_ERROR;
+
+    SuccessOrExitWithMetric("event1", err = CHIP_NO_ERROR);
+    SuccessOrExitWithMetric("event2", err = CHIP_ERROR_BUSY);
+    SuccessOrExitWithMetric("event3", err = CHIP_NO_ERROR);
+
+exit:
+    std::vector<MetricEvent> expected = {
+        MetricEvent(MetricEvent::Type::kInstantEvent, "event2", CHIP_ERROR_BUSY),
+    };
+
+    NL_TEST_ASSERT(inSuite, err == CHIP_ERROR_BUSY);
+    NL_TEST_ASSERT(inSuite, backend.GetMetricEvents().size() == expected.size());
+    NL_TEST_ASSERT(
+        inSuite, std::equal(backend.GetMetricEvents().begin(), backend.GetMetricEvents().end(), expected.begin(), expected.end()));
+}
+
+static const nlTest sMetricTests[] = {
+    NL_TEST_DEF("BasicMetricEvent", TestBasicMetricEvent),               //
+    NL_TEST_DEF("InstantMetricEvent", TestInstantMetricEvent),           //
+    NL_TEST_DEF("BeginEndMetricEvent", TestBeginEndMetricEvent),         //
+    NL_TEST_DEF("ScopedMetricEvent", TestScopedMetricEvent),             //
+    NL_TEST_DEF("VerifyOrExitWithMetric", TestVerifyOrExitWithMetric),   //
+    NL_TEST_DEF("SuccessOrExitWithMetric", TestSuccessOrExitWithMetric), //
+    NL_TEST_SENTINEL()                                                   //
+};
+
+} // namespace
+
+int TestMetricEvents()
+{
+    nlTestSuite theSuite = { "Metric event tests", &sMetricTests[0], nullptr, nullptr };
+
+    // Run test suite against one context.
+    nlTestRunner(&theSuite, nullptr);
+    return nlTestRunnerStats(&theSuite);
+}
+
+CHIP_REGISTER_TEST_SUITE(TestMetricEvents)