// Path: waterfallcache/src/main/java/com/github/simonpercic/waterfallcache/cache/RxCache.java
// public interface RxCache {
// 
//     /**
//      * Get from cache.
//      *
//      * @param key key
//      * @param typeOfT type of cache value
//      * @param <T> T of cache value
//      * @return Observable that emits the cache value
//      */
//     <T> Observable<T> get(String key, Type typeOfT);
// 
//     /**
//      * Put value to cache.
//      *
//      * @param key key
//      * @param object value
//      * @return Observable that emits <tt>true</tt> if successful, <tt>false</tt> otherwise
//      */
//     Observable<Boolean> put(String key, Object object);
// 
//     /**
//      * Cache contains key.
//      *
//      * @param key key
//      * @return Observable that emits <tt>true</tt> if cache contains key, <tt>false</tt> otherwise
//      */
//     Observable<Boolean> contains(String key);
// 
//     /**
//      * Remove cache value.
//      *
//      * @param key key
//      * @return Observable that emits <tt>true</tt> if successful, <tt>false</tt> otherwise
//      */
//     Observable<Boolean> remove(String key);
// 
//     /**
//      * Clear all cache values.
//      *
//      * @return Observable that emits <tt>true</tt> if successful, <tt>false</tt> otherwise
//      */
//     Observable<Boolean> clear();
// }
// 
// Path: waterfallcache/src/androidTest/java/com/github/simonpercic/waterfallcache/model/GenericObject.java
// public class GenericObject<T> {
// 
//     private T object;
// 
//     private String value;
// 
//     public T getObject() {
//         return object;
//     }
// 
//     public String getValue() {
//         return value;
//     }
// 
//     public void setObject(T object) {
//         this.object = object;
//     }
// 
//     public void setValue(String value) {
//         this.value = value;
//     }
// }
// 
// Path: waterfallcache/src/test/java/com/github/simonpercic/waterfallcache/model/SimpleObject.java
// public class SimpleObject {
// 
//     private String value;
// 
//     public SimpleObject(String value) {
//         this.value = value;
//     }
// 
//     public String getValue() {
//         return value;
//     }
// }
// 
// Path: waterfallcache/src/androidTest/java/com/github/simonpercic/waterfallcache/model/WrappedObject.java
// public class WrappedObject {
// 
//     private SimpleObject object;
// 
//     private String value;
// 
//     public SimpleObject getObject() {
//         return object;
//     }
// 
//     public String getValue() {
//         return value;
//     }
// 
//     public void setObject(SimpleObject object) {
//         this.object = object;
//     }
// 
//     public void setValue(String value) {
//         this.value = value;
//     }
// }


