-
Notifications
You must be signed in to change notification settings - Fork 119
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Use API instead of XML for result parsing for android #666
Conversation
// it is making api calls under the hood | ||
testExecution.createTestExecutionData() | ||
} | ||
}.map { deferred -> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can use .awaitAll()
instead, just for simplification
args: IArgs | ||
): JUnitTestResult = GcTestMatrix.refresh( | ||
// Android uses server side sharding, it results only one matrix, so we can simply get first. | ||
matrices.map.values.first().matrixId, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
iOS doesn't use server side sharding right now. Is it possible to use the API for both Android & iOS?
Edit: I see that processXmlFromApi
is only used for Android right now. That makes sense.
test_runner/src/test/kotlin/TmpV2.kt
Outdated
import java.io.File | ||
import kotlin.system.exitProcess | ||
|
||
object TmpV2 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we should probably delete TmpV2
and turn this into a proper JUnit test. What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also probably delete the original tmp
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Of Course I will do that. This is only draft of implementation that I tested manually and looks correct. I now need to update unit tests, cleanup a little, and It will be ready for review.
package ftl.reports.api | ||
|
||
import com.google.api.services.toolresults.model.Duration | ||
import java.util.* |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we have a lint rule about no wildcard imports.
test_runner/src/test/kotlin/TmpV2.kt
Outdated
println(" - OK") | ||
|
||
print("writing api results to file") | ||
File(JUNIT_REPORT_FILE).writeText(jUnitTestResult.xmlToString()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To test this, run an Android app with multiple tests using server side sharding that has flaky tests. Use num-flaky-test-attempts
set to a non-zero value such as 2. Then compare the JUnit XML file that FTL is creating vs the one we create via the API.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was testing exactly that case. I know there is a difference between old and new JUnitReport.xml.
Old:
<?xml version='1.0' encoding='UTF-8' ?>
<testsuites>
<testsuite name="NexusLowRes-28-en-portrait#" tests="3" failures="0" errors="0" skipped="0" time="3.103" timestamp="2020-03-10T17:12:24" hostname="localhost">
<testcase name="test0" classname="com.example.test_app.InstrumentedTest" time="1.161">
<webLink></webLink>
</testcase>
<testcase name="test1" classname="com.example.test_app.InstrumentedTest" time="1.034">
<webLink></webLink>
</testcase>
<testcase name="test2" classname="com.example.test_app.InstrumentedTest" time="0.908">
<webLink></webLink>
</testcase>
</testsuite>
</testsuites>
New:
<?xml version='1.0' encoding='UTF-8' ?>
<testsuites>
<testsuite name="NexusLowRes-28-en-portrait" tests="1" failures="0" errors="0" skipped="0" time="1.161" timestamp="2020-03-10T17:07:54.422Z">
<testcase name="test0" classname="com.example.test_app.InstrumentedTest" time="1.161"/>
</testsuite>
<testsuite name="NexusLowRes-28-en-portrait" tests="2" failures="0" errors="0" skipped="0" time="1.942" timestamp="2020-03-10T17:07:54.422Z">
<testcase name="test1" classname="com.example.test_app.InstrumentedTest" time="1.034"/>
<testcase name="test2" classname="com.example.test_app.InstrumentedTest" time="0.908"/>
</testsuite>
</testsuites>
-
In new JUnitReport suites reflects shards. I can fix it easily, but that was first what I achieved. I guess that maybe is better to expose some info about shards in JUnitReport. If you don't like that I will fix it.
-
The
timestamp
is different yes. Old timestamp is simply timestamp of first of downloaded reports. New one is not correct I will fix it. -
I need to also fix weblinks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
makes sense!
When using Android Test Orchestrator, we'll want to verify the API test times match the times returned in the FTL XML file. |
7d905dc
to
15d9f39
Compare
Codecov Report
@@ Coverage Diff @@
## master #666 +/- ##
============================================
- Coverage 77.10% 76.10% -1.00%
- Complexity 598 615 +17
============================================
Files 99 109 +10
Lines 2367 2528 +161
Branches 337 359 +22
============================================
+ Hits 1825 1924 +99
- Misses 335 381 +46
- Partials 207 223 +16 |
80ecae9
to
25e5867
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great job! That is one big PR with lot of work. I am really impressed.
The only thing I can complain is multiple usage of runBlocking
. Personally I don't like when this is used as kind of bridge between synchronous and asynchronous code (when the first one is invoked inside a coroutine). But TBH I don't know if we have better solution right now.
Again, great job!
return listOf(map["Model"], map["Version"], map["Locale"], map["Orientation"]).joinToString("-") | ||
} | ||
|
||
private fun List<JUnitTestCase>.sumTime() = this |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this
is redundant here but I guess it was left intentionally for sake of readability?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's right, intentionally for readability. I usually use this
for extension functions with expression body. Thats allow clear chain.
.getTestExecutions() | ||
.createJUnitTestResult() | ||
|
||
internal fun refreshTestMatrices( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can make it private
}.awaitAll() | ||
} | ||
|
||
internal fun List<TestMatrix>.getTestExecutions(): List<TestExecution> = this |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can make it private
skipped = if (testCase.status == "skipped") null else "absent" | ||
).apply { | ||
if (errors != null || failures != null) { | ||
webLink = getWebLink(toolResultsStep, testCase.testCaseId) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
|
||
private fun TestExecution.createTestExecutionData(): TestExecutionData { | ||
val response: ListTestCasesResponse = GcToolResults.listTestCases(toolResultsStep) | ||
val step: Step = GcToolResults.getStepResult(toolResultsStep) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Both GcToolResults.listTestCases
and GcToolResults.getStepResult
are making api calls and seems that they don't depend on each other. Do you think we could make async call for each?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, I will fix that.
|
||
fun Long.formatUtcDate() = utcDateFormat.format(this)!! | ||
|
||
var TestCase.flaky: Boolean by mutableMapProperty { false } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
@@ -0,0 +1,34 @@ | |||
package ftl.reports.api |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We already have Utils.kt
file, can we rename this one to sth like ApiUtils
or somehow different? Just for sake of clarity.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like you don't care about packages :). I guess adding part of package as prefix for file is redundant.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, true. Sometimes I am kind of bitchy :D ¯_(ツ)_/¯
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Easy. You are doing your job. It is always better to express your opinion than to do nothing. There are no ultimate ways, everything we do is based on consensus.
@@ -15,7 +15,7 @@ private val xmlMapper = XmlMapper(xmlModule) | |||
.registerModules(KotlinModule()) | |||
.configure(FAIL_ON_UNKNOWN_PROPERTIES, false) | |||
|
|||
private val xmlPrettyWriter = xmlMapper.writerWithDefaultPrettyPrinter() | |||
internal val xmlPrettyWriter = xmlMapper.writerWithDefaultPrettyPrinter() | |||
|
|||
private fun xmlBytes(path: Path): ByteArray { | |||
if (!path.toFile().exists()) RuntimeException("$path doesn't exist!") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am totally aware it is not part of your PR (so feel free to skip this comment). I think there is throw
missing
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good checkup. I can fix it in this PR, no problem.
@@ -29,11 +32,11 @@ data class JUnitTestSuite( | |||
val timestamp: String?, // String. Android only | |||
|
|||
@JacksonXmlProperty(isAttribute = true) | |||
val hostname: String, // String. | |||
val hostname: String? = null, // String. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just for my understanding, are we ok with possible null
here? Previous implementation did not predicted such value. Thanks!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have asked @bootstraponline about that, he said #545 (comment). So I decide to change it to nullable here, because I didn't find hostname in API
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got it, thanks for explanation
Paths.get(args.localResultDir, FtlConstants.configFileName(args)) | ||
} else { | ||
val configFilePath = if (args.useLocalResultDir()) | ||
Paths.get(args.localResultDir, FtlConstants.configFileName(args)) else |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TBH I don't like it when I need to read whole line to find out there is else at the end. But it is matter of personal preferences :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TBH I don't like if else
and prefer using when
but our detect
config forces to use if else
in cases like this. Anyway I like that kind of formatting cause of clear indentation same for two similar parts.
3d5e8bf
* Fix coroutines errors after rebase
e7bd0b0
to
169c7b5
Compare
Fixes #618, Fixes #545
Checklist