Browse Source

Improve class generation for testing

pull/515/head
Decebal Suiu 1 year ago
parent
commit
aebfa790b6

+ 41
- 71
pf4j/src/test/java/org/pf4j/AbstractExtensionFinderTest.java View File

*/ */
package org.pf4j; package org.pf4j;


import com.google.common.collect.ImmutableList;
import com.google.common.io.ByteStreams;
import com.google.testing.compile.Compilation;
import java.util.Comparator;
import java.util.UUID;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.pf4j.test.FailTestPlugin;
import org.pf4j.test.JavaFileObjectClassLoader;
import org.pf4j.test.JavaFileObjectUtils;
import org.pf4j.test.JavaSources;
import org.pf4j.test.TestExtension;
import org.pf4j.test.TestExtensionPoint; import org.pf4j.test.TestExtensionPoint;


import javax.tools.JavaFileObject; import javax.tools.JavaFileObject;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.UUID;


import static com.google.testing.compile.CompilationSubject.assertThat;
import static com.google.testing.compile.Compiler.javac;
import static org.junit.Assert.assertNull;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;


when(pluginStopped.getPluginState()).thenReturn(PluginState.STOPPED); when(pluginStopped.getPluginState()).thenReturn(PluginState.STOPPED);


pluginManager = mock(PluginManager.class); pluginManager = mock(PluginManager.class);
when(pluginManager.getPlugin(eq("plugin1"))).thenReturn(pluginStarted);
when(pluginManager.getPlugin(eq("plugin2"))).thenReturn(pluginStopped);
when(pluginManager.getPluginClassLoader(eq("plugin1"))).thenReturn(getClass().getClassLoader());
when(pluginManager.getPlugin("plugin1")).thenReturn(pluginStarted);
when(pluginManager.getPlugin("plugin2")).thenReturn(pluginStopped);
when(pluginManager.getPluginClassLoader("plugin1")).thenReturn(getClass().getClassLoader());
when(pluginManager.getExtensionFactory()).thenReturn(new DefaultExtensionFactory()); when(pluginManager.getExtensionFactory()).thenReturn(new DefaultExtensionFactory());
} }


} }


}; };
List<ExtensionWrapper<FailTestPlugin>> list = instance.find(FailTestPlugin.class);
List<ExtensionWrapper<TestExtension>> list = instance.find(TestExtension.class);
assertEquals(0, list.size()); assertEquals(0, list.size());
} }




Set<String> bucket = new HashSet<>(); Set<String> bucket = new HashSet<>();
bucket.add("org.pf4j.test.TestExtension"); bucket.add("org.pf4j.test.TestExtension");
bucket.add("org.pf4j.test.FailTestExtension");
entries.put(null, bucket); entries.put(null, bucket);


return entries; return entries;
}; };


List<ExtensionWrapper<TestExtensionPoint>> list = instance.find(TestExtensionPoint.class); List<ExtensionWrapper<TestExtensionPoint>> list = instance.find(TestExtensionPoint.class);
assertEquals(2, list.size());
assertEquals(1, list.size());
} }


/** /**


Set<String> bucket = new HashSet<>(); Set<String> bucket = new HashSet<>();
bucket.add("org.pf4j.test.TestExtension"); bucket.add("org.pf4j.test.TestExtension");
bucket.add("org.pf4j.test.FailTestExtension");
entries.put("plugin1", bucket); entries.put("plugin1", bucket);
bucket = new HashSet<>(); bucket = new HashSet<>();
bucket.add("org.pf4j.test.TestExtension"); bucket.add("org.pf4j.test.TestExtension");
}; };


List<ExtensionWrapper<TestExtensionPoint>> list = instance.find(TestExtensionPoint.class); List<ExtensionWrapper<TestExtensionPoint>> list = instance.find(TestExtensionPoint.class);
assertEquals(2, list.size());
assertEquals(1, list.size());


list = instance.find(TestExtensionPoint.class, "plugin1"); list = instance.find(TestExtensionPoint.class, "plugin1");
assertEquals(2, list.size());
assertEquals(1, list.size());


list = instance.find(TestExtensionPoint.class, "plugin2"); list = instance.find(TestExtensionPoint.class, "plugin2");
// "0" because the status of "plugin2" is STOPPED => no extensions
assertEquals(0, list.size()); assertEquals(0, list.size());
} }


*/ */
@Test @Test
public void testFindExtensionWrappersFromPluginId() { public void testFindExtensionWrappersFromPluginId() {
// complicate the test to show hot to deal with dynamic Java classes (generated at runtime from sources)
PluginWrapper plugin3 = mock(PluginWrapper.class);
JavaFileObject object = JavaSources.compile(DefaultExtensionFactoryTest.FailTestExtension);
JavaFileObjectClassLoader classLoader = new JavaFileObjectClassLoader();
classLoader.load(object);
when(plugin3.getPluginClassLoader()).thenReturn(classLoader);
when(plugin3.getPluginState()).thenReturn(PluginState.STARTED);
when(pluginManager.getPluginClassLoader("plugin3")).thenReturn(classLoader);
when(pluginManager.getPlugin("plugin3")).thenReturn(plugin3);

ExtensionFinder instance = new AbstractExtensionFinder(pluginManager) { ExtensionFinder instance = new AbstractExtensionFinder(pluginManager) {


@Override @Override


Set<String> bucket = new HashSet<>(); Set<String> bucket = new HashSet<>();
bucket.add("org.pf4j.test.TestExtension"); bucket.add("org.pf4j.test.TestExtension");
bucket.add("org.pf4j.test.FailTestExtension");
entries.put("plugin1", bucket); entries.put("plugin1", bucket);
bucket = new HashSet<>(); bucket = new HashSet<>();
bucket.add("org.pf4j.test.TestExtension"); bucket.add("org.pf4j.test.TestExtension");
entries.put("plugin2", bucket); entries.put("plugin2", bucket);
bucket = new HashSet<>();
bucket.add(JavaFileObjectUtils.getClassName(object));
entries.put("plugin3", bucket);


return entries; return entries;
} }
}; };


