151 lines
5.2 KiB
Kotlin
151 lines
5.2 KiB
Kotlin
|
package com.keylesspalace.tusky
|
||
|
|
||
|
import android.text.Spannable
|
||
|
import com.keylesspalace.tusky.util.highlightSpans
|
||
|
import junit.framework.Assert
|
||
|
import org.junit.Test
|
||
|
import org.junit.runner.RunWith
|
||
|
import org.junit.runners.Parameterized
|
||
|
|
||
|
class SpanUtilsTest {
|
||
|
@Test
|
||
|
fun matchesMixedSpans() {
|
||
|
val input = "one #one two @two three https://thr.ee/meh?foo=bar&wat=@at#hmm four #four five @five"
|
||
|
val inputSpannable = FakeSpannable(input)
|
||
|
highlightSpans(inputSpannable, 0xffffff)
|
||
|
val spans = inputSpannable.spans
|
||
|
Assert.assertEquals(5, spans.size)
|
||
|
}
|
||
|
|
||
|
@Test
|
||
|
fun doesntMergeAdjacentURLs() {
|
||
|
val firstURL = "http://first.thing"
|
||
|
val secondURL = "https://second.thing"
|
||
|
val inputSpannable = FakeSpannable("${firstURL} ${secondURL}")
|
||
|
highlightSpans(inputSpannable, 0xffffff)
|
||
|
val spans = inputSpannable.spans
|
||
|
Assert.assertEquals(2, spans.size)
|
||
|
Assert.assertEquals(firstURL.length, spans[0].end - spans[0].start)
|
||
|
Assert.assertEquals(secondURL.length, spans[1].end - spans[1].start)
|
||
|
}
|
||
|
|
||
|
@RunWith(Parameterized::class)
|
||
|
class MatchingTests(private val thingToHighlight: String) {
|
||
|
companion object {
|
||
|
@Parameterized.Parameters(name = "{0}")
|
||
|
@JvmStatic
|
||
|
fun data(): Iterable<Any> {
|
||
|
return listOf(
|
||
|
"@mention",
|
||
|
"#tag",
|
||
|
"https://thr.ee/meh?foo=bar&wat=@at#hmm",
|
||
|
"http://thr.ee/meh?foo=bar&wat=@at#hmm"
|
||
|
)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@Test
|
||
|
fun matchesSpanAtStart() {
|
||
|
val inputSpannable = FakeSpannable(thingToHighlight)
|
||
|
highlightSpans(inputSpannable, 0xffffff)
|
||
|
val spans = inputSpannable.spans
|
||
|
Assert.assertEquals(1, spans.size)
|
||
|
Assert.assertEquals(thingToHighlight.length, spans[0].end - spans[0].start)
|
||
|
}
|
||
|
|
||
|
@Test
|
||
|
fun matchesSpanNotAtStart() {
|
||
|
val inputSpannable = FakeSpannable(" ${thingToHighlight}")
|
||
|
highlightSpans(inputSpannable, 0xffffff)
|
||
|
val spans = inputSpannable.spans
|
||
|
Assert.assertEquals(1, spans.size)
|
||
|
Assert.assertEquals(thingToHighlight.length, spans[0].end - spans[0].start)
|
||
|
}
|
||
|
|
||
|
@Test
|
||
|
fun doesNotMatchSpanEmbeddedInText() {
|
||
|
val inputSpannable = FakeSpannable("aa${thingToHighlight}aa")
|
||
|
highlightSpans(inputSpannable, 0xffffff)
|
||
|
val spans = inputSpannable.spans
|
||
|
Assert.assertTrue(spans.isEmpty())
|
||
|
}
|
||
|
|
||
|
@Test
|
||
|
fun doesNotMatchSpanEmbeddedInAnotherSpan() {
|
||
|
val inputSpannable = FakeSpannable("@aa${thingToHighlight}aa")
|
||
|
highlightSpans(inputSpannable, 0xffffff)
|
||
|
val spans = inputSpannable.spans
|
||
|
Assert.assertEquals(1, spans.size)
|
||
|
}
|
||
|
|
||
|
@Test
|
||
|
fun spansDoNotOverlap() {
|
||
|
val begin = "@begin"
|
||
|
val end = "#end"
|
||
|
val inputSpannable = FakeSpannable("${begin} ${thingToHighlight} ${end}")
|
||
|
highlightSpans(inputSpannable, 0xffffff)
|
||
|
val spans = inputSpannable.spans
|
||
|
Assert.assertEquals(3, spans.size)
|
||
|
|
||
|
val middleSpan = spans.single ({ span -> span.start > 0 && span.end < inputSpannable.lastIndex })
|
||
|
Assert.assertEquals(begin.length + 1, middleSpan.start)
|
||
|
Assert.assertEquals(inputSpannable.length - end.length - 1, middleSpan.end)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
class FakeSpannable(private val text: String) : Spannable {
|
||
|
val spans = mutableListOf<BoundedSpan>()
|
||
|
|
||
|
override fun setSpan(what: Any?, start: Int, end: Int, flags: Int) {
|
||
|
spans.add(BoundedSpan(what, start, end))
|
||
|
}
|
||
|
|
||
|
override fun <T : Any?> getSpans(start: Int, end: Int, type: Class<T>?): Array<T> {
|
||
|
val matching = if (type == null) {
|
||
|
ArrayList<T>()
|
||
|
} else {
|
||
|
spans.filter ({ it.start >= start && it.end <= end && type?.isAssignableFrom(it.span?.javaClass) })
|
||
|
.map({ it -> it.span })
|
||
|
.let { ArrayList(it) }
|
||
|
}
|
||
|
return matching.toArray() as Array<T>
|
||
|
}
|
||
|
|
||
|
override fun removeSpan(what: Any?) {
|
||
|
spans.removeIf({ span -> span.span == what})
|
||
|
}
|
||
|
|
||
|
override fun toString(): String {
|
||
|
return text
|
||
|
}
|
||
|
|
||
|
override val length: Int
|
||
|
get() = text.length
|
||
|
|
||
|
class BoundedSpan(val span: Any?, val start: Int, val end: Int)
|
||
|
|
||
|
override fun nextSpanTransition(start: Int, limit: Int, type: Class<*>?): Int {
|
||
|
throw NotImplementedError()
|
||
|
}
|
||
|
|
||
|
override fun getSpanEnd(tag: Any?): Int {
|
||
|
throw NotImplementedError()
|
||
|
}
|
||
|
|
||
|
override fun getSpanFlags(tag: Any?): Int {
|
||
|
throw NotImplementedError()
|
||
|
}
|
||
|
|
||
|
override fun get(index: Int): Char {
|
||
|
throw NotImplementedError()
|
||
|
}
|
||
|
|
||
|
override fun subSequence(startIndex: Int, endIndex: Int): CharSequence {
|
||
|
throw NotImplementedError()
|
||
|
}
|
||
|
|
||
|
override fun getSpanStart(tag: Any?): Int {
|
||
|
throw NotImplementedError()
|
||
|
}
|
||
|
}
|
||
|
}
|