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>
49 /**************************/
50 /**************************/
51 /* Unit Testing Framework */
52 /**************************/
53 /**************************/
55 /*********************/
56 /* Private interface */
57 /*********************/
59 static const char frameworkname
[] = "pipes_unitester";
61 /* Type for test, fixture set up and fixture tear down functions. */
62 typedef void (*test_fn_t
)();
64 /* Unit test structure. */
70 /* Test suite structure. */
80 unsigned int _timeout
= 0;
81 int _expected_signal
= 0;
85 uintmax_t passed_tests
;
88 void logr(char *format
, ...) __printflike(1, 2);
90 static void die(int condition
, const char *culprit
)
93 printf("%s: %s error: %s.\n", frameworkname
, culprit
,
99 static void die_on_stdout_error()
101 die(ferror(stdout
), "stdout");
104 /* Individual test result logging. */
105 void logr(char *format
, ...)
107 if (_quietness
<= 1) {
110 va_start(ap
, format
);
113 die_on_stdout_error();
117 static suite_t
*create_suite(const char *name
, int numoftests
,
118 test_fn_t set_up
, unit_test_t
*tests
,
121 suite_t
*suite
= (suite_t
*)malloc(sizeof(suite_t
));
122 die(suite
== NULL
, "malloc()");
125 suite
->numoftests
= numoftests
;
126 suite
->set_up
= set_up
;
127 suite
->tests
= tests
;
128 suite
->tear_down
= tear_down
;
132 static void destroy_suite(suite_t
*suite
)
137 static void log_suite_info(suite_t
*suite
)
139 logr("[TEST] %s\n", suite
->name
);
140 logr("Number of tests: %d\n\n", suite
->numoftests
);
143 static void log_suite_results(suite_t
*suite
, int passed_tests
)
145 results
.numoftests
+= (uintmax_t)suite
->numoftests
;
146 results
.passed_tests
+= (uintmax_t)passed_tests
;
149 static void log_test_info(unit_test_t
*unit_test
)
151 logr("[BEGIN] %s\n", unit_test
->name
);
154 static void log_test_result(unit_test_t
*unit_test
,
155 boolean_t test_passed
)
157 logr("[%s] %s\n\n", test_passed
? "PASS" : "FAIL",
161 /* Handler for test time out. */
162 static void alarm_handler(int signo
)
164 write(1,"Child process timed out.\n",
165 strlen("Child process timed out.\n"));
169 /* Run a test with fixture set up and teardown, while enforcing the
170 * time out constraint. */
171 static void run_test(suite_t
*suite
, unit_test_t
*unit_test
)
173 struct sigaction alarm_act
;
175 log_test_info(unit_test
);
176 alarm_act
.sa_handler
= alarm_handler
;
177 sigemptyset(&alarm_act
.sa_mask
);
178 alarm_act
.sa_flags
= 0;
179 die(sigaction(SIGALRM
, &alarm_act
, NULL
) != 0, "sigaction()");
187 /* Check a child return status. */
188 static boolean_t
child_terminated_normally(int child_status
)
190 boolean_t normal_exit
= FALSE
;
192 if (WIFEXITED(child_status
)) {
193 int exit_status
= WEXITSTATUS(child_status
);
195 printf("Child process unexpectedly exited with code "
196 "%d.\n", exit_status
);
197 } else if (!_expected_signal
) {
200 } else if (WIFSIGNALED(child_status
)) {
201 int signal
= WTERMSIG(child_status
);
202 if (signal
== _expected_signal
) {
203 if (_quietness
<= 0) {
204 printf("Child process died with expected signal "
209 printf("Child process unexpectedly died with signal "
213 printf("Child process unexpectedly did not exit nor "
216 die_on_stdout_error();
220 /* Run a test in its own process, and report the result. */
221 static boolean_t
child_test_passed(suite_t
*suite
,
222 unit_test_t
*unit_test
)
226 pid_t test_pid
= fork();
227 die(test_pid
== -1, "fork()");
229 run_test(suite
, unit_test
);
232 while (waitpid(test_pid
, &test_status
, 0) != test_pid
) {
235 boolean_t test_result
= child_terminated_normally(test_status
);
236 log_test_result(unit_test
, test_result
);
240 /* Run each test in a suite, and report the results. */
241 static int count_passed_suite_tests(suite_t
*suite
)
243 int passed_tests
= 0;
246 for (i
= 0; i
< suite
->numoftests
; i
++) {
247 passed_tests
+= child_test_passed(suite
,
253 /********************/
254 /* Public interface */
255 /********************/
257 #define DEFAULT_TIMEOUT 5U
258 #define DEFAULT_QUIETNESS 1
260 #define assert(condition, exit_status, ...) \
261 if (!(condition)) { \
262 _fatal(__FILE__, __LINE__, __func__, \
263 (exit_status), __VA_ARGS__); \
266 /* Include in tests whose expected outcome is a specific signal. */
267 #define expect_signal(signal) \
268 struct sigaction _act; \
269 _act.sa_handler = expected_signal_handler; \
270 sigemptyset(&_act.sa_mask); \
272 assert(sigaction((signal), &_act, NULL) == 0, 1, \
273 "sigaction() error: %s.", strerror(errno));
275 #define run_suite(set_up, tests, tear_down, ...) \
276 _run_suite((sizeof(tests)/sizeof(tests[0])), \
277 (set_up), (tests), (tear_down), __VA_ARGS__)
279 typedef unit_test_t UnitTests
[];
281 void _fatal(const char *file
, int line
, const char *function
,
282 int exit_status
, const char *format
, ...)
284 void _run_suite(int numoftests
, test_fn_t set_up
, UnitTests tests
,
285 test_fn_t tear_down
, const char *format
, ...)
287 void logv(char *format
, ...) __printflike(1, 2);
289 void _fatal(const char *file
, int line
, const char *function
,
290 int exit_status
, const char *format
, ...)
294 va_start(ap
, format
);
297 printf("Assert failed in file %s, function %s(), line %d.\n",
298 file
, function
, line
);
303 void _run_suite(int numoftests
, test_fn_t set_up
, UnitTests tests
,
304 test_fn_t tear_down
, const char *format
, ...)
309 va_start(ap
, format
);
310 die(vasprintf(&name
, format
, ap
) == -1, "vasprintf()");
312 suite_t
*suite
= create_suite(name
, numoftests
, set_up
, tests
,
314 log_suite_info(suite
);
315 log_suite_results(suite
, count_passed_suite_tests(suite
));
317 destroy_suite(suite
);
320 /* Signal handler for tests expected to terminate with a specific
322 void expected_signal_handler(int signo
)
324 write(1,"Child process received expected signal.\n",
325 strlen("Child process received expected signal.\n"));
329 /* Setters and getters for various test framework global
330 * variables. Should only be used outside of the test, set up and tear
333 /* Time out constraint for running a single test. */
334 void set_timeout(unsigned int time
)
339 unsigned int get_timeout()
344 /* Expected signal for a test, default is 0. */
345 void set_expected_signal(int signal
)
347 _expected_signal
= signal
;
350 int get_expected_signal()
352 return _expected_signal
;
355 /* Logging verbosity. */
356 void set_quietness(int value
)
366 /* For fixture set up and tear down functions, and units tests. */
370 /* Verbose (default) logging. */
371 void logv(char *format
, ...)
373 if (get_quietness() <= 0) {
376 va_start(ap
, format
);
379 die_on_stdout_error();
383 void log_aggregated_results()
385 printf("[SUMMARY] Aggregated Test Results\n");
386 printf("Total: %ju\n", results
.numoftests
);
387 printf("Passed: %ju\n", results
.passed_tests
);
388 printf("Failed: %ju\n\n", results
.numoftests
389 - results
.passed_tests
);
390 die_on_stdout_error();
393 /*******************************/
394 /*******************************/
395 /* pipes buffer unit testing */
396 /*******************************/
397 /*******************************/
399 static const char progname
[] = "pipes_unitester";
401 static void die_on_error(int condition
, const char *culprit
)
403 assert(!condition
, 1, "%s: %s error: %s.", progname
, culprit
,
408 /*******************************/
409 /* Usage and option processing */
410 /*******************************/
412 static void usage(int exit_status
)
414 printf("Usage : %s\n", progname
);
418 static void die_on_invalid_value(int condition
,
419 const char *value_string
)
422 printf("%s: invalid value: %s.\n", progname
, value_string
);
427 /* Convert a storage unit suffix into an exponent. */
428 static int strtoexp(const char *string
)
430 if (string
[0] == '\0') {
434 char first_letter
= toupper(string
[0]);
435 char prefixes
[] = "BKMGTPE";
436 const int numofprefixes
= strlen(prefixes
);
437 prefixes
[numofprefixes
] = first_letter
;
440 while (prefixes
[i
] != first_letter
) {
443 die_on_invalid_value(i
>= numofprefixes
|| (string
[1] != '\0' &&
450 static void process_options(int argc
, char *argv
[])
455 setvbuf(stdout
, NULL
, _IONBF
, 0);
457 set_timeout(DEFAULT_TIMEOUT
);
458 set_quietness(DEFAULT_QUIETNESS
);
460 while ((opt
= getopt(argc
, argv
, "t:vqh")) != -1) {
464 set_timeout(strtoul(optarg
, &endptr
, 0));
465 die_on_invalid_value(errno
== ERANGE
|| *endptr
!= '\0'
466 || endptr
== optarg
, optarg
);
469 set_quietness(get_quietness() + 1);
484 /*********************************/
485 /* Various function declarations */
486 /*********************************/
488 void initialize_data(int *ptr
, int len
);
490 int verify_data(int *base
, int *target
, int len
);
492 void clear_data(int *ptr
, int len
);
494 /*******************************/
495 /* Arrays for test suite loops */
496 /*******************************/
499 #define BUFMAXLEN (BUFMAX * sizeof(int))
501 const unsigned int pipesize_blocks
[] = {128,256,1024,2048,PAGE_SIZE
,PAGE_SIZE
*2,PAGE_SIZE
*4};
502 static const int bufsizes
[] = { 128, 512, 1024, 2048, 4096, 16384 };
504 int data
[BUFMAX
],readbuf
[BUFMAX
];
505 int pipefd
[2] = {0,0};
507 typedef int * pipe_t
;
509 struct thread_work_data
{
511 unsigned int total_bytes
;
512 unsigned int chunk_size
;
515 void * reader_thread(void *ptr
);
516 void * writer_thread(void *ptr
);
518 dispatch_semaphore_t r_sem
, w_sem
;
520 unsigned long current_buf_size
=0;
522 /*************************************/
523 /* Global variables set up functions */
524 /*************************************/
527 void initialize_data(int *ptr
, int len
)
530 if (!ptr
|| len
<=0 )
533 for (i
= 0; i
< len
; i
++)
537 void clear_data(int *ptr
, int len
)
543 for (i
= 0; i
< len
; i
++)
547 int verify_data(int *base
, int *target
, int len
)
551 if (!base
|| !target
)
554 for (i
= 0; i
< len
; i
++){
555 if (base
[i
] != target
[i
])
562 void initialize_data_buffer()
564 initialize_data(data
, BUFMAX
);
565 initialize_data(readbuf
, BUFMAX
);
568 /*******************************/
569 /* core read write helper funtions */
570 /*******************************/
572 ssize_t
read_whole_buffer(pipe_t p
, void *scratch_buf
, int size
);
573 ssize_t
pipe_read_data(pipe_t p
, void *dest_buf
, int size
);
574 ssize_t
pipe_write_data(pipe_t p
, void *src_buf
, int size
);
576 ssize_t
read_whole_buffer(pipe_t p
, void *scratch_buf
, int size
)
579 logv("reading whole buffer from fd %d, size %d", fd
, size
);
580 int retval
= pread(fd
, scratch_buf
, size
, 0);
582 logv("Error reading whole buffer. (%d) %s\n",errno
, strerror(errno
));
588 ssize_t
pipe_read_data(pipe_t p
, void *dest_buf
, int size
)
591 //logv("reading from pipe %d, for size %d", fd, size);
592 int retval
= read(fd
, dest_buf
, size
);
594 logv("Error reading from buffer. (%d)",errno
);
599 ssize_t
pipe_write_data(pipe_t p
, void *src_buf
, int size
)
602 //logv("writing to pipe %d, for size %d", fd, size);
603 int retval
= write(fd
, src_buf
, size
);
605 logv("Error writing to buffer. (%d) %s",errno
, strerror(errno
));
611 void * reader_thread(void *ptr
)
613 struct thread_work_data
*m
;
614 m
= (struct thread_work_data
*) ptr
;
615 int i
= m
->total_bytes
/m
->chunk_size
;
616 int retval
, data_idx
=0;
618 dispatch_semaphore_wait(r_sem
, 8000);
619 retval
= pipe_read_data(m
->p
, &readbuf
[data_idx
], m
->chunk_size
);
620 assert(retval
== m
->chunk_size
, 1, "Pipe read returned different amount of numbe");
621 data_idx
+=m
->chunk_size
;
622 //logv("RD %d \n", m->chunk_size);
623 dispatch_semaphore_signal(w_sem
);
629 void * writer_thread(void *ptr
)
631 struct thread_work_data
*m
;
632 m
= (struct thread_work_data
*)ptr
;
633 int i
= m
->total_bytes
/m
->chunk_size
;
634 int retval
, data_idx
=0;
637 dispatch_semaphore_wait(w_sem
, 8000);
638 //logv("WR %d \n", m->chunk_size);
639 retval
=pipe_write_data(m
->p
, &data
[data_idx
], m
->chunk_size
);
640 assert(retval
== m
->chunk_size
, 1, "Pipe write failed");
641 data_idx
+=m
->chunk_size
;
642 dispatch_semaphore_signal(r_sem
);
649 void create_threads(struct thread_work_data
*rdata
, struct thread_work_data
*wdata
){
651 pthread_t thread1
, thread2
;
652 r_sem
= dispatch_semaphore_create(0);
653 w_sem
= dispatch_semaphore_create(1);
655 void * thread_ret1
=0;
656 void * thread_ret2
=0;
657 /* Create independent threads each of which will execute function */
659 iret1
= pthread_create( &thread1
, NULL
, reader_thread
, (void*) rdata
);
660 iret2
= pthread_create( &thread2
, NULL
, writer_thread
, (void*) wdata
);
662 pthread_join( thread2
, &thread_ret1
);
663 pthread_join( thread1
, &thread_ret1
);
664 assert(thread_ret1
== 0, 1, "Reader Thread Failed");
665 assert(thread_ret2
== 0, 1, "Writer Thread Failed");
669 /*******************************/
670 /* Pipes unit test functions */
671 /*******************************/
672 void test_pipebuffer_setup ()
675 logv("Setting up buffers data and readbuf\n");
676 clear_data(data
, BUFMAX
);
677 clear_data(readbuf
, BUFMAX
);
678 logv("Initializing buffers data and readbuf\n");
679 initialize_data(data
, BUFMAX
);
680 initialize_data(readbuf
, BUFMAX
);
681 logv("verifying data for correctness\n");
682 die_on_error(!verify_data(data
, readbuf
, BUFMAX
), "data initialization");
683 clear_data(readbuf
, BUFMAX
);
686 void test_pipe_create(){
687 int pipefds
[2] = {0,0};
691 logv("error opening pipes (%d) %s", errno
, strerror(errno
));
695 die_on_error(0 != close(pipefds
[0]), "close()");
696 die_on_error(0 != close(pipefds
[1]), "close()");
699 void test_pipe_write_single_byte(){
700 int pipefds
[2] = { 0 , 0 };
702 die_on_error( 0 != pipe(p
), "pipe()");
703 initialize_data_buffer();
705 for ( ; i
< current_buf_size
; i
++){
707 logv("cannot fill continuously beyond 16K.");
710 retval
=pipe_write_data(p
, &data
[i
], 1);
711 assert(retval
== 1, 1, "Pipe write failed");
718 void test_pipe_single_read_write(){
719 int pipefds
[2] = { 0 , 0 };
721 die_on_error( 0 != pipe(p
), "pipe()");
722 initialize_data_buffer();
723 struct thread_work_data d
= { p
, current_buf_size
, 1};
724 create_threads(&d
, &d
);
725 verify_data(data
, readbuf
, current_buf_size
);
731 void test_pipe_single_read_2write(){
732 int pipefds
[2] = { 0 , 0 };
734 die_on_error( 0 != pipe(p
), "pipe()");
735 initialize_data_buffer();
736 struct thread_work_data rd
= { p
, current_buf_size
, 1};
737 struct thread_work_data wd
= { p
, current_buf_size
, 2};
738 create_threads(&rd
, &wd
);
739 verify_data(data
, readbuf
, current_buf_size
);
745 void test_pipe_expansion_buffer(){
746 int pipefds
[2] = { 0 , 0 };
749 die_on_error( 0 != pipe(p
), "pipe()");
750 initialize_data_buffer();
751 for ( iter
=0; iter
< sizeof(pipesize_blocks
)/sizeof(unsigned int); iter
++){
752 assert(pipesize_blocks
[iter
] == pipe_write_data(p
, &data
[0], pipesize_blocks
[iter
] ), 1, "expansion write failed");
753 assert(pipesize_blocks
[iter
] == pipe_read_data(p
, &readbuf
[0], pipesize_blocks
[iter
]+200), 1, "reading from expanded data failed");
754 /* logv("finished round for size %u \n", pipesize_blocks[iter]); */
756 verify_data(data
, readbuf
, current_buf_size
);
762 void test_pipe_initial_big_allocation(){
763 int pipefds
[2] = { 0 , 0 };
766 die_on_error( 0 != pipe(p
), "pipe()");
767 initialize_data_buffer();
768 assert(current_buf_size
== pipe_write_data(p
, &data
[0], current_buf_size
), 1, "initial big allocation failed");
769 assert(current_buf_size
== pipe_read_data(p
, &readbuf
[0], current_buf_size
+200), 1, "reading from initial big write failed");
770 assert(verify_data(data
, readbuf
, current_buf_size
), 1, "big pipe initial allocation -not able to verify data");
776 void test_pipe_cycle_small_writes(){
777 int pipefds
[2] = { 0 , 0 };
780 die_on_error( 0 != pipe(p
), "pipe()");
781 initialize_data_buffer();
782 int buf_size
= current_buf_size
/ 2;
784 assert(buf_size
== pipe_write_data(p
, &data
[0], buf_size
), 1, "cycle write failed");
785 assert(buf_size
== pipe_read_data(p
, &readbuf
[0], buf_size
+200), 1, "reading from cycle read failed");
786 assert(verify_data(data
, readbuf
, buf_size
), 1, "data verification failed");
788 assert(buf_size
== pipe_write_data(p
, &data
[0], buf_size
), 1, "cycle write failed");
789 assert(buf_size
== pipe_read_data(p
, &readbuf
[0], buf_size
+200), 1, "reading from cycle read failed");
790 assert(verify_data(data
, readbuf
, buf_size
), 1, "data verification failed");
792 assert(buf_size
== pipe_write_data(p
, &data
[0], buf_size
), 1, "cycle write failed");
793 assert(buf_size
== pipe_read_data(p
, &readbuf
[0], buf_size
+200), 1, "reading from cycle read failed");
794 assert(verify_data(data
, readbuf
, buf_size
), 1, "data verification failed");
801 void test_pipe_moving_data(){
802 int pipefds
[2] = { 0 , 0 };
805 die_on_error( 0 != pipe(p
), "pipe()");
806 initialize_data_buffer();
807 int buf_size
= current_buf_size
/ 2;
808 if (buf_size
> PAGE_SIZE
)
809 buf_size
= PAGE_SIZE
;
811 assert(buf_size
== pipe_write_data(p
, &data
[0], buf_size
), 1, "cycle write failed");
812 logv("write of size =%d\n", buf_size
);
813 assert(buf_size
== pipe_write_data(p
, &data
[buf_size
/sizeof(int)], buf_size
), 1, "cycle write failed");
814 logv("write of size =%d\n", buf_size
*2);
815 assert(buf_size
== pipe_write_data(p
, &data
[(buf_size
*2)/sizeof(int)], buf_size
), 1, "cycle write failed");
816 logv("write of size =%d\n", buf_size
*3);
817 assert((3*buf_size
) == pipe_read_data(p
, &readbuf
[0], (3*buf_size
)+200), 1, "reading from cycle read failed");
818 assert(verify_data(data
, readbuf
, (3*buf_size
)/sizeof(int)), 1, "data verification failed");
830 void run_pipe_basic_tests()
833 int numofsizes
= sizeof(bufsizes
)/sizeof(int);
835 logv("running tests for %d different sizes \n", numofsizes
);
837 UnitTests pipe_basic_tests
= {
838 { "1. create buffer and verify both reads/writes are valid",
839 test_pipebuffer_setup
},
840 { "2. open and close pipes", test_pipe_create
},
841 { "3. single byte write to full", test_pipe_write_single_byte
},
842 { "4. single byte read/write in sync", test_pipe_single_read_write
},
843 { "5. single byte read/2write in sync", test_pipe_single_read_2write
},
844 { "6. expansion from existing size", test_pipe_expansion_buffer
},
845 { "7. initial big allocation " , test_pipe_initial_big_allocation
},
846 { "8. cycle_small_writes " ,test_pipe_cycle_small_writes
},
847 { "9. test moving data " ,test_pipe_moving_data
}
849 for (sizes_idx
= 0; sizes_idx
< numofsizes
; sizes_idx
++) {
850 current_buf_size
= bufsizes
[sizes_idx
];
851 run_suite(do_nothing
,
853 do_nothing
, "pipe create base test "
855 (uintmax_t)bufsizes
[sizes_idx
],
856 (uintmax_t)bufsizes
[sizes_idx
]);
861 int pipes_test(void *the_argp
)
864 run_pipe_basic_tests();
865 //log_aggregated_results();
866 return results
.numoftests
- results
.passed_tests
;
870 * retaining the old main function to debug issues with the tests and not the xnu_quick_test framework
873 int main_nonuse(int argc
, char *argv
[])
875 process_options(argc
, argv
);
877 run_pipe_basic_tests();
879 log_aggregated_results();