List<ExtensionWrapper> plugin1Result = instance.find("plugin1"); List<ExtensionWrapper> plugin1Result = instance.find("plugin1");
assertEquals(2, plugin1Result.size());
assertEquals(1, plugin1Result.size());


List<ExtensionWrapper> plugin2Result = instance.find("plugin2"); List<ExtensionWrapper> plugin2Result = instance.find("plugin2");
assertEquals(0, plugin2Result.size()); assertEquals(0, plugin2Result.size());


List<ExtensionWrapper> plugin3Result = instance.find(UUID.randomUUID().toString());
assertEquals(0, plugin3Result.size());
List<ExtensionWrapper> plugin3Result = instance.find("plugin3");
assertEquals(1, plugin3Result.size());

List<ExtensionWrapper> plugin4Result = instance.find(UUID.randomUUID().toString());
assertEquals(0, plugin4Result.size());
} }


@Test @Test
public void findExtensionAnnotation() throws Exception {
Compilation compilation = javac().compile(ExtensionAnnotationProcessorTest.Greeting,
ExtensionAnnotationProcessorTest.WhazzupGreeting);
assertThat(compilation).succeededWithoutWarnings();
ImmutableList<JavaFileObject> generatedFiles = compilation.generatedFiles();
public void findExtensionAnnotation() {
List<JavaFileObject> generatedFiles = JavaSources.compileAll(JavaSources.Greeting, JavaSources.WhazzupGreeting);
assertEquals(2, generatedFiles.size()); assertEquals(2, generatedFiles.size());


JavaFileObjectClassLoader classLoader = new JavaFileObjectClassLoader();
Map<String, Class<?>> loadedClasses = classLoader.loadClasses(new ArrayList<>(generatedFiles));
Map<String, Class<?>> loadedClasses = new JavaFileObjectClassLoader().load(generatedFiles);
Class<?> clazz = loadedClasses.get("test.WhazzupGreeting"); Class<?> clazz = loadedClasses.get("test.WhazzupGreeting");
Extension extension = AbstractExtensionFinder.findExtensionAnnotation(clazz); Extension extension = AbstractExtensionFinder.findExtensionAnnotation(clazz);
assertNotNull(extension);
Assertions.assertNotNull(extension);
} }


@Test @Test
public void findExtensionAnnotationThatMissing() throws Exception {
Compilation compilation = javac().compile(ExtensionAnnotationProcessorTest.Greeting,
public void findExtensionAnnotationThatMissing() {
List<JavaFileObject> generatedFiles = JavaSources.compileAll(JavaSources.Greeting,
ExtensionAnnotationProcessorTest.SpinnakerExtension_NoExtension, ExtensionAnnotationProcessorTest.SpinnakerExtension_NoExtension,
ExtensionAnnotationProcessorTest.WhazzupGreeting_SpinnakerExtension); ExtensionAnnotationProcessorTest.WhazzupGreeting_SpinnakerExtension);
assertThat(compilation).succeededWithoutWarnings();
ImmutableList<JavaFileObject> generatedFiles = compilation.generatedFiles();
assertEquals(3, generatedFiles.size()); assertEquals(3, generatedFiles.size());


JavaFileObjectClassLoader classLoader = new JavaFileObjectClassLoader();
Map<String, Class<?>> loadedClasses = classLoader.loadClasses(new ArrayList<>(generatedFiles));
Map<String, Class<?>> loadedClasses = new JavaFileObjectClassLoader().load(generatedFiles);
Class<?> clazz = loadedClasses.get("test.WhazzupGreeting"); Class<?> clazz = loadedClasses.get("test.WhazzupGreeting");
Extension extension = AbstractExtensionFinder.findExtensionAnnotation(clazz); Extension extension = AbstractExtensionFinder.findExtensionAnnotation(clazz);
assertNull(extension);
}

static class JavaFileObjectClassLoader extends ClassLoader {

public Map<String, Class<?>> loadClasses(List<JavaFileObject> classes) throws IOException {
// Sort generated ".class" by lastModified field
classes.sort(Comparator.comparingLong(JavaFileObject::getLastModified));

// Load classes
Map<String, Class<?>> loadedClasses = new HashMap<>(classes.size());
for (JavaFileObject clazz : classes) {
String className = getClassName(clazz);
byte[] data = ByteStreams.toByteArray(clazz.openInputStream());
Class<?> loadedClass = defineClass(className, data,0, data.length);
loadedClasses.put(className, loadedClass);
}

return loadedClasses;
}

private static String getClassName(JavaFileObject object) {
String name = object.getName();
// Remove "/CLASS_OUT/" from head and ".class" from tail
name = name.substring(14, name.length() - 6);
name = name.replace('/', '.');

return name;
}

Assertions.assertNull(extension);
} }


} }

