unsplash-image-ZtC4_rPCRXA.jpg

C - Real Time Operating System Metronome

Real Time Operating System Metronome - C


This is a program written in C for the QNX Real Time Operating System (RTOS) simulating a metronome. It prints to console the metronomes measures and “clicks” and allows the user to pause, stop, start and quit the metronome program.

This program utilizes a QNX resource manager (driver) for the device named "/dev/local/metronome", pulses for inter process communication, and a timer for the metronome functionality. The program is written in a multi-threaded structure, where the main thread is responsible for managing resources, and the metronome thread handles the interval timer functionality. The main thread and resource manager sends a pulse to the primary thread of the metronome. The purpose of this pulse is to instruct the metronome thread to pause for a specified duration, stop indefinitely, start again, and quit the metronome program.

A video demonstrating the output and the code is shown below:

View the Source Code below, or view the full codebase on GitHub

Header File
Program
Testing Script
Exported from Notepad++
1 // metronome.h 2 // header file for metronome program 3 4 #ifndef SRC_METRONOME_H_ 5 #define SRC_METRONOME_H_ 6 7 struct ioattr_t; 8 #define IOFUNC_ATTR_T struct ioattr_t 9 struct metro_ocb; 10 #define IOFUNC_OCB_T struct metro_ocb 11 12 #include <stdio.h> 13 #include <stdlib.h> 14 #include <time.h> 15 #include <string.h> 16 #include <unistd.h> 17 #include <errno.h> 18 #include <unistd.h> 19 #include <pthread.h> 20 #include <sched.h> 21 #include <math.h> 22 #include <sys/iofunc.h> 23 #include <sys/dispatch.h> 24 #include <sys/types.h> 25 #include <sys/netmgr.h> 26 #include <sys/neutrino.h> 27 28 #define RESOURCE_NAME "metronome" 29 30 typedef struct ioattr_t { 31 iofunc_attr_t attr; 32 int device; 33 } ioattr_t; 34 35 typedef struct metro_ocb{ 36 iofunc_ocb_t ocb; 37 char buffer[50]; 38 }metro_ocb_t; 39 40 typedef union { 41 struct _pulse pulse; 42 char msg[255]; 43 }my_message_t; 44 45 enum metronome_pulse{ 46 GENERAL_PULSE, 47 RESET_PULSE, 48 START_PULSE, 49 PAUSE_PULSE, 50 STOP_PULSE, 51 QUIT_PULSE 52 }; 53 54 enum time_status { 55 RUNNING, 56 STOPPED 57 }; 58 59 enum device_type{ 60 METRONOME, 61 METRONOME_HELP 62 }; 63 64 struct Metronome { 65 int beatsPerMinute; 66 int timeSignatureTop; 67 int timeSignatureBottom; 68 69 enum time_status timeStatus; 70 double beatsPerSecond; 71 double beatsPerMeasure; 72 double secondsPerInterval; 73 double nanoSeconds; 74 }typedef Metronome_t; 75 76 void *metronome_thread(void*metronome); 77 int io_read(resmgr_context_t *ctp, io_read_t *msg, RESMGR_OCB_T *ocb); 78 int io_write(resmgr_context_t *ctp, io_write_t *msg, RESMGR_OCB_T *ocb); 79 int io_open(resmgr_context_t *ctp, io_open_t *msg, RESMGR_HANDLE_T *handle,void *extra); 80 char* get_interval_string(int tstPlusTsb); 81 void start_interval_timer(struct itimerspec * itime, timer_t timer_id,Metronome_t* Metronome); 82 metro_ocb_t * metro_ocb_calloc(resmgr_context_t *ctp, IOFUNC_ATTR_T *mtattr); 83 void metro_ocb_free(IOFUNC_OCB_T *mocb); 84 85 #endif 86
Exported from Notepad++
1 // metronome.c 2 // program that runs a metronome using a resource manager, a timer and pulses 3 4 #include "metronome.h" 5 6 name_attach_t *attach; 7 Metronome_t Metronome; 8 9 char data[255]; 10 int server_coid; 11 12 int main(int argc, char *argv[]) { 13 dispatch_t *dpp; 14 resmgr_io_funcs_t io_funcs; 15 resmgr_connect_funcs_t conn_funcs; 16 17 ioattr_t ioMetronome; 18 ioattr_t ioMetronomeHelp; 19 20 dispatch_context_t *ctp; 21 22 pthread_attr_t thread_attrib; 23 int id; 24 25 int server_coid; 26 27 // check command line arguments 28 if (argc != 4) { 29 fprintf(stderr, 30 "Error - Invalid Usage, use format of ./metronome beats-per-minute time-signature-top time-signature-bottom\n"); 31 exit(EXIT_FAILURE); 32 } 33 34 // store command line arguments 35 Metronome.beatsPerMinute = atoi(argv[1]); 36 Metronome.timeSignatureTop = atoi(argv[2]); 37 Metronome.timeSignatureBottom = atoi(argv[3]); 38 39 if ((dpp = dispatch_create()) == NULL) { 40 fprintf(stderr, "%s: Unable to allocate dispatch context.\n", argv[0]); 41 return (EXIT_FAILURE); 42 } 43 44 iofunc_func_init(_RESMGR_CONNECT_NFUNCS, &conn_funcs, _RESMGR_IO_NFUNCS, 45 &io_funcs); 46 conn_funcs.open = io_open; 47 io_funcs.read = io_read; 48 io_funcs.write = io_write; 49 50 // override functions for ocb to attach device type 51 iofunc_funcs_t metro_ocb_funcs = { 52 _IOFUNC_NFUNCS, metro_ocb_calloc, metro_ocb_free, }; 53 iofunc_mount_t metro_mount = { 0, 0, 0, 0, &metro_ocb_funcs }; 54 55 // metronome 56 iofunc_attr_init(&ioMetronome.attr, S_IFCHR | 0666, NULL, NULL); 57 ioMetronome.device = METRONOME; 58 // mount functions 59 ioMetronome.attr.mount = &metro_mount; 60 // attach 61 if ((id = resmgr_attach(dpp, NULL, "/dev/local/metronome", _FTYPE_ANY, 0, 62 &conn_funcs, &io_funcs, &ioMetronome)) == -1) { 63 fprintf(stderr, "%s: Unable to attach name.\n", argv[0]); 64 return (EXIT_FAILURE); 65 } 66 67 // metronom-help 68 iofunc_attr_init(&ioMetronomeHelp.attr, S_IFCHR | 0666, NULL, NULL); 69 ioMetronomeHelp.device = METRONOME_HELP; 70 // mount functions 71 ioMetronomeHelp.attr.mount = &metro_mount; 72 // attach 73 if ((id = resmgr_attach(dpp, NULL, "/dev/local/metronome-help", _FTYPE_ANY, 74 0, &conn_funcs, &io_funcs, &ioMetronomeHelp)) == -1) { 75 fprintf(stderr, "%s: Unable to attach name.\n", argv[0]); 76 return (EXIT_FAILURE); 77 } 78 79 ctp = dispatch_context_alloc(dpp); 80 81 // create thread 82 pthread_attr_init(&thread_attrib); 83 pthread_create(NULL, &thread_attrib, &metronome_thread, &Metronome); 84 85 while (1) { 86 if ((ctp = dispatch_block(ctp))) { 87 dispatch_handler(ctp); 88 } else 89 fprintf(stderr, "ERROR \n"); 90 91 } 92 93 pthread_attr_destroy(&thread_attrib); 94 name_detach(attach, 0); 95 name_close(server_coid); 96 return EXIT_SUCCESS; 97 98 } 99 100 void* metronome_thread(void *ta) { 101 struct sigevent event; 102 my_message_t msg; 103 int rcvid; 104 char *intervalString; 105 struct itimerspec itime; 106 timer_t timer_id; 107 108 if ((attach = name_attach(NULL, RESOURCE_NAME, 0)) == NULL) { 109 fprintf(stderr, "\nError - name_attach failed\n"); 110 exit(EXIT_FAILURE); 111 } 112 113 event.sigev_notify = SIGEV_PULSE; 114 event.sigev_coid = ConnectAttach(ND_LOCAL_NODE, 0, attach->chid, 115 _NTO_SIDE_CHANNEL, 0); 116 event.sigev_priority = SIGEV_PULSE_PRIO_INHERIT; 117 event.sigev_code = GENERAL_PULSE; 118 119 // create timer 120 timer_create(CLOCK_REALTIME, &event, &timer_id); 121 start_interval_timer(&itime, timer_id, &Metronome); 122 123 intervalString = get_interval_string( 124 Metronome.timeSignatureTop + Metronome.timeSignatureBottom); 125 126 while (1) { 127 rcvid = MsgReceive(attach->chid, &msg, sizeof(msg), 128 NULL); 129 130 // received pulse 131 if (rcvid == 0) { 132 switch (msg.pulse.code) { 133 case GENERAL_PULSE: 134 // keep writing interval string 135 if (*intervalString == '\0') { 136 printf("\n"); 137 intervalString = get_interval_string( 138 Metronome.timeSignatureTop 139 + Metronome.timeSignatureBottom); 140 } else if (*intervalString == '|') { 141 printf("%.2s", intervalString); 142 intervalString = (intervalString + 2); 143 } else { 144 printf("%c", *intervalString++); 145 } 146 break; 147 case RESET_PULSE: 148 // set new timing 149 intervalString = get_interval_string( 150 Metronome.timeSignatureTop 151 + Metronome.timeSignatureBottom); 152 printf("\n"); 153 start_interval_timer(&itime, timer_id, &Metronome); 154 break; 155 case START_PULSE: 156 // start only if stopped 157 if (Metronome.timeStatus == STOPPED) { 158 Metronome.timeStatus = RUNNING; 159 start_interval_timer(&itime, timer_id, &Metronome); 160 } 161 break; 162 case PAUSE_PULSE: 163 // pause only if running 164 if (Metronome.timeStatus == RUNNING) { 165 itime.it_value.tv_sec = msg.pulse.value.sival_int; 166 timer_settime(timer_id, 0, &itime, NULL); 167 } 168 break; 169 case STOP_PULSE: 170 // stop only if running 171 if (Metronome.timeStatus == RUNNING) { 172 Metronome.timeStatus = STOPPED; 173 itime.it_value.tv_sec = 0; 174 timer_settime(timer_id, 0, &itime, NULL); 175 } 176 break; 177 case QUIT_PULSE: 178 // quit and end 179 timer_delete(timer_id); 180 name_detach(attach, 0); 181 name_close(server_coid); 182 exit(EXIT_SUCCESS); 183 } 184 } else { 185 // error received 186 fprintf(stderr, "\nerror message received\n"); 187 exit(EXIT_FAILURE); 188 } 189 fflush(stdout); 190 } 191 return NULL; 192 } 193 194 int io_read(resmgr_context_t *ctp, io_read_t *msg, metro_ocb_t *mocb) { 195 196 int nb; 197 198 if (data == NULL) 199 return 0; 200 201 // corresponding response message data, /metronome or /metronome-help 202 if (mocb->ocb.attr->device == METRONOME) { 203 204 sprintf(data, 205 "\n[metronome: %d beats/min, time signature: %d/%d, sec-per-interval: %.2f, nanoSecs: %.0lf]\n", 206 Metronome.beatsPerMinute, Metronome.timeSignatureTop, 207 Metronome.timeSignatureBottom, Metronome.secondsPerInterval, 208 Metronome.nanoSeconds); 209 210 } else { 211 sprintf(data, 212 "\nMetronome Resource Manager (Resmgr)" 213 "\n\n Usage: metronome <bpm> <ts-top> <ts-bottom>" 214 "\n\n API:" 215 "\n pause[1-9]\t\t\t\t- pause the metronome for 1-9 seconds" 216 "\n quit:\t\t\t\t- quit the metronome" 217 "\n set <bpm> <ts-top> <ts-bottom>\t- set the metronome to <beatsPerMinute> ts-top/ts-bottom" 218 "\n start\t\t\t\t- start the metronome from stopped state" 219 "\n stop\t\t\t\t\t- stop the metronome; use 'start' to resume\n\n"); 220 221 } 222 223 nb = strlen(data); 224 225 // test to see if we have already sent the whole message 226 if (mocb->ocb.offset == nb) 227 return 0; 228 229 // we will return which ever is smaller the size of our data or the size of the buffer 230 nb = min(nb, msg->i.nbytes); 231 232 // set the number of bytes we will return 233 _IO_SET_READ_NBYTES(ctp, nb); 234 235 // copy data into reply buffer 236 SETIOV(ctp->iov, data, nb); 237 238 // update offset into our data used to determine start position for next read 239 mocb->ocb.offset += nb; 240 241 // if we are going to send any bytes update the access time for this resource 242 if (nb > 0) 243 mocb->ocb.flags |= IOFUNC_ATTR_ATIME; 244 245 return (_RESMGR_NPARTS(1)); 246 } 247 248 int io_write(resmgr_context_t *ctp, io_write_t *msg, metro_ocb_t *mocb) { 249 250 int nb = 0; 251 252 // if user attempts to write to metronome help display error message 253 if (mocb->ocb.attr->device == METRONOME_HELP) { 254 fprintf(stderr, 255 "\nError - can not write to /dev/local/metronome-help\n"); 256 257 nb = msg->i.nbytes; 258 _IO_SET_WRITE_NBYTES(ctp, nb); 259 return (_RESMGR_NPARTS(0)); 260 } 261 262 if (msg->i.nbytes == ctp->info.msglen - (ctp->offset + sizeof(*msg))) { 263 char *buf; 264 char *alert_msg; 265 char *timing_msg; 266 int i, small_integer = 0; 267 buf = (char*) (msg + 1); 268 269 // echo set 270 if (strstr(buf, "set") != NULL) { 271 for (i = 0; i < 4; i++) { 272 timing_msg = strsep(&buf, " "); 273 if (timing_msg == NULL) { 274 fprintf(stderr, 275 "\nError - invalid arguments for set command\n"); 276 break; 277 } 278 279 if (i == 1) { 280 Metronome.beatsPerMinute = atoi(timing_msg); 281 } else if (i == 2) { 282 Metronome.timeSignatureTop = atoi(timing_msg); 283 } else if (i == 3) { 284 Metronome.timeSignatureBottom = atoi(timing_msg); 285 } 286 } 287 MsgSendPulse(server_coid, SchedGet(0, 0, NULL), RESET_PULSE, 288 small_integer); 289 // echo pause 290 } else if (strstr(buf, "pause") != NULL) { 291 for (i = 0; i < 2; i++) { 292 alert_msg = strsep(&buf, " "); 293 if (alert_msg == NULL) { 294 fprintf(stderr, 295 "\nError - invalid arguments for pause command\n"); 296 break; 297 } 298 } 299 small_integer = atoi(alert_msg); 300 if (small_integer >= 1 && small_integer <= 9) { 301 MsgSendPulse(server_coid, SchedGet(0, 0, NULL), PAUSE_PULSE, 302 small_integer); 303 } else { 304 printf("\nInteger is not between 1 and 9.\n"); 305 } 306 // echo start 307 } else if (strstr(buf, "start") != NULL) { 308 MsgSendPulse(server_coid, SchedGet(0, 0, NULL), START_PULSE, 309 small_integer); 310 // echo stop 311 } else if (strstr(buf, "stop") != NULL) { 312 MsgSendPulse(server_coid, SchedGet(0, 0, NULL), STOP_PULSE, 313 small_integer); 314 // echo quit 315 } else if (strstr(buf, "quit") != NULL) { 316 MsgSendPulse(server_coid, SchedGet(0, 0, NULL), QUIT_PULSE, 317 small_integer); 318 } else { 319 alert_msg = strsep(&buf, " "); 320 alert_msg[strcspn(alert_msg, "\r\n")] = 0; 321 fprintf(stderr, "\nError - \'%s\' is not a valid command\n", 322 alert_msg); 323 } 324 325 nb = msg->i.nbytes; 326 } 327 328 _IO_SET_WRITE_NBYTES(ctp, nb); 329 330 if (msg->i.nbytes > 0) { 331 mocb->ocb.flags |= IOFUNC_ATTR_MTIME | IOFUNC_ATTR_CTIME; 332 } 333 334 return (_RESMGR_NPARTS(0)); 335 } 336 337 int io_open(resmgr_context_t *ctp, io_open_t *msg, RESMGR_HANDLE_T *handle, 338 void *extra) { 339 340 if ((server_coid = name_open(RESOURCE_NAME, 0)) == -1) { 341 perror("\nError - name_open failed\n"); 342 return EXIT_FAILURE; 343 } 344 return (iofunc_open_default(ctp, msg, &handle->attr, extra)); 345 } 346 347 char* get_interval_string(int tstPlusTsb) { 348 // return the corresponding string 349 switch (tstPlusTsb) { // tst tsb noi pattern 350 case (2 + 4): // 2 4 4 |1&2& 351 return "|1&2&"; 352 break; 353 case (3 + 4): // 3 4 6 |1&2&3& 354 return "|1&2&3&"; 355 break; 356 case (4 + 4): // 4 4 8 |1&2&3&4& 357 return "|1&2&3&4&"; 358 break; 359 case (5 + 4): // 5 4 10 |1&2&3&4-5- 360 return "|1&2&3&4-5-"; 361 break; 362 case (3 + 8): // 3 8 6 |1-2-3- 363 return "|1-2-3-"; 364 break; 365 case (6 + 8): // 6 8 6 |1&a2&a 366 return "|1&a2&a"; 367 break; 368 case (9 + 8): // 9 8 9 |1&a2&a3&a 369 return "|1&a2&a3&a"; 370 break; 371 case (12 + 8): // 12 8 12 |1&a2&a3&a4&a 372 return "|1&a2&a3&a4&a"; 373 break; 374 default: 375 return "|1&2&"; 376 break; 377 } 378 } 379 380 void start_interval_timer(struct itimerspec *itime, timer_t timer_id, 381 Metronome_t *Metronome) { 382 // calculate timing 383 Metronome->beatsPerSecond = (double) 60 / Metronome->beatsPerMinute; 384 Metronome->beatsPerMeasure = Metronome->beatsPerSecond * 2; 385 Metronome->secondsPerInterval = Metronome->beatsPerMeasure 386 / Metronome->timeSignatureBottom; 387 Metronome->nanoSeconds = (Metronome->secondsPerInterval 388 - (int) Metronome->secondsPerInterval) * 1e+9; 389 390 // start timer 391 itime->it_value.tv_sec = 1; 392 itime->it_value.tv_nsec = 0; 393 itime->it_interval.tv_sec = Metronome->secondsPerInterval; 394 itime->it_interval.tv_nsec = Metronome->nanoSeconds; 395 timer_settime(timer_id, 0, itime, NULL); 396 } 397 398 metro_ocb_t* metro_ocb_calloc(resmgr_context_t *ctp, ioattr_t *mattr) { 399 metro_ocb_t *mocb; 400 mocb = calloc(1, sizeof(metro_ocb_t)); 401 mocb->ocb.offset = 0; 402 return (mocb); 403 } 404 405 void metro_ocb_free(metro_ocb_t *mocb) { 406 free(mocb); 407 } 408
Exported from Notepad++
1 #!/bin/sh 2 ####################################################################### 3 ## - acceptance-test.sh 4 ####################################################################### 5 6 #Unit Test A 7 echo "********************************************\n" 8 echo "Unit Test [A] \n./metronome" 9 echo "Expected: usage message\n" 10 ./metronome 11 sleep 1 12 echo "\n********************************************\n\n" 13 14 15 #Unit Test B 16 echo "********************************************\n" 17 echo "Unit Test [B] \n./metronome 120 2 4 &" 18 echo "Expected: if metronome is already running - attach failed \n otherwise - metronome runs at [metronome: 120 beats/min, time signature 2/4, secs-per-interval: 0.25, nanoSecs: 250000000]\n" 19 ./metronome 120 2 4 & 20 sleep 5 21 echo "\n********************************************\n\n" 22 23 #Unit Test C 24 echo "********************************************\n" 25 echo "Unit Test [C] \ncat /dev/local/metronome" 26 echo "Expected: [metronome: 120 beats/min, time signature 2/4, secs-per-interval: 0.25, nanoSecs: 250000000]\n" 27 cat /dev/local/metronome 28 sleep 1 29 echo "\n********************************************\n\n" 30 31 #Unit Test D 32 echo "********************************************\n" 33 echo "Unit Test [D] \ncat /dev/local/metronome-help" 34 echo "Expected: information regarding the metronome resmgr’s API\n" 35 cat /dev/local/metronome-help 36 sleep 1 37 echo "\n********************************************\n\n" 38 39 #Unit Test E 40 echo "********************************************\n" 41 echo "Unit Test [E] \necho set 100 2 4 > /dev/local/metronome" 42 echo "Expected: metronome regmgr changes settings to: 100 bpm in 2/4 time; run-time behaviour of metronome changes to 100 bpm in 2/4 time.\n" 43 echo set 100 2 4 > /dev/local/metronome 44 sleep 5 45 echo "\n********************************************\n\n" 46 47 #Unit Test F 48 echo "********************************************\n" 49 echo "Unit Test [F] \ncat /dev/local/metronome" 50 echo "Expected: [metronome: 100 beats/min, time signature 2/4, secs-per- interval: 0.30, nanoSecs: 300000000]\n" 51 cat /dev/local/metronome 52 sleep 1 53 echo "\n********************************************\n\n" 54 55 #Unit Test G 56 echo "********************************************\n" 57 echo "Unit Test [G] \necho set 200 5 4 > /dev/local/metronome" 58 echo "Expected: metronome regmgr changes settings to: 200 bpm in 5/4 time; run-time behaviour of metronome changes to 200 bpm in 5/4 time.\n" 59 echo set 200 5 4 > /dev/local/metronome 60 sleep 5 61 echo "\n********************************************\n\n" 62 63 #Unit Test H 64 echo "********************************************\n" 65 echo "Unit Test [H] \ncat /dev/local/metronome" 66 echo "Expected: [metronome: 200 beats/min, time signature 5/4, secs-per- interval: 0.15, nanoSecs: 150000000]\n" 67 cat /dev/local/metronome 68 sleep 1 69 echo "\n********************************************\n\n" 70 71 #Unit Test I 72 echo "********************************************\n" 73 echo "Unit Test [I] \necho stop > /dev/local/metronome" 74 echo "Expected: metronome stops running; metronome resmgr is still running as a process: pidin | grep metronome\n" 75 echo stop > /dev/local/metronome 76 echo "\n" 77 pidin | grep metronome 78 sleep 5 79 echo "\n********************************************\n\n" 80 81 #Unit Test J 82 echo "********************************************\n" 83 echo "Unit Test [J] \necho start > /dev/local/metronome" 84 echo "Expected: metronome starts running again at 200 bpm in 5/4 time, which is the last setting; metronome resmgr is still running as a process: pidin | grep metronome\n" 85 echo start > /dev/local/metronome 86 echo "\n" 87 pidin | grep metronome 88 sleep 5 89 echo "\n********************************************\n\n" 90 91 #Unit Test K 92 echo "********************************************\n" 93 echo "Unit Test [K] \ncat /dev/local/metronome" 94 echo "Expected: [metronome: 200 beats/min, time signature 5/4, secs-per- interval: 0.15, nanoSecs: 150000000]\n" 95 cat /dev/local/metronome 96 sleep 5 97 echo "\n********************************************\n\n" 98 99 #Unit Test L 100 echo "********************************************\n" 101 echo "Unit Test [L] \necho stop > /dev/local/metronome" 102 echo "Expected: metronome stops running; metronome resmgr is still running as a process: pidin | grep metronome.\n" 103 echo stop > /dev/local/metronome 104 echo "\n" 105 pidin | grep metronome 106 sleep 5 107 echo "\n********************************************\n\n" 108 109 #Unit Test M 110 echo "********************************************\n" 111 echo "Unit Test [M] \necho stop > /dev/local/metronome" 112 echo "Expected: metronome remains stopped; metronome resmgr is still running as a process: pidin | grep metronome.\n" 113 echo stop > /dev/local/metronome 114 echo "\n" 115 pidin | grep metronome 116 sleep 5 117 echo "\n********************************************\n\n" 118 119 #Unit Test N 120 echo "********************************************\n" 121 echo "Unit Test [N] \necho start > /dev/local/metronome" 122 echo "Expected: metronome starts running again at 200 bpm in 5/4 time, which is the last setting; metronome resmgr is still running as a process: pidin | grep metronome\n" 123 echo start > /dev/local/metronome 124 echo "\n" 125 pidin | grep metronome 126 sleep 5 127 echo "\n********************************************\n\n" 128 129 #Unit Test O 130 echo "********************************************\n" 131 echo "Unit Test [O] \necho start > /dev/local/metronome" 132 echo "Expected: metronome is still running again at 200 bpm in 5/4 time, which is the last setting; metronome resmgr is still running as a process: pidin | grep metronome\n" 133 echo start > /dev/local/metronome 134 echo "\n" 135 pidin | grep metronome 136 sleep 5 137 echo "\n********************************************\n\n" 138 139 #Unit Test P 140 echo "********************************************\n" 141 echo "Unit Test [P] \ncat /dev/local/metronome" 142 echo "Expected: [metronome: 200 beats/min, time signature 5/4, secs-per- interval: 0.15, nanoSecs: 150000000]\n" 143 cat /dev/local/metronome 144 sleep 1 145 echo "\n********************************************\n\n" 146 147 #Unit Test Q 148 echo "********************************************\n" 149 echo "Unit Test [Q] \necho pause 3 > /dev/local/metronome" 150 echo "Expected: metronome continues on next beat (not next measure).\n" 151 echo pause 3 > /dev/local/metronome 152 sleep 5 153 echo "\n********************************************\n\n" 154 155 #Unit Test R 156 echo "********************************************\n" 157 echo "Unit Test [R] \necho pause 10 > /dev/local/metronome" 158 echo "Expected: properly formatted error message, and metronome continues to run.\n" 159 echo pause 10 > /dev/local/metronome 160 sleep 1 161 echo "\n********************************************\n\n" 162 163 #Unit Test S 164 echo "********************************************\n" 165 echo "Unit Test [S] \necho bogus > /dev/local/metronome" 166 echo "Expected: properly formatted error message, and metronome continues to run.\n" 167 echo bogus > /dev/local/metronome 168 sleep 1 169 echo "\n********************************************\n\n" 170 171 #Unit Test T 172 echo "********************************************\n" 173 echo "Unit Test [T] \necho set 120 2 4 > /dev/local/metronome" 174 echo "Expected: 1 measure per second\n" 175 echo set 120 2 4 > /dev/local/metronome 176 sleep 5 177 echo "\n********************************************\n\n" 178 179 #Unit Test U 180 echo "********************************************\n" 181 echo "Unit Test [U] \ncat /dev/local/metronome" 182 echo "Expected: [metronome: 120 beats/min, time signature 2/4, secs-per-interval: 0.25, nanoSecs: 250000000]\n" 183 cat /dev/local/metronome 184 sleep 1 185 echo "\n********************************************\n\n" 186 187 #Unit Test V 188 echo "********************************************\n" 189 echo "Unit Test [V] \ncat /dev/local/metronome-help" 190 echo "Expected: information regarding the metronome resmgr’s API\n" 191 cat /dev/local/metronome-help 192 sleep 1 193 echo "\n********************************************\n\n" 194 195 #Unit Test W 196 echo "********************************************\n" 197 echo "Unit Test [W] \necho Writes-Not-Allowed > /dev/local/metronome-help" 198 echo "Expected: properly formatted error message, and metronome continues to run.\n" 199 echo Writes-Not-Allowed > /dev/local/metronome-help 200 sleep 1 201 echo "\n********************************************\n\n" 202 203 #Unit Test X 204 echo "********************************************\n" 205 echo "Unit Test [X] \necho quit > /dev/local/metronome && pidin | grep metronome" 206 echo "Expected: metronome gracefully terminates.\n" 207 echo quit > /dev/local/metronome 208 sleep 1 209 pidin | grep metronome 210 sleep 1 211 echo "\n********************************************\n\n" 212