=== modified file 'build.gradle'
--- build.gradle	2022-10-07 10:32:51 +0000
+++ build.gradle	2022-10-27 21:14:39 +0000
@@ -103,6 +103,8 @@
 **     OM  20220812 Upgraded mariadb JDBC driver from 2.7.3 to 3.0.7 GA.
 **     TW  20220928 Changed junixsocket dependency to official repository, and upped its version to latest.
 **     TW  20221007 Upgraded junixsocket to 2.5.2.
+**     VVT 2022xxxx JUnit5 dependencies updated to support both existing tests and custom test engine.
+**                  See #6237.
 */
 
 /*
@@ -582,7 +584,24 @@
        aspectjCompile group: 'org.aspectj', name: 'aspectjweaver', version: aspectjVersion
     }
 
-    fwdAllTest group: 'org.junit.platform', name: 'junit-platform-console-standalone', version: '1.7.1'
+    // Adding JUnit5 jar both to client and server, so it will be also deployed to converted projects
+    fwdClientServer group: 'org.junit.platform',
+                    name: 'junit-platform-engine',
+                    version: '1.9.0'
+
+    fwdClientServer group: 'org.junit.jupiter',
+                    name: 'junit-jupiter-api',
+                    version: '5.9.0'
+
+    fwdClientServer group: 'org.junit.platform',
+                    name: 'junit-platform-suite-engine',
+                    version: '1.9.0'
+    fwdAllTest group: 'org.junit.platform',
+                    name: 'junit-platform-console',
+                    version: '1.9.0'
+    fwdAllTest group: 'org.junit.platform',
+                    name: 'junit-platform-engine',
+                    version: '1.9.0'
 }
 
 // inject ant-optional dependencies to ant, this is needed for the antlr ant target

=== modified file 'build.xml'
--- build.xml	2022-10-24 16:16:27 +0000
+++ build.xml	2022-10-25 16:19:33 +0000
@@ -236,6 +236,7 @@
 **     RFB 20221017          Move the p2jpl.jar out of the standard build, and into its own "p2jpl" target. This is
 **                           the first step in that jar's deprecation. Remove the FontTable class, too. Ref #6664.
 **     RFB 20221024          Tighten up the p2jpl build to not include extras that aren't needed.
+**     VVT 2022xxxx          Copy org.junit.platform.engine.TestEngine file to META-INF/services. See #6237.
 -->
 
 <!--
@@ -886,6 +887,7 @@
     <!-- Copy SPI descriptors to META-INF/services directory -->
     <copy todir="${build.home}/classes/META-INF/services">
       <fileset dir="${manifest.src}" includes="*Provider" />
+      <fileset dir="${manifest.src}" includes="*TestEngine" />
     </copy>
 
     <!-- Copy aop.xml to META-INF directory for LTW-specific AOP jar -->

=== added file 'manifest/org.junit.platform.engine.TestEngine'
--- manifest/org.junit.platform.engine.TestEngine	1970-01-01 00:00:00 +0000
+++ manifest/org.junit.platform.engine.TestEngine	2022-09-19 20:26:43 +0000
@@ -0,0 +1,1 @@
+com.goldencode.p2j.testengine.FWDTestEngine

=== modified file 'rules/annotations/annotations.xml'
--- rules/annotations/annotations.xml	2022-09-14 15:07:57 +0000
+++ rules/annotations/annotations.xml	2022-09-30 11:50:03 +0000
@@ -263,8 +263,8 @@
 **                           element.
 **     VVT 20220913          OEUnit support: annotations/unit_test_support added. See #6237.
 **     VVT 20220913          Prevent unsupported legacy annotations from going to Java code. See #6237.
-**     VVT 20220914          Changes in revs. 14243,14241,14239 temporarily reverted until JUnit runtime
-**                           support is added to client projects.
+**     VVT 2022xxxx          Two more rulesets processed: unit_test_support and legacy_annotations_post.
+**                           See #6237.
 */
  -->
  
@@ -678,8 +678,8 @@
    <rule-set name="annotations/method_defs" />
    <rule-set name="annotations/procedures" />
    <rule-set name="annotations/legacy_services" />
-   <!-- <rule-set name="annotations/unit_test_support" /> -->
-   <!-- <rule-set name="annotations/legacy_annotations_post" /> -->
+   <rule-set name="annotations/unit_test_support" />
+   <rule-set name="annotations/legacy_annotations_post" />
    <rule-set name="annotations/case_statements" />
    <rule-set name="annotations/input_output" />
    <rule-set name="annotations/assignment_style_stmt_rewriting" />

=== modified file 'rules/annotations/legacy_services.rules'
--- rules/annotations/legacy_services.rules	2022-09-14 15:07:57 +0000
+++ rules/annotations/legacy_services.rules	2022-09-30 11:51:48 +0000
@@ -21,8 +21,7 @@
 ** 005 VVT 20220913 OEUnit support: code removing all annotations removed. See #6237.
 **     VVT 20220913 Prevent unsupported legacy annotations from going to Java code. See #6237.
 **                  Use new create_java_annotation() function to create Java annotation nodes.
-**     VVT 20220914 Changes in revs. 14243,14241,14239 temporarily reverted until JUnit runtime
-**                  support is added to client projects.
+**     VVT 2022xxxx Changes in revs. 14243,14241,14239 restored. See #6237.
 */
  -->
  
@@ -212,9 +211,8 @@
             <while>iter.hasNext()
                <action>prest = iter.next()</action>
                
-               <action>ref = createProgressAst(prog.annotation, "@", pref)</action>
-               <action>ref.putAnnotation("javaname", "LegacyServiceParameter")</action>
-               <action>ref.putAnnotation("name", "LegacyServiceParameter")</action>
+               
+               <action>ref = execLib("create_java_annotation", pref, "LegacyServiceParameter", -1)</action>
                
                <rule>execLib("assign_field_int", ref, "ordinal", prest.ordinal())</rule>
                
@@ -308,7 +306,8 @@
             <rule>keep
                <action>hasSrvs = true</action>
                
-               <action on="false">ref.remove()</action>
+               <!-- Do NOT remove unsupported annotations here, they will be removed in legacy_annotations_post.rules -->
+               <!-- <action on="false">ref.remove()</action> -->
             </rule>
          </while>
       </function>
@@ -343,9 +342,7 @@
          
          <action>hasSrvs = true</action>
          
-         <action>ref = createProgressAst(prog.annotation, "@", copy, 1)</action>
-         <action>ref.putAnnotation("javaname", "LegacyService")</action>
-         <action>ref.putAnnotation("name", "LegacyService")</action>
+         <action>ref = execLib("create_java_annotation", copy, "LegacyService", 1)</action>
          
          <action>execLib("assign_field", ref, "type", "SOAP")</action>
          <action>execLib("assign_field", ref, "namespace", soapOp.getNamespace())</action>
@@ -401,9 +398,7 @@
             <action>soapOp = iter.next()</action>
             <action>hasSrvs = true</action>
 
-            <action>ref = createProgressAst(prog.annotation, "@", copy, 1)</action>
-            <action>ref.putAnnotation("javaname", "LegacyService")</action>
-            <action>ref.putAnnotation("name", "LegacyService")</action>
+            <action>ref = execLib("create_java_annotation", copy, "LegacyService", 1)</action>
             
             <action>execLib("assign_field", ref, "type", "SOAP")</action>
             <action>execLib("assign_field", ref, "namespace", soapOp.getNamespace())</action>
@@ -419,9 +414,7 @@
       <rule>this.type == prog.class_def and srv.isWebHandler(oname)
          <action>hasSrvs = true</action>
          
-         <action>ref = createProgressAst(prog.annotation, "@", copy, 1)</action>
-         <action>ref.putAnnotation("javaname", "LegacyService")</action>
-         <action>ref.putAnnotation("name", "LegacyService")</action>
+         <action>ref = execLib("create_java_annotation", copy, "LegacyService", 1)</action>
          
          <rule>execLib("assign_field", ref, "name", srv.getSection(oname))</rule>
          <rule>execLib("assign_field", ref, "type", "WEBHANDLER")</rule>
@@ -432,9 +425,7 @@
          <while>iter.hasNext()
             <action>path = iter.next()</action>
 
-            <action>ref = createProgressAst(prog.annotation, "@", pref)</action>
-            <action>ref.putAnnotation("javaname", "LegacyWebPath")</action>
-            <action>ref.putAnnotation("name", "LegacyWebPath")</action>
+            <action>ref = execLib("create_java_annotation", pref, "LegacyWebPath", -1)</action>
 
             <action>execLib("assign_field", ref, "path", path.path())</action>
             <action>execLib("assign_field_int", ref, "order", path.order())</action>

=== modified file 'rules/annotations/unit_test_support.rules'
--- rules/annotations/unit_test_support.rules	2022-09-13 16:57:43 +0000
+++ rules/annotations/unit_test_support.rules	2022-10-28 17:54:02 +0000
@@ -9,6 +9,8 @@
 ** _#_ _I_ __Date__  ___________________________Description____________________________
 ** 001 VVT 20220913  Initial version
 **     VVT 20220913  Prevent unsupported legacy annotations from going to Java code. See #6237.
+**     VVT 2022xxxx Custom test engine annotations are now used instead of that
+**                  from the Jupiter test engine. See #6237.
 */
  -->
 <!--
@@ -65,41 +67,231 @@
 -->
 <rule-set>
    <worker class="com.goldencode.p2j.uast.ProgressPatternWorker" namespace="prog" />
