Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions release_notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
- [#865](https://github.com/Flank/flank/pull/865) Flank needs to respect the timeout value as that's a cap for billing purposes. ([adamfilipow92](https://github.com/adamfilipow92), [pawelpasterz](https://github.com/pawelpasterz))
- [#866](https://github.com/Flank/flank/pull/866) Fix printing all matrix links. ([piotradamczyk5](https://github.com/piotradamczyk5))
- [#862](https://github.com/Flank/flank/pull/862) Added printing outcome details. ([piotradamczyk5](https://github.com/piotradamczyk5), [jan-gogo](https://github.com/jan-gogo))
- [#876](https://github.com/Flank/flank/pull/876) Added --directories-to-pull validation and avoid making request with empty toolStepResult. ([piotradamczyk5](https://github.com/piotradamczyk5))
-
-

Expand Down
16 changes: 16 additions & 0 deletions test_runner/src/main/kotlin/ftl/args/ValidateAndroidArgs.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ fun AndroidArgs.validate() {
assertShards()
assertTestTypes()
assertRoboTest()
assertDirectoriesToPull()
}

private fun AndroidArgs.assertAdditionalAppTestApks() {
Expand Down Expand Up @@ -57,3 +58,18 @@ private fun AndroidArgs.assertRoboTest() {
"Options robo-directives and robo-script are mutually exclusive, use only one of them."
)
}

// Validation is done according to https://cloud.google.com/sdk/gcloud/reference/firebase/test/android/run#--directories-to-pull
private fun AndroidArgs.assertDirectoriesToPull() {
val correctNameRegex = "(/[a-zA-Z0-9_\\-.+]+)+/?".toRegex()
directoriesToPull
.filter { !it.startsWith("/sdcard") && !it.startsWith("/data/local/tmp") || !correctNameRegex.matches(it) }
.takeIf { it.isNotEmpty() }
?.also {
throw FlankFatalError(
"Invalid value for [directories-to-pull]: Invalid path $it.\n" +
"Path must be absolute paths under /sdcard or /data/local/tmp (for example, --directories-to-pull /sdcard/tempDir1,/data/local/tmp/tempDir2).\n" +
"Path names are restricted to the characters [a-zA-Z0-9_-./+]. "
)
}
}
15 changes: 13 additions & 2 deletions test_runner/src/main/kotlin/ftl/json/SavedMatrix.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import ftl.reports.api.data.TestSuiteOverviewData
import ftl.reports.api.prepareForJUnitResult
import ftl.util.MatrixState.ERROR
import ftl.util.MatrixState.FINISHED
import ftl.util.MatrixState.INVALID
import ftl.util.StepOutcome.failure
import ftl.util.StepOutcome.flaky
import ftl.util.StepOutcome.inconclusive
Expand Down Expand Up @@ -67,8 +68,7 @@ class SavedMatrix(matrix: TestMatrix) {
val changedLink = webLink != newLink

if (changedState) {
this.state = newState
if (this.state == FINISHED) finished(matrix)
updateState(newState, matrix)
}

if (changedLink) {
Expand All @@ -78,6 +78,17 @@ class SavedMatrix(matrix: TestMatrix) {
return changedState || changedLink
}

private fun updateState(newState: String, testMatrix: TestMatrix) {
state = newState
when (state) {
FINISHED -> finished(testMatrix)
INVALID -> {
outcomeDetails = "Matrix is invalid"
outcome = "---"
}
}
}

private fun finished(matrix: TestMatrix) {
if (matrix.state != FINISHED) {
throw RuntimeException("Matrix ${matrix.testMatrixId} ${matrix.state} != $FINISHED")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ internal fun List<TestExecution>.createJUnitTestResult(
withStackTraces: Boolean = false
) = JUnitTestResult(
testsuites = this
.filterNullToolResultsStep()
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. I wonder why FTL is returning null for toolResultsStep?
  2. Can we reproduce this error?
  3. Its safe to skip some tests execution and get correct results?
  4. Maybe flank should display proper warning about some unexpected behaviour?

Copy link
Copy Markdown
Contributor Author

@piotradamczyk5 piotradamczyk5 Jul 6, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. I wonder why FTL is returning null for toolResultsStep?
  2. Can we reproduce this error?
  3. Its safe to skip some tests execution and get correct results?
  4. Maybe flank should display proper warning about some unexpected behaviour?

AD 1. If matrix is invalid FTL returns null as toolResultsStep
AD 2. This is more to avoid crashing flank in the future, so far, for now, I do not know how to reproduce this issue
AD 3. This is safe because tests results are based on a request made with toolResultsStep which will not succeed without toolResultsStep
AD 4. Ideally, we should not trigger INVALID matrix request, but for now, we do not know which settings will cause matrix invalid (see point 2)

.createTestExecutionDataListAsync()
.prepareForJUnitResult()
.let { executionDataList ->
Expand All @@ -16,3 +17,5 @@ internal fun List<TestExecution>.createJUnitTestResult(
.createJUnitTestSuites()
.toMutableList()
)

private fun List<TestExecution>.filterNullToolResultsStep() = filter { it.toolResultsStep != null }
3 changes: 1 addition & 2 deletions test_runner/src/main/kotlin/ftl/util/Utils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -169,8 +169,7 @@ fun withGlobalExceptionHandling(block: () -> Int) {
}

private fun SavedMatrix.logError() {
println("More details are available at [${this.webLink}]")
println(this.asPrintableTable())
println("Matrix is $state")
Copy link
Copy Markdown
Contributor

@jan-goral jan-goral Jul 6, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There will be no table output if flank throw FTLError before MatrixResultsReport?

Copy link
Copy Markdown
Contributor Author

@piotradamczyk5 piotradamczyk5 Jul 6, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FTLError is thrown after printing table


listOf(
    CostReport,
    MatrixResultsReport
).map {
    it.run(matrices, testSuite, printToStdout = true, args = args)  >>>>>>>>>>>>> PRINTING TABLE
}
if (!matrices.allSuccessful()) {
    listOf(
        HtmlErrorReport
    ).map { it.run(matrices, testSuite, printToStdout = false, args = args) }
}
JUnitReport.run(matrices, testSuite?.apply {
    if (ignoredTestCases.isNotEmpty()) {
        testsuites?.add(ignoredTestCases.toJunitTestsResults())
    }
}, printToStdout = false, args = args)
when {
    args.fullJUnitResult -> processFullJunitResult(args, matrices, testShardChunks)
    args.useLegacyJUnitResult -> processJunitXml(testSuite, args, testShardChunks)
}
matrices.validateMatrices(args.ignoreFailedTests) >>>>>>>>>>> FTLError

}

fun <R : MutableMap<String, Any>, T> mutableMapProperty(
Expand Down
4 changes: 2 additions & 2 deletions test_runner/src/test/kotlin/Debug.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ fun main() {
// run "gradle check" to generate required fixtures
val projectId = System.getenv("GOOGLE_CLOUD_PROJECT")
?: "YOUR PROJECT ID"
val quantity = "single"
val type = "robo"
val quantity = "multiple"
val type = "success"

// Bugsnag keeps the process alive so we must call exitProcess
// https://github.com/bugsnag/bugsnag-java/issues/151
Expand Down
25 changes: 21 additions & 4 deletions test_runner/src/test/kotlin/ftl/args/AndroidArgsTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -733,7 +733,7 @@ AndroidArgs
@Test
fun `cli directoriesToPull`() {
val cli = AndroidRunCommand()
CommandLine(cli).parseArgs("--directories-to-pull=a,b")
CommandLine(cli).parseArgs("--directories-to-pull=/sdcard/test,/data/local/tmp/test")

val yaml = """
gcloud:
Expand All @@ -743,7 +743,22 @@ AndroidArgs
assertThat(AndroidArgs.load(yaml).directoriesToPull).isEmpty()

val androidArgs = AndroidArgs.load(yaml, cli)
assertThat(androidArgs.directoriesToPull).isEqualTo(listOf("a", "b"))
assertThat(androidArgs.directoriesToPull).isEqualTo(listOf("/sdcard/test", "/data/local/tmp/test"))
}

@Test(expected = FlankFatalError::class)
fun `should throw FlankFatalError when invalid cli directoriesToPull`() {
val cli = AndroidRunCommand()
CommandLine(cli).parseArgs("--directories-to-pull=a,b")

val yaml = """
gcloud:
app: $appApk
test: $testApk
"""
assertThat(AndroidArgs.load(yaml).directoriesToPull).isEmpty()

AndroidArgs.load(yaml, cli)
}

@Test
Expand Down Expand Up @@ -1455,6 +1470,8 @@ AndroidArgs
}
}

private fun AndroidArgs.Companion.load(yamlData: String, cli: AndroidRunCommand? = null): AndroidArgs = load(StringReader(yamlData), cli)
private fun AndroidArgs.Companion.load(yamlData: String, cli: AndroidRunCommand? = null): AndroidArgs =
load(StringReader(yamlData), cli)

fun getAndroidShardChunks(args: AndroidArgs): ShardChunks = runBlocking { (args.createAndroidTestContexts().first() as InstrumentationTestContext).shards }
fun getAndroidShardChunks(args: AndroidArgs): ShardChunks =
runBlocking { (args.createAndroidTestContexts().first() as InstrumentationTestContext).shards }
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package ftl.args

import ftl.test.util.TestHelper
import org.junit.Assert.assertEquals
import org.junit.Test
import java.io.StringReader

class ValidateDirectoriesToPullAndroidArgsTest {

@Test
fun `should not throw any error with valid directoriesToPull`() {
// given
val testYaml = """
gcloud:
app: ./src/test/kotlin/ftl/fixtures/tmp/apk/app-debug.apk
test: ./src/test/kotlin/ftl/fixtures/tmp/apk/app-single-success-debug-androidTest.apk
directories-to-pull:
- /sdcard/data/sample
- /data/local/tmp/sample
- /sdcard/
flank:
disable-sharding: true
""".trimIndent()

// when
AndroidArgs.load(StringReader(testYaml)).validate()

// then
// no issues
}

@Test
fun `should not throw any error without directoriesToPull specific`() {
// given
val testYaml = """
gcloud:
app: ./src/test/kotlin/ftl/fixtures/tmp/apk/app-debug.apk
test: ./src/test/kotlin/ftl/fixtures/tmp/apk/app-single-success-debug-androidTest.apk
flank:
disable-sharding: true
""".trimIndent()

// when
AndroidArgs.load(StringReader(testYaml)).validate()

// then
// no issues
}

@Test
fun `should throw error with bad prefixed paths in directoriesToPull`() {
// given
val expectedErrorMessage = getExpectedMessageForPaths(listOf("/sdcard/data/sample!", "/data/local/tmp/sample#"))
val testYaml = """
gcloud:
app: ./src/test/kotlin/ftl/fixtures/tmp/apk/app-debug.apk
test: ./src/test/kotlin/ftl/fixtures/tmp/apk/app-single-success-debug-androidTest.apk
directories-to-pull:
- /sdcard/data/sample!
- /data/local/tmp/sample#
flank:
disable-sharding: true
""".trimIndent()

// when
val exception = TestHelper.getThrowable { AndroidArgs.load(StringReader(testYaml)).validate() }

// then
assertEquals(expectedErrorMessage, exception.message)
}

@Test
fun `should throw error with bad characters in paths in directoriesToPull`() {
// given
val expectedErrorMessage = getExpectedMessageForPaths(listOf("/app/", "/data/sample"))
val testYaml = """
gcloud:
app: ./src/test/kotlin/ftl/fixtures/tmp/apk/app-debug.apk
test: ./src/test/kotlin/ftl/fixtures/tmp/apk/app-single-success-debug-androidTest.apk
directories-to-pull:
- /app/
- /data/sample
- /sdcard/test
flank:
disable-sharding: true
""".trimIndent()

// when
val exception = TestHelper.getThrowable { AndroidArgs.load(StringReader(testYaml)).validate() }

// then
assertEquals(expectedErrorMessage, exception.message)
}

private fun getExpectedMessageForPaths(badPaths: List<String>) =
"Invalid value for [directories-to-pull]: Invalid path $badPaths.\n" +
"Path must be absolute paths under /sdcard or /data/local/tmp (for example, --directories-to-pull /sdcard/tempDir1,/data/local/tmp/tempDir2).\n" +
"Path names are restricted to the characters [a-zA-Z0-9_-./+]. "
}
20 changes: 20 additions & 0 deletions test_runner/src/test/kotlin/ftl/json/SavedMatrixTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import ftl.gc.GcAndroidDevice
import ftl.test.util.FlankTestRunner
import ftl.util.MatrixState.ERROR
import ftl.util.MatrixState.FINISHED
import ftl.util.MatrixState.INVALID
import ftl.util.MatrixState.PENDING
import ftl.util.webLink
import org.junit.Assert
Expand Down Expand Up @@ -183,4 +184,23 @@ class SavedMatrixTest {
savedMatrix.update(testMatrix)
Assert.assertEquals(1, savedMatrix.billableVirtualMinutes)
}

@Test
fun `savedMatrix should have outcome and outcome details properly filled when state is INVALID`() {
val expectedOutcome = "---"
val expectedOutcomeDetails = "Matrix is invalid"
val testMatrix = TestMatrix()
testMatrix.testMatrixId = "123"
testMatrix.state = PENDING
testMatrix.resultStorage = createResultsStorage()

val savedMatrix = SavedMatrix(testMatrix)
savedMatrix.update(testMatrix)

testMatrix.state = INVALID
savedMatrix.update(testMatrix)
Assert.assertEquals(expectedOutcome, savedMatrix.outcome)
Assert.assertEquals(expectedOutcomeDetails, savedMatrix.outcomeDetails)
Assert.assertEquals(INVALID, savedMatrix.state)
}
}
17 changes: 1 addition & 16 deletions test_runner/src/test/kotlin/ftl/util/UtilsTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -197,8 +197,7 @@ class UtilsTest {
// will
exit.expectSystemExitWithStatus(3)
exit.checkAssertionAfterwards {
assertTrue(output.log.contains("More details are available at [${testMatrix1.webLink}]"))
assertOutputIsEqualReferenceTable(output.log, testMatrix1.matrixId)
assertTrue(output.log.contains("Matrix is ${testMatrix1.state}"))
}

// when
Expand Down Expand Up @@ -276,20 +275,6 @@ class UtilsTest {
withGlobalExceptionHandling(block)
}

private fun assertOutputIsEqualReferenceTable(outputLog: String, matrixId: String) {
val referenceTable =
"┌─────────┬───────────┬────────────────────┐\n" +
"│ OUTCOME │ MATRIX ID │ TEST DETAILS │\n" +
"├─────────┼───────────┼────────────────────┤\n" +
"│ Failed │ $matrixId │ Test failed to run │\n" +
"└─────────┴───────────┴────────────────────┘"
referenceTable.split("\n")
.forEach {
println(it)
assertTrue(outputLog.contains(it))
}
}

@Test
fun `should terminate process with exit code 1 if there is not tests to run overall`() {
// given
Expand Down