[go: nahoru, domu]

A Gradle Plugin to build and generate benchmarking results for KMP iOS benchmarks.

* Generates Skia Dashboard compatible results.
* Automatically generates the XCode project, and runs benchmarks.

A KMP project needs to do something like:

```groovy
plugins {
  id("androidx.benchmark.darwin")
}
```

and then it can use the `darwinBenchmark` block like so:

```groovy
darwinBenchmark {
    // XCodegen Schema YAML
    xcodeGenConfigFile = project.rootProject.file(
            "benchmark/benchmark-darwin-samples-xcode/xcodegen-project.yml"
    )
    // XCode project name
    xcodeProjectName = "benchmark-darwin-samples-xcode"
    // iOS app scheme
    scheme = "testapp-ios"
    // ios 13, 15.2
    destination = "id=7F61C467-4E4A-437C-B6EF-026FEEF3904C"
    // The XCFrameworkConfig name
    xcFrameworkConfig = "AndroidXDarwinSampleBenchmarks"
}
```

Test: ./gradlew --no-configuration-cache :benchmark:benchmark-darwin-samples:darwinBenchmarkResults

Example metric:

```json
{
  "key": {
    "testDescription": "Allocate an ArrayList of size 1000",
    "metricName": "Memory Peak Physical",
    "metricIdentifier": "com.apple.dt.XCTMetric_Memory.physical_peak",
    "polarity": "prefers smaller",
    "units": "kB"
  },
  "measurements": {
    "stat": [
      {
        "value": "min",
        "measurement": 0.0
      },
      {
        "value": "median",
        "measurement": 0.0
      },
      {
        "value": "max",
        "measurement": 0.0
      },
      {
        "value": "stddev",
        "measurement": 0.0
      }
    ]
  }
}
```

