/*
* 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.taskprocessor;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import javax.annotation.Nullable;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.InOrder;
import org.mockito.Mockito;
import org.sonar.api.utils.log.LogTester;
import org.sonar.api.utils.log.LoggerLevel;
import org.sonar.ce.log.CeLogging;
import org.sonar.ce.queue.CeTask;
import org.sonar.ce.queue.InternalCeQueue;
import org.sonar.db.ce.CeActivityDto;
import org.sonar.db.ce.CeTaskTypes;
import org.sonar.server.computation.task.projectanalysis.taskprocessor.ReportTaskProcessor;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
public class CeWorkerImplTest {
@Rule
public CeTaskProcessorRepositoryRule taskProcessorRepository = new CeTaskProcessorRepositoryRule();
@Rule
public LogTester logTester = new LogTester();
private InternalCeQueue queue = mock(InternalCeQueue.class);
private ReportTaskProcessor taskProcessor = mock(ReportTaskProcessor.class);
private CeLogging ceLogging = spy(CeLogging.class);
private ArgumentCaptor<String> workerUuid = ArgumentCaptor.forClass(String.class);
private CeWorker underTest = new CeWorkerImpl(queue, ceLogging, taskProcessorRepository, UUID.randomUUID().toString());
private InOrder inOrder = Mockito.inOrder(ceLogging, taskProcessor, queue);
@Test
public void getUUID_must_return_the_uuid_of_constructor() {
String uuid = UUID.randomUUID().toString();
CeWorker underTest = new CeWorkerImpl(queue, ceLogging, taskProcessorRepository, uuid);
assertThat(underTest.getUUID()).isEqualTo(uuid);
}
@Test
public void no_pending_tasks_in_queue() throws Exception {
when(queue.peek(anyString())).thenReturn(Optional.empty());
assertThat(underTest.call()).isFalse();
verifyZeroInteractions(taskProcessor, ceLogging);
}
@Test
public void fail_when_no_CeTaskProcessor_is_found_in_repository() throws Exception {
CeTask task = createCeTask(null);
taskProcessorRepository.setNoProcessorForTask(CeTaskTypes.REPORT);
when(queue.peek(anyString())).thenReturn(Optional.of(task));
assertThat(underTest.call()).isTrue();
verifyWorkerUuid();
inOrder.verify(ceLogging).initForTask(task);
inOrder.verify(queue).remove(task, CeActivityDto.Status.FAILED, null, null);
inOrder.verify(ceLogging).clearForTask();
}
@Test
public void peek_and_process_task() throws Exception {
CeTask task = createCeTask(null);
taskProcessorRepository.setProcessorForTask(task.getType(), taskProcessor);
when(queue.peek(anyString())).thenReturn(Optional.of(task));
assertThat(underTest.call()).isTrue();
verifyWorkerUuid();
inOrder.verify(ceLogging).initForTask(task);
inOrder.verify(taskProcessor).process(task);
inOrder.verify(queue).remove(task, CeActivityDto.Status.SUCCESS, null, null);
inOrder.verify(ceLogging).clearForTask();
}
@Test
public void fail_to_process_task() throws Exception {
CeTask task = createCeTask(null);
when(queue.peek(anyString())).thenReturn(Optional.of(task));
taskProcessorRepository.setProcessorForTask(task.getType(), taskProcessor);
Throwable error = makeTaskProcessorFail(task);
assertThat(underTest.call()).isTrue();
verifyWorkerUuid();
inOrder.verify(ceLogging).initForTask(task);
inOrder.verify(taskProcessor).process(task);
inOrder.verify(queue).remove(task, CeActivityDto.Status.FAILED, null, error);
inOrder.verify(ceLogging).clearForTask();
}
@Test
public void do_not_display_submitter_param_in_log_when_submitterLogin_is_not_set_in_case_of_success() throws Exception {
when(queue.peek(anyString())).thenReturn(Optional.of(createCeTask(null)));
taskProcessorRepository.setProcessorForTask(CeTaskTypes.REPORT, taskProcessor);
underTest.call();
verifyWorkerUuid();
List<String> logs = logTester.logs(LoggerLevel.INFO);
assertThat(logs).hasSize(2);
for (int i = 0; i < 2; i++) {
assertThat(logs.get(i)).doesNotContain(" | submitter=");
}
}
@Test
public void do_not_display_submitter_param_in_log_when_submitterLogin_is_not_set_in_case_of_error() throws Exception {
CeTask ceTask = createCeTask(null);
when(queue.peek(anyString())).thenReturn(Optional.of(ceTask));
taskProcessorRepository.setProcessorForTask(ceTask.getType(), taskProcessor);
makeTaskProcessorFail(ceTask);
underTest.call();
verifyWorkerUuid();
List<String> logs = logTester.logs(LoggerLevel.INFO);
assertThat(logs).hasSize(1);
assertThat(logs.get(0)).doesNotContain(" | submitter=");
logs = logTester.logs(LoggerLevel.ERROR);
assertThat(logs).hasSize(2);
for (int i = 0; i < 2; i++) {
assertThat(logs.get(i)).doesNotContain(" | submitter=");
}
assertThat(logTester.logs(LoggerLevel.DEBUG)).isEmpty();
}
@Test
public void display_submitterLogin_in_logs_when_set_in_case_of_success() throws Exception {
when(queue.peek(anyString())).thenReturn(Optional.of(createCeTask("FooBar")));
taskProcessorRepository.setProcessorForTask(CeTaskTypes.REPORT, taskProcessor);
underTest.call();
verifyWorkerUuid();
List<String> logs = logTester.logs(LoggerLevel.INFO);
assertThat(logs).hasSize(2);
assertThat(logs.get(0)).contains(" | submitter=FooBar");
assertThat(logs.get(1)).contains(" | submitter=FooBar | time=");
assertThat(logTester.logs(LoggerLevel.ERROR)).isEmpty();
assertThat(logTester.logs(LoggerLevel.DEBUG)).isEmpty();
}
@Test
public void display_submitterLogin_in_logs_when_set_in_case_of_error() throws Exception {
CeTask ceTask = createCeTask("FooBar");
when(queue.peek(anyString())).thenReturn(Optional.of(ceTask));
taskProcessorRepository.setProcessorForTask(ceTask.getType(), taskProcessor);
makeTaskProcessorFail(ceTask);
underTest.call();
verifyWorkerUuid();
List<String> logs = logTester.logs(LoggerLevel.INFO);
assertThat(logs).hasSize(1);
assertThat(logs.iterator().next()).contains(" | submitter=FooBar");
logs = logTester.logs(LoggerLevel.ERROR);
assertThat(logs).hasSize(2);
assertThat(logs.get(0)).isEqualTo("Failed to execute task " + ceTask.getUuid());
assertThat(logs.get(1)).contains(" | submitter=FooBar | time=");
}
@Test
public void display_start_stop_at_debug_level_for_console_if_DEBUG_is_enabled_and_task_successful() throws Exception {
logTester.setLevel(LoggerLevel.DEBUG);
when(queue.peek(anyString())).thenReturn(Optional.of(createCeTask("FooBar")));
taskProcessorRepository.setProcessorForTask(CeTaskTypes.REPORT, taskProcessor);
underTest.call();
verifyWorkerUuid();
List<String> logs = logTester.logs(LoggerLevel.INFO);
assertThat(logs).hasSize(2);
assertThat(logs.get(0)).contains(" | submitter=FooBar");
assertThat(logs.get(1)).contains(" | submitter=FooBar | time=");
assertThat(logTester.logs(LoggerLevel.ERROR)).isEmpty();
assertThat(logTester.logs(LoggerLevel.DEBUG)).isEmpty();
}
@Test
public void display_start_at_debug_level_stop_at_error_level_for_console_if_DEBUG_is_enabled_and_task_failed() throws Exception {
logTester.setLevel(LoggerLevel.DEBUG);
CeTask ceTask = createCeTask("FooBar");
when(queue.peek(anyString())).thenReturn(Optional.of(ceTask));
taskProcessorRepository.setProcessorForTask(CeTaskTypes.REPORT, taskProcessor);
makeTaskProcessorFail(ceTask);
underTest.call();
verifyWorkerUuid();
List<String> logs = logTester.logs(LoggerLevel.INFO);
assertThat(logs).hasSize(1);
assertThat(logs.iterator().next()).contains(" | submitter=FooBar");
logs = logTester.logs(LoggerLevel.ERROR);
assertThat(logs).hasSize(2);
assertThat(logs.get(0)).isEqualTo("Failed to execute task " + ceTask.getUuid());
assertThat(logs.get(1)).contains(" | submitter=FooBar | time=");
assertThat(logTester.logs(LoggerLevel.DEBUG)).isEmpty();
}
private void verifyWorkerUuid() {
verify(queue).peek(workerUuid.capture());
assertThat(workerUuid.getValue()).startsWith(workerUuid.getValue());
}
private static CeTask createCeTask(@Nullable String submitterLogin) {
return new CeTask.Builder()
.setOrganizationUuid("org1")
.setUuid("TASK_1").setType(CeTaskTypes.REPORT)
.setComponentUuid("PROJECT_1")
.setSubmitterLogin(submitterLogin)
.build();
}
private IllegalStateException makeTaskProcessorFail(CeTask task) {
IllegalStateException error = new IllegalStateException("simulate exception thrown by TaskProcessor#process");
doThrow(error).when(taskProcessor).process(task);
return error;
}
}