blob: 6932086bd50dacdfb1caf309e3631b5cbb4ce797 [file] [log] [blame]
/*
* Copyright (C) 2020 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.build.gradle.internal
import com.android.SdkConstants
import com.android.build.gradle.internal.fixtures.FakeGradleDirectory
import com.android.build.gradle.internal.fixtures.FakeGradleProvider
import com.android.build.gradle.internal.fixtures.FakeGradleRegularFile
import com.android.prefs.AndroidLocationsSingleton
import com.android.repository.io.FileOpUtils
import com.android.sdklib.repository.AndroidSdkHandler
import com.android.testutils.file.recordExistingFile
import com.android.utils.ILogger
import com.google.common.truth.Truth.assertThat
import java.io.File
import java.nio.file.Files
import java.nio.file.Path
import org.junit.Assert.assertThrows
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TemporaryFolder
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
import org.mockito.Answers.RETURNS_DEEP_STUBS
import org.mockito.ArgumentMatchers.anyString
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.Mockito.`when`
import org.mockito.Mockito.mock
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.junit.MockitoJUnit
@RunWith(JUnit4::class)
class AvdManagerTest {
@get:Rule
val mockitoRule = MockitoJUnit.rule()
@get:Rule
val tmpFolder = TemporaryFolder()
private val rootDir: Path by lazy {
tmpFolder.newFolder().toPath()
}
private lateinit var manager: AvdManager
private lateinit var sdkFolder: Path
private lateinit var systemImageFolder: Path
private lateinit var emulatorFolder: Path
private lateinit var androidPrefsFolder: Path
private lateinit var avdFolder: Path
private lateinit var adbExecutable: Path
private lateinit var snapshotHandler: AvdSnapshotHandler
private lateinit var versionedSdkLoader: SdkComponentsBuildService.VersionedSdkLoader
@Mock(answer = RETURNS_DEEP_STUBS)
lateinit var lockManager: ManagedVirtualDeviceLockManager
@Before
fun setup() {
sdkFolder = Files.createDirectories(rootDir.resolve("sdk"))
systemImageFolder = sdkFolder.resolve("system-images/android-29/default/x86")
Files.createDirectories(systemImageFolder)
val vendorImage = systemImageFolder.resolve("system.img")
vendorImage.recordExistingFile()
val userImg =
systemImageFolder.resolve(com.android.sdklib.internal.avd.AvdManager.USERDATA_IMG)
userImg.recordExistingFile()
emulatorFolder = sdkFolder.resolve("tools/lib/emulator")
Files.createDirectories(emulatorFolder)
androidPrefsFolder = rootDir.resolve("android-home")
avdFolder = rootDir.resolve("avd")
adbExecutable = rootDir.resolve("adb")
Files.createDirectories(avdFolder)
snapshotHandler = mock(AvdSnapshotHandler::class.java)
versionedSdkLoader = setupVersionedSdkLoader()
val sdkHandler = setupSdkHandler()
manager = AvdManager(
FileOpUtils.toFile(avdFolder),
FakeGradleProvider(versionedSdkLoader),
sdkHandler,
AndroidLocationsSingleton,
snapshotHandler,
lockManager
)
}
@Test
fun noDevicesFromEmptyFolder() {
assertThat(manager.allAvds()).hasSize(0)
}
@Test
fun addSingleDevice() {
manager.createAvd(
FakeGradleProvider(FakeGradleDirectory(FileOpUtils.toFile(systemImageFolder))),
"system-images;android-29;default;x86",
"device1",
"Pixel 2")
val allAvds = manager.allAvds()
assertThat(allAvds).hasSize(1)
assertThat(allAvds.first()).isEqualTo("device1")
}
@Test
fun addMultipleDevices() {
manager.createAvd(
FakeGradleProvider(FakeGradleDirectory(FileOpUtils.toFile(systemImageFolder))),
"system-images;android-29;default;x86",
"device1",
"Pixel 2")
manager.createAvd(
FakeGradleProvider(FakeGradleDirectory(FileOpUtils.toFile(systemImageFolder))),
"system-images;android-29;default;x86",
"device2",
"Pixel 3")
manager.createAvd(
FakeGradleProvider(FakeGradleDirectory(FileOpUtils.toFile(systemImageFolder))),
"system-images;android-29;default;x86",
"device3",
"Pixel 2")
val allAvds = manager.allAvds()
assertThat(allAvds).hasSize(3)
assertThat(allAvds).contains("device1")
assertThat(allAvds).contains("device2")
assertThat(allAvds).contains("device3")
}
@Test
fun addSameDeviceDoesNotDuplicate() {
manager.createAvd(
FakeGradleProvider(FakeGradleDirectory(FileOpUtils.toFile(systemImageFolder))),
"system-images;android-29;default;x86",
"device1",
"Pixel 2")
manager.createAvd(
FakeGradleProvider(FakeGradleDirectory(FileOpUtils.toFile(systemImageFolder))),
"system-images;android-29;default;x86",
"device1",
"Pixel 2")
val allAvds = manager.allAvds()
assertThat(allAvds).hasSize(1)
assertThat(allAvds.first()).isEqualTo("device1")
}
@Test
fun testDeleteDevices() {
manager.createAvd(
FakeGradleProvider(FakeGradleDirectory(FileOpUtils.toFile(systemImageFolder))),
"system-images;android-29;default;x86",
"device1",
"Pixel 2")
manager.createAvd(
FakeGradleProvider(FakeGradleDirectory(FileOpUtils.toFile(systemImageFolder))),
"system-images;android-29;default;x86",
"device2",
"Pixel 3")
var allAvds = manager.allAvds()
assertThat(allAvds).hasSize(2)
manager.deleteAvds(listOf("device1"))
allAvds = manager.allAvds()
assertThat(allAvds).hasSize(1)
assertThat(allAvds.first()).isEqualTo("device2")
}
@Test
fun testSnapshotCreation() {
`when`(snapshotHandler.getEmulatorExecutable(versionedSdkLoader.emulatorDirectoryProvider))
.thenReturn(FileOpUtils.toFile(emulatorFolder.resolve("emulator")))
`when`(
snapshotHandler.checkSnapshotLoadable(
anyString(),
any(File::class.java),
any(File::class.java),
anyString(),
any(ILogger::class.java),
anyString()))
// first return false to force generation, then return true to assert success.
.thenReturn(false, true)
manager.createAvd(
FakeGradleProvider(FakeGradleDirectory(FileOpUtils.toFile(systemImageFolder))),
"system-images;android-29;default;x86",
"device1",
"Pixel 2")
manager.loadSnapshotIfNeeded("device1", "auto-no-window")
verify(snapshotHandler)
.generateSnapshot(
anyString(),
any(File::class.java),
any(File::class.java),
anyString(),
any(ILogger::class.java))
}
@Test
fun testSnapshotSkippedIfValid() {
`when`(snapshotHandler.getEmulatorExecutable(versionedSdkLoader.emulatorDirectoryProvider))
.thenReturn(FileOpUtils.toFile(emulatorFolder.resolve("emulator")))
`when`(
snapshotHandler.checkSnapshotLoadable(
anyString(),
any(File::class.java),
any(File::class.java),
anyString(),
any(ILogger::class.java),
anyString()))
.thenReturn(true)
manager.createAvd(
FakeGradleProvider(FakeGradleDirectory(FileOpUtils.toFile(systemImageFolder))),
"system-images;android-29;default;x86",
"device1",
"Pixel 2")
manager.loadSnapshotIfNeeded("device1", "auto-no-window")
verify(snapshotHandler, times(0))
.generateSnapshot(
anyString(),
any(File::class.java),
any(File::class.java),
anyString(),
any(ILogger::class.java))
}
@Test
fun synchronizationLockFileIsCleanedUpAfterUse() {
manager.createAvd(
FakeGradleProvider(FakeGradleDirectory(FileOpUtils.toFile(systemImageFolder))),
"system-images;android-29;default;x86",
"device1",
"Pixel 2")
val allAvds = manager.allAvds()
assertThat(allAvds).hasSize(1)
assertThat(allAvds.first()).isEqualTo("device1")
assertThat(avdFolder.toFile().list()).isNotEmpty()
manager.deleteAvds(allAvds)
assertThat(avdFolder.toFile().list()).isEmpty()
}
@Test
fun invalidHardwareProfileShouldSuggestNearMatchDevices() {
val errorMessage = assertThrows(IllegalStateException::class.java) {
manager.createAvd(
FakeGradleProvider(FakeGradleDirectory(FileOpUtils.toFile(systemImageFolder))),
"system-images;android-29;default;x86",
"device1",
"Pixel 300")
}
assertThat(errorMessage.message).isEqualTo("""
Failed to find hardware profile for name: Pixel 300
Try one of the following device profiles: Pixel 3, Pixel 3a, Pixel C, Pixel XL, Pixel 2
""".trimIndent())
}
@Test
fun invalidHardwareProfileShouldSuggestPixel6IfNoGoodOtherCandidates() {
val errorMessage = assertThrows(IllegalStateException::class.java) {
manager.createAvd(
FakeGradleProvider(FakeGradleDirectory(FileOpUtils.toFile(systemImageFolder))),
"system-images;android-29;default;x86",
"device1",
"r4ndom-device")
}
assertThat(errorMessage.message).isEqualTo("""
Failed to find hardware profile for name: r4ndom-device
Try one of the following device profiles: Pixel 6, Pixel C, Pixel 2, Pixel 3, Pixel 4
""".trimIndent())
}
private fun setupVersionedSdkLoader(): SdkComponentsBuildService.VersionedSdkLoader =
mock(SdkComponentsBuildService.VersionedSdkLoader::class.java).also {
`when`(it.sdkDirectoryProvider)
.thenReturn(FakeGradleProvider(FakeGradleDirectory(FileOpUtils.toFile(sdkFolder))))
`when`(it.sdkImageDirectoryProvider(anyString()))
.thenReturn(FakeGradleProvider(FakeGradleDirectory(FileOpUtils.toFile(systemImageFolder))))
`when`(it.emulatorDirectoryProvider)
.thenReturn(FakeGradleProvider(FakeGradleDirectory(FileOpUtils.toFile(emulatorFolder))))
`when`(it.adbExecutableProvider)
.thenReturn(FakeGradleProvider(FakeGradleRegularFile(FileOpUtils.toFile(adbExecutable))))
}
private fun setupSdkHandler(): AndroidSdkHandler {
emulatorFolder.resolve("snapshots.img").recordExistingFile()
emulatorFolder.resolve(SdkConstants.FD_LIB).resolve(SdkConstants.FN_HARDWARE_INI)
.recordExistingFile()
recordSysImg()
return AndroidSdkHandler(sdkFolder, androidPrefsFolder)
}
private fun recordSysImg() {
systemImageFolder.resolve("system.img").recordExistingFile()
systemImageFolder.resolve(com.android.sdklib.internal.avd.AvdManager.USERDATA_IMG)
.recordExistingFile(
)
systemImageFolder.resolve("skins/res1/layout").recordExistingFile()
systemImageFolder.resolve("skins/sample").recordExistingFile()
systemImageFolder.resolve("skins/res2/layout").recordExistingFile()
systemImageFolder.resolve("package.xml").recordExistingFile(
0,
"""<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns3:sdk-sys-img
xmlns:ns2="http://schemas.android.com/sdk/android/repo/repository2/01"
xmlns:ns3="http://schemas.android.com/sdk/android/repo/sys-img2/01"
xmlns:ns4="http://schemas.android.com/repository/android/common/01"
xmlns:ns5="http://schemas.android.com/sdk/android/repo/addon2/01">
<license id="license" type="text">A Very Valid License
</license><localPackage path="system-images;android-29;default;x86"
obsolete="false">
<type-details xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:type="ns3:sysImgDetailsType"><api-level>29</api-level>
<tag><id>default</id><display>Default</display></tag><abi>x86</abi>
</type-details><revision><major>5</major></revision>
<display-name>Intel x86 Atom System Image</display-name>
<uses-license ref="license"/></localPackage>
</ns3:sdk-sys-img>
""".trimIndent().toByteArray())
}
// to fix "cannot be null" issues with argument matchers
private fun <T> any(type: Class<T>): T = Mockito.any(type)
}