Introduce test infrastructure for datatype helper unittests

- Create HealthFitnessTestRule for shared setup and teardown
- Create TransactionTestUtils for common logic to be used in tests
- Add a sample test case to show case usage of the new infra

Test: aTest HealthFitnessUnitTests:RecordHelperTest
Bug: 296850857
Change-Id: Id9ef4368a37859c90df3b85a652cdc90fb03d2a9
diff --git a/tests/unittests/src/com/android/server/healthconnect/TestUtils.java b/tests/unittests/src/com/android/server/healthconnect/TestUtils.java
index 3f63cc1..5084836 100644
--- a/tests/unittests/src/com/android/server/healthconnect/TestUtils.java
+++ b/tests/unittests/src/com/android/server/healthconnect/TestUtils.java
@@ -16,12 +16,15 @@
 
 package com.android.server.healthconnect;
 
+import android.os.UserHandle;
+
 import java.time.Instant;
 import java.time.temporal.ChronoUnit;
 import java.util.concurrent.TimeoutException;
 import java.util.function.Predicate;
 
 public final class TestUtils {
+    public static final UserHandle TEST_USER = UserHandle.of(UserHandle.myUserId());
 
     public static void waitForTaskToFinishSuccessfully(Runnable task) throws TimeoutException {
         Instant startTime = Instant.now();
diff --git a/tests/unittests/src/com/android/server/healthconnect/storage/datatypehelpers/HealthConnectDatabaseTestRule.java b/tests/unittests/src/com/android/server/healthconnect/storage/datatypehelpers/HealthConnectDatabaseTestRule.java
new file mode 100644
index 0000000..471a869
--- /dev/null
+++ b/tests/unittests/src/com/android/server/healthconnect/storage/datatypehelpers/HealthConnectDatabaseTestRule.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2023 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 com.android.server.healthconnect.storage.datatypehelpers;
+
+import static com.android.server.healthconnect.TestUtils.TEST_USER;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.os.Environment;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.dx.mockito.inline.extended.ExtendedMockito;
+import com.android.server.healthconnect.HealthConnectUserContext;
+
+import org.junit.rules.ExternalResource;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
+
+import java.io.File;
+import java.util.Arrays;
+
+/** A test rule that deals with ground work of setting up a mock Health Connect database. */
+public class HealthConnectDatabaseTestRule extends ExternalResource {
+    private MockitoSession mStaticMockSession;
+    private File mMockDataDirectory;
+    private HealthConnectUserContext mContext;
+
+    @Override
+    public void before() {
+        mStaticMockSession =
+                ExtendedMockito.mockitoSession()
+                        .mockStatic(Environment.class)
+                        .strictness(Strictness.LENIENT)
+                        .startMocking();
+
+        mContext =
+                new HealthConnectUserContext(
+                        InstrumentationRegistry.getInstrumentation().getContext(), TEST_USER);
+        mMockDataDirectory = mContext.getDir("mock_data", Context.MODE_PRIVATE);
+        when(Environment.getDataDirectory()).thenReturn(mMockDataDirectory);
+    }
+
+    @Override
+    public void after() {
+        deleteDir(mMockDataDirectory);
+        mStaticMockSession.finishMocking();
+    }
+
+    public HealthConnectUserContext getUserContext() {
+        return mContext;
+    }
+
+    private static void deleteDir(File dir) {
+        File[] files = dir.listFiles();
+        if (files != null) {
+            for (var file : files) {
+                if (file.isDirectory()) {
+                    deleteDir(file);
+                } else {
+                    assertThat(file.delete()).isTrue();
+                }
+            }
+        }
+        assertWithMessage(
+                        "Directory "
+                                + dir.getAbsolutePath()
+                                + " is not empty, Files present = "
+                                + Arrays.toString(dir.list()))
+                .that(dir.delete())
+                .isTrue();
+    }
+}
diff --git a/tests/unittests/src/com/android/server/healthconnect/storage/datatypehelpers/RecordHelperTest.java b/tests/unittests/src/com/android/server/healthconnect/storage/datatypehelpers/RecordHelperTest.java
new file mode 100644
index 0000000..2240e18
--- /dev/null
+++ b/tests/unittests/src/com/android/server/healthconnect/storage/datatypehelpers/RecordHelperTest.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2023 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 com.android.server.healthconnect.storage.datatypehelpers;
+
+import static com.android.server.healthconnect.TestUtils.TEST_USER;
+import static com.android.server.healthconnect.storage.datatypehelpers.StepsRecordHelper.STEPS_TABLE_NAME;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.database.Cursor;
+import android.health.connect.internal.datatypes.RecordInternal;
+import android.health.connect.internal.datatypes.StepsRecordInternal;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.healthconnect.HealthConnectUserContext;
+import com.android.server.healthconnect.storage.TransactionManager;
+import com.android.server.healthconnect.storage.request.ReadTableRequest;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+import java.util.UUID;
+
+@RunWith(AndroidJUnit4.class)
+public class RecordHelperTest {
+    private static final String TEST_PACKAGE_NAME = "package.name";
+
+    @Rule public final HealthConnectDatabaseTestRule testRule = new HealthConnectDatabaseTestRule();
+    private TransactionTestUtils mTransactionTestUtils;
+
+    private TransactionManager mTransactionManager;
+
+    @Before
+    public void setup() throws Exception {
+        HealthConnectUserContext context = testRule.getUserContext();
+        mTransactionManager = TransactionManager.getInstance(context);
+        mTransactionTestUtils = new TransactionTestUtils(context, TEST_USER);
+        mTransactionTestUtils.insertApp(TEST_PACKAGE_NAME);
+    }
+
+    @Test
+    public void getInternalRecords_insertThenRead_recordReturned() {
+        RecordHelper<?> helper = new StepsRecordHelper();
+        String uid = mTransactionTestUtils.insertStepsRecord(4000, 5000, 100);
+        ReadTableRequest request = new ReadTableRequest(STEPS_TABLE_NAME);
+        try (Cursor cursor = mTransactionManager.read(request)) {
+            List<RecordInternal<?>> records = helper.getInternalRecords(cursor, 1);
+            assertThat(records).hasSize(1);
+
+            StepsRecordInternal record = (StepsRecordInternal) records.get(0);
+            assertThat(record.getUuid()).isEqualTo(UUID.fromString(uid));
+            assertThat(record.getStartTimeInMillis()).isEqualTo(4000);
+            assertThat(record.getEndTimeInMillis()).isEqualTo(5000);
+            assertThat(record.getCount()).isEqualTo(100);
+        }
+    }
+}
diff --git a/tests/unittests/src/com/android/server/healthconnect/storage/datatypehelpers/TransactionTestUtils.java b/tests/unittests/src/com/android/server/healthconnect/storage/datatypehelpers/TransactionTestUtils.java
new file mode 100644
index 0000000..6bacaa2
--- /dev/null
+++ b/tests/unittests/src/com/android/server/healthconnect/storage/datatypehelpers/TransactionTestUtils.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2023 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 com.android.server.healthconnect.storage.datatypehelpers;
+
+import static com.android.server.healthconnect.storage.datatypehelpers.AppInfoHelper.PACKAGE_COLUMN_NAME;
+import static com.android.server.healthconnect.storage.datatypehelpers.AppInfoHelper.UNIQUE_COLUMN_INFO;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.health.connect.internal.datatypes.StepsRecordInternal;
+import android.os.UserHandle;
+
+import com.android.server.healthconnect.HealthConnectUserContext;
+import com.android.server.healthconnect.storage.TransactionManager;
+import com.android.server.healthconnect.storage.request.UpsertTableRequest;
+import com.android.server.healthconnect.storage.request.UpsertTransactionRequest;
+
+import com.google.common.collect.ImmutableList;
+
+import java.util.List;
+
+/** Util class provides shared functionality for db transaction testing. */
+public final class TransactionTestUtils {
+    private final TransactionManager mTransactionManager;
+    private final Context mContext;
+
+    public TransactionTestUtils(Context context, UserHandle userHandle) {
+        mContext = context;
+        mTransactionManager =
+                TransactionManager.getInstance(new HealthConnectUserContext(context, userHandle));
+    }
+
+    public void insertApp(String packageName) {
+        ContentValues contentValues = new ContentValues();
+        contentValues.put(PACKAGE_COLUMN_NAME, packageName);
+        mTransactionManager.insert(
+                new UpsertTableRequest(
+                        AppInfoHelper.TABLE_NAME, contentValues, UNIQUE_COLUMN_INFO));
+        assertThat(AppInfoHelper.getInstance().getAppInfoId(packageName)).isEqualTo(1);
+    }
+
+    public String insertStepsRecord(long startTimeMillis, long endTimeMillis, int stepsCount) {
+        StepsRecordInternal recordInternal =
+                (StepsRecordInternal)
+                        new StepsRecordInternal()
+                                .setCount(stepsCount)
+                                .setStartTime(startTimeMillis)
+                                .setEndTime(endTimeMillis);
+
+        List<String> uids =
+                mTransactionManager.insertAll(
+                        new UpsertTransactionRequest(
+                                "package.name",
+                                ImmutableList.of(recordInternal),
+                                mContext,
+                                true,
+                                false));
+        return uids.get(0);
+    }
+}