Bug: b/253517578
Change-Id: Id7c9cbf0e33b76566caa3d5210ad06f43109d435
diff --git a/benchmark/benchmark-darwin-gradle-plugin/README.md b/benchmark/benchmark-darwin-gradle-plugin/README.md
new file mode 100644
index 0000000..3fce72d
--- /dev/null
+++ b/benchmark/benchmark-darwin-gradle-plugin/README.md
@@ -0,0 +1,70 @@
+# Introduction
+
+A Gradle Plugin to build and generate benchmarking results for KMP iOS benchmarks.
+
+* Generates Skia Dashboard compatible results.
+* Automatically generates the XCode project, and runs benchmarks on a target device running iOS
+  or macOS (simulator or physical devices).
+
+# Usage
+
+A KMP project needs to do something like:
+
+```groovy
+plugins {
+    id("androidx.benchmark.darwin")
+}
+```
+
+and then it can use the `darwinBenchmark` block like so:
+
+```groovy
+darwinBenchmark {
+    // XCodegen Schema YAML
+    xcodeGenConfigFile = project.rootProject.file(
+            "benchmark/benchmark-darwin-samples-xcode/xcodegen-project.yml"
+    )
+    // XCode project name
+    xcodeProjectName = "benchmark-darwin-samples-xcode"
+    // iOS app scheme
+    scheme = "testapp-ios"
+    // ios 13, 15.2
+    destination = "id=7F61C467-4E4A-437C-B6EF-026FEEF3904C"
+    // The XCFrameworkConfig name
+    xcFrameworkConfig = "AndroidXDarwinSampleBenchmarks"
+}
+```
+
+Example metrics look like:
+
+```json
+{
+  "key": {
+    "testDescription": "Allocate an ArrayList of size 1000",
+    "metricName": "Memory Peak Physical",
+    "metricIdentifier": "com.apple.dt.XCTMetric_Memory.physical_peak",
+    "polarity": "prefers smaller",
+    "units": "kB"
+  },
+  "measurements": {
+    "stat": [
+      {
+        "value": "min",
+        "measurement": 0.0
+      },
+      {
+        "value": "median",
+        "measurement": 0.0
+      },
+      {
+        "value": "max",
+        "measurement": 0.0
+      },
+      {
+        "value": "stddev",
+        "measurement": 0.0
+      }
+    ]
+  }
+}
+```
diff --git a/benchmark/benchmark-darwin-gradle-plugin/build.gradle b/benchmark/benchmark-darwin-gradle-plugin/build.gradle
new file mode 100644
index 0000000..4282513
--- /dev/null
+++ b/benchmark/benchmark-darwin-gradle-plugin/build.gradle
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+import androidx.build.LibraryType
+import androidx.build.SdkResourceGenerator
+
+plugins {
+    id("AndroidXPlugin")
+    id("kotlin")
+    id("java-gradle-plugin")
+}
+
+dependencies {
+    api(gradleApi())
+    implementation(libs.gson)
+    implementation(libs.apacheCommonsMath)
+    testImplementation(gradleTestKit())
+    testImplementation(libs.junit)
+    testImplementation(libs.truth)
+}
+
+SdkResourceGenerator.generateForHostTest(project)
+
+gradlePlugin {
+    plugins {
+        darwinBenchmark {
+            id = "androidx.benchmark.darwin"
+            implementationClass = "androidx.benchmark.darwin.gradle.DarwinBenchmarkPlugin"
+        }
+    }
+}
+
+androidx {
+    name = "AndroidX Benchmarks - Darwin Gradle Plugin"
+    type = LibraryType.GRADLE_PLUGIN
+    mavenGroup = LibraryGroups.BENCHMARK
+    inceptionYear = "2022"
+    description = "AndroidX Benchmarks - Darwin Gradle Plugin"
+}
+
+validatePlugins {
+    enableStricterValidation = true
+}
diff --git a/benchmark/benchmark-darwin-gradle-plugin/src/main/kotlin/androidx/benchmark/darwin/gradle/DarwinBenchmarkPlugin.kt b/benchmark/benchmark-darwin-gradle-plugin/src/main/kotlin/androidx/benchmark/darwin/gradle/DarwinBenchmarkPlugin.kt
new file mode 100644
index 0000000..5974f14
--- /dev/null
+++ b/benchmark/benchmark-darwin-gradle-plugin/src/main/kotlin/androidx/benchmark/darwin/gradle/DarwinBenchmarkPlugin.kt
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2022 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 androidx.benchmark.darwin.gradle
+
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+
+/**
+ * The Darwin benchmark plugin that helps run KMP benchmarks on iOS devices, and extracts benchmark
+ * results in `json`.
+ */
+class DarwinBenchmarkPlugin : Plugin<Project> {
+    override fun apply(project: Project) {
+        val extension =
+            project.extensions.create("darwinBenchmark", DarwinBenchmarkPluginExtension::class.java)
+
+        val xcodeProjectPath = extension.xcodeProjectName.flatMap { name ->
+            project.layout.buildDirectory.dir("$name.xcodeproj")
+        }
+
+        val xcResultPath = extension.xcodeProjectName.flatMap { name ->
+            project.layout.buildDirectory.dir("$name.xcresult")
+        }
+
+        val generateXCodeProjectTask = project.tasks.register(
+            GENERATE_XCODE_PROJECT_TASK, GenerateXCodeProjectTask::class.java
+        ) {
+            it.yamlFile.set(extension.xcodeGenConfigFile)
+            it.projectName.set(extension.xcodeProjectName)
+            it.xcProjectPath.set(xcodeProjectPath)
+            it.infoPlistPath.set(project.layout.buildDirectory.file("Info.plist"))
+        }
+
+        val runDarwinBenchmarks = project.tasks.register(
+            RUN_DARWIN_BENCHMARKS_TASK, RunDarwinBenchmarksTask::class.java
+        ) {
+            it.xcodeProjectPath.set(generateXCodeProjectTask.flatMap { task ->
+                task.xcProjectPath
+            })
+            it.destination.set(extension.destination)
+            it.scheme.set(extension.scheme)
+            it.xcResultPath.set(xcResultPath)
+            it.dependsOn("assemble${extension.xcFrameworkConfig.get()}ReleaseXCFramework")
+        }
+
+        project.tasks.register(
+            DARWIN_BENCHMARK_RESULTS_TASK, DarwinBenchmarkResultsTask::class.java
+        ) {
+            it.xcResultPath.set(runDarwinBenchmarks.flatMap { task ->
+                task.xcResultPath
+            })
+            it.outputFile.set(
+                project.layout.buildDirectory.file(
+                    "${extension.xcodeProjectName.get()}-benchmark-result.json"
+                )
+            )
+        }
+    }
+
+    private companion object {
+        const val GENERATE_XCODE_PROJECT_TASK = "generateXCodeProject"
+        const val RUN_DARWIN_BENCHMARKS_TASK = "runDarwinBenchmarks"
+        const val DARWIN_BENCHMARK_RESULTS_TASK = "darwinBenchmarkResults"
+    }
+}
diff --git a/benchmark/benchmark-darwin-gradle-plugin/src/main/kotlin/androidx/benchmark/darwin/gradle/DarwinBenchmarkPluginExtension.kt b/benchmark/benchmark-darwin-gradle-plugin/src/main/kotlin/androidx/benchmark/darwin/gradle/DarwinBenchmarkPluginExtension.kt
new file mode 100644
index 0000000..83781e8
--- /dev/null
+++ b/benchmark/benchmark-darwin-gradle-plugin/src/main/kotlin/androidx/benchmark/darwin/gradle/DarwinBenchmarkPluginExtension.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2022 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 androidx.benchmark.darwin.gradle
+
+import org.gradle.api.file.RegularFileProperty
+import org.gradle.api.provider.Property
+
+/**
+ * The [DarwinBenchmarkPlugin] extension.
+ */
+abstract class DarwinBenchmarkPluginExtension {
+    /**
+     * The path to the YAML file that can be used to generate the XCode project.
+     */
+    abstract val xcodeGenConfigFile: RegularFileProperty
+
+    /**
+     * The project name as defined in the YAML file.
+     */
+    abstract val xcodeProjectName: Property<String>
+
+    /**
+     * The project scheme as defined in the YAML file that is the unit test target.
+     */
+    abstract val scheme: Property<String>
+
+    /**
+     * The benchmark device `id`.
+     * This is typically discovered by using `xcrun xctrace list devices`.
+     */
+    abstract val destination: Property<String>
+
+    /**
+     * The name of the `XCFrameworkConfig` defined in the benchmark's module build.gradle.
+     * This is used to derive the name of the `release` task to generate the `XCFramework`.
+     */
+    abstract val xcFrameworkConfig: Property<String>
+}
diff --git a/benchmark/benchmark-darwin-gradle-plugin/src/main/kotlin/androidx/benchmark/darwin/gradle/DarwinBenchmarkResultsTask.kt b/benchmark/benchmark-darwin-gradle-plugin/src/main/kotlin/androidx/benchmark/darwin/gradle/DarwinBenchmarkResultsTask.kt
new file mode 100644
index 0000000..0ecdb27
--- /dev/null
+++ b/benchmark/benchmark-darwin-gradle-plugin/src/main/kotlin/androidx/benchmark/darwin/gradle/DarwinBenchmarkResultsTask.kt
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2022 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 androidx.benchmark.darwin.gradle
+
+import androidx.benchmark.darwin.gradle.skia.Metrics
+import androidx.benchmark.darwin.gradle.xcode.Models
+import androidx.benchmark.darwin.gradle.xcode.XcResultParser
+import java.io.ByteArrayInputStream
+import java.io.ByteArrayOutputStream
+import javax.inject.Inject
+import org.gradle.api.DefaultTask
+import org.gradle.api.file.DirectoryProperty
+import org.gradle.api.file.RegularFileProperty
+import org.gradle.api.tasks.CacheableTask
+import org.gradle.api.tasks.InputDirectory
+import org.gradle.api.tasks.OutputFile
+import org.gradle.api.tasks.PathSensitive
+import org.gradle.api.tasks.PathSensitivity
+import org.gradle.api.tasks.TaskAction
+import org.gradle.process.ExecOperations
+
+@CacheableTask
+abstract class DarwinBenchmarkResultsTask @Inject constructor(
+    private val execOperations: ExecOperations
+) : DefaultTask() {
+    @get:InputDirectory
+    @get:PathSensitive(PathSensitivity.RELATIVE)
+    abstract val xcResultPath: DirectoryProperty
+
+    @get:OutputFile
+    abstract val outputFile: RegularFileProperty
+
+    @TaskAction
+    fun benchmarkResults() {
+        val xcResultFile = xcResultPath.get().asFile
+        val parser = XcResultParser(xcResultFile) { args ->
+            val output = ByteArrayOutputStream()
+            val result = execOperations.exec { spec ->
+                spec.commandLine = args
+                spec.standardOutput = output
+            }
+            result.assertNormalExitValue()
+            output.use {
+                val input = ByteArrayInputStream(output.toByteArray())
+                input.use {
+                    input.reader().readText()
+                }
+            }
+        }
+        val (record, summaries) = parser.parseResults()
+        val metrics = Metrics.buildMetrics(record, summaries)
+        val output = Models.gsonBuilder()
+            .setPrettyPrinting()
+            .create()
+            .toJson(metrics)
+
+        outputFile.get().asFile.writeText(output)
+    }
+}
diff --git a/benchmark/benchmark-darwin-gradle-plugin/src/main/kotlin/androidx/benchmark/darwin/gradle/GenerateXCodeProjectTask.kt b/benchmark/benchmark-darwin-gradle-plugin/src/main/kotlin/androidx/benchmark/darwin/gradle/GenerateXCodeProjectTask.kt
new file mode 100644
index 0000000..3dec4f1
--- /dev/null
+++ b/benchmark/benchmark-darwin-gradle-plugin/src/main/kotlin/androidx/benchmark/darwin/gradle/GenerateXCodeProjectTask.kt
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2022 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 androidx.benchmark.darwin.gradle
+
+import java.io.File
+import javax.inject.Inject
+import org.gradle.api.DefaultTask
+import org.gradle.api.file.DirectoryProperty
+import org.gradle.api.file.RegularFileProperty
+import org.gradle.api.provider.Property
+import org.gradle.api.tasks.CacheableTask
+import org.gradle.api.tasks.Input
+import org.gradle.api.tasks.InputFile
+import org.gradle.api.tasks.OutputDirectory
+import org.gradle.api.tasks.OutputFile
+import org.gradle.api.tasks.PathSensitive
+import org.gradle.api.tasks.PathSensitivity
+import org.gradle.api.tasks.TaskAction
+import org.gradle.process.ExecOperations
+
+@CacheableTask
+abstract class GenerateXCodeProjectTask @Inject constructor(
+    private val execOperations: ExecOperations
+) : DefaultTask() {
+
+    @get:InputFile
+    @get:PathSensitive(PathSensitivity.RELATIVE)
+    abstract val yamlFile: RegularFileProperty
+
+    @get:Input
+    abstract val projectName: Property<String>
+
+    @get:OutputFile
+    abstract val infoPlistPath: RegularFileProperty
+
+    @get:OutputDirectory
+    abstract val xcProjectPath: DirectoryProperty
+
+    @TaskAction
+    fun buildXCodeProject() {
+        val outputFile = xcProjectPath.get().asFile
+        if (outputFile.exists()) {
+            require(outputFile.deleteRecursively()) {
+                "Unable to delete xcode project $outputFile"
+            }
+        }
+        val args = listOf(
+            "xcodegen",
+            "--spec",
+            yamlFile.get().asFile.absolutePath,
+            "--project",
+            outputFile.parent
+        )
+        val result = execOperations.exec { spec ->
+            spec.commandLine = args
+        }
+        result.assertNormalExitValue()
+        require(outputFile.exists()) {
+            "Project $projectName must match the `name` declaration in $yamlFile"
+        }
+        copyProjectMetadata()
+    }
+
+    private fun copyProjectMetadata() {
+        val sourceFile = File(yamlFile.get().asFile.parent, "Info.plist")
+        require(sourceFile.exists())
+        val targetFile = infoPlistPath.get().asFile
+        val copied = sourceFile.copyRecursively(targetFile, overwrite = true)
+        require(copied) {
+            "Unable to copy $sourceFile to $targetFile"
+        }
+    }
+}
diff --git a/benchmark/benchmark-darwin-gradle-plugin/src/main/kotlin/androidx/benchmark/darwin/gradle/RunDarwinBenchmarksTask.kt b/benchmark/benchmark-darwin-gradle-plugin/src/main/kotlin/androidx/benchmark/darwin/gradle/RunDarwinBenchmarksTask.kt
new file mode 100644
index 0000000..55aec880
--- /dev/null
+++ b/benchmark/benchmark-darwin-gradle-plugin/src/main/kotlin/androidx/benchmark/darwin/gradle/RunDarwinBenchmarksTask.kt
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2022 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 androidx.benchmark.darwin.gradle
+
+import javax.inject.Inject
+import org.gradle.api.DefaultTask
+import org.gradle.api.file.DirectoryProperty
+import org.gradle.api.provider.Property
+import org.gradle.api.tasks.CacheableTask
+import org.gradle.api.tasks.Input
+import org.gradle.api.tasks.InputDirectory
+import org.gradle.api.tasks.OutputDirectory
+import org.gradle.api.tasks.PathSensitive
+import org.gradle.api.tasks.PathSensitivity
+import org.gradle.api.tasks.TaskAction
+import org.gradle.process.ExecOperations
+
+@CacheableTask
+abstract class RunDarwinBenchmarksTask @Inject constructor(
+    private val execOperations: ExecOperations
+) : DefaultTask() {
+    @get:InputDirectory
+    @get:PathSensitive(PathSensitivity.RELATIVE)
+    abstract val xcodeProjectPath: DirectoryProperty
+
+    @get:Input
+    abstract val destination: Property<String>
+
+    @get:Input
+    abstract val scheme: Property<String>
+
+    @get:OutputDirectory
+    abstract val xcResultPath: DirectoryProperty
+
+    @TaskAction
+    fun runBenchmarks() {
+        requireXcodeBuild()
+        val xcodeProject = xcodeProjectPath.get().asFile
+        val xcResultFile = xcResultPath.get().asFile
+        if (xcResultFile.exists()) {
+            xcResultFile.deleteRecursively()
+        }
+        val args = listOf(
+            "xcodebuild",
+            "test",
+            "-project", xcodeProject.absolutePath.toString(),
+            "-scheme", scheme.get(),
+            "-destination", destination.get(),
+            "-resultBundlePath", xcResultFile.absolutePath,
+        )
+        logger.info("Command : ${args.joinToString(" ")}")
+        val result = execOperations.exec { spec ->
+            spec.commandLine = args
+        }
+        result.assertNormalExitValue()
+    }
+
+    private fun requireXcodeBuild() {
+        val result = execOperations.exec { spec ->
+            spec.commandLine = listOf("which", "xcodebuild")
+            // Ignore exit value here to return a better exception message
+            spec.isIgnoreExitValue = true
+        }
+        require(result.exitValue == 0) {
+            "xcodebuild is missing on this machine."
+        }
+    }
+}
diff --git a/benchmark/benchmark-darwin-gradle-plugin/src/main/kotlin/androidx/benchmark/darwin/gradle/skia/Metric.kt b/benchmark/benchmark-darwin-gradle-plugin/src/main/kotlin/androidx/benchmark/darwin/gradle/skia/Metric.kt
new file mode 100644
index 0000000..6bed2de
--- /dev/null
+++ b/benchmark/benchmark-darwin-gradle-plugin/src/main/kotlin/androidx/benchmark/darwin/gradle/skia/Metric.kt
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2022 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 androidx.benchmark.darwin.gradle.skia
+
+import androidx.benchmark.darwin.gradle.xcode.ActionTestSummary
+import androidx.benchmark.darwin.gradle.xcode.ActionsInvocationRecord
+import com.google.gson.annotations.SerializedName
+import org.apache.commons.math3.stat.descriptive.DescriptiveStatistics
+
+/**
+ * https://skia.googlesource.com/buildbot/+/refs/heads/main/perf/FORMAT.md
+ */
+
+data class Stat(val value: String, val measurement: Double)
+data class Measurements(
+    @SerializedName("stat")
+    val stats: List<Stat>
+)
+
+data class Metric(val key: Map<String, String>, val measurements: Measurements)
+data class Metrics(val key: Map<String, String>, val results: List<Metric>) {
+    companion object {
+        fun buildMetrics(
+            record: ActionsInvocationRecord,
+            summaries: List<ActionTestSummary>
+        ): Metrics {
+            require(record.actions.actionRecords.isNotEmpty())
+            val runDestination = record.actions.actionRecords.first().runDestination
+            val metricsKeys = mapOf(
+                "destination" to runDestination.displayName.value,
+                "arch" to runDestination.targetArchitecture.value,
+                "targetSdk" to runDestination.targetSDKRecord.identifier.value,
+                "identifier" to runDestination.localComputerRecord.identifier.value,
+                "modelName" to runDestination.localComputerRecord.modelName.value,
+                "modelCode" to runDestination.localComputerRecord.modelCode.value
+            )
+            val results = summaries.flatMap { it.toMetrics() }
+            return Metrics(metricsKeys, results)
+        }
+
+        private fun ActionTestSummary.toMetrics(): List<Metric> {
+            return performanceMetrics.values.map { metricSummary ->
+                val key = mutableMapOf(
+                    "testDescription" to (title() ?: "No description"),
+                    "metricName" to metricSummary.displayName.value,
+                    "metricIdentifier" to metricSummary.identifier.value,
+                    "polarity" to metricSummary.polarity.value,
+                    "units" to metricSummary.unitOfMeasurement.value,
+                )
+                val statistics = DescriptiveStatistics()
+                metricSummary.measurements.values.forEach {
+                    statistics.addValue(it.value)
+                }
+                val min = Stat("min", statistics.min)
+                // The 50th percentile is the median
+                val median = Stat("median", statistics.getPercentile(50.0))
+                val max = Stat("max", statistics.max)
+                val deviation = Stat("stddev", statistics.standardDeviation)
+                Metric(key, Measurements(listOf(min, median, max, deviation)))
+            }
+        }
+    }
+}
diff --git a/benchmark/benchmark-darwin-gradle-plugin/src/main/kotlin/androidx/benchmark/darwin/gradle/xcode/Models.kt b/benchmark/benchmark-darwin-gradle-plugin/src/main/kotlin/androidx/benchmark/darwin/gradle/xcode/Models.kt
new file mode 100644
index 0000000..863da27
--- /dev/null
+++ b/benchmark/benchmark-darwin-gradle-plugin/src/main/kotlin/androidx/benchmark/darwin/gradle/xcode/Models.kt
@@ -0,0 +1,341 @@
+/*
+ * Copyright 2022 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 androidx.benchmark.darwin.gradle.xcode
+
+import com.google.gson.Gson
+import com.google.gson.GsonBuilder
+import com.google.gson.JsonDeserializationContext
+import com.google.gson.JsonDeserializer
+import com.google.gson.JsonElement
+import com.google.gson.annotations.SerializedName
+import java.lang.reflect.Type
+
+// Rather unfortunate that all values types are wrapped in a property bag containing a single
+// key called "_value". This is as per the JSON schema used by `xcresulttool`.
+
+data class StringTypedValue(
+    @SerializedName("_value") val value: String
+)
+
+data class IntTypedValue(
+    @SerializedName("_value") val value: Int
+)
+
+data class DoubleTypedValue(
+    @SerializedName("_value")
+    val value: Double
+)
+
+data class BooleanTypedValue(
+    @SerializedName("_value")
+    val value: Boolean
+)
+
+data class Metrics(
+    private val testsCount: IntTypedValue
+) {
+    fun size(): Int {
+        return testsCount.value
+    }
+}
+
+data class TestReference(
+    val id: StringTypedValue,
+)
+
+data class ActionResult(
+    private val status: StringTypedValue,
+    @SerializedName("testsRef") private val testsReference: TestReference
+) {
+    fun isSuccessful(): Boolean {
+        return status.value == "succeeded"
+    }
+
+    fun testsReferenceId(): String {
+        return testsReference.id.value
+    }
+}
+
+data class ActionPlatformRecord(
+    val identifier: StringTypedValue,
+    val userDescription: StringTypedValue,
+)
+
+data class ActionSDKRecord(
+    val name: StringTypedValue,
+    val identifier: StringTypedValue,
+    val operatingSystemVersion: StringTypedValue,
+    val isInternal: BooleanTypedValue,
+)
+
+data class ActionDeviceRecord(
+    val name: StringTypedValue,
+    val isConcreteDevice: BooleanTypedValue,
+    val operatingSystemVersion: StringTypedValue,
+    val operatingSystemVersionWithBuildNumber: StringTypedValue,
+    val nativeArchitecture: StringTypedValue,
+    val modelName: StringTypedValue,
+    val modelCode: StringTypedValue,
+    val identifier: StringTypedValue,
+    val isWireless: BooleanTypedValue,
+    val cpuKind: StringTypedValue,
+    val cpuCount: IntTypedValue?,
+    val cpuSpeedInMHz: IntTypedValue?,
+    val busSpeedInMHz: IntTypedValue?,
+    val ramSizeInMegabytes: IntTypedValue?,
+    val physicalCPUCoresPerPackage: IntTypedValue?,
+    val logicalCPUCoresPerPackage: IntTypedValue?,
+    val platformRecord: ActionPlatformRecord,
+)
+
+data class ActionRunDestinationRecord(
+    val displayName: StringTypedValue,
+    val targetArchitecture: StringTypedValue,
+    val targetDeviceRecord: ActionDeviceRecord,
+    val localComputerRecord: ActionDeviceRecord,
+    val targetSDKRecord: ActionSDKRecord,
+)
+
+data class ActionRecord(
+    val actionResult: ActionResult,
+    val runDestination: ActionRunDestinationRecord
+)
+
+data class Actions(
+    @SerializedName("_values") val actionRecords: List<ActionRecord>
+) {
+    fun testReferences(): List<String> {
+        return actionRecords.asSequence()
+            .map { it.actionResult.testsReferenceId() }
+            .toList()
+    }
+
+    fun isSuccessful(): Boolean {
+        return actionRecords.all { it.actionResult.isSuccessful() }
+    }
+}
+
+data class ActionsInvocationRecord(
+    val metrics: Metrics,
+    val actions: Actions
+)
+
+// Test Plan Summaries
+
+data class ActionTestMetadataSummary(
+    val id: StringTypedValue
+)
+
+data class TypeDefinition(
+    @SerializedName("_name")
+    val name: String
+)
+
+// Marker interface
+sealed interface ActionsTestSummaryGroupOrMeta
+
+data class ActionsTestSummaryGroupOrMetaArray(
+    @SerializedName("_values")
+    val values: List<ActionsTestSummaryGroupOrMeta>
+)
+
+data class ActionTestSummaryGroup(
+    val duration: DoubleTypedValue,
+    val identifier: StringTypedValue,
+    val name: StringTypedValue,
+    @SerializedName("subtests")
+    val subTests: ActionsTestSummaryGroupOrMetaArray
+) : ActionsTestSummaryGroupOrMeta {
+    fun summaries(): List<ActionTestSummaryMeta> {
+        return buildSummaries(mutableListOf(), this)
+    }
+
+    companion object {
+        internal fun buildSummaries(
+            summaries: MutableList<ActionTestSummaryMeta>,
+            group: ActionTestSummaryGroup
+        ): MutableList<ActionTestSummaryMeta> {
+            for (subTest in group.subTests.values) {
+                when (subTest) {
+                    is ActionTestSummaryGroup -> buildSummaries(summaries, subTest)
+                    is ActionTestSummaryMeta -> summaries += subTest
+                }
+            }
+            return summaries
+        }
+    }
+}
+
+data class ActionTestSummaryMeta(
+    val duration: DoubleTypedValue,
+    val identifier: StringTypedValue,
+    val name: StringTypedValue,
+    val summaryRef: ActionTestMetadataSummary,
+    val testStatus: StringTypedValue
+) : ActionsTestSummaryGroupOrMeta {
+    fun isSuccessful(): Boolean {
+        return testStatus.value == "Success"
+    }
+
+    fun summaryRefId(): String {
+        return summaryRef.id.value
+    }
+}
+
+class ActionTestSummaryDeserializer : JsonDeserializer<ActionsTestSummaryGroupOrMeta> {
+    override fun deserialize(
+        jsonElement: JsonElement,
+        typeOfT: Type,
+        context: JsonDeserializationContext
+    ): ActionsTestSummaryGroupOrMeta {
+        return if (checkType(jsonElement, ACTION_TEST_SUMMARY_GROUP)) {
+            val adapter = Models.gson().getAdapter(ActionTestSummaryGroup::class.java)
+            adapter.fromJson(jsonElement.toString())
+        } else if (checkType(jsonElement, ACTION_TEST_SUMMARY_META)) {
+            val adapter = Models.gson().getAdapter(ActionTestSummaryMeta::class.java)
+            adapter.fromJson(jsonElement.toString())
+        } else {
+            reportException(jsonElement)
+        }
+    }
+
+    private fun reportException(jsonElement: JsonElement): Nothing {
+        throw IllegalStateException("Unable to deserialize to ActionTestSummary ($jsonElement)")
+    }
+
+    companion object {
+        private const val TYPE = "_type"
+        private const val ACTION_TEST_SUMMARY_GROUP = "ActionTestSummaryGroup"
+        private const val ACTION_TEST_SUMMARY_META = "ActionTestMetadata"
+
+        internal fun checkType(jsonElement: JsonElement, name: String): Boolean {
+            if (!jsonElement.isJsonObject) return false
+            val json = jsonElement.asJsonObject
+            val jsonType: JsonElement? = json.get(TYPE)
+            if (jsonType != null && jsonType.isJsonObject) {
+                val adapter = Models.gson().getAdapter(TypeDefinition::class.java)
+                val type = adapter.fromJson(jsonType.toString())
+                return type.name == name
+            }
+            return false
+        }
+    }
+}
+
+data class ActionTestSummaryGroupArray(
+    @SerializedName("_values")
+    val values: List<ActionTestSummaryGroup>
+)
+
+data class ActionTestableSummary(
+    val diagnosticsDirectoryName: StringTypedValue,
+    val name: StringTypedValue,
+    val projectRelativePath: StringTypedValue,
+    val targetName: StringTypedValue,
+    val tests: ActionTestSummaryGroupArray
+)
+
+data class ActionTestableSummaryArray(
+    @SerializedName("_values")
+    val values: List<ActionTestableSummary>
+)
+
+data class ActionTestPlanRunSummary(
+    val testableSummaries: ActionTestableSummaryArray
+)
+
+data class ActionTestPlanSummaryArray(
+    @SerializedName("_values")
+    val values: List<ActionTestPlanRunSummary>
+)
+
+data class ActionTestPlanRunSummaries(
+    val summaries: ActionTestPlanSummaryArray
+) {
+    fun testSummaries(): List<ActionTestSummaryMeta> {
+        return summaries.values.flatMap { testPlanSummary ->
+            testPlanSummary.testableSummaries.values.flatMap { testableSummary ->
+                testableSummary.tests.values.flatMap { summaryGroup ->
+                    summaryGroup.summaries()
+                }
+            }
+        }
+    }
+}
+
+// Test Metrics
+
+data class ActionTestActivitySummary(
+    val title: StringTypedValue
+)
+
+data class ActionTestActivitySummaryArray(
+    @SerializedName("_values")
+    val values: List<ActionTestActivitySummary>
+) {
+    fun title(): String? {
+        return values.firstOrNull()?.title?.value
+    }
+}
+
+data class MeasurementArray(
+    @SerializedName("_values")
+    val values: List<DoubleTypedValue>
+)
+
+data class ActionTestPerformanceMetricSummary(
+    val displayName: StringTypedValue,
+    val identifier: StringTypedValue,
+    val measurements: MeasurementArray,
+    val polarity: StringTypedValue,
+    val unitOfMeasurement: StringTypedValue,
+)
+
+data class ActionTestPerformanceMetricSummaryArray(
+    @SerializedName("_values")
+    val values: List<ActionTestPerformanceMetricSummary>
+)
+
+data class ActionTestSummary(
+    val activitySummaries: ActionTestActivitySummaryArray,
+    val name: StringTypedValue,
+    val testStatus: StringTypedValue,
+    val performanceMetrics: ActionTestPerformanceMetricSummaryArray
+) {
+    fun isSuccessful(): Boolean {
+        return testStatus.value == "Success"
+    }
+
+    fun title(): String? {
+        return activitySummaries.title()
+    }
+}
+
+object Models {
+    internal fun gsonBuilder(): GsonBuilder {
+        val builder = GsonBuilder()
+        builder.registerTypeAdapter(
+            ActionsTestSummaryGroupOrMeta::class.java,
+            ActionTestSummaryDeserializer()
+        )
+        return builder
+    }
+
+    fun gson(): Gson {
+        return gsonBuilder().create()
+    }
+}
diff --git a/benchmark/benchmark-darwin-gradle-plugin/src/main/kotlin/androidx/benchmark/darwin/gradle/xcode/XcResultParser.kt b/benchmark/benchmark-darwin-gradle-plugin/src/main/kotlin/androidx/benchmark/darwin/gradle/xcode/XcResultParser.kt
new file mode 100644
index 0000000..c0c3c15
--- /dev/null
+++ b/benchmark/benchmark-darwin-gradle-plugin/src/main/kotlin/androidx/benchmark/darwin/gradle/xcode/XcResultParser.kt
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2022 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 androidx.benchmark.darwin.gradle.xcode
+
+import java.io.File
+
+/**
+ * Parses benchmark results from the xcresult file.
+ * @param xcResultFile The XCResult output file to parse.
+ * @param commandExecutor An executor that can invoke the `xcrun` and get the results from `stdout`.
+ */
+class XcResultParser(
+    private val xcResultFile: File,
+    private val commandExecutor: (args: List<String>) -> String
+) {
+    fun parseResults(): Pair<ActionsInvocationRecord, List<ActionTestSummary>> {
+        val json = commandExecutor(xcRunCommand())
+        val gson = Models.gson()
+        val record = gson.fromJson(json, ActionsInvocationRecord::class.java)
+        val summaries = record.actions.testReferences().flatMap { testRef ->
+            val summary = commandExecutor(xcRunCommand(testRef))
+            val testPlanSummaries = gson.fromJson(summary, ActionTestPlanRunSummaries::class.java)
+            testPlanSummaries.testSummaries().map { summaryMeta ->
+                val output = commandExecutor(xcRunCommand(summaryMeta.summaryRefId()))
+                val testSummary = gson.fromJson(output, ActionTestSummary::class.java)
+                testSummary
+            }
+        }
+        return record to summaries
+    }
+
+    /**
+     * Builds an `xcrun` command that can parse both the xcresult scaffold, and additionally
+     * traverse nested `plist`s to pull additional benchmark result metadata.
+     */
+    private fun xcRunCommand(id: String? = null): List<String> {
+        val args = mutableListOf(
+            "xcrun",
+            "xcresulttool",
+            "get",
+            "--path",
+            xcResultFile.absolutePath,
+            "--format",
+            "json"
+        )
+
+        if (!id.isNullOrEmpty()) {
+            args += listOf("--id", id)
+        }
+
+        return args
+    }
+}
diff --git a/benchmark/benchmark-darwin-gradle-plugin/src/test/kotlin/ModelsTest.kt b/benchmark/benchmark-darwin-gradle-plugin/src/test/kotlin/ModelsTest.kt
new file mode 100644
index 0000000..663b7ea
--- /dev/null
+++ b/benchmark/benchmark-darwin-gradle-plugin/src/test/kotlin/ModelsTest.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2022 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.
+ */
+
+import androidx.benchmark.darwin.gradle.xcode.ActionTestPlanRunSummaries
+import androidx.benchmark.darwin.gradle.xcode.ActionTestSummary
+import androidx.benchmark.darwin.gradle.xcode.ActionsInvocationRecord
+import androidx.benchmark.darwin.gradle.xcode.Models
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class ModelsTest {
+    @Test
+    fun parseXcResultOutputs() {
+        val json = testData(XCRESULT_OUTPUT_JSON).readText()
+        val gson = Models.gson()
+        val record = gson.fromJson(json, ActionsInvocationRecord::class.java)
+        assertThat(record.actions.testReferences().size).isEqualTo(1)
+        assertThat(record.metrics.size()).isEqualTo(1)
+        assertThat(record.actions.isSuccessful()).isTrue()
+    }
+
+    @Test
+    fun parseTestsReferenceOutput() {
+        val json = testData(XC_TESTS_REFERENCE_OUTPUT_JSON).readText()
+        val gson = Models.gson()
+        val testPlanSummaries = gson.fromJson(json, ActionTestPlanRunSummaries::class.java)
+        val testSummaryMetas = testPlanSummaries.testSummaries()
+        assertThat(testSummaryMetas.size).isEqualTo(1)
+        assertThat(testSummaryMetas[0].summaryRefId()).isNotEmpty()
+        assertThat(testSummaryMetas[0].isSuccessful()).isTrue()
+    }
+
+    @Test
+    fun parseTestOutput() {
+        val json = testData(XC_TEST_OUTPUT_JSON).readText()
+        val gson = Models.gson()
+        val testSummary = gson.fromJson(json, ActionTestSummary::class.java)
+        assertThat(testSummary.title()).isNotEmpty()
+        assertThat(testSummary.isSuccessful()).isTrue()
+    }
+
+    companion object {
+        private const val XCRESULT_OUTPUT_JSON = "xcresult_output.json"
+        private const val XC_TESTS_REFERENCE_OUTPUT_JSON = "tests_reference_output.json"
+        private const val XC_TEST_OUTPUT_JSON = "test_output.json"
+    }
+}
diff --git a/benchmark/benchmark-darwin-gradle-plugin/src/test/kotlin/TestExtensions.kt b/benchmark/benchmark-darwin-gradle-plugin/src/test/kotlin/TestExtensions.kt
new file mode 100644
index 0000000..28003fa
--- /dev/null
+++ b/benchmark/benchmark-darwin-gradle-plugin/src/test/kotlin/TestExtensions.kt
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2022 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.
+ */
+
+import java.io.File
+
+internal fun testData(name: String): File {
+    return File("src/test/test-data", name)
+}
diff --git a/benchmark/benchmark-darwin-gradle-plugin/src/test/kotlin/XcResultParserTest.kt b/benchmark/benchmark-darwin-gradle-plugin/src/test/kotlin/XcResultParserTest.kt
new file mode 100644
index 0000000..00468a9
--- /dev/null
+++ b/benchmark/benchmark-darwin-gradle-plugin/src/test/kotlin/XcResultParserTest.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2022 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.
+ */
+
+import androidx.benchmark.darwin.gradle.skia.Metrics
+import androidx.benchmark.darwin.gradle.xcode.Models
+import androidx.benchmark.darwin.gradle.xcode.XcResultParser
+import com.google.common.truth.Truth.assertThat
+import org.junit.Assume.assumeTrue
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class XcResultParserTest {
+    @Test
+    fun parseXcResultFileTest() {
+        val operatingSystem = System.getProperty("os.name")
+        // Only run this test on an `Mac OS X` machine.
+        assumeTrue(operatingSystem.contains("Mac", ignoreCase = true))
+        val xcResultFile = testData("sample-xcode.xcresult")
+        val parser = XcResultParser(xcResultFile) { args ->
+            val builder = ProcessBuilder(*args.toTypedArray())
+            val process = builder.start()
+            val resultCode = process.waitFor()
+            require(resultCode == 0) {
+                "Process terminated unexpectedly (${args.joinToString(separator = " ")})"
+            }
+            process.inputStream.use {
+                it.reader().readText()
+            }
+        }
+        val (record, summaries) = parser.parseResults()
+        // Usually corresponds to the size of the test suite
+        // In the case of KMP benchmarks, this is always 1 per module.
+        assertThat(record.actions.testReferences().size).isEqualTo(1)
+        assertThat(record.actions.isSuccessful()).isTrue()
+        // Metrics typically correspond to the number of tests
+        assertThat(record.metrics.size()).isEqualTo(2)
+        assertThat(summaries.isNotEmpty()).isTrue()
+        val metrics = Metrics.buildMetrics(record, summaries)
+        val json = Models.gsonBuilder()
+            .setPrettyPrinting()
+            .create()
+            .toJson(metrics)
+        println()
+        println(json)
+        println()
+        assertThat(json).isNotEmpty()
+    }
+}
diff --git a/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/README.md b/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/README.md
new file mode 100644
index 0000000..6610e6b
--- /dev/null
+++ b/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/README.md
@@ -0,0 +1,18 @@
+# Test Data
+
+## [sample-xcode.xcresult](./sample-xcode.xcresult)
+
+This is an example benchmark `xcresult` directory that is the result of running an `xcodebuild`.
+
+An example invocation might look something like:
+
+```bash
+xcodebuild test -project $SRCROOT/benchmark-darwin-samples-xcode.xcodeproj \
+    -scheme testapp-ios \
+    -destination id=7F61C467-4E4A-437C-B6EF-026FEEF3904C \
+    -resultBundlePath $SRCROOT/benchmark-darwin-samples-xcode.xcresult
+```
+
+The `xcresult` output directory stores results in a nested `plist` format. Entities in the top-level
+`plist` file point to other entities stored in other`plist` files (inside the `Data` directory),
+using a unique filename identifier.
diff --git a/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/data.0~4VqMqsI5lOfxRppnud6-VDWcNsU8J7VgFCJfW2dXPwOcAkvU-I8Um5yp9n0Zv6nr3VmcxYggaVMDFfR0U_vjKw== b/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/data.0~4VqMqsI5lOfxRppnud6-VDWcNsU8J7VgFCJfW2dXPwOcAkvU-I8Um5yp9n0Zv6nr3VmcxYggaVMDFfR0U_vjKw==
new file mode 100644
index 0000000..0637a08
--- /dev/null
+++ b/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/data.0~4VqMqsI5lOfxRppnud6-VDWcNsU8J7VgFCJfW2dXPwOcAkvU-I8Um5yp9n0Zv6nr3VmcxYggaVMDFfR0U_vjKw==
@@ -0,0 +1 @@
+[]
\ No newline at end of file
diff --git a/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/data.0~809cv4Ku3dnBcPFN0EL1A1xx_h4MH0THT41Rlz6CEankhzddTnjMa6CWhjt9F69jFsquD4x8Offmms1sFw6EKA== b/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/data.0~809cv4Ku3dnBcPFN0EL1A1xx_h4MH0THT41Rlz6CEankhzddTnjMa6CWhjt9F69jFsquD4x8Offmms1sFw6EKA==
new file mode 100644
index 0000000..bf96d3c
--- /dev/null
+++ b/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/data.0~809cv4Ku3dnBcPFN0EL1A1xx_h4MH0THT41Rlz6CEankhzddTnjMa6CWhjt9F69jFsquD4x8Offmms1sFw6EKA==
Binary files differ
diff --git a/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/data.0~BVkwcGZLlAQkY6Iyxs4jrfCad9IriiNRwFgkj-q5CYa3nlIwjU66k7hfhLcnmUHGVk4q8lK8eljDDjEI2_UwrA== b/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/data.0~BVkwcGZLlAQkY6Iyxs4jrfCad9IriiNRwFgkj-q5CYa3nlIwjU66k7hfhLcnmUHGVk4q8lK8eljDDjEI2_UwrA==
new file mode 100644
index 0000000..07b9130
--- /dev/null
+++ b/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/data.0~BVkwcGZLlAQkY6Iyxs4jrfCad9IriiNRwFgkj-q5CYa3nlIwjU66k7hfhLcnmUHGVk4q8lK8eljDDjEI2_UwrA==
Binary files differ
diff --git a/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/data.0~NcxF_MrjcLDoh1KWbeGq453YSibqfUcZRlcfxDlh1HVpo6_BqONP2ZB1AiJYY9lYfIGL1g5h3VsbOKnwGLjywQ== b/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/data.0~NcxF_MrjcLDoh1KWbeGq453YSibqfUcZRlcfxDlh1HVpo6_BqONP2ZB1AiJYY9lYfIGL1g5h3VsbOKnwGLjywQ==
new file mode 100644
index 0000000..13d30be
--- /dev/null
+++ b/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/data.0~NcxF_MrjcLDoh1KWbeGq453YSibqfUcZRlcfxDlh1HVpo6_BqONP2ZB1AiJYY9lYfIGL1g5h3VsbOKnwGLjywQ==
Binary files differ
diff --git a/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/data.0~PBlxYYh3AAyWwRJ85oIfJdGUcOuzcjExLhwrde-sNFno45FanCv9KkZmQ_eAJL9BVGKBh78r4OF_Fw2MeiGPYg== b/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/data.0~PBlxYYh3AAyWwRJ85oIfJdGUcOuzcjExLhwrde-sNFno45FanCv9KkZmQ_eAJL9BVGKBh78r4OF_Fw2MeiGPYg==
new file mode 100644
index 0000000..3032bd0
--- /dev/null
+++ b/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/data.0~PBlxYYh3AAyWwRJ85oIfJdGUcOuzcjExLhwrde-sNFno45FanCv9KkZmQ_eAJL9BVGKBh78r4OF_Fw2MeiGPYg==
Binary files differ
diff --git a/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/data.0~TV3vc3QcCkBeyTI4OZibWTUhnLOa2pj_khWYMDIsFQfzU1CC6gAO1HxXclYQhodcuQjZSw7GGBlpuUT8X0uctA== b/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/data.0~TV3vc3QcCkBeyTI4OZibWTUhnLOa2pj_khWYMDIsFQfzU1CC6gAO1HxXclYQhodcuQjZSw7GGBlpuUT8X0uctA==
new file mode 100644
index 0000000..305a73b
--- /dev/null
+++ b/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/data.0~TV3vc3QcCkBeyTI4OZibWTUhnLOa2pj_khWYMDIsFQfzU1CC6gAO1HxXclYQhodcuQjZSw7GGBlpuUT8X0uctA==
Binary files differ
diff --git a/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/data.0~YK5eNAHGX7bQW65jgs2rLhWNoTj7vgF1CBLZ2k12n3svTuVa_whSNkDWJY9V9-QVC_GCh2-DzDDLoIpXvYOToA== b/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/data.0~YK5eNAHGX7bQW65jgs2rLhWNoTj7vgF1CBLZ2k12n3svTuVa_whSNkDWJY9V9-QVC_GCh2-DzDDLoIpXvYOToA==
new file mode 100644
index 0000000..9a0adba
--- /dev/null
+++ b/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/data.0~YK5eNAHGX7bQW65jgs2rLhWNoTj7vgF1CBLZ2k12n3svTuVa_whSNkDWJY9V9-QVC_GCh2-DzDDLoIpXvYOToA==
@@ -0,0 +1 @@
+[{"name":"testmanagerd.log","type":1}]
\ No newline at end of file
diff --git a/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/data.0~bO5MkLRUHsU78utGaMM3HVQkRM7RbYps9mAfFre5AyZnGEAQmWmCQMb2RChRtxj7aiaDPHDpQxhAswnGBW3rAA== b/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/data.0~bO5MkLRUHsU78utGaMM3HVQkRM7RbYps9mAfFre5AyZnGEAQmWmCQMb2RChRtxj7aiaDPHDpQxhAswnGBW3rAA==
new file mode 100644
index 0000000..52a8bc8
--- /dev/null
+++ b/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/data.0~bO5MkLRUHsU78utGaMM3HVQkRM7RbYps9mAfFre5AyZnGEAQmWmCQMb2RChRtxj7aiaDPHDpQxhAswnGBW3rAA==
Binary files differ
diff --git a/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/data.0~eMJEUpMqij97v52rtaj8rSl-UZJhPmN04NCDD0JZ0cWyFbAHRCbjw5PJqWyqalQuFswnMkB6LvsT3uFLJVGVqw== b/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/data.0~eMJEUpMqij97v52rtaj8rSl-UZJhPmN04NCDD0JZ0cWyFbAHRCbjw5PJqWyqalQuFswnMkB6LvsT3uFLJVGVqw==
new file mode 100644
index 0000000..b385fa3
--- /dev/null
+++ b/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/data.0~eMJEUpMqij97v52rtaj8rSl-UZJhPmN04NCDD0JZ0cWyFbAHRCbjw5PJqWyqalQuFswnMkB6LvsT3uFLJVGVqw==
Binary files differ
diff --git a/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/data.0~hncU2qbL3W7Jwz-E6zuM7ThWI10rl9EEkPM5cV70-odmasyId-QIR9ZRmIMwYF8DCechev-bgJgPAiYsyImmnQ== b/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/data.0~hncU2qbL3W7Jwz-E6zuM7ThWI10rl9EEkPM5cV70-odmasyId-QIR9ZRmIMwYF8DCechev-bgJgPAiYsyImmnQ==
new file mode 100644
index 0000000..195b954
--- /dev/null
+++ b/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/data.0~hncU2qbL3W7Jwz-E6zuM7ThWI10rl9EEkPM5cV70-odmasyId-QIR9ZRmIMwYF8DCechev-bgJgPAiYsyImmnQ==
Binary files differ
diff --git a/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/data.0~l90yex7j_mtyE5LhZFKn-M7ovFj_tArzqr984SmaEo2RQ3JhValxuXW7UXxzNRGaNamX9MMhtWVxU-wT9DA6Zg== b/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/data.0~l90yex7j_mtyE5LhZFKn-M7ovFj_tArzqr984SmaEo2RQ3JhValxuXW7UXxzNRGaNamX9MMhtWVxU-wT9DA6Zg==
new file mode 100644
index 0000000..88dc22a
--- /dev/null
+++ b/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/data.0~l90yex7j_mtyE5LhZFKn-M7ovFj_tArzqr984SmaEo2RQ3JhValxuXW7UXxzNRGaNamX9MMhtWVxU-wT9DA6Zg==
Binary files differ
diff --git a/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/data.0~no8uvpmY2pkdGvoMP79k6Q4ikuedatoSuF6vBG0HR8h0GsBvPND_wHS-M8mBpxtX6NzpHV4bKIhwFkxUsd7RXA== b/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/data.0~no8uvpmY2pkdGvoMP79k6Q4ikuedatoSuF6vBG0HR8h0GsBvPND_wHS-M8mBpxtX6NzpHV4bKIhwFkxUsd7RXA==
new file mode 100644
index 0000000..c4b8635
--- /dev/null
+++ b/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/data.0~no8uvpmY2pkdGvoMP79k6Q4ikuedatoSuF6vBG0HR8h0GsBvPND_wHS-M8mBpxtX6NzpHV4bKIhwFkxUsd7RXA==
Binary files differ
diff --git a/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/data.0~vcNmCXushFQo2Asf-Ddow5-J6PKttgTE0SMgzoRxYZv26906WiKBSKSrE4LJnPfLcKZXgqb8b9B9dS0xNC6phw== b/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/data.0~vcNmCXushFQo2Asf-Ddow5-J6PKttgTE0SMgzoRxYZv26906WiKBSKSrE4LJnPfLcKZXgqb8b9B9dS0xNC6phw==
new file mode 100644
index 0000000..47daeba
--- /dev/null
+++ b/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/data.0~vcNmCXushFQo2Asf-Ddow5-J6PKttgTE0SMgzoRxYZv26906WiKBSKSrE4LJnPfLcKZXgqb8b9B9dS0xNC6phw==
Binary files differ
diff --git a/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/data.0~wV6va8rQlQLAcZ8rb5UAf1mKxaJdF9ZDlsizSwQ7FqQynxq_02ZsiipQ63-2YJ3jEODImbyk3FvKgJ2KaOPOPQ== b/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/data.0~wV6va8rQlQLAcZ8rb5UAf1mKxaJdF9ZDlsizSwQ7FqQynxq_02ZsiipQ63-2YJ3jEODImbyk3FvKgJ2KaOPOPQ==
new file mode 100644
index 0000000..ff4a430
--- /dev/null
+++ b/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/data.0~wV6va8rQlQLAcZ8rb5UAf1mKxaJdF9ZDlsizSwQ7FqQynxq_02ZsiipQ63-2YJ3jEODImbyk3FvKgJ2KaOPOPQ==
Binary files differ
diff --git a/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/data.0~wpEwfbWjFv9AR7VY2bJ5g_ndbKMYwUEFGZ1w3MdarhPZE605CCX5XHRoL5cjlzYTdIpHF8aXtcLt6kz2ELrAsQ== b/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/data.0~wpEwfbWjFv9AR7VY2bJ5g_ndbKMYwUEFGZ1w3MdarhPZE605CCX5XHRoL5cjlzYTdIpHF8aXtcLt6kz2ELrAsQ==
new file mode 100644
index 0000000..70db723
--- /dev/null
+++ b/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/data.0~wpEwfbWjFv9AR7VY2bJ5g_ndbKMYwUEFGZ1w3MdarhPZE605CCX5XHRoL5cjlzYTdIpHF8aXtcLt6kz2ELrAsQ==
Binary files differ
diff --git a/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/data.0~z_lmQvK-mq_MbksvJ2oEALgVyhIwk-VirNem09JWgC47IaBCD7JaSPfQeouKa8iSKUchkiKh993EavN5hgc0Mg== b/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/data.0~z_lmQvK-mq_MbksvJ2oEALgVyhIwk-VirNem09JWgC47IaBCD7JaSPfQeouKa8iSKUchkiKh993EavN5hgc0Mg==
new file mode 100644
index 0000000..0d78c58
--- /dev/null
+++ b/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/data.0~z_lmQvK-mq_MbksvJ2oEALgVyhIwk-VirNem09JWgC47IaBCD7JaSPfQeouKa8iSKUchkiKh993EavN5hgc0Mg==
Binary files differ
diff --git a/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/refs.0~4VqMqsI5lOfxRppnud6-VDWcNsU8J7VgFCJfW2dXPwOcAkvU-I8Um5yp9n0Zv6nr3VmcxYggaVMDFfR0U_vjKw== b/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/refs.0~4VqMqsI5lOfxRppnud6-VDWcNsU8J7VgFCJfW2dXPwOcAkvU-I8Um5yp9n0Zv6nr3VmcxYggaVMDFfR0U_vjKw==
new file mode 100644
index 0000000..f76dd23
--- /dev/null
+++ b/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/refs.0~4VqMqsI5lOfxRppnud6-VDWcNsU8J7VgFCJfW2dXPwOcAkvU-I8Um5yp9n0Zv6nr3VmcxYggaVMDFfR0U_vjKw==
Binary files differ
diff --git a/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/refs.0~809cv4Ku3dnBcPFN0EL1A1xx_h4MH0THT41Rlz6CEankhzddTnjMa6CWhjt9F69jFsquD4x8Offmms1sFw6EKA== b/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/refs.0~809cv4Ku3dnBcPFN0EL1A1xx_h4MH0THT41Rlz6CEankhzddTnjMa6CWhjt9F69jFsquD4x8Offmms1sFw6EKA==
new file mode 100644
index 0000000..0ec3518
--- /dev/null
+++ b/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/refs.0~809cv4Ku3dnBcPFN0EL1A1xx_h4MH0THT41Rlz6CEankhzddTnjMa6CWhjt9F69jFsquD4x8Offmms1sFw6EKA==
Binary files differ
diff --git a/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/refs.0~BVkwcGZLlAQkY6Iyxs4jrfCad9IriiNRwFgkj-q5CYa3nlIwjU66k7hfhLcnmUHGVk4q8lK8eljDDjEI2_UwrA== b/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/refs.0~BVkwcGZLlAQkY6Iyxs4jrfCad9IriiNRwFgkj-q5CYa3nlIwjU66k7hfhLcnmUHGVk4q8lK8eljDDjEI2_UwrA==
new file mode 100644
index 0000000..f76dd23
--- /dev/null
+++ b/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/refs.0~BVkwcGZLlAQkY6Iyxs4jrfCad9IriiNRwFgkj-q5CYa3nlIwjU66k7hfhLcnmUHGVk4q8lK8eljDDjEI2_UwrA==
Binary files differ
diff --git a/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/refs.0~NcxF_MrjcLDoh1KWbeGq453YSibqfUcZRlcfxDlh1HVpo6_BqONP2ZB1AiJYY9lYfIGL1g5h3VsbOKnwGLjywQ== b/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/refs.0~NcxF_MrjcLDoh1KWbeGq453YSibqfUcZRlcfxDlh1HVpo6_BqONP2ZB1AiJYY9lYfIGL1g5h3VsbOKnwGLjywQ==
new file mode 100644
index 0000000..f76dd23
--- /dev/null
+++ b/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/refs.0~NcxF_MrjcLDoh1KWbeGq453YSibqfUcZRlcfxDlh1HVpo6_BqONP2ZB1AiJYY9lYfIGL1g5h3VsbOKnwGLjywQ==
Binary files differ
diff --git a/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/refs.0~PBlxYYh3AAyWwRJ85oIfJdGUcOuzcjExLhwrde-sNFno45FanCv9KkZmQ_eAJL9BVGKBh78r4OF_Fw2MeiGPYg== b/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/refs.0~PBlxYYh3AAyWwRJ85oIfJdGUcOuzcjExLhwrde-sNFno45FanCv9KkZmQ_eAJL9BVGKBh78r4OF_Fw2MeiGPYg==
new file mode 100644
index 0000000..6fde618
--- /dev/null
+++ b/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/refs.0~PBlxYYh3AAyWwRJ85oIfJdGUcOuzcjExLhwrde-sNFno45FanCv9KkZmQ_eAJL9BVGKBh78r4OF_Fw2MeiGPYg==
Binary files differ
diff --git a/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/refs.0~TV3vc3QcCkBeyTI4OZibWTUhnLOa2pj_khWYMDIsFQfzU1CC6gAO1HxXclYQhodcuQjZSw7GGBlpuUT8X0uctA== b/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/refs.0~TV3vc3QcCkBeyTI4OZibWTUhnLOa2pj_khWYMDIsFQfzU1CC6gAO1HxXclYQhodcuQjZSw7GGBlpuUT8X0uctA==
new file mode 100644
index 0000000..f76dd23
--- /dev/null
+++ b/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/refs.0~TV3vc3QcCkBeyTI4OZibWTUhnLOa2pj_khWYMDIsFQfzU1CC6gAO1HxXclYQhodcuQjZSw7GGBlpuUT8X0uctA==
Binary files differ
diff --git a/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/refs.0~YK5eNAHGX7bQW65jgs2rLhWNoTj7vgF1CBLZ2k12n3svTuVa_whSNkDWJY9V9-QVC_GCh2-DzDDLoIpXvYOToA== b/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/refs.0~YK5eNAHGX7bQW65jgs2rLhWNoTj7vgF1CBLZ2k12n3svTuVa_whSNkDWJY9V9-QVC_GCh2-DzDDLoIpXvYOToA==
new file mode 100644
index 0000000..7bc9ef9c
--- /dev/null
+++ b/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/refs.0~YK5eNAHGX7bQW65jgs2rLhWNoTj7vgF1CBLZ2k12n3svTuVa_whSNkDWJY9V9-QVC_GCh2-DzDDLoIpXvYOToA==
Binary files differ
diff --git a/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/refs.0~bO5MkLRUHsU78utGaMM3HVQkRM7RbYps9mAfFre5AyZnGEAQmWmCQMb2RChRtxj7aiaDPHDpQxhAswnGBW3rAA== b/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/refs.0~bO5MkLRUHsU78utGaMM3HVQkRM7RbYps9mAfFre5AyZnGEAQmWmCQMb2RChRtxj7aiaDPHDpQxhAswnGBW3rAA==
new file mode 100644
index 0000000..f76dd23
--- /dev/null
+++ b/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/refs.0~bO5MkLRUHsU78utGaMM3HVQkRM7RbYps9mAfFre5AyZnGEAQmWmCQMb2RChRtxj7aiaDPHDpQxhAswnGBW3rAA==
Binary files differ
diff --git a/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/refs.0~eMJEUpMqij97v52rtaj8rSl-UZJhPmN04NCDD0JZ0cWyFbAHRCbjw5PJqWyqalQuFswnMkB6LvsT3uFLJVGVqw== b/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/refs.0~eMJEUpMqij97v52rtaj8rSl-UZJhPmN04NCDD0JZ0cWyFbAHRCbjw5PJqWyqalQuFswnMkB6LvsT3uFLJVGVqw==
new file mode 100644
index 0000000..f76dd23
--- /dev/null
+++ b/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/refs.0~eMJEUpMqij97v52rtaj8rSl-UZJhPmN04NCDD0JZ0cWyFbAHRCbjw5PJqWyqalQuFswnMkB6LvsT3uFLJVGVqw==
Binary files differ
diff --git a/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/refs.0~hncU2qbL3W7Jwz-E6zuM7ThWI10rl9EEkPM5cV70-odmasyId-QIR9ZRmIMwYF8DCechev-bgJgPAiYsyImmnQ== b/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/refs.0~hncU2qbL3W7Jwz-E6zuM7ThWI10rl9EEkPM5cV70-odmasyId-QIR9ZRmIMwYF8DCechev-bgJgPAiYsyImmnQ==
new file mode 100644
index 0000000..f76dd23
--- /dev/null
+++ b/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/refs.0~hncU2qbL3W7Jwz-E6zuM7ThWI10rl9EEkPM5cV70-odmasyId-QIR9ZRmIMwYF8DCechev-bgJgPAiYsyImmnQ==
Binary files differ
diff --git a/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/refs.0~l90yex7j_mtyE5LhZFKn-M7ovFj_tArzqr984SmaEo2RQ3JhValxuXW7UXxzNRGaNamX9MMhtWVxU-wT9DA6Zg== b/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/refs.0~l90yex7j_mtyE5LhZFKn-M7ovFj_tArzqr984SmaEo2RQ3JhValxuXW7UXxzNRGaNamX9MMhtWVxU-wT9DA6Zg==
new file mode 100644
index 0000000..f76dd23
--- /dev/null
+++ b/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/refs.0~l90yex7j_mtyE5LhZFKn-M7ovFj_tArzqr984SmaEo2RQ3JhValxuXW7UXxzNRGaNamX9MMhtWVxU-wT9DA6Zg==
Binary files differ
diff --git a/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/refs.0~no8uvpmY2pkdGvoMP79k6Q4ikuedatoSuF6vBG0HR8h0GsBvPND_wHS-M8mBpxtX6NzpHV4bKIhwFkxUsd7RXA== b/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/refs.0~no8uvpmY2pkdGvoMP79k6Q4ikuedatoSuF6vBG0HR8h0GsBvPND_wHS-M8mBpxtX6NzpHV4bKIhwFkxUsd7RXA==
new file mode 100644
index 0000000..e12afe0
--- /dev/null
+++ b/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/refs.0~no8uvpmY2pkdGvoMP79k6Q4ikuedatoSuF6vBG0HR8h0GsBvPND_wHS-M8mBpxtX6NzpHV4bKIhwFkxUsd7RXA==
Binary files differ
diff --git a/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/refs.0~vcNmCXushFQo2Asf-Ddow5-J6PKttgTE0SMgzoRxYZv26906WiKBSKSrE4LJnPfLcKZXgqb8b9B9dS0xNC6phw== b/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/refs.0~vcNmCXushFQo2Asf-Ddow5-J6PKttgTE0SMgzoRxYZv26906WiKBSKSrE4LJnPfLcKZXgqb8b9B9dS0xNC6phw==
new file mode 100644
index 0000000..bd95f97
--- /dev/null
+++ b/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/refs.0~vcNmCXushFQo2Asf-Ddow5-J6PKttgTE0SMgzoRxYZv26906WiKBSKSrE4LJnPfLcKZXgqb8b9B9dS0xNC6phw==
Binary files differ
diff --git a/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/refs.0~wV6va8rQlQLAcZ8rb5UAf1mKxaJdF9ZDlsizSwQ7FqQynxq_02ZsiipQ63-2YJ3jEODImbyk3FvKgJ2KaOPOPQ== b/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/refs.0~wV6va8rQlQLAcZ8rb5UAf1mKxaJdF9ZDlsizSwQ7FqQynxq_02ZsiipQ63-2YJ3jEODImbyk3FvKgJ2KaOPOPQ==
new file mode 100644
index 0000000..f76dd23
--- /dev/null
+++ b/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/refs.0~wV6va8rQlQLAcZ8rb5UAf1mKxaJdF9ZDlsizSwQ7FqQynxq_02ZsiipQ63-2YJ3jEODImbyk3FvKgJ2KaOPOPQ==
Binary files differ
diff --git a/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/refs.0~wpEwfbWjFv9AR7VY2bJ5g_ndbKMYwUEFGZ1w3MdarhPZE605CCX5XHRoL5cjlzYTdIpHF8aXtcLt6kz2ELrAsQ== b/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/refs.0~wpEwfbWjFv9AR7VY2bJ5g_ndbKMYwUEFGZ1w3MdarhPZE605CCX5XHRoL5cjlzYTdIpHF8aXtcLt6kz2ELrAsQ==
new file mode 100644
index 0000000..a5fa9ca
--- /dev/null
+++ b/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/refs.0~wpEwfbWjFv9AR7VY2bJ5g_ndbKMYwUEFGZ1w3MdarhPZE605CCX5XHRoL5cjlzYTdIpHF8aXtcLt6kz2ELrAsQ==
Binary files differ
diff --git a/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/refs.0~z_lmQvK-mq_MbksvJ2oEALgVyhIwk-VirNem09JWgC47IaBCD7JaSPfQeouKa8iSKUchkiKh993EavN5hgc0Mg== b/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/refs.0~z_lmQvK-mq_MbksvJ2oEALgVyhIwk-VirNem09JWgC47IaBCD7JaSPfQeouKa8iSKUchkiKh993EavN5hgc0Mg==
new file mode 100644
index 0000000..f76dd23
--- /dev/null
+++ b/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Data/refs.0~z_lmQvK-mq_MbksvJ2oEALgVyhIwk-VirNem09JWgC47IaBCD7JaSPfQeouKa8iSKUchkiKh993EavN5hgc0Mg==
Binary files differ
diff --git a/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Info.plist b/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Info.plist
new file mode 100644
index 0000000..8e63641
--- /dev/null
+++ b/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/sample-xcode.xcresult/Info.plist
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>dateCreated</key>
+	<date>2022-09-23T17:38:01Z</date>
+	<key>externalLocations</key>
+	<array/>
+	<key>rootId</key>
+	<dict>
+		<key>hash</key>
+		<string>0~vcNmCXushFQo2Asf-Ddow5-J6PKttgTE0SMgzoRxYZv26906WiKBSKSrE4LJnPfLcKZXgqb8b9B9dS0xNC6phw==</string>
+	</dict>
+	<key>storage</key>
+	<dict>
+		<key>backend</key>
+		<string>fileBacked2</string>
+		<key>compression</key>
+		<string>standard</string>
+	</dict>
+	<key>version</key>
+	<dict>
+		<key>major</key>
+		<integer>3</integer>
+		<key>minor</key>
+		<integer>34</integer>
+	</dict>
+</dict>
+</plist>
diff --git a/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/test_output.json b/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/test_output.json
new file mode 100644
index 0000000..a8287ad
--- /dev/null
+++ b/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/test_output.json
@@ -0,0 +1,624 @@
+{
+  "_type" : {
+    "_name" : "ActionTestSummary",
+    "_supertype" : {
+      "_name" : "ActionTestSummaryIdentifiableObject",
+      "_supertype" : {
+        "_name" : "ActionAbstractTestSummary"
+      }
+    }
+  },
+  "activitySummaries" : {
+    "_type" : {
+      "_name" : "Array"
+    },
+    "_values" : [
+      {
+        "_type" : {
+          "_name" : "ActionTestActivitySummary"
+        },
+        "activityType" : {
+          "_type" : {
+            "_name" : "String"
+          },
+          "_value" : "com.apple.dt.xctest.activity-type.userCreated"
+        },
+        "finish" : {
+          "_type" : {
+            "_name" : "Date"
+          },
+          "_value" : "2022-09-20T15:39:30.433-0700"
+        },
+        "start" : {
+          "_type" : {
+            "_name" : "Date"
+          },
+          "_value" : "2022-09-20T15:39:30.366-0700"
+        },
+        "title" : {
+          "_type" : {
+            "_name" : "String"
+          },
+          "_value" : "Allocate an ArrayList of size 1000"
+        },
+        "uuid" : {
+          "_type" : {
+            "_name" : "String"
+          },
+          "_value" : "660362D3-AA8A-4630-B098-780ED90922E2"
+        }
+      }
+    ]
+  },
+  "duration" : {
+    "_type" : {
+      "_name" : "Double"
+    },
+    "_value" : "0.0741419792175293"
+  },
+  "identifier" : {
+    "_type" : {
+      "_name" : "String"
+    },
+    "_value" : "BenchmarkTest\/runBenchmark()"
+  },
+  "name" : {
+    "_type" : {
+      "_name" : "String"
+    },
+    "_value" : "runBenchmark()"
+  },
+  "performanceMetrics" : {
+    "_type" : {
+      "_name" : "Array"
+    },
+    "_values" : [
+      {
+        "_type" : {
+          "_name" : "ActionTestPerformanceMetricSummary"
+        },
+        "displayName" : {
+          "_type" : {
+            "_name" : "String"
+          },
+          "_value" : "Memory Physical"
+        },
+        "identifier" : {
+          "_type" : {
+            "_name" : "String"
+          },
+          "_value" : "com.apple.dt.XCTMetric_Memory.physical"
+        },
+        "maxPercentRegression" : {
+          "_type" : {
+            "_name" : "Double"
+          },
+          "_value" : "10.0"
+        },
+        "maxPercentRelativeStandardDeviation" : {
+          "_type" : {
+            "_name" : "Double"
+          },
+          "_value" : "10.0"
+        },
+        "maxRegression" : {
+          "_type" : {
+            "_name" : "Double"
+          },
+          "_value" : "0.0"
+        },
+        "maxStandardDeviation" : {
+          "_type" : {
+            "_name" : "Double"
+          },
+          "_value" : "0.0"
+        },
+        "measurements" : {
+          "_type" : {
+            "_name" : "Array"
+          },
+          "_values" : [
+            {
+              "_type" : {
+                "_name" : "Double"
+              },
+              "_value" : "8208.448"
+            },
+            {
+              "_type" : {
+                "_name" : "Double"
+              },
+              "_value" : "8192.0"
+            },
+            {
+              "_type" : {
+                "_name" : "Double"
+              },
+              "_value" : "8192.0"
+            },
+            {
+              "_type" : {
+                "_name" : "Double"
+              },
+              "_value" : "8208.384"
+            },
+            {
+              "_type" : {
+                "_name" : "Double"
+              },
+              "_value" : "8257.536"
+            }
+          ]
+        },
+        "polarity" : {
+          "_type" : {
+            "_name" : "String"
+          },
+          "_value" : "prefers smaller"
+        },
+        "unitOfMeasurement" : {
+          "_type" : {
+            "_name" : "String"
+          },
+          "_value" : "kB"
+        }
+      },
+      {
+        "_type" : {
+          "_name" : "ActionTestPerformanceMetricSummary"
+        },
+        "displayName" : {
+          "_type" : {
+            "_name" : "String"
+          },
+          "_value" : "Clock Monotonic Time"
+        },
+        "identifier" : {
+          "_type" : {
+            "_name" : "String"
+          },
+          "_value" : "com.apple.dt.XCTMetric_Clock.time.monotonic"
+        },
+        "maxPercentRegression" : {
+          "_type" : {
+            "_name" : "Double"
+          },
+          "_value" : "10.0"
+        },
+        "maxPercentRelativeStandardDeviation" : {
+          "_type" : {
+            "_name" : "Double"
+          },
+          "_value" : "10.0"
+        },
+        "maxRegression" : {
+          "_type" : {
+            "_name" : "Double"
+          },
+          "_value" : "0.0"
+        },
+        "maxStandardDeviation" : {
+          "_type" : {
+            "_name" : "Double"
+          },
+          "_value" : "0.0"
+        },
+        "measurements" : {
+          "_type" : {
+            "_name" : "Array"
+          },
+          "_values" : [
+            {
+              "_type" : {
+                "_name" : "Double"
+              },
+              "_value" : "0.003374259"
+            },
+            {
+              "_type" : {
+                "_name" : "Double"
+              },
+              "_value" : "0.0011105260000000001"
+            },
+            {
+              "_type" : {
+                "_name" : "Double"
+              },
+              "_value" : "0.0009685430000000001"
+            },
+            {
+              "_type" : {
+                "_name" : "Double"
+              },
+              "_value" : "0.001031847"
+            },
+            {
+              "_type" : {
+                "_name" : "Double"
+              },
+              "_value" : "0.006419944"
+            }
+          ]
+        },
+        "polarity" : {
+          "_type" : {
+            "_name" : "String"
+          },
+          "_value" : "prefers smaller"
+        },
+        "unitOfMeasurement" : {
+          "_type" : {
+            "_name" : "String"
+          },
+          "_value" : "s"
+        }
+      },
+      {
+        "_type" : {
+          "_name" : "ActionTestPerformanceMetricSummary"
+        },
+        "displayName" : {
+          "_type" : {
+            "_name" : "String"
+          },
+          "_value" : "CPU Cycles"
+        },
+        "identifier" : {
+          "_type" : {
+            "_name" : "String"
+          },
+          "_value" : "com.apple.dt.XCTMetric_CPU.cycles"
+        },
+        "maxPercentRegression" : {
+          "_type" : {
+            "_name" : "Double"
+          },
+          "_value" : "10.0"
+        },
+        "maxPercentRelativeStandardDeviation" : {
+          "_type" : {
+            "_name" : "Double"
+          },
+          "_value" : "10.0"
+        },
+        "maxRegression" : {
+          "_type" : {
+            "_name" : "Double"
+          },
+          "_value" : "0.0"
+        },
+        "maxStandardDeviation" : {
+          "_type" : {
+            "_name" : "Double"
+          },
+          "_value" : "0.0"
+        },
+        "measurements" : {
+          "_type" : {
+            "_name" : "Array"
+          },
+          "_values" : [
+            {
+              "_type" : {
+                "_name" : "Double"
+              },
+              "_value" : "8568.973"
+            },
+            {
+              "_type" : {
+                "_name" : "Double"
+              },
+              "_value" : "5173.538"
+            },
+            {
+              "_type" : {
+                "_name" : "Double"
+              },
+              "_value" : "4663.702"
+            },
+            {
+              "_type" : {
+                "_name" : "Double"
+              },
+              "_value" : "4684.408"
+            },
+            {
+              "_type" : {
+                "_name" : "Double"
+              },
+              "_value" : "21681.682"
+            }
+          ]
+        },
+        "polarity" : {
+          "_type" : {
+            "_name" : "String"
+          },
+          "_value" : "prefers smaller"
+        },
+        "unitOfMeasurement" : {
+          "_type" : {
+            "_name" : "String"
+          },
+          "_value" : "kC"
+        }
+      },
+      {
+        "_type" : {
+          "_name" : "ActionTestPerformanceMetricSummary"
+        },
+        "displayName" : {
+          "_type" : {
+            "_name" : "String"
+          },
+          "_value" : "CPU Time"
+        },
+        "identifier" : {
+          "_type" : {
+            "_name" : "String"
+          },
+          "_value" : "com.apple.dt.XCTMetric_CPU.time"
+        },
+        "maxPercentRegression" : {
+          "_type" : {
+            "_name" : "Double"
+          },
+          "_value" : "10.0"
+        },
+        "maxPercentRelativeStandardDeviation" : {
+          "_type" : {
+            "_name" : "Double"
+          },
+          "_value" : "10.0"
+        },
+        "maxRegression" : {
+          "_type" : {
+            "_name" : "Double"
+          },
+          "_value" : "0.0"
+        },
+        "maxStandardDeviation" : {
+          "_type" : {
+            "_name" : "Double"
+          },
+          "_value" : "0.0"
+        },
+        "measurements" : {
+          "_type" : {
+            "_name" : "Array"
+          },
+          "_values" : [
+            {
+              "_type" : {
+                "_name" : "Double"
+              },
+              "_value" : "0.0027404810000000003"
+            },
+            {
+              "_type" : {
+                "_name" : "Double"
+              },
+              "_value" : "0.0016844030000000001"
+            },
+            {
+              "_type" : {
+                "_name" : "Double"
+              },
+              "_value" : "0.0014999850000000001"
+            },
+            {
+              "_type" : {
+                "_name" : "Double"
+              },
+              "_value" : "0.0015120390000000002"
+            },
+            {
+              "_type" : {
+                "_name" : "Double"
+              },
+              "_value" : "0.007035559"
+            }
+          ]
+        },
+        "polarity" : {
+          "_type" : {
+            "_name" : "String"
+          },
+          "_value" : "prefers smaller"
+        },
+        "unitOfMeasurement" : {
+          "_type" : {
+            "_name" : "String"
+          },
+          "_value" : "s"
+        }
+      },
+      {
+        "_type" : {
+          "_name" : "ActionTestPerformanceMetricSummary"
+        },
+        "displayName" : {
+          "_type" : {
+            "_name" : "String"
+          },
+          "_value" : "CPU Instructions Retired"
+        },
+        "identifier" : {
+          "_type" : {
+            "_name" : "String"
+          },
+          "_value" : "com.apple.dt.XCTMetric_CPU.instructions_retired"
+        },
+        "maxPercentRegression" : {
+          "_type" : {
+            "_name" : "Double"
+          },
+          "_value" : "10.0"
+        },
+        "maxPercentRelativeStandardDeviation" : {
+          "_type" : {
+            "_name" : "Double"
+          },
+          "_value" : "10.0"
+        },
+        "maxRegression" : {
+          "_type" : {
+            "_name" : "Double"
+          },
+          "_value" : "0.0"
+        },
+        "maxStandardDeviation" : {
+          "_type" : {
+            "_name" : "Double"
+          },
+          "_value" : "0.0"
+        },
+        "measurements" : {
+          "_type" : {
+            "_name" : "Array"
+          },
+          "_values" : [
+            {
+              "_type" : {
+                "_name" : "Double"
+              },
+              "_value" : "16386.777"
+            },
+            {
+              "_type" : {
+                "_name" : "Double"
+              },
+              "_value" : "8917.514"
+            },
+            {
+              "_type" : {
+                "_name" : "Double"
+              },
+              "_value" : "8777.292"
+            },
+            {
+              "_type" : {
+                "_name" : "Double"
+              },
+              "_value" : "8398.467"
+            },
+            {
+              "_type" : {
+                "_name" : "Double"
+              },
+              "_value" : "40257.553"
+            }
+          ]
+        },
+        "polarity" : {
+          "_type" : {
+            "_name" : "String"
+          },
+          "_value" : "prefers smaller"
+        },
+        "unitOfMeasurement" : {
+          "_type" : {
+            "_name" : "String"
+          },
+          "_value" : "kI"
+        }
+      },
+      {
+        "_type" : {
+          "_name" : "ActionTestPerformanceMetricSummary"
+        },
+        "displayName" : {
+          "_type" : {
+            "_name" : "String"
+          },
+          "_value" : "Memory Peak Physical"
+        },
+        "identifier" : {
+          "_type" : {
+            "_name" : "String"
+          },
+          "_value" : "com.apple.dt.XCTMetric_Memory.physical_peak"
+        },
+        "maxPercentRegression" : {
+          "_type" : {
+            "_name" : "Double"
+          },
+          "_value" : "10.0"
+        },
+        "maxPercentRelativeStandardDeviation" : {
+          "_type" : {
+            "_name" : "Double"
+          },
+          "_value" : "10.0"
+        },
+        "maxRegression" : {
+          "_type" : {
+            "_name" : "Double"
+          },
+          "_value" : "0.0"
+        },
+        "maxStandardDeviation" : {
+          "_type" : {
+            "_name" : "Double"
+          },
+          "_value" : "0.0"
+        },
+        "measurements" : {
+          "_type" : {
+            "_name" : "Array"
+          },
+          "_values" : [
+            {
+              "_type" : {
+                "_name" : "Double"
+              },
+              "_value" : "0.0"
+            },
+            {
+              "_type" : {
+                "_name" : "Double"
+              },
+              "_value" : "0.0"
+            },
+            {
+              "_type" : {
+                "_name" : "Double"
+              },
+              "_value" : "0.0"
+            },
+            {
+              "_type" : {
+                "_name" : "Double"
+              },
+              "_value" : "0.0"
+            },
+            {
+              "_type" : {
+                "_name" : "Double"
+              },
+              "_value" : "0.0"
+            }
+          ]
+        },
+        "polarity" : {
+          "_type" : {
+            "_name" : "String"
+          },
+          "_value" : "prefers smaller"
+        },
+        "unitOfMeasurement" : {
+          "_type" : {
+            "_name" : "String"
+          },
+          "_value" : "kB"
+        }
+      }
+    ]
+  },
+  "testStatus" : {
+    "_type" : {
+      "_name" : "String"
+    },
+    "_value" : "Success"
+  }
+}
diff --git a/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/tests_reference_output.json b/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/tests_reference_output.json
new file mode 100644
index 0000000..9b3286a
--- /dev/null
+++ b/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/tests_reference_output.json
@@ -0,0 +1,277 @@
+{
+  "_type" : {
+    "_name" : "ActionTestPlanRunSummaries"
+  },
+  "summaries" : {
+    "_type" : {
+      "_name" : "Array"
+    },
+    "_values" : [
+      {
+        "_type" : {
+          "_name" : "ActionTestPlanRunSummary",
+          "_supertype" : {
+            "_name" : "ActionAbstractTestSummary"
+          }
+        },
+        "name" : {
+          "_type" : {
+            "_name" : "String"
+          },
+          "_value" : "Test Scheme Action"
+        },
+        "testableSummaries" : {
+          "_type" : {
+            "_name" : "Array"
+          },
+          "_values" : [
+            {
+              "_type" : {
+                "_name" : "ActionTestableSummary",
+                "_supertype" : {
+                  "_name" : "ActionAbstractTestSummary"
+                }
+              },
+              "diagnosticsDirectoryName" : {
+                "_type" : {
+                  "_name" : "String"
+                },
+                "_value" : "testapp-ios-benchmarks-D5CA694B-66BF-479C-ABE4-0D3BDF591768-Configuration-Test Scheme Action-Iteration-1"
+              },
+              "name" : {
+                "_type" : {
+                  "_name" : "String"
+                },
+                "_value" : "testapp-ios-benchmarks"
+              },
+              "projectRelativePath" : {
+                "_type" : {
+                  "_name" : "String"
+                },
+                "_value" : "benchmark-darwin-sample-xcode.xcodeproj"
+              },
+              "targetName" : {
+                "_type" : {
+                  "_name" : "String"
+                },
+                "_value" : "testapp-ios-benchmarks"
+              },
+              "testKind" : {
+                "_type" : {
+                  "_name" : "String"
+                },
+                "_value" : "xctest-tool hosted"
+              },
+              "testLanguage" : {
+                "_type" : {
+                  "_name" : "String"
+                },
+                "_value" : ""
+              },
+              "testRegion" : {
+                "_type" : {
+                  "_name" : "String"
+                },
+                "_value" : ""
+              },
+              "tests" : {
+                "_type" : {
+                  "_name" : "Array"
+                },
+                "_values" : [
+                  {
+                    "_type" : {
+                      "_name" : "ActionTestSummaryGroup",
+                      "_supertype" : {
+                        "_name" : "ActionTestSummaryIdentifiableObject",
+                        "_supertype" : {
+                          "_name" : "ActionAbstractTestSummary"
+                        }
+                      }
+                    },
+                    "duration" : {
+                      "_type" : {
+                        "_name" : "Double"
+                      },
+                      "_value" : "0.07568299770355225"
+                    },
+                    "identifier" : {
+                      "_type" : {
+                        "_name" : "String"
+                      },
+                      "_value" : "All tests"
+                    },
+                    "name" : {
+                      "_type" : {
+                        "_name" : "String"
+                      },
+                      "_value" : "All tests"
+                    },
+                    "subtests" : {
+                      "_type" : {
+                        "_name" : "Array"
+                      },
+                      "_values" : [
+                        {
+                          "_type" : {
+                            "_name" : "ActionTestSummaryGroup",
+                            "_supertype" : {
+                              "_name" : "ActionTestSummaryIdentifiableObject",
+                              "_supertype" : {
+                                "_name" : "ActionAbstractTestSummary"
+                              }
+                            }
+                          },
+                          "duration" : {
+                            "_type" : {
+                              "_name" : "Double"
+                            },
+                            "_value" : "0.07503998279571533"
+                          },
+                          "identifier" : {
+                            "_type" : {
+                              "_name" : "String"
+                            },
+                            "_value" : "testapp-ios-benchmarks.xctest"
+                          },
+                          "name" : {
+                            "_type" : {
+                              "_name" : "String"
+                            },
+                            "_value" : "testapp-ios-benchmarks.xctest"
+                          },
+                          "subtests" : {
+                            "_type" : {
+                              "_name" : "Array"
+                            },
+                            "_values" : [
+                              {
+                                "_type" : {
+                                  "_name" : "ActionTestSummaryGroup",
+                                  "_supertype" : {
+                                    "_name" : "ActionTestSummaryIdentifiableObject",
+                                    "_supertype" : {
+                                      "_name" : "ActionAbstractTestSummary"
+                                    }
+                                  }
+                                },
+                                "duration" : {
+                                  "_type" : {
+                                    "_name" : "Double"
+                                  },
+                                  "_value" : "0.07448196411132812"
+                                },
+                                "identifier" : {
+                                  "_type" : {
+                                    "_name" : "String"
+                                  },
+                                  "_value" : "BenchmarkTest"
+                                },
+                                "name" : {
+                                  "_type" : {
+                                    "_name" : "String"
+                                  },
+                                  "_value" : "BenchmarkTest"
+                                },
+                                "subtests" : {
+                                  "_type" : {
+                                    "_name" : "Array"
+                                  },
+                                  "_values" : [
+                                    {
+                                      "_type" : {
+                                        "_name" : "ActionTestMetadata",
+                                        "_supertype" : {
+                                          "_name" : "ActionTestSummaryIdentifiableObject",
+                                          "_supertype" : {
+                                            "_name" : "ActionAbstractTestSummary"
+                                          }
+                                        }
+                                      },
+                                      "duration" : {
+                                        "_type" : {
+                                          "_name" : "Double"
+                                        },
+                                        "_value" : "0.0741419792175293"
+                                      },
+                                      "identifier" : {
+                                        "_type" : {
+                                          "_name" : "String"
+                                        },
+                                        "_value" : "BenchmarkTest\/runBenchmark()"
+                                      },
+                                      "name" : {
+                                        "_type" : {
+                                          "_name" : "String"
+                                        },
+                                        "_value" : "runBenchmark()"
+                                      },
+                                      "summaryRef" : {
+                                        "_type" : {
+                                          "_name" : "Reference"
+                                        },
+                                        "id" : {
+                                          "_type" : {
+                                            "_name" : "String"
+                                          },
+                                          "_value" : "0~9QZnTT_BgRyY356cOax6Xlj_lclcNKu46aQPHsHicDo5aDaBNGg0f1y5hrlGl30pfE8RtG0-7IY1sZEQSgv-yA=="
+                                        },
+                                        "targetType" : {
+                                          "_type" : {
+                                            "_name" : "TypeDefinition"
+                                          },
+                                          "name" : {
+                                            "_type" : {
+                                              "_name" : "String"
+                                            },
+                                            "_value" : "ActionTestSummary"
+                                          },
+                                          "supertype" : {
+                                            "_type" : {
+                                              "_name" : "TypeDefinition"
+                                            },
+                                            "name" : {
+                                              "_type" : {
+                                                "_name" : "String"
+                                              },
+                                              "_value" : "ActionTestSummaryIdentifiableObject"
+                                            },
+                                            "supertype" : {
+                                              "_type" : {
+                                                "_name" : "TypeDefinition"
+                                              },
+                                              "name" : {
+                                                "_type" : {
+                                                  "_name" : "String"
+                                                },
+                                                "_value" : "ActionAbstractTestSummary"
+                                              }
+                                            }
+                                          }
+                                        }
+                                      },
+                                      "testStatus" : {
+                                        "_type" : {
+                                          "_name" : "String"
+                                        },
+                                        "_value" : "Success"
+                                      }
+                                    }
+                                  ]
+                                }
+                              }
+                            ]
+                          }
+                        }
+                      ]
+                    }
+                  }
+                ]
+              }
+            }
+          ]
+        }
+      }
+    ]
+  }
+}
diff --git a/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/xcresult_output.json b/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/xcresult_output.json
new file mode 100644
index 0000000..bedddde
--- /dev/null
+++ b/benchmark/benchmark-darwin-gradle-plugin/src/test/test-data/xcresult_output.json
@@ -0,0 +1,502 @@
+{
+  "_type" : {
+    "_name" : "ActionsInvocationRecord"
+  },
+  "actions" : {
+    "_type" : {
+      "_name" : "Array"
+    },
+    "_values" : [
+      {
+        "_type" : {
+          "_name" : "ActionRecord"
+        },
+        "actionResult" : {
+          "_type" : {
+            "_name" : "ActionResult"
+          },
+          "coverage" : {
+            "_type" : {
+              "_name" : "CodeCoverageInfo"
+            }
+          },
+          "diagnosticsRef" : {
+            "_type" : {
+              "_name" : "Reference"
+            },
+            "id" : {
+              "_type" : {
+                "_name" : "String"
+              },
+              "_value" : "0~jAQXUmgyNkmcnoWDboB4OeaM0-whlsynWt0yZ3wo4KvsnpcYa713pAK1NtXSCTE8Uc0lFtZHoOCqewug4plRDQ=="
+            }
+          },
+          "issues" : {
+            "_type" : {
+              "_name" : "ResultIssueSummaries"
+            }
+          },
+          "logRef" : {
+            "_type" : {
+              "_name" : "Reference"
+            },
+            "id" : {
+              "_type" : {
+                "_name" : "String"
+              },
+              "_value" : "0~Qjtn8Ykt-p4Y2zYr6DbsVuygoZGAblK7Eph8ZFqa-w0iUTl09NihLHxd7DNfeeR1B0dwrjrN5Fvx_yZPpHvUjQ=="
+            },
+            "targetType" : {
+              "_type" : {
+                "_name" : "TypeDefinition"
+              },
+              "name" : {
+                "_type" : {
+                  "_name" : "String"
+                },
+                "_value" : "ActivityLogSection"
+              }
+            }
+          },
+          "metrics" : {
+            "_type" : {
+              "_name" : "ResultMetrics"
+            },
+            "testsCount" : {
+              "_type" : {
+                "_name" : "Int"
+              },
+              "_value" : "1"
+            }
+          },
+          "resultName" : {
+            "_type" : {
+              "_name" : "String"
+            },
+            "_value" : "action"
+          },
+          "status" : {
+            "_type" : {
+              "_name" : "String"
+            },
+            "_value" : "succeeded"
+          },
+          "testsRef" : {
+            "_type" : {
+              "_name" : "Reference"
+            },
+            "id" : {
+              "_type" : {
+                "_name" : "String"
+              },
+              "_value" : "0~CRExaYFNITauPqprkYOVFcX44BiMR7Y5SK7vYQTvwKNnaF1--St6QQlAOz693pVVLLQkXitHdwytuCOA_J3AmA=="
+            },
+            "targetType" : {
+              "_type" : {
+                "_name" : "TypeDefinition"
+              },
+              "name" : {
+                "_type" : {
+                  "_name" : "String"
+                },
+                "_value" : "ActionTestPlanRunSummaries"
+              }
+            }
+          }
+        },
+        "buildResult" : {
+          "_type" : {
+            "_name" : "ActionResult"
+          },
+          "coverage" : {
+            "_type" : {
+              "_name" : "CodeCoverageInfo"
+            }
+          },
+          "issues" : {
+            "_type" : {
+              "_name" : "ResultIssueSummaries"
+            }
+          },
+          "logRef" : {
+            "_type" : {
+              "_name" : "Reference"
+            },
+            "id" : {
+              "_type" : {
+                "_name" : "String"
+              },
+              "_value" : "0~o__Z27AtErlXE6_lRzQF5cFdk50BUQF_aPv-O8Qsdvi5HMz3kXluFX3_xOAAfqzm0-YWnbEyrw1wXekD7fMHEw=="
+            },
+            "targetType" : {
+              "_type" : {
+                "_name" : "TypeDefinition"
+              },
+              "name" : {
+                "_type" : {
+                  "_name" : "String"
+                },
+                "_value" : "ActivityLogSection"
+              }
+            }
+          },
+          "metrics" : {
+            "_type" : {
+              "_name" : "ResultMetrics"
+            }
+          },
+          "resultName" : {
+            "_type" : {
+              "_name" : "String"
+            },
+            "_value" : "build"
+          },
+          "status" : {
+            "_type" : {
+              "_name" : "String"
+            },
+            "_value" : "succeeded"
+          }
+        },
+        "endedTime" : {
+          "_type" : {
+            "_name" : "Date"
+          },
+          "_value" : "2022-09-20T15:39:30.709-0700"
+        },
+        "runDestination" : {
+          "_type" : {
+            "_name" : "ActionRunDestinationRecord"
+          },
+          "displayName" : {
+            "_type" : {
+              "_name" : "String"
+            },
+            "_value" : "iPhone 13"
+          },
+          "localComputerRecord" : {
+            "_type" : {
+              "_name" : "ActionDeviceRecord"
+            },
+            "busSpeedInMHz" : {
+              "_type" : {
+                "_name" : "Int"
+              },
+              "_value" : "0"
+            },
+            "cpuCount" : {
+              "_type" : {
+                "_name" : "Int"
+              },
+              "_value" : "1"
+            },
+            "cpuKind" : {
+              "_type" : {
+                "_name" : "String"
+              },
+              "_value" : "Apple M1 Pro"
+            },
+            "cpuSpeedInMHz" : {
+              "_type" : {
+                "_name" : "Int"
+              },
+              "_value" : "0"
+            },
+            "identifier" : {
+              "_type" : {
+                "_name" : "String"
+              },
+              "_value" : "00006000-000239D03CE2801E"
+            },
+            "isConcreteDevice" : {
+              "_type" : {
+                "_name" : "Bool"
+              },
+              "_value" : "true"
+            },
+            "logicalCPUCoresPerPackage" : {
+              "_type" : {
+                "_name" : "Int"
+              },
+              "_value" : "10"
+            },
+            "modelCode" : {
+              "_type" : {
+                "_name" : "String"
+              },
+              "_value" : "MacBookPro18,1"
+            },
+            "modelName" : {
+              "_type" : {
+                "_name" : "String"
+              },
+              "_value" : "MacBook Pro"
+            },
+            "modelUTI" : {
+              "_type" : {
+                "_name" : "String"
+              },
+              "_value" : "com.apple.macbookpro-16-2021"
+            },
+            "name" : {
+              "_type" : {
+                "_name" : "String"
+              },
+              "_value" : "My Mac"
+            },
+            "nativeArchitecture" : {
+              "_type" : {
+                "_name" : "String"
+              },
+              "_value" : "arm64e"
+            },
+            "operatingSystemVersion" : {
+              "_type" : {
+                "_name" : "String"
+              },
+              "_value" : "12.6"
+            },
+            "operatingSystemVersionWithBuildNumber" : {
+              "_type" : {
+                "_name" : "String"
+              },
+              "_value" : "12.6 (21G115)"
+            },
+            "physicalCPUCoresPerPackage" : {
+              "_type" : {
+                "_name" : "Int"
+              },
+              "_value" : "10"
+            },
+            "platformRecord" : {
+              "_type" : {
+                "_name" : "ActionPlatformRecord"
+              },
+              "identifier" : {
+                "_type" : {
+                  "_name" : "String"
+                },
+                "_value" : "com.apple.platform.macosx"
+              },
+              "userDescription" : {
+                "_type" : {
+                  "_name" : "String"
+                },
+                "_value" : "macOS"
+              }
+            },
+            "ramSizeInMegabytes" : {
+              "_type" : {
+                "_name" : "Int"
+              },
+              "_value" : "16384"
+            }
+          },
+          "targetArchitecture" : {
+            "_type" : {
+              "_name" : "String"
+            },
+            "_value" : "arm64"
+          },
+          "targetDeviceRecord" : {
+            "_type" : {
+              "_name" : "ActionDeviceRecord"
+            },
+            "busSpeedInMHz" : {
+              "_type" : {
+                "_name" : "Int"
+              },
+              "_value" : "0"
+            },
+            "cpuCount" : {
+              "_type" : {
+                "_name" : "Int"
+              },
+              "_value" : "0"
+            },
+            "cpuSpeedInMHz" : {
+              "_type" : {
+                "_name" : "Int"
+              },
+              "_value" : "0"
+            },
+            "identifier" : {
+              "_type" : {
+                "_name" : "String"
+              },
+              "_value" : "7F61C467-4E4A-437C-B6EF-026FEEF3904C"
+            },
+            "isConcreteDevice" : {
+              "_type" : {
+                "_name" : "Bool"
+              },
+              "_value" : "true"
+            },
+            "logicalCPUCoresPerPackage" : {
+              "_type" : {
+                "_name" : "Int"
+              },
+              "_value" : "0"
+            },
+            "modelCode" : {
+              "_type" : {
+                "_name" : "String"
+              },
+              "_value" : "iPhone14,5"
+            },
+            "modelName" : {
+              "_type" : {
+                "_name" : "String"
+              },
+              "_value" : "iPhone 13"
+            },
+            "modelUTI" : {
+              "_type" : {
+                "_name" : "String"
+              },
+              "_value" : "com.apple.iphone-13-1"
+            },
+            "name" : {
+              "_type" : {
+                "_name" : "String"
+              },
+              "_value" : "iPhone 13"
+            },
+            "nativeArchitecture" : {
+              "_type" : {
+                "_name" : "String"
+              },
+              "_value" : "arm64"
+            },
+            "operatingSystemVersion" : {
+              "_type" : {
+                "_name" : "String"
+              },
+              "_value" : "15.2"
+            },
+            "operatingSystemVersionWithBuildNumber" : {
+              "_type" : {
+                "_name" : "String"
+              },
+              "_value" : "15.2 (19C51)"
+            },
+            "physicalCPUCoresPerPackage" : {
+              "_type" : {
+                "_name" : "Int"
+              },
+              "_value" : "0"
+            },
+            "platformRecord" : {
+              "_type" : {
+                "_name" : "ActionPlatformRecord"
+              },
+              "identifier" : {
+                "_type" : {
+                  "_name" : "String"
+                },
+                "_value" : "com.apple.platform.iphonesimulator"
+              },
+              "userDescription" : {
+                "_type" : {
+                  "_name" : "String"
+                },
+                "_value" : "iOS Simulator"
+              }
+            },
+            "ramSizeInMegabytes" : {
+              "_type" : {
+                "_name" : "Int"
+              },
+              "_value" : "0"
+            }
+          },
+          "targetSDKRecord" : {
+            "_type" : {
+              "_name" : "ActionSDKRecord"
+            },
+            "identifier" : {
+              "_type" : {
+                "_name" : "String"
+              },
+              "_value" : "iphonesimulator15.2"
+            },
+            "name" : {
+              "_type" : {
+                "_name" : "String"
+              },
+              "_value" : "Simulator - iOS 15.2"
+            },
+            "operatingSystemVersion" : {
+              "_type" : {
+                "_name" : "String"
+              },
+              "_value" : "15.2"
+            }
+          }
+        },
+        "schemeCommandName" : {
+          "_type" : {
+            "_name" : "String"
+          },
+          "_value" : "Test"
+        },
+        "schemeTaskName" : {
+          "_type" : {
+            "_name" : "String"
+          },
+          "_value" : "BuildAndAction"
+        },
+        "startedTime" : {
+          "_type" : {
+            "_name" : "Date"
+          },
+          "_value" : "2022-09-20T15:38:15.930-0700"
+        },
+        "title" : {
+          "_type" : {
+            "_name" : "String"
+          },
+          "_value" : "Testing project benchmark-darwin-sample-xcode with scheme testapp-ios"
+        }
+      }
+    ]
+  },
+  "issues" : {
+    "_type" : {
+      "_name" : "ResultIssueSummaries"
+    }
+  },
+  "metadataRef" : {
+    "_type" : {
+      "_name" : "Reference"
+    },
+    "id" : {
+      "_type" : {
+        "_name" : "String"
+      },
+      "_value" : "0~9QqgSKuPd_WJ00r0MtQ53EH7hHP4lpwwOlDne6b7j-OjJ6jPH5Vm31modXkV3kE_g1-SD2HbuvM_J1hZSGH-mw=="
+    },
+    "targetType" : {
+      "_type" : {
+        "_name" : "TypeDefinition"
+      },
+      "name" : {
+        "_type" : {
+          "_name" : "String"
+        },
+        "_value" : "ActionsInvocationMetadata"
+      }
+    }
+  },
+  "metrics" : {
+    "_type" : {
+      "_name" : "ResultMetrics"
+    },
+    "testsCount" : {
+      "_type" : {
+        "_name" : "Int"
+      },
+      "_value" : "1"
+    }
+  }
+}
diff --git a/benchmark/benchmark-darwin-samples-xcode/xcodegen-project.yml b/benchmark/benchmark-darwin-samples-xcode/xcodegen-project.yml
index bece70f..b0e054a 100644
--- a/benchmark/benchmark-darwin-samples-xcode/xcodegen-project.yml
+++ b/benchmark/benchmark-darwin-samples-xcode/xcodegen-project.yml
@@ -1,6 +1,6 @@
 # XCodeGen for the :benchmark:benchmark-darwin-samples module..
 
