Skip to content

Commit

Permalink
[JENKINS-42584] Fix upstream job priority retrieval
Browse files Browse the repository at this point in the history
- Tests added
- incorporated PR change requests
  • Loading branch information
Ronny Schuetz committed Apr 10, 2017
1 parent be0592c commit df09df3
Show file tree
Hide file tree
Showing 6 changed files with 229 additions and 10 deletions.
@@ -1,7 +1,7 @@
/*
* The MIT License
*
* Copyright (c) 2013, Ronny Schuetz
* Copyright (c) 2017, Ronny Schuetz
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
Expand All @@ -27,6 +27,8 @@
import java.util.LinkedList;
import java.util.concurrent.TimeUnit;

import javax.annotation.CheckForNull;

import com.google.common.base.Objects;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
Expand All @@ -40,7 +42,7 @@
* to become jobs, for UpstreamCauseStrategy.
*
* @author Ronny Schuetz
* @since 2.3
* @since 3.6.0
*/
public class StartedJobItemCache {

Expand Down Expand Up @@ -84,7 +86,7 @@ public int hashCode() {
}

@Override
public boolean equals(Object obj) {
public boolean equals(final Object obj) {
if (obj == null)
return false;
if (getClass() != obj.getClass())
Expand All @@ -94,7 +96,7 @@ public boolean equals(Object obj) {
}
}

private LinkedList<PendingItem> pendingItems = new LinkedList<PendingItem>();
private final LinkedList<PendingItem> pendingItems = new LinkedList<PendingItem>();

private final Cache<StartedItem, ItemInfo> startedItems = CacheBuilder.newBuilder()
.expireAfterWrite(RETENTION_TIME_HOURS, TimeUnit.HOURS).maximumSize(RETENTION_COUNT).build();
Expand All @@ -112,12 +114,12 @@ private StartedJobItemCache() {
* @return the {@link ItemInfo} for the provided id or <code>null</code> if
* projectName/buildNumber combination is unknown
*/
public synchronized ItemInfo getStartedItem(String projectName, int buildNumber) {
public synchronized @CheckForNull ItemInfo getStartedItem(final String projectName, final int buildNumber) {
maintainCache();
return startedItems.getIfPresent(new StartedItem(projectName, buildNumber));
}

public synchronized void addItem(ItemInfo itemInfo, WorkUnit primaryWorkUnit) {
public synchronized void addItem(final ItemInfo itemInfo, final WorkUnit primaryWorkUnit) {
pendingItems.addLast(new PendingItem(itemInfo, primaryWorkUnit));
maintainCache();
}
Expand Down
73 changes: 73 additions & 0 deletions src/test/java/jenkins/advancedqueue/test/UpstreamTest.java
@@ -0,0 +1,73 @@
package jenkins.advancedqueue.test;

import java.lang.reflect.Constructor;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import org.junit.Rule;
import org.junit.Test;
import org.jvnet.hudson.test.JenkinsRule;
import org.jvnet.hudson.test.recipes.LocalData;

import hudson.model.Cause;
import hudson.model.Cause.UpstreamCause;
import hudson.model.Cause.UserIdCause;
import jenkins.advancedqueue.testutil.ExpectedItem;
import jenkins.advancedqueue.testutil.JobHelper;
import jenkins.advancedqueue.testutil.TestRunListener;

public class UpstreamTest {
@Rule
public JenkinsRule j = new JenkinsRule();

private final JobHelper jobHelper = new JobHelper(j);

@Test
@LocalData
public void testOrphanDownstreamJob() throws Exception {
// Job 0 should run with default priority, as upstream build is unknown
TestRunListener.init(new ExpectedItem("Job 0", 5));
jobHelper.scheduleProjects(createUpstreamCause("Job X", 987)).go();
j.waitUntilNoActivity();

TestRunListener.assertStartedItems();
}

@Test
@LocalData
public void testUserJobAndAssociatedDownstreamJob() throws Exception {
// Upstream job should run with high priority (user triggered)
TestRunListener.init(new ExpectedItem("Upstream", 1));
jobHelper.scheduleProject("Upstream", new UserIdCause()).go();
j.waitUntilNoActivity();

// Downstream job 1 should run with priority of upstream job build 1
TestRunListener.init(new ExpectedItem("Downstream1", 1));
jobHelper.scheduleProject("Downstream1", createUpstreamCause("Upstream", 1)).go();
j.waitUntilNoActivity();

// Downstream job 2 should run with priority of upstream job build 2 (not present, i.e. default priority
// should be used)
TestRunListener.init(new ExpectedItem("Downstream2", 5));
jobHelper.scheduleProject("Downstream2", createUpstreamCause("Upstream", 2)).go();
j.waitUntilNoActivity();

TestRunListener.assertStartedItems();
}

private UpstreamCause createUpstreamCause(final String upstreamProject, final int upstreamBuild) throws Exception {
final Class<?> clazz = UpstreamCause.class;
final Constructor<?>[] constructors = clazz.getDeclaredConstructors();

for (final Constructor<?> cons : constructors) {
if (Arrays.equals(cons.getParameterTypes(),
new Class<?>[] { String.class, int.class, String.class, List.class })) {
cons.setAccessible(true);
return (UpstreamCause) cons.newInstance(upstreamProject, upstreamBuild, "url",
Collections.<Cause>emptyList());
}
}
return null;
}
}
22 changes: 18 additions & 4 deletions src/test/java/jenkins/advancedqueue/testutil/JobHelper.java
Expand Up @@ -21,7 +21,7 @@
import org.jvnet.hudson.test.JenkinsRule;

public class JobHelper {

private final static Logger LOGGER = Logger.getLogger(JobHelper.class.getName());

public JenkinsRule j;
Expand All @@ -47,6 +47,12 @@ public boolean perform(AbstractBuild<?, ?> build, Launcher launcher, BuildListen
}
}

public FreeStyleProject createProject(String name) throws Exception {
FreeStyleProject project = j.createFreeStyleProject(name);
project.getBuildersList().add(new TestBuilder(100));
return project;
}

public List<FreeStyleProject> createProjects(int numberOfProjects) throws Exception {
List<FreeStyleProject> projects = new ArrayList<FreeStyleProject>(numberOfProjects);
for (int i = 0; i < numberOfProjects; i++) {
Expand All @@ -69,7 +75,7 @@ public List<MatrixProject> createMatrixProjects(int numberOfProjects) throws Exc
}
return projects;
}

public JobHelper scheduleMatrixProjects(Cause... causes) throws Exception {
List<MatrixProject> projects = createMatrixProjects(causes.length);
// Scheduling executors is zero
Expand All @@ -80,6 +86,14 @@ public JobHelper scheduleMatrixProjects(Cause... causes) throws Exception {
return this;
}

public JobHelper scheduleProject(String name, Cause cause) throws Exception {
FreeStyleProject project = createProject(name);
// Scheduling executors is zero
project.scheduleBuild(0, cause);
Thread.sleep(100);
return this;
}

public JobHelper scheduleProjects(Cause... causes) throws Exception {
List<FreeStyleProject> projects = createProjects(causes.length);
// Scheduling executors is zero
Expand All @@ -94,7 +108,7 @@ public void go() throws Exception {
// Set the executors to one and restart
Jenkins.getInstance().setNumExecutors(1);
// TODO: is there any other way to make the 1 take effect than a reload?
Jenkins.getInstance().reload();
Jenkins.getInstance().reload();
}

}
@@ -0,0 +1,85 @@
<?xml version='1.0' encoding='UTF-8'?>
<hudson>
<disabledAdministrativeMonitors>
<string>OldData</string>
</disabledAdministrativeMonitors>
<version>1.520</version>
<numExecutors>0</numExecutors>
<mode>NORMAL</mode>
<useSecurity>true</useSecurity>
<authorizationStrategy class="hudson.security.AuthorizationStrategy$Unsecured"/>
<securityRealm class="hudson.security.SecurityRealm$None"/>
<projectNamingStrategy class="jenkins.model.ProjectNamingStrategy$DefaultProjectNamingStrategy"/>
<workspaceDir>${ITEM_ROOTDIR}/workspace</workspaceDir>
<buildsDir>${ITEM_ROOTDIR}/builds</buildsDir>
<markupFormatter class="hudson.markup.RawHtmlMarkupFormatter">
<disableSyntaxHighlighting>false</disableSyntaxHighlighting>
</markupFormatter>
<jdks/>
<viewsTabBar class="hudson.views.DefaultViewsTabBar"/>
<myViewsTabBar class="hudson.views.DefaultMyViewsTabBar"/>
<clouds/>
<slaves/>
<quietPeriod>5</quietPeriod>
<scmCheckoutRetryCount>0</scmCheckoutRetryCount>
<views>
<hudson.model.AllView>
<owner class="hudson" reference="../../.."/>
<name>All</name>
<filterExecutors>false</filterExecutors>
<filterQueue>false</filterQueue>
<properties class="hudson.model.View$PropertyList"/>
</hudson.model.AllView>
<listView>
<owner class="hudson" reference="../../.."/>
<name>First</name>
<filterExecutors>false</filterExecutors>
<filterQueue>false</filterQueue>
<properties class="hudson.model.View$PropertyList"/>
<jobNames>
<comparator class="hudson.util.CaseInsensitiveComparator"/>
<string>Matrix</string>
</jobNames>
<jobFilters/>
<columns>
<hudson.views.StatusColumn/>
<hudson.views.WeatherColumn/>
<hudson.views.JobColumn/>
<hudson.views.LastSuccessColumn/>
<hudson.views.LastFailureColumn/>
<hudson.views.LastDurationColumn/>
<hudson.views.BuildButtonColumn/>
<hudson.queueSorter.PrioritySorterJobColumn/>
</columns>
<recurse>false</recurse>
</listView>
<listView>
<owner class="hudson" reference="../../.."/>
<name>ALL</name>
<filterExecutors>false</filterExecutors>
<filterQueue>false</filterQueue>
<properties class="hudson.model.View$PropertyList"/>
<jobNames>
<comparator class="hudson.util.CaseInsensitiveComparator"/>
</jobNames>
<jobFilters/>
<columns>
<hudson.views.StatusColumn/>
<hudson.views.WeatherColumn/>
<hudson.views.JobColumn/>
<hudson.views.LastSuccessColumn/>
<hudson.views.LastFailureColumn/>
<hudson.views.LastDurationColumn/>
<hudson.views.BuildButtonColumn/>
<hudson.queueSorter.PrioritySorterJobColumn/>
</columns>
<includeRegex>.*</includeRegex>
<recurse>false</recurse>
</listView>
</views>
<primaryView>All</primaryView>
<slaveAgentPort>0</slaveAgentPort>
<label></label>
<nodeProperties/>
<globalNodeProperties/>
</hudson>
@@ -0,0 +1,27 @@
<?xml version='1.0' encoding='UTF-8'?>
<jenkins.advancedqueue.PriorityConfiguration plugin="PrioritySorter@3.5.1-SNAPSHOT">
<jobGroups class="linked-list">
<jenkins.advancedqueue.JobGroup>
<id>0</id>
<priority>-1</priority>
<jobGroupStrategy class="jenkins.advancedqueue.jobinclusion.strategy.AllJobsJobInclusionStrategy"/>
<description>Upstream cause test</description>
<runExclusive>false</runExclusive>
<useJobFilter>false</useJobFilter>
<jobPattern>.*</jobPattern>
<usePriorityStrategies>true</usePriorityStrategies>
<priorityStrategies>
<jenkins.advancedqueue.JobGroup_-PriorityStrategyHolder>
<id>0</id>
<priorityStrategy class="jenkins.advancedqueue.priority.strategy.UserIdCauseStrategy">
<priority>1</priority>
</priorityStrategy>
</jenkins.advancedqueue.JobGroup_-PriorityStrategyHolder>
<jenkins.advancedqueue.JobGroup_-PriorityStrategyHolder>
<id>1</id>
<priorityStrategy class="jenkins.advancedqueue.priority.strategy.UpstreamCauseStrategy"/>
</jenkins.advancedqueue.JobGroup_-PriorityStrategyHolder>
</priorityStrategies>
</jenkins.advancedqueue.JobGroup>
</jobGroups>
</jenkins.advancedqueue.PriorityConfiguration>
@@ -0,0 +1,18 @@
<?xml version='1.0' encoding='UTF-8'?>
<jenkins.advancedqueue.PrioritySorterConfiguration>
<legacyMode>false</legacyMode>
<legacyMaxPriority>2147483647</legacyMaxPriority>
<legacyMinPriority>-2147483648</legacyMinPriority>
<allowPriorityOnJobs>true</allowPriorityOnJobs>
<strategy class="jenkins.advancedqueue.sorter.strategy.FQStrategy">
<ifCondition></ifCondition>
<unlessCondition></unlessCondition>
<children/>
<location>
<lineNumber>0</lineNumber>
<columnNumber>0</columnNumber>
</location>
<numberOfPriorities>10</numberOfPriorities>
<defaultPriority>5</defaultPriority>
</strategy>
</jenkins.advancedqueue.PrioritySorterConfiguration>

0 comments on commit df09df3

Please sign in to comment.