/**参数化测试，可以为一个测试方法提供多组测试数据
 * @ValueSource
 * @EnumSource
 * @MethodSource
 * @CsvSource/@CsvFileSource
 * @ArgumentsSource
 */
import org.junit.jupiter.api.Assertions.*
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.extension.ExtensionContext
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.aggregator.ArgumentsAccessor
import org.junit.jupiter.params.provider.*
import org.junit.jupiter.params.provider.Arguments.arguments
import java.time.LocalDate
import java.time.temporal.ChronoUnit
import java.time.temporal.TemporalUnit
import java.util.*
import java.util.stream.Stream

fun isPalindrome1(str: String?): Boolean {
    if (str == null) return false

    var start = 0
    var end = str.length - 1
    do {
        if (str[start] == str[end]) {
            start++;end--
        } else
            return false
    } while (start < end)

    /*等效替换1
    val len = str.length
    for(i in 0..str.length/2){
        if (str[i] != str[len - 1 - i]) {
            return false;
        }
    }*/

    /*等效替换2
    val strReversed=str.reversed()
    println(strReversed)
    if (str!=strReversed) return false
     */

    return true
}

//递归算法
fun isPalindrome2(str: String?, start: Int = 0, end: Int? = str!!.length - 1): Boolean {
    if (str == null) return false
    var s = start
    var e = end!!
    if (s >= e) {
        return true
    } else if (str[s] != str[e]) return false
    return isPalindrome2(str, ++s, --e)
}

class ParameterizedTest {
    @ParameterizedTest
    @ValueSource(strings = ["racecar", "radar", "able was I ere I saw elba"])
    fun palindromes(candidate: String?) {
        assertTrue(isPalindrome1(candidate))
    }

    @ParameterizedTest
    @NullSource
    @EmptySource
    @ValueSource(strings = [" ", " ", "\t", "\n"])
    fun nullEmptyAndBlankStrings(text: String?) {
        assertTrue(text == null || text.trim().isEmpty())
    }

    @ParameterizedTest
    @EnumSource(ChronoUnit::class)
    fun testWithEnumSource(unit: TemporalUnit?) {
        assertNotNull(unit)
    }

    @ParameterizedTest
    @EnumSource(value = ChronoUnit::class, names = ["DAYS", "HOURS"])
    fun testWithEnumSourceInclude(unit: ChronoUnit) {
        assertTrue(EnumSet.of(ChronoUnit.DAYS, ChronoUnit.HOURS).contains(unit))
    }

    @ParameterizedTest
    @MethodSource("stringIntAndListProvider")
    fun testWithMultiArgMethodSource(str: String, num: Int, list: List<String?>) {
        assertEquals(5, str.length)
        assertTrue(num >= 1 && num <= 2)
        assertEquals(2, list.size)
    }

    companion object {
        @JvmStatic
        //primitive types:DoubleStream, IntStream, and LongStream
        fun stringIntAndListProvider(): Stream<Arguments?>? {
            return Stream.of(
                arguments("apple", 1, Arrays.asList("a", "b")),
                arguments("lemon", 2, Arrays.asList("x", "y"))
            )
        }
    }

    @ParameterizedTest
    @CsvSource("apple, 1", "banana, 2", "'lemon, lime', 0xF1")
    fun testWithCsvSource(fruit: String?, rank: Int) {
        assertNotNull(fruit)
        assertNotEquals(0, rank)
    }

    @ParameterizedTest
    @CsvFileSource(resources = arrayOf("src/test/resources/twocolumn.csv"), numLinesToSkip = 1)
    fun testWithCsvFileSourceFromFile(country: String?, reference: Int,city:String?) {
        assertNotNull(country)
        assertNotEquals(0, reference)
        assertEquals("Shanghai", city)
    }


    @ParameterizedTest
    @ArgumentsSource(MyArgumentsProvider::class)
    fun testWithArgumentsSource(argument: String) {
        assertTrue(argument.length <= 5)
    }

    class MyArgumentsProvider:ArgumentsProvider {
        override fun provideArguments(context: ExtensionContext): Stream<out Arguments> {
            return Stream.of("apple", "banana").map(Arguments::of)
        }
    }

    @ParameterizedTest
    @CsvSource("Jane, Doex,F, 1990-05-20", "John, Doe,M, 1990-10-22")
    fun testWithArgumentsAccessor(arguments: ArgumentsAccessor) {
        val person = PersonX(
            arguments.getString(0),
            arguments.get(1,String::class.java),
        )
        person.sex=arguments.get(2,String::class.java)
        person.birthDay=arguments.get(3,LocalDate::class.java)

        if (person.firstName.equals("Jane")) {
            assertEquals("F", person.sex)
        }
        else {
            assertEquals("M", person.sex)
        }
        assertEquals("Doe", person.lastName)
        assertEquals(1990, person.birthDay.year);
    }

    @DisplayName("Display name of container")
    @ParameterizedTest(name = "{index} ==> the rank of {0} is {1}")
    @CsvSource("apple, 1, a", "banana, 2, b", "'lemon, lime', 3, c")
    fun testWithCustomDisplayNames(fruit: String?, rank: Int) {
    }
}