Files
libreoffice/qadevOOo/runner/lib/MultiMethodTest.java

503 lines
15 KiB
Java
Raw Normal View History

2012-06-29 14:02:24 +01:00
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 .
*/
2003-01-27 15:27:53 +00:00
package lib;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
2003-01-27 15:27:53 +00:00
import share.DescEntry;
import stats.Summarizer;
import com.sun.star.uno.UnoRuntime;
import com.sun.star.uno.XInterface;
2003-01-27 15:27:53 +00:00
/**
* 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
{
2003-01-27 15:27:53 +00:00
/**
* 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 already called
2003-01-27 15:27:53 +00:00
*/
private ArrayList<String> methCalled = new ArrayList<String>(10);
2003-01-27 15:27:53 +00:00
/**
* Disposes the test environment, which was corrupted by the test.
*
* @param tEnv the environment to dispose
*/
public void disposeEnvironment(TestEnvironment tEnv)
{
2003-01-27 15:27:53 +00:00
disposeEnvironment();
}
/**
* Disposes the current test environment, which was corrupted by the test.
*
* @see #disposeEnvironment(TestEnvironment)
*/
public void disposeEnvironment()
{
2003-01-27 15:27:53 +00:00
tEnv.dispose();
TestCase tCase = tEnv.getTestCase();
tCase.disposeTestEnvironment(tEnv, tParam);
2003-01-27 15:27:53 +00:00
}
/**
* 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 entry the interface test state
2003-01-27 15:27:53 +00:00
* @param tEnv the environment to test
* @param tParam the parameters of the test
*
* @see #before
* @see #after
*/
public TestResult run(DescEntry entry, TestEnvironment tEnv, TestParameters tParam)
{
2003-01-27 15:27:53 +00:00
log = (PrintWriter) entry.Logger;
this.tEnv = tEnv;
this.tParam = tParam;
// this.log = log;
2003-01-27 15:27:53 +00:00
this.entry = entry;
this.tRes = new TestResult();
Class<?> testedClass;
2003-01-27 15:27:53 +00:00
// Some fake code for a self test.
// For normal test we must not be a "ifc.qadevooo._SelfTest"
2011-08-14 17:55:24 +02:00
if (! ("ifc.qadevooo._SelfTest").equals(entry.entryName))
{
String ifcName = getInterfaceName();
2010-02-09 11:18:22 +01:00
// System.out.println("checking : " + ifcName);
System.out.print("checking: [" + entry.longName + "]");
2003-01-27 15:27:53 +00:00
// defining a name of the class corresponding to the tested interface
// or service
String testedClassName;
2003-01-27 15:27:53 +00:00
testedClassName = getTestedClassName();
2003-01-27 15:27:53 +00:00
if (entry.EntryType.equals("service"))
{
testedClassName = "com.sun.star.beans.XPropertySet";
}
2003-01-27 15:27:53 +00:00
try
{
testedClass = Class.forName(testedClassName);
}
catch (ClassNotFoundException cnfE)
{
2010-02-09 11:18:22 +01:00
System.out.println();
cnfE.printStackTrace(log);
log.println("could not find a class : " + getTestedClassName());
return null;
2003-01-27 15:27:53 +00:00
}
2010-02-09 11:18:22 +01:00
System.out.println(" is iface: [" + testedClassName + "] testcode: [" + entry.entryName + "]");
2003-01-27 15:27:53 +00:00
// 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;
}
2003-01-27 15:27:53 +00:00
return null;
}
2003-01-27 15:27:53 +00:00
//setting the field oObj
setField("oObj", oObj);
}
2003-01-27 15:27:53 +00:00
// to perform some stuff before all method tests
try
{
2003-01-27 15:27:53 +00:00
before();
}
catch (Exception e)
{
e.printStackTrace();
2003-01-27 15:27:53 +00:00
setSubStates(e.toString());
return tRes;
}
// executing methods tests
for (int i = 0; i < entry.SubEntryCount; i++)
{
DescEntry aSubEntry = entry.SubEntries[i];
try
{
2010-02-09 11:18:22 +01:00
final String sEntryName = aSubEntry.entryName;
executeMethod(sEntryName);
}
catch (Exception e)
{
log.println("Exception while checking: " + aSubEntry.entryName + " : " + e.getMessage());
2003-01-27 15:27:53 +00:00
}
}
// to perform some stuff after all method tests
try
{
2003-01-27 15:27:53 +00:00
after();
}
catch (Exception e)
{
}
2003-01-27 15:27:53 +00:00
return tRes;
}
/**
* Is called before calling method tests, but after initialization.
* Subclasses may override to perform actions before method tests.
*/
2010-02-09 11:18:22 +01:00
protected void before()
{
2003-01-27 15:27:53 +00:00
}
/**
* Is called after calling method tests. Subclasses may override
* to perform actions after method tests.
*/
protected void after()
{
2003-01-27 15:27:53 +00:00
}
/**
* @return the name of the interface or the service tested.
*/
protected String getTestedClassName()
{
2003-01-27 15:27:53 +00:00
String clsName = this.getClass().getName();
2011-08-14 17:55:24 +02:00
int firstDot = clsName.indexOf('.');
int lastDot = clsName.lastIndexOf('.');
2003-01-27 15:27:53 +00:00
String append = "com.sun.star.";
if (entry.longName.indexOf("::drafts::com::") > -1)
{
2003-01-27 15:27:53 +00:00
append = "drafts.com.sun.star.";
}
return append + clsName.substring(firstDot + 1, lastDot + 1) + clsName.substring(lastDot + 2);
2003-01-27 15:27:53 +00:00
}
/**
* 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);
2003-01-27 15:27:53 +00:00
}
/**
* 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"))
{
2003-01-27 15:27:53 +00:00
entry.SubEntries[k].State = msg;
}
}
}
/**
* Checks if the <code>method</code> is optional in the service.
*/
2010-02-09 11:18:22 +01:00
protected boolean isOptional(String _method)
{
for (int k = 0; k < entry.SubEntryCount; k++)
{
2010-02-09 11:18:22 +01:00
final String sName = entry.SubEntries[k].entryName;
if (sName.equals(_method))
{
2010-02-09 11:18:22 +01:00
final boolean bIsOptional = entry.SubEntries[k].isOptional;
return bIsOptional;
2003-01-27 15:27:53 +00:00
}
}
return false;
}
/**
* Checks if the <code>method</code> test has been already called.
*/
protected boolean isCalled(String method)
{
return methCalled.contains(method);
2003-01-27 15:27:53 +00:00
}
/**
* 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)
{
log.println("starting required method: " + method);
executeMethod(method);
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);
2003-01-27 15:27:53 +00:00
}
}
/**
* 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))
{
log.println("Execute: " + method);
callMethod(method);
log.println(method + ": " + tRes.getStatusFor(method));
2010-02-09 11:18:22 +01:00
log.println();
2003-01-27 15:27:53 +00:00
}
}
/**
* Just calls the <code>method</code> test.
*/
protected void callMethod(String method)
{
2003-01-27 15:27:53 +00:00
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
{
2003-01-27 15:27:53 +00:00
Status stat;
try
{
2003-01-27 15:27:53 +00:00
meth.invoke(this, new Object[0]);
return;
}
catch (InvocationTargetException itE)
{
2003-01-27 15:27:53 +00:00
Throwable t = itE.getTargetException();
if (t instanceof StatusException)
{
stat = ((StatusException) t).getStatus();
}
else
{
2003-01-27 15:27:53 +00:00
t.printStackTrace(log);
stat = Status.exception(t);
}
}
catch (IllegalAccessException iaE)
{
2003-01-27 15:27:53 +00:00
iaE.printStackTrace(log);
stat = Status.exception(iaE);
}
catch (IllegalArgumentException iaE)
{
2003-01-27 15:27:53 +00:00
iaE.printStackTrace(log);
stat = Status.exception(iaE);
}
catch (ClassCastException ccE)
{
2003-01-27 15:27:53 +00:00
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)
{
2003-01-27 15:27:53 +00:00
String mName = "_" + method;
if (mName.endsWith("()"))
{
2003-01-27 15:27:53 +00:00
mName = mName.substring(0, mName.length() - 2);
}
final Class<?>[] paramTypes = new Class[0];
2003-01-27 15:27:53 +00:00
try
{
2003-01-27 15:27:53 +00:00
return this.getClass().getDeclaredMethod(mName, paramTypes);
}
catch (NoSuchMethodException nsmE)
{
2003-01-27 15:27:53 +00:00
return null;
}
}
/**
* @return the name of the interface tested
*/
public String getInterfaceName()
{
2003-01-27 15:27:53 +00:00
String clName = this.getClass().getName();
return clName.substring(clName.lastIndexOf('.') + 1);
2003-01-27 15:27:53 +00:00
}
/**
* 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)
{
2003-01-27 15:27:53 +00:00
Field objField;
try
{
objField = this.getClass().getField(fieldName);
2003-01-27 15:27:53 +00:00
}
catch (NoSuchFieldException nsfE)
{
return Status.exception(nsfE);
2003-01-27 15:27:53 +00:00
}
try
{
objField.set(this, value);
2003-01-27 15:27:53 +00:00
return Status.passed(true);
}
catch (IllegalArgumentException iaE)
{
return Status.exception(iaE);
2003-01-27 15:27:53 +00:00
}
catch (IllegalAccessException iaE)
{
return Status.exception(iaE);
2003-01-27 15:27:53 +00:00
}
}
}