blob: b86d5fb52e30dce3e25b9380f61fe5fbf3562dd0 [file] [log] [blame]
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.healthconnect.cts;
import static android.health.connect.datatypes.NutritionRecord.BIOTIN_TOTAL;
import static android.health.connect.datatypes.NutritionRecord.CAFFEINE_TOTAL;
import static android.health.connect.datatypes.NutritionRecord.CALCIUM_TOTAL;
import static android.health.connect.datatypes.NutritionRecord.CHLORIDE_TOTAL;
import static android.health.connect.datatypes.NutritionRecord.CHOLESTEROL_TOTAL;
import static android.health.connect.datatypes.NutritionRecord.CHROMIUM_TOTAL;
import static android.health.connect.datatypes.NutritionRecord.COPPER_TOTAL;
import static android.health.connect.datatypes.NutritionRecord.DIETARY_FIBER_TOTAL;
import static android.health.connect.datatypes.NutritionRecord.ENERGY_FROM_FAT_TOTAL;
import static android.health.connect.datatypes.NutritionRecord.ENERGY_TOTAL;
import static android.health.connect.datatypes.NutritionRecord.FOLATE_TOTAL;
import static android.health.connect.datatypes.NutritionRecord.FOLIC_ACID_TOTAL;
import static android.health.connect.datatypes.NutritionRecord.IODINE_TOTAL;
import static android.health.connect.datatypes.NutritionRecord.IRON_TOTAL;
import static android.health.connect.datatypes.NutritionRecord.MAGNESIUM_TOTAL;
import static android.health.connect.datatypes.NutritionRecord.MANGANESE_TOTAL;
import static android.health.connect.datatypes.NutritionRecord.MOLYBDENUM_TOTAL;
import static android.health.connect.datatypes.NutritionRecord.MONOUNSATURATED_FAT_TOTAL;
import static android.health.connect.datatypes.NutritionRecord.NIACIN_TOTAL;
import static android.health.connect.datatypes.NutritionRecord.PANTOTHENIC_ACID_TOTAL;
import static android.health.connect.datatypes.NutritionRecord.PHOSPHORUS_TOTAL;
import static android.health.connect.datatypes.NutritionRecord.POLYUNSATURATED_FAT_TOTAL;
import static android.health.connect.datatypes.NutritionRecord.POTASSIUM_TOTAL;
import static android.health.connect.datatypes.NutritionRecord.PROTEIN_TOTAL;
import static android.health.connect.datatypes.NutritionRecord.RIBOFLAVIN_TOTAL;
import static android.health.connect.datatypes.NutritionRecord.SATURATED_FAT_TOTAL;
import static android.health.connect.datatypes.NutritionRecord.SELENIUM_TOTAL;
import static android.health.connect.datatypes.NutritionRecord.SODIUM_TOTAL;
import static android.health.connect.datatypes.NutritionRecord.SUGAR_TOTAL;
import static android.health.connect.datatypes.NutritionRecord.THIAMIN_TOTAL;
import static android.health.connect.datatypes.NutritionRecord.TOTAL_CARBOHYDRATE_TOTAL;
import static android.health.connect.datatypes.NutritionRecord.TOTAL_FAT_TOTAL;
import static android.health.connect.datatypes.NutritionRecord.UNSATURATED_FAT_TOTAL;
import static android.health.connect.datatypes.NutritionRecord.VITAMIN_A_TOTAL;
import static android.health.connect.datatypes.NutritionRecord.VITAMIN_B12_TOTAL;
import static android.health.connect.datatypes.NutritionRecord.VITAMIN_B6_TOTAL;
import static android.health.connect.datatypes.NutritionRecord.VITAMIN_C_TOTAL;
import static android.health.connect.datatypes.NutritionRecord.VITAMIN_D_TOTAL;
import static android.health.connect.datatypes.NutritionRecord.VITAMIN_E_TOTAL;
import static android.health.connect.datatypes.NutritionRecord.VITAMIN_K_TOTAL;
import static android.health.connect.datatypes.NutritionRecord.ZINC_TOTAL;
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
import android.health.connect.AggregateRecordsRequest;
import android.health.connect.AggregateRecordsResponse;
import android.health.connect.DeleteUsingFiltersRequest;
import android.health.connect.HealthConnectException;
import android.health.connect.ReadRecordsRequestUsingFilters;
import android.health.connect.ReadRecordsRequestUsingIds;
import android.health.connect.RecordIdFilter;
import android.health.connect.TimeInstantRangeFilter;
import android.health.connect.changelog.ChangeLogTokenRequest;
import android.health.connect.changelog.ChangeLogTokenResponse;
import android.health.connect.changelog.ChangeLogsRequest;
import android.health.connect.changelog.ChangeLogsResponse;
import android.health.connect.datatypes.AggregationType;
import android.health.connect.datatypes.DataOrigin;
import android.health.connect.datatypes.Device;
import android.health.connect.datatypes.Metadata;
import android.health.connect.datatypes.NutritionRecord;
import android.health.connect.datatypes.Record;
import android.health.connect.datatypes.units.Energy;
import android.health.connect.datatypes.units.Mass;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.runner.AndroidJUnit4;
import org.junit.After;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.time.Instant;
import java.time.ZoneOffset;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.UUID;
@RunWith(AndroidJUnit4.class)
public class NutritionRecordTest {
private static final String TAG = "NutritionRecordTest";
private List<AggregationType<Mass>> mMassAggregateTypesList =
Arrays.asList(
BIOTIN_TOTAL,
CAFFEINE_TOTAL,
CALCIUM_TOTAL,
CHLORIDE_TOTAL,
CHOLESTEROL_TOTAL,
CHROMIUM_TOTAL,
COPPER_TOTAL,
DIETARY_FIBER_TOTAL,
FOLATE_TOTAL,
FOLIC_ACID_TOTAL,
IODINE_TOTAL,
IRON_TOTAL,
MAGNESIUM_TOTAL,
MANGANESE_TOTAL,
MOLYBDENUM_TOTAL,
MONOUNSATURATED_FAT_TOTAL,
NIACIN_TOTAL,
PANTOTHENIC_ACID_TOTAL,
PHOSPHORUS_TOTAL,
POLYUNSATURATED_FAT_TOTAL,
POTASSIUM_TOTAL,
PROTEIN_TOTAL,
RIBOFLAVIN_TOTAL,
SATURATED_FAT_TOTAL,
SELENIUM_TOTAL,
SODIUM_TOTAL,
SUGAR_TOTAL,
THIAMIN_TOTAL,
TOTAL_CARBOHYDRATE_TOTAL,
TOTAL_FAT_TOTAL,
UNSATURATED_FAT_TOTAL,
VITAMIN_A_TOTAL,
VITAMIN_B12_TOTAL,
VITAMIN_B6_TOTAL,
VITAMIN_C_TOTAL,
VITAMIN_D_TOTAL,
VITAMIN_E_TOTAL,
VITAMIN_K_TOTAL,
ZINC_TOTAL);
@After
public void tearDown() throws InterruptedException {
TestUtils.verifyDeleteRecords(
NutritionRecord.class,
new TimeInstantRangeFilter.Builder()
.setStartTime(Instant.EPOCH)
.setEndTime(Instant.now())
.build());
TestUtils.deleteAllStagedRemoteData();
}
@Test
public void testInsertNutritionRecord() throws InterruptedException {
List<Record> records = List.of(getBaseNutritionRecord(), getCompleteNutritionRecord());
TestUtils.insertRecords(records);
}
@Test
public void testReadNutritionRecord_usingIds() throws InterruptedException {
List<Record> recordList =
Arrays.asList(getCompleteNutritionRecord(), getCompleteNutritionRecord());
List<Record> insertedRecords = TestUtils.insertRecords(recordList);
readNutritionRecordUsingIds(insertedRecords);
}
@Test
public void testReadNutritionRecord_invalidIds() throws InterruptedException {
ReadRecordsRequestUsingIds<NutritionRecord> request =
new ReadRecordsRequestUsingIds.Builder<>(NutritionRecord.class)
.addId(UUID.randomUUID().toString())
.build();
List<NutritionRecord> result = TestUtils.readRecords(request);
assertThat(result.size()).isEqualTo(0);
}
@Test
public void testReadNutritionRecord_usingClientRecordIds() throws InterruptedException {
List<Record> recordList =
Arrays.asList(getCompleteNutritionRecord(), getCompleteNutritionRecord());
List<Record> insertedRecords = TestUtils.insertRecords(recordList);
readNutritionRecordUsingClientId(insertedRecords);
}
@Test
public void testReadNutritionRecord_invalidClientRecordIds() throws InterruptedException {
ReadRecordsRequestUsingIds<NutritionRecord> request =
new ReadRecordsRequestUsingIds.Builder<>(NutritionRecord.class)
.addClientRecordId("abc")
.build();
List<NutritionRecord> result = TestUtils.readRecords(request);
assertThat(result.size()).isEqualTo(0);
}
@Test
public void testReadNutritionRecordUsingFilters_default() throws InterruptedException {
List<NutritionRecord> oldNutritionRecords =
TestUtils.readRecords(
new ReadRecordsRequestUsingFilters.Builder<>(NutritionRecord.class)
.build());
NutritionRecord testRecord = getCompleteNutritionRecord();
TestUtils.insertRecords(Collections.singletonList(testRecord));
List<NutritionRecord> newNutritionRecords =
TestUtils.readRecords(
new ReadRecordsRequestUsingFilters.Builder<>(NutritionRecord.class)
.build());
assertThat(newNutritionRecords.size()).isEqualTo(oldNutritionRecords.size() + 1);
assertThat(newNutritionRecords.get(newNutritionRecords.size() - 1).equals(testRecord))
.isTrue();
}
@Test
public void testReadNutritionRecordUsingFilters_timeFilter() throws InterruptedException {
TimeInstantRangeFilter filter =
new TimeInstantRangeFilter.Builder()
.setStartTime(Instant.now())
.setEndTime(Instant.now().plusMillis(3000))
.build();
NutritionRecord testRecord = getCompleteNutritionRecord();
TestUtils.insertRecords(Collections.singletonList(testRecord));
List<NutritionRecord> newNutritionRecords =
TestUtils.readRecords(
new ReadRecordsRequestUsingFilters.Builder<>(NutritionRecord.class)
.setTimeRangeFilter(filter)
.build());
assertThat(newNutritionRecords.size()).isEqualTo(1);
assertThat(newNutritionRecords.get(newNutritionRecords.size() - 1).equals(testRecord))
.isTrue();
}
@Test
public void testReadNutritionRecordUsingFilters_dataFilter_correct()
throws InterruptedException {
Context context = ApplicationProvider.getApplicationContext();
List<NutritionRecord> oldNutritionRecords =
TestUtils.readRecords(
new ReadRecordsRequestUsingFilters.Builder<>(NutritionRecord.class)
.addDataOrigins(
new DataOrigin.Builder()
.setPackageName(context.getPackageName())
.build())
.build());
NutritionRecord testRecord = getCompleteNutritionRecord();
TestUtils.insertRecords(Collections.singletonList(testRecord));
List<NutritionRecord> newNutritionRecords =
TestUtils.readRecords(
new ReadRecordsRequestUsingFilters.Builder<>(NutritionRecord.class)
.addDataOrigins(
new DataOrigin.Builder()
.setPackageName(context.getPackageName())
.build())
.build());
assertThat(newNutritionRecords.size() - oldNutritionRecords.size()).isEqualTo(1);
NutritionRecord newRecord = newNutritionRecords.get(newNutritionRecords.size() - 1);
assertThat(newNutritionRecords.get(newNutritionRecords.size() - 1).equals(testRecord))
.isTrue();
assertThat(newRecord.getUnsaturatedFat()).isEqualTo(testRecord.getUnsaturatedFat());
assertThat(newRecord.getPotassium()).isEqualTo(testRecord.getPotassium());
assertThat(newRecord.getThiamin()).isEqualTo(testRecord.getThiamin());
assertThat(newRecord.getMealType()).isEqualTo(testRecord.getMealType());
assertThat(newRecord.getTransFat()).isEqualTo(testRecord.getTransFat());
assertThat(newRecord.getManganese()).isEqualTo(testRecord.getManganese());
assertThat(newRecord.getEnergyFromFat()).isEqualTo(testRecord.getEnergyFromFat());
assertThat(newRecord.getCaffeine()).isEqualTo(testRecord.getCaffeine());
assertThat(newRecord.getDietaryFiber()).isEqualTo(testRecord.getDietaryFiber());
assertThat(newRecord.getSelenium()).isEqualTo(testRecord.getSelenium());
assertThat(newRecord.getVitaminB6()).isEqualTo(testRecord.getVitaminB6());
assertThat(newRecord.getProtein()).isEqualTo(testRecord.getProtein());
assertThat(newRecord.getChloride()).isEqualTo(testRecord.getChloride());
assertThat(newRecord.getCholesterol()).isEqualTo(testRecord.getCholesterol());
assertThat(newRecord.getCopper()).isEqualTo(testRecord.getCopper());
assertThat(newRecord.getIodine()).isEqualTo(testRecord.getIodine());
assertThat(newRecord.getVitaminB12()).isEqualTo(testRecord.getVitaminB12());
assertThat(newRecord.getZinc()).isEqualTo(testRecord.getZinc());
assertThat(newRecord.getRiboflavin()).isEqualTo(testRecord.getRiboflavin());
assertThat(newRecord.getEnergy()).isEqualTo(testRecord.getEnergy());
assertThat(newRecord.getMolybdenum()).isEqualTo(testRecord.getMolybdenum());
assertThat(newRecord.getPhosphorus()).isEqualTo(testRecord.getPhosphorus());
assertThat(newRecord.getChromium()).isEqualTo(testRecord.getChromium());
assertThat(newRecord.getTotalFat()).isEqualTo(testRecord.getTotalFat());
assertThat(newRecord.getCalcium()).isEqualTo(testRecord.getCalcium());
assertThat(newRecord.getVitaminC()).isEqualTo(testRecord.getVitaminC());
assertThat(newRecord.getVitaminE()).isEqualTo(testRecord.getVitaminE());
assertThat(newRecord.getBiotin()).isEqualTo(testRecord.getBiotin());
assertThat(newRecord.getVitaminD()).isEqualTo(testRecord.getVitaminD());
assertThat(newRecord.getNiacin()).isEqualTo(testRecord.getNiacin());
assertThat(newRecord.getMagnesium()).isEqualTo(testRecord.getMagnesium());
assertThat(newRecord.getTotalCarbohydrate()).isEqualTo(testRecord.getTotalCarbohydrate());
assertThat(newRecord.getVitaminK()).isEqualTo(testRecord.getVitaminK());
assertThat(newRecord.getPolyunsaturatedFat()).isEqualTo(testRecord.getPolyunsaturatedFat());
assertThat(newRecord.getSaturatedFat()).isEqualTo(testRecord.getSaturatedFat());
assertThat(newRecord.getSodium()).isEqualTo(testRecord.getSodium());
assertThat(newRecord.getFolate()).isEqualTo(testRecord.getFolate());
assertThat(newRecord.getMonounsaturatedFat()).isEqualTo(testRecord.getMonounsaturatedFat());
assertThat(newRecord.getPantothenicAcid()).isEqualTo(testRecord.getPantothenicAcid());
assertThat(newRecord.getMealName()).isEqualTo(testRecord.getMealName());
assertThat(newRecord.getIron()).isEqualTo(testRecord.getIron());
assertThat(newRecord.getVitaminA()).isEqualTo(testRecord.getVitaminA());
assertThat(newRecord.getFolicAcid()).isEqualTo(testRecord.getFolicAcid());
assertThat(newRecord.getSugar()).isEqualTo(testRecord.getSugar());
}
@Test
public void testReadNutritionRecordUsingFilters_dataFilter_incorrect()
throws InterruptedException {
TestUtils.insertRecords(Collections.singletonList(getCompleteNutritionRecord()));
List<NutritionRecord> newNutritionRecords =
TestUtils.readRecords(
new ReadRecordsRequestUsingFilters.Builder<>(NutritionRecord.class)
.addDataOrigins(
new DataOrigin.Builder().setPackageName("abc").build())
.build());
assertThat(newNutritionRecords.size()).isEqualTo(0);
}
@Test
public void testDeleteNutritionRecord_no_filters() throws InterruptedException {
String id = TestUtils.insertRecordAndGetId(getCompleteNutritionRecord());
TestUtils.verifyDeleteRecords(new DeleteUsingFiltersRequest.Builder().build());
TestUtils.assertRecordNotFound(id, NutritionRecord.class);
}
@Test
public void testDeleteNutritionRecord_time_filters() throws InterruptedException {
TimeInstantRangeFilter timeInstantRangeFilter =
new TimeInstantRangeFilter.Builder()
.setStartTime(Instant.now())
.setEndTime(Instant.now().plusMillis(1000))
.build();
String id = TestUtils.insertRecordAndGetId(getCompleteNutritionRecord());
TestUtils.verifyDeleteRecords(
new DeleteUsingFiltersRequest.Builder()
.addRecordType(NutritionRecord.class)
.setTimeRangeFilter(timeInstantRangeFilter)
.build());
TestUtils.assertRecordNotFound(id, NutritionRecord.class);
}
@Test
public void testDeleteNutritionRecord_recordId_filters() throws InterruptedException {
List<Record> records = List.of(getBaseNutritionRecord(), getCompleteNutritionRecord());
TestUtils.insertRecords(records);
for (Record record : records) {
TestUtils.verifyDeleteRecords(
new DeleteUsingFiltersRequest.Builder()
.addRecordType(record.getClass())
.build());
TestUtils.assertRecordNotFound(record.getMetadata().getId(), record.getClass());
}
}
@Test
public void testDeleteNutritionRecord_dataOrigin_filters() throws InterruptedException {
Context context = ApplicationProvider.getApplicationContext();
String id = TestUtils.insertRecordAndGetId(getCompleteNutritionRecord());
TestUtils.verifyDeleteRecords(
new DeleteUsingFiltersRequest.Builder()
.addDataOrigin(
new DataOrigin.Builder()
.setPackageName(context.getPackageName())
.build())
.build());
TestUtils.assertRecordNotFound(id, NutritionRecord.class);
}
@Test
public void testDeleteNutritionRecord_dataOrigin_filter_incorrect()
throws InterruptedException {
String id = TestUtils.insertRecordAndGetId(getCompleteNutritionRecord());
TestUtils.verifyDeleteRecords(
new DeleteUsingFiltersRequest.Builder()
.addDataOrigin(new DataOrigin.Builder().setPackageName("abc").build())
.build());
TestUtils.assertRecordFound(id, NutritionRecord.class);
}
@Test
public void testDeleteNutritionRecord_usingIds() throws InterruptedException {
List<Record> records = List.of(getBaseNutritionRecord(), getCompleteNutritionRecord());
List<Record> insertedRecord = TestUtils.insertRecords(records);
List<RecordIdFilter> recordIds = new ArrayList<>(records.size());
for (Record record : insertedRecord) {
recordIds.add(RecordIdFilter.fromId(record.getClass(), record.getMetadata().getId()));
}
TestUtils.verifyDeleteRecords(recordIds);
for (Record record : records) {
TestUtils.assertRecordNotFound(record.getMetadata().getId(), record.getClass());
}
}
@Test
public void testDeleteNutritionRecord_time_range() throws InterruptedException {
TimeInstantRangeFilter timeInstantRangeFilter =
new TimeInstantRangeFilter.Builder()
.setStartTime(Instant.now())
.setEndTime(Instant.now().plusMillis(1000))
.build();
String id = TestUtils.insertRecordAndGetId(getCompleteNutritionRecord());
TestUtils.verifyDeleteRecords(NutritionRecord.class, timeInstantRangeFilter);
TestUtils.assertRecordNotFound(id, NutritionRecord.class);
}
@Test
public void testAggregation_NutritionValuesTotal() throws Exception {
List<Record> records =
Arrays.asList(getCompleteNutritionRecord(), getCompleteNutritionRecord());
AggregateRecordsResponse<Mass> oldResponse =
TestUtils.getAggregateResponse(
new AggregateRecordsRequest.Builder<Mass>(
new TimeInstantRangeFilter.Builder()
.setStartTime(Instant.ofEpochMilli(0))
.setEndTime(Instant.now().plus(1, ChronoUnit.DAYS))
.build())
.addAggregationType(BIOTIN_TOTAL)
.addAggregationType(CAFFEINE_TOTAL)
.addAggregationType(CALCIUM_TOTAL)
.addAggregationType(CHLORIDE_TOTAL)
.addAggregationType(CHOLESTEROL_TOTAL)
.addAggregationType(CHROMIUM_TOTAL)
.addAggregationType(COPPER_TOTAL)
.addAggregationType(DIETARY_FIBER_TOTAL)
.addAggregationType(FOLATE_TOTAL)
.addAggregationType(FOLIC_ACID_TOTAL)
.addAggregationType(IODINE_TOTAL)
.addAggregationType(IRON_TOTAL)
.addAggregationType(MAGNESIUM_TOTAL)
.addAggregationType(MANGANESE_TOTAL)
.addAggregationType(MOLYBDENUM_TOTAL)
.addAggregationType(MONOUNSATURATED_FAT_TOTAL)
.addAggregationType(NIACIN_TOTAL)
.addAggregationType(PANTOTHENIC_ACID_TOTAL)
.addAggregationType(PHOSPHORUS_TOTAL)
.addAggregationType(POTASSIUM_TOTAL)
.addAggregationType(POLYUNSATURATED_FAT_TOTAL)
.addAggregationType(PROTEIN_TOTAL)
.addAggregationType(RIBOFLAVIN_TOTAL)
.addAggregationType(SATURATED_FAT_TOTAL)
.addAggregationType(SELENIUM_TOTAL)
.addAggregationType(SODIUM_TOTAL)
.addAggregationType(SUGAR_TOTAL)
.addAggregationType(THIAMIN_TOTAL)
.addAggregationType(TOTAL_CARBOHYDRATE_TOTAL)
.addAggregationType(TOTAL_FAT_TOTAL)
.addAggregationType(UNSATURATED_FAT_TOTAL)
.addAggregationType(VITAMIN_A_TOTAL)
.addAggregationType(VITAMIN_B12_TOTAL)
.addAggregationType(VITAMIN_B6_TOTAL)
.addAggregationType(VITAMIN_C_TOTAL)
.addAggregationType(VITAMIN_D_TOTAL)
.addAggregationType(VITAMIN_E_TOTAL)
.addAggregationType(VITAMIN_K_TOTAL)
.addAggregationType(ZINC_TOTAL)
.build(),
records);
List<Record> recordNew =
Arrays.asList(getCompleteNutritionRecord(), getCompleteNutritionRecord());
AggregateRecordsResponse<Mass> newResponse =
TestUtils.getAggregateResponse(
new AggregateRecordsRequest.Builder<Mass>(
new TimeInstantRangeFilter.Builder()
.setStartTime(Instant.ofEpochMilli(0))
.setEndTime(Instant.now().plus(1, ChronoUnit.DAYS))
.build())
.addAggregationType(BIOTIN_TOTAL)
.addAggregationType(CAFFEINE_TOTAL)
.addAggregationType(CALCIUM_TOTAL)
.addAggregationType(CHLORIDE_TOTAL)
.addAggregationType(CHOLESTEROL_TOTAL)
.addAggregationType(CHROMIUM_TOTAL)
.addAggregationType(COPPER_TOTAL)
.addAggregationType(DIETARY_FIBER_TOTAL)
.addAggregationType(FOLATE_TOTAL)
.addAggregationType(FOLIC_ACID_TOTAL)
.addAggregationType(IODINE_TOTAL)
.addAggregationType(IRON_TOTAL)
.addAggregationType(MAGNESIUM_TOTAL)
.addAggregationType(MANGANESE_TOTAL)
.addAggregationType(MOLYBDENUM_TOTAL)
.addAggregationType(MONOUNSATURATED_FAT_TOTAL)
.addAggregationType(NIACIN_TOTAL)
.addAggregationType(PANTOTHENIC_ACID_TOTAL)
.addAggregationType(PHOSPHORUS_TOTAL)
.addAggregationType(POTASSIUM_TOTAL)
.addAggregationType(POLYUNSATURATED_FAT_TOTAL)
.addAggregationType(PROTEIN_TOTAL)
.addAggregationType(RIBOFLAVIN_TOTAL)
.addAggregationType(SATURATED_FAT_TOTAL)
.addAggregationType(SELENIUM_TOTAL)
.addAggregationType(SODIUM_TOTAL)
.addAggregationType(SUGAR_TOTAL)
.addAggregationType(THIAMIN_TOTAL)
.addAggregationType(TOTAL_CARBOHYDRATE_TOTAL)
.addAggregationType(TOTAL_FAT_TOTAL)
.addAggregationType(UNSATURATED_FAT_TOTAL)
.addAggregationType(VITAMIN_A_TOTAL)
.addAggregationType(VITAMIN_B12_TOTAL)
.addAggregationType(VITAMIN_B6_TOTAL)
.addAggregationType(VITAMIN_C_TOTAL)
.addAggregationType(VITAMIN_D_TOTAL)
.addAggregationType(VITAMIN_E_TOTAL)
.addAggregationType(VITAMIN_K_TOTAL)
.addAggregationType(ZINC_TOTAL)
.build(),
recordNew);
for (AggregationType<Mass> type : mMassAggregateTypesList) {
Mass newTotal = newResponse.get(type);
Mass oldTotal = oldResponse.get(type);
assertThat(newTotal).isNotNull();
assertThat(oldTotal).isNotNull();
assertThat(newTotal.getInGrams() - oldTotal.getInGrams()).isEqualTo(0.2);
Set<DataOrigin> newDataOrigin = newResponse.getDataOrigins(type);
for (DataOrigin itr : newDataOrigin) {
assertThat(itr.getPackageName()).isEqualTo("android.healthconnect.cts");
}
Set<DataOrigin> oldDataOrigin = oldResponse.getDataOrigins(type);
for (DataOrigin itr : oldDataOrigin) {
assertThat(itr.getPackageName()).isEqualTo("android.healthconnect.cts");
}
}
}
@Test
public void testAggregation_NutritionEnergyValuesTotal() throws Exception {
List<Record> records = Arrays.asList(getCompleteNutritionRecord());
AggregateRecordsResponse<Energy> oldResponse =
TestUtils.getAggregateResponse(
new AggregateRecordsRequest.Builder<Energy>(
new TimeInstantRangeFilter.Builder()
.setStartTime(Instant.ofEpochMilli(0))
.setEndTime(Instant.now().plus(1, ChronoUnit.DAYS))
.build())
.addAggregationType(ENERGY_TOTAL)
.addAggregationType(ENERGY_FROM_FAT_TOTAL)
.build(),
records);
List<Record> newRecords = Arrays.asList(getCompleteNutritionRecord());
AggregateRecordsResponse<Energy> newResponse =
TestUtils.getAggregateResponse(
new AggregateRecordsRequest.Builder<Energy>(
new TimeInstantRangeFilter.Builder()
.setStartTime(Instant.ofEpochMilli(0))
.setEndTime(Instant.now().plus(1, ChronoUnit.DAYS))
.build())
.addAggregationType(ENERGY_TOTAL)
.addAggregationType(ENERGY_FROM_FAT_TOTAL)
.build(),
newRecords);
Energy newEnergy = newResponse.get(ENERGY_TOTAL);
Energy oldEnergy = oldResponse.get(ENERGY_TOTAL);
Energy newFatEnergy = newResponse.get(ENERGY_FROM_FAT_TOTAL);
Energy oldFatEnergy = oldResponse.get(ENERGY_FROM_FAT_TOTAL);
assertThat(newEnergy).isNotNull();
assertThat(oldEnergy).isNotNull();
assertThat(newFatEnergy).isNotNull();
assertThat(oldFatEnergy).isNotNull();
assertThat(newEnergy.getInCalories() - oldEnergy.getInCalories()).isEqualTo(0.1);
assertThat(newFatEnergy.getInCalories() - oldFatEnergy.getInCalories()).isEqualTo(0.1);
}
@Test
public void testZoneOffsets() {
final ZoneOffset defaultZoneOffset =
ZoneOffset.systemDefault().getRules().getOffset(Instant.now());
final ZoneOffset startZoneOffset = ZoneOffset.UTC;
final ZoneOffset endZoneOffset = ZoneOffset.MAX;
NutritionRecord.Builder builder =
new NutritionRecord.Builder(
new Metadata.Builder().build(),
Instant.now(),
Instant.now().plusMillis(1000));
assertThat(builder.setStartZoneOffset(startZoneOffset).build().getStartZoneOffset())
.isEqualTo(startZoneOffset);
assertThat(builder.setEndZoneOffset(endZoneOffset).build().getEndZoneOffset())
.isEqualTo(endZoneOffset);
assertThat(builder.clearStartZoneOffset().build().getStartZoneOffset())
.isEqualTo(defaultZoneOffset);
assertThat(builder.clearEndZoneOffset().build().getEndZoneOffset())
.isEqualTo(defaultZoneOffset);
}
@Test
public void testUpdateRecords_validInput_dataBaseUpdatedSuccessfully()
throws InterruptedException {
List<Record> insertedRecords =
TestUtils.insertRecords(
Arrays.asList(getCompleteNutritionRecord(), getCompleteNutritionRecord()));
// read inserted records and verify that the data is same as inserted.
readNutritionRecordUsingIds(insertedRecords);
// Generate a new set of records that will be used to perform the update operation.
List<Record> updateRecords =
Arrays.asList(getCompleteNutritionRecord(), getCompleteNutritionRecord());
// Modify the uid of the updateRecords to the uuid that was present in the insert records.
for (int itr = 0; itr < updateRecords.size(); itr++) {
updateRecords.set(
itr,
getNutritionRecord_update(
updateRecords.get(itr),
insertedRecords.get(itr).getMetadata().getId(),
insertedRecords.get(itr).getMetadata().getClientRecordId()));
}
TestUtils.updateRecords(updateRecords);
// assert the inserted data has been modified by reading the data.
readNutritionRecordUsingIds(updateRecords);
}
@Test
public void testUpdateRecords_invalidInputRecords_noChangeInDataBase()
throws InterruptedException {
List<Record> insertedRecords =
TestUtils.insertRecords(
Arrays.asList(getCompleteNutritionRecord(), getCompleteNutritionRecord()));
// read inserted records and verify that the data is same as inserted.
readNutritionRecordUsingIds(insertedRecords);
// Generate a second set of records that will be used to perform the update operation.
List<Record> updateRecords =
Arrays.asList(getCompleteNutritionRecord(), getCompleteNutritionRecord());
// Modify the Uid of the updateRecords to the UUID that was present in the insert records,
// leaving out alternate records so that they have a new UUID which is not present in the
// dataBase.
for (int itr = 0; itr < updateRecords.size(); itr++) {
updateRecords.set(
itr,
getNutritionRecord_update(
updateRecords.get(itr),
itr % 2 == 0
? insertedRecords.get(itr).getMetadata().getId()
: UUID.randomUUID().toString(),
itr % 2 == 0
? insertedRecords.get(itr).getMetadata().getId()
: UUID.randomUUID().toString()));
}
try {
TestUtils.updateRecords(updateRecords);
Assert.fail("Expected to fail due to invalid records ids.");
} catch (HealthConnectException exception) {
assertThat(exception.getErrorCode())
.isEqualTo(HealthConnectException.ERROR_INVALID_ARGUMENT);
}
// assert the inserted data has not been modified by reading the data.
readNutritionRecordUsingIds(insertedRecords);
}
@Test
public void testUpdateRecords_recordWithInvalidPackageName_noChangeInDataBase()
throws InterruptedException {
List<Record> insertedRecords =
TestUtils.insertRecords(
Arrays.asList(getCompleteNutritionRecord(), getCompleteNutritionRecord()));
// read inserted records and verify that the data is same as inserted.
readNutritionRecordUsingIds(insertedRecords);
// Generate a second set of records that will be used to perform the update operation.
List<Record> updateRecords =
Arrays.asList(getCompleteNutritionRecord(), getCompleteNutritionRecord());
// Modify the Uuid of the updateRecords to the uuid that was present in the insert records.
for (int itr = 0; itr < updateRecords.size(); itr++) {
updateRecords.set(
itr,
getNutritionRecord_update(
updateRecords.get(itr),
insertedRecords.get(itr).getMetadata().getId(),
insertedRecords.get(itr).getMetadata().getClientRecordId()));
// adding an entry with invalid packageName.
updateRecords.set(itr, getCompleteNutritionRecord());
}
try {
TestUtils.updateRecords(updateRecords);
Assert.fail("Expected to fail due to invalid package.");
} catch (Exception exception) {
// verify that the testcase failed due to invalid argument exception.
assertThat(exception).isNotNull();
}
// assert the inserted data has not been modified by reading the data.
readNutritionRecordUsingIds(insertedRecords);
}
@Test
public void testInsertAndDeleteRecord_changelogs() throws InterruptedException {
Context context = ApplicationProvider.getApplicationContext();
ChangeLogTokenResponse tokenResponse =
TestUtils.getChangeLogToken(
new ChangeLogTokenRequest.Builder()
.addDataOriginFilter(
new DataOrigin.Builder()
.setPackageName(context.getPackageName())
.build())
.addRecordType(NutritionRecord.class)
.build());
ChangeLogsRequest changeLogsRequest =
new ChangeLogsRequest.Builder(tokenResponse.getToken()).build();
ChangeLogsResponse response = TestUtils.getChangeLogs(changeLogsRequest);
assertThat(response.getUpsertedRecords().size()).isEqualTo(0);
assertThat(response.getDeletedLogs().size()).isEqualTo(0);
List<Record> testRecord = Collections.singletonList(getCompleteNutritionRecord());
TestUtils.insertRecords(testRecord);
response = TestUtils.getChangeLogs(changeLogsRequest);
assertThat(response.getUpsertedRecords().size()).isEqualTo(1);
assertThat(
response.getUpsertedRecords().stream()
.map(Record::getMetadata)
.map(Metadata::getId)
.toList())
.containsExactlyElementsIn(
testRecord.stream().map(Record::getMetadata).map(Metadata::getId).toList());
assertThat(response.getDeletedLogs().size()).isEqualTo(0);
TestUtils.verifyDeleteRecords(
new DeleteUsingFiltersRequest.Builder()
.addRecordType(NutritionRecord.class)
.build());
response = TestUtils.getChangeLogs(changeLogsRequest);
assertThat(response.getDeletedLogs()).isEmpty();
}
private void readNutritionRecordUsingClientId(List<Record> insertedRecord)
throws InterruptedException {
ReadRecordsRequestUsingIds.Builder<NutritionRecord> request =
new ReadRecordsRequestUsingIds.Builder<>(NutritionRecord.class);
for (Record record : insertedRecord) {
request.addClientRecordId(record.getMetadata().getClientRecordId());
}
List<NutritionRecord> result = TestUtils.readRecords(request.build());
assertThat(result.size()).isEqualTo(insertedRecord.size());
assertThat(result).containsExactlyElementsIn(insertedRecord);
}
private void readNutritionRecordUsingIds(List<Record> recordList) throws InterruptedException {
ReadRecordsRequestUsingIds.Builder<NutritionRecord> request =
new ReadRecordsRequestUsingIds.Builder<>(NutritionRecord.class);
for (Record record : recordList) {
request.addId(record.getMetadata().getId());
}
List<NutritionRecord> result = TestUtils.readRecords(request.build());
assertThat(result).hasSize(recordList.size());
assertThat(result).containsExactlyElementsIn(recordList);
}
NutritionRecord getNutritionRecord_update(Record record, String id, String clientRecordId) {
Metadata metadata = record.getMetadata();
Metadata metadataWithId =
new Metadata.Builder()
.setId(id)
.setClientRecordId(clientRecordId)
.setClientRecordVersion(metadata.getClientRecordVersion())
.setDataOrigin(metadata.getDataOrigin())
.setDevice(metadata.getDevice())
.setLastModifiedTime(metadata.getLastModifiedTime())
.build();
return new NutritionRecord.Builder(
metadataWithId, Instant.now(), Instant.now().plusMillis(2000))
.setUnsaturatedFat(Mass.fromGrams(0.1))
.setPotassium(Mass.fromGrams(0.1))
.setThiamin(Mass.fromGrams(0.1))
.setMealType(1)
.setTransFat(Mass.fromGrams(0.1))
.setManganese(Mass.fromGrams(0.1))
.setEnergyFromFat(Energy.fromCalories(0.1))
.setCaffeine(Mass.fromGrams(0.1))
.setDietaryFiber(Mass.fromGrams(0.1))
.setSelenium(Mass.fromGrams(0.1))
.setVitaminB6(Mass.fromGrams(0.1))
.setProtein(Mass.fromGrams(0.1))
.setChloride(Mass.fromGrams(0.1))
.setCholesterol(Mass.fromGrams(0.1))
.setCopper(Mass.fromGrams(0.1))
.setIodine(Mass.fromGrams(0.1))
.setVitaminB12(Mass.fromGrams(0.1))
.setZinc(Mass.fromGrams(0.1))
.setRiboflavin(Mass.fromGrams(0.1))
.setEnergy(Energy.fromCalories(0.1))
.setMolybdenum(Mass.fromGrams(0.1))
.setPhosphorus(Mass.fromGrams(0.1))
.setChromium(Mass.fromGrams(0.1))
.setTotalFat(Mass.fromGrams(0.1))
.setCalcium(Mass.fromGrams(0.1))
.setVitaminC(Mass.fromGrams(0.1))
.setVitaminE(Mass.fromGrams(0.1))
.setBiotin(Mass.fromGrams(0.1))
.setVitaminD(Mass.fromGrams(0.1))
.setNiacin(Mass.fromGrams(0.1))
.setMagnesium(Mass.fromGrams(0.02))
.setTotalCarbohydrate(Mass.fromGrams(0.1))
.setVitaminK(Mass.fromGrams(0.1))
.setPolyunsaturatedFat(Mass.fromGrams(0.1))
.setSaturatedFat(Mass.fromGrams(0.1))
.setSodium(Mass.fromGrams(0.1))
.setFolate(Mass.fromGrams(0.1))
.setMonounsaturatedFat(Mass.fromGrams(0.1))
.setPantothenicAcid(Mass.fromGrams(0.1))
.setMealName("Brunch")
.setIron(Mass.fromGrams(0.1))
.setVitaminA(Mass.fromGrams(0.1))
.setFolicAcid(Mass.fromGrams(0.1))
.setSugar(Mass.fromGrams(0.2))
.setStartZoneOffset(ZoneOffset.systemDefault().getRules().getOffset(Instant.now()))
.setEndZoneOffset(ZoneOffset.systemDefault().getRules().getOffset(Instant.now()))
.build();
}
static NutritionRecord getBaseNutritionRecord() {
return new NutritionRecord.Builder(
new Metadata.Builder().build(),
Instant.now(),
Instant.now().plusMillis(1000))
.build();
}
static NutritionRecord getCompleteNutritionRecord() {
Device device =
new Device.Builder()
.setManufacturer("google")
.setModel("Pixel4a")
.setType(2)
.build();
DataOrigin dataOrigin =
new DataOrigin.Builder().setPackageName("android.healthconnect.cts").build();
Metadata.Builder testMetadataBuilder = new Metadata.Builder();
testMetadataBuilder.setDevice(device).setDataOrigin(dataOrigin);
testMetadataBuilder.setClientRecordId("NTR" + Math.random());
return new NutritionRecord.Builder(
testMetadataBuilder.build(), Instant.now(), Instant.now().plusMillis(1000))
.setUnsaturatedFat(Mass.fromGrams(0.1))
.setPotassium(Mass.fromGrams(0.1))
.setThiamin(Mass.fromGrams(0.1))
.setMealType(1)
.setTransFat(Mass.fromGrams(0.1))
.setManganese(Mass.fromGrams(0.1))
.setEnergyFromFat(Energy.fromCalories(0.1))
.setCaffeine(Mass.fromGrams(0.1))
.setDietaryFiber(Mass.fromGrams(0.1))
.setSelenium(Mass.fromGrams(0.1))
.setVitaminB6(Mass.fromGrams(0.1))
.setProtein(Mass.fromGrams(0.1))
.setChloride(Mass.fromGrams(0.1))
.setCholesterol(Mass.fromGrams(0.1))
.setCopper(Mass.fromGrams(0.1))
.setIodine(Mass.fromGrams(0.1))
.setVitaminB12(Mass.fromGrams(0.1))
.setZinc(Mass.fromGrams(0.1))
.setRiboflavin(Mass.fromGrams(0.1))
.setEnergy(Energy.fromCalories(0.1))
.setMolybdenum(Mass.fromGrams(0.1))
.setPhosphorus(Mass.fromGrams(0.1))
.setChromium(Mass.fromGrams(0.1))
.setTotalFat(Mass.fromGrams(0.1))
.setCalcium(Mass.fromGrams(0.1))
.setVitaminC(Mass.fromGrams(0.1))
.setVitaminE(Mass.fromGrams(0.1))
.setBiotin(Mass.fromGrams(0.1))
.setVitaminD(Mass.fromGrams(0.1))
.setNiacin(Mass.fromGrams(0.1))
.setMagnesium(Mass.fromGrams(0.1))
.setTotalCarbohydrate(Mass.fromGrams(0.1))
.setVitaminK(Mass.fromGrams(0.1))
.setPolyunsaturatedFat(Mass.fromGrams(0.1))
.setSaturatedFat(Mass.fromGrams(0.1))
.setSodium(Mass.fromGrams(0.1))
.setFolate(Mass.fromGrams(0.1))
.setMonounsaturatedFat(Mass.fromGrams(0.1))
.setPantothenicAcid(Mass.fromGrams(0.1))
.setMealName("Brunch")
.setIron(Mass.fromGrams(0.1))
.setVitaminA(Mass.fromGrams(0.1))
.setFolicAcid(Mass.fromGrams(0.1))
.setSugar(Mass.fromGrams(0.1))
.setStartZoneOffset(ZoneOffset.systemDefault().getRules().getOffset(Instant.now()))
.setEndZoneOffset(ZoneOffset.systemDefault().getRules().getOffset(Instant.now()))
.build();
}
}