/**
* Copyright 2011-2017 Asakusa Framework Team.
*
* 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 com.asakusafw.yaess.core.task;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.TreeSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import com.asakusafw.runtime.core.context.RuntimeContext;
import com.asakusafw.runtime.core.context.RuntimeContext.ExecutionMode;
import com.asakusafw.yaess.basic.BasicLockProvider;
import com.asakusafw.yaess.basic.BasicMonitorProvider;
import com.asakusafw.yaess.core.CommandScript;
import com.asakusafw.yaess.core.ExecutionLock;
import com.asakusafw.yaess.core.ExecutionPhase;
import com.asakusafw.yaess.core.ProfileContext;
import com.asakusafw.yaess.core.YaessProfile;
import com.asakusafw.yaess.core.task.ExecutionTracker.Record;
/**
* Test for {@link ExecutionTask}.
*/
public class ExecutionTaskTest {
/**
* Temporary folder.
*/
@Rule
public final TemporaryFolder folder = new TemporaryFolder();
/**
* Initializes the test.
* @throws Exception if some errors were occurred
*/
@Before
public void setUp() throws Exception {
SerialExecutionTracker.clear();
}
/**
* Cleans up the test.
* @throws Exception if some errors were occurred
*/
@After
public void tearDown() throws Exception {
SerialExecutionTracker.clear();
}
/**
* Execute setup phase.
* @throws Exception if failed
*/
@Test
public void phase_setup() throws Exception {
ProfileBuilder prf = new ProfileBuilder(folder.getRoot());
ExecutionTask task = prf.task();
task.executePhase("batch", "testing", "f-setup", ExecutionPhase.SETUP);
List<Record> results = SerialExecutionTracker.get(prf.trackingId);
verifyPhaseOrder(results);
assertThat(results.size(), is(2));
List<Record> records = phase(results, "testing", ExecutionPhase.SETUP);
assertThat(records, is(results));
}
/**
* Execute initialize phase.
* @throws Exception if failed
*/
@Test
public void phase_initialize() throws Exception {
ProfileBuilder prf = new ProfileBuilder(folder.getRoot());
ExecutionTask task = prf.task();
task.executePhase("batch", "testing", "f-init", ExecutionPhase.INITIALIZE);
List<Record> results = SerialExecutionTracker.get(prf.trackingId);
verifyPhaseOrder(results);
assertThat(results.size(), is(1));
List<Record> records = phase(results, "testing", ExecutionPhase.INITIALIZE);
assertThat(records, is(results));
}
/**
* Execute import phase.
* @throws Exception if failed
*/
@Test
public void phase_import() throws Exception {
ProfileBuilder prf = new ProfileBuilder(folder.getRoot());
ExecutionTask task = prf.task();
task.executePhase("batch", "testing", "f-imp", ExecutionPhase.IMPORT);
List<Record> results = SerialExecutionTracker.get(prf.trackingId);
verifyPhaseOrder(results);
assertThat(results.size(), is(2));
List<Record> records = phase(results, "testing", ExecutionPhase.IMPORT);
assertThat(records, is(results));
}
/**
* Execute prologue phase.
* @throws Exception if failed
*/
@Test
public void phase_prologue() throws Exception {
ProfileBuilder prf = new ProfileBuilder(folder.getRoot());
ExecutionTask task = prf.task();
task.executePhase("batch", "testing", "f-pro", ExecutionPhase.PROLOGUE);
List<Record> results = SerialExecutionTracker.get(prf.trackingId);
verifyPhaseOrder(results);
assertThat(results.size(), is(1));
List<Record> records = phase(results, "testing", ExecutionPhase.PROLOGUE);
assertThat(records, is(results));
}
/**
* Execute main phase.
* @throws Exception if failed
*/
@Test
public void phase_main() throws Exception {
ProfileBuilder prf = new ProfileBuilder(folder.getRoot());
ExecutionTask task = prf.task();
task.executePhase("batch", "testing", "f1", ExecutionPhase.MAIN);
List<Record> results = SerialExecutionTracker.get(prf.trackingId);
verifyPhaseOrder(results);
assertThat(results.size(), is(4));
assertThat(id(results), is(set("a", "b", "c", "d")));
checkScriptHappensBefore(results, "a", "b");
checkScriptHappensBefore(results, "a", "c");
checkScriptHappensBefore(results, "b", "d");
checkScriptHappensBefore(results, "c", "d");
List<Record> records = phase(results, "testing", ExecutionPhase.MAIN);
assertThat(records, is(results));
}
/**
* Execute epilogue phase.
* @throws Exception if failed
*/
@Test
public void phase_epilogue() throws Exception {
ProfileBuilder prf = new ProfileBuilder(folder.getRoot());
ExecutionTask task = prf.task();
task.executePhase("batch", "testing", "f-pro", ExecutionPhase.EPILOGUE);
List<Record> results = SerialExecutionTracker.get(prf.trackingId);
verifyPhaseOrder(results);
assertThat(results.size(), is(1));
List<Record> records = phase(results, "testing", ExecutionPhase.EPILOGUE);
assertThat(records, is(results));
}
/**
* Execute export phase.
* @throws Exception if failed
*/
@Test
public void phase_export() throws Exception {
ProfileBuilder prf = new ProfileBuilder(folder.getRoot());
ExecutionTask task = prf.task();
task.executePhase("batch", "testing", "f-exp", ExecutionPhase.EXPORT);
List<Record> results = SerialExecutionTracker.get(prf.trackingId);
verifyPhaseOrder(results);
assertThat(results.size(), is(2));
List<Record> records = phase(results, "testing", ExecutionPhase.EXPORT);
assertThat(records, is(results));
}
/**
* Execute finalize phase.
* @throws Exception if failed
*/
@Test
public void phase_finalize() throws Exception {
ProfileBuilder prf = new ProfileBuilder(folder.getRoot());
ExecutionTask task = prf.task();
task.executePhase("batch", "testing", "f-fin", ExecutionPhase.FINALIZE);
List<Record> results = SerialExecutionTracker.get(prf.trackingId);
verifyPhaseOrder(results);
assertThat(results.size(), is(2));
List<Record> records = phase(results, "testing", ExecutionPhase.FINALIZE);
assertThat(records, is(results));
}
/**
* Execute cleanup phase.
* @throws Exception if failed
*/
@Test
public void phase_cleanup() throws Exception {
ProfileBuilder prf = new ProfileBuilder(folder.getRoot());
ExecutionTask task = prf.task();
task.executePhase("batch", "testing", "f-clean", ExecutionPhase.CLEANUP);
List<Record> results = SerialExecutionTracker.get(prf.trackingId);
verifyPhaseOrder(results);
assertThat(results.size(), is(2));
List<Record> records = phase(results, "testing", ExecutionPhase.CLEANUP);
assertThat(records, is(results));
}
/**
* Execute phase but will be skipped.
* @throws Exception if failed
*/
@Test
public void phase_sskip() throws Exception {
ProfileBuilder prf = new ProfileBuilder(folder.getRoot());
ExecutionTask task = prf.task();
task.getSkipFlows().add("testing");
task.executePhase("batch", "testing", "f-setup", ExecutionPhase.SETUP);
List<Record> results = SerialExecutionTracker.get(prf.trackingId);
assertThat(results, is(Collections.emptyList()));
}
/**
* Execute phase as simulation mode.
* @throws Exception if failed
*/
@Test
public void phase_sim() throws Exception {
ProfileBuilder prf = new ProfileBuilder(folder.getRoot());
prf.setInvalid();
ExecutionTask task = prf.task();
task.setRuntimeContext(RuntimeContext.DEFAULT.mode(ExecutionMode.SIMULATION));
task.executePhase("batch", "testing", "f1", ExecutionPhase.MAIN);
}
/**
* Executes flow.
* @throws Exception if failed
*/
@Test
public void executeFlow() throws Exception {
ProfileBuilder prf = new ProfileBuilder(folder.getRoot());
ExecutionTask task = prf.task();
task.executeFlow("batch", "testing", "flow");
List<Record> results = SerialExecutionTracker.get(prf.trackingId);
verifyPhaseOrder(results);
assertThat(phase(results, "testing", ExecutionPhase.SETUP).size(), is(2));
assertThat(phase(results, "testing", ExecutionPhase.INITIALIZE).size(), is(1));
assertThat(phase(results, "testing", ExecutionPhase.IMPORT).size(), is(2));
assertThat(phase(results, "testing", ExecutionPhase.PROLOGUE).size(), is(1));
assertThat(phase(results, "testing", ExecutionPhase.MAIN).size(), is(4));
assertThat(phase(results, "testing", ExecutionPhase.EPILOGUE).size(), is(1));
assertThat(phase(results, "testing", ExecutionPhase.EXPORT).size(), is(2));
assertThat(phase(results, "testing", ExecutionPhase.FINALIZE).size(), is(2));
assertThat(phase(results, "testing", ExecutionPhase.CLEANUP).size(), is(2));
}
/**
* Executes flow but is skipped.
* @throws Exception if failed
*/
@Test
public void executeFlow_skip() throws Exception {
ProfileBuilder prf = new ProfileBuilder(folder.getRoot());
ExecutionTask task = prf.task();
task.getSkipFlows().add("testing");
task.executeFlow("batch", "testing", "flow");
List<Record> results = SerialExecutionTracker.get(prf.trackingId);
assertThat(results, is(Collections.emptyList()));
}
/**
* Executes flow in simulation mode.
* @throws Exception if failed
*/
@Test
public void executeFlow_sim() throws Exception {
ProfileBuilder prf = new ProfileBuilder(folder.getRoot());
prf.setInvalid();
ExecutionTask task = prf.task();
task.setRuntimeContext(RuntimeContext.DEFAULT.mode(ExecutionMode.SIMULATION));
task.executeFlow("batch", "testing", "flow");
}
/**
* Executes flow but exporter is failed.
* @throws Exception if failed
*/
@Test
public void executeFlow_failed_export() throws Exception {
ProfileBuilder prf = new ProfileBuilder(folder.getRoot());
prf.setTracker(ExporterFailed.class);
ExecutionTask task = prf.task();
try {
task.executeFlow("batch", "testing", "flow");
fail();
} catch (IOException e) {
// ok.
}
List<Record> results = SerialExecutionTracker.get(prf.trackingId);
verifyPhaseOrder(results);
assertThat(phase(results, "testing", ExecutionPhase.SETUP).size(), is(2));
assertThat(phase(results, "testing", ExecutionPhase.INITIALIZE).size(), is(1));
assertThat(phase(results, "testing", ExecutionPhase.IMPORT).size(), is(2));
assertThat(phase(results, "testing", ExecutionPhase.PROLOGUE).size(), is(1));
assertThat(phase(results, "testing", ExecutionPhase.MAIN).size(), is(4));
assertThat(phase(results, "testing", ExecutionPhase.EPILOGUE).size(), is(1));
assertThat(phase(results, "testing", ExecutionPhase.EXPORT).size(), lessThan(2));
assertThat(phase(results, "testing", ExecutionPhase.FINALIZE).size(), is(2));
assertThat(phase(results, "testing", ExecutionPhase.CLEANUP).size(), is(0));
}
/**
* Executes flow but finalizer is failed.
* @throws Exception if failed
*/
@Test
public void executeFlow_failed_finalize() throws Exception {
ProfileBuilder prf = new ProfileBuilder(folder.getRoot());
prf.setTracker(FinalizerFailed.class);
ExecutionTask task = prf.task();
try {
task.executeFlow("batch", "testing", "flow");
fail();
} catch (IOException e) {
// ok.
}
List<Record> results = SerialExecutionTracker.get(prf.trackingId);
verifyPhaseOrder(results);
assertThat(phase(results, "testing", ExecutionPhase.SETUP).size(), is(2));
assertThat(phase(results, "testing", ExecutionPhase.INITIALIZE).size(), is(1));
assertThat(phase(results, "testing", ExecutionPhase.IMPORT).size(), is(2));
assertThat(phase(results, "testing", ExecutionPhase.PROLOGUE).size(), is(1));
assertThat(phase(results, "testing", ExecutionPhase.MAIN).size(), is(4));
assertThat(phase(results, "testing", ExecutionPhase.EPILOGUE).size(), is(1));
assertThat(phase(results, "testing", ExecutionPhase.EXPORT).size(), is(2));
assertThat(phase(results, "testing", ExecutionPhase.FINALIZE).size(), is(1));
assertThat(phase(results, "testing", ExecutionPhase.CLEANUP).size(), is(0));
}
/**
* Executes flow but cleanup is failed.
* @throws Exception if failed
*/
@Test
public void executeFlow_failed_cleanup() throws Exception {
ProfileBuilder prf = new ProfileBuilder(folder.getRoot());
prf.setTracker(CleanerFailed.class);
ExecutionTask task = prf.task();
task.executeFlow("batch", "testing", "flow");
List<Record> results = SerialExecutionTracker.get(prf.trackingId);
verifyPhaseOrder(results);
assertThat(phase(results, "testing", ExecutionPhase.SETUP).size(), is(2));
assertThat(phase(results, "testing", ExecutionPhase.INITIALIZE).size(), is(1));
assertThat(phase(results, "testing", ExecutionPhase.IMPORT).size(), is(2));
assertThat(phase(results, "testing", ExecutionPhase.PROLOGUE).size(), is(1));
assertThat(phase(results, "testing", ExecutionPhase.MAIN).size(), is(4));
assertThat(phase(results, "testing", ExecutionPhase.EPILOGUE).size(), is(1));
assertThat(phase(results, "testing", ExecutionPhase.EXPORT).size(), is(2));
assertThat(phase(results, "testing", ExecutionPhase.FINALIZE).size(), is(2));
assertThat(phase(results, "testing", ExecutionPhase.CLEANUP).size(), is(1));
}
/**
* Executes batch.
* @throws Exception if failed
*/
@Test
public void executeBatch() throws Exception {
ProfileBuilder prf = new ProfileBuilder(folder.getRoot());
ExecutionTask task = prf.task();
task.executeBatch("batch");
List<Record> results = SerialExecutionTracker.get(prf.trackingId);
checkFlowHappensBefore(results, "testing", "left");
checkFlowHappensBefore(results, "testing", "right");
checkFlowHappensBefore(results, "left", "last");
checkFlowHappensBefore(results, "right", "last");
verifyPhaseOrder(results);
assertThat(phase(results, "testing", ExecutionPhase.SETUP).size(), is(2));
assertThat(phase(results, "testing", ExecutionPhase.INITIALIZE).size(), is(1));
assertThat(phase(results, "testing", ExecutionPhase.IMPORT).size(), is(2));
assertThat(phase(results, "testing", ExecutionPhase.PROLOGUE).size(), is(1));
assertThat(phase(results, "testing", ExecutionPhase.MAIN).size(), is(4));
assertThat(phase(results, "testing", ExecutionPhase.EPILOGUE).size(), is(1));
assertThat(phase(results, "testing", ExecutionPhase.EXPORT).size(), is(2));
assertThat(phase(results, "testing", ExecutionPhase.FINALIZE).size(), is(2));
assertThat(phase(results, "testing", ExecutionPhase.CLEANUP).size(), is(2));
assertThat(phase(results, "left", ExecutionPhase.SETUP).size(), is(2));
assertThat(phase(results, "left", ExecutionPhase.INITIALIZE).size(), is(0));
assertThat(phase(results, "left", ExecutionPhase.IMPORT).size(), is(0));
assertThat(phase(results, "left", ExecutionPhase.PROLOGUE).size(), is(0));
assertThat(phase(results, "left", ExecutionPhase.MAIN).size(), is(1));
assertThat(phase(results, "left", ExecutionPhase.EPILOGUE).size(), is(0));
assertThat(phase(results, "left", ExecutionPhase.EXPORT).size(), is(0));
assertThat(phase(results, "left", ExecutionPhase.FINALIZE).size(), is(0));
assertThat(phase(results, "left", ExecutionPhase.CLEANUP).size(), is(2));
assertThat(phase(results, "right", ExecutionPhase.SETUP).size(), is(2));
assertThat(phase(results, "right", ExecutionPhase.INITIALIZE).size(), is(0));
assertThat(phase(results, "right", ExecutionPhase.IMPORT).size(), is(0));
assertThat(phase(results, "right", ExecutionPhase.PROLOGUE).size(), is(0));
assertThat(phase(results, "right", ExecutionPhase.MAIN).size(), is(1));
assertThat(phase(results, "right", ExecutionPhase.EPILOGUE).size(), is(0));
assertThat(phase(results, "right", ExecutionPhase.EXPORT).size(), is(0));
assertThat(phase(results, "right", ExecutionPhase.FINALIZE).size(), is(0));
assertThat(phase(results, "right", ExecutionPhase.CLEANUP).size(), is(2));
assertThat(phase(results, "last", ExecutionPhase.SETUP).size(), is(2));
assertThat(phase(results, "last", ExecutionPhase.INITIALIZE).size(), is(0));
assertThat(phase(results, "last", ExecutionPhase.IMPORT).size(), is(0));
assertThat(phase(results, "last", ExecutionPhase.PROLOGUE).size(), is(0));
assertThat(phase(results, "last", ExecutionPhase.MAIN).size(), is(1));
assertThat(phase(results, "last", ExecutionPhase.EPILOGUE).size(), is(0));
assertThat(phase(results, "last", ExecutionPhase.EXPORT).size(), is(0));
assertThat(phase(results, "last", ExecutionPhase.FINALIZE).size(), is(0));
assertThat(phase(results, "last", ExecutionPhase.CLEANUP).size(), is(2));
}
/**
* Executes batch but some flows are skipped.
* @throws Exception if failed
*/
@Test
public void executeBatch_skip() throws Exception {
ProfileBuilder prf = new ProfileBuilder(folder.getRoot());
ExecutionTask task = prf.task();
task.getSkipFlows().add("left");
task.executeBatch("batch");
List<Record> results = SerialExecutionTracker.get(prf.trackingId);
checkFlowHappensBefore(results, "testing", "right");
checkFlowHappensBefore(results, "right", "last");
verifyPhaseOrder(results);
assertThat(phase(results, "testing", ExecutionPhase.SETUP).size(), is(2));
assertThat(phase(results, "testing", ExecutionPhase.INITIALIZE).size(), is(1));
assertThat(phase(results, "testing", ExecutionPhase.IMPORT).size(), is(2));
assertThat(phase(results, "testing", ExecutionPhase.PROLOGUE).size(), is(1));
assertThat(phase(results, "testing", ExecutionPhase.MAIN).size(), is(4));
assertThat(phase(results, "testing", ExecutionPhase.EPILOGUE).size(), is(1));
assertThat(phase(results, "testing", ExecutionPhase.EXPORT).size(), is(2));
assertThat(phase(results, "testing", ExecutionPhase.FINALIZE).size(), is(2));
assertThat(phase(results, "testing", ExecutionPhase.CLEANUP).size(), is(2));
assertThat(flow(results, "left").size(), is(0));
assertThat(phase(results, "right", ExecutionPhase.SETUP).size(), is(2));
assertThat(phase(results, "right", ExecutionPhase.INITIALIZE).size(), is(0));
assertThat(phase(results, "right", ExecutionPhase.IMPORT).size(), is(0));
assertThat(phase(results, "right", ExecutionPhase.PROLOGUE).size(), is(0));
assertThat(phase(results, "right", ExecutionPhase.MAIN).size(), is(1));
assertThat(phase(results, "right", ExecutionPhase.EPILOGUE).size(), is(0));
assertThat(phase(results, "right", ExecutionPhase.EXPORT).size(), is(0));
assertThat(phase(results, "right", ExecutionPhase.FINALIZE).size(), is(0));
assertThat(phase(results, "right", ExecutionPhase.CLEANUP).size(), is(2));
assertThat(phase(results, "last", ExecutionPhase.SETUP).size(), is(2));
assertThat(phase(results, "last", ExecutionPhase.INITIALIZE).size(), is(0));
assertThat(phase(results, "last", ExecutionPhase.IMPORT).size(), is(0));
assertThat(phase(results, "last", ExecutionPhase.PROLOGUE).size(), is(0));
assertThat(phase(results, "last", ExecutionPhase.MAIN).size(), is(1));
assertThat(phase(results, "last", ExecutionPhase.EPILOGUE).size(), is(0));
assertThat(phase(results, "last", ExecutionPhase.EXPORT).size(), is(0));
assertThat(phase(results, "last", ExecutionPhase.FINALIZE).size(), is(0));
assertThat(phase(results, "last", ExecutionPhase.CLEANUP).size(), is(2));
}
/**
* Executes batch but some flows are skipped.
* @throws Exception if failed
*/
@Test
public void executeBatch_seriaize() throws Exception {
ProfileBuilder prf = new ProfileBuilder(folder.getRoot());
prf.setTracker(FlowSerialized.class);
ExecutionTask task = prf.task();
task.setSerializeFlows(true);
task.executeBatch("batch");
List<Record> results = SerialExecutionTracker.get(prf.trackingId);
checkFlowHappensBefore(results, "testing", "left");
checkFlowHappensBefore(results, "testing", "right");
checkFlowHappensBefore(results, "left", "last");
checkFlowHappensBefore(results, "right", "last");
verifyPhaseOrder(results);
}
/**
* Executes batch with simulation mode.
* @throws Exception if failed
*/
@Test
public void executeBatch_sim() throws Exception {
ProfileBuilder prf = new ProfileBuilder(folder.getRoot());
prf.setInvalid();
ExecutionTask task = prf.task();
task.setRuntimeContext(RuntimeContext.DEFAULT.mode(ExecutionMode.SIMULATION));
task.executeBatch("batch");
}
/**
* Executes batch but exporter is failed.
* @throws Exception if failed
*/
@Test
public void executeBatch_failed_export() throws Exception {
ProfileBuilder prf = new ProfileBuilder(folder.getRoot());
prf.setTracker(ExporterFailed.class);
ExecutionTask task = prf.task();
try {
task.executeBatch("batch");
fail();
} catch (IOException e) {
// ok.
}
List<Record> results = SerialExecutionTracker.get(prf.trackingId);
checkFlowHappensBefore(results, "testing", "left");
checkFlowHappensBefore(results, "testing", "right");
checkFlowHappensBefore(results, "left", "last");
checkFlowHappensBefore(results, "right", "last");
verifyPhaseOrder(results);
assertThat(phase(results, "testing", ExecutionPhase.SETUP).size(), is(2));
assertThat(phase(results, "testing", ExecutionPhase.INITIALIZE).size(), is(1));
assertThat(phase(results, "testing", ExecutionPhase.IMPORT).size(), is(2));
assertThat(phase(results, "testing", ExecutionPhase.PROLOGUE).size(), is(1));
assertThat(phase(results, "testing", ExecutionPhase.MAIN).size(), is(4));
assertThat(phase(results, "testing", ExecutionPhase.EPILOGUE).size(), is(1));
assertThat(phase(results, "testing", ExecutionPhase.EXPORT).size(), lessThan(2));
assertThat(phase(results, "testing", ExecutionPhase.FINALIZE).size(), is(2));
assertThat(phase(results, "testing", ExecutionPhase.CLEANUP).size(), is(0));
assertThat(flow(results, "left").size(), is(0));
assertThat(flow(results, "right").size(), is(0));
assertThat(flow(results, "last").size(), is(0));
}
/**
* Executes batch but finalizer is failed.
* @throws Exception if failed
*/
@Test
public void executeBatch_failed_finalize() throws Exception {
ProfileBuilder prf = new ProfileBuilder(folder.getRoot());
prf.setTracker(FinalizerFailed.class);
ExecutionTask task = prf.task();
try {
task.executeBatch("batch");
fail();
} catch (IOException e) {
// ok.
}
List<Record> results = SerialExecutionTracker.get(prf.trackingId);
verifyPhaseOrder(results);
assertThat(phase(results, "testing", ExecutionPhase.SETUP).size(), is(2));
assertThat(phase(results, "testing", ExecutionPhase.INITIALIZE).size(), is(1));
assertThat(phase(results, "testing", ExecutionPhase.IMPORT).size(), is(2));
assertThat(phase(results, "testing", ExecutionPhase.PROLOGUE).size(), is(1));
assertThat(phase(results, "testing", ExecutionPhase.MAIN).size(), is(4));
assertThat(phase(results, "testing", ExecutionPhase.EPILOGUE).size(), is(1));
assertThat(phase(results, "testing", ExecutionPhase.EXPORT).size(), is(2));
assertThat(phase(results, "testing", ExecutionPhase.FINALIZE).size(), is(1));
assertThat(phase(results, "testing", ExecutionPhase.CLEANUP).size(), is(0));
assertThat(flow(results, "left").size(), is(0));
assertThat(flow(results, "right").size(), is(0));
assertThat(flow(results, "last").size(), is(0));
}
/**
* Executes batch but cleanup is failed.
* @throws Exception if failed
*/
@Test
public void executeBatch_failed_cleanup() throws Exception {
ProfileBuilder prf = new ProfileBuilder(folder.getRoot());
prf.setTracker(CleanerFailed.class);
ExecutionTask task = prf.task();
task.executeBatch("batch");
List<Record> results = SerialExecutionTracker.get(prf.trackingId);
verifyPhaseOrder(results);
assertThat(phase(results, "testing", ExecutionPhase.SETUP).size(), is(2));
assertThat(phase(results, "testing", ExecutionPhase.INITIALIZE).size(), is(1));
assertThat(phase(results, "testing", ExecutionPhase.IMPORT).size(), is(2));
assertThat(phase(results, "testing", ExecutionPhase.PROLOGUE).size(), is(1));
assertThat(phase(results, "testing", ExecutionPhase.MAIN).size(), is(4));
assertThat(phase(results, "testing", ExecutionPhase.EPILOGUE).size(), is(1));
assertThat(phase(results, "testing", ExecutionPhase.EXPORT).size(), is(2));
assertThat(phase(results, "testing", ExecutionPhase.FINALIZE).size(), is(2));
assertThat(phase(results, "testing", ExecutionPhase.CLEANUP).size(), is(1));
assertThat(phase(results, "left", ExecutionPhase.SETUP).size(), is(2));
assertThat(phase(results, "left", ExecutionPhase.INITIALIZE).size(), is(0));
assertThat(phase(results, "left", ExecutionPhase.IMPORT).size(), is(0));
assertThat(phase(results, "left", ExecutionPhase.PROLOGUE).size(), is(0));
assertThat(phase(results, "left", ExecutionPhase.MAIN).size(), is(1));
assertThat(phase(results, "left", ExecutionPhase.EPILOGUE).size(), is(0));
assertThat(phase(results, "left", ExecutionPhase.EXPORT).size(), is(0));
assertThat(phase(results, "left", ExecutionPhase.FINALIZE).size(), is(0));
assertThat(phase(results, "left", ExecutionPhase.CLEANUP).size(), lessThan(2));
assertThat(phase(results, "right", ExecutionPhase.SETUP).size(), is(2));
assertThat(phase(results, "right", ExecutionPhase.INITIALIZE).size(), is(0));
assertThat(phase(results, "right", ExecutionPhase.IMPORT).size(), is(0));
assertThat(phase(results, "right", ExecutionPhase.PROLOGUE).size(), is(0));
assertThat(phase(results, "right", ExecutionPhase.MAIN).size(), is(1));
assertThat(phase(results, "right", ExecutionPhase.EPILOGUE).size(), is(0));
assertThat(phase(results, "right", ExecutionPhase.EXPORT).size(), is(0));
assertThat(phase(results, "right", ExecutionPhase.FINALIZE).size(), is(0));
assertThat(phase(results, "right", ExecutionPhase.CLEANUP).size(), lessThan(2));
assertThat(phase(results, "last", ExecutionPhase.SETUP).size(), is(2));
assertThat(phase(results, "last", ExecutionPhase.INITIALIZE).size(), is(0));
assertThat(phase(results, "last", ExecutionPhase.IMPORT).size(), is(0));
assertThat(phase(results, "last", ExecutionPhase.PROLOGUE).size(), is(0));
assertThat(phase(results, "last", ExecutionPhase.MAIN).size(), is(1));
assertThat(phase(results, "last", ExecutionPhase.EPILOGUE).size(), is(0));
assertThat(phase(results, "last", ExecutionPhase.EXPORT).size(), is(0));
assertThat(phase(results, "last", ExecutionPhase.FINALIZE).size(), is(0));
assertThat(phase(results, "last", ExecutionPhase.CLEANUP).size(), lessThan(2));
}
private void checkScriptHappensBefore(List<Record> results, String head, String follow) {
boolean sawFollow = false;
for (Record r : results) {
String id = id(r);
if (head.equals(id)) {
if (sawFollow) {
throw new AssertionError(head + "=>" + follow);
}
} else if (follow.equals(id)) {
sawFollow = false;
}
}
}
private void checkFlowHappensBefore(List<Record> results, String head, String follow) {
boolean sawFollow = false;
for (Record r : results) {
String id = r.context.getFlowId();
if (head.equals(id)) {
if (sawFollow) {
throw new AssertionError(head + "=>" + follow);
}
} else if (follow.equals(id)) {
sawFollow = false;
}
}
}
private Set<String> id(List<Record> records) {
TreeSet<String> results = new TreeSet<>();
for (Record r : records) {
results.add(id(r));
}
return results;
}
private String id(Record r) {
return r.script == null ? r.handler.getHandlerId() : r.script.getId();
}
private Set<String> set(String... values) {
return new TreeSet<>(Arrays.asList(values));
}
private void verifyPhaseOrder(List<Record> results) {
Map<String, List<Record>> partitions = flowPartition(results);
for (Map.Entry<String, List<Record>> entry : partitions.entrySet()) {
String flowId = entry.getKey();
List<Record> records = entry.getValue();
ExecutionPhase last = ExecutionPhase.SETUP;
for (Record r : records) {
ExecutionPhase phase = r.context.getPhase();
assertThat(flowId, phase, greaterThanOrEqualTo(last));
last = phase;
}
}
}
private Map<String, List<Record>> flowPartition(List<Record> records) {
Map<String, List<Record>> results = new HashMap<>();
for (Record r : records) {
String flowId = r.context.getFlowId();
List<Record> list = results.get(flowId);
if (list == null) {
list = new ArrayList<>();
results.put(flowId, list);
}
list.add(r);
}
return results;
}
private List<Record> flow(List<Record> records, String flowId) {
List<Record> results = new ArrayList<>();
for (Record r : records) {
if (r.context.getFlowId().equals(flowId)) {
results.add(r);
}
}
return results;
}
private List<Record> phase(List<Record> records, String flowId, ExecutionPhase phase) {
List<Record> results = new ArrayList<>();
for (Record r : flow(records, flowId)) {
if (r.context.getPhase() == phase) {
results.add(r);
}
}
return results;
}
static String profile(Record record) {
return ((CommandScript) record.script).getProfileName();
}
/**
* for {@link ExecutionTaskTest#executeFlow_failed_export()}.
*/
public static class FlowSerialized extends SerialExecutionTracker {
private String flowId;
@Override
public synchronized void add(Id id, Record record) throws IOException, InterruptedException {
switch (record.context.getPhase()) {
case SETUP:
assertThat(flowId, is(nullValue()));
flowId = record.context.getFlowId();
break;
case CLEANUP:
assertThat(flowId, is(record.context.getFlowId()));
flowId = null;
break;
default:
Thread.sleep(100);
assertThat(flowId, is(record.context.getFlowId()));
break;
}
super.add(id, record);
}
}
/**
* for {@link ExecutionTaskTest#executeFlow_failed_export()}.
*/
public static class ExporterFailed extends SerialExecutionTracker {
@Override
public synchronized void add(Id id, Record record) throws IOException, InterruptedException {
if (record.context.getPhase() == ExecutionPhase.EXPORT
&& profile(record).equals("testing1")) {
throw new IOException();
}
super.add(id, record);
}
}
/**
* for {@link ExecutionTaskTest#executeFlow_failed_finalize()}.
*/
public static class FinalizerFailed extends SerialExecutionTracker {
@Override
public synchronized void add(Id id, Record record) throws IOException, InterruptedException {
if (record.context.getPhase() == ExecutionPhase.FINALIZE
&& profile(record).equals("testing1")) {
throw new IOException();
}
super.add(id, record);
}
}
/**
* for {@link ExecutionTaskTest#executeFlow_failed_cleanup()}.
*/
public static class CleanerFailed extends SerialExecutionTracker {
@Override
public synchronized void add(Id id, Record record) throws IOException, InterruptedException {
if (record.context.getPhase() == ExecutionPhase.CLEANUP
&& record.handler.getHandlerId().equals("hadoop")) {
throw new IOException();
}
super.add(id, record);
}
}
private static class ProfileBuilder {
static final Pattern PLACEHOLDER = Pattern.compile("<<(.+?)>>");
final File asakusaHome;
final File lockDir;
final ExecutionTracker.Id trackingId;
final Map<String, String> replacement;
final Properties override;
ProfileBuilder(File working) {
this.asakusaHome = new File(working, "asakusa");
this.lockDir = new File(working, "lock");
this.trackingId = ExecutionTracker.Id.get("testing");
this.replacement = new HashMap<>();
this.replacement.put("home", asakusaHome.getAbsolutePath());
this.replacement.put("scope", ExecutionLock.Scope.WORLD.getSymbol());
this.replacement.put("locker", BasicLockProvider.class.getName());
this.replacement.put("monitor", BasicMonitorProvider.class.getName());
this.replacement.put("lock", lockDir.getAbsolutePath());
this.replacement.put("tracker", SerialExecutionTracker.class.getName());
this.replacement.put("id", "testing");
this.override = new Properties();
SerialExecutionTracker.clear();
}
void setTracker(Class<? extends ExecutionTracker> tracker) {
this.replacement.put("tracker", tracker.getName());
}
void setInvalid() {
this.replacement.put("monitor", MonitorProviderInvalid.class.getName());
this.replacement.put("locker", LockProviderInvalid.class.getName());
}
ExecutionTask task() throws IOException, InterruptedException {
Properties properties = loadProfile();
YaessProfile profile = YaessProfile.load(properties, ProfileContext.system(getClass().getClassLoader()));
Map<String, String> arguments = Collections.emptyMap();
Properties script = loadScript();
return ExecutionTask.load(profile, script, arguments);
}
Properties loadScript() throws IOException {
Properties result = load("script-template.properties");
return result;
}
Properties loadProfile() throws IOException {
Properties result = load("profile-template.properties");
result.putAll(override);
for (Map.Entry<Object, Object> entry : result.entrySet()) {
String value = (String) entry.getValue();
StringBuilder buf = new StringBuilder();
int start = 0;
Matcher matcher = PLACEHOLDER.matcher(value);
while (matcher.find(start)) {
buf.append(value.subSequence(start, matcher.start()));
String rep = replacement.get(matcher.group(1));
if (rep == null) {
throw new AssertionError(matcher.group(1));
}
buf.append(rep);
start = matcher.end();
}
buf.append(value.substring(start));
entry.setValue(buf.toString());
}
return result;
}
private Properties load(String name) throws IOException {
Properties result = new Properties();
try (InputStream in = getClass().getResourceAsStream(name)) {
assertThat(in, is(notNullValue()));
result.load(in);
}
return result;
}
}
}