Skip to content

Commit 3fa5b8b

Browse files
authored
Reduce memory usage by using Reader and Writer instead of ByteArrays (#767)
* Reduce memory usage by using Reader and Writer instead of ByteArrays where possible.
1 parent 39282ec commit 3fa5b8b

13 files changed

Lines changed: 60 additions & 45 deletions

File tree

release_notes.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
## next (unreleased)
2+
- [#757](https://github.com/Flank/flank/pull/767) Reduce memory usage by using Reader and Writer instead of ByteArrays. ([jan-gogo](https://github.com/jan-gogo))
23
- [#763](https://github.com/Flank/flank/pull/763) Use "localhost" as default for hostname to fix backward compatibility. ([jan-gogo](https://github.com/jan-gogo))
34
- [#757](https://github.com/Flank/flank/pull/757) Print version and revision before each command. ([jan-gogo](https://github.com/jan-gogo))
45
- [#759](https://github.com/Flank/flank/pull/759) Add shard name for uploaded xctestrun files. ([pawelpasterz](https://github.com/pawelpasterz))

test_runner/src/main/kotlin/ftl/args/AndroidArgs.kt

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package ftl.args
22

3+
import com.google.common.annotations.VisibleForTesting
34
import ftl.android.AndroidCatalog
45
import ftl.android.IncompatibleModelVersion
56
import ftl.android.SupportedDeviceConfig
@@ -29,6 +30,7 @@ import ftl.config.Device
2930
import ftl.config.FtlConstants
3031
import ftl.config.parseRoboDirectives
3132
import ftl.util.FlankFatalError
33+
import java.io.Reader
3234
import java.nio.file.Files
3335
import java.nio.file.Path
3436

@@ -187,11 +189,11 @@ AndroidArgs
187189
mergeYmlMaps(GcloudYml, AndroidGcloudYml, FlankYml, AndroidFlankYml)
188190
}
189191

190-
fun load(data: Path, cli: AndroidRunCommand? = null): AndroidArgs =
191-
load(String(Files.readAllBytes(data)), cli)
192+
fun load(yamlPath: Path, cli: AndroidRunCommand? = null): AndroidArgs = load(Files.newBufferedReader(yamlPath), cli)
192193

193-
fun load(yamlData: String, cli: AndroidRunCommand? = null): AndroidArgs {
194-
val data = YamlDeprecated.modifyAndThrow(yamlData, android = true)
194+
@VisibleForTesting
195+
internal fun load(yamlReader: Reader, cli: AndroidRunCommand? = null): AndroidArgs {
196+
val data = YamlDeprecated.modifyAndThrow(yamlReader, android = true)
195197

196198
val flankYml = yamlMapper.readValue(data, FlankYml::class.java)
197199
val gcloudYml = yamlMapper.readValue(data, GcloudYml::class.java)
@@ -215,7 +217,8 @@ AndroidArgs
215217
FlankYml(),
216218
AndroidFlankYml(),
217219
"",
218-
AndroidRunCommand())
220+
AndroidRunCommand()
221+
)
219222
}
220223
}
221224
}

test_runner/src/main/kotlin/ftl/args/IosArgs.kt

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package ftl.args
22

3+
import com.google.common.annotations.VisibleForTesting
34
import ftl.args.ArgsHelper.assertCommonProps
45
import ftl.args.ArgsHelper.assertFileExists
56
import ftl.args.ArgsHelper.assertGcsFileExists
@@ -24,6 +25,7 @@ import ftl.ios.IosCatalog
2425
import ftl.ios.Xctestrun
2526
import ftl.util.FlankTestMethod
2627
import ftl.util.FlankFatalError
28+
import java.io.Reader
2729
import java.nio.file.Files
2830
import java.nio.file.Path
2931

@@ -156,10 +158,11 @@ IosArgs
156158
mergeYmlMaps(GcloudYml, IosGcloudYml, FlankYml, IosFlankYml)
157159
}
158160

159-
fun load(data: Path, cli: IosRunCommand? = null): IosArgs = load(String(Files.readAllBytes(data)), cli)
161+
fun load(yamlPath: Path, cli: IosRunCommand? = null): IosArgs = load(Files.newBufferedReader(yamlPath), cli)
160162

161-
fun load(yamlData: String, cli: IosRunCommand? = null): IosArgs {
162-
val data = YamlDeprecated.modifyAndThrow(yamlData, android = false)
163+
@VisibleForTesting
164+
internal fun load(yamlReader: Reader, cli: IosRunCommand? = null): IosArgs {
165+
val data = YamlDeprecated.modifyAndThrow(yamlReader, android = false)
163166

164167
val flankYml = yamlMapper.readValue(data, FlankYml::class.java)
165168
val iosFlankYml = yamlMapper.readValue(data, IosFlankYml::class.java)
@@ -183,7 +186,8 @@ IosArgs
183186
FlankYml(),
184187
IosFlankYml(),
185188
"",
186-
IosRunCommand())
189+
IosRunCommand()
190+
)
187191
}
188192
}
189193
}

