Merge branch 'master' of https://github.com/hapifhir/org.hl7.fhir
This commit is contained in:
commit
b9902833c5
|
@ -8,4 +8,5 @@
|
||||||
|
|
||||||
## Other code changes
|
## Other code changes
|
||||||
|
|
||||||
* no changes
|
* Improved output for unit test comparisons
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,91 @@
|
||||||
|
package org.hl7.fhir.convertors.conv40_50;
|
||||||
|
|
||||||
|
import org.apache.commons.codec.binary.Base64;
|
||||||
|
import org.hl7.fhir.convertors.factory.VersionConvertorFactory_40_50;
|
||||||
|
import org.junit.jupiter.api.DisplayName;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
public class AuditEvent40_50Test {
|
||||||
|
|
||||||
|
public static final String THE_BASE_64_BINARY_STRING = "dGhpcyBpcyB2YWxpZCBiYXNlNjQ=";
|
||||||
|
public static final byte[] THE_BASE_64_BINARY_BYTE_ARRAY = Base64.decodeBase64(THE_BASE_64_BINARY_STRING.getBytes(ca.uhn.fhir.rest.api.Constants.CHARSET_UTF8));
|
||||||
|
|
||||||
|
public static final String INVALID_BASE_64_BINARY_STRING = "Picard was the best starship captain";
|
||||||
|
public static final byte[] INVALID_BASE_64_BINARY_BYTE_ARRAY = Base64.decodeBase64(INVALID_BASE_64_BINARY_STRING.getBytes(ca.uhn.fhir.rest.api.Constants.CHARSET_UTF8));
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("Test r5 -> r4 AuditEvent conversion.")
|
||||||
|
public void testR5_R4() throws IOException {
|
||||||
|
InputStream r5_input = this.getClass().getResourceAsStream("/auditevent_50_with_base64binary.xml");
|
||||||
|
|
||||||
|
org.hl7.fhir.r5.model.AuditEvent r5_actual = (org.hl7.fhir.r5.model.AuditEvent) new org.hl7.fhir.r5.formats.XmlParser().parse(r5_input);
|
||||||
|
org.hl7.fhir.r4.model.Resource r4_conv = VersionConvertorFactory_40_50.convertResource(r5_actual);
|
||||||
|
|
||||||
|
org.hl7.fhir.r4.formats.XmlParser r4_parser = new org.hl7.fhir.r4.formats.XmlParser();
|
||||||
|
|
||||||
|
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
||||||
|
r4_parser.compose(stream, r4_conv);
|
||||||
|
|
||||||
|
org.hl7.fhir.r4.model.Resource r4_streamed = (org.hl7.fhir.r4.model.AuditEvent) new org.hl7.fhir.r4.formats.XmlParser().parse(new ByteArrayInputStream(stream.toByteArray()));
|
||||||
|
|
||||||
|
assertArrayEquals(((org.hl7.fhir.r4.model.AuditEvent)r4_conv).getEntity().get(0).getQuery(), THE_BASE_64_BINARY_BYTE_ARRAY);
|
||||||
|
assertArrayEquals(((org.hl7.fhir.r4.model.AuditEvent)r4_streamed).getEntity().get(0).getQuery(), THE_BASE_64_BINARY_BYTE_ARRAY);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("Test r5 -> r4 AuditEvent conversion.")
|
||||||
|
public void testR4_R5() throws IOException {
|
||||||
|
InputStream r4_input = this.getClass().getResourceAsStream("/auditevent_40_with_base64binary.xml");
|
||||||
|
|
||||||
|
org.hl7.fhir.r4.model.AuditEvent r4_actual = (org.hl7.fhir.r4.model.AuditEvent) new org.hl7.fhir.r4.formats.XmlParser().parse(r4_input);
|
||||||
|
org.hl7.fhir.r5.model.Resource r5_conv = VersionConvertorFactory_40_50.convertResource(r4_actual);
|
||||||
|
|
||||||
|
org.hl7.fhir.r5.formats.XmlParser r5_parser = new org.hl7.fhir.r5.formats.XmlParser();
|
||||||
|
|
||||||
|
ByteArrayOutputStream stream
|
||||||
|
= new ByteArrayOutputStream();
|
||||||
|
|
||||||
|
r5_parser.compose(stream, r5_conv);
|
||||||
|
|
||||||
|
org.hl7.fhir.r5.model.Resource r5_streamed = (org.hl7.fhir.r5.model.AuditEvent) new org.hl7.fhir.r5.formats.XmlParser().parse(new ByteArrayInputStream(stream.toByteArray()));
|
||||||
|
|
||||||
|
assertArrayEquals(((org.hl7.fhir.r5.model.AuditEvent)r5_conv).getEntity().get(0).getQuery(), THE_BASE_64_BINARY_BYTE_ARRAY);
|
||||||
|
assertArrayEquals(((org.hl7.fhir.r5.model.AuditEvent)r5_streamed).getEntity().get(0).getQuery(), THE_BASE_64_BINARY_BYTE_ARRAY);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("Test r5 -> r4 AuditEvent conversion with invalid Base64Binary.")
|
||||||
|
public void testR4_R5BadBase64Binary() throws IOException {
|
||||||
|
InputStream r4_input = this.getClass().getResourceAsStream("/auditevent_40_with_invalid_base64binary.xml");
|
||||||
|
|
||||||
|
org.hl7.fhir.r4.model.AuditEvent r4_actual = (org.hl7.fhir.r4.model.AuditEvent) new org.hl7.fhir.r4.formats.XmlParser().parse(r4_input);
|
||||||
|
|
||||||
|
org.hl7.fhir.r5.model.Resource r5_conv = VersionConvertorFactory_40_50.convertResource(r4_actual);
|
||||||
|
|
||||||
|
org.hl7.fhir.r5.formats.XmlParser r5_parser = new org.hl7.fhir.r5.formats.XmlParser();
|
||||||
|
|
||||||
|
ByteArrayOutputStream stream
|
||||||
|
= new ByteArrayOutputStream();
|
||||||
|
|
||||||
|
r5_parser.compose(stream, r5_conv);
|
||||||
|
|
||||||
|
org.hl7.fhir.r5.model.Resource r5_streamed = (org.hl7.fhir.r5.model.AuditEvent) new org.hl7.fhir.r5.formats.XmlParser().parse(new ByteArrayInputStream(stream.toByteArray()));
|
||||||
|
|
||||||
|
System.out.println(((org.hl7.fhir.r5.model.AuditEvent)r5_conv).getEntity().get(0).getQueryElement().getValueAsString());
|
||||||
|
|
||||||
|
//FIXME we should not be even getting this far.
|
||||||
|
assertArrayEquals(((org.hl7.fhir.r5.model.AuditEvent)r5_conv).getEntity().get(0).getQuery(), INVALID_BASE_64_BINARY_BYTE_ARRAY);
|
||||||
|
assertArrayEquals(((org.hl7.fhir.r5.model.AuditEvent)r5_streamed).getEntity().get(0).getQuery(), INVALID_BASE_64_BINARY_BYTE_ARRAY);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,95 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<AuditEvent xmlns="http://hl7.org/fhir">
|
||||||
|
<id value="example"/>
|
||||||
|
<text>
|
||||||
|
<status value="generated"/>
|
||||||
|
<div xmlns="http://www.w3.org/1999/xhtml">Application Start for under service login "Grahame" (id: Grahame's Test HL7Connect)</div>
|
||||||
|
</text>
|
||||||
|
|
||||||
|
<type>
|
||||||
|
<system value="http://dicom.nema.org/resources/ontology/DCM"/>
|
||||||
|
<code value="110100"/>
|
||||||
|
<display value="Application Activity"/>
|
||||||
|
</type>
|
||||||
|
<subtype>
|
||||||
|
<system value="http://dicom.nema.org/resources/ontology/DCM"/>
|
||||||
|
<code value="110120"/>
|
||||||
|
<display value="Application Start"/>
|
||||||
|
</subtype>
|
||||||
|
<action value="E"/>
|
||||||
|
<recorded value="2012-10-25T22:04:27+11:00"/>
|
||||||
|
<outcome value="0"/>
|
||||||
|
<agent>
|
||||||
|
<type> <coding> <system value="http://terminology.hl7.org/CodeSystem/extra-security-role-type"/> <code value="humanuser"/> <display value="human user"/> </coding> </type>
|
||||||
|
|
||||||
|
<role>
|
||||||
|
<text value="Service User (Logon)"/>
|
||||||
|
</role>
|
||||||
|
<who>
|
||||||
|
<identifier>
|
||||||
|
<value value="Grahame"/>
|
||||||
|
</identifier>
|
||||||
|
</who>
|
||||||
|
|
||||||
|
<requestor value="false"/>
|
||||||
|
<network>
|
||||||
|
<address value="127.0.0.1"/>
|
||||||
|
<type value="2"/>
|
||||||
|
</network>
|
||||||
|
</agent>
|
||||||
|
<agent> <!-- Source active participant, the software making the . AlternativeUserId - Process ID
|
||||||
|
-->
|
||||||
|
<type> <coding> <system value="http://dicom.nema.org/resources/ontology/DCM"/> <code value="110153"/> <display value="Source Role ID"/> </coding> </type>
|
||||||
|
<who>
|
||||||
|
<identifier>
|
||||||
|
<system value="urn:oid:2.16.840.1.113883.4.2"/>
|
||||||
|
<value value="2.16.840.1.113883.4.2"/>
|
||||||
|
</identifier>
|
||||||
|
</who>
|
||||||
|
<altId value="6580"/>
|
||||||
|
<requestor value="false"/>
|
||||||
|
<network> <address value="Workstation1.ehr.familyclinic.com"/> <type value="1"/> </network>
|
||||||
|
</agent>
|
||||||
|
<source>
|
||||||
|
<site value="Development"/>
|
||||||
|
<observer>
|
||||||
|
<display value="Grahame's Laptop"/>
|
||||||
|
</observer>
|
||||||
|
<type>
|
||||||
|
<system value="http://dicom.nema.org/resources/ontology/DCM"/>
|
||||||
|
<code value="110122"/>
|
||||||
|
<display value="Login"/>
|
||||||
|
</type>
|
||||||
|
</source>
|
||||||
|
<entity>
|
||||||
|
<what> <identifier>
|
||||||
|
<type>
|
||||||
|
<coding>
|
||||||
|
<system value="http://terminology.hl7.org/CodeSystem/v2-0203"/>
|
||||||
|
<code value="SNO"/>
|
||||||
|
</coding>
|
||||||
|
<text value="Dell Serial Number"/>
|
||||||
|
</type>
|
||||||
|
<value value="ABCDEF"/>
|
||||||
|
</identifier>
|
||||||
|
</what>
|
||||||
|
<type>
|
||||||
|
<system value="http://terminology.hl7.org/CodeSystem/audit-entity-type"/>
|
||||||
|
<code value="4"/>
|
||||||
|
<display value="Other"/>
|
||||||
|
</type>
|
||||||
|
<role>
|
||||||
|
<system value="http://terminology.hl7.org/CodeSystem/object-role"/>
|
||||||
|
<code value="4"/>
|
||||||
|
<display value="Domain Resource"/>
|
||||||
|
</role>
|
||||||
|
<lifecycle>
|
||||||
|
<system value="http://terminology.hl7.org/CodeSystem/dicom-audit-lifecycle"/>
|
||||||
|
<code value="6"/>
|
||||||
|
<display value="Access / Use"/>
|
||||||
|
</lifecycle>
|
||||||
|
<name value="Grahame's Laptop"/>
|
||||||
|
<query value="dGhpcyBpcyB2YWxpZCBiYXNlNjQ="/>
|
||||||
|
</entity>
|
||||||
|
</AuditEvent>
|
|
@ -0,0 +1,95 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<AuditEvent xmlns="http://hl7.org/fhir">
|
||||||
|
<id value="example"/>
|
||||||
|
<text>
|
||||||
|
<status value="generated"/>
|
||||||
|
<div xmlns="http://www.w3.org/1999/xhtml">Application Start for under service login "Grahame" (id: Grahame's Test HL7Connect)</div>
|
||||||
|
</text>
|
||||||
|
|
||||||
|
<type>
|
||||||
|
<system value="http://dicom.nema.org/resources/ontology/DCM"/>
|
||||||
|
<code value="110100"/>
|
||||||
|
<display value="Application Activity"/>
|
||||||
|
</type>
|
||||||
|
<subtype>
|
||||||
|
<system value="http://dicom.nema.org/resources/ontology/DCM"/>
|
||||||
|
<code value="110120"/>
|
||||||
|
<display value="Application Start"/>
|
||||||
|
</subtype>
|
||||||
|
<action value="E"/>
|
||||||
|
<recorded value="2012-10-25T22:04:27+11:00"/>
|
||||||
|
<outcome value="0"/>
|
||||||
|
<agent>
|
||||||
|
<type> <coding> <system value="http://terminology.hl7.org/CodeSystem/extra-security-role-type"/> <code value="humanuser"/> <display value="human user"/> </coding> </type>
|
||||||
|
|
||||||
|
<role>
|
||||||
|
<text value="Service User (Logon)"/>
|
||||||
|
</role>
|
||||||
|
<who>
|
||||||
|
<identifier>
|
||||||
|
<value value="Grahame"/>
|
||||||
|
</identifier>
|
||||||
|
</who>
|
||||||
|
|
||||||
|
<requestor value="false"/>
|
||||||
|
<network>
|
||||||
|
<address value="127.0.0.1"/>
|
||||||
|
<type value="2"/>
|
||||||
|
</network>
|
||||||
|
</agent>
|
||||||
|
<agent> <!-- Source active participant, the software making the . AlternativeUserId - Process ID
|
||||||
|
-->
|
||||||
|
<type> <coding> <system value="http://dicom.nema.org/resources/ontology/DCM"/> <code value="110153"/> <display value="Source Role ID"/> </coding> </type>
|
||||||
|
<who>
|
||||||
|
<identifier>
|
||||||
|
<system value="urn:oid:2.16.840.1.113883.4.2"/>
|
||||||
|
<value value="2.16.840.1.113883.4.2"/>
|
||||||
|
</identifier>
|
||||||
|
</who>
|
||||||
|
<altId value="6580"/>
|
||||||
|
<requestor value="false"/>
|
||||||
|
<network> <address value="Workstation1.ehr.familyclinic.com"/> <type value="1"/> </network>
|
||||||
|
</agent>
|
||||||
|
<source>
|
||||||
|
<site value="Development"/>
|
||||||
|
<observer>
|
||||||
|
<display value="Grahame's Laptop"/>
|
||||||
|
</observer>
|
||||||
|
<type>
|
||||||
|
<system value="http://dicom.nema.org/resources/ontology/DCM"/>
|
||||||
|
<code value="110122"/>
|
||||||
|
<display value="Login"/>
|
||||||
|
</type>
|
||||||
|
</source>
|
||||||
|
<entity>
|
||||||
|
<what> <identifier>
|
||||||
|
<type>
|
||||||
|
<coding>
|
||||||
|
<system value="http://terminology.hl7.org/CodeSystem/v2-0203"/>
|
||||||
|
<code value="SNO"/>
|
||||||
|
</coding>
|
||||||
|
<text value="Dell Serial Number"/>
|
||||||
|
</type>
|
||||||
|
<value value="ABCDEF"/>
|
||||||
|
</identifier>
|
||||||
|
</what>
|
||||||
|
<type>
|
||||||
|
<system value="http://terminology.hl7.org/CodeSystem/audit-entity-type"/>
|
||||||
|
<code value="4"/>
|
||||||
|
<display value="Other"/>
|
||||||
|
</type>
|
||||||
|
<role>
|
||||||
|
<system value="http://terminology.hl7.org/CodeSystem/object-role"/>
|
||||||
|
<code value="4"/>
|
||||||
|
<display value="Domain Resource"/>
|
||||||
|
</role>
|
||||||
|
<lifecycle>
|
||||||
|
<system value="http://terminology.hl7.org/CodeSystem/dicom-audit-lifecycle"/>
|
||||||
|
<code value="6"/>
|
||||||
|
<display value="Access / Use"/>
|
||||||
|
</lifecycle>
|
||||||
|
<name value="Grahame's Laptop"/>
|
||||||
|
<query value="Picard was the best starship captain"/>
|
||||||
|
</entity>
|
||||||
|
</AuditEvent>
|
|
@ -0,0 +1,100 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<AuditEvent xmlns="http://hl7.org/fhir">
|
||||||
|
<id value="example"/>
|
||||||
|
<text>
|
||||||
|
<status value="generated"/>
|
||||||
|
<div xmlns="http://www.w3.org/1999/xhtml">Application Start for under service login "Grahame" (id: Grahame's Test HL7Connect)</div>
|
||||||
|
</text>
|
||||||
|
<category>
|
||||||
|
<coding>
|
||||||
|
<system value="http://dicom.nema.org/resources/ontology/DCM"/>
|
||||||
|
<code value="110100"/>
|
||||||
|
<display value="Application Activity"/>
|
||||||
|
</coding>
|
||||||
|
</category>
|
||||||
|
<code>
|
||||||
|
<coding>
|
||||||
|
<system value="http://dicom.nema.org/resources/ontology/DCM"/>
|
||||||
|
<code value="110120"/>
|
||||||
|
<display value="Application Start"/>
|
||||||
|
</coding>
|
||||||
|
</code>
|
||||||
|
<action value="E"/>
|
||||||
|
<recorded value="2012-10-25T22:04:27+11:00"/>
|
||||||
|
<outcome>
|
||||||
|
<code>
|
||||||
|
<system value="http://terminology.hl7.org/CodeSystem/audit-event-outcome"/>
|
||||||
|
<code value="0"/>
|
||||||
|
<display value="Success"/>
|
||||||
|
</code>
|
||||||
|
</outcome>
|
||||||
|
<agent>
|
||||||
|
<role>
|
||||||
|
<text value="Service User (Logon)"/>
|
||||||
|
</role>
|
||||||
|
<who>
|
||||||
|
<identifier>
|
||||||
|
<value value="Grahame"/>
|
||||||
|
</identifier>
|
||||||
|
</who>
|
||||||
|
<requestor value="false"/>
|
||||||
|
|
||||||
|
</agent>
|
||||||
|
<agent>
|
||||||
|
<!-- Source active participant, the software making the . AlternativeUserId - Process ID
|
||||||
|
-->
|
||||||
|
<extension url="http://hl7.org/fhir/StructureDefinition/auditevent-AlternativeUserID">
|
||||||
|
<valueIdentifier>
|
||||||
|
<type>
|
||||||
|
<text value="process ID"/>
|
||||||
|
</type>
|
||||||
|
<value value="6580"/>
|
||||||
|
</valueIdentifier>
|
||||||
|
</extension>
|
||||||
|
<who>
|
||||||
|
<identifier>
|
||||||
|
<system value="urn:oid:2.16.840.1.113883.4.2"/>
|
||||||
|
<value value="2.16.840.1.113883.4.2"/>
|
||||||
|
</identifier>
|
||||||
|
</who>
|
||||||
|
<requestor value="false"/>
|
||||||
|
<networkString value="Workstation1.ehr.familyclinic.com"/>
|
||||||
|
|
||||||
|
</agent>
|
||||||
|
<source>
|
||||||
|
<observer>
|
||||||
|
<display value="Grahame's Laptop"/>
|
||||||
|
</observer>
|
||||||
|
<type>
|
||||||
|
<coding>
|
||||||
|
<system value="http://dicom.nema.org/resources/ontology/DCM"/>
|
||||||
|
<code value="110122"/>
|
||||||
|
<display value="Login"/>
|
||||||
|
</coding>
|
||||||
|
</type>
|
||||||
|
</source>
|
||||||
|
<entity>
|
||||||
|
<what>
|
||||||
|
<identifier>
|
||||||
|
<type>
|
||||||
|
<coding>
|
||||||
|
<system value="http://terminology.hl7.org/CodeSystem/v2-0203"/>
|
||||||
|
<code value="SNO"/>
|
||||||
|
</coding>
|
||||||
|
<text value="Dell Serial Number"/>
|
||||||
|
</type>
|
||||||
|
<value value="ABCDEF"/>
|
||||||
|
</identifier>
|
||||||
|
</what>
|
||||||
|
|
||||||
|
<role>
|
||||||
|
<coding>
|
||||||
|
<system value="http://terminology.hl7.org/CodeSystem/object-role"/>
|
||||||
|
<code value="4"/>
|
||||||
|
<display value="Domain Resource"/>
|
||||||
|
</coding>
|
||||||
|
</role>
|
||||||
|
<query value="dGhpcyBpcyB2YWxpZCBiYXNlNjQ="/>
|
||||||
|
</entity>
|
||||||
|
</AuditEvent>
|
|
@ -1,6 +1,7 @@
|
||||||
package org.hl7.fhir.dstu2.model;
|
package org.hl7.fhir.dstu2.model;
|
||||||
|
|
||||||
import ca.uhn.fhir.parser.DataFormatException;
|
import ca.uhn.fhir.parser.DataFormatException;
|
||||||
|
import org.apache.commons.codec.binary.Base64;
|
||||||
import org.junit.jupiter.api.Assertions;
|
import org.junit.jupiter.api.Assertions;
|
||||||
import org.junit.jupiter.api.DisplayName;
|
import org.junit.jupiter.api.DisplayName;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
@ -11,6 +12,8 @@ class Base64BinaryTypeTest {
|
||||||
|
|
||||||
static final String NON_BASE_64 = "Picard was the best starship captain.";
|
static final String NON_BASE_64 = "Picard was the best starship captain.";
|
||||||
static final String VALID_BASE_64 = "dGhpcyBpcyB2YWxpZCBiYXNlNjQ=";
|
static final String VALID_BASE_64 = "dGhpcyBpcyB2YWxpZCBiYXNlNjQ=";
|
||||||
|
static final byte[] VALID_BASE_64_BYTES = Base64.decodeBase64(VALID_BASE_64.getBytes(ca.uhn.fhir.rest.api.Constants.CHARSET_UTF8));
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("Passing a non Base64 encoded String to constructor causes exception.")
|
@DisplayName("Passing a non Base64 encoded String to constructor causes exception.")
|
||||||
public void testNonBase64String() {
|
public void testNonBase64String() {
|
||||||
|
@ -45,6 +48,15 @@ class Base64BinaryTypeTest {
|
||||||
Assertions.assertNull(b64.getValueAsString());
|
Assertions.assertNull(b64.getValueAsString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("Valid Base64 String creates non-null instance with non-null bytes.")
|
||||||
|
public void testValidBytes() {
|
||||||
|
Base64BinaryType b64 = new Base64BinaryType(VALID_BASE_64_BYTES);
|
||||||
|
Assertions.assertNotNull(b64);
|
||||||
|
Assertions.assertNotNull(b64.getValue());
|
||||||
|
Assertions.assertEquals(VALID_BASE_64, b64.asStringValue());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("Valid Base64 String creates non-null instance with non-null values.")
|
@DisplayName("Valid Base64 String creates non-null instance with non-null values.")
|
||||||
public void testValid() {
|
public void testValid() {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package org.hl7.fhir.dstu2016may.model;
|
package org.hl7.fhir.dstu2016may.model;
|
||||||
|
|
||||||
import ca.uhn.fhir.parser.DataFormatException;
|
import ca.uhn.fhir.parser.DataFormatException;
|
||||||
|
import org.apache.commons.codec.binary.Base64;
|
||||||
import org.junit.jupiter.api.Assertions;
|
import org.junit.jupiter.api.Assertions;
|
||||||
import org.junit.jupiter.api.DisplayName;
|
import org.junit.jupiter.api.DisplayName;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
@ -11,6 +12,8 @@ class Base64BinaryTypeTest {
|
||||||
|
|
||||||
static final String NON_BASE_64 = "Picard was the best starship captain.";
|
static final String NON_BASE_64 = "Picard was the best starship captain.";
|
||||||
static final String VALID_BASE_64 = "dGhpcyBpcyB2YWxpZCBiYXNlNjQ=";
|
static final String VALID_BASE_64 = "dGhpcyBpcyB2YWxpZCBiYXNlNjQ=";
|
||||||
|
static final byte[] VALID_BASE_64_BYTES = Base64.decodeBase64(VALID_BASE_64.getBytes(ca.uhn.fhir.rest.api.Constants.CHARSET_UTF8));
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("Passing a non Base64 encoded String to constructor causes exception.")
|
@DisplayName("Passing a non Base64 encoded String to constructor causes exception.")
|
||||||
public void testNonBase64String() {
|
public void testNonBase64String() {
|
||||||
|
@ -45,6 +48,15 @@ class Base64BinaryTypeTest {
|
||||||
Assertions.assertNull(b64.getValueAsString());
|
Assertions.assertNull(b64.getValueAsString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("Valid Base64 String creates non-null instance with non-null bytes.")
|
||||||
|
public void testValidBytes() {
|
||||||
|
Base64BinaryType b64 = new Base64BinaryType(VALID_BASE_64_BYTES);
|
||||||
|
Assertions.assertNotNull(b64);
|
||||||
|
Assertions.assertNotNull(b64.getValue());
|
||||||
|
Assertions.assertEquals(VALID_BASE_64, b64.asStringValue());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("Valid Base64 String creates non-null instance with non-null values.")
|
@DisplayName("Valid Base64 String creates non-null instance with non-null values.")
|
||||||
public void testValid() {
|
public void testValid() {
|
||||||
|
|
|
@ -130,7 +130,7 @@ public class Base64BinaryType extends PrimitiveType<byte[]> implements IPrimitiv
|
||||||
@Override
|
@Override
|
||||||
public Base64BinaryType setValue(byte[] theValue) throws IllegalArgumentException {
|
public Base64BinaryType setValue(byte[] theValue) throws IllegalArgumentException {
|
||||||
myValue = theValue;
|
myValue = theValue;
|
||||||
return this;
|
return (Base64BinaryType) super.setValue(theValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package org.hl7.fhir.dstu3.model;
|
package org.hl7.fhir.dstu3.model;
|
||||||
|
|
||||||
import ca.uhn.fhir.parser.DataFormatException;
|
import ca.uhn.fhir.parser.DataFormatException;
|
||||||
|
import org.apache.commons.codec.binary.Base64;
|
||||||
import org.junit.jupiter.api.Assertions;
|
import org.junit.jupiter.api.Assertions;
|
||||||
import org.junit.jupiter.api.DisplayName;
|
import org.junit.jupiter.api.DisplayName;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
@ -11,6 +12,8 @@ class Base64BinaryTypeTest {
|
||||||
|
|
||||||
static final String NON_BASE_64 = "Picard was the best starship captain.";
|
static final String NON_BASE_64 = "Picard was the best starship captain.";
|
||||||
static final String VALID_BASE_64 = "dGhpcyBpcyB2YWxpZCBiYXNlNjQ=";
|
static final String VALID_BASE_64 = "dGhpcyBpcyB2YWxpZCBiYXNlNjQ=";
|
||||||
|
static final byte[] VALID_BASE_64_BYTES = Base64.decodeBase64(VALID_BASE_64.getBytes(ca.uhn.fhir.rest.api.Constants.CHARSET_UTF8));
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("Passing a non Base64 encoded String to constructor causes exception.")
|
@DisplayName("Passing a non Base64 encoded String to constructor causes exception.")
|
||||||
public void testNonBase64String() {
|
public void testNonBase64String() {
|
||||||
|
@ -54,6 +57,15 @@ class Base64BinaryTypeTest {
|
||||||
Assertions.assertEquals(VALID_BASE_64, b64.asStringValue());
|
Assertions.assertEquals(VALID_BASE_64, b64.asStringValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("Valid Base64 String creates non-null instance with non-null bytes.")
|
||||||
|
public void testValidBytes() {
|
||||||
|
Base64BinaryType b64 = new Base64BinaryType(VALID_BASE_64_BYTES);
|
||||||
|
Assertions.assertNotNull(b64);
|
||||||
|
Assertions.assertNotNull(b64.getValue());
|
||||||
|
Assertions.assertEquals(VALID_BASE_64, b64.asStringValue());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("Valid Base64 String creates non-null instance with non-null values.")
|
@DisplayName("Valid Base64 String creates non-null instance with non-null values.")
|
||||||
public void testValidSetValueAsString() {
|
public void testValidSetValueAsString() {
|
||||||
|
|
|
@ -130,7 +130,7 @@ public class Base64BinaryType extends PrimitiveType<byte[]> implements IPrimitiv
|
||||||
@Override
|
@Override
|
||||||
public Base64BinaryType setValue(byte[] theValue) throws IllegalArgumentException {
|
public Base64BinaryType setValue(byte[] theValue) throws IllegalArgumentException {
|
||||||
myValue = theValue;
|
myValue = theValue;
|
||||||
return this;
|
return (Base64BinaryType) super.setValue(theValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package org.hl7.fhir.r4.model;
|
package org.hl7.fhir.r4.model;
|
||||||
|
|
||||||
import ca.uhn.fhir.parser.DataFormatException;
|
import ca.uhn.fhir.parser.DataFormatException;
|
||||||
|
import org.apache.commons.codec.binary.Base64;
|
||||||
import org.junit.jupiter.api.Assertions;
|
import org.junit.jupiter.api.Assertions;
|
||||||
import org.junit.jupiter.api.DisplayName;
|
import org.junit.jupiter.api.DisplayName;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
@ -11,6 +12,8 @@ class Base64BinaryTypeTest {
|
||||||
|
|
||||||
static final String NON_BASE_64 = "Picard was the best starship captain.";
|
static final String NON_BASE_64 = "Picard was the best starship captain.";
|
||||||
static final String VALID_BASE_64 = "dGhpcyBpcyB2YWxpZCBiYXNlNjQ=";
|
static final String VALID_BASE_64 = "dGhpcyBpcyB2YWxpZCBiYXNlNjQ=";
|
||||||
|
static final byte[] VALID_BASE_64_BYTES = Base64.decodeBase64(VALID_BASE_64.getBytes(ca.uhn.fhir.rest.api.Constants.CHARSET_UTF8));
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("Passing a non Base64 encoded String to constructor causes exception.")
|
@DisplayName("Passing a non Base64 encoded String to constructor causes exception.")
|
||||||
public void testNonBase64String() {
|
public void testNonBase64String() {
|
||||||
|
@ -54,6 +57,15 @@ class Base64BinaryTypeTest {
|
||||||
Assertions.assertEquals(VALID_BASE_64, b64.asStringValue());
|
Assertions.assertEquals(VALID_BASE_64, b64.asStringValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("Valid Base64 String creates non-null instance with non-null bytes.")
|
||||||
|
public void testValidBytes() {
|
||||||
|
Base64BinaryType b64 = new Base64BinaryType(VALID_BASE_64_BYTES);
|
||||||
|
Assertions.assertNotNull(b64);
|
||||||
|
Assertions.assertNotNull(b64.getValue());
|
||||||
|
Assertions.assertEquals(VALID_BASE_64, b64.asStringValue());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("Valid Base64 String creates non-null instance with non-null values.")
|
@DisplayName("Valid Base64 String creates non-null instance with non-null values.")
|
||||||
public void testValidSetValueAsString() {
|
public void testValidSetValueAsString() {
|
||||||
|
|
|
@ -129,7 +129,7 @@ public class Base64BinaryType extends PrimitiveType<byte[]> implements IPrimitiv
|
||||||
@Override
|
@Override
|
||||||
public Base64BinaryType setValue(byte[] theValue) throws IllegalArgumentException {
|
public Base64BinaryType setValue(byte[] theValue) throws IllegalArgumentException {
|
||||||
myValue = theValue;
|
myValue = theValue;
|
||||||
return this;
|
return (Base64BinaryType) super.setValue(theValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -2,6 +2,7 @@ package org.hl7.fhir.r4b.model;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
|
||||||
|
import org.apache.commons.codec.binary.Base64;
|
||||||
import org.junit.jupiter.api.Assertions;
|
import org.junit.jupiter.api.Assertions;
|
||||||
import org.junit.jupiter.api.DisplayName;
|
import org.junit.jupiter.api.DisplayName;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
@ -12,6 +13,8 @@ class Base64BinaryTypeTest {
|
||||||
|
|
||||||
static final String NON_BASE_64 = "Picard was the best starship captain.";
|
static final String NON_BASE_64 = "Picard was the best starship captain.";
|
||||||
static final String VALID_BASE_64 = "dGhpcyBpcyB2YWxpZCBiYXNlNjQ=";
|
static final String VALID_BASE_64 = "dGhpcyBpcyB2YWxpZCBiYXNlNjQ=";
|
||||||
|
static final byte[] VALID_BASE_64_BYTES = Base64.decodeBase64(VALID_BASE_64.getBytes(ca.uhn.fhir.rest.api.Constants.CHARSET_UTF8));
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("Passing a non Base64 encoded String to constructor causes exception.")
|
@DisplayName("Passing a non Base64 encoded String to constructor causes exception.")
|
||||||
public void testNonBase64String() {
|
public void testNonBase64String() {
|
||||||
|
@ -55,6 +58,15 @@ class Base64BinaryTypeTest {
|
||||||
Assertions.assertEquals(VALID_BASE_64, b64.asStringValue());
|
Assertions.assertEquals(VALID_BASE_64, b64.asStringValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("Valid Base64 String creates non-null instance with non-null bytes.")
|
||||||
|
public void testValidBytes() {
|
||||||
|
Base64BinaryType b64 = new Base64BinaryType(VALID_BASE_64_BYTES);
|
||||||
|
Assertions.assertNotNull(b64);
|
||||||
|
Assertions.assertNotNull(b64.getValue());
|
||||||
|
Assertions.assertEquals(VALID_BASE_64, b64.asStringValue());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("Valid Base64 String creates non-null instance with non-null values.")
|
@DisplayName("Valid Base64 String creates non-null instance with non-null values.")
|
||||||
public void testValidSetValueAsString() {
|
public void testValidSetValueAsString() {
|
||||||
|
|
|
@ -129,7 +129,7 @@ public class Base64BinaryType extends PrimitiveType<byte[]> implements IPrimitiv
|
||||||
@Override
|
@Override
|
||||||
public Base64BinaryType setValue(byte[] theValue) throws IllegalArgumentException {
|
public Base64BinaryType setValue(byte[] theValue) throws IllegalArgumentException {
|
||||||
myValue = theValue;
|
myValue = theValue;
|
||||||
return this;
|
return (Base64BinaryType) super.setValue(theValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -154,7 +154,7 @@ public class ValueSetRenderer extends TerminologyRenderer {
|
||||||
private boolean generateExpansion(XhtmlNode x, ValueSet vs, boolean header, List<UsedConceptMap> maps) throws FHIRFormatError, DefinitionException, IOException {
|
private boolean generateExpansion(XhtmlNode x, ValueSet vs, boolean header, List<UsedConceptMap> maps) throws FHIRFormatError, DefinitionException, IOException {
|
||||||
boolean hasExtensions = false;
|
boolean hasExtensions = false;
|
||||||
List<String> langs = new ArrayList<String>();
|
List<String> langs = new ArrayList<String>();
|
||||||
|
Map<String, String> designations = new HashMap<>(); // map of url = description, where url is the designation code. Designations that are for languages won't make it into this list
|
||||||
|
|
||||||
if (header) {
|
if (header) {
|
||||||
XhtmlNode h = x.addTag(getHeader());
|
XhtmlNode h = x.addTag(getHeader());
|
||||||
|
@ -219,20 +219,24 @@ public class ValueSetRenderer extends TerminologyRenderer {
|
||||||
tr.td().b().tx("System");
|
tr.td().b().tx("System");
|
||||||
XhtmlNode tdDisp = tr.td();
|
XhtmlNode tdDisp = tr.td();
|
||||||
tdDisp.b().tx("Display");
|
tdDisp.b().tx("Display");
|
||||||
boolean doLangs = false;
|
boolean doDesignations = false;
|
||||||
for (ValueSetExpansionContainsComponent c : vs.getExpansion().getContains()) {
|
for (ValueSetExpansionContainsComponent c : vs.getExpansion().getContains()) {
|
||||||
scanForLangs(c, langs);
|
scanForDesignations(c, langs, designations);
|
||||||
}
|
}
|
||||||
if (doDefinition) {
|
if (doDefinition) {
|
||||||
tr.td().b().tx("Definition");
|
tr.td().b().tx("Definition");
|
||||||
doLangs = false;
|
doDesignations = false;
|
||||||
} else {
|
} else {
|
||||||
// if we're not doing definitions and we don't have too many languages, we'll do them in line
|
// if we're not doing definitions and we don't have too many languages, we'll do them in line
|
||||||
if (langs.size() < MAX_DESIGNATIONS_IN_LINE) {
|
doDesignations = langs.size() + designations.size() < MAX_DESIGNATIONS_IN_LINE;
|
||||||
doLangs = true;
|
|
||||||
|
if (doDesignations) {
|
||||||
if (vs.hasLanguage()) {
|
if (vs.hasLanguage()) {
|
||||||
tdDisp.tx(" - "+describeLang(vs.getLanguage()));
|
tdDisp.tx(" - "+describeLang(vs.getLanguage()));
|
||||||
}
|
}
|
||||||
|
for (String url : designations.keySet()) {
|
||||||
|
tr.td().b().addText(designations.get(url));
|
||||||
|
}
|
||||||
for (String lang : langs) {
|
for (String lang : langs) {
|
||||||
tr.td().b().addText(describeLang(lang));
|
tr.td().b().addText(describeLang(lang));
|
||||||
}
|
}
|
||||||
|
@ -242,22 +246,31 @@ public class ValueSetRenderer extends TerminologyRenderer {
|
||||||
|
|
||||||
addMapHeaders(tr, maps);
|
addMapHeaders(tr, maps);
|
||||||
for (ValueSetExpansionContainsComponent c : vs.getExpansion().getContains()) {
|
for (ValueSetExpansionContainsComponent c : vs.getExpansion().getContains()) {
|
||||||
addExpansionRowToTable(t, c, 1, doLevel, doSystem, doDefinition, maps, allCS, langs, doLangs);
|
addExpansionRowToTable(t, c, 1, doLevel, doSystem, doDefinition, maps, allCS, langs, designations, doDesignations);
|
||||||
}
|
}
|
||||||
|
|
||||||
// now, build observed languages
|
// now, build observed languages
|
||||||
|
|
||||||
if (!doLangs && langs.size() > 0) {
|
if (!doDesignations && langs.size() + designations.size() > 0) {
|
||||||
Collections.sort(langs);
|
Collections.sort(langs);
|
||||||
x.para().b().tx("Additional Language Displays");
|
if (designations.size() == 0) {
|
||||||
t = x.table( "codes");
|
x.para().b().tx("Additional Language Displays");
|
||||||
|
} else if (langs.size() == 0) {
|
||||||
|
x.para().b().tx("Additional Designations");
|
||||||
|
} else {
|
||||||
|
x.para().b().tx("Additional Designations and Language Displays");
|
||||||
|
}
|
||||||
|
t = x.table("codes");
|
||||||
tr = t.tr();
|
tr = t.tr();
|
||||||
tr.td().b().tx("Code");
|
tr.td().b().tx("Code");
|
||||||
|
for (String url : designations.keySet()) {
|
||||||
|
tr.td().b().addText(designations.get(url));
|
||||||
|
}
|
||||||
for (String lang : langs) {
|
for (String lang : langs) {
|
||||||
tr.td().b().addText(describeLang(lang));
|
tr.td().b().addText(describeLang(lang));
|
||||||
}
|
}
|
||||||
for (ValueSetExpansionContainsComponent c : vs.getExpansion().getContains()) {
|
for (ValueSetExpansionContainsComponent c : vs.getExpansion().getContains()) {
|
||||||
addLanguageRow(c, t, langs);
|
addDesignationRow(c, t, langs, designations);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -556,12 +569,27 @@ public class ValueSetRenderer extends TerminologyRenderer {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addLanguageRow(ValueSetExpansionContainsComponent c, XhtmlNode t, List<String> langs) {
|
private void addDesignationRow(ValueSetExpansionContainsComponent c, XhtmlNode t, List<String> langs, Map<String, String> designations) {
|
||||||
XhtmlNode tr = t.tr();
|
XhtmlNode tr = t.tr();
|
||||||
tr.td().addText(c.getCode());
|
tr.td().addText(c.getCode());
|
||||||
|
addDesignationsToRow(c, designations, tr);
|
||||||
addLangaugesToRow(c, langs, tr);
|
addLangaugesToRow(c, langs, tr);
|
||||||
for (ValueSetExpansionContainsComponent cc : c.getContains()) {
|
for (ValueSetExpansionContainsComponent cc : c.getContains()) {
|
||||||
addLanguageRow(cc, t, langs);
|
addDesignationRow(cc, t, langs, designations);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addDesignationsToRow(ValueSetExpansionContainsComponent c, Map<String, String> designations, XhtmlNode tr) {
|
||||||
|
for (String url : designations.keySet()) {
|
||||||
|
String d = null;
|
||||||
|
if (d == null) {
|
||||||
|
for (ConceptReferenceDesignationComponent dd : c.getDesignation()) {
|
||||||
|
if (url.equals(getUrlForDesignation(dd))) {
|
||||||
|
d = dd.getValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tr.td().addText(d == null ? "" : d);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -632,6 +660,36 @@ public class ValueSetRenderer extends TerminologyRenderer {
|
||||||
return ref.replace("\\", "/");
|
return ref.replace("\\", "/");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void scanForDesignations(ValueSetExpansionContainsComponent c, List<String> langs, Map<String, String> designations) {
|
||||||
|
for (Extension ext : c.getExtension()) {
|
||||||
|
if (ToolingExtensions.EXT_TRANSLATION.equals(ext.getUrl())) {
|
||||||
|
String lang = ToolingExtensions.readStringExtension(ext, "lang");
|
||||||
|
if (!Utilities.noString(lang) && !langs.contains(lang)) {
|
||||||
|
langs.add(lang);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (ConceptReferenceDesignationComponent d : c.getDesignation()) {
|
||||||
|
String lang = d.getLanguage();
|
||||||
|
if (!Utilities.noString(lang) && !langs.contains(lang)) {
|
||||||
|
langs.add(lang);
|
||||||
|
} else {
|
||||||
|
// can we present this as a designation that we know?
|
||||||
|
String disp = getDisplayForDesignation(d);
|
||||||
|
String url = getUrlForDesignation(d);
|
||||||
|
if (disp == null) {
|
||||||
|
disp = getDisplayForUrl(url);
|
||||||
|
}
|
||||||
|
if (disp != null && !designations.containsKey(url)) {
|
||||||
|
designations.put(url, disp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (ValueSetExpansionContainsComponent cc : c.getContains()) {
|
||||||
|
scanForDesignations(cc, langs, designations);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void scanForLangs(ValueSetExpansionContainsComponent c, List<String> langs) {
|
private void scanForLangs(ValueSetExpansionContainsComponent c, List<String> langs) {
|
||||||
for (Extension ext : c.getExtension()) {
|
for (Extension ext : c.getExtension()) {
|
||||||
if (ToolingExtensions.EXT_TRANSLATION.equals(ext.getUrl())) {
|
if (ToolingExtensions.EXT_TRANSLATION.equals(ext.getUrl())) {
|
||||||
|
@ -651,8 +709,8 @@ public class ValueSetRenderer extends TerminologyRenderer {
|
||||||
scanForLangs(cc, langs);
|
scanForLangs(cc, langs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addExpansionRowToTable(XhtmlNode t, ValueSetExpansionContainsComponent c, int i, boolean doLevel, boolean doSystem, boolean doDefinition, List<UsedConceptMap> maps, CodeSystem allCS, List<String> langs, boolean doLangs) {
|
private void addExpansionRowToTable(XhtmlNode t, ValueSetExpansionContainsComponent c, int i, boolean doLevel, boolean doSystem, boolean doDefinition, List<UsedConceptMap> maps, CodeSystem allCS, List<String> langs, Map<String, String> designations, boolean doDesignations) {
|
||||||
XhtmlNode tr = t.tr();
|
XhtmlNode tr = t.tr();
|
||||||
XhtmlNode td = tr.td();
|
XhtmlNode td = tr.td();
|
||||||
|
|
||||||
|
@ -697,11 +755,12 @@ public class ValueSetRenderer extends TerminologyRenderer {
|
||||||
td.i().tx("("+mapping.comp.getComment()+")");
|
td.i().tx("("+mapping.comp.getComment()+")");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (doLangs) {
|
if (doDesignations) {
|
||||||
|
addDesignationsToRow(c, designations, tr);
|
||||||
addLangaugesToRow(c, langs, tr);
|
addLangaugesToRow(c, langs, tr);
|
||||||
}
|
}
|
||||||
for (ValueSetExpansionContainsComponent cc : c.getContains()) {
|
for (ValueSetExpansionContainsComponent cc : c.getContains()) {
|
||||||
addExpansionRowToTable(t, cc, i+1, doLevel, doSystem, doDefinition, maps, allCS, langs, doLangs);
|
addExpansionRowToTable(t, cc, i+1, doLevel, doSystem, doDefinition, maps, allCS, langs, designations, doDesignations);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -721,7 +780,7 @@ public class ValueSetRenderer extends TerminologyRenderer {
|
||||||
|
|
||||||
private void addCodeToTable(boolean isAbstract, String system, String code, String display, XhtmlNode td) {
|
private void addCodeToTable(boolean isAbstract, String system, String code, String display, XhtmlNode td) {
|
||||||
CodeSystem e = getContext().getWorker().fetchCodeSystem(system);
|
CodeSystem e = getContext().getWorker().fetchCodeSystem(system);
|
||||||
if (e == null || e.getContent() != org.hl7.fhir.r5.model.CodeSystem.CodeSystemContentMode.COMPLETE) {
|
if (e == null || (e.getContent() != org.hl7.fhir.r5.model.CodeSystem.CodeSystemContentMode.COMPLETE && e.getContent() != org.hl7.fhir.r5.model.CodeSystem.CodeSystemContentMode.FRAGMENT)) {
|
||||||
if (isAbstract)
|
if (isAbstract)
|
||||||
td.i().setAttribute("title", ABSTRACT_CODE_HINT).addText(code);
|
td.i().setAttribute("title", ABSTRACT_CODE_HINT).addText(code);
|
||||||
else if ("http://snomed.info/sct".equals(system)) {
|
else if ("http://snomed.info/sct".equals(system)) {
|
||||||
|
@ -824,7 +883,7 @@ public class ValueSetRenderer extends TerminologyRenderer {
|
||||||
}
|
}
|
||||||
for (ConceptSetComponent c : vs.getCompose().getInclude()) {
|
for (ConceptSetComponent c : vs.getCompose().getInclude()) {
|
||||||
for (ConceptReferenceComponent cc : c.getConcept()) {
|
for (ConceptReferenceComponent cc : c.getConcept()) {
|
||||||
addLanguageRow(cc, t, langs);
|
addDesignationRow(cc, t, langs, designations);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -939,9 +998,12 @@ public class ValueSetRenderer extends TerminologyRenderer {
|
||||||
if (!Utilities.noString(lang) && !langs.contains(lang)) {
|
if (!Utilities.noString(lang) && !langs.contains(lang)) {
|
||||||
langs.add(lang);
|
langs.add(lang);
|
||||||
} else {
|
} else {
|
||||||
// can we present this as a designation that we know?
|
// can we present this as a designation that we know?
|
||||||
String url = getUrlForDesignation(d);
|
String disp = getDisplayForDesignation(d);
|
||||||
String disp = getDisplayForUrl(url);
|
String url = getUrlForDesignation(d);
|
||||||
|
if (disp == null) {
|
||||||
|
disp = getDisplayForUrl(url);
|
||||||
|
}
|
||||||
if (disp != null && !designations.containsKey(url)) {
|
if (disp != null && !designations.containsKey(url)) {
|
||||||
designations.put(url, disp);
|
designations.put(url, disp);
|
||||||
}
|
}
|
||||||
|
@ -960,7 +1022,8 @@ public class ValueSetRenderer extends TerminologyRenderer {
|
||||||
case "http://snomed.info/sct#900000000000013009":
|
case "http://snomed.info/sct#900000000000013009":
|
||||||
return "Synonym";
|
return "Synonym";
|
||||||
default:
|
default:
|
||||||
return null;
|
// As specified in http://www.hl7.org/fhir/valueset-definitions.html#ValueSet.compose.include.concept.designation.use and in http://www.hl7.org/fhir/codesystem-definitions.html#CodeSystem.concept.designation.use the terminology binding is extensible.
|
||||||
|
return url;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -972,6 +1035,14 @@ public class ValueSetRenderer extends TerminologyRenderer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String getDisplayForDesignation(ConceptReferenceDesignationComponent d) {
|
||||||
|
if (d.hasUse() && d.getUse().hasDisplay()) {
|
||||||
|
return d.getUse().getDisplay();
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private boolean genInclude(XhtmlNode ul, ConceptSetComponent inc, String type, List<String> langs, boolean doDesignations, List<UsedConceptMap> maps, Map<String, String> designations, int index) throws FHIRException, IOException {
|
private boolean genInclude(XhtmlNode ul, ConceptSetComponent inc, String type, List<String> langs, boolean doDesignations, List<UsedConceptMap> maps, Map<String, String> designations, int index) throws FHIRException, IOException {
|
||||||
boolean hasExtensions = false;
|
boolean hasExtensions = false;
|
||||||
XhtmlNode li;
|
XhtmlNode li;
|
||||||
|
@ -1243,18 +1314,12 @@ public class ValueSetRenderer extends TerminologyRenderer {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void addLanguageRow(ConceptReferenceComponent c, XhtmlNode t, List<String> langs) {
|
|
||||||
|
private void addDesignationRow(ConceptReferenceComponent c, XhtmlNode t, List<String> langs, Map<String, String> designations) {
|
||||||
XhtmlNode tr = t.tr();
|
XhtmlNode tr = t.tr();
|
||||||
tr.td().addText(c.getCode());
|
tr.td().addText(c.getCode());
|
||||||
for (String lang : langs) {
|
addDesignationsToRow(c, designations, tr);
|
||||||
String d = null;
|
addLangaugesToRow(c, langs, tr);
|
||||||
for (ConceptReferenceDesignationComponent cd : c.getDesignation()) {
|
|
||||||
String l = cd.getLanguage();
|
|
||||||
if (lang.equals(l))
|
|
||||||
d = cd.getValue();
|
|
||||||
}
|
|
||||||
tr.td().addText(d == null ? "" : d);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,357 @@
|
||||||
|
package org.hl7.fhir.r5.test.utils;
|
||||||
|
|
||||||
|
import org.apache.commons.codec.binary.Base64;
|
||||||
|
import org.hl7.fhir.utilities.CSFile;
|
||||||
|
import org.hl7.fhir.utilities.TextFile;
|
||||||
|
import org.hl7.fhir.utilities.ToolGlobalSettings;
|
||||||
|
import org.hl7.fhir.utilities.Utilities;
|
||||||
|
|
||||||
|
import org.w3c.dom.Document;
|
||||||
|
import org.w3c.dom.Element;
|
||||||
|
import org.w3c.dom.NamedNodeMap;
|
||||||
|
import org.w3c.dom.Node;
|
||||||
|
|
||||||
|
import com.google.gson.JsonArray;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonNull;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import com.google.gson.JsonPrimitive;
|
||||||
|
import com.google.gson.JsonSyntaxException;
|
||||||
|
import org.hl7.fhir.utilities.tests.BaseTestingUtilities;
|
||||||
|
|
||||||
|
import javax.xml.parsers.DocumentBuilder;
|
||||||
|
import javax.xml.parsers.DocumentBuilderFactory;
|
||||||
|
import java.io.*;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class CompareUtilities extends BaseTestingUtilities {
|
||||||
|
|
||||||
|
private static final boolean SHOW_DIFF = true;
|
||||||
|
|
||||||
|
public static String createNotEqualMessage(final String message, final String expected, final String actual) {
|
||||||
|
return new StringBuilder()
|
||||||
|
.append(message).append('\n')
|
||||||
|
.append("Expected :").append(expected).append('\n')
|
||||||
|
.append("Actual :").append(actual).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String checkXMLIsSame(InputStream expected, InputStream actual) throws Exception {
|
||||||
|
String result = compareXml(expected, actual);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String checkXMLIsSame(String expected, String actual) throws Exception {
|
||||||
|
String result = compareXml(expected, actual);
|
||||||
|
if (result != null && SHOW_DIFF) {
|
||||||
|
String diff = ToolGlobalSettings.hasComparePath() ? ToolGlobalSettings.getComparePath() : Utilities.path(System.getenv("ProgramFiles"), "WinMerge", "WinMergeU.exe");
|
||||||
|
if (new File(diff).exists() || Utilities.isToken(diff)) {
|
||||||
|
Runtime.getRuntime().exec(new String[]{diff, expected, actual});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String compareXml(InputStream expected, InputStream actual) throws Exception {
|
||||||
|
return compareElements("", loadXml(expected).getDocumentElement(), loadXml(actual).getDocumentElement());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String compareXml(String expected, String actual) throws Exception {
|
||||||
|
return compareElements("", loadXml(expected).getDocumentElement(), loadXml(actual).getDocumentElement());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String compareElements(String path, Element expectedElement, Element actualElement) {
|
||||||
|
if (!namespacesMatch(expectedElement.getNamespaceURI(), actualElement.getNamespaceURI()))
|
||||||
|
return createNotEqualMessage("Namespaces differ at " + path, expectedElement.getNamespaceURI(), actualElement.getNamespaceURI());
|
||||||
|
if (!expectedElement.getLocalName().equals(actualElement.getLocalName()))
|
||||||
|
return createNotEqualMessage("Names differ at " + path , expectedElement.getLocalName(), actualElement.getLocalName());
|
||||||
|
path = path + "/" + expectedElement.getLocalName();
|
||||||
|
String s = compareAttributes(path, expectedElement.getAttributes(), actualElement.getAttributes());
|
||||||
|
if (!Utilities.noString(s))
|
||||||
|
return s;
|
||||||
|
s = compareAttributes(path, expectedElement.getAttributes(), actualElement.getAttributes());
|
||||||
|
if (!Utilities.noString(s))
|
||||||
|
return s;
|
||||||
|
|
||||||
|
Node expectedChild = expectedElement.getFirstChild();
|
||||||
|
Node actualChild = actualElement.getFirstChild();
|
||||||
|
expectedChild = skipBlankText(expectedChild);
|
||||||
|
actualChild = skipBlankText(actualChild);
|
||||||
|
while (expectedChild != null && actualChild != null) {
|
||||||
|
if (expectedChild.getNodeType() != actualChild.getNodeType())
|
||||||
|
return createNotEqualMessage("node type mismatch in children of " + path, Short.toString(expectedElement.getNodeType()), Short.toString(actualElement.getNodeType()));
|
||||||
|
if (expectedChild.getNodeType() == Node.TEXT_NODE) {
|
||||||
|
if (!normalise(expectedChild.getTextContent()).equals(normalise(actualChild.getTextContent())))
|
||||||
|
return createNotEqualMessage("Text differs at " + path, normalise(expectedChild.getTextContent()).toString(), normalise(actualChild.getTextContent()).toString());
|
||||||
|
} else if (expectedChild.getNodeType() == Node.ELEMENT_NODE) {
|
||||||
|
s = compareElements(path, (Element) expectedChild, (Element) actualChild);
|
||||||
|
if (!Utilities.noString(s))
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedChild = skipBlankText(expectedChild.getNextSibling());
|
||||||
|
actualChild = skipBlankText(actualChild.getNextSibling());
|
||||||
|
}
|
||||||
|
if (expectedChild != null)
|
||||||
|
return "node mismatch - more nodes in actual in children of " + path;
|
||||||
|
if (actualChild != null)
|
||||||
|
return "node mismatch - more nodes in expected in children of " + path;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean namespacesMatch(String ns1, String ns2) {
|
||||||
|
return ns1 == null ? ns2 == null : ns1.equals(ns2);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Object normalise(String text) {
|
||||||
|
String result = text.trim().replace('\r', ' ').replace('\n', ' ').replace('\t', ' ');
|
||||||
|
while (result.contains(" "))
|
||||||
|
result = result.replace(" ", " ");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String compareAttributes(String path, NamedNodeMap expected, NamedNodeMap actual) {
|
||||||
|
for (int i = 0; i < expected.getLength(); i++) {
|
||||||
|
|
||||||
|
Node expectedNode = expected.item(i);
|
||||||
|
String expectedNodeName = expectedNode.getNodeName();
|
||||||
|
if (!(expectedNodeName.equals("xmlns") || expectedNodeName.startsWith("xmlns:"))) {
|
||||||
|
Node actualNode = actual.getNamedItem(expectedNodeName);
|
||||||
|
if (actualNode == null)
|
||||||
|
return "Attributes differ at " + path + ": missing attribute " + expectedNodeName;
|
||||||
|
if (!normalise(expectedNode.getTextContent()).equals(normalise(actualNode.getTextContent()))) {
|
||||||
|
byte[] b1 = unBase64(expectedNode.getTextContent());
|
||||||
|
byte[] b2 = unBase64(actualNode.getTextContent());
|
||||||
|
if (!sameBytes(b1, b2))
|
||||||
|
return createNotEqualMessage("Attributes differ at " + path, normalise(expectedNode.getTextContent()).toString(), normalise(actualNode.getTextContent()).toString()) ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean sameBytes(byte[] b1, byte[] b2) {
|
||||||
|
if (b1.length == 0 || b2.length == 0)
|
||||||
|
return false;
|
||||||
|
if (b1.length != b2.length)
|
||||||
|
return false;
|
||||||
|
for (int i = 0; i < b1.length; i++)
|
||||||
|
if (b1[i] != b2[i])
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] unBase64(String text) {
|
||||||
|
return Base64.decodeBase64(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Node skipBlankText(Node node) {
|
||||||
|
while (node != null && (((node.getNodeType() == Node.TEXT_NODE) && Utilities.isWhitespace(node.getTextContent())) || (node.getNodeType() == Node.COMMENT_NODE)))
|
||||||
|
node = node.getNextSibling();
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Document loadXml(String fn) throws Exception {
|
||||||
|
return loadXml(new FileInputStream(fn));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Document loadXml(InputStream fn) throws Exception {
|
||||||
|
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
||||||
|
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
|
||||||
|
factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
|
||||||
|
factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
|
||||||
|
factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
|
||||||
|
factory.setXIncludeAware(false);
|
||||||
|
factory.setExpandEntityReferences(false);
|
||||||
|
|
||||||
|
factory.setNamespaceAware(true);
|
||||||
|
DocumentBuilder builder = factory.newDocumentBuilder();
|
||||||
|
return builder.parse(fn);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String checkJsonSrcIsSame(String expected, String actual) throws JsonSyntaxException, FileNotFoundException, IOException {
|
||||||
|
return checkJsonSrcIsSame(expected, actual, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String checkJsonSrcIsSame(String expectedString, String actualString, boolean showDiff) throws JsonSyntaxException, FileNotFoundException, IOException {
|
||||||
|
String result = compareJsonSrc(expectedString, actualString);
|
||||||
|
if (result != null && SHOW_DIFF && showDiff) {
|
||||||
|
String diff = null;
|
||||||
|
if (System.getProperty("os.name").contains("Linux"))
|
||||||
|
diff = Utilities.path("/", "usr", "bin", "meld");
|
||||||
|
else {
|
||||||
|
if (Utilities.checkFile("WinMerge", Utilities.path(System.getenv("ProgramFiles(X86)"), "WinMerge"), "\\WinMergeU.exe", null))
|
||||||
|
diff = Utilities.path(System.getenv("ProgramFiles(X86)"), "WinMerge", "WinMergeU.exe");
|
||||||
|
else if (Utilities.checkFile("WinMerge", Utilities.path(System.getenv("ProgramFiles(X86)"), "Meld"), "\\Meld.exe", null))
|
||||||
|
diff = Utilities.path(System.getenv("ProgramFiles(X86)"), "Meld", "Meld.exe");
|
||||||
|
}
|
||||||
|
if (diff == null || diff.isEmpty())
|
||||||
|
return result;
|
||||||
|
|
||||||
|
List<String> command = new ArrayList<String>();
|
||||||
|
String expected = Utilities.path("[tmp]", "expected" + expectedString.hashCode() + ".json");
|
||||||
|
String actual = Utilities.path("[tmp]", "actual" + actualString.hashCode() + ".json");
|
||||||
|
TextFile.stringToFile(expectedString, expected);
|
||||||
|
TextFile.stringToFile(actualString, actual);
|
||||||
|
command.add(diff);
|
||||||
|
if (diff.toLowerCase().contains("meld"))
|
||||||
|
command.add("--newtab");
|
||||||
|
command.add(expected);
|
||||||
|
command.add(actual);
|
||||||
|
|
||||||
|
ProcessBuilder builder = new ProcessBuilder(command);
|
||||||
|
builder.directory(new CSFile(Utilities.path("[tmp]")));
|
||||||
|
builder.start();
|
||||||
|
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String checkJsonIsSame(String expected, String actual) throws JsonSyntaxException, FileNotFoundException, IOException {
|
||||||
|
String result = compareJson(expected, actual);
|
||||||
|
if (result != null && SHOW_DIFF) {
|
||||||
|
String diff = Utilities.path(System.getenv("ProgramFiles(X86)"), "WinMerge", "WinMergeU.exe");
|
||||||
|
List<String> command = new ArrayList<String>();
|
||||||
|
command.add("\"" + diff + "\" \"" + expected + "\" \"" + actual + "\"");
|
||||||
|
|
||||||
|
ProcessBuilder builder = new ProcessBuilder(command);
|
||||||
|
builder.directory(new CSFile("c:\\temp"));
|
||||||
|
builder.start();
|
||||||
|
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String compareJsonSrc(String expected, String actual) throws JsonSyntaxException, FileNotFoundException, IOException {
|
||||||
|
JsonObject actualJsonObject = (JsonObject) new com.google.gson.JsonParser().parse(actual);
|
||||||
|
JsonObject expectedJsonObject = (JsonObject) new com.google.gson.JsonParser().parse(expected);
|
||||||
|
return compareObjects("", expectedJsonObject, actualJsonObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String compareJson(String expected, String actual) throws JsonSyntaxException, FileNotFoundException, IOException {
|
||||||
|
JsonObject actualJsonObject = (JsonObject) new com.google.gson.JsonParser().parse(TextFile.fileToString(actual));
|
||||||
|
JsonObject expectedJsonObject = (JsonObject) new com.google.gson.JsonParser().parse(TextFile.fileToString(expected));
|
||||||
|
return compareObjects("", expectedJsonObject, actualJsonObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String compareObjects(String path, JsonObject expectedJsonObject, JsonObject actualJsonObject) {
|
||||||
|
for (Map.Entry<String, JsonElement> en : actualJsonObject.entrySet()) {
|
||||||
|
String n = en.getKey();
|
||||||
|
if (!n.equals("fhir_comments")) {
|
||||||
|
if (expectedJsonObject.has(n)) {
|
||||||
|
String s = compareNodes(path + '.' + n, expectedJsonObject.get(n), en.getValue());
|
||||||
|
if (!Utilities.noString(s))
|
||||||
|
return s;
|
||||||
|
} else
|
||||||
|
return "properties differ at " + path + ": missing property " + n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (Map.Entry<String, JsonElement> en : expectedJsonObject.entrySet()) {
|
||||||
|
String n = en.getKey();
|
||||||
|
if (!n.equals("fhir_comments")) {
|
||||||
|
if (!actualJsonObject.has(n))
|
||||||
|
return "properties differ at " + path + ": missing property " + n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String compareNodes(String path, JsonElement expectedJsonElement, JsonElement actualJsonElement) {
|
||||||
|
if (actualJsonElement.getClass() != expectedJsonElement.getClass())
|
||||||
|
return createNotEqualMessage("properties differ at " + path, expectedJsonElement.getClass().getName(), actualJsonElement.getClass().getName());
|
||||||
|
else if (actualJsonElement instanceof JsonPrimitive) {
|
||||||
|
JsonPrimitive actualJsonPrimitive = (JsonPrimitive) actualJsonElement;
|
||||||
|
JsonPrimitive expectedJsonPrimitive = (JsonPrimitive) expectedJsonElement;
|
||||||
|
if (actualJsonPrimitive.isBoolean() && expectedJsonPrimitive.isBoolean()) {
|
||||||
|
if (actualJsonPrimitive.getAsBoolean() != expectedJsonPrimitive.getAsBoolean())
|
||||||
|
return createNotEqualMessage("boolean property values differ at " + path , expectedJsonPrimitive.getAsString(), actualJsonPrimitive.getAsString());
|
||||||
|
} else if (actualJsonPrimitive.isString() && expectedJsonPrimitive.isString()) {
|
||||||
|
String actualJsonString = actualJsonPrimitive.getAsString();
|
||||||
|
String expectedJsonString = expectedJsonPrimitive.getAsString();
|
||||||
|
if (!(actualJsonString.contains("<div") && expectedJsonString.contains("<div")))
|
||||||
|
if (!actualJsonString.equals(expectedJsonString))
|
||||||
|
if (!sameBytes(unBase64(actualJsonString), unBase64(expectedJsonString)))
|
||||||
|
return createNotEqualMessage("string property values differ at " + path, expectedJsonString, actualJsonString);
|
||||||
|
} else if (actualJsonPrimitive.isNumber() && expectedJsonPrimitive.isNumber()) {
|
||||||
|
if (!actualJsonPrimitive.getAsString().equals(expectedJsonPrimitive.getAsString()))
|
||||||
|
return createNotEqualMessage("number property values differ at " + path, expectedJsonPrimitive.getAsString(), actualJsonPrimitive.getAsString());
|
||||||
|
} else
|
||||||
|
return createNotEqualMessage("property types differ at " + path, expectedJsonPrimitive.getAsString(), actualJsonPrimitive.getAsString());
|
||||||
|
} else if (actualJsonElement instanceof JsonObject) {
|
||||||
|
String s = compareObjects(path, (JsonObject) expectedJsonElement, (JsonObject) actualJsonElement);
|
||||||
|
if (!Utilities.noString(s))
|
||||||
|
return s;
|
||||||
|
} else if (actualJsonElement instanceof JsonArray) {
|
||||||
|
JsonArray actualArray = (JsonArray) actualJsonElement;
|
||||||
|
JsonArray expectedArray = (JsonArray) expectedJsonElement;
|
||||||
|
|
||||||
|
if (actualArray.size() != expectedArray.size())
|
||||||
|
return createNotEqualMessage("array properties count differs at " + path, Integer.toString(expectedArray.size()), Integer.toString(actualArray.size()));
|
||||||
|
for (int i = 0; i < actualArray.size(); i++) {
|
||||||
|
String s = compareNodes(path + "[" + Integer.toString(i) + "]", expectedArray.get(i), actualArray.get(i));
|
||||||
|
if (!Utilities.noString(s))
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
} else if (actualJsonElement instanceof JsonNull) {
|
||||||
|
|
||||||
|
} else
|
||||||
|
return "unhandled property " + actualJsonElement.getClass().getName();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String temp() {
|
||||||
|
if (new File("c:\\temp").exists())
|
||||||
|
return "c:\\temp";
|
||||||
|
return System.getProperty("java.io.tmpdir");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String checkTextIsSame(String expected, String actual) throws JsonSyntaxException, FileNotFoundException, IOException {
|
||||||
|
return checkTextIsSame(expected, actual, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String checkTextIsSame(String expectedString, String actualString, boolean showDiff) throws JsonSyntaxException, FileNotFoundException, IOException {
|
||||||
|
String result = compareText(expectedString, actualString);
|
||||||
|
if (result != null && SHOW_DIFF && showDiff) {
|
||||||
|
String diff = null;
|
||||||
|
if (System.getProperty("os.name").contains("Linux"))
|
||||||
|
diff = Utilities.path("/", "usr", "bin", "meld");
|
||||||
|
else {
|
||||||
|
if (Utilities.checkFile("WinMerge", Utilities.path(System.getenv("ProgramFiles(X86)"), "WinMerge"), "\\WinMergeU.exe", null))
|
||||||
|
diff = Utilities.path(System.getenv("ProgramFiles(X86)"), "WinMerge", "WinMergeU.exe");
|
||||||
|
else if (Utilities.checkFile("WinMerge", Utilities.path(System.getenv("ProgramFiles(X86)"), "Meld"), "\\Meld.exe", null))
|
||||||
|
diff = Utilities.path(System.getenv("ProgramFiles(X86)"), "Meld", "Meld.exe");
|
||||||
|
}
|
||||||
|
if (diff == null || diff.isEmpty())
|
||||||
|
return result;
|
||||||
|
|
||||||
|
List<String> command = new ArrayList<String>();
|
||||||
|
String actual = Utilities.path("[tmp]", "actual" + actualString.hashCode() + ".json");
|
||||||
|
String expected = Utilities.path("[tmp]", "expected" + expectedString.hashCode() + ".json");
|
||||||
|
TextFile.stringToFile(expectedString, expected);
|
||||||
|
TextFile.stringToFile(actualString, actual);
|
||||||
|
command.add(diff);
|
||||||
|
if (diff.toLowerCase().contains("meld"))
|
||||||
|
command.add("--newtab");
|
||||||
|
command.add(expected);
|
||||||
|
command.add(actual);
|
||||||
|
|
||||||
|
ProcessBuilder builder = new ProcessBuilder(command);
|
||||||
|
builder.directory(new CSFile(Utilities.path("[tmp]")));
|
||||||
|
builder.start();
|
||||||
|
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static String compareText(String expectedString, String actualString) {
|
||||||
|
for (int i = 0; i < Integer.min(expectedString.length(), actualString.length()); i++) {
|
||||||
|
if (expectedString.charAt(i) != actualString.charAt(i))
|
||||||
|
return createNotEqualMessage("Strings differ at character " + Integer.toString(i), String.valueOf(expectedString.charAt(i)), String.valueOf(actualString.charAt(i)));
|
||||||
|
}
|
||||||
|
if (expectedString.length() != actualString.length())
|
||||||
|
return createNotEqualMessage("Strings differ in length but match to the end of the shortest.", Integer.toString(expectedString.length()), Integer.toString(actualString.length()));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -71,7 +71,6 @@ import com.google.gson.JsonPrimitive;
|
||||||
import com.google.gson.JsonSyntaxException;
|
import com.google.gson.JsonSyntaxException;
|
||||||
|
|
||||||
public class TestingUtilities extends BaseTestingUtilities {
|
public class TestingUtilities extends BaseTestingUtilities {
|
||||||
private static final boolean SHOW_DIFF = true;
|
|
||||||
|
|
||||||
static public Map<String, IWorkerContext> fcontexts;
|
static public Map<String, IWorkerContext> fcontexts;
|
||||||
|
|
||||||
|
@ -178,346 +177,5 @@ public class TestingUtilities extends BaseTestingUtilities {
|
||||||
throw new Error("FHIR US directory not configured");
|
throw new Error("FHIR US directory not configured");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String checkXMLIsSame(InputStream f1, InputStream f2) throws Exception {
|
|
||||||
String result = compareXml(f1, f2);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String checkXMLIsSame(String f1, String f2) throws Exception {
|
|
||||||
String result = compareXml(f1, f2);
|
|
||||||
if (result != null && SHOW_DIFF) {
|
|
||||||
String diff = ToolGlobalSettings.hasComparePath() ? ToolGlobalSettings.getComparePath() : Utilities.path(System.getenv("ProgramFiles"), "WinMerge", "WinMergeU.exe");
|
|
||||||
if (new File(diff).exists() || Utilities.isToken(diff)) {
|
|
||||||
List<String> command = new ArrayList<String>();
|
|
||||||
Process p = Runtime.getRuntime().exec(new String[]{diff, f1, f2});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String compareXml(InputStream f1, InputStream f2) throws Exception {
|
|
||||||
return compareElements("", loadXml(f1).getDocumentElement(), loadXml(f2).getDocumentElement());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String compareXml(String f1, String f2) throws Exception {
|
|
||||||
return compareElements("", loadXml(f1).getDocumentElement(), loadXml(f2).getDocumentElement());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String compareElements(String path, Element e1, Element e2) {
|
|
||||||
if (!namespacesMatch(e1.getNamespaceURI(), e2.getNamespaceURI()))
|
|
||||||
return "Namespaces differ at " + path + ": " + e1.getNamespaceURI() + "/" + e2.getNamespaceURI();
|
|
||||||
if (!e1.getLocalName().equals(e2.getLocalName()))
|
|
||||||
return "Names differ at " + path + ": " + e1.getLocalName() + "/" + e2.getLocalName();
|
|
||||||
path = path + "/" + e1.getLocalName();
|
|
||||||
String s = compareAttributes(path, e1.getAttributes(), e2.getAttributes());
|
|
||||||
if (!Utilities.noString(s))
|
|
||||||
return s;
|
|
||||||
s = compareAttributes(path, e2.getAttributes(), e1.getAttributes());
|
|
||||||
if (!Utilities.noString(s))
|
|
||||||
return s;
|
|
||||||
|
|
||||||
Node c1 = e1.getFirstChild();
|
|
||||||
Node c2 = e2.getFirstChild();
|
|
||||||
c1 = skipBlankText(c1);
|
|
||||||
c2 = skipBlankText(c2);
|
|
||||||
while (c1 != null && c2 != null) {
|
|
||||||
if (c1.getNodeType() != c2.getNodeType())
|
|
||||||
return "node type mismatch in children of " + path + ": " + Integer.toString(e1.getNodeType()) + "/" + Integer.toString(e2.getNodeType());
|
|
||||||
if (c1.getNodeType() == Node.TEXT_NODE) {
|
|
||||||
if (!normalise(c1.getTextContent()).equals(normalise(c2.getTextContent())))
|
|
||||||
return "Text differs at " + path + ": " + normalise(c1.getTextContent()) + "/" + normalise(c2.getTextContent());
|
|
||||||
} else if (c1.getNodeType() == Node.ELEMENT_NODE) {
|
|
||||||
s = compareElements(path, (Element) c1, (Element) c2);
|
|
||||||
if (!Utilities.noString(s))
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
c1 = skipBlankText(c1.getNextSibling());
|
|
||||||
c2 = skipBlankText(c2.getNextSibling());
|
|
||||||
}
|
|
||||||
if (c1 != null)
|
|
||||||
return "node mismatch - more nodes in source in children of " + path;
|
|
||||||
if (c2 != null)
|
|
||||||
return "node mismatch - more nodes in target in children of " + path;
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean namespacesMatch(String ns1, String ns2) {
|
|
||||||
return ns1 == null ? ns2 == null : ns1.equals(ns2);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Object normalise(String text) {
|
|
||||||
String result = text.trim().replace('\r', ' ').replace('\n', ' ').replace('\t', ' ');
|
|
||||||
while (result.contains(" "))
|
|
||||||
result = result.replace(" ", " ");
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String compareAttributes(String path, NamedNodeMap src, NamedNodeMap tgt) {
|
|
||||||
for (int i = 0; i < src.getLength(); i++) {
|
|
||||||
|
|
||||||
Node sa = src.item(i);
|
|
||||||
String sn = sa.getNodeName();
|
|
||||||
if (!(sn.equals("xmlns") || sn.startsWith("xmlns:"))) {
|
|
||||||
Node ta = tgt.getNamedItem(sn);
|
|
||||||
if (ta == null)
|
|
||||||
return "Attributes differ at " + path + ": missing attribute " + sn;
|
|
||||||
if (!normalise(sa.getTextContent()).equals(normalise(ta.getTextContent()))) {
|
|
||||||
byte[] b1 = unBase64(sa.getTextContent());
|
|
||||||
byte[] b2 = unBase64(ta.getTextContent());
|
|
||||||
if (!sameBytes(b1, b2))
|
|
||||||
return "Attributes differ at " + path + ": value " + normalise(sa.getTextContent()) + "/" + normalise(ta.getTextContent());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean sameBytes(byte[] b1, byte[] b2) {
|
|
||||||
if (b1.length == 0 || b2.length == 0)
|
|
||||||
return false;
|
|
||||||
if (b1.length != b2.length)
|
|
||||||
return false;
|
|
||||||
for (int i = 0; i < b1.length; i++)
|
|
||||||
if (b1[i] != b2[i])
|
|
||||||
return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static byte[] unBase64(String text) {
|
|
||||||
return Base64.decodeBase64(text);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Node skipBlankText(Node node) {
|
|
||||||
while (node != null && (((node.getNodeType() == Node.TEXT_NODE) && Utilities.isWhitespace(node.getTextContent())) || (node.getNodeType() == Node.COMMENT_NODE)))
|
|
||||||
node = node.getNextSibling();
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Document loadXml(String fn) throws Exception {
|
|
||||||
return loadXml(new FileInputStream(fn));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Document loadXml(InputStream fn) throws Exception {
|
|
||||||
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
|
||||||
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
|
|
||||||
factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
|
|
||||||
factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
|
|
||||||
factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
|
|
||||||
factory.setXIncludeAware(false);
|
|
||||||
factory.setExpandEntityReferences(false);
|
|
||||||
|
|
||||||
factory.setNamespaceAware(true);
|
|
||||||
DocumentBuilder builder = factory.newDocumentBuilder();
|
|
||||||
return builder.parse(fn);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String checkJsonSrcIsSame(String s1, String s2) throws JsonSyntaxException, FileNotFoundException, IOException {
|
|
||||||
return checkJsonSrcIsSame(s1, s2, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String checkJsonSrcIsSame(String s1, String s2, boolean showDiff) throws JsonSyntaxException, FileNotFoundException, IOException {
|
|
||||||
String result = compareJsonSrc(s1, s2);
|
|
||||||
if (result != null && SHOW_DIFF && showDiff) {
|
|
||||||
String diff = null;
|
|
||||||
if (System.getProperty("os.name").contains("Linux"))
|
|
||||||
diff = Utilities.path("/", "usr", "bin", "meld");
|
|
||||||
else {
|
|
||||||
if (Utilities.checkFile("WinMerge", Utilities.path(System.getenv("ProgramFiles(X86)"), "WinMerge"), "\\WinMergeU.exe", null))
|
|
||||||
diff = Utilities.path(System.getenv("ProgramFiles(X86)"), "WinMerge", "WinMergeU.exe");
|
|
||||||
else if (Utilities.checkFile("WinMerge", Utilities.path(System.getenv("ProgramFiles(X86)"), "Meld"), "\\Meld.exe", null))
|
|
||||||
diff = Utilities.path(System.getenv("ProgramFiles(X86)"), "Meld", "Meld.exe");
|
|
||||||
}
|
|
||||||
if (diff == null || diff.isEmpty())
|
|
||||||
return result;
|
|
||||||
|
|
||||||
List<String> command = new ArrayList<String>();
|
|
||||||
String f1 = Utilities.path("[tmp]", "input" + s1.hashCode() + ".json");
|
|
||||||
String f2 = Utilities.path("[tmp]", "output" + s2.hashCode() + ".json");
|
|
||||||
TextFile.stringToFile(s1, f1);
|
|
||||||
TextFile.stringToFile(s2, f2);
|
|
||||||
command.add(diff);
|
|
||||||
if (diff.toLowerCase().contains("meld"))
|
|
||||||
command.add("--newtab");
|
|
||||||
command.add(f1);
|
|
||||||
command.add(f2);
|
|
||||||
|
|
||||||
ProcessBuilder builder = new ProcessBuilder(command);
|
|
||||||
builder.directory(new CSFile(Utilities.path("[tmp]")));
|
|
||||||
builder.start();
|
|
||||||
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String checkJsonIsSame(String f1, String f2) throws JsonSyntaxException, FileNotFoundException, IOException {
|
|
||||||
String result = compareJson(f1, f2);
|
|
||||||
if (result != null && SHOW_DIFF) {
|
|
||||||
String diff = Utilities.path(System.getenv("ProgramFiles(X86)"), "WinMerge", "WinMergeU.exe");
|
|
||||||
List<String> command = new ArrayList<String>();
|
|
||||||
command.add("\"" + diff + "\" \"" + f1 + "\" \"" + f2 + "\"");
|
|
||||||
|
|
||||||
ProcessBuilder builder = new ProcessBuilder(command);
|
|
||||||
builder.directory(new CSFile("c:\\temp"));
|
|
||||||
builder.start();
|
|
||||||
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String compareJsonSrc(String f1, String f2) throws JsonSyntaxException, FileNotFoundException, IOException {
|
|
||||||
JsonObject o1 = (JsonObject) new com.google.gson.JsonParser().parse(f1);
|
|
||||||
JsonObject o2 = (JsonObject) new com.google.gson.JsonParser().parse(f2);
|
|
||||||
return compareObjects("", o1, o2);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String compareJson(String f1, String f2) throws JsonSyntaxException, FileNotFoundException, IOException {
|
|
||||||
JsonObject o1 = (JsonObject) new com.google.gson.JsonParser().parse(TextFile.fileToString(f1));
|
|
||||||
JsonObject o2 = (JsonObject) new com.google.gson.JsonParser().parse(TextFile.fileToString(f2));
|
|
||||||
return compareObjects("", o1, o2);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String compareObjects(String path, JsonObject o1, JsonObject o2) {
|
|
||||||
for (Map.Entry<String, JsonElement> en : o1.entrySet()) {
|
|
||||||
String n = en.getKey();
|
|
||||||
if (!n.equals("fhir_comments")) {
|
|
||||||
if (o2.has(n)) {
|
|
||||||
String s = compareNodes(path + '.' + n, en.getValue(), o2.get(n));
|
|
||||||
if (!Utilities.noString(s))
|
|
||||||
return s;
|
|
||||||
} else
|
|
||||||
return "properties differ at " + path + ": missing property " + n;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (Map.Entry<String, JsonElement> en : o2.entrySet()) {
|
|
||||||
String n = en.getKey();
|
|
||||||
if (!n.equals("fhir_comments")) {
|
|
||||||
if (!o1.has(n))
|
|
||||||
return "properties differ at " + path + ": missing property " + n;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String compareNodes(String path, JsonElement n1, JsonElement n2) {
|
|
||||||
if (n1.getClass() != n2.getClass())
|
|
||||||
return "properties differ at " + path + ": type " + n1.getClass().getName() + "/" + n2.getClass().getName();
|
|
||||||
else if (n1 instanceof JsonPrimitive) {
|
|
||||||
JsonPrimitive p1 = (JsonPrimitive) n1;
|
|
||||||
JsonPrimitive p2 = (JsonPrimitive) n2;
|
|
||||||
if (p1.isBoolean() && p2.isBoolean()) {
|
|
||||||
if (p1.getAsBoolean() != p2.getAsBoolean())
|
|
||||||
return "boolean property values differ at " + path + ": type " + p1.getAsString() + "/" + p2.getAsString();
|
|
||||||
} else if (p1.isString() && p2.isString()) {
|
|
||||||
String s1 = p1.getAsString();
|
|
||||||
String s2 = p2.getAsString();
|
|
||||||
if (!(s1.contains("<div") && s2.contains("<div")))
|
|
||||||
if (!s1.equals(s2))
|
|
||||||
if (!sameBytes(unBase64(s1), unBase64(s2)))
|
|
||||||
return "string property values differ at " + path + ": type " + s1 + "/" + s2;
|
|
||||||
} else if (p1.isNumber() && p2.isNumber()) {
|
|
||||||
if (!p1.getAsString().equals(p2.getAsString()))
|
|
||||||
return "number property values differ at " + path + ": type " + p1.getAsString() + "/" + p2.getAsString();
|
|
||||||
} else
|
|
||||||
return "property types differ at " + path + ": type " + p1.getAsString() + "/" + p2.getAsString();
|
|
||||||
} else if (n1 instanceof JsonObject) {
|
|
||||||
String s = compareObjects(path, (JsonObject) n1, (JsonObject) n2);
|
|
||||||
if (!Utilities.noString(s))
|
|
||||||
return s;
|
|
||||||
} else if (n1 instanceof JsonArray) {
|
|
||||||
JsonArray a1 = (JsonArray) n1;
|
|
||||||
JsonArray a2 = (JsonArray) n2;
|
|
||||||
|
|
||||||
if (a1.size() != a2.size())
|
|
||||||
return "array properties differ at " + path + ": count " + Integer.toString(a1.size()) + "/" + Integer.toString(a2.size());
|
|
||||||
for (int i = 0; i < a1.size(); i++) {
|
|
||||||
String s = compareNodes(path + "[" + Integer.toString(i) + "]", a1.get(i), a2.get(i));
|
|
||||||
if (!Utilities.noString(s))
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
} else if (n1 instanceof JsonNull) {
|
|
||||||
|
|
||||||
} else
|
|
||||||
return "unhandled property " + n1.getClass().getName();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String temp() {
|
|
||||||
if (new File("c:\\temp").exists())
|
|
||||||
return "c:\\temp";
|
|
||||||
return System.getProperty("java.io.tmpdir");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String checkTextIsSame(String s1, String s2) throws JsonSyntaxException, FileNotFoundException, IOException {
|
|
||||||
return checkTextIsSame(s1, s2, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String checkTextIsSame(String s1, String s2, boolean showDiff) throws JsonSyntaxException, FileNotFoundException, IOException {
|
|
||||||
String result = compareText(s1, s2);
|
|
||||||
if (result != null && SHOW_DIFF && showDiff) {
|
|
||||||
String diff = null;
|
|
||||||
if (System.getProperty("os.name").contains("Linux"))
|
|
||||||
diff = Utilities.path("/", "usr", "bin", "meld");
|
|
||||||
else {
|
|
||||||
if (Utilities.checkFile("WinMerge", Utilities.path(System.getenv("ProgramFiles(X86)"), "WinMerge"), "\\WinMergeU.exe", null))
|
|
||||||
diff = Utilities.path(System.getenv("ProgramFiles(X86)"), "WinMerge", "WinMergeU.exe");
|
|
||||||
else if (Utilities.checkFile("WinMerge", Utilities.path(System.getenv("ProgramFiles(X86)"), "Meld"), "\\Meld.exe", null))
|
|
||||||
diff = Utilities.path(System.getenv("ProgramFiles(X86)"), "Meld", "Meld.exe");
|
|
||||||
}
|
|
||||||
if (diff == null || diff.isEmpty())
|
|
||||||
return result;
|
|
||||||
|
|
||||||
List<String> command = new ArrayList<String>();
|
|
||||||
String f1 = Utilities.path("[tmp]", "input" + s1.hashCode() + ".json");
|
|
||||||
String f2 = Utilities.path("[tmp]", "output" + s2.hashCode() + ".json");
|
|
||||||
TextFile.stringToFile(s1, f1);
|
|
||||||
TextFile.stringToFile(s2, f2);
|
|
||||||
command.add(diff);
|
|
||||||
if (diff.toLowerCase().contains("meld"))
|
|
||||||
command.add("--newtab");
|
|
||||||
command.add(f1);
|
|
||||||
command.add(f2);
|
|
||||||
|
|
||||||
ProcessBuilder builder = new ProcessBuilder(command);
|
|
||||||
builder.directory(new CSFile(Utilities.path("[tmp]")));
|
|
||||||
builder.start();
|
|
||||||
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private static String compareText(String s1, String s2) {
|
|
||||||
for (int i = 0; i < Integer.min(s1.length(), s2.length()); i++) {
|
|
||||||
if (s1.charAt(i) != s2.charAt(i))
|
|
||||||
return "Strings differ at character " + Integer.toString(i) + ": '" + s1.charAt(i) + "' vs '" + s2.charAt(i) + "'";
|
|
||||||
}
|
|
||||||
if (s1.length() != s2.length())
|
|
||||||
return "Strings differ in length: " + Integer.toString(s1.length()) + " vs " + Integer.toString(s2.length()) + " but match to the end of the shortest";
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String tempFile(String folder, String name) throws IOException {
|
|
||||||
String tmp = tempFolder(folder);
|
|
||||||
return Utilities.path(tmp, name);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String tempFolder(String name) throws IOException {
|
|
||||||
File tmp = new File("C:\\temp");
|
|
||||||
if (tmp.exists() && tmp.isDirectory()) {
|
|
||||||
String path = Utilities.path("C:\\temp", name);
|
|
||||||
Utilities.createDirectory(path);
|
|
||||||
return path;
|
|
||||||
} else if (ToolGlobalSettings.hasTempPath()) {
|
|
||||||
return ToolGlobalSettings.getTempPath();
|
|
||||||
} else if (new File("/tmp").exists()) {
|
|
||||||
String path = Utilities.path("/tmp", name);
|
|
||||||
Utilities.createDirectory(path);
|
|
||||||
return path;
|
|
||||||
} else {
|
|
||||||
String path = Utilities.path(System.getProperty("java.io.tmpdir"), name);
|
|
||||||
Utilities.createDirectory(path);
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -2,16 +2,20 @@ package org.hl7.fhir.r5.model;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
|
||||||
|
import org.apache.commons.codec.binary.Base64;
|
||||||
import org.junit.jupiter.api.Assertions;
|
import org.junit.jupiter.api.Assertions;
|
||||||
import org.junit.jupiter.api.DisplayName;
|
import org.junit.jupiter.api.DisplayName;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import ca.uhn.fhir.parser.DataFormatException;
|
import ca.uhn.fhir.parser.DataFormatException;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
class Base64BinaryTypeTest {
|
class Base64BinaryTypeTest {
|
||||||
|
|
||||||
static final String NON_BASE_64 = "Picard was the best starship captain.";
|
static final String NON_BASE_64 = "Picard was the best starship captain.";
|
||||||
static final String VALID_BASE_64 = "dGhpcyBpcyB2YWxpZCBiYXNlNjQ=";
|
static final String VALID_BASE_64 = "dGhpcyBpcyB2YWxpZCBiYXNlNjQ=";
|
||||||
|
static final byte[] VALID_BASE_64_BYTES = Base64.decodeBase64(VALID_BASE_64.getBytes(ca.uhn.fhir.rest.api.Constants.CHARSET_UTF8));
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("Passing a non Base64 encoded String to constructor causes exception.")
|
@DisplayName("Passing a non Base64 encoded String to constructor causes exception.")
|
||||||
public void testNonBase64String() {
|
public void testNonBase64String() {
|
||||||
|
@ -55,6 +59,15 @@ class Base64BinaryTypeTest {
|
||||||
Assertions.assertEquals(VALID_BASE_64, b64.asStringValue());
|
Assertions.assertEquals(VALID_BASE_64, b64.asStringValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("Valid Base64 String creates non-null instance with non-null bytes.")
|
||||||
|
public void testValidBytes() {
|
||||||
|
Base64BinaryType b64 = new Base64BinaryType(VALID_BASE_64_BYTES);
|
||||||
|
Assertions.assertNotNull(b64);
|
||||||
|
Assertions.assertNotNull(b64.getValue());
|
||||||
|
Assertions.assertEquals(VALID_BASE_64, b64.asStringValue());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("Valid Base64 String creates non-null instance with non-null values.")
|
@DisplayName("Valid Base64 String creates non-null instance with non-null values.")
|
||||||
public void testValidSetValueAsString() {
|
public void testValidSetValueAsString() {
|
||||||
|
|
|
@ -20,6 +20,7 @@ import org.hl7.fhir.r5.model.Bundle.BundleLinkComponent;
|
||||||
import org.hl7.fhir.r5.model.Bundle.SearchEntryMode;
|
import org.hl7.fhir.r5.model.Bundle.SearchEntryMode;
|
||||||
import org.hl7.fhir.r5.model.DomainResource;
|
import org.hl7.fhir.r5.model.DomainResource;
|
||||||
import org.hl7.fhir.r5.model.Resource;
|
import org.hl7.fhir.r5.model.Resource;
|
||||||
|
import org.hl7.fhir.r5.test.utils.CompareUtilities;
|
||||||
import org.hl7.fhir.r5.test.utils.TestingUtilities;
|
import org.hl7.fhir.r5.test.utils.TestingUtilities;
|
||||||
import org.hl7.fhir.r5.utils.GraphQLEngine;
|
import org.hl7.fhir.r5.utils.GraphQLEngine;
|
||||||
import org.hl7.fhir.utilities.TextFile;
|
import org.hl7.fhir.utilities.TextFile;
|
||||||
|
@ -88,10 +89,10 @@ public class GraphQLEngineTests implements IGraphQLStorageServices {
|
||||||
StringBuilder str = new StringBuilder();
|
StringBuilder str = new StringBuilder();
|
||||||
gql.getOutput().setWriteWrapper(false);
|
gql.getOutput().setWriteWrapper(false);
|
||||||
gql.getOutput().write(str, 0);
|
gql.getOutput().write(str, 0);
|
||||||
IOUtils.copy(TestingUtilities.loadTestResourceStream("r5", "graphql", source), new FileOutputStream(TestingUtilities.tempFile("graphql", source)));
|
IOUtils.copy(CompareUtilities.loadTestResourceStream("r5", "graphql", source), new FileOutputStream(CompareUtilities.tempFile("graphql", source)));
|
||||||
IOUtils.copy(TestingUtilities.loadTestResourceStream("r5", "graphql", output), new FileOutputStream(TestingUtilities.tempFile("graphql", output)));
|
IOUtils.copy(CompareUtilities.loadTestResourceStream("r5", "graphql", output), new FileOutputStream(CompareUtilities.tempFile("graphql", output)));
|
||||||
TextFile.stringToFile(str.toString(), TestingUtilities.tempFile("graphql", output+".out"));
|
TextFile.stringToFile(str.toString(), CompareUtilities.tempFile("graphql", output+".out"));
|
||||||
msg = TestingUtilities.checkJsonIsSame(TestingUtilities.tempFile("graphql", output+".out"), TestingUtilities.tempFile("graphql", output));
|
msg = CompareUtilities.checkJsonIsSame(CompareUtilities.tempFile("graphql", output), CompareUtilities.tempFile("graphql", output+".out"));
|
||||||
Assertions.assertTrue(Utilities.noString(msg), msg);
|
Assertions.assertTrue(Utilities.noString(msg), msg);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
@ -22,6 +22,7 @@ import org.hl7.fhir.r5.renderers.utils.ElementWrappers;
|
||||||
import org.hl7.fhir.r5.renderers.utils.RenderingContext;
|
import org.hl7.fhir.r5.renderers.utils.RenderingContext;
|
||||||
import org.hl7.fhir.r5.renderers.utils.RenderingContext.ITypeParser;
|
import org.hl7.fhir.r5.renderers.utils.RenderingContext.ITypeParser;
|
||||||
import org.hl7.fhir.r5.renderers.utils.RenderingContext.ResourceRendererMode;
|
import org.hl7.fhir.r5.renderers.utils.RenderingContext.ResourceRendererMode;
|
||||||
|
import org.hl7.fhir.r5.test.utils.CompareUtilities;
|
||||||
import org.hl7.fhir.r5.test.utils.TestingUtilities;
|
import org.hl7.fhir.r5.test.utils.TestingUtilities;
|
||||||
import org.hl7.fhir.utilities.TerminologyServiceOptions;
|
import org.hl7.fhir.utilities.TerminologyServiceOptions;
|
||||||
import org.hl7.fhir.utilities.TextFile;
|
import org.hl7.fhir.utilities.TextFile;
|
||||||
|
@ -134,11 +135,11 @@ public class NarrativeGenerationTests {
|
||||||
XhtmlNode x = RendererFactory.factory(source, rc).build(source);
|
XhtmlNode x = RendererFactory.factory(source, rc).build(source);
|
||||||
String expected = TextFile.streamToString(TestingUtilities.loadTestResourceStream("r5", "narrative", test.getId() + ".html"));
|
String expected = TextFile.streamToString(TestingUtilities.loadTestResourceStream("r5", "narrative", test.getId() + ".html"));
|
||||||
String actual = HEADER+new XhtmlComposer(true, true).compose(x)+FOOTER;
|
String actual = HEADER+new XhtmlComposer(true, true).compose(x)+FOOTER;
|
||||||
String expectedFileName = TestingUtilities.tempFile("narrative", test.getId() + ".expected.html");
|
String expectedFileName = CompareUtilities.tempFile("narrative", test.getId() + ".expected.html");
|
||||||
String actualFileName = TestingUtilities.tempFile("narrative", test.getId() + ".actual.html");
|
String actualFileName = CompareUtilities.tempFile("narrative", test.getId() + ".actual.html");
|
||||||
TextFile.stringToFile(expected, expectedFileName);
|
TextFile.stringToFile(expected, expectedFileName);
|
||||||
TextFile.stringToFile(actual, actualFileName);
|
TextFile.stringToFile(actual, actualFileName);
|
||||||
String msg = TestingUtilities.checkXMLIsSame(actualFileName, expectedFileName);
|
String msg = CompareUtilities.checkXMLIsSame(expectedFileName, actualFileName);
|
||||||
Assertions.assertTrue(msg == null, "Output does not match expected: "+msg);
|
Assertions.assertTrue(msg == null, "Output does not match expected: "+msg);
|
||||||
|
|
||||||
if (test.isMeta()) {
|
if (test.isMeta()) {
|
||||||
|
@ -147,9 +148,9 @@ public class NarrativeGenerationTests {
|
||||||
|
|
||||||
expected = TextFile.streamToString(TestingUtilities.loadTestResourceStream("r5", "narrative", test.getId() + "-meta.html"));
|
expected = TextFile.streamToString(TestingUtilities.loadTestResourceStream("r5", "narrative", test.getId() + "-meta.html"));
|
||||||
actual = HEADER+new XhtmlComposer(true, true).compose(x)+FOOTER;
|
actual = HEADER+new XhtmlComposer(true, true).compose(x)+FOOTER;
|
||||||
actualFileName = TestingUtilities.tempFile("narrative", test.getId() + "-meta.actual.html");
|
actualFileName = CompareUtilities.tempFile("narrative", test.getId() + "-meta.actual.html");
|
||||||
TextFile.stringToFile(actual, actualFileName);
|
TextFile.stringToFile(actual, actualFileName);
|
||||||
msg = TestingUtilities.checkXMLIsSame(actualFileName, expectedFileName);
|
msg = CompareUtilities.checkXMLIsSame(expectedFileName, actualFileName);
|
||||||
Assertions.assertTrue(msg == null, "Meta output does not match expected: "+msg);
|
Assertions.assertTrue(msg == null, "Meta output does not match expected: "+msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ import org.hl7.fhir.r5.renderers.DataRenderer;
|
||||||
import org.hl7.fhir.r5.renderers.RendererFactory;
|
import org.hl7.fhir.r5.renderers.RendererFactory;
|
||||||
import org.hl7.fhir.r5.renderers.utils.RenderingContext;
|
import org.hl7.fhir.r5.renderers.utils.RenderingContext;
|
||||||
import org.hl7.fhir.r5.renderers.utils.RenderingContext.ResourceRendererMode;
|
import org.hl7.fhir.r5.renderers.utils.RenderingContext.ResourceRendererMode;
|
||||||
|
import org.hl7.fhir.r5.test.utils.CompareUtilities;
|
||||||
import org.hl7.fhir.r5.test.utils.TestingUtilities;
|
import org.hl7.fhir.r5.test.utils.TestingUtilities;
|
||||||
import org.hl7.fhir.r5.utils.EOperationOutcome;
|
import org.hl7.fhir.r5.utils.EOperationOutcome;
|
||||||
import org.hl7.fhir.utilities.Utilities;
|
import org.hl7.fhir.utilities.Utilities;
|
||||||
|
|
|
@ -10,6 +10,7 @@ import org.hl7.fhir.r5.formats.JsonParser;
|
||||||
import org.hl7.fhir.r5.model.CapabilityStatement;
|
import org.hl7.fhir.r5.model.CapabilityStatement;
|
||||||
import org.hl7.fhir.r5.openapi.OpenApiGenerator;
|
import org.hl7.fhir.r5.openapi.OpenApiGenerator;
|
||||||
import org.hl7.fhir.r5.openapi.Writer;
|
import org.hl7.fhir.r5.openapi.Writer;
|
||||||
|
import org.hl7.fhir.r5.test.utils.CompareUtilities;
|
||||||
import org.hl7.fhir.r5.test.utils.TestingUtilities;
|
import org.hl7.fhir.r5.test.utils.TestingUtilities;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ import org.hl7.fhir.r5.formats.IParser.OutputStyle;
|
||||||
import org.hl7.fhir.r5.formats.JsonParser;
|
import org.hl7.fhir.r5.formats.JsonParser;
|
||||||
import org.hl7.fhir.r5.formats.XmlParser;
|
import org.hl7.fhir.r5.formats.XmlParser;
|
||||||
import org.hl7.fhir.r5.model.Resource;
|
import org.hl7.fhir.r5.model.Resource;
|
||||||
|
import org.hl7.fhir.r5.test.utils.CompareUtilities;
|
||||||
import org.hl7.fhir.r5.test.utils.TestingUtilities;
|
import org.hl7.fhir.r5.test.utils.TestingUtilities;
|
||||||
import org.hl7.fhir.utilities.TextFile;
|
import org.hl7.fhir.utilities.TextFile;
|
||||||
import org.hl7.fhir.utilities.npm.FilesystemPackageCacheManager;
|
import org.hl7.fhir.utilities.npm.FilesystemPackageCacheManager;
|
||||||
|
@ -57,7 +58,7 @@ public class ParsingTests {
|
||||||
r = new XmlParser().parse(b);
|
r = new XmlParser().parse(b);
|
||||||
b = new JsonParser().setOutputStyle(OutputStyle.PRETTY).composeBytes(r);
|
b = new JsonParser().setOutputStyle(OutputStyle.PRETTY).composeBytes(r);
|
||||||
String output = new String(b);
|
String output = new String(b);
|
||||||
String msg = TestingUtilities.checkJsonSrcIsSame(src, output);
|
String msg = CompareUtilities.checkJsonSrcIsSame(src, output);
|
||||||
Assertions.assertTrue(msg == null, msg);
|
Assertions.assertTrue(msg == null, msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@ import org.hl7.fhir.r5.model.Resource;
|
||||||
import org.hl7.fhir.r5.renderers.RendererFactory;
|
import org.hl7.fhir.r5.renderers.RendererFactory;
|
||||||
import org.hl7.fhir.r5.renderers.utils.RenderingContext;
|
import org.hl7.fhir.r5.renderers.utils.RenderingContext;
|
||||||
import org.hl7.fhir.r5.renderers.utils.RenderingContext.ResourceRendererMode;
|
import org.hl7.fhir.r5.renderers.utils.RenderingContext.ResourceRendererMode;
|
||||||
|
import org.hl7.fhir.r5.test.utils.CompareUtilities;
|
||||||
import org.hl7.fhir.r5.test.utils.TestingUtilities;
|
import org.hl7.fhir.r5.test.utils.TestingUtilities;
|
||||||
import org.hl7.fhir.r5.utils.EOperationOutcome;
|
import org.hl7.fhir.r5.utils.EOperationOutcome;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
|
@ -12,6 +12,7 @@ import javax.xml.parsers.ParserConfigurationException;
|
||||||
import org.hl7.fhir.exceptions.FHIRException;
|
import org.hl7.fhir.exceptions.FHIRException;
|
||||||
import org.hl7.fhir.exceptions.FHIRFormatError;
|
import org.hl7.fhir.exceptions.FHIRFormatError;
|
||||||
import org.hl7.fhir.r5.context.IWorkerContext;
|
import org.hl7.fhir.r5.context.IWorkerContext;
|
||||||
|
import org.hl7.fhir.r5.test.utils.CompareUtilities;
|
||||||
import org.hl7.fhir.r5.test.utils.TestPackageLoader;
|
import org.hl7.fhir.r5.test.utils.TestPackageLoader;
|
||||||
import org.hl7.fhir.r5.formats.IParser.OutputStyle;
|
import org.hl7.fhir.r5.formats.IParser.OutputStyle;
|
||||||
import org.hl7.fhir.r5.formats.JsonParser;
|
import org.hl7.fhir.r5.formats.JsonParser;
|
||||||
|
@ -146,11 +147,11 @@ public class VocabTests {
|
||||||
outcome.getValueset().getExpansion().setTimestamp(null);
|
outcome.getValueset().getExpansion().setTimestamp(null);
|
||||||
String expected = new XmlParser().setOutputStyle(OutputStyle.PRETTY).composeString(targetVS);
|
String expected = new XmlParser().setOutputStyle(OutputStyle.PRETTY).composeString(targetVS);
|
||||||
String actual = new XmlParser().setOutputStyle(OutputStyle.PRETTY).composeString(outcome.getValueset());
|
String actual = new XmlParser().setOutputStyle(OutputStyle.PRETTY).composeString(outcome.getValueset());
|
||||||
String expectedFileName = TestingUtilities.tempFile("vocab", test.getId() + ".expected.html");
|
String expectedFileName = CompareUtilities.tempFile("vocab", test.getId() + ".expected.html");
|
||||||
String actualFileName = TestingUtilities.tempFile("vocab", test.getId() + ".actual.html");
|
String actualFileName = CompareUtilities.tempFile("vocab", test.getId() + ".actual.html");
|
||||||
TextFile.stringToFile(expected, expectedFileName);
|
TextFile.stringToFile(expected, expectedFileName);
|
||||||
TextFile.stringToFile(actual, actualFileName);
|
TextFile.stringToFile(actual, actualFileName);
|
||||||
String msg = TestingUtilities.checkXMLIsSame(actualFileName, expectedFileName);
|
String msg = CompareUtilities.checkXMLIsSame(expectedFileName, actualFileName);
|
||||||
Assertions.assertTrue(msg == null, "Output does not match expected: "+msg);
|
Assertions.assertTrue(msg == null, "Output does not match expected: "+msg);
|
||||||
} else {
|
} else {
|
||||||
Assertions.fail("Expansion Failed: "+outcome.getError());
|
Assertions.fail("Expansion Failed: "+outcome.getError());
|
||||||
|
|
|
@ -0,0 +1,99 @@
|
||||||
|
package org.hl7.fhir.r5.test.utils;
|
||||||
|
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.Arguments;
|
||||||
|
import org.junit.jupiter.params.provider.MethodSource;
|
||||||
|
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||||
|
|
||||||
|
public class CompareUtilitiesTest {
|
||||||
|
|
||||||
|
public static final Path ROOT_TEST_PATH = Paths.get("src","test","resources", "testUtilities");
|
||||||
|
|
||||||
|
public static final Path ROOT_XML_TEST_PATH = ROOT_TEST_PATH.resolve("xml");
|
||||||
|
public static final Path ROOT_JSON_TEST_PATH = ROOT_TEST_PATH.resolve("json");
|
||||||
|
|
||||||
|
|
||||||
|
public String getResourceAsString(String path) throws IOException {
|
||||||
|
InputStream inputStream = new FileInputStream(path);
|
||||||
|
String contents = IOUtils.toString(inputStream, java.nio.charset.StandardCharsets.UTF_8);
|
||||||
|
return contents.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Stream<Arguments> getCompareXMLParams() {
|
||||||
|
return Stream.of(
|
||||||
|
Arguments.of("expected.xml", "expected.xml", null),
|
||||||
|
Arguments.of("expected.xml", "actualDiffText.xml", "actualDiffText.xml.error"),
|
||||||
|
Arguments.of("expected.xml", "actualMissingAttribute.xml", "actualMissingAttribute.xml.error"),
|
||||||
|
Arguments.of("expected.xml", "actualDiffAttribute.xml", "actualDiffAttribute.xml.error"),
|
||||||
|
Arguments.of("expected.xml", "actualDiffNodeType.xml", "actualDiffNodeType.xml.error"),
|
||||||
|
Arguments.of("expected.xml", "actualDiffTextEmpty.xml", "actualDiffTextEmpty.xml.error"),
|
||||||
|
Arguments.of("expected.xml", "actualExtraNode.xml", "actualExtraNode.xml.error"),
|
||||||
|
Arguments.of("expected.xml", "actualMissingNode.xml", "actualMissingNode.xml.error"),
|
||||||
|
Arguments.of("expected.xml", "actualDiffNamespace.xml", "actualDiffNamespace.xml.error"),
|
||||||
|
Arguments.of("expected.xml", "actualDiffLocalName.xml", "actualDiffLocalName.xml.error")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String normalizeNewlines(String input) {
|
||||||
|
return input.replaceAll("\\r\\n?", "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@MethodSource("getCompareXMLParams")
|
||||||
|
public void testCompareXML(String expectedFileName, String actualFileName, String expectedOutputFileName) throws Exception {
|
||||||
|
final String expectedXMLPath = ROOT_XML_TEST_PATH.resolve(expectedFileName).toAbsolutePath().toString();
|
||||||
|
final String actualXMLPath = ROOT_XML_TEST_PATH.resolve(actualFileName).toAbsolutePath().toString();
|
||||||
|
|
||||||
|
final String actualOutput = CompareUtilities.checkXMLIsSame(expectedXMLPath, actualXMLPath);
|
||||||
|
|
||||||
|
if (expectedOutputFileName == null) {
|
||||||
|
assertNull(actualOutput);
|
||||||
|
} else {
|
||||||
|
final String expectedOutputPath = ROOT_XML_TEST_PATH.resolve(expectedOutputFileName).toAbsolutePath().toString();
|
||||||
|
String expectedOutput = normalizeNewlines(getResourceAsString(expectedOutputPath));
|
||||||
|
assertEquals(expectedOutput, normalizeNewlines(actualOutput));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Stream<Arguments> getCompareJSONParams() {
|
||||||
|
return Stream.of(
|
||||||
|
Arguments.of("expected.json", "expected.json", null),
|
||||||
|
Arguments.of("expected.json", "actualDiffValue.json", "actualDiffValue.json.error"),
|
||||||
|
Arguments.of("expected.json", "actualDiffType.json", "actualDiffType.json.error"),
|
||||||
|
Arguments.of("expected.json", "actualDiffArrayContent.json", "actualDiffArrayContent.json.error"),
|
||||||
|
Arguments.of("expected.json", "actualDiffBoolean.json", "actualDiffBoolean.json.error"),
|
||||||
|
Arguments.of("expected.json", "actualMissingProperty.json", "actualMissingProperty.json.error"),
|
||||||
|
Arguments.of("expected.json", "actualDiffArraySize.json", "actualDiffArraySize.json.error"),
|
||||||
|
Arguments.of("expected.json", "actualDiffNumber.json", "actualDiffNumber.json.error"),
|
||||||
|
Arguments.of("expected.json", "actualMissingSubProperty.json", "actualMissingSubProperty.json.error"),
|
||||||
|
Arguments.of("expected.json", "actualExtraProperty.json", "actualExtraProperty.json.error")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@MethodSource("getCompareJSONParams")
|
||||||
|
public void testCompareJSON(String expectedFileName, String actualFileName, String expectedOutputFileName) throws IOException {
|
||||||
|
final String expectedJSONPath = ROOT_JSON_TEST_PATH.resolve(expectedFileName).toAbsolutePath().toString();
|
||||||
|
final String actualJSONPath = ROOT_JSON_TEST_PATH.resolve(actualFileName).toAbsolutePath().toString();
|
||||||
|
|
||||||
|
final String actualOutput = CompareUtilities.checkJsonSrcIsSame(getResourceAsString(expectedJSONPath), getResourceAsString(actualJSONPath), false);
|
||||||
|
if (expectedOutputFileName == null) {
|
||||||
|
assertNull(actualOutput);
|
||||||
|
} else {
|
||||||
|
final String expectedOutputPath = ROOT_JSON_TEST_PATH.resolve(expectedOutputFileName).toAbsolutePath().toString();
|
||||||
|
String expectedOutput = normalizeNewlines(getResourceAsString(expectedOutputPath));
|
||||||
|
assertEquals(expectedOutput, normalizeNewlines(actualOutput));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"expectedString" : "expected value",
|
||||||
|
"expectedBoolean" : true,
|
||||||
|
"expectedNumber" : 123,
|
||||||
|
"expectedArray" : [
|
||||||
|
"unexpectedValue 1",
|
||||||
|
"expectedValue 2"
|
||||||
|
],
|
||||||
|
"property" : {
|
||||||
|
"subField" : 1
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
string property values differ at .expectedArray[0]
|
||||||
|
Expected :expectedValue 1
|
||||||
|
Actual :unexpectedValue 1
|
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"expectedString" : "expected value",
|
||||||
|
"expectedBoolean" : true,
|
||||||
|
"expectedNumber" : 123,
|
||||||
|
"expectedArray" : [
|
||||||
|
"expectedValue 1"
|
||||||
|
],
|
||||||
|
"property" : {
|
||||||
|
"subField" : 1
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
array properties count differs at .expectedArray
|
||||||
|
Expected :2
|
||||||
|
Actual :1
|
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"expectedString" : "expected value",
|
||||||
|
"expectedBoolean" : false,
|
||||||
|
"expectedNumber" : 123,
|
||||||
|
"expectedArray" : [
|
||||||
|
"expectedValue 1",
|
||||||
|
"expectedValue 2"
|
||||||
|
],
|
||||||
|
"property" : {
|
||||||
|
"subField" : 1
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
boolean property values differ at .expectedBoolean
|
||||||
|
Expected :true
|
||||||
|
Actual :false
|
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"expectedString" : "expected value",
|
||||||
|
"expectedBoolean" : true,
|
||||||
|
"expectedNumber" : 789,
|
||||||
|
"expectedArray" : [
|
||||||
|
"expectedValue 1",
|
||||||
|
"expectedValue 2"
|
||||||
|
],
|
||||||
|
"property" : {
|
||||||
|
"subField" : 1
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
number property values differ at .expectedNumber
|
||||||
|
Expected :123
|
||||||
|
Actual :789
|
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"expectedString" : 1,
|
||||||
|
"expectedBoolean" : true,
|
||||||
|
"expectedNumber" : 123,
|
||||||
|
"expectedArray" : [
|
||||||
|
"expectedValue 1",
|
||||||
|
"expectedValue 2"
|
||||||
|
],
|
||||||
|
"property" : {
|
||||||
|
"subField" : 1
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
property types differ at .expectedString
|
||||||
|
Expected :expected value
|
||||||
|
Actual :1
|
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"expectedString" : "unexpected value",
|
||||||
|
"expectedBoolean" : true,
|
||||||
|
"expectedNumber" : 123,
|
||||||
|
"expectedArray" : [
|
||||||
|
"expectedValue 1",
|
||||||
|
"expectedValue 2"
|
||||||
|
],
|
||||||
|
"property" : {
|
||||||
|
"subField" : 1
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
string property values differ at .expectedString
|
||||||
|
Expected :expected value
|
||||||
|
Actual :unexpected value
|
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
"expectedString" : "expected value",
|
||||||
|
"expectedBoolean" : true,
|
||||||
|
"expectedNumber" : 123,
|
||||||
|
"unexpectedProperty" : "totally unexpected",
|
||||||
|
"expectedArray" : [
|
||||||
|
"expectedValue 1",
|
||||||
|
"expectedValue 2"
|
||||||
|
],
|
||||||
|
"property" : {
|
||||||
|
"subField" : 1
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
properties differ at : missing property unexpectedProperty
|
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"expectedString" : "expected value",
|
||||||
|
"expectedNumber" : 123,
|
||||||
|
"expectedArray" : [
|
||||||
|
"expectedValue 1",
|
||||||
|
"expectedValue 2"
|
||||||
|
],
|
||||||
|
"property" : {
|
||||||
|
"subField" : 1
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
properties differ at : missing property expectedBoolean
|
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"expectedString" : "expected value",
|
||||||
|
"expectedBoolean" : true,
|
||||||
|
"expectedNumber" : 123,
|
||||||
|
"expectedArray" : [
|
||||||
|
"expectedValue 1",
|
||||||
|
"expectedValue 2"
|
||||||
|
],
|
||||||
|
"property" : {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
properties differ at .property: missing property subField
|
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"expectedString" : "expected value",
|
||||||
|
"expectedBoolean" : true,
|
||||||
|
"expectedNumber" : 123,
|
||||||
|
"expectedArray" : [
|
||||||
|
"expectedValue 1",
|
||||||
|
"expectedValue 2"
|
||||||
|
],
|
||||||
|
"property" : {
|
||||||
|
"subField" : 1
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
<root>
|
||||||
|
<blah myAtt="wrongwrongwrong">
|
||||||
|
expected
|
||||||
|
</blah>
|
||||||
|
<emptyNode></emptyNode>
|
||||||
|
<foo:nameSpacedNode xmlns:foo="http://www.example.com/FOO" ></foo:nameSpacedNode>
|
||||||
|
</root>
|
|
@ -0,0 +1,3 @@
|
||||||
|
Attributes differ at /root/blah
|
||||||
|
Expected :dummyAtt
|
||||||
|
Actual :wrongwrongwrong
|
|
@ -0,0 +1,7 @@
|
||||||
|
<root>
|
||||||
|
<blah myAtt="dummyAtt">
|
||||||
|
expected
|
||||||
|
</blah>
|
||||||
|
<emptyNode></emptyNode>
|
||||||
|
<foo:wrongNameSpacedNode xmlns:foo="http://www.example.com/FOO" ></foo:wrongNameSpacedNode>
|
||||||
|
</root>
|
|
@ -0,0 +1,3 @@
|
||||||
|
Names differ at /root
|
||||||
|
Expected :nameSpacedNode
|
||||||
|
Actual :wrongNameSpacedNode
|
|
@ -0,0 +1,7 @@
|
||||||
|
<root>
|
||||||
|
<blah myAtt="dummyAtt">
|
||||||
|
expected
|
||||||
|
</blah>
|
||||||
|
<emptyNode></emptyNode>
|
||||||
|
<foo:nameSpacedNode xmlns:foo="http://www.example.com/BAR" ></foo:nameSpacedNode>
|
||||||
|
</root>
|
|
@ -0,0 +1,3 @@
|
||||||
|
Namespaces differ at /root
|
||||||
|
Expected :http://www.example.com/FOO
|
||||||
|
Actual :http://www.example.com/BAR
|
|
@ -0,0 +1,7 @@
|
||||||
|
<root>
|
||||||
|
<blah myAtt="dummyAtt">
|
||||||
|
<foo />
|
||||||
|
</blah>
|
||||||
|
<emptyNode></emptyNode>
|
||||||
|
<foo:nameSpacedNode xmlns:foo="http://www.example.com/FOO" ></foo:nameSpacedNode>
|
||||||
|
</root>
|
|
@ -0,0 +1,3 @@
|
||||||
|
node type mismatch in children of /root/blah
|
||||||
|
Expected :1
|
||||||
|
Actual :1
|
|
@ -0,0 +1,7 @@
|
||||||
|
<root>
|
||||||
|
<blah myAtt="dummyAtt">
|
||||||
|
different
|
||||||
|
</blah>
|
||||||
|
<emptyNode></emptyNode>
|
||||||
|
<foo:nameSpacedNode xmlns:foo="http://www.example.com/FOO" ></foo:nameSpacedNode>
|
||||||
|
</root>
|
|
@ -0,0 +1,3 @@
|
||||||
|
Text differs at /root/blah
|
||||||
|
Expected :expected
|
||||||
|
Actual :different
|
|
@ -0,0 +1,7 @@
|
||||||
|
<root>
|
||||||
|
<blah myAtt="dummyAtt">
|
||||||
|
expected
|
||||||
|
</blah>
|
||||||
|
<emptyNode>not empty</emptyNode>
|
||||||
|
<foo:nameSpacedNode xmlns:foo="http://www.example.com/FOO" ></foo:nameSpacedNode>
|
||||||
|
</root>
|
|
@ -0,0 +1 @@
|
||||||
|
node mismatch - more nodes in expected in children of /root/emptyNode
|
|
@ -0,0 +1,8 @@
|
||||||
|
<root>
|
||||||
|
<blah myAtt="dummyAtt">
|
||||||
|
expected
|
||||||
|
</blah>
|
||||||
|
<emptyNode></emptyNode>
|
||||||
|
<foo:nameSpacedNode xmlns:foo="http://www.example.com/FOO" ></foo:nameSpacedNode>
|
||||||
|
<extraNode/>
|
||||||
|
</root>
|
|
@ -0,0 +1 @@
|
||||||
|
node mismatch - more nodes in expected in children of /root
|
|
@ -0,0 +1,7 @@
|
||||||
|
<root>
|
||||||
|
<blah>
|
||||||
|
expected
|
||||||
|
</blah>
|
||||||
|
<emptyNode></emptyNode>
|
||||||
|
<foo:nameSpacedNode xmlns:foo="http://www.example.com/FOO" ></foo:nameSpacedNode>
|
||||||
|
</root>
|
|
@ -0,0 +1 @@
|
||||||
|
Attributes differ at /root/blah: missing attribute myAtt
|
|
@ -0,0 +1,6 @@
|
||||||
|
<root>
|
||||||
|
<blah myAtt="dummyAtt">
|
||||||
|
expected
|
||||||
|
</blah>
|
||||||
|
<emptyNode></emptyNode>
|
||||||
|
</root>
|
|
@ -0,0 +1 @@
|
||||||
|
node mismatch - more nodes in actual in children of /root
|
|
@ -0,0 +1,7 @@
|
||||||
|
<root>
|
||||||
|
<blah myAtt="dummyAtt">
|
||||||
|
expected
|
||||||
|
</blah>
|
||||||
|
<emptyNode></emptyNode>
|
||||||
|
<foo:nameSpacedNode xmlns:foo="http://www.example.com/FOO" ></foo:nameSpacedNode>
|
||||||
|
</root>
|
|
@ -101,4 +101,28 @@ public class BaseTestingUtilities {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String tempFile(String folder, String name) throws IOException {
|
||||||
|
String tmp = tempFolder(folder);
|
||||||
|
return Utilities.path(tmp, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String tempFolder(String name) throws IOException {
|
||||||
|
File tmp = new File("C:\\temp");
|
||||||
|
if (tmp.exists() && tmp.isDirectory()) {
|
||||||
|
String path = Utilities.path("C:\\temp", name);
|
||||||
|
Utilities.createDirectory(path);
|
||||||
|
return path;
|
||||||
|
} else if (ToolGlobalSettings.hasTempPath()) {
|
||||||
|
return ToolGlobalSettings.getTempPath();
|
||||||
|
} else if (new File("/tmp").exists()) {
|
||||||
|
String path = Utilities.path("/tmp", name);
|
||||||
|
Utilities.createDirectory(path);
|
||||||
|
return path;
|
||||||
|
} else {
|
||||||
|
String path = Utilities.path(System.getProperty("java.io.tmpdir"), name);
|
||||||
|
Utilities.createDirectory(path);
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -69,11 +69,37 @@ public class IgLoader {
|
||||||
this.isDebug = isDebug;
|
this.isDebug = isDebug;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param igs
|
||||||
|
* @param binaries
|
||||||
|
* @param src Source of the IG
|
||||||
|
*
|
||||||
|
* @param recursive
|
||||||
|
* @throws IOException
|
||||||
|
* @throws FHIRException
|
||||||
|
*
|
||||||
|
* @see IgLoader#loadIgSource(String, boolean, boolean) loadIgSource for detailed description of the src parameter
|
||||||
|
*/
|
||||||
public void loadIg(List<ImplementationGuide> igs,
|
public void loadIg(List<ImplementationGuide> igs,
|
||||||
Map<String, byte[]> binaries,
|
Map<String, byte[]> binaries,
|
||||||
String src,
|
String src,
|
||||||
boolean recursive) throws IOException, FHIRException {
|
boolean recursive) throws IOException, FHIRException {
|
||||||
NpmPackage npm = src.matches(FilesystemPackageCacheManager.PACKAGE_VERSION_REGEX_OPT) && !new File(src).exists() ? getPackageCacheManager().loadPackage(src, null) : null;
|
|
||||||
|
final String explicitFhirVersion;
|
||||||
|
final String srcPackage;
|
||||||
|
if (src.startsWith("[") && src.indexOf(']', 1) > 1) {
|
||||||
|
explicitFhirVersion = src.substring(1,src.indexOf(']', 1));
|
||||||
|
srcPackage = src.substring(src.indexOf(']',1) + 1);
|
||||||
|
if (VersionUtilities.isSupportedVersion(explicitFhirVersion)) {
|
||||||
|
throw new FHIRException("Unsupported FHIR Version: " + explicitFhirVersion + " valid versions are " + VersionUtilities.listSupportedVersions());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
explicitFhirVersion = null;
|
||||||
|
srcPackage = src;
|
||||||
|
}
|
||||||
|
|
||||||
|
NpmPackage npm = srcPackage.matches(FilesystemPackageCacheManager.PACKAGE_VERSION_REGEX_OPT) && !new File(srcPackage).exists() ? getPackageCacheManager().loadPackage(srcPackage, null) : null;
|
||||||
if (npm != null) {
|
if (npm != null) {
|
||||||
for (String s : npm.dependencies()) {
|
for (String s : npm.dependencies()) {
|
||||||
if (!getContext().getLoadedPackages().contains(s)) {
|
if (!getContext().getLoadedPackages().contains(s)) {
|
||||||
|
@ -82,17 +108,17 @@ public class IgLoader {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
System.out.print(" Load " + src);
|
System.out.print(" Load " + srcPackage);
|
||||||
if (!src.contains("#")) {
|
if (!srcPackage.contains("#")) {
|
||||||
System.out.print("#" + npm.version());
|
System.out.print("#" + npm.version());
|
||||||
}
|
}
|
||||||
int count = getContext().loadFromPackage(npm, ValidatorUtils.loaderForVersion(npm.fhirVersion()));
|
int count = getContext().loadFromPackage(npm, ValidatorUtils.loaderForVersion(npm.fhirVersion()));
|
||||||
System.out.println(" - " + count + " resources (" + getContext().clock().milestone() + ")");
|
System.out.println(" - " + count + " resources (" + getContext().clock().milestone() + ")");
|
||||||
} else {
|
} else {
|
||||||
System.out.print(" Load " + src);
|
System.out.print(" Load " + srcPackage);
|
||||||
String canonical = null;
|
String canonical = null;
|
||||||
int count = 0;
|
int count = 0;
|
||||||
Map<String, byte[]> source = loadIgSource(src, recursive, true);
|
Map<String, byte[]> source = loadIgSource(srcPackage, recursive, true);
|
||||||
String version = Constants.VERSION;
|
String version = Constants.VERSION;
|
||||||
if (getVersion() != null) {
|
if (getVersion() != null) {
|
||||||
version = getVersion();
|
version = getVersion();
|
||||||
|
@ -100,6 +126,10 @@ public class IgLoader {
|
||||||
if (source.containsKey("version.info")) {
|
if (source.containsKey("version.info")) {
|
||||||
version = readInfoVersion(source.get("version.info"));
|
version = readInfoVersion(source.get("version.info"));
|
||||||
}
|
}
|
||||||
|
if (explicitFhirVersion != null) {
|
||||||
|
version = explicitFhirVersion;
|
||||||
|
}
|
||||||
|
|
||||||
for (Map.Entry<String, byte[]> t : source.entrySet()) {
|
for (Map.Entry<String, byte[]> t : source.entrySet()) {
|
||||||
String fn = t.getKey();
|
String fn = t.getKey();
|
||||||
if (!exemptFile(fn)) {
|
if (!exemptFile(fn)) {
|
||||||
|
@ -126,6 +156,18 @@ public class IgLoader {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param source
|
||||||
|
* @param opName
|
||||||
|
* @param asIg
|
||||||
|
* @return
|
||||||
|
* @throws FHIRException
|
||||||
|
* @throws IOException
|
||||||
|
*
|
||||||
|
* * @see IgLoader#loadIgSource(String, boolean, boolean) loadIgSource for detailed description of the src parameter
|
||||||
|
*/
|
||||||
|
|
||||||
public Content loadContent(String source, String opName, boolean asIg) throws FHIRException, IOException {
|
public Content loadContent(String source, String opName, boolean asIg) throws FHIRException, IOException {
|
||||||
Map<String, byte[]> s = loadIgSource(source, false, asIg);
|
Map<String, byte[]> s = loadIgSource(source, false, asIg);
|
||||||
Content res = new Content();
|
Content res = new Content();
|
||||||
|
@ -150,18 +192,23 @@ public class IgLoader {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* explore should be true if we're trying to load an -ig parameter, and false if we're loading source
|
|
||||||
*
|
*
|
||||||
|
* @param src can be one of the following:
|
||||||
|
* <br> - a canonical url for an ig - this will be converted to a package id and loaded into the cache
|
||||||
|
* <br> - a package id for an ig - this will be loaded into the cache
|
||||||
|
* <br> - a direct reference to a package ("package.tgz") - this will be extracted by the cache manager, but not put in the cache
|
||||||
|
* <br> - a folder containing resources - these will be loaded directly
|
||||||
|
* @param recursive if true and src resolves to a folder, recursively find and load IgSources from that directory
|
||||||
|
* @param explore should be true if we're trying to load an -ig parameter, and false if we're loading source
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* @throws FHIRException
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
**/
|
*/
|
||||||
public Map<String, byte[]> loadIgSource(String src,
|
public Map<String, byte[]> loadIgSource(String src,
|
||||||
boolean recursive,
|
boolean recursive,
|
||||||
boolean explore) throws FHIRException, IOException {
|
boolean explore) throws FHIRException, IOException {
|
||||||
// src can be one of the following:
|
//
|
||||||
// - a canonical url for an ig - this will be converted to a package id and loaded into the cache
|
|
||||||
// - a package id for an ig - this will be loaded into the cache
|
|
||||||
// - a direct reference to a package ("package.tgz") - this will be extracted by the cache manager, but not put in the cache
|
|
||||||
// - a folder containing resources - these will be loaded directly
|
|
||||||
if (Common.isNetworkPath(src)) {
|
if (Common.isNetworkPath(src)) {
|
||||||
String v = null;
|
String v = null;
|
||||||
if (src.contains("|")) {
|
if (src.contains("|")) {
|
||||||
|
@ -627,7 +674,7 @@ public class IgLoader {
|
||||||
return Utilities.existsInList(fn, EXEMPT_FILES);
|
return Utilities.existsInList(fn, EXEMPT_FILES);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Resource loadFileWithErrorChecking(String version, Map.Entry<String, byte[]> t, String fn) {
|
protected Resource loadFileWithErrorChecking(String version, Map.Entry<String, byte[]> t, String fn) {
|
||||||
log("* load file: " + fn);
|
log("* load file: " + fn);
|
||||||
Resource r = null;
|
Resource r = null;
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -266,6 +266,19 @@ public class ValidationEngine implements IValidatorResourceFetcher, IValidationP
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param src
|
||||||
|
* @param recursive
|
||||||
|
* @param terminologyCachePath
|
||||||
|
* @param userAgent
|
||||||
|
* @param tt
|
||||||
|
* @param loggingService
|
||||||
|
* @throws FHIRException
|
||||||
|
* @throws IOException
|
||||||
|
*
|
||||||
|
* @see IgLoader#loadIgSource(String, boolean, boolean) loadIgSource for detailed description of the src parameter
|
||||||
|
*/
|
||||||
private void loadCoreDefinitions(String src, boolean recursive, String terminologyCachePath, String userAgent, TimeTracker tt, IWorkerContext.ILoggingService loggingService) throws FHIRException, IOException {
|
private void loadCoreDefinitions(String src, boolean recursive, String terminologyCachePath, String userAgent, TimeTracker tt, IWorkerContext.ILoggingService loggingService) throws FHIRException, IOException {
|
||||||
NpmPackage npm = getPcm().loadPackage(src, null);
|
NpmPackage npm = getPcm().loadPackage(src, null);
|
||||||
if (npm != null) {
|
if (npm != null) {
|
||||||
|
|
|
@ -4,98 +4,114 @@ The validation tool compares a resource against the base definitions and any
|
||||||
profiles declared in the resource (Resource.meta.profile) or specified on the
|
profiles declared in the resource (Resource.meta.profile) or specified on the
|
||||||
command line
|
command line
|
||||||
|
|
||||||
The FHIR validation tool validates a FHIR resource or bundle.
|
The FHIR validation tool validates a FHIR resource or bundle. Schema and
|
||||||
Schema and schematron checking is performed, then some additional checks are performed.
|
schematron checking is performed, then some additional checks are performed.
|
||||||
|
|
||||||
* XML & Json (FHIR versions {{XML_AND_JSON_FHIR_VERSIONS}})
|
* XML & Json (FHIR versions {{XML_AND_JSON_FHIR_VERSIONS}})
|
||||||
* Turtle (FHIR versions {{TURTLE_FHIR_VERSIONS}})
|
* Turtle (FHIR versions {{TURTLE_FHIR_VERSIONS}})
|
||||||
|
|
||||||
If requested, instances will also be verified against the appropriate schema
|
If requested, instances will also be verified against the appropriate schema W3C
|
||||||
W3C XML Schema, JSON schema or ShEx, as appropriate
|
XML Schema, JSON schema or ShEx, as appropriate
|
||||||
|
|
||||||
Usage: java -jar [validator].jar (parameters)
|
Usage: java -jar [validator].jar (parameters)
|
||||||
|
|
||||||
The following parameters are supported:
|
The following parameters are supported:
|
||||||
[source]: a file, url, directory or pattern for resources to validate. At
|
[source]: a file, url, directory or pattern for resources to validate.
|
||||||
least one source must be declared. If there is more than one source or if
|
At least one source must be declared. If there is more than one source or
|
||||||
the source is other than a single file or url and the output parameter is
|
if the source is other than a single file or url and the output parameter is
|
||||||
used, results will be provided as a Bundle.
|
used, results will be provided as a Bundle.
|
||||||
Patterns are limited to a directory followed by a filename with an embedded
|
Patterns are limited to a directory followed by a filename with an
|
||||||
asterisk. E.g. foo*-examples.xml or someresource.*, etc.
|
embedded asterisk. E.g. foo*-examples.xml or someresource.*, etc.
|
||||||
-version [ver]: The FHIR version to use. This can only appear once.
|
-version [ver]: The FHIR version to use.
|
||||||
valid values {{FHIR_MAJOR_VERSIONS}} or {{FHIR_MINOR_VERSIONS}}
|
This can only appear once.
|
||||||
Default value is {{FHIR_CURRENT_VERSION}}
|
valid values {{FHIR_MAJOR_VERSIONS}} or {{FHIR_MINOR_VERSIONS}}
|
||||||
-ig [package|file|folder|url]: an IG or profile definition to load. Can be
|
Default value is {{FHIR_CURRENT_VERSION}}
|
||||||
the URL of an implementation guide or a package ([id]-[ver]) for
|
-ig [package|file|folder|url]: an IG or profile definition to load.
|
||||||
a built implementation guide or a local folder that contains a
|
Can be the URL of an implementation guide or a package ([id]-[ver]) for a
|
||||||
set of conformance resources.
|
built implementation guide or a local folder that contains a set of
|
||||||
If you would like to load the latest unreleased version of the implementation guide or package,
|
conformance resources.
|
||||||
please define the version as '#current'. If no version is provided, the latest version
|
If you would like to load the latest unreleased version of the
|
||||||
in the package cache will be used, or if no such cached package is available, the
|
implementation guide or package, please define the version as '#current'.
|
||||||
PackageCacheManager will load the latest from the the online package repo.
|
If no version is provided, the latest version in the package cache will
|
||||||
No default value. This parameter can appear any number of times
|
be used, or if no such cached package is available, the PackageCacheManager
|
||||||
|
will load the latest from the the online package repo.
|
||||||
|
If you want the implementation guide to be loaded for a specific version
|
||||||
|
of FHIR, you can prefix the IG with the appropriate version in square
|
||||||
|
brackets ([[fhirVer]][id]-[igVer]).
|
||||||
|
No default value. This parameter can appear any number of times
|
||||||
-tx [url]: the [base] url of a FHIR terminology service
|
-tx [url]: the [base] url of a FHIR terminology service
|
||||||
Default value is http://tx.fhir.org. This parameter can appear once
|
Default value is http://tx.fhir.org. This parameter can appear once
|
||||||
To run without terminology value, specific n/a as the URL
|
To run without terminology value, specific n/a as the URL
|
||||||
-txLog [file]: Produce a log of the terminology server operations in [file]
|
-txLog [file]: Produce a log of the terminology server operations in [file]
|
||||||
Default value is not to produce a log
|
Default value is not to produce a log
|
||||||
-profile [url]: the canonical URL to validate against (same as if it was
|
-profile [url]: the canonical URL to validate against (same as if it was
|
||||||
specified in Resource.meta.profile). If no profile is specified, the
|
specified in Resource.meta.profile).
|
||||||
resource is validated against the base specification. This parameter
|
If no profile is specified, the resource is validated against the base
|
||||||
can appear any number of times.
|
specification. This parameter can appear any number of times.
|
||||||
Note: the profile (and it's dependencies) have to be made available
|
Note: the profile (and it's dependencies) have to be made available
|
||||||
through one of the -ig parameters. Note that package dependencies will
|
through one of the -ig parameters. Note that package dependencies will
|
||||||
automatically be resolved
|
automatically be resolved
|
||||||
-showReferenceMessages
|
-showReferenceMessages
|
||||||
Includes validation messages resulting from validating target resources
|
Includes validation messages resulting from validating target resources
|
||||||
against profiles defined on a reference. This increases the volume of
|
against profiles defined on a reference. This increases the volume of
|
||||||
validation messages, but may allow easier debugging. If not specified,
|
validation messages, but may allow easier debugging. If not specified,
|
||||||
then only a high-level message indicating that the referenced item wasn't
|
then only a high-level message indicating that the referenced item wasn't
|
||||||
valid against the listed profile(s) will be provided.
|
valid against the listed profile(s) will be provided.
|
||||||
-questionnaire mode: what to do with when validating QuestionnaireResponse resources
|
-questionnaire mode: what to do when validating QuestionnaireResponse resources
|
||||||
none (default): just ignore the questionnaire reference
|
* none (default): just ignore the questionnaire reference
|
||||||
required: check that the QuestionnaireResponse has a questionnaire and validate against it
|
* required: check that the QuestionnaireResponse has a questionnaire and
|
||||||
check: if the QuestionnaireResponse has a questionnaire, validate against it
|
validate against it
|
||||||
The questionnaire must be loaded using the -ig parameter
|
* check: if the QuestionnaireResponse has a questionnaire, validate
|
||||||
the location of a questionnaire. If provided, then the validator will validate
|
against it
|
||||||
any QuestionnaireResponse that claims to match the Questionnaire against it
|
The questionnaire must be loaded using the -ig parameter
|
||||||
no default value. This parameter can appear any number of times
|
which specifies the location of a questionnaire. If provided, then the
|
||||||
|
validator will validate any QuestionnaireResponse that claims to match the
|
||||||
|
Questionnaire against it no default value. This parameter can appear any
|
||||||
|
number of times
|
||||||
-output [file]: a filename for the results (OperationOutcome)
|
-output [file]: a filename for the results (OperationOutcome)
|
||||||
Default: results are sent to the std out.
|
Default: results are sent to the std out.
|
||||||
-debug
|
-debug
|
||||||
Produce additional information about the loading/validation process
|
Produce additional information about the loading/validation process
|
||||||
-recurse
|
-recurse
|
||||||
Look in subfolders when -ig refers to a folder
|
Look in subfolders when -ig refers to a folder
|
||||||
-locale
|
-locale
|
||||||
Specifies the locale/language of the validation result messages (eg.: de-DE
|
Specifies the locale/language of the validation result messages (eg.:
|
||||||
|
de-DE
|
||||||
-sct
|
-sct
|
||||||
Specify the edition of SNOMED CT to use. Valid Choices:
|
Specify the edition of SNOMED CT to use. Valid Choices:
|
||||||
intl | us | uk | au | nl | ca | se | dk | es
|
intl | us | uk | au | nl | ca | se | dk | es
|
||||||
tx.fhir.org only supports a subset. To add to this list or tx.fhir.org
|
tx.fhir.org only supports a subset. To add to this list or tx.fhir.org ask
|
||||||
ask on https://chat.fhir.org/#narrow/stream/179202-terminology
|
on https://chat.fhir.org/#narrow/stream/179202-terminology
|
||||||
-native: use schema for validation as well
|
-native: use schema for validation as well
|
||||||
* XML: w3c schema+schematron
|
* XML: w3c schema+schematron
|
||||||
* JSON: json.schema
|
* JSON: json.schema
|
||||||
* RDF: SHEX
|
* RDF: SHEX
|
||||||
Default: false
|
Default: false
|
||||||
-language: [lang]
|
-language: [lang] The language to use when validating coding displays - same
|
||||||
The language to use when validating coding displays - same value as for xml:lang
|
value as for xml:lang
|
||||||
Not used if the resource specifies language
|
Not used if the resource specifies language
|
||||||
Default: no specified language
|
Default: no specified language
|
||||||
-strictExtensions: If present, treat extensions not defined within the specified FHIR version and any
|
-strictExtensions: If present, treat extensions not defined within the specified
|
||||||
referenced implementation guides or profiles as errors. (Default is to only raise information messages.)
|
FHIR version and any referenced implementation guides or profiles as errors.
|
||||||
-hintAboutNonMustSupport: If present, raise hints if the instance contains data elements that are not
|
(Default is to only raise information messages.)
|
||||||
marked as mustSupport=true. Useful to identify elements included that may be ignored by recipients
|
-hintAboutNonMustSupport: If present, raise hints if the instance contains data
|
||||||
-assumeValidRestReferences: If present, assume that URLs that reference resources follow the RESTful URI pattern
|
elements that are not marked as mustSupport=true. Useful to identify
|
||||||
and it is safe to infer the type from the URL
|
elements included that may be ignored by recipients
|
||||||
-security-checks: If present, check that string content doesn't include any html-like tags that might create
|
-assumeValidRestReferences: If present, assume that URLs that reference
|
||||||
problems downstream (though all external input must always be santized by escaping for either html or sql)
|
resources follow the RESTful URI pattern and it is safe to infer the type
|
||||||
|
from the URL
|
||||||
|
-security-checks: If present, check that string content doesn't include any html
|
||||||
|
-like tags that might create problems downstream (though all external input
|
||||||
|
must always be santized by escaping for either html or sql)
|
||||||
|
|
||||||
The validator also supports the param -proxy=[address]:[port] for if you use a proxy
|
The validator also supports the param -proxy=[address]:[port] for if you use a
|
||||||
|
proxy
|
||||||
|
|
||||||
Parameters can appear in any order
|
Parameters can appear in any order
|
||||||
|
|
||||||
Alternatively, you can use the validator to execute a transformation as described by a structure map.
|
Alternatively, you can use the validator to execute a transformation as
|
||||||
To do this, you must provide some additional parameters:
|
described by a structure map. To do this, you must provide some additional
|
||||||
|
parameters:
|
||||||
|
|
||||||
-transform [map]
|
-transform [map]
|
||||||
|
|
||||||
|
@ -103,34 +119,39 @@ To do this, you must provide some additional parameters:
|
||||||
|
|
||||||
Any other dependency maps have to be loaded through an -ig reference
|
Any other dependency maps have to be loaded through an -ig reference
|
||||||
|
|
||||||
-transform uses the parameters -defn, -txserver, -ig (at least one with the map files), and -output
|
-transform uses the parameters -defn, -txserver, -ig (at least one with the map
|
||||||
|
files), and -output
|
||||||
|
|
||||||
Alternatively, you can use the validator to generate narrative for a resource.
|
Alternatively, you can use the validator to generate narrative for a resource.
|
||||||
To do this, you must provide a specific parameter:
|
To do this, you must provide a specific parameter:
|
||||||
|
|
||||||
-narrative
|
-narrative
|
||||||
|
|
||||||
-narrative requires the parameters -defn, -txserver, -source, and -output. ig and profile may be used
|
-narrative requires the parameters -defn, -txserver, -source, and -output. ig
|
||||||
|
and profile may be used
|
||||||
|
|
||||||
Alternatively, you can use the validator to convert a resource or logical model.
|
Alternatively, you can use the validator to convert a resource or logical model.
|
||||||
To do this, you must provide a specific parameter:
|
To do this, you must provide a specific parameter:
|
||||||
|
|
||||||
-convert
|
-convert
|
||||||
|
|
||||||
-convert requires the parameters -source and -output. ig may be used to provide a logical model
|
-convert requires the parameters -source and -output. ig may be used to provide
|
||||||
|
a logical model
|
||||||
|
|
||||||
Alternatively, you can use the validator to evaluate a FHIRPath expression on a resource or logical model.
|
Alternatively, you can use the validator to evaluate a FHIRPath expression on a
|
||||||
To do this, you must provide a specific parameter:
|
resource or logical model. To do this, you must provide a specific parameter:
|
||||||
|
|
||||||
-fhirpath [FHIRPath]
|
-fhirpath [FHIRPath]
|
||||||
|
|
||||||
* [FHIRPath] the FHIRPath expression to evaluate
|
* [FHIRPath] the FHIRPath expression to evaluate
|
||||||
|
|
||||||
-fhirpath requires the parameters -source. ig may be used to provide a logical model
|
-fhirpath requires the parameters -source. ig may be used to provide a logical
|
||||||
|
model
|
||||||
|
|
||||||
Finally, you can use the validator to generate a snapshot for a profile.
|
Finally, you can use the validator to generate a snapshot for a profile.
|
||||||
To do this, you must provide a specific parameter:
|
To do this, you must provide a specific parameter:
|
||||||
|
|
||||||
-snapshot
|
-snapshot
|
||||||
|
|
||||||
-snapshot requires the parameters -defn, -txserver, -source, and -output. ig may be used to provide necessary base profiles
|
-snapshot requires the parameters -defn, -txserver, -source, and -output. ig may
|
||||||
|
be used to provide necessary base profiles
|
|
@ -18,6 +18,7 @@ import org.hl7.fhir.r5.formats.IParser;
|
||||||
import org.hl7.fhir.r5.model.Resource;
|
import org.hl7.fhir.r5.model.Resource;
|
||||||
import org.hl7.fhir.r5.model.StructureDefinition;
|
import org.hl7.fhir.r5.model.StructureDefinition;
|
||||||
import org.hl7.fhir.r5.model.StructureMap;
|
import org.hl7.fhir.r5.model.StructureMap;
|
||||||
|
import org.hl7.fhir.r5.test.utils.CompareUtilities;
|
||||||
import org.hl7.fhir.r5.test.utils.TestingUtilities;
|
import org.hl7.fhir.r5.test.utils.TestingUtilities;
|
||||||
import org.hl7.fhir.r5.utils.structuremap.StructureMapUtilities;
|
import org.hl7.fhir.r5.utils.structuremap.StructureMapUtilities;
|
||||||
import org.hl7.fhir.utilities.TextFile;
|
import org.hl7.fhir.utilities.TextFile;
|
||||||
|
@ -103,11 +104,11 @@ public class FHIRMappingLanguageTests {
|
||||||
assertTrue(e.getMessage(), false);
|
assertTrue(e.getMessage(), false);
|
||||||
}
|
}
|
||||||
if (output.endsWith("json")) {
|
if (output.endsWith("json")) {
|
||||||
msg = TestingUtilities.checkJsonSrcIsSame(s.toString(), outputJson);
|
msg = CompareUtilities.checkJsonSrcIsSame(s.toString(), outputJson);
|
||||||
} else {
|
} else {
|
||||||
TextFile.bytesToFile(s.toByteArray(), fileOutputRes);
|
TextFile.bytesToFile(s.toByteArray(), fileOutputRes);
|
||||||
TextFile.bytesToFile(outputJson.getBytes(), fileOutputResOrig);
|
TextFile.bytesToFile(outputJson.getBytes(), fileOutputResOrig);
|
||||||
msg = TestingUtilities.checkXMLIsSame(new FileInputStream(fileOutputRes), new FileInputStream(fileOutputResOrig));
|
msg = CompareUtilities.checkXMLIsSame(new FileInputStream(fileOutputResOrig), new FileInputStream(fileOutputRes));
|
||||||
}
|
}
|
||||||
if (!Utilities.noString(msg)) {
|
if (!Utilities.noString(msg)) {
|
||||||
System.out.print(s.toString());
|
System.out.print(s.toString());
|
||||||
|
|
|
@ -0,0 +1,98 @@
|
||||||
|
package org.hl7.fhir.validation;
|
||||||
|
|
||||||
|
import org.hl7.fhir.exceptions.FHIRException;
|
||||||
|
import org.hl7.fhir.r5.context.SimpleWorkerContext;
|
||||||
|
import org.hl7.fhir.r5.model.ImplementationGuide;
|
||||||
|
import org.hl7.fhir.utilities.npm.FilesystemPackageCacheManager;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.Arguments;
|
||||||
|
import org.junit.jupiter.params.provider.MethodSource;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.Mockito;
|
||||||
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
|
||||||
|
import static org.mockito.Mockito.*;
|
||||||
|
|
||||||
|
@ExtendWith(MockitoExtension.class)
|
||||||
|
public class IgLoaderTests {
|
||||||
|
|
||||||
|
final static String DUMMY_PATH = Paths.get("src","test","resources", "igLoad", "my-dummy-ig.json").toAbsolutePath().toString();
|
||||||
|
final static String DUMMY_FOO_PATH = Paths.get("src","test","resources", "igLoad", "my-dummy-ig.json").toAbsolutePath().toString();
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
FilesystemPackageCacheManager filesystemPackageCacheManager;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
SimpleWorkerContext simpleWorkerContext;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
org.hl7.fhir.utilities.TimeTracker timeTracker;
|
||||||
|
|
||||||
|
private static Stream<Arguments> getTestIgLoadParams() {
|
||||||
|
return Stream.of(
|
||||||
|
Arguments.of(DUMMY_PATH, DUMMY_PATH, "4.0.1"),
|
||||||
|
Arguments.of("[3.0.1]" + DUMMY_PATH, DUMMY_PATH, "3.0.1"),
|
||||||
|
Arguments.of("[" + DUMMY_PATH, "[" + DUMMY_PATH, "4.0.1"),
|
||||||
|
Arguments.of(DUMMY_FOO_PATH, DUMMY_FOO_PATH, "4.0.1"),
|
||||||
|
Arguments.of("[2.0.1]"+DUMMY_FOO_PATH, DUMMY_FOO_PATH, "2.0.1")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@MethodSource("getTestIgLoadParams")
|
||||||
|
public void testIgLoad(String packageString, String expectedPackageName, String expectedFhirVersion) throws IOException {
|
||||||
|
|
||||||
|
final byte[] dummyBytes = {};
|
||||||
|
final String dummyKey = "dummyKey";
|
||||||
|
|
||||||
|
final Map<String, byte[]> dummyMap = new HashMap<>();
|
||||||
|
dummyMap.put(dummyKey, dummyBytes);
|
||||||
|
|
||||||
|
|
||||||
|
IgLoader igLoader = Mockito.spy(new IgLoader(
|
||||||
|
filesystemPackageCacheManager,
|
||||||
|
simpleWorkerContext,
|
||||||
|
"4.0.1"
|
||||||
|
));
|
||||||
|
|
||||||
|
doReturn(dummyMap).when(igLoader).loadIgSource(expectedPackageName, false, true);
|
||||||
|
doReturn(timeTracker).when(simpleWorkerContext).clock();
|
||||||
|
|
||||||
|
List<ImplementationGuide> igs = Collections.emptyList();
|
||||||
|
igLoader.loadIg( igs,
|
||||||
|
Collections.emptyMap(),
|
||||||
|
packageString,
|
||||||
|
false);
|
||||||
|
|
||||||
|
Mockito.verify(igLoader, times(1)).loadResourceByVersion(expectedFhirVersion, dummyBytes, dummyKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFailIfInvalidFHIRVersion() {
|
||||||
|
Exception exception = assertThrows(FHIRException.class, () -> {
|
||||||
|
IgLoader igLoader = Mockito.spy(new IgLoader(
|
||||||
|
filesystemPackageCacheManager,
|
||||||
|
simpleWorkerContext,
|
||||||
|
"4.0.1"
|
||||||
|
));
|
||||||
|
|
||||||
|
List<ImplementationGuide> igs = Collections.emptyList();
|
||||||
|
igLoader.loadIg(igs,
|
||||||
|
Collections.emptyMap(),
|
||||||
|
"[0.1.2]" + DUMMY_PATH,
|
||||||
|
false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue