package hudson.plugins.throttleconcurrents;
import hudson.model.AbstractProject;
import hudson.model.FreeStyleProject;
import hudson.model.Job;
import hudson.model.Queue;
import hudson.security.ACL;
import hudson.security.AuthorizationStrategy;
import org.jvnet.hudson.test.Bug;
import org.jvnet.hudson.test.HudsonTestCase;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
public class ThrottleJobPropertyTest extends HudsonTestCase {
private static final String THROTTLE_OPTION_CATEGORY = "category"; // TODO move this into ThrottleJobProperty and use consistently; same for "project"
private final Random random = new Random(System.currentTimeMillis());
@Bug(19623)
public void testGetCategoryProjects() throws Exception {
String alpha = "alpha", beta = "beta", gamma = "gamma"; // category names
FreeStyleProject p1 = createFreeStyleProject("p1");
FreeStyleProject p2 = createFreeStyleProject("p2");
p2.addProperty(new ThrottleJobProperty(1, 1, Arrays.asList(alpha), false, THROTTLE_OPTION_CATEGORY, false, "", ThrottleMatrixProjectOptions.DEFAULT));
FreeStyleProject p3 = createFreeStyleProject("p3");
p3.addProperty(new ThrottleJobProperty(1, 1, Arrays.asList(alpha, beta), true, THROTTLE_OPTION_CATEGORY, false, "", ThrottleMatrixProjectOptions.DEFAULT));
FreeStyleProject p4 = createFreeStyleProject("p4");
p4.addProperty(new ThrottleJobProperty(1, 1, Arrays.asList(beta, gamma), true, THROTTLE_OPTION_CATEGORY, false, "", ThrottleMatrixProjectOptions.DEFAULT));
// TODO when core dep ≥1.480.3, add cloudbees-folder as a test dependency so we can check jobs inside folders
assertProjects(alpha, p3);
assertProjects(beta, p3, p4);
assertProjects(gamma, p4);
assertProjects("delta");
p4.renameTo("p-4");
assertProjects(gamma, p4);
p4.delete();
assertProjects(gamma);
AbstractProject<?,?> p3b = jenkins.<AbstractProject<?,?>>copy(p3, "p3b");
assertProjects(beta, p3, p3b);
p3.removeProperty(ThrottleJobProperty.class);
assertProjects(beta, p3b);
}
public void testToString_withNulls(){
ThrottleJobProperty tjp = new ThrottleJobProperty(0,0, null, false, null, false, "", ThrottleMatrixProjectOptions.DEFAULT);
assertNotNull(tjp.toString());
}
public void testThrottleJob_constructor_should_store_arguments() {
Integer expectedMaxConcurrentPerNode = anyInt();
Integer expectedMaxConcurrentTotal = anyInt();
List<String> expectedCategories = Collections.emptyList();
boolean expectedThrottleEnabled = anyBoolean();
String expectedThrottleOption = anyString();
boolean expectedLimitOneJobWithMatchingParams = anyBoolean();
String expectedParamsToUseForLimit = anyString();
ThrottleJobProperty property = new ThrottleJobProperty(expectedMaxConcurrentPerNode,
expectedMaxConcurrentTotal,
expectedCategories, expectedThrottleEnabled, expectedThrottleOption,
expectedLimitOneJobWithMatchingParams, expectedParamsToUseForLimit,
ThrottleMatrixProjectOptions.DEFAULT);
assertEquals(expectedMaxConcurrentPerNode, property.getMaxConcurrentPerNode());
assertEquals(expectedMaxConcurrentTotal, property.getMaxConcurrentTotal());
assertEquals(expectedCategories, property.getCategories());
assertEquals(expectedThrottleEnabled, property.getThrottleEnabled());
assertEquals(expectedThrottleOption, property.getThrottleOption());
}
public void testThrottleJob_should_copy_categories_to_concurrency_safe_list() {
final String category = anyString();
ArrayList<String> unsafeList = new ArrayList<String>() {{
add(category);
}};
ThrottleJobProperty property = new ThrottleJobProperty(anyInt(),
anyInt(),
unsafeList,
anyBoolean(),
"throttle_option",
anyBoolean(),
anyString(),
ThrottleMatrixProjectOptions.DEFAULT);
List<String> storedCategories = property.getCategories();
assertEquals("contents of original and stored list should be the equal", unsafeList, storedCategories);
assertTrue("expected unsafe list to be converted to a converted to some other concurrency-safe impl",
unsafeList != storedCategories);
assertTrue(storedCategories instanceof CopyOnWriteArrayList);
}
public void testThrottleJob_constructor_handles_null_categories(){
ThrottleJobProperty property = new ThrottleJobProperty(anyInt(),
anyInt(),
null,
anyBoolean(),
"throttle_option",
anyBoolean(),
anyString(),
ThrottleMatrixProjectOptions.DEFAULT);
assertEquals(Collections.<String>emptyList(), property.getCategories());
}
public void testDescriptorImpl_should_a_concurrency_safe_list_for_categories(){
ThrottleJobProperty.DescriptorImpl descriptor = new ThrottleJobProperty.DescriptorImpl();
assertTrue(descriptor.getCategories() instanceof CopyOnWriteArrayList);
final ThrottleJobProperty.ThrottleCategory category = new ThrottleJobProperty.ThrottleCategory(
anyString(), anyInt(), anyInt(), null);
ArrayList<ThrottleJobProperty.ThrottleCategory> unsafeList =
new ArrayList<ThrottleJobProperty.ThrottleCategory>() {{
add(category);
}};
descriptor.setCategories(unsafeList);
List<ThrottleJobProperty.ThrottleCategory> storedCategories = descriptor.getCategories();
assertEquals("contents of original and stored list should be the equal", unsafeList, storedCategories);
assertTrue("expected unsafe list to be converted to a converted to some other concurrency-safe impl",
unsafeList != storedCategories);
assertTrue(storedCategories instanceof CopyOnWriteArrayList);
}
private void assertProjects(String category, AbstractProject<?,?>... projects) {
jenkins.setAuthorizationStrategy(new RejectAllAuthorizationStrategy());
try {
assertEquals(new HashSet<Queue.Task>(Arrays.asList(projects)), new HashSet<Queue.Task>
(ThrottleJobProperty.getCategoryTasks(category)));
} finally {
jenkins.setAuthorizationStrategy(AuthorizationStrategy.UNSECURED); // do not check during e.g. rebuildDependencyGraph from delete
}
}
private static class RejectAllAuthorizationStrategy extends AuthorizationStrategy {
RejectAllAuthorizationStrategy() {}
@Override public ACL getRootACL() {
return new AuthorizationStrategy.Unsecured().getRootACL();
}
@Override public Collection<String> getGroups() {
return Collections.emptySet();
}
@Override public ACL getACL(Job<?,?> project) {
fail("not even supposed to be looking at " + project);
return super.getACL(project);
}
}
private String anyString() {
return "concurrency_" + anyInt();
}
private boolean anyBoolean() {
return random.nextBoolean();
}
private int anyInt() {
return random.nextInt(10000);
}
}