Skip to content
This repository has been archived by the owner on Apr 13, 2023. It is now read-only.

Commit

Permalink
Merge pull request #58 from jvfullam/issue#33
Browse files Browse the repository at this point in the history
Added decider tests for Issue #33; Resolves issue #33
  • Loading branch information
scottkurz committed Aug 16, 2016
2 parents 72d8010 + 1e8b802 commit 8a44e6c
Show file tree
Hide file tree
Showing 6 changed files with 345 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Copyright 2016 International Business Machines Corp.
*
* See the NOTICE file distributed with this work for additional information
* regarding copyright ownership. 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.ibm.jbatch.tck.artifacts.reusable;

import javax.batch.api.AbstractBatchlet;
import javax.batch.runtime.context.JobContext;
import javax.batch.runtime.context.StepContext;
import javax.inject.Inject;

@javax.inject.Named("doNothingBatchlet")
public class DoNothingBatchlet extends AbstractBatchlet {

@Override
public String process() {
return "The DoNothingBatchlet COMPLETED Without Incident";
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* Copyright 2016 International Business Machines Corp.
*
* See the NOTICE file distributed with this work for additional information
* regarding copyright ownership. 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.ibm.jbatch.tck.artifacts.specialized;

import java.util.Arrays;
import java.util.Comparator;

import javax.batch.api.BatchProperty;
import javax.batch.api.Decider;
import javax.batch.runtime.StepExecution;
import javax.batch.runtime.context.JobContext;
import javax.inject.Inject;

@javax.inject.Named("deciderReceivesCorrectStepExecutionsDecider")
public class DeciderReceivesCorrectStepExecutionsDecider implements Decider {

@Inject
JobContext jobCtx;

@Inject @BatchProperty(name="expected.number.of.step.executions")
String numOfStepExecs;

@Override
public String decide(StepExecution[] stepExecutions) throws Exception {
if (stepExecutions.length != Integer.parseInt(numOfStepExecs)) {
throw new Exception("Expecting stepExecutions array of size " +numOfStepExecs+ ", found one of size = " + stepExecutions.length);
}

/*we sort the stepExecutions to guarantee that the decider exit status, made up of the concatenated
step executions, will have the same order of step executions on subsequent job executions*/
sortStepExecutionsByStepName(stepExecutions);

/*
* exitStatus will be in the format:
* "split1flow1step1:1235;split1flow1step2:1236;split1flow2step1:1237;"
* split the exitStatus at each ";" for each stepExecution
* split a stepExecution at ":" to get the stepName and stepExecutionId
*/
String exitStatus = "";
for (StepExecution stepExecution: stepExecutions) {
exitStatus += stepExecution.getStepName() + ":" + stepExecution.getStepExecutionId() + ";";
}
return exitStatus; //Decider return value should be set as the Job Exit Status
}

private void sortStepExecutionsByStepName(StepExecution[] stepExecutions) {
Arrays.sort(stepExecutions, new Comparator<StepExecution>() {
@Override
public int compare(StepExecution se1, StepExecution se2) {
return se1.getStepName().compareTo(se2.getStepName());
}
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

import com.ibm.jbatch.tck.ann.*;
import com.ibm.jbatch.tck.artifacts.common.StatusConstants;
import com.ibm.jbatch.tck.artifacts.specialized.DeciderTestsBatchlet;
import com.ibm.jbatch.tck.utils.JobOperatorBridge;
Expand Down Expand Up @@ -923,9 +924,193 @@ public void testDeciderTransitionFromSplitAndAllowRestartFalse() throws Exceptio
handleException(METHOD, e);
}
}

@TCKTest(
versions = {"1.1.WORKING"},
assertions = {"A Decider that follows a single step is passed the most recent StepExecution of the step."},
specRefs = {
@SpecRef(
version = "1.0", section = "9.6",
citations = "The decide method sets a new exit status for a job. It receives an array of StepExecution objects as input. These "
+ "StepExecution objects represent the execution element that transitions to this decider as follows: [...] Step [...] "
+ "When the transition is from a step, the decide method receives the StepExecution corresponding to the step as input.",
notes = "API for Decider"
),
@SpecRef(
version = "1.0RevA", section = "10.8.4",
citations = "The Decider's \"decide\" method is passed a StepExecution array as a parameter. This array "
+ "will be populated with the most-recently completed StepExecution(s) for each corresponding step.",
notes = "See 3.d."
)
},
apiRefs = { @APIRef(className="javax.batch.api.Decider", methodNames={"decide"}) },
issueRefs = {"https://github.com/WASdev/standards.jsr352.tck/issues/33", "https://java.net/bugzilla/show_bug.cgi?id=5780"},
strategy = "JobExecution1: step1 completes, Decider receives this StepExecution, and the job is forced to stop. "
+ "JobExecution2: step1 does not re-execute (allow-start-if-complete=false), verify that the Decider receives "
+ "the same StepExecution as in JobExecution1, and force the job to stop. "
+ "JobExecution3: step1 re-executes (allow-start-if-complete=true), verify that the Decider receives a new StepExecution "
+ "for step1, and check that the job completes. "
)
@Test
@org.junit.Test
public void testDeciderReceivesCorrectStepExecutionAfterStep() throws Exception {
String METHOD = "testDeciderReceivesCorrectStepExecutionAfterStep";

try {
/* Note: The Job Exit Status on each execution is the Decider return value,
* which is set up to indicate which steps and step executions it received */

final String NEW_STEP_EXECUTION_ID = "\\d+;?"; //to be used in regex matching

//Job Execution 1
Reporter.log("Build job parameters for job execution 1.<p>");
Properties executionParameters = new Properties();
executionParameters.setProperty("allow.start.if.complete", "false");
executionParameters.setProperty("decider.stop.on", "*");

Reporter.log("Invoke startJobAndWaitForResult<p>");
JobExecution jobExec1 = jobOp.startJobAndWaitForResult("decider_receives_correct_step_execution_after_step", executionParameters);
String jobExec1Step1Exec = jobExec1.getExitStatus();
assertWithMessage("Expected Exit Status to be set by Decider", jobExec1Step1Exec.matches("step1:" + NEW_STEP_EXECUTION_ID));
assertWithMessage("Expected job to be STOPPED after first execution", BatchStatus.STOPPED, jobExec1.getBatchStatus());

//Job Execution 2
Reporter.log("Don't change job parameters for job execution 2.<p>");

JobExecution jobExec2 = jobOp.restartJobAndWaitForResult(jobExec1.getExecutionId(), executionParameters);
String jobExec2Step1Exec = jobExec2.getExitStatus();
assertWithMessage("Execution for step1 should be the same in jobExec2 as in jobExec1", jobExec1Step1Exec, jobExec2Step1Exec);
assertWithMessage("Expected job to be STOPPED after second execution", BatchStatus.STOPPED, jobExec2.getBatchStatus());

//Job Execution 3
Reporter.log("Build job parameters for job execution 3.<p>");
executionParameters.setProperty("allow.start.if.complete", "true");
executionParameters.setProperty("decider.stop.on", "NONE");

JobExecution jobExec3 = jobOp.restartJobAndWaitForResult(jobExec2.getExecutionId(), executionParameters);
String jobExec3Step1Exec = jobExec3.getExitStatus();
assertWithMessage("Execution for step1 should be different in jobExec3 than in jobExec2", !jobExec2Step1Exec.equals(jobExec3Step1Exec));
assertWithMessage("Expected Exit Status to be set by Decider", jobExec3Step1Exec.matches("step1:" + NEW_STEP_EXECUTION_ID));
assertWithMessage("Expected job to be COMPLETED after third execution", BatchStatus.COMPLETED, jobExec3.getBatchStatus());

} catch(Exception e) {
handleException(METHOD, e);
}
}

@TCKTest(
versions = {"1.1.WORKING"},
assertions = {"A Decider that follows a split-flow is passed the most recent StepExecution for each flow's last step."},
specRefs = {
@SpecRef(
version = "1.0", section = "9.6",
citations = "The decide method sets a new exit status for a job. It receives an array of StepExecution objects as input. These "
+ "StepExecution objects represent the execution element that transitions to this decider as follows: [...] Split [...] "
+ "When the transition is from a split, the decide method receives a StepExecution from each flow defined to the split as input.",
notes = "API for Decider"
),
@SpecRef(
version = "1.0RevA", section = "10.8.4",
citations = { "The Decider's \"decide\" method is passed a StepExecution array as a parameter. This array "
+ "will be populated with the most-recently completed StepExecution(s) for each corresponding step.",
"A single decision following a split could even have a mix of old, new StepExecution(s) in the same array." },
notes = "See 3.d."
)
},
apiRefs = { @APIRef(className="javax.batch.api.Decider", methodNames={"decide"}) },
issueRefs = {"https://github.com/WASdev/standards.jsr352.tck/issues/33", "https://java.net/bugzilla/show_bug.cgi?id=5780"},
strategy = "JobExecution1: split1flow1step1 and split1flow2step1 complete, Decider receives a StepExecution for both steps, and force the job to stop. "
+ "JobExecution2: neither step re-executes (allow-start-if-complete=false for both steps), verify that the Decider receives the same StepExecutions "
+ "as in JobExecution1, and force the job to stop. "
+ "JobExecution3: both steps re-execute (allow-start-if-complete=true for both steps), verify that the Decider receives new StepExecutions for both "
+ "steps, and force the job to stop. "
+ "JobExecution4: only 1 step re-executes (allow-start-if-complete=true only for split1flow2step1), verify that the Decider receives the correct "
+ "mix of old and new StepExecutions, and check that the job completes."
)
@Test/*(enabled=false)*/ //This test exposes a bug in the RI, disable as needed
@org.junit.Test
public void testDeciderReceivesCorrectStepExecutionsAfterSplit() throws Exception {
String METHOD = "testDeciderReceivesCorrectStepExecutionsAfterSplit";

try {
/* Note: The Job Exit Status on each execution is the Decider return value,
* which is set up to indicate which steps and step executions it received */

final String NEW_STEP_EXECUTION_ID = "\\d+;?"; //to be used in regex matching

//Job Execution 1
Reporter.log("Build job parameters for job execution 1.<p>");
Properties executionParameters = new Properties();
executionParameters.setProperty("split1flow1step1.allow.start.if.complete", "false");
executionParameters.setProperty("split1flow2step1.allow.start.if.complete", "false");
executionParameters.setProperty("decider.stop.on", "*");


Reporter.log("Invoke startJobAndWaitForResult<p>");
JobExecution jobExec1 = jobOp.startJobAndWaitForResult("decider_receives_correct_step_executions_after_split", executionParameters);

String jobExec1ExitStatus = jobExec1.getExitStatus();
String[] jobExec1StepExecs = jobExec1ExitStatus.split(";");
String jobExec1Split1Flow1Step1 = jobExec1StepExecs[0];
String jobExec1Split1Flow2Step1 = jobExec1StepExecs[1];

assertWithMessage("Expected Exit Status to be set by Decider", jobExec1ExitStatus.matches("split1flow1step1:" + NEW_STEP_EXECUTION_ID + "split1flow2step1:" + NEW_STEP_EXECUTION_ID));
assertWithMessage("Expected job to be STOPPED after first execution", BatchStatus.STOPPED, jobExec1.getBatchStatus());

//Job Execution 2
Reporter.log("Don't change any job parameters for job execution 2.<p>");

Reporter.log("Invoke restartJobAndWaitForResult<p>");
JobExecution jobExec2 = jobOp.restartJobAndWaitForResult(jobExec1.getExecutionId(), executionParameters);

String jobExec2ExitStatus = jobExec2.getExitStatus();
String[] jobExec2StepExecs = jobExec2ExitStatus.split(";");
String jobExec2Split1Flow1Step1 = jobExec2StepExecs[0];
String jobExec2Split1Flow2Step1 = jobExec2StepExecs[1];

assertWithMessage("Execution for split1flow1step1 in jobExec2 should be the same as in jobExec1", jobExec1Split1Flow1Step1, jobExec2Split1Flow1Step1);
assertWithMessage("Execution for split1flow2step1 in jobExec2 should be the same as in jobExec1", jobExec1Split1Flow2Step1, jobExec2Split1Flow2Step1);
assertWithMessage("Expected job to be STOPPED after second execution", BatchStatus.STOPPED, jobExec2.getBatchStatus());

//Job Execution 3
Reporter.log("Build job parameters for job execution 3.<p>");
executionParameters.setProperty("split1flow1step1.allow.start.if.complete", "true");
executionParameters.setProperty("split1flow2step1.allow.start.if.complete", "true");

Reporter.log("Invoke restartJobAndWaitForResult<p>");
JobExecution jobExec3 = jobOp.restartJobAndWaitForResult(jobExec2.getExecutionId(), executionParameters);

String jobExec3ExitStatus = jobExec3.getExitStatus();
String[] jobExec3StepExecs = jobExec3ExitStatus.split(";");
String jobExec3Split1Flow1Step1 = jobExec3StepExecs[0];
String jobExec3Split1Flow2Step1 = jobExec3StepExecs[1];

assertWithMessage("Execution for split1flow1step1 in jobExec3 should be different than in jobExec2", !jobExec2Split1Flow1Step1.equals(jobExec3Split1Flow1Step1));
assertWithMessage("Execution for split1flow2step1 in jobExec3 should be different than in jobExec2", !jobExec2Split1Flow2Step1.equals(jobExec3Split1Flow2Step1));
assertWithMessage("Expected Exit Status to be set by Decider", jobExec3ExitStatus.matches("split1flow1step1:" + NEW_STEP_EXECUTION_ID + "split1flow2step1:" + NEW_STEP_EXECUTION_ID));
assertWithMessage("Expected job to be STOPPED after third execution", BatchStatus.STOPPED, jobExec3.getBatchStatus());

//Job Execution 4
Reporter.log("Build job parameters for job execution 4.<p>");
executionParameters.setProperty("split1flow1step1.allow.start.if.complete", "false");
executionParameters.setProperty("decider.stop.on", "NONE");

Reporter.log("Invoke restartJobAndWaitForResult<p>");
JobExecution jobExec4 = jobOp.restartJobAndWaitForResult(jobExec3.getExecutionId(), executionParameters);

String jobExec4ExitStatus = jobExec4.getExitStatus();
String[] jobExec4StepExecs = jobExec4ExitStatus.split(";");
String jobExec4Split1Flow1Step1 = jobExec4StepExecs[0];
String jobExec4Split1Flow2Step1 = jobExec4StepExecs[1];

assertWithMessage("Execution for split1flow1step1 in jobExec4 should be the same as in jobExec3", jobExec3Split1Flow1Step1, jobExec4Split1Flow1Step1);
assertWithMessage("Execution for split1flow2step1 in jobExec4 should be different than in jobExec3", !jobExec3Split1Flow2Step1.equals(jobExec4Split1Flow2Step1));
assertWithMessage("Expected job to be COMPLETED after fourth execution", BatchStatus.COMPLETED, jobExec4.getBatchStatus());

} catch(Exception e) {
handleException(METHOD, e);
}
}

private static void handleException(String methodName, Exception e) throws Exception {
Reporter.log("Caught exception: " + e.getMessage()+"<p>");
Reporter.log(methodName + " failed<p>");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Copyright 2016 International Business Machines Corp. See the NOTICE file distributed with this work
for additional information regarding copyright ownership. 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. -->
<job id="decider_receives_correct_step_execution_after_step" xmlns="http://xmlns.jcp.org/xml/ns/javaee" version="1.0">
<step id="step1" next="decider1" allow-start-if-complete="#{jobParameters['allow.start.if.complete']}">
<batchlet ref="doNothingBatchlet"/>
</step>
<decision id="decider1" ref="deciderReceivesCorrectStepExecutionsDecider">
<properties>
<property name="expected.number.of.step.executions" value="1"/>
</properties>
<stop on="#{jobParameters['decider.stop.on']}"/>
<next on="*" to="step2"/>
</decision>
<step id="step2">
<batchlet ref="doNothingBatchlet"/>
<end on="*"/>
</step>
</job>
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Copyright 2016 International Business Machines Corp. See the NOTICE file distributed with this work
for additional information regarding copyright ownership. 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. -->
<job id="decider_receives_correct_step_executions_after_split" xmlns="http://xmlns.jcp.org/xml/ns/javaee" version="1.0">
<split id="split1" next="decider1">
<flow id="split1flow1">
<step id="split1flow1step1" allow-start-if-complete="#{jobParameters['split1flow1step1.allow.start.if.complete']}">
<batchlet ref="doNothingBatchlet"/>
</step>
</flow>
<flow id="split1flow2">
<step id="split1flow2step1" allow-start-if-complete="#{jobParameters['split1flow2step1.allow.start.if.complete']}">
<batchlet ref="doNothingBatchlet"/>
</step>
</flow>
</split>
<decision id="decider1" ref="deciderReceivesCorrectStepExecutionsDecider">
<properties>
<property name="expected.number.of.step.executions" value="2"/>
</properties>
<stop on="#{jobParameters['decider.stop.on']}"/>
<next on="*" to="step2"/>
</decision>
<step id="step2">
<batchlet ref="doNothingBatchlet"/>
<end on="*"/>
</step>
</job>
Loading

0 comments on commit 8a44e6c

Please sign in to comment.