mirror of
https://github.com/recloudstream/cloudstream.git
synced 2024-08-15 01:53:11 +00:00
tests for all providers
This commit is contained in:
parent
255265eed2
commit
509a0a6b90
4 changed files with 206 additions and 20 deletions
|
@ -14,6 +14,9 @@ if (allFilesFromDir != null) {
|
|||
|
||||
|
||||
android {
|
||||
testOptions {
|
||||
unitTests.returnDefaultValues = true
|
||||
}
|
||||
signingConfigs {
|
||||
prerelease {
|
||||
if (prerelaseStoreFile != null) {
|
||||
|
@ -76,6 +79,8 @@ repositories {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
testImplementation 'org.json:json:20180813'
|
||||
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||
implementation 'androidx.core:core-ktx:1.6.0'
|
||||
implementation 'androidx.appcompat:appcompat:1.3.1'
|
||||
|
|
|
@ -18,7 +18,6 @@ class KawaiifuProvider : MainAPI() {
|
|||
override val hasMainPage: Boolean
|
||||
get() = true
|
||||
|
||||
|
||||
override val supportedTypes: Set<TvType>
|
||||
get() = setOf(TvType.Anime, TvType.AnimeMovie, TvType.ONA)
|
||||
|
||||
|
@ -26,12 +25,12 @@ class KawaiifuProvider : MainAPI() {
|
|||
override fun getMainPage(): HomePageResponse {
|
||||
val items = ArrayList<HomePageList>()
|
||||
val resp = get(mainUrl).text
|
||||
println("RESP $resp")
|
||||
|
||||
val soup = Jsoup.parse(resp)
|
||||
|
||||
items.add(HomePageList("Latest Updates", soup.select(".today-update .item").map {
|
||||
val title = it.selectFirst("img").attr("alt")
|
||||
AnimeSearchResponse(
|
||||
val title = it.selectFirst("img").attr("alt")
|
||||
AnimeSearchResponse(
|
||||
title,
|
||||
it.selectFirst("a").attr("href"),
|
||||
this.name,
|
||||
|
@ -48,8 +47,8 @@ class KawaiifuProvider : MainAPI() {
|
|||
try {
|
||||
val title = section.selectFirst(".title").text()
|
||||
val anime = section.select(".list-film > .item").map { ani ->
|
||||
val animTitle = ani.selectFirst("img").attr("alt")
|
||||
AnimeSearchResponse(
|
||||
val animTitle = ani.selectFirst("img").attr("alt")
|
||||
AnimeSearchResponse(
|
||||
animTitle,
|
||||
ani.selectFirst("a").attr("href"),
|
||||
this.name,
|
||||
|
@ -68,7 +67,7 @@ class KawaiifuProvider : MainAPI() {
|
|||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
if(items.size <= 0) throw ErrorLoadingException()
|
||||
if (items.size <= 0) throw ErrorLoadingException()
|
||||
return HomePageResponse(items)
|
||||
}
|
||||
|
||||
|
@ -151,7 +150,7 @@ class KawaiifuProvider : MainAPI() {
|
|||
|
||||
val servers = soupa.select(".list-server").map {
|
||||
val serverName = it.selectFirst(".server-name").text()
|
||||
val episodes = it.select(".list-ep > li > a").map { episode -> Pair(episode.attr("href"), episode.text()) }
|
||||
val episodes = it.select(".list-ep > li > a").map { episode -> Pair(episode.attr("href"), episode.text()) }
|
||||
val episode = if (episodeNum == null) episodes[0] else episodes.mapNotNull { ep ->
|
||||
if ((if (ep.first.contains("ep=")) ep.first.split("ep=")[1].split("&")[0].toIntOrNull() else null) == episodeNum) {
|
||||
ep
|
||||
|
@ -160,27 +159,31 @@ class KawaiifuProvider : MainAPI() {
|
|||
Pair(serverName, episode)
|
||||
}.map {
|
||||
if (it.second.first == data) {
|
||||
val sources = soupa.select("video > source").map { source -> Pair(source.attr("src"), source.attr("data-quality")) }
|
||||
val sources = soupa.select("video > source")
|
||||
.map { source -> Pair(source.attr("src"), source.attr("data-quality")) }
|
||||
Triple(it.first, sources, it.second.second)
|
||||
} else {
|
||||
val html = get(it.second.first).text
|
||||
val soup = Jsoup.parse(html)
|
||||
|
||||
val sources = soup.select("video > source").map { source -> Pair(source.attr("src"), source.attr("data-quality")) }
|
||||
val sources = soup.select("video > source")
|
||||
.map { source -> Pair(source.attr("src"), source.attr("data-quality")) }
|
||||
Triple(it.first, sources, it.second.second)
|
||||
}
|
||||
}
|
||||
|
||||
servers.forEach {
|
||||
it.second.forEach { source ->
|
||||
callback(ExtractorLink(
|
||||
"Kawaiifu",
|
||||
"${it.first} - ${source.second}",
|
||||
source.first,
|
||||
"",
|
||||
getQualityFromName(source.second),
|
||||
source.first.contains(".m3u")
|
||||
))
|
||||
callback(
|
||||
ExtractorLink(
|
||||
"Kawaiifu",
|
||||
"${it.first} - ${source.second}",
|
||||
source.first,
|
||||
"",
|
||||
getQualityFromName(source.second),
|
||||
source.first.contains(".m3u")
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
return true
|
||||
|
|
|
@ -24,6 +24,8 @@ class WatchCartoonOnlineProvider : MainAPI() {
|
|||
get() = setOf(
|
||||
TvType.Cartoon,
|
||||
TvType.Anime,
|
||||
TvType.AnimeMovie,
|
||||
TvType.TvSeries
|
||||
)
|
||||
|
||||
override fun search(query: String): List<SearchResponse> {
|
||||
|
@ -109,7 +111,6 @@ class WatchCartoonOnlineProvider : MainAPI() {
|
|||
val response = get(url).text
|
||||
val document = Jsoup.parse(response)
|
||||
|
||||
|
||||
return if (!isMovie) {
|
||||
val title = document.selectFirst("td.vsbaslik > h2").text()
|
||||
val poster = fixUrl(document.selectFirst("div#cat-img-desc > div > img").attr("src"))
|
||||
|
|
177
app/src/test/java/com/lagradost/cloudstream3/ProviderTests.kt
Normal file
177
app/src/test/java/com/lagradost/cloudstream3/ProviderTests.kt
Normal file
|
@ -0,0 +1,177 @@
|
|||
package com.lagradost.cloudstream3
|
||||
|
||||
import com.lagradost.cloudstream3.mvvm.logError
|
||||
import com.lagradost.cloudstream3.utils.Qualities
|
||||
import com.lagradost.cloudstream3.utils.SubtitleHelper
|
||||
import org.junit.Assert
|
||||
import org.junit.Test
|
||||
|
||||
class ProviderTests {
|
||||
private fun getAllProviders(): List<MainAPI> {
|
||||
val allApis = APIHolder.apis
|
||||
allApis.addAll(APIHolder.restrictedApis)
|
||||
return allApis
|
||||
}
|
||||
|
||||
@Test
|
||||
fun providers_exist() {
|
||||
Assert.assertTrue(getAllProviders().isNotEmpty())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun provider_correct_data() {
|
||||
val isoNames = SubtitleHelper.languages.map { it.ISO_639_1 }
|
||||
Assert.assertFalse("ISO does not contain any languages", isoNames.isNullOrEmpty())
|
||||
for (api in getAllProviders()) {
|
||||
Assert.assertTrue("Api does not contain a mainurl", api.mainUrl != "NONE")
|
||||
Assert.assertTrue("Api does not contain a name", api.name != "NONE")
|
||||
Assert.assertTrue("Api ${api.name} does not contain a valid language code", isoNames.contains(api.lang))
|
||||
Assert.assertTrue("Api ${api.name} does not contain any supported types", api.supportedTypes.isNotEmpty())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun provider_correct_homepage() {
|
||||
for (api in getAllProviders()) {
|
||||
if (api.hasMainPage) {
|
||||
try {
|
||||
val homepage = api.getMainPage()
|
||||
when {
|
||||
homepage == null -> {
|
||||
Assert.fail("Homepage provider ${api.name} did not correctly load homepage!")
|
||||
}
|
||||
homepage.items.isEmpty() -> {
|
||||
Assert.fail("Homepage provider ${api.name} does not contain any items!")
|
||||
}
|
||||
homepage.items.any { it.list.isEmpty() } -> {
|
||||
Assert.fail("Homepage provider ${api.name} does not have any items on result!")
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
if (e.cause is NotImplementedError) {
|
||||
Assert.fail("Provider marked as hasMainPage, while in reality is has not been implemented")
|
||||
}
|
||||
logError(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadLinks(api: MainAPI, url: String?): Boolean {
|
||||
Assert.assertNotNull("Api ${api.name} has invalid url on episode", url)
|
||||
if (url == null) return true
|
||||
var linksLoaded = 0
|
||||
try {
|
||||
val success = api.loadLinks(url, false, {}) { link ->
|
||||
Assert.assertTrue(
|
||||
"Api ${api.name} returns link with invalid Quality",
|
||||
Qualities.values().map { it.value }.contains(link.quality)
|
||||
)
|
||||
Assert.assertTrue("Api ${api.name} returns link with invalid url", link.url.length > 4)
|
||||
linksLoaded++
|
||||
}
|
||||
if (success) {
|
||||
return linksLoaded > 0
|
||||
}
|
||||
Assert.assertTrue("Api ${api.name} has returns false on .loadLinks", success)
|
||||
} catch (e: Exception) {
|
||||
if (e.cause is NotImplementedError) {
|
||||
Assert.fail("Provider has not implemented .loadLinks")
|
||||
}
|
||||
logError(e)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@Test
|
||||
fun provider_correct() {
|
||||
val searchQueries = listOf("over", "iron", "guy")
|
||||
val providers = getAllProviders()
|
||||
for ((index, api) in providers.withIndex()) {
|
||||
try {
|
||||
println("Trying $api (${index + 1}/${providers.size})")
|
||||
var correctResponses = 0
|
||||
var searchResult: List<SearchResponse>? = null
|
||||
for (query in searchQueries) {
|
||||
val response = try {
|
||||
api.search(query)
|
||||
} catch (e: Exception) {
|
||||
if (e.cause is NotImplementedError) {
|
||||
Assert.fail("Provider has not implemented .search")
|
||||
}
|
||||
logError(e)
|
||||
null
|
||||
}
|
||||
if (!response.isNullOrEmpty()) {
|
||||
correctResponses++
|
||||
if (searchResult == null) {
|
||||
searchResult = response
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (correctResponses == 0 || searchResult == null) {
|
||||
println("Api ${api.name} did not return any valid search responses")
|
||||
continue
|
||||
}
|
||||
|
||||
try {
|
||||
var validResults = false
|
||||
for (result in searchResult) {
|
||||
Assert.assertEquals("Invalid apiName on response on ${api.name}", result.apiName, api.name)
|
||||
val load = api.load(result.url) ?: continue
|
||||
Assert.assertEquals("Invalid apiName on load on ${api.name}", load.apiName, result.apiName)
|
||||
Assert.assertTrue(
|
||||
"Api ${api.name} on load does not contain any of the supportedTypes",
|
||||
api.supportedTypes.contains(load.type)
|
||||
)
|
||||
when (load) {
|
||||
is AnimeLoadResponse -> {
|
||||
val gotNoEpisodes =
|
||||
load.dubEpisodes.isNullOrEmpty() && load.subEpisodes.isNullOrEmpty()
|
||||
if (gotNoEpisodes) {
|
||||
println("Api ${api.name} got no episodes on ${load.url}")
|
||||
continue
|
||||
}
|
||||
|
||||
val url = (load.dubEpisodes ?: load.subEpisodes)?.first()?.url
|
||||
validResults = loadLinks(api, url)
|
||||
if (!validResults) continue
|
||||
}
|
||||
is MovieLoadResponse -> {
|
||||
val gotNoEpisodes = load.dataUrl.isBlank()
|
||||
if (gotNoEpisodes) {
|
||||
println("Api ${api.name} got no movie on ${load.url}")
|
||||
continue
|
||||
}
|
||||
|
||||
validResults = loadLinks(api, load.dataUrl)
|
||||
if (!validResults) continue
|
||||
}
|
||||
is TvSeriesLoadResponse -> {
|
||||
val gotNoEpisodes = load.episodes.isEmpty()
|
||||
if (gotNoEpisodes) {
|
||||
println("Api ${api.name} got no episodes on ${load.url}")
|
||||
continue
|
||||
}
|
||||
|
||||
validResults = loadLinks(api, load.episodes.first().data)
|
||||
if (!validResults) continue
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
Assert.assertTrue("Api ${api.name} did not load on any}", validResults)
|
||||
} catch (e: Exception) {
|
||||
if (e.cause is NotImplementedError) {
|
||||
Assert.fail("Provider has not implemented .load")
|
||||
}
|
||||
logError(e)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
logError(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue