SecurityAnalysisToolTest.java
/**
* Copyright (c) 2017, RTE (http://www.rte-france.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
* SPDX-License-Identifier: MPL-2.0
*/
package com.powsybl.security.tools;
import com.google.auto.service.AutoService;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.io.ByteSource;
import com.powsybl.commons.PowsyblException;
import com.powsybl.commons.io.table.TableFormatterConfig;
import com.powsybl.computation.ComputationException;
import com.powsybl.computation.ComputationExceptionBuilder;
import com.powsybl.computation.ComputationManager;
import com.powsybl.contingency.ContingenciesProvider;
import com.powsybl.iidm.network.ImportersLoaderList;
import com.powsybl.iidm.network.Network;
import com.powsybl.security.*;
import com.powsybl.security.distributed.ExternalSecurityAnalysisConfig;
import com.powsybl.security.execution.SecurityAnalysisExecutionBuilder;
import com.powsybl.security.execution.SecurityAnalysisExecutionInput;
import com.powsybl.security.preprocessor.SecurityAnalysisPreprocessor;
import com.powsybl.security.preprocessor.SecurityAnalysisPreprocessorFactory;
import com.powsybl.security.results.PreContingencyResult;
import com.powsybl.tools.test.AbstractToolTest;
import com.powsybl.tools.Tool;
import com.powsybl.tools.ToolOptions;
import com.powsybl.tools.ToolRunningContext;
import org.apache.commons.cli.CommandLine;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
/**
* @author Mathieu Bague {@literal <mathieu.bague at rte-france.com>}
*/
class SecurityAnalysisToolTest extends AbstractToolTest {
private static final String OUTPUT_LOG_FILENAME = "out.zip";
private SecurityAnalysisTool tool;
@Override
@BeforeEach
public void setUp() throws Exception {
super.setUp();
tool = new SecurityAnalysisTool();
Files.createFile(fileSystem.getPath("network.xml"));
}
@Override
protected Iterable<Tool> getTools() {
return Collections.singleton(tool);
}
@Override
public void assertCommand() {
assertCommand(tool.getCommand(), "security-analysis", 14, 1);
assertOption(tool.getCommand().getOptions(), "case-file", true, true);
assertOption(tool.getCommand().getOptions(), "parameters-file", false, true);
assertOption(tool.getCommand().getOptions(), "limit-types", false, true);
assertOption(tool.getCommand().getOptions(), "output-file", false, true);
assertOption(tool.getCommand().getOptions(), "output-format", false, true);
assertOption(tool.getCommand().getOptions(), "contingencies-file", false, true);
assertOption(tool.getCommand().getOptions(), "with-extensions", false, true);
assertOption(tool.getCommand().getOptions(), "task-count", false, true);
assertOption(tool.getCommand().getOptions(), "task", false, true);
assertOption(tool.getCommand().getOptions(), "external", false, false);
assertOption(tool.getCommand().getOptions(), "log-file", false, true);
assertOption(tool.getCommand().getOptions(), "monitoring-file", false, true);
}
@Test
void test() {
assertCommand();
}
private static CommandLine mockCommandLine(Map<String, String> options, Set<String> flags) {
CommandLine cli = mock(CommandLine.class);
when(cli.hasOption(anyString())).thenReturn(false);
when(cli.getOptionValue(anyString())).thenReturn(null);
options.forEach((k, v) -> {
when(cli.getOptionValue(k)).thenReturn(v);
when(cli.hasOption(k)).thenReturn(true);
});
flags.forEach(f -> when(cli.hasOption(f)).thenReturn(true));
when(cli.getOptionProperties(anyString())).thenReturn(new Properties());
return cli;
}
private ToolOptions emptyOptions() {
return mockOptions(Collections.emptyMap());
}
private ToolOptions mockOptions(Map<String, String> options) {
return mockOptions(options, Collections.emptySet());
}
private ToolOptions mockOptions(Map<String, String> options, Set<String> flags) {
return new ToolOptions(mockCommandLine(options, flags), fileSystem);
}
@Test
void parseInputs() throws IOException {
ToolOptions options = emptyOptions();
SecurityAnalysisExecutionInput input = new SecurityAnalysisExecutionInput();
tool.updateInput(options, input);
assertThat(input.getViolationTypes()).isEmpty();
assertThat(input.getResultExtensions()).isEmpty();
assertThat(input.getContingenciesSource()).isNotPresent();
options = mockOptions(ImmutableMap.of(SecurityAnalysisToolConstants.LIMIT_TYPES_OPTION, "HIGH_VOLTAGE,CURRENT"));
tool.updateInput(options, input);
assertThat(input.getViolationTypes()).containsExactly(LimitViolationType.CURRENT, LimitViolationType.HIGH_VOLTAGE);
options = mockOptions(ImmutableMap.of(SecurityAnalysisToolConstants.WITH_EXTENSIONS_OPTION, "ext1,ext2"));
tool.updateInput(options, input);
assertThat(input.getResultExtensions()).containsExactly("ext1", "ext2");
ToolOptions invalidOptions = mockOptions(ImmutableMap.of(SecurityAnalysisToolConstants.CONTINGENCIES_FILE_OPTION, "contingencies"));
assertThatIllegalArgumentException().isThrownBy(() -> tool.updateInput(invalidOptions, input));
Files.write(fileSystem.getPath("contingencies"), "test".getBytes());
options = mockOptions(ImmutableMap.of(SecurityAnalysisToolConstants.CONTINGENCIES_FILE_OPTION, "contingencies"));
tool.updateInput(options, input);
assertThat(input.getContingenciesSource()).isPresent();
if (input.getContingenciesSource().isPresent()) {
assertEquals("test", new String(input.getContingenciesSource().get().read()));
} else {
fail();
}
}
@Test
void buildPreprocessedInput() {
SecurityAnalysisExecutionInput executionInput = new SecurityAnalysisExecutionInput()
.setNetworkVariant(mock(Network.class), "")
.setParameters(new SecurityAnalysisParameters());
SecurityAnalysisPreprocessor preprocessor = mock(SecurityAnalysisPreprocessor.class);
SecurityAnalysisPreprocessorFactory factory = mock(SecurityAnalysisPreprocessorFactory.class);
when(factory.newPreprocessor(any())).thenReturn(preprocessor);
SecurityAnalysisInput input = SecurityAnalysisTool.buildPreprocessedInput(executionInput, LimitViolationFilter::new, factory);
assertSame(executionInput.getParameters(), input.getParameters());
assertSame(executionInput.getNetworkVariant(), input.getNetworkVariant());
verify(factory, times(0)).newPreprocessor(any());
executionInput.setContingenciesSource(ByteSource.empty());
SecurityAnalysisTool.buildPreprocessedInput(executionInput, LimitViolationFilter::new, factory);
verify(factory, times(1)).newPreprocessor(any());
verify(preprocessor, times(1)).preprocess(any());
}
@Test
void readNetwork() {
try {
ToolRunningContext context = new ToolRunningContext(mock(PrintStream.class), mock(PrintStream.class), fileSystem,
mock(ComputationManager.class), mock(ComputationManager.class));
CommandLine cli = mockCommandLine(ImmutableMap.of("case-file", "network.xml"), Collections.emptySet());
Network network = SecurityAnalysisTool.readNetwork(cli, context, new ImportersLoaderList(new NetworkImporterMock()));
assertNotNull(network);
} catch (Exception e) {
fail(e);
}
}
@Test
void testRunWithLog() throws Exception {
try (ByteArrayOutputStream bout = new ByteArrayOutputStream();
ByteArrayOutputStream berr = new ByteArrayOutputStream();
PrintStream out = new PrintStream(bout);
PrintStream err = new PrintStream(berr);
ComputationManager cm = mock(ComputationManager.class)) {
CommandLine cl = mockCommandLine(ImmutableMap.of("case-file", "network.xml",
SecurityAnalysisToolConstants.OUTPUT_LOG_OPTION, OUTPUT_LOG_FILENAME), ImmutableSet.of("skip-postproc"));
ToolRunningContext context = new ToolRunningContext(out, err, fileSystem, cm, cm);
SecurityAnalysisExecutionBuilder builderRun = new SecurityAnalysisExecutionBuilder(ExternalSecurityAnalysisConfig::new,
"SecurityAnalysisToolProviderMock",
executionInput -> new SecurityAnalysisInput(executionInput.getNetworkVariant()));
// Check runWithLog execution
tool.run(cl, context, builderRun,
new ImportersLoaderList(new NetworkImporterMock()),
TableFormatterConfig::new);
// Check log-file creation
Path logPath = context.getFileSystem().getPath(OUTPUT_LOG_FILENAME);
assertTrue(Files.exists(logPath));
// Need to clean for next test
Files.delete(logPath);
// Check run execution
when(cl.hasOption("log-file")).thenReturn(false);
tool.run(cl, context, builderRun,
new ImportersLoaderList(new NetworkImporterMock()),
TableFormatterConfig::new);
// Check no log-file creation
assertFalse(Files.exists(logPath));
// exception happens
SecurityAnalysisExecutionBuilder builderException = new SecurityAnalysisExecutionBuilder(ExternalSecurityAnalysisConfig::new,
"SecurityAnalysisToolExceptionProviderMock",
executionInput -> new SecurityAnalysisInput(executionInput.getNetworkVariant()));
try {
tool.run(cl, context, builderException,
new ImportersLoaderList(new NetworkImporterMock()),
TableFormatterConfig::new);
fail();
} catch (CompletionException exception) {
assertInstanceOf(ComputationException.class, exception.getCause());
assertEquals("outLog", ((ComputationException) exception.getCause()).getOutLogs().get("out"));
assertEquals("errLog", ((ComputationException) exception.getCause()).getErrLogs().get("err"));
}
}
}
@Test
void testRunWithBuilderCreation() throws Exception {
try (ByteArrayOutputStream bout = new ByteArrayOutputStream();
ByteArrayOutputStream berr = new ByteArrayOutputStream();
PrintStream out = new PrintStream(bout);
PrintStream err = new PrintStream(berr);
ComputationManager cm = mock(ComputationManager.class)) {
CommandLine cl = mockCommandLine(ImmutableMap.of("case-file", "network.xml",
SecurityAnalysisToolConstants.OUTPUT_LOG_OPTION, OUTPUT_LOG_FILENAME), ImmutableSet.of("skip-postproc"));
ToolRunningContext context = new ToolRunningContext(out, err, fileSystem, cm, cm);
PowsyblException e = assertThrows(PowsyblException.class, () -> tool.run(cl, context));
assertTrue(e.getMessage().startsWith("Property ContingenciesProviderFactory is not set"));
}
}
@AutoService(SecurityAnalysisProvider.class)
public static class SecurityAnalysisProviderMock implements SecurityAnalysisProvider {
@Override
public CompletableFuture<SecurityAnalysisReport> run(Network network, String workingVariantId, ContingenciesProvider contingenciesProvider, SecurityAnalysisRunParameters runParameters) {
CompletableFuture<SecurityAnalysisReport> cfSar = mock(CompletableFuture.class);
SecurityAnalysisReport report = mock(SecurityAnalysisReport.class);
when(report.getResult()).thenReturn(mock(SecurityAnalysisResult.class));
when(report.getResult().getPreContingencyResult()).thenReturn(mock(PreContingencyResult.class));
when(report.getResult().getPreContingencyLimitViolationsResult()).thenReturn(mock(LimitViolationsResult.class));
when(report.getLogBytes()).thenReturn(Optional.of("Hello world".getBytes()));
when(cfSar.join()).thenReturn(report);
return cfSar;
}
@Override
public String getName() {
return "SecurityAnalysisToolProviderMock";
}
@Override
public String getVersion() {
return "1.0";
}
}
@AutoService(SecurityAnalysisProvider.class)
public static class SecurityAnalysisExceptionProviderMock implements SecurityAnalysisProvider {
@Override
public CompletableFuture<SecurityAnalysisReport> run(Network network, String workingVariantId, ContingenciesProvider contingenciesProvider, SecurityAnalysisRunParameters runParameters) {
ComputationExceptionBuilder ceb = new ComputationExceptionBuilder(new RuntimeException("test"));
ceb.addOutLog("out", "outLog")
.addErrLog("err", "errLog");
ComputationException computationException = ceb.build();
throw new CompletionException(computationException);
}
@Override
public String getName() {
return "SecurityAnalysisToolExceptionProviderMock";
}
@Override
public String getVersion() {
return "1.0";
}
}
}