-name: benchmark-darwin-sample-xcode
+name: benchmark-darwin-samples-xcode
 targets:
 
   testapp-ios:
@@ -13,19 +13,9 @@
     scheme:
       testTargets:
         - testapp-ios-benchmarks
-      preActions:
-        - name: build AndroidXDarwinSampleBenchmarks.xcframework
-          basedOnDependencyAnalysis: false
-          settingsTarget: testapp-ios
-          script: |
-            cd ${PROJECT_DIR}/../..
-            ANDROIDX_PROJECTS=KMP ./gradlew :benchmark:benchmark-darwin-samples:assembleAndroidXDarwinSampleBenchmarksReleaseXCFramework \
-                --no-configuration-cache < /dev/null
-          outputFiles:
-            - "${PROJECT_DIR}/../../../../out/androidx/benchmark/benchmark-darwin-samples/build/XCFrameworks/release/AndroidXDarwinSampleBenchmarks.xcframework"
       gatherCoverageData: false
     dependencies:
-      - framework: "${PROJECT_DIR}/../../../../out/androidx/benchmark/benchmark-darwin-samples/build/XCFrameworks/release/AndroidXDarwinSampleBenchmarks.xcframework"
+      - framework: "${PROJECT_DIR}/../../../../androidx/benchmark/benchmark-darwin-samples/build/XCFrameworks/release/AndroidXDarwinSampleBenchmarks.xcframework"
     settings:
       PRODUCT_NAME: testapp-ios
 