+ 22
- 2
pf4j/src/test/java/org/pf4j/DefaultExtensionFactoryTest.java View File

*/ */
package org.pf4j; package org.pf4j;


import com.google.testing.compile.JavaFileObjects;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.pf4j.test.FailTestExtension;
import org.pf4j.test.JavaFileObjectClassLoader;
import org.pf4j.test.JavaSources;
import org.pf4j.test.TestExtension; import org.pf4j.test.TestExtension;


import javax.tools.JavaFileObject;

import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertThrows;


*/ */
public class DefaultExtensionFactoryTest { public class DefaultExtensionFactoryTest {


public static final JavaFileObject FailTestExtension = JavaFileObjects.forSourceLines("FailTestExtension",
"package test;",
"import org.pf4j.test.TestExtensionPoint;",
"import org.pf4j.Extension;",
"",
"@Extension",
"public class FailTestExtension implements TestExtensionPoint {",
" public FailTestExtension(String name) {}",
"",
" @Override",
" public String saySomething() { return \"I am a fail test extension\";}",
"}");

private ExtensionFactory extensionFactory; private ExtensionFactory extensionFactory;


@BeforeEach @BeforeEach
*/ */
@Test @Test
public void testCreateFailConstructor() { public void testCreateFailConstructor() {
assertThrows(PluginRuntimeException.class, () -> extensionFactory.create(FailTestExtension.class));
JavaFileObject object = JavaSources.compile(FailTestExtension);
JavaFileObjectClassLoader classLoader = new JavaFileObjectClassLoader();
Class<?> extensionClass = (Class<?>) classLoader.load(object).values().toArray()[0];
assertThrows(PluginRuntimeException.class, () -> extensionFactory.create(extensionClass));
} }


} }

+ 49
- 10
pf4j/src/test/java/org/pf4j/DefaultPluginFactoryTest.java View File

*/ */
package org.pf4j; package org.pf4j;


import com.google.testing.compile.JavaFileObjects;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.pf4j.test.AnotherFailTestPlugin;
import org.pf4j.test.AnotherTestPlugin;
import org.pf4j.test.FailTestPlugin;
import org.pf4j.test.JavaFileObjectClassLoader;
import org.pf4j.test.JavaFileObjectUtils;
import org.pf4j.test.JavaSources;
import org.pf4j.test.TestPlugin; import org.pf4j.test.TestPlugin;


import javax.tools.JavaFileObject;

