Skip to content

Commit c76619e

Browse files
committed
add test cases: invalid extension and ns not match expected version
Signed-off-by: Samir Romdhani <samir.romdhani_externe@rte-france.com>
1 parent 65f0051 commit c76619e

File tree

3 files changed

+89
-5
lines changed

3 files changed

+89
-5
lines changed

iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/NetworkSerDe.java

Lines changed: 50 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
import java.util.zip.GZIPInputStream;
6464
import java.util.zip.GZIPOutputStream;
6565

66+
import static com.powsybl.commons.xml.XmlUtil.getXMLInputFactory;
6667
import static com.powsybl.iidm.serde.AbstractTreeDataImporter.SUFFIX_MAPPING;
6768
import static com.powsybl.iidm.serde.IidmSerDeConstants.IIDM_PREFIX;
6869
import static com.powsybl.iidm.serde.IidmSerDeConstants.INDENT;
@@ -163,6 +164,16 @@ private static void validate(InputStream is, IidmVersion version, ExtensionsSupp
163164
Objects.requireNonNull(is);
164165
Objects.requireNonNull(version);
165166
Objects.requireNonNull(extensionsSupplier);
167+
168+
// check version namespace
169+
byte[] xmlBytes;
170+
try {
171+
xmlBytes = is.readAllBytes();
172+
checkNamespace(xmlBytes, version);
173+
} catch (IOException e) {
174+
throw new UncheckedIOException(e);
175+
}
176+
// XSD validation
166177
Schema schema;
167178
if (extensionsSupplier == DefaultExtensionsSupplier.getInstance()) {
168179
schema = DEFAULT_SCHEMAS_SUPPLIER.get().get(version);
@@ -173,7 +184,7 @@ private static void validate(InputStream is, IidmVersion version, ExtensionsSupp
173184
schema = createSchema(extensionsSupplier, version);
174185
}
175186
try {
176-
schema.newValidator().validate(new StreamSource(is));
187+
schema.newValidator().validate(new StreamSource(new ByteArrayInputStream(xmlBytes)));
177188
} catch (IOException e) {
178189
throw new UncheckedIOException(e);
179190
} catch (SAXException e) {
@@ -254,8 +265,17 @@ private static List<Source> getExtensionSources(ExtensionsSupplier extensionsSup
254265
return extensions;
255266
}
256267

268+
private static void checkNamespace(byte[] xmlBytes, IidmVersion validationVersion) {
269+
String actualNs = readRootNamespace(xmlBytes);
270+
boolean matches = actualNs.equals(validationVersion.getNamespaceURI())
271+
|| validationVersion.supportEquipmentValidationLevel() && actualNs.equals(validationVersion.getNamespaceURI(false));
272+
if (!matches) {
273+
throw new PowsyblException("Namespace mismatch: expected validation version " + validationVersion.toString(".") + ", found namespace " + actualNs);
274+
}
275+
}
276+
257277
/**
258-
* Extract {@code xs:import/@schemaLocation} in an XSD document
278+
* Extract {@code xs:import/@schemaLocation} from XSD document
259279
*
260280
* <p>XSD document snippet:</p>
261281
* <pre>{@code
@@ -271,9 +291,7 @@ private static List<Source> getExtensionSources(ExtensionsSupplier extensionsSup
271291
*/
272292
private static List<String> extractSchemaLocations(byte[] xsdBytes) {
273293
List<String> locations = new ArrayList<>();
274-
XMLInputFactory xif = XMLInputFactory.newFactory();
275-
xif.setProperty(XMLInputFactory.SUPPORT_DTD, false);
276-
xif.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false);
294+
XMLInputFactory xif = getXMLInputFactory();
277295
try {
278296
XMLStreamReader reader = xif.createXMLStreamReader(new ByteArrayInputStream(xsdBytes));
279297
while (reader.hasNext()) {
@@ -294,6 +312,33 @@ private static List<String> extractSchemaLocations(byte[] xsdBytes) {
294312
return locations;
295313
}
296314

315+
/**
316+
* Read the namespace declared on {@code <network>} element
317+
*
318+
* @param xmlBytes XML document content as bytes
319+
* @return Namespace URI
320+
*/
321+
private static String readRootNamespace(byte[] xmlBytes) {
322+
XMLInputFactory xif = getXMLInputFactory();
323+
try {
324+
XMLStreamReader reader = xif.createXMLStreamReader(new ByteArrayInputStream(xmlBytes));
325+
while (reader.hasNext()) {
326+
if (reader.next() == XMLStreamConstants.START_ELEMENT) {
327+
if (!NETWORK_ROOT_ELEMENT_NAME.equals(reader.getLocalName())) {
328+
throw new PowsyblException("Unexpected root element: " + reader.getLocalName());
329+
}
330+
String ns = reader.getNamespaceURI();
331+
reader.close();
332+
return ns;
333+
}
334+
}
335+
reader.close();
336+
throw new PowsyblException("No root element found");
337+
} catch (XMLStreamException e) {
338+
throw new RuntimeException(e);
339+
}
340+
}
341+
297342
private static void throwExceptionIfOption(AbstractOptions<?> options, String message) {
298343
if (options.isThrowExceptionIfExtensionNotFound()) {
299344
throw new PowsyblException(message);

iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/NetworkSerDeTest.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,16 @@ void testValidateByVersionWhenValidNetworkInRecentVersion() throws IOException {
406406
}
407407
}
408408

409+
@Test
410+
void testValidateByVersionWhenNetworkVersionDoesNotMatchExpectedValidationVersion() throws IOException {
411+
try (InputStream is = getClass().getResourceAsStream("/V1_16/shuntRoundTripRef.xml")) {
412+
assertNotNull(is);
413+
assertThatThrownBy(() -> NetworkSerDe.validate(is, IidmVersion.V_1_15))
414+
.isInstanceOf(PowsyblException.class)
415+
.hasMessageContaining("Namespace mismatch: expected validation version 1.15, found namespace http://www.powsybl.org/schema/iidm/1_16");
416+
}
417+
}
418+
409419
@Test
410420
void testValidateByVersionWhenInvalidNetwork() throws IOException {
411421
try (InputStream is = getClass().getResourceAsStream("/V1_16/shuntOldTagName.xml")) {
@@ -430,4 +440,16 @@ void testValidateByVersionWhenNetworkContainTerminalMockExtension() throws IOExc
430440
}
431441
}
432442

443+
@Test
444+
void testValidateByVersionWhenInValidExtension() throws IOException {
445+
try (InputStream is = getClass().getResourceAsStream("/branchStatusWrongEnum.xml")) {
446+
assertNotNull(is);
447+
assertThatThrownBy(() -> NetworkSerDe.validate(is, IidmVersion.V_1_16))
448+
.isInstanceOf(com.powsybl.commons.exceptions.UncheckedSaxException.class)
449+
.hasMessageContaining("Value 'TEST' is not facet-valid with respect to enumeration " +
450+
"'[IN_OPERATION, PLANNED_OUTAGE, FORCED_OUTAGE]'. It must be a value from the enumeration.");
451+
452+
}
453+
}
454+
433455
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<iidm:network xmlns:iidm="http://www.powsybl.org/schema/iidm/1_16" xmlns:bs="http://www.powsybl.org/schema/iidm/ext/branch_status/1_0" id="test" caseDate="2016-06-27T12:27:58.535+02:00" forecastDistance="0" sourceFormat="test" minimumValidationLevel="STEADY_STATE_HYPOTHESIS">
3+
<iidm:substation id="S" country="FR">
4+
<iidm:voltageLevel id="VL" nominalV="400.0" topologyKind="NODE_BREAKER">
5+
<iidm:nodeBreakerTopology></iidm:nodeBreakerTopology>
6+
</iidm:voltageLevel>
7+
</iidm:substation>
8+
<iidm:substation id="S2" country="FR">
9+
<iidm:voltageLevel id="VL2" nominalV="400.0" topologyKind="NODE_BREAKER">
10+
<iidm:nodeBreakerTopology></iidm:nodeBreakerTopology>
11+
</iidm:voltageLevel>
12+
</iidm:substation>
13+
<iidm:line id="L" r="1.0" x="1.0" g1="0.0" b1="0.0" g2="0.0" b2="0.0" node1="2" voltageLevelId1="VL" node2="0" voltageLevelId2="VL2"/>
14+
<iidm:extension id="L">
15+
<bs:branchStatus>TEST</bs:branchStatus>
16+
</iidm:extension>
17+
</iidm:network>

0 commit comments

Comments
 (0)