| // Copyright 2020 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. |
| |
| // clang-format off |
| #define PW_TRACE_MODULE_NAME "TST" |
| |
| #include "pw_trace/trace.h" |
| #include "pw_trace_tokenized/trace_tokenized.h" |
| #include "pw_trace_tokenized/trace_callback.h" |
| // clang-format on |
| |
| #include <deque> |
| |
| #include "gtest/gtest.h" |
| |
| namespace { |
| |
| // These are line numbers for the functions below. Moving these functions to |
| // other lines will require updating these macros. |
| #define TRACE_FUNCTION_LINE 35 |
| #define TRACE_FUNCTION_GROUP_LINE 36 |
| #define TRACE_FUNCTION_ID_LINE 38 |
| |
| void TraceFunction() { PW_TRACE_FUNCTION(); } |
| void TraceFunctionGroup() { PW_TRACE_FUNCTION("FunctionGroup"); } |
| void TraceFunctionTraceId(uint32_t id) { |
| PW_TRACE_FUNCTION("FunctionGroup", id); |
| } |
| |
| // This trace test interface registers as a trace callback to capture trace |
| // events to verify extpected behaviour. It also supports testing common actions |
| // within the callback. |
| class TraceTestInterface { |
| public: |
| struct TraceInfo { |
| uint32_t trace_ref; |
| pw::trace::EventType event_type; |
| const char* module; |
| uint32_t trace_id; |
| bool operator==(const TraceInfo& b) const { |
| return trace_ref == b.trace_ref && event_type == b.event_type && |
| module == b.module && trace_id == b.trace_id; |
| } |
| }; |
| |
| TraceTestInterface() { |
| PW_TRACE_SET_ENABLED(true); |
| pw::trace::Callbacks::Instance() |
| .RegisterSink(TraceSinkStartBlock, |
| TraceSinkAddBytes, |
| TraceSinkEndBlock, |
| this, |
| &sink_handle_) |
| .IgnoreError(); // TODO(pwbug/387): Handle Status properly |
| pw::trace::Callbacks::Instance() |
| .RegisterEventCallback(TraceEventCallback, |
| pw::trace::CallbacksImpl::kCallOnlyWhenEnabled, |
| this, |
| &event_callback_handle_) |
| .IgnoreError(); // TODO(pwbug/387): Handle Status properly |
| } |
| ~TraceTestInterface() { |
| pw::trace::Callbacks::Instance() |
| .UnregisterSink(sink_handle_) |
| .IgnoreError(); // TODO(pwbug/387): Handle Status properly |
| pw::trace::Callbacks::Instance() |
| .UnregisterEventCallback(event_callback_handle_) |
| .IgnoreError(); // TODO(pwbug/387): Handle Status properly |
| } |
| // ActionOnEvent will perform a specific action within the callback when an |
| // event matches one of the characteristics of event_match_. |
| enum class ActionOnEvent { None, Enable, Disable, DisableAfter, Skip }; |
| void SetCallbackEventAction(ActionOnEvent action, TraceInfo event) { |
| action_ = action; |
| event_match_ = event; |
| } |
| |
| // The trace event callback will save the trace event info and add it to |
| // buffer_ in the TraceSink callback, that way it only gets added to the |
| // buffer if tracing is enabled and the sample was not surpressed. |
| static pw_trace_TraceEventReturnFlags TraceEventCallback( |
| void* user_data, |
| uint32_t trace_ref, |
| pw_trace_EventType event_type, |
| const char* module, |
| uint32_t trace_id, |
| uint8_t /* flags */) { |
| TraceTestInterface* test_interface = |
| reinterpret_cast<TraceTestInterface*>(user_data); |
| pw_trace_TraceEventReturnFlags ret = 0; |
| if (test_interface->action_ != ActionOnEvent::None && |
| (test_interface->event_match_.trace_ref == trace_ref || |
| test_interface->event_match_.event_type == event_type || |
| test_interface->event_match_.module == module || |
| (trace_id != PW_TRACE_TRACE_ID_DEFAULT && |
| test_interface->event_match_.trace_id == trace_id))) { |
| if (test_interface->action_ == ActionOnEvent::Skip) { |
| ret |= PW_TRACE_EVENT_RETURN_FLAGS_SKIP_EVENT; |
| } else if (test_interface->action_ == ActionOnEvent::Enable) { |
| PW_TRACE_SET_ENABLED(true); |
| } else if (test_interface->action_ == ActionOnEvent::Disable) { |
| PW_TRACE_SET_ENABLED(false); |
| } else if (test_interface->action_ == ActionOnEvent::DisableAfter) { |
| ret |= PW_TRACE_EVENT_RETURN_FLAGS_DISABLE_AFTER_PROCESSING; |
| } |
| } |
| |
| test_interface->current_trace_event_ = |
| TraceInfo{trace_ref, event_type, module, trace_id}; |
| return ret; |
| } |
| |
| // Only adds the event to buffer if the number of bytes inidcates is what is |
| // provided. |
| static void TraceSinkStartBlock(void* user_data, size_t size) { |
| TraceTestInterface* test_interface = |
| reinterpret_cast<TraceTestInterface*>(user_data); |
| test_interface->sink_block_size_ = size; |
| test_interface->sink_bytes_received_ = 0; |
| } |
| |
| static void TraceSinkAddBytes(void* user_data, |
| const void* bytes, |
| size_t size) { |
| TraceTestInterface* test_interface = |
| reinterpret_cast<TraceTestInterface*>(user_data); |
| static_cast<void>(bytes); |
| test_interface->sink_bytes_received_ += size; |
| } |
| |
| static void TraceSinkEndBlock(void* user_data) { |
| TraceTestInterface* test_interface = |
| reinterpret_cast<TraceTestInterface*>(user_data); |
| if (test_interface->sink_block_size_ == |
| test_interface->sink_bytes_received_) { |
| test_interface->buffer_.push_back(test_interface->current_trace_event_); |
| } |
| } |
| |
| // Get the event buffer. |
| std::deque<TraceInfo>& GetEvents() { return buffer_; } |
| |
| // Check that the next event in the buffer is equal to the expected (and pop |
| // that event). |
| bool CheckEvent(const TraceInfo& expected) { |
| if (buffer_.empty()) { |
| return false; |
| } |
| TraceInfo actual = buffer_.front(); |
| buffer_.pop_front(); |
| return actual == expected; |
| } |
| |
| private: |
| ActionOnEvent action_ = ActionOnEvent::None; |
| TraceInfo event_match_; |
| TraceInfo current_trace_event_; |
| size_t sink_block_size_; |
| size_t sink_bytes_received_; |
| std::deque<TraceInfo> buffer_; |
| pw::trace::CallbacksImpl::SinkHandle sink_handle_; |
| pw::trace::CallbacksImpl::EventCallbackHandle event_callback_handle_; |
| }; |
| |
| } // namespace |
| |
| // Helper macro to pop the next trace out of test interface and check it against |
| // expecte values. |
| #define EXPECT_TRACE(...) PW_DELEGATE_BY_ARG_COUNT(_EXPECT_TRACE, __VA_ARGS__) |
| #define _EXPECT_TRACE3(interface, event_type, label) \ |
| _EXPECT_TRACE7(interface, \ |
| event_type, \ |
| label, \ |
| PW_TRACE_GROUP_LABEL_DEFAULT, \ |
| PW_TRACE_TRACE_ID_DEFAULT, \ |
| PW_TRACE_MODULE_NAME, \ |
| PW_TRACE_FLAGS_DEFAULT) |
| #define _EXPECT_TRACE4(interface, event_type, label, group) \ |
| _EXPECT_TRACE7(interface, \ |
| event_type, \ |
| label, \ |
| group, \ |
| PW_TRACE_TRACE_ID_DEFAULT, \ |
| PW_TRACE_MODULE_NAME, \ |
| PW_TRACE_FLAGS_DEFAULT) |
| #define _EXPECT_TRACE5(interface, event_type, label, group, trace_id) \ |
| _EXPECT_TRACE7(interface, \ |
| event_type, \ |
| label, \ |
| group, \ |
| trace_id, \ |
| PW_TRACE_MODULE_NAME, \ |
| PW_TRACE_FLAGS_DEFAULT) |
| #define _EXPECT_TRACE6(interface, event_type, label, group, trace_id, module) \ |
| _EXPECT_TRACE7(interface, \ |
| event_type, \ |
| label, \ |
| group, \ |
| trace_id, \ |
| module, \ |
| PW_TRACE_FLAGS_DEFAULT) |
| #define _EXPECT_TRACE7( \ |
| interface, event_type, label, group, trace_id, module, flags) \ |
| do { \ |
| static uint32_t _label_token = \ |
| PW_TRACE_REF(event_type, module, label, flags, group); \ |
| EXPECT_TRUE( \ |
| interface.CheckEvent({_label_token, event_type, module, trace_id})); \ |
| } while (0) |
| |
| #define EXPECT_TRACE_DATA(...) \ |
| PW_DELEGATE_BY_ARG_COUNT(_EXPECT_TRACE_DATA, __VA_ARGS__) |
| #define _EXPECT_TRACE_DATA4(interface, event_type, label, data_type) \ |
| _EXPECT_TRACE_DATA8(interface, \ |
| event_type, \ |
| label, \ |
| PW_TRACE_GROUP_LABEL_DEFAULT, \ |
| PW_TRACE_TRACE_ID_DEFAULT, \ |
| data_type, \ |
| PW_TRACE_MODULE_NAME, \ |
| PW_TRACE_FLAGS_DEFAULT) |
| #define _EXPECT_TRACE_DATA5(interface, event_type, label, group, data_type) \ |
| _EXPECT_TRACE_DATA8(interface, \ |
| event_type, \ |
| label, \ |
| group, \ |
| PW_TRACE_TRACE_ID_DEFAULT, \ |
| data_type, \ |
| PW_TRACE_MODULE_NAME, \ |
| PW_TRACE_FLAGS_DEFAULT) |
| #define _EXPECT_TRACE_DATA6( \ |
| interface, event_type, label, group, trace_id, data_type) \ |
| _EXPECT_TRACE_DATA8(interface, \ |
| event_type, \ |
| label, \ |
| group, \ |
| trace_id, \ |
| data_type, \ |
| PW_TRACE_MODULE_NAME, \ |
| PW_TRACE_FLAGS_DEFAULT) |
| #define _EXPECT_TRACE_DATA7( \ |
| interface, event_type, label, group, trace_id, data_type, module) \ |
| _EXPECT_TRACE_DATA8(interface, \ |
| event_type, \ |
| label, \ |
| group, \ |
| trace_id, \ |
| data_type, \ |
| module, \ |
| PW_TRACE_FLAGS_DEFAULT) |
| #define _EXPECT_TRACE_DATA8( \ |
| interface, event_type, label, group, trace_id, data_type, module, flags) \ |
| do { \ |
| static uint32_t _label_token = \ |
| PW_TRACE_REF_DATA(event_type, module, label, flags, group, data_type); \ |
| EXPECT_TRUE( \ |
| interface.CheckEvent({_label_token, event_type, module, trace_id})); \ |
| } while (0) |
| |
| // Start of tests |
| |
| TEST(TokenizedTrace, Instant) { |
| TraceTestInterface test_interface; |
| |
| PW_TRACE_INSTANT("Test"); |
| PW_TRACE_INSTANT("Test2", "g"); |
| PW_TRACE_INSTANT("Test3", "g", 2); |
| |
| // Check results |
| EXPECT_TRACE(test_interface, PW_TRACE_TYPE_INSTANT, "Test"); |
| EXPECT_TRACE(test_interface, PW_TRACE_TYPE_INSTANT_GROUP, "Test2", "g"); |
| EXPECT_TRACE(test_interface, PW_TRACE_TYPE_ASYNC_INSTANT, "Test3", "g", 2); |
| EXPECT_TRUE(test_interface.GetEvents().empty()); |
| } |
| |
| TEST(TokenizedTrace, Duration) { |
| TraceTestInterface test_interface; |
| |
| PW_TRACE_START("Test"); |
| PW_TRACE_END("Test"); |
| |
| // Check results |
| EXPECT_TRACE(test_interface, PW_TRACE_TYPE_DURATION_START, "Test"); |
| EXPECT_TRACE(test_interface, PW_TRACE_TYPE_DURATION_END, "Test"); |
| EXPECT_TRUE(test_interface.GetEvents().empty()); |
| } |
| |
| TEST(TokenizedTrace, DurationGroup) { |
| TraceTestInterface test_interface; |
| |
| PW_TRACE_START("Parent", "group"); |
| PW_TRACE_START("Child", "group"); |
| PW_TRACE_END("Child", "group"); |
| PW_TRACE_END("Parent", "group"); |
| |
| // Check results |
| EXPECT_TRACE( |
| test_interface, PW_TRACE_TYPE_DURATION_GROUP_START, "Parent", "group"); |
| EXPECT_TRACE( |
| test_interface, PW_TRACE_TYPE_DURATION_GROUP_START, "Child", "group"); |
| EXPECT_TRACE( |
| test_interface, PW_TRACE_TYPE_DURATION_GROUP_END, "Child", "group"); |
| EXPECT_TRACE( |
| test_interface, PW_TRACE_TYPE_DURATION_GROUP_END, "Parent", "group"); |
| EXPECT_TRUE(test_interface.GetEvents().empty()); |
| } |
| |
| TEST(TokenizedTrace, Async) { |
| TraceTestInterface test_interface; |
| |
| uint32_t trace_id = 1; |
| PW_TRACE_START("label for async", "group", trace_id); |
| PW_TRACE_INSTANT("label for step", "group", trace_id); |
| PW_TRACE_END("label for async", "group", trace_id); |
| |
| // Check results |
| EXPECT_TRACE(test_interface, |
| PW_TRACE_TYPE_ASYNC_START, |
| "label for async", |
| "group", |
| trace_id); |
| EXPECT_TRACE(test_interface, |
| PW_TRACE_TYPE_ASYNC_INSTANT, |
| "label for step", |
| "group", |
| trace_id); |
| EXPECT_TRACE(test_interface, |
| PW_TRACE_TYPE_ASYNC_END, |
| "label for async", |
| "group", |
| trace_id); |
| EXPECT_TRUE(test_interface.GetEvents().empty()); |
| } |
| |
| TEST(TokenizedTrace, SkipEvent) { |
| TraceTestInterface test_interface; |
| |
| // Set trace interface to use skip flag in callback for a specific event. |
| TraceTestInterface::TraceInfo skip_event{ |
| 0, PW_TRACE_EVENT_TYPE_INVALID, "", PW_TRACE_TRACE_ID_DEFAULT}; |
| skip_event.trace_ref = PW_TRACE_REF(PW_TRACE_TYPE_INSTANT, |
| "TST", |
| "Test2", |
| PW_TRACE_FLAGS_DEFAULT, |
| PW_TRACE_GROUP_LABEL_DEFAULT); |
| test_interface.SetCallbackEventAction(TraceTestInterface::ActionOnEvent::Skip, |
| skip_event); |
| |
| PW_TRACE_INSTANT("Test"); |
| PW_TRACE_INSTANT("Test2"); |
| |
| // Check results |
| EXPECT_TRACE(test_interface, PW_TRACE_TYPE_INSTANT, "Test"); |
| EXPECT_TRUE(test_interface.GetEvents().empty()); |
| } |
| |
| TEST(TokenizedTrace, SkipModule) { |
| TraceTestInterface test_interface; |
| // Set trace interface to use skip flag in callback for a module. |
| TraceTestInterface::TraceInfo skip_event{ |
| 0, PW_TRACE_EVENT_TYPE_INVALID, "", PW_TRACE_TRACE_ID_DEFAULT}; |
| skip_event.module = "SkipModule"; |
| test_interface.SetCallbackEventAction(TraceTestInterface::ActionOnEvent::Skip, |
| skip_event); |
| |
| #undef PW_TRACE_MODULE_NAME |
| #define PW_TRACE_MODULE_NAME "SkipModule" |
| PW_TRACE_INSTANT("Test"); |
| #undef PW_TRACE_MODULE_NAME |
| #define PW_TRACE_MODULE_NAME "TST" |
| PW_TRACE_INSTANT("Test2"); |
| |
| // Check results |
| EXPECT_TRACE(test_interface, PW_TRACE_TYPE_INSTANT, "Test2"); |
| EXPECT_TRUE(test_interface.GetEvents().empty()); |
| } |
| |
| TEST(TokenizedTrace, DisableBeforeTrace) { |
| TraceTestInterface test_interface; |
| |
| // Set trace interface to disable when a specific event happens. |
| TraceTestInterface::TraceInfo trigger{ |
| 0, PW_TRACE_EVENT_TYPE_INVALID, "", PW_TRACE_TRACE_ID_DEFAULT}; |
| // Stop capturing when Test2 event shows up. |
| trigger.trace_ref = PW_TRACE_REF(PW_TRACE_TYPE_INSTANT, |
| "TST", // Module |
| "Test2", // Label |
| PW_TRACE_FLAGS_DEFAULT, |
| PW_TRACE_GROUP_LABEL_DEFAULT); |
| test_interface.SetCallbackEventAction( |
| TraceTestInterface::ActionOnEvent::Disable, trigger); |
| |
| PW_TRACE_INSTANT("Test1"); |
| PW_TRACE_INSTANT("Test2"); |
| PW_TRACE_INSTANT("Test3"); |
| |
| // Check results |
| EXPECT_TRACE(test_interface, PW_TRACE_TYPE_INSTANT, "Test1"); |
| EXPECT_TRUE(test_interface.GetEvents().empty()); |
| } |
| |
| TEST(TokenizedTrace, DisableAfterTrace) { |
| TraceTestInterface test_interface; |
| |
| // Set trace interface to use flag to disable after a specific event happens. |
| TraceTestInterface::TraceInfo trigger{ |
| 0, PW_TRACE_EVENT_TYPE_INVALID, "", PW_TRACE_TRACE_ID_DEFAULT}; |
| // Stop capturing after Test2 event shows up. |
| trigger.trace_ref = PW_TRACE_REF(PW_TRACE_TYPE_INSTANT, |
| "TST", // Module |
| "Test2", // Label |
| PW_TRACE_FLAGS_DEFAULT, |
| PW_TRACE_GROUP_LABEL_DEFAULT); |
| test_interface.SetCallbackEventAction( |
| TraceTestInterface::ActionOnEvent::DisableAfter, trigger); |
| |
| PW_TRACE_INSTANT("Test1"); |
| PW_TRACE_INSTANT("Test2"); |
| PW_TRACE_INSTANT("Test3"); |
| |
| // Check results |
| EXPECT_TRACE(test_interface, PW_TRACE_TYPE_INSTANT, "Test1"); |
| EXPECT_TRACE(test_interface, PW_TRACE_TYPE_INSTANT, "Test2"); |
| EXPECT_TRUE(test_interface.GetEvents().empty()); |
| } |
| |
| TEST(TokenizedTrace, Scope) { |
| TraceTestInterface test_interface; |
| |
| { PW_TRACE_SCOPE("scoped trace"); } |
| |
| // Check results |
| EXPECT_TRACE(test_interface, PW_TRACE_TYPE_DURATION_START, "scoped trace"); |
| EXPECT_TRACE(test_interface, PW_TRACE_TYPE_DURATION_END, "scoped trace"); |
| EXPECT_TRUE(test_interface.GetEvents().empty()); |
| } |
| |
| TEST(TokenizedTrace, ScopeGroup) { |
| TraceTestInterface test_interface; |
| |
| { PW_TRACE_SCOPE("scoped group trace", "group"); } |
| |
| // Check results |
| EXPECT_TRACE(test_interface, |
| PW_TRACE_TYPE_DURATION_GROUP_START, |
| "scoped group trace", |
| "group"); |
| EXPECT_TRACE(test_interface, |
| PW_TRACE_TYPE_DURATION_GROUP_END, |
| "scoped group trace", |
| "group"); |
| EXPECT_TRUE(test_interface.GetEvents().empty()); |
| } |
| |
| TEST(TokenizedTrace, ScopeLoop) { |
| TraceTestInterface test_interface; |
| |
| for (uint32_t i = 0; i < 10; i++) { |
| PW_TRACE_SCOPE("scoped loop", "group", i); |
| } |
| // Check results |
| for (uint32_t i = 0; i < 10; i++) { |
| EXPECT_TRACE( |
| test_interface, PW_TRACE_TYPE_ASYNC_START, "scoped loop", "group", i); |
| EXPECT_TRACE( |
| test_interface, PW_TRACE_TYPE_ASYNC_END, "scoped loop", "group", i); |
| } |
| EXPECT_TRUE(test_interface.GetEvents().empty()); |
| } |
| |
| TEST(TokenizedTrace, Function) { |
| TraceTestInterface test_interface; |
| |
| TraceFunction(); |
| |
| // Check results |
| EXPECT_TRACE( |
| test_interface, |
| PW_TRACE_TYPE_DURATION_START, |
| PW_TRACE_FUNCTION_LABEL_FILE_LINE(__FILE__, TRACE_FUNCTION_LINE)); |
| EXPECT_TRACE( |
| test_interface, |
| PW_TRACE_TYPE_DURATION_END, |
| PW_TRACE_FUNCTION_LABEL_FILE_LINE(__FILE__, TRACE_FUNCTION_LINE)); |
| EXPECT_TRUE(test_interface.GetEvents().empty()); |
| } |
| |
| TEST(TokenizedTrace, FunctionGroup) { |
| TraceTestInterface test_interface; |
| |
| TraceFunctionGroup(); |
| |
| // Check results |
| EXPECT_TRACE( |
| test_interface, |
| PW_TRACE_TYPE_DURATION_GROUP_START, |
| PW_TRACE_FUNCTION_LABEL_FILE_LINE(__FILE__, TRACE_FUNCTION_GROUP_LINE), |
| "FunctionGroup"); |
| EXPECT_TRACE( |
| test_interface, |
| PW_TRACE_TYPE_DURATION_GROUP_END, |
| PW_TRACE_FUNCTION_LABEL_FILE_LINE(__FILE__, TRACE_FUNCTION_GROUP_LINE), |
| "FunctionGroup"); |
| EXPECT_TRUE(test_interface.GetEvents().empty()); |
| } |
| |
| TEST(TokenizedTrace, FunctionTraceId) { |
| TraceTestInterface test_interface; |
| static constexpr uint32_t kTraceId = 5; |
| TraceFunctionTraceId(kTraceId); |
| |
| // Check results |
| EXPECT_TRACE( |
| test_interface, |
| PW_TRACE_TYPE_ASYNC_START, |
| PW_TRACE_FUNCTION_LABEL_FILE_LINE(__FILE__, TRACE_FUNCTION_ID_LINE), |
| "FunctionGroup", |
| kTraceId); |
| EXPECT_TRACE( |
| test_interface, |
| PW_TRACE_TYPE_ASYNC_END, |
| PW_TRACE_FUNCTION_LABEL_FILE_LINE(__FILE__, TRACE_FUNCTION_ID_LINE), |
| "FunctionGroup", |
| kTraceId); |
| EXPECT_TRUE(test_interface.GetEvents().empty()); |
| } |
| |
| TEST(TokenizedTrace, Data) { |
| TraceTestInterface test_interface; |
| int value = 5; |
| PW_TRACE_INSTANT_DATA("label", "i", &value, sizeof(value)); |
| // Check results |
| EXPECT_TRACE_DATA(test_interface, |
| PW_TRACE_TYPE_INSTANT, |
| "label", |
| "i"); // TODO(rgoliver): check data |
| EXPECT_TRUE(test_interface.GetEvents().empty()); |
| } |
| |
| // Create some helper macros that generated some test trace data based from a |
| // number, and can check that it is correct. |
| constexpr std::byte kTestData[] = { |
| std::byte{0}, std::byte{1}, std::byte{2}, std::byte{3}, std::byte{4}}; |
| #define QUEUE_TESTS_ARGS(num) \ |
| (num), static_cast<pw_trace_EventType>((num) % 10), \ |
| "module_" PW_STRINGIFY(num), (num), (num), kTestData, \ |
| (num) % PW_ARRAY_SIZE(kTestData) |
| #define QUEUE_CHECK_RESULT(queue_size, result, num) \ |
| result && ((result->trace_token) == (num)) && \ |
| ((result->event_type) == static_cast<pw_trace_EventType>((num) % 10)) && \ |
| (strncmp(result->module, \ |
| "module_" PW_STRINGIFY(num), \ |
| strlen("module_" PW_STRINGIFY(num))) == 0) && \ |
| ((result->trace_id) == (num)) && ((result->flags) == (num)) && \ |
| (memcmp(const_cast<const pw::trace::internal::TraceQueue< \ |
| queue_size>::QueueEventBlock*>(result) \ |
| ->data_buffer, \ |
| kTestData, \ |
| result->data_size) == 0) && \ |
| (result->data_size == (num) % PW_ARRAY_SIZE(kTestData)) |
| |
| TEST(TokenizedTrace, QueueSimple) { |
| constexpr size_t kQueueSize = 5; |
| pw::trace::internal::TraceQueue<kQueueSize> queue; |
| constexpr size_t kTestNum = 1; |
| queue.TryPushBack(QUEUE_TESTS_ARGS(kTestNum)) |
| .IgnoreError(); // TODO(pwbug/387): Handle Status properly |
| EXPECT_FALSE(queue.IsEmpty()); |
| EXPECT_FALSE(queue.IsFull()); |
| EXPECT_TRUE(QUEUE_CHECK_RESULT(kQueueSize, queue.PeekFront(), kTestNum)); |
| queue.PopFront(); |
| EXPECT_TRUE(queue.IsEmpty()); |
| EXPECT_TRUE(queue.PeekFront() == nullptr); |
| EXPECT_FALSE(queue.IsFull()); |
| } |
| |
| TEST(TokenizedTrace, QueueFull) { |
| constexpr size_t kQueueSize = 5; |
| pw::trace::internal::TraceQueue<kQueueSize> queue; |
| for (size_t i = 0; i < kQueueSize; i++) { |
| EXPECT_EQ(queue.TryPushBack(QUEUE_TESTS_ARGS(i)), pw::OkStatus()); |
| } |
| EXPECT_FALSE(queue.IsEmpty()); |
| EXPECT_TRUE(queue.IsFull()); |
| EXPECT_EQ(queue.TryPushBack(QUEUE_TESTS_ARGS(1)), |
| pw::Status::ResourceExhausted()); |
| |
| for (size_t i = 0; i < kQueueSize; i++) { |
| EXPECT_TRUE(QUEUE_CHECK_RESULT(kQueueSize, queue.PeekFront(), i)); |
| queue.PopFront(); |
| } |
| EXPECT_TRUE(queue.IsEmpty()); |
| EXPECT_TRUE(queue.PeekFront() == nullptr); |
| EXPECT_FALSE(queue.IsFull()); |
| } |
| |
| TEST(TokenizedTrace, Clear) { |
| constexpr size_t kQueueSize = 5; |
| pw::trace::internal::TraceQueue<kQueueSize> queue; |
| for (size_t i = 0; i < kQueueSize; i++) { |
| EXPECT_EQ(queue.TryPushBack(QUEUE_TESTS_ARGS(i)), pw::OkStatus()); |
| } |
| EXPECT_FALSE(queue.IsEmpty()); |
| EXPECT_TRUE(queue.IsFull()); |
| queue.Clear(); |
| EXPECT_TRUE(queue.IsEmpty()); |
| EXPECT_TRUE(queue.PeekFront() == nullptr); |
| EXPECT_FALSE(queue.IsFull()); |
| } |