Skip to content

Commit

Permalink
Add tests and refactor stream.
Browse files Browse the repository at this point in the history
  • Loading branch information
arunkumar9t2 committed Jun 3, 2018
1 parent 7e1a5bc commit 64d7de9
Show file tree
Hide file tree
Showing 6 changed files with 179 additions and 38 deletions.
3 changes: 1 addition & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
buildscript {
repositories {
jcenter()
maven { url 'https://maven.google.com' }
google()
}
dependencies {
Expand All @@ -29,7 +28,7 @@ buildscript {
allprojects {
repositories {
jcenter()
maven { url 'https://maven.google.com' }
google()
}
}

Expand Down
10 changes: 6 additions & 4 deletions suggestions-core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ android {

}
buildTypes {
debug {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
Expand Down Expand Up @@ -58,10 +62,8 @@ dependencies {
implementation 'com.android.support:support-core-utils:27.1.1'
// RxJava
implementation "io.reactivex.rxjava2:rxjava:2.1.13"
compile 'io.reactivex.rxjava2:rxandroid:2.0.2'
implementation 'io.reactivex.rxjava2:rxandroid:2.0.2'

androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
testImplementation 'junit:junit:4.12'
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Copyright 2018 Arunkumar
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package in.arunkumarsampath.suggestions2;

import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.support.test.InstrumentationRegistry;

public class Util {
public static boolean isOnline() {
final ConnectivityManager manager = (ConnectivityManager) InstrumentationRegistry.getContext()
.getSystemService(Context.CONNECTIVITY_SERVICE);
final NetworkInfo networkInfo = manager.getActiveNetworkInfo();
boolean isAvailable = false;
if (networkInfo != null && networkInfo.isConnected()) {
isAvailable = true;
}
return isAvailable;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
* Copyright 2018 Arunkumar
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package in.arunkumarsampath.suggestions2.source.google;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.io.FileNotFoundException;

public class GoogleSuggestionSourceTest {

private static final String TAG = GoogleSuggestionSourceTest.class.getSimpleName();

private GoogleSuggestionSource suggestionSource;

@Before
public void setUp() {
suggestionSource = new GoogleSuggestionSource();
}

@After
public void tearDown() {
suggestionSource = null;
}

@Test
public void getSuggestions() {
suggestionSource.getSuggestions("Hello")
.test(1)
.assertSubscribed()
.assertComplete()
.assertNoErrors();
}

@Test
public void getSuggestionsFailure() {
suggestionSource.getSuggestions("")
.test()
.assertError(FileNotFoundException.class)
.assertNotComplete()
.assertNoValues();
}


@Test
public void getSuggestions_ValuesNotEmpty() {
suggestionSource.getSuggestions("Hello")
.test()
.assertComplete();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import in.arunkumarsampath.suggestions2.util.Util;
import io.reactivex.BackpressureStrategy;
import io.reactivex.Flowable;
import io.reactivex.FlowableEmitter;

import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
import static org.xmlpull.v1.XmlPullParser.START_TAG;
Expand All @@ -47,42 +48,48 @@ public class GoogleSuggestionSource implements SuggestionSource {
@NonNull
@Override
public Flowable<SuggestionItem> getSuggestions(@NonNull String value) {
return Flowable.just(value)
.map(Util::prepareSearchTerm)
.flatMap(searchTerm -> Flowable.create(emitter -> {
HttpURLConnection httpURLConnection = null;
return Flowable.fromCallable(() -> {
HttpURLConnection httpURLConnection;
final String suggestUrl = String.format(SUGGEST_URL_FORMAT, Util.prepareSearchTerm(value));
final URL url = new URL(suggestUrl);
httpURLConnection = (HttpURLConnection) url.openConnection();
httpURLConnection.connect();
return httpURLConnection;
}).flatMap(httpURLConnection -> Flowable.create(emitter -> {
try (final InputStream inputStream = httpURLConnection.getInputStream()) {
final XmlPullParser xmlParser = XmlPullParserFactory.newInstance().newPullParser();
xmlParser.setInput(inputStream, Util.extractEncoding(httpURLConnection.getContentType()));

final String suggestUrl = String.format(SUGGEST_URL_FORMAT, searchTerm);
try {
final URL url = new URL(suggestUrl);
httpURLConnection = (HttpURLConnection) url.openConnection();
httpURLConnection.connect();
int eventType = xmlParser.getEventType();

try (final InputStream inputStream = httpURLConnection.getInputStream()) {
final XmlPullParser xmlParser = XmlPullParserFactory.newInstance().newPullParser();
xmlParser.setInput(inputStream, "");

int eventType = xmlParser.getEventType();

// Perform back pressure aware iteration.
while (eventType != END_DOCUMENT && !emitter.isCancelled() && emitter.requested() >= 0) {
boolean validEvent = eventType == START_TAG && xmlParser.getName().equalsIgnoreCase(SUGGESTION);
if (validEvent) {
final String suggestion = xmlParser.getAttributeValue(0);
emitter.onNext(new StringSuggestionItem(suggestion));
}
eventType = xmlParser.next();
}
emitter.onComplete();
}
} finally {
cancel(httpURLConnection);
while (isFlowableEmissionValid(emitter, eventType)) { // Perform back pressure aware iteration.
boolean validEvent = eventType == START_TAG && xmlParser.getName().equalsIgnoreCase(SUGGESTION);
if (validEvent) {
final String suggestion = xmlParser.getAttributeValue(0);
emitter.onNext(new StringSuggestionItem(suggestion));
}
eventType = xmlParser.next();
}
emitter.onComplete();
} finally {
cancel(httpURLConnection);
}
emitter.setCancellable(() -> cancel(httpURLConnection));
}, BackpressureStrategy.LATEST));
}

// Cleanup resources on finish
HttpURLConnection httpUrlConnCopy = httpURLConnection;
emitter.setCancellable(() -> cancel(httpUrlConnCopy));
}, BackpressureStrategy.LATEST));
/**
* Method to check if XML iteration can be still performed.
* <p>
* Checks if {@link Flowable} is still valid, if downstream is actively requesting further elements,
* and we have not reached XML document end.
*
* @param emitter The emitter to currently handling events.
* @param eventType XML document event type.
* @return {@code true} if Flowable emission is valid.
*/
private boolean isFlowableEmissionValid(FlowableEmitter<SuggestionItem> emitter, int eventType) {
return eventType != END_DOCUMENT && !emitter.isCancelled() && emitter.requested() > 0;
}

private void cancel(@Nullable HttpURLConnection httpURLConnection) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,46 @@
package in.arunkumarsampath.suggestions2.util;

import android.support.annotation.NonNull;
import android.support.annotation.Nullable;

public class Util {

private Util() {
}

private final static String UTF8 = "UTF-8";
private final static String ISO = "ISO-8859-1";

@NonNull
public static String prepareSearchTerm(@NonNull String searchTerm) {
return searchTerm.trim().replace(" ", "+");
}


/**
* Tries to extract type of encoding for the given content type.
*
* @param contentType Content type gotten from {@link java.net.HttpURLConnection#getContentType()}
* @return Extracted encoding.
*/
@NonNull
public static String extractEncoding(@Nullable String contentType) {
final String[] values;
if (contentType != null) {
values = contentType.split(";");
} else {
values = new String[0];
}
String charset = "";

for (String value : values) {
value = value.trim().toLowerCase();
if (value.startsWith("charset="))
charset = value.substring("charset=".length());
}
// http1.1 says ISO-8859-1 is the default charset
if (charset.length() == 0)
charset = ISO;
return charset;
}
}

0 comments on commit 64d7de9

Please sign in to comment.