Skip to content

Commit 94f6cc4

Browse files
committed
add test and guava as test dep, check for null evictingMapFactory
1 parent c81b6b0 commit 94f6cc4

File tree

3 files changed

+52
-3
lines changed

3 files changed

+52
-3
lines changed

build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ apply plugin: 'nebula.rxjava-project'
1313
dependencies {
1414
testCompile 'junit:junit:4.12'
1515
testCompile 'org.mockito:mockito-core:1.10.19'
16+
testCompile 'com.google.guava:guava:19.0'
1617

1718
perfCompile 'org.openjdk.jmh:jmh-core:1.11.3'
1819
perfCompile 'org.openjdk.jmh:jmh-generator-annprocess:1.11.3'

src/main/java/rx/Observable.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6385,8 +6385,7 @@ public final <K, R> Observable<GroupedObservable<K, R>> groupBy(final Func1<? su
63856385
* items to the appropriate {@code GroupedObservable}s. The {@code Map} instance must be thread-safe
63866386
* and any eviction must trigger a call to the supplied action (synchronously or asynchronously).
63876387
* This can be used to limit the size of the map by evicting keys by maximum size or access time for
6388-
* instance. If {@code evictingMapFactory} is null then no eviction strategy will be applied (and a suitable default thread-safe
6389-
* implementation of {@code Map} will be supplied). Here's an example using Guava's {@code CacheBuilder} from v19.0:
6388+
* instance. Here's an example using Guava's {@code CacheBuilder} from v19.0:
63906389
* <pre>
63916390
* {@code
63926391
* Func1<Action1<K>, Map<K, Object>> mapFactory
@@ -6405,10 +6404,16 @@ public final <K, R> Observable<GroupedObservable<K, R>> groupBy(final Func1<? su
64056404
* @return an {@code Observable} that emits {@link GroupedObservable}s, each of which corresponds to a
64066405
* unique key value and each of which emits those items from the source Observable that share that
64076406
* key value
6407+
* @throws NullPointerException
6408+
* if {@code evictingMapFactory} is null
64086409
* @see <a href="http://reactivex.io/documentation/operators/groupby.html">ReactiveX operators documentation: GroupBy</a>
64096410
*/
64106411
@Experimental
6411-
public final <K, R> Observable<GroupedObservable<K, R>> groupBy(final Func1<? super T, ? extends K> keySelector, final Func1<? super T, ? extends R> elementSelector, final Func1<Action1<K>, Map<K, Object>> evictingMapFactory) {
6412+
public final <K, R> Observable<GroupedObservable<K, R>> groupBy(final Func1<? super T, ? extends K> keySelector,
6413+
final Func1<? super T, ? extends R> elementSelector, final Func1<Action1<K>, Map<K, Object>> evictingMapFactory) {
6414+
if (evictingMapFactory == null) {
6415+
throw new NullPointerException("evictingMapFactory cannot be null");
6416+
}
64126417
return lift(new OperatorGroupBy<T, K, R>(keySelector, elementSelector, evictingMapFactory));
64136418
}
64146419

src/test/java/rx/internal/operators/OperatorGroupByTest.java

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import rx.Observable;
3131
import rx.Observable.OnSubscribe;
3232
import rx.Observer;
33+
import rx.exceptions.Exceptions;
3334
import rx.exceptions.TestException;
3435
import rx.functions.*;
3536
import rx.internal.util.*;
@@ -1865,4 +1866,46 @@ public String call(Integer x) {
18651866
}).toList().toBlocking().single();
18661867
assertEquals(expected, ts.getOnNextEvents());
18671868
}
1869+
1870+
private static final Func1<Integer, Integer> EVICTING_MAP_KEY_SELECTOR = new Func1<Integer, Integer> (){
1871+
@Override
1872+
public Integer call(Integer t) {
1873+
return t /10;
1874+
}};
1875+
1876+
@Test
1877+
public void testEvictingMapFactoryIfMapPutThrowsThen() {
1878+
Func1<Integer, Integer> elementSelector = UtilityFunctions.identity();
1879+
Exceptions.throwIfFatal(null);
1880+
final RuntimeException exception = new RuntimeException("boo");
1881+
//normally we would use Guava CacheBuilder for instance but to save a test dependency
1882+
//we make something custom
1883+
Func1<Action1<Integer>, Map<Integer, Object>> mapFactory = new Func1<Action1<Integer>, Map<Integer, Object>>() {
1884+
@SuppressWarnings("serial")
1885+
@Override
1886+
public Map<Integer, Object> call(final Action1<Integer> evicted) {
1887+
// is a bit risky to override the put method because
1888+
// of possible side-effects (e.g. remove could call put and we did not know it)
1889+
// to fix just need to use composition but needs a verbose implementation of Map
1890+
// interface
1891+
return new ConcurrentHashMap<Integer,Object>() {
1892+
1893+
@Override
1894+
public Object put(Integer key, Object value) {
1895+
throw exception;
1896+
}};
1897+
}};
1898+
TestSubscriber<Object> ts = TestSubscriber.create();
1899+
Observable
1900+
.range(1, 100)
1901+
.groupBy(EVICTING_MAP_KEY_SELECTOR, elementSelector, mapFactory)
1902+
.flatMap(UtilityFunctions.<Observable<Integer>>identity())
1903+
.subscribe(ts);
1904+
ts.assertError(exception);
1905+
}
1906+
1907+
@Test
1908+
public void mapFactoryEvictionWorksWithGuavaCache() {
1909+
1910+
}
18681911
}

0 commit comments

Comments
 (0)