✅ Don’t run some tests in parallel
[Sone.git] / src / test / kotlin / net / pterodactylus / sone / main / SonePluginTest.kt
1 package net.pterodactylus.sone.main
2
3 import com.google.common.eventbus.*
4 import com.google.inject.*
5 import freenet.client.async.*
6 import freenet.l10n.BaseL10n.LANGUAGE.*
7 import freenet.node.*
8 import freenet.pluginmanager.*
9 import net.pterodactylus.sone.core.*
10 import net.pterodactylus.sone.core.event.*
11 import net.pterodactylus.sone.fcp.*
12 import net.pterodactylus.sone.freenet.wot.*
13 import net.pterodactylus.sone.test.*
14 import net.pterodactylus.sone.web.*
15 import net.pterodactylus.sone.web.notification.*
16 import org.hamcrest.MatcherAssert.*
17 import org.hamcrest.Matchers.*
18 import org.junit.experimental.categories.*
19 import org.mockito.Mockito.*
20 import java.io.*
21 import java.util.concurrent.atomic.*
22 import kotlin.test.*
23
24 /**
25  * Unit test for [SonePlugin].
26  */
27 @Dirty
28 @Category(NotParallel::class)
29 class SonePluginTest {
30
31         private var sonePlugin = SonePlugin { injector }
32         private val pluginRespirator = deepMock<PluginRespirator>()
33         private val node = deepMock<Node>()
34         private val clientCore = deepMock<NodeClientCore>()
35         private val uskManager = deepMock<USKManager>()
36
37         init {
38                 setField(node, "clientCore", clientCore)
39                 whenever(pluginRespirator.node).thenReturn(node)
40                 setField(clientCore, "uskManager", uskManager)
41         }
42
43         @Test
44         fun `sone plugin can be started`() {
45                 sonePlugin.setLanguage(ENGLISH)
46                 sonePlugin.runPlugin(pluginRespirator)
47         }
48
49         @Test
50         fun `core can be created`() {
51                 val injector: Injector = runSonePluginWithRealInjector()
52                 assertThat(injector.getInstance<Core>(), notNullValue())
53         }
54
55         @Test
56         fun `fcp interface can be created`() {
57                 val injector: Injector = runSonePluginWithRealInjector()
58                 assertThat(injector.getInstance<FcpInterface>(), notNullValue())
59         }
60
61         @Test
62         fun `web interface can be created`() {
63                 val injector: Injector = runSonePluginWithRealInjector()
64                 assertThat(injector.getInstance<WebInterface>(), notNullValue())
65         }
66
67         @Test
68         fun `web of trust connector can be created`() {
69                 val injector: Injector = runSonePluginWithRealInjector()
70                 assertThat(injector.getInstance<WebOfTrustConnector>(), notNullValue())
71         }
72
73         @Test
74         fun `notification handler can be created`() {
75                 val injector: Injector = runSonePluginWithRealInjector()
76                 assertThat(injector.getInstance<NotificationHandler>(), notNullValue())
77         }
78
79         private fun runSonePluginWithRealInjector(injectorConsumer: (Injector) -> Unit = {}): Injector {
80                 lateinit var injector: Injector
81                 sonePlugin = SonePlugin {
82                         Guice.createInjector(*it).also {
83                                 injector = it
84                                 injectorConsumer(it)
85                         }
86                 }
87                 sonePlugin.setLanguage(ENGLISH)
88                 sonePlugin.runPlugin(pluginRespirator)
89                 return injector
90         }
91
92         @Test
93         fun `core is being started`() {
94                 sonePlugin.runPlugin(pluginRespirator)
95                 val core = injector.getInstance<Core>()
96                 verify(core).start()
97         }
98
99         @Test
100         fun `notification handler is being requested`() {
101                 sonePlugin.runPlugin(pluginRespirator)
102                 assertThat(getInjected(NotificationHandler::class.java), notNullValue())
103         }
104
105         @Test
106         fun `ticker shutdown is being requested`() {
107                 sonePlugin.runPlugin(pluginRespirator)
108                 assertThat(getInjected(TickerShutdown::class.java), notNullValue())
109         }
110
111         private class FirstStartListener(private val firstStartReceived: AtomicBoolean) {
112                 @Subscribe
113                 fun firstStart(firstStart: FirstStart) {
114                         firstStartReceived.set(true)
115                 }
116         }
117
118         @Test
119         fun `first-start event is sent to event bus when first start is true`() {
120                 File("sone.properties").delete()
121                 val firstStartReceived = AtomicBoolean()
122                 runSonePluginWithRealInjector {
123                         val eventBus = it.getInstance(EventBus::class.java)
124                         eventBus.register(FirstStartListener(firstStartReceived))
125                 }
126                 assertThat(firstStartReceived.get(), equalTo(true))
127         }
128
129         @Test
130         fun `first-start event is not sent to event bus when first start is false`() {
131                 File("sone.properties").deleteAfter {
132                         writeText("# empty")
133                         val firstStartReceived = AtomicBoolean()
134                         runSonePluginWithRealInjector {
135                                 val eventBus = it.getInstance(EventBus::class.java)
136                                 eventBus.register(FirstStartListener(firstStartReceived))
137                         }
138                         assertThat(firstStartReceived.get(), equalTo(false))
139                 }
140         }
141
142         private class ConfigNotReadListener(private val configNotReadReceiver: AtomicBoolean) {
143                 @Subscribe
144                 fun configNotRead(configNotRead: ConfigNotRead) {
145                         configNotReadReceiver.set(true)
146                 }
147         }
148
149         @Test
150         fun `config-not-read event is sent to event bus when new config is true`() {
151                 File("sone.properties").deleteAfter {
152                         writeText("Invalid")
153                         val configNotReadReceived = AtomicBoolean()
154                         runSonePluginWithRealInjector {
155                                 val eventBus = it.getInstance(EventBus::class.java)
156                                 eventBus.register(ConfigNotReadListener(configNotReadReceived))
157                         }
158                         assertThat(configNotReadReceived.get(), equalTo(true))
159                 }
160         }
161
162         @Test
163         fun `config-not-read event is not sent to event bus when first start is true`() {
164                 File("sone.properties").delete()
165                 val configNotReadReceived = AtomicBoolean()
166                 runSonePluginWithRealInjector {
167                         val eventBus = it.getInstance(EventBus::class.java)
168                         eventBus.register(ConfigNotReadListener(configNotReadReceived))
169                 }
170                 assertThat(configNotReadReceived.get(), equalTo(false))
171         }
172
173         @Test
174         fun `config-not-read event is not sent to event bus when new config is false`() {
175                 File("sone.properties").deleteAfter {
176                         writeText("# comment")
177                         val configNotReadReceived = AtomicBoolean()
178                         runSonePluginWithRealInjector {
179                                 val eventBus = it.getInstance(EventBus::class.java)
180                                 eventBus.register(ConfigNotReadListener(configNotReadReceived))
181                         }
182                         assertThat(configNotReadReceived.get(), equalTo(false))
183                 }
184         }
185
186         private class StartupListener(private val startupReceived: () -> Unit) {
187                 @Subscribe
188                 fun startup(startup: Startup) {
189                         startupReceived()
190                 }
191         }
192
193         @Test
194         fun `startup event is sent to event bus`() {
195                 val startupReceived = AtomicBoolean()
196                 runSonePluginWithRealInjector {
197                         val eventBus = it.getInstance(EventBus::class.java)
198                         eventBus.register(StartupListener { startupReceived.set(true) })
199                 }
200                 assertThat(startupReceived.get(), equalTo(true))
201         }
202
203         private class ShutdownListener(private val shutdownReceived: () -> Unit) {
204                 @Subscribe
205                 fun shutdown(shutdown: Shutdown) {
206                         shutdownReceived()
207                 }
208         }
209
210         @Test
211         fun `shutdown event is sent to event bus on terminate`() {
212                 val shutdownReceived = AtomicBoolean()
213                 runSonePluginWithRealInjector {
214                         val eventBus = it.getInstance(EventBus::class.java)
215                         eventBus.register(ShutdownListener { shutdownReceived.set(true) })
216                 }
217                 sonePlugin.terminate()
218                 assertThat(shutdownReceived.get(), equalTo(true))
219         }
220
221         private fun <T> getInjected(clazz: Class<T>, annotation: Annotation? = null): T? =
222                         injected[TypeLiteral.get(clazz) to annotation] as? T
223
224         private val injected =
225                         mutableMapOf<Pair<TypeLiteral<*>, Annotation?>, Any>()
226
227         private val injector = mock<Injector>().apply {
228                 fun mockValue(clazz: Class<*>) = false.takeIf { clazz.name == java.lang.Boolean::class.java.name } ?: mock(clazz)
229                 whenever(getInstance(any<Key<*>>())).then {
230                         injected.getOrPut((it.getArgument(0) as Key<*>).let { it.typeLiteral to it.annotation }) {
231                                 it.getArgument<Key<*>>(0).typeLiteral.type.typeName.toClass().let(::mockValue)
232                         }
233                 }
234                 whenever(getInstance(any<Class<*>>())).then {
235                         injected.getOrPut(TypeLiteral.get(it.getArgument(0) as Class<*>) to null) {
236                                 it.getArgument<Class<*>>(0).let(::mockValue)
237                         }
238                 }
239         }
240
241 }
242
243 private fun String.toClass(): Class<*> = SonePlugin::class.java.classLoader.loadClass(this)
244
245 private fun File.deleteAfter(action: File.() -> Unit) = try {
246         action(this)
247 } finally {
248         this.delete()
249 }