import android.content.Context;
import android.support.test.InstrumentationRegistry;
import com.github.simonpercic.waterfallcache.cache.RxCache;
import com.github.simonpercic.waterfallcache.model.GenericObject;
import com.github.simonpercic.waterfallcache.model.SimpleObject;
import com.github.simonpercic.waterfallcache.model.WrappedObject;
import com.google.gson.reflect.TypeToken;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import java.lang.reflect.Type;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import rx.Observable;
import rx.functions.Action1;
import rx.schedulers.Schedulers;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
        genericObject.setObject(wrappedObject);

        Type type = new TypeToken<GenericObject<WrappedObject>>() {
        }.getType();

        when(mockCache.get(eq(key), eq(type))).thenReturn(Observable.just(genericObject));

        Observable<GenericObject<WrappedObject>> getObservable = waterfallCache.get(key, type);
        testObservable(getObservable, result -> {
            assertEquals(genericValue, result.getValue());
            assertEquals(wrappedValue, result.getObject().getValue());
            assertEquals(simpleValue, result.getObject().getObject().getValue());
        });

        getObservable = waterfallCache.get(key, type);
        testObservable(getObservable, result -> {
            assertEquals(genericValue, result.getValue());
            assertEquals(wrappedValue, result.getObject().getValue());
            assertEquals(simpleValue, result.getObject().getObject().getValue());
        });

        verify(mockCache).get(eq(key), eq(type));
    }

    private <T> void testObservable(Observable<T> observable, Action1<T> assertAction) throws InterruptedException {
        CountDownLatch countDownLatch = new CountDownLatch(1);

        observable.subscribeOn(Schedulers.immediate())
                .subscribe((value) -> {
                    if (assertAction != null) {
// Path: waterfallcache/src/main/java/com/github/simonpercic/waterfallcache/cache/RxCache.java
// public interface RxCache {
// 
//     /**
//      * Get from cache.
//      *
//      * @param key key
//      * @param typeOfT type of cache value
//      * @param <T> T of cache value
//      * @return Observable that emits the cache value
//      */
//     <T> Observable<T> get(String key, Type typeOfT);
// 
//     /**
//      * Put value to cache.
//      *
//      * @param key key
//      * @param object value
//      * @return Observable that emits <tt>true</tt> if successful, <tt>false</tt> otherwise
//      */
//     Observable<Boolean> put(String key, Object object);
// 
//     /**
//      * Cache contains key.
//      *
//      * @param key key
//      * @return Observable that emits <tt>true</tt> if cache contains key, <tt>false</tt> otherwise
//      */
//     Observable<Boolean> contains(String key);
// 
//     /**
//      * Remove cache value.
//      *
//      * @param key key
//      * @return Observable that emits <tt>true</tt> if successful, <tt>false</tt> otherwise
//      */
//     Observable<Boolean> remove(String key);
// 
//     /**
//      * Clear all cache values.
//      *
//      * @return Observable that emits <tt>true</tt> if successful, <tt>false</tt> otherwise
//      */
//     Observable<Boolean> clear();
// }
// 
// Path: waterfallcache/src/androidTest/java/com/github/simonpercic/waterfallcache/model/GenericObject.java
// public class GenericObject<T> {
// 
//     private T object;
// 
//     private String value;
// 
//     public T getObject() {
//         return object;
//     }
// 
//     public String getValue() {
//         return value;
//     }
// 
//     public void setObject(T object) {
//         this.object = object;
//     }
// 
//     public void setValue(String value) {
//         this.value = value;
//     }
// }
// 
// Path: waterfallcache/src/test/java/com/github/simonpercic/waterfallcache/model/SimpleObject.java
// public class SimpleObject {
// 
//     private String value;
// 
//     public SimpleObject(String value) {
//         this.value = value;
//     }
// 
//     public String getValue() {
//         return value;
//     }
// }
// 
// Path: waterfallcache/src/androidTest/java/com/github/simonpercic/waterfallcache/model/WrappedObject.java
// public class WrappedObject {
// 
//     private SimpleObject object;
// 
//     private String value;
// 
//     public SimpleObject getObject() {
//         return object;
//     }
// 
//     public String getValue() {
//         return value;
//     }
// 
//     public void setObject(SimpleObject object) {
//         this.object = object;
//     }
// 
//     public void setValue(String value) {
//         this.value = value;
//     }
// }


// Path: waterfallcache/src/androidTest/java/com/github/simonpercic/waterfallcache/WaterfallCacheTest.java
import android.content.Context;
import android.support.test.InstrumentationRegistry;
import com.github.simonpercic.waterfallcache.cache.RxCache;
import com.github.simonpercic.waterfallcache.model.GenericObject;
import com.github.simonpercic.waterfallcache.model.SimpleObject;
import com.github.simonpercic.waterfallcache.model.WrappedObject;
import com.google.gson.reflect.TypeToken;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import java.lang.reflect.Type;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import rx.Observable;
import rx.functions.Action1;
import rx.schedulers.Schedulers;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

        genericObject.setObject(wrappedObject);

        Type type = new TypeToken<GenericObject<WrappedObject>>() {
        }.getType();

        when(mockCache.get(eq(key), eq(type))).thenReturn(Observable.just(genericObject));

        Observable<GenericObject<WrappedObject>> getObservable = waterfallCache.get(key, type);
        testObservable(getObservable, result -> {
            assertEquals(genericValue, result.getValue());
            assertEquals(wrappedValue, result.getObject().getValue());
            assertEquals(simpleValue, result.getObject().getObject().getValue());
        });

        getObservable = waterfallCache.get(key, type);
        testObservable(getObservable, result -> {
            assertEquals(genericValue, result.getValue());
            assertEquals(wrappedValue, result.getObject().getValue());
            assertEquals(simpleValue, result.getObject().getObject().getValue());
        });

        verify(mockCache).get(eq(key), eq(type));
    }

    private <T> void testObservable(Observable<T> observable, Action1<T> assertAction) throws InterruptedException {
        CountDownLatch countDownLatch = new CountDownLatch(1);

        observable.subscribeOn(Schedulers.immediate())
                .subscribe((value) -> {
                    if (assertAction != null) {
                        assertAction.call(value);