/*
 * Copyright 2019 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.api.solver;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutionException;

import org.junit.Test;
import org.optaplanner.core.config.constructionheuristic.ConstructionHeuristicPhaseConfig;
import org.optaplanner.core.config.phase.custom.CustomPhaseConfig;
import org.optaplanner.core.config.solver.SolverConfig;
import org.optaplanner.core.config.solver.SolverManagerConfig;
import org.optaplanner.core.impl.testdata.domain.TestdataEntity;
import org.optaplanner.core.impl.testdata.domain.TestdataSolution;
import org.optaplanner.core.impl.testdata.util.PlannerTestUtils;

import static org.junit.Assert.*;
import static org.optaplanner.core.impl.testdata.util.PlannerAssert.*;

public class SolverManagerTest {

    @Test(timeout = 600_000)
    public void solveBatch_2InParallel() throws ExecutionException, InterruptedException {
        CyclicBarrier barrier = new CyclicBarrier(2);
        final SolverConfig solverConfig = PlannerTestUtils.buildSolverConfig(TestdataSolution.class, TestdataEntity.class)
                .withPhases(new CustomPhaseConfig().withCustomPhaseCommands(
                        scoreDirector -> {
                            try {
                                barrier.await();
                            } catch (InterruptedException | BrokenBarrierException e) {
                                fail("Cyclic barrier failed.");
                            }
                        }
                ), new ConstructionHeuristicPhaseConfig());
        SolverManagerConfig solverManagerConfig = new SolverManagerConfig(solverConfig)
                .withParallelSolverCount("2");
        SolverManager<TestdataSolution, Long> solverManager = SolverManager.create(solverManagerConfig);

        SolverJob<TestdataSolution, Long> solverJob1 = solverManager.solveBatch(1L,
                PlannerTestUtils.generateTestdataSolution("s1"));
        SolverJob<TestdataSolution, Long> solverJob2 = solverManager.solveBatch(2L,
                PlannerTestUtils.generateTestdataSolution("s2"));

        assertSolutionInitialized(solverJob1.getFinalBestSolution());
        assertSolutionInitialized(solverJob2.getFinalBestSolution());
    }

    @Test(timeout = 600_000)
    public void getSolverStatus() throws InterruptedException, BrokenBarrierException, ExecutionException {
        CyclicBarrier solverThreadReadyBarrier = new CyclicBarrier(2);
        CyclicBarrier mainThreadReadyBarrier = new CyclicBarrier(2);
        final SolverConfig solverConfig = PlannerTestUtils.buildSolverConfig(TestdataSolution.class, TestdataEntity.class)
                .withPhases(new CustomPhaseConfig().withCustomPhaseCommands(
                        scoreDirector -> {
                            try {
                                solverThreadReadyBarrier.await();
                            } catch (InterruptedException | BrokenBarrierException e) {
                                fail("Cyclic barrier failed.");
                            }
                            try {
                                mainThreadReadyBarrier.await();
                            } catch (InterruptedException | BrokenBarrierException e) {
                                fail("Cyclic barrier failed.");
                            }
                        }
                ), new ConstructionHeuristicPhaseConfig());
        SolverManagerConfig solverManagerConfig = new SolverManagerConfig(solverConfig)
                .withParallelSolverCount("1");
        SolverManager<TestdataSolution, Long> solverManager = SolverManager.create(solverManagerConfig);

        SolverJob<TestdataSolution, Long> solverJob1 = solverManager.solveBatch(1L,
                PlannerTestUtils.generateTestdataSolution("s1"));
        SolverJob<TestdataSolution, Long> solverJob2 = solverManager.solveBatch(2L,
                PlannerTestUtils.generateTestdataSolution("s2"));
        solverThreadReadyBarrier.await();
        assertEquals(SolverStatus.SOLVING_ACTIVE, solverManager.getSolverStatus(1L));
        assertEquals(SolverStatus.SOLVING_SCHEDULED, solverManager.getSolverStatus(2L));
        mainThreadReadyBarrier.await();
        solverThreadReadyBarrier.await();
        assertEquals(SolverStatus.NOT_SOLVING, solverManager.getSolverStatus(1L));
        assertEquals(SolverStatus.SOLVING_ACTIVE, solverManager.getSolverStatus(2L));
        mainThreadReadyBarrier.await();
        solverJob1.getFinalBestSolution();
        solverJob2.getFinalBestSolution();
        assertEquals(SolverStatus.NOT_SOLVING, solverManager.getSolverStatus(1L));
        assertEquals(SolverStatus.NOT_SOLVING, solverManager.getSolverStatus(2L));
    }

}

@@ -4056,32 +4056,124 @@
 PhaseConfig());%0A
+        // Only 1 solver can run at the same time to predict the solver status of each job.%0A
         SolverMa
@@ -4236,32 +4236,32 @@
 g(solverConfig)%0A
-
                 
@@ -4539,32 +4539,74 @@
 olution(%22s1%22));%0A
+        solverThreadReadyBarrier.await();%0A
         SolverJo
@@ -4743,50 +4743,8 @@
 ));%0A
-        solverThreadReadyBarrier.await();%0A
     
@@ -4817,32 +4817,113 @@
 verStatus(1L));%0A
+        assertEquals(SolverStatus.SOLVING_ACTIVE, solverJob1.getSolverStatus());%0A
         assertEq
@@ -4987,32 +4987,116 @@
 verStatus(2L));%0A
+        assertEquals(SolverStatus.SOLVING_SCHEDULED, solverJob2.getSolverStatus());%0A
         mainThre
@@ -5236,32 +5236,110 @@
 verStatus(1L));%0A
+        assertEquals(SolverStatus.NOT_SOLVING, solverJob1.getSolverStatus());%0A
         assertEq
@@ -5400,32 +5400,113 @@
 verStatus(2L));%0A
+        assertEquals(SolverStatus.SOLVING_ACTIVE, solverJob2.getSolverStatus());%0A
         mainThre
@@ -5690,32 +5690,110 @@
 verStatus(1L));%0A
+        assertEquals(SolverStatus.NOT_SOLVING, solverJob1.getSolverStatus());%0A
         assertEq
@@ -5855,21 +5855,99 @@
 tatus(2L));%0A
+        assertEquals(SolverStatus.NOT_SOLVING, solverJob2.getSolverStatus());%0A
     %7D%0A%0A%7D%0A

improve test getSolverStatus