blob: 8569ad47fc64d289a4fb0da9f64a70a6dc3dd203 [file] [log] [blame]
/**************************************************************
*
* 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
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
package lib;
import com.sun.star.beans.Property;
import com.sun.star.beans.PropertyAttribute;
import com.sun.star.beans.PropertyVetoException;
import com.sun.star.beans.XPropertySet;
import com.sun.star.beans.XPropertySetInfo;
import com.sun.star.beans.UnknownPropertyException;
import com.sun.star.lang.XServiceInfo;
import com.sun.star.lang.IllegalArgumentException;
import com.sun.star.lang.WrappedTargetException;
import com.sun.star.uno.UnoRuntime;
import java.lang.reflect.Method;
import util.ValueChanger;
import util.ValueComparer;
import util.utils;
import com.sun.star.uno.Any;
import com.sun.star.uno.AnyConverter;
import com.sun.star.uno.Type;
/**
* MultiPropertyTest extends the functionality of MultiMethodTest to support
* services testing. Since, in most cases, service tests has one method testing
* most of its properties, the MultiPropertyTest provides unified version of
* the method: testProperty().
*
* <p>The testProperty() is called, when the MultiMethodTest's testing method
* is not found in the subclass. So, by defining such methods for properties
* the standard testing behavioutr can be changed.
*
* <p>The testing behaviour also can be changed by overriding compare(),
* getNewVAlue() or toString(Object) methods, or by extending PropertyTester
* class.
*
* @see MultiMethodTest
* @see #testProperty(String)
* @see #testProperty(String, PropertyTester)
* @see #getNewValue
* @see #compare
* @see #toString(Object)
*/
public class MultiPropertyTest extends MultiMethodTest
{
/**
* Contains a XPropertySet interface of the tested object. Is initialized
* in MultiMethodTest code.
*/
public XPropertySet oObj;
protected boolean optionalService = false;
/**
* Overrides super.before() to check the service is supported by the object.
*/
protected void before()
{
XServiceInfo xInfo = (XServiceInfo) UnoRuntime.queryInterface(
XServiceInfo.class, oObj);
optionalService = entry.isOptional;
String theService = getTestedClassName();
if (xInfo != null && !xInfo.supportsService(theService))
{
log.println("Service " + theService + " not available");
if (optionalService)
{
log.println("This is OK since it is optional");
}
else
{
Status.failed(theService + " is not supported");
}
}
}
/**
* Overrides MultiMethodTest.invokeTestMethod(). If the test for the
* <code>meth</code> is not available (<code>meth</code> == <tt>null</tt>)
* calls testProperty method for the method. Otherwise calls
* super.invokeTestMethod().
*
* @see MultiMethodTest#invokeTestMethod
*/
protected void invokeTestMethod(Method meth, String methName)
{
if (meth != null)
{
super.invokeTestMethod(meth, methName);
}
else
{
testProperty(methName);
}
}
/**
* PropertyTester class defines how to test a property and defined
* to allow subclasses of MultiPropertyTest to change the testing
* behaviour more flexible, since the behaviour can be customized for
* each property separately, by providing subclass of PropertyTester
* and passing it to testProperty(String, PropertyTester method).
*/
public class PropertyTester
{
/**
* The method defines the whole process of testing propName
* property.
*
* <p>First, it checks if the property exists(it maybe optional).
* Then, a value to set the property with is calculated with
* getNewValue method. Normally, the new value is calculated
* based on old value, but subclasses can override the behaviour
* (for example, if old value is null) and specify their own value.
* Then the property is set with that new value and the result(
* it maybe an exception too, for example a PropertyVetoException)
* is checked with checkResult method.
*
* @param propName - the property to test.
* @result - adds the result of testing propName property to
* MultiMethodTest.tRes.
*/
protected void testProperty(String propName)
{
XPropertySetInfo info = oObj.getPropertySetInfo();
if (info != null)
{
final boolean bHasProperty = info.hasPropertyByName(propName);
if (!bHasProperty)
{
if (isOptional(propName) || optionalService)
{
// skipping optional property test
log.println("Property '" + propName + "' is optional and not supported");
tRes.tested(propName, true);
return;
}
else
{
// cannot test the property
log.println("Tested XPropertySet does not contain'" + propName + "' property");
tRes.tested(propName, false);
return;
}
}
}
try
{
Object oldValue = oObj.getPropertyValue(propName);
if( (oldValue==null) || utils.isVoid(oldValue) )
{
// #i111560# method getNewValue() does not work with an empty oldValue
Property prop = info.getPropertyByName(propName);
if( (prop.Attributes & PropertyAttribute.MAYBEVOID) != 0 )
{
// todo: implement a new test independent from method getNewValue()
log.println("changing initially empty MAYBEVOID properties is not supported by the test framework so far - skip test of property: " + propName);
tRes.tested(propName, true);
return;
}
else
{
log.println( "property '"+propName+"' is not set but is not MAYBEVOID");
tRes.tested(propName, false);
return;
}
}
Object newValue;
// trying to create new value
try
{
newValue = getNewValue(propName, oldValue);
}
catch (java.lang.IllegalArgumentException e)
{
// skipping test since new value is not available
Status.failed("Cannot create new value for '" + propName + " : " + e.getMessage());
return;
}
// for an exception thrown during setting new value
// to pass it to checkResult method
Exception exception = null;
try
{
log.println("try to set:");
log.println("old = " + toString(oldValue));
log.println("new = " + toString(newValue));
oObj.setPropertyValue(propName, newValue);
}
catch (IllegalArgumentException e)
{
exception = e;
}
catch (PropertyVetoException e)
{
exception = e;
}
catch (WrappedTargetException e)
{
exception = e;
}
catch (UnknownPropertyException e)
{
exception = e;
}
catch (RuntimeException e)
{
exception = e;
}
// getting result value
Object resValue = oObj.getPropertyValue(propName);
// checking results
checkResult(propName, oldValue, newValue, resValue, exception);
}
catch (Exception e)
{
log.println("Exception occured while testing property '" + propName + "'");
e.printStackTrace(log);
tRes.tested(propName, false);
}
}
/**
* The method checks result of setting a new value to the
* property based o the following arguments:
* @param propName - the property to test
* @param oldValue - the old value of the property, before changing it.
* @param newValue - the new value the property has been set with
* @param resValue - the value of the property after having changed it
* @param exception - if not null - the exception thrown by
* XPropertySet.setPropertyValue, else indicates
* normal method completion.
*
* <p>If the property is READ_ONLY, than either PropertyVetoException
* should be thrown or the value of property should not have changed
* (resValue is compared with oldValue with compare method).
*
* <p>If the property is not READ_ONLY, checks that the new value has
* been successfully set(resValue is compared with newValue with
* compare method).
*
* <p>If the exception is not null then(except the case of read-only
* property and PropertyVetoException above) it is rethrown to allow
* further catching it if needed.
*
* <p>Subclasses can override to change this behaviour.
*/
protected void checkResult(String propName, Object oldValue,
Object newValue, Object resValue, Exception exception)
throws Exception
{
XPropertySetInfo info = oObj.getPropertySetInfo();
if (info == null)
{
log.println("Can't get XPropertySetInfo for property " + propName);
tRes.tested(propName, false);
return;
}
Property prop = info.getPropertyByName(propName);
short attr = prop.Attributes;
boolean readOnly = (prop.Attributes & PropertyAttribute.READONLY) != 0;
boolean maybeVoid = (prop.Attributes & PropertyAttribute.MAYBEVOID) != 0;
//check get-set methods
if (maybeVoid)
{
log.println("Property " + propName + " is void");
}
if (readOnly)
{
log.println("Property " + propName + " is readOnly");
}
if (util.utils.isVoid(oldValue) && !maybeVoid)
{
log.println(propName + " is void, but it's not MAYBEVOID");
tRes.tested(propName, false);
}
else if (oldValue == null)
{
log.println(propName + " has null value, and therefore can't be changed");
tRes.tested(propName, true);
}
else if (readOnly)
{
// check if exception was thrown
if (exception != null)
{
if (exception instanceof PropertyVetoException)
{
// the change of read only prohibited - OK
log.println("Property is ReadOnly and wasn't changed");
log.println("Property '" + propName + "' OK");
tRes.tested(propName, true);
}
else if (exception instanceof IllegalArgumentException)
{
// the change of read only prohibited - OK
log.println("Property is ReadOnly and wasn't changed");
log.println("Property '" + propName + "' OK");
tRes.tested(propName, true);
}
else if (exception instanceof UnknownPropertyException)
{
// the change of read only prohibited - OK
log.println("Property is ReadOnly and wasn't changed");
log.println("Property '" + propName + "' OK");
tRes.tested(propName, true);
}
else if (exception instanceof RuntimeException)
{
// the change of read only prohibited - OK
log.println("Property is ReadOnly and wasn't changed");
log.println("Property '" + propName + "' OK");
tRes.tested(propName, true);
}
else
{
throw exception;
}
}
else
{
// if no exception - check that value
// has not changed
if (!compare(resValue, oldValue))
{
log.println("Read only property '" + propName + "' has changed");
try
{
if (!util.utils.isVoid(oldValue) && oldValue instanceof Any)
{
oldValue = AnyConverter.toObject(new Type(((Any) oldValue).getClass()), oldValue);
}
// log.println("old = " + toString(oldValue));
// log.println("new = " + toString(newValue));
log.println("result = " + toString(resValue));
}
catch (com.sun.star.lang.IllegalArgumentException iae)
{
log.println("NOTIFY: this property needs further investigations.");
log.println("\t The type seems to be an Any with value of NULL.");
log.println("\t Maybe the property should get it's own test method.");
}
tRes.tested(propName, false);
}
else
{
log.println("Read only property '" + propName + "' hasn't changed");
log.println("Property '" + propName + "' OK");
tRes.tested(propName, true);
}
}
}
else
{
if (exception == null)
{
// if no exception thrown
// check that the new value is set
if ((!compare(resValue, newValue)) || (compare(resValue, oldValue)))
{
log.println("Value for '" + propName + "' hasn't changed as expected");
try
{
if (!util.utils.isVoid(oldValue) && oldValue instanceof Any)
{
oldValue = AnyConverter.toObject(new Type(((Any) oldValue).getClass()), oldValue);
}
// log.println("old = " + toString(oldValue));
// log.println("new = " + toString(newValue));
log.println("result = " + toString(resValue));
}
catch (com.sun.star.lang.IllegalArgumentException iae)
{
log.println("NOTIFY: this property needs further investigations.");
log.println("\t The type seems to be an Any with value of NULL.");
log.println("\t Maybe the property should get it's own test method.");
}
if (resValue != null)
{
if ((!compare(resValue, oldValue)) || (!resValue.equals(oldValue)))
{
log.println("But it has changed.");
tRes.tested(propName, true);
}
else
{
tRes.tested(propName, false);
}
}
else
{
tRes.tested(propName, false);
}
//tRes.tested(propName, false);
}
else
{
log.println("Property '" + propName + "' OK");
try
{
if (!util.utils.isVoid(oldValue) && oldValue instanceof Any)
{
oldValue = AnyConverter.toObject(new Type(((Any) oldValue).getClass()), oldValue);
}
// log.println("old = " + toString(oldValue));
// log.println("new = " + toString(newValue));
log.println("result = " + toString(resValue));
}
catch (com.sun.star.lang.IllegalArgumentException iae)
{
}
tRes.tested(propName, true);
}
}
else
{
throw exception;
}
}
}
/**
* The method produces new value of the property from the oldValue.
* It returns the result of ValueChanger.changePValue method.
* Subclasses can override the method to return their own value,
* when the changePValue beahviour is not enough, for example,
* when oldValue is null.
*/
protected Object getNewValue(String propName, Object oldValue)
throws java.lang.IllegalArgumentException
{
return ValueChanger.changePValue(oldValue);
}
/**
* The method compares obj1 and obj2. It calls
* MultiPropertyTest.compare, but subclasses can override to change
* the behaviour, since normally compare calls Object.equals method
* which is not apropriate in some cases(e.g., structs with equals
* not overridden).
*/
protected boolean compare(Object obj1, Object obj2)
{
return callCompare(obj1, obj2);
}
/**
* The method returns a String representation of the obj. It calls
* MultipropertyTest.toString(Object), but subclasses can override
* to change the behaviour.
*/
protected String toString(Object obj)
{
return callToString(obj);
}
}
/**
* Extension for <code>PropertyTester</code> which switches two
* different values. <code>getNewValue()</code> method of this
* class returns one of these two values depending on the
* old value, so new value is not equal to old value.
*/
public class PropertyValueSwitcher extends PropertyTester
{
Object val1 = null;
Object val2 = null;
/**
* Constructs a property tester with two different values
* specified as parameters.
*
* @param val1 Not <code>null</code> value for the property
* tested.
* @param val2 Not <code>null</code> value for the property
* tested which differs from the first value.
*/
public PropertyValueSwitcher(Object val1, Object val2)
{
this.val1 = val1;
this.val2 = val2;
}
/**
* Overriden method of <code>PropertyTester</code> which
* retruns new value from two values specified.
*
* @return The second value if old value is equal to the first
* one, the first value otherwise.
*/
protected Object getNewValue(String propName, Object old)
{
if (ValueComparer.equalValue(val1, old))
{
return val2;
}
else
{
return val1;
}
}
}
/**
* The method performs testing of propName property using propTester.
*/
protected void testProperty(String propName, PropertyTester propTester)
{
propTester.testProperty(propName);
}
/**
* The method performs testing of propName property. It uses PropertyTester
* instance for testing.
*/
protected void testProperty(String propName)
{
testProperty(propName, new PropertyTester());
}
/**
* Tests the property using <code>PropertyValueSwitcher</code>
* tester and two values for this property.
*
* @see PropertyValueSwitcher
*/
protected void testProperty(String propName, Object val1, Object val2)
{
testProperty(propName, new PropertyValueSwitcher(val1, val2));
}
/**
* The method just calls compare. This is a workaround to CodeWarrior's
* compiler bug.
*/
private boolean callCompare(Object obj1, Object obj2)
{
return compare(obj1, obj2);
}
/**
* Compares two object. In the implementation calls obj1.equals(obj2).
*/
protected boolean compare(Object obj1, Object obj2)
{
return ValueComparer.equalValue(obj1, obj2);
}
/**
* The method just calls toString. This is a workaround to
* CodeWarrior's compiler bug.
*/
private String callToString(Object obj)
{
return toString(obj);
}
/**
* Gets string representation of the obj. In the implementation
* returns obj.toString().
*/
protected String toString(Object obj)
{
return obj == null ? "null" : obj.toString();
}
}