Compare commits
13 Commits
dev-update
...
QR-SCAN
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9f2b53c7f7 | ||
|
|
4958af6fd9 | ||
|
|
69e129cddb | ||
|
|
2460c58bea | ||
|
|
63331c2253 | ||
|
|
3c4a1fa56b | ||
|
|
bddebfaf2e | ||
|
|
1871f8c77a | ||
|
|
06d62506a9 | ||
|
|
0b13e02ece | ||
|
|
d1733a817f | ||
|
|
14bf1bab1f | ||
|
|
97ec931814 |
@@ -86,3 +86,16 @@ debugTests:
|
||||
stage: test
|
||||
script:
|
||||
- ./gradlew -Pci --console=plain :app:testDebug
|
||||
artifacts:
|
||||
paths:
|
||||
- app/build/test-results/
|
||||
|
||||
publishTestResults:
|
||||
stage: test
|
||||
script:
|
||||
- echo "Publishing JUnit test results"
|
||||
needs: [debugTests]
|
||||
artifacts:
|
||||
when: always
|
||||
reports:
|
||||
junit: app/build/test-results/testDebugUnitTest/*.xml
|
||||
|
||||
@@ -16,11 +16,11 @@ android {
|
||||
|
||||
// prod - production, preprod - preproduction, quality - qc, dev - development
|
||||
defaultConfig {
|
||||
applicationId "in.sminnovations.hpostesting.quality"
|
||||
applicationId "in.sminnovations.hpostesting.dev"
|
||||
minSdk 21
|
||||
targetSdk 34
|
||||
versionCode 93
|
||||
versionName "2.1.93"
|
||||
versionCode 97
|
||||
versionName "2.1.97"
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
@@ -80,7 +80,7 @@ dependencies {
|
||||
implementation 'com.google.firebase:firebase-storage-ktx'
|
||||
implementation 'com.firebaseui:firebase-ui-firestore:8.0.2'
|
||||
implementation 'com.google.android.gms:play-services-auth:20.7.0'
|
||||
implementation 'com.google.android.gms:play-services-location:21.0.1'
|
||||
implementation 'com.google.android.gms:play-services-location:21.1.0'
|
||||
implementation 'com.google.android.gms:play-services-ads-identifier:18.0.1'
|
||||
implementation 'com.google.android.things:androidthings:1.0'
|
||||
implementation 'com.google.firebase:firebase-appdistribution:16.0.0-beta11'
|
||||
@@ -95,6 +95,8 @@ dependencies {
|
||||
// Testing
|
||||
testImplementation 'junit:junit:4.13.2'
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
|
||||
androidTestImplementation 'com.android.support.test:runner:1.0.2'
|
||||
androidTestImplementation 'com.android.support.test:rules:1.0.2'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
|
||||
testImplementation "androidx.work:work-testing:2.9.0"
|
||||
androidTestImplementation 'androidx.test:core-ktx:1.5.0'
|
||||
@@ -113,7 +115,6 @@ dependencies {
|
||||
testImplementation "androidx.arch.core:core-testing:2.2.0"
|
||||
testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.1'
|
||||
|
||||
|
||||
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.7.0"
|
||||
implementation 'com.opencsv:opencsv:5.9'
|
||||
implementation 'com.github.mik3y:usb-serial-for-android:3.5.1'
|
||||
@@ -160,4 +161,6 @@ dependencies {
|
||||
// implementation("io.nats:jnats:2.11.2")
|
||||
implementation 'com.google.android.play:core:1.10.3'
|
||||
|
||||
implementation fileTree(dir: 'libs', include: ['*.aar'])
|
||||
// implementation fileTree(dir: '/Users/apple/Downloads/work/hpos/app/libs', include: ['*.aar', '*.jar'])
|
||||
}
|
||||
BIN
app/libs/barcode_scanner_library_v2.6.19.0-release.aar
Executable file
BIN
app/libs/barcode_scanner_library_v2.6.19.0-release.aar
Executable file
Binary file not shown.
@@ -1246,7 +1246,7 @@ object Constants {
|
||||
listOf(23500, 26250),
|
||||
listOf(23500, 26250),
|
||||
),
|
||||
"HCV-000-1015" to listOf(
|
||||
"HCV-000-3013" to listOf(
|
||||
listOf(23500, 26250),
|
||||
listOf(23500, 26250),
|
||||
listOf(23500, 26250),
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.example.hpostesting.data.model
|
||||
|
||||
import com.example.hpostesting.data.constant.Constants
|
||||
import com.example.hpostesting.data.model.patient.DeviceData
|
||||
import com.example.hpostesting.data.model.patient.HemoCubeTestData
|
||||
|
||||
data class TestState (
|
||||
var testDetails: HemoCubeTestData? = null,
|
||||
var isOnline: Boolean = false,
|
||||
var currentDeviceData: DeviceData? = null,
|
||||
var resultData: String = "",
|
||||
var currentResultData: String = "",
|
||||
var isUsingExistingBuffer: Boolean = false,
|
||||
var isTestOngoing: Boolean = false,
|
||||
var led1BufferForDevice: Double = 0.0,
|
||||
var led2BufferForDevice: Double = 0.0,
|
||||
var led3BufferForDevice: Double = 0.0,
|
||||
var led4BufferForDevice: Double = 0.0,
|
||||
var led1SampleForDevice: Double = 0.0,
|
||||
var led2SampleForDevice: Double = 0.0,
|
||||
var led3SampleForDevice: Double = 0.0,
|
||||
var led4SampleForDevice: Double = 0.0,
|
||||
var fittedAbs1: Double = 0.0,
|
||||
var fittedAbs2: Double = 0.0,
|
||||
var fittedAbs3: Double = 0.0,
|
||||
var fittedAbs4: Double = 0.0,
|
||||
var calculatedPredictedDenovixRatio: Double = 0.0,
|
||||
var validationError: Boolean = false,
|
||||
var deviceHardwareId: String = "",
|
||||
var allErrorMessages: String = "",
|
||||
var testStatusCode: Double = 0.0,
|
||||
var repeatReadingCount: Int = 0,
|
||||
var readingsPerSample: Int = Constants.READINGS_PER_SAMPLE,
|
||||
var uploadedToCloud: Boolean = false,
|
||||
var uploadedToMolbio: Boolean = false
|
||||
)
|
||||
@@ -57,7 +57,7 @@ class DatabaseRepository @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun deviceProvision(deviceProvisionRequest: DeviceProvisionRequest): Result<DeviceProvisionResponse> {
|
||||
override suspend fun deviceProvision(deviceProvisionRequest: DeviceProvisionRequest): Result<DeviceProvisionResponse> {
|
||||
return safeApiCall { molbioAuthApi.deviceProvision(deviceProvisionRequest) }
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@ package com.example.hpostesting.data.repository
|
||||
|
||||
import com.example.hpostesting.data.Result
|
||||
import com.example.hpostesting.data.model.Response
|
||||
import com.example.hpostesting.data.model.deviceprovision.DeviceProvisionRequest
|
||||
import com.example.hpostesting.data.model.deviceprovision.DeviceProvisionResponse
|
||||
import com.example.hpostesting.data.model.diagnostics.DiagnosticsData
|
||||
import com.example.hpostesting.data.model.log.UploadLogsResponse
|
||||
import com.example.hpostesting.data.model.login.LoginRequest
|
||||
@@ -33,6 +35,8 @@ interface Repository {
|
||||
abstract fun <UserData> addTestToDatabase(testDetails: UserData): Any
|
||||
// suspend fun addToDatabase(data: PatientDetails)
|
||||
|
||||
suspend fun deviceProvision(deviceProvisionRequest: DeviceProvisionRequest): Result<DeviceProvisionResponse>
|
||||
|
||||
suspend fun login(loginRequest: LoginRequest): Result<LoginResponse>
|
||||
|
||||
suspend fun uploadResults(molbioV2ResultRequest: MolbioV2ResultRequest): Result<MolbioV2ResultResponse>
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
package com.example.hpostesting.presentation
|
||||
|
||||
import com.example.hpostesting.presentation.dashboard.DashboardActivity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.SharedPreferences
|
||||
import android.os.AsyncTask
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import android.widget.EditText
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.example.hpostesting.data.DataHolder
|
||||
import com.example.hpostesting.data.constant.Constants
|
||||
import com.example.hpostesting.data.constant.LanguageManager
|
||||
|
||||
import com.example.hpostesting.presentation.dashboard.DashboardActivity
|
||||
import com.example.hpostesting.presentation.hemocube.HemocubeActivity
|
||||
import com.example.hpostesting.presentation.testRight.TestRightActivity
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
@@ -20,16 +21,29 @@ import com.google.firebase.crashlytics.FirebaseCrashlytics
|
||||
import com.journeyapps.barcodescanner.ScanContract
|
||||
import com.journeyapps.barcodescanner.ScanIntentResult
|
||||
import com.journeyapps.barcodescanner.ScanOptions
|
||||
import com.zebra.barcode.sdk.sms.ConfigurationUpdateEvent
|
||||
import com.zebra.scannercontrol.DCSSDKDefs
|
||||
import com.zebra.scannercontrol.DCSSDKDefs.DCSSDK_COMMAND_OPCODE
|
||||
import com.zebra.scannercontrol.DCSSDKDefs.DCSSDK_RESULT
|
||||
import com.zebra.scannercontrol.DCSScannerInfo
|
||||
import com.zebra.scannercontrol.FirmwareUpdateEvent
|
||||
import com.zebra.scannercontrol.IDcsSdkApiDelegate
|
||||
import com.zebra.scannercontrol.SDKHandler
|
||||
import `in`.sminnovations.hpostesting.R
|
||||
import `in`.sminnovations.hpostesting.databinding.ActivityKitScanBinding
|
||||
import java.nio.charset.StandardCharsets
|
||||
|
||||
class KitScanActivity : AppCompatActivity() {
|
||||
class KitScanActivity : AppCompatActivity(), IDcsSdkApiDelegate {
|
||||
|
||||
private val TAG = "KitScanActivity"
|
||||
private lateinit var binding: ActivityKitScanBinding
|
||||
|
||||
private lateinit var sharedPreference: SharedPreferences
|
||||
|
||||
var sdkHandler: SDKHandler? = null
|
||||
var editBarcode: EditText? = null
|
||||
var mScannerInfoList = ArrayList<DCSScannerInfo>()
|
||||
|
||||
private val barcodeLauncher = registerForActivityResult(
|
||||
ScanContract()
|
||||
) { result: ScanIntentResult ->
|
||||
@@ -51,6 +65,35 @@ class KitScanActivity : AppCompatActivity() {
|
||||
super.attachBaseContext(newBase)
|
||||
}
|
||||
|
||||
|
||||
override fun dcssdkEventScannerAppeared(dcsScannerInfo: DCSScannerInfo?) {}
|
||||
|
||||
override fun dcssdkEventScannerDisappeared(i: Int) {}
|
||||
|
||||
override fun dcssdkEventCommunicationSessionEstablished(dcsScannerInfo: DCSScannerInfo?) {}
|
||||
|
||||
override fun dcssdkEventCommunicationSessionTerminated(i: Int) {}
|
||||
// override fun dcssdkEventBarcode(p0: ByteArray?, p1: Int, p2: Int) {
|
||||
// TODO("Not yet implemented")
|
||||
// }
|
||||
|
||||
|
||||
override fun dcssdkEventImage(bytes: ByteArray?, i: Int) {}
|
||||
|
||||
override fun dcssdkEventVideo(bytes: ByteArray?, i: Int) {}
|
||||
|
||||
override fun dcssdkEventBinaryData(bytes: ByteArray?, i: Int) {}
|
||||
|
||||
override fun dcssdkEventFirmwareUpdate(firmwareUpdateEvent: FirmwareUpdateEvent?) {}
|
||||
|
||||
override fun dcssdkEventAuxScannerAppeared(
|
||||
dcsScannerInfo: DCSScannerInfo?,
|
||||
dcsScannerInfo1: DCSScannerInfo?
|
||||
) {
|
||||
}
|
||||
|
||||
override fun dcssdkEventConfigurationUpdate(configurationUpdateEvent: ConfigurationUpdateEvent?) {}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
@@ -110,7 +153,8 @@ class KitScanActivity : AppCompatActivity() {
|
||||
setSupportActionBar(binding.toolbar)
|
||||
|
||||
binding.btnScanNow.setOnClickListener {
|
||||
startScanningNow()
|
||||
// startScanningNow()
|
||||
pullTrigger()
|
||||
}
|
||||
|
||||
|
||||
@@ -164,6 +208,79 @@ class KitScanActivity : AppCompatActivity() {
|
||||
Toast.makeText(this, R.string.invalid_kit_number, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
}
|
||||
|
||||
//Setting up the SDK handler
|
||||
|
||||
//Setting up the SDK handler
|
||||
sdkHandler = SDKHandler(this)
|
||||
|
||||
//Registers a particular object which conforms to IDcsSdkApiDelegate interface as a receiver of SDK notifications.
|
||||
|
||||
//Registers a particular object which conforms to IDcsSdkApiDelegate interface as a receiver of SDK notifications.
|
||||
sdkHandler!!.dcssdkSetDelegate(this)
|
||||
|
||||
//this command is telling the sdk that we're going to be connecting to the scanner via USB
|
||||
|
||||
//this command is telling the sdk that we're going to be connecting to the scanner via USB
|
||||
sdkHandler!!.dcssdkSetOperationalMode(DCSSDKDefs.DCSSDK_MODE.DCSSDK_OPMODE_SNAPI)
|
||||
|
||||
|
||||
//deciding what kind of notifications we want to receive. Explained more in the function
|
||||
//first we use bitmapping to set these values into the notifications_mask.
|
||||
|
||||
|
||||
//deciding what kind of notifications we want to receive. Explained more in the function
|
||||
//first we use bitmapping to set these values into the notifications_mask.
|
||||
var notifications_mask = 0
|
||||
// We would like to subscribe to all barcode events
|
||||
// We would like to subscribe to all barcode events
|
||||
notifications_mask =
|
||||
notifications_mask or DCSSDKDefs.DCSSDK_EVENT.DCSSDK_EVENT_BARCODE.value
|
||||
// subscribe to events set in notification mask
|
||||
// subscribe to events set in notification mask
|
||||
sdkHandler!!.dcssdkSubsribeForEvents(notifications_mask)
|
||||
|
||||
|
||||
//so adding all the scanners to the mScannerInfoList, then connecting to the first scanner.
|
||||
//There's only one scanner so this step is unnecessary, I did it thinking about possible scalability.
|
||||
//here I just get the list of available scanners which also returns the USB scanner attached to the tinker board.
|
||||
|
||||
|
||||
//so adding all the scanners to the mScannerInfoList, then connecting to the first scanner.
|
||||
//There's only one scanner so this step is unnecessary, I did it thinking about possible scalability.
|
||||
//here I just get the list of available scanners which also returns the USB scanner attached to the tinker board.
|
||||
mScannerInfoList.addAll(sdkHandler!!.dcssdkGetAvailableScannersList())
|
||||
|
||||
//I'm doing mScannerInfoList.get(0) because there's only one scanner which will be at position 0.
|
||||
|
||||
//I'm doing mScannerInfoList.get(0) because there's only one scanner which will be at position 0.
|
||||
sdkHandler!!.dcssdkEstablishCommunicationSession(mScannerInfoList[0].scannerID)
|
||||
}
|
||||
|
||||
fun pullTrigger() {
|
||||
if (!mScannerInfoList[0].isActive) {
|
||||
sdkHandler!!.dcssdkEstablishCommunicationSession(mScannerInfoList[0].scannerID)
|
||||
}
|
||||
val inXML = "<inArgs><scannerID> 1 </scannerID></inArgs>"
|
||||
val outXML = StringBuilder()
|
||||
val result: DCSSDK_RESULT =
|
||||
sdkHandler!!.dcssdkExecuteCommandOpCodeInXMLForScanner(
|
||||
DCSSDK_COMMAND_OPCODE.DCSSDK_DEVICE_PULL_TRIGGER, inXML, outXML, 1
|
||||
)
|
||||
if (result == DCSSDK_RESULT.DCSSDK_RESULT_SUCCESS) Log.d(
|
||||
"Scanning",
|
||||
"Success"
|
||||
) else if (result == DCSSDK_RESULT.DCSSDK_RESULT_FAILURE) Log.d("Scanning", "Failed")
|
||||
//new MyAsyncTask(1, DCSSDKDefs.DCSSDK_COMMAND_OPCODE.DCSSDK_DEVICE_PULL_TRIGGER,null).execute(inXML);
|
||||
}
|
||||
|
||||
//this function is called if barcode is detected.
|
||||
override fun dcssdkEventBarcode(barcodeData: ByteArray?, barcodeType: Int, fromScannerID: Int) {
|
||||
val result = String(barcodeData!!)
|
||||
Log.d("BARCODE", result)
|
||||
runOnUiThread {
|
||||
binding.tvTitle2.text = result
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkHemoCubeKitData(): Boolean {
|
||||
|
||||
@@ -12,7 +12,6 @@ import android.view.ViewGroup
|
||||
import android.widget.AdapterView
|
||||
import android.widget.ArrayAdapter
|
||||
import android.widget.Spinner
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.fragment.app.Fragment
|
||||
import com.example.hpostesting.data.DataHolder
|
||||
import com.example.hpostesting.data.constant.Constants
|
||||
@@ -22,7 +21,7 @@ import `in`.sminnovations.hpostesting.databinding.FragmentAssuranceControlsBindi
|
||||
import java.time.Instant
|
||||
|
||||
class AssuranceControlsFragment: Fragment() {
|
||||
private lateinit var binding: FragmentAssuranceControlsBinding
|
||||
lateinit var binding: FragmentAssuranceControlsBinding
|
||||
private lateinit var sharedPreferences: SharedPreferences
|
||||
|
||||
override fun onCreateView(
|
||||
@@ -43,7 +42,7 @@ class AssuranceControlsFragment: Fragment() {
|
||||
// binding.btnSubmit.visibility = View.GONE
|
||||
|
||||
val solutionSpinner: Spinner = binding.spinnerSolutions
|
||||
val solutionOptions = arrayOf("Select solution", "KMnO4", "Tartrazine", "AR", "HB", "Blood")
|
||||
val solutionOptions = arrayOf("Select solution", "Tartrazine", "AR")
|
||||
val solutionAdapter = ArrayAdapter(requireContext(), R.layout.simple_spinner_item, solutionOptions)
|
||||
solutionAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
|
||||
solutionSpinner.adapter = solutionAdapter
|
||||
@@ -65,7 +64,7 @@ class AssuranceControlsFragment: Fragment() {
|
||||
}
|
||||
|
||||
val concentrationSpinner: Spinner = binding.spinnerConcentration
|
||||
val concentrationOptions = arrayOf("Select concentration", "250umol", "350umol", "450umol", "550umol", "650umol")
|
||||
val concentrationOptions = arrayOf("Select concentration", "65umol", "45umol", "22.5umol", "12.25umol", "6.125umol", "75umol", "50umol", "25umol", "12.5umol", "6.25umol")
|
||||
val concentrationAdapter = ArrayAdapter(requireContext(), R.layout.simple_spinner_item, concentrationOptions)
|
||||
concentrationAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
|
||||
concentrationSpinner.adapter = concentrationAdapter
|
||||
|
||||
@@ -4,7 +4,6 @@ import android.app.DownloadManager
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
@@ -24,10 +23,6 @@ import com.example.hpostesting.data.constant.LanguageManager
|
||||
import com.example.hpostesting.presentation.NatsManager
|
||||
import com.example.hpostesting.presentation.hemocube.HemoCubeViewModel
|
||||
import com.google.android.material.navigation.NavigationView
|
||||
import com.google.android.material.progressindicator.LinearProgressIndicator
|
||||
import com.google.android.play.core.appupdate.AppUpdateManager
|
||||
import com.google.android.play.core.appupdate.AppUpdateManagerFactory
|
||||
|
||||
import com.google.firebase.appdistribution.FirebaseAppDistribution
|
||||
import com.google.firebase.appdistribution.FirebaseAppDistributionException
|
||||
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
||||
@@ -35,7 +30,6 @@ import dagger.hilt.android.AndroidEntryPoint
|
||||
import `in`.sminnovations.hpostesting.R
|
||||
import `in`.sminnovations.hpostesting.databinding.ActivityDashboardBinding
|
||||
import okhttp3.ResponseBody
|
||||
|
||||
import java.io.File
|
||||
|
||||
open interface IDataCollector {
|
||||
@@ -138,8 +132,10 @@ class DashboardActivity : AppCompatActivity(), IDataCollector {
|
||||
|
||||
val downloadManager = getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
|
||||
downloadId = downloadManager.enqueue(request)
|
||||
val filter = IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE)
|
||||
registerReceiver(downloadReceiver, filter)
|
||||
|
||||
// Register a BroadcastReceiver to receive the download complete event
|
||||
// val filter = IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE)
|
||||
// registerReceiver(downloadReceiver, filter)
|
||||
}
|
||||
private fun extractApkUrl(responseBody: ResponseBody): String {
|
||||
return responseBody.string()
|
||||
@@ -183,7 +179,7 @@ class DashboardActivity : AppCompatActivity(), IDataCollector {
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
unregisterReceiver(downloadReceiver)
|
||||
// unregisterReceiver(downloadReceiver)
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
|
||||
@@ -1,29 +1,22 @@
|
||||
package com.example.hpostesting.presentation.deviceprovision
|
||||
|
||||
import android.content.Context
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.example.hpostesting.data.NetworkStatusLiveData
|
||||
import com.example.hpostesting.data.Result
|
||||
import com.example.hpostesting.data.model.deviceprovision.DeviceProvisionRequest
|
||||
import com.example.hpostesting.data.model.deviceprovision.DeviceProvisionResponse
|
||||
import com.example.hpostesting.data.repository.DatabaseRepository
|
||||
import com.example.hpostesting.data.repository.Repository
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class DeviceProvisionViewModel @Inject constructor(
|
||||
private val databaseRepository: DatabaseRepository,
|
||||
context: Context
|
||||
private val databaseRepository: Repository
|
||||
) : ViewModel() {
|
||||
|
||||
var isServiceConnected = false
|
||||
private val _networkStatusLiveData = NetworkStatusLiveData(context)
|
||||
val networkStatusLiveData: LiveData<Boolean>
|
||||
get() = _networkStatusLiveData
|
||||
|
||||
val deviceProvisionResponse = MutableLiveData<Result<DeviceProvisionResponse>>()
|
||||
|
||||
|
||||
@@ -1,39 +1,22 @@
|
||||
package com.example.hpostesting.presentation.diagnostics
|
||||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import android.util.Log
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.example.hpostesting.data.DataHolder
|
||||
import com.example.hpostesting.data.NetworkStatusLiveData
|
||||
import com.example.hpostesting.data.constant.Constants
|
||||
import com.example.hpostesting.data.dao.HemoCubeDao
|
||||
import com.example.hpostesting.data.model.Response
|
||||
import com.example.hpostesting.data.model.diagnostics.DiagnosticsData
|
||||
import com.example.hpostesting.data.model.patient.DeviceData
|
||||
import com.example.hpostesting.data.model.patient.HemoCubeTestData
|
||||
import com.example.hpostesting.data.model.patient.toHemoCubeTestData
|
||||
import com.example.hpostesting.data.repository.DatabaseRepository
|
||||
import com.example.hpostesting.data.repository.Repository
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.launch
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Calendar
|
||||
import java.util.Locale
|
||||
import javax.inject.Inject
|
||||
@HiltViewModel
|
||||
class DiagnosticsViewModel @Inject constructor(
|
||||
private val hemoCubeDao: HemoCubeDao,
|
||||
private val repository: DatabaseRepository,
|
||||
context: Context
|
||||
private val repository: Repository
|
||||
) : ViewModel() {
|
||||
var isServiceConnected = false
|
||||
val progressBar = MutableLiveData(false)
|
||||
val messages = MutableLiveData<String>()
|
||||
private val sharedPreference: SharedPreferences =
|
||||
context.getSharedPreferences("HEMOCUBE", Context.MODE_PRIVATE)
|
||||
|
||||
val deviceData = MutableLiveData<DeviceData?>()
|
||||
val fireBaseUpload = MutableLiveData<String>()
|
||||
|
||||
@@ -17,6 +17,7 @@ import com.example.hpostesting.data.Result
|
||||
import com.example.hpostesting.data.constant.Constants
|
||||
import com.example.hpostesting.data.constant.HemoCubeCommands
|
||||
import com.example.hpostesting.data.constant.TestStatus
|
||||
import com.example.hpostesting.data.model.TestState
|
||||
import com.example.hpostesting.data.model.patient.DeviceData
|
||||
import com.example.hpostesting.data.model.patient.toHemoCubeTestData
|
||||
import com.example.hpostesting.presentation.UsbServiceListener
|
||||
@@ -65,6 +66,7 @@ class HemoCubeFragment : Fragment() {
|
||||
private var readingsPerSample = Constants.READINGS_PER_SAMPLE
|
||||
private var uploadedToCloud = false
|
||||
private var uploadedToMolbio = false
|
||||
lateinit var testState: TestState
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?,
|
||||
@@ -72,6 +74,11 @@ class HemoCubeFragment : Fragment() {
|
||||
binding = FragmentHemoCubeReferenceBinding.inflate(inflater, container, false)
|
||||
sharedPreferences =
|
||||
requireContext().getSharedPreferences("HEMOCUBE", Context.MODE_PRIVATE)
|
||||
|
||||
testState = TestState(
|
||||
testDetails = DataHolder.selectedTest?.toHemoCubeTestData(),
|
||||
)
|
||||
|
||||
return binding.root
|
||||
}
|
||||
|
||||
@@ -317,9 +324,7 @@ class HemoCubeFragment : Fragment() {
|
||||
}
|
||||
|
||||
private fun listenToHemoCube() {
|
||||
if (DataHolder.hemoCubeTestData == null) {
|
||||
DataHolder.hemoCubeTestData = DataHolder.selectedTest?.toHemoCubeTestData()
|
||||
}
|
||||
DataHolder.hemoCubeTestData = DataHolder.selectedTest?.toHemoCubeTestData()
|
||||
hemoCubeViewModel.progressBar.postValue(true)
|
||||
|
||||
val fullReadOutput = StringBuilder()
|
||||
@@ -364,7 +369,7 @@ class HemoCubeFragment : Fragment() {
|
||||
})
|
||||
}
|
||||
|
||||
private fun handleUsbData(stringData: String) {
|
||||
fun handleUsbData(stringData: String) {
|
||||
if (stringData.contains("#")) {
|
||||
isTestOngoing = true
|
||||
}
|
||||
@@ -374,8 +379,12 @@ class HemoCubeFragment : Fragment() {
|
||||
hemoCubeViewModel.deviceMessages.postValue(currentResultData)
|
||||
|
||||
when {
|
||||
(resultData.contains("SNE") || resultData.contains("SN"))&& this.testStatusCode < TestStatus.CONFIG_COMPLETED.code -> {
|
||||
processHardwareId(resultData)
|
||||
resultData.contains("SNE") && this.testStatusCode < TestStatus.CONFIG_COMPLETED.code -> {
|
||||
processV2HardwareId(resultData)
|
||||
}
|
||||
|
||||
(resultData.contains("SN") && !resultData.contains("SNS") && !resultData.contains("SNE") && resultData.length >= 15) && this.testStatusCode < TestStatus.CONFIG_COMPLETED.code -> {
|
||||
processV1HardwareId(resultData)
|
||||
}
|
||||
|
||||
resultData.contains("#BS") && this.testStatusCode < TestStatus.BUFFER_STARTED.code -> {
|
||||
@@ -570,8 +579,8 @@ class HemoCubeFragment : Fragment() {
|
||||
currentResultData = ""
|
||||
}
|
||||
|
||||
fun processHardwareId(resultData: String) {
|
||||
val hardwareId = extractMiddleString(resultData)
|
||||
fun processV1HardwareId(resultData: String) {
|
||||
val hardwareId = extractV1HardwareId(resultData)
|
||||
|
||||
if (!hardwareId.isNullOrBlank()) {
|
||||
updateDeviceId(hardwareId)
|
||||
@@ -584,15 +593,44 @@ class HemoCubeFragment : Fragment() {
|
||||
}
|
||||
|
||||
if (!Constants.DEVICE_CONFIGURATION.containsKey(deviceHardwareId)) {
|
||||
assignDefaultDevice()
|
||||
allErrorMessages += "Calibration configuration for this device id is not found\n"
|
||||
assignDefaultDevice(resultData)
|
||||
testState.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"
|
||||
testState.allErrorMessages += "ADC thresholds for this device id are not found\n"
|
||||
}
|
||||
}
|
||||
|
||||
fun assignDefaultDevice() {
|
||||
fun extractV1HardwareId(input: String): String? {
|
||||
val regex = Regex("SN (\\S+)")
|
||||
val matchResult = regex.find(input)
|
||||
|
||||
return matchResult?.groupValues?.get(1)
|
||||
}
|
||||
|
||||
fun processV2HardwareId(resultData: String) {
|
||||
val hardwareId = extractV2HardwareId(resultData)
|
||||
|
||||
if (!hardwareId.isNullOrBlank()) {
|
||||
updateDeviceId(hardwareId)
|
||||
activity?.runOnUiThread {
|
||||
binding.btnPlacebuffer.visibility = View.VISIBLE
|
||||
}
|
||||
hemoCubeViewModel.messages.postValue(getString(R.string.start))
|
||||
} else {
|
||||
hemoCubeViewModel.messages.postValue("Config error")
|
||||
}
|
||||
|
||||
if (!Constants.DEVICE_CONFIGURATION.containsKey(deviceHardwareId)) {
|
||||
assignDefaultDevice(resultData)
|
||||
testState.allErrorMessages += "Calibration configuration for this device id is not found\n"
|
||||
}
|
||||
if (!Constants.BUFFER_INTENSITY_THRESHOLDS.containsKey(deviceHardwareId)) {
|
||||
testState.allErrorMessages += "ADC thresholds for this device id are not found\n"
|
||||
}
|
||||
}
|
||||
|
||||
fun assignDefaultDevice(configData: String) {
|
||||
deviceHardwareId = "HCV-000-3001"
|
||||
testStatusCode = TestStatus.CONFIG_COMPLETED.code
|
||||
activity?.runOnUiThread {
|
||||
@@ -601,8 +639,8 @@ class HemoCubeFragment : Fragment() {
|
||||
hemoCubeViewModel.messages.postValue(getString(R.string.start))
|
||||
}
|
||||
|
||||
fun extractMiddleString(input: String): String? {
|
||||
val pattern = Regex("SNS\\s(.*?)\\sSNE")
|
||||
fun extractV2HardwareId(input: String): String? {
|
||||
val pattern = Regex("SNS\\s*(.*?)\\s*SNE")
|
||||
val matchResult: MatchResult? = pattern.find(input)
|
||||
|
||||
return matchResult?.groups?.get(1)?.value
|
||||
@@ -656,7 +694,7 @@ class HemoCubeFragment : Fragment() {
|
||||
)?.get(0)!!
|
||||
) {
|
||||
validationError = true
|
||||
allErrorMessages += "Error: Invalid Test. Improper buffer reading (low)"
|
||||
testState.allErrorMessages += "Error: Invalid Test. Improper buffer reading (low)"
|
||||
activity?.runOnUiThread {
|
||||
binding.errorMessage.text = getString(R.string.error_improper_buffer_low)
|
||||
// binding.errorMessage.visibility = View.VISIBLE
|
||||
@@ -676,7 +714,7 @@ class HemoCubeFragment : Fragment() {
|
||||
)?.get(1)!!
|
||||
) {
|
||||
validationError = true
|
||||
allErrorMessages += "Error: Invalid Test. Improper buffer reading (high)" + "\n"
|
||||
testState.allErrorMessages += "Error: Invalid Test. Improper buffer reading (high)" + "\n"
|
||||
activity?.runOnUiThread {
|
||||
binding.errorMessage.text =
|
||||
getString(R.string.error_improper_buffer_high)
|
||||
@@ -724,7 +762,7 @@ class HemoCubeFragment : Fragment() {
|
||||
|
||||
if (fittedAbs1 <= fittedAbs2) {
|
||||
validationError = true
|
||||
allErrorMessages += "Error: Invalid Test. Problem with de-oxygenation" + "\n"
|
||||
testState.allErrorMessages += "Error: Invalid Test. Problem with de-oxygenation" + "\n"
|
||||
activity?.runOnUiThread {
|
||||
binding.errorMessage.text = getString(R.string.error_invalid_test)
|
||||
// binding.errorMessage.visibility = View.VISIBLE
|
||||
@@ -749,7 +787,7 @@ class HemoCubeFragment : Fragment() {
|
||||
|
||||
if (fittedAbs3 < 0.1) {
|
||||
validationError = true
|
||||
allErrorMessages += "Error: Low Hb. Repeat test" + "\n"
|
||||
testState.allErrorMessages += "Error: Low Hb. Repeat test" + "\n"
|
||||
activity?.runOnUiThread {
|
||||
binding.errorMessage.text = "Error: Low Hb. Repeat test"
|
||||
// binding.errorMessage.visibility = View.VISIBLE
|
||||
@@ -789,8 +827,8 @@ class HemoCubeFragment : Fragment() {
|
||||
this.classificationResult = deviceRatioClass //findResult(calculatedRatio)
|
||||
hemoCubeViewModel.messages.postValue("${this.deviceRatioClass} ${if (led4Average < 0.17) " - Low Hb" else ""}\n")
|
||||
if (DataHolder.hemoCubeTestData?.testType == "HB")
|
||||
hemoCubeViewModel.messages.postValue("HB: $calculatedHb4")
|
||||
this.errorMessages = allErrorMessages
|
||||
hemoCubeViewModel.messages.postValue("Hb: $calculatedHb4")
|
||||
this.errorMessages = testState.allErrorMessages
|
||||
this.resultData = deviceLog
|
||||
this.batteryLevel = hemoCubeViewModel.getBatteryLevel().toString()
|
||||
this.batteryCapacity =
|
||||
@@ -853,36 +891,38 @@ class HemoCubeFragment : Fragment() {
|
||||
|
||||
fun deviceRatioClassification(ratio: Double?): String {
|
||||
try {
|
||||
hemoCubeViewModel.messages.postValue("result classification")
|
||||
if (ratio != null) {
|
||||
if (ratio in 0.2..0.29) {
|
||||
activity?.runOnUiThread {
|
||||
binding.tvSubtitle4.setTextColor(
|
||||
ContextCompat.getColor(
|
||||
requireContext(),
|
||||
R.color.green_2
|
||||
)
|
||||
)
|
||||
}
|
||||
return getString(R.string.normal)
|
||||
if (ratio in 0.1..0.29) {
|
||||
// setSubtitleTextColor(R.color.green_2)
|
||||
return "Normal"
|
||||
}
|
||||
if (ratio in 0.29..0.32)
|
||||
return getString(R.string.negative_borderline)
|
||||
return "Negative Borderline, Repeat Test"
|
||||
if (ratio in 0.32..0.35)
|
||||
return getString(R.string.sickle_cell_trait)
|
||||
return "Sickle Cell Trait"
|
||||
if (ratio in 0.35..0.38)
|
||||
return getString(R.string.positive_for_sickle_cell)
|
||||
return "Positive for Sickle Cell. HPLC for Confirmation"
|
||||
if (ratio in 0.38..0.5)
|
||||
return getString(R.string.sickle_cell_disease)
|
||||
return "Sickle Cell Disease"
|
||||
} else {
|
||||
return getString(R.string.invalid)
|
||||
return "Invalid"
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
showToast(R.string.error_classification)
|
||||
Firebase.crashlytics.recordException(e)
|
||||
return getString(R.string.error)
|
||||
handleException(e)
|
||||
return "Error"
|
||||
}
|
||||
return getString(R.string.invalid)
|
||||
return "Invalid"
|
||||
}
|
||||
|
||||
fun setSubtitleTextColor(colorResId: Int) {
|
||||
activity?.runOnUiThread {
|
||||
binding.tvSubtitle4.setTextColor(ContextCompat.getColor(requireContext(), colorResId))
|
||||
}
|
||||
}
|
||||
|
||||
fun handleException(e: Exception) {
|
||||
showToast(R.string.error_classification)
|
||||
Firebase.crashlytics.recordException(e)
|
||||
}
|
||||
|
||||
fun slopeRatioClassification(ratio: Double?): String {
|
||||
|
||||
9
app/src/main/res/layout/activity_kitscan_java.xml
Normal file
9
app/src/main/res/layout/activity_kitscan_java.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context="com.example.hpostesting.presentation.KitscanJava">
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -62,6 +62,7 @@
|
||||
<Spinner
|
||||
android:id="@+id/spinner_volume"
|
||||
android:layout_width="0dp"
|
||||
android:visibility="gone"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="52dp"
|
||||
android:background="@drawable/spinner_border"
|
||||
|
||||
@@ -101,7 +101,7 @@
|
||||
<string name="normal">Normal</string>
|
||||
<string name="sickle_cell_disease">Sickle Cell Disease</string>
|
||||
<string name="sickle_cell_trait">Sickle Cell Trait</string>
|
||||
<string name="undefined">Undefined\ \ </string>
|
||||
<string name="undefined">Undefined</string>
|
||||
<string name="sicklecell_nconfirmatory">Sicklecell\nConfirmatory</string>
|
||||
<string name="sicklecell_screening">Sicklecell Screening</string>
|
||||
<string name="thalassemia">Thalassemia</string>
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
package com.example.hpostesting
|
||||
|
||||
import android.content.SharedPreferences
|
||||
import com.example.hpostesting.presentation.assurance.AssuranceControlsFragment
|
||||
import junit.framework.TestCase.assertEquals
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.mockito.Mock
|
||||
import org.mockito.MockitoAnnotations
|
||||
|
||||
class AssuranceControlsFragmentTest {
|
||||
@Mock
|
||||
lateinit var sharedPreferences: SharedPreferences
|
||||
|
||||
private lateinit var assuranceControlsFragment: AssuranceControlsFragment
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
assuranceControlsFragment = AssuranceControlsFragment()
|
||||
}
|
||||
|
||||
// @Test
|
||||
// fun testInitViews() {
|
||||
// val fragmentScenario = FragmentScenario.launchInContainer(AssuranceControlsFragment::class.java)
|
||||
//
|
||||
// fragmentScenario.onFragment { fragment ->
|
||||
// // Mock the shared preferences
|
||||
// `when`(fragment.requireContext().getSharedPreferences("HEMOCUBE", Context.MODE_PRIVATE)).thenReturn(sharedPreferences)
|
||||
//
|
||||
// // Mock the solution spinner selection
|
||||
// fragment.binding.spinnerSolutions.setSelection(1)
|
||||
//
|
||||
// // Verify that DataHolder.hemoCubeTestData!!.solution is set correctly
|
||||
// assertEquals("KMnO4", DataHolder.hemoCubeTestData!!.solution)
|
||||
//
|
||||
// // You can similarly test other parts of initViews() based on your requirements
|
||||
// }
|
||||
// }
|
||||
|
||||
@Test
|
||||
fun testGetPositionOfValue() {
|
||||
val fragment = AssuranceControlsFragment()
|
||||
|
||||
val testData = listOf("Select solution", "KMnO4", "Tartrazine", "AR", "HB", "Blood")
|
||||
|
||||
// Test a value that exists in the list
|
||||
val positionKMnO4 = fragment.getPositionOfValue("KMnO4", testData)
|
||||
assertEquals(1, positionKMnO4)
|
||||
|
||||
// Test a value that does not exist in the list
|
||||
val positionNonExistent = fragment.getPositionOfValue("NonExistent", testData)
|
||||
assertEquals(0, positionNonExistent)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package com.example.hpostesting
|
||||
|
||||
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
|
||||
import androidx.lifecycle.Observer
|
||||
import com.example.hpostesting.data.Result
|
||||
import com.example.hpostesting.data.model.deviceprovision.DeviceProvisionRequest
|
||||
import com.example.hpostesting.data.model.deviceprovision.DeviceProvisionResponse
|
||||
import com.example.hpostesting.data.repository.Repository
|
||||
import com.example.hpostesting.presentation.deviceprovision.DeviceProvisionViewModel
|
||||
import com.example.hpostesting.util.TestCoroutineRule
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.runBlockingTest
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito
|
||||
import org.mockito.MockitoAnnotations
|
||||
|
||||
@ExperimentalCoroutinesApi
|
||||
class DeviceProvisionViewModelTest {
|
||||
|
||||
// Rule to make LiveData updates instant
|
||||
@get:Rule
|
||||
val instantExecutorRule = InstantTaskExecutorRule()
|
||||
|
||||
// Rule for testing coroutines
|
||||
@get:Rule
|
||||
val coroutineRule = TestCoroutineRule()
|
||||
|
||||
// Mocks
|
||||
@Mock
|
||||
private lateinit var databaseRepository: Repository
|
||||
|
||||
@Mock
|
||||
private lateinit var deviceProvisionObserver: Observer<Result<DeviceProvisionResponse>>
|
||||
|
||||
// Class under test
|
||||
private lateinit var viewModel: DeviceProvisionViewModel
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
MockitoAnnotations.openMocks(this)
|
||||
viewModel = DeviceProvisionViewModel(databaseRepository)
|
||||
viewModel.deviceProvisionResponse.observeForever(deviceProvisionObserver)
|
||||
}
|
||||
|
||||
@After
|
||||
fun tearDown() {
|
||||
viewModel.deviceProvisionResponse.removeObserver(deviceProvisionObserver)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `deviceProvision success`() = coroutineRule.runBlockingTest {
|
||||
// Arrange
|
||||
val deviceProvisionRequest = DeviceProvisionRequest(/* provide necessary parameters */)
|
||||
val expectedResult = Result.Success(DeviceProvisionResponse(/* provide necessary response data */))
|
||||
Mockito.`when`(databaseRepository.deviceProvision(deviceProvisionRequest)).thenReturn(expectedResult)
|
||||
|
||||
// Act
|
||||
viewModel.deviceProvision(deviceProvisionRequest)
|
||||
|
||||
// Assert
|
||||
// Mockito.verify(deviceProvisionObserver, Mockito.timeout(1000)).onChanged(Result.Loading())
|
||||
Mockito.verify(deviceProvisionObserver, Mockito.timeout(1000)).onChanged(expectedResult)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `deviceProvision error`() = coroutineRule.runBlockingTest {
|
||||
// Arrange
|
||||
val deviceProvisionRequest = DeviceProvisionRequest(/* provide necessary parameters */)
|
||||
val expectedError = Result.Error(Exception("Test error"))
|
||||
Mockito.`when`(databaseRepository.deviceProvision(deviceProvisionRequest)).thenReturn(expectedError)
|
||||
|
||||
// Act
|
||||
viewModel.deviceProvision(deviceProvisionRequest)
|
||||
|
||||
// Assert
|
||||
// Mockito.verify(deviceProvisionObserver, Mockito.timeout(1000)).onChanged(Result.Loading())
|
||||
Mockito.verify(deviceProvisionObserver, Mockito.timeout(1000)).onChanged(expectedError)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
package com.example.hpostesting
|
||||
|
||||
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
|
||||
import androidx.lifecycle.Observer
|
||||
import com.example.hpostesting.data.model.Response
|
||||
import com.example.hpostesting.data.model.diagnostics.DiagnosticsData
|
||||
import com.example.hpostesting.data.repository.Repository
|
||||
import com.example.hpostesting.presentation.diagnostics.DiagnosticsViewModel
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.mockk
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.test.TestCoroutineDispatcher
|
||||
import kotlinx.coroutines.test.TestCoroutineScope
|
||||
import kotlinx.coroutines.test.runBlockingTest
|
||||
import kotlinx.coroutines.test.setMain
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.mockito.Mock
|
||||
import org.mockito.MockitoAnnotations
|
||||
|
||||
@ExperimentalCoroutinesApi
|
||||
class DiagnosticsViewModelTest {
|
||||
|
||||
// Use this rule to swap the background executor used by the Architecture Components
|
||||
@get:Rule
|
||||
var instantTaskExecutorRule = InstantTaskExecutorRule()
|
||||
|
||||
// Use this dispatcher to control execution of coroutines in tests
|
||||
private val testDispatcher = TestCoroutineDispatcher()
|
||||
|
||||
// Use this scope to control the execution of coroutines in tests
|
||||
private val testCoroutineScope = TestCoroutineScope(testDispatcher)
|
||||
|
||||
// Mock Repository
|
||||
private val mockRepository = mockk<Repository>()
|
||||
|
||||
// Mock Observer for LiveData
|
||||
@Mock
|
||||
private lateinit var mockObserver: Observer<String>
|
||||
|
||||
// Subject under test
|
||||
private lateinit var diagnosticsViewModel: DiagnosticsViewModel
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
// Initialize mocks
|
||||
MockitoAnnotations.openMocks(this)
|
||||
|
||||
// Create the ViewModel with the mock repository and set the coroutine dispatcher
|
||||
diagnosticsViewModel = DiagnosticsViewModel(repository = mockRepository)
|
||||
// diagnosticsViewModel.viewModelScope = testCoroutineScope
|
||||
|
||||
// Observe the LiveData
|
||||
// diagnosticsViewModel.fireBaseUpload.observeForever(mockObserver)
|
||||
|
||||
Dispatchers.setMain(TestCoroutineDispatcher())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test addDiagnosticsDataToDb success`() = runBlocking {
|
||||
// Given
|
||||
val diagnosticsData = mockk<DiagnosticsData>()
|
||||
coEvery { mockRepository.addDiagnostics(diagnosticsData) } returns Response.Success("Success")
|
||||
|
||||
// When
|
||||
diagnosticsViewModel.addDiagnosticsDataToDb(diagnosticsData)
|
||||
|
||||
// Then
|
||||
assert(diagnosticsViewModel.fireBaseUpload.value == "Success")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `addDiagnosticsDataToDb error`() = testCoroutineScope.runBlockingTest {
|
||||
// Given
|
||||
coEvery { mockRepository.addDiagnostics(any()) } returns Response.Error(Exception("Test error"))
|
||||
|
||||
// When
|
||||
diagnosticsViewModel.addDiagnosticsDataToDb(mockk())
|
||||
|
||||
// Then
|
||||
assert(diagnosticsViewModel.fireBaseUpload.value == "Error")
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,13 @@
|
||||
package com.example.hpostesting
|
||||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import com.example.hpostesting.presentation.hemocube.HemoCubeFragment
|
||||
import com.example.hpostesting.presentation.hemocube.HemocubeActivity
|
||||
import `in`.sminnovations.hpostesting.databinding.FragmentHemoCubeReferenceBinding
|
||||
import junit.framework.TestCase
|
||||
import junit.framework.TestCase.assertEquals
|
||||
import junit.framework.TestCase.assertNull
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.mockito.ArgumentMatchers
|
||||
@@ -14,6 +17,9 @@ import org.mockito.MockitoAnnotations
|
||||
|
||||
class HemoCubeFragmentTest {
|
||||
|
||||
@Mock
|
||||
lateinit var mockContext: Context
|
||||
|
||||
@Mock
|
||||
private lateinit var mockSharedPreferences: SharedPreferences
|
||||
|
||||
@@ -32,7 +38,7 @@ class HemoCubeFragmentTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `extractMiddleString to get device id`() {
|
||||
fun `extractV2HardwareId to get device id`() {
|
||||
// Arrange
|
||||
Mockito.`when`(
|
||||
mockSharedPreferences.getString(
|
||||
@@ -42,7 +48,7 @@ class HemoCubeFragmentTest {
|
||||
).thenReturn("dummy_value")
|
||||
|
||||
// Act
|
||||
val deviceId = hemoCubeFragment.extractMiddleString("SNS HPP1-9000 SNE")
|
||||
val deviceId = hemoCubeFragment.extractV2HardwareId("SNS HPP1-9000 SNE")
|
||||
|
||||
// Assert
|
||||
TestCase.assertEquals("HPP1-9000", deviceId)
|
||||
@@ -81,4 +87,274 @@ class HemoCubeFragmentTest {
|
||||
TestCase.assertEquals(true, result)
|
||||
TestCase.assertEquals(hemoCubeFragment.allReadingsComplete(0, 1), false)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `extractV1HardwareId should return hardware ID when input contains SN`() {
|
||||
// Arrange
|
||||
val input = "Some text SN ABC123 some more text"
|
||||
|
||||
// Act
|
||||
val result = hemoCubeFragment.extractV1HardwareId(input)
|
||||
|
||||
// Assert
|
||||
assertEquals("ABC123", result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `extractV1HardwareId should return null when input does not contain SN`() {
|
||||
// Arrange
|
||||
val input = "Some text without SN"
|
||||
|
||||
// Act
|
||||
val result = hemoCubeFragment.extractV1HardwareId(input)
|
||||
|
||||
// Assert
|
||||
assertNull(result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `extractV1HardwareId should return null when input is empty`() {
|
||||
// Arrange
|
||||
val input = ""
|
||||
|
||||
// Act
|
||||
val result = hemoCubeFragment.extractV1HardwareId(input)
|
||||
|
||||
// Assert
|
||||
assertNull(result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `extractV1HardwareId should return null when input is null`() {
|
||||
// Arrange
|
||||
val input: String? = null
|
||||
|
||||
// Act
|
||||
val result = input?.let { hemoCubeFragment.extractV1HardwareId(it) }
|
||||
|
||||
// Assert
|
||||
assertNull(result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `extractV1HardwareId should return hardware ID when input contains SN in a specific format`() {
|
||||
// Arrange
|
||||
val input = """
|
||||
SN HCV-000-3001
|
||||
#BS
|
||||
#BC
|
||||
#SS
|
||||
#SC
|
||||
RESULT
|
||||
LB1 20636.32
|
||||
LB2 15855.67
|
||||
LB3 21801.36
|
||||
LB4 18362.33
|
||||
LS1 17287
|
||||
LS2 14855.67
|
||||
LS3 15282.31
|
||||
LS4 9737.98
|
||||
REND
|
||||
""".trimIndent()
|
||||
|
||||
// Act
|
||||
val result = hemoCubeFragment.extractV1HardwareId(input)
|
||||
|
||||
// Assert
|
||||
assertEquals("HCV-000-3001", result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `extractV2HardwareId should return the correct hardware ID when it exists in the input`() {
|
||||
// Arrange
|
||||
val input = "SNS ABC123 SNE"
|
||||
|
||||
// Act
|
||||
val result = hemoCubeFragment.extractV2HardwareId(input)
|
||||
|
||||
// Assert
|
||||
assertEquals("ABC123", result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `extractV2HardwareId should return null when no hardware ID is found in the input`() {
|
||||
// Arrange
|
||||
val input = "No hardware ID in this input"
|
||||
|
||||
// Act
|
||||
val result = hemoCubeFragment.extractV2HardwareId(input)
|
||||
|
||||
// Assert
|
||||
assertNull(result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `extractV2HardwareId should handle whitespace around the hardware ID`() {
|
||||
// Arrange
|
||||
val input = "SNS XYZ789 SNE"
|
||||
|
||||
// Act
|
||||
val result = hemoCubeFragment.extractV2HardwareId(input)
|
||||
|
||||
// Assert
|
||||
assertEquals("XYZ789", result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `extractV2HardwareId should handle provided input string with HCV-000-3013`() {
|
||||
// Arrange
|
||||
val input = "SNS HCV-000-3013 SNE\n" +
|
||||
"#SS1\n" +
|
||||
"#SC1\n" +
|
||||
"RESULT \n" +
|
||||
"LB1 23411.00\n" +
|
||||
"LB2 21417.00\n" +
|
||||
"LB3 23869.00\n" +
|
||||
"LB4 24967.00\n" +
|
||||
"LS1 3401.00\n" +
|
||||
"LS2 1107.00\n" +
|
||||
"LS3 14410.00\n" +
|
||||
"LS4 15047.00\n" +
|
||||
"REND\n"
|
||||
|
||||
// Act
|
||||
val result = hemoCubeFragment.extractV2HardwareId(input)
|
||||
|
||||
// Assert
|
||||
assertEquals("HCV-000-3013", result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `extractV2HardwareId should handle provided input string with HPP1-4001`() {
|
||||
// Arrange
|
||||
val input = "SNS HPP1-4001 SNE#SS1\n" +
|
||||
"#SC1\n" +
|
||||
"RESULT\n" +
|
||||
"LB1 23777\n" +
|
||||
"LB2 24130\n" +
|
||||
"LB3 23442\n" +
|
||||
"LB4 23945\n" +
|
||||
"LS1 2521\n" +
|
||||
"LS2 973\n" +
|
||||
"LS3 10252\n" +
|
||||
"LS4 11017\n" +
|
||||
"REND\n"
|
||||
|
||||
// Act
|
||||
val result = hemoCubeFragment.extractV2HardwareId(input)
|
||||
|
||||
// Assert
|
||||
assertEquals("HPP1-4001", result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `extractV2HardwareId should handle provided input string with HPP1-000-4001`() {
|
||||
// Arrange
|
||||
val input = "SNS HPP1-000-4001 SNE#SS1\n" +
|
||||
"#SC1\n" +
|
||||
"RESULT\n" +
|
||||
"LB1 23777\n" +
|
||||
"LB2 24130\n" +
|
||||
"LB3 23442\n" +
|
||||
"LB4 23945\n" +
|
||||
"LS1 2521\n" +
|
||||
"LS2 973\n" +
|
||||
"LS3 10252\n" +
|
||||
"LS4 11017\n" +
|
||||
"REND\n"
|
||||
|
||||
// Act
|
||||
val result = hemoCubeFragment.extractV2HardwareId(input)
|
||||
|
||||
// Assert
|
||||
assertEquals("HPP1-000-4001", result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `extractV2HardwareId should handle provided input string with HPP-000-4001`() {
|
||||
// Arrange
|
||||
val input = "SNS HPP-000-4001 SNE#SS1\n" +
|
||||
"#SC1\n" +
|
||||
"RESULT\n" +
|
||||
"LB1 23777\n" +
|
||||
"LB2 24130\n" +
|
||||
"LB3 23442\n" +
|
||||
"LB4 23945\n" +
|
||||
"LS1 2521\n" +
|
||||
"LS2 973\n" +
|
||||
"LS3 10252\n" +
|
||||
"LS4 11017\n" +
|
||||
"REND\n"
|
||||
|
||||
// Act
|
||||
val result = hemoCubeFragment.extractV2HardwareId(input)
|
||||
|
||||
// Assert
|
||||
assertEquals("HPP-000-4001", result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `extractV2HardwareId should handle provided input string with HPP-000-5001`() {
|
||||
// Arrange
|
||||
val input = "SNS HPP-000-5001 SNE#SS1\n" +
|
||||
"#SC1\n" +
|
||||
"RESULT\n" +
|
||||
"LB1 23777\n" +
|
||||
"LB2 24130\n" +
|
||||
"LB3 23442\n" +
|
||||
"LB4 23945\n" +
|
||||
"LS1 2521\n" +
|
||||
"LS2 973\n" +
|
||||
"LS3 10252\n" +
|
||||
"LS4 11017\n" +
|
||||
"REND\n"
|
||||
|
||||
// Act
|
||||
val result = hemoCubeFragment.extractV2HardwareId(input)
|
||||
|
||||
// Assert
|
||||
assertEquals("HPP-000-5001", result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDeviceRatioClassificationNormal() {
|
||||
val ratio = 0.25
|
||||
val result = hemoCubeFragment.deviceRatioClassification(ratio)
|
||||
assertEquals("Normal", result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDeviceRatioClassificationNegativeBorderline() {
|
||||
val ratio = 0.31
|
||||
val result = hemoCubeFragment.deviceRatioClassification(ratio)
|
||||
assertEquals("Negative Borderline, Repeat Test", result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDeviceRatioClassificationSickleCellTrait() {
|
||||
val ratio = 0.34
|
||||
val result = hemoCubeFragment.deviceRatioClassification(ratio)
|
||||
assertEquals("Sickle Cell Trait", result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDeviceRatioClassificationPositiveForSickleCell() {
|
||||
val ratio = 0.37
|
||||
val result = hemoCubeFragment.deviceRatioClassification(ratio)
|
||||
assertEquals("Positive for Sickle Cell. HPLC for Confirmation", result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDeviceRatioClassificationSickleCellDisease() {
|
||||
val ratio = 0.45
|
||||
val result = hemoCubeFragment.deviceRatioClassification(ratio)
|
||||
assertEquals("Sickle Cell Disease", result)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDeviceRatioClassificationInvalid() {
|
||||
val ratio: Double? = null
|
||||
val result = hemoCubeFragment.deviceRatioClassification(ratio)
|
||||
assertEquals("Invalid", result)
|
||||
}
|
||||
}
|
||||
Binary file not shown.
2
barcode_scanner_library_v2.0.8.0/build.gradle
Normal file
2
barcode_scanner_library_v2.0.8.0/build.gradle
Normal file
@@ -0,0 +1,2 @@
|
||||
configurations.maybeCreate("default")
|
||||
artifacts.add("default", file('barcode_scanner_library_v2.0.8.0.aar'))
|
||||
Reference in New Issue
Block a user