Skip to content

Commit 62edc50

Browse files
ananyaa06johnnzhou
andauthored
Replace Ping functionality with RTT from TCP & export data in CSV via settings (#14)
Co-authored-by: Zhennan Zhou <[email protected]>
1 parent dc5a45d commit 62edc50

File tree

22 files changed

+1231
-153
lines changed

22 files changed

+1231
-153
lines changed

app/build.gradle

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,13 +184,22 @@ dependencies {
184184
implementation 'org.bouncycastle:bcprov-jdk15to18:1.70'
185185
implementation 'org.apache.commons:commons-csv:1.9.0'
186186
implementation 'io.github.azhon:appupdate:4.3.2'
187+
implementation 'androidx.documentfile:documentfile:1.0.1'
187188

188189
// Analytics
189190
implementation "com.microsoft.appcenter:appcenter-analytics:${appCenterSdkVersion}"
190191
implementation "com.microsoft.appcenter:appcenter-crashes:${appCenterSdkVersion}"
191192

192193
// Testing
193194
testImplementation 'junit:junit:4.+'
195+
testImplementation 'androidx.work:work-testing:2.8.1'
196+
testImplementation 'io.mockk:mockk:1.13.5'
197+
testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.3'
198+
androidTestImplementation 'junit:junit:4.+'
199+
androidTestImplementation 'androidx.work:work-testing:2.8.1'
200+
androidTestImplementation 'io.mockk:mockk:1.13.5'
201+
androidTestImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.3'
202+
androidTestImplementation 'org.robolectric:robolectric:4.9'
194203
}
195204

196205
protobuf {
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
{
2+
"formatVersion": 1,
3+
"database": {
4+
"version": 1,
5+
"identityHash": "0c7a8a929af48ae178cd610468177570",
6+
"entities": [
7+
{
8+
"tableName": "signal_strength_table",
9+
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`latitude` REAL NOT NULL, `longitude` REAL NOT NULL, `time_stamp` TEXT NOT NULL, `cellId` TEXT NOT NULL, `deviceId` TEXT NOT NULL, `signal_strength` INTEGER NOT NULL, `signal_strength_level` INTEGER NOT NULL, `reported` INTEGER NOT NULL, PRIMARY KEY(`time_stamp`))",
10+
"fields": [
11+
{
12+
"fieldPath": "latitude",
13+
"columnName": "latitude",
14+
"affinity": "REAL",
15+
"notNull": true
16+
},
17+
{
18+
"fieldPath": "longitude",
19+
"columnName": "longitude",
20+
"affinity": "REAL",
21+
"notNull": true
22+
},
23+
{
24+
"fieldPath": "timestamp",
25+
"columnName": "time_stamp",
26+
"affinity": "TEXT",
27+
"notNull": true
28+
},
29+
{
30+
"fieldPath": "cellId",
31+
"columnName": "cellId",
32+
"affinity": "TEXT",
33+
"notNull": true
34+
},
35+
{
36+
"fieldPath": "deviceId",
37+
"columnName": "deviceId",
38+
"affinity": "TEXT",
39+
"notNull": true
40+
},
41+
{
42+
"fieldPath": "dbm",
43+
"columnName": "signal_strength",
44+
"affinity": "INTEGER",
45+
"notNull": true
46+
},
47+
{
48+
"fieldPath": "levelCode",
49+
"columnName": "signal_strength_level",
50+
"affinity": "INTEGER",
51+
"notNull": true
52+
},
53+
{
54+
"fieldPath": "reported",
55+
"columnName": "reported",
56+
"affinity": "INTEGER",
57+
"notNull": true
58+
}
59+
],
60+
"primaryKey": {
61+
"autoGenerate": false,
62+
"columnNames": [
63+
"time_stamp"
64+
]
65+
},
66+
"indices": [],
67+
"foreignKeys": []
68+
},
69+
{
70+
"tableName": "connectivity_table",
71+
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`latitude` REAL NOT NULL, `longitude` REAL NOT NULL, `time_stamp` TEXT NOT NULL, `cellId` TEXT NOT NULL, `deviceId` TEXT NOT NULL, `upload_speed` REAL NOT NULL, `download_speed` REAL NOT NULL, `ping` REAL NOT NULL, `package_loss` REAL NOT NULL, `reported` INTEGER NOT NULL, PRIMARY KEY(`time_stamp`))",
72+
"fields": [
73+
{
74+
"fieldPath": "latitude",
75+
"columnName": "latitude",
76+
"affinity": "REAL",
77+
"notNull": true
78+
},
79+
{
80+
"fieldPath": "longitude",
81+
"columnName": "longitude",
82+
"affinity": "REAL",
83+
"notNull": true
84+
},
85+
{
86+
"fieldPath": "timestamp",
87+
"columnName": "time_stamp",
88+
"affinity": "TEXT",
89+
"notNull": true
90+
},
91+
{
92+
"fieldPath": "cellId",
93+
"columnName": "cellId",
94+
"affinity": "TEXT",
95+
"notNull": true
96+
},
97+
{
98+
"fieldPath": "deviceId",
99+
"columnName": "deviceId",
100+
"affinity": "TEXT",
101+
"notNull": true
102+
},
103+
{
104+
"fieldPath": "uploadSpeed",
105+
"columnName": "upload_speed",
106+
"affinity": "REAL",
107+
"notNull": true
108+
},
109+
{
110+
"fieldPath": "downloadSpeed",
111+
"columnName": "download_speed",
112+
"affinity": "REAL",
113+
"notNull": true
114+
},
115+
{
116+
"fieldPath": "ping",
117+
"columnName": "ping",
118+
"affinity": "REAL",
119+
"notNull": true
120+
},
121+
{
122+
"fieldPath": "packetLoss",
123+
"columnName": "package_loss",
124+
"affinity": "REAL",
125+
"notNull": true
126+
},
127+
{
128+
"fieldPath": "reported",
129+
"columnName": "reported",
130+
"affinity": "INTEGER",
131+
"notNull": true
132+
}
133+
],
134+
"primaryKey": {
135+
"autoGenerate": false,
136+
"columnNames": [
137+
"time_stamp"
138+
]
139+
},
140+
"indices": [],
141+
"foreignKeys": []
142+
}
143+
],
144+
"views": [],
145+
"setupQueries": [
146+
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
147+
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '0c7a8a929af48ae178cd610468177570')"
148+
]
149+
}
150+
}
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
package com.lcl.lclmeasurementtool.sync
2+
3+
import android.content.Context
4+
import androidx.test.core.app.ApplicationProvider
5+
import androidx.work.*
6+
import androidx.work.testing.TestWorkerBuilder
7+
import io.mockk.coEvery
8+
import io.mockk.mockk
9+
import kotlinx.coroutines.ExperimentalCoroutinesApi
10+
import kotlinx.coroutines.test.TestCoroutineDispatcher
11+
import kotlinx.coroutines.test.runTest
12+
import org.junit.Assert.assertEquals
13+
import org.junit.Before
14+
import org.junit.Test
15+
import org.junit.runner.RunWith
16+
import org.robolectric.RobolectricTestRunner
17+
import retrofit2.HttpException
18+
import java.io.IOException
19+
20+
@RunWith(RobolectricTestRunner::class)
21+
@ExperimentalCoroutinesApi
22+
class UploadWorkerTest {
23+
24+
private lateinit var context: Context
25+
private lateinit var signalRepo: SignalStrengthRepository
26+
private lateinit var connectivityRepo: ConnectivityRepository
27+
private lateinit var ioDispatcher: TestCoroutineDispatcher
28+
29+
@Before
30+
fun setUp() {
31+
context = ApplicationProvider.getApplicationContext()
32+
signalRepo = mockk()
33+
connectivityRepo = mockk()
34+
ioDispatcher = TestCoroutineDispatcher()
35+
}
36+
37+
private fun createWorker(runAttemptCount: Int = 0): UploadWorker {
38+
return TestWorkerBuilder<UploadWorker>(context)
39+
.setWorkerFactory(object : WorkerFactory() {
40+
override fun createWorker(
41+
appContext: Context,
42+
workerClassName: String,
43+
workerParameters: WorkerParameters
44+
): ListenableWorker? {
45+
return UploadWorker(appContext, workerParameters, signalRepo, connectivityRepo, ioDispatcher)
46+
}
47+
})
48+
.setInputData(Data.EMPTY)
49+
.setRunAttemptCount(runAttemptCount)
50+
.build()
51+
}
52+
53+
@Test
54+
fun testDoWork_success() = runTest {
55+
coEvery { signalRepo.sync() } returns true
56+
coEvery { connectivityRepo.sync() } returns true
57+
58+
val worker = createWorker()
59+
val result = worker.doWork()
60+
61+
assertEquals(Result.success(), result)
62+
}
63+
64+
@Test
65+
fun testDoWork_failure_syncFails() = runTest {
66+
coEvery { signalRepo.sync() } returns false
67+
coEvery { connectivityRepo.sync() } returns true
68+
69+
val worker = createWorker()
70+
val result = worker.doWork()
71+
72+
assertEquals(Result.failure(), result)
73+
}
74+
75+
@Test
76+
fun testDoWork_retryOnIOException() = runTest {
77+
coEvery { signalRepo.sync() } throws IOException("Test")
78+
coEvery { connectivityRepo.sync() } returns true
79+
80+
val worker = createWorker()
81+
val result = worker.doWork()
82+
83+
assertEquals(Result.retry(), result)
84+
}
85+
86+
@Test
87+
fun testDoWork_retryOnHttpException() = runTest {
88+
coEvery { signalRepo.sync() } throws HttpException(mockk())
89+
coEvery { connectivityRepo.sync() } returns true
90+
91+
val worker = createWorker()
92+
val result = worker.doWork()
93+
94+
assertEquals(Result.retry(), result)
95+
}
96+
97+
@Test
98+
fun testDoWork_failureOnInvalidKeySpecException() = runTest {
99+
coEvery { signalRepo.sync() } throws java.security.spec.InvalidKeySpecException("Test")
100+
coEvery { connectivityRepo.sync() } returns true
101+
102+
val worker = createWorker()
103+
val result = worker.doWork()
104+
105+
assertEquals(Result.failure(), result)
106+
}
107+
108+
@Test
109+
fun testDoWork_failureOnInvalidKeyException() = runTest {
110+
coEvery { signalRepo.sync() } throws java.security.InvalidKeyException("Test")
111+
coEvery { connectivityRepo.sync() } returns true
112+
113+
val worker = createWorker()
114+
val result = worker.doWork()
115+
116+
assertEquals(Result.failure(), result)
117+
}
118+
119+
@Test
120+
fun testDoWork_failureOnIllegalStateException() = runTest {
121+
coEvery { signalRepo.sync() } throws IllegalStateException("Test")
122+
coEvery { connectivityRepo.sync() } returns true
123+
124+
val worker = createWorker()
125+
val result = worker.doWork()
126+
127+
assertEquals(Result.failure(), result)
128+
}
129+
130+
@Test
131+
fun testDoWork_failureOnGenericException() = runTest {
132+
coEvery { signalRepo.sync() } throws Exception("Test")
133+
coEvery { connectivityRepo.sync() } returns true
134+
135+
val worker = createWorker()
136+
val result = worker.doWork()
137+
138+
assertEquals(Result.failure(), result)
139+
}
140+
141+
@Test
142+
fun testDoWork_failureOnMaxRetries() = runTest {
143+
coEvery { signalRepo.sync() } throws IOException("Test")
144+
coEvery { connectivityRepo.sync() } returns true
145+
146+
val worker = createWorker(runAttemptCount = 5)
147+
val result = worker.doWork()
148+
149+
assertEquals(Result.failure(), result)
150+
}
151+
}

