SObjectizer  5.8
Loading...
Searching...
No Matches
experimental/testing/v1/all.hpp
Go to the documentation of this file.
1/*
2 * SObjectizer-5
3 */
4
5/*!
6 * \file
7 * \brief Testing related stuff.
8 *
9 * \since v.5.5.24
10 */
11
12#pragma once
13
14#include <so_5/all.hpp>
15
16#include <sstream>
17
18#if defined( SO_5_MSVC )
19 #pragma warning(push)
20 #pragma warning(disable: 4251)
21#endif
22
23namespace so_5 {
24
25namespace experimental {
26
27namespace testing {
28
29inline namespace v1 {
30
31namespace details {
32
33//
34// Forward declarations
35//
38class scenario_in_progress_accessor_t;
39
40/*!
41 * \brief A description of an event for testing scenario.
42 *
43 * Instances of that type will be passed to various hooks of
44 * testing scenario and scenario's steps.
45 *
46 * \since v.5.5.24
47 */
48struct incident_info_t final
49 {
50 //! Target of an event.
52 //! Type of message or signal.
54 //! ID of mbox from that message/signal was received.
55 mbox_id_t m_src_mbox_id;
56
58 const agent_t * agent,
59 const std::type_index & msg_type,
60 mbox_id_t src_mbox_id )
61 : m_agent( agent )
63 , m_src_mbox_id( src_mbox_id )
64 {}
65 };
66
67/*!
68 * \brief What happened with source of an event.
69 *
70 * When a message or signal is delivered to an agent that message/signal
71 * can be either handled or ignored. Some scenario triggers are activated
72 * when source message/signal is handled, some are activated when incident
73 * is ignored. This enumeration can be used for selection of that cases.
74 *
75 * \since v.5.5.24
76 */
78 {
79 //! Message or signal has been handled.
80 handled,
81 //! Message or signal has been ignored.
83 };
84
85/*!
86 * \brief Description of context on that a trigger is completed.
87 *
88 * Some triggers should do some actions on completion. Because
89 * of that triggers should have access to scenario's step or even to
90 * the whole scenario. This type holds a reference to objects those
91 * can be accessible to a trigger.
92 *
93 * \note
94 * The references inside that object are valid only for small amount
95 * of time. They shouldn't be used after completion of trigger.
96 *
97 * \since v.5.5.24
98 */
100 {
101 const scenario_in_progress_accessor_t & m_scenario_accessor;
103 };
104
105/*!
106 * \brief Description of context on that an attempt to activate a trigger
107 * is performing.
108 *
109 * Some triggers should do some actions on activation. Because
110 * of that triggers should have access to scenario's step or even to
111 * the whole scenario. This type holds a reference to objects those
112 * can be accessible to a trigger.
113 *
114 * \note
115 * The references inside that object are valid only for small amount
116 * of time. They shouldn't be used after completion of trigger.
117 *
118 * \since v.5.8.3
119 */
121 {
122 //! Access to the running scenario.
123 const scenario_in_progress_accessor_t & m_scenario_accessor;
124
125 //! The current step for that activation is being performed.
127
128 //! Incoming message or signal.
129 /*!
130 * \note
131 * This will be a nullptr in case of a signal.
132 */
133 const message_ref_t & m_incoming_msg;
134 };
135
136/*!
137 * \brief An implementation of trigger for scenario's step.
138 *
139 * In the current version trigger is implemented as a concrete class (just for
140 * simplicity), but in the future versions it could be an abstract interface.
141 *
142 * \since v.5.5.24
143 */
144class SO_5_TYPE trigger_t final
145 {
146 public :
147 //! Type of functor to be used on the completion of the trigger.
148 using completion_function_t = std::function<
149 void(const trigger_completion_context_t &) /*noexcept*/ >;
150
151 //! Type of functor to be used on the activation of the trigger.
152 /*!
153 * \since v.5.8.3
154 */
155 using activation_function_t = std::function<
156 void(const trigger_activation_context_t &) /*noexcept*/ >;
157
158 private :
159 //! What should happen with initial message/signal.
161 //! A reference to the target agent.
162 /*!
163 * Note that this reference should be used with care.
164 * In complex scenarios an agent can be deregistered and
165 * this reference can point to free memory or reallocated
166 * for another agent memory.
167 *
168 * Before access to that reference it is necessary to check
169 * m_target_id field.
170 */
172 //! The unique ID of target's direct mbox.
173 /*!
174 * ID of mbox is a unique value. ID is not reused even if
175 * the agent is destroyed and its memory is reallocated for
176 * another agent.
177 */
178 const mbox_id_t m_target_id;
179 //! Type of message/signal to activate the trigger.
181 //! ID of source mbox of message/signal to activate the trigger.
182 const mbox_id_t m_src_mbox_id;
183
184 //! Optional function for completion of the trigger.
185 /*!
186 * If m_completion is empty then there is no need to
187 * separate completion action for the trigger and trigger
188 * become completed just after activation.
189 */
191
192 //! Optional function for activation of the trigger.
193 /*!
194 * \since v.5.8.3
195 */
197
198 // Note. These are required by Clang.
199 trigger_t( const trigger_t & ) = delete;
200 trigger_t( trigger_t && ) = delete;
201
202 public :
203 //! Initializing constructor.
204 trigger_t(
205 incident_status_t incident_status,
206 const agent_t & target,
207 std::type_index msg_type,
208 mbox_id_t src_mbox_id );
209 ~trigger_t();
210
211 //! Get the reference of the target agent.
212 /*!
213 * \attention
214 * This method should be used with care because if target agent
215 * is deregistered then a dangling reference will be returned.
216 */
217 [[nodiscard]]
218 const agent_t &
219 target_agent() const noexcept;
220
221 //! Setter for completion function.
222 /*!
223 * @note
224 * If a completon function is already set then the old and new functions
225 * are joined. It means that the old function will be called first and
226 * then the new function.
227 */
228 void
229 set_completion( completion_function_t fn );
230
231 //! Setter for activation function.
232 /*!
233 * \note
234 * If an activation function is already set then the old and new functions
235 * are joined. It means that the old function will be called first and
236 * then the new function.
237 *
238 * \since v.5.8.3
239 */
240 void
241 set_activation( activation_function_t fn );
242
243 //! Check for activation of the trigger.
244 /*!
245 * \retval true Trigger is activated.
246 * \retval false Trigger is not activated and information about
247 * the event can be used for checking for other triggers.
248 */
249 [[nodiscard]]
250 bool
251 check(
252 //! What happened with message/signal?
253 const incident_status_t incident_status,
254 //! Informationa about the incident.
255 const incident_info_t & info ) const noexcept;
256
257 //! Does this trigger require separate completion action?
258 [[nodiscard]]
259 bool
260 requires_completion() const noexcept;
261
262 //! Do activation of the trigger.
263 /*!
264 * If there is an activation function it will be called.
265 *
266 * \since v.5.8.3
267 */
268 void
269 activate(
270 const trigger_activation_context_t & context ) noexcept;
271
272 //! Do completion of a trigger.
273 void
274 complete(
275 const trigger_completion_context_t & context ) noexcept;
276 };
277
278/*!
279 * \brief An alias for unique_ptr of trigger.
280 *
281 * \since v.5.5.24
282 */
284
285/*!
286 * \brief An alias for type of tigger's container.
287 *
288 * \since v.5.5.24
289 */
291
292/*!
293 * \brief A special data class with partial info for a new trigger.
294 *
295 * This data class contains a type of message/signal and optional
296 * mbox_id for source mbox. If mbox_id is not specified then the direct
297 * mbox of a target agent will be used as source mbox.
298 *
299 * \since v.5.5.24
300 */
301template< incident_status_t Status >
302struct trigger_source_t final
303 {
306
308 std::type_index msg_type,
309 mbox_id_t src_mbox_id)
310 : m_msg_type( std::move(msg_type) )
311 , m_src_mbox_id( src_mbox_id )
312 {}
313
315 std::type_index msg_type )
316 : m_msg_type( std::move(msg_type) )
317 {}
318 };
319
320/*!
321 * \brief A special data object for case of store-state-name completion action.
322 *
323 * \since v.5.5.24
324 */
326 {
327 //! Name of tag for store-state-name action.
329 };
330
331/*!
332 * \brief A special data object for case when a step should be completed only
333 * after returning from the event handler.
334 *
335 * \since v.5.8.3
336 */
338 {
339 // Yes, it's an empty type.
340 };
341
342/*!
343 * \brief A special data object for case when a message inspector has
344 * to be used on an incoming message.
345 *
346 * \since v.5.8.3
347 */
349 {
350 //! Name of a tag for store-msg-inspection action.
352
353 //! Inspector for a message.
355 };
356
357/*!
358 * \brief An interface of step's constraints.
359 *
360 * \since v.5.5.24
361 */
363 {
364 public :
365 constraint_t() = default;
366 virtual ~constraint_t() noexcept = default;
367
368 constraint_t( const constraint_t & ) = delete;
369 constraint_t & operator=( const constraint_t & ) = delete;
370
373
374 //! Hook for step preactivation.
375 /*!
376 * This hook will be called when step is preactivated.
377 * Constraint object can do some initial actions here. For
378 * example the current timestamp can be store. Or some resources
379 * can be acquired.
380 */
381 virtual void
382 start() noexcept = 0;
383
384 //! Hook for step completion.
385 /*!
386 * This hook will be called when step is completed.
387 * Constraint object can do some cleanup actions here. For example
388 * resources acquired in start() method can be released.
389 */
390 virtual void
391 finish() noexcept = 0;
392
393 //! Check for fulfillment of constraint.
394 /*!
395 * \retval true If constraint fulfilled.
396 * \retval false If constraint is not fulfilled and an incident
397 * should be ignored.
398 */
399 [[nodiscard]]
400 virtual bool
402 //! What happened with message/signal?
403 const incident_status_t incident_status,
404 //! Context for that event.
405 const incident_info_t & info ) const noexcept = 0;
406 };
407
408/*!
409 * \brief An alias for unique_ptr of constraint.
410 *
411 * \since v.5.5.24
412 */
414
415/*!
416 * \brief An alias for container of constraints.
417 *
418 * \since v.5.5.24
419 */
421
422/*!
423 * \brief Implementation of 'not_before' constraint.
424 *
425 * \since v.5.5.24
426 */
427class not_before_constraint_t final
428 : public constraint_t
429 {
430 //! Value to be used.
432
433 //! Time point of step preactivation.
434 /*!
435 * Receives value only in start() method.
436 */
438
439 public :
441 std::chrono::steady_clock::duration pause )
442 : m_pause( pause )
443 {}
444
445 void
446 start() noexcept override
447 {
448 m_started_at = std::chrono::steady_clock::now();
449 }
450
451 void
452 finish() noexcept override { /* nothing to do */ }
453
454 [[nodiscard]]
455 bool
457 const incident_status_t /*incident_status*/,
458 const incident_info_t & /*info*/ ) const noexcept override
459 {
460 return m_started_at + m_pause <=
461 std::chrono::steady_clock::now();
462 }
463 };
464
465/*!
466 * \brief Implementation of 'not_after' constraint.
467 *
468 * \since v.5.5.24
469 */
470class not_after_constraint_t final
471 : public constraint_t
472 {
473 //! Value to be used.
475
476 //! Time point of step preactivation.
477 /*!
478 * Receives value only in start() method.
479 */
481
482 public :
484 std::chrono::steady_clock::duration pause )
485 : m_pause( pause )
486 {}
487
488 void
489 start() noexcept override
490 {
491 m_started_at = std::chrono::steady_clock::now();
492 }
493
494 void
495 finish() noexcept override { /* nothing to do */ }
496
497 [[nodiscard]]
498 bool
500 const incident_status_t /*incident_status*/,
501 const incident_info_t & /*info*/ ) const noexcept override
502 {
503 return m_started_at + m_pause >
504 std::chrono::steady_clock::now();
505 }
506 };
507
508/*!
509 * \brief An alias for type of step's preactivation action.
510 *
511 * \since v.5.5.24
512 */
513using preactivate_action_t = std::function< void() /*noexcept*/ >;
514
515/*!
516 * \brief An interface of testing scenario step.
517 *
518 * This interface is described in a public header file just for
519 * definition of step_definition_proxy_t class. But abstract_scenario_step_t
520 * is an internal and implementation-specific type, please don't use
521 * it in end-user code.
522 *
523 * \since v.5.5.24
524 */
526 {
527 public :
528 //! Status of step.
529 enum class status_t
530 {
531 //! Step is not preactivated yet.
532 //! Step can be changed while it in this state.
533 passive,
534 //! Step is preactivated.
535 //! It means that this step is the current step in testing
536 //! scenario now. But not all required triggers are activated
537 //! yet.
539 //! Step is activated.
540 //! It means that all required triggers are activated, but
541 //! some triggers wait for completion actions.
542 active,
543 //! Step is completed.
544 //! It means that all required triggers were activated and
545 //! all of them were completed.
547 };
548
549 /*!
550 * \brief Type of token returned from pre-handler-hook.
551 *
552 * This token can be in valid or invalid states. If it is in
553 * valid state then it should be passed as is to
554 * abstract_scenario_step_t::post_handler_hook() method.
555 *
556 * \since v.5.5.24
557 */
558 class token_t final
559 {
560 //! Activated trigger.
561 /*!
562 * Can be null. It means that there was no activated triggers
563 * at pre_handler_hook() method. And token is in invalid state.
564 *
565 * This pointer can also be null if trigger was activated but
566 * wasn't require completion. It means that trigger switched
567 * to completed state and there is no need in separate
568 * completion action.
569 *
570 * If this pointer is not null then post_handler_hook()
571 * should be called for scenario step.
572 */
573 trigger_t * m_trigger;
574
575 public:
576 token_t() noexcept
577 : m_trigger( nullptr )
578 {}
579 token_t( trigger_t * trigger ) noexcept
580 : m_trigger( trigger )
581 {}
582
583 [[nodiscard]]
584 bool
585 valid() const noexcept { return m_trigger != nullptr; }
586
587 //! Get a reference to activated trigger.
588 /*!
589 * This method should be called only if token is in valid
590 * state.
591 */
592 [[nodiscard]]
593 trigger_t &
594 trigger() const noexcept { return *m_trigger; }
595 };
596
597 // Note. These are required by Clang compiler.
603
604 virtual ~abstract_scenario_step_t() = default;
605
606 //! Get the name of the step.
607 [[nodiscard]]
608 virtual const std::string &
609 name() const noexcept = 0;
610
611 //! Perform preactivation of the step.
612 /*!
613 * Preactivation means that the step becomes the current step
614 * of the scenario and all events will go to
615 * pre_handler_hook(), post_handler_hook() and
616 * no_handler_hook() of that step.
617 *
618 * This method should call all preactivation actions passed to
619 * the step via add_peactivate_action() method.
620 */
621 virtual void
622 preactivate() noexcept = 0;
623
624 //! Hook that should be called before invocation of event-handler.
625 /*!
626 * This hook should be called before invocation of any event-handler
627 * for ordinary message, service request or enveloped message.
628 *
629 * The step should update its status inside that method.
630 *
631 * If a valid token is returned then this token should be passed
632 * to subsequent call to post_handler_hook() method.
633 *
634 * \note
635 * The \a incoming_msg may be nullptr in case of a signal.
636 */
637 [[nodiscard]]
638 virtual token_t
640 const scenario_in_progress_accessor_t & scenario_accessor,
641 const incident_info_t & info,
642 const message_ref_t & incoming_msg ) noexcept = 0;
643
644 //! Hook that should be called just after completion of event-handler.
645 /*!
646 * If previous call to pre_handler_hook() returned a valid
647 * token object then this hook should be called just after
648 * completion of event-handler.
649 */
650 virtual void
652 const scenario_in_progress_accessor_t & scenario_accessor,
653 token_t token ) noexcept = 0;
654
655 //! Hook that should be called if there is no event-handler for
656 //! a message or service request.
657 /*!
658 * The step should update its status inside that method.
659 *
660 * \note
661 * The \a incoming_msg may be nullptr in case of a signal.
662 */
663 virtual void
665 const scenario_in_progress_accessor_t & scenario_accessor,
666 const incident_info_t & info,
667 const message_ref_t & incoming_msg ) noexcept = 0;
668
669 //! Get the current status of the step.
670 [[nodiscard]]
671 virtual status_t
672 status() const noexcept = 0;
673
674 //! Add another preactivation action.
675 /*!
676 * Can be called several times. New action will be added to the end
677 * of list of preactivation actions.
678 */
679 virtual void
681 preactivate_action_t action ) = 0;
682
683 //! Setup triggers for the step.
684 /*!
685 * It is assumed that this method will be called only once per step.
686 * If it is called several times the new information will replace
687 * the old one.
688 *
689 * \note
690 * Value of \a triggers_to_activate has sence for case of
691 * step_definition_proxy_t::when_any() method: there can be
692 * several triggers in \a triggers container, but only one of
693 * them has to be triggered to activate the step.
694 */
695 virtual void
697 trigger_container_t triggers,
698 std::size_t triggers_to_activate ) noexcept = 0;
699
700 //! Setup constraints for the step.
701 /*!
702 * It is assumed that this method will be called only once per
703 * step. If it is called several times the new information will
704 * replace the old one.
705 */
706 virtual void
708 constraint_container_t constraints ) noexcept = 0;
709 };
710
711/*!
712 * \brief An alias for unique_ptr of scenario-step.
713 *
714 * \since v.5.5.24
715 */
717
718/*!
719 * \brief A helper class for holding unique_ptr to a trigger while
720 * trigger is being configured.
721 *
722 * \note
723 * This class is Movable, but not Copyable.
724 *
725 * \since v.5.5.24
726 */
727template< incident_status_t Status >
728class trigger_holder_t final
729 {
731
732 public :
733 trigger_holder_t( trigger_unique_ptr_t trigger ) noexcept
734 : m_trigger( std::move(trigger) )
735 {}
736
737 trigger_holder_t( trigger_holder_t && ) noexcept = default;
738 trigger_holder_t & operator=( trigger_holder_t && ) noexcept = default;
739
740 trigger_holder_t( const trigger_holder_t & ) = delete;
741 trigger_holder_t & operator=( const trigger_holder_t & ) = delete;
742
743 //! Get the trigger object from the holder.
744 /*!
745 * \note
746 * Holder becomes empty and should not be used anymore
747 * (except for assigning a new value);
748 */
751 {
752 return std::move(m_trigger);
753 }
754 };
755
756} /* namespace details */
757
758/*!
759 * \brief A special object that should be used for definition of a step
760 * of a testing scenario.
761 *
762 * Usage example:
763 * \code
764 * using namespace so_5::experimental::testing;
765 * testing_env_t env;
766 *
767 * so_5::agent_t * test_agent = ...;
768 * so_5::agent_t * another_agent = ...;
769 *
770 * auto scenario = env.scenario();
771 *
772 * // Create a simple step. It triggers when agent receives a message
773 * // from its direct mbox.
774 * scenario.define_step("one").when(*test_agent & reacts_to<some_message>());
775 *
776 * // Create a step with constraint: a message should be received only
777 * // after a 15ms from preactivation of this step.
778 * scenario.define_step("two")
779 * .constraints(not_before(std::chrono::milliseconds(15)))
780 * .when(*test_agent & reacts_to<another_message>(some_mbox));
781 *
782 * // Create a step with initial actions: several messages will be sent
783 * // during preactivation of this step.
784 * // There are also two constraints: a time range for receiving expected
785 * // message.
786 * scenario.define_step("three")
787 * .impact<first_message>(first_target)
788 * .impact<second_message>(second_target, arg1, arg2, arg3)
789 * .constraints(
790 * not_before(std::chrono::milliseconds(15)),
791 * not_after(std::chrono::seconds(2)))
792 * .when(*test_agent & reacts_to<expected_message>(some_mbox));
793 *
794 * // Create a step that trigges when both agents receive messages.
795 * scenario.define_step("four")
796 * .when_all(
797 * *test_agent & reacts_to<one_message>(some_mbox),
798 * *another_agent & reacts_to<different_message>(another_mbox));
799 *
800 * // Create a step that triggers when one of agents receive a message.
801 * scenario.define_step("five")
802 * .when_any(
803 * *test_agent & reacts_to<one_message>(some_mbox),
804 * *another_agent & reacts_to<different_message>(another_mbox));
805 * \endcode
806 *
807 * \note
808 * The object of this type can be stored to define a step in several
809 * footsteps:
810 * \code
811 * auto step = env.scenario().define_step("my_step");
812 * if(some_condition)
813 * step.constraints(...);
814 * if(another_condition)
815 * step.impact(...);
816 * if(third_condition)
817 * step.when(...);
818 * else
819 * step.when_all(...);
820 * \endcode
821 * But all definitions should be done before calling of
822 * scenario_proxy_t::run_for().
823 *
824 * \attention
825 * This class is not thread-safe. Because of that it should be used on
826 * a context of a signle thread.
827 *
828 * \since v.5.5.24
829 */
831 {
833
834 void
836
837 template<
839 typename... Args >
840 void
842 details::trigger_container_t & to,
843 details::trigger_holder_t<Status> event,
844 Args && ...args )
845 {
848 }
849
850 void
852
853 template< typename... Args >
854 void
856 details::constraint_container_t & to,
857 details::constraint_unique_ptr_t head,
858 Args && ...tail )
859 {
862 }
863
864 public :
865 //! Initializing constructor.
866 /*!
867 * Despite the fact that this is a public constructor
868 * it is a part of SObjectizer implementation and is subject
869 * of change without a notice. Don't use it in end-user code.
870 */
875
876 /*!
877 * \brief Define a preactivation action in form of
878 * sending a message/signal to the specified target.
879 *
880 * This method creates and stores an instance of a message/signal
881 * and then sends this instance when step is preactivated.
882 *
883 * Usage example:
884 * \code
885 * env.scenario().define_step("my_step")
886 * .impact<my_message>(*test_agent, arg1, arg2, arg3)
887 * .impact<my_signal>(some_mbox)
888 * .impact<another_message>(mchain)
889 * ...;
890 * \endcode
891 *
892 * Please note that this method can be called several times.
893 *
894 * \tparam Msg_Type type of a message/signal to send.
895 * \tparam Target type of a target for message/signal.
896 * It can be a reference to mbox, a reference to agent
897 * (agent's direct mbox will be used in that case), or
898 * a reference to mchain.
899 * \tparam Args types of arguments for Msg_Type's constructor.
900 */
901 template<
902 typename Msg_Type,
903 typename Target,
904 typename... Args >
906 impact( Target && target, Args && ...args )
907 {
908 // Deduce actual mbox of the recevier.
909 // This mbox will be captured by lambda function.
912
913 // Make an instance of a message.
914 // This instance will be captured by lambda function.
915 // Mutability of a message will be changed appropriately
916 // in make_message_instance.
919 std::forward<Args>(args)... )
920 };
921
922 // Now we can create a lambda-function that will send
923 // the message instance at the appropriate time.
925 [to = std::move(target_mbox),
926 msg = std::move(msg_instance)]() noexcept {
927 using namespace so_5::low_level_api;
930 *to,
933 std::move(msg) );
934 } );
935
936 return *this;
937 }
938
939 /*!
940 * \brief Add preactivation action in form of lambda-object.
941 *
942 * This method can be used for non-trivial actions on step
943 * preactivation, like sending enveloped messages.
944 *
945 * Usage example:
946 * \code
947 * env.scenario().define_step("my_step")
948 * .impact([some_mbox, some_data] {
949 * some_mbox->do_deliver_enveloped_msg(
950 * so_5::message_payload_type<my_message>::subscription_type_index(),
951 * std::make_unique<my_envelope<my_message>>(some_data),
952 * 1u);
953 * });
954 * \endcode
955 *
956 * Please note that this method can be called several times.
957 *
958 * \note
959 * Preactivation of scenario step is performed when scenario object
960 * is locked. Becase of that actions inside \a lambda must be performed
961 * with care to avoid deadlocks or blocking of scenario for too long.
962 *
963 * \attention
964 * \a lambda should be noexcept-function.
965 */
966 template< typename Lambda >
968 impact( Lambda && lambda )
969 {
971 [lambda]() noexcept { lambda(); } );
972
973 return *this;
974 }
975
976 /*!
977 * \brief Add a tigger for activation of that step
978 *
979 * Step is activated when this trigger is activated.
980 *
981 * Usage example:
982 * \code
983 * env.scenario().define_step("my_step")
984 * .when(some_agent & reacts_to<my_message>());
985 * \endcode
986 *
987 * \note
988 * This method is indended to be called only once.
989 * All subsequent calls to that method will replace triggers
990 * those where set by previous calls to when(), when_all() and
991 * when_any() methods.
992 */
993 template< details::incident_status_t Status >
996 details::trigger_holder_t<Status> event )
997 {
1000
1001 m_step->setup_triggers( std::move(cnt), 1u );
1002
1003 return *this;
1004 }
1005
1006 /*!
1007 * \brief Add a list of tiggers for activation of that step
1008 *
1009 * Step is activated when \b any of those triggers is activated.
1010 *
1011 * Usage example:
1012 * \code
1013 * env.scenario().define_step("my_step")
1014 * .when_any(
1015 * some_agent & reacts_to<my_message>(),
1016 * another_agent & reacts_to<another_message>(),
1017 * yet_another_agent & reacts_to<yet_another_message>());
1018 * \endcode
1019 *
1020 * \note
1021 * This method is indended to be called only once.
1022 * All subsequent calls to that method will replace triggers
1023 * those where set by previous calls to when(), when_all() and
1024 * when_any() methods.
1025 */
1026 template<
1027 details::incident_status_t Status,
1028 typename... Args >
1031 details::trigger_holder_t<Status> event,
1032 Args && ...args )
1033 {
1035 cnt.reserve( 1u + sizeof...(args) );
1036
1038 cnt,
1039 std::move(event),
1040 std::forward<Args>(args)... );
1041
1042 m_step->setup_triggers( std::move(cnt), 1u );
1043
1044 return *this;
1045 }
1046
1047 /*!
1048 * \brief Add a list of tiggers for activation of that step
1049 *
1050 * Step is activated when \a all of those triggers is activated.
1051 *
1052 * Usage example:
1053 * \code
1054 * env.scenario().define_step("my_step")
1055 * .when_all(
1056 * some_agent & reacts_to<my_message>(),
1057 * another_agent & reacts_to<another_message>(),
1058 * yet_another_agent & reacts_to<yet_another_message>());
1059 * \endcode
1060 *
1061 * \note
1062 * This method is indended to be called only once.
1063 * All subsequent calls to that method will replace triggers
1064 * those where set by previous calls to when(), when_all() and
1065 * when_any() methods.
1066 */
1067 template<
1068 details::incident_status_t Status,
1069 typename... Args >
1072 details::trigger_holder_t<Status> event,
1073 Args && ...args )
1074 {
1075 const auto total_triggers = 1u + sizeof...(args);
1076
1079
1081 cnt,
1082 std::move(event),
1083 std::forward<Args>(args)... );
1084
1086
1087 return *this;
1088 }
1089
1090 /*!
1091 * \brief Add a list of constraints for that step
1092 *
1093 * All specified constraints should be fulfilled to enable
1094 * activation of that step.
1095 *
1096 * Usage example:
1097 * \code
1098 * env.scenario().define_step("my_step")
1099 * .constraints(
1100 * not_before(std::chrono::milliseconds(10)))
1101 * ...;
1102 * env.scenario().define_step("another_step")
1103 * .constraints(
1104 * not_after(std::chrono::milliseconds(500),
1105 * not_before(std::chrono::milliseconds(10)))
1106 * ...;
1107 * \endcode
1108 *
1109 * \note
1110 * This method is indended to be called only once.
1111 * All subsequent calls to that method will replace constraints
1112 * those where set by previous calls to constraints().
1113 */
1114 template< typename... Args >
1117 details::constraint_unique_ptr_t head,
1118 Args && ...tail )
1119 {
1121 cnt.reserve( 1u + sizeof...(tail) );
1122
1124 cnt,
1125 std::move(head),
1126 std::forward<Args>(tail)... );
1127
1129
1130 return *this;
1131 }
1132 };
1133
1134/*!
1135 * \brief Status of testing scenario.
1136 *
1137 * This enumeration is used by testing scenario itself and by
1138 * scenario_result_t type.
1139 *
1140 * \since v.5.5.24
1141 */
1143 {
1144 //! Testing scenario is not started yet.
1145 //! New step can be added when scenario is in state.
1147 //! Testing scenario is started but not finished yet.
1148 //! New steps can't be added.
1150 //! Testing scenario is successfuly completed.
1151 completed,
1152 //! Testing scenario is not working any more, but it is not
1153 //! completed becase there is no more time to run the scenario.
1154 timed_out
1155 };
1156
1157/*!
1158 * \brief The result of run of testing scenario.
1159 *
1160 * The result contains the status of the scenario
1161 * (in form of scenario_status_t enumeration) and optional textual
1162 * description of the result.
1163 *
1164 * Note that description is missing if testing scenario completed
1165 * successfuly (it means that scenario has scenario_status_t::completed
1166 * state after completion of scenario_proxy_t::run_for() method).
1167 *
1168 * The content and format of textual description is not specified and
1169 * can be changed in the future versions of SObjectizer.
1170 *
1171 * Usage example:
1172 * \code
1173 * TEST_CASE("some_case") {
1174 * using namespace so_5::experimental::testing;
1175 *
1176 * testing_env_t env;
1177 * ...
1178 * env.scenario().run_for(std::chrono::milliseconds(500));
1179 *
1180 * REQUIRE(completed() == env.scenario().result());
1181 * }
1182 * \endcode
1183 *
1184 * \note
1185 * Object of scenario_result_t type can be dumped to std::ostream.
1186 * For example:
1187 * \code
1188 * auto result = env.scenario().result();
1189 * if(completed() != result)
1190 * std::cout << "The result is: " << result << std::endl;
1191 * \endcode
1192 *
1193 * \since v.5.5.24
1194 */
1196 {
1199
1200 public :
1201 //! The constructor for a case when there is only status of scenario.
1203 scenario_status_t status )
1204 : m_status( status )
1205 {}
1206 //! The constructor for a case when there are status and description
1207 //! of scenario.
1209 scenario_status_t status,
1210 std::string description )
1211 : m_status( status )
1213 {}
1214
1215 //! Check for equality.
1216 /*!
1217 * \note
1218 * Only status is compared.
1219 */
1220 [[nodiscard]]
1221 bool
1222 operator==( const scenario_result_t & o ) const noexcept
1223 {
1224 return m_status == o.m_status;
1225 }
1226
1227 //! Check for inequality.
1228 /*!
1229 * \note
1230 * Only status is compared.
1231 */
1232 [[nodiscard]]
1233 bool
1234 operator!=( const scenario_result_t & o ) const noexcept
1235 {
1236 return m_status != o.m_status;
1237 }
1238
1239 //! Dump of object's content to ostream.
1240 friend std::ostream &
1241 operator<<( std::ostream & to, const scenario_result_t & v )
1242 {
1243 const auto status_name =
1244 [](scenario_status_t st) -> const char * {
1245 const char * result{};
1246 switch( st )
1247 {
1249 result = "not_started";
1250 break;
1252 result = "in_progress";
1253 break;
1255 result = "completed";
1256 break;
1258 result = "timed_out";
1259 break;
1260 }
1261 return result;
1262 };
1263
1264 to << "[" << status_name( v.m_status );
1265 if( v.m_description )
1266 to << ",{" << *v.m_description << "}";
1267 to << "]";
1268
1269 return to;
1270 }
1271 };
1272
1273/*!
1274 * \brief Create a value that means that scenario completed successfuly.
1275 *
1276 * Usage example:
1277 * \code
1278 * TEST_CASE("some_case") {
1279 * using namespace so_5::experimental::testing;
1280 *
1281 * testing_env_t env;
1282 * ...
1283 * env.scenario().run_for(std::chrono::milliseconds(500));
1284 *
1285 * REQUIRE(completed() == env.scenario().result());
1286 * }
1287 * \endcode
1288 *
1289 * \since v.5.5.24
1290 */
1291[[nodiscard]]
1292inline scenario_result_t
1294
1295/*!
1296 * \brief Define a trigger that activates when an agent receives and
1297 * handles a message from the direct mbox.
1298 *
1299 * Usage example:
1300 * \code
1301 * using namespace so_5::experimental::testing;
1302 * ...
1303 * env.scenario().define_step("my_step")
1304 * .when(some_agent & reacts_to<some_message>());
1305 * \endcode
1306 *
1307 * \since v.5.5.24
1308 */
1309template<typename Msg_Type>
1310[[nodiscard]]
1316
1317/*!
1318 * \brief Define a trigger that activates when an agent receives and
1319 * handles a message from the specific mbox.
1320 *
1321 * Usage example:
1322 * \code
1323 * using namespace so_5::experimental::testing;
1324 * ...
1325 * env.scenario().define_step("my_step")
1326 * .when(some_agent & reacts_to<some_message>(some_mbox));
1327 * \endcode
1328 *
1329 * \since v.5.5.24
1330 */
1331template<typename Msg_Type>
1332[[nodiscard]]
1334reacts_to( const so_5::mbox_t & mbox )
1335 {
1336 return {
1338 mbox->id()
1339 };
1340 }
1341
1342/*!
1343 * \brief Create a special marker for a trigger for storing
1344 * agent's state name inside scenario.
1345 *
1346 * Usage example:
1347 * \code
1348 * using namespace so_5::experimental::testing;
1349 * ...
1350 * env.scenario().define_step("my_step")
1351 * .when(some_agent & reacts_to<some_message>() & store_agent_name("my_agent"));
1352 * ...
1353 * env.scenario().run_for(std::chrono::seconds(1));
1354 *
1355 * REQUIRE(completed() == env.scenario().result());
1356 * REQUIRE("some_state" == env.scenario().stored_state_name("my_step", "my_agent"));
1357 * \endcode
1358 *
1359 * \since v.5.5.24
1360 */
1361[[nodiscard]]
1363store_state_name( std::string tag )
1364 {
1365 return { std::move(tag) };
1366 }
1367
1368/*!
1369 * \brief Create a special marker for a trigger for inspecting an
1370 * incoming message and storing the inspection result inside the scenario.
1371 *
1372 * Usage example:
1373 * \code
1374 * using namespace so_5::experimental::testing;
1375 * ...
1376 * env.scenario().define_step("my_step")
1377 * .when(some_agent & reacts_to<some_message>()
1378 * & inspect_msg("msg-check", [](const some_message & msg) -> std::string {
1379 * return msg.some_field == expected_value ? "OK" : "FAILED";
1380 * })
1381 * );
1382 * ...
1383 * env.scenario().run_for(std::chrono::seconds(1));
1384 *
1385 * REQUIRE(completed() == env.scenario().result());
1386 * REQUIRE("OK" == env.scenario().stored_msg_inspection_result("my_step", "msg-check"));
1387 * \endcode
1388 *
1389 * The inspect_msg() can be used for mutable messages too:
1390 *
1391 * \code
1392 * env.scenario().define_step("my_step")
1393 * .when(some_agent & reacts_to<so_5::mutable_msg<some_message>>()
1394 * & inspect_msg("msg-check", [](const some_message & msg) -> std::string {
1395 * return msg.some_field == expected_value ? "OK" : "FAILED";
1396 * })
1397 * );
1398 * \endcode
1399 *
1400 * The inspect_msg() can't be used with signals, because signals have no actual data.
1401 *
1402 * \attention
1403 * The inspect_msg() doesn't check the type of the message. So it's possible to
1404 * make a mistake like this:
1405 * \code
1406 * env.scenario().define_step("my_step")
1407 * .when(some_agent & reacts_to<some_message>()
1408 * & inspect_msg("msg-check", [](const another_msg & msg) -> std::string {
1409 * return msg.some_field == expected_value ? "OK" : "FAILED";
1410 * })
1411 * );
1412 * \endcode
1413 * And this will lead to a run-time error (with possible termination of the
1414 * application).
1415 *
1416 * \since v.5.8.3
1417 */
1418template< typename Lambda >
1419[[nodiscard]]
1422 //! Tag to be used for indentification of the inspection result.
1423 std::string tag,
1424 //! Inspection functor.
1425 Lambda && inspector )
1426 {
1427 using namespace so_5::details::lambda_traits;
1428
1431
1432 // Signal can't be inspected.
1434
1435 return {
1436 std::move(tag),
1438 ( const message_ref_t & message ) -> std::string
1439 {
1440 const argument_type & payload_ref =
1442 // Because inspect_msg can't be used for signals
1443 // we can assume that message is not nullptr.
1444 *message );
1445 return lambda_to_call( payload_ref );
1446 }
1447 };
1448 }
1449
1450/*!
1451 * \brief Create a special marker for a trigger that requires waiting for
1452 * completion of an event handler.
1453 *
1454 * Usage example:
1455 * \code
1456 * using namespace so_5::experimental::testing;
1457 * ...
1458 * env.scenario().define_step("my_step")
1459 * .when(some_agent & reacts_to<some_message>() & wait_event_handler_completion());
1460 * ...
1461 * env.scenario().run_for(std::chrono::seconds(1));
1462 *
1463 * REQUIRE(completed() == env.scenario().result());
1464 * \endcode
1465 *
1466 * @note
1467 * This marker can be combined with reacts_to(), but not with ignores().
1468 *
1469 * \since v.5.8.3
1470 */
1471[[nodiscard]]
1474 {
1475 return {};
1476 }
1477
1478/*!
1479 * \brief Define a trigger that activates when an agent rejects a message from
1480 * the direct mbox.
1481 *
1482 * Usage example:
1483 * \code
1484 * using namespace so_5::experimental::testing;
1485 * ...
1486 * env.scenario().define_step("my_step")
1487 * .when(some_agent & ignores<some_message>());
1488 * \endcode
1489 *
1490 * \attention
1491 * It is necessary that agent should be subscribed to that message but
1492 * ignores it in the current state.
1493 * If an agent is not subscribed to a message then the message can be
1494 * simply skipped inside a call to send() function. It means that there
1495 * won't be delivery of message at all.
1496 *
1497 * \since v.5.5.24
1498 */
1499template<typename Msg_Type>
1500[[nodiscard]]
1506
1507/*!
1508 * \brief Define a trigger that activates when an agent rejects a message from
1509 * the direct mbox.
1510 *
1511 * Usage example:
1512 * \code
1513 * using namespace so_5::experimental::testing;
1514 * ...
1515 * env.scenario().define_step("my_step")
1516 * .when(some_agent & ignores<some_message>(some_mbox));
1517 * \endcode
1518 *
1519 * \attention
1520 * It is necessary that agent should be subscribed to that message but
1521 * ignores it in the current state.
1522 * If an agent is not subscribed to a message then the message can be
1523 * simply skipped inside a call to send() function. It means that there
1524 * won't be delivery of message at all.
1525 *
1526 * \since v.5.5.24
1527 */
1528template<typename Msg_Type>
1529[[nodiscard]]
1531ignores( const so_5::mbox_t & mbox )
1532 {
1533 return {
1535 mbox->id()
1536 };
1537 }
1538
1539/*!
1540 * \brief Create a constraint not-before.
1541 *
1542 * That constraint is fulfilled if an event is happened after
1543 * a specified \a pause. Time is calculated from moment of preactivation
1544 * of a scenario's step.
1545 *
1546 * Usage example:
1547 * \code
1548 * using namespace so_5::experimental::testing;
1549 * env.scenario().define_step("my_step")
1550 * .constraints(not_before(std::chrono::milliseconds(50)))
1551 * .when(some_agent & reacts_to<some_message>());
1552 * \endcode
1553 * In that case step won't be activated if agent receives a message
1554 * after, for example, 15ms.
1555 *
1556 * \note
1557 * If not_before() is used with not_after() then the correctness of those
1558 * constraints is not checked.
1559 *
1560 * \since v.5.5.24
1561 */
1562[[nodiscard]]
1566 {
1567 return std::make_unique< details::not_before_constraint_t >(pause);
1568 }
1569
1570/*!
1571 * \brief Create a constraint not-after.
1572 *
1573 * That constraint is fulfilled if an event is happened before
1574 * a specified \a pause. Time is calculated from moment of preactivation
1575 * of a scenario's step.
1576 *
1577 * Usage example:
1578 * \code
1579 * using namespace so_5::experimental::testing;
1580 * env.scenario().define_step("my_step")
1581 * .constraints(not_after(std::chrono::milliseconds(50)))
1582 * .when(some_agent & reacts_to<some_message>());
1583 * \endcode
1584 * In that case step won't be activated if agent receives a message
1585 * after, for example, 55ms.
1586 *
1587 * \note
1588 * If not_after() is used with not_before() then the correctness of those
1589 * constraints is not checked.
1590 *
1591 * \since v.5.5.24
1592 */
1593[[nodiscard]]
1597 {
1598 return std::make_unique< details::not_after_constraint_t >(pause);
1599 }
1600
1601namespace details {
1602
1603/*!
1604 * \brief A special objects that allows to call some specific methods
1605 * of a testing scenario.
1606 *
1607 * In the current version abstract_scenario_t has at least one
1608 * method that should be called only when testing scenario is in
1609 * progess. This method requires an instance of that class as a special
1610 * token.
1611 *
1612 * \since v.5.5.24
1613 */
1614class scenario_in_progress_accessor_t final
1615 {
1617
1618 private :
1620
1625
1627 const scenario_in_progress_accessor_t & ) = delete;
1628 scenario_in_progress_accessor_t & operator=(
1629 const scenario_in_progress_accessor_t & ) = delete;
1630
1632 scenario_in_progress_accessor_t && ) = delete;
1633 scenario_in_progress_accessor_t & operator=(
1634 scenario_in_progress_accessor_t && ) = delete;
1635
1636 public :
1638 scenario() const noexcept { return m_scenario.get(); }
1639 };
1640
1641/*!
1642 * \brief An interface of testing scenario.
1643 *
1644 * Please note that this type is a part of SObjectizer implementation
1645 * and is subject of changes in upcoming version. Do not use it in your
1646 * code, there is scenario_proxy_t call intended to be used by
1647 * end-users.
1648 *
1649 * This class is described in a public header-file in the current
1650 * version of SObjectizer. This is just to simplify implementation of
1651 * some testing-related stuff. The definition of that class can be move
1652 * to another file in future versions of SObjectizer without any
1653 * previous notice.
1654 *
1655 * \since v.5.5.24
1656 */
1658 {
1659 protected :
1660 //! Helper method for creation of scenario_in_progress_accessor instance.
1661 [[nodiscard]]
1662 scenario_in_progress_accessor_t
1663 make_accessor() noexcept
1664 {
1665 return { outliving_mutable(*this) };
1666 }
1667
1668 public :
1669 /*!
1670 * \brief Type of token returned by pre-event-handler hook.
1671 *
1672 * Object of that type should be stored and then it should
1673 * be passed to abstract_scenario_t::post_handler_hook() method.
1674 *
1675 * A token can be valid. It means that token holds an actual
1676 * pointer to some scenario step.
1677 *
1678 * Or token can be invalid. It means that there is no a valid
1679 * pointer to scenario step. In that case methods like
1680 * activated_step() and step_token() should not be called.
1681 *
1682 * \since v.5.5.24
1683 */
1684 class token_t final
1685 {
1688
1689 public :
1691 : m_activated_step( nullptr )
1692 {}
1694 abstract_scenario_step_t * activated_step,
1695 abstract_scenario_step_t::token_t step_token )
1696 : m_activated_step( activated_step )
1697 , m_step_token( step_token )
1698 {}
1699
1700 [[nodiscard]]
1701 bool
1702 valid() const noexcept
1703 {
1704 return nullptr != m_activated_step;
1705 }
1706
1707 [[nodiscard]]
1709 activated_step() const noexcept
1710 {
1711 return *m_activated_step;
1712 }
1713
1714 [[nodiscard]]
1716 step_token() const noexcept
1717 {
1718 return m_step_token;
1719 }
1720 };
1721
1722 public :
1724
1727
1730
1731 virtual ~abstract_scenario_t() = default;
1732
1733 //! Create a new step and return proxy for it.
1734 [[nodiscard]]
1737
1738 //! Get the result of scenario execution.
1739 [[nodiscard]]
1740 virtual scenario_result_t
1741 result() const noexcept = 0;
1742
1743 //! Run the scenario until completion or for specific amount of time.
1744 virtual void
1745 run_for( std::chrono::steady_clock::duration run_time ) = 0;
1746
1747 //! Hook that should be called before invocation of event-handler.
1748 /*!
1749 * This hook should be called before invocation of any event-handler
1750 * for ordinary message or enveloped message.
1751 *
1752 * The token returned should then be passed to post_handler_hook().
1753 */
1754 [[nodiscard]]
1755 virtual token_t
1757 //! Information about incoming message/signal.
1758 const incident_info_t & info,
1759 //! Reference to the incoming message.
1760 //! It may be nullptr in case of a signal.
1761 const message_ref_t & incoming_msg ) noexcept = 0;
1762
1763 //! Hook that should be called just after completion of event-handler.
1764 /*!
1765 * This hook should be called just after completion of any event-handler
1766 * for ordinary message, service request or enveloped message.
1767 */
1768 virtual void
1770 //! Token returned by previous pre_handler_hook() call.
1771 token_t token ) noexcept = 0;
1772
1773 //! Hook that should be called if there is no event-handler for
1774 //! a message or service request.
1775 virtual void
1777 //! Information about incoming message/signal.
1778 const incident_info_t & info,
1779 //! Reference to the incoming message.
1780 //! It may be nullptr in case of a signal.
1781 const message_ref_t & incoming_msg ) noexcept = 0;
1782
1783 //! Store a name of an agent state in the scenario.
1784 /*!
1785 * Note this method can be accessed only when scenario object is locked.
1786 */
1787 virtual void
1789 const scenario_in_progress_accessor_t & /*accessor*/,
1790 const abstract_scenario_step_t & step,
1791 const std::string & tag,
1792 const std::string & state_name ) = 0;
1793
1794 //! Get the stored state name.
1795 /*!
1796 * Should be called only after completion of scenario.
1797 *
1798 * Will throw an exception if there is no stored state for
1799 * a pair of (\a step_name,\a tag).
1800 */
1801 [[nodiscard]]
1802 virtual std::string
1804 const std::string & step_name,
1805 const std::string & tag ) const = 0;
1806
1807 //! Check presence of the stored state name.
1808 /*!
1809 * Should be called only after completion of scenario.
1810 *
1811 * \since v.5.8.4
1812 */
1813 [[nodiscard]]
1814 virtual bool
1816 const std::string & step_name,
1817 const std::string & tag ) const = 0;
1818
1819 //! Store msg inspection result in the scenario.
1820 /*!
1821 * Note this method can be accessed only when scenario object is locked.
1822 *
1823 * \since v.5.8.3
1824 */
1825 virtual void
1827 const scenario_in_progress_accessor_t & /*accessor*/,
1828 const abstract_scenario_step_t & step,
1829 const std::string & tag,
1830 const std::string & inspection_result ) = 0;
1831
1832 //! Get a value of stored msg inspection result.
1833 /*!
1834 * Note this method can be accessed only when scenario object is locked.
1835 *
1836 * \throw exception_t if there is no msg inspecation result with such
1837 * tag name for the specified step name.
1838 *
1839 * \since v.5.8.3
1840 */
1841 [[nodiscard]]
1842 virtual std::string
1844 const std::string & step_name,
1845 const std::string & tag ) const = 0;
1846
1847 /*!
1848 * \brief Is there the inspection result?
1849 *
1850 * Note this method can be accessed only when scenario object is locked.
1851 *
1852 * \throw exception_t if the scenario is not completed yet.
1853 *
1854 * \since v.5.8.4
1855 */
1856 [[nodiscard]]
1857 virtual bool
1859 const std::string & step_name,
1860 const std::string & tag ) const = 0;
1861 };
1862
1863/*!
1864 * \brief A helper operator to create a trigger for the specified agent.
1865 *
1866 * \since v.5.5.24
1867 */
1868template< incident_status_t Status >
1869trigger_holder_t<Status>
1871 const so_5::agent_t & agent,
1872 const trigger_source_t<Status> & src )
1873 {
1874 const auto src_mbox_id = src.m_src_mbox_id ?
1876
1877 return {
1879 Status,
1880 agent,
1882 src_mbox_id )
1883 };
1884 }
1885
1886/*!
1887 * \brief A helper operator to create a tigger that stores the
1888 * name of the current agent's state.
1889 *
1890 * \since v.5.5.24
1891 */
1892inline trigger_holder_t<incident_status_t::handled>
1894 trigger_holder_t<incident_status_t::handled> && old_holder,
1895 store_agent_state_name_t data_to_store )
1896 {
1897 auto trigger_ptr = old_holder.giveout_trigger();
1898 auto * target_agent = &(trigger_ptr->target_agent());
1899 trigger_ptr->set_completion(
1900 [data = std::move(data_to_store), target_agent](
1901 const trigger_completion_context_t & ctx ) noexcept
1902 {
1903 ctx.m_scenario_accessor.scenario().store_state_name(
1904 ctx.m_scenario_accessor,
1905 ctx.m_step,
1906 data.m_tag,
1907 target_agent->so_current_state().query_name() );
1908 } );
1909
1910 return { std::move(trigger_ptr) };
1911 }
1912
1913/*!
1914 * \brief A helper operator to create a tigger that inspects the incoming
1915 * message and stores the result into the scenario.
1916 *
1917 * \since v.5.8.3
1918 */
1919template< incident_status_t Status >
1920trigger_holder_t< Status >
1922 trigger_holder_t<Status> && old_holder,
1923 store_msg_inspection_result_t inspection_info )
1924 {
1928 const trigger_activation_context_t & ctx ) noexcept
1929 {
1933 ctx.m_step,
1934 info.m_tag,
1935 result );
1936 } );
1937
1938 return { std::move(trigger_ptr) };
1939 }
1940
1941/*!
1942 * \brief A helper operator to create a tigger that requires the
1943 * completion of an event handler.
1944 *
1945 * \since v.5.8.3
1946 */
1947inline trigger_holder_t<incident_status_t::handled>
1949 trigger_holder_t<incident_status_t::handled> && old_holder,
1950 wait_event_handler_completion_t /*data_to_store*/ )
1951 {
1952 auto trigger_ptr = old_holder.giveout_trigger();
1953 trigger_ptr->set_completion(
1954 []( const trigger_completion_context_t & /*ctx*/ ) noexcept
1955 {
1956 // Do nothing.
1957 // The presence of this completion_function requires
1958 // waiting of the return from an event handler.
1959 } );
1960
1961 return { std::move(trigger_ptr) };
1962 }
1963
1964namespace mbox_receives_msg_impl
1965{
1966
1967/*!
1968 * \brief Agent that receives a message/signal from specified mbox.
1969 *
1970 * This agent is a part of `receives` trigger.
1971 *
1972 * \tparam Msg type of message/signal to be received.
1973 *
1974 * \since v.5.8.3
1975 */
1976template< typename Msg >
1977class a_msg_catcher_t final : public agent_t
1978 {
1979 using msg_traits_t = message_payload_type< Msg >;
1980
1981 //! Source for a message.
1982 const mbox_t m_from;
1983
1984 public:
1986 context_t ctx,
1987 mbox_t from )
1988 : agent_t{ std::move(ctx) }
1989 , m_from{ std::move(from) }
1990 {}
1991
1992 void
1994 {
1995 so_subscribe( m_from ).event( &a_msg_catcher_t::evt_msg_arrived );
1996 }
1997
1998 private:
1999 void
2001 {
2002 // Nothing to do.
2003 }
2004 };
2005
2006//
2007// msg_catcher_map_layer_t
2008//
2009/// \brief Special layer that holds a map of catcher agents.
2010///
2011/// There should be just one agent for every pair of (msg_type, mbox),
2012/// otherwise there would be errors on attempt to use receives() with
2013/// MPSC mboxes. Because of that a map of catcher agent is necessary.
2014/// This layer holds such a map and provides a method to create a
2015/// new catcher agent or get a pointer to existing one.
2016///
2017/// This layer will be automatically added to testing_env_t.
2018///
2019/// When a receives_indicator_t is transformed into a trigger this
2020/// layer will be obtained from mbox's environment.
2021///
2022/// \note
2023/// This layer holds pointers to agents. It's not a good idea in
2024/// general, because the lifetime of catcher agents is not under
2025/// control of the layer. But in this use case it seems to be safe
2026/// because pointers are used only during creation of triggers and
2027/// at this moment all catcher agents are alive.
2028///
2029/// \since v.5.8.4
2030class SO_5_TYPE msg_catcher_map_layer_t final
2031 : public layer_t
2032 {
2033 public:
2034 /// Information about a catcher agent.
2035 ///
2036 /// This information has to be used for building a trigger.
2037 ///
2038 /// @note
2039 /// At the current moment just a pointer is enough. But it's better
2040 /// to place it into a struct to have a possibility to extend it
2041 /// in the future.
2043 {
2044 /// Pointer to the agent.
2046
2047 /// Initializing constructor.
2049 const agent_t * target_agent )
2050 : m_target_agent{ target_agent }
2051 {}
2052 };
2053
2054 private:
2055 /// Type of key for identifying agents.
2056 using catcher_key_t = std::tuple<
2057 // Type of message/signal to be received.
2058 std::type_index,
2059 // ID of source mbox.
2060 mbox_id_t
2061 >;
2062
2063 /// Type of map of already registered agents.
2064 using agents_map_t = std::map< catcher_key_t, catcher_info_t >;
2065
2066 /// The lock for thread safety.
2068
2069 /// The map of already created catcher agents.
2071
2072 /// Helper for registration of a new agent.
2073 template< typename Msg >
2074 [[nodiscard]] const agent_t *
2076 const so_5::mbox_t & from ) const
2077 {
2078 // NOTE: the agent will be bound to the default dispatcher.
2079 return this->so_environment().introduce_coop(
2080 [&from]( coop_t & coop ) -> const agent_t * {
2081 using agent_to_create_t = a_msg_catcher_t< Msg >;
2082
2083 return coop.make_agent< agent_to_create_t >( from );
2084 } );
2085 }
2086
2087 public:
2089 ~msg_catcher_map_layer_t() noexcept override;
2090
2091 /// Get information about a catcher agent for (msg_type, mbox) pair.
2092 ///
2093 /// If there is no such an agent for this (msg_type, mbox) pair it
2094 /// will be created. Otherwise an information about already existing
2095 /// agent will be returned.
2096 template< typename Msg >
2097 [[nodiscard]] catcher_info_t
2099 const so_5::mbox_t & from )
2100 {
2101 std::lock_guard< std::mutex > lock{ m_lock };
2102
2103 const catcher_key_t key{
2104 message_payload_type< Msg >::subscription_type_index(),
2105 from->id()
2106 };
2107 auto it = m_agents.find( key );
2108 if( it == m_agents.end() )
2109 {
2110 // For exception safety we add a key with nullptr
2111 // to the m_agents map, and then update the pointer if
2112 // there won't be problems with registration of a new agent.
2113 it = m_agents.emplace(
2114 key,
2115 catcher_info_t{ nullptr } )
2116 .first;
2117
2118 ::so_5::details::do_with_rollback_on_exception(
2119 [&]() {
2120 // It's necessary to create an agent first time.
2121 const agent_t * catcher_agent =
2122 register_new_catcher< Msg >( from );
2123
2124 // Now we have an actual agent pointer and can update
2125 // m_agents map.
2126 it->second.m_target_agent = catcher_agent;
2127 },
2128 [&]() {
2129 // Information about new agent has to be removed
2130 // because it wasn't registered.
2131 m_agents.erase( it );
2132 } );
2133 }
2134
2135 // Now the agent is present in the map.
2136 return it->second;
2137 }
2138 };
2139
2140} /* namespace mbox_receives_msg_impl */
2141
2142/*!
2143 * \brief Special indicator to be used in implementation of `receives` trigger.
2144 *
2145 * \tparam Msg type of message/signal to be received.
2146 *
2147 * \since v.5.8.3
2148 */
2149template< typename Msg >
2151 {};
2152
2153/*!
2154 * \brief A helper operator to create a tigger that receives a message/signal
2155 * from specified mbox.
2156 *
2157 * \tparam Msg type of message/signal to be received.
2158 *
2159 * \since v.5.8.3
2160 */
2161template< typename Msg >
2162trigger_holder_t< incident_status_t::handled >
2164 //! Mbox from that a message/signal has to be received.
2165 const mbox_t & from,
2166 //! Type of message to be received.
2167 receives_indicator_t< Msg > )
2168 {
2169 // Information about catcher agents is stored in a special layer.
2172 // The appropriate catcher agent will be created or reused.
2174
2175 return {
2180 from->id() )
2181 };
2182 }
2183
2184} /* namespace details */
2185
2186/*!
2187 * \brief Helper function to be used for a trigger that receives
2188 * a message/singal from a mbox.
2189 *
2190 * Usage example:
2191 * \code
2192 * so_5::testing::testing_env_t env;
2193 * ...
2194 * so_5::mbox_t dest = env.environment().create_mbox();
2195 * ...
2196 * env.scenario().define_step("message_arrives")
2197 * .when(dest & tests::receives<some_msg>());
2198 * ...
2199 * \endcode
2200 *
2201 * Since v.5.8.4 this helper can be used for the same mbox and the same
2202 * message type several times in one scenario (but in different scenario
2203 * steps):
2204 * \code
2205 * so_5::testing::testing_env_t env;
2206 * ...
2207 * so_5::mbox_t dest = env.environment().create_mbox();
2208 * ...
2209 * env.scenario().define_step("message_arrives")
2210 * .when(dest & tests::receives<some_msg>());
2211 * ...
2212 * env.scenario().define_step("another_time")
2213 * .when(dest & tests::receives<some_msg>());
2214 * ...
2215 * env.scenario().define_step("and_yet_another_time")
2216 * .when(dest & tests::receives<some_msg>());
2217 * \endcode
2218 *
2219 * \note
2220 * A mutable message can be specified too. But the mbox should allow
2221 * subscription for mutable messages. For example:
2222 * \code
2223 * so_5::testing::testing_env_t env;
2224 * ...
2225 * so_5::mbox_t dest = so_5::make_unique_subscribers_mbox(env.environment());
2226 * ...
2227 * env.scenario().define_step("message_arrives")
2228 * .when(dest & tests::receives< so_5::mutable_msg<some_msg> >());
2229 * ...
2230 * \endcode
2231 *
2232 * \since v.5.8.3
2233 */
2234template< typename Msg >
2235[[nodiscard]]
2238 {
2239 return {};
2240 }
2241
2242/*!
2243 * \brief A special wrapper around scenario object.
2244 *
2245 * The class scenario_proxy_t is a public interface to scenario object.
2246 * The actual scenario object is inside of testing_env_t instance and
2247 * access to it is provided via scenario_proxy_t wrapper.
2248 *
2249 * Usage example:
2250 * \code
2251 * using namespace so_5::experimental::testing;
2252 * TEST_CASE("some_case") {
2253 * testing_env_t env;
2254 *
2255 * so_5::agent_t * test_agent;
2256 * env.environment().introduce_coop([&](so_5::coop_t & coop) {
2257 * test_agent = coop.make_agent<some_agent>();
2258 * });
2259 *
2260 * env.scenario().define_step("one")
2261 * .impact<some_message>(*test_agent)
2262 * .when(*test_agent & reacts_to<some_message>());
2263 *
2264 * env.scenario().run_for(std::chrono::milliseconds(200));
2265 *
2266 * REQUIRE(completed() == env.scenario().result());
2267 * }
2268 * \endcode
2269 * Or in more conciseness form:
2270 * \code
2271 * using namespace so_5::experimental::testing;
2272 * TEST_CASE("some_case") {
2273 * testing_env_t env;
2274 *
2275 * so_5::agent_t * test_agent;
2276 * env.environment().introduce_coop([&](so_5::coop_t & coop) {
2277 * test_agent = coop.make_agent<some_agent>();
2278 * });
2279 *
2280 * auto scenario = env.scenario();
2281 *
2282 * scenario.define_step("one")
2283 * .impact<some_message>(*test_agent)
2284 * .when(*test_agent & reacts_to<some_message>());
2285 *
2286 * scenario.run_for(std::chrono::milliseconds(200));
2287 *
2288 * REQUIRE(completed() == scenario.result());
2289 * }
2290 * \endcode
2291 *
2292 * \note
2293 * scenario_proxy_t holds a reference to an object from testing_env_t
2294 * instance. It means that scenario_proxy shouldn't outlive the
2295 * corresponding testing_env_t object. For example this code will
2296 * lead to a dangling reference:
2297 * \code
2298 * so_5::experimental::testing::scenario_proxy_t get_scenario() {
2299 * so_5::experimental::testing::testing_env_t env;
2300 * return env.scenario(); // OOPS! A reference to destroyed object will be returned.
2301 * }
2302 * \endcode
2303 *
2304 * \since v.5.5.24
2305 */
2306class SO_5_TYPE scenario_proxy_t final
2307 {
2308 friend class testing_env_t;
2309
2310 private :
2312
2315
2316 public :
2317 //! Start definition of a new scenario's step.
2318 /*!
2319 * New steps can be defined until run_for() is called.
2320 * When the scenario is in progress (or is already
2321 * finished) then an attempt to call to define_step() will
2322 * lead to an exception.
2323 *
2324 * \return A special wrapper around a new step instance.
2325 * This wrapped should be used to define the step.
2326 *
2327 * \note
2328 * The value of \a step_name has to be unique, but the current
2329 * version of SObjectizer doesn't controll that.
2330 */
2331 [[nodiscard]]
2333 define_step( nonempty_name_t step_name );
2334
2335 //! Get the result of scenario execution.
2336 /*!
2337 * \note
2338 * This method is intended to be called only after the completion of
2339 * run_for() method.
2340 */
2341 [[nodiscard]]
2343 result() const;
2344
2345 //! Runs the scenario for specified amount of time.
2346 /*!
2347 * This method does to things:
2348 * - unfreeze all agents registered in testing environment
2349 * up to this time;
2350 * - run scenario until scenario will be completed or
2351 * \a run_time elapsed.
2352 *
2353 * The result of scenario run can then be obtained by result()
2354 * method.
2355 *
2356 * Usage example:
2357 * \code
2358 * using namespace so_5::experimental::testing;
2359 * TEST_CASE("some_case") {
2360 * testing_env_t env; // Create a testing environment.
2361 *
2362 * // Introduce some agents.
2363 * env.environment().introduce_coop(...);
2364 *
2365 * // Do define the scenario for the test case.
2366 * auto scenario = env.scenario();
2367 * ...
2368 * // Run the scenario for at most 1 second.
2369 * scenario.run_for(std::chrono::seconds(1));
2370 * // Check the result of the scenario.
2371 * REQUIRE(completed() == scenario.result());
2372 * }
2373 * \endcode
2374 */
2375 void
2376 run_for( std::chrono::steady_clock::duration run_time );
2377
2378 //! Try to get stored name of an agent's state.
2379 /*!
2380 * This method allows to get state name stored by
2381 * store_state_name() trigger. For example:
2382 * \code
2383 * using namespace so_5::experimental::testing;
2384 * TEST_CASE("some_case") {
2385 * testing_env_t env;
2386 *
2387 * so_5::agent_t * test_agent;
2388 * env.environment().introduce_coop([&](so_5::coop_t & coop) {
2389 * test_agent = coop.make_agent<some_agent_type>(...);
2390 * });
2391 *
2392 * env.scenario().define_step("one")
2393 * .impact<some_message>(*test_agent, ...)
2394 * .when(*test_agent & reacts_to<some_message>()
2395 * & store_state_name("my_agent"));
2396 * ...
2397 * env.scenario().run_for(std::chrono::seconds(1));
2398 *
2399 * REQUIRE(completed() == env.scenario().result());
2400 *
2401 * REQUIRE("some_state" == env.scenario().stored_state_name("one", "my_agent"));
2402 * }
2403 * \endcode
2404 *
2405 * \attention
2406 * This method can be called only after completion of the scenario.
2407 * Otherwise an instance of so_5::exception_t will be thrown.
2408 *
2409 * \return Name of stored state. If there is no stored name for
2410 * a pair of (\a step_name, \a tag) then an instance of
2411 * so_5::exception_t will be thrown.
2412 */
2413 [[nodiscard]]
2414 std::string
2416 const std::string & step_name,
2417 const std::string & tag ) const;
2418
2419 //! Is there the inspection result?
2420 /*!
2421 * This method allows to check presence of the state name stored by
2422 * store_state_name() trigger. For example:
2423 * \code
2424 * using namespace so_5::experimental::testing;
2425 * TEST_CASE("some_case") {
2426 * testing_env_t env;
2427 *
2428 * so_5::agent_t * test_agent;
2429 * env.environment().introduce_coop([&](so_5::coop_t & coop) {
2430 * test_agent = coop.make_agent<some_agent_type>(...);
2431 * });
2432 *
2433 * env.scenario().define_step("one")
2434 * .impact<some_message>(*test_agent, ...)
2435 * .when_any(
2436 * *test_agent & reacts_to<some_message>()
2437 * & store_state_name("my_agent"),
2438 * ... // Other triggers...
2439 * );
2440 * ...
2441 * env.scenario().run_for(std::chrono::seconds(1));
2442 *
2443 * REQUIRE(completed() == env.scenario().result());
2444 *
2445 * // Because store_state_name was used in when_any then, the corresponding
2446 * // trigger may not have been activated.
2447 * if(env.scenario().has_stored_state_name("one", "my_agent"))
2448 * REQUIRE("some_state" == env.scenario().stored_state_name("one", "my_agent"));
2449 * }
2450 * \endcode
2451 *
2452 * \attention
2453 * This method can be called only after completion of the scenario.
2454 * Otherwise an instance of so_5::exception_t will be thrown.
2455 *
2456 * \since v.5.8.4
2457 */
2458 [[nodiscard]]
2459 bool
2461 const std::string & step_name,
2462 const std::string & tag ) const;
2463
2464 //! Try to get stored msg inspection result.
2465 /*!
2466 * This method allows to get msg inspection result stored by
2467 * inspect_msg() trigger. For example:
2468 * \code
2469 * using namespace so_5::experimental::testing;
2470 * TEST_CASE("some_case") {
2471 * testing_env_t env;
2472 *
2473 * so_5::agent_t * test_agent;
2474 * env.environment().introduce_coop([&](so_5::coop_t & coop) {
2475 * test_agent = coop.make_agent<some_agent_type>(...);
2476 * });
2477 *
2478 * env.scenario().define_step("one")
2479 * .impact<some_message>(*test_agent, ...)
2480 * .when(*test_agent & reacts_to<some_message>()
2481 * & inspect_msg("msg-check"
2482 * [](const some_message & msg) -> std::string {
2483 * return msg.some_field = expected_value ? "OK" : "FAIL";
2484 * })
2485 * );
2486 * ...
2487 * env.scenario().run_for(std::chrono::seconds(1));
2488 *
2489 * REQUIRE(completed() == env.scenario().result());
2490 *
2491 * REQUIRE("OK" == env.scenario().stored_msg_inspection_result("one", "msg-check"));
2492 * }
2493 * \endcode
2494 *
2495 * \attention
2496 * This method can be called only after completion of the scenario.
2497 * Otherwise an instance of so_5::exception_t will be thrown.
2498 *
2499 * \return The value of the inspection result. If there is no stored
2500 * result for a pair of (\a step_name, \a tag) then an instance of
2501 * so_5::exception_t will be thrown.
2502 *
2503 * \since v.5.8.3
2504 */
2505 [[nodiscard]]
2506 std::string
2508 const std::string & step_name,
2509 const std::string & tag ) const;
2510
2511 //! Is there the inspection result?
2512 /*!
2513 * This method allows to check presence of them msg inspection result
2514 * stored by inspect_msg() trigger. For example:
2515 * \code
2516 * using namespace so_5::experimental::testing;
2517 * TEST_CASE("some_case") {
2518 * testing_env_t env;
2519 *
2520 * so_5::agent_t * test_agent;
2521 * env.environment().introduce_coop([&](so_5::coop_t & coop) {
2522 * test_agent = coop.make_agent<some_agent_type>(...);
2523 * });
2524 *
2525 * env.scenario().define_step("one")
2526 * .impact<some_message>(*test_agent, ...)
2527 * .when_any(
2528 * *test_agent & reacts_to<some_message>()
2529 * & inspect_msg("msg-check"
2530 * [](const some_message & msg) -> std::string {
2531 * return msg.some_field = expected_value ? "OK" : "FAIL";
2532 * }),
2533 * ... // some other triggers.
2534 * );
2535 * ...
2536 * env.scenario().run_for(std::chrono::seconds(1));
2537 *
2538 * REQUIRE(completed() == env.scenario().result());
2539 *
2540 * // Because inspect_msg was used in when_any then, the corresponding
2541 * // trigger may not have been activated.
2542 * if(env.scenario().has_stored_msg_inspection_result("one", "msg-check"))
2543 * REQUIRE("OK" == env.scenario().stored_msg_inspection_result("one", "msg-check"));
2544 * }
2545 * \endcode
2546 *
2547 * \attention
2548 * This method can be called only after completion of the scenario.
2549 * Otherwise an instance of so_5::exception_t will be thrown.
2550 *
2551 * \since v.5.8.4
2552 */
2553 [[nodiscard]]
2554 bool
2556 const std::string & step_name,
2557 const std::string & tag ) const;
2558 };
2559
2560/*!
2561 * \brief A special testing environment that should be used for
2562 * testing of agents.
2563 *
2564 * To allow testing of agent a special environment is necessary.
2565 * That environment creates special hooks those control event
2566 * handling. The class testing_env_t is an implementation of that
2567 * special environment.
2568 *
2569 * To create a test-case for some agent (or even several agents)
2570 * is it necessary to create an instance of testing_env_t class:
2571 * \code
2572 * using namespace so_5::experimental::testing;
2573 * TEST_CASE("some_case") {
2574 * testing_env_t env;
2575 * ...
2576 * }
2577 * \endcode
2578 * An instance of testing_env_t creates and launches SObjectizer
2579 * Environment in the constructor. That Environment will be shut down
2580 * in the destructor of testing_env_t instance automatically.
2581 * It is possible to shut down SObjectizer Environment manually
2582 * by using stop(), join(), stop_then_join() methods
2583 * (they play the same role as the corresponding methods of
2584 * so_5::wrapped_env_t class).
2585 *
2586 * The testing_env_t instance already has an instance of scenario
2587 * object inside. Access to that object can be obtained via
2588 * scenario() method:
2589 * \code
2590 * using namespace so_5::experimental::testing;
2591 * TEST_CASE("some_case") {
2592 * testing_env_t env;
2593 * ...
2594 * env.scenario().define_step("one")...;
2595 * ...
2596 * // Or, for conciseness:
2597 * auto scenario = env.scenario();
2598 * scenario.define_step("two")...;
2599 * ...
2600 * }
2601 * \endcode
2602 *
2603 * Please note that this is a special environment. One important
2604 * feature of it is a behaviour of agents created inside that
2605 * environment. All agents registered before call to
2606 * scenario_proxy_t::run_for() method will be in "frozen" mode:
2607 * they are present in the SObjectizer Environment, but they can't
2608 * receive any incoming messages (even so_5::agent_t::so_evt_start()
2609 * is not called for them).
2610 *
2611 * All registered agents will be unfreezed when scenario_proxy_t::run_for()
2612 * will be called (or if testing_env_t is stopped). For example:
2613 * \code
2614 * class hello final : public so_5::agent_t {
2615 * public:
2616 * using so_5::agent_t::agent_t;
2617 * void so_evt_start() override {
2618 * std::cout << "Hello, World!" << std::endl;
2619 * }
2620 * };
2621 * void testing_env_demo() {
2622 * so_5::experimental::testing::testing_env_t env;
2623 *
2624 * env.environment().introduce_coop([](so_5::coop_t & coop) {
2625 * coop.make_agent<hello>();
2626 * });
2627 *
2628 * std::cout << "Bye, bye!" << std::endl;
2629 *
2630 * env.scenario().run_for(std::chrono::milliseconds(100));
2631 * }
2632 * \endcode
2633 * In that case we will have the following output:
2634\verbatim
2635Bye, bye!
2636Hello, World!
2637\endverbatim
2638 *
2639 * \since v.5.5.24
2640 */
2642 {
2643 public :
2644 //! Default constructor.
2645 /*!
2646 * \note
2647 * This constructor launches SObjectizer Environment with
2648 * default parameters.
2649 */
2650 testing_env_t();
2651 //! A constructor that allows to tune environment's parameters.
2652 /*!
2653 * Usage example:
2654 * \code
2655 * using namespace so_5::experimental::testing;
2656 * testing_env_t env{
2657 * [](so_5::environment_params_t & params) {
2658 * // Turn message delivery tracing on.
2659 * params.message_delivery_tracer(
2660 * so_5::msg_tracing::std_cout_tracer());
2661 * }
2662 * };
2663 * \endcode
2664 *
2665 * \note
2666 * The testing_env_t class can change some values in environment_params_t
2667 * after the return from \a env_params_tuner.
2668 */
2670 //! Function for environment's params tuning.
2671 so_5::generic_simple_so_env_params_tuner_t env_params_tuner );
2672 //! A constructor that receives already constructed
2673 //! environment parameters.
2674 /*!
2675 * Usage example:
2676 * \code
2677 * so_5::environment_params_t make_params() {
2678 * so_5::environment_params_t params;
2679 * ...
2680 * params.message_delivery_tracer(
2681 * so_5::msg_tracing::std_cout_tracer());
2682 * ...
2683 * return params;
2684 * }
2685 * ...
2686 * TEST_CASE("some_case") {
2687 * using namespace so_5::experimental::testing;
2688 * testing_env_t env{ make_params() };
2689 * ...
2690 * }
2691 * \endcode
2692 *
2693 * \note
2694 * The testing_env_t class can change some values in environment_params_t
2695 * before launching a SObjectizer Environment.
2696 */
2698 environment_params_t && env_params );
2699 ~testing_env_t();
2700
2701 //! Access to wrapped environment.
2703 environment() const;
2704
2705 //! Send stop signal to environment.
2706 /*!
2707 * Please note that stop() just sends a shut down signal to the
2708 * SObjectizer Environment, but doesn't wait the completion of
2709 * the Environment. If you calls stop() method you have to
2710 * call join() to ensure that Environment is shut down.
2711 */
2712 void
2713 stop();
2714
2715 //! Wait for complete finish of environment's work.
2716 void
2717 join();
2718
2719 //! Send stop signal and wait for complete finish of environment's work.
2720 void
2722
2723 //! Access to the associated scenario.
2724 [[nodiscard]]
2725 scenario_proxy_t
2726 scenario() noexcept;
2727
2728 struct internals_t;
2729
2730 private :
2732
2734
2735 void
2737
2738 void
2740 };
2741
2742} /* namespace v1 */
2743
2744} /* namespace testing */
2745
2746} /* namespace experimental */
2747
2748} /* namespace so_5 */
2749
2750#if defined( SO_5_MSVC )
2751 #pragma warning(pop)
2752#endif
A base class for agents.
Definition agent.hpp:673
static demand_handler_pfn_t get_demand_handler_on_message_ptr() noexcept
Definition agent.cpp:1526
static demand_handler_pfn_t get_demand_handler_on_enveloped_msg_ptr() noexcept
Definition agent.cpp:1544
An interface of envelope with some message/signal inside.
virtual void access_hook(access_context_t context, handler_invoker_t &invoker) noexcept=0
An extended version of handling_context which can be used for calling event handler.
An information about payload inside envelope.
message_ref_t & message() const noexcept
Parameters for the SObjectizer Environment initialization.
environment_params_t()
Constructor.
SObjectizer Environment.
Interface of event_queue_hook object.
An interface of event queue for agent.
trigger_t & trigger() const noexcept
Get a reference to activated trigger.
virtual void no_handler_hook(const scenario_in_progress_accessor_t &scenario_accessor, const incident_info_t &info, const message_ref_t &incoming_msg) noexcept=0
abstract_scenario_step_t & operator=(const abstract_scenario_step_t &)=delete
virtual void add_preactivate_action(preactivate_action_t action)=0
Add another preactivation action.
virtual void setup_triggers(trigger_container_t triggers, std::size_t triggers_to_activate) noexcept=0
Setup triggers for the step.
virtual void setup_constraints(constraint_container_t constraints) noexcept=0
Setup constraints for the step.
virtual const std::string & name() const noexcept=0
Get the name of the step.
virtual void post_handler_hook(const scenario_in_progress_accessor_t &scenario_accessor, token_t token) noexcept=0
Hook that should be called just after completion of event-handler.
abstract_scenario_step_t & operator=(abstract_scenario_step_t &&)=delete
virtual void preactivate() noexcept=0
Perform preactivation of the step.
virtual token_t pre_handler_hook(const scenario_in_progress_accessor_t &scenario_accessor, const incident_info_t &info, const message_ref_t &incoming_msg) noexcept=0
Hook that should be called before invocation of event-handler.
abstract_scenario_step_t(const abstract_scenario_step_t &)=delete
virtual status_t status() const noexcept=0
Get the current status of the step.
token_t(abstract_scenario_step_t *activated_step, abstract_scenario_step_t::token_t step_token)
virtual void store_msg_inspection_result(const scenario_in_progress_accessor_t &, const abstract_scenario_step_t &step, const std::string &tag, const std::string &inspection_result)=0
Store msg inspection result in the scenario.
scenario_in_progress_accessor_t make_accessor() noexcept
Helper method for creation of scenario_in_progress_accessor instance.
virtual void run_for(std::chrono::steady_clock::duration run_time)=0
Run the scenario until completion or for specific amount of time.
virtual token_t pre_handler_hook(const incident_info_t &info, const message_ref_t &incoming_msg) noexcept=0
Hook that should be called before invocation of event-handler.
virtual std::string stored_msg_inspection_result(const std::string &step_name, const std::string &tag) const =0
Get a value of stored msg inspection result.
virtual void post_handler_hook(token_t token) noexcept=0
Hook that should be called just after completion of event-handler.
virtual void no_handler_hook(const incident_info_t &info, const message_ref_t &incoming_msg) noexcept=0
abstract_scenario_t & operator=(const abstract_scenario_t &)=delete
virtual void store_state_name(const scenario_in_progress_accessor_t &, const abstract_scenario_step_t &step, const std::string &tag, const std::string &state_name)=0
Store a name of an agent state in the scenario.
virtual bool has_stored_state_name(const std::string &step_name, const std::string &tag) const =0
Check presence of the stored state name.
virtual std::string stored_state_name(const std::string &step_name, const std::string &tag) const =0
Get the stored state name.
abstract_scenario_t & operator=(abstract_scenario_t &&)=delete
virtual scenario_result_t result() const noexcept=0
Get the result of scenario execution.
virtual step_definition_proxy_t define_step(nonempty_name_t step_name)=0
Create a new step and return proxy for it.
abstract_scenario_t(const abstract_scenario_t &)=delete
virtual bool has_stored_msg_inspection_result(const std::string &step_name, const std::string &tag) const =0
Is there the inspection result?
An interface for object that will unfreeze all registered agents when testing scenario starts.
Definition all.cpp:449
agent_unfreezer_t & operator=(const agent_unfreezer_t &)=delete
virtual void unfreeze() noexcept=0
Issue a command to unfreeze all frozen agents.
agent_unfreezer_t & operator=(agent_unfreezer_t &&)=delete
constraint_t & operator=(constraint_t &&)=delete
virtual void start() noexcept=0
Hook for step preactivation.
constraint_t & operator=(const constraint_t &)=delete
virtual bool check(const incident_status_t incident_status, const incident_info_t &info) const noexcept=0
Check for fulfillment of constraint.
virtual void finish() noexcept=0
Hook for step completion.
const agent_t * register_new_catcher(const so_5::mbox_t &from) const
Helper for registration of a new agent.
const std::chrono::steady_clock::duration m_pause
Value to be used.
std::chrono::steady_clock::time_point m_started_at
Time point of step preactivation.
bool check(const incident_status_t, const incident_info_t &) const noexcept override
Check for fulfillment of constraint.
std::chrono::steady_clock::time_point m_started_at
Time point of step preactivation.
const std::chrono::steady_clock::duration m_pause
Value to be used.
bool check(const incident_status_t, const incident_info_t &) const noexcept override
Check for fulfillment of constraint.
preactivate_actions_container_t m_preactivate_actions
All preactivation actions.
Definition all.cpp:143
std::size_t m_triggers_activated
Count of triggers those are activated.
Definition all.cpp:175
void no_handler_hook(const scenario_in_progress_accessor_t &scenario_accessor, const incident_info_t &info, const message_ref_t &incoming_msg) noexcept override
Definition all.cpp:256
token_t pre_handler_hook(const scenario_in_progress_accessor_t &scenario_accessor, const incident_info_t &info, const message_ref_t &incoming_msg) noexcept override
Hook that should be called before invocation of event-handler.
Definition all.cpp:210
bool try_pass_constraints(const incident_status_t incident_status, const incident_info_t &info) const noexcept
An attempt to check constraints for a new incident.
Definition all.cpp:353
void setup_constraints(constraint_container_t constraints) noexcept override
Setup constraints for the step.
Definition all.cpp:301
void preactivate() noexcept override
Perform preactivation of the step.
Definition all.cpp:203
std::size_t m_last_non_activated_trigger
Index of last trigger in the first part of trigger's container.
Definition all.cpp:166
status_t status() const noexcept override
Get the current status of the step.
Definition all.cpp:273
std::size_t m_triggers_to_completion
Count of activated triggers those are not completed yet.
Definition all.cpp:184
status_t m_status
The current state of the step.
Definition all.cpp:187
void post_handler_hook(const scenario_in_progress_accessor_t &scenario_accessor, token_t token) noexcept override
Hook that should be called just after completion of event-handler.
Definition all.cpp:230
const std::string & name() const noexcept override
Get the name of the step.
Definition all.cpp:197
token_t try_activate(const trigger_activation_context_t &context, const incident_status_t incident_status, const incident_info_t &info) noexcept
An attempt to activate the step when a new incident arrives.
Definition all.cpp:367
constraint_container_t m_constraints
All constraints.
Definition all.cpp:146
void add_preactivate_action(preactivate_action_t action) override
Add another preactivation action.
Definition all.cpp:279
void setup_triggers(trigger_container_t triggers, std::size_t triggers_to_activate) noexcept override
Setup triggers for the step.
Definition all.cpp:286
void no_handler_hook(const incident_info_t &info, const message_ref_t &incoming_msg) noexcept override
Definition all.cpp:656
std::string stored_msg_inspection_result(const std::string &step_name, const std::string &tag) const override
Get a value of stored msg inspection result.
Definition all.cpp:737
bool has_stored_state_name(const std::string &step_name, const std::string &tag) const override
Check presence of the stored state name.
Definition all.cpp:719
std::size_t m_waiting_step_index
Index of the current preactivated step.
Definition all.cpp:499
std::set< abstract_scenario_step_t * > m_active_steps
Set of active step those are not completed yet.
Definition all.cpp:496
scenario_result_t result() const noexcept override
Get the result of scenario execution.
Definition all.cpp:566
void store_msg_inspection_result(const scenario_in_progress_accessor_t &, const abstract_scenario_step_t &step, const std::string &tag, const std::string &inspection_result) override
Store msg inspection result in the scenario.
Definition all.cpp:682
bool has_stored_msg_inspection_result(const std::string &step_name, const std::string &tag) const override
Is there the inspection result?
Definition all.cpp:762
std::string stored_state_name(const std::string &step_name, const std::string &tag) const override
Get the stored state name.
Definition all.cpp:694
token_t react_on_pre_handler_hook(const incident_info_t &info, const message_ref_t &incoming_msg) noexcept
Definition all.cpp:789
std::condition_variable m_completion_cv
Condition variable for waiting completion of the scenario.
Definition all.cpp:480
void react_on_no_handler_hook(const incident_info_t &info, const message_ref_t &incoming_msg) noexcept
Definition all.cpp:833
void post_handler_hook(token_t token) noexcept override
Hook that should be called just after completion of event-handler.
Definition all.cpp:627
void run_for(std::chrono::steady_clock::duration run_time) override
Run the scenario until completion or for specific amount of time.
Definition all.cpp:577
agent_unfreezer_t * m_unfreezer
Unfreezer for registered agents.
Definition all.cpp:530
void setup_unfreezer(agent_unfreezer_t &unfreezer) noexcept
Set the unfreezer for registered agents.
Definition all.cpp:541
inspection_result_map_t m_stored_inspection_results
Container for holding stored inspection results for messages.
Definition all.cpp:524
void store_state_name(const scenario_in_progress_accessor_t &, const abstract_scenario_step_t &step, const std::string &tag, const std::string &state_name) override
Store a name of an agent state in the scenario.
Definition all.cpp:672
token_t pre_handler_hook(const incident_info_t &info, const message_ref_t &incoming_msg) noexcept override
Hook that should be called before invocation of event-handler.
Definition all.cpp:607
step_definition_proxy_t define_step(nonempty_name_t step_name) override
Create a new step and return proxy for it.
Definition all.cpp:548
state_name_map_t m_stored_states
Container for holding stored state names.
Definition all.cpp:518
scenario_status_t m_status
The current state of the scenario.
Definition all.cpp:483
std::vector< step_unique_ptr_t > m_steps
Scenario's steps.
Definition all.cpp:489
scenario_in_progress_accessor_t & operator=(scenario_in_progress_accessor_t &&)=delete
scenario_in_progress_accessor_t(outliving_reference_t< abstract_scenario_t > scenario)
scenario_in_progress_accessor_t(scenario_in_progress_accessor_t &&)=delete
scenario_in_progress_accessor_t & operator=(const scenario_in_progress_accessor_t &)=delete
scenario_in_progress_accessor_t(const scenario_in_progress_accessor_t &)=delete
trigger_holder_t & operator=(trigger_holder_t &&) noexcept=default
trigger_holder_t & operator=(const trigger_holder_t &)=delete
trigger_holder_t(trigger_holder_t &&) noexcept=default
trigger_unique_ptr_t giveout_trigger() noexcept
Get the trigger object from the holder.
activation_function_t m_activation
Optional function for activation of the trigger.
const std::type_index m_msg_type
Type of message/signal to activate the trigger.
completion_function_t m_completion
Optional function for completion of the trigger.
void set_completion(completion_function_t fn)
Setter for completion function.
Definition all.cpp:51
void set_activation(activation_function_t fn)
Setter for activation function.
Definition all.cpp:73
void activate(const trigger_activation_context_t &context) noexcept
Do activation of the trigger.
Definition all.cpp:114
bool check(const incident_status_t incident_status, const incident_info_t &info) const noexcept
Check for activation of the trigger.
Definition all.cpp:96
const mbox_id_t m_src_mbox_id
ID of source mbox of message/signal to activate the trigger.
trigger_t(incident_status_t incident_status, const agent_t &target, std::type_index msg_type, mbox_id_t src_mbox_id)
Initializing constructor.
Definition all.cpp:28
const agent_t & target_agent() const noexcept
Get the reference of the target agent.
Definition all.cpp:45
const incident_status_t m_incident_status
What should happen with initial message/signal.
bool requires_completion() const noexcept
Does this trigger require separate completion action?
Definition all.cpp:108
const mbox_id_t m_target_id
The unique ID of target's direct mbox.
void complete(const trigger_completion_context_t &context) noexcept
Do completion of a trigger.
Definition all.cpp:122
const agent_t & m_target_agent
A reference to the target agent.
outliving_reference_t< so_5::enveloped_msg::handler_invoker_t > m_invoker
Handler invoker that has to be used for extracted message.
Definition all.cpp:1079
invoker_for_message_extraction_t(outliving_reference_t< so_5::enveloped_msg::handler_invoker_t > invoker, so_5::enveloped_msg::access_context_t access_context)
Initializing constructor.
Definition all.cpp:1089
const so_5::enveloped_msg::access_context_t m_access_context
Context for accessing enveloped message.
Definition all.cpp:1082
bool handled() const noexcept
Has the message actually been handled?
Definition all.cpp:1128
void invoke(const so_5::enveloped_msg::payload_info_t &payload) noexcept override
Call an actual handler for the enveloped message/signal.
Definition all.cpp:1099
void invoke(const payload_info_t &payload) noexcept override
Call an actual handler for the enveloped message/signal.
Definition all.cpp:1061
no_handler_invoker_t(outliving_reference_t< special_envelope_t > owner)
Initializing constructor.
Definition all.cpp:1055
outliving_reference_t< special_envelope_t > m_owner
Owner of this invoker.
Definition all.cpp:1051
outliving_reference_t< handler_invoker_t > m_actual_invoker
Invoker to be used to call the actual event handler.
Definition all.cpp:1016
void invoke(const payload_info_t &payload) noexcept override
Call an actual handler for the enveloped message/signal.
Definition all.cpp:1030
pre_handler_hook_invoker_t(outliving_reference_t< special_envelope_t > owner, outliving_reference_t< handler_invoker_t > actual_invoker)
Intializing constructor.
Definition all.cpp:1020
outliving_reference_t< special_envelope_t > m_owner
Owner of this invoker.
Definition all.cpp:1013
outliving_reference_t< details::abstract_scenario_t > m_scenario
A testing scenario for that envelope.
Definition all.cpp:1000
special_envelope_t(outliving_reference_t< details::abstract_scenario_t > scenario, const execution_demand_t &demand)
Initializing constructor.
Definition all.cpp:1136
details::incident_info_t m_demand_info
Information about enveloped message.
Definition all.cpp:1002
void access_hook(access_context_t context, handler_invoker_t &invoker) noexcept override
Definition all.cpp:1165
delivery_result_t m_delivery_result
Was this message handled by a receiver?
Definition all.cpp:1007
void unfreeze() noexcept override
Issue a command to unfreeze all frozen agents.
Definition all.cpp:1490
outliving_reference_t< details::abstract_scenario_t > m_scenario
Testing scenario for that this object is created.
Definition all.cpp:1440
void on_unbind(agent_t *, event_queue_t *queue) noexcept override
A reaction to unbinding of an agent from some event_queue.
Definition all.cpp:1480
queue_mode_t m_mode
Mode of operation for new queues.
Definition all.cpp:1437
std::vector< special_event_queue_t * > m_created_queues
List of all queues created before unfreeze was called.
Definition all.cpp:1452
special_event_queue_hook_t(outliving_reference_t< details::abstract_scenario_t > scenario)
Definition all.cpp:1455
event_queue_t * on_bind(agent_t *, event_queue_t *original_queue) noexcept override
A reaction to binding of an agent to some event_queue.
Definition all.cpp:1462
std::vector< execution_demand_t > m_buffer
Local storage for demands to be used in buffered mode.
Definition all.cpp:1280
static bool is_ordinary_demand(const execution_demand_t &demand) noexcept
Definition all.cpp:1283
special_event_queue_t(outliving_reference_t< details::abstract_scenario_t > scenario, outliving_reference_t< event_queue_t > original_queue, queue_mode_t queue_mode)
Definition all.cpp:1303
void push_evt_finish(execution_demand_t demand) noexcept override
Enqueue a demand for evt_finish event.
Definition all.cpp:1344
queue_mode_t m_mode
The current mode of operation.
Definition all.cpp:1278
void push_evt_start(execution_demand_t demand) override
Enqueue a demand for evt_start event.
Definition all.cpp:1337
outliving_reference_t< event_queue_t > m_original_queue
Original event_queue.
Definition all.cpp:1275
void push(execution_demand_t demand) override
Enqueue new event to the queue.
Definition all.cpp:1313
outliving_reference_t< details::abstract_scenario_t > m_scenario
Testing scenario for that this queue was created.
Definition all.cpp:1273
stop_guard_for_unfreezer_t(outliving_reference_t< details::agent_unfreezer_t > unfreezer, outliving_reference_t< environment_t > env)
Definition all.cpp:1396
outliving_reference_t< details::agent_unfreezer_t > m_unfreezer
Definition all.cpp:1392
void stop() noexcept override
Perform stop-related actions.
Definition all.cpp:1404
step_definition_proxy_t define_step(nonempty_name_t step_name)
Start definition of a new scenario's step.
Definition all.cpp:1608
std::string stored_state_name(const std::string &step_name, const std::string &tag) const
Try to get stored name of an agent's state.
Definition all.cpp:1628
std::string stored_msg_inspection_result(const std::string &step_name, const std::string &tag) const
Try to get stored msg inspection result.
Definition all.cpp:1644
outliving_reference_t< details::abstract_scenario_t > m_scenario
bool has_stored_state_name(const std::string &step_name, const std::string &tag) const
Is there the inspection result?
Definition all.cpp:1636
bool has_stored_msg_inspection_result(const std::string &step_name, const std::string &tag) const
Is there the inspection result?
Definition all.cpp:1652
void run_for(std::chrono::steady_clock::duration run_time)
Runs the scenario for specified amount of time.
Definition all.cpp:1621
scenario_proxy_t(outliving_reference_t< details::abstract_scenario_t > scenario)
Definition all.cpp:1602
scenario_result_t result() const
Get the result of scenario execution.
Definition all.cpp:1615
scenario_result_t(scenario_status_t status)
The constructor for a case when there is only status of scenario.
scenario_result_t(scenario_status_t status, std::string description)
friend std::ostream & operator<<(std::ostream &to, const scenario_result_t &v)
Dump of object's content to ostream.
bool operator!=(const scenario_result_t &o) const noexcept
Check for inequality.
bool operator==(const scenario_result_t &o) const noexcept
Check for equality.
A special object that should be used for definition of a step of a testing scenario.
step_definition_proxy_t(details::abstract_scenario_step_t *step)
Initializing constructor.
step_definition_proxy_t & impact(Lambda &&lambda)
Add preactivation action in form of lambda-object.
step_definition_proxy_t & when_all(details::trigger_holder_t< Status > event, Args &&...args)
Add a list of tiggers for activation of that step.
void append_trigger_to(details::trigger_container_t &to, details::trigger_holder_t< Status > event, Args &&...args)
void append_constraint_to(details::constraint_container_t &to, details::constraint_unique_ptr_t head, Args &&...tail)
step_definition_proxy_t & impact(Target &&target, Args &&...args)
Define a preactivation action in form of sending a message/signal to the specified target.
step_definition_proxy_t & constraints(details::constraint_unique_ptr_t head, Args &&...tail)
Add a list of constraints for that step.
step_definition_proxy_t & when_any(details::trigger_holder_t< Status > event, Args &&...args)
Add a list of tiggers for activation of that step.
step_definition_proxy_t & when(details::trigger_holder_t< Status > event)
Add a tigger for activation of that step.
A special testing environment that should be used for testing of agents.
void stop()
Send stop signal to environment.
Definition all.cpp:1694
environment_t & environment() const
Access to wrapped environment.
Definition all.cpp:1688
testing_env_t(environment_params_t &&env_params)
Definition all.cpp:1671
scenario_proxy_t scenario() noexcept
Access to the associated scenario.
Definition all.cpp:1713
void join()
Wait for complete finish of environment's work.
Definition all.cpp:1700
void tune_environment_on_start(environment_t &env)
Definition all.cpp:1719
void stop_then_join()
Send stop signal and wait for complete finish of environment's work.
Definition all.cpp:1706
testing_env_t(so_5::generic_simple_so_env_params_tuner_t env_params_tuner)
A constructor that allows to tune environment's parameters.
Definition all.cpp:1666
An interface of the additional SObjectizer Environment layer.
Definition so_layer.hpp:31
A base class for agent messages.
Definition message.hpp:47
friend message_kind_t message_kind(const so_5::intrusive_ptr_t< message_t > &what)
Helper method for quering kind of the message.
Definition message.hpp:154
A class for the name which cannot be empty.
Helper class for indication of long-lived reference via its type.
Definition outliving.hpp:98
An interface of stop_guard entity.
A wrapped environment.
#define SO_5_TYPE
Definition declspec.hpp:46
#define SO_5_THROW_EXCEPTION(error_code, desc)
Definition exception.hpp:74
Some reusable and low-level classes/functions which can be used in public header files.
envelope_t & message_to_envelope(const message_ref_t &src_msg)
A helper function for casting message instance to envelope instance.
access_context_t
Information about context on that enveloped message is handled.
trigger_holder_t< incident_status_t::handled > operator&(trigger_holder_t< incident_status_t::handled > &&old_holder, wait_event_handler_completion_t)
A helper operator to create a tigger that requires the completion of an event handler.
trigger_holder_t< Status > operator&(trigger_holder_t< Status > &&old_holder, store_msg_inspection_result_t inspection_info)
A helper operator to create a tigger that inspects the incoming message and stores the result into th...
trigger_holder_t< incident_status_t::handled > operator&(const mbox_t &from, receives_indicator_t< Msg >)
A helper operator to create a tigger that receives a message/signal from specified mbox.
trigger_holder_t< Status > operator&(const so_5::agent_t &agent, const trigger_source_t< Status > &src)
A helper operator to create a trigger for the specified agent.
incident_status_t
What happened with source of an event.
trigger_holder_t< incident_status_t::handled > operator&(trigger_holder_t< incident_status_t::handled > &&old_holder, store_agent_state_name_t data_to_store)
A helper operator to create a tigger that stores the name of the current agent's state.
environment_params_t make_special_params(outliving_reference_t< testing_env_t::internals_t > internals, environment_params_t &&params)
Definition all.cpp:1582
void setup_special_queue_hook(outliving_reference_t< testing_env_t::internals_t > internals, environment_params_t &to)
Definition all.cpp:1558
environment_params_t make_tuned_params(so_5::generic_simple_so_env_params_tuner_t env_params_tuner)
Definition all.cpp:1571
queue_mode_t
A mode of work for special_event_queue.
Definition all.cpp:1224
@ direct
All messages should go to the original queue without buffering.
@ buffer
All messages must be stored locally.
scenario_result_t completed()
Create a value that means that scenario completed successfuly.
details::receives_indicator_t< Msg > receives()
Helper function to be used for a trigger that receives a message/singal from a mbox.
details::trigger_source_t< details::incident_status_t::ignored > ignores()
Define a trigger that activates when an agent rejects a message from the direct mbox.
@ completed
Testing scenario is successfuly completed.
details::constraint_unique_ptr_t not_after(std::chrono::steady_clock::duration pause)
Create a constraint not-after.
details::store_msg_inspection_result_t inspect_msg(std::string tag, Lambda &&inspector)
Create a special marker for a trigger for inspecting an incoming message and storing the inspection r...
details::store_agent_state_name_t store_state_name(std::string tag)
Create a special marker for a trigger for storing agent's state name inside scenario.
details::trigger_source_t< details::incident_status_t::handled > reacts_to()
Define a trigger that activates when an agent receives and handles a message from the direct mbox.
details::trigger_source_t< details::incident_status_t::handled > reacts_to(const so_5::mbox_t &mbox)
Define a trigger that activates when an agent receives and handles a message from the specific mbox.
details::constraint_unique_ptr_t not_before(std::chrono::steady_clock::duration pause)
Create a constraint not-before.
details::trigger_source_t< details::incident_status_t::ignored > ignores(const so_5::mbox_t &mbox)
Define a trigger that activates when an agent rejects a message from the direct mbox.
details::wait_event_handler_completion_t wait_event_handler_completion()
Create a special marker for a trigger that requires waiting for completion of an event handler.
Private part of message limit implementation.
Definition agent.cpp:33
@ user_type_message
Message is an user type message.
@ enveloped_msg
Message is an envelope with some other message inside.
A description of event execution demand.
demand_handler_pfn_t m_demand_handler
Demand handler.
const std::type_index m_msg_type
Type of message or signal.
incident_info_t(const agent_t *agent, const std::type_index &msg_type, mbox_id_t src_mbox_id)
mbox_id_t m_src_mbox_id
ID of mbox from that message/signal was received.
Special indicator to be used in implementation of receives trigger.
A special data object for case of store-state-name completion action.
A special data object for case when a message inspector has to be used on an incoming message.
std::function< std::string(const message_ref_t &) > m_inspector
Inspector for a message.
Description of context on that an attempt to activate a trigger is performing.
const scenario_in_progress_accessor_t & m_scenario_accessor
Access to the running scenario.
abstract_scenario_step_t & m_step
The current step for that activation is being performed.
trigger_source_t(std::type_index msg_type, mbox_id_t src_mbox_id)
A special data object for case when a step should be completed only after returning from the event ha...
A helper object for synchronization between helper worker where testing environment is launched and u...
Definition all.cpp:1525
Internal data for testing environment.
Definition all.cpp:1538
impl::special_event_queue_hook_t m_special_hook
Definition all.cpp:1540
static std::unique_ptr< internals_t > make()
Definition all.cpp:1552
A helper class for detection of payload type of message.
Definition message.hpp:783