blob: 733f46387a838ee33b81febc6691c1f6e057f365 [file] [log] [blame]
/*
* Copyright (C) 2023 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 android.tools.utils
import android.content.Context
import android.tools.common.Scenario
import android.tools.common.ScenarioBuilder
import android.tools.common.ScenarioImpl
import android.tools.common.Timestamp
import android.tools.common.Timestamps
import android.tools.common.io.Reader
import android.tools.common.io.ResultArtifactDescriptor
import android.tools.common.io.RunStatus
import android.tools.device.traces.io.ArtifactBuilder
import android.tools.device.traces.io.ResultWriter
import android.tools.device.traces.parsers.surfaceflinger.LayersTraceParser
import android.tools.device.traces.parsers.wm.WindowManagerDumpParser
import android.tools.device.traces.parsers.wm.WindowManagerTraceParser
import android.tools.rules.CacheCleanupRule
import android.tools.rules.InitializeCrossPlatformRule
import android.tools.rules.StopAllTracesRule
import androidx.test.platform.app.InstrumentationRegistry
import com.google.common.io.ByteStreams
import com.google.common.truth.Truth
import java.io.File
import java.io.FileInputStream
import java.io.IOException
import java.util.zip.ZipInputStream
import kotlin.io.path.createTempDirectory
import kotlin.io.path.name
import org.junit.rules.RuleChain
/** Factory function to create cleanup test rule */
fun CleanFlickerEnvironmentRule(): RuleChain =
RuleChain.outerRule(InitializeCrossPlatformRule())
.around(StopAllTracesRule())
.around(CacheCleanupRule())
val TEST_SCENARIO = ScenarioBuilder().forClass("test").build() as ScenarioImpl
/**
* Runs `r` and asserts that an exception with type `expectedThrowable` is thrown.
*
* @param r the [Runnable] which is run and expected to throw.
* @throws AssertionError if `r` does not throw, or throws a runnable that is not an instance of
* `expectedThrowable`.
*/
inline fun <reified ExceptionType> assertThrows(r: () -> Unit): ExceptionType {
try {
r()
} catch (t: Throwable) {
when {
ExceptionType::class.java.isInstance(t) -> return t as ExceptionType
t is Exception ->
throw AssertionError(
"Expected ${ExceptionType::class.java}, but got '${t.javaClass}'",
t
)
// Re-throw Errors and other non-Exception throwables.
else -> throw t
}
}
error("Expected exception ${ExceptionType::class.java}, but nothing was thrown")
}
fun assertFail(expectedMessage: String, predicate: () -> Any) {
val error = assertThrows<AssertionError> { predicate() }
Truth.assertThat(error).hasMessageThat().contains(expectedMessage)
}
fun assertThatErrorContainsDebugInfo(error: Throwable) {
Truth.assertThat(error).hasMessageThat().contains("What?")
Truth.assertThat(error).hasMessageThat().contains("Where?")
}
fun assertArchiveContainsFiles(archivePath: File, expectedFiles: List<String>) {
Truth.assertWithMessage("Expected trace archive `$archivePath` to exist")
.that(archivePath.exists())
.isTrue()
val archiveStream = ZipInputStream(FileInputStream(archivePath))
val actualFiles = generateSequence { archiveStream.nextEntry }.map { it.name }.toList()
Truth.assertWithMessage("Trace archive doesn't contain all expected traces")
.that(actualFiles)
.containsExactlyElementsIn(expectedFiles)
}
fun getWmTraceReaderFromAsset(
relativePath: String,
from: Long = Long.MIN_VALUE,
to: Long = Long.MAX_VALUE,
addInitialEntry: Boolean = true,
legacyTrace: Boolean = false,
): Reader {
return ParsedTracesReader(
artifact = InMemoryArtifact(relativePath),
wmTrace =
WindowManagerTraceParser(legacyTrace)
.parse(
readAsset(relativePath),
Timestamps.from(elapsedNanos = from),
Timestamps.from(elapsedNanos = to),
addInitialEntry,
clearCache = false
)
)
}
fun getWmDumpReaderFromAsset(relativePath: String): Reader {
return ParsedTracesReader(
artifact = InMemoryArtifact(relativePath),
wmTrace = WindowManagerDumpParser().parse(readAsset(relativePath), clearCache = false)
)
}
fun getLayerTraceReaderFromAsset(
relativePath: String,
ignoreOrphanLayers: Boolean = true,
legacyTrace: Boolean = false,
from: Timestamp = Timestamps.min(),
to: Timestamp = Timestamps.max()
): Reader {
return ParsedTracesReader(
artifact = InMemoryArtifact(relativePath),
layersTrace =
LayersTraceParser(
ignoreLayersStackMatchNoDisplay = false,
ignoreLayersInVirtualDisplay = false,
legacyTrace = legacyTrace,
) {
ignoreOrphanLayers
}
.parse(readAsset(relativePath), from, to)
)
}
@Throws(Exception::class)
fun readAsset(relativePath: String): ByteArray {
val context: Context = InstrumentationRegistry.getInstrumentation().context
val inputStream = context.resources.assets.open("testdata/$relativePath")
return ByteStreams.toByteArray(inputStream)
}
@Throws(IOException::class)
fun readAssetAsFile(relativePath: String): File {
val context: Context = InstrumentationRegistry.getInstrumentation().context
return File(context.cacheDir, relativePath).also {
if (!it.exists()) {
it.outputStream().use { cache ->
context.assets.open("testdata/$relativePath").use { inputStream ->
inputStream.copyTo(cache)
}
}
}
}
}
fun newTestResultWriter(
scenario: Scenario = ScenarioBuilder().forClass(kotlin.io.path.createTempFile().name).build()
) =
ResultWriter()
.forScenario(scenario)
.withOutputDir(createTempDirectory().toFile())
.setRunComplete()
fun assertExceptionMessage(error: Throwable?, expectedValue: String) {
Truth.assertWithMessage("Expected exception")
.that(error)
.hasMessageThat()
.contains(expectedValue)
}
fun outputFileName(status: RunStatus) =
File("/sdcard/flicker/${status.prefix}__test_ROTATION_0_GESTURAL_NAV.zip")
fun createDefaultArtifactBuilder(
status: RunStatus,
outputDir: File = createTempDirectory().toFile(),
files: Map<ResultArtifactDescriptor, File> = emptyMap()
) =
ArtifactBuilder()
.withScenario(TEST_SCENARIO)
.withOutputDir(outputDir)
.withStatus(status)
.withFiles(files)