1 /*
<lambda>null2  * Copyright (C) 2024 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 package com.android.launcher3.util
17 
18 import androidx.test.ext.junit.runners.AndroidJUnit4
19 import com.google.common.collect.ImmutableList
20 import java.util.Locale
21 import kotlin.annotation.AnnotationRetention.RUNTIME
22 import kotlin.annotation.AnnotationTarget.CLASS
23 import org.junit.runner.Runner
24 import org.junit.runners.Suite
25 
26 /**
27  * A custom runner for multivalent tests with launcher specific features
28  * 1) Adds support for @UiThread annotations in deviceless tests
29  * 2) Allows emulating multiple devices when running in deviceless mode
30  */
31 class LauncherMultivalentJUnit(klass: Class<*>?) : Suite(klass, ImmutableList.of()) {
32 
33     val runners: List<Runner> =
34         (testClass.getAnnotation(EmulatedDevices::class.java)?.value ?: emptyArray()).let { devices
35             ->
36             if (!isRunningInRobolectric) {
37                 return@let null
38             }
39             try {
40                 (testClass.javaClass.classLoader.loadClass(ROBOLECTRIC_RUNNER) as Class<Runner>)
41                     .getConstructor(Class::class.java, String::class.java)
42                     .let { ctor ->
43                         if (devices.isEmpty()) listOf(ctor.newInstance(testClass.javaClass, null))
44                         else devices.map { ctor.newInstance(testClass.javaClass, it) }
45                     }
46             } catch (e: Exception) {
47                 null
48             }
49         }
50             ?: listOf(AndroidJUnit4(testClass.javaClass))
51 
52     override fun getChildren() = runners
53 
54     /**
55      * Annotation to be added to a test so run it on a list of emulated devices for deviceless test
56      */
57     @Retention(RUNTIME) @Target(CLASS) annotation class EmulatedDevices(val value: Array<String>)
58 
59     companion object {
60         private const val ROBOLECTRIC_RUNNER = "com.android.launcher3.util.RobolectricDeviceRunner"
61 
62         val isRunningInRobolectric: Boolean
63             get() =
64                 if (
65                     System.getProperty("java.runtime.name")
66                         .lowercase(Locale.getDefault())
67                         .contains("android")
68                 ) {
69                     false
70                 } else {
71                     try {
72                         // Check if robolectric runner exists
73                         Class.forName("org.robolectric.RobolectricTestRunner") != null
74                     } catch (e: ClassNotFoundException) {
75                         false
76                     }
77                 }
78     }
79 }
80