Skip to content

Commit

Permalink
[#8965] Add SharedTestLifeCycle to simplify plugin integration tests
Browse files Browse the repository at this point in the history
  • Loading branch information
emeroad committed Jun 23, 2022
1 parent 7890a14 commit c67ce21
Show file tree
Hide file tree
Showing 8 changed files with 268 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public class ExecuteSharedThread {
private static final TaggedLogger logger = TestLogger.getLogger();

private final Thread thread;

private final ExecuteSharedThreadRunnable runnable;
private final String testClazzName;
private final ClassLoader testClassLoader;

Expand All @@ -50,7 +50,9 @@ public ExecuteSharedThread(String testClazzName, ClassLoader testClassLoader) {
this.testClazzName = Objects.requireNonNull(testClazzName, "testClazzName");
this.testClassLoader = Objects.requireNonNull(testClassLoader, "testClassLoader");

thread = new Thread(new ExecuteSharedThreadRunnable());
this.runnable = new ExecuteSharedThreadRunnable();

thread = new Thread(runnable);
thread.setName(testClazzName + "-Shared-Thread");
thread.setContextClassLoader(testClassLoader);
thread.setDaemon(true);
Expand All @@ -60,6 +62,14 @@ void startBefore() {
thread.start();
}

public SharedTestLifeCycleWrapper getSharedClassWrapper() {
return runnable.sharedTestLifeCycleWrapper;
}

public Throwable getRunnableError() {
return runnable.throwable;
}

boolean awaitBeforeCompleted(long timeout, TimeUnit unit) {
if (!thread.isAlive()) {
throw new IllegalStateException("Thread is not alive.");
Expand Down Expand Up @@ -123,26 +133,43 @@ boolean join(long millis) {
}

private class ExecuteSharedThreadRunnable implements Runnable {

private volatile SharedTestLifeCycleWrapper sharedTestLifeCycleWrapper;
private volatile Throwable throwable;
@Override
public void run() {
final Class<?> testClazz;
Class<?> testClazz = null;
try {
testClazz = loadClass();

logger.debug("Execute testClazz:{} cl:{}", testClazz.getName(), testClazz.getClassLoader());

sharedTestLifeCycleWrapper = SharedTestLifeCycleWrapper.newVersionTestLifeCycleWrapper(testClazz);
if (sharedTestLifeCycleWrapper != null) {
sharedTestLifeCycleWrapper.beforeAll();
}

runBeforeSharedClass(testClazz);
Map<String, Object> result = getProperties(testClazz);
if (result.size() > 0) {
properties.putAll(result);
// bind parameter
for (Map.Entry<String, Object> entry : result.entrySet()) {
if (entry.getValue() != null) {
properties.put(entry.getKey(), entry.getValue());
}
}
}
} catch (Throwable th) {
logger.warn("{} testclass error", testClazzName, th);
throwable = th;
} finally {
setBeforeCompleted();
}

awaitAfterStart();
runAfterSharedClass(testClazz);
if (sharedTestLifeCycleWrapper != null) {
sharedTestLifeCycleWrapper.afterAll();
}
}

private Class<?> loadClass() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
public class SharedPinpointPluginTest {
private static final TaggedLogger logger = TestLogger.getLogger();

public static void main(String[] args) throws Exception {
public static void main(String[] args) throws Throwable {
final String mavenDependencyResolverClassPaths = System.getProperty(SharedPluginTestConstants.MAVEN_DEPENDENCY_RESOLVER_CLASS_PATHS);
if (mavenDependencyResolverClassPaths == null) {
logger.error("mavenDependencyResolverClassPaths must not be empty");
Expand Down Expand Up @@ -202,7 +202,7 @@ private void logTestInformation() {
}
}

public void execute() throws Exception {
public void execute() throws Throwable {
logTestInformation();
ClassLoader mavenDependencyResolverClassLoader = new ChildFirstClassLoader(URLUtils.fileToUrls(mavenDependencyResolverClassPaths));
File testClazzLocation = new File(testLocation);
Expand All @@ -211,7 +211,7 @@ public void execute() throws Exception {
executes(testInfos);
}

private void executes(List<TestInfo> testInfos) {
private void executes(List<TestInfo> testInfos) throws Throwable {
if (!CollectionUtils.hasLength(testInfos)) {
return;
}
Expand All @@ -222,13 +222,18 @@ private void executes(List<TestInfo> testInfos) {

executeSharedThread.startBefore();
executeSharedThread.awaitBeforeCompleted(10, TimeUnit.MINUTES);
Throwable runnableError = executeSharedThread.getRunnableError();
if (runnableError != null) {
throw runnableError;
}
Properties properties = executeSharedThread.getProperties();
if (logger.isDebugEnabled()) {
logger.debug("sharedThread properties:{}", properties);
}

final SharedTestLifeCycleWrapper sharedTestLifeCycleWrapper = executeSharedThread.getSharedClassWrapper();
for (TestInfo testInfo : testInfos) {
execute(testInfo, properties);
execute(testInfo, properties, sharedTestLifeCycleWrapper);
}

executeSharedThread.startAfter();
Expand All @@ -248,7 +253,7 @@ private ClassLoader createTestClassLoader(TestInfo testInfo) {
return testClassLoader;
}

private void execute(final TestInfo testInfo, final Properties properties) {
private void execute(final TestInfo testInfo, final Properties properties, SharedTestLifeCycleWrapper sharedTestLifeCycleWrapper) {
try {
final ClassLoader testClassLoader = createTestClassLoader(testInfo);

Expand All @@ -257,11 +262,16 @@ private void execute(final TestInfo testInfo, final Properties properties) {
public void run() {
final Class<?> testClazz = loadClass();
logger.debug("testClazz:{} cl:{}", testClazz.getName(), testClazz.getClassLoader());

SharedTestBeforeAllInvoker invoker = new SharedTestBeforeAllInvoker(testClazz);
try {
invoker.invoke(sharedTestLifeCycleWrapper.getLifeCycleResult());
} catch (Throwable th) {
logger.error(th, "invoker setter method failed. testClazz:{} testId:{}", testClazzName, testInfo.getTestId());
}
try {
MethodUtils.invokeSetMethod(testClazz, properties);
} catch (Exception e) {
logger.warn(e, "invoker setter method failed. testClazz:{} testId:{}", testClazzName, testInfo.getTestId());
logger.error(e, "invoker setter method failed. testClazz:{} testId:{}", testClazzName, testInfo.getTestId());
}

try {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package com.navercorp.pinpoint.test.plugin.shared;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Properties;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
* @author emeroad
*/
public class SharedTestBeforeAllInvoker {
private final Class<?> testClazz;

public SharedTestBeforeAllInvoker(Class<?> testClazz) {
this.testClazz = Objects.requireNonNull(testClazz, "testClazz");
}


List<Method> getMethods(Class<?> testClazz, Predicate<Method> predicate) {
Method[] methods = testClazz.getMethods();
Stream<Method> stream = Arrays.stream(methods);
return stream.filter(predicate)
.collect(Collectors.toList());
}

boolean beforeAllFilter(Method method) {
if (method.getAnnotation(SharedTestBeforeAllResult.class) == null) {
return false;
}
Class<?>[] parameterTypes = method.getParameterTypes();
if (parameterTypes.length != 1) {
return false;
}
return Properties.class.isAssignableFrom(parameterTypes[0]);
}

public void invoke(Properties properties) throws Throwable {
List<Method> methods = getMethods(testClazz, this::beforeAllFilter);
for (Method method : methods) {
method.invoke(testClazz, properties);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Copyright 2021 NAVER Corp.
*
* 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 com.navercorp.pinpoint.test.plugin.shared;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* @author emeroad
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface SharedTestBeforeAllResult {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.navercorp.pinpoint.test.plugin.shared;

import java.util.Properties;

/**
* @author emeroad
*/
public interface SharedTestLifeCycle {
Properties beforeAll();
void afterAll();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Copyright 2021 NAVER Corp.
*
* 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 com.navercorp.pinpoint.test.plugin.shared;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* @author emeroad
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface SharedTestLifeCycleClass {
Class<? extends SharedTestLifeCycle> value();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package com.navercorp.pinpoint.test.plugin.shared;

import java.util.Objects;
import java.util.Properties;

/**
* @author emeroad
*/
public class SharedTestLifeCycleWrapper {
private final SharedTestLifeCycle sharedTestLifecycle;
private Properties lifeCycleResult;

private static Class<? extends SharedTestLifeCycle> getVersionTestLifeCycle(final Class<?> testClazz) {
SharedTestLifeCycleClass sharedTestLifeCycleClass = testClazz.getAnnotation(SharedTestLifeCycleClass.class);
if (sharedTestLifeCycleClass == null) {
return null;
}
return sharedTestLifeCycleClass.value();
}

public static SharedTestLifeCycleWrapper newVersionTestLifeCycleWrapper(final Class<?> testClazz) {
Class<? extends SharedTestLifeCycle> versionTestClazz = getVersionTestLifeCycle(testClazz);
if (versionTestClazz == null) {
return null;
}
try {
return new SharedTestLifeCycleWrapper(versionTestClazz.newInstance());
} catch (InstantiationException | IllegalAccessException e) {
throw new RuntimeException(e);
}
}

public SharedTestLifeCycleWrapper(SharedTestLifeCycle sharedTestLifecycle) {
this.sharedTestLifecycle = Objects.requireNonNull(sharedTestLifecycle, "versionTestLifecycle");
}

public Properties getLifeCycleResult() {
return lifeCycleResult;
}

public void beforeAll() {
lifeCycleResult = sharedTestLifecycle.beforeAll();
}

public void afterAll() {
sharedTestLifecycle.afterAll();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.navercorp.pinpoint.test.plugin.shared;

import org.junit.Assert;
import org.junit.Test;

import java.lang.reflect.Method;
import java.util.List;
import java.util.Properties;

public class SharedTestBeforeAllInvokerTest {

@Test
public void invoke() throws Throwable {
SharedTestBeforeAllInvoker mock = new SharedTestBeforeAllInvoker(TestClass.class);

List<Method> methods = mock.getMethods(TestClass.class, mock::beforeAllFilter);
Assert.assertEquals(1, methods.size());
Assert.assertEquals("setBeforeAllResult", methods.get(0).getName());

mock.invoke(new Properties());
Assert.assertNotNull(TestClass.properties);
}

@Test
public void invoke_extends() throws Throwable {
SharedTestBeforeAllInvoker mock = new SharedTestBeforeAllInvoker(ChildTestClass.class);

List<Method> methods = mock.getMethods(ChildTestClass.class, mock::beforeAllFilter);
Assert.assertEquals(1, methods.size());
Assert.assertEquals("setBeforeAllResult", methods.get(0).getName());

mock.invoke(new Properties());
Assert.assertNotNull(ChildTestClass.properties);
}

public static class TestClass {
static Properties properties;

@SharedTestBeforeAllResult
public static void setBeforeAllResult(Properties properties) {
TestClass.properties = properties;
}

public static void fake(Properties p) {

}
}

public static class ChildTestClass extends TestClass {

}
}

0 comments on commit c67ce21

Please sign in to comment.