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