• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  */
16 package androidx.emoji.widget;
17 
18 import static org.hamcrest.Matchers.arrayWithSize;
19 import static org.hamcrest.Matchers.instanceOf;
20 import static org.junit.Assert.assertEquals;
21 import static org.junit.Assert.assertNotNull;
22 import static org.junit.Assert.assertSame;
23 import static org.junit.Assert.assertThat;
24 import static org.mockito.Matchers.any;
25 import static org.mockito.Matchers.anyInt;
26 import static org.mockito.Matchers.anyObject;
27 import static org.mockito.Matchers.same;
28 import static org.mockito.Mockito.mock;
29 import static org.mockito.Mockito.never;
30 import static org.mockito.Mockito.reset;
31 import static org.mockito.Mockito.times;
32 import static org.mockito.Mockito.verify;
33 import static org.mockito.Mockito.withSettings;
34 
35 import android.support.test.filters.SmallTest;
36 import android.support.test.runner.AndroidJUnit4;
37 import android.text.Editable;
38 import android.text.SpanWatcher;
39 import android.text.Spannable;
40 import android.text.Spanned;
41 import android.text.TextWatcher;
42 import android.text.style.QuoteSpan;
43 
44 import androidx.emoji.text.EmojiSpan;
45 
46 import org.junit.Before;
47 import org.junit.Test;
48 import org.junit.runner.RunWith;
49 
50 @SmallTest
51 @RunWith(AndroidJUnit4.class)
52 public class SpannableBuilderTest {
53 
54     private TextWatcher mWatcher;
55     private Class mClass;
56 
57     @Before
setup()58     public void setup() {
59         mWatcher = mock(TextWatcher.class, withSettings().extraInterfaces(SpanWatcher.class));
60         mClass = mWatcher.getClass();
61     }
62 
63     @Test
testConstructor()64     public void testConstructor() {
65         new SpannableBuilder(mClass);
66 
67         new SpannableBuilder(mClass, "abc");
68 
69         new SpannableBuilder(mClass, "abc", 0, 3);
70 
71         // test spannable copying? do I need it?
72     }
73 
74     @Test
testSubSequence()75     public void testSubSequence() {
76         final SpannableBuilder spannable = new SpannableBuilder(mClass, "abc");
77         final QuoteSpan span1 = mock(QuoteSpan.class);
78         final QuoteSpan span2 = mock(QuoteSpan.class);
79         spannable.setSpan(span1, 0, 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
80         spannable.setSpan(span2, 2, 3, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
81 
82         final CharSequence subsequence = spannable.subSequence(0, 1);
83         assertNotNull(subsequence);
84         assertThat(subsequence, instanceOf(SpannableBuilder.class));
85 
86         final QuoteSpan[] spans = spannable.getSpans(0, 1, QuoteSpan.class);
87         assertThat(spans, arrayWithSize(1));
88         assertSame(spans[0], span1);
89     }
90 
91     @Test
testSetAndGetSpan()92     public void testSetAndGetSpan() {
93         final SpannableBuilder spannable = new SpannableBuilder(mClass, "abcde");
94         spannable.setSpan(mWatcher, 1, 2, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
95 
96         // getSpans should return the span
97         Object[] spans = spannable.getSpans(0, spannable.length(), mClass);
98         assertNotNull(spans);
99         assertThat(spans, arrayWithSize(1));
100         assertSame(mWatcher, spans[0]);
101 
102         // span attributes should be correct
103         assertEquals(1, spannable.getSpanStart(mWatcher));
104         assertEquals(2, spannable.getSpanEnd(mWatcher));
105         assertEquals(Spanned.SPAN_INCLUSIVE_INCLUSIVE, spannable.getSpanFlags(mWatcher));
106 
107         // should remove the span
108         spannable.removeSpan(mWatcher);
109         spans = spannable.getSpans(0, spannable.length(), QuoteSpan.class);
110         assertNotNull(spans);
111         assertThat(spans, arrayWithSize(0));
112     }
113 
114     @Test
testNextSpanTransition()115     public void testNextSpanTransition() {
116         final SpannableBuilder spannable = new SpannableBuilder(mClass, "abcde");
117         spannable.setSpan(mWatcher, 1, 2, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
118         final int start = spannable.nextSpanTransition(0, spannable.length(), mClass);
119         assertEquals(1, start);
120     }
121 
122     @Test
testBlocksSpanCallbacks_forEmojiSpans()123     public void testBlocksSpanCallbacks_forEmojiSpans() {
124         final EmojiSpan span = mock(EmojiSpan.class);
125         final SpannableBuilder spannable = new SpannableBuilder(mClass, "123456");
126         spannable.setSpan(mWatcher, 0, spannable.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
127         spannable.setSpan(span, 1, 2, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
128         reset(mWatcher);
129 
130         spannable.delete(0, 3);
131 
132         // verify that characters are deleted
133         assertEquals("456", spannable.toString());
134         // verify EmojiSpan is deleted
135         EmojiSpan[] spans = spannable.getSpans(0, spannable.length(), EmojiSpan.class);
136         assertThat(spans, arrayWithSize(0));
137 
138         // verify the call to span callbacks are blocked
139         verify((SpanWatcher) mWatcher, never()).onSpanRemoved(any(Spannable.class),
140                 same(span), anyInt(), anyInt());
141         verify((SpanWatcher) mWatcher, never()).onSpanAdded(any(Spannable.class),
142                 same(span), anyInt(), anyInt());
143         verify((SpanWatcher) mWatcher, never()).onSpanChanged(any(Spannable.class),
144                 same(span), anyInt(), anyInt(), anyInt(), anyInt());
145 
146         // verify the call to TextWatcher callbacks are called
147         verify(mWatcher, times(1)).beforeTextChanged(any(CharSequence.class), anyInt(),
148                 anyInt(), anyInt());
149         verify(mWatcher, times(1)).onTextChanged(any(CharSequence.class), anyInt(), anyInt(),
150                 anyInt());
151         verify(mWatcher, times(1)).afterTextChanged(any(Editable.class));
152     }
153 
154     @Test
testDoesNotBlockSpanCallbacks_forNonEmojiSpans()155     public void testDoesNotBlockSpanCallbacks_forNonEmojiSpans() {
156         final QuoteSpan span = mock(QuoteSpan.class);
157         final SpannableBuilder spannable = new SpannableBuilder(mClass, "123456");
158         spannable.setSpan(mWatcher, 0, spannable.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
159         spannable.setSpan(span, 1, 2, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
160         reset(mWatcher);
161 
162         spannable.delete(0, 3);
163 
164         // verify that characters are deleted
165         assertEquals("456", spannable.toString());
166         // verify QuoteSpan is deleted
167         QuoteSpan[] spans = spannable.getSpans(0, spannable.length(), QuoteSpan.class);
168         assertThat(spans, arrayWithSize(0));
169 
170         // verify the call to span callbacks are not blocked
171         verify((SpanWatcher) mWatcher, times(1)).onSpanRemoved(any(Spannable.class),
172                 anyObject(), anyInt(), anyInt());
173 
174         // verify the call to TextWatcher callbacks are called
175         verify(mWatcher, times(1)).beforeTextChanged(any(CharSequence.class), anyInt(), anyInt(),
176                 anyInt());
177         verify(mWatcher, times(1)).onTextChanged(any(CharSequence.class), anyInt(), anyInt(),
178                 anyInt());
179         verify(mWatcher, times(1)).afterTextChanged(any(Editable.class));
180     }
181 
182     @Test
testDoesNotBlockSpanCallbacksForOtherWatchers()183     public void testDoesNotBlockSpanCallbacksForOtherWatchers() {
184         final TextWatcher textWatcher = mock(TextWatcher.class);
185         final SpanWatcher spanWatcher = mock(SpanWatcher.class);
186 
187         final EmojiSpan span = mock(EmojiSpan.class);
188         final SpannableBuilder spannable = new SpannableBuilder(mClass, "123456");
189         spannable.setSpan(textWatcher, 0, spannable.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
190         spannable.setSpan(spanWatcher, 0, spannable.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
191         spannable.setSpan(span, 1, 2, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
192         reset(textWatcher);
193 
194         spannable.delete(0, 3);
195 
196         // verify that characters are deleted
197         assertEquals("456", spannable.toString());
198         // verify EmojiSpan is deleted
199         EmojiSpan[] spans = spannable.getSpans(0, spannable.length(), EmojiSpan.class);
200         assertThat(spans, arrayWithSize(0));
201 
202         // verify the call to span callbacks are blocked
203         verify(spanWatcher, times(1)).onSpanRemoved(any(Spannable.class), same(span),
204                 anyInt(), anyInt());
205 
206         // verify the call to TextWatcher callbacks are called
207         verify(textWatcher, times(1)).beforeTextChanged(any(CharSequence.class), anyInt(),
208                 anyInt(), anyInt());
209         verify(textWatcher, times(1)).onTextChanged(any(CharSequence.class), anyInt(), anyInt(),
210                 anyInt());
211         verify(textWatcher, times(1)).afterTextChanged(any(Editable.class));
212     }
213 }
214