• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2024 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.utils;
18 
19 import static android.health.connect.datatypes.Metadata.RECORDING_METHOD_ACTIVELY_RECORDED;
20 
21 import static com.google.common.truth.Truth.assertThat;
22 
23 import android.content.Context;
24 import android.health.connect.changelog.ChangeLogTokenRequest;
25 import android.health.connect.datatypes.BasalMetabolicRateRecord;
26 import android.health.connect.datatypes.DataOrigin;
27 import android.health.connect.datatypes.Device;
28 import android.health.connect.datatypes.DistanceRecord;
29 import android.health.connect.datatypes.ExerciseCompletionGoal;
30 import android.health.connect.datatypes.ExerciseLap;
31 import android.health.connect.datatypes.ExerciseRoute;
32 import android.health.connect.datatypes.ExerciseSegment;
33 import android.health.connect.datatypes.ExerciseSegmentType;
34 import android.health.connect.datatypes.ExerciseSessionRecord;
35 import android.health.connect.datatypes.ExerciseSessionType;
36 import android.health.connect.datatypes.HeartRateRecord;
37 import android.health.connect.datatypes.Metadata;
38 import android.health.connect.datatypes.PlannedExerciseBlock;
39 import android.health.connect.datatypes.PlannedExerciseSessionRecord;
40 import android.health.connect.datatypes.PlannedExerciseStep;
41 import android.health.connect.datatypes.Record;
42 import android.health.connect.datatypes.SleepSessionRecord;
43 import android.health.connect.datatypes.StepsRecord;
44 import android.health.connect.datatypes.TotalCaloriesBurnedRecord;
45 import android.health.connect.datatypes.WeightRecord;
46 import android.health.connect.datatypes.units.Energy;
47 import android.health.connect.datatypes.units.Length;
48 import android.health.connect.datatypes.units.Mass;
49 import android.health.connect.datatypes.units.Power;
50 
51 import androidx.annotation.Nullable;
52 import androidx.test.core.app.ApplicationProvider;
53 
54 import java.time.Instant;
55 import java.time.ZoneOffset;
56 import java.time.temporal.ChronoUnit;
57 import java.util.ArrayList;
58 import java.util.Arrays;
59 import java.util.List;
60 import java.util.UUID;
61 
62 public final class DataFactory {
63     // truncate to MILLIS because HC does, so reduce flakiness in some tests.
64     public static final Instant NOW = Instant.now().truncatedTo(ChronoUnit.MILLIS);
65     public static final Instant SESSION_START_TIME = NOW.minus(10, ChronoUnit.DAYS);
66     public static final Instant SESSION_END_TIME = SESSION_START_TIME.plus(1, ChronoUnit.HOURS);
67     public static final long DEFAULT_LONG = -1;
68     public static final int DEFAULT_PAGE_SIZE = 1000;
69     public static final int MINIMUM_PAGE_SIZE = 1;
70     public static final int MAXIMUM_PAGE_SIZE = 5000;
71 
buildDevice()72     public static Device buildDevice() {
73         return new Device.Builder()
74                 .setManufacturer("google")
75                 .setModel("Pixel4a")
76                 .setType(2)
77                 .build();
78     }
79 
generateMetadata()80     public static Metadata generateMetadata() {
81         return generateMetadata(UUID.randomUUID().toString());
82     }
83 
84     /** Generates a {@link Metadata} with specific {@code id}. */
generateMetadata(String id)85     public static Metadata generateMetadata(String id) {
86         return generateMetadata(id, "clientRecordId" + Math.random());
87     }
88 
89     /** Generates a {@link Metadata} with specific {@code id} and {@code clientId}. */
generateMetadata(String id, String clientId)90     public static Metadata generateMetadata(String id, String clientId) {
91         Context context = ApplicationProvider.getApplicationContext();
92         return new Metadata.Builder()
93                 .setDevice(buildDevice())
94                 .setId(id)
95                 .setClientRecordId(clientId)
96                 .setDataOrigin(
97                         new DataOrigin.Builder().setPackageName(context.getPackageName()).build())
98                 .setRecordingMethod(Metadata.RECORDING_METHOD_UNKNOWN)
99                 .build();
100     }
101 
102     /** Generates a {@link Metadata} with a specific {@code clientId}. */
generateMetadataWithClientId(String clientId)103     public static Metadata generateMetadataWithClientId(String clientId) {
104         return generateMetadata(UUID.randomUUID().toString(), clientId);
105     }
106 
getEmptyMetadata()107     public static Metadata getEmptyMetadata() {
108         return new Metadata.Builder().build();
109     }
110 
111     /** Creates a {@link Metadata} with the given record id. */
getMetadataForId(String id)112     public static Metadata getMetadataForId(String id) {
113         return new Metadata.Builder().setId(id).build();
114     }
115 
116     /** Creates a {@link Metadata} with the given record id and data origin. */
getMetadataForId(String id, DataOrigin dataOrigin)117     public static Metadata getMetadataForId(String id, DataOrigin dataOrigin) {
118         return new Metadata.Builder().setId(id).setDataOrigin(dataOrigin).build();
119     }
120 
121     /** Creates a {@link Metadata} with the given client record id. */
getMetadataForClientId(String clientId)122     public static Metadata getMetadataForClientId(String clientId) {
123         return new Metadata.Builder().setClientRecordId(clientId).build();
124     }
125 
126     /** Creates a {@link Metadata} with the given client record id. */
getMetadataForClientId(String clientId, DataOrigin dataOrigin)127     public static Metadata getMetadataForClientId(String clientId, DataOrigin dataOrigin) {
128         return new Metadata.Builder().setClientRecordId(clientId).setDataOrigin(dataOrigin).build();
129     }
130 
131     /** Creates a {@link Metadata} with the given client record id. */
getMetadataForClientIdAndVersion(String clientId, long clientVersion)132     public static Metadata getMetadataForClientIdAndVersion(String clientId, long clientVersion) {
133         return new Metadata.Builder()
134                 .setClientRecordId(clientId)
135                 .setClientRecordVersion(clientVersion)
136                 .build();
137     }
138 
139     /** Creates a {@link Metadata} with the given data origin. */
getMetadata(DataOrigin dataOrigin)140     public static Metadata getMetadata(DataOrigin dataOrigin) {
141         return new Metadata.Builder().setDataOrigin(dataOrigin).build();
142     }
143 
144     /** Creates a {@link DataOrigin} with the given package name. */
getDataOrigin(String packageName)145     public static DataOrigin getDataOrigin(String packageName) {
146         return new DataOrigin.Builder().setPackageName(packageName).build();
147     }
148 
149     /** Creates a list of {@link DataOrigin} from a list of package names. */
getDataOrigins(String... packageNames)150     public static List<DataOrigin> getDataOrigins(String... packageNames) {
151         return Arrays.stream(packageNames).map(DataFactory::getDataOrigin).toList();
152     }
153 
buildSleepSession()154     public static SleepSessionRecord buildSleepSession() {
155         return buildSleepSession(generateMetadata());
156     }
157 
158     /** Builds a {@link SleepSessionRecord} with a specific {@code clientId}. */
buildSleepSessionWithClientId(String clientId)159     public static SleepSessionRecord buildSleepSessionWithClientId(String clientId) {
160         return buildSleepSession(generateMetadataWithClientId(clientId));
161     }
162 
163     /** Builds a {@link SleepSessionRecord} with empty {@link Metadata}. */
buildSleepSessionWithEmptyMetadata()164     public static SleepSessionRecord buildSleepSessionWithEmptyMetadata() {
165         return buildSleepSession(getEmptyMetadata());
166     }
167 
168     /** Builds a {@link SleepSessionRecord} with a specific {@link Metadata}. */
buildSleepSession(Metadata metadata)169     public static SleepSessionRecord buildSleepSession(Metadata metadata) {
170         return new SleepSessionRecord.Builder(metadata, SESSION_START_TIME, SESSION_END_TIME)
171                 .setNotes("warm")
172                 .setTitle("Afternoon nap")
173                 .setStages(
174                         List.of(
175                                 new SleepSessionRecord.Stage(
176                                         SESSION_START_TIME,
177                                         SESSION_START_TIME.plusSeconds(300),
178                                         SleepSessionRecord.StageType.STAGE_TYPE_SLEEPING_LIGHT),
179                                 new SleepSessionRecord.Stage(
180                                         SESSION_START_TIME.plusSeconds(300),
181                                         SESSION_START_TIME.plusSeconds(600),
182                                         SleepSessionRecord.StageType.STAGE_TYPE_SLEEPING_REM),
183                                 new SleepSessionRecord.Stage(
184                                         SESSION_START_TIME.plusSeconds(900),
185                                         SESSION_START_TIME.plusSeconds(1200),
186                                         SleepSessionRecord.StageType.STAGE_TYPE_SLEEPING_DEEP)))
187                 .build();
188     }
189 
190     /** Builds a {@link ExerciseSessionRecord} with {@link #generateMetadata()}. */
buildExerciseSession()191     public static ExerciseSessionRecord buildExerciseSession() {
192         return buildExerciseSession(generateMetadata());
193     }
194 
195     /** Builds a {@link ExerciseSessionRecord} with an empty {@link Metadata}. */
buildExerciseSessionWithEmptyMetadata()196     public static ExerciseSessionRecord buildExerciseSessionWithEmptyMetadata() {
197         return buildExerciseSession(getEmptyMetadata());
198     }
199 
200     /** Builds a {@link ExerciseSessionRecord} with a specific {@code clientId}. */
buildExerciseSessionWithClientId(String clientId)201     public static ExerciseSessionRecord buildExerciseSessionWithClientId(String clientId) {
202         return buildExerciseSession(generateMetadataWithClientId(clientId));
203     }
204 
205     /** Builds a {@link ExerciseSessionRecord} with a specific {@link Metadata}. */
buildExerciseSession(Metadata metadata)206     public static ExerciseSessionRecord buildExerciseSession(Metadata metadata) {
207         return new ExerciseSessionRecord.Builder(
208                         metadata,
209                         SESSION_START_TIME,
210                         SESSION_END_TIME,
211                         ExerciseSessionType.EXERCISE_SESSION_TYPE_OTHER_WORKOUT)
212                 .setRoute(buildExerciseRoute())
213                 .setLaps(
214                         List.of(
215                                 new ExerciseLap.Builder(
216                                                 SESSION_START_TIME,
217                                                 SESSION_START_TIME.plusSeconds(20))
218                                         .setLength(Length.fromMeters(10))
219                                         .build(),
220                                 new ExerciseLap.Builder(
221                                                 SESSION_END_TIME.minusSeconds(20), SESSION_END_TIME)
222                                         .build()))
223                 .setSegments(
224                         List.of(
225                                 new ExerciseSegment.Builder(
226                                                 SESSION_START_TIME.plusSeconds(1),
227                                                 SESSION_START_TIME.plusSeconds(10),
228                                                 ExerciseSegmentType
229                                                         .EXERCISE_SEGMENT_TYPE_BENCH_PRESS)
230                                         .build(),
231                                 new ExerciseSegment.Builder(
232                                                 SESSION_START_TIME.plusSeconds(21),
233                                                 SESSION_START_TIME.plusSeconds(124),
234                                                 ExerciseSegmentType.EXERCISE_SEGMENT_TYPE_BURPEE)
235                                         .setRepetitionsCount(15)
236                                         .build()))
237                 .setEndZoneOffset(ZoneOffset.MAX)
238                 .setStartZoneOffset(ZoneOffset.MIN)
239                 .setNotes("rain")
240                 .setTitle("Morning training")
241                 .build();
242     }
243 
buildExerciseRoute()244     public static ExerciseRoute buildExerciseRoute() {
245         return new ExerciseRoute(
246                 List.of(
247                         buildLocationTimePoint(SESSION_START_TIME),
248                         buildLocationTimePoint(SESSION_START_TIME),
249                         buildLocationTimePoint(SESSION_START_TIME)));
250     }
251 
buildLocationTimePoint(Instant startTime)252     public static ExerciseRoute.Location buildLocationTimePoint(Instant startTime) {
253         return new ExerciseRoute.Location.Builder(
254                         Instant.ofEpochMilli(
255                                 (long) (startTime.toEpochMilli() + 10 + Math.random() * 50)),
256                         Math.random() * 50,
257                         Math.random() * 50)
258                 .build();
259     }
260 
261     /** Returns a training plan builder, prepopulated with test data. */
plannedExerciseSession(Metadata metadata)262     public static PlannedExerciseSessionRecord.Builder plannedExerciseSession(Metadata metadata) {
263         PlannedExerciseSessionRecord.Builder sessionBuilder =
264                 new PlannedExerciseSessionRecord.Builder(
265                         metadata,
266                         ExerciseSessionType.EXERCISE_SESSION_TYPE_BIKING,
267                         SESSION_START_TIME,
268                         SESSION_END_TIME);
269         sessionBuilder.setNotes("Some notes");
270         sessionBuilder.setTitle("Some training plan");
271         sessionBuilder.setStartZoneOffset(ZoneOffset.UTC);
272         sessionBuilder.setEndZoneOffset(ZoneOffset.UTC);
273         var stepBuilder =
274                 new PlannedExerciseStep.Builder(
275                         ExerciseSegmentType.EXERCISE_SEGMENT_TYPE_BIKING,
276                         PlannedExerciseStep.EXERCISE_CATEGORY_ACTIVE,
277                         new ExerciseCompletionGoal.DistanceGoal(Length.fromMeters(100)));
278         var blockBuilder = new PlannedExerciseBlock.Builder(3).setDescription("Main set");
279         blockBuilder.setSteps(List.of(stepBuilder.build()));
280         sessionBuilder.setBlocks(List.of(blockBuilder.build()));
281 
282         return sessionBuilder;
283     }
284 
285     /** Gets a {@link HeartRateRecord} with an empty {@link Metadata}. */
getHeartRateRecordWithEmptyMetadata()286     public static HeartRateRecord getHeartRateRecordWithEmptyMetadata() {
287         return getHeartRateRecord(72, getEmptyMetadata());
288     }
289 
290     /** Gets a {@link HeartRateRecord} with a specific heart rate and {@link Metadata}. */
getHeartRateRecord(int heartRate, Metadata metadata)291     public static HeartRateRecord getHeartRateRecord(int heartRate, Metadata metadata) {
292         Instant instant = NOW;
293         HeartRateRecord.HeartRateSample heartRateSample =
294                 new HeartRateRecord.HeartRateSample(heartRate, instant.plusMillis(10));
295         return new HeartRateRecord.Builder(
296                         metadata, instant, instant.plusMillis(1000), List.of(heartRateSample))
297                 .build();
298     }
299 
getHeartRateRecord()300     public static HeartRateRecord getHeartRateRecord() {
301         return getHeartRateRecord(72);
302     }
303 
getHeartRateRecord(int heartRate, String clientId)304     public static HeartRateRecord getHeartRateRecord(int heartRate, String clientId) {
305         return getHeartRateRecord(heartRate, NOW.plusMillis(100), clientId);
306     }
307 
getHeartRateRecord(int heartRate)308     public static HeartRateRecord getHeartRateRecord(int heartRate) {
309         return getHeartRateRecord(heartRate, NOW.plusMillis(100));
310     }
311 
getHeartRateRecord(int heartRate, Instant instant)312     public static HeartRateRecord getHeartRateRecord(int heartRate, Instant instant) {
313         return getHeartRateRecord(heartRate, instant, "HR" + Math.random());
314     }
315 
getHeartRateRecord( List<HeartRateRecord.HeartRateSample> samples, Instant start, Instant end)316     public static HeartRateRecord getHeartRateRecord(
317             List<HeartRateRecord.HeartRateSample> samples, Instant start, Instant end) {
318         return new HeartRateRecord.Builder(getEmptyMetadata(), start, end, samples).build();
319     }
320 
getHeartRateRecord( int heartRate, Instant instant, String clientId)321     public static HeartRateRecord getHeartRateRecord(
322             int heartRate, Instant instant, String clientId) {
323         String packageName = ApplicationProvider.getApplicationContext().getPackageName();
324         HeartRateRecord.HeartRateSample heartRateSample =
325                 new HeartRateRecord.HeartRateSample(heartRate, instant);
326         ArrayList<HeartRateRecord.HeartRateSample> heartRateSamples = new ArrayList<>();
327         heartRateSamples.add(heartRateSample);
328         heartRateSamples.add(heartRateSample);
329         Device device = buildDevice();
330         DataOrigin dataOrigin = new DataOrigin.Builder().setPackageName(packageName).build();
331 
332         return new HeartRateRecord.Builder(
333                         new Metadata.Builder()
334                                 .setDevice(device)
335                                 .setDataOrigin(dataOrigin)
336                                 .setClientRecordId(clientId)
337                                 .build(),
338                         instant.minusMillis(100),
339                         instant.plusMillis(100),
340                         heartRateSamples)
341                 .build();
342     }
343 
344     /** Creates and returns a {@link WeightRecord} with the specified arguments. */
getWeightRecord(double grams, Instant time)345     public static WeightRecord getWeightRecord(double grams, Instant time) {
346         return new WeightRecord.Builder(new Metadata.Builder().build(), time, Mass.fromGrams(grams))
347                 .build();
348     }
349 
getWeightRecord(double weight, Instant time, ZoneOffset offset)350     public static WeightRecord getWeightRecord(double weight, Instant time, ZoneOffset offset) {
351         return new WeightRecord.Builder(getEmptyMetadata(), time, Mass.fromGrams(weight))
352                 .setZoneOffset(offset)
353                 .build();
354     }
355 
356     /** Returns a new weight record with the specified fields. */
getWeightRecord(double grams, Instant time, String clientId)357     public static WeightRecord getWeightRecord(double grams, Instant time, String clientId) {
358         return new WeightRecord.Builder(
359                         getMetadataForClientId(clientId), time, Mass.fromGrams(grams))
360                 .build();
361     }
362 
getStepsRecordWithEmptyMetaData()363     public static StepsRecord getStepsRecordWithEmptyMetaData() {
364         return getStepsRecord(10, getEmptyMetadata());
365     }
366 
getStepsRecord()367     public static StepsRecord getStepsRecord() {
368         return getStepsRecord(10);
369     }
370 
371     /** Creates and returns a {@link StepsRecord} with the specified arguments. */
getStepsRecord(long steps)372     public static StepsRecord getStepsRecord(long steps) {
373         return getStepsRecord(steps, generateMetadata());
374     }
375 
376     /** Creates and returns a {@link StepsRecord} with the specified arguments. */
getStepsRecord(long steps, String clientId)377     public static StepsRecord getStepsRecord(long steps, String clientId) {
378         return getStepsRecord(steps, getMetadataForClientId(clientId));
379     }
380 
381     /** Creates and returns a {@link StepsRecord} with the specified metadata. */
getStepsRecord(long steps, Metadata metadata)382     public static StepsRecord getStepsRecord(long steps, Metadata metadata) {
383         return new StepsRecord.Builder(metadata, NOW, NOW.plusMillis(1000), steps).build();
384     }
385 
386     /** Creates and returns a {@link StepsRecord} with the specified arguments. */
getStepsRecord(long steps, Instant start, Instant end)387     public static StepsRecord getStepsRecord(long steps, Instant start, Instant end) {
388         return new StepsRecord.Builder(getEmptyMetadata(), start, end, steps).build();
389     }
390 
391     /** Creates and returns a {@link StepsRecord} with the specified arguments. */
getStepsRecord( long steps, Instant start, Instant end, String clientId)392     public static StepsRecord getStepsRecord(
393             long steps, Instant start, Instant end, String clientId) {
394         return new StepsRecord.Builder(getMetadataForClientId(clientId), start, end, steps).build();
395     }
396 
getStepsRecord(String id)397     public static StepsRecord getStepsRecord(String id) {
398         return new StepsRecord.Builder(generateMetadata(id), NOW, NOW.plusMillis(1000), 10).build();
399     }
400 
401     /** Creates and returns a {@link StepsRecord} with default arguments. */
getCompleteStepsRecord()402     public static StepsRecord getCompleteStepsRecord() {
403         return getCompleteStepsRecord(
404                 NOW, NOW.plusMillis(1000), /* clientRecordId= */ "SR" + Math.random());
405     }
406 
407     /** Creates and returns a {@link StepsRecord} with the specified arguments. */
getCompleteStepsRecord( Instant startTime, Instant endTime, String clientRecordId)408     public static StepsRecord getCompleteStepsRecord(
409             Instant startTime, Instant endTime, String clientRecordId) {
410         return getCompleteStepsRecord(startTime, endTime, clientRecordId, /* count= */ 10);
411     }
412 
413     /** Creates and returns a {@link StepsRecord} with the specified arguments. */
getCompleteStepsRecord( String id, Instant startTime, Instant endTime, long count)414     public static StepsRecord getCompleteStepsRecord(
415             String id, Instant startTime, Instant endTime, long count) {
416         return getCompleteStepsRecord(
417                 id,
418                 startTime,
419                 endTime,
420                 /* clientRecordId= */ null,
421                 /* clientRecordVersion= */ 0L,
422                 count);
423     }
424 
425     /** Creates and returns a {@link StepsRecord} with the specified arguments. */
getCompleteStepsRecord( Instant startTime, Instant endTime, long count)426     public static StepsRecord getCompleteStepsRecord(
427             Instant startTime, Instant endTime, long count) {
428         return getCompleteStepsRecord(
429                 startTime,
430                 endTime,
431                 /* clientRecordId= */ null,
432                 /* clientRecordVersion= */ 0L,
433                 count);
434     }
435 
436     /** Creates and returns a {@link StepsRecord} with the specified arguments. */
getCompleteStepsRecord( Instant startTime, Instant endTime, String clientRecordId, long count)437     public static StepsRecord getCompleteStepsRecord(
438             Instant startTime, Instant endTime, String clientRecordId, long count) {
439         return getCompleteStepsRecord(
440                 startTime, endTime, clientRecordId, /* clientRecordVersion= */ 0L, count);
441     }
442 
443     /** Creates and returns a {@link StepsRecord} with the specified arguments. */
getCompleteStepsRecord( Instant startTime, Instant endTime, String clientRecordId, long clientRecordVersion, long count)444     public static StepsRecord getCompleteStepsRecord(
445             Instant startTime,
446             Instant endTime,
447             String clientRecordId,
448             long clientRecordVersion,
449             long count) {
450         return getCompleteStepsRecord(
451                 /* id= */ null, startTime, endTime, clientRecordId, clientRecordVersion, count);
452     }
453 
454     /** Creates and returns a {@link StepsRecord} with the specified arguments. */
getCompleteStepsRecord( String id, Instant startTime, Instant endTime, String clientRecordId, long clientRecordVersion, long count)455     public static StepsRecord getCompleteStepsRecord(
456             String id,
457             Instant startTime,
458             Instant endTime,
459             String clientRecordId,
460             long clientRecordVersion,
461             long count) {
462         Device device =
463                 new Device.Builder().setManufacturer("google").setModel("Pixel").setType(1).build();
464         DataOrigin dataOrigin =
465                 new DataOrigin.Builder().setPackageName("android.healthconnect.cts").build();
466 
467         Metadata.Builder testMetadataBuilder = new Metadata.Builder();
468         if (id != null) {
469             testMetadataBuilder.setId(id);
470         }
471         testMetadataBuilder.setDevice(device).setDataOrigin(dataOrigin);
472         testMetadataBuilder.setClientRecordId(clientRecordId);
473         testMetadataBuilder.setClientRecordVersion(clientRecordVersion);
474         testMetadataBuilder.setRecordingMethod(RECORDING_METHOD_ACTIVELY_RECORDED);
475         Metadata testMetaData = testMetadataBuilder.build();
476         assertThat(testMetaData.getRecordingMethod()).isEqualTo(RECORDING_METHOD_ACTIVELY_RECORDED);
477         return new StepsRecord.Builder(testMetaData, startTime, endTime, count).build();
478     }
479 
getUpdatedStepsRecord( Record record, String id, String clientRecordId)480     public static StepsRecord getUpdatedStepsRecord(
481             Record record, String id, String clientRecordId) {
482         Metadata metadata = record.getMetadata();
483         Metadata metadataWithId =
484                 new Metadata.Builder()
485                         .setId(id)
486                         .setClientRecordId(clientRecordId)
487                         .setClientRecordVersion(metadata.getClientRecordVersion())
488                         .setDataOrigin(metadata.getDataOrigin())
489                         .setDevice(metadata.getDevice())
490                         .setLastModifiedTime(metadata.getLastModifiedTime())
491                         .build();
492         return new StepsRecord.Builder(metadataWithId, NOW, NOW.plusMillis(2000), 20)
493                 .setStartZoneOffset(ZoneOffset.systemDefault().getRules().getOffset(NOW))
494                 .setEndZoneOffset(ZoneOffset.systemDefault().getRules().getOffset(NOW))
495                 .build();
496     }
497 
498     /** Creates a {@link DistanceRecord}. */
getDistanceRecord()499     public static DistanceRecord getDistanceRecord() {
500         return getDistanceRecord(10.0, NOW, NOW.plusMillis(1000));
501     }
502 
503     /** Creates a {@link DistanceRecord} with a specified {@code clientId}. */
getDistanceRecordWithClientId(String clientId)504     public static DistanceRecord getDistanceRecordWithClientId(String clientId) {
505         return getDistanceRecord(
506                 10,
507                 NOW,
508                 NOW.plusMillis(1000),
509                 /* startZoneOffset= */ null,
510                 /* endZoneOffset= */ null,
511                 generateMetadataWithClientId(clientId));
512     }
513 
514     /** Create a {@link DistanceRecord} with non empty record ID. */
getDistanceRecordWithNonEmptyId()515     public static DistanceRecord getDistanceRecordWithNonEmptyId() {
516         return getDistanceRecord(
517                 10,
518                 NOW,
519                 NOW.plusMillis(1000),
520                 /* startZoneOffset= */ null,
521                 /* endZoneOffset= */ null,
522                 generateMetadata());
523     }
524 
525     /** Create a {@link DistanceRecord} with empty {@link Metadata}. */
getDistanceRecordWithEmptyMetadata()526     public static DistanceRecord getDistanceRecordWithEmptyMetadata() {
527         return getDistanceRecord(
528                 10,
529                 NOW,
530                 NOW.plusMillis(1000),
531                 /* startZoneOffset= */ null,
532                 /* endZoneOffset= */ null,
533                 getEmptyMetadata());
534     }
535 
536     /** Create a {@link DistanceRecord} with the specified arguments. */
getDistanceRecord(double distance, Instant start, Instant end)537     public static DistanceRecord getDistanceRecord(double distance, Instant start, Instant end) {
538         return getDistanceRecord(
539                 distance,
540                 start,
541                 end,
542                 /* startZoneOffset= */ null,
543                 /* endZoneOffset= */ null,
544                 getEmptyMetadata());
545     }
546 
547     /** Create a {@link DistanceRecord} with the specified arguments. */
getDistanceRecord( double distance, Instant start, Instant end, ZoneOffset offset)548     public static DistanceRecord getDistanceRecord(
549             double distance, Instant start, Instant end, ZoneOffset offset) {
550         return getDistanceRecord(distance, start, end, offset, offset, getEmptyMetadata());
551     }
552 
553     /** Create a {@link DistanceRecord} with the specified arguments. */
getDistanceRecord( double distance, Instant start, Instant end, String clientId)554     public static DistanceRecord getDistanceRecord(
555             double distance, Instant start, Instant end, String clientId) {
556         return getDistanceRecord(
557                 distance,
558                 start,
559                 end,
560                 /* startZoneOffset= */ null,
561                 /* endZoneOffset= */ null,
562                 getMetadataForClientId(clientId));
563     }
564 
565     /** Create a {@link DistanceRecord} with the specified arguments. */
getDistanceRecord( double distance, Instant start, Instant end, @Nullable ZoneOffset startZoneOffset, @Nullable ZoneOffset endZoneOffset, Metadata metadata)566     public static DistanceRecord getDistanceRecord(
567             double distance,
568             Instant start,
569             Instant end,
570             @Nullable ZoneOffset startZoneOffset,
571             @Nullable ZoneOffset endZoneOffset,
572             Metadata metadata) {
573         DistanceRecord.Builder builder =
574                 new DistanceRecord.Builder(metadata, start, end, Length.fromMeters(distance));
575         if (startZoneOffset != null) {
576             builder.setStartZoneOffset(startZoneOffset);
577         }
578         if (endZoneOffset != null) {
579             builder.setEndZoneOffset(endZoneOffset);
580         }
581         return builder.build();
582     }
583 
584     /** Gets a {@link TotalCaloriesBurnedRecord} with a specific {@code clientId}. */
getTotalCaloriesBurnedRecord(String clientId)585     public static TotalCaloriesBurnedRecord getTotalCaloriesBurnedRecord(String clientId) {
586         return getTotalCaloriesBurnedRecord(getMetadataForClientId(clientId));
587     }
588 
589     /** Gets a {@link TotalCaloriesBurnedRecord} with a specific {@link Metadata}. */
getTotalCaloriesBurnedRecord(Metadata metadata)590     public static TotalCaloriesBurnedRecord getTotalCaloriesBurnedRecord(Metadata metadata) {
591         return new TotalCaloriesBurnedRecord.Builder(
592                         metadata, NOW, NOW.plusMillis(1000), Energy.fromCalories(10.0))
593                 .build();
594     }
595 
getTotalCaloriesBurnedRecordWithEmptyMetadata()596     public static TotalCaloriesBurnedRecord getTotalCaloriesBurnedRecordWithEmptyMetadata() {
597         return getTotalCaloriesBurnedRecord(getEmptyMetadata());
598     }
599 
getTestRecords()600     public static List<Record> getTestRecords() {
601         return Arrays.asList(
602                 getStepsRecord(),
603                 getHeartRateRecord(),
604                 getBasalMetabolicRateRecord(),
605                 buildExerciseSession());
606     }
607 
getChangeLogTokenRequestForTestRecordTypes()608     public static ChangeLogTokenRequest.Builder getChangeLogTokenRequestForTestRecordTypes() {
609         return new ChangeLogTokenRequest.Builder()
610                 .addRecordType(StepsRecord.class)
611                 .addRecordType(HeartRateRecord.class)
612                 .addRecordType(BasalMetabolicRateRecord.class)
613                 .addRecordType(ExerciseSessionRecord.class);
614     }
615 
getBasalMetabolicRateRecord()616     public static BasalMetabolicRateRecord getBasalMetabolicRateRecord() {
617         return new BasalMetabolicRateRecord.Builder(generateMetadata(), NOW, Power.fromWatts(100.0))
618                 .build();
619     }
620 }
621