• 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 android.support.text.emoji.widget;
17 
18 import static junit.framework.Assert.assertSame;
19 import static junit.framework.TestCase.assertNotNull;
20 
21 import static org.hamcrest.Matchers.arrayWithSize;
22 import static org.hamcrest.Matchers.instanceOf;
23 import static org.junit.Assert.assertEquals;
24 import static org.junit.Assert.assertThat;
25 import static org.mockito.Matchers.any;
26 import static org.mockito.Matchers.anyInt;
27 import static org.mockito.Matchers.anyObject;
28 import static org.mockito.Matchers.same;
29 import static org.mockito.Mockito.mock;
30 import static org.mockito.Mockito.never;
31 import static org.mockito.Mockito.reset;
32 import static org.mockito.Mockito.times;
33 import static org.mockito.Mockito.verify;
34 import static org.mockito.Mockito.withSettings;
35 
36 import android.support.test.filters.SmallTest;
37 import android.support.test.runner.AndroidJUnit4;
38 import android.support.text.emoji.EmojiSpan;
39 import android.text.Editable;
40 import android.text.SpanWatcher;
41 import android.text.Spannable;
42 import android.text.Spanned;
43 import android.text.TextWatcher;
44 import android.text.style.QuoteSpan;
45 
46 import junit.framework.Assert;
47 
48 import org.junit.Before;
49 import org.junit.Test;
50 import org.junit.runner.RunWith;
51 
52 @SmallTest
53 @RunWith(AndroidJUnit4.class)
54 public class SpannableBuilderTest {
55 
56     private TextWatcher mWatcher;
57     private Class mClass;
58 
59     @Before
setup()60     public void setup() {
61         mWatcher = mock(TextWatcher.class, withSettings().extraInterfaces(SpanWatcher.class));
62         mClass = mWatcher.getClass();
63     }
64 
65     @Test
testConstructor()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
testSubSequence()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
testSetAndGetSpan()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
testNextSpanTransition()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
testBlocksSpanCallbacks_forEmojiSpans()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
testDoesNotBlockSpanCallbacks_forNonEmojiSpans()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
testDoesNotBlockSpanCallbacksForOtherWatchers()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