limpbizkit | 5019270 | 2008-05-01 09:55:02 +0000 | [diff] [blame] | 1 | /** |
| 2 | * Copyright (C) 2008 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.multibindings; |
| 18 | |
limpbizkit | eab7647 | 2008-05-26 19:45:12 +0000 | [diff] [blame] | 19 | import static com.google.inject.Asserts.assertContains; |
sberlin | 75fcf6f | 2010-09-20 00:42:24 +0000 | [diff] [blame] | 20 | import static com.google.inject.multibindings.SpiUtils.assertSetVisitor; |
| 21 | import static com.google.inject.multibindings.SpiUtils.instance; |
| 22 | import static com.google.inject.multibindings.SpiUtils.providerInstance; |
| 23 | import static com.google.inject.multibindings.SpiUtils.VisitType.BOTH; |
| 24 | import static com.google.inject.multibindings.SpiUtils.VisitType.MODULE; |
| 25 | import static com.google.inject.name.Names.named; |
| 26 | import static java.lang.annotation.RetentionPolicy.RUNTIME; |
sberlin | 680c8b5 | 2010-06-11 19:11:13 +0000 | [diff] [blame] | 27 | |
sberlin | 75fcf6f | 2010-09-20 00:42:24 +0000 | [diff] [blame] | 28 | import java.lang.annotation.Annotation; |
| 29 | import java.lang.annotation.ElementType; |
| 30 | import java.lang.annotation.Retention; |
| 31 | import java.lang.annotation.RetentionPolicy; |
| 32 | import java.lang.annotation.Target; |
| 33 | import java.lang.reflect.Method; |
| 34 | import java.util.Arrays; |
| 35 | import java.util.Collections; |
| 36 | import java.util.HashSet; |
| 37 | import java.util.List; |
| 38 | import java.util.Set; |
| 39 | |
| 40 | import junit.framework.TestCase; |
| 41 | |
| 42 | import com.google.inject.AbstractModule; |
limpbizkit | ddb3862 | 2008-12-29 05:21:16 +0000 | [diff] [blame] | 43 | import com.google.inject.Binding; |
limpbizkit | 9532e62 | 2008-06-18 08:20:54 +0000 | [diff] [blame] | 44 | import com.google.inject.BindingAnnotation; |
| 45 | import com.google.inject.CreationException; |
| 46 | import com.google.inject.Guice; |
| 47 | import com.google.inject.Injector; |
| 48 | import com.google.inject.Key; |
| 49 | import com.google.inject.Module; |
| 50 | import com.google.inject.Provider; |
| 51 | import com.google.inject.ProvisionException; |
sberlin | 8bc8387 | 2010-04-26 02:12:22 +0000 | [diff] [blame] | 52 | import com.google.inject.Stage; |
limpbizkit | 9532e62 | 2008-06-18 08:20:54 +0000 | [diff] [blame] | 53 | import com.google.inject.TypeLiteral; |
sberlin | d9c913a | 2011-06-26 21:02:54 +0000 | [diff] [blame^] | 54 | import com.google.common.collect.ImmutableList; |
| 55 | import com.google.common.collect.ImmutableSet; |
| 56 | import com.google.common.collect.Sets; |
limpbizkit | 5019270 | 2008-05-01 09:55:02 +0000 | [diff] [blame] | 57 | import com.google.inject.name.Names; |
limpbizkit | ddb3862 | 2008-12-29 05:21:16 +0000 | [diff] [blame] | 58 | import com.google.inject.spi.Dependency; |
| 59 | import com.google.inject.spi.HasDependencies; |
sberlin | 8bc8387 | 2010-04-26 02:12:22 +0000 | [diff] [blame] | 60 | import com.google.inject.spi.InstanceBinding; |
| 61 | import com.google.inject.spi.LinkedKeyBinding; |
limpbizkit | d8750fd | 2009-08-10 23:30:46 +0000 | [diff] [blame] | 62 | import com.google.inject.util.Modules; |
limpbizkit | 5019270 | 2008-05-01 09:55:02 +0000 | [diff] [blame] | 63 | import com.google.inject.util.Providers; |
sberlin | 680c8b5 | 2010-06-11 19:11:13 +0000 | [diff] [blame] | 64 | |
limpbizkit | 5019270 | 2008-05-01 09:55:02 +0000 | [diff] [blame] | 65 | /** |
| 66 | * @author jessewilson@google.com (Jesse Wilson) |
| 67 | */ |
| 68 | public class MultibinderTest extends TestCase { |
| 69 | |
| 70 | final TypeLiteral<Set<String>> setOfString = new TypeLiteral<Set<String>>() {}; |
| 71 | final TypeLiteral<Set<Integer>> setOfInteger = new TypeLiteral<Set<Integer>>() {}; |
sberlin | 75fcf6f | 2010-09-20 00:42:24 +0000 | [diff] [blame] | 72 | final TypeLiteral<String> stringType = TypeLiteral.get(String.class); |
| 73 | final TypeLiteral<Integer> intType = TypeLiteral.get(Integer.class); |
limpbizkit | 5019270 | 2008-05-01 09:55:02 +0000 | [diff] [blame] | 74 | |
| 75 | public void testMultibinderAggregatesMultipleModules() { |
| 76 | Module abc = new AbstractModule() { |
| 77 | protected void configure() { |
| 78 | Multibinder<String> multibinder = Multibinder.newSetBinder(binder(), String.class); |
| 79 | multibinder.addBinding().toInstance("A"); |
| 80 | multibinder.addBinding().toInstance("B"); |
| 81 | multibinder.addBinding().toInstance("C"); |
| 82 | } |
| 83 | }; |
| 84 | Module de = new AbstractModule() { |
| 85 | protected void configure() { |
| 86 | Multibinder<String> multibinder = Multibinder.newSetBinder(binder(), String.class); |
| 87 | multibinder.addBinding().toInstance("D"); |
| 88 | multibinder.addBinding().toInstance("E"); |
| 89 | } |
| 90 | }; |
| 91 | |
| 92 | Injector injector = Guice.createInjector(abc, de); |
sberlin | 75fcf6f | 2010-09-20 00:42:24 +0000 | [diff] [blame] | 93 | Key<Set<String>> setKey = Key.get(setOfString); |
| 94 | Set<String> abcde = injector.getInstance(setKey); |
| 95 | Set<String> results = setOf("A", "B", "C", "D", "E"); |
limpbizkit | 5019270 | 2008-05-01 09:55:02 +0000 | [diff] [blame] | 96 | |
sberlin | 75fcf6f | 2010-09-20 00:42:24 +0000 | [diff] [blame] | 97 | assertEquals(results, abcde); |
| 98 | assertSetVisitor(setKey, stringType, setOf(abc, de), BOTH, false, 0, instance("A"), |
| 99 | instance("B"), instance("C"), instance("D"), instance("E")); |
limpbizkit | 5019270 | 2008-05-01 09:55:02 +0000 | [diff] [blame] | 100 | } |
| 101 | |
| 102 | public void testMultibinderAggregationForAnnotationInstance() { |
sberlin | 75fcf6f | 2010-09-20 00:42:24 +0000 | [diff] [blame] | 103 | Module module = new AbstractModule() { |
limpbizkit | 5019270 | 2008-05-01 09:55:02 +0000 | [diff] [blame] | 104 | protected void configure() { |
| 105 | Multibinder<String> multibinder |
| 106 | = Multibinder.newSetBinder(binder(), String.class, Names.named("abc")); |
| 107 | multibinder.addBinding().toInstance("A"); |
| 108 | multibinder.addBinding().toInstance("B"); |
| 109 | |
| 110 | multibinder = Multibinder.newSetBinder(binder(), String.class, Names.named("abc")); |
| 111 | multibinder.addBinding().toInstance("C"); |
| 112 | } |
sberlin | 75fcf6f | 2010-09-20 00:42:24 +0000 | [diff] [blame] | 113 | }; |
| 114 | Injector injector = Guice.createInjector(module); |
limpbizkit | 5019270 | 2008-05-01 09:55:02 +0000 | [diff] [blame] | 115 | |
sberlin | 75fcf6f | 2010-09-20 00:42:24 +0000 | [diff] [blame] | 116 | Key<Set<String>> setKey = Key.get(setOfString, Names.named("abc")); |
| 117 | Set<String> abc = injector.getInstance(setKey); |
| 118 | Set<String> results = setOf("A", "B", "C"); |
| 119 | assertEquals(results, abc); |
| 120 | assertSetVisitor(setKey, stringType, setOf(module), BOTH, false, 0, instance("A"), |
| 121 | instance("B"), instance("C")); |
limpbizkit | 5019270 | 2008-05-01 09:55:02 +0000 | [diff] [blame] | 122 | } |
| 123 | |
| 124 | public void testMultibinderAggregationForAnnotationType() { |
sberlin | 75fcf6f | 2010-09-20 00:42:24 +0000 | [diff] [blame] | 125 | Module module = new AbstractModule() { |
limpbizkit | 5019270 | 2008-05-01 09:55:02 +0000 | [diff] [blame] | 126 | protected void configure() { |
| 127 | Multibinder<String> multibinder |
| 128 | = Multibinder.newSetBinder(binder(), String.class, Abc.class); |
| 129 | multibinder.addBinding().toInstance("A"); |
| 130 | multibinder.addBinding().toInstance("B"); |
| 131 | |
| 132 | multibinder = Multibinder.newSetBinder(binder(), String.class, Abc.class); |
| 133 | multibinder.addBinding().toInstance("C"); |
| 134 | } |
sberlin | 75fcf6f | 2010-09-20 00:42:24 +0000 | [diff] [blame] | 135 | }; |
| 136 | Injector injector = Guice.createInjector(module); |
limpbizkit | 5019270 | 2008-05-01 09:55:02 +0000 | [diff] [blame] | 137 | |
sberlin | 75fcf6f | 2010-09-20 00:42:24 +0000 | [diff] [blame] | 138 | Key<Set<String>> setKey = Key.get(setOfString, Abc.class); |
| 139 | Set<String> abcde = injector.getInstance(setKey); |
| 140 | Set<String> results = setOf("A", "B", "C"); |
| 141 | assertEquals(results, abcde); |
| 142 | assertSetVisitor(setKey, stringType, setOf(module), BOTH, false, 0, instance("A"), |
| 143 | instance("B"), instance("C")); |
limpbizkit | 5019270 | 2008-05-01 09:55:02 +0000 | [diff] [blame] | 144 | } |
| 145 | |
| 146 | public void testMultibinderWithMultipleAnnotationValueSets() { |
sberlin | 75fcf6f | 2010-09-20 00:42:24 +0000 | [diff] [blame] | 147 | Module module = new AbstractModule() { |
limpbizkit | 5019270 | 2008-05-01 09:55:02 +0000 | [diff] [blame] | 148 | protected void configure() { |
| 149 | Multibinder<String> abcMultibinder |
| 150 | = Multibinder.newSetBinder(binder(), String.class, named("abc")); |
| 151 | abcMultibinder.addBinding().toInstance("A"); |
| 152 | abcMultibinder.addBinding().toInstance("B"); |
| 153 | abcMultibinder.addBinding().toInstance("C"); |
| 154 | |
| 155 | Multibinder<String> deMultibinder |
| 156 | = Multibinder.newSetBinder(binder(), String.class, named("de")); |
| 157 | deMultibinder.addBinding().toInstance("D"); |
| 158 | deMultibinder.addBinding().toInstance("E"); |
| 159 | } |
sberlin | 75fcf6f | 2010-09-20 00:42:24 +0000 | [diff] [blame] | 160 | }; |
| 161 | Injector injector = Guice.createInjector(module); |
limpbizkit | 5019270 | 2008-05-01 09:55:02 +0000 | [diff] [blame] | 162 | |
sberlin | 75fcf6f | 2010-09-20 00:42:24 +0000 | [diff] [blame] | 163 | Key<Set<String>> abcSetKey = Key.get(setOfString, named("abc")); |
| 164 | Set<String> abc = injector.getInstance(abcSetKey); |
| 165 | Key<Set<String>> deSetKey = Key.get(setOfString, named("de")); |
| 166 | Set<String> de = injector.getInstance(deSetKey); |
| 167 | Set<String> abcResults = setOf("A", "B", "C"); |
| 168 | assertEquals(abcResults, abc); |
| 169 | Set<String> deResults = setOf("D", "E"); |
| 170 | assertEquals(deResults, de); |
| 171 | assertSetVisitor(abcSetKey, stringType, setOf(module), BOTH, false, 1, instance("A"), |
| 172 | instance("B"), instance("C")); |
| 173 | assertSetVisitor(deSetKey, stringType, setOf(module), BOTH, false, 1, instance("D"), instance("E")); |
limpbizkit | 5019270 | 2008-05-01 09:55:02 +0000 | [diff] [blame] | 174 | } |
| 175 | |
| 176 | public void testMultibinderWithMultipleAnnotationTypeSets() { |
sberlin | 75fcf6f | 2010-09-20 00:42:24 +0000 | [diff] [blame] | 177 | Module module = new AbstractModule() { |
limpbizkit | 5019270 | 2008-05-01 09:55:02 +0000 | [diff] [blame] | 178 | protected void configure() { |
| 179 | Multibinder<String> abcMultibinder |
| 180 | = Multibinder.newSetBinder(binder(), String.class, Abc.class); |
| 181 | abcMultibinder.addBinding().toInstance("A"); |
| 182 | abcMultibinder.addBinding().toInstance("B"); |
| 183 | abcMultibinder.addBinding().toInstance("C"); |
| 184 | |
| 185 | Multibinder<String> deMultibinder |
| 186 | = Multibinder.newSetBinder(binder(), String.class, De.class); |
| 187 | deMultibinder.addBinding().toInstance("D"); |
| 188 | deMultibinder.addBinding().toInstance("E"); |
| 189 | } |
sberlin | 75fcf6f | 2010-09-20 00:42:24 +0000 | [diff] [blame] | 190 | }; |
| 191 | Injector injector = Guice.createInjector(module); |
limpbizkit | 5019270 | 2008-05-01 09:55:02 +0000 | [diff] [blame] | 192 | |
sberlin | 75fcf6f | 2010-09-20 00:42:24 +0000 | [diff] [blame] | 193 | Key<Set<String>> abcSetKey = Key.get(setOfString, Abc.class); |
| 194 | Set<String> abc = injector.getInstance(abcSetKey); |
| 195 | Key<Set<String>> deSetKey = Key.get(setOfString, De.class); |
| 196 | Set<String> de = injector.getInstance(deSetKey); |
| 197 | Set<String> abcResults = setOf("A", "B", "C"); |
| 198 | assertEquals(abcResults, abc); |
| 199 | Set<String> deResults = setOf("D", "E"); |
| 200 | assertEquals(deResults, de); |
| 201 | assertSetVisitor(abcSetKey, stringType, setOf(module), BOTH, false, 1, instance("A"), |
| 202 | instance("B"), instance("C")); |
| 203 | assertSetVisitor(deSetKey, stringType, setOf(module), BOTH, false, 1, instance("D"), instance("E")); |
limpbizkit | 5019270 | 2008-05-01 09:55:02 +0000 | [diff] [blame] | 204 | } |
| 205 | |
limpbizkit | 7c533ac | 2008-05-01 14:17:53 +0000 | [diff] [blame] | 206 | public void testMultibinderWithMultipleSetTypes() { |
sberlin | 75fcf6f | 2010-09-20 00:42:24 +0000 | [diff] [blame] | 207 | Module module = new AbstractModule() { |
limpbizkit | 7c533ac | 2008-05-01 14:17:53 +0000 | [diff] [blame] | 208 | protected void configure() { |
| 209 | Multibinder.newSetBinder(binder(), String.class) |
| 210 | .addBinding().toInstance("A"); |
| 211 | Multibinder.newSetBinder(binder(), Integer.class) |
| 212 | .addBinding().toInstance(1); |
| 213 | } |
sberlin | 75fcf6f | 2010-09-20 00:42:24 +0000 | [diff] [blame] | 214 | }; |
| 215 | Injector injector = Guice.createInjector(module); |
limpbizkit | 7c533ac | 2008-05-01 14:17:53 +0000 | [diff] [blame] | 216 | |
| 217 | assertEquals(setOf("A"), injector.getInstance(Key.get(setOfString))); |
| 218 | assertEquals(setOf(1), injector.getInstance(Key.get(setOfInteger))); |
sberlin | 75fcf6f | 2010-09-20 00:42:24 +0000 | [diff] [blame] | 219 | assertSetVisitor(Key.get(setOfString), stringType, setOf(module), BOTH, false, 1, instance("A")); |
| 220 | assertSetVisitor(Key.get(setOfInteger), intType, setOf(module), BOTH, false, 1, instance(1)); |
limpbizkit | 7c533ac | 2008-05-01 14:17:53 +0000 | [diff] [blame] | 221 | } |
| 222 | |
limpbizkit | 5019270 | 2008-05-01 09:55:02 +0000 | [diff] [blame] | 223 | public void testMultibinderWithEmptySet() { |
sberlin | 75fcf6f | 2010-09-20 00:42:24 +0000 | [diff] [blame] | 224 | Module module = new AbstractModule() { |
limpbizkit | 5019270 | 2008-05-01 09:55:02 +0000 | [diff] [blame] | 225 | protected void configure() { |
| 226 | Multibinder.newSetBinder(binder(), String.class); |
| 227 | } |
sberlin | 75fcf6f | 2010-09-20 00:42:24 +0000 | [diff] [blame] | 228 | }; |
| 229 | Injector injector = Guice.createInjector(module); |
limpbizkit | 5019270 | 2008-05-01 09:55:02 +0000 | [diff] [blame] | 230 | |
| 231 | Set<String> set = injector.getInstance(Key.get(setOfString)); |
| 232 | assertEquals(Collections.emptySet(), set); |
sberlin | 75fcf6f | 2010-09-20 00:42:24 +0000 | [diff] [blame] | 233 | assertSetVisitor(Key.get(setOfString), stringType, setOf(module), BOTH, false, 0); |
limpbizkit | 5019270 | 2008-05-01 09:55:02 +0000 | [diff] [blame] | 234 | } |
| 235 | |
| 236 | public void testMultibinderSetIsUnmodifiable() { |
| 237 | Injector injector = Guice.createInjector(new AbstractModule() { |
| 238 | protected void configure() { |
| 239 | Multibinder.newSetBinder(binder(), String.class) |
| 240 | .addBinding().toInstance("A"); |
| 241 | } |
| 242 | }); |
| 243 | |
| 244 | Set<String> set = injector.getInstance(Key.get(setOfString)); |
| 245 | try { |
| 246 | set.clear(); |
| 247 | fail(); |
| 248 | } catch(UnsupportedOperationException expected) { |
| 249 | } |
| 250 | } |
| 251 | |
| 252 | public void testMultibinderSetIsLazy() { |
sberlin | 75fcf6f | 2010-09-20 00:42:24 +0000 | [diff] [blame] | 253 | Module module = new AbstractModule() { |
limpbizkit | 5019270 | 2008-05-01 09:55:02 +0000 | [diff] [blame] | 254 | protected void configure() { |
| 255 | Multibinder.newSetBinder(binder(), Integer.class) |
| 256 | .addBinding().toProvider(new Provider<Integer>() { |
| 257 | int nextValue = 1; |
| 258 | public Integer get() { |
| 259 | return nextValue++; |
| 260 | } |
| 261 | }); |
| 262 | } |
sberlin | 75fcf6f | 2010-09-20 00:42:24 +0000 | [diff] [blame] | 263 | }; |
| 264 | Injector injector = Guice.createInjector(module); |
limpbizkit | 5019270 | 2008-05-01 09:55:02 +0000 | [diff] [blame] | 265 | |
| 266 | assertEquals(setOf(1), injector.getInstance(Key.get(setOfInteger))); |
| 267 | assertEquals(setOf(2), injector.getInstance(Key.get(setOfInteger))); |
| 268 | assertEquals(setOf(3), injector.getInstance(Key.get(setOfInteger))); |
sberlin | 75fcf6f | 2010-09-20 00:42:24 +0000 | [diff] [blame] | 269 | assertSetVisitor(Key.get(setOfInteger), intType, setOf(module), BOTH, false, 0, providerInstance(1)); |
limpbizkit | 5019270 | 2008-05-01 09:55:02 +0000 | [diff] [blame] | 270 | } |
| 271 | |
| 272 | public void testMultibinderSetForbidsDuplicateElements() { |
sberlin | 75fcf6f | 2010-09-20 00:42:24 +0000 | [diff] [blame] | 273 | Module module = new AbstractModule() { |
limpbizkit | 5019270 | 2008-05-01 09:55:02 +0000 | [diff] [blame] | 274 | protected void configure() { |
| 275 | final Multibinder<String> multibinder = Multibinder.newSetBinder(binder(), String.class); |
| 276 | multibinder.addBinding().toInstance("A"); |
| 277 | multibinder.addBinding().toInstance("A"); |
| 278 | } |
sberlin | 75fcf6f | 2010-09-20 00:42:24 +0000 | [diff] [blame] | 279 | }; |
| 280 | Injector injector = Guice.createInjector(module); |
limpbizkit | 5019270 | 2008-05-01 09:55:02 +0000 | [diff] [blame] | 281 | |
| 282 | try { |
| 283 | injector.getInstance(Key.get(setOfString)); |
| 284 | fail(); |
limpbizkit | 49f67c0 | 2008-06-10 20:56:17 +0000 | [diff] [blame] | 285 | } catch(ProvisionException expected) { |
limpbizkit | 72d11dd | 2008-11-02 07:59:13 +0000 | [diff] [blame] | 286 | assertContains(expected.getMessage(), |
| 287 | "1) Set injection failed due to duplicated element \"A\""); |
limpbizkit | 5019270 | 2008-05-01 09:55:02 +0000 | [diff] [blame] | 288 | } |
sberlin | 75fcf6f | 2010-09-20 00:42:24 +0000 | [diff] [blame] | 289 | |
| 290 | // But we can still visit the module! |
| 291 | assertSetVisitor(Key.get(setOfString), stringType, setOf(module), MODULE, false, 0, |
| 292 | instance("A"), instance("A")); |
limpbizkit | 5019270 | 2008-05-01 09:55:02 +0000 | [diff] [blame] | 293 | } |
sberlin | 75fcf6f | 2010-09-20 00:42:24 +0000 | [diff] [blame] | 294 | |
limpbizkit | 398017a | 2009-06-23 06:30:37 +0000 | [diff] [blame] | 295 | public void testMultibinderSetPermitDuplicateElements() { |
sberlin | 75fcf6f | 2010-09-20 00:42:24 +0000 | [diff] [blame] | 296 | Module ab = new AbstractModule() { |
| 297 | protected void configure() { |
| 298 | Multibinder<String> multibinder = Multibinder.newSetBinder(binder(), String.class); |
| 299 | multibinder.addBinding().toInstance("A"); |
| 300 | multibinder.addBinding().toInstance("B"); |
| 301 | } |
| 302 | }; |
| 303 | Module bc = new AbstractModule() { |
| 304 | protected void configure() { |
| 305 | Multibinder<String> multibinder = Multibinder.newSetBinder(binder(), String.class); |
| 306 | multibinder.permitDuplicates(); |
| 307 | multibinder.addBinding().toInstance("B"); |
| 308 | multibinder.addBinding().toInstance("C"); |
| 309 | } |
| 310 | }; |
| 311 | Injector injector = Guice.createInjector(ab, bc); |
limpbizkit | 398017a | 2009-06-23 06:30:37 +0000 | [diff] [blame] | 312 | |
| 313 | assertEquals(setOf("A", "B", "C"), injector.getInstance(Key.get(setOfString))); |
sberlin | 75fcf6f | 2010-09-20 00:42:24 +0000 | [diff] [blame] | 314 | assertSetVisitor(Key.get(setOfString), stringType, setOf(ab, bc), BOTH, true, 0, |
| 315 | instance("A"), instance("B"), instance("B"), instance("C")); |
limpbizkit | 398017a | 2009-06-23 06:30:37 +0000 | [diff] [blame] | 316 | } |
| 317 | |
| 318 | public void testMultibinderSetPermitDuplicateCallsToPermitDuplicates() { |
sberlin | 75fcf6f | 2010-09-20 00:42:24 +0000 | [diff] [blame] | 319 | Module ab = new AbstractModule() { |
| 320 | protected void configure() { |
| 321 | Multibinder<String> multibinder = Multibinder.newSetBinder(binder(), String.class); |
| 322 | multibinder.permitDuplicates(); |
| 323 | multibinder.addBinding().toInstance("A"); |
| 324 | multibinder.addBinding().toInstance("B"); |
| 325 | } |
| 326 | }; |
| 327 | Module bc = new AbstractModule() { |
| 328 | protected void configure() { |
| 329 | Multibinder<String> multibinder = Multibinder.newSetBinder(binder(), String.class); |
| 330 | multibinder.permitDuplicates(); |
| 331 | multibinder.addBinding().toInstance("B"); |
| 332 | multibinder.addBinding().toInstance("C"); |
| 333 | } |
| 334 | }; |
| 335 | Injector injector = Guice.createInjector(ab, bc); |
limpbizkit | 398017a | 2009-06-23 06:30:37 +0000 | [diff] [blame] | 336 | |
| 337 | assertEquals(setOf("A", "B", "C"), injector.getInstance(Key.get(setOfString))); |
sberlin | 75fcf6f | 2010-09-20 00:42:24 +0000 | [diff] [blame] | 338 | assertSetVisitor(Key.get(setOfString), stringType, setOf(ab, bc), BOTH, true, 0, |
| 339 | instance("A"), instance("B"), instance("B"), instance("C")); |
limpbizkit | 398017a | 2009-06-23 06:30:37 +0000 | [diff] [blame] | 340 | } |
limpbizkit | 5019270 | 2008-05-01 09:55:02 +0000 | [diff] [blame] | 341 | |
| 342 | public void testMultibinderSetForbidsNullElements() { |
| 343 | Injector injector = Guice.createInjector(new AbstractModule() { |
| 344 | protected void configure() { |
| 345 | Multibinder.newSetBinder(binder(), String.class) |
| 346 | .addBinding().toProvider(Providers.<String>of(null)); |
| 347 | } |
| 348 | }); |
| 349 | |
| 350 | try { |
| 351 | injector.getInstance(Key.get(setOfString)); |
| 352 | fail(); |
limpbizkit | 49f67c0 | 2008-06-10 20:56:17 +0000 | [diff] [blame] | 353 | } catch(ProvisionException expected) { |
limpbizkit | 72d11dd | 2008-11-02 07:59:13 +0000 | [diff] [blame] | 354 | assertContains(expected.getMessage(), |
| 355 | "1) Set injection failed due to null element"); |
limpbizkit | 5019270 | 2008-05-01 09:55:02 +0000 | [diff] [blame] | 356 | } |
| 357 | } |
| 358 | |
limpbizkit | d6967b9 | 2008-05-16 15:28:51 +0000 | [diff] [blame] | 359 | public void testSourceLinesInMultibindings() { |
| 360 | try { |
| 361 | Guice.createInjector(new AbstractModule() { |
| 362 | @Override protected void configure() { |
| 363 | Multibinder.newSetBinder(binder(), Integer.class).addBinding(); |
| 364 | } |
| 365 | }); |
| 366 | fail(); |
limpbizkit | eab7647 | 2008-05-26 19:45:12 +0000 | [diff] [blame] | 367 | } catch (CreationException expected) { |
limpbizkit | a6e0e78 | 2008-09-03 06:19:56 +0000 | [diff] [blame] | 368 | assertContains(expected.getMessage(), "No implementation for java.lang.Integer", |
| 369 | "at " + getClass().getName()); |
limpbizkit | d6967b9 | 2008-05-16 15:28:51 +0000 | [diff] [blame] | 370 | } |
| 371 | } |
| 372 | |
limpbizkit | ddb3862 | 2008-12-29 05:21:16 +0000 | [diff] [blame] | 373 | /** |
| 374 | * We just want to make sure that multibinder's binding depends on each of its values. We don't |
| 375 | * really care about the underlying structure of those bindings, which are implementation details. |
| 376 | */ |
| 377 | public void testMultibinderDependencies() { |
| 378 | Injector injector = Guice.createInjector(new AbstractModule() { |
| 379 | protected void configure() { |
| 380 | Multibinder<String> multibinder = Multibinder.newSetBinder(binder(), String.class); |
| 381 | multibinder.addBinding().toInstance("A"); |
| 382 | multibinder.addBinding().to(Key.get(String.class, Names.named("b"))); |
| 383 | |
| 384 | bindConstant().annotatedWith(Names.named("b")).to("B"); |
| 385 | } |
| 386 | }); |
| 387 | |
| 388 | Binding<Set<String>> binding = injector.getBinding(new Key<Set<String>>() {}); |
| 389 | HasDependencies withDependencies = (HasDependencies) binding; |
| 390 | Set<String> elements = Sets.newHashSet(); |
| 391 | for (Dependency<?> dependency : withDependencies.getDependencies()) { |
| 392 | elements.add((String) injector.getInstance(dependency.getKey())); |
| 393 | } |
| 394 | assertEquals(ImmutableSet.of("A", "B"), elements); |
| 395 | } |
sberlin | 8bc8387 | 2010-04-26 02:12:22 +0000 | [diff] [blame] | 396 | |
| 397 | /** |
| 398 | * We just want to make sure that multibinder's binding depends on each of its values. We don't |
| 399 | * really care about the underlying structure of those bindings, which are implementation details. |
| 400 | */ |
| 401 | public void testMultibinderDependenciesInToolStage() { |
sberlin | 8b64d45 | 2010-12-13 02:44:36 +0000 | [diff] [blame] | 402 | Injector injector = Guice.createInjector(Stage.TOOL, new AbstractModule() { |
sberlin | 8bc8387 | 2010-04-26 02:12:22 +0000 | [diff] [blame] | 403 | protected void configure() { |
| 404 | Multibinder<String> multibinder = Multibinder.newSetBinder(binder(), String.class); |
| 405 | multibinder.addBinding().toInstance("A"); |
| 406 | multibinder.addBinding().to(Key.get(String.class, Names.named("b"))); |
| 407 | |
| 408 | bindConstant().annotatedWith(Names.named("b")).to("B"); |
sberlin | 8b64d45 | 2010-12-13 02:44:36 +0000 | [diff] [blame] | 409 | }}); |
sberlin | 8bc8387 | 2010-04-26 02:12:22 +0000 | [diff] [blame] | 410 | |
| 411 | Binding<Set<String>> binding = injector.getBinding(new Key<Set<String>>() {}); |
| 412 | HasDependencies withDependencies = (HasDependencies) binding; |
| 413 | InstanceBinding<?> instanceBinding = null; |
| 414 | LinkedKeyBinding<?> linkedBinding = null; |
| 415 | // The non-tool stage test can test this by calling injector.getInstance to ensure |
| 416 | // the right values are returned -- in tool stage we can't do that. It's also a |
| 417 | // little difficult to validate the dependencies & bindings, because they're |
| 418 | // bindings created internally within Multibinder. |
| 419 | // To workaround this, we just validate that the dependencies lookup to a single |
| 420 | // InstanceBinding whose value is "A" and another LinkedBinding whose target is |
| 421 | // the Key of @Named("b") String=B |
| 422 | for (Dependency<?> dependency : withDependencies.getDependencies()) { |
| 423 | Binding<?> b = injector.getBinding(dependency.getKey()); |
| 424 | if(b instanceof InstanceBinding) { |
| 425 | if(instanceBinding != null) { |
| 426 | fail("Already have an instance binding of: " + instanceBinding + ", and now want to add: " + b); |
| 427 | } else { |
| 428 | instanceBinding = (InstanceBinding)b; |
| 429 | } |
| 430 | } else if(b instanceof LinkedKeyBinding) { |
| 431 | if(linkedBinding != null) { |
| 432 | fail("Already have a linked binding of: " + linkedBinding + ", and now want to add: " + b); |
| 433 | } else { |
| 434 | linkedBinding = (LinkedKeyBinding)b; |
| 435 | } |
| 436 | } else { |
| 437 | fail("Unexpected dependency of: " + dependency); |
| 438 | } |
| 439 | } |
| 440 | |
| 441 | assertNotNull(instanceBinding); |
| 442 | assertNotNull(linkedBinding); |
| 443 | |
| 444 | assertEquals("A", instanceBinding.getInstance()); |
| 445 | assertEquals(Key.get(String.class, Names.named("b")), linkedBinding.getLinkedKey()); |
| 446 | } |
limpbizkit | ddb3862 | 2008-12-29 05:21:16 +0000 | [diff] [blame] | 447 | |
limpbizkit | 63a9605 | 2009-03-30 17:28:16 +0000 | [diff] [blame] | 448 | /** |
| 449 | * Our implementation maintains order, but doesn't guarantee it in the API spec. |
| 450 | * TODO: specify the iteration order? |
| 451 | */ |
| 452 | public void testBindOrderEqualsIterationOrder() { |
| 453 | Injector injector = Guice.createInjector( |
| 454 | new AbstractModule() { |
| 455 | protected void configure() { |
| 456 | Multibinder<String> multibinder = Multibinder.newSetBinder(binder(), String.class); |
| 457 | multibinder.addBinding().toInstance("leonardo"); |
| 458 | multibinder.addBinding().toInstance("donatello"); |
| 459 | install(new AbstractModule() { |
| 460 | protected void configure() { |
| 461 | Multibinder.newSetBinder(binder(), String.class) |
| 462 | .addBinding().toInstance("michaelangelo"); |
| 463 | } |
| 464 | }); |
| 465 | } |
| 466 | }, |
| 467 | new AbstractModule() { |
| 468 | protected void configure() { |
| 469 | Multibinder.newSetBinder(binder(), String.class).addBinding().toInstance("raphael"); |
| 470 | } |
| 471 | }); |
| 472 | |
| 473 | List<String> inOrder = ImmutableList.copyOf(injector.getInstance(Key.get(setOfString))); |
| 474 | assertEquals(ImmutableList.of("leonardo", "donatello", "michaelangelo", "raphael"), inOrder); |
| 475 | } |
| 476 | |
limpbizkit | 5019270 | 2008-05-01 09:55:02 +0000 | [diff] [blame] | 477 | @Retention(RUNTIME) @BindingAnnotation |
| 478 | @interface Abc {} |
| 479 | |
| 480 | @Retention(RUNTIME) @BindingAnnotation |
| 481 | @interface De {} |
| 482 | |
| 483 | private <T> Set<T> setOf(T... elements) { |
limpbizkit | 9532e62 | 2008-06-18 08:20:54 +0000 | [diff] [blame] | 484 | Set<T> result = Sets.newHashSet(); |
limpbizkit | 5019270 | 2008-05-01 09:55:02 +0000 | [diff] [blame] | 485 | result.addAll(Arrays.asList(elements)); |
| 486 | return result; |
| 487 | } |
limpbizkit | d8750fd | 2009-08-10 23:30:46 +0000 | [diff] [blame] | 488 | |
| 489 | /** |
| 490 | * With overrides, we should get the union of all multibindings. |
| 491 | */ |
| 492 | public void testModuleOverrideAndMultibindings() { |
| 493 | Module ab = new AbstractModule() { |
| 494 | protected void configure() { |
| 495 | Multibinder<String> multibinder = Multibinder.newSetBinder(binder(), String.class); |
| 496 | multibinder.addBinding().toInstance("A"); |
| 497 | multibinder.addBinding().toInstance("B"); |
| 498 | } |
| 499 | }; |
| 500 | Module cd = new AbstractModule() { |
| 501 | protected void configure() { |
| 502 | Multibinder<String> multibinder = Multibinder.newSetBinder(binder(), String.class); |
| 503 | multibinder.addBinding().toInstance("C"); |
| 504 | multibinder.addBinding().toInstance("D"); |
| 505 | } |
| 506 | }; |
| 507 | Module ef = new AbstractModule() { |
| 508 | protected void configure() { |
| 509 | Multibinder<String> multibinder = Multibinder.newSetBinder(binder(), String.class); |
| 510 | multibinder.addBinding().toInstance("E"); |
| 511 | multibinder.addBinding().toInstance("F"); |
| 512 | } |
| 513 | }; |
| 514 | |
| 515 | Module abcd = Modules.override(ab).with(cd); |
| 516 | Injector injector = Guice.createInjector(abcd, ef); |
| 517 | assertEquals(ImmutableSet.of("A", "B", "C", "D", "E", "F"), |
| 518 | injector.getInstance(Key.get(setOfString))); |
| 519 | |
sberlin | 75fcf6f | 2010-09-20 00:42:24 +0000 | [diff] [blame] | 520 | assertSetVisitor(Key.get(setOfString), stringType, setOf(abcd, ef), BOTH, false, 0, |
| 521 | instance("A"), instance("B"), instance("C"), instance("D"), instance("E"), instance("F")); |
limpbizkit | d8750fd | 2009-08-10 23:30:46 +0000 | [diff] [blame] | 522 | } |
sberlin | 75fcf6f | 2010-09-20 00:42:24 +0000 | [diff] [blame] | 523 | |
sberlin | 34d2f00 | 2010-05-02 20:11:28 +0000 | [diff] [blame] | 524 | /** |
| 525 | * With overrides, we should get the union of all multibindings. |
| 526 | */ |
| 527 | public void testModuleOverrideAndMultibindingsWithPermitDuplicates() { |
sberlin | 75fcf6f | 2010-09-20 00:42:24 +0000 | [diff] [blame] | 528 | Module abc = new AbstractModule() { |
sberlin | 34d2f00 | 2010-05-02 20:11:28 +0000 | [diff] [blame] | 529 | protected void configure() { |
| 530 | Multibinder<String> multibinder = Multibinder.newSetBinder(binder(), String.class); |
| 531 | multibinder.addBinding().toInstance("A"); |
| 532 | multibinder.addBinding().toInstance("B"); |
sberlin | 75fcf6f | 2010-09-20 00:42:24 +0000 | [diff] [blame] | 533 | multibinder.addBinding().toInstance("C"); |
sberlin | 34d2f00 | 2010-05-02 20:11:28 +0000 | [diff] [blame] | 534 | multibinder.permitDuplicates(); |
| 535 | } |
| 536 | }; |
| 537 | Module cd = new AbstractModule() { |
| 538 | protected void configure() { |
| 539 | Multibinder<String> multibinder = Multibinder.newSetBinder(binder(), String.class); |
| 540 | multibinder.addBinding().toInstance("C"); |
| 541 | multibinder.addBinding().toInstance("D"); |
| 542 | multibinder.permitDuplicates(); |
| 543 | } |
| 544 | }; |
| 545 | Module ef = new AbstractModule() { |
| 546 | protected void configure() { |
| 547 | Multibinder<String> multibinder = Multibinder.newSetBinder(binder(), String.class); |
| 548 | multibinder.addBinding().toInstance("E"); |
| 549 | multibinder.addBinding().toInstance("F"); |
| 550 | multibinder.permitDuplicates(); |
| 551 | } |
| 552 | }; |
| 553 | |
sberlin | 75fcf6f | 2010-09-20 00:42:24 +0000 | [diff] [blame] | 554 | Module abcd = Modules.override(abc).with(cd); |
sberlin | 34d2f00 | 2010-05-02 20:11:28 +0000 | [diff] [blame] | 555 | Injector injector = Guice.createInjector(abcd, ef); |
| 556 | assertEquals(ImmutableSet.of("A", "B", "C", "D", "E", "F"), |
| 557 | injector.getInstance(Key.get(setOfString))); |
| 558 | |
sberlin | 75fcf6f | 2010-09-20 00:42:24 +0000 | [diff] [blame] | 559 | assertSetVisitor(Key.get(setOfString), stringType, setOf(abcd, ef), BOTH, true, 0, |
| 560 | instance("A"), instance("B"), instance("C"), instance("C"), instance("D"), instance("E"), instance("F")); |
sberlin | 680c8b5 | 2010-06-11 19:11:13 +0000 | [diff] [blame] | 561 | } |
sberlin | 75fcf6f | 2010-09-20 00:42:24 +0000 | [diff] [blame] | 562 | |
sberlin | 680c8b5 | 2010-06-11 19:11:13 +0000 | [diff] [blame] | 563 | @BindingAnnotation |
| 564 | @Retention(RetentionPolicy.RUNTIME) |
| 565 | @Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD}) |
| 566 | private static @interface Marker {} |
| 567 | |
| 568 | @Marker |
| 569 | public void testMultibinderMatching() throws Exception { |
| 570 | Method m = MultibinderTest.class.getDeclaredMethod("testMultibinderMatching"); |
| 571 | assertNotNull(m); |
| 572 | final Annotation marker = m.getAnnotation(Marker.class); |
| 573 | Injector injector = Guice.createInjector(new AbstractModule() { |
| 574 | @Override public void configure() { |
| 575 | Multibinder<Integer> mb1 = Multibinder.newSetBinder(binder(), Integer.class, Marker.class); |
| 576 | Multibinder<Integer> mb2 = Multibinder.newSetBinder(binder(), Integer.class, marker); |
| 577 | mb1.addBinding().toInstance(1); |
| 578 | mb2.addBinding().toInstance(2); |
| 579 | |
| 580 | // This assures us that the two binders are equivalent, so we expect the instance added to |
| 581 | // each to have been added to one set. |
| 582 | assertEquals(mb1, mb2); |
| 583 | } |
| 584 | }); |
| 585 | TypeLiteral<Set<Integer>> t = new TypeLiteral<Set<Integer>>() {}; |
| 586 | Set<Integer> s1 = injector.getInstance(Key.get(t, Marker.class)); |
| 587 | Set<Integer> s2 = injector.getInstance(Key.get(t, marker)); |
| 588 | |
| 589 | // This assures us that the two sets are in fact equal. They may not be same set (as in Java |
| 590 | // object identical), but we shouldn't expect that, since probably Guice creates the set each |
| 591 | // time in case the elements are dependent on scope. |
| 592 | assertEquals(s1, s2); |
| 593 | |
| 594 | // This ensures that MultiBinder is internally using the correct set name -- |
| 595 | // making sure that instances of marker annotations have the same set name as |
| 596 | // MarkerAnnotation.class. |
| 597 | Set<Integer> expected = new HashSet<Integer>(); |
| 598 | expected.add(1); |
| 599 | expected.add(2); |
| 600 | assertEquals(expected, s1); |
sberlin | 75fcf6f | 2010-09-20 00:42:24 +0000 | [diff] [blame] | 601 | } |
limpbizkit | 5019270 | 2008-05-01 09:55:02 +0000 | [diff] [blame] | 602 | } |