diff --git a/benchmark/benchmark-darwin-samples/build.gradle b/benchmark/benchmark-darwin-samples/build.gradle
index f31dd41..80282e3 100644
--- a/benchmark/benchmark-darwin-samples/build.gradle
+++ b/benchmark/benchmark-darwin-samples/build.gradle
@@ -3,6 +3,7 @@
 
 plugins {
     id("AndroidXPlugin")
+    id("androidx.benchmark.darwin")
 }
 
 androidXMultiplatform {
@@ -46,6 +47,17 @@
     }
 }
 
+darwinBenchmark {
+    xcodeGenConfigFile = project.rootProject.file(
+            "benchmark/benchmark-darwin-samples-xcode/xcodegen-project.yml"
+    )
+    xcodeProjectName = "benchmark-darwin-samples-xcode"
+    scheme = "testapp-ios"
+    // ios 13, 15.2
+    destination = "id=7F61C467-4E4A-437C-B6EF-026FEEF3904C"
+    xcFrameworkConfig = "AndroidXDarwinSampleBenchmarks"
+}
+
 androidx {
     name = "AndroidX Benchmarks - Darwin Samples"
     mavenGroup = LibraryGroups.BENCHMARK
diff --git a/buildSrc/public/build.gradle b/buildSrc/public/build.gradle
index f98bc4c..e37e156 100644
--- a/buildSrc/public/build.gradle
+++ b/buildSrc/public/build.gradle
@@ -4,6 +4,9 @@
     main.java.srcDirs += "${supportRootFolder}/benchmark/gradle-plugin/src/main/kotlin"
     main.resources.srcDirs += "${supportRootFolder}/benchmark/gradle-plugin/src/main/resources"
 
+    main.java.srcDirs += "${supportRootFolder}/benchmark/benchmark-darwin-gradle-plugin/src/main/kotlin"
+    main.resources.srcDirs += "${supportRootFolder}/benchmark/benchmark-darwin-gradle-plugin/src/main/resources"
+
     main.java.srcDirs += "${supportRootFolder}/inspection/inspection-gradle-plugin/src/main/kotlin"
     main.resources.srcDirs += "${supportRootFolder}/inspection/inspection-gradle-plugin/src/main" +
             "/resources"
@@ -15,6 +18,11 @@
             "src/main/kotlin"
 }
 