app/src/main/AndroidManifest.xml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
<uses-permission android:name="android.permission.VIBRATE" />
2020
<uses-permission android:name="android.permission.WAKE_LOCK" />
2121
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
22+
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
2223
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
2324
<permission-group android:name="${applicationId}.andpermission"/>
2425
<queries>
@@ -51,7 +52,7 @@
5152
android:grantUriPermissions="true">
5253
<meta-data
5354
android:name="android.support.FILE_PROVIDER_PATHS"
54-
android:resource="@xml/filepaths" />
55+
android:resource="@xml/file_paths" />
5556
</provider>
5657

5758
<provider

app/src/main/java/com/lcl/lclmeasurementtool/MainActivity2.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ class MainActivity2 : ComponentActivity() {
5555

5656
if (!hasPermission()) {
5757
XXPermissions.with(this)
58-
.permission(Permission.CAMERA, Permission.READ_EXTERNAL_STORAGE, Permission.ACCESS_FINE_LOCATION)
58+
.permission(Permission.CAMERA, Permission.READ_MEDIA_AUDIO, Permission.READ_MEDIA_VIDEO, Permission.READ_MEDIA_IMAGES, Permission.ACCESS_FINE_LOCATION)
5959
.request { _, allGranted ->
6060
run {
6161
if (!allGranted) {

0 commit comments

Comments
 (0)