This is an automated email from the ASF dual-hosted git repository.
klund pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/geode.git
The following commit(s) were added to refs/heads/develop by this push:
new 2959d17 GEODE-6033: Add VMEventListener for DUnit Rules (#3161)
2959d17 is described below
commit 2959d179a5993c2b6921d6013c1855e3482ec6e1
Author: Kirk Lund <[email protected]>
AuthorDate: Wed Feb 6 09:33:43 2019 -0800
GEODE-6033: Add VMEventListener for DUnit Rules (#3161)
A DUnit Rule (or even a test) can now register a VMEventListener:
VM.getVMEventListenerRegistry().addVMEventListener(vmEventListener);
VMEventListeners will receive the following notifications:
void afterCreateVM(VM vm);
void beforeBounceVM(VM vm);
void afterBounceVM(VM vm);
DUnit Rules can use these callbacks in order to support dynamic
creation or bouncing of VMs.
Add junitparams dependency to geode-dunit.
---
geode-dunit/build.gradle | 1 +
.../tests/VmEventListenerDistributedTest.java | 138 +++++++++++++++++
.../java/org/apache/geode/test/dunit/Host.java | 12 +-
.../main/java/org/apache/geode/test/dunit/VM.java | 26 +++-
.../apache/geode/test/dunit/VMEventListener.java | 44 ++++++
.../geode/test/dunit/VMEventListenerRegistry.java | 31 ++++
.../geode/test/dunit/internal/DUnitHost.java | 10 +-
.../geode/test/dunit/internal/DUnitLauncher.java | 6 +-
.../geode/test/dunit/internal/VMEventNotifier.java | 75 ++++++++++
.../test/dunit/rules/AbstractDistributedRule.java | 75 +++++++---
.../test/dunit/internal/VMEventNotifierTest.java | 164 +++++++++++++++++++++
geode-dunit/src/test/resources/expected-pom.xml | 5 +
12 files changed, 554 insertions(+), 33 deletions(-)
diff --git a/geode-dunit/build.gradle b/geode-dunit/build.gradle
index 7d29fec..269fb80 100755
--- a/geode-dunit/build.gradle
+++ b/geode-dunit/build.gradle
@@ -50,6 +50,7 @@ dependencies {
compile('org.assertj:assertj-core')
compile('org.mockito:mockito-core')
compile('org.awaitility:awaitility')
+ compile('pl.pragmatists:JUnitParams')
compile('junit:junit') {
exclude module: 'hamcrest-core'
diff --git
a/geode-dunit/src/distributedTest/java/org/apache/geode/test/dunit/rules/tests/VmEventListenerDistributedTest.java
b/geode-dunit/src/distributedTest/java/org/apache/geode/test/dunit/rules/tests/VmEventListenerDistributedTest.java
new file mode 100644
index 0000000..2eb8e7f
--- /dev/null
+++
b/geode-dunit/src/distributedTest/java/org/apache/geode/test/dunit/rules/tests/VmEventListenerDistributedTest.java
@@ -0,0 +1,138 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
contributor license
+ * agreements. See the NOTICE file distributed with this work for additional
information regarding
+ * copyright ownership. The ASF licenses this file to You 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.apache.geode.test.dunit.rules.tests;
+
+import static org.apache.geode.test.dunit.VM.getVM;
+import static org.apache.geode.test.dunit.VM.getVMCount;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.InOrder;
+
+import org.apache.geode.test.dunit.VM;
+import org.apache.geode.test.dunit.VMEventListener;
+import org.apache.geode.test.dunit.rules.DistributedRule;
+
+/**
+ * Distributed tests for {@link VMEventListener} callbacks.
+ */
+public class VmEventListenerDistributedTest {
+
+ @Rule
+ public AccessibleDistributedRule distributedRule = spy(new
AccessibleDistributedRule());
+
+ private ArgumentCaptor<VM> vmCaptor;
+ private int beforeVmCount;
+ private VM vm;
+
+ @Before
+ public void setUp() {
+ vmCaptor = ArgumentCaptor.forClass(VM.class);
+ beforeVmCount = getVMCount();
+ vm = getVM(0);
+ }
+
+ @Test
+ public void afterCreateVmIsInvokedForNewlyCreatedVm() {
+ getVM(beforeVmCount);
+
+ verify(distributedRule).afterCreateVM(eq(getVM(beforeVmCount)));
+ }
+
+ @Test
+ public void afterCreateVmIsInvokedForMultipleNewlyCreatedVms() {
+ // getVM implicitly creates intervening VMs between beforeVmCount and
beforeVmCount+2
+ getVM(beforeVmCount + 2);
+
+ verify(distributedRule, times(3)).afterCreateVM(vmCaptor.capture());
+ assertThat(vmCaptor.getAllValues()).containsExactly(getVM(beforeVmCount),
+ getVM(beforeVmCount + 1), getVM(beforeVmCount + 2));
+ }
+
+ @Test
+ public void beforeBounceVmIsInvokedWhenInvokingBounce() {
+ vm.bounce();
+
+ verify(distributedRule).beforeBounceVM(eq(vm));
+ }
+
+ @Test
+ public void afterBounceVmIsInvokedWhenInvokingBounce() {
+ vm.bounce();
+
+ verify(distributedRule).afterBounceVM(eq(vm));
+ }
+
+ @Test
+ public void beforeAndAfterBounceVmAreInvokedInOrderWhenInvokingBounce() {
+ vm.bounce();
+
+ InOrder inOrder = inOrder(distributedRule);
+ inOrder.verify(distributedRule).beforeBounceVM(eq(vm));
+ inOrder.verify(distributedRule).afterBounceVM(eq(vm));
+ }
+
+ @Test
+ public void beforeBounceVmIsInvokedWhenInvokingBounceForcibly() {
+ vm.bounceForcibly();
+
+ verify(distributedRule).beforeBounceVM(eq(vm));
+ }
+
+ @Test
+ public void afterBounceVmIsInvokedWhenInvokingBounceForcibly() {
+ vm.bounceForcibly();
+
+ verify(distributedRule).afterBounceVM(eq(vm));
+ }
+
+ @Test
+ public void
beforeAndAfterBounceVmAreInvokedInOrderWhenInvokingBounceForcibly() {
+ vm.bounceForcibly();
+
+ InOrder inOrder = inOrder(distributedRule);
+ inOrder.verify(distributedRule).beforeBounceVM(eq(vm));
+ inOrder.verify(distributedRule).afterBounceVM(eq(vm));
+ }
+
+ /**
+ * Increase visibility of {@link VMEventListener} callbacks in {@link
DistributedRule}.
+ */
+ private static class AccessibleDistributedRule extends DistributedRule {
+
+ @Override
+ public void afterCreateVM(VM vm) {
+ // exposed for spy
+ }
+
+ @Override
+ public void beforeBounceVM(VM vm) {
+ // exposed for spy
+ }
+
+ @Override
+ public void afterBounceVM(VM vm) {
+ // exposed for spy
+ }
+ }
+}
diff --git a/geode-dunit/src/main/java/org/apache/geode/test/dunit/Host.java
b/geode-dunit/src/main/java/org/apache/geode/test/dunit/Host.java
index 9821b19c..685d940 100755
--- a/geode-dunit/src/main/java/org/apache/geode/test/dunit/Host.java
+++ b/geode-dunit/src/main/java/org/apache/geode/test/dunit/Host.java
@@ -21,6 +21,7 @@ import java.util.List;
import org.apache.geode.test.dunit.internal.ChildVMLauncher;
import org.apache.geode.test.dunit.internal.ProcessHolder;
import org.apache.geode.test.dunit.internal.RemoteDUnitVMIF;
+import org.apache.geode.test.dunit.internal.VMEventNotifier;
import org.apache.geode.test.version.VersionManager;
/**
@@ -31,7 +32,6 @@ import org.apache.geode.test.version.VersionManager;
* Additionally, it provides access to the Java RMI registry that runs on the
host. By default, an
* RMI registry is only started on the host on which Hydra's Master VM runs.
RMI registries may be
* started on other hosts via additional Hydra configuration.
- *
*/
@SuppressWarnings("serial")
public abstract class Host implements Serializable {
@@ -47,6 +47,8 @@ public abstract class Host implements Serializable {
/** The VMs that run on this host */
private final List<VM> vms;
+ private final transient VMEventNotifier vmEventNotifier;
+
/**
* Returns the number of known hosts
*/
@@ -101,7 +103,7 @@ public abstract class Host implements Serializable {
/**
* Creates a new {@code Host} with the given name
*/
- protected Host(String hostName) {
+ protected Host(String hostName, VMEventNotifier vmEventNotifier) {
if (hostName == null) {
String message = "Cannot create a Host with a null name";
throw new NullPointerException(message);
@@ -109,6 +111,7 @@ public abstract class Host implements Serializable {
this.hostName = hostName;
vms = new ArrayList<>();
+ this.vmEventNotifier = vmEventNotifier;
}
/**
@@ -168,6 +171,7 @@ public abstract class Host implements Serializable {
ChildVMLauncher childVMLauncher) {
VM vm = new VM(this, vmid, client, processHolder, childVMLauncher);
vms.add(vm);
+ vmEventNotifier.notifyAfterCreateVM(vm);
}
public static VM getLocator() {
@@ -207,4 +211,8 @@ public abstract class Host implements Serializable {
public int hashCode() {
return getHostName().hashCode();
}
+
+ VMEventNotifier getVMEventNotifier() {
+ return vmEventNotifier;
+ }
}
diff --git a/geode-dunit/src/main/java/org/apache/geode/test/dunit/VM.java
b/geode-dunit/src/main/java/org/apache/geode/test/dunit/VM.java
index ad670d7..6ef2724 100644
--- a/geode-dunit/src/main/java/org/apache/geode/test/dunit/VM.java
+++ b/geode-dunit/src/main/java/org/apache/geode/test/dunit/VM.java
@@ -33,6 +33,7 @@ import
org.apache.geode.test.dunit.internal.MethodInvokerResult;
import org.apache.geode.test.dunit.internal.ProcessHolder;
import org.apache.geode.test.dunit.internal.RemoteDUnitVMIF;
import org.apache.geode.test.dunit.internal.StandAloneDUnitEnv;
+import org.apache.geode.test.dunit.internal.VMEventNotifier;
import org.apache.geode.test.version.VersionManager;
/**
@@ -64,7 +65,7 @@ public class VM implements Serializable {
private transient volatile ProcessHolder processHolder;
- private transient ChildVMLauncher childVMLauncher;
+ private final transient ChildVMLauncher childVMLauncher;
/**
* Returns the {@code VM} identity. For {@link StandAloneDUnitEnv} the
number returned is a
@@ -154,6 +155,24 @@ public class VM implements Serializable {
}
/**
+ * Registers a {@link VMEventListener}.
+ */
+ public static void addVMEventListener(final VMEventListener listener) {
+ getVMEventNotifier().addVMEventListener(listener);
+ }
+
+ /**
+ * Deregisters a {@link VMEventListener}.
+ */
+ public static void removeVMEventListener(final VMEventListener listener) {
+ getVMEventNotifier().removeVMEventListener(listener);
+ }
+
+ private static VMEventNotifier getVMEventNotifier() {
+ return Host.getHost(0).getVMEventNotifier();
+ }
+
+ /**
* Creates a new {@code VM} that runs on a given host with a given process
id.
*/
public VM(final Host host, final int id, final RemoteDUnitVMIF client,
@@ -480,6 +499,8 @@ public class VM implements Serializable {
checkAvailability(getClass().getName(), "bounceVM");
logger.info("Bouncing {} old pid is {}", id, getPid());
+ getVMEventNotifier().notifyBeforeBounceVM(this);
+
available = false;
try {
if (force) {
@@ -501,7 +522,10 @@ public class VM implements Serializable {
version = targetVersion;
client = childVMLauncher.getStub(id);
available = true;
+
logger.info("Bounced {} new pid is {}", id, getPid());
+ getVMEventNotifier().notifyAfterBounceVM(this);
+
} catch (InterruptedException | IOException | NotBoundException e) {
throw new Error("Unable to restart VM " + id, e);
}
diff --git
a/geode-dunit/src/main/java/org/apache/geode/test/dunit/VMEventListener.java
b/geode-dunit/src/main/java/org/apache/geode/test/dunit/VMEventListener.java
new file mode 100644
index 0000000..f2f639b
--- /dev/null
+++ b/geode-dunit/src/main/java/org/apache/geode/test/dunit/VMEventListener.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
contributor license
+ * agreements. See the NOTICE file distributed with this work for additional
information regarding
+ * copyright ownership. The ASF licenses this file to You 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.apache.geode.test.dunit;
+
+/**
+ * Provides callback notifications for creation of and bouncing of dunit VMs.
+ */
+public interface VMEventListener {
+
+ /**
+ * Invoked after creating a new dunit VM.
+ *
+ * @see VM#getVM(int)
+ */
+ void afterCreateVM(VM vm);
+
+ /**
+ * Invoked before bouncing a dunit VM.
+ *
+ * @see VM#bounce()
+ * @see VM#bounceForcibly()
+ */
+ void beforeBounceVM(VM vm);
+
+ /**
+ * Invoked after bouncing a dunit VM.
+ *
+ * @see VM#bounce()
+ * @see VM#bounceForcibly()
+ */
+ void afterBounceVM(VM vm);
+}
diff --git
a/geode-dunit/src/main/java/org/apache/geode/test/dunit/VMEventListenerRegistry.java
b/geode-dunit/src/main/java/org/apache/geode/test/dunit/VMEventListenerRegistry.java
new file mode 100644
index 0000000..87b2d6e
--- /dev/null
+++
b/geode-dunit/src/main/java/org/apache/geode/test/dunit/VMEventListenerRegistry.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
contributor license
+ * agreements. See the NOTICE file distributed with this work for additional
information regarding
+ * copyright ownership. The ASF licenses this file to You 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.apache.geode.test.dunit;
+
+/**
+ * Manages registration of {@link VMEventListener}s.
+ */
+public interface VMEventListenerRegistry {
+
+ /**
+ * Registers a {@code VMEventListener}.
+ */
+ void addVMEventListener(VMEventListener listener);
+
+ /**
+ * Deregisters a {@code VMEventListener}.
+ */
+ void removeVMEventListener(VMEventListener listener);
+}
diff --git
a/geode-dunit/src/main/java/org/apache/geode/test/dunit/internal/DUnitHost.java
b/geode-dunit/src/main/java/org/apache/geode/test/dunit/internal/DUnitHost.java
index 651377e..11e0606 100644
---
a/geode-dunit/src/main/java/org/apache/geode/test/dunit/internal/DUnitHost.java
+++
b/geode-dunit/src/main/java/org/apache/geode/test/dunit/internal/DUnitHost.java
@@ -28,13 +28,15 @@ class DUnitHost extends Host {
private static final long serialVersionUID = -8034165624503666383L;
private final transient VM debuggingVM;
+ private final transient ProcessManager processManager;
+ private final transient VMEventNotifier vmEventNotifier;
- private transient ProcessManager processManager;
-
- public DUnitHost(String hostName, ProcessManager processManager) throws
RemoteException {
- super(hostName);
+ DUnitHost(String hostName, ProcessManager processManager, VMEventNotifier
vmEventNotifier)
+ throws RemoteException {
+ super(hostName, vmEventNotifier);
this.debuggingVM = new VM(this, -1, new RemoteDUnitVM(), null, null);
this.processManager = processManager;
+ this.vmEventNotifier = vmEventNotifier;
}
public void init(Registry registry, int numVMs)
diff --git
a/geode-dunit/src/main/java/org/apache/geode/test/dunit/internal/DUnitLauncher.java
b/geode-dunit/src/main/java/org/apache/geode/test/dunit/internal/DUnitLauncher.java
index 3dea304..f80431e 100644
---
a/geode-dunit/src/main/java/org/apache/geode/test/dunit/internal/DUnitLauncher.java
+++
b/geode-dunit/src/main/java/org/apache/geode/test/dunit/internal/DUnitLauncher.java
@@ -123,6 +123,8 @@ public class DUnitLauncher {
private static final String LAUNCHED_PROPERTY = GEMFIRE_PREFIX +
"DUnitLauncher.LAUNCHED";
+ private static final VMEventNotifier vmEventNotifier = new VMEventNotifier();
+
private static Master master;
private DUnitLauncher() {}
@@ -228,9 +230,9 @@ public class DUnitLauncher {
// populate the Host class with our stubs. The tests use this host class
DUnitHost host =
- new DUnitHost(InetAddress.getLocalHost().getCanonicalHostName(),
processManager);
+ new DUnitHost(InetAddress.getLocalHost().getCanonicalHostName(),
processManager,
+ vmEventNotifier);
host.init(registry, NUM_VMS);
-
}
public static Properties getDistributedSystemProperties() {
diff --git
a/geode-dunit/src/main/java/org/apache/geode/test/dunit/internal/VMEventNotifier.java
b/geode-dunit/src/main/java/org/apache/geode/test/dunit/internal/VMEventNotifier.java
new file mode 100644
index 0000000..426b574
--- /dev/null
+++
b/geode-dunit/src/main/java/org/apache/geode/test/dunit/internal/VMEventNotifier.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
contributor license
+ * agreements. See the NOTICE file distributed with this work for additional
information regarding
+ * copyright ownership. The ASF licenses this file to You 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.apache.geode.test.dunit.internal;
+
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import org.apache.geode.test.dunit.VM;
+import org.apache.geode.test.dunit.VMEventListener;
+import org.apache.geode.test.dunit.VMEventListenerRegistry;
+
+/**
+ * Implements {@code VMEventListenerRegistry} and provides thread-safe
notifications to registered
+ * listeners.
+ */
+public class VMEventNotifier implements VMEventListenerRegistry {
+
+ private final List<VMEventListener> listeners;
+
+ VMEventNotifier() {
+ listeners = new CopyOnWriteArrayList<>();
+ }
+
+ @Override
+ public void addVMEventListener(VMEventListener listener) {
+ listeners.add(listener);
+ }
+
+ @Override
+ public void removeVMEventListener(VMEventListener listener) {
+ listeners.remove(listener);
+ }
+
+ /**
+ * Notifies currently registered listeners of {@link
VMEventListener#afterCreateVM(VM)}.
+ * Concurrent changes to listener registration are ignored while notifying.
+ */
+ public void notifyAfterCreateVM(VM vm) {
+ for (VMEventListener listener : listeners) {
+ listener.afterCreateVM(vm);
+ }
+ }
+
+ /**
+ * Notifies currently registered listeners of {@link
VMEventListener#beforeBounceVM(VM)}.
+ * Concurrent changes to listener registration are ignored while notifying.
+ */
+ public void notifyBeforeBounceVM(VM vm) {
+ for (VMEventListener listener : listeners) {
+ listener.beforeBounceVM(vm);
+ }
+ }
+
+ /**
+ * Notifies currently registered listeners of {@link
VMEventListener#afterBounceVM(VM)}.
+ * Concurrent changes to listener registration are ignored while notifying.
+ */
+ public void notifyAfterBounceVM(VM vm) {
+ for (VMEventListener listener : listeners) {
+ listener.afterBounceVM(vm);
+ }
+ }
+}
diff --git
a/geode-dunit/src/main/java/org/apache/geode/test/dunit/rules/AbstractDistributedRule.java
b/geode-dunit/src/main/java/org/apache/geode/test/dunit/rules/AbstractDistributedRule.java
index 44cb498..dea30dd 100644
---
a/geode-dunit/src/main/java/org/apache/geode/test/dunit/rules/AbstractDistributedRule.java
+++
b/geode-dunit/src/main/java/org/apache/geode/test/dunit/rules/AbstractDistributedRule.java
@@ -15,12 +15,12 @@
package org.apache.geode.test.dunit.rules;
import static org.apache.geode.test.dunit.VM.DEFAULT_VM_COUNT;
-import static org.apache.geode.test.dunit.VM.getVMCount;
-import static org.assertj.core.api.Assertions.assertThat;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
+import org.apache.geode.test.dunit.VM;
+import org.apache.geode.test.dunit.VMEventListener;
import org.apache.geode.test.dunit.internal.DUnitLauncher;
import org.apache.geode.test.dunit.internal.TestHistoryLogger;
import org.apache.geode.test.junit.rules.serializable.SerializableStatement;
@@ -31,8 +31,6 @@ class AbstractDistributedRule implements SerializableTestRule
{
private final int vmCount;
private final RemoteInvoker invoker;
- private volatile int beforeVmCount;
-
protected AbstractDistributedRule() {
this(DEFAULT_VM_COUNT);
}
@@ -46,50 +44,49 @@ class AbstractDistributedRule implements
SerializableTestRule {
this.invoker = invoker;
}
-
@Override
- public Statement apply(final Statement base, final Description description) {
- return statement(base, description);
+ public Statement apply(final Statement statement, final Description
description) {
+ return statement(statement, description);
}
- private Statement statement(final Statement base, Description
testDescription) {
+ private Statement statement(final Statement baseStatement, final Description
description) {
return new SerializableStatement() {
@Override
public void evaluate() throws Throwable {
- beforeDistributedTest(testDescription);
+ VMEventListener vmEventListener = new InternalVMEventListener();
+ beforeDistributedTest(description);
+ VM.addVMEventListener(vmEventListener);
before();
try {
- base.evaluate();
+ baseStatement.evaluate();
} finally {
after();
- afterDistributedTest(testDescription);
+ VM.removeVMEventListener(vmEventListener);
+ afterDistributedTest(description);
}
}
};
}
- private void beforeDistributedTest(Description testDescription) throws
Throwable {
-
TestHistoryLogger.logTestHistory(testDescription.getTestClass().getSimpleName(),
- testDescription.getMethodName());
+ private void beforeDistributedTest(final Description description) throws
Throwable {
+
TestHistoryLogger.logTestHistory(description.getTestClass().getSimpleName(),
+ description.getMethodName());
DUnitLauncher.launchIfNeeded(vmCount);
- beforeVmCount = getVMCount();
- System.out.println("\n\n[setup] START TEST " +
testDescription.getClassName() + "."
- + testDescription.getMethodName());
+ System.out.println("\n\n[setup] START TEST " + description.getClassName()
+ "."
+ + description.getMethodName());
}
- private void afterDistributedTest(Description testDescription) throws
Throwable {
- System.out.println("\n\n[setup] END TEST " +
testDescription.getTestClass().getSimpleName()
- + "." + testDescription.getMethodName());
- int afterVmCount = getVMCount();
- assertThat(afterVmCount).isEqualTo(beforeVmCount);
+ private void afterDistributedTest(final Description description) throws
Throwable {
+ System.out.println("\n\n[setup] END TEST " +
description.getTestClass().getSimpleName()
+ + "." + description.getMethodName());
}
protected void before() throws Throwable {
- // override
+ // override if needed
}
protected void after() throws Throwable {
- // override
+ // override if needed
}
protected RemoteInvoker invoker() {
@@ -99,4 +96,34 @@ class AbstractDistributedRule implements
SerializableTestRule {
protected int vmCount() {
return vmCount;
}
+
+ protected void afterCreateVM(VM vm) {
+ // override if needed
+ }
+
+ protected void beforeBounceVM(VM vm) {
+ // override if needed
+ }
+
+ protected void afterBounceVM(VM vm) {
+ // override if needed
+ }
+
+ private class InternalVMEventListener implements VMEventListener {
+
+ @Override
+ public void afterCreateVM(VM vm) {
+ AbstractDistributedRule.this.afterCreateVM(vm);
+ }
+
+ @Override
+ public void beforeBounceVM(VM vm) {
+ AbstractDistributedRule.this.beforeBounceVM(vm);
+ }
+
+ @Override
+ public void afterBounceVM(VM vm) {
+ AbstractDistributedRule.this.afterBounceVM(vm);
+ }
+ }
}
diff --git
a/geode-dunit/src/test/java/org/apache/geode/test/dunit/internal/VMEventNotifierTest.java
b/geode-dunit/src/test/java/org/apache/geode/test/dunit/internal/VMEventNotifierTest.java
new file mode 100644
index 0000000..039b471
--- /dev/null
+++
b/geode-dunit/src/test/java/org/apache/geode/test/dunit/internal/VMEventNotifierTest.java
@@ -0,0 +1,164 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
contributor license
+ * agreements. See the NOTICE file distributed with this work for additional
information regarding
+ * copyright ownership. The ASF licenses this file to You 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.apache.geode.test.dunit.internal;
+
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
+
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+import junitparams.naming.TestCaseName;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.apache.geode.test.awaitility.GeodeAwaitility;
+import org.apache.geode.test.dunit.VM;
+import org.apache.geode.test.dunit.VMEventListener;
+import org.apache.geode.test.junit.rules.ExecutorServiceRule;
+
+/**
+ * Unit tests for {@link VMEventNotifier};
+ */
+@RunWith(JUnitParamsRunner.class)
+public class VMEventNotifierTest {
+
+ private static final long TIMEOUT_MILLIS =
GeodeAwaitility.getTimeout().getValueInMS();
+
+ @Rule
+ public ExecutorServiceRule executorServiceRule = new ExecutorServiceRule();
+
+ private final CountDownLatch startLatch = new CountDownLatch(1);
+ private final CountDownLatch stopLatch = new CountDownLatch(1);
+
+ private VMEventListener vmEventListener1;
+ private VMEventListener vmEventListener2;
+ private VM vm;
+
+ private VMEventNotifier vmEventNotifier;
+
+ @Before
+ public void setUp() {
+ vmEventListener1 = mock(VMEventListener.class);
+ vmEventListener2 = mock(VMEventListener.class);
+ vm = mock(VM.class);
+
+ vmEventNotifier = new VMEventNotifier();
+ }
+
+ @Test
+ @Parameters({"AFTER_CREATE_VM", "BEFORE_BOUNCE_VM", "AFTER_BOUNCE_VM"})
+ @TestCaseName("{method}({params})")
+ public void addsListenerConcurrentlyWithNotification(Notification
notification) throws Exception {
+ doAnswer(invocation -> {
+ startLatch.countDown();
+ stopLatch.await(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
+ return null;
+ }).when(vmEventListener1).afterCreateVM(vm);
+
+ vmEventNotifier.addVMEventListener(vmEventListener1);
+
+ Future<Void> notifiedFuture = executorServiceRule.submit(() -> {
+ notification.notify(vmEventNotifier, vm);
+ return null;
+ });
+
+ startLatch.await(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
+
+ vmEventNotifier.addVMEventListener(vmEventListener2);
+
+ stopLatch.countDown();
+
+ notifiedFuture.get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
+
+ verify(vmEventListener1).afterCreateVM(eq(vm));
+ verifyZeroInteractions(vmEventListener2);
+ }
+
+ @Test
+ @Parameters({"AFTER_CREATE_VM", "BEFORE_BOUNCE_VM", "AFTER_BOUNCE_VM"})
+ @TestCaseName("{method}({params})")
+ public void removesListenerConcurrentlyWithNotification(Notification
notification)
+ throws Exception {
+ doAnswer(invocation -> {
+ startLatch.countDown();
+ stopLatch.await(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
+ return null;
+ }).when(vmEventListener1).afterCreateVM(vm);
+
+ vmEventNotifier.addVMEventListener(vmEventListener1);
+ vmEventNotifier.addVMEventListener(vmEventListener2);
+
+ Future<Void> notifiedFuture = executorServiceRule.submit(() -> {
+ notification.notify(vmEventNotifier, vm);
+ return null;
+ });
+
+ startLatch.await(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
+
+ vmEventNotifier.removeVMEventListener(vmEventListener2);
+
+ stopLatch.countDown();
+
+ notifiedFuture.get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
+
+ verify(vmEventListener1).afterCreateVM(eq(vm));
+ verify(vmEventListener2).afterCreateVM(eq(vm));
+ }
+
+ @SuppressWarnings("unused")
+ private enum Notification {
+ AFTER_CREATE_VM(params ->
params.vmEventNotifier().notifyAfterCreateVM(params.vm())),
+ BEFORE_BOUNCE_VM(params ->
params.vmEventNotifier().notifyAfterCreateVM(params.vm())),
+ AFTER_BOUNCE_VM(params ->
params.vmEventNotifier().notifyAfterCreateVM(params.vm()));
+
+ private final Consumer<NotificationParams> strategy;
+
+ Notification(Consumer<NotificationParams> strategy) {
+ this.strategy = strategy;
+ }
+
+ void notify(VMEventNotifier vmEventNotifier, VM vm) {
+ strategy.accept(new NotificationParams(vmEventNotifier, vm));
+ }
+ }
+
+ private static class NotificationParams {
+ private final VMEventNotifier vmEventNotifier;
+ private final VM vm;
+
+ NotificationParams(VMEventNotifier vmEventNotifier, VM vm) {
+ this.vmEventNotifier = vmEventNotifier;
+ this.vm = vm;
+ }
+
+ VMEventNotifier vmEventNotifier() {
+ return vmEventNotifier;
+ }
+
+ VM vm() {
+ return vm;
+ }
+ }
+}
diff --git a/geode-dunit/src/test/resources/expected-pom.xml
b/geode-dunit/src/test/resources/expected-pom.xml
index e0c77c6..41d5422 100644
--- a/geode-dunit/src/test/resources/expected-pom.xml
+++ b/geode-dunit/src/test/resources/expected-pom.xml
@@ -149,6 +149,11 @@
<scope>compile</scope>
</dependency>
<dependency>
+ <groupId>pl.pragmatists</groupId>
+ <artifactId>JUnitParams</artifactId>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>compile</scope>