Skip to content

Commit 5676ac2

Browse files
authored
fix(ios): add xcode timeout result parsing (#985)
* [fix] consider xcode timeout when parsing test run progress. * Consider 1 minute timeout case
1 parent 4da8f00 commit 5676ac2

File tree

5 files changed

+78
-2
lines changed

5 files changed

+78
-2
lines changed

vendor/vendor-apple/base/src/main/kotlin/com/malinskiy/marathon/apple/logparser/parser/DeviceFailureParser.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import com.malinskiy.marathon.apple.logparser.TestEventProducer
44
import com.malinskiy.marathon.apple.test.TestEvent
55
import com.malinskiy.marathon.apple.test.TestRunFailed
66

7-
class DeviceFailureParser : com.malinskiy.marathon.apple.logparser.TestEventProducer {
7+
class DeviceFailureParser : TestEventProducer {
88
private val patterns = listOf(
99
"Failed to install or launch the test runner",
1010
"Software caused connection abort",

vendor/vendor-apple/base/src/main/kotlin/com/malinskiy/marathon/apple/logparser/parser/TestRunProgressParser.kt

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import kotlin.math.roundToInt
1414
class TestRunProgressParser(
1515
private val timer: Timer,
1616
private val targetName: String,
17-
) : com.malinskiy.marathon.apple.logparser.TestEventProducer {
17+
) : TestEventProducer {
1818

1919
val logger = MarathonLogging.logger(TestRunProgressParser::class.java.simpleName)
2020

@@ -29,6 +29,11 @@ class TestRunProgressParser(
2929
* $4 = reason
3030
*/
3131
val FAILING_TEST_MATCHER = "(/.+:\\d+):\\serror:\\s[\\+\\-]\\[(.*)\\s(.*)\\]\\s:(\\s.*)".toRegex()
32+
33+
/**
34+
* Timeout case from https://developer.apple.com/documentation/xctest/xctestcase/3526064-executiontimeallowance
35+
*/
36+
val TIMEOUT_TEST_MATCHER = """Test Case '-\[(.+) (.+)]' exceeded execution time allowance of (\d+) minutes*\. The test may have hung.*""".toRegex()
3237

3338
private var failingTestLine: String? = null
3439

@@ -37,6 +42,8 @@ class TestRunProgressParser(
3742
parseTestStarted(line)?.let { listOf(it) }
3843
} else if (line.matches(TEST_CASE_FINISHED)) {
3944
parseTestFinished(line)?.let { listOf(it) }
45+
} else if (line.matches(TIMEOUT_TEST_MATCHER)) {
46+
parseTestTimeout(line)?.let { listOf(it) }
4047
} else if (line.matches(FAILING_TEST_MATCHER)) {
4148
failingTestLine = line
4249
null
@@ -108,6 +115,40 @@ class TestRunProgressParser(
108115
return null
109116
}
110117

118+
private fun parseTestTimeout(line: String): TestEvent? {
119+
val matchResult = TIMEOUT_TEST_MATCHER.find(line)
120+
val pkgWithClass = matchResult?.groups?.get(1)?.value
121+
var pkg: String? = null
122+
var clazz: String? = null
123+
if (pkgWithClass != null) {
124+
if (pkgWithClass.contains('.')) {
125+
pkg = pkgWithClass.substringBeforeLast('.', missingDelimiterValue = "")
126+
clazz = pkgWithClass.substringAfter('.', missingDelimiterValue = pkgWithClass)
127+
} else {
128+
pkg = targetName
129+
clazz = pkgWithClass
130+
}
131+
}
132+
133+
val method = matchResult?.groups?.get(2)?.value
134+
val duration = matchResult?.groups?.get(3)?.value?.toFloat()?.times(60)
135+
136+
logger.debug { "Test $pkg.$clazz.$method finished with result <timeout> after $duration seconds" }
137+
138+
if (pkg != null && clazz != null && method != null && duration != null) {
139+
val test = Test(pkg, clazz, method, emptyList())
140+
141+
val endTime = timer.currentTimeMillis()
142+
val startTime = endTime - (duration * 1000).roundToInt()
143+
144+
val trace = failingTestLine?.let {
145+
parseFailingTest(it)
146+
}
147+
return TestFailed(test, startTime, endTime, trace)
148+
}
149+
return null
150+
}
151+
111152
private fun parseTestStarted(line: String): TestStarted? {
112153
failingTestLine = null
113154
val matchResult = TEST_CASE_STARTED.find(line)

vendor/vendor-apple/base/src/test/kotlin/com/malinskiy/marathon/apple/logparser/parser/TestRunProgressParserTest.kt

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import assertk.assertThat
44
import assertk.assertions.isEqualTo
55
import com.malinskiy.marathon.apple.test.TestEvent
66
import com.malinskiy.marathon.time.Timer
7+
import org.amshove.kluent.internal.assertEquals
78
import org.junit.jupiter.api.BeforeEach
89
import org.junit.jupiter.api.Test
910
import org.mockito.Mockito.reset
@@ -104,4 +105,21 @@ class TestRunProgressParserTest {
104105
assertThat(events.map { it.toString() }.reduce { acc, s -> acc + "\n" + s })
105106
.isEqualTo(javaClass.getResourceAsStream("/fixtures/test_output/patrol_1.expected").reader().readText().trimEnd())
106107
}
108+
109+
@Test
110+
fun testSample5() {
111+
val parser = TestRunProgressParser(mockTimer, "")
112+
113+
val events = mutableListOf<TestEvent>()
114+
javaClass.getResourceAsStream("/fixtures/test_output/timeout_0.log.input").bufferedReader().use {
115+
it.lines().forEach { line ->
116+
parser.process(line)?.let {
117+
events.addAll(it)
118+
}
119+
}
120+
}
121+
122+
assertThat(events.map { it.toString() }.reduce { acc, s -> acc + "\n" + s })
123+
.isEqualTo(javaClass.getResourceAsStream("/fixtures/test_output/timeout_0.expected").reader().readText().trimEnd())
124+
}
107125
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
TestStarted(id=Test(pkg=sample_appUITests, clazz=MoreTests, method=testPresentModal, metaProperties=[]))
2+
TestFailed(id=Test(pkg=sample_appUITests, clazz=MoreTests, method=testPresentModal, metaProperties=[]), startTime=1537187516000, endTime=1537187696000, trace=null)
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
Test Case '-[sample_appUITests.MoreTests testPresentModal]' started.
2+
t = 0.00s Start Test at 2018-09-15 01:08:51.148
3+
t = 0.03s Set Up
4+
t = 0.03s Open com.agoda.sample-app
5+
t = 0.06s Launch com.agoda.sample-app
6+
t = 2.61s Wait for com.agoda.sample-app to idle
7+
t = 3.85s Waiting 30.0s for Button to exist
8+
t = 4.85s Checking `Expect predicate `exists == 1` for object Button`
9+
t = 4.86s Tap Button
10+
t = 4.86s Wait for com.agoda.sample-app to idle
11+
t = 4.87s Find the Button
12+
t = 4.98s Wait for com.agoda.sample-app to idle
13+
t = 4.99s Synthesize event
14+
t = 5.07s Wait for com.agoda.sample-app to idle
15+
Test Case '-[sample_appUITests.MoreTests testPresentModal]' exceeded execution time allowance of 3 minutes. The test may have hung; check Xcode's test report for additional diagnostics. If the test requires additional time, use -[XCTestCase executionTimeAllowance] or one of xcodebuild's command-line overrides.

0 commit comments

Comments
 (0)