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
|
||||
|
||||
* 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;
|
||||
|
||||
import ca.uhn.fhir.parser.DataFormatException;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
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 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
|
||||
@DisplayName("Passing a non Base64 encoded String to constructor causes exception.")
|
||||
public void testNonBase64String() {
|
||||
|
@ -45,6 +48,15 @@ class Base64BinaryTypeTest {
|
|||
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
|
||||
@DisplayName("Valid Base64 String creates non-null instance with non-null values.")
|
||||
public void testValid() {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package org.hl7.fhir.dstu2016may.model;
|
||||
|
||||
import ca.uhn.fhir.parser.DataFormatException;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
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 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
|
||||
@DisplayName("Passing a non Base64 encoded String to constructor causes exception.")
|
||||
public void testNonBase64String() {
|
||||
|
@ -45,6 +48,15 @@ class Base64BinaryTypeTest {
|
|||
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
|
||||
@DisplayName("Valid Base64 String creates non-null instance with non-null values.")
|
||||
public void testValid() {
|
||||
|
|
|
@ -130,7 +130,7 @@ public class Base64BinaryType extends PrimitiveType<byte[]> implements IPrimitiv
|
|||
@Override
|
||||
public Base64BinaryType setValue(byte[] theValue) throws IllegalArgumentException {
|
||||
myValue = theValue;
|
||||
return this;
|
||||
return (Base64BinaryType) super.setValue(theValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package org.hl7.fhir.dstu3.model;
|
||||
|
||||
import ca.uhn.fhir.parser.DataFormatException;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
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 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
|
||||
@DisplayName("Passing a non Base64 encoded String to constructor causes exception.")
|
||||
public void testNonBase64String() {
|
||||
|
@ -54,6 +57,15 @@ class Base64BinaryTypeTest {
|
|||
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
|
||||
@DisplayName("Valid Base64 String creates non-null instance with non-null values.")
|
||||
public void testValidSetValueAsString() {
|
||||
|
|
|
@ -130,7 +130,7 @@ public class Base64BinaryType extends PrimitiveType<byte[]> implements IPrimitiv
|
|||
@Override
|
||||
public Base64BinaryType setValue(byte[] theValue) throws IllegalArgumentException {
|
||||
myValue = theValue;
|
||||
return this;
|
||||
return (Base64BinaryType) super.setValue(theValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package org.hl7.fhir.r4.model;
|
||||
|
||||
import ca.uhn.fhir.parser.DataFormatException;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
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 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
|
||||
@DisplayName("Passing a non Base64 encoded String to constructor causes exception.")
|
||||
public void testNonBase64String() {
|
||||
|
@ -54,6 +57,15 @@ class Base64BinaryTypeTest {
|
|||
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
|
||||
@DisplayName("Valid Base64 String creates non-null instance with non-null values.")
|
||||
public void testValidSetValueAsString() {
|
||||
|
|
|
@ -129,7 +129,7 @@ public class Base64BinaryType extends PrimitiveType<byte[]> implements IPrimitiv
|
|||
@Override
|
||||
public Base64BinaryType setValue(byte[] theValue) throws IllegalArgumentException {
|
||||
myValue = theValue;
|
||||
return this;
|
||||
return (Base64BinaryType) super.setValue(theValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -2,6 +2,7 @@ package org.hl7.fhir.r4b.model;
|
|||
|
||||
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.DisplayName;
|
||||
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 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
|
||||
@DisplayName("Passing a non Base64 encoded String to constructor causes exception.")
|
||||
public void testNonBase64String() {
|
||||
|
@ -55,6 +58,15 @@ class Base64BinaryTypeTest {
|
|||
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
|
||||
@DisplayName("Valid Base64 String creates non-null instance with non-null values.")
|
||||
public void testValidSetValueAsString() {
|
||||
|
|
|
@ -129,7 +129,7 @@ public class Base64BinaryType extends PrimitiveType<byte[]> implements IPrimitiv
|
|||
@Override
|
||||
public Base64BinaryType setValue(byte[] theValue) throws IllegalArgumentException {
|
||||
myValue = theValue;
|
||||
return this;
|
||||
return (Base64BinaryType) super.setValue(theValue);
|
||||
}
|
||||
|
||||
@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 {
|
||||
boolean hasExtensions = false;
|
||||
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) {
|
||||
XhtmlNode h = x.addTag(getHeader());
|
||||
|
@ -219,20 +219,24 @@ public class ValueSetRenderer extends TerminologyRenderer {
|
|||
tr.td().b().tx("System");
|
||||
XhtmlNode tdDisp = tr.td();
|
||||
tdDisp.b().tx("Display");
|
||||
boolean doLangs = false;
|
||||
boolean doDesignations = false;
|
||||
for (ValueSetExpansionContainsComponent c : vs.getExpansion().getContains()) {
|
||||
scanForLangs(c, langs);
|
||||
scanForDesignations(c, langs, designations);
|
||||
}
|
||||
if (doDefinition) {
|
||||
tr.td().b().tx("Definition");
|
||||
doLangs = false;
|
||||
doDesignations = false;
|
||||
} else {
|
||||
// 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) {
|
||||
doLangs = true;
|
||||
doDesignations = langs.size() + designations.size() < MAX_DESIGNATIONS_IN_LINE;
|
||||
|
||||
if (doDesignations) {
|
||||
if (vs.hasLanguage()) {
|
||||
tdDisp.tx(" - "+describeLang(vs.getLanguage()));
|
||||
}
|
||||
for (String url : designations.keySet()) {
|
||||
tr.td().b().addText(designations.get(url));
|
||||
}
|
||||
for (String lang : langs) {
|
||||
tr.td().b().addText(describeLang(lang));
|
||||
}
|
||||
|
@ -242,22 +246,31 @@ public class ValueSetRenderer extends TerminologyRenderer {
|
|||
|
||||
addMapHeaders(tr, maps);
|
||||
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
|
||||
|
||||
if (!doLangs && langs.size() > 0) {
|
||||
if (!doDesignations && langs.size() + designations.size() > 0) {
|
||||
Collections.sort(langs);
|
||||
x.para().b().tx("Additional Language Displays");
|
||||
t = x.table( "codes");
|
||||
if (designations.size() == 0) {
|
||||
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.td().b().tx("Code");
|
||||
for (String url : designations.keySet()) {
|
||||
tr.td().b().addText(designations.get(url));
|
||||
}
|
||||
for (String lang : langs) {
|
||||
tr.td().b().addText(describeLang(lang));
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
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();
|
||||
tr.td().addText(c.getCode());
|
||||
addDesignationsToRow(c, designations, tr);
|
||||
addLangaugesToRow(c, langs, tr);
|
||||
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("\\", "/");
|
||||
}
|
||||
|
||||
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) {
|
||||
for (Extension ext : c.getExtension()) {
|
||||
if (ToolingExtensions.EXT_TRANSLATION.equals(ext.getUrl())) {
|
||||
|
@ -652,7 +710,7 @@ public class ValueSetRenderer extends TerminologyRenderer {
|
|||
}
|
||||
}
|
||||
|
||||
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 td = tr.td();
|
||||
|
||||
|
@ -697,11 +755,12 @@ public class ValueSetRenderer extends TerminologyRenderer {
|
|||
td.i().tx("("+mapping.comp.getComment()+")");
|
||||
}
|
||||
}
|
||||
if (doLangs) {
|
||||
if (doDesignations) {
|
||||
addDesignationsToRow(c, designations, tr);
|
||||
addLangaugesToRow(c, langs, tr);
|
||||
}
|
||||
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) {
|
||||
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)
|
||||
td.i().setAttribute("title", ABSTRACT_CODE_HINT).addText(code);
|
||||
else if ("http://snomed.info/sct".equals(system)) {
|
||||
|
@ -824,7 +883,7 @@ public class ValueSetRenderer extends TerminologyRenderer {
|
|||
}
|
||||
for (ConceptSetComponent c : vs.getCompose().getInclude()) {
|
||||
for (ConceptReferenceComponent cc : c.getConcept()) {
|
||||
addLanguageRow(cc, t, langs);
|
||||
addDesignationRow(cc, t, langs, designations);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -940,8 +999,11 @@ public class ValueSetRenderer extends TerminologyRenderer {
|
|||
langs.add(lang);
|
||||
} else {
|
||||
// can we present this as a designation that we know?
|
||||
String disp = getDisplayForDesignation(d);
|
||||
String url = getUrlForDesignation(d);
|
||||
String disp = getDisplayForUrl(url);
|
||||
if (disp == null) {
|
||||
disp = getDisplayForUrl(url);
|
||||
}
|
||||
if (disp != null && !designations.containsKey(url)) {
|
||||
designations.put(url, disp);
|
||||
}
|
||||
|
@ -960,7 +1022,8 @@ public class ValueSetRenderer extends TerminologyRenderer {
|
|||
case "http://snomed.info/sct#900000000000013009":
|
||||
return "Synonym";
|
||||
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 {
|
||||
boolean hasExtensions = false;
|
||||
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();
|
||||
tr.td().addText(c.getCode());
|
||||
for (String lang : langs) {
|
||||
String d = null;
|
||||
for (ConceptReferenceDesignationComponent cd : c.getDesignation()) {
|
||||
String l = cd.getLanguage();
|
||||
if (lang.equals(l))
|
||||
d = cd.getValue();
|
||||
}
|
||||
tr.td().addText(d == null ? "" : d);
|
||||
}
|
||||
addDesignationsToRow(c, designations, tr);
|
||||
addLangaugesToRow(c, langs, tr);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
public class TestingUtilities extends BaseTestingUtilities {
|
||||
private static final boolean SHOW_DIFF = true;
|
||||
|
||||
static public Map<String, IWorkerContext> fcontexts;
|
||||
|
||||
|
@ -178,346 +177,5 @@ public class TestingUtilities extends BaseTestingUtilities {
|
|||
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 org.apache.commons.codec.binary.Base64;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import ca.uhn.fhir.parser.DataFormatException;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
class Base64BinaryTypeTest {
|
||||
|
||||
static final String NON_BASE_64 = "Picard was the best starship captain.";
|
||||
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
|
||||
@DisplayName("Passing a non Base64 encoded String to constructor causes exception.")
|
||||
public void testNonBase64String() {
|
||||
|
@ -55,6 +59,15 @@ class Base64BinaryTypeTest {
|
|||
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
|
||||
@DisplayName("Valid Base64 String creates non-null instance with non-null values.")
|
||||
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.DomainResource;
|
||||
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.utils.GraphQLEngine;
|
||||
import org.hl7.fhir.utilities.TextFile;
|
||||
|
@ -88,10 +89,10 @@ public class GraphQLEngineTests implements IGraphQLStorageServices {
|
|||
StringBuilder str = new StringBuilder();
|
||||
gql.getOutput().setWriteWrapper(false);
|
||||
gql.getOutput().write(str, 0);
|
||||
IOUtils.copy(TestingUtilities.loadTestResourceStream("r5", "graphql", source), new FileOutputStream(TestingUtilities.tempFile("graphql", source)));
|
||||
IOUtils.copy(TestingUtilities.loadTestResourceStream("r5", "graphql", output), new FileOutputStream(TestingUtilities.tempFile("graphql", output)));
|
||||
TextFile.stringToFile(str.toString(), TestingUtilities.tempFile("graphql", output+".out"));
|
||||
msg = TestingUtilities.checkJsonIsSame(TestingUtilities.tempFile("graphql", output+".out"), TestingUtilities.tempFile("graphql", output));
|
||||
IOUtils.copy(CompareUtilities.loadTestResourceStream("r5", "graphql", source), new FileOutputStream(CompareUtilities.tempFile("graphql", source)));
|
||||
IOUtils.copy(CompareUtilities.loadTestResourceStream("r5", "graphql", output), new FileOutputStream(CompareUtilities.tempFile("graphql", output)));
|
||||
TextFile.stringToFile(str.toString(), CompareUtilities.tempFile("graphql", output+".out"));
|
||||
msg = CompareUtilities.checkJsonIsSame(CompareUtilities.tempFile("graphql", output), CompareUtilities.tempFile("graphql", output+".out"));
|
||||
Assertions.assertTrue(Utilities.noString(msg), msg);
|
||||
}
|
||||
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.ITypeParser;
|
||||
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.utilities.TerminologyServiceOptions;
|
||||
import org.hl7.fhir.utilities.TextFile;
|
||||
|
@ -134,11 +135,11 @@ public class NarrativeGenerationTests {
|
|||
XhtmlNode x = RendererFactory.factory(source, rc).build(source);
|
||||
String expected = TextFile.streamToString(TestingUtilities.loadTestResourceStream("r5", "narrative", test.getId() + ".html"));
|
||||
String actual = HEADER+new XhtmlComposer(true, true).compose(x)+FOOTER;
|
||||
String expectedFileName = TestingUtilities.tempFile("narrative", test.getId() + ".expected.html");
|
||||
String actualFileName = TestingUtilities.tempFile("narrative", test.getId() + ".actual.html");
|
||||
String expectedFileName = CompareUtilities.tempFile("narrative", test.getId() + ".expected.html");
|
||||
String actualFileName = CompareUtilities.tempFile("narrative", test.getId() + ".actual.html");
|
||||
TextFile.stringToFile(expected, expectedFileName);
|
||||
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);
|
||||
|
||||
if (test.isMeta()) {
|
||||
|
@ -147,9 +148,9 @@ public class NarrativeGenerationTests {
|
|||
|
||||
expected = TextFile.streamToString(TestingUtilities.loadTestResourceStream("r5", "narrative", test.getId() + "-meta.html"));
|
||||
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);
|
||||
msg = TestingUtilities.checkXMLIsSame(actualFileName, expectedFileName);
|
||||
msg = CompareUtilities.checkXMLIsSame(expectedFileName, actualFileName);
|
||||
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.utils.RenderingContext;
|
||||
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.utils.EOperationOutcome;
|
||||
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.openapi.OpenApiGenerator;
|
||||
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.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.XmlParser;
|
||||
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.utilities.TextFile;
|
||||
import org.hl7.fhir.utilities.npm.FilesystemPackageCacheManager;
|
||||
|
@ -57,7 +58,7 @@ public class ParsingTests {
|
|||
r = new XmlParser().parse(b);
|
||||
b = new JsonParser().setOutputStyle(OutputStyle.PRETTY).composeBytes(r);
|
||||
String output = new String(b);
|
||||
String msg = TestingUtilities.checkJsonSrcIsSame(src, output);
|
||||
String msg = CompareUtilities.checkJsonSrcIsSame(src, output);
|
||||
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.utils.RenderingContext;
|
||||
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.utils.EOperationOutcome;
|
||||
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.FHIRFormatError;
|
||||
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.formats.IParser.OutputStyle;
|
||||
import org.hl7.fhir.r5.formats.JsonParser;
|
||||
|
@ -146,11 +147,11 @@ public class VocabTests {
|
|||
outcome.getValueset().getExpansion().setTimestamp(null);
|
||||
String expected = new XmlParser().setOutputStyle(OutputStyle.PRETTY).composeString(targetVS);
|
||||
String actual = new XmlParser().setOutputStyle(OutputStyle.PRETTY).composeString(outcome.getValueset());
|
||||
String expectedFileName = TestingUtilities.tempFile("vocab", test.getId() + ".expected.html");
|
||||
String actualFileName = TestingUtilities.tempFile("vocab", test.getId() + ".actual.html");
|
||||
String expectedFileName = CompareUtilities.tempFile("vocab", test.getId() + ".expected.html");
|
||||
String actualFileName = CompareUtilities.tempFile("vocab", test.getId() + ".actual.html");
|
||||
TextFile.stringToFile(expected, expectedFileName);
|
||||
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);
|
||||
} else {
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @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,
|
||||
Map<String, byte[]> binaries,
|
||||
String src,
|
||||
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) {
|
||||
for (String s : npm.dependencies()) {
|
||||
if (!getContext().getLoadedPackages().contains(s)) {
|
||||
|
@ -82,17 +108,17 @@ public class IgLoader {
|
|||
}
|
||||
}
|
||||
}
|
||||
System.out.print(" Load " + src);
|
||||
if (!src.contains("#")) {
|
||||
System.out.print(" Load " + srcPackage);
|
||||
if (!srcPackage.contains("#")) {
|
||||
System.out.print("#" + npm.version());
|
||||
}
|
||||
int count = getContext().loadFromPackage(npm, ValidatorUtils.loaderForVersion(npm.fhirVersion()));
|
||||
System.out.println(" - " + count + " resources (" + getContext().clock().milestone() + ")");
|
||||
} else {
|
||||
System.out.print(" Load " + src);
|
||||
System.out.print(" Load " + srcPackage);
|
||||
String canonical = null;
|
||||
int count = 0;
|
||||
Map<String, byte[]> source = loadIgSource(src, recursive, true);
|
||||
Map<String, byte[]> source = loadIgSource(srcPackage, recursive, true);
|
||||
String version = Constants.VERSION;
|
||||
if (getVersion() != null) {
|
||||
version = getVersion();
|
||||
|
@ -100,6 +126,10 @@ public class IgLoader {
|
|||
if (source.containsKey("version.info")) {
|
||||
version = readInfoVersion(source.get("version.info"));
|
||||
}
|
||||
if (explicitFhirVersion != null) {
|
||||
version = explicitFhirVersion;
|
||||
}
|
||||
|
||||
for (Map.Entry<String, byte[]> t : source.entrySet()) {
|
||||
String fn = t.getKey();
|
||||
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 {
|
||||
Map<String, byte[]> s = loadIgSource(source, false, asIg);
|
||||
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
|
||||
**/
|
||||
*/
|
||||
public Map<String, byte[]> loadIgSource(String src,
|
||||
boolean recursive,
|
||||
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)) {
|
||||
String v = null;
|
||||
if (src.contains("|")) {
|
||||
|
@ -627,7 +674,7 @@ public class IgLoader {
|
|||
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);
|
||||
Resource r = null;
|
||||
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 {
|
||||
NpmPackage npm = getPcm().loadPackage(src, 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
|
||||
command line
|
||||
|
||||
The FHIR validation tool validates a FHIR resource or bundle.
|
||||
Schema and schematron checking is performed, then some additional checks are performed.
|
||||
The FHIR validation tool validates a FHIR resource or bundle. Schema and
|
||||
schematron checking is performed, then some additional checks are performed.
|
||||
|
||||
* XML & Json (FHIR versions {{XML_AND_JSON_FHIR_VERSIONS}})
|
||||
* Turtle (FHIR versions {{TURTLE_FHIR_VERSIONS}})
|
||||
|
||||
If requested, instances will also be verified against the appropriate schema
|
||||
W3C XML Schema, JSON schema or ShEx, as appropriate
|
||||
If requested, instances will also be verified against the appropriate schema W3C
|
||||
XML Schema, JSON schema or ShEx, as appropriate
|
||||
|
||||
Usage: java -jar [validator].jar (parameters)
|
||||
|
||||
The following parameters are supported:
|
||||
[source]: a file, url, directory or pattern for resources to validate. At
|
||||
least one source must be declared. If there is more than one source or if
|
||||
the source is other than a single file or url and the output parameter is
|
||||
[source]: a file, url, directory or pattern for resources to validate.
|
||||
At least one source must be declared. If there is more than one source or
|
||||
if the source is other than a single file or url and the output parameter is
|
||||
used, results will be provided as a Bundle.
|
||||
Patterns are limited to a directory followed by a filename with an embedded
|
||||
asterisk. E.g. foo*-examples.xml or someresource.*, etc.
|
||||
-version [ver]: The FHIR version to use. This can only appear once.
|
||||
valid values {{FHIR_MAJOR_VERSIONS}} or {{FHIR_MINOR_VERSIONS}}
|
||||
Default value is {{FHIR_CURRENT_VERSION}}
|
||||
-ig [package|file|folder|url]: an IG or profile definition to load. Can be
|
||||
the URL of an implementation guide or a package ([id]-[ver]) for
|
||||
a built implementation guide or a local folder that contains a
|
||||
set of conformance resources.
|
||||
If you would like to load the latest unreleased version of the implementation guide or package,
|
||||
please define the version as '#current'. If no version is provided, the latest version
|
||||
in the package cache will be used, or if no such cached package is available, the
|
||||
PackageCacheManager will load the latest from the the online package repo.
|
||||
No default value. This parameter can appear any number of times
|
||||
Patterns are limited to a directory followed by a filename with an
|
||||
embedded asterisk. E.g. foo*-examples.xml or someresource.*, etc.
|
||||
-version [ver]: The FHIR version to use.
|
||||
This can only appear once.
|
||||
valid values {{FHIR_MAJOR_VERSIONS}} or {{FHIR_MINOR_VERSIONS}}
|
||||
Default value is {{FHIR_CURRENT_VERSION}}
|
||||
-ig [package|file|folder|url]: an IG or profile definition to load.
|
||||
Can be the URL of an implementation guide or a package ([id]-[ver]) for a
|
||||
built implementation guide or a local folder that contains a set of
|
||||
conformance resources.
|
||||
If you would like to load the latest unreleased version of the
|
||||
implementation guide or package, please define the version as '#current'.
|
||||
If no version is provided, the latest version in the package cache will
|
||||
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
|
||||
Default value is http://tx.fhir.org. This parameter can appear once
|
||||
To run without terminology value, specific n/a as the URL
|
||||
Default value is http://tx.fhir.org. This parameter can appear once
|
||||
To run without terminology value, specific n/a as the URL
|
||||
-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
|
||||
specified in Resource.meta.profile). If no profile is specified, the
|
||||
resource is validated against the base specification. This parameter
|
||||
can appear any number of times.
|
||||
Note: the profile (and it's dependencies) have to be made available
|
||||
specified in Resource.meta.profile).
|
||||
If no profile is specified, the resource is validated against the base
|
||||
specification. This parameter can appear any number of times.
|
||||
Note: the profile (and it's dependencies) have to be made available
|
||||
through one of the -ig parameters. Note that package dependencies will
|
||||
automatically be resolved
|
||||
-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
|
||||
validation messages, but may allow easier debugging. If not specified,
|
||||
then only a high-level message indicating that the referenced item wasn't
|
||||
valid against the listed profile(s) will be provided.
|
||||
-questionnaire mode: what to do with when validating QuestionnaireResponse resources
|
||||
none (default): just ignore the questionnaire reference
|
||||
required: check that the QuestionnaireResponse has a questionnaire and validate against it
|
||||
check: if the QuestionnaireResponse has a questionnaire, validate against it
|
||||
The questionnaire must be loaded using the -ig parameter
|
||||
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
|
||||
-questionnaire mode: what to do when validating QuestionnaireResponse resources
|
||||
* none (default): just ignore the questionnaire reference
|
||||
* required: check that the QuestionnaireResponse has a questionnaire and
|
||||
validate against it
|
||||
* check: if the QuestionnaireResponse has a questionnaire, validate
|
||||
against it
|
||||
The questionnaire must be loaded using the -ig parameter
|
||||
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)
|
||||
Default: results are sent to the std out.
|
||||
Default: results are sent to the std out.
|
||||
-debug
|
||||
Produce additional information about the loading/validation process
|
||||
Produce additional information about the loading/validation process
|
||||
-recurse
|
||||
Look in subfolders when -ig refers to a folder
|
||||
Look in subfolders when -ig refers to a folder
|
||||
-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
|
||||
Specify the edition of SNOMED CT to use. Valid Choices:
|
||||
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
|
||||
ask on https://chat.fhir.org/#narrow/stream/179202-terminology
|
||||
tx.fhir.org only supports a subset. To add to this list or tx.fhir.org ask
|
||||
on https://chat.fhir.org/#narrow/stream/179202-terminology
|
||||
-native: use schema for validation as well
|
||||
* XML: w3c schema+schematron
|
||||
* JSON: json.schema
|
||||
* RDF: SHEX
|
||||
Default: false
|
||||
-language: [lang]
|
||||
The language to use when validating coding displays - same value as for xml:lang
|
||||
Not used if the resource specifies language
|
||||
Default: no specified language
|
||||
-strictExtensions: If present, treat extensions not defined within the specified FHIR version and any
|
||||
referenced implementation guides or profiles as errors. (Default is to only raise information messages.)
|
||||
-hintAboutNonMustSupport: If present, raise hints if the instance contains data elements that are not
|
||||
marked as mustSupport=true. Useful to identify elements included that may be ignored by recipients
|
||||
-assumeValidRestReferences: If present, assume that URLs that reference 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)
|
||||
-language: [lang] The language to use when validating coding displays - same
|
||||
value as for xml:lang
|
||||
Not used if the resource specifies language
|
||||
Default: no specified language
|
||||
-strictExtensions: If present, treat extensions not defined within the specified
|
||||
FHIR version and any referenced implementation guides or profiles as errors.
|
||||
(Default is to only raise information messages.)
|
||||
-hintAboutNonMustSupport: If present, raise hints if the instance contains data
|
||||
elements that are not marked as mustSupport=true. Useful to identify
|
||||
elements included that may be ignored by recipients
|
||||
-assumeValidRestReferences: If present, assume that URLs that reference
|
||||
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
|
||||
|
||||
Alternatively, you can use the validator to execute a transformation as described by a structure map.
|
||||
To do this, you must provide some additional parameters:
|
||||
Alternatively, you can use the validator to execute a transformation as
|
||||
described by a structure map. To do this, you must provide some additional
|
||||
parameters:
|
||||
|
||||
-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
|
||||
|
||||
-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.
|
||||
To do this, you must provide a specific parameter:
|
||||
|
||||
-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.
|
||||
To do this, you must provide a specific parameter:
|
||||
|
||||
-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.
|
||||
To do this, you must provide a specific parameter:
|
||||
Alternatively, you can use the validator to evaluate a FHIRPath expression on a
|
||||
resource or logical model. To do this, you must provide a specific parameter:
|
||||
|
||||
-fhirpath [FHIRPath]
|
||||
|
||||
* [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.
|
||||
To do this, you must provide a specific parameter:
|
||||
|
||||
-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.StructureDefinition;
|
||||
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.utils.structuremap.StructureMapUtilities;
|
||||
import org.hl7.fhir.utilities.TextFile;
|
||||
|
@ -103,11 +104,11 @@ public class FHIRMappingLanguageTests {
|
|||
assertTrue(e.getMessage(), false);
|
||||
}
|
||||
if (output.endsWith("json")) {
|
||||
msg = TestingUtilities.checkJsonSrcIsSame(s.toString(), outputJson);
|
||||
msg = CompareUtilities.checkJsonSrcIsSame(s.toString(), outputJson);
|
||||
} else {
|
||||
TextFile.bytesToFile(s.toByteArray(), fileOutputRes);
|
||||
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)) {
|
||||
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