Skip to content

Commit f3a90c0

Browse files
maskaravivekmisaochan
authored andcommitted
Upload tests (commons-app#2086)
* Add unit tests for upload flows * Tests for upload model * Test fixes * Remove empty test cases * Changes based on comments
1 parent 1070137 commit f3a90c0

19 files changed

+533
-252
lines changed

app/build.gradle

+4
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@ dependencies {
7777
releaseImplementation "com.squareup.leakcanary:leakcanary-android-no-op:$LEAK_CANARY"
7878
testImplementation "com.squareup.leakcanary:leakcanary-android-no-op:$LEAK_CANARY"
7979

80+
androidTestImplementation "org.mockito:mockito-core:2.10.0"
81+
8082
//For handling runtime permissions
8183
implementation 'com.karumi:dexter:5.0.0'
8284

@@ -100,6 +102,8 @@ android {
100102
}
101103

102104
testOptions {
105+
unitTests.returnDefaultValues = true
106+
103107
unitTests.all {
104108
jvmArgs '-noverify'
105109
}

app/src/main/java/fr/free/nrw/commons/CommonsApplication.java

+5-1
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ public void onCreate() {
119119
ContributionUtils.emptyTemporaryDirectory();
120120

121121
initAcra();
122-
if (BuildConfig.DEBUG) {
122+
if (BuildConfig.DEBUG && !isRoboUnitTest()) {
123123
Stetho.initializeWithDefaults(this);
124124
}
125125

@@ -162,6 +162,10 @@ private void initAcra() {
162162
Thread.setDefaultUncaughtExceptionHandler(exceptionHandler);
163163
}
164164

165+
public static boolean isRoboUnitTest() {
166+
return "robolectric".equals(Build.FINGERPRINT);
167+
}
168+
165169
private ThreadPoolService getFileLoggingThreadPool() {
166170
return new ThreadPoolService.Builder("file-logging-thread")
167171
.setPriority(Process.THREAD_PRIORITY_LOWEST)

app/src/main/java/fr/free/nrw/commons/upload/FileProcessor.java

+9-20
Original file line numberDiff line numberDiff line change
@@ -2,37 +2,31 @@
22

33
import android.annotation.SuppressLint;
44
import android.content.ContentResolver;
5-
import android.content.Context;
65
import android.content.SharedPreferences;
76
import android.media.ExifInterface;
87
import android.net.Uri;
98
import android.os.Build;
10-
import android.os.Bundle;
119
import android.os.ParcelFileDescriptor;
1210
import android.support.annotation.NonNull;
13-
import android.support.annotation.Nullable;
14-
import android.support.v7.app.AppCompatActivity;
1511

1612
import java.io.File;
1713
import java.io.FileNotFoundException;
1814
import java.io.IOException;
19-
import java.util.Date;
2015
import java.util.List;
2116

2217
import javax.inject.Inject;
2318
import javax.inject.Named;
19+
import javax.inject.Singleton;
2420

2521
import fr.free.nrw.commons.caching.CacheController;
26-
import fr.free.nrw.commons.di.ApplicationlessInjection;
2722
import fr.free.nrw.commons.mwapi.CategoryApi;
2823
import io.reactivex.schedulers.Schedulers;
2924
import timber.log.Timber;
3025

31-
import static com.mapbox.mapboxsdk.Mapbox.getApplicationContext;
32-
3326
/**
3427
* Processing of the image file that is about to be uploaded via ShareActivity is done here
3528
*/
29+
@Singleton
3630
public class FileProcessor implements SimilarImageDialogFragment.onResponse {
3731

3832
@Inject
@@ -47,24 +41,23 @@ public class FileProcessor implements SimilarImageDialogFragment.onResponse {
4741
private String filePath;
4842
private ContentResolver contentResolver;
4943
private GPSExtractor imageObj;
50-
private Context context;
5144
private String decimalCoords;
5245
private ExifInterface exifInterface;
53-
private boolean useExtStorage;
5446
private boolean haveCheckedForOtherImages = false;
5547
private GPSExtractor tempImageObj;
5648

57-
FileProcessor(@NonNull String filePath, ContentResolver contentResolver, Context context) {
49+
@Inject
50+
FileProcessor() {
51+
}
52+
53+
void initFileDetails(@NonNull String filePath, ContentResolver contentResolver) {
5854
this.filePath = filePath;
5955
this.contentResolver = contentResolver;
60-
this.context = context;
61-
ApplicationlessInjection.getInstance(context.getApplicationContext()).getCommonsApplicationComponent().inject(this);
6256
try {
63-
exifInterface=new ExifInterface(filePath);
57+
exifInterface = new ExifInterface(filePath);
6458
} catch (IOException e) {
6559
Timber.e(e);
6660
}
67-
useExtStorage = prefs.getBoolean("useExternalStorage", true);
6861
}
6962

7063
/**
@@ -85,10 +78,6 @@ GPSExtractor processFileCoordinates(SimilarImageInterface similarImageInterface)
8578
return imageObj;
8679
}
8780

88-
String getDecimalCoords() {
89-
return decimalCoords;
90-
}
91-
9281
/**
9382
* Find other images around the same location that were taken within the last 20 sec
9483
* @param similarImageInterface
@@ -142,7 +131,7 @@ private void findOtherImages(SimilarImageInterface similarImageInterface) {
142131
* Then initiates the calls to MediaWiki API through an instance of CategoryApi.
143132
*/
144133
@SuppressLint("CheckResult")
145-
public void useImageCoords() {
134+
private void useImageCoords() {
146135
if (decimalCoords != null) {
147136
Timber.d("Decimal coords of image: %s", decimalCoords);
148137
Timber.d("is EXIF data present:" + imageObj.imageCoordsExists + " from findOther image");

app/src/main/java/fr/free/nrw/commons/upload/FileUtils.java

+9-5
Original file line numberDiff line numberDiff line change
@@ -234,8 +234,8 @@ else if ("file".equalsIgnoreCase(uri.getScheme())) {
234234
* @return The value of the _data column, which is typically a file path.
235235
*/
236236
@Nullable
237-
public static String getDataColumn(Context context, Uri uri, String selection,
238-
String[] selectionArgs) {
237+
private static String getDataColumn(Context context, Uri uri, String selection,
238+
String[] selectionArgs) {
239239

240240
Cursor cursor = null;
241241
final String column = MediaStore.Images.ImageColumns.DATA;
@@ -311,7 +311,7 @@ public static void copy(@NonNull FileInputStream source, @NonNull FileOutputStre
311311
* @param destination file path copied to
312312
* @throws IOException thrown when failing to read source or opening destination file
313313
*/
314-
public static void copy(@NonNull FileDescriptor source, @NonNull String destination)
314+
private static void copy(@NonNull FileDescriptor source, @NonNull String destination)
315315
throws IOException {
316316
copy(new FileInputStream(source), new FileOutputStream(destination));
317317
}
@@ -415,7 +415,7 @@ public static String getFilename(Uri uri, ContentResolver contentResolver) {
415415
return result;
416416
}
417417

418-
public static String getFileExt(String fileName){
418+
static String getFileExt(String fileName){
419419
//Default file extension
420420
String extension=".jpg";
421421

@@ -426,7 +426,11 @@ public static String getFileExt(String fileName){
426426
return extension;
427427
}
428428

429-
public static String getFileExt(Uri uri, ContentResolver contentResolver) {
429+
private static String getFileExt(Uri uri, ContentResolver contentResolver) {
430430
return getFileExt(getFilename(uri, contentResolver));
431431
}
432+
433+
public static FileInputStream getFileInputStream(String filePath) throws FileNotFoundException {
434+
return new FileInputStream(filePath);
435+
}
432436
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package fr.free.nrw.commons.upload;
2+
3+
import android.content.ContentResolver;
4+
import android.content.Context;
5+
import android.net.Uri;
6+
7+
import java.io.FileInputStream;
8+
import java.io.FileNotFoundException;
9+
import java.io.IOException;
10+
import java.io.InputStream;
11+
12+
import javax.inject.Inject;
13+
import javax.inject.Singleton;
14+
15+
@Singleton
16+
public class FileUtilsWrapper {
17+
18+
@Inject
19+
public FileUtilsWrapper() {
20+
21+
}
22+
23+
public String createExternalCopyPathAndCopy(Uri uri, ContentResolver contentResolver) throws IOException {
24+
return FileUtils.createExternalCopyPathAndCopy(uri, contentResolver);
25+
}
26+
27+
public String createCopyPathAndCopy(Uri uri, Context context) throws IOException {
28+
return FileUtils.createCopyPathAndCopy(uri, context);
29+
}
30+
31+
public String getFileExt(String fileName) {
32+
return FileUtils.getFileExt(fileName);
33+
}
34+
35+
public String getSHA1(InputStream is) {
36+
return FileUtils.getSHA1(is);
37+
}
38+
39+
public FileInputStream getFileInputStream(String filePath) throws FileNotFoundException {
40+
return FileUtils.getFileInputStream(filePath);
41+
}
42+
}

app/src/main/java/fr/free/nrw/commons/upload/GPSExtractor.java

+9-9
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,12 @@
1414
* Extracts geolocation to be passed to API for category suggestions. If a picture with geolocation
1515
* is uploaded, extract latitude and longitude from EXIF data of image.
1616
*/
17-
public class GPSExtractor {
17+
class GPSExtractor {
1818

19-
public static final GPSExtractor DUMMY= new GPSExtractor();
19+
static final GPSExtractor DUMMY= new GPSExtractor();
2020
private double decLatitude;
2121
private double decLongitude;
22-
public boolean imageCoordsExists;
22+
boolean imageCoordsExists;
2323
private String latitude;
2424
private String longitude;
2525
private String latitudeRef;
@@ -37,7 +37,7 @@ private GPSExtractor(){
3737
* @param fileDescriptor the file descriptor of the image
3838
*/
3939
@RequiresApi(24)
40-
public GPSExtractor(@NonNull FileDescriptor fileDescriptor) {
40+
GPSExtractor(@NonNull FileDescriptor fileDescriptor) {
4141
try {
4242
ExifInterface exif = new ExifInterface(fileDescriptor);
4343
processCoords(exif);
@@ -51,7 +51,7 @@ public GPSExtractor(@NonNull FileDescriptor fileDescriptor) {
5151
* @param path file path of the image
5252
*
5353
*/
54-
public GPSExtractor(@NonNull String path) {
54+
GPSExtractor(@NonNull String path) {
5555
try {
5656
ExifInterface exif = new ExifInterface(path);
5757
processCoords(exif);
@@ -65,7 +65,7 @@ public GPSExtractor(@NonNull String path) {
6565
* @param exif exif interface of the image
6666
*
6767
*/
68-
public GPSExtractor(@NonNull ExifInterface exif){
68+
GPSExtractor(@NonNull ExifInterface exif){
6969
processCoords(exif);
7070
}
7171

@@ -89,7 +89,7 @@ private void processCoords(ExifInterface exif){
8989
* @return coordinates as string (needs to be passed as a String in API query)
9090
*/
9191
@Nullable
92-
public String getCoords() {
92+
String getCoords() {
9393
if(decimalCoords!=null){
9494
return decimalCoords;
9595
}else if (latitude!=null && latitudeRef!=null && longitude!=null && longitudeRef!=null) {
@@ -103,11 +103,11 @@ public String getCoords() {
103103
}
104104
}
105105

106-
public double getDecLatitude() {
106+
double getDecLatitude() {
107107
return decLatitude;
108108
}
109109

110-
public double getDecLongitude() {
110+
double getDecLongitude() {
111111
return decLongitude;
112112
}
113113

app/src/main/java/fr/free/nrw/commons/upload/UploadController.java

+6-5
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ public UploadController(SessionManager sessionManager, Context context, SharedPr
5151
}
5252

5353
private boolean isUploadServiceConnected;
54-
private ServiceConnection uploadServiceConnection = new ServiceConnection() {
54+
public ServiceConnection uploadServiceConnection = new ServiceConnection() {
5555
@Override
5656
public void onServiceConnected(ComponentName componentName, IBinder binder) {
5757
uploadService = (UploadService) ((HandlerService.HandlerServiceLocalBinder) binder).getService();
@@ -61,14 +61,15 @@ public void onServiceConnected(ComponentName componentName, IBinder binder) {
6161
@Override
6262
public void onServiceDisconnected(ComponentName componentName) {
6363
// this should never happen
64+
isUploadServiceConnected = false;
6465
Timber.e(new RuntimeException("UploadService died but the rest of the process did not!"));
6566
}
6667
};
6768

6869
/**
6970
* Prepares the upload service.
7071
*/
71-
public void prepareService() {
72+
void prepareService() {
7273
Intent uploadServiceIntent = new Intent(context, UploadService.class);
7374
uploadServiceIntent.setAction(UploadService.ACTION_START_SERVICE);
7475
context.startService(uploadServiceIntent);
@@ -78,7 +79,7 @@ public void prepareService() {
7879
/**
7980
* Disconnects the upload service.
8081
*/
81-
public void cleanup() {
82+
void cleanup() {
8283
if (isUploadServiceConnected) {
8384
context.unbindService(uploadServiceConnection);
8485
}
@@ -89,7 +90,7 @@ public void cleanup() {
8990
*
9091
* @param contribution the contribution object
9192
*/
92-
public void startUpload(Contribution contribution) {
93+
void startUpload(Contribution contribution) {
9394
startUpload(contribution, c -> {});
9495
}
9596

@@ -100,7 +101,7 @@ public void startUpload(Contribution contribution) {
100101
* @param onComplete the progress tracker
101102
*/
102103
@SuppressLint("StaticFieldLeak")
103-
public void startUpload(final Contribution contribution, final ContributionUploadProgress onComplete) {
104+
private void startUpload(final Contribution contribution, final ContributionUploadProgress onComplete) {
104105
//Set creator, desc, and license
105106
if (TextUtils.isEmpty(contribution.getCreator())) {
106107
Account currentAccount = sessionManager.getCurrentAccount();

0 commit comments

Comments
 (0)