1 /* 2 * Copyright (C) 2008 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 17 package android.widget.cts; 18 19 import static org.junit.Assert.assertEquals; 20 import static org.junit.Assert.assertFalse; 21 import static org.junit.Assert.assertNotNull; 22 import static org.junit.Assert.assertSame; 23 import static org.junit.Assert.assertTrue; 24 import static org.junit.Assert.fail; 25 26 import android.app.Activity; 27 import android.content.Context; 28 import android.support.test.annotation.UiThreadTest; 29 import android.support.test.filters.SmallTest; 30 import android.support.test.rule.ActivityTestRule; 31 import android.support.test.runner.AndroidJUnit4; 32 import android.util.AttributeSet; 33 import android.util.Xml; 34 import android.view.Gravity; 35 import android.view.View; 36 import android.view.ViewGroup; 37 import android.view.ViewGroup.OnHierarchyChangeListener; 38 import android.widget.LinearLayout; 39 import android.widget.RadioButton; 40 import android.widget.RadioGroup; 41 import android.widget.RadioGroup.LayoutParams; 42 import android.widget.RadioGroup.OnCheckedChangeListener; 43 import android.widget.RelativeLayout; 44 45 import org.junit.Before; 46 import org.junit.Rule; 47 import org.junit.Test; 48 import org.junit.runner.RunWith; 49 import org.xmlpull.v1.XmlPullParser; 50 import org.xmlpull.v1.XmlPullParserException; 51 52 import java.io.IOException; 53 import java.util.Vector; 54 55 /** 56 * Test {@link RadioGroup}. 57 */ 58 @SmallTest 59 @RunWith(AndroidJUnit4.class) 60 public class RadioGroupTest { 61 private Activity mActivity; 62 private RadioGroup mRadioGroup; 63 64 @Rule 65 public ActivityTestRule<RadioGroupCtsActivity> mActivityRule = 66 new ActivityTestRule<>(RadioGroupCtsActivity.class); 67 68 @Before 69 public void setup() { 70 mActivity = mActivityRule.getActivity(); 71 mRadioGroup = (RadioGroup) mActivity.findViewById(R.id.radio_group); 72 } 73 74 @Test 75 public void testConstructors() { 76 new RadioGroup(mActivity); 77 78 AttributeSet attrs = getAttributeSet(R.layout.radiogroup_1); 79 new RadioGroup(mActivity, attrs); 80 new RadioGroup(mActivity, null); 81 } 82 83 @UiThreadTest 84 @Test 85 public void testSetOnHierarchyChangeListener() { 86 MockOnHierarchyChangeListener listener = new MockOnHierarchyChangeListener(); 87 mRadioGroup.setOnHierarchyChangeListener(listener); 88 89 View button3 = mRadioGroup.findViewById(R.id.radio_button_3); 90 listener.reset(); 91 mRadioGroup.removeView(button3); 92 assertSame(mRadioGroup, listener.getOnChildViewRemovedParentParam()); 93 assertSame(button3, listener.getOnChildViewRemovedChildParam()); 94 95 listener.reset(); 96 mRadioGroup.addView(button3); 97 assertSame(mRadioGroup, listener.getOnChildViewAddedParentParam()); 98 assertSame(button3, listener.getOnChildViewAddedChildParam()); 99 100 // Set listener to null 101 mRadioGroup.setOnHierarchyChangeListener(null); 102 // and no exceptions thrown in the following method calls 103 mRadioGroup.removeView(button3); 104 mRadioGroup.addView(button3); 105 } 106 107 @UiThreadTest 108 @Test 109 public void testInternalPassThroughHierarchyChangeListener() { 110 RadioButton newButton = new RadioButton(mActivity); 111 112 assertEquals(View.NO_ID, newButton.getId()); 113 mRadioGroup.addView(newButton, new RadioGroup.LayoutParams( 114 RadioGroup.LayoutParams.WRAP_CONTENT, RadioGroup.LayoutParams.WRAP_CONTENT)); 115 // aapt-generated IDs have a nonzero high byte; check that the ID generated by 116 // RadioGroup falls within a range that will not collide with aapt IDs. 117 assertEquals(0, newButton.getId() & 0xFF000000); 118 } 119 120 @UiThreadTest 121 @Test 122 public void testInternalCheckedStateTracker() { 123 RadioButton newButton = new RadioButton(mActivity); 124 // inject the tracker to the button when the button is added by 125 // CompoundButton#setOnCheckedChangeWidgetListener(OnCheckedChangeListener) 126 mRadioGroup.addView(newButton, new RadioGroup.LayoutParams( 127 RadioGroup.LayoutParams.WRAP_CONTENT, RadioGroup.LayoutParams.WRAP_CONTENT)); 128 MockOnCheckedChangeListener listener = new MockOnCheckedChangeListener(); 129 mRadioGroup.setOnCheckedChangeListener(listener); 130 131 listener.reset(); 132 newButton.setChecked(true); 133 // the tracker informs the checked state change of the button to the group 134 assertHasCalledOnCheckedChanged(listener); 135 136 listener.reset(); 137 // the tracker informs the checked state change of the button to the group 138 newButton.setChecked(false); 139 assertHasCalledOnCheckedChanged(listener); 140 141 // remove the tracker from the button when the button is removed 142 mRadioGroup.removeView(newButton); 143 listener.reset(); 144 newButton.setChecked(true); 145 assertHaveNotCalledOnCheckedChanged(listener); 146 147 listener.reset(); 148 newButton.setChecked(false); 149 assertHaveNotCalledOnCheckedChanged(listener); 150 } 151 152 @UiThreadTest 153 @Test 154 public void testGetCheckedRadioButtonId() { 155 assertEquals(-1, mRadioGroup.getCheckedRadioButtonId()); 156 157 mRadioGroup.check(R.id.radio_button_0); 158 assertEquals(R.id.radio_button_0, mRadioGroup.getCheckedRadioButtonId()); 159 160 mRadioGroup.check(R.id.radio_button_3); 161 assertEquals(R.id.radio_button_3, mRadioGroup.getCheckedRadioButtonId()); 162 163 // None of the buttons inside the group has of of the following IDs 164 mRadioGroup.check(4); 165 assertEquals(4, mRadioGroup.getCheckedRadioButtonId()); 166 167 mRadioGroup.check(-1); 168 assertEquals(-1, mRadioGroup.getCheckedRadioButtonId()); 169 170 mRadioGroup.check(-3); 171 assertEquals(-3, mRadioGroup.getCheckedRadioButtonId()); 172 } 173 174 @UiThreadTest 175 @Test 176 public void testClearCheck() { 177 MockOnCheckedChangeListener listener = new MockOnCheckedChangeListener(); 178 mRadioGroup.setOnCheckedChangeListener(listener); 179 180 mRadioGroup.check(R.id.radio_button_3); 181 assertEquals(R.id.radio_button_3, mRadioGroup.getCheckedRadioButtonId()); 182 183 listener.reset(); 184 mRadioGroup.clearCheck(); 185 assertEquals(-1, mRadioGroup.getCheckedRadioButtonId()); 186 assertHasCalledOnCheckedChanged(listener); 187 // uncheck the original button 188 assertOnCheckedChangedParams(listener, 0, mRadioGroup, R.id.radio_button_3); 189 190 // None of the buttons inside the group has of of the following IDs 191 mRadioGroup.check(4); 192 assertEquals(4, mRadioGroup.getCheckedRadioButtonId()); 193 194 listener.reset(); 195 mRadioGroup.clearCheck(); 196 assertEquals(-1, mRadioGroup.getCheckedRadioButtonId()); 197 // why the method is called while none of the button is checked or unchecked? 198 assertHasCalledOnCheckedChanged(listener); 199 assertOnCheckedChangedParams(listener, 0, mRadioGroup, -1); 200 201 mRadioGroup.check(-1); 202 assertEquals(-1, mRadioGroup.getCheckedRadioButtonId()); 203 204 listener.reset(); 205 mRadioGroup.clearCheck(); 206 assertEquals(-1, mRadioGroup.getCheckedRadioButtonId()); 207 // why the method is called while none of the button is checked or unchecked? 208 assertHasCalledOnCheckedChanged(listener); 209 assertOnCheckedChangedParams(listener, 0, mRadioGroup, -1); 210 } 211 212 @UiThreadTest 213 @Test 214 public void testCheck() { 215 MockOnCheckedChangeListener listener = new MockOnCheckedChangeListener(); 216 mRadioGroup.setOnCheckedChangeListener(listener); 217 assertEquals(-1, mRadioGroup.getCheckedRadioButtonId()); 218 219 listener.reset(); 220 mRadioGroup.check(R.id.radio_button_0); 221 assertHasCalledOnCheckedChanged(listener); 222 assertOnCheckedChangedParams(listener, 0, mRadioGroup, R.id.radio_button_0); 223 224 listener.reset(); 225 mRadioGroup.check(R.id.radio_button_1); 226 assertHasCalledOnCheckedChanged(listener); 227 // uncheck the original button 228 assertOnCheckedChangedParams(listener, 0, mRadioGroup, R.id.radio_button_0); 229 // check the new button 230 assertOnCheckedChangedParams(listener, 1, mRadioGroup, R.id.radio_button_1); 231 232 listener.reset(); 233 mRadioGroup.check(-1); 234 assertHasCalledOnCheckedChanged(listener); 235 // uncheck the original button 236 assertOnCheckedChangedParams(listener, 0, mRadioGroup, R.id.radio_button_1); 237 assertOnCheckedChangedParams(listener, 1, mRadioGroup, -1); 238 239 // None of the buttons inside the group has of of the following IDs 240 listener.reset(); 241 mRadioGroup.check(-1); 242 // why the method is called while none of the inside buttons has been changed 243 assertHasCalledOnCheckedChanged(listener); 244 assertOnCheckedChangedParams(listener, 0, mRadioGroup, -1); 245 246 listener.reset(); 247 mRadioGroup.check(4); 248 // why the method is called while none of the inside buttons has been changed 249 assertHasCalledOnCheckedChanged(listener); 250 assertOnCheckedChangedParams(listener, 0, mRadioGroup, 4); 251 252 // Set listener to null 253 mRadioGroup.setOnCheckedChangeListener(null); 254 // no exceptions thrown during the following method 255 mRadioGroup.check(0); 256 } 257 258 @UiThreadTest 259 @Test 260 public void testSetOnCheckedChangeListener() { 261 MockOnCheckedChangeListener listener = new MockOnCheckedChangeListener(); 262 mRadioGroup.setOnCheckedChangeListener(listener); 263 264 listener.reset(); 265 mRadioGroup.check(R.id.radio_button_0); 266 assertHasCalledOnCheckedChanged(listener); 267 268 // does not call the method if the button the id is already checked 269 listener.reset(); 270 mRadioGroup.check(R.id.radio_button_0); 271 assertHaveNotCalledOnCheckedChanged(listener); 272 273 // call the method if none of the buttons inside the group has the id 274 listener.reset(); 275 mRadioGroup.check(-3); 276 assertHasCalledOnCheckedChanged(listener); 277 278 // does not call the method if the button the id is already checked 279 // and none of the buttons inside the group has the id 280 listener.reset(); 281 mRadioGroup.check(-3); 282 assertHaveNotCalledOnCheckedChanged(listener); 283 284 // always call the method if the checked id is -1 285 listener.reset(); 286 mRadioGroup.clearCheck(); 287 assertHasCalledOnCheckedChanged(listener); 288 289 listener.reset(); 290 mRadioGroup.check(-1); 291 assertHasCalledOnCheckedChanged(listener); 292 293 // Set listener to null 294 mRadioGroup.setOnCheckedChangeListener(null); 295 // no exceptions thrown during the following method 296 mRadioGroup.check(0); 297 } 298 299 @Test 300 public void testGenerateLayoutParams() { 301 RadioGroup.LayoutParams layoutParams = 302 mRadioGroup.generateLayoutParams((AttributeSet) null); 303 assertNotNull(layoutParams); 304 // default values 305 assertEquals(0.0, layoutParams.weight, 0); 306 assertEquals(-1, layoutParams.gravity); 307 assertEquals(0, layoutParams.leftMargin); 308 assertEquals(0, layoutParams.topMargin); 309 assertEquals(0, layoutParams.rightMargin); 310 assertEquals(0, layoutParams.bottomMargin); 311 assertEquals(LayoutParams.WRAP_CONTENT, layoutParams.width); 312 assertEquals(LayoutParams.WRAP_CONTENT, layoutParams.height); 313 314 AttributeSet attrs = getAttributeSet(R.layout.radiogroup_1); 315 layoutParams = mRadioGroup.generateLayoutParams(attrs); 316 // values from layout 317 assertNotNull(layoutParams); 318 assertEquals(0.5, layoutParams.weight, 0); 319 assertEquals(Gravity.BOTTOM, layoutParams.gravity); 320 assertEquals(5, layoutParams.leftMargin); 321 assertEquals(5, layoutParams.topMargin); 322 assertEquals(5, layoutParams.rightMargin); 323 assertEquals(5, layoutParams.bottomMargin); 324 assertEquals(LayoutParams.MATCH_PARENT, layoutParams.width); 325 assertEquals(LayoutParams.MATCH_PARENT, layoutParams.height); 326 } 327 328 @Test 329 public void testCheckLayoutParams() { 330 MockRadioGroup mRadioGroupWrapper = new MockRadioGroup(mActivity); 331 332 assertFalse(mRadioGroupWrapper.checkLayoutParams(null)); 333 334 RelativeLayout.LayoutParams relativeParams = new RelativeLayout.LayoutParams( 335 RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT); 336 assertFalse(mRadioGroupWrapper.checkLayoutParams(relativeParams)); 337 338 LinearLayout.LayoutParams linearParams = new LinearLayout.LayoutParams( 339 LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT); 340 assertFalse(mRadioGroupWrapper.checkLayoutParams(linearParams)); 341 342 RadioGroup.LayoutParams radioParams = new RadioGroup.LayoutParams( 343 RadioGroup.LayoutParams.MATCH_PARENT, RadioGroup.LayoutParams.MATCH_PARENT); 344 assertTrue(mRadioGroupWrapper.checkLayoutParams(radioParams)); 345 } 346 347 @Test 348 public void testGenerateDefaultLayoutParams() { 349 MockRadioGroup radioGroupWrapper = new MockRadioGroup(mActivity); 350 LinearLayout.LayoutParams p = radioGroupWrapper.generateDefaultLayoutParams(); 351 352 assertTrue(p instanceof RadioGroup.LayoutParams); 353 assertEquals(RadioGroup.LayoutParams.WRAP_CONTENT, p.width); 354 assertEquals(RadioGroup.LayoutParams.WRAP_CONTENT, p.height); 355 } 356 357 @UiThreadTest 358 @Test 359 public void testOnFinishInflate() { 360 MockRadioGroup radioGroup = new MockRadioGroup(mActivity); 361 int checkId = 100; 362 radioGroup.check(checkId); 363 // the button is added after the check(int)method 364 // and it not checked though it has exactly the checkId 365 RadioButton button = new RadioButton(mActivity); 366 button.setId(checkId); 367 radioGroup.addView(button, new LinearLayout.LayoutParams( 368 LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT)); 369 MockOnCheckedChangeListener listener = new MockOnCheckedChangeListener(); 370 radioGroup.setOnCheckedChangeListener(listener); 371 372 // check the button which id is CheckedRadioButtonId 373 listener.reset(); 374 assertFalse(button.isChecked()); 375 radioGroup.onFinishInflate(); 376 assertTrue(button.isChecked()); 377 assertHasCalledOnCheckedChanged(listener); 378 assertEquals(checkId, radioGroup.getCheckedRadioButtonId()); 379 380 radioGroup = new MockRadioGroup(mActivity); 381 button = new RadioButton(mActivity); 382 radioGroup.addView(button, new LinearLayout.LayoutParams( 383 LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT)); 384 listener = new MockOnCheckedChangeListener(); 385 radioGroup.setOnCheckedChangeListener(listener); 386 387 // nothing happens if checkedRadioButtonId is -1 388 assertEquals(-1, radioGroup.getCheckedRadioButtonId()); 389 assertFalse(button.isChecked()); 390 listener.reset(); 391 radioGroup.onFinishInflate(); 392 assertHaveNotCalledOnCheckedChanged(listener); 393 assertEquals(-1, radioGroup.getCheckedRadioButtonId()); 394 assertFalse(button.isChecked()); 395 } 396 397 @UiThreadTest 398 @Test 399 public void testAddView() { 400 mRadioGroup.check(R.id.radio_button_0); 401 assertEquals(R.id.radio_button_0, mRadioGroup.getCheckedRadioButtonId()); 402 assertEquals(4, mRadioGroup.getChildCount()); 403 404 int id = R.id.radio_button_3 + 10; 405 RadioButton choice4 = new RadioButton(mActivity); 406 choice4.setText("choice4"); 407 choice4.setId(id); 408 choice4.setChecked(true); 409 mRadioGroup.addView(choice4, 4, new ViewGroup.LayoutParams(100, 200)); 410 assertEquals(id, mRadioGroup.getCheckedRadioButtonId()); 411 assertEquals(5, mRadioGroup.getChildCount()); 412 } 413 414 private AttributeSet getAttributeSet(int resId) { 415 XmlPullParser parser = mActivity.getResources().getLayout(resId); 416 assertNotNull(parser); 417 int type = 0; 418 try { 419 while ((type = parser.next()) != XmlPullParser.START_TAG 420 && type != XmlPullParser.END_DOCUMENT) { 421 // Empty 422 } 423 } catch (XmlPullParserException e) { 424 fail(e.getMessage()); 425 } catch (IOException e) { 426 fail(e.getMessage()); 427 } 428 429 assertEquals("No RadioGroup element found", XmlPullParser.START_TAG, type); 430 assertEquals("The first element is not RadioGroup", "RadioGroup", parser.getName()); 431 return Xml.asAttributeSet(parser); 432 } 433 434 private void assertHaveNotCalledOnCheckedChanged(MockOnCheckedChangeListener listener) { 435 assertEquals(0, listener.getOnCheckedChangedGroupParams().size()); 436 assertEquals(0, listener.getOnCheckedChangedCheckedIdParams().size()); 437 } 438 439 private void assertHasCalledOnCheckedChanged(MockOnCheckedChangeListener listener) { 440 assertTrue(listener.getOnCheckedChangedGroupParams().size() > 0); 441 assertTrue(listener.getOnCheckedChangedCheckedIdParams().size() > 0); 442 } 443 444 private void assertOnCheckedChangedParams(MockOnCheckedChangeListener listener, int time, 445 RadioGroup paramGroup, int paramCheckedId) { 446 assertSame(paramGroup, 447 listener.getOnCheckedChangedGroupParams().get(time)); 448 assertEquals(paramCheckedId, listener 449 .getOnCheckedChangedCheckedIdParams().get(time).intValue()); 450 } 451 452 private class MockOnCheckedChangeListener implements OnCheckedChangeListener { 453 private Vector<RadioGroup> mOnCheckedChangedGroupParams = new Vector<RadioGroup>(); 454 455 private Vector<Integer> mOnCheckedChangedCheckedIdParams = new Vector<Integer>(); 456 457 public Vector<RadioGroup> getOnCheckedChangedGroupParams() { 458 return mOnCheckedChangedGroupParams; 459 } 460 461 public Vector<Integer> getOnCheckedChangedCheckedIdParams() { 462 return mOnCheckedChangedCheckedIdParams; 463 } 464 465 public void reset() { 466 mOnCheckedChangedGroupParams.clear(); 467 mOnCheckedChangedCheckedIdParams.clear(); 468 } 469 470 public void onCheckedChanged(RadioGroup group, int checkedId) { 471 mOnCheckedChangedGroupParams.add(group); 472 mOnCheckedChangedCheckedIdParams.add(checkedId); 473 } 474 } 475 476 private class MockOnHierarchyChangeListener implements OnHierarchyChangeListener { 477 private View mOnChildViewAddedParentParam; 478 479 private View mOnChildViewAddedChildParam; 480 481 private View mOnChildViewRemovedParentParam; 482 483 private View mOnChildViewRemovedChildParam; 484 485 public View getOnChildViewAddedParentParam() { 486 return mOnChildViewAddedParentParam; 487 } 488 489 public View getOnChildViewAddedChildParam() { 490 return mOnChildViewAddedChildParam; 491 } 492 493 public View getOnChildViewRemovedParentParam() { 494 return mOnChildViewRemovedParentParam; 495 } 496 497 public View getOnChildViewRemovedChildParam() { 498 return mOnChildViewRemovedChildParam; 499 } 500 501 public void reset() { 502 mOnChildViewAddedParentParam = null; 503 mOnChildViewAddedChildParam = null; 504 mOnChildViewRemovedParentParam = null; 505 mOnChildViewRemovedChildParam = null; 506 } 507 508 public void onChildViewAdded(View parent, View child) { 509 mOnChildViewAddedParentParam = parent; 510 mOnChildViewAddedChildParam = child; 511 } 512 513 public void onChildViewRemoved(View parent, View child) { 514 mOnChildViewRemovedParentParam = parent; 515 mOnChildViewRemovedChildParam = child; 516 } 517 } 518 519 public static class MockRadioGroup extends RadioGroup { 520 public MockRadioGroup(Context context) { 521 super(context); 522 } 523 524 @Override 525 protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { 526 return super.checkLayoutParams(p); 527 } 528 529 @Override 530 protected android.widget.LinearLayout.LayoutParams generateDefaultLayoutParams() { 531 return super.generateDefaultLayoutParams(); 532 } 533 534 @Override 535 protected void onFinishInflate() { 536 super.onFinishInflate(); 537 } 538 } 539 } 540