2003-01-27 15:27:53 +00:00
|
|
|
/*************************************************************************
|
|
|
|
*
|
|
|
|
* $RCSfile: MultiMethodTest.java,v $
|
|
|
|
*
|
2004-11-02 10:36:44 +00:00
|
|
|
* $Revision: 1.2 $
|
2003-01-27 15:27:53 +00:00
|
|
|
*
|
2004-11-02 10:36:44 +00:00
|
|
|
* last change: $Author: pjunck $ $Date: 2004-11-02 11:36:44 $
|
2003-01-27 15:27:53 +00:00
|
|
|
*
|
|
|
|
* The Contents of this file are made available subject to the terms of
|
|
|
|
* either of the following licenses
|
|
|
|
*
|
|
|
|
* - GNU Lesser General Public License Version 2.1
|
|
|
|
* - Sun Industry Standards Source License Version 1.1
|
|
|
|
*
|
|
|
|
* Sun Microsystems Inc., October, 2000
|
|
|
|
*
|
|
|
|
* GNU Lesser General Public License Version 2.1
|
|
|
|
* =============================================
|
|
|
|
* Copyright 2000 by Sun Microsystems, Inc.
|
|
|
|
* 901 San Antonio Road, Palo Alto, CA 94303, USA
|
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License version 2.1, as published by the Free Software Foundation.
|
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
* License along with this library; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
|
|
|
|
* MA 02111-1307 USA
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* Sun Industry Standards Source License Version 1.1
|
|
|
|
* =================================================
|
|
|
|
* The contents of this file are subject to the Sun Industry Standards
|
|
|
|
* Source License Version 1.1 (the "License"); You may not use this file
|
|
|
|
* except in compliance with the License. You may obtain a copy of the
|
|
|
|
* License at http://www.openoffice.org/license.html.
|
|
|
|
*
|
|
|
|
* Software provided under this License is provided on an "AS IS" basis,
|
|
|
|
* WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
|
|
|
|
* WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
|
|
|
|
* MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
|
|
|
|
* See the License for the specific provisions governing your rights and
|
|
|
|
* obligations concerning the Software.
|
|
|
|
*
|
|
|
|
* The Initial Developer of the Original Code is: Sun Microsystems, Inc.
|
|
|
|
*
|
|
|
|
* Copyright: 2000 by Sun Microsystems, Inc.
|
|
|
|
*
|
|
|
|
* All Rights Reserved.
|
|
|
|
*
|
|
|
|
* Contributor(s): _______________________________________
|
|
|
|
*
|
|
|
|
*
|
|
|
|
************************************************************************/
|
|
|
|
|
|
|
|
package lib;
|
|
|
|
|
|
|
|
import java.io.PrintWriter;
|
|
|
|
import java.lang.reflect.Field;
|
|
|
|
import java.lang.reflect.InvocationTargetException;
|
|
|
|
import java.lang.reflect.Method;
|
|
|
|
import java.util.Hashtable;
|
|
|
|
import java.util.StringTokenizer;
|
|
|
|
import java.util.Vector;
|
|
|
|
|
|
|
|
import com.sun.star.uno.UnoRuntime;
|
|
|
|
import com.sun.star.uno.XInterface;
|
|
|
|
|
|
|
|
import share.DescEntry;
|
|
|
|
import lib.TestParameters;
|
|
|
|
import stats.Summarizer;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The class supports method based interface tests development.
|
|
|
|
*
|
|
|
|
* <p>There are some points that should be fulfilled in a subclass to work
|
|
|
|
* correctly in the multi-method framework:
|
|
|
|
*
|
|
|
|
* 1. each subclass schould define a public field named oObj of type tested
|
|
|
|
* by the subclass, e.g. 'public XText oObj;'. That field will be initialized
|
|
|
|
* by the MultiMethodTest code with the instance of the interface to test.
|
|
|
|
* In a case of service testing the field type should be XPropertySet.
|
|
|
|
*
|
|
|
|
* 2. for the test of each method of the tested interface(or a property in the
|
|
|
|
* case of service testing) should be method with the following signature
|
|
|
|
* provided: 'public void _<method name>()', e.g. 'public void _getText()'.
|
|
|
|
* The methods will be called by MultiMethodText code using reflection API
|
|
|
|
* for each method in the interface description.
|
|
|
|
*
|
|
|
|
* 3. to set status for a call 'tRes.tested(String method,
|
|
|
|
* boolean result)' should be used. For example 'tRes.tested("getText()",
|
|
|
|
* true)'. Also 'tRes.assert(String assertion, boolean result)' call can
|
|
|
|
* be used. Note, that one can call the methods not neccesarily from the
|
|
|
|
* test for the tested method, but from other method tests too (in the
|
|
|
|
* MultiMethodTest subclass). See also TestResult and MultiMethodTest.tRes
|
|
|
|
* documentation.
|
|
|
|
*
|
|
|
|
* 4. the before() and after() methods can be overriden to perform some
|
|
|
|
* actions, accordingly, before and after calling the test methods.
|
|
|
|
*
|
|
|
|
* 5. besides tRes, there are some fields initialized in the MultiMethodTest,
|
|
|
|
* that can be used for implementing tests:
|
|
|
|
*
|
|
|
|
* - tEnv contains the environment tested
|
|
|
|
* - tParam contains parameters of the test
|
|
|
|
* - log a writer to log information about the test
|
|
|
|
*
|
|
|
|
* @see TestResult
|
|
|
|
*/
|
|
|
|
public class MultiMethodTest {
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Contains the TestEnvironment being tested, to allow for tests to access
|
|
|
|
* it.
|
|
|
|
*/
|
|
|
|
protected TestEnvironment tEnv;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Contains the TestParameters for the tests, to allow for tests to access
|
|
|
|
* it.
|
|
|
|
*/
|
|
|
|
protected TestParameters tParam;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Contains the Description for the test
|
|
|
|
* it.
|
|
|
|
*/
|
|
|
|
protected DescEntry entry;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Contains a writer to log an information about the interface testing, to
|
|
|
|
* allows for tests to access it.
|
|
|
|
*/
|
|
|
|
protected PrintWriter log;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Contains the TestResult instance for the interface test to collect
|
|
|
|
* information about methods test.
|
|
|
|
*/
|
|
|
|
protected TestResult tRes;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Contains names of the methods have been alreadycalled
|
|
|
|
*/
|
|
|
|
private Vector methCalled = new Vector(10);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Disposes the test environment, which was corrupted by the test.
|
|
|
|
*
|
|
|
|
* @param tEnv the environment to dispose
|
|
|
|
*/
|
|
|
|
public void disposeEnvironment( TestEnvironment tEnv ) {
|
|
|
|
disposeEnvironment();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Disposes the current test environment, which was corrupted by the test.
|
|
|
|
*
|
|
|
|
* @see #disposeEnvironment(TestEnvironment)
|
|
|
|
*/
|
|
|
|
public void disposeEnvironment() {
|
|
|
|
tEnv.dispose();
|
|
|
|
TestCase tCase = tEnv.getTestCase();
|
|
|
|
tCase.disposeTestEnvironment( tEnv, tParam );
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Runs the interface test: its method tests. First, it initializes some
|
|
|
|
* of MultiMethodTest fields, like tRes, log, tEnv, etc. Then, it queries
|
|
|
|
* the tested interface and initializes 'oObj' field (defined in a
|
|
|
|
* subclass). Before calling method tests, before() method calles to allow
|
|
|
|
* initialization of s stuff before testing. Then, the method tests are
|
|
|
|
* called. After them, after() method is called, to allow cleaning up the
|
|
|
|
* stuff initialized in before() and test methods.
|
|
|
|
*
|
|
|
|
* @param tEnv the environment to test
|
|
|
|
* @param ifcState the interface test state
|
|
|
|
* @param tParam the parameters of the test
|
|
|
|
*
|
|
|
|
* @see #before
|
|
|
|
* @see #after
|
|
|
|
*/
|
|
|
|
public TestResult run( DescEntry entry, TestEnvironment tEnv, TestParameters tParam ) {
|
|
|
|
|
|
|
|
log = (PrintWriter) entry.Logger;
|
|
|
|
|
|
|
|
this.tEnv = tEnv;
|
|
|
|
this.tParam = tParam;
|
|
|
|
this.log = log;
|
|
|
|
this.entry = entry;
|
|
|
|
this.tRes = new TestResult();
|
|
|
|
Class testedClass;
|
|
|
|
|
|
|
|
String ifcName = getInterfaceName();
|
|
|
|
System.out.println("checking : "+ifcName);
|
|
|
|
|
|
|
|
// defining a name of the class corresponding to the tested interface
|
|
|
|
// or service
|
|
|
|
String testedClassName;
|
|
|
|
|
|
|
|
testedClassName = getTestedClassName();
|
|
|
|
|
|
|
|
if (entry.EntryType.equals("service")) {
|
|
|
|
testedClassName = "com.sun.star.beans.XPropertySet";
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
testedClass = Class.forName(testedClassName);
|
|
|
|
} catch (ClassNotFoundException cnfE) {
|
|
|
|
|
|
|
|
cnfE.printStackTrace(log);
|
2004-11-02 10:36:44 +00:00
|
|
|
log.println("could not find a class : " + getTestedClassName());
|
2003-01-27 15:27:53 +00:00
|
|
|
return null;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// quering the tested interface from the tested object
|
|
|
|
XInterface tCase = tEnv.getTestObject();
|
|
|
|
Object oObj = UnoRuntime.queryInterface(
|
|
|
|
testedClass, tEnv.getTestObject());
|
|
|
|
|
|
|
|
if (oObj == null) {
|
|
|
|
if (entry.isOptional) {
|
|
|
|
Summarizer.summarizeDown(
|
|
|
|
entry,"Not supported but optional.OK");
|
|
|
|
} else {
|
|
|
|
Summarizer.summarizeDown(
|
|
|
|
entry,"queryInterface returned null.FAILED");
|
|
|
|
entry.ErrorMsg="queryInterface returned null";
|
|
|
|
entry.hasErrorMsg=true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
//setting the field oObj
|
|
|
|
setField("oObj",oObj);
|
|
|
|
|
|
|
|
|
|
|
|
// to perform some stuff before all method tests
|
|
|
|
try {
|
|
|
|
before();
|
|
|
|
} catch (Exception e){
|
|
|
|
setSubStates(e.toString());
|
|
|
|
return tRes;
|
|
|
|
}
|
|
|
|
|
|
|
|
// executing methods tests
|
|
|
|
for (int i=0;i<entry.SubEntryCount;i++) {
|
|
|
|
try {
|
|
|
|
executeMethod( entry.SubEntries[i].entryName );
|
|
|
|
} catch (Exception e){
|
|
|
|
log.println("Exception while checking: "+
|
|
|
|
entry.SubEntries[i].entryName+" : "+e.getMessage());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// to perform some stuff after all method tests
|
|
|
|
try {
|
|
|
|
after();
|
|
|
|
} catch (Exception e){}
|
|
|
|
|
|
|
|
return tRes;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Is called before calling method tests, but after initialization.
|
|
|
|
* Subclasses may override to perform actions before method tests.
|
|
|
|
*/
|
|
|
|
protected void before() {
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Is called after calling method tests. Subclasses may override
|
|
|
|
* to perform actions after method tests.
|
|
|
|
*/
|
|
|
|
protected void after() {
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return the name of the interface or the service tested.
|
|
|
|
*/
|
|
|
|
protected String getTestedClassName() {
|
|
|
|
String clsName = this.getClass().getName();
|
|
|
|
|
|
|
|
int firstDot = clsName.indexOf(".");
|
|
|
|
int lastDot = clsName.lastIndexOf(".");
|
|
|
|
|
|
|
|
String append = "com.sun.star.";
|
|
|
|
|
|
|
|
if (entry.longName.indexOf("::drafts::com::")>-1) {
|
|
|
|
append = "drafts.com.sun.star.";
|
|
|
|
}
|
|
|
|
|
|
|
|
return append
|
|
|
|
+ clsName.substring(firstDot + 1, lastDot + 1)
|
|
|
|
+ clsName.substring(lastDot + 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets a method status.
|
|
|
|
*
|
|
|
|
* @param methName the method name to set status
|
|
|
|
* @param methStatus the status to set to the method
|
|
|
|
*/
|
|
|
|
protected void setStatus( String methName, Status methStatus ) {
|
|
|
|
tRes.tested( methName, methStatus );
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* sets the substates
|
|
|
|
*/
|
|
|
|
protected void setSubStates(String msg) {
|
|
|
|
for (int k=0;k<entry.SubEntryCount;k++) {
|
|
|
|
entry.SubEntries[k].hasErrorMsg=true;
|
|
|
|
entry.SubEntries[k].ErrorMsg=msg;
|
|
|
|
if (entry.SubEntries[k].State.equals("UNKNOWN")) {
|
|
|
|
entry.SubEntries[k].State = msg;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Checks if the <code>method</code> is optional in the service.
|
|
|
|
*/
|
|
|
|
protected boolean isOptional(String method) {
|
|
|
|
for (int k=0;k<entry.SubEntryCount;k++) {
|
|
|
|
if (entry.SubEntries[k].entryName.equals(method)) {
|
|
|
|
return entry.SubEntries[k].isOptional;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Checks if the <code>method</code> test has been already called.
|
|
|
|
*/
|
|
|
|
protected boolean isCalled( String method ) {
|
|
|
|
return methCalled.contains( method );
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Calling of the method indicates that the <code>method</code> test should
|
|
|
|
* be called. The method checks this and if it is not called, calls it.
|
|
|
|
* If the method is failed or skipped, it throws StatusException.
|
|
|
|
*/
|
|
|
|
protected void requiredMethod(String method) {
|
2004-11-02 10:36:44 +00:00
|
|
|
log.println("starting required method: " + method);
|
2003-01-27 15:27:53 +00:00
|
|
|
executeMethod( method );
|
2004-11-02 10:36:44 +00:00
|
|
|
Status mtStatus = tRes.getStatusFor( method );
|
2003-01-27 15:27:53 +00:00
|
|
|
|
|
|
|
if ( mtStatus != null
|
|
|
|
&& (!mtStatus.isPassed() || mtStatus.isFailed()) ) {
|
|
|
|
log.println( "! Required method " + method + " failed" );
|
|
|
|
throw new StatusException( mtStatus );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Checks if the <code>method</code> was called, and if not, call it.
|
|
|
|
* On contrary to requiredMethod(), he method doesn't check its status.
|
|
|
|
*/
|
|
|
|
protected void executeMethod( String method ) {
|
|
|
|
if ( ! isCalled( method ) ) {
|
2004-11-02 10:36:44 +00:00
|
|
|
log.println("Execute: "+ method);
|
2003-01-27 15:27:53 +00:00
|
|
|
callMethod( method );
|
2004-11-02 10:36:44 +00:00
|
|
|
log.println(method + ": " + tRes.getStatusFor(method));
|
2003-01-27 15:27:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Just calls the <code>method</code> test.
|
|
|
|
*/
|
|
|
|
protected void callMethod(String method) {
|
|
|
|
methCalled.add(method);
|
|
|
|
invokeTestMethod(getMethodFor(method), method);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Invokes a test method of the subclass using reflection API. Handles
|
|
|
|
* the method results and sets its status.
|
|
|
|
*
|
|
|
|
* @param meth the subclass' method to invoke
|
|
|
|
* @param methName the name of the method
|
|
|
|
*/
|
|
|
|
protected void invokeTestMethod(Method meth, String methName) {
|
|
|
|
if (meth == null) {
|
|
|
|
setStatus( methName, Status.skipped( false ) );
|
|
|
|
} else {
|
|
|
|
Status stat;
|
|
|
|
|
|
|
|
try {
|
|
|
|
meth.invoke(this, new Object[0]);
|
|
|
|
return;
|
|
|
|
} catch (InvocationTargetException itE) {
|
|
|
|
Throwable t = itE.getTargetException();
|
|
|
|
|
|
|
|
if ( t instanceof StatusException ) {
|
|
|
|
stat = ((StatusException)t).getStatus();
|
|
|
|
} else {
|
|
|
|
t.printStackTrace(log);
|
|
|
|
stat = Status.exception(t);
|
|
|
|
}
|
|
|
|
} catch (IllegalAccessException iaE) {
|
|
|
|
iaE.printStackTrace(log);
|
|
|
|
stat = Status.exception(iaE);
|
|
|
|
} catch (IllegalArgumentException iaE) {
|
|
|
|
iaE.printStackTrace(log);
|
|
|
|
stat = Status.exception(iaE);
|
|
|
|
} catch (ClassCastException ccE) {
|
|
|
|
ccE.printStackTrace(log);
|
|
|
|
stat = Status.exception(ccE);
|
|
|
|
}
|
|
|
|
|
|
|
|
setStatus(methName, stat);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Finds a testing method for the <code>method</code> of the interface.
|
|
|
|
*
|
|
|
|
* @return the testing method, if found, <tt>null</tt> otherwise
|
|
|
|
*/
|
|
|
|
protected Method getMethodFor(String method) {
|
|
|
|
String mName = "_" + method;
|
|
|
|
|
|
|
|
if (mName.endsWith("()")) {
|
|
|
|
mName = mName.substring(0, mName.length() - 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
final Class[] paramTypes = new Class[0];
|
|
|
|
|
|
|
|
try {
|
|
|
|
return this.getClass().getDeclaredMethod(mName, paramTypes);
|
|
|
|
} catch (NoSuchMethodException nsmE) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return the name of the interface tested
|
|
|
|
*/
|
|
|
|
public String getInterfaceName() {
|
|
|
|
String clName = this.getClass().getName();
|
|
|
|
return clName.substring( clName.lastIndexOf('.') + 1 );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Initializes <code>fieldName</code> of the subclass with
|
|
|
|
* <code>value</code>.
|
|
|
|
*
|
|
|
|
* @return Status describing the result of the operation.
|
|
|
|
*/
|
|
|
|
protected Status setField( String fieldName, Object value) {
|
|
|
|
Field objField;
|
|
|
|
|
|
|
|
try {
|
|
|
|
objField = this.getClass().getField( fieldName );
|
|
|
|
}
|
|
|
|
catch( NoSuchFieldException nsfE ){
|
|
|
|
return Status.exception( nsfE );
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
objField.set( this, value );
|
|
|
|
return Status.passed(true);
|
|
|
|
}
|
|
|
|
catch( IllegalArgumentException iaE ){
|
|
|
|
return Status.exception( iaE );
|
|
|
|
}
|
|
|
|
catch( IllegalAccessException iaE ){
|
|
|
|
return Status.exception( iaE );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|