test_runner/src/main/kotlin/ftl/args/yml/YamlDeprecated.kt

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@ import com.fasterxml.jackson.databind.JsonNode
44
import com.fasterxml.jackson.databind.node.JsonNodeFactory
55
import com.fasterxml.jackson.databind.node.MissingNode
66
import com.fasterxml.jackson.databind.node.ObjectNode
7+
import com.google.common.annotations.VisibleForTesting
78
import ftl.args.ArgsHelper.yamlMapper
89
import ftl.util.FlankFatalError
10+
import java.io.Reader
911
import java.nio.file.Files
1012
import java.nio.file.Path
1113

@@ -123,8 +125,7 @@ object YamlDeprecated {
123125
fun modify(yamlPath: Path): Boolean {
124126
if (yamlPath.toFile().exists().not()) throw FlankFatalError("Flank yml doesn't exist at path $yamlPath")
125127

126-
val data = String(Files.readAllBytes(yamlPath))
127-
val (errorDetected, string) = modify(data)
128+
val (errorDetected, string) = modify(Files.newBufferedReader(yamlPath))
128129

129130
Files.write(yamlPath, string.toByteArray())
130131
println("\nUpdated ${yamlPath.fileName} file")
@@ -133,8 +134,8 @@ object YamlDeprecated {
133134
}
134135

135136
// Throw exception when Level.Error modified key is found.
136-
fun modifyAndThrow(yamlData: String, android: Boolean): String {
137-
val (error, data) = YamlDeprecated.modify(yamlData)
137+
fun modifyAndThrow(yamlReader: Reader, android: Boolean): String {
138+
val (error, data) = modify(yamlReader)
138139

139140
if (error) {
140141
val platform = if (android) "android" else "ios"
@@ -144,7 +145,8 @@ object YamlDeprecated {
144145
return data
145146
}
146147

147-
fun modify(yamlData: String): Pair<Boolean, String> {
148+
@VisibleForTesting
149+
internal fun modify(yamlData: Reader): Pair<Boolean, String> {
148150
val mappedYaml = yamlMapper.readTree(yamlData)
149151

150152
val parsed = if (mappedYaml == null || mappedYaml is MissingNode) {

test_runner/src/main/kotlin/ftl/doctor/Doctor.kt

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,20 @@
11
package ftl.doctor
22

3+
import com.google.common.annotations.VisibleForTesting
34
import ftl.args.ArgsHelper
45
import ftl.args.IArgsCompanion
6+
import java.io.Reader
57
import java.nio.file.Files
68
import java.nio.file.Path
79

810
object Doctor {
911
fun validateYaml(args: IArgsCompanion, data: Path): String {
1012
if (!data.toFile().exists()) return "Skipping yaml validation. No file at path $data"
11-
return validateYaml(args, String(Files.readAllBytes(data)))
13+
return validateYaml(args, Files.newBufferedReader(data))
1214
}
1315

14-
fun validateYaml(args: IArgsCompanion, data: String): String {
16+
@VisibleForTesting
17+
internal fun validateYaml(args: IArgsCompanion, data: Reader): String {
1518
var result = ""
1619
val parsed = ArgsHelper.yamlMapper.readTree(data)
1720

test_runner/src/main/kotlin/ftl/mock/MockServer.kt

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,8 @@ object MockServer {
5757

5858
private inline fun <reified T> loadCatalog(fileName: String): T {
5959
val jsonPath = Paths.get("./src/test/kotlin/ftl/fixtures/$fileName")
60-
if (!jsonPath.toFile().exists()) throw RuntimeException("Path doesn't exist: $fileName")
61-
val jsonString = String(Files.readAllBytes(jsonPath))
62-
return JSON_FACTORY.fromString(jsonString, T::class.java)
60+
if (!Files.exists(jsonPath)) throw RuntimeException("Path doesn't exist: $fileName")
61+
return JSON_FACTORY.fromReader(Files.newBufferedReader(jsonPath), T::class.java)
6362
}
6463

6564
private val androidCatalog by lazy { loadCatalog<AndroidDeviceCatalog>("android_catalog.json") }

test_runner/src/main/kotlin/ftl/reports/xml/JUnitXml.kt

Lines changed: 10 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PRO
77
import ftl.reports.xml.model.JUnitTestResult
88
import ftl.reports.xml.model.JUnitTestSuite
99
import java.io.File
10-
import java.nio.file.Files
1110
import java.nio.file.Path
1211

1312
private val xmlModule = JacksonXmlModule().apply { setDefaultUseWrapper(false) }
@@ -17,47 +16,36 @@ private val xmlMapper = XmlMapper(xmlModule)
1716

1817
internal val xmlPrettyWriter = xmlMapper.writerWithDefaultPrettyPrinter()
1918

20-
private fun xmlBytes(path: Path): ByteArray {
21-
if (!path.toFile().exists()) throw RuntimeException("$path doesn't exist!")
22-
return Files.readAllBytes(path)
23-
}
24-
2519
fun JUnitTestResult?.xmlToString(): String {
2620
if (this == null) return ""
2721
val prefix = "<?xml version='1.0' encoding='UTF-8' ?>\n"
2822
return prefix + xmlPrettyWriter.writeValueAsString(this)
2923
}
3024

31-
fun parseOneSuiteXml(bytes: ByteArray): JUnitTestResult {
32-
return JUnitTestResult(mutableListOf(xmlMapper.readValue(bytes, JUnitTestSuite::class.java)))
33-
}
34-
3525
fun parseOneSuiteXml(path: Path): JUnitTestResult {
36-
return parseOneSuiteXml(xmlBytes(path))
26+
return parseOneSuiteXml(path.toFile())
3727
}
3828

39-
fun parseOneSuiteXml(path: File): JUnitTestResult {
40-
return parseOneSuiteXml(xmlBytes(path.toPath()))
29+
fun parseOneSuiteXml(file: File): JUnitTestResult {
30+
if (!file.exists()) throw RuntimeException("$file doesn't exist!")
31+
return JUnitTestResult(mutableListOf(xmlMapper.readValue(file, JUnitTestSuite::class.java)))
4132
}
4233

4334
fun parseOneSuiteXml(data: String): JUnitTestResult {
44-
return parseOneSuiteXml(data.toByteArray())
35+
return JUnitTestResult(mutableListOf(xmlMapper.readValue(data, JUnitTestSuite::class.java)))
4536
}
4637

4738
// --
4839

49-
fun parseAllSuitesXml(bytes: ByteArray): JUnitTestResult {
50-
return xmlMapper.readValue(bytes, JUnitTestResult::class.java)
51-
}
52-
5340
fun parseAllSuitesXml(path: Path): JUnitTestResult {
54-
return parseAllSuitesXml(xmlBytes(path))
41+
return parseAllSuitesXml(path.toFile())
5542
}
5643

57-
fun parseAllSuitesXml(path: File): JUnitTestResult {
58-
return parseAllSuitesXml(path.toPath())
44+
fun parseAllSuitesXml(file: File): JUnitTestResult {
45+
if (!file.exists()) throw RuntimeException("$file doesn't exist!")
46+
return xmlMapper.readValue(file, JUnitTestResult::class.java)
5947
}
6048

6149
fun parseAllSuitesXml(data: String): JUnitTestResult {
62-
return parseAllSuitesXml(data.toByteArray())
50+
return xmlMapper.readValue(data, JUnitTestResult::class.java)
6351
}

test_runner/src/main/kotlin/ftl/util/Utils.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,8 +120,10 @@ fun copyBinaryResource(name: String) {
120120
destinationPath.parent.toFile().mkdirs()
121121

122122
// "binaries/" folder prefix is required for Linux to find the resource.
123-
val bytes = getResource("binaries/$name").use { it.readBytes() }
124-
Files.write(destinationPath, bytes)
123+
Files.copy(
124+
getResource("binaries/$name"),
125+
destinationPath
126+
)
125127
destinationFile.setExecutable(true)
126128
}
127129

test_runner/src/test/kotlin/Debug.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ fun main() {
1010
// run "gradle check" to generate required fixtures
1111
val projectId = System.getenv("GOOGLE_CLOUD_PROJECT")
1212
?: "YOUR PROJECT ID"
13-
val quantity = "single"
14-
val type = "robo"
13+
val quantity = "multiple"
14+
val type = "error"
1515

1616
// Bugsnag keeps the process alive so we must call exitProcess
1717
// https://github.com/bugsnag/bugsnag-java/issues/151

test_runner/src/test/kotlin/ftl/args/AndroidArgsTest.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import org.junit.Test
2626
import org.junit.rules.ExpectedException
2727
import org.junit.runner.RunWith
2828
import picocli.CommandLine
29+
import java.io.StringReader
2930

3031
@Suppress("TooManyFunctions")
3132
@RunWith(FlankTestRunner::class)
@@ -1242,3 +1243,5 @@ AndroidArgs
12421243
runBlocking { runAndroidTests(parsedYml) }
12431244
}
12441245
}
1246+
1247+
private fun AndroidArgs.Companion.load(yamlData: String, cli: AndroidRunCommand? = null): AndroidArgs = load(StringReader(yamlData), cli)

0 commit comments

Comments
 (0)