+   <worker class="com.goldencode.p2j.uast.UastHintsWorker"       namespace="hints" />
+   <worker class="com.goldencode.p2j.convert.ExpressionConversionWorker" namespace="ecw" />
+   
+   <func-library access="private">
+      <!--
+          Create an assignment <attName>=
+      -->
+      <function name="create_attribute_assignment_right">
+         <!-- The parent node -->
+         <parameter name="parent" type="com.goldencode.ast.Aast" />
+         <!-- the left part attribute name -->
+         <parameter name="attName" type="java.lang.String" />
+         
+         <return name="tref" type="com.goldencode.ast.Aast" />
+         
+         <variable name="chref" type="com.goldencode.ast.Aast" />
+         
+         <rule>tref = createProgressAst(prog.assign, "=", parent)</rule>
+         
+         <rule>chref = createProgressAst(prog.symbol, attName, tref)</rule>
+         <rule>chref.putAnnotation("javaname", attName)</rule>
+      </function>
+   
+      <!--
+          Create an array assignment <attName>=[
+      -->
+      <function name="create_attribute_array_assignment_right">
+         <parameter name="parent" type="com.goldencode.ast.Aast" />
+         <parameter name="attName" type="java.lang.String" />
+         
+         <return name="tref" type="com.goldencode.ast.Aast" />
+         
+         <rule>tref = execLib("create_attribute_assignment_right", parent, attName)</rule>
+         <rule>tref = createProgressAst(prog.lbracket, "[", tref)</rule>
+      </function>
+
+
+      <!--
+          Lookup legacy annotation attribute value
+
+          Input conditions:
+          1. current node is the legacy annotation node
+          2. The attName parameter contains the name of the attribute to lookup
+          
+          Return: attribute value string of null if this annotation has no attribute with the given name.
+      -->
+      <function name="lookup_annotation_attribute_string">
+         <parameter name="attName" type="java.lang.String" />
+         <return name="result" type="java.lang.String" />
+         
+         <rule>result = null</rule>
+         
+         <variable name="attAssignment" type="com.goldencode.ast.Aast" />
+         <!-- Skip the annotation name SYMBOL -->
+         <rule>attAssignment = copy.firstChild.nextSibling</rule>
+         <while>attAssignment != null
+           <!-- Go to the second child, which must be attribute assignment -->
+           <rule>attAssignment.type == prog.assign
+              <action on="false">compileError(367, "Annotation children must be ASSIGNMENTs")</action>
+           </rule>
+           <variable name="attNameNode" type="com.goldencode.ast.Aast" />
+           <action>attNameNode = attAssignment.firstChild</action>
+           <rule>attNameNode.getAnnotation("javaname").equals(attName)
+              <action>result = ecw.stripQuotes(attNameNode.nextSibling.getText())</action>
+              <break/>
+           </rule>
+           <action>attAssignment = attAssignment.nextSibling</action>
+         </while>      
+      </function>
+
+      <function name="create_java_annotation">
+         <parameter name="parent" type="com.goldencode.ast.Aast" />
+         <parameter name="className" type="java.lang.String" />
+         <return name="selectClassesAnno" type="com.goldencode.ast.Aast" />
+         <rule>selectClassesAnno = createProgressAst(prog.annotation, "@", parent)</rule>
+         <rule>selectClassesAnno.putAnnotation("javaname", className)</rule>
+         <!-- ...and mark "our" annotations as preserved -->
+         <rule>selectClassesAnno.putAnnotation("fwd", true)</rule>
+      </function>
+      <!--
+          Process ABLUnit @TestSuite annotation:
+          1. If the suite includes classes, then create a @SelectClasses(value = {Class1.class, Class2...}) annotation
+          2. If the suite includes procedures, then create a @SelectProcedures(value = {"procedure1.p", "procedure2.p"...}) annotation
+          3. Create an empty @Suite annotation
+
+          On entry the current node is the annotation.
+      -->
+      <function name="process_ablunit_test_suite">
+         <!-- Create a marker @Suite annotation-->
+         <rule>execLib("create_java_annotation", copy.parent, "com.goldencode.p2j.testengine.api.Suite")</rule>
+         
+         <!-- Get the "classes" attribute, convert the quoted comma-separated list of class
+              names to nodes of prog.symbol type -->
+         <variable name="classList" type="java.lang.String" />
+         <rule>classList = execLib("lookup_annotation_attribute_string", "classes")</rule>
+         
+         <variable name="lref" type="com.goldencode.ast.Aast" />
+         <rule>classList != null
+            <action>printfln("@TestSuite Classlist %s", classList)</action>
+
+            <!-- Create the @SelectClasses annotation -->
+            <variable name="selectClassesAnno" type="com.goldencode.ast.Aast" />
+            <action>selectClassesAnno = execLib("create_java_annotation", copy.parent, "com.goldencode.p2j.testengine.api.SelectClasses")</action>
+            
+            <!-- Convert comma-separated class name list string into an array of class references -->
+            <!--   Create a node of the array type -->
+            <action>lref = execLib("create_attribute_array_assignment_right", selectClassesAnno, "value")</action>
+            <!-- Parse and convert the comma-separated list -->
+            <variable name="classNameList" type="java.util.List" />
+            <action>classNameList = createListFromObject(classList.split(","))</action>
+            <variable name="classNames" type="java.util.Iterator" />
+            <action>classNames = classNameList.iterator()</action>
+            <while>classNames.hasNext()
+               <variable name="className" type="java.lang.String" />
+               <action>className = (#(java.lang.String)classNames.next()).trim()</action>
+               <action>printfln("Class name '%s'", className)</action>                                       
+               <action>createProgressAst(prog.symbol, sprintf("%s.class", className), lref)</action>
+            </while>
+         </rule>
+
+         <!-- Get the "procedures" attribute, convert the quoted comma-separated list of procedure
+              names to nodes of prog.symbol type -->
+         <variable name="procedureList" type="java.lang.String" />
+         <rule>procedureList = execLib("lookup_annotation_attribute_string", "procedures")</rule>
+         
+         <rule>procedureList != null
+            <action>printfln("@TestSuite procedures %s", procedureList)</action>
+
+            <!-- Create the @SelectProcedures annotation -->
+            <variable name="selectProceduresAnno" type="com.goldencode.ast.Aast" />
+            <action>selectProceduresAnno = execLib("create_java_annotation", copy.parent, "com.goldencode.p2j.testengine.api.SelectProcedures")</action>
+            <!-- Convert comma-separated procedure name list string into an array of procedure name strings -->
+            <!--   Create a node of the array type -->
+            <action>lref = execLib("create_attribute_array_assignment_right", selectProceduresAnno, "value")</action>
+            <!-- Parse and convert the comma-separated list -->
+            <variable name="procedureNameList" type="java.util.List" />
+            <action>procedureNameList = createListFromObject(procedureList.split(","))</action>
+            <variable name="procedureNames" type="java.util.Iterator" />
+            <action>procedureNames = procedureNameList.iterator()</action>
+            <while>procedureNames.hasNext()
+               <variable name="procedureName" type="java.lang.String" />
+               <action>procedureName = (#(java.lang.String)procedureNames.next()).trim()</action>
+               <action>printfln("Procedure name '%s'", procedureName)</action>                                       
+               <action>createProgressAst(prog.string, sprintf("'%s'", procedureName), lref)</action>
+            </while>
+         </rule>
+         <!-- Drop the original value -->
+         <rule>copy.remove()</rule>
+      </function>
+   </func-library>
+
    <walk-rules>
       <rule>this.type == prog.annotation
-            and parent.type == prog.method_def
+            and (parent.type == prog.method_def
+                 or parent.type == prog.procedure
+                 or parent.type == prog.class_def)
+            
+         <!-- Get required unit test legacy type configuration (hints) -->
+         <variable name="unitTestType" type="java.lang.String" />
+         <action>unitTestType = hints.getUnitTestType()</action>
+         <rule>unitTestType == null
+            <action>compileError(223, "No unit test legacy type is configured")</action>
+         </rule>
+         
+         <variable name="isABLUnit" type="java.lang.Boolean" />
+         <action>isABLUnit = unitTestType.equals("ABLUnit")</action>
+
+         <variable name="methodNode" type="com.goldencode.ast.Aast" />
+         <action>methodNode = parent.firstChild</action>
+
+         <!-- FIXME: Add similar support for procedures -->
 
          <!-- Get initial annotation name -->
          <variable name="annoName" type="java.lang.String" />
          <action>annoName = this.getAnnotation("javaname")</action>
 
+         <action>printfln("method %s is ABL unit test type %s annoName @%s", methodNode.getAnnotation("name"), isABLUnit, annoName)</action>
+
          <variable name="newName" type="java.lang.String" />
          <action>newName = null</action>
 
-         <!-- Replace OEUnit annotation names (made after JUnit 4) by JUnit 5 names -->
-         <rule>annoName.equals("Test")
-            <action>newName = "org.junit.jupiter.api.Test"</action>
-            <rule on="false">annoName.equals("Before")
-               <action>newName = "org.junit.jupiter.api.BeforeEach"</action>
-               <rule on="false">annoName.equals("After")
-                  <action>newName = "org.junit.jupiter.api.AfterEach"</action>
-                  <rule on="false">annoName.equals("BeforeClass")
-                     <action>newName = "org.junit.jupiter.api.BeforeAll"</action>
-                     <rule on="false">annoName.equals("AfterClass")
-                        <action>newName = "org.junit.jupiter.api.AfterAll"</action>
-                        <rule on="false">annoName.equals("Ignore")
-                           <action>newName = "org.junit.jupiter.api.Disabled"</action>
+         <!-- Replace OEUnit/ABLUnit annotation names by corresponding JUnit5 names -->
+         <rule>annoName.equalsIgnoreCase("Test")
+            <action>newName = "com.goldencode.p2j.testengine.api.Test"</action>
+
+            <!-- Note: the meaning of @Before and @After is opposite in ABLUnit and OEUnit -->
+            <rule on="false">annoName.equalsIgnoreCase("Before")
+               <rule>isABLUnit
+                  <action>newName = "com.goldencode.p2j.testengine.api.BeforeAll"</action>
+                  <action on="false">newName = "com.goldencode.p2j.testengine.api.BeforeEach"</action>
+               </rule>
+               <rule on="false">annoName.equalsIgnoreCase("After")
+                  <rule>isABLUnit
+                     <action>newName = "com.goldencode.p2j.testengine.api.AfterAll"</action>
+                     <action on="false">newName = "com.goldencode.p2j.testengine.api.AfterEach"</action>
+                  </rule>
+                  <rule on="false">annoName.equalsIgnoreCase("BeforeClass")
+                     <action>newName = "com.goldencode.p2j.testengine.api.BeforeAll"</action>
+                     <rule on="false">annoName.equalsIgnoreCase("AfterClass")
+                        <action>newName = "com.goldencode.p2j.testengine.api.AfterAll"</action>
+                        <rule on="false">annoName.equalsIgnoreCase("Ignore")
+                           <action>newName = "com.goldencode.p2j.testengine.api.Disabled"</action>
+                           <rule on="false">annoName.equalsIgnoreCase("Setup")
+                              <action>newName = "com.goldencode.p2j.testengine.api.BeforeEach"</action>
+                              <rule on="false">annoName.equalsIgnoreCase("TearDown")
+                                 <action>newName = "com.goldencode.p2j.testengine.api.AfterEach"</action>
+   
+                                 <!-- ABLUnit @TestSuite(classes="classname1,classname2...", procedures="procedure1.p,procedure2.p...") -->
+                                 <rule on="false">annoName.equalsIgnoreCase("TestSuite")
+                                    <action>execLib("process_ablunit_test_suite")</action>
+                                 </rule>
+                              </rule>
+                           </rule>
                         </rule>
                      </rule>
                   </rule>
                </rule>
             </rule>
          </rule>
-
          <rule>newName != null
-           <!-- Write it back and mark "our" annotations as preserved -->
-           <action>copy.putAnnotation("javaname", newName)</action>
-           <action>copy.putAnnotation("fwd", true)</action>
+            <action>printfln("newName %s", newName)</action>
+            <!-- Write it back... -->
+            <action>copy.putAnnotation("javaname", newName)</action>
+            <!-- ...and mark "our" annotations as preserved -->
+            <action>copy.putAnnotation("fwd", true)</action>
          </rule>
       </rule>
       

=== modified file 'rules/convert/core_conversion.xml'
--- rules/convert/core_conversion.xml	2022-09-14 15:07:57 +0000
+++ rules/convert/core_conversion.xml	2022-09-30 11:59:12 +0000
@@ -71,6 +71,7 @@
 ** 026 VVT 20220913          OEUnit support: convert/unit_test_support added. See #6237.
 **     VVT 20220914          Changes in revs. 14243,14241,14239 temporarily reverted until JUnit runtime
 **                           support is added to client projects.
+**     VVT 2022xxxx          Changes in revs. 14243,14241,14239 restored. See #6237.
 */
  -->
  
@@ -184,7 +185,7 @@
         Progress source constructs into the equivalent Java constructs
         rooted in the Java AST -->
    <rule-set honor-hidden="true" input="tree" load-condition="runtime-query">
-      <!-- <rule-set name="convert/unit_test_support" /> -->
+      <rule-set name="convert/unit_test_support" />
       <rule-set name="convert/variable_definitions" />
       <!-- the order of the following rules is important - method_definitions' ascent-rules will
            emit the signature's parameter annotations -->

=== modified file 'rules/convert/unit_test_support.rules'
--- rules/convert/unit_test_support.rules	2022-09-13 16:57:43 +0000
+++ rules/convert/unit_test_support.rules	2022-10-18 18:36:43 +0000
@@ -9,6 +9,8 @@
 ** _#_ _I_ __Date__  ___________________________Description____________________________
 ** 001 VVT 20220913  Initial version
 **     VVT 20220913  Prevent unsupported legacy annotations from going to Java code. See #6237.
+**     VVT 2022xxxx  Custom test engine annotations are now used instead of that
+**                   from the Jupiter test engine. See #6237.
 */
  -->
 <!--
@@ -66,30 +68,32 @@
 <rule-set>
    <worker class="com.goldencode.p2j.convert.ConverterHelper" namespace="help" />
    <worker class="com.goldencode.p2j.uast.ProgressPatternWorker" namespace="prog" />
+   <worker class="com.goldencode.p2j.convert.ExpressionConversionWorker" namespace="ecw" />
+   
    <variable name="hasJUnit5Annotations" type="java.lang.Boolean"/>
 
    <!-- Detect if any of JUnit 5 annotations exist in the tree -->
    <walk-rules>
       <rule>this.type == prog.annotation
-            and parent.type == prog.method_def
+            and (parent.type == prog.method_def or parent.type == prog.class_def)
          <variable name="annoName" type="java.lang.String" />
          <action>annoName = this.getAnnotation("javaname")</action>
          <rule>
-            annoName.equals("org.junit.jupiter.api.Test") or
-            annoName.equals("org.junit.jupiter.api.BeforeEach") or
-            annoName.equals("org.junit.jupiter.api.AfterEach")  or
-            annoName.equals("org.junit.jupiter.api.BeforeAll")  or
-            annoName.equals("org.junit.jupiter.api.AfterAll")   or
-            annoName.equals("org.junit.jupiter.api.Disabled")
+            annoName.startsWith("com.goldencode.p2j.testengine.api.")
             <!-- Replace fully-qualified name by simple name -->
-            <action>copy.putAnnotation("javaname", annoName.substring(22))</action>
+            <action>copy.putAnnotation("javaname", annoName.substring(34))</action>
             <action>hasJUnit5Annotations = true</action>
          </rule>
       </rule>
+      <rule>upPath("ANNOTATION/ASSIGN/LBRACKET")
+            and this.type == prog.symbol
+         <action>printfln("ANNOTATION/ASSIGN/LBRACKET text '%s'", this.getText())</action>
+         <action>createJavaAst(java.reference, ecw.stripQuotes(this.getText()), getClosestPeerId())</action>
+      </rule>
    </walk-rules>
    <post-rules>
       <rule>hasJUnit5Annotations == true
-         <action>help.createImport("org.junit.jupiter.api.*")</action>
+         <action>help.createImport("com.goldencode.p2j.testengine.api.*")</action>
          <action>hasJUnit5Annotations = false</action>
       </rule>
    </post-rules>

=== modified file 'src/com/goldencode/p2j/cfg/Configuration.java'
--- src/com/goldencode/p2j/cfg/Configuration.java	2022-04-22 22:17:35 +0000
+++ src/com/goldencode/p2j/cfg/Configuration.java	2022-09-30 11:47:48 +0000
@@ -75,6 +75,7 @@
 **     CA  20220412          Added file-set support (either -Z command-line option or p2j.cfg.xml 
 **                           configuration).
 **     OM  20220421          Restored the ability to load the configuration file relative to P2J_HOME folder.
+**     VVT 2022xxxx          Constant added: default legacy unit test framework type. See #6237. 
 */
 
 /*
@@ -268,6 +269,9 @@
    /** Default configuration filename */
    public static final String DEF_CFG_FILE = "p2j.cfg.xml";
    
+   /** Default legacy unit test type */
+   public static final String DEF_UNIT_TEST_TYPE = "ABLUnit";
+   
    /** Singleton instance. */
    private static volatile Configuration instance;
    

=== modified file 'src/com/goldencode/p2j/main/ClientCore.java'
--- src/com/goldencode/p2j/main/ClientCore.java	2022-09-18 16:13:54 +0000
+++ src/com/goldencode/p2j/main/ClientCore.java	2022-10-12 22:01:49 +0000
@@ -60,6 +60,8 @@
 **     CA  20220208 Added -errorstack command-line option (client:cmd-line-option:error-stack).
 **     SVL 20220620 "ininame" parameter handling moved earlier to TC.initializePost.
 **     CA  20220918 Added socket trust store filename and password to the ClientParameters.
+**     VVT 2022xxxx Native methods processInit and getPid made public to be accessible from
+**                  FWD test engine. See #6237.
 */
 /*
 ** This program is free software: you can redistribute it and/or modify
@@ -121,12 +123,13 @@
 import java.util.logging.*;
 
 import com.goldencode.p2j.cfg.*;
+import com.goldencode.p2j.directory.*;
 import com.goldencode.p2j.net.*;
-import com.goldencode.p2j.directory.*;
+import com.goldencode.p2j.net.Session;
 import com.goldencode.p2j.security.SecurityManager;
+import com.goldencode.p2j.ui.chui.*;
 import com.goldencode.p2j.ui.client.*;
 import com.goldencode.p2j.ui.client.driver.*;
-import com.goldencode.p2j.ui.chui.ThinClient;
 import com.goldencode.p2j.util.*;
 
 /**
@@ -172,7 +175,6 @@
     *           <code>false</code> for the normal client JVM startup.
     * @param    diag
     *           Details about the driver state that can help diagnose server startup issues.
-    *           
     * @throws Exception 
     */
    public static void start(BootstrapConfig config, boolean file, boolean single, Supplier<String> diag)
@@ -357,6 +359,7 @@
          // set up current user name for client session
          ThinClient.getInstance().setUserName(params.osUserName);
 
+         
          // call the application
          running = main.standardEntry();
          
@@ -438,17 +441,21 @@
    /**
     * Native call to set up and redefine signal processing handlers.
     */
-   private static native void processInit();
+   public static native void processInit();
    
    /**
     * Native call to read the operating system pid for the current JVM process.
+    * 
+    * @return see above 
     */
-   private static native long getPid();
+   public static native long getPid();
    
    /**
     * Native call to read the operating system user name for the current JVM process.
+    * 
+    * @return see above 
     */
-   private static native String getUserName();
+   public static native String getUserName();
    
    /**
     * Initialize the logger if needed.

=== modified file 'src/com/goldencode/p2j/main/StandardServer.java'
--- src/com/goldencode/p2j/main/StandardServer.java	2022-10-05 08:54:36 +0000
+++ src/com/goldencode/p2j/main/StandardServer.java	2022-10-12 21:57:10 +0000
@@ -203,6 +203,7 @@
 **                           allows a lazy registration of scopeables, to avoid the unnecessary overhead of
 **                           processing all the scopeables for each and every block.
 **     CA  20220906          Moved the interactive client cleaner to LogicalTerminal.registerCleaner().
+**     VVT 2022xxxx          UnitTestEngine static network server registration added. See #6237.
 */
 
 /*
@@ -264,12 +265,15 @@
 import java.lang.reflect.*;
 import java.util.*;
 import java.util.concurrent.*;
-import java.util.function.Supplier;
+import java.util.function.*;
 import java.util.logging.*;
+
 import javax.servlet.*;
+
 import org.eclipse.jetty.server.handler.*;
 import org.eclipse.jetty.util.resource.Resource;
 import org.eclipse.jetty.webapp.*;
+
 import com.goldencode.ast.*;
 import com.goldencode.p2j.*;
 import com.goldencode.p2j.admin.*;
@@ -287,6 +291,7 @@
 import com.goldencode.p2j.security.*;
 import com.goldencode.p2j.security.SecurityManager;
 import com.goldencode.p2j.soap.*;
+import com.goldencode.p2j.testengine.*;
 import com.goldencode.p2j.ui.*;
 import com.goldencode.p2j.util.*;
 import com.goldencode.p2j.util.ErrorManager;
@@ -992,7 +997,9 @@
       
       ifaces = new Class[] { ServerPropertiesInspector.class };
       RemoteObject.registerStaticNetworkServer(ifaces, ServerPropertiesDaemon.class);
-      
+            
+      RemoteObject.registerStaticNetworkServer(new Class[] { UnitTestEngine.class }, UnitTestServer.class);
+
       AdminServerImpl.initialize();
       
       DirectoryManager.initServer();
@@ -1114,7 +1121,7 @@
     * @param   ds
     *          The directory service.
     */
-   private void initClassLoaders(DirectoryService ds)
+   private static void initClassLoaders(DirectoryService ds)
    {
       // initialize the class loaders for the jars defined in the directory
       String path = Utils.findDirectoryNodePath(ds,

=== added file 'src/com/goldencode/p2j/main/UnitTestDriver.java'
--- src/com/goldencode/p2j/main/UnitTestDriver.java	1970-01-01 00:00:00 +0000
+++ src/com/goldencode/p2j/main/UnitTestDriver.java	2022-10-12 21:44:30 +0000
@@ -0,0 +1,105 @@
+/**
+ * 
+ */
+package com.goldencode.p2j.main;
+
+import com.goldencode.p2j.cfg.*;
+
+/**
+ * The purpose of this class is to explore how to start client with no command line.
+ */
+public class UnitTestDriver
+{
+
+   /**
+    * The main entry
+    * 
+    * @param args
+    *        for now unused
+    */
+   public static void main(String[] args)
+   {
+      // process arguments: either config files of overrides
+      // try standard configuration file if none is given
+      BootstrapConfig bc = null;
+
+      try
+      {
+         bc = new BootstrapConfig(null, null, null, null);
+      }
+      catch (ConfigurationException e)
+      {
+         e.printStackTrace();
+         System.exit(1);
+      }
+
+      bc.setServer(false);
+
+      String convo = null;
+
+      try
+      {
+         convo = bc.getConfigItem("net", "queue", "conversation");
+      }
+      catch (ConfigurationException ce)
+      {
+         // ignore
+      }
+
+      // default to conversation mode if not specified
+      if (convo == null)
+      {
+         bc.setConfigItem("net", "queue", "conversation", "true");
+      }
+
+      // either way, force start thread to false (this client is going to
+      // use its main thread as the conversation thread so we don't need
+      // to start one
+      bc.setConfigItem("net", "queue", "start_thread", "false");
+
+      bc.setConfigItem("net", "server", "host", "localhost");
+      bc.setConfigItem("net", "connection", "secure", "false");
+      bc.setConfigItem("net", "server", "secure_port", "4434");
+      bc.setConfigItem("net", "server", "insecure_port", "4534");
+      bc.setConfigItem("client", "driver", "type", "gui_swing");
+      bc.setConfigItem("client", "driver", "theme",
+               "com.goldencode.p2j.ui.client.gui.theme.Windows10Theme");
+
+      // run the application
+      try
+      {
+         //         if (config.getBoolean("net", "http_client", "disable_ssl_certificate_validation", false))
+         //         {
+         //            Utils.disableClientSSLCertificateValidation();
+         //         }
+
+         //         if (config.getBoolean("client", "mode", "broker", false))
+         //         {
+         //            setClientLog("broker");
+         //            BrokerCore.start(config);
+         //         }
+         //         else
+         //         {
+         //            ClientCore.start(config, true, false, this::printDriverDetails);
+         //         }
+
+         ClientCore.start(bc, true, false, () -> "");
+         // ensure that remaining non-daemon threads don't stop us from closing
+         System.exit(0);
+      }
+      catch (Throwable thr)
+      {
+         // STOP conditions don't need to be reported, they are an expected
+         // mechanism to terminate
+         if (ClientCore.isCauseStop(thr))
+         {
+            System.exit(0);
+         }
+
+         thr.printStackTrace(System.err);
+         System.err.flush();
+         System.exit(-13);
+      }
+
+   }
+}

=== added directory 'src/com/goldencode/p2j/testengine'
=== added file 'src/com/goldencode/p2j/testengine/AbstractBranchTestDescriptor.java'
--- src/com/goldencode/p2j/testengine/AbstractBranchTestDescriptor.java	1970-01-01 00:00:00 +0000
+++ src/com/goldencode/p2j/testengine/AbstractBranchTestDescriptor.java	2022-10-12 21:04:57 +0000
@@ -0,0 +1,215 @@
+/*
+** Module   : AbstractBranchTestDescriptor.java
+** Abstract : Common accessor for non-terminal test descriptors
+**
+** Copyright (c) 2022, Golden Code Development Corporation.
+**
+** -#- -I- --Date-- ----------------Description----------------------
+** 001 VVT 2022xxxx Created initial version.
+*/
+/*
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU Affero General Public License as
+** published by the Free Software Foundation, either version 3 of the
+** License, or (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU Affero General Public License for more details.
+**
+** You may find a copy of the GNU Affero GPL version 3 at the following
+** location: https://www.gnu.org/licenses/agpl-3.0.en.html
+** 
+** Additional terms under GNU Affero GPL version 3 section 7:
+** 
+**   Under Section 7 of the GNU Affero GPL version 3, the following additional
+**   terms apply to the works covered under the License.  These additional terms
+**   are non-permissive additional terms allowed under Section 7 of the GNU
+**   Affero GPL version 3 and may not be removed by you.
+** 
+**   0. Attribution Requirement.
+** 
+**     You must preserve all legal notices or author attributions in the covered
+**     work or Appropriate Legal Notices displayed by works containing the covered
+**     work.  You may not remove from the covered work any author or developer
+**     credit already included within the covered work.
+** 
+**   1. No License To Use Trademarks.
+** 
+**     This license does not grant any license or rights to use the trademarks
+**     Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
+**     of Golden Code Development Corporation. You are not authorized to use the
+**     name Golden Code, FWD, or the names of any author or contributor, for
+**     publicity purposes without written authorization.
+** 
+**   2. No Misrepresentation of Affiliation.
+** 
+**     You may not represent yourself as Golden Code Development Corporation or FWD.
+** 
+**     You may not represent yourself for publicity purposes as associated with
+**     Golden Code Development Corporation, FWD, or any author or contributor to
+**     the covered work, without written authorization.
+** 
+**   3. No Misrepresentation of Source or Origin.
+** 
+**     You may not represent the covered work as solely your work.  All modified
+**     versions of the covered work must be marked in a reasonable way to make it
+**     clear that the modified work is not originating from Golden Code Development
+**     Corporation or FWD.  All modified versions must contain the notices of
+**     attribution required in this license.
+*/
+package com.goldencode.p2j.testengine;
+
+import java.io.*;
+import java.util.*;
+
+import org.junit.platform.commons.util.*;
+import org.junit.platform.engine.*;
+
+/**
+ * This class provides common functions for branch test descriptors
+ */
+public abstract class AbstractBranchTestDescriptor
+extends AbstractFWDTestDescriptor
+{
+   /** Child descriptors */
+   private Set<TestDescriptor> children = Collections.synchronizedSet(new LinkedHashSet<>(16));
+
+   /**
+    * The constructor.
+    * 
+    * @param uniqueId
+    *        this descriptor unique ID
+    * @param displayName
+    *        this descriptor display name
+    */
+   public AbstractBranchTestDescriptor(final UniqueId uniqueId, final String displayName)
+   {
+      super(uniqueId, displayName);
+   }
+
+   /**
+    * Add a <em>child</em> to this descriptor.
+    *
+    * @param descriptor the child to add to this descriptor; never {@code null}
+    */
+   @Override
+   public void addChild(final TestDescriptor descriptor)
+   {
+      Preconditions.notNull(descriptor, "child must not be null");
+
+      descriptor.setParent(this);
+      children.add(descriptor);
+   }
+
+   /**
+    * Find the descriptor with the supplied unique ID.
+    *
+    * <p>The search algorithm begins with this descriptor and then searches
+    * through its descendants.
+    *
+    * @param uniqueId the {@code UniqueId} to search for; never {@code null}
+    */
+   @Override
+   public Optional<? extends TestDescriptor> findByUniqueId(final UniqueId uniqueId)
+   {
+      if (getUniqueId().equals(uniqueId))
+      {
+         return Optional.of(this);
+      }
+
+      return children.stream().map(child -> child.findByUniqueId(uniqueId))
+               .filter(Optional::isPresent).findAny().orElse(Optional.empty());
+   }
+
+   /**
+    * Get the immutable set of <em>children</em> of this descriptor.
+    *
+    * @return the set of children of this descriptor; neither {@code null}
+    * nor mutable, but potentially empty
+    * @see #getDescendants()
+    */
+   @Override
+   public final Set<? extends TestDescriptor> getChildren()
+   {
+      return Collections.unmodifiableSet(children);
+   }
+
+   /**
+   * Determine the {@link org.junit.platform.engine.TestDescriptor.Type} of this descriptor.
+   *
+   * @return the descriptor type; never {@code null}.
+   * @see #isContainer()
+   * @see #isTest()
+   */
+   @Override
+   public Type getType()
+   {
+      return Type.CONTAINER;
+   }
+
+   /**
+    * The object implements the readExternal method to restore its
+    * contents by calling the methods of DataInput for primitive
+    * types and readObject for objects, strings and arrays.  The
+    * readExternal method must read the values in the same sequence
+    * and with the same types as were written by writeExternal.
+    *
+    * @param in the stream to read data from in order to restore the object
+    * @exception IOException if I/O errors occur
+    * @exception ClassNotFoundException If the class for an object being
+    *              restored cannot be found.
+    */
+   @Override
+   public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException
+   {
+      super.readExternal(in);
+      
+      children = (Set<TestDescriptor>) in.readObject();
+      
+      for(final TestDescriptor child : children)
+      {
+         child.setParent(this);
+      }
+   }
+
+   /**
+    * Remove a <em>child</em> from this descriptor.
+    *
+    * @param descriptor the child to remove from this descriptor; never
+    * {@code null}
+    */
+   @Override
+   public void removeChild(TestDescriptor descriptor)
+   {
+      Preconditions.notNull(descriptor, "child must not be null");
+
+      children.remove(descriptor);
+      descriptor.setParent(null);
+   }
+
+   /**
+    * The object implements the writeExternal method to save its contents
+    * by calling the methods of DataOutput for its primitive values or
+    * calling the writeObject method of ObjectOutput for objects, strings,
+    * and arrays.
+    *
+    * @serialData Overriding methods should use this tag to describe
+    *             the data layout of this Externalizable object.
+    *             List the sequence of element types and, if possible,
+    *             relate the element to a public/protected field and/or
+    *             method of this Externalizable class.
+    *
+    * @param out the stream to write the object to
+    * @exception IOException Includes any I/O exceptions that may occur
+    */
+   @Override
+   public void writeExternal(ObjectOutput out) throws IOException
+   {
+      super.writeExternal(out);
+      
+      out.writeObject(children);
+   }
+
+}
\ No newline at end of file

=== added file 'src/com/goldencode/p2j/testengine/AbstractClassTestDescriptor.java'
--- src/com/goldencode/p2j/testengine/AbstractClassTestDescriptor.java	1970-01-01 00:00:00 +0000
+++ src/com/goldencode/p2j/testengine/AbstractClassTestDescriptor.java	2022-10-21 15:15:58 +0000
@@ -0,0 +1,224 @@
+/*
+** Module   : AbstractClassTestDescriptor.java
+** Abstract : Common ancestor for legacy class and legacy procedure test descriptors.
+**
+** Copyright (c) 2022, Golden Code Development Corporation.
+**
+** -#- -I- --Date-- ----------------Description----------------------
+** 001 VVT 2022xxxx Created initial version.
+*/
+/*
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU Affero General Public License as
+** published by the Free Software Foundation, either version 3 of the
+** License, or (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU Affero General Public License for more details.
+**
+** You may find a copy of the GNU Affero GPL version 3 at the following
+** location: https://www.gnu.org/licenses/agpl-3.0.en.html
+** 
+** Additional terms under GNU Affero GPL version 3 section 7:
+** 
+**   Under Section 7 of the GNU Affero GPL version 3, the following additional
+**   terms apply to the works covered under the License.  These additional terms
+**   are non-permissive additional terms allowed under Section 7 of the GNU
+**   Affero GPL version 3 and may not be removed by you.
+** 
+**   0. Attribution Requirement.
+** 
+**     You must preserve all legal notices or author attributions in the covered
+**     work or Appropriate Legal Notices displayed by works containing the covered
+**     work.  You may not remove from the covered work any author or developer
+**     credit already included within the covered work.
+** 
+**   1. No License To Use Trademarks.
+** 
+**     This license does not grant any license or rights to use the trademarks
+**     Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
+**     of Golden Code Development Corporation. You are not authorized to use the
+**     name Golden Code, FWD, or the names of any author or contributor, for
+**     publicity purposes without written authorization.
+** 
+**   2. No Misrepresentation of Affiliation.
+** 
+**     You may not represent yourself as Golden Code Development Corporation or FWD.
+** 
+**     You may not represent yourself for publicity purposes as associated with
+**     Golden Code Development Corporation, FWD, or any author or contributor to
+**     the covered work, without written authorization.
+** 
+**   3. No Misrepresentation of Source or Origin.
+** 
+**     You may not represent the covered work as solely your work.  All modified
+**     versions of the covered work must be marked in a reasonable way to make it
+**     clear that the modified work is not originating from Golden Code Development
+**     Corporation or FWD.  All modified versions must contain the notices of
+**     attribution required in this license.
+*/
+package com.goldencode.p2j.testengine;
+
+import java.io.*;
+import java.lang.reflect.*;
+import java.util.*;
+
+import org.junit.platform.engine.*;
+
+import com.goldencode.util.*;
+
+/**
+ * Common ancestor for legacy class and legacy procedure test descriptors.
+ */
+public abstract class AbstractClassTestDescriptor
+extends AbstractBranchTestDescriptor
+{
+
+   /**
+    * Class methods marked to execute before all tests 
+    */
+   List<Method> afterAll;
+
+   /**
+    * Class methods marked to execute before all tests 
+    */
+   List<Method> beforeAll;
+
+   /**
+    * The Java class.
+    */
+   Class clazz;
+
+   /**
+    * Default constructor. Required for serialization. Not to be used in application code.
+    */
+   protected AbstractClassTestDescriptor()
+   {
+      super(null, null);
+
+      this.clazz = null;
+      this.beforeAll = null;
+      this.afterAll = null;      
+   }
+
+   /**
+    * The constructor.
+    * 
+    * @param clazz
+    *        the Java class
+    * @param parent
+    *        the parent descriptor
+    * @param test
+    *        class methods marked as tests  
+    * @param beforeEach
+    *        class methods marked to execute before each test  
+    * @param afterEach
+    *        class methods marked to execute after each test  
+    * @param beforeAll
+    *        class methods marked to execute before all tests  
+    * @param afterAll
+    *        class methods marked to execute after all tests  
+    */
+   protected AbstractClassTestDescriptor(final Class<?> clazz, final TestDescriptor parent,
+            final List<Method> test, final List<Method> beforeEach, final List<Method> afterEach,
+            final List<Method> beforeAll, final List<Method> afterAll)
+   {
+      super(parent.getUniqueId().append("class", clazz.getName()), clazz.getSimpleName());
+
+      this.clazz = clazz;
+      this.beforeAll = beforeAll;
+      this.afterAll = afterAll;
+   }
+   
+   /**
+    * Execute the <em>after</em> behavior of this node implementation.
+    *
+    * <p>This method will be called once <em>after</em> {@linkplain #executeImpl()
+    * execution} of this node.
+    * 
+    * @throws Exception 
+    * 
+    */
+   @Override
+   public void afterImpl() throws Exception
+   {
+      callMethodsImpl(afterAll);
+   }
+
+   /**
+    * Execute the <em>before</em> behavior of this node implementation.
+    *
+    * <p>This method will be called once <em>before</em> {@linkplain #executeImpl()
+    * execution} of this node.
+    * 
+    * @throws Exception 
+    *
+    */
+   @Override
+   public void beforeImpl() throws Exception
+   {
+      callMethodsImpl(beforeAll);
+   }
+
+   /**
+    * Print all class-specific fields.
+    * 
+    * @param printer
+    *        the string printer to print into
+    */
+   @Override
+   public void print(PrintHelper printer)
+   {
+      printer.print(clazz.getName());
+   }
+
+   /**
+    * The object implements the readExternal method to restore its
+    * contents by calling the methods of DataInput for primitive
+    * types and readObject for objects, strings and arrays.  The
+    * readExternal method must read the values in the same sequence
+    * and with the same types as were written by writeExternal.
+    *
+    * @param in the stream to read data from in order to restore the object
+    * @exception IOException if I/O errors occur
+    * @exception ClassNotFoundException If the class for an object being
+    *              restored cannot be found.
+    */
+   @Override
+   public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException
+   {
+      super.readExternal(in);
+      
+      clazz = (Class<?>) in.readObject();
+      beforeAll = SerializationSupport.readMethods(in);
+      afterAll = SerializationSupport.readMethods(in);
+   }
+
+   /**
+    * The object implements the writeExternal method to save its contents
+    * by calling the methods of DataOutput for its primitive values or
+    * calling the writeObject method of ObjectOutput for objects, strings,
+    * and arrays.
+    *
+    * @serialData Overriding methods should use this tag to describe
+    *             the data layout of this Externalizable object.
+    *             List the sequence of element types and, if possible,
+    *             relate the element to a public/protected field and/or
+    *             method of this Externalizable class.
+    *
+    * @param out the stream to write the object to
+    * @exception IOException Includes any I/O exceptions that may occur
+    */
+   @Override
+   public void writeExternal(ObjectOutput out) throws IOException
+   {
+      super.writeExternal(out);
+      
+      out.writeObject(clazz);
+      SerializationSupport.writeMethods(beforeAll, out);
+      SerializationSupport.writeMethods(afterAll, out);
+   }
+
+}

=== added file 'src/com/goldencode/p2j/testengine/AbstractFWDTestDescriptor.java'
--- src/com/goldencode/p2j/testengine/AbstractFWDTestDescriptor.java	1970-01-01 00:00:00 +0000
+++ src/com/goldencode/p2j/testengine/AbstractFWDTestDescriptor.java	2022-10-27 14:28:06 +0000
@@ -0,0 +1,349 @@
+/*
+** Module   : AbstractDWDTestDescriptor.java
+** Abstract : Common accessor for all test descriptors
+**
+** Copyright (c) 2022, Golden Code Development Corporation.
+**
+** -#- -I- --Date-- ----------------Description----------------------
+** 001 VVT 2022xxxx Created initial version.
+*/
+/*
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU Affero General Public License as
+** published by the Free Software Foundation, either version 3 of the
+** License, or (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU Affero General Public License for more details.
+**
+** You may find a copy of the GNU Affero GPL version 3 at the following
+** location: https://www.gnu.org/licenses/agpl-3.0.en.html
+** 
+** Additional terms under GNU Affero GPL version 3 section 7:
+** 
+**   Under Section 7 of the GNU Affero GPL version 3, the following additional
+**   terms apply to the works covered under the License.  These additional terms
+**   are non-permissive additional terms allowed under Section 7 of the GNU
+**   Affero GPL version 3 and may not be removed by you.
+** 
+**   0. Attribution Requirement.
+** 
+**     You must preserve all legal notices or author attributions in the covered
+**     work or Appropriate Legal Notices displayed by works containing the covered
+**     work.  You may not remove from the covered work any author or developer
+**     credit already included within the covered work.
+** 
+**   1. No License To Use Trademarks.
+** 
+**     This license does not grant any license or rights to use the trademarks
+**     Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
+**     of Golden Code Development Corporation. You are not authorized to use the
+**     name Golden Code, FWD, or the names of any author or contributor, for
+**     publicity purposes without written authorization.
+** 
+**   2. No Misrepresentation of Affiliation.
+** 
+**     You may not represent yourself as Golden Code Development Corporation or FWD.
+** 
+**     You may not represent yourself for publicity purposes as associated with
+**     Golden Code Development Corporation, FWD, or any author or contributor to
+**     the covered work, without written authorization.
+** 
+**   3. No Misrepresentation of Source or Origin.
+** 
+**     You may not represent the covered work as solely your work.  All modified
+**     versions of the covered work must be marked in a reasonable way to make it
+**     clear that the modified work is not originating from Golden Code Development
+**     Corporation or FWD.  All modified versions must contain the notices of
+**     attribution required in this license.
+*/
+package com.goldencode.p2j.testengine;
+
+import java.lang.reflect.*;
+import java.util.*;
+
+import org.junit.platform.engine.*;
+import org.junit.platform.engine.support.hierarchical.*;
+
+/**
+ * This class provides common functions for all FWD test descriptors:
+ * 
+ * <li>Stores unique ID, display name and parent reference.
+ * <li>Serialization: FWD test descriptors travel over network, so they must be serializable.
+ * <li>Implements {@link Node} interface: all methods are passed to a proxy to be executed on the server.
+ */
+public abstract class AbstractFWDTestDescriptor
+extends NoopDescriptor
+implements Node<FWDEngineExecutionContext>
+{
+
+   /**
+    * The constructor.
+    * 
+    * @param uniqueId
+    *        the descriptor unique ID
+    * @param displayName
+    *        the descriptor display name
+    */
+   public AbstractFWDTestDescriptor(final UniqueId uniqueId, final String displayName)
+   {
+      super(uniqueId, displayName);
+   }
+
+   /**
+    * Default constructor. Must be used internally for serialization only.
+    */
+   protected AbstractFWDTestDescriptor()
+   {
+      this(null, null);
+   }
+
+   /**
+    * Execute the <em>after</em> behavior of this node.
+    *
+    * <p>This method will be called once <em>after</em> {@linkplain #execute
+    * execution} of this node.
+    *
+    * @param context
+    *        the context to execute in
+    * 
+    */
+   @Override
+   public void after(final FWDEngineExecutionContext context) throws Exception
+   {
+      context.unitTestSupport.after(getUniqueId());
+   }
+
+   /**
+    * Execute the <em>after</em> behavior of this node implementation.
+    *
+    * <p>This method will be called once <em>after</em> {@linkplain #executeImpl()
+    * execution} of this node.
+    * 
+    * @throws Exception 
+    * 
+    */
+   public void afterImpl() throws Exception
+   {
+      // no-op
+   }
+
+   /**
+    * Execute the <em>before</em> behavior of this node.
+    *
+    * <p>This method will be called once <em>before</em> {@linkplain #execute
+    * execution} of this node.
+    *
+    * @param context
+    *        the context to execute in
+    * @return the new context to be used for children of this node; never
+    * {@code null}
+    */
+   @Override
+   public FWDEngineExecutionContext before(FWDEngineExecutionContext context) throws Exception
+   {
+      context.unitTestSupport.before(getUniqueId());
+
+      return context;
+   }
+
+   /**
+    * Execute the <em>before</em> behavior of this node implementation.
+    *
+    * <p>This method will be called once <em>before</em> {@linkplain #executeImpl()
+    * execution} of this node.
+    * 
+    * @throws Exception 
+    *
+    */
+   public void beforeImpl() throws Exception
+   {
+      // no-op
+   }
+
+   /**
+    * Clean up the supplied {@code context} after execution.
+    * 
+    * Reset the test class instance.
+    *
+    * @param context
+    *        the context to execute in
+   
+    * @see #prepare
+    */
+   @Override
+   public void cleanUp(final FWDEngineExecutionContext context) throws Exception
+   {
+      context.unitTestSupport.cleanUp(getUniqueId());
+   }
+
+   /**
+    * Clean up the supplied {@code context} after execution implementation.
+    * 
+    * Reset the test class instance.
+    * 
+    * @throws Exception 
+    *
+    * @see #prepare
+    */
+   public void cleanUpImpl() throws Exception
+   {
+      // no-op
+   }
+
+   /**
+    * Execute the <em>behavior</em> of this node.
+    *
+    * <p>Containers typically do not implement this method since the
+    * {@link HierarchicalTestEngine} handles execution of their children.
+    *
+    * <p>The supplied {@code dynamicTestExecutor} may be used to submit
+    * additional dynamic tests for immediate execution.
+    *
+    * @param context
+    *         the context to execute in
+    * @param dynamicTestExecutor
+    *        the executor to submit dynamic tests to
+    *        
+    * @return the new context to be used for children of this node and for the
+    * <em>after</em> behavior of the parent of this node, if any
+    */
+   @Override
+   public FWDEngineExecutionContext execute(final FWDEngineExecutionContext context,
+            DynamicTestExecutor dynamicTestExecutor) throws Exception
+   {
+      context.unitTestSupport.execute(getUniqueId());
+
+      return context;
+   }
+
+   /**
+    * Execute the implementation <em>behavior</em> of this node.
+    *
+    * @throws Exception 
+    *
+    */
+   public void executeImpl() throws Exception
+   {
+      // no-op
+   }
+
+   /**
+    * Prepare the supplied {@code context} prior to execution.
+    * 
+    * Create test class instance.
+    *
+    * @see #cleanUp
+    */
+   @Override
+   public FWDEngineExecutionContext prepare(FWDEngineExecutionContext context) throws Exception
+   {
+      context.unitTestSupport.prepare(getUniqueId());
+
+      return context;
+   }
+
+   /**
+    * Prepare the supplied {@code context} prior to execution implementation.
+    * 
+    * @throws Exception 
+    *
+    * @see #cleanUpImpl
+    */
+   public void prepareImpl() throws Exception
+   {
+      // no-op
+   }
+
+   /**
+   * Returns a string representation of the object. In general, the
+   * {@code toString} method returns a string that
+   * "textually represents" this object. The result should
+   * be a concise but informative representation that is easy for a
+   * person to read.
+   * It is recommended that all subclasses override this method.
+   * <p>
+   * The {@code toString} method for class {@code Object}
+   * returns a string consisting of the name of the class of which the
+   * object is an instance, the at-sign character `{@code @}', and
+   * the unsigned hexadecimal representation of the hash code of the
+   * object. In other words, this method returns a string equal to the
+   * value of:
+   * <blockquote>
+   * <pre>
+   * getClass().getName() + '@' + Integer.toHexString(hashCode())
+   * </pre></blockquote>
+   *
+   * @return  a string representation of the object.
+   */
+   @SuppressWarnings("null")
+   @Override
+   public String toString()
+   {
+      return asString();
+   }
+
+   /**
+    * Execute a method.
+    *  
+    * @param method
+    *        the method to execute
+    * @param o
+    *        the object to use, can be {@code null} for "static" methods. 
+    */
+   @SuppressWarnings("static-method")
+   protected void callMethodImpl(final Method method, final Object o)
+   {
+      throw new RuntimeException("Derived classes must override this method");
+   }
+
+   /**
+    * Execute list of methods implementation.
+    * 
+    * @param methods
+    *        the methods to execute
+    *        
+    * @throws Exception
+    * 
+    * @see #beforeImpl
+    * @see #afterImpl
+    * @see #executeImpl
+    */
+   protected final void callMethodsImpl(final List<Method> methods) throws Exception
+   {
+      if (methods.isEmpty())
+      {
+         return;
+      }
+      
+      final Object testObject = getTestObject();
+      assert testObject != null;
+      
+      for (final Method m : methods)
+      {
+         callMethodImpl(m, testObject);
+      }
+   }
+
+   /**
+    * Get test object. The test object is either a legacy class instance if tests are implemented
+    * as legacy class methods or a persistent procedure instance it tests are implemented as
+    * internal procedures.
+    * 
+    * The value should be {@code null} if the test method is a static legacy class method.
+    * 
+    * The value is created in class test descriptor {@link Node#prepare(EngineExecutionContext)},
+    * and destroyed in {@link Node#cleanUp(EngineExecutionContext)}.
+    * 
+    * @return the test object or {@code null}
+    *         
+    */
+   @SuppressWarnings("static-method")
+   protected Object getTestObject()
+   {
+      return null;
+   }
+
+}
\ No newline at end of file

=== added file 'src/com/goldencode/p2j/testengine/AbstractMethodTestDescriptor.java'
--- src/com/goldencode/p2j/testengine/AbstractMethodTestDescriptor.java	1970-01-01 00:00:00 +0000
+++ src/com/goldencode/p2j/testengine/AbstractMethodTestDescriptor.java	2022-10-21 15:20:00 +0000
@@ -0,0 +1,248 @@
+/*
+** Module   : AbstractMethodTestDescriptor.java
+** Abstract : Test descriptor for a Java method.
+**
+** Copyright (c) 2022, Golden Code Development Corporation.
+**
+** -#- -I- --Date-- ----------------Description----------------------
+** 001 VVT 2022xxxx Created initial version.
+*/
+/*
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU Affero General Public License as
+** published by the Free Software Foundation, either version 3 of the
+** License, or (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU Affero General Public License for more details.
+**
+** You may find a copy of the GNU Affero GPL version 3 at the following
+** location: https://www.gnu.org/licenses/agpl-3.0.en.html
+** 
+** Additional terms under GNU Affero GPL version 3 section 7:
+** 
+**   Under Section 7 of the GNU Affero GPL version 3, the following additional
+**   terms apply to the works covered under the License.  These additional terms
+**   are non-permissive additional terms allowed under Section 7 of the GNU
+**   Affero GPL version 3 and may not be removed by you.
+** 
+**   0. Attribution Requirement.
+** 
+**     You must preserve all legal notices or author attributions in the covered
+**     work or Appropriate Legal Notices displayed by works containing the covered
+**     work.  You may not remove from the covered work any author or developer
+**     credit already included within the covered work.
+** 
+**   1. No License To Use Trademarks.
+** 
+**     This license does not grant any license or rights to use the trademarks
+**     Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
+**     of Golden Code Development Corporation. You are not authorized to use the
+**     name Golden Code, FWD, or the names of any author or contributor, for
+**     publicity purposes without written authorization.
+** 
+**   2. No Misrepresentation of Affiliation.
+** 
+**     You may not represent yourself as Golden Code Development Corporation or FWD.
+** 
+**     You may not represent yourself for publicity purposes as associated with
+**     Golden Code Development Corporation, FWD, or any author or contributor to
+**     the covered work, without written authorization.
+** 
+**   3. No Misrepresentation of Source or Origin.
+** 
+**     You may not represent the covered work as solely your work.  All modified
+**     versions of the covered work must be marked in a reasonable way to make it
+**     clear that the modified work is not originating from Golden Code Development
+**     Corporation or FWD.  All modified versions must contain the notices of
+**     attribution required in this license.
+*/
+package com.goldencode.p2j.testengine;
+
+import java.io.*;
+import java.lang.reflect.*;
+import java.util.*;
+
+import org.junit.platform.engine.support.hierarchical.*;
+
+/**
+ * Test descriptor for a Java method.
+ */
+public abstract class AbstractMethodTestDescriptor
+extends AbstractFWDTestDescriptor
+{
+   /**
+    * Methods to execute after the main method, if any.
+    */
+   List<Method> afterEach;
+
+   /**
+    * Methods to execute before the main method, if any.
+    */
+   List<Method> beforeEach;
+
+   /**
+    * The Java class to create instances of.
+    */
+   Class<?> clazz;
+
+   /**
+    * The test method
+    */
+   Method method;
+
+   /**
+    * Default constructor. Required for serialization only. Not to be used in applications.
+    */
+   protected AbstractMethodTestDescriptor()
+   {
+      super(null, null);
+
+      this.clazz = null;
+      this.method = null;
+      this.beforeEach = null;
+      this.afterEach = null;
+   }
+
+   /**
+    * Constructor.
+    * 
+    * @param parent 
+    *        the parent descriptor
+    * @param clazz
+    *        the Java class to create instances
+    * @param execute
+    *        the method to execute
+    * @param beforeEach
+    *        methods to execute before the execute method
+    * @param afterEach
+    *        methods to execute after the execute method
+    */
+   protected AbstractMethodTestDescriptor(final AbstractClassTestDescriptor parent,
+            final Class<?> clazz,
+            final Method execute,
+            final List<Method> beforeEach,
+            final List<Method> afterEach)
+   {
+      super(parent.getUniqueId().append("method", execute.getName()), execute.getName());
+
+      this.clazz = clazz;
+      this.method = execute;
+      this.beforeEach = beforeEach;
+      this.afterEach = afterEach;
+   }
+
+   /**
+    * Execute the <em>after</em> behavior of this node implementation.
+    *
+    * <p>This method will be called once <em>after</em> {@linkplain #executeImpl()
+    * execution} of this node.
+    * 
+    * @throws Exception 
+    * 
+    */
+   @Override
+   public void afterImpl() throws Exception
+   {
+      callMethodsImpl(afterEach);
+   }
+
+   /**
+    * Execute the <em>before</em> behavior of this node implementation.
+    *
+    * <p>This method will be called once <em>before</em> {@linkplain #executeImpl()
+    * execution} of this node.
+    * 
+    * @throws Exception 
+    *
+    */
+   @Override
+   public void beforeImpl() throws Exception
+   {
+      callMethodsImpl(beforeEach);
+   }
+
+   /**
+    * Execute the implementation <em>behavior</em> of this node.
+    *
+    * @throws Exception 
+    *
+    */
+   @Override
+   public void executeImpl() throws Exception
+   {
+      callMethodsImpl(Collections.singletonList(method));
+   }
+
+   /**
+    * The object implements the readExternal method to restore its
+    * contents by calling the methods of DataInput for primitive
+    * types and readObject for objects, strings and arrays.  The
+    * readExternal method must read the values in the same sequence
+    * and with the same types as were written by writeExternal.
+    *
+    * @param in the stream to read data from in order to restore the object
+    * @exception IOException if I/O errors occur
+    * @exception ClassNotFoundException If the class for an object being
+    *              restored cannot be found.
+    */
+   @Override
+   public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException
+   {
+      super.readExternal(in);
+
+      clazz = (Class<?>) in.readObject();
+      method = SerializationSupport.readMethod(in);
+      beforeEach = SerializationSupport.readMethods(in);
+      afterEach = SerializationSupport.readMethods(in);
+   }
+
+   /**
+    * The object implements the writeExternal method to save its contents
+    * by calling the methods of DataOutput for its primitive values or
+    * calling the writeObject method of ObjectOutput for objects, strings,
+    * and arrays.
+    *
+    * @serialData Overriding methods should use this tag to describe
+    *             the data layout of this Externalizable object.
+    *             List the sequence of element types and, if possible,
+    *             relate the element to a public/protected field and/or
+    *             method of this Externalizable class.
+    *
+    * @param out the stream to write the object to
+    * @exception IOException Includes any I/O exceptions that may occur
+    */
+   @Override
+   public void writeExternal(ObjectOutput out) throws IOException
+   {
+      super.writeExternal(out);
+
+      out.writeObject(clazz);
+      SerializationSupport.writeMethod(method, out);
+      SerializationSupport.writeMethods(beforeEach, out);
+      SerializationSupport.writeMethods(afterEach, out);
+   }
+
+   /**
+    * Get test object. The test object is either a legacy class instance if tests are implemented
+    * as legacy class methods or a persistent procedure instance it tests are implemented as
+    * internal procedures.
+    * 
+    * The value should be {@code null} if the test method is a static legacy class method.
+    * 
+    * The value is created in class test descriptor {@link Node#prepare(EngineExecutionContext)},
+    * and destroyed in {@link Node#cleanUp(EngineExecutionContext)}.
+    * 
+    * @return the test object or {@code null}
+    *         
+    */
+   @Override
+   protected Object getTestObject()
+   {
+      // The text object is stored by the class test descriptor
+      return ((AbstractFWDTestDescriptor) getParent().get()).getTestObject();
+   }
+
+}

=== added file 'src/com/goldencode/p2j/testengine/FWDClassMethodTestDescriptor.java'
--- src/com/goldencode/p2j/testengine/FWDClassMethodTestDescriptor.java	1970-01-01 00:00:00 +0000
+++ src/com/goldencode/p2j/testengine/FWDClassMethodTestDescriptor.java	2022-10-28 14:35:32 +0000
@@ -0,0 +1,119 @@
+/*
+** Module   : FWDClassMethodTestDescriptor.java
+** Abstract : Test descriptor for a legacy class test method.
+**
+** Copyright (c) 2022, Golden Code Development Corporation.
+**
+** -#- -I- --Date-- ----------------Description----------------------
+** 001 VVT 2022xxxx Created initial version.
+*/
+/*
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU Affero General Public License as
+** published by the Free Software Foundation, either version 3 of the
+** License, or (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU Affero General Public License for more details.
+**
+** You may find a copy of the GNU Affero GPL version 3 at the following
+** location: https://www.gnu.org/licenses/agpl-3.0.en.html
+** 
+** Additional terms under GNU Affero GPL version 3 section 7:
+** 
+**   Under Section 7 of the GNU Affero GPL version 3, the following additional
+**   terms apply to the works covered under the License.  These additional terms
+**   are non-permissive additional terms allowed under Section 7 of the GNU
+**   Affero GPL version 3 and may not be removed by you.
+** 
+**   0. Attribution Requirement.
+** 
+**     You must preserve all legal notices or author attributions in the covered
+**     work or Appropriate Legal Notices displayed by works containing the covered
+**     work.  You may not remove from the covered work any author or developer
+**     credit already included within the covered work.
+** 
+**   1. No License To Use Trademarks.
+** 
+**     This license does not grant any license or rights to use the trademarks
+**     Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
+**     of Golden Code Development Corporation. You are not authorized to use the
+**     name Golden Code, FWD, or the names of any author or contributor, for
+**     publicity purposes without written authorization.
+** 
+**   2. No Misrepresentation of Affiliation.
+** 
+**     You may not represent yourself as Golden Code Development Corporation or FWD.
+** 
+**     You may not represent yourself for publicity purposes as associated with
+**     Golden Code Development Corporation, FWD, or any author or contributor to
+**     the covered work, without written authorization.
+** 
+**   3. No Misrepresentation of Source or Origin.
+** 
+**     You may not represent the covered work as solely your work.  All modified
+**     versions of the covered work must be marked in a reasonable way to make it
+**     clear that the modified work is not originating from Golden Code Development
+**     Corporation or FWD.  All modified versions must contain the notices of
+**     attribution required in this license.
+*/
+package com.goldencode.p2j.testengine;
+
+import java.lang.reflect.*;
+import java.util.*;
+
+import com.goldencode.p2j.util.*;
+
+/**
+ * Test descriptor for a legacy class test method.
+ */
+public class FWDClassMethodTestDescriptor
+extends AbstractMethodTestDescriptor
+{
+
+   /**
+    * Default constructor, required for de-serialization.
+    */
+   public FWDClassMethodTestDescriptor()
+   {
+      // no-op
+   }
+
+   /**
+    * Constructor.
+    * 
+    * @param parent 
+    *        the parent descriptor
+    * @param clazz
+    *        the Java class to create instances
+    * @param execute
+    *        the method to execute
+    * @param beforeEach
+    *        methods to execute before the execute method
+    * @param afterEach
+    *        methods to execute after the execute method
+    */
+   FWDClassMethodTestDescriptor(AbstractClassTestDescriptor parent, Class<?> clazz,
+            Method execute, List<Method> beforeEach, List<Method> afterEach)
+   {
+      super(parent, clazz, execute, beforeEach, afterEach);
+   }
+
+   /**
+    * Call 4gl class instance or static method.
+    * 
+    * @param method
+    *        the Java method to call
+    * @param o
+    *        the class instance to invoke the method on, or {@code null} to call a static method
+    */
+   @Override
+   protected void callMethodImpl(final Method method, final Object o)
+   {
+      TestExecutionSupport.callClassMethod(method, (object<?>) o);
+   }
+
+
+}

=== added file 'src/com/goldencode/p2j/testengine/FWDClassTestDescriptor.java'
--- src/com/goldencode/p2j/testengine/FWDClassTestDescriptor.java	1970-01-01 00:00:00 +0000
+++ src/com/goldencode/p2j/testengine/FWDClassTestDescriptor.java	2022-10-28 14:40:39 +0000
@@ -0,0 +1,187 @@
+/*
+** Module   : FWDClassTestDescriptor.java
+** Abstract : Test descriptor for a legacy class.
+**
+** Copyright (c) 2022, Golden Code Development Corporation.
+**
+** -#- -I- --Date-- ----------------Description----------------------
+** 001 VVT 2022xxxx Created initial version.
+*/
+/*
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU Affero General Public License as
+** published by the Free Software Foundation, either version 3 of the
+** License, or (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU Affero General Public License for more details.
+**
+** You may find a copy of the GNU Affero GPL version 3 at the following
+** location: https://www.gnu.org/licenses/agpl-3.0.en.html
+** 
+** Additional terms under GNU Affero GPL version 3 section 7:
+** 
+**   Under Section 7 of the GNU Affero GPL version 3, the following additional
+**   terms apply to the works covered under the License.  These additional terms
+**   are non-permissive additional terms allowed under Section 7 of the GNU
+**   Affero GPL version 3 and may not be removed by you.
+** 
+**   0. Attribution Requirement.
+** 
+**     You must preserve all legal notices or author attributions in the covered
+**     work or Appropriate Legal Notices displayed by works containing the covered
+**     work.  You may not remove from the covered work any author or developer
+**     credit already included within the covered work.
+** 
+**   1. No License To Use Trademarks.
+** 
+**     This license does not grant any license or rights to use the trademarks
+**     Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
+**     of Golden Code Development Corporation. You are not authorized to use the
+**     name Golden Code, FWD, or the names of any author or contributor, for
+**     publicity purposes without written authorization.
+** 
+**   2. No Misrepresentation of Affiliation.
+** 
+**     You may not represent yourself as Golden Code Development Corporation or FWD.
+** 
+**     You may not represent yourself for publicity purposes as associated with
+**     Golden Code Development Corporation, FWD, or any author or contributor to
+**     the covered work, without written authorization.
+** 
+**   3. No Misrepresentation of Source or Origin.
+** 
+**     You may not represent the covered work as solely your work.  All modified
+**     versions of the covered work must be marked in a reasonable way to make it
+**     clear that the modified work is not originating from Golden Code Development
+**     Corporation or FWD.  All modified versions must contain the notices of
+**     attribution required in this license.
+*/
+package com.goldencode.p2j.testengine;
+
+import java.lang.reflect.*;
+import java.util.*;
+
+import org.junit.platform.engine.*;
+import org.junit.platform.engine.support.hierarchical.*;
+
+import com.goldencode.p2j.util.*;
+
+/**
+ * Test descriptor for a legacy class.
+ */
+public class FWDClassTestDescriptor
+extends AbstractClassTestDescriptor
+{
+
+   /**
+    * Test class instance to invoke the method on.
+    * Unlike JUnit5, the same instance is used to run all test methods
+    * for a class.
+    */
+   transient object<?> testInstance;
+
+   /**
+    * Default constructor, required for de-serialization
+    */
+   public FWDClassTestDescriptor()
+   {
+      // no-op
+   }
+
+   /**
+    * The constructor.
+    * 
+    * @param clazz
+    *        the Java class
+    * @param parent
+    *        the parent descriptor
+    * @param test
+    *        class methods marked as tests  
+    * @param beforeEach
+    *        class methods marked to execute before each test  
+    * @param afterEach
+    *        class methods marked to execute after each test  
+    * @param beforeAll
+    *        class methods marked to execute before all tests  
+    * @param afterAll
+    *        class methods marked to execute after all tests  
+    */
+   FWDClassTestDescriptor(Class<?> clazz, TestDescriptor parent, List<Method> test,
+            List<Method> beforeEach, List<Method> afterEach, List<Method> beforeAll,
+            List<Method> afterAll)
+   {
+      super(clazz, parent, test, beforeEach, afterEach, beforeAll, afterAll);
+      // Create method descriptors for "each" type methods
+      test.stream()
+               .map(m -> new FWDClassMethodTestDescriptor(this, clazz, m, beforeEach, afterEach))
+               .forEach(this::addChild);
+   }
+
+   /**
+    * Clean up the supplied {@code context} after execution implementation.
+    * 
+    * Reset the test class instance.
+    * 
+    * @throws Exception 
+    *
+    * @see #prepare
+    */
+   @Override
+   public void cleanUpImpl() throws Exception
+   {
+      // TODO do we need to do any additional cleanup?
+      testInstance = null;
+   }
+
+   /**
+    * Prepare the supplied {@code context} prior to execution implementation.
+    * 
+    * @throws Exception 
+    *
+    * @see #cleanUpImpl
+    */
+   @Override
+   public void prepareImpl() throws Exception
+   {
+      final object<?> inst = UndoableFactory.object(clazz);
+      inst.assign(ObjectOps.newInstance(clazz));
+      testInstance = inst;
+   }
+
+   /**
+    * Get test object. The test object is either a legacy class instance if tests are implemented
+    * as legacy class methods or a persistent procedure instance it tests are implemented as
+    * internal procedures.
+    * 
+    * The value should be {@code null} if the test method is a static legacy class method.
+    * 
+    * The value is created in class test descriptor {@link Node#prepare(EngineExecutionContext)},
+    * and destroyed in {@link Node#cleanUp(EngineExecutionContext)}.
+    * 
+    * @return the test object or {@code null}
+    *         
+    */
+   @Override
+   protected Object getTestObject()
+   {
+      return testInstance;
+   }
+
+   /**
+    * Call 4gl class instance or static method.
+    * 
+    * @param method
+    *        the Java method to call
+    * @param o
+    *        the class instance to invoke the method on, or {@code null} to call a static method
+    */
+   @Override
+   protected void callMethodImpl(final Method method, final Object o)
+   {
+      TestExecutionSupport.callClassMethod(method, (object<?>) o);
+   }
+
+}

=== added file 'src/com/goldencode/p2j/testengine/FWDEngineDescriptor.java'
--- src/com/goldencode/p2j/testengine/FWDEngineDescriptor.java	1970-01-01 00:00:00 +0000
+++ src/com/goldencode/p2j/testengine/FWDEngineDescriptor.java	2022-10-28 14:41:47 +0000
@@ -0,0 +1,162 @@
+/*
+** Module   : FWDEngineDescriptor.java
+** Abstract : Test engine descriptor.
+**
+** Copyright (c) 2022, Golden Code Development Corporation.
+**
+** -#- -I- --Date-- ----------------Description----------------------
+** 001 VVT 2022xxxx Created initial version.
+*/
+/*
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU Affero General Public License as
+** published by the Free Software Foundation, either version 3 of the
+** License, or (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU Affero General Public License for more details.
+**
+** You may find a copy of the GNU Affero GPL version 3 at the following
+** location: https://www.gnu.org/licenses/agpl-3.0.en.html
+** 
+** Additional terms under GNU Affero GPL version 3 section 7:
+** 
+**   Under Section 7 of the GNU Affero GPL version 3, the following additional
+**   terms apply to the works covered under the License.  These additional terms
+**   are non-permissive additional terms allowed under Section 7 of the GNU
+**   Affero GPL version 3 and may not be removed by you.
+** 
+**   0. Attribution Requirement.
+** 
+**     You must preserve all legal notices or author attributions in the covered
+**     work or Appropriate Legal Notices displayed by works containing the covered
+**     work.  You may not remove from the covered work any author or developer
+**     credit already included within the covered work.
+** 
+**   1. No License To Use Trademarks.
+** 
+**     This license does not grant any license or rights to use the trademarks
+**     Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
+**     of Golden Code Development Corporation. You are not authorized to use the
+**     name Golden Code, FWD, or the names of any author or contributor, for
+**     publicity purposes without written authorization.
+** 
+**   2. No Misrepresentation of Affiliation.
+** 
+**     You may not represent yourself as Golden Code Development Corporation or FWD.
+** 
+**     You may not represent yourself for publicity purposes as associated with
+**     Golden Code Development Corporation, FWD, or any author or contributor to
+**     the covered work, without written authorization.
+** 
+**   3. No Misrepresentation of Source or Origin.
+** 
+**     You may not represent the covered work as solely your work.  All modified
+**     versions of the covered work must be marked in a reasonable way to make it
+**     clear that the modified work is not originating from Golden Code Development
+**     Corporation or FWD.  All modified versions must contain the notices of
+**     attribution required in this license.
+*/
+package com.goldencode.p2j.testengine;
+
+import org.junit.platform.commons.*;
+import org.junit.platform.engine.*;
+
+import com.goldencode.p2j.persist.trigger.*;
+import com.goldencode.p2j.ui.*;
+import com.goldencode.p2j.util.*;
+
+/**
+ * Test engine descriptor. Provides this engine unique ID and same thread execution mode. 
+ */
+public class FWDEngineDescriptor
+extends AbstractBranchTestDescriptor
+{
+   /**
+    * Default constructor for de-serialization only,
+    * must never be used by applications.
+    */
+   public FWDEngineDescriptor()
+   {
+      this(null);
+   }
+
+   /**
+    * The constructor.
+    * 
+    * @param uniqueId the {@code UniqueId} for the described {@code TestEngine};
+    * never {@code null}
+    */
+   public FWDEngineDescriptor(final UniqueId uniqueId)
+   {
+      super(uniqueId, "FWD Test");
+   }
+
+   /**
+    * Get the preferred of {@linkplain org.junit.platform.engine.support.hierarchical.Node.ExecutionMode execution mode} for
+    * parallel execution of this node.
+    *
+    * @return the preferred execution mode of this node; never {@code null}
+    * @see org.junit.platform.engine.support.hierarchical.Node.ExecutionMode
+    */
+   @Override
+   public ExecutionMode getExecutionMode()
+   {
+      // No concurrent execution is planned yet
+      return ExecutionMode.SAME_THREAD;
+   }
+
+   /**
+    * Remove this non-root descriptor from its parent and remove all the
+    * children from this descriptor.
+    *
+    * <p>If this method is invoked on a {@linkplain #isRoot root} descriptor,
+    * this method must throw a {@link org.junit.platform.commons.JUnitException
+    * JUnitException} explaining that a root cannot be removed from the
+    * hierarchy.
+    */
+   @Override
+   public void removeFromHierarchy()
+   {
+      throw new PreconditionViolationException("cannot remove the root of a hierarchy");
+   }
+
+   /**
+    * Clean up the supplied {@code context} after execution implementation.
+    * 
+    * Reset the test class instance.
+    * 
+    * @throws Exception 
+    *
+    * @see #prepare
+    */
+   @Override
+   public void cleanUpImpl() throws Exception
+   {
+      TransactionManager.popScope();
+   }
+
+   /**
+    * Prepare the supplied {@code context} prior to execution implementation.
+    * 
+    * @throws Exception 
+    *
+    * @see #cleanUpImpl
+    */
+   @Override
+   public void prepareImpl() throws Exception
+   {
+      // force DatabaseTriggerManager context-local instantiation before the new scope is pushed
+      DatabaseTriggerManager.get();
+
+      // add the topmost scope to the TransactionManager
+      TransactionManager.pushScope("startup", TransactionManager.NO_TRANSACTION, true, true,
+               false, false);
+
+      // make sure that the global block gets a special notification - but only for non-appserver sessions.
+      LogicalTerminal.registerCleaner();
+   }
+
+}

=== added file 'src/com/goldencode/p2j/testengine/FWDEngineExecutionContext.java'
--- src/com/goldencode/p2j/testengine/FWDEngineExecutionContext.java	1970-01-01 00:00:00 +0000
+++ src/com/goldencode/p2j/testengine/FWDEngineExecutionContext.java	2022-10-28 14:36:00 +0000
@@ -0,0 +1,85 @@
+/*
+** Module   : FWDEngineExecutionContext.java
+** Abstract : Test engine execution context.
+**
+** Copyright (c) 2022, Golden Code Development Corporation.
+**
+** -#- -I- --Date-- ----------------Description----------------------
+** 001 VVT 2022xxxx Created initial version.
+*/
+/*
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU Affero General Public License as
+** published by the Free Software Foundation, either version 3 of the
+** License, or (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU Affero General Public License for more details.
+**
+** You may find a copy of the GNU Affero GPL version 3 at the following
+** location: https://www.gnu.org/licenses/agpl-3.0.en.html
+** 
+** Additional terms under GNU Affero GPL version 3 section 7:
+** 
+**   Under Section 7 of the GNU Affero GPL version 3, the following additional
+**   terms apply to the works covered under the License.  These additional terms
+**   are non-permissive additional terms allowed under Section 7 of the GNU
+**   Affero GPL version 3 and may not be removed by you.
+** 
+**   0. Attribution Requirement.
+** 
+**     You must preserve all legal notices or author attributions in the covered
+**     work or Appropriate Legal Notices displayed by works containing the covered
+**     work.  You may not remove from the covered work any author or developer
+**     credit already included within the covered work.
+** 
+**   1. No License To Use Trademarks.
+** 
+**     This license does not grant any license or rights to use the trademarks
+**     Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
+**     of Golden Code Development Corporation. You are not authorized to use the
+**     name Golden Code, FWD, or the names of any author or contributor, for
+**     publicity purposes without written authorization.
+** 
+**   2. No Misrepresentation of Affiliation.
+** 
+**     You may not represent yourself as Golden Code Development Corporation or FWD.
+** 
+**     You may not represent yourself for publicity purposes as associated with
+**     Golden Code Development Corporation, FWD, or any author or contributor to
+**     the covered work, without written authorization.
+** 
+**   3. No Misrepresentation of Source or Origin.
+** 
+**     You may not represent the covered work as solely your work.  All modified
+**     versions of the covered work must be marked in a reasonable way to make it
+**     clear that the modified work is not originating from Golden Code Development
+**     Corporation or FWD.  All modified versions must contain the notices of
+**     attribution required in this license.
+*/
+package com.goldencode.p2j.testengine;
+
+import org.junit.platform.engine.support.hierarchical.*;
+
+/**
+ * Test engine execution context.
+ * 
+ */
+public class FWDEngineExecutionContext
+implements EngineExecutionContext
+{
+   /** Proxy instance to execute remove operations on the server */
+   final UnitTestEngine unitTestSupport;
+
+   /**
+    * The constructor.
+    * 
+    * @param unitTestSupport 
+    */
+   FWDEngineExecutionContext(final UnitTestEngine unitTestSupport)
+   {
+      this.unitTestSupport = unitTestSupport;
+   }
+}

=== added file 'src/com/goldencode/p2j/testengine/FWDProcedureMethodTestDescriptor.java'
--- src/com/goldencode/p2j/testengine/FWDProcedureMethodTestDescriptor.java	1970-01-01 00:00:00 +0000
+++ src/com/goldencode/p2j/testengine/FWDProcedureMethodTestDescriptor.java	2022-10-28 14:36:09 +0000
@@ -0,0 +1,118 @@
+/*
+** Module   : FWDProcedureMethodTestDescriptor.java
+** Abstract : Test descriptor for a legacy internal procedure.
+**
+** Copyright (c) 2022, Golden Code Development Corporation.
+**
+** -#- -I- --Date-- ----------------Description----------------------
+** 001 VVT 2022xxxx Created initial version.
+*/
+/*
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU Affero General Public License as
+** published by the Free Software Foundation, either version 3 of the
+** License, or (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU Affero General Public License for more details.
+**
+** You may find a copy of the GNU Affero GPL version 3 at the following
+** location: https://www.gnu.org/licenses/agpl-3.0.en.html
+** 
+** Additional terms under GNU Affero GPL version 3 section 7:
+** 
+**   Under Section 7 of the GNU Affero GPL version 3, the following additional
+**   terms apply to the works covered under the License.  These additional terms
+**   are non-permissive additional terms allowed under Section 7 of the GNU
+**   Affero GPL version 3 and may not be removed by you.
+** 
+**   0. Attribution Requirement.
+** 
+**     You must preserve all legal notices or author attributions in the covered
+**     work or Appropriate Legal Notices displayed by works containing the covered
+**     work.  You may not remove from the covered work any author or developer
+**     credit already included within the covered work.
+** 
+**   1. No License To Use Trademarks.
+** 
+**     This license does not grant any license or rights to use the trademarks
+**     Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
+**     of Golden Code Development Corporation. You are not authorized to use the
+**     name Golden Code, FWD, or the names of any author or contributor, for
+**     publicity purposes without written authorization.
+** 
+**   2. No Misrepresentation of Affiliation.
+** 
+**     You may not represent yourself as Golden Code Development Corporation or FWD.
+** 
+**     You may not represent yourself for publicity purposes as associated with
+**     Golden Code Development Corporation, FWD, or any author or contributor to
+**     the covered work, without written authorization.
+** 
+**   3. No Misrepresentation of Source or Origin.
+** 
+**     You may not represent the covered work as solely your work.  All modified
+**     versions of the covered work must be marked in a reasonable way to make it
+**     clear that the modified work is not originating from Golden Code Development
+**     Corporation or FWD.  All modified versions must contain the notices of
+**     attribution required in this license.
+*/
+package com.goldencode.p2j.testengine;
+
+import java.lang.reflect.*;
+import java.util.*;
+
+import com.goldencode.p2j.util.*;
+
+/**
+ * Test descriptor for a legacy internal procedure.
+ */
+public class FWDProcedureMethodTestDescriptor
+extends AbstractMethodTestDescriptor
+{
+
+   /**
+    * Default constructor, required for de-serialization
+    */
+   public FWDProcedureMethodTestDescriptor()
+   {
+      // no-op
+   }
+
+   /**
+    * Constructor.
+    * 
+    * @param parent 
+    *        the parent descriptor
+    * @param clazz
+    *        the Java class to create instances
+    * @param execute
+    *        the method to execute
+    * @param beforeEach
+    *        methods to execute before the execute method
+    * @param afterEach
+    *        methods to execute after the execute method
+    */
+   FWDProcedureMethodTestDescriptor(AbstractClassTestDescriptor parent, Class<?> clazz,
+            Method execute, List<Method> beforeEach, List<Method> afterEach)
+   {
+      super(parent, clazz, execute, beforeEach, afterEach);
+   }
+
+   /**
+    * Execute a method.
+    *  
+    * @param method
+    *        the method to execute
+    * @param o
+    *        the object to use, must be a procedure handle
+    */
+   @Override
+   protected void callMethodImpl(final Method method, final Object o)
+   {
+      TestExecutionSupport.callProcedureMethod(method, (handle) o);
+   }
+
+}

=== added file 'src/com/goldencode/p2j/testengine/FWDProcedureTestDescriptor.java'
--- src/com/goldencode/p2j/testengine/FWDProcedureTestDescriptor.java	1970-01-01 00:00:00 +0000
+++ src/com/goldencode/p2j/testengine/FWDProcedureTestDescriptor.java	2022-10-28 14:43:23 +0000
@@ -0,0 +1,200 @@
+/*
+** Module   : FWDProcedureTestDescriptor.java
+** Abstract : Test descriptor for a legacy procedure.
+**
+** Copyright (c) 2022, Golden Code Development Corporation.
+**
+** -#- -I- --Date-- ----------------Description----------------------
+** 001 VVT 2022xxxx Created initial version.
+*/
+/*
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU Affero General Public License as
+** published by the Free Software Foundation, either version 3 of the
+** License, or (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU Affero General Public License for more details.
+**
+** You may find a copy of the GNU Affero GPL version 3 at the following
+** location: https://www.gnu.org/licenses/agpl-3.0.en.html
+** 
+** Additional terms under GNU Affero GPL version 3 section 7:
+** 
+**   Under Section 7 of the GNU Affero GPL version 3, the following additional
+**   terms apply to the works covered under the License.  These additional terms
+**   are non-permissive additional terms allowed under Section 7 of the GNU
+**   Affero GPL version 3 and may not be removed by you.
+** 
+**   0. Attribution Requirement.
+** 
+**     You must preserve all legal notices or author attributions in the covered
+**     work or Appropriate Legal Notices displayed by works containing the covered
+**     work.  You may not remove from the covered work any author or developer
+**     credit already included within the covered work.
+** 
+**   1. No License To Use Trademarks.
+** 
+**     This license does not grant any license or rights to use the trademarks
+**     Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
+**     of Golden Code Development Corporation. You are not authorized to use the
+**     name Golden Code, FWD, or the names of any author or contributor, for
+**     publicity purposes without written authorization.
+** 
+**   2. No Misrepresentation of Affiliation.
+** 
+**     You may not represent yourself as Golden Code Development Corporation or FWD.
+** 
+**     You may not represent yourself for publicity purposes as associated with
+**     Golden Code Development Corporation, FWD, or any author or contributor to
+**     the covered work, without written authorization.
+** 
+**   3. No Misrepresentation of Source or Origin.
+** 
+**     You may not represent the covered work as solely your work.  All modified
+**     versions of the covered work must be marked in a reasonable way to make it
+**     clear that the modified work is not originating from Golden Code Development
+**     Corporation or FWD.  All modified versions must contain the notices of
+**     attribution required in this license.
+*/
+package com.goldencode.p2j.testengine;
+
+import java.lang.reflect.*;
+import java.util.*;
+
+import org.junit.platform.engine.*;
+import org.junit.platform.engine.support.hierarchical.*;
+
+import com.goldencode.p2j.util.*;
+
+/**
+ * Test descriptor for a legacy procedure.
+ */
+public class FWDProcedureTestDescriptor
+extends AbstractClassTestDescriptor
+{
+
+   /**
+    * Persistent procedure instance to invoke the methods on.
+    * 
+    * Unlike JUnit5, the same instance is used to run all test methods
+    * for a procedure.
+    */
+   transient handle procedure;
+
+   /**
+    * Default constructor, required for de-serialization
+    */
+   public FWDProcedureTestDescriptor()
+   {
+      // no-op
+   }
+
+   /**
+    * The constructor.
+    * 
+    * @param clazz
+    *        the Java class
+    * @param parent
+    *        the parent descriptor
+    * @param test
+    *        class methods marked as tests  
+    * @param beforeEach
+    *        class methods marked to execute before each test  
+    * @param afterEach
+    *        class methods marked to execute after each test  
+    * @param beforeAll
+    *        class methods marked to execute before all tests  
+    * @param afterAll
+    *        class methods marked to execute after all tests  
+    */
+   FWDProcedureTestDescriptor(Class<?> clazz, TestDescriptor parent, List<Method> test,
+            List<Method> beforeEach, List<Method> afterEach, List<Method> beforeAll,
+            List<Method> afterAll)
+   {
+      super(clazz, parent, test, beforeEach, afterEach, beforeAll, afterAll);
+      // Create method descriptors for "each" type methods
+      test.stream().map(
+               m -> new FWDProcedureMethodTestDescriptor(this, clazz, m, beforeEach, afterEach))
+               .forEach(this::addChild);
+
+   }
+
+   /**
+    * Clean up the supplied {@code context} after execution implementation.
+    * 
+    * Reset the test class instance.
+    * 
+    * @throws Exception 
+    *
+    * @see #prepare
+    */
+   @Override
+   public void cleanUpImpl() throws Exception
+   {
+      // TODO do we need to do any additional cleanup?
+      procedure = null;
+   }
+
+   /**
+    * Prepare the supplied {@code context} prior to execution implementation.
+    * 
+    * @throws Exception 
+    *
+    * @see #cleanUpImpl
+    */
+   @Override
+   public void prepareImpl() throws Exception
+   {
+      try
+      {
+         // Locate the execute() method
+         final Method m = clazz.getDeclaredMethod("execute");
+         final LegacySignature annotation = m.getAnnotation(LegacySignature.class);
+         final String name = annotation.name();
+         final handle h = new handle();
+         new InvokeConfig(name).persistent().setProcedureHandle(h).run();
+         procedure = h;
+      }
+      catch (NoSuchMethodException | SecurityException e)
+      {
+         throw new RuntimeException(e);
+      }
+   }
+
+   /**
+    * Get test object. The test object is either a legacy class instance if tests are implemented
+    * as legacy class methods or a persistent procedure instance it tests are implemented as
+    * internal procedures.
+    * 
+    * The value should be {@code null} if the test method is a static legacy class method.
+    * 
+    * The value is created in class test descriptor {@link Node#prepare(EngineExecutionContext)},
+    * and destroyed in {@link Node#cleanUp(EngineExecutionContext)}.
+    * 
+    * @return the test object or {@code null}
+    *         
+    */
+   @Override
+   protected Object getTestObject()
+   {
+      return procedure;
+   }
+
+   /**
+    * Execute a method.
+    *  
+    * @param method
+    *        the method to execute
+    * @param o
+    *        the object to use, can be {@code null} for "static" methods. 
+    */
+   @Override
+   protected void callMethodImpl(Method method, Object o)
+   {
+      TestExecutionSupport.callProcedureMethod(method, (handle) o);
+   }
+
+}

=== added file 'src/com/goldencode/p2j/testengine/FWDTestEngine.java'
--- src/com/goldencode/p2j/testengine/FWDTestEngine.java	1970-01-01 00:00:00 +0000
+++ src/com/goldencode/p2j/testengine/FWDTestEngine.java	2022-10-27 14:44:37 +0000
@@ -0,0 +1,401 @@
+/*
+** Module   : FWDTestEngine.java
+** Abstract : Test engine.
+**
+** Copyright (c) 2022, Golden Code Development Corporation.
+**
+** -#- -I- --Date-- ----------------Description----------------------
+** 001 VVT 2022xxxx Created initial version.
+*/
+/*
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU Affero General Public License as
+** published by the Free Software Foundation, either version 3 of the
+** License, or (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU Affero General Public License for more details.
+**
+** You may find a copy of the GNU Affero GPL version 3 at the following
+** location: https://www.gnu.org/licenses/agpl-3.0.en.html
+** 
+** Additional terms under GNU Affero GPL version 3 section 7:
+** 
+**   Under Section 7 of the GNU Affero GPL version 3, the following additional
+**   terms apply to the works covered under the License.  These additional terms
+**   are non-permissive additional terms allowed under Section 7 of the GNU
+**   Affero GPL version 3 and may not be removed by you.
+** 
+**   0. Attribution Requirement.
+** 
+**     You must preserve all legal notices or author attributions in the covered
+**     work or Appropriate Legal Notices displayed by works containing the covered
+**     work.  You may not remove from the covered work any author or developer
+**     credit already included within the covered work.
+** 
+**   1. No License To Use Trademarks.
+** 
+**     This license does not grant any license or rights to use the trademarks
+**     Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
+**     of Golden Code Development Corporation. You are not authorized to use the
+**     name Golden Code, FWD, or the names of any author or contributor, for
+**     publicity purposes without written authorization.
+** 
+**   2. No Misrepresentation of Affiliation.
+** 
+**     You may not represent yourself as Golden Code Development Corporation or FWD.
+** 
+**     You may not represent yourself for publicity purposes as associated with
+**     Golden Code Development Corporation, FWD, or any author or contributor to
+**     the covered work, without written authorization.
+** 
+**   3. No Misrepresentation of Source or Origin.
+** 
+**     You may not represent the covered work as solely your work.  All modified
+**     versions of the covered work must be marked in a reasonable way to make it
+**     clear that the modified work is not originating from Golden Code Development
+**     Corporation or FWD.  All modified versions must contain the notices of
+**     attribution required in this license.
+*/
+package com.goldencode.p2j.testengine;
+
+import org.junit.platform.engine.*;
+import org.junit.platform.engine.discovery.*;
+import org.junit.platform.engine.support.hierarchical.*;
+
+import com.goldencode.p2j.cfg.*;
+import com.goldencode.p2j.directory.*;
+import com.goldencode.p2j.main.*;
+import com.goldencode.p2j.net.*;
+import com.goldencode.p2j.net.Session;
+import com.goldencode.p2j.security.*;
+import com.goldencode.p2j.security.SecurityManager;
+import com.goldencode.p2j.ui.chui.*;
+import com.goldencode.p2j.ui.client.*;
+import com.goldencode.p2j.ui.client.driver.*;
+import com.goldencode.p2j.util.*;
+
+/**
+ * FWD Test engine.
+ *
+ */
+public class FWDTestEngine
+extends HierarchicalTestEngine<FWDEngineExecutionContext>
+{
+
+   /**
+    * Flag indicating this engine is disabled.
+    * 
+    * If this engine initialization ended with an exception, it does not throw the exception,
+    * but continue disabled instead. Disabled engine will never discover any tests.
+    * 
+    * This feature let other test engines a chance to work.
+    */
+   private boolean disabled;
+   
+   /**
+    * The session. Created in the {@link #initialize(BootstrapConfig)}, and
+    * closed in {@link #shutdown(BootstrapConfig)}.
+    */
+   private static Session session;
+
+   /**
+    * Unit test support on the server proxy instance.
+    */
+   private static UnitTestEngine unitTestSupport;
+
+   /**
+    * Fill in the bootstrap configuration with some fixed values.
+    * 
+    * WARNING: this method is a temporary replacement for the normal
+    * FWD bootstrap configuration process. It must be used for debugging only!
+    *  
+    * WARNING: the values compiled in should be probably edited manually
+    *  
+    * @return
+    *        the configuration instance
+    *        
+    * @throws ConfigurationException
+    */
+   private static BootstrapConfig initConfig() throws ConfigurationException
+   {
+      final BootstrapConfig bc = new BootstrapConfig(null, null, null, null);
+
+      bc.setServer(false);
+
+      bc.setConfigItem("net", "queue", "conversation", "true");
+
+      // either way, force start thread to false (this client is going to
+      // use its main thread as the conversation thread so we don't need
+      // to start one
+      bc.setConfigItem("net", "queue", "start_thread", "false");
+
+      // TODO: these configuration statements are temporarily for initial debugging only!!! 
+      bc.setConfigItem("net", "server", "host", "localhost");
+      bc.setConfigItem("net", "connection", "secure", "false");
+      bc.setConfigItem("net", "server", "secure_port", "4434");
+      bc.setConfigItem("net", "server", "insecure_port", "4534");
+      bc.setConfigItem("client", "driver", "type", "gui_swing");
+      bc.setConfigItem("client", "driver", "theme",
+               "com.goldencode.p2j.ui.client.gui.theme.Windows10Theme");
+      return bc;
+   }
+
+   /**
+    * Initialize FWD client process.
+    * 
+    * @param config
+    *        the input bootstrap configuration
+    *        
+    * @throws Exception
+    */
+   private static void initialize(final BootstrapConfig config) throws Exception
+   {
+      // Load native library
+      System.loadLibrary("p2j");
+      ClientCore.processInit();
+
+      SecurityManager.createInstance(config);
+
+      // instantiate network session manager
+      final SessionManager sessMgr = SessionManagerFactory.createLeafNode(config);
+
+      // initialize core UI services
+      ThinClient.initializePrep(config, null /* driver */, false);
+
+      final ScreenDriver<?> driver = OutputManager.getDriver();
+
+      // establish or re-establish a session with the P2J server
+      // (this handles authentication too)
+      session = sessMgr.connectDirect(new InterruptedExceptionHandler(), null);
+
+      // some client code must read configuration from the directory
+      DirectoryManager.initRemote(session);
+
+      // FIXME: do we really need this MainEntry?
+      final MainEntry main = (MainEntry) RemoteObject.obtainNetworkInstance(MainEntry.class,
+               session);
+
+      final String projectToken = config.getString("client", "project", "token", null);
+      // this will set the token from either client:project:token or directory's project_token.
+      main.setProjectToken(projectToken);
+
+      // attempt to emulate the '-ininame' command-line parameter:
+      String iniFileName = config.getString("client", "cmd-line-option", "ininame", "");
+
+      final Directory remoteDir = DirectoryManager.getInstance();
+      if (iniFileName.isEmpty() && remoteDir != null)
+      {
+         iniFileName = remoteDir.getString(Directory.ID_RELATIVE, "ininame", "");
+      }
+
+      // finish initializing the thin client
+      ThinClient.initializePost(config, session, null, main.loadStartupParameters(), iniFileName);
+
+      if (remoteDir != null)
+      {
+         // initialize the Ease Access Manager
+         EaseAccessManager.init(remoteDir);
+      }
+
+      ThinClient.loadEnvironment(config);
+
+      // After full initialization of the client code we can set up the ErrorWriter for
+      // client side.
+      if (session != null)
+      {
+         OutputManager.initErrorWriter(session);
+      }
+
+      // initializeLogger();
+      // outputDiagnostics(config, diag);
+
+      // determine any client-side parameters
+      ClientParameters params = processClientParams(driver, config);
+
+      // Propagate client-side parameters to the server
+      main.setClientParams(params);
+      ThinClient.setClientParameters(params);
+
+      if (params.clientlog != null)
+      {
+         ThinClient.initClientLog(params.clientlog);
+      }
+
+      // initialize initial tooltip settings
+      ThinClient.setSessionTooltips();
+
+      // above this point, any CTRL-C processing that causes a STOP
+      // condition to be thrown will cause an exit (this is very timing
+      // sensitive but if you hold down the CTRL-C keys AND the server
+      // environment is configured to force a relogin (the return from
+      // standardEntry() is true) then at some point you will see such
+      // an exception; we can protect against this, BUT this same behavior
+      // has been seen in the 4GL, so it has been left here on purpose
+
+      // Activates batch mode if exists. If batch mode operation is requested the real work
+      // should be postponed until all subsystems to be initialized properly using usual
+      // console mode. At this point this preliminary work should be done and we can
+      // "activate" the special processing for batch mode. 
+      if (OutputManager.inBatchMode())
+      {
+         OutputManager.activateBatch(true);
+      }
+
+      driver.clientReady();
+      ThinClient.getInstance().updateClientMetrics();
+
+      // set up current user name for client session
+      ThinClient.getInstance().setUserName(params.osUserName);
+
+      unitTestSupport = (UnitTestEngine) RemoteObject.obtainNetworkInstance(UnitTestEngine.class,
+               session);
+
+      // Do server-side initialization
+      unitTestSupport.initialize();
+   }
+
+   /**
+    * Create a {@link ClientParameters} instance from {@link BootstrapConfig}.
+    * 
+    * @param driver
+    *        screen driver
+    * @param config
+    *        input bootstrap config
+    * 
+    * @return the instance created
+    */
+   private static ClientParameters processClientParams(final ScreenDriver<?> driver,
+            final BootstrapConfig config)
+   {
+      final ClientParameters cp = new ClientParameters();
+      final String win = driver.getWindowSystem();
+
+      cp.windowSystem = config.getString("client", "driver", "window-system", win);
+      cp.param = config.getString("client", "cmd-line-option", "param", null);
+      cp.startupProc = config.getString("client", "cmd-line-option", "startup-procedure", null);
+      cp.clientlog = config.getString("client", "cmd-line-option", "clientlog", null);
+      cp.osUserName = ClientCore.getUserName();
+      cp.terminalName = config.getString("client", "tty", "name", null);
+      cp.debugAlert = config.getBoolean("client", "cmd-line-option", "debugalert", false);
+      cp.errorStack = config.getBoolean("client", "cmd-line-option", "errorstack", false);
+      cp.pid = ClientCore.getPid();
+      cp.chui = driver.isChui();
+      cp.batchMode = config.getBoolean("client", "mode", "batch", false) ? 2 : 0;
+      cp.socketTrustStoreFilename = config.getString("ssl-socket", "truststore", "location",
+               "trusted-cert.store");
+      cp.socketTrustStorePassword = config.getString("ssl-socket", "truststore", "password", "");
+
+      return cp;
+   }
+
+   /**
+    * Terminate FWD client process.
+    * 
+    * FIXME: method is not used now
+    * 
+    * @param config
+    *        input bootstrap config
+    * @throws RestrictedUseException
+    */
+   @SuppressWarnings("unused")
+   private final static void shutdown(final BootstrapConfig config) throws RestrictedUseException
+   {
+      //dependent threads need to be shutdown, even if the client restarts on CTRL-C
+      ThinClient.terminate();
+
+      OutputManager.getDriver().shutdown();
+
+      // shut down session
+      session.terminate();
+
+      // clear after me
+      SecurityManager.clearInstance();
+   }
+
+   /**
+    * Default constructor.
+    */
+   public FWDTestEngine()
+   {
+      // process configuration. In most use cases (e.g. running from IDE), it is not possible to pass
+      // any command line arguments: so only config files can be used for the purpose.
+      try
+      {
+         final BootstrapConfig bc = initConfig();
+
+         initialize(bc);
+      }
+      catch (Throwable thr)
+      {
+         System.err.println("Test engine cannot initialize and will be disabled: " + thr);
+         disabled = true;
+      }
+
+   }
+
+   /**
+    * Discover tests according to the supplied {@link EngineDiscoveryRequest}.
+    *
+    * <p>The supplied {@link UniqueId} must be used as the unique ID of the
+    * returned root {@link TestDescriptor}. In addition, the {@code UniqueId}
+    * must be used to create unique IDs for children of the root's descriptor
+    * by calling {@link UniqueId#append}.
+    *
+    * @param discoveryRequest the discovery request; never {@code null}
+    * @param uniqueId the unique ID to be used for this test engine's
+    * {@code TestDescriptor}; never {@code null}
+    * @return the root {@code TestDescriptor} of this engine, typically an
+    * instance of {@code EngineDescriptor}
+    * @see org.junit.platform.engine.support.descriptor.EngineDescriptor
+    */
+   @Override
+   public TestDescriptor discover(final EngineDiscoveryRequest discoveryRequest,
+            final UniqueId uniqueId)
+   {
+      if (disabled)
+      {
+         return new NoopDescriptor(UniqueId.forEngine(getId()), "No-op");
+      }
+      
+      return unitTestSupport.discover(uniqueId,
+               discoveryRequest.getSelectorsByType(PackageSelector.class).stream()
+                        .map(PackageSelector::getPackageName).toArray(String[]::new),
+               discoveryRequest.getSelectorsByType(ClassSelector.class).stream()
+                        .map(ClassSelector::getClassName).toArray(String[]::new),
+               discoveryRequest.getSelectorsByType(MethodSelector.class).stream()
+                        .map(s -> s.getClassName() + "#" + s.getMethodName())
+                        .toArray(String[]::new));
+   }
+
+   /**
+    * Get the ID that uniquely identifies this test engine.
+    *
+    * <p>Each test engine must provide a unique ID. For example, JUnit Vintage
+    * and JUnit Jupiter use {@code "junit-vintage"} and {@code "junit-jupiter"},
+    * respectively. When in doubt, you may use the fully qualified name of your
+    * custom {@code TestEngine} implementation class.
+    */
+   @Override
+   public String getId()
+   {
+      // Note: "junit-fwd" would be more natural, but the "junit-" prefix
+      // is reserved by JUnit5
+      return "fwd-junit";
+   }
+
+   /**
+    * Create the initial execution context for executing the supplied
+    * {@linkplain ExecutionRequest request}.
+    *
+    * @param request the request about to be executed
+    * @return the initial context that will be passed to nodes in the hierarchy
+    */
+   @Override
+   protected FWDEngineExecutionContext createExecutionContext(ExecutionRequest request)
+   {
+      return new FWDEngineExecutionContext(unitTestSupport);
+   }
+}

=== added file 'src/com/goldencode/p2j/testengine/SerializationSupport.java'
--- src/com/goldencode/p2j/testengine/SerializationSupport.java	1970-01-01 00:00:00 +0000
+++ src/com/goldencode/p2j/testengine/SerializationSupport.java	2022-10-12 21:24:04 +0000
@@ -0,0 +1,163 @@
+/*
+** Module   : SerializationSupport.java
+** Abstract : Test descriptor serialization support
+**
+** Copyright (c) 2022, Golden Code Development Corporation.
+**
+** -#- -I- --Date-- ----------------Description----------------------
+** 001 VVT 2022xxxx Created initial version.
+*/
+/*
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU Affero General Public License as
+** published by the Free Software Foundation, either version 3 of the
+** License, or (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU Affero General Public License for more details.
+**
+** You may find a copy of the GNU Affero GPL version 3 at the following
+** location: https://www.gnu.org/licenses/agpl-3.0.en.html
+** 
+** Additional terms under GNU Affero GPL version 3 section 7:
+** 
+**   Under Section 7 of the GNU Affero GPL version 3, the following additional
+**   terms apply to the works covered under the License.  These additional terms
+**   are non-permissive additional terms allowed under Section 7 of the GNU
+**   Affero GPL version 3 and may not be removed by you.
+** 
+**   0. Attribution Requirement.
+** 
+**     You must preserve all legal notices or author attributions in the covered
+**     work or Appropriate Legal Notices displayed by works containing the covered
+**     work.  You may not remove from the covered work any author or developer
+**     credit already included within the covered work.
+** 
+**   1. No License To Use Trademarks.
+** 
+**     This license does not grant any license or rights to use the trademarks
+**     Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
+**     of Golden Code Development Corporation. You are not authorized to use the
+**     name Golden Code, FWD, or the names of any author or contributor, for
+**     publicity purposes without written authorization.
+** 
+**   2. No Misrepresentation of Affiliation.
+** 
+**     You may not represent yourself as Golden Code Development Corporation or FWD.
+** 
+**     You may not represent yourself for publicity purposes as associated with
+**     Golden Code Development Corporation, FWD, or any author or contributor to
+**     the covered work, without written authorization.
+** 
+**   3. No Misrepresentation of Source or Origin.
+** 
+**     You may not represent the covered work as solely your work.  All modified
+**     versions of the covered work must be marked in a reasonable way to make it
+**     clear that the modified work is not originating from Golden Code Development
+**     Corporation or FWD.  All modified versions must contain the notices of
+**     attribution required in this license.
+*/
+package com.goldencode.p2j.testengine;
+
+import java.io.*;
+import java.lang.reflect.*;
+import java.util.*;
+
+/**
+ * Serialization support utilities
+ *
+ */
+public class SerializationSupport
+{
+   /**
+    * Read {@link Method} from binary input stream previously serialized with
+    * {@link #writeMethod(Method, ObjectOutput)}.
+    * 
+    * @param in
+    *        the input stream to read method from
+    *        
+    * @return the method
+    * 
+    * @throws IOException
+    * @throws SecurityException
+    */
+   public final static Method readMethod(final ObjectInput in) throws IOException
+   {
+      try
+      {
+         final Class<?> clazz = (Class<?>) in.readObject();
+         final String methodName = (String) in.readObject();
+         return clazz.getMethod(methodName);
+      }
+      catch (ClassNotFoundException | NoSuchMethodException | SecurityException e)
+      {
+         throw new RuntimeException(e);
+      }
+   }
+
+   /**
+    * Read list of {@link Method} objects from binary input stream previously serialized with 
+    *  {@link #writeMethods(List, ObjectOutput)}.
+    * 
+    * @param in
+    *        the input stream to read method from
+    *        
+    * @return the method list
+    * 
+    * @throws IOException
+    * @throws SecurityException
+    */
+   public final static List<Method> readMethods(final ObjectInput in) throws IOException
+   {
+      final List<Method> result = new ArrayList<>();
+      final int size = in.readInt();
+      for (int i = 0; i < size; i++)
+      {
+         result.add(readMethod(in));
+      }
+      
+      return result;
+   }
+
+   /**
+    * Write {@link Method} to binary output stream.
+    * 
+    * @param method
+    *        the method to write
+    * @param out
+    *        the output stream to write methods to
+    *        
+    * @throws IOException
+    * @throws SecurityException
+    */
+   public final static void writeMethod(final Method method, final ObjectOutput out)
+            throws IOException, SecurityException
+   {
+      out.writeObject(method.getDeclaringClass());
+      out.writeObject(method.getName());
+   }
+
+   /**
+    * Write list of {@link Method} objects to binary output stream.
+    * 
+    * @param methods
+    *        the method list to write
+    * @param out
+    *        the output stream to write methods to
+    *        
+    * @throws IOException
+    * @throws SecurityException
+    */
+   public final static void writeMethods(final List<Method> methods, final ObjectOutput out)
+            throws IOException, SecurityException
+   {
+      out.writeInt(methods.size());
+      for (final Method m : methods)
+      {
+         writeMethod(m, out);
+      }
+   }
+
+}

=== added file 'src/com/goldencode/p2j/testengine/TestExecutionSupport.java'
--- src/com/goldencode/p2j/testengine/TestExecutionSupport.java	1970-01-01 00:00:00 +0000
+++ src/com/goldencode/p2j/testengine/TestExecutionSupport.java	2022-10-21 15:40:15 +0000
@@ -0,0 +1,193 @@
+/*
+** Module   : TestExecutionSupport.java
+** Abstract : Static methods to support legacy class methods and legacy procedure execution.
+**
+** Copyright (c) 2022, Golden Code Development Corporation.
+**
+** -#- -I- --Date-- ----------------Description----------------------
+** 001 VVT 2022xxxx Created initial version.
+*/
+/*
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU Affero General Public License as
+** published by the Free Software Foundation, either version 3 of the
+** License, or (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU Affero General Public License for more details.
+**
+** You may find a copy of the GNU Affero GPL version 3 at the following
+** location: https://www.gnu.org/licenses/agpl-3.0.en.html
+** 
+** Additional terms under GNU Affero GPL version 3 section 7:
+** 
+**   Under Section 7 of the GNU Affero GPL version 3, the following additional
+**   terms apply to the works covered under the License.  These additional terms
+**   are non-permissive additional terms allowed under Section 7 of the GNU
+**   Affero GPL version 3 and may not be removed by you.
+** 
+**   0. Attribution Requirement.
+** 
+**     You must preserve all legal notices or author attributions in the covered
+**     work or Appropriate Legal Notices displayed by works containing the covered
+**     work.  You may not remove from the covered work any author or developer
+**     credit already included within the covered work.
+** 
+**   1. No License To Use Trademarks.
+** 
+**     This license does not grant any license or rights to use the trademarks
+**     Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
+**     of Golden Code Development Corporation. You are not authorized to use the
+**     name Golden Code, FWD, or the names of any author or contributor, for
+**     publicity purposes without written authorization.
+** 
+**   2. No Misrepresentation of Affiliation.
+** 
+**     You may not represent yourself as Golden Code Development Corporation or FWD.
+** 
+**     You may not represent yourself for publicity purposes as associated with
+**     Golden Code Development Corporation, FWD, or any author or contributor to
+**     the covered work, without written authorization.
+** 
+**   3. No Misrepresentation of Source or Origin.
+** 
+**     You may not represent the covered work as solely your work.  All modified
+**     versions of the covered work must be marked in a reasonable way to make it
+**     clear that the modified work is not originating from Golden Code Development
+**     Corporation or FWD.  All modified versions must contain the notices of
+**     attribution required in this license.
+*/
+package com.goldencode.p2j.testengine;
+
+import java.lang.reflect.*;
+
+import org.junit.platform.commons.support.*;
+
+import com.goldencode.p2j.net.*;
+import com.goldencode.p2j.util.*;
+
+/**
+ * Static methods to support legacy class methods and legacy procedure execution.
+ */
+public class TestExecutionSupport
+{
+   /**
+    * Call a legacy internal procedure.
+    *
+    * @param method
+    *        the Java method converted from the legacy internal procedure
+    * @param h
+    *        the persistent procedure handle
+    */
+   final static void callProcedureMethod(final Method method, final handle h)
+   {
+      final LegacySignature annotation = method.getAnnotation(LegacySignature.class);
+      final String name = annotation.name();
+      final InvokeConfig invokeConfig = new InvokeConfig().setTarget(name);
+      invokeConfig.clone().setInHandle(h).run();
+   }
+
+   /**
+    * Call a legacy class method.
+    *
+    * @param method
+    *        the Java method converted from the legacy class method
+    * @param o
+    *        the legacy class instance or {@code null} for a static method
+    */
+   final static void callClassMethod(final Method method, final object<?> o)
+   {
+      startup:
+      {
+         TransactionManager.blockSetup();
+
+         do
+         {
+            try
+            {
+               ReflectionSupport.invokeMethod(method, o.ref());
+            }
+            catch (StopConditionException sce)
+            {
+               TransactionManager.disableLoopProtection(null);
+               TransactionManager.triggerRetry(null);
+            }
+            catch (ConditionException ce)
+            {
+               if (ce instanceof QuitConditionException)
+               {
+                  TransactionManager.setProcessingQuit(true);
+               }
+               break startup;
+            }
+            catch (RetryUnwindException ru)
+            {
+               TransactionManager.honorRetry(ru);
+            }
+            catch (Throwable th)
+            {
+               Throwable chained = th;
+
+               boolean isError = false;
+               @SuppressWarnings("unused")
+               boolean isStop = false;
+
+               // look through all causes to determine if there was an
+               // underlying error
+               while (chained != null)
+               {
+                  if (chained instanceof Error)
+                  {
+                     isError = true;
+                  }
+
+                  if (chained instanceof StopConditionException)
+                  {
+                     isStop = true;
+                  }
+
+                  if (chained instanceof SilentUnwindException)
+                  {
+                     isStop = true;
+                  }
+
+                  if (chained instanceof UnstoppableExitException)
+                  {
+                     throw new UnstoppableExitException(chained);
+                  }
+
+                  chained = chained.getCause();
+               }
+
+               // treat Exception (but not Error) types the same as if
+               // we got a STOP
+               if (!isError)
+               {
+
+                  TransactionManager.disableLoopProtection(null);
+                  TransactionManager.triggerRetry(null);
+               }
+               else
+               {
+                  // the abend was an instance of Error - this is so
+                  // severe that we can't let the user continue 
+
+                  // set the flag to allow direct exit since we have such
+                  // a severe problem that we are going to exit to the
+                  // client BUT we MUST do this without trying to access
+                  // the client any further (via LogicalTerminal's
+                  // pauseBeforeEnd())
+                  TransactionManager.setProcessingQuit(true);
+
+                  // re-throw to ensure that we exit
+                  throw new RuntimeException(th);
+               }
+            }
+
+         }
+         while (TransactionManager.needsRetry());
+      }
+   }
+}

=== added file 'src/com/goldencode/p2j/testengine/UnitTestEngine.java'
--- src/com/goldencode/p2j/testengine/UnitTestEngine.java	1970-01-01 00:00:00 +0000
+++ src/com/goldencode/p2j/testengine/UnitTestEngine.java	2022-10-12 21:25:46 +0000
@@ -0,0 +1,152 @@
+/*
+** Module   : UnitTestEngine.java
+** Abstract : FWD unit test remote protocol
+**
+** Copyright (c) 2022, Golden Code Development Corporation.
+**
+** -#- -I- --Date-- ----------------Description----------------------
+** 001 VVT 2022xxxx Created initial version.
+*/
+/*
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU Affero General Public License as
+** published by the Free Software Foundation, either version 3 of the
+** License, or (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU Affero General Public License for more details.
+**
+** You may find a copy of the GNU Affero GPL version 3 at the following
+** location: https://www.gnu.org/licenses/agpl-3.0.en.html
+** 
+** Additional terms under GNU Affero GPL version 3 section 7:
+** 
+**   Under Section 7 of the GNU Affero GPL version 3, the following additional
+**   terms apply to the works covered under the License.  These additional terms
+**   are non-permissive additional terms allowed under Section 7 of the GNU
+**   Affero GPL version 3 and may not be removed by you.
+** 
+**   0. Attribution Requirement.
+** 
+**     You must preserve all legal notices or author attributions in the covered
+**     work or Appropriate Legal Notices displayed by works containing the covered
+**     work.  You may not remove from the covered work any author or developer
+**     credit already included within the covered work.
+** 
+**   1. No License To Use Trademarks.
+** 
+**     This license does not grant any license or rights to use the trademarks
+**     Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
+**     of Golden Code Development Corporation. You are not authorized to use the
+**     name Golden Code, FWD, or the names of any author or contributor, for
+**     publicity purposes without written authorization.
+** 
+**   2. No Misrepresentation of Affiliation.
+** 
+**     You may not represent yourself as Golden Code Development Corporation or FWD.
+** 
+**     You may not represent yourself for publicity purposes as associated with
+**     Golden Code Development Corporation, FWD, or any author or contributor to
+**     the covered work, without written authorization.
+** 
+**   3. No Misrepresentation of Source or Origin.
+** 
+**     You may not represent the covered work as solely your work.  All modified
+**     versions of the covered work must be marked in a reasonable way to make it
+**     clear that the modified work is not originating from Golden Code Development
+**     Corporation or FWD.  All modified versions must contain the notices of
+**     attribution required in this license.
+*/
+package com.goldencode.p2j.testengine;
+
+import org.junit.platform.engine.*;
+import org.junit.platform.engine.support.hierarchical.*;
+
+/**
+ * Interface to support FWD unit testing engine remote operations. See {@link UnitTestServer}
+ * for the implementation.
+ */
+public interface UnitTestEngine
+{
+   /**
+    * Execute the <em>after</em> behavior of this node.
+    *
+    * <p>This method will be called once <em>after</em> {@linkplain #execute
+    * execution} of this node.
+    * 
+    * @param testId
+    *        test descriptor ID
+    *
+    * @throws Exception 
+    * 
+    */
+   public void after(final UniqueId testId) throws Exception;
+
+   /**
+    * Execute the <em>before</em> behavior of this node.
+    *
+    * <p>This method will be called once <em>before</em> {@linkplain #execute
+    * execution} of this node.
+    * 
+    * @param testId 
+    *        test descriptor ID
+    *
+    * @throws Exception 
+    */
+   public void before(final UniqueId testId) throws Exception;
+
+   /**
+    * Clean up the supplied {@code context} after execution.
+    * 
+    * Reset the test class instance.
+    * @param testId 
+    *        test descriptor ID
+    *
+    * @throws Exception 
+    *
+    * @see #prepare
+    */
+   public void cleanUp(final UniqueId testId) throws Exception;
+
+   /**
+    * @param uniqueId 
+    * @param packageSelectors 
+    * @param classSelectors 
+    * @param methodSelectors 
+    * 
+    * @return the test descriptor discovered or {@code null} if no match was found.
+    *  
+    */
+   public TestDescriptor discover(final UniqueId uniqueId, final String[] packageSelectors,
+            final String[] classSelectors, final String[] methodSelectors);
+
+   /**
+    * Execute the <em>behavior</em> of this node.
+    *
+    * <p>Containers typically do not implement this method since the
+    * {@link HierarchicalTestEngine} handles execution of their children.
+    * 
+    * @param testId
+    *        test descriptor ID
+    * 
+    * @throws Exception 
+    *
+    */
+   public void execute(final UniqueId testId) throws Exception;
+
+   /**
+    * Do server-side initialization
+    */
+   public void initialize();
+
+   /**
+    * Prepare test
+    * 
+    * @param testId 
+    *        test descriptor ID
+    */
+   public void prepare(final UniqueId testId);
+
+}

=== added file 'src/com/goldencode/p2j/testengine/UnitTestServer.java'
--- src/com/goldencode/p2j/testengine/UnitTestServer.java	1970-01-01 00:00:00 +0000
+++ src/com/goldencode/p2j/testengine/UnitTestServer.java	2022-10-28 20:58:09 +0000
@@ -0,0 +1,459 @@
+/*
+** Module   : UnitTestServer.java
+** Abstract : FWD unit test remote protocol implementation
+**
+** Copyright (c) 2022, Golden Code Development Corporation.
+**
+** -#- -I- --Date-- ----------------Description----------------------
+** 001 VVT 2022xxxx Created initial version.
+*/
+/*
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU Affero General Public License as
+** published by the Free Software Foundation, either version 3 of the
+** License, or (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU Affero General Public License for more details.
+**
+** You may find a copy of the GNU Affero GPL version 3 at the following
+** location: https://www.gnu.org/licenses/agpl-3.0.en.html
+** 
+** Additional terms under GNU Affero GPL version 3 section 7:
+** 
+**   Under Section 7 of the GNU Affero GPL version 3, the following additional
+**   terms apply to the works covered under the License.  These additional terms
+**   are non-permissive additional terms allowed under Section 7 of the GNU
+**   Affero GPL version 3 and may not be removed by you.
+** 
+**   0. Attribution Requirement.
+** 
+**     You must preserve all legal notices or author attributions in the covered
+**     work or Appropriate Legal Notices displayed by works containing the covered
+**     work.  You may not remove from the covered work any author or developer
+**     credit already included within the covered work.
+** 
+**   1. No License To Use Trademarks.
+** 
+**     This license does not grant any license or rights to use the trademarks
+**     Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
+**     of Golden Code Development Corporation. You are not authorized to use the
+**     name Golden Code, FWD, or the names of any author or contributor, for
+**     publicity purposes without written authorization.
+** 
+**   2. No Misrepresentation of Affiliation.
+** 
+**     You may not represent yourself as Golden Code Development Corporation or FWD.
+** 
+**     You may not represent yourself for publicity purposes as associated with
+**     Golden Code Development Corporation, FWD, or any author or contributor to
+**     the covered work, without written authorization.
+** 
+**   3. No Misrepresentation of Source or Origin.
+** 
+**     You may not represent the covered work as solely your work.  All modified
+**     versions of the covered work must be marked in a reasonable way to make it
+**     clear that the modified work is not originating from Golden Code Development
+**     Corporation or FWD.  All modified versions must contain the notices of
+**     attribution required in this license.
+*/
+package com.goldencode.p2j.testengine;
+
+import java.lang.annotation.*;
+import java.lang.reflect.*;
+import java.util.*;
+
+import org.junit.platform.commons.support.*;
+import org.junit.platform.commons.util.*;
+import org.junit.platform.engine.*;
+
+import com.goldencode.p2j.oo.lang.*;
+import com.goldencode.p2j.persist.*;
+import com.goldencode.p2j.security.*;
+import com.goldencode.p2j.testengine.api.*;
+import com.goldencode.p2j.util.*;
+import com.google.common.collect.*;
+
+/**
+ * FWD unit testing engine remote operation server-side implementation.
+ * 
+ * This class contains static methods matching the {@link UnitTestEngine} interface.
+ */
+public class UnitTestServer
+{
+   /** Context local work area. */
+   private static final ContextLocal<WorkArea> context = new ContextLocal<WorkArea>()
+   {
+      @Override
+      protected WorkArea initialValue()
+      {
+         return new WorkArea();
+      };
+   };
+
+   /**
+    * Execute the <em>after</em> behavior of this node.
+    *
+    * <p>This method will be called once <em>after</em> {@linkplain #execute
+    * execution} of this node.
+    * 
+    * @param testId
+    *        test descriptor ID
+    *
+    * @throws Exception 
+    * 
+    */
+   public static void after(final UniqueId testId) throws Exception
+   {
+      final AbstractFWDTestDescriptor desc = getDescriptor(testId);
+      assert desc != null;
+      System.err.println("after: " + testId + " desc: " + desc);
+      desc.afterImpl();
+   }
+
+   /**
+    * Execute the <em>before</em> behavior of this node.
+    *
+    * <p>This method will be called once <em>before</em> {@linkplain #execute
+    * execution} of this node.
+    * 
+    * @param testId 
+    *        test descriptor ID
+    *
+    * @throws Exception 
+    */
+   public static void before(final UniqueId testId) throws Exception
+   {
+      final AbstractFWDTestDescriptor desc = getDescriptor(testId);
+      assert desc != null;
+      System.err.println("before: " + testId + " desc: " + desc);
+      desc.beforeImpl();
+   }
+
+   /**
+    * Clean up the supplied {@code context} after execution.
+    * 
+    * Reset the test class instance.
+    * @param testId 
+    *        test descriptor ID
+    *
+    * @throws Exception 
+    *
+    * @see #prepare
+    */
+   public static void cleanUp(final UniqueId testId) throws Exception
+   {
+      final AbstractFWDTestDescriptor desc = getDescriptor(testId);
+      assert desc != null;
+      System.err.println("cleanUp : " + testId + " desc: " + desc);
+      desc.cleanUpImpl();
+   }
+
+   /**
+    * Do test discovery.
+    * 
+    * Currently supports scanning Java packages, Java classes and Java methods for tests
+    * 
+    * @param uniqueId 
+    *        the test engine uniqueId 
+    * @param packageSelectors
+    *        selectors for Java packages to scan
+    * @param classSelectors 
+    *        selectors for Java classes to scan
+    * @param methodSelectors 
+    *        selectors for Java methods to scan
+    * 
+    * @return a test descriptor with all test descriptors discovered as children
+    *  
+    */
+   public static TestDescriptor discover(final UniqueId uniqueId, final String[] packageSelectors,
+            final String[] classSelectors, final String[] methodSelectors)
+   {
+      // Create and register the root engine descriptor
+      final AbstractFWDTestDescriptor engineDescriptor = new FWDEngineDescriptor(uniqueId);
+
+      register(engineDescriptor);
+
+      try
+      {
+         for (final String methodId : methodSelectors)
+         {
+            final int delimeterIdx = methodId.indexOf('#');
+            assert delimeterIdx > 0;
+            final String className = methodId.substring(0, delimeterIdx);
+            final Class<?> clazz = Class.forName(className);
+            final String methodName = methodId.substring(delimeterIdx);
+            final Method method = clazz.getMethod(methodName);
+
+            addOptionally(engineDescriptor, clazz, method);
+         }
+
+         for (final String selector : classSelectors)
+         {
+            scanClass(engineDescriptor, Class.forName(selector));
+         }
+      }
+      catch (ClassNotFoundException | NoSuchMethodException | SecurityException e)
+      {
+         throw new RuntimeException(e);
+      }
+
+      for (final String selector : packageSelectors)
+      {
+         // Note: we need to restrict the search by only the classes immediately located in the package,
+         // excluding all classes in sub-packages
+         final List<Class<?>> classes = ReflectionSupport.findAllClassesInPackage(selector,
+                  (Class<?> c) -> selector.equals(c.getPackage().getName()), name -> true);
+         for (Class<?> javaClass : classes)
+         {
+            scanClass(engineDescriptor, javaClass);
+         }
+      }
+
+      return engineDescriptor;
+   }
+
+   /**
+    * Execute the <em>behavior</em> of this node.
+    *
+    * Locate the test descriptor by its unique ID and calls
+    * {@link AbstractFWDTestDescriptor#executeImpl()}
+    * 
+    * @param testId
+    *        test descriptor ID
+    * 
+    * @throws Exception 
+    *
+    */
+   public static void execute(final UniqueId testId) throws Exception
+   {
+      final AbstractFWDTestDescriptor desc = getDescriptor(testId);
+      assert desc != null;
+      System.err.println("execute: " + testId + " desc: " + desc);
+      desc.executeImpl();
+   }
+
+   /**
+    * Initialize FWD server.
+    */
+   public static void initialize()
+   {
+      // this is an app server and needs its own management
+      if (AppServerManager.startAppServer())
+      {
+         throw new RuntimeException("Cannot start AppServer");
+      }
+
+      //      if (params.batchMode > 0)
+      //      {
+      //         // run this connection in batch mode (as requested from client command-line)
+      //         EnvironmentOps.setBatchMode(true);
+      //         // LogicalTerminal.activateBatchMode(true); -- should already be set ?
+      //      }
+
+      DatabaseManager.autoConnect();
+   }
+
+   /**
+    * Prepare test descriptor.
+    * 
+    * Locate the test descriptor by its unique ID and calls
+    * {@link AbstractFWDTestDescriptor#prepareImpl()}
+    * 
+    * @param testId 
+    *        test descriptor ID
+    *        
+    * @throws Exception 
+    */
+   public static void prepare(final UniqueId testId) throws Exception
+   {
+      final AbstractFWDTestDescriptor desc = getDescriptor(testId);
+      assert desc != null;
+      System.err.println("prepare : " + testId + " desc: " + desc);
+      desc.prepareImpl();
+   }
+
+   /**
+    * Scan a class for test methods, declared in the class.
+    * 
+    * @param engineDescriptor
+    *        the engine descriptor to register found test descriptors in
+    * @param javaClass
+    *        the Java class to scan
+    * @param explicitMethods
+    *        optional list of explicit test methods to execute, can be {@code null}
+    */
+   private final static void addOptionally(final TestDescriptor engineDescriptor,
+            final Class<?> javaClass, final Method explicitMethods)
+   {
+      final AbstractClassTestDescriptor instance = testAndCreate(javaClass, engineDescriptor,
+               explicitMethods);
+      if (instance != null)
+      {
+         engineDescriptor.addChild(instance);
+      }
+   }
+
+   /**
+    * Collect all methods annotated with the given class, but not annotated as disabled.
+    * 
+    * @param clazz
+    *        the class to scan for methods
+    * @param annoClass
+    *        the annotation class to match
+    * @param topDown
+    *        if {@code true}, then traverse class hierarchy top-down, otherwise traverse bottom-up.
+    * @return
+    *        the list of methods found; never {@code null}
+    */
+   private final static List<Method> collectAnnotatedMethods(final Class<?> clazz,
+            final Class<? extends Annotation> annoClass, final boolean topDown)
+   {
+      return ReflectionUtils.findMethods(clazz,
+               m -> AnnotationUtils.isAnnotated(m, annoClass)
+                        && !AnnotationUtils.isAnnotated(m, Disabled.class),
+               topDown ? ReflectionUtils.HierarchyTraversalMode.TOP_DOWN
+                        : ReflectionUtils.HierarchyTraversalMode.BOTTOM_UP);
+   }
+
+   /**
+    * Find descriptor by its ID.
+    * 
+    * @param testId
+    *        unique ID
+    *        
+    * @return the test descriptor.
+    */
+   private final static AbstractFWDTestDescriptor getDescriptor(final UniqueId testId)
+   {
+      return context.get().testDescriptorRegistry.get(testId);
+   }
+
+   /**
+    * Register a new descriptor.
+    * 
+    * @param descriptor
+    */
+   private final static void register(final AbstractFWDTestDescriptor descriptor)
+   {
+      final Map<UniqueId, AbstractFWDTestDescriptor> registry = context
+               .get().testDescriptorRegistry;
+      final UniqueId uniqueId = descriptor.getUniqueId();
+
+      // The same class can be collected more than once in case test suites are present 
+      if (!registry.containsKey(uniqueId))
+      {
+         registry.put(uniqueId, descriptor);
+      }
+   }
+
+   /**
+    * Recursively scan a class for all test methods, including whose defined in the class
+    * and referenced classes, if the class is marked as test suite.
+    * 
+    * @param engineDescriptor
+    *        the engine descriptor to register found test descriptors in
+    * @param javaClass
+    *        the Java class to scan
+    */
+   private final static void scanClass(final TestDescriptor engineDescriptor,
+            final Class<?> javaClass)
+   {
+      addOptionally(engineDescriptor, javaClass, null);
+      // Add suites
+      if (AnnotationSupport.isAnnotated(javaClass, Suite.class))
+      {
+         final SelectClasses classesAnnotation = javaClass.getAnnotation(SelectClasses.class);
+         if (classesAnnotation != null)
+         {
+            for (final Class<?> referencedClass : classesAnnotation.value())
+            {
+               scanClass(engineDescriptor, referencedClass);
+            }
+         }
+
+         final SelectProcedures proceduresAnnotation = javaClass
+                  .getAnnotation(SelectProcedures.class);
+         if (proceduresAnnotation != null)
+         {
+            for (final String procedureName : proceduresAnnotation.value())
+            {
+               try
+               {
+                  final String className = SourceNameMapper.convertNameToClass(procedureName);
+                  if (className == null)
+                  {
+                     throw new RuntimeException("Cannot resolve class for procedure: " + procedureName);
+                  }
+                  
+                  addOptionally(engineDescriptor, Class.forName(className), null);
+               }
+               catch (ClassNotFoundException e)
+               {
+                  throw new RuntimeException("Cannot find class: " + e);
+               }
+            }
+         }
+      }
+   }
+
+   /**
+    * Test a Java class candidate for existent tests methods. If found, create a new class
+    * test descriptor.
+    * 
+    * @param clazz
+    *        the class under test 
+    * @param parentDescriptor
+    *        the parent descriptor
+    * @param optionalMethod
+    *        the explicit method or {@code null} to scan the class for all test methods
+    *        
+    * @return  the class test descriptor or {@code null} if this class has no test methods.
+    */
+   private final static AbstractClassTestDescriptor testAndCreate(final Class<?> clazz,
+            final TestDescriptor parentDescriptor, Method optionalMethod)
+   {
+      // FIXME: find out if the test for abstract classes is really necessary.
+      // For example, Eclipse does not run abstract classes as JUnit test
+      if (Modifier.isAbstract(clazz.getModifiers()))
+      {
+         return null;
+      }
+
+      final List<Method> test = optionalMethod != null ? Lists.newArrayList(optionalMethod)
+               : collectAnnotatedMethods(clazz, Test.class, true);
+
+      if (test.isEmpty())
+      {
+         return null;
+      }
+
+      final List<Method> beforeEach = collectAnnotatedMethods(clazz, BeforeEach.class, true);
+      final List<Method> afterEach = collectAnnotatedMethods(clazz, AfterEach.class, false);
+      final List<Method> beforeAll = collectAnnotatedMethods(clazz, BeforeAll.class, true);
+      final List<Method> afterAll = collectAnnotatedMethods(clazz, AfterAll.class, false);
+      final AbstractClassTestDescriptor classDescriptor = BaseObject.class.isAssignableFrom(clazz)
+               ? new FWDClassTestDescriptor(clazz, parentDescriptor, test, beforeEach, afterEach,
+                        beforeAll, afterAll)
+               : new FWDProcedureTestDescriptor(clazz, parentDescriptor, test, beforeEach,
+                        afterEach, beforeAll, afterAll);
+      register(classDescriptor);
+
+      for (TestDescriptor child : classDescriptor.getChildren())
+      {
+         register((AbstractFWDTestDescriptor) child);
+      }
+
+      return classDescriptor;
+   }
+
+   /**
+    * Context local work area.
+    */
+   private static class WorkArea
+   {
+      private final Map<UniqueId, AbstractFWDTestDescriptor> testDescriptorRegistry = new HashMap<>();
+   }
+}

=== added directory 'src/com/goldencode/p2j/testengine/api'
=== added file 'src/com/goldencode/p2j/testengine/api/AfterAll.java'
--- src/com/goldencode/p2j/testengine/api/AfterAll.java	1970-01-01 00:00:00 +0000
+++ src/com/goldencode/p2j/testengine/api/AfterAll.java	2022-09-30 12:24:40 +0000
@@ -0,0 +1,74 @@
+/*
+** Module   : AfterAll.java
+** Abstract : Mark methods, which should be run after all @Test methods in the class.
+**
+** Copyright (c) 2022, Golden Code Development Corporation.
+**
+** -#- -I- --Date-- ----------------Description----------------------
+** 001 VVT 2022xxxx Created initial version.
+*/
+/*
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU Affero General Public License as
+** published by the Free Software Foundation, either version 3 of the
+** License, or (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU Affero General Public License for more details.
+**
+** You may find a copy of the GNU Affero GPL version 3 at the following
+** location: https://www.gnu.org/licenses/agpl-3.0.en.html
+** 
+** Additional terms under GNU Affero GPL version 3 section 7:
+** 
+**   Under Section 7 of the GNU Affero GPL version 3, the following additional
+**   terms apply to the works covered under the License.  These additional terms
+**   are non-permissive additional terms allowed under Section 7 of the GNU
+**   Affero GPL version 3 and may not be removed by you.
+** 
+**   0. Attribution Requirement.
+** 
+**     You must preserve all legal notices or author attributions in the covered
+**     work or Appropriate Legal Notices displayed by works containing the covered
+**     work.  You may not remove from the covered work any author or developer
+**     credit already included within the covered work.
+** 
+**   1. No License To Use Trademarks.
+** 
+**     This license does not grant any license or rights to use the trademarks
+**     Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
+**     of Golden Code Development Corporation. You are not authorized to use the
+**     name Golden Code, FWD, or the names of any author or contributor, for
+**     publicity purposes without written authorization.
+** 
+**   2. No Misrepresentation of Affiliation.
+** 
+**     You may not represent yourself as Golden Code Development Corporation or FWD.
+** 
+**     You may not represent yourself for publicity purposes as associated with
+**     Golden Code Development Corporation, FWD, or any author or contributor to
+**     the covered work, without written authorization.
+** 
+**   3. No Misrepresentation of Source or Origin.
+** 
+**     You may not represent the covered work as solely your work.  All modified
+**     versions of the covered work must be marked in a reasonable way to make it
+**     clear that the modified work is not originating from Golden Code Development
+**     Corporation or FWD.  All modified versions must contain the notices of
+**     attribution required in this license.
+*/
+package com.goldencode.p2j.testengine.api;
+
+import java.lang.annotation.*;
+
+/**
+ * {@link AfterAll} is used to mark methods, which should be run after all {@link Test} methods in the class.
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface AfterAll
+{
+   // no-op
+}

=== added file 'src/com/goldencode/p2j/testengine/api/AfterEach.java'
--- src/com/goldencode/p2j/testengine/api/AfterEach.java	1970-01-01 00:00:00 +0000
+++ src/com/goldencode/p2j/testengine/api/AfterEach.java	2022-09-30 12:24:46 +0000
@@ -0,0 +1,74 @@
+/*
+** Module   : AfterEach.java
+** Abstract : Mark methods, which should be run after each @Test method.
+**
+** Copyright (c) 2022, Golden Code Development Corporation.
+**
+** -#- -I- --Date-- ----------------Description----------------------
+** 001 VVT 2022xxxx Created initial version.
+*/
+/*
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU Affero General Public License as
+** published by the Free Software Foundation, either version 3 of the
+** License, or (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU Affero General Public License for more details.
+**
+** You may find a copy of the GNU Affero GPL version 3 at the following
+** location: https://www.gnu.org/licenses/agpl-3.0.en.html
+** 
+** Additional terms under GNU Affero GPL version 3 section 7:
+** 
+**   Under Section 7 of the GNU Affero GPL version 3, the following additional
+**   terms apply to the works covered under the License.  These additional terms
+**   are non-permissive additional terms allowed under Section 7 of the GNU
+**   Affero GPL version 3 and may not be removed by you.
+** 
+**   0. Attribution Requirement.
+** 
+**     You must preserve all legal notices or author attributions in the covered
+**     work or Appropriate Legal Notices displayed by works containing the covered
+**     work.  You may not remove from the covered work any author or developer
+**     credit already included within the covered work.
+** 
+**   1. No License To Use Trademarks.
+** 
+**     This license does not grant any license or rights to use the trademarks
+**     Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
+**     of Golden Code Development Corporation. You are not authorized to use the
+**     name Golden Code, FWD, or the names of any author or contributor, for
+**     publicity purposes without written authorization.
+** 
+**   2. No Misrepresentation of Affiliation.
+** 
+**     You may not represent yourself as Golden Code Development Corporation or FWD.
+** 
+**     You may not represent yourself for publicity purposes as associated with
+**     Golden Code Development Corporation, FWD, or any author or contributor to
+**     the covered work, without written authorization.
+** 
+**   3. No Misrepresentation of Source or Origin.
+** 
+**     You may not represent the covered work as solely your work.  All modified
+**     versions of the covered work must be marked in a reasonable way to make it
+**     clear that the modified work is not originating from Golden Code Development
+**     Corporation or FWD.  All modified versions must contain the notices of
+**     attribution required in this license.
+*/
+package com.goldencode.p2j.testengine.api;
+
+import java.lang.annotation.*;
+
+/**
+ * {@link AfterEach} is used to mark methods, which should be run after each {@link Test} method.
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface AfterEach
+{
+   // no-op
+}

=== added file 'src/com/goldencode/p2j/testengine/api/BeforeAll.java'
--- src/com/goldencode/p2j/testengine/api/BeforeAll.java	1970-01-01 00:00:00 +0000
+++ src/com/goldencode/p2j/testengine/api/BeforeAll.java	2022-09-30 12:24:50 +0000
@@ -0,0 +1,75 @@
+/*
+** Module   : BeforeAll.java
+** Abstract : Mark methods, which should be run before all @Test methods in the class.
+**
+** Copyright (c) 2022, Golden Code Development Corporation.
+**
+** -#- -I- --Date-- ----------------Description----------------------
+** 001 VVT 2022xxxx Created initial version.
+*/
+/*
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU Affero General Public License as
+** published by the Free Software Foundation, either version 3 of the
+** License, or (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU Affero General Public License for more details.
+**
+** You may find a copy of the GNU Affero GPL version 3 at the following
+** location: https://www.gnu.org/licenses/agpl-3.0.en.html
+** 
+** Additional terms under GNU Affero GPL version 3 section 7:
+** 
+**   Under Section 7 of the GNU Affero GPL version 3, the following additional
+**   terms apply to the works covered under the License.  These additional terms
+**   are non-permissive additional terms allowed under Section 7 of the GNU
+**   Affero GPL version 3 and may not be removed by you.
+** 
+**   0. Attribution Requirement.
+** 
+**     You must preserve all legal notices or author attributions in the covered
+**     work or Appropriate Legal Notices displayed by works containing the covered
+**     work.  You may not remove from the covered work any author or developer
+**     credit already included within the covered work.
+** 
+**   1. No License To Use Trademarks.
+** 
+**     This license does not grant any license or rights to use the trademarks
+**     Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
+**     of Golden Code Development Corporation. You are not authorized to use the
+**     name Golden Code, FWD, or the names of any author or contributor, for
+**     publicity purposes without written authorization.
+** 
+**   2. No Misrepresentation of Affiliation.
+** 
+**     You may not represent yourself as Golden Code Development Corporation or FWD.
+** 
+**     You may not represent yourself for publicity purposes as associated with
+**     Golden Code Development Corporation, FWD, or any author or contributor to
+**     the covered work, without written authorization.
+** 
+**   3. No Misrepresentation of Source or Origin.
+** 
+**     You may not represent the covered work as solely your work.  All modified
+**     versions of the covered work must be marked in a reasonable way to make it
+**     clear that the modified work is not originating from Golden Code Development
+**     Corporation or FWD.  All modified versions must contain the notices of
+**     attribution required in this license.
+*/
+package com.goldencode.p2j.testengine.api;
+
+import java.lang.annotation.*;
+
+/**
+ * {@link BeforeAll} is used to mark methods, which should be run before all {@link Test} methods
+ * in the class.
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface BeforeAll
+{
+   // no-op
+}

=== added file 'src/com/goldencode/p2j/testengine/api/BeforeEach.java'
--- src/com/goldencode/p2j/testengine/api/BeforeEach.java	1970-01-01 00:00:00 +0000
+++ src/com/goldencode/p2j/testengine/api/BeforeEach.java	2022-09-30 12:25:01 +0000
@@ -0,0 +1,74 @@
+/*
+** Module   : BeforeEach.java
+** Abstract : Mark methods, which should be run before each @Test method.
+**
+** Copyright (c) 2022, Golden Code Development Corporation.
+**
+** -#- -I- --Date-- ----------------Description----------------------
+** 001 VVT 2022xxxx Created initial version.
+*/
+/*
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU Affero General Public License as
+** published by the Free Software Foundation, either version 3 of the
+** License, or (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU Affero General Public License for more details.
+**
+** You may find a copy of the GNU Affero GPL version 3 at the following
+** location: https://www.gnu.org/licenses/agpl-3.0.en.html
+** 
+** Additional terms under GNU Affero GPL version 3 section 7:
+** 
+**   Under Section 7 of the GNU Affero GPL version 3, the following additional
+**   terms apply to the works covered under the License.  These additional terms
+**   are non-permissive additional terms allowed under Section 7 of the GNU
+**   Affero GPL version 3 and may not be removed by you.
+** 
+**   0. Attribution Requirement.
+** 
+**     You must preserve all legal notices or author attributions in the covered
+**     work or Appropriate Legal Notices displayed by works containing the covered
+**     work.  You may not remove from the covered work any author or developer
+**     credit already included within the covered work.
+** 
+**   1. No License To Use Trademarks.
+** 
+**     This license does not grant any license or rights to use the trademarks
+**     Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
+**     of Golden Code Development Corporation. You are not authorized to use the
+**     name Golden Code, FWD, or the names of any author or contributor, for
+**     publicity purposes without written authorization.
+** 
+**   2. No Misrepresentation of Affiliation.
+** 
+**     You may not represent yourself as Golden Code Development Corporation or FWD.
+** 
+**     You may not represent yourself for publicity purposes as associated with
+**     Golden Code Development Corporation, FWD, or any author or contributor to
+**     the covered work, without written authorization.
+** 
+**   3. No Misrepresentation of Source or Origin.
+** 
+**     You may not represent the covered work as solely your work.  All modified
+**     versions of the covered work must be marked in a reasonable way to make it
+**     clear that the modified work is not originating from Golden Code Development
+**     Corporation or FWD.  All modified versions must contain the notices of
+**     attribution required in this license.
+*/
+package com.goldencode.p2j.testengine.api;
+
+import java.lang.annotation.*;
+
+/**
+ * {@link BeforeEach} is used to mark methods, which should be run before each {@link Test} method.
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface BeforeEach
+{
+   // no-op
+}

=== added file 'src/com/goldencode/p2j/testengine/api/Disabled.java'
--- src/com/goldencode/p2j/testengine/api/Disabled.java	1970-01-01 00:00:00 +0000
+++ src/com/goldencode/p2j/testengine/api/Disabled.java	2022-09-30 14:32:06 +0000
@@ -0,0 +1,74 @@
+/*
+** Module   : Disabled.java
+** Abstract : Mark methods, which should not be run as tests.
+**
+** Copyright (c) 2022, Golden Code Development Corporation.
+**
+** -#- -I- --Date-- ----------------Description----------------------
+** 001 VVT 2022xxxx Created initial version.
+*/
+/*
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU Affero General Public License as
+** published by the Free Software Foundation, either version 3 of the
+** License, or (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU Affero General Public License for more details.
+**
+** You may find a copy of the GNU Affero GPL version 3 at the following
+** location: https://www.gnu.org/licenses/agpl-3.0.en.html
+** 
+** Additional terms under GNU Affero GPL version 3 section 7:
+** 
+**   Under Section 7 of the GNU Affero GPL version 3, the following additional
+**   terms apply to the works covered under the License.  These additional terms
+**   are non-permissive additional terms allowed under Section 7 of the GNU
+**   Affero GPL version 3 and may not be removed by you.
+** 
+**   0. Attribution Requirement.
+** 
+**     You must preserve all legal notices or author attributions in the covered
+**     work or Appropriate Legal Notices displayed by works containing the covered
+**     work.  You may not remove from the covered work any author or developer
+**     credit already included within the covered work.
+** 
+**   1. No License To Use Trademarks.
+** 
+**     This license does not grant any license or rights to use the trademarks
+**     Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
+**     of Golden Code Development Corporation. You are not authorized to use the
+**     name Golden Code, FWD, or the names of any author or contributor, for
+**     publicity purposes without written authorization.
+** 
+**   2. No Misrepresentation of Affiliation.
+** 
+**     You may not represent yourself as Golden Code Development Corporation or FWD.
+** 
+**     You may not represent yourself for publicity purposes as associated with
+**     Golden Code Development Corporation, FWD, or any author or contributor to
+**     the covered work, without written authorization.
+** 
+**   3. No Misrepresentation of Source or Origin.
+** 
+**     You may not represent the covered work as solely your work.  All modified
+**     versions of the covered work must be marked in a reasonable way to make it
+**     clear that the modified work is not originating from Golden Code Development
+**     Corporation or FWD.  All modified versions must contain the notices of
+**     attribution required in this license.
+*/
+package com.goldencode.p2j.testengine.api;
+
+import java.lang.annotation.*;
+
+/**
+ * {@link Disabled} is used to mark methods, which should not be run as tests.
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Disabled
+{
+   // no-op
+}

=== added file 'src/com/goldencode/p2j/testengine/api/SelectClasses.java'
--- src/com/goldencode/p2j/testengine/api/SelectClasses.java	1970-01-01 00:00:00 +0000
+++ src/com/goldencode/p2j/testengine/api/SelectClasses.java	2022-09-30 13:14:57 +0000
@@ -0,0 +1,78 @@
+/*
+** Module   : SelectClasses.java
+** Abstract : Provides a list of Java classes for @Suite.
+**
+** Copyright (c) 2022, Golden Code Development Corporation.
+**
+** -#- -I- --Date-- ----------------Description----------------------
+** 001 VVT 2022xxxx Created initial version.
+*/
+/*
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU Affero General Public License as
+** published by the Free Software Foundation, either version 3 of the
+** License, or (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU Affero General Public License for more details.
+**
+** You may find a copy of the GNU Affero GPL version 3 at the following
+** location: https://www.gnu.org/licenses/agpl-3.0.en.html
+** 
+** Additional terms under GNU Affero GPL version 3 section 7:
+** 
+**   Under Section 7 of the GNU Affero GPL version 3, the following additional
+**   terms apply to the works covered under the License.  These additional terms
+**   are non-permissive additional terms allowed under Section 7 of the GNU
+**   Affero GPL version 3 and may not be removed by you.
+** 
+**   0. Attribution Requirement.
+** 
+**     You must preserve all legal notices or author attributions in the covered
+**     work or Appropriate Legal Notices displayed by works containing the covered
+**     work.  You may not remove from the covered work any author or developer
+**     credit already included within the covered work.
+** 
+**   1. No License To Use Trademarks.
+** 
+**     This license does not grant any license or rights to use the trademarks
+**     Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
+**     of Golden Code Development Corporation. You are not authorized to use the
+**     name Golden Code, FWD, or the names of any author or contributor, for
+**     publicity purposes without written authorization.
+** 
+**   2. No Misrepresentation of Affiliation.
+** 
+**     You may not represent yourself as Golden Code Development Corporation or FWD.
+** 
+**     You may not represent yourself for publicity purposes as associated with
+**     Golden Code Development Corporation, FWD, or any author or contributor to
+**     the covered work, without written authorization.
+** 
+**   3. No Misrepresentation of Source or Origin.
+** 
+**     You may not represent the covered work as solely your work.  All modified
+**     versions of the covered work must be marked in a reasonable way to make it
+**     clear that the modified work is not originating from Golden Code Development
+**     Corporation or FWD.  All modified versions must contain the notices of
+**     attribution required in this license.
+*/
+package com.goldencode.p2j.testengine.api;
+
+import java.lang.annotation.*;
+
+/**
+ * {@link SelectClasses} provides a list of Java classes for {@link Suite}.
+ */
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface SelectClasses
+{
+	/**
+	 * Class list.
+	 */
+	Class<?>[] value();
+
+}

=== added file 'src/com/goldencode/p2j/testengine/api/SelectProcedures.java'
--- src/com/goldencode/p2j/testengine/api/SelectProcedures.java	1970-01-01 00:00:00 +0000
+++ src/com/goldencode/p2j/testengine/api/SelectProcedures.java	2022-10-28 14:04:27 +0000
@@ -0,0 +1,78 @@
+/*
+** Module   : SelectProcedures.java
+** Abstract : Provides a list of legacy procedure names for ABLUnit @TestSuite annotation.
+**
+** Copyright (c) 2022, Golden Code Development Corporation.
+**
+** -#- -I- --Date-- ----------------Description----------------------
+** 001 VVT 2022xxxx Created initial version.
+*/
+/*
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU Affero General Public License as
+** published by the Free Software Foundation, either version 3 of the
+** License, or (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU Affero General Public License for more details.
+**
+** You may find a copy of the GNU Affero GPL version 3 at the following
+** location: https://www.gnu.org/licenses/agpl-3.0.en.html
+** 
+** Additional terms under GNU Affero GPL version 3 section 7:
+** 
+**   Under Section 7 of the GNU Affero GPL version 3, the following additional
+**   terms apply to the works covered under the License.  These additional terms
+**   are non-permissive additional terms allowed under Section 7 of the GNU
+**   Affero GPL version 3 and may not be removed by you.
+** 
+**   0. Attribution Requirement.
+** 
+**     You must preserve all legal notices or author attributions in the covered
+**     work or Appropriate Legal Notices displayed by works containing the covered
+**     work.  You may not remove from the covered work any author or developer
+**     credit already included within the covered work.
+** 
+**   1. No License To Use Trademarks.
+** 
+**     This license does not grant any license or rights to use the trademarks
+**     Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
+**     of Golden Code Development Corporation. You are not authorized to use the
+**     name Golden Code, FWD, or the names of any author or contributor, for
+**     publicity purposes without written authorization.
+** 
+**   2. No Misrepresentation of Affiliation.
+** 
+**     You may not represent yourself as Golden Code Development Corporation or FWD.
+** 
+**     You may not represent yourself for publicity purposes as associated with
+**     Golden Code Development Corporation, FWD, or any author or contributor to
+**     the covered work, without written authorization.
+** 
+**   3. No Misrepresentation of Source or Origin.
+** 
+**     You may not represent the covered work as solely your work.  All modified
+**     versions of the covered work must be marked in a reasonable way to make it
+**     clear that the modified work is not originating from Golden Code Development
+**     Corporation or FWD.  All modified versions must contain the notices of
+**     attribution required in this license.
+*/
+package com.goldencode.p2j.testengine.api;
+
+import java.lang.annotation.*;
+
+/**
+ * {@link SelectProcedures} provides list of legacy procedure names for ABLUnit @TestSuite annotation.
+ */
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface SelectProcedures
+{
+	/**
+	 * Array of legacy procedure names.
+	 */
+	String[] value();
+
+}

=== added file 'src/com/goldencode/p2j/testengine/api/Suite.java'
--- src/com/goldencode/p2j/testengine/api/Suite.java	1970-01-01 00:00:00 +0000
+++ src/com/goldencode/p2j/testengine/api/Suite.java	2022-09-30 12:24:07 +0000
@@ -0,0 +1,74 @@
+/*
+** Module   : Suite.java
+** Abstract : Mark classes, which should be used as test suites.
+**
+** Copyright (c) 2022, Golden Code Development Corporation.
+**
+** -#- -I- --Date-- ----------------Description----------------------
+** 001 VVT 2022xxxx Created initial version.
+*/
+/*
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU Affero General Public License as
+** published by the Free Software Foundation, either version 3 of the
+** License, or (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU Affero General Public License for more details.
+**
+** You may find a copy of the GNU Affero GPL version 3 at the following
+** location: https://www.gnu.org/licenses/agpl-3.0.en.html
+** 
+** Additional terms under GNU Affero GPL version 3 section 7:
+** 
+**   Under Section 7 of the GNU Affero GPL version 3, the following additional
+**   terms apply to the works covered under the License.  These additional terms
+**   are non-permissive additional terms allowed under Section 7 of the GNU
+**   Affero GPL version 3 and may not be removed by you.
+** 
+**   0. Attribution Requirement.
+** 
+**     You must preserve all legal notices or author attributions in the covered
+**     work or Appropriate Legal Notices displayed by works containing the covered
+**     work.  You may not remove from the covered work any author or developer
+**     credit already included within the covered work.
+** 
+**   1. No License To Use Trademarks.
+** 
+**     This license does not grant any license or rights to use the trademarks
+**     Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
+**     of Golden Code Development Corporation. You are not authorized to use the
+**     name Golden Code, FWD, or the names of any author or contributor, for
+**     publicity purposes without written authorization.
+** 
+**   2. No Misrepresentation of Affiliation.
+** 
+**     You may not represent yourself as Golden Code Development Corporation or FWD.
+** 
+**     You may not represent yourself for publicity purposes as associated with
+**     Golden Code Development Corporation, FWD, or any author or contributor to
+**     the covered work, without written authorization.
+** 
+**   3. No Misrepresentation of Source or Origin.
+** 
+**     You may not represent the covered work as solely your work.  All modified
+**     versions of the covered work must be marked in a reasonable way to make it
+**     clear that the modified work is not originating from Golden Code Development
+**     Corporation or FWD.  All modified versions must contain the notices of
+**     attribution required in this license.
+*/
+package com.goldencode.p2j.testengine.api;
+
+import java.lang.annotation.*;
+
+/**
+ * {@link Suite} is used to mark methods, which should be used as test suites.
+ */
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Suite
+{
+   // no-op
+}

=== added file 'src/com/goldencode/p2j/testengine/api/Test.java'
--- src/com/goldencode/p2j/testengine/api/Test.java	1970-01-01 00:00:00 +0000
+++ src/com/goldencode/p2j/testengine/api/Test.java	2022-09-30 11:15:12 +0000
@@ -0,0 +1,74 @@
+/*
+** Module   : Test.java
+** Abstract : Mark methods, which should be run as tests.
+**
+** Copyright (c) 2022, Golden Code Development Corporation.
+**
+** -#- -I- --Date-- ----------------Description----------------------
+** 001 VVT 2022xxxx Created initial version.
+*/
+/*
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU Affero General Public License as
+** published by the Free Software Foundation, either version 3 of the
+** License, or (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU Affero General Public License for more details.
+**
+** You may find a copy of the GNU Affero GPL version 3 at the following
+** location: https://www.gnu.org/licenses/agpl-3.0.en.html
+** 
+** Additional terms under GNU Affero GPL version 3 section 7:
+** 
+**   Under Section 7 of the GNU Affero GPL version 3, the following additional
+**   terms apply to the works covered under the License.  These additional terms
+**   are non-permissive additional terms allowed under Section 7 of the GNU
+**   Affero GPL version 3 and may not be removed by you.
+** 
+**   0. Attribution Requirement.
+** 
+**     You must preserve all legal notices or author attributions in the covered
+**     work or Appropriate Legal Notices displayed by works containing the covered
+**     work.  You may not remove from the covered work any author or developer
+**     credit already included within the covered work.
+** 
+**   1. No License To Use Trademarks.
+** 
+**     This license does not grant any license or rights to use the trademarks
+**     Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
+**     of Golden Code Development Corporation. You are not authorized to use the
+**     name Golden Code, FWD, or the names of any author or contributor, for
+**     publicity purposes without written authorization.
+** 
+**   2. No Misrepresentation of Affiliation.
+** 
+**     You may not represent yourself as Golden Code Development Corporation or FWD.
+** 
+**     You may not represent yourself for publicity purposes as associated with
+**     Golden Code Development Corporation, FWD, or any author or contributor to
+**     the covered work, without written authorization.
+** 
+**   3. No Misrepresentation of Source or Origin.
+** 
+**     You may not represent the covered work as solely your work.  All modified
+**     versions of the covered work must be marked in a reasonable way to make it
+**     clear that the modified work is not originating from Golden Code Development
+**     Corporation or FWD.  All modified versions must contain the notices of
+**     attribution required in this license.
+*/
+package com.goldencode.p2j.testengine.api;
+
+import java.lang.annotation.*;
+
+/**
+ * {@link Test} is used to mark methods, which should be run as tests.
+ */
+@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD })
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Test
+{
+   // no-op
+}

=== modified file 'src/com/goldencode/p2j/ui/client/UiUtils.java'
--- src/com/goldencode/p2j/ui/client/UiUtils.java	2022-10-11 14:53:34 +0000
+++ src/com/goldencode/p2j/ui/client/UiUtils.java	2022-10-28 13:27:20 +0000
@@ -111,6 +111,7 @@
 **     IAS 20220803 Fixed loop condition in 'resolveLocalFileResource'.
 **     EVL 20220809 Fixed issue with buildCanonicalUrl() on Windows server installation.
 **     ME  20220928 Added two methods to get widget container and top level window.
+**     VVT 2022xxxx NPE problem resolved in getTopLevelComponents(). See #6237.
 */
 
 /*
@@ -1469,7 +1470,10 @@
     */
    public static Widget<?>[] getTopLevelComponents(TopLevelWindow wnd)
    {
-      return wnd.getContentPane().widgets();
+      final Container contentPane = wnd.getContentPane();
+      return contentPane != null
+             ? contentPane.widgets()
+             : new Widget[0];
    }
 
    /**

=== modified file 'test/com/goldencode/p2j/comauto/TestComObject.java'
--- test/com/goldencode/p2j/comauto/TestComObject.java	2021-11-26 20:14:59 +0000
+++ test/com/goldencode/p2j/comauto/TestComObject.java	2022-09-30 12:02:26 +0000
@@ -2,11 +2,12 @@
 ** Module   : TestComObject.java
 ** Abstract : Testing of OCX call input parameter conversion.
 **
-** Copyright (c) 2021, Golden Code Development Corporation.
+** Copyright (c) 2021-2022, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- ---------------------------------Description---------------------------------
 ** 001 VVT 20211125 Created initial version.
 **     VVT 20211126 A test for conversion decimal -> Object updated.
+**     VVT 2022xxxx Refactored to make tests pure JUnit5. See #6237.
 */
 /*
 ** This program is free software: you can redistribute it and/or modify
@@ -62,12 +63,11 @@
 */
 package com.goldencode.p2j.comauto;
 
-import static org.junit.Assert.*;
-import static org.junit.jupiter.api.Assertions.assertEquals;
 import static com.goldencode.p2j.comauto.ComObject.convertInputParameter;
+import static org.junit.jupiter.api.Assertions.*;
 
 import java.time.*;
-import org.junit.*;
+import org.junit.jupiter.api.*;
 
 import com.goldencode.p2j.util.*;
 

=== modified file 'test/com/goldencode/p2j/testing/AbstractFWDTest.java'
--- test/com/goldencode/p2j/testing/AbstractFWDTest.java	2021-11-23 20:02:38 +0000
+++ test/com/goldencode/p2j/testing/AbstractFWDTest.java	2022-09-30 12:02:37 +0000
@@ -12,6 +12,7 @@
 **                  Class members sorted.
 **     VVT 20210413 Javadoc fixed
 **     VVT 20211123 Missing Javadocs and annotations added.
+**     VVT 2022xxxx Refactored to make tests pure JUnit5. See #6237.
 */
 /*
 ** This program is free software: you can redistribute it and/or modify
@@ -67,9 +68,9 @@
 */
 package com.goldencode.p2j.testing;
 
-import static org.junit.Assert.*;
+import static org.junit.jupiter.api.Assertions.*;
 
-import org.junit.function.*;
+import org.junit.jupiter.api.function.*;
 
 import com.goldencode.p2j.*;
 import com.goldencode.p2j.util.*;
@@ -93,10 +94,9 @@
        *        the argument for the code
        * @throws ErrorConditionException
        */
-      void run(final T arg)
-      throws ErrorConditionException;
+      void run(final T arg) throws ErrorConditionException;
    }
-   
+
    /**
     * A helper method to check that code throws the OE error 14312.
     * 
@@ -106,8 +106,7 @@
     *        the format to pass to the code
     *        
     */
-   public final static void assertMustThrowError14312(final ErrorCode code,
-            final String format)
+   public final static void assertMustThrowError14312(final ErrorCode code, final String format)
    {
       assertMustThrowError(code, 14312, "Too many 'S' characters in format \"%s\"", format,
                format);
@@ -123,8 +122,7 @@
     *        the format to pass to the code
     *        
     */
-   public final static void assertMustThrowError154(final ErrorCode code,
-            final String format)
+   public final static void assertMustThrowError154(final ErrorCode code, final String format)
    {
       assertMustThrowError(code, 154, "Date format %s is incomplete", format, format);
    }
@@ -138,8 +136,7 @@
     *        the format to pass to the code
     *        
     */
-   public final static void assertMustThrowError164(final ErrorCode code,
-            final String format)
+   public final static void assertMustThrowError164(final ErrorCode code, final String format)
    {
       assertMustThrowError(code, 164, "Character format %s is incomplete", format, format);
    }
@@ -155,8 +152,8 @@
     * @param offendingCharIndex
     *        the index of the offending character
     */
-   public final static void assertMustThrowError22(final ErrorCode code,
-            final String format, final int offendingCharIndex)
+   public final static void assertMustThrowError22(final ErrorCode code, final String format,
+            final int offendingCharIndex)
    {
       assertMustThrowError(code, 22, "Character number %d of format %s is invalid", format,
                offendingCharIndex, format);
@@ -174,8 +171,8 @@
     *        the value to format
     *        
     */
-   public final static void assertMustThrowError74(final ErrorCode code,
-            final String format, final String value)
+   public final static void assertMustThrowError74(final ErrorCode code, final String format,
+            final String value)
    {
       assertMustThrowError(code, 74, "Value %s cannot be displayed using %s", format, value,
                format);
@@ -189,8 +186,7 @@
     * @param format
     *        the format to pass to the code
     */
-   protected final static void assertMustThrowError148(final ErrorCode code,
-            final String format)
+   protected final static void assertMustThrowError148(final ErrorCode code, final String format)
    {
       assertMustThrowError(code, 148, "Numeric format %s provides for no digits", format, format);
    }
@@ -261,11 +257,11 @@
     * @param args
     *        other arguments if the message format expects them
     */
-   private final static void assertMustThrowErrorConditional(final ThrowingRunnable code,
+   private final static void assertMustThrowErrorConditional(final Executable code,
             final int errorCodeExpected, final String expectedMessageFormat, Object... args)
    {
-      final ErrorConditionException exc = assertThrows("Must throw",
-               ErrorConditionException.class, code);
+      final ErrorConditionException exc = assertThrows(ErrorConditionException.class, code,
+               "Must throw");
       final Throwable cause = exc.getCause();
       assertTrue(cause instanceof NumberedException);
       NumberedException numbered = (NumberedException) cause;

=== modified file 'test/com/goldencode/p2j/testing/FormatTestSuite.java'
--- test/com/goldencode/p2j/testing/FormatTestSuite.java	2021-04-28 15:57:14 +0000
+++ test/com/goldencode/p2j/testing/FormatTestSuite.java	2022-09-30 12:02:49 +0000
@@ -10,6 +10,7 @@
 **     VVT 20210413 The set of the tests updated.
 **     VVT 20210426 The set of the tests updated after test classes were renamed.
 **     VVT 20210428 The set of the tests updated after test classes were renamed.
+**     VVT 2022xxxx Refactored to make tests pure JUnit5. See #6237.
 */
 /*
 ** This program is free software: you can redistribute it and/or modify
@@ -65,8 +66,7 @@
 */
 package com.goldencode.p2j.testing;
 
-import org.junit.runner.*;
-import org.junit.runners.*;
+import org.junit.platform.suite.api.*;
 
 import com.goldencode.p2j.ui.client.format.*;
 import com.goldencode.p2j.util.*;
@@ -74,22 +74,13 @@
 /**
  * Test suite for parsing 4gl formats.
  */
-@RunWith(Suite.class)
-@Suite.SuiteClasses({
-   TestLogicalSplitformat.class,
-   TestCharacterToStringFormatted.class,
-   TestCharacterFormatParser.class,
-   TestCharacterFormatter.class,
-   TestCharacterPreprocessFormatString.class,
-   TestCharacterValueOf.class,
-   TestDateFormatting.class,
-   TestDateFormatParse.class,
-   TestDatetimeFormatting.class,
-   TestDatetimeFormatParsing.class,
-   TestNumberTypeParseFormat.class,
-   TestRowid.class,
-   TestStringFormatParse.class
-})
+@Suite
+@SelectClasses(value = { TestLogicalSplitformat.class, TestCharacterToStringFormatted.class,
+         TestCharacterFormatParser.class, TestCharacterFormatter.class,
+         TestCharacterPreprocessFormatString.class, TestCharacterValueOf.class,
+         TestDateFormatting.class, TestDateFormatParse.class, TestDatetimeFormatting.class,
+         TestDatetimeFormatParsing.class, TestNumberTypeParseFormat.class, TestRowid.class,
+         TestStringFormatParse.class })
 public class FormatTestSuite
 {
    // empty

=== modified file 'test/com/goldencode/p2j/ui/client/format/TestDatetimeFormatParsing.java'
--- test/com/goldencode/p2j/ui/client/format/TestDatetimeFormatParsing.java	2021-11-25 18:47:40 +0000
+++ test/com/goldencode/p2j/ui/client/format/TestDatetimeFormatParsing.java	2022-09-30 12:03:04 +0000
@@ -9,6 +9,7 @@
 **     VVT 20210428 Javadocs fixed.
 **     VVT 20211123 Missing Javadocs and annotations added.
 **     VVT 20211125 Typo in javadocs fixed.
+**     VVT 2022xxxx Refactored to make tests pure JUnit5. See #6237.
 */
 /*
 ** This program is free software: you can redistribute it and/or modify
@@ -64,11 +65,11 @@
 */
 package com.goldencode.p2j.ui.client.format;
 
-import static org.junit.Assert.*;
+import static org.junit.jupiter.api.Assertions.*;
 
 import java.lang.reflect.*;
 
-import org.junit.*;
+import org.junit.jupiter.api.*;
 
 import com.goldencode.p2j.util.*;
 

=== modified file 'test/com/goldencode/p2j/ui/client/format/TestStringFormatParse.java'
--- test/com/goldencode/p2j/ui/client/format/TestStringFormatParse.java	2021-04-08 18:56:52 +0000
+++ test/com/goldencode/p2j/ui/client/format/TestStringFormatParse.java	2022-09-30 12:03:15 +0000
@@ -10,6 +10,7 @@
 **     VVT 20210408 Re-usable inherited tests added. Test methods got better names. See #4880.
 **                  Obsolete comments removed for already fixed problems.
 **                  Class members sorted.
+**     VVT 2022xxxx Refactored to make tests pure JUnit5. See #6237.
 */
 /*
 ** This program is free software: you can redistribute it and/or modify
@@ -65,9 +66,9 @@
 */
 package com.goldencode.p2j.ui.client.format;
 
-import static org.junit.Assert.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
 
-import org.junit.*;
+import org.junit.jupiter.api.*;
 
 import com.goldencode.p2j.util.*;
 

=== modified file 'test/com/goldencode/p2j/util/AbstractCharacterFormatTest.java'
--- test/com/goldencode/p2j/util/AbstractCharacterFormatTest.java	2021-04-28 15:57:14 +0000
+++ test/com/goldencode/p2j/util/AbstractCharacterFormatTest.java	2022-09-30 12:03:26 +0000
@@ -8,6 +8,7 @@
 ** 001 VVT 20210408 Created initial version.
 **     VVT 20210426 Test methods sorted.
 **     VVT 20210428 Unused imports removed.
+**     VVT 2022xxxx Refactored to make tests pure JUnit5. See #6237.
 */
 /*
 ** This program is free software: you can redistribute it and/or modify
@@ -63,7 +64,7 @@
 */
 package com.goldencode.p2j.util;
 
-import org.junit.*;
+import org.junit.jupiter.api.*;
 
 /**
  * This class provides common methods and data for all character parsing/formatting tests.

=== modified file 'test/com/goldencode/p2j/util/AbstractDateFormatTest.java'
--- test/com/goldencode/p2j/util/AbstractDateFormatTest.java	2021-09-17 15:14:10 +0000
+++ test/com/goldencode/p2j/util/AbstractDateFormatTest.java	2022-09-30 12:03:35 +0000
@@ -8,6 +8,7 @@
 ** 001 VVT 20210413 Created initial version.
 **     VVT 20210426 Code style fixed.
 **     VVT 20210917 Javadoc fixed. 
+**     VVT 2022xxxx Refactored to make tests pure JUnit5. See #6237.
 */
 /*
 ** This program is free software: you can redistribute it and/or modify
@@ -65,7 +66,7 @@
 
 import java.util.*;
 
-import org.junit.*;
+import org.junit.jupiter.api.*;
 
 import com.goldencode.p2j.ui.client.format.*;
 

=== modified file 'test/com/goldencode/p2j/util/AbstractDatetimeFormatTest.java'
--- test/com/goldencode/p2j/util/AbstractDatetimeFormatTest.java	2021-11-23 20:02:38 +0000
+++ test/com/goldencode/p2j/util/AbstractDatetimeFormatTest.java	2022-09-30 12:03:43 +0000
@@ -9,6 +9,7 @@
 **     VVT 20210427 This class moved to more appropriate package.
 **     VVT 20210428 Unused makeDatatimeFormat() method removed.
 **     VVT 20211123 Missing Javadocs and annotations added.
+**     VVT 2022xxxx Refactored to make tests pure JUnit5. See #6237.
 */
 /*
 ** This program is free software: you can redistribute it and/or modify
@@ -64,7 +65,7 @@
 */
 package com.goldencode.p2j.util;
 
-import org.junit.*;
+import org.junit.jupiter.api.*;
 
 /**
  * Datetime-related tests helper class

=== modified file 'test/com/goldencode/p2j/util/AbstractLogicalFormatParseTest.java'
--- test/com/goldencode/p2j/util/AbstractLogicalFormatParseTest.java	2021-04-28 15:57:14 +0000
+++ test/com/goldencode/p2j/util/AbstractLogicalFormatParseTest.java	2022-09-30 12:03:55 +0000
@@ -6,6 +6,7 @@
 **
 ** -#- -I- --Date-- ---------------------------------Description---------------------------------
 ** 001 VVT 20210428 Created initial version.
+**     VVT 2022xxxx Refactored to make tests pure JUnit5. See #6237.
 */
 /*
 ** This program is free software: you can redistribute it and/or modify
@@ -61,7 +62,7 @@
 */
 package com.goldencode.p2j.util;
 
-import org.junit.*;
+import org.junit.jupiter.api.*;
 
 /**
  * Common methods and data for all OE logical values parsing/formatting tests.

=== modified file 'test/com/goldencode/p2j/util/AbstractNumberFormatParseTest.java'
--- test/com/goldencode/p2j/util/AbstractNumberFormatParseTest.java	2021-11-23 20:02:38 +0000
+++ test/com/goldencode/p2j/util/AbstractNumberFormatParseTest.java	2022-09-30 12:04:06 +0000
@@ -12,6 +12,7 @@
 **                  Methods were given mode matching names.
 **     VVT 20210428 Imports statements fixed.
 **     VVT 20211123 Missing Javadocs and annotations added.
+**     VVT 2022xxxx Refactored to make tests pure JUnit5. See #6237.
 */
 /*
 ** This program is free software: you can redistribute it and/or modify
@@ -69,7 +70,7 @@
 
 import java.math.*;
 
-import org.junit.*;
+import org.junit.jupiter.api.*;
 
 /**
  * Common fields and methods for testing 4gl number formats.

=== modified file 'test/com/goldencode/p2j/util/AbstractNumberFormatterTest.java'
--- test/com/goldencode/p2j/util/AbstractNumberFormatterTest.java	2021-11-23 20:02:38 +0000
+++ test/com/goldencode/p2j/util/AbstractNumberFormatterTest.java	2022-09-30 12:04:15 +0000
@@ -9,6 +9,7 @@
 **     VVT 20210427 checkInvalidValue() API changed; a new test leftOverflowError() added.
 **     VVT 20210428 Import statements fixed.
 **     VVT 20211123 Missing Javadocs and annotations added.
+**     VVT 2022xxxx Refactored to make tests pure JUnit5. See #6237.
 */
 /*
 ** This program is free software: you can redistribute it and/or modify
@@ -64,11 +65,11 @@
 */
 package com.goldencode.p2j.util;
 
-import static org.junit.Assert.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
 
 import java.math.*;
 
-import org.junit.*;
+import org.junit.jupiter.api.*;
 
 /**
  * Common fields and methods for testing formatting with 4gl number formats.

=== modified file 'test/com/goldencode/p2j/util/TestCharacterFormatParser.java'
--- test/com/goldencode/p2j/util/TestCharacterFormatParser.java	2021-09-17 15:14:10 +0000
+++ test/com/goldencode/p2j/util/TestCharacterFormatParser.java	2022-09-30 12:05:04 +0000
@@ -9,6 +9,7 @@
 **     VVT 20210426 The splitGroup() method fixed.
 **     VVT 20210427 API changed: the format is not passed to constructor anymore.
 **     VVT 20210917 Javadoc fixed. 
+**     VVT 2022xxxx Refactored to make tests pure JUnit5. See #6237.
 */
 /*
 ** This program is free software: you can redistribute it and/or modify
@@ -66,7 +67,7 @@
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 
-import org.junit.*;
+import org.junit.jupiter.api.*;
 
 /**
  * Tests for {@link CharacterFormatParser#parse(String)}

=== modified file 'test/com/goldencode/p2j/util/TestCharacterPreprocessFormatString.java'
--- test/com/goldencode/p2j/util/TestCharacterPreprocessFormatString.java	2021-11-23 20:02:38 +0000
+++ test/com/goldencode/p2j/util/TestCharacterPreprocessFormatString.java	2022-09-30 12:05:13 +0000
@@ -12,6 +12,7 @@
 **                  Class members sorted.
 **     VVT 20210426 The broken() method removed.
 **     VVT 20211123 Missing Javadocs and annotations added.
+**     VVT 2022xxxx Refactored to make tests pure JUnit5. See #6237.
 */
 /*
 ** This program is free software: you can redistribute it and/or modify
@@ -67,10 +68,9 @@
 */
 package com.goldencode.p2j.util;
 
-import static org.junit.Assert.assertNotNull;
-import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.*;
 
-import org.junit.*;
+import org.junit.jupiter.api.*;
 
 /**
  * Tests for {@link character#preprocessFormatString(String)}.

=== modified file 'test/com/goldencode/p2j/util/TestCharacterToStringFormatted.java'
--- test/com/goldencode/p2j/util/TestCharacterToStringFormatted.java	2021-04-28 15:57:14 +0000
+++ test/com/goldencode/p2j/util/TestCharacterToStringFormatted.java	2022-09-30 12:05:21 +0000
@@ -13,6 +13,7 @@
 **                  Class members sorted.
 **     VVT 20210426 The generic testFormat() is used instead of plain asserts.
 **     VVT 20210428 Import statements fixed.
+**     VVT 2022xxxx Refactored to make tests pure JUnit5. See #6237.
 */
 /*
 ** This program is free software: you can redistribute it and/or modify
@@ -68,9 +69,9 @@
 */
 package com.goldencode.p2j.util;
 
-import static org.junit.Assert.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
 
-import org.junit.*;
+import org.junit.jupiter.api.*;
 
 /**
  * Tests for {@link character#toString(String)}.

=== modified file 'test/com/goldencode/p2j/util/TestCharacterValueOf.java'
--- test/com/goldencode/p2j/util/TestCharacterValueOf.java	2021-11-23 20:02:38 +0000
+++ test/com/goldencode/p2j/util/TestCharacterValueOf.java	2022-09-30 12:05:28 +0000
@@ -12,6 +12,7 @@
 **                  Class members sorted.
 **     VVT 20210426 The tests now use inheritance.
 **     VVT 20211123 Missing Javadocs and annotations added.
+**     VVT 2022xxxx Refactored to make tests pure JUnit5. See #6237.
 */
 /*
 ** This program is free software: you can redistribute it and/or modify
@@ -67,9 +68,9 @@
 */
 package com.goldencode.p2j.util;
 
-import static org.junit.Assert.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
-import org.junit.*;
+import org.junit.jupiter.api.*;
 
 /**
  * Tests for {@link character#valueOf(String)}.

=== modified file 'test/com/goldencode/p2j/util/TestDateFormatParse.java'
--- test/com/goldencode/p2j/util/TestDateFormatParse.java	2021-11-23 20:02:38 +0000
+++ test/com/goldencode/p2j/util/TestDateFormatParse.java	2022-09-30 12:05:39 +0000
@@ -13,6 +13,7 @@
 **     VVT 20210408 Test methods got better names. See #4880.
 **     VVT 20210413 Test methods inheritance added.
 **     VVT 20211123 Missing Javadocs and annotations added.
+**     VVT 2022xxxx Refactored to make tests pure JUnit5. See #6237.
 */
 /*
 ** This program is free software: you can redistribute it and/or modify
@@ -68,9 +69,9 @@
 */
 package com.goldencode.p2j.util;
 
-import static org.junit.Assert.*;
+import static org.junit.jupiter.api.Assertions.*;
 
-import org.junit.*;
+import org.junit.jupiter.api.*;
 
 /**
  * Tests for parsing date format with {@link ParsedDateFormat#getInstance(String)}.
@@ -138,7 +139,7 @@
    /**
     * This method is called once by the test engine before all test methods in this class.
     */
-   @BeforeClass
+   @BeforeAll
    public static void beforeClass()
    {
       date.setDateOrder(AbstractDateFormatTest.ORDER_DMY);

=== modified file 'test/com/goldencode/p2j/util/TestDateFormatter.java'
--- test/com/goldencode/p2j/util/TestDateFormatter.java	2021-04-26 21:13:50 +0000
+++ test/com/goldencode/p2j/util/TestDateFormatter.java	2022-09-30 12:05:47 +0000
@@ -7,6 +7,7 @@
 ** -#- -I- --Date-- ---------------------------------Description---------------------------------
 ** 001 VVT 20210413 Created initial version.
 **     VVT 20210426 Updated for changed DateFormatter API. Missing Javadocs added.
+**     VVT 2022xxxx Refactored to make tests pure JUnit5. See #6237.
 */
 /*
 ** This program is free software: you can redistribute it and/or modify
@@ -62,9 +63,9 @@
 */
 package com.goldencode.p2j.util;
 
-import static org.junit.Assert.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
 
-import org.junit.*;
+import org.junit.jupiter.api.*;
 
 /**
  * Tests for formatting dates.

=== modified file 'test/com/goldencode/p2j/util/TestDateFormatting.java'
--- test/com/goldencode/p2j/util/TestDateFormatting.java	2021-11-23 20:02:38 +0000
+++ test/com/goldencode/p2j/util/TestDateFormatting.java	2022-09-30 12:05:54 +0000
@@ -8,6 +8,7 @@
 ** 001 VVT 20210413 Created initial version.
 **     VVT 20210426 Javadocs fixed.
 **     VVT 20211123 Missing Javadocs and annotations added.
+**     VVT 2022xxxx Refactored to make tests pure JUnit5. See #6237.
 */
 /*
 ** This program is free software: you can redistribute it and/or modify
@@ -63,9 +64,9 @@
 */
 package com.goldencode.p2j.util;
 
-import static org.junit.Assert.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
 
-import org.junit.*;
+import org.junit.jupiter.api.*;
 
 /**
  * Tests for formatting dates with {@link date#toString(String)}.
@@ -87,7 +88,7 @@
    /**
     * This method is called once by the test engine before all test methods in this class.
     */
-   @BeforeClass
+   @BeforeAll
    public static void beforeClass()
    {
       date.setDateOrder(AbstractDateFormatTest.ORDER_DMY);

=== modified file 'test/com/goldencode/p2j/util/TestDatetimeFormatting.java'
--- test/com/goldencode/p2j/util/TestDatetimeFormatting.java	2021-11-23 20:02:38 +0000
+++ test/com/goldencode/p2j/util/TestDatetimeFormatting.java	2022-09-30 12:06:06 +0000
@@ -8,6 +8,7 @@
 ** 001 VVT 20210413 Created initial version.
 **     VVT 20210428 Class hierarchy changed.
 **     VVT 20211123 Missing Javadocs and annotations added.
+**     VVT 2022xxxx Refactored to make tests pure JUnit5. See #6237.
 */
 /*
 ** This program is free software: you can redistribute it and/or modify
@@ -63,9 +64,9 @@
 */
 package com.goldencode.p2j.util;
 
-import static org.junit.Assert.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
 
-import org.junit.*;
+import org.junit.jupiter.api.*;
 
 /**
  * Tests for formatting with {@link datetime#toString(String)}
@@ -146,7 +147,7 @@
    /**
     * This method is called once by the test engine before all test methods in this class.
     */
-   @BeforeClass
+   @BeforeAll
    public static void beforeClass()
    {
       date.setDateOrder(AbstractDateFormatTest.ORDER_DMY);

=== modified file 'test/com/goldencode/p2j/util/TestLogicalSplitformat.java'
--- test/com/goldencode/p2j/util/TestLogicalSplitformat.java	2021-11-23 20:02:38 +0000
+++ test/com/goldencode/p2j/util/TestLogicalSplitformat.java	2022-09-30 12:06:13 +0000
@@ -13,6 +13,7 @@
 **     VVT 20210426 The class made generic. The test now use inheritance.
 **     VVT 20210428 Class renamed, was: TestBoolFormat. Class hierarchy changed.
 **     VVT 20211123 Missing Javadocs and annotations added.
+**     VVT 2022xxxx Refactored to make tests pure JUnit5. See #6237.
 */
 /*
 ** This program is free software: you can redistribute it and/or modify
@@ -69,9 +70,9 @@
 package com.goldencode.p2j.util;
 
 import static com.goldencode.p2j.util.character.valueOf;
-import static org.junit.Assert.*;
+import static org.junit.jupiter.api.Assertions.*;
 
-import org.junit.*;
+import org.junit.jupiter.api.*;
 
 /**
  * Tests for {@link logical#splitFormat(String)} and some related functions of {@link logical}.

=== modified file 'test/com/goldencode/p2j/util/TestNumberTypeParseFormat.java'
--- test/com/goldencode/p2j/util/TestNumberTypeParseFormat.java	2021-11-23 20:02:38 +0000
+++ test/com/goldencode/p2j/util/TestNumberTypeParseFormat.java	2022-09-30 12:06:21 +0000
@@ -13,6 +13,7 @@
 **                  The class was renamed.
 **     VVT 20210426 Class renamed, former name TestNumberFormatParse.
 **     VVT 20211123 Missing Javadocs and annotations added.
+**     VVT 2022xxxx Refactored to make tests pure JUnit5. See #6237.
 */
 /*
 ** This program is free software: you can redistribute it and/or modify
@@ -68,9 +69,9 @@
 */
 package com.goldencode.p2j.util;
 
-import static org.junit.Assert.*;
+import static org.junit.jupiter.api.Assertions.*;
 
-import org.junit.*;
+import org.junit.jupiter.api.*;
 
 import com.goldencode.p2j.util.NumberType.*;
 

=== modified file 'test/com/goldencode/p2j/util/TestRowid.java'
--- test/com/goldencode/p2j/util/TestRowid.java	2021-09-17 15:14:10 +0000
+++ test/com/goldencode/p2j/util/TestRowid.java	2022-09-30 12:06:28 +0000
@@ -9,6 +9,7 @@
 **     VVT 20210331 A few more parse tests added after the review. See #5218-30.
 **     VVT 20210331 Two typos fixed.
 **     VVT 20210917 Javadoc fixed. 
+**     VVT 2022xxxx Refactored to make tests pure JUnit5. See #6237.
 */
 /*
 ** This program is free software: you can redistribute it and/or modify
@@ -64,12 +65,12 @@
 */
 package com.goldencode.p2j.util;
 
-import static org.junit.Assert.assertNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 
 import java.util.function.*;
 
-import org.junit.*;
+import org.junit.jupiter.api.*;
 
 /**
  * Various tests for ROWID parsing and formatting. Includes tests for

=== modified file 'test/com/goldencode/util/TestPrintable.java'
--- test/com/goldencode/util/TestPrintable.java	2022-03-07 22:24:50 +0000
+++ test/com/goldencode/util/TestPrintable.java	2022-10-27 14:20:37 +0000
@@ -7,6 +7,7 @@
 ** -#- -I- --Date-- ---------------------------------Description---------------------------------
 ** 001 VVT 20220306 Created initial version.
 **     VVT 20220307 AbstractPrintable class eliminated. See #6013-153.
+**     VVT 2022xxxx Refactored to make tests pure JUnit5. See #6237.
 */
 /*
 ** This program is free software: you can redistribute it and/or modify
@@ -64,7 +65,7 @@
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 
-import org.junit.*;
+import org.junit.jupiter.api.*;
 
 /**
  * Test classes, which implement {@link Printable} interface.

