TestEtmViewModel.kt 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. /*
  2. * Nextcloud Android client application
  3. *
  4. * @author Chris Narkiewicz
  5. * Copyright (C) 2020 Chris Narkiewicz <hello@ezaquarii.com>
  6. *
  7. * This program is free software: you can redistribute it and/or modify
  8. * it under the terms of the GNU Affero General Public License as published by
  9. * the Free Software Foundation, either version 3 of the License, or
  10. * (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU Affero General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU Affero General Public License
  18. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  19. */
  20. package com.nextcloud.client.etm
  21. import android.accounts.AccountManager
  22. import android.content.Context
  23. import android.content.SharedPreferences
  24. import android.content.res.Resources
  25. import androidx.arch.core.executor.testing.InstantTaskExecutorRule
  26. import androidx.lifecycle.LiveData
  27. import androidx.lifecycle.Observer
  28. import com.nextcloud.client.account.MockUser
  29. import com.nextcloud.client.account.UserAccountManager
  30. import com.nextcloud.client.etm.pages.EtmBackgroundJobsFragment
  31. import com.nextcloud.client.jobs.BackgroundJobManager
  32. import com.nextcloud.client.jobs.JobInfo
  33. import com.nextcloud.client.migrations.MigrationsDb
  34. import com.nextcloud.client.migrations.MigrationsManager
  35. import org.junit.Assert.assertEquals
  36. import org.junit.Assert.assertFalse
  37. import org.junit.Assert.assertNotNull
  38. import org.junit.Assert.assertNull
  39. import org.junit.Assert.assertSame
  40. import org.junit.Assert.assertTrue
  41. import org.junit.Before
  42. import org.junit.Rule
  43. import org.junit.Test
  44. import org.junit.runner.RunWith
  45. import org.junit.runners.Suite
  46. import org.mockito.kotlin.any
  47. import org.mockito.kotlin.anyOrNull
  48. import org.mockito.kotlin.eq
  49. import org.mockito.kotlin.inOrder
  50. import org.mockito.kotlin.mock
  51. import org.mockito.kotlin.never
  52. import org.mockito.kotlin.reset
  53. import org.mockito.kotlin.same
  54. import org.mockito.kotlin.times
  55. import org.mockito.kotlin.verify
  56. import org.mockito.kotlin.whenever
  57. @RunWith(Suite::class)
  58. @Suite.SuiteClasses(
  59. TestEtmViewModel.MainPage::class,
  60. TestEtmViewModel.PreferencesPage::class,
  61. TestEtmViewModel.BackgroundJobsPage::class
  62. )
  63. class TestEtmViewModel {
  64. internal abstract class Base {
  65. @get:Rule
  66. val rule = InstantTaskExecutorRule()
  67. protected lateinit var context: Context
  68. protected lateinit var platformAccountManager: AccountManager
  69. protected lateinit var accountManager: UserAccountManager
  70. protected lateinit var sharedPreferences: SharedPreferences
  71. protected lateinit var vm: EtmViewModel
  72. protected lateinit var resources: Resources
  73. protected lateinit var backgroundJobManager: BackgroundJobManager
  74. protected lateinit var migrationsManager: MigrationsManager
  75. protected lateinit var migrationsDb: MigrationsDb
  76. @Before
  77. fun setUpBase() {
  78. context = mock()
  79. sharedPreferences = mock()
  80. platformAccountManager = mock()
  81. accountManager = mock()
  82. resources = mock()
  83. backgroundJobManager = mock()
  84. migrationsManager = mock()
  85. migrationsDb = mock()
  86. whenever(resources.getString(any())).thenReturn("mock-account-type")
  87. whenever(accountManager.user).thenReturn(MockUser())
  88. vm = EtmViewModel(
  89. context,
  90. sharedPreferences,
  91. platformAccountManager,
  92. accountManager,
  93. resources,
  94. backgroundJobManager,
  95. migrationsManager,
  96. migrationsDb
  97. )
  98. }
  99. }
  100. internal class MainPage : Base() {
  101. @Test
  102. fun `current page is not set`() {
  103. // GIVEN
  104. // main page is displayed
  105. // THEN
  106. // current page is null
  107. assertNull(vm.currentPage.value)
  108. }
  109. @Test
  110. fun `back key is not handled`() {
  111. // GIVEN
  112. // main page is displayed
  113. // WHEN
  114. // back key is pressed
  115. val handled = vm.onBackPressed()
  116. // THEN
  117. // is not handled
  118. assertFalse(handled)
  119. }
  120. @Test
  121. fun `page is selected`() {
  122. val observer: Observer<EtmMenuEntry?> = mock()
  123. val selectedPageIndex = 0
  124. val expectedPage = vm.pages[selectedPageIndex]
  125. // GIVEN
  126. // main page is displayed
  127. // current page observer is registered
  128. vm.currentPage.observeForever(observer)
  129. reset(observer)
  130. // WHEN
  131. // page is selected
  132. vm.onPageSelected(selectedPageIndex)
  133. // THEN
  134. // current page is set
  135. // page observer is called once with selected entry
  136. assertNotNull(vm.currentPage.value)
  137. verify(observer, times(1)).onChanged(same(expectedPage))
  138. }
  139. @Test
  140. fun `out of range index is ignored`() {
  141. val maxIndex = vm.pages.size
  142. // GIVEN
  143. // observer is registered
  144. val observer: Observer<EtmMenuEntry?> = mock()
  145. vm.currentPage.observeForever(observer)
  146. reset(observer)
  147. // WHEN
  148. // out of range page index is selected
  149. vm.onPageSelected(maxIndex + 1)
  150. // THEN
  151. // nothing happens
  152. verify(observer, never()).onChanged(anyOrNull())
  153. assertNull(vm.currentPage.value)
  154. }
  155. }
  156. internal class PreferencesPage : Base() {
  157. @Before
  158. fun setUp() {
  159. vm.onPageSelected(0)
  160. }
  161. @Test
  162. fun `back goes back to main page`() {
  163. val observer: Observer<EtmMenuEntry?> = mock()
  164. // GIVEN
  165. // a page is selected
  166. // page observer is registered
  167. assertNotNull(vm.currentPage.value)
  168. vm.currentPage.observeForever(observer)
  169. // WHEN
  170. // back is pressed
  171. val handled = vm.onBackPressed()
  172. // THEN
  173. // back press is handled
  174. // observer is called with null page
  175. assertTrue(handled)
  176. verify(observer).onChanged(eq(null))
  177. }
  178. @Test
  179. fun `back is handled only once`() {
  180. // GIVEN
  181. // a page is selected
  182. assertNotNull(vm.currentPage.value)
  183. // WHEN
  184. // back is pressed twice
  185. val first = vm.onBackPressed()
  186. val second = vm.onBackPressed()
  187. // THEN
  188. // back is handled only once
  189. assertTrue(first)
  190. assertFalse(second)
  191. }
  192. @Test
  193. fun `preferences are loaded from shared preferences`() {
  194. // GIVEN
  195. // shared preferences contain values of different types
  196. val preferenceValues: Map<String, Any> = mapOf(
  197. "key1" to 1,
  198. "key2" to "value2",
  199. "key3" to false
  200. )
  201. whenever(sharedPreferences.all).thenReturn(preferenceValues)
  202. // WHEN
  203. // vm preferences are read
  204. val prefs = vm.preferences
  205. // THEN
  206. // all preferences are converted to strings
  207. assertEquals(preferenceValues.size, prefs.size)
  208. assertEquals("1", prefs["key1"])
  209. assertEquals("value2", prefs["key2"])
  210. assertEquals("false", prefs["key3"])
  211. }
  212. }
  213. internal class BackgroundJobsPage : Base() {
  214. @Before
  215. fun setUp() {
  216. vm.onPageSelected(EtmViewModel.PAGE_JOBS)
  217. assertEquals(EtmBackgroundJobsFragment::class, vm.currentPage.value?.pageClass)
  218. }
  219. @Test
  220. fun `prune jobs action is delegated to job manager`() {
  221. vm.pruneJobs()
  222. verify(backgroundJobManager).pruneJobs()
  223. }
  224. @Test
  225. fun `start stop test job actions are delegated to job manager`() {
  226. vm.startTestJob(true)
  227. vm.cancelTestJob()
  228. inOrder(backgroundJobManager).apply {
  229. verify(backgroundJobManager).scheduleTestJob()
  230. verify(backgroundJobManager).cancelTestJob()
  231. }
  232. }
  233. @Test
  234. fun `job info is taken from job manager`() {
  235. val jobInfo: LiveData<List<JobInfo>> = mock()
  236. whenever(backgroundJobManager.jobs).thenReturn(jobInfo)
  237. assertSame(jobInfo, vm.backgroundJobs)
  238. }
  239. }
  240. }