SourceForge > WSRPtk
 

Programming New Test Assertions

Introduction

The following sections provide an overview of the organization of the Java classes which makeup the test kit. The test kit extends the WS-I implementation and relies on the behaviour of existing base classes wherever possible.

Factory

There is an extension point in the WS-I framework which allows us to insert our own factory implementation during initialization. We use these factories to cause our specialized classes to be constructed. The wsi.properties file specifies our factory:

# -----------------------------------------------------------------------
# Profile validation factory.
# -----------------------------------------------------------------------
wsi.profile.validator.factory=org.oasis.wsrp.test.impl.WSRPValidatorFactoryImpl

WS-I Base Validator

Refer to the diagram below. The classes in yellow are the WS-I base classes and the classes in white are the WSRP extensions. The WSRPValidatorFactoryImpl creates the WSRP specific extensions when the Analyzer is started. The WSRPMessageValidatorImpl class plays an important role in the execution of Test Assertions because there is only one instance of this class for all message Test Assertions. The WSRPMessageValidatorImpl instance is used to share data between Test Assertions or between messages. This is discussed in more detail in a subsequent section. Relationships among base validateor implementations and extensions

In the WSRPTK, there are very few Test Assertions which deal with the WSDL. The vast majority of Test Assertions are focused on examining the messages in the log file. In general, there is a symmetrical treatment of message artifacts and description artifacts. For the next few sections, only the structure and relationships among the classes involved in the message processing side of the system are discussed. Classes which handle description artifacts may be included on the diagrams, but are not distinguished further.

Extending AssertionProcess

Refer to the diagram below. Again, the yellow classes are WS-I base classes, and the white classes are WSRP specific. The small class, RP0090, at the bottom of the diagram is an example of a Test Assertion. Test Assertions ultimately extend AssertionProcess and get a reference to WSRPMessageValidatorImpl

Ultimately, all Test Assertions are derived from AssertionProcess. This class declares the validate() method that all Test Assertions must implement. The AssertionProcess class also declares a member variable to hold an instance of BaseValidatorImpl.

WSRPTK introduces 2 intermediate classes between Test Assertions and AssertionProcess. WSRPBaseAssertionProcess is used to implement convenience methods such as pass(), fail(), etc, for setting the result of a Test Assertion, regardless of whether the Test Assertion is on the message artifact side or the description artifact side.

Constructor Hack

WSRPMessageAssertionProcess extends the WSRPBaseAssertionProcess in order to declare the instance of the specialized WSRPMessageValidatorImpl to which all the Test Assertions need access for data sharing. The BaseValidatorImpl is responsible for parsing and loading the message log file and then creating and invoking the Test Assertions in the Profile. When a Test Assertion Java class, e.g. RP0090, gets initialized, the BaseValidatorImpl passes an instance of BaseMessageValidatorImpl into the constructor. The Test Assertion calls the constructor on it's superclass, WSRPMessageAssertionProcess, passing the BaseMessageValidatorImpl. The superclass forces a cast of the BaseMessageValidatorImpl to WSRPMessageValidatorImpl and saves it in a protected member variable called validator.

This hack is necessary for historical reasons. The original WS-I framework was organized so that all the Test Assertion classes were inner classes of the WSRPMessageValidatorImpl class, and so they had direct access to its methods and state. At some point, the architecture was changed so that the Test Assertions were separated into individual files and this hack became necessary to preserve access to the specialized methods and state of the WSRPMessageValidatorImpl.

WSRPMessageValidatorImpl

As explained above, there is a single instance of this class which is available to all Test Assertions through the use of the protected variable validator. This class offers many convenience methods as well as saving state between messages. Let's look at the following Test Assertion:

RP0510

The value supplied in MarkupParams.windowState shall be either "wsrp:view" or one of the values from the PortletDescription.markupTypes[].windowStates for a matching mime type, which has to have a value

This Test Assertion looks at a getMarkupRequest and has to check the value of window state against a set of valid window states which were supplied in the getServiceDescriptionResponse. In the following code fragment, RP0510 uses convenience methods from the WSRPMessageValidatorImpl to locate the offeredPortlet from the saved getServiceDescriptionResponse.

