I am creating junit tests for my android application network layer and intermittently i see the following message in my test logs
System.logW: A resource was acquired at attached stack trace but never released. See java.io.Closeable for information on avoiding resource leaks.
java.lang.Throwable: Explicit termination method 'SQLiteConnection.close' not called
at dalvik.system.CloseGuard.$$robo$$dalvik_system_CloseGuard$openWithCallSite(CloseGuard.java:288)
at dalvik.system.CloseGuard.openWithCallSite(CloseGuard.java)
at dalvik.system.CloseGuard.$$robo$$dalvik_system_CloseGuard$open(CloseGuard.java:257)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.base/java.lang.reflect.Method.invoke(Unknown Source)
at org.robolectric.shadows.ShadowCloseGuard$CloseGuardReflector$$Reflector289.open(Unknown Source)
at org.robolectric.shadows.ShadowCloseGuard.open(ShadowCloseGuard.java:38)
at dalvik.system.CloseGuard.open(CloseGuard.java)
at android.database.sqlite.SQLiteConnection.$$robo$$android_database_sqlite_SQLiteConnection$__constructor__(SQLiteConnection.java:182)
at android.database.sqlite.SQLiteConnection.<init>(SQLiteConnection.java)
at android.database.sqlite.SQLiteConnection.$$robo$$android_database_sqlite_SQLiteConnection$open(SQLiteConnection.java:202)
at android.database.sqlite.SQLiteConnection.open(SQLiteConnection.java)
at android.database.sqlite.SQLiteConnectionPool.$$robo$$android_database_sqlite_SQLiteConnectionPool$openConnectionLocked(SQLiteConnectionPool.java:512)
at android.database.sqlite.SQLiteConnectionPool.openConnectionLocked(SQLiteConnectionPool.java)
at android.database.sqlite.SQLiteConnectionPool.$$robo$$android_database_sqlite_SQLiteConnectionPool$open(SQLiteConnectionPool.java:210)
at android.database.sqlite.SQLiteConnectionPool.open(SQLiteConnectionPool.java)
at android.database.sqlite.SQLiteConnectionPool.$$robo$$android_database_sqlite_SQLiteConnectionPool$open(SQLiteConnectionPool.java:202)
at android.database.sqlite.SQLiteConnectionPool.open(SQLiteConnectionPool.java)
at android.database.sqlite.SQLiteDatabase.$$robo$$android_database_sqlite_SQLiteDatabase$openInner(SQLiteDatabase.java:1085)
at android.database.sqlite.SQLiteDatabase.openInner(SQLiteDatabase.java)
at android.database.sqlite.SQLiteDatabase.$$robo$$android_database_sqlite_SQLiteDatabase$open(SQLiteDatabase.java:1065)
at android.database.sqlite.SQLiteDatabase.open(SQLiteDatabase.java)
at android.database.sqlite.SQLiteDatabase.$$robo$$android_database_sqlite_SQLiteDatabase$openDatabase(SQLiteDatabase.java:929)
at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java)
at android.database.sqlite.SQLiteDatabase.$$robo$$android_database_sqlite_SQLiteDatabase$openDatabase(SQLiteDatabase.java:918)
at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java)
at android.database.sqlite.SQLiteOpenHelper.$$robo$$android_database_sqlite_SQLiteOpenHelper$getDatabaseLocked(SQLiteOpenHelper.java:373)
at android.database.sqlite.SQLiteOpenHelper.getDatabaseLocked(SQLiteOpenHelper.java)
at android.database.sqlite.SQLiteOpenHelper.$$robo$$android_database_sqlite_SQLiteOpenHelper$getWritableDatabase(SQLiteOpenHelper.java:316)
at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java)
at com.google.android.datatransport.runtime.scheduling.persistence.SQLiteEventStore.retryIfDbLocked(SQLiteEventStore.java:582)
at com.google.android.datatransport.runtime.scheduling.persistence.SQLiteEventStore.getDb(SQLiteEventStore.java:95)
at com.google.android.datatransport.runtime.scheduling.persistence.SQLiteEventStore.runCriticalSection(SQLiteEventStore.java:765)
at com.google.android.datatransport.runtime.scheduling.jobscheduling.WorkInitializer.lambda$ensureContextsScheduled$1(WorkInitializer.java:54)
at com.google.android.datatransport.runtime.SafeLoggingExecutor$SafeLoggingRunnable.run(SafeLoggingExecutor.java:47)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.base/java.lang.Thread.run(Unknown Source)
and in addition i am seeing this message
ERROR: Failed to destroy temp directory
java.nio.file.DirectoryNotEmptyException: /var/folders/pr/pzzm0m_17273s8dcc2fw7b6w0000gn/T/robolectric-nativeruntime16490046703417862469/fonts
at java.base/sun.nio.fs.UnixFileSystemProvider.implDelete(Unknown Source)
at java.base/sun.nio.fs.AbstractFileSystemProvider.delete(Unknown Source)
at java.base/java.nio.file.Files.delete(Unknown Source)
at org.robolectric.util.TempDirectory$1.postVisitDirectory(TempDirectory.java:129)
at org.robolectric.util.TempDirectory$1.postVisitDirectory(TempDirectory.java:119)
at java.base/java.nio.file.Files.walkFileTree(Unknown Source)
at java.base/java.nio.file.Files.walkFileTree(Unknown Source)
at org.robolectric.util.TempDirectory.clearDirectory(TempDirectory.java:119)
at org.robolectric.util.TempDirectory.destroy(TempDirectory.java:111)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.base/java.lang.Thread.run(Unknown Source)
heres my junit
import androidx.test.core.app.ApplicationProvider
import com.google.firebase.FirebaseApp
import io.ktor.client.HttpClient
import io.ktor.client.call.body
import io.ktor.client.engine.mock.MockEngine
import io.ktor.client.plugins.ClientRequestException
import io.ktor.client.plugins.HttpTimeout
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
import io.ktor.client.plugins.defaultRequest
import io.ktor.client.plugins.logging.LogLevel
import io.ktor.client.plugins.logging.Logger
import io.ktor.client.plugins.logging.Logging
import io.ktor.client.plugins.observer.ResponseObserver
import io.ktor.client.request.header
import io.ktor.http.ContentType
import io.ktor.http.HttpHeaders
import io.ktor.serialization.kotlinx.json.json
import kotlinx.coroutines.runBlocking
import kotlinx.serialization.json.Json
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.middle.earth.lotr.BuildConfig
import org.middle.earth.lotr.data.remote.dto.character.CharacterResponse
import org.middle.earth.lotr.di.NETWORK_TIME_OUT
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
@RunWith(RobolectricTestRunner::class)
@Config(minSdk = 26, maxSdk = 34)
class TheOneApiNetworkTest {
@Before
fun setUp() {
FirebaseApp.initializeApp(ApplicationProvider.getApplicationContext())
}
@Test
fun character() {
val httpClient = getHttpClient(mockEngineCharactersOk)
runBlocking {
val response = TheOneApiHttpService(httpClient).character(1)
val characters = response.body<CharacterResponse>()
assertEquals(characters.characters.isNotEmpty(), true)
}
}
@Test(expected = ClientRequestException::class)
fun characterBadRequest() {
val httpClient = getHttpClient(mockEngineBadRequest)
runBlocking {
TheOneApiHttpService(httpClient).character(1)
}
}
private fun getHttpClient(mockEngine: MockEngine) = HttpClient(mockEngine) {
expectSuccess = true
install(ContentNegotiation) {
json(
Json {
prettyPrint = true
isLenient = true
useAlternativeNames = true
ignoreUnknownKeys = false
encodeDefaults = true
}
)
}
install(HttpTimeout) {
requestTimeoutMillis = NETWORK_TIME_OUT
connectTimeoutMillis = NETWORK_TIME_OUT
socketTimeoutMillis = NETWORK_TIME_OUT
}
install(Logging) {
logger = object : Logger {
override fun log(message: String) {
println("log() called with: message = $message")
}
}
level = LogLevel.ALL
}
install(ResponseObserver) {
onResponse { response ->
println("providesHttpClient() called with: response = $response\n${response.status.value}")
}
}
defaultRequest {
header(HttpHeaders.ContentType, ContentType.Application.Json)
header(HttpHeaders.Authorization, "Bearer ${BuildConfig.THE_ONE_API_ACCESS_TOKEN}")
header(HttpHeaders.UserAgent, "Android Mock User Agent")
}
}
}
heres my gradle for testing resources
testImplementation("junit:junit:4.13.2")
testImplementation("org.slf4j:slf4j-api:2.0.11")
testImplementation("org.slf4j:slf4j-simple:2.0.11")
testImplementation("androidx.test:core:1.5.0")
testImplementation("io.ktor:ktor-client-mock:2.3.7")
testImplementation("io.mockk:mockk:1.13.9")
testImplementation("org.robolectric:robolectric:4.11.1")
what am i doing wrong? do i need to be concerned about these messages? is there any way to stop them. resolve this?
The warning about
SQLiteConnection.closenot being called indicates that a database connection is being opened but not properly closed. That can lead to memory leaks and resource management issues in your tests.Make sure all resources like database connections are closed in the teardown process of your tests. That can be done in an
@Afterannotated method in your test class. See also "System.Data.SQLite Close()not releasing database file"The error regarding the failure to destroy the temp directory could mean that some files or resources created during the test run are not being cleaned up properly.
If you are using any custom resources or components that need to be closed or released, make sure they are properly handled in the test lifecycle.
Robolectric creates temporary directories for test runs. Make sure these directories are being cleaned up after the tests. That can be sometimes an issue with the test framework itself, but often it is related to how resources are handled in your tests.
If the issue persists, you might need to manually clean up or use a try-with-resources statement to make sure proper closure of resources.
Since the stack trace includes references to
com.google.android.datatransport.runtime, make sure any third-party libraries you are using are compatible with Robolectric and are not causing the resource management issues.