diff --git a/wca-integration/src/main/java/eu/itesla_project/wca/WCAImpl.java b/wca-integration/src/main/java/eu/itesla_project/wca/WCAImpl.java index 994961d5..2304052b 100644 --- a/wca-integration/src/main/java/eu/itesla_project/wca/WCAImpl.java +++ b/wca-integration/src/main/java/eu/itesla_project/wca/WCAImpl.java @@ -1,6 +1,6 @@ /** * Copyright (c) 2016, All partners of the iTesla project (http://www.itesla-project.eu/consortium) - * Copyright (c) 2016-2017, RTE (http://www.rte-france.com) + * Copyright (c) 2016-2018, 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/. @@ -174,7 +174,7 @@ public WCAImpl(Network network, ComputationManager computationManager, HistoDbCl .setMinBaseVoltage(config.getVoltageLevelConstraintFilter()) .setCountries(config.getCountryConstraintFilter().isEmpty() ? null : config.getCountryConstraintFilter()); - this.wcaReport = new WCAReportImpl(network.getId()); + this.wcaReport = new WCAReportImpl(network); env = ImmutableMap.of("XPRESS", config.getXpressHome().resolve("bin").toString(), "LD_LIBRARY_PATH", config.getXpressHome().resolve("lib").toString()); @@ -491,10 +491,10 @@ private CompletableFuture createClustersWorkflowTask(Contingency WCAPostContingencyStatus postContingencyStatus = new WCAPostContingencyStatus(contingency.getId(), new WCALoadflowResult(true, null)); - List contingencyStateLimitViolations = violationsFilter.apply(Security.checkLimits(network)); + List contingencyStateLimitViolations = violationsFilter.apply(Security.checkLimits(network), network); if (contingencyStateLimitViolations.size() > 0) { LOGGER.warn("Network {}, contingency {}: constraint violantions found in post contingency state:\n{}", - network.getId(), contingency.getId(), Security.printLimitsViolations(contingencyStateLimitViolations, violationsFilter)); + network.getId(), contingency.getId(), Security.printLimitsViolations(contingencyStateLimitViolations, network, violationsFilter)); postContingencyStatus.setPostContingencyViolationsWithoutUncertainties(contingencyStateLimitViolations); filteredClusters.removeClusters(contingency.getId(), EnumSet.of(WCAClusterNum.ONE), @@ -551,7 +551,7 @@ private CompletableFuture createClustersWorkflowTask(Contingency boolean violationsRemoved = false; boolean actionApplied = false; String comment = null; - List curativeStateLimitViolations = violationsFilter.apply(Security.checkLimits(network)); + List curativeStateLimitViolations = violationsFilter.apply(Security.checkLimits(network), network); if (curativeStateLimitViolations.isEmpty()) { LOGGER.info("Network {}, contingency {}, curative action {} solves violations: adding curative action to list for 'clusters' task", network.getId(), contingency.getId(), curativeActionId); @@ -562,7 +562,7 @@ private CompletableFuture createClustersWorkflowTask(Contingency previousState = curativeStateId; } else { LOGGER.warn("Network {}, contingency {}, curative action {}: violantions found in post curative action state:\n{}", - network.getId(), contingency.getId(), curativeActionId, Security.printLimitsViolations(curativeStateLimitViolations, violationsFilter)); + network.getId(), contingency.getId(), curativeActionId, Security.printLimitsViolations(curativeStateLimitViolations, network, violationsFilter)); comment = "violantions found in post curative action state"; if (!filterCurativeActions) { LOGGER.info("Network {}, contingency {}, curative action {}: adding anyway curative action to list for 'clusters' task (config filterCurativeActions = false)", @@ -640,10 +640,10 @@ private CompletableFuture createClustersWorkflowTask(Contingency ); } else { postContingencyStatus.setPostContingencyWithUncertaintiesLoadflowResult(new WCALoadflowResult(true, null)); - List clustersLimitViolations = violationsFilter.apply(Security.checkLimits(network)); + List clustersLimitViolations = violationsFilter.apply(Security.checkLimits(network), network); if (clustersLimitViolations.size() > 0) { LOGGER.warn("Network {}, contingency {}: constraint violantions found in state with 'clusters' uncertainties:\n{}", - network.getId(), contingency.getId(), Security.printLimitsViolations(clustersLimitViolations, violationsFilter)); + network.getId(), contingency.getId(), Security.printLimitsViolations(clustersLimitViolations, network, violationsFilter)); postContingencyStatus.setPostContingencyViolationsWithUncertainties(clustersLimitViolations); } else { LOGGER.warn("Network {}, contingency {}: no violations found in state with 'clusters' uncertainties", @@ -713,10 +713,10 @@ private CompletableFuture>> createWcaTask(Str }); } else { wcaReport.setBaseStateLoadflowResult(new WCALoadflowResult(true, null)); - List baseStateLimitViolations = violationsFilter.apply(Security.checkLimits(network)); + List baseStateLimitViolations = violationsFilter.apply(Security.checkLimits(network), network); if (baseStateLimitViolations.size() > 0) { LOGGER.warn("Network {}: constraint violantions found in base state:\n{}", - network.getId(), Security.printLimitsViolations(baseStateLimitViolations, violationsFilter)); + network.getId(), Security.printLimitsViolations(baseStateLimitViolations, network, violationsFilter)); wcaReport.setPreContingencyViolationsWithoutUncertainties(baseStateLimitViolations); contingencies.forEach(contingency -> filteredClusters.removeClusters(contingency.getId(), EnumSet.of(WCAClusterNum.ONE, WCAClusterNum.TWO), @@ -776,10 +776,10 @@ private CompletableFuture>> createWcaTask(Str wcaReport.setBaseStateWithUncertaintiesLoadflowResult(new WCALoadflowResult(false, "load flow on state with 'domains' uncertainties diverged: metrics = " + loadFlowResult.getMetrics())); return CompletableFuture.completedFuture(baseStateLimitViolations); } else { - List domainsLimitViolations = violationsFilter.apply(Security.checkLimits(network)); + List domainsLimitViolations = violationsFilter.apply(Security.checkLimits(network), network); if (domainsLimitViolations.size() > 0) { LOGGER.warn("Network {}: constraint violantions found in state with 'domains' uncertainties:\n{}", - network.getId(), Security.printLimitsViolations(domainsLimitViolations, violationsFilter)); + network.getId(), Security.printLimitsViolations(domainsLimitViolations, network, violationsFilter)); wcaReport.setPreContingencyViolationsWithUncertainties(domainsLimitViolations); contingencies.forEach(contingency -> filteredClusters.removeClusters(contingency.getId(), EnumSet.of(WCAClusterNum.ONE, WCAClusterNum.TWO), @@ -798,7 +798,7 @@ private CompletableFuture>> createWcaTask(Str network.getStateManager().setWorkingState(baseStateId); } LOGGER.info("Network {}: {} violations to be prevented:\n{}", - network.getId(), violationsToBePrevented.size(), Security.printLimitsViolations(violationsToBePrevented, violationsFilter)); + network.getId(), violationsToBePrevented.size(), Security.printLimitsViolations(violationsToBePrevented, network, violationsFilter)); List preventiveStateIdsForDomains = Collections.synchronizedList(new ArrayList<>()); List preventiveActionIdsForDomains = Collections.synchronizedList(new ArrayList<>()); Map> possibleActionsToApply = Collections.synchronizedMap(new HashMap>()); @@ -838,7 +838,7 @@ private CompletableFuture>> createWcaTask(Str try { loadFlowResult1 = loadFlow.run(LOAD_FLOW_PARAMETERS); if (loadFlowResult1.isOk()) { - List preventiveStateLimitViolations = violationsFilter.apply(Security.checkLimits(network)); + List preventiveStateLimitViolations = violationsFilter.apply(Security.checkLimits(network), network); Optional notSolvedLimitViolation = preventiveStateLimitViolations .stream() .filter(preventiveStateLimitViolation -> preventiveStateLimitViolation.getSubjectId().equals(violationToBePrevented.getSubjectId())) @@ -862,7 +862,7 @@ private CompletableFuture>> createWcaTask(Str message = "post preventive action state contains new violations"; } LOGGER.warn("Network {}, preventive action {}: {}:\n{}", - network.getId(), preventiveActionId, message, Security.printLimitsViolations(preventiveStateLimitViolations, violationsFilter)); + network.getId(), preventiveActionId, message, Security.printLimitsViolations(preventiveStateLimitViolations, network, violationsFilter)); if (!config.filterPreventiveActions()) { LOGGER.info("Network {}, preventive action {}: adding anyway preventive action to list (config filterPreventiveActions = false)", network.getId(), preventiveActionId); @@ -970,7 +970,7 @@ private CompletableFuture>> createWcaTask(Str .thenAccept(ignored -> { // check that the violations have disappeared? this check has already been done previously // update basecase remaining violations - wcaReport.setBaseStateRemainingViolations(violationsFilter.apply(Security.checkLimits(network))); + wcaReport.setBaseStateRemainingViolations(violationsFilter.apply(Security.checkLimits(network), network)); }) .exceptionally(throwable -> { if (throwable != null) { @@ -987,7 +987,7 @@ private CompletableFuture>> createWcaTask(Str wcaReport.setPostPreventiveActionsViolationsWithUncertainties(wcaReport.getBaseStateRemainingViolations()); if (!wcaReport.getBaseStateRemainingViolations().isEmpty()) { LOGGER.warn("Network {}: loosening the basecase constraints for remaining violations:\n{}", - network.getId(), Security.printLimitsViolations(new ArrayList<>(wcaReport.getBaseStateRemainingViolations()), violationsFilter)); + network.getId(), Security.printLimitsViolations(new ArrayList<>(wcaReport.getBaseStateRemainingViolations()), network, violationsFilter)); ConstraintsModifierConfig constraintsModifierConfig = new ConstraintsModifierConfig( config.getCountryConstraintFilter(), config.ignoreVoltageConstraints() ? EnumSet.of(LimitViolationType.CURRENT) : EnumSet.allOf(LimitViolationType.class) diff --git a/wca-integration/src/main/java/eu/itesla_project/wca/report/WCAReportImpl.java b/wca-integration/src/main/java/eu/itesla_project/wca/report/WCAReportImpl.java index 15b98760..3a6f86b4 100644 --- a/wca-integration/src/main/java/eu/itesla_project/wca/report/WCAReportImpl.java +++ b/wca-integration/src/main/java/eu/itesla_project/wca/report/WCAReportImpl.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2017, RTE (http://www.rte-france.com) + * Copyright (c) 2017-2018, 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/. @@ -26,12 +26,15 @@ import com.powsybl.commons.io.table.CsvTableFormatterFactory; import com.powsybl.commons.io.table.TableFormatter; import com.powsybl.commons.io.table.TableFormatterConfig; +import com.powsybl.iidm.network.Network; +import com.powsybl.security.LimitViolation; +import com.powsybl.security.LimitViolationHelper; + import eu.itesla_project.modules.wca.report.WCAActionApplication; import eu.itesla_project.modules.wca.report.WCALoadflowResult; import eu.itesla_project.modules.wca.report.WCAPostContingencyStatus; import eu.itesla_project.modules.wca.report.WCAReport; import eu.itesla_project.modules.wca.report.WCASecurityRuleApplication; -import com.powsybl.security.LimitViolation; /** * @@ -59,7 +62,7 @@ public class WCAReportImpl implements WCAReport { private static TableFormatterConfig TABLE_FORMATTER_CONFIG = TableFormatterConfig.load(); private static String LOADFLOW_STEP = "Loadflow"; - private final String basecase; + private final Network basecase; private WCALoadflowResult baseStateLoadflowResult; private List preContingencyViolationsWithoutUncertainties = Collections.synchronizedList(new ArrayList<>()); private WCALoadflowResult baseStateWithUncertaintiesLoadflowResult; @@ -70,13 +73,13 @@ public class WCAReportImpl implements WCAReport { private List securityRulesApplication = Collections.synchronizedList(new ArrayList<>()); private List postContingenciesStatus = Collections.synchronizedList(new ArrayList<>()); - public WCAReportImpl(String basecase) { + public WCAReportImpl(Network basecase) { this.basecase = Objects.requireNonNull(basecase); } @Override public String getBasecase() { - return basecase; + return basecase.getId(); } @Override @@ -197,7 +200,7 @@ private void writeViolations(TableFormatter formatter, String contingencyId, WCA List violations) { if (loadflowResult != null && !loadflowResult.loadflowConverged()) { try { - formatter.writeCell(basecase); + formatter.writeCell(basecase.getId()); if (contingencyId != null) { formatter.writeCell(contingencyId); } @@ -215,7 +218,7 @@ private void writeViolations(TableFormatter formatter, String contingencyId, WCA } else if (!violations.isEmpty()) { violations.forEach(violation -> { try { - formatter.writeCell(basecase); + formatter.writeCell(basecase.getId()); if (contingencyId != null) { formatter.writeCell(contingencyId); } @@ -225,8 +228,13 @@ private void writeViolations(TableFormatter formatter, String contingencyId, WCA .writeCell(violation.getSubjectId()) .writeCell(violation.getValue()) .writeCell(violation.getLimit()) - .writeCell(violation.getCountry().name()) - .writeCell(violation.getBaseVoltage()); + .writeCell(LimitViolationHelper.getCountry(violation, basecase).name()) + .writeCell(LimitViolationHelper.getNominalVoltage(violation, basecase)); + if (violation.getSide() != null) { + formatter.writeCell(violation.getSide().name()); + } else { + formatter.writeEmptyCell(); + } } catch (IOException e) { throw new RuntimeException(e); } @@ -246,7 +254,8 @@ private void exportViolations(Path folder, String file, String title, WCALoadflo new Column("Value"), new Column("Limit"), new Column("Country"), - new Column("BaseVoltage") + new Column("BaseVoltage"), + new Column("Side"), }; CsvTableFormatterFactory factory = new CsvTableFormatterFactory(); try (Writer writer = Files.newBufferedWriter(violationsPath, StandardCharsets.UTF_8); @@ -254,6 +263,7 @@ private void exportViolations(Path folder, String file, String title, WCALoadflo writeViolations(formatter, null, loadflowResult, violations); } } + private void exportPreContingencyViolationsWithoutUncertainties(Path folder) throws IOException { LOGGER.info("Exporting pre-contingency violations without uncertainties report of basecase {} to file {}", basecase, folder + File.separator + PRE_CONTINGENCY_VIOLATIONS_WITHOUT_UNCERTAINTIES_FILE); @@ -279,7 +289,7 @@ private void writeActionsApplications(TableFormatter formatter, String contingen if (!actionsApplication.isEmpty()) { actionsApplication.forEach(actionApplication -> { try { - formatter.writeCell(basecase); + formatter.writeCell(basecase.getId()); if (contingencyId != null) { formatter.writeCell(contingencyId); } @@ -363,7 +373,7 @@ private void exportSecurityRulesApplication(Path folder) throws IOException { if (!securityRulesApplication.isEmpty()) { securityRulesApplication.forEach(ruleApplication -> { try { - formatter.writeCell(basecase) + formatter.writeCell(basecase.getId()) .writeCell(ruleApplication.getContingencyId()); if (ruleApplication.getSecurityRule() != null) { formatter.writeCell(ruleApplication.getSecurityRule().getId().toString()) @@ -404,7 +414,8 @@ private void exportPostContingencyViolations(Path folder) throws IOException { new Column("Value"), new Column("Limit"), new Column("Country"), - new Column("BaseVoltage") + new Column("BaseVoltage"), + new Column("Side") }; CsvTableFormatterFactory factory = new CsvTableFormatterFactory(); try (Writer writer1 = Files.newBufferedWriter(violationsPath1, StandardCharsets.UTF_8); diff --git a/wca-integration/src/test/java/eu/itesla_project/wca/SimpleContingencyDbFacadeTest.java b/wca-integration/src/test/java/eu/itesla_project/wca/SimpleContingencyDbFacadeTest.java index 7e361940..0dbc22c3 100644 --- a/wca-integration/src/test/java/eu/itesla_project/wca/SimpleContingencyDbFacadeTest.java +++ b/wca-integration/src/test/java/eu/itesla_project/wca/SimpleContingencyDbFacadeTest.java @@ -14,6 +14,7 @@ import java.util.List; import java.util.Map; +import com.powsybl.iidm.network.*; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mockito; @@ -24,11 +25,6 @@ import com.powsybl.contingency.Contingency; import com.powsybl.contingency.ContingencyImpl; import com.powsybl.contingency.BranchContingency; -import com.powsybl.iidm.network.Country; -import com.powsybl.iidm.network.Line; -import com.powsybl.iidm.network.Network; -import com.powsybl.iidm.network.NetworkFactory; -import com.powsybl.iidm.network.TopologyKind; import eu.itesla_project.modules.contingencies.Action; import eu.itesla_project.modules.contingencies.ActionPlan; import eu.itesla_project.modules.contingencies.ActionPlanOption; @@ -56,6 +52,10 @@ @RunWith(MockitoJUnitRunner.class) public class SimpleContingencyDbFacadeTest { + private static LimitViolation createMockLimitViolation(String lineId) { + return new LimitViolation(lineId, LimitViolationType.CURRENT,null, 0, Float.NaN, Float.NaN, Float.NaN, Branch.Side.ONE); + } + @Test public void testGetContingencies() throws Exception { @@ -191,7 +191,7 @@ public void testGetCurativeActionsWithViolations() throws Exception { .setB2(0) .add(); // limit violation - LimitViolation mockLimitViolation = new LimitViolation(mockLineId, LimitViolationType.CURRENT, Float.NaN, null, Float.NaN); + LimitViolation mockLimitViolation = createMockLimitViolation(mockLineId); List mockLimitViolations = Arrays.asList(mockLimitViolation); // contingency String mockContingencyId = "mockContingency"; @@ -268,7 +268,7 @@ public void testGetPreventiveActions() throws Exception { .setB2(0) .add(); // limit violation - LimitViolation mockLimitViolation = new LimitViolation(mockLineId, LimitViolationType.CURRENT, Float.NaN, null, Float.NaN); + LimitViolation mockLimitViolation = createMockLimitViolation(mockLineId); // action String mockActionId = "mockAction"; Action mockAction = new ActionImpl(mockActionId, true, true, new SwitchClosingAction("mockVoltageLevel", "mockSwitch")); diff --git a/wca-integration/src/test/java/eu/itesla_project/wca/WCAReportImplTest.java b/wca-integration/src/test/java/eu/itesla_project/wca/WCAReportImplTest.java index 5165972e..40607754 100644 --- a/wca-integration/src/test/java/eu/itesla_project/wca/WCAReportImplTest.java +++ b/wca-integration/src/test/java/eu/itesla_project/wca/WCAReportImplTest.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2017, RTE (http://www.rte-france.com) + * Copyright (c) 2017-2018, 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/. @@ -27,8 +27,17 @@ import com.google.common.io.CharStreams; import com.google.common.jimfs.Configuration; import com.google.common.jimfs.Jimfs; - +import com.powsybl.iidm.network.Branch; import com.powsybl.iidm.network.Country; +import com.powsybl.iidm.network.Network; +import com.powsybl.iidm.network.Substation; +import com.powsybl.iidm.network.Terminal; +import com.powsybl.iidm.network.VoltageLevel; +import com.powsybl.security.LimitViolation; +import com.powsybl.security.LimitViolationType; +import com.powsybl.simulation.securityindexes.SecurityIndexId; +import com.powsybl.simulation.securityindexes.SecurityIndexType; + import eu.itesla_project.modules.rules.RuleAttributeSet; import eu.itesla_project.modules.rules.RuleId; import eu.itesla_project.modules.rules.SecurityRule; @@ -37,10 +46,6 @@ import eu.itesla_project.modules.wca.report.WCAPostContingencyStatus; import eu.itesla_project.modules.wca.report.WCARuleViolationType; import eu.itesla_project.modules.wca.report.WCASecurityRuleApplication; -import com.powsybl.security.LimitViolation; -import com.powsybl.security.LimitViolationType; -import com.powsybl.simulation.securityindexes.SecurityIndexId; -import com.powsybl.simulation.securityindexes.SecurityIndexType; import eu.itesla_project.wca.report.WCAReportImpl; /** @@ -48,17 +53,47 @@ * @author Massimo Ferraro */ public class WCAReportImplTest { - + private FileSystem fileSystem; private LimitViolation line1Violation; private LimitViolation line2Violation; - + private Network network; + private String networkId = "network1"; + private String line1Id = "line1"; + private String line2Id = "line2"; + @Before public void setUp() throws Exception { fileSystem = Jimfs.newFileSystem(Configuration.unix()); - line1Violation = new LimitViolation("line1", LimitViolationType.CURRENT, 1000f, "20'", 1, 1100f, Country.FR, 380f); - line2Violation = new LimitViolation("line2", LimitViolationType.CURRENT, 900f, "10'", 1, 950f, Country.FR, 380f); + line1Violation = new LimitViolation(line1Id, LimitViolationType.CURRENT, "20'", 20*60, 1000f, 1f, 1100f, Branch.Side.ONE); + line2Violation = new LimitViolation(line2Id, LimitViolationType.CURRENT, "10'", 10*60, 900f, 1f, 950f, Branch.Side.ONE); + + Substation substation = Mockito.mock(Substation.class); + Mockito.when(substation.getCountry()).thenReturn(Country.FR); + + VoltageLevel voltageLevel = Mockito.mock(VoltageLevel.class); + Mockito.when(voltageLevel.getSubstation()).thenReturn(substation); + Mockito.when(voltageLevel.getNominalV()).thenReturn(380f); + + Terminal line1Terminal = Mockito.mock(Terminal.class); + Mockito.when(line1Terminal.getVoltageLevel()).thenReturn(voltageLevel); + + Branch line1 = Mockito.mock(Branch.class); + Mockito.when(line1.getId()).thenReturn(line1Id); + Mockito.when(line1.getTerminal(Branch.Side.ONE)).thenReturn(line1Terminal); + + Terminal line2Terminal = Mockito.mock(Terminal.class); + Mockito.when(line2Terminal.getVoltageLevel()).thenReturn(voltageLevel); + + Branch line2 = Mockito.mock(Branch.class); + Mockito.when(line2.getId()).thenReturn(line2Id); + Mockito.when(line2.getTerminal(Branch.Side.ONE)).thenReturn(line2Terminal); + + network = Mockito.mock(Network.class); + Mockito.when(network.getId()).thenReturn(networkId); + Mockito.when(network.getIdentifiable(line1Id)).thenReturn(line1); + Mockito.when(network.getIdentifiable(line2Id)).thenReturn(line2); } @After @@ -69,84 +104,84 @@ public void tearDown() throws Exception { @Test public void testExportPreContigencyViolationsWithoutUncertaintiesLoadflowDivergence() throws IOException { Path folder = Files.createDirectory(fileSystem.getPath("/export-folder")); - - WCAReportImpl wcaReport = new WCAReportImpl("network1"); + + WCAReportImpl wcaReport = new WCAReportImpl(network); wcaReport.setBaseStateLoadflowResult(new WCALoadflowResult(false, "base state loadflow diverged")); wcaReport.exportCsv(folder); - + Path report = folder.resolve(WCAReportImpl.PRE_CONTINGENCY_VIOLATIONS_WITHOUT_UNCERTAINTIES_FILE); assertTrue(Files.exists(report)); String reportContent = String.join(System.lineSeparator(), WCAReportImpl.PRE_CONTINGENCY_VIOLATIONS_WITHOUT_UNCERTAINTIES_TITLE, - "Basecase;FailureStep;FailureDescription;ViolationType;Equipment;Value;Limit;Country;BaseVoltage", - "network1;Loadflow;base state loadflow diverged;;;;;;"); + "Basecase;FailureStep;FailureDescription;ViolationType;Equipment;Value;Limit;Country;BaseVoltage;Side", + networkId + ";Loadflow;base state loadflow diverged;;;;;;;"); assertEquals(reportContent, CharStreams.toString(new InputStreamReader(Files.newInputStream(report))).trim()); } @Test public void testExportPreContigencyViolationsWithoutUncertainties() throws IOException { Path folder = Files.createDirectory(fileSystem.getPath("/export-folder")); - - WCAReportImpl wcaReport = new WCAReportImpl("network1"); + + WCAReportImpl wcaReport = new WCAReportImpl(network); wcaReport.setBaseStateLoadflowResult(new WCALoadflowResult(true, null)); wcaReport.setPreContingencyViolationsWithoutUncertainties(Arrays.asList(line1Violation, line2Violation)); wcaReport.exportCsv(folder); - + Path report = folder.resolve(WCAReportImpl.PRE_CONTINGENCY_VIOLATIONS_WITHOUT_UNCERTAINTIES_FILE); assertTrue(Files.exists(report)); String reportContent = String.join(System.lineSeparator(), WCAReportImpl.PRE_CONTINGENCY_VIOLATIONS_WITHOUT_UNCERTAINTIES_TITLE, - "Basecase;FailureStep;FailureDescription;ViolationType;Equipment;Value;Limit;Country;BaseVoltage", - "network1;;;CURRENT;line1;" + String.format(Locale.getDefault(),"%g",1100f) + ";" + String.format(Locale.getDefault(),"%g",1000f) - + ";FR" + ";" + String.format(Locale.getDefault(),"%g",380f), - "network1;;;CURRENT;line2;" + String.format(Locale.getDefault(),"%g",950f) + ";" + String.format(Locale.getDefault(),"%g",900f) - + ";FR" + ";" + String.format(Locale.getDefault(),"%g",380f)); + "Basecase;FailureStep;FailureDescription;ViolationType;Equipment;Value;Limit;Country;BaseVoltage;Side", + networkId + ";;;CURRENT;" + line1Id + ";" + String.format(Locale.getDefault(),"%g",1100f) + ";" + String.format(Locale.getDefault(),"%g",1000f) + + ";FR" + ";" + String.format(Locale.getDefault(),"%g",380f) + ";ONE", + networkId + ";;;CURRENT;" + line2Id + ";" + String.format(Locale.getDefault(),"%g",950f) + ";" + String.format(Locale.getDefault(),"%g",900f) + + ";FR" + ";" + String.format(Locale.getDefault(),"%g",380f) + ";ONE"); assertEquals(reportContent, CharStreams.toString(new InputStreamReader(Files.newInputStream(report))).trim()); } - + @Test public void testExportPreContigencyViolationsWithUncertaintiesLoadflowDivergence() throws IOException { Path folder = Files.createDirectory(fileSystem.getPath("/export-folder")); - - WCAReportImpl wcaReport = new WCAReportImpl("network1"); + + WCAReportImpl wcaReport = new WCAReportImpl(network); wcaReport.setBaseStateWithUncertaintiesLoadflowResult(new WCALoadflowResult(false, "base state with uncertainties loadflow diverged")); wcaReport.exportCsv(folder); - + Path report = folder.resolve(WCAReportImpl.PRE_CONTINGENCY_VIOLATIONS_WITH_UNCERTAINTIES_FILE); assertTrue(Files.exists(report)); String reportContent = String.join(System.lineSeparator(), WCAReportImpl.PRE_CONTINGENCY_VIOLATIONS_WITH_UNCERTAINTIES_TITLE, - "Basecase;FailureStep;FailureDescription;ViolationType;Equipment;Value;Limit;Country;BaseVoltage", - "network1;Loadflow;base state with uncertainties loadflow diverged;;;;;;"); + "Basecase;FailureStep;FailureDescription;ViolationType;Equipment;Value;Limit;Country;BaseVoltage;Side", + networkId + ";Loadflow;base state with uncertainties loadflow diverged;;;;;;;"); assertEquals(reportContent, CharStreams.toString(new InputStreamReader(Files.newInputStream(report))).trim()); } - + @Test public void testExportPreContigencyViolationsWithUncertainties() throws IOException { Path folder = Files.createDirectory(fileSystem.getPath("/export-folder")); - - WCAReportImpl wcaReport = new WCAReportImpl("network1"); + + WCAReportImpl wcaReport = new WCAReportImpl(network); wcaReport.setBaseStateLoadflowResult(new WCALoadflowResult(true, null)); wcaReport.setPreContingencyViolationsWithUncertainties(Arrays.asList(line1Violation, line2Violation)); wcaReport.exportCsv(folder); - + Path report = folder.resolve(WCAReportImpl.PRE_CONTINGENCY_VIOLATIONS_WITH_UNCERTAINTIES_FILE); assertTrue(Files.exists(report)); String reportContent = String.join(System.lineSeparator(), WCAReportImpl.PRE_CONTINGENCY_VIOLATIONS_WITH_UNCERTAINTIES_TITLE, - "Basecase;FailureStep;FailureDescription;ViolationType;Equipment;Value;Limit;Country;BaseVoltage", - "network1;;;CURRENT;line1;" + String.format(Locale.getDefault(),"%g",1100f) + ";" + String.format(Locale.getDefault(),"%g",1000f) - + ";FR" + ";" + String.format(Locale.getDefault(),"%g",380f), - "network1;;;CURRENT;line2;" + String.format(Locale.getDefault(),"%g",950f) + ";" + String.format(Locale.getDefault(),"%g",900f) - + ";FR" + ";" + String.format(Locale.getDefault(),"%g",380f)); + "Basecase;FailureStep;FailureDescription;ViolationType;Equipment;Value;Limit;Country;BaseVoltage;Side", + networkId + ";;;CURRENT;" + line1Id + ";" + String.format(Locale.getDefault(),"%g",1100f) + ";" + String.format(Locale.getDefault(),"%g",1000f) + + ";FR" + ";" + String.format(Locale.getDefault(),"%g",380f) + ";ONE", + networkId + ";;;CURRENT;" + line2Id + ";" + String.format(Locale.getDefault(),"%g",950f) + ";" + String.format(Locale.getDefault(),"%g",900f) + + ";FR" + ";" + String.format(Locale.getDefault(),"%g",380f) + ";ONE"); assertEquals(reportContent, CharStreams.toString(new InputStreamReader(Files.newInputStream(report))).trim()); } - + @Test public void testExportPreventiveActionsApplication() throws IOException { Path folder = Files.createDirectory(fileSystem.getPath("/export-folder")); - - WCAReportImpl wcaReport = new WCAReportImpl("network1"); + + WCAReportImpl wcaReport = new WCAReportImpl(network); wcaReport.addPreventiveActionApplication(new WCAActionApplication("action1", line1Violation, new WCALoadflowResult(true, null), @@ -166,44 +201,44 @@ public void testExportPreventiveActionsApplication() throws IOException { false, null)); wcaReport.exportCsv(folder); - + Path report = folder.resolve(WCAReportImpl.POST_PREVENTIVE_ACTIONS_FILE); assertTrue(Files.exists(report)); String reportContent = String.join(System.lineSeparator(), WCAReportImpl.POST_PREVENTIVE_ACTIONS_TITLE, "Basecase;ActionId;ViolatedEquipment;ViolationType;FailureStep;FailureDescription;ViolationRemoved;ActionApplied;Comment", - "network1;action1;line1;CURRENT;;;false;false;post action state contains new violations", - "network1;action2;line1;CURRENT;;;true;true;", - "network1;action3;line2;CURRENT;Loadflow;loadflow on post action state diverged;false;false;"); + networkId + ";action1;" + line1Id + ";CURRENT;;;false;false;post action state contains new violations", + networkId + ";action2;" + line1Id + ";CURRENT;;;true;true;", + networkId + ";action3;" + line2Id + ";CURRENT;Loadflow;loadflow on post action state diverged;false;false;"); assertEquals(reportContent, CharStreams.toString(new InputStreamReader(Files.newInputStream(report))).trim()); } - + @Test public void testExportPostPreventiveActionsViolationsWithUncertainties() throws IOException { Path folder = Files.createDirectory(fileSystem.getPath("/export-folder")); - - WCAReportImpl wcaReport = new WCAReportImpl("network1"); + + WCAReportImpl wcaReport = new WCAReportImpl(network); wcaReport.setBaseStateLoadflowResult(new WCALoadflowResult(true, null)); wcaReport.setPostPreventiveActionsViolationsWithUncertainties(Arrays.asList(line1Violation, line2Violation)); wcaReport.exportCsv(folder); - + Path report = folder.resolve(WCAReportImpl.POST_PREVENTIVE_ACTIONS_VIOLATIONS_WITH_UNCERTAINTIES_FILE); assertTrue(Files.exists(report)); String reportContent = String.join(System.lineSeparator(), WCAReportImpl.POST_PREVENTIVE_ACTIONS_VIOLATIONS_WITH_UNCERTAINTIES_TITLE, - "Basecase;FailureStep;FailureDescription;ViolationType;Equipment;Value;Limit;Country;BaseVoltage", - "network1;;;CURRENT;line1;" + String.format(Locale.getDefault(),"%g",1100f) + ";" + String.format(Locale.getDefault(),"%g",1000f) - + ";FR" + ";" + String.format(Locale.getDefault(),"%g",380f), - "network1;;;CURRENT;line2;" + String.format(Locale.getDefault(),"%g",950f) + ";" + String.format(Locale.getDefault(),"%g",900f) - + ";FR" + ";" + String.format(Locale.getDefault(),"%g",380f)); + "Basecase;FailureStep;FailureDescription;ViolationType;Equipment;Value;Limit;Country;BaseVoltage;Side", + networkId + ";;;CURRENT;" + line1Id + ";" + String.format(Locale.getDefault(),"%g",1100f) + ";" + String.format(Locale.getDefault(),"%g",1000f) + + ";FR" + ";" + String.format(Locale.getDefault(),"%g",380f) + ";ONE", + networkId + ";;;CURRENT;" + line2Id + ";" + String.format(Locale.getDefault(),"%g",950f) + ";" + String.format(Locale.getDefault(),"%g",900f) + + ";FR" + ";" + String.format(Locale.getDefault(),"%g",380f) + ";ONE"); assertEquals(reportContent, CharStreams.toString(new InputStreamReader(Files.newInputStream(report))).trim()); } @Test public void testExportSecurityRulesApplication() throws IOException { Path folder = Files.createDirectory(fileSystem.getPath("/export-folder")); - - WCAReportImpl wcaReport = new WCAReportImpl("network1"); + + WCAReportImpl wcaReport = new WCAReportImpl(network); SecurityRule rule1 = Mockito.mock(SecurityRule.class); Mockito.when(rule1.getId()).thenReturn(new RuleId(RuleAttributeSet.WORST_CASE, new SecurityIndexId("fault1", SecurityIndexType.TSO_OVERLOAD))); Mockito.when(rule1.getWorkflowId()).thenReturn("workflow-0"); @@ -227,41 +262,41 @@ public void testExportSecurityRulesApplication() throws IOException { WCARuleViolationType.MISSING_RULE, missingRuleMessage)); wcaReport.exportCsv(folder); - + Path report = folder.resolve(WCAReportImpl.SECURITY_RULES_VIOLATIONS_WITHOUT_UNCERTAINTIES_FILE); assertTrue(Files.exists(report)); String reportContent = String.join(System.lineSeparator(), WCAReportImpl.SECURITY_RULES_VIOLATIONS_WITHOUT_UNCERTAINTIES_TITLE, "Basecase;ContingencyId;SecurityRule;WorkflowId;RuleViolated;ViolationType;Cause", - "network1;fault1;"+rule1.getId().toString()+";workflow-0;true;MISSING_ATTRIBUTE;Missing attributes for rule " + rule1.getId()+ ": attribute1", - "network1;fault1;"+rule2.getId().toString()+";workflow-0;false;NO_VIOLATION;Rule " + rule2.getId() + " verified", - "network1;fault2;;;true;MISSING_RULE;"+ missingRuleMessage); + networkId + ";fault1;"+rule1.getId().toString()+";workflow-0;true;MISSING_ATTRIBUTE;Missing attributes for rule " + rule1.getId()+ ": attribute1", + networkId + ";fault1;"+rule2.getId().toString()+";workflow-0;false;NO_VIOLATION;Rule " + rule2.getId() + " verified", + networkId + ";fault2;;;true;MISSING_RULE;"+ missingRuleMessage); assertEquals(reportContent, CharStreams.toString(new InputStreamReader(Files.newInputStream(report))).trim()); } - + @Test public void testExportPostContigencyViolationsWithoutUncertaintiesLoadflowDivergence() throws IOException { Path folder = Files.createDirectory(fileSystem.getPath("/export-folder")); - - WCAReportImpl wcaReport = new WCAReportImpl("network1"); + + WCAReportImpl wcaReport = new WCAReportImpl(network); WCAPostContingencyStatus postContingencyStatus = new WCAPostContingencyStatus("fault1", new WCALoadflowResult(false, "post contingency loadflow diverged")); wcaReport.addPostContingencyStatus(postContingencyStatus); wcaReport.exportCsv(folder); - + Path report = folder.resolve(WCAReportImpl.POST_CONTINGENCY_VIOLATIONS_WITHOUT_UNCERTAINTIES_FILE); assertTrue(Files.exists(report)); String reportContent = String.join(System.lineSeparator(), WCAReportImpl.POST_CONTINGENCY_VIOLATIONS_WITHOUT_UNCERTAINTIES_TITLE, - "Basecase;Contingency;FailureStep;FailureDescription;ViolationType;Equipment;Value;Limit;Country;BaseVoltage", - "network1;fault1;Loadflow;post contingency loadflow diverged;;;;;;"); + "Basecase;Contingency;FailureStep;FailureDescription;ViolationType;Equipment;Value;Limit;Country;BaseVoltage;Side", + networkId + ";fault1;Loadflow;post contingency loadflow diverged;;;;;;;"); assertEquals(reportContent, CharStreams.toString(new InputStreamReader(Files.newInputStream(report))).trim()); } - + @Test public void testExportPostContigencyViolationsWithoutUncertainties() throws IOException { Path folder = Files.createDirectory(fileSystem.getPath("/export-folder")); - - WCAReportImpl wcaReport = new WCAReportImpl("network1"); + + WCAReportImpl wcaReport = new WCAReportImpl(network); WCAPostContingencyStatus postContingencyStatus1 = new WCAPostContingencyStatus("fault1", new WCALoadflowResult(true, null)); postContingencyStatus1.setPostContingencyViolationsWithoutUncertainties(Collections.singletonList(line1Violation)); wcaReport.addPostContingencyStatus(postContingencyStatus1); @@ -269,43 +304,43 @@ public void testExportPostContigencyViolationsWithoutUncertainties() throws IOEx postContingencyStatus2.setPostContingencyViolationsWithoutUncertainties(Collections.singletonList(line2Violation)); wcaReport.addPostContingencyStatus(postContingencyStatus2); wcaReport.exportCsv(folder); - + Path report = folder.resolve(WCAReportImpl.POST_CONTINGENCY_VIOLATIONS_WITHOUT_UNCERTAINTIES_FILE); assertTrue(Files.exists(report)); String reportContent = String.join(System.lineSeparator(), WCAReportImpl.POST_CONTINGENCY_VIOLATIONS_WITHOUT_UNCERTAINTIES_TITLE, - "Basecase;Contingency;FailureStep;FailureDescription;ViolationType;Equipment;Value;Limit;Country;BaseVoltage", - "network1;fault1;;;CURRENT;line1;" + String.format(Locale.getDefault(),"%g",1100f) + ";" + String.format(Locale.getDefault(),"%g",1000f) - + ";FR" + ";" + String.format(Locale.getDefault(),"%g",380f), - "network1;fault2;;;CURRENT;line2;" + String.format(Locale.getDefault(),"%g",950f) + ";" + String.format(Locale.getDefault(),"%g",900f) - + ";FR" + ";" + String.format(Locale.getDefault(),"%g",380f)); + "Basecase;Contingency;FailureStep;FailureDescription;ViolationType;Equipment;Value;Limit;Country;BaseVoltage;Side", + networkId + ";fault1;;;CURRENT;" + line1Id + ";" + String.format(Locale.getDefault(),"%g",1100f) + ";" + String.format(Locale.getDefault(),"%g",1000f) + + ";FR" + ";" + String.format(Locale.getDefault(),"%g",380f) + ";ONE", + networkId + ";fault2;;;CURRENT;" + line2Id + ";" + String.format(Locale.getDefault(),"%g",950f) + ";" + String.format(Locale.getDefault(),"%g",900f) + + ";FR" + ";" + String.format(Locale.getDefault(),"%g",380f) + ";ONE"); assertEquals(reportContent, CharStreams.toString(new InputStreamReader(Files.newInputStream(report))).trim()); } - + @Test public void testExportPostContigencyViolationsWithUncertaintiesLoadflowDivergence() throws IOException { Path folder = Files.createDirectory(fileSystem.getPath("/export-folder")); - - WCAReportImpl wcaReport = new WCAReportImpl("network1"); + + WCAReportImpl wcaReport = new WCAReportImpl(network); WCAPostContingencyStatus postContingencyStatus = new WCAPostContingencyStatus("fault1", new WCALoadflowResult(true, null)); postContingencyStatus.setPostContingencyWithUncertaintiesLoadflowResult(new WCALoadflowResult(false, "post contingency with uncertainties loadflow diverged")); wcaReport.addPostContingencyStatus(postContingencyStatus); wcaReport.exportCsv(folder); - + Path report = folder.resolve(WCAReportImpl.POST_CONTINGENCY_VIOLATIONS_WITH_UNCERTAINTIES_FILE); assertTrue(Files.exists(report)); String reportContent = String.join(System.lineSeparator(), WCAReportImpl.POST_CONTINGENCY_VIOLATIONS_WITH_UNCERTAINTIES_TITLE, - "Basecase;Contingency;FailureStep;FailureDescription;ViolationType;Equipment;Value;Limit;Country;BaseVoltage", - "network1;fault1;Loadflow;post contingency with uncertainties loadflow diverged;;;;;;"); + "Basecase;Contingency;FailureStep;FailureDescription;ViolationType;Equipment;Value;Limit;Country;BaseVoltage;Side", + networkId + ";fault1;Loadflow;post contingency with uncertainties loadflow diverged;;;;;;;"); assertEquals(reportContent, CharStreams.toString(new InputStreamReader(Files.newInputStream(report))).trim()); } - + @Test public void testExportPostContigencyViolationsWithUncertainties() throws IOException { Path folder = Files.createDirectory(fileSystem.getPath("/export-folder")); - - WCAReportImpl wcaReport = new WCAReportImpl("network1"); + + WCAReportImpl wcaReport = new WCAReportImpl(network); WCAPostContingencyStatus postContingencyStatus1 = new WCAPostContingencyStatus("fault1", new WCALoadflowResult(true, null)); postContingencyStatus1.setPostContingencyWithUncertaintiesLoadflowResult(new WCALoadflowResult(true, null)); postContingencyStatus1.setPostContingencyViolationsWithUncertainties(Collections.singletonList(line1Violation)); @@ -315,24 +350,24 @@ public void testExportPostContigencyViolationsWithUncertainties() throws IOExcep postContingencyStatus2.setPostContingencyViolationsWithUncertainties(Collections.singletonList(line2Violation)); wcaReport.addPostContingencyStatus(postContingencyStatus2); wcaReport.exportCsv(folder); - + Path report = folder.resolve(WCAReportImpl.POST_CONTINGENCY_VIOLATIONS_WITH_UNCERTAINTIES_FILE); assertTrue(Files.exists(report)); String reportContent = String.join(System.lineSeparator(), WCAReportImpl.POST_CONTINGENCY_VIOLATIONS_WITH_UNCERTAINTIES_TITLE, - "Basecase;Contingency;FailureStep;FailureDescription;ViolationType;Equipment;Value;Limit;Country;BaseVoltage", - "network1;fault1;;;CURRENT;line1;" + String.format(Locale.getDefault(),"%g",1100f) + ";" + String.format(Locale.getDefault(),"%g",1000f) - + ";FR" + ";" + String.format(Locale.getDefault(),"%g",380f), - "network1;fault2;;;CURRENT;line2;" + String.format(Locale.getDefault(),"%g",950f) + ";" + String.format(Locale.getDefault(),"%g",900f) - + ";FR" + ";" + String.format(Locale.getDefault(),"%g",380f)); + "Basecase;Contingency;FailureStep;FailureDescription;ViolationType;Equipment;Value;Limit;Country;BaseVoltage;Side", + networkId + ";fault1;;;CURRENT;" + line1Id + ";" + String.format(Locale.getDefault(),"%g",1100f) + ";" + String.format(Locale.getDefault(),"%g",1000f) + + ";FR" + ";" + String.format(Locale.getDefault(),"%g",380f) + ";ONE", + networkId + ";fault2;;;CURRENT;" + line2Id + ";" + String.format(Locale.getDefault(),"%g",950f) + ";" + String.format(Locale.getDefault(),"%g",900f) + + ";FR" + ";" + String.format(Locale.getDefault(),"%g",380f) + ";ONE"); assertEquals(reportContent, CharStreams.toString(new InputStreamReader(Files.newInputStream(report))).trim()); } - + @Test public void testExportCurativeActionsApplication() throws IOException { Path folder = Files.createDirectory(fileSystem.getPath("/export-folder")); - - WCAReportImpl wcaReport = new WCAReportImpl("network1"); + + WCAReportImpl wcaReport = new WCAReportImpl(network); WCAPostContingencyStatus postContingencyStatus1 = new WCAPostContingencyStatus("fault1", new WCALoadflowResult(true, null)); postContingencyStatus1.setCurativeActionsApplication(Arrays.asList(new WCAActionApplication("action1", null, @@ -356,30 +391,30 @@ public void testExportCurativeActionsApplication() throws IOException { null))); wcaReport.addPostContingencyStatus(postContingencyStatus2); wcaReport.exportCsv(folder); - + Path report = folder.resolve(WCAReportImpl.POST_CURATIVE_ACTIONS_FILE); assertTrue(Files.exists(report)); String reportContent = String.join(System.lineSeparator(), WCAReportImpl.POST_CURATIVE_ACTIONS_TITLE, "Basecase;Contingency;ActionId;ViolatedEquipment;ViolationType;FailureStep;FailureDescription;ViolationRemoved;ActionApplied;Comment", - "network1;fault1;action1;;;;;false;false;violantions found in post action state", - "network1;fault1;action2;;;;;true;true;", - "network1;fault2;action3;;;Loadflow;loadflow on post action state diverged;false;false;"); + networkId + ";fault1;action1;;;;;false;false;violantions found in post action state", + networkId + ";fault1;action2;;;;;true;true;", + networkId + ";fault2;action3;;;Loadflow;loadflow on post action state diverged;false;false;"); assertEquals(reportContent, CharStreams.toString(new InputStreamReader(Files.newInputStream(report))).trim()); } - + @Test public void testExportCreateFolder() throws IOException { Path folder = fileSystem.getPath("/export-folder"); - WCAReportImpl wcaReport = new WCAReportImpl("network1"); + WCAReportImpl wcaReport = new WCAReportImpl(network); wcaReport.exportCsv(folder); assertTrue(Files.exists(folder)); } - + @Test public void testFailExportToFile() throws IOException { Path file = Files.createFile(fileSystem.getPath("/file")); - WCAReportImpl wcaReport = new WCAReportImpl("network1"); + WCAReportImpl wcaReport = new WCAReportImpl(network); assertFalse(wcaReport.exportCsv(file)); } } diff --git a/wca-integration/src/test/java/eu/itesla_project/wca/WCAUtilsTest.java b/wca-integration/src/test/java/eu/itesla_project/wca/WCAUtilsTest.java index 817178fb..d1eeff84 100644 --- a/wca-integration/src/test/java/eu/itesla_project/wca/WCAUtilsTest.java +++ b/wca-integration/src/test/java/eu/itesla_project/wca/WCAUtilsTest.java @@ -23,6 +23,7 @@ import java.util.Map; import java.util.zip.GZIPInputStream; +import com.powsybl.iidm.network.Branch; import org.apache.commons.io.IOUtils; import org.joda.time.DateTime; import org.joda.time.DateTimeZone; @@ -184,9 +185,9 @@ public void testApplyInjections() { @Test public void testContainsViolation() { - LimitViolation line1Violation = new LimitViolation("line1", LimitViolationType.CURRENT, 1000f, "10'", 1100f); - LimitViolation line2Violation = new LimitViolation("line2", LimitViolationType.CURRENT, 900f, "20'", 950f); - LimitViolation line3Violation = new LimitViolation("line3", LimitViolationType.CURRENT, 1000f, "30'", 1300f); + LimitViolation line1Violation = new LimitViolation("line1", LimitViolationType.CURRENT, "10'",10*60, 1f, 1000f, 1100f, Branch.Side.ONE); + LimitViolation line2Violation = new LimitViolation("line2", LimitViolationType.CURRENT, "20'", 20*60, 1f, 900f, 950f, Branch.Side.ONE); + LimitViolation line3Violation = new LimitViolation("line3", LimitViolationType.CURRENT, "30'", 30*60, 1000f, 1f, 1300f, Branch.Side.ONE); assertFalse(WCAUtils.containsViolation(Arrays.asList(line1Violation, line3Violation), line2Violation)); assertTrue(WCAUtils.containsViolation(Arrays.asList(line1Violation, line2Violation, line3Violation), line2Violation));