limpbizkit | b3a8f0b | 2008-09-05 22:30:40 +0000 | [diff] [blame] | 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 | |
| 17 | package com.google.inject.spi; |
| 18 | |
limpbizkit | 759662b | 2009-04-26 21:00:24 +0000 | [diff] [blame] | 19 | import static com.google.inject.Asserts.assertContains; |
sberlin | b7a02b0 | 2011-07-08 00:34:16 +0000 | [diff] [blame] | 20 | import static java.lang.annotation.RetentionPolicy.RUNTIME; |
| 21 | |
| 22 | import com.google.common.collect.ImmutableList; |
| 23 | import com.google.common.collect.ImmutableSet; |
sameb | 9867f9c | 2015-02-02 12:45:25 -0800 | [diff] [blame^] | 24 | import com.google.common.collect.Iterables; |
| 25 | import com.google.common.collect.Lists; |
sberlin | b7a02b0 | 2011-07-08 00:34:16 +0000 | [diff] [blame] | 26 | import com.google.inject.AbstractModule; |
limpbizkit | b3a8f0b | 2008-09-05 22:30:40 +0000 | [diff] [blame] | 27 | import com.google.inject.Binder; |
Sam Berlin | 29ce12b | 2014-03-10 12:52:01 -0400 | [diff] [blame] | 28 | import com.google.inject.Binding; |
limpbizkit | b3a8f0b | 2008-09-05 22:30:40 +0000 | [diff] [blame] | 29 | import com.google.inject.BindingAnnotation; |
| 30 | import com.google.inject.CreationException; |
| 31 | import com.google.inject.Guice; |
| 32 | import com.google.inject.Inject; |
| 33 | import com.google.inject.Injector; |
| 34 | import com.google.inject.Key; |
| 35 | import com.google.inject.Module; |
| 36 | import com.google.inject.Provides; |
sameb | 9867f9c | 2015-02-02 12:45:25 -0800 | [diff] [blame^] | 37 | import com.google.inject.ProvisionException; |
limpbizkit | b3a8f0b | 2008-09-05 22:30:40 +0000 | [diff] [blame] | 38 | import com.google.inject.Singleton; |
Sam Berlin | 409e0f5 | 2014-05-10 10:34:16 -0400 | [diff] [blame] | 39 | import com.google.inject.Stage; |
sameb | 9867f9c | 2015-02-02 12:45:25 -0800 | [diff] [blame^] | 40 | import com.google.inject.TypeLiteral; |
| 41 | import com.google.inject.internal.Errors; |
| 42 | import com.google.inject.internal.InternalFlags; |
limpbizkit | 714df3c | 2009-01-08 02:40:04 +0000 | [diff] [blame] | 43 | import com.google.inject.internal.ProviderMethod; |
| 44 | import com.google.inject.internal.ProviderMethodsModule; |
limpbizkit | b3a8f0b | 2008-09-05 22:30:40 +0000 | [diff] [blame] | 45 | import com.google.inject.name.Named; |
| 46 | import com.google.inject.name.Names; |
sameb | 9867f9c | 2015-02-02 12:45:25 -0800 | [diff] [blame^] | 47 | import com.google.inject.util.Providers; |
limpbizkit | b3a8f0b | 2008-09-05 22:30:40 +0000 | [diff] [blame] | 48 | import com.google.inject.util.Types; |
sberlin | b7a02b0 | 2011-07-08 00:34:16 +0000 | [diff] [blame] | 49 | |
| 50 | import junit.framework.TestCase; |
| 51 | |
limpbizkit | b3a8f0b | 2008-09-05 22:30:40 +0000 | [diff] [blame] | 52 | import java.lang.annotation.ElementType; |
| 53 | import java.lang.annotation.Retention; |
sameb | 9867f9c | 2015-02-02 12:45:25 -0800 | [diff] [blame^] | 54 | import java.lang.annotation.RetentionPolicy; |
limpbizkit | b3a8f0b | 2008-09-05 22:30:40 +0000 | [diff] [blame] | 55 | import java.lang.annotation.Target; |
sameb | 9867f9c | 2015-02-02 12:45:25 -0800 | [diff] [blame^] | 56 | import java.lang.reflect.Method; |
Sam Berlin | 76be88e | 2014-07-01 16:56:13 -0400 | [diff] [blame] | 57 | import java.util.ArrayList; |
Sam Berlin | 409e0f5 | 2014-05-10 10:34:16 -0400 | [diff] [blame] | 58 | import java.util.Collection; |
limpbizkit | b3a8f0b | 2008-09-05 22:30:40 +0000 | [diff] [blame] | 59 | import java.util.List; |
| 60 | import java.util.Set; |
sberlin | 82cbc6d | 2010-05-19 03:12:07 +0000 | [diff] [blame] | 61 | import java.util.concurrent.atomic.AtomicReference; |
sameb | 9867f9c | 2015-02-02 12:45:25 -0800 | [diff] [blame^] | 62 | import java.util.logging.Handler; |
| 63 | import java.util.logging.LogRecord; |
sberlin | 82cbc6d | 2010-05-19 03:12:07 +0000 | [diff] [blame] | 64 | import java.util.logging.Logger; |
| 65 | |
limpbizkit | b3a8f0b | 2008-09-05 22:30:40 +0000 | [diff] [blame] | 66 | /** |
| 67 | * @author crazybob@google.com (Bob Lee) |
| 68 | */ |
| 69 | public class ProviderMethodsTest extends TestCase implements Module { |
| 70 | |
| 71 | @SuppressWarnings("unchecked") |
| 72 | public void testProviderMethods() { |
| 73 | Injector injector = Guice.createInjector(this); |
| 74 | |
| 75 | Bob bob = injector.getInstance(Bob.class); |
| 76 | assertEquals("A Bob", bob.getName()); |
| 77 | |
| 78 | Bob clone = injector.getInstance(Bob.class); |
| 79 | assertEquals("A Bob", clone.getName()); |
| 80 | |
| 81 | assertNotSame(bob, clone); |
| 82 | assertSame(bob.getDaughter(), clone.getDaughter()); |
| 83 | |
| 84 | Key soleBobKey = Key.get(Bob.class, Sole.class); |
| 85 | assertSame( |
| 86 | injector.getInstance(soleBobKey), |
| 87 | injector.getInstance(soleBobKey) |
| 88 | ); |
| 89 | } |
| 90 | |
| 91 | public void configure(Binder binder) {} |
| 92 | |
| 93 | interface Bob { |
| 94 | String getName(); |
| 95 | Dagny getDaughter(); |
| 96 | } |
| 97 | |
| 98 | interface Dagny { |
| 99 | int getAge(); |
| 100 | } |
| 101 | |
| 102 | @Provides |
| 103 | Bob provideBob(final Dagny dagny) { |
| 104 | return new Bob() { |
| 105 | public String getName() { |
| 106 | return "A Bob"; |
| 107 | } |
| 108 | |
| 109 | public Dagny getDaughter() { |
| 110 | return dagny; |
| 111 | } |
| 112 | }; |
| 113 | } |
| 114 | |
| 115 | @Provides |
| 116 | @Singleton |
| 117 | @Sole |
| 118 | Bob provideSoleBob(final Dagny dagny) { |
| 119 | return new Bob() { |
| 120 | public String getName() { |
| 121 | return "Only Bob"; |
| 122 | } |
| 123 | |
| 124 | public Dagny getDaughter() { |
| 125 | return dagny; |
| 126 | } |
| 127 | }; |
| 128 | } |
| 129 | |
| 130 | @Provides |
| 131 | @Singleton |
| 132 | Dagny provideDagny() { |
| 133 | return new Dagny() { |
| 134 | public int getAge() { |
| 135 | return 1; |
| 136 | } |
| 137 | }; |
| 138 | } |
| 139 | |
| 140 | @Retention(RUNTIME) |
| 141 | @Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD }) |
| 142 | @BindingAnnotation |
| 143 | @interface Sole {} |
| 144 | |
| 145 | |
| 146 | |
| 147 | // We'll have to make getProvider() support circular dependencies before this |
| 148 | // will work. |
| 149 | // |
| 150 | // public void testCircularDependency() { |
| 151 | // Injector injector = Guice.createInjector(new Module() { |
| 152 | // public void configure(Binder binder) { |
| 153 | // binder.install(ProviderMethods.from(ProviderMethodsTest.this)); |
| 154 | // } |
| 155 | // }); |
| 156 | // |
| 157 | // Foo foo = injector.getInstance(Foo.class); |
| 158 | // assertEquals(5, foo.getI()); |
| 159 | // assertEquals(10, foo.getBar().getI()); |
| 160 | // assertEquals(5, foo.getBar().getFoo().getI()); |
| 161 | // } |
| 162 | // |
| 163 | // interface Foo { |
| 164 | // Bar getBar(); |
| 165 | // int getI(); |
| 166 | // } |
| 167 | // |
| 168 | // interface Bar { |
| 169 | // Foo getFoo(); |
| 170 | // int getI(); |
| 171 | // } |
| 172 | // |
| 173 | // @Provides Foo newFoo(final Bar bar) { |
| 174 | // return new Foo() { |
| 175 | // |
| 176 | // public Bar getBar() { |
| 177 | // return bar; |
| 178 | // } |
| 179 | // |
| 180 | // public int getI() { |
| 181 | // return 5; |
| 182 | // } |
| 183 | // }; |
| 184 | // } |
| 185 | // |
| 186 | // @Provides Bar newBar(final Foo foo) { |
| 187 | // return new Bar() { |
| 188 | // |
| 189 | // public Foo getFoo() { |
| 190 | // return foo; |
| 191 | // } |
| 192 | // |
| 193 | // public int getI() { |
| 194 | // return 10; |
| 195 | // } |
| 196 | // }; |
| 197 | // } |
| 198 | |
| 199 | |
| 200 | public void testMultipleBindingAnnotations() { |
| 201 | try { |
| 202 | Guice.createInjector(new AbstractModule() { |
Christian Edward Gruber | 2e39ef7 | 2013-10-05 14:04:53 -0700 | [diff] [blame] | 203 | @Override protected void configure() {} |
limpbizkit | b3a8f0b | 2008-09-05 22:30:40 +0000 | [diff] [blame] | 204 | |
| 205 | @Provides @Named("A") @Blue |
| 206 | public String provideString() { |
| 207 | return "a"; |
| 208 | } |
| 209 | }); |
| 210 | fail(); |
| 211 | } catch (CreationException expected) { |
limpbizkit | 759662b | 2009-04-26 21:00:24 +0000 | [diff] [blame] | 212 | assertContains(expected.getMessage(), |
limpbizkit | b3a8f0b | 2008-09-05 22:30:40 +0000 | [diff] [blame] | 213 | "more than one annotation annotated with @BindingAnnotation:", "Named", "Blue", |
| 214 | "at " + getClass().getName(), ".provideString(ProviderMethodsTest.java:"); |
| 215 | } |
| 216 | |
| 217 | } |
| 218 | |
| 219 | @Retention(RUNTIME) |
| 220 | @BindingAnnotation @interface Blue {} |
| 221 | |
| 222 | public void testGenericProviderMethods() { |
| 223 | Injector injector = Guice.createInjector( |
| 224 | new ProvideTs<String>("A", "B") {}, new ProvideTs<Integer>(1, 2) {}); |
sameb | 9867f9c | 2015-02-02 12:45:25 -0800 | [diff] [blame^] | 225 | |
limpbizkit | b3a8f0b | 2008-09-05 22:30:40 +0000 | [diff] [blame] | 226 | assertEquals("A", injector.getInstance(Key.get(String.class, Names.named("First")))); |
| 227 | assertEquals("B", injector.getInstance(Key.get(String.class, Names.named("Second")))); |
| 228 | assertEquals(ImmutableSet.of("A", "B"), |
| 229 | injector.getInstance(Key.get(Types.setOf(String.class)))); |
| 230 | |
| 231 | assertEquals(1, injector.getInstance(Key.get(Integer.class, Names.named("First"))).intValue()); |
| 232 | assertEquals(2, injector.getInstance(Key.get(Integer.class, Names.named("Second"))).intValue()); |
| 233 | assertEquals(ImmutableSet.of(1, 2), |
| 234 | injector.getInstance(Key.get(Types.setOf(Integer.class)))); |
| 235 | } |
| 236 | |
| 237 | abstract class ProvideTs<T> extends AbstractModule { |
| 238 | final T first; |
| 239 | final T second; |
| 240 | |
| 241 | protected ProvideTs(T first, T second) { |
| 242 | this.first = first; |
| 243 | this.second = second; |
| 244 | } |
| 245 | |
Christian Edward Gruber | 2e39ef7 | 2013-10-05 14:04:53 -0700 | [diff] [blame] | 246 | @Override protected void configure() {} |
limpbizkit | b3a8f0b | 2008-09-05 22:30:40 +0000 | [diff] [blame] | 247 | |
| 248 | @Named("First") @Provides T provideFirst() { |
| 249 | return first; |
| 250 | } |
| 251 | |
| 252 | @Named("Second") @Provides T provideSecond() { |
| 253 | return second; |
| 254 | } |
| 255 | |
| 256 | @Provides Set<T> provideBoth(@Named("First") T first, @Named("Second") T second) { |
| 257 | return ImmutableSet.of(first, second); |
| 258 | } |
| 259 | } |
sameb | 9867f9c | 2015-02-02 12:45:25 -0800 | [diff] [blame^] | 260 | |
limpbizkit | b3a8f0b | 2008-09-05 22:30:40 +0000 | [diff] [blame] | 261 | public void testAutomaticProviderMethods() { |
| 262 | Injector injector = Guice.createInjector((Module) new AbstractModule() { |
Christian Edward Gruber | 2e39ef7 | 2013-10-05 14:04:53 -0700 | [diff] [blame] | 263 | @Override protected void configure() { } |
limpbizkit | b3a8f0b | 2008-09-05 22:30:40 +0000 | [diff] [blame] | 264 | private int next = 1; |
| 265 | |
| 266 | @Provides @Named("count") |
| 267 | public Integer provideCount() { |
| 268 | return next++; |
| 269 | } |
| 270 | }); |
| 271 | |
| 272 | assertEquals(1, injector.getInstance(Key.get(Integer.class, Names.named("count"))).intValue()); |
| 273 | assertEquals(2, injector.getInstance(Key.get(Integer.class, Names.named("count"))).intValue()); |
| 274 | assertEquals(3, injector.getInstance(Key.get(Integer.class, Names.named("count"))).intValue()); |
| 275 | } |
| 276 | |
| 277 | /** |
| 278 | * If the user installs provider methods for the module manually, that shouldn't cause a double |
| 279 | * binding of the provider methods' types. |
| 280 | */ |
| 281 | public void testAutomaticProviderMethodsDoNotCauseDoubleBinding() { |
| 282 | Module installsSelf = new AbstractModule() { |
Christian Edward Gruber | 2e39ef7 | 2013-10-05 14:04:53 -0700 | [diff] [blame] | 283 | @Override protected void configure() { |
limpbizkit | b3a8f0b | 2008-09-05 22:30:40 +0000 | [diff] [blame] | 284 | install(this); |
| 285 | bind(Integer.class).toInstance(5); |
| 286 | } |
| 287 | @Provides public String provideString(Integer count) { |
| 288 | return "A" + count; |
| 289 | } |
| 290 | }; |
| 291 | |
| 292 | Injector injector = Guice.createInjector(installsSelf); |
| 293 | assertEquals("A5", injector.getInstance(String.class)); |
| 294 | } |
sameb | 9867f9c | 2015-02-02 12:45:25 -0800 | [diff] [blame^] | 295 | |
limpbizkit | b3a8f0b | 2008-09-05 22:30:40 +0000 | [diff] [blame] | 296 | public void testWildcardProviderMethods() { |
| 297 | final List<String> strings = ImmutableList.of("A", "B", "C"); |
| 298 | final List<Number> numbers = ImmutableList.<Number>of(1, 2, 3); |
| 299 | |
| 300 | Injector injector = Guice.createInjector(new AbstractModule() { |
Christian Edward Gruber | 2e39ef7 | 2013-10-05 14:04:53 -0700 | [diff] [blame] | 301 | @Override protected void configure() { |
limpbizkit | b3a8f0b | 2008-09-05 22:30:40 +0000 | [diff] [blame] | 302 | @SuppressWarnings("unchecked") |
| 303 | Key<List<? super Integer>> listOfSupertypesOfInteger = (Key<List<? super Integer>>) |
| 304 | Key.get(Types.listOf(Types.supertypeOf(Integer.class))); |
| 305 | bind(listOfSupertypesOfInteger).toInstance(numbers); |
| 306 | } |
| 307 | @Provides public List<? extends CharSequence> provideCharSequences() { |
| 308 | return strings; |
| 309 | } |
| 310 | @Provides public Class<?> provideType() { |
| 311 | return Float.class; |
| 312 | } |
| 313 | }); |
| 314 | |
| 315 | assertSame(strings, injector.getInstance(HasWildcardInjection.class).charSequences); |
| 316 | assertSame(numbers, injector.getInstance(HasWildcardInjection.class).numbers); |
| 317 | assertSame(Float.class, injector.getInstance(HasWildcardInjection.class).type); |
| 318 | } |
| 319 | |
| 320 | static class HasWildcardInjection { |
| 321 | @Inject List<? extends CharSequence> charSequences; |
| 322 | @Inject List<? super Integer> numbers; |
| 323 | @Inject Class<?> type; |
| 324 | } |
limpbizkit | 76c24b1 | 2008-12-25 04:32:41 +0000 | [diff] [blame] | 325 | |
sameb | 9867f9c | 2015-02-02 12:45:25 -0800 | [diff] [blame^] | 326 | public void testProviderMethodDependenciesAreExposed() throws Exception { |
| 327 | Module module = new AbstractModule() { |
Christian Edward Gruber | 2e39ef7 | 2013-10-05 14:04:53 -0700 | [diff] [blame] | 328 | @Override protected void configure() { |
limpbizkit | 76c24b1 | 2008-12-25 04:32:41 +0000 | [diff] [blame] | 329 | bind(Integer.class).toInstance(50); |
| 330 | bindConstant().annotatedWith(Names.named("units")).to("Kg"); |
| 331 | } |
| 332 | @Provides @Named("weight") String provideWeight(Integer count, @Named("units") String units) { |
| 333 | return count + units; |
| 334 | } |
sameb | 9867f9c | 2015-02-02 12:45:25 -0800 | [diff] [blame^] | 335 | }; |
| 336 | Injector injector = Guice.createInjector(module); |
limpbizkit | 76c24b1 | 2008-12-25 04:32:41 +0000 | [diff] [blame] | 337 | |
| 338 | ProviderInstanceBinding<?> binding = (ProviderInstanceBinding<?>) injector.getBinding( |
| 339 | Key.get(String.class, Names.named("weight"))); |
sameb | 9867f9c | 2015-02-02 12:45:25 -0800 | [diff] [blame^] | 340 | Method method = |
| 341 | module.getClass().getDeclaredMethod("provideWeight", Integer.class, String.class); |
| 342 | InjectionPoint point = new InjectionPoint(TypeLiteral.get(module.getClass()), method, false); |
| 343 | assertEquals(ImmutableSet.<Dependency<?>>of( |
| 344 | new Dependency<Integer>(point, Key.get(Integer.class), false, 0), |
| 345 | new Dependency<String>(point, Key.get(String.class, Names.named("units")), false, 1)), |
| 346 | binding.getDependencies()); |
limpbizkit | 76c24b1 | 2008-12-25 04:32:41 +0000 | [diff] [blame] | 347 | } |
limpbizkit | 714df3c | 2009-01-08 02:40:04 +0000 | [diff] [blame] | 348 | |
| 349 | public void testNonModuleProviderMethods() { |
| 350 | final Object methodsObject = new Object() { |
| 351 | @Provides @Named("foo") String provideFoo() { |
| 352 | return "foo-value"; |
| 353 | } |
| 354 | }; |
| 355 | |
| 356 | Module module = new AbstractModule() { |
| 357 | @Override protected void configure() { |
| 358 | install(ProviderMethodsModule.forObject(methodsObject)); |
| 359 | } |
| 360 | }; |
| 361 | |
| 362 | Injector injector = Guice.createInjector(module); |
| 363 | |
| 364 | Key<String> key = Key.get(String.class, Names.named("foo")); |
| 365 | assertEquals("foo-value", injector.getInstance(key)); |
| 366 | |
| 367 | // Test the provider method object itself. This makes sure getInstance works, since GIN uses it |
| 368 | List<Element> elements = Elements.getElements(module); |
| 369 | assertEquals(1, elements.size()); |
| 370 | |
| 371 | Element element = elements.get(0); |
| 372 | assertTrue(element + " instanceof ProviderInstanceBinding", |
| 373 | element instanceof ProviderInstanceBinding); |
| 374 | |
| 375 | ProviderInstanceBinding binding = (ProviderInstanceBinding) element; |
sameb | 9867f9c | 2015-02-02 12:45:25 -0800 | [diff] [blame^] | 376 | javax.inject.Provider provider = binding.getUserSuppliedProvider(); |
Sam Berlin | c00df28 | 2014-07-01 16:53:41 -0400 | [diff] [blame] | 377 | assertTrue(provider instanceof ProviderMethod); |
sameb | 9867f9c | 2015-02-02 12:45:25 -0800 | [diff] [blame^] | 378 | assertEquals(methodsObject, ((ProviderMethod) provider).getInstance()); |
Sam Berlin | 8ad60eb | 2014-03-10 12:49:46 -0400 | [diff] [blame] | 379 | assertSame(provider, binding.getProviderInstance()); |
limpbizkit | 714df3c | 2009-01-08 02:40:04 +0000 | [diff] [blame] | 380 | } |
limpbizkit | 759662b | 2009-04-26 21:00:24 +0000 | [diff] [blame] | 381 | |
| 382 | public void testVoidProviderMethods() { |
| 383 | try { |
| 384 | Guice.createInjector(new AbstractModule() { |
Christian Edward Gruber | 2e39ef7 | 2013-10-05 14:04:53 -0700 | [diff] [blame] | 385 | @Override protected void configure() {} |
limpbizkit | 759662b | 2009-04-26 21:00:24 +0000 | [diff] [blame] | 386 | |
| 387 | @Provides void provideFoo() {} |
| 388 | }); |
| 389 | fail(); |
| 390 | } catch (CreationException expected) { |
sameb | 9867f9c | 2015-02-02 12:45:25 -0800 | [diff] [blame^] | 391 | assertContains(expected.getMessage(), |
limpbizkit | 759662b | 2009-04-26 21:00:24 +0000 | [diff] [blame] | 392 | "1) Provider methods must return a value. Do not return void.", |
| 393 | getClass().getName(), ".provideFoo(ProviderMethodsTest.java:"); |
| 394 | } |
| 395 | } |
sameb | 9867f9c | 2015-02-02 12:45:25 -0800 | [diff] [blame^] | 396 | |
sberlin | 82cbc6d | 2010-05-19 03:12:07 +0000 | [diff] [blame] | 397 | public void testInjectsJustOneLogger() { |
| 398 | AtomicReference<Logger> loggerRef = new AtomicReference<Logger>(); |
| 399 | Injector injector = Guice.createInjector(new FooModule(loggerRef)); |
sameb | 9867f9c | 2015-02-02 12:45:25 -0800 | [diff] [blame^] | 400 | |
sberlin | 82cbc6d | 2010-05-19 03:12:07 +0000 | [diff] [blame] | 401 | assertNull(loggerRef.get()); |
| 402 | injector.getInstance(Integer.class); |
| 403 | Logger lastLogger = loggerRef.getAndSet(null); |
| 404 | assertNotNull(lastLogger); |
| 405 | injector.getInstance(Integer.class); |
| 406 | assertSame(lastLogger, loggerRef.get()); |
sameb | 9867f9c | 2015-02-02 12:45:25 -0800 | [diff] [blame^] | 407 | |
| 408 | assertEquals(FooModule.class.getName(), lastLogger.getName()); |
sberlin | 82cbc6d | 2010-05-19 03:12:07 +0000 | [diff] [blame] | 409 | } |
sameb | 9867f9c | 2015-02-02 12:45:25 -0800 | [diff] [blame^] | 410 | |
sberlin | 82cbc6d | 2010-05-19 03:12:07 +0000 | [diff] [blame] | 411 | private static class FooModule extends AbstractModule { |
| 412 | private final AtomicReference<Logger> loggerRef; |
sameb | 9867f9c | 2015-02-02 12:45:25 -0800 | [diff] [blame^] | 413 | |
sberlin | 82cbc6d | 2010-05-19 03:12:07 +0000 | [diff] [blame] | 414 | public FooModule(AtomicReference<Logger> loggerRef) { |
| 415 | this.loggerRef = loggerRef; |
| 416 | } |
| 417 | |
| 418 | @Override protected void configure() {} |
| 419 | |
| 420 | @SuppressWarnings("unused") |
| 421 | @Provides Integer foo(Logger logger) { |
| 422 | loggerRef.set(logger); |
| 423 | return 42; |
| 424 | } |
| 425 | } |
sameb | 9867f9c | 2015-02-02 12:45:25 -0800 | [diff] [blame^] | 426 | |
Sam Berlin | 29ce12b | 2014-03-10 12:52:01 -0400 | [diff] [blame] | 427 | public void testSpi() throws Exception { |
| 428 | Module m1 = new AbstractModule() { |
| 429 | @Override protected void configure() {} |
| 430 | @Provides @Named("foo") String provideFoo(Integer dep) { return "foo"; } |
| 431 | }; |
| 432 | Module m2 = new AbstractModule() { |
| 433 | @Override protected void configure() {} |
| 434 | @Provides Integer provideInt(@Named("foo") String dep) { return 42; } |
| 435 | }; |
| 436 | Injector injector = Guice.createInjector(m1, m2); |
sameb | 9867f9c | 2015-02-02 12:45:25 -0800 | [diff] [blame^] | 437 | |
Sam Berlin | 29ce12b | 2014-03-10 12:52:01 -0400 | [diff] [blame] | 438 | Binding<String> stringBinding = |
| 439 | injector.getBinding(Key.get(String.class, Names.named("foo"))); |
| 440 | ProvidesMethodBinding<String> stringMethod = |
| 441 | stringBinding.acceptTargetVisitor(new BindingCapturer<String>()); |
| 442 | assertEquals(m1, stringMethod.getEnclosingInstance()); |
| 443 | assertEquals(m1.getClass().getDeclaredMethod("provideFoo", Integer.class), |
| 444 | stringMethod.getMethod()); |
| 445 | assertEquals(((HasDependencies) stringBinding).getDependencies(), |
| 446 | stringMethod.getDependencies()); |
| 447 | assertEquals(Key.get(String.class, Names.named("foo")), stringMethod.getKey()); |
sameb | 9867f9c | 2015-02-02 12:45:25 -0800 | [diff] [blame^] | 448 | |
Sam Berlin | 29ce12b | 2014-03-10 12:52:01 -0400 | [diff] [blame] | 449 | Binding<Integer> intBinding = injector.getBinding(Integer.class); |
| 450 | ProvidesMethodBinding<Integer> intMethod = |
| 451 | intBinding.acceptTargetVisitor(new BindingCapturer<Integer>()); |
| 452 | assertEquals(m2, intMethod.getEnclosingInstance()); |
| 453 | assertEquals(m2.getClass().getDeclaredMethod("provideInt", String.class), |
| 454 | intMethod.getMethod()); |
| 455 | assertEquals(((HasDependencies) intBinding).getDependencies(), |
| 456 | intMethod.getDependencies()); |
| 457 | assertEquals(Key.get(Integer.class), intMethod.getKey()); |
sameb | 9867f9c | 2015-02-02 12:45:25 -0800 | [diff] [blame^] | 458 | |
Sam Berlin | 29ce12b | 2014-03-10 12:52:01 -0400 | [diff] [blame] | 459 | } |
sameb | 9867f9c | 2015-02-02 12:45:25 -0800 | [diff] [blame^] | 460 | |
Sam Berlin | 29ce12b | 2014-03-10 12:52:01 -0400 | [diff] [blame] | 461 | private static class BindingCapturer<T> extends DefaultBindingTargetVisitor<T, ProvidesMethodBinding<T>> |
| 462 | implements ProvidesMethodTargetVisitor<T, ProvidesMethodBinding<T>> { |
sameb | 9867f9c | 2015-02-02 12:45:25 -0800 | [diff] [blame^] | 463 | |
Sam Berlin | 29ce12b | 2014-03-10 12:52:01 -0400 | [diff] [blame] | 464 | @SuppressWarnings("unchecked") |
| 465 | public ProvidesMethodBinding<T> visit( |
| 466 | ProvidesMethodBinding<? extends T> providesMethodBinding) { |
| 467 | return (ProvidesMethodBinding<T>)providesMethodBinding; |
| 468 | } |
sameb | 9867f9c | 2015-02-02 12:45:25 -0800 | [diff] [blame^] | 469 | |
Sam Berlin | 29ce12b | 2014-03-10 12:52:01 -0400 | [diff] [blame] | 470 | @Override protected ProvidesMethodBinding<T> visitOther(Binding<? extends T> binding) { |
| 471 | throw new IllegalStateException("unexpected visit of: " + binding); |
sameb | 9867f9c | 2015-02-02 12:45:25 -0800 | [diff] [blame^] | 472 | } |
Sam Berlin | 29ce12b | 2014-03-10 12:52:01 -0400 | [diff] [blame] | 473 | } |
Sam Berlin | 409e0f5 | 2014-05-10 10:34:16 -0400 | [diff] [blame] | 474 | |
| 475 | public void testProvidesMethodVisibility() { |
| 476 | Injector injector = Guice.createInjector(new VisibilityModule()); |
| 477 | |
| 478 | assertEquals(42, injector.getInstance(Integer.class).intValue()); |
| 479 | assertEquals(42L, injector.getInstance(Long.class).longValue()); |
| 480 | assertEquals(42D, injector.getInstance(Double.class).doubleValue()); |
| 481 | assertEquals(42F, injector.getInstance(Float.class).floatValue()); |
| 482 | } |
| 483 | |
| 484 | private static class VisibilityModule extends AbstractModule { |
| 485 | @Override protected void configure() {} |
| 486 | |
| 487 | @SuppressWarnings("unused") |
| 488 | @Provides Integer foo() { |
| 489 | return 42; |
| 490 | } |
| 491 | |
| 492 | @SuppressWarnings("unused") |
| 493 | @Provides private Long bar() { |
| 494 | return 42L; |
| 495 | } |
| 496 | |
| 497 | @SuppressWarnings("unused") |
| 498 | @Provides protected Double baz() { |
| 499 | return 42D; |
| 500 | } |
| 501 | |
| 502 | @SuppressWarnings("unused") |
| 503 | @Provides public Float quux() { |
| 504 | return 42F; |
| 505 | } |
| 506 | } |
| 507 | |
| 508 | public void testProvidesMethodInheritenceHierarchy() { |
| 509 | try { |
| 510 | Guice.createInjector(new Sub1Module(), new Sub2Module()); |
| 511 | fail("Expected injector creation failure"); |
| 512 | } catch (CreationException expected) { |
| 513 | // both of our super class bindings cause errors |
sameb | 9867f9c | 2015-02-02 12:45:25 -0800 | [diff] [blame^] | 514 | assertContains(expected.getMessage(), |
Sam Berlin | 409e0f5 | 2014-05-10 10:34:16 -0400 | [diff] [blame] | 515 | "A binding to java.lang.Long was already configured", |
| 516 | "A binding to java.lang.Integer was already configured"); |
| 517 | } |
| 518 | } |
| 519 | |
| 520 | public void testProvidesMethodsDefinedInSuperClass() { |
| 521 | Injector injector = Guice.createInjector(new Sub1Module()); |
| 522 | assertEquals(42, injector.getInstance(Integer.class).intValue()); |
| 523 | assertEquals(42L, injector.getInstance(Long.class).longValue()); |
| 524 | assertEquals(42D, injector.getInstance(Double.class).doubleValue()); |
| 525 | } |
| 526 | |
| 527 | private static class BaseModule extends AbstractModule { |
| 528 | @Override protected void configure() {} |
| 529 | |
| 530 | @Provides Integer foo() { |
| 531 | return 42; |
| 532 | } |
| 533 | |
| 534 | @Provides Long bar() { |
| 535 | return 42L; |
| 536 | } |
| 537 | } |
| 538 | |
| 539 | private static class Sub1Module extends BaseModule { |
| 540 | @Provides Double baz() { |
| 541 | return 42D; |
| 542 | } |
| 543 | } |
| 544 | |
| 545 | private static class Sub2Module extends BaseModule { |
| 546 | @Provides Float quux() { |
| 547 | return 42F; |
| 548 | } |
| 549 | } |
| 550 | |
| 551 | /*if[AOP]*/ |
| 552 | public void testShareFastClass() { |
| 553 | CallerInspecterModule module = new CallerInspecterModule(); |
| 554 | Guice.createInjector(Stage.PRODUCTION, module); |
| 555 | assertEquals(module.fooCallerClass, module.barCallerClass); |
| 556 | assertTrue(module.fooCallerClass.contains("$$FastClassByGuice$$")); |
| 557 | } |
| 558 | |
| 559 | private static class CallerInspecterModule extends AbstractModule { |
| 560 | // start them off as unequal |
| 561 | String barCallerClass = "not_set_bar"; |
| 562 | String fooCallerClass = "not_set_foo"; |
| 563 | |
| 564 | @Override protected void configure() {} |
| 565 | |
| 566 | @Provides @Singleton Integer foo() { |
| 567 | this.fooCallerClass = new Exception().getStackTrace()[1].getClassName(); |
| 568 | return 42; |
| 569 | } |
| 570 | |
| 571 | @Provides @Singleton Long bar() { |
| 572 | this.barCallerClass = new Exception().getStackTrace()[1].getClassName(); |
| 573 | return 42L; |
| 574 | } |
| 575 | } |
| 576 | |
| 577 | public void testShareFastClassWithSuperClass() { |
| 578 | CallerInspecterSubClassModule module = new CallerInspecterSubClassModule(); |
| 579 | Guice.createInjector(Stage.PRODUCTION, module); |
sameb | 9867f9c | 2015-02-02 12:45:25 -0800 | [diff] [blame^] | 580 | assertEquals("Expected provider methods in the same class to share fastclass classes", |
Sam Berlin | 409e0f5 | 2014-05-10 10:34:16 -0400 | [diff] [blame] | 581 | module.fooCallerClass, module.barCallerClass); |
| 582 | assertFalse( |
| 583 | "Did not expect provider methods in the subclasses to share fastclass classes " |
| 584 | + "with their parent classes", |
| 585 | module.bazCallerClass.equals(module.barCallerClass)); |
| 586 | } |
sameb | 9867f9c | 2015-02-02 12:45:25 -0800 | [diff] [blame^] | 587 | |
Sam Berlin | 409e0f5 | 2014-05-10 10:34:16 -0400 | [diff] [blame] | 588 | |
| 589 | private static class CallerInspecterSubClassModule extends CallerInspecterModule { |
| 590 | String bazCallerClass; |
| 591 | |
| 592 | @Override protected void configure() {} |
| 593 | |
| 594 | @Provides @Singleton Double baz() { |
| 595 | this.bazCallerClass = new Exception().getStackTrace()[1].getClassName(); |
| 596 | return 42D; |
| 597 | } |
| 598 | } |
| 599 | /*end[AOP]*/ |
Sam Berlin | 409e0f5 | 2014-05-10 10:34:16 -0400 | [diff] [blame] | 600 | |
Sam Berlin | 76be88e | 2014-07-01 16:56:13 -0400 | [diff] [blame] | 601 | static class SuperClassModule extends AbstractModule { |
Sam Berlin | 409e0f5 | 2014-05-10 10:34:16 -0400 | [diff] [blame] | 602 | @Override protected void configure() {} |
Sam Berlin | 76be88e | 2014-07-01 16:56:13 -0400 | [diff] [blame] | 603 | @Provides Number providerMethod() { |
Sam Berlin | 409e0f5 | 2014-05-10 10:34:16 -0400 | [diff] [blame] | 604 | return 1D; |
| 605 | } |
Sam Berlin | 76be88e | 2014-07-01 16:56:13 -0400 | [diff] [blame] | 606 | @Provides @Named("rawlist") List rawProvider(@Named("list") List<String> f) { |
| 607 | return f; |
Sam Berlin | 409e0f5 | 2014-05-10 10:34:16 -0400 | [diff] [blame] | 608 | } |
| 609 | |
Sam Berlin | 76be88e | 2014-07-01 16:56:13 -0400 | [diff] [blame] | 610 | @Provides @Named("unrawlist") List<String> rawParameterProvider(@Named("rawlist") List f) { |
| 611 | return f; |
Sam Berlin | 409e0f5 | 2014-05-10 10:34:16 -0400 | [diff] [blame] | 612 | } |
sameb | 9867f9c | 2015-02-02 12:45:25 -0800 | [diff] [blame^] | 613 | |
Sam Berlin | 76be88e | 2014-07-01 16:56:13 -0400 | [diff] [blame] | 614 | @Provides @Named("list") List<String> annotatedGenericProviderMethod() { |
| 615 | return new ArrayList<String>(); |
Sam Berlin | 409e0f5 | 2014-05-10 10:34:16 -0400 | [diff] [blame] | 616 | } |
Sam Berlin | 76be88e | 2014-07-01 16:56:13 -0400 | [diff] [blame] | 617 | @Provides @Named("collection") Collection<String> annotatedGenericParameterProviderMethod( |
| 618 | @Named("list") List<String> foo) { |
| 619 | return foo; |
| 620 | } |
| 621 | @Provides private String privateProviderMethod() { |
| 622 | return "hello"; |
| 623 | } |
| 624 | } |
| 625 | |
| 626 | public void testOverrideProviderMethod_overrideHasProvides() { |
| 627 | class SubClassModule extends SuperClassModule { |
| 628 | @Override @Provides Number providerMethod() { |
| 629 | return 2D; |
| 630 | } |
| 631 | } |
| 632 | try { |
| 633 | Guice.createInjector(new SubClassModule()); |
| 634 | fail(); |
| 635 | } catch (CreationException e) { |
sameb | 9867f9c | 2015-02-02 12:45:25 -0800 | [diff] [blame^] | 636 | assertContains(e.getMessage(), |
Sam Berlin | 76be88e | 2014-07-01 16:56:13 -0400 | [diff] [blame] | 637 | "Overriding @Provides methods is not allowed.", |
| 638 | "@Provides method: " + SuperClassModule.class.getName() + ".providerMethod()", |
| 639 | "overridden by: " + SubClassModule.class.getName() + ".providerMethod()"); |
| 640 | } |
| 641 | } |
| 642 | |
| 643 | public void testOverrideProviderMethod_overrideHasProvides_withNewAnnotation() { |
| 644 | class SubClassModule extends SuperClassModule { |
| 645 | @Override @Provides @Named("foo") Number providerMethod() { |
| 646 | return 2D; |
| 647 | } |
| 648 | } |
| 649 | try { |
| 650 | Guice.createInjector(new SubClassModule()); |
| 651 | fail(); |
| 652 | } catch (CreationException e) { |
sameb | 9867f9c | 2015-02-02 12:45:25 -0800 | [diff] [blame^] | 653 | assertContains(e.getMessage(), |
Sam Berlin | 76be88e | 2014-07-01 16:56:13 -0400 | [diff] [blame] | 654 | "Overriding @Provides methods is not allowed.", |
| 655 | "@Provides method: " + SuperClassModule.class.getName() + ".providerMethod()", |
| 656 | "overridden by: " + SubClassModule.class.getName() + ".providerMethod()"); |
| 657 | } |
| 658 | } |
| 659 | |
| 660 | public void testOverrideProviderMethod_overrideDoesntHaveProvides() { |
| 661 | class SubClassModule extends SuperClassModule { |
| 662 | @Override Number providerMethod() { |
| 663 | return 2D; |
| 664 | } |
| 665 | } |
| 666 | try { |
| 667 | Guice.createInjector(new SubClassModule()); |
| 668 | fail(); |
| 669 | } catch (CreationException e) { |
sameb | 9867f9c | 2015-02-02 12:45:25 -0800 | [diff] [blame^] | 670 | assertContains(e.getMessage(), |
Sam Berlin | 76be88e | 2014-07-01 16:56:13 -0400 | [diff] [blame] | 671 | "Overriding @Provides methods is not allowed.", |
| 672 | "@Provides method: " + SuperClassModule.class.getName() + ".providerMethod()", |
| 673 | "overridden by: " + SubClassModule.class.getName() + ".providerMethod()"); |
| 674 | } |
| 675 | } |
| 676 | public void testOverrideProviderMethod_overrideDoesntHaveProvides_withNewAnnotation() { |
| 677 | class SubClassModule extends SuperClassModule { |
| 678 | @Override @Named("foo") Number providerMethod() { |
| 679 | return 2D; |
| 680 | } |
| 681 | } |
| 682 | try { |
| 683 | Guice.createInjector(new SubClassModule()); |
| 684 | fail(); |
| 685 | } catch (CreationException e) { |
sameb | 9867f9c | 2015-02-02 12:45:25 -0800 | [diff] [blame^] | 686 | assertContains(e.getMessage(), |
Sam Berlin | 76be88e | 2014-07-01 16:56:13 -0400 | [diff] [blame] | 687 | "Overriding @Provides methods is not allowed.", |
| 688 | "@Provides method: " + SuperClassModule.class.getName() + ".providerMethod()", |
| 689 | "overridden by: " + SubClassModule.class.getName() + ".providerMethod()"); |
| 690 | } |
| 691 | } |
sameb | 9867f9c | 2015-02-02 12:45:25 -0800 | [diff] [blame^] | 692 | |
Sam Berlin | 76be88e | 2014-07-01 16:56:13 -0400 | [diff] [blame] | 693 | |
| 694 | public void testOverrideProviderMethod_covariantOverrideDoesntHaveProvides() { |
| 695 | class SubClassModule extends SuperClassModule { |
| 696 | @Override Double providerMethod() { |
| 697 | return 2D; |
| 698 | } |
| 699 | } |
| 700 | try { |
| 701 | Guice.createInjector(new SubClassModule()); |
| 702 | fail(); |
| 703 | } catch (CreationException e) { |
sameb | 9867f9c | 2015-02-02 12:45:25 -0800 | [diff] [blame^] | 704 | assertContains(e.getMessage(), |
Sam Berlin | 76be88e | 2014-07-01 16:56:13 -0400 | [diff] [blame] | 705 | "Overriding @Provides methods is not allowed.", |
| 706 | "@Provides method: " + SuperClassModule.class.getName() + ".providerMethod()", |
| 707 | "overridden by: " + SubClassModule.class.getName() + ".providerMethod()"); |
| 708 | } |
| 709 | } |
| 710 | |
| 711 | public void testOverrideProviderMethod_covariantOverrideHasProvides() { |
| 712 | class SubClassModule extends SuperClassModule { |
| 713 | @Override @Provides Double providerMethod() { |
| 714 | return 2D; |
| 715 | } |
| 716 | } |
| 717 | try { |
| 718 | Guice.createInjector(new SubClassModule()); |
| 719 | fail(); |
| 720 | } catch (CreationException e) { |
sameb | 9867f9c | 2015-02-02 12:45:25 -0800 | [diff] [blame^] | 721 | assertContains(e.getMessage(), |
Sam Berlin | 76be88e | 2014-07-01 16:56:13 -0400 | [diff] [blame] | 722 | "Overriding @Provides methods is not allowed.", |
| 723 | "@Provides method: " + SuperClassModule.class.getName() + ".providerMethod()", |
| 724 | "overridden by: " + SubClassModule.class.getName() + ".providerMethod()"); |
| 725 | } |
| 726 | } |
| 727 | |
| 728 | public void testOverrideProviderMethod_fakeOverridePrivateMethod() { |
| 729 | class SubClassModule extends SuperClassModule { |
| 730 | // not actually an override, just looks like it |
| 731 | String privateProviderMethod() { |
| 732 | return "sub"; |
| 733 | } |
| 734 | } |
| 735 | assertEquals("hello", Guice.createInjector(new SubClassModule()).getInstance(String.class)); |
| 736 | } |
| 737 | |
| 738 | public void testOverrideProviderMethod_subclassRawTypes_returnType() { |
| 739 | class SubClassModule extends SuperClassModule { |
| 740 | @Override List annotatedGenericProviderMethod() { |
| 741 | return super.annotatedGenericProviderMethod(); |
| 742 | } |
| 743 | } |
| 744 | try { |
| 745 | Guice.createInjector(new SubClassModule()); |
| 746 | fail(); |
| 747 | } catch (CreationException e) { |
| 748 | assertContains(e.getMessage(), |
| 749 | "Overriding @Provides methods is not allowed.", |
| 750 | "@Provides method: " + SuperClassModule.class.getName() |
| 751 | + ".annotatedGenericProviderMethod()", |
| 752 | "overridden by: " + SubClassModule.class.getName() + ".annotatedGenericProviderMethod()"); |
| 753 | } |
| 754 | } |
| 755 | |
| 756 | public void testOverrideProviderMethod_subclassRawTypes_parameterType() { |
| 757 | class SubClassModule extends SuperClassModule { |
| 758 | @Override Collection<String> annotatedGenericParameterProviderMethod(List foo) { |
| 759 | return super.annotatedGenericParameterProviderMethod(foo); |
| 760 | } |
| 761 | } |
| 762 | try { |
| 763 | Guice.createInjector(new SubClassModule()); |
| 764 | fail(); |
| 765 | } catch (CreationException e) { |
sameb | 9867f9c | 2015-02-02 12:45:25 -0800 | [diff] [blame^] | 766 | assertContains(e.getMessage(), |
Sam Berlin | 76be88e | 2014-07-01 16:56:13 -0400 | [diff] [blame] | 767 | "Overriding @Provides methods is not allowed.", |
sameb | 9867f9c | 2015-02-02 12:45:25 -0800 | [diff] [blame^] | 768 | "@Provides method: " + SuperClassModule.class.getName() |
Sam Berlin | 76be88e | 2014-07-01 16:56:13 -0400 | [diff] [blame] | 769 | + ".annotatedGenericParameterProviderMethod()", |
sameb | 9867f9c | 2015-02-02 12:45:25 -0800 | [diff] [blame^] | 770 | "overridden by: " + SubClassModule.class.getName() |
Sam Berlin | 76be88e | 2014-07-01 16:56:13 -0400 | [diff] [blame] | 771 | + ".annotatedGenericParameterProviderMethod()"); |
| 772 | } |
| 773 | } |
| 774 | |
| 775 | public void testOverrideProviderMethod_superclassRawTypes_returnType() { |
| 776 | class SubClassModule extends SuperClassModule { |
| 777 | // remove the rawtype from the override |
| 778 | @Override List<String> rawProvider(List<String> f) { |
| 779 | return f; |
| 780 | } |
| 781 | } |
| 782 | try { |
| 783 | Guice.createInjector(new SubClassModule()); |
| 784 | fail(); |
| 785 | } catch (CreationException e) { |
sameb | 9867f9c | 2015-02-02 12:45:25 -0800 | [diff] [blame^] | 786 | assertContains(e.getMessage(), |
Sam Berlin | 76be88e | 2014-07-01 16:56:13 -0400 | [diff] [blame] | 787 | "Overriding @Provides methods is not allowed.", |
| 788 | "@Provides method: " + SuperClassModule.class.getName() + ".rawProvider()", |
| 789 | "overridden by: " + SubClassModule.class.getName() + ".rawProvider()"); |
| 790 | } |
| 791 | } |
| 792 | |
| 793 | abstract static class GenericSuperModule<T> extends AbstractModule { |
| 794 | @Provides String provide(T thing) { |
| 795 | return thing.toString(); |
| 796 | } |
| 797 | } |
| 798 | |
| 799 | // This is a tricky case where signatures don't match, but it is an override (facilitated via a |
| 800 | // bridge method) |
| 801 | public void testOverrideProviderMethod_erasureBasedOverrides() { |
| 802 | class SubClassModule extends GenericSuperModule<Integer> { |
| 803 | @Override String provide(Integer thing) { |
| 804 | return thing.toString(); |
| 805 | } |
| 806 | |
| 807 | @Override protected void configure() { |
| 808 | bind(Integer.class).toInstance(3); |
| 809 | } |
| 810 | } |
| 811 | try { |
| 812 | Guice.createInjector(new SubClassModule()); |
| 813 | fail(); |
| 814 | } catch (CreationException e) { |
| 815 | assertContains(e.getMessage(), |
| 816 | "Overriding @Provides methods is not allowed.", |
| 817 | "@Provides method: " + GenericSuperModule.class.getName() + ".provide()", |
| 818 | "overridden by: " + SubClassModule.class.getName() + ".provide()"); |
| 819 | } |
| 820 | } |
| 821 | |
| 822 | class RestrictedSuper extends AbstractModule { |
| 823 | @Provides public String provideFoo() { return "foo"; } |
| 824 | @Override protected void configure() {} |
| 825 | } |
| 826 | |
| 827 | public class ExposedSub extends RestrictedSuper {} |
| 828 | |
| 829 | public void testOverrideProviderMethod_increasedVisibility() { |
| 830 | // ensure we don't detect the synthetic provideFoo method in ExposedSub as an override (it is, |
| 831 | // but since it is synthetic it would be annoying to throw an error on it). |
| 832 | assertEquals("foo", Guice.createInjector(new ExposedSub()).getInstance(String.class)); |
Sam Berlin | 409e0f5 | 2014-05-10 10:34:16 -0400 | [diff] [blame] | 833 | } |
Sam Berlin | 53a5936 | 2014-05-23 18:19:12 -0400 | [diff] [blame] | 834 | |
| 835 | interface ProviderInterface<T> { |
| 836 | T getT(); |
| 837 | } |
| 838 | |
| 839 | static class ModuleImpl extends AbstractModule implements ProviderInterface<String> { |
| 840 | @Override protected void configure() {} |
| 841 | @Provides public String getT() { |
| 842 | return "string"; |
| 843 | } |
| 844 | @Provides public Object getObject() { |
| 845 | return new Object(); |
| 846 | } |
| 847 | /* javac will synthesize a bridge method for getT with the types erased, equivalent to: |
| 848 | * @Provides public Object getT() { ... } |
| 849 | */ |
| 850 | } |
| 851 | |
| 852 | public void testIgnoreSyntheticBridgeMethods() { |
| 853 | Guice.createInjector(new ModuleImpl()); |
| 854 | } |
sameb | 9867f9c | 2015-02-02 12:45:25 -0800 | [diff] [blame^] | 855 | |
| 856 | public void testNullability() throws Exception { |
| 857 | Module module = new AbstractModule() { |
| 858 | @Override |
| 859 | protected void configure() { |
| 860 | bind(String.class).toProvider(Providers.<String>of(null)); |
| 861 | } |
| 862 | |
| 863 | @SuppressWarnings("unused") |
| 864 | @Provides |
| 865 | Integer fail(String foo) { |
| 866 | return 1; |
| 867 | } |
| 868 | |
| 869 | @SuppressWarnings("unused") |
| 870 | @Provides |
| 871 | Long succeed(@Nullable String foo) { |
| 872 | return 2L; |
| 873 | } |
| 874 | }; |
| 875 | Injector injector = Guice.createInjector(module); |
| 876 | InjectionPoint fooPoint = InjectionPoint.forMethod( |
| 877 | module.getClass().getDeclaredMethod("fail", String.class), |
| 878 | TypeLiteral.get(module.getClass())); |
| 879 | Dependency<?> fooDependency = Iterables.getOnlyElement(fooPoint.getDependencies()); |
| 880 | |
| 881 | runNullableTest(injector, fooDependency, module); |
| 882 | |
| 883 | injector.getInstance(Long.class); |
| 884 | } |
| 885 | |
| 886 | private void runNullableTest(Injector injector, Dependency<?> dependency, Module module) { |
| 887 | switch (InternalFlags.getNullableProvidesOption()) { |
| 888 | case ERROR: |
| 889 | validateNullableFails(injector, module); |
| 890 | break; |
| 891 | case IGNORE: |
| 892 | validateNullableIgnored(injector); |
| 893 | break; |
| 894 | case WARN: |
| 895 | validateNullableWarns(injector, dependency); |
| 896 | break; |
| 897 | } |
| 898 | } |
| 899 | |
| 900 | private void validateNullableFails(Injector injector, Module module) { |
| 901 | try { |
| 902 | injector.getInstance(Integer.class); |
| 903 | fail(); |
| 904 | } catch (ProvisionException expected) { |
| 905 | assertContains(expected.getMessage(), |
| 906 | "1) null returned by binding at " + module.getClass().getName() + ".configure(", |
| 907 | "but parameter 0 of " + module.getClass().getName() + ".fail() is not @Nullable", |
| 908 | "while locating java.lang.String", |
| 909 | "for parameter 0 at " + module.getClass().getName() + ".fail(", |
| 910 | "while locating java.lang.Integer"); |
| 911 | |
| 912 | assertEquals(1, expected.getErrorMessages().size()); |
| 913 | } |
| 914 | } |
| 915 | |
| 916 | private void validateNullableIgnored(Injector injector) { |
| 917 | injector.getInstance(Integer.class); // no exception |
| 918 | } |
| 919 | |
| 920 | private void validateNullableWarns(Injector injector, Dependency<?> dependency) { |
| 921 | final List<LogRecord> logRecords = Lists.newArrayList(); |
| 922 | final Handler fakeHandler = new Handler() { |
| 923 | @Override |
| 924 | public void publish(LogRecord logRecord) { |
| 925 | logRecords.add(logRecord); |
| 926 | } |
| 927 | @Override |
| 928 | public void flush() {} |
| 929 | @Override |
| 930 | public void close() throws SecurityException {} |
| 931 | }; |
| 932 | Logger.getLogger(Guice.class.getName()).addHandler(fakeHandler); |
| 933 | try { |
| 934 | injector.getInstance(Integer.class); // no exception, but assert it does log. |
| 935 | LogRecord record = Iterables.getOnlyElement(logRecords); |
| 936 | assertEquals( |
| 937 | "Guice injected null into parameter {0} of {1} (a {2}), please mark it @Nullable." |
| 938 | + " Use -Dguice_check_nullable_provides_params=ERROR to turn this into an" |
| 939 | + " error.", |
| 940 | record.getMessage()); |
| 941 | assertEquals(dependency.getParameterIndex(), record.getParameters()[0]); |
| 942 | assertEquals(Errors.convert(dependency.getInjectionPoint().getMember()), |
| 943 | record.getParameters()[1]); |
| 944 | assertEquals(Errors.convert(dependency.getKey()), record.getParameters()[2]); |
| 945 | } finally { |
| 946 | Logger.getLogger(Guice.class.getName()).removeHandler(fakeHandler); |
| 947 | } |
| 948 | } |
| 949 | |
| 950 | @Retention(RetentionPolicy.RUNTIME) |
| 951 | @interface Nullable {} |
Sam Berlin | 8ad60eb | 2014-03-10 12:49:46 -0400 | [diff] [blame] | 952 | } |