package suwayomi.tachidesk.manga.impl /* * Copyright (C) Contributors to the Suwayomi project * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ import eu.kanade.tachiyomi.source.model.Filter import eu.kanade.tachiyomi.source.model.FilterList import eu.kanade.tachiyomi.source.model.MangasPage import eu.kanade.tachiyomi.source.model.SManga import io.javalin.json.JavalinJackson import io.javalin.json.toJsonString import kotlinx.coroutines.runBlocking import org.junit.jupiter.api.AfterAll import org.junit.jupiter.api.BeforeAll import org.junit.jupiter.api.Test import org.junit.jupiter.api.TestInstance import suwayomi.tachidesk.manga.impl.Search.FilterChange import suwayomi.tachidesk.manga.impl.Search.FilterObject import suwayomi.tachidesk.manga.impl.Search.SerializableGroup import suwayomi.tachidesk.manga.impl.Search.getFilterList import suwayomi.tachidesk.manga.impl.Search.setFilter import suwayomi.tachidesk.manga.impl.Search.sourceSearch import suwayomi.tachidesk.manga.impl.util.source.GetSource.registerSource import suwayomi.tachidesk.manga.impl.util.source.GetSource.unregisterSource import suwayomi.tachidesk.manga.impl.util.source.StubSource import suwayomi.tachidesk.test.ApplicationTest import suwayomi.tachidesk.test.createSMangas import kotlin.reflect.KClass import kotlin.reflect.full.primaryConstructor import kotlin.test.assertEquals @TestInstance(TestInstance.Lifecycle.PER_CLASS) class SearchTest : ApplicationTest() { class FakeSearchableSource( id: Long, ) : StubSource(id) { var mangas: List = emptyList() override suspend fun getSearchManga( page: Int, query: String, filters: FilterList, ): MangasPage = MangasPage(mangas, false) } private val sourceId = 1L private val source = FakeSearchableSource(sourceId) private val mangasCount = 10 @BeforeAll fun setup() { registerSource(sourceId to source) this.source.mangas = createSMangas(mangasCount) } @Test fun searchWorks() { val searchResults = runBlocking { sourceSearch(sourceId, "all the mangas", 1) } assertEquals(mangasCount, searchResults.mangaList.size, "should return all the mangas") } @AfterAll fun teardown() { unregisterSource(this.sourceId) } } @Suppress("UNCHECKED_CAST") class FilterListTest : ApplicationTest() { open class EmptyFilterListSource( id: Long, ) : StubSource(id) { open var mFilterList = FilterList() override fun getFilterList(): FilterList = mFilterList } @Test fun `empty FilterList returns empty List`() { val source = registerSource(EmptyFilterListSource::class) source.mFilterList = FilterList() val filterList = getFilterList(source.id, false) assertEquals( 0, filterList.size, ) } class FilterListSource( id: Long, ) : EmptyFilterListSource(id) { class SelectFilter( name: String, values: Array, ) : Filter.Select(name, values) class TextFilter( name: String, ) : Filter.Text(name) class TestCheckBox( name: String, ) : Filter.CheckBox(name, false) class TriState( name: String, state: Int, ) : Filter.TriState(name, state) class Group( name: String, state: List, ) : Filter.Group(name, state) class Sort( name: String, values: Array, state: Selection, ) : Filter.Sort(name, values, state) override var mFilterList = FilterList( Filter.Header("This is a header"), Filter.Separator(), SelectFilter("Select one of these:", arrayOf("this", "that", "none of them")), TextFilter("text filter"), TestCheckBox("check this or else!"), TriState("wanna hook up?", Filter.TriState.STATE_IGNORE), Group( "my Todo", listOf( TestCheckBox("Write Tests"), TestCheckBox("Write More Tests"), TestCheckBox("Write Even More Tests"), ), ), Sort( "Sort", arrayOf("Alphabetic", "Date published", "Rating"), Filter.Sort.Selection(2, false), ), ) } @Test fun convertsEveryTypeCorrectly() { val source = registerSource(FilterListSource::class) val filterList = getFilterList(source.id, false) assertEquals( FilterObject("Header", source.mFilterList[0]), filterList[0], ) assertEquals( FilterObject("Separator", source.mFilterList[1]), filterList[1], ) assertEquals( FilterObject("Select", source.mFilterList[2]), filterList[2], ) assertEquals( FilterObject("Text", source.mFilterList[3]), filterList[3], ) assertEquals( FilterObject("CheckBox", source.mFilterList[4]), filterList[4], ) assertEquals( FilterObject("TriState", source.mFilterList[5]), filterList[5], ) assertEquals( filterList[6], FilterObject( "Group", SerializableGroup( source.mFilterList[6].name, listOf( FilterObject("CheckBox", (source.mFilterList[6].state as List>)[0]), FilterObject("CheckBox", (source.mFilterList[6].state as List>)[1]), FilterObject("CheckBox", (source.mFilterList[6].state as List>)[2]), ), ), ), ) assertEquals( FilterObject("Sort", source.mFilterList[7]), filterList[7], ) // make sure that we can convert this to json JavalinJackson().toJsonString(filterList) } fun setFilter( sourceId: Long, filterChange: FilterChange, ) { setFilter(sourceId, listOf(filterChange)) } @Test fun `Header and Separator should not change`() { val source = registerSource(FilterListSource::class) setFilter( source.id, FilterChange(0, "change!"), ) setFilter( source.id, FilterChange(1, "change!"), ) val filterList = getFilterList(source.id, false) assertEquals( filterList[0].filter.state, 0, ) assertEquals( filterList[1].filter.state, 0, ) } @Test fun `Select changes are Int`() { val source = registerSource(FilterListSource::class) setFilter( source.id, FilterChange(2, "1"), ) val filterList = getFilterList(source.id, false) assertEquals( filterList[2].filter.state, 1, ) } @Test fun `Text changes are String`() { val source = registerSource(FilterListSource::class) setFilter( source.id, FilterChange(3, "I'm a changed man!"), ) val filterList = getFilterList(source.id, false) assertEquals( filterList[3].filter.state, "I'm a changed man!", ) } @Test fun `CheckBox changes are Boolean`() { val source = registerSource(FilterListSource::class) setFilter( source.id, FilterChange(4, "true"), ) val filterList = getFilterList(source.id, false) assertEquals( filterList[4].filter.state, true, ) } @Test fun `TriState changes are Int`() { val source = registerSource(FilterListSource::class) setFilter( source.id, FilterChange(5, "1"), ) val filterList = getFilterList(source.id, false) assertEquals( filterList[5].filter.state, Filter.TriState.STATE_INCLUDE, ) } @Test fun `Group changes are Filters`() { val source = registerSource(FilterListSource::class) setFilter( source.id, FilterChange(6, """{"position":0,"state":"true"}"""), ) val filterList = getFilterList(source.id, false) assertEquals( (filterList[6].filter.state as List)[0].filter.state, true, ) } @Test fun `Sort changes are Filter,Sort,Selection`() { val source = registerSource(FilterListSource::class) setFilter( source.id, FilterChange(7, """{"index":1,"ascending":"true"}"""), ) val filterList = getFilterList(source.id, false) assertEquals( filterList[7].filter.state, Filter.Sort.Selection(1, true), ) } companion object { private var sourceCount = 0L private fun registerSource(sourceClass: KClass<*>): EmptyFilterListSource = synchronized(sourceClass) { val source = sourceClass.primaryConstructor!!.call(sourceCount) as EmptyFilterListSource registerSource(sourceCount to source) sourceCount++ source } @AfterAll @JvmStatic fun teardown() { (0 until sourceCount).forEach { unregisterSource(it) } } } }