| package ioc.specs |
| |
| import org.apache.tapestry5.func.F |
| import org.apache.tapestry5.func.Predicate |
| import org.apache.tapestry5.ioc.annotations.Inject |
| import org.apache.tapestry5.ioc.def.ServiceDef |
| import org.apache.tapestry5.ioc.def.ServiceDef2 |
| import org.apache.tapestry5.ioc.internal.QuietOperationTracker |
| import org.apache.tapestry5.ioc.services.Builtin |
| import org.apache.tapestry5.ioc.services.Coercion |
| import org.apache.tapestry5.ioc.services.SymbolSource |
| import spock.lang.Shared |
| import spock.lang.Specification |
| import spock.lang.Unroll |
| |
| import java.lang.reflect.Method |
| |
| import org.apache.tapestry5.ioc.* |
| import org.apache.tapestry5.ioc.internal.util.* |
| |
| class InternalUtilsSpec extends Specification { |
| |
| private static class PrivateInnerClass { |
| |
| public PrivateInnerClass() { |
| } |
| } |
| |
| static class PublicInnerClass { |
| |
| protected PublicInnerClass() { |
| } |
| } |
| |
| @Shared |
| def tracker = new QuietOperationTracker(); |
| |
| |
| @Unroll |
| def "asString(): #desc"() { |
| |
| when: |
| |
| Method m = clazz.getMethod(methodName, * paramTypes) |
| |
| then: |
| |
| InternalUtils.asString(m) == expected |
| |
| where: |
| |
| clazz | methodName | paramTypes | expected | desc |
| Object | "toString" | [] | "java.lang.Object.toString()" | "method with no arguments" |
| Collections | "sort" | [List, Comparator] | "java.util.Collections.sort(List, Comparator)" | "method with multiple argments" |
| Object | "wait" | [long] | "java.lang.Object.wait(long)" | "method with primitive argument" |
| Arrays | "sort" | [int[]] | "java.util.Arrays.sort(int[])" | "method with primitive array argument" |
| Arrays | "sort" | [Object[]] | "java.util.Arrays.sort(Object[])" | "method with object array argument" |
| } |
| |
| @Unroll |
| def "size(): #desc"() { |
| expect: |
| |
| InternalUtils.size(array as Object[]) == expected |
| |
| where: |
| |
| array | expected | desc |
| [] | 0 | "empty array" |
| null | 0 | "null is size 0" |
| [1, 2, 3] | 3 | "non-empty array" |
| } |
| |
| @Unroll |
| def "stripMemberName('#input') should be '#expected'"() { |
| expect: |
| |
| InternalUtils.stripMemberName(input) == expected |
| |
| where: |
| |
| input | expected |
| "simple" | "simple" |
| "_name" | "name" |
| '$name' | "name" |
| '__$ruby_style_' | "ruby_style" |
| '$_$__$__$_$____$_$_$_$$name' | "name" |
| "foo_" | "foo" |
| "_foo_" | "foo" |
| } |
| |
| def "invalid input to stripMemberName() is an exception"() { |
| when: |
| |
| InternalUtils.stripMemberName("!foo") |
| |
| then: |
| |
| IllegalArgumentException e = thrown() |
| |
| e.message == "Input '!foo' is not a valid Java identifier." |
| } |
| |
| def "toList(Enumeration) is a sorted list"() { |
| when: |
| |
| def e = Collections.enumeration(["wilma", "fred", "barney"]) |
| |
| then: |
| |
| InternalUtils.toList(e) == ["barney", "fred", "wilma"] |
| } |
| |
| @Unroll |
| def "join(): #desc"() { |
| expect: |
| |
| InternalUtils.join(list) == expected |
| |
| where: |
| |
| list | expected | desc |
| ["barney"] | "barney" | "single value" |
| ["fred", "barney", "wilma"] | "fred, barney, wilma" | "multiple values" |
| ["fred", "barney", "", "wilma"] | "fred, barney, (blank), wilma" | "empty string converted to '(blank)'" |
| } |
| |
| @Unroll |
| def "joinSorted(): #desc"() { |
| InternalUtils.joinSorted(list) == expected |
| |
| where: |
| |
| list | expected | desc |
| null | "(none)" | "null list is '(none)'" |
| [] | "(none)" | "empty list is '(none)'" |
| ["barney"] | "barney" | "single value" |
| ["fred", "barney", "wilma"] | "barney, fred, wilma" | "multiple values" |
| ["fred", "barney", "", "wilma"] | "(blank), barney, fred, wilma" | "empty string converted to '(blank)'" |
| } |
| |
| @Unroll |
| def "capitalize('#input') is '#expected'"() { |
| expect: |
| |
| InternalUtils.capitalize(input) == expected |
| |
| where: |
| |
| input | expected |
| "hello" | "Hello" |
| "Goodbye" | "Goodbye" |
| "" | "" |
| "a" | "A" |
| "A" | "A" |
| } |
| |
| def "locationOf(Object)"() { |
| Locatable locatable = Mock() |
| Location l = Mock() |
| |
| expect: |
| |
| InternalUtils.locationOf(null) == null |
| InternalUtils.locationOf("La! La!") == null |
| |
| InternalUtils.locationOf(l).is(l) |
| |
| when: |
| |
| def actual = InternalUtils.locationOf(locatable) |
| |
| then: |
| |
| _ * locatable.location >> l |
| |
| actual.is(l) |
| } |
| |
| @Unroll |
| def "sortedKeys(): #desc"() { |
| expect: |
| |
| InternalUtils.sortedKeys(map) == expected |
| |
| where: |
| |
| map | expected | desc |
| null | [] | "null map" |
| [:] | [] | "empty map" |
| ["fred": "flintstone", "barney": "rubble"] | ["barney", "fred"] | "standard map" |
| } |
| |
| @Unroll |
| def "get(Map,Object): #desc"() { |
| expect: |
| |
| InternalUtils.get(map, key) == expected |
| |
| where: |
| |
| map | key | expected | desc |
| null | null | null | "null key and map" |
| null | "foo" | null | "null map" |
| ["fred": "flintstone"] | "fred" | "flintstone" | "real map and key" |
| ["fred": "flintstone"] | "barney" | null | "real map with missing key" |
| } |
| |
| def "reverseIterator(List)"() { |
| when: |
| |
| def i = InternalUtils.reverseIterator(["a", "b", "c"]) |
| |
| then: |
| |
| i.hasNext() |
| i.next() == "c" |
| |
| i.hasNext() |
| i.next() == "b" |
| |
| i.hasNext() |
| i.next() == "a" |
| |
| !i.hasNext() |
| } |
| |
| def "remove() not supported by reverse Iterator"() { |
| def i = InternalUtils.reverseIterator(["a", "b", "c"]) |
| |
| when: |
| |
| i.remove() |
| |
| then: |
| |
| thrown(UnsupportedOperationException) |
| } |
| |
| @Unroll |
| def "lastTerm(): #desc"() { |
| expect: |
| |
| InternalUtils.lastTerm(input) == expected |
| |
| where: |
| |
| input | expected | desc |
| "simple" | "simple" | "single term" |
| "fee.fie.foe.fum" | "fum" | "dotted name sequence" |
| } |
| |
| def "simple value passed to lastTerm() returns the exact input value"() { |
| def input = "simple" |
| |
| expect: |
| |
| InternalUtils.lastTerm(input).is(input) |
| } |
| |
| def "addToMapList()"() { |
| def map = [:] |
| |
| when: |
| |
| InternalUtils.addToMapList(map, "fred", 1) |
| |
| then: |
| |
| map == ["fred": [1]] |
| |
| when: |
| |
| InternalUtils.addToMapList(map, "fred", 2) |
| |
| then: |
| |
| map == ["fred": [1, 2]] |
| } |
| |
| def "validateMarkerAnnotation()"() { |
| |
| when: |
| |
| InternalUtils.validateMarkerAnnotation(Inject) |
| |
| then: |
| |
| noExceptionThrown() |
| |
| when: |
| |
| InternalUtils.validateMarkerAnnotations([Inject, NotRetainedRuntime] as Class[]) |
| |
| then: |
| |
| IllegalArgumentException e = thrown() |
| |
| e.message == "Marker annotation class org.apache.tapestry5.ioc.internal.util.NotRetainedRuntime is not valid because it is not visible at runtime. Add a @Retention(RetentionPolicy.RUNTIME) to the class." |
| } |
| |
| def "close(Closable) for null does nothing"() { |
| when: |
| InternalUtils.close(null) |
| |
| then: |
| noExceptionThrown() |
| } |
| |
| def "close(Closable) for success case"() { |
| Closeable c = Mock() |
| |
| when: |
| |
| InternalUtils.close(c) |
| |
| then: |
| |
| 1 * c.close() |
| } |
| |
| def "close(Closable) ignores exceptions"() { |
| Closeable c = Mock() |
| |
| when: |
| |
| InternalUtils.close(c) |
| |
| then: |
| |
| 1 * c.close() >> { |
| throw new IOException("ignored") |
| } |
| } |
| |
| def "constructor with Tapestry @Inject annotation"() { |
| when: |
| |
| def c = InternalUtils.findAutobuildConstructor(InjectoBean) |
| |
| then: |
| |
| c.parameterTypes == [String] |
| } |
| |
| def "constructor with javax @Inject annotation"() { |
| when: |
| |
| def c = InternalUtils.findAutobuildConstructor(JavaxInjectBean) |
| |
| then: |
| |
| c.parameterTypes == [String] |
| } |
| |
| def "too many autobuild constructors"() { |
| when: |
| |
| InternalUtils.findAutobuildConstructor(TooManyAutobuildConstructorsBean) |
| |
| then: |
| |
| IllegalArgumentException e = thrown() |
| |
| e.message == "Too many autobuild constructors found: use either @org.apache.tapestry5.ioc.annotations.Inject or @javax.inject.Inject annotation to mark a single constructor for autobuilding." |
| } |
| |
| def "validateConstructorForAutobuild(): ensure check that the class itself is public"() { |
| def cons = PrivateInnerClass.constructors[0] |
| |
| when: |
| |
| InternalUtils.validateConstructorForAutobuild(cons) |
| |
| then: |
| |
| IllegalArgumentException e = thrown() |
| |
| e.message == "Class ${PrivateInnerClass.name} is not a public class and may not be autobuilt." |
| } |
| |
| def "validateConstructorForAutobuild(): ensure check that constructor is public"() { |
| def cons = PublicInnerClass.declaredConstructors[0] |
| |
| when: |
| |
| InternalUtils.validateConstructorForAutobuild(cons) |
| |
| then: |
| |
| IllegalArgumentException e = thrown() |
| |
| e.message == "Constructor protected ${PublicInnerClass.name}() is not public and may not be used for autobuilding an instance of the class. " + |
| "You should make the constructor public, or mark an alternate public constructor with the @Inject annotation." |
| } |
| |
| def "@Inject service annotation on a field"() { |
| ObjectLocator ol = Mock() |
| def target = new FieldInjectionViaInjectService() |
| Runnable fred = Mock() |
| |
| when: |
| |
| InternalUtils.injectIntoFields(target, ol, null, tracker) |
| |
| then: |
| |
| target.fred.is(fred) |
| |
| 1 * ol.getService("FredService", Runnable) >> fred |
| } |
| |
| def "@javax.annotations.Inject / @Named annotation on field"() { |
| ObjectLocator ol = Mock() |
| def target = new FieldInjectionViaJavaxNamed() |
| Runnable fred = Mock() |
| |
| when: |
| |
| InternalUtils.injectIntoFields(target, ol, null, tracker) |
| |
| then: |
| |
| target.fred.is(fred) |
| |
| 1 * ol.getService("BarneyService", Runnable) >> fred |
| } |
| |
| def "@Inject annotation on field"() { |
| ObjectLocator ol = Mock() |
| def target = new FieldInjectionViaInject() |
| SymbolSource source = Mock() |
| InjectionResources resources = Mock() |
| |
| when: |
| |
| InternalUtils.injectIntoFields(target, ol, resources, tracker) |
| |
| then: |
| |
| target.symbolSource.is(source) |
| |
| 1 * resources.findResource(SymbolSource, SymbolSource) >> null |
| 1 * ol.getObject(SymbolSource, _) >> { type, ap -> |
| assert ap.getAnnotation(Builtin) != null |
| |
| return source |
| } |
| } |
| |
| def "@javax.annotation.Inject annotation on field"() { |
| ObjectLocator ol = Mock() |
| def target = new FieldInjectionViaJavaxInject() |
| SymbolSource source = Mock() |
| InjectionResources resources = Mock() |
| |
| when: |
| |
| InternalUtils.injectIntoFields(target, ol, resources, tracker) |
| |
| then: |
| |
| target.symbolSource.is(source) |
| |
| 1 * resources.findResource(SymbolSource, SymbolSource) >> null |
| 1 * ol.getObject(SymbolSource, _) >> { type, ap -> |
| assert ap.getAnnotation(Builtin) != null |
| |
| return source |
| } |
| } |
| |
| def "check handling of exception while injecting into a field"() { |
| ObjectLocator ol = Mock() |
| def target = new FieldInjectionViaInjectService() |
| |
| when: |
| |
| InternalUtils.injectIntoFields(target, ol, null, tracker) |
| |
| then: |
| |
| Exception e = thrown() |
| |
| 1 * ol.getService("FredService", Runnable) >> "NotTheRightType" |
| |
| e.message.contains "Unable to set field 'fred' of <FieldInjectionViaInjectService> to NotTheRightType" |
| } |
| |
| @Unroll |
| def "keys(Map): #desc"() { |
| expect: |
| |
| InternalUtils.keys(map) == (expected as Set) |
| |
| where: |
| |
| map | expected | desc |
| null | [] | "null map" |
| [:] | [] | "empty map" |
| ["fred": "flintstone", "barney": "rubble"] | ["fred", "barney"] | "non-empty map" |
| } |
| |
| @Unroll |
| def "size(Collection): #desc"() { |
| expect: |
| |
| InternalUtils.size(coll) == expected |
| |
| where: |
| |
| coll | expected | desc |
| null | 0 | "null collection" |
| [] | 0 | "empty collection" |
| [1, 2, 3] | 3 | "non-empty collection" |
| } |
| |
| def "toServiceDef2() delegates most methods to ServiceDef instance"() { |
| ServiceDef delegate = Mock() |
| ServiceBuilderResources resources = Mock() |
| ObjectCreator creator = Mock() |
| def serviceId = "fred" |
| def markers = [] as Set |
| |
| ServiceDef2 sd = InternalUtils.toServiceDef2(delegate) |
| |
| when: |
| |
| def actual = sd.createServiceCreator(resources) |
| |
| then: |
| |
| actual.is creator |
| |
| 1 * delegate.createServiceCreator(resources) >> creator |
| |
| |
| when: |
| |
| actual = sd.getServiceId() |
| |
| then: |
| actual.is serviceId |
| |
| 1 * delegate.serviceId >> serviceId |
| |
| when: |
| |
| actual = sd.markers |
| |
| then: |
| |
| actual.is markers |
| 1 * delegate.markers >> markers |
| |
| |
| when: |
| |
| actual = sd.serviceInterface |
| |
| then: |
| |
| actual == Runnable |
| 1 * delegate.serviceInterface >> Runnable |
| |
| when: |
| |
| actual = sd.serviceScope |
| |
| then: |
| |
| actual == ScopeConstants.PERTHREAD |
| 1 * delegate.serviceScope >> ScopeConstants.PERTHREAD |
| |
| when: |
| |
| actual = sd.eagerLoad |
| |
| then: |
| |
| actual == true |
| 1 * delegate.eagerLoad >> true |
| |
| expect: |
| |
| !sd.preventDecoration |
| } |
| |
| def "matchAndSort()"() { |
| def pred = { !it.startsWith(".") } as Predicate |
| |
| expect: |
| |
| InternalUtils.matchAndSort(["Fred", "Barney", "..", ".hidden", "Wilma"], pred) == ["Barney", "Fred", "Wilma"] |
| } |
| |
| def "toMapper(Coercion)"() { |
| def coercion = { it.toUpperCase() } as Coercion |
| |
| def flow = F.flow("Mary", "had", "a", "little", "lamb") |
| |
| expect: |
| |
| flow.map(InternalUtils.toMapper(coercion)).toList() == ["MARY", "HAD", "A", "LITTLE", "LAMB"] |
| } |
| } |
| |