Skip to content

Commit

Permalink
Fix error in RealmAny.equals when comparing similarly typed object (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
rorbech authored Sep 20, 2023
1 parent 1e6ffa1 commit 6f349ef
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 20 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
* Realm will no longer set the JVM bytecode to 1.8 when applying the Realm plugin. ([#1513](https://github.com/realm/realm-kotlin/issues/1513))

### Fixed
* Fix error in `RealmAny.equals` that would sometimes return `true` when comparing RealmAnys wrapping same type but different values. (Issue [#1523](https://github.com/realm/realm-kotlin/pull/1523))
* [Sync] If calling a function on App Services that resulted in a redirect, it would only redirect for GET requests. (Issue [#1517](https://github.com/realm/realm-kotlin/pull/1517))

### Compatibility
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,27 +127,12 @@ internal class RealmAnyImpl<T : Any> constructor(
if (other.type != this.type) return false
if (clazz == ByteArray::class) {
if (other.internalValue !is ByteArray) return false
if (!other.internalValue.contentEquals(this.internalValue as ByteArray)) return false
} else if (internalValue is BsonObjectId) {
if (other.clazz != BsonObjectId::class) return false
if (other.internalValue != this.internalValue) return false
return other.internalValue.contentEquals(this.internalValue as ByteArray)
} else if (internalValue is RealmObject) {
if (other.clazz != this.clazz) return false
if (other.internalValue !== this.internalValue) return false
} else if (internalValue is Number) { // Numerics are the same as long as their value is the same
when (other.internalValue) {
is Char -> if (other.internalValue.code.toLong() != internalValue.toLong()) return false
is Number -> if (other.internalValue.toLong() != this.internalValue.toLong()) return false
else -> return false
}
} else if (internalValue is Char) { // We are comparing chars
when (other.internalValue) {
is Char -> if (other.internalValue.code.toLong() != internalValue.toLong()) return false
is Number -> if (other.internalValue.toLong() != this.internalValue.toLong()) return false
else -> return false
}
return other.internalValue == this.internalValue
}
return true
return internalValue == other.internalValue
}

override fun hashCode(): Int {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ import kotlinx.serialization.Serializable
import kotlinx.serialization.UseSerializers
import org.mongodb.kbson.BsonObjectId
import org.mongodb.kbson.Decimal128
import kotlin.reflect.KClass
import kotlin.reflect.KMutableProperty1

@Suppress("MagicNumber")
Expand All @@ -65,7 +66,9 @@ class SerializableSample : RealmObject {
var floatField: Float = 3.14f
var doubleField: Double = 1.19840122
var decimal128Field: Decimal128 = Decimal128("1.8446744073709551618E-6157")
var timestampField: RealmInstant = RealmInstant.from(100, 1000)
// We will loose nano second precision when we round trip these, so framework only works for
// timestamps with 0-nanosecond fraction.
var timestampField: RealmInstant = RealmInstant.from(100, 1000000)
var bsonObjectIdField: BsonObjectId = BsonObjectId("507f1f77bcf86cd799439011")
var uuidField: RealmUUID = RealmUUID.from("46423f1b-ce3e-4a7e-812f-004cf9c42d76")
var binaryField: ByteArray = byteArrayOf(42)
Expand Down Expand Up @@ -212,7 +215,7 @@ class SerializableSample : RealmObject {
)

@Suppress("UNCHECKED_CAST")
val listNullableProperties = mapOf(
val listNullableProperties: Map<KClass<out Any>, KMutableProperty1<SerializableSample, MutableCollection<Any?>>> = mapOf(
String::class to SerializableSample::nullableStringListField as KMutableProperty1<SerializableSample, MutableCollection<Any?>>,
Byte::class to SerializableSample::nullableByteListField as KMutableProperty1<SerializableSample, MutableCollection<Any?>>,
Char::class to SerializableSample::nullableCharListField as KMutableProperty1<SerializableSample, MutableCollection<Any?>>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ import kotlin.test.assertContentEquals
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertIs
import kotlin.test.assertNotEquals
import kotlin.test.assertNotNull
import kotlin.test.assertNull
import kotlin.test.fail
Expand Down Expand Up @@ -421,6 +422,74 @@ class RealmAnyTests {
}
}

@Test
fun equals() {
RealmAny.Type.values().forEach { type ->
when (type) {
RealmAny.Type.INT -> {
assertEquals(RealmAny.create(1), RealmAny.create(Char(1)))
assertEquals(RealmAny.create(1), RealmAny.create(1.toByte()))
assertEquals(RealmAny.create(1), RealmAny.create(1.toShort()))
assertEquals(RealmAny.create(1), RealmAny.create(1.toInt()))
assertEquals(RealmAny.create(1), RealmAny.create(1.toLong()))
assertNotEquals(RealmAny.create(1), RealmAny.create(2))
}
RealmAny.Type.BOOL -> {
assertEquals(RealmAny.create(true), RealmAny.create(true))
assertNotEquals(RealmAny.create(true), RealmAny.create(false))
}
RealmAny.Type.STRING -> {
assertEquals(RealmAny.create("Realm"), RealmAny.create("Realm"))
assertNotEquals(RealmAny.create("Realm"), RealmAny.create("Not Realm"))
}
RealmAny.Type.BINARY -> {
assertEquals(
RealmAny.create(byteArrayOf(1, 2)), RealmAny.create(byteArrayOf(1, 2))
)
assertNotEquals(
RealmAny.create(byteArrayOf(1, 2)), RealmAny.create(byteArrayOf(2, 1))
)
}
RealmAny.Type.TIMESTAMP -> {
val now = RealmInstant.now()
assertEquals(RealmAny.create(now), RealmAny.create(now))
assertNotEquals(RealmAny.create(RealmInstant.from(1, 1)), RealmAny.create(now))
}
RealmAny.Type.FLOAT -> {
assertEquals(RealmAny.create(1.5f), RealmAny.create(1.5f))
assertNotEquals(RealmAny.create(1.2f), RealmAny.create(1.3f))
}
RealmAny.Type.DOUBLE -> {
assertEquals(RealmAny.create(1.5), RealmAny.create(1.5))
assertNotEquals(RealmAny.create(1.2), RealmAny.create(1.3))
}
RealmAny.Type.DECIMAL128 -> {
assertEquals(RealmAny.create(Decimal128("1E64")), RealmAny.create(Decimal128("1E64")))
assertNotEquals(RealmAny.create(Decimal128("1E64")), RealmAny.create(Decimal128("-1E64")))
}
RealmAny.Type.OBJECT_ID -> {
val value = ObjectId()
assertEquals(RealmAny.create(value), RealmAny.create(value))
assertNotEquals(RealmAny.create(ObjectId()), RealmAny.create(value))
}
RealmAny.Type.UUID -> {
val value = RealmUUID.random()
assertEquals(RealmAny.create(value), RealmAny.create(value))
assertNotEquals(RealmAny.create(RealmUUID.random()), RealmAny.create(value))
}
RealmAny.Type.OBJECT -> {
val realmObject = Sample()
// Same object is equal
assertEquals(RealmAny.create(realmObject), RealmAny.create(realmObject))
// Different kind of objects are not equal
assertNotEquals(RealmAny.create(RealmAnyContainer()), RealmAny.create(realmObject))
// Different objects of same type are not equal
assertNotEquals(RealmAny.create(Sample()), RealmAny.create(realmObject))
}
}
}
}

@Test
fun embeddedObject_worksInsideParent() {
val embeddedChild = EmbeddedChild("CHILD")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,11 @@ class SerializationTests {
RealmInstant::class -> dataSet.map {
(it as RealmInstant?)?.restrictToMillisPrecision() as T
}
RealmAny::class -> dataSet.map {
if ((it as? RealmAny)?.type == RealmAny.Type.TIMESTAMP) {
RealmAny.create((it.asRealmInstant()!!.restrictToMillisPrecision()))as T
} else { it }
}
else -> dataSet
}

Expand Down Expand Up @@ -177,6 +182,12 @@ class SerializationTests {
RealmInstant::class -> dataSet.map { entry ->
entry.first to (entry.second as RealmInstant?)?.restrictToMillisPrecision() as T
}
RealmAny::class -> dataSet.map { entry ->
val (key, value) = entry
if ((value as? RealmAny)?.type == RealmAny.Type.TIMESTAMP) {
key to RealmAny.create((value.asRealmInstant()!!.restrictToMillisPrecision()))as T
} else { entry }
}
else -> dataSet
}

Expand Down

0 comments on commit 6f349ef

Please sign in to comment.