1 /* 2 * Copyright (c) 2004, Bull S.A.. All rights reserved. 3 * Created by: Sebastien Decugis 4 5 * This program is free software; you can redistribute it and/or modify it 6 * under the terms of version 2 of the GNU General Public License as 7 * published by the Free Software Foundation. 8 * 9 * This program is distributed in the hope that it would be useful, but 10 * WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 * 13 * You should have received a copy of the GNU General Public License along 14 * with this program; if not, write the Free Software Foundation, Inc., 15 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 16 17 * This file is a helper file for the pthread_create tests 18 * It defines the following objects: 19 * scenarii: array of struct __scenario type. 20 * NSCENAR : macro giving the total # of scenarii 21 * scenar_init(): function to call before use the scenarii array. 22 * scenar_fini(): function to call after end of use of the scenarii array. 23 * 24 */ 25 26 struct __scenario { 27 /* Object to hold the given configuration, and which will be used to create the threads */ 28 pthread_attr_t ta; 29 /* General parameters */ 30 int detached; /* 0 => joinable; 1 => detached */ 31 /* Scheduling parameters */ 32 int explicitsched; /* 0 => sched policy is inherited; 1 => sched policy from the attr param */ 33 int schedpolicy; /* 0 => default; 1=> SCHED_FIFO; 2=> SCHED_RR */ 34 int schedparam; /* 0 => default sched param; 1 => max value for sched param; -1 => min value for sched param */ 35 int altscope; /* 0 => default contension scope; 1 => alternative contension scope */ 36 /* Stack parameters */ 37 int altstack; /* 0 => system manages the stack; 1 => stack is provided */ 38 int guard; /* 0 => default guardsize; 1=> guardsize is 0; 2=> guard is 1 page -- this setting only affect system stacks (not user's). */ 39 int altsize; /* 0 => default stack size; 1 => stack size specified (min value) -- ignored when stack is provided */ 40 /* Additionnal information */ 41 char *descr; /* object description */ 42 void *bottom; /* Stores the stack start when an alternate stack is required */ 43 int result; /* This thread creation is expected to: 0 => succeed; 1 => fail; 2 => unknown */ 44 sem_t sem; /* This semaphore is used to signal the end of the detached threads execution */ 45 } scenarii[] = 46 #define CASE(det,expl,scp,spa,sco,sta,gua,ssi,desc,res) \ 47 { \ 48 .detached=det, \ 49 .explicitsched=expl, \ 50 .schedpolicy=scp, \ 51 .schedparam=spa, \ 52 .altscope=sco, \ 53 .altstack=sta, \ 54 .guard=gua, \ 55 .altsize=ssi, \ 56 .descr=desc, \ 57 .bottom=NULL, \ 58 .result=res } 59 #define CASE_POS(det,expl,scp,spa,sco,sta,gua,ssi,desc) CASE(det,expl,scp,spa,sco,sta,gua,ssi,desc,0) 60 #define CASE_NEG(det,expl,scp,spa,sco,sta,gua,ssi,desc) CASE(det,expl,scp,spa,sco,sta,gua,ssi,desc,1) 61 #define CASE_UNK(det,expl,scp,spa,sco,sta,gua,ssi,desc) CASE(det,expl,scp,spa,sco,sta,gua,ssi,desc,2) 62 /* 63 * This array gives the different combinations of threads attributes for the testcases. 64 * 65 * Some combinations must be avoided. 66 * -> Do not have a detached thread use an alternative stack; 67 * as we don't know when the thread terminates to free the stack memory 68 * -> ... (to be completed) 69 * 70 */ 71 { 72 /* Unary tests */ 73 /* 0*/ CASE_POS(0, 0, 0, 0, 0, 0, 0, 0, "default") 74 75 /* 1*/ 76 , CASE_POS(1, 0, 0, 0, 0, 0, 0, 0, "detached") 77 /* 2*/ 78 , CASE_POS(0, 1, 0, 0, 0, 0, 0, 0, "Explicit sched") 79 /* 3*/ 80 , CASE_UNK(0, 0, 1, 0, 0, 0, 0, 0, "FIFO Policy") 81 /* 4*/ 82 , CASE_UNK(0, 0, 2, 0, 0, 0, 0, 0, "RR Policy") 83 /* 5*/ 84 , CASE_UNK(0, 0, 0, 1, 0, 0, 0, 0, "Max sched param") 85 /* 6*/ 86 , CASE_UNK(0, 0, 0, -1, 0, 0, 0, 0, "Min sched param") 87 /* 7*/ 88 , CASE_POS(0, 0, 0, 0, 1, 0, 0, 0, "Alternative contension scope") 89 /* 8*/ 90 , CASE_POS(0, 0, 0, 0, 0, 1, 0, 0, "Alternative stack") 91 /* 9*/ 92 , CASE_POS(0, 0, 0, 0, 0, 0, 1, 0, "No guard size") 93 /*10*/ 94 , CASE_UNK(0, 0, 0, 0, 0, 0, 2, 0, "1p guard size") 95 /*11*/ 96 , CASE_POS(0, 0, 0, 0, 0, 0, 0, 1, "Min stack size") 97 98 /* We create several instances of the basic cases for the pthread_self testing */ 99 /* 0*/ , CASE_POS(0, 0, 0, 0, 0, 0, 0, 0, "default") 100 /* 1*/ 101 , CASE_POS(1, 0, 0, 0, 0, 0, 0, 0, "detached") 102 /* 2*/ 103 , CASE_POS(0, 1, 0, 0, 0, 0, 0, 0, "Explicit sched") 104 /* 3*/ 105 , CASE_UNK(0, 0, 1, 0, 0, 0, 0, 0, "FIFO Policy") 106 /* 4*/ 107 , CASE_UNK(0, 0, 2, 0, 0, 0, 0, 0, "RR Policy") 108 /* 5*/ 109 , CASE_UNK(0, 0, 0, 1, 0, 0, 0, 0, "Max sched param") 110 /* 6*/ 111 , CASE_UNK(0, 0, 0, -1, 0, 0, 0, 0, "Min sched param") 112 /* 7*/ 113 , CASE_POS(0, 0, 0, 0, 1, 0, 0, 0, "Alternative contension scope") 114 /* 8*//*,CASE_POS(0, 0, 0, 0, 0, 1, 0, 0, "Alternative stack") */ 115 /* 9*/ , CASE_POS(0, 0, 0, 0, 0, 0, 1, 0, "No guard size") 116 /*10*/ 117 , CASE_UNK(0, 0, 0, 0, 0, 0, 2, 0, "1p guard size") 118 /*11*/ 119 , CASE_POS(0, 0, 0, 0, 0, 0, 0, 1, "Min stack size") 120 /* 0*/ 121 , CASE_POS(0, 0, 0, 0, 0, 0, 0, 0, "default") 122 /* 1*/ 123 , CASE_POS(1, 0, 0, 0, 0, 0, 0, 0, "detached") 124 /* 2*/ 125 , CASE_POS(0, 1, 0, 0, 0, 0, 0, 0, "Explicit sched") 126 /* 3*/ 127 , CASE_UNK(0, 0, 1, 0, 0, 0, 0, 0, "FIFO Policy") 128 /* 4*/ 129 , CASE_UNK(0, 0, 2, 0, 0, 0, 0, 0, "RR Policy") 130 /* 5*/ 131 , CASE_UNK(0, 0, 0, 1, 0, 0, 0, 0, "Max sched param") 132 /* 6*/ 133 , CASE_UNK(0, 0, 0, -1, 0, 0, 0, 0, "Min sched param") 134 /* 7*/ 135 , CASE_POS(0, 0, 0, 0, 1, 0, 0, 0, "Alternative contension scope") 136 /* 8*//*,CASE_POS(0, 0, 0, 0, 0, 1, 0, 0, "Alternative stack") */ 137 /* 9*/ , CASE_POS(0, 0, 0, 0, 0, 0, 1, 0, "No guard size") 138 /*10*/ 139 , CASE_UNK(0, 0, 0, 0, 0, 0, 2, 0, "1p guard size") 140 /*11*/ 141 , CASE_POS(0, 0, 0, 0, 0, 0, 0, 1, "Min stack size") 142 /* 0*/ 143 , CASE_POS(0, 0, 0, 0, 0, 0, 0, 0, "default") 144 /* 1*/ 145 , CASE_POS(1, 0, 0, 0, 0, 0, 0, 0, "detached") 146 /* 2*/ 147 , CASE_POS(0, 1, 0, 0, 0, 0, 0, 0, "Explicit sched") 148 /* 3*/ 149 , CASE_UNK(0, 0, 1, 0, 0, 0, 0, 0, "FIFO Policy") 150 /* 4*/ 151 , CASE_UNK(0, 0, 2, 0, 0, 0, 0, 0, "RR Policy") 152 /* 5*/ 153 , CASE_UNK(0, 0, 0, 1, 0, 0, 0, 0, "Max sched param") 154 /* 6*/ 155 , CASE_UNK(0, 0, 0, -1, 0, 0, 0, 0, "Min sched param") 156 /* 7*/ 157 , CASE_POS(0, 0, 0, 0, 1, 0, 0, 0, "Alternative contension scope") 158 /* 8*//*,CASE_POS(0, 0, 0, 0, 0, 1, 0, 0, "Alternative stack") */ 159 /* 9*/ , CASE_POS(0, 0, 0, 0, 0, 0, 1, 0, "No guard size") 160 /*10*/ 161 , CASE_UNK(0, 0, 0, 0, 0, 0, 2, 0, "1p guard size") 162 /*11*/ 163 , CASE_POS(0, 0, 0, 0, 0, 0, 0, 1, "Min stack size") 164 165 /* Stack play */ 166 , CASE_POS(0, 0, 0, 0, 0, 0, 1, 1, "Min stack size, no guard") 167 , CASE_UNK(0, 0, 0, 0, 0, 0, 2, 1, "Min stack size, 1p guard") 168 /*,CASE_POS(1, 0, 0, 0, 0, 1, 0, 0, "Detached, Alternative stack") */ 169 , CASE_POS(1, 0, 0, 0, 0, 0, 1, 1, 170 "Detached, Min stack size, no guard") 171 , CASE_UNK(1, 0, 0, 0, 0, 0, 2, 1, 172 "Detached, Min stack size, 1p guard") 173 174 /* Scheduling play -- all results are unknown since it might depend on the user priviledges */ 175 , CASE_UNK(0, 1, 1, 1, 0, 0, 0, 0, "Explicit FIFO max param") 176 , CASE_UNK(0, 1, 2, 1, 0, 0, 0, 0, "Explicit RR max param") 177 , CASE_UNK(0, 1, 1, -1, 0, 0, 0, 0, "Explicit FIFO min param") 178 , CASE_UNK(0, 1, 2, -1, 0, 0, 0, 0, "Explicit RR min param") 179 , CASE_UNK(0, 1, 1, 1, 1, 0, 0, 0, 180 "Explicit FIFO max param, alt scope") 181 , CASE_UNK(0, 1, 2, 1, 1, 0, 0, 0, 182 "Explicit RR max param, alt scope") 183 , CASE_UNK(0, 1, 1, -1, 1, 0, 0, 0, 184 "Explicit FIFO min param, alt scope") 185 , CASE_UNK(0, 1, 2, -1, 1, 0, 0, 0, 186 "Explicit RR min param, alt scope") 187 , CASE_UNK(1, 1, 1, 1, 0, 0, 0, 0, 188 "Detached, explicit FIFO max param") 189 , CASE_UNK(1, 1, 2, 1, 0, 0, 0, 0, 190 "Detached, explicit RR max param") 191 , CASE_UNK(1, 1, 1, -1, 0, 0, 0, 0, 192 "Detached, explicit FIFO min param") 193 , CASE_UNK(1, 1, 2, -1, 0, 0, 0, 0, 194 "Detached, explicit RR min param") 195 , CASE_UNK(1, 1, 1, 1, 1, 0, 0, 0, 196 "Detached, explicit FIFO max param, alt scope") 197 , CASE_UNK(1, 1, 2, 1, 1, 0, 0, 0, 198 "Detached, explicit RR max param, alt scope") 199 , CASE_UNK(1, 1, 1, -1, 1, 0, 0, 0, 200 "Detached, explicit FIFO min param, alt scope") 201 , CASE_UNK(1, 1, 2, -1, 1, 0, 0, 0, 202 "Detached, explicit RR min param, alt scope") 203 }; 204 205 #define NSCENAR (sizeof(scenarii) / sizeof(scenarii[0])) 206 207 /* This function will initialize every pthread_attr_t object in the scenarii array */ 208 void scenar_init() 209 { 210 int ret = 0; 211 int i; 212 int old; 213 long pagesize, minstacksize; 214 long tsa, tss, tps; 215 216 pagesize = sysconf(_SC_PAGESIZE); 217 minstacksize = sysconf(_SC_THREAD_STACK_MIN); 218 tsa = sysconf(_SC_THREAD_ATTR_STACKADDR); 219 tss = sysconf(_SC_THREAD_ATTR_STACKSIZE); 220 tps = sysconf(_SC_THREAD_PRIORITY_SCHEDULING); 221 222 #if VERBOSE > 0 223 output("System abilities:\n"); 224 output(" TSA: %li\n", tsa); 225 output(" TSS: %li\n", tss); 226 output(" TPS: %li\n", tps); 227 output(" pagesize: %li\n", pagesize); 228 output(" min stack size: %li\n", minstacksize); 229 #endif 230 231 if (minstacksize % pagesize) { 232 UNTESTED 233 ("The min stack size is not a multiple of the page size"); 234 } 235 236 for (i = 0; i < NSCENAR; i++) { 237 #if VERBOSE > 2 238 output("Initializing attribute for scenario %i: %s\n", i, 239 scenarii[i].descr); 240 #endif 241 242 ret = pthread_attr_init(&scenarii[i].ta); 243 if (ret != 0) { 244 UNRESOLVED(ret, 245 "Failed to initialize a thread attribute object"); 246 } 247 248 /* Set the attributes according to the scenario */ 249 if (scenarii[i].detached == 1) { 250 ret = 251 pthread_attr_setdetachstate(&scenarii[i].ta, 252 PTHREAD_CREATE_DETACHED); 253 if (ret != 0) { 254 UNRESOLVED(ret, "Unable to set detachstate"); 255 } 256 } else { 257 ret = 258 pthread_attr_getdetachstate(&scenarii[i].ta, &old); 259 if (ret != 0) { 260 UNRESOLVED(ret, 261 "Unable to get detachstate from initialized attribute"); 262 } 263 if (old != PTHREAD_CREATE_JOINABLE) { 264 FAILED 265 ("The default attribute is not PTHREAD_CREATE_JOINABLE"); 266 } 267 } 268 #if VERBOSE > 4 269 output("Detach state was set sucessfully\n"); 270 #endif 271 272 /* Sched related attributes */ 273 if (tps > 0) { /* This routine is dependent on the Thread Execution Scheduling option */ 274 if (scenarii[i].explicitsched == 1) 275 ret = 276 pthread_attr_setinheritsched(&scenarii[i]. 277 ta, 278 PTHREAD_EXPLICIT_SCHED); 279 else 280 ret = 281 pthread_attr_setinheritsched(&scenarii[i]. 282 ta, 283 PTHREAD_INHERIT_SCHED); 284 if (ret != 0) { 285 UNRESOLVED(ret, 286 "Unable to set inheritsched attribute"); 287 } 288 #if VERBOSE > 4 289 output("inheritsched state was set sucessfully\n"); 290 #endif 291 } 292 #if VERBOSE > 4 293 else 294 output 295 ("TPS unsupported => inheritsched parameter untouched\n"); 296 #endif 297 298 if (tps > 0) { /* This routine is dependent on the Thread Execution Scheduling option */ 299 if (scenarii[i].schedpolicy == 1) { 300 ret = 301 pthread_attr_setschedpolicy(&scenarii[i].ta, 302 SCHED_FIFO); 303 } 304 if (scenarii[i].schedpolicy == 2) { 305 ret = 306 pthread_attr_setschedpolicy(&scenarii[i].ta, 307 SCHED_RR); 308 } 309 if (ret != 0) { 310 UNRESOLVED(ret, 311 "Unable to set the sched policy"); 312 } 313 #if VERBOSE > 4 314 if (scenarii[i].schedpolicy) 315 output("Sched policy was set sucessfully\n"); 316 else 317 output("Sched policy untouched\n"); 318 #endif 319 } 320 #if VERBOSE > 4 321 else 322 output 323 ("TPS unsupported => sched policy parameter untouched\n"); 324 #endif 325 326 if (scenarii[i].schedparam != 0) { 327 struct sched_param sp; 328 329 ret = 330 pthread_attr_getschedpolicy(&scenarii[i].ta, &old); 331 if (ret != 0) { 332 UNRESOLVED(ret, 333 "Unable to get sched policy from attribute"); 334 } 335 336 if (scenarii[i].schedparam == 1) 337 sp.sched_priority = sched_get_priority_max(old); 338 if (scenarii[i].schedparam == -1) 339 sp.sched_priority = sched_get_priority_min(old); 340 341 ret = pthread_attr_setschedparam(&scenarii[i].ta, &sp); 342 if (ret != 0) { 343 UNRESOLVED(ret, 344 "Failed to set the sched param"); 345 } 346 #if VERBOSE > 4 347 output("Sched param was set sucessfully to %i\n", 348 sp.sched_priority); 349 } else { 350 output("Sched param untouched\n"); 351 #endif 352 } 353 354 if (tps > 0) { /* This routine is dependent on the Thread Execution Scheduling option */ 355 ret = pthread_attr_getscope(&scenarii[i].ta, &old); 356 if (ret != 0) { 357 UNRESOLVED(ret, 358 "Failed to get contension scope from thread attribute"); 359 } 360 361 if (scenarii[i].altscope != 0) { 362 if (old == PTHREAD_SCOPE_PROCESS) 363 old = PTHREAD_SCOPE_SYSTEM; 364 else 365 old = PTHREAD_SCOPE_PROCESS; 366 367 ret = 368 pthread_attr_setscope(&scenarii[i].ta, old); 369 //if (ret != 0) { UNRESOLVED(ret, "Failed to set contension scope"); } 370 #if VERBOSE > 0 371 if (ret != 0) { 372 output 373 ("WARNING: The TPS option is claimed to be supported but setscope fails\n"); 374 } 375 #endif 376 377 #if VERBOSE > 4 378 output("Contension scope set to %s\n", 379 old == 380 PTHREAD_SCOPE_PROCESS ? 381 "PTHREAD_SCOPE_PROCESS" : 382 "PTHREAD_SCOPE_SYSTEM"); 383 } else { 384 output("Contension scope untouched (%s)\n", 385 old == 386 PTHREAD_SCOPE_PROCESS ? 387 "PTHREAD_SCOPE_PROCESS" : 388 "PTHREAD_SCOPE_SYSTEM"); 389 #endif 390 } 391 } 392 #if VERBOSE > 4 393 else 394 output 395 ("TPS unsupported => sched contension scope parameter untouched\n"); 396 #endif 397 398 /* Stack related attributes */ 399 if ((tss > 0) && (tsa > 0)) { /* This routine is dependent on the Thread Stack Address Attribute 400 and Thread Stack Size Attribute options */ 401 if (scenarii[i].altstack != 0) { 402 /* This is slightly more complicated. We need to alloc a new stack 403 and free it upon test termination */ 404 /* We will alloc with a simulated guardsize of 1 pagesize */ 405 scenarii[i].bottom = malloc(minstacksize + pagesize); 406 if (scenarii[i].bottom == NULL) { 407 UNRESOLVED(errno, 408 "Unable to alloc enough memory for alternative stack"); 409 } 410 411 ret = 412 pthread_attr_setstack(&scenarii[i].ta, 413 scenarii[i].bottom, 414 minstacksize); 415 if (ret != 0) { 416 UNRESOLVED(ret, 417 "Failed to specify alternate stack"); 418 } 419 #if VERBOSE > 1 420 output 421 ("Alternate stack created successfully. Bottom=%p, Size=%i\n", 422 scenarii[i].bottom, minstacksize); 423 #endif 424 } 425 } 426 #if VERBOSE > 4 427 else 428 output 429 ("TSA or TSS unsupported => No alternative stack\n"); 430 #endif 431 432 #ifndef WITHOUT_XOPEN 433 if (scenarii[i].guard != 0) { 434 if (scenarii[i].guard == 1) 435 ret = 436 pthread_attr_setguardsize(&scenarii[i].ta, 437 0); 438 if (scenarii[i].guard == 2) 439 ret = 440 pthread_attr_setguardsize(&scenarii[i].ta, 441 pagesize); 442 if (ret != 0) { 443 UNRESOLVED(ret, 444 "Unable to set guard area size in thread stack"); 445 } 446 #if VERBOSE > 4 447 output("Guard size set to %i\n", 448 (scenarii[i].guard == 1) ? 1 : pagesize); 449 #endif 450 } 451 #endif 452 453 if (tss > 0) { /* This routine is dependent on the Thread Stack Size Attribute option */ 454 if (scenarii[i].altsize != 0) { 455 ret = 456 pthread_attr_setstacksize(&scenarii[i].ta, 457 minstacksize); 458 if (ret != 0) { 459 UNRESOLVED(ret, 460 "Unable to change stack size"); 461 } 462 #if VERBOSE > 4 463 output 464 ("Stack size set to %i (this is the min)\n", 465 minstacksize); 466 #endif 467 } 468 } 469 #if VERBOSE > 4 470 else 471 output("TSS unsupported => stack size unchanged\n"); 472 #endif 473 474 ret = sem_init(&scenarii[i].sem, 0, 0); 475 if (ret == -1) { 476 UNRESOLVED(errno, "Unable to init a semaphore"); 477 } 478 479 } 480 #if VERBOSE > 0 481 output("All %i thread attribute objects were initialized\n\n", NSCENAR); 482 #endif 483 } 484 485 /* This function will free all resources consumed in the scenar_init() routine */ 486 void scenar_fini(void) 487 { 488 int ret = 0, i; 489 490 for (i = 0; i < NSCENAR; i++) { 491 if (scenarii[i].bottom != NULL) 492 free(scenarii[i].bottom); 493 494 ret = sem_destroy(&scenarii[i].sem); 495 if (ret == -1) { 496 UNRESOLVED(errno, "Unable to destroy a semaphore"); 497 } 498 499 ret = pthread_attr_destroy(&scenarii[i].ta); 500 if (ret != 0) { 501 UNRESOLVED(ret, 502 "Failed to destroy a thread attribute object"); 503 } 504 } 505 } 506 507 int sc = 0; /* This might be very dirty... but is much simpler */ 508 509 #ifdef STD_MAIN /* We want main to be defined here */ 510 511 extern void *threaded(void *arg); /* This is the test function */ 512 513 int main(int argc, char *argv[]) 514 { 515 int ret = 0; 516 pthread_t child; 517 518 /* Initialize output routine */ 519 output_init(); 520 521 /* Initialize thread attribute objects */ 522 scenar_init(); 523 524 for (sc = 0; sc < NSCENAR; sc++) { 525 #if VERBOSE > 0 526 output("-----\n"); 527 output("Starting test with scenario (%i): %s\n", sc, 528 scenarii[sc].descr); 529 #endif 530 531 ret = pthread_create(&child, &scenarii[sc].ta, threaded, NULL); 532 switch (scenarii[sc].result) { 533 case 0: /* Operation was expected to succeed */ 534 if (ret != 0) { 535 UNRESOLVED(ret, "Failed to create this thread"); 536 } 537 break; 538 539 case 1: /* Operation was expected to fail */ 540 if (ret == 0) { 541 UNRESOLVED(-1, 542 "An error was expected but the thread creation succeeded"); 543 } 544 break; 545 546 case 2: /* We did not know the expected result */ 547 default: 548 #if VERBOSE > 0 549 if (ret == 0) { 550 output 551 ("Thread has been created successfully for this scenario\n"); 552 } else { 553 output 554 ("Thread creation failed with the error: %s\n", 555 strerror(ret)); 556 } 557 #endif 558 } 559 if (ret == 0) { /* The new thread is running */ 560 if (scenarii[sc].detached == 0) { 561 ret = pthread_join(child, NULL); 562 if (ret != 0) { 563 UNRESOLVED(ret, 564 "Unable to join a thread"); 565 } 566 } else { 567 /* Just wait for the thread to terminate */ 568 do { 569 ret = sem_wait(&scenarii[sc].sem); 570 } 571 while ((ret == -1) && (errno == EINTR)); 572 if (ret == -1) { 573 UNRESOLVED(errno, 574 "Failed to wait for the semaphore"); 575 } 576 } 577 } 578 } 579 580 scenar_fini(); 581 #if VERBOSE > 0 582 output("-----\n"); 583 output("All test data destroyed\n"); 584 output("Test PASSED\n"); 585 #endif 586 587 PASSED; 588 } 589 #endif 590