blob: b046636c8cd1ea84314acb6cb46a4cefffd55330 [file] [log] [blame]
/*
* 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.
*/
package com.android.server.wm.flicker.assertions
import com.android.server.wm.flicker.RunResultArtifacts
import com.android.server.wm.flicker.dsl.AssertionTag
import com.android.server.wm.flicker.traces.FlickerSubjectException
import com.google.common.truth.Fact
import java.io.ByteArrayOutputStream
import java.io.PrintStream
class FlickerAssertionErrorBuilder {
private var error: Throwable? = null
private var traceFile: RunResultArtifacts? = null
private var tag = ""
fun fromError(error: Throwable): FlickerAssertionErrorBuilder = apply {
this.error = error
}
fun withTrace(traceFile: RunResultArtifacts?): FlickerAssertionErrorBuilder = apply {
this.traceFile = traceFile
}
fun atTag(tag: String): FlickerAssertionErrorBuilder = apply {
this.tag = when (tag) {
AssertionTag.START -> "before transition (initial state)"
AssertionTag.END -> "after transition (final state)"
AssertionTag.ALL -> "during transition"
else -> "at user-defined location ($tag)"
}
}
fun build(): FlickerAssertionError {
return FlickerAssertionError(errorMessage, rootCause, traceFile)
}
private val errorMessage get() = buildString {
val error = error
requireNotNull(error)
if (error is FlickerSubjectException) {
appendLine(error.errorType)
appendLine()
append(error.errorDescription)
appendLine()
append(error.subjectInformation)
append("\t").appendLine(Fact.fact("Location", tag))
appendLine()
} else {
appendLine(error.message)
}
append("Trace file:").append(traceFileMessage)
appendLine()
appendLine("Cause:")
append(rootCauseStackTrace)
appendLine()
appendLine("Full stacktrace:")
appendLine()
}
private val traceFileMessage get() = buildString {
traceFile?.path?.let {
append("\t")
append(it)
}
}
private val rootCauseStackTrace: String get() {
val rootCause = rootCause
return if (rootCause != null) {
val baos = ByteArrayOutputStream()
PrintStream(baos, true)
.use { ps -> rootCause.printStackTrace(ps) }
"\t$baos"
} else {
""
}
}
/**
* In some paths the exceptions are encapsulated by the Truth subjects
* To make sure the correct error is printed, located the first non-subject
* related exception and use that as cause.
*/
private val rootCause: Throwable? get() {
var childCause: Throwable? = this.error?.cause
if (childCause != null && childCause is FlickerSubjectException) {
childCause = childCause.cause
}
return childCause
}
}