1 /* Mach virtual memory unit tests
3 * The main goal of this code is to facilitate the construction,
4 * running, result logging and clean up of a test suite, taking care
5 * of all the scaffolding. A test suite is a sequence of very targeted
6 * unit tests, each running as a separate process to isolate its
8 * A unit test is abstracted as a unit_test_t structure, consisting of
9 * a test function and a logging identifier. A test suite is a suite_t
10 * structure, consisting of an unit_test_t array, a logging identifier,
11 * and fixture set up and tear down functions.
12 * Test suites are created dynamically. Each of its unit test runs in
13 * its own fork()d process, with the fixture set up and tear down
14 * running before and after each test. The parent process will log a
15 * pass result if the child exits normally, and a fail result in any
16 * other case (non-zero exit status, abnormal signal). The suite
17 * results are then aggregated and logged, and finally the test suite
19 * Everything is logged to stdout in the standard Testbot format, which
20 * can be easily converted to Munin or SimonSays logging
21 * format. Logging is factored out as much as possible for future
22 * flexibility. In our particular case, a unit test is logged as a
23 * Testbot Test Case ([BEGIN]/[PASS]/[FAIL], and a test suite is
24 * logged as a Testbot Test ([TEST]). This is confusing but
25 * unfortunately cannot be avoided for compatibility. Suite results
26 * are aggregated after the [SUMMARY] keyword.
27 * The included test suites cover the various pipe buffer operations
28 * with dynamic expansion.
30 * Vishal Patel (vishal_patel@apple.com)
41 #include <sys/sysctl.h>
45 #include <sys/types.h>
46 #include <dispatch/dispatch.h>
48 /**************************/
49 /**************************/
50 /* Unit Testing Framework */
51 /**************************/
52 /**************************/
54 /*********************/
55 /* Private interface */
56 /*********************/
58 static const char frameworkname
[] = "pipes_unitester";
60 /* Type for test, fixture set up and fixture tear down functions. */
61 typedef void (*test_fn_t
)();
63 /* Unit test structure. */
69 /* Test suite structure. */
79 unsigned int _timeout
= 0;
80 int _expected_signal
= 0;
84 uintmax_t passed_tests
;
87 void logr(char *format
, ...) __printflike(1, 2);
89 static void die(int condition
, const char *culprit
)
92 printf("%s: %s error: %s.\n", frameworkname
, culprit
,
98 static void die_on_stdout_error()
100 die(ferror(stdout
), "stdout");
103 /* Individual test result logging. */
104 void logr(char *format
, ...)
106 if (_quietness
<= 1) {
109 va_start(ap
, format
);
112 die_on_stdout_error();
116 static suite_t
*create_suite(const char *name
, int numoftests
,
117 test_fn_t set_up
, unit_test_t
*tests
,
120 suite_t
*suite
= (suite_t
*)malloc(sizeof(suite_t
));
121 die(suite
== NULL
, "malloc()");
124 suite
->numoftests
= numoftests
;
125 suite
->set_up
= set_up
;
126 suite
->tests
= tests
;
127 suite
->tear_down
= tear_down
;
131 static void destroy_suite(suite_t
*suite
)
136 static void log_suite_info(suite_t
*suite
)
138 logr("[TEST] %s\n", suite
->name
);
139 logr("Number of tests: %d\n\n", suite
->numoftests
);
142 static void log_suite_results(suite_t
*suite
, int passed_tests
)
144 results
.numoftests
+= (uintmax_t)suite
->numoftests
;
145 results
.passed_tests
+= (uintmax_t)passed_tests
;
148 static void log_test_info(unit_test_t
*unit_test
)
150 logr("[BEGIN] %s\n", unit_test
->name
);
153 static void log_test_result(unit_test_t
*unit_test
,
154 boolean_t test_passed
)
156 logr("[%s] %s\n\n", test_passed
? "PASS" : "FAIL",
160 /* Handler for test time out. */
161 static void alarm_handler(int signo
)
163 write(1,"Child process timed out.\n",
164 strlen("Child process timed out.\n"));
168 /* Run a test with fixture set up and teardown, while enforcing the
169 * time out constraint. */
170 static void run_test(suite_t
*suite
, unit_test_t
*unit_test
)
172 struct sigaction alarm_act
;
174 log_test_info(unit_test
);
175 alarm_act
.sa_handler
= alarm_handler
;
176 sigemptyset(&alarm_act
.sa_mask
);
177 alarm_act
.sa_flags
= 0;
178 die(sigaction(SIGALRM
, &alarm_act
, NULL
) != 0, "sigaction()");
186 /* Check a child return status. */
187 static boolean_t
child_terminated_normally(int child_status
)
189 boolean_t normal_exit
= FALSE
;
191 if (WIFEXITED(child_status
)) {
192 int exit_status
= WEXITSTATUS(child_status
);
194 printf("Child process unexpectedly exited with code "
195 "%d.\n", exit_status
);
196 } else if (!_expected_signal
) {
199 } else if (WIFSIGNALED(child_status
)) {
200 int signal
= WTERMSIG(child_status
);
201 if (signal
== _expected_signal
) {
202 if (_quietness
<= 0) {
203 printf("Child process died with expected signal "
208 printf("Child process unexpectedly died with signal "
212 printf("Child process unexpectedly did not exit nor "
215 die_on_stdout_error();
219 /* Run a test in its own process, and report the result. */
220 static boolean_t
child_test_passed(suite_t
*suite
,
221 unit_test_t
*unit_test
)
225 pid_t test_pid
= fork();
226 die(test_pid
== -1, "fork()");
228 run_test(suite
, unit_test
);
231 while (waitpid(test_pid
, &test_status
, 0) != test_pid
) {
234 boolean_t test_result
= child_terminated_normally(test_status
);
235 log_test_result(unit_test
, test_result
);
239 /* Run each test in a suite, and report the results. */
240 static int count_passed_suite_tests(suite_t
*suite
)
242 int passed_tests
= 0;
245 for (i
= 0; i
< suite
->numoftests
; i
++) {
246 passed_tests
+= child_test_passed(suite
,
252 /********************/
253 /* Public interface */
254 /********************/
256 #define DEFAULT_TIMEOUT 5U
257 #define DEFAULT_QUIETNESS 1
259 #define assert(condition, exit_status, ...) \
260 if (!(condition)) { \
261 _fatal(__FILE__, __LINE__, __func__, \
262 (exit_status), __VA_ARGS__); \
265 /* Include in tests whose expected outcome is a specific signal. */
266 #define expect_signal(signal) \
267 struct sigaction _act; \
268 _act.sa_handler = expected_signal_handler; \
269 sigemptyset(&_act.sa_mask); \
271 assert(sigaction((signal), &_act, NULL) == 0, 1, \
272 "sigaction() error: %s.", strerror(errno));
274 #define run_suite(set_up, tests, tear_down, ...) \
275 _run_suite((sizeof(tests)/sizeof(tests[0])), \
276 (set_up), (tests), (tear_down), __VA_ARGS__)
278 typedef unit_test_t UnitTests
[];
280 void _fatal(const char *file
, int line
, const char *function
,
281 int exit_status
, const char *format
, ...)
283 void _run_suite(int numoftests
, test_fn_t set_up
, UnitTests tests
,
284 test_fn_t tear_down
, const char *format
, ...)
286 void logv(char *format
, ...) __printflike(1, 2);
288 void _fatal(const char *file
, int line
, const char *function
,
289 int exit_status
, const char *format
, ...)
293 va_start(ap
, format
);
296 printf("Assert failed in file %s, function %s(), line %d.\n",
297 file
, function
, line
);
302 void _run_suite(int numoftests
, test_fn_t set_up
, UnitTests tests
,
303 test_fn_t tear_down
, const char *format
, ...)
308 va_start(ap
, format
);
309 die(vasprintf(&name
, format
, ap
) == -1, "vasprintf()");
311 suite_t
*suite
= create_suite(name
, numoftests
, set_up
, tests
,
313 log_suite_info(suite
);
314 log_suite_results(suite
, count_passed_suite_tests(suite
));
316 destroy_suite(suite
);
319 /* Signal handler for tests expected to terminate with a specific
321 void expected_signal_handler(int signo
)
323 write(1,"Child process received expected signal.\n",
324 strlen("Child process received expected signal.\n"));
328 /* Setters and getters for various test framework global
329 * variables. Should only be used outside of the test, set up and tear
332 /* Time out constraint for running a single test. */
333 void set_timeout(unsigned int time
)
338 unsigned int get_timeout()
343 /* Expected signal for a test, default is 0. */
344 void set_expected_signal(int signal
)
346 _expected_signal
= signal
;
349 int get_expected_signal()
351 return _expected_signal
;
354 /* Logging verbosity. */
355 void set_quietness(int value
)
365 /* For fixture set up and tear down functions, and units tests. */
369 /* Verbose (default) logging. */
370 void logv(char *format
, ...)
372 if (get_quietness() <= 0) {
375 va_start(ap
, format
);
378 die_on_stdout_error();
382 void log_aggregated_results()
384 printf("[SUMMARY] Aggregated Test Results\n");
385 printf("Total: %ju\n", results
.numoftests
);
386 printf("Passed: %ju\n", results
.passed_tests
);
387 printf("Failed: %ju\n\n", results
.numoftests
388 - results
.passed_tests
);
389 die_on_stdout_error();
392 /*******************************/
393 /*******************************/
394 /* pipes buffer unit testing */
395 /*******************************/
396 /*******************************/
398 static const char progname
[] = "pipes_unitester";
400 static void die_on_error(int condition
, const char *culprit
)
402 assert(!condition
, 1, "%s: %s error: %s.", progname
, culprit
,
407 /*******************************/
408 /* Usage and option processing */
409 /*******************************/
411 static void usage(int exit_status
)
413 printf("Usage : %s\n", progname
);
417 static void die_on_invalid_value(int condition
,
418 const char *value_string
)
421 printf("%s: invalid value: %s.\n", progname
, value_string
);
426 /* Convert a storage unit suffix into an exponent. */
427 static int strtoexp(const char *string
)
429 if (string
[0] == '\0') {
433 char first_letter
= toupper(string
[0]);
434 char prefixes
[] = "BKMGTPE";
435 const int numofprefixes
= strlen(prefixes
);
436 prefixes
[numofprefixes
] = first_letter
;
439 while (prefixes
[i
] != first_letter
) {
442 die_on_invalid_value(i
>= numofprefixes
|| (string
[1] != '\0' &&
449 static void process_options(int argc
, char *argv
[])
454 setvbuf(stdout
, NULL
, _IONBF
, 0);
456 set_timeout(DEFAULT_TIMEOUT
);
457 set_quietness(DEFAULT_QUIETNESS
);
459 while ((opt
= getopt(argc
, argv
, "t:vqh")) != -1) {
463 set_timeout(strtoul(optarg
, &endptr
, 0));
464 die_on_invalid_value(errno
== ERANGE
|| *endptr
!= '\0'
465 || endptr
== optarg
, optarg
);
468 set_quietness(get_quietness() + 1);
483 /*********************************/
484 /* Various function declarations */
485 /*********************************/
487 void initialize_data(int *ptr
, int len
);
489 int verify_data(int *base
, int *target
, int len
);
491 void clear_data(int *ptr
, int len
);
493 /*******************************/
494 /* Arrays for test suite loops */
495 /*******************************/
498 #define BUFMAXLEN (BUFMAX * sizeof(int))
500 const unsigned int pipesize_blocks
[] = {128,256,1024,2048,PAGE_SIZE
,PAGE_SIZE
*2,PAGE_SIZE
*4};
501 static const int bufsizes
[] = { 128, 512, 1024, 2048, 4096, 16384 };
503 int data
[BUFMAX
],readbuf
[BUFMAX
];
504 int pipefd
[2] = {0,0};
506 typedef int * pipe_t
;
508 struct thread_work_data
{
510 unsigned int total_bytes
;
511 unsigned int chunk_size
;
514 void * reader_thread(void *ptr
);
515 void * writer_thread(void *ptr
);
517 dispatch_semaphore_t r_sem
, w_sem
;
519 unsigned long current_buf_size
=0;
521 /*************************************/
522 /* Global variables set up functions */
523 /*************************************/
526 void initialize_data(int *ptr
, int len
)
529 if (!ptr
|| len
<=0 )
532 for (i
= 0; i
< len
; i
++)
536 void clear_data(int *ptr
, int len
)
542 for (i
= 0; i
< len
; i
++)
546 int verify_data(int *base
, int *target
, int len
)
550 if (!base
|| !target
)
553 for (i
= 0; i
< len
; i
++){
554 if (base
[i
] != target
[i
])
561 void initialize_data_buffer()
563 initialize_data(data
, BUFMAX
);
564 initialize_data(readbuf
, BUFMAX
);
567 /*******************************/
568 /* core read write helper funtions */
569 /*******************************/
571 ssize_t
read_whole_buffer(pipe_t p
, void *scratch_buf
, int size
);
572 ssize_t
pipe_read_data(pipe_t p
, void *dest_buf
, int size
);
573 ssize_t
pipe_write_data(pipe_t p
, void *src_buf
, int size
);
575 ssize_t
read_whole_buffer(pipe_t p
, void *scratch_buf
, int size
)
578 logv("reading whole buffer from fd %d, size %d", fd
, size
);
579 int retval
= pread(fd
, scratch_buf
, size
, 0);
581 logv("Error reading whole buffer. (%d) %s\n",errno
, strerror(errno
));
587 ssize_t
pipe_read_data(pipe_t p
, void *dest_buf
, int size
)
590 //logv("reading from pipe %d, for size %d", fd, size);
591 int retval
= read(fd
, dest_buf
, size
);
593 logv("Error reading from buffer. (%d)",errno
);
598 ssize_t
pipe_write_data(pipe_t p
, void *src_buf
, int size
)
601 //logv("writing to pipe %d, for size %d", fd, size);
602 int retval
= write(fd
, src_buf
, size
);
604 logv("Error writing to buffer. (%d) %s",errno
, strerror(errno
));
610 void * reader_thread(void *ptr
)
612 struct thread_work_data
*m
;
613 m
= (struct thread_work_data
*) ptr
;
614 int i
= m
->total_bytes
/m
->chunk_size
;
615 int retval
, data_idx
=0;
617 dispatch_semaphore_wait(r_sem
, 8000);
618 retval
= pipe_read_data(m
->p
, &readbuf
[data_idx
], m
->chunk_size
);
619 assert(retval
== m
->chunk_size
, 1, "Pipe read returned different amount of numbe");
620 data_idx
+=m
->chunk_size
;
621 //logv("RD %d \n", m->chunk_size);
622 dispatch_semaphore_signal(w_sem
);
628 void * writer_thread(void *ptr
)
630 struct thread_work_data
*m
;
631 m
= (struct thread_work_data
*)ptr
;
632 int i
= m
->total_bytes
/m
->chunk_size
;
633 int retval
, data_idx
=0;
636 dispatch_semaphore_wait(w_sem
, 8000);
637 //logv("WR %d \n", m->chunk_size);
638 retval
=pipe_write_data(m
->p
, &data
[data_idx
], m
->chunk_size
);
639 assert(retval
== m
->chunk_size
, 1, "Pipe write failed");
640 data_idx
+=m
->chunk_size
;
641 dispatch_semaphore_signal(r_sem
);
648 void create_threads(struct thread_work_data
*rdata
, struct thread_work_data
*wdata
){
650 pthread_t thread1
, thread2
;
651 r_sem
= dispatch_semaphore_create(0);
652 w_sem
= dispatch_semaphore_create(1);
654 void * thread_ret1
=0;
655 void * thread_ret2
=0;
656 /* Create independent threads each of which will execute function */
658 iret1
= pthread_create( &thread1
, NULL
, reader_thread
, (void*) rdata
);
659 iret2
= pthread_create( &thread2
, NULL
, writer_thread
, (void*) wdata
);
661 pthread_join( thread2
, &thread_ret1
);
662 pthread_join( thread1
, &thread_ret1
);
663 assert(thread_ret1
== 0, 1, "Reader Thread Failed");
664 assert(thread_ret2
== 0, 1, "Writer Thread Failed");
668 /*******************************/
669 /* Pipes unit test functions */
670 /*******************************/
671 void test_pipebuffer_setup ()
674 logv("Setting up buffers data and readbuf\n");
675 clear_data(data
, BUFMAX
);
676 clear_data(readbuf
, BUFMAX
);
677 logv("Initializing buffers data and readbuf\n");
678 initialize_data(data
, BUFMAX
);
679 initialize_data(readbuf
, BUFMAX
);
680 logv("verifying data for correctness\n");
681 die_on_error(!verify_data(data
, readbuf
, BUFMAX
), "data initialization");
682 clear_data(readbuf
, BUFMAX
);
685 void test_pipe_create(){
686 int pipefds
[2] = {0,0};
690 logv("error opening pipes (%d) %s", errno
, strerror(errno
));
694 die_on_error(0 != close(pipefds
[0]), "close()");
695 die_on_error(0 != close(pipefds
[1]), "close()");
698 void test_pipe_write_single_byte(){
699 int pipefds
[2] = { 0 , 0 };
701 die_on_error( 0 != pipe(p
), "pipe()");
702 initialize_data_buffer();
704 for ( ; i
< current_buf_size
; i
++){
706 logv("cannot fill continuously beyond 16K.");
709 retval
=pipe_write_data(p
, &data
[i
], 1);
710 assert(retval
== 1, 1, "Pipe write failed");
717 void test_pipe_single_read_write(){
718 int pipefds
[2] = { 0 , 0 };
720 die_on_error( 0 != pipe(p
), "pipe()");
721 initialize_data_buffer();
722 struct thread_work_data d
= { p
, current_buf_size
, 1};
723 create_threads(&d
, &d
);
724 verify_data(data
, readbuf
, current_buf_size
);
730 void test_pipe_single_read_2write(){
731 int pipefds
[2] = { 0 , 0 };
733 die_on_error( 0 != pipe(p
), "pipe()");
734 initialize_data_buffer();
735 struct thread_work_data rd
= { p
, current_buf_size
, 1};
736 struct thread_work_data wd
= { p
, current_buf_size
, 2};
737 create_threads(&rd
, &wd
);
738 verify_data(data
, readbuf
, current_buf_size
);
744 void test_pipe_expansion_buffer(){
745 int pipefds
[2] = { 0 , 0 };
748 die_on_error( 0 != pipe(p
), "pipe()");
749 initialize_data_buffer();
750 for ( iter
=0; iter
< sizeof(pipesize_blocks
)/sizeof(unsigned int); iter
++){
751 assert(pipesize_blocks
[iter
] == pipe_write_data(p
, &data
[0], pipesize_blocks
[iter
] ), 1, "expansion write failed");
752 assert(pipesize_blocks
[iter
] == pipe_read_data(p
, &readbuf
[0], pipesize_blocks
[iter
]+200), 1, "reading from expanded data failed");
753 /* logv("finished round for size %u \n", pipesize_blocks[iter]); */
755 verify_data(data
, readbuf
, current_buf_size
);
761 void test_pipe_initial_big_allocation(){
762 int pipefds
[2] = { 0 , 0 };
765 die_on_error( 0 != pipe(p
), "pipe()");
766 initialize_data_buffer();
767 assert(current_buf_size
== pipe_write_data(p
, &data
[0], current_buf_size
), 1, "initial big allocation failed");
768 assert(current_buf_size
== pipe_read_data(p
, &readbuf
[0], current_buf_size
+200), 1, "reading from initial big write failed");
769 assert(verify_data(data
, readbuf
, current_buf_size
), 1, "big pipe initial allocation -not able to verify data");
775 void test_pipe_cycle_small_writes(){
776 int pipefds
[2] = { 0 , 0 };
779 die_on_error( 0 != pipe(p
), "pipe()");
780 initialize_data_buffer();
781 int buf_size
= current_buf_size
/ 2;
783 assert(buf_size
== pipe_write_data(p
, &data
[0], buf_size
), 1, "cycle write failed");
784 assert(buf_size
== pipe_read_data(p
, &readbuf
[0], buf_size
+200), 1, "reading from cycle read failed");
785 assert(verify_data(data
, readbuf
, buf_size
), 1, "data verification failed");
787 assert(buf_size
== pipe_write_data(p
, &data
[0], buf_size
), 1, "cycle write failed");
788 assert(buf_size
== pipe_read_data(p
, &readbuf
[0], buf_size
+200), 1, "reading from cycle read failed");
789 assert(verify_data(data
, readbuf
, buf_size
), 1, "data verification failed");
791 assert(buf_size
== pipe_write_data(p
, &data
[0], buf_size
), 1, "cycle write failed");
792 assert(buf_size
== pipe_read_data(p
, &readbuf
[0], buf_size
+200), 1, "reading from cycle read failed");
793 assert(verify_data(data
, readbuf
, buf_size
), 1, "data verification failed");
800 void test_pipe_moving_data(){
801 int pipefds
[2] = { 0 , 0 };
804 die_on_error( 0 != pipe(p
), "pipe()");
805 initialize_data_buffer();
806 int buf_size
= current_buf_size
/ 2;
807 if (buf_size
> PAGE_SIZE
)
808 buf_size
= PAGE_SIZE
;
810 assert(buf_size
== pipe_write_data(p
, &data
[0], buf_size
), 1, "cycle write failed");
811 logv("write of size =%d\n", buf_size
);
812 assert(buf_size
== pipe_write_data(p
, &data
[buf_size
/sizeof(int)], buf_size
), 1, "cycle write failed");
813 logv("write of size =%d\n", buf_size
*2);
814 assert(buf_size
== pipe_write_data(p
, &data
[(buf_size
*2)/sizeof(int)], buf_size
), 1, "cycle write failed");
815 logv("write of size =%d\n", buf_size
*3);
816 assert((3*buf_size
) == pipe_read_data(p
, &readbuf
[0], (3*buf_size
)+200), 1, "reading from cycle read failed");
817 assert(verify_data(data
, readbuf
, (3*buf_size
)/sizeof(int)), 1, "data verification failed");
829 void run_pipe_basic_tests()
832 int numofsizes
= sizeof(bufsizes
)/sizeof(int);
834 logv("running tests for %d different sizes \n", numofsizes
);
836 UnitTests pipe_basic_tests
= {
837 { "1. create buffer and verify both reads/writes are valid",
838 test_pipebuffer_setup
},
839 { "2. open and close pipes", test_pipe_create
},
840 { "3. single byte write to full", test_pipe_write_single_byte
},
841 { "4. single byte read/write in sync", test_pipe_single_read_write
},
842 { "5. single byte read/2write in sync", test_pipe_single_read_2write
},
843 { "6. expansion from existing size", test_pipe_expansion_buffer
},
844 { "7. initial big allocation " , test_pipe_initial_big_allocation
},
845 { "8. cycle_small_writes " ,test_pipe_cycle_small_writes
},
846 { "9. test moving data " ,test_pipe_moving_data
}
848 for (sizes_idx
= 0; sizes_idx
< numofsizes
; sizes_idx
++) {
849 current_buf_size
= bufsizes
[sizes_idx
];
850 run_suite(do_nothing
,
852 do_nothing
, "pipe create base test "
854 (uintmax_t)bufsizes
[sizes_idx
],
855 (uintmax_t)bufsizes
[sizes_idx
]);
860 int pipes_test(void *the_argp
)
863 run_pipe_basic_tests();
864 //log_aggregated_results();
865 return results
.numoftests
- results
.passed_tests
;
869 * retaining the old main function to debug issues with the tests and not the xnu_quick_test framework
872 int main_nonuse(int argc
, char *argv
[])
874 process_options(argc
, argv
);
876 run_pipe_basic_tests();
878 log_aggregated_results();