/*
* Copyright 2015 Jacek Marchwicki <jacek.marchwicki@gmail.com>
*
* 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 com.appunite.rx.operators;
import com.google.common.collect.ImmutableList;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import javax.annotation.Nonnull;
import rx.Observable;
import rx.Observer;
import rx.functions.Func1;
import rx.functions.Func2;
import rx.schedulers.Schedulers;
import rx.schedulers.TestScheduler;
import rx.subjects.PublishSubject;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.verifyZeroInteractions;
@SuppressWarnings("unchecked")
public class OperatorMergeNextTokenTest {
private PublishSubject<Object> downloadNext;
private Observable<Data> dataObservable;
private TestScheduler scheduler;
@Mock
Observer<? super Data> dataObserver;
@Spy
Downloader downloader = new Downloader();
class Downloader {
public Observable<Data> downloadData(int page) {
switch (page) {
case 0:
return Observable.just(new Data(ImmutableList.of("data1", "data2"), 1))
.delay(1, TimeUnit.SECONDS, scheduler);
case 1:
return Observable.just(new Data(ImmutableList.of("data3", "data4"), 2))
.delay(1, TimeUnit.SECONDS, scheduler);
case 2:
return Observable.just(new Data(ImmutableList.of("data5", "data6"), -1))
.delay(1, TimeUnit.SECONDS, scheduler);
default:
return Observable.<Data>error(new IOException("Wrong token"))
.delay(1, TimeUnit.SECONDS, scheduler);
}
}
}
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
scheduler = Schedulers.test();
downloadNext = PublishSubject.create();
dataObservable = downloadNext.startWith((Object) null)
.lift(OperatorMergeNextToken.create(new Data(ImmutableList.<String>of(), 0),
new Func1<Data, Observable<Data>>() {
@Override
public Observable<Data> call(final Data data) {
if (data.nextPage < 0) {
return Observable.never();
}
return Observable.just(data)
.zipWith(downloader.downloadData(data.nextPage), new Func2<Data, Data, Data>() {
@Override
public Data call(final Data data, final Data data2) {
return data.mergeWith(data2);
}
});
}
}));
}
@Test
public void testSubscribe_getData() throws Exception {
dataObservable.subscribe(dataObserver);
scheduler.advanceTimeBy(5, TimeUnit.SECONDS);
verify(dataObserver).onNext(new Data(ImmutableList.of("data1", "data2"), 1));
verifyNoMoreInteractions(dataObserver);
}
@Test
public void testLoadMore_mergeData() throws Exception {
dataObservable.subscribe(dataObserver);
scheduler.advanceTimeBy(5, TimeUnit.SECONDS);
verify(dataObserver).onNext(new Data(ImmutableList.of("data1", "data2"), 1));
reset(dataObserver);
downloadNext.onNext(null);
scheduler.advanceTimeBy(5, TimeUnit.SECONDS);
verify(dataObserver).onNext(new Data(ImmutableList.of("data1", "data2", "data3", "data4"), 2));
verifyNoMoreInteractions(dataObserver);
}
@Test
public void testAfterLastMethod_noMoreData() throws Exception {
dataObservable.subscribe(dataObserver);
downloadNext.onNext(null);
scheduler.advanceTimeBy(1, TimeUnit.SECONDS);
downloadNext.onNext(null);
scheduler.advanceTimeBy(1, TimeUnit.SECONDS);
downloadNext.onNext(null);
scheduler.advanceTimeBy(1, TimeUnit.SECONDS);
downloadNext.onNext(null);
scheduler.advanceTimeBy(1, TimeUnit.SECONDS);
reset(dataObserver);
downloadNext.onNext(null);
scheduler.advanceTimeBy(5, TimeUnit.SECONDS);
verifyZeroInteractions(dataObserver);
}
@Test
public void testWhileDownloading_onNextDoNothing() throws Exception {
dataObservable.subscribe(dataObserver);
downloadNext.onNext(null);
scheduler.advanceTimeBy(10, TimeUnit.SECONDS);
verify(downloader).downloadData(0);
verify(dataObserver).onNext(new Data(ImmutableList.of("data1", "data2"), 1));
verifyNoMoreInteractions(dataObserver);
}
@Test
public void testAfterManyRequests_errorNotThrown() throws Exception {
dataObservable.subscribe(dataObserver);
downloadNext.onNext(null);
downloadNext.onNext(null);
downloadNext.onNext(null);
downloadNext.onNext(null);
verify(dataObserver, never()).onError(any(Throwable.class));
}
@Test
public void testAfterError_propagateError() throws Exception {
final IOException error = new IOException("error");
dataObservable.subscribe(dataObserver);
downloadNext.onError(error);
verify(dataObserver).onError(error);
}
private static class Data {
@Nonnull
private final ImmutableList<String> data;
private final int nextPage;
Data(@Nonnull final ImmutableList<String> data, final int nextPage) {
this.data = data;
this.nextPage = nextPage;
}
@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (!(o instanceof Data)) return false;
final Data data1 = (Data) o;
return nextPage == data1.nextPage && data.equals(data1.data);
}
public Data mergeWith(Data newData) {
final ImmutableList<String> data = ImmutableList.<String>builder()
.addAll(this.data)
.addAll(newData.data)
.build();
return new Data(data,
newData.nextPage);
}
@Override
public int hashCode() {
int result = data.hashCode();
result = 31 * result + nextPage;
return result;
}
@Override
public String toString() {
return "Data{" +
"data=" + data +
", nextPage=" + nextPage +
'}';
}
}
}