package uk.ac.ox.zoo.seeg.abraid.mp.modelwrapper.model;
import net.lingala.zip4j.exception.ZipException;
import org.apache.commons.io.FileUtils;
import org.apache.log4j.Logger;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import uk.ac.ox.zoo.seeg.abraid.mp.common.domain.ModelRunStatus;
import uk.ac.ox.zoo.seeg.abraid.mp.common.dto.json.AbraidJsonObjectMapper;
import uk.ac.ox.zoo.seeg.abraid.mp.common.web.ModelOutputConstants;
import uk.ac.ox.zoo.seeg.abraid.mp.testutils.GeneralTestUtils;
import java.io.File;
import java.io.IOException;
import java.util.*;
import static ch.lambdaj.Lambda.filter;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.core.IsNot.not;
import static org.mockito.Mockito.*;
import static uk.ac.ox.zoo.seeg.abraid.mp.modelwrapper.ZipFileAssert.assertThatZip;
/**
* Tests for ModelStatusReporterImpl.
* Copyright (c) 2014 University of Oxford
*/
public class ModelStatusReporterTest {
@Rule
public TemporaryFolder testFolder = new TemporaryFolder(); ///CHECKSTYLE:SUPPRESS VisibilityModifier
private static final String TEST_DATA_FOLDER = "ModelWrapper/test/uk/ac/ox/zoo/seeg/abraid/mp/modelwrapper/model/testdata";
private static final File MEAN_PREDICTION_RASTER_TEST_FILE = new File(TEST_DATA_FOLDER, ModelOutputConstants.MEAN_PREDICTION_RASTER_FILENAME);
private static final File PREDICTION_UNCERTAINTY_RASTER_TEST_FILE = new File(TEST_DATA_FOLDER, ModelOutputConstants.PREDICTION_UNCERTAINTY_RASTER_FILENAME);
private static final File EXTENT_INPUT_RASTER_TEST_FILE = new File(TEST_DATA_FOLDER, ModelOutputConstants.EXTENT_INPUT_RASTER_FILENAME);
private static final File VALIDATION_STATS_TEST_FILE = new File(TEST_DATA_FOLDER, ModelOutputConstants.VALIDATION_STATISTICS_FILENAME);
private static final File RELATIVE_INFLUENCE_TEST_FILE = new File(TEST_DATA_FOLDER, ModelOutputConstants.RELATIVE_INFLUENCE_FILENAME);
private static final File EFFECT_CURVES_TEST_FILE = new File(TEST_DATA_FOLDER, ModelOutputConstants.EFFECT_CURVES_FILENAME);
private static final List<File> RESULTS_FILES = Arrays.asList(MEAN_PREDICTION_RASTER_TEST_FILE, PREDICTION_UNCERTAINTY_RASTER_TEST_FILE, EXTENT_INPUT_RASTER_TEST_FILE, VALIDATION_STATS_TEST_FILE, RELATIVE_INFLUENCE_TEST_FILE, EFFECT_CURVES_TEST_FILE);
private static final File COMPLETED_METADATA_JSON_TEST_FILE = new File(TEST_DATA_FOLDER + "/completed", ModelOutputConstants.METADATA_JSON_FILENAME);
private static final File FAILED_METADATA_JSON_TEST_FILE = new File(TEST_DATA_FOLDER + "/failed", ModelOutputConstants.METADATA_JSON_FILENAME);
private static final String MODEL_RUN_NAME = "deng_2014-05-13-14-49-14_652cc144-3836-4819-b489-e271212a96ed";
@Test
public void reportSendsCorrectOutputsForCompletedStatus() throws Exception {
// Arrange
File workingDirectory = testFolder.newFolder();
ModelOutputHandlerWebService mockOutputServiceClient = mock(ModelOutputHandlerWebService.class);
ModelStatusReporter target = new ModelStatusReporterImpl(MODEL_RUN_NAME, workingDirectory.toPath(), true, mockOutputServiceClient, new AbraidJsonObjectMapper());
Logger logger = GeneralTestUtils.createMockLogger(target);
String outputText = "test output text";
String errorText = "test error text";
addResultsToWorkspace(RESULTS_FILES, workingDirectory);
List<File> expectedFiles = new ArrayList<>();
expectedFiles.addAll(RESULTS_FILES);
expectedFiles.addAll(Arrays.asList(COMPLETED_METADATA_JSON_TEST_FILE));
// This file will have been deleted by the end of "act" so if we want to check it we need to make a copy at the moment when handleOutputs is called
File handledZip = setupModelOutputServiceHandleOutputsFileRetention(mockOutputServiceClient);
// Act
target.report(ModelRunStatus.COMPLETED, outputText, errorText);
// Assert
assertThatZip(handledZip).hasContentFiles(expectedFiles, testFolder);
verify(logger).info(eq("Successfully sent model outputs to model output handler."));
}
@Test
public void reportDeletesWorkspaceIfCompletedResultsSent() throws Exception {
// Arrange
File workingDirectory = testFolder.newFolder();
ModelOutputHandlerWebService mockOutputServiceClient = mock(ModelOutputHandlerWebService.class);
ModelStatusReporter target = new ModelStatusReporterImpl(MODEL_RUN_NAME, workingDirectory.toPath(), true, mockOutputServiceClient, new AbraidJsonObjectMapper());
String outputText = "test output text";
String errorText = "test error text";
addResultsToWorkspace(RESULTS_FILES, workingDirectory);
// Act
target.report(ModelRunStatus.COMPLETED, outputText, errorText);
// Assert
assertThat(workingDirectory).doesNotExist();
}
@Test
public void reportDoesNotDeleteWorkspaceIfCompletedResultsSentIfSetToRetain() throws Exception {
// Arrange
File workingDirectory = testFolder.newFolder();
ModelOutputHandlerWebService mockOutputServiceClient = mock(ModelOutputHandlerWebService.class);
ModelStatusReporter target = new ModelStatusReporterImpl(MODEL_RUN_NAME, workingDirectory.toPath(), false, mockOutputServiceClient, new AbraidJsonObjectMapper());
String outputText = "test output text";
String errorText = "test error text";
addResultsToWorkspace(RESULTS_FILES, workingDirectory);
// Act
target.report(ModelRunStatus.COMPLETED, outputText, errorText);
// Assert
assertThat(workingDirectory).exists();
}
@Test
public void reportDoesNotDeleteWorkspaceIfNonCompletedResultsSent() throws Exception {
// Arrange
File workingDirectory = testFolder.newFolder();
ModelOutputHandlerWebService mockOutputServiceClient = mock(ModelOutputHandlerWebService.class);
ModelStatusReporter target = new ModelStatusReporterImpl(MODEL_RUN_NAME, workingDirectory.toPath(), true, mockOutputServiceClient, new AbraidJsonObjectMapper());
String outputText = "test output text";
String errorText = "test error text";
addResultsToWorkspace(RESULTS_FILES, workingDirectory);
// Act
target.report(ModelRunStatus.FAILED, outputText, errorText);
// Assert
assertThat(workingDirectory).exists();
}
@Test
public void reportSendsCorrectOutputsForFailedStatus() throws Exception {
// Arrange
File workingDirectory = testFolder.newFolder();
ModelOutputHandlerWebService mockOutputServiceClient = mock(ModelOutputHandlerWebService.class);
ModelStatusReporter target = new ModelStatusReporterImpl(MODEL_RUN_NAME, workingDirectory.toPath(), true, mockOutputServiceClient, new AbraidJsonObjectMapper());
Logger logger = GeneralTestUtils.createMockLogger(target);
String outputText = "test output text";
String errorText = "test error text";
List<File> expectedFiles = Arrays.asList(FAILED_METADATA_JSON_TEST_FILE);
// This file will have been deleted by the end of "act" so if we want to check it we need to make a copy at the moment when handleOutputs is called
File handledZip = setupModelOutputServiceHandleOutputsFileRetention(mockOutputServiceClient);
// Act
target.report(ModelRunStatus.FAILED, outputText, errorText);
// Assert
assertThatZip(handledZip).hasContentFiles(expectedFiles, testFolder);
verify(logger).info(eq("Successfully sent model outputs to model output handler."));
}
@Test
public void reportLogsErrorIfWebServiceReturnsErrorText() throws Exception {
// Arrange
File workingDirectory = testFolder.newFolder();
ModelOutputHandlerWebService mockOutputServiceClient = mock(ModelOutputHandlerWebService.class);
ModelStatusReporter target = new ModelStatusReporterImpl(MODEL_RUN_NAME, workingDirectory.toPath(), true, mockOutputServiceClient, new AbraidJsonObjectMapper());
Logger logger = GeneralTestUtils.createMockLogger(target);
addResultsToWorkspace(RESULTS_FILES, workingDirectory);
String webServiceResponseMessage = "WebService error text";
when(mockOutputServiceClient.handleOutputs(any(File.class))).thenReturn(webServiceResponseMessage);
// Act
target.report(ModelRunStatus.COMPLETED, "", "");
// Assert
verify(logger).error(eq("Error received from model output handler: " + webServiceResponseMessage));
}
@Test
public void reportLogsErrorIfWebServiceThrowsException() throws Exception {
// Arrange
File workingDirectory = testFolder.newFolder();
ModelOutputHandlerWebService mockOutputServiceClient = mock(ModelOutputHandlerWebService.class);
ModelStatusReporter target = new ModelStatusReporterImpl(MODEL_RUN_NAME, workingDirectory.toPath(), true, mockOutputServiceClient, new AbraidJsonObjectMapper());
Logger logger = GeneralTestUtils.createMockLogger(target);
addResultsToWorkspace(RESULTS_FILES, workingDirectory);
String webServiceResponseMessage = "WebService error text";
IOException ioException = new IOException(webServiceResponseMessage);
when(mockOutputServiceClient.handleOutputs(any(File.class))).thenThrow(ioException);
// Act
target.report(ModelRunStatus.COMPLETED, "", "");
// Assert
verify(logger).fatal("Error sending model outputs for handling: " + webServiceResponseMessage, ioException);
}
@Test
public void reportDoesNotDeleteWorkspaceIfExceptionThrown() throws Exception {
// Arrange
File workingDirectory = testFolder.newFolder();
ModelOutputHandlerWebService mockOutputServiceClient = mock(ModelOutputHandlerWebService.class);
ModelStatusReporter target = new ModelStatusReporterImpl(MODEL_RUN_NAME, workingDirectory.toPath(), true, mockOutputServiceClient, new AbraidJsonObjectMapper());
addResultsToWorkspace(RESULTS_FILES, workingDirectory);
String webServiceResponseMessage = "WebService error text";
IOException ioException = new IOException(webServiceResponseMessage);
when(mockOutputServiceClient.handleOutputs(any(File.class))).thenThrow(ioException);
// Act
target.report(ModelRunStatus.COMPLETED, "", "");
// Assert
assertThat(workingDirectory).exists();
}
@Test
public void reportLogsErrorIfWorkingDirectoryDoesNotExist() throws Exception {
// Arrange
File workingDirectory = new File("this path does not exist");
ModelOutputHandlerWebService mockOutputServiceClient = mock(ModelOutputHandlerWebService.class);
ModelStatusReporter target = new ModelStatusReporterImpl(MODEL_RUN_NAME, workingDirectory.toPath(), true, mockOutputServiceClient, new AbraidJsonObjectMapper());
Logger logger = GeneralTestUtils.createMockLogger(target);
// Act
target.report(ModelRunStatus.COMPLETED, "", "");
// Assert
verify(logger).fatal(
startsWith("Error sending model outputs for handling: working directory"),
any(IllegalArgumentException.class));
}
@Test
public void reportLogsErrorIfMeanPredictionRasterDoesNotExist() throws Exception {
reportLogsErrorIfResultFileIsMissing(MEAN_PREDICTION_RASTER_TEST_FILE);
}
@Test
public void reportLogsErrorIfPredictionUncertaintyRasterDoesNotExist() throws Exception {
reportLogsErrorIfResultFileIsMissing(PREDICTION_UNCERTAINTY_RASTER_TEST_FILE);
}
@Test
public void reportLogsErrorIfValidationStatsFileDoesNotExist() throws Exception {
reportLogsErrorIfResultFileIsMissing(VALIDATION_STATS_TEST_FILE);
}
@Test
public void reportLogsErrorIfRelativeInfluenceFileDoesNotExist() throws Exception {
reportLogsErrorIfResultFileIsMissing(RELATIVE_INFLUENCE_TEST_FILE);
}
private void reportLogsErrorIfResultFileIsMissing(File missingFile) throws Exception {
// Arrange
File workingDirectory = testFolder.newFolder();
ModelOutputHandlerWebService mockOutputServiceClient = mock(ModelOutputHandlerWebService.class);
ModelStatusReporter target = new ModelStatusReporterImpl(MODEL_RUN_NAME, workingDirectory.toPath(), true, mockOutputServiceClient, new AbraidJsonObjectMapper());
Logger logger = GeneralTestUtils.createMockLogger(target);
addResultsToWorkspace(filter(not(missingFile), RESULTS_FILES), workingDirectory);
// Act
target.report(ModelRunStatus.COMPLETED, "", "");
// Assert
verify(logger).fatal(
startsWith("Error sending model outputs for handling: File does not exist"),
any(ZipException.class));
}
private void addResultsToWorkspace(List<File> files, File workspace) throws IOException {
// Arrange - copy outputs to test folder
File resultsDir = new File(workspace, "results");
File dataDir = new File(workspace, "data");
resultsDir.mkdir();
dataDir.mkdir();
for (File file : files) {
if (file.getParentFile().getName().equals("results")) {
FileUtils.copyFileToDirectory(file, resultsDir);
} else {
FileUtils.copyFileToDirectory(file, dataDir);
}
}
}
private File setupModelOutputServiceHandleOutputsFileRetention(ModelOutputHandlerWebService mockOutputServiceClient) throws IOException {
final File destination = testFolder.newFile();
when(mockOutputServiceClient.handleOutputs(any(File.class))).thenAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocationOnMock) throws Throwable {
FileUtils.copyFile((File) invocationOnMock.getArguments()[0], destination);
return null;
}
});
return destination;
}
}