Skip to content

Commit 8333f52

Browse files
annzimmerrlazo
andauthored
Set up functional/integration tests. (firebase#2324)
* Set up functional/integration tests. * Suggested Changes * remove manifest - not needed for functional tests. * More local mode functional tests. * updating SharedPreferencesUtil usage in functional tests. * update teardown * update teardown * reviewer comments and minor refactor to use getDownloadedModelList. Co-authored-by: Rodrigo Lazo Paz <rlazo@google.com>
1 parent d254e55 commit 8333f52

File tree

3 files changed

+264
-4
lines changed

3 files changed

+264
-4
lines changed

firebase-ml-modeldownloader/firebase-ml-modeldownloader.gradle

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ plugins {
1818
}
1919

2020
firebaseLibrary {
21-
testLab.enabled = false
21+
testLab.enabled = true
2222
publishJavadoc = false
2323
}
2424

@@ -89,4 +89,17 @@ dependencies {
8989
//Android compatible version of Apache httpclient.
9090
testImplementation 'org.apache.httpcomponents:httpclient-android:4.3.5.1'
9191
testImplementation 'org.mockito:mockito-core:3.3.3'
92-
}
92+
93+
androidTestImplementation 'junit:junit:4.13.1'
94+
androidTestImplementation "androidx.annotation:annotation:1.1.0"
95+
androidTestImplementation 'androidx.test:runner:1.2.0'
96+
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
97+
androidTestImplementation "com.google.truth:truth:$googleTruthVersion"
98+
}
99+
100+
// ==========================================================================
101+
// Copy from here down if you want to use the google-services plugin in your
102+
// androidTest integration tests.
103+
// ==========================================================================
104+
ext.packageName = "com.google.firebase.ml.modeldownloader"
105+
apply from: '../gradle/googleServices.gradle'
Lines changed: 248 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,248 @@
1+
// Copyright 2021 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
package com.google.firebase.ml.modeldownloader;
15+
16+
import static org.junit.Assert.assertEquals;
17+
import static org.junit.Assert.assertNotEquals;
18+
import static org.junit.Assert.assertNotNull;
19+
import static org.junit.Assert.assertNull;
20+
import static org.junit.Assert.assertTrue;
21+
22+
import android.os.ParcelFileDescriptor;
23+
import androidx.test.core.app.ApplicationProvider;
24+
import androidx.test.runner.AndroidJUnit4;
25+
import com.google.android.gms.tasks.Task;
26+
import com.google.android.gms.tasks.Tasks;
27+
import com.google.firebase.FirebaseApp;
28+
import com.google.firebase.ml.modeldownloader.internal.ModelFileManager;
29+
import com.google.firebase.ml.modeldownloader.internal.SharedPreferencesUtil;
30+
import java.io.File;
31+
import java.util.Set;
32+
import java.util.concurrent.ExecutionException;
33+
import org.junit.After;
34+
import org.junit.Before;
35+
import org.junit.Test;
36+
import org.junit.runner.RunWith;
37+
38+
/**
39+
* Integration tests for Get Model, download type LOCAL_MODEL. Only use getLocalModel* models for
40+
* this test.
41+
*/
42+
@RunWith(AndroidJUnit4.class)
43+
public class TestGetModelLocal {
44+
private FirebaseModelDownloader firebaseModelDownloader;
45+
private static final String MODEL_NAME_LOCAL = "getLocalModel";
46+
private static final String MODEL_NAME_LOCAL_2 = "getLocalModel2";
47+
private static final String MODEL_HASH = "origHash324";
48+
private final CustomModel SETUP_LOADED_LOCAL_MODEL =
49+
new CustomModel(MODEL_NAME_LOCAL, MODEL_HASH, 100, 0);
50+
51+
private FirebaseApp app;
52+
private File firstDeviceModelFile;
53+
private File firstLoadTempModelFile;
54+
private SharedPreferencesUtil sharedPreferencesUtil;
55+
56+
@Before
57+
public void before() {
58+
app = FirebaseApp.initializeApp(ApplicationProvider.getApplicationContext());
59+
firebaseModelDownloader = FirebaseModelDownloader.getInstance(app);
60+
61+
sharedPreferencesUtil = new SharedPreferencesUtil(app);
62+
// reset shared preferences and downloads for models used by this test.
63+
firebaseModelDownloader.deleteDownloadedModel(MODEL_NAME_LOCAL);
64+
firebaseModelDownloader.deleteDownloadedModel(MODEL_NAME_LOCAL_2);
65+
66+
// Equivalent to clearing the cache for the models used by this test.
67+
sharedPreferencesUtil.clearModelDetails(MODEL_NAME_LOCAL);
68+
sharedPreferencesUtil.clearModelDetails(MODEL_NAME_LOCAL_2);
69+
}
70+
71+
@After
72+
public void teardown() {
73+
if (firstLoadTempModelFile != null) {
74+
firstLoadTempModelFile.deleteOnExit();
75+
}
76+
if (firstDeviceModelFile != null) {
77+
firstDeviceModelFile.deleteOnExit();
78+
}
79+
}
80+
81+
private void setUpLoadedLocalModelWithFile() throws Exception {
82+
ModelFileManager fileManager = ModelFileManager.getInstance();
83+
final File testDir = new File(app.getApplicationContext().getNoBackupFilesDir(), "tmpModels");
84+
testDir.mkdirs();
85+
// make sure the directory is empty. Doesn't recurse into subdirs, but that's OK since
86+
// we're only using this directory for this test and we won't create any subdirs.
87+
for (File f : testDir.listFiles()) {
88+
if (f.isFile()) {
89+
f.delete();
90+
}
91+
}
92+
93+
firstLoadTempModelFile = File.createTempFile("modelFile", ".tflite");
94+
95+
String expectedDestinationFolder =
96+
new File(
97+
app.getApplicationContext().getNoBackupFilesDir(),
98+
ModelFileManager.CUSTOM_MODEL_ROOT_PATH)
99+
.getAbsolutePath()
100+
+ "/"
101+
+ app.getPersistenceKey()
102+
+ "/"
103+
+ MODEL_NAME_LOCAL;
104+
// move test files to expected locations.
105+
ParcelFileDescriptor fd =
106+
ParcelFileDescriptor.open(firstLoadTempModelFile, ParcelFileDescriptor.MODE_READ_ONLY);
107+
108+
firstDeviceModelFile = fileManager.moveModelToDestinationFolder(SETUP_LOADED_LOCAL_MODEL, fd);
109+
assertEquals(firstDeviceModelFile, new File(expectedDestinationFolder + "/0"));
110+
assertTrue(firstDeviceModelFile.exists());
111+
fd.close();
112+
113+
fakePreloadedCustomModel(
114+
MODEL_NAME_LOCAL,
115+
SETUP_LOADED_LOCAL_MODEL.getModelHash(),
116+
99,
117+
expectedDestinationFolder + "/0");
118+
}
119+
120+
@Test
121+
public void localModel_successTestPath()
122+
throws ExecutionException, InterruptedException, FirebaseMlException {
123+
124+
// no models to start
125+
Set<CustomModel> listModelTask = getDownloadedModelList();
126+
assertEquals(listModelTask.size(), 0);
127+
128+
// download 2 models and check they are in the list.
129+
Task<CustomModel> modelTask =
130+
FirebaseModelDownloader.getInstance()
131+
.getModel(
132+
MODEL_NAME_LOCAL,
133+
DownloadType.LOCAL_MODEL,
134+
new CustomModelDownloadConditions.Builder().build());
135+
Tasks.await(modelTask);
136+
assertTrue(modelTask.isSuccessful());
137+
138+
Task<CustomModel> modelUpdatedTask =
139+
FirebaseModelDownloader.getInstance()
140+
.getModel(
141+
MODEL_NAME_LOCAL_2,
142+
DownloadType.LOCAL_MODEL,
143+
new CustomModelDownloadConditions.Builder().build());
144+
Tasks.await(modelUpdatedTask);
145+
assertTrue(modelUpdatedTask.isSuccessful());
146+
147+
listModelTask = getDownloadedModelList();
148+
assertTrue(listModelTask.size() >= 2);
149+
assertTrue(listModelTask.contains(modelTask.getResult()));
150+
assertTrue(listModelTask.contains(modelUpdatedTask.getResult()));
151+
152+
// delete the old model
153+
FirebaseModelDownloader.getInstance().deleteDownloadedModel(MODEL_NAME_LOCAL);
154+
listModelTask = getDownloadedModelList();
155+
assertEquals(listModelTask.size(), 1);
156+
157+
// verify model file was also deleted
158+
assertNull(modelTask.getResult().getFile());
159+
}
160+
161+
@Test
162+
public void localModel_fetchDueToMissingFile()
163+
throws ExecutionException, InterruptedException, FirebaseMlException {
164+
// no models to start
165+
Set<CustomModel> listModelTask = getDownloadedModelList();
166+
assertEquals(listModelTask.size(), 0);
167+
168+
fakePreloadedCustomModel(MODEL_NAME_LOCAL_2, MODEL_HASH, 123L, "fake/path/model/0");
169+
listModelTask = getDownloadedModelList();
170+
assertEquals(listModelTask.size(), 1);
171+
172+
// bad path doesn't exist
173+
CustomModel model = listModelTask.iterator().next();
174+
assertNull(model.getFile());
175+
assertEquals(model.getModelHash(), MODEL_HASH);
176+
177+
// download models and check that it gets new download since file is missing.
178+
Task<CustomModel> modelTask =
179+
FirebaseModelDownloader.getInstance()
180+
.getModel(
181+
MODEL_NAME_LOCAL_2,
182+
DownloadType.LOCAL_MODEL,
183+
new CustomModelDownloadConditions.Builder().build());
184+
Tasks.await(modelTask);
185+
assertTrue(modelTask.isSuccessful());
186+
187+
listModelTask = getDownloadedModelList();
188+
assertEquals(listModelTask.size(), 1);
189+
190+
// updated path works.
191+
model = listModelTask.iterator().next();
192+
assertNotNull(model.getFile());
193+
assertNotEquals(model.getModelHash(), MODEL_HASH);
194+
}
195+
196+
@Test
197+
public void localModel_preloadedDoNotFetchUpdate() throws Exception {
198+
// no models to start
199+
Set<CustomModel> listModelTask = getDownloadedModelList();
200+
assertEquals(listModelTask.size(), 0);
201+
202+
setUpLoadedLocalModelWithFile();
203+
listModelTask = getDownloadedModelList();
204+
assertEquals(listModelTask.size(), 1);
205+
206+
// bad path doesn't exist
207+
CustomModel model = listModelTask.iterator().next();
208+
assertNotNull(model.getFile());
209+
assertEquals(model.getModelHash(), MODEL_HASH);
210+
211+
// download models and check that it gets new download since file is missing.
212+
Task<CustomModel> modelTask =
213+
FirebaseModelDownloader.getInstance()
214+
.getModel(
215+
MODEL_NAME_LOCAL,
216+
DownloadType.LOCAL_MODEL,
217+
new CustomModelDownloadConditions.Builder().build());
218+
Tasks.await(modelTask);
219+
assertTrue(modelTask.isSuccessful());
220+
221+
listModelTask = getDownloadedModelList();
222+
assertEquals(listModelTask.size(), 1);
223+
224+
// updated path works.
225+
model = listModelTask.iterator().next();
226+
assertNotNull(model.getFile());
227+
assertEquals(model.getModelHash(), MODEL_HASH);
228+
}
229+
230+
private void fakePreloadedCustomModel(String modelName, String hash, long size, String filePath) {
231+
SharedPreferencesUtil sharedPreferencesUtil = new SharedPreferencesUtil(app);
232+
sharedPreferencesUtil.setLoadedCustomModelDetails(
233+
new CustomModel(modelName, hash, size, 0L, filePath));
234+
}
235+
236+
private Set<CustomModel> getDownloadedModelList()
237+
throws ExecutionException, InterruptedException {
238+
239+
Task<Set<CustomModel>> modelSetTask =
240+
FirebaseModelDownloader.getInstance().listDownloadedModels();
241+
242+
Tasks.await(modelSetTask);
243+
if (modelSetTask.isSuccessful()) {
244+
return modelSetTask.getResult();
245+
}
246+
throw new InternalError("listDownloadedModels unexpected failure.");
247+
}
248+
}

firebase-ml-modeldownloader/src/main/java/com/google/firebase/ml/modeldownloader/internal/SharedPreferencesUtil.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,7 @@ public class SharedPreferencesUtil {
3333

3434
public static final String DOWNLOADING_MODEL_ID_MATCHER = "downloading_model_id_(.*?)_([^/]+)/?";
3535

36-
@VisibleForTesting
37-
static final String PREFERENCES_PACKAGE_NAME = "com.google.firebase.ml.modelDownloader";
36+
public static final String PREFERENCES_PACKAGE_NAME = "com.google.firebase.ml.modelDownloader";
3837

3938
// local model details
4039
private static final String LOCAL_MODEL_HASH_PATTERN = "current_model_hash_%s_%s";

0 commit comments

Comments
 (0)