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