/*
* Copyright 1999-2009 University of Chicago
*
* 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.nimbustools.ctxbroker.blackboard;
import org.testng.annotations.Test;
import org.testng.annotations.DataProvider;
import static org.testng.Assert.*;
import static org.testng.Assert.assertEquals;
import org.nimbustools.ctxbroker.ContextBrokerException;
import org.nimbustools.ctxbroker.Identity;
import java.util.List;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.UUID;
public class BlackboardTest {
final static String ID = "test-Blackboard";
int nextWorkspaceID = 0;
private synchronized Integer getWorkspaceID() {
return nextWorkspaceID++;
}
private Identity getIdentity(int workspaceID) {
return new Identity("publicnic",
"192.168.0."+workspaceID,
"id-"+workspaceID,
"asdfghjk");
}
private static void assertDataPairs(List<DataPair> actual, List<DataPair> expected) {
assertEquals(actual.size(), expected.size());
for (DataPair expectedPair : expected) {
boolean found = false;
for (DataPair actualPair : actual) {
if (expectedPair.getName().equals(actualPair.getName()) &&
expectedPair.getValue().equals(expectedPair.getValue())) {
found = true;
break;
}
}
assertTrue(found, "Data pair: "+expectedPair.toString() +
" was not found in actual data pair list");
}
}
private static void assertIdentitiesEqual(Identity actual, Identity expected) {
assertNotNull(actual);
assertNotNull(expected);
assertEquals(actual.getIface(), expected.getIface());
assertEquals(actual.getIp(), expected.getIp());
assertEquals(actual.getPubkey(), expected.getPubkey());
assertEquals(actual.getHostname(), expected.getHostname());
}
@Test
public void testNodeCountChange() throws ContextBrokerException {
Blackboard bb = new Blackboard(ID);
Integer workspaceId = getWorkspaceID();
Identity[] ids = new Identity[] {getIdentity(workspaceId)};
bb.addWorkspace(workspaceId, ids, true, null, null, null, 3);
boolean failed = false;
try {
bb.addWorkspace(workspaceId, ids, true, null, null, null, 4);
} catch (ContextBrokerException e) {
failed = true;
}
assertTrue(failed, "Mismatched node counts did not cause failure");
}
@Test
public void testTooManyNodes() {
Blackboard bb = new Blackboard(ID);
final int nodeCount = 3;
boolean failed = false;
// we try to add one more node than we advertise
for (int i=0; i<=nodeCount; i++) {
Integer workspaceId = getWorkspaceID();
Identity[] ids = new Identity[] {getIdentity(workspaceId)};
try {
bb.addWorkspace(workspaceId, ids, true, null,
null, null, nodeCount);
} catch (ContextBrokerException e) {
assertEquals(i, nodeCount, "addWorkspace() failed on an " +
"earlier node than expected");
failed = true;
}
}
assertTrue(failed, "Adding one too many nodes did not cause failure");
}
@Test
public void testDuplicateNodeId() throws ContextBrokerException {
Blackboard bb = new Blackboard(ID);
Integer workspaceId = getWorkspaceID();
Identity[] ids = new Identity[] {getIdentity(workspaceId)};
bb.addWorkspace(workspaceId, ids, true, null, null, null, 3);
boolean failed = false;
try {
bb.addWorkspace(workspaceId, ids, true, null, null, null, 3);
} catch (ContextBrokerException e) {
failed = true;
}
assertTrue(failed, "Adding node with duplicate ID did not cause failure");
}
@DataProvider(name="allofthebools")
private Object[][] generateBooleans() {
// seems a little silly
return new Object[][] {
new Object[] {true},
new Object[] {false}
};
}
@Test(dataProvider = "allofthebools")
public void testSingleNode(boolean reportOk) throws ContextBrokerException {
Blackboard bb = new Blackboard(ID);
Integer workspaceId = getWorkspaceID();
Identity[] ids = new Identity[] {getIdentity(workspaceId)};
DataPair[] data = new DataPair[] {
new DataPair("foo", "bar"),
new DataPair("billy", "anyteen")
};
CtxStatus ctxStatus = bb.getStatus();
assertFalse(ctxStatus.isAllOk());
assertFalse(ctxStatus.isErrorOccurred());
assertFalse(ctxStatus.isComplete());
bb.addWorkspace(workspaceId, ids, true, null, data, null, 1);
final NodeManifest nodeManifest = bb.retrieve(workspaceId);
assertDataPairs(nodeManifest.getData(), Arrays.asList(data));
assertEquals(nodeManifest.getRequiredRoles().size(), 0);
assertEquals(nodeManifest.getIdentities().size(), 1);
// node has retrieved its data but not reported in yet
ctxStatus = bb.getStatus();
assertFalse(ctxStatus.isAllOk());
assertFalse(ctxStatus.isErrorOccurred());
assertTrue(ctxStatus.isComplete());
if (reportOk) {
bb.okExit(workspaceId);
ctxStatus = bb.getStatus();
assertTrue(ctxStatus.isAllOk());
assertFalse(ctxStatus.isErrorOccurred());
assertTrue(ctxStatus.isComplete());
final List<NodeStatus> list = bb.identities(true, null, null);
assertEquals(list.size(), 1);
final NodeStatus nodeStatus = list.get(0);
assertEquals(nodeStatus.getIdentities().size(), 1);
assertIdentitiesEqual(nodeStatus.getIdentities().get(0), ids[0]);
assertEquals(nodeStatus.getErrorCode(),0);
assertNull(nodeStatus.getErrorMessage());
assertFalse(nodeStatus.isErrorOccurred());
assertTrue(nodeStatus.isOkOccurred());
} else {
final short exitCode = 1;
final String errorMsg = "this isn't really an error";
bb.errorExit(workspaceId, exitCode, errorMsg);
ctxStatus = bb.getStatus();
assertFalse(ctxStatus.isAllOk());
assertTrue(ctxStatus.isErrorOccurred());
assertTrue(ctxStatus.isComplete());
final List<NodeStatus> list = bb.identities(true, null, null);
assertEquals(list.size(), 1);
final NodeStatus nodeStatus = list.get(0);
assertEquals(nodeStatus.getIdentities().size(), 1);
assertIdentitiesEqual(nodeStatus.getIdentities().get(0), ids[0]);
assertEquals(nodeStatus.getErrorCode(), exitCode);
assertEquals(nodeStatus.getErrorMessage(), errorMsg);
assertTrue(nodeStatus.isErrorOccurred());
assertFalse(nodeStatus.isOkOccurred());
}
}
@Test(dataProvider = "allofthebools")
public void testMasterWorker(boolean requireAllIdentities)
throws ContextBrokerException {
// one-way dependency: a master role that depends on nothing but is required by multiple workers
// requireAllIdentities affects whether nodes want information for every node, or just
// those that they depend on. This affects two things:
// * Whether nodes can be retrieve()d once they have dependencies satisfied but not all
// nodes have checked in
// * How many Identities nodes get back from retrieve()
Blackboard bb = new Blackboard(ID);
final String masterRole ="mastermaster";
final int workerCount = 2;
final int nodeCount = workerCount+1;
final Integer masterId = getWorkspaceID();
final Identity[] masterIdentities = new Identity[] { getIdentity(masterId)};
final ProvidedRoleDescription[] masterProvidedRoles = new ProvidedRoleDescription[] {
new ProvidedRoleDescription(masterRole, null)
};
final RequiredRole[] masterRequiredRoles = null;
bb.addWorkspace(masterId, masterIdentities, requireAllIdentities,
masterRequiredRoles, null, masterProvidedRoles, nodeCount);
List<Integer> workerIds = new ArrayList<Integer>(workerCount);
for (int i=0; i< workerCount; i++) {
// context should not be complete and retrieve should fail until workers check in
assertFalse(bb.isComplete());
if (requireAllIdentities) {
assertTrue(bb.retrieve(masterId) == null);
for (Integer id : workerIds) {
assertTrue(bb.retrieve(id) == null);
}
} else {
assertTrue(bb.retrieve(masterId) != null);
for (Integer id : workerIds) {
assertTrue(bb.retrieve(id) != null);
}
}
final Integer workerId = getWorkspaceID();
workerIds.add(workerId);
final Identity[] workerIdentities = new Identity[] { getIdentity(workerId)};
final ProvidedRoleDescription[] workerProvidedRoles = null;
final RequiredRole[] workerRequiredRoles = new RequiredRole[] {
new RequiredRole(masterRole, true, true)
};
bb.addWorkspace(workerId, workerIdentities, requireAllIdentities,
workerRequiredRoles, null, workerProvidedRoles, nodeCount);
}
// okay now everyone is checked in.
assertTrue(bb.isComplete());
for (Integer workerId : workerIds) {
final NodeManifest man = bb.retrieve(workerId);
assertTrue(man.getData().isEmpty());
assertEquals(man.getRequiredRoles().size(), 1);
RoleIdentityPair rolePair = man.getRequiredRoles().get(0);
assertIdentitiesEqual(rolePair.getIdentity(), masterIdentities[0]);
assertEquals(rolePair.getRole(), masterRole);
if (requireAllIdentities) {
assertEquals(man.getIdentities().size(), nodeCount);
} else {
// just the master
assertEquals(man.getIdentities().size(), 1);
assertIdentitiesEqual(man.getIdentities().get(0), masterIdentities[0]);
}
}
final NodeManifest masterManifest = bb.retrieve(masterId);
assertTrue(masterManifest.getData().isEmpty());
assertTrue(masterManifest.getRequiredRoles().isEmpty());
// if we don't require all identities, master will get 0 identities
assertEquals(masterManifest.getIdentities().size(),
requireAllIdentities ? nodeCount : 0);
}
@Test
public void testFindIdentities() throws ContextBrokerException {
final Blackboard bb = new Blackboard(ID);
final int nodeCount = 3;
Identity[] ids = new Identity[nodeCount];
for (int i=0; i< nodeCount; i++) {
Integer workspaceId = getWorkspaceID();
ids[i] = getIdentity(workspaceId);
bb.addWorkspace(workspaceId, new Identity[] {ids[i]},
true, null, null, null, nodeCount);
}
// find all identities
final List<NodeStatus> allNodes = bb.identities(true, null, null);
assertEquals(allNodes.size(), nodeCount);
// find by IP
for (Identity id : ids) {
final List<NodeStatus> list = bb.identities(false, null, id.getIp());
assertEquals(list.size(), 1);
final List<Identity> nodeIdentities = list.get(0).getIdentities();
assertEquals(list.size(), 1);
assertIdentitiesEqual(nodeIdentities.get(0), id);
}
// and a nonexistent IP
assertEquals(bb.identities(false, null, "10.10.10.10").size(), 0);
// find by hostname
for (Identity id : ids) {
final List<NodeStatus> list = bb.identities(false, id.getHostname(), null);
assertEquals(list.size(), 1);
final List<Identity> nodeIdentities = list.get(0).getIdentities();
assertEquals(list.size(), 1);
assertIdentitiesEqual(nodeIdentities.get(0), id);
}
// and a nonexistent hostname
assertEquals(bb.identities(false, "google.com", null).size(), 0);
}
@Test
public void testInjectData() throws ContextBrokerException {
final Blackboard bb = new Blackboard(ID);
final String dataName = "OMGDATA";
final String[] dataValues = new String[] {"VALUE1", "VALUE2"};
// add a node that requires a data but provides no value
DataPair dataNoValue = new DataPair(dataName);
DataPair dataWithValue = new DataPair(dataName, dataValues[0]);
Integer workspaceNoValueId = getWorkspaceID();
bb.addWorkspace(workspaceNoValueId, new Identity[] {getIdentity(workspaceNoValueId)},
true, null, new DataPair[] {dataNoValue}, null, 2);
Integer workspaceWithValueId = getWorkspaceID();
bb.addWorkspace(workspaceWithValueId, new Identity[] {getIdentity(workspaceWithValueId)},
true, null, new DataPair[] {dataWithValue}, null, 2);
bb.injectData(dataName, dataValues[1]);
NodeManifest node1 = bb.retrieve(workspaceNoValueId);
assertNotNull(node1);
final List<DataPair> node1Data = node1.getData();
assertEquals(node1Data.size(), dataValues.length);
for (DataPair dp : node1Data) {
assertEquals(dp.getName(), dataName);
final String value = dp.getValue();
assertNotNull(value);
int matchCount = 0;
for (String dataValue : dataValues) {
if (value.equals(dataValue)) {
matchCount++;
}
}
assertEquals(matchCount, 1);
}
NodeManifest node2 = bb.retrieve(workspaceWithValueId);
assertNotNull(node2);
final List<DataPair> node2Data = node2.getData();
assertEquals(node2Data.size(), dataValues.length);
for (DataPair dp : node2Data) {
assertEquals(dp.getName(), dataName);
final String value = dp.getValue();
assertNotNull(value);
int matchCount = 0;
for (String dataValue : dataValues) {
if (value.equals(dataValue)) {
matchCount++;
}
}
assertEquals(matchCount, 1);
}
}
@Test
public void testStaticBlackboardFactory() {
final String id = UUID.randomUUID().toString();
final Blackboard blackboard = Blackboard.createOrGetBlackboard(id);
assertSame(Blackboard.createOrGetBlackboard(id), blackboard);
final String anotherId = UUID.randomUUID().toString();
assertNotSame(Blackboard.createOrGetBlackboard(anotherId), blackboard);
}
}