001 /***************************************************************************** 002 * Copyright (C) PicoContainer Organization. All rights reserved. * 003 * ------------------------------------------------------------------------- * 004 * The software in this package is published under the terms of the BSD * 005 * style license a copy of which has been included with this distribution in * 006 * the LICENSE.txt file. * 007 * * 008 * Original code by * 009 *****************************************************************************/ 010 package org.picocontainer.defaults; 011 012 import static org.junit.Assert.assertEquals; 013 import static org.junit.Assert.assertFalse; 014 import static org.junit.Assert.assertNotNull; 015 import static org.junit.Assert.assertNotSame; 016 import static org.junit.Assert.assertSame; 017 import static org.junit.Assert.assertTrue; 018 import static org.junit.Assert.fail; 019 import static org.picocontainer.tck.MockFactory.mockeryWithCountingNamingScheme; 020 021 import java.util.Arrays; 022 import java.util.Collection; 023 import java.util.Collections; 024 import java.util.HashSet; 025 import java.util.List; 026 import java.util.Map; 027 import java.util.Set; 028 import java.util.SortedMap; 029 import java.util.SortedSet; 030 031 import org.jmock.Expectations; 032 import org.jmock.Mockery; 033 import org.jmock.integration.junit4.JMock; 034 import org.junit.Test; 035 import org.junit.runner.RunWith; 036 import org.picocontainer.ComponentAdapter; 037 import org.picocontainer.DefaultPicoContainer; 038 import org.picocontainer.MutablePicoContainer; 039 import org.picocontainer.PicoCompositionException; 040 import org.picocontainer.PicoContainer; 041 import org.picocontainer.adapters.InstanceAdapter; 042 import org.picocontainer.behaviors.Caching; 043 import org.picocontainer.injectors.AbstractInjector; 044 import org.picocontainer.injectors.ConstructorInjector; 045 import org.picocontainer.lifecycle.NullLifecycleStrategy; 046 import org.picocontainer.monitors.NullComponentMonitor; 047 import org.picocontainer.parameters.CollectionComponentParameter; 048 import org.picocontainer.parameters.ComponentParameter; 049 import org.picocontainer.testmodel.SimpleTouchable; 050 import org.picocontainer.testmodel.Touchable; 051 052 /** 053 * @author Aslak Hellesøy 054 * @author Jörg Schaible 055 * @author Mauro Talevi 056 */ 057 @SuppressWarnings("serial") 058 @RunWith(JMock.class) 059 public class CollectionComponentParameterTestCase { 060 061 private Mockery mockery = mockeryWithCountingNamingScheme(); 062 063 @Test 064 public void testShouldInstantiateArrayOfStrings() { 065 CollectionComponentParameter ccp = new CollectionComponentParameter(); 066 final ComponentAdapter componentAdapter = mockery 067 .mock(ComponentAdapter.class); 068 final PicoContainer picoContainer = mockery.mock(PicoContainer.class); 069 mockery.checking(new Expectations() { 070 { 071 atLeast(1).of(componentAdapter).getComponentKey(); 072 will(returnValue("x")); 073 one(picoContainer).getComponentAdapters(); 074 will(returnValue(new HashSet())); 075 one(picoContainer).getComponentAdapters( 076 with(equal(String.class))); 077 will(returnValue(Arrays.asList(new InstanceAdapter("y", 078 "Hello", new NullLifecycleStrategy(), 079 new NullComponentMonitor()), new InstanceAdapter("z", 080 "World", new NullLifecycleStrategy(), 081 new NullComponentMonitor())))); 082 one(picoContainer).getComponent(with(equal("z"))); 083 will(returnValue("World")); 084 one(picoContainer).getComponent(with(equal("y"))); 085 will(returnValue("Hello")); 086 one(picoContainer).getParent(); 087 will(returnValue(null)); 088 } 089 }); 090 List expected = Arrays.asList("Hello", "World"); 091 Collections.sort(expected); 092 List actual = Arrays.asList((Object[]) ccp.resolveInstance( 093 picoContainer, componentAdapter, String[].class, 094 null, false, null)); 095 Collections.sort(actual); 096 assertEquals(expected, actual); 097 } 098 099 static public interface Fish { 100 } 101 102 static public class Cod implements Fish { 103 public String toString() { 104 return "Cod"; 105 } 106 } 107 108 static public class Shark implements Fish { 109 public String toString() { 110 return "Shark"; 111 } 112 } 113 114 static public class Bowl { 115 private final Cod[] cods; 116 private final Fish[] fishes; 117 118 public Bowl(Cod cods[], Fish fishes[]) { 119 this.cods = cods; 120 this.fishes = fishes; 121 } 122 } 123 124 private MutablePicoContainer getDefaultPicoContainer() { 125 MutablePicoContainer mpc = new DefaultPicoContainer(new Caching()); 126 mpc.addComponent(Bowl.class); 127 mpc.addComponent(Cod.class); 128 mpc.addComponent(Shark.class); 129 return mpc; 130 } 131 132 @Test 133 public void testNativeArrays() { 134 MutablePicoContainer mpc = getDefaultPicoContainer(); 135 Cod cod = mpc.getComponent(Cod.class); 136 Bowl bowl = mpc.getComponent(Bowl.class); 137 assertEquals(1, bowl.cods.length); 138 assertEquals(2, bowl.fishes.length); 139 assertSame(cod, bowl.cods[0]); 140 assertNotSame(bowl.fishes[0], bowl.fishes[1]); 141 } 142 143 @Test 144 public void testCollectionsAreGeneratedOnTheFly() { 145 MutablePicoContainer mpc = new DefaultPicoContainer(); 146 mpc 147 .addAdapter(new ConstructorInjector(Bowl.class, Bowl.class, 148 null, new NullComponentMonitor(), 149 new NullLifecycleStrategy(), false)); 150 mpc.addComponent(Cod.class); 151 Bowl bowl = mpc.getComponent(Bowl.class); 152 assertEquals(1, bowl.cods.length); 153 mpc.addComponent("Nemo", new Cod()); 154 bowl = mpc.getComponent(Bowl.class); 155 assertEquals(2, bowl.cods.length); 156 assertNotSame(bowl.cods[0], bowl.cods[1]); 157 } 158 159 static public class CollectedBowl { 160 private final Cod[] cods; 161 private final Fish[] fishes; 162 163 public CollectedBowl(Collection cods, Collection fishes) { 164 this.cods = (Cod[]) cods.toArray(new Cod[cods.size()]); 165 this.fishes = (Fish[]) fishes.toArray(new Fish[fishes.size()]); 166 } 167 } 168 169 @Test 170 public void testCollections() { 171 MutablePicoContainer mpc = new DefaultPicoContainer(new Caching()); 172 mpc.addComponent(CollectedBowl.class, CollectedBowl.class, 173 new ComponentParameter(Cod.class, false), 174 new ComponentParameter(Fish.class, false)); 175 mpc.addComponent(Cod.class); 176 mpc.addComponent(Shark.class); 177 Cod cod = mpc.getComponent(Cod.class); 178 CollectedBowl bowl = mpc.getComponent(CollectedBowl.class); 179 assertEquals(1, bowl.cods.length); 180 assertEquals(2, bowl.fishes.length); 181 assertSame(cod, bowl.cods[0]); 182 assertNotSame(bowl.fishes[0], bowl.fishes[1]); 183 } 184 185 static public class MappedBowl { 186 private final Fish[] fishes; 187 188 public MappedBowl(Map map) { 189 Collection collection = map.values(); 190 this.fishes = (Fish[]) collection.toArray(new Fish[collection 191 .size()]); 192 } 193 } 194 195 @Test 196 public void testMaps() { 197 MutablePicoContainer mpc = new DefaultPicoContainer(); 198 mpc.addComponent(MappedBowl.class, MappedBowl.class, 199 new ComponentParameter(Fish.class, false)); 200 mpc.addComponent(Cod.class); 201 mpc.addComponent(Shark.class); 202 MappedBowl bowl = mpc.getComponent(MappedBowl.class); 203 assertEquals(2, bowl.fishes.length); 204 assertNotSame(bowl.fishes[0], bowl.fishes[1]); 205 } 206 207 public static class UngenericCollectionBowl { 208 public UngenericCollectionBowl(Collection fish) { 209 } 210 } 211 212 @Test 213 public void testShouldNotInstantiateCollectionForUngenericCollectionParameters() { 214 MutablePicoContainer pico = getDefaultPicoContainer(); 215 pico.addComponent(UngenericCollectionBowl.class); 216 try { 217 pico.getComponent(UngenericCollectionBowl.class); 218 fail(); 219 } catch (AbstractInjector.UnsatisfiableDependenciesException e) { 220 // expected 221 } 222 } 223 224 public static class AnotherGenericCollectionBowl { 225 private final String[] strings; 226 227 public AnotherGenericCollectionBowl(String[] strings) { 228 this.strings = strings; 229 } 230 231 public String[] getStrings() { 232 return strings; 233 } 234 } 235 236 @Test 237 public void testShouldFailWhenThereAreNoComponentsToPutInTheArray() { 238 MutablePicoContainer pico = getDefaultPicoContainer(); 239 pico.addComponent(AnotherGenericCollectionBowl.class); 240 try { 241 pico.getComponent(AnotherGenericCollectionBowl.class); 242 fail(); 243 } catch (AbstractInjector.UnsatisfiableDependenciesException e) { 244 // expected 245 } 246 } 247 248 @Test 249 public void testAllowsEmptyArraysIfEspeciallySet() { 250 MutablePicoContainer pico = getDefaultPicoContainer(); 251 pico.addComponent(AnotherGenericCollectionBowl.class, 252 AnotherGenericCollectionBowl.class, 253 ComponentParameter.ARRAY_ALLOW_EMPTY); 254 AnotherGenericCollectionBowl bowl = pico 255 .getComponent(AnotherGenericCollectionBowl.class); 256 assertNotNull(bowl); 257 assertEquals(0, bowl.strings.length); 258 } 259 260 public static class TouchableObserver implements Touchable { 261 private final Touchable[] touchables; 262 263 public TouchableObserver(Touchable[] touchables) { 264 this.touchables = touchables; 265 266 } 267 268 public void touch() { 269 for (Touchable touchable : touchables) { 270 touchable.touch(); 271 } 272 } 273 } 274 275 @Test 276 public void testWillOmitSelfFromCollection() { 277 MutablePicoContainer pico = getDefaultPicoContainer(); 278 pico.addComponent(SimpleTouchable.class); 279 pico.addComponent(TouchableObserver.class); 280 Touchable observer = pico.getComponent(TouchableObserver.class); 281 assertNotNull(observer); 282 observer.touch(); 283 SimpleTouchable touchable = pico.getComponent(SimpleTouchable.class); 284 assertTrue(touchable.wasTouched); 285 } 286 287 @Test 288 public void testWillRemoveComponentsWithMatchingKeyFromParent() { 289 MutablePicoContainer parent = new DefaultPicoContainer(); 290 parent.addComponent("Tom", Cod.class); 291 parent.addComponent("Dick", Cod.class); 292 parent.addComponent("Harry", Cod.class); 293 MutablePicoContainer child = new DefaultPicoContainer(parent); 294 child.addComponent("Dick", Shark.class); 295 child.addComponent(Bowl.class); 296 Bowl bowl = child.getComponent(Bowl.class); 297 assertEquals(3, bowl.fishes.length); 298 assertEquals(2, bowl.cods.length); 299 } 300 301 @Test 302 public void testBowlWithoutTom() { 303 MutablePicoContainer mpc = new DefaultPicoContainer(); 304 mpc.addComponent("Tom", Cod.class); 305 mpc.addComponent("Dick", Cod.class); 306 mpc.addComponent("Harry", Cod.class); 307 mpc.addComponent(Shark.class); 308 mpc.addComponent(CollectedBowl.class, CollectedBowl.class, 309 new CollectionComponentParameter(Cod.class, false) { 310 protected boolean evaluate(ComponentAdapter adapter) { 311 return !"Tom".equals(adapter.getComponentKey()); 312 } 313 }, new CollectionComponentParameter(Fish.class, false)); 314 CollectedBowl bowl = mpc.getComponent(CollectedBowl.class); 315 Cod tom = (Cod) mpc.getComponent("Tom"); 316 assertEquals(4, bowl.fishes.length); 317 assertEquals(2, bowl.cods.length); 318 assertFalse(Arrays.asList(bowl.cods).contains(tom)); 319 } 320 321 public static class DependsOnAll { 322 public DependsOnAll(Set set, SortedSet sortedSet, 323 Collection collection, List list, SortedMap sortedMap, Map map 324 // , ConcurrentMap concurrentMap, Queue queue, BlockingQueue 325 // blockingQueue 326 ) { 327 assertNotNull(set); 328 assertNotNull(sortedSet); 329 assertNotNull(collection); 330 assertNotNull(list); 331 assertNotNull(sortedMap); 332 assertNotNull(map); 333 // assertNotNull(concurrentMap); 334 // assertNotNull(queue); 335 // assertNotNull(blockingQueue); 336 } 337 } 338 339 @Test 340 public void testDifferentCollectiveTypesAreResolved() { 341 MutablePicoContainer pico = new DefaultPicoContainer(); 342 CollectionComponentParameter parameter = new CollectionComponentParameter( 343 Fish.class, true); 344 pico.addComponent(DependsOnAll.class, DependsOnAll.class, parameter, 345 parameter, parameter, parameter, parameter, parameter); 346 assertNotNull(pico.getComponent(DependsOnAll.class)); 347 } 348 349 @Test 350 public void testVerify() { 351 MutablePicoContainer pico = new DefaultPicoContainer(); 352 CollectionComponentParameter parameterNonEmpty = CollectionComponentParameter.ARRAY; 353 pico.addComponent(Shark.class); 354 parameterNonEmpty.verify(pico, null, Fish[].class, null, false, null); 355 try { 356 parameterNonEmpty 357 .verify(pico, null, Cod[].class, null, false, null); 358 fail("(PicoCompositionException expected"); 359 } catch (PicoCompositionException e) { 360 assertTrue(e.getMessage().indexOf(Cod.class.getName()) > 0); 361 } 362 CollectionComponentParameter parameterEmpty = CollectionComponentParameter.ARRAY_ALLOW_EMPTY; 363 parameterEmpty.verify(pico, null, Fish[].class, null, false, null); 364 parameterEmpty.verify(pico, null, Cod[].class, null, false, null); 365 } 366 367 // PICO-243 : this test will fail if executed on jdk1.3 without 368 // commons-collections 369 @Test 370 public void testOrderOfElementsOfAnArrayDependencyIsPreserved() { 371 MutablePicoContainer pico = new DefaultPicoContainer(); 372 pico.addComponent("first", "first"); 373 pico.addComponent("second", "second"); 374 pico.addComponent("third", "third"); 375 pico.addComponent("fourth", "fourth"); 376 pico.addComponent("fifth", "fifth"); 377 pico.addComponent(Truc.class); 378 379 final List strings = pico.getComponents(String.class); 380 assertEquals("first", strings.get(0)); 381 assertEquals("second", strings.get(1)); 382 assertEquals("third", strings.get(2)); 383 assertEquals("fourth", strings.get(3)); 384 assertEquals("fifth", strings.get(4)); 385 386 pico.getComponent(Truc.class); 387 } 388 389 public static final class Truc { 390 public Truc(String[] s) { 391 assertEquals("first", s[0]); 392 assertEquals("second", s[1]); 393 assertEquals("third", s[2]); 394 assertEquals("fourth", s[3]); 395 assertEquals("fifth", s[4]); 396 } 397 } 398 399 }