Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
import java.util.Map;
import java.util.PriorityQueue;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
Expand Down Expand Up @@ -145,6 +146,18 @@
tr = ((hudson.tasks.junit.TestResult) tr).getResultForPipelineBlock(stageId.getId());
} else {
listener.getLogger().println("No stage \"" + stageName + "\" found in " + run.getFullDisplayName());
var stages = new TreeSet<String>();
for (var n : new DepthFirstScanner().allNodes(execution)) {
var a = n.getPersistentAction(LabelAction.class);
if (a != null) {
stages.add(a.getDisplayName());
}
}
if (stages.isEmpty()) {

Check warning on line 156 in src/main/java/org/jenkinsci/plugins/parallel_test_executor/Splitter.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 156 is only partially covered, one branch is missing
listener.getLogger().println("(No possible stages found.)");

Check warning on line 157 in src/main/java/org/jenkinsci/plugins/parallel_test_executor/Splitter.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered line

Line 157 is not covered by tests
} else {
listener.getLogger().println("(Observed stages: " + stages.stream().collect(Collectors.joining(", ")) + ")");
}
}
} else {
listener.getLogger().println("No flow execution found in " + run.getFullDisplayName());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,31 @@
import hudson.model.StringParameterDefinition;
import hudson.model.StringParameterValue;
import hudson.tasks.junit.TestResultAction;
import java.io.File;
import java.nio.charset.StandardCharsets;
import org.apache.commons.io.FileUtils;
import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition;
import org.jenkinsci.plugins.workflow.cps.SnippetizerTester;
import org.jenkinsci.plugins.workflow.job.WorkflowJob;
import org.jenkinsci.plugins.workflow.job.WorkflowRun;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.jvnet.hudson.test.Issue;
import org.jvnet.hudson.test.JenkinsRule;
import org.jvnet.hudson.test.junit.jupiter.BuildWatcherExtension;
import org.jvnet.hudson.test.junit.jupiter.WithJenkins;
import org.jvnet.hudson.test.recipes.LocalData;

import java.io.File;
import java.nio.charset.StandardCharsets;

import static jenkins.test.RunMatchers.logContains;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.allOf;
import static org.junit.jupiter.api.Assertions.assertTrue;

@WithJenkins
class ParallelTestExecutorTest {

@RegisterExtension
private static final BuildWatcherExtension buildWatcher = new BuildWatcherExtension();

@Test
@LocalData
void xmlWithNoAddJUnitPublisherIsLoadedCorrectly(JenkinsRule jenkinsRule) {
Expand All @@ -43,15 +49,19 @@ void workflowGenerateInclusions(JenkinsRule jenkinsRule) throws Exception {
new SnippetizerTester(jenkinsRule).assertRoundTrip(step, "splitTests generateInclusions: true, parallelism: time(3)");
WorkflowJob p = jenkinsRule.jenkins.createProject(WorkflowJob.class, "p");
p.setDefinition(new CpsFlowDefinition(
"def splits = splitTests parallelism: count(2), generateInclusions: true\n" +
"echo \"splits.size=${splits.size()}\"; for (int i = 0; i < splits.size(); i++) {\n" +
" def split = splits[i]; echo \"splits[${i}]: includes=${split.includes} list=${split.list}\"\n" +
"}\n" +
"node {\n" +
" writeFile file: 'TEST-1.xml', text: '<testsuite name=\"one\"><testcase name=\"x\"/></testsuite>'\n" +
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cleaning up text blocks while I am here.

" writeFile file: 'TEST-2.xml', text: '<testsuite name=\"two\"><testcase name=\"y\"/></testsuite>'\n" +
" junit 'TEST-*.xml'\n" +
"}", true));
"""
def splits = splitTests parallelism: count(2), generateInclusions: true
echo "splits.size=${splits.size()}"
for (int i = 0; i < splits.size(); i++) {
def split = splits[i]
echo "splits[${i}]: includes=${split.includes} list=${split.list}"
}
node {
writeFile file: 'TEST-1.xml', text: '<testsuite name="one"><testcase name="x"/></testsuite>'
writeFile file: 'TEST-2.xml', text: '<testsuite name="two"><testcase name="y"/></testsuite>'
junit 'TEST-*.xml'
}
""", true));
WorkflowRun b1 = jenkinsRule.assertBuildStatusSuccess(p.scheduleBuild2(0));
jenkinsRule.assertLogContains("splits.size=1", b1);
jenkinsRule.assertLogContains("splits[0]: includes=false list=[]", b1);
Expand All @@ -75,21 +85,25 @@ void workflowDoesNotGenerateInclusionsFromRunningBuild(JenkinsRule jenkinsRule)
* b) Once the second build finish the previous one is killed by using milestones
*/
p.setDefinition(new CpsFlowDefinition(
"lock('test-results') {\n" +
" def splits = splitTests parallelism: count(2), generateInclusions: true\n" +
" echo \"splits.size=${splits.size()}\"; for (int i = 0; i < splits.size(); i++) {\n" +
" def split = splits[i]; echo \"splits[${i}]: includes=${split.includes} list=${split.list}\"\n" +
" }\n" +
" node {\n" +
" writeFile file: 'TEST-1.xml', text: '<testsuite name=\"one\"><testcase name=\"x\" failures=\"1\"/></testsuite>'\n" +
" writeFile file: 'TEST-2.xml', text: '<testsuite name=\"two\"><testcase name=\"y\"/></testsuite>'\n" +
" junit 'TEST-*.xml'\n" +
" currentBuild.result = 'UNSTABLE'\n" + // Needed due to https://issues.jenkins-ci.org/browse/JENKINS-48178
" }\n" +
"}\n" +
"milestone 1\n" +
"sleep time: Integer.valueOf(params.SLEEP), unit: 'SECONDS'\n" +
"milestone 2", true));
"""
lock('test-results') {
def splits = splitTests parallelism: count(2), generateInclusions: true
echo "splits.size=${splits.size()}"
for (int i = 0; i < splits.size(); i++) {
def split = splits[i]
echo "splits[${i}]: includes=${split.includes} list=${split.list}"
}
node {
writeFile file: 'TEST-1.xml', text: '<testsuite name="one"><testcase name="x" failures="1"/></testsuite>'
writeFile file: 'TEST-2.xml', text: '<testsuite name="two"><testcase name="y"/></testsuite>'
junit 'TEST-*.xml'
currentBuild.result = 'UNSTABLE' // Needed due to https://github.com/jenkinsci/junit-plugin/issues/1094
}
}
milestone 1
sleep time: Integer.valueOf(params.SLEEP), unit: 'SECONDS'
milestone 2
""", true));
WorkflowRun b1 = p.scheduleBuild2(0, new ParametersAction(new StringParameterValue("SLEEP", "100"))).waitForStart();
jenkinsRule.waitForMessage("Lock acquired on", b1);
WorkflowRun b2 = p.scheduleBuild2(0, new ParametersAction(new StringParameterValue("SLEEP", "0"))).get();
Expand All @@ -109,10 +123,12 @@ void unloadableTestResult(JenkinsRule jenkinsRule) throws Exception {
{
WorkflowJob p = jenkinsRule.jenkins.createProject(WorkflowJob.class, "p");
p.setDefinition(new CpsFlowDefinition(
" node {\n" +
" writeFile file: 'TEST-one.xml', text: $/<testsuite name='one'><testcase name='x'/></testsuite>/$\n" +
" junit 'TEST-*.xml'\n" +
" }\n", true));
"""
node {
writeFile file: 'TEST-one.xml', text: "<testsuite name='one'><testcase name='x'/></testsuite>"
junit 'TEST-*.xml'
}
""", true));
jenkinsRule.buildAndAssertSuccess(p);
WorkflowRun b2 = jenkinsRule.buildAndAssertSuccess(p);
FileUtils.write(new File(b2.getRootDir(), "junitResult.xml"), "<broken", StandardCharsets.UTF_8);
Expand All @@ -131,28 +147,32 @@ void unloadableTestResult(JenkinsRule jenkinsRule) throws Exception {
void splitTestsWithinStage(JenkinsRule jenkinsRule) throws Exception {
WorkflowJob p = jenkinsRule.jenkins.createProject(WorkflowJob.class, "p");
p.setDefinition(new CpsFlowDefinition(
"def splits = splitTests parallelism: count(2), generateInclusions: true, stage: 'first'\n" +
"echo \"splits.size=${splits.size()}\"; for (int i = 0; i < splits.size(); i++) {\n" +
" def split = splits[i]; echo \"splits[${i}]: includes=${split.includes} list=${split.list}\"\n" +
"}\n" +
"def allSplits = splitTests parallelism: count(2), generateInclusions: true\n" +
"echo \"allSplits.size=${allSplits.size()}\"; for (int i = 0; i < allSplits.size(); i++) {\n" +
" def split = allSplits[i]; echo \"allSplits[${i}]: includes=${split.includes} list=${split.list}\"\n" +
"}\n" +
"stage('first') {\n" +
" node {\n" +
" writeFile file: 'TEST-1.xml', text: '<testsuite name=\"one\"><testcase name=\"x\"/></testsuite>'\n" +
" writeFile file: 'TEST-2.xml', text: '<testsuite name=\"two\"><testcase name=\"y\"/></testsuite>'\n" +
" junit 'TEST-*.xml'\n" +
" }\n" +
"}\n" +
"stage('second') {\n" +
" node {\n" +
" writeFile file: 'TEST-3.xml', text: '<testsuite name=\"three\"><testcase name=\"a\"/></testsuite>'\n" +
" writeFile file: 'TEST-4.xml', text: '<testsuite name=\"four\"><testcase name=\"b\"/></testsuite>'\n" +
" junit 'TEST-*.xml'\n" +
" }\n" +
"}\n", true));
"""
def splits = splitTests parallelism: count(2), generateInclusions: true, stage: 'first'
echo "splits.size=${splits.size()}"
for (int i = 0; i < splits.size(); i++) {
def split = splits[i]; echo "splits[${i}]: includes=${split.includes} list=${split.list}"
}
def allSplits = splitTests parallelism: count(2), generateInclusions: true
echo "allSplits.size=${allSplits.size()}"
for (int i = 0; i < allSplits.size(); i++) {
def split = allSplits[i]; echo "allSplits[${i}]: includes=${split.includes} list=${split.list}"
}
stage('first') {
node {
writeFile file: 'TEST-1.xml', text: '<testsuite name="one"><testcase name="x"/></testsuite>'
writeFile file: 'TEST-2.xml', text: '<testsuite name="two"><testcase name="y"/></testsuite>'
junit 'TEST-*.xml'
}
}
stage('second') {
node {
writeFile file: 'TEST-3.xml', text: '<testsuite name="three"><testcase name="a"/></testsuite>'
writeFile file: 'TEST-4.xml', text: '<testsuite name="four"><testcase name="b"/></testsuite>'
junit 'TEST-*.xml'
}
}
""", true));
WorkflowRun b1 = jenkinsRule.assertBuildStatusSuccess(p.scheduleBuild2(0));
jenkinsRule.assertLogContains("splits.size=1", b1);
jenkinsRule.assertLogContains("splits[0]: includes=false list=[]", b1);
Expand All @@ -172,31 +192,36 @@ void splitTestsWithinStage(JenkinsRule jenkinsRule) throws Exception {
void splitTestsWithinParallelStage(JenkinsRule jenkinsRule) throws Exception {
WorkflowJob p = jenkinsRule.jenkins.createProject(WorkflowJob.class, "p");
p.setDefinition(new CpsFlowDefinition(
"def splits = splitTests parallelism: count(2), generateInclusions: true, stage: 'Branch: first'\n" +
"echo \"splits.size=${splits.size()}\"; for (int i = 0; i < splits.size(); i++) {\n" +
" def split = splits[i]; echo \"splits[${i}]: includes=${split.includes} list=${split.list}\"\n" +
"}\n" +
"def allSplits = splitTests parallelism: count(2), generateInclusions: true\n" +
"echo \"allSplits.size=${allSplits.size()}\"; for (int i = 0; i < allSplits.size(); i++) {\n" +
" def split = allSplits[i]; echo \"allSplits[${i}]: includes=${split.includes} list=${split.list}\"\n" +
"}\n" +
"def branches = [:]\n" +
"branches['first'] = {\n" +
" node {\n" +
" writeFile file: 'TEST-1.xml', text: '<testsuite name=\"one\"><testcase name=\"x\"/></testsuite>'\n" +
" writeFile file: 'TEST-2.xml', text: '<testsuite name=\"two\"><testcase name=\"y\"/></testsuite>'\n" +
" junit 'TEST-*.xml'\n" +
" }\n" +
"}\n" +
"branches['second'] = {\n" +
" node {\n" +
" writeFile file: 'TEST-3.xml', text: '<testsuite name=\"three\"><testcase name=\"a\"/></testsuite>'\n" +
" writeFile file: 'TEST-4.xml', text: '<testsuite name=\"four\"><testcase name=\"b\"/></testsuite>'\n" +
" junit 'TEST-*.xml'\n" +
" }\n" +
"}\n" +
"parallel branches\n"
, true));
"""
def splits = splitTests parallelism: count(2), generateInclusions: true, stage: 'Branch: first'
echo "splits.size=${splits.size()}"
for (int i = 0; i < splits.size(); i++) {
def split = splits[i]
echo "splits[${i}]: includes=${split.includes} list=${split.list}"
}
def allSplits = splitTests parallelism: count(2), generateInclusions: true
echo "allSplits.size=${allSplits.size()}"
for (int i = 0; i < allSplits.size(); i++) {
def split = allSplits[i]
echo "allSplits[${i}]: includes=${split.includes} list=${split.list}"
}
def branches = [:]
branches['first'] = {
node {
writeFile file: 'TEST-1.xml', text: '<testsuite name="one"><testcase name="x"/></testsuite>'
writeFile file: 'TEST-2.xml', text: '<testsuite name="two"><testcase name="y"/></testsuite>'
junit 'TEST-*.xml'
}
}
branches['second'] = {
node {
writeFile file: 'TEST-3.xml', text: '<testsuite name="three"><testcase name="a"/></testsuite>'
writeFile file: 'TEST-4.xml', text: '<testsuite name="four"><testcase name="b"/></testsuite>'
junit 'TEST-*.xml'
}
}
parallel branches
""", true));
WorkflowRun b1 = jenkinsRule.assertBuildStatusSuccess(p.scheduleBuild2(0));
jenkinsRule.assertLogContains("splits.size=1", b1);
jenkinsRule.assertLogContains("splits[0]: includes=false list=[]", b1);
Expand All @@ -217,12 +242,40 @@ void splitTestsWithinParallelStage(JenkinsRule jenkinsRule) throws Exception {
void estimateTestsFromFiles(JenkinsRule jenkinsRule) throws Exception {
jenkinsRule.createSlave("remote", null, null);
WorkflowJob p = jenkinsRule.jenkins.createProject(WorkflowJob.class, "p");
p.setDefinition(new CpsFlowDefinition("" +
"node('remote') {\n" +
" writeFile file: 'src/test/java/TopTest.java', text: ''\n" +
" writeFile file: 'subdir/src/test/java/some/pkg/OtherTest.java', text: ''\n" +
" echo(/splits: ${splitTests(parallelism: count(2), estimateTestsFromFiles: true).flatten().sort()}/)\n" +
"}", true));
p.setDefinition(new CpsFlowDefinition(
"""
node('remote') {
writeFile file: 'src/test/java/TopTest.java', text: ''
writeFile file: 'subdir/src/test/java/some/pkg/OtherTest.java', text: ''
echo "splits: ${splitTests(parallelism: count(2), estimateTestsFromFiles: true).flatten().sort()}"
}
""", true));
jenkinsRule.assertLogContains("splits: [TopTest.class, TopTest.java, some/pkg/OtherTest.class, some/pkg/OtherTest.java]", jenkinsRule.buildAndAssertSuccess(p));
}

@Test
void unknownStage(JenkinsRule r) throws Exception {
var p = r.createProject(WorkflowJob.class, "p");
p.setDefinition(new CpsFlowDefinition(
"""
stage('first') {
node {
writeFile file: 'TEST-1.xml', text: '<testsuite name="one"><testcase name="x"/></testsuite>'
writeFile file: 'TEST-2.xml', text: '<testsuite name="two"><testcase name="y"/></testsuite>'
junit 'TEST-*.xml'
}
}
stage('second') {
node {
writeFile file: 'TEST-3.xml', text: '<testsuite name="three"><testcase name="a"/></testsuite>'
writeFile file: 'TEST-4.xml', text: '<testsuite name="four"><testcase name="b"/></testsuite>'
junit 'TEST-*.xml'
}
}
splitTests parallelism: count(2), stage: 'nonexistent'
""", true));
var b1 = r.buildAndAssertSuccess(p);
var b2 = r.buildAndAssertSuccess(p);
assertThat(b2, allOf(logContains("No stage \"nonexistent\" found in p #1"), logContains("(Observed stages: first, second)")));
}
}
Loading