[go: nahoru, domu]

blob: e450f2b2649ea29078ddd9cc5bf5207221f7cb02 [file] [log] [blame]
/*
* Copyright 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.preference.tests
import android.os.Build
import android.util.DisplayMetrics
import android.view.View
import android.widget.SeekBar
import androidx.preference.SeekBarPreference
import androidx.preference.test.R
import androidx.preference.tests.helpers.PreferenceTestHelperActivity
import androidx.test.annotation.UiThreadTest
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.ViewAction
import androidx.test.espresso.action.CoordinatesProvider
import androidx.test.espresso.action.GeneralSwipeAction
import androidx.test.espresso.action.Press
import androidx.test.espresso.action.Swipe
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.BoundedMatcher
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.espresso.matcher.ViewMatchers.withText
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest
import androidx.test.filters.SdkSuppress
import androidx.test.rule.ActivityTestRule
import org.hamcrest.Description
import org.hamcrest.Matchers.allOf
import org.hamcrest.Matchers.not
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
/**
* Test for [androidx.preference.SeekBarPreference].
*/
@RunWith(AndroidJUnit4::class)
@LargeTest
class SeekBarPreferenceTest {
@get:Rule
val activityRule = ActivityTestRule(PreferenceTestHelperActivity::class.java)
private lateinit var seekBarPreference: SeekBarPreference
@Before
@UiThreadTest
fun setUp() {
val fragment = activityRule.activity.setupPreferenceHierarchy(
R.xml.test_seekbar
)
seekBarPreference = fragment.preferenceScreen.getPreference(0) as SeekBarPreference
seekBarPreference.min = 0
seekBarPreference.max = 5
}
@Test
fun testSetValue() {
activityRule.runOnUiThread {
// When a value of 3 is set
seekBarPreference.value = 3
// The internal value should be set to 3
assertEquals(3, seekBarPreference.value)
}
// The seekbar's progress should be set to 3
onView(withId(R.id.seekbar)).check(matches(withSeekBarProgress(3)))
}
@Test
fun testSetValue_belowMinValue() {
activityRule.runOnUiThread {
// Given a non-zero min value
seekBarPreference.min = 3
// When a value lower than the min value is set
seekBarPreference.value = 0
// The actual value set should be equal to the min value
assertEquals(3, seekBarPreference.value)
}
// The seekbar's progress should be 0, as we are currently at the min value
onView(withId(R.id.seekbar)).check(matches(withSeekBarProgress(0)))
}
@Test
fun testSetValue_aboveMaxValue() {
activityRule.runOnUiThread {
// Given a max value
seekBarPreference.max = 7
// When a value higher than the max value is set
seekBarPreference.value = 10
// The actual value set should be equal to the max value
assertEquals(7, seekBarPreference.value)
}
// The seekbar's progress should be equal to the max value
onView(withId(R.id.seekbar)).check(matches(withSeekBarProgress(7)))
}
@Test
fun testSeekBarLabel_updatesWhenValueIsChanged() {
// By default only the seekbar, not the label should be visible
onView(withId(R.id.seekbar)).check(matches(isDisplayed()))
onView(withId(R.id.seekbar_value)).check(matches(not(isDisplayed())))
// When we enable showing the value
activityRule.runOnUiThread { seekBarPreference.showSeekBarValue = true }
// Both the seekbar and the label should be visible
onView(withId(R.id.seekbar)).check(matches(isDisplayed()))
onView(allOf(withId(R.id.seekbar_value), withText("0"))).check(matches(isDisplayed()))
// When we change the value
activityRule.runOnUiThread { seekBarPreference.value = 5 }
// The label should update to show the new value
onView(allOf(withId(R.id.seekbar_value), withText("5"))).check(matches(isDisplayed()))
}
@Test
fun testSeekBarLabel_updatesWhenSeekbarProgressChanges() {
activityRule.runOnUiThread { seekBarPreference.showSeekBarValue = true }
onView(allOf(withId(R.id.seekbar_value), withText("0"))).check(matches(isDisplayed()))
// When the seekbar's progress changes
activityRule.activity.findViewById<SeekBar>(R.id.seekbar).progress = 5
// The label should update to show the new value
onView(allOf(withId(R.id.seekbar_value), withText("5"))).check(matches(isDisplayed()))
}
@Test
fun testSeekBarLabel_shouldCorrectlyHandlesMinValue() {
activityRule.runOnUiThread {
// Set the minimum and the saved value to 3
seekBarPreference.min = 3
seekBarPreference.value = 3
seekBarPreference.showSeekBarValue = true
}
// The label should display the saved value
onView(allOf(withId(R.id.seekbar_value), withText("3"))).check(matches(isDisplayed()))
// Since the value is also the minimum, the seekbar should have no progress
onView(withId(R.id.seekbar)).check(matches(withSeekBarProgress(0)))
// Change the value to 5
activityRule.runOnUiThread { seekBarPreference.value = 5 }
// The label should display the saved value
onView(allOf(withId(R.id.seekbar_value), withText("5"))).check(matches(isDisplayed()))
// However since the minimum is 3, the seekbar's progress should be offset by 3, and should
// only have progressed to 2 (the current value - the minimum)
onView(withId(R.id.seekbar)).check(matches(withSeekBarProgress(2)))
}
@Test
// Seems that these tests are flaky on certain devices with large screens due to the swipe not
// fully dragging from one end to another. Should be safer to only run them on newer devices
// where they are stable.
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.P)
fun testSeekBarPreferenceChangeListener() {
// How many times the change listener has been called
var updateCount = 0
activityRule.runOnUiThread {
seekBarPreference.value = 0
seekBarPreference.showSeekBarValue = true
seekBarPreference.updatesContinuously = false
seekBarPreference.setOnPreferenceChangeListener { _, _ ->
updateCount++
true
}
}
onView(allOf(withId(R.id.seekbar_value), withText("0"))).check(matches(isDisplayed()))
onView(withId(R.id.seekbar)).check(matches(withSeekBarProgress(0)))
// Fully drag the seekbar from left to right
onView(withId(R.id.seekbar)).perform(dragSeekBar())
// The current value should be 5
onView(allOf(withId(R.id.seekbar_value), withText("5"))).check(matches(isDisplayed()))
onView(withId(R.id.seekbar)).check(matches(withSeekBarProgress(5)))
activityRule.runOnUiThread {
// SeekBarPreference should only attempt to update once, when the drag has stopped
assertEquals(1, updateCount)
assertEquals(5, seekBarPreference.value)
}
}
@Test
// Seems that these tests are flaky on certain devices with large screens due to the swipe not
// fully dragging from one end to another. Should be safer to only run them on newer devices
// where they are stable.
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.P)
fun testSeekBarPreferenceChangeListenerWithContinuousUpdates() {
// How many times the change listener has been called
var updateCount = 0
activityRule.runOnUiThread {
seekBarPreference.value = 0
seekBarPreference.max = 5
seekBarPreference.showSeekBarValue = true
seekBarPreference.updatesContinuously = true
seekBarPreference.setOnPreferenceChangeListener { _, _ ->
updateCount++
true
}
}
onView(allOf(withId(R.id.seekbar_value), withText("0"))).check(matches(isDisplayed()))
onView(withId(R.id.seekbar)).check(matches(withSeekBarProgress(0)))
// Fully drag the seekbar from left to right
onView(withId(R.id.seekbar)).perform(dragSeekBar())
// The current value should be 5
onView(allOf(withId(R.id.seekbar_value), withText("5"))).check(matches(isDisplayed()))
onView(withId(R.id.seekbar)).check(matches(withSeekBarProgress(5)))
activityRule.runOnUiThread {
// SeekBarPreference should attempt to update every time the seekbar's progress changed.
// From 0-5, it should update 5 times.
assertEquals(5, updateCount)
assertEquals(5, seekBarPreference.value)
}
}
/**
* A [ViewAction] that drags a [SeekBar] from its left edge to the right edge of the screen
*/
private fun dragSeekBar(): ViewAction {
return GeneralSwipeAction(Swipe.FAST,
CoordinatesProvider { view ->
val location = IntArray(2)
view.getLocationOnScreen(location)
val posX = location[0]
val posY = location[1]
// Start at the beginning of the seekbar
floatArrayOf(posX.toFloat(), posY.toFloat())
}, CoordinatesProvider { view ->
val location = IntArray(2)
view.getLocationOnScreen(location)
// We want to swipe all the way to the right edge of the screen to avoid
// flakiness due to sometimes not reaching the end of the seekbar
val metrics = DisplayMetrics()
@Suppress("DEPRECATION") /* defaultDisplay */
activityRule.activity.windowManager.defaultDisplay.getMetrics(metrics)
val posX = metrics.widthPixels
val posY = location[1]
floatArrayOf(posX.toFloat(), posY.toFloat())
}, Press.PINPOINT
)
}
/**
* A matcher to assert the current progress of a [SeekBar]
*
* @param progress The expected progress of the [SeekBar]
*/
private fun withSeekBarProgress(progress: Int): BoundedMatcher<View, SeekBar> {
return object : BoundedMatcher<View, SeekBar>(SeekBar::class.java) {
override fun matchesSafely(item: SeekBar?): Boolean {
return item!!.progress == progress
}
override fun describeTo(description: Description?) {
description?.appendText("with SeekBar progress:")
?.appendValue(progress)
}
}
}
}