/** * Nextcloud Android client application * * @author Chris Narkiewicz * Copyright (C) 2020 Chris Narkiewicz * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ package com.nextcloud.client.files.downloader import android.content.ComponentName import android.content.Context import com.nextcloud.client.account.MockUser import com.owncloud.android.datamodel.OCFile import io.mockk.MockKAnnotations import io.mockk.every import io.mockk.impl.annotations.MockK import io.mockk.mockk import io.mockk.verify import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Test class DownloaderConnectionTest { lateinit var connection: DownloaderConnection @MockK lateinit var context: Context @MockK lateinit var firstDownloadListener: (Download) -> Unit @MockK lateinit var secondDownloadListener: (Download) -> Unit @MockK lateinit var firstStatusListener: (Downloader.Status) -> Unit @MockK lateinit var secondStatusListener: (Downloader.Status) -> Unit @MockK lateinit var binder: DownloaderService.Binder val file get() = OCFile("/path") val componentName = ComponentName("", DownloaderService::class.java.simpleName) val user = MockUser() @Before fun setUp() { MockKAnnotations.init(this, relaxed = true) connection = DownloaderConnection(context, user) } @Test fun listeners_are_set_after_connection() { // GIVEN // not connected // listener is added connection.registerDownloadListener(firstDownloadListener) connection.registerDownloadListener(secondDownloadListener) // WHEN // service is bound connection.onServiceConnected(componentName, binder) // THEN // all listeners are passed to the service val listeners = mutableListOf<(Download) -> Unit>() verify { binder.registerDownloadListener(capture(listeners)) } assertEquals(listOf(firstDownloadListener, secondDownloadListener), listeners) } @Test fun listeners_are_set_immediately_when_connected() { // GIVEN // service is bound connection.onServiceConnected(componentName, binder) // WHEN // listeners are added connection.registerDownloadListener(firstDownloadListener) // THEN // listener is forwarded to service verify { binder.registerDownloadListener(firstDownloadListener) } } @Test fun listeners_are_removed_when_unbinding() { // GIVEN // service is bound // service has some listeners connection.onServiceConnected(componentName, binder) connection.registerDownloadListener(firstDownloadListener) connection.registerDownloadListener(secondDownloadListener) // WHEN // service unbound connection.unbind() // THEN // listeners removed from service verify { binder.removeDownloadListener(firstDownloadListener) } verify { binder.removeDownloadListener(secondDownloadListener) } } @Test fun missed_updates_are_delivered_on_connection() { // GIVEN // not bound // has listeners // download is scheduled and is progressing connection.registerDownloadListener(firstDownloadListener) connection.registerDownloadListener(secondDownloadListener) val request1 = Request(user, file) connection.download(request1) val download1 = Download(request1.uuid, DownloadState.RUNNING, 50, request1.file, request1) val request2 = Request(user, file) connection.download(request2) val download2 = Download(request2.uuid, DownloadState.RUNNING, 50, request2.file, request1) every { binder.getDownload(request1.uuid) } returns download1 every { binder.getDownload(request2.uuid) } returns download2 // WHEN // service is bound connection.onServiceConnected(componentName, binder) // THEN // listeners receive current download state for pending downloads val firstListenerNotifications = mutableListOf() verify { firstDownloadListener(capture(firstListenerNotifications)) } assertEquals(listOf(download1, download2), firstListenerNotifications) val secondListenerNotifications = mutableListOf() verify { secondDownloadListener(capture(secondListenerNotifications)) } assertEquals(listOf(download1, download2), secondListenerNotifications) } @Test fun downloader_status_updates_are_delivered_on_connection() { // GIVEN // not bound // has status listeners val mockStatus: Downloader.Status = mockk() every { binder.status } returns mockStatus connection.registerStatusListener(firstStatusListener) connection.registerStatusListener(secondStatusListener) // WHEN // service is bound connection.onServiceConnected(componentName, binder) // THEN // downloader status is delivered verify { firstStatusListener(mockStatus) } verify { secondStatusListener(mockStatus) } } @Test fun downloader_status_not_requested_if_no_listeners() { // GIVEN // not bound // no status listeners // WHEN // service is bound connection.onServiceConnected(componentName, binder) // THEN // downloader status is not requested verify(exactly = 0) { binder.status } } @Test fun not_running_if_not_connected() { // GIVEN // downloader is running // connection not bound every { binder.isRunning } returns true // THEN // not running assertFalse(connection.isRunning) } @Test fun is_running_from_binder_if_connected() { // GIVEN // service bound every { binder.isRunning } returns true connection.onServiceConnected(componentName, binder) // WHEN // is runnign flag accessed val isRunning = connection.isRunning // THEN // call delegated to binder assertTrue(isRunning) verify(exactly = 1) { binder.isRunning } } @Test fun missed_updates_not_tracked_before_listeners_registered() { // GIVEN // not bound // some downloads requested without listener val request = Request(user, file) connection.download(request) val download = Download(request.uuid, DownloadState.RUNNING, 50, request.file, request) connection.registerDownloadListener(firstDownloadListener) every { binder.getDownload(request.uuid) } returns download // WHEN // service is bound connection.onServiceConnected(componentName, binder) // THEN // missed updates not redelivered verify(exactly = 0) { firstDownloadListener(any()) } } }