commit
3725da7e53
31 changed files with 350 additions and 245 deletions
|
|
@ -6,13 +6,12 @@ plugins {
|
||||||
apply from: 'signing.config.gradle'
|
apply from: 'signing.config.gradle'
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 30
|
compileSdk 31
|
||||||
buildToolsVersion "30.0.2"
|
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "org.fnives.tiktokdownloader"
|
applicationId "org.fnives.tiktokdownloader"
|
||||||
minSdkVersion 23
|
minSdk 23
|
||||||
targetSdkVersion 30
|
targetSdk 31
|
||||||
versionCode 1
|
versionCode 1
|
||||||
versionName "1.0.0"
|
versionName "1.0.0"
|
||||||
|
|
||||||
|
|
@ -51,41 +50,65 @@ android {
|
||||||
lintOptions {
|
lintOptions {
|
||||||
abortOnError true
|
abortOnError true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
testOptions.unitTests {
|
||||||
|
includeAndroidResources = true
|
||||||
|
all {
|
||||||
|
useJUnitPlatform()
|
||||||
|
testLogging {
|
||||||
|
events 'started', 'passed', 'skipped', 'failed'
|
||||||
|
exceptionFormat "full"
|
||||||
|
showStandardStreams true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.configureEach { task ->
|
||||||
|
if (task.taskIdentity.type.toString() == "class org.jetbrains.kotlin.gradle.tasks.KotlinCompile") {
|
||||||
|
task.kotlinOptions {
|
||||||
|
freeCompilerArgs += "-opt-in=kotlin.RequiresOptIn"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||||
implementation "androidx.core:core-ktx:1.3.2"
|
implementation "androidx.core:core-ktx:1.7.0"
|
||||||
implementation "androidx.appcompat:appcompat:1.2.0"
|
implementation "androidx.appcompat:appcompat:1.4.1"
|
||||||
implementation "androidx.activity:activity-ktx:1.2.0-beta01"
|
implementation "androidx.activity:activity-ktx:1.4.0"
|
||||||
implementation "androidx.fragment:fragment-ktx:1.3.0-beta01"
|
implementation "androidx.fragment:fragment-ktx:1.4.1"
|
||||||
implementation "com.google.android.material:material:1.2.1"
|
implementation "com.google.android.material:material:1.5.0"
|
||||||
implementation "androidx.constraintlayout:constraintlayout:2.0.4"
|
implementation "androidx.constraintlayout:constraintlayout:2.1.3"
|
||||||
|
|
||||||
// Coroutines
|
// Coroutines
|
||||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.0-M1"
|
def coroutine_version = "1.6.0"
|
||||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9"
|
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutine_version"
|
||||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9"
|
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutine_version"
|
||||||
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0"
|
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutine_version"
|
||||||
implementation "androidx.fragment:fragment-ktx:1.2.5"
|
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.1"
|
||||||
|
implementation "androidx.fragment:fragment-ktx:1.4.1"
|
||||||
|
|
||||||
implementation "com.github.bumptech.glide:glide:4.11.0"
|
def glide_version = "4.11.0"
|
||||||
kapt "com.github.bumptech.glide:compiler:4.11.0"
|
implementation "com.github.bumptech.glide:glide:$glide_version"
|
||||||
|
kapt "com.github.bumptech.glide:compiler:$glide_version"
|
||||||
|
|
||||||
|
def okhttp_version = "4.9.3"
|
||||||
implementation "com.squareup.retrofit2:retrofit:2.9.0"
|
implementation "com.squareup.retrofit2:retrofit:2.9.0"
|
||||||
implementation "com.squareup.okhttp3:logging-interceptor:4.7.2"
|
implementation "com.squareup.okhttp3:logging-interceptor:$okhttp_version"
|
||||||
implementation 'com.pierfrancescosoffritti.androidyoutubeplayer:core:10.0.5'
|
implementation 'com.pierfrancescosoffritti.androidyoutubeplayer:core:11.0.1'
|
||||||
|
|
||||||
testImplementation "org.junit.jupiter:junit-jupiter-engine:5.7.0"
|
def junit_version = "5.7.0"
|
||||||
testImplementation "org.junit.jupiter:junit-jupiter-params:5.7.0"
|
testImplementation "org.junit.jupiter:junit-jupiter-engine:$junit_version"
|
||||||
testImplementation 'com.jraska.livedata:testing-ktx:1.1.2'
|
testImplementation "org.junit.jupiter:junit-jupiter-params:$junit_version"
|
||||||
testImplementation "com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0"
|
testImplementation 'com.jraska.livedata:testing-ktx:1.2.0'
|
||||||
testImplementation "com.squareup.okhttp3:mockwebserver:4.2.1"
|
testImplementation "org.mockito.kotlin:mockito-kotlin:4.0.0"
|
||||||
|
testImplementation "com.squareup.okhttp3:mockwebserver:$okhttp_version"
|
||||||
testImplementation "commons-io:commons-io:2.8.0"
|
testImplementation "commons-io:commons-io:2.8.0"
|
||||||
testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:1.4.0-M1"
|
testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutine_version"
|
||||||
testImplementation "androidx.arch.core:core-testing:2.1.0"
|
testImplementation "androidx.arch.core:core-testing:2.1.0"
|
||||||
|
|
||||||
androidTestImplementation "androidx.test.ext:junit:1.1.2"
|
androidTestImplementation "androidx.test.ext:junit:1.1.3"
|
||||||
androidTestImplementation "androidx.test.espresso:espresso-core:3.3.0"
|
androidTestImplementation "androidx.test.espresso:espresso-core:3.4.0"
|
||||||
}
|
}
|
||||||
|
|
@ -13,19 +13,23 @@
|
||||||
<application
|
<application
|
||||||
android:name=".App"
|
android:name=".App"
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
|
android:fullBackupContent="false"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:fullBackupContent="false"
|
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/Theme.TikTokDownloader">
|
android:theme="@style/Theme.TikTokDownloader">
|
||||||
<activity android:name=".ui.main.MainActivity">
|
<activity
|
||||||
|
android:name=".ui.main.MainActivity"
|
||||||
|
android:exported="true">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
<activity android:name=".ui.service.DownloadIntentReceiverActivity"
|
<activity
|
||||||
|
android:name=".ui.service.DownloadIntentReceiverActivity"
|
||||||
|
android:exported="true"
|
||||||
android:theme="@style/NoDisplayTheme">
|
android:theme="@style/NoDisplayTheme">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.SEND" />
|
<action android:name="android.intent.action.SEND" />
|
||||||
|
|
@ -35,6 +39,7 @@
|
||||||
<data android:mimeType="message/*" />
|
<data android:mimeType="message/*" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<service android:name=".ui.service.QueueService" />
|
<service android:name=".ui.service.QueueService" />
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
|
|
|
||||||
12
app/src/main/java/org/fnives/tiktokdownloader/Logger.kt
Normal file
12
app/src/main/java/org/fnives/tiktokdownloader/Logger.kt
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
package org.fnives.tiktokdownloader
|
||||||
|
|
||||||
|
object Logger {
|
||||||
|
|
||||||
|
private const val TAG = "TTDTag"
|
||||||
|
|
||||||
|
fun logMessage(message: String) {
|
||||||
|
if (BuildConfig.DEBUG) {
|
||||||
|
System.err.println("TTDTag $message")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -39,10 +39,10 @@ class SharedPreferencesManagerImpl private constructor(private val sharedPrefere
|
||||||
override fun getValue(thisRef: SharedPreferencesManagerImpl, property: KProperty<*>): Flow<Set<String>> =
|
override fun getValue(thisRef: SharedPreferencesManagerImpl, property: KProperty<*>): Flow<Set<String>> =
|
||||||
callbackFlow {
|
callbackFlow {
|
||||||
val listener = SharedPreferences.OnSharedPreferenceChangeListener { _, _ ->
|
val listener = SharedPreferences.OnSharedPreferenceChangeListener { _, _ ->
|
||||||
offer(thisRef.getValues())
|
trySend(thisRef.getValues())
|
||||||
}
|
}
|
||||||
thisRef.sharedPreferences.registerOnSharedPreferenceChangeListener(listener)
|
thisRef.sharedPreferences.registerOnSharedPreferenceChangeListener(listener)
|
||||||
offer(thisRef.getValues())
|
trySend(thisRef.getValues())
|
||||||
|
|
||||||
awaitClose {
|
awaitClose {
|
||||||
thisRef.sharedPreferences.unregisterOnSharedPreferenceChangeListener(listener)
|
thisRef.sharedPreferences.unregisterOnSharedPreferenceChangeListener(listener)
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package org.fnives.tiktokdownloader.data.network
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
import org.fnives.tiktokdownloader.Logger
|
||||||
import org.fnives.tiktokdownloader.data.model.VideoInPending
|
import org.fnives.tiktokdownloader.data.model.VideoInPending
|
||||||
import org.fnives.tiktokdownloader.data.model.VideoInSavingIntoFile
|
import org.fnives.tiktokdownloader.data.model.VideoInSavingIntoFile
|
||||||
import org.fnives.tiktokdownloader.data.network.exceptions.CaptchaRequiredException
|
import org.fnives.tiktokdownloader.data.network.exceptions.CaptchaRequiredException
|
||||||
|
|
@ -22,8 +23,10 @@ class TikTokDownloadRemoteSource(
|
||||||
wrapIntoProperException {
|
wrapIntoProperException {
|
||||||
delay(delayBeforeRequest) // added just so captcha trigger may not happen
|
delay(delayBeforeRequest) // added just so captcha trigger may not happen
|
||||||
val actualUrl = service.getContentActualUrlAndCookie(videoInPending.url)
|
val actualUrl = service.getContentActualUrlAndCookie(videoInPending.url)
|
||||||
|
Logger.logMessage("actualUrl found = ${actualUrl.url}")
|
||||||
delay(delayBeforeRequest) // added just so captcha trigger may not happen
|
delay(delayBeforeRequest) // added just so captcha trigger may not happen
|
||||||
val videoUrl = service.getVideoUrl(actualUrl.url)
|
val videoUrl = service.getVideoUrl(actualUrl.url)
|
||||||
|
Logger.logMessage("videoFileUrl found = ${videoUrl.videoFileUrl}")
|
||||||
delay(delayBeforeRequest) // added just so captcha trigger may not happen
|
delay(delayBeforeRequest) // added just so captcha trigger may not happen
|
||||||
val response = service.getVideo(videoUrl.videoFileUrl)
|
val response = service.getVideo(videoUrl.videoFileUrl)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
package org.fnives.tiktokdownloader.data.network.parsing.converter
|
package org.fnives.tiktokdownloader.data.network.parsing.converter
|
||||||
|
|
||||||
import okhttp3.ResponseBody
|
import okhttp3.ResponseBody
|
||||||
|
import org.fnives.tiktokdownloader.Logger
|
||||||
import org.fnives.tiktokdownloader.data.network.exceptions.CaptchaRequiredException
|
import org.fnives.tiktokdownloader.data.network.exceptions.CaptchaRequiredException
|
||||||
import org.fnives.tiktokdownloader.data.network.parsing.response.VideoFileUrl
|
import org.fnives.tiktokdownloader.data.network.parsing.response.VideoFileUrl
|
||||||
import kotlin.jvm.Throws
|
|
||||||
|
|
||||||
class VideoFileUrlConverter(
|
class VideoFileUrlConverter(
|
||||||
private val throwIfIsCaptchaResponse: ThrowIfIsCaptchaResponse
|
private val throwIfIsCaptchaResponse: ThrowIfIsCaptchaResponse
|
||||||
|
|
@ -12,8 +12,8 @@ class VideoFileUrlConverter(
|
||||||
@Throws(IllegalArgumentException::class, IndexOutOfBoundsException::class, CaptchaRequiredException::class)
|
@Throws(IllegalArgumentException::class, IndexOutOfBoundsException::class, CaptchaRequiredException::class)
|
||||||
override fun convertSafely(responseBody: ResponseBody): VideoFileUrl? {
|
override fun convertSafely(responseBody: ResponseBody): VideoFileUrl? {
|
||||||
val html = responseBody.string().also(throwIfIsCaptchaResponse::invoke)
|
val html = responseBody.string().also(throwIfIsCaptchaResponse::invoke)
|
||||||
val url = tryToParseDownloadLink(html)
|
val url = tryToParseDownloadLink(html).also { Logger.logMessage("parsed download link = $it") }
|
||||||
?: tryToParseVideoSrc(html)
|
?: tryToParseVideoSrc(html).also { Logger.logMessage("parsed video src = $it") }
|
||||||
?: throw IllegalArgumentException("Couldn't parse url from HTML: $html")
|
?: throw IllegalArgumentException("Couldn't parse url from HTML: $html")
|
||||||
|
|
||||||
return VideoFileUrl(url)
|
return VideoFileUrl(url)
|
||||||
|
|
@ -26,7 +26,7 @@ class VideoFileUrlConverter(
|
||||||
html.split("\"playAddr\"")[1]
|
html.split("\"playAddr\"")[1]
|
||||||
.dropWhile { it != '\"' }.drop(1)
|
.dropWhile { it != '\"' }.drop(1)
|
||||||
.takeWhile { it != '\"' }
|
.takeWhile { it != '\"' }
|
||||||
.replace("\\u0026", "&")
|
.urlCharacterReplacements()
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|
@ -39,10 +39,19 @@ class VideoFileUrlConverter(
|
||||||
.dropWhile { it != '=' }
|
.dropWhile { it != '=' }
|
||||||
.dropWhile { it != '\"' }.drop(1)
|
.dropWhile { it != '\"' }.drop(1)
|
||||||
.takeWhile { it != '\"' }
|
.takeWhile { it != '\"' }
|
||||||
.replace("\\u0026", "&")
|
.urlCharacterReplacements()
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val replacements = mutableMapOf(
|
||||||
|
"\\u002F" to "/",
|
||||||
|
"\\u0026" to "&"
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun String.urlCharacterReplacements(): String =
|
||||||
|
replacements.entries.fold(this) { result, toReplaceEntry ->
|
||||||
|
result.replace(toReplaceEntry.key, toReplaceEntry.value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -5,6 +5,6 @@ import org.fnives.tiktokdownloader.data.network.parsing.response.VideoResponse
|
||||||
|
|
||||||
class VideoResponseConverter : ParsingExceptionThrowingConverter<VideoResponse>() {
|
class VideoResponseConverter : ParsingExceptionThrowingConverter<VideoResponse>() {
|
||||||
|
|
||||||
override fun convertSafely(value: ResponseBody): VideoResponse? =
|
override fun convertSafely(responseBody: ResponseBody): VideoResponse? =
|
||||||
VideoResponse(value.contentType(), value.byteStream())
|
VideoResponse(responseBody.contentType(), responseBody.byteStream())
|
||||||
}
|
}
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
package org.fnives.tiktokdownloader.data.usecase
|
package org.fnives.tiktokdownloader.data.usecase
|
||||||
|
|
||||||
import kotlinx.coroutines.CoroutineDispatcher
|
import kotlinx.coroutines.CoroutineDispatcher
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.FlowPreview
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.combine
|
import kotlinx.coroutines.flow.combine
|
||||||
import kotlinx.coroutines.flow.debounce
|
import kotlinx.coroutines.flow.debounce
|
||||||
|
|
@ -16,6 +16,7 @@ import org.fnives.tiktokdownloader.data.model.VideoInPending
|
||||||
import org.fnives.tiktokdownloader.data.model.VideoInProgress
|
import org.fnives.tiktokdownloader.data.model.VideoInProgress
|
||||||
import org.fnives.tiktokdownloader.data.model.VideoState
|
import org.fnives.tiktokdownloader.data.model.VideoState
|
||||||
|
|
||||||
|
@OptIn(FlowPreview::class)
|
||||||
class StateOfVideosObservableUseCase(
|
class StateOfVideosObservableUseCase(
|
||||||
videoInProgressLocalSource: VideoInProgressLocalSource,
|
videoInProgressLocalSource: VideoInProgressLocalSource,
|
||||||
videoInPendingLocalSource: VideoInPendingLocalSource,
|
videoInPendingLocalSource: VideoInPendingLocalSource,
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ package org.fnives.tiktokdownloader.data.usecase
|
||||||
|
|
||||||
import kotlinx.coroutines.CoroutineDispatcher
|
import kotlinx.coroutines.CoroutineDispatcher
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.FlowPreview
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.SharingStarted
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
|
|
@ -30,6 +31,7 @@ import org.fnives.tiktokdownloader.data.network.exceptions.CaptchaRequiredExcept
|
||||||
import org.fnives.tiktokdownloader.data.network.exceptions.NetworkException
|
import org.fnives.tiktokdownloader.data.network.exceptions.NetworkException
|
||||||
import org.fnives.tiktokdownloader.data.network.exceptions.ParsingException
|
import org.fnives.tiktokdownloader.data.network.exceptions.ParsingException
|
||||||
|
|
||||||
|
@OptIn(FlowPreview::class)
|
||||||
class VideoDownloadingProcessorUseCase(
|
class VideoDownloadingProcessorUseCase(
|
||||||
private val tikTokDownloadRemoteSource: TikTokDownloadRemoteSource,
|
private val tikTokDownloadRemoteSource: TikTokDownloadRemoteSource,
|
||||||
private val videoInProgressLocalSource: VideoInProgressLocalSource,
|
private val videoInProgressLocalSource: VideoInProgressLocalSource,
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ class ViewModelFactory(
|
||||||
private val viewModelModule: ViewModelModule,
|
private val viewModelModule: ViewModelModule,
|
||||||
) : AbstractSavedStateViewModelFactory(savedStateRegistryOwner, defaultArgs) {
|
) : AbstractSavedStateViewModelFactory(savedStateRegistryOwner, defaultArgs) {
|
||||||
|
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
override fun <T : ViewModel?> create(key: String, modelClass: Class<T>, handle: SavedStateHandle): T {
|
override fun <T : ViewModel?> create(key: String, modelClass: Class<T>, handle: SavedStateHandle): T {
|
||||||
val viewModel = when (modelClass) {
|
val viewModel = when (modelClass) {
|
||||||
MainViewModel::class.java -> viewModelModule.mainViewModel(handle)
|
MainViewModel::class.java -> viewModelModule.mainViewModel(handle)
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ import android.os.Bundle
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import android.view.animation.AccelerateDecelerateInterpolator
|
import android.view.animation.AccelerateDecelerateInterpolator
|
||||||
import android.view.animation.OvershootInterpolator
|
import android.view.animation.OvershootInterpolator
|
||||||
import androidx.activity.viewModels
|
|
||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||||
|
|
@ -43,28 +42,28 @@ class MainActivity : AppCompatActivity() {
|
||||||
animateFabClicked(downloadFab)
|
animateFabClicked(downloadFab)
|
||||||
viewModel.onFetchDownloadClicked()
|
viewModel.onFetchDownloadClicked()
|
||||||
}
|
}
|
||||||
viewModel.refreshActionVisibility.observe(this, {
|
viewModel.refreshActionVisibility.observe(this) {
|
||||||
animateFabVisibility(downloadFab, it == true)
|
animateFabVisibility(downloadFab, it == true)
|
||||||
})
|
}
|
||||||
viewModel.errorMessage.observe(this, {
|
viewModel.errorMessage.observe(this) {
|
||||||
val stringRes = it?.item?.stringRes ?: return@observe
|
val stringRes = it?.item?.stringRes ?: return@observe
|
||||||
Snackbar.make(snackBarAnchor, stringRes, Snackbar.LENGTH_SHORT).show()
|
Snackbar.make(snackBarAnchor, stringRes, Snackbar.LENGTH_SHORT).show()
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupBottomNavigationView(bottomNavigationView: BottomNavigationView, savedInstanceState: Bundle?) {
|
private fun setupBottomNavigationView(bottomNavigationView: BottomNavigationView, savedInstanceState: Bundle?) {
|
||||||
bottomNavigationView.setOnNavigationItemSelectedListener(BottomNavigationView.OnNavigationItemSelectedListener { item ->
|
bottomNavigationView.setOnItemSelectedListener { item ->
|
||||||
val fragment = when (item.itemId) {
|
val fragment = when (item.itemId) {
|
||||||
R.id.help_menu_item -> HelpFragment.newInstance()
|
R.id.help_menu_item -> HelpFragment.newInstance()
|
||||||
R.id.queue_menu_item -> QueueFragment.newInstance()
|
R.id.queue_menu_item -> QueueFragment.newInstance()
|
||||||
else -> return@OnNavigationItemSelectedListener false
|
else -> return@setOnItemSelectedListener false
|
||||||
}
|
}
|
||||||
item.toScreen()?.let(viewModel::onScreenSelected)
|
item.toScreen()?.let(viewModel::onScreenSelected)
|
||||||
supportFragmentManager.beginTransaction()
|
supportFragmentManager.beginTransaction()
|
||||||
.replace(R.id.fragment_holder, fragment)
|
.replace(R.id.fragment_holder, fragment)
|
||||||
.commit()
|
.commit()
|
||||||
return@OnNavigationItemSelectedListener true
|
return@setOnItemSelectedListener true
|
||||||
})
|
}
|
||||||
if (savedInstanceState == null) {
|
if (savedInstanceState == null) {
|
||||||
bottomNavigationView.selectedItemId = R.id.queue_menu_item
|
bottomNavigationView.selectedItemId = R.id.queue_menu_item
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -75,7 +75,7 @@ class QueueService : Service() {
|
||||||
.setSmallIcon(R.drawable.ic_download)
|
.setSmallIcon(R.drawable.ic_download)
|
||||||
.setContentIntent(buildMainPendingIntent(this))
|
.setContentIntent(buildMainPendingIntent(this))
|
||||||
.setAutoCancel(true)
|
.setAutoCancel(true)
|
||||||
.setNotificationSilent()
|
.setSilent(true)
|
||||||
.build()
|
.build()
|
||||||
NotificationState.Finish -> {
|
NotificationState.Finish -> {
|
||||||
stopSelf()
|
stopSelf()
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,6 @@
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="@dimen/default_padding"
|
android:layout_marginTop="@dimen/default_padding"
|
||||||
app:autoPlay="false"
|
app:autoPlay="false"
|
||||||
app:showFullScreenButton="false"
|
|
||||||
app:enableAutomaticInitialization="true"
|
app:enableAutomaticInitialization="true"
|
||||||
app:handleNetworkEvents="true"
|
app:handleNetworkEvents="true"
|
||||||
app:videoId="NXv3JpmwA8Y" />
|
app:videoId="NXv3JpmwA8Y" />
|
||||||
|
|
@ -82,7 +81,6 @@
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="@dimen/default_padding"
|
android:layout_marginTop="@dimen/default_padding"
|
||||||
app:autoPlay="false"
|
app:autoPlay="false"
|
||||||
app:showFullScreenButton="false"
|
|
||||||
app:enableAutomaticInitialization="true"
|
app:enableAutomaticInitialization="true"
|
||||||
app:handleNetworkEvents="true"
|
app:handleNetworkEvents="true"
|
||||||
app:videoId="jxaxffE8c4c" />
|
app:videoId="jxaxffE8c4c" />
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,22 @@
|
||||||
package org.fnives.tiktokdownloader.data.local
|
package org.fnives.tiktokdownloader.data.local
|
||||||
|
|
||||||
import com.nhaarman.mockitokotlin2.spy
|
|
||||||
import org.fnives.tiktokdownloader.data.local.persistent.SharedPreferencesManager
|
import org.fnives.tiktokdownloader.data.local.persistent.SharedPreferencesManager
|
||||||
import org.fnives.tiktokdownloader.helper.mock.InMemorySharedPreferencesManager
|
import org.fnives.tiktokdownloader.helper.mock.InMemorySharedPreferencesManager
|
||||||
import org.junit.jupiter.api.Assertions
|
import org.junit.jupiter.api.Assertions
|
||||||
import org.junit.jupiter.api.BeforeEach
|
import org.junit.jupiter.api.BeforeEach
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
|
import org.junit.jupiter.api.Timeout
|
||||||
|
import org.mockito.kotlin.spy
|
||||||
|
|
||||||
@Suppress("TestFunctionName")
|
@Suppress("TestFunctionName")
|
||||||
|
@Timeout(value = 2)
|
||||||
class CaptchaTimeoutLocalSourceTest {
|
class CaptchaTimeoutLocalSourceTest {
|
||||||
|
|
||||||
private lateinit var mockSharedPreferencesManager: SharedPreferencesManager
|
private lateinit var mockSharedPreferencesManager: SharedPreferencesManager
|
||||||
private lateinit var sut: CaptchaTimeoutLocalSource
|
private lateinit var sut: CaptchaTimeoutLocalSource
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
fun setup(){
|
fun setup() {
|
||||||
mockSharedPreferencesManager = spy(InMemorySharedPreferencesManager())
|
mockSharedPreferencesManager = spy(InMemorySharedPreferencesManager())
|
||||||
sut = CaptchaTimeoutLocalSource(mockSharedPreferencesManager, 60)
|
sut = CaptchaTimeoutLocalSource(mockSharedPreferencesManager, 60)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,9 @@
|
||||||
package org.fnives.tiktokdownloader.data.local
|
package org.fnives.tiktokdownloader.data.local
|
||||||
|
|
||||||
import com.nhaarman.mockitokotlin2.anyOrNull
|
|
||||||
import com.nhaarman.mockitokotlin2.doReturn
|
|
||||||
import com.nhaarman.mockitokotlin2.mock
|
|
||||||
import com.nhaarman.mockitokotlin2.verifyZeroInteractions
|
|
||||||
import com.nhaarman.mockitokotlin2.whenever
|
|
||||||
import kotlinx.coroutines.CancellationException
|
import kotlinx.coroutines.CancellationException
|
||||||
import kotlinx.coroutines.TimeoutCancellationException
|
import kotlinx.coroutines.TimeoutCancellationException
|
||||||
import kotlinx.coroutines.async
|
import kotlinx.coroutines.async
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.flow.collect
|
|
||||||
import kotlinx.coroutines.flow.first
|
import kotlinx.coroutines.flow.first
|
||||||
import kotlinx.coroutines.flow.take
|
import kotlinx.coroutines.flow.take
|
||||||
import kotlinx.coroutines.flow.toList
|
import kotlinx.coroutines.flow.toList
|
||||||
|
|
@ -26,9 +20,16 @@ import org.fnives.tiktokdownloader.helper.mock.InMemorySharedPreferencesManager
|
||||||
import org.junit.jupiter.api.Assertions
|
import org.junit.jupiter.api.Assertions
|
||||||
import org.junit.jupiter.api.BeforeEach
|
import org.junit.jupiter.api.BeforeEach
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
|
import org.junit.jupiter.api.Timeout
|
||||||
|
import org.mockito.kotlin.anyOrNull
|
||||||
|
import org.mockito.kotlin.doReturn
|
||||||
|
import org.mockito.kotlin.mock
|
||||||
|
import org.mockito.kotlin.verifyNoInteractions
|
||||||
|
import org.mockito.kotlin.whenever
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
|
|
||||||
@Suppress("TestFunctionName")
|
|
||||||
|
@Timeout(value = 2)
|
||||||
class VideoDownloadedLocalSourceTest {
|
class VideoDownloadedLocalSourceTest {
|
||||||
|
|
||||||
private lateinit var sut: VideoDownloadedLocalSource
|
private lateinit var sut: VideoDownloadedLocalSource
|
||||||
|
|
@ -52,8 +53,8 @@ class VideoDownloadedLocalSourceTest {
|
||||||
@Test
|
@Test
|
||||||
fun GIVEN_observing_saved_videos_WHEN_initialized_THEN_emptylist_is_emitted() = runBlocking<Unit> {
|
fun GIVEN_observing_saved_videos_WHEN_initialized_THEN_emptylist_is_emitted() = runBlocking<Unit> {
|
||||||
Assertions.assertEquals(emptyList<VideoDownloaded>(), sut.savedVideos.first())
|
Assertions.assertEquals(emptyList<VideoDownloaded>(), sut.savedVideos.first())
|
||||||
verifyZeroInteractions(mockSaveVideoFile)
|
verifyNoInteractions(mockSaveVideoFile)
|
||||||
verifyZeroInteractions(mockVerifyFileForUriExists)
|
verifyNoInteractions(mockVerifyFileForUriExists)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,10 @@ import org.fnives.tiktokdownloader.data.model.VideoInPending
|
||||||
import org.junit.jupiter.api.Assertions
|
import org.junit.jupiter.api.Assertions
|
||||||
import org.junit.jupiter.api.BeforeEach
|
import org.junit.jupiter.api.BeforeEach
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
|
import org.junit.jupiter.api.Timeout
|
||||||
|
|
||||||
@Suppress("TestFunctionName")
|
|
||||||
|
@Timeout(value = 2)
|
||||||
class VideoInPendingLocalSourceTest {
|
class VideoInPendingLocalSourceTest {
|
||||||
|
|
||||||
private lateinit var sut: VideoInPendingLocalSource
|
private lateinit var sut: VideoInPendingLocalSource
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,10 @@ import org.fnives.tiktokdownloader.data.model.VideoInProgress
|
||||||
import org.junit.jupiter.api.Assertions
|
import org.junit.jupiter.api.Assertions
|
||||||
import org.junit.jupiter.api.BeforeEach
|
import org.junit.jupiter.api.BeforeEach
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
|
import org.junit.jupiter.api.Timeout
|
||||||
|
|
||||||
@Suppress("TestFunctionName")
|
|
||||||
|
@Timeout(value = 2)
|
||||||
class VideoInProgressLocalSourceTest {
|
class VideoInProgressLocalSourceTest {
|
||||||
|
|
||||||
private lateinit var sut: VideoInProgressLocalSource
|
private lateinit var sut: VideoInProgressLocalSource
|
||||||
|
|
|
||||||
|
|
@ -14,12 +14,14 @@ import org.junit.jupiter.api.AfterEach
|
||||||
import org.junit.jupiter.api.Assertions
|
import org.junit.jupiter.api.Assertions
|
||||||
import org.junit.jupiter.api.BeforeEach
|
import org.junit.jupiter.api.BeforeEach
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
|
import org.junit.jupiter.api.Timeout
|
||||||
import org.junit.jupiter.params.ParameterizedTest
|
import org.junit.jupiter.params.ParameterizedTest
|
||||||
import org.junit.jupiter.params.provider.Arguments
|
import org.junit.jupiter.params.provider.Arguments
|
||||||
import org.junit.jupiter.params.provider.MethodSource
|
import org.junit.jupiter.params.provider.MethodSource
|
||||||
import java.util.stream.Stream
|
import java.util.stream.Stream
|
||||||
|
|
||||||
@Suppress("TestFunctionName")
|
|
||||||
|
@Timeout(value = 2)
|
||||||
class TikTokDownloadRemoteSourceTest {
|
class TikTokDownloadRemoteSourceTest {
|
||||||
|
|
||||||
private lateinit var mockWebServer: MockWebServer
|
private lateinit var mockWebServer: MockWebServer
|
||||||
|
|
@ -126,14 +128,19 @@ class TikTokDownloadRemoteSourceTest {
|
||||||
mockWebServer.enqueue(MockResponse().setResponseCode(200).setHeader("Content-Type", "video/mp4").setBody("banan"))
|
mockWebServer.enqueue(MockResponse().setResponseCode(200).setHeader("Content-Type", "video/mp4").setBody("banan"))
|
||||||
val videoInPending = VideoInPending("alma", TEST_URL)
|
val videoInPending = VideoInPending("alma", TEST_URL)
|
||||||
|
|
||||||
|
|
||||||
val response = sut.getVideo(videoInPending)
|
val response = sut.getVideo(videoInPending)
|
||||||
Assertions.assertEquals(expectedId, response.id)
|
Assertions.assertEquals(expectedId, response.id)
|
||||||
Assertions.assertEquals(expectedUrl, response.url)
|
Assertions.assertEquals(expectedUrl, response.url)
|
||||||
Assertions.assertEquals(expectedContentType, response.contentType)
|
Assertions.assertEquals(expectedContentType, response.contentType)
|
||||||
Assertions.assertEquals("banan", response.byteStream.reader().readText())
|
Assertions.assertEquals("banan", response.byteStream.reader().readText())
|
||||||
|
|
||||||
|
mockWebServer.takeRequest()
|
||||||
|
mockWebServer.takeRequest()
|
||||||
|
val videoRequest = mockWebServer.takeRequest()
|
||||||
|
Assertions.assertEquals("http://localhost:8080/?a=a&b=b&c=c", videoRequest.requestUrl?.toUri()?.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun GIVEN_proper_responses_as_variant2_THEN_parsed_properly() = runBlocking<Unit> {
|
fun GIVEN_proper_responses_as_variant2_THEN_parsed_properly() = runBlocking<Unit> {
|
||||||
val expectedId = "e-alma"
|
val expectedId = "e-alma"
|
||||||
|
|
@ -152,6 +159,10 @@ class TikTokDownloadRemoteSourceTest {
|
||||||
Assertions.assertEquals(expectedUrl, response.url)
|
Assertions.assertEquals(expectedUrl, response.url)
|
||||||
Assertions.assertEquals(expectedContentType, response.contentType)
|
Assertions.assertEquals(expectedContentType, response.contentType)
|
||||||
Assertions.assertEquals("a-banan", response.byteStream.reader().readText())
|
Assertions.assertEquals("a-banan", response.byteStream.reader().readText())
|
||||||
|
mockWebServer.takeRequest()
|
||||||
|
mockWebServer.takeRequest()
|
||||||
|
val videoRequest = mockWebServer.takeRequest()
|
||||||
|
Assertions.assertEquals("http://localhost:8080/?a=a&b=b&c=c", videoRequest.requestUrl?.toUri()?.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -257,31 +268,34 @@ class TikTokDownloadRemoteSourceTest {
|
||||||
private const val MAIN_PAGE_VARIANT_2_RESPONSE = "response/main_page_v1.html"
|
private const val MAIN_PAGE_VARIANT_2_RESPONSE = "response/main_page_v1.html"
|
||||||
private const val PORT = 8080
|
private const val PORT = 8080
|
||||||
private const val TEST_URL = "http://127.0.0.1:$PORT"
|
private const val TEST_URL = "http://127.0.0.1:$PORT"
|
||||||
|
private const val SHORTENED_TEST_URL = "http://127.0.0.1:$PORT"
|
||||||
|
private const val VIDEO_FILE_TEST_URL = "http:\\u002F/127.0.0.1:$PORT?a=a\\u0026b=b&c=c"
|
||||||
|
private const val CAPTCHA_TEST_URL = "http://127.0.0.1:$PORT"
|
||||||
|
|
||||||
private fun Any.readResourceFileShortenedUrlResponse() =
|
private fun Any.readResourceFileShortenedUrlResponse() =
|
||||||
readResourceFile(SHORTENED_URL_RESPONSE)
|
readResourceFile(SHORTENED_URL_RESPONSE)
|
||||||
.replace("https://www.tiktok.com/@ieclauuu/video/6887614455967010049", TEST_URL)
|
.replace("https://www.tiktok.com/@ieclauuu/video/6887614455967010049", SHORTENED_TEST_URL)
|
||||||
|
|
||||||
private fun Any.readResourceFileMainPageVariant1Response() =
|
private fun Any.readResourceFileMainPageVariant1Response() =
|
||||||
readResourceFile(MAIN_PAGE_VARIANT_1_RESPONSE)
|
readResourceFile(MAIN_PAGE_VARIANT_1_RESPONSE)
|
||||||
.replace(
|
.replace(
|
||||||
"https://v16-web.tiktok.com/video/tos/alisg/tos-alisg-pve-0037c001/9ddfc12f43b04f6596f9953c9a9ca072/?a=1988\\u0026br=1534\\u0026bt=767\\u0026cr=0\\u0026cs=0\\u0026cv=1\\u0026dr=0\\u0026ds=3\\u0026er=\\u0026expire=1603682739\\u0026l=20201025212533010189074225590A080D\\u0026lr=tiktok_m\\u0026mime_type=video_mp4\\u0026policy=2\\u0026qs=0\\u0026rc=amlxbmV1O291eDMzMzczM0ApZDRoZDQ3Nzw1N2U5Nzs3O2dicW1vL2AxZV5fLS1iMTRzczA2Y2NgYTQ2LmE1Y2E0My46Yw%3D%3D\\u0026signature=cce079fd02e4dde94c1c93cfdbd1d100\\u0026tk=tt_webid_v2\\u0026vl=\\u0026vr=",
|
"https://v16-web.tiktok.com/video/tos/alisg/tos-alisg-pve-0037c001/9ddfc12f43b04f6596f9953c9a9ca072/?a=1988\\u0026br=1534\\u0026bt=767\\u0026cr=0\\u0026cs=0\\u0026cv=1\\u0026dr=0\\u0026ds=3\\u0026er=\\u0026expire=1603682739\\u0026l=20201025212533010189074225590A080D\\u0026lr=tiktok_m\\u0026mime_type=video_mp4\\u0026policy=2\\u0026qs=0\\u0026rc=amlxbmV1O291eDMzMzczM0ApZDRoZDQ3Nzw1N2U5Nzs3O2dicW1vL2AxZV5fLS1iMTRzczA2Y2NgYTQ2LmE1Y2E0My46Yw%3D%3D\\u0026signature=cce079fd02e4dde94c1c93cfdbd1d100\\u0026tk=tt_webid_v2\\u0026vl=\\u0026vr=",
|
||||||
TEST_URL
|
VIDEO_FILE_TEST_URL
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun Any.readResourceFileMainPageVariant2Response() =
|
private fun Any.readResourceFileMainPageVariant2Response() =
|
||||||
readResourceFile(MAIN_PAGE_VARIANT_2_RESPONSE)
|
readResourceFile(MAIN_PAGE_VARIANT_2_RESPONSE)
|
||||||
.replace(
|
.replace(
|
||||||
"https://v16-web.tiktok.com/video/tos/alisg/tos-alisg-pve-0037c001/9ddfc12f43b04f6596f9953c9a9ca072/?a=1988\\u0026br=1534\\u0026bt=767\\u0026cr=0\\u0026cs=0\\u0026cv=1\\u0026dr=0\\u0026ds=3\\u0026er=\\u0026expire=1603682739\\u0026l=20201025212533010189074225590A080D\\u0026lr=tiktok_m\\u0026mime_type=video_mp4\\u0026policy=2\\u0026qs=0\\u0026rc=amlxbmV1O291eDMzMzczM0ApZDRoZDQ3Nzw1N2U5Nzs3O2dicW1vL2AxZV5fLS1iMTRzczA2Y2NgYTQ2LmE1Y2E0My46Yw%3D%3D\\u0026signature=cce079fd02e4dde94c1c93cfdbd1d100\\u0026tk=tt_webid_v2\\u0026vl=\\u0026vr=",
|
"https://v16-web.tiktok.com/video/tos/alisg/tos-alisg-pve-0037c001/9ddfc12f43b04f6596f9953c9a9ca072/?a=1988\\u0026br=1534\\u0026bt=767\\u0026cr=0\\u0026cs=0\\u0026cv=1\\u0026dr=0\\u0026ds=3\\u0026er=\\u0026expire=1603682739\\u0026l=20201025212533010189074225590A080D\\u0026lr=tiktok_m\\u0026mime_type=video_mp4\\u0026policy=2\\u0026qs=0\\u0026rc=amlxbmV1O291eDMzMzczM0ApZDRoZDQ3Nzw1N2U5Nzs3O2dicW1vL2AxZV5fLS1iMTRzczA2Y2NgYTQ2LmE1Y2E0My46Yw%3D%3D\\u0026signature=cce079fd02e4dde94c1c93cfdbd1d100\\u0026tk=tt_webid_v2\\u0026vl=\\u0026vr=",
|
||||||
TEST_URL
|
VIDEO_FILE_TEST_URL
|
||||||
)
|
)
|
||||||
private fun Any.readCaptchaOneResponse() =
|
private fun Any.readCaptchaOneResponse() =
|
||||||
readResourceFile(CAPTCHA_REQUIRED_RESPONSE_ONE)
|
readResourceFile(CAPTCHA_REQUIRED_RESPONSE_ONE)
|
||||||
.replace("https://www.tiktok.com/@ieclauuu/video/6887614455967010049", TEST_URL)
|
.replace("https://www.tiktok.com/@ieclauuu/video/6887614455967010049", CAPTCHA_TEST_URL)
|
||||||
|
|
||||||
private fun Any.readCaptchaTwoResponse() =
|
private fun Any.readCaptchaTwoResponse() =
|
||||||
readResourceFile(CAPTCHA_REQUIRED_RESPONSE_TWO)
|
readResourceFile(CAPTCHA_REQUIRED_RESPONSE_TWO)
|
||||||
.replace("https://www.tiktok.com/@ieclauuu/video/6887614455967010049", TEST_URL)
|
.replace("https://www.tiktok.com/@ieclauuu/video/6887614455967010049", CAPTCHA_TEST_URL)
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
private fun captchaResponses() = Stream.of(
|
private fun captchaResponses() = Stream.of(
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import org.junit.jupiter.api.Assertions
|
||||||
import org.junit.jupiter.api.BeforeEach
|
import org.junit.jupiter.api.BeforeEach
|
||||||
import org.junit.jupiter.api.Disabled
|
import org.junit.jupiter.api.Disabled
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
|
import org.junit.jupiter.api.Timeout
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -16,7 +17,8 @@ import java.io.File
|
||||||
* Since the website may change anytime without any notice, this test verifies with actual request going out.
|
* Since the website may change anytime without any notice, this test verifies with actual request going out.
|
||||||
* However this makes the test shaky, because if the device has no proper connection it may fail.
|
* However this makes the test shaky, because if the device has no proper connection it may fail.
|
||||||
*/
|
*/
|
||||||
@Suppress("TestFunctionName")
|
|
||||||
|
@Timeout(value = 2)
|
||||||
class TikTokDownloadRemoteSourceUpToDateTest {
|
class TikTokDownloadRemoteSourceUpToDateTest {
|
||||||
|
|
||||||
private lateinit var sut: TikTokDownloadRemoteSource
|
private lateinit var sut: TikTokDownloadRemoteSource
|
||||||
|
|
@ -27,6 +29,7 @@ class TikTokDownloadRemoteSourceUpToDateTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Disabled("Can trigger captcha, so only run it separately")
|
@Disabled("Can trigger captcha, so only run it separately")
|
||||||
|
@Timeout(value = 120)
|
||||||
@Test
|
@Test
|
||||||
fun GIVEN_actualVideo_WHEN_downloading_THEN_the_file_matching_with_the_previously_loaded_video() {
|
fun GIVEN_actualVideo_WHEN_downloading_THEN_the_file_matching_with_the_previously_loaded_video() {
|
||||||
val parameter = VideoInPending("123", SUBJECT_VIDEO_URL)
|
val parameter = VideoInPending("123", SUBJECT_VIDEO_URL)
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,22 @@
|
||||||
package org.fnives.tiktokdownloader.data.usecase
|
package org.fnives.tiktokdownloader.data.usecase
|
||||||
|
|
||||||
import com.nhaarman.mockitokotlin2.anyOrNull
|
|
||||||
import com.nhaarman.mockitokotlin2.doReturn
|
|
||||||
import com.nhaarman.mockitokotlin2.mock
|
|
||||||
import com.nhaarman.mockitokotlin2.times
|
|
||||||
import com.nhaarman.mockitokotlin2.verify
|
|
||||||
import com.nhaarman.mockitokotlin2.verifyNoMoreInteractions
|
|
||||||
import com.nhaarman.mockitokotlin2.verifyZeroInteractions
|
|
||||||
import com.nhaarman.mockitokotlin2.whenever
|
|
||||||
import org.fnives.tiktokdownloader.data.local.VideoInPendingLocalSource
|
import org.fnives.tiktokdownloader.data.local.VideoInPendingLocalSource
|
||||||
import org.fnives.tiktokdownloader.data.model.VideoInPending
|
import org.fnives.tiktokdownloader.data.model.VideoInPending
|
||||||
import org.junit.jupiter.api.Assertions
|
import org.junit.jupiter.api.Assertions
|
||||||
import org.junit.jupiter.api.BeforeEach
|
import org.junit.jupiter.api.BeforeEach
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
|
import org.junit.jupiter.api.Timeout
|
||||||
|
import org.mockito.kotlin.anyOrNull
|
||||||
|
import org.mockito.kotlin.doReturn
|
||||||
|
import org.mockito.kotlin.mock
|
||||||
|
import org.mockito.kotlin.times
|
||||||
|
import org.mockito.kotlin.verify
|
||||||
|
import org.mockito.kotlin.verifyNoInteractions
|
||||||
|
import org.mockito.kotlin.verifyNoMoreInteractions
|
||||||
|
import org.mockito.kotlin.whenever
|
||||||
|
|
||||||
@Suppress("TestFunctionName")
|
@Suppress("TestFunctionName")
|
||||||
|
@Timeout(value = 2)
|
||||||
class AddVideoToQueueUseCaseTest {
|
class AddVideoToQueueUseCaseTest {
|
||||||
|
|
||||||
private lateinit var sut: AddVideoToQueueUseCase
|
private lateinit var sut: AddVideoToQueueUseCase
|
||||||
|
|
@ -30,8 +32,8 @@ class AddVideoToQueueUseCaseTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun GIVEN_no_action_THEN_the_local_source_and_verifier_is_not_touched() {
|
fun GIVEN_no_action_THEN_the_local_source_and_verifier_is_not_touched() {
|
||||||
verifyZeroInteractions(mockUrlVerificationUseCase)
|
verifyNoInteractions(mockUrlVerificationUseCase)
|
||||||
verifyZeroInteractions(mockVideoInPendingLocalSource)
|
verifyNoInteractions(mockVideoInPendingLocalSource)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -83,6 +85,6 @@ class AddVideoToQueueUseCaseTest {
|
||||||
Assertions.assertFalse(actual, "Url is Saved while it should NOT be")
|
Assertions.assertFalse(actual, "Url is Saved while it should NOT be")
|
||||||
verify(mockUrlVerificationUseCase, times(1)).invoke(expectedUrl)
|
verify(mockUrlVerificationUseCase, times(1)).invoke(expectedUrl)
|
||||||
verifyNoMoreInteractions(mockUrlVerificationUseCase)
|
verifyNoMoreInteractions(mockUrlVerificationUseCase)
|
||||||
verifyZeroInteractions(mockVideoInPendingLocalSource)
|
verifyNoInteractions(mockVideoInPendingLocalSource)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,19 +1,15 @@
|
||||||
package org.fnives.tiktokdownloader.data.usecase
|
package org.fnives.tiktokdownloader.data.usecase
|
||||||
|
|
||||||
import com.nhaarman.mockitokotlin2.doReturn
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
import com.nhaarman.mockitokotlin2.mock
|
|
||||||
import com.nhaarman.mockitokotlin2.times
|
|
||||||
import com.nhaarman.mockitokotlin2.verify
|
|
||||||
import com.nhaarman.mockitokotlin2.verifyNoMoreInteractions
|
|
||||||
import com.nhaarman.mockitokotlin2.verifyZeroInteractions
|
|
||||||
import com.nhaarman.mockitokotlin2.whenever
|
|
||||||
import kotlinx.coroutines.async
|
import kotlinx.coroutines.async
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.first
|
import kotlinx.coroutines.flow.first
|
||||||
import kotlinx.coroutines.flow.take
|
import kotlinx.coroutines.flow.take
|
||||||
import kotlinx.coroutines.flow.toList
|
import kotlinx.coroutines.flow.toList
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import kotlinx.coroutines.test.TestCoroutineDispatcher
|
import kotlinx.coroutines.test.StandardTestDispatcher
|
||||||
|
import kotlinx.coroutines.test.TestDispatcher
|
||||||
|
|
||||||
import org.fnives.tiktokdownloader.data.local.VideoDownloadedLocalSource
|
import org.fnives.tiktokdownloader.data.local.VideoDownloadedLocalSource
|
||||||
import org.fnives.tiktokdownloader.data.local.VideoInPendingLocalSource
|
import org.fnives.tiktokdownloader.data.local.VideoInPendingLocalSource
|
||||||
import org.fnives.tiktokdownloader.data.local.VideoInProgressLocalSource
|
import org.fnives.tiktokdownloader.data.local.VideoInProgressLocalSource
|
||||||
|
|
@ -21,14 +17,26 @@ import org.fnives.tiktokdownloader.data.model.VideoDownloaded
|
||||||
import org.fnives.tiktokdownloader.data.model.VideoInPending
|
import org.fnives.tiktokdownloader.data.model.VideoInPending
|
||||||
import org.fnives.tiktokdownloader.data.model.VideoInProgress
|
import org.fnives.tiktokdownloader.data.model.VideoInProgress
|
||||||
import org.fnives.tiktokdownloader.data.model.VideoState
|
import org.fnives.tiktokdownloader.data.model.VideoState
|
||||||
|
import org.fnives.tiktokdownloader.helper.advanceTimeBy
|
||||||
|
import org.fnives.tiktokdownloader.helper.advanceUntilIdle
|
||||||
import org.junit.jupiter.api.Assertions
|
import org.junit.jupiter.api.Assertions
|
||||||
import org.junit.jupiter.api.BeforeEach
|
import org.junit.jupiter.api.BeforeEach
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
|
import org.junit.jupiter.api.Timeout
|
||||||
|
import org.mockito.kotlin.doReturn
|
||||||
|
import org.mockito.kotlin.mock
|
||||||
|
import org.mockito.kotlin.times
|
||||||
|
import org.mockito.kotlin.verify
|
||||||
|
import org.mockito.kotlin.verifyNoInteractions
|
||||||
|
import org.mockito.kotlin.verifyNoMoreInteractions
|
||||||
|
import org.mockito.kotlin.whenever
|
||||||
|
|
||||||
@Suppress("TestFunctionName")
|
@Suppress("TestFunctionName")
|
||||||
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
|
@Timeout(value = 2)
|
||||||
class StateOfVideosObservableUseCaseTest {
|
class StateOfVideosObservableUseCaseTest {
|
||||||
|
|
||||||
private lateinit var testDispatcher: TestCoroutineDispatcher
|
private lateinit var testDispatcher: TestDispatcher
|
||||||
private lateinit var mockVideoInProgressLocalSource: VideoInProgressLocalSource
|
private lateinit var mockVideoInProgressLocalSource: VideoInProgressLocalSource
|
||||||
private lateinit var mockVideoInPendingLocalSource: VideoInPendingLocalSource
|
private lateinit var mockVideoInPendingLocalSource: VideoInPendingLocalSource
|
||||||
private lateinit var mockVideoDownloadedLocalSource: VideoDownloadedLocalSource
|
private lateinit var mockVideoDownloadedLocalSource: VideoDownloadedLocalSource
|
||||||
|
|
@ -48,7 +56,7 @@ class StateOfVideosObservableUseCaseTest {
|
||||||
whenever(mockVideoInProgressLocalSource.videoInProcessFlow).doReturn(videoInProgressMutableFlow)
|
whenever(mockVideoInProgressLocalSource.videoInProcessFlow).doReturn(videoInProgressMutableFlow)
|
||||||
whenever(mockVideoInPendingLocalSource.pendingVideos).doReturn(videoInPendingMutableFlow)
|
whenever(mockVideoInPendingLocalSource.pendingVideos).doReturn(videoInPendingMutableFlow)
|
||||||
whenever(mockVideoDownloadedLocalSource.savedVideos).doReturn(videoDownloadedMutableFlow)
|
whenever(mockVideoDownloadedLocalSource.savedVideos).doReturn(videoDownloadedMutableFlow)
|
||||||
testDispatcher = TestCoroutineDispatcher()
|
testDispatcher = StandardTestDispatcher()
|
||||||
sut = StateOfVideosObservableUseCase(
|
sut = StateOfVideosObservableUseCase(
|
||||||
videoInProgressLocalSource = mockVideoInProgressLocalSource,
|
videoInProgressLocalSource = mockVideoInProgressLocalSource,
|
||||||
videoInPendingLocalSource = mockVideoInPendingLocalSource,
|
videoInPendingLocalSource = mockVideoInPendingLocalSource,
|
||||||
|
|
@ -59,13 +67,13 @@ class StateOfVideosObservableUseCaseTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun WHEN_no_invoke_is_called_THEN_no_dependency_is_called() {
|
fun WHEN_no_invoke_is_called_THEN_no_dependency_is_called() {
|
||||||
verifyZeroInteractions(mockVideoDownloadedLocalSource)
|
verifyNoInteractions(mockVideoDownloadedLocalSource)
|
||||||
verifyZeroInteractions(mockVideoInPendingLocalSource)
|
verifyNoInteractions(mockVideoInPendingLocalSource)
|
||||||
verifyZeroInteractions(mockVideoInProgressLocalSource)
|
verifyNoInteractions(mockVideoInProgressLocalSource)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun GIVEN_no_inProgress_AND_empty_pending_AND_empty_saved_THEN_emptyList_is_emitted() = runBlocking(testDispatcher) {
|
fun GIVEN_no_inProgress_AND_empty_pending_AND_empty_saved_THEN_emptyList_is_emitted() = runBlocking {
|
||||||
videoInProgressMutableFlow.value = null
|
videoInProgressMutableFlow.value = null
|
||||||
videoInPendingMutableFlow.value = emptyList()
|
videoInPendingMutableFlow.value = emptyList()
|
||||||
videoDownloadedMutableFlow.value = emptyList()
|
videoDownloadedMutableFlow.value = emptyList()
|
||||||
|
|
@ -83,7 +91,7 @@ class StateOfVideosObservableUseCaseTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun GIVEN_inProgress_AND_empty_pending_AND_empty_saved_THEN_inProgress_is_emitted() = runBlocking(testDispatcher) {
|
fun GIVEN_inProgress_AND_empty_pending_AND_empty_saved_THEN_inProgress_is_emitted() = runBlocking {
|
||||||
val videoInProgress = VideoInProgress("alma", "url")
|
val videoInProgress = VideoInProgress("alma", "url")
|
||||||
val expected = listOf<VideoState>(VideoState.InProcess(videoInProgress))
|
val expected = listOf<VideoState>(VideoState.InProcess(videoInProgress))
|
||||||
val expectedList = listOf(emptyList(), expected)
|
val expectedList = listOf(emptyList(), expected)
|
||||||
|
|
@ -109,7 +117,7 @@ class StateOfVideosObservableUseCaseTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun GIVEN_inProgress_AND_pendingWithSameId_AND_empty_saved_THEN_inProgress_is_emitted() = runBlocking(testDispatcher) {
|
fun GIVEN_inProgress_AND_pendingWithSameId_AND_empty_saved_THEN_inProgress_is_emitted() = runBlocking {
|
||||||
val videoInProgress = VideoInProgress("alma", "url")
|
val videoInProgress = VideoInProgress("alma", "url")
|
||||||
val videoInPending = VideoInPending(id = videoInProgress.id, url = videoInProgress.url)
|
val videoInPending = VideoInPending(id = videoInProgress.id, url = videoInProgress.url)
|
||||||
val expected = listOf<VideoState>(VideoState.InProcess(videoInProgress))
|
val expected = listOf<VideoState>(VideoState.InProcess(videoInProgress))
|
||||||
|
|
@ -130,7 +138,7 @@ class StateOfVideosObservableUseCaseTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun GIVEN_in_pending_AND_nothing_inprogress_AND_empty_saved_THEN_inPending_is_emitted() = runBlocking(testDispatcher) {
|
fun GIVEN_in_pending_AND_nothing_inprogress_AND_empty_saved_THEN_inPending_is_emitted() = runBlocking {
|
||||||
val videoInPending = VideoInPending(id = "alma", url = "url")
|
val videoInPending = VideoInPending(id = "alma", url = "url")
|
||||||
val expected = listOf<VideoState>(VideoState.InPending(videoInPending))
|
val expected = listOf<VideoState>(VideoState.InPending(videoInPending))
|
||||||
val expectedList = listOf(emptyList(), expected)
|
val expectedList = listOf(emptyList(), expected)
|
||||||
|
|
@ -151,7 +159,7 @@ class StateOfVideosObservableUseCaseTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun GIVEN_inProgress_AND_pendingWithSameId_AND_savedWithSameId_THEN_inProgress_And_saved_is_emitted() =
|
fun GIVEN_inProgress_AND_pendingWithSameId_AND_savedWithSameId_THEN_inProgress_And_saved_is_emitted() =
|
||||||
runBlocking(testDispatcher) {
|
runBlocking {
|
||||||
val videoInProgress = VideoInProgress("alma", "url")
|
val videoInProgress = VideoInProgress("alma", "url")
|
||||||
val videoInPending = VideoInPending(id = videoInProgress.id, url = videoInProgress.url)
|
val videoInPending = VideoInPending(id = videoInProgress.id, url = videoInProgress.url)
|
||||||
val videoDownloaded = VideoDownloaded(id = videoInProgress.id, url = videoInProgress.url, uri = "uri")
|
val videoDownloaded = VideoDownloaded(id = videoInProgress.id, url = videoInProgress.url, uri = "uri")
|
||||||
|
|
@ -173,7 +181,7 @@ class StateOfVideosObservableUseCaseTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun GIVEN_new_item_faster_than_debounce_THEN_only_the_last_items_are_emitted() = runBlocking(testDispatcher) {
|
fun GIVEN_new_item_faster_than_debounce_THEN_only_the_last_items_are_emitted() = runBlocking {
|
||||||
val videoInProgress = VideoInProgress("alma", "url")
|
val videoInProgress = VideoInProgress("alma", "url")
|
||||||
val expected = listOf(VideoState.InProcess(videoInProgress))
|
val expected = listOf(VideoState.InProcess(videoInProgress))
|
||||||
val expectedList = listOf(expected)
|
val expectedList = listOf(expected)
|
||||||
|
|
@ -194,7 +202,7 @@ class StateOfVideosObservableUseCaseTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun GIVEN_new_item_slower_than_debounce_THEN_both_list_is_emitted() = runBlocking(testDispatcher) {
|
fun GIVEN_new_item_slower_than_debounce_THEN_both_list_is_emitted() = runBlocking {
|
||||||
val videoInProgress = VideoInProgress("alma", "url")
|
val videoInProgress = VideoInProgress("alma", "url")
|
||||||
val expected = listOf(VideoState.InProcess(videoInProgress))
|
val expected = listOf(VideoState.InProcess(videoInProgress))
|
||||||
val expectedList = listOf(emptyList(), expected)
|
val expectedList = listOf(emptyList(), expected)
|
||||||
|
|
@ -215,7 +223,7 @@ class StateOfVideosObservableUseCaseTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun GIVEN_processing_LATER_pendingWithSameId_THEN_no_new_emition_happens() = runBlocking(testDispatcher) {
|
fun GIVEN_processing_LATER_pendingWithSameId_THEN_no_new_emition_happens() = runBlocking {
|
||||||
val videoInProgress = VideoInProgress("alma", "url")
|
val videoInProgress = VideoInProgress("alma", "url")
|
||||||
val videoInPendingSameAsProgress = VideoInPending(id = videoInProgress.id, url = videoInProgress.url)
|
val videoInPendingSameAsProgress = VideoInPending(id = videoInProgress.id, url = videoInProgress.url)
|
||||||
val videoInPendingOther = VideoInPending(id = "alma2", url = "url2")
|
val videoInPendingOther = VideoInPending(id = "alma2", url = "url2")
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,10 @@ package org.fnives.tiktokdownloader.data.usecase
|
||||||
import org.junit.jupiter.api.Assertions
|
import org.junit.jupiter.api.Assertions
|
||||||
import org.junit.jupiter.api.BeforeEach
|
import org.junit.jupiter.api.BeforeEach
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
|
import org.junit.jupiter.api.Timeout
|
||||||
|
|
||||||
@Suppress("TestFunctionName")
|
|
||||||
|
@Timeout(value = 2)
|
||||||
class UrlVerificationUseCaseTest {
|
class UrlVerificationUseCaseTest {
|
||||||
|
|
||||||
private lateinit var sut: UrlVerificationUseCase
|
private lateinit var sut: UrlVerificationUseCase
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,15 @@
|
||||||
package org.fnives.tiktokdownloader.data.usecase
|
package org.fnives.tiktokdownloader.data.usecase
|
||||||
|
|
||||||
import com.nhaarman.mockitokotlin2.anyOrNull
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
import com.nhaarman.mockitokotlin2.doReturn
|
|
||||||
import com.nhaarman.mockitokotlin2.mock
|
|
||||||
import com.nhaarman.mockitokotlin2.times
|
|
||||||
import com.nhaarman.mockitokotlin2.verify
|
|
||||||
import com.nhaarman.mockitokotlin2.verifyNoMoreInteractions
|
|
||||||
import com.nhaarman.mockitokotlin2.verifyZeroInteractions
|
|
||||||
import com.nhaarman.mockitokotlin2.whenever
|
|
||||||
import kotlinx.coroutines.async
|
import kotlinx.coroutines.async
|
||||||
import kotlinx.coroutines.cancelAndJoin
|
import kotlinx.coroutines.cancelAndJoin
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.take
|
import kotlinx.coroutines.flow.take
|
||||||
import kotlinx.coroutines.flow.toList
|
import kotlinx.coroutines.flow.toList
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import kotlinx.coroutines.test.TestCoroutineDispatcher
|
import kotlinx.coroutines.test.StandardTestDispatcher
|
||||||
import kotlinx.coroutines.test.runBlockingTest
|
import kotlinx.coroutines.test.TestDispatcher
|
||||||
|
import kotlinx.coroutines.test.runTest
|
||||||
import org.fnives.tiktokdownloader.data.local.CaptchaTimeoutLocalSource
|
import org.fnives.tiktokdownloader.data.local.CaptchaTimeoutLocalSource
|
||||||
import org.fnives.tiktokdownloader.data.local.VideoDownloadedLocalSource
|
import org.fnives.tiktokdownloader.data.local.VideoDownloadedLocalSource
|
||||||
import org.fnives.tiktokdownloader.data.local.VideoInPendingLocalSource
|
import org.fnives.tiktokdownloader.data.local.VideoInPendingLocalSource
|
||||||
|
|
@ -30,15 +24,27 @@ import org.fnives.tiktokdownloader.data.network.TikTokDownloadRemoteSource
|
||||||
import org.fnives.tiktokdownloader.data.network.exceptions.CaptchaRequiredException
|
import org.fnives.tiktokdownloader.data.network.exceptions.CaptchaRequiredException
|
||||||
import org.fnives.tiktokdownloader.data.network.exceptions.NetworkException
|
import org.fnives.tiktokdownloader.data.network.exceptions.NetworkException
|
||||||
import org.fnives.tiktokdownloader.data.network.exceptions.ParsingException
|
import org.fnives.tiktokdownloader.data.network.exceptions.ParsingException
|
||||||
|
import org.fnives.tiktokdownloader.helper.advanceTimeBy
|
||||||
|
import org.fnives.tiktokdownloader.helper.advanceUntilIdle
|
||||||
import org.junit.jupiter.api.Assertions
|
import org.junit.jupiter.api.Assertions
|
||||||
import org.junit.jupiter.api.BeforeEach
|
import org.junit.jupiter.api.BeforeEach
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
|
import org.mockito.kotlin.anyOrNull
|
||||||
|
import org.mockito.kotlin.doReturn
|
||||||
|
import org.mockito.kotlin.mock
|
||||||
|
import org.mockito.kotlin.times
|
||||||
|
import org.mockito.kotlin.verify
|
||||||
|
import org.mockito.kotlin.verifyNoInteractions
|
||||||
|
import org.mockito.kotlin.verifyNoMoreInteractions
|
||||||
|
import org.mockito.kotlin.whenever
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
|
|
||||||
@Suppress("TestFunctionName")
|
@Suppress("TestFunctionName")
|
||||||
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
|
//@Timeout(value = 2)
|
||||||
class VideoDownloadingProcessorUseCaseTest {
|
class VideoDownloadingProcessorUseCaseTest {
|
||||||
|
|
||||||
private lateinit var testDispatcher: TestCoroutineDispatcher
|
private lateinit var testDispatcher: TestDispatcher
|
||||||
private lateinit var mockVideoInProgressLocalSource: VideoInProgressLocalSource
|
private lateinit var mockVideoInProgressLocalSource: VideoInProgressLocalSource
|
||||||
private lateinit var mockVideoInPendingLocalSource: VideoInPendingLocalSource
|
private lateinit var mockVideoInPendingLocalSource: VideoInPendingLocalSource
|
||||||
private lateinit var mockVideoDownloadedLocalSource: VideoDownloadedLocalSource
|
private lateinit var mockVideoDownloadedLocalSource: VideoDownloadedLocalSource
|
||||||
|
|
@ -62,7 +68,7 @@ class VideoDownloadingProcessorUseCaseTest {
|
||||||
whenever(mockVideoInProgressLocalSource.videoInProcessFlow).doReturn(videoInProgressMutableFlow)
|
whenever(mockVideoInProgressLocalSource.videoInProcessFlow).doReturn(videoInProgressMutableFlow)
|
||||||
whenever(mockVideoInPendingLocalSource.pendingVideos).doReturn(videoInPendingMutableFlow)
|
whenever(mockVideoInPendingLocalSource.pendingVideos).doReturn(videoInPendingMutableFlow)
|
||||||
whenever(mockVideoDownloadedLocalSource.savedVideos).doReturn(videoDownloadedMutableFlow)
|
whenever(mockVideoDownloadedLocalSource.savedVideos).doReturn(videoDownloadedMutableFlow)
|
||||||
testDispatcher = TestCoroutineDispatcher()
|
testDispatcher = StandardTestDispatcher()
|
||||||
sut = VideoDownloadingProcessorUseCase(
|
sut = VideoDownloadingProcessorUseCase(
|
||||||
videoInProgressLocalSource = mockVideoInProgressLocalSource,
|
videoInProgressLocalSource = mockVideoInProgressLocalSource,
|
||||||
videoInPendingLocalSource = mockVideoInPendingLocalSource,
|
videoInPendingLocalSource = mockVideoInPendingLocalSource,
|
||||||
|
|
@ -75,24 +81,24 @@ class VideoDownloadingProcessorUseCaseTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun WHEN_no_method_invoked_THEN_no_interaction_with_dependencies() {
|
fun WHEN_no_method_invoked_THEN_no_interaction_with_dependencies() {
|
||||||
verifyZeroInteractions(mockVideoInProgressLocalSource)
|
verifyNoInteractions(mockVideoInProgressLocalSource)
|
||||||
verifyZeroInteractions(mockVideoInPendingLocalSource)
|
verifyNoInteractions(mockVideoInPendingLocalSource)
|
||||||
verifyZeroInteractions(mockVideoDownloadedLocalSource)
|
verifyNoInteractions(mockVideoDownloadedLocalSource)
|
||||||
verifyZeroInteractions(mockTikTokDownloadRemoteSource)
|
verifyNoInteractions(mockTikTokDownloadRemoteSource)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun GIVEN_not_observing_WHEN_fetching_THEN_nothing_happens() {
|
fun GIVEN_not_observing_WHEN_fetching_THEN_nothing_happens() {
|
||||||
sut.fetchVideoInState()
|
sut.fetchVideoInState()
|
||||||
|
|
||||||
verifyZeroInteractions(mockVideoInProgressLocalSource)
|
verifyNoInteractions(mockVideoInProgressLocalSource)
|
||||||
verifyZeroInteractions(mockVideoInPendingLocalSource)
|
verifyNoInteractions(mockVideoInPendingLocalSource)
|
||||||
verifyZeroInteractions(mockVideoDownloadedLocalSource)
|
verifyNoInteractions(mockVideoDownloadedLocalSource)
|
||||||
verifyZeroInteractions(mockTikTokDownloadRemoteSource)
|
verifyNoInteractions(mockTikTokDownloadRemoteSource)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun GIVEN_empty_pendingVideos_WHEN_observing_THEN_error_is_emited() = runBlocking(testDispatcher) {
|
fun GIVEN_empty_pendingVideos_WHEN_observing_THEN_error_is_emited() = runBlocking {
|
||||||
videoInPendingMutableFlow.value = emptyList()
|
videoInPendingMutableFlow.value = emptyList()
|
||||||
val expected = ProcessState.Finished
|
val expected = ProcessState.Finished
|
||||||
val expectedList = listOf(expected)
|
val expectedList = listOf(expected)
|
||||||
|
|
@ -103,13 +109,12 @@ class VideoDownloadingProcessorUseCaseTest {
|
||||||
Assertions.assertEquals(expectedList, resultList.await())
|
Assertions.assertEquals(expectedList, resultList.await())
|
||||||
verify(mockVideoInPendingLocalSource, times(1)).pendingVideos
|
verify(mockVideoInPendingLocalSource, times(1)).pendingVideos
|
||||||
verifyNoMoreInteractions(mockVideoInPendingLocalSource)
|
verifyNoMoreInteractions(mockVideoInPendingLocalSource)
|
||||||
verifyZeroInteractions(mockVideoInPendingLocalSource)
|
verifyNoInteractions(mockVideoDownloadedLocalSource)
|
||||||
verifyZeroInteractions(mockVideoDownloadedLocalSource)
|
verifyNoInteractions(mockTikTokDownloadRemoteSource)
|
||||||
verifyZeroInteractions(mockTikTokDownloadRemoteSource)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun GIVEN_one_pending_video_AND_network_error_WHEN_observing_THEN_error_is_emited() = runBlocking(testDispatcher) {
|
fun GIVEN_one_pending_video_AND_network_error_WHEN_observing_THEN_error_is_emited() = runBlocking {
|
||||||
val videoInPending = VideoInPending("alma", "banan")
|
val videoInPending = VideoInPending("alma", "banan")
|
||||||
videoInPendingMutableFlow.value = listOf(videoInPending)
|
videoInPendingMutableFlow.value = listOf(videoInPending)
|
||||||
whenever(mockTikTokDownloadRemoteSource.getVideo(videoInPending)).then { throw NetworkException() }
|
whenever(mockTikTokDownloadRemoteSource.getVideo(videoInPending)).then { throw NetworkException() }
|
||||||
|
|
@ -126,11 +131,10 @@ class VideoDownloadingProcessorUseCaseTest {
|
||||||
verify(mockVideoDownloadedLocalSource, times(1)).savedVideos
|
verify(mockVideoDownloadedLocalSource, times(1)).savedVideos
|
||||||
verifyNoMoreInteractions(mockTikTokDownloadRemoteSource)
|
verifyNoMoreInteractions(mockTikTokDownloadRemoteSource)
|
||||||
verifyNoMoreInteractions(mockVideoDownloadedLocalSource)
|
verifyNoMoreInteractions(mockVideoDownloadedLocalSource)
|
||||||
verifyZeroInteractions(mockVideoInPendingLocalSource)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun GIVEN_one_pending_video_AND_parsing_error_WHEN_observing_THEN_parsingError_is_emited() = runBlocking(testDispatcher) {
|
fun GIVEN_one_pending_video_AND_parsing_error_WHEN_observing_THEN_parsingError_is_emited() = runBlocking {
|
||||||
val videoInPending = VideoInPending("alma", "banan")
|
val videoInPending = VideoInPending("alma", "banan")
|
||||||
videoInPendingMutableFlow.value = listOf(videoInPending)
|
videoInPendingMutableFlow.value = listOf(videoInPending)
|
||||||
whenever(mockTikTokDownloadRemoteSource.getVideo(videoInPending)).then { throw ParsingException() }
|
whenever(mockTikTokDownloadRemoteSource.getVideo(videoInPending)).then { throw ParsingException() }
|
||||||
|
|
@ -144,7 +148,7 @@ class VideoDownloadingProcessorUseCaseTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun GIVEN_one_pending_video_AND_unexpected_error_WHEN_observing_THEN_unknown_error_is_emitted() = runBlockingTest(testDispatcher) {
|
fun GIVEN_one_pending_video_AND_unexpected_error_WHEN_observing_THEN_unknown_error_is_emitted() = runTest(testDispatcher) {
|
||||||
val videoInPending = VideoInPending("alma", "banan")
|
val videoInPending = VideoInPending("alma", "banan")
|
||||||
videoInPendingMutableFlow.value = listOf(videoInPending)
|
videoInPendingMutableFlow.value = listOf(videoInPending)
|
||||||
whenever(mockTikTokDownloadRemoteSource.getVideo(videoInPending)).then { throw Throwable() }
|
whenever(mockTikTokDownloadRemoteSource.getVideo(videoInPending)).then { throw Throwable() }
|
||||||
|
|
@ -158,7 +162,7 @@ class VideoDownloadingProcessorUseCaseTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun GIVEN_one_pending_video_AND_network_errors_WHILE_observing_WHEN_fetching_THEN_it_retries() = runBlocking<Unit>(testDispatcher) {
|
fun GIVEN_one_pending_video_AND_network_errors_WHILE_observing_WHEN_fetching_THEN_it_retries() = runBlocking<Unit> {
|
||||||
val videoInPending = VideoInPending("alma", "banan")
|
val videoInPending = VideoInPending("alma", "banan")
|
||||||
videoInPendingMutableFlow.value = listOf(videoInPending)
|
videoInPendingMutableFlow.value = listOf(videoInPending)
|
||||||
var specificException = true
|
var specificException = true
|
||||||
|
|
@ -177,7 +181,7 @@ class VideoDownloadingProcessorUseCaseTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun GIVEN_one_pending_video_AND_parsing_errors_WHILE_observing_WHEN_fetching_THEN_it_retries() = runBlocking<Unit>(testDispatcher) {
|
fun GIVEN_one_pending_video_AND_parsing_errors_WHILE_observing_WHEN_fetching_THEN_it_retries() = runBlocking<Unit> {
|
||||||
val videoInPending = VideoInPending("alma", "banan")
|
val videoInPending = VideoInPending("alma", "banan")
|
||||||
videoInPendingMutableFlow.value = listOf(videoInPending)
|
videoInPendingMutableFlow.value = listOf(videoInPending)
|
||||||
var specificException = true
|
var specificException = true
|
||||||
|
|
@ -196,7 +200,7 @@ class VideoDownloadingProcessorUseCaseTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun GIVEN_one_pending_video_AND_unknown_errors_WHILE_observing_WHEN_fetching_THEN_it_retries() = runBlocking<Unit>(testDispatcher) {
|
fun GIVEN_one_pending_video_AND_unknown_errors_WHILE_observing_WHEN_fetching_THEN_it_retries() = runBlocking<Unit> {
|
||||||
val videoInPending = VideoInPending("alma", "banan")
|
val videoInPending = VideoInPending("alma", "banan")
|
||||||
videoInPendingMutableFlow.value = listOf(videoInPending)
|
videoInPendingMutableFlow.value = listOf(videoInPending)
|
||||||
var specificException = true
|
var specificException = true
|
||||||
|
|
@ -216,7 +220,7 @@ class VideoDownloadingProcessorUseCaseTest {
|
||||||
|
|
||||||
// verify that fetching even while request is running doesn't matter, only after error is emitted
|
// verify that fetching even while request is running doesn't matter, only after error is emitted
|
||||||
@Test
|
@Test
|
||||||
fun GIVEN_one_pending_video_AND_delaying_until_fetch_WHILE_observing_WHEN_fetching_THEN_emition_happens_only_once() = runBlocking<Unit>(testDispatcher) {
|
fun GIVEN_one_pending_video_AND_delaying_until_fetch_WHILE_observing_WHEN_fetching_THEN_emition_happens_only_once() = runBlocking<Unit> {
|
||||||
val videoInPending = VideoInPending("alma", "banan")
|
val videoInPending = VideoInPending("alma", "banan")
|
||||||
videoInPendingMutableFlow.value = listOf(videoInPending)
|
videoInPendingMutableFlow.value = listOf(videoInPending)
|
||||||
var specificException = true
|
var specificException = true
|
||||||
|
|
@ -242,7 +246,7 @@ class VideoDownloadingProcessorUseCaseTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun GIVEN_one_pending_video_AND_failing_request_WHEN_observing_THEN_video_is_marked_processing_then_unprocessing() = runBlocking<Unit>(testDispatcher) {
|
fun GIVEN_one_pending_video_AND_failing_request_WHEN_observing_THEN_video_is_marked_processing_then_unprocessing() = runBlocking<Unit> {
|
||||||
val videoInPending = VideoInPending("alma", "banan")
|
val videoInPending = VideoInPending("alma", "banan")
|
||||||
videoInPendingMutableFlow.value = listOf(videoInPending)
|
videoInPendingMutableFlow.value = listOf(videoInPending)
|
||||||
whenever(mockTikTokDownloadRemoteSource.getVideo(videoInPending)).then {
|
whenever(mockTikTokDownloadRemoteSource.getVideo(videoInPending)).then {
|
||||||
|
|
@ -261,10 +265,10 @@ class VideoDownloadingProcessorUseCaseTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun GIVEN_one_pending_video_AND_successful_request_AND_storage_error_WHEN_observing_THEN_video_is_saved_called_and_error_is_propogated() = runBlocking(testDispatcher) {
|
fun GIVEN_one_pending_video_AND_successful_request_AND_storage_error_WHEN_observing_THEN_video_is_saved_called_and_error_is_propogated() = runBlocking {
|
||||||
val videoInPending = VideoInPending("alma", "banan")
|
val videoInPending = VideoInPending("alma", "banan")
|
||||||
videoInPendingMutableFlow.value = listOf(videoInPending)
|
videoInPendingMutableFlow.value = listOf(videoInPending)
|
||||||
val videoInSavingIntoFile = VideoInSavingIntoFile("x","u",VideoInSavingIntoFile.ContentType("a","b"), FalseInputStream())
|
val videoInSavingIntoFile = VideoInSavingIntoFile("x", "u", VideoInSavingIntoFile.ContentType("a", "b"), FalseInputStream())
|
||||||
whenever(mockTikTokDownloadRemoteSource.getVideo(videoInPending)).doReturn(videoInSavingIntoFile)
|
whenever(mockTikTokDownloadRemoteSource.getVideo(videoInPending)).doReturn(videoInSavingIntoFile)
|
||||||
whenever(mockVideoDownloadedLocalSource.saveVideo(anyOrNull())).then {
|
whenever(mockVideoDownloadedLocalSource.saveVideo(anyOrNull())).then {
|
||||||
throw StorageException()
|
throw StorageException()
|
||||||
|
|
@ -282,10 +286,10 @@ class VideoDownloadingProcessorUseCaseTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun GIVEN_one_pending_video_AND_successful_request_AND_unexpected_error_WHEN_observing_THEN_video_is_saved_called_and_error_is_propogated() = runBlocking(testDispatcher) {
|
fun GIVEN_one_pending_video_AND_successful_request_AND_unexpected_error_WHEN_observing_THEN_video_is_saved_called_and_error_is_propogated() = runBlocking {
|
||||||
val videoInPending = VideoInPending("alma", "banan")
|
val videoInPending = VideoInPending("alma", "banan")
|
||||||
videoInPendingMutableFlow.value = listOf(videoInPending)
|
videoInPendingMutableFlow.value = listOf(videoInPending)
|
||||||
val videoInSavingIntoFile = VideoInSavingIntoFile("x","u",VideoInSavingIntoFile.ContentType("a","b"), FalseInputStream())
|
val videoInSavingIntoFile = VideoInSavingIntoFile("x", "u", VideoInSavingIntoFile.ContentType("a", "b"), FalseInputStream())
|
||||||
whenever(mockTikTokDownloadRemoteSource.getVideo(videoInPending)).doReturn(videoInSavingIntoFile)
|
whenever(mockTikTokDownloadRemoteSource.getVideo(videoInPending)).doReturn(videoInSavingIntoFile)
|
||||||
whenever(mockVideoDownloadedLocalSource.saveVideo(anyOrNull())).then {
|
whenever(mockVideoDownloadedLocalSource.saveVideo(anyOrNull())).then {
|
||||||
throw Throwable()
|
throw Throwable()
|
||||||
|
|
@ -303,11 +307,11 @@ class VideoDownloadingProcessorUseCaseTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun GIVEN_one_pending_video_AND_successful_request_AND_successful_file_save_WHEN_observing_THEN_pending_is_removed() = runBlocking(testDispatcher) {
|
fun GIVEN_one_pending_video_AND_successful_request_AND_successful_file_save_WHEN_observing_THEN_pending_is_removed() = runBlocking {
|
||||||
val videoInPending = VideoInPending("alma", "banan")
|
val videoInPending = VideoInPending("alma", "banan")
|
||||||
val videoDownloaded = VideoDownloaded("zz","yy","xx")
|
val videoDownloaded = VideoDownloaded("zz", "yy", "xx")
|
||||||
videoInPendingMutableFlow.value = listOf(videoInPending)
|
videoInPendingMutableFlow.value = listOf(videoInPending)
|
||||||
val videoInSavingIntoFile = VideoInSavingIntoFile("x","u",VideoInSavingIntoFile.ContentType("a","b"), FalseInputStream())
|
val videoInSavingIntoFile = VideoInSavingIntoFile("x", "u", VideoInSavingIntoFile.ContentType("a", "b"), FalseInputStream())
|
||||||
whenever(mockTikTokDownloadRemoteSource.getVideo(videoInPending)).doReturn(videoInSavingIntoFile)
|
whenever(mockTikTokDownloadRemoteSource.getVideo(videoInPending)).doReturn(videoInSavingIntoFile)
|
||||||
whenever(mockVideoDownloadedLocalSource.saveVideo(anyOrNull())).doReturn(videoDownloaded)
|
whenever(mockVideoDownloadedLocalSource.saveVideo(anyOrNull())).doReturn(videoDownloaded)
|
||||||
val expectedList = listOf(
|
val expectedList = listOf(
|
||||||
|
|
@ -325,7 +329,7 @@ class VideoDownloadingProcessorUseCaseTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun GIVEN_one_pending_video_AND_captcha_timeout_WHEN_observing_THEN_captcha_timeout_is_emited() = runBlocking(testDispatcher) {
|
fun GIVEN_one_pending_video_AND_captcha_timeout_WHEN_observing_THEN_captcha_timeout_is_emited() = runBlocking {
|
||||||
val videoInPending = VideoInPending("alma", "banan")
|
val videoInPending = VideoInPending("alma", "banan")
|
||||||
videoInPendingMutableFlow.value = listOf(videoInPending)
|
videoInPendingMutableFlow.value = listOf(videoInPending)
|
||||||
whenever(mockCaptchaTimeoutLocalSource.isInCaptchaTimeout()).doReturn(true)
|
whenever(mockCaptchaTimeoutLocalSource.isInCaptchaTimeout()).doReturn(true)
|
||||||
|
|
@ -344,33 +348,34 @@ class VideoDownloadingProcessorUseCaseTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun GIVEN_one_pending_video_AND_successful_request_AND_successful_file_save_WHEN_observing_with_2_THEN_pending_is_removed_AND_only_once_executed() = runBlocking(testDispatcher) {
|
fun GIVEN_one_pending_video_AND_successful_request_AND_successful_file_save_WHEN_observing_with_2_THEN_pending_is_removed_AND_only_once_executed() =
|
||||||
val videoInPending = VideoInPending("alma", "banan")
|
runBlocking {
|
||||||
val videoDownloaded = VideoDownloaded("zz","yy","xx")
|
val videoInPending = VideoInPending("alma", "banan")
|
||||||
videoInPendingMutableFlow.value = listOf(videoInPending)
|
val videoDownloaded = VideoDownloaded("zz", "yy", "xx")
|
||||||
val videoInSavingIntoFile = VideoInSavingIntoFile("x","u",VideoInSavingIntoFile.ContentType("a","b"), FalseInputStream())
|
videoInPendingMutableFlow.value = listOf(videoInPending)
|
||||||
whenever(mockTikTokDownloadRemoteSource.getVideo(videoInPending)).doReturn(videoInSavingIntoFile)
|
val videoInSavingIntoFile = VideoInSavingIntoFile("x", "u", VideoInSavingIntoFile.ContentType("a", "b"), FalseInputStream())
|
||||||
whenever(mockVideoDownloadedLocalSource.saveVideo(anyOrNull())).doReturn(videoDownloaded)
|
whenever(mockTikTokDownloadRemoteSource.getVideo(videoInPending)).doReturn(videoInSavingIntoFile)
|
||||||
val expectedList = listOf(
|
whenever(mockVideoDownloadedLocalSource.saveVideo(anyOrNull())).doReturn(videoDownloaded)
|
||||||
ProcessState.Processing(videoInPending),
|
val expectedList = listOf(
|
||||||
ProcessState.Processed(videoDownloaded)
|
ProcessState.Processing(videoInPending),
|
||||||
)
|
ProcessState.Processed(videoDownloaded)
|
||||||
|
)
|
||||||
|
|
||||||
val resultList1 = async(testDispatcher) { sut.processState.take(2).toList() }
|
val resultList1 = async(testDispatcher) { sut.processState.take(2).toList() }
|
||||||
val resultList2 = async(testDispatcher) { sut.processState.take(2).toList() }
|
val resultList2 = async(testDispatcher) { sut.processState.take(2).toList() }
|
||||||
testDispatcher.advanceUntilIdle()
|
testDispatcher.advanceUntilIdle()
|
||||||
|
|
||||||
Assertions.assertEquals(expectedList, resultList1.await())
|
Assertions.assertEquals(expectedList, resultList1.await())
|
||||||
Assertions.assertEquals(expectedList, resultList2.await())
|
Assertions.assertEquals(expectedList, resultList2.await())
|
||||||
verify(mockVideoInPendingLocalSource, times(1)).removeVideoFromQueue(videoInPending)
|
verify(mockVideoInPendingLocalSource, times(1)).removeVideoFromQueue(videoInPending)
|
||||||
verify(mockVideoInPendingLocalSource, times(1)).pendingVideos
|
verify(mockVideoInPendingLocalSource, times(1)).pendingVideos
|
||||||
verifyNoMoreInteractions(mockVideoInPendingLocalSource)
|
verifyNoMoreInteractions(mockVideoInPendingLocalSource)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun GIVEN_one_pending_video_BUT_already_downloaded_WHEN_observing_THEN_processed_is_emitted_but_no_request_call() = runBlocking(testDispatcher) {
|
fun GIVEN_one_pending_video_BUT_already_downloaded_WHEN_observing_THEN_processed_is_emitted_but_no_request_call() = runBlocking {
|
||||||
val videoInPending = VideoInPending("alma", "banan")
|
val videoInPending = VideoInPending("alma", "banan")
|
||||||
val videoDownloaded = VideoDownloaded("alma","banan","xx")
|
val videoDownloaded = VideoDownloaded("alma", "banan", "xx")
|
||||||
videoInPendingMutableFlow.value = listOf(videoInPending)
|
videoInPendingMutableFlow.value = listOf(videoInPending)
|
||||||
videoDownloadedMutableFlow.value = listOf(videoDownloaded)
|
videoDownloadedMutableFlow.value = listOf(videoDownloaded)
|
||||||
val expectedList = listOf(
|
val expectedList = listOf(
|
||||||
|
|
@ -389,13 +394,13 @@ class VideoDownloadingProcessorUseCaseTest {
|
||||||
verifyNoMoreInteractions(mockVideoInPendingLocalSource)
|
verifyNoMoreInteractions(mockVideoInPendingLocalSource)
|
||||||
verifyNoMoreInteractions(mockVideoDownloadedLocalSource)
|
verifyNoMoreInteractions(mockVideoDownloadedLocalSource)
|
||||||
verifyNoMoreInteractions(mockVideoInProgressLocalSource)
|
verifyNoMoreInteractions(mockVideoInProgressLocalSource)
|
||||||
verifyZeroInteractions(mockTikTokDownloadRemoteSource)
|
verifyNoInteractions(mockTikTokDownloadRemoteSource)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun GIVEN_one_pending_video_BUT_already_downloaded_AND_captcha_timeout_WHEN_observing_THEN_processed_is_emitted_but_no_request_call() = runBlocking(testDispatcher) {
|
fun GIVEN_one_pending_video_BUT_already_downloaded_AND_captcha_timeout_WHEN_observing_THEN_processed_is_emitted_but_no_request_call() = runBlocking {
|
||||||
val videoInPending = VideoInPending("alma", "banan")
|
val videoInPending = VideoInPending("alma", "banan")
|
||||||
val videoDownloaded = VideoDownloaded("alma","banan","xx")
|
val videoDownloaded = VideoDownloaded("alma", "banan", "xx")
|
||||||
videoInPendingMutableFlow.value = listOf(videoInPending)
|
videoInPendingMutableFlow.value = listOf(videoInPending)
|
||||||
videoDownloadedMutableFlow.value = listOf(videoDownloaded)
|
videoDownloadedMutableFlow.value = listOf(videoDownloaded)
|
||||||
whenever(mockCaptchaTimeoutLocalSource.isInCaptchaTimeout()).doReturn(true)
|
whenever(mockCaptchaTimeoutLocalSource.isInCaptchaTimeout()).doReturn(true)
|
||||||
|
|
@ -415,11 +420,11 @@ class VideoDownloadingProcessorUseCaseTest {
|
||||||
verifyNoMoreInteractions(mockVideoInPendingLocalSource)
|
verifyNoMoreInteractions(mockVideoInPendingLocalSource)
|
||||||
verifyNoMoreInteractions(mockVideoDownloadedLocalSource)
|
verifyNoMoreInteractions(mockVideoDownloadedLocalSource)
|
||||||
verifyNoMoreInteractions(mockVideoInProgressLocalSource)
|
verifyNoMoreInteractions(mockVideoInProgressLocalSource)
|
||||||
verifyZeroInteractions(mockTikTokDownloadRemoteSource)
|
verifyNoInteractions(mockTikTokDownloadRemoteSource)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun GIVEN_one_pending_video_BUT_CaptchaTimeoutException_WHEN_observing_THEN_its_saved_and_captchaError_emitted() = runBlocking(testDispatcher) {
|
fun GIVEN_one_pending_video_BUT_CaptchaTimeoutException_WHEN_observing_THEN_its_saved_and_captchaError_emitted() = runBlocking {
|
||||||
val videoInPending = VideoInPending("alma", "banan")
|
val videoInPending = VideoInPending("alma", "banan")
|
||||||
videoInPendingMutableFlow.value = listOf(videoInPending)
|
videoInPendingMutableFlow.value = listOf(videoInPending)
|
||||||
videoDownloadedMutableFlow.value = listOf()
|
videoDownloadedMutableFlow.value = listOf()
|
||||||
|
|
@ -451,7 +456,7 @@ class VideoDownloadingProcessorUseCaseTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun GIVEN_one_pending_video_AND_not_advancing_enough_WHILE_observing_WHEN_fetching_THEN_nothing_is_called() = runBlocking<Unit>(testDispatcher) {
|
fun GIVEN_one_pending_video_AND_not_advancing_enough_WHILE_observing_WHEN_fetching_THEN_nothing_is_called() = runBlocking<Unit> {
|
||||||
val videoInPending = VideoInPending("alma", "banan")
|
val videoInPending = VideoInPending("alma", "banan")
|
||||||
videoInPendingMutableFlow.value = listOf(videoInPending)
|
videoInPendingMutableFlow.value = listOf(videoInPending)
|
||||||
whenever(mockTikTokDownloadRemoteSource.getVideo(videoInPending)).then { throw NetworkException() }
|
whenever(mockTikTokDownloadRemoteSource.getVideo(videoInPending)).then { throw NetworkException() }
|
||||||
|
|
@ -459,21 +464,23 @@ class VideoDownloadingProcessorUseCaseTest {
|
||||||
val resultList = async(testDispatcher) { sut.processState.take(2).toList() }
|
val resultList = async(testDispatcher) { sut.processState.take(2).toList() }
|
||||||
testDispatcher.advanceTimeBy(199)
|
testDispatcher.advanceTimeBy(199)
|
||||||
|
|
||||||
verifyZeroInteractions(mockTikTokDownloadRemoteSource)
|
verifyNoInteractions(mockTikTokDownloadRemoteSource)
|
||||||
|
testDispatcher.advanceUntilIdle()
|
||||||
resultList.cancelAndJoin()
|
resultList.cancelAndJoin()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun GIVEN_one_pending_video_AND_but_advancing_enough_WHILE_observing_WHEN_fetching_THEN_nothing_is_called() = runBlocking<Unit>(testDispatcher) {
|
fun GIVEN_one_pending_video_AND_but_advancing_enough_WHILE_observing_WHEN_fetching_THEN_nothing_is_called() = runBlocking<Unit> {
|
||||||
val videoInPending = VideoInPending("alma", "banan")
|
val videoInPending = VideoInPending("alma", "banan")
|
||||||
videoInPendingMutableFlow.value = listOf(videoInPending)
|
videoInPendingMutableFlow.value = listOf(videoInPending)
|
||||||
whenever(mockTikTokDownloadRemoteSource.getVideo(videoInPending)).then { throw NetworkException() }
|
whenever(mockTikTokDownloadRemoteSource.getVideo(videoInPending)).then { throw NetworkException() }
|
||||||
|
|
||||||
val resultList = async(testDispatcher) { sut.processState.take(2).toList() }
|
val resultList = async(testDispatcher) { sut.processState.take(2).toList() }
|
||||||
testDispatcher.advanceTimeBy(200)
|
testDispatcher.advanceTimeBy(201)
|
||||||
|
|
||||||
verify(mockTikTokDownloadRemoteSource, times(1)).getVideo(videoInPending)
|
verify(mockTikTokDownloadRemoteSource, times(1)).getVideo(videoInPending)
|
||||||
verifyNoMoreInteractions(mockTikTokDownloadRemoteSource)
|
verifyNoMoreInteractions(mockTikTokDownloadRemoteSource)
|
||||||
|
testDispatcher.advanceUntilIdle()
|
||||||
resultList.cancelAndJoin()
|
resultList.cancelAndJoin()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,6 @@
|
||||||
package org.fnives.tiktokdownloader.di
|
package org.fnives.tiktokdownloader.di
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import com.nhaarman.mockitokotlin2.anyOrNull
|
|
||||||
import com.nhaarman.mockitokotlin2.doReturn
|
|
||||||
import com.nhaarman.mockitokotlin2.mock
|
|
||||||
import com.nhaarman.mockitokotlin2.whenever
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.test.resetMain
|
import kotlinx.coroutines.test.resetMain
|
||||||
import kotlinx.coroutines.test.setMain
|
import kotlinx.coroutines.test.setMain
|
||||||
|
|
@ -14,10 +10,16 @@ import org.fnives.tiktokdownloader.ui.main.queue.QueueViewModel
|
||||||
import org.junit.jupiter.api.AfterEach
|
import org.junit.jupiter.api.AfterEach
|
||||||
import org.junit.jupiter.api.BeforeEach
|
import org.junit.jupiter.api.BeforeEach
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
|
import org.junit.jupiter.api.Timeout
|
||||||
|
import org.mockito.kotlin.anyOrNull
|
||||||
|
import org.mockito.kotlin.doReturn
|
||||||
|
import org.mockito.kotlin.mock
|
||||||
|
import org.mockito.kotlin.whenever
|
||||||
|
|
||||||
|
@Timeout(value = 2)
|
||||||
class ServiceLocatorTest {
|
class ServiceLocatorTest {
|
||||||
|
|
||||||
private lateinit var mockContext : Context
|
private lateinit var mockContext: Context
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
fun setup() {
|
fun setup() {
|
||||||
|
|
@ -30,7 +32,7 @@ class ServiceLocatorTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@AfterEach
|
@AfterEach
|
||||||
fun tearDown(){
|
fun tearDown() {
|
||||||
Dispatchers.resetMain()
|
Dispatchers.resetMain()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
package org.fnives.tiktokdownloader.helper
|
||||||
|
|
||||||
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
import kotlinx.coroutines.test.TestDispatcher
|
||||||
|
|
||||||
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
|
fun TestDispatcher.advanceUntilIdle() = scheduler.advanceUntilIdle()
|
||||||
|
|
||||||
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
|
fun TestDispatcher.advanceTimeBy(delayTimeMillis: Long) = scheduler.advanceTimeBy(delayTimeMillis)
|
||||||
|
|
@ -3,7 +3,7 @@ package org.fnives.tiktokdownloader.helper.mock
|
||||||
import androidx.lifecycle.Lifecycle
|
import androidx.lifecycle.Lifecycle
|
||||||
import androidx.savedstate.SavedStateRegistry
|
import androidx.savedstate.SavedStateRegistry
|
||||||
import androidx.savedstate.SavedStateRegistryOwner
|
import androidx.savedstate.SavedStateRegistryOwner
|
||||||
import com.nhaarman.mockitokotlin2.mock
|
import org.mockito.kotlin.mock
|
||||||
|
|
||||||
class MockSavedStateRegistryOwner(
|
class MockSavedStateRegistryOwner(
|
||||||
private val lifecycle: Lifecycle = MockLifecycle(),
|
private val lifecycle: Lifecycle = MockLifecycle(),
|
||||||
|
|
|
||||||
|
|
@ -2,14 +2,7 @@ package org.fnives.tiktokdownloader.ui.main
|
||||||
|
|
||||||
import androidx.lifecycle.SavedStateHandle
|
import androidx.lifecycle.SavedStateHandle
|
||||||
import com.jraska.livedata.test
|
import com.jraska.livedata.test
|
||||||
import com.nhaarman.mockitokotlin2.doReturn
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
import com.nhaarman.mockitokotlin2.mock
|
|
||||||
import com.nhaarman.mockitokotlin2.times
|
|
||||||
import com.nhaarman.mockitokotlin2.verify
|
|
||||||
import com.nhaarman.mockitokotlin2.verifyNoMoreInteractions
|
|
||||||
import com.nhaarman.mockitokotlin2.whenever
|
|
||||||
import kotlinx.coroutines.channels.ConflatedBroadcastChannel
|
|
||||||
import kotlinx.coroutines.flow.asFlow
|
|
||||||
import org.fnives.tiktokdownloader.data.model.ProcessState
|
import org.fnives.tiktokdownloader.data.model.ProcessState
|
||||||
import org.fnives.tiktokdownloader.data.model.VideoDownloaded
|
import org.fnives.tiktokdownloader.data.model.VideoDownloaded
|
||||||
import org.fnives.tiktokdownloader.data.model.VideoInPending
|
import org.fnives.tiktokdownloader.data.model.VideoInPending
|
||||||
|
|
@ -24,23 +17,29 @@ import org.junit.jupiter.api.extension.ExtendWith
|
||||||
import org.junit.jupiter.params.ParameterizedTest
|
import org.junit.jupiter.params.ParameterizedTest
|
||||||
import org.junit.jupiter.params.provider.Arguments
|
import org.junit.jupiter.params.provider.Arguments
|
||||||
import org.junit.jupiter.params.provider.MethodSource
|
import org.junit.jupiter.params.provider.MethodSource
|
||||||
|
import org.mockito.kotlin.doReturn
|
||||||
|
import org.mockito.kotlin.mock
|
||||||
|
import org.mockito.kotlin.times
|
||||||
|
import org.mockito.kotlin.verify
|
||||||
|
import org.mockito.kotlin.verifyNoMoreInteractions
|
||||||
|
import org.mockito.kotlin.whenever
|
||||||
import java.util.stream.Stream
|
import java.util.stream.Stream
|
||||||
|
|
||||||
@Suppress("TestFunctionName")
|
@Suppress("TestFunctionName")
|
||||||
@ExtendWith(InstantExecutorExtension::class, MainDispatcherExtension::class)
|
@ExtendWith(InstantExecutorExtension::class, MainDispatcherExtension::class)
|
||||||
class MainViewModelTest {
|
class MainViewModelTest {
|
||||||
|
|
||||||
private lateinit var conflatedBroadcastChannel: ConflatedBroadcastChannel<ProcessState>
|
private lateinit var processStateFlow: MutableSharedFlow<ProcessState>
|
||||||
private lateinit var mockVideoDownloadingProcessorUseCase: VideoDownloadingProcessorUseCase
|
private lateinit var mockVideoDownloadingProcessorUseCase: VideoDownloadingProcessorUseCase
|
||||||
private lateinit var mockAddVideoToQueueUseCase: AddVideoToQueueUseCase
|
private lateinit var mockAddVideoToQueueUseCase: AddVideoToQueueUseCase
|
||||||
private lateinit var sut: MainViewModel
|
private lateinit var sut: MainViewModel
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
fun setup() {
|
fun setup() {
|
||||||
conflatedBroadcastChannel = ConflatedBroadcastChannel()
|
processStateFlow = MutableSharedFlow(replay = 1)
|
||||||
mockVideoDownloadingProcessorUseCase = mock()
|
mockVideoDownloadingProcessorUseCase = mock()
|
||||||
mockAddVideoToQueueUseCase = mock()
|
mockAddVideoToQueueUseCase = mock()
|
||||||
whenever(mockVideoDownloadingProcessorUseCase.processState).doReturn(conflatedBroadcastChannel.asFlow())
|
whenever(mockVideoDownloadingProcessorUseCase.processState).doReturn(processStateFlow)
|
||||||
sut = MainViewModel(mockVideoDownloadingProcessorUseCase, mockAddVideoToQueueUseCase, SavedStateHandle())
|
sut = MainViewModel(mockVideoDownloadingProcessorUseCase, mockAddVideoToQueueUseCase, SavedStateHandle())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -95,7 +94,7 @@ class MainViewModelTest {
|
||||||
|
|
||||||
val testObserver = sut.refreshActionVisibility.test()
|
val testObserver = sut.refreshActionVisibility.test()
|
||||||
|
|
||||||
conflatedBroadcastChannel.offer(processState)
|
processStateFlow.tryEmit(processState)
|
||||||
|
|
||||||
testObserver.assertHistorySize(2).assertValueHistory(false, expected)
|
testObserver.assertHistorySize(2).assertValueHistory(false, expected)
|
||||||
verify(mockVideoDownloadingProcessorUseCase, times(1)).processState
|
verify(mockVideoDownloadingProcessorUseCase, times(1)).processState
|
||||||
|
|
@ -114,7 +113,7 @@ class MainViewModelTest {
|
||||||
|
|
||||||
val testObserver = sut.errorMessage.test()
|
val testObserver = sut.errorMessage.test()
|
||||||
|
|
||||||
conflatedBroadcastChannel.offer(processState)
|
processStateFlow.tryEmit(processState)
|
||||||
|
|
||||||
testObserver.assertHistorySize(2).assertValueHistory(null, expected?.let(::Event))
|
testObserver.assertHistorySize(2).assertValueHistory(null, expected?.let(::Event))
|
||||||
verify(mockVideoDownloadingProcessorUseCase, times(1)).processState
|
verify(mockVideoDownloadingProcessorUseCase, times(1)).processState
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,7 @@
|
||||||
package org.fnives.tiktokdownloader.ui.main.queue
|
package org.fnives.tiktokdownloader.ui.main.queue
|
||||||
|
|
||||||
import com.jraska.livedata.test
|
import com.jraska.livedata.test
|
||||||
import com.nhaarman.mockitokotlin2.doReturn
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
import com.nhaarman.mockitokotlin2.mock
|
|
||||||
import com.nhaarman.mockitokotlin2.times
|
|
||||||
import com.nhaarman.mockitokotlin2.verify
|
|
||||||
import com.nhaarman.mockitokotlin2.verifyNoMoreInteractions
|
|
||||||
import com.nhaarman.mockitokotlin2.verifyZeroInteractions
|
|
||||||
import com.nhaarman.mockitokotlin2.whenever
|
|
||||||
import kotlinx.coroutines.channels.ConflatedBroadcastChannel
|
|
||||||
import kotlinx.coroutines.flow.asFlow
|
|
||||||
import org.fnives.tiktokdownloader.data.model.VideoInPending
|
import org.fnives.tiktokdownloader.data.model.VideoInPending
|
||||||
import org.fnives.tiktokdownloader.data.model.VideoState
|
import org.fnives.tiktokdownloader.data.model.VideoState
|
||||||
import org.fnives.tiktokdownloader.data.usecase.AddVideoToQueueUseCase
|
import org.fnives.tiktokdownloader.data.usecase.AddVideoToQueueUseCase
|
||||||
|
|
@ -21,13 +13,19 @@ import org.fnives.tiktokdownloader.ui.shared.Event
|
||||||
import org.junit.jupiter.api.BeforeEach
|
import org.junit.jupiter.api.BeforeEach
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
import org.junit.jupiter.api.extension.ExtendWith
|
import org.junit.jupiter.api.extension.ExtendWith
|
||||||
import kotlin.math.exp
|
import org.mockito.kotlin.doReturn
|
||||||
|
import org.mockito.kotlin.mock
|
||||||
|
import org.mockito.kotlin.times
|
||||||
|
import org.mockito.kotlin.verify
|
||||||
|
import org.mockito.kotlin.verifyNoInteractions
|
||||||
|
import org.mockito.kotlin.verifyNoMoreInteractions
|
||||||
|
import org.mockito.kotlin.whenever
|
||||||
|
|
||||||
@Suppress("TestFunctionName")
|
@Suppress("TestFunctionName")
|
||||||
@ExtendWith(InstantExecutorExtension::class, MainDispatcherExtension::class)
|
@ExtendWith(InstantExecutorExtension::class, MainDispatcherExtension::class)
|
||||||
class QueueViewModelTest {
|
class QueueViewModelTest {
|
||||||
|
|
||||||
private lateinit var stateOfVideosConflatedBroadcastChannel: ConflatedBroadcastChannel<List<VideoState>>
|
private lateinit var stateOfVideosFlow: MutableSharedFlow<List<VideoState>>
|
||||||
private lateinit var mockStateOfVideosObservableUseCase: StateOfVideosObservableUseCase
|
private lateinit var mockStateOfVideosObservableUseCase: StateOfVideosObservableUseCase
|
||||||
private lateinit var mockAddVideoToQueueUseCase: AddVideoToQueueUseCase
|
private lateinit var mockAddVideoToQueueUseCase: AddVideoToQueueUseCase
|
||||||
private lateinit var mockVideoDownloadingProcessorUseCase: VideoDownloadingProcessorUseCase
|
private lateinit var mockVideoDownloadingProcessorUseCase: VideoDownloadingProcessorUseCase
|
||||||
|
|
@ -35,9 +33,9 @@ class QueueViewModelTest {
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
fun setup() {
|
fun setup() {
|
||||||
stateOfVideosConflatedBroadcastChannel = ConflatedBroadcastChannel()
|
stateOfVideosFlow = MutableSharedFlow(replay = 1)
|
||||||
mockStateOfVideosObservableUseCase = mock()
|
mockStateOfVideosObservableUseCase = mock()
|
||||||
whenever(mockStateOfVideosObservableUseCase.invoke()).doReturn(stateOfVideosConflatedBroadcastChannel.asFlow())
|
whenever(mockStateOfVideosObservableUseCase.invoke()).doReturn(stateOfVideosFlow)
|
||||||
mockAddVideoToQueueUseCase = mock()
|
mockAddVideoToQueueUseCase = mock()
|
||||||
mockVideoDownloadingProcessorUseCase = mock()
|
mockVideoDownloadingProcessorUseCase = mock()
|
||||||
sut = QueueViewModel(mockStateOfVideosObservableUseCase, mockAddVideoToQueueUseCase, mockVideoDownloadingProcessorUseCase)
|
sut = QueueViewModel(mockStateOfVideosObservableUseCase, mockAddVideoToQueueUseCase, mockVideoDownloadingProcessorUseCase)
|
||||||
|
|
@ -48,37 +46,37 @@ class QueueViewModelTest {
|
||||||
sut.downloads.test().assertNoValue()
|
sut.downloads.test().assertNoValue()
|
||||||
verify(mockStateOfVideosObservableUseCase, times(1)).invoke()
|
verify(mockStateOfVideosObservableUseCase, times(1)).invoke()
|
||||||
verifyNoMoreInteractions(mockStateOfVideosObservableUseCase)
|
verifyNoMoreInteractions(mockStateOfVideosObservableUseCase)
|
||||||
verifyZeroInteractions(mockAddVideoToQueueUseCase)
|
verifyNoInteractions(mockAddVideoToQueueUseCase)
|
||||||
verifyZeroInteractions(mockVideoDownloadingProcessorUseCase)
|
verifyNoInteractions(mockVideoDownloadingProcessorUseCase)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun GIVEN_initialized_AND_observing_WHEN_emitting_a_emptyList_THEN_it_is_sent_out() {
|
fun GIVEN_initialized_AND_observing_WHEN_emitting_a_emptyList_THEN_it_is_sent_out() {
|
||||||
val expected = listOf<VideoState>()
|
val expected = listOf<VideoState>()
|
||||||
stateOfVideosConflatedBroadcastChannel.offer(expected)
|
stateOfVideosFlow.tryEmit(expected)
|
||||||
|
|
||||||
sut.downloads.test().assertValue(expected)
|
sut.downloads.test().assertValue(expected)
|
||||||
verify(mockStateOfVideosObservableUseCase, times(1)).invoke()
|
verify(mockStateOfVideosObservableUseCase, times(1)).invoke()
|
||||||
verifyNoMoreInteractions(mockStateOfVideosObservableUseCase)
|
verifyNoMoreInteractions(mockStateOfVideosObservableUseCase)
|
||||||
verifyZeroInteractions(mockAddVideoToQueueUseCase)
|
verifyNoInteractions(mockAddVideoToQueueUseCase)
|
||||||
verifyZeroInteractions(mockVideoDownloadingProcessorUseCase)
|
verifyNoInteractions(mockVideoDownloadingProcessorUseCase)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun GIVEN_initialized_AND_observing_WHEN_emitting_two_list_THEN_both_are_sent_out_in_order() {
|
fun GIVEN_initialized_AND_observing_WHEN_emitting_two_list_THEN_both_are_sent_out_in_order() {
|
||||||
val expected1 = listOf(VideoState.InPending(VideoInPending("a1","b1")))
|
val expected1 = listOf(VideoState.InPending(VideoInPending("a1", "b1")))
|
||||||
val expected2 = listOf(VideoState.InPending(VideoInPending("a2","b2")))
|
val expected2 = listOf(VideoState.InPending(VideoInPending("a2", "b2")))
|
||||||
val testObserver = sut.downloads.test()
|
val testObserver = sut.downloads.test()
|
||||||
|
|
||||||
stateOfVideosConflatedBroadcastChannel.offer(expected1)
|
stateOfVideosFlow.tryEmit(expected1)
|
||||||
stateOfVideosConflatedBroadcastChannel.offer(expected2)
|
stateOfVideosFlow.tryEmit(expected2)
|
||||||
|
|
||||||
testObserver.assertHistorySize(2).assertHasValue()
|
testObserver.assertHistorySize(2).assertHasValue()
|
||||||
.assertValueHistory(expected1, expected2)
|
.assertValueHistory(expected1, expected2)
|
||||||
verify(mockStateOfVideosObservableUseCase, times(1)).invoke()
|
verify(mockStateOfVideosObservableUseCase, times(1)).invoke()
|
||||||
verifyNoMoreInteractions(mockStateOfVideosObservableUseCase)
|
verifyNoMoreInteractions(mockStateOfVideosObservableUseCase)
|
||||||
verifyZeroInteractions(mockAddVideoToQueueUseCase)
|
verifyNoInteractions(mockAddVideoToQueueUseCase)
|
||||||
verifyZeroInteractions(mockVideoDownloadingProcessorUseCase)
|
verifyNoInteractions(mockVideoDownloadingProcessorUseCase)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,7 @@
|
||||||
package org.fnives.tiktokdownloader.ui.service
|
package org.fnives.tiktokdownloader.ui.service
|
||||||
|
|
||||||
import com.jraska.livedata.test
|
import com.jraska.livedata.test
|
||||||
import com.nhaarman.mockitokotlin2.anyOrNull
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
import com.nhaarman.mockitokotlin2.doReturn
|
|
||||||
import com.nhaarman.mockitokotlin2.mock
|
|
||||||
import com.nhaarman.mockitokotlin2.times
|
|
||||||
import com.nhaarman.mockitokotlin2.verify
|
|
||||||
import com.nhaarman.mockitokotlin2.verifyNoMoreInteractions
|
|
||||||
import com.nhaarman.mockitokotlin2.verifyZeroInteractions
|
|
||||||
import com.nhaarman.mockitokotlin2.whenever
|
|
||||||
import kotlinx.coroutines.channels.ConflatedBroadcastChannel
|
|
||||||
import kotlinx.coroutines.flow.asFlow
|
|
||||||
import org.fnives.tiktokdownloader.R
|
import org.fnives.tiktokdownloader.R
|
||||||
import org.fnives.tiktokdownloader.data.model.ProcessState
|
import org.fnives.tiktokdownloader.data.model.ProcessState
|
||||||
import org.fnives.tiktokdownloader.data.model.VideoDownloaded
|
import org.fnives.tiktokdownloader.data.model.VideoDownloaded
|
||||||
|
|
@ -25,22 +16,30 @@ import org.junit.jupiter.api.extension.ExtendWith
|
||||||
import org.junit.jupiter.params.ParameterizedTest
|
import org.junit.jupiter.params.ParameterizedTest
|
||||||
import org.junit.jupiter.params.provider.Arguments
|
import org.junit.jupiter.params.provider.Arguments
|
||||||
import org.junit.jupiter.params.provider.MethodSource
|
import org.junit.jupiter.params.provider.MethodSource
|
||||||
|
import org.mockito.kotlin.anyOrNull
|
||||||
|
import org.mockito.kotlin.doReturn
|
||||||
|
import org.mockito.kotlin.mock
|
||||||
|
import org.mockito.kotlin.times
|
||||||
|
import org.mockito.kotlin.verify
|
||||||
|
import org.mockito.kotlin.verifyNoInteractions
|
||||||
|
import org.mockito.kotlin.verifyNoMoreInteractions
|
||||||
|
import org.mockito.kotlin.whenever
|
||||||
import java.util.stream.Stream
|
import java.util.stream.Stream
|
||||||
|
|
||||||
@Suppress("TestFunctionName")
|
@Suppress("TestFunctionName")
|
||||||
@ExtendWith(InstantExecutorExtension::class, MainDispatcherExtension::class)
|
@ExtendWith(InstantExecutorExtension::class, MainDispatcherExtension::class)
|
||||||
class QueueServiceViewModelTest {
|
class QueueServiceViewModelTest {
|
||||||
|
|
||||||
private lateinit var videoDownloadingProcessorChannel: ConflatedBroadcastChannel<ProcessState>
|
private lateinit var videoDownloadingProcessorFlow: MutableSharedFlow<ProcessState>
|
||||||
private lateinit var mockVideoDownloadingProcessorUseCase: VideoDownloadingProcessorUseCase
|
private lateinit var mockVideoDownloadingProcessorUseCase: VideoDownloadingProcessorUseCase
|
||||||
private lateinit var mockAddVideoToQueueUseCase: AddVideoToQueueUseCase
|
private lateinit var mockAddVideoToQueueUseCase: AddVideoToQueueUseCase
|
||||||
private lateinit var sut: QueueServiceViewModel
|
private lateinit var sut: QueueServiceViewModel
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
fun setup() {
|
fun setup() {
|
||||||
videoDownloadingProcessorChannel = ConflatedBroadcastChannel()
|
videoDownloadingProcessorFlow = MutableSharedFlow(replay = 1)
|
||||||
mockVideoDownloadingProcessorUseCase = mock()
|
mockVideoDownloadingProcessorUseCase = mock()
|
||||||
whenever(mockVideoDownloadingProcessorUseCase.processState).doReturn(videoDownloadingProcessorChannel.asFlow())
|
whenever(mockVideoDownloadingProcessorUseCase.processState).doReturn(videoDownloadingProcessorFlow)
|
||||||
mockAddVideoToQueueUseCase = mock()
|
mockAddVideoToQueueUseCase = mock()
|
||||||
sut = QueueServiceViewModel(mockAddVideoToQueueUseCase, mockVideoDownloadingProcessorUseCase)
|
sut = QueueServiceViewModel(mockAddVideoToQueueUseCase, mockVideoDownloadingProcessorUseCase)
|
||||||
}
|
}
|
||||||
|
|
@ -64,7 +63,7 @@ class QueueServiceViewModelTest {
|
||||||
|
|
||||||
verify(mockAddVideoToQueueUseCase, times(1)).invoke("url.com")
|
verify(mockAddVideoToQueueUseCase, times(1)).invoke("url.com")
|
||||||
verifyNoMoreInteractions(mockAddVideoToQueueUseCase)
|
verifyNoMoreInteractions(mockAddVideoToQueueUseCase)
|
||||||
verifyZeroInteractions(mockVideoDownloadingProcessorUseCase)
|
verifyNoInteractions(mockVideoDownloadingProcessorUseCase)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -89,7 +88,7 @@ class QueueServiceViewModelTest {
|
||||||
) {
|
) {
|
||||||
whenever(mockAddVideoToQueueUseCase.invoke(anyOrNull())).doReturn(true)
|
whenever(mockAddVideoToQueueUseCase.invoke(anyOrNull())).doReturn(true)
|
||||||
sut.onUrlReceived("")
|
sut.onUrlReceived("")
|
||||||
videoDownloadingProcessorChannel.offer(processState)
|
videoDownloadingProcessorFlow.tryEmit(processState)
|
||||||
|
|
||||||
sut.notificationState.test()
|
sut.notificationState.test()
|
||||||
.assertHistorySize(1)
|
.assertHistorySize(1)
|
||||||
|
|
@ -101,7 +100,7 @@ class QueueServiceViewModelTest {
|
||||||
sut.onClear()
|
sut.onClear()
|
||||||
|
|
||||||
sut.onUrlReceived("alma")
|
sut.onUrlReceived("alma")
|
||||||
videoDownloadingProcessorChannel.offer(ProcessState.UnknownError)
|
videoDownloadingProcessorFlow.tryEmit(ProcessState.UnknownError)
|
||||||
|
|
||||||
sut.notificationState.test().assertNoValue()
|
sut.notificationState.test().assertNoValue()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,13 @@
|
||||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||||
buildscript {
|
buildscript {
|
||||||
ext.kotlin_version = "1.4.10"
|
ext.kotlin_version = "1.6.20"
|
||||||
repositories {
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
google()
|
google()
|
||||||
jcenter()
|
maven { url "https://plugins.gradle.org/m2/" }
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath "com.android.tools.build:gradle:4.1.0"
|
classpath 'com.android.tools.build:gradle:7.1.3'
|
||||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||||
|
|
||||||
// NOTE: Do not place your application dependencies here; they belong
|
// NOTE: Do not place your application dependencies here; they belong
|
||||||
|
|
@ -16,8 +17,8 @@ buildscript {
|
||||||
|
|
||||||
allprojects {
|
allprojects {
|
||||||
repositories {
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
google()
|
google()
|
||||||
jcenter()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
6
gradle/wrapper/gradle-wrapper.properties
vendored
6
gradle/wrapper/gradle-wrapper.properties
vendored
|
|
@ -1,6 +1,6 @@
|
||||||
#Sat Oct 31 22:42:23 EET 2020
|
#Thu Jan 27 21:44:07 EET 2022
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue