update tests

This commit is contained in:
David Luhmer 2019-04-05 14:47:00 -03:00
parent 0914892f76
commit 1772606290
8 changed files with 209 additions and 96 deletions

View file

@ -179,6 +179,9 @@ dependencies {
testImplementation 'com.google.dexmaker:dexmaker-mockito:1.2'
testImplementation 'com.squareup.okhttp3:mockwebserver:3.14.0'
androidTestAnnotationProcessor "com.google.dagger:dagger-compiler:2.16"
//androidTestCompile 'com.squareup.okhttp:mockwebserver:2.5.0'
// https://mvnrepository.com/artifact/com.squareup.okhttp3/mockwebserver
androidTestImplementation group: 'com.squareup.okhttp3', name: 'mockwebserver', version: '3.14.0'
@ -186,7 +189,7 @@ dependencies {
androidTestImplementation 'tools.fastlane:screengrab:1.2.0'
//androidTestImplementation 'org.mockito:mockito-core:2.25.1'
androidTestImplementation 'org.mockito:mockito-android:2.25.1'
androidTestImplementation 'org.mockito:mockito-android:2.18.3'
//androidTestImplementation 'com.google.dexmaker:dexmaker:1.2'
@ -222,6 +225,7 @@ dependencies {
}
/*
// Workaround for https://github.com/mockito/mockito/issues/1511#issuecomment-458544398
configurations.all {
resolutionStrategy.eachDependency { details ->
if (details.requested.group == 'net.bytebuddy') {
@ -229,4 +233,4 @@ configurations.all {
}
}
}
*/
*/

View file

@ -39,4 +39,10 @@
-keep class okhttp3.internal.platform.ConscryptPlatform
-keep class org.conscrypt.Conscrypt { *; }
-keep interface org.conscrypt.Conscrypt { *; }
-keep interface org.conscrypt.Conscrypt { *; }
#org.conscrypt
-dontwarn org.conscrypt.**
#-keep class org.conscrypt.** { *; }
#-keep interface org.conscrypt.** { *; }

View file

@ -1,31 +1,35 @@
package com.nextcloud.android.sso.api;
import com.nextcloud.android.sso.aidl.NextcloudRequest;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import javax.inject.Inject;
import androidx.test.filters.LargeTest;
import androidx.test.rule.ActivityTestRule;
import de.luhmer.owncloudnewsreader.NewFeedActivity;
import de.luhmer.owncloudnewsreader.R;
import de.luhmer.owncloudnewsreader.TestApplication;
import de.luhmer.owncloudnewsreader.di.ApiProvider;
import de.luhmer.owncloudnewsreader.di.TestComponent;
import de.luhmer.owncloudnewsreader.reader.nextcloud.API;
import retrofit2.NextcloudRetrofitApiBuilder;
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.action.ViewActions.click;
import static androidx.test.espresso.action.ViewActions.closeSoftKeyboard;
import static androidx.test.espresso.action.ViewActions.typeText;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
import static androidx.test.espresso.matcher.ViewMatchers.hasErrorText;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
import static junit.framework.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.after;
import static org.hamcrest.CoreMatchers.is;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.verify;
@ -37,60 +41,78 @@ public class NewFeedTests {
@Rule
public ActivityTestRule<NewFeedActivity> activityRule = new ActivityTestRule<>(NewFeedActivity.class);
private final String mApiEndpoint = "/index.php/apps/news/api/v1-2/";
//private API mApi;
@Mock
private NextcloudAPI nextcloudApiMock;
@Mock
private API mApi;
//@Rule
//public MockitoRule mockitoRule = MockitoJUnit.rule();
@Inject ApiProvider mApi;
@Before
public void setUp() {
//MockitoAnnotation.initMocks(this);
//when(nextcloudApiMock.getGson()).thenReturn(new GsonBuilder().create());
//mApi = new NextcloudRetrofitApiBuilder(nextcloudApiMock, mApiEndpoint).create(API.class);
//mApi = new NextcloudRetrofitApiBuilder(nextcloudApiMock, mApiEndpoint).create(API.class);
TestComponent ac = (TestComponent) ((TestApplication)(activityRule.getActivity().getApplication())).getAppComponent();
ac.inject(this);
}
@Test
public void changeText_sameActivity() {
public void addNewFeed_New_sameActivity() {
String feed = "http://test.de/new";
// Type text and then press the button.
onView(withId(R.id.et_feed_url)).perform(typeText("http://test.html"), closeSoftKeyboard());
onView(withId(R.id.et_feed_url)).perform(typeText(feed), closeSoftKeyboard());
onView(withId(R.id.btn_addFeed)).perform(click());
// Check that the text was changed.
//onView(withId(R.id.et_feed_url))
// .check(matches(withText(stringToBetyped)));
NextcloudRequest request = new NextcloudRequest.Builder()
.setMethod("GET")
.setUrl(mApiEndpoint + "feeds")
.build();
//Robolectric.flushForegroundThreadScheduler();
//Robolectric.flushBackgroundThreadScheduler();
try {
verify(mApi, after(5000)).createFeed(any());
verify(nextcloudApiMock, after(5000)).performNetworkRequest(eq(request));
API api = mApi.getAPI();
verify(api, timeout(2000)).createFeed(feed, 0L);
//onView(withId(R.id.et_feed_url)).check(matches(hasErrorText(nullValue(String.class))));
//verify(mApi, timeout(5000)).createFeed(any(), eq(request));
// Check Activity existed
Thread.sleep(1000);
assertFalse(activityRule.getActivity().getWindow().getDecorView().isShown());
} catch (Exception e) {
fail(e.getMessage());
}
}
@Test
public void addNewFeed_Existing_sameActivity() {
String feed = "http://test.de/existing";
// Type text and then press the button.
onView(withId(R.id.et_feed_url)).perform(typeText(feed), closeSoftKeyboard());
onView(withId(R.id.btn_addFeed)).perform(click());
try {
API api = mApi.getAPI();
verify(api, timeout(2000)).createFeed(feed, 0L);
// Check Activity still open
Thread.sleep(1000);
assertTrue(activityRule.getActivity().getWindow().getDecorView().isShown());
onView(withId(R.id.et_feed_url)).check(matches(hasErrorText(is("Feed konnte nicht hinzugefügt werden: Existiert bereits"))));
} catch (Exception e) {
fail(e.getMessage());
}
}
@Test
public void addNewFeed_Invalid_sameActivity() {
String feed = "http://test.de/fail";
// Type text and then press the button.
onView(withId(R.id.et_feed_url)).perform(typeText(feed), closeSoftKeyboard());
onView(withId(R.id.btn_addFeed)).perform(click());
try {
API api = mApi.getAPI();
verify(api, timeout(2000)).createFeed(feed, 0L);
// Check Activity still open
Thread.sleep(1000);
assertTrue(activityRule.getActivity().getWindow().getDecorView().isShown());
onView(withId(R.id.et_feed_url)).check(matches(hasErrorText(is("FeedIo\\Adapter\\NotFoundException: Client error: `GET http://feeds2.feedburner.com/stadt-bremerhaven/dqXM222` resulted in a `404 Feed not found error: ..."))));
} catch (Exception e) {
fail(e.getMessage());
}
}
}

View file

@ -1,23 +1,15 @@
package de.luhmer.owncloudnewsreader;
import android.app.Application;
import javax.inject.Inject;
import androidx.fragment.app.Fragment;
import de.luhmer.owncloudnewsreader.di.ApiModule;
import de.luhmer.owncloudnewsreader.di.AppComponent;
import de.luhmer.owncloudnewsreader.di.DaggerAppComponent;
import de.luhmer.owncloudnewsreader.di.DaggerTestComponent;
import de.luhmer.owncloudnewsreader.di.TestApiModule;
import de.luhmer.owncloudnewsreader.helper.AdBlocker;
import de.luhmer.owncloudnewsreader.helper.ForegroundListener;
public class TestApplication extends NewsReaderApplication {
@Override
public void initDaggerAppComponent() {
// Dagger%COMPONENT_NAME%
mAppComponent = DaggerAppComponent.builder()
mAppComponent = DaggerTestComponent.builder()
.apiModule(new TestApiModule(this))
.build();

View file

@ -3,30 +3,27 @@ package de.luhmer.owncloudnewsreader.di;
import android.app.Application;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Looper;
import android.os.NetworkOnMainThreadException;
import com.nextcloud.android.sso.AccountImporter;
import com.nextcloud.android.sso.helper.SingleAccountHelper;
import com.nextcloud.android.sso.model.SingleSignOnAccount;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import java.io.IOException;
import java.util.List;
import javax.inject.Singleton;
import dagger.Provides;
import de.luhmer.owncloudnewsreader.SettingsActivity;
import de.luhmer.owncloudnewsreader.database.model.Feed;
import de.luhmer.owncloudnewsreader.di.ApiModule;
import de.luhmer.owncloudnewsreader.di.ApiProvider;
import de.luhmer.owncloudnewsreader.reader.nextcloud.API;
import de.luhmer.owncloudnewsreader.ssl.MemorizingTrustManager;
import okhttp3.Request;
import okhttp3.ResponseBody;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyString;
@ -36,42 +33,106 @@ import static org.mockito.Mockito.when;
public class TestApiModule extends ApiModule {
private Application application;
public TestApiModule(Application application) {
super(application);
this.application = application;
}
@Override
public SharedPreferences providesSharedPreferences() {
SharedPreferences sharedPrefs = Mockito.mock(SharedPreferences.class);
final Context context = Mockito.mock(Context.class);
SharedPreferences sharedPrefs = mock(SharedPreferences.class);
final Context context = mock(Context.class);
when(context.getSharedPreferences(anyString(), anyInt())).thenReturn(sharedPrefs);
// Turn on Single-Sign-On
when(sharedPrefs.getBoolean(SettingsActivity.SW_USE_SINGLE_SIGN_ON, false)).thenReturn(true);
// Set cache size
when(sharedPrefs.getString(eq(SettingsActivity.SP_MAX_CACHE_SIZE), any())).thenReturn("500");
// Add dummy account
String accountName = "test-account";
String username = "david";
String token = "abc";
String server_url = "http://nextcloud.com/";
String prefKey = "PREF_ACCOUNT_STRING" + accountName;
SingleSignOnAccount ssoAccount = new SingleSignOnAccount(accountName, username, token, server_url);
try {
AccountImporter.getSharedPreferences(application).edit().putString(prefKey, SingleSignOnAccount.toString(ssoAccount)).commit();
} catch (IOException e) {
e.printStackTrace();
}
//try {
// when(sharedPrefs.getString(eq(prefKey), any())).thenReturn(SingleSignOnAccount.toString(ssoAccount));
//} catch (IOException e) {
// e.printStackTrace();
//}
SingleAccountHelper.setCurrentAccount(application, accountName);
return sharedPrefs;
}
private final String NEW_FEED_SUCCESS = "http://test.de/new";
private final String NEW_FEED_EXISTING = "http://test.de/existing";
private final String NEW_FEED_FAIL = "http://test.de/fail";
private final String NEW_FEED_EXISTING_ERROR_MESSAGE = "{\"message\":\"Feed konnte nicht hinzugef\\u00fcgt werden: Existiert bereits\"}";
private final String NEW_FEED_FAIL_ERROR_MESSAGE = "{\"message\":\"FeedIo\\\\Adapter\\\\NotFoundException: Client error: `GET http:\\/\\/feeds2.feedburner.com\\/stadt-bremerhaven\\/dqXM222` resulted in a `404 Feed not found error: FeedBurner cannot locate this feed URI.` response:\\n\\u003Chtml\\u003E\\n\\u003Chead\\u003E\\n\\u003Cstyle type=\\\"text\\/css\\\"\\u003E\\na:link, a:visited {\\n color: #000099;\\n text-decoration: underline;\\n}\\n\\na:hover {\\n (truncated...)\\n in \\/apps2\\/news\\/lib\\/Fetcher\\/Client\\/FeedIoClient.php:57\\nStack trace:\\n#0 \\/apps2\\/news\\/vendor\\/debril\\/feed-io\\/src\\/FeedIo\\/Reader.php(116): OCA\\\\News\\\\Fetcher\\\\Client\\\\FeedIoClient-\\u003EgetResponse('http:\\/\\/feeds2.f...', Object(DateTime))\\n#1 \\/apps2\\/news\\/vendor\\/debril\\/feed-io\\/src\\/FeedIo\\/FeedIo.php(286): FeedIo\\\\Reader-\\u003Eread('http:\\/\\/feeds2.f...', Object(FeedIo\\\\Feed), Object(DateTime))\\n#2 \\/apps2\\/news\\/lib\\/Fetcher\\/FeedFetcher.php(77): FeedIo\\\\FeedIo-\\u003Eread('http:\\/\\/feeds2.f...')\\n#3 \\/apps2\\/news\\/lib\\/Fetcher\\/Fetcher.php(68): OCA\\\\News\\\\Fetcher\\\\FeedFetcher-\\u003Efetch('http:\\/\\/feeds2.f...', true, NULL, NULL, NULL)\\n#4 \\/apps2\\/news\\/lib\\/Service\\/FeedService.php(116): OCA\\\\News\\\\Fetcher\\\\Fetcher-\\u003Efetch('http:\\/\\/feeds2.f...', true, NULL, NULL, NULL)\\n#5 \\/apps2\\/news\\/lib\\/Controller\\/FeedApiController.php(96): OCA\\\\News\\\\Service\\\\FeedService-\\u003Ecreate('http:\\/\\/feeds2.f...', 0, 'david')\\n#6 \\/nextcloud\\/lib\\/private\\/AppFramework\\/Http\\/Dispatcher.php(166): OCA\\\\News\\\\Controller\\\\FeedApiController-\\u003Ecreate('http:\\/\\/feeds2.f...', 0)\\n#7 \\/nextcloud\\/lib\\/private\\/AppFramework\\/Http\\/Dispatcher.php(99): OC\\\\AppFramework\\\\Http\\\\Dispatcher-\\u003EexecuteController(Object(OCA\\\\News\\\\Controller\\\\FeedApiController), 'create')\\n#8 \\/nextcloud\\/lib\\/private\\/AppFramework\\/App.php(118): OC\\\\AppFramework\\\\Http\\\\Dispatcher-\\u003Edispatch(Object(OCA\\\\News\\\\Controller\\\\FeedApiController), 'create')\\n#9 \\/nextcloud\\/lib\\/private\\/AppFramework\\/Routing\\/RouteActionHandler.php(47): OC\\\\AppFramework\\\\App::main('OCA\\\\\\\\News\\\\\\\\Contro...', 'create', Object(OC\\\\AppFramework\\\\DependencyInjection\\\\DIContainer), Array)\\n#10 [internal function]: OC\\\\AppFramework\\\\Routing\\\\RouteActionHandler-\\u003E__invoke(Array)\\n#11 \\/nextcloud\\/lib\\/private\\/Route\\/Router.php(297): call_user_func(Object(OC\\\\AppFramework\\\\Routing\\\\RouteActionHandler), Array)\\n#12 \\/nextcloud\\/lib\\/base.php(987): OC\\\\Route\\\\Router-\\u003Ematch('\\/apps\\/news\\/api\\/...')\\n#13 \\/nextcloud\\/index.php(42): OC::handleRequest()\\n#14 {main}\"}";
@Override
ApiProvider provideAPI(MemorizingTrustManager mtm, SharedPreferences sp) {
ApiProvider apiProvider = Mockito.mock(ApiProvider.class);
API api = Mockito.mock(API.class);
when(apiProvider.getAPI()).thenReturn(api);
apiProvider.setAPI(api);
Call mockCall = Mockito.mock(Call.class);
when(api.createFeed(any())).thenReturn(mockCall);
/*
doAnswer(invocation -> {
if(Looper.myLooper() == Looper.getMainLooper()) {
throw new NetworkOnMainThreadException();
}
return null;
}).when(mockCall).enqueue(any());
*/
Call<List<Feed>> mockCallSuccess = mockCreateFeed(NEW_FEED_SUCCESS);
Call<List<Feed>> mockCallExisting = mockCreateFeed(NEW_FEED_EXISTING);
Call<List<Feed>> mockCallFail = mockCreateFeed(NEW_FEED_FAIL);
when(api.createFeed(eq(NEW_FEED_SUCCESS), any())).thenReturn(mockCallSuccess);
when(api.createFeed(eq(NEW_FEED_EXISTING), any())).thenReturn(mockCallExisting);
when(api.createFeed(eq(NEW_FEED_FAIL), any())).thenReturn(mockCallFail);
return apiProvider;
}
// https://github.com/nextcloud/news/blob/master/docs/externalapi/Legacy.md#create-a-feed
private Call<List<Feed>> mockCreateFeed(String url) {
Call<List<Feed>> mockCall = Mockito.mock(Call.class);
doAnswer((Answer<Void>) invocation -> {
Object[] args = invocation.getArguments();
Callback callback = (Callback) args[0];
final Thread thr = new Thread() {
@Override
public void run() {
switch(url) {
case NEW_FEED_SUCCESS:
callback.onResponse(mockCall, Response.success(""));
break;
case NEW_FEED_EXISTING:
callback.onResponse(mockCall, Response.error(409, ResponseBody.create(null, NEW_FEED_EXISTING_ERROR_MESSAGE)));
break;
case NEW_FEED_FAIL:
callback.onResponse(mockCall,Response.error(422 , ResponseBody.create(null, NEW_FEED_FAIL_ERROR_MESSAGE)));
break;
default:
throw new RuntimeException("URL NOT KNOWN FOR TEST!");
}
}
};
thr.start();
return null;
}).when(mockCall).enqueue(any());
return mockCall;
}
}

View file

@ -0,0 +1,13 @@
package de.luhmer.owncloudnewsreader.di;
import com.nextcloud.android.sso.api.NewFeedTests;
import javax.inject.Singleton;
import dagger.Component;
@Singleton
@Component(modules = { ApiModule.class })
public interface TestComponent extends AppComponent {
void inject(NewFeedTests test);
}

View file

@ -7,10 +7,6 @@ import android.content.Context;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import android.text.TextUtils;
import android.util.Log;
import android.util.Xml;
@ -25,6 +21,8 @@ import android.widget.Toast;
import com.nbsp.materialfilepicker.ui.FilePickerActivity;
import org.json.JSONException;
import org.json.JSONObject;
import org.xmlpull.v1.XmlPullParser;
import java.io.BufferedReader;
@ -43,6 +41,10 @@ import java.util.Map;
import javax.inject.Inject;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
@ -255,11 +257,7 @@ public class NewFeedActivity extends AppCompatActivity {
}
}
final Map<String, Object> feedMap = new HashMap<>(2);
feedMap.put("url", feedUrl);
feedMap.put("folderId", folderId);
Feed feed = mApi.getAPI().createFeed(feedMap).execute().body().get(0);
Feed feed = mApi.getAPI().createFeed(feedUrl, folderId).execute().body().get(0);
Log.v(TAG, "New Feed-ID: " + feed.getId());
}
} catch (Exception e) {
@ -336,7 +334,7 @@ public class NewFeedActivity extends AppCompatActivity {
// perform the user login attempt.
showProgress(true);
mApi.getAPI().createFeed(urlToFeed, (int) folder.getId()).enqueue(new Callback<List<Feed>>() {
mApi.getAPI().createFeed(urlToFeed, folder.getId()).enqueue(new Callback<List<Feed>>() {
@Override
public void onResponse(Call<List<Feed>> call, final Response<List<Feed>> response) {
runOnUiThread(() -> {
@ -351,6 +349,14 @@ public class NewFeedActivity extends AppCompatActivity {
} else {
try {
String errorMessage = response.errorBody().string();
try {
//Log.e(TAG, errorMessage);
JSONObject jObjError= new JSONObject(errorMessage);
errorMessage = jObjError.getString("message");
errorMessage = truncate(errorMessage, 150);
} catch (JSONException e) {
Log.e(TAG, "Extracting error message failed: " + errorMessage, e);
}
mFeedUrlView.setError(errorMessage);
Log.e(TAG, errorMessage);
} catch (IOException e) {
@ -374,6 +380,15 @@ public class NewFeedActivity extends AppCompatActivity {
});
}
}
public static String truncate(String str, int len) {
if (str.length() > len) {
return str.substring(0, len) + "...";
} else {
return str;
}
}
private boolean isUrlValid(String url) {
try {
new URL(url);

View file

@ -58,7 +58,7 @@ public interface API {
@FormUrlEncoded
@POST("feeds")
Call<List<Feed>> createFeed(@Field("url") String url, @Field("folderId") Integer parentFolderID);
Call<List<Feed>> createFeed(@Field("url") String url, @Field("folderId") Long parentFolderID);
@PUT("feeds/{feedId}/rename")