• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 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;
18 
19 import static android.health.connect.datatypes.ExerciseSessionRecord.EXERCISE_DURATION_TOTAL;
20 import static android.healthconnect.cts.TestUtils.SESSION_END_TIME;
21 import static android.healthconnect.cts.TestUtils.SESSION_START_TIME;
22 
23 import static com.google.common.truth.Truth.assertThat;
24 
25 import android.health.connect.AggregateRecordsGroupedByDurationResponse;
26 import android.health.connect.AggregateRecordsRequest;
27 import android.health.connect.AggregateRecordsResponse;
28 import android.health.connect.LocalTimeRangeFilter;
29 import android.health.connect.TimeInstantRangeFilter;
30 import android.health.connect.datatypes.ExerciseSegment;
31 import android.health.connect.datatypes.ExerciseSegmentType;
32 import android.health.connect.datatypes.ExerciseSessionRecord;
33 import android.health.connect.datatypes.ExerciseSessionType;
34 
35 import org.junit.After;
36 import org.junit.Test;
37 
38 import java.time.Duration;
39 import java.time.Instant;
40 import java.time.LocalDateTime;
41 import java.time.ZoneOffset;
42 import java.time.temporal.ChronoUnit;
43 import java.util.List;
44 
45 public class ExerciseDurationAggregationTest {
46     private final TimeInstantRangeFilter mFilterAllSession =
47             new TimeInstantRangeFilter.Builder()
48                     .setStartTime(Instant.EPOCH)
49                     .setEndTime(Instant.now().plusSeconds(1000))
50                     .build();
51 
52     private final TimeInstantRangeFilter mFilterSmallWindow =
53             new TimeInstantRangeFilter.Builder()
54                     .setStartTime(SESSION_START_TIME)
55                     .setEndTime(SESSION_END_TIME)
56                     .build();
57 
58     private final AggregateRecordsRequest<Long> mAggregateAllRecordsRequest =
59             new AggregateRecordsRequest.Builder<Long>(mFilterAllSession)
60                     .addAggregationType(EXERCISE_DURATION_TOTAL)
61                     .build();
62 
63     private final AggregateRecordsRequest<Long> mAggregateInSmallWindow =
64             new AggregateRecordsRequest.Builder<Long>(mFilterSmallWindow)
65                     .addAggregationType(EXERCISE_DURATION_TOTAL)
66                     .build();
67 
68     @After
tearDown()69     public void tearDown() throws InterruptedException {
70         TestUtils.verifyDeleteRecords(
71                 ExerciseSessionRecord.class,
72                 new TimeInstantRangeFilter.Builder()
73                         .setStartTime(Instant.EPOCH)
74                         .setEndTime(Instant.now())
75                         .build());
76     }
77 
78     @Test
testSimpleAggregation_oneSession_returnsItsDuration()79     public void testSimpleAggregation_oneSession_returnsItsDuration() throws InterruptedException {
80         ExerciseSessionRecord session =
81                 new ExerciseSessionRecord.Builder(
82                                 TestUtils.generateMetadata(),
83                                 SESSION_START_TIME,
84                                 SESSION_END_TIME,
85                                 ExerciseSessionType
86                                         .EXERCISE_SESSION_TYPE_HIGH_INTENSITY_INTERVAL_TRAINING)
87                         .build();
88         AggregateRecordsResponse<Long> response =
89                 TestUtils.getAggregateResponse(mAggregateAllRecordsRequest, List.of(session));
90 
91         assertThat(response.get(EXERCISE_DURATION_TOTAL)).isNotNull();
92         assertThat(response.get(EXERCISE_DURATION_TOTAL))
93                 .isEqualTo(
94                         session.getEndTime().toEpochMilli()
95                                 - session.getStartTime().toEpochMilli());
96         assertThat(response.getZoneOffset(EXERCISE_DURATION_TOTAL))
97                 .isEqualTo(ZoneOffset.systemDefault().getRules().getOffset(Instant.now()));
98     }
99 
100     @Test
testSimpleAggregation_oneSessionStartEarlierThanWindow_returnsOverlapDuration()101     public void testSimpleAggregation_oneSessionStartEarlierThanWindow_returnsOverlapDuration()
102             throws InterruptedException {
103         ExerciseSessionRecord session =
104                 new ExerciseSessionRecord.Builder(
105                                 TestUtils.generateMetadata(),
106                                 SESSION_START_TIME.minusSeconds(10),
107                                 SESSION_END_TIME,
108                                 ExerciseSessionType
109                                         .EXERCISE_SESSION_TYPE_HIGH_INTENSITY_INTERVAL_TRAINING)
110                         .build();
111         AggregateRecordsResponse<Long> response =
112                 TestUtils.getAggregateResponse(mAggregateInSmallWindow, List.of(session));
113 
114         assertThat(response.get(EXERCISE_DURATION_TOTAL)).isNotNull();
115         assertThat(response.get(EXERCISE_DURATION_TOTAL))
116                 .isEqualTo(SESSION_END_TIME.toEpochMilli() - SESSION_START_TIME.toEpochMilli());
117         assertThat(response.getZoneOffset(EXERCISE_DURATION_TOTAL))
118                 .isEqualTo(ZoneOffset.systemDefault().getRules().getOffset(Instant.now()));
119     }
120 
121     @Test
testSimpleAggregation_oneSessionBiggerThanWindow_returnsOverlapDuration()122     public void testSimpleAggregation_oneSessionBiggerThanWindow_returnsOverlapDuration()
123             throws InterruptedException {
124         ExerciseSessionRecord session =
125                 new ExerciseSessionRecord.Builder(
126                                 TestUtils.generateMetadata(),
127                                 SESSION_START_TIME.minusSeconds(100),
128                                 SESSION_END_TIME.plusSeconds(100),
129                                 ExerciseSessionType
130                                         .EXERCISE_SESSION_TYPE_HIGH_INTENSITY_INTERVAL_TRAINING)
131                         .build();
132         AggregateRecordsResponse<Long> response =
133                 TestUtils.getAggregateResponse(mAggregateInSmallWindow, List.of(session));
134 
135         assertThat(response.get(EXERCISE_DURATION_TOTAL)).isNotNull();
136         assertThat(response.get(EXERCISE_DURATION_TOTAL))
137                 .isEqualTo(SESSION_END_TIME.toEpochMilli() - SESSION_START_TIME.toEpochMilli());
138         assertThat(response.getZoneOffset(EXERCISE_DURATION_TOTAL))
139                 .isEqualTo(ZoneOffset.systemDefault().getRules().getOffset(Instant.now()));
140     }
141 
142     @Test
testSimpleAggregation_oneSessionWithRest_returnsDurationMinusRest()143     public void testSimpleAggregation_oneSessionWithRest_returnsDurationMinusRest()
144             throws InterruptedException {
145         ExerciseSegment restSegment =
146                 new ExerciseSegment.Builder(
147                                 SESSION_START_TIME,
148                                 SESSION_START_TIME.plusSeconds(100),
149                                 ExerciseSegmentType.EXERCISE_SEGMENT_TYPE_REST)
150                         .build();
151         ExerciseSessionRecord session =
152                 new ExerciseSessionRecord.Builder(
153                                 TestUtils.generateMetadata(),
154                                 SESSION_START_TIME,
155                                 SESSION_END_TIME,
156                                 ExerciseSessionType.EXERCISE_SESSION_TYPE_CALISTHENICS)
157                         .setSegments(
158                                 List.of(
159                                         restSegment,
160                                         new ExerciseSegment.Builder(
161                                                         SESSION_START_TIME.plusSeconds(200),
162                                                         SESSION_START_TIME.plusSeconds(600),
163                                                         ExerciseSegmentType
164                                                                 .EXERCISE_SEGMENT_TYPE_BURPEE)
165                                                 .build()))
166                         .build();
167 
168         AggregateRecordsResponse<Long> response =
169                 TestUtils.getAggregateResponse(mAggregateAllRecordsRequest, List.of(session));
170 
171         assertThat(response.get(EXERCISE_DURATION_TOTAL)).isNotNull();
172 
173         long restDuration =
174                 restSegment.getEndTime().toEpochMilli() - restSegment.getStartTime().toEpochMilli();
175         assertThat(response.get(EXERCISE_DURATION_TOTAL))
176                 .isEqualTo(
177                         session.getEndTime().toEpochMilli()
178                                 - session.getStartTime().toEpochMilli()
179                                 - restDuration);
180     }
181 
182     @Test
testAggregationByDuration_oneSession_returnsSplitDurationIntoGroups()183     public void testAggregationByDuration_oneSession_returnsSplitDurationIntoGroups()
184             throws InterruptedException {
185         Instant endTime = SESSION_START_TIME.plus(10, ChronoUnit.HOURS);
186         ExerciseSessionRecord session =
187                 new ExerciseSessionRecord.Builder(
188                                 TestUtils.generateMetadata(),
189                                 SESSION_START_TIME,
190                                 endTime,
191                                 ExerciseSessionType.EXERCISE_SESSION_TYPE_BADMINTON)
192                         .build();
193         TestUtils.insertRecords(List.of(session));
194 
195         List<AggregateRecordsGroupedByDurationResponse<Long>> responses =
196                 TestUtils.getAggregateResponseGroupByDuration(
197                         new AggregateRecordsRequest.Builder<Long>(
198                                         new TimeInstantRangeFilter.Builder()
199                                                 .setStartTime(SESSION_START_TIME)
200                                                 .setEndTime(endTime)
201                                                 .build())
202                                 .addAggregationType(EXERCISE_DURATION_TOTAL)
203                                 .build(),
204                         Duration.of(1, ChronoUnit.HOURS));
205 
206         assertThat(responses).isNotEmpty();
207         assertThat(responses.size()).isEqualTo(10);
208         for (AggregateRecordsGroupedByDurationResponse<Long> response : responses) {
209             assertThat(response.get(EXERCISE_DURATION_TOTAL)).isEqualTo(3600000);
210         }
211     }
212 
213     @Test
testAggregation_oneSessionLocalTimeFilter_findsSessionWithMinOffset()214     public void testAggregation_oneSessionLocalTimeFilter_findsSessionWithMinOffset()
215             throws InterruptedException {
216         Instant endTime = Instant.now();
217         LocalDateTime endTimeLocal = LocalDateTime.ofInstant(endTime, ZoneOffset.UTC);
218 
219         long sessionDurationSeconds = 3600;
220         ExerciseSessionRecord session =
221                 new ExerciseSessionRecord.Builder(
222                                 TestUtils.generateMetadata(),
223                                 endTime.minusSeconds(sessionDurationSeconds),
224                                 endTime,
225                                 ExerciseSessionType.EXERCISE_SESSION_TYPE_BADMINTON)
226                         .setStartZoneOffset(ZoneOffset.MIN)
227                         .setEndZoneOffset(ZoneOffset.MIN)
228                         .build();
229 
230         AggregateRecordsResponse<Long> response =
231                 TestUtils.getAggregateResponse(
232                         new AggregateRecordsRequest.Builder<Long>(
233                                         new LocalTimeRangeFilter.Builder()
234                                                 .setStartTime(endTimeLocal.minusHours(25))
235                                                 .setEndTime(endTimeLocal.minusHours(15))
236                                                 .build())
237                                 .addAggregationType(EXERCISE_DURATION_TOTAL)
238                                 .build(),
239                         List.of(session));
240 
241         assertThat(response.get(EXERCISE_DURATION_TOTAL)).isEqualTo(sessionDurationSeconds * 1000);
242     }
243 
244     @Test
testAggregation_oneSessionLocalTimeFilterExcludeSegment_substractsExcludeInterval()245     public void testAggregation_oneSessionLocalTimeFilterExcludeSegment_substractsExcludeInterval()
246             throws InterruptedException {
247         Instant endTime = SESSION_START_TIME.plus(1, ChronoUnit.HOURS);
248         ExerciseSessionRecord session =
249                 new ExerciseSessionRecord.Builder(
250                                 TestUtils.generateMetadata(),
251                                 SESSION_START_TIME,
252                                 endTime,
253                                 ExerciseSessionType.EXERCISE_SESSION_TYPE_BADMINTON)
254                         .setStartZoneOffset(ZoneOffset.MIN)
255                         .setEndZoneOffset(ZoneOffset.MIN)
256                         .setSegments(
257                                 List.of(
258                                         new ExerciseSegment.Builder(
259                                                         SESSION_START_TIME.plusSeconds(10),
260                                                         endTime.minusSeconds(10),
261                                                         ExerciseSegmentType
262                                                                 .EXERCISE_SEGMENT_TYPE_PAUSE)
263                                                 .build()))
264                         .build();
265 
266         LocalDateTime endTimeLocal = LocalDateTime.ofInstant(endTime, ZoneOffset.UTC);
267         AggregateRecordsResponse<Long> response =
268                 TestUtils.getAggregateResponse(
269                         new AggregateRecordsRequest.Builder<Long>(
270                                         new LocalTimeRangeFilter.Builder()
271                                                 .setStartTime(endTimeLocal.minusHours(25))
272                                                 .setEndTime(endTimeLocal.minusHours(15))
273                                                 .build())
274                                 .addAggregationType(EXERCISE_DURATION_TOTAL)
275                                 .build(),
276                         List.of(session));
277 
278         assertThat(response.get(EXERCISE_DURATION_TOTAL)).isEqualTo(20000);
279     }
280 }
281