1 /**
<lambda>null2 * Copyright (C) 2022 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 * in compliance with the License. You may obtain a copy of the License at
6 *
7 * ```
8 * http://www.apache.org/licenses/LICENSE-2.0
9 * ```
10 *
11 * Unless required by applicable law or agreed to in writing, software distributed under the License
12 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
13 * or implied. See the License for the specific language governing permissions and limitations under
14 * the License.
15 */
16 package com.android.healthconnect.controller.tests.utils
17
18 import android.health.connect.datatypes.BasalMetabolicRateRecord
19 import android.health.connect.datatypes.BodyTemperatureMeasurementLocation
20 import android.health.connect.datatypes.BodyTemperatureRecord
21 import android.health.connect.datatypes.BodyWaterMassRecord
22 import android.health.connect.datatypes.DataOrigin
23 import android.health.connect.datatypes.Device
24 import android.health.connect.datatypes.DistanceRecord
25 import android.health.connect.datatypes.ExerciseCompletionGoal
26 import android.health.connect.datatypes.ExercisePerformanceGoal
27 import android.health.connect.datatypes.ExerciseSegmentType
28 import android.health.connect.datatypes.ExerciseSessionType
29 import android.health.connect.datatypes.FhirResource
30 import android.health.connect.datatypes.FhirResource.FHIR_RESOURCE_TYPE_IMMUNIZATION
31 import android.health.connect.datatypes.FhirVersion
32 import android.health.connect.datatypes.HeartRateRecord
33 import android.health.connect.datatypes.HydrationRecord
34 import android.health.connect.datatypes.IntermenstrualBleedingRecord
35 import android.health.connect.datatypes.MedicalDataSource
36 import android.health.connect.datatypes.MedicalResource
37 import android.health.connect.datatypes.Metadata
38 import android.health.connect.datatypes.OxygenSaturationRecord
39 import android.health.connect.datatypes.PlannedExerciseBlock
40 import android.health.connect.datatypes.PlannedExerciseSessionRecord
41 import android.health.connect.datatypes.PlannedExerciseStep
42 import android.health.connect.datatypes.Record
43 import android.health.connect.datatypes.SleepSessionRecord
44 import android.health.connect.datatypes.StepsRecord
45 import android.health.connect.datatypes.TotalCaloriesBurnedRecord
46 import android.health.connect.datatypes.WeightRecord
47 import android.health.connect.datatypes.units.Energy
48 import android.health.connect.datatypes.units.Length
49 import android.health.connect.datatypes.units.Mass
50 import android.health.connect.datatypes.units.Percentage
51 import android.health.connect.datatypes.units.Power
52 import android.health.connect.datatypes.units.Temperature
53 import android.health.connect.datatypes.units.Velocity
54 import android.health.connect.datatypes.units.Volume
55 import android.net.Uri
56 import androidx.test.platform.app.InstrumentationRegistry
57 import androidx.test.uiautomator.UiDevice
58 import com.android.healthconnect.controller.dataentries.units.PowerConverter
59 import com.android.healthconnect.controller.permissions.data.FitnessPermissionType
60 import com.android.healthconnect.controller.permissions.data.HealthPermission
61 import com.android.healthconnect.controller.shared.app.AppMetadata
62 import com.android.healthconnect.controller.utils.TimeSource
63 import com.android.healthconnect.controller.utils.randomInstant
64 import com.android.healthconnect.controller.utils.toInstant
65 import com.android.healthconnect.controller.utils.toLocalDateTime
66 import com.google.common.truth.Truth.assertThat
67 import java.time.Instant
68 import java.time.LocalDate
69 import java.time.ZoneOffset
70 import java.util.UUID
71 import kotlin.random.Random
72 import org.mockito.Mockito
73
74 val NOW: Instant = Instant.parse("2022-10-20T07:06:05.432Z")
75 val MIDNIGHT: Instant = Instant.parse("2022-10-20T00:00:00.000Z")
76
77 fun getHeartRateRecord(heartRateValues: List<Long>, startTime: Instant = NOW): HeartRateRecord {
78 return HeartRateRecord.Builder(
79 getMetaData(),
80 startTime,
81 startTime.plusSeconds(2),
82 heartRateValues.map { HeartRateRecord.HeartRateSample(it, NOW) },
83 )
84 .build()
85 }
86
getStepsRecordnull87 fun getStepsRecord(steps: Long, time: Instant = NOW): StepsRecord {
88 return StepsRecord.Builder(getMetaData(), time, time.plusSeconds(2), steps).build()
89 }
90
getStepsRecordWithUniqueIdsnull91 fun getStepsRecordWithUniqueIds(steps: Long, time: Instant = NOW): StepsRecord {
92 return StepsRecord.Builder(getMetaDataWithUniqueIds(), time, time.plusSeconds(2), steps).build()
93 }
94
getBasalMetabolicRateRecordnull95 fun getBasalMetabolicRateRecord(calories: Long): BasalMetabolicRateRecord {
96 val watts = PowerConverter.convertWattsFromCalories(calories)
97 return BasalMetabolicRateRecord.Builder(getMetaData(), NOW, Power.fromWatts(watts)).build()
98 }
99
getDistanceRecordnull100 fun getDistanceRecord(distance: Length, time: Instant = NOW): DistanceRecord {
101 return DistanceRecord.Builder(getMetaData(), time, time.plusSeconds(2), distance).build()
102 }
103
getTotalCaloriesBurnedRecordnull104 fun getTotalCaloriesBurnedRecord(calories: Energy, time: Instant = NOW): TotalCaloriesBurnedRecord {
105 return TotalCaloriesBurnedRecord.Builder(getMetaData(), time, time.plusSeconds(2), calories)
106 .build()
107 }
108
getSleepSessionRecordnull109 fun getSleepSessionRecord(startTime: Instant = NOW): SleepSessionRecord {
110 val endTime = startTime.toLocalDateTime().plusHours(8).toInstant()
111 return SleepSessionRecord.Builder(getMetaData(), startTime, endTime).build()
112 }
113
getSleepSessionRecordnull114 fun getSleepSessionRecord(startTime: Instant, endTime: Instant): SleepSessionRecord {
115 return SleepSessionRecord.Builder(getMetaData(), startTime, endTime).build()
116 }
117
getWeightRecordnull118 fun getWeightRecord(time: Instant = NOW, weight: Mass): WeightRecord {
119 return WeightRecord.Builder(getMetaData(), time, weight).build()
120 }
121
getIntermenstrualBleedingRecordnull122 fun getIntermenstrualBleedingRecord(time: Instant): IntermenstrualBleedingRecord {
123 return IntermenstrualBleedingRecord.Builder(getMetaData(), time).build()
124 }
125
getBodyTemperatureRecordnull126 fun getBodyTemperatureRecord(
127 time: Instant,
128 location: Int,
129 temperature: Temperature,
130 ): BodyTemperatureRecord {
131 return BodyTemperatureRecord.Builder(getMetaData(), time, location, temperature).build()
132 }
133
getOxygenSaturationRecordnull134 fun getOxygenSaturationRecord(time: Instant, percentage: Percentage): OxygenSaturationRecord {
135 return OxygenSaturationRecord.Builder(getMetaData(), time, percentage).build()
136 }
137
getHydrationRecordnull138 fun getHydrationRecord(startTime: Instant, endTime: Instant, volume: Volume): HydrationRecord {
139 return HydrationRecord.Builder(getMetaData(), startTime, endTime, volume).build()
140 }
141
getBodyWaterMassRecordnull142 fun getBodyWaterMassRecord(time: Instant, bodyWaterMass: Mass): BodyWaterMassRecord {
143 return BodyWaterMassRecord.Builder(getMetaData(), time, bodyWaterMass).build()
144 }
145
getRandomRecordnull146 fun getRandomRecord(fitnessPermissionType: FitnessPermissionType, date: LocalDate): Record {
147 return when (fitnessPermissionType) {
148 FitnessPermissionType.STEPS ->
149 getStepsRecord(Random.nextLong(0, 5000), date.randomInstant())
150 FitnessPermissionType.DISTANCE ->
151 getDistanceRecord(
152 Length.fromMeters(Random.nextDouble(0.0, 5000.0)),
153 date.randomInstant(),
154 )
155 FitnessPermissionType.TOTAL_CALORIES_BURNED ->
156 getTotalCaloriesBurnedRecord(
157 Energy.fromCalories(Random.nextDouble(1500.0, 5000.0)),
158 date.randomInstant(),
159 )
160 FitnessPermissionType.SLEEP -> getSleepSessionRecord(date.randomInstant())
161 else ->
162 throw IllegalArgumentException(
163 "HealthPermissionType $fitnessPermissionType not supported"
164 )
165 }
166 }
167
getSamplePlannedExerciseSessionRecordnull168 fun getSamplePlannedExerciseSessionRecord(): PlannedExerciseSessionRecord {
169 val exerciseBlock1 =
170 getPlannedExerciseBlock(
171 repetitions = 1,
172 description = "Warm up",
173 exerciseSteps =
174 listOf(
175 getPlannedExerciseStep(
176 exerciseSegmentType = ExerciseSegmentType.EXERCISE_SEGMENT_TYPE_RUNNING,
177 completionGoal =
178 ExerciseCompletionGoal.DistanceGoal(Length.fromMeters(1000.0)),
179 performanceGoals =
180 listOf(
181 ExercisePerformanceGoal.HeartRateGoal(100, 150),
182 ExercisePerformanceGoal.SpeedGoal(
183 Velocity.fromMetersPerSecond(25.0),
184 Velocity.fromMetersPerSecond(15.0),
185 ),
186 ),
187 )
188 ),
189 )
190 val exerciseBlock2 =
191 getPlannedExerciseBlock(
192 repetitions = 1,
193 description = "Main set",
194 exerciseSteps =
195 listOf(
196 getPlannedExerciseStep(
197 exerciseSegmentType = ExerciseSegmentType.EXERCISE_SEGMENT_TYPE_RUNNING,
198 completionGoal =
199 ExerciseCompletionGoal.DistanceGoal(Length.fromMeters(4000.0)),
200 performanceGoals =
201 listOf(
202 ExercisePerformanceGoal.HeartRateGoal(150, 180),
203 ExercisePerformanceGoal.SpeedGoal(
204 Velocity.fromMetersPerSecond(50.0),
205 Velocity.fromMetersPerSecond(25.0),
206 ),
207 ),
208 )
209 ),
210 )
211 val exerciseBlocks = listOf(exerciseBlock1, exerciseBlock2)
212
213 return getPlannedExerciseSessionRecord(
214 title = "Morning Run",
215 note = "Morning quick run by the park",
216 exerciseBlocks = exerciseBlocks,
217 )
218 }
219
getPlannedExerciseSessionRecordnull220 fun getPlannedExerciseSessionRecord(
221 title: String,
222 note: String,
223 exerciseBlocks: List<PlannedExerciseBlock>,
224 ): PlannedExerciseSessionRecord {
225 return basePlannedExerciseSession(ExerciseSessionType.EXERCISE_SESSION_TYPE_RUNNING)
226 .setTitle(title)
227 .setNotes(note)
228 .setBlocks(exerciseBlocks)
229 .build()
230 }
231
basePlannedExerciseSessionnull232 private fun basePlannedExerciseSession(exerciseType: Int): PlannedExerciseSessionRecord.Builder {
233 val builder: PlannedExerciseSessionRecord.Builder =
234 PlannedExerciseSessionRecord.Builder(
235 getMetaData(),
236 exerciseType,
237 NOW,
238 NOW.plusSeconds(3600),
239 )
240 builder.setNotes("Sample training plan notes")
241 builder.setTitle("Training plan title")
242 builder.setStartZoneOffset(ZoneOffset.UTC)
243 builder.setEndZoneOffset(ZoneOffset.UTC)
244 return builder
245 }
246
getPlannedExerciseBlocknull247 fun getPlannedExerciseBlock(
248 repetitions: Int,
249 description: String,
250 exerciseSteps: List<PlannedExerciseStep>,
251 ): PlannedExerciseBlock {
252 return PlannedExerciseBlock.Builder(repetitions)
253 .setDescription(description)
254 .setSteps(exerciseSteps)
255 .build()
256 }
257
getPlannedExerciseStepnull258 fun getPlannedExerciseStep(
259 exerciseSegmentType: Int,
260 completionGoal: ExerciseCompletionGoal,
261 performanceGoals: List<ExercisePerformanceGoal>,
262 ): PlannedExerciseStep {
263 return PlannedExerciseStep.Builder(
264 exerciseSegmentType,
265 PlannedExerciseStep.EXERCISE_CATEGORY_ACTIVE,
266 completionGoal,
267 )
268 .setPerformanceGoals(performanceGoals)
269 .build()
270 }
271
getMetaDatanull272 fun getMetaData(): Metadata {
273 return getMetaData(TEST_APP_PACKAGE_NAME)
274 }
275
getMetaDataWithUniqueIdsnull276 fun getMetaDataWithUniqueIds(): Metadata {
277 return getMetaDataWithUniqueIds(TEST_APP_PACKAGE_NAME)
278 }
279
getMetaDatanull280 fun getMetaData(packageName: String): Metadata {
281 val device: Device =
282 Device.Builder().setManufacturer("google").setModel("Pixel4a").setType(2).build()
283 val dataOrigin = DataOrigin.Builder().setPackageName(packageName).build()
284 return Metadata.Builder()
285 .setId("test_id")
286 .setDevice(device)
287 .setDataOrigin(dataOrigin)
288 .setClientRecordId("BMR" + Math.random().toString())
289 .build()
290 }
291
getMetaDataWithUniqueIdsnull292 fun getMetaDataWithUniqueIds(packageName: String): Metadata {
293 val device: Device =
294 Device.Builder().setManufacturer("google").setModel("Pixel4a").setType(2).build()
295 val dataOrigin = DataOrigin.Builder().setPackageName(packageName).build()
296 return Metadata.Builder()
297 .setId(getUniqueId())
298 .setDevice(device)
299 .setDataOrigin(dataOrigin)
300 .setClientRecordId("BMR" + Math.random().toString())
301 .build()
302 }
303
getUniqueIdnull304 fun getUniqueId(): String {
305 return UUID.randomUUID().toString()
306 }
307
getDataOriginnull308 fun getDataOrigin(packageName: String): DataOrigin =
309 DataOrigin.Builder().setPackageName(packageName).build()
310
311 fun getSleepSessionRecords(inputDates: List<Pair<Instant, Instant>>): List<SleepSessionRecord> {
312 val result = arrayListOf<SleepSessionRecord>()
313 inputDates.forEach { (startTime, endTime) ->
314 result.add(SleepSessionRecord.Builder(getMetaData(), startTime, endTime).build())
315 }
316
317 return result
318 }
319
verifySleepSessionListsEqualnull320 fun verifySleepSessionListsEqual(actual: List<Record>, expected: List<SleepSessionRecord>) {
321 assertThat(actual.size).isEqualTo(expected.size)
322 for ((index, element) in actual.withIndex()) {
323 assertThat(element is SleepSessionRecord).isTrue()
324 val expectedElement = expected[index]
325 val actualElement = element as SleepSessionRecord
326
327 assertThat(actualElement.startTime).isEqualTo(expectedElement.startTime)
328 assertThat(actualElement.endTime).isEqualTo(expectedElement.endTime)
329 assertThat(actualElement.notes).isEqualTo(expectedElement.notes)
330 assertThat(actualElement.title).isEqualTo(expectedElement.title)
331 assertThat(actualElement.stages).isEqualTo(expectedElement.stages)
332 }
333 }
334
verifyOxygenSaturationListsEqualnull335 fun verifyOxygenSaturationListsEqual(actual: List<Record>, expected: List<OxygenSaturationRecord>) {
336 assertThat(actual.size).isEqualTo(expected.size)
337 for ((index, element) in actual.withIndex()) {
338 assertThat(element is OxygenSaturationRecord).isTrue()
339 val expectedElement = expected[index]
340 val actualElement = element as OxygenSaturationRecord
341
342 assertThat(actualElement.time).isEqualTo(expectedElement.time)
343 assertThat(actualElement.percentage).isEqualTo(expectedElement.percentage)
344 }
345 }
346
verifyHydrationListsEqualnull347 fun verifyHydrationListsEqual(actual: List<Record>, expected: List<HydrationRecord>) {
348 assertThat(actual.size).isEqualTo(expected.size)
349 for ((index, element) in actual.withIndex()) {
350 assertThat(element is HydrationRecord).isTrue()
351 val expectedElement = expected[index]
352 val actualElement = element as HydrationRecord
353
354 assertThat(actualElement.startTime).isEqualTo(expectedElement.startTime)
355 assertThat(actualElement.endTime).isEqualTo(expectedElement.endTime)
356 assertThat(actualElement.volume).isEqualTo(expectedElement.volume)
357 }
358 }
359
verifyBodyWaterMassListsEqualnull360 fun verifyBodyWaterMassListsEqual(actual: List<Record>, expected: List<Record>) {
361 assertThat(actual.size).isEqualTo(expected.size)
362 for ((index, element) in actual.withIndex()) {
363 assertThat(element is BodyWaterMassRecord).isTrue()
364 val expectedElement = expected[index] as BodyWaterMassRecord
365 val actualElement = element as BodyWaterMassRecord
366
367 assertThat(actualElement.time).isEqualTo(expectedElement.time)
368 assertThat(actualElement.bodyWaterMass).isEqualTo(expectedElement.bodyWaterMass)
369 }
370 }
371
372 // test data constants - start
373
374 val START_TIME = Instant.parse("2023-06-12T22:30:00Z")
375
376 // pre-defined Instants within a day, week, and month of the START_TIME Instant
377 val INSTANT_DAY: Instant = Instant.parse("2023-06-11T23:30:00Z")
378 val INSTANT_DAY2: Instant = Instant.parse("2023-06-12T02:00:00Z")
379 val INSTANT_WEEK: Instant = Instant.parse("2023-06-14T11:15:00Z")
380 val INSTANT_MONTH1: Instant = Instant.parse("2023-06-26T23:10:00Z")
381 val INSTANT_MONTH2: Instant = Instant.parse("2023-06-30T11:30:00Z")
382 val INSTANT_MONTH3: Instant = Instant.parse("2023-07-01T07:45:00Z")
383 val INSTANT_MONTH4: Instant = Instant.parse("2023-07-01T19:15:00Z")
384 val INSTANT_MONTH5: Instant = Instant.parse("2023-07-05T03:45:00Z")
385 val INSTANT_MONTH6: Instant = Instant.parse("2023-07-07T07:05:00Z")
386
387 val SLEEP_DAY_0H20 =
388 getSleepSessionRecord(
389 Instant.parse("2023-06-12T21:00:00Z"),
390 Instant.parse("2023-06-12T21:20:00Z"),
391 )
392 val SLEEP_DAY_1H45 =
393 getSleepSessionRecord(
394 Instant.parse("2023-06-12T16:00:00Z"),
395 Instant.parse("2023-06-12T17:45:00Z"),
396 )
397 val SLEEP_DAY_9H15 =
398 getSleepSessionRecord(
399 Instant.parse("2023-06-12T22:30:00Z"),
400 Instant.parse("2023-06-13T07:45:00Z"),
401 )
402 val SLEEP_WEEK_9H15 =
403 getSleepSessionRecord(
404 Instant.parse("2023-06-14T22:30:00Z"),
405 Instant.parse("2023-06-15T07:45:00Z"),
406 )
407 val SLEEP_WEEK_33H15 =
408 getSleepSessionRecord(
409 Instant.parse("2023-06-11T22:30:00Z"),
410 Instant.parse("2023-06-13T07:45:00Z"),
411 )
412 val SLEEP_MONTH_81H15 =
413 getSleepSessionRecord(
414 Instant.parse("2023-07-09T22:30:00Z"),
415 Instant.parse("2023-07-13T07:45:00Z"),
416 )
417
418 val HYDRATION_MONTH: HydrationRecord =
419 getHydrationRecord(INSTANT_MONTH1, INSTANT_MONTH2, Volume.fromLiters(2.0))
420 val HYDRATION_MONTH2: HydrationRecord =
421 getHydrationRecord(INSTANT_MONTH3, INSTANT_MONTH4, Volume.fromLiters(0.3))
422 val HYDRATION_MONTH3: HydrationRecord =
423 getHydrationRecord(INSTANT_MONTH5, INSTANT_MONTH6, Volume.fromLiters(1.5))
424
425 val OXYGENSATURATION_DAY: OxygenSaturationRecord =
426 getOxygenSaturationRecord(INSTANT_DAY, Percentage.fromValue(98.0))
427 val OXYGENSATURATION_DAY2: OxygenSaturationRecord =
428 getOxygenSaturationRecord(INSTANT_DAY2, Percentage.fromValue(95.0))
429
430 val DISTANCE_STARTDATE_1500: DistanceRecord =
431 getDistanceRecord(Length.fromMeters(1500.0), START_TIME)
432
433 val WEIGHT_DAY_100: WeightRecord = getWeightRecord(INSTANT_DAY, Mass.fromGrams(100000.0))
434 val WEIGHT_WEEK_100: WeightRecord = getWeightRecord(INSTANT_WEEK, Mass.fromGrams(100000.0))
435 val WEIGHT_MONTH_100: WeightRecord = getWeightRecord(INSTANT_MONTH3, Mass.fromGrams(100000.0))
436 val WEIGHT_STARTDATE_100: WeightRecord = getWeightRecord(START_TIME, Mass.fromGrams(100000.0))
437
438 val INTERMENSTRUAL_BLEEDING_DAY: IntermenstrualBleedingRecord =
439 getIntermenstrualBleedingRecord(INSTANT_DAY)
440
441 val BODYTEMPERATURE_MONTH: BodyTemperatureRecord =
442 getBodyTemperatureRecord(
443 INSTANT_MONTH3,
444 BodyTemperatureMeasurementLocation.MEASUREMENT_LOCATION_MOUTH,
445 Temperature.fromCelsius(100.0),
446 )
447
448 val BODYWATERMASS_WEEK: BodyWaterMassRecord =
449 getBodyWaterMassRecord(INSTANT_WEEK, Mass.fromGrams(1000.0))
450
451 // records using today's date, yesterday's date, and the date two days ago - for header testing
getMixedRecordsAcrossTwoDaysnull452 fun getMixedRecordsAcrossTwoDays(timeSource: TimeSource): List<Record> {
453 val instantToday: Instant = timeSource.currentLocalDateTime().toInstant()
454 val instantYesterday: Instant = timeSource.currentLocalDateTime().minusDays(1).toInstant()
455 return listOf(
456 getHydrationRecord(instantToday, instantToday.plusSeconds(900), Volume.fromLiters(2.0)),
457 getSleepSessionRecord(instantToday, instantToday.plusSeconds(1800)),
458 getDistanceRecord(Length.fromMeters(2500.0), instantYesterday),
459 getOxygenSaturationRecord(instantYesterday, Percentage.fromValue(99.0)),
460 )
461 }
462
getMixedRecordsAcrossThreeDaysnull463 fun getMixedRecordsAcrossThreeDays(timeSource: TimeSource): List<Record> {
464 val instantTwoDaysAgo: Instant = timeSource.currentLocalDateTime().minusDays(2).toInstant()
465 return getMixedRecordsAcrossTwoDays(timeSource)
466 .plus(
467 listOf(
468 getWeightRecord(instantTwoDaysAgo, Mass.fromGrams(95000.0)),
469 getDistanceRecord(Length.fromMeters(2000.0), instantTwoDaysAgo),
470 )
471 )
472 }
473
474 // test data constants - end
475
476 // Enables or disables animations in a test
toggleAnimationnull477 fun toggleAnimation(isEnabled: Boolean) {
478 with(UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())) {
479 executeShellCommand(
480 "settings put global transition_animation_scale ${if (isEnabled) 1 else 0}"
481 )
482 executeShellCommand("settings put global window_animation_scale ${if (isEnabled) 1 else 0}")
483 executeShellCommand(
484 "settings put global animator_duration_scale ${if (isEnabled) 1 else 0}"
485 )
486 }
487 }
488
489 // Used for matching arguments for [RequestPermissionViewModel]
anynull490 fun <T> any(type: Class<T>): T = Mockito.any<T>(type)
491
492 /** Utility function to turn an array of permission strings to a list of [HealthPermission]s */
493 fun Array<String>.toPermissionsList(): List<HealthPermission> {
494 return this.map { HealthPermission.fromPermissionString(it) }.toList()
495 }
496
497 // region apps
498
499 const val TEST_APP_PACKAGE_NAME = "android.healthconnect.controller.test.app"
500 const val TEST_APP_PACKAGE_NAME_2 = "android.healthconnect.controller.test.app2"
501 const val TEST_APP_PACKAGE_NAME_3 = "package.name.3"
502 const val UNSUPPORTED_TEST_APP_PACKAGE_NAME = "android.healthconnect.controller.test.app3"
503 const val OLD_PERMISSIONS_TEST_APP_PACKAGE_NAME = "android.healthconnect.controller.test.app4"
504 const val MEDICAL_PERMISSIONS_TEST_APP_PACKAGE_NAME = "android.healthconnect.controller.test.app5"
505 const val WEAR_LEGACY_TEST_APP_PACKAGE_NAME = "android.healthconnect.controller.test.app6"
506 const val WEAR_TEST_APP_PACKAGE_NAME = "android.healthconnect.controller.test.app7"
507 const val TEST_APP_NAME = "Health Connect test app"
508 const val TEST_APP_NAME_2 = "Health Connect test app 2"
509 const val TEST_APP_NAME_3 = "Health Connect test app 3"
510 const val OLD_APP_NAME = "Old permissions test app"
511 const val MEDICAL_APP_NAME = "Medical permissions HC app"
512
513 val TEST_APP =
514 AppMetadata(packageName = TEST_APP_PACKAGE_NAME, appName = TEST_APP_NAME, icon = null)
515 val TEST_APP_2 =
516 AppMetadata(packageName = TEST_APP_PACKAGE_NAME_2, appName = TEST_APP_NAME_2, icon = null)
517 val TEST_APP_3 =
518 AppMetadata(packageName = TEST_APP_PACKAGE_NAME_3, appName = TEST_APP_NAME_3, icon = null)
519 val OLD_TEST_APP =
520 AppMetadata(
521 packageName = OLD_PERMISSIONS_TEST_APP_PACKAGE_NAME,
522 appName = OLD_APP_NAME,
523 icon = null,
524 )
525 // endregion
526
527 // PHR
528 val TEST_DATASOURCE_ID = getUniqueId()
529 val TEST_FHIR_VERSION: FhirVersion = FhirVersion.parseFhirVersion("4.0.1")
530 val TEST_FHIR_RESOURCE_IMMUNIZATION: FhirResource =
531 FhirResource.Builder(
532 FHIR_RESOURCE_TYPE_IMMUNIZATION,
533 "Immunization1",
534 "{\"resourceType\":\"Immunization\",\"id\":\"immunization-1\",\"status\":\"completed\",\"vaccineCode\":{\"coding\":[{\"system\":\"http://hl7.org/fhir/sid/cvx\",\"code\":\"115\"},{\"system\":\"http://hl7.org/fhir/sid/ndc\",\"code\":\"58160-842-11\"}],\"text\":\"Tdap\"},\"patient\":{\"reference\":\"Patient/patient_1\",\"display\":\"Example, Anne\"},\"occurrenceDateTime\":\"2018-05-21\"}"
535 )
536 .build()
537 val TEST_FHIR_RESOURCE_IMMUNIZATION_2: FhirResource =
538 FhirResource.Builder(
539 FHIR_RESOURCE_TYPE_IMMUNIZATION,
540 "Immunization2",
541 "{\"resourceType\":\"Immunization\",\"id\":\"immunization-2\",\"status\":\"completed\",\"vaccineCode\":{\"coding\":[{\"system\":\"http://hl7.org/fhir/sid/cvx\",\"code\":\"115\"},{\"system\":\"http://hl7.org/fhir/sid/ndc\",\"code\":\"58160-842-11\"}],\"text\":\"Tdap\"},\"patient\":{\"reference\":\"Patient/patient_1\",\"display\":\"Example, Anne\"},\"occurrenceDateTime\":\"2018-05-21\"}"
542 )
543 .build()
544 val TEST_FHIR_RESOURCE_IMMUNIZATION_3: FhirResource =
545 FhirResource.Builder(
546 FHIR_RESOURCE_TYPE_IMMUNIZATION,
547 "Immunization3",
548 "{\"resourceType\":\"Immunization\",\"id\":\"immunization-3\",\"status\":\"completed\",\"vaccineCode\":{\"coding\":[{\"system\":\"http://hl7.org/fhir/sid/cvx\",\"code\":\"115\"},{\"system\":\"http://hl7.org/fhir/sid/ndc\",\"code\":\"58160-842-11\"}],\"text\":\"Tdap\"},\"patient\":{\"reference\":\"Patient/patient_1\",\"display\":\"Example, Anne\"},\"occurrenceDateTime\":\"2018-05-21\"}"
549 )
550 .build()
551 val TEST_FHIR_RESOURCE_IMMUNIZATION_LONG: FhirResource =
552 FhirResource.Builder(
553 FHIR_RESOURCE_TYPE_IMMUNIZATION,
554 "Immunization11",
555 "{\"resourceType\":\"Immunization\",\"id\":\"immunization-1\",\"status\":\"completed\",\"vaccineCode\":{\"coding\":[{\"system\":\"http://hl7.org/fhir/sid/cvx\",\"code\":\"115\"},{\"system\":\"http://hl7.org/fhir/sid/ndc\",\"code\":\"58160-842-11\"}],\"text\":\"Tdap\"},\"patient\":{\"reference\":\"Patient/patient_1\",\"display\":\"Example, Anne\"},\"encounter\":{\"reference\":\"Encounter/encounter_unk\",\"display\":\"GP Visit\"},\"occurrenceDateTime\":\"2018-05-21\",\"primarySource\":true,\"manufacturer\":{\"display\":\"Sanofi Pasteur\"},\"lotNumber\":\"1\",\"site\":{\"coding\":[{\"system\":\"http://terminology.hl7.org/CodeSystem/v3-ActSite\",\"code\":\"LA\",\"display\":\"Left Arm\"}],\"text\":\"Left Arm\"},\"route\":{\"coding\":[{\"system\":\"http://terminology.hl7.org/CodeSystem/v3-RouteOfAdministration\",\"code\":\"IM\",\"display\":\"Injection, intramuscular\"}],\"text\":\"Injection, intramuscular\"},\"doseQuantity\":{\"value\":0.5,\"unit\":\"mL\"},\"performer\":[{\"function\":{\"coding\":[{\"system\":\"http://terminology.hl7.org/CodeSystem/v2-0443\",\"code\":\"AP\",\"display\":\"Administering Provider\"}],\"text\":\"Administering Provider\"},\"actor\":{\"reference\":\"Practitioner/practitioner_1\",\"type\":\"Practitioner\",\"display\":\"Dr Maria Hernandez\"}}]}",
556 )
557 .build()
558 val TEST_FHIR_RESOURCE_INVALID_JSON: FhirResource =
559 FhirResource.Builder(
560 FHIR_RESOURCE_TYPE_IMMUNIZATION,
561 "invalid_json",
562 "{\"resourceType\" : \"Immunization\", {{{\"id\"\" : \"Immunization-3\"}",
563 )
564 .build()
565 val TEST_MEDICAL_RESOURCE_IMMUNIZATION: MedicalResource =
566 MedicalResource.Builder(
567 MedicalResource.MEDICAL_RESOURCE_TYPE_VACCINES,
568 TEST_DATASOURCE_ID,
569 TEST_FHIR_VERSION,
570 TEST_FHIR_RESOURCE_IMMUNIZATION,
571 )
572 .build()
573 val TEST_MEDICAL_RESOURCE_IMMUNIZATION_2: MedicalResource =
574 MedicalResource.Builder(
575 MedicalResource.MEDICAL_RESOURCE_TYPE_VACCINES,
576 TEST_DATASOURCE_ID,
577 TEST_FHIR_VERSION,
578 TEST_FHIR_RESOURCE_IMMUNIZATION_2,
579 )
580 .build()
581 val TEST_MEDICAL_RESOURCE_IMMUNIZATION_3: MedicalResource =
582 MedicalResource.Builder(
583 MedicalResource.MEDICAL_RESOURCE_TYPE_VACCINES,
584 TEST_DATASOURCE_ID,
585 TEST_FHIR_VERSION,
586 TEST_FHIR_RESOURCE_IMMUNIZATION_3,
587 )
588 .build()
589 val TEST_MEDICAL_RESOURCE_IMMUNIZATION_LONG: MedicalResource =
590 MedicalResource.Builder(
591 MedicalResource.MEDICAL_RESOURCE_TYPE_VACCINES,
592 TEST_DATASOURCE_ID,
593 TEST_FHIR_VERSION,
594 TEST_FHIR_RESOURCE_IMMUNIZATION_LONG,
595 )
596 .build()
597 val TEST_MEDICAL_RESOURCE_INVALID_JSON: MedicalResource =
598 MedicalResource.Builder(
599 MedicalResource.MEDICAL_RESOURCE_TYPE_VACCINES,
600 TEST_DATASOURCE_ID,
601 TEST_FHIR_VERSION,
602 TEST_FHIR_RESOURCE_INVALID_JSON,
603 )
604 .build()
605 val TEST_MEDICAL_DATA_SOURCE: MedicalDataSource =
606 MedicalDataSource.Builder(
607 /* id= */ TEST_DATASOURCE_ID,
608 TEST_APP_PACKAGE_NAME,
609 /* fhirBaseUri= */ Uri.parse("fhir.base.uri"),
610 /* displayName= */ "App A Data Source",
611 /* fhirVersion= */ TEST_FHIR_VERSION,
612 )
613 .build()
614 val TEST_MEDICAL_DATA_SOURCE_2: MedicalDataSource =
615 MedicalDataSource.Builder(
616 /* id= */ getUniqueId(),
617 TEST_APP_PACKAGE_NAME,
618 /* fhirBaseUri= */ Uri.parse("fhir.base.uri"),
619 /* displayName= */ "App A Data Source 2",
620 /* fhirVersion= */ TEST_FHIR_VERSION,
621 )
622 .build()
623 val TEST_MEDICAL_DATA_SOURCE_DIFFERENT_APP: MedicalDataSource =
624 MedicalDataSource.Builder(
625 /* id= */ getUniqueId(),
626 TEST_APP_PACKAGE_NAME_2,
627 /* fhirBaseUri= */ Uri.parse("fhir.base.uri"),
628 /* displayName= */ "App B Data Source",
629 /* fhirVersion= */ TEST_FHIR_VERSION,
630 )
631 .build()
632 // endregion
633