+dependencies {
+    // This is for androidx.benchmark.darwin
+    implementation(libs.apacheCommonsMath)
+}
+
 gradlePlugin {
     plugins {
         benchmark {
@@ -25,5 +33,9 @@
             id = "androidx.inspection"
             implementationClass = "androidx.inspection.gradle.InspectionPlugin"
         }
+        darwinBenchmark {
+            id = "androidx.benchmark.darwin"
+            implementationClass = "androidx.benchmark.darwin.gradle.DarwinBenchmarkPlugin"
+        }
     }
 }
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 6bef6a7..5fd4854 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -82,6 +82,7 @@
 apacheAnt = { module = "org.apache.ant:ant", version = "1.10.11" }
 apacheCommonsCodec = { module = "commons-codec:commons-codec", version = "1.15" }
 apacheCommonIo = { module = "commons-io:commons-io", version = "2.4" }
+apacheCommonsMath = { module = "org.apache.commons:commons-math3", version = "3.6.1" }
 assertj = { module = "org.assertj:assertj-core", version = "3.23.1" }
 asm = { module = "org.ow2.asm:asm", version.ref = "asm"}
 asmCommons = { module = "org.ow2.asm:asm-commons", version.ref = "asm"}
diff --git a/gradle/verification-keyring.keys b/gradle/verification-keyring.keys
index 6334dd7..787b19f 100644
--- a/gradle/verification-keyring.keys
+++ b/gradle/verification-keyring.keys
@@ -4496,6 +4496,42 @@
 -----END PGP PUBLIC KEY BLOCK-----
 
 
