1 /************************************************************************* 2 * 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * Copyright 2000, 2010 Oracle and/or its affiliates. 6 * 7 * OpenOffice.org - a multi-platform office productivity suite 8 * 9 * This file is part of OpenOffice.org. 10 * 11 * OpenOffice.org is free software: you can redistribute it and/or modify 12 * it under the terms of the GNU Lesser General Public License version 3 13 * only, as published by the Free Software Foundation. 14 * 15 * OpenOffice.org is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU Lesser General Public License version 3 for more details 19 * (a copy is included in the LICENSE file that accompanied this code). 20 * 21 * You should have received a copy of the GNU Lesser General Public License 22 * version 3 along with OpenOffice.org. If not, see 23 * <http://www.openoffice.org/license.html> 24 * for a copy of the LGPLv3 License. 25 * 26 ************************************************************************/ 27 package lib; 28 29 import com.sun.star.beans.Property; 30 import com.sun.star.beans.PropertyAttribute; 31 import com.sun.star.beans.PropertyVetoException; 32 import com.sun.star.beans.XPropertySet; 33 import com.sun.star.beans.XPropertySetInfo; 34 import com.sun.star.beans.UnknownPropertyException; 35 import com.sun.star.lang.XServiceInfo; 36 import com.sun.star.lang.IllegalArgumentException; 37 import com.sun.star.lang.WrappedTargetException; 38 import com.sun.star.uno.UnoRuntime; 39 40 import java.lang.reflect.Method; 41 42 import util.ValueChanger; 43 import util.ValueComparer; 44 import util.utils; 45 46 import com.sun.star.uno.Any; 47 import com.sun.star.uno.AnyConverter; 48 import com.sun.star.uno.Type; 49 50 /** 51 * MultiPropertyTest extends the functionality of MultiMethodTest to support 52 * services testing. Since, in most cases, service tests has one method testing 53 * most of its properties, the MultiPropertyTest provides unified version of 54 * the method: testProperty(). 55 * 56 * <p>The testProperty() is called, when the MultiMethodTest's testing method 57 * is not found in the subclass. So, by defining such methods for properties 58 * the standard testing behavioutr can be changed. 59 * 60 * <p>The testing behaviour also can be changed by overriding compare(), 61 * getNewVAlue() or toString(Object) methods, or by extending PropertyTester 62 * class. 63 * 64 * @see MultiMethodTest 65 * @see #testProperty(String) 66 * @see #testProperty(String, Propertytester) 67 * @see #getNewValue 68 * @see #compare 69 * @see #toString(Object) 70 */ 71 public class MultiPropertyTest extends MultiMethodTest 72 { 73 74 /** 75 * Contains a XPropertySet interface of the tested object. Is initialized 76 * in MultiMethodTest code. 77 */ 78 public XPropertySet oObj; 79 protected boolean optionalService = false; 80 81 /** 82 * Overrides super.before() to check the service is supported by the object. 83 */ 84 protected void before() 85 { 86 XServiceInfo xInfo = (XServiceInfo) UnoRuntime.queryInterface( 87 XServiceInfo.class, oObj); 88 89 optionalService = entry.isOptional; 90 91 String theService = getTestedClassName(); 92 if (xInfo != null && !xInfo.supportsService(theService)) 93 { 94 log.println("Service " + theService + " not available"); 95 if (optionalService) 96 { 97 log.println("This is OK since it is optional"); 98 } 99 else 100 { 101 Status.failed(theService + " is not supported"); 102 } 103 } 104 } 105 106 /** 107 * Overrides MultiMethodTest.invokeTestMethod(). If the test for the 108 * <code>meth</code> is not available (<code>meth</code> == <tt>null</tt>) 109 * calls testProperty method for the method. Otherwise calls 110 * super.invokeTestMethod(). 111 * 112 * @see #MultiMethodTest.invokeTestMethod() 113 */ 114 protected void invokeTestMethod(Method meth, String methName) 115 { 116 if (meth != null) 117 { 118 super.invokeTestMethod(meth, methName); 119 } 120 else 121 { 122 testProperty(methName); 123 } 124 } 125 126 /** 127 * PropertyTester class defines how to test a property and defined 128 * to allow subclasses of MultiPropertyTest to change the testing 129 * behaviour more flexible, since the behaviour can be customized for 130 * each property separately, by providing subclass of PropertyTester 131 * and passing it to testProperty(String, PropertyTester method). 132 */ 133 public class PropertyTester 134 { 135 136 /** 137 * The method defines the whole process of testing propName 138 * property. 139 * 140 * <p>First, it checks if the property exists(it maybe optional). 141 * Then, a value to set the property with is calculated with 142 * getNewValue method. Normally, the new value is calculated 143 * based on old value, but subclasses can override the behaviour 144 * (for example, if old value is null) and specify their own value. 145 * Then the property is set with that new value and the result( 146 * it maybe an exception too, for example a PropertyVetoException) 147 * is checked with checkResult method. 148 * 149 * @param propName - the property to test. 150 * @result - adds the result of testing propName property to 151 * MultiMethodTest.tRes. 152 */ 153 protected void testProperty(String propName) 154 { 155 XPropertySetInfo info = oObj.getPropertySetInfo(); 156 157 if (info != null) 158 { 159 final boolean bHasProperty = info.hasPropertyByName(propName); 160 if (!bHasProperty) 161 { 162 if (isOptional(propName) || optionalService) 163 { 164 // skipping optional property test 165 log.println("Property '" + propName + "' is optional and not supported"); 166 tRes.tested(propName, true); 167 return; 168 } 169 else 170 { 171 // cannot test the property 172 log.println("Tested XPropertySet does not contain'" + propName + "' property"); 173 tRes.tested(propName, false); 174 return; 175 } 176 } 177 } 178 179 try 180 { 181 Object oldValue = oObj.getPropertyValue(propName); 182 183 if( (oldValue==null) || utils.isVoid(oldValue) ) 184 { 185 // #i111560# method getNewValue() does not work with an empty oldValue 186 Property prop = info.getPropertyByName(propName); 187 if( (prop.Attributes & PropertyAttribute.MAYBEVOID) != 0 ) 188 { 189 // todo: implement a new test independent from method getNewValue() 190 log.println("changing initially empty MAYBEVOID properties is not supported by the test framework so far - skip test of property: " + propName); 191 tRes.tested(propName, true); 192 return; 193 } 194 else 195 { 196 log.println( "property '"+propName+"' is not set but is not MAYBEVOID"); 197 tRes.tested(propName, false); 198 return; 199 } 200 } 201 202 Object newValue; 203 204 // trying to create new value 205 try 206 { 207 newValue = getNewValue(propName, oldValue); 208 } 209 catch (java.lang.IllegalArgumentException e) 210 { 211 // skipping test since new value is not available 212 Status.failed("Cannot create new value for '" + propName + " : " + e.getMessage()); 213 return; 214 } 215 216 // for an exception thrown during setting new value 217 // to pass it to checkResult method 218 Exception exception = null; 219 220 try 221 { 222 log.println("try to set:"); 223 log.println("old = " + toString(oldValue)); 224 log.println("new = " + toString(newValue)); 225 oObj.setPropertyValue(propName, newValue); 226 } 227 catch (IllegalArgumentException e) 228 { 229 exception = e; 230 } 231 catch (PropertyVetoException e) 232 { 233 exception = e; 234 } 235 catch (WrappedTargetException e) 236 { 237 exception = e; 238 } 239 catch (UnknownPropertyException e) 240 { 241 exception = e; 242 } 243 catch (RuntimeException e) 244 { 245 exception = e; 246 } 247 248 // getting result value 249 Object resValue = oObj.getPropertyValue(propName); 250 251 // checking results 252 checkResult(propName, oldValue, newValue, resValue, exception); 253 } 254 catch (Exception e) 255 { 256 log.println("Exception occured while testing property '" + propName + "'"); 257 e.printStackTrace(log); 258 tRes.tested(propName, false); 259 } 260 } 261 262 /** 263 * The method checks result of setting a new value to the 264 * property based o the following arguments: 265 * @propName - the property to test 266 * @oldValue - the old value of the property, before changing it. 267 * @newValue - the new value the property has been set with 268 * @resValue - the value of the property after having changed it 269 * @exception - if not null - the exception thrown by 270 * XPropertySet.setPropertyValue, else indicates 271 * normal method completion. 272 * 273 * <p>If the property is READ_ONLY, than either PropertyVetoException 274 * should be thrown or the value of property should not have changed 275 * (resValue is compared with oldValue with compare method). 276 * 277 * <p>If the property is not READ_ONLY, checks that the new value has 278 * been successfully set(resValue is compared with newValue with 279 * compare method). 280 * 281 * <p>If the exception is not null then(except the case of read-only 282 * property and PropertyVetoException above) it is rethrown to allow 283 * further catching it if needed. 284 * 285 * <p>Subclasses can override to change this behaviour. 286 */ 287 protected void checkResult(String propName, Object oldValue, 288 Object newValue, Object resValue, Exception exception) 289 throws Exception 290 { 291 XPropertySetInfo info = oObj.getPropertySetInfo(); 292 if (info == null) 293 { 294 log.println("Can't get XPropertySetInfo for property " + propName); 295 tRes.tested(propName, false); 296 return; 297 } 298 Property prop = info.getPropertyByName(propName); 299 300 short attr = prop.Attributes; 301 boolean readOnly = (prop.Attributes & PropertyAttribute.READONLY) != 0; 302 boolean maybeVoid = (prop.Attributes & PropertyAttribute.MAYBEVOID) != 0; 303 //check get-set methods 304 if (maybeVoid) 305 { 306 log.println("Property " + propName + " is void"); 307 } 308 if (readOnly) 309 { 310 log.println("Property " + propName + " is readOnly"); 311 } 312 if (util.utils.isVoid(oldValue) && !maybeVoid) 313 { 314 log.println(propName + " is void, but it's not MAYBEVOID"); 315 tRes.tested(propName, false); 316 } 317 else if (oldValue == null) 318 { 319 log.println(propName + " has null value, and therefore can't be changed"); 320 tRes.tested(propName, true); 321 } 322 else if (readOnly) 323 { 324 // check if exception was thrown 325 if (exception != null) 326 { 327 if (exception instanceof PropertyVetoException) 328 { 329 // the change of read only prohibited - OK 330 log.println("Property is ReadOnly and wasn't changed"); 331 log.println("Property '" + propName + "' OK"); 332 tRes.tested(propName, true); 333 } 334 else if (exception instanceof IllegalArgumentException) 335 { 336 // the change of read only prohibited - OK 337 log.println("Property is ReadOnly and wasn't changed"); 338 log.println("Property '" + propName + "' OK"); 339 tRes.tested(propName, true); 340 } 341 else if (exception instanceof UnknownPropertyException) 342 { 343 // the change of read only prohibited - OK 344 log.println("Property is ReadOnly and wasn't changed"); 345 log.println("Property '" + propName + "' OK"); 346 tRes.tested(propName, true); 347 } 348 else if (exception instanceof RuntimeException) 349 { 350 // the change of read only prohibited - OK 351 log.println("Property is ReadOnly and wasn't changed"); 352 log.println("Property '" + propName + "' OK"); 353 tRes.tested(propName, true); 354 } 355 else 356 { 357 throw exception; 358 } 359 } 360 else 361 { 362 // if no exception - check that value 363 // has not changed 364 if (!compare(resValue, oldValue)) 365 { 366 log.println("Read only property '" + propName + "' has changed"); 367 try 368 { 369 if (!util.utils.isVoid(oldValue) && oldValue instanceof Any) 370 { 371 oldValue = AnyConverter.toObject(new Type(((Any) oldValue).getClass()), oldValue); 372 } 373 // log.println("old = " + toString(oldValue)); 374 // log.println("new = " + toString(newValue)); 375 log.println("result = " + toString(resValue)); 376 } 377 catch (com.sun.star.lang.IllegalArgumentException iae) 378 { 379 log.println("NOTIFY: this property needs further investigations."); 380 log.println("\t The type seems to be an Any with value of NULL."); 381 log.println("\t Maybe the property should get it's own test method."); 382 } 383 384 tRes.tested(propName, false); 385 } 386 else 387 { 388 log.println("Read only property '" + propName + "' hasn't changed"); 389 log.println("Property '" + propName + "' OK"); 390 tRes.tested(propName, true); 391 } 392 } 393 } 394 else 395 { 396 if (exception == null) 397 { 398 // if no exception thrown 399 // check that the new value is set 400 if ((!compare(resValue, newValue)) || (compare(resValue, oldValue))) 401 { 402 log.println("Value for '" + propName + "' hasn't changed as expected"); 403 try 404 { 405 if (!util.utils.isVoid(oldValue) && oldValue instanceof Any) 406 { 407 oldValue = AnyConverter.toObject(new Type(((Any) oldValue).getClass()), oldValue); 408 } 409 // log.println("old = " + toString(oldValue)); 410 // log.println("new = " + toString(newValue)); 411 log.println("result = " + toString(resValue)); 412 } 413 catch (com.sun.star.lang.IllegalArgumentException iae) 414 { 415 log.println("NOTIFY: this property needs further investigations."); 416 log.println("\t The type seems to be an Any with value of NULL."); 417 log.println("\t Maybe the property should get it's own test method."); 418 } 419 if (resValue != null) 420 { 421 if ((!compare(resValue, oldValue)) || (!resValue.equals(oldValue))) 422 { 423 log.println("But it has changed."); 424 tRes.tested(propName, true); 425 } 426 else 427 { 428 tRes.tested(propName, false); 429 } 430 } 431 else 432 { 433 tRes.tested(propName, false); 434 } 435 //tRes.tested(propName, false); 436 } 437 else 438 { 439 log.println("Property '" + propName + "' OK"); 440 try 441 { 442 if (!util.utils.isVoid(oldValue) && oldValue instanceof Any) 443 { 444 oldValue = AnyConverter.toObject(new Type(((Any) oldValue).getClass()), oldValue); 445 } 446 // log.println("old = " + toString(oldValue)); 447 // log.println("new = " + toString(newValue)); 448 log.println("result = " + toString(resValue)); 449 } 450 catch (com.sun.star.lang.IllegalArgumentException iae) 451 { 452 } 453 tRes.tested(propName, true); 454 } 455 } 456 else 457 { 458 throw exception; 459 } 460 } 461 } 462 463 /** 464 * The method produces new value of the property from the oldValue. 465 * It returns the result of ValueChanger.changePValue method. 466 * Subclasses can override the method to return their own value, 467 * when the changePValue beahviour is not enough, for example, 468 * when oldValue is null. 469 */ 470 protected Object getNewValue(String propName, Object oldValue) 471 throws java.lang.IllegalArgumentException 472 { 473 return ValueChanger.changePValue(oldValue); 474 } 475 476 /** 477 * The method compares obj1 and obj2. It calls 478 * MultiPropertyTest.compare, but subclasses can override to change 479 * the behaviour, since normally compare calls Object.equals method 480 * which is not apropriate in some cases(e.g., structs with equals 481 * not overridden). 482 */ 483 protected boolean compare(Object obj1, Object obj2) 484 { 485 return callCompare(obj1, obj2); 486 } 487 488 /** 489 * The method returns a String representation of the obj. It calls 490 * MultipropertyTest.toString(Object), but subclasses can override 491 * to change the behaviour. 492 */ 493 protected String toString(Object obj) 494 { 495 return callToString(obj); 496 } 497 } 498 499 /** 500 * Extension for <code>PropertyTester</code> which switches two 501 * different values. <code>getNewValue()</code> method of this 502 * class returns one of these two values depending on the 503 * old value, so new value is not equal to old value. 504 */ 505 public class PropertyValueSwitcher extends PropertyTester 506 { 507 508 Object val1 = null; 509 Object val2 = null; 510 511 /** 512 * Constructs a property tester with two different values 513 * specified as parameters. 514 * 515 * @param val1 Not <code>null</code> value for the property 516 * tested. 517 * @param val1 Not <code>null</code> value for the property 518 * tested which differs from the first value. 519 */ 520 public PropertyValueSwitcher(Object val1, Object val2) 521 { 522 this.val1 = val1; 523 this.val2 = val2; 524 } 525 526 /** 527 * Overriden method of <code>PropertyTester</code> which 528 * retruns new value from two values specified. 529 * 530 * @return The second value if old value is equal to the first 531 * one, the first value otherwise. 532 */ 533 protected Object getNewValue(String propName, Object old) 534 { 535 if (ValueComparer.equalValue(val1, old)) 536 { 537 return val2; 538 } 539 else 540 { 541 return val1; 542 } 543 } 544 } 545 546 /** 547 * The method performs testing of propName property using propTester. 548 */ 549 protected void testProperty(String propName, PropertyTester propTester) 550 { 551 propTester.testProperty(propName); 552 } 553 554 /** 555 * The method performs testing of propName property. It uses PropertyTester 556 * instance for testing. 557 */ 558 protected void testProperty(String propName) 559 { 560 testProperty(propName, new PropertyTester()); 561 } 562 563 /** 564 * Tests the property using <code>PropertyValueSwitcher</code> 565 * tester and two values for this property. 566 * 567 * @see #PropertyValueSwitcher 568 */ 569 protected void testProperty(String propName, Object val1, Object val2) 570 { 571 testProperty(propName, new PropertyValueSwitcher(val1, val2)); 572 } 573 574 /** 575 * The method just calls compare. This is a workaround to CodeWarrior's 576 * compiler bug. 577 */ 578 private boolean callCompare(Object obj1, Object obj2) 579 { 580 return compare(obj1, obj2); 581 } 582 583 /** 584 * Compares two object. In the implementation calls obj1.equals(obj2). 585 */ 586 protected boolean compare(Object obj1, Object obj2) 587 { 588 return ValueComparer.equalValue(obj1, obj2); 589 } 590 591 /** 592 * The method just calls toString. This is a workaround to 593 * CodeWarrior's compiler bug. 594 */ 595 private String callToString(Object obj) 596 { 597 return toString(obj); 598 } 599 600 /** 601 * Gets string representation of the obj. In the implementation 602 * returns obj.toString(). 603 */ 604 protected String toString(Object obj) 605 { 606 return obj == null ? "null" : obj.toString(); 607 } 608 } 609