Skip to content

Commit

Permalink
Add code coverage for phase lifecycle and fix a few bugs (#194)
Browse files Browse the repository at this point in the history
* Add phase lifecycle test & fix wrong solvingStarted() calls

* Solving started/ended is now called only once per solve() execution

* Fix DefaultSolver#removePhaseLifecycleListener(listener)
  • Loading branch information
yurloc authored and ge0ffrey committed Jun 6, 2016
1 parent be14625 commit d27d630
Show file tree
Hide file tree
Showing 6 changed files with 97 additions and 6 deletions.
Expand Up @@ -148,7 +148,7 @@ public void phaseEnded(ConstructionHeuristicPhaseScope<Solution_> phaseScope) {

@Override
public void solvingEnded(DefaultSolverScope<Solution_> solverScope) {
super.solvingStarted(solverScope);
super.solvingEnded(solverScope);
entityPlacer.solvingEnded(solverScope);
decider.solvingEnded(solverScope);
}
Expand Down
Expand Up @@ -259,7 +259,7 @@ public void phaseEnded(ExhaustiveSearchPhaseScope<Solution_> phaseScope) {

@Override
public void solvingEnded(DefaultSolverScope<Solution_> solverScope) {
super.solvingStarted(solverScope);
super.solvingEnded(solverScope);
entitySelector.solvingEnded(solverScope);
decider.solvingEnded(solverScope);
}
Expand Down
Expand Up @@ -107,14 +107,12 @@ public void setAssertShadowVariablesAreNotStaleAfterStep(boolean assertShadowVar
public void solvingStarted(DefaultSolverScope<Solution_> solverScope) {
// bestSolutionRecaller.solvingStarted(...) is called by DefaultSolver
termination.solvingStarted(solverScope);
phaseLifecycleSupport.fireSolvingStarted(solverScope);
}

@Override
public void solvingEnded(DefaultSolverScope<Solution_> solverScope) {
// bestSolutionRecaller.solvingEnded(...) is called by DefaultSolver
termination.solvingEnded(solverScope);
phaseLifecycleSupport.fireSolvingEnded(solverScope);
}

@Override
Expand Down
Expand Up @@ -28,6 +28,7 @@
import org.optaplanner.core.config.solver.EnvironmentMode;
import org.optaplanner.core.impl.phase.Phase;
import org.optaplanner.core.impl.phase.event.PhaseLifecycleListener;
import org.optaplanner.core.impl.phase.event.PhaseLifecycleSupport;
import org.optaplanner.core.impl.score.director.InnerScoreDirectorFactory;
import org.optaplanner.core.impl.solver.event.SolverEventSupport;
import org.optaplanner.core.impl.solver.random.RandomFactory;
Expand All @@ -47,6 +48,7 @@ public class DefaultSolver<Solution_> implements Solver<Solution_> {
protected final transient Logger logger = LoggerFactory.getLogger(getClass());

protected SolverEventSupport<Solution_> solverEventSupport = new SolverEventSupport<>(this);
protected PhaseLifecycleSupport<Solution_> phaseLifecycleSupport = new PhaseLifecycleSupport<>();

protected EnvironmentMode environmentMode;
protected RandomFactory randomFactory;
Expand Down Expand Up @@ -214,6 +216,7 @@ public void solvingStarted(DefaultSolverScope<Solution_> solverScope) {
}
int startingSolverCount = solverScope.getStartingSolverCount() + 1;
solverScope.setStartingSolverCount(startingSolverCount);
phaseLifecycleSupport.fireSolvingStarted(solverScope);
logger.info("Solving {}: time spent ({}), best score ({}), environment mode ({}), random ({}).",
(startingSolverCount == 1 ? "started" : "restarted"),
solverScope.calculateTimeMillisSpentUpToNow(),
Expand All @@ -240,6 +243,7 @@ public void solvingEnded(DefaultSolverScope<Solution_> solverScope) {
}
bestSolutionRecaller.solvingEnded(solverScope);
solverScope.endingNow();
phaseLifecycleSupport.fireSolvingEnded(solverScope);
}

public void outerSolvingEnded(DefaultSolverScope<Solution_> solverScope) {
Expand Down Expand Up @@ -297,14 +301,16 @@ public void removeEventListener(SolverEventListener<Solution_> eventListener) {
}

public void addPhaseLifecycleListener(PhaseLifecycleListener<Solution_> phaseLifecycleListener) {
phaseLifecycleSupport.addEventListener(phaseLifecycleListener);
for (Phase phase : phaseList) {
phase.addPhaseLifecycleListener(phaseLifecycleListener);
}
}

public void removePhaseLifecycleListener(PhaseLifecycleListener<Solution_> phaseLifecycleListener) {
phaseLifecycleSupport.removeEventListener(phaseLifecycleListener);
for (Phase phase : phaseList) {
phase.addPhaseLifecycleListener(phaseLifecycleListener);
phase.removePhaseLifecycleListener(phaseLifecycleListener);
}
}

Expand Down
@@ -0,0 +1,85 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates.
*
* 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.optaplanner.core.impl.phase;

import java.util.ArrayList;
import java.util.Arrays;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.runners.MockitoJUnitRunner;
import org.optaplanner.core.api.solver.Solver;
import org.optaplanner.core.api.solver.SolverFactory;
import org.optaplanner.core.impl.phase.event.PhaseLifecycleListener;
import org.optaplanner.core.impl.solver.DefaultSolver;
import org.optaplanner.core.impl.testdata.domain.TestdataEntity;
import org.optaplanner.core.impl.testdata.domain.TestdataSolution;
import org.optaplanner.core.impl.testdata.domain.TestdataValue;
import org.optaplanner.core.impl.testdata.util.PlannerAssert;
import org.optaplanner.core.impl.testdata.util.PlannerTestUtils;

@RunWith(MockitoJUnitRunner.class)
public class PhaseLifecyleTest {

@Mock
private PhaseLifecycleListener<TestdataSolution> listener;

@Test
public void verifyEventCounts() {
// prepare solver
SolverFactory<TestdataSolution> solverFactory = PlannerTestUtils.buildSolverFactory(
TestdataSolution.class, TestdataEntity.class);
Solver<TestdataSolution> solver = solverFactory.buildSolver();

// prepare solution
TestdataSolution solution = new TestdataSolution("s1");
solution.setValueList(Arrays.asList(new TestdataValue("v1"), new TestdataValue("v1")));
final int entitiesCount = 17;
ArrayList<TestdataEntity> entities = new ArrayList<>(entitiesCount);
for (int i = 0; i < entitiesCount; i++) {
entities.add(new TestdataEntity(String.valueOf(i)));
}
solution.setEntityList(entities);

// add listener mock and solve
((DefaultSolver<TestdataSolution>) solver).addPhaseLifecycleListener(listener);
TestdataSolution solvedSolution = solver.solve(solution);

// step count = number of uninitialized entities (CH) + LS step count limit
final int stepCount = entitiesCount + PlannerTestUtils.TERMINATION_STEP_COUNT_LIMIT;
final int phaseCount = solverFactory.getSolverConfig().getPhaseConfigList().size();
final int solvingCount = 1;
PlannerAssert.verifyPhaseLifecycle(listener, solvingCount, phaseCount, stepCount);

// forget previous invocations
Mockito.<PhaseLifecycleListener<?>>reset(listener);

// uninitialize 1 entity and solve again
solvedSolution.getEntityList().get(0).setValue(null);
solver.solve(solvedSolution);
PlannerAssert.verifyPhaseLifecycle(listener, solvingCount, phaseCount, 1 + PlannerTestUtils.TERMINATION_STEP_COUNT_LIMIT);

// forget previous invocations
Mockito.<PhaseLifecycleListener<?>>reset(listener);

// remove listener and solve again
((DefaultSolver<TestdataSolution>) solver).removePhaseLifecycleListener(listener);
solver.solve(solution);
PlannerAssert.verifyPhaseLifecycle(listener, 0, 0, 0);
}
}
Expand Up @@ -52,6 +52,8 @@
*/
public class PlannerTestUtils {

public static final int TERMINATION_STEP_COUNT_LIMIT = 10;

// ************************************************************************
// SolverFactory methods
// ************************************************************************
Expand All @@ -70,7 +72,7 @@ public static <Solution_> SolverFactory<Solution_> buildSolverFactory(
phaseConfigList.add(new ConstructionHeuristicPhaseConfig());
LocalSearchPhaseConfig localSearchPhaseConfig = new LocalSearchPhaseConfig();
TerminationConfig terminationConfig = new TerminationConfig();
terminationConfig.setStepCountLimit(10);
terminationConfig.setStepCountLimit(TERMINATION_STEP_COUNT_LIMIT);
localSearchPhaseConfig.setTerminationConfig(terminationConfig);
phaseConfigList.add(localSearchPhaseConfig);
solverConfig.setPhaseConfigList(phaseConfigList);
Expand Down

0 comments on commit d27d630

Please sign in to comment.