• 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 com.android.health.connect.backuprestore;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 
21 import static org.mockito.ArgumentMatchers.any;
22 import static org.mockito.Mockito.never;
23 import static org.mockito.Mockito.spy;
24 import static org.mockito.Mockito.times;
25 import static org.mockito.Mockito.verify;
26 
27 import android.app.backup.BackupDataInput;
28 import android.app.backup.BackupDataOutput;
29 import android.app.backup.FullBackupDataOutput;
30 import android.content.Context;
31 import android.health.connect.HealthConnectManager;
32 import android.health.connect.restore.StageRemoteDataException;
33 import android.os.OutcomeReceiver;
34 import android.os.ParcelFileDescriptor;
35 import android.util.ArrayMap;
36 
37 import androidx.test.platform.app.InstrumentationRegistry;
38 import androidx.test.runner.AndroidJUnit4;
39 
40 import org.junit.Before;
41 import org.junit.Test;
42 import org.junit.runner.RunWith;
43 import org.mockito.ArgumentCaptor;
44 import org.mockito.Captor;
45 import org.mockito.Mock;
46 import org.mockito.MockitoAnnotations;
47 
48 import java.io.File;
49 import java.io.FileWriter;
50 import java.io.IOException;
51 import java.util.ArrayList;
52 import java.util.List;
53 import java.util.Map;
54 import java.util.Set;
55 import java.util.stream.Collectors;
56 import java.util.stream.Stream;
57 
58 /** Unit test class for {@link HealthConnectBackupAgent} */
59 @RunWith(AndroidJUnit4.class)
60 public class HealthConnectBackupAgentTest {
61     private TestableHealthConnectBackupAgent mHealthConnectBackupAgent;
62     @Mock private HealthConnectManager mHealthConnectManager;
63 
64     @Captor ArgumentCaptor<Map<String, ParcelFileDescriptor>> mPfdsByFileNameCaptor;
65     @Captor ArgumentCaptor<OutcomeReceiver<Void, StageRemoteDataException>> mStagingCallbackCaptor;
66     @Mock private Context mContext;
67     @Mock private FullBackupDataOutput mFullBackupDataOutput;
68     @Mock private ParcelFileDescriptor mOldState;
69     @Mock private ParcelFileDescriptor mNewState;
70     @Mock private BackupDataInput mBackupDataInput;
71     @Mock private BackupDataOutput mBackupDataOutput;
72     private File mBackupDataDirectory;
73 
74     @Before
setUp()75     public void setUp() {
76         MockitoAnnotations.initMocks(this);
77         mContext = InstrumentationRegistry.getInstrumentation().getContext();
78         mBackupDataDirectory = mContext.getDir("mock_data", Context.MODE_PRIVATE);
79 
80         mHealthConnectBackupAgent = new TestableHealthConnectBackupAgent();
81         mHealthConnectBackupAgent.onCreate();
82     }
83 
84     @Test
testOnRestoreFinished_stagingAll_sendsAllDataForStaging()85     public void testOnRestoreFinished_stagingAll_sendsAllDataForStaging() throws IOException {
86         createAndGetNonEmptyFile(mHealthConnectBackupAgent.getBackupDataDir(), "testFile1");
87         createAndGetNonEmptyFile(mHealthConnectBackupAgent.getBackupDataDir(), "testFile2");
88         Set<String> expectedStagedFileNames =
89                 Stream.of(mHealthConnectBackupAgent.getBackupDataDir().listFiles())
90                         .filter(file -> !file.isDirectory())
91                         .map(File::getName)
92                         .collect(Collectors.toSet());
93         assertThat(expectedStagedFileNames.size()).isEqualTo(2);
94 
95         Set<String> capturedStagedFileNames;
96 
97         mHealthConnectBackupAgent.onRestoreFinished();
98         verify(mHealthConnectManager, times(1))
99                 .stageAllHealthConnectRemoteData(mPfdsByFileNameCaptor.capture(), any(), any());
100         capturedStagedFileNames = mPfdsByFileNameCaptor.getValue().keySet();
101 
102         assertThat(capturedStagedFileNames.equals(expectedStagedFileNames)).isEqualTo(true);
103     }
104 
105     @Test
testOnRestoreFinished_afterStagingSuccess_deletesLocalData()106     public void testOnRestoreFinished_afterStagingSuccess_deletesLocalData() throws IOException {
107         HealthConnectBackupAgent spyForVerification = spy(mHealthConnectBackupAgent);
108         createAndGetNonEmptyFile(mHealthConnectBackupAgent.getBackupDataDir(), "testFile1");
109         Set<String> expectedStagedFileNames =
110                 Stream.of(mHealthConnectBackupAgent.getBackupDataDir().listFiles())
111                         .filter(file -> !file.isDirectory())
112                         .map(File::getName)
113                         .collect(Collectors.toSet());
114         assertThat(expectedStagedFileNames.size()).isEqualTo(1);
115 
116         mHealthConnectBackupAgent.onRestoreFinished();
117         verify(mHealthConnectManager, times(1))
118                 .stageAllHealthConnectRemoteData(any(), any(), mStagingCallbackCaptor.capture());
119         mStagingCallbackCaptor.getValue().onResult(null);
120 
121         verify(spyForVerification, times(1)).deleteBackupFiles();
122     }
123 
124     @Test
testOnRestoreFinished_afterStagingFailure_deletesLocalData()125     public void testOnRestoreFinished_afterStagingFailure_deletesLocalData() throws IOException {
126         HealthConnectBackupAgent spyForVerification = spy(mHealthConnectBackupAgent);
127         createAndGetNonEmptyFile(mHealthConnectBackupAgent.getBackupDataDir(), "testFile1");
128         Set<String> expectedStagedFileNames =
129                 Stream.of(mHealthConnectBackupAgent.getBackupDataDir().listFiles())
130                         .filter(file -> !file.isDirectory())
131                         .map(File::getName)
132                         .collect(Collectors.toSet());
133         assertThat(expectedStagedFileNames.size()).isEqualTo(1);
134 
135         mHealthConnectBackupAgent.onRestoreFinished();
136         verify(mHealthConnectManager, times(1))
137                 .stageAllHealthConnectRemoteData(any(), any(), mStagingCallbackCaptor.capture());
138         mStagingCallbackCaptor.getValue().onError(new StageRemoteDataException(new ArrayMap<>()));
139 
140         verify(spyForVerification, times(1)).deleteBackupFiles();
141     }
142 
143     @Test
testOnFullBackup_backsUpEverything()144     public void testOnFullBackup_backsUpEverything() throws Exception {
145         createAndGetNonEmptyFile(mBackupDataDirectory, "testFile1");
146         createAndGetNonEmptyFile(mBackupDataDirectory, "testFile2");
147 
148         mHealthConnectBackupAgent.onFullBackup(mFullBackupDataOutput);
149 
150         assertThat(mHealthConnectBackupAgent.mBackedUpFiles).hasSize(2);
151     }
152 
153     @Test
testOnBackup_doesNotBackUpAnything()154     public void testOnBackup_doesNotBackUpAnything() throws Exception {
155         createAndGetNonEmptyFile(mBackupDataDirectory, "testFile1");
156         createAndGetNonEmptyFile(mBackupDataDirectory, "testFile2");
157 
158         mHealthConnectBackupAgent.onBackup(mOldState, mBackupDataOutput, mNewState);
159 
160         assertThat(mHealthConnectBackupAgent.mBackedUpFiles).hasSize(0);
161     }
162 
163     @Test
testOnRestore_doesNotStageAnything()164     public void testOnRestore_doesNotStageAnything() throws IOException {
165         createAndGetNonEmptyFile(mHealthConnectBackupAgent.getBackupDataDir(), "testFile1");
166         createAndGetNonEmptyFile(mHealthConnectBackupAgent.getBackupDataDir(), "testFile2");
167         Set<String> expectedStagedFileNames =
168                 Stream.of(mHealthConnectBackupAgent.getBackupDataDir().listFiles())
169                         .filter(file -> !file.isDirectory())
170                         .map(File::getName)
171                         .collect(Collectors.toSet());
172         assertThat(expectedStagedFileNames.size()).isEqualTo(2);
173 
174         mHealthConnectBackupAgent.onRestore(mBackupDataInput, 1, mNewState);
175         verify(mHealthConnectManager, never())
176                 .stageAllHealthConnectRemoteData(mPfdsByFileNameCaptor.capture(), any(), any());
177     }
178 
createAndGetNonEmptyFile(File dir, String fileName)179     private static void createAndGetNonEmptyFile(File dir, String fileName) throws IOException {
180         File file = new File(dir, fileName);
181         FileWriter fileWriter = new FileWriter(file);
182         fileWriter.write("Contents of file " + fileName);
183         fileWriter.close();
184     }
185 
186     /** A testable {@link HealthConnectBackupAgent} */
187     public class TestableHealthConnectBackupAgent extends HealthConnectBackupAgent {
188         List<File> mBackedUpFiles = new ArrayList<>();
189 
190         @Override
getBaseContext()191         public Context getBaseContext() {
192             return mContext;
193         }
194 
195         @Override
getBackupDataDir()196         File getBackupDataDir() {
197             return mBackupDataDirectory;
198         }
199 
200         @Override
getHealthConnectService()201         HealthConnectManager getHealthConnectService() {
202             return mHealthConnectManager;
203         }
204 
205         @Override
backupFile(File file, FullBackupDataOutput data)206         void backupFile(File file, FullBackupDataOutput data) {
207             mBackedUpFiles.add(file);
208         }
209     }
210 }
211