1/** 2 * Copyright (C) 2007 Google Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.google.inject.throwingproviders; 18 19import static java.lang.annotation.ElementType.METHOD; 20import static java.lang.annotation.RetentionPolicy.RUNTIME; 21 22import com.google.common.base.Function; 23import com.google.common.collect.ImmutableList; 24import com.google.common.collect.ImmutableSet; 25import com.google.common.collect.Iterables; 26import com.google.inject.AbstractModule; 27import com.google.inject.BindingAnnotation; 28import com.google.inject.CreationException; 29import com.google.inject.Guice; 30import com.google.inject.Inject; 31import com.google.inject.Injector; 32import com.google.inject.Key; 33import com.google.inject.TypeLiteral; 34import com.google.inject.internal.util.Classes; 35import com.google.inject.name.Named; 36import com.google.inject.name.Names; 37import com.google.inject.spi.Dependency; 38import com.google.inject.spi.HasDependencies; 39import com.google.inject.spi.Message; 40 41import junit.framework.TestCase; 42 43import java.io.IOException; 44import java.lang.annotation.Annotation; 45import java.lang.annotation.Retention; 46import java.lang.annotation.Target; 47import java.rmi.AccessException; 48import java.rmi.RemoteException; 49import java.util.Arrays; 50import java.util.List; 51import java.util.Set; 52import java.util.TooManyListenersException; 53 54/** 55 * @author jmourits@google.com (Jerome Mourits) 56 * @author jessewilson@google.com (Jesse Wilson) 57 */ 58@SuppressWarnings("deprecation") 59public class ThrowingProviderTest extends TestCase { 60 @Target(METHOD) @Retention(RUNTIME) @BindingAnnotation 61 @interface NotExceptionScoping { }; 62 63 private final TypeLiteral<RemoteProvider<String>> remoteProviderOfString 64 = new TypeLiteral<RemoteProvider<String>>() { }; 65 private final MockRemoteProvider<String> mockRemoteProvider = new MockRemoteProvider<String>(); 66 private final TestScope testScope = new TestScope(); 67 private Injector bindInjector = Guice.createInjector(new AbstractModule() { 68 protected void configure() { 69 ThrowingProviderBinder.create(binder()) 70 .bind(RemoteProvider.class, String.class) 71 .to(mockRemoteProvider) 72 .in(testScope); 73 74 ThrowingProviderBinder.create(binder()) 75 .bind(RemoteProvider.class, String.class) 76 .annotatedWith(NotExceptionScoping.class) 77 .scopeExceptions(false) 78 .to(mockRemoteProvider) 79 .in(testScope); 80 } 81 }); 82 private Injector providesInjector = Guice.createInjector(new AbstractModule() { 83 protected void configure() { 84 install(ThrowingProviderBinder.forModule(this)); 85 bindScope(TestScope.Scoped.class, testScope); 86 } 87 88 @SuppressWarnings("unused") 89 @CheckedProvides(RemoteProvider.class) 90 @TestScope.Scoped 91 String throwOrGet() throws RemoteException { 92 return mockRemoteProvider.get(); 93 } 94 95 @SuppressWarnings("unused") 96 @CheckedProvides(value = RemoteProvider.class, scopeExceptions = false) 97 @NotExceptionScoping 98 @TestScope.Scoped 99 String notExceptionScopingThrowOrGet() throws RemoteException { 100 return mockRemoteProvider.get(); 101 } 102 }); 103 104 public void testExceptionsThrown_Bind() { 105 tExceptionsThrown(bindInjector); 106 } 107 108 public void testExceptionsThrown_Provides() { 109 tExceptionsThrown(providesInjector); 110 } 111 112 private void tExceptionsThrown(Injector injector) { 113 RemoteProvider<String> remoteProvider = 114 injector.getInstance(Key.get(remoteProviderOfString)); 115 116 mockRemoteProvider.throwOnNextGet("kaboom!"); 117 try { 118 remoteProvider.get(); 119 fail(); 120 } catch (RemoteException expected) { 121 assertEquals("kaboom!", expected.getMessage()); 122 } 123 } 124 125 public void testValuesScoped_Bind() throws RemoteException { 126 tValuesScoped(bindInjector, null); 127 } 128 129 public void testValuesScoped_Provides() throws RemoteException { 130 tValuesScoped(providesInjector, null); 131 } 132 133 public void testValuesScopedWhenNotExceptionScoping_Bind() throws RemoteException { 134 tValuesScoped(bindInjector, NotExceptionScoping.class); 135 } 136 137 public void testValuesScopedWhenNotExceptionScoping_Provides() throws RemoteException { 138 tValuesScoped(providesInjector, NotExceptionScoping.class); 139 } 140 141 private void tValuesScoped(Injector injector, Class<? extends Annotation> annotation) 142 throws RemoteException { 143 Key<RemoteProvider<String>> key = annotation != null ? 144 Key.get(remoteProviderOfString, annotation) : 145 Key.get(remoteProviderOfString); 146 RemoteProvider<String> remoteProvider = injector.getInstance(key); 147 148 mockRemoteProvider.setNextToReturn("A"); 149 assertEquals("A", remoteProvider.get()); 150 151 mockRemoteProvider.setNextToReturn("B"); 152 assertEquals("A", remoteProvider.get()); 153 154 testScope.beginNewScope(); 155 assertEquals("B", remoteProvider.get()); 156 } 157 158 public void testExceptionsScoped_Bind() { 159 tExceptionsScoped(bindInjector); 160 } 161 162 public void testExceptionsScoped_Provides() { 163 tExceptionsScoped(providesInjector); 164 } 165 166 private void tExceptionsScoped(Injector injector) { 167 RemoteProvider<String> remoteProvider = 168 injector.getInstance(Key.get(remoteProviderOfString)); 169 170 mockRemoteProvider.throwOnNextGet("A"); 171 try { 172 remoteProvider.get(); 173 fail(); 174 } catch (RemoteException expected) { 175 assertEquals("A", expected.getMessage()); 176 } 177 178 mockRemoteProvider.throwOnNextGet("B"); 179 try { 180 remoteProvider.get(); 181 fail(); 182 } catch (RemoteException expected) { 183 assertEquals("A", expected.getMessage()); 184 } 185 } 186 187 public void testExceptionsNotScopedWhenNotExceptionScoping_Bind() { 188 tExceptionsNotScopedWhenNotExceptionScoping(bindInjector); 189 } 190 191 public void testExceptionsNotScopedWhenNotExceptionScoping_Provides() { 192 tExceptionsNotScopedWhenNotExceptionScoping(providesInjector); 193 } 194 195 private void tExceptionsNotScopedWhenNotExceptionScoping(Injector injector) { 196 RemoteProvider<String> remoteProvider = 197 injector.getInstance(Key.get(remoteProviderOfString, NotExceptionScoping.class)); 198 199 mockRemoteProvider.throwOnNextGet("A"); 200 try { 201 remoteProvider.get(); 202 fail(); 203 } catch (RemoteException expected) { 204 assertEquals("A", expected.getMessage()); 205 } 206 207 mockRemoteProvider.throwOnNextGet("B"); 208 try { 209 remoteProvider.get(); 210 fail(); 211 } catch (RemoteException expected) { 212 assertEquals("B", expected.getMessage()); 213 } 214 } 215 216 public void testAnnotations_Bind() throws RemoteException { 217 final MockRemoteProvider<String> mockRemoteProviderA = new MockRemoteProvider<String>(); 218 final MockRemoteProvider<String> mockRemoteProviderB = new MockRemoteProvider<String>(); 219 bindInjector = Guice.createInjector(new AbstractModule() { 220 protected void configure() { 221 ThrowingProviderBinder.create(binder()) 222 .bind(RemoteProvider.class, String.class) 223 .annotatedWith(Names.named("a")) 224 .to(mockRemoteProviderA); 225 226 ThrowingProviderBinder.create(binder()) 227 .bind(RemoteProvider.class, String.class) 228 .to(mockRemoteProviderB); 229 } 230 }); 231 tAnnotations(bindInjector, mockRemoteProviderA, mockRemoteProviderB); 232 } 233 234 public void testAnnotations_Provides() throws RemoteException { 235 final MockRemoteProvider<String> mockRemoteProviderA = new MockRemoteProvider<String>(); 236 final MockRemoteProvider<String> mockRemoteProviderB = new MockRemoteProvider<String>(); 237 providesInjector = Guice.createInjector(new AbstractModule() { 238 protected void configure() { 239 install(ThrowingProviderBinder.forModule(this)); 240 } 241 242 @SuppressWarnings("unused") 243 @CheckedProvides(RemoteProvider.class) 244 @Named("a") 245 String throwOrGet() throws RemoteException { 246 return mockRemoteProviderA.get(); 247 } 248 249 @SuppressWarnings("unused") 250 @CheckedProvides(RemoteProvider.class) 251 String throwOrGet2() throws RemoteException { 252 return mockRemoteProviderB.get(); 253 } 254 }); 255 tAnnotations(providesInjector, mockRemoteProviderA, mockRemoteProviderB); 256 } 257 258 private void tAnnotations(Injector injector, MockRemoteProvider<String> mockA, 259 MockRemoteProvider<String> mockB) throws RemoteException { 260 mockA.setNextToReturn("A"); 261 mockB.setNextToReturn("B"); 262 assertEquals("A", 263 injector.getInstance(Key.get(remoteProviderOfString, Names.named("a"))).get()); 264 265 assertEquals("B", 266 injector.getInstance(Key.get(remoteProviderOfString)).get()); 267 } 268 269 public void testUndeclaredExceptions_Bind() throws RemoteException { 270 tUndeclaredExceptions(bindInjector); 271 } 272 273 public void testUndeclaredExceptions_Provides() throws RemoteException { 274 tUndeclaredExceptions(providesInjector); 275 } 276 277 private void tUndeclaredExceptions(Injector injector) throws RemoteException { 278 RemoteProvider<String> remoteProvider = 279 injector.getInstance(Key.get(remoteProviderOfString)); 280 mockRemoteProvider.throwOnNextGet(new IndexOutOfBoundsException("A")); 281 try { 282 remoteProvider.get(); 283 fail(); 284 } catch (RuntimeException e) { 285 assertEquals("A", e.getCause().getMessage()); 286 } 287 288 // undeclared exceptions shouldn't be scoped 289 mockRemoteProvider.throwOnNextGet(new IndexOutOfBoundsException("B")); 290 try { 291 remoteProvider.get(); 292 fail(); 293 } catch (RuntimeException e) { 294 assertEquals("B", e.getCause().getMessage()); 295 } 296 } 297 298 public void testThrowingProviderSubclassing() throws RemoteException { 299 final SubMockRemoteProvider aProvider = new SubMockRemoteProvider(); 300 aProvider.setNextToReturn("A"); 301 302 bindInjector = Guice.createInjector(new AbstractModule() { 303 protected void configure() { 304 ThrowingProviderBinder.create(binder()) 305 .bind(RemoteProvider.class, String.class) 306 .to(aProvider); 307 } 308 }); 309 310 assertEquals("A", 311 bindInjector.getInstance(Key.get(remoteProviderOfString)).get()); 312 } 313 314 static class SubMockRemoteProvider extends MockRemoteProvider<String> { } 315 316 public void testBindingToNonInterfaceType_Bind() throws RemoteException { 317 try { 318 Guice.createInjector(new AbstractModule() { 319 protected void configure() { 320 ThrowingProviderBinder.create(binder()) 321 .bind(MockRemoteProvider.class, String.class) 322 .to(mockRemoteProvider); 323 } 324 }); 325 fail(); 326 } catch (CreationException expected) { 327 assertEquals(MockRemoteProvider.class.getName() + " must be an interface", 328 Iterables.getOnlyElement(expected.getErrorMessages()).getMessage()); 329 } 330 } 331 332 public void testBindingToNonInterfaceType_Provides() throws RemoteException { 333 try { 334 Guice.createInjector(new AbstractModule() { 335 protected void configure() { 336 install(ThrowingProviderBinder.forModule(this)); 337 } 338 339 @SuppressWarnings("unused") 340 @CheckedProvides(MockRemoteProvider.class) 341 String foo() { 342 return null; 343 } 344 }); 345 fail(); 346 } catch (CreationException expected) { 347 assertEquals(MockRemoteProvider.class.getName() + " must be an interface", 348 Iterables.getOnlyElement(expected.getErrorMessages()).getMessage()); 349 } 350 } 351 352 public void testBindingToSubSubInterface_Bind() throws RemoteException { 353 try { 354 bindInjector = Guice.createInjector(new AbstractModule() { 355 protected void configure() { 356 ThrowingProviderBinder.create(binder()) 357 .bind(SubRemoteProvider.class, String.class); 358 } 359 }); 360 fail(); 361 } catch (CreationException expected) { 362 assertEquals(SubRemoteProvider.class.getName() + " must extend CheckedProvider (and only CheckedProvider)", 363 Iterables.getOnlyElement(expected.getErrorMessages()).getMessage()); 364 } 365 } 366 367 public void testBindingToSubSubInterface_Provides() throws RemoteException { 368 try { 369 Guice.createInjector(new AbstractModule() { 370 protected void configure() { 371 install(ThrowingProviderBinder.forModule(this)); 372 } 373 374 @SuppressWarnings("unused") 375 @CheckedProvides(SubRemoteProvider.class) 376 String foo() { 377 return null; 378 } 379 }); 380 fail(); 381 } catch (CreationException expected) { 382 assertEquals(SubRemoteProvider.class.getName() + " must extend CheckedProvider (and only CheckedProvider)", 383 Iterables.getOnlyElement(expected.getErrorMessages()).getMessage()); 384 } 385 } 386 387 interface SubRemoteProvider extends RemoteProvider<String> { } 388 389 public void testBindingToInterfaceWithExtraMethod_Bind() throws RemoteException { 390 try { 391 bindInjector = Guice.createInjector(new AbstractModule() { 392 protected void configure() { 393 ThrowingProviderBinder.create(binder()) 394 .bind(RemoteProviderWithExtraMethod.class, String.class); 395 } 396 }); 397 fail(); 398 } catch (CreationException expected) { 399 assertEquals(RemoteProviderWithExtraMethod.class.getName() + " may not declare any new methods, but declared " 400 + RemoteProviderWithExtraMethod.class.getDeclaredMethods()[0].toGenericString(), 401 Iterables.getOnlyElement(expected.getErrorMessages()).getMessage()); 402 } 403 } 404 405 public void testBindingToInterfaceWithExtraMethod_Provides() throws RemoteException { 406 try { 407 Guice.createInjector(new AbstractModule() { 408 protected void configure() { 409 install(ThrowingProviderBinder.forModule(this)); 410 } 411 412 @SuppressWarnings("unused") 413 @CheckedProvides(RemoteProviderWithExtraMethod.class) 414 String foo() { 415 return null; 416 } 417 }); 418 fail(); 419 } catch (CreationException expected) { 420 assertEquals(RemoteProviderWithExtraMethod.class.getName() + " may not declare any new methods, but declared " 421 + RemoteProviderWithExtraMethod.class.getDeclaredMethods()[0].toGenericString(), 422 Iterables.getOnlyElement(expected.getErrorMessages()).getMessage()); 423 } 424 } 425 426 public void testDependencies_Bind() { 427 bindInjector = Guice.createInjector(new AbstractModule() { 428 protected void configure() { 429 bind(String.class).toInstance("Foo"); 430 bind(Integer.class).toInstance(5); 431 bind(Double.class).toInstance(5d); 432 bind(Long.class).toInstance(5L); 433 ThrowingProviderBinder.create(binder()) 434 .bind(RemoteProvider.class, String.class) 435 .to(DependentRemoteProvider.class); 436 } 437 }); 438 439 HasDependencies hasDependencies = 440 (HasDependencies)bindInjector.getBinding(Key.get(remoteProviderOfString)); 441 hasDependencies = 442 (HasDependencies)bindInjector.getBinding( 443 Iterables.getOnlyElement(hasDependencies.getDependencies()).getKey()); 444 // Make sure that that is dependent on DependentRemoteProvider. 445 assertEquals(Dependency.get(Key.get(DependentRemoteProvider.class)), 446 Iterables.getOnlyElement(hasDependencies.getDependencies())); 447 // And make sure DependentRemoteProvider has the proper dependencies. 448 hasDependencies = (HasDependencies)bindInjector.getBinding(DependentRemoteProvider.class); 449 Set<Key<?>> dependencyKeys = ImmutableSet.copyOf( 450 Iterables.transform(hasDependencies.getDependencies(), 451 new Function<Dependency<?>, Key<?>>() { 452 public Key<?> apply(Dependency<?> from) { 453 return from.getKey(); 454 } 455 })); 456 assertEquals(ImmutableSet.<Key<?>>of(Key.get(String.class), Key.get(Integer.class), 457 Key.get(Long.class), Key.get(Double.class)), dependencyKeys); 458 } 459 460 public void testDependencies_Provides() { 461 providesInjector = Guice.createInjector(new AbstractModule() { 462 protected void configure() { 463 bind(String.class).toInstance("Foo"); 464 bind(Integer.class).toInstance(5); 465 bind(Double.class).toInstance(5d); 466 bind(Long.class).toInstance(5L); 467 install(ThrowingProviderBinder.forModule(this)); 468 } 469 470 @SuppressWarnings("unused") 471 @CheckedProvides(RemoteProvider.class) 472 String foo(String s, Integer i, Double d, Long l) { 473 return null; 474 } 475 }); 476 477 HasDependencies hasDependencies = 478 (HasDependencies)providesInjector.getBinding(Key.get(remoteProviderOfString)); 479 // RemoteProvider<String> is dependent on the provider method.. 480 hasDependencies = 481 (HasDependencies)providesInjector.getBinding( 482 Iterables.getOnlyElement(hasDependencies.getDependencies()).getKey()); 483 // And the provider method has our real dependencies.. 484 hasDependencies = (HasDependencies)providesInjector.getBinding( 485 Iterables.getOnlyElement(hasDependencies.getDependencies()).getKey()); 486 Set<Key<?>> dependencyKeys = ImmutableSet.copyOf( 487 Iterables.transform(hasDependencies.getDependencies(), 488 new Function<Dependency<?>, Key<?>>() { 489 public Key<?> apply(Dependency<?> from) { 490 return from.getKey(); 491 } 492 })); 493 assertEquals(ImmutableSet.<Key<?>>of(Key.get(String.class), Key.get(Integer.class), 494 Key.get(Long.class), Key.get(Double.class)), dependencyKeys); 495 } 496 497 interface RemoteProviderWithExtraMethod<T> extends ThrowingProvider<T, RemoteException> { 498 T get(T defaultValue) throws RemoteException; 499 } 500 501 interface RemoteProvider<T> extends ThrowingProvider<T, RemoteException> { } 502 503 static class DependentRemoteProvider<T> implements RemoteProvider<T> { 504 @Inject double foo; 505 506 @Inject public DependentRemoteProvider(String foo, int bar) { 507 } 508 509 @Inject void initialize(long foo) {} 510 511 public T get() throws RemoteException { 512 return null; 513 } 514 } 515 516 static class MockRemoteProvider<T> implements RemoteProvider<T> { 517 Exception nextToThrow; 518 T nextToReturn; 519 520 public void throwOnNextGet(String message) { 521 throwOnNextGet(new RemoteException(message)); 522 } 523 524 public void throwOnNextGet(Exception nextToThrow) { 525 this.nextToThrow = nextToThrow; 526 } 527 528 public void setNextToReturn(T nextToReturn) { 529 this.nextToReturn = nextToReturn; 530 } 531 532 public T get() throws RemoteException { 533 if (nextToThrow instanceof RemoteException) { 534 throw (RemoteException) nextToThrow; 535 } else if (nextToThrow instanceof RuntimeException) { 536 throw (RuntimeException) nextToThrow; 537 } else if (nextToThrow == null) { 538 return nextToReturn; 539 } else { 540 throw new AssertionError("nextToThrow must be a runtime or remote exception"); 541 } 542 } 543 } 544 545 public void testBindingToInterfaceWithBoundValueType_Bind() throws RemoteException { 546 bindInjector = Guice.createInjector(new AbstractModule() { 547 protected void configure() { 548 ThrowingProviderBinder.create(binder()) 549 .bind(StringRemoteProvider.class, String.class) 550 .to(new StringRemoteProvider() { 551 public String get() throws RemoteException { 552 return "A"; 553 } 554 }); 555 } 556 }); 557 558 assertEquals("A", bindInjector.getInstance(StringRemoteProvider.class).get()); 559 } 560 561 public void testBindingToInterfaceWithBoundValueType_Provides() throws RemoteException { 562 providesInjector = Guice.createInjector(new AbstractModule() { 563 protected void configure() { 564 install(ThrowingProviderBinder.forModule(this)); 565 } 566 567 @SuppressWarnings("unused") 568 @CheckedProvides(StringRemoteProvider.class) 569 String foo() throws RemoteException { 570 return "A"; 571 } 572 }); 573 574 assertEquals("A", providesInjector.getInstance(StringRemoteProvider.class).get()); 575 } 576 577 interface StringRemoteProvider extends ThrowingProvider<String, RemoteException> { } 578 579 public void testBindingToInterfaceWithGeneric_Bind() throws RemoteException { 580 bindInjector = Guice.createInjector(new AbstractModule() { 581 protected void configure() { 582 ThrowingProviderBinder.create(binder()) 583 .bind(RemoteProvider.class, new TypeLiteral<List<String>>() { }.getType()) 584 .to(new RemoteProvider<List<String>>() { 585 public List<String> get() throws RemoteException { 586 return Arrays.asList("A", "B"); 587 } 588 }); 589 } 590 }); 591 592 Key<RemoteProvider<List<String>>> key 593 = Key.get(new TypeLiteral<RemoteProvider<List<String>>>() { }); 594 assertEquals(Arrays.asList("A", "B"), bindInjector.getInstance(key).get()); 595 } 596 597 public void testBindingToInterfaceWithGeneric_Provides() throws RemoteException { 598 providesInjector = Guice.createInjector(new AbstractModule() { 599 protected void configure() { 600 install(ThrowingProviderBinder.forModule(this)); 601 } 602 603 @SuppressWarnings("unused") 604 @CheckedProvides(RemoteProvider.class) 605 List<String> foo() throws RemoteException { 606 return Arrays.asList("A", "B"); 607 } 608 }); 609 610 Key<RemoteProvider<List<String>>> key 611 = Key.get(new TypeLiteral<RemoteProvider<List<String>>>() { }); 612 assertEquals(Arrays.asList("A", "B"), providesInjector.getInstance(key).get()); 613 } 614 615 public void testProviderMethodWithWrongException() { 616 try { 617 Guice.createInjector(new AbstractModule() { 618 protected void configure() { 619 install(ThrowingProviderBinder.forModule(this)); 620 } 621 622 @SuppressWarnings("unused") 623 @CheckedProvides(RemoteProvider.class) 624 String foo() throws InterruptedException { 625 return null; 626 } 627 }); 628 fail(); 629 } catch(CreationException ce) { 630 assertEquals(InterruptedException.class.getName() + " is not compatible with the exceptions ([" 631 + RemoteException.class + "]) declared in the CheckedProvider interface (" 632 + RemoteProvider.class.getName() + ")", 633 Iterables.getOnlyElement(ce.getErrorMessages()).getMessage()); 634 } 635 } 636 637 public void testProviderMethodWithSubclassOfExceptionIsOk() { 638 providesInjector = Guice.createInjector(new AbstractModule() { 639 protected void configure() { 640 install(ThrowingProviderBinder.forModule(this)); 641 } 642 643 @SuppressWarnings("unused") 644 @CheckedProvides(RemoteProvider.class) 645 String foo() throws AccessException { 646 throw new AccessException("boo!"); 647 } 648 }); 649 650 RemoteProvider<String> remoteProvider = 651 providesInjector.getInstance(Key.get(remoteProviderOfString)); 652 653 try { 654 remoteProvider.get(); 655 fail(); 656 } catch (RemoteException expected) { 657 assertTrue(expected instanceof AccessException); 658 assertEquals("boo!", expected.getMessage()); 659 } 660 } 661 662 public void testProviderMethodWithSuperclassFails() { 663 try { 664 Guice.createInjector(new AbstractModule() { 665 protected void configure() { 666 install(ThrowingProviderBinder.forModule(this)); 667 } 668 669 @SuppressWarnings("unused") 670 @CheckedProvides(RemoteProvider.class) 671 String foo() throws IOException { 672 return null; 673 } 674 }); 675 fail(); 676 } catch(CreationException ce) { 677 assertEquals(IOException.class.getName() + " is not compatible with the exceptions ([" 678 + RemoteException.class + "]) declared in the CheckedProvider interface (" 679 + RemoteProvider.class.getName() + ")", 680 Iterables.getOnlyElement(ce.getErrorMessages()).getMessage()); 681 } 682 } 683 684 public void testProviderMethodWithRuntimeExceptionsIsOk() throws RemoteException { 685 providesInjector = Guice.createInjector(new AbstractModule() { 686 protected void configure() { 687 install(ThrowingProviderBinder.forModule(this)); 688 } 689 690 @SuppressWarnings("unused") 691 @CheckedProvides(RemoteProvider.class) 692 String foo() throws RuntimeException { 693 throw new RuntimeException("boo!"); 694 } 695 }); 696 697 RemoteProvider<String> remoteProvider = 698 providesInjector.getInstance(Key.get(remoteProviderOfString)); 699 700 try { 701 remoteProvider.get(); 702 fail(); 703 } catch (RuntimeException expected) { 704 assertEquals("boo!", expected.getCause().getMessage()); 705 } 706 } 707 708 public void testProviderMethodWithManyExceptions() { 709 try { 710 Guice.createInjector(new AbstractModule() { 711 protected void configure() { 712 install(ThrowingProviderBinder.forModule(this)); 713 } 714 715 @SuppressWarnings("unused") 716 @CheckedProvides(RemoteProvider.class) 717 String foo() throws InterruptedException, RuntimeException, RemoteException, 718 AccessException, TooManyListenersException { 719 return null; 720 } 721 }); 722 fail(); 723 } catch(CreationException ce) { 724 // The only two that should fail are Interrupted & TooManyListeners.. the rest are OK. 725 List<Message> errors = ImmutableList.copyOf(ce.getErrorMessages()); 726 assertEquals(InterruptedException.class.getName() + " is not compatible with the exceptions ([" 727 + RemoteException.class + "]) declared in the CheckedProvider interface (" 728 + RemoteProvider.class.getName() + ")", 729 errors.get(0).getMessage()); 730 assertEquals(TooManyListenersException.class.getName() + " is not compatible with the exceptions ([" 731 + RemoteException.class + "]) declared in the CheckedProvider interface (" 732 + RemoteProvider.class.getName() + ")", 733 errors.get(1).getMessage()); 734 assertEquals(2, errors.size()); 735 } 736 } 737 738 public void testMoreTypeParameters() { 739 try { 740 Guice.createInjector(new AbstractModule() { 741 protected void configure() { 742 install(ThrowingProviderBinder.forModule(this)); 743 } 744 745 @SuppressWarnings("unused") 746 @CheckedProvides(TooManyTypeParameters.class) 747 String foo() { 748 return null; 749 } 750 }); 751 fail(); 752 } catch(CreationException ce) { 753 assertEquals(TooManyTypeParameters.class.getName() + " has more than one generic type parameter: [T, P]", 754 Iterables.getOnlyElement(ce.getErrorMessages()).getMessage()); 755 } 756 } 757 758 public void testWrongThrowingProviderType() { 759 try { 760 Guice.createInjector(new AbstractModule() { 761 protected void configure() { 762 install(ThrowingProviderBinder.forModule(this)); 763 } 764 765 @SuppressWarnings("unused") 766 @CheckedProvides(WrongThrowingProviderType.class) 767 String foo() { 768 return null; 769 } 770 }); 771 fail(); 772 } catch(CreationException ce) { 773 assertEquals(WrongThrowingProviderType.class.getName() 774 + " does not properly extend CheckedProvider, the first type parameter of CheckedProvider " 775 + "(java.lang.String) is not a generic type", 776 Iterables.getOnlyElement(ce.getErrorMessages()).getMessage()); 777 } 778 } 779 780 public void testOneMethodThatIsntGet() { 781 try { 782 Guice.createInjector(new AbstractModule() { 783 protected void configure() { 784 install(ThrowingProviderBinder.forModule(this)); 785 } 786 787 @SuppressWarnings("unused") 788 @CheckedProvides(OneNoneGetMethod.class) 789 String foo() { 790 return null; 791 } 792 }); 793 fail(); 794 } catch(CreationException ce) { 795 assertEquals(OneNoneGetMethod.class.getName() 796 + " may not declare any new methods, but declared " + Classes.toString(OneNoneGetMethod.class.getDeclaredMethods()[0]), 797 Iterables.getOnlyElement(ce.getErrorMessages()).getMessage()); 798 } 799 } 800 801 public void testManyMethods() { 802 try { 803 Guice.createInjector(new AbstractModule() { 804 protected void configure() { 805 install(ThrowingProviderBinder.forModule(this)); 806 } 807 808 @SuppressWarnings("unused") 809 @CheckedProvides(ManyMethods.class) 810 String foo() { 811 return null; 812 } 813 }); 814 fail(); 815 } catch(CreationException ce) { 816 assertEquals(ManyMethods.class.getName() 817 + " may not declare any new methods, but declared " + Arrays.asList(ManyMethods.class.getDeclaredMethods()), 818 Iterables.getOnlyElement(ce.getErrorMessages()).getMessage()); 819 } 820 } 821 822 public void testIncorrectPredefinedType_Bind() { 823 try { 824 Guice.createInjector(new AbstractModule() { 825 protected void configure() { 826 ThrowingProviderBinder.create(binder()) 827 .bind(StringRemoteProvider.class, Integer.class) 828 .to(new StringRemoteProvider() { 829 public String get() throws RemoteException { 830 return "A"; 831 } 832 }); 833 } 834 }); 835 fail(); 836 } catch(CreationException ce) { 837 assertEquals(StringRemoteProvider.class.getName() 838 + " expects the value type to be java.lang.String, but it was java.lang.Integer", 839 Iterables.getOnlyElement(ce.getErrorMessages()).getMessage()); 840 } 841 } 842 843 public void testIncorrectPredefinedType_Provides() { 844 try { 845 Guice.createInjector(new AbstractModule() { 846 protected void configure() { 847 install(ThrowingProviderBinder.forModule(this)); 848 } 849 850 @SuppressWarnings("unused") 851 @CheckedProvides(StringRemoteProvider.class) 852 Integer foo() { 853 return null; 854 } 855 }); 856 fail(); 857 } catch(CreationException ce) { 858 assertEquals(StringRemoteProvider.class.getName() 859 + " expects the value type to be java.lang.String, but it was java.lang.Integer", 860 Iterables.getOnlyElement(ce.getErrorMessages()).getMessage()); 861 } 862 } 863 864 private static interface TooManyTypeParameters<T, P> extends ThrowingProvider<T, Exception> { 865 } 866 867 private static interface WrongThrowingProviderType<T> extends ThrowingProvider<String, Exception> { 868 } 869 870 private static interface OneNoneGetMethod<T> extends ThrowingProvider<T, Exception> { 871 T bar(); 872 } 873 874 private static interface ManyMethods<T> extends ThrowingProvider<T, Exception> { 875 T bar(); 876 String baz(); 877 } 878} 879