blob: 1411e5371b370ccb5d19c8def9096829c26cc577 [file] [log] [blame]
/*
* Copyright 2021 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.platform
import android.content.ClipDescription
import android.content.ClipboardManager
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shadow
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.ExperimentalTextApi
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.font.FontSynthesis
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.BaselineShift
import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.text.style.TextGeometricTransform
import androidx.compose.ui.text.withStyle
import androidx.compose.ui.unit.sp
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import androidx.test.platform.app.InstrumentationRegistry
import com.google.common.truth.Truth.assertThat
import com.nhaarman.mockitokotlin2.mock
import com.nhaarman.mockitokotlin2.whenever
import org.junit.Test
import org.junit.runner.RunWith
@SmallTest
@RunWith(AndroidJUnit4::class)
@OptIn(ExperimentalTextApi::class)
class AndroidClipboardManagerTest {
private val context = InstrumentationRegistry.getInstrumentation().context
@Test
fun annotatedString_singleSpanStyle_convertToCharSequenceAndRecover() {
val annotatedString = buildAnnotatedString {
withStyle(SpanStyle(color = Color.Yellow)) {
append("Hello ")
}
append("World")
}
assertEncodeAndDecode(annotatedString)
}
@Test
fun annotatedString_multipleSpanStyle_convertToCharSequenceAndRecover() {
val annotatedString = buildAnnotatedString {
withStyle(SpanStyle(color = Color.Yellow)) {
append("Hello")
}
append("World")
withStyle(SpanStyle(letterSpacing = 0.4.sp)) {
append("Hello")
}
}
assertEncodeAndDecode(annotatedString)
}
@Test
fun annotatedString_nestedSpanStyle_convertToCharSequenceAndRecover() {
val annotatedString = buildAnnotatedString {
withStyle(SpanStyle(letterSpacing = 0.4.sp)) {
withStyle(SpanStyle(color = Color.Yellow)) {
append("Hello")
}
append("World")
}
append("Hello")
}
assertEncodeAndDecode(annotatedString)
}
@Test
fun spanStyle_default_encodeAndDecode() {
val spanStyle = SpanStyle()
assertEncodeAndDecode(spanStyle)
}
@Test
fun spanStyle_withColor_encodeAndDecode() {
val spanStyle = SpanStyle(color = Color.Cyan)
assertEncodeAndDecode(spanStyle)
}
@Test
fun spanStyle_withFontSize_encodeAndDecode() {
val spanStyle = SpanStyle(fontSize = 16.sp)
assertEncodeAndDecode(spanStyle)
}
@Test
fun spanStyle_withFontWeight_encodeAndDecode() {
val spanStyle = SpanStyle(fontWeight = FontWeight.ExtraBold)
assertEncodeAndDecode(spanStyle)
}
@Test
fun spanStyle_withFontStyle_encodeAndDecode() {
val spanStyle = SpanStyle(fontStyle = FontStyle.Italic)
assertEncodeAndDecode(spanStyle)
}
@Test
fun spanStyle_withFontSynthesis_encodeAndDecode() {
val spanStyle = SpanStyle(fontSynthesis = FontSynthesis.Weight)
assertEncodeAndDecode(spanStyle)
}
@Test
fun spanStyle_withFontFeatureSettings_encodeAndDecode() {
val spanStyle = SpanStyle(fontFeatureSettings = "smcp")
assertEncodeAndDecode(spanStyle)
}
@Test
fun spanStyle_withLetterSpacing_encodeAndDecode() {
val spanStyle = SpanStyle(letterSpacing = 0.3.sp)
assertEncodeAndDecode(spanStyle)
}
@Test
fun spanStyle_withBaselineShift_encodeAndDecode() {
val spanStyle = SpanStyle(baselineShift = BaselineShift.Subscript)
assertEncodeAndDecode(spanStyle)
}
@Test
fun spanStyle_withTextGeometricTransform_encodeAndDecode() {
val spanStyle = SpanStyle(
textGeometricTransform = TextGeometricTransform(
scaleX = 1.5f,
skewX = 0.3f
)
)
assertEncodeAndDecode(spanStyle)
}
@Test
fun spanStyle_withBackground_encodeAndDecode() {
val spanStyle = SpanStyle(background = Color.Cyan)
assertEncodeAndDecode(spanStyle)
}
@Test
fun spanStyle_withTextDecoration_encodeAndDecode() {
val spanStyle = SpanStyle(textDecoration = TextDecoration.Underline)
assertEncodeAndDecode(spanStyle)
}
@Test
fun spanStyle_withShadow_encodeAndDecode() {
val spanStyle = SpanStyle(
shadow = Shadow(
color = Color.Cyan,
offset = Offset(1f, 2f),
blurRadius = 3f
)
)
assertEncodeAndDecode(spanStyle)
}
@Test
fun spanStyle_withEverything_encodeAndDecode() {
val spanStyle = SpanStyle(
color = Color.Cyan,
fontSize = 16.sp,
fontWeight = FontWeight.ExtraLight,
fontSynthesis = FontSynthesis.Weight,
fontFeatureSettings = "smcp",
letterSpacing = 0.3.sp,
baselineShift = BaselineShift.Superscript,
textGeometricTransform = TextGeometricTransform(
scaleX = 1.1f,
skewX = 0.1f
),
background = Color.Yellow,
textDecoration = TextDecoration.LineThrough,
shadow = Shadow(
color = Color.Cyan,
offset = Offset(1f, 2f),
blurRadius = 3f
)
)
assertEncodeAndDecode(spanStyle)
}
@Test
fun getText_whenHasPrimary_butNoPrimary_returnsNull() {
val clipboardManager = mock<ClipboardManager>()
whenever(clipboardManager.hasPrimaryClip()).thenReturn(true)
whenever(clipboardManager.primaryClip).thenReturn(null)
val subject = AndroidClipboardManager(clipboardManager)
assertThat(subject.getText()).isNull()
}
@Test
fun hasText_whenHasPrimaryClipDescription_Text_returnsTrue() {
val clipboardManager = mock<ClipboardManager>()
val clipDescription = mock<ClipDescription>()
whenever(clipboardManager.primaryClipDescription).thenReturn(clipDescription)
whenever(clipDescription.hasMimeType("text/*")).thenReturn(true)
val subject = AndroidClipboardManager(clipboardManager)
assertThat(subject.hasText()).isTrue()
}
@Test
fun hasText_whenHasPrimaryClipDescription_notText_returnsFalse() {
val clipboardManager = mock<ClipboardManager>()
val clipDescription = mock<ClipDescription>()
whenever(clipboardManager.primaryClipDescription).thenReturn(clipDescription)
whenever(clipDescription.hasMimeType("text/*")).thenReturn(false)
val subject = AndroidClipboardManager(clipboardManager)
assertThat(subject.hasText()).isFalse()
}
@Test
fun hasText_whenNoPrimaryClipDescription_returnsFalse() {
val clipboardManager = mock<ClipboardManager>()
whenever(clipboardManager.primaryClipDescription).thenReturn(null)
val subject = AndroidClipboardManager(clipboardManager)
assertThat(subject.hasText()).isFalse()
}
private fun assertEncodeAndDecode(spanStyle: SpanStyle) {
val encoder = EncodeHelper()
encoder.encode(spanStyle)
val encodedString = encoder.encodedString()
val decodeHelper = DecodeHelper(encodedString)
val decodedSpanStyle = decodeHelper.decodeSpanStyle()
assertThat(spanStyle).isEqualTo(decodedSpanStyle)
}
private fun assertEncodeAndDecode(annotatedString: AnnotatedString) {
val charSequence = annotatedString.convertToCharSequence()
val recoveredAnnotatedString = charSequence.convertToAnnotatedString()
assertThat(annotatedString).isEqualTo(recoveredAnnotatedString)
}
}