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 #include <mach/vm_param.h>
50 /**************************/
51 /**************************/
52 /* Unit Testing Framework */
53 /**************************/
54 /**************************/
56 /*********************/
57 /* Private interface */
58 /*********************/
60 static const char frameworkname
[] = "pipes_unitester";
62 /* Type for test, fixture set up and fixture tear down functions. */
63 typedef void (*test_fn_t
)();
65 /* Unit test structure. */
71 /* Test suite structure. */
81 unsigned int _timeout
= 0;
82 int _expected_signal
= 0;
86 uintmax_t passed_tests
;
89 void logr(char *format
, ...) __printflike(1, 2);
91 static void die(int condition
, const char *culprit
)
94 printf("%s: %s error: %s.\n", frameworkname
, culprit
,
100 static void die_on_stdout_error()
102 die(ferror(stdout
), "stdout");
105 /* Individual test result logging. */
106 void logr(char *format
, ...)
108 if (_quietness
<= 1) {
111 va_start(ap
, format
);
114 die_on_stdout_error();
118 static suite_t
*create_suite(const char *name
, int numoftests
,
119 test_fn_t set_up
, unit_test_t
*tests
,
122 suite_t
*suite
= (suite_t
*)malloc(sizeof(suite_t
));
123 die(suite
== NULL
, "malloc()");
126 suite
->numoftests
= numoftests
;
127 suite
->set_up
= set_up
;
128 suite
->tests
= tests
;
129 suite
->tear_down
= tear_down
;
133 static void destroy_suite(suite_t
*suite
)
138 static void log_suite_info(suite_t
*suite
)
140 logr("[TEST] %s\n", suite
->name
);
141 logr("Number of tests: %d\n\n", suite
->numoftests
);
144 static void log_suite_results(suite_t
*suite
, int passed_tests
)
146 results
.numoftests
+= (uintmax_t)suite
->numoftests
;
147 results
.passed_tests
+= (uintmax_t)passed_tests
;
150 static void log_test_info(unit_test_t
*unit_test
)
152 logr("[BEGIN] %s\n", unit_test
->name
);
155 static void log_test_result(unit_test_t
*unit_test
,
156 boolean_t test_passed
)
158 logr("[%s] %s\n\n", test_passed
? "PASS" : "FAIL",
162 /* Handler for test time out. */
163 static void alarm_handler(int signo
)
165 write(1,"Child process timed out.\n",
166 strlen("Child process timed out.\n"));
170 /* Run a test with fixture set up and teardown, while enforcing the
171 * time out constraint. */
172 static void run_test(suite_t
*suite
, unit_test_t
*unit_test
)
174 struct sigaction alarm_act
;
176 log_test_info(unit_test
);
177 alarm_act
.sa_handler
= alarm_handler
;
178 sigemptyset(&alarm_act
.sa_mask
);
179 alarm_act
.sa_flags
= 0;
180 die(sigaction(SIGALRM
, &alarm_act
, NULL
) != 0, "sigaction()");
188 /* Check a child return status. */
189 static boolean_t
child_terminated_normally(int child_status
)
191 boolean_t normal_exit
= FALSE
;
193 if (WIFEXITED(child_status
)) {
194 int exit_status
= WEXITSTATUS(child_status
);
196 printf("Child process unexpectedly exited with code "
197 "%d.\n", exit_status
);
198 } else if (!_expected_signal
) {
201 } else if (WIFSIGNALED(child_status
)) {
202 int signal
= WTERMSIG(child_status
);
203 if (signal
== _expected_signal
) {
204 if (_quietness
<= 0) {
205 printf("Child process died with expected signal "
210 printf("Child process unexpectedly died with signal "
214 printf("Child process unexpectedly did not exit nor "
217 die_on_stdout_error();
221 /* Run a test in its own process, and report the result. */
222 static boolean_t
child_test_passed(suite_t
*suite
,
223 unit_test_t
*unit_test
)
227 pid_t test_pid
= fork();
228 die(test_pid
== -1, "fork()");
230 run_test(suite
, unit_test
);
233 while (waitpid(test_pid
, &test_status
, 0) != test_pid
) {
236 boolean_t test_result
= child_terminated_normally(test_status
);
237 log_test_result(unit_test
, test_result
);
241 /* Run each test in a suite, and report the results. */
242 static int count_passed_suite_tests(suite_t
*suite
)
244 int passed_tests
= 0;
247 for (i
= 0; i
< suite
->numoftests
; i
++) {
248 passed_tests
+= child_test_passed(suite
,
254 /********************/
255 /* Public interface */
256 /********************/
258 #define DEFAULT_TIMEOUT 5U
259 #define DEFAULT_QUIETNESS 1
261 #define assert(condition, exit_status, ...) \
262 if (!(condition)) { \
263 _fatal(__FILE__, __LINE__, __func__, \
264 (exit_status), __VA_ARGS__); \
267 /* Include in tests whose expected outcome is a specific signal. */
268 #define expect_signal(signal) \
269 struct sigaction _act; \
270 _act.sa_handler = expected_signal_handler; \
271 sigemptyset(&_act.sa_mask); \
273 assert(sigaction((signal), &_act, NULL) == 0, 1, \
274 "sigaction() error: %s.", strerror(errno));
276 #define run_suite(set_up, tests, tear_down, ...) \
277 _run_suite((sizeof(tests)/sizeof(tests[0])), \
278 (set_up), (tests), (tear_down), __VA_ARGS__)
280 typedef unit_test_t UnitTests
[];
282 void _fatal(const char *file
, int line
, const char *function
,
283 int exit_status
, const char *format
, ...)
285 void _run_suite(int numoftests
, test_fn_t set_up
, UnitTests tests
,
286 test_fn_t tear_down
, const char *format
, ...)
288 void logv(char *format
, ...) __printflike(1, 2);
290 void _fatal(const char *file
, int line
, const char *function
,
291 int exit_status
, const char *format
, ...)
295 va_start(ap
, format
);
298 printf("Assert failed in file %s, function %s(), line %d.\n",
299 file
, function
, line
);
304 void _run_suite(int numoftests
, test_fn_t set_up
, UnitTests tests
,
305 test_fn_t tear_down
, const char *format
, ...)
310 va_start(ap
, format
);
311 die(vasprintf(&name
, format
, ap
) == -1, "vasprintf()");
313 suite_t
*suite
= create_suite(name
, numoftests
, set_up
, tests
,
315 log_suite_info(suite
);
316 log_suite_results(suite
, count_passed_suite_tests(suite
));
318 destroy_suite(suite
);
321 /* Signal handler for tests expected to terminate with a specific
323 void expected_signal_handler(int signo
)
325 write(1,"Child process received expected signal.\n",
326 strlen("Child process received expected signal.\n"));
330 /* Setters and getters for various test framework global
331 * variables. Should only be used outside of the test, set up and tear
334 /* Time out constraint for running a single test. */
335 void set_timeout(unsigned int time
)
340 unsigned int get_timeout()
345 /* Expected signal for a test, default is 0. */
346 void set_expected_signal(int signal
)
348 _expected_signal
= signal
;
351 int get_expected_signal()
353 return _expected_signal
;
356 /* Logging verbosity. */
357 void set_quietness(int value
)
367 /* For fixture set up and tear down functions, and units tests. */
371 /* Verbose (default) logging. */
372 void logv(char *format
, ...)
374 if (get_quietness() <= 0) {
377 va_start(ap
, format
);
380 die_on_stdout_error();
384 void log_aggregated_results()
386 printf("[SUMMARY] Aggregated Test Results\n");
387 printf("Total: %ju\n", results
.numoftests
);
388 printf("Passed: %ju\n", results
.passed_tests
);
389 printf("Failed: %ju\n\n", results
.numoftests
390 - results
.passed_tests
);
391 die_on_stdout_error();
394 /*******************************/
395 /*******************************/
396 /* pipes buffer unit testing */
397 /*******************************/
398 /*******************************/
400 static const char progname
[] = "pipes_unitester";
402 static void die_on_error(int condition
, const char *culprit
)
404 assert(!condition
, 1, "%s: %s error: %s.", progname
, culprit
,
409 /*******************************/
410 /* Usage and option processing */
411 /*******************************/
413 static void usage(int exit_status
)
415 printf("Usage : %s\n", progname
);
419 static void die_on_invalid_value(int condition
,
420 const char *value_string
)
423 printf("%s: invalid value: %s.\n", progname
, value_string
);
428 /* Convert a storage unit suffix into an exponent. */
429 static int strtoexp(const char *string
)
431 if (string
[0] == '\0') {
435 char first_letter
= toupper(string
[0]);
436 char prefixes
[] = "BKMGTPE";
437 const int numofprefixes
= strlen(prefixes
);
438 prefixes
[numofprefixes
] = first_letter
;
441 while (prefixes
[i
] != first_letter
) {
444 die_on_invalid_value(i
>= numofprefixes
|| (string
[1] != '\0' &&
451 static void process_options(int argc
, char *argv
[])
456 setvbuf(stdout
, NULL
, _IONBF
, 0);
458 set_timeout(DEFAULT_TIMEOUT
);
459 set_quietness(DEFAULT_QUIETNESS
);
461 while ((opt
= getopt(argc
, argv
, "t:vqh")) != -1) {
465 set_timeout(strtoul(optarg
, &endptr
, 0));
466 die_on_invalid_value(errno
== ERANGE
|| *endptr
!= '\0'
467 || endptr
== optarg
, optarg
);
470 set_quietness(get_quietness() + 1);
485 /*********************************/
486 /* Various function declarations */
487 /*********************************/
489 void initialize_data(int *ptr
, int len
);
491 int verify_data(int *base
, int *target
, int len
);
493 void clear_data(int *ptr
, int len
);
495 /*******************************/
496 /* Arrays for test suite loops */
497 /*******************************/
500 #define BUFMAXLEN (BUFMAX * sizeof(int))
502 const unsigned int pipesize_blocks
[] = {128,256,1024,2048,4096,8192,16384};
503 static const int bufsizes
[] = { 128, 512, 1024, 2048, 4096, 16384 };
505 int data
[BUFMAX
],readbuf
[BUFMAX
];
506 int pipefd
[2] = {0,0};
508 typedef int * pipe_t
;
510 struct thread_work_data
{
512 unsigned int total_bytes
;
513 unsigned int chunk_size
;
516 void * reader_thread(void *ptr
);
517 void * writer_thread(void *ptr
);
519 dispatch_semaphore_t r_sem
, w_sem
;
521 unsigned long current_buf_size
=0;
523 /*************************************/
524 /* Global variables set up functions */
525 /*************************************/
528 void initialize_data(int *ptr
, int len
)
531 if (!ptr
|| len
<=0 )
534 for (i
= 0; i
< len
; i
++)
538 void clear_data(int *ptr
, int len
)
544 for (i
= 0; i
< len
; i
++)
548 int verify_data(int *base
, int *target
, int len
)
552 if (!base
|| !target
)
555 for (i
= 0; i
< len
; i
++){
556 if (base
[i
] != target
[i
])
563 void initialize_data_buffer()
565 initialize_data(data
, BUFMAX
);
566 initialize_data(readbuf
, BUFMAX
);
569 /*******************************/
570 /* core read write helper funtions */
571 /*******************************/
573 ssize_t
read_whole_buffer(pipe_t p
, void *scratch_buf
, int size
);
574 ssize_t
pipe_read_data(pipe_t p
, void *dest_buf
, int size
);
575 ssize_t
pipe_write_data(pipe_t p
, void *src_buf
, int size
);
577 ssize_t
read_whole_buffer(pipe_t p
, void *scratch_buf
, int size
)
580 logv("reading whole buffer from fd %d, size %d", fd
, size
);
581 int retval
= pread(fd
, scratch_buf
, size
, 0);
583 logv("Error reading whole buffer. (%d) %s\n",errno
, strerror(errno
));
589 ssize_t
pipe_read_data(pipe_t p
, void *dest_buf
, int size
)
592 //logv("reading from pipe %d, for size %d", fd, size);
593 int retval
= read(fd
, dest_buf
, size
);
595 logv("Error reading from buffer. (%d)",errno
);
600 ssize_t
pipe_write_data(pipe_t p
, void *src_buf
, int size
)
603 //logv("writing to pipe %d, for size %d", fd, size);
604 int retval
= write(fd
, src_buf
, size
);
606 logv("Error writing to buffer. (%d) %s",errno
, strerror(errno
));
612 void * reader_thread(void *ptr
)
614 struct thread_work_data
*m
;
615 m
= (struct thread_work_data
*) ptr
;
616 int i
= m
->total_bytes
/m
->chunk_size
;
617 int retval
, data_idx
=0;
619 dispatch_semaphore_wait(r_sem
, 8000);
620 retval
= pipe_read_data(m
->p
, &readbuf
[data_idx
], m
->chunk_size
);
621 assert(retval
== m
->chunk_size
, 1, "Pipe read returned different amount of numbe");
622 data_idx
+=m
->chunk_size
;
623 //logv("RD %d \n", m->chunk_size);
624 dispatch_semaphore_signal(w_sem
);
630 void * writer_thread(void *ptr
)
632 struct thread_work_data
*m
;
633 m
= (struct thread_work_data
*)ptr
;
634 int i
= m
->total_bytes
/m
->chunk_size
;
635 int retval
, data_idx
=0;
638 dispatch_semaphore_wait(w_sem
, 8000);
639 //logv("WR %d \n", m->chunk_size);
640 retval
=pipe_write_data(m
->p
, &data
[data_idx
], m
->chunk_size
);
641 assert(retval
== m
->chunk_size
, 1, "Pipe write failed");
642 data_idx
+=m
->chunk_size
;
643 dispatch_semaphore_signal(r_sem
);
650 void create_threads(struct thread_work_data
*rdata
, struct thread_work_data
*wdata
){
652 pthread_t thread1
, thread2
;
653 r_sem
= dispatch_semaphore_create(0);
654 w_sem
= dispatch_semaphore_create(1);
656 void * thread_ret1
=0;
657 void * thread_ret2
=0;
658 /* Create independent threads each of which will execute function */
660 iret1
= pthread_create( &thread1
, NULL
, reader_thread
, (void*) rdata
);
661 iret2
= pthread_create( &thread2
, NULL
, writer_thread
, (void*) wdata
);
663 pthread_join( thread2
, &thread_ret1
);
664 pthread_join( thread1
, &thread_ret1
);
665 assert(thread_ret1
== 0, 1, "Reader Thread Failed");
666 assert(thread_ret2
== 0, 1, "Writer Thread Failed");
670 /*******************************/
671 /* Pipes unit test functions */
672 /*******************************/
673 void test_pipebuffer_setup ()
676 logv("Setting up buffers data and readbuf\n");
677 clear_data(data
, BUFMAX
);
678 clear_data(readbuf
, BUFMAX
);
679 logv("Initializing buffers data and readbuf\n");
680 initialize_data(data
, BUFMAX
);
681 initialize_data(readbuf
, BUFMAX
);
682 logv("verifying data for correctness\n");
683 die_on_error(!verify_data(data
, readbuf
, BUFMAX
), "data initialization");
684 clear_data(readbuf
, BUFMAX
);
687 void test_pipe_create(){
688 int pipefds
[2] = {0,0};
692 logv("error opening pipes (%d) %s", errno
, strerror(errno
));
696 die_on_error(0 != close(pipefds
[0]), "close()");
697 die_on_error(0 != close(pipefds
[1]), "close()");
700 void test_pipe_write_single_byte(){
701 int pipefds
[2] = { 0 , 0 };
703 die_on_error( 0 != pipe(p
), "pipe()");
704 initialize_data_buffer();
706 for ( ; i
< current_buf_size
; i
++){
708 logv("cannot fill continuously beyond 16K.");
711 retval
=pipe_write_data(p
, &data
[i
], 1);
712 assert(retval
== 1, 1, "Pipe write failed");
719 void test_pipe_single_read_write(){
720 int pipefds
[2] = { 0 , 0 };
722 die_on_error( 0 != pipe(p
), "pipe()");
723 initialize_data_buffer();
724 struct thread_work_data d
= { p
, current_buf_size
, 1};
725 create_threads(&d
, &d
);
726 verify_data(data
, readbuf
, current_buf_size
);
732 void test_pipe_single_read_2write(){
733 int pipefds
[2] = { 0 , 0 };
735 die_on_error( 0 != pipe(p
), "pipe()");
736 initialize_data_buffer();
737 struct thread_work_data rd
= { p
, current_buf_size
, 1};
738 struct thread_work_data wd
= { p
, current_buf_size
, 2};
739 create_threads(&rd
, &wd
);
740 verify_data(data
, readbuf
, current_buf_size
);
746 void test_pipe_expansion_buffer(){
747 int pipefds
[2] = { 0 , 0 };
750 die_on_error( 0 != pipe(p
), "pipe()");
751 initialize_data_buffer();
752 for ( iter
=0; iter
< sizeof(pipesize_blocks
)/sizeof(unsigned int); iter
++){
753 assert(pipesize_blocks
[iter
] == pipe_write_data(p
, &data
[0], pipesize_blocks
[iter
] ), 1, "expansion write failed");
754 assert(pipesize_blocks
[iter
] == pipe_read_data(p
, &readbuf
[0], pipesize_blocks
[iter
]+200), 1, "reading from expanded data failed");
755 /* logv("finished round for size %u \n", pipesize_blocks[iter]); */
757 verify_data(data
, readbuf
, current_buf_size
);
763 void test_pipe_initial_big_allocation(){
764 int pipefds
[2] = { 0 , 0 };
767 die_on_error( 0 != pipe(p
), "pipe()");
768 initialize_data_buffer();
769 assert(current_buf_size
== pipe_write_data(p
, &data
[0], current_buf_size
), 1, "initial big allocation failed");
770 assert(current_buf_size
== pipe_read_data(p
, &readbuf
[0], current_buf_size
+200), 1, "reading from initial big write failed");
771 assert(verify_data(data
, readbuf
, current_buf_size
), 1, "big pipe initial allocation -not able to verify data");
777 void test_pipe_cycle_small_writes(){
778 int pipefds
[2] = { 0 , 0 };
781 die_on_error( 0 != pipe(p
), "pipe()");
782 initialize_data_buffer();
783 int buf_size
= current_buf_size
/ 2;
785 assert(buf_size
== pipe_write_data(p
, &data
[0], buf_size
), 1, "cycle write failed");
786 assert(buf_size
== pipe_read_data(p
, &readbuf
[0], buf_size
+200), 1, "reading from cycle read failed");
787 assert(verify_data(data
, readbuf
, buf_size
), 1, "data verification failed");
789 assert(buf_size
== pipe_write_data(p
, &data
[0], buf_size
), 1, "cycle write failed");
790 assert(buf_size
== pipe_read_data(p
, &readbuf
[0], buf_size
+200), 1, "reading from cycle read failed");
791 assert(verify_data(data
, readbuf
, buf_size
), 1, "data verification failed");
793 assert(buf_size
== pipe_write_data(p
, &data
[0], buf_size
), 1, "cycle write failed");
794 assert(buf_size
== pipe_read_data(p
, &readbuf
[0], buf_size
+200), 1, "reading from cycle read failed");
795 assert(verify_data(data
, readbuf
, buf_size
), 1, "data verification failed");
802 void test_pipe_moving_data(){
803 int pipefds
[2] = { 0 , 0 };
806 die_on_error( 0 != pipe(p
), "pipe()");
807 initialize_data_buffer();
808 int buf_size
= current_buf_size
/ 2;
809 if (buf_size
> PAGE_SIZE
)
810 buf_size
= PAGE_SIZE
;
812 assert(buf_size
== pipe_write_data(p
, &data
[0], buf_size
), 1, "cycle write failed");
813 logv("write of size =%d\n", buf_size
);
814 assert(buf_size
== pipe_write_data(p
, &data
[buf_size
/sizeof(int)], buf_size
), 1, "cycle write failed");
815 logv("write of size =%d\n", buf_size
*2);
816 assert(buf_size
== pipe_write_data(p
, &data
[(buf_size
*2)/sizeof(int)], buf_size
), 1, "cycle write failed");
817 logv("write of size =%d\n", buf_size
*3);
818 assert((3*buf_size
) == pipe_read_data(p
, &readbuf
[0], (3*buf_size
)+200), 1, "reading from cycle read failed");
819 assert(verify_data(data
, readbuf
, (3*buf_size
)/sizeof(int)), 1, "data verification failed");
831 void run_pipe_basic_tests()
834 int numofsizes
= sizeof(bufsizes
)/sizeof(int);
836 logv("running tests for %d different sizes \n", numofsizes
);
838 UnitTests pipe_basic_tests
= {
839 { "1. create buffer and verify both reads/writes are valid",
840 test_pipebuffer_setup
},
841 { "2. open and close pipes", test_pipe_create
},
842 { "3. single byte write to full", test_pipe_write_single_byte
},
843 { "4. single byte read/write in sync", test_pipe_single_read_write
},
844 { "5. single byte read/2write in sync", test_pipe_single_read_2write
},
845 { "6. expansion from existing size", test_pipe_expansion_buffer
},
846 { "7. initial big allocation " , test_pipe_initial_big_allocation
},
847 { "8. cycle_small_writes " ,test_pipe_cycle_small_writes
},
848 { "9. test moving data " ,test_pipe_moving_data
}
850 for (sizes_idx
= 0; sizes_idx
< numofsizes
; sizes_idx
++) {
851 current_buf_size
= bufsizes
[sizes_idx
];
852 run_suite(do_nothing
,
854 do_nothing
, "pipe create base test "
856 (uintmax_t)bufsizes
[sizes_idx
],
857 (uintmax_t)bufsizes
[sizes_idx
]);
862 int pipes_test(void *the_argp
)
865 run_pipe_basic_tests();
866 //log_aggregated_results();
867 return results
.numoftests
- results
.passed_tests
;
871 * retaining the old main function to debug issues with the tests and not the xnu_quick_test framework
874 int main_nonuse(int argc
, char *argv
[])
876 process_options(argc
, argv
);
878 run_pipe_basic_tests();
880 log_aggregated_results();