1#! /bin/sh 2# $Id: shunit2 322 2011-04-24 00:09:45Z kate.ward@forestent.com $ 3# vim:et:ft=sh:sts=2:sw=2 4# 5# Copyright 2008 Kate Ward. All Rights Reserved. 6# Released under the LGPL (GNU Lesser General Public License) 7# 8# shUnit2 -- Unit testing framework for Unix shell scripts. 9# http://code.google.com/p/shunit2/ 10# 11# Author: kate.ward@forestent.com (Kate Ward) 12# 13# shUnit2 is a xUnit based unit test framework for Bourne shell scripts. It is 14# based on the popular JUnit unit testing framework for Java. 15 16# return if shunit already loaded 17[ -n "${SHUNIT_VERSION:-}" ] && exit 0 18 19SHUNIT_VERSION='2.1.6' 20 21SHUNIT_TRUE=0 22SHUNIT_FALSE=1 23SHUNIT_ERROR=2 24 25# enable strict mode by default 26SHUNIT_STRICT=${SHUNIT_STRICT:-${SHUNIT_TRUE}} 27 28_shunit_warn() { echo "shunit2:WARN $@" >&2; } 29_shunit_error() { echo "shunit2:ERROR $@" >&2; } 30_shunit_fatal() { echo "shunit2:FATAL $@" >&2; exit ${SHUNIT_ERROR}; } 31 32# specific shell checks 33if [ -n "${ZSH_VERSION:-}" ]; then 34 setopt |grep "^shwordsplit$" >/dev/null 35 if [ $? -ne ${SHUNIT_TRUE} ]; then 36 _shunit_fatal 'zsh shwordsplit option is required for proper operation' 37 fi 38 if [ -z "${SHUNIT_PARENT:-}" ]; then 39 _shunit_fatal "zsh does not pass \$0 through properly. please declare \ 40\"SHUNIT_PARENT=\$0\" before calling shUnit2" 41 fi 42fi 43 44# 45# constants 46# 47 48__SHUNIT_ASSERT_MSG_PREFIX='ASSERT:' 49__SHUNIT_MODE_SOURCED='sourced' 50__SHUNIT_MODE_STANDALONE='standalone' 51__SHUNIT_PARENT=${SHUNIT_PARENT:-$0} 52 53# set the constants readonly 54shunit_constants_=`set |grep '^__SHUNIT_' |cut -d= -f1` 55echo "${shunit_constants_}" |grep '^Binary file' >/dev/null && \ 56 shunit_constants_=`set |grep -a '^__SHUNIT_' |cut -d= -f1` 57for shunit_constant_ in ${shunit_constants_}; do 58 shunit_ro_opts_='' 59 case ${ZSH_VERSION:-} in 60 '') ;; # this isn't zsh 61 [123].*) ;; # early versions (1.x, 2.x, 3.x) 62 *) shunit_ro_opts_='-g' ;; # all later versions. declare readonly globally 63 esac 64 readonly ${shunit_ro_opts_} ${shunit_constant_} 65done 66unset shunit_constant_ shunit_constants_ shunit_ro_opts_ 67 68# variables 69__shunit_lineno='' # line number of executed test 70__shunit_mode=${__SHUNIT_MODE_SOURCED} # operating mode 71__shunit_reportGenerated=${SHUNIT_FALSE} # is report generated 72__shunit_script='' # filename of unittest script (standalone mode) 73__shunit_skip=${SHUNIT_FALSE} # is skipping enabled 74__shunit_suite='' # suite of tests to execute 75 76# counts of tests 77__shunit_testSuccess=${SHUNIT_TRUE} 78__shunit_testsTotal=0 79__shunit_testsPassed=0 80__shunit_testsFailed=0 81 82# counts of asserts 83__shunit_assertsTotal=0 84__shunit_assertsPassed=0 85__shunit_assertsFailed=0 86__shunit_assertsSkipped=0 87 88# macros 89_SHUNIT_LINENO_='eval __shunit_lineno=""; if [ "${1:-}" = "--lineno" ]; then [ -n "$2" ] && __shunit_lineno="[$2] "; shift 2; fi' 90 91#----------------------------------------------------------------------------- 92# assert functions 93# 94 95# Assert that two values are equal to one another. 96# 97# Args: 98# message: string: failure message [optional] 99# expected: string: expected value 100# actual: string: actual value 101# Returns: 102# integer: success (TRUE/FALSE/ERROR constant) 103assertEquals() 104{ 105 ${_SHUNIT_LINENO_} 106 if [ $# -lt 2 -o $# -gt 3 ]; then 107 _shunit_error "assertEquals() requires two or three arguments; $# given" 108 _shunit_error "1: ${1:+$1} 2: ${2:+$2} 3: ${3:+$3}" 109 return ${SHUNIT_ERROR} 110 fi 111 _shunit_shouldSkip && return ${SHUNIT_TRUE} 112 113 shunit_message_=${__shunit_lineno} 114 if [ $# -eq 3 ]; then 115 shunit_message_="${shunit_message_}$1" 116 shift 117 fi 118 shunit_expected_=$1 119 shunit_actual_=$2 120 121 shunit_return=${SHUNIT_TRUE} 122 if [ "${shunit_expected_}" = "${shunit_actual_}" ]; then 123 _shunit_assertPass 124 else 125 failNotEquals "${shunit_message_}" "${shunit_expected_}" "${shunit_actual_}" 126 shunit_return=${SHUNIT_FALSE} 127 fi 128 129 unset shunit_message_ shunit_expected_ shunit_actual_ 130 return ${shunit_return} 131} 132_ASSERT_EQUALS_='eval assertEquals --lineno "${LINENO:-}"' 133 134# Assert that two values are not equal to one another. 135# 136# Args: 137# message: string: failure message [optional] 138# expected: string: expected value 139# actual: string: actual value 140# Returns: 141# integer: success (TRUE/FALSE/ERROR constant) 142assertNotEquals() 143{ 144 ${_SHUNIT_LINENO_} 145 if [ $# -lt 2 -o $# -gt 3 ]; then 146 _shunit_error "assertNotEquals() requires two or three arguments; $# given" 147 return ${SHUNIT_ERROR} 148 fi 149 _shunit_shouldSkip && return ${SHUNIT_TRUE} 150 151 shunit_message_=${__shunit_lineno} 152 if [ $# -eq 3 ]; then 153 shunit_message_="${shunit_message_}$1" 154 shift 155 fi 156 shunit_expected_=$1 157 shunit_actual_=$2 158 159 shunit_return=${SHUNIT_TRUE} 160 if [ "${shunit_expected_}" != "${shunit_actual_}" ]; then 161 _shunit_assertPass 162 else 163 failSame "${shunit_message_}" "$@" 164 shunit_return=${SHUNIT_FALSE} 165 fi 166 167 unset shunit_message_ shunit_expected_ shunit_actual_ 168 return ${shunit_return} 169} 170_ASSERT_NOT_EQUALS_='eval assertNotEquals --lineno "${LINENO:-}"' 171 172# Assert that a value is null (i.e. an empty string) 173# 174# Args: 175# message: string: failure message [optional] 176# actual: string: actual value 177# Returns: 178# integer: success (TRUE/FALSE/ERROR constant) 179assertNull() 180{ 181 ${_SHUNIT_LINENO_} 182 if [ $# -lt 1 -o $# -gt 2 ]; then 183 _shunit_error "assertNull() requires one or two arguments; $# given" 184 return ${SHUNIT_ERROR} 185 fi 186 _shunit_shouldSkip && return ${SHUNIT_TRUE} 187 188 shunit_message_=${__shunit_lineno} 189 if [ $# -eq 2 ]; then 190 shunit_message_="${shunit_message_}$1" 191 shift 192 fi 193 assertTrue "${shunit_message_}" "[ -z '$1' ]" 194 shunit_return=$? 195 196 unset shunit_message_ 197 return ${shunit_return} 198} 199_ASSERT_NULL_='eval assertNull --lineno "${LINENO:-}"' 200 201# Assert that a value is not null (i.e. a non-empty string) 202# 203# Args: 204# message: string: failure message [optional] 205# actual: string: actual value 206# Returns: 207# integer: success (TRUE/FALSE/ERROR constant) 208assertNotNull() 209{ 210 ${_SHUNIT_LINENO_} 211 if [ $# -gt 2 ]; then # allowing 0 arguments as $1 might actually be null 212 _shunit_error "assertNotNull() requires one or two arguments; $# given" 213 return ${SHUNIT_ERROR} 214 fi 215 _shunit_shouldSkip && return ${SHUNIT_TRUE} 216 217 shunit_message_=${__shunit_lineno} 218 if [ $# -eq 2 ]; then 219 shunit_message_="${shunit_message_}$1" 220 shift 221 fi 222 shunit_actual_=`_shunit_escapeCharactersInString "${1:-}"` 223 test -n "${shunit_actual_}" 224 assertTrue "${shunit_message_}" $? 225 shunit_return=$? 226 227 unset shunit_actual_ shunit_message_ 228 return ${shunit_return} 229} 230_ASSERT_NOT_NULL_='eval assertNotNull --lineno "${LINENO:-}"' 231 232# Assert that two values are the same (i.e. equal to one another). 233# 234# Args: 235# message: string: failure message [optional] 236# expected: string: expected value 237# actual: string: actual value 238# Returns: 239# integer: success (TRUE/FALSE/ERROR constant) 240assertSame() 241{ 242 ${_SHUNIT_LINENO_} 243 if [ $# -lt 2 -o $# -gt 3 ]; then 244 _shunit_error "assertSame() requires two or three arguments; $# given" 245 return ${SHUNIT_ERROR} 246 fi 247 _shunit_shouldSkip && return ${SHUNIT_TRUE} 248 249 shunit_message_=${__shunit_lineno} 250 if [ $# -eq 3 ]; then 251 shunit_message_="${shunit_message_}$1" 252 shift 253 fi 254 assertEquals "${shunit_message_}" "$1" "$2" 255 shunit_return=$? 256 257 unset shunit_message_ 258 return ${shunit_return} 259} 260_ASSERT_SAME_='eval assertSame --lineno "${LINENO:-}"' 261 262# Assert that two values are not the same (i.e. not equal to one another). 263# 264# Args: 265# message: string: failure message [optional] 266# expected: string: expected value 267# actual: string: actual value 268# Returns: 269# integer: success (TRUE/FALSE/ERROR constant) 270assertNotSame() 271{ 272 ${_SHUNIT_LINENO_} 273 if [ $# -lt 2 -o $# -gt 3 ]; then 274 _shunit_error "assertNotSame() requires two or three arguments; $# given" 275 return ${SHUNIT_ERROR} 276 fi 277 _shunit_shouldSkip && return ${SHUNIT_TRUE} 278 279 shunit_message_=${__shunit_lineno} 280 if [ $# -eq 3 ]; then 281 shunit_message_="${shunit_message_:-}$1" 282 shift 283 fi 284 assertNotEquals "${shunit_message_}" "$1" "$2" 285 shunit_return=$? 286 287 unset shunit_message_ 288 return ${shunit_return} 289} 290_ASSERT_NOT_SAME_='eval assertNotSame --lineno "${LINENO:-}"' 291 292# Assert that a value or shell test condition is true. 293# 294# In shell, a value of 0 is true and a non-zero value is false. Any integer 295# value passed can thereby be tested. 296# 297# Shell supports much more complicated tests though, and a means to support 298# them was needed. As such, this function tests that conditions are true or 299# false through evaluation rather than just looking for a true or false. 300# 301# The following test will succeed: 302# assertTrue 0 303# assertTrue "[ 34 -gt 23 ]" 304# The folloing test will fail with a message: 305# assertTrue 123 306# assertTrue "test failed" "[ -r '/non/existant/file' ]" 307# 308# Args: 309# message: string: failure message [optional] 310# condition: string: integer value or shell conditional statement 311# Returns: 312# integer: success (TRUE/FALSE/ERROR constant) 313assertTrue() 314{ 315 ${_SHUNIT_LINENO_} 316 if [ $# -gt 2 ]; then 317 _shunit_error "assertTrue() takes one two arguments; $# given" 318 return ${SHUNIT_ERROR} 319 fi 320 _shunit_shouldSkip && return ${SHUNIT_TRUE} 321 322 shunit_message_=${__shunit_lineno} 323 if [ $# -eq 2 ]; then 324 shunit_message_="${shunit_message_}$1" 325 shift 326 fi 327 shunit_condition_=$1 328 329 # see if condition is an integer, i.e. a return value 330 shunit_match_=`expr "${shunit_condition_}" : '\([0-9]*\)'` 331 shunit_return=${SHUNIT_TRUE} 332 if [ -z "${shunit_condition_}" ]; then 333 # null condition 334 shunit_return=${SHUNIT_FALSE} 335 elif [ -n "${shunit_match_}" -a "${shunit_condition_}" = "${shunit_match_}" ] 336 then 337 # possible return value. treating 0 as true, and non-zero as false. 338 [ ${shunit_condition_} -ne 0 ] && shunit_return=${SHUNIT_FALSE} 339 else 340 # (hopefully) a condition 341 ( eval ${shunit_condition_} ) >/dev/null 2>&1 342 [ $? -ne 0 ] && shunit_return=${SHUNIT_FALSE} 343 fi 344 345 # record the test 346 if [ ${shunit_return} -eq ${SHUNIT_TRUE} ]; then 347 _shunit_assertPass 348 else 349 _shunit_assertFail "${shunit_message_}" 350 fi 351 352 unset shunit_message_ shunit_condition_ shunit_match_ 353 return ${shunit_return} 354} 355_ASSERT_TRUE_='eval assertTrue --lineno "${LINENO:-}"' 356 357# Assert that a value or shell test condition is false. 358# 359# In shell, a value of 0 is true and a non-zero value is false. Any integer 360# value passed can thereby be tested. 361# 362# Shell supports much more complicated tests though, and a means to support 363# them was needed. As such, this function tests that conditions are true or 364# false through evaluation rather than just looking for a true or false. 365# 366# The following test will succeed: 367# assertFalse 1 368# assertFalse "[ 'apples' = 'oranges' ]" 369# The folloing test will fail with a message: 370# assertFalse 0 371# assertFalse "test failed" "[ 1 -eq 1 -a 2 -eq 2 ]" 372# 373# Args: 374# message: string: failure message [optional] 375# condition: string: integer value or shell conditional statement 376# Returns: 377# integer: success (TRUE/FALSE/ERROR constant) 378assertFalse() 379{ 380 ${_SHUNIT_LINENO_} 381 if [ $# -lt 1 -o $# -gt 2 ]; then 382 _shunit_error "assertFalse() quires one or two arguments; $# given" 383 return ${SHUNIT_ERROR} 384 fi 385 _shunit_shouldSkip && return ${SHUNIT_TRUE} 386 387 shunit_message_=${__shunit_lineno} 388 if [ $# -eq 2 ]; then 389 shunit_message_="${shunit_message_}$1" 390 shift 391 fi 392 shunit_condition_=$1 393 394 # see if condition is an integer, i.e. a return value 395 shunit_match_=`expr "${shunit_condition_}" : '\([0-9]*\)'` 396 shunit_return=${SHUNIT_TRUE} 397 if [ -z "${shunit_condition_}" ]; then 398 # null condition 399 shunit_return=${SHUNIT_FALSE} 400 elif [ -n "${shunit_match_}" -a "${shunit_condition_}" = "${shunit_match_}" ] 401 then 402 # possible return value. treating 0 as true, and non-zero as false. 403 [ ${shunit_condition_} -eq 0 ] && shunit_return=${SHUNIT_FALSE} 404 else 405 # (hopefully) a condition 406 ( eval ${shunit_condition_} ) >/dev/null 2>&1 407 [ $? -eq 0 ] && shunit_return=${SHUNIT_FALSE} 408 fi 409 410 # record the test 411 if [ ${shunit_return} -eq ${SHUNIT_TRUE} ]; then 412 _shunit_assertPass 413 else 414 _shunit_assertFail "${shunit_message_}" 415 fi 416 417 unset shunit_message_ shunit_condition_ shunit_match_ 418 return ${shunit_return} 419} 420_ASSERT_FALSE_='eval assertFalse --lineno "${LINENO:-}"' 421 422#----------------------------------------------------------------------------- 423# failure functions 424# 425 426# Records a test failure. 427# 428# Args: 429# message: string: failure message [optional] 430# Returns: 431# integer: success (TRUE/FALSE/ERROR constant) 432fail() 433{ 434 ${_SHUNIT_LINENO_} 435 if [ $# -gt 1 ]; then 436 _shunit_error "fail() requires zero or one arguments; $# given" 437 return ${SHUNIT_ERROR} 438 fi 439 _shunit_shouldSkip && return ${SHUNIT_TRUE} 440 441 shunit_message_=${__shunit_lineno} 442 if [ $# -eq 1 ]; then 443 shunit_message_="${shunit_message_}$1" 444 shift 445 fi 446 447 _shunit_assertFail "${shunit_message_}" 448 449 unset shunit_message_ 450 return ${SHUNIT_FALSE} 451} 452_FAIL_='eval fail --lineno "${LINENO:-}"' 453 454# Records a test failure, stating two values were not equal. 455# 456# Args: 457# message: string: failure message [optional] 458# expected: string: expected value 459# actual: string: actual value 460# Returns: 461# integer: success (TRUE/FALSE/ERROR constant) 462failNotEquals() 463{ 464 ${_SHUNIT_LINENO_} 465 if [ $# -lt 2 -o $# -gt 3 ]; then 466 _shunit_error "failNotEquals() requires one or two arguments; $# given" 467 return ${SHUNIT_ERROR} 468 fi 469 _shunit_shouldSkip && return ${SHUNIT_TRUE} 470 471 shunit_message_=${__shunit_lineno} 472 if [ $# -eq 3 ]; then 473 shunit_message_="${shunit_message_}$1" 474 shift 475 fi 476 shunit_expected_=$1 477 shunit_actual_=$2 478 479 _shunit_assertFail "${shunit_message_:+${shunit_message_} }expected:<${shunit_expected_}> but was:<${shunit_actual_}>" 480 481 unset shunit_message_ shunit_expected_ shunit_actual_ 482 return ${SHUNIT_FALSE} 483} 484_FAIL_NOT_EQUALS_='eval failNotEquals --lineno "${LINENO:-}"' 485 486# Records a test failure, stating two values should have been the same. 487# 488# Args: 489# message: string: failure message [optional] 490# expected: string: expected value 491# actual: string: actual value 492# Returns: 493# integer: success (TRUE/FALSE/ERROR constant) 494failSame() 495{ 496 ${_SHUNIT_LINENO_} 497 if [ $# -lt 2 -o $# -gt 3 ]; then 498 _shunit_error "failSame() requires two or three arguments; $# given" 499 return ${SHUNIT_ERROR} 500 fi 501 _shunit_shouldSkip && return ${SHUNIT_TRUE} 502 503 shunit_message_=${__shunit_lineno} 504 if [ $# -eq 3 ]; then 505 shunit_message_="${shunit_message_}$1" 506 shift 507 fi 508 509 _shunit_assertFail "${shunit_message_:+${shunit_message_} }expected not same" 510 511 unset shunit_message_ 512 return ${SHUNIT_FALSE} 513} 514_FAIL_SAME_='eval failSame --lineno "${LINENO:-}"' 515 516# Records a test failure, stating two values were not equal. 517# 518# This is functionally equivalent to calling failNotEquals(). 519# 520# Args: 521# message: string: failure message [optional] 522# expected: string: expected value 523# actual: string: actual value 524# Returns: 525# integer: success (TRUE/FALSE/ERROR constant) 526failNotSame() 527{ 528 ${_SHUNIT_LINENO_} 529 if [ $# -lt 2 -o $# -gt 3 ]; then 530 _shunit_error "failNotEquals() requires one or two arguments; $# given" 531 return ${SHUNIT_ERROR} 532 fi 533 _shunit_shouldSkip && return ${SHUNIT_TRUE} 534 535 shunit_message_=${__shunit_lineno} 536 if [ $# -eq 3 ]; then 537 shunit_message_="${shunit_message_}$1" 538 shift 539 fi 540 failNotEquals "${shunit_message_}" "$1" "$2" 541 shunit_return=$? 542 543 unset shunit_message_ 544 return ${shunit_return} 545} 546_FAIL_NOT_SAME_='eval failNotSame --lineno "${LINENO:-}"' 547 548#----------------------------------------------------------------------------- 549# skipping functions 550# 551 552# Force remaining assert and fail functions to be "skipped". 553# 554# This function forces the remaining assert and fail functions to be "skipped", 555# i.e. they will have no effect. Each function skipped will be recorded so that 556# the total of asserts and fails will not be altered. 557# 558# Args: 559# None 560startSkipping() 561{ 562 __shunit_skip=${SHUNIT_TRUE} 563} 564 565# Resume the normal recording behavior of assert and fail calls. 566# 567# Args: 568# None 569endSkipping() 570{ 571 __shunit_skip=${SHUNIT_FALSE} 572} 573 574# Returns the state of assert and fail call skipping. 575# 576# Args: 577# None 578# Returns: 579# boolean: (TRUE/FALSE constant) 580isSkipping() 581{ 582 return ${__shunit_skip} 583} 584 585#----------------------------------------------------------------------------- 586# suite functions 587# 588 589# Stub. This function should contains all unit test calls to be made. 590# 591# DEPRECATED (as of 2.1.0) 592# 593# This function can be optionally overridden by the user in their test suite. 594# 595# If this function exists, it will be called when shunit2 is sourced. If it 596# does not exist, shunit2 will search the parent script for all functions 597# beginning with the word 'test', and they will be added dynamically to the 598# test suite. 599# 600# This function should be overridden by the user in their unit test suite. 601# Note: see _shunit_mktempFunc() for actual implementation 602# 603# Args: 604# None 605#suite() { :; } # DO NOT UNCOMMENT THIS FUNCTION 606 607# Adds a function name to the list of tests schedule for execution. 608# 609# This function should only be called from within the suite() function. 610# 611# Args: 612# function: string: name of a function to add to current unit test suite 613suite_addTest() 614{ 615 shunit_func_=${1:-} 616 617 __shunit_suite="${__shunit_suite:+${__shunit_suite} }${shunit_func_}" 618 __shunit_testsTotal=`expr ${__shunit_testsTotal} + 1` 619 620 unset shunit_func_ 621} 622 623# Stub. This function will be called once before any tests are run. 624# 625# Common one-time environment preparation tasks shared by all tests can be 626# defined here. 627# 628# This function should be overridden by the user in their unit test suite. 629# Note: see _shunit_mktempFunc() for actual implementation 630# 631# Args: 632# None 633#oneTimeSetUp() { :; } # DO NOT UNCOMMENT THIS FUNCTION 634 635# Stub. This function will be called once after all tests are finished. 636# 637# Common one-time environment cleanup tasks shared by all tests can be defined 638# here. 639# 640# This function should be overridden by the user in their unit test suite. 641# Note: see _shunit_mktempFunc() for actual implementation 642# 643# Args: 644# None 645#oneTimeTearDown() { :; } # DO NOT UNCOMMENT THIS FUNCTION 646 647# Stub. This function will be called before each test is run. 648# 649# Common environment preparation tasks shared by all tests can be defined here. 650# 651# This function should be overridden by the user in their unit test suite. 652# Note: see _shunit_mktempFunc() for actual implementation 653# 654# Args: 655# None 656#setUp() { :; } 657 658# Note: see _shunit_mktempFunc() for actual implementation 659# Stub. This function will be called after each test is run. 660# 661# Common environment cleanup tasks shared by all tests can be defined here. 662# 663# This function should be overridden by the user in their unit test suite. 664# Note: see _shunit_mktempFunc() for actual implementation 665# 666# Args: 667# None 668#tearDown() { :; } # DO NOT UNCOMMENT THIS FUNCTION 669 670#------------------------------------------------------------------------------ 671# internal shUnit2 functions 672# 673 674# Create a temporary directory to store various run-time files in. 675# 676# This function is a cross-platform temporary directory creation tool. Not all 677# OSes have the mktemp function, so one is included here. 678# 679# Args: 680# None 681# Outputs: 682# string: the temporary directory that was created 683_shunit_mktempDir() 684{ 685 # try the standard mktemp function 686 ( exec mktemp -dqt shunit.XXXXXX 2>/dev/null ) && return 687 688 # the standard mktemp didn't work. doing our own. 689 if [ -r '/dev/urandom' -a -x '/usr/bin/od' ]; then 690 _shunit_random_=`/usr/bin/od -vAn -N4 -tx4 </dev/urandom \ 691 |sed 's/^[^0-9a-f]*//'` 692 elif [ -n "${RANDOM:-}" ]; then 693 # $RANDOM works 694 _shunit_random_=${RANDOM}${RANDOM}${RANDOM}$$ 695 else 696 # $RANDOM doesn't work 697 _shunit_date_=`date '+%Y%m%d%H%M%S'` 698 _shunit_random_=`expr ${_shunit_date_} / $$` 699 fi 700 701 _shunit_tmpDir_="${TMPDIR:-/tmp}/shunit.${_shunit_random_}" 702 ( umask 077 && mkdir "${_shunit_tmpDir_}" ) || \ 703 _shunit_fatal 'could not create temporary directory! exiting' 704 705 echo ${_shunit_tmpDir_} 706 unset _shunit_date_ _shunit_random_ _shunit_tmpDir_ 707} 708 709# This function is here to work around issues in Cygwin. 710# 711# Args: 712# None 713_shunit_mktempFunc() 714{ 715 for _shunit_func_ in oneTimeSetUp oneTimeTearDown setUp tearDown suite noexec 716 do 717 _shunit_file_="${__shunit_tmpDir}/${_shunit_func_}" 718 cat <<EOF >"${_shunit_file_}" 719#! /bin/sh 720exit ${SHUNIT_TRUE} 721EOF 722 chmod +x "${_shunit_file_}" 723 done 724 725 unset _shunit_file_ 726} 727 728# Final cleanup function to leave things as we found them. 729# 730# Besides removing the temporary directory, this function is in charge of the 731# final exit code of the unit test. The exit code is based on how the script 732# was ended (e.g. normal exit, or via Ctrl-C). 733# 734# Args: 735# name: string: name of the trap called (specified when trap defined) 736_shunit_cleanup() 737{ 738 _shunit_name_=$1 739 740 case ${_shunit_name_} in 741 EXIT) _shunit_signal_=0 ;; 742 INT) _shunit_signal_=2 ;; 743 TERM) _shunit_signal_=15 ;; 744 *) 745 _shunit_warn "unrecognized trap value (${_shunit_name_})" 746 _shunit_signal_=0 747 ;; 748 esac 749 750 # do our work 751 rm -fr "${__shunit_tmpDir}" 752 753 # exit for all non-EXIT signals 754 if [ ${_shunit_name_} != 'EXIT' ]; then 755 _shunit_warn "trapped and now handling the (${_shunit_name_}) signal" 756 # disable EXIT trap 757 trap 0 758 # add 128 to signal and exit 759 exit `expr ${_shunit_signal_} + 128` 760 elif [ ${__shunit_reportGenerated} -eq ${SHUNIT_FALSE} ] ; then 761 _shunit_assertFail 'Unknown failure encountered running a test' 762 _shunit_generateReport 763 exit ${SHUNIT_ERROR} 764 fi 765 766 unset _shunit_name_ _shunit_signal_ 767} 768 769# The actual running of the tests happens here. 770# 771# Args: 772# None 773_shunit_execSuite() 774{ 775 for _shunit_test_ in ${__shunit_suite}; do 776 __shunit_testSuccess=${SHUNIT_TRUE} 777 778 # disable skipping 779 endSkipping 780 781 # execute the per-test setup function 782 setUp 783 784 # execute the test 785 echo "${_shunit_test_}" 786 eval ${_shunit_test_} 787 788 # execute the per-test tear-down function 789 tearDown 790 791 # update stats 792 if [ ${__shunit_testSuccess} -eq ${SHUNIT_TRUE} ]; then 793 __shunit_testsPassed=`expr ${__shunit_testsPassed} + 1` 794 else 795 __shunit_testsFailed=`expr ${__shunit_testsFailed} + 1` 796 fi 797 done 798 799 unset _shunit_test_ 800} 801 802# Generates the user friendly report with appropriate OK/FAILED message. 803# 804# Args: 805# None 806# Output: 807# string: the report of successful and failed tests, as well as totals. 808_shunit_generateReport() 809{ 810 _shunit_ok_=${SHUNIT_TRUE} 811 812 # if no exit code was provided one, determine an appropriate one 813 [ ${__shunit_testsFailed} -gt 0 \ 814 -o ${__shunit_testSuccess} -eq ${SHUNIT_FALSE} ] \ 815 && _shunit_ok_=${SHUNIT_FALSE} 816 817 echo 818 if [ ${__shunit_testsTotal} -eq 1 ]; then 819 echo "Ran ${__shunit_testsTotal} test." 820 else 821 echo "Ran ${__shunit_testsTotal} tests." 822 fi 823 824 _shunit_failures_='' 825 _shunit_skipped_='' 826 [ ${__shunit_assertsFailed} -gt 0 ] \ 827 && _shunit_failures_="failures=${__shunit_assertsFailed}" 828 [ ${__shunit_assertsSkipped} -gt 0 ] \ 829 && _shunit_skipped_="skipped=${__shunit_assertsSkipped}" 830 831 if [ ${_shunit_ok_} -eq ${SHUNIT_TRUE} ]; then 832 _shunit_msg_='OK' 833 [ -n "${_shunit_skipped_}" ] \ 834 && _shunit_msg_="${_shunit_msg_} (${_shunit_skipped_})" 835 else 836 _shunit_msg_="FAILED (${_shunit_failures_}" 837 [ -n "${_shunit_skipped_}" ] \ 838 && _shunit_msg_="${_shunit_msg_},${_shunit_skipped_}" 839 _shunit_msg_="${_shunit_msg_})" 840 fi 841 842 echo 843 echo ${_shunit_msg_} 844 __shunit_reportGenerated=${SHUNIT_TRUE} 845 846 unset _shunit_failures_ _shunit_msg_ _shunit_ok_ _shunit_skipped_ 847} 848 849# Test for whether a function should be skipped. 850# 851# Args: 852# None 853# Returns: 854# boolean: whether the test should be skipped (TRUE/FALSE constant) 855_shunit_shouldSkip() 856{ 857 [ ${__shunit_skip} -eq ${SHUNIT_FALSE} ] && return ${SHUNIT_FALSE} 858 _shunit_assertSkip 859} 860 861# Records a successful test. 862# 863# Args: 864# None 865_shunit_assertPass() 866{ 867 __shunit_assertsPassed=`expr ${__shunit_assertsPassed} + 1` 868 __shunit_assertsTotal=`expr ${__shunit_assertsTotal} + 1` 869} 870 871# Records a test failure. 872# 873# Args: 874# message: string: failure message to provide user 875_shunit_assertFail() 876{ 877 _shunit_msg_=$1 878 879 __shunit_testSuccess=${SHUNIT_FALSE} 880 __shunit_assertsFailed=`expr ${__shunit_assertsFailed} + 1` 881 __shunit_assertsTotal=`expr ${__shunit_assertsTotal} + 1` 882 echo "${__SHUNIT_ASSERT_MSG_PREFIX}${_shunit_msg_}" 883 884 unset _shunit_msg_ 885} 886 887# Records a skipped test. 888# 889# Args: 890# None 891_shunit_assertSkip() 892{ 893 __shunit_assertsSkipped=`expr ${__shunit_assertsSkipped} + 1` 894 __shunit_assertsTotal=`expr ${__shunit_assertsTotal} + 1` 895} 896 897# Prepare a script filename for sourcing. 898# 899# Args: 900# script: string: path to a script to source 901# Returns: 902# string: filename prefixed with ./ (if necessary) 903_shunit_prepForSourcing() 904{ 905 _shunit_script_=$1 906 case "${_shunit_script_}" in 907 /*|./*) echo "${_shunit_script_}" ;; 908 *) echo "./${_shunit_script_}" ;; 909 esac 910 unset _shunit_script_ 911} 912 913# Escape a character in a string. 914# 915# Args: 916# c: string: unescaped character 917# s: string: to escape character in 918# Returns: 919# string: with escaped character(s) 920_shunit_escapeCharInStr() 921{ 922 [ -n "$2" ] || return # no point in doing work on an empty string 923 924 # Note: using shorter variable names to prevent conflicts with 925 # _shunit_escapeCharactersInString(). 926 _shunit_c_=$1 927 _shunit_s_=$2 928 929 930 # escape the character 931 echo ''${_shunit_s_}'' |sed 's/\'${_shunit_c_}'/\\\'${_shunit_c_}'/g' 932 933 unset _shunit_c_ _shunit_s_ 934} 935 936# Escape a character in a string. 937# 938# Args: 939# str: string: to escape characters in 940# Returns: 941# string: with escaped character(s) 942_shunit_escapeCharactersInString() 943{ 944 [ -n "$1" ] || return # no point in doing work on an empty string 945 946 _shunit_str_=$1 947 948 # Note: using longer variable names to prevent conflicts with 949 # _shunit_escapeCharInStr(). 950 for _shunit_char_ in '"' '$' "'" '`'; do 951 _shunit_str_=`_shunit_escapeCharInStr "${_shunit_char_}" "${_shunit_str_}"` 952 done 953 954 echo "${_shunit_str_}" 955 unset _shunit_char_ _shunit_str_ 956} 957 958# Extract list of functions to run tests against. 959# 960# Args: 961# script: string: name of script to extract functions from 962# Returns: 963# string: of function names 964_shunit_extractTestFunctions() 965{ 966 _shunit_script_=$1 967 968 # extract the lines with test function names, strip of anything besides the 969 # function name, and output everything on a single line. 970 _shunit_regex_='^[ ]*(function )*test[A-Za-z0-9_]* *\(\)' 971 egrep "${_shunit_regex_}" "${_shunit_script_}" \ 972 |sed 's/^[^A-Za-z0-9_]*//;s/^function //;s/\([A-Za-z0-9_]*\).*/\1/g' \ 973 |xargs 974 975 unset _shunit_regex_ _shunit_script_ 976} 977 978#------------------------------------------------------------------------------ 979# main 980# 981 982# determine the operating mode 983if [ $# -eq 0 ]; then 984 __shunit_script=${__SHUNIT_PARENT} 985 __shunit_mode=${__SHUNIT_MODE_SOURCED} 986else 987 __shunit_script=$1 988 [ -r "${__shunit_script}" ] || \ 989 _shunit_fatal "unable to read from ${__shunit_script}" 990 __shunit_mode=${__SHUNIT_MODE_STANDALONE} 991fi 992 993# create a temporary storage location 994__shunit_tmpDir=`_shunit_mktempDir` 995 996# provide a public temporary directory for unit test scripts 997# TODO(kward): document this 998SHUNIT_TMPDIR="${__shunit_tmpDir}/tmp" 999mkdir "${SHUNIT_TMPDIR}" 1000 1001# setup traps to clean up after ourselves 1002trap '_shunit_cleanup EXIT' 0 1003trap '_shunit_cleanup INT' 2 1004trap '_shunit_cleanup TERM' 15 1005 1006# create phantom functions to work around issues with Cygwin 1007_shunit_mktempFunc 1008PATH="${__shunit_tmpDir}:${PATH}" 1009 1010# make sure phantom functions are executable. this will bite if /tmp (or the 1011# current $TMPDIR) points to a path on a partition that was mounted with the 1012# 'noexec' option. the noexec command was created with _shunit_mktempFunc(). 1013noexec 2>/dev/null || _shunit_fatal \ 1014 'please declare TMPDIR with path on partition with exec permission' 1015 1016# we must manually source the tests in standalone mode 1017if [ "${__shunit_mode}" = "${__SHUNIT_MODE_STANDALONE}" ]; then 1018 . "`_shunit_prepForSourcing \"${__shunit_script}\"`" 1019fi 1020 1021# execute the oneTimeSetUp function (if it exists) 1022oneTimeSetUp 1023 1024# execute the suite function defined in the parent test script 1025# deprecated as of 2.1.0 1026suite 1027 1028# if no suite function was defined, dynamically build a list of functions 1029if [ -z "${__shunit_suite}" ]; then 1030 shunit_funcs_=`_shunit_extractTestFunctions "${__shunit_script}"` 1031 for shunit_func_ in ${shunit_funcs_}; do 1032 suite_addTest ${shunit_func_} 1033 done 1034fi 1035unset shunit_func_ shunit_funcs_ 1036 1037# execute the tests 1038_shunit_execSuite 1039 1040# execute the oneTimeTearDown function (if it exists) 1041oneTimeTearDown 1042 1043# generate the report 1044_shunit_generateReport 1045 1046# that's it folks 1047[ ${__shunit_testsFailed} -eq 0 ] 1048exit $? 1049