Skip to content

Commit

Permalink
GEODE-6033: Add VMEventListener for DUnit Rules (apache#3161)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
kirklund authored Feb 6, 2019
1 parent f31bfce commit 2959d17
Show file tree
Hide file tree
Showing 12 changed files with 554 additions and 33 deletions.
1 change: 1 addition & 0 deletions geode-dunit/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
Original file line number Diff line number Diff line change
@@ -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
}
}
}
12 changes: 10 additions & 2 deletions geode-dunit/src/main/java/org/apache/geode/test/dunit/Host.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
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;

/**
Expand All @@ -31,7 +32,6 @@
* 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 {
Expand All @@ -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
*/
Expand Down Expand Up @@ -101,14 +103,15 @@ public static void setAllVMsToCurrentVersion() {
/**
* 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);
}

this.hostName = hostName;
vms = new ArrayList<>();
this.vmEventNotifier = vmEventNotifier;
}

/**
Expand Down Expand Up @@ -168,6 +171,7 @@ protected void addVM(int vmid, RemoteDUnitVMIF client, ProcessHolder processHold
ChildVMLauncher childVMLauncher) {
VM vm = new VM(this, vmid, client, processHolder, childVMLauncher);
vms.add(vm);
vmEventNotifier.notifyAfterCreateVM(vm);
}

public static VM getLocator() {
Expand Down Expand Up @@ -207,4 +211,8 @@ public boolean equals(Object o) {
public int hashCode() {
return getHostName().hashCode();
}

VMEventNotifier getVMEventNotifier() {
return vmEventNotifier;
}
}
26 changes: 25 additions & 1 deletion geode-dunit/src/main/java/org/apache/geode/test/dunit/VM.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
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;

/**
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -153,6 +154,24 @@ public static VM[] toArray(VM... vms) {
return vms;
}

/**
* 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.
*/
Expand Down Expand Up @@ -480,6 +499,8 @@ private synchronized void bounce(final String targetVersion, boolean force) {
checkAvailability(getClass().getName(), "bounceVM");

logger.info("Bouncing {} old pid is {}", id, getPid());
getVMEventNotifier().notifyBeforeBounceVM(this);

available = false;
try {
if (force) {
Expand All @@ -501,7 +522,10 @@ private synchronized void bounce(final String targetVersion, boolean force) {
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);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}
Original file line number Diff line number Diff line change
@@ -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);
}
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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() {}
Expand Down Expand Up @@ -228,9 +230,9 @@ private static void launch() throws AlreadyBoundException, IOException,

// 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() {
Expand Down
Loading

0 comments on commit 2959d17

Please sign in to comment.