// Path: waterfallcache/src/test/java/com/github/simonpercic/waterfallcache/ObservableTestUtils.java
// public final class ObservableTestUtils {
// 
//     private ObservableTestUtils() {
//         //no instance
//     }
// 
//     public static <T> void testObservable(Observable<T> observable, Action1<T> assertAction, boolean assertNotNull) {
//         observable = observable.subscribeOn(Schedulers.immediate());
// 
//         TestSubscriber<T> testSubscriber = new TestSubscriber<>();
//         observable.subscribe(testSubscriber);
// 
//         testSubscriber.assertNoErrors();
//         testSubscriber.assertValueCount(1);
// 
//         List<T> onNextEvents = testSubscriber.getOnNextEvents();
//         assertEquals(1, onNextEvents.size());
// 
//         T value = onNextEvents.get(0);
// 
//         if (assertNotNull) {
//             assertNotNull(value);
//         }
// 
//         assertAction.call(value);
//     }
// 
//     public static <T> void testObservable(Observable<T> observable, Action1<T> assertAction) {
//         testObservable(observable, assertAction, true);
//     }
// }
// 
// 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/main/java/com/github/simonpercic/waterfallcache/expire/LazyExpirableCache.java
// static class TimedValue<T> {
//     T value;
//     long addedOn;
// 
//     TimedValue(T value, long time) {
//         this.value = value;
//         this.addedOn = time;
//     }
// }
// 
// 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;
//     }
// }


import com.github.simonpercic.waterfallcache.ObservableTestUtils;
import com.github.simonpercic.waterfallcache.cache.RxCache;
import com.github.simonpercic.waterfallcache.expire.LazyExpirableCache.TimedValue;
import com.github.simonpercic.waterfallcache.model.SimpleObject;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.concurrent.TimeUnit;
import rx.Observable;
import static org.junit.Assert.assertEquals;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

        long currentTime = TimeUnit.HOURS.toMillis(2);
        when(simpleTimeProvider.currentTime()).thenReturn(currentTime);

        String cacheKey = "cache_key";

        when(underlyingCache.get(eq(cacheKey), any()))
                .thenReturn(Observable.just(null));

        Observable<Boolean> observable = lazyExpirableCache.contains(cacheKey);
        ObservableTestUtils.testObservable(observable, Assert::assertFalse);
    }

    @Test
    public void testRemove() throws Exception {
        RxCache underlyingCache = mock(RxCache.class);

        LazyExpirableCache lazyExpirableCache = LazyExpirableCache.fromCache(underlyingCache, 10, TimeUnit.SECONDS,
                simpleTimeProvider);

        String cacheKey = "cache_key";

        when(underlyingCache.remove(eq(cacheKey))).thenReturn(Observable.just(true));

        Observable<Boolean> observable = lazyExpirableCache.remove(cacheKey);
        ObservableTestUtils.testObservable(observable, Assert::assertTrue);

        verify(underlyingCache).remove(cacheKey);
    }

// Path: waterfallcache/src/test/java/com/github/simonpercic/waterfallcache/ObservableTestUtils.java
// public final class ObservableTestUtils {
// 
//     private ObservableTestUtils() {
//         //no instance
//     }
// 
//     public static <T> void testObservable(Observable<T> observable, Action1<T> assertAction, boolean assertNotNull) {
//         observable = observable.subscribeOn(Schedulers.immediate());
// 
//         TestSubscriber<T> testSubscriber = new TestSubscriber<>();
//         observable.subscribe(testSubscriber);
// 
//         testSubscriber.assertNoErrors();
//         testSubscriber.assertValueCount(1);
// 
//         List<T> onNextEvents = testSubscriber.getOnNextEvents();
//         assertEquals(1, onNextEvents.size());
// 
//         T value = onNextEvents.get(0);
// 
//         if (assertNotNull) {
//             assertNotNull(value);
//         }
// 
//         assertAction.call(value);
//     }
// 
//     public static <T> void testObservable(Observable<T> observable, Action1<T> assertAction) {
//         testObservable(observable, assertAction, true);
//     }
// }
// 
// 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/main/java/com/github/simonpercic/waterfallcache/expire/LazyExpirableCache.java
// static class TimedValue<T> {
//     T value;
//     long addedOn;
// 
//     TimedValue(T value, long time) {
//         this.value = value;
//         this.addedOn = time;
//     }
// }
// 
// 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/test/java/com/github/simonpercic/waterfallcache/expire/LazyExpirableCacheTest.java
import com.github.simonpercic.waterfallcache.ObservableTestUtils;
import com.github.simonpercic.waterfallcache.cache.RxCache;
import com.github.simonpercic.waterfallcache.expire.LazyExpirableCache.TimedValue;
import com.github.simonpercic.waterfallcache.model.SimpleObject;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.concurrent.TimeUnit;
import rx.Observable;
import static org.junit.Assert.assertEquals;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;


        long currentTime = TimeUnit.HOURS.toMillis(2);
        when(simpleTimeProvider.currentTime()).thenReturn(currentTime);

        String cacheKey = "cache_key";

        when(underlyingCache.get(eq(cacheKey), any()))
                .thenReturn(Observable.just(null));

        Observable<Boolean> observable = lazyExpirableCache.contains(cacheKey);
        ObservableTestUtils.testObservable(observable, Assert::assertFalse);
    }

    @Test
    public void testRemove() throws Exception {
        RxCache underlyingCache = mock(RxCache.class);

        LazyExpirableCache lazyExpirableCache = LazyExpirableCache.fromCache(underlyingCache, 10, TimeUnit.SECONDS,
                simpleTimeProvider);

        String cacheKey = "cache_key";

        when(underlyingCache.remove(eq(cacheKey))).thenReturn(Observable.just(true));

        Observable<Boolean> observable = lazyExpirableCache.remove(cacheKey);
        ObservableTestUtils.testObservable(observable, Assert::assertTrue);

        verify(underlyingCache).remove(cacheKey);
    }

    @Test