blob: 95ac12deac1f7d51230f91202b88463d6e56bedf [file] [log] [blame]
sberlin4e114572011-06-28 23:50:03 +00001/**
2 * Copyright (C) 2010 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.multibindings;
18
19import static com.google.inject.multibindings.MapBinder.entryOfProviderOf;
20import static com.google.inject.multibindings.MapBinder.mapOf;
Sam Berlin3338a482013-12-06 17:07:34 -050021import static com.google.inject.multibindings.MapBinder.mapOfJavaxProviderOf;
sberlin4e114572011-06-28 23:50:03 +000022import static com.google.inject.multibindings.MapBinder.mapOfProviderOf;
23import static com.google.inject.multibindings.MapBinder.mapOfSetOfProviderOf;
Sam Berlin4faa20e2014-08-11 13:24:22 -040024import static com.google.inject.multibindings.Multibinder.collectionOfProvidersOf;
sberlin4e114572011-06-28 23:50:03 +000025import static com.google.inject.multibindings.Multibinder.setOf;
sameb1d3f8cd2014-10-07 15:32:12 -070026import static com.google.inject.multibindings.OptionalBinder.javaOptionalOfJavaxProvider;
27import static com.google.inject.multibindings.OptionalBinder.javaOptionalOfProvider;
Christian Edward Gruberaf24f632014-04-01 15:07:55 -070028import static com.google.inject.multibindings.OptionalBinder.optionalOfJavaxProvider;
29import static com.google.inject.multibindings.OptionalBinder.optionalOfProvider;
sberlin4e114572011-06-28 23:50:03 +000030import static com.google.inject.multibindings.SpiUtils.BindType.INSTANCE;
31import static com.google.inject.multibindings.SpiUtils.BindType.LINKED;
32import static com.google.inject.multibindings.SpiUtils.BindType.PROVIDER_INSTANCE;
Christian Edward Gruberaf24f632014-04-01 15:07:55 -070033import static com.google.inject.multibindings.SpiUtils.BindType.PROVIDER_KEY;
sberlin4e114572011-06-28 23:50:03 +000034import static com.google.inject.multibindings.SpiUtils.VisitType.BOTH;
35import static com.google.inject.multibindings.SpiUtils.VisitType.INJECTOR;
36import static com.google.inject.multibindings.SpiUtils.VisitType.MODULE;
37import static junit.framework.Assert.assertEquals;
Sam Berlin4faa20e2014-08-11 13:24:22 -040038import static junit.framework.Assert.assertFalse;
sberlin4e114572011-06-28 23:50:03 +000039import static junit.framework.Assert.assertNotNull;
Christian Edward Gruberaf24f632014-04-01 15:07:55 -070040import static junit.framework.Assert.assertNull;
sberlin4e114572011-06-28 23:50:03 +000041import static junit.framework.Assert.assertTrue;
42import static junit.framework.Assert.fail;
43
Christian Edward Gruberaf24f632014-04-01 15:07:55 -070044import com.google.common.base.Objects;
45import com.google.common.base.Optional;
46import com.google.common.collect.ImmutableMap;
Christian Edward Gruber6c69bcf2013-09-18 10:55:21 -070047import com.google.common.collect.ImmutableSet;
sberlinb7a02b02011-07-08 00:34:16 +000048import com.google.common.collect.Lists;
Sam Berlinc34e0182014-08-06 11:56:26 -040049import com.google.common.collect.Maps;
50import com.google.common.collect.Multimap;
51import com.google.common.collect.MultimapBuilder;
52import com.google.common.collect.Sets;
sberlin4e114572011-06-28 23:50:03 +000053import com.google.inject.Binding;
54import com.google.inject.Guice;
55import com.google.inject.Injector;
56import com.google.inject.Key;
57import com.google.inject.Module;
Christian Edward Gruberaf24f632014-04-01 15:07:55 -070058import com.google.inject.Provider;
sberlin4e114572011-06-28 23:50:03 +000059import com.google.inject.TypeLiteral;
Sam Berlinc34e0182014-08-06 11:56:26 -040060import com.google.inject.multibindings.Indexer.IndexedBinding;
61import com.google.inject.multibindings.MapBinder.RealMapBinder.ProviderMapEntry;
Christian Edward Gruberaf24f632014-04-01 15:07:55 -070062import com.google.inject.multibindings.OptionalBinder.Source;
sberlin4e114572011-06-28 23:50:03 +000063import com.google.inject.spi.DefaultBindingTargetVisitor;
64import com.google.inject.spi.Element;
65import com.google.inject.spi.Elements;
66import com.google.inject.spi.InstanceBinding;
67import com.google.inject.spi.LinkedKeyBinding;
68import com.google.inject.spi.ProviderInstanceBinding;
Christian Edward Gruberaf24f632014-04-01 15:07:55 -070069import com.google.inject.spi.ProviderKeyBinding;
sberlin4e114572011-06-28 23:50:03 +000070import com.google.inject.spi.ProviderLookup;
71
sberlinb7a02b02011-07-08 00:34:16 +000072import java.util.HashSet;
73import java.util.List;
74import java.util.Map;
75import java.util.Set;
76
sberlin4e114572011-06-28 23:50:03 +000077/**
78 * Utilities for testing the Multibinder & MapBinder extension SPI.
79 *
80 * @author sameb@google.com (Sam Berlin)
81 */
82public class SpiUtils {
83
sameb1d3f8cd2014-10-07 15:32:12 -070084 private static final boolean HAS_JAVA_OPTIONAL;
85 static {
86 Class<?> optional = null;
87 try {
88 optional = Class.forName("java.util.Optional");
89 } catch (ClassNotFoundException ignored) {}
90 HAS_JAVA_OPTIONAL = optional != null;
91 }
92
sberlin4e114572011-06-28 23:50:03 +000093 /** The kind of test we should perform. A live Injector, a raw Elements (Module) test, or both. */
94 enum VisitType { INJECTOR, MODULE, BOTH }
95
96 /**
97 * Asserts that MapBinderBinding visitors for work correctly.
98 *
99 * @param <T> The type of the binding
100 * @param mapKey The key the map belongs to.
101 * @param keyType the TypeLiteral of the key of the map
102 * @param valueType the TypeLiteral of the value of the map
103 * @param modules The modules that define the mapbindings
104 * @param visitType The kind of test we should perform. A live Injector, a raw Elements (Module) test, or both.
105 * @param allowDuplicates If duplicates are allowed.
106 * @param expectedMapBindings The number of other mapbinders we expect to see.
107 * @param results The kind of bindings contained in the mapbinder.
108 */
109 static <T> void assertMapVisitor(Key<T> mapKey, TypeLiteral<?> keyType, TypeLiteral<?> valueType,
110 Iterable<? extends Module> modules, VisitType visitType, boolean allowDuplicates,
111 int expectedMapBindings, MapResult... results) {
112 if(visitType == null) {
113 fail("must test something");
114 }
115
116 if (visitType == BOTH || visitType == INJECTOR) {
117 mapInjectorTest(mapKey, keyType, valueType, modules, allowDuplicates, expectedMapBindings,
118 results);
119 }
120
121 if (visitType == BOTH || visitType == MODULE) {
122 mapModuleTest(mapKey, keyType, valueType, modules, allowDuplicates, expectedMapBindings,
123 results);
124 }
125 }
126
127 @SuppressWarnings("unchecked")
128 private static <T> void mapInjectorTest(Key<T> mapKey, TypeLiteral<?> keyType,
129 TypeLiteral<?> valueType, Iterable<? extends Module> modules, boolean allowDuplicates,
130 int expectedMapBindings, MapResult... results) {
131 Injector injector = Guice.createInjector(modules);
132 Visitor<T> visitor = new Visitor<T>();
133 Binding<T> mapBinding = injector.getBinding(mapKey);
134 MapBinderBinding<T> mapbinder = (MapBinderBinding<T>)mapBinding.acceptTargetVisitor(visitor);
135 assertNotNull(mapbinder);
136 assertEquals(keyType, mapbinder.getKeyTypeLiteral());
137 assertEquals(valueType, mapbinder.getValueTypeLiteral());
138 assertEquals(allowDuplicates, mapbinder.permitsDuplicates());
139 List<Map.Entry<?, Binding<?>>> entries = Lists.newArrayList(mapbinder.getEntries());
140 List<MapResult> mapResults = Lists.newArrayList(results);
141 assertEquals("wrong entries, expected: " + mapResults + ", but was: " + entries,
142 mapResults.size(), entries.size());
143
144 for(MapResult result : mapResults) {
145 Map.Entry<?, Binding<?>> found = null;
146 for(Map.Entry<?, Binding<?>> entry : entries) {
147 Object key = entry.getKey();
148 Binding<?> value = entry.getValue();
Christian Edward Gruberaf24f632014-04-01 15:07:55 -0700149 if(key.equals(result.k) && matches(value, result.v)) {
150 found = entry;
sberlin4e114572011-06-28 23:50:03 +0000151 break;
152 }
153 }
154 if(found == null) {
155 fail("Could not find entry: " + result + " in remaining entries: " + entries);
156 } else {
Christian Edward Gruberaf24f632014-04-01 15:07:55 -0700157 assertTrue("mapBinder doesn't contain: " + found.getValue(),
158 mapbinder.containsElement(found.getValue()));
sberlin4e114572011-06-28 23:50:03 +0000159 entries.remove(found);
160 }
161 }
162
163 if(!entries.isEmpty()) {
164 fail("Found all entries of: " + mapResults + ", but more were left over: " + entries);
165 }
166
Sam Berlin3338a482013-12-06 17:07:34 -0500167 Key<?> mapOfJavaxProvider = mapKey.ofType(mapOfJavaxProviderOf(keyType, valueType));
168 Key<?> mapOfProvider = mapKey.ofType(mapOfProviderOf(keyType, valueType));
169 Key<?> mapOfSetOfProvider = mapKey.ofType(mapOfSetOfProviderOf(keyType, valueType));
170 Key<?> mapOfSet = mapKey.ofType(mapOf(keyType, setOf(valueType)));
171 Key<?> setOfEntry = mapKey.ofType(setOf(entryOfProviderOf(keyType, valueType)));
flan615fd2b2014-11-04 09:07:58 -0800172 Key<?> collectionOfProvidersOfEntryOfProvider =
173 mapKey.ofType(collectionOfProvidersOf(entryOfProviderOf(keyType, valueType)));
sberlin4e114572011-06-28 23:50:03 +0000174 boolean entrySetMatch = false;
Sam Berlin3338a482013-12-06 17:07:34 -0500175 boolean mapJavaxProviderMatch = false;
sberlin4e114572011-06-28 23:50:03 +0000176 boolean mapProviderMatch = false;
177 boolean mapSetMatch = false;
178 boolean mapSetProviderMatch = false;
flan615fd2b2014-11-04 09:07:58 -0800179 boolean collectionOfProvidersOfEntryOfProviderMatch = false;
sberlin4e114572011-06-28 23:50:03 +0000180 List<Object> otherMapBindings = Lists.newArrayList();
181 List<Binding> otherMatches = Lists.newArrayList();
Sam Berlinc34e0182014-08-06 11:56:26 -0400182 Multimap<Object, IndexedBinding> indexedEntries =
183 MultimapBuilder.hashKeys().hashSetValues().build();
184 Indexer indexer = new Indexer(injector);
185 int duplicates = 0;
sberlin4e114572011-06-28 23:50:03 +0000186 for(Binding b : injector.getAllBindings().values()) {
187 boolean contains = mapbinder.containsElement(b);
188 Object visited = b.acceptTargetVisitor(visitor);
189 if(visited instanceof MapBinderBinding) {
190 if(visited.equals(mapbinder)) {
191 assertTrue(contains);
192 } else {
193 otherMapBindings.add(visited);
194 }
195 } else if(b.getKey().equals(mapOfProvider)) {
196 assertTrue(contains);
197 mapProviderMatch = true;
Sam Berlin3338a482013-12-06 17:07:34 -0500198 } else if (b.getKey().equals(mapOfJavaxProvider)) {
199 assertTrue(contains);
200 mapJavaxProviderMatch = true;
sberlin4e114572011-06-28 23:50:03 +0000201 } else if(b.getKey().equals(mapOfSet)) {
202 assertTrue(contains);
203 mapSetMatch = true;
204 } else if(b.getKey().equals(mapOfSetOfProvider)) {
205 assertTrue(contains);
206 mapSetProviderMatch = true;
207 } else if(b.getKey().equals(setOfEntry)) {
208 assertTrue(contains);
209 entrySetMatch = true;
210 // Validate that this binding is also a MultibinderBinding.
211 assertTrue(b.acceptTargetVisitor(visitor) instanceof MultibinderBinding);
flan615fd2b2014-11-04 09:07:58 -0800212 } else if(b.getKey().equals(collectionOfProvidersOfEntryOfProvider)) {
213 assertTrue(contains);
214 collectionOfProvidersOfEntryOfProviderMatch = true;
sberlin4e114572011-06-28 23:50:03 +0000215 } else if (contains) {
Sam Berlinc34e0182014-08-06 11:56:26 -0400216 if (b instanceof ProviderInstanceBinding) {
217 ProviderInstanceBinding<?> pib = (ProviderInstanceBinding<?>)b;
218 if (pib.getUserSuppliedProvider() instanceof ProviderMapEntry) {
Sam Berline5abfb22014-08-06 15:54:54 -0400219 // weird casting required to workaround compilation issues with jdk6
220 ProviderMapEntry<?, ?> pme =
221 (ProviderMapEntry<?, ?>) (Provider) pib.getUserSuppliedProvider();
Sam Berlinc34e0182014-08-06 11:56:26 -0400222 Binding<?> valueBinding = injector.getBinding(pme.getValueKey());
223 if (indexer.isIndexable(valueBinding)
224 && !indexedEntries.put(pme.getKey(), valueBinding.acceptTargetVisitor(indexer))) {
225 duplicates++;
226 }
227 }
228 }
sberlin4e114572011-06-28 23:50:03 +0000229 otherMatches.add(b);
230 }
231 }
232
233 int sizeOfOther = otherMatches.size();
234 if(allowDuplicates) {
235 sizeOfOther--; // account for 1 duplicate binding
236 }
flan615fd2b2014-11-04 09:07:58 -0800237 // Multiply by two because each has a value and Map.Entry.
238 int expectedSize = 2 * (mapResults.size() + duplicates);
239 assertEquals("Incorrect other matches: " + otherMatches, expectedSize, sizeOfOther);
sberlin4e114572011-06-28 23:50:03 +0000240 assertTrue(entrySetMatch);
241 assertTrue(mapProviderMatch);
Sam Berlin3338a482013-12-06 17:07:34 -0500242 assertTrue(mapJavaxProviderMatch);
flan615fd2b2014-11-04 09:07:58 -0800243 assertTrue(collectionOfProvidersOfEntryOfProviderMatch);
sberlin4e114572011-06-28 23:50:03 +0000244 assertEquals(allowDuplicates, mapSetMatch);
245 assertEquals(allowDuplicates, mapSetProviderMatch);
246 assertEquals("other MapBindings found: " + otherMapBindings, expectedMapBindings,
247 otherMapBindings.size());
248 }
249
sberlin4e114572011-06-28 23:50:03 +0000250 @SuppressWarnings("unchecked")
251 private static <T> void mapModuleTest(Key<T> mapKey, TypeLiteral<?> keyType,
252 TypeLiteral<?> valueType, Iterable<? extends Module> modules, boolean allowDuplicates,
253 int expectedMapBindings, MapResult... results) {
Christian Edward Gruber6c69bcf2013-09-18 10:55:21 -0700254 Set<Element> elements = ImmutableSet.copyOf(Elements.getElements(modules));
sberlin4e114572011-06-28 23:50:03 +0000255 Visitor<T> visitor = new Visitor<T>();
256 MapBinderBinding<T> mapbinder = null;
Sam Berlinc34e0182014-08-06 11:56:26 -0400257 Map<Key<?>, Binding<?>> keyMap = Maps.newHashMap();
sberlin4e114572011-06-28 23:50:03 +0000258 for(Element element : elements) {
Sam Berlinc34e0182014-08-06 11:56:26 -0400259 if(element instanceof Binding) {
260 Binding<?> binding = (Binding<?>)element;
261 keyMap.put(binding.getKey(), binding);
262 if (binding.getKey().equals(mapKey)) {
263 mapbinder = (MapBinderBinding<T>)((Binding<T>)binding).acceptTargetVisitor(visitor);
264 }
sberlin4e114572011-06-28 23:50:03 +0000265 }
266 }
267 assertNotNull(mapbinder);
268
269 assertEquals(keyType, mapbinder.getKeyTypeLiteral());
270 assertEquals(valueType, mapbinder.getValueTypeLiteral());
271 List<MapResult> mapResults = Lists.newArrayList(results);
272
Sam Berlin3338a482013-12-06 17:07:34 -0500273 Key<?> mapOfProvider = mapKey.ofType(mapOfProviderOf(keyType, valueType));
flan615fd2b2014-11-04 09:07:58 -0800274 Key<?> mapOfJavaxProvider = mapKey.ofType(mapOfJavaxProviderOf(keyType, valueType));
Sam Berlin3338a482013-12-06 17:07:34 -0500275 Key<?> mapOfSetOfProvider = mapKey.ofType(mapOfSetOfProviderOf(keyType, valueType));
276 Key<?> mapOfSet = mapKey.ofType(mapOf(keyType, setOf(valueType)));
277 Key<?> setOfEntry = mapKey.ofType(setOf(entryOfProviderOf(keyType, valueType)));
Sam Berlin4faa20e2014-08-11 13:24:22 -0400278 Key<?> collectionOfProvidersOfEntry =
279 mapKey.ofType(collectionOfProvidersOf(entryOfProviderOf(keyType, valueType)));
sberlin4e114572011-06-28 23:50:03 +0000280 boolean entrySetMatch = false;
Sam Berlin4faa20e2014-08-11 13:24:22 -0400281 boolean entryProviderCollectionMatch = false;
sberlin4e114572011-06-28 23:50:03 +0000282 boolean mapProviderMatch = false;
flan615fd2b2014-11-04 09:07:58 -0800283 boolean mapJavaxProviderMatch = false;
284 boolean mapSetMatch = false;
sberlin4e114572011-06-28 23:50:03 +0000285 boolean mapSetProviderMatch = false;
286 List<Object> otherMapBindings = Lists.newArrayList();
287 List<Element> otherMatches = Lists.newArrayList();
288 List<Element> otherElements = Lists.newArrayList();
Sam Berlinc34e0182014-08-06 11:56:26 -0400289 Indexer indexer = new Indexer(null);
290 Multimap<Object, IndexedBinding> indexedEntries =
291 MultimapBuilder.hashKeys().hashSetValues().build();
292 int duplicates = 0;
sberlin4e114572011-06-28 23:50:03 +0000293 for(Element element : elements) {
294 boolean contains = mapbinder.containsElement(element);
295 if(!contains) {
296 otherElements.add(element);
297 }
298 boolean matched = false;
299 Key key = null;
300 Binding b = null;
301 if(element instanceof Binding) {
302 b = (Binding)element;
Sam Berlinc34e0182014-08-06 11:56:26 -0400303 if (b instanceof ProviderInstanceBinding) {
304 ProviderInstanceBinding<?> pb = (ProviderInstanceBinding<?>) b;
305 if (pb.getUserSuppliedProvider() instanceof ProviderMapEntry) {
Sam Berline5abfb22014-08-06 15:54:54 -0400306 // weird casting required to workaround jdk6 compilation problems
307 ProviderMapEntry<?, ?> pme =
308 (ProviderMapEntry<?, ?>) (Provider) pb.getUserSuppliedProvider();
Sam Berlinc34e0182014-08-06 11:56:26 -0400309 Binding<?> valueBinding = keyMap.get(pme.getValueKey());
310 if (indexer.isIndexable(valueBinding)
311 && !indexedEntries.put(pme.getKey(), valueBinding.acceptTargetVisitor(indexer))) {
312 duplicates++;
313 }
314 }
315 }
316
sberlin4e114572011-06-28 23:50:03 +0000317 key = b.getKey();
318 Object visited = b.acceptTargetVisitor(visitor);
319 if(visited instanceof MapBinderBinding) {
320 matched = true;
321 if(visited.equals(mapbinder)) {
322 assertTrue(contains);
323 } else {
324 otherMapBindings.add(visited);
325 }
326 }
327 } else if(element instanceof ProviderLookup) {
328 key = ((ProviderLookup)element).getKey();
329 }
330
331 if(!matched && key != null) {
332 if(key.equals(mapOfProvider)) {
333 matched = true;
334 assertTrue(contains);
335 mapProviderMatch = true;
flan615fd2b2014-11-04 09:07:58 -0800336 } else if(key.equals(mapOfJavaxProvider)) {
337 matched = true;
338 assertTrue(contains);
339 mapJavaxProviderMatch = true;
sberlin4e114572011-06-28 23:50:03 +0000340 } else if(key.equals(mapOfSet)) {
341 matched = true;
342 assertTrue(contains);
343 mapSetMatch = true;
344 } else if(key.equals(mapOfSetOfProvider)) {
345 matched = true;
346 assertTrue(contains);
347 mapSetProviderMatch = true;
348 } else if(key.equals(setOfEntry)) {
349 matched = true;
350 assertTrue(contains);
351 entrySetMatch = true;
352 // Validate that this binding is also a MultibinderBinding.
353 if(b != null) {
354 assertTrue(b.acceptTargetVisitor(visitor) instanceof MultibinderBinding);
355 }
Sam Berlin4faa20e2014-08-11 13:24:22 -0400356 } else if(key.equals(collectionOfProvidersOfEntry)) {
357 matched = true;
358 assertTrue(contains);
359 entryProviderCollectionMatch = true;
sberlin4e114572011-06-28 23:50:03 +0000360 }
361 }
362
Sam Berlin4faa20e2014-08-11 13:24:22 -0400363 if (!matched && contains) {
sberlin4e114572011-06-28 23:50:03 +0000364 otherMatches.add(element);
365 }
366 }
367
368 int otherMatchesSize = otherMatches.size();
Sam Berlin4faa20e2014-08-11 13:24:22 -0400369 if (allowDuplicates) {
sberlin4e114572011-06-28 23:50:03 +0000370 otherMatchesSize--; // allow for 1 duplicate binding
371 }
flan615fd2b2014-11-04 09:07:58 -0800372 // Multiply by 3 because each has a value, ProviderLookup, and Map.Entry
373 int expectedSize = (mapResults.size() + duplicates) * 3;
374 assertEquals("incorrect number of contains, leftover matches: " + otherMatches,
375 expectedSize, otherMatchesSize);
sberlin4e114572011-06-28 23:50:03 +0000376
377 assertTrue(entrySetMatch);
Sam Berlin4faa20e2014-08-11 13:24:22 -0400378 assertTrue(entryProviderCollectionMatch);
sberlin4e114572011-06-28 23:50:03 +0000379 assertTrue(mapProviderMatch);
flan615fd2b2014-11-04 09:07:58 -0800380 assertTrue(mapJavaxProviderMatch);
sberlin4e114572011-06-28 23:50:03 +0000381 assertEquals(allowDuplicates, mapSetMatch);
382 assertEquals(allowDuplicates, mapSetProviderMatch);
383 assertEquals("other MapBindings found: " + otherMapBindings, expectedMapBindings,
384 otherMapBindings.size());
385
386 // Validate that we can construct an injector out of the remaining bindings.
387 Guice.createInjector(Elements.getModule(otherElements));
388 }
389
390 /**
391 * Asserts that MultibinderBinding visitors work correctly.
392 *
393 * @param <T> The type of the binding
394 * @param setKey The key the set belongs to.
395 * @param elementType the TypeLiteral of the element
396 * @param modules The modules that define the multibindings
397 * @param visitType The kind of test we should perform. A live Injector, a raw Elements (Module) test, or both.
398 * @param allowDuplicates If duplicates are allowed.
399 * @param expectedMultibindings The number of other multibinders we expect to see.
400 * @param results The kind of bindings contained in the multibinder.
401 */
flan9c8b6182014-11-04 07:16:41 -0800402 static <T> void assertSetVisitor(Key<Set<T>> setKey, TypeLiteral<?> elementType,
sberlin4e114572011-06-28 23:50:03 +0000403 Iterable<? extends Module> modules, VisitType visitType, boolean allowDuplicates,
404 int expectedMultibindings, BindResult... results) {
405 if(visitType == null) {
406 fail("must test something");
407 }
408
409 if(visitType == BOTH || visitType == INJECTOR) {
flan9c8b6182014-11-04 07:16:41 -0800410 setInjectorTest(setKey, elementType, modules, allowDuplicates,
Sam Berlin4faa20e2014-08-11 13:24:22 -0400411 expectedMultibindings, results);
sberlin4e114572011-06-28 23:50:03 +0000412 }
413
414 if(visitType == BOTH || visitType == MODULE) {
flan9c8b6182014-11-04 07:16:41 -0800415 setModuleTest(setKey, elementType, modules, allowDuplicates,
Sam Berlin4faa20e2014-08-11 13:24:22 -0400416 expectedMultibindings, results);
sberlin4e114572011-06-28 23:50:03 +0000417 }
418 }
419
420 @SuppressWarnings("unchecked")
flan9c8b6182014-11-04 07:16:41 -0800421 private static <T> void setInjectorTest(Key<Set<T>> setKey, TypeLiteral<?> elementType,
sberlin4e114572011-06-28 23:50:03 +0000422 Iterable<? extends Module> modules, boolean allowDuplicates, int otherMultibindings,
423 BindResult... results) {
flan9c8b6182014-11-04 07:16:41 -0800424 Key<?> collectionOfProvidersKey = setKey.ofType(collectionOfProvidersOf(elementType));
sberlin4e114572011-06-28 23:50:03 +0000425 Injector injector = Guice.createInjector(modules);
Sam Berlin4faa20e2014-08-11 13:24:22 -0400426 Visitor<Set<T>> visitor = new Visitor<Set<T>>();
427 Binding<Set<T>> binding = injector.getBinding(setKey);
428 MultibinderBinding<Set<T>> multibinder =
429 (MultibinderBinding<Set<T>>)binding.acceptTargetVisitor(visitor);
sberlin4e114572011-06-28 23:50:03 +0000430 assertNotNull(multibinder);
431 assertEquals(elementType, multibinder.getElementTypeLiteral());
432 assertEquals(allowDuplicates, multibinder.permitsDuplicates());
433 List<Binding<?>> elements = Lists.newArrayList(multibinder.getElements());
434 List<BindResult> bindResults = Lists.newArrayList(results);
Sam Berlinc34e0182014-08-06 11:56:26 -0400435 assertEquals("wrong bind elements, expected: " + bindResults
436 + ", but was: " + multibinder.getElements(),
sberlin4e114572011-06-28 23:50:03 +0000437 bindResults.size(), elements.size());
Sam Berlin4faa20e2014-08-11 13:24:22 -0400438
sberlin4e114572011-06-28 23:50:03 +0000439 for(BindResult result : bindResults) {
440 Binding found = null;
441 for(Binding item : elements) {
Christian Edward Gruberaf24f632014-04-01 15:07:55 -0700442 if (matches(item, result)) {
443 found = item;
sberlin4e114572011-06-28 23:50:03 +0000444 break;
445 }
446 }
447 if(found == null) {
448 fail("Could not find element: " + result + " in remaining elements: " + elements);
449 } else {
450 elements.remove(found);
451 }
452 }
453
454 if(!elements.isEmpty()) {
455 fail("Found all elements of: " + bindResults + ", but more were left over: " + elements);
456 }
Sam Berlinc34e0182014-08-06 11:56:26 -0400457
sberlin4e114572011-06-28 23:50:03 +0000458 Set<Binding> setOfElements = new HashSet<Binding>(multibinder.getElements());
Sam Berlinc34e0182014-08-06 11:56:26 -0400459 Set<IndexedBinding> setOfIndexed = Sets.newHashSet();
460 Indexer indexer = new Indexer(injector);
461 for (Binding<?> oneBinding : setOfElements) {
462 setOfIndexed.add(oneBinding.acceptTargetVisitor(indexer));
463 }
464
sberlin4e114572011-06-28 23:50:03 +0000465 List<Object> otherMultibinders = Lists.newArrayList();
466 List<Binding> otherContains = Lists.newArrayList();
Sam Berlin4faa20e2014-08-11 13:24:22 -0400467 boolean collectionOfProvidersMatch = false;
sberlin4e114572011-06-28 23:50:03 +0000468 for(Binding b : injector.getAllBindings().values()) {
469 boolean contains = multibinder.containsElement(b);
Sam Berlin4faa20e2014-08-11 13:24:22 -0400470 Key key = b.getKey();
sberlin4e114572011-06-28 23:50:03 +0000471 Object visited = b.acceptTargetVisitor(visitor);
472 if(visited != null) {
473 if(visited.equals(multibinder)) {
474 assertTrue(contains);
475 } else {
476 otherMultibinders.add(visited);
477 }
478 } else if(setOfElements.contains(b)) {
479 assertTrue(contains);
Sam Berlin4faa20e2014-08-11 13:24:22 -0400480 } else if (key.equals(collectionOfProvidersKey)) {
481 assertTrue(contains);
482 collectionOfProvidersMatch = true;
483 } else if (contains) {
Sam Berlinc34e0182014-08-06 11:56:26 -0400484 if (!indexer.isIndexable(b) || !setOfIndexed.contains(b.acceptTargetVisitor(indexer))) {
485 otherContains.add(b);
486 }
sberlin4e114572011-06-28 23:50:03 +0000487 }
488 }
Sam Berlin4faa20e2014-08-11 13:24:22 -0400489
490 assertTrue(collectionOfProvidersMatch);
491
sberlin4e114572011-06-28 23:50:03 +0000492 if(allowDuplicates) {
493 assertEquals("contained more than it should: " + otherContains, 1, otherContains.size());
494 } else {
495 assertTrue("contained more than it should: " + otherContains, otherContains.isEmpty());
496 }
497 assertEquals("other multibindings found: " + otherMultibinders, otherMultibindings,
498 otherMultibinders.size());
499
500 }
501
502 @SuppressWarnings("unchecked")
flan9c8b6182014-11-04 07:16:41 -0800503 private static <T> void setModuleTest(Key<Set<T>> setKey, TypeLiteral<?> elementType,
sberlin4e114572011-06-28 23:50:03 +0000504 Iterable<? extends Module> modules, boolean allowDuplicates, int otherMultibindings,
505 BindResult... results) {
flan9c8b6182014-11-04 07:16:41 -0800506 Key<?> collectionOfProvidersKey = setKey.ofType(collectionOfProvidersOf(elementType));
sberlin4e114572011-06-28 23:50:03 +0000507 List<BindResult> bindResults = Lists.newArrayList(results);
508 List<Element> elements = Elements.getElements(modules);
509 Visitor<T> visitor = new Visitor<T>();
Sam Berlin4faa20e2014-08-11 13:24:22 -0400510 MultibinderBinding<Set<T>> multibinder = null;
sberlin4e114572011-06-28 23:50:03 +0000511 for(Element element : elements) {
512 if(element instanceof Binding && ((Binding)element).getKey().equals(setKey)) {
Sam Berlin4faa20e2014-08-11 13:24:22 -0400513 multibinder = (MultibinderBinding<Set<T>>)((Binding)element).acceptTargetVisitor(visitor);
sberlin4e114572011-06-28 23:50:03 +0000514 break;
515 }
516 }
517 assertNotNull(multibinder);
518
519 assertEquals(elementType, multibinder.getElementTypeLiteral());
520 List<Object> otherMultibinders = Lists.newArrayList();
521 Set<Element> otherContains = new HashSet<Element>();
522 List<Element> otherElements = Lists.newArrayList();
Sam Berlinc34e0182014-08-06 11:56:26 -0400523 int duplicates = 0;
524 Set<IndexedBinding> setOfIndexed = Sets.newHashSet();
525 Indexer indexer = new Indexer(null);
Sam Berlin4faa20e2014-08-11 13:24:22 -0400526 boolean collectionOfProvidersMatch = false;
sberlin4e114572011-06-28 23:50:03 +0000527 for(Element element : elements) {
528 boolean contains = multibinder.containsElement(element);
529 if(!contains) {
530 otherElements.add(element);
531 }
532 boolean matched = false;
Sam Berlin4faa20e2014-08-11 13:24:22 -0400533 Key key = null;
sberlin4e114572011-06-28 23:50:03 +0000534 if(element instanceof Binding) {
535 Binding binding = (Binding)element;
Sam Berlinc34e0182014-08-06 11:56:26 -0400536 if (indexer.isIndexable(binding)
537 && !setOfIndexed.add((IndexedBinding) binding.acceptTargetVisitor(indexer))) {
538 duplicates++;
539 }
Sam Berlin4faa20e2014-08-11 13:24:22 -0400540 key = binding.getKey();
sberlin4e114572011-06-28 23:50:03 +0000541 Object visited = binding.acceptTargetVisitor(visitor);
542 if(visited != null) {
543 matched = true;
544 if(visited.equals(multibinder)) {
545 assertTrue(contains);
546 } else {
547 otherMultibinders.add(visited);
548 }
549 }
550 }
Sam Berlin4faa20e2014-08-11 13:24:22 -0400551
552 if (collectionOfProvidersKey.equals(key)) {
553 assertTrue(contains);
554 assertFalse(matched);
555 collectionOfProvidersMatch = true;
556 } else if (!matched && contains) {
sberlin4e114572011-06-28 23:50:03 +0000557 otherContains.add(element);
558 }
559 }
Sam Berlin4faa20e2014-08-11 13:24:22 -0400560
sberlin4e114572011-06-28 23:50:03 +0000561 if(allowDuplicates) {
Sam Berlinc34e0182014-08-06 11:56:26 -0400562 assertEquals("wrong contained elements: " + otherContains,
563 bindResults.size() + 1 + duplicates, otherContains.size());
sberlin4e114572011-06-28 23:50:03 +0000564 } else {
Sam Berlinc34e0182014-08-06 11:56:26 -0400565 assertEquals("wrong contained elements: " + otherContains,
566 bindResults.size() + duplicates, otherContains.size());
sberlin4e114572011-06-28 23:50:03 +0000567 }
Sam Berlin4faa20e2014-08-11 13:24:22 -0400568
sberlin4e114572011-06-28 23:50:03 +0000569 assertEquals("other multibindings found: " + otherMultibinders, otherMultibindings,
570 otherMultibinders.size());
Sam Berlin4faa20e2014-08-11 13:24:22 -0400571 assertTrue(collectionOfProvidersMatch);
572
sberlin4e114572011-06-28 23:50:03 +0000573 // Validate that we can construct an injector out of the remaining bindings.
574 Guice.createInjector(Elements.getModule(otherElements));
575 }
Christian Edward Gruberaf24f632014-04-01 15:07:55 -0700576
577 /**
578 * Asserts that OptionalBinderBinding visitors for work correctly.
579 *
580 * @param <T> The type of the binding
581 * @param keyType The key OptionalBinder is binding
582 * @param modules The modules that define the bindings
583 * @param visitType The kind of test we should perform. A live Injector, a raw Elements (Module)
584 * test, or both.
585 * @param expectedOtherOptionalBindings the # of other optional bindings we expect to see.
586 * @param expectedDefault the expected default binding, or null if none
587 * @param expectedActual the expected actual binding, or null if none
Sam Berlin842f3512014-07-09 20:30:23 -0400588 * @param expectedUserLinkedActual the user binding that is the actual binding, used if
589 * neither the default nor actual are set and a user binding existed for the type.
Christian Edward Gruberaf24f632014-04-01 15:07:55 -0700590 */
591 static <T> void assertOptionalVisitor(Key<T> keyType,
592 Iterable<? extends Module> modules,
593 VisitType visitType,
594 int expectedOtherOptionalBindings,
595 BindResult<?> expectedDefault,
Sam Berlin842f3512014-07-09 20:30:23 -0400596 BindResult<?> expectedActual,
597 BindResult<?> expectedUserLinkedActual) {
Christian Edward Gruberaf24f632014-04-01 15:07:55 -0700598 if (visitType == null) {
599 fail("must test something");
600 }
601
sameb1d3f8cd2014-10-07 15:32:12 -0700602 // if java.util.Optional is bound, there'll be twice as many as we expect.
603 if (HAS_JAVA_OPTIONAL) {
604 expectedOtherOptionalBindings *= 2;
605 }
606
Christian Edward Gruberaf24f632014-04-01 15:07:55 -0700607 if (visitType == BOTH || visitType == INJECTOR) {
608 optionalInjectorTest(keyType, modules, expectedOtherOptionalBindings, expectedDefault,
Sam Berlin842f3512014-07-09 20:30:23 -0400609 expectedActual, expectedUserLinkedActual);
Christian Edward Gruberaf24f632014-04-01 15:07:55 -0700610 }
611
612 if (visitType == BOTH || visitType == MODULE) {
613 optionalModuleTest(keyType, modules, expectedOtherOptionalBindings, expectedDefault,
Sam Berlin842f3512014-07-09 20:30:23 -0400614 expectedActual, expectedUserLinkedActual);
Christian Edward Gruberaf24f632014-04-01 15:07:55 -0700615 }
616 }
617
sameb1d3f8cd2014-10-07 15:32:12 -0700618 @SuppressWarnings({ "unchecked", "rawtypes" })
Sam Berlin842f3512014-07-09 20:30:23 -0400619 private static <T> void optionalInjectorTest(Key<T> keyType,
620 Iterable<? extends Module> modules,
621 int expectedOtherOptionalBindings,
622 BindResult<?> expectedDefault,
623 BindResult<?> expectedActual,
624 BindResult<?> expectedUserLinkedActual) {
625 if (expectedUserLinkedActual != null) {
626 assertNull("cannot have actual if expecting user binding", expectedActual);
627 assertNull("cannot have default if expecting user binding", expectedDefault);
628 }
sameb1d3f8cd2014-10-07 15:32:12 -0700629
Christian Edward Gruberaf24f632014-04-01 15:07:55 -0700630 Key<Optional<T>> optionalKey =
631 keyType.ofType(OptionalBinder.optionalOf(keyType.getTypeLiteral()));
sameb1d3f8cd2014-10-07 15:32:12 -0700632 Key<?> javaOptionalKey = HAS_JAVA_OPTIONAL ?
633 keyType.ofType(OptionalBinder.javaOptionalOf(keyType.getTypeLiteral())) : null;
Christian Edward Gruberaf24f632014-04-01 15:07:55 -0700634 Injector injector = Guice.createInjector(modules);
635 Binding<Optional<T>> optionalBinding = injector.getBinding(optionalKey);
sameb1d3f8cd2014-10-07 15:32:12 -0700636 Visitor visitor = new Visitor();
637 OptionalBinderBinding<Optional<T>> optionalBinder =
638 (OptionalBinderBinding<Optional<T>>) optionalBinding.acceptTargetVisitor(visitor);
Christian Edward Gruberaf24f632014-04-01 15:07:55 -0700639 assertNotNull(optionalBinder);
640 assertEquals(optionalKey, optionalBinder.getKey());
641
sameb1d3f8cd2014-10-07 15:32:12 -0700642 Binding<?> javaOptionalBinding = null;
643 OptionalBinderBinding<?> javaOptionalBinder = null;
644 if (HAS_JAVA_OPTIONAL) {
645 javaOptionalBinding = injector.getBinding(javaOptionalKey);
646 javaOptionalBinder = (OptionalBinderBinding<?>) javaOptionalBinding.acceptTargetVisitor(visitor);
647 assertNotNull(javaOptionalBinder);
648 assertEquals(javaOptionalKey, javaOptionalBinder.getKey());
649 }
650
Christian Edward Gruberaf24f632014-04-01 15:07:55 -0700651 if (expectedDefault == null) {
652 assertNull("did not expect a default binding", optionalBinder.getDefaultBinding());
sameb1d3f8cd2014-10-07 15:32:12 -0700653 if (HAS_JAVA_OPTIONAL) {
654 assertNull("did not expect a default binding", javaOptionalBinder.getDefaultBinding());
655 }
Christian Edward Gruberaf24f632014-04-01 15:07:55 -0700656 } else {
657 assertTrue("expectedDefault: " + expectedDefault + ", actualDefault: "
sameb1d3f8cd2014-10-07 15:32:12 -0700658 + optionalBinder.getDefaultBinding(),
Christian Edward Gruberaf24f632014-04-01 15:07:55 -0700659 matches(optionalBinder.getDefaultBinding(), expectedDefault));
sameb1d3f8cd2014-10-07 15:32:12 -0700660 if (HAS_JAVA_OPTIONAL) {
661 assertTrue("expectedDefault: " + expectedDefault + ", actualDefault: "
662 + javaOptionalBinder.getDefaultBinding(),
663 matches(javaOptionalBinder.getDefaultBinding(), expectedDefault));
664 }
Christian Edward Gruberaf24f632014-04-01 15:07:55 -0700665 }
666
Sam Berlin842f3512014-07-09 20:30:23 -0400667 if (expectedActual == null && expectedUserLinkedActual == null) {
Christian Edward Gruberaf24f632014-04-01 15:07:55 -0700668 assertNull(optionalBinder.getActualBinding());
sameb1d3f8cd2014-10-07 15:32:12 -0700669 if (HAS_JAVA_OPTIONAL) {
670 assertNull(javaOptionalBinder.getActualBinding());
671 }
Sam Berlin842f3512014-07-09 20:30:23 -0400672 } else if (expectedActual != null) {
Christian Edward Gruberaf24f632014-04-01 15:07:55 -0700673 assertTrue("expectedActual: " + expectedActual + ", actualActual: "
sameb1d3f8cd2014-10-07 15:32:12 -0700674 + optionalBinder.getActualBinding(),
Christian Edward Gruberaf24f632014-04-01 15:07:55 -0700675 matches(optionalBinder.getActualBinding(), expectedActual));
sameb1d3f8cd2014-10-07 15:32:12 -0700676 if (HAS_JAVA_OPTIONAL) {
677 assertTrue("expectedActual: " + expectedActual + ", actualActual: "
678 + javaOptionalBinder.getActualBinding(),
679 matches(javaOptionalBinder.getActualBinding(), expectedActual));
680 }
Sam Berlin842f3512014-07-09 20:30:23 -0400681 } else if (expectedUserLinkedActual != null) {
682 assertTrue("expectedUserLinkedActual: " + expectedUserLinkedActual + ", actualActual: "
sameb1d3f8cd2014-10-07 15:32:12 -0700683 + optionalBinder.getActualBinding(),
Sam Berlin842f3512014-07-09 20:30:23 -0400684 matches(optionalBinder.getActualBinding(), expectedUserLinkedActual));
sameb1d3f8cd2014-10-07 15:32:12 -0700685 if (HAS_JAVA_OPTIONAL) {
686 assertTrue("expectedUserLinkedActual: " + expectedUserLinkedActual + ", actualActual: "
687 + javaOptionalBinder.getActualBinding(),
688 matches(javaOptionalBinder.getActualBinding(), expectedUserLinkedActual));
689 }
Christian Edward Gruberaf24f632014-04-01 15:07:55 -0700690 }
691
692
693 Key<Optional<javax.inject.Provider<T>>> optionalJavaxProviderKey =
694 keyType.ofType(optionalOfJavaxProvider(keyType.getTypeLiteral()));
sameb1d3f8cd2014-10-07 15:32:12 -0700695 Key<?> javaOptionalJavaxProviderKey = HAS_JAVA_OPTIONAL ?
696 keyType.ofType(javaOptionalOfJavaxProvider(keyType.getTypeLiteral())) : null;
Christian Edward Gruberaf24f632014-04-01 15:07:55 -0700697 Key<Optional<Provider<T>>> optionalProviderKey =
698 keyType.ofType(optionalOfProvider(keyType.getTypeLiteral()));
sameb1d3f8cd2014-10-07 15:32:12 -0700699 Key<?> javaOptionalProviderKey = HAS_JAVA_OPTIONAL ?
700 keyType.ofType(javaOptionalOfProvider(keyType.getTypeLiteral())) : null;
Christian Edward Gruberaf24f632014-04-01 15:07:55 -0700701
702 boolean keyMatch = false;
703 boolean optionalKeyMatch = false;
sameb1d3f8cd2014-10-07 15:32:12 -0700704 boolean javaOptionalKeyMatch = false;
Christian Edward Gruberaf24f632014-04-01 15:07:55 -0700705 boolean optionalJavaxProviderKeyMatch = false;
sameb1d3f8cd2014-10-07 15:32:12 -0700706 boolean javaOptionalJavaxProviderKeyMatch = false;
Christian Edward Gruberaf24f632014-04-01 15:07:55 -0700707 boolean optionalProviderKeyMatch = false;
sameb1d3f8cd2014-10-07 15:32:12 -0700708 boolean javaOptionalProviderKeyMatch = false;
Christian Edward Gruberaf24f632014-04-01 15:07:55 -0700709 boolean defaultMatch = false;
710 boolean actualMatch = false;
711 List<Object> otherOptionalBindings = Lists.newArrayList();
712 List<Binding> otherMatches = Lists.newArrayList();
713 for (Binding b : injector.getAllBindings().values()) {
714 boolean contains = optionalBinder.containsElement(b);
sameb1d3f8cd2014-10-07 15:32:12 -0700715 if (HAS_JAVA_OPTIONAL) {
716 assertEquals(contains, javaOptionalBinder.containsElement(b));
717 }
Christian Edward Gruberaf24f632014-04-01 15:07:55 -0700718 Object visited = b.acceptTargetVisitor(visitor);
719 if (visited instanceof OptionalBinderBinding) {
720 if (visited.equals(optionalBinder)) {
721 assertTrue(contains);
sameb1d3f8cd2014-10-07 15:32:12 -0700722 } else if (HAS_JAVA_OPTIONAL && visited.equals(javaOptionalBinder)) {
723 assertTrue(contains);
Christian Edward Gruberaf24f632014-04-01 15:07:55 -0700724 } else {
725 otherOptionalBindings.add(visited);
726 }
727 }
728 if (b.getKey().equals(keyType)) {
729 // keyType might match because a user bound it
730 // (which is possible in a purely absent OptionalBinder)
731 assertEquals(expectedDefault != null || expectedActual != null, contains);
732 if (contains) {
733 keyMatch = true;
734 }
735 } else if (b.getKey().equals(optionalKey)) {
736 assertTrue(contains);
737 optionalKeyMatch = true;
sameb1d3f8cd2014-10-07 15:32:12 -0700738 } else if (b.getKey().equals(javaOptionalKey)) {
739 assertTrue(contains);
740 javaOptionalKeyMatch = true;
Christian Edward Gruberaf24f632014-04-01 15:07:55 -0700741 } else if (b.getKey().equals(optionalJavaxProviderKey)) {
742 assertTrue(contains);
743 optionalJavaxProviderKeyMatch = true;
sameb1d3f8cd2014-10-07 15:32:12 -0700744 } else if (b.getKey().equals(javaOptionalJavaxProviderKey)) {
745 assertTrue(contains);
746 javaOptionalJavaxProviderKeyMatch = true;
Christian Edward Gruberaf24f632014-04-01 15:07:55 -0700747 } else if (b.getKey().equals(optionalProviderKey)) {
748 assertTrue(contains);
749 optionalProviderKeyMatch = true;
sameb1d3f8cd2014-10-07 15:32:12 -0700750 } else if (b.getKey().equals(javaOptionalProviderKey)) {
751 assertTrue(contains);
752 javaOptionalProviderKeyMatch = true;
Christian Edward Gruberaf24f632014-04-01 15:07:55 -0700753 } else if (expectedDefault != null && matches(b, expectedDefault)) {
754 assertTrue(contains);
755 defaultMatch = true;
756 } else if (expectedActual != null && matches(b, expectedActual)) {
757 assertTrue(contains);
758 actualMatch = true;
Christian Edward Gruberaf24f632014-04-01 15:07:55 -0700759 } else if (contains) {
760 otherMatches.add(b);
761 }
762 }
763
764 assertEquals(otherMatches.toString(), 0, otherMatches.size());
765 // only expect a keymatch if either default or actual are set
766 assertEquals(expectedDefault != null || expectedActual != null, keyMatch);
767 assertTrue(optionalKeyMatch);
768 assertTrue(optionalJavaxProviderKeyMatch);
769 assertTrue(optionalProviderKeyMatch);
sameb1d3f8cd2014-10-07 15:32:12 -0700770 assertEquals(HAS_JAVA_OPTIONAL, javaOptionalKeyMatch);
771 assertEquals(HAS_JAVA_OPTIONAL, javaOptionalJavaxProviderKeyMatch);
772 assertEquals(HAS_JAVA_OPTIONAL, javaOptionalProviderKeyMatch);
Christian Edward Gruberaf24f632014-04-01 15:07:55 -0700773 assertEquals(expectedDefault != null, defaultMatch);
774 assertEquals(expectedActual != null, actualMatch);
775 assertEquals("other OptionalBindings found: " + otherOptionalBindings,
776 expectedOtherOptionalBindings, otherOptionalBindings.size());
777 }
778
sameb1d3f8cd2014-10-07 15:32:12 -0700779 @SuppressWarnings({ "unchecked", "rawtypes" })
Sam Berlin842f3512014-07-09 20:30:23 -0400780 private static <T> void optionalModuleTest(Key<T> keyType,
781 Iterable<? extends Module> modules,
782 int expectedOtherOptionalBindings,
783 BindResult<?> expectedDefault,
784 BindResult<?> expectedActual,
785 BindResult<?> expectedUserLinkedActual) {
786 if (expectedUserLinkedActual != null) {
787 assertNull("cannot have actual if expecting user binding", expectedActual);
788 assertNull("cannot have default if expecting user binding", expectedDefault);
789 }
Christian Edward Gruberaf24f632014-04-01 15:07:55 -0700790 Set<Element> elements = ImmutableSet.copyOf(Elements.getElements(modules));
791 Map<Key<?>, Binding<?>> indexed = index(elements);
792 Key<Optional<T>> optionalKey =
793 keyType.ofType(OptionalBinder.optionalOf(keyType.getTypeLiteral()));
sameb1d3f8cd2014-10-07 15:32:12 -0700794 Key<?> javaOptionalKey = HAS_JAVA_OPTIONAL ?
795 keyType.ofType(OptionalBinder.javaOptionalOf(keyType.getTypeLiteral())) : null;
796 Visitor visitor = new Visitor();
797 OptionalBinderBinding<Optional<T>> optionalBinder = null;
798 OptionalBinderBinding<?> javaOptionalBinder = null;
Christian Edward Gruberaf24f632014-04-01 15:07:55 -0700799 Key<?> defaultKey = null;
800 Key<?> actualKey = null;
801
802 Binding optionalBinding = indexed.get(optionalKey);
sameb1d3f8cd2014-10-07 15:32:12 -0700803 optionalBinder =
804 (OptionalBinderBinding<Optional<T>>) optionalBinding.acceptTargetVisitor(visitor);
805
806 if (HAS_JAVA_OPTIONAL) {
807 Binding javaOptionalBinding = indexed.get(javaOptionalKey);
808 javaOptionalBinder = (OptionalBinderBinding) javaOptionalBinding.acceptTargetVisitor(visitor);
809 }
Christian Edward Gruberaf24f632014-04-01 15:07:55 -0700810
811 // Locate the defaultKey & actualKey
812 for (Element element : elements) {
813 if (optionalBinder.containsElement(element) && element instanceof Binding) {
814 Binding binding = (Binding) element;
815 if (isSourceEntry(binding, Source.DEFAULT)) {
Sam Berlinc66f08e2014-07-09 20:29:11 -0400816 defaultKey = binding.getKey();
Christian Edward Gruberaf24f632014-04-01 15:07:55 -0700817 } else if (isSourceEntry(binding, Source.ACTUAL)) {
Sam Berlinc66f08e2014-07-09 20:29:11 -0400818 actualKey = binding.getKey();
Christian Edward Gruberaf24f632014-04-01 15:07:55 -0700819 }
820 }
821 }
822 assertNotNull(optionalBinder);
sameb1d3f8cd2014-10-07 15:32:12 -0700823 if (HAS_JAVA_OPTIONAL) {
824 assertNotNull(javaOptionalBinder);
825 }
Christian Edward Gruberaf24f632014-04-01 15:07:55 -0700826 assertEquals(expectedDefault == null, defaultKey == null);
827 assertEquals(expectedActual == null, actualKey == null);
828
829 Key<Optional<javax.inject.Provider<T>>> optionalJavaxProviderKey =
830 keyType.ofType(optionalOfJavaxProvider(keyType.getTypeLiteral()));
sameb1d3f8cd2014-10-07 15:32:12 -0700831 Key<?> javaOptionalJavaxProviderKey = HAS_JAVA_OPTIONAL ?
832 keyType.ofType(javaOptionalOfJavaxProvider(keyType.getTypeLiteral())) : null;
Christian Edward Gruberaf24f632014-04-01 15:07:55 -0700833 Key<Optional<Provider<T>>> optionalProviderKey =
834 keyType.ofType(optionalOfProvider(keyType.getTypeLiteral()));
sameb1d3f8cd2014-10-07 15:32:12 -0700835 Key<?> javaOptionalProviderKey = HAS_JAVA_OPTIONAL ?
836 keyType.ofType(javaOptionalOfProvider(keyType.getTypeLiteral())) : null;
Christian Edward Gruberaf24f632014-04-01 15:07:55 -0700837 boolean keyMatch = false;
838 boolean optionalKeyMatch = false;
sameb1d3f8cd2014-10-07 15:32:12 -0700839 boolean javaOptionalKeyMatch = false;
Christian Edward Gruberaf24f632014-04-01 15:07:55 -0700840 boolean optionalJavaxProviderKeyMatch = false;
sameb1d3f8cd2014-10-07 15:32:12 -0700841 boolean javaOptionalJavaxProviderKeyMatch = false;
Christian Edward Gruberaf24f632014-04-01 15:07:55 -0700842 boolean optionalProviderKeyMatch = false;
sameb1d3f8cd2014-10-07 15:32:12 -0700843 boolean javaOptionalProviderKeyMatch = false;
Christian Edward Gruberaf24f632014-04-01 15:07:55 -0700844 boolean defaultMatch = false;
845 boolean actualMatch = false;
846 List<Object> otherOptionalElements = Lists.newArrayList();
847 List<Element> otherContains = Lists.newArrayList();
848 List<Element> nonContainedElements = Lists.newArrayList();
849 for (Element element : elements) {
850 boolean contains = optionalBinder.containsElement(element);
sameb1d3f8cd2014-10-07 15:32:12 -0700851 if (HAS_JAVA_OPTIONAL) {
852 assertEquals(contains, javaOptionalBinder.containsElement(element));
853 }
Christian Edward Gruberaf24f632014-04-01 15:07:55 -0700854 if (!contains) {
855 nonContainedElements.add(element);
856 }
857 Key key = null;
858 Binding b = null;
859 if (element instanceof Binding) {
860 b = (Binding) element;
861 key = b.getKey();
862 Object visited = b.acceptTargetVisitor(visitor);
863 if (visited instanceof OptionalBinderBinding) {
864 if (visited.equals(optionalBinder)) {
865 assertTrue(contains);
sameb1d3f8cd2014-10-07 15:32:12 -0700866 } else if (HAS_JAVA_OPTIONAL && visited.equals(javaOptionalBinder)) {
867 assertTrue(contains);
Christian Edward Gruberaf24f632014-04-01 15:07:55 -0700868 } else {
869 otherOptionalElements.add(visited);
870 }
871 }
872 } else if (element instanceof ProviderLookup) {
873 key = ((ProviderLookup) element).getKey();
874 }
875
876 if (key != null && key.equals(keyType)) {
877 // keyType might match because a user bound it
878 // (which is possible in a purely absent OptionalBinder)
879 assertEquals(expectedDefault != null || expectedActual != null, contains);
880 if (contains) {
881 keyMatch = true;
882 }
883 } else if (key != null && key.equals(optionalKey)) {
884 assertTrue(contains);
885 optionalKeyMatch = true;
sameb1d3f8cd2014-10-07 15:32:12 -0700886 } else if (key != null && key.equals(javaOptionalKey)) {
887 assertTrue(contains);
888 javaOptionalKeyMatch = true;
Christian Edward Gruberaf24f632014-04-01 15:07:55 -0700889 } else if (key != null && key.equals(optionalJavaxProviderKey)) {
890 assertTrue(contains);
891 optionalJavaxProviderKeyMatch = true;
sameb1d3f8cd2014-10-07 15:32:12 -0700892 } else if (key != null && key.equals(javaOptionalJavaxProviderKey)) {
893 assertTrue(contains);
894 javaOptionalJavaxProviderKeyMatch = true;
Christian Edward Gruberaf24f632014-04-01 15:07:55 -0700895 } else if (key != null && key.equals(optionalProviderKey)) {
896 assertTrue(contains);
897 optionalProviderKeyMatch = true;
sameb1d3f8cd2014-10-07 15:32:12 -0700898 } else if (key != null && key.equals(javaOptionalProviderKey)) {
899 assertTrue(contains);
900 javaOptionalProviderKeyMatch = true;
Christian Edward Gruberaf24f632014-04-01 15:07:55 -0700901 } else if (key != null && key.equals(defaultKey)) {
902 assertTrue(contains);
903 if (b != null) { // otherwise it might just be a ProviderLookup into it
904 assertTrue("expected: " + expectedDefault + ", but was: " + b,
905 matches(b, expectedDefault));
906 defaultMatch = true;
907 }
908 } else if (key != null && key.equals(actualKey)) {
909 assertTrue(contains);
910 if (b != null) { // otherwise it might just be a ProviderLookup into it
911 assertTrue("expected: " + expectedActual + ", but was: " + b, matches(b, expectedActual));
912 actualMatch = true;
913 }
Christian Edward Gruberaf24f632014-04-01 15:07:55 -0700914 } else if (contains) {
915 otherContains.add(element);
916 }
917 }
918
919 // only expect a keymatch if either default or actual are set
920 assertEquals(expectedDefault != null || expectedActual != null, keyMatch);
921 assertTrue(optionalKeyMatch);
922 assertTrue(optionalJavaxProviderKeyMatch);
923 assertTrue(optionalProviderKeyMatch);
sameb1d3f8cd2014-10-07 15:32:12 -0700924 assertEquals(HAS_JAVA_OPTIONAL, javaOptionalKeyMatch);
925 assertEquals(HAS_JAVA_OPTIONAL, javaOptionalJavaxProviderKeyMatch);
926 assertEquals(HAS_JAVA_OPTIONAL, javaOptionalProviderKeyMatch);
Christian Edward Gruberaf24f632014-04-01 15:07:55 -0700927 assertEquals(expectedDefault != null, defaultMatch);
928 assertEquals(expectedActual != null, actualMatch);
929 assertEquals(otherContains.toString(), 0, otherContains.size());
930 assertEquals("other OptionalBindings found: " + otherOptionalElements,
931 expectedOtherOptionalBindings, otherOptionalElements.size());
932
933 // Validate that we can construct an injector out of the remaining bindings.
934 Guice.createInjector(Elements.getModule(nonContainedElements));
935 }
Christian Edward Gruberaf24f632014-04-01 15:07:55 -0700936
937 private static boolean isSourceEntry(Binding b, Source type) {
Sam Berlinc66f08e2014-07-09 20:29:11 -0400938 switch(type) {
939 case ACTUAL:
940 return b.getKey().getAnnotation() instanceof OptionalBinder.Actual;
941 case DEFAULT:
942 return b.getKey().getAnnotation() instanceof OptionalBinder.Default;
943 default:
944 throw new IllegalStateException("invalid type: " + type);
Christian Edward Gruberaf24f632014-04-01 15:07:55 -0700945 }
Christian Edward Gruberaf24f632014-04-01 15:07:55 -0700946 }
947
948 /** Returns the subset of elements that have keys, indexed by them. */
949 private static Map<Key<?>, Binding<?>> index(Iterable<Element> elements) {
950 ImmutableMap.Builder<Key<?>, Binding<?>> builder = ImmutableMap.builder();
951 for (Element element : elements) {
952 if (element instanceof Binding) {
953 builder.put(((Binding) element).getKey(), (Binding) element);
954 }
955 }
956 return builder.build();
957 }
sberlin4e114572011-06-28 23:50:03 +0000958
959 static <K, V> MapResult instance(K k, V v) {
960 return new MapResult<K, V>(k, new BindResult<V>(INSTANCE, v, null));
961 }
962
963 static <K, V> MapResult linked(K k, Class<? extends V> clazz) {
964 return new MapResult<K, V>(k, new BindResult<V>(LINKED, null, Key.get(clazz)));
965 }
966
967 static <K, V> MapResult linked(K k, Key<? extends V> key) {
968 return new MapResult<K, V>(k, new BindResult<V>(LINKED, null, key));
969 }
970
971 static <K, V> MapResult providerInstance(K k, V v) {
972 return new MapResult<K, V>(k, new BindResult<V>(PROVIDER_INSTANCE, v, null));
973 }
974
Christian Edward Gruberbf2b16c2013-05-15 18:39:15 -0700975 static class MapResult<K, V> {
sberlin4e114572011-06-28 23:50:03 +0000976 private final K k;
977 private final BindResult<V> v;
978
979 MapResult(K k, BindResult<V> v) {
980 this.k = k;
981 this.v = v;
982 }
983
984 @Override
985 public String toString() {
986 return "entry[key[" + k + "],value[" + v + "]]";
987 }
988 }
Christian Edward Gruberaf24f632014-04-01 15:07:55 -0700989
990 private static boolean matches(Binding<?> item, BindResult<?> result) {
991 switch (result.type) {
992 case INSTANCE:
993 if (item instanceof InstanceBinding
994 && ((InstanceBinding) item).getInstance().equals(result.instance)) {
995 return true;
996 }
997 break;
998 case LINKED:
999 if (item instanceof LinkedKeyBinding
1000 && ((LinkedKeyBinding) item).getLinkedKey().equals(result.key)) {
1001 return true;
1002 }
1003 break;
1004 case PROVIDER_INSTANCE:
1005 if (item instanceof ProviderInstanceBinding
1006 && Objects.equal(((ProviderInstanceBinding) item).getUserSuppliedProvider().get(),
1007 result.instance)) {
1008 return true;
1009 }
1010 break;
1011 case PROVIDER_KEY:
1012 if (item instanceof ProviderKeyBinding
1013 && ((ProviderKeyBinding) item).getProviderKey().equals(result.key)) {
1014 return true;
1015 }
1016 break;
1017 }
1018 return false;
1019 }
sberlin4e114572011-06-28 23:50:03 +00001020
Christian Edward Gruberaf24f632014-04-01 15:07:55 -07001021 static <T> BindResult<T> instance(T t) {
sberlin4e114572011-06-28 23:50:03 +00001022 return new BindResult<T>(INSTANCE, t, null);
1023 }
1024
Christian Edward Gruberaf24f632014-04-01 15:07:55 -07001025 static <T> BindResult<T> linked(Class<? extends T> clazz) {
sberlin4e114572011-06-28 23:50:03 +00001026 return new BindResult<T>(LINKED, null, Key.get(clazz));
1027 }
1028
Christian Edward Gruberaf24f632014-04-01 15:07:55 -07001029 static <T> BindResult<T> linked(Key<? extends T> key) {
sberlin4e114572011-06-28 23:50:03 +00001030 return new BindResult<T>(LINKED, null, key);
1031 }
1032
Christian Edward Gruberaf24f632014-04-01 15:07:55 -07001033 static <T> BindResult<T> providerInstance(T t) {
sberlin4e114572011-06-28 23:50:03 +00001034 return new BindResult<T>(PROVIDER_INSTANCE, t, null);
1035 }
Christian Edward Gruberaf24f632014-04-01 15:07:55 -07001036
1037 static <T> BindResult<T> providerKey(Key<T> key) {
1038 return new BindResult<T>(PROVIDER_KEY, null, key);
1039 }
sberlin4e114572011-06-28 23:50:03 +00001040
1041 /** The kind of binding. */
Christian Edward Gruberaf24f632014-04-01 15:07:55 -07001042 static enum BindType { INSTANCE, LINKED, PROVIDER_INSTANCE, PROVIDER_KEY }
sberlin4e114572011-06-28 23:50:03 +00001043 /** The result of the binding. */
Christian Edward Gruberbf2b16c2013-05-15 18:39:15 -07001044 static class BindResult<T> {
sberlin4e114572011-06-28 23:50:03 +00001045 private final BindType type;
Christian Edward Gruberaf24f632014-04-01 15:07:55 -07001046 private final Key<?> key;
sberlin4e114572011-06-28 23:50:03 +00001047 private final T instance;
1048
Christian Edward Gruberaf24f632014-04-01 15:07:55 -07001049 private BindResult(BindType type, T instance, Key<?> key) {
sberlin4e114572011-06-28 23:50:03 +00001050 this.type = type;
1051 this.instance = instance;
1052 this.key = key;
1053 }
1054
1055 @Override
1056 public String toString() {
1057 switch(type) {
1058 case INSTANCE:
1059 return "instance[" + instance + "]";
1060 case LINKED:
1061 return "linkedKey[" + key + "]";
1062 case PROVIDER_INSTANCE:
1063 return "providerInstance[" + instance + "]";
Christian Edward Gruberaf24f632014-04-01 15:07:55 -07001064 case PROVIDER_KEY:
1065 return "providerKey[" + key + "]";
sberlin4e114572011-06-28 23:50:03 +00001066 }
1067 return null;
1068 }
1069 }
1070
1071 private static class Visitor<T> extends
1072 DefaultBindingTargetVisitor<T, Object> implements MultibindingsTargetVisitor<T, Object> {
1073
1074 public Object visit(MultibinderBinding<? extends T> multibinding) {
1075 return multibinding;
1076 }
1077
1078 public Object visit(MapBinderBinding<? extends T> mapbinding) {
1079 return mapbinding;
Christian Edward Gruberaf24f632014-04-01 15:07:55 -07001080 }
1081
1082 public Object visit(OptionalBinderBinding<? extends T> optionalbinding) {
1083 return optionalbinding;
1084 }
sberlin4e114572011-06-28 23:50:03 +00001085 }
1086}