summaryrefslogtreecommitdiffstats
path: root/wizards/source/sfunittests
diff options
context:
space:
mode:
Diffstat (limited to 'wizards/source/sfunittests')
-rw-r--r--wizards/source/sfunittests/SF_Register.xba202
-rw-r--r--wizards/source/sfunittests/SF_UnitTest.xba1818
-rw-r--r--wizards/source/sfunittests/__License.xba26
-rw-r--r--wizards/source/sfunittests/dialog.xlb3
-rw-r--r--wizards/source/sfunittests/script.xlb7
5 files changed, 2056 insertions, 0 deletions
diff --git a/wizards/source/sfunittests/SF_Register.xba b/wizards/source/sfunittests/SF_Register.xba
new file mode 100644
index 000000000..360abba50
--- /dev/null
+++ b/wizards/source/sfunittests/SF_Register.xba
@@ -0,0 +1,202 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE script:module PUBLIC "-//OpenOffice.org//DTD OfficeDocument 1.0//EN" "module.dtd">
+<script:module xmlns:script="http://openoffice.org/2000/script" script:name="SF_Register" script:language="StarBasic" script:moduleType="normal">REM =======================================================================================================================
+REM === The ScriptForge library and its associated libraries are part of the LibreOffice project. ===
+REM === The SFUnitTests library is one of the associated libraries. ===
+REM === Full documentation is available on https://help.libreoffice.org/ ===
+REM =======================================================================================================================
+
+Option Compatible
+Option Explicit
+
+&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;
+&apos;&apos;&apos; SF_Register
+&apos;&apos;&apos; ===========
+&apos;&apos;&apos; The ScriptForge framework includes
+&apos;&apos;&apos; the master ScriptForge library
+&apos;&apos;&apos; a number of &quot;associated&quot; libraries SF*
+&apos;&apos;&apos; any user/contributor extension wanting to fit into the framework
+&apos;&apos;&apos;
+&apos;&apos;&apos; The main methods in this module allow the current library to cling to ScriptForge
+&apos;&apos;&apos; - RegisterScriptServices
+&apos;&apos;&apos; Register the list of services implemented by the current library
+&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;
+
+REM ================================================================== EXCEPTIONS
+
+Private Const UNITTESTLIBRARYERROR = &quot;UNITTESTLIBRARYERROR&quot;
+
+REM ============================================================== PUBLIC METHODS
+
+REM -----------------------------------------------------------------------------
+Public Sub RegisterScriptServices() As Variant
+&apos;&apos;&apos; Register into ScriptForge the list of the services implemented by the current library
+&apos;&apos;&apos; Each library pertaining to the framework must implement its own version of this method
+&apos;&apos;&apos;
+&apos;&apos;&apos; It consists in successive calls to the RegisterService() and RegisterEventManager() methods
+&apos;&apos;&apos; with 2 arguments:
+&apos;&apos;&apos; ServiceName: the name of the service as a case-insensitive string
+&apos;&apos;&apos; ServiceReference: the reference as an object
+&apos;&apos;&apos; If the reference refers to a module, then return the module as an object:
+&apos;&apos;&apos; GlobalScope.Library.Module
+&apos;&apos;&apos; If the reference is a class instance, then return a string referring to the method
+&apos;&apos;&apos; containing the New statement creating the instance
+&apos;&apos;&apos; &quot;libraryname.modulename.function&quot;
+
+ With GlobalScope.ScriptForge.SF_Services
+ .RegisterService(&quot;UnitTest&quot;, &quot;SFUnitTests.SF_Register._NewUnitTest&quot;) &apos; Reference to the function initializing the service
+ End With
+
+End Sub &apos; SFUnitTests.SF_Register.RegisterScriptServices
+
+REM =========================================================== PRIVATE FUNCTIONS
+
+REM -----------------------------------------------------------------------------
+Public Function _NewUnitTest(Optional ByVal pvArgs As Variant) As Object
+&apos;&apos;&apos; Create a new instance of the SF_UnitTest class
+&apos; Args:
+&apos;&apos;&apos; Location: if empty, the location of the library is presumed to be in GlobalScope.BasicLibraries
+&apos;&apos;&apos; Alternatives are:
+&apos;&apos;&apos; - the name of a document: see SF_UI.WindowName
+&apos;&apos;&apos; - an explicit SFDocuments.Document instance
+&apos;&apos;&apos; - the component containing the library, typically ThisComponent
+&apos;&apos;&apos; LibraryName: the name of the library containing the test code
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; The instance or Nothing
+&apos;&apos;&apos; Exceptions:
+&apos;&apos;&apos; UNITTESTLIBRARYNOTFOUND The library could not be found
+
+Dim oUnitTest As Object &apos; Return value
+Dim vLocation As Variant &apos; Alias of pvArgs(0)
+Dim vLibraryName As Variant &apos; alias of pvArgs(1)
+Dim vLocations As Variant &apos; &quot;user&quot;, &quot;share&quot; or document
+Dim sLocation As String &apos; A single location
+Dim sTargetLocation As String &apos; &quot;user&quot; or the document name
+Dim vLanguages As Variant &apos; &quot;Basic&quot;, &quot;Python&quot;, ... programming languages
+Dim sLanguage As String &apos; A single programming language
+Dim vLibraries As Variant &apos; Library names
+Dim sLibrary As String &apos; A single library
+Dim vModules As Variant &apos; Module names
+Dim sModule As String &apos; A single module
+Dim vModuleNames As Variant &apos; Module names
+Dim oRoot As Object &apos; com.sun.star.script.browse.BrowseNodeFactory
+Dim iLibrary As Integer &apos; The index of the target location in vLibraries
+
+Dim FSO As Object &apos; SF_FileSystem
+Dim i As Integer, j As Integer, k As Integer, l As Integer
+
+Const cstService = &quot;SFUnitTests.UnitTest&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+
+Check:
+ If IsMissing(pvArgs) Or IsEmpty(pvArgs) Then pvArgs = Array()
+ If UBound(pvArgs) &gt;= 0 Then vLocation = pvArgs(0) Else vLocation = &quot;&quot;
+ If IsEmpty(vLocation) Then vLocation = &quot;&quot;
+ If UBound(pvArgs) &gt;= 1 Then vLibraryName = pvArgs(1) Else vLibraryName = &quot;&quot;
+ If IsEmpty(vLibraryName) Then vLibraryName = &quot;&quot;
+ If Not ScriptForge.SF_Utils._Validate(vLocation, &quot;Location&quot;, Array(V_STRING, ScriptForge.V_OBJECT)) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(vLibraryName, &quot;LibraryName&quot;, V_STRING) Then GoTo Finally
+
+ Set oUnitTest = Nothing
+ Set FSO = CreateScriptService(&quot;ScriptForge.FileSystem&quot;)
+
+ &apos; Determine the library container hosting the test code
+
+ &apos; Browsing starts from root element
+ Set oRoot = SF_Utils._GetUNOService(&quot;BrowseNodeFactory&quot;).createView(com.sun.star.script.browse.BrowseNodeFactoryViewTypes.MACROORGANIZER)
+
+ If Len(vLibraryName) &gt; 0 Then
+
+ &apos; Determine the target location, as a string. The location is either:
+ &apos; - the last component of a document&apos;s file name
+ &apos; - &quot;user&quot; = My Macros &amp; Dialogs
+ If VarType(vLocation) = ScriptForge.V_OBJECT Then
+ sTargetLocation = FSO.GetName(vLocation.URL)
+ ElseIf Len(vLocation) = 0 Then
+ sTargetLocation = &quot;user&quot; &apos; Testing code is presumed NOT in &quot;share&quot;
+ Else
+ sTargetLocation = FSO.GetName(vLocation)
+ End If
+
+ &apos; Exploration is done via tree nodes
+ iLibrary = -1
+ If Not IsNull(oRoot) Then
+ If oRoot.hasChildNodes() Then
+ vLocations = oRoot.getChildNodes()
+ For i = 0 To UBound(vLocations)
+ sLocation = vLocations(i).getName()
+ If sLocation = sTargetLocation Then
+ If vLocations(i).hasChildNodes() Then
+ vLanguages = vLocations(i).getChildNodes()
+ For j = 0 To UBound(vLanguages)
+ sLanguage = vLanguages(j).getName()
+ &apos; Consider Basic libraries only
+ If sLanguage = &quot;Basic&quot; Then
+ If vLanguages(j).hasChildNodes() Then
+ vLibraries = vLanguages(j).getChildNodes()
+ For k = 0 To UBound(vLibraries)
+ sLibrary = vLibraries(k).getName()
+ &apos; Consider the targeted library only
+ If sLibrary = vLibraryName Then
+ iLibrary = k
+ If vLibraries(k).hasChildNodes() Then
+ vModules = vLibraries(k).getChildNodes()
+ vModuleNames = Array()
+ For l = 0 To UBound(vModules)
+ sModule = vModules(l).getName()
+ vModuleNames = ScriptForge.SF_Array.Append(vModuleNames, sModule)
+ Next l
+ End If
+ Exit For
+ End If
+ Next k
+ End If
+ End If
+ If iLibrary &gt;= 0 Then Exit For
+ Next j
+ End If
+ End If
+ If iLibrary &gt;= 0 Then Exit For
+ Next i
+ End If
+ End If
+ If iLibrary &lt; 0 Then GoTo CatchLibrary
+
+ End If
+
+Try:
+ &apos; Create the unittest Basic object and initialize its attributes
+ Set oUnitTest = New SF_UnitTest
+ With oUnitTest
+ Set .[Me] = oUnitTest
+ If Len(vLibraryName) &gt; 0 Then
+ .LibrariesContainer = sTargetLocation
+ .Scope = Iif(sTargetLocation = &quot;user&quot;, &quot;application&quot;, &quot;document&quot;)
+ .Libraries = vLibraries
+ .LibraryName = sLibrary
+ .LibraryIndex = iLibrary
+ .Modules = vModules
+ .ModuleNames = vModuleNames
+ ._ExecutionMode = .FULLMODE
+ ._WhenAssertionFails = .FAILSTOPSUITE
+ &apos; Launch the test timer
+ .TestTimer = CreateScriptService(&quot;ScriptForge.Timer&quot;, True)
+ Else
+ ._ExecutionMode = .SIMPLEMODE
+ ._WhenAssertionFails = .FAILIMMEDIATESTOP
+ End If
+ End With
+
+Finally:
+ Set _NewUnitTest = oUnitTest
+ Exit Function
+Catch:
+ GoTo Finally
+CatchLibrary:
+ ScriptForge.SF_Exception.RaiseFatal(UNITTESTLIBRARYERROR, vLibraryName)
+ GoTo Finally
+End Function &apos; SFUnitTests.SF_Register._NewUnitTest
+
+REM ============================================== END OF SFUNITTESTS.SF_REGISTER
+</script:module> \ No newline at end of file
diff --git a/wizards/source/sfunittests/SF_UnitTest.xba b/wizards/source/sfunittests/SF_UnitTest.xba
new file mode 100644
index 000000000..5007fb6a7
--- /dev/null
+++ b/wizards/source/sfunittests/SF_UnitTest.xba
@@ -0,0 +1,1818 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE script:module PUBLIC "-//OpenOffice.org//DTD OfficeDocument 1.0//EN" "module.dtd">
+<script:module xmlns:script="http://openoffice.org/2000/script" script:name="SF_UnitTest" script:language="StarBasic" script:moduleType="normal">REM =======================================================================================================================
+REM === The ScriptForge library and its associated libraries are part of the LibreOffice project. ===
+REM === Full documentation is available on https://help.libreoffice.org/ ===
+REM =======================================================================================================================
+
+Option Compatible
+Option ClassModule
+
+Option Explicit
+
+&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;
+&apos;&apos;&apos; SF_UnitTest
+&apos;&apos;&apos; ===========
+&apos;&apos;&apos; Class providing a framework to execute and check sets of unit tests.
+&apos;&apos;&apos;
+&apos;&apos;&apos; The UnitTest unit testing framework was originally inspired by unittest.py in Python
+&apos;&apos;&apos; and has a similar flavor as major unit testing frameworks in other languages.
+&apos;&apos;&apos;
+&apos;&apos;&apos; It supports test automation, sharing of setup and shutdown code for tests,
+&apos;&apos;&apos; aggregation of tests into collections.
+&apos;&apos;&apos;
+&apos;&apos;&apos; Both the
+&apos;&apos;&apos; - code describing the unit tests
+&apos;&apos;&apos; - code to be tested
+&apos;&apos;&apos; must be written exclusively in Basic (the code might call functions written in other languages).
+&apos;&apos;&apos; Even if either code may be contained in the same module, a much better practice is to
+&apos;&apos;&apos; store them in separate libraries.
+&apos;&apos;&apos; Typically:
+&apos;&apos;&apos; - in a same document when the code to be tested is contained in that document
+&apos;&apos;&apos; - either in a &quot;test&quot; document or in a &quot;My Macros&quot; library when the code
+&apos;&apos;&apos; to be tested is a shared library (My Macros or LibreOffice Macros).
+&apos;&apos;&apos; The code to be tested may be released as an extension. It does not need to make
+&apos;&apos;&apos; use of ScriptForge services in any way.
+&apos;&apos;&apos;
+&apos;&apos;&apos; The test reporting device is the Console. Read about the console in the ScriptForge.Exception service.
+&apos;&apos;&apos;
+&apos;&apos;&apos; Definitions:
+&apos;&apos;&apos; - Test Case
+&apos;&apos;&apos; A test case is the individual unit of testing.
+&apos;&apos;&apos; It checks for a specific response to a particular set of inputs.
+&apos;&apos;&apos; A test case in the UnitTest service is represented by a Basic Sub.
+&apos;&apos;&apos; The name of the Sub starts conventionally with &quot;Test_&quot;.
+&apos;&apos;&apos; The test fails if one of the included AssertXXX methods returns False
+&apos;&apos;&apos; - Test Suite
+&apos;&apos;&apos; A test suite is a collection of test cases that should be executed together.
+&apos;&apos;&apos; A test suite is represented by a Basic module.
+&apos;&apos;&apos; A suite may include the tasks needed to prepare one or more tests, and any associated cleanup actions.
+&apos;&apos;&apos; This may involve, for example, creating temporary files or directories, opening a document, loading libraries.
+&apos;&apos;&apos; Conventionally those tasks are part pf the SetUp&apos;) and TearDown() methods.
+&apos;&apos;&apos; - Unit test
+&apos;&apos;&apos; A full unit test is a set of test suites (each suite in a separate Basic module),
+&apos;&apos;&apos; each of them being a set of test cases (each case is located in a separate Basic Sub).
+&apos;&apos;&apos;
+&apos;&apos;&apos; Two modes:
+&apos;&apos;&apos; Beside the normal mode (&quot;full mode&quot;), using test suites and test cases, a second mode exists, called &quot;simple mode&quot;
+&apos;&apos;&apos; limited to the use exclusively of the Assert...() methods.
+&apos;&apos;&apos; Their boolean returned value may support the execution of limited unit tests.
+&apos;&apos;&apos;
+&apos;&apos;&apos; Service invocation examples:
+&apos;&apos;&apos; In full mode, the service creation is external to test cases
+&apos;&apos;&apos; Dim myUnitTest As Variant
+&apos;&apos;&apos; myUnitTest = CreateScriptService(&quot;UnitTest&quot;, ThisComponent, &quot;Tests&quot;)
+&apos;&apos;&apos; &apos; Test code is in the library &quot;Tests&quot; located in the current document
+&apos;&apos;&apos; In simple mode, the service creation is internal to every test case
+&apos;&apos;&apos; Dim myUnitTest As Variant
+&apos;&apos;&apos; myUnitTest = CreateScriptService(&quot;UnitTest&quot;)
+&apos;&apos;&apos; With myUnitTest
+&apos;&apos;&apos; If Not .AssertTrue(...) Then ... &apos; Only calls to the Assert...() methods are allowed
+&apos;&apos;&apos; &apos; ...
+&apos;&apos;&apos; .Dispose()
+&apos;&apos;&apos; End With
+&apos;&apos;&apos;
+&apos;&apos;&apos; Minimalist full mode example
+&apos;&apos;&apos; Code to be tested (stored in library &quot;Standard&quot; of document &quot;MyDoc.ods&quot;) :
+&apos;&apos;&apos; Function ArraySize(arr As Variant) As Long
+&apos;&apos;&apos; If IsArray(arr) Then ArraySize = UBound(arr) - LBound(arr) + 1 Else ArraySize = -1
+&apos;&apos;&apos; End Function
+&apos;&apos;&apos; Test code (stored in module &quot;AllTests&quot; of library &quot;Tests&quot; of document &quot;MyDoc.ods&quot;) :
+&apos;&apos;&apos; Sub Main() &apos; Sub to trigger manually, f.i. from the Tools + Run Macro tabbed bar
+&apos;&apos;&apos; GlobalScope.BasicLibraries.loadLibrary(&quot;ScriptForge&quot;)
+&apos;&apos;&apos; Dim test : test = CreateScriptService(&quot;UnitTest&quot;, ThisComponent, &quot;Tests&quot;)
+&apos;&apos;&apos; test.RunTest(&quot;AllTests&quot;) &apos; AllTests is a module name ; test cases are named &quot;Test_*&quot; (default)
+&apos;&apos;&apos; test.Dispose()
+&apos;&apos;&apos; End Sub
+&apos;&apos;&apos; REM ------------------------------------------------------------------------------
+&apos;&apos;&apos; Sub Setup(test) &apos; The unittest service is passed as argument
+&apos;&apos;&apos; &apos; Optional Sub to initialize processing of the actual test suite
+&apos;&apos;&apos; Dim exc : exc = CreateScriptService(&quot;Exception&quot;)
+&apos;&apos;&apos; exc.Console(Modal := False) &apos; Watch test progress in the console
+&apos;&apos;&apos; End Sub
+&apos;&apos;&apos; REM ------------------------------------------------------------------------------
+&apos;&apos;&apos; Sub Test_ArraySize(test)
+&apos;&apos;&apos; On Local Error GoTo CatchErr
+&apos;&apos;&apos; test.AssertEqual(ArraySize(10), -1, &quot;When not array&quot;)
+&apos;&apos;&apos; test.AssertEqual(ArraySize(Array(1, 2, 3)), 3, &quot;When simple array&quot;)
+&apos;&apos;&apos; test.AssertEqual(ArraySize(DimArray(3)), 4, &quot;When array with empty items&quot;)
+&apos;&apos;&apos; Exit Sub
+&apos;&apos;&apos; CatchErr:
+&apos;&apos;&apos; test.ReportError(&quot;ArraySize() is corrupt&quot;)
+&apos;&apos;&apos; End Sub
+&apos;&apos;&apos; REM ------------------------------------------------------------------------------
+&apos;&apos;&apos; Sub TearDown(test)
+&apos;&apos;&apos; &apos; Optional Sub to finalize processing of the actual test suite
+&apos;&apos;&apos; End Sub
+&apos;&apos;&apos;
+&apos;&apos;&apos; Error handling
+&apos;&apos;&apos; To support the debugging of the tested code, the UnitTest service, in cases of
+&apos;&apos;&apos; - assertion failure
+&apos;&apos;&apos; - Basic run-time error in the tested code
+&apos;&apos;&apos; - Basic run-time error in the testing code (the unit tests)
+&apos;&apos;&apos; will comment the error location and description in a message box and in the console log,
+&apos;&apos;&apos; providing every test case (in either mode) implements an error handler containing at least:
+&apos;&apos;&apos; Sub Test_Case1(test As Variant)
+&apos;&apos;&apos; On Local Error GoTo Catch
+&apos;&apos;&apos; &apos; ... (AssertXXX(), Fail(), ...)
+&apos;&apos;&apos; Exit Sub
+&apos;&apos;&apos; Catch:
+&apos;&apos;&apos; test.ReportError()
+&apos;&apos;&apos; End Sub
+&apos;&apos;&apos;
+&apos;&apos;&apos; Detailed user documentation:
+&apos;&apos;&apos; https://help.libreoffice.org/latest/en-US/text/sbasic/shared/03/sf_unittest.html?DbPAR=BASIC
+&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;
+
+REM ================================================================== EXCEPTIONS
+
+Private Const UNITTESTMETHODERROR = &quot;UNITTESTMETHODERROR&quot;
+
+REM ============================================================= PRIVATE MEMBERS
+
+Private [Me] As Object
+Private [_Parent] As Object
+Private ObjectType As String &apos; Must be &quot;UNITTEST&quot;
+Private ServiceName As String
+
+&apos; Testing code
+Private LibrariesContainer As String &apos; Document or user Basic library containing the test library
+Private Scope As String &apos; Scope when running a Basic script with Session.ExecuteBasicScript()
+Private Libraries As Variant &apos; Set of libraries
+Private LibraryName As String &apos; Name of the library containing the test code
+Private LibraryIndex As Integer &apos; Index in Libraries
+Private Modules As Variant &apos; Set of modules
+Private ModuleNames As Variant &apos; Set of module names
+Private MethodNames As Variant &apos; Set of methods in a given module
+
+&apos; Internals
+Private _Verbose As Boolean &apos; When True, every assertion is reported,failing or not
+Private _LongMessage As Boolean &apos; When False, only the message provided by the tester is considered
+ &apos; When True (default), that message is appended to the standard message
+Private _WhenAssertionFails As Integer &apos; Determines what to do when a test fails
+
+&apos; Test status
+Private _Status As Integer &apos; 0 = standby
+ &apos; 1 = test suite started
+ &apos; 2 = setup started
+ &apos; 3 = test case started
+ &apos; 4 = teardown started
+Private _ExecutionMode As Integer &apos; 1 = Test started with RunTest()
+ &apos; 2 = Test started with CreateScriptService() Only Assert() methods allowed
+Private _Module As String &apos; Exact name of module currently running
+Private _TestCase As String &apos; Exact name of test case currently running
+Private _ReturnCode As Integer &apos; 0 = Normal end
+ &apos; 1 = Assertion failed
+ &apos; 2 = Skip request (in Setup() only)
+ &apos;-1 = abnormal end
+Private _FailedAssert As String &apos; Assert function that returned a failure
+
+&apos; Timers
+Private TestTimer As Object &apos; Started by CreateScriptService()
+Private SuiteTimer As Object &apos; Started by RunTest()
+Private CaseTimer As Object &apos; Started by new case
+
+&apos; Services
+Private Exception As Object &apos; SF_Exception
+Private Session As Object &apos; SF_Session
+
+REM ============================================================ MODULE CONSTANTS
+
+&apos; When assertion fails constants: error is reported + ...
+Global Const FAILIGNORE = 0 &apos; Ignore the failure
+Global Const FAILSTOPSUITE = 1 &apos; Module TearDown is executed, then next suite may be started (default in full mode)
+Global Const FAILIMMEDIATESTOP = 2 &apos; Stop immediately (default in simple mode)
+
+&apos; Unit tests status (internal use only =&gt; not Global)
+Const STATUSSTANDBY = 0 &apos; No test active
+Const STATUSSUITESTARTED = 1 &apos; RunTest() started
+Const STATUSSETUP = 2 &apos; A Setup() method is running
+Const STATUSTESTCASE = 3 &apos; A test case is running
+Const STATUSTEARDOWN = 4 &apos; A TearDown() method is running
+
+&apos; Return codes
+Global Const RCNORMALEND = 0 &apos; Normal end of test or test not started
+Global Const RCASSERTIONFAILED = 1 &apos; An assertion within a test case returned False
+Global Const RCSKIPTEST = 2 &apos; A SkipTest() was issued by a Setup() method
+Global Const RCABORTTEST = 3 &apos; Abnormal end of test
+
+&apos; Execution modes
+Global Const FULLMODE = 1 &apos; 1 = Test started with RunTest()
+Global Const SIMPLEMODE = 2 &apos; 2 = Test started with CreateScriptService() Only Assert() methods allowed
+
+Const INVALIDPROCEDURECALL = &quot;5&quot; &apos; Artificial error raised when an assertion fails
+
+REM ===================================================== CONSTRUCTOR/DESTRUCTOR
+
+REM -----------------------------------------------------------------------------
+Private Sub Class_Initialize()
+ Set [Me] = Nothing
+ Set [_Parent] = Nothing
+ ObjectType = &quot;UNITTEST&quot;
+ ServiceName = &quot;SFUnitTests.UnitTest&quot;
+ LibrariesContainer = &quot;&quot;
+ Scope = &quot;&quot;
+ Libraries = Array()
+ LibraryName = &quot;&quot;
+ LibraryIndex = -1
+ _Verbose = False
+ _LongMessage = True
+ _WhenAssertionFails = -1
+ _Status = STATUSSTANDBY
+ _ExecutionMode = SIMPLEMODE
+ _Module = &quot;&quot;
+ _TestCase = &quot;&quot;
+ _ReturnCode = RCNORMALEND
+ _FailedAssert = &quot;&quot;
+ Set TestTimer = Nothing
+ Set SuiteTimer = Nothing
+ Set CaseTimer = Nothing
+ Set Exception = CreateScriptService(&quot;ScriptForge.Exception&quot;)
+ Set Session = CreateScriptService(&quot;ScriptForge.Session&quot;)
+End Sub &apos; SFUnitTests.SF_UnitTest Constructor
+
+REM -----------------------------------------------------------------------------
+Private Sub Class_Terminate()
+ If Not IsNull(CaseTimer) Then CaseTimer = CaseTimer.Dispose()
+ If Not IsNull(SuiteTimer) Then SuiteTimer = SuiteTimer.Dispose()
+ If Not IsNull(TestTimer) Then TestTimer = TestTimer.Dispose()
+ Call Class_Initialize()
+End Sub &apos; SFUnitTests.SF_UnitTest Destructor
+
+REM -----------------------------------------------------------------------------
+Public Function Dispose() As Variant
+ Call Class_Terminate()
+ Set Dispose = Nothing
+End Function &apos; SFUnitTests.SF_UnitTest Explicit destructor
+
+REM ================================================================== PROPERTIES
+
+REM -----------------------------------------------------------------------------
+Property Get LongMessage() As Variant
+&apos;&apos;&apos; When False, only the message provided by the tester is considered
+&apos;&apos;&apos; When True (default), that message is appended to the standard message
+ LongMessage = _PropertyGet(&quot;LongMessage&quot;)
+End Property &apos; SFUnitTests.SF_UnitTest.LongMessage (get)
+
+REM -----------------------------------------------------------------------------
+Property Let LongMessage(Optional ByVal pvLongMessage As Variant)
+&apos;&apos;&apos; Set the updatable property LongMessage
+ _PropertySet(&quot;LongMessage&quot;, pvLongMessage)
+End Property &apos; SFUnitTests.SF_UnitTest.LongMessage (let)
+
+REM -----------------------------------------------------------------------------
+Property Get ReturnCode() As Integer
+&apos;&apos;&apos; RCNORMALEND = 0 &apos; Normal end of test or test not started
+&apos;&apos;&apos; RCASSERTIONFAILED = 1 &apos; An assertion within a test case returned False
+&apos;&apos;&apos; RCSKIPTEST = 2 &apos; A SkipTest() was issued by a Setup() method
+&apos;&apos;&apos; RCABORTTEST = 3 &apos; Abnormal end of test
+ ReturnCode = _PropertyGet(&quot;ReturnCode&quot;)
+End Property &apos; SFUnitTests.SF_UnitTest.ReturnCode (get)
+
+REM -----------------------------------------------------------------------------
+Property Get Verbose() As Variant
+&apos;&apos;&apos; The Verbose property indicates if all assertions (True AND False) are reported
+ Verbose = _PropertyGet(&quot;Verbose&quot;)
+End Property &apos; SFUnitTests.SF_UnitTest.Verbose (get)
+
+REM -----------------------------------------------------------------------------
+Property Let Verbose(Optional ByVal pvVerbose As Variant)
+&apos;&apos;&apos; Set the updatable property Verbose
+ _PropertySet(&quot;Verbose&quot;, pvVerbose)
+End Property &apos; SFUnitTests.SF_UnitTest.Verbose (let)
+
+REM -----------------------------------------------------------------------------
+Property Get WhenAssertionFails() As Variant
+&apos;&apos;&apos; What when an AssertXXX() method returns False
+&apos;&apos;&apos; FAILIGNORE = 0 &apos; Ignore the failure
+&apos;&apos;&apos; FAILSTOPSUITE = 1 &apos; Module TearDown is executed, then next suite may be started (default in FULL mode)
+&apos;&apos;&apos; FAILIMMEDIATESTOP = 2 &apos; Stop immediately (default in SIMPLE mode)
+&apos;&apos;&apos; In simple mode, only FAILIGNORE and FAILIMMEDIATESTOP are allowed.
+&apos;&apos;&apos; In both modes, when WhenAssertionFails has not the value FAILIGNORE,
+&apos;&apos;&apos; each test case MUST have a run-time error handler calling the ReportError() method.
+&apos;&apos;&apos; Example:
+&apos;&apos;&apos; Sub Test_sometest(Optional test)
+&apos;&apos;&apos; On Local Error GoTo CatchError
+&apos;&apos;&apos; &apos; ... one or more assert verbs
+&apos;&apos;&apos; Exit Sub
+&apos;&apos;&apos; CatchError:
+&apos;&apos;&apos; test.ReportError()
+&apos;&apos;&apos; End Sub
+ WhenAssertionFails = _PropertyGet(&quot;WhenAssertionFails&quot;)
+End Property &apos; SFUnitTests.SF_UnitTest.WhenAssertionFails (get)
+
+REM -----------------------------------------------------------------------------
+Property Let WhenAssertionFails(Optional ByVal pvWhenAssertionFails As Variant)
+&apos;&apos;&apos; Set the updatable property WhenAssertionFails
+ _PropertySet(&quot;WhenAssertionFails&quot;, pvWhenAssertionFails)
+End Property &apos; SFUnitTests.SF_UnitTest.WhenAssertionFails (let)
+
+REM ===================================================================== METHODS
+
+REM -----------------------------------------------------------------------------
+Public Function AssertAlmostEqual(Optional ByRef A As Variant _
+ , Optional ByRef B As Variant _
+ , Optional ByVal Tolerance As Variant _
+ , Optional ByVal Message As Variant _
+ ) As Boolean
+&apos;&apos;&apos; Returns True when A and B are numerical values and are found close to each other.
+&apos;&apos;&apos; It is typically used to compare very large or very small numbers.
+&apos;&apos;&apos; Equality is confirmed when
+&apos;&apos;&apos; - A and B can be converted to doubles
+&apos;&apos;&apos; - The absolute difference between a and b, relative to the larger absolute value of a or b,
+&apos;&apos;&apos; is lower or equal to the tolerance. The default tolerance is 1E-09,
+&apos;&apos;&apos; Examples: 1E+12 and 1E+12 + 100 are almost equal
+&apos;&apos;&apos; 1E-20 and 2E-20 are not almost equal
+&apos;&apos;&apos; 100 and 95 are almost equal when Tolerance = 0.05
+
+Dim bAssert As Boolean &apos; Return value
+Const cstTolerance = 1E-09
+Const cstThisSub = &quot;UnitTest.AssertAlmostEqual&quot;
+Const cstSubArgs = &quot;A, B, [Tolerance=1E-09], [Message=&quot;&quot;&quot;&quot;]&quot;
+
+Check:
+ If IsMissing(A) Then A = Empty
+ If IsMissing(B) Then B = Empty
+ If IsMissing(Tolerance) Then Tolerance = cstTolerance
+ If IsMissing(Message) Then Message = &quot;&quot;
+ ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) &apos; Unconditional !
+ If Not ScriptForge.SF_Utils._Validate(Tolerance, &quot;Tolerance&quot;, ScriptForge.V_NUMERIC) Then GoTo Catch
+
+Try:
+ bAssert = _Assert(&quot;AssertAlmostEqual&quot;, True, A, B, Message, Tolerance)
+
+Finally:
+ AssertAlmostEqual = bAssert
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ bAssert = False
+ GoTo Finally
+End Function &apos; SFUnitTests.SF_UnitTest.AssertAlmostEqual
+
+REM -----------------------------------------------------------------------------
+Public Function AssertEqual(Optional ByRef A As Variant _
+ , Optional ByRef B As Variant _
+ , Optional ByVal Message As Variant _
+ ) As Boolean
+&apos;&apos;&apos; Returns True when A and B are found equal.
+&apos;&apos;&apos; Equality is confirmed when
+&apos;&apos;&apos; If A and B are scalars:
+&apos;&apos;&apos; They should have the same VarType or both be numeric
+&apos;&apos;&apos; Booleans and numeric values are compared with the = operator
+&apos;&apos;&apos; Strings are compared with the StrComp() builtin function. The comparison is case-sensitive
+&apos;&apos;&apos; Dates and times are compared up to the second
+&apos;&apos;&apos; Null, Empty and Nothing are not equal, but AssertEqual(Nothing, Nothing) returns True
+&apos;&apos;&apos; UNO objects are compared with the EqualUnoObjects() method
+&apos;&apos;&apos; Basic objects are NEVER equal
+&apos;&apos;&apos; If A and B are arrays:
+&apos;&apos;&apos; They should have the same number of dimensions (maximum 2)
+&apos;&apos;&apos; The lower and upper bounds must be identical for each dimension
+&apos;&apos;&apos; Two empty arrays are equal
+&apos;&apos;&apos; Their items must be equal one by one
+
+Dim bAssert As Boolean &apos; Return value
+Const cstThisSub = &quot;UnitTest.AssertEqual&quot;
+Const cstSubArgs = &quot;A, B, [Message=&quot;&quot;&quot;&quot;]&quot;
+
+Check:
+ If IsMissing(A) Then A = Empty
+ If IsMissing(B) Then B = Empty
+ If IsMissing(Message) Then Message = &quot;&quot;
+ ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) &apos; Unconditional !
+
+Try:
+ bAssert = _Assert(&quot;AssertEqual&quot;, True, A, B, Message)
+
+Finally:
+ AssertEqual = bAssert
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+End Function &apos; SFUnitTests.SF_UnitTest.AssertEqual
+
+REM -----------------------------------------------------------------------------
+Public Function AssertFalse(Optional ByRef A As Variant _
+ , Optional ByVal Message As Variant _
+ ) As Boolean
+&apos;&apos;&apos; Returns True when A is a Boolean and its value is False
+
+Dim bAssert As Boolean &apos; Return value
+Const cstThisSub = &quot;UnitTest.AssertFalse&quot;
+Const cstSubArgs = &quot;A, [Message=&quot;&quot;&quot;&quot;]&quot;
+
+Check:
+ If IsMissing(A) Then A = Empty
+ If IsMissing(Message) Then Message = &quot;&quot;
+ ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) &apos; Unconditional !
+
+Try:
+ bAssert = _Assert(&quot;AssertFalse&quot;, True, A, Empty, Message)
+
+Finally:
+ AssertFalse = bAssert
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+End Function &apos; SFUnitTests.SF_UnitTest.AssertFalse
+
+REM -----------------------------------------------------------------------------
+Public Function AssertGreater(Optional ByRef A As Variant _
+ , Optional ByRef B As Variant _
+ , Optional ByVal Message As Variant _
+ ) As Boolean
+&apos;&apos;&apos; Returns True when A is greater than B.
+&apos;&apos;&apos; To compare A and B:
+&apos;&apos;&apos; They should have the same VarType or both be numeric
+&apos;&apos;&apos; Eligible datatypes are String, Date or numeric.
+&apos;&apos;&apos; String comparisons are case-sensitive.
+
+Dim bAssert As Boolean &apos; Return value
+Const cstThisSub = &quot;UnitTest.AssertGreater&quot;
+Const cstSubArgs = &quot;A, B, [Message=&quot;&quot;&quot;&quot;]&quot;
+
+Check:
+ If IsMissing(A) Then A = Empty
+ If IsMissing(B) Then B = Empty
+ If IsMissing(Message) Then Message = &quot;&quot;
+ ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) &apos; Unconditional !
+
+Try:
+ bAssert = _Assert(&quot;AssertGreater&quot;, True, A, B, Message)
+
+Finally:
+ AssertGreater = bAssert
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+End Function &apos; SFUnitTests.SF_UnitTest.AssertGreater
+
+REM -----------------------------------------------------------------------------
+Public Function AssertGreaterEqual(Optional ByRef A As Variant _
+ , Optional ByRef B As Variant _
+ , Optional ByVal Message As Variant _
+ ) As Boolean
+&apos;&apos;&apos; Returns True when A is greater than or equal to B.
+&apos;&apos;&apos; To compare A and B:
+&apos;&apos;&apos; They should have the same VarType or both be numeric
+&apos;&apos;&apos; Eligible datatypes are String, Date or numeric.
+&apos;&apos;&apos; String comparisons are case-sensitive.
+
+Dim bAssert As Boolean &apos; Return value
+Const cstThisSub = &quot;UnitTest.AssertGreaterEqual&quot;
+Const cstSubArgs = &quot;A, B, [Message=&quot;&quot;&quot;&quot;]&quot;
+
+Check:
+ If IsMissing(A) Then A = Empty
+ If IsMissing(B) Then B = Empty
+ If IsMissing(Message) Then Message = &quot;&quot;
+ ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) &apos; Unconditional !
+
+Try:
+ bAssert = _Assert(&quot;AssertGreaterEqual&quot;, True, A, B, Message)
+
+Finally:
+ AssertGreaterEqual = bAssert
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+End Function &apos; SFUnitTests.SF_UnitTest.AssertGreaterEqual
+
+REM -----------------------------------------------------------------------------
+Public Function AssertIn(Optional ByRef A As Variant _
+ , Optional ByRef B As Variant _
+ , Optional ByVal Message As Variant _
+ ) As Boolean
+&apos;&apos;&apos; Returns True when A, a string, is found within B
+&apos;&apos;&apos; B may be a 1D array, a ScriptForge dictionary or a string.
+&apos;&apos;&apos; When B is an array, A may be a date or a numeric value.
+&apos;&apos;&apos; String comparisons are case-sensitive.
+
+Dim bAssert As Boolean &apos; Return value
+Const cstThisSub = &quot;UnitTest.AssertIn&quot;
+Const cstSubArgs = &quot;A, B, [Message=&quot;&quot;&quot;&quot;]&quot;
+
+Check:
+ If IsMissing(A) Then A = Empty
+ If IsMissing(B) Then B = Empty
+ If IsMissing(Message) Then Message = &quot;&quot;
+ ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) &apos; Unconditional !
+
+Try:
+ bAssert = _Assert(&quot;AssertIn&quot;, True, A, B, Message)
+
+Finally:
+ AssertIn = bAssert
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+End Function &apos; SFUnitTests.SF_UnitTest.AssertIn
+
+REM -----------------------------------------------------------------------------
+Public Function AssertIsInstance(Optional ByRef A As Variant _
+ , Optional ByRef ObjectType As Variant _
+ , Optional ByVal Message As Variant _
+ ) As Boolean
+&apos;&apos;&apos; Returns True when A is an object instance of the class ObjectType or a variable of type ObjectType.
+&apos;&apos;&apos; A may be:
+&apos;&apos;&apos; - a ScriptForge object
+&apos;&apos;&apos; ObjectType is a string like &quot;DICTIONARY&quot;, &quot;calc&quot;, &quot;Dialog&quot;, &quot;exception&quot;, etc.
+&apos;&apos;&apos; - a UNO object
+&apos;&apos;&apos; ObjectType is a string identical with values returned by the SF_Session.UnoObjectType()
+&apos;&apos;&apos; - any variable, providing it is neither an object nor an array
+&apos;&apos;&apos; ObjectType is a string identifying a value returned by the TypeName() builtin function
+&apos;&apos;&apos; - an array
+&apos;&apos;&apos; ObjectType is expected to be &quot;array&quot;
+
+Dim bAssert As Boolean &apos; Return value
+Const cstThisSub = &quot;UnitTest.AssertIsInstance&quot;
+Const cstSubArgs = &quot;A, ObjectType, [Message=&quot;&quot;&quot;&quot;]&quot;
+
+Check:
+ If IsMissing(A) Then A = Empty
+ If IsMissing(ObjectType) Then ObjectType = Empty
+ If IsMissing(Message) Then Message = &quot;&quot;
+ ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) &apos; Unconditional !
+ If Not ScriptForge.SF_Utils._Validate(ObjectType, &quot;ObjectType&quot;, V_STRING) Then GoTo Catch
+
+
+Try:
+ bAssert = _Assert(&quot;AssertIsInstance&quot;, True, A, Empty, Message, ObjectType)
+
+Finally:
+ AssertIsInstance = bAssert
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ bAssert = False
+ GoTo Finally
+End Function &apos; SFUnitTests.SF_UnitTest.AssertIsInstance
+
+REM -----------------------------------------------------------------------------
+Public Function AssertIsNothing(Optional ByRef A As Variant _
+ , Optional ByVal Message As Variant _
+ ) As Boolean
+&apos;&apos;&apos; Returns True when A is an object that has the Nothing value
+
+Dim bAssert As Boolean &apos; Return value
+Const cstThisSub = &quot;UnitTest.AssertIsNothing&quot;
+Const cstSubArgs = &quot;A, [Message=&quot;&quot;&quot;&quot;]&quot;
+
+Check:
+ If IsMissing(A) Then A = Empty
+ If IsMissing(Message) Then Message = &quot;&quot;
+ ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) &apos; Unconditional !
+
+Try:
+ bAssert = _Assert(&quot;AssertIsNothing&quot;, True, A, Empty, Message)
+
+Finally:
+ AssertIsNothing = bAssert
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+End Function &apos; SFUnitTests.SF_UnitTest.AssertIsNothing
+
+REM -----------------------------------------------------------------------------
+Public Function AssertIsNull(Optional ByRef A As Variant _
+ , Optional ByVal Message As Variant _
+ ) As Boolean
+&apos;&apos;&apos; Returns True when A has the Null value
+
+Dim bAssert As Boolean &apos; Return value
+Const cstThisSub = &quot;UnitTest.AssertIsNull&quot;
+Const cstSubArgs = &quot;A, [Message=&quot;&quot;&quot;&quot;]&quot;
+
+Check:
+ If IsMissing(A) Then A = Empty
+ If IsMissing(Message) Then Message = &quot;&quot;
+ ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) &apos; Unconditional !
+
+Try:
+ bAssert = _Assert(&quot;AssertIsNull&quot;, True, A, Empty, Message)
+
+Finally:
+ AssertIsNull = bAssert
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+End Function &apos; SFUnitTests.SF_UnitTest.AssertIsNull
+
+REM -----------------------------------------------------------------------------
+Public Function AssertLess(Optional ByRef A As Variant _
+ , Optional ByRef B As Variant _
+ , Optional ByVal Message As Variant _
+ ) As Boolean
+&apos;&apos;&apos; Returns True when A is less than B.
+&apos;&apos;&apos; To compare A and B:
+&apos;&apos;&apos; They should have the same VarType or both be numeric
+&apos;&apos;&apos; Eligible datatypes are String, Date or numeric.
+&apos;&apos;&apos; String comparisons are case-sensitive.
+
+Dim bAssert As Boolean &apos; Return value
+Const cstThisSub = &quot;UnitTest.AssertLess&quot;
+Const cstSubArgs = &quot;A, B, [Message=&quot;&quot;&quot;&quot;]&quot;
+
+Check:
+ If IsMissing(A) Then A = Empty
+ If IsMissing(B) Then B = Empty
+ If IsMissing(Message) Then Message = &quot;&quot;
+ ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) &apos; Unconditional !
+
+Try:
+ bAssert = _Assert(&quot;AssertLess&quot;, False, A, B, Message)
+
+Finally:
+ AssertLess = bAssert
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+End Function &apos; SFUnitTests.SF_UnitTest.AssertLess
+
+REM -----------------------------------------------------------------------------
+Public Function AssertLessEqual(Optional ByRef A As Variant _
+ , Optional ByRef B As Variant _
+ , Optional ByVal Message As Variant _
+ ) As Boolean
+&apos;&apos;&apos; Returns True when A is less than or equal to B.
+&apos;&apos;&apos; To compare A and B:
+&apos;&apos;&apos; They should have the same VarType or both be numeric
+&apos;&apos;&apos; Eligible datatypes are String, Date or numeric.
+&apos;&apos;&apos; String comparisons are case-sensitive.
+
+Dim bAssert As Boolean &apos; Return value
+Const cstThisSub = &quot;UnitTest.AssertLessEqual&quot;
+Const cstSubArgs = &quot;A, B, [Message=&quot;&quot;&quot;&quot;]&quot;
+
+Check:
+ If IsMissing(A) Then A = Empty
+ If IsMissing(B) Then B = Empty
+ If IsMissing(Message) Then Message = &quot;&quot;
+ ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) &apos; Unconditional !
+
+Try:
+ bAssert = _Assert(&quot;AssertLessEqual&quot;, False, A, B, Message)
+
+Finally:
+ AssertLessEqual = bAssert
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+End Function &apos; SFUnitTests.SF_UnitTest.AssertLessEqual
+
+REM -----------------------------------------------------------------------------
+Public Function AssertLike(Optional ByRef A As Variant _
+ , Optional ByRef Pattern As Variant _
+ , Optional ByVal Message As Variant _
+ ) As Boolean
+&apos;&apos;&apos; Returns True if string A matches a given pattern containing wildcards
+&apos;&apos;&apos; Admitted wildcard are: the &quot;?&quot; represents any single character
+&apos;&apos;&apos; the &quot;*&quot; represents zero, one, or multiple characters
+&apos;&apos;&apos; The comparison is case-sensitive.
+
+Dim bAssert As Boolean &apos; Return value
+Const cstThisSub = &quot;UnitTest.AssertLike&quot;
+Const cstSubArgs = &quot;A, Pattern, [Message=&quot;&quot;&quot;&quot;]&quot;
+
+Check:
+ If IsMissing(A) Then A = Empty
+ If IsMissing(Pattern) Then Pattern = &quot;&quot;
+ If IsMissing(Message) Then Message = &quot;&quot;
+ ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) &apos; Unconditional !
+ If Not ScriptForge.SF_Utils._Validate(Pattern, &quot;Pattern&quot;, V_STRING) Then GoTo Catch
+
+Try:
+ bAssert = _Assert(&quot;AssertLike&quot;, True, A, Empty, Message, Pattern)
+
+Finally:
+ AssertLike = bAssert
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ bAssert = False
+ GoTo Finally
+End Function &apos; SFUnitTests.SF_UnitTest.AssertLike
+
+REM -----------------------------------------------------------------------------
+Public Function AssertNotAlmostEqual(Optional ByRef A As Variant _
+ , Optional ByRef B As Variant _
+ , Optional ByVal Tolerance As Variant _
+ , Optional ByVal Message As Variant _
+ ) As Boolean
+&apos;&apos;&apos; Returns True when A and B are numerical values and are not found close to each other.
+&apos;&apos;&apos; Read about almost equality in the comments linked to the AssertEqual() method.
+
+Dim bAssert As Boolean &apos; Return value
+Const cstTolerance = 1E-09
+Const cstThisSub = &quot;UnitTest.AssertNotAlmostEqual&quot;
+Const cstSubArgs = &quot;A, B, [Tolerance=1E-09], [Message=&quot;&quot;&quot;&quot;]&quot;
+
+Check:
+ If IsMissing(A) Then A = Empty
+ If IsMissing(B) Then B = Empty
+ If IsMissing(Tolerance) Then Tolerance = cstTolerance
+ If IsMissing(Message) Then Message = &quot;&quot;
+ ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) &apos; Unconditional !
+ If Not ScriptForge.SF_Utils._Validate(Tolerance, &quot;Tolerance&quot;, ScriptForge.V_NUMERIC) Then GoTo Catch
+
+Try:
+ bAssert = _Assert(&quot;AssertNotAlmostEqual&quot;, False, A, B, Message, Tolerance)
+
+Finally:
+ AssertNotAlmostEqual = bAssert
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ bAssert = False
+ GoTo Finally
+End Function &apos; SFUnitTests.SF_UnitTest.AssertNotAlmostEqual
+
+REM -----------------------------------------------------------------------------
+Public Function AssertNotEqual(Optional ByRef A As Variant _
+ , Optional ByRef B As Variant _
+ , Optional ByVal Message As Variant _
+ ) As Boolean
+&apos;&apos;&apos; Returns True when A and B are found unequal.
+&apos;&apos;&apos; Read about equality in the comments linked to the AssertEqual() method.
+
+Dim bAssert As Boolean &apos; Return value
+Const cstThisSub = &quot;UnitTest.AssertNotEqual&quot;
+Const cstSubArgs = &quot;A, B, [Message=&quot;&quot;&quot;&quot;]&quot;
+
+Check:
+ If IsMissing(A) Then A = Empty
+ If IsMissing(B) Then B = Empty
+ If IsMissing(Message) Then Message = &quot;&quot;
+ ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) &apos; Unconditional !
+
+Try:
+ bAssert = _Assert(&quot;AssertNotEqual&quot;, False, A, B, Message)
+
+Finally:
+ AssertNotEqual = bAssert
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+End Function &apos; SFUnitTests.SF_UnitTest.AssertNotEqual
+
+REM -----------------------------------------------------------------------------
+Public Function AssertNotIn(Optional ByRef A As Variant _
+ , Optional ByRef B As Variant _
+ , Optional ByVal Message As Variant _
+ ) As Boolean
+&apos;&apos;&apos; Returns True when A, a string, is not found within B
+&apos;&apos;&apos; B may be a 1D array, a ScriptForge dictionary or a string.
+&apos;&apos;&apos; When B is an array, A may be a date or a numeric value.
+&apos;&apos;&apos; String comparisons are case-sensitive.
+
+Dim bAssert As Boolean &apos; Return value
+Const cstThisSub = &quot;UnitTest.AssertNotIn&quot;
+Const cstSubArgs = &quot;A, B, [Message=&quot;&quot;&quot;&quot;]&quot;
+
+Check:
+ If IsMissing(A) Then A = Empty
+ If IsMissing(B) Then B = Empty
+ If IsMissing(Message) Then Message = &quot;&quot;
+ ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) &apos; Unconditional !
+
+Try:
+ bAssert = _Assert(&quot;AssertNotIn&quot;, False, A, B, Message)
+
+Finally:
+ AssertNotIn = bAssert
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+End Function &apos; SFUnitTests.SF_UnitTest.AssertNotIn
+
+REM -----------------------------------------------------------------------------
+Public Function AssertNotInstance(Optional ByRef A As Variant _
+ , Optional ByRef ObjectType As Variant _
+ , Optional ByVal Message As Variant _
+ ) As Boolean
+&apos;&apos;&apos; Returns True when A is an object instance of the class ObjectType or a variable of type ObjectType.
+&apos;&apos;&apos; More details to be read under the AssertInstance() function.
+
+Dim bAssert As Boolean &apos; Return value
+Const cstThisSub = &quot;UnitTest.AssertNotInstance&quot;
+Const cstSubArgs = &quot;A, ObjectType, [Message=&quot;&quot;&quot;&quot;]&quot;
+
+Check:
+ If IsMissing(A) Then A = Empty
+ If IsMissing(ObjectType) Then ObjectType = Empty
+ If IsMissing(Message) Then Message = &quot;&quot;
+ ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) &apos; Unconditional !
+ If Not ScriptForge.SF_Utils._Validate(ObjectType, &quot;ObjectType&quot;, V_STRING) Then GoTo Catch
+
+Try:
+ bAssert = _Assert(&quot;AssertNotInstance&quot;, False, A, Empty, Message, ObjectType)
+
+Finally:
+ AssertNotInstance = bAssert
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ bAssert = False
+ GoTo Finally
+End Function &apos; SFUnitTests.SF_UnitTest.AssertNotInstance
+
+REM -----------------------------------------------------------------------------
+Public Function AssertNotLike(Optional ByRef A As Variant _
+ , Optional ByRef Pattern As Variant _
+ , Optional ByVal Message As Variant _
+ ) As Boolean
+&apos;&apos;&apos; Returns True if A is not a string or does not match a given pattern containing wildcards
+&apos;&apos;&apos; Admitted wildcard are: the &quot;?&quot; represents any single character
+&apos;&apos;&apos; the &quot;*&quot; represents zero, one, or multiple characters
+&apos;&apos;&apos; The comparison is case-sensitive.
+
+Dim bAssert As Boolean &apos; Return value
+Const cstThisSub = &quot;UnitTest.AssertNotLike&quot;
+Const cstSubArgs = &quot;A, Pattern, [Message=&quot;&quot;&quot;&quot;]&quot;
+
+Check:
+ If IsMissing(A) Then A = Empty
+ If IsMissing(Pattern) Then Pattern = &quot;&quot;
+ If IsMissing(Message) Then Message = &quot;&quot;
+ ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) &apos; Unconditional !
+ If Not ScriptForge.SF_Utils._Validate(Pattern, &quot;Pattern&quot;, V_STRING) Then GoTo Catch
+
+Try:
+ bAssert = _Assert(&quot;AssertNotLike&quot;, False, A, Empty, Message, Pattern)
+
+Finally:
+ AssertNotLike = bAssert
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ bAssert = False
+ GoTo Finally
+End Function &apos; SFUnitTests.SF_UnitTest.AssertNotLike
+
+REM -----------------------------------------------------------------------------
+Public Function AssertNotNothing(Optional ByRef A As Variant _
+ , Optional ByVal Message As Variant _
+ ) As Boolean
+&apos;&apos;&apos; Returns True except when A is an object that has the Nothing value
+
+Dim bAssert As Boolean &apos; Return value
+Const cstThisSub = &quot;UnitTest.AssertNotNothing&quot;
+Const cstSubArgs = &quot;A, [Message=&quot;&quot;&quot;&quot;]&quot;
+
+Check:
+ If IsMissing(A) Then A = Empty
+ If IsMissing(Message) Then Message = &quot;&quot;
+ ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) &apos; Unconditional !
+
+Try:
+ bAssert = _Assert(&quot;AssertNotNothing&quot;, False, A, Empty, Message)
+
+Finally:
+ AssertNotNothing = bAssert
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+End Function &apos; SFUnitTests.SF_UnitTest.AssertNotNothing
+
+REM -----------------------------------------------------------------------------
+Public Function AssertNotNull(Optional ByRef A As Variant _
+ , Optional ByVal Message As Variant _
+ ) As Boolean
+&apos;&apos;&apos; Returns True except when A has the Null value
+
+Dim bAssert As Boolean &apos; Return value
+Const cstThisSub = &quot;UnitTest.AssertNotNull&quot;
+Const cstSubArgs = &quot;A, [Message=&quot;&quot;&quot;&quot;]&quot;
+
+Check:
+ If IsMissing(A) Then A = Empty
+ If IsMissing(Message) Then Message = &quot;&quot;
+ ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) &apos; Unconditional !
+
+Try:
+ bAssert = _Assert(&quot;AssertNotNull&quot;, False, A, Empty, Message)
+
+Finally:
+ AssertNotNull = bAssert
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+End Function &apos; SFUnitTests.SF_UnitTest.AssertNotNull
+
+REM -----------------------------------------------------------------------------
+Public Function AssertNotRegex(Optional ByRef A As Variant _
+ , Optional ByRef Regex As Variant _
+ , Optional ByVal Message As Variant _
+ ) As Boolean
+&apos;&apos;&apos; Returns True when A is not a string or does not match the given regular expression.
+&apos;&apos;&apos; The comparison is case-sensitive.
+
+Dim bAssert As Boolean &apos; Return value
+Const cstThisSub = &quot;UnitTest.AssertNotRegex&quot;
+Const cstSubArgs = &quot;A, Regex, [Message=&quot;&quot;&quot;&quot;]&quot;
+
+Check:
+ If IsMissing(A) Then A = Empty
+ If IsMissing(Regex) Then Regex = &quot;&quot;
+ If IsMissing(Message) Then Message = &quot;&quot;
+ ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) &apos; Unconditional !
+ If Not ScriptForge.SF_Utils._Validate(Regex, &quot;Regex&quot;, V_STRING) Then GoTo Catch
+
+Try:
+ bAssert = _Assert(&quot;AssertNotRegex&quot;, False, A, Empty, Message, Regex)
+
+Finally:
+ AssertNotRegex = bAssert
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ bAssert = False
+ GoTo Finally
+End Function &apos; SFUnitTests.SF_UnitTest.AssertNotRegex
+
+REM -----------------------------------------------------------------------------
+Public Function AssertRegex(Optional ByRef A As Variant _
+ , Optional ByRef Regex As Variant _
+ , Optional ByVal Message As Variant _
+ ) As Boolean
+&apos;&apos;&apos; Returns True when string A matches the given regular expression.
+&apos;&apos;&apos; The comparison is case-sensitive.
+
+Dim bAssert As Boolean &apos; Return value
+Const cstThisSub = &quot;UnitTest.AssertRegex&quot;
+Const cstSubArgs = &quot;A, Regex, [Message=&quot;&quot;&quot;&quot;]&quot;
+
+Check:
+ If IsMissing(A) Then A = Empty
+ If IsMissing(Regex) Then Regex = &quot;&quot;
+ If IsMissing(Message) Then Message = &quot;&quot;
+ ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) &apos; Unconditional !
+ If Not ScriptForge.SF_Utils._Validate(Regex, &quot;Regex&quot;, V_STRING) Then GoTo Catch
+
+Try:
+ bAssert = _Assert(&quot;AssertRegex&quot;, True, A, Empty, Message, Regex)
+
+Finally:
+ AssertRegex = bAssert
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ bAssert = False
+ GoTo Finally
+End Function &apos; SFUnitTests.SF_UnitTest.AssertRegex
+
+REM -----------------------------------------------------------------------------
+Public Function AssertTrue(Optional ByRef A As Variant _
+ , Optional ByVal Message As Variant _
+ ) As Boolean
+&apos;&apos;&apos; Returns True when A is a Boolean and its value is True
+
+Dim bAssert As Boolean &apos; Return value
+Const cstThisSub = &quot;UnitTest.AssertTrue&quot;
+Const cstSubArgs = &quot;A, [Message=&quot;&quot;&quot;&quot;]&quot;
+
+Check:
+ If IsMissing(A) Then A = Empty
+ If IsMissing(Message) Then Message = &quot;&quot;
+ ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) &apos; Unconditional !
+
+Try:
+ bAssert = _Assert(&quot;AssertTrue&quot;, True, A, Empty, Message)
+
+Finally:
+ AssertTrue = bAssert
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+End Function &apos; SFUnitTests.SF_UnitTest.AssertTrue
+
+REM -----------------------------------------------------------------------------
+Public Sub Fail(Optional ByVal Message As Variant)
+&apos;&apos;&apos; Forces a test failure
+
+Dim bAssert As Boolean &apos; Fictive return value
+Const cstThisSub = &quot;UnitTest.Fail&quot;
+Const cstSubArgs = &quot;[Message=&quot;&quot;&quot;&quot;]&quot;
+
+Check:
+ If IsMissing(Message) Then Message = &quot;&quot;
+ ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) &apos; Unconditional !
+
+Try:
+ bAssert = _Assert(&quot;Fail&quot;, False, Empty, Empty, Message)
+
+Finally:
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Sub
+End Sub &apos; SFUnitTests.SF_UnitTest.Fail
+
+REM -----------------------------------------------------------------------------
+Public Sub Log(Optional ByVal Message As Variant)
+&apos;&apos;&apos; Records the given message in the test report (console)
+
+Dim bAssert As Boolean &apos; Fictive return value
+Dim bVerbose As Boolean : bVerbose = _Verbose
+Const cstThisSub = &quot;UnitTest.Log&quot;
+Const cstSubArgs = &quot;[Message=&quot;&quot;&quot;&quot;]&quot;
+
+Check:
+ If IsMissing(Message) Then Message = &quot;&quot;
+ ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) &apos; Unconditional !
+
+Try:
+ &apos; Force the display of the message in the console
+ _Verbose = True
+ bAssert = _Assert(&quot;Log&quot;, True, Empty, Empty, Message)
+ _Verbose = bVerbose
+
+Finally:
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Sub
+End Sub &apos; SFUnitTests.SF_UnitTest.Log
+
+REM -----------------------------------------------------------------------------
+Public Function GetProperty(Optional ByVal PropertyName As Variant) As Variant
+&apos;&apos;&apos; Return the actual value of the given property
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; PropertyName: the name of the property as a string
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; The actual value of the property
+&apos;&apos;&apos; Exceptions
+&apos;&apos;&apos; ARGUMENTERROR The property does not exist
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; myUnitTest.GetProperty(&quot;Duration&quot;)
+
+Const cstThisSub = &quot;UnitTest.GetProperty&quot;
+Const cstSubArgs = &quot;PropertyName&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ GetProperty = Null
+
+Check:
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not ScriptForge.SF_Utils._Validate(PropertyName, &quot;PropertyName&quot;, V_STRING, Properties()) Then GoTo Catch
+ End If
+
+Try:
+ GetProperty = _PropertyGet(PropertyName)
+
+Finally:
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFUnitTests.SF_UnitTest.Properties
+
+REM -----------------------------------------------------------------------------
+Public Function Methods() As Variant
+&apos;&apos;&apos; Return the list or methods of the UnitTest class as an array
+
+ Methods = Array( _
+ &quot;AssertAlmostEqual&quot; _
+ , &quot;AssertEqual&quot; _
+ , &quot;AssertFalse&quot; _
+ , &quot;AssertGreater&quot; _
+ , &quot;AssertGreaterEqual&quot; _
+ , &quot;AssertIn&quot; _
+ , &quot;AssertIsInstance&quot; _
+ , &quot;AssertIsNothing&quot; _
+ , &quot;AssertLike&quot; _
+ , &quot;AssertNotRegex&quot; _
+ , &quot;AssertIsNull&quot; _
+ , &quot;AssertLess&quot; _
+ , &quot;AssertLessEqual&quot; _
+ , &quot;AssertNotAlmostEqual&quot; _
+ , &quot;AssertNotEqual&quot; _
+ , &quot;AssertNotIn&quot; _
+ , &quot;AssertNotInstance&quot; _
+ , &quot;AssertNotLike&quot; _
+ , &quot;AssertNotNothing&quot; _
+ , &quot;AssertNotNull&quot; _
+ , &quot;AssertRegex&quot; _
+ , &quot;AssertTrue&quot; _
+ , &quot;Fail&quot; _
+ , &quot;Log&quot; _
+ , &quot;RunTest&quot; _
+ , &quot;SkipTest&quot; _
+ )
+
+End Function &apos; SFUnitTests.SF_UnitTest.Methods
+
+REM -----------------------------------------------------------------------------
+Public Function Properties() As Variant
+&apos;&apos;&apos; Return the list or properties of the UnitTest class as an array
+
+ Properties = Array( _
+ &quot;LongMessage&quot; _
+ , &quot;ReturnCode&quot; _
+ , &quot;Verbose&quot; _
+ , &quot;WhenAssertionFails&quot; _
+ )
+
+End Function &apos; SFUnitTests.SF_UnitTest.Properties
+
+REM -----------------------------------------------------------------------------
+Public Sub ReportError(Optional ByVal Message As Variant)
+&apos;&apos;&apos; DIsplay a message box with the current property values of the &quot;Exception&quot; service.
+&apos;&apos;&apos; Depending on the WhenAssertionFails property, a Raise() or RaiseWarning()
+&apos;&apos;&apos; is issued. The Raise() method stops completely the Basic running process.
+&apos;&apos;&apos; The ReportError() method is presumed present in a user script in an error
+&apos;&apos;&apos; handling part of the actual testcase.
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Message: a string to replace or to complete the standard message description
+&apos;&apos;&apos; Example:
+&apos;&apos;&apos; See the Test_ArraySize() sub in the module&apos;s heading example
+
+Dim sLine As String &apos; Line number where the error occurred
+Dim sError As String &apos; Exception description
+Dim sErrorCode As String &apos; Exception number
+Const cstThisSub = &quot;UnitTest.ReportError&quot;
+Const cstSubArgs = &quot;[Message=&quot;&quot;&quot;&quot;]&quot;
+
+Check:
+ If IsMissing(Message) Or IsEmpty(Message) Then Message = &quot;&quot;
+ ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) &apos; Unconditional !
+ If VarType(Message) &lt;&gt; V_STRING Then Message = &quot;&quot;
+
+Try:
+ sLine = &quot;ln &quot; &amp; CStr(Exception.Source)
+ If _ExecutionMode = FULLMODE Then sLine = _Module &amp; &quot;.&quot; &amp; _TestCase &amp; &quot; &quot; &amp; sLine
+ If Len(Message) &gt; 0 Then
+ sError = Message
+ Else
+ If Exception.Number = INVALIDPROCEDURECALL Then
+ sError = &quot;Test case failure&quot;
+ sErrorCode = &quot;ASSERTIONFAILED&quot;
+ Else
+ sError = Exception.Description
+ sErrorCode = CStr(Exception.Number)
+ End If
+ End If
+
+ Select Case _WhenAssertionFails
+ Case FAILIGNORE
+ Case FAILSTOPSUITE
+ Exception.RaiseWarning(sErrorCode, sLine, sError)
+ Case FAILIMMEDIATESTOP
+ Exception.Raise(sErrorCode, sLine, sError)
+ End Select
+
+Finally:
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Sub
+End Sub &apos; SFUnitTests.SF_UnitTest.ReportError
+REM -----------------------------------------------------------------------------
+Public Function RunTest(Optional ByVal TestSuite As Variant _
+ , Optional ByVal TestCasePattern As Variant _
+ , Optional ByVal Message As Variant _
+ ) As Integer
+&apos;&apos;&apos; Execute a test suite pointed out by a module name.
+&apos;&apos;&apos; Each test case will be run independently from each other.
+&apos;&apos;&apos; The names of the test cases to be run may be selected with a string pattern.
+&apos;&apos;&apos; The test is &quot;orchestrated&quot; by this method:
+&apos;&apos;&apos; 1. Execute the optional Setup() method present in the module
+&apos;&apos;&apos; 2. Execute once each test case, in any order
+&apos;&apos;&apos; 3, Execute the optional TearDown() method present in the module
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; TestSuite: the name of the module containing the set of test cases to run
+&apos;&apos;&apos; TestCasePattern: the pattern that the test cases must match. The comparison is not case-sensitive.
+&apos;&apos;&apos; Non-matching functions and subs are ignored.
+&apos;&apos;&apos; Admitted wildcard are: the &quot;?&quot; represents any single character
+&apos;&apos;&apos; the &quot;*&quot; represents zero, one, or multiple characters
+&apos;&apos;&apos; The default pattern is &quot;Test_*&quot;
+&apos;&apos;&apos; Message: the message to be displayed in the console when the test starts.
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; One of the return codes of the execution (RCxxx constants)
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; GlobalScope.BasicLibraries.loadLibrary(&quot;ScriptForge&quot;)
+&apos;&apos;&apos; Dim test : test = CreateScriptService(&quot;UnitTest&quot;, ThisComponent, &quot;Tests&quot;)
+&apos;&apos;&apos; test.RunTest(&quot;AllTests&quot;) &apos; AllTests is a module name ; test cases are named &quot;Test_*&quot; (default)
+
+Dim iRun As Integer &apos; Return value
+Dim sRunMessage As String &apos; Reporting
+Dim iModule As Integer &apos; Index of module currently running
+Dim vMethods As Variant &apos; Set of methods
+Dim sMethod As String &apos; A single method
+Dim iMethod As Integer &apos; Index in MethodNames
+Dim m As Integer
+
+Const cstThisSub = &quot;UnitTest.RunTest&quot;
+Const cstSubArgs = &quot;TestSuite, [TestCasePattern=&quot;&quot;Test_*&quot;&quot;], [Message=&quot;&quot;&quot;&quot;]&quot;
+
+ iRun = RCNORMALEND
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+
+Check:
+ If IsMissing(TestCasePattern) Or IsEmpty(TestCasePattern) Then TestCasePattern = &quot;Test_*&quot;
+ If IsMissing(Message) Or IsEmpty(Message) Then Message = &quot;&quot;
+ ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) &apos; Unconditional !
+ If Not ScriptForge.SF_Utils._Validate(TestSuite, &quot;TestSuite&quot;, V_STRING, ModuleNames) Then GoTo Catch
+ If Not ScriptForge.SF_Utils._Validate(TestCasePattern, &quot;TestCasePattern&quot;, V_STRING) Then GoTo Catch
+ If Not ScriptForge.SF_Utils._Validate(Message, &quot;Message&quot;, V_STRING) Then GoTo Catch
+
+ &apos; A RunTest() is forbidden inside a test suite or when simple mode
+ If _Status &lt;&gt; STATUSSTANDBY Or _ExecutionMode &lt;&gt; FULLMODE Then GoTo CatchMethod
+
+ &apos; Ignore any call when an abnormal end has been encountered
+ If _ReturnCode = RCABORTTEST Then GoTo Catch
+
+Try:
+ iModule = ScriptForge.SF_Array.IndexOf(ModuleNames, TestSuite, CaseSensitive := False, SortOrder := &quot;ASC&quot;)
+ _Module = ModuleNames(iModule)
+
+ &apos; Start timer
+ If Not IsNull(SuiteTimer) Then SuiteTimer = SuiteTimer.Dispose()
+ Set SuiteTimer = CreateScriptService(&quot;ScriptForge.Timer&quot;, True)
+
+ &apos; Report the start of a new test suite
+ sRunMessage = &quot;RUNTEST ENTER testsuite=&apos;&quot; &amp; LibraryName &amp; &quot;.&quot; &amp; _Module &amp; &quot;&apos;, pattern=&apos;&quot; &amp; TestCasePattern &amp; &quot;&apos;&quot;
+ _ReportMessage(sRunMessage, Message)
+ _Status = STATUSSUITESTARTED
+
+ &apos; Collect all the methods of the module
+ If Modules(iModule).hasChildNodes() Then
+ vMethods = Modules(iModule).getChildNodes()
+ MethodNames = Array()
+ For m = 0 To UBound(vMethods)
+ sMethod = vMethods(m).getName()
+ MethodNames = ScriptForge.SF_Array.Append(MethodNames, sMethod)
+ Next m
+ End If
+
+ &apos; Execute the Setup() method, if it exists
+ iMethod = ScriptForge.SF_Array.IndexOf(MethodNames, &quot;Setup&quot;, CaseSensitive := False, SortOrder := &quot;ASC&quot;)
+ If iMethod &gt;= 0 Then
+ _TestCase = MethodNames(iMethod) &apos; _TestCase is used in ReportError()
+ If Not _ExecuteScript(_TestCase) Then GoTo Catch
+ End If
+
+ &apos; Execute the test cases that match the pattern
+ For iMethod = 0 To UBound(MethodNames)
+ If _ReturnCode = RCSKIPTEST Or _ReturnCode = RCASSERTIONFAILED Then Exit For
+ sMethod = MethodNames(iMethod)
+ If ScriptForge.SF_String.IsLike(sMethod, TestCasePattern, CaseSensitive := False) Then
+ _TestCase = sMethod
+ &apos; Start timer
+ If Not IsNull(CaseTimer) Then CaseTimer = CaseTimer.Dispose()
+ Set CaseTimer = CreateScriptService(&quot;ScriptForge.Timer&quot;, True)
+ If Not _ExecuteScript(sMethod) Then GoTo Catch
+ CaseTimer.Terminate()
+ _TestCase = &quot;&quot;
+ End If
+ Next iMethod
+
+ If _ReturnCode &lt;&gt; RCSKIPTEST Then
+ &apos; Execute the TearDown() method, if it exists
+ iMethod = ScriptForge.SF_Array.IndexOf(MethodNames, &quot;TearDown&quot;, CaseSensitive := False, SortOrder := &quot;ASC&quot;)
+ If iMethod &gt;= 0 Then
+ _TestCase = MethodNames(iMethod) &apos; _TestCase is used in ReportError()
+ If Not _ExecuteScript(_TestCase) Then GoTo Catch
+ End If
+ End If
+
+ &apos; Report the end of the current test suite
+ sRunMessage = &quot;RUNTEST EXIT testsuite=&apos;&quot; &amp; LibraryName &amp; &quot;.&quot; &amp; _Module &amp; &quot;&apos; &quot; &amp; _Duration(&quot;Suite&quot;, True)
+ _ReportMessage(sRunMessage, Message)
+
+ &apos; Stop timer
+ SuiteTimer.Terminate()
+
+ &apos; Housekeeping
+ MethodNames = Array()
+ _Module = &quot;&quot;
+ _Status = STATUSSTANDBY
+
+Finally:
+ _ReturnCode = iRun
+ RunTest = iRun
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ iRun = RCABORTTEST
+ GoTo Finally
+CatchMethod:
+ ScriptForge.SF_Exception.RaiseFatal(UNITTESTMETHODERROR, &quot;RunTest&quot;)
+ GoTo Catch
+End Function &apos; SFUnitTests.SF_UnitTest.RunTest
+
+REM -----------------------------------------------------------------------------
+Public Function SetProperty(Optional ByVal PropertyName As Variant _
+ , Optional ByRef Value As Variant _
+ ) As Boolean
+&apos;&apos;&apos; Set a new value to the given property
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; PropertyName: the name of the property as a string
+&apos;&apos;&apos; Value: its new value
+&apos;&apos;&apos; Exceptions
+&apos;&apos;&apos; ARGUMENTERROR The property does not exist
+
+Const cstThisSub = &quot;UnitTest.SetProperty&quot;
+Const cstSubArgs = &quot;PropertyName, Value&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ SetProperty = False
+
+Check:
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not ScriptForge.SF_Utils._Validate(PropertyName, &quot;PropertyName&quot;, V_STRING, Properties()) Then GoTo Catch
+ End If
+
+Try:
+ SetProperty = _PropertySet(PropertyName, Value)
+
+Finally:
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFUnitTests.SF_UnitTest.SetProperty
+
+REM -----------------------------------------------------------------------------
+Public Function SkipTest(Optional ByVal Message As Variant) As Boolean
+&apos;&apos;&apos; Interrupt the running test suite. The TearDown() method is NOT executed.
+&apos;&apos;&apos; The SkipTest() method is normally meaningful only in a Setup() method when not all the
+&apos;&apos;&apos; conditions to run the test are met.
+&apos;&apos;&apos; It is up to the Setup() script to exit shortly after the SkipTest() call..
+&apos;&apos;&apos; The method may also be executed in a test case. Next test cases will not be executed.
+&apos;&apos;&apos; Remember however that the test cases are executed is an arbitrary order.
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Message: the message to be displayed in the console
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; True when successful
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; GlobalScope.BasicLibraries.loadLibrary(&quot;ScriptForge&quot;)
+&apos;&apos;&apos; Dim test : test = CreateScriptService(&quot;UnitTest&quot;, ThisComponent, &quot;Tests&quot;)
+&apos;&apos;&apos; test.SkipTest(&quot;AllTests&quot;) &apos; AllTests is a module name ; test cases are named &quot;Test_*&quot; (default)
+
+Dim bSkip As Boolean &apos; Return value
+Dim sSkipMessage As String &apos; Reporting
+
+Const cstThisSub = &quot;UnitTest.SkipTest&quot;
+Const cstSubArgs = &quot;[Message=&quot;&quot;&quot;&quot;]&quot;
+
+ bSkip = False
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+
+Check:
+ If IsMissing(Message) Or IsEmpty(Message) Then Message = &quot;&quot;
+ ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) &apos; Unconditional !
+ If Not ScriptForge.SF_Utils._Validate(Message, &quot;Message&quot;, V_STRING) Then GoTo Catch
+
+ &apos; A SkipTest() is forbidden when simple mode
+ If _ExecutionMode &lt;&gt; FULLMODE Then GoTo CatchMethod
+
+ &apos; Ignore any call when an abnormal end has been encountered
+ If _ReturnCode = RCABORTTEST Then GoTo Catch
+
+Try:
+ If _Status = STATUSSETUP Or _Status = STATUSTESTCASE Then
+ _ReturnCode = RCSKIPTEST
+ bSkip = True
+ &apos; Exit message
+ sSkipMessage = &quot; SKIPTEST testsuite=&apos;&quot; &amp; LibraryName &amp; &quot;.&quot; &amp; _Module &amp; &quot;&apos; &quot; &amp; _Duration(&quot;Suite&quot;, True)
+ _ReportMessage(sSkipMessage, Message)
+ End If
+
+Finally:
+ SkipTest = bSkip
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ _ReturnCode = RCABORTTEST
+ GoTo Finally
+CatchMethod:
+ ScriptForge.SF_Exception.RaiseFatal(UNITTESTMETHODERROR, &quot;SkipTest&quot;)
+ GoTo Catch
+End Function &apos; SFUnitTests.SF_UnitTest.SkipTest
+
+REM =========================================================== PRIVATE FUNCTIONS
+
+REM -----------------------------------------------------------------------------
+Private Function _Assert(ByVal psAssert As String _
+ , ByVal pvReturn As Variant _
+ , ByRef A As Variant _
+ , ByRef B As Variant _
+ , Optional ByVal pvMessage As Variant _
+ , Optional ByVal pvArg As Variant _
+ ) As Boolean
+&apos;&apos;&apos; Evaluation of the assertion and management of the success or the failure
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; psAssert: the assertion verb as a string
+&apos;&apos;&apos; pvReturn: may be True, False or Empty
+&apos;&apos;&apos; When True (resp. False), the assertion must be evaluated as True (resp. False)
+&apos;&apos;&apos; e.g. AssertEqual() will call _Assert(&quot;AssertEqual&quot;, True, ...)
+&apos;&apos;&apos; AssertNotEqual() will call _Assert(&quot;AssertNotEqual&quot;, False, ...)
+&apos;&apos;&apos; Empty may be used for recursive calls of the function (for comparing arrays, ...)
+&apos;&apos;&apos; A: always present
+&apos;&apos;&apos; B: may be empty
+&apos;&apos;&apos; pvMessage: the message to display on the console
+&apos;&apos;&apos; pvArg: optional additional argument of the assert function
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; True when success
+
+Dim bAssert As Boolean &apos; Return value
+Dim bEval As Boolean &apos; To be compared with pvReturn
+Dim iVarTypeA As Integer &apos; Alias of _VarTypeExt(A)
+Dim iVarTypeB As Integer &apos; Alias of _VarTypeExt(B)
+Dim oVarTypeObjA As Object &apos; SF_Utils.ObjectDescriptor
+Dim oVarTypeObjB As Object &apos; SF_Utils.ObjectDescriptor
+Dim oUtils As Object : Set oUtils = ScriptForge.SF_Utils
+Dim iDims As Integer &apos; Number of dimensions of array
+Dim oAliasB As Object &apos; Alias of B to bypass the &quot;Object variable not set&quot; issue
+Dim dblA As Double &apos; Alias of A
+Dim dblB As Double &apos; Alias of B
+Dim dblTolerance As Double &apos; Alias of pvArg
+Dim oString As Object : Set oString = ScriptForge.SF_String
+Dim sArgName As String &apos; Argument description
+Dim i As Long, j As Long
+
+Check:
+ bAssert = False
+ If IsMissing(pvMessage) Then pvMessage = &quot;&quot;
+ If Not oUtils._Validate(pvMessage, &quot;Message&quot;, V_STRING) Then GoTo Finally
+ If IsMissing(pvArg) Then pvArg = &quot;&quot;
+
+Try:
+ iVarTypeA = oUtils._VarTypeExt(A)
+ iVarTypeB = oUtils._VarTypeExt(B)
+ sArgName = &quot;&quot;
+
+ Select Case UCase(psAssert)
+ Case UCase(&quot;AssertAlmostEqual&quot;), UCase(&quot;AssertNotAlmostEqual&quot;)
+ bEval = ( iVarTypeA = iVarTypeB And iVarTypeA = ScriptForge.V_NUMERIC )
+ If bEval Then
+ dblA = CDbl(A)
+ dblB = CDbl(B)
+ dblTolerance = Abs(CDbl(pvArg))
+ bEval = ( Abs(dblA - dblB) &lt;= (dblTolerance * Iif(Abs(dblA) &gt; Abs(DblB), Abs(dblA), Abs(dblB))) )
+ End If
+ Case UCase(&quot;AssertEqual&quot;), UCase(&quot;AssertNotEqual&quot;)
+ If Not IsArray(A) Then
+ bEval = ( iVarTypeA = iVarTypeB )
+ If bEval Then
+ Select Case iVarTypeA
+ Case V_EMPTY, V_NULL
+ Case V_STRING
+ bEval = ( StrComp(A, B, 1) = 0 )
+ Case ScriptForge.V_NUMERIC, ScriptForge.V_BOOLEAN
+ bEval = ( A = B )
+ Case V_DATE
+ bEval = ( Abs(DateDiff(&quot;s&quot;, A, B)) = 0 )
+ Case ScriptForge.V_OBJECT
+ Set oVarTypeObjA = oUtils._VarTypeObj(A)
+ Set oVarTypeObjB = oUtils._VarTypeObj(B)
+ bEval = ( oVarTypeObjA.iVarType = oVarTypeObjB.iVarType )
+ If bEval Then
+ Select Case oVarTypeObjA.iVarType
+ Case ScriptForge.V_NOTHING
+ Case ScriptForge.V_UNOOBJECT
+ bEval = EqualUnoObjects(A, B)
+ Case ScriptForge.V_SFOBJECT, ScriptForge.V_BASICOBJECT
+ bEval = False
+ End Select
+ End If
+ End Select
+ End If
+ Else &apos; Compare arrays
+ bEval = IsArray(B)
+ If bEval Then
+ iDims = ScriptForge.SF_Array.CountDims(A)
+ bEval = ( iDims = ScriptForge.SF_Array.CountDims(B) And iDims &lt;= 2 )
+ If bEval Then
+ Select Case iDims
+ Case -1, 0 &apos; Scalars (not possible) or empty arrays
+ Case 1 &apos; 1D array
+ bEval = ( LBound(A) = LBound(B) And UBound(A) = UBound(B) )
+ If bEval Then
+ For i = LBound(A) To UBound(A)
+ bEval = _Assert(psAssert, Empty, A(i), B(i))
+ If Not bEval Then Exit For
+ Next i
+ End If
+ Case 2 &apos; 2D array
+ bEval = ( LBound(A, 1) = LBound(B, 1) And UBound(A, 1) = UBound(B, 1) _
+ And LBound(A, 2) = LBound(B, 2) And UBound(A, 2) = UBound(B, 2) )
+ If bEval Then
+ For i = LBound(A, 1) To UBound(A, 1)
+ For j = LBound(A, 2) To UBound(A, 2)
+ bEval = _Assert(psAssert, Empty, A(i, j), B(i, j))
+ If Not bEval Then Exit For
+ Next j
+ If Not bEval Then Exit For
+ Next i
+ End If
+ End Select
+ End If
+ End If
+ End If
+ Case UCase(&quot;AssertFalse&quot;)
+ If iVarTypeA = ScriptForge.V_BOOLEAN Then bEval = Not A Else bEval = False
+ Case UCase(&quot;AssertGreater&quot;), UCase(&quot;AssertLessEqual&quot;)
+ bEval = ( iVarTypeA = iVarTypeB _
+ And (iVarTypeA = ScriptForge.V_NUMERIC Or iVarTypeA = V_STRING Or iVarTypeA = V_DATE) )
+ If bEval Then bEval = ( A &gt; B )
+ Case UCase(&quot;AssertGreaterEqual&quot;), UCase(&quot;AssertLess&quot;)
+ bEval = ( iVarTypeA = iVarTypeB _
+ And (iVarTypeA = ScriptForge.V_NUMERIC Or iVarTypeA = V_STRING Or iVarTypeA = V_DATE) )
+ If bEval Then bEval = ( A &gt;= B )
+ Case UCase(&quot;AssertIn&quot;), UCase(&quot;AssertNotIn&quot;)
+ Set oVarTypeObjB = oUtils._VarTypeObj(B)
+ Select Case True
+ Case iVarTypeA = V_STRING And iVarTypeB = V_STRING
+ bEval = ( Len(A) &gt; 0 And Len(B) &gt; 0 )
+ If bEval Then bEval = ( InStr(1, B, A, 0) &gt; 0 )
+ Case (iVarTypeA = V_DATE Or iVarTypeA = V_STRING Or iVarTypeA = ScriptForge.V_NUMERIC) _
+ And iVarTypeB &gt;= ScriptForge.V_ARRAY
+ bEval = ( ScriptForge.SF_Array.CountDims(B) = 1 )
+ If bEval Then bEval = ScriptForge.SF_Array.Contains(B, A, CaseSensitive := True)
+ Case oVarTypeObjB.iVarType = ScriptForge.V_SFOBJECT And oVarTypeObjB.sObjectType = &quot;DICTIONARY&quot;
+ bEval = ( Len(A) &gt; 0 )
+ If bEval Then
+ Set oAliasB = B
+ bEval = ScriptForge.SF_Array.Contains(oAliasB.Keys(), A, CaseSensitive := True)
+ End If
+ Case Else
+ bEval = False
+ End Select
+ Case UCase(&quot;AssertIsInstance&quot;), UCase(&quot;AssertNotInstance&quot;)
+ Set oVarTypeObjA = oUtils._VarTypeObj(A)
+ sArgName = &quot;ObjectType&quot;
+ With oVarTypeObjA
+ Select Case .iVarType
+ Case ScriptForge.V_UNOOBJECT
+ bEval = ( pvArg = .sObjectType )
+ Case ScriptForge.V_SFOBJECT
+ bEval = ( UCase(pvArg) = UCase(.sObjectType) Or UCase(pvArg) = &quot;SF_&quot; &amp; UCase(.sObjectType) _
+ Or UCase(pvArg) = UCase(.sServiceName) )
+ Case ScriptForge.V_NOTHING, ScriptForge.V_BASICOBJECT
+ bEval = False
+ Case &gt;= ScriptForge.V_ARRAY
+ bEval = ( UCase(pvArg) = &quot;ARRAY&quot; )
+ Case Else
+ bEval = ( UCase(TypeName(A)) = UCase(pvArg) )
+ End Select
+ End With
+ Case UCase(&quot;AssertIsNothing&quot;), UCase(&quot;AssertNotNothing&quot;)
+ bEval = ( iVarTypeA = ScriptForge.V_OBJECT )
+ If bEval Then bEval = ( A Is Nothing )
+ Case UCase(&quot;AssertIsNull&quot;), UCase(&quot;AssertNotNull&quot;)
+ bEval = ( iVarTypeA = V_NULL )
+ Case UCase(&quot;AssertLike&quot;), UCase(&quot;AssertNotLike&quot;)
+ sArgName = &quot;Pattern&quot;
+ bEval = ( iVarTypeA = V_STRING And Len(pvArg) &gt; 0 )
+ If bEval Then bEval = oString.IsLike(A, pvArg, CaseSensitive := True)
+ Case UCase(&quot;AssertRegex&quot;), UCase(&quot;AssertNotRegex&quot;)
+ sArgName = &quot;Regex&quot;
+ bEval = ( iVarTypeA = V_STRING And Len(pvArg) &gt; 0 )
+ If bEval Then bEval = oString.IsRegex(A, pvArg, CaseSensitive := True)
+ Case UCase(&quot;AssertTrue&quot;)
+ If iVarTypeA = ScriptForge.V_BOOLEAN Then bEval = A Else bEval = False
+ Case UCase(&quot;FAIL&quot;), UCase(&quot;Log&quot;)
+ bEval = True
+ Case Else
+ End Select
+
+ &apos; Check the result of the assertion vs. what it should be
+ If IsEmpty(pvReturn) Then
+ bAssert = bEval &apos; Recursive call =&gt; Reporting and failure management are done by calling _Assert() procedure
+ Else &apos; pvReturn is Boolean =&gt; Call from user script
+ bAssert = Iif(pvReturn, bEval, Not bEval)
+ &apos; Report the assertion evaluation
+ If _Verbose Or Not bAssert Then
+ _ReportMessage(&quot; &quot; &amp; psAssert _
+ &amp; Iif(IsEmpty(A), &quot;&quot;, &quot; = &quot; &amp; bAssert &amp; &quot;, A = &quot; &amp; oUtils._Repr(A)) _
+ &amp; Iif(IsEmpty(B), &quot;&quot;, &quot;, B = &quot; &amp; oUtils._Repr(B)) _
+ &amp; Iif(Len(sArgName) = 0, &quot;&quot;, &quot;, &quot; &amp; sArgName &amp; &quot; = &quot; &amp; pvArg) _
+ , pvMessage)
+ End If
+ &apos; Manage assertion failure
+ If Not bAssert Then
+ _FailedAssert = psAssert
+ Select Case _WhenAssertionFails
+ Case FAILIGNORE &apos; Do nothing
+ Case Else
+ _ReturnCode = RCASSERTIONFAILED
+ &apos; Cause artificially a run-time error
+ Dim STRINGBADUSE As String
+
+ &apos;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ &apos;+ To avoid a run-time error on next executable statement, +
+ &apos;+ insert an error handler in the code of your test case: +
+ &apos;+ Like in next code: +
+ &apos;+ On Local Error GoTo Catch +
+ &apos;+ ... +
+ &apos;+ Catch: +
+ &apos;+ myTest.ReportError() +
+ &apos;+ Exit Sub +
+ &apos;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+ STRINGBADUSE = Right(&quot;&quot;, -1) &apos; Raises &quot;#5 - Invalid procedure call&quot; error
+
+ End Select
+ End If
+ End If
+
+Finally:
+ _Assert = bAssert
+ Exit Function
+
+End Function &apos; SFUnitTests.SF_UnitTest._Assert
+
+REM -----------------------------------------------------------------------------
+Private Function _Duration(ByVal psTimer As String _
+ , Optional ByVal pvBrackets As Variant _
+ ) As String
+&apos;&apos;&apos; Return the Duration property of the given timer
+&apos;&apos;&apos; or the empty string if the timer is undefined or not started
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; psTimer: &quot;Test&quot;, &quot;Suite&quot; or &quot;TestCase&quot;
+&apos;&apos;&apos; pbBrackets: surround with brackets when True. Default = False
+
+Dim sDuration As String &apos; Return value
+Dim oTimer As Object &apos; Alias of psTimer
+
+Check:
+ If IsMissing(pvBrackets) Or IsEmpty(pvBrackets) Then pvBrackets = False
+
+Try:
+ Select Case psTimer
+ Case &quot;Test&quot; : Set oTimer = TestTimer
+ Case &quot;Suite&quot; : Set oTimer = SuiteTimer
+ Case &quot;TestCase&quot;, &quot;Case&quot; : Set oTimer = CaseTimer
+ End Select
+ If Not IsNull(oTimer) Then
+ sDuration = CStr(oTimer.Duration) &amp; &quot; &quot;
+ If pvBrackets Then sDuration = &quot;(&quot; &amp; Trim(sDuration) &amp; &quot; sec)&quot;
+ Else
+ sDuration = &quot;&quot;
+ End If
+
+Finally:
+ _Duration = sDuration
+End Function &apos; SFUnitTests.SF_UnitTest._Duration
+
+REM -----------------------------------------------------------------------------
+Private Function _ExecuteScript(psMethod As String) As Boolean
+&apos;&apos;&apos; Run the given method and report start and stop
+&apos;&apos;&apos; The targeted method is presumed not to return anything (Sub)
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; psMethod: the scope, the library and the module are predefined in the instance internals
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; True when successful
+
+Dim bExecute As Boolean &apos; Return value
+Dim sRun As String &apos; SETUP, TEARDOWN or TESTCASE
+
+ On Local Error GoTo Catch
+ bExecute = True
+
+Try:
+ &apos; Set status before the effective execution
+ sRun = UCase(psMethod)
+ Select Case UCase(psMethod)
+ Case &quot;SETUP&quot; : _Status = STATUSSETUP
+ Case &quot;TEARDOWN&quot; : _Status = STATUSTEARDOWN
+ Case Else : _Status = STATUSTESTCASE
+ sRun = &quot;TESTCASE&quot;
+ End Select
+
+ &apos; Report and execute
+ _ReportMessage(&quot; &quot; &amp; sRun &amp; &quot; &quot; &amp; LibraryName &amp; &quot;.&quot; &amp; _Module &amp; &quot;.&quot; &amp; psMethod &amp; &quot;() ENTER&quot;)
+ Session.ExecuteBasicScript(Scope, LibraryName &amp; &quot;.&quot; &amp; _Module &amp; &quot;.&quot; &amp; psMethod, [Me])
+ _ReportMessage(&quot; &quot; &amp; sRun &amp; &quot; &quot; &amp; LibraryName &amp; &quot;.&quot; &amp; _Module &amp; &quot;.&quot; &amp; psMethod &amp; &quot;() EXIT&quot; _
+ &amp; Iif(_STATUS = STATUSTESTCASE, &quot; &quot; &amp; _Duration(&quot;Case&quot;, True), &quot;&quot;))
+ &apos; Reset status
+ _Status = STATUSSUITESTARTED
+
+Finally:
+ _ExecuteScript = bExecute
+ Exit Function
+Catch:
+ bExecute = False
+ _ReturnCode = RCABORTTEST
+ GoTo Finally
+End Function &apos; SFUnitTests.SF_UnitTest._ExecuteScript
+
+REM -----------------------------------------------------------------------------
+Private Function _PropertyGet(Optional ByVal psProperty As String)
+&apos;&apos;&apos; Return the named property
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; psProperty: the name of the property
+
+Dim cstThisSub As String
+Dim cstSubArgs As String
+
+ cstThisSub = &quot;UnitTest.get&quot; &amp; psProperty
+ cstSubArgs = &quot;&quot;
+ SF_Utils._EnterFunction(cstThisSub, cstSubArgs)
+
+ Select Case UCase(psProperty)
+ Case UCase(&quot;LongMessage&quot;)
+ _PropertyGet = _LongMessage
+ Case UCase(&quot;ReturnCode&quot;)
+ _PropertyGet = _ReturnCode
+ Case UCase(&quot;Verbose&quot;)
+ _PropertyGet = _Verbose
+ Case UCase(&quot;WhenAssertionFails&quot;)
+ _PropertyGet = _WhenAssertionFails
+ Case Else
+ _PropertyGet = Null
+ End Select
+
+Finally:
+ SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+End Function &apos; SFUnitTests.SF_UnitTest._PropertyGet
+
+REM -----------------------------------------------------------------------------
+Private Function _PropertySet(Optional ByVal psProperty As String _
+ , Optional ByVal pvValue As Variant _
+ ) As Boolean
+&apos;&apos;&apos; Set the new value of the named property
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; psProperty: the name of the property
+&apos;&apos;&apos; pvValue: the new value of the given property
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; True if successful
+
+Dim bSet As Boolean &apos; Return value
+Dim vWhenFailure As Variant &apos; WhenAssertionFails allowed values
+Dim cstThisSub As String
+Const cstSubArgs = &quot;Value&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ bSet = False
+
+ cstThisSub = &quot;SFUnitTests.UnitTest.set&quot; &amp; psProperty
+ ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs)
+
+ bSet = True
+ Select Case UCase(psProperty)
+ Case UCase(&quot;LongMessage&quot;)
+ If Not ScriptForge.SF_Utils._Validate(pvValue, &quot;LongMessage&quot;, ScriptForge.V_BOOLEAN) Then GoTo Finally
+ _LongMessage = pvValue
+ Case UCase(&quot;Verbose&quot;)
+ If Not ScriptForge.SF_Utils._Validate(pvValue, &quot;Verbose&quot;, ScriptForge.V_BOOLEAN) Then GoTo Finally
+ _Verbose = pvValue
+ Case UCase(&quot;WhenAssertionFails&quot;)
+ If _ExecutionMode = SIMPLEMODE Then vWhenFailure = Array(0, 3) Else vWhenFailure = Array(0, 1, 2, 3)
+ If Not ScriptForge.SF_Utils._Validate(pvValue, &quot;WhenAssertionFails&quot;, ScriptForge.V_NUMERIC, vWhenFailure) Then GoTo Finally
+ _WhenAssertionFails = pvValue
+ Case Else
+ bSet = False
+ End Select
+
+Finally:
+ _PropertySet = bSet
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFUnitTests.SF_UnitTest._PropertySet
+
+REM -----------------------------------------------------------------------------
+Private Function _ReportMessage(ByVal psSysMessage As String _
+ , Optional ByVal pvMessage As Variant _
+ ) As Boolean
+&apos;&apos;&apos; Report in the console:
+&apos;&apos;&apos; - either the standard message
+&apos;&apos;&apos; - either the user message when not blank
+&apos;&apos;&apos; - or both
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; psSysMessage: the standard message as built by the calling routine
+&apos;&apos;&apos; psMessage: the message provided by the user script
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; True when successful
+
+Dim bReport As Boolean &apos; Return value
+Dim sIndent As String &apos; Indentation spaces
+
+ bReport = False
+ On Local Error GoTo Catch
+ If IsMissing(pvMessage) Or IsEmpty(pvMessage) Then pvMessage = &quot;&quot;
+
+Try:
+ Select Case True
+ Case Len(pvMessage) = 0
+ Exception.DebugPrint(psSysMessage)
+ Case _LongMessage
+ Exception.DebugPrint(psSysMessage, pvMessage)
+ Case Else
+ Select Case _Status
+ Case STATUSSTANDBY, STATUSSUITESTARTED : sIndent = &quot;&quot;
+ Case STATUSSUITESTARTED : sIndent = Space(2)
+ Case Else : sIndent = Space(4)
+ End Select
+ Exception.DebugPrint(sIndent &amp; pvMessage)
+ End Select
+
+Finally:
+ _ReportMessage = bReport
+ Exit Function
+Catch:
+ bReport = False
+ GoTo Finally
+End Function &apos; SFUnitTests.SF_UnitTest._ReportMessage
+
+REM -----------------------------------------------------------------------------
+Private Function _Repr() As String
+&apos;&apos;&apos; Convert the UnitTest instance to a readable string, typically for debugging purposes (DebugPrint ...)
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Return:
+&apos;&apos;&apos; &quot;[UnitTest]
+
+Const cstUnitTest = &quot;[UnitTest]&quot;
+Const cstMaxLength = 50 &apos; Maximum length for items
+
+ _Repr = cstUnitTest
+
+End Function &apos; SFUnitTests.SF_UnitTest._Repr
+
+REM ============================================== END OF SFUNITTESTS.SF_UNITTEST
+</script:module> \ No newline at end of file
diff --git a/wizards/source/sfunittests/__License.xba b/wizards/source/sfunittests/__License.xba
new file mode 100644
index 000000000..a8e6a7779
--- /dev/null
+++ b/wizards/source/sfunittests/__License.xba
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE script:module PUBLIC "-//OpenOffice.org//DTD OfficeDocument 1.0//EN" "module.dtd">
+<script:module xmlns:script="http://openoffice.org/2000/script" script:name="__License" script:language="StarBasic" script:moduleType="normal">
+&apos;&apos;&apos; Copyright 2019-2022 Jean-Pierre LEDURE, Rafael LIMA, Alain ROMEDENNE
+
+REM =======================================================================================================================
+REM === The ScriptForge library and its associated libraries are part of the LibreOffice project. ===
+REM === The SFUnitTests library is one of the associated libraries. ===
+REM === Full documentation is available on https://help.libreoffice.org/ ===
+REM =======================================================================================================================
+
+&apos;&apos;&apos; ScriptForge is distributed in the hope that it will be useful,
+&apos;&apos;&apos; but WITHOUT ANY WARRANTY; without even the implied warranty of
+&apos;&apos;&apos; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+&apos;&apos;&apos; ScriptForge is free software; you can redistribute it and/or modify it under the terms of either (at your option):
+
+&apos;&apos;&apos; 1) The Mozilla Public License, v. 2.0. If a copy of the MPL was not
+&apos;&apos;&apos; distributed with this file, you can obtain one at http://mozilla.org/MPL/2.0/ .
+
+&apos;&apos;&apos; 2) The GNU Lesser General Public License as published by
+&apos;&apos;&apos; the Free Software Foundation, either version 3 of the License, or
+&apos;&apos;&apos; (at your option) any later version. If a copy of the LGPL was not
+&apos;&apos;&apos; distributed with this file, see http://www.gnu.org/licenses/ .
+
+</script:module> \ No newline at end of file
diff --git a/wizards/source/sfunittests/dialog.xlb b/wizards/source/sfunittests/dialog.xlb
new file mode 100644
index 000000000..2d4a57045
--- /dev/null
+++ b/wizards/source/sfunittests/dialog.xlb
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE library:library PUBLIC "-//OpenOffice.org//DTD OfficeDocument 1.0//EN" "library.dtd">
+<library:library xmlns:library="http://openoffice.org/2000/library" library:name="SFUnitTests" library:readonly="false" library:passwordprotected="false"/> \ No newline at end of file
diff --git a/wizards/source/sfunittests/script.xlb b/wizards/source/sfunittests/script.xlb
new file mode 100644
index 000000000..3292dc12c
--- /dev/null
+++ b/wizards/source/sfunittests/script.xlb
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE library:library PUBLIC "-//OpenOffice.org//DTD OfficeDocument 1.0//EN" "library.dtd">
+<library:library xmlns:library="http://openoffice.org/2000/library" library:name="SFUnitTests" library:readonly="false" library:passwordprotected="false">
+ <library:element library:name="__License"/>
+ <library:element library:name="SF_UnitTest"/>
+ <library:element library:name="SF_Register"/>
+</library:library> \ No newline at end of file