API
 
Loading...
Searching...
No Matches
MagAOXApp_test.cpp
Go to the documentation of this file.
1// #define CATCH_CONFIG_MAIN
2#include "../../../tests/catch2/catch.hpp"
3
4#include <filesystem>
5
6#include <mx/sys/timeUtils.hpp>
7
8#include "../MagAOXApp.hpp"
9#include "MagAOXApp_test.hpp"
10
11#undef app_MagAOXApp_hpp
12#undef app_tests_MagAOXApp_test_hpp
13#define XWCTEST_NAMESPACE XWCTEST_MAGAOXAPP_PID_LOCKED_ns
14#define XWCTEST_MAGAOXAPP_PID_LOCKED
15#include "../MagAOXApp.hpp"
16#include "MagAOXApp_test.hpp"
17#undef XWCTEST_NAMESPACE
18#undef XWCTEST_MAGAOXAPP_PID_LOCKED
19
20#undef app_MagAOXApp_hpp
21#undef app_tests_MagAOXApp_test_hpp
22#define XWCTEST_NAMESPACE XWCTEST_MAGAOXAPP_PID_WRITE_FAIL_ns
23#define XWCTEST_MAGAOXAPP_PID_WRITE_FAIL
24#include "../MagAOXApp.hpp"
25#include "MagAOXApp_test.hpp"
26#undef XWCTEST_NAMESPACE
27#undef XWCTEST_MAGAOXAPP_PID_WRITE_FAIL
28
29#undef app_MagAOXApp_hpp
30#undef app_tests_MagAOXApp_test_hpp
31#define XWCTEST_NAMESPACE XWCTEST_MAGAOXAPP_EXEC_NORM_ns
32#define XWCTEST_MAGAOXAPP_EXEC_NORM
33#include "../MagAOXApp.hpp"
34#include "MagAOXApp_test.hpp"
35#undef XWCTEST_NAMESPACE
36#undef XWCTEST_MAGAOXAPP_EXEC_NORM
37
38namespace libXWCTest
39{
40namespace appTest
41{
42
43/** \defgroup app_unit_test libXWC::app Unit Tests
44 * \ingroup unit_test
45 */
46
47/** \defgroup MagAOXApp_unit_test MagAOXApp Unit Tests
48 * \ingroup app_unit_test
49 */
50
51/// Namespace for XWC::app::MagAOXApp tests
52/** \ingroup MagAOXApp_unit_test
53 *
54 */
55namespace MagAOXAppTest
56{
57
58/// MagAOXApp 2nd instance
59/**
60 * \ingroup MagAOXApp_unit_test
61 */
62TEST_CASE( "MagAOXApp 2nd instance", "[app::MagAOXApp]" )
63{
64 SECTION( "test 2nd app" )
65 {
66 bool caught = false;
67
68 MagAOXApp_test app1;
69
70 try
71 {
72 MagAOXApp_test app2;
73 }
74 catch( const std::logic_error &e )
75 {
76 caught = true;
77 }
78
79 REQUIRE( caught == true );
80 }
81
82#ifdef XWCTEST_DOXYGEN_REF_PROTECTED
83 MagAOX::app::MagAOXApp<true> app( "", true );
84#endif
85}
86
87/// MagAOXApp INDI NewProperty
88/**
89 * \ingroup MagAOXApp_unit_test
90 */
91SCENARIO( "MagAOXApp INDI NewProperty", "[app::MagAOXApp]" )
92{
93 GIVEN( "a new property request" )
94 {
95 WHEN( "a wrong device name" )
96 {
98
99 app.setConfigName( "test" );
100
101 REQUIRE( app.configName() == "test" );
102
103 pcf::IndiProperty prop;
104 app.registerIndiPropertyNew( prop,
105 "nprop",
106 pcf::IndiProperty::Number,
107 pcf::IndiProperty::ReadWrite,
108 pcf::IndiProperty::Idle,
109 callback );
110
111 pcf::IndiProperty nprop;
112
113 // First test the right device name
114 nprop.setDevice( "test" );
115 nprop.setName( "nprop" );
116
117 app.handleNewProperty( nprop );
118
119 REQUIRE( app.called_back == 1 );
120
121 app.called_back = 0;
122
123 // Now test the wrong device name
124 nprop.setDevice( "wrong" );
125
126 app.handleNewProperty( nprop );
127
128 REQUIRE( app.called_back == 0 );
129 }
130 }
131
132#ifdef XWCTEST_DOXYGEN_REF_PROTECTED
133 MagAOX::app::MagAOXApp<true> app( "", true );
134 app.configName();
135 pcf::IndiProperty prop;
137 prop, "nprop", pcf::IndiProperty::Number, pcf::IndiProperty::ReadWrite, pcf::IndiProperty::Idle, callback );
138 app.handleNewProperty( prop );
139#endif
140}
141
142/// Setting defaults
143/**
144 * \ingroup MagAOXApp_unit_test
145 */
146TEST_CASE( "MagAOXApp INDI SetProperty retry backoff", "[app::MagAOXApp]" )
147{
148 using namespace std::chrono;
149
150 MagAOXApp_test app;
151
152 pcf::IndiProperty prop;
153 REQUIRE( app.registerIndiPropertySet( prop, "publisher", "property", callback ) == 0 );
154
155 std::string key = app.setPropertyKey( prop );
156
157 auto now = steady_clock::now();
158
159 REQUIRE( app.shouldRequestSetProperty( key, false, now ) == true );
160
161 app.noteSetPropertyRequested( key, now );
162 REQUIRE( app.setPropertyRetryCount( key ) == 1 );
163 REQUIRE( app.setPropertyRetryDelay( key ) == seconds( 1 ) );
164 REQUIRE( app.shouldRequestSetProperty( key, false, now ) == false );
165 REQUIRE( app.shouldRequestSetProperty( key, false, now + seconds( 1 ) ) == true );
166
167 app.noteSetPropertyRequested( key, now + seconds( 1 ) );
168 REQUIRE( app.setPropertyRetryCount( key ) == 2 );
169 REQUIRE( app.setPropertyRetryDelay( key ) == seconds( 2 ) );
170 REQUIRE( app.shouldRequestSetProperty( key, false, now + seconds( 2 ) ) == false );
171 REQUIRE( app.shouldRequestSetProperty( key, false, now + seconds( 3 ) ) == true );
172
173 app.noteSetPropertyRequested( key, now + seconds( 3 ) );
174 app.noteSetPropertyRequested( key, now + seconds( 7 ) );
175 app.noteSetPropertyRequested( key, now + seconds( 15 ) );
176 app.noteSetPropertyRequested( key, now + seconds( 31 ) );
177 REQUIRE( app.setPropertyRetryDelay( key ) == seconds( 32 ) );
178 REQUIRE( app.setPropertyMissingLogged( key ) == false );
179
180 app.noteSetPropertyRequested( key, now + seconds( 63 ) );
181 REQUIRE( app.setPropertyRetryDelay( key ) == seconds( 60 ) );
182 REQUIRE( app.setPropertyMissingLogged( key ) == true );
183
184 app.noteSetPropertyRequested( key, now + seconds( 123 ) );
185 REQUIRE( app.setPropertyRetryDelay( key ) == seconds( 60 ) );
186 REQUIRE( app.setPropertyRetryCount( key ) == 8 );
187}
188
189TEST_CASE( "MagAOXApp INDI SetProperty retry reset", "[app::MagAOXApp]" )
190{
191 using namespace std::chrono;
192
193 MagAOXApp_test app;
194
195 pcf::IndiProperty prop;
196 REQUIRE( app.registerIndiPropertySet( prop, "publisher", "property", callback ) == 0 );
197
198 std::string key = app.setPropertyKey( prop );
199
200 auto now = steady_clock::now();
201
202 app.noteSetPropertyRequested( key, now );
203 app.noteSetPropertyRequested( key, now + seconds( 1 ) );
204
205 REQUIRE( app.setPropertyRetryCount( key ) == 2 );
206 REQUIRE( app.setPropertyRetryDelay( key ) == seconds( 2 ) );
207
208 app.resetSetPropertyRetry( key );
209 REQUIRE( app.setPropertyRetryCount( key ) == 0 );
210 REQUIRE( app.setPropertyRetryDelay( key ) == steady_clock::duration::zero() );
211 REQUIRE( app.setPropertyMissingLogged( key ) == false );
212 REQUIRE( app.shouldRequestSetProperty( key, false, now ) == true );
213
214 app.markSetPropertyReceived( key, true );
215 REQUIRE( app.shouldRequestSetProperty( key, false, now ) == false );
216 REQUIRE( app.shouldRequestSetProperty( key, true, now ) == true );
217}
218
219/// Setting defaults
220/**
221 * \ingroup MagAOXApp_unit_test
222 */
223TEST_CASE( "Setting defaults", "[app::MagAOXApp]" )
224{
225 SECTION( "using default paths, configname is invoked name" )
226 {
227 std::vector<std::string> argvstr( { "./execname" } );
228
229 std::vector<const char *> argv( argvstr.size() + 1, NULL );
230 for( size_t index = 0; index < argvstr.size(); ++index )
231 {
232 argv[index] = argvstr[index].c_str();
233 }
234
235 MagAOXApp_test app;
236
237 app.invokedName() = argv[0];
238 REQUIRE( app.doHelp() == false );
239 app.setDefaults( argv.size() - 1, const_cast<char **>( argv.data() ) );
240 REQUIRE( app.doHelp() == true );
241
242 app.basePath(); // make lcov records this call
243 REQUIRE( app.basePath() == MAGAOX_path );
244 app.configDir(); // make lcov records this call
245 REQUIRE( app.configDir() == app.basePath() + '/' + MAGAOX_configRelPath );
246 REQUIRE( app.configPathGlobal() == app.configDir() + "/magaox.conf" );
247 app.calibDir(); // make lcov records this call
248 REQUIRE( app.calibDir() == app.basePath() + '/' + MAGAOX_calibRelPath );
249 REQUIRE( app.m_log.logPath() == app.basePath() + '/' + MAGAOX_logRelPath );
250 app.sysPath(); // make lcov records this call
251 REQUIRE( app.sysPath() == app.basePath() + '/' + MAGAOX_sysRelPath );
252 app.secretsPath(); // make lcov records this call
253 REQUIRE( app.secretsPath() == app.basePath() + '/' + MAGAOX_secretsRelPath );
254 app.cpusetPath(); // make lcov records this call
255 REQUIRE( app.cpusetPath() == MAGAOX_cpusetPath );
256 app.configBase(); // make lcov records this call
257 REQUIRE( app.configBase() == "" );
258 REQUIRE( app.configPathUser() == "" );
259 REQUIRE( app.configName() == "execname" );
260 REQUIRE( app.configPathLocal() == app.configDir() + "/execname.conf" );
261
262 REQUIRE( app.doHelp() == true );
263 }
264
265 SECTION( "using default paths, with config-ed name" )
266 {
267 std::vector<std::string> argvstr( { "./execname", "-n", "testapp" } );
268
269 std::vector<const char *> argv( argvstr.size() + 1, NULL );
270 for( size_t index = 0; index < argvstr.size(); ++index )
271 {
272 argv[index] = argvstr[index].c_str();
273 }
274
275 MagAOXApp_test app;
276 app.invokedName() = argv[0];
277
278 app.setDefaults( argv.size() - 1, const_cast<char **>( argv.data() ) );
279
280 REQUIRE( app.basePath() == MAGAOX_path );
281 REQUIRE( app.configDir() == app.basePath() + '/' + MAGAOX_configRelPath );
282 REQUIRE( app.configPathGlobal() == app.configDir() + "/magaox.conf" );
283 REQUIRE( app.calibDir() == app.basePath() + '/' + MAGAOX_calibRelPath );
284 REQUIRE( app.m_log.logPath() == app.basePath() + '/' + MAGAOX_logRelPath );
285
286 REQUIRE( app.sysPath() == app.basePath() + '/' + MAGAOX_sysRelPath );
287 REQUIRE( app.secretsPath() == app.basePath() + '/' + MAGAOX_secretsRelPath );
288 REQUIRE( app.cpusetPath() == MAGAOX_cpusetPath );
289 REQUIRE( app.configBase() == "" );
290 REQUIRE( app.configPathUser() == "" );
291 REQUIRE( app.configName() == "testapp" );
292 REQUIRE( app.configPathLocal() == app.configDir() + "/testapp.conf" );
293 REQUIRE( app.doHelp() == false );
294 }
295
296 // Something goes wrong here, third time is the charm.
297 // Hangs on config.parseCommandLine
298 SECTION( "using environment paths, with config-ed name" )
299 {
300 std::vector<const char *> argv;
301 std::vector<std::string> argvstr( { "./execname", "--name", "testapp2" } );
302
303 argv.resize( argvstr.size() + 1, NULL );
304 for( size_t index = 0; index < argvstr.size(); ++index )
305 {
306 argv[index] = argvstr[index].c_str();
307 }
308
309 char ppath[1024];
310 snprintf( ppath, sizeof( ppath ), "%s=/tmp/MagAOXApp_test", MAGAOX_env_path );
311 putenv( ppath );
312
313 char cpath[1024];
314 snprintf( cpath, sizeof( cpath ), "%s=config2", MAGAOX_env_config );
315 putenv( cpath );
316
317 char cbpath[1024];
318 snprintf( cbpath, sizeof( cbpath ), "%s=calib2", MAGAOX_env_calib );
319 putenv( cbpath );
320
321 char lpath[1024];
322 snprintf( lpath, sizeof( lpath ), "%s=logs2", MAGAOX_env_log );
323 putenv( lpath );
324
325 char syspath[1024];
326 snprintf( syspath, sizeof( syspath ), "%s=sys2", MAGAOX_env_sys );
327 putenv( syspath );
328
329 char secretspath[1024];
330 snprintf( secretspath, sizeof( secretspath ), "%s=secrets2", MAGAOX_env_secrets );
331 putenv( secretspath );
332
333 char cpupath[1024];
334 snprintf( cpupath, sizeof( cpupath ), "%s=/tmp/MagAOX/cpuset", MAGAOX_env_cpuset );
335 putenv( cpupath );
336
337 MagAOXApp_test app;
338
339 app.invokedName() = argv[0];
340 app.setConfigBase( "cbase" );
341
342 app.setDefaults( argv.size() - 1, const_cast<char **>( argv.data() ) );
343
344 REQUIRE( app.basePath() == "/tmp/MagAOXApp_test" );
345 REQUIRE( app.configDir() == app.basePath() + '/' + "config2" );
346 REQUIRE( app.configPathGlobal() == app.configDir() + "/magaox.conf" );
347 REQUIRE( app.calibDir() == app.basePath() + '/' + "calib2" );
348 REQUIRE( app.m_log.logPath() == app.basePath() + '/' + "logs2" );
349 REQUIRE( app.sysPath() == app.basePath() + '/' + "sys2" );
350 REQUIRE( app.secretsPath() == app.basePath() + '/' + "secrets2" );
351 REQUIRE( app.cpusetPath() == "/tmp/MagAOX/cpuset" );
352 REQUIRE( app.configBase() == "cbase" );
353 REQUIRE( app.configPathUser() == app.configDir() + "/cbase.conf" );
354 REQUIRE( app.configName() == "testapp2" );
355 REQUIRE( app.configPathLocal() == app.configDir() + "/testapp2.conf" );
356 REQUIRE( app.doHelp() == false );
357 }
358}
359
360/// Configuring MagAOXApp
361/**
362 * \ingroup MagAOXApp_unit_test
363 */
364TEST_CASE( "Configuring MagAOXApp", "[app::MagAOXApp]" )
365{
366 SECTION( "setup basic config" )
367 {
368 MagAOXApp_test app;
369 app.setPowerMgtEnabled( true );
370
371 app.setupBasicConfig();
372
373 REQUIRE( app.shutdown() == false );
374 }
375
376 SECTION( "load basic config w all defaults w/out pwr management" )
377 {
378 MagAOXApp_test app;
379 app.setPowerMgtEnabled( false );
380
381 app.setupBasicConfig();
382
383 app.loadBasicConfig();
384
385 app.checkConfig();
386
387 REQUIRE( app.stateAlert() == false );
388 REQUIRE( app.gitAlert() == false );
389 REQUIRE( app.shutdown() == false );
390 }
391
392 SECTION( "load basic config w all defaults w/out pwr management, setting state and clearing alerts" )
393 {
394 std::vector<const char *> argv;
395 std::vector<std::string> argvstr( { "./execname", "--name", "testapp" } );
396
397 argv.resize( argvstr.size() + 1, NULL );
398 for( size_t index = 0; index < argvstr.size(); ++index )
399 {
400 argv[index] = argvstr[index].c_str();
401 }
402
403 MagAOXApp_test app( true );
404 app.setPowerMgtEnabled( false );
405
406 app.setupBasicConfig();
407
408 app.setDefaults( argv.size() - 1, const_cast<char **>( argv.data() ) );
409
410 app.loadBasicConfig();
411
412 app.checkConfig();
413
414 REQUIRE( app.stateAlert() == true );
415 REQUIRE( app.gitAlert() == true );
416 REQUIRE( app.shutdown() == false );
417
418 app.doFSMClearAlert();
419 REQUIRE( app.stateAlert() == false );
420 REQUIRE( app.gitAlert() == true );
421 REQUIRE( app.shutdown() == false );
422
423 app.doFSMClearAlert(); // calls an immediate return of clearFSMAlert
424
425 // Now test each path out of clearFSMAlert
427 REQUIRE( app.state() == MagAOX::app::stateCodes::READY );
428
429 REQUIRE( app.stateLogged() == 0 );
430 REQUIRE( app.stateLogged() == 1 );
431
432 app.setAlert();
433 REQUIRE( app.stateAlert() == true );
434
435 app.doFSMClearAlert();
436 REQUIRE( app.stateAlert() == false );
437
439 REQUIRE( app.state() == MagAOX::app::stateCodes::HOMING );
440
441 app.setAlert();
442 REQUIRE( app.stateAlert() == true );
443
444 app.doFSMClearAlert();
445 REQUIRE( app.stateAlert() == false );
446
448 REQUIRE( app.state() == MagAOX::app::stateCodes::NODEVICE );
449
450 app.setAlert();
451 REQUIRE( app.stateAlert() == true );
452
453 app.doFSMClearAlert();
454 REQUIRE( app.stateAlert() == false );
455
457 REQUIRE( app.state() == MagAOX::app::stateCodes::LOGGEDIN );
458
459 app.setAlert();
460 REQUIRE( app.stateAlert() == true );
461
462 app.doFSMClearAlert();
463 REQUIRE( app.stateAlert() == false );
464
466 REQUIRE( app.state() == MagAOX::app::stateCodes::NODEVICE );
467
468 app.setAlert();
469 REQUIRE( app.stateAlert() == true );
470
471 app.doFSMClearAlert();
472 REQUIRE( app.stateAlert() == false );
473
475 REQUIRE( app.state() == MagAOX::app::stateCodes::NOTHOMED );
476
477 app.setAlert();
478 REQUIRE( app.stateAlert() == true );
479
480 app.doFSMClearAlert();
481 REQUIRE( app.stateAlert() == false );
482 }
483
484 SECTION( "load basic config w all defaults w unconfigured pwr management" )
485 {
486 MagAOXApp_test app;
487 app.setPowerMgtEnabled( true );
488
489 app.setupBasicConfig();
490
491 app.loadBasicConfig();
492
493 REQUIRE( app.shutdown() == true );
494
495 app.checkConfig();
496
497 REQUIRE( app.stateAlert() == false );
498 REQUIRE( app.gitAlert() == false );
499 REQUIRE( app.shutdown() == true );
500 }
501
502 SECTION( "load a full config" )
503 {
504 std::vector<const char *> argv;
505 std::vector<std::string> argvstr( { "./execname", "-n", "testapp", "--config.validate" } );
506
507 argv.resize( argvstr.size() + 1, NULL );
508 for( size_t index = 0; index < argvstr.size(); ++index )
509 {
510 argv[index] = argvstr[index].c_str();
511 }
512
513 char ppath[1024];
514 snprintf( ppath, sizeof( ppath ), "%s=/tmp/MagAOXApp_test", MAGAOX_env_path );
515 putenv( ppath );
516
517 mx::ioutils::createDirectories( "/tmp/MagAOXApp_test/config" );
518
519 std::ofstream fout;
520 fout.open( "/tmp/MagAOXApp_test/config/magaox.conf" );
521 fout.close();
522
523 mx::app::writeConfigFile( "/tmp/MagAOXApp_test/config/testapp.conf",
524 { "", "power", "power", "power", "power", "power" },
525 { "loopPause", "device", "channel", "element", "targetElement", "powerOnWait" },
526 { "2500", "pdu9", "thisch", "thisel", "thistgtel", "500000" } );
527
528 MagAOXApp_test app( true );
529 app.setPowerMgtEnabled( true );
530
531 app.setup( argv.size() - 1, const_cast<char **>( argv.data() ) );
532
533 REQUIRE( app.stateAlert() == true ); // due to git
534 REQUIRE( app.configOnly() == true );
535 REQUIRE( app.loopPause() == 2500 );
536 REQUIRE( app.powerDevice() == "pdu9" );
537 REQUIRE( app.powerChannel() == "thisch" );
538 REQUIRE( app.powerElement() == "thisel" );
539 REQUIRE( app.powerTargetElement() == "thistgtel" );
540 REQUIRE( app.powerOnWait() == 0 );
541
542 REQUIRE( app.shutdown() == false );
543 }
544
545 SECTION( "load a full config w unknown config in file, do help" )
546 {
547 std::vector<const char *> argv;
548 std::vector<std::string> argvstr( { "./execname", "-n", "testapp" } );
549
550 argv.resize( argvstr.size() + 1, NULL );
551 for( size_t index = 0; index < argvstr.size(); ++index )
552 {
553 argv[index] = argvstr[index].c_str();
554 }
555
556 char ppath[1024];
557 snprintf( ppath, sizeof( ppath ), "%s=/tmp/MagAOXApp_test", MAGAOX_env_path );
558 putenv( ppath );
559
560 mx::ioutils::createDirectories( "/tmp/MagAOXApp_test/config" );
561
562 std::ofstream fout;
563 fout.open( "/tmp/MagAOXApp_test/config/magaox.conf" );
564 fout.close();
565
566 // this adds unknown=value
567 mx::app::writeConfigFile(
568 "/tmp/MagAOXApp_test/config/testapp.conf",
569 { "", "power", "power", "power", "power", "power", "" },
570 { "loopPause", "device", "channel", "element", "targetElement", "powerOnWait", "unknown" },
571 { "2500", "pdu9", "thisch", "thisel", "thistgtel", "500", "value" } );
572
573 MagAOXApp_test app( false );
574 app.setPowerMgtEnabled( true );
575
576 app.setup( argv.size() - 1, const_cast<char **>( argv.data() ) );
577
578 REQUIRE( app.stateAlert() == false );
579 REQUIRE( app.configOnly() == false );
580 REQUIRE( app.loopPause() == 2500 );
581 REQUIRE( app.powerDevice() == "pdu9" );
582 REQUIRE( app.powerChannel() == "thisch" );
583 REQUIRE( app.powerElement() == "thisel" );
584 REQUIRE( app.powerTargetElement() == "thistgtel" );
585 REQUIRE( app.powerOnWait() == 500 );
586
587 REQUIRE( app.doHelp() == true );
588 REQUIRE( app.shutdown() == true );
589 }
590
591 SECTION( "load a full config w unknown config in file, validate" )
592 {
593 std::vector<const char *> argv;
594 std::vector<std::string> argvstr( { "./execname", "-n", "testapp", "--config.validate" } );
595
596 argv.resize( argvstr.size() + 1, NULL );
597 for( size_t index = 0; index < argvstr.size(); ++index )
598 {
599 argv[index] = argvstr[index].c_str();
600 }
601
602 char ppath[1024];
603 snprintf( ppath, sizeof( ppath ), "%s=/tmp/MagAOXApp_test", MAGAOX_env_path );
604 putenv( ppath );
605
606 mx::ioutils::createDirectories( "/tmp/MagAOXApp_test/config" );
607
608 std::ofstream fout;
609 fout.open( "/tmp/MagAOXApp_test/config/magaox.conf" );
610 fout.close();
611
612 // this adds unknown=value
613 mx::app::writeConfigFile(
614 "/tmp/MagAOXApp_test/config/testapp.conf",
615 { "", "power", "power", "power", "power", "power", "" },
616 { "loopPause", "device", "channel", "element", "targetElement", "powerOnWait", "unknown" },
617 { "2500", "pdu9", "thisch", "thisel", "thistgtel", "500000", "value" } );
618
619 MagAOXApp_test app( true );
620 app.setPowerMgtEnabled( true );
621
622 app.setup( argv.size() - 1, const_cast<char **>( argv.data() ) );
623
624 REQUIRE( app.stateAlert() == true ); // due to git
625 REQUIRE( app.configOnly() == true );
626 REQUIRE( app.loopPause() == 2500 );
627 REQUIRE( app.powerDevice() == "pdu9" );
628 REQUIRE( app.powerChannel() == "thisch" );
629 REQUIRE( app.powerElement() == "thisel" );
630 REQUIRE( app.powerTargetElement() == "thistgtel" );
631 REQUIRE( app.powerOnWait() == 0 );
632
633 REQUIRE( app.doHelp() == false );
634 REQUIRE( app.shutdown() == true );
635 }
636
637 SECTION( "load a full config w non-option clopt" )
638 {
639 std::vector<const char *> argv;
640 std::vector<std::string> argvstr( { "./execname", "-n", "testapp", "straylight" } );
641
642 argv.resize( argvstr.size() + 1, NULL );
643 for( size_t index = 0; index < argvstr.size(); ++index )
644 {
645 argv[index] = argvstr[index].c_str();
646 }
647
648 char ppath[1024];
649 snprintf( ppath, sizeof( ppath ), "%s=/tmp/MagAOXApp_test", MAGAOX_env_path );
650 putenv( ppath );
651
652 mx::ioutils::createDirectories( "/tmp/MagAOXApp_test/config" );
653
654 std::ofstream fout;
655 fout.open( "/tmp/MagAOXApp_test/config/magaox.conf" );
656 fout.close();
657
658 mx::app::writeConfigFile( "/tmp/MagAOXApp_test/config/testapp.conf",
659 { "", "power", "power", "power", "power", "power" },
660 { "loopPause", "device", "channel", "element", "targetElement", "powerOnWait" },
661 { "2500", "pdu9", "thisch", "thisel", "thistgtel", "500000" } );
662
663 MagAOXApp_test app( false );
664 app.setPowerMgtEnabled( true );
665
666 app.setup( argv.size() - 1, const_cast<char **>( argv.data() ) );
667
668 REQUIRE( app.stateAlert() == false ); // due to git
669 REQUIRE( app.configOnly() == false );
670 REQUIRE( app.loopPause() == 2500 );
671 REQUIRE( app.powerDevice() == "pdu9" );
672 REQUIRE( app.powerChannel() == "thisch" );
673 REQUIRE( app.powerElement() == "thisel" );
674 REQUIRE( app.powerTargetElement() == "thistgtel" );
675 REQUIRE( app.powerOnWait() == 0 );
676
677 REQUIRE( app.doHelp() == true );
678 REQUIRE( app.shutdown() == true );
679 }
680
681 SECTION( "load a full config w no power mgt opts" )
682 {
683 std::vector<const char *> argv;
684 std::vector<std::string> argvstr( { "./execname", "-n", "testapp" } );
685
686 argv.resize( argvstr.size() + 1, NULL );
687 for( size_t index = 0; index < argvstr.size(); ++index )
688 {
689 argv[index] = argvstr[index].c_str();
690 }
691
692 char ppath[1024];
693 snprintf( ppath, sizeof( ppath ), "%s=/tmp/MagAOXApp_test", MAGAOX_env_path );
694 putenv( ppath );
695
696 mx::ioutils::createDirectories( "/tmp/MagAOXApp_test/config" );
697
698 std::ofstream fout;
699 fout.open( "/tmp/MagAOXApp_test/config/magaox.conf" );
700 fout.close();
701
702 mx::app::writeConfigFile( "/tmp/MagAOXApp_test/config/testapp.conf", { "" }, { "loopPause" }, { "2500" } );
703
704 MagAOXApp_test app( false );
705 app.setPowerMgtEnabled( true );
706
707 app.setup( argv.size() - 1, const_cast<char **>( argv.data() ) );
708
709 REQUIRE( app.stateAlert() == false ); // due to git
710 REQUIRE( app.configOnly() == false );
711 REQUIRE( app.loopPause() == 2500 );
712 REQUIRE( app.powerDevice() == "" );
713 REQUIRE( app.powerChannel() == "" );
714 REQUIRE( app.powerElement() == "state" );
715 REQUIRE( app.powerTargetElement() == "target" );
716 REQUIRE( app.powerOnWait() == 0 );
717
718 REQUIRE( app.doHelp() == true );
719 REQUIRE( app.shutdown() == true );
720 }
721
722 SECTION( "load a full config w unused config options" )
723 {
724 std::vector<const char *> argv;
725 std::vector<std::string> argvstr( { "./execname", "-n", "testapp", "--config.validate" } );
726
727 argv.resize( argvstr.size() + 1, NULL );
728 for( size_t index = 0; index < argvstr.size(); ++index )
729 {
730 argv[index] = argvstr[index].c_str();
731 }
732
733 char ppath[1024];
734 snprintf( ppath, sizeof( ppath ), "%s=/tmp/MagAOXApp_test", MAGAOX_env_path );
735 putenv( ppath );
736
737 mx::ioutils::createDirectories( "/tmp/MagAOXApp_test/config" );
738
739 std::ofstream fout;
740 fout.open( "/tmp/MagAOXApp_test/config/magaox.conf" );
741 fout.close();
742
743 // this adds unknown=value
744 mx::app::writeConfigFile( "/tmp/MagAOXApp_test/config/testapp.conf",
745 { "", "power", "power", "power", "power", "power" },
746 { "loopPause", "device", "channel", "element", "targetElement", "powerOnWait" },
747 { "2500", "pdu9", "thisch", "thisel", "thistgtel", "500000" } );
748
749 MagAOXApp_test app( true );
750 app.setPowerMgtEnabled( true );
751
752 app.addUnusedConfig();
753
754 app.setup( argv.size() - 1, const_cast<char **>( argv.data() ) );
755
756 REQUIRE( app.stateAlert() == true ); // due to git
757 REQUIRE( app.configOnly() == true );
758 REQUIRE( app.loopPause() == 2500 );
759 REQUIRE( app.powerDevice() == "pdu9" );
760 REQUIRE( app.powerChannel() == "thisch" );
761 REQUIRE( app.powerElement() == "thisel" );
762 REQUIRE( app.powerTargetElement() == "thistgtel" );
763 REQUIRE( app.powerOnWait() == 0 );
764
765 REQUIRE( app.doHelp() == false );
766 REQUIRE( app.shutdown() == false );
767 }
768}
769
770/// PID Locking
771/**
772 * \ingroup MagAOXApp_unit_test
773 */
774TEST_CASE( "PID Locking", "[app::MagAOXApp]" )
775{
776 SECTION( "Basic PID Lock" )
777 {
778 std::vector<const char *> argv;
779 std::vector<std::string> argvstr( { "./execname", "-n", "testapp" } );
780
781 argv.resize( argvstr.size() + 1, NULL );
782 for( size_t index = 0; index < argvstr.size(); ++index )
783 {
784 argv[index] = argvstr[index].c_str();
785 }
786
787 char ppath[1024];
788 snprintf( ppath, sizeof( ppath ), "%s=/tmp/MagAOXApp_test", MAGAOX_env_path );
789 putenv( ppath );
790
791 mx::ioutils::createDirectories( "/tmp/MagAOXApp_test/sys/" );
792
793 // First delete the directory and files in case this is a repeat call
794 std::filesystem::remove_all( "/tmp/MagAOXApp_test/sys/testapp" );
795
796 MagAOXApp_test app;
797 app.invokedName() = argv[0];
798 app.setDefaults( argv.size() - 1, const_cast<char **>( argv.data() ) );
799
800 REQUIRE( !std::filesystem::exists( "/tmp/MagAOXApp_test/sys/testapp/pid" ) );
801
802 int rv = app.lockPID();
803 REQUIRE( rv == 0 );
804 REQUIRE( std::filesystem::exists( "/tmp/MagAOXApp_test/sys/testapp/pid" ) );
805
806 rv = app.unlockPID();
807 REQUIRE( rv == 0 );
808 REQUIRE( !std::filesystem::exists( "/tmp/MagAOXApp_test/sys/testapp/pid" ) );
809 }
810
811 SECTION( "PID Lock, app directory creation error" )
812 {
813 std::vector<const char *> argv;
814 std::vector<std::string> argvstr( { "./execname", "-n", "testapp" } );
815
816 argv.resize( argvstr.size() + 1, NULL );
817 for( size_t index = 0; index < argvstr.size(); ++index )
818 {
819 argv[index] = argvstr[index].c_str();
820 }
821
822 char ppath[1024];
823 snprintf( ppath, sizeof( ppath ), "%s=/tmp/MagAOXApp_test", MAGAOX_env_path );
824 putenv( ppath );
825
826 // First delete the directory and files in case this is a repeat call
827 std::filesystem::remove_all( "/tmp/MagAOXApp_test" );
828
829 MagAOXApp_test app;
830 app.invokedName() = argv[0];
831 app.setDefaults( argv.size() - 1, const_cast<char **>( argv.data() ) );
832
833 int rv = app.lockPID();
834 REQUIRE( rv == -1 );
835
836 rv = app.unlockPID();
837 REQUIRE( rv == -1 );
838 }
839
840 SECTION( "Stale lock" )
841 {
842 std::vector<const char *> argv;
843 std::vector<std::string> argvstr( { "./execname", "-n", "testapp" } );
844
845 argv.resize( argvstr.size() + 1, NULL );
846 for( size_t index = 0; index < argvstr.size(); ++index )
847 {
848 argv[index] = argvstr[index].c_str();
849 }
850
851 char ppath[1024];
852 snprintf( ppath, sizeof( ppath ), "%s=/tmp/MagAOXApp_test", MAGAOX_env_path );
853 putenv( ppath );
854
855 mx::ioutils::createDirectories( "/tmp/MagAOXApp_test/sys/testapp" );
856
857 std::ofstream fout;
858 fout.open( "/tmp/MagAOXApp_test/sys/testapp/pid" );
859 fout << 1;
860 fout.close();
861
862 MagAOXApp_test app;
863 app.invokedName() = argv[0];
864 app.setDefaults( argv.size() - 1, const_cast<char **>( argv.data() ) );
865
866 int rv = app.lockPID();
867 REQUIRE( rv == 0 );
868 REQUIRE( std::filesystem::exists( "/tmp/MagAOXApp_test/sys/testapp/pid" ) );
869
870 rv = app.unlockPID();
871 REQUIRE( rv == 0 );
872 REQUIRE( !std::filesystem::exists( "/tmp/MagAOXApp_test/sys/testapp/pid" ) );
873 }
874
875 SECTION( "already locked" )
876 {
877 std::vector<const char *> argv;
878 std::vector<std::string> argvstr( { "./execname", "-n", "testapp" } );
879
880 argv.resize( argvstr.size() + 1, NULL );
881 for( size_t index = 0; index < argvstr.size(); ++index )
882 {
883 argv[index] = argvstr[index].c_str();
884 }
885
886 char ppath[1024];
887 snprintf( ppath, sizeof( ppath ), "%s=/tmp/MagAOXApp_test", MAGAOX_env_path );
888 putenv( ppath );
889
890 mx::ioutils::createDirectories( "/tmp/MagAOXApp_test/sys/testapp" );
891
892 std::ofstream fout;
893 fout.open( "/tmp/MagAOXApp_test/sys/testapp/pid" );
894 fout << 1;
895 fout.close();
896
897 XWCTEST_MAGAOXAPP_PID_LOCKED_ns::MagAOXApp_test app;
898 app.invokedName() = argv[0];
899 app.setDefaults( argv.size() - 1, const_cast<char **>( argv.data() ) );
900
901 int rv = app.lockPID();
902 REQUIRE( rv == -1 );
903 }
904
905 SECTION( "write fails" )
906 {
907 std::vector<const char *> argv;
908 std::vector<std::string> argvstr( { "./execname", "-n", "testapp" } );
909
910 argv.resize( argvstr.size() + 1, NULL );
911 for( size_t index = 0; index < argvstr.size(); ++index )
912 {
913 argv[index] = argvstr[index].c_str();
914 }
915
916 char ppath[1024];
917 snprintf( ppath, sizeof( ppath ), "%s=/tmp/MagAOXApp_test", MAGAOX_env_path );
918 putenv( ppath );
919
920 mx::ioutils::createDirectories( "/tmp/MagAOXApp_test/sys/testapp" );
921
922 std::ofstream fout;
923 fout.open( "/tmp/MagAOXApp_test/sys/testapp/pid" );
924 fout << 1;
925 fout.close();
926
927 XWCTEST_MAGAOXAPP_PID_WRITE_FAIL_ns::MagAOXApp_test app;
928 app.invokedName() = argv[0];
929 app.setDefaults( argv.size() - 1, const_cast<char **>( argv.data() ) );
930
931 int rv = app.lockPID();
932 REQUIRE( rv == -1 );
933 }
934}
935
936/// MagAOXApp Power Management Logic Outside of Execute
937/**
938 * \ingroup MagAOXApp_unit_test
939 */
940TEST_CASE( "MagAOXApp Power Management Logic Outside of Execute", "[app::MagAOXApp]" )
941{
942 SECTION( "Power Management Not Configured" )
943 {
944 MagAOXApp_test app( true );
945 app.setPowerMgtEnabled( false );
946
947 REQUIRE( app.onPowerOff() == 0 );
948 REQUIRE( app.whilePowerOff() == 0 );
949 REQUIRE( app.powerOnWaitElapsed() == true );
950 REQUIRE( app.powerState() == 1 );
951 REQUIRE( app.powerStateTarget() == 1 );
952 }
953
954 SECTION( "Power Management Configured" )
955 {
956 MagAOXApp_test app( true );
957 app.setPowerMgtEnabled( true );
958 app.configurePowerManagement( "pdu", "test" );
959
960 REQUIRE( app.onPowerOff() == 0 );
961 REQUIRE( app.whilePowerOff() == 0 );
962 REQUIRE( app.powerOnWaitElapsed() == true );
963
964 // Comes up unknown
965 REQUIRE( app.powerState() == -1 );
966 REQUIRE( app.powerStateTarget() == -1 );
967
968 app.setPowerState( "Off", "Off" );
969 REQUIRE( app.powerState() == 0 );
970 REQUIRE( app.powerStateTarget() == 0 );
971
972 app.setPowerState( "Int", "Int" );
973 REQUIRE( app.powerState() == -1 );
974 REQUIRE( app.powerStateTarget() == -1 );
975
976 app.setPowerState( "Off", "On" );
977 REQUIRE( app.powerState() == 0 );
978 REQUIRE( app.powerStateTarget() == 1 );
979
980 app.configurePowerOnWait( 10, 0, 1e9 );
981 REQUIRE( app.loopPause() == 1e9 );
982
983 // 10 checks, then true on 11th
984 REQUIRE( app.powerOnWaitElapsed() == false );
985 REQUIRE( app.powerOnWaitElapsed() == false );
986 REQUIRE( app.powerOnWaitElapsed() == false );
987 REQUIRE( app.powerOnWaitElapsed() == false );
988 REQUIRE( app.powerOnWaitElapsed() == false );
989 REQUIRE( app.powerOnWaitElapsed() == false );
990 REQUIRE( app.powerOnWaitElapsed() == false );
991 REQUIRE( app.powerOnWaitElapsed() == false );
992 REQUIRE( app.powerOnWaitElapsed() == false );
993 REQUIRE( app.powerOnWaitElapsed() == false );
994 REQUIRE( app.powerOnWaitElapsed() == false );
995 REQUIRE( app.powerOnWaitElapsed() == true );
996
997 app.setPowerState( "On", "On" );
998 REQUIRE( app.powerState() == 1 );
999 REQUIRE( app.powerStateTarget() == 1 );
1000
1001 app.setPowerState( "On", "Off" );
1002 REQUIRE( app.powerState() == 1 );
1003 REQUIRE( app.powerStateTarget() == 0 );
1004
1005 app.setPowerState( "Off", "Off" );
1006 REQUIRE( app.powerState() == 0 );
1007 REQUIRE( app.powerStateTarget() == 0 );
1008 }
1009}
1010
1011/// INDI preperty creation utilities
1012/**
1013 * \ingroup MagAOXApp_unit_test
1014 */
1015TEST_CASE( "INDI preperty creation utilities", "[app::MagAOXApp]" )
1016{
1017 SECTION( "createStandardIndiText" )
1018 {
1019 MagAOXApp_test app;
1020 app.setConfigName( "test" );
1021
1022 pcf::IndiProperty ip;
1023
1024 app.createStandardIndiText( ip, "tprop", "tlabel", "tgroup" );
1025
1026 REQUIRE( ip.getType() == pcf::IndiProperty::Text );
1027 REQUIRE( ip.getDevice() == "test" );
1028 REQUIRE( ip.getName() == "tprop" );
1029 REQUIRE( ip.getPerm() == pcf::IndiProperty::ReadWrite );
1030 REQUIRE( ip.getState() == pcf::IndiProperty::Idle );
1031 REQUIRE( ip.find( "current" ) == true );
1032 REQUIRE( ip.find( "target" ) == true );
1033 REQUIRE( ip.getLabel() == "tlabel" );
1034 REQUIRE( ip.getGroup() == "tgroup" );
1035 }
1036
1037 SECTION( "createROIndiText" )
1038 {
1039 MagAOXApp_test app;
1040 app.setConfigName( "test" );
1041
1042 pcf::IndiProperty ip;
1043
1044 app.createROIndiText( ip, "tprop", "tel", "tlabel", "tgroup", "ellabel" );
1045
1046 REQUIRE( ip.getType() == pcf::IndiProperty::Text );
1047 REQUIRE( ip.getDevice() == "test" );
1048 REQUIRE( ip.getName() == "tprop" );
1049 REQUIRE( ip.getPerm() == pcf::IndiProperty::ReadOnly );
1050 REQUIRE( ip.getState() == pcf::IndiProperty::Idle );
1051 REQUIRE( ip.getLabel() == "tlabel" );
1052 REQUIRE( ip.getGroup() == "tgroup" );
1053
1054 REQUIRE( ip.find( "tel" ) == true );
1055 REQUIRE( ip["tel"].getLabel() == "ellabel" );
1056 }
1057
1058 SECTION( "createStandardIndiNumber" )
1059 {
1060 MagAOXApp_test app;
1061 app.setConfigName( "test" );
1062
1063 pcf::IndiProperty ip;
1064
1065 app.createStandardIndiNumber<double>( ip, "tprop", 0.001, 1, 0.002, "%0.23g", "tlabel", "tgroup" );
1066
1067 REQUIRE( ip.getType() == pcf::IndiProperty::Number );
1068 REQUIRE( ip.getDevice() == "test" );
1069 REQUIRE( ip.getName() == "tprop" );
1070 REQUIRE( ip.getPerm() == pcf::IndiProperty::ReadWrite );
1071 REQUIRE( ip.getState() == pcf::IndiProperty::Idle );
1072
1073 REQUIRE( ip.find( "current" ) == true );
1074 REQUIRE( ip["current"].getMin() == "0.001" );
1075 REQUIRE( ip["current"].getMax() == "1" );
1076 REQUIRE( ip["current"].getStep() == "0.002" );
1077 REQUIRE( ip["current"].getFormat() == "%0.23g" );
1078
1079 REQUIRE( ip.find( "target" ) == true );
1080 REQUIRE( ip["target"].getMin() == "0.001" );
1081 REQUIRE( ip["target"].getMax() == "1" );
1082 REQUIRE( ip["target"].getStep() == "0.002" );
1083 REQUIRE( ip["target"].getFormat() == "%0.23g" );
1084
1085 REQUIRE( ip.getLabel() == "tlabel" );
1086 REQUIRE( ip.getGroup() == "tgroup" );
1087 }
1088
1089 SECTION( "createROIndiNumber" )
1090 {
1091 MagAOXApp_test app;
1092 app.setConfigName( "test" );
1093
1094 pcf::IndiProperty ip;
1095
1096 app.createROIndiNumber( ip, "tprop", "tlabel", "tgroup" );
1097
1098 REQUIRE( ip.getType() == pcf::IndiProperty::Number );
1099 REQUIRE( ip.getDevice() == "test" );
1100 REQUIRE( ip.getName() == "tprop" );
1101 REQUIRE( ip.getPerm() == pcf::IndiProperty::ReadOnly );
1102 REQUIRE( ip.getState() == pcf::IndiProperty::Idle );
1103 REQUIRE( ip.getLabel() == "tlabel" );
1104 REQUIRE( ip.getGroup() == "tgroup" );
1105 }
1106
1107 SECTION( "createStandardIndiToggleSw" )
1108 {
1109 MagAOXApp_test app;
1110 app.setConfigName( "testz" );
1111
1112 pcf::IndiProperty ip;
1113
1114 app.createStandardIndiToggleSw( ip, "tpropz", "tlabelz", "tgroupz" );
1115
1116 REQUIRE( ip.getType() == pcf::IndiProperty::Switch );
1117 REQUIRE( ip.getDevice() == "testz" );
1118 REQUIRE( ip.getName() == "tpropz" );
1119 REQUIRE( ip.getPerm() == pcf::IndiProperty::ReadWrite );
1120 REQUIRE( ip.getState() == pcf::IndiProperty::Idle );
1121 REQUIRE( ip.getRule() == pcf::IndiProperty::AtMostOne );
1122
1123 REQUIRE( ip.getNumElements() == 1 );
1124 REQUIRE( ip.find( "toggle" ) == true );
1125 REQUIRE( ip["toggle"].getSwitchState() == pcf::IndiElement::Off );
1126 REQUIRE( ip.getLabel() == "tlabelz" );
1127 REQUIRE( ip.getGroup() == "tgroupz" );
1128 }
1129
1130 SECTION( "createStandardIndiRequestSw" )
1131 {
1132 MagAOXApp_test app;
1133 app.setConfigName( "testz" );
1134
1135 pcf::IndiProperty ip;
1136
1137 app.createStandardIndiRequestSw( ip, "tpropz", "tlabelz", "tgroupz" );
1138
1139 REQUIRE( ip.getType() == pcf::IndiProperty::Switch );
1140 REQUIRE( ip.getDevice() == "testz" );
1141 REQUIRE( ip.getName() == "tpropz" );
1142 REQUIRE( ip.getPerm() == pcf::IndiProperty::ReadWrite );
1143 REQUIRE( ip.getState() == pcf::IndiProperty::Idle );
1144 REQUIRE( ip.getRule() == pcf::IndiProperty::AtMostOne );
1145
1146 REQUIRE( ip.getNumElements() == 1 );
1147 REQUIRE( ip.find( "request" ) == true );
1148 REQUIRE( ip["request"].getSwitchState() == pcf::IndiElement::Off );
1149 REQUIRE( ip.getLabel() == "tlabelz" );
1150 REQUIRE( ip.getGroup() == "tgroupz" );
1151 }
1152
1153 SECTION( "createStandardIndiSelectionSw, w/ labels" )
1154 {
1155 MagAOXApp_test app;
1156 app.setConfigName( "testy" );
1157
1158 pcf::IndiProperty ip;
1159
1160 std::vector<std::string> els( { "el1", "el2", "el3" } );
1161 std::vector<std::string> labs( { "l1", "", "l3" } );
1162
1163 app.createStandardIndiSelectionSw( ip, "tpropy", els, labs, "tlabely", "tgroupy" );
1164
1165 REQUIRE( ip.getType() == pcf::IndiProperty::Switch );
1166 REQUIRE( ip.getDevice() == "testy" );
1167 REQUIRE( ip.getName() == "tpropy" );
1168 REQUIRE( ip.getPerm() == pcf::IndiProperty::ReadWrite );
1169 REQUIRE( ip.getState() == pcf::IndiProperty::Idle );
1170 REQUIRE( ip.getRule() == pcf::IndiProperty::OneOfMany );
1171
1172 REQUIRE( ip.getNumElements() == 3 );
1173 REQUIRE( ip.find( "el1" ) == true );
1174 REQUIRE( ip["el1"].getSwitchState() == pcf::IndiElement::Off );
1175 REQUIRE( ip["el1"].getLabel() == "l1" );
1176
1177 REQUIRE( ip.find( "el2" ) == true );
1178 REQUIRE( ip["el2"].getSwitchState() == pcf::IndiElement::Off );
1179 REQUIRE( ip["el2"].getLabel() == "" );
1180
1181 REQUIRE( ip.find( "el3" ) == true );
1182 REQUIRE( ip["el3"].getSwitchState() == pcf::IndiElement::Off );
1183 REQUIRE( ip["el3"].getLabel() == "l3" );
1184
1185 REQUIRE( ip.getLabel() == "tlabely" );
1186 REQUIRE( ip.getGroup() == "tgroupy" );
1187 }
1188
1189 SECTION( "createStandardIndiSelectionSw, no labels" )
1190 {
1191 MagAOXApp_test app;
1192 app.setConfigName( "testy" );
1193
1194 pcf::IndiProperty ip;
1195
1196 std::vector<std::string> els( { "el1", "el2", "el3" } );
1197
1198 app.createStandardIndiSelectionSw( ip, "tpropy", els, "tlabely", "tgroupy" );
1199
1200 REQUIRE( ip.getType() == pcf::IndiProperty::Switch );
1201 REQUIRE( ip.getDevice() == "testy" );
1202 REQUIRE( ip.getName() == "tpropy" );
1203 REQUIRE( ip.getPerm() == pcf::IndiProperty::ReadWrite );
1204 REQUIRE( ip.getState() == pcf::IndiProperty::Idle );
1205 REQUIRE( ip.getRule() == pcf::IndiProperty::OneOfMany );
1206
1207 REQUIRE( ip.getNumElements() == 3 );
1208 REQUIRE( ip.find( "el1" ) == true );
1209 REQUIRE( ip["el1"].getSwitchState() == pcf::IndiElement::Off );
1210 REQUIRE( ip["el1"].getLabel() == "el1" );
1211
1212 REQUIRE( ip.find( "el2" ) == true );
1213 REQUIRE( ip["el2"].getSwitchState() == pcf::IndiElement::Off );
1214 REQUIRE( ip["el2"].getLabel() == "el2" );
1215
1216 REQUIRE( ip.find( "el3" ) == true );
1217 REQUIRE( ip["el3"].getSwitchState() == pcf::IndiElement::Off );
1218 REQUIRE( ip["el3"].getLabel() == "el3" );
1219
1220 REQUIRE( ip.getLabel() == "tlabely" );
1221 REQUIRE( ip.getGroup() == "tgroupy" );
1222 }
1223}
1224
1225/// Signal Handlers
1226/**
1227 * \ingroup MagAOXApp_unit_test
1228 */
1229TEST_CASE( "Signal Handlers", "[app::MagAOXApp]" )
1230{
1231 SECTION( "Setting and calling signal handler: SIGTERM" )
1232 {
1233
1234 MagAOXApp_test app;
1235
1236 // this is just to touch this function
1237 REQUIRE( app.setSigTermHandler() == 0 );
1238
1239 REQUIRE( app.shutdown() == 0 );
1240 app._handlerSigTerm( SIGTERM, nullptr, nullptr );
1241 REQUIRE( app.shutdown() == 1 );
1242 }
1243
1244 SECTION( "Setting and calling signal handler: SIGINT" )
1245 {
1246
1247 MagAOXApp_test app;
1248
1249 // this is just to touch this function
1250 REQUIRE( app.setSigTermHandler() == 0 );
1251
1252 REQUIRE( app.shutdown() == 0 );
1253 app._handlerSigTerm( SIGINT, nullptr, nullptr );
1254 REQUIRE( app.shutdown() == 1 );
1255 }
1256
1257 SECTION( "Setting and calling signal handler: SIGQUIT" )
1258 {
1259
1260 MagAOXApp_test app;
1261
1262 // this is just to touch this function
1263 REQUIRE( app.setSigTermHandler() == 0 );
1264
1265 REQUIRE( app.shutdown() == 0 );
1266 app._handlerSigTerm( SIGQUIT, nullptr, nullptr );
1267 REQUIRE( app.shutdown() == 1 );
1268 }
1269
1270 SECTION( "Setting and calling signal handler: SIGHUP" )
1271 {
1272
1273 MagAOXApp_test app;
1274
1275 // this is just to touch this function
1276 REQUIRE( app.setSigTermHandler() == 0 );
1277
1278 REQUIRE( app.shutdown() == 0 );
1279 app._handlerSigTerm( SIGHUP, nullptr, nullptr );
1280 REQUIRE( app.shutdown() == 1 );
1281 }
1282}
1283
1284/// Setting Euid
1285/**
1286 * \ingroup MagAOXApp_unit_test
1287 */
1288TEST_CASE( "Setting Euid", "[app::MagAOXApp]" )
1289{
1290
1291 MagAOXApp_test app;
1292
1293 REQUIRE( app.setEuidReal() == 0 );
1294
1295 REQUIRE( app.setEuidCalled() == 0 );
1296
1297 REQUIRE( app.setEuidReal( 0 ) == -1 );
1298 REQUIRE( app.setEuidCalled( 0 ) == -1 );
1299}
1300
1301/// Tests of utilities in cpp
1302/**
1303 * \ingroup MagAOXApp_unit_test
1304 */
1305TEST_CASE( "Tests of utilities in cpp", "[app::MagAOXApp]" )
1306{
1307 SECTION( "sigusr1 handler" )
1308 {
1309 // this is just to touch this function
1310 MagAOX::app::sigUsr1Handler( 0, nullptr, nullptr );
1311
1312 REQUIRE( true );
1313 }
1314}
1315
1316} // namespace MagAOXAppTest
1317} // namespace appTest
1318} // namespace libXWCTest
The base-class for XWCTk applications.
int registerIndiPropertyNew(pcf::IndiProperty &prop, int(*)(void *, const pcf::IndiProperty &))
Register an INDI property which is exposed for others to request a New Property for.
void handleNewProperty(const pcf::IndiProperty &ipRecv)
Handler for the new INDI property request.
std::string configName()
Get the config name.
TEST_CASE("MagAOXApp 2nd instance", "[app::MagAOXApp]")
MagAOXApp 2nd instance.
SCENARIO("MagAOXApp INDI NewProperty", "[app::MagAOXApp]")
MagAOXApp INDI NewProperty.
#define MAGAOX_calibRelPath
The relative path to the calibration files.
Definition paths.hpp:36
#define MAGAOX_configRelPath
The relative path to the configuration files.
Definition paths.hpp:29
#define MAGAOX_logRelPath
The relative path to the log directory.
Definition paths.hpp:50
#define MAGAOX_sysRelPath
The relative path to the system directory.
Definition paths.hpp:64
#define MAGAOX_path
The path to the MagAO-X system files.
Definition paths.hpp:22
#define MAGAOX_secretsRelPath
The relative path to the secrets directory. Used for storing passwords, etc.
Definition paths.hpp:71
#define MAGAOX_cpusetPath
The absolute path to the cpuset mount point.
Definition paths.hpp:99
#define MAGAOX_env_sys
Environment variable setting the relative system directory path.
#define MAGAOX_env_path
Environment variable setting the MagAO-X path.
#define MAGAOX_env_log
Environment variable setting the relative log path.
#define MAGAOX_env_calib
Environment variable setting the relative calib path.
#define MAGAOX_env_secrets
Environment variable setting the relative secrets path.
#define MAGAOX_env_config
Environment variable setting the relative config path.
#define MAGAOX_env_cpuset
Environment variable setting the cpu set path.
void sigUsr1Handler(int signum, siginfo_t *siginf, void *ucont)
Empty signal handler. SIGUSR1 is used to interrupt sleep in various threads.
Definition MagAOXApp.cpp:18
int callback(void *app, const pcf::IndiProperty &ipRecv)
Namespace for all libXWC tests.
@ NODEVICE
No device exists for the application to control.
@ NOTHOMED
The device has not been homed.
@ HOMING
The device is homing.
@ READY
The device is ready for operation, but is not operating.
@ LOGGEDIN
The application has logged into the device or service.
uint32_t setPropertyRetryCount(const std::string &key)
void _handlerSigTerm(int signum, siginfo_t *siginf, void *ucont)
void configurePowerManagement(const std::string &device, const std::string &channel)
void configurePowerOnWait(unsigned long powerOnWait, int powerOnCounter, int loopPause)
std::string setPropertyKey(const pcf::IndiProperty &prop)
int setPowerState(const std::string &state, const std::string target)
void markSetPropertyReceived(const std::string &key, bool received)
std::chrono::steady_clock::duration setPropertyRetryDelay(const std::string &key)
void noteSetPropertyRequested(const std::string &key, const std::chrono::steady_clock::time_point &now)
bool shouldRequestSetProperty(const std::string &key, bool all, const std::chrono::steady_clock::time_point &now)