diff --git a/doc/changelog.html b/doc/changelog.html
index 70d41bc..c050652 100644
--- a/doc/changelog.html
+++ b/doc/changelog.html
@@ -16,6 +16,8 @@
OEUnit - Unit Testing Framework
Writing a Test Suite
Running a Test
Reporting Test Results
+ DataProviders
+ Fixtures
License
Change Log
diff --git a/doc/dataprovider.html b/doc/dataprovider.html
new file mode 100644
index 0000000..a4fe201
--- /dev/null
+++ b/doc/dataprovider.html
@@ -0,0 +1,214 @@
+
+
+
+
+ OEUnit - DataProviders
+
+
+
+
+ DataProviders
+
+ What is a DataProvider?
+
+ A DataProvider is a class method inside of a test case, which provides data which is passed
+ to individual test methods as method parameters.
+
+ A test method declares that is requires a DataProvider when it is defined, using the dataProvider
+ attribute, supplying the name of a DataProvider method as the attribute value.
+
+ A class method is defined with the @DataProvider annotation, with an optional
+ name attribute. This method returns an object of type OEUnit.Data.DataProvider which
+ contains the data that will be used to repeatedly invoke the test method.
+
+ How to use a DataProvider
+
+ 1. Define a test method, with INPUT parameters for each test variable.
+ Use the dataProvider attribute to specify the name of a class method which will provide data
+ for the test variables.
+ For example:
+ ROUTINE-LEVEL ON ERROR UNDO, THROW.
+
+ USING OEUnit.Assertion.Assert.
+ USING OEUnit.Data.DataProvider.
+
+ CLASS SimpleTest:
+
+ @Test (dataProvider=StatusChangeProvider).
+ METHOD PUBLIC VOID AcceptStatusChange(INPUT varStatus AS CHARACTER, INPUT varAccepted AS LOGICAL):
+ DEFINE VARIABLE result AS LOGICAL NO-UNDO.
+ result = Order:ChangeStatus(INPUT varStatus).
+ Assert:AreEqual(result,varAccepted).
+ END METHOD .
+
+ END CLASS .
+
+
+ 2. Add a method to the class which returns a DataProvider object.
+ Use the @DataProvider annotation to indicate that the class is a DataProvider method.
+ For example:
+ @DataProvider.
+ METHOD PUBLIC DataProvider StatusChangeProvider():
+ DEFINE VARIABLE dataProvider AS DataProvider NO-UNDO .
+ dataProvider = NEW DataProvider().
+ dataProvider:FromJSON("~{ ~"data~": ["
+ + "~{ ~"status~": ~"NEW~", ~"accepted~": true},"
+ + "~{ ~"status~": ~"ACCEPTED~", ~"accepted": true},"
+ + "~{ ~"status~": ~"PICKING~", ~"accepted": true},"
+ + "~{ ~"status~": ~"POSTED~", ~"accepted": false},"
+ + "~{ ~"status~": ~"DELIVERED~", ~"accepted": false},"
+ + "~{ ~"status~": ~"CANCELLED~", ~"accepted": true},"
+ + "]}" ).
+ RETURN dataProvider.
+ END METHOD .
+
+
+ Important Notes:
+
+ The name of the DataProvider class method must match the value of the dataProvider
+ attribute on the test method, or the @DataProvider annotation must
+ have a name attribute which matches the value of the dataProvider
+ attribute on the test method.
+ Execution of a DataProvider method will stop when an assertion fails or a Progress.Lang.Error
+ is thrown.
+ There are no special naming requirements for DataProvider methods.
+ DataProvider methods must be PUBLIC
+ DataProvider methods must accept no parameters.
+ DataProvider methods can be STATIC.
+ DataProvider methods can be used by more than one test method.
+ Any @Before methods are run before each test method invocation.
+ Any @After methods are run after each test method invocation.
+
+
+
+ 3. Run the test case as per normal.
+
+
+ 4. In the test case results, the test method will be listed multiple times with a separate status per invocation.
+
+ Name Attribute
+
+ Each method annotated with @DataProvider can specify a name attribute which is used
+ when searching for a DataProvider method, instead of the method name.
+
+
+ Syntax:
+
+
@DataProvider[(name="DataProviderName ")].
+
+ DataProviderName
+ The Name of the DataProvider. This name can then be specified by
+ a test method as its DataProvider, in the dataProvider attribute
+
+
+
+
+ Example:
+
+ @Test (dataProvider=StatusChangeProvider).
+ METHOD PUBLIC VOID AcceptStatusChange(INPUT varStatus AS CHARACTER, INPUT varAccepted AS LOGICAL):
+ DEFINE VARIABLE result AS LOGICAL NO-UNDO.
+ result = Order:ChangeStatus(INPUT varStatus).
+ Assert:AreEqual(result,varAccepted).
+ END METHOD .
+
+ @DataProvider(name=StatusChangeProvider).
+ METHOD PUBLIC DataProvider myDataProvider():
+ DEFINE VARIABLE dataProvider AS DataProvider NO-UNDO .
+ dataProvider = NEW DataProvider().
+ dataProvider:FromJSON("~{ ~"data~": ["
+ + "~{ ~"status~": ~"NEW~", ~"accepted~": true},"
+ + "~{ ~"status~": ~"ACCEPTED~", ~"accepted": true},"
+ + "~{ ~"status~": ~"PICKING~", ~"accepted": true},"
+ + "~{ ~"status~": ~"POSTED~", ~"accepted": false},"
+ + "~{ ~"status~": ~"DELIVERED~", ~"accepted": false},"
+ + "~{ ~"status~": ~"CANCELLED~", ~"accepted": true},"
+ + "]}" ).
+ RETURN dataProvider.
+ END METHOD .
+ DataProvider Methods
+ The DataProvider class provides a range of methods for loading data into the DataProvider.
+ METHOD PUBLIC LOGICAL FromJSON(INPUT json AS LONGCHAR)
+ This method is used to load data from JSON text stored in a LONGCHAR .
+
+
+
+ Notes:
+
+ The structure of the JSON data should match that which is accepted by an OpenEdge DATASET
+ or TEMP-TABLE when using the READ-JSON method.
+ The method will return TRUE if the data was successfully loaded.
+
+ METHOD PUBLIC LOGICAL FromJSONFile(INPUT path AS CHARACTER):
+ This method is used to load data from JSON text stored in a file. The file path should be provided as the
+ INPUT parameter.
+
+
+
+ Notes:
+
+ The structure of the JSON data in the file should match that which is accepted by an OpenEdge
+ DATASET or TEMP-TABLE when using the
+ READ-JSON method.
+ The method will return TRUE if the data was successfully loaded.
+
+ METHOD PUBLIC LOGICAL FromXML(INPUT xml AS LONGCHAR)
+ This method is used to load data from XML data stored in a LONGCHAR .
+
+
+
+ Notes:
+
+ The structure of the XML data should match that which is accepted by an OpenEdge DATASET
+ or TEMP-TABLE when using the READ-XML method.
+ The method will return TRUE if the data was successfully loaded.
+ It is advised to specify the XML Schema in the XML data in order for data types to be correctly interpreted.
+
+ METHOD PUBLIC LOGICAL FromXMLFile(INPUT path AS CHARACTER):
+ This method is used to load data from XML data stored in a file. The file path should be provided as the
+ INPUT parameter.
+
+
+
+ Notes:
+
+ The structure of the XML data in the file should match that which is accepted by an OpenEdge
+ DATASET or TEMP-TABLE when using the
+ READ-XML method.
+ The method will return TRUE if the data was successfully loaded.
+ It is advised to specify the XML Schema in the XML data in order for data types to be correctly interpreted.
+
+ METHOD PUBLIC LOGICAL FromTempTable(INPUT ttSrc AS HANDLE):
+ This method is used to load data from an existing temp-table, via the provided HANDLE INPUT parameter.
+
+
+
+ Notes:
+
+ The entire data and metaschema of the temp-table are copied.
+ Data is copied in EMPTY mode
+ The data and metaschema of the provided source temp-table are not affected.
+ The method will return TRUE if the data was successfully loaded.
+
+
+
+
diff --git a/doc/fixtures.html b/doc/fixtures.html
new file mode 100644
index 0000000..ac335a1
--- /dev/null
+++ b/doc/fixtures.html
@@ -0,0 +1,233 @@
+
+
+
+
+ OEUnit - Fixtures
+
+
+
+
+ Fixtures
+
+ What are Fixtures?
+
+ A fixture is an object that inserts, or creates, data into one or more database tables before
+ a unit test is run. At the end of the test, the fixture data is removed.
+
+ A test method declares that is requires a Fixture when it is defined, using the fixture
+ attribute, supplying the name of a fixture class method as the attribute value.
+
+ A class method is defined in the test case, with the @Fixture
+ annotation, with an optional name attribute. This method returns an object of type
+ OEUnit.Data.Fixture which will contain the data to be loaded into a database when required.
+
+ How to use a Fixture
+
+ 1. Define a test method, using the fixture attribute to specify the name of a class method
+ which will provide a fixture object.
+ For example:
+ ROUTINE-LEVEL ON ERROR UNDO, THROW.
+
+ USING OEUnit.Assertion.Assert.
+ USING OEUnit.Data.Fixture.
+
+ CLASS SimpleTest:
+
+ @Test (fixture=OrderStatusFixture).
+ METHOD PUBLIC VOID AcceptStatusChange():
+ DEFINE VARIABLE result AS LOGICAL NO-UNDO.
+ FIND FIRST Order
+ WHERE Order.Status = "NEW" NO-LOCK NO-ERROR .
+ Order:OpenOrder(Order.OrderNo).
+ result = Order:ChangeStatus(INPUT "ACCEPTED").
+ Assert:AreEqual(result,TRUE ).
+ END METHOD .
+
+ END CLASS .
+
+
+ 2. Add a method to the class which returns an OEUnit.Data.Fixture object.
+ Use the @Fixture annotation to indicate that the class is a Fixture method.
+ For example:
+ @Fixture.
+ METHOD PUBLIC Fixture OrderStatusFixture():
+ DEFINE VARIABLE fixture AS Fixture NO-UNDO .
+ fixture = NEW Fixture().
+ fixture:FromJSON("~{ ~"Order~": ["
+ + "~{ ~"OrderNo~": ~"10000~", ~"CustomerNo~": 99000, ~"Status~": ~"COMPLETED~"},"
+ + "~{ ~"OrderNo~": ~"10001~", ~"CustomerNo~": 99001, ~"Status~": ~"CANCELLED~"},"
+ + "~{ ~"OrderNo~": ~"10002~", ~"CustomerNo~": 99002, ~"Status~": ~"COMPLETED~"},"
+ + "~{ ~"OrderNo~": ~"10003~", ~"CustomerNo~": 99000, ~"Status~": ~"NEW~"},
+ + "~{ ~"OrderNo~": ~"10004~", ~"CustomerNo~": 99003, ~"Status~": ~"ACCEPTED~"},"
+ + "~{ ~"OrderNo~": ~"10005~", ~"CustomerNo~": 99004, ~"Status~": ~"PAID~"},"
+ + "]}" ).
+ RETURN fixture.
+ END METHOD .
+
+
+ Important Notes:
+
+ The name of the Fixture class method must match the value of the fixture
+ attribute on the test method, or the @Fixture annotation must
+ have a name attribute which matches the value of the fixture
+ attribute on the test method.
+ Execution of a Fixture method will stop when an assertion fails or a Progress.Lang.Error
+ is thrown.
+ There are no special naming requirements for Fixture methods.
+ Fixture methods must be PUBLIC
+ Fixture methods must accept no parameters.
+ Fixture methods can be STATIC.
+ Fixture methods can be used by more than one test method.
+ A transaction is created before the records provided in the fixture are loaded, and is rolled-back
+ after the test method returns.
+
+
+
+ 3. Run the test case as per normal.
+
+
+
Name Attribute
+
+ Each method annotated with @Fixture can specify a name attribute which is used
+ when searching for a Fixture method, instead of the method name.
+
+
+ Syntax:
+
+
@Fixture[(name="FixtureName ")].
+
+ FixtureName
+ The Name of the Fixture. This name can then be specified by
+ a test method as its Fixture, in the fixture attribute
+
+
+
+
+ Example:
+
+ @Test (dataProvider=OrderStatusFixture).
+ METHOD PUBLIC VOID AcceptStatusChange():
+ DEFINE VARIABLE result AS LOGICAL NO-UNDO.
+ FIND FIRST Order
+ WHERE Order.Status = "NEW" NO-LOCK NO-ERROR .
+ Order:OpenOrder(Order.OrderNo).
+ result = Order:ChangeStatus(INPUT "ACCEPTED").
+ Assert:AreEqual(result,TRUE ).
+ END METHOD .
+
+ @Fixture (name=OrderStatusFixture).
+ METHOD PUBLIC Fixture myFixture():
+ DEFINE VARIABLE fixture AS Fixture NO-UNDO .
+ fixture = NEW Fixture().
+ fixture:FromJSON("~{ ~"Order~": ["
+ + "~{ ~"OrderNo~": ~"10000~", ~"CustomerNo~": 99000, ~"Status~": ~"COMPLETED~"},"
+ + "~{ ~"OrderNo~": ~"10001~", ~"CustomerNo~": 99001, ~"Status~": ~"CANCELLED~"},"
+ + "~{ ~"OrderNo~": ~"10002~", ~"CustomerNo~": 99002, ~"Status~": ~"COMPLETED~"},"
+ + "~{ ~"OrderNo~": ~"10003~", ~"CustomerNo~": 99000, ~"Status~": ~"NEW~"},"
+ + "~{ ~"OrderNo~": ~"10004~", ~"CustomerNo~": 99003, ~"Status~": ~"ACCEPTED~"},"
+ + "~{ ~"OrderNo~": ~"10005~", ~"CustomerNo~": 99004, ~"Status~": ~"PAID~"},"
+ + "]}" ).
+ RETURN fixture.
+ END METHOD .
+ Fixture Methods
+ The Fixture class provides a range of methods for loading data into the Fixture.
+ Each successive call to one of these methods defines a range of data to be loaded. When
+ the records are loaded, they are done so in the order that they are specified to the Fixture
+ object.
+ METHOD PUBLIC LOGICAL FromJSON(INPUT json AS LONGCHAR)
+ This method is used to load data from JSON text stored in a LONGCHAR .
+
+
+
+ Notes:
+
+ The structure of the JSON data should match that which is accepted by an OpenEdge DATASET
+ when using the READ-JSON method.
+ The method will return TRUE if the data was successfully loaded.
+ Only data in the top-level objects in the JSON data will be loaded. Child objects are ignored.
+
+ METHOD PUBLIC LOGICAL FromJSONFile(INPUT path AS CHARACTER):
+ This method is used to load data from JSON text stored in a file. The file path should be provided as the
+ INPUT parameter.
+
+
+
+ Notes:
+
+ The structure of the JSON data in the file should match that which is accepted by an OpenEdge
+ DATASET when using the READ-JSON method.
+ The method will return TRUE if the data was successfully loaded.
+ Only data in the top-level objects in the JSON data will be loaded. Child objects are ignored.
+
+ METHOD PUBLIC LOGICAL FromXML(INPUT xml AS LONGCHAR)
+ This method is used to load data from XML data stored in a LONGCHAR .
+
+
+
+ Notes:
+
+ The structure of the XML data should match that which is accepted by an OpenEdge DATASET
+ when using the READ-XML method.
+ The method will return TRUE if the data was successfully loaded.
+ It is advised to specify the XML Schema in the XML data in order for data types to be correctly interpreted.
+ Only data in the top-level objects in the XML data will be loaded. Child objects are ignored.
+
+ METHOD PUBLIC LOGICAL FromXMLFile(INPUT path AS CHARACTER):
+ This method is used to load data from XML data stored in a file. The file path should be provided as the
+ INPUT parameter.
+
+
+
+ Notes:
+
+ The structure of the XML data in the file should match that which is accepted by an OpenEdge
+ DATASET when using the READ-XML method.
+ The method will return TRUE if the data was successfully loaded.
+ It is advised to specify the XML Schema in the XML data in order for data types to be correctly interpreted.
+ Only data in the top-level objects in the XML data will be loaded. Child objects are ignored.
+
+ METHOD PUBLIC LOGICAL FromTempTable(INPUT ttSrc AS HANDLE):
+ This method is used to load data from an existing temp-table, via the provided HANDLE INPUT parameter.
+
+
+
+ Notes:
+
+ The entire data and metaschema of the temp-table are copied.
+ Data is copied in EMPTY mode
+ The data and metaschema of the provided source temp-table are not affected.
+ The method will return TRUE if the data was successfully loaded.
+
+ METHOD PUBLIC LOGICAL FromDataSet(INPUT dsSrc AS HANDLE):
+ This method is used to load data from an existing DataSet, via the provided HANDLE INPUT parameter.
+
+
+
+ Notes:
+
+ The entire data and metaschema of the temp-table are copied.
+ Data is copied in EMPTY mode
+ The method will return TRUE if the data was successfully loaded.
+
+
+
+
diff --git a/doc/index.html b/doc/index.html
index 846a450..f711665 100644
--- a/doc/index.html
+++ b/doc/index.html
@@ -16,6 +16,8 @@ OEUnit - Unit Testing Framework
Writing a Test Suite
Running a Test
Reporting Test Results
+ DataProviders
+ Fixtures
License
Change Log
diff --git a/doc/installation.html b/doc/installation.html
index efce2b5..b53f69c 100644
--- a/doc/installation.html
+++ b/doc/installation.html
@@ -16,6 +16,8 @@ OEUnit - Unit Testing Framework
Writing a Test Suite
Running a Test
Reporting Test Results
+ DataProviders
+ Fixtures
License
Change Log
diff --git a/doc/reportingtests.html b/doc/reportingtests.html
index 030faa2..cdadad8 100644
--- a/doc/reportingtests.html
+++ b/doc/reportingtests.html
@@ -16,6 +16,8 @@ OEUnit - Unit Testing Framework
Writing a Test Suite
Running a Test
Reporting Test Results
+ DataProviders
+ Fixtures
License
Change Log
diff --git a/doc/runningtests.html b/doc/runningtests.html
index 5ae59e3..15f4629 100644
--- a/doc/runningtests.html
+++ b/doc/runningtests.html
@@ -16,6 +16,8 @@ OEUnit - Unit Testing Framework
Writing a Test Suite
Running a Test
Reporting Test Results
+ DataProviders
+ Fixtures
License
Change Log
diff --git a/doc/testcase.html b/doc/testcase.html
index 4ef4b51..166c2b6 100644
--- a/doc/testcase.html
+++ b/doc/testcase.html
@@ -16,6 +16,8 @@ OEUnit - Unit Testing Framework
Writing a Test Suite
Running a Test
Reporting Test Results
+ DataProviders
+ Fixtures
License
Change Log
@@ -77,7 +79,7 @@ Test Methods
Syntax:
-
@Test[(expected="ErrorTypeName ")].
+ @Test[([expected="ErrorTypeName "][,dataProvider="DataProviderMethod"])].
ErrorTypeName
The Type-Name of a class that inherits from Progress.Lang.Error.
@@ -85,6 +87,11 @@
Test Methods
Test method. If the expected error is thrown, the test passes, otherwise the test
fails.
+
+ DataProviderMethod
+ The name of a method that has been defined with a @DataProvider annotation.
+ For more information on DataProviders, see Data Providers .
+
@@ -120,7 +127,11 @@ Test Methods
If no errors are thrown by while running a Test method, the test is assumed to have
succeeded.
There are no special naming requirements for Test methods.
- Test methods must be PUBLIC and accept no parameters.
+ Test methods must be PUBLIC
+ Test methods must accept no parameters, unless they have been defined with the DataProvider
+ attribute. In which case, the number of parameters, data types, and order must match the order of
+ the data provided in the @DataProvider method. Only the INPUT parameter type is supported. For more
+ infomation on DataProviders, see DataProviders .
Test methods can be STATIC.
Before Methods
diff --git a/doc/testsuite.html b/doc/testsuite.html
index c23c419..12c3785 100644
--- a/doc/testsuite.html
+++ b/doc/testsuite.html
@@ -16,6 +16,8 @@ OEUnit - Unit Testing Framework
Writing a Test Suite
Running a Test
Reporting Test Results
+ DataProviders
+ Fixtures
License
Change Log
diff --git a/readme.md b/readme.md
index c35c457..b8298b7 100644
--- a/readme.md
+++ b/readme.md
@@ -1,19 +1,20 @@
-#OEUnit
+# OEUnit
OEUnit is a unit testing framework for OpenEdge ABL. OEUnit is intended to help write and run repeatable unit tests - similar to JUnit and other xUnit-based unit testing frameworks.
For full documentation, see **doc/index.html** in the repository.
-##Download
+## Download
See the [Releases](https://github.com/CameronWills/OEUnit/releases) page.
-##Screenshot
+## Screenshot
Below is a screenshot of the test results docked in Progress Developer Studio (previously OpenEdge Architect).

-##Change Log
-###1.4
+## Change Log
+
+### 1.4
- version 1.4 requires OpenEdge 10.2B03
- Merged changes to better support automation and XML reporting. Thanks to Arek Jaworski and Mark Abbott.
@@ -21,7 +22,7 @@ Below is a screenshot of the test results docked in Progress Developer Studio (p
- Removed 'Author' from the file headers - a leftover relic from a heading template and not conducive to social coding :)
- Tested with OpenEdge 11.3 on Windows 7 64bit
-###1.3
+### 1.3
- Version 1.3 requires OpenEdge 10.B03
- Fixed bug where the results window would display for a moment and then disappear.
@@ -30,7 +31,7 @@ Below is a screenshot of the test results docked in Progress Developer Studio (p
- Improved documentation in line with the new Progress Developer Studio (previously named OpenEdge Architect)
- Tested with OpenEdge 11.2 on Windows 7 64bit
-###1.2
+### 1.2
- Version 1.2 requires OpenEdge 10.2B
- Changes to show the error message returned from simple RETURN ERROR "error message" statements
@@ -42,10 +43,10 @@ Below is a screenshot of the test results docked in Progress Developer Studio (p
- Simplified interfaces for Util/List.cls and Util/IComparator.cls
- Added a change log to the project documentation
-###1.1
+### 1.1
- Added changes to support the OpenEdge 10.2a runtime
- Corrected the method modifiers in UI/ResultsWindowView.cls - causing compile time errors in 10.2a, but was somehow working in 10.1c ?
- Fixed bug in Remove() method of Util/List.cls - elements were being re-indexed incorrectly
-###1.0
+### 1.0
- Initial release - basic functionality to run test cases and suites.
diff --git a/src/OEUnit/Assertion/AssertStringType.i b/src/OEUnit/Assertion/AssertStringType.i
index e4755fa..3dc3261 100644
--- a/src/OEUnit/Assertion/AssertStringType.i
+++ b/src/OEUnit/Assertion/AssertStringType.i
@@ -38,8 +38,8 @@
throws an AssertionFailedError with the given failMessage.
----------------------------------------------------------------------------*/
METHOD STATIC VOID IsNotNullOrEmpty(INPUT val AS {&DataType}, INPUT failMessage AS CHARACTER):
- Assert:IsNotNull(val).
- Assert:AreNotEqual(val, "").
+ Assert:IsNotNull(val, failMessage).
+ Assert:AreNotEqual(val, "", failMessage).
END METHOD.
/*----------------------------------------------------------------------------
diff --git a/src/OEUnit/Automation/JUnitReporter.cls b/src/OEUnit/Automation/JUnitReporter.cls
index 7234823..0641fef 100644
--- a/src/OEUnit/Automation/JUnitReporter.cls
+++ b/src/OEUnit/Automation/JUnitReporter.cls
@@ -41,8 +41,9 @@ CLASS OEUnit.Automation.JUnitReporter INHERITS FileReporter:
----------------------------------------------------------------------------*/
METHOD PROTECTED VOID ReportTestClassResult(INPUT hSaxWriter AS HANDLE, INPUT results AS TestClassResult):
- DEFINE VARIABLE HasChildSuites AS LOGICAL NO-UNDO INITIAL FALSE.
- DEFINE VARIABLE i AS INTEGER NO-UNDO.
+ DEFINE VARIABLE HasChildSuites AS LOGICAL NO-UNDO INITIAL FALSE.
+ DEFINE VARIABLE i AS INTEGER NO-UNDO.
+ DEFINE VARIABLE Package AS CHARACTER NO-UNDO.
/* Determine if there are any child TestClassResult items - if so, this
* needs to be output as a "testsuites" element, not a "testsuite".
@@ -71,11 +72,20 @@ CLASS OEUnit.Automation.JUnitReporter INHERITS FileReporter:
IF NOT HasChildSuites THEN
DO:
hSaxWriter:INSERT-ATTRIBUTE("skipped", STRING(results:CountTestsWithStatus(TestResult:StatusIgnored))).
+
+ /* Determine package name by removing class name from end of full class name */
+ ASSIGN Package = results:GetName()
+ i = R-INDEX(Package, ".").
+
+ IF(i > 1) THEN
+ ASSIGN Package = SUBSTRING(Package,1, i - 1).
+
+ hSaxWriter:INSERT-ATTRIBUTE("package", TRIM(Package,".")).
+
/* The following attributes are not implementable at this point in time, but are available in the
* JUnit XML format. Uncomment, and implement when possible.
*/
/* hSaxWriter:INSERT-ATTRIBUTE("id", ""). */
- /* hSaxWriter:INSERT-ATTRIBUTE("package", ""). */
/* hSaxWriter:INSERT-ATTRIBUTE("hostname", ""). */
/* hSaxWriter:INSERT-ATTRIBUTE("timestamp", ""). */
END.
diff --git a/src/OEUnit/Automation/Pct/RunTests.p b/src/OEUnit/Automation/Pct/RunTests.p
index dd78f65..3e9670b 100644
--- a/src/OEUnit/Automation/Pct/RunTests.p
+++ b/src/OEUnit/Automation/Pct/RunTests.p
@@ -20,23 +20,26 @@ DEFINE INPUT PARAMETER testLocation AS CHARACTER NO-UNDO.
DEFINE OUTPUT PARAMETER hasErrors AS LOGICAL NO-UNDO.
/* if the testLocation is a file, assume it's a .cls reference and use it */
-DEFINE VARIABLE classFiles AS LONGCHAR NO-UNDO.
+DEFINE TEMP-TABLE ttClassFiles NO-UNDO
+ FIELD classFile AS CHARACTER.
+
+DEFINE BUFFER b_ClassFile FOR ttClassFiles.
FILE-INFO:FILE-NAME = testLocation.
IF SUBSTRING(FILE-INFO:FILE-TYPE, 1, 1) = "F" THEN DO:
- classFiles = testLocation.
+ CREATE b_ClassFile.
+ b_ClassFile.classFile = testLocation.
END.
ELSE DO:
/* the testLocation is a directory, create a list of test classes */
- RUN FindClassFiles(INPUT testLocation, OUTPUT classFiles).
+ RUN FindClassFiles(INPUT testLocation).
END.
DEFINE VARIABLE i AS INTEGER NO-UNDO.
DEFINE VARIABLE classFile AS CHARACTER NO-UNDO.
-REPEAT i = 1 TO NUM-ENTRIES(classFiles, "*"):
- classFile = ENTRY(i, classFiles, "*").
- RUN RunClassAsTest(classFile).
+FOR EACH b_ClassFile:
+ RUN RunClassAsTest(b_ClassFile.classFile).
END.
@@ -73,42 +76,35 @@ END PROCEDURE.
/*----------------------------------------------------------------------------
- Searches recursively for class files in a given path. Full filenames are
+ Searches recursively for class files in a given path. Full filenames are
returned seperated by a star(*)
- ----------------------------------------------------------------------------*/
+ ----------------------------------------------------------------------------*/
PROCEDURE FindClassFiles PRIVATE:
DEFINE INPUT PARAMETER path AS CHARACTER NO-UNDO.
- DEFINE OUTPUT PARAMETER classFiles AS CHARACTER NO-UNDO INIT "".
- DEFINE VARIABLE childClassFiles AS LONGCHAR NO-UNDO.
DEFINE VARIABLE directoryEntry AS CHARACTER NO-UNDO.
-
+
INPUT FROM OS-DIR (path).
REPEAT:
-
+
IMPORT directoryEntry.
FILE-INFO:FILE-NAME = path
+ (IF OPSYS = "WIN32" THEN "\" ELSE "/")
+ directoryEntry.
-
+
CASE SUBSTRING(FILE-INFO:FILE-TYPE, 1, 1):
WHEN "F" THEN DO:
- IF directoryEntry MATCHES ("*.cls") THEN
- classFiles = classFiles
- + (IF classFiles <> "" THEN "*" ELSE "")
- + FILE-INFO:FULL-PATHNAME.
+ IF directoryEntry MATCHES ("*.cls") THEN DO:
+ CREATE b_ClassFile.
+ b_ClassFile.classFile = FILE-INFO:FULL-PATHNAME.
+ END.
END.
WHEN "D" THEN DO:
IF directoryEntry <> ".." AND directoryEntry <> "." THEN DO:
- RUN FindClassFiles(INPUT FILE-INFO:FULL-PATHNAME, OUTPUT childClassFiles).
- IF childClassFiles <> "" AND childClassFiles <> ? THEN DO:
- classFiles = classFiles
- + (IF classFiles <> "" THEN "*" ELSE "")
- + childClassFiles.
- END.
+ RUN FindClassFiles(INPUT FILE-INFO:FULL-PATHNAME).
END.
END.
END CASE.
END.
-
+
END PROCEDURE.
diff --git a/src/OEUnit/Data/DataProvider.cls b/src/OEUnit/Data/DataProvider.cls
new file mode 100644
index 0000000..99c1768
--- /dev/null
+++ b/src/OEUnit/Data/DataProvider.cls
@@ -0,0 +1,171 @@
+/*------------------------------------------------------------------------------
+ File : DataProvider.cls
+ Package : OEUnit.Data
+ Description : Stores and uses data provided in associated methods when
+ calling a test case so that one test case can be called
+ multiple times.
+------------------------------------------------------------------------------*/
+USING Progress.Lang.*.
+USING OEUnit.Reflection.*.
+USING OEUnit.Data.*.
+
+ROUTINE-LEVEL ON ERROR UNDO, THROW.
+
+CLASS OEUnit.Data.DataProvider:
+
+ DEFINE PROTECTED VARIABLE ttData AS HANDLE NO-UNDO.
+ DEFINE PROTECTED VARIABLE ttQuery AS HANDLE NO-UNDO.
+
+ /*----------------------------------------------------------------------------
+ Property that holds the current size of data
+ ----------------------------------------------------------------------------*/
+ DEFINE PUBLIC PROPERTY Size AS INTEGER NO-UNDO GET. PRIVATE SET.
+
+ CONSTRUCTOR PUBLIC DataProvider():
+ SUPER().
+ Size = 0.
+ END CONSTRUCTOR.
+
+ DESTRUCTOR PUBLIC DataProvider():
+ IF VALID-HANDLE(ttData) THEN DELETE OBJECT ttData.
+ IF VALID-HANDLE(ttQuery) THEN
+ DO:
+ ttQuery:QUERY-CLOSE().
+ DELETE OBJECT ttQuery.
+ END.
+ END DESTRUCTOR.
+
+ METHOD PROTECTED VOID CountSize():
+ DEFINE VARIABLE res AS LOGICAL NO-UNDO.
+ Size = 0.
+ ResetQuery().
+ DO WHILE MoveNext() NE FALSE:
+ ASSIGN Size = Size + 1.
+ END.
+ ResetQuery().
+ END.
+
+ METHOD PUBLIC LOGICAL FromJSON(INPUT json AS LONGCHAR):
+ DEFINE VARIABLE res AS LOGICAL NO-UNDO.
+ IF VALID-HANDLE(ttQuery) THEN DELETE OBJECT ttQuery.
+ IF VALID-HANDLE(ttData) THEN DELETE OBJECT ttData.
+ CREATE TEMP-TABLE ttData.
+ res = ttData:READ-JSON("LONGCHAR", json, "EMPTY").
+ CountSize().
+ RETURN res.
+ END METHOD.
+
+ METHOD PUBLIC LOGICAL FromJSONFile(INPUT path AS CHARACTER):
+ DEFINE VARIABLE res AS LOGICAL NO-UNDO.
+ IF VALID-HANDLE(ttQuery) THEN DELETE OBJECT ttQuery.
+ IF VALID-HANDLE(ttData) THEN DELETE OBJECT ttData.
+ CREATE TEMP-TABLE ttData.
+ res = ttData:READ-JSON("FILE", path, "EMPTY").
+ CountSize().
+ RETURN res.
+ END METHOD.
+
+ METHOD PUBLIC LOGICAL FromXML(INPUT xml AS LONGCHAR):
+ DEFINE VARIABLE res AS LOGICAL NO-UNDO.
+ IF VALID-HANDLE(ttQuery) THEN DELETE OBJECT ttQuery.
+ IF VALID-HANDLE(ttData) THEN DELETE OBJECT ttData.
+ CREATE TEMP-TABLE ttData.
+ res = ttData:READ-XML("LONGCHAR", xml, "EMPTY", ?, ?).
+ CountSize().
+ RETURN res.
+ END METHOD.
+
+ METHOD PUBLIC LOGICAL FromXMLFile(INPUT path AS CHARACTER):
+ DEFINE VARIABLE res AS LOGICAL NO-UNDO.
+ IF VALID-HANDLE(ttQuery) THEN DELETE OBJECT ttQuery.
+ IF VALID-HANDLE(ttData) THEN DELETE OBJECT ttData.
+ CREATE TEMP-TABLE ttData.
+ res = ttData:READ-XML("FILE", path, "EMPTY", ?, ?).
+ CountSize().
+ RETURN res.
+ END METHOD.
+
+ METHOD PUBLIC LOGICAL FromTempTable(INPUT ttSrc AS HANDLE):
+ DEFINE VARIABLE res AS LOGICAL NO-UNDO.
+ IF VALID-HANDLE(ttQuery) THEN DELETE OBJECT ttQuery.
+ IF VALID-HANDLE(ttData) THEN DELETE OBJECT ttData.
+ CREATE TEMP-TABLE ttData.
+ res = ttData:COPY-TEMP-TABLE(ttSrc).
+ CountSize().
+ RETURN res.
+ END METHOD.
+
+ METHOD PUBLIC Progress.Lang.ParameterList GetParameterList():
+ DEFINE VARIABLE numParams AS INTEGER NO-UNDO.
+ DEFINE VARIABLE bufHandle AS HANDLE NO-UNDO.
+ DEFINE VARIABLE i AS INTEGER NO-UNDO.
+ DEFINE VARIABLE fieldHandle AS HANDLE NO-UNDO.
+ DEFINE VARIABLE params AS Progress.Lang.ParameterList NO-UNDO.
+ IF NOT VALID-HANDLE(ttData) THEN RETURN ERROR NEW DataProviderError("Data has not been loaded into DataProvider").
+ IF NOT VALID-HANDLE(ttQuery) OR ttQuery:IS-OPEN = FALSE THEN ResetQuery().
+ IF ttQuery:QUERY-OFF-END = FALSE THEN
+ DO:
+ bufHandle = ttQuery:GET-BUFFER-HANDLE(1).
+ IF NOT bufHandle:AVAILABLE THEN MoveFirst().
+ numParams = bufHandle:NUM-FIELDS.
+ params = NEW Progress.Lang.ParameterList(numParams).
+ DO i = 1 TO numParams:
+ fieldHandle = bufHandle:BUFFER-FIELD(i).
+ params:SetParameter(i, fieldHandle:DATA-TYPE, "INPUT", fieldHandle:BUFFER-VALUE()).
+ END.
+ END.
+ RETURN params.
+ END METHOD.
+
+ METHOD PROTECTED VOID ResetQuery():
+ DEFINE VARIABLE qry AS CHARACTER NO-UNDO.
+ IF NOT VALID-HANDLE(ttData) THEN RETURN.
+ IF VALID-HANDLE(ttQuery) THEN
+ DO:
+ ttQuery:QUERY-CLOSE().
+ DELETE OBJECT ttQuery.
+ END.
+ qry = "FOR EACH " + ttData:NAME.
+ CREATE QUERY ttQuery.
+ ttQuery:SET-BUFFERS(ttData:DEFAULT-BUFFER-HANDLE).
+ IF ttQuery:QUERY-PREPARE(qry) THEN ttQuery:QUERY-OPEN().
+ ELSE IF VALID-HANDLE(ttQuery) THEN DELETE OBJECT ttQuery.
+ END METHOD.
+
+ METHOD PUBLIC LOGICAL MoveFirst():
+ DEFINE VARIABLE res AS LOGICAL NO-UNDO INITIAL FALSE.
+ IF NOT VALID-HANDLE(ttQuery) THEN
+ ResetQuery().
+ IF VALID-HANDLE(ttQuery) THEN
+ res = ttQuery:GET-FIRST(NO-LOCK).
+ RETURN res.
+ END METHOD.
+
+ METHOD PUBLIC LOGICAL MoveLast():
+ DEFINE VARIABLE res AS LOGICAL NO-UNDO INITIAL FALSE.
+ IF NOT VALID-HANDLE(ttQuery) THEN
+ ResetQuery().
+ IF VALID-HANDLE(ttQuery) THEN
+ res = ttQuery:GET-LAST(NO-LOCK).
+ RETURN res.
+ END METHOD.
+
+ METHOD PUBLIC LOGICAL MoveNext():
+ DEFINE VARIABLE res AS LOGICAL NO-UNDO INITIAL FALSE.
+ IF NOT VALID-HANDLE(ttQuery) THEN
+ ResetQuery().
+ IF VALID-HANDLE(ttQuery) THEN
+ res = ttQuery:GET-NEXT(NO-LOCK).
+ RETURN res.
+ END METHOD.
+
+ METHOD PUBLIC LOGICAL MovePrev():
+ DEFINE VARIABLE res AS LOGICAL NO-UNDO INITIAL FALSE.
+ IF NOT VALID-HANDLE(ttQuery) THEN
+ ResetQuery().
+ IF VALID-HANDLE(ttQuery) THEN
+ res = ttQuery:GET-PREV(NO-LOCK).
+ RETURN res.
+ END METHOD.
+
+END CLASS.
\ No newline at end of file
diff --git a/src/OEUnit/Data/DataProviderError.cls b/src/OEUnit/Data/DataProviderError.cls
new file mode 100644
index 0000000..762ff99
--- /dev/null
+++ b/src/OEUnit/Data/DataProviderError.cls
@@ -0,0 +1,18 @@
+/*------------------------------------------------------------------------------
+ File : DataProvider.cls
+ Package : OEUnit.Data
+ Description : The exception thrown when DataProvider setup is not complete.
+------------------------------------------------------------------------------*/
+
+ROUTINE-LEVEL ON ERROR UNDO, THROW.
+
+CLASS OEUnit.Data.DataProviderError INHERITS Progress.Lang.AppError:
+
+ /*----------------------------------------------------------------------------
+ Constructor. Accepts an error message.
+ ----------------------------------------------------------------------------*/
+ CONSTRUCTOR PUBLIC DataProviderError(INPUT errorMessage AS CHARACTER):
+ SUPER(errorMessage, 0).
+ END CONSTRUCTOR.
+
+END CLASS.
\ No newline at end of file
diff --git a/src/OEUnit/Data/Fixture.cls b/src/OEUnit/Data/Fixture.cls
new file mode 100644
index 0000000..bf0dd84
--- /dev/null
+++ b/src/OEUnit/Data/Fixture.cls
@@ -0,0 +1,171 @@
+/*------------------------------------------------------------------------------
+ File : Fixture.cls
+ Package : OEUnit.Data
+ Description : Stores and uses data provided in associated methods when
+ calling a test case to pre-load database tables for a test.
+------------------------------------------------------------------------------*/
+USING Progress.Lang.*.
+USING OEUnit.Data.*.
+USING OEUnit.Util.*.
+
+ROUTINE-LEVEL ON ERROR UNDO, THROW.
+
+CLASS OEUnit.Data.Fixture:
+
+ DEFINE PROTECTED VARIABLE Fixtures AS List NO-UNDO.
+
+ /*----------------------------------------------------------------------------
+ Read-only Property that returns the number of top level buffers in dataset
+ ----------------------------------------------------------------------------*/
+ DEFINE PUBLIC PROPERTY TableCount AS INTEGER NO-UNDO
+ GET ():
+ DEFINE VARIABLE tCount AS INTEGER NO-UNDO INITIAL 0.
+ DEFINE VARIABLE res AS LOGICAL NO-UNDO.
+ DEFINE VARIABLE fix AS FixtureDataSet NO-UNDO.
+ IF VALID-OBJECT(Fixtures) THEN
+ DO:
+ res = Fixtures:MoveFirst().
+ DO WHILE res = TRUE
+ ON ERROR UNDO, THROW:
+ fix = CAST(Fixtures:CURRENT, "OEUnit.Data.FixtureDataSet").
+ tCount = tCount + fix:TableCount.
+ res = Fixtures:MoveNext().
+ END.
+ RETURN tCount.
+ END.
+ RETURN 0.
+ END GET.
+
+ /*----------------------------------------------------------------------------
+ The number of elements in the list
+ ----------------------------------------------------------------------------*/
+ DEFINE PUBLIC PROPERTY Size AS INTEGER NO-UNDO INIT 0
+ GET ():
+ IF VALID-OBJECT(Fixtures) THEN RETURN Fixtures:Size.
+ ELSE RETURN 0.
+ END GET.
+ PRIVATE SET.
+
+ CONSTRUCTOR PUBLIC Fixture():
+ SUPER ().
+ Fixtures = NEW List(TRUE). /* TRUE => destory list objects on destruction */
+ END CONSTRUCTOR.
+
+ DESTRUCTOR PUBLIC Fixture():
+ IF VALID-OBJECT(Fixtures) THEN DELETE OBJECT Fixtures.
+ END DESTRUCTOR.
+
+ /*----------------------------------------------------------------------------
+ Import fixture data from dataset encoded in JSON via parameter
+ ----------------------------------------------------------------------------*/
+ METHOD PUBLIC LOGICAL FromJSON(INPUT json AS LONGCHAR):
+ DEFINE VARIABLE fix AS FixtureDataSet NO-UNDO.
+ DEFINE VARIABLE res AS LOGICAL NO-UNDO.
+ fix = NEW FixtureDataSet().
+ res = fix:FromJSON(INPUT json).
+ IF res = TRUE THEN
+ Fixtures:Add(fix).
+ ELSE
+ IF VALID-OBJECT(fix) THEN
+ DELETE OBJECT fix.
+ RETURN res.
+ END METHOD.
+
+ /*----------------------------------------------------------------------------
+ Import fixture data from data encoded in JSON in given file
+ ----------------------------------------------------------------------------*/
+ METHOD PUBLIC LOGICAL FromJSONFile(INPUT path AS CHARACTER):
+ DEFINE VARIABLE fix AS FixtureDataSet NO-UNDO.
+ DEFINE VARIABLE res AS LOGICAL NO-UNDO.
+ fix = NEW FixtureDataSet().
+ res = fix:FromJSONFile(INPUT path).
+ IF res = TRUE THEN
+ Fixtures:Add(fix).
+ ELSE
+ IF VALID-OBJECT(fix) THEN
+ DELETE OBJECT fix.
+ RETURN res.
+ END METHOD.
+
+ /*----------------------------------------------------------------------------
+ Import fixture data from dataset encoded in XML via parameter
+ ----------------------------------------------------------------------------*/
+ METHOD PUBLIC LOGICAL FromXML(INPUT xml AS LONGCHAR):
+ DEFINE VARIABLE fix AS FixtureDataSet NO-UNDO.
+ DEFINE VARIABLE res AS LOGICAL NO-UNDO.
+ fix = NEW FixtureDataSet().
+ res = fix:FromXML(INPUT xml).
+ IF res = TRUE THEN
+ Fixtures:Add(fix).
+ ELSE
+ IF VALID-OBJECT(fix) THEN
+ DELETE OBJECT fix.
+ RETURN res.
+ END METHOD.
+
+ /*----------------------------------------------------------------------------
+ Import fixture data from data encoded in XML in given file
+ ----------------------------------------------------------------------------*/
+ METHOD PUBLIC LOGICAL FromXMLFile(INPUT path AS CHARACTER):
+ DEFINE VARIABLE fix AS FixtureDataSet NO-UNDO.
+ DEFINE VARIABLE res AS LOGICAL NO-UNDO.
+ fix = NEW FixtureDataSet().
+ res = fix:FromXMLFile(INPUT path).
+ IF res = TRUE THEN
+ Fixtures:Add(fix).
+ ELSE
+ IF VALID-OBJECT(fix) THEN
+ DELETE OBJECT fix.
+ RETURN res.
+ END METHOD.
+
+ /*----------------------------------------------------------------------------
+ Import fixture data by copying from an existing dataset
+ ----------------------------------------------------------------------------*/
+ METHOD PUBLIC LOGICAL FromDataSet(INPUT dsSrc AS HANDLE):
+ DEFINE VARIABLE fix AS FixtureDataSet NO-UNDO.
+ DEFINE VARIABLE res AS LOGICAL NO-UNDO.
+ fix = NEW FixtureDataSet().
+ res = fix:FromDataSet(INPUT dsSrc).
+ IF res = TRUE THEN
+ Fixtures:Add(fix).
+ ELSE
+ IF VALID-OBJECT(fix) THEN
+ DELETE OBJECT fix.
+ RETURN res.
+ END METHOD.
+
+ /*----------------------------------------------------------------------------
+ Import fixture data by copying from a temp-table
+ ----------------------------------------------------------------------------*/
+ METHOD PUBLIC LOGICAL FromTempTable(INPUT ttSrc AS HANDLE):
+ DEFINE VARIABLE fix AS FixtureDataSet NO-UNDO.
+ DEFINE VARIABLE res AS LOGICAL NO-UNDO.
+ fix = NEW FixtureDataSet().
+ res = fix:FromTempTable(INPUT ttSrc).
+ IF res = TRUE THEN
+ Fixtures:Add(fix).
+ ELSE
+ IF VALID-OBJECT(fix) THEN
+ DELETE OBJECT fix.
+ RETURN res.
+ END METHOD.
+
+ /*----------------------------------------------------------------------------
+ Create data in the attached databases, based on data in dataset tables.
+ ----------------------------------------------------------------------------*/
+ METHOD PUBLIC LOGICAL CreateData():
+ DEFINE VARIABLE fix AS FixtureDataSet NO-UNDO.
+ DEFINE VARIABLE res AS LOGICAL NO-UNDO.
+ IF NOT VALID-OBJECT(Fixtures) THEN RETURN FALSE.
+ res = fixtures:MoveFirst().
+ DO WHILE res = TRUE
+ ON ERROR UNDO, THROW:
+ fix = CAST(fixtures:CURRENT, "OEUnit.Data.FixtureDataSet").
+ IF VALID-OBJECT(fix) THEN
+ fix:CreateData().
+ res = fixtures:MoveNext().
+ END.
+ END METHOD.
+
+END CLASS.
\ No newline at end of file
diff --git a/src/OEUnit/Data/FixtureCreateError.cls b/src/OEUnit/Data/FixtureCreateError.cls
new file mode 100644
index 0000000..465a7c4
--- /dev/null
+++ b/src/OEUnit/Data/FixtureCreateError.cls
@@ -0,0 +1,18 @@
+/*------------------------------------------------------------------------------
+ File : FixtureCreateError.cls
+ Package : OEUnit.Data
+ Description : The exception thrown when Fixture encounters a problem.
+------------------------------------------------------------------------------*/
+
+ROUTINE-LEVEL ON ERROR UNDO, THROW.
+
+CLASS OEUnit.Data.FixtureCreateError INHERITS Progress.Lang.AppError:
+
+ /*----------------------------------------------------------------------------
+ Constructor. Accepts an error message.
+ ----------------------------------------------------------------------------*/
+ CONSTRUCTOR PUBLIC FixtureCreateError(INPUT errorMessage AS CHARACTER):
+ SUPER(SUBSTITUTE("Fixture Error: &1",errorMessage), 0).
+ END CONSTRUCTOR.
+
+END CLASS.
\ No newline at end of file
diff --git a/src/OEUnit/Data/FixtureDataSet.cls b/src/OEUnit/Data/FixtureDataSet.cls
new file mode 100644
index 0000000..ad4698f
--- /dev/null
+++ b/src/OEUnit/Data/FixtureDataSet.cls
@@ -0,0 +1,157 @@
+/*------------------------------------------------------------------------------
+ File : FixtureDataSet.cls
+ Package : OEUnit.Data
+ Description : Stores and uses data provided in associated methods when
+ calling a test case to pre-load database tables for a test.
+------------------------------------------------------------------------------*/
+USING Progress.Lang.*.
+USING OEUnit.Data.*.
+
+ROUTINE-LEVEL ON ERROR UNDO, THROW.
+
+CLASS OEUnit.Data.FixtureDataSet:
+
+ DEFINE PROTECTED VARIABLE dsData AS HANDLE NO-UNDO.
+
+ /*----------------------------------------------------------------------------
+ Read-only Property that returns the number of top level buffers in dataset
+ ----------------------------------------------------------------------------*/
+ DEFINE PUBLIC PROPERTY TableCount AS INTEGER NO-UNDO
+ GET ():
+ IF VALID-HANDLE(dsData) THEN RETURN dsData:NUM-TOP-BUFFERS.
+ ELSE RETURN 0.
+ END GET.
+
+ CONSTRUCTOR PUBLIC FixtureDataSet():
+ SUPER ().
+ END CONSTRUCTOR.
+
+ DESTRUCTOR PUBLIC FixtureDataSet():
+ IF VALID-HANDLE(dsData) THEN DELETE OBJECT dsData.
+ END DESTRUCTOR.
+
+ /*----------------------------------------------------------------------------
+ Import fixture data from dataset encoded in JSON via parameter
+ ----------------------------------------------------------------------------*/
+ METHOD PUBLIC LOGICAL FromJSON(INPUT json AS LONGCHAR):
+ DEFINE VARIABLE res AS LOGICAL NO-UNDO.
+ IF VALID-HANDLE(dsData) THEN DELETE OBJECT dsData.
+ CREATE DATASET dsData.
+ res = dsData:READ-JSON("LONGCHAR", json, "EMPTY").
+ RETURN res.
+ END METHOD.
+
+ /*----------------------------------------------------------------------------
+ Import fixture data from data encoded in JSON in given file
+ ----------------------------------------------------------------------------*/
+ METHOD PUBLIC LOGICAL FromJSONFile(INPUT path AS CHARACTER):
+ DEFINE VARIABLE res AS LOGICAL NO-UNDO.
+ IF VALID-HANDLE(dsData) THEN DELETE OBJECT dsData.
+ CREATE DATASET dsData.
+ res = dsData:READ-JSON("FILE", path, "EMPTY").
+ RETURN res.
+ END METHOD.
+
+ /*----------------------------------------------------------------------------
+ Import fixture data from dataset encoded in XML via parameter
+ ----------------------------------------------------------------------------*/
+ METHOD PUBLIC LOGICAL FromXML(INPUT xml AS LONGCHAR):
+ DEFINE VARIABLE res AS LOGICAL NO-UNDO.
+ IF VALID-HANDLE(dsData) THEN DELETE OBJECT dsData.
+ CREATE DATASET dsData.
+ res = dsData:READ-XML("LONGCHAR", xml, "EMPTY", ?, ?).
+ RETURN res.
+ END METHOD.
+
+ /*----------------------------------------------------------------------------
+ Import fixture data from data encoded in XML in given file
+ ----------------------------------------------------------------------------*/
+ METHOD PUBLIC LOGICAL FromXMLFile(INPUT path AS CHARACTER):
+ DEFINE VARIABLE res AS LOGICAL NO-UNDO.
+ IF VALID-HANDLE(dsData) THEN DELETE OBJECT dsData.
+ CREATE DATASET dsData.
+ res = dsData:READ-XML("FILE", path, "EMPTY", ?, ?).
+ RETURN res.
+ END METHOD.
+
+ /*----------------------------------------------------------------------------
+ Import fixture data by copying from an existing dataset
+ ----------------------------------------------------------------------------*/
+ METHOD PUBLIC LOGICAL FromDataSet(INPUT dsSrc AS HANDLE):
+ DEFINE VARIABLE res AS LOGICAL NO-UNDO.
+ IF VALID-HANDLE(dsData) THEN DELETE OBJECT dsData.
+ CREATE DATASET dsData.
+ res = dsData:COPY-DATASET(dsSrc).
+ RETURN res.
+ END METHOD.
+
+ /*----------------------------------------------------------------------------
+ Import fixture data by copying from a temp-table
+ ----------------------------------------------------------------------------*/
+ METHOD PUBLIC LOGICAL FromTempTable(INPUT ttSrc AS HANDLE):
+ DEFINE VARIABLE res AS LOGICAL NO-UNDO.
+ DEFINE VARIABLE ttData AS HANDLE NO-UNDO.
+ DEFINE VARIABLE ttDataBuf AS HANDLE NO-UNDO.
+ IF VALID-HANDLE(dsData) THEN DELETE OBJECT dsData.
+ CREATE DATASET dsData.
+ IF NOT VALID-HANDLE(dsData) THEN RETURN FALSE.
+ CREATE TEMP-TABLE ttData.
+ /* COPY-TEMP-TABLE will also copy data as well, and hence there
+ * is no need to configure data sources and call DataSet:Fill()
+ */
+ res = ttData:COPY-TEMP-TABLE(ttSrc,FALSE,FALSE,FALSE,"").
+ IF res = FALSE THEN
+ DO:
+ IF VALID-HANDLE(dsData) THEN DELETE OBJECT dsData.
+ IF VALID-HANDLE(ttData) THEN DELETE OBJECT ttData.
+ RETURN res.
+ END.
+ ttDataBuf = ttData:DEFAULT-BUFFER-HANDLE.
+ dsData:ADD-BUFFER(ttDataBuf).
+ RETURN TRUE.
+ END METHOD.
+
+ /*----------------------------------------------------------------------------
+ Create data in the attached databases, based on data in dataset tables.
+ ----------------------------------------------------------------------------*/
+ METHOD PUBLIC LOGICAL CreateData():
+ DEFINE VARIABLE hBuffer AS HANDLE NO-UNDO.
+ DEFINE VARIABLE hQuery AS HANDLE NO-UNDO.
+ DEFINE VARIABLE hQBuffer AS HANDLE NO-UNDO.
+ DEFINE VARIABLE hRBuffer AS HANDLE NO-UNDO.
+ DEFINE VARIABLE iCount AS INTEGER NO-UNDO.
+
+ /* Ensure that any errors are thrown properly */
+ DO ON ERROR UNDO, THROW:
+ IF NOT VALID-HANDLE(dsData) THEN RETURN FALSE.
+ DO iCount = 1 TO dsData:NUM-TOP-BUFFERS
+ ON ERROR UNDO, THROW:
+ hBuffer = dsData:GET-TOP-BUFFER(iCount).
+ IF NOT VALID-HANDLE(hBuffer) THEN RETURN ERROR NEW FixtureError("Failed to fetch buffer from DataSet").
+ IF VALID-HANDLE(hQuery) THEN DELETE OBJECT hQuery.
+ IF VALID-HANDLE(hQBuffer) THEN DELETE OBJECT hQBuffer.
+ CREATE BUFFER hQBuffer FOR TABLE hBuffer.
+ CREATE BUFFER hRBuffer FOR TABLE hBuffer:NAME NO-ERROR.
+ IF ERROR-STATUS:ERROR THEN RETURN ERROR NEW FixtureError(SUBSTITUTE("Could not create buffer for destination table &1.", hBuffer:NAME)).
+ CREATE QUERY hQuery.
+ hQuery:SET-BUFFERS(hQBuffer). /* NOTE: Because this is taken from Dataset, names of tables can overlap and doesn't cause a problem */
+ IF(hQuery:QUERY-PREPARE("FOR EACH " + hBuffer:NAME + " NO-LOCK:") = FALSE) THEN
+ RETURN ERROR NEW FixtureError(SUBSTITUTE("Could not query data in source &1.", hBuffer:NAME)).
+ hQuery:QUERY-OPEN().
+ hQuery:GET-FIRST(NO-LOCK).
+ DO WHILE NOT hQuery:QUERY-OFF-END
+ ON ERROR UNDO, THROW:
+ hRBuffer:BUFFER-CREATE().
+ hRBuffer:BUFFER-COPY(hQBuffer).
+ hRBuffer:BUFFER-RELEASE().
+ hQuery:GET-NEXT().
+ END.
+ hQuery:QUERY-CLOSE().
+ IF VALID-HANDLE(hRBuffer) THEN DELETE OBJECT hRBuffer.
+ IF VALID-HANDLE(hQBuffer) THEN DELETE OBJECT hQBuffer.
+ IF VALID-HANDLE(hQuery) THEN DELETE OBJECT hQuery.
+ END.
+ END.
+ END METHOD.
+
+END CLASS.
\ No newline at end of file
diff --git a/src/OEUnit/Data/FixtureError.cls b/src/OEUnit/Data/FixtureError.cls
new file mode 100644
index 0000000..a07d447
--- /dev/null
+++ b/src/OEUnit/Data/FixtureError.cls
@@ -0,0 +1,18 @@
+/*------------------------------------------------------------------------------
+ File : FixtureError.cls
+ Package : OEUnit.Data
+ Description : The exception thrown when Fixture encounters a problem.
+------------------------------------------------------------------------------*/
+
+ROUTINE-LEVEL ON ERROR UNDO, THROW.
+
+CLASS OEUnit.Data.FixtureError INHERITS Progress.Lang.AppError:
+
+ /*----------------------------------------------------------------------------
+ Constructor. Accepts an error message.
+ ----------------------------------------------------------------------------*/
+ CONSTRUCTOR PUBLIC FixtureError(INPUT errorMessage AS CHARACTER):
+ SUPER(errorMessage, 0).
+ END CONSTRUCTOR.
+
+END CLASS.
\ No newline at end of file
diff --git a/src/OEUnit/Reflection/AnnotationInfo.cls b/src/OEUnit/Reflection/AnnotationInfo.cls
index dc46771..c1b4505 100644
--- a/src/OEUnit/Reflection/AnnotationInfo.cls
+++ b/src/OEUnit/Reflection/AnnotationInfo.cls
@@ -10,6 +10,8 @@
ROUTINE-LEVEL ON ERROR UNDO, THROW.
+USING OEUnit.Util.NamedList.
+
CLASS OEUnit.Reflection.AnnotationInfo:
/*----------------------------------------------------------------------------
@@ -17,13 +19,20 @@ CLASS OEUnit.Reflection.AnnotationInfo:
AnnotationType(AttributeName=AttributeValue)
----------------------------------------------------------------------------*/
DEFINE PUBLIC PROPERTY AnnotationType AS CHARACTER NO-UNDO GET. PRIVATE SET.
- DEFINE PUBLIC PROPERTY AttributeName AS CHARACTER NO-UNDO GET. PRIVATE SET.
- DEFINE PUBLIC PROPERTY AttributeValue AS CHARACTER NO-UNDO GET. PRIVATE SET.
+
+ /*----------------------------------------------------------------------------
+ Annotation attributes. Defined by the syntax below:
+ AnnotationType(AttributeName=AttributeValue,AttributeName=AttributeValue)
+ ----------------------------------------------------------------------------*/
+ DEFINE PUBLIC PROPERTY Attributes AS NamedList NO-UNDO
+ GET.
+ PROTECTED SET.
/*----------------------------------------------------------------------------
Constructor. Accepts the annotation string. Some input examples:
Test
Test(expected=Progress.Lang.Error)
+ Test(expected=Progress.Lang.Error,dataProvider=GenerateTestMatrix)
Ignore
----------------------------------------------------------------------------*/
CONSTRUCTOR PUBLIC AnnotationInfo(INPUT annotationString AS CHARACTER):
@@ -32,8 +41,43 @@ CLASS OEUnit.Reflection.AnnotationInfo:
ASSIGN
AnnotationType = ENTRY(1, annotationString, "(")
attribute = TRIM(SUBSTRING(annotationString, LENGTH(AnnotationType) + 1),"()")
- AttributeName = ENTRY(1, attribute, "=")
- AttributeValue = SUBSTRING(attribute, LENGTH(AttributeName) + 2).
+
+ Attributes = NEW NamedList().
+ IF attribute NE "" AND attribute NE ? THEN ParseAttributes(attribute).
END CONSTRUCTOR.
+
+ DESTRUCTOR AnnotationInfo():
+ DELETE OBJECT Attributes NO-ERROR.
+ END DESTRUCTOR.
+
+ METHOD PROTECTED VOID ParseAttributes(INPUT attribs AS CHARACTER):
+
+ DEFINE VARIABLE startPos AS INTEGER NO-UNDO INITIAL 1.
+ DEFINE VARIABLE endPos AS INTEGER NO-UNDO.
+ DEFINE VARIABLE attribName AS CHARACTER NO-UNDO.
+ DEFINE VARIABLE attribValue AS CHARACTER NO-UNDO.
+
+ attribLoop:
+ DO WHILE startPos < LENGTH(attribs):
+ endPos = INDEX(attribs,"=", startPos).
+ IF(endPos = 0) THEN LEAVE attribLoop.
+ attribName = TRIM(SUBSTRING(attribs, startPos, endPos - startPos)).
+ startPos = endPos + 1.
+ endPos = INDEX(attribs, ",", startPos).
+ IF(endPos = 0) THEN endPos = LENGTH(attribs) + 1.
+ attribValue = TRIM(SUBSTRING(attribs, startPos, endPos - startPos)).
+ startPos = endPos + 1.
+ Attributes:Add(attribName, attribValue).
+ END.
+ END METHOD.
+
+ METHOD PUBLIC LOGICAL HasAttribute(INPUT attribName AS CHARACTER):
+ RETURN Attributes:HasValue(INPUT attribName).
+ END METHOD.
+
+ METHOD PUBLIC CHARACTER GetAttribute(INPUT attribName AS CHARACTER):
+ IF HasAttribute(attribName) = TRUE THEN RETURN Attributes:Get(attribName).
+ ELSE RETURN ?.
+ END METHOD.
END CLASS.
\ No newline at end of file
diff --git a/src/OEUnit/Reflection/MethodInfo.cls b/src/OEUnit/Reflection/MethodInfo.cls
index 92461cf..c5ca24c 100644
--- a/src/OEUnit/Reflection/MethodInfo.cls
+++ b/src/OEUnit/Reflection/MethodInfo.cls
@@ -9,6 +9,8 @@ ROUTINE-LEVEL ON ERROR UNDO, THROW.
USING OEUnit.Reflection.*.
USING OEUnit.Util.*.
+USING OEUnit.Runners.*.
+USING OEUnit.Data.*.
CLASS OEUnit.Reflection.MethodInfo INHERITS StatementInfo:
@@ -59,5 +61,162 @@ CLASS OEUnit.Reflection.MethodInfo INHERITS StatementInfo:
END METHOD.
+ /*----------------------------------------------------------------------------
+ Invoke the method being represented, with a parameter list. The method being
+ called must be a public method accepting the same parameters as are listed
+ in params. A StopConditionError will be thrown if the method cannot be found.
+ ----------------------------------------------------------------------------*/
+ METHOD PUBLIC VOID Invoke(INPUT params AS Progress.Lang.ParameterList):
+ DEFINE VARIABLE classInf AS Progress.Lang.Class NO-UNDO.
+ DO ON ERROR UNDO, THROW
+ ON QUIT UNDO, RETURN ERROR NEW QuitConditionError("Quit condition occured")
+ ON STOP UNDO, LEAVE:
+ classInf = Progress.Lang.Class:GetClass(parentInfo:Name).
+ IF IsStatic THEN
+ classInf:Invoke(Name, params).
+ ELSE
+ classInf:Invoke(parentInfo:ClassInstance, Name, params).
+
+ RETURN.
+ END.
+
+ /* HACK: To accomodate bug in OpenEdge runtime, crashes when returning custom
+ error instances on stop conditions */
+ RETURN ERROR NEW StopConditionError("Stop condition occured").
+ END METHOD.
+
+ /*----------------------------------------------------------------------------
+ Returns another MethodInfo object which holds the information regarding the
+ data provider method for this MethodInfo.
+ ----------------------------------------------------------------------------*/
+ METHOD PUBLIC MethodInfo GetDataProviderInfo():
+ RETURN GetAssociatedMethodInfo(INPUT OEUnitAnnotations:TestDataProviderAttribute,
+ INPUT OEUnitAnnotations:DataProvider,
+ INPUT OEUnitAnnotations:DataProviderNameAttribute).
+ END METHOD.
+
+ /*----------------------------------------------------------------------------
+ Determines whether this method has a @Test annotation with a DataProvider
+ attribute, and a method with a matching @DataProvider attribute exists.
+ ----------------------------------------------------------------------------*/
+ METHOD PUBLIC LOGICAL RequiresDataProvider():
+ RETURN (GetDataProviderInfo() NE ?).
+ END METHOD.
+
+ /*----------------------------------------------------------------------------
+ Returns a DataProvider object from the invocation of a DataProvider method.
+ ----------------------------------------------------------------------------*/
+ METHOD PUBLIC DataProvider GetDataProvider():
+ DEFINE VARIABLE dataProvMethod AS MethodInfo NO-UNDO.
+ DEFINE VARIABLE dataProvider AS DataProvider NO-UNDO.
+ DO ON ERROR UNDO, THROW
+ ON QUIT UNDO, RETURN ERROR NEW QuitConditionError("Quit condition occured")
+ ON STOP UNDO, LEAVE:
+ dataProvMethod = GetDataProviderInfo().
+ IF dataProvMethod NE ? AND VALID-OBJECT(dataProvMethod) THEN
+ DO:
+ IF dataProvMethod:IsStatic THEN
+ dataProvider = DYNAMIC-INVOKE(dataProvMethod:ParentInfo:Name, dataProvMethod:Name).
+ ELSE
+ dataProvider = DYNAMIC-INVOKE(dataProvMethod:ParentInfo:ClassInstance, dataProvMethod:Name).
+ END.
+
+ RETURN dataProvider.
+ END.
+
+ /* HACK: To accomodate bug in OpenEdge runtime, crashes when returning custom
+ error instances on stop conditions */
+ RETURN ERROR NEW StopConditionError("Stop condition occured").
+ END METHOD.
+
+ /*----------------------------------------------------------------------------
+ Returns another MethodInfo object which holds the information regarding the
+ fixture method for this MethodInfo.
+ ----------------------------------------------------------------------------*/
+ METHOD PUBLIC MethodInfo GetFixtureInfo():
+ RETURN GetAssociatedMethodInfo(INPUT OEUnitAnnotations:TestFixtureAttribute,
+ INPUT OEUnitAnnotations:Fixture,
+ INPUT OEUnitAnnotations:FixtureNameAttribute).
+ END METHOD.
+
+ /*----------------------------------------------------------------------------
+ Determines whether this method has a @Test annotation with a Fixture
+ attribute, and a method with a matching @Fixture attribute exists.
+ ----------------------------------------------------------------------------*/
+ METHOD PUBLIC LOGICAL RequiresFixture():
+ RETURN (GetFixtureInfo() NE ?).
+ END METHOD.
+
+ /*----------------------------------------------------------------------------
+ Returns a Fixture object from the invocation of a Fixture method.
+ ----------------------------------------------------------------------------*/
+ METHOD PUBLIC Fixture GetFixture():
+ DEFINE VARIABLE fixtureMethod AS MethodInfo NO-UNDO.
+ DEFINE VARIABLE Fixture AS Fixture NO-UNDO.
+ DO ON ERROR UNDO, THROW
+ ON QUIT UNDO, RETURN ERROR NEW QuitConditionError("Quit condition occured")
+ ON STOP UNDO, LEAVE:
+ fixtureMethod = GetFixtureInfo().
+ IF fixtureMethod NE ? AND VALID-OBJECT(fixtureMethod) THEN
+ DO:
+ IF fixtureMethod:IsStatic THEN
+ Fixture = DYNAMIC-INVOKE(fixtureMethod:ParentInfo:Name, fixtureMethod:Name).
+ ELSE
+ Fixture = DYNAMIC-INVOKE(fixtureMethod:ParentInfo:ClassInstance, fixtureMethod:Name).
+ END.
+
+ RETURN Fixture.
+ END.
+
+ /* HACK: To accomodate bug in OpenEdge runtime, crashes when returning custom
+ error instances on stop conditions */
+ RETURN ERROR NEW StopConditionError("Stop condition occured").
+ END METHOD.
+
+ /*----------------------------------------------------------------------------
+ Returns another MethodInfo object for a method indicated in the attributes
+ in this MethodInfo.
+ ----------------------------------------------------------------------------*/
+ METHOD PROTECTED MethodInfo GetAssociatedMethodInfo(INPUT methodAttribute AS CHARACTER,
+ INPUT associatedAnnotation AS CHARACTER,
+ INPUT associatedAttribute AS CHARACTER):
+
+ DEFINE VARIABLE TestAnnotation AS AnnotationInfo NO-UNDO.
+ DEFINE VARIABLE MethodAnnotation AS AnnotationInfo NO-UNDO.
+ DEFINE VARIABLE AnnotatedMethods AS List NO-UNDO.
+ DEFINE VARIABLE AssociatedMethodName AS CHARACTER NO-UNDO.
+ DEFINE VARIABLE AssociatedMethodInfo AS MethodInfo NO-UNDO.
+ DEFINE VARIABLE result AS LOGICAL NO-UNDO INITIAL FALSE.
+
+ /* If this is a @Test, with a "Fixture=" attribute, then attempt to
+ * find the data provider method
+ */
+ TestAnnotation = GetAnnotationOfType(OEUnitAnnotations:Test).
+
+ IF TestAnnotation NE ? AND TestAnnotation:HasAttribute(methodAttribute) THEN
+ DO:
+ AssociatedMethodName = TestAnnotation:Attributes:Get(methodAttribute).
+ AnnotatedMethods = parentInfo:GetAnnotatedMethods(associatedAnnotation).
+
+ /* Find first data provider with that name attribute or method name */
+ AnnotatedMethods:MoveFirst().
+ MethodLoop:
+ DO WHILE AnnotatedMethods:CURRENT NE ?:
+ AssociatedMethodInfo = CAST(AnnotatedMethods:CURRENT,"MethodInfo").
+ MethodAnnotation = AssociatedMethodInfo:GetAnnotationOfType(associatedAnnotation).
+ IF (MethodAnnotation:HasAttribute(associatedAttribute) = TRUE) THEN
+ DO:
+ IF AssociatedMethodName = MethodAnnotation:Attributes:Get(associatedAttribute) THEN
+ LEAVE MethodLoop.
+ END.
+ ELSE IF AssociatedMethodInfo:Name = AssociatedMethodName THEN LEAVE MethodLoop.
+ ELSE AssociatedMethodInfo = ?.
+ AnnotatedMethods:MoveNext().
+ END.
+ END.
+
+ RETURN AssociatedMethodInfo.
+ END METHOD.
+
END CLASS.
diff --git a/src/OEUnit/Runners/OEUnitAnnotations.cls b/src/OEUnit/Runners/OEUnitAnnotations.cls
index 699e3de..19a67b1 100644
--- a/src/OEUnit/Runners/OEUnitAnnotations.cls
+++ b/src/OEUnit/Runners/OEUnitAnnotations.cls
@@ -11,16 +11,30 @@ CLASS OEUnit.Runners.OEUnitAnnotations:
/*----------------------------------------------------------------------------
OEUnit Annotation Types - Read Only Properties
----------------------------------------------------------------------------*/
- DEFINE PUBLIC STATIC PROPERTY Test AS CHARACTER NO-UNDO INIT "Test" GET.
- DEFINE PUBLIC STATIC PROPERTY Ignore AS CHARACTER NO-UNDO INIT "Ignore" GET.
- DEFINE PUBLIC STATIC PROPERTY Before AS CHARACTER NO-UNDO INIT "Before" GET.
- DEFINE PUBLIC STATIC PROPERTY After AS CHARACTER NO-UNDO INIT "After" GET.
- DEFINE PUBLIC STATIC PROPERTY BeforeClass AS CHARACTER NO-UNDO INIT "BeforeClass" GET.
- DEFINE PUBLIC STATIC PROPERTY AfterClass AS CHARACTER NO-UNDO INIT "AfterClass" GET.
+ DEFINE PUBLIC STATIC PROPERTY Test AS CHARACTER NO-UNDO INIT "Test" GET.
+ DEFINE PUBLIC STATIC PROPERTY Ignore AS CHARACTER NO-UNDO INIT "Ignore" GET.
+ DEFINE PUBLIC STATIC PROPERTY Before AS CHARACTER NO-UNDO INIT "Before" GET.
+ DEFINE PUBLIC STATIC PROPERTY After AS CHARACTER NO-UNDO INIT "After" GET.
+ DEFINE PUBLIC STATIC PROPERTY BeforeClass AS CHARACTER NO-UNDO INIT "BeforeClass" GET.
+ DEFINE PUBLIC STATIC PROPERTY AfterClass AS CHARACTER NO-UNDO INIT "AfterClass" GET.
+ DEFINE PUBLIC STATIC PROPERTY DataProvider AS CHARACTER NO-UNDO INIT "DataProvider" GET.
+ DEFINE PUBLIC STATIC PROPERTY Fixture AS CHARACTER NO-UNDO INIT "Fixture" GET.
/*----------------------------------------------------------------------------
Expected Attribute for the Test Annotation.
----------------------------------------------------------------------------*/
- DEFINE PUBLIC STATIC PROPERTY TestExpectedAttribute AS CHARACTER NO-UNDO INIT "Expected" GET.
+ DEFINE PUBLIC STATIC PROPERTY TestExpectedAttribute AS CHARACTER NO-UNDO INIT "Expected" GET.
+ DEFINE PUBLIC STATIC PROPERTY TestDataProviderAttribute AS CHARACTER NO-UNDO INIT "DataProvider" GET.
+ DEFINE PUBLIC STATIC PROPERTY TestFixtureAttribute AS CHARACTER NO-UNDO INIT "Fixture" GET.
+
+ /*----------------------------------------------------------------------------
+ Expected Attribute for the DataProvider Annotation.
+ ----------------------------------------------------------------------------*/
+ DEFINE PUBLIC STATIC PROPERTY DataProviderNameAttribute AS CHARACTER NO-UNDO INIT "Name" GET.
+
+ /*----------------------------------------------------------------------------
+ Expected Attribute for the Fixture Annotation.
+ ----------------------------------------------------------------------------*/
+ DEFINE PUBLIC STATIC PROPERTY FixtureNameAttribute AS CHARACTER NO-UNDO INIT "Name" GET.
END CLASS.
\ No newline at end of file
diff --git a/src/OEUnit/Runners/OEUnitRunner.cls b/src/OEUnit/Runners/OEUnitRunner.cls
index bd0fa08..cda5cd8 100644
--- a/src/OEUnit/Runners/OEUnitRunner.cls
+++ b/src/OEUnit/Runners/OEUnitRunner.cls
@@ -17,6 +17,7 @@ USING OEUnit.Reflection.*.
USING OEUnit.Runner.*.
USING OEUnit.Runners.OEUnitAnnotations.
USING OEUnit.Util.*.
+USING OEUnit.Data.*.
USING Progress.Lang.*.
CLASS OEUnit.Runners.OEUnitRunner INHERITS BaseRunner:
@@ -158,35 +159,100 @@ CLASS OEUnit.Runners.OEUnitRunner INHERITS BaseRunner:
Run all of the test methods in the given list.
----------------------------------------------------------------------------*/
METHOD PROTECTED VOID RunTestMethods(INPUT testMethods AS List):
+ DEFINE VARIABLE meth AS MethodInfo NO-UNDO.
testMethods:Reset().
DO WHILE testMethods:MoveNext():
- RunTestMethod(CAST(testMethods:Current, MethodInfo)).
+ meth = CAST(testMethods:Current, MethodInfo).
+ IF meth:RequiresDataProvider() THEN
+ RunTestMethodWithDataProvider(meth).
+ ELSE
+ RunTestMethod(meth).
END.
FINALLY:
DELETE OBJECT testMethods NO-ERROR.
END FINALLY.
END METHOD.
+
+ /*----------------------------------------------------------------------------
+ Run a single test method multiple times using the given DataProvider.
+ ----------------------------------------------------------------------------*/
+ METHOD PROTECTED VOID RunTestMethodWithDataProvider(INPUT testMethod AS MethodInfo):
+
+ DEFINE VARIABLE dataProvider AS DataProvider NO-UNDO.
+ DEFINE VARIABLE moveResult AS LOGICAL NO-UNDO.
+ DEFINE VARIABLE counter AS INTEGER NO-UNDO INITIAL 0.
+ DEFINE VARIABLE methodResult AS TestMethodResult NO-UNDO.
+ DEFINE VARIABLE methodName AS CHARACTER NO-UNDO.
+ dataProvider = testMethod:GetDataProvider().
+ IF NOT VALID-OBJECT(dataProvider) OR dataProvider = ? THEN
+ RETURN ERROR NEW DataProviderError("Invalid DataProvider object returned from method.").
+ methodName = testMethod:NAME.
+ moveResult = dataProvider:MoveFirst().
+ DO WHILE moveResult EQ TRUE:
+ counter = counter + 1.
+ RunTestMethod(testMethod,dataProvider:GetParameterList()).
+ moveResult = dataProvider:MoveNext().
+ END.
+ END METHOD.
/*----------------------------------------------------------------------------
Run a single test method and store it's result. Any Progress.Lang.Errors
thrown during the run are caught and stored in the result.
----------------------------------------------------------------------------*/
METHOD PROTECTED VOID RunTestMethod(INPUT testMethod AS MethodInfo):
+ RunTestMethod(INPUT testMethod, INPUT ?).
+ END METHOD.
+
+ /*----------------------------------------------------------------------------
+ Run a single test method with the given parameters and store it's result. If
+ a test fixture is required, it will be extracted and a transaction started.
+ Any Progress.Lang.Errors thrown during the run are caught and stored in the
+ result.
+ ----------------------------------------------------------------------------*/
+ METHOD PROTECTED VOID RunTestMethod(INPUT testMethod AS MethodInfo, INPUT paramList AS Progress.Lang.ParameterList):
+
+ DEFINE VARIABLE fix AS Fixture NO-UNDO.
+
+ IF testMethod:RequiresFixture() AND ShouldRun(testMethod) THEN
+ DO:
+ OEUnitFixtureTransaction:
+ DO TRANSACTION ON ERROR UNDO, THROW:
+ fix = testMethod:GetFixture().
+ RunTestMethod(INPUT testMethod, INPUT fix, INPUT paramList).
+ UNDO OEUnitFixtureTransaction, LEAVE OEUnitFixtureTransaction.
+ END.
+ DELETE OBJECT fix NO-ERROR.
+ END.
+ ELSE
+ DO:
+ RunTestMethod(INPUT testMethod, INPUT ?, INPUT paramList).
+ END.
+ END METHOD.
+
+ /*----------------------------------------------------------------------------
+ Run a single test method with the given fixure object and parameters and
+ store it's result. If a fixture is provided, no transaction is started in
+ this method. Any Progress.Lang.Errors thrown during the run are caught and
+ stored in the result.
+ ----------------------------------------------------------------------------*/
+ METHOD PROTECTED VOID RunTestMethod(INPUT testMethod AS MethodInfo, INPUT testFixture AS Fixture, INPUT paramList AS Progress.Lang.ParameterList):
DEFINE VARIABLE methodResult AS TestMethodResult NO-UNDO.
methodResult = NEW TestMethodResult(testMethod).
currentResult:AddResult(methodResult).
IF ShouldRun(testMethod) THEN DO ON ERROR UNDO, THROW:
NotifyOfTestStart(testMethod).
+ IF testFixture NE ? AND VALID-OBJECT(testFixture) AND TRANSACTION THEN testFixture:CreateData().
ETIME(TRUE).
RunBefores(currentInfo:GetAnnotatedMethods(OEUnitAnnotations:Before)).
ETIME(TRUE).
- CallTestMethod(testMethod).
+ CallTestMethod(testMethod,paramList).
methodResult:SetStatus(TestResult:StatusPassed).
CATCH err AS Progress.Lang.Error :
methodResult:AddError(err).
END CATCH.
FINALLY:
+ IF VALID-OBJECT(testFixture) THEN DELETE OBJECT testFixture.
methodResult:SetDuration(ETIME).
RunAfters(currentInfo:GetAnnotatedMethods(OEUnitAnnotations:After), methodResult).
NotifyOfTestFinish(methodResult).
@@ -203,21 +269,33 @@ CLASS OEUnit.Runners.OEUnitRunner INHERITS BaseRunner:
during the test, the error is re-thrown to the caller.
----------------------------------------------------------------------------*/
METHOD PROTECTED VOID CallTestMethod(INPUT testMethod AS MethodInfo):
+ CallTestMethod(testMethod, ?).
+ END METHOD.
+
+ /*----------------------------------------------------------------------------
+ Invoke the given test method with the given parameter list. If an unexpected
+ Progress.Lang.Error is thrown during the test, the error is re-thrown to the
+ caller.
+ ----------------------------------------------------------------------------*/
+ METHOD PROTECTED VOID CallTestMethod(INPUT testMethod AS MethodInfo, INPUT paramList AS Progress.Lang.ParameterList):
DEFINE VARIABLE testAnnotation AS AnnotationInfo NO-UNDO.
- testAnnotation = testMethod:GetAnnotationOfType(OEUnitAnnotations:Test).
- testMethod:Invoke().
- IF testAnnotation:AttributeName = OEUnitAnnotations:TestExpectedAttribute THEN
- RETURN ERROR NEW AssertionFailedError("Expecting error: " + testAnnotation:AttributeValue).
+ testAnnotation = testMethod:GetAnnotationOfType(OEUnitAnnotations:Test).
+ IF paramList NE ? AND VALID-OBJECT(paramList) THEN
+ testMethod:Invoke(paramList).
+ ELSE
+ testMethod:Invoke().
+ IF testAnnotation:HasAttribute(OEUnitAnnotations:TestExpectedAttribute) THEN
+ RETURN ERROR NEW AssertionFailedError("Expecting error: " + testAnnotation:GetAttribute(OEUnitAnnotations:TestExpectedAttribute)).
CATCH assertionFailed AS AssertionFailedError:
RETURN ERROR assertionFailed.
END CATCH.
CATCH err AS Progress.Lang.Error:
- IF testAnnotation:AttributeName = OEUnitAnnotations:TestExpectedAttribute THEN DO:
- IF IsTypeOf(err, GetExpectedErrorType(testAnnotation:AttributeValue)) THEN DO:
+ IF testAnnotation:HasAttribute(OEUnitAnnotations:TestExpectedAttribute) THEN DO:
+ IF IsTypeOf(err, GetExpectedErrorType(testAnnotation:GetAttribute(OEUnitAnnotations:TestExpectedAttribute))) THEN DO:
DELETE OBJECT err NO-ERROR.
END.
ELSE
diff --git a/src/OEUnit/Tests/AllTestSuite.cls b/src/OEUnit/Tests/AllTestSuite.cls
index 789da08..3ea21c6 100644
--- a/src/OEUnit/Tests/AllTestSuite.cls
+++ b/src/OEUnit/Tests/AllTestSuite.cls
@@ -7,6 +7,7 @@ CLASS OEUnit.Tests.AllTestSuite INHERITS TestSuite:
CONSTRUCTOR AllTestSuite():
AddTest(NEW OEUnit.Tests.Assertion.AllTestSuite()).
+ AddTest(NEW OEUnit.Tests.Data.AllTestSuite()).
AddTest(NEW OEUnit.Tests.Reflection.AllTestSuite()).
AddTest(NEW OEUnit.Tests.Runner.AllTestSuite()).
AddTest(NEW OEUnit.Tests.Runners.AllTestSuite()).
diff --git a/src/OEUnit/Tests/Data/AllTestSuite.cls b/src/OEUnit/Tests/Data/AllTestSuite.cls
new file mode 100644
index 0000000..0ed42f1
--- /dev/null
+++ b/src/OEUnit/Tests/Data/AllTestSuite.cls
@@ -0,0 +1,14 @@
+ROUTINE-LEVEL ON ERROR UNDO, THROW.
+
+USING OEUnit.Runner.TestSuite.
+USING OEUnit.Tests.Data.*.
+
+CLASS OEUnit.Tests.Data.AllTestSuite INHERITS TestSuite:
+
+ CONSTRUCTOR AllTestSuite():
+ AddTest(NEW DataProviderTest()).
+ AddTest(NEW FixtureDataSetTest()).
+ AddTest(NEW FixtureTest()).
+ END CONSTRUCTOR.
+
+END CLASS.
\ No newline at end of file
diff --git a/src/OEUnit/Tests/Data/DataProviderTest.cls b/src/OEUnit/Tests/Data/DataProviderTest.cls
new file mode 100644
index 0000000..177a1e9
--- /dev/null
+++ b/src/OEUnit/Tests/Data/DataProviderTest.cls
@@ -0,0 +1,238 @@
+
+ROUTINE-LEVEL ON ERROR UNDO, THROW.
+
+USING Progress.Lang.*.
+USING OEUnit.Tests.Data.*.
+USING OEUnit.Assertion.Assert.
+USING OEUnit.Data.DataProvider.
+
+CLASS OEUnit.Tests.Data.DataProviderTest:
+
+ DEFINE PROTECTED TEMP-TABLE ttTestTable NO-UNDO
+ FIELD StatusCode AS CHARACTER
+ FIELD Accepted AS LOGICAL.
+
+ DEFINE VARIABLE dataProvider AS DataProvider NO-UNDO.
+
+ @After.
+ METHOD PUBLIC VOID DeleteDataProvider():
+ DELETE OBJECT dataProvider NO-ERROR.
+ END METHOD.
+
+ @Test.
+ METHOD PUBLIC VOID FromJSON():
+ dataProvider = NEW DataProvider().
+ dataProvider:FromJSON("~{ ~"data~": ["
+ + "~{ ~"name~": ~"Char Value~", ~"intvalue~": 0, ~"bool~": true, ~"nullvalue~": null},"
+ + "~{ ~"name~": ~"Next Value~", ~"intvalue~": 15, ~"bool~": false, ~"nullvalue~": 0},"
+ + "]}").
+ Assert:AreEqual(dataProvider:Size, 2).
+ END METHOD.
+
+ @Test.
+ METHOD PUBLIC VOID FromJSON_Reset():
+ dataProvider = NEW DataProvider().
+ dataProvider:FromJSON("~{ ~"data~": ["
+ + "~{ ~"name~": ~"Char Value~", ~"intvalue~": 0, ~"bool~": true, ~"nullvalue~": null},"
+ + "~{ ~"name~": ~"Next Value~", ~"intvalue~": 15, ~"bool~": false, ~"nullvalue~": 0},"
+ + "]}").
+ Assert:AreEqual(dataProvider:Size, 2).
+ dataProvider:FromJSON("~{ ~"data~": ["
+ + "~{ ~"ame~": ~"Char Value~", ~"intvalue~": 0, ~"bool~": true, ~"nullvalue~": null},"
+ + "~{ ~"name~": ~"Next Value~", ~"intvalue~": 15, ~"bool~": false, ~"nullvalue~": 0},"
+ + "~{ ~"name~": ~"Next Value~", ~"intvalue~": 15, ~"bool~": false, ~"nullvalue~": 0},"
+ + "~{ ~"name~": ~"Next Value~", ~"intvalue~": 15, ~"bool~": false, ~"nullvalue~": 0},"
+ + "]}").
+ Assert:AreEqual(dataProvider:Size, 4).
+ END METHOD.
+
+ @Test.
+ METHOD PUBLIC VOID FromXML():
+ dataProvider = NEW DataProvider().
+ dataProvider:FromXML(''
+ + ''
+ + ''
+ + ' NEW '
+ + ' true '
+ + ' '
+ + ''
+ + ' ACCEPTED '
+ + ' true '
+ + ' '
+ + ' ').
+ Assert:AreEqual(dataProvider:Size, 2).
+ END METHOD.
+
+ @Test.
+ METHOD PUBLIC VOID FromXML_Reset():
+ dataProvider = NEW DataProvider().
+ dataProvider:FromXML(''
+ + ''
+ + ''
+ + ' NEW '
+ + ' true '
+ + ' '
+ + ''
+ + ' ACCEPTED '
+ + ' true '
+ + ' '
+ + ' ').
+ Assert:AreEqual(dataProvider:Size, 2).
+ dataProvider:FromXML(''
+ + ''
+ + ''
+ + ' NEW '
+ + ' true '
+ + ' '
+ + ''
+ + ' ACCEPTED '
+ + ' true '
+ + ' '
+ + ''
+ + ' PICKING '
+ + ' true '
+ + ' '
+ + ''
+ + ' POSTED '
+ + ' false '
+ + ' '
+ + ' ').
+ Assert:AreEqual(dataProvider:Size, 4).
+ END METHOD.
+
+ @Test.
+ METHOD PUBLIC VOID FromTempTable():
+ dataProvider = NEW DataProvider().
+ EMPTY TEMP-TABLE ttTestTable.
+ CREATE ttTestTable.
+ ASSIGN ttTestTable.StatusCode = "NEW"
+ ttTestTable.Accepted = TRUE.
+ CREATE ttTestTable.
+ ASSIGN ttTestTable.StatusCode = "ACCEPTED"
+ ttTestTable.Accepted = TRUE.
+ dataProvider:FromTempTable(TEMP-TABLE ttTestTable:HANDLE).
+ Assert:AreEqual(dataProvider:Size, 2).
+ END METHOD.
+
+ @Test.
+ METHOD PUBLIC VOID FromTempTable_Reset():
+ dataProvider = NEW DataProvider().
+ EMPTY TEMP-TABLE ttTestTable.
+ CREATE ttTestTable.
+ ASSIGN ttTestTable.StatusCode = "NEW"
+ ttTestTable.Accepted = TRUE.
+ CREATE ttTestTable.
+ ASSIGN ttTestTable.StatusCode = "ACCEPTED"
+ ttTestTable.Accepted = TRUE.
+ CREATE ttTestTable.
+ ASSIGN ttTestTable.StatusCode = "PICKING"
+ ttTestTable.Accepted = FALSE.
+ CREATE ttTestTable.
+ ASSIGN ttTestTable.StatusCode = "POSTED"
+ ttTestTable.Accepted = FALSE.
+ dataProvider:FromTempTable(TEMP-TABLE ttTestTable:HANDLE).
+ Assert:AreEqual(dataProvider:Size, 4).
+ EMPTY TEMP-TABLE ttTestTable.
+ CREATE ttTestTable.
+ ASSIGN ttTestTable.StatusCode = "NEW"
+ ttTestTable.Accepted = TRUE.
+ CREATE ttTestTable.
+ ASSIGN ttTestTable.StatusCode = "ACCEPTED"
+ ttTestTable.Accepted = TRUE.
+ dataProvider:FromTempTable(TEMP-TABLE ttTestTable:HANDLE).
+ Assert:AreEqual(dataProvider:Size, 2).
+ END METHOD.
+
+ @Test.
+ METHOD PUBLIC VOID MoveFirst():
+ dataProvider = NEW DataProvider().
+
+ /* Object not fully initialise, should return false */
+ Assert:IsFalse(dataProvider:MoveFirst()).
+
+ dataProvider:FromJSON("~{ ~"data~": ["
+ + "~{ ~"name~": ~"Char Value~", ~"intvalue~": 0, ~"bool~": true, ~"nullvalue~": null},"
+ + "~{ ~"name~": ~"Next Value~", ~"intvalue~": 15, ~"bool~": false, ~"nullvalue~": 0},"
+ + "]}").
+ Assert:IsTrue(dataProvider:MoveFirst()).
+ END METHOD.
+
+ @Test.
+ METHOD PUBLIC VOID MoveLast():
+ dataProvider = NEW DataProvider().
+
+ /* Object not fully initialise, should return false */
+ Assert:IsFalse(dataProvider:MoveLast()).
+
+ dataProvider:FromJSON("~{ ~"data~": ["
+ + "~{ ~"name~": ~"Char Value~", ~"intvalue~": 0, ~"bool~": true, ~"nullvalue~": null},"
+ + "~{ ~"name~": ~"Next Value~", ~"intvalue~": 15, ~"bool~": false, ~"nullvalue~": 0},"
+ + "]}").
+ Assert:IsTrue(dataProvider:MoveLast()).
+ END METHOD.
+
+ @Test.
+ METHOD PUBLIC VOID MoveNext():
+ dataProvider = NEW DataProvider().
+
+ /* Object not fully initialise, should return false */
+ Assert:IsFalse(dataProvider:MoveNext()).
+
+
+ dataProvider:FromJSON("~{ ~"data~": ["
+ + "~{ ~"name~": ~"Char Value~", ~"intvalue~": 0, ~"bool~": true, ~"nullvalue~": null},"
+ + "~{ ~"name~": ~"Next Value~", ~"intvalue~": 15, ~"bool~": false, ~"nullvalue~": 0},"
+ + "]}").
+ dataProvider:MoveLast().
+ Assert:IsFalse(dataProvider:MoveNext()).
+ dataProvider:MoveFirst().
+ Assert:IsTrue(dataProvider:MoveNext()).
+ END METHOD.
+
+ @Test.
+ METHOD PUBLIC VOID MovePrev():
+ dataProvider = NEW DataProvider().
+
+ /* Object not fully initialise, should return false */
+ Assert:IsFalse(dataProvider:MovePrev()).
+
+
+ dataProvider:FromJSON("~{ ~"data~": ["
+ + "~{ ~"name~": ~"Char Value~", ~"intvalue~": 0, ~"bool~": true, ~"nullvalue~": null},"
+ + "~{ ~"name~": ~"Next Value~", ~"intvalue~": 15, ~"bool~": false, ~"nullvalue~": 0},"
+ + "]}").
+ dataProvider:MoveFirst().
+ Assert:IsFalse(dataProvider:MovePrev()).
+ dataProvider:MoveLast().
+ Assert:IsTrue(dataProvider:MovePrev()).
+ END METHOD.
+
+ @Test.
+ METHOD PUBLIC VOID GetParameterList():
+ DEFINE VARIABLE params AS Progress.Lang.ParameterList NO-UNDO.
+ dataProvider = NEW DataProvider().
+ dataProvider:FromJSON("~{ ~"data~": ["
+ + "~{ ~"name~": ~"Char Value~", ~"intvalue~": 0, ~"bool~": true, ~"nullvalue~": null},"
+ + "~{ ~"name~": ~"Next Value~", ~"intvalue~": 15, ~"bool~": false, ~"nullvalue~": 0},"
+ + "]}").
+ params = dataProvider:GetParameterList().
+ Assert:IsNotNull(params).
+ Assert:AreEqual(params:NumParameters, 4).
+ DELETE OBJECT params.
+ dataProvider:MoveNext().
+ params = dataProvider:GetParameterList().
+ Assert:IsNotNull(params).
+ Assert:AreEqual(params:NumParameters, 4).
+ DELETE OBJECT params.
+ dataProvider:MoveNext().
+ params = dataProvider:GetParameterList().
+ Assert:IsNull(params).
+ END METHOD.
+
+ @Test(expected="OEUnit.Data.DataProviderError").
+ METHOD PUBLIC VOID GetParameterList_NotInitialised():
+ dataProvider = NEW DataProvider().
+ dataProvider:GetParameterList().
+ END METHOD.
+
+END CLASS.
\ No newline at end of file
diff --git a/src/OEUnit/Tests/Data/FixtureDataSetTest.cls b/src/OEUnit/Tests/Data/FixtureDataSetTest.cls
new file mode 100644
index 0000000..a7ed3e0
--- /dev/null
+++ b/src/OEUnit/Tests/Data/FixtureDataSetTest.cls
@@ -0,0 +1,198 @@
+
+ROUTINE-LEVEL ON ERROR UNDO, THROW.
+
+USING Progress.Lang.*.
+USING OEUnit.Tests.Data.*.
+USING OEUnit.Assertion.Assert.
+USING OEUnit.Data.FixtureDataSet.
+
+CLASS OEUnit.Tests.Data.FixtureDataSetTest:
+
+ DEFINE PROTECTED TEMP-TABLE ttTestTable NO-UNDO
+ FIELD StatusCode AS CHARACTER
+ FIELD Accepted AS LOGICAL
+ INDEX StatusCode IS PRIMARY UNIQUE StatusCode ASCENDING.
+
+ DEFINE VARIABLE fixture AS FixtureDataSet NO-UNDO.
+
+ @After.
+ METHOD PUBLIC VOID DeleteFixtureDataSet():
+ DELETE OBJECT fixture NO-ERROR.
+ END METHOD.
+
+ @Test.
+ METHOD PUBLIC VOID FromJSON():
+ fixture = NEW FixtureDataSet().
+ fixture:FromJSON("~{ ~"FirstTable~": ["
+ + " ~{ ~"name~": ~"Char Value~", ~"intvalue~": 0, ~"bool~": true, ~"nullvalue~": null},"
+ + " ~{ ~"name~": ~"Next Value~", ~"intvalue~": 15, ~"bool~": false, ~"nullvalue~": 0},"
+ + " ], "
+ + " ~"SecondTable~": ["
+ + " ~{ ~"name~": ~"Char Value~", ~"intvalue~": 0, ~"bool~": true, ~"nullvalue~": null},"
+ + " ~{ ~"name~": ~"Next Value~", ~"intvalue~": 15, ~"bool~": false, ~"nullvalue~": 0}"
+ + "]}").
+ Assert:AreEqual(fixture:TableCount, 2).
+ END METHOD.
+
+ @Test.
+ METHOD PUBLIC VOID FromJSON_Reset():
+ fixture = NEW FixtureDataSet().
+ fixture:FromJSON("~{ ~"FirstTable~": ["
+ + "~{ ~"name~": ~"Char Value~", ~"intvalue~": 0, ~"bool~": true, ~"nullvalue~": null},"
+ + "~{ ~"name~": ~"Next Value~", ~"intvalue~": 15, ~"bool~": false, ~"nullvalue~": 0},"
+ + "]}").
+ Assert:AreEqual(fixture:TableCount, 1).
+ fixture:FromJSON("~{ ~"FirstTable~": ["
+ + " ~{ ~"name~": ~"Char Value~", ~"intvalue~": 0, ~"bool~": true, ~"nullvalue~": null},"
+ + " ~{ ~"name~": ~"Next Value~", ~"intvalue~": 15, ~"bool~": false, ~"nullvalue~": 0},"
+ + " ], "
+ + " ~"SecondTable~": ["
+ + " ~{ ~"name~": ~"Char Value~", ~"intvalue~": 0, ~"bool~": true, ~"nullvalue~": null},"
+ + " ~{ ~"name~": ~"Next Value~", ~"intvalue~": 15, ~"bool~": false, ~"nullvalue~": 0}"
+ + "]}").
+ Assert:AreEqual(fixture:TableCount, 2).
+ END METHOD.
+
+ @Test.
+ METHOD PUBLIC VOID FromXML():
+ fixture = NEW FixtureDataSet().
+ fixture:FromXML(''
+ + ''
+ + ''
+ + 'NEW '
+ + 'true '
+ + ' '
+ + ''
+ + 'ACCEPTED '
+ + 'true '
+ + ' '
+ + ''
+ + 'ACCEPTED '
+ + 'true '
+ + ' '
+ + ' ').
+ Assert:AreEqual(fixture:TableCount, 2).
+ END METHOD.
+
+ @Test.
+ METHOD PUBLIC VOID FromXML_Reset():
+ fixture = NEW FixtureDataSet().
+ fixture:FromXML(''
+ + ''
+ + ''
+ + 'NEW '
+ + 'true '
+ + ' '
+ + ''
+ + 'ACCEPTED '
+ + 'true '
+ + ' '
+ + ' ').
+ Assert:AreEqual(fixture:TableCount, 1).
+ fixture:FromXML(''
+ + ''
+ + ''
+ + 'NEW '
+ + 'true '
+ + ' '
+ + ''
+ + 'ACCEPTED '
+ + 'true '
+ + ' '
+ + ''
+ + 'ACCEPTED '
+ + 'true '
+ + ' '
+ + ' ').
+ Assert:AreEqual(fixture:TableCount, 2).
+ END METHOD.
+
+ @Test.
+ METHOD PUBLIC VOID FromDataSet():
+
+ DEFINE VARIABLE httTest AS HANDLE NO-UNDO.
+ DEFINE VARIABLE hdSet AS HANDLE NO-UNDO.
+ DEFINE VARIABLE htt AS HANDLE NO-UNDO.
+ DEFINE VARIABLE hdSource AS HANDLE NO-UNDO.
+ DEFINE VARIABLE httBuffer AS HANDLE NO-UNDO.
+
+ fixture = NEW FixtureDataSet().
+ EMPTY TEMP-TABLE ttTestTable.
+ CREATE ttTestTable.
+ ASSIGN ttTestTable.StatusCode = "NEW"
+ ttTestTable.Accepted = TRUE.
+ CREATE ttTestTable.
+ ASSIGN ttTestTable.StatusCode = "ACCEPTED"
+ ttTestTable.Accepted = TRUE.
+
+ ASSIGN httTest = TEMP-TABLE ttTestTable:HANDLE.
+
+ CREATE TEMP-TABLE htt.
+ htt:COPY-TEMP-TABLE(httTest, FALSE, TRUE).
+
+ httBuffer = htt:DEFAULT-BUFFER-HANDLE.
+
+ CREATE DATASET hdSet.
+ hdSet:ADD-BUFFER(httBuffer).
+
+ CREATE DATA-SOURCE hdSource.
+ hdSource:ADD-SOURCE-BUFFER(httTest:DEFAULT-BUFFER-HANDLE,?).
+ httBuffer:ATTACH-DATA-SOURCE(hdSource).
+ hdSet:FILL().
+ fixture:FromDataSet(hdSet).
+ Assert:AreEqual(fixture:TableCount, 1).
+
+ END METHOD.
+ /*
+ @Test.
+ METHOD PUBLIC VOID FromTempTable_Reset():
+ fixture = NEW FixtureDataSet().
+ EMPTY TEMP-TABLE ttTestTable.
+ CREATE ttTestTable.
+ ASSIGN ttTestTable.StatusCode = "NEW"
+ ttTestTable.Accepted = TRUE.
+ CREATE ttTestTable.
+ ASSIGN ttTestTable.StatusCode = "ACCEPTED"
+ ttTestTable.Accepted = TRUE.
+ CREATE ttTestTable.
+ ASSIGN ttTestTable.StatusCode = "PICKING"
+ ttTestTable.Accepted = FALSE.
+ CREATE ttTestTable.
+ ASSIGN ttTestTable.StatusCode = "POSTED"
+ ttTestTable.Accepted = FALSE.
+ fixture:FromTempTable(TEMP-TABLE ttTestTable:HANDLE).
+ /*Assert:AreEqual(fixture:Size, 4).*/
+ EMPTY TEMP-TABLE ttTestTable.
+ CREATE ttTestTable.
+ ASSIGN ttTestTable.StatusCode = "NEW"
+ ttTestTable.Accepted = TRUE.
+ CREATE ttTestTable.
+ ASSIGN ttTestTable.StatusCode = "ACCEPTED"
+ ttTestTable.Accepted = TRUE.
+ fixture:FromTempTable(TEMP-TABLE ttTestTable:HANDLE).
+ /*Assert:AreEqual(fixture:Size, 2).*/
+ END METHOD.
+ */
+
+ @Test.
+ METHOD PUBLIC VOID FromTempTable():
+
+ DEFINE VARIABLE httTest AS HANDLE NO-UNDO.
+ DEFINE VARIABLE hdSet AS HANDLE NO-UNDO.
+ DEFINE VARIABLE htt AS HANDLE NO-UNDO.
+ DEFINE VARIABLE hdSource AS HANDLE NO-UNDO.
+ DEFINE VARIABLE httBuffer AS HANDLE NO-UNDO.
+
+ fixture = NEW FixtureDataSet().
+ EMPTY TEMP-TABLE ttTestTable.
+ CREATE ttTestTable.
+ ASSIGN ttTestTable.StatusCode = "NEW"
+ ttTestTable.Accepted = TRUE.
+ CREATE ttTestTable.
+ ASSIGN ttTestTable.StatusCode = "ACCEPTED"
+ ttTestTable.Accepted = TRUE.
+
+ fixture:FromTempTable(TEMP-TABLE ttTestTable:HANDLE).
+ Assert:AreEqual(fixture:TableCount,1).
+ END.
+END CLASS.
\ No newline at end of file
diff --git a/src/OEUnit/Tests/Data/FixtureTest.cls b/src/OEUnit/Tests/Data/FixtureTest.cls
new file mode 100644
index 0000000..74c47c8
--- /dev/null
+++ b/src/OEUnit/Tests/Data/FixtureTest.cls
@@ -0,0 +1,250 @@
+
+ROUTINE-LEVEL ON ERROR UNDO, THROW.
+
+USING Progress.Lang.*.
+USING OEUnit.Tests.Data.*.
+USING OEUnit.Assertion.Assert.
+USING OEUnit.Data.Fixture.
+
+CLASS OEUnit.Tests.Data.FixtureTest:
+
+ DEFINE PROTECTED TEMP-TABLE ttTestTable NO-UNDO
+ FIELD StatusCode AS CHARACTER
+ FIELD Accepted AS LOGICAL
+ INDEX StatusCode IS PRIMARY UNIQUE StatusCode ASCENDING.
+
+ DEFINE PROTECTED TEMP-TABLE ttSecondTestTable NO-UNDO
+ LIKE ttTestTable.
+
+ DEFINE VARIABLE fixture AS Fixture NO-UNDO.
+
+ @After.
+ METHOD PUBLIC VOID DeleteFixture():
+ DELETE OBJECT fixture NO-ERROR.
+ END METHOD.
+
+ @Test.
+ METHOD PUBLIC VOID FromJSON():
+ fixture = NEW Fixture().
+ fixture:FromJSON("~{ ~"FirstTable~": ["
+ + " ~{ ~"name~": ~"Char Value~", ~"intvalue~": 0, ~"bool~": true, ~"nullvalue~": null},"
+ + " ~{ ~"name~": ~"Next Value~", ~"intvalue~": 15, ~"bool~": false, ~"nullvalue~": 0},"
+ + " ], "
+ + " ~"SecondTable~": ["
+ + " ~{ ~"name~": ~"Char Value~", ~"intvalue~": 0, ~"bool~": true, ~"nullvalue~": null},"
+ + " ~{ ~"name~": ~"Next Value~", ~"intvalue~": 15, ~"bool~": false, ~"nullvalue~": 0}"
+ + "]}").
+ Assert:AreEqual(fixture:TableCount, 2).
+ Assert:AreEqual(fixture:Size, 1).
+ END METHOD.
+
+ @Test.
+ METHOD PUBLIC VOID FromJSON_Multiple():
+ fixture = NEW Fixture().
+ fixture:FromJSON("~{ ~"FirstTable~": ["
+ + "~{ ~"name~": ~"Char Value~", ~"intvalue~": 0, ~"bool~": true, ~"nullvalue~": null},"
+ + "~{ ~"name~": ~"Next Value~", ~"intvalue~": 15, ~"bool~": false, ~"nullvalue~": 0},"
+ + "]}").
+ Assert:AreEqual(fixture:TableCount, 1).
+ Assert:AreEqual(fixture:Size, 1).
+ fixture:FromJSON("~{ ~"FirstTable~": ["
+ + " ~{ ~"name~": ~"Char Value~", ~"intvalue~": 0, ~"bool~": true, ~"nullvalue~": null},"
+ + " ~{ ~"name~": ~"Next Value~", ~"intvalue~": 15, ~"bool~": false, ~"nullvalue~": 0},"
+ + " ], "
+ + " ~"SecondTable~": ["
+ + " ~{ ~"name~": ~"Char Value~", ~"intvalue~": 0, ~"bool~": true, ~"nullvalue~": null},"
+ + " ~{ ~"name~": ~"Next Value~", ~"intvalue~": 15, ~"bool~": false, ~"nullvalue~": 0}"
+ + "]}").
+ Assert:AreEqual(fixture:TableCount, 3).
+ Assert:AreEqual(fixture:Size, 2).
+ END METHOD.
+
+ @Test.
+ METHOD PUBLIC VOID FromXML():
+ fixture = NEW Fixture().
+ fixture:FromXML(''
+ + ''
+ + ''
+ + 'NEW '
+ + 'true '
+ + ' '
+ + ''
+ + 'ACCEPTED '
+ + 'true '
+ + ' '
+ + ''
+ + 'ACCEPTED '
+ + 'true '
+ + ' '
+ + ' ').
+ Assert:AreEqual(fixture:TableCount, 2).
+ Assert:AreEqual(fixture:Size, 1).
+ END METHOD.
+
+ @Test.
+ METHOD PUBLIC VOID FromXML_Multiple():
+ fixture = NEW Fixture().
+ fixture:FromXML(''
+ + ''
+ + ''
+ + 'NEW '
+ + 'true '
+ + ' '
+ + ''
+ + 'ACCEPTED '
+ + 'true '
+ + ' '
+ + ' ').
+ Assert:AreEqual(fixture:TableCount, 1).
+ Assert:AreEqual(fixture:Size, 1).
+ fixture:FromXML(''
+ + ''
+ + ''
+ + 'NEW '
+ + 'true '
+ + ' '
+ + ''
+ + 'ACCEPTED '
+ + 'true '
+ + ' '
+ + ''
+ + 'ACCEPTED '
+ + 'true '
+ + ' '
+ + ' ').
+ Assert:AreEqual(fixture:TableCount, 3).
+ Assert:AreEqual(fixture:Size, 2).
+ END METHOD.
+
+ METHOD PROTECTED HANDLE CreateDataSetFromTempTable(INPUT httTest AS HANDLE):
+
+ DEFINE VARIABLE hdSet AS HANDLE NO-UNDO.
+ DEFINE VARIABLE htt AS HANDLE NO-UNDO.
+ DEFINE VARIABLE hdSource AS HANDLE NO-UNDO.
+ DEFINE VARIABLE httBuffer AS HANDLE NO-UNDO.
+ DEFINE VARIABLE httTestBuf AS HANDLE NO-UNDO.
+
+ CREATE TEMP-TABLE htt.
+ htt:COPY-TEMP-TABLE(httTest, FALSE, TRUE).
+
+ /* Create buffers for temp-tables */
+ CREATE BUFFER httBuffer FOR TABLE htt.
+ CREATE BUFFER httTestBuf FOR TABLE httTest.
+
+ CREATE DATASET hdSet.
+ hdSet:ADD-BUFFER(httBuffer).
+
+ CREATE DATA-SOURCE hdSource.
+ hdSource:ADD-SOURCE-BUFFER(httTestBuf,?).
+ httBuffer:ATTACH-DATA-SOURCE(hdSource).
+ hdSet:FILL().
+
+ RETURN hdSet.
+
+ END METHOD.
+
+ @Test.
+ METHOD PUBLIC VOID FromDataSet():
+
+ fixture = NEW Fixture().
+ EMPTY TEMP-TABLE ttTestTable.
+ CREATE ttTestTable.
+ ASSIGN ttTestTable.StatusCode = "NEW"
+ ttTestTable.Accepted = TRUE.
+ CREATE ttTestTable.
+ ASSIGN ttTestTable.StatusCode = "ACCEPTED"
+ ttTestTable.Accepted = TRUE.
+
+ fixture:FromDataSet(CreateDataSetFromTempTable(INPUT TEMP-TABLE ttTestTable:HANDLE)).
+ Assert:AreEqual(fixture:TableCount, 1).
+ Assert:AreEqual(fixture:Size, 1).
+
+ END METHOD.
+
+ @Test.
+ METHOD PUBLIC VOID FromDataSet_Multiple():
+
+ DEFINE BUFFER ttBufTestTable FOR ttTestTable.
+ DEFINE BUFFER ttSecondTestTable FOR ttSecondTestTable.
+
+ fixture = NEW Fixture().
+ EMPTY TEMP-TABLE ttTestTable.
+ CREATE ttTestTable.
+ ASSIGN ttTestTable.StatusCode = "NEW"
+ ttTestTable.Accepted = TRUE.
+ CREATE ttTestTable.
+ ASSIGN ttTestTable.StatusCode = "ACCEPTED"
+ ttTestTable.Accepted = TRUE.
+
+ fixture:FromDataSet(CreateDataSetFromTempTable(INPUT TEMP-TABLE ttBufTestTable:HANDLE)).
+ Assert:AreEqual(fixture:TableCount, 1).
+ Assert:AreEqual(fixture:Size, 1).
+
+ EMPTY TEMP-TABLE ttSecondTestTable.
+ CREATE ttSecondTestTable.
+ ASSIGN ttSecondTestTable.StatusCode = "NEW"
+ ttSecondTestTable.Accepted = TRUE.
+ CREATE ttSecondTestTable.
+ ASSIGN ttSecondTestTable.StatusCode = "ACCEPTED"
+ ttSecondTestTable.Accepted = TRUE.
+
+ fixture:FromDataSet(CreateDataSetFromTempTable(INPUT TEMP-TABLE ttSecondTestTable:HANDLE)).
+ Assert:AreEqual(fixture:TableCount, 2).
+ Assert:AreEqual(fixture:Size, 2).
+
+ END METHOD.
+
+ @Test.
+ METHOD PUBLIC VOID FromTempTable():
+
+ DEFINE VARIABLE httTest AS HANDLE NO-UNDO.
+ DEFINE VARIABLE hdSet AS HANDLE NO-UNDO.
+ DEFINE VARIABLE htt AS HANDLE NO-UNDO.
+ DEFINE VARIABLE hdSource AS HANDLE NO-UNDO.
+ DEFINE VARIABLE httBuffer AS HANDLE NO-UNDO.
+
+ fixture = NEW Fixture().
+ EMPTY TEMP-TABLE ttTestTable.
+ CREATE ttTestTable.
+ ASSIGN ttTestTable.StatusCode = "NEW"
+ ttTestTable.Accepted = TRUE.
+ CREATE ttTestTable.
+ ASSIGN ttTestTable.StatusCode = "ACCEPTED"
+ ttTestTable.Accepted = TRUE.
+
+ fixture:FromTempTable(TEMP-TABLE ttTestTable:HANDLE).
+ Assert:AreEqual(fixture:TableCount,1).
+ Assert:AreEqual(fixture:Size, 1).
+ END.
+
+ @Test.
+ METHOD PUBLIC VOID FromTempTable_Multiple():
+ fixture = NEW Fixture().
+ EMPTY TEMP-TABLE ttTestTable.
+ CREATE ttTestTable.
+ ASSIGN ttTestTable.StatusCode = "NEW"
+ ttTestTable.Accepted = TRUE.
+ CREATE ttTestTable.
+ ASSIGN ttTestTable.StatusCode = "ACCEPTED"
+ ttTestTable.Accepted = TRUE.
+ CREATE ttTestTable.
+ ASSIGN ttTestTable.StatusCode = "PICKING"
+ ttTestTable.Accepted = FALSE.
+ CREATE ttTestTable.
+ ASSIGN ttTestTable.StatusCode = "POSTED"
+ ttTestTable.Accepted = FALSE.
+ fixture:FromTempTable(TEMP-TABLE ttTestTable:HANDLE).
+ Assert:AreEqual(fixture:TableCount, 1).
+ Assert:AreEqual(fixture:Size, 1).
+ EMPTY TEMP-TABLE ttSecondTestTable.
+ CREATE ttSecondTestTable.
+ ASSIGN ttSecondTestTable.StatusCode = "NEW"
+ ttSecondTestTable.Accepted = TRUE.
+ CREATE ttSecondTestTable.
+ ASSIGN ttSecondTestTable.StatusCode = "ACCEPTED"
+ ttSecondTestTable.Accepted = TRUE.
+ fixture:FromTempTable(TEMP-TABLE ttSecondTestTable:HANDLE).
+ Assert:AreEqual(fixture:TableCount, 2).
+ Assert:AreEqual(fixture:Size, 2).
+ END METHOD.
+END CLASS.
\ No newline at end of file
diff --git a/src/OEUnit/Tests/Reflection/AnnotationInfoTest.cls b/src/OEUnit/Tests/Reflection/AnnotationInfoTest.cls
index 33aa147..437f788 100644
--- a/src/OEUnit/Tests/Reflection/AnnotationInfoTest.cls
+++ b/src/OEUnit/Tests/Reflection/AnnotationInfoTest.cls
@@ -16,24 +16,50 @@ CLASS OEUnit.Tests.Reflection.AnnotationInfoTest:
METHOD PUBLIC VOID ParseAnnotationNoAttribute():
annotation = NEW AnnotationInfo("Annotation").
Assert:AreEqual(annotation:AnnotationType, "Annotation").
- Assert:AreEqual(annotation:AttributeName, "").
- Assert:AreEqual(annotation:AttributeValue, "").
+ Assert:AreEqual(annotation:Attributes:Size, 0).
END METHOD.
@Test.
METHOD PUBLIC VOID ParseAnnotationWithAttribute():
annotation = NEW AnnotationInfo("Annotation(Attribute=Value)").
Assert:AreEqual(annotation:AnnotationType, "Annotation").
- Assert:AreEqual(annotation:AttributeName, "Attribute").
- Assert:AreEqual(annotation:AttributeValue, "Value").
+ Assert:AreEqual(annotation:Attributes:Size, 1).
+ Assert:IsTrue(annotation:HasAttribute("Attribute")).
+ Assert:AreEqual(annotation:GetAttribute("Attribute"), "Value").
END METHOD.
@Test.
METHOD PUBLIC VOID ParseAnnotationComplexAttribute():
annotation = NEW AnnotationInfo("Annotation(a=5+1=6)").
Assert:AreEqual(annotation:AnnotationType, "Annotation").
- Assert:AreEqual(annotation:AttributeName, "a").
- Assert:AreEqual(annotation:AttributeValue, "5+1=6").
- END METHOD.
+ Assert:AreEqual(annotation:Attributes:Size, 1).
+ Assert:IsTrue(annotation:HasAttribute("a")).
+ Assert:AreEqual(annotation:GetAttribute("a"), "5+1=6").
+ END METHOD.
+
+ @Test.
+ METHOD PUBLIC VOID ParseAnnotationMultipleAttributes():
+ annotation = NEW AnnotationInfo("Annotation(Attribute1=Value1,Attribute2=Value2,Attribute3=Value3)").
+ Assert:AreEqual(annotation:Attributes:Size, 3).
+ Assert:IsTrue(annotation:HasAttribute("Attribute1")).
+ Assert:IsTrue(annotation:HasAttribute("Attribute2")).
+ Assert:IsTrue(annotation:HasAttribute("Attribute3")).
+ END METHOD.
+
+ @Test.
+ METHOD PUBLIC VOID AnnotationHasAttribute():
+ annotation = NEW AnnotationInfo("Annotation(Attribute=Value)").
+ Assert:IsTrue(annotation:HasAttribute("Attribute")).
+ Assert:IsFalse(annotation:HasAttribute("DoesNotExist")).
+ END METHOD.
+
+ @Test.
+ METHOD PUBLIC VOID AnnotationGetAttribute():
+ annotation = NEW AnnotationInfo("Annotation(Attribute1=Value1,Attribute2=Value2,Attribute3=Value3)").
+ Assert:AreEqual(annotation:GetAttribute("Attribute1"), "Value1").
+ Assert:AreEqual(annotation:GetAttribute("Attribute2"), "Value2").
+ Assert:AreEqual(annotation:GetAttribute("Attribute3"), "Value3").
+ Assert:IsNull(annotation:GetAttribute("Attribute4")).
+ END METHOD.
END CLASS.
\ No newline at end of file
diff --git a/src/OEUnit/Tests/Util/AllTestSuite.cls b/src/OEUnit/Tests/Util/AllTestSuite.cls
index 30f498f..d2043f9 100644
--- a/src/OEUnit/Tests/Util/AllTestSuite.cls
+++ b/src/OEUnit/Tests/Util/AllTestSuite.cls
@@ -8,6 +8,7 @@ CLASS OEUnit.Tests.Util.AllTestSuite INHERITS TestSuite:
CONSTRUCTOR AllTestSuite():
AddTest(NEW ListTest()).
+ AddTest(NEW NamedListTest()).
END CONSTRUCTOR.
END CLASS.
\ No newline at end of file
diff --git a/src/OEUnit/Tests/Util/NamedListTest.cls b/src/OEUnit/Tests/Util/NamedListTest.cls
new file mode 100644
index 0000000..993763b
--- /dev/null
+++ b/src/OEUnit/Tests/Util/NamedListTest.cls
@@ -0,0 +1,101 @@
+
+ROUTINE-LEVEL ON ERROR UNDO, THROW.
+
+USING Progress.Lang.*.
+USING OEUnit.Assertion.Assert.
+USING OEUnit.Util.*.
+USING OEUnit.Tests.Util.*.
+
+CLASS OEUnit.Tests.Util.NamedListTest :
+
+ DEFINE PRIVATE VARIABLE list1 AS NamedList NO-UNDO.
+
+ DEFINE PRIVATE VARIABLE name1 AS CHARACTER NO-UNDO INITIAL "Name1".
+ DEFINE PRIVATE VARIABLE value1 AS CHARACTER NO-UNDO INITIAL "Value1".
+ DEFINE PRIVATE VARIABLE name2 AS CHARACTER NO-UNDO INITIAL "Name2".
+ DEFINE PRIVATE VARIABLE value2 AS CHARACTER NO-UNDO INITIAL "Value2".
+ DEFINE PRIVATE VARIABLE name3 AS CHARACTER NO-UNDO INITIAL "Name3".
+ DEFINE PRIVATE VARIABLE value3 AS CHARACTER NO-UNDO INITIAL "Value3".
+ DEFINE PRIVATE VARIABLE name4 AS CHARACTER NO-UNDO INITIAL "Name4".
+ DEFINE PRIVATE VARIABLE value4 AS CHARACTER NO-UNDO INITIAL "Value4".
+
+ @Before.
+ METHOD PUBLIC VOID CreateList():
+ list1 = NEW NamedList().
+ list1:Add(name1, value1).
+ list1:Add(name2, value2).
+ list1:Add(name3, value3).
+ list1:Add(name4, value4).
+ END METHOD.
+
+ @After.
+ METHOD PUBLIC VOID DeleteList():
+ DELETE OBJECT list1 NO-ERROR.
+ END METHOD.
+
+ @Test.
+ METHOD PUBLIC VOID AddDuplicate():
+ list1:Add(name1, value2).
+ Assert:AreEqual(list1:GET(name1), value2).
+ END METHOD.
+
+ @Test.
+ METHOD PUBLIC VOID TestMoveAndReset():
+ list1:MoveFirst().
+ Assert:AreEqual(list1:CurrentName, name1).
+ Assert:AreEqual(list1:CurrentValue, value1).
+ list1:MoveNext().
+ Assert:AreEqual(list1:CurrentName, name2).
+ Assert:AreEqual(list1:CurrentValue, value2).
+ list1:Reset().
+ list1:MoveNext().
+ Assert:AreEqual(list1:CurrentName, name1).
+ Assert:AreEqual(list1:CurrentValue, value1).
+ list1:MoveNext().
+ Assert:AreEqual(list1:CurrentName, name2).
+ Assert:AreEqual(list1:CurrentValue, value2).
+ list1:Reset().
+ list1:MovePrevious().
+ Assert:AreEqual(list1:CurrentName, name4).
+ Assert:AreEqual(list1:CurrentValue, value4).
+ list1:MovePrevious().
+ Assert:AreEqual(list1:CurrentName, name3).
+ Assert:AreEqual(list1:CurrentValue, value3).
+ list1:MoveFirst().
+ Assert:AreEqual(list1:CurrentName, name1).
+ Assert:AreEqual(list1:CurrentValue, value1).
+ list1:MoveLast().
+ Assert:AreEqual(list1:CurrentName, name4).
+ Assert:AreEqual(list1:CurrentValue, value4).
+ END METHOD.
+
+ @Test(expected="Progress.Lang.AppError").
+ METHOD PUBLIC VOID Remove():
+ Assert:AreEqual(list1:Get(name1), value1).
+ list1:Remove(name1).
+ list1:Get(name1).
+ END METHOD.
+
+ @Test(expected="Progress.Lang.AppError").
+ METHOD PUBLIC VOID RemoveNonExisting():
+ list1:Remove("does not exist").
+ END METHOD.
+
+ @Test.
+ METHOD PUBLIC VOID Get():
+ Assert:AreEqual(list1:Get(name1), value1).
+ END METHOD.
+
+ @Test(expected="Progress.Lang.AppError").
+ METHOD PUBLIC VOID GetNonExisting():
+ list1:Get("does not exist").
+ END METHOD.
+
+ @Test.
+ METHOD PUBLIC VOID HasValue():
+ Assert:IsTrue(list1:HasValue(name1)).
+ Assert:IsFalse(list1:HasValue("does not exist")).
+ END METHOD.
+
+END CLASS.
+
diff --git a/src/OEUnit/UI/ResultsWindowView.cls b/src/OEUnit/UI/ResultsWindowView.cls
index f0851ab..bf1d326 100644
--- a/src/OEUnit/UI/ResultsWindowView.cls
+++ b/src/OEUnit/UI/ResultsWindowView.cls
@@ -2,7 +2,7 @@
File : ResultsWindowView.cls
Package : OEUnit.UI
Description : Results view for windows based user-interfaces (GUI). Displays
- results in a window that can be docked in Progress Developer
+ results in a window that can be docked in Progress Developer
Studio.
------------------------------------------------------------------------------*/
@@ -16,38 +16,43 @@ CLASS OEUnit.UI.ResultsWindowView IMPLEMENTS IResultsView:
/*----------------------------------------------------------------------------
Test Result and Error Temp-Tables
- ----------------------------------------------------------------------------*/
+ ----------------------------------------------------------------------------*/
{OEUnit/UI/ttTestResult.i &access = "STATIC"}
{OEUnit/UI/ttError.i &access = "STATIC"}
/*----------------------------------------------------------------------------
Total number of records in the ttTestResult temp-table.
- ----------------------------------------------------------------------------*/
+ ----------------------------------------------------------------------------*/
DEFINE PRIVATE STATIC VARIABLE testCount AS INTEGER NO-UNDO.
-
+
/*----------------------------------------------------------------------------
Store the details of the last test run - so it may be easily rerun.
- ----------------------------------------------------------------------------*/
+ ----------------------------------------------------------------------------*/
DEFINE PRIVATE STATIC VARIABLE lastClassFile AS CHARACTER NO-UNDO.
DEFINE PRIVATE STATIC VARIABLE lastClassName AS CHARACTER NO-UNDO.
DEFINE PRIVATE STATIC VARIABLE lastMethodName AS CHARACTER NO-UNDO.
-
+
/*----------------------------------------------------------------------------
- Handle to the persistently run ResultsWindow procedure.
+ Handle to the persistently run ResultsWindow procedure.
+ Never directly use this variable, except from GetResultsWindow()
----------------------------------------------------------------------------*/
DEFINE PRIVATE STATIC VARIABLE resultWindow AS HANDLE NO-UNDO.
- DEFINE PRIVATE STATIC PROPERTY ResultsWindow AS HANDLE NO-UNDO
- GET:
+
+ /*----------------------------------------------------------------------------
+ We can't use a property to get the resultWindow, since that somehow triggers
+ a WAIT-FOR condition, which causes errors later on...
+ ----------------------------------------------------------------------------*/
+ METHOD PRIVATE STATIC VOID GetResultsWindow(OUTPUT resultsWindow AS HANDLE):
IF NOT(VALID-HANDLE(resultWindow)) THEN
RUN OEUnit/UI/ResultsWindow.w PERSISTENT SET resultWindow.
- RETURN resultWindow.
- END GET.
+ ASSIGN resultsWindow = resultWindow.
+ END METHOD.
/*----------------------------------------------------------------------------
Run the given file as a test. Must be a valid class file (.cls).
- ----------------------------------------------------------------------------*/
+ ----------------------------------------------------------------------------*/
METHOD PUBLIC STATIC VOID RunAsTest(INPUT classFileName AS CHARACTER):
- ASSIGN
+ ASSIGN
lastClassName = ""
lastMethodName = ""
lastClassFile = classFileName.
@@ -55,16 +60,16 @@ CLASS OEUnit.UI.ResultsWindowView IMPLEMENTS IResultsView:
ResultsPresenter:RunAsTest(classFileName).
CATCH e AS Progress.Lang.Error :
DisplayError(classFileName, e).
- DELETE OBJECT e NO-ERROR.
+ DELETE OBJECT e NO-ERROR.
END CATCH.
END METHOD.
/*----------------------------------------------------------------------------
- Reruns the given test class (must be the full class type-name). Optionally
+ Reruns the given test class (must be the full class type-name). Optionally
specify the name of a test method name to run that method only.
- ----------------------------------------------------------------------------*/
+ ----------------------------------------------------------------------------*/
METHOD PUBLIC STATIC VOID RerunTest(INPUT className AS CHARACTER, INPUT methodName AS CHARACTER):
- ASSIGN
+ ASSIGN
lastClassName = className
lastMethodName = methodName
lastClassFile = "".
@@ -72,13 +77,13 @@ CLASS OEUnit.UI.ResultsWindowView IMPLEMENTS IResultsView:
ResultsPresenter:RerunTest(className, methodName).
CATCH e AS Progress.Lang.Error :
DisplayError(className, e).
- DELETE OBJECT e NO-ERROR.
+ DELETE OBJECT e NO-ERROR.
END CATCH.
END METHOD.
-/*----------------------------------------------------------------------------
- Reruns the last run test class (and filtered method if set)
- ----------------------------------------------------------------------------*/
+ /*----------------------------------------------------------------------------
+ Reruns the last run test class (and filtered method if set)
+ ----------------------------------------------------------------------------*/
METHOD PUBLIC STATIC VOID RerunTest():
IF lastClassFile <> "" THEN
RunAsTest(lastClassFile).
@@ -88,8 +93,9 @@ CLASS OEUnit.UI.ResultsWindowView IMPLEMENTS IResultsView:
/*----------------------------------------------------------------------------
Display the results window and indicate that testing is in progress
- ----------------------------------------------------------------------------*/
- METHOD PRIVATE STATIC LOGICAL DisplayRunning(INPUT className AS CHARACTER):
+ ----------------------------------------------------------------------------*/
+ METHOD PRIVATE STATIC VOID DisplayRunning(INPUT className AS CHARACTER):
+ DEFINE VARIABLE resultsWindow AS HANDLE NO-UNDO.
EMPTY TEMP-TABLE ttTestResult.
EMPTY TEMP-TABLE ttError.
CREATE ttTestResult.
@@ -104,14 +110,16 @@ CLASS OEUnit.UI.ResultsWindowView IMPLEMENTS IResultsView:
ttTestResult.ErrorCount = 0
ttTestResult.IsClass = TRUE
ttTestResult.HasErrors = FALSE.
- RUN setTestResults IN ResultsWindow (INPUT TABLE ttTestResult BIND,
+ GetResultsWindow(OUTPUT resultsWindow).
+ RUN setTestResults IN resultsWindow (INPUT TABLE ttTestResult BIND,
INPUT TABLE ttError BIND, 1, 0, 0, 0, 0).
- END METHOD.
+ END METHOD.
/*----------------------------------------------------------------------------
Display the given error in the results window.
- ----------------------------------------------------------------------------*/
- METHOD PRIVATE STATIC LOGICAL DisplayError(INPUT className AS CHARACTER, INPUT err AS Progress.Lang.Error):
+ ----------------------------------------------------------------------------*/
+ METHOD PRIVATE STATIC VOID DisplayError(INPUT className AS CHARACTER, INPUT err AS Progress.Lang.Error):
+ DEFINE VARIABLE resultsWindow AS HANDLE NO-UNDO.
EMPTY TEMP-TABLE ttTestResult.
EMPTY TEMP-TABLE ttError.
CREATE ttTestResult.
@@ -126,30 +134,33 @@ CLASS OEUnit.UI.ResultsWindowView IMPLEMENTS IResultsView:
ttTestResult.IsClass = TRUE
ttTestResult.HasErrors = TRUE.
AddError(err, 1).
- RUN setTestResults IN ResultsWindow (INPUT TABLE ttTestResult BIND,
+ GetResultsWindow(OUTPUT resultsWindow).
+ RUN setTestResults IN resultsWindow (INPUT TABLE ttTestResult BIND,
INPUT TABLE ttError BIND, 1, 0, 0, 1, 0).
- END METHOD.
+ END METHOD.
/*----------------------------------------------------------------------------
Displays the given test results in the results window.
----------------------------------------------------------------------------*/
METHOD PUBLIC VOID DisplayResults(INPUT results AS TestClassResult):
+ DEFINE VARIABLE resultsWindow AS HANDLE NO-UNDO.
testCount = 0.
EMPTY TEMP-TABLE ttTestResult.
EMPTY TEMP-TABLE ttError.
AddResult(results,"",0).
- RUN setTestResults IN ResultsWindow (INPUT TABLE ttTestResult BIND,
+ GetResultsWindow(OUTPUT resultsWindow).
+ RUN setTestResults IN resultsWindow (INPUT TABLE ttTestResult BIND,
INPUT TABLE ttError BIND, testCount,
- results:CountTestsWithStatus(TestResult:StatusPassed),
- results:CountTestsWithStatus(TestResult:StatusFailed),
- results:CountTestsWithStatus(TestResult:StatusError),
+ results:CountTestsWithStatus(TestResult:StatusPassed),
+ results:CountTestsWithStatus(TestResult:StatusFailed),
+ results:CountTestsWithStatus(TestResult:StatusError),
results:CountTestsWithStatus(TestResult:StatusIgnored)).
END METHOD.
/*----------------------------------------------------------------------------
Add the given test result to the ttTestResult and ttError tables.
- ----------------------------------------------------------------------------*/
- METHOD PRIVATE VOID AddResult(INPUT res AS TestResult, INPUT parentClass AS CHARACTER,
+ ----------------------------------------------------------------------------*/
+ METHOD PRIVATE VOID AddResult(INPUT res AS TestResult, INPUT parentClass AS CHARACTER,
INPUT depth AS INTEGER):
DEFINE VARIABLE testName AS CHARACTER NO-UNDO.
@@ -164,10 +175,10 @@ CLASS OEUnit.UI.ResultsWindowView IMPLEMENTS IResultsView:
ttTestResult.ErrorCount = res:GetErrors():Size
ttTestResult.ResultMessage = res:GetMessage()
ttTestResult.ResultStatus = res:GetStatus()
- ttTestResult.ResultStatusString = CAPS(res:GetStatusAsString()).
-
+ ttTestResult.ResultStatusString = CAPS(res:GetStatusAsString()).
+
AddErrors(res:GetErrors()).
-
+
IF TYPE-OF(res, TestClassResult) THEN DO:
DEFINE VARIABLE classResult AS TestClassResult NO-UNDO.
ASSIGN
@@ -175,15 +186,15 @@ CLASS OEUnit.UI.ResultsWindowView IMPLEMENTS IResultsView:
ttTestResult.IsClass = TRUE
ttTestResult.HasPasses = (res:CountTestsWithStatus(TestResult:StatusPassed) > 0)
ttTestResult.HasFailures = (res:CountTestsWithStatus(TestResult:StatusFailed) > 0)
- ttTestResult.HasErrors = ((res:CountTestsWithStatus(TestResult:StatusError) > 0)
+ ttTestResult.HasErrors = ((res:CountTestsWithStatus(TestResult:StatusError) > 0)
OR (classResult:GetStatus() = TestResult:StatusError)).
- ttTestResult.HasIgnored = ((res:CountTestsWithStatus(TestResult:StatusIgnored) > 0)
- OR (classResult:GetStatus() = TestResult:StatusIgnored)).
- IF classResult:GetMessage() = "" AND classResult:GetStatus() > TestResult:StatusNoTests THEN
+ ttTestResult.HasIgnored = ((res:CountTestsWithStatus(TestResult:StatusIgnored) > 0)
+ OR (classResult:GetStatus() = TestResult:StatusIgnored)).
+ IF classResult:GetMessage() = "" AND classResult:GetStatus() > TestResult:StatusNoTests THEN
ttTestResult.ResultStatusString = "".
-
+
/* Add child results */
- DEFINE VARIABLE i AS INTEGER NO-UNDO.
+ DEFINE VARIABLE i AS INTEGER NO-UNDO.
DO i = 1 TO classResult:resultCount:
AddResult(classResult:GetResult(i), testName, depth + 1).
END.
@@ -194,7 +205,7 @@ CLASS OEUnit.UI.ResultsWindowView IMPLEMENTS IResultsView:
/*----------------------------------------------------------------------------
Add each of the Progress.Lang.Errors in the given list to the ttError table.
- ----------------------------------------------------------------------------*/
+ ----------------------------------------------------------------------------*/
METHOD PRIVATE STATIC VOID AddErrors(INPUT errors AS List):
DEFINE VARIABLE i AS INTEGER NO-UNDO.
DO i = 1 TO errors:Size:
@@ -202,19 +213,19 @@ CLASS OEUnit.UI.ResultsWindowView IMPLEMENTS IResultsView:
END.
END METHOD.
-
+
/*----------------------------------------------------------------------------
Add the given error to ttError.
- ----------------------------------------------------------------------------*/
+ ----------------------------------------------------------------------------*/
METHOD PRIVATE STATIC VOID AddError(INPUT err AS Progress.Lang.Error, INPUT errorId AS INTEGER):
CREATE ttError.
ASSIGN ttError.TestId = ttTestResult.TestId
- ttError.ErrorId = errorId
+ ttError.ErrorId = errorId
ttError.ErrorMessage = Errors:GetMessage(err)
- ttError.CallStack = err:GetClass():TypeName + " at~n"
+ ttError.CallStack = err:GetClass():TypeName + " at~n"
+ Errors:TrimCallStack(err:CallStack).
-
+
END METHOD.
-
+
END CLASS.
\ No newline at end of file
diff --git a/src/OEUnit/Util/NamedList.cls b/src/OEUnit/Util/NamedList.cls
new file mode 100644
index 0000000..92998ee
--- /dev/null
+++ b/src/OEUnit/Util/NamedList.cls
@@ -0,0 +1,168 @@
+ /*------------------------------------------------------------------------------
+ File : NamedList.cls
+ Package : OEUnit.Util
+ Description : Stores a named list of strings. List elements can be accessed
+ by their name only.
+------------------------------------------------------------------------------*/
+
+ROUTINE-LEVEL ON ERROR UNDO, THROW.
+
+USING Progress.Lang.*.
+
+CLASS OEUnit.Util.NamedList:
+
+ DEFINE PRIVATE VARIABLE reset AS LOGICAL NO-UNDO.
+
+ /*----------------------------------------------------------------------------
+ Temp table to store elements (integer-object pairs).
+ ----------------------------------------------------------------------------*/
+ DEFINE PROTECTED TEMP-TABLE element NO-UNDO
+ FIELD name AS CHARACTER
+ FIELD val AS CHARACTER
+ INDEX sequence IS PRIMARY UNIQUE name ASCENDING.
+
+ /*----------------------------------------------------------------------------
+ The number of elements in the list
+ ----------------------------------------------------------------------------*/
+ DEFINE PUBLIC PROPERTY Size AS INTEGER NO-UNDO INIT 0
+ GET.
+ PRIVATE SET.
+
+ /*----------------------------------------------------------------------------
+ Return the name of item in the list that is currently being pointed to.
+ ----------------------------------------------------------------------------*/
+ DEFINE PUBLIC PROPERTY CurrentName AS CHARACTER NO-UNDO
+ GET():
+ IF AVAILABLE(element) THEN
+ RETURN element.name.
+ RETURN ?.
+ END GET.
+ /*SET. */
+
+ /*----------------------------------------------------------------------------
+ Return the value of item in the list that is currently being pointed to.
+ ----------------------------------------------------------------------------*/
+ DEFINE PUBLIC PROPERTY CurrentValue AS CHARACTER NO-UNDO
+ GET():
+ IF AVAILABLE(element) THEN
+ RETURN element.val.
+ RETURN ?.
+ END GET.
+ /*SET. */
+
+ /*----------------------------------------------------------------------------
+ Default Constructor.
+ ----------------------------------------------------------------------------*/
+ CONSTRUCTOR NamedList():
+ END CONSTRUCTOR.
+
+ DESTRUCTOR NamedList():
+ FOR EACH element:
+ DELETE element.
+ END.
+ END DESTRUCTOR.
+
+ /*----------------------------------------------------------------------------
+ Add the name and value pair to the list.
+ ----------------------------------------------------------------------------*/
+ METHOD PUBLIC VOID Add(INPUT nam AS CHARACTER, INPUT val AS CHARACTER):
+ FIND element WHERE element.name = nam NO-ERROR.
+ IF NOT AVAILABLE(element) THEN
+ DO:
+ Size = Size + 1.
+ CREATE element.
+ ASSIGN element.name = nam
+ element.val = val.
+ END.
+ ELSE ASSIGN element.val = val.
+ END METHOD.
+
+ /*----------------------------------------------------------------------------
+ Returns the value matching the given name from the list.
+ ----------------------------------------------------------------------------*/
+ METHOD PUBLIC CHARACTER Get(INPUT name AS CHARACTER):
+ FIND element WHERE element.name = name NO-ERROR.
+ IF AVAILABLE(element) THEN RETURN element.val.
+ RETURN ERROR NEW AppError("Value not found with name: " + name,0).
+ END METHOD.
+
+ /*----------------------------------------------------------------------------
+ Removes the given object from the list.
+ ----------------------------------------------------------------------------*/
+ METHOD PUBLIC VOID Remove(INPUT name AS CHARACTER):
+ FIND element WHERE element.name = name NO-ERROR.
+ IF AVAILABLE(element) THEN
+ RemoveCurrent().
+ ELSE
+ RETURN ERROR NEW AppError("Value not found in list with name: " + name,0).
+ END METHOD.
+
+ /*----------------------------------------------------------------------------
+ Removes the current element from the list.
+ ----------------------------------------------------------------------------*/
+ METHOD PUBLIC VOID RemoveCurrent():
+ IF AVAILABLE(element) THEN DO:
+ DELETE element.
+ Size = Size - 1.
+ END.
+ END METHOD.
+
+ /*----------------------------------------------------------------------------
+ Reset the current pointer position. Calling MoveNext() after calling this
+ method will position the pointer at the first element in the list. Whereas
+ calling MovePrevious() will position the pointer at the last element in the
+ list.
+ ----------------------------------------------------------------------------*/
+ METHOD PUBLIC VOID Reset():
+ reset = TRUE.
+ END METHOD.
+
+ /*----------------------------------------------------------------------------
+ Positions the pointer at the first element in the list. Returns true if exists.
+ ----------------------------------------------------------------------------*/
+ METHOD PUBLIC LOGICAL MoveFirst():
+ FIND FIRST element NO-ERROR.
+ RETURN AVAILABLE(element).
+ END METHOD.
+
+ /*----------------------------------------------------------------------------
+ Positions the pointer at the last element in the list. Returns true if exists.
+ ----------------------------------------------------------------------------*/
+ METHOD PUBLIC LOGICAL MoveLast():
+ FIND LAST element NO-ERROR.
+ RETURN AVAILABLE(element).
+ END METHOD.
+
+ /*----------------------------------------------------------------------------
+ Position the pointer at the next element in the list. Returns true if exists.
+ ----------------------------------------------------------------------------*/
+ METHOD PUBLIC LOGICAL MoveNext():
+ IF reset THEN DO:
+ reset = FALSE.
+ RETURN MoveFirst().
+ END.
+ FIND NEXT element NO-ERROR.
+ RETURN AVAILABLE(element).
+ END METHOD.
+
+ /*----------------------------------------------------------------------------
+ Position the pointer at the previous element in the list. Returns true if
+ exists.
+ ----------------------------------------------------------------------------*/
+ METHOD PUBLIC LOGICAL MovePrevious():
+ IF reset THEN DO:
+ reset = FALSE.
+ RETURN MoveLast().
+ END.
+ FIND PREV element NO-ERROR.
+ RETURN AVAILABLE(element).
+ END METHOD.
+
+ /*----------------------------------------------------------------------------
+ Return true is a list element with the given name is in the list.
+ ----------------------------------------------------------------------------*/
+ METHOD PUBLIC LOGICAL HasValue(INPUT name AS CHARACTER):
+ RETURN CAN-FIND(FIRST element WHERE element.name = name).
+ END METHOD.
+
+END CLASS.
\ No newline at end of file