aboutsummaryrefslogtreecommitdiff
path: root/app/src/test/kotlin/com/pitchedapps/frost/rx/FlyweightTest.kt
blob: 834163bd0b6b6ef579db7d142fab8fa24ec3e88d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
package com.pitchedapps.frost.rx

import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.async
import kotlinx.coroutines.runBlocking
import org.junit.Rule
import org.junit.rules.Timeout
import java.util.concurrent.atomic.AtomicInteger
import kotlin.test.BeforeTest
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertTrue
import kotlin.test.fail

class FlyweightTest {

    @get:Rule
    val globalTimeout: Timeout = Timeout.seconds(5)

    lateinit var flyweight: Flyweight<Int, Int>

    lateinit var callCount: AtomicInteger

    private val LONG_RUNNING_KEY = -78

    @BeforeTest
    fun before() {
        callCount = AtomicInteger(0)
        flyweight = Flyweight(GlobalScope, 100, 200L) {
            callCount.incrementAndGet()
            when (it) {
                LONG_RUNNING_KEY -> Thread.sleep(100000)
                else -> Thread.sleep(100)
            }
            it * 2
        }
    }

    @Test
    fun basic() {
        assertEquals(2, runBlocking { flyweight.fetch(1) }, "Invalid result")
        assertEquals(1, callCount.get(), "1 call expected")
    }

    @Test
    fun multipleWithOneKey() {
        val results: List<Int> = runBlocking {
            (0..1000).map {
                flyweight.scope.async {
                    flyweight.fetch(1)
                }
            }.map { it.await() }
        }
        assertEquals(1, callCount.get(), "1 call expected")
        assertEquals(1001, results.size, "Incorrect number of results returned")
        assertTrue(results.all { it == 2 }, "Result should all be 2")
    }

    @Test
    fun consecutiveReuse() {
        runBlocking {
            flyweight.fetch(1)
            assertEquals(1, callCount.get(), "1 call expected")
            flyweight.fetch(1)
            assertEquals(1, callCount.get(), "Reuse expected")
            Thread.sleep(300)
            flyweight.fetch(1)
            assertEquals(2, callCount.get(), "Refetch expected")
        }
    }

    @Test
    fun invalidate() {
        runBlocking {
            flyweight.fetch(1)
            assertEquals(1, callCount.get(), "1 call expected")
            flyweight.invalidate(1)
            flyweight.fetch(1)
            assertEquals(2, callCount.get(), "New call expected")
        }
    }

    @Test
    fun destroy() {
        runBlocking {
            val longRunningResult = async { flyweight.fetch(LONG_RUNNING_KEY) }
            flyweight.fetch(1)
            flyweight.cancel()
            try {
                flyweight.fetch(1)
                fail("Flyweight should not be fulfilled after it is destroyed")
            } catch (e: Exception) {
                assertEquals("Flyweight is not active", e.message, "Incorrect error found on fetch after destruction")
            }
            try {
                longRunningResult.await()
                fail("Flyweight should have cancelled previously running requests")
            } catch (e: Exception) {
                assertEquals(
                    "Flyweight cancelled",
                    e.message,
                    "Incorrect error found on fetch cancelled by destruction"
                )
            }
            println("Done")
        }
    }
}