SObjectizer  5.8
Loading...
Searching...
No Matches
all.cpp
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#include <so_5/experimental/testing/v1/all.hpp>
13
14#include <so_5/details/safe_cv_wait_for.hpp>
15
16#include <so_5/impl/enveloped_msg_details.hpp>
17
18namespace so_5 {
19
20namespace experimental {
21
22namespace testing {
23
24inline namespace v1 {
25
26namespace details {
27
28trigger_t::trigger_t(
29 incident_status_t incident_status,
30 const agent_t & target,
31 std::type_index msg_type,
32 mbox_id_t src_mbox_id )
33 : m_incident_status( incident_status )
34 , m_target_agent( target )
37 , m_src_mbox_id( src_mbox_id )
38 {}
39
40trigger_t::~trigger_t()
41 {}
42
43[[nodiscard]]
44const agent_t &
45trigger_t::target_agent() const noexcept
46 {
47 return m_target_agent;
48 }
49
50void
51trigger_t::set_completion( completion_function_t fn )
52 {
53 if( !m_completion )
54 // Simple store the new function.
55 m_completion = std::move(fn);
56 else
57 {
58 // We have to join the old and the new ones.
59 completion_function_t joined_version =
60 [old_fn = m_completion, new_fn = std::move(fn)]
61 ( const trigger_completion_context_t & ctx ) {
62 // NOTE: don't care about exception because completion
63 // functions have to be noexcept.
64 old_fn( ctx );
65 new_fn( ctx );
66 };
67
68 m_completion = std::move(joined_version);
69 }
70 }
71
72void
73trigger_t::set_activation( activation_function_t fn )
74 {
75 if( !m_activation )
76 // Simple store the new function.
77 m_activation = std::move(fn);
78 else
79 {
80 // We have to join the old and the new ones.
81 activation_function_t joined_version =
82 [old_fn = m_activation, new_fn = std::move(fn)]
83 ( const trigger_activation_context_t & ctx ) {
84 // NOTE: don't care about exception because completion
85 // functions have to be noexcept.
86 old_fn( ctx );
87 new_fn( ctx );
88 };
89
90 m_activation = std::move(joined_version);
91 }
92 }
93
94[[nodiscard]]
95bool
96trigger_t::check(
97 const incident_status_t incident_status,
98 const incident_info_t & info ) const noexcept
99 {
100 return incident_status == m_incident_status
101 && info.m_agent->so_direct_mbox()->id() == m_target_id
102 && info.m_msg_type == m_msg_type
103 && info.m_src_mbox_id == m_src_mbox_id;
104 }
105
106[[nodiscard]]
107bool
108trigger_t::requires_completion() const noexcept
109 {
110 return static_cast<bool>(m_completion);
111 }
112
113void
114trigger_t::activate(
115 const trigger_activation_context_t & context ) noexcept
116 {
117 if( m_activation )
118 m_activation( context );
119 }
120
121void
122trigger_t::complete(
123 const trigger_completion_context_t & context ) noexcept
124 {
125 m_completion( context );
126 }
127
128/*!
129 * \brief An actual implementation of step of testing scenario.
130 *
131 * \since v.5.5.24
132 */
133class real_scenario_step_t final : public abstract_scenario_step_t
134 {
135 //! Type of container for preactivation actions.
136 using preactivate_actions_container_t =
137 std::vector< preactivate_action_t >;
138
139 //! Name of a step.
141
142 //! All preactivation actions.
144
145 //! All constraints.
147
148 //! All triggers.
149 /*!
150 * This containers holds all triggers as for when_all, as
151 * for when_any case.
152 *
153 * There is an important trick: activated triggers moved to
154 * the end of that container. It means that first N triggers
155 * in that containers wait for activation. The value of N
156 * is defined by m_last_non_activated_trigger attribute.
157 */
159 //! Index of last trigger in the first part of trigger's container.
160 /*!
161 * Receives actual value in setup_triggers() method.
162 * At the begining, if m_triggers.size() is not 0, it holds
163 * (m_triggers.size()-1). Then it is decremented after activation
164 * of yet another trigger.
165 */
167
168 //! Count of triggers those should be activated for activation
169 //! of the step.
170 /*!
171 * Receives actual value in setup_triggers() method.
172 */
174 //! Count of triggers those are activated.
176
177 //! Count of activated triggers those are not completed yet.
178 /*!
179 * Condition for completion of the step looks like:
180 * \code
181 * (m_triggers_to_activate == m_triggers_activated) && (0u == m_triggers_to_completion)
182 * \endcode
183 */
185
186 //! The current state of the step.
188
189 public :
191 std::string name )
192 : m_name( std::move(name) )
193 {}
194
195 [[nodiscard]]
196 const std::string &
197 name() const noexcept override
198 {
199 return m_name;
200 }
201
202 void
203 preactivate() noexcept override
204 {
206 }
207
208 [[nodiscard]]
209 token_t
211 const scenario_in_progress_accessor_t & scenario_accessor,
212 const incident_info_t & info,
213 const message_ref_t & incoming_msg ) noexcept override
214 {
215 token_t result;
216
218 result = try_activate(
219 trigger_activation_context_t{
220 scenario_accessor,
221 *this,
222 incoming_msg
223 },
225
226 return result;
227 }
228
229 void
231 const scenario_in_progress_accessor_t & scenario_accessor,
232 token_t token ) noexcept override
233 {
234 if( token.valid() )
235 {
237 trigger_completion_context_t{
238 scenario_accessor,
239 *this
240 } );
241
242 --m_triggers_to_completion;
243
244 // If step activated and there is no triggers to
245 // be completed then the step can be completed.
246 // But there can be cases when m_triggers_to_completion
247 // is zero, but the step is not activated yet
248 // (for example when `when_all` is used).
249 if( !m_triggers_to_completion &&
250 status_t::active == m_status )
252 }
253 }
254
255 void
257 const scenario_in_progress_accessor_t & scenario_accessor,
258 const incident_info_t & info,
259 const message_ref_t & incoming_msg ) noexcept override
260 {
262 (void)try_activate(
263 trigger_activation_context_t{
264 scenario_accessor,
265 *this,
266 incoming_msg,
267 },
269 }
270
271 [[nodiscard]]
273 status() const noexcept override
274 {
275 return m_status;
276 }
277
278 void
280 preactivate_action_t action ) override
281 {
282 m_preactivate_actions.emplace_back( std::move(action) );
283 }
284
285 void
287 trigger_container_t triggers,
288 std::size_t triggers_to_activate ) noexcept override
289 {
290 using std::swap;
291
292 swap( m_triggers, triggers );
293 m_triggers_to_activate = triggers_to_activate;
294
295 m_last_non_activated_trigger = m_triggers.size();
296 if( m_last_non_activated_trigger )
297 --m_last_non_activated_trigger;
298 }
299
300 void
302 constraint_container_t constraints ) noexcept override
303 {
304 using std::swap;
305
306 swap( m_constraints, constraints );
307 }
308
309 private :
310 //! Change the state of the step and perform all necessary
311 //! transitional actions.
312 /*!
313 * If a new state is status_t::preactivated then preactivation
314 * actions will be called and constraint_t::start() will be called
315 * for all constraints.
316 *
317 * If a new state is status_t::completed then constraint_t::finish()
318 * will be called for all constraints.
319 */
320 void
321 change_status( status_t status ) noexcept
322 {
323 m_status = status;
324 switch( status )
325 {
327 {
328 for( auto & act : m_preactivate_actions )
329 act();
330
331 for( auto & c : m_constraints )
332 c->start();
333 }
334 break;
336 {
337 for( auto & c : m_constraints )
338 c->finish();
339 }
340 break;
341
342 case status_t::passive: break;
343 case status_t::active: break;
344 }
345 }
346
347 //! An attempt to check constraints for a new incident.
348 /*!
349 * \retval true if all constraints fullfilled.
350 */
351 [[nodiscard]]
352 bool
354 const incident_status_t incident_status,
355 const incident_info_t & info ) const noexcept
356 {
357 for( auto & c : m_constraints )
358 if( !c->check( incident_status, info ) )
359 return false;
360
361 return true;
362 }
363
364 //! An attempt to activate the step when a new incident arrives.
365 [[nodiscard]]
366 token_t
368 const trigger_activation_context_t & context,
369 const incident_status_t incident_status,
370 const incident_info_t & info ) noexcept
371 {
372 token_t result;
373
374 // All constraints must be fullfilled.
375 if( !try_pass_constraints( incident_status, info ) )
376 return result;
377
378 // Check triggers those are not activated yet.
379 // Those triggers are in the first part of m_triggers.
380 // It is safe to add 1 to m_last_non_activated_trigger, because
381 // this method is called only if there is at least one
382 // non-activated trigger in the triggers container.
383 auto end = std::begin(m_triggers) +
384 static_cast<trigger_container_t::iterator::difference_type>(
385 m_last_non_activated_trigger + 1u);
386
387 auto it = std::find_if(
388 std::begin(m_triggers), end,
389 [incident_status, &info]
390 ( trigger_unique_ptr_t & trigger ) {
391 return trigger->check( incident_status, info );
392 } );
393
394 if( it == end )
395 return result;
396
397 trigger_t * active_trigger = it->get();
398 // Trigger has to be activated.
399 active_trigger->activate( context );
400
401 // Actual trigger should be stored separatelly.
402 if( m_last_non_activated_trigger )
403 {
404 // Actual trigger should be moved to the end of triggers list.
405 std::swap( *it, m_triggers[ m_last_non_activated_trigger ] );
406 --m_last_non_activated_trigger;
407 }
408
409 ++m_triggers_activated;
410 if( active_trigger->requires_completion() )
411 {
412 ++m_triggers_to_completion;
413 // Actual trigger should be returned in result token.
414 result = token_t{ active_trigger };
415 }
416
417 // Now the new state of the step can be calculated.
418 if( m_triggers_activated == m_triggers_to_activate )
419 {
420 // All expected triggers are activated.
421 // New state should be `active` or `completed`.
422 change_status( (0u != m_triggers_to_completion) ?
423 status_t::active : status_t::completed );
424 }
425
426 return result;
427 }
428 };
429
430/*!
431 * \brief An interface for object that will unfreeze all registered
432 * agents when testing scenario starts.
433 *
434 * It is necessary for testing scenarios to keep all registered agents in
435 * frozen state. Those agents should be present in the Environment but should
436 * not receive nor handle messages. It is done by a special implementation of
437 * event queues and event_queue_hook.
438 *
439 * But at some moment all frozen agents should become unfrozen. This moment is
440 * known only for testing scenario. It means that testing scenario should
441 * issue a command to unfreeze all frozen agents.
442 *
443 * This interface allows testing scenario to unfreeze agents but hides the
444 * details of this procedure.
445 *
446 * \since v.5.5.24
447 */
449 {
452
455
456 public :
457 agent_unfreezer_t() = default;
458 virtual ~agent_unfreezer_t() = default;
459
460 //! Issue a command to unfreeze all frozen agents.
461 virtual void
462 unfreeze() noexcept = 0;
463 };
464
465/*!
466 * \brief The actual implementation of testing scenario.
467 *
468 * \since v.5.5.24
469 */
470class real_scenario_t final : public abstract_scenario_t
471 {
472 private :
473 //! Object lock.
474 /*!
475 * \note
476 * It is mutable because it has to be used in a const method.
477 */
478 mutable std::mutex m_lock;
479 //! Condition variable for waiting completion of the scenario.
481
482 //! The current state of the scenario.
484
485 //! Scenario's steps.
486 /*!
487 * Can be empty.
488 */
490 //! Set of active step those are not completed yet.
491 /*!
492 * If a step switches from preactivated state to the completed
493 * without staying in active state then this step won't go
494 * to this set.
495 */
497
498 //! Index of the current preactivated step.
500
501 //! Type of container for holding stored state names.
502 /*!
503 * The key consists of step_name and tag_name.
504 */
505 using state_name_map_t = std::map<
506 std::pair< std::string, std::string >,
507 std::string >;
508
509 //! Type of container for holding stored inspection results for messages.
510 /*!
511 * The key consists of step_name and tag_name.
512 */
513 using inspection_result_map_t = std::map<
514 std::pair< std::string, std::string >,
515 std::string >;
516
517 //! Container for holding stored state names.
519
520 //! Container for holding stored inspection results for messages.
521 /*!
522 * \since v.5.8.3
523 */
525
526 //! Unfreezer for registered agents.
527 /*!
528 * \note Will receive an actual value later.
529 */
531
532 public :
533 real_scenario_t() = default;
534
535 //! Set the unfreezer for registered agents.
536 /*!
537 * \attention
538 * This method must be called before start of the scenario.
539 */
540 void
541 setup_unfreezer( agent_unfreezer_t & unfreezer ) noexcept
542 {
543 m_unfreezer = &unfreezer;
544 }
545
546 [[nodiscard]]
548 define_step( nonempty_name_t step_name ) override
549 {
550 std::lock_guard< std::mutex > lock{ m_lock };
551 if( scenario_status_t::not_started != m_status )
553 rc_unable_to_define_new_step,
554 "new testing scenario step can be defined only when "
555 "scenario is not started yet" );
556
557 m_steps.emplace_back(
558 std::make_unique< real_scenario_step_t >(
559 step_name.giveout_value() ) );
560
561 return { m_steps.back().get() };
562 }
563
564 [[nodiscard]]
566 result() const noexcept override
567 {
568 std::lock_guard< std::mutex > lock{ m_lock };
569
572 else
573 return { m_status, describe_current_state() };
574 }
575
576 void
577 run_for( std::chrono::steady_clock::duration run_time ) override
578 {
579 std::unique_lock< std::mutex > lock{ m_lock };
581 {
582 // Note. There is a trick: unfreezing of agents is performed
583 // when scenario is locked. It means that event handlers of
584 // dispatched messages will wait while that method completes.
586
587 if( m_steps.empty() )
589 else
590 {
593
594 ::so_5::details::wait_for_big_interval(
595 lock,
596 m_completion_cv,
597 run_time,
598 [this]{ return scenario_status_t::completed == m_status; });
601 }
602 }
603 }
604
605 [[nodiscard]]
606 token_t
608 const incident_info_t & info,
609 const message_ref_t & incoming_msg ) noexcept override
610 {
611 token_t result;
612
613 std::lock_guard< std::mutex > lock{ m_lock };
614
615 // There can be cases when pre_handler_hook is called
616 // when testing scenario is already finished.
617 if( scenario_status_t::in_progress == m_status &&
618 m_waiting_step_index < m_steps.size() )
619 {
620 result = react_on_pre_handler_hook( info, incoming_msg );
621 }
622
623 return result;
624 }
625
626 void
628 token_t token ) noexcept override
629 {
630 std::lock_guard< std::mutex > lock{ m_lock };
631
632 // There can be cases when post_handler_hook is called
633 // when testing scenario is already finished.
634 // Also we can ignore that event if token has no valid info inside.
636 {
637 // There is a waiting step for that post_handler_hook
638 // should be called.
639 auto & step_to_check = token.activated_step();
640
641 step_to_check.post_handler_hook(
643 token.step_token() );
644
646 == step_to_check.status() )
647 {
648 m_active_steps.erase( &step_to_check );
649
651 }
652 }
653 }
654
655 void
657 const incident_info_t & info,
658 const message_ref_t & incoming_msg ) noexcept override
659 {
660 std::lock_guard< std::mutex > lock{ m_lock };
661
662 // There can be cases when no_handler_hook is called
663 // when testing scenario is already finished.
664 if( scenario_status_t::in_progress == m_status &&
665 m_waiting_step_index < m_steps.size() )
666 {
667 react_on_no_handler_hook( info, incoming_msg );
668 }
669 }
670
671 void
673 const scenario_in_progress_accessor_t & /*accessor*/,
674 const abstract_scenario_step_t & step,
675 const std::string & tag,
676 const std::string & state_name ) override
677 {
678 m_stored_states[ std::make_pair(step.name(), tag) ] = state_name;
679 }
680
681 void
683 const scenario_in_progress_accessor_t & /*accessor*/,
684 const abstract_scenario_step_t & step,
685 const std::string & tag,
686 const std::string & inspection_result ) override
687 {
688 m_stored_inspection_results[ std::make_pair(step.name(), tag) ]
689 = inspection_result;
690 }
691
692 [[nodiscard]]
693 std::string
695 const std::string & step_name,
696 const std::string & tag ) const override
697 {
698 std::lock_guard< std::mutex > lock{ m_lock };
699
700 if( scenario_status_t::completed != m_status )
702 rc_scenario_must_be_completed,
703 "scenario must be completed before call to "
704 "stored_state_name()" );
705
706 const auto it = m_stored_states.find(
707 std::make_pair(step_name, tag) );
708 if( it == m_stored_states.end() )
710 rc_stored_state_name_not_found,
711 "unable to find stored state name for <" +
712 step_name + "," + tag + ">" );
713
714 return it->second;
715 }
716
717 [[nodiscard]]
718 bool
720 const std::string & step_name,
721 const std::string & tag ) const override
722 {
723 std::lock_guard< std::mutex > lock{ m_lock };
724
725 if( scenario_status_t::completed != m_status )
727 rc_scenario_must_be_completed,
728 "scenario must be completed before call to "
729 "stored_state_name()" );
730
731 return m_stored_states.end() != m_stored_states.find(
732 std::make_pair(step_name, tag) );
733 }
734
735 [[nodiscard]]
736 std::string
738 const std::string & step_name,
739 const std::string & tag ) const override
740 {
741 std::lock_guard< std::mutex > lock{ m_lock };
742
743 if( scenario_status_t::completed != m_status )
745 rc_scenario_must_be_completed,
746 "scenario must be completed before call to "
747 "stored_msg_inspection_result()" );
748
749 const auto it = m_stored_inspection_results.find(
750 std::make_pair(step_name, tag) );
751 if( it == m_stored_inspection_results.end() )
753 rc_stored_msg_inspection_result_not_found,
754 "unable to find stored msg inspection result for <" +
755 step_name + "," + tag + ">" );
756
757 return it->second;
758 }
759
760 [[nodiscard]]
761 bool
763 const std::string & step_name,
764 const std::string & tag ) const override
765 {
766 std::lock_guard< std::mutex > lock{ m_lock };
767
768 if( scenario_status_t::completed != m_status )
770 rc_scenario_must_be_completed,
771 "scenario must be completed before call to "
772 "has_stored_msg_inspection_result()" );
773
774 return m_stored_inspection_results.end() !=
775 m_stored_inspection_results.find(
776 std::make_pair(step_name, tag) );
777 }
778
779
780 private :
781 void
783 {
784 m_steps[ m_waiting_step_index ]->preactivate();
785 }
786
787 [[nodiscard]]
788 token_t
790 const incident_info_t & info,
791 const message_ref_t & incoming_msg ) noexcept
792 {
793 token_t result;
794
795 // pre_handler_hook on the current waiting step must be called.
796 auto & step_to_check = *(m_steps[ m_waiting_step_index ]);
797 auto step_token = step_to_check.pre_handler_hook(
798 make_accessor(),
799 info,
800 incoming_msg );
801
802 if( step_token.valid() )
803 // Because step's token is not NULL, we should return
804 // our valid token.
805 // This is the case when token is necessary for the
806 // subsequent post_handler_hook() call.
807 result = token_t{ &step_to_check, std::move(step_token) };
808
809 // The step can change its status...
810 switch( step_to_check.status() )
811 {
812 case abstract_scenario_step_t::status_t::active :
813 // The step is activated and we need to wait its completion.
814 m_active_steps.insert( &step_to_check );
815 switch_to_next_step_if_possible();
816 break;
817
818 case abstract_scenario_step_t::status_t::completed :
819 // There is no need to wait the completion of the step.
820 switch_to_next_step_if_possible();
821
822 check_scenario_completion();
823 break;
824
825 case abstract_scenario_step_t::status_t::passive: break;
826 case abstract_scenario_step_t::status_t::preactivated: break;
827 }
828
829 return result;
830 }
831
832 void
834 const incident_info_t & info,
835 const message_ref_t & incoming_msg ) noexcept
836 {
837 // no_handler_hook on the current waiting step must be called.
838 auto & step_to_check = *(m_steps[ m_waiting_step_index ]);
839 step_to_check.no_handler_hook(
840 make_accessor(),
841 info,
842 incoming_msg );
843
844 // The step can change its status...
845 switch( step_to_check.status() )
846 {
847 case abstract_scenario_step_t::status_t::active :
848 // The step is activated and we need to wait its completion.
849 m_active_steps.insert( &step_to_check );
850 switch_to_next_step_if_possible();
851 break;
852
853 case abstract_scenario_step_t::status_t::completed :
854 // There is no need to wait the completion of the step.
855 switch_to_next_step_if_possible();
856
857 check_scenario_completion();
858 break;
859
860 case abstract_scenario_step_t::status_t::passive: break;
861 case abstract_scenario_step_t::status_t::preactivated: break;
862 }
863 }
864
865 void
867 {
868 ++m_waiting_step_index;
869 if( m_waiting_step_index < m_steps.size() )
870 {
872 }
873 }
874
875 //! Checks the possibility of completion of the scenario and
876 //! completes scenario if it is possible.
877 void
879 {
880 if( m_active_steps.empty() &&
881 m_waiting_step_index >= m_steps.size() )
882 {
884 m_completion_cv.notify_all();
885 }
886 }
887
888 [[nodiscard]]
889 std::string
891 {
892 std::ostringstream ss;
893
894 if( m_waiting_step_index < m_steps.size() )
895 ss << "preactivated step:"
896 << m_steps[ m_waiting_step_index ]->name();
897 else
898 ss << "all steps handled";
899
900 ss << ";";
901
902 if( !m_active_steps.empty() )
903 {
904 ss << " active steps:{";
905 bool need_comma = false;
906 for( auto * s : m_active_steps )
907 {
908 if( need_comma )
909 ss << ", ";
910 else
911 need_comma = true;
912
913 ss << s->name();
914 }
915 ss << "};";
916 }
917
918 if( !m_stored_states.empty() )
919 {
920 ss << " stored states:{";
921 bool need_comma = false;
922 for( auto & p : m_stored_states )
923 {
924 if( need_comma )
925 ss << ", ";
926 else
927 need_comma = true;
928
929 ss << "[" << p.first.first << ", "
930 << p.first.second << "]="
931 << p.second;
932 }
933 ss << "};";
934 }
935
936 return ss.str();
937 }
938 };
939
940//
941// msg_catcher_map_layer_t
942//
943
945{
946
947msg_catcher_map_layer_t::msg_catcher_map_layer_t() = default;
948msg_catcher_map_layer_t::~msg_catcher_map_layer_t() noexcept = default;
949
950} /* namespace mbox_receives_msg_impl */
951
952} /* namespace details */
953
954namespace impl {
955
956namespace details = so_5::experimental::testing::v1::details;
957
958
959/*!
960 * \brief A special envelope that is necessary for testing scenarios.
961 *
962 * Testing scenario should know about every handled or rejected message
963 * delivered to agents. To make this possible a trick with enveloped
964 * messages is used: every message is enveloped into a special envelope.
965 * This envelope has a reference to testing scenario and informs this
966 * scenario when message is handled by a receiver.
967 *
968 * But the situation with rejected messages is not that simple.
969 * We assume that if access_hook() wasn't called then message was rejected.
970 * Because of that envelope has a boolean flag m_handled that is set
971 * in access_hook(). This flag is analyzed in the destructor and, if it
972 * is not set, we assume that message was rejected.
973 *
974 * In theory there can be cases when envelope is destroyed without delivery
975 * to an agent. But for testing scenarios we just ignore those case.
976 *
977 * \since v.5.5.24
978 */
979class special_envelope_t final : public so_5::enveloped_msg::envelope_t
980 {
981 //! Delivery result for a message inside the envelope.
982 //!
983 //! If the message is another envelope then there could be
984 //! case when the actual message will be suppressed by a nested envelope.
985 //! This case has to be handled separatelly: in that case message
986 //! isn't handled nor delivered.
987 //!
988 //! \since v.5.8.3
990 {
991 //! Message ignored by the destination agent.
992 ignored,
993 //! Message delivered to the destination agent.
994 delivered,
995 //! Message suppressed by a nested envelope.
997 };
998
999 //! A testing scenario for that envelope.
1001 //! Information about enveloped message.
1002 details::incident_info_t m_demand_info;
1003 //! Enveloped message.
1004 message_ref_t m_message;
1005
1006 //! Was this message handled by a receiver?
1008
1009 //! A special invoker to be used to call pre_handler_hook.
1010 class pre_handler_hook_invoker_t final : public handler_invoker_t
1011 {
1012 //! Owner of this invoker.
1013 outliving_reference_t< special_envelope_t > m_owner;
1014
1015 //! Invoker to be used to call the actual event handler.
1017
1018 public:
1019 //! Intializing constructor.
1021 //! Owner of this invoker.
1022 outliving_reference_t< special_envelope_t > owner,
1023 //! Invoker to be used to call the actual event handler.
1024 outliving_reference_t< handler_invoker_t > actual_invoker )
1025 : m_owner{ owner }
1027 {}
1028
1029 void
1030 invoke( const payload_info_t & payload ) noexcept override
1031 {
1032 // We must get token...
1033 auto token = m_owner.get()
1034 .m_scenario.get()
1035 .pre_handler_hook(
1036 m_owner.get().m_demand_info,
1037 payload.message() );
1038
1039 // Actual event handler has to be called.
1040 m_actual_invoker.get().invoke( payload );
1041
1042 // And now the token must be passed back.
1043 m_owner.get().m_scenario.get().post_handler_hook( token );
1044 }
1045 };
1046
1047 //! A special invoker to be used to call no_handler_hook.
1048 class no_handler_invoker_t final : public handler_invoker_t
1049 {
1050 //! Owner of this invoker.
1051 outliving_reference_t< special_envelope_t > m_owner;
1052
1053 public:
1054 //! Initializing constructor.
1056 outliving_reference_t< special_envelope_t > owner )
1057 : m_owner{ owner }
1058 {}
1059
1060 void
1061 invoke( const payload_info_t & payload ) noexcept override
1062 {
1063 m_owner.get().m_scenario.get().no_handler_hook(
1064 m_owner.get().m_demand_info,
1065 payload.message() );
1066 }
1067 };
1068
1069 //! Special handler invoker that tries to extract the actual message.
1070 //!
1071 //! This invoker is necessary because the message may be enveloped
1072 //! more than once and one of the nested envelopes may suppress it.
1073 //!
1074 //! \since v.5.8.3
1075 class invoker_for_message_extraction_t final
1077 {
1078 //! Handler invoker that has to be used for extracted message.
1080
1081 //! Context for accessing enveloped message.
1083
1084 //! Has the message actually been handled?
1085 bool m_handled{ false };
1086
1087 public:
1088 //! Initializing constructor.
1090 //! Handler invoker that has to be used if message is extracted.
1092 //! Context for accessing enveloped message.
1093 so_5::enveloped_msg::access_context_t access_context )
1094 : m_invoker{ invoker }
1095 , m_access_context{ access_context }
1096 {}
1097
1098 void
1099 invoke( const so_5::enveloped_msg::payload_info_t & payload ) noexcept override
1100 {
1101 const auto msg_kind = message_kind( payload.message() );
1102
1103 switch( msg_kind )
1104 {
1105 case message_t::kind_t::signal : [[fallthrough]];
1106 case message_t::kind_t::classical_message : [[fallthrough]];
1108 m_handled = true;
1109 m_invoker.get().invoke( payload );
1110 break;
1111
1112 case message_t::kind_t::enveloped_msg :
1113 {
1114 // Do recurvise call.
1115 // Value for was_handled will be detected in the
1116 // nested call.
1117 auto & nested_envelope =
1119 payload.message() );
1120 nested_envelope.access_hook( m_access_context, *this );
1121 }
1122 break;
1123 }
1124 }
1125
1126 //! Has the message actually been handled?
1127 [[nodiscard]] bool
1128 handled() const noexcept
1129 {
1130 return m_handled;
1131 }
1132 };
1133
1134 public :
1135 //! Initializing constructor.
1143 ~special_envelope_t() noexcept override
1144 {
1145 // If message wasn't handled we assume that agent rejected
1146 // this message.
1148 {
1149 // But we have to check that message isn't suppressed by
1150 // a nested envelope.
1151 no_handler_invoker_t invoker_no_handler_hook{
1152 outliving_mutable( *this )
1153 };
1154 invoker_for_message_extraction_t special_invoker{
1155 outliving_mutable( invoker_no_handler_hook ),
1156 access_context_t::inspection
1157 };
1158 // If the message is not suppressed then no_handler_hook
1159 // will be called by invoker_no_handler_hook.
1160 special_invoker.invoke( payload_info_t{ m_message } );
1161 }
1162 }
1163
1164 void
1166 //! Why this hook is called.
1167 access_context_t context,
1168 //! Proxy object which can call an actual event handler.
1169 handler_invoker_t & invoker ) noexcept override
1170 {
1171 switch( context )
1172 {
1173 case access_context_t::handler_found :
1174 {
1175 // This invoker will call pre/post_handler_hooks and
1176 // the actual event-handler.
1177 pre_handler_hook_invoker_t pre_handler_hook_invoker{
1178 outliving_mutable( *this ),
1179 outliving_mutable( invoker )
1180 };
1181
1182 // The message can be envelope itself.
1183 // And the envelope can hide the message and does not return it.
1184 // This case has to be handled.
1185 invoker_for_message_extraction_t special_invoker{
1186 outliving_mutable( pre_handler_hook_invoker ),
1187 context
1188 };
1189 // The special invoker will do the back call of
1190 // call_handler_invoker method.
1191 special_invoker.invoke( payload_info_t{ m_message } );
1192
1193 // If we don't change m_handled flag there the
1194 // destructor will call no_handler_hook().
1195 m_delivery_result = special_invoker.handled()
1198 }
1199 break;
1200
1201 case access_context_t::transformation :
1202 invoker.invoke( payload_info_t{ m_message } );
1203 break;
1204
1205 case access_context_t::inspection :
1206 invoker.invoke( payload_info_t{ m_message } );
1207 break;
1208 }
1209 }
1210 };
1211
1212/*!
1213 * \brief A mode of work for special_event_queue.
1214 *
1215 * Special event queue can work in two modes:
1216 * - buffered, when all messages must be stored in a local queue;
1217 * - direct, when all messages go directly to the original queue.
1218 *
1219 * This enumeration describes mode of operation for special_event_queue_t.
1220 *
1221 * \since v.5.5.24
1222 */
1224 {
1225 //! All messages must be stored locally.
1226 buffer,
1227 //! All messages should go to the original queue without buffering.
1228 direct
1229 };
1230
1231/*!
1232 * \brief A special event_queue to be used for testing scenarios.
1233 *
1234 * \par Support for agents freeze
1235 *
1236 * If a testing scenario is not started yet all registered agents
1237 * should be in frozen state. It means that they are present in
1238 * the SObjectizer Environment, but do not receive nor handle any message
1239 * (even special message for invoking so_evt_start() method).
1240 *
1241 * To implement that special event_queue is created for every agent.
1242 * This event_queue works in two modes, described by
1243 * queue_mode_t enumeration.
1244 *
1245 * In queue_mode_t::buffer mode all messages are stored in local queue
1246 * and not passed to the actual event_queue created for agent by a dispatcher.
1247 * This way we prevent delivery of messages to agents.
1248 *
1249 * In queue_mode_t::direct mode all message are passed to the actual
1250 * event_queue directly, without storing them locally. In that mode
1251 * special event_queue works just as a proxy.
1252 *
1253 * Transfer of locally stored events is performed when mode is
1254 * switched from queue_mode_t::buffer to queue_mode_t::direct.
1255 *
1256 * \par Creation of special envelopes
1257 *
1258 * There is yet another task for special event_queue: all ordinary
1259 * messages/signals, service requests and enveloped messages must be
1260 * wrapped into a special envelope. This is done in push() method.
1261 *
1262 * \since v.5.5.24
1263 */
1264class special_event_queue_t final : public event_queue_t
1265 {
1266 //! Object lock.
1267 /*!
1268 * All operations will be performed under that lock.
1269 */
1271
1272 //! Testing scenario for that this queue was created.
1274 //! Original event_queue.
1276
1277 //! The current mode of operation.
1279 //! Local storage for demands to be used in buffered mode.
1281
1282 static bool
1290
1291 void
1293 {
1294 std::lock_guard< std::mutex > lock{ m_lock };
1295
1296 if( queue_mode_t::buffer == m_mode )
1297 m_buffer.push_back( std::move(demand) );
1298 else
1299 m_original_queue.get().push( std::move(demand) );
1300 }
1301
1302 public :
1304 outliving_reference_t< details::abstract_scenario_t > scenario,
1305 outliving_reference_t< event_queue_t > original_queue,
1306 queue_mode_t queue_mode )
1307 : m_scenario{ scenario }
1309 , m_mode{ queue_mode }
1310 {}
1311
1312 void
1313 push( execution_demand_t demand ) override
1314 {
1315 if( !is_ordinary_demand( demand ) )
1316 // Demand must go into the original queue without transformations.
1317 push_to_queue( std::move(demand) );
1318 else
1319 {
1320 // Original message must be wrapped into a special
1321 // envelope and original demand must be modified.
1322 message_ref_t new_env{
1323 std::make_unique< special_envelope_t >(
1324 m_scenario,
1325 std::cref(demand) )
1326 };
1327
1328 demand.m_message_ref = std::move(new_env);
1329 demand.m_demand_handler =
1331
1332 push_to_queue( std::move(demand) );
1333 }
1334 }
1335
1336 void
1338 {
1339 // Demand must go into the original queue without transformations.
1340 push_to_queue( std::move(demand) );
1341 }
1342
1343 void
1344 push_evt_finish( execution_demand_t demand ) noexcept override
1345 {
1346 // Demand must go into the original queue without transformations.
1347 push_to_queue( std::move(demand) );
1348 }
1349
1350 void
1352 {
1353 // Cleanup of buffer should be performed when
1354 // object is unlocked.
1355 decltype(m_buffer) tmp;
1356 {
1357 std::lock_guard< std::mutex > lock{ m_lock };
1358
1360
1361 for( auto & d : m_buffer )
1362 m_original_queue.get().push( std::move(d) );
1363
1364 using std::swap;
1365 swap( tmp, m_buffer );
1366 }
1367 }
1368 };
1369
1370/*!
1371 * \brief A special stop guard that unfreezes all agents if this
1372 * is not done yet.
1373 *
1374 * There can be cases when testing environment is started, some
1375 * agents are registered, but testing scenario is not started. For example
1376 * the work of test case is cancelled by an exception.
1377 *
1378 * In those cases testing environment should be correctly stopped.
1379 * All agents have to be deregistered. We can do that only if they
1380 * is not frozen. But they are frozen.
1381 *
1382 * This special stop_guard is intended to unfreeze all agents if
1383 * testing environment finishes its work without launching the
1384 * testing scenario.
1385 *
1386 * \since v.5.5.24
1387 */
1388class stop_guard_for_unfreezer_t final
1389 : public stop_guard_t
1390 , public std::enable_shared_from_this< stop_guard_for_unfreezer_t >
1391 {
1394
1395 public :
1402
1403 void
1404 stop() noexcept override
1405 {
1406 so_5::details::invoke_noexcept_code( [this] {
1407 // Agents should become unfrozen.
1408 m_unfreezer.get().unfreeze();
1409
1410 // We should remove itself to allow the environment
1411 // to continue shutdown operation.
1412 m_env.get().remove_stop_guard( shared_from_this() );
1413 } );
1414 }
1415 };
1416
1417/*!
1418 * \brief A special event_queue_hook that creates instances of
1419 * special event_queue for testing scenario.
1420 *
1421 * This type is also an implementation of agent_unfreezer_t interface.
1422 *
1423 * To implement unfreezing of agents object of this types stores pointers
1424 * to all created event_queue in internal container. In unfreeze()
1425 * method all those queues are switched from buffered to direct mode.
1426 *
1427 * \since v.5.5.24
1428 */
1429class special_event_queue_hook_t final
1430 : public event_queue_hook_t
1431 , public details::agent_unfreezer_t
1432 {
1433 //! Lock for this object.
1435
1436 //! Mode of operation for new queues.
1438
1439 //! Testing scenario for that this object is created.
1441
1442 //! List of all queues created before unfreeze was called.
1443 /*!
1444 * If unfreeze() was called then pointers to new event queues
1445 * are not stored in that list.
1446 *
1447 * \note
1448 * Items from this containers are not removed in on_unbind method.
1449 * We assume that agents can't be deregistered when all agents
1450 * are frozen.
1451 */
1453
1454 public :
1459
1460 [[nodiscard]]
1463 agent_t * /*agent*/,
1464 event_queue_t * original_queue ) noexcept override
1465 {
1466 std::lock_guard< std::mutex > lock{ m_lock };
1467
1468 auto sq = std::make_unique< special_event_queue_t >(
1469 m_scenario,
1470 outliving_mutable( *original_queue ),
1471 m_mode );
1472
1473 if( queue_mode_t::buffer == m_mode )
1474 m_created_queues.push_back( sq.get() );
1475
1476 return sq.release();
1477 }
1478
1479 void
1481 agent_t * /*agent*/,
1482 event_queue_t * queue ) noexcept override
1483 {
1484 // Assume that queue is an instance of special_event_queue_t
1485 // and simply delete it.
1486 delete queue;
1487 }
1488
1489 void
1490 unfreeze() noexcept override
1491 {
1492 // Mode will be switched under locked mutex.
1493 // But actual switching of queue operation mode
1494 // for already created queue will be performed with
1495 // unlocked mutex.
1496 decltype(m_created_queues) tmp;
1497 {
1498 std::lock_guard< std::mutex > lock{ m_lock };
1499
1501
1502 using std::swap;
1503 swap( tmp, m_created_queues );
1504 }
1505
1506 // Now we can switch mode for all queues those were created
1507 // in buffered mode.
1508 for( auto * sq : tmp )
1509 sq->switch_to_direct_mode();
1510 }
1511 };
1512
1513/*!
1514 * \brief A helper object for synchronization between helper worker where
1515 * testing environment is launched and user thread.
1516 *
1517 * We must stop work of testing_env_t constructor until
1518 * testing_env_t::tune_environment_on_start() finishes its work.
1519 * To do that constructor will wait on a future object for that
1520 * promise, and tune_environment_on_start() will set this promise.
1521 *
1522 * \since v.5.5.24
1523 */
1528
1529} /* namespace impl */
1530
1531/*!
1532 * \brief Internal data for testing environment.
1533 *
1534 * \attention
1535 * real_scenario object must be declared before special_hook object.
1536 */
1538 {
1539 details::real_scenario_t m_scenario;
1540 impl::special_event_queue_hook_t m_special_hook;
1541
1543
1546 {
1547 m_scenario.setup_unfreezer( m_special_hook );
1548 }
1549
1550 [[nodiscard]]
1551 static std::unique_ptr< internals_t >
1552 make() { return std::make_unique<internals_t>(); }
1553 };
1554
1555namespace impl {
1556
1557void
1561 {
1562 to.event_queue_hook(
1563 event_queue_hook_unique_ptr_t{
1564 std::addressof( internals.get().m_special_hook ),
1565 event_queue_hook_t::noop_deleter
1566 } );
1567 }
1568
1569[[nodiscard]]
1572 so_5::generic_simple_so_env_params_tuner_t env_params_tuner )
1573 {
1574 environment_params_t result;
1575 env_params_tuner( result );
1576
1577 return result;
1578 }
1579
1580[[nodiscard]]
1584 environment_params_t && params )
1585 {
1586 setup_special_queue_hook( internals, params );
1587
1588 // Special layer has to be added to the environment.
1590 ::msg_catcher_map_layer_t;
1591 params.add_layer(
1592 std::make_unique< msg_catcher_map_layer_t >() );
1593
1594 return std::move(params);
1595 }
1596
1597} /* namespace impl */
1598
1599//
1600// scenario_proxy_t
1601//
1602scenario_proxy_t::scenario_proxy_t(
1604 : m_scenario{ scenario }
1605 {}
1606
1608scenario_proxy_t::define_step(
1609 nonempty_name_t step_name )
1610 {
1611 return m_scenario.get().define_step( std::move(step_name) );
1612 }
1613
1615scenario_proxy_t::result() const
1616 {
1617 return m_scenario.get().result();
1618 }
1619
1620void
1621scenario_proxy_t::run_for(
1622 std::chrono::steady_clock::duration run_time )
1623 {
1624 return m_scenario.get().run_for( run_time );
1625 }
1626
1627std::string
1628scenario_proxy_t::stored_state_name(
1629 const std::string & step_name,
1630 const std::string & tag ) const
1631 {
1632 return m_scenario.get().stored_state_name( step_name, tag );
1633 }
1634
1635bool
1636scenario_proxy_t::has_stored_state_name(
1637 const std::string & step_name,
1638 const std::string & tag ) const
1639 {
1640 return m_scenario.get().has_stored_state_name( step_name, tag );
1641 }
1642
1643std::string
1645 const std::string & step_name,
1646 const std::string & tag ) const
1647 {
1648 return m_scenario.get().stored_msg_inspection_result( step_name, tag );
1649 }
1650
1651bool
1653 const std::string & step_name,
1654 const std::string & tag ) const
1655 {
1656 return m_scenario.get().has_stored_msg_inspection_result( step_name, tag );
1657 }
1658
1659//
1660// testing_env_t
1661//
1665
1667 so_5::generic_simple_so_env_params_tuner_t env_params_tuner )
1669 {}
1670
1672 environment_params_t && env_params )
1674 , m_sobjectizer{
1675 [this]( environment_t & env ) { tune_environment_on_start( env ); },
1679 {
1680 // We must wait completion of tune_environment_on_start().
1682 }
1683
1686
1689 {
1690 return m_sobjectizer.environment();
1691 }
1692
1693void
1695 {
1696 m_sobjectizer.stop();
1697 }
1698
1699void
1701 {
1702 m_sobjectizer.join();
1703 }
1704
1705void
1707 {
1708 m_sobjectizer.stop_then_join();
1709 }
1710
1711[[nodiscard]]
1712scenario_proxy_t
1714 {
1715 return { outliving_mutable(m_internals->m_scenario) };
1716 }
1717
1718void
1720 {
1721 // stop_guard for unfreezing agents must be installed before
1722 // an user will start work with the environment.
1723 env.setup_stop_guard(
1724 std::make_shared< impl::stop_guard_for_unfreezer_t >(
1725 outliving_mutable(m_internals->m_special_hook),
1726 outliving_mutable(env) ) );
1727
1728 // This action was done on separate thread.
1729 // The constructor of testing_env_t waits completion of
1730 // this method on another thread.
1731 // Do allow the constructor to complete its work.
1732 m_internals->m_init_completed.m_completed.set_value();
1733 }
1734
1735void
1737 {
1738 m_internals->m_init_completed.m_completed.get_future().wait();
1739 }
1740
1741} /* namespace v1 */
1742
1743} /* namespace testing */
1744
1745} /* namespace experimental */
1746
1747} /* namespace so_5 */
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 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.
virtual status_t status() const noexcept=0
Get the current status of the step.
scenario_in_progress_accessor_t make_accessor() noexcept
Helper method for creation of scenario_in_progress_accessor instance.
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
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
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
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
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.
A special object that should be used for definition of a step of a testing scenario.
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
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.
#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.
incident_status_t
What happened with source of an event.
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.
@ completed
Testing scenario is successfuly completed.
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.
Description of context on that an attempt to activate a trigger is performing.
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