Skip to content

Commit 5877d7a

Browse files
[GSoC] Added Unit Tests and Fixed Landscape Mode Bug (#3872)
* Fixes #3861 Use the APIs to fetch leaderboard’s based on uploads via mobile app (all time) and display it in the Leaderboard screen. * Fixed Bug - missing data in landscape mode * Added Unit Tests for Leaderboard * Added JavaDocs * Updated JavaDocs
1 parent 196b914 commit 5877d7a

File tree

8 files changed

+178
-8
lines changed

8 files changed

+178
-8
lines changed

app/build.gradle

+4-3
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ dependencies {
2121
// Utils
2222
implementation 'in.yuvi:http.fluent:1.3'
2323
implementation 'com.google.code.gson:gson:2.8.5'
24-
implementation 'com.squareup.okhttp3:okhttp:4.5.0'
24+
implementation 'com.squareup.okhttp3:okhttp:4.8.0'
2525
implementation 'com.squareup.okio:okio:2.2.2'
2626
implementation 'io.reactivex.rxjava2:rxandroid:2.1.0'
2727
implementation 'io.reactivex.rxjava2:rxjava:2.2.3'
@@ -50,6 +50,7 @@ dependencies {
5050
testImplementation "androidx.paging:paging-common-ktx:$PAGING_VERSION"
5151
implementation "androidx.paging:paging-rxjava2-ktx:$PAGING_VERSION"
5252
implementation "androidx.recyclerview:recyclerview:1.2.0-alpha02"
53+
implementation 'com.squareup.okhttp3:okhttp-ws:3.4.1'
5354

5455
// Logging
5556
implementation 'ch.acra:acra-dialog:5.3.0'
@@ -79,7 +80,7 @@ dependencies {
7980
testImplementation 'junit:junit:4.13'
8081
testImplementation 'org.robolectric:robolectric:4.3'
8182
testImplementation 'androidx.test:core:1.2.0'
82-
testImplementation 'com.squareup.okhttp3:mockwebserver:3.12.1'
83+
testImplementation "com.squareup.okhttp3:mockwebserver:4.8.0"
8384
testImplementation "org.powermock:powermock-module-junit4:2.0.0-beta.5"
8485
testImplementation "org.powermock:powermock-api-mockito2:2.0.0-beta.5"
8586
testImplementation 'org.mockito:mockito-core:2.23.0'
@@ -94,7 +95,7 @@ dependencies {
9495
androidTestImplementation 'androidx.test:runner:1.2.0'
9596
androidTestImplementation 'androidx.test:rules:1.2.0'
9697
androidTestImplementation 'androidx.annotation:annotation:1.1.0'
97-
androidTestImplementation 'com.squareup.okhttp3:mockwebserver:3.12.1'
98+
androidTestImplementation 'com.squareup.okhttp3:mockwebserver:4.8.0'
9899
androidTestUtil 'androidx.test:orchestrator:1.2.0'
99100

100101
// Debugging

app/src/main/java/fr/free/nrw/commons/profile/ProfileActivity.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@ public static void startYourself(Context context) {
5656
context.startActivity(intent);
5757
}
5858

59+
/**
60+
* Set the tabs for the fragments
61+
*/
5962
private void setTabs() {
6063
List<Fragment> fragmentList = new ArrayList<>();
6164
List<String> titleList = new ArrayList<>();
@@ -69,7 +72,6 @@ private void setTabs() {
6972
viewPagerAdapter.notifyDataSetChanged();
7073

7174
}
72-
7375
@Override
7476
public void onDestroy() {
7577
super.onDestroy();

app/src/main/java/fr/free/nrw/commons/profile/leaderboard/LeaderboardFragment.java

+4
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,10 @@ private void setLeaderboard() {
9797
}
9898
}
9999

100+
/**
101+
* Set the views
102+
* @param response Leaderboard Response Object
103+
*/
100104
private void setLeaderboardUser(LeaderboardResponse response) {
101105
hideProgressBar();
102106
avatar.setImageURI(

app/src/main/java/fr/free/nrw/commons/profile/leaderboard/LeaderboardListAdapter.java

+19
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ public ListViewHolder(View itemView) {
3232
this.count = itemView.findViewById(R.id.user_count);
3333
}
3434

35+
/**
36+
* This method will return the Context
37+
* @return Context
38+
*/
3539
public Context getContext() {
3640
return itemView.getContext();
3741
}
@@ -41,6 +45,12 @@ public LeaderboardListAdapter(List<LeaderboardList> leaderboardList) {
4145
this.leaderboardList = leaderboardList;
4246
}
4347

48+
/**
49+
* Overrides the onCreateViewHolder and inflates the recyclerview list item layout
50+
* @param parent
51+
* @param viewType
52+
* @return
53+
*/
4454
@NonNull
4555
@Override
4656
public LeaderboardListAdapter.ListViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
@@ -50,6 +60,11 @@ public LeaderboardListAdapter.ListViewHolder onCreateViewHolder(@NonNull ViewGro
5060
return new ListViewHolder(view);
5161
}
5262

63+
/**
64+
* Overrides the onBindViewHolder Set the view at the specific position with the specific value
65+
* @param holder
66+
* @param position
67+
*/
5368
@Override
5469
public void onBindViewHolder(@NonNull LeaderboardListAdapter.ListViewHolder holder, int position) {
5570
TextView rank = holder.rank;
@@ -66,6 +81,10 @@ public void onBindViewHolder(@NonNull LeaderboardListAdapter.ListViewHolder hold
6681
count.setText(leaderboardList.get(position).getCategoryCount().toString());
6782
}
6883

84+
/**
85+
* Override the getItemCount method
86+
* @return the size of the recycler view list
87+
*/
6988
@Override
7089
public int getItemCount() {
7190
return leaderboardList.size();

app/src/main/res/layout/fragment_leaderboard.xml

+10-4
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
<androidx.constraintlayout.widget.ConstraintLayout
1010
android:layout_width="match_parent"
11-
android:layout_height="match_parent">
11+
android:layout_height="wrap_content">
1212

1313
<com.facebook.drawee.view.SimpleDraweeView
1414
android:id="@+id/avatar"
@@ -87,13 +87,19 @@
8787

8888
</LinearLayout>
8989

90-
<androidx.recyclerview.widget.RecyclerView
91-
android:id="@+id/leaderboard_list"
90+
<androidx.core.widget.NestedScrollView
9291
android:layout_width="match_parent"
9392
android:layout_height="wrap_content"
9493
app:layout_constraintEnd_toEndOf="parent"
9594
app:layout_constraintStart_toStartOf="parent"
96-
app:layout_constraintTop_toBottomOf="@+id/column_names" />
95+
app:layout_constraintTop_toBottomOf="@+id/column_names">
96+
97+
<androidx.recyclerview.widget.RecyclerView
98+
android:id="@+id/leaderboard_list"
99+
android:layout_width="match_parent"
100+
android:layout_height="wrap_content"/>
101+
102+
</androidx.core.widget.NestedScrollView>
97103

98104
<ProgressBar
99105
android:id="@+id/progressBar"

app/src/test/kotlin/fr/free/nrw/commons/delete/ReasonBuilderTest.kt

+3
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import fr.free.nrw.commons.Media
66
import fr.free.nrw.commons.auth.SessionManager
77
import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient
88
import fr.free.nrw.commons.profile.achievements.FeedbackResponse
9+
import fr.free.nrw.commons.profile.leaderboard.LeaderboardResponse
910
import fr.free.nrw.commons.utils.ViewUtilWrapper
1011
import io.reactivex.Single
1112
import media
@@ -54,6 +55,8 @@ class ReasonBuilderTest {
5455
`when`(sessionManager?.doesAccountExist()).thenReturn(true)
5556
`when`(okHttpJsonApiClient!!.getAchievements(anyString()))
5657
.thenReturn(Single.just(mock(FeedbackResponse::class.java)))
58+
`when`(okHttpJsonApiClient!!.getLeaderboard(anyString(), anyString(), anyString(), anyString(), anyString()))
59+
.thenReturn(Single.just(mock(LeaderboardResponse::class.java)))
5760

5861
val media = media(filename="test_file", dateUploaded = Date())
5962

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
package fr.free.nrw.commons.leaderboard;
2+
3+
import com.google.gson.Gson;
4+
import fr.free.nrw.commons.profile.leaderboard.LeaderboardResponse;
5+
import java.io.BufferedReader;
6+
import java.io.IOException;
7+
import java.io.InputStream;
8+
import java.io.InputStreamReader;
9+
import okhttp3.HttpUrl;
10+
import okhttp3.OkHttpClient;
11+
import okhttp3.Request;
12+
import okhttp3.Request.Builder;
13+
import okhttp3.Response;
14+
import okhttp3.mockwebserver.MockResponse;
15+
import okhttp3.mockwebserver.MockWebServer;
16+
import org.junit.After;
17+
import org.junit.Assert;
18+
import org.junit.Before;
19+
import org.junit.Test;
20+
21+
/**
22+
* This class tests the Leaderboard API calls
23+
*/
24+
public class LeaderboardApiTest {
25+
26+
MockWebServer server;
27+
private static final String TEST_USERNAME = "user";
28+
private static final String TEST_AVATAR = "avatar";
29+
private static final int TEST_USER_RANK = 1;
30+
private static final int TEST_USER_COUNT = 0;
31+
32+
private static final String FILE_NAME = "leaderboard_sample_response.json";
33+
private static final String ENDPOINT = "/leaderboard.py";
34+
35+
/**
36+
* This method initialises a Mock Server
37+
*/
38+
@Before
39+
public void initTest() {
40+
server = new MockWebServer();
41+
}
42+
43+
/**
44+
* This method will setup a Mock Server and load Test JSON Response File
45+
* @throws Exception
46+
*/
47+
@Before
48+
public void setUp() throws Exception {
49+
50+
String testResponseBody = convertStreamToString(getClass().getClassLoader().getResourceAsStream(FILE_NAME));
51+
52+
server.enqueue(new MockResponse().setBody(testResponseBody));
53+
server.start();
54+
}
55+
56+
/**
57+
* This method converts a Input Stream to String
58+
* @param is takes Input Stream of JSON File as Parameter
59+
* @return a String with JSON data
60+
* @throws Exception
61+
*/
62+
private static String convertStreamToString(InputStream is) throws Exception {
63+
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
64+
StringBuilder sb = new StringBuilder();
65+
String line;
66+
while ((line = reader.readLine()) != null) {
67+
sb.append(line).append("\n");
68+
}
69+
reader.close();
70+
return sb.toString();
71+
}
72+
73+
/**
74+
* This method will call the Mock Server and Test it with sample values.
75+
* It will test the Leaderboard API call functionality and check if the object is
76+
* being created with the correct values
77+
* @throws IOException
78+
*/
79+
@Test
80+
public void apiTest() throws IOException {
81+
HttpUrl httpUrl = server.url(ENDPOINT);
82+
LeaderboardResponse response = sendRequest(new OkHttpClient(), httpUrl);
83+
84+
Assert.assertEquals(TEST_AVATAR, response.getAvatar());
85+
Assert.assertEquals(TEST_USERNAME, response.getUsername());
86+
Assert.assertEquals(Integer.valueOf(TEST_USER_RANK), response.getRank());
87+
Assert.assertEquals(Integer.valueOf(TEST_USER_COUNT), response.getCategoryCount());
88+
}
89+
90+
/**
91+
* This method will call the Mock API and returns the Leaderboard Response Object
92+
* @param okHttpClient
93+
* @param httpUrl
94+
* @return Leaderboard Response Object
95+
* @throws IOException
96+
*/
97+
private LeaderboardResponse sendRequest(OkHttpClient okHttpClient, HttpUrl httpUrl)
98+
throws IOException {
99+
Request request = new Builder().url(httpUrl).build();
100+
Response response = okHttpClient.newCall(request).execute();
101+
if (response.isSuccessful()) {
102+
Gson gson = new Gson();
103+
return gson.fromJson(response.body().string(), LeaderboardResponse.class);
104+
}
105+
return null;
106+
}
107+
108+
/**
109+
* This method shuts down the Mock Server
110+
* @throws IOException
111+
*/
112+
@After
113+
public void shutdown() throws IOException {
114+
server.shutdown();
115+
}
116+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"status": 200,
3+
"username": "user",
4+
"category_count": 0,
5+
"limit": null,
6+
"avatar": "avatar",
7+
"offset": null,
8+
"duration": "all_time",
9+
"leaderboard_list": [
10+
{
11+
"username": "user",
12+
"category_count": 0,
13+
"avatar": "avatar",
14+
"rank": 1
15+
}
16+
],
17+
"category": "used",
18+
"rank": 1
19+
}

0 commit comments

Comments
 (0)