//-----------------------------------
// we got a portlet handle out of the request, so
// lookup the offeredPortlet in the getServiceDescriptionResponse which was
// previously saved
regHandle = validator.findRegistrationHandle((Node) doc.getDocumentElement());
portletHandle = validator.checkForClonedPortlet(portletHandle, regHandle);
offeredPortlet = validator.findOfferedPortlet(portletHandle);
if (offeredPortlet == null) {
// this means the portlet was not found in the serviceDescriptionResponse
fail("Cannot find portlet with handle: " +
portletHandle +
" in serviceDescriptionResponse");
done = true;
}

This Test Assertion relies on the fact that another Test Assertion, RP0320 saves the serviceDescriptionResponse. Note that if a log file is analyzed that does not contain the serviceDescriptionResponse message, then any Test Assertion that relies on it will fail.

The WSRPMessageValidatorImpl has many methods which are beyond the scope of this tutorial. The reader is encouraged to examine the source code. The following diagram shows the WSRPMessageValidatorImpl and all the classes it references. Not all the methods are visible. WSRPMessageValidatorImpl

Choosing a Base Class to Extend

There are several choices as to which class to extend to create a new Test Assertion. The WSRPBaseAssertionProcess class, the BaseRP class, CustomItemChecker, or an existing Test Assertion. The diagram below shows several Test Assertions and the classes they extend. CustomItemChecker is specialized and is used to parse the markup have a listener called when certain things are found. The BaseRP class checks the message type and then delegates handling of the message to the Test Assertion subclass. This design pattern was introduced after many Test Assertions were already implemented, so you may discover Test Assertions that would have benefited from this approach. WSRPMessageAssertionProcess

Worked Example

Let's look at a simple Test Assertion.

RP0090

Every time an extension element appears in a WSRP message, it's child element shall use the xsi:type attribute to declare its type.

Here is the source code:

/*
* Copyright (c) 2002-2005 IBM Corporation. All rights reserved.
*
* =============================================
*
* @author Julie MacNaught jmacna@us.ibm.com
* @author Martin Fanta Martin.Fanta@cz.ibm.com
*
*/
package org.oasis.wsrp.test.impl.message;

import javax.xml.transform.TransformerException;

import org.eclipse.wst.wsi.internal.core.WSIException;
import org.eclipse.wst.wsi.internal.core.profile.TestAssertion;
import org.eclipse.wst.wsi.internal.core.profile.validator.EntryContext;
import org.eclipse.wst.wsi.internal.core.profile.validator.impl.BaseMessageValidator;
import org.eclipse.wst.wsi.internal.core.report.AssertionResult;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
* Assertion: Every time an extension element appears in a WSRP message, it's child
* element shall use the xsi:type attribute to declare its type.
*
* @author Julie MacNaught (jmacna@us.ibm.com)
* @author Martin Fanta (Martin.Fanta@cz.ibm.com)
*
*/

public class RP0090 extends WSRPMessageAssertionProcess {

public RP0090(BaseMessageValidator impl) {
  super(impl);
}

public AssertionResult validate(TestAssertion testAssertion, EntryContext entryContext) throws WSIException {
  if (validator.isOneWayResponse(entryContext)) {
    na();
  } else {
    try {
      // parse the request message
      Document reqDoc = entryContext.getRequestDocument();
      // get all the extension elements included in the request
      NodeList extNodes = NodeUtils.getNodes(reqDoc, "extension");
      int extNodeCount = extNodes.getLength();
      if (extNodeCount < 1) {
        na();
      } else {
        Node extNode, childNode;
        NamedNodeMap attribs;
        for (int ii = 0; ii < extNodeCount; ii++) {
          extNode = extNodes.item(ii);
          childNode = extNode.getFirstChild();
          if (childNode != null) {
            attribs = childNode.getAttributes();
            if (attribs.getNamedItem("xsi:type") != null) {
              // it is probably sufficient to assume that if the attribute is present,
              // the condition of this test case is fulfilled
              pass();
            } else {
              fail("If an extension element is present, its child has to have the 'xsi:type' attribute defined");
            }
          } else {
            warn("Extension element has no children");
          }
        }
      }
    } catch (TransformerException te) {
      te.printStackTrace();
      fail(te.getMessage());
    }
  }
  return createAssertionResult(testAssertion, result, failureDetailMessage);
}
}

This Test Assertion extends WSRPBaseAssertionProcess and implements the validate() method. It also implements a constructor so it can establish a handle to the WSRPMessageValidatorImpl class. It uses a class called NodeUtils which implements various shortcuts to XPATH for finding nodes in messages.