import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Assertions.*
import org.junit.jupiter.api.Test
import java.time.Duration.ofMillis
import java.time.Duration.ofSeconds
import java.util.*
import java.util.concurrent.CountDownLatch

class AssertionTest {
    val calculator = Calculator();
    val person = PersonX("Jane", "Doe");
    val people = setOf(person, PersonX("John", "Doe1"))
    //标准断言，message通常为失败时抛出的提示(可选)
    @Test
    fun standardAssertions() {
        assertEquals(2, calculator.add(1, 1))
        assertEquals(
            5, calculator.mul(2, 2),
            "The optional failure message is now the last parameter"
        )
        assertTrue('a' > 'b') { "Assertion messages can be lazily evaluated -- " + "to avoid constructing complex messages unnecessarily." }
    }

    //group ---- 所有failed断言都会报错，可以对集合中的元素进行断言操作
    @Test
    fun groupedAssertions() {
        // In a grouped assertion all assertions are executed, and all failures will be reported together.
        assertAll("person",
            { assertEquals("Jane1", person.firstName) },
            { assertEquals("Doe1", person.lastName) }
        )
    }

    @Test
    fun `grouped assertions from a collection`() {
        org.junit.jupiter.api.assertAll("People with last name of Doe",
            people.map { { assertEquals("Doe1", it.lastName) } }
        )
    }

    @Test
    fun `grouped assertions from a stream`() {
        org.junit.jupiter.api.assertAll("People with first name starting with J",
            people
                .stream()
                .map {
                    // This mapping returns Stream<() -> Unit>
                    { assertTrue(it.firstName.startsWith("J")) }
                }
        )
    }
    //同一个block中前面断言失效，后面则不再执行
    @Test
    fun dependentAssertions() {
        // Within a code block, if an assertion fails， the subsequent code in the same block will be skipped.
        assertAll("properties",
            {
                val firstName = person.firstName;
                assertNotNull(firstName);
                // Executed only if the previous assertion is valid.
                assertAll("first name",
                    { assertTrue(firstName.startsWith("J")) },
                    { assertTrue(firstName.endsWith("e")) }
                )
            },
            {
                // Grouped assertion, so processed independently of results of first name assertions.
                val lastName = person.lastName;
                assertNotNull(lastName);
                //Executed only if the previous assertion is valid.
                assertAll("last name",
                    { assertTrue(lastName.startsWith("D")) },
                    { assertTrue(lastName.endsWith("e")) }
                )
            }
        )
    }

    //抛异常断言：
    @Test
    fun exceptionTesting() {
        val exception =
            assertThrows(ArithmeticException::class.java) { calculator.div(1, 0)}
            assertEquals("/ by zero", exception.message)
    }

    @Test
    fun `exception testing in Kotlin`() {
        val exception = org.junit.jupiter.api.assertThrows<ArithmeticException>("Should throw an exception") {
            calculator.div(1, 0)
        }
        assertEquals("/ by Zero", exception.message)
    }

    //超时断言
    @Test
    fun timeoutNotExceeded() {
        // The following assertion succeeds.
        assertTimeout(ofSeconds(2)) {
        // Perform task that takes less than 2 seconds.
            println("timeoutNotExceeded")
        }
    }

    @Test
    fun timeoutNotExceededWithResult() {
        // The following assertion succeeds, and returns the supplied object.
        val actualResult: String = assertTimeout<String>(ofSeconds(2)) { "result" }
        assertEquals("a result", actualResult)
    }

    @Test
    fun timeoutNotExceededWithMethod() {
        // The following assertion invokes a method reference and returns an object.
        val actualGreeting = assertTimeout(ofSeconds(2), ::greeting)
        assertEquals("Hello, World!", actualGreeting);
    }

    @Test
    fun timeoutExceeded() {
        // The following assertion fails with an error message similar to:
        // execution exceeded timeout of 10 ms by 91 ms
        assertTimeout(ofMillis(10)) {
            // Simulate task that takes more than 10 ms.
            Thread.sleep(100)
        }
    }

    @Test
    fun timeoutExceededWithPreemptiveTermination() {
        // The following assertion fails with an error message similar to:
        // execution timed out after 10 ms
        assertTimeoutPreemptively(ofMillis(10)) {
            // Simulate task that takes more than 10 ms.
            CountDownLatch(1).await();
        }
    }

    //流式断言器assertThat:需第三方库支持,
    @Test
    fun testUsingAssertJ() {
        assertThat(calculator.sub(4, 1)).isEqualTo(3)
        // 子符串判断
        val s = "abcde"
        assertThat(s).`as`("字符串判读").startsWith("abc").endsWith("de").hasSize(5)
        // 数字判断
        val i = 500
           assertThat(i).`as`("数字判断,数字大小比较").isGreaterThan(100).isLessThan(1000)
        // 日期判断
        val date1 = Date()
        val date2 = Date(date1.getTime() + 100)
        val date3 = Date(date1.getTime() - 100)
        assertThat(date1).`as`("日期判断：日期大小比较").isBefore(date2).isAfter(date3)
        // list比较
        val list = Arrays.asList("a", "b", "c", "d")
        assertThat(list).`as`("list的首尾元素及长度").startsWith("a").endsWith("d").hasSize(4)
        // Map判断
        val map: MutableMap<String, Any> = HashMap()
        map["A"] = 1
        map["B"] = 2
        map["C"] = 3
        assertThat(map).`as`("Map的长度及键值测试").hasSize(3).containsKeys("A", "B", "C")
    }

    private fun greeting(): String? {
        return "Hello, World!"
    }
}