blob: 0e30058447fa5b536934d3a5c50bbb454b0dda35 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2018 RCP Vision (http://www.rcp-vision.com) and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Lorenzo Bettini - Initial contribution and API
*******************************************************************************/
package org.eclipse.emf.parsley.tests
import com.google.inject.AbstractModule
import com.google.inject.Binder
import com.google.inject.Inject
import com.google.inject.Key
import com.google.inject.TypeLiteral
import com.google.inject.util.Modules
import java.util.ArrayList
import org.eclipse.emf.ecore.EClass
import org.eclipse.emf.ecore.EcorePackage
import org.eclipse.emf.parsley.EmfParsleyJavaGuiceModule
import org.eclipse.emf.parsley.inject.parameters.FactoryParameter
import org.eclipse.emf.parsley.inject.parameters.InjectableParameter
import org.eclipse.emf.parsley.internal.inject.GenericFactory
import org.eclipse.emf.parsley.junit4.AbstractEmfParsleyShellBasedTest
import org.junit.Before
import org.junit.Test
import static extension org.junit.Assert.*
class GenericFactoryTest extends AbstractEmfParsleyShellBasedTest {
@FactoryParameter
private static class MyInjectableParameter implements InjectableParameter {
public var int i
new() {
}
new(int i) {
this.i = i
}
}
private static interface InjectableObjectInterface {
}
private static class InjectableObject implements InjectableObjectInterface {
public var MyInjectableParameter param
@Inject
new(MyInjectableParameter param) {
this.param = param
}
}
private static class AnotherInjectableObject implements InjectableObjectInterface {
public var MyInjectableParameter param
@Inject
new(MyInjectableParameter param) {
this.param = param
}
}
private static class InjectableObjectCustom extends InjectableObject {
public var AnotherInjectableObject nested
@Inject
new(MyInjectableParameter param, InjectableObjectFactory factory) {
super(param)
// pass an incremented integer
nested = factory.create(AnotherInjectableObject, param.i+1)
}
}
private static class InjectableObjectFactory {
@Inject
GenericFactory<InjectableObjectInterface> internalFactory
def <T extends InjectableObjectInterface> T create(Class<T> type, int param) {
return internalFactory.createInstance(type, new MyInjectableParameter(param))
}
}
private static class InjectableObjectModule extends EmfParsleyJavaGuiceModule {
override configure(Binder binder) {
super.configure(binder)
}
}
@FactoryParameter
private static class GenericInjectableObject<T extends InjectableParameter> {
public T value
@Inject
new(T value) {
this.value = value
}
}
@FactoryParameter
private static class StringP implements InjectableParameter {
public String value
new () {
}
new (String value) {
this.value = value
}
}
@FactoryParameter
private static class EClassP implements InjectableParameter {
public EClass value
new () {
}
new (EClass value) {
this.value = value
}
}
private static class GenericInjectableObjectWithString extends GenericInjectableObject<StringP> {
@Inject
new (StringP value) {
super(value)
}
}
private static class GenericInjectableObjectWithEClass extends GenericInjectableObject<EClassP> {
@Inject
new (EClassP value) {
super(value)
}
}
/**
* The injected parameters are themselves injectable objects
* with injectable parameters.
*
* Though we don't specify arguments in the factory for this
* constructor (GenericInjectableObjectWithString, GenericInjectableObjectWithEClass),
* Guice will be able to inject the arguments
* after injecting EStringP and EClassP into
* GenericInjectableObjectWithString, GenericInjectableObjectWithEClass.
*/
private static class ClassWithNestedInjectableObjects {
public GenericInjectableObjectWithString withString
public GenericInjectableObjectWithEClass withEClass
@Inject
new (GenericInjectableObjectWithString withString, GenericInjectableObjectWithEClass withEClass) {
this.withString = withString
this.withEClass = withEClass
}
}
var InjectableObjectFactory factory
@Before
def void setupFactory() {
factory = createInjector(new InjectableObjectModule).getInstance(InjectableObjectFactory)
}
@Test
def void testCanCreateInjectableObject() {
val c = factory.create(InjectableObject, 10)
10.assertSame(c.param.i)
}
@Test
def void testCanCreateSeveralInjectableObject() {
var c = factory.create(InjectableObject, 10)
10.assertSame(c.param.i)
c = factory.create(InjectableObject, 20)
20.assertSame(c.param.i)
}
@Test
def void testCanCreateInjectableObjectCustom() {
factory = createInjector(new InjectableObjectModule() {
override configure(Binder binder) {
super.configure(binder)
binder.bind(InjectableObject).to(InjectableObjectCustom)
}
})
.getInstance(InjectableObjectFactory)
val c = factory.create(InjectableObject, 10) as InjectableObjectCustom
10.assertSame(c.param.i)
11.assertSame(c.nested.param.i) // the nested is passed an incremented integer
}
@Test
def void testCanCreateInjectableObjectCustomWithModuleOverride() {
val override = Modules.override(new InjectableObjectModule()).with(new AbstractModule() {
override protected configure() {
bind(InjectableObject).to(InjectableObjectCustom)
}
})
factory = createInjector(override).getInstance(InjectableObjectFactory)
val c = factory.create(InjectableObject, 10) as InjectableObjectCustom
10.assertSame(c.param.i)
11.assertSame(c.nested.param.i) // the nested is passed an incremented integer
}
@Test
def void testCanCreateGenericInjectableObjects() {
val injector = getOrCreateInjector
val genericInjectableObjectStringFactory =
injector.getInstance(
Key.get(new TypeLiteral<GenericFactory<GenericInjectableObject<StringP>>>() {})
)
val genericInjectableObjectEClassFactory =
injector.getInstance(
Key.get(new TypeLiteral<GenericFactory<GenericInjectableObject<EClassP>>>() {})
)
val eclass = EcorePackage.eINSTANCE.EObject
val o1 = genericInjectableObjectStringFactory
.createInstance(GenericInjectableObjectWithString, new StringP("test"))
val o2 = genericInjectableObjectEClassFactory
.createInstance(GenericInjectableObjectWithEClass, new EClassP(eclass))
"test".assertEquals(o1.value.value)
eclass.assertEquals(o2.value.value)
}
@Test
def void testCanInjectWithNestedInjectableObjects() {
val injector = getOrCreateInjector
val factory = injector.getInstance(
Key.get(new TypeLiteral<GenericFactory<ClassWithNestedInjectableObjects>>() {})
)
val eclass = EcorePackage.eINSTANCE.EObject
// StringP and EClassP are not directly arguments
// for GenericFactory<ClassWithNestedInjectableObjects
// they are arguments for the constructors of the arguments
val o = factory.createInstance(
ClassWithNestedInjectableObjects,
new StringP("test"),
new EClassP(eclass)
)
"test".assertEquals(o.withString.value.value)
eclass.assertEquals(o.withEClass.value.value)
}
@Test(expected=NullPointerException)
def void testInjectNull() {
val injector = getOrCreateInjector
val genericInjectableObjectStringFactory =
injector.getInstance(
Key.get(new TypeLiteral<GenericFactory<GenericInjectableObject<StringP>>>() {})
)
genericInjectableObjectStringFactory
.createInstance(GenericInjectableObjectWithString, null)
}
@Test
def void testMultiThreading() throws Exception {
val injector = getOrCreateInjector
val factory =
injector.getInstance(
Key.get(new TypeLiteral<GenericFactory<GenericInjectableObject<StringP>>>() {})
)
val threads = new ArrayList;
val exceptions = new ArrayList;
for (var i = 0; i <100; i++) {
val thread = new Thread() {
override void run() {
try {
factory.createInstance(GenericInjectableObjectWithString, new StringP("test"))
} catch (Exception e) {
exceptions.add(e);
}
}
};
threads.add(thread);
thread.start();
}
for (Thread thread : threads) {
thread.join();
}
if (!exceptions.isEmpty()) {
throw exceptions.get(0);
}
}
}