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:

    + +

    +
    + 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:

    + +

    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:

    + +

    METHOD PUBLIC LOGICAL FromXML(INPUT xml AS LONGCHAR)

    +

    This method is used to load data from XML data stored in a LONGCHAR.

    +

    + +
    + Notes:

    + +

    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:

    + +

    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:

    + + + + 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:

    + +

    +
    + 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:

    + +

    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:

    + +

    METHOD PUBLIC LOGICAL FromXML(INPUT xml AS LONGCHAR)

    +

    This method is used to load data from XML data stored in a LONGCHAR.

    +

    + +
    + Notes:

    + +

    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:

    + +

    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:

    + +

    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:

    + + + + 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). ![Image](/doc/images/oea_example1.png?raw=true) -##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