/*
* Copyright (c) 2010 Ecole des Mines de Nantes.
*
* This file is part of Entropy.
*
* Entropy is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Entropy is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Entropy. If not, see <http://www.gnu.org/licenses/>.
*/
package entropy.plan.choco;
import java.util.List;
import org.testng.Assert;
import org.testng.annotations.Test;
import choco.kernel.solver.search.ISolutionPool;
import choco.kernel.solver.search.SolutionPoolFactory;
import entropy.TestHelper;
import entropy.configuration.Configuration;
import entropy.configuration.DefaultConfiguration;
import entropy.configuration.DefaultManagedElementSet;
import entropy.configuration.DefaultNode;
import entropy.configuration.ManagedElementSet;
import entropy.configuration.Node;
import entropy.configuration.SimpleConfiguration;
import entropy.configuration.SimpleManagedElementSet;
import entropy.configuration.SimpleNode;
import entropy.configuration.SimpleVirtualMachine;
import entropy.configuration.VirtualMachine;
import entropy.plan.MultipleResultingStateException;
import entropy.plan.NoAvailableTransitionException;
import entropy.plan.NonViableSourceConfigurationException;
import entropy.plan.PlanException;
import entropy.plan.SolutionStatistics;
import entropy.plan.SolvingStatistics;
import entropy.plan.UnknownResultingStateException;
import entropy.plan.choco.actionModel.MigratableActionModel;
import entropy.plan.choco.actionModel.ResumeActionModel;
import entropy.plan.choco.actionModel.RunActionModel;
import entropy.plan.choco.actionModel.StayOfflineNodeActionModel;
import entropy.plan.choco.actionModel.SuspendActionModel;
import entropy.plan.choco.actionModel.TimedReconfigurationPlanModelHelper;
import entropy.plan.choco.actionModel.VirtualMachineActionModel;
import entropy.plan.durationEvaluator.MockDurationEvaluator;
/**
* Unit tests for BasicTimedReconfigurationPlanModel.
* TODO: tests duration bounds
*
* @author Fabien Hermenier
*/
@Test(groups = {"unit", "RP-core"})
public class TestDefaultReconfigurationProblem {
/**
* Location of resources used for tests.
*/
public static final String RESOURCES_LOCATION = "src/test/resources/entropy/plan/choco/TestTimedReconfigurationPlanModel.";
/**
* Just check the instantiation, access to the source configuration and start and end moment.
*/
public void testInstantiation() {
Configuration src = TestHelper.readConfiguration(RESOURCES_LOCATION + "sample_src.txt");
Configuration dst = TestHelper.readConfiguration(RESOURCES_LOCATION + "sample_src.txt");
ReconfigurationProblem model = TimedReconfigurationPlanModelHelper.makeBasicModel(src, dst);
Assert.assertTrue(model.getStart().isInstantiated());
Assert.assertEquals(model.getStart().getVal(), 0);
Assert.assertEquals(model.getEnd().getName(), "end");
Assert.assertEquals(model.getVirtualMachineActions().size(), 7);
for (VirtualMachineActionModel vma : model.getVirtualMachineActions()) {
String vmName = vma.getVirtualMachine().getName();
if (vmName.equals("VM1") || vmName.equals("VM2") || vmName.equals("VM3")
|| vmName.equals("VM4") || vmName.equals("VM5") || vmName.equals("VM6")
|| vmName.equals("VM7")) {
Assert.assertEquals(vma.getClass(), MigratableActionModel.class);
}
}
}
/**
* Test the creation and the cache of group of VMs in various conditions.
*/
/*public void testGetGroupOfVMs() {
Configuration src = TestHelper.readConfiguration(RESOURCES_LOCATION + "sample_src.txt");
Configuration dst = TestHelper.readConfiguration(RESOURCES_LOCATION + "sample_src.txt");
ReconfigurationProblem model = TimedReconfigurationPlanModelHelper.makeBasicModel(src, dst);
Assert.assertEquals(model.getVMGroups().size(), 0);
ManagedElementSet<VirtualMachine> t1 = new SimpleManagedElementSet<VirtualMachine>();
t1.add(src.getRunnings().get("VM1"));
t1.add(src.getRunnings().get("VM2"));
IntDomainVar v1 = model.getVMGroup(t1);
//One group was created
Assert.assertEquals(model.getVMGroups().size(), 1);
for (VirtualMachine vm : src.getAllVirtualMachines()) {
IntDomainVar v = model.getAssociatedGroup(vm);
if (t1.contains(vm)) {
Assert.assertNotNull(v);
} else {
Assert.assertNull(v);
}
}
//No group was created
IntDomainVar v2 = model.getVMGroup(t1);
Assert.assertEquals(model.getVMGroups().size(), 1);
Assert.assertEquals(v1, v2);
for (VirtualMachine vm : t1) {
Assert.assertEquals(model.getAssociatedGroup(vm), v1);
}
//new group, without overlapping
ManagedElementSet<VirtualMachine> t2 = new SimpleManagedElementSet<VirtualMachine>();
t2.add(src.getRunnings().get("VM3"));
t2.add(src.getRunnings().get("VM4"));
v2 = model.getVMGroup(t2);
for (VirtualMachine vm : src.getAllVirtualMachines()) {
IntDomainVar v = model.getAssociatedGroup(vm);
if (t1.contains(vm) || t2.contains(vm)) {
Assert.assertNotNull(v);
} else {
Assert.assertNull(v);
}
}
//New group was created
Assert.assertEquals(model.getVMGroups().size(), 2);
Assert.assertNotSame(v1, v2);
//Last, a group with a VM already associated to a group
ManagedElementSet<VirtualMachine> t3 = new SimpleManagedElementSet<VirtualMachine>();
t3.add(src.getRunnings().get("VM5"));
t3.add(src.getRunnings().get("VM6"));
t3.add(src.getRunnings().get("VM4"));
Assert.assertNull(model.getVMGroup(t3));
//No change
Assert.assertEquals(model.getVMGroups().size(), 2);
Assert.assertNull(model.getAssociatedGroup(src.getRunnings().get("VM5")));
Assert.assertNull(model.getAssociatedGroup(src.getRunnings().get("VM6")));
Assert.assertEquals(v2, model.getAssociatedGroup(src.getRunnings().get("VM4")));
} */
/**
* Test the creation and the cache of group of nodes.
*/
public void testGetGroupOfNodes() {
Configuration src = TestHelper.readConfiguration(RESOURCES_LOCATION + "sample_src.txt");
Configuration dst = TestHelper.readConfiguration(RESOURCES_LOCATION + "sample_src.txt");
ReconfigurationProblem model = TimedReconfigurationPlanModelHelper.makeBasicModel(src, dst);
Assert.assertEquals(model.getNodesGroups().size(), 0);
//Create a group
ManagedElementSet<Node> grp = new SimpleManagedElementSet<Node>();
grp.add(src.getOnlines().get("N1"));
grp.add(src.getOnlines().get("N2"));
int v = model.getGroup(grp);
Assert.assertEquals(v, 0);
Assert.assertEquals(model.getNodesGroups().size(), 1);
for (Node n : grp) {
Assert.assertTrue(model.getAssociatedGroups(n).contains(0));
Assert.assertEquals(model.getAssociatedGroups(n).size(), 1);
}
//Try to create an already defined group
grp = new SimpleManagedElementSet<Node>();
grp.add(src.getOnlines().get("N1"));
grp.add(src.getOnlines().get("N2"));
int v2 = model.getGroup(grp);
Assert.assertEquals(v2, v);
Assert.assertEquals(model.getNodesGroups().size(), 1);
for (Node n : grp) {
Assert.assertEquals(model.getAssociatedGroups(n).size(), 1);
}
//A new group with overlapping
grp = new SimpleManagedElementSet<Node>();
grp.add(src.getOnlines().get("N1"));
grp.add(src.getOnlines().get("N3"));
int v3 = model.getGroup(grp);
Assert.assertEquals(v3, 1);
Assert.assertEquals(model.getNodesGroups().size(), 2);
Assert.assertEquals(model.getAssociatedGroups(src.getOnlines().get("N1")).size(), 2);
Assert.assertEquals((int) model.getAssociatedGroups(src.getOnlines().get("N1")).get(0), 0);
Assert.assertEquals((int) model.getAssociatedGroups(src.getOnlines().get("N1")).get(1), 1);
}
/**
* Check an instantiation with a non viable source configuration
*
* @throws NonViableSourceConfigurationException
* the expected exception
*/
@Test(expectedExceptions = {NonViableSourceConfigurationException.class})
public void testInstantiationWithNonViableConfiguration() throws PlanException {
try {
Configuration src = TestHelper.readConfiguration(RESOURCES_LOCATION + "bad_src.txt");
Configuration dst = TestHelper.readConfiguration(RESOURCES_LOCATION + "sample_src.txt");
new DefaultReconfigurationProblem(src,
dst.getRunnings(),
dst.getWaitings(),
dst.getSleepings(),
new DefaultManagedElementSet<VirtualMachine>(),
dst.getOnlines(),
dst.getOfflines(),
new MockDurationEvaluator(1, 2, 3, 4, 5, 6, 7, 8));
} catch (NoAvailableTransitionException e) {
Assert.fail(e.getMessage());
} catch (MultipleResultingStateException e) {
Assert.fail(e.getMessage());
} catch (UnknownResultingStateException e) {
Assert.fail(e.getMessage());
}
}
/**
* Test identify(*) in various situations.
*/
public void testGetNode() {
Configuration src = TestHelper.readConfiguration(RESOURCES_LOCATION + "sample_src.txt");
Configuration dst = TestHelper.readConfiguration(RESOURCES_LOCATION + "sample_src.txt");
ReconfigurationProblem model = TimedReconfigurationPlanModelHelper.makeBasicModel(src, dst);
Configuration source = model.getSourceConfiguration();
//Check values with an unexistant node
Node n = new SimpleNode("fake", 1, 2, 3);
Assert.assertEquals(model.getNode(n), -1);
Assert.assertNull(model.getNode(Integer.MAX_VALUE), null);
//Check the conversion from & to index
for (Node n2 : source.getOnlines()) {
int idx = model.getNode(n2);
Assert.assertNotSame(idx, -1);
Node rev = model.getNode(idx);
Assert.assertNotNull(rev);
Assert.assertEquals(rev, n2);
}
for (Node n2 : source.getOfflines()) {
int idx = model.getNode(n2);
Assert.assertNotSame(idx, -1);
Node rev = model.getNode(idx);
Assert.assertNotNull(rev);
Assert.assertEquals(rev, n2);
}
}
/**
* Test getVirtualMachine(*) in various situations.
*/
public void testGetVirtualMachine() {
Configuration src = TestHelper.readConfiguration(RESOURCES_LOCATION + "sample_src.txt");
Configuration dst = TestHelper.readConfiguration(RESOURCES_LOCATION + "sample_src.txt");
ReconfigurationProblem model = TimedReconfigurationPlanModelHelper.makeBasicModel(src, dst);
//Check values with an unexistant virtual machine
VirtualMachine vm = new SimpleVirtualMachine("fake", 1, 1, 1);
Assert.assertEquals(model.getVirtualMachine(vm), -1);
Assert.assertEquals(model.getVirtualMachine(Integer.MAX_VALUE), null);
}
/**
* Test mustBeRunning on a VM already running.
* We must observe the addition of a migration.
*/
public void testMustBeRunningWithRunning() {
Configuration src = TestHelper.readConfiguration(RESOURCES_LOCATION + "sample_src.txt");
Configuration dst = TestHelper.readConfiguration(RESOURCES_LOCATION + "sample_src.txt");
VirtualMachine vm = src.getRunnings().get("VM1");
dst.setRunOn(vm, dst.getOnlines().get(0));
ReconfigurationProblem model = TimedReconfigurationPlanModelHelper.makeBasicModel(src, dst);
Assert.assertEquals(model.getAssociatedAction(vm).getClass(), MigratableActionModel.class);
}
/**
* Test mustBeRunning on a VM currently sleeping.
* We must observe the addition of a resume action.
*/
public void testMustBeRunningWithSleeping() {
Configuration src = TestHelper.readConfiguration(RESOURCES_LOCATION + "sample_src.txt");
Configuration dst = TestHelper.readConfiguration(RESOURCES_LOCATION + "sample_src.txt");
VirtualMachine vm = src.getSleepings().get("VM10");
dst.setRunOn(vm, dst.getOnlines().get(0));
ReconfigurationProblem model = TimedReconfigurationPlanModelHelper.makeBasicModel(src, dst);
Assert.assertEquals(model.getAssociatedAction(vm).getClass(), ResumeActionModel.class);
}
/**
* Test mustBeRunning on a VM currently waiting.
* We must observe the addition of a waiting action.
*/
public void testMustBeRunningWithWaiting() {
Configuration src = TestHelper.readConfiguration(RESOURCES_LOCATION + "sample_src.txt");
Configuration dst = TestHelper.readConfiguration(RESOURCES_LOCATION + "sample_src.txt");
VirtualMachine vm = src.getWaitings().get("VM14");
dst.setRunOn(vm, dst.getOnlines().get(0));
ReconfigurationProblem model = TimedReconfigurationPlanModelHelper.makeBasicModel(src, dst);
Assert.assertEquals(model.getAssociatedAction(vm).getClass(), RunActionModel.class);
}
/**
* Test mustBeRunning with a VM in an incorrect state.
*
* @throws NoAvailableTransitionException the exception we expect
*/
@Test(expectedExceptions = {NoAvailableTransitionException.class})
public void testMustbeRunningWithUnknownVM() throws PlanException {
Configuration src = TestHelper.readConfiguration(RESOURCES_LOCATION + "sample_src.txt");
Configuration dst = TestHelper.readConfiguration(RESOURCES_LOCATION + "sample_src.txt");
VirtualMachine vm = new SimpleVirtualMachine("fake", 1, 1, 1);
dst.setRunOn(vm, dst.getOnlines().get(0));
try {
new DefaultReconfigurationProblem(src,
dst.getRunnings(),
dst.getWaitings(),
dst.getSleepings(),
new DefaultManagedElementSet<VirtualMachine>(),
dst.getOnlines(),
dst.getOfflines(),
new MockDurationEvaluator(1, 2, 3, 4, 5, 6, 7, 8));
} catch (MultipleResultingStateException e) {
Assert.fail(e.getMessage());
} catch (NonViableSourceConfigurationException e) {
Assert.fail(e.getMessage());
} catch (UnknownResultingStateException e) {
Assert.fail(e.getMessage());
}
}
/**
* Test mustBeSleeping with a running VM.
* Check the addition of a suspend action.
*/
public void testMustbeSleepingWithRunning() {
Configuration src = TestHelper.readConfiguration(RESOURCES_LOCATION + "sample_src.txt");
Configuration dst = TestHelper.readConfiguration(RESOURCES_LOCATION + "sample_src.txt");
VirtualMachine vm = src.getRunnings().get("VM1");
dst.setSleepOn(vm, dst.getLocation(vm));
ReconfigurationProblem model = TimedReconfigurationPlanModelHelper.makeBasicModel(src, dst);
Assert.assertEquals(model.getVirtualMachineActions().size(), 7);
Assert.assertEquals(model.getAssociatedAction(vm).getClass(), SuspendActionModel.class);
}
/**
* Test mustBeSleeping with a VM in a incorrect state.
*
* @throws NoAvailableTransitionException the exception we expect
*/
@Test(expectedExceptions = {NoAvailableTransitionException.class})
public void testMustBeSleepingWithIncorrectState() throws PlanException {
Configuration src = TestHelper.readConfiguration(RESOURCES_LOCATION + "sample_src.txt");
Configuration dst = TestHelper.readConfiguration(RESOURCES_LOCATION + "sample_src.txt");
VirtualMachine vm = new SimpleVirtualMachine("fake", 1, 1, 1);
dst.setSleepOn(vm, dst.getOnlines().get(0));
try {
new DefaultReconfigurationProblem(src,
dst.getRunnings(),
dst.getWaitings(),
dst.getSleepings(),
new DefaultManagedElementSet<VirtualMachine>(),
dst.getOnlines(),
dst.getOfflines(),
new MockDurationEvaluator(1, 2, 3, 4, 5, 6, 7, 8));
} catch (MultipleResultingStateException e) {
Assert.fail(e.getMessage());
} catch (NonViableSourceConfigurationException e) {
Assert.fail(e.getMessage());
} catch (UnknownResultingStateException e) {
Assert.fail(e.getMessage());
}
}
/**
* Test mustBeWaiting with a VM in an incorrect state.
* must return false
*
* @throws NoAvailableTransitionException the error we expect
*/
@Test(expectedExceptions = {NoAvailableTransitionException.class})
public void tetsMustBeWaitingWithIncorrectState() throws PlanException {
Configuration src = TestHelper.readConfiguration(RESOURCES_LOCATION + "sample_src.txt");
Configuration dst = TestHelper.readConfiguration(RESOURCES_LOCATION + "sample_src.txt");
dst.addWaiting(src.getRunnings().get("VM1"));
try {
new DefaultReconfigurationProblem(src,
dst.getRunnings(),
dst.getWaitings(),
dst.getSleepings(),
new DefaultManagedElementSet<VirtualMachine>(),
dst.getOnlines(),
dst.getOfflines(),
new MockDurationEvaluator(1, 2, 3, 4, 5, 6, 7, 8));
} catch (MultipleResultingStateException e) {
Assert.fail(e.getMessage());
} catch (NonViableSourceConfigurationException e) {
Assert.fail(e.getMessage());
} catch (UnknownResultingStateException e) {
Assert.fail(e.getMessage());
}
}
public void testWithStayOfflines() {
Node n = new DefaultNode("N1", 1, 1, 1);
Configuration src = new DefaultConfiguration();
src.addOffline(n);
try {
ReconfigurationProblem m = new DefaultReconfigurationProblem(
src,
src.getRunnings(),
src.getWaitings(),
src.getSleepings(),
new DefaultManagedElementSet<VirtualMachine>(),
src.getOnlines(),
src.getOfflines(),
new MockDurationEvaluator(1, 2, 3, 4, 5, 6, 7, 8));
Assert.assertEquals(m.getAssociatedAction(n).getClass(), StayOfflineNodeActionModel.class);
} catch (Exception e) {
Assert.fail(e.getMessage(), e);
}
}
public void testStatistics() {
Configuration src = new SimpleConfiguration();
Node n1 = new SimpleNode("N1", 3, 3, 3);
Node n2 = new SimpleNode("N1", 3, 3, 3);
src.addOnline(n1);
src.addOnline(n2);
VirtualMachine vm1 = new SimpleVirtualMachine("VM1", 1, 1, 2);
VirtualMachine vm2 = new SimpleVirtualMachine("VM2", 1, 1, 1);
src.setRunOn(vm1, src.getOnlines().get("N1"));
src.setRunOn(vm2, src.getOnlines().get("N1"));
try {
ReconfigurationProblem m = new DefaultReconfigurationProblem(
src,
src.getRunnings(),
src.getWaitings(),
src.getSleepings(),
new DefaultManagedElementSet<VirtualMachine>(),
src.getOnlines(),
src.getOfflines(),
new MockDurationEvaluator(1, 2, 3, 4, 5, 6, 7, 8));
m.post(m.leq(m.getEnd(), 2));
m.setFirstSolution(false);
m.generateSearchStrategy();
SolvingStatistics st = m.getSolvingStatistics();
Assert.assertTrue(st.getNbBacktracks() == 0 && st.getNbNodes() == 0 && st.getTimeCount() == 0);
Assert.assertEquals(m.getSolutionsStatistics().size(), 0);
ISolutionPool sp = SolutionPoolFactory.makeInfiniteSolutionPool(m.getSearchStrategy());
m.getSearchStrategy().setSolutionPool(sp);
m.launch();
Boolean ret = m.isFeasible();
Assert.assertTrue(ret);
List<SolutionStatistics> sols = m.getSolutionsStatistics();
//Every solutions, asc sorted wrt. solving duration
System.err.println(sols);
for (int i = 1; i < sols.size(); i++) {
SolutionStatistics s2 = sols.get(i);
SolutionStatistics s1 = sols.get(i - 1);
Assert.assertTrue(s2.getNbBacktracks() >= s1.getNbBacktracks());
Assert.assertTrue(s2.getNbNodes() >= s1.getNbNodes());
Assert.assertTrue(s2.getTimeCount() >= s1.getTimeCount());
Assert.assertTrue(s1.getObjective() >= s2.getObjective());
}
//Solving statistics at least equals to the last solution
SolutionStatistics s = sols.get(sols.size() - 1);
st = m.getSolvingStatistics();
System.err.println(st);
Assert.assertTrue(st.getNbBacktracks() >= s.getNbBacktracks());
Assert.assertTrue(st.getNbNodes() >= s.getNbNodes());
Assert.assertTrue(st.getTimeCount() >= s.getTimeCount());
System.err.flush();
} catch (Exception e) {
Assert.fail(e.getMessage(), e);
}
}
}