< Summary - Igor Pro Universal Testing Framework

Information
Class: procedures.igortest-basics
Assembly: procedures
File(s): /builds/mirror/igortest/procedures/igortest-basics.ipf
Tag: 74147b3
Line coverage
49%
Covered lines: 655
Uncovered lines: 668
Coverable lines: 1323
Total lines: 2069
Line coverage: 49.5%
Branch coverage
57%
Covered branches: 94
Total branches: 164
Branch coverage: 57.3%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity Line coverage
IUTF_Basics#GetTestRunData()100%2100%
IUTF_Basics#ClearRTError()100%1100%
EqualWavesModeToString()100%110%
IUTF_FuncRefIsAssigned()100%1100%
UTF_FuncRefIsAssigned()100%10%
IUTF_Basics#GetDimLabels()100%30%
GenerateDimLabelDifference()100%110%
GetVersion()100%1100%
GetPackageFolder()50%266.67%
EvaluateResults()100%3100%
shouldDoAbort()50%375%
IUTF_Basics#setAbortFlag()100%10%
IUTF_Basics#InitAbortFlag()100%1100%
IUTF_Basics#IsAbortFromSkip()100%30%
IUTF_Basics#SetAbortFromSkipFlag()100%10%
IUTF_Basics#InitAbortFromSkipFlag()100%1100%
IsExpectedFailure()50%375%
IUTF_Basics#SetExpectedFailure()100%2100%
IUTF_Basics#IsProcGlobal()100%1100%
IUTF_Basics#getFullFunctionName()50%476%
IUTF_Basics#EvaluateRTE()100%130%
IUTF_Basics#CheckAbortCondition()100%20%
IUTF_Basics#GetTestCaseList()50%290%
IUTF_Basics#SortTestCaseList()50%290%
IUTF_Basics#HasProcedureShuffleBan()100%40%
IUTF_Basics#CreateTestRunSetup()50%2655.83%
IUTF_Basics#GetTestCaseCount()50%575%
IUTF_Basics#QueryIgorOption()100%1100%
IUTF_Basics#AdaptProcWinList()50%483.33%
IUTF_Basics#GetProcedureList()50%358.33%
IUTF_Basics#FindProcedures()50%962.07%
UTFBackgroundMonitor()100%10%
IUTFBackgroundMonitor()100%140%
IUTF_Basics#ClearReentrytoIUTF()100%1100%
IUTF_Basics#SaveState()100%1100%
IUTF_Basics#RestoreState()100%1100%
IUTF_Basics#IsBckgRegistered()100%2100%
IUTF_Basics#ResetBckgRegistered()100%1100%
IUTF_Basics#CallTestCase()64.29%3326.52%
IUTF_Basics#InitStrRunTest()100%1100%
RegisterUTFMonitor()100%40%
RegisterIUTFMonitor()100%120%
UnRegisterIUTFMonitor()100%10%
IUTF_Basics#CanRetry()50%927.27%
IUTF_Basics#CleanupRetry()100%10%
IUTF_Basics#ClearTestSetupWaves()100%1100%
IUTF_Basics#DetectDeprecation()100%238.46%
RunTest()63.46%8656.18%

File(s)

/builds/mirror/igortest/procedures/igortest-basics.ipf

