2 * This file is part of the LibreOffice project.
4 * This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 * This file incorporates work covered by the following license notice:
10 * Licensed to the Apache Software Foundation (ASF) under one or more
11 * contributor license agreements. See the NOTICE file distributed
12 * with this work for additional information regarding copyright
13 * ownership. The ASF licenses this file to you under the Apache
14 * License, Version 2.0 (the "License"); you may not use this file
15 * except in compliance with the License. You may obtain a copy of
16 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 import com
.sun
.star
.beans
.Property
;
21 import com
.sun
.star
.beans
.PropertyAttribute
;
22 import com
.sun
.star
.beans
.PropertyVetoException
;
23 import com
.sun
.star
.beans
.XPropertySet
;
24 import com
.sun
.star
.beans
.XPropertySetInfo
;
25 import com
.sun
.star
.beans
.UnknownPropertyException
;
26 import com
.sun
.star
.lang
.XServiceInfo
;
27 import com
.sun
.star
.lang
.IllegalArgumentException
;
28 import com
.sun
.star
.lang
.WrappedTargetException
;
29 import com
.sun
.star
.uno
.UnoRuntime
;
31 import java
.lang
.reflect
.Method
;
33 import util
.ValueChanger
;
34 import util
.ValueComparer
;
37 import com
.sun
.star
.uno
.Any
;
38 import com
.sun
.star
.uno
.AnyConverter
;
39 import com
.sun
.star
.uno
.Type
;
42 * MultiPropertyTest extends the functionality of MultiMethodTest to support
43 * services testing. Since, in most cases, service tests has one method testing
44 * most of its properties, the MultiPropertyTest provides unified version of
45 * the method: testProperty().
47 * <p>The testProperty() is called, when the MultiMethodTest's testing method
48 * is not found in the subclass. So, by defining such methods for properties
49 * the standard testing behavioutr can be changed.
51 * <p>The testing behaviour also can be changed by overriding compare(),
52 * getNewVAlue() or toString(Object) methods, or by extending PropertyTester
55 * @see MultiMethodTest
56 * @see #testProperty(String)
57 * @see #testProperty(String, PropertyTester)
59 * @see #toString(Object)
61 public class MultiPropertyTest
extends MultiMethodTest
65 * Contains a XPropertySet interface of the tested object. Is initialized
66 * in MultiMethodTest code.
68 public XPropertySet oObj
;
69 protected boolean optionalService
= false;
72 * Overrides super.before() to check the service is supported by the object.
75 protected void before()
77 XServiceInfo xInfo
= UnoRuntime
.queryInterface(
78 XServiceInfo
.class, oObj
);
80 optionalService
= entry
.isOptional
;
82 String theService
= getTestedClassName();
83 if (xInfo
!= null && !xInfo
.supportsService(theService
))
85 log
.println("Service " + theService
+ " not available");
88 log
.println("This is OK since it is optional");
92 Status
.failed(theService
+ " is not supported");
98 * Overrides MultiMethodTest.invokeTestMethod(). If the test for the
99 * <code>meth</code> is not available (<code>meth</code> == <tt>null</tt>)
100 * calls testProperty method for the method. Otherwise calls
101 * super.invokeTestMethod().
103 * @see MultiMethodTest#invokeTestMethod
106 protected void invokeTestMethod(Method meth
, String methName
)
110 super.invokeTestMethod(meth
, methName
);
114 testProperty(methName
);
119 * PropertyTester class defines how to test a property and defined
120 * to allow subclasses of MultiPropertyTest to change the testing
121 * behaviour more flexible, since the behaviour can be customized for
122 * each property separately, by providing subclass of PropertyTester
123 * and passing it to testProperty(String, PropertyTester method).
125 public class PropertyTester
129 * The method defines the whole process of testing propName
132 * <p>First, it checks if the property exists(it maybe optional).
133 * Then, a value to set the property with is calculated with
134 * getNewValue method. Normally, the new value is calculated
135 * based on old value, but subclasses can override the behaviour
136 * (for example, if old value is null) and specify their own value.
137 * Then the property is set with that new value and the result(
138 * it maybe an exception too, for example a PropertyVetoException)
139 * is checked with checkResult method.
141 * @param propName - the property to test.
142 * @result - adds the result of testing propName property to
143 * MultiMethodTest.tRes.
145 protected void testProperty(String propName
)
147 XPropertySetInfo info
= oObj
.getPropertySetInfo();
151 final boolean bHasProperty
= info
.hasPropertyByName(propName
);
154 if (isOptional(propName
) || optionalService
)
156 // skipping optional property test
157 log
.println("Property '" + propName
+ "' is optional and not supported");
158 tRes
.tested(propName
, true);
163 // cannot test the property
164 log
.println("Tested XPropertySet does not contain'" + propName
+ "' property");
165 tRes
.tested(propName
, false);
173 Object oldValue
= oObj
.getPropertyValue(propName
);
175 if( (oldValue
==null) || utils
.isVoid(oldValue
) )
177 // #i111560# method getNewValue() does not work with an empty oldValue
178 Property prop
= info
.getPropertyByName(propName
);
179 if( (prop
.Attributes
& PropertyAttribute
.MAYBEVOID
) != 0 )
181 // todo: implement a new test independent from method getNewValue()
182 log
.println("changing initially empty MAYBEVOID properties is not supported by the test framework so far - skip test of property: " + propName
);
183 tRes
.tested(propName
, true);
188 log
.println( "property '"+propName
+"' is not set but is not MAYBEVOID");
189 tRes
.tested(propName
, false);
196 // trying to create new value
199 newValue
= getNewValue(propName
, oldValue
);
201 catch (java
.lang
.IllegalArgumentException e
)
203 // skipping test since new value is not available
204 Status
.failed("Cannot create new value for '" + propName
+ " : " + e
.getMessage());
208 // for an exception thrown during setting new value
209 // to pass it to checkResult method
210 Exception exception
= null;
214 log
.println("try to set:");
215 log
.println("old = " + toString(oldValue
));
216 log
.println("new = " + toString(newValue
));
217 oObj
.setPropertyValue(propName
, newValue
);
219 catch (IllegalArgumentException e
)
223 catch (PropertyVetoException e
)
227 catch (WrappedTargetException e
)
231 catch (UnknownPropertyException e
)
235 catch (RuntimeException e
)
240 // getting result value
241 Object resValue
= oObj
.getPropertyValue(propName
);
244 checkResult(propName
, oldValue
, newValue
, resValue
, exception
);
248 log
.println("Exception occurred while testing property '" + propName
+ "'");
249 e
.printStackTrace(log
);
250 tRes
.tested(propName
, false);
255 * The method checks result of setting a new value to the
256 * property based o the following arguments:
257 * @param propName - the property to test
258 * @param oldValue - the old value of the property, before changing it.
259 * @param newValue - the new value the property has been set with
260 * @param resValue - the value of the property after having changed it
261 * @param exception - if not null - the exception thrown by
262 * XPropertySet.setPropertyValue, else indicates
263 * normal method completion.
265 * <p>If the property is READ_ONLY, than either PropertyVetoException
266 * should be thrown or the value of property should not have changed
267 * (resValue is compared with oldValue with compare method).
269 * <p>If the property is not READ_ONLY, checks that the new value has
270 * been successfully set(resValue is compared with newValue with
273 * <p>If the exception is not null then(except the case of read-only
274 * property and PropertyVetoException above) it is rethrown to allow
275 * further catching it if needed.
277 * <p>Subclasses can override to change this behaviour.
279 protected void checkResult(String propName
, Object oldValue
,
280 Object newValue
, Object resValue
, Exception exception
)
283 XPropertySetInfo info
= oObj
.getPropertySetInfo();
286 log
.println("Can't get XPropertySetInfo for property " + propName
);
287 tRes
.tested(propName
, false);
290 Property prop
= info
.getPropertyByName(propName
);
292 boolean readOnly
= (prop
.Attributes
& PropertyAttribute
.READONLY
) != 0;
293 boolean maybeVoid
= (prop
.Attributes
& PropertyAttribute
.MAYBEVOID
) != 0;
294 //check get-set methods
297 log
.println("Property " + propName
+ " is void");
301 log
.println("Property " + propName
+ " is readOnly");
303 if (util
.utils
.isVoid(oldValue
) && !maybeVoid
)
305 log
.println(propName
+ " is void, but it's not MAYBEVOID");
306 tRes
.tested(propName
, false);
308 else if (oldValue
== null)
310 log
.println(propName
+ " has null value, and therefore can't be changed");
311 tRes
.tested(propName
, true);
315 // check if exception was thrown
316 if (exception
!= null)
318 if (exception
instanceof PropertyVetoException
)
320 // the change of read only prohibited - OK
321 log
.println("Property is ReadOnly and wasn't changed");
322 log
.println("Property '" + propName
+ "' OK");
323 tRes
.tested(propName
, true);
325 else if (exception
instanceof IllegalArgumentException
)
327 // the change of read only prohibited - OK
328 log
.println("Property is ReadOnly and wasn't changed");
329 log
.println("Property '" + propName
+ "' OK");
330 tRes
.tested(propName
, true);
332 else if (exception
instanceof UnknownPropertyException
)
334 // the change of read only prohibited - OK
335 log
.println("Property is ReadOnly and wasn't changed");
336 log
.println("Property '" + propName
+ "' OK");
337 tRes
.tested(propName
, true);
339 else if (exception
instanceof RuntimeException
)
341 // the change of read only prohibited - OK
342 log
.println("Property is ReadOnly and wasn't changed");
343 log
.println("Property '" + propName
+ "' OK");
344 tRes
.tested(propName
, true);
353 // if no exception - check that value
355 if (!compare(resValue
, oldValue
))
357 log
.println("Read only property '" + propName
+ "' has changed");
360 if (!util
.utils
.isVoid(oldValue
) && oldValue
instanceof Any
)
362 oldValue
= AnyConverter
.toObject(new Type(((Any
) oldValue
).getClass()), oldValue
);
364 log
.println("result = " + toString(resValue
));
366 catch (com
.sun
.star
.lang
.IllegalArgumentException iae
)
368 log
.println("NOTIFY: this property needs further investigations.");
369 log
.println("\t The type seems to be an Any with value of NULL.");
370 log
.println("\t Maybe the property should get its own test method.");
373 tRes
.tested(propName
, false);
377 log
.println("Read only property '" + propName
+ "' hasn't changed");
378 log
.println("Property '" + propName
+ "' OK");
379 tRes
.tested(propName
, true);
385 if (exception
== null)
387 // if no exception thrown
388 // check that the new value is set
389 if ((!compare(resValue
, newValue
)) || (compare(resValue
, oldValue
)))
391 log
.println("Value for '" + propName
+ "' hasn't changed as expected");
394 if (!util
.utils
.isVoid(oldValue
) && oldValue
instanceof Any
)
396 oldValue
= AnyConverter
.toObject(new Type(((Any
) oldValue
).getClass()), oldValue
);
398 log
.println("result = " + toString(resValue
));
400 catch (com
.sun
.star
.lang
.IllegalArgumentException iae
)
402 log
.println("NOTIFY: this property needs further investigations.");
403 log
.println("\t The type seems to be an Any with value of NULL.");
404 log
.println("\t Maybe the property should get its own test method.");
406 if (resValue
!= null)
408 if ((!compare(resValue
, oldValue
)) || (!resValue
.equals(oldValue
)))
410 log
.println("But it has changed.");
411 tRes
.tested(propName
, true);
415 tRes
.tested(propName
, false);
420 tRes
.tested(propName
, false);
425 log
.println("Property '" + propName
+ "' OK");
428 if (!util
.utils
.isVoid(oldValue
) && oldValue
instanceof Any
)
430 oldValue
= AnyConverter
.toObject(new Type(((Any
) oldValue
).getClass()), oldValue
);
432 log
.println("result = " + toString(resValue
));
434 catch (com
.sun
.star
.lang
.IllegalArgumentException iae
)
437 tRes
.tested(propName
, true);
448 * The method produces new value of the property from the oldValue.
449 * It returns the result of ValueChanger.changePValue method.
450 * Subclasses can override the method to return their own value,
451 * when the changePValue behavior is not enough, for example,
452 * when oldValue is null.
454 protected Object
getNewValue(String propName
, Object oldValue
)
455 throws java
.lang
.IllegalArgumentException
457 return ValueChanger
.changePValue(oldValue
, propName
);
461 * The method compares obj1 and obj2. It calls
462 * MultiPropertyTest.compare, but subclasses can override to change
463 * the behavior, since normally compare calls Object.equals method
464 * which is not appropriate in some cases(e.g., structs with equals
467 protected boolean compare(Object obj1
, Object obj2
)
469 return callCompare(obj1
, obj2
);
473 * The method returns a String representation of the obj. It calls
474 * MultipropertyTest.toString(Object), but subclasses can override
475 * to change the behavior.
477 protected String
toString(Object obj
)
479 return callToString(obj
);
484 * Extension for <code>PropertyTester</code> which switches two
485 * different values. <code>getNewValue()</code> method of this
486 * class returns one of these two values depending on the
487 * old value, so new value is not equal to old value.
489 public class PropertyValueSwitcher
extends PropertyTester
496 * Constructs a property tester with two different values
497 * specified as parameters.
499 * @param val1 Not <code>null</code> value for the property
501 * @param val2 Not <code>null</code> value for the property
502 * tested which differs from the first value.
504 public PropertyValueSwitcher(Object val1
, Object val2
)
511 * Overridden method of <code>PropertyTester</code> which
512 * retruns new value from two values specified.
514 * @return The second value if old value is equal to the first
515 * one, the first value otherwise.
518 protected Object
getNewValue(String propName
, Object old
)
520 if (ValueComparer
.equalValue(val1
, old
))
532 * The method performs testing of propName property using propTester.
534 protected void testProperty(String propName
, PropertyTester propTester
)
536 propTester
.testProperty(propName
);
540 * The method performs testing of propName property. It uses PropertyTester
541 * instance for testing.
543 protected void testProperty(String propName
)
545 testProperty(propName
, new PropertyTester());
549 * Tests the property using <code>PropertyValueSwitcher</code>
550 * tester and two values for this property.
552 * @see PropertyValueSwitcher
554 protected void testProperty(String propName
, Object val1
, Object val2
)
556 testProperty(propName
, new PropertyValueSwitcher(val1
, val2
));
560 * The method just calls compare. This is a workaround to CodeWarrior's
563 private boolean callCompare(Object obj1
, Object obj2
)
565 return compare(obj1
, obj2
);
569 * Compares two object. In the implementation calls obj1.equals(obj2).
571 protected boolean compare(Object obj1
, Object obj2
)
573 return ValueComparer
.equalValue(obj1
, obj2
);
577 * The method just calls toString. This is a workaround to
578 * CodeWarrior's compiler bug.
580 private String
callToString(Object obj
)
582 return toString(obj
);
586 * Gets string representation of the obj. In the implementation
587 * returns obj.toString().
589 protected String
toString(Object obj
)
591 return obj
== null ?
"null" : obj
.toString();