1/* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16package android.support.text.emoji.widget; 17 18import static junit.framework.Assert.assertSame; 19import static junit.framework.TestCase.assertNotNull; 20 21import static org.hamcrest.Matchers.arrayWithSize; 22import static org.hamcrest.Matchers.instanceOf; 23import static org.junit.Assert.assertEquals; 24import static org.junit.Assert.assertThat; 25import static org.mockito.Matchers.any; 26import static org.mockito.Matchers.anyInt; 27import static org.mockito.Matchers.anyObject; 28import static org.mockito.Matchers.same; 29import static org.mockito.Mockito.mock; 30import static org.mockito.Mockito.never; 31import static org.mockito.Mockito.reset; 32import static org.mockito.Mockito.times; 33import static org.mockito.Mockito.verify; 34import static org.mockito.Mockito.withSettings; 35 36import android.support.test.filters.SmallTest; 37import android.support.test.runner.AndroidJUnit4; 38import android.support.text.emoji.EmojiSpan; 39import android.text.Editable; 40import android.text.SpanWatcher; 41import android.text.Spannable; 42import android.text.Spanned; 43import android.text.TextWatcher; 44import android.text.style.QuoteSpan; 45 46import junit.framework.Assert; 47 48import org.junit.Before; 49import org.junit.Test; 50import org.junit.runner.RunWith; 51 52@SmallTest 53@RunWith(AndroidJUnit4.class) 54public class SpannableBuilderTest { 55 56 private TextWatcher mWatcher; 57 private Class mClass; 58 59 @Before 60 public void setup() { 61 mWatcher = mock(TextWatcher.class, withSettings().extraInterfaces(SpanWatcher.class)); 62 mClass = mWatcher.getClass(); 63 } 64 65 @Test 66 public void testConstructor() { 67 new SpannableBuilder(mClass); 68 69 new SpannableBuilder(mClass, "abc"); 70 71 new SpannableBuilder(mClass, "abc", 0, 3); 72 73 // test spannable copying? do I need it? 74 } 75 76 @Test 77 public void testSubSequence() { 78 final SpannableBuilder spannable = new SpannableBuilder(mClass, "abc"); 79 final QuoteSpan span1 = mock(QuoteSpan.class); 80 final QuoteSpan span2 = mock(QuoteSpan.class); 81 spannable.setSpan(span1, 0, 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 82 spannable.setSpan(span2, 2, 3, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 83 84 final CharSequence subsequence = spannable.subSequence(0, 1); 85 assertNotNull(subsequence); 86 assertThat(subsequence, instanceOf(SpannableBuilder.class)); 87 88 final QuoteSpan[] spans = spannable.getSpans(0, 1, QuoteSpan.class); 89 assertThat(spans, arrayWithSize(1)); 90 assertSame(spans[0], span1); 91 } 92 93 @Test 94 public void testSetAndGetSpan() { 95 final SpannableBuilder spannable = new SpannableBuilder(mClass, "abcde"); 96 spannable.setSpan(mWatcher, 1, 2, Spanned.SPAN_INCLUSIVE_INCLUSIVE); 97 98 // getSpans should return the span 99 Object[] spans = spannable.getSpans(0, spannable.length(), mClass); 100 assertNotNull(spans); 101 assertThat(spans, arrayWithSize(1)); 102 assertSame(mWatcher, spans[0]); 103 104 // span attributes should be correct 105 assertEquals(1, spannable.getSpanStart(mWatcher)); 106 assertEquals(2, spannable.getSpanEnd(mWatcher)); 107 assertEquals(Spanned.SPAN_INCLUSIVE_INCLUSIVE, spannable.getSpanFlags(mWatcher)); 108 109 // should remove the span 110 spannable.removeSpan(mWatcher); 111 spans = spannable.getSpans(0, spannable.length(), QuoteSpan.class); 112 assertNotNull(spans); 113 assertThat(spans, arrayWithSize(0)); 114 } 115 116 @Test 117 public void testNextSpanTransition() { 118 final SpannableBuilder spannable = new SpannableBuilder(mClass, "abcde"); 119 spannable.setSpan(mWatcher, 1, 2, Spanned.SPAN_INCLUSIVE_INCLUSIVE); 120 final int start = spannable.nextSpanTransition(0, spannable.length(), mClass); 121 Assert.assertEquals(1, start); 122 } 123 124 @Test 125 public void testBlocksSpanCallbacks_forEmojiSpans() { 126 final EmojiSpan span = mock(EmojiSpan.class); 127 final SpannableBuilder spannable = new SpannableBuilder(mClass, "123456"); 128 spannable.setSpan(mWatcher, 0, spannable.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); 129 spannable.setSpan(span, 1, 2, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 130 reset(mWatcher); 131 132 spannable.delete(0, 3); 133 134 // verify that characters are deleted 135 assertEquals("456", spannable.toString()); 136 // verify EmojiSpan is deleted 137 EmojiSpan[] spans = spannable.getSpans(0, spannable.length(), EmojiSpan.class); 138 assertThat(spans, arrayWithSize(0)); 139 140 // verify the call to span callbacks are blocked 141 verify((SpanWatcher) mWatcher, never()).onSpanRemoved(any(Spannable.class), 142 same(span), anyInt(), anyInt()); 143 verify((SpanWatcher) mWatcher, never()).onSpanAdded(any(Spannable.class), 144 same(span), anyInt(), anyInt()); 145 verify((SpanWatcher) mWatcher, never()).onSpanChanged(any(Spannable.class), 146 same(span), anyInt(), anyInt(), anyInt(), anyInt()); 147 148 // verify the call to TextWatcher callbacks are called 149 verify(mWatcher, times(1)).beforeTextChanged(any(CharSequence.class), anyInt(), 150 anyInt(), anyInt()); 151 verify(mWatcher, times(1)).onTextChanged(any(CharSequence.class), anyInt(), anyInt(), 152 anyInt()); 153 verify(mWatcher, times(1)).afterTextChanged(any(Editable.class)); 154 } 155 156 @Test 157 public void testDoesNotBlockSpanCallbacks_forNonEmojiSpans() { 158 final QuoteSpan span = mock(QuoteSpan.class); 159 final SpannableBuilder spannable = new SpannableBuilder(mClass, "123456"); 160 spannable.setSpan(mWatcher, 0, spannable.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); 161 spannable.setSpan(span, 1, 2, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 162 reset(mWatcher); 163 164 spannable.delete(0, 3); 165 166 // verify that characters are deleted 167 assertEquals("456", spannable.toString()); 168 // verify QuoteSpan is deleted 169 QuoteSpan[] spans = spannable.getSpans(0, spannable.length(), QuoteSpan.class); 170 assertThat(spans, arrayWithSize(0)); 171 172 // verify the call to span callbacks are not blocked 173 verify((SpanWatcher) mWatcher, times(1)).onSpanRemoved(any(Spannable.class), 174 anyObject(), anyInt(), anyInt()); 175 176 // verify the call to TextWatcher callbacks are called 177 verify(mWatcher, times(1)).beforeTextChanged(any(CharSequence.class), anyInt(), anyInt(), 178 anyInt()); 179 verify(mWatcher, times(1)).onTextChanged(any(CharSequence.class), anyInt(), anyInt(), 180 anyInt()); 181 verify(mWatcher, times(1)).afterTextChanged(any(Editable.class)); 182 } 183 184 @Test 185 public void testDoesNotBlockSpanCallbacksForOtherWatchers() { 186 final TextWatcher textWatcher = mock(TextWatcher.class); 187 final SpanWatcher spanWatcher = mock(SpanWatcher.class); 188 189 final EmojiSpan span = mock(EmojiSpan.class); 190 final SpannableBuilder spannable = new SpannableBuilder(mClass, "123456"); 191 spannable.setSpan(textWatcher, 0, spannable.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); 192 spannable.setSpan(spanWatcher, 0, spannable.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); 193 spannable.setSpan(span, 1, 2, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 194 reset(textWatcher); 195 196 spannable.delete(0, 3); 197 198 // verify that characters are deleted 199 assertEquals("456", spannable.toString()); 200 // verify EmojiSpan is deleted 201 EmojiSpan[] spans = spannable.getSpans(0, spannable.length(), EmojiSpan.class); 202 assertThat(spans, arrayWithSize(0)); 203 204 // verify the call to span callbacks are blocked 205 verify(spanWatcher, times(1)).onSpanRemoved(any(Spannable.class), same(span), 206 anyInt(), anyInt()); 207 208 // verify the call to TextWatcher callbacks are called 209 verify(textWatcher, times(1)).beforeTextChanged(any(CharSequence.class), anyInt(), 210 anyInt(), anyInt()); 211 verify(textWatcher, times(1)).onTextChanged(any(CharSequence.class), anyInt(), anyInt(), 212 anyInt()); 213 verify(textWatcher, times(1)).afterTextChanged(any(Editable.class)); 214 } 215} 216