001 // Copyright 2004, 2005 The Apache Software Foundation 002 // 003 // Licensed under the Apache License, Version 2.0 (the "License"); 004 // you may not use this file except in compliance with the License. 005 // You may obtain a copy of the License at 006 // 007 // http://www.apache.org/licenses/LICENSE-2.0 008 // 009 // Unless required by applicable law or agreed to in writing, software 010 // distributed under the License is distributed on an "AS IS" BASIS, 011 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 012 // See the License for the specific language governing permissions and 013 // limitations under the License. 014 015 package org.apache.tapestry.enhance; 016 017 import org.apache.hivemind.ApplicationRuntimeException; 018 import org.apache.hivemind.ClassResolver; 019 import org.apache.hivemind.Location; 020 import org.apache.hivemind.impl.DefaultClassResolver; 021 import org.apache.hivemind.service.BodyBuilder; 022 import org.apache.hivemind.service.ClassFab; 023 import org.apache.hivemind.service.ClassFactory; 024 import org.apache.hivemind.service.MethodSignature; 025 import org.apache.hivemind.service.impl.ClassFactoryImpl; 026 import org.apache.tapestry.*; 027 import org.apache.tapestry.components.Insert; 028 import org.apache.tapestry.event.PageDetachListener; 029 import org.apache.tapestry.event.PageValidateListener; 030 import org.apache.tapestry.link.ServiceLink; 031 import org.apache.tapestry.services.ComponentConstructor; 032 import org.apache.tapestry.spec.IComponentSpecification; 033 import static org.easymock.EasyMock.*; 034 import org.testng.annotations.BeforeMethod; 035 import org.testng.annotations.Test; 036 037 import java.lang.reflect.Modifier; 038 import java.util.HashMap; 039 import java.util.List; 040 import java.util.Map; 041 import java.util.Set; 042 043 /** 044 * Tests for {@link org.apache.tapestry.enhance.EnhancementOperationImpl}. 045 * 046 * @author Howard M. Lewis Ship 047 * @since 4.0 048 */ 049 @Test(sequential=true) 050 public class TestEnhancementOperation extends BaseComponentTestCase 051 { 052 @BeforeMethod(alwaysRun=true) 053 protected void setUp() throws Exception 054 { 055 EnhancementOperationImpl._uid = 97; 056 } 057 058 public abstract static class Fixture 059 { 060 public abstract String getStringProperty(); 061 062 public abstract boolean isBooleanProperty(); 063 064 public abstract boolean getFlagProperty(); 065 } 066 067 public abstract static class ValidatingComponent extends AbstractComponent implements 068 PageValidateListener 069 { 070 } 071 072 public abstract static class GetClassReferenceFixture 073 { 074 public abstract Class getClassReference(); 075 } 076 077 public static class MissingConstructorFixture 078 { 079 public MissingConstructorFixture(String foo) 080 { 081 // 082 } 083 } 084 085 public abstract static class UnclaimedAbstractPropertiesFixture 086 { 087 public abstract String getReadOnly(); 088 089 public abstract void setWriteOnly(String value); 090 091 public void setConcrete(int i) 092 { 093 // 094 } 095 096 public int getConcrete() 097 { 098 return -1; 099 } 100 } 101 102 public void test_Claimed_Property() 103 { 104 EnhancementOperationImpl eo = new EnhancementOperationImpl(); 105 106 eo.claimProperty("foo"); 107 eo.claimProperty("bar"); 108 109 try 110 { 111 eo.claimProperty("foo"); 112 unreachable(); 113 } 114 catch (ApplicationRuntimeException ex) 115 { 116 assertEquals(EnhanceMessages.claimedProperty("foo"), ex.getMessage()); 117 } 118 } 119 120 public void test_Claim_Readonly_Property_Does_Not_Exist() 121 { 122 IComponentSpecification spec = newSpec(); 123 ClassFactory cf = newClassFactory(); 124 125 replay(); 126 127 EnhancementOperationImpl eo = new EnhancementOperationImpl(new DefaultClassResolver(), spec, 128 BaseComponent.class, cf, null); 129 130 assertFalse(eo.canClaimAsReadOnlyProperty("foo")); 131 132 eo.claimReadonlyProperty("foo"); 133 134 assertFalse(eo.canClaimAsReadOnlyProperty("foo")); 135 136 verify(); 137 } 138 139 public void test_Claim_Readonly_Property_Claimed() 140 { 141 IComponentSpecification spec = newSpec(); 142 ClassFactory cf = newClassFactory(); 143 144 replay(); 145 146 EnhancementOperation eo = new EnhancementOperationImpl(new DefaultClassResolver(), spec, 147 BaseComponent.class, cf, null); 148 149 eo.claimReadonlyProperty("foo"); 150 eo.claimReadonlyProperty("bar"); 151 152 try 153 { 154 eo.claimProperty("foo"); 155 unreachable(); 156 } 157 catch (ApplicationRuntimeException ex) 158 { 159 assertEquals(EnhanceMessages.claimedProperty("foo"), ex.getMessage()); 160 } 161 162 verify(); 163 } 164 165 public void test_Claim_Readonly_Property_Has_Setter() 166 { 167 IComponentSpecification spec = newSpec(); 168 ClassFactory cf = newClassFactory(); 169 170 replay(); 171 172 EnhancementOperation eo = new EnhancementOperationImpl(new DefaultClassResolver(), spec, 173 BaseComponent.class, cf, null); 174 175 try 176 { 177 // id is a read/write property (even if it isn't abstract) 178 eo.claimReadonlyProperty("id"); 179 unreachable(); 180 } 181 catch (ApplicationRuntimeException ex) 182 { 183 assertEquals( 184 "Property id should be read-only; remove method public void org.apache.tapestry.AbstractComponent.setId(java.lang.String).", 185 ex.getMessage()); 186 } 187 188 verify(); 189 } 190 191 private ClassFactory newClassFactory() 192 { 193 return newClassFactory(BaseComponent.class); 194 } 195 196 private ClassFactory newClassFactory(Class baseClass) 197 { 198 return newClassFactory(baseClass, newClassFab()); 199 } 200 201 private ClassFactory newClassFactory(Class baseClass, ClassFab classFab) 202 { 203 ClassFactory factory = newMock(ClassFactory.class); 204 205 String className = baseClass.getName(); 206 int dotx = className.lastIndexOf('.'); 207 String baseName = className.substring(dotx + 1); 208 209 expect(factory.newClass(startsWith("$" + baseName), eq(baseClass))).andReturn(classFab); 210 211 return factory; 212 } 213 214 private ClassFab newClassFab() 215 { 216 return newMock(ClassFab.class); 217 } 218 219 public void test_Constructor_And_Accessors() 220 { 221 IComponentSpecification spec = newSpec(); 222 ClassFactory cf = newClassFactory(); 223 224 replay(); 225 226 EnhancementOperation eo = new EnhancementOperationImpl(new DefaultClassResolver(), spec, 227 BaseComponent.class, cf, null); 228 229 assertSame(BaseComponent.class, eo.getBaseClass()); 230 231 verify(); 232 } 233 234 public void test_Check_Implements_No_Interface() 235 { 236 IComponentSpecification spec = newSpec(); 237 ClassFactory cf = newClassFactory(); 238 239 replay(); 240 241 EnhancementOperation eo = new EnhancementOperationImpl(new DefaultClassResolver(), spec, 242 BaseComponent.class, cf, null); 243 244 assertEquals(false, eo.implementsInterface(PageValidateListener.class)); 245 246 verify(); 247 } 248 249 public void test_Check_Implements_Class_Implements() 250 { 251 IComponentSpecification spec = newSpec(); 252 ClassFactory cf = newClassFactory(ValidatingComponent.class); 253 254 replay(); 255 256 EnhancementOperation eo = new EnhancementOperationImpl(new DefaultClassResolver(), spec, 257 ValidatingComponent.class, cf, null); 258 259 assertEquals(true, eo.implementsInterface(PageValidateListener.class)); 260 261 verify(); 262 } 263 264 public void testCheckImplementsNoMatchForAddedInterfaces() 265 { 266 IComponentSpecification spec = newSpec(); 267 ClassFactory factory = newMock(ClassFactory.class); 268 ClassFab classfab = newMock(ClassFab.class); 269 270 expect(factory.newClass(startsWith("$BaseComponent"), eq(BaseComponent.class))).andReturn(classfab); 271 272 classfab.addInterface(PageDetachListener.class); 273 274 replay(); 275 276 EnhancementOperation eo = new EnhancementOperationImpl(new DefaultClassResolver(), spec, 277 BaseComponent.class, factory, null); 278 279 eo.extendMethodImplementation( 280 PageDetachListener.class, 281 EnhanceUtils.PAGE_DETACHED_SIGNATURE, 282 "foo();"); 283 284 assertEquals(false, eo.implementsInterface(PageValidateListener.class)); 285 286 verify(); 287 } 288 289 public void testCheckImplementsMatchAddedInterfaces() 290 { 291 IComponentSpecification spec = newSpec(); 292 293 ClassFactory factory = newMock(ClassFactory.class); 294 ClassFab classfab = newMock(ClassFab.class); 295 296 expect(factory.newClass(startsWith("$BaseComponent"), eq(BaseComponent.class))).andReturn(classfab); 297 298 classfab.addInterface(PageDetachListener.class); 299 300 replay(); 301 302 EnhancementOperation eo = new EnhancementOperationImpl(new DefaultClassResolver(), spec, 303 BaseComponent.class, factory, null); 304 305 eo.extendMethodImplementation( 306 PageDetachListener.class, 307 EnhanceUtils.PAGE_DETACHED_SIGNATURE, 308 "foo();"); 309 310 assertEquals(true, eo.implementsInterface(PageDetachListener.class)); 311 312 verify(); 313 } 314 315 public void testValidatePropertyNew() 316 { 317 IComponentSpecification spec = newSpec(); 318 ClassFactory cf = newClassFactory(); 319 320 replay(); 321 322 EnhancementOperation eo = new EnhancementOperationImpl(new DefaultClassResolver(), spec, 323 BaseComponent.class, cf, null); 324 325 // Validates because BaseComponent doesn't have such a property. 326 327 eo.validateProperty("abraxis", Set.class); 328 329 verify(); 330 } 331 332 public void testValidatePropertyMatches() 333 { 334 IComponentSpecification spec = newSpec(); 335 ClassFactory cf = newClassFactory(); 336 337 replay(); 338 339 EnhancementOperation eo = new EnhancementOperationImpl(new DefaultClassResolver(), spec, 340 BaseComponent.class, cf, null); 341 342 // Validates because BaseComponent inherits a page property of type IPage 343 344 eo.validateProperty("page", IPage.class); 345 346 verify(); 347 } 348 349 public void testValidatePropertyMismatch() 350 { 351 IComponentSpecification spec = newSpec(); 352 ClassFactory cf = newClassFactory(); 353 354 replay(); 355 356 EnhancementOperation eo = new EnhancementOperationImpl(new DefaultClassResolver(), spec, 357 BaseComponent.class, cf, null); 358 359 // Validates because BaseComponent inherits a page property of type IPage 360 361 try 362 { 363 eo.validateProperty("page", String.class); 364 unreachable(); 365 } 366 catch (ApplicationRuntimeException ex) 367 { 368 assertEquals(EnhanceMessages.propertyTypeMismatch( 369 BaseComponent.class, 370 "page", 371 IPage.class, 372 String.class), ex.getMessage()); 373 } 374 375 verify(); 376 } 377 378 public void testConvertTypeName() 379 { 380 IComponentSpecification spec = newSpec(); 381 ClassFactory cf = newClassFactory(); 382 383 replay(); 384 385 EnhancementOperation eo = new EnhancementOperationImpl(new DefaultClassResolver(), spec, 386 BaseComponent.class, cf, null); 387 388 assertSame(boolean.class, eo.convertTypeName("boolean")); 389 assertSame(float[].class, eo.convertTypeName("float[]")); 390 assertSame(double[][].class, eo.convertTypeName("double[][]")); 391 392 assertSame(Set.class, eo.convertTypeName(Set.class.getName())); 393 394 assertSame(List[].class, eo.convertTypeName(List.class.getName() + "[]")); 395 396 try 397 { 398 eo.convertTypeName("package.DoesNotExist"); 399 unreachable(); 400 } 401 catch (ApplicationRuntimeException ex) 402 { 403 // Ignore message, it's from HiveMind anyway. 404 } 405 406 verify(); 407 408 } 409 410 public void testAddField() 411 { 412 IComponentSpecification spec = newSpec(); 413 414 ClassFactory cf = newMock(ClassFactory.class); 415 ClassFab fab = newMock(ClassFab.class); 416 417 expect(cf.newClass(startsWith("$BaseComponent"), eq(BaseComponent.class))).andReturn(fab); 418 419 fab.addField("fred", String.class); 420 421 replay(); 422 423 EnhancementOperation eo = new EnhancementOperationImpl(new DefaultClassResolver(), spec, 424 BaseComponent.class, cf, null); 425 426 eo.addField("fred", String.class); 427 428 verify(); 429 } 430 431 public void testAddInjectedField() 432 { 433 IComponentSpecification spec = newSpec(); 434 435 ClassFactory cf = newMock(ClassFactory.class); 436 ClassFab fab = newMock(ClassFab.class); 437 438 expect(cf.newClass(startsWith("$BaseComponent"), eq(BaseComponent.class))).andReturn(fab); 439 440 // String because "FRED_VALUE" is a String 441 442 fab.addField("fred", String.class); 443 444 replay(); 445 446 EnhancementOperationImpl eo = new EnhancementOperationImpl(new DefaultClassResolver(), 447 spec, BaseComponent.class, cf, null); 448 449 assertEquals("fred", eo.addInjectedField("fred", String.class, "FRED_VALUE")); 450 451 verify(); 452 453 HashMap map = new HashMap(); 454 455 fab.addField("fred_0", Map.class); 456 457 replay(); 458 459 assertEquals("fred_0", eo.addInjectedField("fred", Map.class, map)); 460 461 verify(); 462 463 BodyBuilder body = new BodyBuilder(); 464 body.begin(); 465 body.addln("fred = $1;"); 466 body.addln("fred_0 = $2;"); 467 body.end(); 468 469 fab.addConstructor(aryEq(new Class[] { String.class, Map.class }), (Class[])isNull(), 470 eq(body.toString())); 471 472 replay(); 473 474 eo.finalizeEnhancedClass(); 475 476 verify(); 477 } 478 479 public void testAddMethod() 480 { 481 Location l = newLocation(); 482 483 Class baseClass = Insert.class; 484 MethodSignature sig = new MethodSignature(void.class, "frob", null, null); 485 486 IComponentSpecification spec = newSpec(); 487 488 ClassFactory cf = newMock(ClassFactory.class); 489 ClassFab fab = newMock(ClassFab.class); 490 491 // We force the uid to 97 in setUp() 492 493 expect(cf.newClass(startsWith("$Insert"), eq(baseClass))).andReturn(fab); 494 495 expect(fab.addMethod(Modifier.PUBLIC, sig, "method body")).andReturn(null); 496 497 replay(); 498 499 EnhancementOperation eo = new EnhancementOperationImpl(new DefaultClassResolver(), spec, 500 baseClass, cf, null); 501 502 eo.addMethod(Modifier.PUBLIC, sig, "method body", l); 503 504 verify(); 505 } 506 507 public void testAddMethodDuplicate() 508 { 509 Location firstLocation = newLocation(); 510 Location secondLocation = newLocation(); 511 512 Class baseClass = Insert.class; 513 MethodSignature sig = new MethodSignature(void.class, "frob", null, null); 514 515 IComponentSpecification spec = newSpec(); 516 517 ClassFactory cf = newMock(ClassFactory.class); 518 ClassFab fab = newMock(ClassFab.class); 519 520 // We force the uid to 97 in setUp() 521 522 expect(cf.newClass(startsWith("$Insert"), eq(baseClass))).andReturn(fab); 523 524 expect(fab.addMethod(Modifier.PUBLIC, sig, "method body")).andReturn(null); 525 526 replay(); 527 528 EnhancementOperation eo = new EnhancementOperationImpl(new DefaultClassResolver(), spec, 529 baseClass, cf, null); 530 531 eo.addMethod(Modifier.PUBLIC, sig, "method body", firstLocation); 532 533 try 534 { 535 eo.addMethod(Modifier.PUBLIC, sig, "second method body", secondLocation); 536 unreachable(); 537 } 538 catch (ApplicationRuntimeException ex) 539 { 540 assertTrue(ex.getMessage() 541 .indexOf("A new implementation of method 'void frob()' conflicts with an existing " 542 + "implementation") > -1); 543 544 assertSame(secondLocation, ex.getLocation()); 545 } 546 547 verify(); 548 } 549 550 public void testGetAccessorMethodName() 551 { 552 IComponentSpecification spec = newSpec(); 553 ClassFactory cf = newClassFactory(Fixture.class); 554 555 replay(); 556 557 EnhancementOperation eo = new EnhancementOperationImpl(new DefaultClassResolver(), spec, 558 Fixture.class, cf, null); 559 560 assertEquals("getStringProperty", eo.getAccessorMethodName("stringProperty")); 561 assertEquals("isBooleanProperty", eo.getAccessorMethodName("booleanProperty")); 562 assertEquals("getFlagProperty", eo.getAccessorMethodName("flagProperty")); 563 assertEquals("getUnknownProperty", eo.getAccessorMethodName("unknownProperty")); 564 565 verify(); 566 } 567 568 /** 569 * On this test, instead of mocking up everything, we actually use the raw implementations to 570 * construct a new class; the class gets a class reference passed to it in its constructor. 571 */ 572 573 public void test_Get_Class_Reference() throws Exception 574 { 575 Location l = newLocation(); 576 IComponentSpecification spec = newSpec(); 577 578 expect(spec.getLocation()).andReturn(l); 579 580 replay(); 581 582 EnhancementOperationImpl eo = new EnhancementOperationImpl(new DefaultClassResolver(), 583 spec, GetClassReferenceFixture.class, new ClassFactoryImpl(), null); 584 585 // This does two things; it creates a new field, and it sets up a new constructor 586 // parameter to inject the class value (Map.class) into each new instance. 587 588 String ref = eo.getClassReference(Map.class); 589 String ref2 = eo.getClassReference(Map.class); 590 591 eo.addMethod(Modifier.PUBLIC, new MethodSignature(Class.class, "getClassReference", null, 592 null), "return " + ref + ";", l); 593 594 ComponentConstructor cc = eo.getConstructor(); 595 596 GetClassReferenceFixture f = (GetClassReferenceFixture) cc.newInstance(); 597 598 assertSame(Map.class, f.getClassReference()); 599 600 verify(); 601 602 assertSame(ref, ref2); 603 } 604 605 public void testGetArrayClassReference() throws Exception 606 { 607 IComponentSpecification spec = newSpec(); 608 609 replay(); 610 611 EnhancementOperationImpl eo = new EnhancementOperationImpl(new DefaultClassResolver(), 612 spec, GetClassReferenceFixture.class, new ClassFactoryImpl(), null); 613 614 String ref = eo.getClassReference(int[].class); 615 616 assertTrue(ref.indexOf('[') < 0); 617 618 verify(); 619 } 620 621 /** 622 * Really a test for {@link org.apache.tapestry.enhance.ComponentConstructorImpl}; 623 * {@link #test_Get_Class_Reference()} tests the success case, just want to fill in the failure. 624 */ 625 626 public void testComponentConstructorFailure() 627 { 628 Location l = newLocation(); 629 630 ComponentConstructor cc = new ComponentConstructorImpl(BaseComponent.class 631 .getConstructors()[0], new Object[] 632 { "unexpected" }, "<classfab>", l); 633 634 try 635 { 636 cc.newInstance(); 637 unreachable(); 638 } 639 catch (ApplicationRuntimeException ex) 640 { 641 assertExceptionSubstring( 642 ex, 643 "Unable to instantiate instance of class org.apache.tapestry.BaseComponent"); 644 assertSame(l, ex.getLocation()); 645 } 646 } 647 648 public void test_Get_Property_Type() 649 { 650 IComponentSpecification spec = newSpec(); 651 ClassFactory cf = newClassFactory(); 652 653 replay(); 654 655 EnhancementOperation eo = new EnhancementOperationImpl(new DefaultClassResolver(), spec, 656 BaseComponent.class, cf, null); 657 658 assertEquals(Map.class, eo.getPropertyType("assets")); 659 assertEquals(IPage.class, eo.getPropertyType("page")); 660 661 // Doesn't exist, so returns null 662 663 assertNull(eo.getPropertyType("foosball")); 664 665 verify(); 666 } 667 668 public void test_Find_Unclaimed_Abstract_Properties() 669 { 670 ClassResolver cr = newMock(ClassResolver.class); 671 IComponentSpecification spec = newSpec(); 672 ClassFactory cf = newClassFactory(UnclaimedAbstractPropertiesFixture.class); 673 674 replay(); 675 676 EnhancementOperation eo = new EnhancementOperationImpl(cr, spec, 677 UnclaimedAbstractPropertiesFixture.class, cf, null); 678 679 List l = eo.findUnclaimedAbstractProperties(); 680 681 assertEquals(2, l.size()); 682 assertEquals(true, l.contains("readOnly")); 683 assertEquals(true, l.contains("writeOnly")); 684 685 eo.claimProperty("readOnly"); 686 687 l = eo.findUnclaimedAbstractProperties(); 688 689 assertEquals(1, l.size()); 690 assertEquals(true, l.contains("writeOnly")); 691 692 eo.claimProperty("writeOnly"); 693 694 l = eo.findUnclaimedAbstractProperties(); 695 696 assertEquals(true, l.isEmpty()); 697 698 verify(); 699 } 700 701 public void testGetNewMethod() 702 { 703 ClassResolver cr = new DefaultClassResolver(); 704 705 IComponentSpecification spec = newSpec(); 706 707 ClassFactory cf = newMock(ClassFactory.class); 708 709 ClassFab fab = newMock(ClassFab.class); 710 711 expect(cf.newClass(startsWith("$BaseComponent"), eq(BaseComponent.class))).andReturn(fab); 712 713 fab.addInterface(PageDetachListener.class); 714 715 replay(); 716 717 EnhancementOperationImpl eo = new EnhancementOperationImpl(cr, spec, BaseComponent.class, 718 cf, null); 719 720 MethodSignature sig = EnhanceUtils.PAGE_DETACHED_SIGNATURE; 721 722 eo.extendMethodImplementation(PageDetachListener.class, sig, "some-code();"); 723 724 verify(); 725 726 replay(); 727 728 // Check that repeated calls do not 729 // keep adding methods. 730 731 eo.extendMethodImplementation(PageDetachListener.class, sig, "more-code();"); 732 733 verify(); 734 735 expect(fab.addMethod(Modifier.PUBLIC, sig, "{\n some-code();\n more-code();\n}\n")).andReturn(null); 736 737 expect(fab.createClass()).andReturn(BaseComponent.class); 738 739 expect(spec.getLocation()).andReturn(null); 740 741 replay(); 742 743 eo.getConstructor(); 744 745 verify(); 746 } 747 748 public void testGetExistingMethod() 749 { 750 ClassResolver cr = new DefaultClassResolver(); 751 752 IComponentSpecification spec = newSpec(); 753 754 ClassFactory cf = newMock(ClassFactory.class); 755 756 ClassFab fab = newMock(ClassFab.class); 757 758 expect(cf.newClass(startsWith("$BaseComponent"), eq(BaseComponent.class))).andReturn(fab); 759 760 replay(); 761 762 EnhancementOperationImpl eo = new EnhancementOperationImpl(cr, spec, BaseComponent.class, 763 cf, null); 764 765 MethodSignature sig = EnhanceUtils.FINISH_LOAD_SIGNATURE; 766 767 eo.extendMethodImplementation(IComponent.class, sig, "some-code();"); 768 769 verify(); 770 771 expect(fab.addMethod(Modifier.PUBLIC, sig, "{\n super.finishLoad($$);\n some-code();\n}\n")) 772 .andReturn(null); 773 774 expect(fab.createClass()).andReturn(BaseComponent.class); 775 776 expect(spec.getLocation()).andReturn(null); 777 778 replay(); 779 780 eo.getConstructor(); 781 782 verify(); 783 } 784 785 public void testGetExistingProtectedMethod() 786 { 787 ClassResolver cr = new DefaultClassResolver(); 788 IComponentSpecification spec = newSpec(); 789 790 ClassFactory cf = newMock(ClassFactory.class); 791 792 ClassFab fab = newMock(ClassFab.class); 793 794 expect(cf.newClass(startsWith("$BaseComponent"), eq(BaseComponent.class))).andReturn(fab); 795 796 replay(); 797 798 EnhancementOperationImpl eo = new EnhancementOperationImpl(cr, spec, BaseComponent.class, 799 cf, null); 800 801 // A protected method 802 MethodSignature sig = EnhanceUtils.CLEANUP_AFTER_RENDER_SIGNATURE; 803 804 eo.extendMethodImplementation(IComponent.class, sig, "some-code();"); 805 806 verify(); 807 808 expect(fab.addMethod( 809 Modifier.PUBLIC, 810 sig, 811 "{\n super.cleanupAfterRender($$);\n some-code();\n}\n")) 812 .andReturn(null); 813 814 expect(fab.createClass()).andReturn(BaseComponent.class); 815 816 expect(spec.getLocation()).andReturn(null); 817 818 replay(); 819 820 eo.getConstructor(); 821 822 verify(); 823 } 824 825 public static abstract class ExistingAbstractMethodFixture extends BaseComponent implements 826 PageDetachListener 827 { 828 // 829 } 830 831 public void getExistingAbstractMethod() 832 { 833 ClassResolver cr = new DefaultClassResolver(); 834 IComponentSpecification spec = newSpec(); 835 836 ClassFactory cf = newMock(ClassFactory.class); 837 838 ClassFab fab = newMock(ClassFab.class); 839 840 expect(cf.newClass(startsWith("$TestEnhancementOperation$ExistingAbstractMethodFixture"), eq(TestEnhancementOperation.ExistingAbstractMethodFixture.class))) 841 .andReturn(fab); 842 843 replay(); 844 845 EnhancementOperationImpl eo = new EnhancementOperationImpl(cr, spec, 846 ExistingAbstractMethodFixture.class, cf, null); 847 848 MethodSignature sig = EnhanceUtils.PAGE_DETACHED_SIGNATURE; 849 850 eo.extendMethodImplementation(PageDetachListener.class, sig, "some-code();"); 851 852 verify(); 853 854 expect(fab.addMethod(Modifier.PUBLIC, sig, "{\n some-code();\n}\n")).andReturn(null); 855 856 expect(fab.createClass()).andReturn(BaseComponent.class); 857 858 expect(spec.getLocation()).andReturn(null); 859 860 replay(); 861 862 eo.getConstructor(); 863 864 verify(); 865 } 866 867 /** 868 * This seems to pass on the command line, but fail inside Eclipse. I think Eclipse's Java 869 * compiler works a little different from Java's ... in this example (TODO: Create test fixture 870 * classes for this test) the getTarget() method doesn't show up as a declared public method 871 * when Eclipse compiles the code, but does when JDK compiles the code. 872 */ 873 public void testPropertyInheritedFromInterface() 874 { 875 IComponentSpecification spec = newSpec(); 876 ClassFactory cf = newClassFactory(ServiceLink.class); 877 878 replay(); 879 880 EnhancementOperation eo = new EnhancementOperationImpl(new DefaultClassResolver(), spec, ServiceLink.class, cf, null); 881 882 assertEquals(String.class, eo.getPropertyType("target")); 883 884 verify(); 885 } 886 887 public void testConstructorFailure() 888 { 889 IComponentSpecification spec = newSpec(); 890 891 ClassFab classFab = newMock(ClassFab.class); 892 893 ClassFactory cf = newClassFactory(ServiceLink.class, classFab); 894 895 Throwable t = new RuntimeException("Inconceivable!"); 896 897 expect(classFab.createClass()).andThrow(t); 898 899 replay(); 900 901 EnhancementOperationImpl eo = new EnhancementOperationImpl(new DefaultClassResolver(), 902 spec, ServiceLink.class, cf, null); 903 904 try 905 { 906 eo.getConstructor(); 907 unreachable(); 908 } 909 catch (ApplicationRuntimeException ex) 910 { 911 assertEquals( 912 "Failure enhancing class org.apache.tapestry.link.ServiceLink: Inconceivable!", 913 ex.getMessage()); 914 assertSame(classFab, ex.getComponent()); 915 assertSame(t, ex.getRootCause()); 916 } 917 918 verify(); 919 } 920 921 }