2 * SPDX-License-Identifier: GPL-2.0-only
4 * Copyright (C) 2024 EfficiOS Inc.
7 #include "cpp-common/bt2/component-class-dev.hpp"
8 #include "cpp-common/bt2/component-class.hpp"
9 #include "cpp-common/bt2/error.hpp"
10 #include "cpp-common/bt2/graph.hpp"
11 #include "cpp-common/bt2/plugin-load.hpp"
12 #include "cpp-common/bt2c/call.hpp"
13 #include "cpp-common/vendor/fmt/format.h" /* IWYU pragma: keep */
19 /* The types of messages a `TestSourceIter` is instructed to send. */
22 /* Send stream beginning and stream end messages. */
25 /* Send a message iterator inactivity message. */
29 __attribute__((used
)) const char *format_as(MsgType msgType
) noexcept
33 return "stream beginning/end";
35 case MsgType::MSG_ITER_INACTIVITY
:
36 return "message iterator inactivity";
42 using CreateClockClass
= bt2::ClockClass::Shared (*)(bt2::SelfComponent
);
44 struct TestSourceData final
46 /* The function to call to obtain a clock class. */
47 CreateClockClass createClockClass
;
49 /* The type of messages to send. */
52 /* If not empty, the clock snapshot to set on the message. */
53 bt2s::optional
<std::uint64_t> clockSnapshot
;
58 class TestSourceIter final
: public bt2::UserMessageIterator
<TestSourceIter
, TestSource
>
60 friend bt2::UserMessageIterator
<TestSourceIter
, TestSource
>;
63 explicit TestSourceIter(const bt2::SelfMessageIterator self
,
64 bt2::SelfMessageIteratorConfiguration
,
65 const bt2::SelfComponentOutputPort port
) :
66 bt2::UserMessageIterator
<TestSourceIter
, TestSource
> {self
, "TEST-SRC-MSG-ITER"},
67 _mData
{&port
.data
<const TestSourceData
>()}, _mSelf
{self
}
72 void _next(bt2::ConstMessageArray
& msgs
)
78 const auto clockCls
= _mData
->createClockClass(_mSelf
.component());
80 switch (_mData
->msgType
) {
83 const auto traceCls
= _mSelf
.component().createTraceClass();
84 const auto streamCls
= traceCls
->createStreamClass();
87 streamCls
->defaultClockClass(*clockCls
);
90 const auto stream
= streamCls
->instantiate(*traceCls
->instantiate());
92 /* Create stream beginning message. */
93 msgs
.append(bt2c::call([&] {
94 const auto streamBeginningMsg
= this->_createStreamBeginningMessage(*stream
);
96 /* Set clock snapshot if instructed to. */
97 if (_mData
->clockSnapshot
) {
98 streamBeginningMsg
->defaultClockSnapshot(*_mData
->clockSnapshot
);
101 return streamBeginningMsg
;
105 * The iterator needs to send a stream end message to avoid
106 * a postcondition assertion failure, where it's ended but
107 * didn't end all streams.
109 * The stream end messages don't play a role in the test
112 msgs
.append(this->_createStreamEndMessage(*stream
));
116 case MsgType::MSG_ITER_INACTIVITY
:
118 this->_createMessageIteratorInactivityMessage(*clockCls
, *_mData
->clockSnapshot
));
126 const TestSourceData
*_mData
;
127 bt2::SelfMessageIterator _mSelf
;
130 class TestSource final
: public bt2::UserSourceComponent
<TestSource
, TestSourceIter
, TestSourceData
>
132 friend class TestSourceIter
;
134 using _ThisUserSourceComponent
=
135 bt2::UserSourceComponent
<TestSource
, TestSourceIter
, TestSourceData
>;
138 static constexpr auto name
= "test-source";
140 explicit TestSource(const bt2::SelfSourceComponent self
, bt2::ConstMapValue
,
141 TestSourceData
* const data
) :
142 _ThisUserSourceComponent
{self
, "TEST-SRC"},
145 this->_addOutputPort("out", _mData
);
149 TestSourceData _mData
;
152 class ErrorTestCase final
155 /* Intentionally not explicit */
156 ErrorTestCase(CreateClockClass createClockClass1Param
, CreateClockClass createClockClass2Param
,
157 const char * const testName
, const char * const expectedCauseMsg
) :
158 _mCreateClockClass1
{createClockClass1Param
},
159 _mCreateClockClass2
{createClockClass2Param
}, _mTestName
{testName
}, _mExpectedCauseMsg
{
164 void run() const noexcept
;
167 void _runOne(MsgType msgType1
, MsgType msgType2
) const noexcept
;
169 CreateClockClass _mCreateClockClass1
;
170 CreateClockClass _mCreateClockClass2
;
171 const char *_mTestName
;
172 const char *_mExpectedCauseMsg
;
175 bt2::ClockClass::Shared
noClockClass(bt2::SelfComponent
) noexcept
177 return bt2::ClockClass::Shared
{};
180 void ErrorTestCase::run() const noexcept
182 static constexpr std::array
<MsgType
, 2> msgTypes
{
184 MsgType::MSG_ITER_INACTIVITY
,
187 for (const auto msgType1
: msgTypes
) {
188 for (const auto msgType2
: msgTypes
) {
190 * It's not possible to create message iterator inactivity
191 * messages without a clock class. Skip those cases.
193 if ((msgType1
== MsgType::MSG_ITER_INACTIVITY
&& _mCreateClockClass1
== noClockClass
) ||
194 (msgType2
== MsgType::MSG_ITER_INACTIVITY
&& _mCreateClockClass2
== noClockClass
)) {
199 * The test scenarios depend on the message with the first
200 * clock class going through the muxer first.
202 * Between a message with a clock snapshot and a message
203 * without a clock snapshot, the muxer always picks the
204 * message without a clock snapshot first.
206 * Message iterator inactivity messages always have a clock
207 * snapshot. Therefore, if:
210 * A message iterator inactivity message (always has a
214 * Doesn't have a clock class (never has a clock
217 * Then there's no way for the first message to go through
222 if (msgType1
== MsgType::MSG_ITER_INACTIVITY
&& _mCreateClockClass2
== noClockClass
) {
226 this->_runOne(msgType1
, msgType2
);
231 std::string
makeSpecTestName(const char * const testName
, const MsgType msgType1
,
232 const MsgType msgType2
)
234 return fmt::format("{} ({}, {})", testName
, msgType1
, msgType2
);
237 void ErrorTestCase::_runOne(const MsgType msgType1
, const MsgType msgType2
) const noexcept
239 const auto specTestName
= makeSpecTestName(_mTestName
, msgType1
, msgType2
);
240 const auto srcCompCls
= bt2::SourceComponentClass::create
<TestSource
>();
241 const auto graph
= bt2::Graph::create(0);
245 * The test scenarios depend on the message with the first clock class going through the
246 * muxer first. Between a message with a clock snapshot and a message without a clock
247 * snapshot, the muxer always picks the message without a clock snapshot first.
249 * Therefore, for the first message, only set a clock snapshot when absolutely necessary,
250 * that is when the message type is "message iterator inactivity".
252 * For the second message, always set a clock snapshot when possible, that is when a clock
253 * class is defined for that message.
255 const auto srcComp1
=
256 graph
->addComponent(*srcCompCls
, "source-1",
257 TestSourceData
{_mCreateClockClass1
, msgType1
,
258 msgType1
== MsgType::MSG_ITER_INACTIVITY
?
259 bt2s::optional
<std::uint64_t> {10} :
261 const auto srcComp2
=
262 graph
->addComponent(*srcCompCls
, "source-2",
263 TestSourceData
{_mCreateClockClass2
, msgType2
,
264 _mCreateClockClass2
!= noClockClass
?
265 bt2s::optional
<std::uint64_t> {20} :
268 const auto utilsPlugin
= bt2::findPlugin("utils");
270 BT_ASSERT(utilsPlugin
);
272 /* Add muxer component */
273 const auto muxerComp
= bt2c::call([&] {
274 const auto muxerCompCls
= utilsPlugin
->filterComponentClasses()["muxer"];
276 BT_ASSERT(muxerCompCls
);
278 return graph
->addComponent(*muxerCompCls
, "the-muxer");
281 /* Add dummy sink component */
282 const auto sinkComp
= bt2c::call([&] {
283 const auto dummySinkCompCls
= utilsPlugin
->sinkComponentClasses()["dummy"];
285 BT_ASSERT(dummySinkCompCls
);
287 return graph
->addComponent(*dummySinkCompCls
, "the-sink");
291 graph
->connectPorts(*srcComp1
.outputPorts()["out"], *muxerComp
.inputPorts()["in0"]);
292 graph
->connectPorts(*srcComp2
.outputPorts()["out"], *muxerComp
.inputPorts()["in1"]);
293 graph
->connectPorts(*muxerComp
.outputPorts()["out"], *sinkComp
.inputPorts()["in"]);
296 const auto thrown
= bt2c::call([&graph
] {
299 } catch (const bt2::Error
&) {
306 ok(thrown
, "%s - `bt2::Error` thrown", specTestName
.c_str());
308 const auto error
= bt2::takeCurrentThreadError();
310 ok(error
, "%s - current thread has an error", specTestName
.c_str());
311 ok(error
.length() > 0, "%s - error has at least one cause", specTestName
.c_str());
313 const auto cause
= error
[0];
315 if (!ok(cause
.message().startsWith(_mExpectedCauseMsg
), "%s - cause's message is expected",
316 specTestName
.c_str())) {
317 diag("expected: %s", _mExpectedCauseMsg
);
318 diag("actual: %s", cause
.message().data());
321 ok(cause
.actorTypeIsMessageIterator(), "%s - cause's actor type is message iterator",
322 specTestName
.c_str());
323 ok(cause
.asMessageIterator().componentName() == "the-muxer",
324 "%s - causes's component name is `the-muxer`", specTestName
.c_str());
327 const bt2c::Uuid uuidA
{"f00aaf65-ebec-4eeb-85b2-fc255cf1aa8a"};
328 const bt2c::Uuid uuidB
{"03482981-a77b-4d7b-94c4-592bf9e91785"};
330 const ErrorTestCase errorTestCases
[] = {
332 [](const bt2::SelfComponent self
) {
333 return self
.createClockClass();
335 "no clock class followed by clock class", "Expecting no clock class, but got one"},
337 {[](const bt2::SelfComponent self
) {
338 return self
.createClockClass();
340 noClockClass
, "clock class with Unix epoch origin followed by no clock class",
341 "Expecting a clock class, but got none"},
343 {[](const bt2::SelfComponent self
) {
344 return self
.createClockClass();
346 [](const bt2::SelfComponent self
) {
347 const auto clockCls
= self
.createClockClass();
349 clockCls
->originIsUnixEpoch(false);
352 "clock class with Unix epoch origin followed by clock class with other origin",
353 "Expecting a clock class having a Unix epoch origin, but got one not having a Unix epoch origin"},
355 {[](const bt2::SelfComponent self
) {
356 const auto clockCls
= self
.createClockClass();
358 clockCls
->originIsUnixEpoch(false).uuid(uuidA
);
361 noClockClass
, "clock class with other origin and a UUID followed by no clock class",
362 "Expecting a clock class, but got none"},
364 {[](const bt2::SelfComponent self
) {
365 const auto clockCls
= self
.createClockClass();
367 clockCls
->originIsUnixEpoch(false).uuid(uuidA
);
370 [](const bt2::SelfComponent self
) {
371 return self
.createClockClass();
373 "clock class with other origin and a UUID followed by clock class with Unix epoch origin",
374 "Expecting a clock class not having a Unix epoch origin, but got one having a Unix epoch origin"},
376 {[](const bt2::SelfComponent self
) {
377 const auto clockCls
= self
.createClockClass();
379 clockCls
->originIsUnixEpoch(false).uuid(uuidA
);
382 [](const bt2::SelfComponent self
) {
383 const auto clockCls
= self
.createClockClass();
385 clockCls
->originIsUnixEpoch(false);
388 "clock class with other origin and a UUID followed by clock class with other origin and no UUID",
389 "Expecting a clock class with a UUID, but got one without a UUID"},
391 {[](const bt2::SelfComponent self
) {
392 const auto clockCls
= self
.createClockClass();
394 clockCls
->originIsUnixEpoch(false).uuid(uuidA
);
397 [](const bt2::SelfComponent self
) {
398 const auto clockCls
= self
.createClockClass();
400 clockCls
->originIsUnixEpoch(false).uuid(uuidB
);
403 "clock class with other origin and a UUID followed by clock class with other origin and another UUID",
404 "Expecting a clock class with a specific UUID, but got one with a different UUID"},
406 {[](const bt2::SelfComponent self
) {
407 const auto clockCls
= self
.createClockClass();
409 clockCls
->originIsUnixEpoch(false);
412 noClockClass
, "clock class with other origin and no UUID followed by no clock class",
413 "Expecting a clock class, but got none"},
415 {[](const bt2::SelfComponent self
) {
416 const auto clockCls
= self
.createClockClass();
418 clockCls
->originIsUnixEpoch(false);
422 [](const bt2::SelfComponent self
) {
423 const auto clockCls
= self
.createClockClass();
425 clockCls
->originIsUnixEpoch(false);
428 "clock class with other origin and no UUID followed by different clock class",
429 "Unexpected clock class"},
438 for (auto& errorTestCase
: errorTestCases
) {
442 return exit_status();