Skip to content

Commit 7382118

Browse files
committed
[ECO-5426] tests: Added few more spec based tests for objetcs and objectspool
1 parent 557dac6 commit 7382118

6 files changed

Lines changed: 161 additions & 20 deletions

File tree

live-objects/src/main/kotlin/io/ably/lib/objects/ObjectsPool.kt

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -55,23 +55,25 @@ internal class ObjectsPool(
5555
init {
5656
// Initialize pool with root object
5757
createInitialPool()
58-
5958
// Start garbage collection coroutine
6059
startGCJob()
6160
}
6261

6362
/**
64-
* Gets a live object from the pool by object ID.
63+
* Creates the initial pool with root object.
64+
*
65+
* @spec RTO3b - Creates root LiveMap object
6566
*/
66-
internal fun get(objectId: String): BaseLiveObject? {
67-
return pool[objectId]
67+
private fun createInitialPool() {
68+
val root = DefaultLiveMap.zeroValue(ROOT_OBJECT_ID, adapter, this)
69+
pool[ROOT_OBJECT_ID] = root
6870
}
6971

7072
/**
71-
* Deletes objects from the pool for which object ids are not found in the provided array of ids.
73+
* Gets a live object from the pool by object ID.
7274
*/
73-
internal fun deleteExtraObjectIds(objectIds: MutableSet<String>) {
74-
pool.entries.removeIf { (key, _) -> key !in objectIds }
75+
internal fun get(objectId: String): BaseLiveObject? {
76+
return pool[objectId]
7577
}
7678

7779
/**
@@ -92,13 +94,21 @@ internal class ObjectsPool(
9294
pool.clear()
9395
set(ROOT_OBJECT_ID, root)
9496

95-
// Clear the data, this will only clear the root object
97+
// this will only clear the remaining root object and emit update events
9698
clearObjectsData(emitUpdateEvents)
9799
} else {
98100
Log.w(tag, "Root object not found in pool during reset")
99101
}
100102
}
101103

104+
105+
/**
106+
* Deletes objects from the pool for which object ids are not found in the provided array of ids.
107+
*/
108+
internal fun deleteExtraObjectIds(objectIds: MutableSet<String>) {
109+
pool.entries.removeIf { (key, _) -> key !in objectIds }
110+
}
111+
102112
/**
103113
* Clears the data stored for all objects in the pool.
104114
*/
@@ -132,16 +142,6 @@ internal class ObjectsPool(
132142
return zeroValueObject
133143
}
134144

135-
/**
136-
* Creates the initial pool with root object.
137-
*
138-
* @spec RTO3b - Creates root LiveMap object
139-
*/
140-
private fun createInitialPool() {
141-
val root = DefaultLiveMap.zeroValue(ROOT_OBJECT_ID, adapter, this)
142-
pool[ROOT_OBJECT_ID] = root
143-
}
144-
145145
/**
146146
* Garbage collection interval handler.
147147
*/

live-objects/src/test/kotlin/io/ably/lib/objects/unit/LiveObjectTest.kt renamed to live-objects/src/test/kotlin/io/ably/lib/objects/unit/RealtimeObjectsTest.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import kotlinx.coroutines.test.runTest
44
import org.junit.Test
55
import kotlin.test.assertNotNull
66

7-
class LiveObjectTest {
7+
class RealtimeObjectsTest {
88
@Test
99
fun testChannelObjectGetterTest() = runTest {
1010
val channel = getMockRealtimeChannel("test-channel")

live-objects/src/test/kotlin/io/ably/lib/objects/unit/TestHelpers.kt

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
package io.ably.lib.objects.unit
22

3+
import io.ably.lib.objects.*
4+
import io.ably.lib.objects.DefaultLiveObjects
5+
import io.ably.lib.objects.ObjectsManager
6+
import io.ably.lib.objects.type.BaseLiveObject
37
import io.ably.lib.realtime.AblyRealtime
48
import io.ably.lib.realtime.Channel
59
import io.ably.lib.realtime.ChannelState
@@ -35,3 +39,37 @@ internal fun getMockRealtimeChannel(
3539
state = ChannelState.attached
3640
}
3741
}
42+
43+
internal fun getMockLiveObjectsAdapter(): LiveObjectsAdapter {
44+
return mockk<LiveObjectsAdapter>(relaxed = true)
45+
}
46+
47+
internal fun ObjectsPool.size(): Int {
48+
val pool = this.getPrivateField<Map<String, BaseLiveObject>>("pool")
49+
return pool.size
50+
}
51+
52+
internal val ObjectsManager.SyncObjectsDataPool: Map<String, ObjectState>
53+
get() = this.getPrivateField("syncObjectsDataPool")
54+
55+
internal val ObjectsManager.BufferedObjectOperations: List<ObjectMessage>
56+
get() = this.getPrivateField("bufferedObjectOperations")
57+
58+
internal var DefaultLiveObjects.ObjectsManager: ObjectsManager
59+
get() = this.getPrivateField("objectsManager")
60+
set(value) = this.setPrivateField("objectsManager", value)
61+
62+
internal var DefaultLiveObjects.ObjectsPool: ObjectsPool
63+
get() = this.objectsPool
64+
set(value) = this.setPrivateField("objectsPool", value)
65+
66+
internal fun getDefaultLiveObjectsWithMockedDeps(
67+
channelName: String = "testChannelName",
68+
): DefaultLiveObjects {
69+
val defaultLiveObjects = DefaultLiveObjects(channelName, getMockLiveObjectsAdapter())
70+
// mock objectsPool to allow verification of method calls
71+
defaultLiveObjects.ObjectsPool = spyk(defaultLiveObjects.objectsPool, recordPrivateCalls = true)
72+
// mock objectsManager to allow verification of method calls
73+
defaultLiveObjects.ObjectsManager = spyk(defaultLiveObjects.ObjectsManager, recordPrivateCalls = true)
74+
return defaultLiveObjects
75+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package io.ably.lib.objects.unit.objects
2+
3+
import io.ably.lib.objects.ObjectData
4+
import io.ably.lib.objects.ObjectsState
5+
import io.ably.lib.objects.ROOT_OBJECT_ID
6+
import io.ably.lib.objects.assertWaiter
7+
import io.ably.lib.objects.type.livecounter.DefaultLiveCounter
8+
import io.ably.lib.objects.type.livemap.DefaultLiveMap
9+
import io.ably.lib.objects.type.livemap.LiveMapEntry
10+
import io.ably.lib.objects.unit.*
11+
import io.ably.lib.objects.unit.BufferedObjectOperations
12+
import io.ably.lib.objects.unit.ObjectsManager
13+
import io.ably.lib.objects.unit.SyncObjectsDataPool
14+
import io.ably.lib.objects.unit.getDefaultLiveObjectsWithMockedDeps
15+
import io.ably.lib.realtime.ChannelState
16+
import io.mockk.mockk
17+
import io.mockk.verify
18+
import kotlinx.coroutines.test.runTest
19+
import org.junit.Test
20+
import kotlin.test.assertEquals
21+
22+
class DefaultLiveObjectsTest {
23+
24+
@Test
25+
fun `(RTO4, RTO4a) When channel ATTACHED with HAS_OBJECTS flag true should start sync sequence`() = runTest {
26+
val defaultLiveObjects = getDefaultLiveObjectsWithMockedDeps()
27+
28+
// RTO4a - If the HAS_OBJECTS flag is 1, the server will shortly perform an OBJECT_SYNC sequence
29+
defaultLiveObjects.handleStateChange(ChannelState.attached, true)
30+
// It is expected that the client will start a new sync sequence
31+
verify(exactly = 1) {
32+
defaultLiveObjects.ObjectsManager.startNewSync(null)
33+
}
34+
verify(exactly = 0) {
35+
defaultLiveObjects.ObjectsManager.endSync(any<Boolean>())
36+
}
37+
assertWaiter { defaultLiveObjects.state == ObjectsState.SYNCING }
38+
}
39+
40+
@Test
41+
fun `(RTO4, RTO4b) When channel ATTACHED with HAS_OBJECTS flag false should complete sync immediately`() = runTest {
42+
val defaultLiveObjects = getDefaultLiveObjectsWithMockedDeps()
43+
44+
// Set up some objects in objectPool that should be cleared
45+
val rootObject = defaultLiveObjects.objectsPool.get(ROOT_OBJECT_ID) as DefaultLiveMap
46+
rootObject.data["key1"] = LiveMapEntry(data = ObjectData("testValue1"))
47+
defaultLiveObjects.objectsPool.set("dummyObjectId", DefaultLiveCounter("dummyObjectId", mockk(relaxed = true)))
48+
49+
// RTO4b - If the HAS_OBJECTS flag is 0, the sync sequence must be considered complete immediately
50+
defaultLiveObjects.handleStateChange(ChannelState.attached, false)
51+
52+
verify(exactly = 1) {
53+
defaultLiveObjects.objectsPool.resetToInitialPool(true)
54+
}
55+
verify(exactly = 1) {
56+
defaultLiveObjects.ObjectsManager.endSync(any<Boolean>())
57+
}
58+
59+
// Verify expected outcomes
60+
assertWaiter { defaultLiveObjects.state == ObjectsState.SYNCED } // RTO4b4
61+
assertEquals(0, defaultLiveObjects.ObjectsManager.SyncObjectsDataPool.size) // RTO4b3
62+
assertEquals(0, defaultLiveObjects.ObjectsManager.BufferedObjectOperations.size) // RTO4b5
63+
assertEquals(1, defaultLiveObjects.objectsPool.size()) // RTO4b1 - Only root remains
64+
assertEquals(rootObject, defaultLiveObjects.objectsPool.get(ROOT_OBJECT_ID)) // points to previously created root object
65+
assertEquals(0, rootObject.data.size) // RTO4b2 - root object must be empty
66+
}
67+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package io.ably.lib.objects.unit.objects
2+
3+
import io.ably.lib.objects.DefaultLiveObjects
4+
import io.ably.lib.objects.ROOT_OBJECT_ID
5+
import io.ably.lib.objects.type.livecounter.DefaultLiveCounter
6+
import io.ably.lib.objects.type.livemap.DefaultLiveMap
7+
import io.mockk.mockk
8+
import org.junit.Test
9+
import kotlin.test.assertEquals
10+
import kotlin.test.assertNotNull
11+
import kotlin.test.assertTrue
12+
13+
class ObjectsPoolTest {
14+
15+
@Test
16+
fun `(RTO3, RTO3a, RTO3b) An internal ObjectsPool should be used to maintain the list of objects present on a channel`() {
17+
val defaultLiveObjects = DefaultLiveObjects("dummyChannel", mockk(relaxed = true))
18+
val objectsPool = defaultLiveObjects.objectsPool
19+
assertNotNull(objectsPool)
20+
21+
// RTO3b - It must always contain a LiveMap object with id root
22+
val rootLiveMap = objectsPool.get(ROOT_OBJECT_ID)
23+
assertNotNull(rootLiveMap)
24+
assertTrue(rootLiveMap is DefaultLiveMap)
25+
assertTrue(rootLiveMap.data.isEmpty())
26+
27+
// RTO3a - ObjectsPool is a Dict, a map of LiveObjects keyed by objectId string
28+
val testLiveMap = DefaultLiveMap("testObjectId", mockk(relaxed = true), objectsPool)
29+
objectsPool.set("testObjectId", testLiveMap)
30+
val testLiveCounter = DefaultLiveCounter("testCounterId", mockk(relaxed = true))
31+
objectsPool.set("testCounterId", testLiveCounter)
32+
// Assert that the objects are stored in the pool
33+
assertEquals(testLiveMap, objectsPool.get("testObjectId"))
34+
assertEquals(testLiveCounter, objectsPool.get("testCounterId"))
35+
}
36+
}

live-objects/src/test/kotlin/io/ably/lib/objects/unit/LiveMapManagerTest.kt renamed to live-objects/src/test/kotlin/io/ably/lib/objects/unit/type/livemap/LiveMapManagerTest.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package io.ably.lib.objects.unit
1+
package io.ably.lib.objects.unit.type.livemap
22

33
import io.ably.lib.objects.*
44
import io.ably.lib.objects.type.livemap.LiveMapEntry

0 commit comments

Comments
 (0)