All Downloads are FREE. Search and download functionalities are using the official Maven repository.

androidTest.androidx.compose.ui.text.input.GapBufferTest.kt Maven / Gradle / Ivy

/*
 * Copyright 2019 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package androidx.compose.ui.text.input

import androidx.compose.ui.text.InternalTextApi
import androidx.compose.ui.text.matchers.assertThat
import com.google.common.truth.Truth.assertThat
import kotlin.random.Random
import kotlin.test.assertFailsWith
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4

@OptIn(InternalTextApi::class)
@RunWith(JUnit4::class)
class GapBufferTest {

    @Test
    fun insertTest_insert_to_empty_string() {
        assertThat(
            PartialGapBuffer("").apply {
                replace(0, 0, "A")
            }
        ).hasChars("A")
    }

    @Test
    fun insertTest_insert_and_append() {
        assertThat(
            PartialGapBuffer("").apply {
                replace(0, 0, "A")
                replace(0, 0, "B")
            }
        ).hasChars("BA")
    }

    @Test
    fun insertTest_insert_and_prepend() {
        assertThat(
            PartialGapBuffer("").apply {
                replace(0, 0, "A")
                replace(1, 1, "B")
            }
        ).hasChars("AB")
    }

    @Test
    fun insertTest_insert_and_insert_into_middle() {
        assertThat(
            PartialGapBuffer("").apply {
                replace(0, 0, "AA")
                replace(1, 1, "B")
            }
        ).hasChars("ABA")
    }

    @Test
    fun insertTest_intoExistingText_prepend() {
        assertThat(
            PartialGapBuffer("XX").apply {
                replace(0, 0, "A")
            }
        ).hasChars("AXX")
    }

    @Test
    fun insertTest_intoExistingText_insert_into_middle() {
        assertThat(
            PartialGapBuffer("XX").apply {
                replace(1, 1, "A")
            }
        ).hasChars("XAX")
    }

    @Test
    fun insertTest_intoExistingText_append() {
        assertThat(
            PartialGapBuffer("XX").apply {
                replace(2, 2, "A")
            }
        ).hasChars("XXA")
    }

    @Test
    fun insertTest_intoExistingText_prepend_and_prepend() {
        assertThat(
            PartialGapBuffer("XX").apply {
                replace(0, 0, "A")
                replace(0, 0, "B")
            }
        ).hasChars("BAXX")
    }

    @Test
    fun insertTest_intoExistingText_prepend_and_append() {
        assertThat(
            PartialGapBuffer("XX").apply {
                replace(0, 0, "A")
                replace(1, 1, "B")
            }
        ).hasChars("ABXX")
    }

    @Test
    fun insertTest_intoExistingText_prepend_and_insert_middle() {
        assertThat(
            PartialGapBuffer("XX").apply {
                replace(0, 0, "A")
                replace(2, 2, "B")
            }
        ).hasChars("AXBX")
    }

    @Test
    fun insertTest_intoExistingText_insert_two_chars_and_append() {
        assertThat(
            PartialGapBuffer("XX").apply {
                replace(0, 0, "AA")
                replace(1, 1, "B")
            }
        ).hasChars("ABAXX")
    }

    @Test
    fun deleteTest_insert_and_delete_from_head() {
        assertThat(
            PartialGapBuffer("").apply {
                replace(0, 0, "ABC")
                replace(0, 1, "")
            }
        ).hasChars("BC")
    }

    @Test
    fun deleteTest_insert_and_delete_middle() {
        assertThat(
            PartialGapBuffer("").apply {
                replace(0, 0, "ABC")
                replace(1, 2, "")
            }
        ).hasChars("AC")
    }

    @Test
    fun deleteTest_insert_and_delete_tail() {
        assertThat(
            PartialGapBuffer("").apply {
                replace(0, 0, "ABC")
                replace(2, 3, "")
            }
        ).hasChars("AB")
    }

    @Test
    fun deleteTest_insert_and_delete_two_head() {
        assertThat(
            PartialGapBuffer("").apply {
                replace(0, 0, "ABC")
                replace(0, 2, "")
            }
        ).hasChars("C")
    }

    @Test
    fun deleteTest_insert_and_delete_two_tail() {
        assertThat(
            PartialGapBuffer("").apply {
                replace(0, 0, "ABC")
                replace(1, 3, "")
            }
        ).hasChars("A")
    }

    @Test
    fun deleteTest_insert_and_delete_with_two_instruction_from_haed() {
        assertThat(
            PartialGapBuffer("").apply {
                replace(0, 0, "ABC")
                replace(0, 1, "")
                replace(0, 1, "")
            }
        ).hasChars("C")
    }

    @Test
    fun deleteTest_insert_and_delet_with_two_instruction_from_head_and_tail() {
        assertThat(
            PartialGapBuffer("").apply {
                replace(0, 0, "ABC")
                replace(0, 1, "")
                replace(1, 2, "")
            }
        ).hasChars("B")
    }

    @Test
    fun deleteTest_insert_and_delet_with_two_instruction_from_tail() {
        assertThat(
            PartialGapBuffer("").apply {
                replace(0, 0, "ABC")
                replace(1, 2, "")
                replace(1, 2, "")
            }
        ).hasChars("A")
    }

    @Test
    fun deleteTest_insert_and_delete_three_chars() {
        assertThat(
            PartialGapBuffer("").apply {
                replace(0, 0, "ABC")
                replace(0, 3, "")
            }
        ).hasChars("")
    }

    @Test
    fun deleteTest_insert_and_delete_three_chars_with_three_instructions() {
        assertThat(
            PartialGapBuffer("").apply {
                replace(0, 0, "ABC")
                replace(0, 1, "")
                replace(0, 1, "")
                replace(0, 1, "")
            }
        ).hasChars("")
    }

    @Test
    fun deleteTest_fromExistingText_from_head() {
        assertThat(
            PartialGapBuffer("ABC").apply {
                replace(0, 1, "")
            }
        ).hasChars("BC")
    }

    @Test
    fun deleteTest_fromExistingText_from_middle() {
        assertThat(
            PartialGapBuffer("ABC").apply {
                replace(1, 2, "")
            }
        ).hasChars("AC")
    }

    @Test
    fun deleteTest_fromExistingText_from_tail() {
        assertThat(
            PartialGapBuffer("ABC").apply {
                replace(2, 3, "")
            }
        ).hasChars("AB")
    }

    @Test
    fun deleteTest_fromExistingText_delete_two_chars_from_head() {
        assertThat(
            PartialGapBuffer("ABC").apply {
                replace(0, 2, "")
            }
        ).hasChars("C")
    }

    @Test
    fun deleteTest_fromExistingText_delete_two_chars_from_tail() {
        assertThat(
            PartialGapBuffer("ABC").apply {
                replace(1, 3, "")
            }
        ).hasChars("A")
    }

    @Test
    fun deleteTest_fromExistingText_delete_two_chars_with_two_instruction_from_head() {
        assertThat(
            PartialGapBuffer("ABC").apply {
                replace(0, 1, "")
                replace(0, 1, "")
            }
        ).hasChars("C")
    }

    @Test
    fun deleteTest_fromExistingText_delete_two_chars_with_two_instruction_from_head_and_tail() {
        assertThat(
            PartialGapBuffer("ABC").apply {
                replace(0, 1, "")
                replace(1, 2, "")
            }
        ).hasChars("B")
    }

    @Test
    fun deleteTest_fromExistingText_delete_two_chars_with_two_instruction_from_tail() {
        assertThat(
            PartialGapBuffer("ABC").apply {
                replace(1, 2, "")
                replace(1, 2, "")
            }
        ).hasChars("A")
    }

    @Test
    fun deleteTest_fromExistingText_delete_three_chars() {
        assertThat(
            PartialGapBuffer("ABC").apply {
                replace(0, 3, "")
            }
        ).hasChars("")
    }

    @Test
    fun deleteTest_fromExistingText_delete_three_chars_with_three_instructions() {
        assertThat(
            PartialGapBuffer("ABC").apply {
                replace(0, 1, "")
                replace(0, 1, "")
                replace(0, 1, "")
            }
        ).hasChars("")
    }

    @Test
    fun replaceTest_head() {
        assertThat(
            PartialGapBuffer("").apply {
                replace(0, 0, "ABC")
                replace(0, 1, "X")
            }
        ).hasChars("XBC")
    }

    @Test
    fun replaceTest_middle() {
        assertThat(
            PartialGapBuffer("").apply {
                replace(0, 0, "ABC")
                replace(1, 2, "X")
            }
        ).hasChars("AXC")
    }

    @Test
    fun replaceTest_tail() {
        assertThat(
            PartialGapBuffer("").apply {
                replace(0, 0, "ABC")
                replace(2, 3, "X")
            }
        ).hasChars("ABX")
    }

    @Test
    fun replaceTest_head_two_chars() {
        assertThat(
            PartialGapBuffer("").apply {
                replace(0, 0, "ABC")
                replace(0, 2, "X")
            }
        ).hasChars("XC")
    }

    @Test
    fun replaceTest_middle_two_chars() {
        assertThat(
            PartialGapBuffer("").apply {
                replace(0, 0, "ABC")
                replace(1, 3, "X")
            }
        ).hasChars("AX")
    }

    @Test
    fun replaceTest_three_chars() {
        assertThat(
            PartialGapBuffer("").apply {
                replace(0, 0, "ABC")
                replace(0, 3, "X")
            }
        ).hasChars("X")
    }

    @Test
    fun replaceTest_one_char_with_two_chars_from_head() {
        assertThat(
            PartialGapBuffer("").apply {
                replace(0, 0, "ABC")
                replace(0, 1, "XY")
            }
        ).hasChars("XYBC")
    }

    @Test
    fun replaceTest_one_char_with_two_chars_from_middle() {
        assertThat(
            PartialGapBuffer("").apply {
                replace(0, 0, "ABC")
                replace(1, 2, "XY")
            }
        ).hasChars("AXYC")
    }

    @Test
    fun replaceTest_one_char_with_two_chars_from_tail() {
        assertThat(
            PartialGapBuffer("").apply {
                replace(0, 0, "ABC")
                replace(2, 3, "XY")
            }
        ).hasChars("ABXY")
    }

    @Test
    fun replaceTest_two_chars_with_two_chars_from_head() {
        assertThat(
            PartialGapBuffer("").apply {
                replace(0, 0, "ABC")
                replace(0, 2, "XY")
            }
        ).hasChars("XYC")
    }

    @Test
    fun replaceTest_two_chars_with_two_chars_from_tail() {
        assertThat(
            PartialGapBuffer("").apply {
                replace(0, 0, "ABC")
                replace(1, 3, "XY")
            }
        ).hasChars("AXY")
    }

    @Test
    fun replaceTest_three_chars_with_two_char() {
        assertThat(
            PartialGapBuffer("").apply {
                replace(0, 0, "ABC")
                replace(0, 3, "XY")
            }
        ).hasChars("XY")
    }

    @Test
    fun replaceTest_fromExistingText_head() {
        assertThat(
            PartialGapBuffer("ABC").apply {
                replace(0, 1, "X")
            }
        ).hasChars("XBC")
    }

    @Test
    fun replaceTest_fromExistingText_middle() {
        assertThat(
            PartialGapBuffer("ABC").apply {
                replace(1, 2, "X")
            }
        ).hasChars("AXC")
    }

    @Test
    fun replaceTest_fromExistingText_tail() {
        assertThat(
            PartialGapBuffer("ABC").apply {
                replace(2, 3, "X")
            }
        ).hasChars("ABX")
    }

    @Test
    fun replaceTest_fromExistingText_two_chars_with_one_char_from_head() {
        assertThat(
            PartialGapBuffer("ABC").apply {
                replace(0, 2, "X")
            }
        ).hasChars("XC")
    }

    @Test
    fun replaceTest_fromExistingText_two_chars_with_one_char_from_tail() {
        assertThat(
            PartialGapBuffer("ABC").apply {
                replace(1, 3, "X")
            }
        ).hasChars("AX")
    }

    @Test
    fun replaceTest_fromExistingText_three_chars() {
        assertThat(
            PartialGapBuffer("ABC").apply {
                replace(0, 3, "X")
            }
        ).hasChars("X")
    }

    @Test
    fun replaceTest_fromExistingText_one_char_with_two_chars_from_head() {
        assertThat(
            PartialGapBuffer("ABC").apply {
                replace(0, 1, "XY")
            }
        ).hasChars("XYBC")
    }

    @Test
    fun replaceTest_fromExistingText_one_char_with_two_chars_from_middle() {
        assertThat(
            PartialGapBuffer("ABC").apply {
                replace(1, 2, "XY")
            }
        ).hasChars("AXYC")
    }

    @Test
    fun replaceTest_fromExistingText_one_char_with_two_chars_from_tail() {
        assertThat(
            PartialGapBuffer("ABC").apply {
                replace(2, 3, "XY")
            }
        ).hasChars("ABXY")
    }

    @Test
    fun replaceTest_fromExistingText_two_chars_with_two_chars_from_head() {
        assertThat(
            PartialGapBuffer("ABC").apply {
                replace(0, 2, "XY")
            }
        ).hasChars("XYC")
    }

    @Test
    fun replaceTest_fromExistingText_two_chars_with_two_chars_from_tail() {
        assertThat(
            PartialGapBuffer("ABC").apply {
                replace(1, 3, "XY")
            }
        ).hasChars("AXY")
    }

    @Test
    fun replaceTest_fromExistingText_three_chars_with_three_chars() {
        assertThat(
            PartialGapBuffer("ABC").apply {
                replace(0, 3, "XY")
            }
        ).hasChars("XY")
    }

    @Test
    fun replace_throws_whenStartGreaterThanEnd() {
        val buffer = PartialGapBuffer("ABCD")

        val error = assertFailsWith {
            buffer.replace(3, 2, "")
        }
        assertThat(error).hasMessageThat().contains("3 > 2")
    }

    @Test
    fun replace_throws_whenStartNegative() {
        val buffer = PartialGapBuffer("ABCD")

        val error = assertFailsWith {
            buffer.replace(-1, 2, "XY")
        }
        assertThat(error).hasMessageThat().contains("-1")
    }

    // Compare with the result of StringBuffer. We trust the StringBuffer works correctly
    private fun assertReplace(
        start: Int,
        end: Int,
        str: String,
        sb: StringBuffer,
        gb: PartialGapBuffer
    ) {
        sb.replace(start, end, str)
        gb.replace(start, end, str)
        assertThat(gb).hasChars(sb.toString())
    }

    private val LONG_INIT_TEXT = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".repeat(256)
    private val SHORT_TEXT = "A"
    private val MEDIUM_TEXT = "Hello, World"
    private val LONG_TEXT = "abcdefghijklmnopqrstuvwxyz".repeat(16)

    @Test
    fun longTextTest_keep_insertion() {
        val sb = StringBuffer(LONG_INIT_TEXT)
        val gb = PartialGapBuffer(LONG_INIT_TEXT)

        var c = 256 // cursor
        assertReplace(c, c, SHORT_TEXT, sb, gb)
        c += SHORT_TEXT.length
        assertReplace(c, c, MEDIUM_TEXT, sb, gb)
        c += MEDIUM_TEXT.length
        assertReplace(c, c, LONG_TEXT, sb, gb)
        c += LONG_TEXT.length
        assertReplace(c, c, MEDIUM_TEXT, sb, gb)
        c += MEDIUM_TEXT.length
        assertReplace(c, c, SHORT_TEXT, sb, gb)
    }

    @Test
    fun longTextTest_keep_deletion() {
        val sb = StringBuffer(LONG_INIT_TEXT)
        val gb = PartialGapBuffer(LONG_INIT_TEXT)

        var c = 2048 // cursor
        // Forward deletion
        assertReplace(c, c + 10, "", sb, gb)
        assertReplace(c, c + 100, "", sb, gb)
        assertReplace(c, c + 1000, "", sb, gb)

        // Backspacing
        assertReplace(c - 10, c, "", sb, gb)
        c -= 10
        assertReplace(c - 100, c, "", sb, gb)
        c -= 100
        assertReplace(c - 1000, c, "", sb, gb)
    }

    @Test
    fun longTextTest_farInput() {
        val sb = StringBuffer(LONG_INIT_TEXT)
        val gb = PartialGapBuffer(LONG_INIT_TEXT)

        assertReplace(1024, 1024, "Hello, World", sb, gb)
        assertReplace(128, 128, LONG_TEXT, sb, gb)
    }

    @Test
    fun randomInsertDeleteStressTest() {
        val sb = StringBuffer(LONG_INIT_TEXT)
        val gb = PartialGapBuffer(LONG_INIT_TEXT)

        val r = Random(10 /* fix the seed for reproduction */)

        val insertTexts = arrayOf(SHORT_TEXT, MEDIUM_TEXT, LONG_TEXT)
        val delLengths = arrayOf(1, 10, 100)

        var c = LONG_INIT_TEXT.length / 2

        for (i in 0..100) {
            when (r.nextInt() % 4) {
                0 -> { // insert
                    val txt = insertTexts.random(r)
                    assertReplace(c, c, txt, sb, gb)
                    c += txt.length
                }
                1 -> { // forward delete
                    assertReplace(c, c + delLengths.random(r), "", sb, gb)
                }
                2 -> { // backspacing
                    val len = delLengths.random(r)
                    assertReplace(c - len, c, "", sb, gb)
                    c -= len
                }
                3 -> { // replacing
                    val txt = insertTexts.random(r)
                    val len = delLengths.random(r)

                    assertReplace(c, c + len, txt, sb, gb)
                }
            }
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy