Feature #7851
automatically add to conversion list all classes referenced via unit test suite annotations
100%
History
#1 Updated by Greg Shah almost 3 years ago
The ABLUnit/OEUnit @TestSuite annotation lists classes that are to be executed as part of the suite. We will make conversion smarter/easier if we automatically add these to the conversion list.
#2 Updated by Dănuț Filimon 8 months ago
- Status changed from New to WIP
- Assignee set to Dănuț Filimon
#3 Updated by Dănuț Filimon 8 months ago
This root is built in ProgressParser.oea_annotation(), line 10017:
@ [ANNOTATION]:<id_unavailable> @1:1
TestSuite [SYMBOL]:<id_unavailable> @1:2
= [ASSIGN]:<id_unavailable> @1:22
procedures [SYMBOL]:<id_unavailable> @1:12
"tests.small.start, tests.small.start1" [STRING]:<id_unavailable> @1:23
. It is possible to extract the text from the STRING and turn it into a filename which can be searched. Then maybe ScanDriver.addSource() can be used.#4 Updated by Greg Shah 8 months ago
[...]. It is possible to extract the text from the STRING and turn it into a filename which can be searched. Then maybe
ScanDriver.addSource()can be used.
Yes, this makes sense. You can use character.parseProgressCharLiteral() to create the Java String instance.
#5 Updated by Dănuț Filimon 8 months ago
Greg Shah wrote:
[...]. It is possible to extract the text from the STRING and turn it into a filename which can be searched. Then maybe
ScanDriver.addSource()can be used.Yes, this makes sense. You can use
character.parseProgressCharLiteral()to create the JavaStringinstance.
The most important part is adding the source to the symbol resolver.
sym.loadClass(files[i], true);and the current issue I am facing is
java.lang.RuntimeException: Cannot find class/interface tests/small/start in PROPATH.
So matching the procedure/class to the correct filename is something I need to work on. There's SymbolResolver.initPossibleClasses() which does this, but only for *.cls.
#6 Updated by Dănuț Filimon 8 months ago
In the following code:
// find the class file name in the propath
if (!srchPropathFindFile(found.clsname, found.type, found))
{
if (wa.preScanPass >= 1 && !wa.forceLoadFailure)
{
classDict.addSymbol(false, found.clsname, MOCK_CLASS_DEF);
wa.preScanRefs.add(name);
// this class is not found, but it must not break parsing - show a warning and
// 'sink' all references to it
String err = reportClassNotFound(found.clsname, false);
System.out.printf("WARNING: Lvl%02d parse: %s\n", wa.preScanPass, err);
return found.clsname;
}
else
{
// we fail only if we are referencing this class from the currently parsed
// file (we are not in pre-scan-mode)
reportClassNotFound(found.clsname, true);
}
}
srchPropathFindFile() call returns false, and ends up calling reportClassNotFound(). This calls SymbolResolver.findFile() and looks into propathCls, oo4glCls, dotnetCls, assemblyCls collections. The problem here is that propathCls contains classes from the skeleton and those added to the conversion process. There's also a cache of resolved classes which contains the same values. I want to add another cache and get all procedures and classes from the folder, regardless if they were added to the conversion or not.
I'll check where this gets me and expand from there.
#7 Updated by Dănuț Filimon 8 months ago
- % Done changed from 0 to 50
I managed to implement the fix for TestSuite that specifies classes:
@TestSuite(classes="tests.small.TestingClass,
tests.small.TestingClass1").
class tests.TestSuiteClass:
end class.
TestingClass and TestingClass1 are included in the conversion in this scenario. The good part is that classes are already supported, but procedures will require more work. This is the stacktrace:at com.goldencode.p2j.uast.ProgressParser.pre_scan_class(ProgressParser.java:6125) at com.goldencode.p2j.uast.AstGenerator.preScanClass(AstGenerator.java:1568) at com.goldencode.p2j.uast.SymbolResolver.loadClass(SymbolResolver.java:4937) at com.goldencode.p2j.uast.SymbolResolver.loadClass(SymbolResolver.java:4803) at com.goldencode.p2j.uast.ProgressParser.handleReferenceFiles(ProgressParser.java:2692) // <--- method that calls loadClass(), added for this task at com.goldencode.p2j.uast.ProgressParser.oea_annotation(ProgressParser.java:10070) at com.goldencode.p2j.uast.ProgressParser.oea_annotations(ProgressParser.java:9988) at com.goldencode.p2j.uast.ProgressParser.single_block(ProgressParser.java:8505) at com.goldencode.p2j.uast.ProgressParser.block(ProgressParser.java:7586) at com.goldencode.p2j.uast.ProgressParser.external_proc(ProgressParser.java:7513) at com.goldencode.p2j.uast.AstGenerator.parse(AstGenerator.java:1658) at com.goldencode.p2j.uast.AstGenerator.processFile(AstGenerator.java:1068) at com.goldencode.p2j.uast.ScanDriver.lambda$scan$0(ScanDriver.java:469) at com.goldencode.p2j.uast.ScanDriver$$Lambda$188/0x000077160c55dd98.call(Unknown Source:-1) at com.goldencode.p2j.uast.ScanDriver.scan(ScanDriver.java:509) at com.goldencode.p2j.uast.ScanDriver.scan(ScanDriver.java:318) at com.goldencode.p2j.convert.TransformDriver.runScanDriver(TransformDriver.java:432) at com.goldencode.p2j.convert.TransformDriver.front(TransformDriver.java:293) at com.goldencode.p2j.convert.TransformDriver.executeJob(TransformDriver.java:1082) at com.goldencode.p2j.convert.ConversionDriver.main(ConversionDriver.java:1258)
If procedures are used instead, it will result in a parsing issue because it is not a CLASS.
#8 Updated by Dănuț Filimon 8 months ago
Greg, I think we'll need something similar to pre_scan_class in progress.g, but for procedures. What do you think?
#9 Updated by Greg Shah 8 months ago
Dănuț Filimon wrote:
Greg, I think we'll need something similar to pre_scan_class in progress.g, but for procedures. What do you think?
I would very much like to avoid that. pre_scan_class is only needed because the 4GL compiler uses 2 passes to parse .cls files. In a procedure file (e.g. .p), code cannot reference symbols defined later then itself. So if you define a variable you can only use it after that definition. In a .cls, that is not the case.
Can't you call the same processing directly from the oea_annotation rule? Why does it need to be in pre_scan_class? The files referenced don't have to be loaded before parsing of the current file finishes, so we shouldn't need to do this during pre_scan_class.
#10 Updated by Dănuț Filimon 8 months ago
Greg Shah wrote:
Dănuț Filimon wrote:
Greg, I think we'll need something similar to pre_scan_class in progress.g, but for procedures. What do you think?
I would very much like to avoid that.
pre_scan_classis only needed because the 4GL compiler uses 2 passes to parse.clsfiles. In a procedure file (e.g..p), code cannot reference symbols defined later then itself. So if you define a variable you can only use it after that definition. In a.cls, that is not the case.Can't you call the same processing directly from the
oea_annotationrule? Why does it need to be inpre_scan_class? The files referenced don't have to be loaded before parsing of the current file finishes, so we shouldn't need to do this duringpre_scan_class.
I need to call ScanDriver.addSource() and this happens when I call SymbolResolver.loadClass(). I can avoid using AstGenerator.preScanClass() entirely and the conversion will work.
#11 Updated by Dănuț Filimon 8 months ago
- Status changed from WIP to Review
- % Done changed from 50 to 100
- reviewer Greg Shah added
Committed 7851a/16298. Added support for automatically adding procedures/classes referenced using the TestSuite annotation.
This is the conversion result:
[java] ------------------------------------------------------------------------------
[java] Incremental Conversion - checking file signature
[java] ------------------------------------------------------------------------------
[java]
[java] Using default schema profile.
[java] ./abl/tests/TestSuiteClass.cls - added for conversion
[java] ./abl/tests/TestSuiteClass1.cls - added for conversion
[java]
[java] ------------------------------------------------------------------------------
[java] SchemaLoader
[java] ------------------------------------------------------------------------------
[java]
...
[java]
[java] ------------------------------------------------------------------------------
[java] Scanning Progress Source (preprocessor, lexer, parser, persist ASTs)
[java] ------------------------------------------------------------------------------
[java]
[java] ./abl/tests/TestSuiteClass.cls
...
[java] Lvl01 parse: ./abl/tests/TestSuiteClass.cls
[java] Lvl01 DONE: ./abl/tests/TestSuiteClass.cls
[java] ./abl/tests/TestSuiteClass.cls (already parsed)
[java] ./abl/tests/small/start1.p
[java] ./abl/tests/small/start.p
[java] ./abl/tests/TestSuiteClass1.cls
[java] Lvl01 parse: ./abl/tests/small/TestingClass.cls
[java] Lvl01 DONE: ./abl/tests/small/TestingClass.cls
[java] Lvl01 parse: ./abl/tests/small/TestingClass1.cls
[java] Lvl01 DONE: ./abl/tests/small/TestingClass1.cls
[java] Lvl01 parse: ./abl/tests/TestSuiteClass1.cls
[java] Lvl01 DONE: ./abl/tests/TestSuiteClass1.cls
[java] ./abl/tests/TestSuiteClass1.cls (already parsed)
[java] ./abl/tests/small/TestingClass1.cls
[java] ./abl/tests/small/TestingClass.cls
[java]
[java] ------------------------------------------------------------------------------
As it can be seen above, both procedures and classes references using the TestSuite annotation are automatically added to the conversion process.
Greg, please review.
#12 Updated by Greg Shah 8 months ago
- reviewer Constantin Asofiei added
Code Review Task branch 7851a Revision 16298
1. The string comparisons in handleReferenceFiles() probably need to be case-insensitive (and possibly also even ignore trailing whitespace).
2. Does handleReferenceFiles() need to handle the possibility that both classes and procedures are specified?
3. The code can be simpler if you pass a parameter from oea_annotation to oea_annotation_arg (a boolean that is true when it sees the annotation name "TestSuite". Then handleReferenceFiles() can be called from oea_annotation_arg (and not in oea_annotation), making it easy to handle both "classes" and "procedures".
4. The SR.loadClass() and SR.findFile() implementation was already overcomplicated. And it was not 100% correct. With this change, we are adding complexity (and extra searches) for a large number of callers when only the calls from handleReferenceFiles() need the changes. For this reason, I'm hesitant to accept the change. You have admirably reused a lot of our existing code, which is generally a good idea. But in this case, I worry that we are adding too much on top of something that was already not nice.
- There is a lot of duplication between the
.clsfiles inpropathClsand the newprogramSourcesforCLASS_SPLAT. This means each searching which will almost never be needed. - The infrastructure for all of this was only to process classes/interfaces/enums. It did not support external procedures. Now, the external procedures are added into this which will be very confusing for future readers. For example, you had to add the
if (found.filename != null && !found.filename.endsWith(".cls"))inloadClass()because otherwise we would try to recursively parse procedures that could never have been found before. - The addition of
getBasepathFileList("*.p", caseSens),does not seem right. External procedures can be named anything. It is common to see.wfiles but they can really have any extension or even no extension. There is no easy way to make a list of all procedures.
My gut instinct tells me to solve this a different way (only for the handleReferenceFiles() case) where we dynamically search the filesystem to find these files and add them.
Constantin: Please take a look and offer your thoughts.
#13 Updated by Dănuț Filimon 8 months ago
The TestSuite can specify both the classes and procedures, so it's better to handle all of them rather than half. The filesystem search is the best option right now because, as mentioned, adding the external procedures when calling loadClass() will cause a lot of confusion.
#15 Updated by Dănuț Filimon 8 months ago
@TestSuite(classes=""). @TestSuite(classes=" "). @TestSuite(classes=","). @TestSuite(classes="tests.TestClass1"). @TestSuite(classes="tests/TestClass2.cls"). @TestSuite(claSSes="tests/TestClass3"). @TestSuite(CLassES="tests/TestClass4.cls,tests/TestClass5.cls"). @TestSuite(CLASSES="tests/TestClass6.cls,tests/TestClass7,tests.TestClass8"). @TestSuite(procedures=""). @TestSuite(procedures=" "). @TestSuite(procedures=","). @TestSuite(procEDUres="tests/testproc1.p"). @TestSuite(PRoceduRES="tests/testproc2.p,tests/testproc3.p"). @TestSuite(classes="tests.TestClass9,,tests/TestClass10.cls,tests/TestClass11", procedures="tests/testproc4.p,,tests/testproc5.p"). CLASS TestSuite7851: END CLASS.I am using this test case to check if the implementation behaves properly in each scenario. I noticed the following:
- Only *.p files can be used for
procedures, - In
procedures,/must be used and not.. - In
procedures, if the extension is not mentioned, it will use.cls - Multiple
classes/procedurescan be specified, but the ones after the first (in order) will always fail to find the first file (regardless if it exists or not). ,can be used to specify empty entries, it will not affect the list.classes/proceduresmust be used as full words, can't be abbreviated.
#17 Updated by Dănuț Filimon 8 months ago
- Status changed from WIP to Review
- % Done changed from 80 to 100
#18 Updated by Vladimir Tsichevski 8 months ago
I noticed I was not listed as a reviewer, though I have already completed the review.
7851a rev 16299 code review:
SymbolResolver.loadProceduremethod should bestatic.progress.g:symbolTextis nevernull, so the null check should be removed:if (symbolText == null ||- The code:
boolean isTestSuite = false; if (tmp != null) { isTestSuite = tmp.getText().equalsIgnoreCase("TestSuite"); }
can be rewritten as:boolean isTestSuite = tmp != null && tmp.getText().equalsIgnoreCase("TestSuite");
Also, from your code I gathered the classes in the @TestSuite annotation can be referenced by class file names instead of class names. This is not mentioned in OE ABLUnit documentation, but it works in OE.
Currently this is not implemented in conversion, but probably should. I am going to create another task for this.
#19 Updated by Dănuț Filimon 8 months ago
- Status changed from Review to Internal Test
- reviewer Vladimir Tsichevski added
Thank you Vladimir! I committed 7851a/16300 to address your review.
#21 Updated by Marian Edu 7 months ago
Greg Shah wrote:
+ Marian/Vladimir
Greg, all I can share about this is that we were only running `ABLUnit` in FWD from inside Eclipse so we always excluded the `test suites` from conversion just to avoid having all tests ran twice. Not sure if Vladimir had changed anything in that area but at the time the engine was running all tests in the package so if there were a test suite all tests were executed twice, hence we didn't bother with the test suite nor had the need to automatically add the tests in it to the conversion list but this makes a lot of sense if the engine allows specifying a test suite for test execution.
What we had done on the subject was to automatically add all base classes, statically ran procedures and database table triggers to the conversion list - everything that we've considered as 'dependency' in order to keep the conversion lists cleaner. Any dynamic run - using `value` or `call` interface - can't be resolved as it can actually be anything if variables are used :(
#22 Updated by Dănuț Filimon 7 months ago
Regression tests passed, I am rebasing to check for conflicts.
#23 Updated by Dănuț Filimon 7 months ago
Rebased 7851a to latest trunk/16312, the branch is now at revision 16315.
7851a can be merged.
#25 Updated by Alexandru Lungu 7 months ago
- Status changed from Internal Test to Merge Pending
Alex: Please schedule this in the merge queue when trunk thaws.
Please merge 7851a to trunk now.
#26 Updated by Dănuț Filimon 7 months ago
- version_resolved set to trunk/16317
- Status changed from Merge Pending to Test
Branch 7851a was merged into trunk as rev. 16317 and archived.