API
 
Loading...
Searching...
No Matches
stateRuleEngine_test.cpp
Go to the documentation of this file.
1/** \file stateRuleEngine_test.cpp
2 * \brief Catch2 tests for the stateRuleEngine app.
3 * \author Jared R. Males (jaredmales@gmail.com)
4 *
5 * \ingroup stateRuleEngine_files
6 */
7
8#include "../../../tests/testXWC.hpp"
9
10#include <stdexcept>
11
12#include "../stateRuleEngine.hpp"
13
14using namespace MagAOX::app;
15
16namespace MagAOX
17{
18namespace app
19{
20
21/// \cond DOXYGEN_SUPPRESS_TEST_HARNESS
22class stateRuleEngine_test : public stateRuleEngine
23{
24 public:
29
30 /// Add one rule to the harness and provision its published switch element.
31 void addRule( const std::string &name, indiCompRule *rule /**< [in] rule instance owned by the app rule map */ )
32 {
33 m_ruleMaps.rules[name] = rule;
34
35 pcf::IndiProperty *property = ruleStateProperty( rule->priority() );
36 if( property == nullptr )
37 {
38 return;
39 }
40
41 if( property->getType() != pcf::IndiProperty::Switch )
42 {
43 *property = pcf::IndiProperty( pcf::IndiProperty::Switch );
44 }
45
46 if( !property->find( name ) )
47 {
48 property->add( pcf::IndiElement( name, pcf::IndiElement::Off ) );
49 }
50 }
51
52 /// Force the published switch state for one rule element.
53 void setPublishedRuleState( const rulePriority &priority, /**< [in] priority whose published property is updated */
54 const std::string &name, /**< [in] rule element name to update */
55 pcf::IndiElement::SwitchStateType state /**< [in] switch state to write */ )
56 {
57 pcf::IndiProperty *property = ruleStateProperty( priority );
58 if( property == nullptr )
59 {
60 throw std::runtime_error( "published rule property is not available" );
61 }
62
63 if( property->getType() != pcf::IndiProperty::Switch )
64 {
65 *property = pcf::IndiProperty( pcf::IndiProperty::Switch );
66 }
67
68 if( !property->find( name ) )
69 {
70 property->add( pcf::IndiElement( name, pcf::IndiElement::Off ) );
71 }
72
73 ( *property )[name].setSwitchState( state );
74 }
75
76 /// Read the notification messages captured during `appLogic()`.
77 const std::vector<std::string> &notifications() const
78 {
79 return m_notifications;
80 }
81
82 /// Clear the captured notification list.
83 void clearNotifications()
84 {
85 m_notifications.clear();
86 }
87
88 protected:
89 /// Capture the notification text instead of sending it through an INDI driver.
90 int sendNotification( const std::string &message /**< [in] formatted notification text */ ) override
91 {
92 m_notifications.push_back( message );
93 return 0;
94 }
95
96 /// Notification messages observed by the test harness.
97 std::vector<std::string> m_notifications;
98};
99
100/// Fixed-value rule used to exercise `stateRuleEngine` notification handling.
101class fixedRule : public indiCompRule
102{
103 public:
104 /// Construct a rule with an initial boolean value.
105 fixedRule( bool value = false /**< [in] initial rule value */ ) : m_value( value )
106 {
107 }
108
109 /// Update the value returned by `value()`.
110 void value( bool value /**< [in] new boolean result */ )
111 {
112 m_value = value;
113 }
114
115 /// Report that this test rule is always valid.
116 boolorerr_t valid() override
117 {
118 return true;
119 }
120
121 /// Return the configured boolean result.
122 bool value() override
123 {
124 return m_value;
125 }
126
127 private:
128 /// Boolean result returned by the harness rule.
129 bool m_value{ false };
130};
131/// \endcond
132
133} // namespace app
134} // namespace MagAOX
135
136namespace libXWCTest
137{
138
139/** \defgroup stateRuleEngine_unit_test stateRuleEngine Unit Tests
140 * \brief Unit tests for the stateRuleEngine application.
141 *
142 * \ingroup application_unit_test
143 */
144
145/// Namespace for `stateRuleEngine` unit tests.
146/** \ingroup stateRuleEngine_unit_test
147 */
148namespace stateRuleEngineTest
149{
150
151/// Verify the placeholder stateRuleEngine test harness instantiates the app cleanly.
152/**
153 * \ingroup stateRuleEngine_unit_test
154 */
155TEST_CASE( "stateRuleEngine placeholder harness instantiates the app", "[stateRuleEngine]" )
156{
157 // clang-format off
158 #ifdef STATERULEENGINE_TEST_DOXYGEN_REF
160 #endif
161 // clang-format on
162
163 SECTION( "default construction succeeds" )
164 {
165 stateRuleEngine app;
166
167 REQUIRE( true );
168 }
169}
170
171/// Verify notification helpers preserve active severity labels and clear formatting.
172/**
173 * \ingroup stateRuleEngine_unit_test
174 */
175TEST_CASE( "stateRuleEngine notification helpers format active and clear messages", "[stateRuleEngine]" )
176{
177 fixedRule rule;
178 rule.message( std::string( "camera combo mismatch" ) );
179
180 // clang-format off
181 #ifdef STATERULEENGINE_TEST_DOXYGEN_REF
183 stateRuleEngine::notificationMessage( std::string(), rule, std::string() );
184 #endif
185 // clang-format on
186
187 SECTION( "active notifications use the rule priority label" )
188 {
189 REQUIRE( stateRuleEngine_test::notificationLabel( rulePriority::warning ) == "WARNING" );
190 REQUIRE( stateRuleEngine_test::notificationMessage(
191 "combo-mismatch", rule, stateRuleEngine_test::notificationLabel( rulePriority::warning ) ) ==
192 "WARNING: camera combo mismatch" );
193 }
194
195 SECTION( "clear notifications prefix the configured rule message" )
196 {
197 REQUIRE( stateRuleEngine_test::notificationMessage(
198 "combo-mismatch", rule, stateRuleEngine_test::notificationLabel( rulePriority::info ), true ) ==
199 "INFO: Cleared: camera combo mismatch" );
200 }
201
202 SECTION( "clear notifications fall back to the rule name when message is empty" )
203 {
204 rule.message( std::string() );
205
206 REQUIRE( stateRuleEngine_test::notificationMessage(
207 "combo-mismatch", rule, stateRuleEngine_test::notificationLabel( rulePriority::info ), true ) ==
208 "INFO: Cleared: combo-mismatch" );
209 }
210}
211
212/// Verify the published-state helpers select the right property and detect switch state.
213/**
214 * \ingroup stateRuleEngine_unit_test
215 */
216TEST_CASE( "stateRuleEngine published-state helpers select properties and detect On state", "[stateRuleEngine]" )
217{
218 stateRuleEngine_test app;
219
220 // clang-format off
221 #ifdef STATERULEENGINE_TEST_DOXYGEN_REF
223 stateRuleEngine::ruleIsOn( pcf::IndiProperty(), std::string() );
224 #endif
225 // clang-format on
226
227 app.m_indiP_info = pcf::IndiProperty( pcf::IndiProperty::Switch );
228 app.m_indiP_caution = pcf::IndiProperty( pcf::IndiProperty::Switch );
229 app.m_indiP_warning = pcf::IndiProperty( pcf::IndiProperty::Switch );
230 app.m_indiP_alert = pcf::IndiProperty( pcf::IndiProperty::Switch );
231
232 REQUIRE( app.ruleStateProperty( rulePriority::info ) == &app.m_indiP_info );
233 REQUIRE( app.ruleStateProperty( rulePriority::caution ) == &app.m_indiP_caution );
234 REQUIRE( app.ruleStateProperty( rulePriority::warning ) == &app.m_indiP_warning );
235 REQUIRE( app.ruleStateProperty( rulePriority::alert ) == &app.m_indiP_alert );
236 REQUIRE( app.ruleStateProperty( rulePriority::none ) == nullptr );
237
238 app.m_indiP_warning.add( pcf::IndiElement( "combo-mismatch", pcf::IndiElement::Off ) );
239
240 REQUIRE( stateRuleEngine_test::ruleIsOn( app.m_indiP_warning, "combo-mismatch" ) == false );
241
242 app.m_indiP_warning["combo-mismatch"].setSwitchState( pcf::IndiElement::On );
243 REQUIRE( stateRuleEngine_test::ruleIsOn( app.m_indiP_warning, "combo-mismatch" ) == true );
244
245 REQUIRE( stateRuleEngine_test::ruleIsOn( app.m_indiP_warning, "missing-rule" ) == false );
246}
247
248/// Verify `appLogic()` emits one clear notification for an observed `On -> Off` transition.
249/**
250 * \ingroup stateRuleEngine_unit_test
251 */
252TEST_CASE( "stateRuleEngine appLogic reports one clear notification per observed On-to-Off transition",
253 "[stateRuleEngine]" )
254{
255 stateRuleEngine_test app;
256 auto *rule = new fixedRule( false );
257
259 rule->message( std::string( "camera combo mismatch" ) );
260 rule->messageCount( 2 );
261 app.addRule( "combo-mismatch", rule );
262
263 // clang-format off
264 #ifdef STATERULEENGINE_TEST_DOXYGEN_REF
266 #endif
267 // clang-format on
268
269 SECTION( "configured rule message is used for the clear notification" )
270 {
271 app.setPublishedRuleState( rulePriority::warning, "combo-mismatch", pcf::IndiElement::On );
272
273 REQUIRE( app.appLogic() == 0 );
274 REQUIRE( app.notifications().size() == 1 );
275 REQUIRE( app.notifications().front() == "INFO: Cleared: camera combo mismatch" );
276 REQUIRE( rule->messageCount() == 0 );
277
278 app.clearNotifications();
279 app.setPublishedRuleState( rulePriority::warning, "combo-mismatch", pcf::IndiElement::Off );
280
281 REQUIRE( app.appLogic() == 0 );
282 REQUIRE( app.notifications().empty() );
283 }
284
285 SECTION( "rule name is used when no explicit message is configured" )
286 {
287 rule->message( std::string() );
288 app.setPublishedRuleState( rulePriority::warning, "combo-mismatch", pcf::IndiElement::On );
289
290 REQUIRE( app.appLogic() == 0 );
291 REQUIRE( app.notifications().size() == 1 );
292 REQUIRE( app.notifications().front() == "INFO: Cleared: combo-mismatch" );
293 REQUIRE( rule->messageCount() == 0 );
294 }
295}
296
297} // namespace stateRuleEngineTest
298
299} // namespace libXWCTest
The MagAO-X stateRuleEngine.
static bool ruleIsOn(pcf::IndiProperty &property, const std::string &ruleName)
Report whether a published rule element is currently On.
static std::string notificationMessage(const std::string &ruleName, indiCompRule &rule, const std::string &label, bool cleared=false, bool settime=false)
Format a notification message for a rule.
static std::string notificationLabel(const rulePriority &priority)
Get the notification label for a reporting priority.
pcf::IndiProperty * ruleStateProperty(const rulePriority &priority)
Get the published rule-state property for a reporting priority.
virtual int appLogic()
Implementation of the FSM for stateRuleEngine.
TEST_CASE("stateRuleEngine placeholder harness instantiates the app", "[stateRuleEngine]")
Verify the placeholder stateRuleEngine test harness instantiates the app cleanly.
rulePriority
Reporting priorities for rules.
@ none
Don't publish.
@ caution
Caution – make sure you know what you're doing.
@ warning
Warning – something is probably wrong, you should check.
@ alert
Alert – something is definitely wrong, you should take action.
@ info
For information only.
Definition dm.hpp:19
Namespace for all libXWC tests.
Virtual base-class for all rules.
void messageCount(int mc)
Set the message count.
void priority(const rulePriority &p, double delay=-1)
Set priority of this rule.
void message(const std::string &m)
Set the message.