+pub    64A16FAAEC16A4BE
+uid    Evan Ward <evanward@apache.org>
+
+sub    1E8F1D57A4450BCB
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: BCPG v1.68
+
+mQENBFbqsT0BCADwERe1Rc9qNWwXOvwZHsjauVDy0TpqNVY8I3S+OYm4rX1dkjyh
++6bTEH1ys6bKevvR+PLhYzTGKboHnMT0RIINY/DQQSzHr/GRyCiiRlRvULbt9Fnz
+kJJDgH2BcbNSmWJlrCqYk+E3GAyQial+szkEZED+02wXHsbs0z3vozjQGHy0RVOj
+Gc5Shwk7Hr/F3vw9EQKa1nNffWmcGEx9B+WcC9ALPVd/fpQVXvlqfbi+kaIbqv2x
+NHQr7BL8j3SpN6vhfZM/3zeghlxQ5HYWER983XwkmvbNdMxt5HWsMKWZ0utt4ocK
+TnQP8NFGlPWEQhPvRRFNb9BI0wvGD0NUb1gjABEBAAG0H0V2YW4gV2FyZCA8ZXZh
+bndhcmRAYXBhY2hlLm9yZz6JATkEEwECACMFAlbqsT0CGwMHCwkIBwMCAQYVCAIJ
+CgsEFgIDAQIeAQIXgAAKCRBkoW+q7BakvnygB/41oiYgfDqkG5srQ4nC7jE0Pe5V
+MnuLVHqsfJBGPvt2tz5+Z1ciIFFwUi/xsafX5DhC+FVOOGdeEnkKnskPBOI7uMFh
+v/s90lbhNV62LfwcS9hptE4qn0JTg7mYiiL0Zue99mlkeP105+GlMmvH5q54X2Le
+hIDBVR8DehL8ZqZCvNEVK1ftpx45mvF/4yh0YK0oVuCAAzwF9+6OxeWTCUTRHTZC
+4CWjtXKUHMq4nTRSp0wGdqd5UV0VbMn0bKTkhgRNCJAKyFw6lJ0FZWwmuG28T0s+
+bKuRAJHTAZmSM7UmBnKo22t9whNcozcqxWhK1lcS4OWEArXpCKxAx4kXmbwnuQEN
+BFbqsT0BCADj5dHK9K27rmkFscDY6x53w4L/X6AYKmVu7Qol7VhR+1WtZXgxZaLM
+xDxj5RK4sNOIqh6R3vEMlAVS+iYbzahI/A0fcSciCoLCgjJKCR3STnTu2k0D/MO0
+la+wF/bGPa0UADGIJLRCjalkl5uv4c7zZbyLnRl8a9XSc81cp2WJTxafZJlJkFOU
+4cyewwFuH0pwMvc9Wmwhkh5IeBF6w8Asj77M5bzJINXYxtKMGYA506609HrvN0+r
+obfgx4Aqy4hGKsqXMsSZiuPDvbdtH3gIRV8NPdYRq+dQg/gv222Y3G1xprDVbl1A
+1CCHlaUqT2lIFPovjoB2O2SBeX9xKRJzABEBAAGJAR8EGAECAAkFAlbqsT0CGwwA
+CgkQZKFvquwWpL4yawf/WDI4VqLkR9RqaX3am/kS8481pZPWZUlCCL7jONB7X7eG
+Bit/FjmQWzfL5nWAEB5qhm2qqCgvgtPmVxCrQLECVmaGmDFmhGIFh8TQsYvQJPK6
+HZDxZj97lUKsG/ojOY4ZArvZnsXBU6C963QUZF+P5UL52n1pE/ByMV1R3enEfrYI
+X+wZslOx5uRFOR8dgUpG/ohh2vkFCaKD6KJQHm6C5lGBgUNqGMFxp1nknKJaNqYq
+jvippm6KcocWARfTHx6Xm3mBqxigmpsalUKAGpjcsxsIEY6jnnN/5i5y1XeokTY8
+6fqEt2OSFSkWiApqq6lxMRluTiq33bSquTxSomKfQQ==
+=PImz
+-----END PGP PUBLIC KEY BLOCK-----
+
+
 pub    6525FD70CC303655
 uid    Stephane Nicoll <snicoll@apache.org>
 
diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml
index 805dbeb..f5b29b5 100644
--- a/gradle/verification-metadata.xml
+++ b/gradle/verification-metadata.xml
@@ -146,6 +146,7 @@
          <trusted-key id="3f36885c24df4b75" group="org.mozilla"/>
          <trusted-key id="3faad2cd5ecbb314" group="org.apache.commons"/>
          <trusted-key id="41321490758aad6f" group="org.codehaus.groovy"/>
+         <trusted-key id="41a1a08c62fca78b79d3081164a16faaec16a4be" group="org.apache.commons" name="commons-math3"/>
          <trusted-key id="41cd49b4ef5876f9e9f691dabac30622339994c4">
             <trusting group="com.google.testing.compile"/>
             <trusting group="com.google.truth"/>
diff --git a/settings.gradle b/settings.gradle
index fbc4a7c..331f5c6 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -384,6 +384,7 @@
 includeProject(":benchmark:benchmark-darwin", [BuildType.KMP])
 includeProject(":benchmark:benchmark-darwin-core", [BuildType.KMP])
 includeProject(":benchmark:benchmark-darwin-samples", [BuildType.KMP])
+includeProject(":benchmark:benchmark-darwin-gradle-plugin", [BuildType.KMP])
 includeProject(":benchmark:benchmark-gradle-plugin", "benchmark/gradle-plugin", [BuildType.MAIN])
 includeProject(":benchmark:benchmark-junit4")
 includeProject(":benchmark:benchmark-macro", [BuildType.MAIN, BuildType.COMPOSE])