import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertNull;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
*/ */
public class DefaultPluginFactoryTest { public class DefaultPluginFactoryTest {


public static final JavaFileObject FailTestPlugin = JavaFileObjects.forSourceLines("FailTestPlugin",
"package test;",
"import org.pf4j.Plugin;",
"",
"public class FailTestPlugin {",
"}");

public static final JavaFileObject AnotherFailTestPlugin = JavaFileObjects.forSourceLines("AnotherFailTestPlugin",
"package test;",
"import org.pf4j.Plugin;",
"",
"public class AnotherFailTestPlugin extends Plugin {",
" public AnotherFailTestPlugin() { super(null); }",
"}");

public static final JavaFileObject AnotherTestPlugin = JavaFileObjects.forSourceLines("AnotherTestPlugin",
"package test;",
"import org.pf4j.Plugin;",
"",
"public class AnotherTestPlugin extends Plugin {",
" public AnotherTestPlugin() { super(); }",
"}");

@Test @Test
public void testCreate() { public void testCreate() {
PluginDescriptor pluginDescriptor = mock(PluginDescriptor.class); PluginDescriptor pluginDescriptor = mock(PluginDescriptor.class);
@Test @Test
public void pluginConstructorNoParameters() { public void pluginConstructorNoParameters() {
PluginDescriptor pluginDescriptor = mock(PluginDescriptor.class); PluginDescriptor pluginDescriptor = mock(PluginDescriptor.class);
when(pluginDescriptor.getPluginClass()).thenReturn(AnotherTestPlugin.class.getName());
JavaFileObject object = JavaSources.compile(AnotherTestPlugin);
String pluginClassName = JavaFileObjectUtils.getClassName(object);
when(pluginDescriptor.getPluginClass()).thenReturn(pluginClassName);


PluginWrapper pluginWrapper = mock(PluginWrapper.class); PluginWrapper pluginWrapper = mock(PluginWrapper.class);
when(pluginWrapper.getDescriptor()).thenReturn(pluginDescriptor); when(pluginWrapper.getDescriptor()).thenReturn(pluginDescriptor);
when(pluginWrapper.getPluginClassLoader()).thenReturn(getClass().getClassLoader());
JavaFileObjectClassLoader classLoader = new JavaFileObjectClassLoader();
classLoader.load(AnotherTestPlugin);
when(pluginWrapper.getPluginClassLoader()).thenReturn(classLoader);


PluginFactory pluginFactory = new DefaultPluginFactory(); PluginFactory pluginFactory = new DefaultPluginFactory();


Plugin result = pluginFactory.create(pluginWrapper); Plugin result = pluginFactory.create(pluginWrapper);
assertNotNull(result); assertNotNull(result);
assertThat(result, instanceOf(AnotherTestPlugin.class));
assertEquals(pluginClassName, result.getClass().getName());
} }


@Test @Test
public void testCreateFail() { public void testCreateFail() {
PluginDescriptor pluginDescriptor = mock(PluginDescriptor.class); PluginDescriptor pluginDescriptor = mock(PluginDescriptor.class);
when(pluginDescriptor.getPluginClass()).thenReturn(FailTestPlugin.class.getName());
JavaFileObject object = JavaSources.compile(FailTestPlugin);
String pluginClassName = JavaFileObjectUtils.getClassName(object);
when(pluginDescriptor.getPluginClass()).thenReturn(pluginClassName);


PluginWrapper pluginWrapper = mock(PluginWrapper.class); PluginWrapper pluginWrapper = mock(PluginWrapper.class);
when(pluginWrapper.getDescriptor()).thenReturn(pluginDescriptor); when(pluginWrapper.getDescriptor()).thenReturn(pluginDescriptor);
when(pluginWrapper.getPluginClassLoader()).thenReturn(getClass().getClassLoader());
JavaFileObjectClassLoader classLoader = new JavaFileObjectClassLoader();
classLoader.load(FailTestPlugin);
when(pluginWrapper.getPluginClassLoader()).thenReturn(classLoader);


PluginFactory pluginFactory = new DefaultPluginFactory(); PluginFactory pluginFactory = new DefaultPluginFactory();


@Test @Test
public void testCreateFailConstructor() { public void testCreateFailConstructor() {
PluginDescriptor pluginDescriptor = mock(PluginDescriptor.class); PluginDescriptor pluginDescriptor = mock(PluginDescriptor.class);
when(pluginDescriptor.getPluginClass()).thenReturn(AnotherFailTestPlugin.class.getName());
JavaFileObject object = JavaSources.compile(AnotherFailTestPlugin);
String pluginClassName = JavaFileObjectUtils.getClassName(object);
when(pluginDescriptor.getPluginClass()).thenReturn(pluginClassName);


PluginWrapper pluginWrapper = mock(PluginWrapper.class); PluginWrapper pluginWrapper = mock(PluginWrapper.class);
when(pluginWrapper.getDescriptor()).thenReturn(pluginDescriptor); when(pluginWrapper.getDescriptor()).thenReturn(pluginDescriptor);
when(pluginWrapper.getPluginClassLoader()).thenReturn(getClass().getClassLoader());
JavaFileObjectClassLoader classLoader = new JavaFileObjectClassLoader();
classLoader.load(AnotherFailTestPlugin);
when(pluginWrapper.getPluginClassLoader()).thenReturn(classLoader);


PluginFactory pluginFactory = new DefaultPluginFactory(); PluginFactory pluginFactory = new DefaultPluginFactory();



+ 39
- 54
pf4j/src/test/java/org/pf4j/ExtensionAnnotationProcessorTest.java View File

package org.pf4j; package org.pf4j;


import com.google.testing.compile.Compilation; import com.google.testing.compile.Compilation;
import com.google.testing.compile.Compiler;
import com.google.testing.compile.JavaFileObjects; import com.google.testing.compile.JavaFileObjects;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.pf4j.processor.ExtensionAnnotationProcessor; import org.pf4j.processor.ExtensionAnnotationProcessor;
import org.pf4j.processor.LegacyExtensionStorage; import org.pf4j.processor.LegacyExtensionStorage;
import org.pf4j.test.JavaSources;


import javax.tools.JavaFileObject; import javax.tools.JavaFileObject;
import java.io.IOException;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
*/ */
public class ExtensionAnnotationProcessorTest { public class ExtensionAnnotationProcessorTest {


public static final JavaFileObject Greeting = JavaFileObjects.forSourceLines(
"Greeting",
"package test;",
"import org.pf4j.ExtensionPoint;",
"",
"public interface Greeting extends ExtensionPoint {",
" String getGreeting();",
"}");

public static final JavaFileObject WhazzupGreeting = JavaFileObjects.forSourceLines(
"WhazzupGreeting",
"package test;",
"import org.pf4j.Extension;",
"",
"@Extension",
"public class WhazzupGreeting implements Greeting {",
" @Override",
" public String getGreeting() {",
" return \"Whazzup\";",
" }",
"}");

public static final JavaFileObject WhazzupGreeting_NoExtensionPoint = JavaFileObjects.forSourceLines(
"WhazzupGreeting",
public static final JavaFileObject WhazzupGreeting_NoExtensionPoint = JavaFileObjects.forSourceLines("WhazzupGreeting",
"package test;", "package test;",
"import org.pf4j.Extension;", "import org.pf4j.Extension;",
"", "",
" }", " }",
"}"); "}");


public static final JavaFileObject SpinnakerExtension = JavaFileObjects.forSourceLines(
"SpinnakerExtension",
public static final JavaFileObject SpinnakerExtension = JavaFileObjects.forSourceLines("SpinnakerExtension",
"package test;", "package test;",
"", "",
"import org.pf4j.Extension;", "import org.pf4j.Extension;",
"public @interface SpinnakerExtension {", "public @interface SpinnakerExtension {",
"}"); "}");


public static final JavaFileObject WhazzupGreeting_SpinnakerExtension = JavaFileObjects.forSourceLines(
"WhazzupGreeting",
public static final JavaFileObject WhazzupGreeting_SpinnakerExtension = JavaFileObjects.forSourceLines("WhazzupGreeting",
"package test;", "package test;",
"", "",
"@SpinnakerExtension", "@SpinnakerExtension",
"}"); "}");


/** /**
* The same like {@link #SpinnakerExtension} but without {@code Extension} annotation.
* The same as {@link #SpinnakerExtension} but without {@code Extension} annotation.
*/ */
public static final JavaFileObject SpinnakerExtension_NoExtension = JavaFileObjects.forSourceLines(
"SpinnakerExtension",
public static final JavaFileObject SpinnakerExtension_NoExtension = JavaFileObjects.forSourceLines("SpinnakerExtension",
"package test;", "package test;",
"", "",
"import org.pf4j.Extension;", "import org.pf4j.Extension;",
"public @interface SpinnakerExtension {", "public @interface SpinnakerExtension {",
"}"); "}");


private ExtensionAnnotationProcessor annotationProcessor;

@BeforeEach
public void setUp() throws IOException {
annotationProcessor = new ExtensionAnnotationProcessor();
}

@Test @Test
public void getSupportedAnnotationTypes() { public void getSupportedAnnotationTypes() {
ExtensionAnnotationProcessor instance = new ExtensionAnnotationProcessor();
Set<String> result = instance.getSupportedAnnotationTypes();
Set<String> result = annotationProcessor.getSupportedAnnotationTypes();
assertEquals(1, result.size()); assertEquals(1, result.size());
assertEquals("*", result.iterator().next()); assertEquals("*", result.iterator().next());
} }


@Test @Test
public void getSupportedOptions() { public void getSupportedOptions() {
ExtensionAnnotationProcessor instance = new ExtensionAnnotationProcessor();
Set<String> result = instance.getSupportedOptions();
Set<String> result = annotationProcessor.getSupportedOptions();
assertEquals(2, result.size()); assertEquals(2, result.size());
} }


@Test @Test
public void options() { public void options() {
ExtensionAnnotationProcessor processor = new ExtensionAnnotationProcessor();
Compilation compilation = javac().withProcessors(processor).withOptions("-Ab=2", "-Ac=3")
.compile(Greeting, WhazzupGreeting);
assertEquals(compilation.status(), Compilation.Status.SUCCESS);
Compilation compilation = compiler().withOptions("-Ab=2", "-Ac=3")
.compile(JavaSources.Greeting, JavaSources.WhazzupGreeting);
assertEquals(Compilation.Status.SUCCESS, compilation.status());
Map<String, String> options = new HashMap<>(); Map<String, String> options = new HashMap<>();
options.put("b", "2"); options.put("b", "2");
options.put("c", "3"); options.put("c", "3");
assertEquals(options, processor.getProcessingEnvironment().getOptions());
assertEquals(options, annotationProcessor.getProcessingEnvironment().getOptions());
} }


@Test @Test
public void storage() { public void storage() {
ExtensionAnnotationProcessor processor = new ExtensionAnnotationProcessor();
Compilation compilation = javac().withProcessors(processor).compile(Greeting, WhazzupGreeting);
assertEquals(compilation.status(), Compilation.Status.SUCCESS);
assertEquals(processor.getStorage().getClass(), LegacyExtensionStorage.class);
Compilation compilation = compile(JavaSources.Greeting, JavaSources.WhazzupGreeting);
assertEquals(Compilation.Status.SUCCESS, compilation.status());
assertEquals(annotationProcessor.getStorage().getClass(), LegacyExtensionStorage.class);
} }


@Test @Test
public void compileWithoutError() { public void compileWithoutError() {
ExtensionAnnotationProcessor processor = new ExtensionAnnotationProcessor();
Compilation compilation = javac().withProcessors(processor).compile(Greeting, WhazzupGreeting);
Compilation compilation = compile(JavaSources.Greeting, JavaSources.WhazzupGreeting);
assertThat(compilation).succeededWithoutWarnings(); assertThat(compilation).succeededWithoutWarnings();
} }


@Test @Test
public void compileWithError() { public void compileWithError() {
ExtensionAnnotationProcessor processor = new ExtensionAnnotationProcessor();
Compilation compilation = javac().withProcessors(processor).compile(Greeting, WhazzupGreeting_NoExtensionPoint);
Compilation compilation = compile(JavaSources.Greeting, WhazzupGreeting_NoExtensionPoint);
assertThat(compilation).failed(); assertThat(compilation).failed();
assertThat(compilation).hadErrorContaining("it doesn't implement ExtensionPoint") assertThat(compilation).hadErrorContaining("it doesn't implement ExtensionPoint")
.inFile(WhazzupGreeting_NoExtensionPoint) .inFile(WhazzupGreeting_NoExtensionPoint)


@Test @Test
public void getExtensions() { public void getExtensions() {
ExtensionAnnotationProcessor processor = new ExtensionAnnotationProcessor();
Compilation compilation = javac().withProcessors(processor).compile(Greeting, WhazzupGreeting);
Compilation compilation = compile(JavaSources.Greeting, JavaSources.WhazzupGreeting);
assertThat(compilation).succeededWithoutWarnings(); assertThat(compilation).succeededWithoutWarnings();
Map<String, Set<String>> extensions = new HashMap<>(); Map<String, Set<String>> extensions = new HashMap<>();
extensions.put("test.Greeting", new HashSet<>(Collections.singletonList("test.WhazzupGreeting"))); extensions.put("test.Greeting", new HashSet<>(Collections.singletonList("test.WhazzupGreeting")));
assertEquals(extensions, processor.getExtensions());
assertEquals(extensions, annotationProcessor.getExtensions());
} }


@Test @Test
public void compileNestedExtensionAnnotation() { public void compileNestedExtensionAnnotation() {
ExtensionAnnotationProcessor processor = new ExtensionAnnotationProcessor();
Compilation compilation = javac().withProcessors(processor).compile(Greeting, SpinnakerExtension, WhazzupGreeting_SpinnakerExtension);
Compilation compilation = compile(JavaSources.Greeting, SpinnakerExtension, WhazzupGreeting_SpinnakerExtension);
assertThat(compilation).succeededWithoutWarnings(); assertThat(compilation).succeededWithoutWarnings();
Map<String, Set<String>> extensions = new HashMap<>(); Map<String, Set<String>> extensions = new HashMap<>();
extensions.put("test.Greeting", new HashSet<>(Collections.singletonList("test.WhazzupGreeting"))); extensions.put("test.Greeting", new HashSet<>(Collections.singletonList("test.WhazzupGreeting")));
assertEquals(extensions, processor.getExtensions());
assertEquals(extensions, annotationProcessor.getExtensions());
}

private Compiler compiler() {
return javac().withProcessors(annotationProcessor);
}

private Compilation compile(JavaFileObject... sources) {
return compiler().compile(sources);
} }


} }

+ 1
- 2
pf4j/src/test/java/org/pf4j/SingletonExtensionFactoryTest.java View File

import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.pf4j.test.FailTestExtension;
import org.pf4j.test.TestExtension; import org.pf4j.test.TestExtension;


import java.io.File; import java.io.File;


@Test @Test
public void createNewEachTime() { public void createNewEachTime() {
ExtensionFactory extensionFactory = new SingletonExtensionFactory(pluginManager, FailTestExtension.class.getName());
ExtensionFactory extensionFactory = new SingletonExtensionFactory(pluginManager, "FailTestExtension.class");
Object extensionOne = extensionFactory.create(TestExtension.class); Object extensionOne = extensionFactory.create(TestExtension.class);
Object extensionTwo = extensionFactory.create(TestExtension.class); Object extensionTwo = extensionFactory.create(TestExtension.class);
assertNotSame(extensionOne, extensionTwo); assertNotSame(extensionOne, extensionTwo);

+ 0
- 32
pf4j/src/test/java/org/pf4j/test/AnotherFailTestPlugin.java View File

/*
* Copyright (C) 2012-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.pf4j.test;

import org.pf4j.Plugin;

/**
* A wrong {@link org.pf4j.Plugin}.
* It's wrong because it calls super constructor with {@code null} for ({@link org.pf4j.PluginWrapper} parameter).
*
* @author Mario Franco
*/
public class AnotherFailTestPlugin extends Plugin {

public AnotherFailTestPlugin() {
super(null);
}

}

+ 0
- 34
pf4j/src/test/java/org/pf4j/test/AnotherTestPlugin.java View File

/*
* Copyright (C) 2012-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.pf4j.test;

import org.pf4j.Plugin;

/**
* A simple {@link Plugin}.
*
* In real applications you don't need to create a plugin like this if you are not interested in lifecycle events.
* {@code PF4J} will automatically create a plugin similar to this (empty / dummy) if no class plugin is specified.
*
* @author Decebal Suiu
*/
public class AnotherTestPlugin extends Plugin {

public AnotherTestPlugin() {
super();
}

}

+ 5
- 15
pf4j/src/test/java/org/pf4j/test/DefaultClassDataProvider.java View File

*/ */
package org.pf4j.test; package org.pf4j.test;


import java.io.ByteArrayOutputStream;
import com.google.common.io.ByteStreams;

import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream;


/** /**
* Get class data from the class path. * Get class data from the class path.
throw new RuntimeException("Cannot find class data"); throw new RuntimeException("Cannot find class data");
} }


try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
copyStream(classDataStream, outputStream);
return outputStream.toByteArray();
try {
return ByteStreams.toByteArray(classDataStream);
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException(e.getMessage(), e);
}
}

private void copyStream(InputStream in, OutputStream out) throws IOException {
byte[] buffer = new byte[1024];

int bytesRead;
while ((bytesRead = in.read(buffer)) != -1) {
out.write(buffer, 0, bytesRead);
throw new IllegalStateException(e);
} }
} }



+ 0
- 37
pf4j/src/test/java/org/pf4j/test/FailTestExtension.java View File

/*
* Copyright (C) 2012-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.pf4j.test;

import org.pf4j.Extension;

/**
* A wrong {@link org.pf4j.Extension}.
* It's wrong because it doesn't contain a constructor with empty parameters (or only the default constructor).
*
* @author Mario Franco
*/
@Extension
public class FailTestExtension implements TestExtensionPoint {

public FailTestExtension(String name) {
}

@Override
public String saySomething() {
return "I am a fail test extension";
}

}

+ 0
- 26
pf4j/src/test/java/org/pf4j/test/FailTestPlugin.java View File

/*
* Copyright (C) 2012-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.pf4j.test;

/**
* A wrong {@link org.pf4j.Plugin}.
* It's wrong because it doesn't extends {@link org.pf4j.Plugin}.
*
* @author Mario Franco
*/
public class FailTestPlugin {

}

+ 73
- 0
pf4j/src/test/java/org/pf4j/test/JavaFileObjectClassLoader.java View File

/*
* Copyright (C) 2012-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.pf4j.test;

import javax.tools.JavaFileObject;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
* {@link ClassLoader} that loads {@link JavaFileObject.Kind#CLASS}s.
* If {@code JavaFileObject} type is {@link JavaFileObject.Kind#SOURCE} them the source is compiled.
*
* @author Decebal Suiu
*/
public class JavaFileObjectClassLoader extends ClassLoader {

public Map<String, Class<?>> load(JavaFileObject... objects) {
return load(Arrays.asList(objects));
}

public Map<String, Class<?>> load(List<JavaFileObject> objects) {
Objects.requireNonNull(objects);

List<JavaFileObject> mutableObjects = new ArrayList<>(objects);

// Sort generated ".class" by lastModified field
mutableObjects.sort(Comparator.comparingLong(JavaFileObject::getLastModified));

// Compile Java sources (if exists)
for (int i = 0; i < mutableObjects.size(); i++) {
JavaFileObject object = mutableObjects.get(i);
if (object.getKind() == JavaFileObject.Kind.CLASS) {
continue;
}

if (object.getKind() == JavaFileObject.Kind.SOURCE) {
mutableObjects.set(i, JavaSources.compile(object));
} else {
throw new IllegalStateException("Type " + object.getKind() + " is not supported");
}
}

// Load objects
Map<String, Class<?>> loadedClasses = new HashMap<>();
for (JavaFileObject object : mutableObjects) {
String className = JavaFileObjectUtils.getClassName(object);
byte[] data = JavaFileObjectUtils.getAllBytes(object);
Class<?> loadedClass = defineClass(className, data, 0, data.length);
loadedClasses.put(className, loadedClass);
}

return loadedClasses;
}

}

+ 62
- 0
pf4j/src/test/java/org/pf4j/test/JavaFileObjectDataProvider.java View File

/*
* Copyright (C) 2012-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.pf4j.test;

import javax.tools.FileObject;
import javax.tools.JavaFileObject;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
* Get class data from {@link JavaFileObject}.
* If {@code JavaFileObject} type is {@link JavaFileObject.Kind#SOURCE} them the source is compiled.
*
* @author Decebal Suiu
*/
public class JavaFileObjectDataProvider implements ClassDataProvider {

private final Map<String, JavaFileObject> classes;

public JavaFileObjectDataProvider(Map<String, JavaFileObject> classes) {
this.classes = classes;
}

public static JavaFileObjectDataProvider of(List<JavaFileObject> objects) {
List<JavaFileObject> tmp = new ArrayList<>(objects.size());
for (JavaFileObject object : objects) {
if (object.getKind() == JavaFileObject.Kind.CLASS) {
tmp.add(object);
} else if (object.getKind() == JavaFileObject.Kind.SOURCE) {
tmp.add(JavaSources.compile(object));
} else {
throw new IllegalStateException("Type " + object.getKind() + " is not supported");
}
}

// TODO JavaFileObjectUtils.getClassName() ?!
Map<String, JavaFileObject> classes = tmp.stream().collect(Collectors.toMap(FileObject::getName, c -> c));

return new JavaFileObjectDataProvider(classes);
}

@Override
public byte[] getClassData(String className) {
return JavaFileObjectUtils.getAllBytes(classes.get(className));
}

}

+ 52
- 0
pf4j/src/test/java/org/pf4j/test/JavaFileObjectUtils.java View File

/*
* Copyright (C) 2012-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.pf4j.test;

import com.google.common.io.ByteStreams;

import javax.tools.JavaFileObject;
import java.io.IOException;
import java.io.InputStream;

/**
* @author Decebal Suiu
*/
public class JavaFileObjectUtils {

private JavaFileObjectUtils() {}

public static String getClassName(JavaFileObject object) {
if (object.getKind() != JavaFileObject.Kind.CLASS) {
throw new IllegalStateException("Only Kind.CLASS is supported");
}

String name = object.getName();
// Remove "/CLASS_OUT/" from head and ".class" from tail
name = name.substring(14, name.length() - 6);
name = name.replace('/', '.');

return name;
}

public static byte[] getAllBytes(JavaFileObject object) {
try (InputStream in = object.openInputStream()) {
return ByteStreams.toByteArray(in);
} catch (IOException e) {
throw new IllegalStateException(e);
}
}

}

+ 64
- 0
pf4j/src/test/java/org/pf4j/test/JavaSources.java View File

/*
* Copyright (C) 2012-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.pf4j.test;

import com.google.testing.compile.JavaFileObjects;

import javax.tools.JavaFileObject;
import java.util.List;

import static com.google.testing.compile.Compiler.javac;

/**
* Keep common Java sources (useful in many tests).
* For Java 13+ is recommended to use Text Block feature (it's more clear).
*
* @author Decebal Suiu
*/
public class JavaSources {

public static final JavaFileObject Greeting = JavaFileObjects.forSourceLines("Greeting",
"package test;",
"import org.pf4j.ExtensionPoint;",
"",
"public interface Greeting extends ExtensionPoint {",
" String getGreeting();",
"}");

public static final JavaFileObject WhazzupGreeting = JavaFileObjects.forSourceLines("WhazzupGreeting",
"package test;",
"import org.pf4j.Extension;",
"",
"@Extension",
"public class WhazzupGreeting implements Greeting {",
" @Override",
" public String getGreeting() {",
" return \"Whazzup\";",
" }",
"}");

/**
* Compile a list of sources using javac compiler.
*/
public static List<JavaFileObject> compileAll(JavaFileObject... sources) {
return javac().compile(sources).generatedFiles();
}

public static JavaFileObject compile(JavaFileObject source) {
return compileAll(source).get(0);
}

}

+ 1
- 2
pf4j/src/test/java/org/pf4j/test/TestPlugin.java View File



/** /**
* A simple {@link Plugin}. * A simple {@link Plugin}.
*
* In real applications you don't need to create a plugin like this if you are not interested in lifecycle events. * In real applications you don't need to create a plugin like this if you are not interested in lifecycle events.
* {@codes PF4J} will automatically create a plugin similar to this (empty / dummy) if no class plugin is specified.
* {@code PF4J} will automatically create a plugin similar to this (empty / dummy) if no class plugin is specified.
* *
* @author Mario Franco * @author Mario Franco
*/ */

Loading…
Cancel
Save