Move Kosmos to its own build module
Bug: 302691505
Flag: NA
Test: atest
Change-Id: I0f2fbf525fb37133f801d07e62eb3f4cf89e26c6
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index dc7850f..8113be1 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -198,12 +198,29 @@
}
filegroup {
+ name: "kosmos-src",
+ srcs: ["tests/utils/kosmos/src/**/*.kt"],
+ path: "tests/utils/kosmos",
+}
+
+java_library {
+ name: "kosmos",
+ host_supported: true,
+ srcs: [":kosmos-src"],
+ static_libs: [
+ "kotlin-reflect",
+ "kotlin-stdlib",
+ ],
+}
+
+filegroup {
name: "SystemUI-tests-utils",
srcs: [
"tests/utils/src/**/*.java",
"tests/utils/src/**/*.kt",
+ ":kosmos-src",
],
- path: "tests/utils/src",
+ path: "tests/utils",
}
filegroup {
@@ -336,6 +353,7 @@
"kotlin-test",
"SystemUICustomizationTestUtils",
"androidx.compose.runtime_runtime",
+ "kosmos",
],
libs: [
"android.test.runner",
@@ -419,6 +437,7 @@
"inline-mockito-robolectric-prebuilt",
"platform-parametric-runner-lib",
"SystemUICustomizationTestUtils",
+ "kosmos",
],
libs: [
"android.test.runner",
@@ -452,6 +471,7 @@
"androidx.test.uiautomator_uiautomator",
"androidx.core_core-animation-testing",
"androidx.test.ext.junit",
+ "kosmos",
],
libs: [
"android.test.runner",
diff --git a/packages/SystemUI/tests/utils/kosmos/src/com/android/systemui/kosmos/Kosmos.kt b/packages/SystemUI/tests/utils/kosmos/src/com/android/systemui/kosmos/Kosmos.kt
new file mode 100644
index 0000000..685bb0a
--- /dev/null
+++ b/packages/SystemUI/tests/utils/kosmos/src/com/android/systemui/kosmos/Kosmos.kt
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2024 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 com.android.systemui.kosmos
+
+import kotlin.reflect.KProperty
+
+// (Historical note: The name Kosmos is meant to invoke "Kotlin", the "Object Mother" pattern
+// (https://martinfowler.com/bliki/ObjectMother.html), and of course the Greek word "kosmos" for
+// the "order of the world" (https://en.wiktionary.org/wiki/%CE%BA%CF%8C%CF%83%CE%BC%CE%BF%CF%82)
+
+/**
+ * Each Kosmos is its own self-contained set of fixtures, which may reference each other. Fixtures
+ * can be defined through extension properties in any file:
+ * ```
+ * // fixture that must be set:
+ * var Kosmos.context by Fixture<Context>()
+ *
+ * // fixture with overrideable default.
+ * var Kosmos.landscapeMode by Fixture { false }
+ *
+ * // fixture forbidding override (note `val`, and referencing context fixture from above)
+ * val Kosmos.lifecycleScope by Fixture { context.lifecycleScope }
+ * ```
+ *
+ * To use the fixtures, create an instance of Kosmos and retrieve the values you need:
+ * ```
+ * val k = Kosmos()
+ * k.context = mContext
+ * val underTest = YourInteractor(
+ * context = k.context,
+ * landscapeMode = k.landscapeMode,
+ * )
+ * ```
+ */
+interface Kosmos {
+ /**
+ * Lookup a fixture in the Kosmos by [name], using [creator] to instantiate and store one if
+ * there is none present.
+ */
+ fun <T> get(name: String, creator: (Kosmos.() -> T)?): T
+
+ /** Sets the [value] of a fixture with the given [name]. */
+ fun set(name: String, value: Any?)
+
+ /**
+ * A value in the kosmos that has a single value once it's read. It can be overridden before
+ * first use only; all objects that are dependent on this fixture will get the same value.
+ *
+ * Example classic uses would be a clock, filesystem, or singleton controller.
+ *
+ * If no [creator] parameter is provided, the fixture must be set before use.
+ */
+ class Fixture<T>(private val creator: (Kosmos.() -> T)? = null) {
+ operator fun getValue(thisRef: Kosmos, property: KProperty<*>): T =
+ thisRef.get(property.name, creator)
+
+ operator fun setValue(thisRef: Kosmos, property: KProperty<*>, value: T) {
+ thisRef.set(property.name, value)
+ }
+ }
+}
+
+/** Constructs a fresh Kosmos. */
+fun Kosmos(): Kosmos = KosmosRegistry()
+
+private class KosmosRegistry : Kosmos {
+ val map: MutableMap<String, Any?> = mutableMapOf()
+ val gotten: MutableSet<String> = mutableSetOf()
+
+ override fun <T> get(name: String, creator: (Kosmos.() -> T)?): T {
+ gotten.add(name)
+ if (name !in map) {
+ checkNotNull(creator) { "Fixture $name has no default, and is read before set." }
+ map[name] = creator()
+ }
+ @Suppress("UNCHECKED_CAST") return map[name] as T
+ }
+
+ override fun set(name: String, value: Any?) {
+ check(name !in gotten) { "Tried to set fixture '$name' after it's already been read." }
+ map[name] = value
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/Kosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/Kosmos.kt
deleted file mode 100644
index c74cdd4..0000000
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/Kosmos.kt
+++ /dev/null
@@ -1,69 +0,0 @@
-package com.android.systemui.kosmos
-
-import kotlin.reflect.KProperty
-
-// (Historical note: The name Kosmos is meant to invoke "Kotlin", the "Object Mother" pattern
-// (https://martinfowler.com/bliki/ObjectMother.html), and of course the Greek word "kosmos" for
-// the "order of the world" (https://en.wiktionary.org/wiki/%CE%BA%CF%8C%CF%83%CE%BC%CE%BF%CF%82)
-/**
- * Each Kosmos is its own self-contained set of fixtures, which may reference each other. Fixtures
- * can be defined through extension properties in any file:
- * ```
- * // fixture that must be set:
- * var Kosmos.context by Fixture<Context>()
- *
- * // fixture with overrideable default.
- * var Kosmos.landscapeMode by Fixture { false }
- *
- * // fixture forbidding override (note `val`, and referencing context fixture from above)
- * val Kosmos.lifecycleScope by Fixture { context.lifecycleScope }
- * ```
- *
- * To use the fixtures, create an instance of Kosmos and retrieve the values you need:
- * ```
- * val k = Kosmos()
- * k.context = mContext
- * val underTest = YourInteractor(
- * context = k.context,
- * landscapeMode = k.landscapeMode,
- * )
- * ```
- */
-class Kosmos {
- private val map: MutableMap<String, Any?> = mutableMapOf()
- private val gotten: MutableSet<String> = mutableSetOf()
-
- /**
- * A value in the kosmos that has a single value once it's read. It can be overridden before
- * first use only; all objects that are dependent on this fixture will get the same value.
- *
- * Example classic uses would be a clock, filesystem, or singleton controller.
- *
- * If no [creator] parameter is provided, the fixture must be set before use.
- */
- class Fixture<T>(private val creator: (Kosmos.() -> T)? = null) {
- operator fun getValue(thisRef: Kosmos, property: KProperty<*>): T {
- thisRef.gotten.add(property.name)
- @Suppress("UNCHECKED_CAST")
- if (!thisRef.map.contains(property.name)) {
- if (creator == null) {
- throw IllegalStateException(
- "Fixture ${property.name} has no default, and is read before set."
- )
- } else {
- val nonNullCreator = creator
- // The Kotlin compiler seems to need this odd workaround
- thisRef.map[property.name] = thisRef.nonNullCreator()
- }
- }
- return thisRef.map[property.name] as T
- }
-
- operator fun setValue(thisRef: Kosmos, property: KProperty<*>, value: T) {
- check(!thisRef.gotten.contains(property.name)) {
- "Tried to set fixture '${property.name}' after it's already been read."
- }
- thisRef.map[property.name] = value
- }
- }
-}