/*
* SonarQube
* Copyright (C) 2009-2017 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.ce.queue;
import com.google.common.collect.ImmutableSet;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.util.List;
import java.util.Optional;
import java.util.Random;
import javax.annotation.Nullable;
import org.hamcrest.Matchers;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.sonar.api.utils.System2;
import org.sonar.api.utils.internal.AlwaysIncreasingSystem2;
import org.sonar.ce.monitoring.CEQueueStatus;
import org.sonar.ce.monitoring.CEQueueStatusImpl;
import org.sonar.core.util.UuidFactory;
import org.sonar.core.util.UuidFactoryImpl;
import org.sonar.db.DbSession;
import org.sonar.db.DbTester;
import org.sonar.db.ce.CeActivityDto;
import org.sonar.db.ce.CeQueueDto;
import org.sonar.db.ce.CeTaskTypes;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.component.ComponentTesting;
import org.sonar.db.organization.OrganizationDto;
import org.sonar.server.organization.DefaultOrganization;
import org.sonar.server.organization.DefaultOrganizationProvider;
import static com.google.common.base.Preconditions.checkArgument;
import static java.util.Arrays.asList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class InternalCeQueueImplTest {
private static final String AN_ANALYSIS_UUID = "U1";
private static final String WORKER_UUID_1 = "worker uuid 1";
private static final String WORKER_UUID_2 = "worker uuid 2";
private System2 system2 = new AlwaysIncreasingSystem2();
@Rule
public ExpectedException expectedException = ExpectedException.none();
@Rule
public DbTester dbTester = DbTester.create(system2);
private DbSession session = dbTester.getSession();
private UuidFactory uuidFactory = UuidFactoryImpl.INSTANCE;
private CEQueueStatus queueStatus = new CEQueueStatusImpl(dbTester.getDbClient());
private DefaultOrganizationProvider defaultOrganizationProvider = mock(DefaultOrganizationProvider.class);
private InternalCeQueue underTest = new InternalCeQueueImpl(system2, dbTester.getDbClient(), uuidFactory, queueStatus, defaultOrganizationProvider);
@Before
public void setUp() throws Exception {
OrganizationDto defaultOrganization = dbTester.getDefaultOrganization();
when(defaultOrganizationProvider.get()).thenReturn(DefaultOrganization.newBuilder()
.setUuid(defaultOrganization.getUuid())
.setKey(defaultOrganization.getKey())
.setName(defaultOrganization.getName())
.setCreatedAt(defaultOrganization.getCreatedAt())
.setUpdatedAt(defaultOrganization.getUpdatedAt())
.build());
}
@Test
public void submit_returns_task_populated_from_CeTaskSubmit_and_creates_CeQueue_row() {
CeTaskSubmit taskSubmit = createTaskSubmit(CeTaskTypes.REPORT, "PROJECT_1", "rob");
CeTask task = underTest.submit(taskSubmit);
verifyCeTask(taskSubmit, task, null);
verifyCeQueueDtoForTaskSubmit(taskSubmit);
}
@Test
public void submit_populates_component_name_and_key_of_CeTask_if_component_exists() {
ComponentDto componentDto = insertComponent(newComponentDto("PROJECT_1"));
CeTaskSubmit taskSubmit = createTaskSubmit(CeTaskTypes.REPORT, componentDto.uuid(), null);
CeTask task = underTest.submit(taskSubmit);
verifyCeTask(taskSubmit, task, componentDto);
}
@Test
public void submit_returns_task_without_component_info_when_submit_has_none() {
CeTaskSubmit taskSubmit = createTaskSubmit("not cpt related");
CeTask task = underTest.submit(taskSubmit);
verifyCeTask(taskSubmit, task, null);
}
@Test
public void submit_fails_with_ISE_if_paused() {
underTest.pauseSubmit();
expectedException.expect(IllegalStateException.class);
expectedException.expectMessage("Compute Engine does not currently accept new tasks");
submit(CeTaskTypes.REPORT, "PROJECT_1");
}
@Test
public void massSubmit_returns_tasks_for_each_CeTaskSubmit_populated_from_CeTaskSubmit_and_creates_CeQueue_row_for_each() {
CeTaskSubmit taskSubmit1 = createTaskSubmit(CeTaskTypes.REPORT, "PROJECT_1", "rob");
CeTaskSubmit taskSubmit2 = createTaskSubmit("some type");
List<CeTask> tasks = underTest.massSubmit(asList(taskSubmit1, taskSubmit2));
assertThat(tasks).hasSize(2);
verifyCeTask(taskSubmit1, tasks.get(0), null);
verifyCeTask(taskSubmit2, tasks.get(1), null);
verifyCeQueueDtoForTaskSubmit(taskSubmit1);
verifyCeQueueDtoForTaskSubmit(taskSubmit2);
}
@Test
public void massSubmit_populates_component_name_and_key_of_CeTask_if_component_exists() {
ComponentDto componentDto1 = insertComponent(newComponentDto("PROJECT_1"));
CeTaskSubmit taskSubmit1 = createTaskSubmit(CeTaskTypes.REPORT, componentDto1.uuid(), null);
CeTaskSubmit taskSubmit2 = createTaskSubmit("something", "non existing component uuid", null);
List<CeTask> tasks = underTest.massSubmit(asList(taskSubmit1, taskSubmit2));
assertThat(tasks).hasSize(2);
verifyCeTask(taskSubmit1, tasks.get(0), componentDto1);
verifyCeTask(taskSubmit2, tasks.get(1), null);
}
@Test
public void peek_throws_NPE_if_workerUUid_is_null() {
expectedException.expect(NullPointerException.class);
expectedException.expectMessage("workerUuid can't be null");
underTest.peek(null);
}
@Test
public void test_remove() {
CeTask task = submit(CeTaskTypes.REPORT, "PROJECT_1");
Optional<CeTask> peek = underTest.peek(WORKER_UUID_1);
underTest.remove(peek.get(), CeActivityDto.Status.SUCCESS, null, null);
// queue is empty
assertThat(dbTester.getDbClient().ceQueueDao().selectByUuid(dbTester.getSession(), task.getUuid()).isPresent()).isFalse();
assertThat(underTest.peek(WORKER_UUID_2).isPresent()).isFalse();
// available in history
Optional<CeActivityDto> history = dbTester.getDbClient().ceActivityDao().selectByUuid(dbTester.getSession(), task.getUuid());
assertThat(history.isPresent()).isTrue();
assertThat(history.get().getStatus()).isEqualTo(CeActivityDto.Status.SUCCESS);
assertThat(history.get().getIsLast()).isTrue();
assertThat(history.get().getAnalysisUuid()).isNull();
}
@Test
public void remove_throws_IAE_if_exception_is_provided_but_status_is_SUCCESS() {
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("Error can be provided only when status is FAILED");
underTest.remove(mock(CeTask.class), CeActivityDto.Status.SUCCESS, null, new RuntimeException("Some error"));
}
@Test
public void remove_throws_IAE_if_exception_is_provided_but_status_is_CANCELED() {
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("Error can be provided only when status is FAILED");
underTest.remove(mock(CeTask.class), CeActivityDto.Status.CANCELED, null, new RuntimeException("Some error"));
}
@Test
public void remove_does_not_set_analysisUuid_in_CeActivity_when_CeTaskResult_has_no_analysis_uuid() {
CeTask task = submit(CeTaskTypes.REPORT, "PROJECT_1");
Optional<CeTask> peek = underTest.peek(WORKER_UUID_1);
underTest.remove(peek.get(), CeActivityDto.Status.SUCCESS, newTaskResult(null), null);
// available in history
Optional<CeActivityDto> history = dbTester.getDbClient().ceActivityDao().selectByUuid(dbTester.getSession(), task.getUuid());
assertThat(history.isPresent()).isTrue();
assertThat(history.get().getAnalysisUuid()).isNull();
}
@Test
public void remove_sets_analysisUuid_in_CeActivity_when_CeTaskResult_has_analysis_uuid() {
CeTask task = submit(CeTaskTypes.REPORT, "PROJECT_1");
Optional<CeTask> peek = underTest.peek(WORKER_UUID_2);
underTest.remove(peek.get(), CeActivityDto.Status.SUCCESS, newTaskResult(AN_ANALYSIS_UUID), null);
// available in history
Optional<CeActivityDto> history = dbTester.getDbClient().ceActivityDao().selectByUuid(dbTester.getSession(), task.getUuid());
assertThat(history.isPresent()).isTrue();
assertThat(history.get().getAnalysisUuid()).isEqualTo("U1");
}
@Test
public void remove_saves_error_message_and_stacktrace_when_exception_is_provided() {
Throwable error = new NullPointerException("Fake NPE to test persistence to DB");
CeTask task = submit(CeTaskTypes.REPORT, "PROJECT_1");
Optional<CeTask> peek = underTest.peek(WORKER_UUID_1);
underTest.remove(peek.get(), CeActivityDto.Status.FAILED, null, error);
Optional<CeActivityDto> activityDto = dbTester.getDbClient().ceActivityDao().selectByUuid(session, task.getUuid());
assertThat(activityDto).isPresent();
assertThat(activityDto.get().getErrorMessage()).isEqualTo(error.getMessage());
assertThat(activityDto.get().getErrorStacktrace()).isEqualToIgnoringWhitespace(stacktraceToString(error));
}
@Test
public void remove_copies_executionCount_and_workerUuid() {
dbTester.getDbClient().ceQueueDao().insert(session, new CeQueueDto()
.setUuid("uuid")
.setTaskType("foo")
.setStatus(CeQueueDto.Status.PENDING)
.setWorkerUuid("Dustin")
.setExecutionCount(2));
dbTester.commit();
underTest.remove(new CeTask.Builder()
.setOrganizationUuid("foo")
.setUuid("uuid")
.setType("bar")
.build(), CeActivityDto.Status.SUCCESS, null, null);
CeActivityDto dto = dbTester.getDbClient().ceActivityDao().selectByUuid(dbTester.getSession(), "uuid").get();
assertThat(dto.getExecutionCount()).isEqualTo(2);
assertThat(dto.getWorkerUuid()).isEqualTo("Dustin");
}
@Test
public void fail_to_remove_if_not_in_queue() throws Exception {
CeTask task = submit(CeTaskTypes.REPORT, "PROJECT_1");
underTest.remove(task, CeActivityDto.Status.SUCCESS, null, null);
expectedException.expect(IllegalStateException.class);
underTest.remove(task, CeActivityDto.Status.SUCCESS, null, null);
}
@Test
public void test_peek() throws Exception {
CeTask task = submit(CeTaskTypes.REPORT, "PROJECT_1");
Optional<CeTask> peek = underTest.peek(WORKER_UUID_1);
assertThat(peek.isPresent()).isTrue();
assertThat(peek.get().getUuid()).isEqualTo(task.getUuid());
assertThat(peek.get().getType()).isEqualTo(CeTaskTypes.REPORT);
assertThat(peek.get().getComponentUuid()).isEqualTo("PROJECT_1");
// no more pending tasks
peek = underTest.peek(WORKER_UUID_2);
assertThat(peek.isPresent()).isFalse();
}
@Test
public void peek_overrides_workerUuid_to_argument() {
dbTester.getDbClient().ceQueueDao().insert(session, new CeQueueDto()
.setUuid("uuid")
.setTaskType("foo")
.setStatus(CeQueueDto.Status.PENDING)
.setWorkerUuid("must be overriden"));
dbTester.commit();
underTest.peek(WORKER_UUID_1);
CeQueueDto ceQueueDto = dbTester.getDbClient().ceQueueDao().selectByUuid(session, "uuid").get();
assertThat(ceQueueDto.getWorkerUuid()).isEqualTo(WORKER_UUID_1);
}
@Test
public void peek_nothing_if_paused() throws Exception {
submit(CeTaskTypes.REPORT, "PROJECT_1");
underTest.pausePeek();
Optional<CeTask> peek = underTest.peek(WORKER_UUID_1);
assertThat(peek.isPresent()).isFalse();
}
@Test
public void peek_peeks_pending_tasks_with_executionCount_equal_to_0_and_increases_it() {
dbTester.getDbClient().ceQueueDao().insert(session, new CeQueueDto()
.setUuid("uuid")
.setTaskType("foo")
.setStatus(CeQueueDto.Status.PENDING)
.setExecutionCount(0));
dbTester.commit();
assertThat(underTest.peek(WORKER_UUID_1).get().getUuid()).isEqualTo("uuid");
assertThat(dbTester.getDbClient().ceQueueDao().selectByUuid(session, "uuid").get().getExecutionCount()).isEqualTo(1);
}
@Test
public void peek_peeks_pending_tasks_with_executionCount_equal_to_1_and_increases_it() {
dbTester.getDbClient().ceQueueDao().insert(session, new CeQueueDto()
.setUuid("uuid")
.setTaskType("foo")
.setStatus(CeQueueDto.Status.PENDING)
.setExecutionCount(1));
dbTester.commit();
assertThat(underTest.peek(WORKER_UUID_1).get().getUuid()).isEqualTo("uuid");
assertThat(dbTester.getDbClient().ceQueueDao().selectByUuid(session, "uuid").get().getExecutionCount()).isEqualTo(2);
}
@Test
public void peek_ignores_pending_tasks_with_executionCount_equal_to_2() {
dbTester.getDbClient().ceQueueDao().insert(session, new CeQueueDto()
.setUuid("uuid")
.setTaskType("foo")
.setStatus(CeQueueDto.Status.PENDING)
.setExecutionCount(2));
dbTester.commit();
assertThat(underTest.peek(WORKER_UUID_1).isPresent()).isFalse();
}
@Test
public void peek_ignores_pending_tasks_with_executionCount_greater_than_2() {
dbTester.getDbClient().ceQueueDao().insert(session, new CeQueueDto()
.setUuid("uuid")
.setTaskType("foo")
.setStatus(CeQueueDto.Status.PENDING)
.setExecutionCount(2 + Math.abs(new Random().nextInt(100))));
dbTester.commit();
assertThat(underTest.peek(WORKER_UUID_1).isPresent()).isFalse();
}
@Test
public void peek_resets_to_pending_any_task_in_progress_for_specified_worker_uuid_and_updates_updatedAt_no_matter_execution_count() {
insertPending("u0", "doesn't matter", 0); // add a pending one that will be picked so that u1 isn't peek and status reset is visible in DB
CeQueueDto u1 = insertPending("u1", WORKER_UUID_1, 2);// won't be peeked because it's worn-out
CeQueueDto u2 = insertInProgress("u2", WORKER_UUID_1, 3);// will be reset but won't be picked because it's worn-out
CeQueueDto u3 = insertPending("u3", WORKER_UUID_1, 1);// will be picked-because older than any of the reset ones
CeQueueDto u4 = insertInProgress("u4", WORKER_UUID_1, 1);// will be reset
assertThat(underTest.peek(WORKER_UUID_1).get().getUuid()).isEqualTo("u0");
verifyUnmodifiedTask(u1);
verifyResetTask(u2);
verifyUnmodifiedTask(u3);
verifyResetTask(u4);
}
@Test
public void peek_resets_to_pending_any_task_in_progress_for_specified_worker_uuid_and_only_this_uuid() {
insertPending("u0", "doesn't matter", 0); // add a pending one that will be picked so that u1 isn't peek and status reset is visible in DB
CeQueueDto u1 = insertInProgress("u1", WORKER_UUID_1, 3);
CeQueueDto u2 = insertInProgress("u2", WORKER_UUID_2, 3);
CeQueueDto u3 = insertInProgress("u3", WORKER_UUID_1, 3);
CeQueueDto u4 = insertInProgress("u4", WORKER_UUID_2, 1);
assertThat(underTest.peek(WORKER_UUID_1).get().getUuid()).isEqualTo("u0");
verifyResetTask(u1);
verifyUnmodifiedTask(u2);
verifyResetTask(u3);
verifyUnmodifiedTask(u4);
}
@Test
public void peek_resets_to_pending_any_task_in_progress_for_specified_worker_uuid_and_peeks_the_oldest_non_worn_out_no_matter_if_it_has_been_reset_or_not() {
insertPending("u1", WORKER_UUID_1, 3); // won't be picked because worn out
insertInProgress("u2", WORKER_UUID_1, 3); // will be reset but won't be picked because worn out
insertPending("u3", WORKER_UUID_1, 0); // will be picked first
insertInProgress("u4", WORKER_UUID_1, 1); // will be reset and picked on second call only
Optional<CeTask> ceTask = underTest.peek(WORKER_UUID_1);
assertThat(ceTask.get().getUuid()).isEqualTo("u3");
// remove first task and do another peek: will pick the reset task since it's now the oldest one
underTest.remove(ceTask.get(), CeActivityDto.Status.SUCCESS, null, null);
assertThat(underTest.peek(WORKER_UUID_1).get().getUuid()).isEqualTo("u4");
}
@Test
public void peek_resets_to_pending_any_task_in_progress_for_specified_worker_uuid_and_peeks_reset_tasks_if_is_the_oldest_non_worn_out() {
insertPending("u1", WORKER_UUID_1, 3); // won't be picked because worn out
insertInProgress("u2", WORKER_UUID_1, 3); // will be reset but won't be picked because worn out
insertInProgress("u3", WORKER_UUID_1, 1); // will be reset and picked
insertPending("u4", WORKER_UUID_1, 0); // will be picked second
Optional<CeTask> ceTask = underTest.peek(WORKER_UUID_1);
assertThat(ceTask.get().getUuid()).isEqualTo("u3");
// remove first task and do another peek: will pick the reset task since it's now the oldest one
underTest.remove(ceTask.get(), CeActivityDto.Status.SUCCESS, null, null);
assertThat(underTest.peek(WORKER_UUID_1).get().getUuid()).isEqualTo("u4");
}
private void verifyResetTask(CeQueueDto originalDto) {
CeQueueDto dto = dbTester.getDbClient().ceQueueDao().selectByUuid(session, originalDto.getUuid()).get();
assertThat(dto.getStatus()).isEqualTo(CeQueueDto.Status.PENDING);
assertThat(dto.getExecutionCount()).isEqualTo(originalDto.getExecutionCount());
assertThat(dto.getCreatedAt()).isEqualTo(originalDto.getCreatedAt());
assertThat(dto.getUpdatedAt()).isGreaterThan(originalDto.getUpdatedAt());
}
private void verifyUnmodifiedTask(CeQueueDto originalDto) {
CeQueueDto dto = dbTester.getDbClient().ceQueueDao().selectByUuid(session, originalDto.getUuid()).get();
assertThat(dto.getStatus()).isEqualTo(originalDto.getStatus());
assertThat(dto.getExecutionCount()).isEqualTo(originalDto.getExecutionCount());
assertThat(dto.getCreatedAt()).isEqualTo(originalDto.getCreatedAt());
assertThat(dto.getUpdatedAt()).isEqualTo(originalDto.getUpdatedAt());
}
private CeQueueDto insertInProgress(String uuid, String workerUuid, int executionCount) {
checkArgument(executionCount > 0, "execution count less than 1 does not make sense for an in progress task");
CeQueueDto dto = new CeQueueDto()
.setUuid(uuid)
.setTaskType("foo")
.setStatus(CeQueueDto.Status.IN_PROGRESS)
.setWorkerUuid(workerUuid)
.setExecutionCount(executionCount);
dbTester.getDbClient().ceQueueDao().insert(session, dto);
dbTester.commit();
return dto;
}
private CeQueueDto insertPending(String uuid, String workerUuid, int executionCount) {
checkArgument(executionCount > -1, "execution count less than 0 does not make sense for a pending task");
CeQueueDto dto = new CeQueueDto()
.setUuid(uuid)
.setTaskType("foo")
.setStatus(CeQueueDto.Status.PENDING)
.setWorkerUuid(workerUuid)
.setExecutionCount(executionCount);
dbTester.getDbClient().ceQueueDao().insert(session, dto);
dbTester.commit();
return dto;
}
@Test
public void cancel_pending() throws Exception {
CeTask task = submit(CeTaskTypes.REPORT, "PROJECT_1");
// ignore
boolean canceled = underTest.cancel("UNKNOWN");
assertThat(canceled).isFalse();
canceled = underTest.cancel(task.getUuid());
assertThat(canceled).isTrue();
Optional<CeActivityDto> activity = dbTester.getDbClient().ceActivityDao().selectByUuid(dbTester.getSession(), task.getUuid());
assertThat(activity.isPresent()).isTrue();
assertThat(activity.get().getStatus()).isEqualTo(CeActivityDto.Status.CANCELED);
}
@Test
public void cancel_copies_executionCount_and_workerUuid() {
dbTester.getDbClient().ceQueueDao().insert(session, new CeQueueDto()
.setUuid("uuid")
.setTaskType("foo")
.setStatus(CeQueueDto.Status.PENDING)
.setWorkerUuid("Dustin")
.setExecutionCount(2));
dbTester.commit();
underTest.cancel("uuid");
CeActivityDto dto = dbTester.getDbClient().ceActivityDao().selectByUuid(dbTester.getSession(), "uuid").get();
assertThat(dto.getExecutionCount()).isEqualTo(2);
assertThat(dto.getWorkerUuid()).isEqualTo("Dustin");
}
@Test
public void fail_to_cancel_if_in_progress() throws Exception {
expectedException.expect(IllegalStateException.class);
expectedException.expectMessage(Matchers.startsWith("Task is in progress and can't be canceled"));
CeTask task = submit(CeTaskTypes.REPORT, "PROJECT_1");
underTest.peek(WORKER_UUID_2);
underTest.cancel(task.getUuid());
}
@Test
public void cancelAll_pendings_but_not_in_progress() throws Exception {
CeTask inProgressTask = submit(CeTaskTypes.REPORT, "PROJECT_1");
CeTask pendingTask1 = submit(CeTaskTypes.REPORT, "PROJECT_2");
CeTask pendingTask2 = submit(CeTaskTypes.REPORT, "PROJECT_3");
underTest.peek(WORKER_UUID_2);
int canceledCount = underTest.cancelAll();
assertThat(canceledCount).isEqualTo(2);
Optional<CeActivityDto> history = dbTester.getDbClient().ceActivityDao().selectByUuid(dbTester.getSession(), pendingTask1.getUuid());
assertThat(history.get().getStatus()).isEqualTo(CeActivityDto.Status.CANCELED);
history = dbTester.getDbClient().ceActivityDao().selectByUuid(dbTester.getSession(), pendingTask2.getUuid());
assertThat(history.get().getStatus()).isEqualTo(CeActivityDto.Status.CANCELED);
history = dbTester.getDbClient().ceActivityDao().selectByUuid(dbTester.getSession(), inProgressTask.getUuid());
assertThat(history.isPresent()).isFalse();
}
@Test
public void cancelWornOuts_cancels_pending_tasks_with_executionCount_greater_or_equal_to_2() {
CeQueueDto u1 = insertCeQueueDto("u1", CeQueueDto.Status.PENDING, 0, "worker1");
CeQueueDto u2 = insertCeQueueDto("u2", CeQueueDto.Status.PENDING, 1, "worker1");
CeQueueDto u3 = insertCeQueueDto("u3", CeQueueDto.Status.PENDING, 2, "worker1");
CeQueueDto u4 = insertCeQueueDto("u4", CeQueueDto.Status.PENDING, 3, "worker1");
CeQueueDto u5 = insertCeQueueDto("u5", CeQueueDto.Status.IN_PROGRESS, 0, "worker1");
CeQueueDto u6 = insertCeQueueDto("u6", CeQueueDto.Status.IN_PROGRESS, 1, "worker1");
CeQueueDto u7 = insertCeQueueDto("u7", CeQueueDto.Status.IN_PROGRESS, 2, "worker1");
CeQueueDto u8 = insertCeQueueDto("u8", CeQueueDto.Status.IN_PROGRESS, 3, "worker1");
underTest.cancelWornOuts();
verifyUnmodified(u1);
verifyUnmodified(u2);
verifyCanceled(u3);
verifyCanceled(u4);
verifyUnmodified(u5);
verifyUnmodified(u6);
verifyUnmodified(u7);
verifyUnmodified(u8);
}
@Test
public void resetTasksWithUnknownWorkerUUIDs_reset_only_in_progress_tasks() {
CeQueueDto u1 = insertCeQueueDto("u1", CeQueueDto.Status.PENDING, 0, null);
CeQueueDto u2 = insertCeQueueDto("u2", CeQueueDto.Status.PENDING, 1, "worker1");
CeQueueDto u3 = insertCeQueueDto("u3", CeQueueDto.Status.PENDING, 2, null);
CeQueueDto u4 = insertCeQueueDto("u4", CeQueueDto.Status.PENDING, 3, "worker2");
CeQueueDto u5 = insertCeQueueDto("u5", CeQueueDto.Status.IN_PROGRESS, 0, null);
CeQueueDto u6 = insertCeQueueDto("u6", CeQueueDto.Status.IN_PROGRESS, 1, "worker1");
CeQueueDto u7 = insertCeQueueDto("u7", CeQueueDto.Status.IN_PROGRESS, 2, "worker2");
CeQueueDto u8 = insertCeQueueDto("u8", CeQueueDto.Status.IN_PROGRESS, 3, "worker3");
underTest.resetTasksWithUnknownWorkerUUIDs(ImmutableSet.of("worker2", "worker3"));
// Pending tasks must not be modified even if a workerUUID is not present
verifyUnmodified(u1);
verifyUnmodified(u2);
verifyUnmodified(u3);
verifyUnmodified(u4);
// Unknown worker : null, "worker1"
verifyReset(u5);
verifyReset(u6);
// Known workers : "worker2", "worker3"
verifyUnmodified(u7);
verifyUnmodified(u8);
}
@Test
public void resetTasksWithUnknownWorkerUUIDs_with_empty_set_will_reset_all_in_progress_tasks() {
CeQueueDto u1 = insertCeQueueDto("u1", CeQueueDto.Status.PENDING, 0, null);
CeQueueDto u2 = insertCeQueueDto("u2", CeQueueDto.Status.PENDING, 1, "worker1");
CeQueueDto u3 = insertCeQueueDto("u3", CeQueueDto.Status.PENDING, 2, null);
CeQueueDto u4 = insertCeQueueDto("u4", CeQueueDto.Status.PENDING, 3, "worker2");
CeQueueDto u5 = insertCeQueueDto("u5", CeQueueDto.Status.IN_PROGRESS, 0, null);
CeQueueDto u6 = insertCeQueueDto("u6", CeQueueDto.Status.IN_PROGRESS, 1, "worker1");
CeQueueDto u7 = insertCeQueueDto("u7", CeQueueDto.Status.IN_PROGRESS, 2, "worker2");
CeQueueDto u8 = insertCeQueueDto("u8", CeQueueDto.Status.IN_PROGRESS, 3, "worker3");
underTest.resetTasksWithUnknownWorkerUUIDs(ImmutableSet.of());
// Pending tasks must not be modified even if a workerUUID is not present
verifyUnmodified(u1);
verifyUnmodified(u2);
verifyUnmodified(u3);
verifyUnmodified(u4);
// Unknown worker : null, "worker1"
verifyReset(u5);
verifyReset(u6);
verifyReset(u7);
verifyReset(u8);
}
@Test
public void resetTasksWithUnknownWorkerUUIDs_with_worker_without_tasks_will_reset_all_in_progress_tasks() {
CeQueueDto u1 = insertCeQueueDto("u1", CeQueueDto.Status.PENDING, 0, null);
CeQueueDto u2 = insertCeQueueDto("u2", CeQueueDto.Status.PENDING, 1, "worker1");
CeQueueDto u3 = insertCeQueueDto("u3", CeQueueDto.Status.PENDING, 2, null);
CeQueueDto u4 = insertCeQueueDto("u4", CeQueueDto.Status.PENDING, 3, "worker2");
CeQueueDto u5 = insertCeQueueDto("u5", CeQueueDto.Status.IN_PROGRESS, 0, null);
CeQueueDto u6 = insertCeQueueDto("u6", CeQueueDto.Status.IN_PROGRESS, 1, "worker1");
CeQueueDto u7 = insertCeQueueDto("u7", CeQueueDto.Status.IN_PROGRESS, 2, "worker2");
CeQueueDto u8 = insertCeQueueDto("u8", CeQueueDto.Status.IN_PROGRESS, 3, "worker3");
underTest.resetTasksWithUnknownWorkerUUIDs(ImmutableSet.of("worker1000", "worker1001"));
// Pending tasks must not be modified even if a workerUUID is not present
verifyUnmodified(u1);
verifyUnmodified(u2);
verifyUnmodified(u3);
verifyUnmodified(u4);
// Unknown worker : null, "worker1"
verifyReset(u5);
verifyReset(u6);
verifyReset(u7);
verifyReset(u8);
}
private void verifyReset(CeQueueDto original) {
CeQueueDto dto = dbTester.getDbClient().ceQueueDao().selectByUuid(dbTester.getSession(), original.getUuid()).get();
// We do not touch ExecutionCount nor CreatedAt
assertThat(dto.getExecutionCount()).isEqualTo(original.getExecutionCount());
assertThat(dto.getCreatedAt()).isEqualTo(original.getCreatedAt());
// Status must have changed to PENDING and must not be equal to previous status
assertThat(dto.getStatus()).isEqualTo(CeQueueDto.Status.PENDING).isNotEqualTo(original.getStatus());
// UpdatedAt must have been updated
assertThat(dto.getUpdatedAt()).isNotEqualTo(original.getUpdatedAt());
// StartedAt must be null
assertThat(dto.getStartedAt()).isNull();
// WorkerUuid must be null
assertThat(dto.getWorkerUuid()).isNull();
}
private void verifyUnmodified(CeQueueDto original) {
CeQueueDto dto = dbTester.getDbClient().ceQueueDao().selectByUuid(dbTester.getSession(), original.getUuid()).get();
assertThat(dto.getStatus()).isEqualTo(original.getStatus());
assertThat(dto.getExecutionCount()).isEqualTo(original.getExecutionCount());
assertThat(dto.getCreatedAt()).isEqualTo(original.getCreatedAt());
assertThat(dto.getUpdatedAt()).isEqualTo(original.getUpdatedAt());
}
private void verifyCanceled(CeQueueDto original) {
assertThat(dbTester.getDbClient().ceQueueDao().selectByUuid(dbTester.getSession(), original.getUuid())).isEmpty();
CeActivityDto dto = dbTester.getDbClient().ceActivityDao().selectByUuid(dbTester.getSession(), original.getUuid()).get();
assertThat(dto.getStatus()).isEqualTo(CeActivityDto.Status.CANCELED);
assertThat(dto.getExecutionCount()).isEqualTo(original.getExecutionCount());
assertThat(dto.getWorkerUuid()).isEqualTo(original.getWorkerUuid());
}
private CeQueueDto insertCeQueueDto(String uuid, CeQueueDto.Status status, int executionCount, String workerUuid) {
CeQueueDto dto = new CeQueueDto()
.setUuid(uuid)
.setTaskType("foo")
.setStatus(status)
.setExecutionCount(executionCount)
.setWorkerUuid(workerUuid);
dbTester.getDbClient().ceQueueDao().insert(dbTester.getSession(), dto);
dbTester.commit();
return dto;
}
@Test
public void pause_and_resume_submits() throws Exception {
assertThat(underTest.isSubmitPaused()).isFalse();
underTest.pauseSubmit();
assertThat(underTest.isSubmitPaused()).isTrue();
underTest.resumeSubmit();
assertThat(underTest.isSubmitPaused()).isFalse();
}
@Test
public void pause_and_resume_peeks() throws Exception {
assertThat(underTest.isPeekPaused()).isFalse();
underTest.pausePeek();
assertThat(underTest.isPeekPaused()).isTrue();
underTest.resumePeek();
assertThat(underTest.isPeekPaused()).isFalse();
}
private void verifyCeTask(CeTaskSubmit taskSubmit, CeTask task, @Nullable ComponentDto componentDto) {
if (componentDto == null) {
assertThat(task.getOrganizationUuid()).isEqualTo(defaultOrganizationProvider.get().getUuid());
} else {
assertThat(task.getOrganizationUuid()).isEqualTo(componentDto.getOrganizationUuid());
}
assertThat(task.getUuid()).isEqualTo(taskSubmit.getUuid());
assertThat(task.getComponentUuid()).isEqualTo(task.getComponentUuid());
assertThat(task.getType()).isEqualTo(taskSubmit.getType());
if (componentDto == null) {
assertThat(task.getComponentKey()).isNull();
assertThat(task.getComponentName()).isNull();
} else {
assertThat(task.getComponentKey()).isEqualTo(componentDto.key());
assertThat(task.getComponentName()).isEqualTo(componentDto.name());
}
assertThat(task.getSubmitterLogin()).isEqualTo(taskSubmit.getSubmitterLogin());
}
private void verifyCeQueueDtoForTaskSubmit(CeTaskSubmit taskSubmit) {
Optional<CeQueueDto> queueDto = dbTester.getDbClient().ceQueueDao().selectByUuid(dbTester.getSession(), taskSubmit.getUuid());
assertThat(queueDto.isPresent()).isTrue();
CeQueueDto dto = queueDto.get();
assertThat(dto.getTaskType()).isEqualTo(taskSubmit.getType());
assertThat(dto.getComponentUuid()).isEqualTo(taskSubmit.getComponentUuid());
assertThat(dto.getSubmitterLogin()).isEqualTo(taskSubmit.getSubmitterLogin());
assertThat(dto.getCreatedAt()).isEqualTo(dto.getUpdatedAt()).isNotNull();
}
private ComponentDto newComponentDto(String uuid) {
return ComponentTesting.newPublicProjectDto(dbTester.getDefaultOrganization(), uuid).setName("name_" + uuid).setKey("key_" + uuid);
}
private CeTask submit(String reportType, String componentUuid) {
return underTest.submit(createTaskSubmit(reportType, componentUuid, null));
}
private CeTaskSubmit createTaskSubmit(String type) {
return createTaskSubmit(type, null, null);
}
private CeTaskSubmit createTaskSubmit(String type, @Nullable String componentUuid, @Nullable String submitterLogin) {
CeTaskSubmit.Builder submission = underTest.prepareSubmit();
submission.setType(type);
submission.setComponentUuid(componentUuid);
submission.setSubmitterLogin(submitterLogin);
return submission.build();
}
private CeTaskResult newTaskResult(@Nullable String analysisUuid) {
CeTaskResult taskResult = mock(CeTaskResult.class);
when(taskResult.getAnalysisUuid()).thenReturn(java.util.Optional.ofNullable(analysisUuid));
return taskResult;
}
private ComponentDto insertComponent(ComponentDto componentDto) {
dbTester.getDbClient().componentDao().insert(session, componentDto);
session.commit();
return componentDto;
}
private static String stacktraceToString(Throwable error) {
ByteArrayOutputStream out = new ByteArrayOutputStream();
error.printStackTrace(new PrintStream(out));
return out.toString();
}
}