Convert util/{HttpHeaderLink,PairedList,TimestampUtils,ThemeUtils} to Kotlin (#3046)

* Fix off-by-one error in HttpHeaderLink

Link headers with multiple URLs with multiple parameters were being parsed
incorrectly.

Detected by adding unit tests ahead of converting to Kotlin.

* Convert util/HttpHeaderLink from Java to Kotlin

* Convert util/ThemeUtils from Java to Kotlin

* Convert util/TimestampUtils from Java to Kotlin

* Add tests for PairedList

* Convert util/PairedList from Java to Kotlin

* Implement feedback from PR

* Relicense as GPL
This commit is contained in:
Nik Clayton 2022-12-31 13:01:35 +01:00 committed by GitHub
commit 22834431ca
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
30 changed files with 624 additions and 510 deletions

View file

@ -0,0 +1,77 @@
package com.keylesspalace.tusky.util
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.annotation.Config
@Config(sdk = [28])
@RunWith(AndroidJUnit4::class)
class HttpHeaderLinkTest {
data class TestData(val name: String, val input: String, val want: List<HttpHeaderLink>)
@Test
fun shouldParseValidLinks() {
val testData = arrayOf(
// Examples from https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Link
TestData(
"Single URL",
"<https://example.com>",
listOf(HttpHeaderLink("https://example.com"))
),
TestData(
"Single URL with parameters",
"<https://example.com>; rel=\"preconnect\"",
listOf(HttpHeaderLink("https://example.com"))
),
TestData(
"Single encoded URL with parameters",
"<https://example.com/%E8%8B%97%E6%9D%A1>; rel=\"preconnect\"",
listOf(HttpHeaderLink("https://example.com/%E8%8B%97%E6%9D%A1"))
),
TestData(
"Multiple URLs, separated by commas",
"<https://one.example.com>; rel=\"preconnect\", <https://two.example.com>; rel=\"preconnect\", <https://three.example.com>; rel=\"preconnect\"",
listOf(
HttpHeaderLink("https://one.example.com"),
HttpHeaderLink("https://two.example.com"),
HttpHeaderLink("https://three.example.com")
)
),
// Examples from https://httpwg.org/specs/rfc8288.html#rfc.section.3.5
TestData(
"Single URL, multiple parameters",
"<http://example.com/TheBook/chapter2>; rel=\"previous\"; title=\"previous chapter\"",
listOf(HttpHeaderLink("http://example.com/TheBook/chapter2"))
),
TestData(
"Root resource",
"</>; rel=\"http://example.net/foo\"",
listOf(HttpHeaderLink("/"))
),
TestData(
"Terms and anchor",
"</terms>; rel=\"copyright\"; anchor=\"#foo\"",
listOf(HttpHeaderLink("/terms"))
),
TestData(
"Multiple URLs with parameter encoding",
"</TheBook/chapter2>; rel=\"previous\"; title*=UTF-8'de'letztes%20Kapitel, </TheBook/chapter4>; rel=\"next\"; title*=UTF-8'de'n%c3%a4chstes%20Kapitel",
listOf(
HttpHeaderLink("/TheBook/chapter2"),
HttpHeaderLink("/TheBook/chapter4")
)
)
)
// Verify that the URLs are parsed correctly
for (test in testData) {
val links = HttpHeaderLink.parse(test.input)
assertEquals("${test.name}: Same size", links.size, test.want.size)
for (i in links.indices) {
assertEquals(test.name, test.want[i].uri, links[i].uri)
}
}
}
}

View file

@ -0,0 +1,91 @@
package com.keylesspalace.tusky.util
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNull
import org.junit.Before
import org.junit.Test
/**
* Tests for PairedList, with a mapper that multiples everything by 2.
*/
class PairedListTest {
private lateinit var pairedList: PairedList<Int, Int>
@Before
fun beforeEachTest() {
pairedList = PairedList { it * 2 }
for (i in 0..10) {
pairedList.add(i)
}
}
@Test
fun pairedCopy() {
val copy = pairedList.pairedCopy
for (i in 0..10) {
assertEquals(i * 2, copy[i])
}
}
@Test
fun getPairedItem() {
for (i in 0..10) {
assertEquals(i * 2, pairedList.getPairedItem(i))
}
}
@Test
fun getPairedItemOrNull() {
for (i in 0..10) {
assertEquals(i * 2, pairedList.getPairedItem(i))
}
assertNull(pairedList.getPairedItemOrNull(11))
}
@Test
fun setPairedItem() {
pairedList.setPairedItem(2, 2)
assertEquals(2, pairedList.getPairedItem(2))
}
@Test
fun get() {
for (i in 0..10) {
assertEquals(i, pairedList[i])
}
}
@Test
fun set() {
assertEquals(0, pairedList[0])
pairedList[0] = 10
assertEquals(10, pairedList[0])
assertEquals(20, pairedList.getPairedItem(0))
}
@Test
fun add() {
pairedList.add(11)
assertEquals(11, pairedList[11])
assertEquals(22, pairedList.getPairedItem(11))
}
@Test
fun addAtIndex() {
pairedList.add(11, 11)
assertEquals(11, pairedList[11])
assertEquals(22, pairedList.getPairedItem(11))
}
@Test
fun removeAt() {
pairedList.removeAt(5)
assertEquals(6, pairedList[5])
assertEquals(12, pairedList.getPairedItem(5))
}
@Test
fun size() {
assertEquals(11, pairedList.size)
}
}

View file

@ -21,9 +21,9 @@ class TimestampUtilsTest {
@Test
fun shouldShowNowForSmallTimeSpans() {
assertEquals(STATUS_CREATED_AT_NOW, TimestampUtils.getRelativeTimeSpanString(ctx, 0, 300))
assertEquals(STATUS_CREATED_AT_NOW, TimestampUtils.getRelativeTimeSpanString(ctx, 300, 0))
assertEquals(STATUS_CREATED_AT_NOW, TimestampUtils.getRelativeTimeSpanString(ctx, 501, 0))
assertEquals(STATUS_CREATED_AT_NOW, TimestampUtils.getRelativeTimeSpanString(ctx, 0, 999))
assertEquals(STATUS_CREATED_AT_NOW, getRelativeTimeSpanString(ctx, 0, 300))
assertEquals(STATUS_CREATED_AT_NOW, getRelativeTimeSpanString(ctx, 300, 0))
assertEquals(STATUS_CREATED_AT_NOW, getRelativeTimeSpanString(ctx, 501, 0))
assertEquals(STATUS_CREATED_AT_NOW, getRelativeTimeSpanString(ctx, 0, 999))
}
}