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.StepsCadenceRecord.STEPS_CADENCE_RATE_AVG; 20 import static android.health.connect.datatypes.StepsCadenceRecord.STEPS_CADENCE_RATE_MAX; 21 import static android.health.connect.datatypes.StepsCadenceRecord.STEPS_CADENCE_RATE_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.DataOrigin; 41 import android.health.connect.datatypes.Device; 42 import android.health.connect.datatypes.Metadata; 43 import android.health.connect.datatypes.Record; 44 import android.health.connect.datatypes.StepsCadenceRecord; 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 StepsCadenceRecordTest { 72 73 private static final String TAG = "StepsCadenceRecordTest"; 74 75 private static final String PACKAGE_NAME = "android.healthconnect.cts"; 76 77 @Rule 78 public AssumptionCheckerRule mSupportedHardwareRule = 79 new AssumptionCheckerRule( 80 TestUtils::isHardwareSupported, "Tests should run on supported hardware only."); 81 82 @Before setUp()83 public void setUp() throws InterruptedException { 84 TestUtils.deleteAllStagedRemoteData(); 85 } 86 87 @After tearDown()88 public void tearDown() throws InterruptedException { 89 TestUtils.verifyDeleteRecords( 90 StepsCadenceRecord.class, 91 new TimeInstantRangeFilter.Builder() 92 .setStartTime(Instant.EPOCH) 93 .setEndTime(Instant.now()) 94 .build()); 95 TestUtils.deleteAllStagedRemoteData(); 96 } 97 98 @Test testInsertStepsCadenceRecord()99 public void testInsertStepsCadenceRecord() throws InterruptedException { 100 TestUtils.insertRecords( 101 Arrays.asList(getBaseStepsCadenceRecord(), getCompleteStepsCadenceRecord())); 102 } 103 104 @Test testReadStepsCadenceRecord_usingIds()105 public void testReadStepsCadenceRecord_usingIds() throws InterruptedException { 106 testReadStepsCadenceRecordIds(); 107 } 108 109 @Test testReadStepsCadenceRecord_invalidIds()110 public void testReadStepsCadenceRecord_invalidIds() throws InterruptedException { 111 ReadRecordsRequestUsingIds<StepsCadenceRecord> request = 112 new ReadRecordsRequestUsingIds.Builder<>(StepsCadenceRecord.class) 113 .addId(UUID.randomUUID().toString()) 114 .build(); 115 List<StepsCadenceRecord> result = TestUtils.readRecords(request); 116 assertThat(result.size()).isEqualTo(0); 117 } 118 119 @Test testReadStepsCadenceRecord_usingClientRecordIds()120 public void testReadStepsCadenceRecord_usingClientRecordIds() throws InterruptedException { 121 List<Record> recordList = 122 Arrays.asList(getCompleteStepsCadenceRecord(), getCompleteStepsCadenceRecord()); 123 List<Record> insertedRecords = TestUtils.insertRecords(recordList); 124 readStepsCadenceRecordUsingClientId(insertedRecords); 125 } 126 127 @Test testReadStepsCadenceRecord_invalidClientRecordIds()128 public void testReadStepsCadenceRecord_invalidClientRecordIds() throws InterruptedException { 129 ReadRecordsRequestUsingIds<StepsCadenceRecord> request = 130 new ReadRecordsRequestUsingIds.Builder<>(StepsCadenceRecord.class) 131 .addClientRecordId("abc") 132 .build(); 133 List<StepsCadenceRecord> result = TestUtils.readRecords(request); 134 assertThat(result.size()).isEqualTo(0); 135 } 136 137 @Test testReadStepsCadenceRecordUsingFilters_default()138 public void testReadStepsCadenceRecordUsingFilters_default() throws InterruptedException { 139 List<StepsCadenceRecord> oldStepsCadenceRecords = 140 TestUtils.readRecords( 141 new ReadRecordsRequestUsingFilters.Builder<>(StepsCadenceRecord.class) 142 .build()); 143 144 StepsCadenceRecord testRecord = 145 (StepsCadenceRecord) TestUtils.insertRecord(getCompleteStepsCadenceRecord()); 146 List<StepsCadenceRecord> newStepsCadenceRecords = 147 TestUtils.readRecords( 148 new ReadRecordsRequestUsingFilters.Builder<>(StepsCadenceRecord.class) 149 .build()); 150 assertThat(newStepsCadenceRecords.size()).isEqualTo(oldStepsCadenceRecords.size() + 1); 151 assertThat(newStepsCadenceRecords.get(newStepsCadenceRecords.size() - 1).equals(testRecord)) 152 .isTrue(); 153 } 154 155 @Test testReadStepsCadenceRecordUsingFilters_timeFilter()156 public void testReadStepsCadenceRecordUsingFilters_timeFilter() throws InterruptedException { 157 TimeInstantRangeFilter filter = 158 new TimeInstantRangeFilter.Builder() 159 .setStartTime(Instant.now()) 160 .setEndTime(Instant.now().plusMillis(3000)) 161 .build(); 162 163 StepsCadenceRecord testRecord = 164 (StepsCadenceRecord) TestUtils.insertRecord(getCompleteStepsCadenceRecord()); 165 List<StepsCadenceRecord> newStepsCadenceRecords = 166 TestUtils.readRecords( 167 new ReadRecordsRequestUsingFilters.Builder<>(StepsCadenceRecord.class) 168 .setTimeRangeFilter(filter) 169 .build()); 170 assertThat(newStepsCadenceRecords.size()).isEqualTo(1); 171 assertThat(newStepsCadenceRecords.get(newStepsCadenceRecords.size() - 1).equals(testRecord)) 172 .isTrue(); 173 } 174 175 @Test testReadStepsCadenceRecordUsingFilters_dataFilter_correct()176 public void testReadStepsCadenceRecordUsingFilters_dataFilter_correct() 177 throws InterruptedException { 178 Context context = ApplicationProvider.getApplicationContext(); 179 List<StepsCadenceRecord> oldStepsCadenceRecords = 180 TestUtils.readRecords( 181 new ReadRecordsRequestUsingFilters.Builder<>(StepsCadenceRecord.class) 182 .addDataOrigins( 183 new DataOrigin.Builder() 184 .setPackageName(context.getPackageName()) 185 .build()) 186 .build()); 187 188 StepsCadenceRecord testRecord = 189 (StepsCadenceRecord) TestUtils.insertRecord(getCompleteStepsCadenceRecord()); 190 List<StepsCadenceRecord> newStepsCadenceRecords = 191 TestUtils.readRecords( 192 new ReadRecordsRequestUsingFilters.Builder<>(StepsCadenceRecord.class) 193 .addDataOrigins( 194 new DataOrigin.Builder() 195 .setPackageName(context.getPackageName()) 196 .build()) 197 .build()); 198 assertThat(newStepsCadenceRecords.size() - oldStepsCadenceRecords.size()).isEqualTo(1); 199 assertThat(newStepsCadenceRecords.get(newStepsCadenceRecords.size() - 1).equals(testRecord)) 200 .isTrue(); 201 StepsCadenceRecord newRecord = 202 newStepsCadenceRecords.get(newStepsCadenceRecords.size() - 1); 203 assertThat(newRecord.equals(testRecord)).isTrue(); 204 for (int idx = 0; idx < newRecord.getSamples().size(); idx++) { 205 assertThat(newRecord.getSamples().get(idx).getTime().toEpochMilli()) 206 .isEqualTo(testRecord.getSamples().get(idx).getTime().toEpochMilli()); 207 assertThat(newRecord.getSamples().get(idx).getRate()) 208 .isEqualTo(testRecord.getSamples().get(idx).getRate()); 209 } 210 } 211 212 @Test testReadStepsCadenceRecordUsingFilters_dataFilter_incorrect()213 public void testReadStepsCadenceRecordUsingFilters_dataFilter_incorrect() 214 throws InterruptedException { 215 TestUtils.insertRecords(Collections.singletonList(getCompleteStepsCadenceRecord())); 216 List<StepsCadenceRecord> newStepsCadenceRecords = 217 TestUtils.readRecords( 218 new ReadRecordsRequestUsingFilters.Builder<>(StepsCadenceRecord.class) 219 .addDataOrigins( 220 new DataOrigin.Builder().setPackageName("abc").build()) 221 .build()); 222 assertThat(newStepsCadenceRecords.size()).isEqualTo(0); 223 } 224 225 @Test testDeleteStepsCadenceRecord_no_filters()226 public void testDeleteStepsCadenceRecord_no_filters() throws InterruptedException { 227 String id = TestUtils.insertRecordAndGetId(getCompleteStepsCadenceRecord()); 228 TestUtils.verifyDeleteRecords(new DeleteUsingFiltersRequest.Builder().build()); 229 TestUtils.assertRecordNotFound(id, StepsCadenceRecord.class); 230 } 231 232 @Test testDeleteStepsCadenceRecord_time_filters()233 public void testDeleteStepsCadenceRecord_time_filters() throws InterruptedException { 234 TimeInstantRangeFilter timeRangeFilter = 235 new TimeInstantRangeFilter.Builder() 236 .setStartTime(Instant.now()) 237 .setEndTime(Instant.now().plusMillis(1000)) 238 .build(); 239 String id = TestUtils.insertRecordAndGetId(getCompleteStepsCadenceRecord()); 240 TestUtils.verifyDeleteRecords( 241 new DeleteUsingFiltersRequest.Builder() 242 .addRecordType(StepsCadenceRecord.class) 243 .setTimeRangeFilter(timeRangeFilter) 244 .build()); 245 TestUtils.assertRecordNotFound(id, StepsCadenceRecord.class); 246 } 247 248 @Test testDeleteStepsCadenceRecord_recordId_filters()249 public void testDeleteStepsCadenceRecord_recordId_filters() throws InterruptedException { 250 List<Record> records = 251 TestUtils.insertRecords( 252 List.of(getBaseStepsCadenceRecord(), getCompleteStepsCadenceRecord())); 253 254 for (Record record : records) { 255 TestUtils.verifyDeleteRecords( 256 new DeleteUsingFiltersRequest.Builder() 257 .addRecordType(record.getClass()) 258 .build()); 259 TestUtils.assertRecordNotFound(record.getMetadata().getId(), record.getClass()); 260 } 261 } 262 263 @Test testDeleteStepsCadenceRecord_dataOrigin_filters()264 public void testDeleteStepsCadenceRecord_dataOrigin_filters() throws InterruptedException { 265 Context context = ApplicationProvider.getApplicationContext(); 266 String id = TestUtils.insertRecordAndGetId(getCompleteStepsCadenceRecord()); 267 TestUtils.verifyDeleteRecords( 268 new DeleteUsingFiltersRequest.Builder() 269 .addDataOrigin( 270 new DataOrigin.Builder() 271 .setPackageName(context.getPackageName()) 272 .build()) 273 .build()); 274 TestUtils.assertRecordNotFound(id, StepsCadenceRecord.class); 275 } 276 277 @Test testDeleteStepsCadenceRecord_dataOrigin_filter_incorrect()278 public void testDeleteStepsCadenceRecord_dataOrigin_filter_incorrect() 279 throws InterruptedException { 280 String id = TestUtils.insertRecordAndGetId(getCompleteStepsCadenceRecord()); 281 TestUtils.verifyDeleteRecords( 282 new DeleteUsingFiltersRequest.Builder() 283 .addDataOrigin(new DataOrigin.Builder().setPackageName("abc").build()) 284 .build()); 285 TestUtils.assertRecordFound(id, StepsCadenceRecord.class); 286 } 287 288 @Test testDeleteStepsCadenceRecord_usingIds()289 public void testDeleteStepsCadenceRecord_usingIds() throws InterruptedException { 290 List<Record> insertedRecord = 291 TestUtils.insertRecords( 292 List.of(getBaseStepsCadenceRecord(), getCompleteStepsCadenceRecord())); 293 List<RecordIdFilter> recordIds = new ArrayList<>(insertedRecord.size()); 294 for (Record record : insertedRecord) { 295 recordIds.add(RecordIdFilter.fromId(record.getClass(), record.getMetadata().getId())); 296 } 297 298 TestUtils.verifyDeleteRecords(recordIds); 299 for (Record record : insertedRecord) { 300 TestUtils.assertRecordNotFound(record.getMetadata().getId(), record.getClass()); 301 } 302 } 303 304 @Test testDeleteStepsCadenceRecord_time_range()305 public void testDeleteStepsCadenceRecord_time_range() throws InterruptedException { 306 TimeInstantRangeFilter timeRangeFilter = 307 new TimeInstantRangeFilter.Builder() 308 .setStartTime(Instant.now()) 309 .setEndTime(Instant.now().plusMillis(1000)) 310 .build(); 311 String id = TestUtils.insertRecordAndGetId(getCompleteStepsCadenceRecord()); 312 TestUtils.verifyDeleteRecords(StepsCadenceRecord.class, timeRangeFilter); 313 TestUtils.assertRecordNotFound(id, StepsCadenceRecord.class); 314 } 315 316 @Test testZoneOffsets()317 public void testZoneOffsets() { 318 final ZoneOffset defaultZoneOffset = 319 ZoneOffset.systemDefault().getRules().getOffset(Instant.now()); 320 final ZoneOffset startZoneOffset = ZoneOffset.UTC; 321 final ZoneOffset endZoneOffset = ZoneOffset.MAX; 322 StepsCadenceRecord.Builder builder = 323 new StepsCadenceRecord.Builder( 324 new Metadata.Builder().build(), 325 Instant.now(), 326 Instant.now().plusMillis(1000), 327 Collections.emptyList()); 328 329 assertThat(builder.setStartZoneOffset(startZoneOffset).build().getStartZoneOffset()) 330 .isEqualTo(startZoneOffset); 331 assertThat(builder.setEndZoneOffset(endZoneOffset).build().getEndZoneOffset()) 332 .isEqualTo(endZoneOffset); 333 assertThat(builder.clearStartZoneOffset().build().getStartZoneOffset()) 334 .isEqualTo(defaultZoneOffset); 335 assertThat(builder.clearEndZoneOffset().build().getEndZoneOffset()) 336 .isEqualTo(defaultZoneOffset); 337 } 338 339 @Test testUpdateRecords_validInput_dataBaseUpdatedSuccessfully()340 public void testUpdateRecords_validInput_dataBaseUpdatedSuccessfully() 341 throws InterruptedException { 342 343 List<Record> insertedRecords = 344 TestUtils.insertRecords( 345 Arrays.asList( 346 getCompleteStepsCadenceRecord(), getCompleteStepsCadenceRecord())); 347 348 // read inserted records and verify that the data is same as inserted. 349 readStepsCadenceRecordUsingIds(insertedRecords); 350 351 // Generate a new set of records that will be used to perform the update operation. 352 List<Record> updateRecords = 353 Arrays.asList(getCompleteStepsCadenceRecord(), getCompleteStepsCadenceRecord()); 354 355 // Modify the uid of the updateRecords to the uuid that was present in the insert records. 356 for (int itr = 0; itr < updateRecords.size(); itr++) { 357 updateRecords.set( 358 itr, 359 getStepsCadenceRecord_update( 360 updateRecords.get(itr), 361 insertedRecords.get(itr).getMetadata().getId(), 362 insertedRecords.get(itr).getMetadata().getClientRecordId())); 363 } 364 365 TestUtils.updateRecords(updateRecords); 366 367 // assert the inserted data has been modified by reading the data. 368 readStepsCadenceRecordUsingIds(updateRecords); 369 } 370 371 @Test testUpdateRecords_invalidInputRecords_noChangeInDataBase()372 public void testUpdateRecords_invalidInputRecords_noChangeInDataBase() 373 throws InterruptedException { 374 List<Record> insertedRecords = 375 TestUtils.insertRecords( 376 Arrays.asList( 377 getCompleteStepsCadenceRecord(), getCompleteStepsCadenceRecord())); 378 379 // read inserted records and verify that the data is same as inserted. 380 readStepsCadenceRecordUsingIds(insertedRecords); 381 382 // Generate a second set of records that will be used to perform the update operation. 383 List<Record> updateRecords = 384 Arrays.asList(getCompleteStepsCadenceRecord(), getCompleteStepsCadenceRecord()); 385 386 // Modify the Uid of the updateRecords to the UUID that was present in the insert records, 387 // leaving out alternate records so that they have a new UUID which is not present in the 388 // dataBase. 389 for (int itr = 0; itr < updateRecords.size(); itr++) { 390 updateRecords.set( 391 itr, 392 getStepsCadenceRecord_update( 393 updateRecords.get(itr), 394 itr % 2 == 0 395 ? insertedRecords.get(itr).getMetadata().getId() 396 : UUID.randomUUID().toString(), 397 itr % 2 == 0 398 ? insertedRecords.get(itr).getMetadata().getId() 399 : UUID.randomUUID().toString())); 400 } 401 402 try { 403 TestUtils.updateRecords(updateRecords); 404 Assert.fail("Expected to fail due to invalid records ids."); 405 } catch (HealthConnectException exception) { 406 assertThat(exception.getErrorCode()) 407 .isEqualTo(HealthConnectException.ERROR_INVALID_ARGUMENT); 408 } 409 410 // assert the inserted data has not been modified by reading the data. 411 readStepsCadenceRecordUsingIds(insertedRecords); 412 } 413 414 @Test testUpdateRecords_recordWithInvalidPackageName_noChangeInDataBase()415 public void testUpdateRecords_recordWithInvalidPackageName_noChangeInDataBase() 416 throws InterruptedException { 417 List<Record> insertedRecords = 418 TestUtils.insertRecords( 419 Arrays.asList( 420 getCompleteStepsCadenceRecord(), getCompleteStepsCadenceRecord())); 421 422 // read inserted records and verify that the data is same as inserted. 423 readStepsCadenceRecordUsingIds(insertedRecords); 424 425 // Generate a second set of records that will be used to perform the update operation. 426 List<Record> updateRecords = 427 Arrays.asList(getCompleteStepsCadenceRecord(), getCompleteStepsCadenceRecord()); 428 429 // Modify the Uuid of the updateRecords to the uuid that was present in the insert records. 430 for (int itr = 0; itr < updateRecords.size(); itr++) { 431 updateRecords.set( 432 itr, 433 getStepsCadenceRecord_update( 434 updateRecords.get(itr), 435 insertedRecords.get(itr).getMetadata().getId(), 436 insertedRecords.get(itr).getMetadata().getClientRecordId())); 437 // adding an entry with invalid packageName. 438 updateRecords.set(itr, getCompleteStepsCadenceRecord()); 439 } 440 441 try { 442 TestUtils.updateRecords(updateRecords); 443 Assert.fail("Expected to fail due to invalid package."); 444 } catch (Exception exception) { 445 // verify that the testcase failed due to invalid argument exception. 446 assertThat(exception).isNotNull(); 447 } 448 449 // assert the inserted data has not been modified by reading the data. 450 readStepsCadenceRecordUsingIds(insertedRecords); 451 } 452 453 @Test testInsertAndDeleteRecord_changelogs()454 public void testInsertAndDeleteRecord_changelogs() throws InterruptedException { 455 Context context = ApplicationProvider.getApplicationContext(); 456 ChangeLogTokenResponse tokenResponse = 457 TestUtils.getChangeLogToken( 458 new ChangeLogTokenRequest.Builder() 459 .addDataOriginFilter( 460 new DataOrigin.Builder() 461 .setPackageName(context.getPackageName()) 462 .build()) 463 .addRecordType(StepsCadenceRecord.class) 464 .build()); 465 ChangeLogsRequest changeLogsRequest = 466 new ChangeLogsRequest.Builder(tokenResponse.getToken()).build(); 467 ChangeLogsResponse response = TestUtils.getChangeLogs(changeLogsRequest); 468 assertThat(response.getUpsertedRecords().size()).isEqualTo(0); 469 assertThat(response.getDeletedLogs().size()).isEqualTo(0); 470 471 List<Record> testRecord = 472 TestUtils.insertRecords(Collections.singletonList(getCompleteStepsCadenceRecord())); 473 response = TestUtils.getChangeLogs(changeLogsRequest); 474 assertThat(response.getUpsertedRecords().size()).isEqualTo(1); 475 assertThat( 476 response.getUpsertedRecords().stream() 477 .map(Record::getMetadata) 478 .map(Metadata::getId) 479 .toList()) 480 .containsExactlyElementsIn( 481 testRecord.stream().map(Record::getMetadata).map(Metadata::getId).toList()); 482 assertThat(response.getDeletedLogs().size()).isEqualTo(0); 483 484 TestUtils.verifyDeleteRecords( 485 new DeleteUsingFiltersRequest.Builder() 486 .addRecordType(StepsCadenceRecord.class) 487 .build()); 488 response = TestUtils.getChangeLogs(changeLogsRequest); 489 assertThat(response.getDeletedLogs()).hasSize(testRecord.size()); 490 assertThat( 491 response.getDeletedLogs().stream() 492 .map(ChangeLogsResponse.DeletedLog::getDeletedRecordId) 493 .toList()) 494 .containsExactlyElementsIn( 495 testRecord.stream().map(Record::getMetadata).map(Metadata::getId).toList()); 496 } 497 testReadStepsCadenceRecordIds()498 private void testReadStepsCadenceRecordIds() throws InterruptedException { 499 List<Record> recordList = 500 TestUtils.insertRecords( 501 Arrays.asList( 502 getCompleteStepsCadenceRecord(), getCompleteStepsCadenceRecord())); 503 readStepsCadenceRecordUsingIds(recordList); 504 } 505 readStepsCadenceRecordUsingClientId(List<Record> insertedRecord)506 private void readStepsCadenceRecordUsingClientId(List<Record> insertedRecord) 507 throws InterruptedException { 508 ReadRecordsRequestUsingIds.Builder<StepsCadenceRecord> request = 509 new ReadRecordsRequestUsingIds.Builder<>(StepsCadenceRecord.class); 510 for (Record record : insertedRecord) { 511 request.addClientRecordId(record.getMetadata().getClientRecordId()); 512 } 513 List<StepsCadenceRecord> result = TestUtils.readRecords(request.build()); 514 assertThat(result.size()).isEqualTo(insertedRecord.size()); 515 assertThat(result).containsExactlyElementsIn(insertedRecord); 516 } 517 readStepsCadenceRecordUsingIds(List<Record> insertedRecords)518 private void readStepsCadenceRecordUsingIds(List<Record> insertedRecords) 519 throws InterruptedException { 520 ReadRecordsRequestUsingIds.Builder<StepsCadenceRecord> request = 521 new ReadRecordsRequestUsingIds.Builder<>(StepsCadenceRecord.class); 522 for (Record record : insertedRecords) { 523 request.addId(record.getMetadata().getId()); 524 } 525 ReadRecordsRequestUsingIds requestUsingIds = request.build(); 526 assertThat(requestUsingIds.getRecordType()).isEqualTo(StepsCadenceRecord.class); 527 assertThat(requestUsingIds.getRecordIdFilters()).isNotNull(); 528 List<StepsCadenceRecord> result = TestUtils.readRecords(requestUsingIds); 529 assertThat(result).hasSize(insertedRecords.size()); 530 assertThat(result.containsAll(insertedRecords)).isTrue(); 531 } 532 533 @Test(expected = IllegalArgumentException.class) testCreateStepsCadenceRecord_invalidValue()534 public void testCreateStepsCadenceRecord_invalidValue() { 535 new StepsCadenceRecord.StepsCadenceRecordSample(10001.0, Instant.now().plusMillis(100)); 536 } 537 538 @Test testInsertWithClientVersion()539 public void testInsertWithClientVersion() throws InterruptedException { 540 List<Record> records = List.of(getStepsCadenceRecordWithClientVersion(10, 1, "testId")); 541 final String id = TestUtils.insertRecords(records).get(0).getMetadata().getId(); 542 ReadRecordsRequestUsingIds<StepsCadenceRecord> request = 543 new ReadRecordsRequestUsingIds.Builder<>(StepsCadenceRecord.class) 544 .addClientRecordId("testId") 545 .build(); 546 StepsCadenceRecord stepsRecord = TestUtils.readRecords(request).get(0); 547 int sampleSize = ((StepsCadenceRecord) records.get(0)).getSamples().size(); 548 assertThat(stepsRecord.getSamples()).hasSize(sampleSize); 549 assertThat(stepsRecord.getSamples().get(0).getRate()).isEqualTo(10); 550 551 records = List.of(getStepsCadenceRecordWithClientVersion(20, 2, "testId")); 552 TestUtils.insertRecords(records); 553 554 stepsRecord = TestUtils.readRecords(request).get(0); 555 assertThat(stepsRecord.getMetadata().getId()).isEqualTo(id); 556 assertThat(stepsRecord.getSamples()).hasSize(sampleSize); 557 assertThat(stepsRecord.getSamples().get(0).getRate()).isEqualTo(20); 558 559 records = List.of(getStepsCadenceRecordWithClientVersion(30, 1, "testId")); 560 TestUtils.insertRecords(records); 561 stepsRecord = TestUtils.readRecords(request).get(0); 562 assertThat(stepsRecord.getMetadata().getId()).isEqualTo(id); 563 assertThat(stepsRecord.getSamples()).hasSize(sampleSize); 564 assertThat(stepsRecord.getSamples().get(0).getRate()).isEqualTo(20); 565 } 566 567 @Test testRateAggregation_getAggregationFromThreerecords_aggResponsesAreCorrect()568 public void testRateAggregation_getAggregationFromThreerecords_aggResponsesAreCorrect() 569 throws Exception { 570 TestUtils.setupAggregation(PACKAGE_NAME, HealthDataCategory.ACTIVITY); 571 List<Record> records = 572 Arrays.asList( 573 buildRecordForStepsCadence(120, 100), 574 buildRecordForStepsCadence(100, 101), 575 buildRecordForStepsCadence(80, 102)); 576 AggregateRecordsResponse<Double> response = 577 TestUtils.getAggregateResponse( 578 new AggregateRecordsRequest.Builder<Double>( 579 new TimeInstantRangeFilter.Builder() 580 .setStartTime(Instant.ofEpochMilli(0)) 581 .setEndTime(Instant.now().plus(1, ChronoUnit.DAYS)) 582 .build()) 583 .addAggregationType(STEPS_CADENCE_RATE_MAX) 584 .addAggregationType(STEPS_CADENCE_RATE_MIN) 585 .addAggregationType(STEPS_CADENCE_RATE_AVG) 586 .build(), 587 records); 588 checkAggregationResult(STEPS_CADENCE_RATE_MAX, 120, response); 589 checkAggregationResult(STEPS_CADENCE_RATE_MIN, 80, response); 590 checkAggregationResult(STEPS_CADENCE_RATE_AVG, 100, response); 591 } 592 checkAggregationResult( AggregationType<Double> type, double expectedResult, AggregateRecordsResponse<Double> response)593 private void checkAggregationResult( 594 AggregationType<Double> type, 595 double expectedResult, 596 AggregateRecordsResponse<Double> response) { 597 assertThat(response.get(type)).isNotNull(); 598 assertThat(response.get(type)).isEqualTo(expectedResult); 599 assertThat(response.getZoneOffset(type)) 600 .isEqualTo(ZoneOffset.systemDefault().getRules().getOffset(Instant.now())); 601 Set<DataOrigin> dataOrigins = response.getDataOrigins(type); 602 assertThat(dataOrigins).hasSize(1); 603 for (DataOrigin itr : dataOrigins) { 604 assertThat(itr.getPackageName()).isEqualTo("android.healthconnect.cts"); 605 } 606 } 607 getStepsCadenceRecord_update( Record record, String id, String clientRecordId)608 StepsCadenceRecord getStepsCadenceRecord_update( 609 Record record, String id, String clientRecordId) { 610 Metadata metadata = record.getMetadata(); 611 Metadata metadataWithId = 612 new Metadata.Builder() 613 .setId(id) 614 .setClientRecordId(clientRecordId) 615 .setClientRecordVersion(metadata.getClientRecordVersion()) 616 .setDataOrigin(metadata.getDataOrigin()) 617 .setDevice(metadata.getDevice()) 618 .setLastModifiedTime(metadata.getLastModifiedTime()) 619 .build(); 620 621 StepsCadenceRecord.StepsCadenceRecordSample stepsCadenceRecordSample = 622 new StepsCadenceRecord.StepsCadenceRecordSample(8.0, Instant.now().plusMillis(100)); 623 624 return new StepsCadenceRecord.Builder( 625 metadataWithId, 626 Instant.now(), 627 Instant.now().plusMillis(2000), 628 List.of(stepsCadenceRecordSample, stepsCadenceRecordSample)) 629 .setStartZoneOffset(ZoneOffset.systemDefault().getRules().getOffset(Instant.now())) 630 .setEndZoneOffset(ZoneOffset.systemDefault().getRules().getOffset(Instant.now())) 631 .build(); 632 } 633 getStepsCadenceRecordWithClientVersion( int rate, int version, String clientRecordId)634 static StepsCadenceRecord getStepsCadenceRecordWithClientVersion( 635 int rate, int version, String clientRecordId) { 636 Metadata.Builder testMetadataBuilder = new Metadata.Builder(); 637 testMetadataBuilder.setClientRecordId(clientRecordId); 638 testMetadataBuilder.setClientRecordVersion(version); 639 Metadata testMetaData = testMetadataBuilder.build(); 640 StepsCadenceRecord.StepsCadenceRecordSample stepsCadenceRecord = 641 new StepsCadenceRecord.StepsCadenceRecordSample( 642 rate, Instant.now().plusMillis(100)); 643 ArrayList<StepsCadenceRecord.StepsCadenceRecordSample> stepsCadenceRecords = 644 new ArrayList<>(); 645 stepsCadenceRecords.add(stepsCadenceRecord); 646 stepsCadenceRecords.add(stepsCadenceRecord); 647 648 return new StepsCadenceRecord.Builder( 649 testMetaData, 650 Instant.now(), 651 Instant.now().plusMillis(1000), 652 stepsCadenceRecords) 653 .build(); 654 } 655 getBaseStepsCadenceRecord()656 private static StepsCadenceRecord getBaseStepsCadenceRecord() { 657 StepsCadenceRecord.StepsCadenceRecordSample stepsCadenceRecord = 658 new StepsCadenceRecord.StepsCadenceRecordSample(1, Instant.now().plusMillis(100)); 659 ArrayList<StepsCadenceRecord.StepsCadenceRecordSample> stepsCadenceRecords = 660 new ArrayList<>(); 661 stepsCadenceRecords.add(stepsCadenceRecord); 662 stepsCadenceRecords.add(stepsCadenceRecord); 663 664 return new StepsCadenceRecord.Builder( 665 new Metadata.Builder().build(), 666 Instant.now(), 667 Instant.now().plusMillis(1000), 668 stepsCadenceRecords) 669 .build(); 670 } 671 getCompleteStepsCadenceRecord()672 private static StepsCadenceRecord getCompleteStepsCadenceRecord() { 673 return buildRecordForStepsCadence(1, 100); 674 } 675 buildRecordForStepsCadence( double rate, long millisFromStart)676 private static StepsCadenceRecord buildRecordForStepsCadence( 677 double rate, long millisFromStart) { 678 Device device = 679 new Device.Builder() 680 .setManufacturer("google") 681 .setModel("Pixel4a") 682 .setType(2) 683 .build(); 684 DataOrigin dataOrigin = 685 new DataOrigin.Builder().setPackageName("android.healthconnect.cts").build(); 686 Metadata.Builder testMetadataBuilder = new Metadata.Builder(); 687 testMetadataBuilder.setDevice(device).setDataOrigin(dataOrigin); 688 testMetadataBuilder.setClientRecordId("SCR" + Math.random()); 689 testMetadataBuilder.setRecordingMethod(Metadata.RECORDING_METHOD_ACTIVELY_RECORDED); 690 StepsCadenceRecord.StepsCadenceRecordSample stepsCadenceRecord = 691 new StepsCadenceRecord.StepsCadenceRecordSample( 692 rate, Instant.now().plusMillis(millisFromStart)); 693 ArrayList<StepsCadenceRecord.StepsCadenceRecordSample> stepsCadenceRecords = 694 new ArrayList<>(); 695 stepsCadenceRecords.add(stepsCadenceRecord); 696 stepsCadenceRecords.add(stepsCadenceRecord); 697 698 return new StepsCadenceRecord.Builder( 699 testMetadataBuilder.build(), 700 Instant.now(), 701 Instant.now().plusMillis(1000), 702 stepsCadenceRecords) 703 .build(); 704 } 705 } 706