1 /* 2 * Copyright 2019 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 androidx.appcompat.widget 18 19 import android.provider.Settings 20 import android.view.ViewGroup 21 import android.widget.CheckBox 22 import android.widget.ImageView 23 import android.widget.SeekBar 24 import android.widget.TextView 25 import androidx.appcompat.app.AppCompatActivity 26 import androidx.appcompat.test.R 27 import androidx.test.ext.junit.runners.AndroidJUnit4 28 import androidx.test.filters.MediumTest 29 import androidx.test.filters.SdkSuppress 30 import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation 31 import org.junit.After 32 import org.junit.Assert.assertEquals 33 import org.junit.Assert.assertTrue 34 import org.junit.Assume.assumeTrue 35 import org.junit.Before 36 import org.junit.Rule 37 import org.junit.Test 38 import org.junit.runner.RunWith 39 40 @RunWith(AndroidJUnit4::class) 41 @MediumTest 42 @SdkSuppress(minSdkVersion = 29) 43 class AppCompatAttributeTest { 44 @Suppress("DEPRECATION") 45 @get:Rule 46 val activityRule = 47 androidx.test.rule.ActivityTestRule(AppCompatActivity::class.java, true, false) 48 49 @Before setupnull50 fun setup() { 51 getInstrumentation() 52 .uiAutomation 53 .executeShellCommand("settings put global $DEBUG_VIEW_ATTRIBUTES $TEST_PACKAGE") 54 assumeDebugViewAttributes(TEST_PACKAGE) 55 activityRule.launchActivity(null) 56 } 57 58 @After tearDownnull59 fun tearDown() { 60 getInstrumentation() 61 .uiAutomation 62 .executeShellCommand("settings delete global $DEBUG_VIEW_ATTRIBUTES") 63 assumeDebugViewAttributes(null) 64 } 65 66 @Test testAppCompatImageViewAttributesnull67 fun testAppCompatImageViewAttributes() { 68 val root = 69 activityRule.activity.layoutInflater.inflate(R.layout.view_attribute_layout, null) 70 as ViewGroup 71 val imageView = root.findViewById<ImageView>(R.id.image_view) 72 assertTrue(imageView.attributeSourceResourceMap.isNotEmpty()) 73 assertEquals( 74 R.layout.view_attribute_layout, 75 imageView.attributeSourceResourceMap[R.attr.srcCompat] 76 ) 77 assertEquals( 78 R.layout.view_attribute_layout, 79 imageView.attributeSourceResourceMap[R.attr.backgroundTint] 80 ) 81 assertEquals( 82 R.layout.view_attribute_layout, 83 imageView.attributeSourceResourceMap[R.attr.backgroundTintMode] 84 ) 85 } 86 87 @Test testAppCompatCheckBoxAttributesnull88 fun testAppCompatCheckBoxAttributes() { 89 val root = 90 activityRule.activity.layoutInflater.inflate(R.layout.view_attribute_layout, null) 91 as ViewGroup 92 val checkBox = root.findViewById<CheckBox>(R.id.check_box) 93 assertTrue(checkBox.attributeSourceResourceMap.isNotEmpty()) 94 assertEquals( 95 R.layout.view_attribute_layout, 96 checkBox.attributeSourceResourceMap[R.attr.buttonTint] 97 ) 98 } 99 100 @Test testAppCompatSeekBarAttributesnull101 fun testAppCompatSeekBarAttributes() { 102 val root = 103 activityRule.activity.layoutInflater.inflate(R.layout.view_attribute_layout, null) 104 as ViewGroup 105 val seekBar = root.findViewById<SeekBar>(R.id.seek_bar) 106 assertTrue(seekBar.attributeSourceResourceMap.isNotEmpty()) 107 assertEquals( 108 R.layout.view_attribute_layout, 109 seekBar.attributeSourceResourceMap[R.attr.tickMarkTint] 110 ) 111 } 112 113 @Test testAppCompatTextViewAttributesnull114 fun testAppCompatTextViewAttributes() { 115 val root = 116 activityRule.activity.layoutInflater.inflate(R.layout.view_attribute_layout, null) 117 as ViewGroup 118 val textView = root.findViewById<TextView>(R.id.text_view) 119 assertTrue(textView.attributeSourceResourceMap.isNotEmpty()) 120 assertEquals( 121 R.layout.view_attribute_layout, 122 textView.attributeSourceResourceMap[R.attr.autoSizeTextType] 123 ) 124 } 125 126 @Test testSwitchCompatAttributesnull127 fun testSwitchCompatAttributes() { 128 val root = 129 activityRule.activity.layoutInflater.inflate(R.layout.view_attribute_layout, null) 130 as ViewGroup 131 val switchCompat = root.findViewById<SwitchCompat>(R.id.switch_compat) 132 assertTrue(switchCompat.attributeSourceResourceMap.isNotEmpty()) 133 assertEquals( 134 R.layout.view_attribute_layout, 135 switchCompat.attributeSourceResourceMap[R.attr.thumbTint] 136 ) 137 } 138 139 @Test testToolbarAttributesnull140 fun testToolbarAttributes() { 141 val root = 142 activityRule.activity.layoutInflater.inflate(R.layout.view_attribute_layout, null) 143 as ViewGroup 144 val toolbar = root.findViewById<Toolbar>(R.id.toolbar) 145 assertTrue(toolbar.attributeSourceResourceMap.isNotEmpty()) 146 assertEquals( 147 R.layout.view_attribute_layout, 148 toolbar.attributeSourceResourceMap[R.attr.titleMargin] 149 ) 150 } 151 152 @Test testLinearLayoutCompatAttributesnull153 fun testLinearLayoutCompatAttributes() { 154 val root = 155 activityRule.activity.layoutInflater.inflate(R.layout.view_attribute_layout, null) 156 as LinearLayoutCompat 157 assertTrue(root.attributeSourceResourceMap.isNotEmpty()) 158 assertEquals( 159 R.layout.view_attribute_layout, 160 root.attributeSourceResourceMap[R.attr.showDividers] 161 ) 162 } 163 164 private companion object { 165 const val SETTINGS_TIMEOUT = 5000 // 5 seconds 166 const val DEBUG_VIEW_ATTRIBUTES = "debug_view_attributes_application_package" 167 const val TEST_PACKAGE = "androidx.appcompat.test" 168 busyWaitnull169 fun busyWait(timeout: Int, predicate: () -> Boolean): Boolean { 170 val deadline = System.currentTimeMillis() + timeout 171 172 do { 173 if (predicate()) { 174 return true 175 } 176 Thread.sleep(50) 177 } while (System.currentTimeMillis() < deadline) 178 179 return false 180 } 181 assumeDebugViewAttributesnull182 fun assumeDebugViewAttributes(expected: String?) { 183 val timeout = SETTINGS_TIMEOUT / 1000F 184 val contentResolver = getInstrumentation().targetContext.contentResolver 185 assumeTrue( 186 "Assumed $DEBUG_VIEW_ATTRIBUTES would be $expected within $timeout seconds", 187 busyWait(SETTINGS_TIMEOUT) { 188 Settings.Global.getString(contentResolver, DEBUG_VIEW_ATTRIBUTES) == expected 189 } 190 ) 191 } 192 } 193 } 194