tinytest_demo.c (8480B)
1 /* tinytest_demo.c -- Copyright 2009-2012 Nick Mathewson 2 * 3 * Redistribution and use in source and binary forms, with or without 4 * modification, are permitted provided that the following conditions 5 * are met: 6 * 1. Redistributions of source code must retain the above copyright 7 * notice, this list of conditions and the following disclaimer. 8 * 2. Redistributions in binary form must reproduce the above copyright 9 * notice, this list of conditions and the following disclaimer in the 10 * documentation and/or other materials provided with the distribution. 11 * 3. The name of the author may not be used to endorse or promote products 12 * derived from this software without specific prior written permission. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26 27 /* Welcome to the example file for tinytest! I'll show you how to set up 28 * some simple and not-so-simple testcases. */ 29 30 /* Make sure you include these headers. */ 31 #include "tinytest.h" 32 #include "tinytest_macros.h" 33 34 #include <stdio.h> 35 #include <stdlib.h> 36 #include <string.h> 37 #include <errno.h> 38 #include <time.h> 39 #ifdef _WIN32 40 #include <windows.h> 41 #else 42 #include <unistd.h> 43 #endif 44 45 /* ============================================================ */ 46 47 /* First, let's see if strcmp is working. (All your test cases should be 48 * functions declared to take a single void * as an argument.) */ 49 void 50 test_strcmp(void *data) 51 { 52 (void)data; /* This testcase takes no data. */ 53 54 /* Let's make sure the empty string is equal to itself */ 55 if (strcmp("","")) { 56 /* This macro tells tinytest to stop the current test 57 * and go straight to the "end" label. */ 58 tt_abort_msg("The empty string was not equal to itself"); 59 } 60 61 /* Pretty often, calling tt_abort_msg to indicate failure is more 62 heavy-weight than you want. Instead, just say: */ 63 tt_assert(strcmp("testcase", "testcase") == 0); 64 65 /* Occasionally, you don't want to stop the current testcase just 66 because a single assertion has failed. In that case, use 67 tt_want: */ 68 tt_want(strcmp("tinytest", "testcase") > 0); 69 70 /* You can use the tt_*_op family of macros to compare values and to 71 fail unless they have the relationship you want. They produce 72 more useful output than tt_assert, since they display the actual 73 values of the failing things. 74 75 Fail unless strcmp("abc, "abc") == 0 */ 76 tt_int_op(strcmp("abc", "abc"), ==, 0); 77 78 /* Fail unless strcmp("abc, "abcd") is less than 0 */ 79 tt_int_op(strcmp("abc", "abcd"), < , 0); 80 81 /* Incidentally, there's a test_str_op that uses strcmp internally. */ 82 tt_str_op("abc", <, "abcd"); 83 84 85 /* Every test-case function needs to finish with an "end:" 86 label and (optionally) code to clean up local variables. */ 87 end: 88 ; 89 } 90 91 /* ============================================================ */ 92 93 /* Now let's mess with setup and teardown functions! These are handy if 94 you have a bunch of tests that all need a similar environment, and you 95 want to reconstruct that environment freshly for each one. */ 96 97 /* First you declare a type to hold the environment info, and functions to 98 set it up and tear it down. */ 99 struct data_buffer { 100 /* We're just going to have couple of character buffer. Using 101 setup/teardown functions is probably overkill for this case. 102 103 You could also do file descriptors, complicated handles, temporary 104 files, etc. */ 105 char buffer1[512]; 106 char buffer2[512]; 107 }; 108 /* The setup function needs to take a const struct testcase_t and return 109 void* */ 110 void * 111 setup_data_buffer(const struct testcase_t *testcase) 112 { 113 struct data_buffer *db = malloc(sizeof(struct data_buffer)); 114 115 /* If you had a complicated set of setup rules, you might behave 116 differently here depending on testcase->flags or 117 testcase->setup_data or even or testcase->name. */ 118 119 /* Returning a NULL here would mean that we couldn't set up for this 120 test, so we don't need to test db for null. */ 121 return db; 122 } 123 /* The clean function deallocates storage carefully and returns true on 124 success. */ 125 int 126 clean_data_buffer(const struct testcase_t *testcase, void *ptr) 127 { 128 struct data_buffer *db = ptr; 129 130 if (db) { 131 free(db); 132 return 1; 133 } 134 return 0; 135 } 136 /* Finally, declare a testcase_setup_t with these functions. */ 137 struct testcase_setup_t data_buffer_setup = { 138 setup_data_buffer, clean_data_buffer 139 }; 140 141 142 /* Now let's write our test. */ 143 void 144 test_memcpy(void *ptr) 145 { 146 /* This time, we use the argument. */ 147 struct data_buffer *db = ptr; 148 149 /* We'll also introduce a local variable that might need cleaning up. */ 150 char *mem = NULL; 151 152 /* Let's make sure that memcpy does what we'd like. */ 153 strcpy(db->buffer1, "String 0"); 154 memcpy(db->buffer2, db->buffer1, sizeof(db->buffer1)); 155 tt_str_op(db->buffer1, ==, db->buffer2); 156 157 /* This one works if there's an internal NUL. */ 158 tt_mem_op(db->buffer1, <, db->buffer2, sizeof(db->buffer1)); 159 160 /* Now we've allocated memory that's referenced by a local variable. 161 The end block of the function will clean it up. */ 162 mem = strdup("Hello world."); 163 tt_assert(mem); 164 165 /* Another rather trivial test. */ 166 tt_str_op(db->buffer1, !=, mem); 167 168 end: 169 /* This time our end block has something to do. */ 170 if (mem) 171 free(mem); 172 } 173 174 void 175 test_timeout(void *ptr) 176 { 177 time_t t1, t2; 178 (void)ptr; 179 t1 = time(NULL); 180 #ifdef _WIN32 181 Sleep(5000); 182 #else 183 sleep(5); 184 #endif 185 t2 = time(NULL); 186 187 tt_int_op(t2-t1, >=, 4); 188 189 tt_int_op(t2-t1, <=, 6); 190 191 end: 192 ; 193 } 194 195 void 196 test_timeout_retry(void *ptr) 197 { 198 static int i = 0; 199 200 ++i; 201 tt_int_op(i, !=, 1); 202 203 time_t t1, t2; 204 (void)ptr; 205 t1 = time(NULL); 206 #ifdef _WIN32 207 Sleep(5000); 208 #else 209 sleep(5); 210 #endif 211 t2 = time(NULL); 212 213 tt_int_op(t2-t1, >=, 4); 214 215 tt_int_op(t2-t1, <=, 6); 216 217 end: 218 ; 219 } 220 221 /* ============================================================ */ 222 223 /* Now we need to make sure that our tests get invoked. First, you take 224 a bunch of related tests and put them into an array of struct testcase_t. 225 */ 226 227 struct testcase_t demo_tests[] = { 228 /* Here's a really simple test: it has a name you can refer to it 229 with, and a function to invoke it. */ 230 { "strcmp", test_strcmp, }, 231 232 /* The second test has a flag, "TT_FORK", to make it run in a 233 subprocess, and a pointer to the testcase_setup_t that configures 234 its environment. */ 235 { "memcpy", test_memcpy, TT_FORK, &data_buffer_setup }, 236 237 /* This flag is off-by-default, since it takes a while to run. You 238 * can enable it manually by passing +demo/timeout at the command line.*/ 239 { "timeout", test_timeout, TT_OFF_BY_DEFAULT }, 240 241 /* This test will be retried. (and it will not pass from the first 242 * time) */ 243 { "timeout_retry", test_timeout_retry, TT_RETRIABLE }, 244 245 /* The array has to end with END_OF_TESTCASES. */ 246 END_OF_TESTCASES 247 }; 248 249 /* Next, we make an array of testgroups. This is mandatory. Unlike more 250 heavy-duty testing frameworks, groups can't nest. */ 251 struct testgroup_t groups[] = { 252 253 /* Every group has a 'prefix', and an array of tests. That's it. */ 254 { "demo/", demo_tests }, 255 256 END_OF_GROUPS 257 }; 258 259 /* We can also define test aliases. These can be used for types of tests that 260 * cut across groups. */ 261 const char *alltests[] = { "+..", NULL }; 262 const char *slowtests[] = { "+demo/timeout", NULL }; 263 struct testlist_alias_t aliases[] = { 264 265 { "ALL", alltests }, 266 { "SLOW", slowtests }, 267 268 END_OF_ALIASES 269 }; 270 271 272 int 273 main(int c, const char **v) 274 { 275 /* Finally, just call tinytest_main(). It lets you specify verbose 276 or quiet output with --verbose and --quiet. You can list 277 specific tests: 278 279 tinytest-demo demo/memcpy 280 281 or use a ..-wildcard to select multiple tests with a common 282 prefix: 283 284 tinytest-demo demo/.. 285 286 If you list no tests, you get them all by default, so that 287 "tinytest-demo" and "tinytest-demo .." mean the same thing. 288 289 */ 290 tinytest_set_aliases(aliases); 291 return tinytest_main(c, v, groups); 292 }