diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 36dba7a6d..5b5859c6c 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -25,6 +25,8 @@ updates: - '/test/android' schedule: interval: 'weekly' + ignore: + - dependency-name: 'io.github.vvb2060.ndk:curl' groups: dependencies: patterns: diff --git a/.gitmodules b/.gitmodules index c878230b9..9876bf671 100644 --- a/.gitmodules +++ b/.gitmodules @@ -34,9 +34,6 @@ [submodule "vendor/cpp-httplib"] path = vendor/cpp-httplib url = https://github.com/yhirose/cpp-httplib.git -[submodule "platform/android/vendor/mapbox-gestures-android"] - path = platform/android/vendor/mapbox-gestures-android - url = https://github.com/mapbox/mapbox-gestures-android.git [submodule "docs/doxygen/doxygen-awesome-css"] path = docs/doxygen/doxygen-awesome-css url = https://github.com/jothepro/doxygen-awesome-css.git diff --git a/platform/android/Makefile b/platform/android/Makefile index 1f3f0f3d8..8e77ea063 100644 --- a/platform/android/Makefile +++ b/platform/android/Makefile @@ -277,14 +277,9 @@ android-license: .PHONY: android-ndk-stack android-ndk-stack: android-ndk-stack-arm-v7 -# Updates Android's vendor submodules -.PHONY: android-update-vendor -android-update-vendor: - $(MLN_ANDROID_GRADLE) -Pmaplibre.abis=none updateVendorSubmodules - # Run android nitpick script .PHONY: run-android-nitpick -run-android-nitpick: android-update-vendor +run-android-nitpick: $(MLN_ANDROID_GRADLE) -Pmaplibre.abis=none androidNitpick # Creates a dependency graph using Graphviz diff --git a/platform/android/MapLibreAndroid/build.gradle b/platform/android/MapLibreAndroid/build.gradle index b90373113..95701d360 100644 --- a/platform/android/MapLibreAndroid/build.gradle +++ b/platform/android/MapLibreAndroid/build.gradle @@ -13,7 +13,7 @@ apply from: "${rootDir}/gradle/native-build.gradle" dependencies { lintChecks project(":MapLibreAndroidLint") api libs.maplibreJavaGeoJSON - api libs.mapboxAndroidGestures + api libs.maplibreGestures implementation libs.maplibreJavaTurf implementation libs.supportAnnotations @@ -123,19 +123,15 @@ android { warningsAsErrors false } - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - - kotlinOptions { - jvmTarget = "1.8" - } buildFeatures { buildConfig true } } +kotlin { + jvmToolchain(17) +} + licenseReport { generateHtmlReport = false generateJsonReport = true @@ -157,7 +153,6 @@ configurations { apply from: "${rootDir}/gradle/gradle-checkstyle.gradle" apply from: "${rootDir}/gradle/gradle-dependencies-graph.gradle" -apply from: "${rootDir}/gradle/gradle-update-vendor-modules.gradle" apply from: "${rootDir}/gradle/android-nitpick.gradle" apply from: "${rootDir}/gradle/gradle-publish.gradle" // intentionally disabled diff --git a/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/location/LocationCameraController.java b/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/location/LocationCameraController.java index 3bd85ed68..b34b4e158 100644 --- a/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/location/LocationCameraController.java +++ b/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/location/LocationCameraController.java @@ -10,9 +10,9 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; -import com.mapbox.android.gestures.AndroidGesturesManager; -import com.mapbox.android.gestures.MoveGestureDetector; -import com.mapbox.android.gestures.RotateGestureDetector; +import org.maplibre.android.gestures.AndroidGesturesManager; +import org.maplibre.android.gestures.MoveGestureDetector; +import org.maplibre.android.gestures.RotateGestureDetector; import org.maplibre.android.location.modes.CameraMode; import org.maplibre.android.maps.MapLibreMap; import org.maplibre.android.maps.Transform; diff --git a/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/location/LocationComponentOptions.java b/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/location/LocationComponentOptions.java index 16867fd0b..83b6db8a2 100644 --- a/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/location/LocationComponentOptions.java +++ b/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/location/LocationComponentOptions.java @@ -15,7 +15,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.StyleRes; -import com.mapbox.android.gestures.AndroidGesturesManager; +import org.maplibre.android.gestures.AndroidGesturesManager; import org.maplibre.android.R; import org.maplibre.android.maps.MapLibreMap; @@ -730,7 +730,7 @@ public class LocationComponentOptions implements Parcelable { * like zooming in and out, without breaking tracking. *
* Note: If set to true, this can overwrite some of the gesture thresholds - * and the custom {@link com.mapbox.android.gestures.AndroidGesturesManager} that was set with + * and the custom {@link org.maplibre.android.gestures.AndroidGesturesManager} that was set with * {@link MapLibreMap#setGesturesManager(AndroidGesturesManager, boolean, boolean)}. * * @return true if gestures are adjusted when in one of the camera tracking modes, false otherwise @@ -1784,7 +1784,7 @@ public class LocationComponentOptions implements Parcelable { * like zooming in and out, without breaking tracking. *
* Note: This can overwrite some of the gesture thresholds
- * and the custom {@link com.mapbox.android.gestures.AndroidGesturesManager} that was set with
+ * and the custom {@link org.maplibre.android.gestures.AndroidGesturesManager} that was set with
* {@link MapLibreMap#setGesturesManager(AndroidGesturesManager, boolean, boolean)}.
*
* @param trackingGesturesManagement true if gestures should be adjusted when in one of the camera tracking modes,
diff --git a/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/maps/MapGestureDetector.java b/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/maps/MapGestureDetector.java
index 91cdec250..af36ccbd2 100644
--- a/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/maps/MapGestureDetector.java
+++ b/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/maps/MapGestureDetector.java
@@ -15,13 +15,13 @@ import android.view.animation.DecelerateInterpolator;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import com.mapbox.android.gestures.AndroidGesturesManager;
-import com.mapbox.android.gestures.MoveGestureDetector;
-import com.mapbox.android.gestures.MultiFingerTapGestureDetector;
-import com.mapbox.android.gestures.RotateGestureDetector;
-import com.mapbox.android.gestures.ShoveGestureDetector;
-import com.mapbox.android.gestures.StandardGestureDetector;
-import com.mapbox.android.gestures.StandardScaleGestureDetector;
+import org.maplibre.android.gestures.AndroidGesturesManager;
+import org.maplibre.android.gestures.MoveGestureDetector;
+import org.maplibre.android.gestures.MultiFingerTapGestureDetector;
+import org.maplibre.android.gestures.RotateGestureDetector;
+import org.maplibre.android.gestures.ShoveGestureDetector;
+import org.maplibre.android.gestures.StandardGestureDetector;
+import org.maplibre.android.gestures.StandardScaleGestureDetector;
import org.maplibre.android.R;
import org.maplibre.android.constants.MapLibreConstants;
import org.maplibre.android.log.Logger;
@@ -124,7 +124,7 @@ final class MapGestureDetector {
if (attachDefaultListeners) {
StandardGestureListener standardGestureListener = new StandardGestureListener(
context.getResources().getDimension(
- com.mapbox.android.gestures.R.dimen.mapbox_defaultScaleSpanSinceStartThreshold));
+ org.maplibre.android.gestures.R.dimen.mapbox_defaultScaleSpanSinceStartThreshold));
MoveGestureListener moveGestureListener = new MoveGestureListener();
ScaleGestureListener scaleGestureListener = new ScaleGestureListener(
context.getResources().getDimension(R.dimen.maplibre_density_constant),
@@ -138,7 +138,7 @@ final class MapGestureDetector {
context.getResources().getDimension(R.dimen.maplibre_angular_velocity_multiplier),
context.getResources().getDimension(R.dimen.maplibre_minimum_angular_velocity),
context.getResources().getDimension(
- com.mapbox.android.gestures.R.dimen.mapbox_defaultScaleSpanSinceStartThreshold));
+ org.maplibre.android.gestures.R.dimen.mapbox_defaultScaleSpanSinceStartThreshold));
ShoveGestureListener shoveGestureListener = new ShoveGestureListener();
TapGestureListener tapGestureListener = new TapGestureListener();
diff --git a/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/maps/MapLibreMap.java b/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/maps/MapLibreMap.java
index 55bde01c7..ecc80b707 100644
--- a/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/maps/MapLibreMap.java
+++ b/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/maps/MapLibreMap.java
@@ -15,11 +15,11 @@ import androidx.annotation.Nullable;
import androidx.annotation.Size;
import androidx.annotation.UiThread;
-import com.mapbox.android.gestures.AndroidGesturesManager;
-import com.mapbox.android.gestures.MoveGestureDetector;
-import com.mapbox.android.gestures.RotateGestureDetector;
-import com.mapbox.android.gestures.ShoveGestureDetector;
-import com.mapbox.android.gestures.StandardScaleGestureDetector;
+import org.maplibre.android.gestures.AndroidGesturesManager;
+import org.maplibre.android.gestures.MoveGestureDetector;
+import org.maplibre.android.gestures.RotateGestureDetector;
+import org.maplibre.android.gestures.ShoveGestureDetector;
+import org.maplibre.android.gestures.StandardScaleGestureDetector;
import org.maplibre.geojson.Feature;
import org.maplibre.geojson.Geometry;
import org.maplibre.android.MapStrictMode;
diff --git a/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/maps/MapView.java b/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/maps/MapView.java
index 1537bc9fb..58b883fda 100644
--- a/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/maps/MapView.java
+++ b/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/maps/MapView.java
@@ -20,7 +20,7 @@ import androidx.annotation.UiThread;
import androidx.collection.LongSparseArray;
import timber.log.Timber;
-import com.mapbox.android.gestures.AndroidGesturesManager;
+import org.maplibre.android.gestures.AndroidGesturesManager;
import org.maplibre.android.MapStrictMode;
import org.maplibre.android.MapLibre;
import org.maplibre.android.R;
diff --git a/platform/android/MapLibreAndroid/src/test/java/org/maplibre/android/location/LocationCameraControllerTest.kt b/platform/android/MapLibreAndroid/src/test/java/org/maplibre/android/location/LocationCameraControllerTest.kt
index 9e28d9c70..f675f01d1 100644
--- a/platform/android/MapLibreAndroid/src/test/java/org/maplibre/android/location/LocationCameraControllerTest.kt
+++ b/platform/android/MapLibreAndroid/src/test/java/org/maplibre/android/location/LocationCameraControllerTest.kt
@@ -3,8 +3,8 @@ package org.maplibre.android.location
import android.graphics.PointF
import android.graphics.RectF
import android.location.Location
-import com.mapbox.android.gestures.AndroidGesturesManager
-import com.mapbox.android.gestures.MoveGestureDetector
+import org.maplibre.android.gestures.AndroidGesturesManager
+import org.maplibre.android.gestures.MoveGestureDetector
import org.maplibre.android.camera.CameraPosition
import org.maplibre.android.camera.CameraUpdate
import org.maplibre.android.camera.CameraUpdateFactory.newCameraPosition
diff --git a/platform/android/MapLibreAndroid/src/test/java/org/maplibre/android/maps/MapTouchListenersTest.kt b/platform/android/MapLibreAndroid/src/test/java/org/maplibre/android/maps/MapTouchListenersTest.kt
index 7f67df839..c6b9be584 100644
--- a/platform/android/MapLibreAndroid/src/test/java/org/maplibre/android/maps/MapTouchListenersTest.kt
+++ b/platform/android/MapLibreAndroid/src/test/java/org/maplibre/android/maps/MapTouchListenersTest.kt
@@ -1,10 +1,10 @@
package org.maplibre.android.maps
import android.graphics.PointF
-import com.mapbox.android.gestures.MoveGestureDetector
-import com.mapbox.android.gestures.RotateGestureDetector
-import com.mapbox.android.gestures.ShoveGestureDetector
-import com.mapbox.android.gestures.StandardScaleGestureDetector
+import org.maplibre.android.gestures.MoveGestureDetector
+import org.maplibre.android.gestures.RotateGestureDetector
+import org.maplibre.android.gestures.ShoveGestureDetector
+import org.maplibre.android.gestures.StandardScaleGestureDetector
import org.maplibre.android.geometry.LatLng
import org.maplibre.android.maps.MapLibreMap.*
import org.junit.Before
diff --git a/platform/android/MapLibreAndroidLint/build.gradle b/platform/android/MapLibreAndroidLint/build.gradle
index 98bcd08e4..dbddbcd5f 100644
--- a/platform/android/MapLibreAndroidLint/build.gradle
+++ b/platform/android/MapLibreAndroidLint/build.gradle
@@ -18,14 +18,6 @@ dependencies {
testImplementation libs.lintTests
}
-compileKotlin {
- kotlinOptions {
- jvmTarget = "17"
- }
-}
-
-compileTestKotlin {
- kotlinOptions {
- jvmTarget = "17"
- }
-}
+kotlin {
+ jvmToolchain(17)
+}
\ No newline at end of file
diff --git a/platform/android/MapLibreAndroidTestApp/build.gradle b/platform/android/MapLibreAndroidTestApp/build.gradle
index 550cd2890..28bace323 100644
--- a/platform/android/MapLibreAndroidTestApp/build.gradle
+++ b/platform/android/MapLibreAndroidTestApp/build.gradle
@@ -29,15 +29,6 @@ android {
multiDexEnabled = true
}
- compileOptions {
- sourceCompatibility JavaVersion.VERSION_1_8
- targetCompatibility JavaVersion.VERSION_1_8
- }
-
- kotlinOptions {
- jvmTarget = "1.8"
- }
-
nativeBuild(["example-custom-layer"])
packagingOptions {
resources {
@@ -92,11 +83,14 @@ android {
}
}
+kotlin {
+ jvmToolchain(17)
+}
+
dependencies {
implementation project(':MapLibreAndroid')
implementation libs.maplibreJavaTurf
- implementation libs.supportAppcompatV7
implementation libs.supportRecyclerView
implementation libs.supportDesign
implementation libs.supportConstraintLayout
@@ -108,6 +102,9 @@ dependencies {
implementation libs.okhttp3
+ implementation libs.kotlinxCoroutinesCore
+ implementation libs.kotlinxCoroutinesAndroid
+
debugImplementation libs.leakCanary
androidTestImplementation libs.supportAnnotations
diff --git a/platform/android/MapLibreAndroidTestApp/src/main/java/org/maplibre/android/testapp/activity/camera/GestureDetectorActivity.kt b/platform/android/MapLibreAndroidTestApp/src/main/java/org/maplibre/android/testapp/activity/camera/GestureDetectorActivity.kt
index 14648af0e..86d94ba1a 100644
--- a/platform/android/MapLibreAndroidTestApp/src/main/java/org/maplibre/android/testapp/activity/camera/GestureDetectorActivity.kt
+++ b/platform/android/MapLibreAndroidTestApp/src/main/java/org/maplibre/android/testapp/activity/camera/GestureDetectorActivity.kt
@@ -17,11 +17,11 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
-import com.mapbox.android.gestures.AndroidGesturesManager
-import com.mapbox.android.gestures.MoveGestureDetector
-import com.mapbox.android.gestures.RotateGestureDetector
-import com.mapbox.android.gestures.ShoveGestureDetector
-import com.mapbox.android.gestures.StandardScaleGestureDetector
+import org.maplibre.android.gestures.AndroidGesturesManager
+import org.maplibre.android.gestures.MoveGestureDetector
+import org.maplibre.android.gestures.RotateGestureDetector
+import org.maplibre.android.gestures.ShoveGestureDetector
+import org.maplibre.android.gestures.StandardScaleGestureDetector
import org.maplibre.android.annotations.Marker
import org.maplibre.android.annotations.MarkerOptions
import org.maplibre.android.camera.CameraUpdateFactory
diff --git a/platform/android/MapLibreAndroidTestApp/src/main/java/org/maplibre/android/testapp/activity/style/DraggableMarkerActivity.kt b/platform/android/MapLibreAndroidTestApp/src/main/java/org/maplibre/android/testapp/activity/style/DraggableMarkerActivity.kt
index 340e75b8c..de24495f3 100644
--- a/platform/android/MapLibreAndroidTestApp/src/main/java/org/maplibre/android/testapp/activity/style/DraggableMarkerActivity.kt
+++ b/platform/android/MapLibreAndroidTestApp/src/main/java/org/maplibre/android/testapp/activity/style/DraggableMarkerActivity.kt
@@ -7,8 +7,8 @@ import android.view.MotionEvent
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.snackbar.Snackbar
-import com.mapbox.android.gestures.AndroidGesturesManager
-import com.mapbox.android.gestures.MoveGestureDetector
+import org.maplibre.android.gestures.AndroidGesturesManager
+import org.maplibre.android.gestures.MoveGestureDetector
import org.maplibre.geojson.Feature
import org.maplibre.geojson.FeatureCollection
import org.maplibre.geojson.Point
diff --git a/platform/android/gradle/gradle-update-vendor-modules.gradle b/platform/android/gradle/gradle-update-vendor-modules.gradle
deleted file mode 100644
index e215450ec..000000000
--- a/platform/android/gradle/gradle-update-vendor-modules.gradle
+++ /dev/null
@@ -1,8 +0,0 @@
-task updateVendorSubmodules {
- doLast {
- exec {
- workingDir = "${rootDir}"
- commandLine "git", "submodule", "update", "--init", "vendor"
- }
- }
-}
\ No newline at end of file
diff --git a/platform/android/gradle/libs.versions.toml b/platform/android/gradle/libs.versions.toml
index b818f0138..66606f38d 100644
--- a/platform/android/gradle/libs.versions.toml
+++ b/platform/android/gradle/libs.versions.toml
@@ -1,6 +1,6 @@
[versions]
mapLibreServices = "6.0.1"
-mapboxGestures = "0.7.0"
+maplibreGestures = "0.0.1"
appCompat = "1.7.0"
constraintLayout = "2.1.4"
interpolator = "1.0.0"
@@ -11,7 +11,7 @@ espresso = "3.6.1"
testRunner = "1.2.1"
testRules = "1.6.1"
fragment = "1.8.2"
-annotation = "1.8.1"
+annotation = "1.8.2"
leakCanary = "2.14"
junit = "4.13.2"
mockito = "5.12.0"
@@ -19,9 +19,9 @@ mockk = "1.13.12"
robolectric = "4.13"
timber = "5.0.1"
okhttp = "4.12.0"
-kotlin = "2.0.0"
+kotlin = "2.0.10"
licenses = "0.9.8"
-lint = "31.5.1"
+lint = "31.5.2"
appcenter = "1.5"
commonsIO = "2.16.1"
assertj = "3.26.3"
@@ -31,18 +31,19 @@ multidex = "2.0.1"
androidxTestExtJUnit = "1.1.5"
androidxTestCoreKtx = "1.5.0"
kotlinxSerializationJson = "1.6.0"
-kotlinxCoroutinesTest = "1.6.4"
+kotlinxCoroutines = { strictly = "[1.0, 2.0[" }
-androidGradlePlugin = "8.5.1"
+androidGradlePlugin = "8.5.2"
[libraries]
-kotlinxCoroutinesTest = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "kotlinxCoroutinesTest" }
+kotlinxCoroutinesCore = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinxCoroutines" }
+kotlinxCoroutinesTest = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "kotlinxCoroutines" }
+kotlinxCoroutinesAndroid = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "kotlinxCoroutines" }
maplibreJavaGeoJSON = { group = "org.maplibre.gl", name = "android-sdk-geojson", version.ref = "mapLibreServices" }
-mapboxAndroidGestures = { group = "com.mapbox.mapboxsdk", name = "mapbox-android-gestures", version.ref = "mapboxGestures" }
+maplibreGestures = { group = "org.maplibre.gl", name = "maplibre-android-gestures", version.ref = "maplibreGestures" }
maplibreJavaTurf = { group = "org.maplibre.gl", name = "android-sdk-turf", version.ref = "mapLibreServices" }
junit = { group = "junit", name = "junit", version.ref = "junit" }
-amdroidxJunit = { group = "androidx.test.ext", name = "junit", version.ref = "testRunner" }
mockito = { group = "org.mockito", name = "mockito-core", version.ref = "mockito" }
mockk = { group = "io.mockk", name = "mockk", version.ref = "mockk" }
robolectric = { group = "org.robolectric", name = "robolectric", version.ref = "robolectric" }
@@ -55,7 +56,6 @@ testEspressoIntents = { group = "androidx.test.espresso", name = "espresso-inten
testEspressoContrib = { group = "androidx.test.espresso", name = "espresso-contrib", version.ref = "espresso" }
testUiAutomator = { group = "androidx.test.uiautomator", name = "uiautomator", version.ref = "uiAutomator" }
supportAnnotations = { group = "androidx.annotation", name = "annotation", version.ref = "annotation" }
-supportAppcompatV7 = { group = "androidx.appcompat", name = "appcompat", version.ref = "appCompat" }
supportFragmentV4 = { group = "androidx.fragment", name = "fragment", version.ref = "fragment" }
supportRecyclerView = { group = "androidx.recyclerview", name = "recyclerview", version.ref = "recyclerView" }
supportConstraintLayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintLayout" }
@@ -89,6 +89,6 @@ androidGradlePlugin = { group = "com.android.tools.build", name = "gradle", vers
[plugins]
nexusPublishPlugin = { id = "io.github.gradle-nexus.publish-plugin", version = "2.0.0" }
kotlinter = { id = "org.jmailen.kotlinter", version = "4.4.1" }
-kotlinAndroid = { id = "org.jetbrains.kotlin.android", version = "2.0.0" }
+kotlinAndroid = { id = "org.jetbrains.kotlin.android", version = "2.0.10" }
dokka = { id = "org.jetbrains.dokka", version = "1.9.20" }
-kotlinPluginSerialization = { id = "org.jetbrains.kotlin.plugin.serialization", version = "2.0.0" }
\ No newline at end of file
+kotlinPluginSerialization = { id = "org.jetbrains.kotlin.plugin.serialization", version = "2.0.10" }
\ No newline at end of file
diff --git a/platform/android/src/test/render_test_runner.cpp b/platform/android/src/test/render_test_runner.cpp
index 9f7173c66..eb1c3e6dd 100644
--- a/platform/android/src/test/render_test_runner.cpp
+++ b/platform/android/src/test/render_test_runner.cpp
@@ -35,7 +35,10 @@ void android_main(struct android_app* app) {
int finishedTestCount = 0;
std::function