Add test artifact to support testing

Add test artifact to support testing.
Add EmptyWindowLayoutInfoRule to always produce an empty set of features

Relnote: Add test artifact for WindowManager Jetpack Library.

Bug: 184266682
Test: ./gradlew window:window-test:cAT
Change-Id: I57f66ad83498ff5e09d0a1d1c9cda0554c2c04e1
diff --git a/window/window-testing/api/current.txt b/window/window-testing/api/current.txt
new file mode 100644
index 0000000..e0d15ce
--- /dev/null
+++ b/window/window-testing/api/current.txt
@@ -0,0 +1,11 @@
+// Signature format: 4.0
+package androidx.window.test {
+
+  public final class WindowLayoutInfoPublisherRule implements org.junit.rules.TestRule {
+    ctor public WindowLayoutInfoPublisherRule();
+    method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
+    method public void overrideWindowLayoutInfo(androidx.window.WindowLayoutInfo info);
+  }
+
+}
+
diff --git a/window/window-testing/api/public_plus_experimental_current.txt b/window/window-testing/api/public_plus_experimental_current.txt
new file mode 100644
index 0000000..f00e796
--- /dev/null
+++ b/window/window-testing/api/public_plus_experimental_current.txt
@@ -0,0 +1,11 @@
+// Signature format: 4.0
+package androidx.window.test {
+
+  public final class WindowLayoutInfoPublisherRule implements org.junit.rules.TestRule {
+    ctor public WindowLayoutInfoPublisherRule();
+    method @kotlinx.coroutines.ExperimentalCoroutinesApi public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
+    method public void overrideWindowLayoutInfo(androidx.window.WindowLayoutInfo info);
+  }
+
+}
+
diff --git a/window/window-testing/api/res-current.txt b/window/window-testing/api/res-current.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/window/window-testing/api/res-current.txt
diff --git a/window/window-testing/api/restricted_current.txt b/window/window-testing/api/restricted_current.txt
new file mode 100644
index 0000000..e0d15ce
--- /dev/null
+++ b/window/window-testing/api/restricted_current.txt
@@ -0,0 +1,11 @@
+// Signature format: 4.0
+package androidx.window.test {
+
+  public final class WindowLayoutInfoPublisherRule implements org.junit.rules.TestRule {
+    ctor public WindowLayoutInfoPublisherRule();
+    method public org.junit.runners.model.Statement apply(org.junit.runners.model.Statement base, org.junit.runner.Description description);
+    method public void overrideWindowLayoutInfo(androidx.window.WindowLayoutInfo info);
+  }
+
+}
+
diff --git a/window/window-testing/build.gradle b/window/window-testing/build.gradle
new file mode 100644
index 0000000..5a706af
--- /dev/null
+++ b/window/window-testing/build.gradle
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 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
+ *
+ *      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.
+ */
+
+import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
+
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.LibraryType
+import androidx.build.LibraryVersions
+
+plugins {
+    id("AndroidXPlugin")
+    id("com.android.library")
+    id("org.jetbrains.kotlin.android")
+}
+
+android {
+    defaultConfig {
+        multiDexEnabled = true
+    }
+}
+
+dependencies {
+    api(KOTLIN_STDLIB)
+    api(project(":window:window"))
+    api(JUNIT)
+    implementation("androidx.core:core:1.3.2")
+
+    androidTestImplementation(ANDROIDX_TEST_EXT_JUNIT)
+    androidTestImplementation(ANDROIDX_TEST_RUNNER)
+    androidTestImplementation(ANDROIDX_TEST_RULES)
+    androidTestImplementation(KOTLIN_COROUTINES_TEST)
+    androidTestImplementation(MULTIDEX)
+    androidTestImplementation(TRUTH)
+}
+
+androidx {
+    name = "WindowManager Test Library"
+    type = LibraryType.PUBLISHED_LIBRARY
+    mavenVersion = LibraryVersions.WINDOW
+    mavenGroup = LibraryGroups.WINDOW
+    inceptionYear = "2021"
+    description = "WindowManager Test Library"
+}
+
+// Allow usage of Kotlin's @OptIn.
+tasks.withType(KotlinCompile).configureEach {
+    kotlinOptions {
+        freeCompilerArgs += ["-Xopt-in=kotlin.RequiresOptIn"]
+    }
+}
\ No newline at end of file
diff --git a/window/window-testing/src/androidTest/AndroidManifest.xml b/window/window-testing/src/androidTest/AndroidManifest.xml
new file mode 100644
index 0000000..7d587da
--- /dev/null
+++ b/window/window-testing/src/androidTest/AndroidManifest.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  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
+
+       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.
+  -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="androidx.window.test">
+    <application>
+        <activity android:name="TestActivity"/>
+    </application>
+</manifest>
diff --git a/window/window-testing/src/androidTest/java/androidx/window/test/TestActivity.kt b/window/window-testing/src/androidTest/java/androidx/window/test/TestActivity.kt
new file mode 100644
index 0000000..3d934fd
--- /dev/null
+++ b/window/window-testing/src/androidTest/java/androidx/window/test/TestActivity.kt
@@ -0,0 +1,24 @@
+/*
+ * 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
+ *
+ *      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.window.test
+
+import android.app.Activity
+
+/**
+ * A test [Activity] for testing purposes.
+ */
+public class TestActivity : Activity()
\ No newline at end of file
diff --git a/window/window-testing/src/androidTest/java/androidx/window/test/WindowLayoutInfoPublisherRuleTest.kt b/window/window-testing/src/androidTest/java/androidx/window/test/WindowLayoutInfoPublisherRuleTest.kt
new file mode 100644
index 0000000..a93682b
--- /dev/null
+++ b/window/window-testing/src/androidTest/java/androidx/window/test/WindowLayoutInfoPublisherRuleTest.kt
@@ -0,0 +1,99 @@
+/*
+ * 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
+ *
+ *      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.window.test
+
+import android.graphics.Rect
+import androidx.test.ext.junit.rules.ActivityScenarioRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.LargeTest
+import androidx.window.DisplayFeature
+import androidx.window.WindowLayoutInfo
+import androidx.window.windowInfoRepository
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.async
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.flow.take
+import kotlinx.coroutines.flow.toCollection
+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
+
+@LargeTest
+@RunWith(AndroidJUnit4::class)
+public class WindowLayoutInfoPublisherRuleTest {
+
+    private val activityRule = ActivityScenarioRule(TestActivity::class.java)
+    private val publisherRule = WindowLayoutInfoPublisherRule()
+
+    @get:Rule
+    public val testRule: TestRule
+
+    init {
+        testRule = RuleChain.outerRule(publisherRule).around(activityRule)
+    }
+
+    @OptIn(ExperimentalCoroutinesApi::class)
+    @Test
+    public fun testWindowLayoutInfo_relayValue(): Unit = runBlockingTest {
+        val expected = WindowLayoutInfo.Builder().setDisplayFeatures(emptyList()).build()
+        activityRule.scenario.onActivity { activity ->
+            val value = async {
+                activity.windowInfoRepository().windowLayoutInfo.first()
+            }
+            publisherRule.overrideWindowLayoutInfo(expected)
+            runBlockingTest {
+                val actual = value.await()
+                assertEquals(expected, actual)
+            }
+        }
+    }
+
+    @OptIn(ExperimentalCoroutinesApi::class)
+    @Test
+    public fun testWindowLayoutInfo_multipleValues(): Unit = runBlockingTest {
+        val feature1 = object : DisplayFeature {
+            override val bounds: Rect
+                get() = Rect()
+        }
+        val feature2 = object : DisplayFeature {
+            override val bounds: Rect
+                get() = Rect()
+        }
+        val expected1 = WindowLayoutInfo.Builder().setDisplayFeatures(listOf(feature1)).build()
+        val expected2 = WindowLayoutInfo.Builder().setDisplayFeatures(listOf(feature2)).build()
+        activityRule.scenario.onActivity { activity ->
+            val values = mutableListOf<WindowLayoutInfo>()
+            val value = async {
+                activity.windowInfoRepository().windowLayoutInfo.take(4).toCollection(values)
+            }
+            publisherRule.overrideWindowLayoutInfo(expected1)
+            publisherRule.overrideWindowLayoutInfo(expected2)
+            publisherRule.overrideWindowLayoutInfo(expected1)
+            publisherRule.overrideWindowLayoutInfo(expected2)
+            runBlockingTest {
+                assertEquals(
+                    listOf(expected1, expected2, expected1, expected2),
+                    value.await().toList()
+                )
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/window/window-testing/src/main/AndroidManifest.xml b/window/window-testing/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..4bd698f
--- /dev/null
+++ b/window/window-testing/src/main/AndroidManifest.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  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
+
+       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.
+  -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="androidx.window.test">
+</manifest>
\ No newline at end of file
diff --git a/window/window-testing/src/main/java/androidx/window/test/PublishLayoutInfoRepo.kt b/window/window-testing/src/main/java/androidx/window/test/PublishLayoutInfoRepo.kt
new file mode 100644
index 0000000..b3591df
--- /dev/null
+++ b/window/window-testing/src/main/java/androidx/window/test/PublishLayoutInfoRepo.kt
@@ -0,0 +1,30 @@
+/*
+ * 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
+ *
+ *      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.window.test
+
+import androidx.window.WindowInfoRepo
+import androidx.window.WindowLayoutInfo
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableSharedFlow
+
+internal class PublishLayoutInfoRepo(
+    private val core: WindowInfoRepo,
+    private val flow: MutableSharedFlow<WindowLayoutInfo>
+) : WindowInfoRepo by core {
+    override val windowLayoutInfo: Flow<WindowLayoutInfo>
+        get() = flow
+}
\ No newline at end of file
diff --git a/window/window-testing/src/main/java/androidx/window/test/PublishWindowInfoRepoDecorator.kt b/window/window-testing/src/main/java/androidx/window/test/PublishWindowInfoRepoDecorator.kt
new file mode 100644
index 0000000..7cf34ba
--- /dev/null
+++ b/window/window-testing/src/main/java/androidx/window/test/PublishWindowInfoRepoDecorator.kt
@@ -0,0 +1,30 @@
+/*
+ * 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
+ *
+ *      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.window.test
+
+import androidx.window.WindowInfoRepo
+import androidx.window.WindowInfoRepoDecorator
+import androidx.window.WindowLayoutInfo
+import kotlinx.coroutines.flow.MutableSharedFlow
+
+internal class PublishWindowInfoRepoDecorator(
+    private val flow: MutableSharedFlow<WindowLayoutInfo>
+) : WindowInfoRepoDecorator {
+    override fun decorate(repo: WindowInfoRepo): WindowInfoRepo {
+        return PublishLayoutInfoRepo(repo, flow)
+    }
+}
\ No newline at end of file
diff --git a/window/window-testing/src/main/java/androidx/window/test/WindowLayoutInfoPublisher.kt b/window/window-testing/src/main/java/androidx/window/test/WindowLayoutInfoPublisher.kt
new file mode 100644
index 0000000..2261e68
--- /dev/null
+++ b/window/window-testing/src/main/java/androidx/window/test/WindowLayoutInfoPublisher.kt
@@ -0,0 +1,44 @@
+/*
+ * 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
+ *
+ *      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.window.test
+
+import androidx.window.WindowInfoRepo
+import androidx.window.WindowLayoutInfo
+import androidx.window.WindowMetrics
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.StateFlow
+
+internal class WindowLayoutInfoPublisher(
+    private val core: WindowInfoRepo,
+    private val flow: StateFlow<WindowLayoutInfo>
+) : WindowInfoRepo {
+
+    override val currentWindowMetrics: WindowMetrics
+        get() {
+            return core.currentWindowMetrics
+        }
+
+    override val maximumWindowMetrics: WindowMetrics
+        get() {
+            return core.maximumWindowMetrics
+        }
+
+    override val windowLayoutInfo: Flow<WindowLayoutInfo>
+        get() {
+            return flow
+        }
+}
\ No newline at end of file
diff --git a/window/window-testing/src/main/java/androidx/window/test/WindowLayoutInfoPublisherRule.kt b/window/window-testing/src/main/java/androidx/window/test/WindowLayoutInfoPublisherRule.kt
new file mode 100644
index 0000000..f9e92fc
--- /dev/null
+++ b/window/window-testing/src/main/java/androidx/window/test/WindowLayoutInfoPublisherRule.kt
@@ -0,0 +1,75 @@
+/*
+ * 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
+ *
+ *      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.window.test
+
+import androidx.window.WindowInfoRepo
+import androidx.window.WindowLayoutInfo
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.channels.BufferOverflow.DROP_OLDEST
+import kotlinx.coroutines.flow.MutableSharedFlow
+import org.junit.rules.TestRule
+import org.junit.runner.Description
+import org.junit.runners.model.Statement
+
+/**
+ * A [TestRule] to help test consuming a stream of [WindowLayoutInfo] values.
+ * [WindowLayoutInfoPublisherRule] allows you to push through different [WindowLayoutInfo] values
+ * on demand.
+ *
+ * Here are some recommended testing scenarios.
+ *
+ * To test the scenario where no [WindowLayoutInfo] is ever emitted.  Just set the rule as a
+ * standard rule.
+ *
+ * To test sending a generic feature build your own [WindowLayoutInfo] and publish it through the
+ * method [WindowLayoutInfoPublisherRule.overrideWindowLayoutInfo].
+ *
+ * Some helper methods are provided to test the following scenarios.
+ * <ul>
+ *     <li>A fold in the middle and the dimension matches the shortest window dimension.</li>
+ *     <li>A fold in the middle and the dimension matches the longest window dimension.</li>
+ *     <li>A fold in the middle and has vertical orientation.</li>
+ *     <li>A fold in the middle and has horizontal orientation.</li>
+ * </ul>
+ */
+public class WindowLayoutInfoPublisherRule() : TestRule {
+
+    private val flow = MutableSharedFlow<WindowLayoutInfo>(
+        extraBufferCapacity = 1,
+        onBufferOverflow = DROP_OLDEST
+    )
+    private val overrideServices = PublishWindowInfoRepoDecorator(flow)
+
+    @ExperimentalCoroutinesApi
+    override fun apply(base: Statement, description: Description): Statement {
+        return object : Statement() {
+            override fun evaluate() {
+                WindowInfoRepo.overrideDecorator(overrideServices)
+                base.evaluate()
+                WindowInfoRepo.reset()
+            }
+        }
+    }
+
+    /**
+     * Send an arbitrary [WindowLayoutInfo] through
+     * [androidx.window.WindowInfoRepo.windowLayoutInfo]. Each event is sent only once.
+     */
+    public fun overrideWindowLayoutInfo(info: WindowLayoutInfo) {
+        flow.tryEmit(info)
+    }
+}
\ No newline at end of file