refactoring hemocube fragment and add tests

This commit is contained in:
Pritimay Sarkar
2024-01-23 13:42:27 +05:30
parent a0fc7f7be4
commit f83823a15f
6 changed files with 210 additions and 75 deletions

View File

@@ -108,6 +108,8 @@ dependencies {
testImplementation 'org.mockito:mockito-core:3.12.4' testImplementation 'org.mockito:mockito-core:3.12.4'
androidTestImplementation 'org.mockito:mockito-android:3.12.4' androidTestImplementation 'org.mockito:mockito-android:3.12.4'
androidTestImplementation 'org.mockito:mockito-inline:3.12.4' androidTestImplementation 'org.mockito:mockito-inline:3.12.4'
androidTestImplementation 'org.mockito:mockito-android:3.12.4'
testImplementation "androidx.arch.core:core-testing:2.2.0" testImplementation "androidx.arch.core:core-testing:2.2.0"
testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.1' testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.1'

View File

@@ -25,12 +25,10 @@ import com.example.hpostesting.presentation.utils.MyDialogListener
import com.example.hpostesting.presentation.utils.UIUtils import com.example.hpostesting.presentation.utils.UIUtils
import com.google.firebase.crashlytics.ktx.crashlytics import com.google.firebase.crashlytics.ktx.crashlytics
import com.google.firebase.ktx.Firebase import com.google.firebase.ktx.Firebase
import com.google.firebase.perf.ktx.performance
import `in`.sminnovations.hpostesting.R import `in`.sminnovations.hpostesting.R
import `in`.sminnovations.hpostesting.databinding.FragmentHemoCubeReferenceBinding import `in`.sminnovations.hpostesting.databinding.FragmentHemoCubeReferenceBinding
import kotlin.math.abs import kotlin.math.abs
import kotlin.math.log10 import kotlin.math.log10
import kotlin.math.round
@Suppress("MemberVisibilityCanBePrivate") @Suppress("MemberVisibilityCanBePrivate")
class HemoCubeFragment : Fragment() { class HemoCubeFragment : Fragment() {
@@ -44,7 +42,6 @@ class HemoCubeFragment : Fragment() {
private var resultData: String = "" private var resultData: String = ""
private var currentResultData: String = "" private var currentResultData: String = ""
private var isUsingExistingBuffer = false private var isUsingExistingBuffer = false
private var loginId: String = ""
private var isTestOngoing = false private var isTestOngoing = false
private var startListening = MutableLiveData<Boolean>(false) private var startListening = MutableLiveData<Boolean>(false)
private var led1BufferForDevice = 0.0 private var led1BufferForDevice = 0.0
@@ -59,7 +56,7 @@ class HemoCubeFragment : Fragment() {
private var fittedAbs2 = 0.0 private var fittedAbs2 = 0.0
private var fittedAbs3 = 0.0 private var fittedAbs3 = 0.0
private var fittedAbs4 = 0.0 private var fittedAbs4 = 0.0
private var _predictedDenovixRatio = 0.0 private var calculatedPredictedDenovixRatio = 0.0
private var validationError = false private var validationError = false
private var deviceHardwareId = "" private var deviceHardwareId = ""
private var allErrorMessages = "" private var allErrorMessages = ""
@@ -211,9 +208,9 @@ class HemoCubeFragment : Fragment() {
apply { apply {
DataHolder.hemoCubeTestData?.let { DataHolder.hemoCubeTestData?.let {
currentDeviceData?.coefficients?.let { coefficients -> currentDeviceData?.coefficients?.let { coefficients ->
val coefficient1 = coefficients[0] // val coefficient1 = coefficients[0]
val coefficient2 = coefficients[1] // val coefficient2 = coefficients[1]
val result = coefficient1 * coefficient2 // val result = coefficient1 * coefficient2
} }
} }
isOnline = isNetworkAvailable isOnline = isNetworkAvailable
@@ -394,7 +391,7 @@ class HemoCubeFragment : Fragment() {
} }
} }
resultData.contains("#SS") && this.testStatusCode < TestStatus.SAMPLE_STARTED.code -> { (resultData.contains("#SS") || resultData.contains("#SS1")) && this.testStatusCode < TestStatus.SAMPLE_STARTED.code -> {
this.testStatusCode = TestStatus.SAMPLE_STARTED.code this.testStatusCode = TestStatus.SAMPLE_STARTED.code
activity?.runOnUiThread { activity?.runOnUiThread {
binding.tvSubtitle4.text = getString(R.string.sample_started) binding.tvSubtitle4.text = getString(R.string.sample_started)
@@ -402,7 +399,7 @@ class HemoCubeFragment : Fragment() {
} }
} }
resultData.contains("#SC") && this.testStatusCode < TestStatus.SAMPLE_COMPLETED.code -> { (resultData.contains("#SC") || resultData.contains("#SC1")) && this.testStatusCode < TestStatus.SAMPLE_COMPLETED.code -> {
this.testStatusCode = TestStatus.SAMPLE_COMPLETED.code this.testStatusCode = TestStatus.SAMPLE_COMPLETED.code
hemoCubeViewModel.messages.postValue( hemoCubeViewModel.messages.postValue(
getString(R.string.sample_completed) + "\n" + getString(R.string.gathering_data)) getString(R.string.sample_completed) + "\n" + getString(R.string.gathering_data))
@@ -574,7 +571,7 @@ class HemoCubeFragment : Fragment() {
} }
fun processHardwareId(resultData: String) { fun processHardwareId(resultData: String) {
val hardwareId = "HCV-000-3001" //extractMiddleString(resultData) val hardwareId = extractMiddleString(resultData)
if (!hardwareId.isNullOrBlank()) { if (!hardwareId.isNullOrBlank()) {
updateDeviceId(hardwareId) updateDeviceId(hardwareId)
@@ -582,17 +579,17 @@ class HemoCubeFragment : Fragment() {
binding.btnPlacebuffer.visibility = View.VISIBLE binding.btnPlacebuffer.visibility = View.VISIBLE
} }
hemoCubeViewModel.messages.postValue(getString(R.string.start)) hemoCubeViewModel.messages.postValue(getString(R.string.start))
if (!Constants.DEVICE_CONFIGURATION.containsKey(deviceHardwareId)) {
deviceHardwareId = "HCV-000-3001"
allErrorMessages += "Calibration configuration for this device id is not found\n"
}
if (!Constants.BUFFER_INTENSITY_THRESHOLDS.containsKey(deviceHardwareId)) {
allErrorMessages += "ADC thresholds for this device id are not found\n"
}
} else { } else {
hemoCubeViewModel.messages.postValue("Config error") hemoCubeViewModel.messages.postValue("Config error")
} }
if (!Constants.DEVICE_CONFIGURATION.containsKey(deviceHardwareId)) {
deviceHardwareId = "HCV-000-3001"
allErrorMessages += "Calibration configuration for this device id is not found\n"
}
if (!Constants.BUFFER_INTENSITY_THRESHOLDS.containsKey(deviceHardwareId)) {
allErrorMessages += "ADC thresholds for this device id are not found\n"
}
} }
fun extractMiddleString(input: String): String? { fun extractMiddleString(input: String): String? {
@@ -696,25 +693,25 @@ class HemoCubeFragment : Fragment() {
val slope1 = Constants.DEVICE_HB_PARAMETERS[deviceHardwareId]?.get(1)?.get(0) val slope1 = Constants.DEVICE_HB_PARAMETERS[deviceHardwareId]?.get(1)?.get(0)
val intercept1 = Constants.DEVICE_HB_PARAMETERS[deviceHardwareId]?.get(1)?.get(1) val intercept1 = Constants.DEVICE_HB_PARAMETERS[deviceHardwareId]?.get(1)?.get(1)
val _hb1 = (led3Average - intercept1!!) / slope1!! val calculatedHb1 = (led1Average - intercept1!!) / slope1!!
val slope2 = Constants.DEVICE_HB_PARAMETERS[deviceHardwareId]?.get(3)?.get(0) val slope2 = Constants.DEVICE_HB_PARAMETERS[deviceHardwareId]?.get(3)?.get(0)
val intercept = Constants.DEVICE_HB_PARAMETERS[deviceHardwareId]?.get(3)?.get(1) val intercept = Constants.DEVICE_HB_PARAMETERS[deviceHardwareId]?.get(3)?.get(1)
val _hb2 = (led4Average - intercept!!) / slope2!! val calculatedHb2 = (led2Average - intercept!!) / slope2!!
val slope3 = Constants.DEVICE_HB_PARAMETERS[deviceHardwareId]?.get(2)?.get(0) val slope3 = Constants.DEVICE_HB_PARAMETERS[deviceHardwareId]?.get(2)?.get(0)
val intercept3 = Constants.DEVICE_HB_PARAMETERS[deviceHardwareId]?.get(2)?.get(1) val intercept3 = Constants.DEVICE_HB_PARAMETERS[deviceHardwareId]?.get(2)?.get(1)
val _hb3 = (led3Average - intercept3!!) / slope3!! val calculatedHb3 = (led3Average - intercept3!!) / slope3!!
val slope4 = Constants.DEVICE_HB_PARAMETERS[deviceHardwareId]?.get(3)?.get(0) val slope4 = Constants.DEVICE_HB_PARAMETERS[deviceHardwareId]?.get(3)?.get(0)
val intercept4 = Constants.DEVICE_HB_PARAMETERS[deviceHardwareId]?.get(3)?.get(1) val intercept4 = Constants.DEVICE_HB_PARAMETERS[deviceHardwareId]?.get(3)?.get(1)
val _hb4 = (led4Average - intercept4!!) / slope4!! val calculatedHb4 = (led4Average - intercept4!!) / slope4!!
_predictedDenovixRatio = fittedAbs3.div(fittedAbs1) calculatedPredictedDenovixRatio = fittedAbs3.div(fittedAbs1)
val slope = (led1Average - led2Average) / (435 - 415) val slope = (led1Average - led2Average) / (435 - 415)
val _slopeRatio = abs(led3Average / slope) val calculatedSlopeRatio = abs(led3Average / slope)
val slopeClass = slopeRatioClassification(_slopeRatio) val slopeClass = slopeRatioClassification(calculatedSlopeRatio)
if (fittedAbs1 <= fittedAbs2) { if (fittedAbs1 <= fittedAbs2) {
validationError = true validationError = true
@@ -733,7 +730,7 @@ class HemoCubeFragment : Fragment() {
} }
} }
if ((led1Average < 0.7 || led2Average < 0.7 || led3Average < 0.7 || led4Average < 0.7) && _slopeRatio > 35.0) { if ((led1Average < 0.7 || led2Average < 0.7 || led3Average < 0.7 || led4Average < 0.7) && calculatedSlopeRatio > 35.0) {
validationError = true validationError = true
activity?.runOnUiThread { activity?.runOnUiThread {
binding.errorMessage.text = "Severely Low Hb. Repeat test with 12 ul in 2 ml Buffer" binding.errorMessage.text = "Severely Low Hb. Repeat test with 12 ul in 2 ml Buffer"
@@ -769,12 +766,12 @@ class HemoCubeFragment : Fragment() {
this.abs2 = fittedAbs2 this.abs2 = fittedAbs2
this.abs3 = fittedAbs3 this.abs3 = fittedAbs3
this.abs4 = fittedAbs4 this.abs4 = fittedAbs4
this.hb3 = _hb3 this.hb3 = calculatedHb3
this.hb4 = _hb4 this.hb4 = calculatedHb4
this.deviceRatio = deviceRatio this.deviceRatio = deviceRatio
this.calculatedRatio = calculateRatio(deviceRatio) this.calculatedRatio = calculateRatio(deviceRatio)
this.predictedDenovixRatio = _predictedDenovixRatio this.predictedDenovixRatio = calculatedPredictedDenovixRatio
this.slopeRatio = _slopeRatio this.slopeRatio = calculatedSlopeRatio
this.coefficients = currentDeviceData?.coefficients?.get(0) this.coefficients = currentDeviceData?.coefficients?.get(0)
.toString() + ", " + currentDeviceData?.coefficients?.get(1).toString() .toString() + ", " + currentDeviceData?.coefficients?.get(1).toString()
this.prdClassification = absorbanceBasedClassification(predictedDenovixRatio) this.prdClassification = absorbanceBasedClassification(predictedDenovixRatio)
@@ -783,7 +780,7 @@ class HemoCubeFragment : Fragment() {
this.classificationResult = deviceRatioClass //findResult(calculatedRatio) this.classificationResult = deviceRatioClass //findResult(calculatedRatio)
hemoCubeViewModel.messages.postValue("${this.deviceRatioClass} ${if (led4Average < 0.17) " - Low Hb" else ""}\n") hemoCubeViewModel.messages.postValue("${this.deviceRatioClass} ${if (led4Average < 0.17) " - Low Hb" else ""}\n")
if (DataHolder.hemoCubeTestData?.testType == "HB") if (DataHolder.hemoCubeTestData?.testType == "HB")
hemoCubeViewModel.messages.postValue("HB: $_hb4") hemoCubeViewModel.messages.postValue("HB: $calculatedHb4")
this.errorMessages = allErrorMessages this.errorMessages = allErrorMessages
this.resultData = deviceLog this.resultData = deviceLog
this.batteryLevel = hemoCubeViewModel.getBatteryLevel().toString() this.batteryLevel = hemoCubeViewModel.getBatteryLevel().toString()

View File

@@ -1,27 +1,20 @@
package com.example.hpostesting package com.example.hpostesting
import android.content.Context import android.content.Context
import android.os.Build
import androidx.arch.core.executor.testing.InstantTaskExecutorRule import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.work.Configuration
import androidx.work.testing.SynchronousExecutor
import androidx.work.testing.WorkManagerTestInitHelper
import com.example.hpostesting.data.Result import com.example.hpostesting.data.Result
import com.example.hpostesting.data.model.login.LoginResponse import com.example.hpostesting.data.model.login.LoginResponse
import com.example.hpostesting.data.repository.Repository import com.example.hpostesting.data.repository.Repository
import com.example.hpostesting.domain.LogFileManager import com.example.hpostesting.domain.LogFileManager
import com.example.hpostesting.presentation.hemocube.HemoCubeViewModel import com.example.hpostesting.presentation.hemocube.HemoCubeViewModel
import com.example.hpostesting.util.TestCoroutineRule import com.example.hpostesting.util.TestCoroutineRule
import io.mockk.MockKAnnotations
import io.mockk.coEvery
import io.mockk.coVerify
import io.mockk.every import io.mockk.every
import io.mockk.impl.annotations.MockK import io.mockk.impl.annotations.MockK
import io.mockk.mockk import io.mockk.mockk
import io.mockk.verify import io.mockk.verify
import junit.framework.TestCase
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runBlockingTest
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before
import org.junit.Rule import org.junit.Rule
@@ -29,35 +22,34 @@ import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config import org.robolectric.annotation.Config
import java.io.File
//@RunWith(RobolectricTestRunner::class) @RunWith(RobolectricTestRunner::class)
//@Config(manifest = Config.NONE) @Config(manifest = Config.NONE)
//@ExperimentalCoroutinesApi @ExperimentalCoroutinesApi
//class HemocubeViewModelTest { class HemocubeViewModelTest {
//
// @get:Rule @get:Rule
// val instantTaskExecutorRule = InstantTaskExecutorRule() val instantTaskExecutorRule = InstantTaskExecutorRule()
//
// @get:Rule @get:Rule
// val coroutineRule = TestCoroutineRule() val coroutineRule = TestCoroutineRule()
//
// @MockK(relaxed = true) @MockK(relaxed = true)
// lateinit var repository: Repository lateinit var repository: Repository
//
// @MockK(relaxed = true) @MockK(relaxed = true)
// lateinit var logFileManager: LogFileManager lateinit var logFileManager: LogFileManager
//
// @MockK(relaxed = true) @MockK(relaxed = true)
// lateinit var context: Context lateinit var context: Context
//
// @MockK(relaxed = true) @MockK(relaxed = true)
// lateinit var observer: Observer<Result<LoginResponse>> lateinit var observer: Observer<Result<LoginResponse>>
//
// private lateinit var viewModel: HemoCubeViewModel private lateinit var viewModel: HemoCubeViewModel
//
// @Before @Before
// fun setup() { fun setup() {
// MockKAnnotations.init(this) // MockKAnnotations.init(this)
// //
// val config = Configuration.Builder() // val config = Configuration.Builder()
@@ -78,13 +70,34 @@ import java.io.File
// context // context
// ) // )
// viewModel.loginResponse.observeForever(observer) // viewModel.loginResponse.observeForever(observer)
// } }
//
// @After @After
// fun teardown() { fun teardown() {
// viewModel.loginResponse.removeObserver(observer) // viewModel.loginResponse.removeObserver(observer)
// } }
//
@Test
fun `testAddNumbers in HemocubeViewModelTest`() {
// Create a mock of the Calculator class using Mockito-Kotlin
val calculatorMock = mockk<Calculator>()
// Define the behavior of the add method on the mock
every { calculatorMock.add(2, 3) } returns 5
// Create an instance of MathApplication with the mock Calculator
val mathApplication = MathApplication(calculatorMock)
// Perform the test using the MathApplication
val result = mathApplication.addNumbers(2, 3)
// Verify that the add method of the mock was called with the correct parameters
verify { calculatorMock.add(2, 3) }
// Verify the result of the test
TestCase.assertEquals(5, result)
}
// @Test // @Test
// fun `login() should update LiveData with success`() = coroutineRule.runBlockingTest { // fun `login() should update LiveData with success`() = coroutineRule.runBlockingTest {
// //
@@ -124,7 +137,7 @@ import java.io.File
// viewModel.uploadLogs() // viewModel.uploadLogs()
// //
// // Assert // // Assert
// verify { observer.onChanged(response) } // verify { observer.onChanged(response as Result<LoginResponse>) }
// } // }
// //
// @Test // @Test
@@ -141,4 +154,4 @@ import java.io.File
// // Assert // // Assert
// verify { observer.onChanged(response) } // verify { observer.onChanged(response) }
// } // }
//} }

View File

@@ -0,0 +1,15 @@
package com.example.hpostesting
class Calculator {
fun add(a: Int, b: Int): Int {
return a + b
}
}
class MathApplication(private val calculator: Calculator) {
fun addNumbers(a: Int, b: Int): Int {
return calculator.add(a, b)
}
}

View File

@@ -0,0 +1,31 @@
package com.example.hpostesting
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import junit.framework.TestCase.assertEquals
import org.junit.Test
class MathApplicationTest {
@Test
fun testAddNumbers() {
// Create a mock of the Calculator class using Mockito-Kotlin
val calculatorMock = mockk<Calculator>()
// Define the behavior of the add method on the mock
every { calculatorMock.add(2, 3) } returns 5
// Create an instance of MathApplication with the mock Calculator
val mathApplication = MathApplication(calculatorMock)
// Perform the test using the MathApplication
val result = mathApplication.addNumbers(2, 3)
// Verify that the add method of the mock was called with the correct parameters
verify { calculatorMock.add(2, 3) }
// Verify the result of the test
assertEquals(5, result)
}
}

View File

@@ -0,0 +1,77 @@
package com.example.hpostesting
import android.content.Context
import android.net.ConnectivityManager
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import junit.framework.TestCase
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.junit.MockitoJUnitRunner
@RunWith(MockitoJUnitRunner::class)
class NetworkStatusLiveDataTest {
@get:Rule
val instantTaskExecutorRule = InstantTaskExecutorRule()
@Mock
private lateinit var mockContext: Context
@Mock
private lateinit var mockConnectivityManager: ConnectivityManager
@Before
fun setUp() {
// mockContext = ApplicationProvider.getApplicationContext<Context>()
// mockConnectivityManager = mockContext.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
}
@Test
fun testAddNumbers() {
// Create a mock of the Calculator class using Mockito-Kotlin
val calculatorMock = mockk<Calculator>()
// Define the behavior of the add method on the mock
every { calculatorMock.add(2, 3) } returns 5
// Create an instance of MathApplication with the mock Calculator
val mathApplication = MathApplication(calculatorMock)
// Perform the test using the MathApplication
val result = mathApplication.addNumbers(2, 3)
// Verify that the add method of the mock was called with the correct parameters
verify { calculatorMock.add(2, 3) }
// Verify the result of the test
TestCase.assertEquals(5, result)
}
// @Test
// fun testNetworkStatusLiveData() {
// // Assuming mockConnectivityManager is a mocked ConnectivityManager instance
// val mockNetwork = mock(Network::class.java)
// val mockNetworkCapabilities = mock(NetworkCapabilities::class.java)
//
// // Define desired capabilities
// `when`(mockNetworkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)).thenReturn(true)
// `when`(mockNetworkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)).thenReturn(true)
//
// // Set up network to return mocked capabilities
// `when`(mockConnectivityManager.activeNetwork).thenReturn(mockNetwork)
// `when`(mockConnectivityManager.getNetworkCapabilities(mockNetwork)).thenReturn(mockNetworkCapabilities)
//
// // Act
// val networkStatusLiveData = NetworkStatusLiveData(mockContext)
// val isConnected = networkStatusLiveData.value
//
// // Assert
// assertTrue("Network should be connected", isConnected!!)
// }
}