From fe322040ed4e7c676a3aa3c76447c870c2202a9a Mon Sep 17 00:00:00 2001
From: Mark Abbott
Date: Mon, 21 Jul 2014 17:06:27 +0100
Subject: [PATCH 01/33] Issue #8 - AssertString:IsNotNullOrEmpty does not use
failMessage - Pass failMessage parameter to Assert methods called in this
method.
---
src/OEUnit/Assertion/AssertStringType.i | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
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.
/*----------------------------------------------------------------------------
From b81dc3a9e119aa90f193bb0879415c32b665a2ba Mon Sep 17 00:00:00 2001
From: Mark Abbott
Date: Thu, 28 Aug 2014 17:13:37 +0100
Subject: [PATCH 02/33] Fixes #10 - JUnit Package name - Package name to be
extracted from full class name and used in JUnit Reporter.
---
src/OEUnit/Automation/JUnitReporter.cls | 16 +++++++++++++---
1 file changed, 13 insertions(+), 3 deletions(-)
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.
From bc5c962e1574fe36d9031011f7c13967fe1ad2e4 Mon Sep 17 00:00:00 2001
From: Mark Abbott
Date: Tue, 14 Oct 2014 09:25:06 +0100
Subject: [PATCH 03/33] Add NamedList class to OEUnit.Util and matching unit
tests
---
src/OEUnit/Tests/Util/AllTestSuite.cls | 1 +
src/OEUnit/Tests/Util/NamedListTest.cls | 101 ++++++++++++++
src/OEUnit/Util/NamedList.cls | 168 ++++++++++++++++++++++++
3 files changed, 270 insertions(+)
create mode 100644 src/OEUnit/Tests/Util/NamedListTest.cls
create mode 100644 src/OEUnit/Util/NamedList.cls
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/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
From 36cfac5cc0fac4ce152873695659187218d37b24 Mon Sep 17 00:00:00 2001
From: Mark Abbott
Date: Thu, 30 Oct 2014 13:47:10 +0000
Subject: [PATCH 04/33] Add core DataProvider class, error class, and
associated unit tests
---
src/OEUnit/Data/DataProvider.cls | 141 +++++++++++++++++++++
src/OEUnit/Data/DataProviderError.cls | 18 +++
src/OEUnit/Tests/Data/AllTestSuite.cls | 12 ++
src/OEUnit/Tests/Data/DataProviderTest.cls | 137 ++++++++++++++++++++
4 files changed, 308 insertions(+)
create mode 100644 src/OEUnit/Data/DataProvider.cls
create mode 100644 src/OEUnit/Data/DataProviderError.cls
create mode 100644 src/OEUnit/Tests/Data/AllTestSuite.cls
create mode 100644 src/OEUnit/Tests/Data/DataProviderTest.cls
diff --git a/src/OEUnit/Data/DataProvider.cls b/src/OEUnit/Data/DataProvider.cls
new file mode 100644
index 0000000..f897be9
--- /dev/null
+++ b/src/OEUnit/Data/DataProvider.cls
@@ -0,0 +1,141 @@
+/*------------------------------------------------------------------------------
+ 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 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/Tests/Data/AllTestSuite.cls b/src/OEUnit/Tests/Data/AllTestSuite.cls
new file mode 100644
index 0000000..8772bbf
--- /dev/null
+++ b/src/OEUnit/Tests/Data/AllTestSuite.cls
@@ -0,0 +1,12 @@
+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()).
+ 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..7beb53c
--- /dev/null
+++ b/src/OEUnit/Tests/Data/DataProviderTest.cls
@@ -0,0 +1,137 @@
+
+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 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 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
From d733cbb5df082266823f460a427845ecc586359f Mon Sep 17 00:00:00 2001
From: Mark Abbott
Date: Thu, 30 Oct 2014 13:49:23 +0000
Subject: [PATCH 05/33] Ensure new Data directory is included when all unit
tests are run.
---
src/OEUnit/Tests/AllTestSuite.cls | 1 +
1 file changed, 1 insertion(+)
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()).
From 469cb2c46ca4e10e2dfa2daabdee38b5d908c6dc Mon Sep 17 00:00:00 2001
From: Mark Abbott
Date: Thu, 30 Oct 2014 14:00:47 +0000
Subject: [PATCH 06/33] Define new annotation types, and attribute types
---
src/OEUnit/Runners/OEUnitAnnotations.cls | 17 ++++++++++-------
1 file changed, 10 insertions(+), 7 deletions(-)
diff --git a/src/OEUnit/Runners/OEUnitAnnotations.cls b/src/OEUnit/Runners/OEUnitAnnotations.cls
index 699e3de..2aaa0e5 100644
--- a/src/OEUnit/Runners/OEUnitAnnotations.cls
+++ b/src/OEUnit/Runners/OEUnitAnnotations.cls
@@ -11,16 +11,19 @@ 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.
/*----------------------------------------------------------------------------
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 DataProviderNameAttribute AS CHARACTER NO-UNDO INIT "Name" GET.
END CLASS.
\ No newline at end of file
From 56949acc6619ea570459fd6453de72bbf40ef518 Mon Sep 17 00:00:00 2001
From: Mark Abbott
Date: Mon, 3 Nov 2014 14:18:44 +0000
Subject: [PATCH 07/33] Allow multiple attributes to be defined inside of one
annotation
---
src/OEUnit/Reflection/AnnotationInfo.cls | 48 +++++++++++++++++++
.../Tests/Reflection/AnnotationInfoTest.cls | 33 ++++++++++++-
2 files changed, 80 insertions(+), 1 deletion(-)
diff --git a/src/OEUnit/Reflection/AnnotationInfo.cls b/src/OEUnit/Reflection/AnnotationInfo.cls
index dc46771..d296537 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:
/*----------------------------------------------------------------------------
@@ -20,10 +22,19 @@ CLASS OEUnit.Reflection.AnnotationInfo:
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):
@@ -34,6 +45,43 @@ CLASS OEUnit.Reflection.AnnotationInfo:
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/Tests/Reflection/AnnotationInfoTest.cls b/src/OEUnit/Tests/Reflection/AnnotationInfoTest.cls
index 33aa147..cb49fe5 100644
--- a/src/OEUnit/Tests/Reflection/AnnotationInfoTest.cls
+++ b/src/OEUnit/Tests/Reflection/AnnotationInfoTest.cls
@@ -26,6 +26,9 @@ CLASS OEUnit.Tests.Reflection.AnnotationInfoTest:
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.
@@ -34,6 +37,34 @@ CLASS OEUnit.Tests.Reflection.AnnotationInfoTest:
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
From 25c29a900c2922e943fabe92df1a7e3116bc4830 Mon Sep 17 00:00:00 2001
From: Mark Abbott
Date: Tue, 4 Nov 2014 08:49:21 +0000
Subject: [PATCH 08/33] Remove definitions of, and references to,
AnnotationInfo:AttributeName and AnnotationInfo:AttributeValue. Replace with
calls to AnnotationInfo:HasAttribute and AnnotationInfo:GetAttribute
---
src/OEUnit/Reflection/AnnotationInfo.cls | 4 ----
src/OEUnit/Runners/OEUnitRunner.cls | 8 ++++----
src/OEUnit/Tests/Reflection/AnnotationInfoTest.cls | 7 +------
3 files changed, 5 insertions(+), 14 deletions(-)
diff --git a/src/OEUnit/Reflection/AnnotationInfo.cls b/src/OEUnit/Reflection/AnnotationInfo.cls
index d296537..c1b4505 100644
--- a/src/OEUnit/Reflection/AnnotationInfo.cls
+++ b/src/OEUnit/Reflection/AnnotationInfo.cls
@@ -19,8 +19,6 @@ 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:
@@ -43,8 +41,6 @@ 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).
diff --git a/src/OEUnit/Runners/OEUnitRunner.cls b/src/OEUnit/Runners/OEUnitRunner.cls
index bd0fa08..ea3d548 100644
--- a/src/OEUnit/Runners/OEUnitRunner.cls
+++ b/src/OEUnit/Runners/OEUnitRunner.cls
@@ -208,16 +208,16 @@ CLASS OEUnit.Runners.OEUnitRunner INHERITS BaseRunner:
testAnnotation = testMethod:GetAnnotationOfType(OEUnitAnnotations:Test).
testMethod:Invoke().
- IF testAnnotation:AttributeName = OEUnitAnnotations:TestExpectedAttribute THEN
- RETURN ERROR NEW AssertionFailedError("Expecting error: " + testAnnotation:AttributeValue).
+ 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/Reflection/AnnotationInfoTest.cls b/src/OEUnit/Tests/Reflection/AnnotationInfoTest.cls
index cb49fe5..437f788 100644
--- a/src/OEUnit/Tests/Reflection/AnnotationInfoTest.cls
+++ b/src/OEUnit/Tests/Reflection/AnnotationInfoTest.cls
@@ -16,16 +16,13 @@ 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").
@@ -35,8 +32,6 @@ CLASS OEUnit.Tests.Reflection.AnnotationInfoTest:
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").
Assert:AreEqual(annotation:Attributes:Size, 1).
Assert:IsTrue(annotation:HasAttribute("a")).
Assert:AreEqual(annotation:GetAttribute("a"), "5+1=6").
From 4d8ec2574f2116cdc1fac5b4b40bf5c03ea3c0bf Mon Sep 17 00:00:00 2001
From: Mark Abbott
Date: Tue, 4 Nov 2014 08:54:09 +0000
Subject: [PATCH 09/33] Add methods to MethodInfo to Invoke a method with a
ParameterList object, and to interact with a method's DataProvider object.
---
src/OEUnit/Reflection/MethodInfo.cls | 103 +++++++++++++++++++++++++++
1 file changed, 103 insertions(+)
diff --git a/src/OEUnit/Reflection/MethodInfo.cls b/src/OEUnit/Reflection/MethodInfo.cls
index 92461cf..f109b86 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,106 @@ 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():
+
+ DEFINE VARIABLE annotation AS AnnotationInfo NO-UNDO.
+ DEFINE VARIABLE dataProviderAnnotation AS AnnotationInfo NO-UNDO.
+ DEFINE VARIABLE dataProviders AS List NO-UNDO.
+ DEFINE VARIABLE dataProviderName AS CHARACTER NO-UNDO.
+ DEFINE VARIABLE dataProvider AS MethodInfo NO-UNDO.
+ DEFINE VARIABLE result AS LOGICAL NO-UNDO INITIAL FALSE.
+
+ /* If this is a @Test, with a "DataProvider=" attribute, then attempt to
+ * find the data provider method
+ */
+ annotation = GetAnnotationOfType(OEUnitAnnotations:Test).
+
+ IF annotation NE ? AND annotation:HasAttribute("dataProvider") THEN
+ DO:
+ dataProviderName = annotation:Attributes:Get(OEUnitAnnotations:TestDataProviderAttribute).
+ dataProviders = parentInfo:GetAnnotatedMethods(OEUnitAnnotations:DataProvider).
+
+ /* Find first data provider with that name attribute or method name */
+ dataProviders:MoveFirst().
+ dataProviderLoop:
+ DO WHILE dataProviders:CURRENT NE ?:
+ dataProvider = CAST(dataProviders:CURRENT,"MethodInfo").
+ dataProviderAnnotation = dataProvider:GetAnnotationOfType(OEUnitAnnotations:DataProvider).
+ IF (dataProviderAnnotation:HasAttribute("name") = TRUE) THEN
+ DO:
+ IF dataProviderName = dataProviderAnnotation:Attributes:Get(OEUnitAnnotations:DataProviderNameAttribute) THEN
+ LEAVE dataProviderLoop.
+ END.
+ ELSE IF dataProvider:Name = dataProviderName THEN LEAVE dataProviderLoop.
+ ELSE dataProvider = ?.
+ dataProviders:MoveNext().
+ END.
+ END.
+
+ RETURN dataProvider.
+ 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.
+
END CLASS.
From 9265be3d2707ff273a4ba8cd2ff2805231d48f3d Mon Sep 17 00:00:00 2001
From: Mark Abbott
Date: Tue, 4 Nov 2014 13:38:20 +0000
Subject: [PATCH 10/33] Change OEUnitRunner to recognise test methods which
require a DataProvider, and to invoke the test method correctly.
---
src/OEUnit/Runners/OEUnitRunner.cls | 57 +++++++++++++++++++++++++++--
1 file changed, 53 insertions(+), 4 deletions(-)
diff --git a/src/OEUnit/Runners/OEUnitRunner.cls b/src/OEUnit/Runners/OEUnitRunner.cls
index ea3d548..e7daf4b 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,20 +159,56 @@ 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. 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 methodResult AS TestMethodResult NO-UNDO.
methodResult = NEW TestMethodResult(testMethod).
@@ -181,7 +218,7 @@ CLASS OEUnit.Runners.OEUnitRunner INHERITS BaseRunner:
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).
@@ -203,11 +240,23 @@ 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().
+ 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)).
From 0d30ab1065d31da7ce5ef5b0ad5e7393d7c02e35 Mon Sep 17 00:00:00 2001
From: Mark Abbott
Date: Wed, 5 Nov 2014 08:51:56 +0000
Subject: [PATCH 11/33] Add documentation for DataProviders
---
doc/changelog.html | 1 +
doc/dataprovider.html | 175 ++++++++++++++++++++++++++++++++++++++++
doc/index.html | 1 +
doc/installation.html | 1 +
doc/reportingtests.html | 1 +
doc/runningtests.html | 1 +
doc/testcase.html | 14 +++-
doc/testsuite.html | 1 +
8 files changed, 193 insertions(+), 2 deletions(-)
create mode 100644 doc/dataprovider.html
diff --git a/doc/changelog.html b/doc/changelog.html
index 70d41bc..8904116 100644
--- a/doc/changelog.html
+++ b/doc/changelog.html
@@ -16,6 +16,7 @@
+ 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:
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.
ErrorTypeName
The Type-Name of a class that inherits from Progress.Lang.Error.
@@ -85,6 +86,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 +126,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.