1 /* 2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.healthconnect.cts.datatypes; 18 19 import static android.health.connect.datatypes.CyclingPedalingCadenceRecord.RPM_AVG; 20 import static android.health.connect.datatypes.CyclingPedalingCadenceRecord.RPM_MAX; 21 import static android.health.connect.datatypes.CyclingPedalingCadenceRecord.RPM_MIN; 22 23 import static com.google.common.truth.Truth.assertThat; 24 25 import android.content.Context; 26 import android.health.connect.AggregateRecordsRequest; 27 import android.health.connect.AggregateRecordsResponse; 28 import android.health.connect.DeleteUsingFiltersRequest; 29 import android.health.connect.HealthConnectException; 30 import android.health.connect.HealthDataCategory; 31 import android.health.connect.ReadRecordsRequestUsingFilters; 32 import android.health.connect.ReadRecordsRequestUsingIds; 33 import android.health.connect.RecordIdFilter; 34 import android.health.connect.TimeInstantRangeFilter; 35 import android.health.connect.changelog.ChangeLogTokenRequest; 36 import android.health.connect.changelog.ChangeLogTokenResponse; 37 import android.health.connect.changelog.ChangeLogsRequest; 38 import android.health.connect.changelog.ChangeLogsResponse; 39 import android.health.connect.datatypes.AggregationType; 40 import android.health.connect.datatypes.CyclingPedalingCadenceRecord; 41 import android.health.connect.datatypes.DataOrigin; 42 import android.health.connect.datatypes.Device; 43 import android.health.connect.datatypes.Metadata; 44 import android.health.connect.datatypes.Record; 45 import android.healthconnect.cts.utils.AssumptionCheckerRule; 46 import android.healthconnect.cts.utils.TestUtils; 47 import android.platform.test.annotations.AppModeFull; 48 49 import androidx.test.core.app.ApplicationProvider; 50 import androidx.test.runner.AndroidJUnit4; 51 52 import org.junit.After; 53 import org.junit.Assert; 54 import org.junit.Before; 55 import org.junit.Rule; 56 import org.junit.Test; 57 import org.junit.runner.RunWith; 58 59 import java.time.Instant; 60 import java.time.ZoneOffset; 61 import java.time.temporal.ChronoUnit; 62 import java.util.ArrayList; 63 import java.util.Arrays; 64 import java.util.Collections; 65 import java.util.List; 66 import java.util.Set; 67 import java.util.UUID; 68 69 @AppModeFull(reason = "HealthConnectManager is not accessible to instant apps") 70 @RunWith(AndroidJUnit4.class) 71 public class CyclingPedalingCadenceRecordTest { 72 73 private static final String TAG = "CyclingPedalingCadenceRecordTest"; 74 private static final String PACKAGE_NAME = "android.healthconnect.cts"; 75 76 @Rule 77 public AssumptionCheckerRule mSupportedHardwareRule = 78 new AssumptionCheckerRule( 79 TestUtils::isHardwareSupported, "Tests should run on supported hardware only."); 80 81 @Before setUp()82 public void setUp() throws InterruptedException { 83 TestUtils.deleteAllStagedRemoteData(); 84 } 85 86 @After tearDown()87 public void tearDown() throws InterruptedException { 88 TestUtils.verifyDeleteRecords( 89 CyclingPedalingCadenceRecord.class, 90 new TimeInstantRangeFilter.Builder() 91 .setStartTime(Instant.EPOCH) 92 .setEndTime(Instant.now()) 93 .build()); 94 TestUtils.deleteAllStagedRemoteData(); 95 } 96 97 @Test testInsertCyclingPedalingCadenceRecord()98 public void testInsertCyclingPedalingCadenceRecord() throws InterruptedException { 99 CyclingPedalingCadenceRecord baseCyclingPedalingCadenceRecord = 100 getBaseCyclingPedalingCadenceRecord(); 101 102 assertThat(baseCyclingPedalingCadenceRecord.getSamples().get(0).getTime()).isNotNull(); 103 assertThat(baseCyclingPedalingCadenceRecord.getSamples().get(0).getRevolutionsPerMinute()) 104 .isNotNull(); 105 TestUtils.insertRecords( 106 Arrays.asList( 107 baseCyclingPedalingCadenceRecord, 108 getCompleteCyclingPedalingCadenceRecord())); 109 } 110 111 @Test testReadCyclingPedalingCadenceRecord_usingIds()112 public void testReadCyclingPedalingCadenceRecord_usingIds() throws InterruptedException { 113 testReadCyclingPedalingCadenceRecordIds(); 114 } 115 116 @Test testReadCyclingPedalingCadenceRecord_invalidIds()117 public void testReadCyclingPedalingCadenceRecord_invalidIds() throws InterruptedException { 118 ReadRecordsRequestUsingIds<CyclingPedalingCadenceRecord> request = 119 new ReadRecordsRequestUsingIds.Builder<>(CyclingPedalingCadenceRecord.class) 120 .addId(UUID.randomUUID().toString()) 121 .build(); 122 List<CyclingPedalingCadenceRecord> result = TestUtils.readRecords(request); 123 assertThat(result.size()).isEqualTo(0); 124 } 125 126 @Test testReadCyclingPedalingCadenceRecord_usingClientRecordIds()127 public void testReadCyclingPedalingCadenceRecord_usingClientRecordIds() 128 throws InterruptedException { 129 List<Record> recordList = 130 Arrays.asList( 131 getCompleteCyclingPedalingCadenceRecord(), 132 getCompleteCyclingPedalingCadenceRecord()); 133 List<Record> insertedRecords = TestUtils.insertRecords(recordList); 134 readCyclingPedalingCadenceRecordUsingClientId(insertedRecords); 135 } 136 137 @Test testReadCyclingPedalingCadenceRecord_invalidClientRecordIds()138 public void testReadCyclingPedalingCadenceRecord_invalidClientRecordIds() 139 throws InterruptedException { 140 ReadRecordsRequestUsingIds<CyclingPedalingCadenceRecord> request = 141 new ReadRecordsRequestUsingIds.Builder<>(CyclingPedalingCadenceRecord.class) 142 .addClientRecordId("abc") 143 .build(); 144 List<CyclingPedalingCadenceRecord> result = TestUtils.readRecords(request); 145 assertThat(result.size()).isEqualTo(0); 146 } 147 148 @Test testReadCyclingPedalingCadenceRecordUsingFilters_default()149 public void testReadCyclingPedalingCadenceRecordUsingFilters_default() 150 throws InterruptedException { 151 List<CyclingPedalingCadenceRecord> oldCyclingPedalingCadenceRecords = 152 TestUtils.readRecords( 153 new ReadRecordsRequestUsingFilters.Builder<>( 154 CyclingPedalingCadenceRecord.class) 155 .build()); 156 157 CyclingPedalingCadenceRecord testRecord = 158 (CyclingPedalingCadenceRecord) 159 TestUtils.insertRecord(getCompleteCyclingPedalingCadenceRecord()); 160 List<CyclingPedalingCadenceRecord> newCyclingPedalingCadenceRecords = 161 TestUtils.readRecords( 162 new ReadRecordsRequestUsingFilters.Builder<>( 163 CyclingPedalingCadenceRecord.class) 164 .build()); 165 assertThat(newCyclingPedalingCadenceRecords.size()) 166 .isEqualTo(oldCyclingPedalingCadenceRecords.size() + 1); 167 assertThat( 168 newCyclingPedalingCadenceRecords 169 .get(newCyclingPedalingCadenceRecords.size() - 1) 170 .equals(testRecord)) 171 .isTrue(); 172 } 173 174 @Test testReadCyclingPedalingCadenceRecordUsingFilters_timeFilter()175 public void testReadCyclingPedalingCadenceRecordUsingFilters_timeFilter() 176 throws InterruptedException { 177 TimeInstantRangeFilter filter = 178 new TimeInstantRangeFilter.Builder() 179 .setStartTime(Instant.now()) 180 .setEndTime(Instant.now().plusMillis(3000)) 181 .build(); 182 183 CyclingPedalingCadenceRecord testRecord = 184 (CyclingPedalingCadenceRecord) 185 TestUtils.insertRecord(getCompleteCyclingPedalingCadenceRecord()); 186 List<CyclingPedalingCadenceRecord> newCyclingPedalingCadenceRecords = 187 TestUtils.readRecords( 188 new ReadRecordsRequestUsingFilters.Builder<>( 189 CyclingPedalingCadenceRecord.class) 190 .setTimeRangeFilter(filter) 191 .build()); 192 assertThat(newCyclingPedalingCadenceRecords.size()).isEqualTo(1); 193 assertThat( 194 newCyclingPedalingCadenceRecords 195 .get(newCyclingPedalingCadenceRecords.size() - 1) 196 .equals(testRecord)) 197 .isTrue(); 198 } 199 200 @Test testReadCyclingPedalingCadenceRecordUsingFilters_dataFilter_correct()201 public void testReadCyclingPedalingCadenceRecordUsingFilters_dataFilter_correct() 202 throws InterruptedException { 203 Context context = ApplicationProvider.getApplicationContext(); 204 List<CyclingPedalingCadenceRecord> oldCyclingPedalingCadenceRecords = 205 TestUtils.readRecords( 206 new ReadRecordsRequestUsingFilters.Builder<>( 207 CyclingPedalingCadenceRecord.class) 208 .addDataOrigins( 209 new DataOrigin.Builder() 210 .setPackageName(context.getPackageName()) 211 .build()) 212 .build()); 213 CyclingPedalingCadenceRecord testRecord = 214 (CyclingPedalingCadenceRecord) 215 TestUtils.insertRecord(getCompleteCyclingPedalingCadenceRecord()); 216 List<CyclingPedalingCadenceRecord> newCyclingPedalingCadenceRecords = 217 TestUtils.readRecords( 218 new ReadRecordsRequestUsingFilters.Builder<>( 219 CyclingPedalingCadenceRecord.class) 220 .addDataOrigins( 221 new DataOrigin.Builder() 222 .setPackageName(context.getPackageName()) 223 .build()) 224 .build()); 225 assertThat( 226 newCyclingPedalingCadenceRecords.size() 227 - oldCyclingPedalingCadenceRecords.size()) 228 .isEqualTo(1); 229 assertThat( 230 newCyclingPedalingCadenceRecords 231 .get(newCyclingPedalingCadenceRecords.size() - 1) 232 .equals(testRecord)) 233 .isTrue(); 234 235 CyclingPedalingCadenceRecord newRecord = 236 newCyclingPedalingCadenceRecords.get(newCyclingPedalingCadenceRecords.size() - 1); 237 for (int idx = 0; idx < newRecord.getSamples().size(); idx++) { 238 assertThat(newRecord.getSamples().get(idx).getTime().toEpochMilli()) 239 .isEqualTo(testRecord.getSamples().get(idx).getTime().toEpochMilli()); 240 assertThat(newRecord.getSamples().get(idx).getRevolutionsPerMinute()) 241 .isEqualTo(testRecord.getSamples().get(idx).getRevolutionsPerMinute()); 242 } 243 } 244 245 @Test testReadCyclingPedalingCadenceRecordUsingFilters_dataFilter_incorrect()246 public void testReadCyclingPedalingCadenceRecordUsingFilters_dataFilter_incorrect() 247 throws InterruptedException { 248 TestUtils.insertRecords( 249 Collections.singletonList(getCompleteCyclingPedalingCadenceRecord())); 250 List<CyclingPedalingCadenceRecord> newCyclingPedalingCadenceRecords = 251 TestUtils.readRecords( 252 new ReadRecordsRequestUsingFilters.Builder<>( 253 CyclingPedalingCadenceRecord.class) 254 .addDataOrigins( 255 new DataOrigin.Builder().setPackageName("abc").build()) 256 .build()); 257 assertThat(newCyclingPedalingCadenceRecords.size()).isEqualTo(0); 258 } 259 260 @Test testDeleteCyclingPedalingCadenceRecord_no_filters()261 public void testDeleteCyclingPedalingCadenceRecord_no_filters() throws InterruptedException { 262 String id = TestUtils.insertRecordAndGetId(getCompleteCyclingPedalingCadenceRecord()); 263 TestUtils.verifyDeleteRecords(new DeleteUsingFiltersRequest.Builder().build()); 264 TestUtils.assertRecordNotFound(id, CyclingPedalingCadenceRecord.class); 265 } 266 267 @Test testDeleteCyclingPedalingCadenceRecord_time_filters()268 public void testDeleteCyclingPedalingCadenceRecord_time_filters() throws InterruptedException { 269 TimeInstantRangeFilter timeRangeFilter = 270 new TimeInstantRangeFilter.Builder() 271 .setStartTime(Instant.now()) 272 .setEndTime(Instant.now().plusMillis(1000)) 273 .build(); 274 String id = TestUtils.insertRecordAndGetId(getCompleteCyclingPedalingCadenceRecord()); 275 TestUtils.verifyDeleteRecords( 276 new DeleteUsingFiltersRequest.Builder() 277 .addRecordType(CyclingPedalingCadenceRecord.class) 278 .setTimeRangeFilter(timeRangeFilter) 279 .build()); 280 TestUtils.assertRecordNotFound(id, CyclingPedalingCadenceRecord.class); 281 } 282 283 @Test testDeleteCyclingPedalingCadenceRecord_recordId_filters()284 public void testDeleteCyclingPedalingCadenceRecord_recordId_filters() 285 throws InterruptedException { 286 List<Record> records = 287 TestUtils.insertRecords( 288 List.of( 289 getBaseCyclingPedalingCadenceRecord(), 290 getCompleteCyclingPedalingCadenceRecord())); 291 292 for (Record record : records) { 293 TestUtils.verifyDeleteRecords( 294 new DeleteUsingFiltersRequest.Builder() 295 .addRecordType(record.getClass()) 296 .build()); 297 TestUtils.assertRecordNotFound(record.getMetadata().getId(), record.getClass()); 298 } 299 } 300 301 @Test testDeleteCyclingPedalingCadenceRecord_dataOrigin_filters()302 public void testDeleteCyclingPedalingCadenceRecord_dataOrigin_filters() 303 throws InterruptedException { 304 Context context = ApplicationProvider.getApplicationContext(); 305 String id = TestUtils.insertRecordAndGetId(getCompleteCyclingPedalingCadenceRecord()); 306 TestUtils.verifyDeleteRecords( 307 new DeleteUsingFiltersRequest.Builder() 308 .addDataOrigin( 309 new DataOrigin.Builder() 310 .setPackageName(context.getPackageName()) 311 .build()) 312 .build()); 313 TestUtils.assertRecordNotFound(id, CyclingPedalingCadenceRecord.class); 314 } 315 316 @Test testDeleteCyclingPedalingCadenceRecord_dataOrigin_filter_incorrect()317 public void testDeleteCyclingPedalingCadenceRecord_dataOrigin_filter_incorrect() 318 throws InterruptedException { 319 String id = TestUtils.insertRecordAndGetId(getCompleteCyclingPedalingCadenceRecord()); 320 TestUtils.verifyDeleteRecords( 321 new DeleteUsingFiltersRequest.Builder() 322 .addDataOrigin(new DataOrigin.Builder().setPackageName("abc").build()) 323 .build()); 324 TestUtils.assertRecordFound(id, CyclingPedalingCadenceRecord.class); 325 } 326 327 @Test testDeleteCyclingPedalingCadenceRecord_usingIds()328 public void testDeleteCyclingPedalingCadenceRecord_usingIds() throws InterruptedException { 329 List<Record> records = 330 TestUtils.insertRecords( 331 List.of( 332 getBaseCyclingPedalingCadenceRecord(), 333 getCompleteCyclingPedalingCadenceRecord())); 334 List<RecordIdFilter> recordIds = new ArrayList<>(records.size()); 335 for (Record record : records) { 336 recordIds.add(RecordIdFilter.fromId(record.getClass(), record.getMetadata().getId())); 337 } 338 339 TestUtils.verifyDeleteRecords(recordIds); 340 for (Record record : records) { 341 TestUtils.assertRecordNotFound(record.getMetadata().getId(), record.getClass()); 342 } 343 } 344 345 @Test testDeleteCyclingPedalingCadenceRecord_time_range()346 public void testDeleteCyclingPedalingCadenceRecord_time_range() throws InterruptedException { 347 TimeInstantRangeFilter timeRangeFilter = 348 new TimeInstantRangeFilter.Builder() 349 .setStartTime(Instant.now()) 350 .setEndTime(Instant.now().plusMillis(1000)) 351 .build(); 352 String id = TestUtils.insertRecordAndGetId(getCompleteCyclingPedalingCadenceRecord()); 353 TestUtils.verifyDeleteRecords(CyclingPedalingCadenceRecord.class, timeRangeFilter); 354 TestUtils.assertRecordNotFound(id, CyclingPedalingCadenceRecord.class); 355 } 356 357 @Test testZoneOffsets()358 public void testZoneOffsets() { 359 final ZoneOffset defaultZoneOffset = 360 ZoneOffset.systemDefault().getRules().getOffset(Instant.now()); 361 final ZoneOffset startZoneOffset = ZoneOffset.UTC; 362 final ZoneOffset endZoneOffset = ZoneOffset.MAX; 363 CyclingPedalingCadenceRecord.Builder builder = 364 new CyclingPedalingCadenceRecord.Builder( 365 new Metadata.Builder().build(), 366 Instant.now(), 367 Instant.now().plusMillis(1000), 368 Collections.emptyList()); 369 370 assertThat(builder.setStartZoneOffset(startZoneOffset).build().getStartZoneOffset()) 371 .isEqualTo(startZoneOffset); 372 assertThat(builder.setEndZoneOffset(endZoneOffset).build().getEndZoneOffset()) 373 .isEqualTo(endZoneOffset); 374 assertThat(builder.clearStartZoneOffset().build().getStartZoneOffset()) 375 .isEqualTo(defaultZoneOffset); 376 assertThat(builder.clearEndZoneOffset().build().getEndZoneOffset()) 377 .isEqualTo(defaultZoneOffset); 378 } 379 380 @Test testUpdateRecords_validInput_dataBaseUpdatedSuccessfully()381 public void testUpdateRecords_validInput_dataBaseUpdatedSuccessfully() 382 throws InterruptedException { 383 384 List<Record> insertedRecords = 385 TestUtils.insertRecords( 386 Arrays.asList( 387 getCompleteCyclingPedalingCadenceRecord(), 388 getCompleteCyclingPedalingCadenceRecord())); 389 390 // read inserted records and verify that the data is same as inserted. 391 readCyclingPedalingCadenceRecordUsingIds(insertedRecords); 392 393 // Generate a new set of records that will be used to perform the update operation. 394 List<Record> updateRecords = 395 Arrays.asList( 396 getCompleteCyclingPedalingCadenceRecord(), 397 getCompleteCyclingPedalingCadenceRecord()); 398 399 // Modify the uid of the updateRecords to the uuid that was present in the insert records. 400 for (int itr = 0; itr < updateRecords.size(); itr++) { 401 updateRecords.set( 402 itr, 403 getCyclingPedalingCadenceRecord_update( 404 updateRecords.get(itr), 405 insertedRecords.get(itr).getMetadata().getId(), 406 insertedRecords.get(itr).getMetadata().getClientRecordId())); 407 } 408 409 TestUtils.updateRecords(updateRecords); 410 411 // assert the inserted data has been modified by reading the data. 412 readCyclingPedalingCadenceRecordUsingIds(updateRecords); 413 } 414 415 @Test testUpdateRecords_invalidInputRecords_noChangeInDataBase()416 public void testUpdateRecords_invalidInputRecords_noChangeInDataBase() 417 throws InterruptedException { 418 List<Record> insertedRecords = 419 TestUtils.insertRecords( 420 Arrays.asList( 421 getCompleteCyclingPedalingCadenceRecord(), 422 getCompleteCyclingPedalingCadenceRecord())); 423 424 // read inserted records and verify that the data is same as inserted. 425 readCyclingPedalingCadenceRecordUsingIds(insertedRecords); 426 427 // Generate a second set of records that will be used to perform the update operation. 428 List<Record> updateRecords = 429 Arrays.asList( 430 getCompleteCyclingPedalingCadenceRecord(), 431 getCompleteCyclingPedalingCadenceRecord()); 432 433 // Modify the Uid of the updateRecords to the UUID that was present in the insert records, 434 // leaving out alternate records so that they have a new UUID which is not present in the 435 // dataBase. 436 for (int itr = 0; itr < updateRecords.size(); itr++) { 437 updateRecords.set( 438 itr, 439 getCyclingPedalingCadenceRecord_update( 440 updateRecords.get(itr), 441 itr % 2 == 0 442 ? insertedRecords.get(itr).getMetadata().getId() 443 : UUID.randomUUID().toString(), 444 itr % 2 == 0 445 ? insertedRecords.get(itr).getMetadata().getId() 446 : UUID.randomUUID().toString())); 447 } 448 449 try { 450 TestUtils.updateRecords(updateRecords); 451 Assert.fail("Expected to fail due to invalid records ids."); 452 } catch (HealthConnectException exception) { 453 assertThat(exception.getErrorCode()) 454 .isEqualTo(HealthConnectException.ERROR_INVALID_ARGUMENT); 455 } 456 457 // assert the inserted data has not been modified by reading the data. 458 readCyclingPedalingCadenceRecordUsingIds(insertedRecords); 459 } 460 461 @Test testUpdateRecords_recordWithInvalidPackageName_noChangeInDataBase()462 public void testUpdateRecords_recordWithInvalidPackageName_noChangeInDataBase() 463 throws InterruptedException { 464 List<Record> insertedRecords = 465 TestUtils.insertRecords( 466 Arrays.asList( 467 getCompleteCyclingPedalingCadenceRecord(), 468 getCompleteCyclingPedalingCadenceRecord())); 469 470 // read inserted records and verify that the data is same as inserted. 471 readCyclingPedalingCadenceRecordUsingIds(insertedRecords); 472 473 // Generate a second set of records that will be used to perform the update operation. 474 List<Record> updateRecords = 475 Arrays.asList( 476 getCompleteCyclingPedalingCadenceRecord(), 477 getCompleteCyclingPedalingCadenceRecord()); 478 479 // Modify the Uuid of the updateRecords to the uuid that was present in the insert records. 480 for (int itr = 0; itr < updateRecords.size(); itr++) { 481 updateRecords.set( 482 itr, 483 getCyclingPedalingCadenceRecord_update( 484 updateRecords.get(itr), 485 insertedRecords.get(itr).getMetadata().getId(), 486 insertedRecords.get(itr).getMetadata().getClientRecordId())); 487 // adding an entry with invalid packageName. 488 updateRecords.set(itr, getCompleteCyclingPedalingCadenceRecord()); 489 } 490 491 try { 492 TestUtils.updateRecords(updateRecords); 493 Assert.fail("Expected to fail due to invalid package."); 494 } catch (Exception exception) { 495 // verify that the testcase failed due to invalid argument exception. 496 assertThat(exception).isNotNull(); 497 } 498 499 // assert the inserted data has not been modified by reading the data. 500 readCyclingPedalingCadenceRecordUsingIds(insertedRecords); 501 } 502 503 @Test testInsertAndDeleteRecord_changelogs()504 public void testInsertAndDeleteRecord_changelogs() throws InterruptedException { 505 Context context = ApplicationProvider.getApplicationContext(); 506 ChangeLogTokenResponse tokenResponse = 507 TestUtils.getChangeLogToken( 508 new ChangeLogTokenRequest.Builder() 509 .addDataOriginFilter( 510 new DataOrigin.Builder() 511 .setPackageName(context.getPackageName()) 512 .build()) 513 .addRecordType(CyclingPedalingCadenceRecord.class) 514 .build()); 515 ChangeLogsRequest changeLogsRequest = 516 new ChangeLogsRequest.Builder(tokenResponse.getToken()).build(); 517 ChangeLogsResponse response = TestUtils.getChangeLogs(changeLogsRequest); 518 assertThat(response.getUpsertedRecords().size()).isEqualTo(0); 519 assertThat(response.getDeletedLogs().size()).isEqualTo(0); 520 521 List<Record> testRecord = 522 TestUtils.insertRecords( 523 Collections.singletonList(getCompleteCyclingPedalingCadenceRecord())); 524 response = TestUtils.getChangeLogs(changeLogsRequest); 525 assertThat(response.getUpsertedRecords().size()).isEqualTo(1); 526 assertThat( 527 response.getUpsertedRecords().stream() 528 .map(Record::getMetadata) 529 .map(Metadata::getId) 530 .toList()) 531 .containsExactlyElementsIn( 532 testRecord.stream().map(Record::getMetadata).map(Metadata::getId).toList()); 533 assertThat(response.getDeletedLogs().size()).isEqualTo(0); 534 535 TestUtils.verifyDeleteRecords( 536 new DeleteUsingFiltersRequest.Builder() 537 .addRecordType(CyclingPedalingCadenceRecord.class) 538 .build()); 539 response = TestUtils.getChangeLogs(changeLogsRequest); 540 assertThat(response.getDeletedLogs()).hasSize(testRecord.size()); 541 assertThat( 542 response.getDeletedLogs().stream() 543 .map(ChangeLogsResponse.DeletedLog::getDeletedRecordId) 544 .toList()) 545 .containsExactlyElementsIn( 546 testRecord.stream().map(Record::getMetadata).map(Metadata::getId).toList()); 547 } 548 549 @Test testRpmAggregation_getAggregationFromThreerecords_aggResponsesAreCorrect()550 public void testRpmAggregation_getAggregationFromThreerecords_aggResponsesAreCorrect() 551 throws Exception { 552 TestUtils.setupAggregation(PACKAGE_NAME, HealthDataCategory.ACTIVITY); 553 List<Record> records = 554 Arrays.asList( 555 buildRecordForRpm(120, 100), 556 buildRecordForRpm(100, 101), 557 buildRecordForRpm(80, 102)); 558 AggregateRecordsResponse<Double> response = 559 TestUtils.getAggregateResponse( 560 new AggregateRecordsRequest.Builder<Double>( 561 new TimeInstantRangeFilter.Builder() 562 .setStartTime(Instant.ofEpochMilli(0)) 563 .setEndTime(Instant.now().plus(1, ChronoUnit.DAYS)) 564 .build()) 565 .addAggregationType(RPM_MAX) 566 .addAggregationType(RPM_MIN) 567 .addAggregationType(RPM_AVG) 568 .build(), 569 records); 570 checkAggregationResult(RPM_MIN, 80, response); 571 checkAggregationResult(RPM_AVG, 100, response); 572 checkAggregationResult(RPM_MAX, 120, response); 573 } 574 checkAggregationResult( AggregationType<Double> type, double expectedResult, AggregateRecordsResponse<Double> response)575 private void checkAggregationResult( 576 AggregationType<Double> type, 577 double expectedResult, 578 AggregateRecordsResponse<Double> response) { 579 assertThat(response.get(type)).isNotNull(); 580 assertThat(response.get(type)).isEqualTo(expectedResult); 581 assertThat(response.getZoneOffset(type)) 582 .isEqualTo(ZoneOffset.systemDefault().getRules().getOffset(Instant.now())); 583 Set<DataOrigin> dataOrigins = response.getDataOrigins(type); 584 assertThat(dataOrigins).hasSize(1); 585 for (DataOrigin itr : dataOrigins) { 586 assertThat(itr.getPackageName()).isEqualTo("android.healthconnect.cts"); 587 } 588 } 589 testReadCyclingPedalingCadenceRecordIds()590 private void testReadCyclingPedalingCadenceRecordIds() throws InterruptedException { 591 List<Record> recordList = 592 TestUtils.insertRecords( 593 Arrays.asList( 594 getCompleteCyclingPedalingCadenceRecord(), 595 getCompleteCyclingPedalingCadenceRecord())); 596 readCyclingPedalingCadenceRecordUsingIds(recordList); 597 } 598 readCyclingPedalingCadenceRecordUsingClientId(List<Record> insertedRecords)599 private void readCyclingPedalingCadenceRecordUsingClientId(List<Record> insertedRecords) 600 throws InterruptedException { 601 ReadRecordsRequestUsingIds.Builder<CyclingPedalingCadenceRecord> request = 602 new ReadRecordsRequestUsingIds.Builder<>(CyclingPedalingCadenceRecord.class); 603 for (Record record : insertedRecords) { 604 request.addClientRecordId(record.getMetadata().getClientRecordId()); 605 } 606 List<CyclingPedalingCadenceRecord> result = TestUtils.readRecords(request.build()); 607 assertThat(result.containsAll(insertedRecords)).isTrue(); 608 } 609 readCyclingPedalingCadenceRecordUsingIds(List<Record> insertedRecords)610 private void readCyclingPedalingCadenceRecordUsingIds(List<Record> insertedRecords) 611 throws InterruptedException { 612 ReadRecordsRequestUsingIds.Builder<CyclingPedalingCadenceRecord> request = 613 new ReadRecordsRequestUsingIds.Builder<>(CyclingPedalingCadenceRecord.class); 614 for (Record record : insertedRecords) { 615 request.addId(record.getMetadata().getId()); 616 } 617 ReadRecordsRequestUsingIds requestUsingIds = request.build(); 618 assertThat(requestUsingIds.getRecordType()).isEqualTo(CyclingPedalingCadenceRecord.class); 619 assertThat(requestUsingIds.getRecordIdFilters()).isNotNull(); 620 List<CyclingPedalingCadenceRecord> result = TestUtils.readRecords(requestUsingIds); 621 assertThat(result).hasSize(insertedRecords.size()); 622 assertThat(result.containsAll(insertedRecords)).isTrue(); 623 } 624 getCyclingPedalingCadenceRecord_update( Record record, String id, String clientRecordId)625 CyclingPedalingCadenceRecord getCyclingPedalingCadenceRecord_update( 626 Record record, String id, String clientRecordId) { 627 Metadata metadata = record.getMetadata(); 628 Metadata metadataWithId = 629 new Metadata.Builder() 630 .setId(id) 631 .setClientRecordId(clientRecordId) 632 .setClientRecordVersion(metadata.getClientRecordVersion()) 633 .setDataOrigin(metadata.getDataOrigin()) 634 .setDevice(metadata.getDevice()) 635 .setLastModifiedTime(metadata.getLastModifiedTime()) 636 .build(); 637 638 CyclingPedalingCadenceRecord.CyclingPedalingCadenceRecordSample 639 cyclingPedalingCadenceRecordSample = 640 new CyclingPedalingCadenceRecord.CyclingPedalingCadenceRecordSample( 641 8, Instant.now().plusMillis(100)); 642 643 return new CyclingPedalingCadenceRecord.Builder( 644 metadataWithId, 645 Instant.now(), 646 Instant.now().plusMillis(2000), 647 List.of( 648 cyclingPedalingCadenceRecordSample, 649 cyclingPedalingCadenceRecordSample)) 650 .setStartZoneOffset(ZoneOffset.systemDefault().getRules().getOffset(Instant.now())) 651 .setEndZoneOffset(ZoneOffset.systemDefault().getRules().getOffset(Instant.now())) 652 .build(); 653 } 654 getBaseCyclingPedalingCadenceRecord()655 private static CyclingPedalingCadenceRecord getBaseCyclingPedalingCadenceRecord() { 656 CyclingPedalingCadenceRecord.CyclingPedalingCadenceRecordSample 657 cyclingPedalingCadenceRecord = 658 new CyclingPedalingCadenceRecord.CyclingPedalingCadenceRecordSample( 659 1, Instant.now().plusMillis(100)); 660 ArrayList<CyclingPedalingCadenceRecord.CyclingPedalingCadenceRecordSample> 661 cyclingPedalingCadenceRecords = new ArrayList<>(); 662 cyclingPedalingCadenceRecords.add(cyclingPedalingCadenceRecord); 663 cyclingPedalingCadenceRecords.add(cyclingPedalingCadenceRecord); 664 665 return new CyclingPedalingCadenceRecord.Builder( 666 new Metadata.Builder().build(), 667 Instant.now(), 668 Instant.now().plusMillis(1000), 669 cyclingPedalingCadenceRecords) 670 .build(); 671 } 672 getCompleteCyclingPedalingCadenceRecord()673 private static CyclingPedalingCadenceRecord getCompleteCyclingPedalingCadenceRecord() { 674 return buildRecordForRpm(1, 100); 675 } 676 buildRecordForRpm( double rpm, long millisFromStart)677 private static CyclingPedalingCadenceRecord buildRecordForRpm( 678 double rpm, long millisFromStart) { 679 Device device = 680 new Device.Builder() 681 .setManufacturer("google") 682 .setModel("Pixel4a") 683 .setType(2) 684 .build(); 685 DataOrigin dataOrigin = 686 new DataOrigin.Builder().setPackageName("android.healthconnect.cts").build(); 687 Metadata.Builder testMetadataBuilder = new Metadata.Builder(); 688 testMetadataBuilder.setDevice(device).setDataOrigin(dataOrigin); 689 testMetadataBuilder.setClientRecordId("CPCR" + Math.random()); 690 testMetadataBuilder.setRecordingMethod(Metadata.RECORDING_METHOD_ACTIVELY_RECORDED); 691 Instant recordStartTime = Instant.now(); 692 693 CyclingPedalingCadenceRecord.CyclingPedalingCadenceRecordSample 694 cyclingPedalingCadenceRecord = 695 new CyclingPedalingCadenceRecord.CyclingPedalingCadenceRecordSample( 696 rpm, recordStartTime.plusMillis(millisFromStart)); 697 698 ArrayList<CyclingPedalingCadenceRecord.CyclingPedalingCadenceRecordSample> 699 cyclingPedalingCadenceRecords = new ArrayList<>(); 700 cyclingPedalingCadenceRecords.add(cyclingPedalingCadenceRecord); 701 cyclingPedalingCadenceRecords.add(cyclingPedalingCadenceRecord); 702 703 return new CyclingPedalingCadenceRecord.Builder( 704 testMetadataBuilder.build(), 705 recordStartTime, 706 recordStartTime.plusMillis(1000), 707 cyclingPedalingCadenceRecords) 708 .build(); 709 } 710 } 711