1368 lines
40 KiB
Plaintext
1368 lines
40 KiB
Plaintext
; --------------------------------------------------------------------------------
|
|
; @Title: LBTEST - Unit tests in PRACTICE
|
|
; @Description:
|
|
; Unit test framework for automated tests
|
|
; @Keywords: assertion assertions lbtest test unit unittest
|
|
; @Author: MOB
|
|
; @Copyright: (C) 1989-2021 Lauterbach GmbH, licensed for use with TRACE32(R) only
|
|
; --------------------------------------------------------------------------------
|
|
; $Id: lbtest.cmm 21247 2023-08-10 05:43:04Z mobermeir $
|
|
|
|
PMACRO.EXPLICIT // force the script-writer to declare all macros explicitly
|
|
|
|
PRIVATE &lbTestOptions
|
|
ENTRY %LINE &lbTestOptions
|
|
|
|
ON CoMmanD PUTS GOSUB Puts // helper command for printing
|
|
|
|
Var.NEWLOCAL char[9.][4096.] \LbTest_settings
|
|
GOSUB ParseOptions "&lbTestOptions"
|
|
|
|
GOSUB Initialize
|
|
|
|
PRIVATE &statusAllTestCases
|
|
GOSUB RunAllTestCases
|
|
RETURNVALUES &statusAllTestCases
|
|
|
|
GOSUB Finalize
|
|
|
|
ENDDO "&statusAllTestCases"
|
|
|
|
; --------------------------------------------------------------------------------
|
|
|
|
; parse parameters and set configuration options:
|
|
ParseOptions:
|
|
(
|
|
PARAMETERS &options
|
|
IF (STRing.SCAN("&options","--help",0.)>=0.)
|
|
(
|
|
PRINT "Usage: "
|
|
PRINT " DO lbtest.cmm [options]"
|
|
PRINT ""
|
|
PRINT "Available options:"
|
|
PRINT " --workingdir=</path/to/dir> run in /path/to/dir, i.e. expects to find the"
|
|
PRINT " tests there and also writes logfiles there."
|
|
PRINT " default: current working directory (pwd)"
|
|
PRINT " --logfile=<file> write log output to <file> (default: lbtest.log)"
|
|
PRINT " only applicable if --logging=allinone"
|
|
PRINT " --summarylogfile=<file> write summary log output to <file> "
|
|
PRINT " (default: test_summary.log)"
|
|
PRINT " --logging=<allinone|individual> write log output to one file (default) or"
|
|
PRINT " to individual files for each test case"
|
|
PRINT " --printtime=<on|off> print elapsed time of each test (default: on)"
|
|
PRINT " --debugmode=<mode> stop script for debugging, available <mode>s:"
|
|
PRINT " failedassertion: stop after a failed assertion"
|
|
PRINT " unhandlederror: stop after an unhandled error"
|
|
PRINT " --logcommands=<off|all> print every command before executing it"
|
|
PRINT " --verbose print more log messages"
|
|
PRINT " --veryverbose print even more log messages"
|
|
PRINT " --help display this help and exit"
|
|
PRINT ""
|
|
ENDDO
|
|
)
|
|
PRIVATE ¶m &workingdir
|
|
|
|
&workingdir=STRing.Replace(OS.PresentWorkingDirectory(),"\","/",0)
|
|
Var.Assign \LbTest_settings[8.]="&workingdir" // backup initial working directory
|
|
|
|
&workingdir=STRing.SCANAndExtract("&options","--workingdir=","&workingdir")
|
|
&workingdir=STRing.Replace(OS.FILE.ABSPATH("&workingdir"),"\","/",0)
|
|
Var.Assign \LbTest_settings[7.]="&workingdir"
|
|
|
|
¶m=STRing.SCANAndExtract("&options","--logfile=","&workingdir/lbtest.log")
|
|
¶m=STRing.Replace("¶m","\","/",0)
|
|
Var.Assign \LbTest_settings[0.]="¶m"
|
|
|
|
¶m=STRing.SCANAndExtract("&options","--summarylogfile=","&workingdir/lbtest_summary.log")
|
|
¶m=STRing.Replace("¶m","\","/",0)
|
|
Var.Assign \LbTest_settings[1.]="¶m"
|
|
|
|
¶m=STRing.SCANAndExtract("&options","--logging=","allinone")
|
|
Var.Assign \LbTest_settings[2.]="¶m"
|
|
|
|
¶m=STRing.SCANAndExtract("&options","--printtime=","on")
|
|
Var.Assign \LbTest_settings[3.]="¶m"
|
|
|
|
¶m=STRing.SCANAndExtract("&options","--debugmode=","")
|
|
Var.Assign \LbTest_settings[4.]="¶m"
|
|
|
|
¶m=STRing.SCANAndExtract("&options","--logcommands=","off")
|
|
Var.Assign \LbTest_settings[5.]="¶m"
|
|
|
|
IF (STRing.SCAN("&options","--verbose",0.)>=0.)
|
|
(
|
|
Var.Assign \LbTest_settings[6.]="verbose"
|
|
)
|
|
IF (STRing.SCAN("&options","--veryverbose",0.)>=0.)
|
|
(
|
|
Var.Assign \LbTest_settings[6.]="veryverbose"
|
|
)
|
|
RETURN
|
|
)
|
|
|
|
Initialize:
|
|
(
|
|
PRIVATE &requiredSoftwareVersion &testSummaryLog
|
|
&requiredSoftwareVersion=143582.
|
|
IF (VERSION.BUILD.BASE()<&requiredSoftwareVersion)
|
|
(
|
|
PRINT %ERROR "lbtest.cmm requires at least TRACE32 software version &requiredSoftwareVersion (this is " SOFTWARE.VERSION() ")"
|
|
ENDDO "FAIL"
|
|
)
|
|
|
|
IF (Var.STRing(\LbTest_settings[5.])=="all") // --logcommands=all
|
|
(
|
|
LOG.toAREA ON /IndentCalls /COLOR GRAY
|
|
)
|
|
|
|
AREA.Create A000 1024. 32767.
|
|
AREA.CLEAR
|
|
|
|
IF (Var.STRing(\LbTest_settings[2.])=="allinone") // --logging=allinone
|
|
(
|
|
PRIVATE &logfile
|
|
&logfile=Var.STRing(\LbTest_settings[0.]) // --logfile=<file>
|
|
AREA.OPEN A000 "&logfile" /Create /NoFileCache // start logging
|
|
)
|
|
AREA.Select A000
|
|
AREA.view A000
|
|
|
|
&testSummaryLog=Var.STRing(\LbTest_settings[1.]) // --summarylogfile=<file>
|
|
PUTS "logging test summary to &testSummaryLog" "DEBUG1"
|
|
IF (OS.FILE.readable("&testSummaryLog"))
|
|
(
|
|
RM "&testSummaryLog"
|
|
)
|
|
|
|
IF (OS.PresentWorkingDirectory()!=Var.STRing(\LbTest_settings[7.]))
|
|
(
|
|
// change to working directory (defaults to pwd)
|
|
PRIVATE &workingdir
|
|
&workingdir=Var.STRing(\LbTest_settings[7.]) // --workingdir=</path/to/dir>
|
|
SILENT.ChDir "&workingdir"
|
|
)
|
|
RETURN
|
|
)
|
|
|
|
Finalize:
|
|
(
|
|
IF (Var.STRing(\LbTest_settings[2.])=="allinone") // --logging=allinone
|
|
(
|
|
AREA.CLOSE A000 // stop logging to lbtest.log
|
|
)
|
|
AREA.Select A000
|
|
AREA.view A000
|
|
|
|
IF (Var.STRing(\LbTest_settings[5.])=="all") // --logcommands=all
|
|
(
|
|
LOG.toAREA OFF
|
|
)
|
|
|
|
IF (OS.PresentWorkingDirectory()!=Var.STRing(\LbTest_settings[8.])) // initial working directory
|
|
(
|
|
// change back to initial working directory
|
|
PRIVATE &workingdir
|
|
&workingdir=Var.STRing(\LbTest_settings[8.])
|
|
SILENT.ChDir "&workingdir"
|
|
)
|
|
|
|
RETURN
|
|
)
|
|
|
|
RunAllTestCases:
|
|
(
|
|
PRIVATE &testCaseIndex &statusAllTests &numTestCases &unitTestStartTime &unitTestDuration
|
|
PRIVATE &numTestCasesPass &numTestCasesFail &numTestCasesNotExec
|
|
&numTestCasesPass=0.
|
|
&numTestCasesFail=0.
|
|
&numTestCasesNotExec=0.
|
|
|
|
Var.NEWLOCAL char[1000.][256.] \LbTest_testCases
|
|
GOSUB FindTestCases "\LbTest_testCases"
|
|
RETURNVALUES &numTestCases
|
|
|
|
GOSUB PutsStart "UNITTEST" "lbtest.cmm" "&numTestCases"
|
|
&unitTestStartTime=OS.TIMER()
|
|
&testCaseIndex=0.
|
|
Var.WHILE (\LbTest_testCases[&testCaseIndex][0])
|
|
(
|
|
PRIVATE &testCase &statusTestCase &numTests &setupTestCase &setupTest &tearDownTest &tearDownTestCase
|
|
&testCase=Var.STRing(\LbTest_testCases[&testCaseIndex])
|
|
|
|
PUTS "analyzing test case: &testCase" "DEBUG2"
|
|
Var.NEWLOCAL char[1000.][256.] \LbTest_tests
|
|
GOSUB FindTestCaseLabels "&testCase" "\LbTest_tests"
|
|
RETURNVALUES &numTests &setupTestCase &setupTest &tearDownTest &tearDownTestCase
|
|
|
|
GOSUB RunTestCase "&testCase" "\LbTest_tests" "&numTests" "&setupTestCase" "&setupTest" "&tearDownTest" "&tearDownTestCase"
|
|
RETURNVALUES &statusTestCase
|
|
GOSUB Statistics "&statusTestCase" "&numTestCasesPass" "&numTestCasesFail" "&numTestCasesNotExec"
|
|
RETURNVALUES &numTestCasesPass &numTestCasesFail &numTestCasesNotExec
|
|
|
|
&testCaseIndex=&testCaseIndex+1.
|
|
)
|
|
&unitTestDuration=OS.TIMER()-&unitTestStartTime
|
|
GOSUB PrintSummary "UNITTEST" "lbtest.cmm" "0." "&numTestCasesPass" "&numTestCasesFail" "&numTestCasesNotExec" "&unitTestDuration"
|
|
RETURNVALUES &statusAllTests
|
|
|
|
RETURN "&statusAllTests"
|
|
)
|
|
|
|
RunTestCase:
|
|
(
|
|
PARAMETERS &testCase &hllArray &numTests &setupTestCase &setupTest &tearDownTest &tearDownTestCase
|
|
PRIVATE &testCaseLog &status &numSetupTearDownFail &numTestsPass &numTestsFail &numTestsNotExec
|
|
PRIVATE &testId &unusedMessage &testCaseStartTime &testCaseDuration
|
|
&numSetupTearDownFail=0.
|
|
&numTestsPass=0.
|
|
&numTestsFail=0.
|
|
&numTestsNotExec=0.
|
|
|
|
IF (Var.STRing(\LbTest_settings[2.])=="individual") // --logging=individual
|
|
(
|
|
; log each test case to file:
|
|
&testCaseLog=STRing.Replace("&testCase",".cmm",".log",-1.)
|
|
AREA.OPEN A000 "&testCaseLog" /Create /NoFileCache // start logging to test_*.log file
|
|
)
|
|
AREA.Select A000
|
|
AREA.view A000
|
|
|
|
GOSUB PutsStart "TESTCASE" "&testCase" "&numTests"
|
|
&testCaseStartTime=OS.TIMER()
|
|
; SetupTestCase:
|
|
GOSUB RunTestRoutine "&testCase" "&setupTestCase"
|
|
RETURNVALUES &status &testId &unusedMessage
|
|
IF ("&status"=="FAIL")
|
|
(
|
|
PUTS "tests not executed, because &testId failed" "FAIL"
|
|
&numSetupTearDownFail=&numSetupTearDownFail+1.
|
|
)
|
|
ELSE
|
|
(
|
|
PRIVATE &testIndex
|
|
&testIndex=0.
|
|
Var.WHILE (&hllArray[&testIndex][0]!=0.)
|
|
(
|
|
PRIVATE &test &statusTest
|
|
&test=Var.STRing(&hllArray[&testIndex])
|
|
; Test_*:
|
|
GOSUB RunTest "&testCase" "&setupTest" "&test" "&tearDownTest"
|
|
RETURNVALUES &statusTest
|
|
GOSUB Statistics "&statusTest" "&numTestsPass" "&numTestsFail" "&numTestsNotExec"
|
|
RETURNVALUES &numTestsPass &numTestsFail &numTestsNotExec
|
|
&testIndex=&testIndex+1.
|
|
)
|
|
)
|
|
; TearDownTestCase:
|
|
GOSUB RunTestRoutine "&testCase" "&tearDownTestCase"
|
|
RETURNVALUES &status &testId &unusedMessage
|
|
IF ("&status"=="FAIL")
|
|
(
|
|
PUTS "&testId" "FAIL"
|
|
&numSetupTearDownFail=&numSetupTearDownFail+1.
|
|
)
|
|
&testCaseDuration=OS.TIMER()-&testCaseStartTime
|
|
GOSUB PrintSummary "TESTCASE" "&testCase" "&numSetupTearDownFail" "&numTestsPass" "&numTestsFail" "&numTestsNotExec" "&testCaseDuration"
|
|
RETURNVALUES &status
|
|
|
|
IF (Var.STRing(\LbTest_settings[2.])=="individual") // --logging=individual
|
|
(
|
|
AREA.CLOSE A000 // stop logging to test_*.log
|
|
)
|
|
AREA.Select A000
|
|
RETURN "&status"
|
|
)
|
|
|
|
RunTest:
|
|
(
|
|
PARAMETERS &testCase &setupTest &test &tearDownTest
|
|
PRIVATE &statusSetupTest &statusTest &statusTearDownTest &statusRunTest
|
|
PRIVATE &testId &unusedMessage &optionalMessage &testStartTime
|
|
|
|
GOSUB GetTestId "&testCase" "&test"
|
|
RETURNVALUES &testId
|
|
GOSUB PutsStart "TEST" "&testId" ""
|
|
|
|
(
|
|
PRIVATE &routineId
|
|
&testStartTime=OS.TIMER()
|
|
GOSUB RunTestRoutine "&testCase" "&setupTest"
|
|
RETURNVALUES &statusSetupTest &routineId &unusedMessage
|
|
|
|
IF ("&statusSetupTest"=="FAIL")
|
|
(
|
|
&optionalMessage="test setup failed"
|
|
GOSUB PutsResult "INSIDETEST" "&statusSetupTest" "&routineId" "&optionalMessage (actual test &testId will not be executed)" "&testStartTime"
|
|
)
|
|
ELSE
|
|
(
|
|
; Test_*:
|
|
GOSUB RunTestRoutine "&testCase" "&test"
|
|
RETURNVALUES &statusTest &routineId &optionalMessage
|
|
)
|
|
; TearDownTest:
|
|
GOSUB RunTestRoutine "&testCase" "&tearDownTest"
|
|
RETURNVALUES &statusTearDownTest &routineId &unusedMessage
|
|
IF ("&statusTearDownTest"=="FAIL")
|
|
(
|
|
&optionalMessage="test tear-down failed"
|
|
GOSUB PutsResult "INSIDETEST" "&statusTearDownTest" "&routineId" "&optionalMessage" "&testStartTime" // TODO message?
|
|
)
|
|
)
|
|
IF (("&statusSetupTest"=="FAIL")||("&statusTest"=="FAIL")||("&statusTearDownTest"=="FAIL"))
|
|
(
|
|
&statusRunTest="FAIL"
|
|
)
|
|
ELSE IF ("&statusTest"=="NOT_EXEC")
|
|
(
|
|
; if the test was not executed, we don't care whether setup or tear-down was NOT_EXEC or PASS
|
|
&statusRunTest="NOT_EXEC"
|
|
)
|
|
ELSE IF ("&statusTest"=="PASS")
|
|
(
|
|
; here we don't care neither whether setup or tear-down was NOT_EXEC or PASS
|
|
&statusRunTest="PASS"
|
|
)
|
|
ELSE
|
|
(
|
|
; fallback
|
|
&statusRunTest="FAIL"
|
|
)
|
|
|
|
GOSUB PutsResult "TEST" "&statusRunTest" "&testId" "&optionalMessage" "&testStartTime"
|
|
|
|
RETURN "&statusRunTest"
|
|
)
|
|
|
|
RunTestRoutine:
|
|
(
|
|
PARAMETERS &testCase &routine
|
|
PRIVATE &testId
|
|
|
|
PRIVATE &statusRoutine &optionalReturnMessage
|
|
&statusRoutine="NOT_EXEC"
|
|
IF ("&routine"=="")
|
|
(
|
|
; nothing to do
|
|
)
|
|
ELSE
|
|
(
|
|
LOCAL &LbTest_assertionResult // TRUE() or FALSE()
|
|
Var.NEWLOCAL int[4.] \LbTest_assertions
|
|
; \LbTest_assertions[0.]: number of failed assertions
|
|
; \LbTest_assertions[1.]: number of unhandled errors
|
|
; \LbTest_assertions[2.]: state of assertion (sanity check for assertion-nesting)
|
|
; \LbTest_assertions[3.]: state of printed in assertion
|
|
|
|
; set up hooks to assertions:
|
|
; - generic assertions
|
|
ON CoMmanD A_TRUE GOSUB AssertTrue
|
|
ON CoMmanD A_FALSE GOSUB AssertFalse
|
|
ON CoMmanD A_EQUALS GOSUB AssertEquals
|
|
ON CoMmanD A_UNEQUAL GOSUB AssertNotEquals
|
|
; - numbers
|
|
ON CoMmanD A_NUM_EQ GOSUB AssertNumEquals
|
|
ON CoMmanD A_NUM_NE GOSUB AssertNumNotEquals
|
|
ON CoMmanD A_NUM_GT GOSUB AssertNumGreaterThan
|
|
ON CoMmanD A_NUM_GE GOSUB AssertNumGreaterThanOrEqualTo
|
|
ON CoMmanD A_NUM_LT GOSUB AssertNumLessThan
|
|
ON CoMmanD A_NUM_LE GOSUB AssertNumLessThanOrEqualto
|
|
; - strings
|
|
ON CoMmanD A_STR_EQ GOSUB AssertStringEquals
|
|
ON CoMmanD A_STR_NE GOSUB AssertStringNotEquals
|
|
ON CoMmanD A_STR_Z GOSUB AssertStringEmpty
|
|
ON CoMmanD A_STR_N GOSUB AssertStringNotEmpty
|
|
; - execution
|
|
ON CoMmanD A_X_PASS GOSUB AssertExecutePass
|
|
ON CoMmanD A_X_FAIL GOSUB AssertExecuteFail
|
|
|
|
(
|
|
PRIVATE &statusTestRoutine
|
|
GOSUB GetTestId "&testCase" "&routine"
|
|
RETURNVALUES &testId
|
|
|
|
Var.Assign \LbTest_assertions[0.]=0. // reset counter of assertion failures
|
|
Var.Assign \LbTest_assertions[1.]=0. // reset counter of unhandled errors
|
|
Var.Assign \LbTest_assertions[2.]=0. // reset state of assertion-nesting
|
|
|
|
(
|
|
; run the actual test:
|
|
PUTS "running: &testId" "DEBUG1"
|
|
ON.ERROR.GOSUB UnhandledError
|
|
DO "&testCase" &routine
|
|
RETURNVALUES &statusTestRoutine
|
|
)
|
|
|
|
&statusRoutine="FAIL"
|
|
IF (Var.VALUE(\LbTest_assertions[0.])!=0.) // if at least one assertion failed
|
|
(
|
|
PRIVATE &s
|
|
IF (Var.VALUE(\LbTest_assertions[0.])>1.)
|
|
(
|
|
&s="s"
|
|
)
|
|
&optionalReturnMessage=FORMAT.Decimal(0.,Var.VALUE(\LbTest_assertions[0.]))+" assertion&s failed"
|
|
)
|
|
ELSE IF (Var.VALUE(\LbTest_assertions[1.])!=0.) // if at least one unhandled error occurred
|
|
(
|
|
PRIVATE &s
|
|
IF (Var.VALUE(\LbTest_assertions[1.])>1.)
|
|
(
|
|
&s="s"
|
|
)
|
|
&optionalReturnMessage=FORMAT.Decimal(0.,Var.VALUE(\LbTest_assertions[1.]))+" unhandled error&s"
|
|
)
|
|
ELSE IF ("&statusTestRoutine"=="FAIL")
|
|
(
|
|
; nothing to do (&statusRoutine already set to FAIL above)
|
|
)
|
|
ELSE IF ("&statusTestRoutine"=="NOT_EXEC")
|
|
(
|
|
&statusRoutine="NOT_EXEC"
|
|
)
|
|
ELSE IF (("&statusTestRoutine"=="PASS")||("&statusTestRoutine"==""))
|
|
(
|
|
&statusRoutine="PASS"
|
|
)
|
|
ELSE
|
|
(
|
|
&optionalReturnMessage="unknown return value"
|
|
)
|
|
)
|
|
)
|
|
RETURN "&statusRoutine" "&testId" "&optionalReturnMessage"
|
|
)
|
|
|
|
Statistics:
|
|
(
|
|
PARAMETERS &status &numPass &numFail &numNotExec
|
|
IF ("&status"=="PASS")
|
|
(
|
|
&numPass=&numPass+1.
|
|
)
|
|
ELSE IF ("&status"=="NOT_EXEC")
|
|
(
|
|
&numNotExec=&numNotExec+1.
|
|
)
|
|
ELSE
|
|
(
|
|
&numFail=&numFail+1.
|
|
)
|
|
RETURN "&numPass" "&numFail" "&numNotExec"
|
|
)
|
|
|
|
PutsResult:
|
|
(
|
|
PARAMETERS &putsLevel &status &testId &message &startTime
|
|
PRIVATE &out &testSummaryLog
|
|
&out="&testId"
|
|
IF ("&message"!="")
|
|
(
|
|
&out="&out: &message"
|
|
)
|
|
IF (Var.STRing(\LbTest_settings[3.])=="on") // --printtime=on
|
|
(
|
|
PRIVATE &duration
|
|
&duration=OS.TIMER()-&startTime
|
|
GOSUB FormatDuration "&duration"
|
|
RETURNVALUES &duration
|
|
&out="&out (&duration)"
|
|
)
|
|
PUTS "&out" "&status" "&putsLevel"
|
|
&testSummaryLog=Var.STRing(\LbTest_settings[1.]) // --summarylogfile=<file>
|
|
APPEND "&testSummaryLog" %String "&status: &out"
|
|
RETURN
|
|
)
|
|
|
|
PutsStart:
|
|
(
|
|
PARAMETERS &putsLevel &module &numTests
|
|
PRIVATE &out
|
|
IF ("&putsLevel"=="TEST")
|
|
(
|
|
&out="&module"
|
|
)
|
|
ELSE
|
|
(
|
|
PRIVATE &type &s
|
|
IF ("&putsLevel"=="UNITTEST")
|
|
(
|
|
&type="test case"
|
|
)
|
|
ELSE // IF ("&putsLevel"=="TESTCASE")
|
|
(
|
|
&type="test"
|
|
)
|
|
IF (&numTests>1.)
|
|
(
|
|
&s="s"
|
|
)
|
|
&module=OS.FILE.NAME("&module")
|
|
&out="Running "+FORMAT.Decimal(0.,&numTests)+" &type&s from &module"
|
|
)
|
|
PUTS "&out" "RUN" "&putsLevel"
|
|
RETURN
|
|
)
|
|
|
|
GetTestId:
|
|
(
|
|
PARAMS &testCase &routine
|
|
PRIVATE &testId
|
|
&testId=OS.FILE.NAME(&testCase) // strip path
|
|
&testId=STRing.Replace("&testId","test_","",1.)
|
|
&testId=STRing.Replace("&testId",".cmm","",-1.) // strip extension
|
|
&routine=STRing.Replace("&routine","Test_","",1.)
|
|
IF ("&routine"!="")
|
|
(
|
|
&testId="&testId.&routine"
|
|
)
|
|
RETURN "&testId"
|
|
)
|
|
|
|
PrintSummary:
|
|
(
|
|
PARAMETERS &putsLevel &module &numSetupTearDownFail &numPass &numFail &numNotExec &duration
|
|
PRIVATE &summaryString &status
|
|
&module=OS.FILE.NAME("&module")
|
|
IF (&numSetupTearDownFail!=0.)
|
|
(
|
|
&summaryString=FORMAT.Decimal(0.,&numSetupTearDownFail)+" failures in Setup / TearDown"
|
|
&status="FAIL"
|
|
)
|
|
ELSE
|
|
(
|
|
PRIVATE &nTotal
|
|
&nTotal=&numFail+&numPass+&numNotExec
|
|
&summaryString=FORMAT.Decimal(0.,&nTotal)+" total"
|
|
&status="PASS"
|
|
|
|
IF (&numPass>0.)
|
|
(
|
|
&summaryString="&summaryString, "+FORMAT.Decimal(0.,&numPass)+" passed"
|
|
)
|
|
IF (&numFail>0.)
|
|
(
|
|
&summaryString="&summaryString, "+FORMAT.Decimal(0.,&numFail)+" failed"
|
|
&status="FAIL"
|
|
)
|
|
IF (&numNotExec>0.)
|
|
(
|
|
&summaryString="&summaryString, "+FORMAT.Decimal(0.,&numNotExec)+" not executed"
|
|
)
|
|
)
|
|
&summaryString="Summary of &module: &summaryString"
|
|
IF (Var.STRing(\LbTest_settings[3.])=="on") // --printtime=on
|
|
(
|
|
GOSUB FormatDuration "&duration"
|
|
RETURNVALUES &duration
|
|
&summaryString="&summaryString (&duration total)"
|
|
)
|
|
PUTS "&summaryString" "&status" "&putsLevel"
|
|
PRINT "" // emtpy line
|
|
RETURN "&status"
|
|
)
|
|
|
|
FormatDuration:
|
|
(
|
|
PARAMS &duration
|
|
PRIVATE &ret
|
|
IF (&duration<1000.)
|
|
(
|
|
&ret=FORMAT.Decimal(0.,&duration)+" ms"
|
|
)
|
|
ELSE
|
|
(
|
|
&ret=FORMAT.Decimal(0.,&duration/1000.)+"."+FORMAT.Decimal(0.,&duration%1000.)+" s"
|
|
)
|
|
RETURN "&ret"
|
|
)
|
|
|
|
FindTestCases:
|
|
(
|
|
PARAMETERS &hllArray
|
|
PRIVATE &pattern &file ¤tDirectory &testCaseIndex
|
|
&testCaseIndex=0.
|
|
¤tDirectory=OS.PresentWorkingDirectory()
|
|
&pattern="test_*.cmm"
|
|
&file=OS.FIRSTFILE("&pattern")
|
|
WHILE ("&file"!="")
|
|
(
|
|
PRIVATE &fullFile
|
|
; force forward slashes for c-strings:
|
|
&fullFile=STRing.Replace("¤tDirectory/&file","\","/",0)
|
|
PUTS "FindTestCases: found test case: &fullFile" "DEBUG2"
|
|
Var.Assign &hllArray[&testCaseIndex]="&fullFile"
|
|
&testCaseIndex=&testCaseIndex+1.
|
|
&file=OS.NEXTFILE()
|
|
)
|
|
RETURN "&testCaseIndex"
|
|
)
|
|
|
|
; scan for all Test_*: labels
|
|
FindTestCaseLabels:
|
|
(
|
|
PARAMETERS &testCase &hllArray
|
|
PRIVATE &setupTestCase &setupTest &tearDownTest &tearDownTestCase &labelIndex
|
|
&labelIndex=0.
|
|
OPEN #101 "&testCase" /Read
|
|
RePeaT
|
|
(
|
|
PRIVATE ¤tLine
|
|
READ #101 %LINE ¤tLine
|
|
IF (STRing.FIND("¤tLine",":"))
|
|
(
|
|
IF (("&setupTestCase"=="")&&(STRing.SCAN("¤tLine","SetupTestCase:",0.)==0.))
|
|
(
|
|
GOSUB LineIsLabel "¤tLine"
|
|
RETURNVALUES &setupTestCase
|
|
)
|
|
ELSE IF (("&setupTest"=="")&&(STRing.SCAN("¤tLine","SetupTest:",0.)==0.))
|
|
(
|
|
GOSUB LineIsLabel "¤tLine"
|
|
RETURNVALUES &setupTest
|
|
)
|
|
ELSE IF (("&tearDownTest"=="")&&(STRing.SCAN("¤tLine","TearDownTest:",0.)==0.))
|
|
(
|
|
GOSUB LineIsLabel "¤tLine"
|
|
RETURNVALUES &tearDownTest
|
|
)
|
|
ELSE IF (("&tearDownTestCase"=="")&&(STRing.SCAN("¤tLine","TearDownTestCase:",0.)==0.))
|
|
(
|
|
GOSUB LineIsLabel "¤tLine"
|
|
RETURNVALUES &tearDownTestCase
|
|
)
|
|
ELSE IF (STRing.SCAN("¤tLine","Test_",0.)==0.)
|
|
(
|
|
PRIVATE &label
|
|
GOSUB LineIsLabel "¤tLine"
|
|
RETURNVALUES &label
|
|
IF ("&label"!="")
|
|
(
|
|
PUTS "Found test: &label" "DEBUG2"
|
|
Var.Assign &hllArray[&labelIndex]="&label"
|
|
&labelIndex=&labelIndex+1.
|
|
)
|
|
)
|
|
)
|
|
)
|
|
WHILE (!FILE.EOFLASTREAD())
|
|
CLOSE #101
|
|
RETURN "&labelIndex" "&setupTestCase" "&setupTest" "&tearDownTest" "&tearDownTestCase"
|
|
)
|
|
|
|
LineIsLabel:
|
|
(
|
|
PARAMETERS &line
|
|
&line=STRing.TRIM("&line")
|
|
IF (STRing.FIND("&line",":"))
|
|
(
|
|
; contains a colon
|
|
PRIVATE &label &optionalComment
|
|
&label=STRing.SPLIT("&line",":",0.)
|
|
&optionalComment=STRing.SPLIT("&line",":",1.)
|
|
&optionalComment=STRing.TRIM("&optionalComment")
|
|
IF (("&optionalComment"=="")||(STRing.SCAN("&optionalComment",";",0.)==0.)||(STRing.SCAN("&optionalComment","//",0.)==0.))
|
|
(
|
|
; nothing illegal in the label
|
|
IF (STRing.TOKEN(STRing.LoWeR("&label"),"abcdefghijklmnopqrstuvwxyz_0123456789",0.)=="")
|
|
(
|
|
; no illegal characters in label
|
|
; PUTS "LineIsLabel found: &label" "DEBUG2"
|
|
RETURN "&label"
|
|
)
|
|
)
|
|
)
|
|
RETURN ""
|
|
)
|
|
|
|
; PUTS: colored printing
|
|
Puts:
|
|
(
|
|
PARAMETERS &message &optionalMode &optionalLevel
|
|
&message=STRing.TRIM("&message")
|
|
&optionalMode=STRing.TRIM("&optionalMode")
|
|
PRIVATE &prefix
|
|
IF ("&optionalMode"!="")
|
|
(
|
|
PRIVATE &colorPrefix
|
|
IF ("&optionalMode"=="FAIL")
|
|
(
|
|
&colorPrefix="%COLOR.RED"
|
|
&prefix="[ FAILED ]"
|
|
)
|
|
ELSE IF ("&optionalMode"=="PASS")
|
|
(
|
|
&colorPrefix="%COLOR.GREEN"
|
|
IF ("&optionalMode"=="PASS")
|
|
(
|
|
&prefix="[ PASS ]"
|
|
)
|
|
)
|
|
ELSE IF ("&optionalMode"=="NOT_EXEC")
|
|
(
|
|
&colorPrefix="%COLOR.OLIVE"
|
|
&prefix="[ NOT_EXEC ]"
|
|
)
|
|
ELSE IF ("&optionalMode"=="RUN")
|
|
(
|
|
&colorPrefix="%COLOR.NAVY"
|
|
&prefix="[ RUN ]"
|
|
)
|
|
ELSE IF ("&optionalMode"=="DEBUG1")
|
|
(
|
|
IF ((Var.STRing(\LbTest_settings[6.])!="verbose")&&(Var.STRing(\LbTest_settings[6.])!="veryverbose"))
|
|
(
|
|
RETURN // dont' print anything
|
|
)
|
|
&colorPrefix="%COLOR.GREY"
|
|
&prefix="[ DEBUG1 ]"
|
|
)
|
|
ELSE IF ("&optionalMode"=="DEBUG2")
|
|
(
|
|
IF (Var.STRing(\LbTest_settings[6.])!="veryverbose")
|
|
(
|
|
RETURN // dont' print anything
|
|
)
|
|
&colorPrefix="%COLOR.SILVER"
|
|
&prefix="[ DEBUG2 ]"
|
|
)
|
|
IF ("&optionalLevel"!="")
|
|
(
|
|
IF ("&optionalLevel"=="UNITTEST")
|
|
(
|
|
&prefix=STRing.Replace("&prefix"," ","=",0.)
|
|
)
|
|
ELSE IF ("&optionalLevel"=="TESTCASE")
|
|
(
|
|
&prefix=STRing.Replace("&prefix"," ","-",0.)
|
|
)
|
|
ELSE IF ("&optionalLevel"=="TEST")
|
|
(
|
|
; keep &prefix as is (spaces)
|
|
)
|
|
ELSE IF ("&optionalLevel"=="INSIDETEST")
|
|
(
|
|
&prefix=STRing.LoWeR("&prefix")
|
|
)
|
|
)
|
|
&prefix="&colorPrefix ""&prefix "" %COLOR.NAVY"
|
|
)
|
|
ELSE
|
|
(
|
|
&prefix=""" """
|
|
)
|
|
|
|
PRINT &prefix "&message"
|
|
RETURN
|
|
)
|
|
|
|
; --------------------------------------------------------------------------------
|
|
; Assertions:
|
|
|
|
; A_TRUE <condition> [optional fail message]
|
|
; Assert that given <condition> is true.
|
|
AssertTrue:
|
|
(
|
|
PRIVATE &testMe &optionalFailMessage
|
|
ENTRY &testMe %LINE &optionalFailMessage
|
|
GOSUB AssertBooleanGeneric "&testMe" "Expected true, got false (&testMe)" "&optionalFailMessage" "1."
|
|
RETURN &LbTest_assertionResult
|
|
)
|
|
|
|
; A_FALSE <condition> [optional fail message]
|
|
; Assert that given <condition> is false.
|
|
AssertFalse:
|
|
(
|
|
PRIVATE &testMe &optionalFailMessage
|
|
ENTRY &testMe %LINE &optionalFailMessage
|
|
GOSUB AssertBooleanGeneric "!(&testMe)" "Expected false, got true (&testMe)" "&optionalFailMessage" "1."
|
|
RETURN &LbTest_assertionResult
|
|
)
|
|
|
|
; A_EQUALS <expected> <actual> [optional fail message]
|
|
; Assert that given arguments are equal.
|
|
; This is a generic command which also makes sure that the types of both
|
|
; arguments are equal. Use more specialized assertions if possible
|
|
; (e.g. A_NUM_EQ or A_STR_EQ).
|
|
AssertEquals:
|
|
(
|
|
PRIVATE &compare1 &compare2 &optionalFailMessage
|
|
ENTRY &compare1 &compare2 %LINE &optionalFailMessage
|
|
GOSUB AssertEqualityGeneric "&compare1" "==" "&compare2" "equal to" "&optionalFailMessage" "1."
|
|
RETURN &LbTest_assertionResult
|
|
)
|
|
|
|
; A_UNEQUAL <expected> <actual> [optional fail message]
|
|
; Assert that given arguments are unequal.
|
|
AssertNotEquals:
|
|
(
|
|
PRIVATE &compare1 &compare2 &optionalFailMessage
|
|
ENTRY &compare1 &compare2 %LINE &optionalFailMessage
|
|
GOSUB AssertEqualityGeneric "&compare1" "!=" "&compare2" "unequal to" "&optionalFailMessage" "1."
|
|
RETURN &LbTest_assertionResult
|
|
)
|
|
|
|
; A_NUM_EQ <expected> <actual> [optional fail message]
|
|
; Assert that given arguments are numerically equal. This works for
|
|
; hexadecimal, integer and binary types.
|
|
AssertNumEquals:
|
|
(
|
|
PRIVATE &compare1 &compare2 &optionalFailMessage
|
|
ENTRY &compare1 &compare2 %LINE &optionalFailMessage
|
|
GOSUB AssertNumericalGeneric "&compare1" "==" "&compare2" "numerically equal to" "&optionalFailMessage" "1."
|
|
RETURN &LbTest_assertionResult
|
|
)
|
|
|
|
; A_NUM_NE <expected> <actual> [optional fail message]
|
|
; Assert that given arguments are numerically not equal. This works for
|
|
; hexadecimal, integer and binary types.
|
|
AssertNumNotEquals:
|
|
(
|
|
PRIVATE &compare1 &compare2 &optionalFailMessage
|
|
ENTRY &compare1 &compare2 %LINE &optionalFailMessage
|
|
GOSUB AssertNumericalGeneric "&compare1" "!=" "&compare2" "numerically unequal to" "&optionalFailMessage" "1."
|
|
RETURN &LbTest_assertionResult
|
|
)
|
|
|
|
; A_NUM_GT <arg1> <arg2> [optional fail message]
|
|
; Assert that arg1 is numerically greater than arg2. This works for
|
|
; hexadecimal, integer and binary types.
|
|
AssertNumGreaterThan:
|
|
(
|
|
PRIVATE &compare1 &compare2 &optionalFailMessage
|
|
ENTRY &compare1 &compare2 %LINE &optionalFailMessage
|
|
GOSUB AssertNumericalGeneric "&compare1" ">" "&compare2" "greater than" "&optionalFailMessage" "1."
|
|
RETURN &LbTest_assertionResult
|
|
)
|
|
|
|
; A_NUM_GE <arg1> <arg2> [optional fail message]
|
|
; Assert that arg1 is numerically greater than or equal to arg2. This works
|
|
; for hexadecimal, integer and binary types.
|
|
AssertNumGreaterThanOrEqualTo:
|
|
(
|
|
PRIVATE &compare1 &compare2 &optionalFailMessage
|
|
ENTRY &compare1 &compare2 %LINE &optionalFailMessage
|
|
GOSUB AssertNumericalGeneric "&compare1" ">=" "&compare2" "greater than or equal to" "&optionalFailMessage" "1."
|
|
RETURN &LbTest_assertionResult
|
|
)
|
|
|
|
; A_NUM_LT <arg1> <arg2> [optional fail message]
|
|
; Assert that arg1 is numerically less than arg2. This works for hexadecimal,
|
|
; integer and binary types.
|
|
AssertNumLessThan:
|
|
(
|
|
PRIVATE &compare1 &compare2 &optionalFailMessage
|
|
ENTRY &compare1 &compare2 %LINE &optionalFailMessage
|
|
GOSUB AssertNumericalGeneric "&compare1" "<" "&compare2" "less than" "&optionalFailMessage" "1."
|
|
RETURN &LbTest_assertionResult
|
|
)
|
|
|
|
; A_NUM_LE <arg1> <arg2> [optional fail message]
|
|
; Assert that arg1 is numerically less than or equal to arg2. This works for
|
|
; hexadecimal, integer and binary types.
|
|
AssertNumLessThanOrEqualto:
|
|
(
|
|
PRIVATE &compare1 &compare2 &optionalFailMessage
|
|
ENTRY &compare1 &compare2 %LINE &optionalFailMessage
|
|
GOSUB AssertNumericalGeneric "&compare1" "<=" "&compare2" "less than or equal to" "&optionalFailMessage" "1."
|
|
RETURN &LbTest_assertionResult
|
|
)
|
|
|
|
; A_STR_EQ <expected> <actual> [optional fail message]
|
|
; Assert that given argument strings are equal. This works only for string
|
|
; types.
|
|
AssertStringEquals:
|
|
(
|
|
PRIVATE &compare1 &compare2 &optionalFailMessage
|
|
ENTRY &compare1 &compare2 %LINE &optionalFailMessage
|
|
GOSUB AssertStringGeneric "&compare1" "==" "&compare2" "notEmpty" "&optionalFailMessage" "1."
|
|
RETURN &LbTest_assertionResult
|
|
)
|
|
|
|
; A_STR_NE <expected> <actual> [optional fail message]
|
|
; Assert that given argument strings are not equal. This works only for
|
|
; string types.
|
|
AssertStringNotEquals:
|
|
(
|
|
PRIVATE &compare1 &compare2 &optionalFailMessage
|
|
ENTRY &compare1 &compare2 %LINE &optionalFailMessage
|
|
GOSUB AssertStringGeneric "&compare1" "!=" "&compare2" "notEmpty" "&optionalFailMessage" "1."
|
|
RETURN &LbTest_assertionResult
|
|
)
|
|
|
|
; A_STR_Z <arg> [optional fail message]
|
|
; Assert that given argument string is empty.
|
|
AssertStringEmpty:
|
|
(
|
|
PRIVATE &testMe &optionalFailMessage
|
|
ENTRY &testMe %LINE &optionalFailMessage
|
|
; """""" is actually an empty string "" in quotes with escaped quote signs
|
|
GOSUB AssertStringGeneric "&testMe" "==" """""" "empty" "&optionalFailMessage" "1."
|
|
RETURN &LbTest_assertionResult
|
|
)
|
|
|
|
; A_STR_N <arg> [optional fail message]
|
|
; Assert that given argument string is not empty.
|
|
AssertStringNotEmpty:
|
|
(
|
|
PRIVATE &testMe &optionalFailMessage
|
|
ENTRY &testMe %LINE &optionalFailMessage
|
|
; """""" is actually an empty string "" in quotes with escaped quote signs
|
|
GOSUB AssertStringGeneric "&testMe" "!=" """""" "empty" "&optionalFailMessage" "1."
|
|
RETURN &LbTest_assertionResult
|
|
)
|
|
|
|
; A_X_PASS <command> [args] //no optional fail message
|
|
; Assert that execution of given command raises no error.
|
|
AssertExecutePass:
|
|
(
|
|
PRIVATE &execMe
|
|
ENTRY %LINE &execMe
|
|
GOSUB AssertExecuteGeneric "&execMe" "pass" "1."
|
|
RETURN &LbTest_assertionResult
|
|
)
|
|
|
|
; A_X_FAIL <command> [args] //no optional fail message
|
|
; Assert that execution of given command raises some error.
|
|
AssertExecuteFail:
|
|
(
|
|
PRIVATE &execMe
|
|
ENTRY %LINE &execMe
|
|
GOSUB AssertExecuteGeneric "&execMe" "fail" "1."
|
|
RETURN &LbTest_assertionResult
|
|
)
|
|
|
|
; --------------------------------------------------------------------------------
|
|
; Helper functions for assertions:
|
|
|
|
InitializeAssertion:
|
|
(
|
|
PARAMETERS &callerNesting
|
|
&LbTest_assertionResult=FALSE() // be pessimistic and assume the assertion will fail
|
|
Var.Assign \LbTest_assertions[2.]++ // increase counter for sanity check for assertion-nesting
|
|
IF (Var.VALUE(\LbTest_assertions[2.])!=1.)
|
|
(
|
|
&callerNesting=&callerNesting+1.
|
|
GOSUB FailAssertion "Wrong usage: Assertions can not be nested" "&callerNesting"
|
|
)
|
|
Var.Assign \LbTest_assertions[3.]=0. // no failure was printed yet
|
|
RETURN
|
|
)
|
|
|
|
FinalizeAssertion:
|
|
(
|
|
PARAMETERS &callerNesting
|
|
&callerNesting=&callerNesting+1.
|
|
Var.Assign \LbTest_assertions[2.]--
|
|
IF (Var.VALUE(\LbTest_assertions[2.])!=0.)
|
|
(
|
|
GOSUB FailAssertion "Wrong usage: Assertions can not be nested" "&callerNesting"
|
|
)
|
|
IF ((!&LbTest_assertionResult)&&(Var.VALUE(\LbTest_assertions[3.])!=1.))
|
|
(
|
|
; the assertion failed, but we forgot to print an error
|
|
; (framework issue here in lbtest.cmm)
|
|
GOSUB FailAssertion "unknown error in assertion" "&callerNesting"
|
|
)
|
|
RETURN
|
|
)
|
|
|
|
; This function may be used only from inside official assertions
|
|
CallOtherGenericAssertion:
|
|
(
|
|
PARAMETERS &genericAssertion &arg1 &arg2 &arg3 &arg4 &arg5 &arg6level
|
|
&arg6level=&arg6level+1.
|
|
; pre-decrement and post-increment \LbTest_assertions[2.] to
|
|
; avoid "Wrong usage: Assertions can not be nested" error
|
|
Var.Assign \LbTest_assertions[2.]--
|
|
GOSUB &genericAssertion "&arg1" "&arg2" "&arg3" "&arg4" "&arg5" "&arg6level"
|
|
Var.Assign \LbTest_assertions[2.]++
|
|
RETURN
|
|
)
|
|
|
|
AssertBooleanGeneric:
|
|
(
|
|
PARAMETERS &passCondition &defaultFailMessage &optionalFailMessage &callerNesting
|
|
PRIVATE &type1
|
|
&callerNesting=&callerNesting+1.
|
|
GOSUB InitializeAssertion "&callerNesting"
|
|
GOSUB EnsureType "&passCondition" "boolean" "&callerNesting"
|
|
RETURNVALUES &type1
|
|
IF (&type1!=0)
|
|
(
|
|
; this is the actual check:
|
|
IF (&passCondition)
|
|
(
|
|
&LbTest_assertionResult=TRUE()
|
|
)
|
|
ELSE
|
|
(
|
|
IF ("&optionalFailMessage"=="")
|
|
(
|
|
&optionalFailMessage="&defaultFailMessage"
|
|
)
|
|
GOSUB FailAssertion "&optionalFailMessage" "&callerNesting"
|
|
)
|
|
)
|
|
GOSUB FinalizeAssertion "&callerNesting"
|
|
RETURN
|
|
)
|
|
|
|
AssertNumericalGeneric:
|
|
(
|
|
PARAMETERS &compare1 &operator &compare2 &defaultFailMessagePart &optionalFailMessage &callerNesting
|
|
PRIVATE &type1 &type2
|
|
&callerNesting=&callerNesting+1.
|
|
GOSUB InitializeAssertion "&callerNesting"
|
|
GOSUB EnsureType "&compare1" "intnum" "&callerNesting"
|
|
RETURNVALUES &type1
|
|
GOSUB EnsureType "&compare2" "intnum" "&callerNesting"
|
|
RETURNVALUES &type2
|
|
IF ((&type1!=0.)&&(&type2!=0.))
|
|
(
|
|
; this is the actual check:
|
|
IF (&(compare1)&(operator)&(compare2))
|
|
(
|
|
&LbTest_assertionResult=TRUE()
|
|
)
|
|
ELSE
|
|
(
|
|
IF ("&optionalFailMessage"=="")
|
|
(
|
|
PRIVATE &message &compare1Expanded &compare2Expanded
|
|
&message="Expected &compare1"
|
|
&compare1Expanded=&compare1
|
|
IF ("&compare1Expanded"!="&compare1")
|
|
(
|
|
&message="&message (&compare1Expanded)"
|
|
)
|
|
&message="&message to be &defaultFailMessagePart &compare2"
|
|
&compare2Expanded=&compare2
|
|
IF ("&compare2Expanded"!="&compare2")
|
|
(
|
|
&message="&message (&compare2Expanded)"
|
|
)
|
|
&optionalFailMessage="&message"
|
|
)
|
|
GOSUB FailAssertion "&optionalFailMessage" "&callerNesting"
|
|
)
|
|
)
|
|
GOSUB FinalizeAssertion "&callerNesting"
|
|
RETURN
|
|
)
|
|
|
|
AssertStringGeneric:
|
|
(
|
|
PARAMETERS &compare1 &operator &compare2 &empty &optionalFailMessage &callerNesting
|
|
PRIVATE &type1 &type2
|
|
&callerNesting=&callerNesting+1.
|
|
GOSUB InitializeAssertion "&callerNesting"
|
|
GOSUB EnsureType "&compare1" "string" "&callerNesting"
|
|
RETURNVALUES &type1
|
|
GOSUB EnsureType "&compare2" "string" "&callerNesting"
|
|
RETURNVALUES &type2
|
|
IF ((&type1!=0.)&&(&type2!=0.))
|
|
(
|
|
; this is the actual check:
|
|
IF (&(compare1)&(operator)&(compare2))
|
|
(
|
|
&LbTest_assertionResult=TRUE()
|
|
)
|
|
ELSE
|
|
(
|
|
IF ("&optionalFailMessage"=="")
|
|
(
|
|
IF ("&operator"=="==")
|
|
(
|
|
IF ("&empty"=="empty")
|
|
(
|
|
&optionalFailMessage="Expected empty string, but it contains '"+&compare1+"'"
|
|
)
|
|
ELSE
|
|
(
|
|
&optionalFailMessage="Expected string equality, but '"+&compare1+"'!='"+&compare2"'"
|
|
)
|
|
)
|
|
ELSE // IF ("&operator"=="!=")
|
|
(
|
|
IF ("&empty"=="empty")
|
|
(
|
|
&optionalFailMessage="Expected content in string, but it is emtpy"
|
|
)
|
|
ELSE
|
|
(
|
|
&optionalFailMessage="Expected string inequality, but '"+&compare1+"'=='"+&compare2"'"
|
|
)
|
|
)
|
|
)
|
|
GOSUB FailAssertion "&optionalFailMessage" "&callerNesting"
|
|
)
|
|
)
|
|
GOSUB FinalizeAssertion "&callerNesting"
|
|
RETURN
|
|
)
|
|
|
|
AssertEqualityGeneric:
|
|
(
|
|
PARAMETERS &compare1 &operator &compare2 &defaultFailMessagePart &optionalFailMessage &callerNesting
|
|
PRIVATE &type1 &type2
|
|
&callerNesting=&callerNesting+1.
|
|
GOSUB InitializeAssertion "&callerNesting"
|
|
GOSUB EnsureType "&compare1" "any" "&callerNesting"
|
|
RETURNVALUES &type1
|
|
GOSUB EnsureType "&compare2" "any" "&callerNesting"
|
|
RETURNVALUES &type2
|
|
IF ((&type1!=0.)&&(&type2!=0.))
|
|
(
|
|
; preconditions:
|
|
IF (&type1!=&type2)
|
|
(
|
|
GOSUB FailAssertion "Wrong usage: Given arguments (&compare1,&compare2) are of different types (&type1,&type2)" "&callerNesting"
|
|
)
|
|
ELSE IF (((&type1)&((0x8000)))!=0x0) //empty/no expression parameter
|
|
(
|
|
GOSUB FailAssertion "Wrong usage: Both arguments are empty" "&callerNesting"
|
|
)
|
|
ELSE IF (((&type1)&((0x4000)))!=0x0) //bitmask
|
|
(
|
|
GOSUB FailAssertion "Wrong usage: Unable to compare bitmasks" "&callerNesting"
|
|
)
|
|
ELSE
|
|
(
|
|
; redirect to more specialized assertions if possible:
|
|
IF (((&type1)&((0x000e)))!=0x0) //hex, integer or binary
|
|
(
|
|
IF ("&operator"=="==")
|
|
(
|
|
GOSUB CallOtherGenericAssertion "AssertNumericalGeneric" "&compare1" "==" "&compare2" "numerically equal to" "&optionalFailMessage" "&callerNesting"
|
|
)
|
|
ELSE
|
|
(
|
|
GOSUB CallOtherGenericAssertion "AssertNumericalGeneric" "&compare1" "!=" "&compare2" "numerically unequal to" "&optionalFailMessage" "&callerNesting"
|
|
)
|
|
)
|
|
ELSE IF (((&type1)&((0x0040)))!=0x0) //string
|
|
(
|
|
IF ("&operator"=="==")
|
|
(
|
|
GOSUB CallOtherGenericAssertion "AssertStringGeneric" "&compare1" "==" "&compare2" "notEmpty" "&optionalFailMessage" "&callerNesting"
|
|
)
|
|
ELSE
|
|
(
|
|
GOSUB CallOtherGenericAssertion "AssertStringGeneric" "&compare1" "!=" "&compare2" "notEmpty" "&optionalFailMessage" "&callerNesting"
|
|
)
|
|
)
|
|
ELSE
|
|
(
|
|
; this is the actual check:
|
|
IF (&(compare1)&(operator)&(compare2))
|
|
(
|
|
&LbTest_assertionResult=TRUE()
|
|
)
|
|
ELSE
|
|
(
|
|
IF ("&optionalFailMessage"=="")
|
|
(
|
|
PRIVATE &message &compare1Expanded &compare2Expanded
|
|
&message="Expected &compare1"
|
|
&compare1Expanded=&compare1
|
|
IF ("&compare1Expanded"!="&compare1")
|
|
(
|
|
&message="&message (&compare1Expanded)"
|
|
)
|
|
&message="&message to be &defaultFailMessagePart &compare2"
|
|
&compare2Expanded=&compare2
|
|
IF ("&compare2Expanded"!="&compare2")
|
|
(
|
|
&message="&message (&compare2Expanded)"
|
|
)
|
|
&optionalFailMessage="&message"
|
|
)
|
|
GOSUB FailAssertion "&optionalFailMessage" "&callerNesting"
|
|
)
|
|
)
|
|
)
|
|
)
|
|
GOSUB FinalizeAssertion "&callerNesting"
|
|
RETURN
|
|
)
|
|
|
|
AssertExecuteGeneric:
|
|
(
|
|
PARAMETERS &execMe &expect &callerNesting
|
|
&callerNesting=&callerNesting+1.
|
|
LOCAL &gotError
|
|
&gotError=FALSE()
|
|
ON.ERROR.GOSUB // install error handler
|
|
(
|
|
&gotError=TRUE()
|
|
RETURN
|
|
)
|
|
GOSUB InitializeAssertion "&callerNesting"
|
|
; this is the actual check (execute command):
|
|
&execMe
|
|
IF ("&expect"=="pass")
|
|
(
|
|
IF (&gotError)
|
|
(
|
|
GOSUB FailAssertion "Expected proper execution, but errors occurred executing '&execMe'" "&callerNesting"
|
|
)
|
|
ELSE
|
|
(
|
|
&LbTest_assertionResult=TRUE()
|
|
)
|
|
)
|
|
ELSE
|
|
(
|
|
IF (!&gotError)
|
|
(
|
|
GOSUB FailAssertion "Expected errors, but no errors occurred executing '&execMe'" "&callerNesting"
|
|
)
|
|
ELSE
|
|
(
|
|
&LbTest_assertionResult=TRUE()
|
|
)
|
|
)
|
|
GOSUB FinalizeAssertion "&callerNesting"
|
|
RETURN
|
|
)
|
|
|
|
TryEval:
|
|
(
|
|
LOCAL &gotError
|
|
&gotError=FALSE()
|
|
ON ERROR GOSUB
|
|
(
|
|
&gotError=TRUE()
|
|
RETURN
|
|
)
|
|
PARAMETERS &evalme
|
|
Eval &evalme
|
|
PRIVATE &evaltype
|
|
&evaltype=EVAL.TYPE()
|
|
IF (!&gotError)
|
|
(
|
|
RETURN "&evaltype"
|
|
)
|
|
PUTS "TryEval failed" "DEBUG1"
|
|
RETURN "0."
|
|
)
|
|
|
|
EnsureType:
|
|
(
|
|
PARAMETERS &checkVar &expectedType &callerNesting
|
|
PRIVATE &expectedMask &actualType
|
|
&callerNesting=&callerNesting+1.
|
|
IF ("&expectedType"=="boolean")
|
|
(
|
|
&expectedMask=0x0001
|
|
)
|
|
ELSE IF ("&expectedType"=="intnum")
|
|
(
|
|
&expectedMask=0x000e
|
|
)
|
|
ELSE IF ("&expectedType"=="string")
|
|
(
|
|
&expectedMask=0x0040
|
|
)
|
|
ELSE IF ("&expectedType"=="any")
|
|
(
|
|
&expectedMask=0xffff
|
|
)
|
|
ELSE
|
|
(
|
|
; PRINT "Values Expression Types"
|
|
; PRINT "0x0001 boolean"
|
|
; PRINT "0x0002 binary"
|
|
; PRINT "0x0004 hex"
|
|
; PRINT "0x0008 integer"
|
|
; PRINT "0x0010 float"
|
|
; PRINT "0x0020 ASCII constant"
|
|
; PRINT "0x0040 string"
|
|
; PRINT "0x0080 numeric range"
|
|
; PRINT "0x0100 address"
|
|
; PRINT "0x0200 address range"
|
|
; PRINT "0x0400 time"
|
|
; PRINT "0x0800 time range"
|
|
; PRINT "0x4000 bitmask"
|
|
; PRINT "0x8000 empty/no expression parameter"
|
|
&expectedMask=0x0
|
|
)
|
|
GOSUB TryEval "&checkVar"
|
|
RETURNVALUES &actualType
|
|
|
|
IF (&expectedMask==0x0)
|
|
(
|
|
GOSUB FailAssertion "Wrong usage: Unknown type '&expectedType'" "&callerNesting"
|
|
&actualType=0.
|
|
)
|
|
ELSE IF (((&actualType)&((0xffff)))==0x0)
|
|
(
|
|
GOSUB FailAssertion "Wrong usage: Can not determine type of argument (&checkVar)" "&callerNesting"
|
|
&actualType=0.
|
|
)
|
|
ELSE IF (((&actualType)&(&expectedMask))==0x0)
|
|
(
|
|
IF (&expectedMask==0x0001)
|
|
(
|
|
GOSUB FailAssertion "Wrong usage: type (&actualType) of given argument (&checkVar) is not boolean" "&callerNesting"
|
|
)
|
|
ELSE IF (&expectedMask==0x000e)
|
|
(
|
|
GOSUB FailAssertion "Wrong usage: type (&actualType) of given argument (&checkVar) is not numerical (hex/int/binary)" "&callerNesting"
|
|
)
|
|
ELSE IF (&expectedMask==0x0040)
|
|
(
|
|
GOSUB FailAssertion "Wrong usage: type (&actualType) of given argument (&checkVar) is not a string" "&callerNesting"
|
|
)
|
|
ELSE
|
|
(
|
|
GOSUB FailAssertion "Wrong usage: type (&actualType) of given argument (&checkVar) does not match mask (&expectedMask) as expected" "&callerNesting"
|
|
)
|
|
&actualType=0.
|
|
)
|
|
RETURN "&actualType"
|
|
)
|
|
|
|
UnhandledError:
|
|
(
|
|
PRIVATE &errorId &callerFile &callerLine
|
|
&errorId=ERROR.ID()
|
|
&callerFile=PRACTICE.CALLER.FILE(1.)
|
|
&callerLine=PRACTICE.CALLER.LINE(1.)
|
|
IF (Var.STRing(\LbTest_settings[4.])=="unhandlederror") // --debugmode=unhandlederror
|
|
(
|
|
PLIST
|
|
PBREAK.Set &callerLine+1. "&callerFile" /TeMPorary
|
|
)
|
|
&callerFile=OS.FILE.NAME("&callerFile")
|
|
&callerLine=FORMAT.Decimal(0.,&callerLine)
|
|
PUTS "&callerFile:&callerLine: unhandled error (&errorId)" "FAIL" "INSIDETEST"
|
|
Var.Assign \LbTest_assertions[1.]++
|
|
RETURN
|
|
)
|
|
|
|
; Fail an assertion
|
|
; This function is called by every assertion (A_*)
|
|
FailAssertion:
|
|
(
|
|
PARAMETERS &failMessage &callerNesting
|
|
&callerNesting=&callerNesting+1.
|
|
&LbTest_assertionResult=FALSE() // marke assertion as failed
|
|
Var.Assign \LbTest_assertions[0.]++ //increase assertion failure counter
|
|
PRIVATE &scriptFile &assertionLine
|
|
&scriptFile=PRACTICE.CALLER.FILE(&callerNesting)
|
|
&assertionLine=PRACTICE.CALLER.LINE(&callerNesting)
|
|
IF (Var.STRing(\LbTest_settings[4.])=="failedassertion") // --debugmode=failedassertion
|
|
(
|
|
PLIST
|
|
PBREAK.Set &assertionLine+1. "&scriptFile" /TeMPorary
|
|
)
|
|
&scriptFile=OS.FILE.NAME("&scriptFile")
|
|
&assertionLine=FORMAT.Decimal(0.,&assertionLine)
|
|
&failMessage="&scriptFile:&assertionLine: &failMessage"
|
|
PUTS "&failMessage" "FAIL" "INSIDETEST"
|
|
Var.Assign \LbTest_assertions[3.]=1. // assertion failure was printed
|
|
RETURN
|
|
)
|