diff --git a/README.md b/README.md index 7e678e2f0..1b76917f3 100644 --- a/README.md +++ b/README.md @@ -653,20 +653,23 @@ consistent formatting using the [`spotless`](https://github.com/diffplug/spotles ### Testing -The library includes comprehensive **serialization round-trip tests** for examples published in the +The library includes comprehensive **serialization round-trip and equality tests** for the example resources published in the following packages: - [hl7.fhir.r4.examples](https://simplifier.net/packages/hl7.fhir.r4.examples) (5309 examples) - [hl7.fhir.r4b.examples](https://simplifier.net/packages/hl7.fhir.r4b.examples) (2840 examples) - [hl7.fhir.r5.examples](https://simplifier.net/packages/hl7.fhir.r5.examples) (2822 examples) -For each JSON example of a FHIR resource in the packages above, a test is performed with the -following steps: +For each JSON example of a FHIR resource in the referenced packages, two categories of tests are executed: -1. Deserialization: The JSON is deserialized into the corresponding generated Kotlin resource class. -2. Serialization: The Kotlin object is then serialized back into JSON format. -3. Verification: The newly generated JSON is compared, character by character[^7], to the original - JSON to ensure complete fidelity. +1. Serialization round-trip test: + - Deserialization: The JSON is parsed into its corresponding generated Kotlin resource class. + - Serialization: That Kotlin object is then converted back into JSON. + - Verification: The regenerated JSON is compared character by character[^7] with the original to confirm exact fidelity. +2. Equality test: + - First instance: Deserialize the JSON into a Kotlin resource object. + - Second instance: Deserialize the same JSON into a separate Kotlin resource object. + - Verification: The two objects are structurally equal (using `==` operator). [^7]: There are several exceptions. The FHIR specification allows for some variability in data representation, which may lead to differences between the original and newly serialized JSON. For diff --git a/fhir-codegen/gradle-plugin/src/main/kotlin/com/google/fhir/codegen/ModelTypeSpecGenerator.kt b/fhir-codegen/gradle-plugin/src/main/kotlin/com/google/fhir/codegen/ModelTypeSpecGenerator.kt index c534e1160..18dada844 100644 --- a/fhir-codegen/gradle-plugin/src/main/kotlin/com/google/fhir/codegen/ModelTypeSpecGenerator.kt +++ b/fhir-codegen/gradle-plugin/src/main/kotlin/com/google/fhir/codegen/ModelTypeSpecGenerator.kt @@ -81,6 +81,11 @@ class ModelTypeSpecGenerator(private val valueSetMap: Map) { // with '_'. if (structureDefinition.name == "Element") { addModifiers(KModifier.OPEN) + // Implement equals/hashCode for Element to compare properties (like data classes). + addEqualsAndHashCodeFunctions( + structureDefinition.name, + structureDefinition.rootElements, + ) } else { addModifiers(KModifier.SEALED) } @@ -92,6 +97,12 @@ class ModelTypeSpecGenerator(private val valueSetMap: Map) { // since they need to be subclassed. E.g. Uri can be extended by Url, and Quantity can // be extended by Duration. addModifiers(KModifier.OPEN) + // Implement equals/hashCode for open classes (to perform property-based comparison, + // like data classes). + addEqualsAndHashCodeFunctions( + structureDefinition.name, + structureDefinition.rootElements, + ) } else { addModifiers(KModifier.DATA) } @@ -195,6 +206,55 @@ class ModelTypeSpecGenerator(private val valueSetMap: Map) { return typeSpec } + private fun TypeSpec.Builder.addEqualsAndHashCodeFunctions( + name: String, + elements: List, + ) { + if (elements.isEmpty()) return + val equalsFunSpec = + FunSpec.builder("equals") + .addModifiers(KModifier.OVERRIDE) + .addParameter("other", Any::class.asTypeName().copy(nullable = true)) + .returns(Boolean::class) + .addCode( + """ + if (this === other) return true + if (other !is ${name.capitalized()}) return false + """ + .trimIndent() + ) + .addCode( + elements.joinToString(separator = "\n", prefix = "\n", postfix = "\n") { + "if( ${it.getElementName()} != other.${it.getElementName()}) return false" + } + ) + .addCode("return true") + .build() + + this.addFunction(equalsFunSpec) + + val hashCodeFunSpec = + FunSpec.builder("hashCode") + .addModifiers(KModifier.OVERRIDE) + .returns(Int::class) + .addCode( + "// Using 31 improves hash distribution and reduces collisions in hash-based collections\n" + ) + .addCode("var result = ${elements.first().getElementName()}?.hashCode() ?: 0") + .addCode( + elements.subList(1, elements.size).joinToString( + separator = "\n", + prefix = "\n", + postfix = "\n", + ) { + "result = 31 * result + (${it.getElementName()}?.hashCode() ?: 0)" + } + ) + .addCode("return result") + .build() + this.addFunction(hashCodeFunSpec) + } + /** Adds a nested class for each BackboneElement in the [StructureDefinition]. */ private fun TypeSpec.Builder.addBackboneElement( path: String, diff --git a/fhir-codegen/gradle-plugin/src/main/kotlin/com/google/fhir/codegen/primitives/FhirDateFileSpecGenerator.kt b/fhir-codegen/gradle-plugin/src/main/kotlin/com/google/fhir/codegen/primitives/FhirDateFileSpecGenerator.kt index e575c310d..1880c7197 100644 --- a/fhir-codegen/gradle-plugin/src/main/kotlin/com/google/fhir/codegen/primitives/FhirDateFileSpecGenerator.kt +++ b/fhir-codegen/gradle-plugin/src/main/kotlin/com/google/fhir/codegen/primitives/FhirDateFileSpecGenerator.kt @@ -64,6 +64,7 @@ object FhirDateFileSpecGenerator { ) addType( TypeSpec.classBuilder("YearMonth") + .addModifiers(KModifier.DATA) .addSuperinterface(sealedInterfaceClassName) .primaryConstructor( FunSpec.constructorBuilder() @@ -84,6 +85,7 @@ object FhirDateFileSpecGenerator { ) addType( TypeSpec.classBuilder("Date") + .addModifiers(KModifier.DATA) .addSuperinterface(sealedInterfaceClassName) .primaryConstructor( FunSpec.constructorBuilder().addParameter("date", LocalDate::class).build() diff --git a/fhir-codegen/gradle-plugin/src/main/kotlin/com/google/fhir/codegen/primitives/FhirDateTimeFileSpecGenerator.kt b/fhir-codegen/gradle-plugin/src/main/kotlin/com/google/fhir/codegen/primitives/FhirDateTimeFileSpecGenerator.kt index e6fb83e74..3885b996a 100644 --- a/fhir-codegen/gradle-plugin/src/main/kotlin/com/google/fhir/codegen/primitives/FhirDateTimeFileSpecGenerator.kt +++ b/fhir-codegen/gradle-plugin/src/main/kotlin/com/google/fhir/codegen/primitives/FhirDateTimeFileSpecGenerator.kt @@ -69,6 +69,7 @@ object FhirDateTimeFileSpecGenerator { ) addType( TypeSpec.classBuilder("YearMonth") + .addModifiers(KModifier.DATA) .addSuperinterface(sealedInterfaceClassName) .primaryConstructor( FunSpec.constructorBuilder() @@ -89,6 +90,7 @@ object FhirDateTimeFileSpecGenerator { ) addType( TypeSpec.classBuilder("Date") + .addModifiers(KModifier.DATA) .addSuperinterface(sealedInterfaceClassName) .primaryConstructor( FunSpec.constructorBuilder().addParameter("date", LocalDate::class).build() @@ -107,6 +109,7 @@ object FhirDateTimeFileSpecGenerator { ) addType( TypeSpec.classBuilder("DateTime") + .addModifiers(KModifier.DATA) .addSuperinterface(sealedInterfaceClassName) .primaryConstructor( FunSpec.constructorBuilder() diff --git a/fhir-model/build.gradle.kts b/fhir-model/build.gradle.kts index f5941d40f..478950188 100644 --- a/fhir-model/build.gradle.kts +++ b/fhir-model/build.gradle.kts @@ -134,7 +134,11 @@ kotlin { } } val jvmMain by getting - val jvmTest by getting + val jvmTest by getting { + dependencies { + implementation("org.jetbrains.kotlin:kotlin-reflect") + } + } val jsMain by getting val jsTest by getting } diff --git a/fhir-model/src/androidInstrumentedTest/kotlin/com/google/fhir/model/SerializationRoundTripTest.android.kt b/fhir-model/src/androidInstrumentedTest/kotlin/com/google/fhir/model/TestLoader.android.kt similarity index 100% rename from fhir-model/src/androidInstrumentedTest/kotlin/com/google/fhir/model/SerializationRoundTripTest.android.kt rename to fhir-model/src/androidInstrumentedTest/kotlin/com/google/fhir/model/TestLoader.android.kt diff --git a/fhir-model/src/commonMain/kotlin/com/google/fhir/model/r4/Element.kt b/fhir-model/src/commonMain/kotlin/com/google/fhir/model/r4/Element.kt index c39ab3857..3a49bd23a 100644 --- a/fhir-model/src/commonMain/kotlin/com/google/fhir/model/r4/Element.kt +++ b/fhir-model/src/commonMain/kotlin/com/google/fhir/model/r4/Element.kt @@ -18,6 +18,9 @@ package com.google.fhir.model.r4 +import kotlin.Any +import kotlin.Boolean +import kotlin.Int import kotlin.String import kotlin.Suppress import kotlin.collections.MutableList @@ -44,4 +47,19 @@ public open class Element( * simplicity for everyone. */ public open var extension: MutableList = mutableListOf(), -) +) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is Element) return false + if (id != other.id) return false + if (extension != other.extension) return false + return true + } + + override fun hashCode(): Int { + // Using 31 improves hash distribution and reduces collisions in hash-based collections + var result = id?.hashCode() ?: 0 + result = 31 * result + (extension?.hashCode() ?: 0) + return result + } +} diff --git a/fhir-model/src/commonMain/kotlin/com/google/fhir/model/r4/FhirDate.kt b/fhir-model/src/commonMain/kotlin/com/google/fhir/model/r4/FhirDate.kt index da25c158a..ea1803db3 100644 --- a/fhir-model/src/commonMain/kotlin/com/google/fhir/model/r4/FhirDate.kt +++ b/fhir-model/src/commonMain/kotlin/com/google/fhir/model/r4/FhirDate.kt @@ -30,11 +30,11 @@ public sealed interface FhirDate { override fun toString(): String = value.toString() } - public class YearMonth(public val year: Int, public val month: Int) : FhirDate { + public data class YearMonth(public val year: Int, public val month: Int) : FhirDate { override fun toString(): String = "$year-${month.toString().padStart(2,'0')}" } - public class Date(public val date: LocalDate) : FhirDate { + public data class Date(public val date: LocalDate) : FhirDate { override fun toString(): String = date.toString() } diff --git a/fhir-model/src/commonMain/kotlin/com/google/fhir/model/r4/FhirDateTime.kt b/fhir-model/src/commonMain/kotlin/com/google/fhir/model/r4/FhirDateTime.kt index b4843a45c..ca268fe20 100644 --- a/fhir-model/src/commonMain/kotlin/com/google/fhir/model/r4/FhirDateTime.kt +++ b/fhir-model/src/commonMain/kotlin/com/google/fhir/model/r4/FhirDateTime.kt @@ -33,15 +33,15 @@ public sealed interface FhirDateTime { override fun toString(): String = value.toString() } - public class YearMonth(public val year: Int, public val month: Int) : FhirDateTime { + public data class YearMonth(public val year: Int, public val month: Int) : FhirDateTime { override fun toString(): String = "$year-${month.toString().padStart(2,'0')}" } - public class Date(public val date: LocalDate) : FhirDateTime { + public data class Date(public val date: LocalDate) : FhirDateTime { override fun toString(): String = date.toString() } - public class DateTime(public val dateTime: LocalDateTime, public val utcOffset: UtcOffset) : + public data class DateTime(public val dateTime: LocalDateTime, public val utcOffset: UtcOffset) : FhirDateTime { override fun toString(): String = dateTime.format(LocalDateTime.Formats.ISO) + utcOffset.toString() diff --git a/fhir-model/src/commonMain/kotlin/com/google/fhir/model/r4/Integer.kt b/fhir-model/src/commonMain/kotlin/com/google/fhir/model/r4/Integer.kt index eed3757b9..c8408c8c7 100644 --- a/fhir-model/src/commonMain/kotlin/com/google/fhir/model/r4/Integer.kt +++ b/fhir-model/src/commonMain/kotlin/com/google/fhir/model/r4/Integer.kt @@ -18,6 +18,8 @@ package com.google.fhir.model.r4 +import kotlin.Any +import kotlin.Boolean import kotlin.Int import kotlin.String import kotlin.Suppress @@ -43,6 +45,23 @@ public open class Integer( /** The actual value */ public open var `value`: Int? = null, ) : Element(id, extension) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is Integer) return false + if (id != other.id) return false + if (extension != other.extension) return false + if (value != other.value) return false + return true + } + + override fun hashCode(): Int { + // Using 31 improves hash distribution and reduces collisions in hash-based collections + var result = id?.hashCode() ?: 0 + result = 31 * result + (extension?.hashCode() ?: 0) + result = 31 * result + (value?.hashCode() ?: 0) + return result + } + public open fun toElement(): Element? { if (id != null || extension.isNotEmpty()) { return Element(id, extension) diff --git a/fhir-model/src/commonMain/kotlin/com/google/fhir/model/r4/Quantity.kt b/fhir-model/src/commonMain/kotlin/com/google/fhir/model/r4/Quantity.kt index b3ac6dd85..275f21a83 100644 --- a/fhir-model/src/commonMain/kotlin/com/google/fhir/model/r4/Quantity.kt +++ b/fhir-model/src/commonMain/kotlin/com/google/fhir/model/r4/Quantity.kt @@ -19,6 +19,9 @@ package com.google.fhir.model.r4 import com.google.fhir.model.r4.serializers.QuantitySerializer +import kotlin.Any +import kotlin.Boolean +import kotlin.Int import kotlin.Suppress import kotlin.collections.MutableList import kotlinx.serialization.Serializable @@ -74,6 +77,31 @@ public open class Quantity( */ public open var code: Code? = null, ) : Element() { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is Quantity) return false + if (id != other.id) return false + if (extension != other.extension) return false + if (value != other.value) return false + if (comparator != other.comparator) return false + if (unit != other.unit) return false + if (system != other.system) return false + if (code != other.code) return false + return true + } + + override fun hashCode(): Int { + // Using 31 improves hash distribution and reduces collisions in hash-based collections + var result = id?.hashCode() ?: 0 + result = 31 * result + (extension?.hashCode() ?: 0) + result = 31 * result + (value?.hashCode() ?: 0) + result = 31 * result + (comparator?.hashCode() ?: 0) + result = 31 * result + (unit?.hashCode() ?: 0) + result = 31 * result + (system?.hashCode() ?: 0) + result = 31 * result + (code?.hashCode() ?: 0) + return result + } + /** How the Quantity should be understood and represented. */ public enum class QuantityComparator( private val code: kotlin.String, diff --git a/fhir-model/src/commonMain/kotlin/com/google/fhir/model/r4/String.kt b/fhir-model/src/commonMain/kotlin/com/google/fhir/model/r4/String.kt index cdc8d8ab8..2d058cb8e 100644 --- a/fhir-model/src/commonMain/kotlin/com/google/fhir/model/r4/String.kt +++ b/fhir-model/src/commonMain/kotlin/com/google/fhir/model/r4/String.kt @@ -18,6 +18,9 @@ package com.google.fhir.model.r4 +import kotlin.Any +import kotlin.Boolean +import kotlin.Int import kotlin.Suppress import kotlin.collections.MutableList @@ -41,6 +44,23 @@ public open class String( /** The actual value */ public open var `value`: kotlin.String? = null, ) : Element(id, extension) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is String) return false + if (id != other.id) return false + if (extension != other.extension) return false + if (value != other.value) return false + return true + } + + override fun hashCode(): Int { + // Using 31 improves hash distribution and reduces collisions in hash-based collections + var result = id?.hashCode() ?: 0 + result = 31 * result + (extension?.hashCode() ?: 0) + result = 31 * result + (value?.hashCode() ?: 0) + return result + } + public open fun toElement(): Element? { if (id != null || extension.isNotEmpty()) { return Element(id, extension) diff --git a/fhir-model/src/commonMain/kotlin/com/google/fhir/model/r4/Uri.kt b/fhir-model/src/commonMain/kotlin/com/google/fhir/model/r4/Uri.kt index 876594c5c..d5673fda4 100644 --- a/fhir-model/src/commonMain/kotlin/com/google/fhir/model/r4/Uri.kt +++ b/fhir-model/src/commonMain/kotlin/com/google/fhir/model/r4/Uri.kt @@ -18,6 +18,9 @@ package com.google.fhir.model.r4 +import kotlin.Any +import kotlin.Boolean +import kotlin.Int import kotlin.String import kotlin.Suppress import kotlin.collections.MutableList @@ -44,6 +47,23 @@ public open class Uri( /** The actual value */ public open var `value`: String? = null, ) : Element(id, extension) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is Uri) return false + if (id != other.id) return false + if (extension != other.extension) return false + if (value != other.value) return false + return true + } + + override fun hashCode(): Int { + // Using 31 improves hash distribution and reduces collisions in hash-based collections + var result = id?.hashCode() ?: 0 + result = 31 * result + (extension?.hashCode() ?: 0) + result = 31 * result + (value?.hashCode() ?: 0) + return result + } + public open fun toElement(): Element? { if (id != null || extension.isNotEmpty()) { return Element(id, extension) diff --git a/fhir-model/src/commonMain/kotlin/com/google/fhir/model/r4b/Element.kt b/fhir-model/src/commonMain/kotlin/com/google/fhir/model/r4b/Element.kt index f53132f64..c07909a9a 100644 --- a/fhir-model/src/commonMain/kotlin/com/google/fhir/model/r4b/Element.kt +++ b/fhir-model/src/commonMain/kotlin/com/google/fhir/model/r4b/Element.kt @@ -18,6 +18,9 @@ package com.google.fhir.model.r4b +import kotlin.Any +import kotlin.Boolean +import kotlin.Int import kotlin.String import kotlin.Suppress import kotlin.collections.MutableList @@ -44,4 +47,19 @@ public open class Element( * simplicity for everyone. */ public open var extension: MutableList = mutableListOf(), -) +) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is Element) return false + if (id != other.id) return false + if (extension != other.extension) return false + return true + } + + override fun hashCode(): Int { + // Using 31 improves hash distribution and reduces collisions in hash-based collections + var result = id?.hashCode() ?: 0 + result = 31 * result + (extension?.hashCode() ?: 0) + return result + } +} diff --git a/fhir-model/src/commonMain/kotlin/com/google/fhir/model/r4b/FhirDate.kt b/fhir-model/src/commonMain/kotlin/com/google/fhir/model/r4b/FhirDate.kt index a85e8ac8e..6d54c6cde 100644 --- a/fhir-model/src/commonMain/kotlin/com/google/fhir/model/r4b/FhirDate.kt +++ b/fhir-model/src/commonMain/kotlin/com/google/fhir/model/r4b/FhirDate.kt @@ -30,11 +30,11 @@ public sealed interface FhirDate { override fun toString(): String = value.toString() } - public class YearMonth(public val year: Int, public val month: Int) : FhirDate { + public data class YearMonth(public val year: Int, public val month: Int) : FhirDate { override fun toString(): String = "$year-${month.toString().padStart(2,'0')}" } - public class Date(public val date: LocalDate) : FhirDate { + public data class Date(public val date: LocalDate) : FhirDate { override fun toString(): String = date.toString() } diff --git a/fhir-model/src/commonMain/kotlin/com/google/fhir/model/r4b/FhirDateTime.kt b/fhir-model/src/commonMain/kotlin/com/google/fhir/model/r4b/FhirDateTime.kt index 44f86990c..ecde52de1 100644 --- a/fhir-model/src/commonMain/kotlin/com/google/fhir/model/r4b/FhirDateTime.kt +++ b/fhir-model/src/commonMain/kotlin/com/google/fhir/model/r4b/FhirDateTime.kt @@ -33,15 +33,15 @@ public sealed interface FhirDateTime { override fun toString(): String = value.toString() } - public class YearMonth(public val year: Int, public val month: Int) : FhirDateTime { + public data class YearMonth(public val year: Int, public val month: Int) : FhirDateTime { override fun toString(): String = "$year-${month.toString().padStart(2,'0')}" } - public class Date(public val date: LocalDate) : FhirDateTime { + public data class Date(public val date: LocalDate) : FhirDateTime { override fun toString(): String = date.toString() } - public class DateTime(public val dateTime: LocalDateTime, public val utcOffset: UtcOffset) : + public data class DateTime(public val dateTime: LocalDateTime, public val utcOffset: UtcOffset) : FhirDateTime { override fun toString(): String = dateTime.format(LocalDateTime.Formats.ISO) + utcOffset.toString() diff --git a/fhir-model/src/commonMain/kotlin/com/google/fhir/model/r4b/Integer.kt b/fhir-model/src/commonMain/kotlin/com/google/fhir/model/r4b/Integer.kt index 2a5ee4d5a..86f761e61 100644 --- a/fhir-model/src/commonMain/kotlin/com/google/fhir/model/r4b/Integer.kt +++ b/fhir-model/src/commonMain/kotlin/com/google/fhir/model/r4b/Integer.kt @@ -18,6 +18,8 @@ package com.google.fhir.model.r4b +import kotlin.Any +import kotlin.Boolean import kotlin.Int import kotlin.String import kotlin.Suppress @@ -43,6 +45,23 @@ public open class Integer( /** The actual value */ public open var `value`: Int? = null, ) : Element(id, extension) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is Integer) return false + if (id != other.id) return false + if (extension != other.extension) return false + if (value != other.value) return false + return true + } + + override fun hashCode(): Int { + // Using 31 improves hash distribution and reduces collisions in hash-based collections + var result = id?.hashCode() ?: 0 + result = 31 * result + (extension?.hashCode() ?: 0) + result = 31 * result + (value?.hashCode() ?: 0) + return result + } + public open fun toElement(): Element? { if (id != null || extension.isNotEmpty()) { return Element(id, extension) diff --git a/fhir-model/src/commonMain/kotlin/com/google/fhir/model/r4b/Quantity.kt b/fhir-model/src/commonMain/kotlin/com/google/fhir/model/r4b/Quantity.kt index b4ee3e208..a23a1dc41 100644 --- a/fhir-model/src/commonMain/kotlin/com/google/fhir/model/r4b/Quantity.kt +++ b/fhir-model/src/commonMain/kotlin/com/google/fhir/model/r4b/Quantity.kt @@ -19,6 +19,9 @@ package com.google.fhir.model.r4b import com.google.fhir.model.r4b.serializers.QuantitySerializer +import kotlin.Any +import kotlin.Boolean +import kotlin.Int import kotlin.Suppress import kotlin.collections.MutableList import kotlinx.serialization.Serializable @@ -74,6 +77,31 @@ public open class Quantity( */ public open var code: Code? = null, ) : Element() { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is Quantity) return false + if (id != other.id) return false + if (extension != other.extension) return false + if (value != other.value) return false + if (comparator != other.comparator) return false + if (unit != other.unit) return false + if (system != other.system) return false + if (code != other.code) return false + return true + } + + override fun hashCode(): Int { + // Using 31 improves hash distribution and reduces collisions in hash-based collections + var result = id?.hashCode() ?: 0 + result = 31 * result + (extension?.hashCode() ?: 0) + result = 31 * result + (value?.hashCode() ?: 0) + result = 31 * result + (comparator?.hashCode() ?: 0) + result = 31 * result + (unit?.hashCode() ?: 0) + result = 31 * result + (system?.hashCode() ?: 0) + result = 31 * result + (code?.hashCode() ?: 0) + return result + } + /** How the Quantity should be understood and represented. */ public enum class QuantityComparator( private val code: kotlin.String, diff --git a/fhir-model/src/commonMain/kotlin/com/google/fhir/model/r4b/String.kt b/fhir-model/src/commonMain/kotlin/com/google/fhir/model/r4b/String.kt index a12a4eb98..20685f85c 100644 --- a/fhir-model/src/commonMain/kotlin/com/google/fhir/model/r4b/String.kt +++ b/fhir-model/src/commonMain/kotlin/com/google/fhir/model/r4b/String.kt @@ -18,6 +18,9 @@ package com.google.fhir.model.r4b +import kotlin.Any +import kotlin.Boolean +import kotlin.Int import kotlin.Suppress import kotlin.collections.MutableList @@ -41,6 +44,23 @@ public open class String( /** The actual value */ public open var `value`: kotlin.String? = null, ) : Element(id, extension) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is String) return false + if (id != other.id) return false + if (extension != other.extension) return false + if (value != other.value) return false + return true + } + + override fun hashCode(): Int { + // Using 31 improves hash distribution and reduces collisions in hash-based collections + var result = id?.hashCode() ?: 0 + result = 31 * result + (extension?.hashCode() ?: 0) + result = 31 * result + (value?.hashCode() ?: 0) + return result + } + public open fun toElement(): Element? { if (id != null || extension.isNotEmpty()) { return Element(id, extension) diff --git a/fhir-model/src/commonMain/kotlin/com/google/fhir/model/r4b/Uri.kt b/fhir-model/src/commonMain/kotlin/com/google/fhir/model/r4b/Uri.kt index 807852057..425f58471 100644 --- a/fhir-model/src/commonMain/kotlin/com/google/fhir/model/r4b/Uri.kt +++ b/fhir-model/src/commonMain/kotlin/com/google/fhir/model/r4b/Uri.kt @@ -18,6 +18,9 @@ package com.google.fhir.model.r4b +import kotlin.Any +import kotlin.Boolean +import kotlin.Int import kotlin.String import kotlin.Suppress import kotlin.collections.MutableList @@ -44,6 +47,23 @@ public open class Uri( /** The actual value */ public open var `value`: String? = null, ) : Element(id, extension) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is Uri) return false + if (id != other.id) return false + if (extension != other.extension) return false + if (value != other.value) return false + return true + } + + override fun hashCode(): Int { + // Using 31 improves hash distribution and reduces collisions in hash-based collections + var result = id?.hashCode() ?: 0 + result = 31 * result + (extension?.hashCode() ?: 0) + result = 31 * result + (value?.hashCode() ?: 0) + return result + } + public open fun toElement(): Element? { if (id != null || extension.isNotEmpty()) { return Element(id, extension) diff --git a/fhir-model/src/commonMain/kotlin/com/google/fhir/model/r5/Element.kt b/fhir-model/src/commonMain/kotlin/com/google/fhir/model/r5/Element.kt index 75763b26a..37ac0f4d3 100644 --- a/fhir-model/src/commonMain/kotlin/com/google/fhir/model/r5/Element.kt +++ b/fhir-model/src/commonMain/kotlin/com/google/fhir/model/r5/Element.kt @@ -18,6 +18,9 @@ package com.google.fhir.model.r5 +import kotlin.Any +import kotlin.Boolean +import kotlin.Int import kotlin.String import kotlin.Suppress import kotlin.collections.MutableList @@ -44,4 +47,19 @@ public open class Element( * simplicity for everyone. */ public open var extension: MutableList = mutableListOf(), -) : Base() +) : Base() { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is Element) return false + if (id != other.id) return false + if (extension != other.extension) return false + return true + } + + override fun hashCode(): Int { + // Using 31 improves hash distribution and reduces collisions in hash-based collections + var result = id?.hashCode() ?: 0 + result = 31 * result + (extension?.hashCode() ?: 0) + return result + } +} diff --git a/fhir-model/src/commonMain/kotlin/com/google/fhir/model/r5/FhirDate.kt b/fhir-model/src/commonMain/kotlin/com/google/fhir/model/r5/FhirDate.kt index 289429c0e..aedf9a90f 100644 --- a/fhir-model/src/commonMain/kotlin/com/google/fhir/model/r5/FhirDate.kt +++ b/fhir-model/src/commonMain/kotlin/com/google/fhir/model/r5/FhirDate.kt @@ -30,11 +30,11 @@ public sealed interface FhirDate { override fun toString(): String = value.toString() } - public class YearMonth(public val year: Int, public val month: Int) : FhirDate { + public data class YearMonth(public val year: Int, public val month: Int) : FhirDate { override fun toString(): String = "$year-${month.toString().padStart(2,'0')}" } - public class Date(public val date: LocalDate) : FhirDate { + public data class Date(public val date: LocalDate) : FhirDate { override fun toString(): String = date.toString() } diff --git a/fhir-model/src/commonMain/kotlin/com/google/fhir/model/r5/FhirDateTime.kt b/fhir-model/src/commonMain/kotlin/com/google/fhir/model/r5/FhirDateTime.kt index 9079eaffd..98247a437 100644 --- a/fhir-model/src/commonMain/kotlin/com/google/fhir/model/r5/FhirDateTime.kt +++ b/fhir-model/src/commonMain/kotlin/com/google/fhir/model/r5/FhirDateTime.kt @@ -33,15 +33,15 @@ public sealed interface FhirDateTime { override fun toString(): String = value.toString() } - public class YearMonth(public val year: Int, public val month: Int) : FhirDateTime { + public data class YearMonth(public val year: Int, public val month: Int) : FhirDateTime { override fun toString(): String = "$year-${month.toString().padStart(2,'0')}" } - public class Date(public val date: LocalDate) : FhirDateTime { + public data class Date(public val date: LocalDate) : FhirDateTime { override fun toString(): String = date.toString() } - public class DateTime(public val dateTime: LocalDateTime, public val utcOffset: UtcOffset) : + public data class DateTime(public val dateTime: LocalDateTime, public val utcOffset: UtcOffset) : FhirDateTime { override fun toString(): String = dateTime.format(LocalDateTime.Formats.ISO) + utcOffset.toString() diff --git a/fhir-model/src/commonMain/kotlin/com/google/fhir/model/r5/Integer.kt b/fhir-model/src/commonMain/kotlin/com/google/fhir/model/r5/Integer.kt index cfdb2fa5b..7da548299 100644 --- a/fhir-model/src/commonMain/kotlin/com/google/fhir/model/r5/Integer.kt +++ b/fhir-model/src/commonMain/kotlin/com/google/fhir/model/r5/Integer.kt @@ -18,6 +18,8 @@ package com.google.fhir.model.r5 +import kotlin.Any +import kotlin.Boolean import kotlin.Int import kotlin.String import kotlin.Suppress @@ -43,6 +45,23 @@ public open class Integer( /** The actual value */ public open var `value`: Int? = null, ) : PrimitiveType() { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is Integer) return false + if (id != other.id) return false + if (extension != other.extension) return false + if (value != other.value) return false + return true + } + + override fun hashCode(): Int { + // Using 31 improves hash distribution and reduces collisions in hash-based collections + var result = id?.hashCode() ?: 0 + result = 31 * result + (extension?.hashCode() ?: 0) + result = 31 * result + (value?.hashCode() ?: 0) + return result + } + public open fun toElement(): Element? { if (id != null || extension.isNotEmpty()) { return Element(id, extension) diff --git a/fhir-model/src/commonMain/kotlin/com/google/fhir/model/r5/Quantity.kt b/fhir-model/src/commonMain/kotlin/com/google/fhir/model/r5/Quantity.kt index d48c8a4b2..73a344ce4 100644 --- a/fhir-model/src/commonMain/kotlin/com/google/fhir/model/r5/Quantity.kt +++ b/fhir-model/src/commonMain/kotlin/com/google/fhir/model/r5/Quantity.kt @@ -19,6 +19,9 @@ package com.google.fhir.model.r5 import com.google.fhir.model.r5.serializers.QuantitySerializer +import kotlin.Any +import kotlin.Boolean +import kotlin.Int import kotlin.Suppress import kotlin.collections.MutableList import kotlinx.serialization.Serializable @@ -74,6 +77,31 @@ public open class Quantity( */ public open var code: Code? = null, ) : DataType() { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is Quantity) return false + if (id != other.id) return false + if (extension != other.extension) return false + if (value != other.value) return false + if (comparator != other.comparator) return false + if (unit != other.unit) return false + if (system != other.system) return false + if (code != other.code) return false + return true + } + + override fun hashCode(): Int { + // Using 31 improves hash distribution and reduces collisions in hash-based collections + var result = id?.hashCode() ?: 0 + result = 31 * result + (extension?.hashCode() ?: 0) + result = 31 * result + (value?.hashCode() ?: 0) + result = 31 * result + (comparator?.hashCode() ?: 0) + result = 31 * result + (unit?.hashCode() ?: 0) + result = 31 * result + (system?.hashCode() ?: 0) + result = 31 * result + (code?.hashCode() ?: 0) + return result + } + /** How the Quantity should be understood and represented. */ public enum class QuantityComparator( private val code: kotlin.String, diff --git a/fhir-model/src/commonMain/kotlin/com/google/fhir/model/r5/String.kt b/fhir-model/src/commonMain/kotlin/com/google/fhir/model/r5/String.kt index f0017db54..f9e8e3dea 100644 --- a/fhir-model/src/commonMain/kotlin/com/google/fhir/model/r5/String.kt +++ b/fhir-model/src/commonMain/kotlin/com/google/fhir/model/r5/String.kt @@ -18,6 +18,9 @@ package com.google.fhir.model.r5 +import kotlin.Any +import kotlin.Boolean +import kotlin.Int import kotlin.Suppress import kotlin.collections.MutableList @@ -41,6 +44,23 @@ public open class String( /** The actual value */ public open var `value`: kotlin.String? = null, ) : PrimitiveType() { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is String) return false + if (id != other.id) return false + if (extension != other.extension) return false + if (value != other.value) return false + return true + } + + override fun hashCode(): Int { + // Using 31 improves hash distribution and reduces collisions in hash-based collections + var result = id?.hashCode() ?: 0 + result = 31 * result + (extension?.hashCode() ?: 0) + result = 31 * result + (value?.hashCode() ?: 0) + return result + } + public open fun toElement(): Element? { if (id != null || extension.isNotEmpty()) { return Element(id, extension) diff --git a/fhir-model/src/commonMain/kotlin/com/google/fhir/model/r5/Uri.kt b/fhir-model/src/commonMain/kotlin/com/google/fhir/model/r5/Uri.kt index 5df1dcb18..c62206c65 100644 --- a/fhir-model/src/commonMain/kotlin/com/google/fhir/model/r5/Uri.kt +++ b/fhir-model/src/commonMain/kotlin/com/google/fhir/model/r5/Uri.kt @@ -18,6 +18,9 @@ package com.google.fhir.model.r5 +import kotlin.Any +import kotlin.Boolean +import kotlin.Int import kotlin.String import kotlin.Suppress import kotlin.collections.MutableList @@ -42,6 +45,23 @@ public open class Uri( /** The actual value */ public open var `value`: String? = null, ) : PrimitiveType() { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is Uri) return false + if (id != other.id) return false + if (extension != other.extension) return false + if (value != other.value) return false + return true + } + + override fun hashCode(): Int { + // Using 31 improves hash distribution and reduces collisions in hash-based collections + var result = id?.hashCode() ?: 0 + result = 31 * result + (extension?.hashCode() ?: 0) + result = 31 * result + (value?.hashCode() ?: 0) + return result + } + public open fun toElement(): Element? { if (id != null || extension.isNotEmpty()) { return Element(id, extension) diff --git a/fhir-model/src/commonTest/kotlin/com/google/fhir/model/EqualityTest.kt b/fhir-model/src/commonTest/kotlin/com/google/fhir/model/EqualityTest.kt new file mode 100644 index 000000000..0d58d05cb --- /dev/null +++ b/fhir-model/src/commonTest/kotlin/com/google/fhir/model/EqualityTest.kt @@ -0,0 +1,96 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.fhir.model + +import kotlin.test.Test +import kotlin.test.assertEquals + +class EqualityTest { + + @Test + fun shouldMatchResourcesWithSimilarPropertiesOnComparisonInR4() { + loadR4Examples( + fileNameFilter = { + return@loadR4Examples (filterFileName(it) && !exclusionListR4.contains(it)) + } + ) + .forEach { exampleJson -> + val firstResource = jsonR4.decodeFromString(exampleJson) + val secondResource = jsonR4.decodeFromString(exampleJson) + assertEquals(firstResource, secondResource) + } + } + + @Test + fun shouldMatchResourcesWithSimilarPropertiesOnComparisonInR4B() { + loadR4BExamples( + fileNameFilter = { + return@loadR4BExamples (filterFileName(it) && !exclusionListR4B.contains(it)) + } + ) + .forEach { exampleJson -> + val firstResource: com.google.fhir.model.r4b.Resource = + jsonR4B.decodeFromString(exampleJson) + val secondResource: com.google.fhir.model.r4b.Resource = + jsonR4B.decodeFromString(exampleJson) + assertEquals(firstResource, secondResource) + } + } + + @Test + fun shouldMatchResourcesWithSimilarPropertiesOnComparisonInR5() { + loadR5Examples( + fileNameFilter = { + return@loadR5Examples (filterFileName(it) && !exclusionListR5.contains(it)) + } + ) + .forEach { exampleJson -> + val firstResource: com.google.fhir.model.r5.Resource = + jsonR5.decodeFromString(exampleJson) + val secondResource: com.google.fhir.model.r5.Resource = + jsonR5.decodeFromString(exampleJson) + assertEquals(firstResource, secondResource) + } + } + + companion object { + + private val exclusionListR4 = + listOf( + // Invalid resources + "ActivityDefinition-administer-zika-virus-exposure-assessment.json", + "ImplementationGuide-fhir.json", + "Questionnaire-qs1.json", + "ig-r4.json", + ) + + private val exclusionListR4B = + listOf( + // Invalid resource + "Bundle-valuesets.json", + "CodeSystem-catalogType.json", + "ValueSet-catalogType.json", + "ActivityDefinition-administer-zika-virus-exposure-assessment.json", + ) + + private val exclusionListR5 = + listOf( + // Unknown code 'text/CQL' for enum ExpressionLanguage; codes are case-sensitive + "ChargeItemDefinition-ebm.json" + ) + } +} diff --git a/fhir-model/src/commonTest/kotlin/com/google/fhir/model/SerializationRoundTripTest.kt b/fhir-model/src/commonTest/kotlin/com/google/fhir/model/SerializationRoundTripTest.kt index d096e375b..40475a567 100644 --- a/fhir-model/src/commonTest/kotlin/com/google/fhir/model/SerializationRoundTripTest.kt +++ b/fhir-model/src/commonTest/kotlin/com/google/fhir/model/SerializationRoundTripTest.kt @@ -16,81 +16,11 @@ package com.google.fhir.model -import com.google.fhir.model.r4.configureR4 -import com.google.fhir.model.r4b.configureR4b -import com.google.fhir.model.r5.configureR5 import kotlin.test.Test import kotlin.test.assertEquals import kotlinx.serialization.json.Json import kotlinx.serialization.json.jsonObject -const val r4ExamplePackage = "hl7.fhir.r4.examples/package/" -const val r4bExamplePackage = "hl7.fhir.r4b.examples/package/" -const val r5ExamplePackage = "hl7.fhir.r5.examples/package/" - -val trailingZeroRegex = "\\.0+".toRegex() - -val exclusionListR4 = - listOf( - // Hanging for no reason - "Bundle-terminologies.json", - "CodeSystem-v2-0003.json", - "Bundle-valueset-expansions.json", - - // Java heap space - "Bundle-resources.json", - "Bundle-dataelements.json", - "CodeSystem-v3-ManagedParticipationStatus.json", - - // Instant with trailing 0s - "ValueSet-v3-hl7PublishingSubSection.json", - - // Scientific notation - "Observation-decimal.json", - - // Invalid resources - "ActivityDefinition-administer-zika-virus-exposure-assessment.json", - "ImplementationGuide-fhir.json", - "Questionnaire-qs1.json", - "ig-r4.json", - ) - -val exclusionListR4B = - listOf( - // Java heap space - "Bundle-resources.json", - - // Scientific notation - "Observation-decimal.json", - - // Invalid resource - "Bundle-valuesets.json", - "CodeSystem-catalogType.json", - "ValueSet-catalogType.json", - - // Invalid resources - "ActivityDefinition-administer-zika-virus-exposure-assessment.json", - ) - -val exclusionListR5 = - listOf( - // Hanging - "Bundle-searchParams.json", - - // Java heap space - "Bundle-resources.json", - - // Trailing 0 in milliseconds - "ArtifactAssessment-example-certainty-rating.json", - "Citation-citation-example-research-doi.json", - - // Scientific notation - "Observation-decimal.json", - - // Unknown code 'text/CQL' for enum ExpressionLanguage; codes are case-sensitive - "ChargeItemDefinition-ebm.json", - ) - /** * This test verifies the generated code can be used to deserialize published FHIR examples and * serialize them back to the original JSON. @@ -105,8 +35,7 @@ class SerializationRoundTripTest { ) .forEach { val exampleJson = prettyPrintJson(it) - val domainResource: com.google.fhir.model.r4.Resource = - jsonR4.decodeFromString(exampleJson) + val domainResource = jsonR4.decodeFromString(exampleJson) val reserializedString = jsonR4.encodeToString(domainResource) assertEqualsIgnoringZeros(exampleJson, reserializedString) } @@ -145,35 +74,70 @@ class SerializationRoundTripTest { } companion object { - private val jsonR4 = Json { - prettyPrint = true - configureR4() - } + private val prettyPrintJson = Json { prettyPrint = true } - private val jsonR4B = Json { - prettyPrint = true - configureR4b() - } + private val trailingZeroRegex = "\\.0+".toRegex() - private val jsonR5 = Json { - prettyPrint = true - configureR5() - } + private val exclusionListR4 = + listOf( + // Hanging for no reason + "Bundle-terminologies.json", + "CodeSystem-v2-0003.json", + "Bundle-valueset-expansions.json", - private val prettyPrintJson = Json { prettyPrint = true } + // Java heap space + "Bundle-resources.json", + "Bundle-dataelements.json", + "CodeSystem-v3-ManagedParticipationStatus.json", - fun filterFileName(name: String): Boolean { - return name.endsWith(".json") && - !name.startsWith('.') // filter out `.index.json` file - && - name != "package.json" - } + // Instant with trailing 0s + "ValueSet-v3-hl7PublishingSubSection.json", - private fun prettyPrintJson(jsonString: String): String { - val jsonElement = - prettyPrintJson.parseToJsonElement(jsonString) // Parse the string to a JsonElement - return prettyPrintJson.encodeToString(jsonElement) // Re-encode with pretty printing - } + // Scientific notation + "Observation-decimal.json", + + // Invalid resources + "ActivityDefinition-administer-zika-virus-exposure-assessment.json", + "ImplementationGuide-fhir.json", + "Questionnaire-qs1.json", + "ig-r4.json", + ) + + private val exclusionListR4B = + listOf( + // Java heap space + "Bundle-resources.json", + + // Scientific notation + "Observation-decimal.json", + + // Invalid resource + "Bundle-valuesets.json", + "CodeSystem-catalogType.json", + "ValueSet-catalogType.json", + + // Invalid resources + "ActivityDefinition-administer-zika-virus-exposure-assessment.json", + ) + + private val exclusionListR5 = + listOf( + // Hanging + "Bundle-searchParams.json", + + // Java heap space + "Bundle-resources.json", + + // Trailing 0 in milliseconds + "ArtifactAssessment-example-certainty-rating.json", + "Citation-citation-example-research-doi.json", + + // Scientific notation + "Observation-decimal.json", + + // Unknown code 'text/CQL' for enum ExpressionLanguage; codes are case-sensitive + "ChargeItemDefinition-ebm.json", + ) private fun assertEqualsIgnoringZeros(exampleJson: String, reserializedString: String) { val expected = @@ -191,11 +155,11 @@ class SerializationRoundTripTest { } private fun String.removeZerosAfterDecimalPoint(): String = replace(trailingZeroRegex, "") + + private fun prettyPrintJson(jsonString: String): String { + val jsonElement = + prettyPrintJson.parseToJsonElement(jsonString) // Parse the string to a JsonElement + return prettyPrintJson.encodeToString(jsonElement) // Re-encode with pretty printing + } } } - -expect fun loadR4Examples(fileNameFilter: (String) -> Boolean): Sequence - -expect fun loadR4BExamples(fileNameFilter: (String) -> Boolean): Sequence - -expect fun loadR5Examples(fileNameFilter: (String) -> Boolean): Sequence diff --git a/fhir-model/src/commonTest/kotlin/com/google/fhir/model/TestJson.kt b/fhir-model/src/commonTest/kotlin/com/google/fhir/model/TestJson.kt new file mode 100644 index 000000000..1d7580774 --- /dev/null +++ b/fhir-model/src/commonTest/kotlin/com/google/fhir/model/TestJson.kt @@ -0,0 +1,37 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.fhir.model + +import com.google.fhir.model.r4.configureR4 +import com.google.fhir.model.r4b.configureR4b +import com.google.fhir.model.r5.configureR5 +import kotlinx.serialization.json.Json + +val jsonR4 = Json { + prettyPrint = true + configureR4() +} + +val jsonR4B = Json { + prettyPrint = true + configureR4b() +} + +val jsonR5 = Json { + prettyPrint = true + configureR5() +} diff --git a/fhir-model/src/commonTest/kotlin/com/google/fhir/model/TestLoader.kt b/fhir-model/src/commonTest/kotlin/com/google/fhir/model/TestLoader.kt new file mode 100644 index 000000000..d92c296e0 --- /dev/null +++ b/fhir-model/src/commonTest/kotlin/com/google/fhir/model/TestLoader.kt @@ -0,0 +1,34 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.fhir.model + +const val r4ExamplePackage = "hl7.fhir.r4.examples/package/" +const val r4bExamplePackage = "hl7.fhir.r4b.examples/package/" +const val r5ExamplePackage = "hl7.fhir.r5.examples/package/" + +fun filterFileName(name: String): Boolean { + return name.endsWith(".json") && + !name.startsWith('.') // filter out `.index.json` file + && + name != "package.json" +} + +expect fun loadR4Examples(fileNameFilter: (String) -> Boolean): Sequence + +expect fun loadR4BExamples(fileNameFilter: (String) -> Boolean): Sequence + +expect fun loadR5Examples(fileNameFilter: (String) -> Boolean): Sequence diff --git a/fhir-model/src/jvmTest/kotlin/com/google/fhir/model/SerializationRoundTripTest.jvm.kt b/fhir-model/src/jvmTest/kotlin/com/google/fhir/model/TestLoader.jvm.kt similarity index 100% rename from fhir-model/src/jvmTest/kotlin/com/google/fhir/model/SerializationRoundTripTest.jvm.kt rename to fhir-model/src/jvmTest/kotlin/com/google/fhir/model/TestLoader.jvm.kt index 1253c0d83..df473eef4 100644 --- a/fhir-model/src/jvmTest/kotlin/com/google/fhir/model/SerializationRoundTripTest.jvm.kt +++ b/fhir-model/src/jvmTest/kotlin/com/google/fhir/model/TestLoader.jvm.kt @@ -18,17 +18,6 @@ package com.google.fhir.model import java.io.File -private fun loadExamplesFromFileSystem( - directoryName: String, - fileNameFilter: (String) -> Boolean, -): Sequence { - return File("${System.getProperty("projectRootDir")}/third_party/${directoryName}") - .listFiles()!! - .asSequence() - .filter { fileNameFilter(it.name) } - .map { it.readText() } -} - actual fun loadR4Examples(fileNameFilter: (String) -> Boolean): Sequence { return loadExamplesFromFileSystem(r4ExamplePackage, fileNameFilter) } @@ -40,3 +29,14 @@ actual fun loadR4BExamples(fileNameFilter: (String) -> Boolean): Sequence Boolean): Sequence { return loadExamplesFromFileSystem(r5ExamplePackage, fileNameFilter) } + +private fun loadExamplesFromFileSystem( + directoryName: String, + fileNameFilter: (String) -> Boolean, +): Sequence { + return File("${System.getProperty("projectRootDir")}/third_party/${directoryName}") + .listFiles()!! + .asSequence() + .filter { fileNameFilter(it.name) } + .map { it.readText() } +}