#LineLine coverage
 1#pragma rtGlobals=3
 2#pragma rtFunctionErrors=1
 3#pragma version=1.10
 4#pragma TextEncoding="UTF-8"
 5#pragma ModuleName=IUTF_Basics
 6
 7#undef UTF_ALLOW_TRACING
 8#if Exists("TUFXOP_Version")
 9
 10#if IgorVersion() >= 10.00
 11#define UTF_ALLOW_TRACING
 12#elif (IgorVersion() >= 9.00) && (NumberByKey("BUILD", IgorInfo(0)) >= 38812)
 13#define UTF_ALLOW_TRACING
 14#endif
 15
 16#endif
 17
 18///@cond HIDDEN_SYMBOL
 19
 20static Constant FFNAME_OK        = 0x00
 21static Constant FFNAME_NOT_FOUND = 0x01
 22static Constant FFNAME_NO_MODULE = 0x02
 23static Constant TC_MATCH_OK      = 0x00
 24static Constant TC_REGEX_INVALID = 0x04
 25static Constant TC_NOT_FOUND     = 0x08
 26static Constant TC_LIST_EMPTY    = 0x10
 27static Constant GREPLIST_ERROR   = 0x20
 28
 29static Constant IGOR_MAX_DIMENSIONS = 4
 30
 31static StrConstant FIXED_LOG_FILENAME = "IUTF_Test"
 32
 33static StrConstant NO_SOURCE_PROCEDURE = "No source procedure"
 34
 35static StrConstant BACKGROUNDMONTASK = "IUTFBackgroundMonitor"
 36static StrConstant BACKGROUNDMONFUNC = "IUTFBackgroundMonitor"
 37static StrConstant BACKGROUNDINFOSTR = ":UNUSED_FOR_REENTRY:"
 38
 39static Constant TC_MODE_NORMAL = 0
 40static Constant TC_MODE_MD     = 1
 41static Constant TC_MODE_MMD    = 2
 42
 43static Constant MAX_PROCTAGS_SCAN_LINES = 20
 44
 45/// @brief Returns a global wave that stores data about this testrun
 33646static Function/WAVE GetTestRunData()
 47
 33648  string name = "TestRunData"
 49
 33650  DFREF    dfr = GetPackageFolder()
 33651  WAVE/Z/T wv  = dfr:$name
 33652  if(WaveExists(wv))
 32553    return wv
 1154  endif
 55
 1156  Make/T/N=(0, 6) dfr:$name/WAVE=wv
 57
 1158  SetDimLabel UTF_COLUMN, 0, PROCWIN, wv
 1159  SetDimLabel UTF_COLUMN, 1, TESTCASE, wv
 1160  SetDimLabel UTF_COLUMN, 2, FULLFUNCNAME, wv
 1161  SetDimLabel UTF_COLUMN, 3, DGENLIST, wv
 1162  SetDimLabel UTF_COLUMN, 4, SKIP, wv
 1163  SetDimLabel UTF_COLUMN, 5, EXPECTFAIL, wv
 64
 1165  return wv
 33666End
 67
 68/// @brief Helper function for try/catch with AbortOnRTE
 69///
 70/// Not clearing the RTE before calling `AbortOnRTE` will always trigger the RTE no
 71/// matter what you do in that line.
 72///
 73/// Usage:
 74/// @code
 75///
 76///    try
 77///       ClearRTError()
 78///       myFunc(); AbortOnRTE
 79///    catch
 80///      err = GetRTError(1)
 81///    endtry
 82///
 83/// @endcode
 3984static Function ClearRTError()
 85
 3986  variable err = GetRTError(1)
 3987End
 88
 89/// @brief Convert the mode parameter for `EqualWaves` to a string
 090Function/S EqualWavesModeToString(mode)
 91  variable mode
 92
 093  switch(mode)
 094    case WAVE_DATA:
 095      return "WAVE_DATA"
 096    case WAVE_DATA_TYPE:
 097      return "WAVE_DATA_TYPE"
 098    case WAVE_SCALING:
 099      return "WAVE_SCALING"
 0100    case DATA_UNITS:
 0101      return "DATA_UNITS"
 0102    case DIMENSION_UNITS:
 0103      return "DIMENSION_UNITS"
 0104    case DIMENSION_LABELS:
 0105      return "DIMENSION_LABELS"
 0106    case WAVE_NOTE:
 0107      return "WAVE_NOTE"
 0108    case WAVE_LOCK_STATE:
 0109      return "WAVE_LOCK_STATE"
 0110    case DATA_FULL_SCALE:
 0111      return "DATA_FULL_SCALE"
 0112    case DIMENSION_SIZES:
 0113      return "DIMENSION_SIZES"
 0114    default:
 0115      return "unknown mode"
 0116  endswitch
 0117End
 118
 119/// @class FUNC_REF_IS_ASSIGNED_DOCU
 120/// @brief Check wether the function reference points to
 121/// the prototype function or to an assigned function
 122///
 123/// Due to Igor Pro limitations you need to pass the function
 124/// info from `FuncRefInfo` and not the function reference itself.
 125///
 126/// @return 0 if pointing to prototype function, 1 otherwise
 159127Function IUTF_FuncRefIsAssigned(funcInfo)
 128  string funcInfo
 129
 159130  return NumberByKey("ISPROTO", funcInfo) == 0
 159131End
 132
 133/// @copydoc FUNC_REF_IS_ASSIGNED_DOCU
 134/// @deprecated Use IUTF_FuncRefIsAssigned instead
 0135Function UTF_FuncRefIsAssigned(funcInfo)
 136  string funcInfo
 137
 0138  return IUTF_FuncRefIsAssigned(funcInfo)
 0139End
 140
 141/// @brief Return a free text wave with the dimension labels of the
 142///        given dimension of the wave
 0143static Function/WAVE GetDimLabels(wv, dim)
 144  WAVE/Z   wv
 145  variable dim
 146
 0147  variable size
 148
 0149  if(!WaveExists(wv))
 0150    return $""
 0151  endif
 152
 0153  size = DimSize(wv, dim)
 154
 0155  if(size == 0)
 0156    return $""
 0157  endif
 158
 0159  Make/FREE/T/N=(size) labels = GetDimLabel(wv, dim, p)
 160
 0161  return labels
 0162End
 163
 164/// @brief Create a diagnostic message of the differing dimension labels
 165///
 166/// @param[in] wv1  Possible non-existing wave
 167/// @param[in] wv2  Possible non-existing wave
 168/// @param[out] str Diagnostic message indicating the deviations, empty string on success
 169///
 170/// @return 1 with no differences, 0 with differences
 0171Function GenerateDimLabelDifference(wv1, wv2, msg)
 172  WAVE/Z wv1, wv2
 173  string &msg
 174
 0175  variable i, j, numEntries
 0176  string str1, str2, tmpStr1, tmpStr2
 0177  variable ret
 178
 0179  msg = ""
 180
 0181  for(i = 0; i < IGOR_MAX_DIMENSIONS; i += 1)
 182
 0183    WAVE/Z/T label1 = GetDimLabels(wv1, i)
 0184    WAVE/Z/T label2 = GetDimLabels(wv2, i)
 185
 0186    if(!WaveExists(label1) && !WaveExists(label2))
 0187      break
 0188    endif
 189
 0190    if(!WaveExists(label1))
 0191      sprintf msg, "Empty dimension vs non-empty dimension"
 0192      return 0
 0193    elseif(!WaveExists(label2))
 0194      sprintf msg, "Non-empty dimension vs empty dimension"
 0195      return 0
 0196    else
 0197      // both exist but differ
 0198      str1 = GetDimLabel(wv1, i, -1)
 0199      str2 = GetDimLabel(wv2, i, -1)
 200
 0201      if(cmpstr(str1, str2))
 0202        tmpStr1 = IUTF_Utils#IUTF_PrepareStringForOut(str1)
 0203        tmpStr2 = IUTF_Utils#IUTF_PrepareStringForOut(str2)
 0204        sprintf msg, "Dimension labels for the entire dimension %d differ: %s vs %s", i, tmpStr1, tmpStr2
 0205        return 0
 0206      endif
 207
 0208      if(EqualWaves(label1, label2, WAVE_DATA))
 0209        continue
 0210      endif
 211
 0212      if(DimSize(label1, i) != DimSize(label2, i))
 0213        sprintf msg, "The sizes for dimension %d don't match: %d vs %d", i, DimSize(label1, i), DimSize(label2, i)
 0214        return 0
 0215      endif
 216
 0217      numEntries = DimSize(label1, i)
 0218      for(j = 0; j < numEntries; j += 1)
 0219        if(!cmpstr(label1[j], label2[j], 1))
 0220          continue
 0221        endif
 0222        str1    = label1[j]
 0223        str2    = label2[j]
 0224        tmpStr1 = IUTF_Utils#IUTF_PrepareStringForOut(str1)
 0225        tmpStr2 = IUTF_Utils#IUTF_PrepareStringForOut(str2)
 0226        sprintf msg, "Differing dimension label in dimension %d at index %d: %s vs %s", i, j, tmpStr1, tmpStr2
 0227        return 0
 0228      endfor
 0229    endif
 0230  endfor
 231
 0232  return 1
 0233End
 234
 5235Function/S GetVersion()
 5236  string version
 5237  sprintf version, "%.2f", PKG_VERSION
 238
 5239  return version
 5240End
 241
 242/// Returns the package folder
 5487243Function/DF GetPackageFolder()
 244
 5487245  DFREF dfr = $PKG_FOLDER
 5487246  if(!DataFolderRefStatus(dfr))
 0247    NewDataFolder/O root:Packages
 0248    NewDataFolder/O root:Packages:igortest
 0249    DFREF dfr = $PKG_FOLDER
 5487250  endif
 251
 5487252  return dfr
 5487253End
 254
 255/// Evaluate the result of an assertion that was used in a testcase. For evaluating internal errors use
 256/// ReportError* functions.
 257/// @param result          Set to 0 to signal an error. Any value different to 0 will be considered as success.
 258/// @param str             The message to report.
 259/// @param flags           A combination flags that are used by ReportResults() to determine what to do if result
 260///                        is in an error state.
 261/// @param cleanupInfo [optional, default enabled] If set different to zero it will cleanup
 262///               any assertion info message at the end of this function.
 263///               Cleanup is enforced if flags contains the ABORT_FUNCTION flag.
 264/// @param callStack       [optional, default current callStack] Can be used to set the callStack
 265///                        to a previous recorded callStack (GetRTStackInfo(3)).
 355266Function EvaluateResults(result, str, flags, [cleanupInfo, callStack])
 267  variable result, flags
 268  string   str
 269  variable cleanupInfo
 270  string   callStack
 271
 355272  cleanupInfo = ParamIsDefault(cleanupInfo) ? 1 : !!cleanupInfo
 273
 355274  IUTF_Debug#DebugFailedAssertion(result)
 355275  if(ParamIsDefault(callStack))
 351276    IUTF_Reporting#ReportResults(result, str, flags, cleanupInfo = cleanupInfo)
 4277  else
 4278    IUTF_Reporting#ReportResults(result, str, flags, cleanupInfo = cleanupInfo, callStack = callStack)
 355279  endif
 355280End
 281
 282/// Returns 1 if the abortFlag is set and zero otherwise
 700283Function shouldDoAbort()
 700284  NVAR/Z/SDFR=GetPackageFolder() abortFlag
 700285  if(NVAR_Exists(abortFlag) && abortFlag == 1)
 0286    return 1
 700287  else
 700288    return 0
 0289  endif
 700290End
 291
 292/// Sets the abort flag
 0293static Function setAbortFlag()
 0294  DFREF      dfr           = GetPackageFolder()
 0295  variable/G dfr:abortFlag = 1
 0296End
 297
 298/// Resets the abort flag
 11299static Function InitAbortFlag()
 11300  DFREF      dfr           = GetPackageFolder()
 11301  variable/G dfr:abortFlag = 0
 11302End
 303
 304/// Returns 1 if the abortFromSkipFlag is set and zero otherwise
 0305static Function IsAbortFromSkip()
 0306  NVAR/Z/SDFR=GetPackageFolder() abortFromSkipFlag
 0307  if(NVAR_Exists(abortFromSkipFlag) && abortFromSkipFlag == 1)
 0308    return 1
 0309  else
 0310    return 0
 0311  endif
 0312End
 313
 314/// Sets the abortFromSkipFlag flag
 0315static Function SetAbortFromSkipFlag()
 0316  DFREF      dfr                   = GetPackageFolder()
 0317  variable/G dfr:abortFromSkipFlag = 1
 0318End
 319
 320/// Resets the abortFromSkipFlag flag
 5321static Function InitAbortFromSkipFlag()
 5322  DFREF      dfr                   = GetPackageFolder()
 5323  variable/G dfr:abortFromSkipFlag = 0
 5324End
 325
 326/// @brief returns 1 if the current testcase is marked as expected failure, zero otherwise
 327///
 328/// @returns 1 if the current testcase is marked as expected failure, zero otherwise
 84329Function IsExpectedFailure()
 84330  NVAR/Z/SDFR=GetPackageFolder() expected_failure_flag
 331
 84332  if(NVAR_Exists(expected_failure_flag) && expected_failure_flag == 1)
 0333    return 1
 84334  else
 84335    return 0
 0336  endif
 84337End
 338
 339/// Sets the expected_failure_flag global
 79340static Function SetExpectedFailure(val)
 341  variable val
 342
 79343  DFREF dfr = GetPackageFolder()
 79344  NVAR/Z/SDFR=dfr expected_failure_flag
 345
 79346  if(!NVAR_Exists(expected_failure_flag))
 5347    variable/G    dfr:expected_failure_flag
 5348    NVAR/SDFR=dfr expected_failure_flag
 79349  endif
 350
 79351  expected_failure_flag = val
 79352End
 353
 354/// Return true if running in `ProcGlobal`, false otherwise
 17355static Function IsProcGlobal()
 356
 17357  return !cmpstr("ProcGlobal", GetIndependentModuleName())
 17358End
 359
 360/// Returns the full name of a function including its module
 361/// @param &err returns 0 for no error, 1 if function not found, 2 is static function in proc without ModuleName
 84362static Function/S getFullFunctionName(err, funcName, procName)
 363  variable &err
 364  string funcName, procName
 365
 84366  err = FFNAME_OK
 84367  string errMsg, module, infoStr, funcNameReturn
 368
 84369  infoStr = FunctionInfo(funcName, procName)
 370
 84371  if(IUTF_Utils#IsEmpty(infoStr))
 0372    sprintf errMsg, "Function %s in procedure file %s is unknown", funcName, procName
 0373    err = FFNAME_NOT_FOUND
 0374    return errMsg
 84375  endif
 376
 84377  funcNameReturn = StringByKey("NAME", infoStr)
 378
 84379  if(!cmpstr(StringByKey("SPECIAL", infoStr), "static"))
 84380    module = StringByKey("MODULE", infoStr)
 381
 84382    // we can only use static functions if they live in a module
 84383    if(IUTF_Utils#IsEmpty(module))
 0384      sprintf errMsg, "The procedure file %s is missing a \"#pragma ModuleName=myName\" declaration.", procName
 0385      err = FFNAME_NO_MODULE
 0386      return errMsg
 84387    endif
 388
 84389    funcNameReturn = module + "#" + funcNameReturn
 84390  endif
 391
 84392  // even if we are running in an independent module we don't need its name prepended as we
 84393  // 1.) run in the same IM anyway
 84394  // 2.) FuncRef does not accept that
 395
 84396  return funcNameReturn
 84397End
 398
 399/// Evaluates an RTE and puts a composite error message into message/type
 0400static Function EvaluateRTE(err, errmessage, abortCode, funcName, funcType, procWin)
 401  variable err
 402  string   errmessage
 403  variable abortCode, funcType
 404  string funcName
 405  string procWin
 406
 0407  DFREF  dfr     = GetPackageFolder()
 0408  string message = ""
 0409  string str, funcTypeString
 0410  variable i, length
 411
 0412  if(!err && !abortCode)
 0413    return NaN
 0414  endif
 415
 0416  switch(funcType)
 0417    case IUTF_TEST_CASE_TYPE:
 0418      funcTypeString = "test case"
 0419      break
 0420    case IUTF_USER_HOOK_TYPE:
 0421      funcTypeString = "user hook"
 0422      break
 0423    case IUTF_DATA_GEN_TYPE:
 0424      funcTypeString = "data generator"
 0425      break
 0426    default:
 0427      IUTF_Reporting#ReportErrorAndAbort("Unknown func type in EvaluateRTE")
 0428      break
 0429  endswitch
 430
 0431  if(err)
 0432    sprintf str, "Uncaught runtime error %d:\"%s\" in %s \"%s\" (%s)", err, errmessage, funcTypeString, funcName, procWi
 0433    IUTF_Reporting#AddFailedSummaryInfo(str)
 0434    IUTF_Reporting#AddError(str, IUTF_STATUS_ERROR)
 0435    message = str
 0436  endif
 0437  if(abortCode != -4)
 0438    str = ""
 0439    switch(abortCode)
 0440      case -1:
 0441        sprintf str, "User aborted Test Run manually in %s \"%s\" (%s)", funcTypeString, funcName, procWin
 0442        IUTF_Reporting#AddFailedSummaryInfo(str)
 0443        IUTF_Reporting#AddError(str, IUTF_STATUS_ERROR)
 0444        break
 0445      case -2:
 0446        sprintf str, "Stack Overflow in %s \"%s\" (%s)", funcTypeString, funcName, procWin
 0447        IUTF_Reporting#AddFailedSummaryInfo(str)
 0448        IUTF_Reporting#AddError(str, IUTF_STATUS_ERROR)
 0449        break
 0450      case -3:
 0451        sprintf str, "Encountered \"Abort\" in %s \"%s\" (%s)", funcTypeString, funcName, procWin
 0452        IUTF_Reporting#AddFailedSummaryInfo(str)
 0453        IUTF_Reporting#AddError(str, IUTF_STATUS_ERROR)
 0454        break
 0455      default:
 0456        break
 0457    endswitch
 0458    message += str
 0459    if(abortCode > 0)
 0460      sprintf str, "Encountered \"AbortOnValue\" Code %d in %s \"%s\" (%s)", abortCode, funcTypeString, funcName, procWi
 0461      IUTF_Reporting#AddFailedSummaryInfo(str)
 0462      IUTF_Reporting#AddError(str, IUTF_STATUS_ERROR)
 0463      message += str
 0464    endif
 0465  endif
 466
 0467  IUTF_Reporting#ReportError(message, incrGlobalErrorCounter = 0)
 0468  WAVE/T wvInfoMsg = IUTF_Reporting#GetInfoMsg()
 0469  length = IUTF_Utils_Vector#GetLength(wvInfoMsg)
 0470  for(i = 0; i < length; i += 1)
 0471    IUTF_Reporting#ReportError(wvInfoMsg[i], incrGlobalErrorCounter = 0)
 0472  endfor
 473
 0474  CheckAbortCondition(abortCode)
 0475End
 476
 477/// Check if the User manually pressed Abort and set Abort flag
 478///
 479/// @param abortCode V_AbortCode output from try...catch
 0480static Function CheckAbortCondition(abortCode)
 481  variable abortCode
 482
 0483  if(abortCode == -1)
 0484    setAbortFlag()
 0485  endif
 0486End
 487
 488/// Returns List of Test Functions in Procedure Window procWin
 27489static Function/S GetTestCaseList(procWin)
 490  string procWin
 491
 27492  string testCaseList   = FunctionList("!*_IGNORE", ";", "KIND:18,NPARAMS:0,VALTYPE:1,WIN:" + procWin)
 27493  string testCaseMDList = FunctionList("!*_IGNORE", ";", "KIND:18,NPARAMS:1,VALTYPE:1,WIN:" + procWin)
 494
 27495  testCaseList   = GrepList(testCaseList, PROCNAME_NOT_REENTRY)
 27496  testCaseMDList = GrepList(testCaseMDList, PROCNAME_NOT_REENTRY)
 497
 27498  if(!IUTF_Utils#IsEmpty(testCaseMDList))
 0499    testCaseList = testCaseList + testCaseMDList
 27500  endif
 501
 27502  return SortTestCaseList(procWin, testCaseList)
 27503End
 504
 505/// Returns the list of testcases sorted by line number
 27506static Function/S SortTestCaseList(procWin, testCaseList)
 507  string procWin, testCaseList
 508
 27509  if(IUTF_Utils#IsEmpty(testCaseList))
 0510    return ""
 27511  endif
 512
 27513  WAVE/T testCaseWave = ListToTextWave(testCaseList, ";")
 514
 27515  Make/FREE/N=(ItemsInList(testCaseList)) lineNumberWave
 27516  lineNumberWave[] = str2num(StringByKey("PROCLINE", FunctionInfo(testCaseWave[p], procWin)))
 517
 27518  Sort lineNumberWave, testCaseWave
 519
 27520  return IUTF_Utils#TextWaveToList(testCaseWave, ";")
 27521End
 522
 523/// @brief Checks if the procedure window has the global tag to ban shuffling test cases. This will
 524/// keep the test cases in their original order for this single procedure file only.
 525///
 526/// @param procWin The name of the procedure window. This can include independent module names.
 527///
 528/// @returns 1 if procedure file has the ban tag, 0 if not
 0529static Function HasProcedureShuffleBan(procWin)
 530  string procWin
 531
 0532  variable i, numLines
 0533  string fullText, line, tagValue
 534
 0535  fullText = ProcedureText("", 0, procWin + " [" + GetIndependentModuleName() + "]")
 0536  WAVE/T wv = ListToTextWave(fullText, "\r")
 0537  numLines = DimSize(wv, UTF_ROW)
 538
 0539  numLines = min(numLines, MAX_PROCTAGS_SCAN_LINES)
 0540  for(i = 0; i < numLines; i += 1)
 0541    line = wv[i]
 0542    if(IUTF_Utils#IsEmpty(line))
 0543      continue
 0544    endif
 545
 0546    if(IUTF_FunctionTags#IsTagMatch(IUTF_NO_SHUFFLE_TEST_CASE, line, tagValue))
 0547      return 1
 0548    endif
 0549  endfor
 550
 0551  return 0
 0552End
 553
 554/// @brief get test cases matching a certain pattern and fill TesRunSetup wave
 555///
 556/// This function searches for test cases in a given list of test suites. The
 557/// search can be performed either using a regular expression or on a defined
 558/// list of test cases. All Matches are checked.
 559/// The function returns an error
 560/// * If a given test case is not found
 561/// * If no test case was found
 562/// * if fullFunctionName returned an error
 563///
 564/// @param[in]  procWinList List of test suites, separated by ";"
 565/// @param[in]  matchStr    * List of test cases, separated by ";" (enableRegExp = 0)
 566///                         * *one* regular expression without ";" (enableRegExp = 1)
 567/// @param[in]  enableRegExp (0,1) defining the type of search for matchStr
 568/// @param[out] errMsg error message in case of error
 569/// @param[in]  enableTAP   Specify if TAP output is enabled or not
 570/// @param[in]  debugMode   The current debug mode
 571/// @param[in]  shuffleMode The current test shuffle mode
 572///
 573/// @returns Numeric Error Code
 6574static Function CreateTestRunSetup(procWinList, matchStr, enableRegExp, errMsg, enableTAP, debugMode, shuffleMode)
 575  string   procWinList
 576  string   matchStr
 577  variable enableRegExp
 578  string  &errMsg
 579  variable enableTAP, debugMode, shuffleMode
 580
 6581  string procWin
 6582  string funcName
 6583  string funcList
 6584  string fullFuncName, dgenList
 6585  string testCase, testCaseMatch
 6586  variable numTC, numpWL, numFL, markSkip
 6587  variable i, j, tdIndex
 6588  variable err     = TC_MATCH_OK
 6589  variable hasDGen = 0
 590
 6591  if(enableRegExp && !(strsearch(matchStr, ";", 0) < 0))
 0592    errMsg = "semicolon is not allowed in given regex pattern: " + matchStr
 0593    return TC_REGEX_INVALID
 6594  endif
 595
 6596  if(enableRegExp)
 6597    sprintf matchStr, "^(?i)%s$", matchStr
 6598  endif
 599
 6600  if(shuffleMode & IUTF_SHUFFLE_TEST_SUITES)
 0601    procWinList = IUTF_Utils_Strings#ShuffleList(procWinList)
 6602  endif
 603
 6604  WAVE/T testRunData = GetTestRunData()
 605
 6606  numTC  = ItemsInList(matchStr)
 6607  numpWL = ItemsInList(procWinList)
 6608  Make/FREE/N=(numTC) usedTC
 6609  for(i = 0; i < numpWL; i += 1)
 27610    procWin       = StringFromList(i, procWinList)
 27611    funcList      = getTestCaseList(procWin)
 27612    testCaseMatch = ""
 613
 27614    if(enableRegExp)
 27615      try
 27616        ClearRTError()
 27617        testCaseMatch = GrepList(funcList, matchStr, 0, ";"); AbortOnRTE
 27618      catch
 0619        testCaseMatch = ""
 0620        err           = GetRTError(1)
 0621        switch(err)
 0622          case 1233:
 0623            errMsg = "Regular expression error: " + matchStr
 0624            err    = TC_REGEX_INVALID
 0625            break
 0626          default:
 0627            errMsg = GetErrMessage(err)
 0628            err    = GREPLIST_ERROR
 0629        endswitch
 0630        sprintf errMsg, "Error executing GrepList: %s", errMsg
 0631        return err
 0632      endtry
 0633    else
 0634      for(j = 0; j < numTC; j += 1)
 0635        testCase = StringFromList(j, matchStr)
 0636        if(WhichListItem(testCase, funcList, ";", 0, 0) < 0)
 0637          continue
 0638        endif
 0639        testCaseMatch = AddListItem(testCase, testCaseMatch, ";", Inf)
 0640        usedTC[j]     = 1
 0641      endfor
 27642    endif
 643
 27644    if((shuffleMode & IUTF_SHUFFLE_TEST_CASES) && !HasProcedureShuffleBan(procWin))
 0645      testCaseMatch = IUTF_Utils_Strings#ShuffleList(testCaseMatch)
 27646    endif
 647
 27648    numFL = ItemsInList(testCaseMatch)
 27649    for(j = 0; j < numFL; j += 1)
 79650      funcName     = StringFromList(j, testCaseMatch)
 79651      fullFuncName = getFullFunctionName(err, funcName, procWin)
 79652      if(err)
 0653        sprintf errMsg, "Could not get full function name: %s", fullFuncName
 0654        return err
 79655      endif
 656
 79657      IUTF_FunctionTags#AddFunctionTagWave(fullFuncName)
 658
 79659      if(IUTF_Test_MD#GetDataGeneratorListTC(procWin, fullFuncName, dgenList))
 0660        continue
 79661      endif
 662
 79663      IUTF_Utils_Vector#EnsureCapacity(testRunData, tdIndex)
 79664      testRunData[tdIndex][%PROCWIN]      = procWin
 79665      testRunData[tdIndex][%TESTCASE]     = fullFuncName
 79666      testRunData[tdIndex][%FULLFUNCNAME] = fullFuncName
 79667      testRunData[tdIndex][%DGENLIST]     = dgenList
 79668      markSkip                            = IUTF_FunctionTags#HasFunctionTag(fullFuncName, UTF_FTAG_SKIP)
 79669      testRunData[tdIndex][%SKIP]         = SelectString(enableTAP, num2istr(markSkip), num2istr(IUTF_TAP#TAP_IsFunction
 79670      testRunData[tdIndex][%EXPECTFAIL]   = num2istr(IUTF_FunctionTags#HasFunctionTag(fullFuncName, UTF_FTAG_EXPECTED_FA
 79671      tdIndex                            += 1
 672
 79673      hasDGen = hasDGen | !IUTF_Utils#IsEmpty(dgenList)
 79674    endfor
 27675  endfor
 676
 6677  if(!enableRegExp)
 0678    for(i = 0; i < numTC; i += 1)
 0679      if(!usedTC[i])
 0680        testCase = StringFromList(i, matchStr)
 0681        sprintf errMsg, "Could not find test case \"%s\" in procedure list \"%s\".", testCase, procWinList
 0682        return TC_NOT_FOUND
 0683      endif
 0684    endfor
 6685  endif
 686
 6687  Redimension/N=(tdIndex, -1, -1, -1) testRunData
 688
 6689  if(hasDGen)
 0690    IUTF_Test_MD_Gen#ExecuteAllDataGenerators(debugMode)
 6691  endif
 692
 6693  for(i = 0; i < tdIndex; i += 1)
 79694    dgenList = testRunData[i][%DGENLIST]
 695
 79696    if(IUTF_Utils#IsEmpty(dgenList))
 79697      continue
 0698    endif
 699
 0700    procWin      = testRunData[i][%PROCWIN]
 0701    fullFuncName = testRunData[i][%FULLFUNCNAME]
 702
 0703    if(IUTF_Test_MD#CheckFunctionSignatureTC(procWin, fullFuncName, markSkip))
 0704      // There is something wrong which is already reported. The old approach was to remove
 0705      // this test case from the list which isn't possible anymore. So let's skip it safely.
 0706      testRunData[i][%SKIP] = "1"
 0707      continue
 0708    endif
 709
 0710    if(markSkip)
 0711      testRunData[i][%SKIP] = "1"
 0712    endif
 0713  endfor
 714
 6715  if(!tdIndex)
 0716    errMsg = "No test cases found."
 0717    return TC_LIST_EMPTY
 6718  endif
 719
 6720  return TC_MATCH_OK
 6721End
 722
 723/// Function determines the total number of test cases
 724/// Normal test cases are counted with 1
 725/// MD test cases are counted by multiplying all data generator wave sizes
 726/// When the optional string procWin is given then the number of test cases for that
 727/// procedure window (test suite) is returned.
 728/// Returns the total number of all test cases to be called
 6729static Function GetTestCaseCount([procWin])
 730  string procWin
 731
 6732  variable i, j, size, dgenSize, index
 6733  variable tcCount, dgenCount
 6734  string dgenList, dgen
 735
 6736  WAVE/WAVE dgenWaves   = IUTF_Test_MD_Gen#GetDataGeneratorWaves()
 6737  WAVE/T    testRunData = GetTestRunData()
 6738  size = DimSize(testRunData, UTF_ROW)
 6739  for(i = 0; i < size; i += 1)
 79740    if(!ParamIsDefault(procWin) && CmpStr(procWin, testRunData[i][%PROCWIN]))
 0741      continue
 79742    endif
 743
 79744    dgenCount = 1
 79745    dgenList  = testRunData[i][%DGENLIST]
 79746    dgenSize  = ItemsInList(dgenList)
 79747    for(j = 0; j < dgenSize; j += 1)
 0748      dgen  = StringFromList(j, dgenList)
 0749      index = IUTF_Test_MD_Gen#GetDataGeneratorRef(dgen)
 0750      WAVE wv = dgenWaves[index]
 0751      dgenCount *= DimSize(wv, UTF_ROW)
 0752    endfor
 79753    tcCount += dgenCount
 79754  endfor
 755
 6756  return tcCount
 6757End
 758
 759// Return the status of an `SetIgorOption` setting
 1760static Function QueryIgorOption(option)
 761  string option
 762
 1763  variable state
 764
 1765  Execute/Q "SetIgorOption " + option + "=?"
 1766  NVAR V_Flag
 767
 1768  state = V_Flag
 1769  KillVariables/Z V_Flag
 770
 1771  return state
 1772End
 773
 774/// Add an IM specification to every procedure name if running in an IM
 6775static Function/S AdaptProcWinList(procWinList, enableRegExp)
 776  string   procWinList
 777  variable enableRegExp
 778
 6779  variable i, numEntries
 6780  string str
 6781  string list = ""
 782
 6783  if(IsProcGlobal())
 5784    return procWinList
 1785  endif
 786
 1787  numEntries = ItemsInList(procWinList)
 1788  for(i = 0; i < numEntries; i += 1)
 1789    if(enableRegExp)
 1790      str = StringFromList(i, procWinList) + "[[:space:]]\[" + GetIndependentModuleName() + "\]"
 0791    else
 0792      str = StringFromList(i, procWinList) + " [" + GetIndependentModuleName() + "]"
 1793    endif
 1794    list = AddListItem(str, list, ";", Inf)
 1795  endfor
 796
 1797  return list
 6798End
 799
 800/// get all available procedures as a ";" separated list
 6801static Function/S GetProcedureList()
 802
 6803  string msg
 804
 6805  if(!IsProcGlobal())
 1806    if(!QueryIgorOption("IndependentModuleDev"))
 0807      sprintf msg, "Error: The universal testing framework lives in the IM \"%s\" but \"SetIgorOption IndependentModuleD
 0808      IUTF_Reporting#ReportError(msg)
 0809      return ""
 1810    endif
 1811    return WinList("* [" + GetIndependentModuleName() + "]", ";", "WIN:128,INDEPENDENTMODULE:1")
 5812  endif
 5813  return WinList("*", ";", "WIN:128")
 6814End
 815
 816/// verify that the selected procedures are available.
 817///
 818/// @param procWinList   a list of procedures to check
 819/// @param enableRegExp  treat list items as regular expressions
 820/// @returns parsed list of procedures
 6821static Function/S FindProcedures(procWinListIn, enableRegExp)
 822  string   procWinListIn
 823  variable enableRegExp
 824
 6825  string procWin
 6826  string procWinMatch
 6827  string allProcWindows
 6828  string errMsg, msg
 6829  variable numItemsPW
 6830  variable numMatches
 6831  variable err
 6832  variable i, j
 6833  string procWinListOut = ""
 834
 6835  numItemsPW = ItemsInList(procWinListIn)
 6836  if(numItemsPW <= 0)
 0837    return ""
 6838  endif
 839
 6840  allProcWindows = GetProcedureList()
 6841  numItemsPW     = ItemsInList(procWinListIn)
 6842  for(i = 0; i < numItemsPW; i += 1)
 6843    procWin = StringFromList(i, procWinListIn)
 6844    if(enableRegExp)
 6845      procWin = "^(?i)" + procWin + "$"
 6846      try
 6847        ClearRTError()
 6848        procWinMatch = GrepList(allProcWindows, procWin, 0, ";"); AbortOnRTE
 6849      catch
 0850        procWinMatch = ""
 0851        err          = GetRTError(1)
 0852        switch(err)
 0853          case 1233:
 0854            errMsg = "Regular expression error"
 0855            break
 0856          default:
 0857            errMsg = GetErrMessage(err)
 0858        endswitch
 0859        sprintf msg, "Error executing GrepList: %s", errMsg
 0860        IUTF_Reporting#ReportError(msg)
 0861      endtry
 0862    else
 0863      procWinMatch = StringFromList(WhichListItem(procWin, allProcWindows, ";", 0, 0), allProcWindows)
 6864    endif
 865
 6866    numMatches = ItemsInList(procWinMatch)
 6867    if(numMatches <= 0)
 0868      sprintf msg, "Error: A procedure window matching the pattern \"%s\" could not be found.", procWin
 0869      IUTF_Reporting#ReportError(msg)
 0870      return ""
 6871    endif
 872
 6873    for(j = 0; j < numMatches; j += 1)
 27874      procWin = StringFromList(j, procWinMatch)
 27875      if(FindListItem(procWin, procWinListOut, ";", 0, 0) == -1)
 27876        procWinListOut = AddListItem(procWin, procWinListOut, ";", Inf)
 0877      else
 0878        sprintf msg, "Error: The procedure window named \"%s\" is a duplicate entry in the input list of procedures.", p
 0879        IUTF_Reporting#ReportError(msg)
 0880        return ""
 27881      endif
 27882    endfor
 6883  endfor
 884
 6885  return procWinListOut
 6886End
 887
 888/// @copydoc BACKGROUND_MONITOR_DOCU
 889/// @deprecated use IUTFBackgroundMonitor instead
 0890Function UTFBackgroundMonitor(s)
 891  STRUCT WMBackgroundStruct &s
 892
 0893  IUTFBackgroundMonitor(s)
 0894End
 895
 896/// @class BACKGROUND_MONITOR_DOCU
 897/// @brief Background monitor of the Universal Testing Framework
 0898Function IUTFBackgroundMonitor(s)
 899  STRUCT WMBackgroundStruct &s
 900
 0901  variable i, numTasks, result, stopState
 0902  string task
 903
 0904  DFREF  df            = GetPackageFolder()
 0905  SVAR/Z tList         = df:BCKG_TaskList
 0906  SVAR/Z rFunc         = df:BCKG_ReentryFunc
 0907  NVAR/Z timeout       = df:BCKG_EndTime
 0908  NVAR/Z mode          = df:BCKG_Mode
 0909  NVAR/Z failOnTimeout = df:BCKG_failOnTimeout
 910
 0911  if(!SVAR_Exists(tList) || !SVAR_Exists(rFunc) || !NVAR_Exists(mode) || !NVAR_Exists(timeout) || !NVAR_Exists(failOnTim
 0912    IUTF_Reporting#ReportErrorAndAbort("IUTF BackgroundMonitor can not find monitoring data in package DF, aborting moni
 0913    ClearReentrytoIUTF()
 0914    QuitOnAutoRunFull()
 0915    return 2
 0916  endif
 917
 0918  if(mode == BACKGROUNDMONMODE_OR)
 0919    result = 0
 0920  elseif(mode == BACKGROUNDMONMODE_AND)
 0921    result = 1
 0922  else
 0923    IUTF_Reporting#ReportErrorAndAbort("Unknown mode set for background monitor", setFlagOnly = 1)
 0924    ClearReentrytoIUTF()
 0925    QuitOnAutoRunFull()
 0926    return 2
 0927  endif
 928
 0929  if(timeout && datetime > timeout)
 0930    IUTF_Reporting#ReportError("IUTF background monitor has reached the timeout for reentry", incrGlobalErrorCounter = f
 931
 0932    RunTest(BACKGROUNDINFOSTR)
 0933    return 0
 0934  endif
 935
 0936  numTasks = ItemsInList(tList)
 0937  for(i = 0; i < numTasks; i += 1)
 0938    task = StringFromList(i, tList)
 0939    CtrlNamedBackground $task, status
 0940    stopState = !NumberByKey("RUN", S_Info)
 0941    if(mode == BACKGROUNDMONMODE_OR)
 0942      result = result | stopState
 0943    elseif(mode == BACKGROUNDMONMODE_AND)
 0944      result = result & stopState
 0945    endif
 0946  endfor
 947
 0948  if(result)
 0949    RunTest(BACKGROUNDINFOSTR)
 0950  endif
 951
 0952  return 0
 0953End
 954
 955/// @brief Clear the glboal reentry flag, removes any saved RunTest state and stops the IUTF monitoring task
 15956static Function ClearReentrytoIUTF()
 957
 15958  ResetBckgRegistered()
 15959  KillDataFolder/Z $PKG_FOLDER_SAVE
 15960  CtrlNamedBackground $BACKGROUNDMONTASK, stop
 15961End
 962
 963/// @brief Saves the variable state of RunTest from a strRunTest structure to a dfr
 5964static Function SaveState(dfr, s)
 965  DFREF              dfr
 966  STRUCT strRunTest &s
 967
 5968  // save all local vars
 5969  string/G   dfr:SprocWinList      = s.procWinList
 5970  string/G   dfr:Sname             = s.name
 5971  string/G   dfr:StestCase         = s.testCase
 5972  variable/G dfr:SenableJU         = s.enableJU
 5973  variable/G dfr:SenableTAP        = s.enableTAP
 5974  variable/G dfr:SenableRegExp     = s.enableRegExp
 5975  variable/G dfr:SkeepDataFolder   = s.keepDataFolder
 5976  variable/G dfr:SenableRegExpTC   = s.enableRegExpTC
 5977  variable/G dfr:SenableRegExpTS   = s.enableRegExpTS
 5978  variable/G dfr:SdgenIndex        = s.dgenIndex
 5979  variable/G dfr:SdgenSize         = s.dgenSize
 5980  variable/G dfr:SmdMode           = s.mdMode
 5981  variable/G dfr:StracingEnabled   = s.tracingEnabled
 5982  variable/G dfr:ShtmlCreation     = s.htmlCreation
 5983  variable/G dfr:Sshuffle          = s.shuffle
 5984  variable/G dfr:Scobertura        = s.cobertura
 5985  string/G   dfr:ScoberturaSources = s.coberturaSources
 5986  string/G   dfr:ScoberturaOut     = s.coberturaOut
 5987  string/G   dfr:StcSuffix         = s.tcSuffix
 5988  variable/G dfr:SretryMode        = s.retryMode
 5989  variable/G dfr:SretryCount       = s.retryCount
 5990  variable/G dfr:SretryIndex       = s.retryIndex
 5991  variable/G dfr:SretryFailedProc  = s.retryFailedProc
 992
 5993  variable/G dfr:Si   = s.i
 5994  variable/G dfr:Serr = s.err
 5995  IUTF_Hooks#StoreHooks(dfr, s.hooks, "TH")
 5996  IUTF_Hooks#StoreHooks(dfr, s.procHooks, "PH")
 5997End
 998
 999/// @brief Restores the variable state of RunTest from dfr to a strRunTest structure
 101000static Function RestoreState(dfr, s)
 1001  DFREF              dfr
 1002  STRUCT strRunTest &s
 1003
 101004  SVAR str = dfr:SprocWinList
 101005  s.procWinList = str
 101006  SVAR str = dfr:Sname
 101007  s.name = str
 101008  SVAR str = dfr:StestCase
 101009  s.testCase = str
 101010  NVAR var = dfr:SenableJU
 101011  s.enableJU = var
 101012  NVAR var = dfr:SenableTAP
 101013  s.enableTAP = var
 101014  NVAR var = dfr:SenableRegExp
 101015  s.enableRegExp = var
 101016  NVAR var = dfr:SkeepDataFolder
 101017  s.keepDataFolder = var
 101018  NVAR var = dfr:SenableRegExpTC
 101019  s.enableRegExpTC = var
 101020  NVAR var = dfr:SenableRegExpTS
 101021  s.enableRegExpTS = var
 1022
 101023  NVAR var = dfr:SdgenIndex
 101024  s.dgenIndex = var
 101025  NVAR var = dfr:SdgenSize
 101026  s.dgenSize = var
 101027  NVAR var = dfr:SmdMode
 101028  s.mdMode = var
 101029  NVAR var = dfr:StracingEnabled
 101030  s.tracingEnabled = var
 101031  NVAR var = dfr:ShtmlCreation
 101032  s.htmlCreation = var
 101033  NVAR var = dfr:Sshuffle
 101034  s.shuffle = var
 101035  NVAR var = dfr:Scobertura
 101036  s.cobertura = var
 101037  SVAR str = dfr:ScoberturaSources
 101038  s.coberturaSources = str
 101039  SVAR str = dfr:ScoberturaOut
 101040  s.coberturaOut = str
 101041  SVAR str = dfr:StcSuffix
 101042  s.tcSuffix = str
 1043
 101044  NVAR var = dfr:SretryMode
 101045  s.retryMode = var
 101046  NVAR var = dfr:SretryCount
 101047  s.retryCount = var
 101048  NVAR var = dfr:SretryIndex
 101049  s.retryIndex = var
 101050  NVAR var = dfr:SretryFailedProc
 101051  s.retryFailedProc = var
 1052
 101053  NVAR var = dfr:Si
 101054  s.i = var
 101055  NVAR var = dfr:Serr
 101056  s.err = var
 1057
 101058  IUTF_Hooks#RestoreHooks(dfr, s.hooks, "TH")
 101059  IUTF_Hooks#RestoreHooks(dfr, s.procHooks, "PH")
 101060End
 1061
 941062static Function IsBckgRegistered()
 941063  DFREF  dfr            = GetPackageFolder()
 941064  NVAR/Z bckgRegistered = dfr:BCKG_Registered
 941065  return NVAR_Exists(bckgRegistered) && bckgRegistered == 1
 941066End
 1067
 251068static Function ResetBckgRegistered()
 251069  DFREF      dfr                 = GetPackageFolder()
 251070  variable/G dfr:BCKG_Registered = 0
 251071End
 1072
 841073static Function CallTestCase(s, reentry)
 1074  STRUCT strRunTest &s
 1075  variable           reentry
 1076
 841077  STRUCT IUTF_mData mData
 1078
 841079  variable wType0, wType1, wRefSubType, err, tcIndex, refIndex
 841080  string func, msg, dgenFuncName, origTCName, funcInfo
 1081
 841082  WAVE/T testRunData = GetTestRunData()
 841083  tcIndex = s.i
 1084
 841085  if(reentry)
 51086    DFREF  dfr      = GetPackageFolder()
 51087    NVAR/Z compMode = dfr:COMP_Mode
 1088
 51089    if(NVAR_Exists(compMode))
 51090      if(compMode == 1)
 41091        IUTF_Test_Compilation#TestCompilationReentry()
 41092        return NaN
 11093      else
 11094        KillVariables/Z dfr:COMP_Mode
 11095      endif
 11096    endif
 1097
 11098    SVAR reentryFuncName = dfr:BCKG_ReentryFunc
 11099    func = reentryFuncName
 1100
 11101    // Require only optional parameter
 11102    funcInfo = FunctionInfo(func)
 11103    if(NumberByKey("N_PARAMS", funcInfo) != NumberByKey("N_OPT_PARAMS", funcInfo))
 01104      sprintf msg, "Reentry functions require all its parameter as optional: \"%s\"", func
 01105      IUTF_Reporting#ReportErrorAndAbort(msg)
 11106    endif
 1107
 11108    sprintf msg, "Entering reentry \"%s\"", func
 11109    IUTF_Reporting#IUTF_PrintStatusMessage(msg)
 791110  else
 791111    func = testRunData[tcIndex][%FULLFUNCNAME]
 801112  endif
 1113
 801114  if(s.mdMode == TC_MODE_MD)
 1115
 01116    WAVE/WAVE dgenWaves = IUTF_Test_MD_Gen#GetDataGeneratorWaves()
 01117    dgenFuncName = StringFromList(0, testRunData[tcIndex][%DGENLIST])
 01118    refIndex     = IUTF_Test_MD_Gen#GetDataGeneratorRef(dgenFuncName)
 01119    WAVE wGenerator = dgenWaves[refIndex]
 01120    wType0 = WaveType(wGenerator)
 01121    wType1 = WaveType(wGenerator, 1)
 01122    if(wType1 == IUTF_WAVETYPE1_NUM)
 01123      if(wType0 & IUTF_WAVETYPE0_CMPL)
 1124
 01125        FUNCREF TEST_CASE_PROTO_MD_CMPL fTCMD_CMPL = $func
 01126        if(reentry && !IUTF_FuncRefIsAssigned(FuncRefInfo(fTCMD_CMPL)))
 01127          sprintf msg, "Reentry function %s does not meet required format for Complex argument.", func
 01128          IUTF_Reporting#ReportErrorAndAbort(msg)
 01129        endif
 01130        fTCMD_CMPL(cmpl = wGenerator[s.dgenIndex]); AbortOnRTE
 1131
 01132      elseif(wType0 & IUTF_WAVETYPE0_INT64)
 1133
 01134        FUNCREF TEST_CASE_PROTO_MD_INT fTCMD_INT = $func
 01135        if(reentry && !IUTF_FuncRefIsAssigned(FuncRefInfo(fTCMD_INT)))
 01136          sprintf msg, "Reentry function %s does not meet required format for INT64 argument.", func
 01137          IUTF_Reporting#ReportErrorAndAbort(msg)
 01138        endif
 01139        fTCMD_INT(int = wGenerator[s.dgenIndex]); AbortOnRTE
 1140
 01141      else
 1142
 01143        FUNCREF TEST_CASE_PROTO_MD_VAR fTCMD_VAR = $func
 01144        if(reentry && !IUTF_FuncRefIsAssigned(FuncRefInfo(fTCMD_VAR)))
 01145          sprintf msg, "Reentry function %s does not meet required format for numeric argument.", func
 01146          IUTF_Reporting#ReportErrorAndAbort(msg)
 01147        endif
 01148        fTCMD_VAR(var = wGenerator[s.dgenIndex]); AbortOnRTE
 1149
 01150      endif
 01151    elseif(wType1 == IUTF_WAVETYPE1_TEXT)
 1152
 01153      WAVE/T                         wGeneratorStr = wGenerator
 01154      FUNCREF TEST_CASE_PROTO_MD_STR fTCMD_STR     = $func
 01155      if(reentry && !IUTF_FuncRefIsAssigned(FuncRefInfo(fTCMD_STR)))
 01156        sprintf msg, "Reentry function %s does not meet required format for string argument.", func
 01157        IUTF_Reporting#ReportErrorAndAbort(msg)
 01158      endif
 01159      fTCMD_STR(str = wGeneratorStr[s.dgenIndex]); AbortOnRTE
 1160
 01161    elseif(wType1 == IUTF_WAVETYPE1_DFR)
 1162
 01163      WAVE/DF                        wGeneratorDF = wGenerator
 01164      FUNCREF TEST_CASE_PROTO_MD_DFR fTCMD_DFR    = $func
 01165      if(reentry && !IUTF_FuncRefIsAssigned(FuncRefInfo(fTCMD_DFR)))
 01166        sprintf msg, "Reentry function %s does not meet required format for data folder reference argument.", func
 01167        IUTF_Reporting#ReportErrorAndAbort(msg)
 01168      endif
 01169      fTCMD_DFR(dfr = wGeneratorDF[s.dgenIndex]); AbortOnRTE
 1170
 01171    elseif(wType1 == IUTF_WAVETYPE1_WREF)
 1172
 01173      WAVE/WAVE                     wGeneratorWV = wGenerator
 01174      FUNCREF TEST_CASE_PROTO_MD_WV fTCMD_WV     = $func
 01175      if(IUTF_FuncRefIsAssigned(FuncRefInfo(fTCMD_WV)))
 01176        fTCMD_WV(wv = wGeneratorWV[s.dgenIndex]); AbortOnRTE
 01177      else
 01178        wRefSubType = WaveType(wGeneratorWV[s.dgenIndex], 1)
 01179        if(wRefSubType == IUTF_WAVETYPE1_TEXT)
 01180          FUNCREF TEST_CASE_PROTO_MD_WVTEXT fTCMD_WVTEXT = $func
 01181          if(IUTF_FuncRefIsAssigned(FuncRefInfo(fTCMD_WVTEXT)))
 01182            fTCMD_WVTEXT(wv = wGeneratorWV[s.dgenIndex]); AbortOnRTE
 01183          else
 01184            err = 1
 01185          endif
 01186        elseif(wRefSubType == IUTF_WAVETYPE1_DFR)
 01187          FUNCREF TEST_CASE_PROTO_MD_WVDFREF fTCMD_WVDFREF = $func
 01188          if(IUTF_FuncRefIsAssigned(FuncRefInfo(fTCMD_WVDFREF)))
 01189            fTCMD_WVDFREF(wv = wGeneratorWV[s.dgenIndex]); AbortOnRTE
 01190          else
 01191            err = 1
 01192          endif
 01193        elseif(wRefSubType == IUTF_WAVETYPE1_WREF)
 01194          FUNCREF TEST_CASE_PROTO_MD_WVWAVEREF fTCMD_WVWAVEREF = $func
 01195          if(IUTF_FuncRefIsAssigned(FuncRefInfo(fTCMD_WVWAVEREF)))
 01196            fTCMD_WVWAVEREF(wv = wGeneratorWV[s.dgenIndex]); AbortOnRTE
 01197          else
 01198            err = 1
 01199          endif
 01200        else
 01201          sprintf msg, "Got wave reference wave from Data Generator %s with waves of unsupported type for reentry of tes
 01202          IUTF_Reporting#ReportErrorAndAbort(msg)
 01203        endif
 01204        if(err)
 01205          sprintf msg, "Reentry function %s does not meet required format for wave reference argument from data generato
 01206          IUTF_Reporting#ReportErrorAndAbort(msg)
 01207        endif
 01208      endif
 1209
 01210    endif
 801211  elseif(s.mdMode == TC_MODE_MMD)
 01212    origTCName = testRunData[tcIndex][%FULLFUNCNAME]
 01213    IUTF_Test_MD_MMD#SetupMMDStruct(mData, origTCName)
 01214    FUNCREF TEST_CASE_PROTO_MD fTCMD = $func
 01215    if(!IUTF_FuncRefIsAssigned(FuncRefInfo(fTCMD)))
 01216      sprintf msg, "Reentry function %s does not meet required format for multi-multi-data test case.", func
 01217      IUTF_Reporting#ReportErrorAndAbort(msg)
 01218    else
 01219      fTCMD(md = mData); AbortOnRTE
 01220    endif
 801221  elseif(s.mdMode == TC_MODE_NORMAL)
 801222    FUNCREF TEST_CASE_PROTO TestCaseFunc = $func
 801223    TestCaseFunc(); AbortOnRTE
 01224  else
 01225    sprintf msg, "Unknown test case mode for function %s.", func
 01226    IUTF_Reporting#ReportErrorAndAbort(msg)
 801227  endif
 841228End
 1229
 1230/// @brief initialize all strings in strRunTest structure to be non <null>
 101231static Function InitStrRunTest(s)
 1232  STRUCT strRunTest &s
 1233
 101234  s.procWinList = ""
 101235  s.name        = ""
 101236  s.testCase    = ""
 1237
 101238  s.coberturaSources = ""
 101239  s.coberturaOut     = ""
 101240  s.tcSuffix         = ""
 1241
 101242  IUTF_Hooks#InitHooks(s.hooks)
 101243  IUTF_Hooks#InitHooks(s.procHooks)
 101244End
 1245
 1246/// @brief this structure stores all local variables used in RunTest. It is used to store the complete function state.
 1247static Structure strRunTest
 1248  string procWinList
 1249  string name
 1250  string testCase
 1251  variable enableJU
 1252  variable enableTAP
 1253  variable enableRegExp
 1254  variable debugMode
 1255  variable keepDataFolder
 1256  variable enableRegExpTC
 1257  variable enableRegExpTS
 1258  variable dgenIndex
 1259  variable dgenSize
 1260  variable mdMode
 1261  variable tracingEnabled
 1262  variable htmlCreation
 1263  variable shuffle
 1264  variable cobertura
 1265  string coberturaSources
 1266  string coberturaOut
 1267  string tcSuffix
 1268  STRUCT IUTF_TestHooks hooks
 1269  STRUCT IUTF_TestHooks procHooks
 1270  variable retryMode
 1271  variable retryCount
 1272  variable retryIndex
 1273  variable retryFailedProc
 1274  variable i
 1275  variable err
 1276EndStructure
 1277
 1278///@endcond // HIDDEN_SYMBOL
 1279
 1280/// @copydoc REGISTER_IUTF_MONITOR_DOCU
 1281/// @deprecated use RegisterIUTFMonitor instead
 01282Function RegisterUTFMonitor(taskList, mode, reentryFunc, [timeout, failOnTimeout])
 1283  string   taskList
 1284  variable mode
 1285  string   reentryFunc
 1286  variable timeout, failOnTimeout
 1287
 01288  if(ParamIsDefault(timeout))
 01289    if(ParamIsDefault(failOnTimeout))
 01290      RegisterIUTFMonitor(taskList, mode, reentryFunc)
 01291    else
 01292      RegisterIUTFMonitor(taskList, mode, reentryFunc, failOnTimeout = failOnTimeout)
 01293    endif
 01294  else
 01295    if(ParamIsDefault(failOnTimeout))
 01296      RegisterIUTFMonitor(taskList, mode, reentryFunc, timeout = timeout)
 01297    else
 01298      RegisterIUTFMonitor(taskList, mode, reentryFunc, timeout = timeout, failOnTimeout = failOnTimeout)
 01299    endif
 01300  endif
 01301End
 1302
 1303/// @class REGISTER_IUTF_MONITOR_DOCU
 1304/// @brief Registers a background monitor for a list of other background tasks
 1305///
 1306/// @verbatim embed:rst:leading-slashes
 1307///     .. code-block:: igor
 1308///        :caption: usage example
 1309///
 1310///        RegisterIUTFMonitor("TestCaseTask1;TestCaseTask2", BACKGROUNDMONMODE_OR, \
 1311///                           "testcase_REENTRY", timeout = 60)
 1312///
 1313///     This command will register the IUTF background monitor task to monitor
 1314///     the state of `TestCaseTask1` and `TestCaseTask2`. As mode is set to
 1315///     `BACKGROUNDMONMODE_OR`, when `TestCaseTask1` OR `TestCaseTask2` has
 1316///     finished the function `testcase_REENTRY()` is called to  continue the
 1317///     current test case. The reentry function is also called if after 60 seconds
 1318///     both tasks are still running.
 1319///
 1320/// @endverbatim
 1321///
 1322/// @param   taskList      A list of background task names that should be monitored by the universal testing framework
 1323///                        @n The list should be given semicolon (";") separated.
 1324///
 1325/// @param   mode          Mode sets how multiple tasks are evaluated. If set to
 1326///                        `BACKGROUNDMONMODE_AND` all tasks of the list must finish (AND).
 1327///                        If set to `BACKGROUNDMONMODE_OR` one task of the list must finish (OR).
 1328///
 1329/// @param   reentryFunc   Name of the function that the universal testing framework calls when the monitored background
 1330///                        The function name must end with _REENTRY and it must be of the form `$fun_REENTRY()` (same fo
 1331///                        The reentry function *continues* the current test case therefore no hooks are called.
 1332///
 1333/// @param   timeout       (optional) default 0. Timeout in seconds that the background monitor waits for the test case 
 1334///                        A timeout of 0 equals no timeout. If the timeout is reached the registered reentry function i
 1335/// @param   failOnTimeout (optional) default to false. If the test case should be failed on reaching the timeout.
 01336Function RegisterIUTFMonitor(taskList, mode, reentryFunc, [timeout, failOnTimeout])
 1337  string   taskList
 1338  variable mode
 1339  string   reentryFunc
 1340  variable timeout, failOnTimeout
 1341
 01342  string procWinList, rFunc
 01343  variable tmpVar
 01344  DFREF dfr = GetPackageFolder()
 1345
 01346  if(ParamIsDefault(timeout))
 01347    timeout = 0
 01348  endif
 01349  timeout = timeout <= 0 ? 0 : datetime + timeout
 1350
 01351  failOnTimeout = ParamIsDefault(failOnTimeout) ? 0 : !!failOnTimeout
 1352
 01353  if(IUTF_Utils#IsEmpty(tasklist))
 01354    IUTF_Reporting#ReportErrorAndAbort("Tasklist is empty.")
 01355  endif
 1356
 01357  if(!(mode == BACKGROUNDMONMODE_OR || mode == BACKGROUNDMONMODE_AND))
 01358    IUTF_Reporting#ReportErrorAndAbort("Unknown mode set")
 01359  endif
 1360
 01361  if(FindListItem(BACKGROUNDMONTASK, taskList) != -1)
 01362    IUTF_Reporting#ReportErrorAndAbort("Igor Universal Testing framework will not monitor its own monitoring task (" + B
 01363  endif
 1364
 01365  // check valid reentry function
 01366  if(GrepString(reentryFunc, PROCNAME_NOT_REENTRY))
 01367    IUTF_Reporting#ReportErrorAndAbort("Name of Reentry function must end with _REENTRY")
 01368  endif
 01369  FUNCREF TEST_CASE_PROTO    rFuncRef    = $reentryFunc
 01370  FUNCREF TEST_CASE_PROTO_MD rFuncRefMMD = $reentryFunc
 01371  if(!IUTF_FuncRefIsAssigned(FuncRefInfo(rFuncRef)) && !IUTF_FuncRefIsAssigned(FuncRefInfo(rFuncRefMMD)) && !IUTF_Test_M
 01372    IUTF_Reporting#ReportErrorAndAbort("Specified reentry procedure has wrong format. The format must be function_REENTR
 01373  endif
 1374
 01375  string/G   dfr:BCKG_TaskList    = taskList
 01376  string/G   dfr:BCKG_ReentryFunc = reentryFunc
 01377  variable/G dfr:BCKG_Mode        = mode
 1378
 01379  variable/G dfr:BCKG_EndTime       = timeout
 01380  variable/G dfr:BCKG_Registered    = 1
 01381  variable/G dfr:BCKG_FailOnTimeout = failOnTimeout
 1382
 01383  CtrlNamedBackground $BACKGROUNDMONTASK, proc=IUTFBackgroundMonitor, period=10, start
 01384End
 1385
 1386/// @brief Unregisters the IUTF background monitor task
 01387Function UnRegisterIUTFMonitor()
 1388
 01389  DFREF      dfr                 = GetPackageFolder()
 01390  variable/G dfr:BCKG_Registered = 0
 1391
 01392  CtrlNamedBackground $BACKGROUNDMONTASK, stop
 01393End
 1394
 1395// Checks if a test case can be retried with the given conditions. Returns 1 if the test case can be
 1396// retried and 0 if not.
 791397static Function CanRetry(skip, s, fullFuncName, tcResultIndex)
 1398  variable skip, tcResultIndex
 1399  STRUCT strRunTest &s
 1400  string             fullFuncName
 1401
 791402  // if the test case is marked as skipped, the maximum retries are reached the test case will
 791403  // no longer be retried or if retry is not enabled
 791404  if(skip || s.retryIndex >= s.retryCount || !(s.retryMode & IUTF_RETRY_FAILED_UNTIL_PASS))
 791405    return 0
 01406  endif
 1407
 01408  // check if the test run should be aborted and IUTF_RETRY_REQUIRES is not set
 01409  if(!(s.retryMode & IUTF_RETRY_REQUIRES) && shouldDoAbort())
 01410    return 0
 01411  endif
 1412
 01413  // check if test case succeeded
 01414  WAVE/T wvTestCaseResults = IUTF_Reporting#GetTestCaseWave()
 01415  if(!CmpStr(wvTestCaseResults[tcResultIndex][%STATUS], IUTF_STATUS_SUCCESS))
 01416    return 0
 01417  endif
 1418
 01419  // check if function is not allowed to be retried
 01420  if(!((s.retryMode & IUTF_RETRY_MARK_ALL_AS_RETRY) || IUTF_FunctionTags#HasFunctionTag(fullFuncName, UTF_FTAG_RETRY_FAI
 01421    return 0
 01422  endif
 1423
 01424  // function has to be retried!
 01425  return 1
 791426End
 1427
 01428static Function CleanupRetry(s, tcResultIndex)
 1429  STRUCT strRunTest &s
 1430  variable           tcResultIndex
 1431
 01432  // increment retry counter
 01433  s.retryIndex += 1
 01434  // update test case status
 01435  WAVE/T wvTestCaseResults = IUTF_Reporting#GetTestCaseWave()
 01436  wvTestCaseResults[tcResultIndex][%STATUS] = IUTF_STATUS_RETRY
 01437  // remove errors from test suite
 01438  WAVE/T wvTestSuite = IUTF_Reporting#GetTestSuiteWave()
 01439  wvTestSuite[%CURRENT][%NUM_ERROR]        = num2istr(str2num(wvTestSuite[%CURRENT][%NUM_ERROR]) - 1)
 01440  wvTestSuite[%CURRENT][%NUM_ASSERT_ERROR] = num2istr(str2num(wvTestSuite[%CURRENT][%NUM_ASSERT_ERROR]) - str2num(wvTest
 01441  // cleanup test summary
 01442  WAVE/T wvFailedProc = IUTF_Reporting#GetFailedProcWave()
 01443  IUTF_Utils_Vector#SetLength(wvFailedProc, s.retryFailedProc)
 01444  IUTF_Utils_Waves#MoveDimLabel(wvFailedProc, UTF_ROW, "CURRENT", s.retryFailedProc - 1)
 01445  // cleanup abort flag to allow failed REQUIRE
 01446  InitAbortFlag()
 01447End
 1448
 51449static Function ClearTestSetupWaves()
 1450
 51451  WAVE/T    testRunData = GetTestRunData()
 51452  WAVE/WAVE dgenWaves   = IUTF_Test_MD_Gen#GetDataGeneratorWaves()
 51453  WAVE/T    dgenRefs    = IUTF_Test_MD_Gen#GetDataGeneratorRefs()
 51454  WAVE/WAVE ftagWaves   = IUTF_FunctionTags#GetFunctionTagWaves()
 51455  WAVE/WAVE ftagRefs    = IUTF_FunctionTags#GetFunctionTagRefs()
 51456  WAVE/WAVE mdState     = IUTF_Test_MD_MMD#GetMMDataState()
 51457  WAVE/T    mdStateRefs = IUTF_Test_MD_MMD#GetMMDataStateRefs()
 1458
 51459  KillWaves testRunData, dgenWaves, dgenRefs, ftagWaves, ftagRefs, mdState, mdStateRefs
 51460End
 1461
 1462/// @brief Detects if deprecated files are included and prompt a warning.
 51463static Function DetectDeprecation()
 51464  string text = ProcedureText("", 0, "unit-testing.ipf")
 51465  if(IUTF_Utils#IsEmpty(text))
 51466    return NaN
 01467  endif
 1468
 01469  IUTF_Reporting#IUTF_PrintStatusMessage("WARNING: You are using a deprecated method to include the Igor Pro Universal T
 01470  IUTF_Reporting#IUTF_PrintStatusMessage("WARNING: Search in your code for all")
 01471  IUTF_Reporting#IUTF_PrintStatusMessage("WARNING:     #include \"unit-testing\"")
 01472  IUTF_Reporting#IUTF_PrintStatusMessage("WARNING: and replace it with")
 01473  IUTF_Reporting#IUTF_PrintStatusMessage("WARNING:     #include \"igortest\"")
 01474  IUTF_Reporting#IUTF_PrintStatusMessage("WARNING: In a future release will this warning and the deprecated file removed
 01475  IUTF_Reporting#IUTF_PrintStatusMessage("", allowEmptyLine = 1)
 51476End
 1477
 1478/// @brief Main function to execute test suites with the universal testing framework.
 1479///
 1480/// You can abort the test run using Command-dot on Macintosh, Ctrl+Break on Windows or Shift+Escape
 1481/// on all platforms.
 1482///
 1483/// @verbatim embed:rst:leading-slashes
 1484///     .. code-block:: igor
 1485///        :caption: usage example
 1486///
 1487///        RunTest("proc0;proc1", name="myTest")
 1488///
 1489///     This command will run the test suites `proc0` and `proc1` in a test named `myTest`.
 1490/// @endverbatim
 1491///
 1492/// @param   procWinList    A list of procedure files that should be treated as test suites.
 1493///                         @n The list should be given semicolon (";") separated.
 1494///                         @n The procedure name must not include Independent Module specifications.
 1495///                         @n This parameter can be given as a regular expression with enableRegExp set to 1.
 1496///
 1497/// @param   name           (optional) default "Unnamed" @n
 1498///                         descriptive name for the executed test suites. This can be
 1499///                         used to group multiple test suites into a single test run.
 1500///
 1501/// @param   testCase       (optional) default ".*" (all test cases in the list of test suites) @n
 1502///                         function names, resembling test cases, which should be
 1503///                         executed in the given list of test suites (procWinList).
 1504///                         @n The list should be given semicolon (";") separated.
 1505///                         @n This parameter can be treated as a regular expression with enableRegExp set to 1.
 1506///
 1507/// @param   enableJU       (optional) default disabled, enabled when set to 1: @n
 1508///                         A JUNIT compatible XML file is written at the end of the Test Run.
 1509///                         It allows the combination of this framework with continuous integration
 1510///                         servers like Atlassian Bamboo/GitLab/etc.
 1511///                         The experiment is required to be saved somewhere on the disk. (it is okay to have unsaved ch
 1512///
 1513/// @param   enableTAP      (optional) default disabled, enabled when set to 1: @n
 1514///                         A TAP compatible file is written at the end of the test run.
 1515///                         @verbatim embed:rst:leading-slashes
 1516///                             `Test Anything Protocol (TAP) <https://testanything.org>`__
 1517///                             `standard 13 <https://testanything.org/tap-version-13-specification.html>`__
 1518///                         @endverbatim
 1519///                         The experiment is required to be saved somewhere on the disk. (it is okay to have unsaved ch
 1520///
 1521/// @param   enableRegExp   (optional) default disabled, enabled when set to 1: @n
 1522///                         The input for test suites (procWinList) and test cases (testCase) is
 1523///                         treated as a regular expression.
 1524///                         @verbatim embed:rst:leading-slashes
 1525///                             .. code-block:: igor
 1526///                                :caption: Example
 1527///
 1528///                                RunTest("example[1-3]-plain\\.ipf", enableRegExp=1)
 1529///
 1530///                             This command will run all test cases in the following test suites:
 1531///
 1532///                             * :ref:`example1-plain.ipf<example1>`
 1533///                             * :ref:`example2-plain.ipf<example2>`
 1534///                             * :ref:`example3-plain.ipf<example3>`
 1535///                         @endverbatim
 1536///
 1537/// @param   allowDebug     (optional) default disabled, enabled when set to 1: @n
 1538///                         The Igor debugger will be left in its current state when running the
 1539///                         tests. Is ignored when debugMode is also enabled.
 1540///
 1541/// @param  debugMode      (optional) default disabled, enabled when set to 1-15: @n
 1542///                         The Igor debugger will be turned on in the state: @n
 1543///                  1st bit = 1 (IUTF_DEBUG_ENABLE): Enable Debugger (only breakpoints) @n
 1544///                         2nd bit = 1 (IUTF_DEBUG_ON_ERROR): Debug on Error @n
 1545///                  3rd bit = 1 (IUTF_DEBUG_NVAR_SVAR_WAVE): Check NVAR SVAR WAVE @n
 1546///                         4th bit = 1 (IUTF_DEBUG_FAILED_ASSERTION): Debug on failed assertion
 1547///                         @verbatim embed:rst:leading-slashes
 1548///                             .. code-block:: igor
 1549///                                :caption: Example
 1550///
 1551///                                RunTest(..., debugMode = IUTF_DEBUG_ON_ERROR | IUTF_DEBUG_FAILED_ASSERTION)
 1552///
 1553///                             This will enable the debugger with Debug On Error and debugging on failed assertion.
 1554///                         @endverbatim
 1555///
 1556/// @param   keepDataFolder (optional) default disabled, enabled when set to 1: @n
 1557///                         The temporary data folder wherein each test case is executed is not
 1558///                         removed at the end of the test case. This allows to review the
 1559///                         produced data.
 1560///
 1561/// @param   traceWinList   (optional) default ""
 1562///                         A list of windows where execution gets traced. The universal testing framework saves a RTF d
 1563///                         for each traced procedure file. When REGEXP was set in traceOptions then traceWinList is als
 1564///                         as a regular expression.
 1565///                         The experiment is required to be saved somewhere on the disk. (it is okay to have unsaved ch
 1566///
 1567/// @param   traceOptions   (optional) default ""
 1568///                         A key:value pair list of additional tracing options. Currently supported is:
 1569///                         INSTRUMENTONLY:boolean When set, run instrumentation only and return. No tests are executed.
 1570///                         HTMLCREATION:boolean When set to zero, no htm result files are created at the end of the run
 1571///                         REGEXP:boolean When set, traceWinList is interpreted as regular expression
 1572///                         COBERTURA:boolean When set, it will export the tracing results in Cobertura format
 1573///                         COBERTURA_SOURCES:string A comma (,) delimited list of directory paths that should be used
 1574///                             as source paths for the procedure files. If this list is empty or this option not set it
 1575///                             will use the current home directory of this experiment as the source path for all proced
 1576///                             files.
 1577///                         COBERTURA_OUT:string The output directory to locate the generated cobertura files. The defau
 1578///                             is to use the current home directory.
 1579///
 1580/// @param   fixLogName     (optional) default 0 disabled, enabled when set to 1: @n
 1581///                         If enabled the output files that will be generated after an autorun will have predictable na
 1582///                         "IUTF_Test.log". If disabled the file names will always contain the name of the procedure fi
 1583///                         timestamp.
 1584///
 1585/// @param   waveTrackingMode (optional) default disabled, enabled when set to a value different than 0: @n
 1586///                         Monitors the number of free waves before and after a test case run. If for some reasons the 
 1587///                         the same as before this considered as an error. If you want to opt-out a single test case yo
 1588///                         it with IUTF_NO_WAVE_TRACKING.
 1589///                         This uses the flags UTF_WAVE_TRACKING_FREE, UTF_WAVE_TRACKING_LOCAL and UTF_WAVE_TRACKING_AL
 1590///                         This feature is only available since Igor Pro 9.
 1591///
 1592/// @param   retry          (optional) default IUTF_RETRY_NORETRY
 1593///                         Set the conditions and options when IUTF should retry a test case. The following flags are a
 1594///                         - IUTF_RETRY_FAILED_UNTIL_PASS: Reruns every failed flaky test up to retryMaxCount. A flaky 
 1595///                           the IUTF_RETRY_FAILED function tag.
 1596///                         - IUTF_RETRY_MARK_ALL_AS_RETRY: Treats all test cases as flaky. There is no need to use the 
 1597///                           function tag. This option does nothing if IUTF_RETRY_FAILED_UNTIL_PASS is not set.
 1598///                         - IUTF_RETRY_REQUIRES: Allow to retry failed REQUIRE assertions. This option does nothing if
 1599///                           IUTF_RETRY_FAILED_UNTIL_PASS is not set.
 1600///
 1601/// @param   retryMaxCount  (optional) default IUTF_MAX_SUPPORTED_RETRY
 1602///                         Sets the maximum number of retries if rerunning of flaky tests is enabled. Setting this numb
 1603///                         higher than IUTF_MAX_SUPPORTED_RETRY is not allowed.
 1604///
 1605/// @param   shuffle        (optional) default IUTF_SHUFFLE_NONE
 1606///                         A combination of flags which specify the current shuffle mode. Supported flags are:
 1607///                         IUTF_SHUFFLE_NONE: Shuffle nothing. Use a deterministic execution order.
 1608///                         IUTF_SHUFFLE_TEST_SUITES: Shuffle the order of execution of the test suites
 1609///                         IUTF_SHUFFLE_TEST_CASES: Shuffle the order of execution of the test cases inside the test su
 1610///                           You can opt-out single procedure files if you place the tag IUTF_NO_SHUFFLE_TEST_CASE some
 1611///                           specific files.
 1612///                         IUTF_SHUFFLE_ALL: A combination of IUTF_SHUFFLE_TEST_SUITES and IUTF_SHUFFLE_TEST_CASES
 1613///
 1614/// @return                 total number of errors
 101615Function RunTest(procWinList, [name, testCase, enableJU, enableTAP, enableRegExp, allowDebug, debugMode, keepDataFolder,
 1616  string procWinList, name, testCase
 1617  variable enableJU, enableTAP, enableRegExp
 1618  variable allowDebug, debugMode, keepDataFolder
 1619  string traceWinList, traceOptions
 1620  variable fixLogName
 1621  variable waveTrackingMode, retry, retryMaxCount, shuffle
 1622
 101623  // All variables that are needed to keep the local function state are wrapped in s
 101624  // new var/str must be added to strRunTest and added in SaveState/RestoreState functions
 101625  STRUCT strRunTest s
 101626  InitStrRunTest(s)
 1627
 101628  DFREF dfr = GetPackageFolder()
 1629
 101630  // do not save these for reentry
 101631  //
 101632  variable reentry
 101633  variable testSuiteCreated = 0
 101634  // these use a very local scope where used
 101635  // loop counter and loop end derived vars
 101636  variable i, j, tcFuncCount, startNextTS, skip, tcCount, reqSave, tcResultIndex
 101637  string procWin, fullFuncName, previousProcWin, dgenFuncName
 101638  // used as temporal locals
 101639  variable var, err
 101640  string msg, errMsg
 1641
 101642  fixLogName       = ParamIsDefault(fixLogName) ? 0 : !!fixLogName
 101643  waveTrackingMode = ParamIsDefault(waveTrackingMode) ? UTF_WAVE_TRACKING_NONE : waveTrackingMode
 1644
 101645  reentry = IsBckgRegistered()
 101646  ResetBckgRegistered()
 101647  if(reentry)
 1648
 51649    // check also if a saved state is existing
 51650    if(!DataFolderExists(PKG_FOLDER_SAVE))
 01651      IUTF_Reporting#ReportErrorAndAbort("No saved test state found, aborting. (Did you RegisterIUTFMonitor in an End Ho
 51652    endif
 51653    DFREF  dfr      = GetPackageFolder()
 51654    NVAR/Z compMode = dfr:COMP_Mode
 51655    // check if the reentry call originates from our own background monitor or compilation tester
 51656    if(!NVAR_Exists(compMode) && CmpStr(GetRTStackInfo(2), BACKGROUNDMONFUNC))
 01657      ClearReentrytoIUTF()
 01658      IUTF_Reporting#ReportErrorAndAbort("RunTest was called by user after background monitoring was registered. This is
 51659    endif
 1660
 51661    // a test suite must have been created if this is a reentry
 51662    testSuiteCreated = 1
 1663
 51664  else
 51665    // no early return/abort above this point
 51666    DetectDeprecation()
 51667    IUTF_Utils_Paths#ClearHomePath()
 51668    DFREF    dfr                       = GetPackageFolder()
 51669    string/G dfr:baseFilenameOverwrite = SelectString(fixLogName, "", FIXED_LOG_FILENAME)
 51670    ClearTestSetupWaves()
 51671    IUTF_Reporting#ClearTestResultWaves()
 51672    ClearBaseFilename()
 51673    CreateHistoryLog()
 51674    InitAbortFlag()
 51675    InitAbortFromSkipFlag()
 51676    IUTF_Reporting_Control#SetupTestRun()
 1677
 51678    allowDebug = ParamIsDefault(allowDebug) ? 0 : !!allowDebug
 1679
 51680    // transfer parameters to s. variables
 51681    s.enableRegExp   = enableRegExp
 51682    s.enableRegExpTC = ParamIsDefault(enableRegExp) ? 0 : !!enableRegExp
 51683    s.enableRegExpTS = s.enableRegExpTC
 51684    s.enableJU       = ParamIsDefault(enableJU) ? 0 : !!enableJU
 51685    s.enableTAP      = ParamIsDefault(enableTAP) ? 0 : !!enableTAP
 51686    s.debugMode      = ParamIsDefault(debugMode) ? 0 : debugMode
 51687    s.keepDataFolder = ParamIsDefault(keepDataFolder) ? 0 : !!keepDataFolder
 51688    s.retryMode      = ParamIsDefault(retry) ? IUTF_RETRY_NORETRY : retry
 51689    s.retryCount     = ParamIsDefault(retryMaxCount) ? IUTF_MAX_SUPPORTED_RETRY : retryMaxCount
 51690    s.shuffle        = ParamIsDefault(shuffle) ? IUTF_SHUFFLE_NONE : shuffle
 1691
 51692    s.tracingEnabled = !ParamIsDefault(traceWinList) && !IUTF_Utils#IsEmpty(traceWinList)
 1693
 51694    if(s.enableJU || s.enableTAP || s.tracingEnabled)
 51695      // the path is only needed locally
 51696      msg = IUTF_Utils_Paths#GetHomePath()
 51697      if(IUTF_Utils#IsEmpty(msg))
 01698        IUTF_Reporting#ReportError("Error: Please Save experiment first.")
 01699        return NaN
 51700      endif
 51701    endif
 1702
 51703    var = IUTF_DEBUG_ENABLE | IUTF_DEBUG_ON_ERROR | IUTF_DEBUG_NVAR_SVAR_WAVE | IUTF_DEBUG_FAILED_ASSERTION
 51704    if(s.debugMode > var || s.debugMode < 0 || !IUTF_Utils#IsInteger(s.debugMode))
 01705      sprintf msg, "debugMode can only be an integer between 0 and %d. The input %g is wrong, aborting!.\r", var, s.debu
 01706      msg = msg + "Use the constants IUTF_DEBUG_ENABLE, IUTF_DEBUG_ON_ERROR,\r"
 01707      msg = msg + "IUTF_DEBUG_NVAR_SVAR_WAVE and IUTF_DEBUG_FAILED_ASSERTION for debugMode.\r\r"
 01708      msg = msg + "Example: debugMode = IUTF_DEBUG_ON_ERROR | IUTF_DEBUG_NVAR_SVAR_WAVE"
 01709      IUTF_Reporting#ReportErrorAndAbort(msg)
 51710    endif
 1711
 51712    if(s.debugMode > 0 && allowDebug > 0)
 01713      print "Note: debugMode parameter is set, allowDebug parameter is ignored."
 51714    endif
 51715    if(s.debugMode == 0 && allowDebug > 0)
 01716      s.debugMode = IUTF_Debug#GetCurrentDebuggerState()
 51717    endif
 1718
 51719    if(shuffle & ~IUTF_SHUFFLE_ALL)
 01720      sprintf msg, "Invalid shuffle mode %d", shuffle
 01721      IUTF_Reporting#ReportErrorAndAbort(msg)
 51722    endif
 1723
 1724#if IgorVersion() < 9.00
 01725    if(waveTrackingMode)
 01726      IUTF_Reporting#ReportErrorAndAbort("Error: wave tracking is only allowed to be used in Igor Pro 9 or higher.")
 01727    else
 01728      variable/G dfr:waveTrackingMode = UTF_WAVE_TRACKING_NONE
 01729    endif
 1730#else
 51731    if((waveTrackingMode & UTF_WAVE_TRACKING_ALL) != waveTrackingMode)
 01732      sprintf msg, "Error: Invalid wave tracking mode %d", waveTrackingMode
 01733      IUTF_Reporting#ReportErrorAndAbort(msg)
 51734    endif
 51735    variable/G dfr:waveTrackingMode = waveTrackingMode
 1736#endif
 1737
 51738    if(s.retryMode & ~(IUTF_RETRY_FAILED_UNTIL_PASS | IUTF_RETRY_MARK_ALL_AS_RETRY | IUTF_RETRY_REQUIRES))
 01739      sprintf msg, "Error: Invalid retry mode %d", s.retryMode
 01740      IUTF_Reporting#ReportErrorAndAbort(msg)
 51741    endif
 1742
 51743    if(!IUTF_Utils#IsFinite(s.retryCount) || s.retryCount < 0 || s.retryCount > IUTF_MAX_SUPPORTED_RETRY)
 01744      sprintf msg, "Error: Invalid number of maximum retries: %d (maximum supported: %d)", s.retryCount, IUTF_MAX_SUPPOR
 01745      IUTF_Reporting#ReportErrorAndAbort(msg)
 51746    endif
 1747
 51748    traceOptions = SelectString(ParamIsDefault(traceOptions), traceOptions, "")
 1749
 51750    if(ParamIsDefault(name))
 51751      s.name = "Unnamed"
 01752    else
 01753      s.name = name
 51754    endif
 1755
 51756    if(ParamIsDefault(testCase))
 51757      s.testCase       = ".*"
 51758      s.enableRegExpTC = 1
 01759    else
 01760      s.testCase = testCase
 51761    endif
 51762    s.procWinList = procWinList
 1763
 51764    if(s.tracingEnabled)
 1765#ifdef UTF_ALLOW_TRACING
 51766      if(!CmpStr(traceWinList, IUTF_TRACE_REENTRY_KEYWORD))
 51767        DFREF dfSave = $PKG_FOLDER_SAVE
 51768        RestoreState(dfSave, s)
 51769        ClearReentrytoIUTF()
 01770      else
 01771        ClearReentrytoIUTF()
 1772
 01773        var            = NumberByKey(UTF_KEY_HTMLCREATION, traceOptions)
 01774        s.htmlCreation = IUTF_Utils#IsNaN(var) ? 1 : var
 1775
 01776        var                = NumberByKey(UTF_KEY_COBERTURA, traceOptions)
 01777        s.cobertura        = IUTF_Utils#IsNaN(var) ? 0 : !!var
 01778        s.coberturaSources = StringByKey(UTF_KEY_COBERTURA_SOURCES, traceOptions)
 01779        s.coberturaOut     = StringByKey(UTF_KEY_COBERTURA_OUT, traceOptions)
 1780
 01781        NewDataFolder $PKG_FOLDER_SAVE
 01782        DFREF dfSave = $PKG_FOLDER_SAVE
 01783        SaveState(dfSave, s)
 01784        TUFXOP_Init/N="IUTF_Testrun"
 01785        TUFXOP_Clear/Q/Z/N="IUTF_Error"
 01786        IUTF_Tracing#SetupTracing(traceWinList, traceOptions)
 01787        return NaN
 51788      endif
 1789#else
 01790      IUTF_Reporting#ReportErrorAndAbort("Tracing requires Igor Pro 9 Build 38812 (or later) and the Thread Utilities XO
 1791#endif // UTF_ALLOW_TRACING
 01792    else
 1793#ifdef UTF_ALLOW_TRACING
 01794      TUFXOP_Init/N="IUTF_Testrun"
 1795#endif // UTF_ALLOW_TRACING
 61796    endif
 1797
 61798    // below here use only s. variables to keep local state in struct
 1799
 61800    s.procWinList = AdaptProcWinList(s.procWinList, s.enableRegExpTS)
 61801    s.procWinList = FindProcedures(s.procWinList, s.enableRegExpTS)
 1802
 61803    if(ItemsInList(s.procWinList) <= 0)
 01804      IUTF_Reporting#ReportError("Error: The list of procedure windows is empty or invalid.")
 01805      return NaN
 61806    endif
 1807
 61808    err     = CreateTestRunSetup(s.procWinList, s.testCase, s.enableRegExpTC, errMsg, s.enableTAP, s.debugMode, shuffle)
 61809    tcCount = GetTestCaseCount()
 1810
 61811    if(err != TC_MATCH_OK)
 01812      if(err == TC_LIST_EMPTY)
 01813        errMsg = s.procWinList
 01814        errMsg = IUTF_Utils#IUTF_PrepareStringForOut(errMsg)
 01815        sprintf msg, "Error: A test case matching the pattern \"%s\" could not be found in test suite(s) \"%s\".", s.tes
 01816        IUTF_Reporting#ReportError(msg)
 01817        return NaN
 01818      endif
 1819
 01820      errMsg = IUTF_Utils#IUTF_PrepareStringForOut(errMsg)
 01821      sprintf msg, "Error %d in CreateTestRunSetup: %s", err, errMsg
 01822      IUTF_Reporting#ReportError(msg)
 01823      return NaN
 61824    endif
 1825
 61826    // 1.) set the hooks to the default implementations
 61827    IUTF_Hooks#setDefaultHooks(s.hooks)
 61828    // 2.) get global user hooks which reside in ProcGlobal and replace the default ones
 61829    IUTF_Hooks#getGlobalHooks(s.hooks)
 1830
 61831    // Reinitializes
 61832    IUTF_Hooks#ExecuteHooks(IUTF_TEST_BEGIN_CONST, s.hooks, s.enableTAP, s.enableJU, s.name, NO_SOURCE_PROCEDURE, s.i, p
 1833
 61834    // TAP Handling, find out if all should be skipped and number of all test cases
 61835    if(s.enableTAP)
 01836      if(IUTF_TAP#TAP_AreAllFunctionsSkip())
 01837        IUTF_Hooks#ExecuteHooks(IUTF_TEST_END_CONST, s.hooks, s.enableTAP, s.enableJU, s.name, NO_SOURCE_PROCEDURE, s.i,
 01838        return 0
 01839      endif
 61840    endif
 1841
 111842  endif
 1843
 111844  // The Test Run itself is split into Test Suites for each Procedure File
 111845  WAVE/WAVE dgenWaves   = IUTF_Test_MD_Gen#GetDataGeneratorWaves()
 111846  WAVE/T    testRunData = GetTestRunData()
 111847  tcFuncCount = DimSize(testRunData, UTF_ROW)
 111848  for(i = 0; i < tcFuncCount; i += 1)
 841849    s.i = i
 1850
 841851    procWin      = testRunData[i][%PROCWIN]
 841852    fullFuncName = testRunData[i][%FULLFUNCNAME]
 841853    if(s.i > 0)
 731854      previousProcWin = testRunData[s.i - 1][%PROCWIN]
 111855    else
 111856      previousProcWin = ""
 841857    endif
 841858    startNextTS = !!CmpStr(previousProcWin, procWin)
 1859
 841860    if(!reentry)
 1861
 791862      if(startNextTS)
 271863        if(i > 0)
 211864          s.procHooks = s.hooks
 211865          // 3.) get local user hooks which reside in the same Module as the requested procedure
 211866          IUTF_Hooks#getLocalHooks(s.procHooks, previousProcWin)
 211867          IUTF_Hooks#ExecuteHooks(IUTF_TEST_SUITE_END_CONST, s.procHooks, s.enableTAP, s.enableJU, previousProcWin, prev
 271868        endif
 1869
 271870        if(shouldDoAbort())
 01871          break
 271872        endif
 791873      endif
 1874
 791875      s.procHooks = s.hooks
 791876      // 3.) dito
 791877      IUTF_Hooks#getLocalHooks(s.procHooks, procWin)
 1878
 791879      if(startNextTS)
 271880        IUTF_Hooks#ExecuteHooks(IUTF_TEST_SUITE_BEGIN_CONST, s.procHooks, s.enableTAP, s.enableJU, procWin, procWin, s.i
 271881        testSuiteCreated = 1
 791882      endif
 1883
 791884      SetExpectedFailure(str2num(testRunData[s.i][%EXPECTFAIL]))
 791885      skip        = str2num(testRunData[s.i][%SKIP])
 791886      s.dgenIndex = 0
 791887      s.tcSuffix  = ""
 791888      FUNCREF TEST_CASE_PROTO    TestCaseFunc    = $fullFuncName
 791889      FUNCREF TEST_CASE_PROTO_MD TestCaseFuncMMD = $fullFuncName
 791890      if(IUTF_FuncRefIsAssigned(FuncRefInfo(TestCaseFunc)))
 791891        s.mdMode = TC_MODE_NORMAL
 01892      elseif(IUTF_FuncRefIsAssigned(FuncRefInfo(TestCaseFuncMMD)))
 01893        s.mdMode = TC_MODE_MMD
 01894      else
 01895        s.mdMode     = TC_MODE_MD
 01896        dgenFuncName = StringFromList(0, testRunData[s.i][%DGENLIST])
 01897        var          = IUTF_Test_MD_Gen#GetDataGeneratorRef(dgenFuncName)
 01898        WAVE wGenerator = dgenWaves[var]
 01899        s.dgenSize = DimSize(wGenerator, UTF_ROW)
 791900      endif
 1901
 841902    endif
 1903
 841904    s.retryIndex = 0
 1905
 841906    do
 1907
 841908      if(!reentry)
 1909
 791910        if(s.mdMode == TC_MODE_MD)
 01911          dgenFuncName = StringFromList(0, testRunData[s.i][%DGENLIST])
 01912          var          = IUTF_Test_MD_Gen#GetDataGeneratorRef(dgenFuncName)
 01913          WAVE wGenerator = dgenWaves[var]
 01914          s.tcSuffix = ":" + GetDimLabel(wGenerator, UTF_ROW, s.dgenIndex)
 01915          if(strlen(s.tcSuffix) == 1)
 01916            s.tcSuffix = IUTF_TC_SUFFIX_SEP + num2istr(s.dgenIndex)
 01917          endif
 791918        elseif(s.mdMode == TC_MODE_MMD)
 01919          s.tcSuffix = IUTF_Test_MD_MMD#GetMMDTCSuffix(i)
 791920        endif
 1921
 791922        IUTF_Hooks#ExecuteHooks(IUTF_TEST_CASE_BEGIN_CONST, s.procHooks, s.enableTAP, s.enableJU, fullFuncName + s.tcSuf
 51923      else
 1924
 51925        DFREF dfSave = $PKG_FOLDER_SAVE
 51926        RestoreState(dfSave, s)
 51927        // restore state done
 51928        DFREF dfSave = $""
 51929        ClearReentrytoIUTF()
 51930        // restore all loop counters and end loop locals
 51931        i            = s.i
 51932        procWin      = testRunData[s.i][%PROCWIN]
 51933        fullFuncName = testRunData[s.i][%FULLFUNCNAME]
 51934        skip         = str2num(testRunData[s.i][%SKIP])
 1935
 841936      endif
 1937
 841938      if(!skip)
 1939
 841940        WAVE/T wvFailedProc = IUTF_Reporting#GetFailedProcWave()
 841941        s.retryFailedProc = IUTF_Utils_Vector#GetLength(wvFailedProc)
 1942
 841943        if(GetRTError(0))
 01944          msg = GetRTErrMessage()
 01945          err = GetRTError(1)
 01946          sprintf msg, "Internal runtime error in IUTF %d:\"%s\" before executing test case \"%s\".", err, msg, fullFunc
 01947          IUTF_Reporting#ReportErrorAndAbort(msg, setFlagOnly = 1)
 841948        endif
 1949
 841950        try
 841951          CallTestCase(s, reentry)
 841952        catch
 01953          msg   = GetRTErrMessage()
 01954          s.err = GetRTError(1)
 01955          // clear the abort code from setAbortFlag()
 01956          V_AbortCode = shouldDoAbort() || IsAbortFromSkip() ? 0 : V_AbortCode
 01957          EvaluateRTE(s.err, msg, V_AbortCode, fullFuncName, IUTF_TEST_CASE_TYPE, procWin)
 1958
 01959          if(shouldDoAbort() && !(s.enableTAP && IUTF_TAP#TAP_IsFunctionTodo(fullFuncName)))
 01960            // check if a retry is possible
 01961            WAVE/T wvTestCaseResults = IUTF_Reporting#GetTestCaseWave()
 01962            tcResultIndex = FindDimLabel(wvTestCaseResults, UTF_ROW, "CURRENT")
 1963
 01964            if(CanRetry(skip, s, fullFuncName, tcResultIndex))
 01965              IUTF_Hooks#ExecuteHooks(IUTF_TEST_CASE_END_CONST, s.procHooks, s.enableTAP, s.enableJU, fullFuncName + s.t
 01966              CleanupRetry(s, tcResultIndex)
 01967              continue
 01968            endif
 1969
 01970            // abort condition is on hold while in catch/endtry, so all cleanup must happen here
 01971            IUTF_Hooks#ExecuteHooks(IUTF_TEST_CASE_END_CONST, s.procHooks, s.enableTAP, s.enableJU, fullFuncName + s.tcS
 1972
 01973            IUTF_Hooks#ExecuteHooks(IUTF_TEST_SUITE_END_CONST, s.procHooks, s.enableTAP, s.enableJU, procWin, procWin, s
 1974
 01975            IUTF_Hooks#ExecuteHooks(IUTF_TEST_END_CONST, s.hooks, s.enableTAP, s.enableJU, s.name, NO_SOURCE_PROCEDURE, 
 1976
 01977            ClearReentrytoIUTF()
 01978            QuitOnAutoRunFull()
 1979
 01980            WAVE/T wvTestRun = IUTF_Reporting#GetTestRunWave()
 01981            return str2num(wvTestRun[%CURRENT][%NUM_ERROR])
 01982          endif
 1983
 01984          InitAbortFromSkipFlag()
 01985        endtry
 1986
 1987#ifdef UTF_ALLOW_TRACING
 841988        // check if Z_ has stored some errors
 841989        if(s.tracingEnabled)
 751990          TUFXOP_GetStorage/Z/Q/N="IUTF_Error" wvAllStorage
 751991          if(!V_flag)
 01992            variable numThreads = NumberByKey("Index", note(wvAllStorage))
 01993            for(j = 0; j < numThreads; ++j)
 01994              WAVE/WAVE wvStorage = wvAllStorage[j]
 01995              WAVE/T    data      = wvStorage[0]
 01996              IUTF_Reporting#ReportError(data[0])
 01997            endfor
 751998          endif
 841999        endif
 2000#endif // UTF_ALLOW_TRACING
 2001
 842002      endif
 2003
 842004      reentry = 0
 2005
 842006      if(IsBckgRegistered())
 52007        // save state
 52008        NewDataFolder $PKG_FOLDER_SAVE
 52009        DFREF dfSave = $PKG_FOLDER_SAVE
 52010        SaveState(dfSave, s)
 2011
 52012        return RUNTEST_RET_BCKG
 792013      endif
 2014
 792015      WAVE/T wvTestCaseResults = IUTF_Reporting#GetTestCaseWave()
 792016      tcResultIndex = FindDimLabel(wvTestCaseResults, UTF_ROW, "CURRENT")
 2017
 792018      IUTF_Hooks#ExecuteHooks(IUTF_TEST_CASE_END_CONST, s.procHooks, s.enableTAP, s.enableJU, fullFuncName + s.tcSuffix,
 2019
 792020      if(CanRetry(skip, s, fullFuncName, tcResultIndex))
 02021        CleanupRetry(s, tcResultIndex)
 02022        // retry the the current test case. If this is a multi-data test case or
 02023        // multi-multi-data test case it will retry the current index which failed and not
 02024        // all previous runs.
 02025        continue
 792026      else
 792027        s.retryIndex = 0
 792028      endif
 2029
 792030      if(shouldDoAbort())
 02031        break
 792032      endif
 2033
 792034      if(s.mdMode == TC_MODE_MD)
 02035        s.dgenIndex += 1
 792036      elseif(s.mdMode == TC_MODE_MMD)
 02037        s.dgenIndex = IUTF_Test_MD_MMD#IncreaseMMDIndices(fullFuncName)
 792038      endif
 2039
 792040    while((s.mdMode == TC_MODE_MD && s.dgenIndex < s.dgenSize) || (s.mdMode == TC_MODE_MMD && !s.dgenIndex))
 2041
 792042    if(shouldDoAbort())
 02043      break
 792044    endif
 2045
 792046  endfor
 2047
 62048  // at this code path it is unclear if a test suite was ever started, so we have to check this manually
 62049  if(testSuiteCreated)
 62050    IUTF_Hooks#ExecuteHooks(IUTF_TEST_SUITE_END_CONST, s.procHooks, s.enableTAP, s.enableJU, procWin, procWin, s.i)
 62051  endif
 62052  IUTF_Hooks#ExecuteHooks(IUTF_TEST_END_CONST, s.hooks, s.enableTAP, s.enableJU, s.name, NO_SOURCE_PROCEDURE, s.i, param
 2053
 52054  ClearReentrytoIUTF()
 2055
 2056#ifdef UTF_ALLOW_TRACING
 52057  if(s.htmlCreation)
 02058    IUTF_Tracing#AnalyzeTracingResult()
 52059  endif
 52060  if(s.cobertura)
 52061    IUTF_Tracing_Cobertura#PrintReport(s.coberturaSources, s.coberturaOut)
 02062  endif
 2063#endif // UTF_ALLOW_TRACING
 2064
 02065  QuitOnAutoRunFull()
 2066
 02067  WAVE/T wvTestRun = IUTF_Reporting#GetTestRunWave()
 02068  return str2num(wvTestRun[%CURRENT][%NUM_ERROR])
 102069End