Skip to content

Commit

Permalink
Merge pull request android#260 from pfmaggi/pfm/testing
Browse files Browse the repository at this point in the history
Add Espresso tests for Activities using Jetpack WindowManager
  • Loading branch information
pfmaggi authored Jul 20, 2021
2 parents cb73be1 + 76aa94b commit 96e65d2
Show file tree
Hide file tree
Showing 5 changed files with 277 additions and 32 deletions.
9 changes: 9 additions & 0 deletions WindowManager/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,19 @@ android {

kotlinOptions {
jvmTarget = JavaVersion.VERSION_1_8.toString()

// Allow usage of Kotlin's @OptIn.
freeCompilerArgs += '-Xopt-in=kotlin.RequiresOptIn'
}
buildFeatures {
viewBinding = true
}

// Workaround for https://issuetracker.google.com/161465530
packagingOptions {
pickFirst 'META-INF/AL2.0'
pickFirst 'META-INF/LGPL2.1'
}
}

dependencies {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
/*
* Copyright 2021 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
*
* https://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 com.example.windowmanagersample

import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.espresso.matcher.ViewMatchers.withSubstring
import androidx.test.ext.junit.rules.ActivityScenarioRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.window.FoldingFeature.Orientation.Companion.HORIZONTAL
import androidx.window.FoldingFeature.Orientation.Companion.VERTICAL
import androidx.window.FoldingFeature.State.Companion.FLAT
import androidx.window.FoldingFeature.State.Companion.HALF_OPENED
import androidx.window.WindowLayoutInfo
import androidx.window.testing.FoldingFeature
import androidx.window.testing.WindowLayoutInfoPublisherRule
import androidx.window.windowInfoRepository
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.async
import kotlinx.coroutines.flow.take
import kotlinx.coroutines.flow.toCollection
import kotlinx.coroutines.test.TestCoroutineScope
import kotlinx.coroutines.test.runBlockingTest
import org.junit.Assert.assertEquals
import org.junit.Rule
import org.junit.Test
import org.junit.rules.RuleChain
import org.junit.rules.TestRule
import org.junit.runner.RunWith

@RunWith(AndroidJUnit4::class)
@OptIn(ExperimentalCoroutinesApi::class)
class DisplayFeaturesActivityTest {
private val activityRule = ActivityScenarioRule(DisplayFeaturesActivity::class.java)
private val publisherRule = WindowLayoutInfoPublisherRule()

private val testScope = TestCoroutineScope()

@get:Rule
val testRule: TestRule

init {
testRule = RuleChain.outerRule(publisherRule).around(activityRule)
}

@Test
fun testDeviceOpen_Flat(): Unit = testScope.runBlockingTest {
activityRule.scenario.onActivity { activity ->
val feature = FoldingFeature(
activity = activity,
state = FLAT,
orientation = HORIZONTAL
)
val expected = WindowLayoutInfo.Builder().setDisplayFeatures(listOf(feature)).build()

val values = mutableListOf<WindowLayoutInfo>()
val value = testScope.async {
activity.windowInfoRepository().windowLayoutInfo.take(1).toCollection(values)
}
publisherRule.overrideWindowLayoutInfo(expected)
runBlockingTest {
val newValues = value.await().toList()
assertEquals(
listOf(expected),
newValues
)
}
}
onView(withId(R.id.state_update_log)).check(matches(withSubstring("state=FLAT")))
onView(withId(R.id.current_state)).check(matches(withSubstring("is not separated")))
onView(withId(R.id.current_state)).check(matches(withSubstring("Hinge is horizontal")))
}

@Test
fun testDeviceOpen_TableTop(): Unit = testScope.runBlockingTest {
activityRule.scenario.onActivity { activity ->
val feature =
FoldingFeature(activity = activity, state = HALF_OPENED, orientation = HORIZONTAL)
val expected = WindowLayoutInfo.Builder().setDisplayFeatures(listOf(feature)).build()

val values = mutableListOf<WindowLayoutInfo>()
val value = testScope.async {
activity.windowInfoRepository().windowLayoutInfo.take(1).toCollection(values)
}
publisherRule.overrideWindowLayoutInfo(expected)
runBlockingTest {
val newValues = value.await().toList()
assertEquals(
listOf(expected),
newValues
)
}
}
onView(withId(R.id.state_update_log)).check(matches(withSubstring("state=HALF_OPENED")))
onView(withId(R.id.current_state)).check(matches(withSubstring("are separated")))
onView(withId(R.id.current_state)).check(matches(withSubstring("Hinge is horizontal")))
}

@Test
fun testDeviceOpen_Book(): Unit = testScope.runBlockingTest {
activityRule.scenario.onActivity { activity ->
val feature =
FoldingFeature(activity = activity, state = HALF_OPENED, orientation = VERTICAL)
val expected = WindowLayoutInfo.Builder().setDisplayFeatures(listOf(feature)).build()

val values = mutableListOf<WindowLayoutInfo>()
val value = testScope.async {
activity.windowInfoRepository().windowLayoutInfo.take(1).toCollection(values)
}
publisherRule.overrideWindowLayoutInfo(expected)
runBlockingTest {
val newValues = value.await().toList()
assertEquals(
listOf(expected),
newValues
)
}
}
onView(withId(R.id.state_update_log)).check(matches(withSubstring("state=HALF_OPENED")))
onView(withId(R.id.current_state)).check(matches(withSubstring("are separated")))
onView(withId(R.id.current_state)).check(matches(withSubstring("Hinge is vertical")))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
/*
* Copyright 2021 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
*
* https://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 com.example.windowmanagersample

import android.graphics.Rect
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.ext.junit.rules.ActivityScenarioRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.window.FoldingFeature
import androidx.window.FoldingFeature.State.Companion.HALF_OPENED
import androidx.window.FoldingFeature.Type.Companion.FOLD
import androidx.window.WindowLayoutInfo
import androidx.window.testing.WindowLayoutInfoPublisherRule
import androidx.window.windowInfoRepository
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.async
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.take
import kotlinx.coroutines.flow.toCollection
import kotlinx.coroutines.test.TestCoroutineScope
import kotlinx.coroutines.test.runBlockingTest
import org.junit.Assert
import org.junit.Rule
import org.junit.Test
import org.junit.rules.RuleChain
import org.junit.rules.TestRule
import org.junit.runner.RunWith

@OptIn(ExperimentalCoroutinesApi::class)
@RunWith(AndroidJUnit4::class)
class SplitLayoutActivityTest {
private val activityRule = ActivityScenarioRule(SplitLayoutActivity::class.java)
private val publisherRule = WindowLayoutInfoPublisherRule()

private val testScope = TestCoroutineScope()

@get:Rule
val testRule: TestRule

init {
testRule = RuleChain.outerRule(publisherRule).around(activityRule)
}

@Test
fun testDeviceOpen_Vertical(): Unit = testScope.runBlockingTest {
activityRule.scenario.onActivity { activity ->
val bounds = activity.windowInfoRepository().currentWindowMetrics.bounds
val center = bounds.centerX()
val feature = FoldingFeature(
Rect(center, 0, center, bounds.height()),
FOLD,
HALF_OPENED
)
val expected = WindowLayoutInfo.Builder().setDisplayFeatures(listOf(feature)).build()

val values = mutableListOf<WindowLayoutInfo>()
val value = testScope.async {
activity.windowInfoRepository().windowLayoutInfo.take(1).toCollection(values)
}
publisherRule.overrideWindowLayoutInfo(expected)
runBlockingTest {
val newValues = value.await().toList()
Assert.assertEquals(
listOf(expected),
newValues
)
delay(5000)
}
}
onView(withId(R.id.start_layout)).check(matches(isDisplayed()))
onView(withId(R.id.end_layout)).check(matches(isDisplayed()))
}

@Test
fun testDeviceOpen_Horizontal(): Unit = testScope.runBlockingTest {
activityRule.scenario.onActivity { activity ->
val bounds = activity.windowInfoRepository().currentWindowMetrics.bounds
val center = bounds.centerY()
val feature = FoldingFeature(
Rect(0, center, bounds.height(), center),
FOLD,
HALF_OPENED
)
val expected = WindowLayoutInfo.Builder().setDisplayFeatures(listOf(feature)).build()

val values = mutableListOf<WindowLayoutInfo>()
val value = testScope.async {
activity.windowInfoRepository().windowLayoutInfo.take(1).toCollection(values)
}
publisherRule.overrideWindowLayoutInfo(expected)
runBlockingTest {
val newValues = value.await().toList()
Assert.assertEquals(
listOf(expected),
newValues
)
delay(5000)
}
}
onView(withId(R.id.start_layout)).check(matches(isDisplayed()))
onView(withId(R.id.end_layout)).check(matches(isDisplayed()))
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -66,13 +66,15 @@ fun getFeaturePositionInViewRect(
* Gets the layout params for placing a rectangle indicating a display feature inside a
* [FrameLayout].
*/
fun getLayoutParamsForFeatureInFrameLayout(displayFeature: DisplayFeature, view: FrameLayout):
FrameLayout.LayoutParams? {
val featureRectInView = getFeaturePositionInViewRect(displayFeature, view) ?: return null
fun getLayoutParamsForFeatureInFrameLayout(
displayFeature: DisplayFeature,
view: FrameLayout
): FrameLayout.LayoutParams? {
val featureRectInView = getFeaturePositionInViewRect(displayFeature, view) ?: return null

val lp = FrameLayout.LayoutParams(featureRectInView.width(), featureRectInView.height())
lp.leftMargin = featureRectInView.left
lp.topMargin = featureRectInView.top
val lp = FrameLayout.LayoutParams(featureRectInView.width(), featureRectInView.height())
lp.leftMargin = featureRectInView.left
lp.topMargin = featureRectInView.top

return lp
}
return lp
}

0 comments on commit 96e65d2

Please sign in to comment.