@Test
    public void
        shouldThrowExceptionIfNoTypeMappingWasFoundWithTransactionWithoutAffectingDbBlocking() {
      final StorIOSQLite storIOSQLite = mock(StorIOSQLite.class);
      final StorIOSQLite.Internal internal = mock(StorIOSQLite.Internal.class);

      when(storIOSQLite.lowLevel()).thenReturn(internal);

      when(storIOSQLite.put()).thenReturn(new PreparedPut.Builder(storIOSQLite));

      final List<TestItem> items = asList(TestItem.newInstance(), TestItem.newInstance());

      final PreparedPut<PutResults<TestItem>> preparedPut =
          storIOSQLite.put().objects(items).useTransaction(true).prepare();

      try {
        preparedPut.executeAsBlocking();
        failBecauseExceptionWasNotThrown(StorIOException.class);
      } catch (StorIOException expected) {
        // it's okay, no type mapping was found
        assertThat(expected).hasCauseInstanceOf(IllegalStateException.class);
      }

      verify(storIOSQLite).put();
      verify(storIOSQLite).lowLevel();
      verify(internal).typeMapping(TestItem.class);
      verify(internal, never()).insert(any(InsertQuery.class), any(ContentValues.class));
      verify(internal, never()).update(any(UpdateQuery.class), any(ContentValues.class));
      verifyNoMoreInteractions(storIOSQLite, internal);
    }
    @Test
    public void shouldFinishTransactionIfExceptionHasOccurredBlocking() {
      final StorIOSQLite storIOSQLite = mock(StorIOSQLite.class);
      final StorIOSQLite.Internal internal = mock(StorIOSQLite.Internal.class);

      when(storIOSQLite.internal()).thenReturn(internal);

      //noinspection unchecked
      final DeleteResolver<Object> deleteResolver = mock(DeleteResolver.class);

      when(deleteResolver.performDelete(same(storIOSQLite), anyObject()))
          .thenThrow(new IllegalStateException("test exception"));

      try {
        new PreparedDeleteCollectionOfObjects.Builder<Object>(
                storIOSQLite, singletonList(new Object()))
            .useTransaction(true)
            .withDeleteResolver(deleteResolver)
            .prepare()
            .executeAsBlocking();

        failBecauseExceptionWasNotThrown(StorIOException.class);
      } catch (StorIOException expected) {
        IllegalStateException cause = (IllegalStateException) expected.getCause();
        assertThat(cause).hasMessage("test exception");

        verify(internal).beginTransaction();
        verify(internal, never()).setTransactionSuccessful();
        verify(internal).endTransaction();

        verify(storIOSQLite).internal();
        verify(deleteResolver).performDelete(same(storIOSQLite), anyObject());
        verifyNoMoreInteractions(storIOSQLite, internal, deleteResolver);
      }
    }
  @NonNull
  @Override
  public Person mapFromCursor(@NonNull Cursor cursor) {
    final StorIOSQLite storIOSQLite = storIOSQLiteFromPerformGet.get();

    // BTW, you don't need a transaction here
    // StorIO will wrap mapFromCursor() into the transaction if needed

    final long personId = cursor.getLong(cursor.getColumnIndexOrThrow(PersonsTable.COLUMN_ID));
    final String personUuid =
        cursor.getString(cursor.getColumnIndexOrThrow(PersonsTable.COLUMN_UUID));
    final String personName =
        cursor.getString(cursor.getColumnIndexOrThrow(PersonsTable.COLUMN_NAME));

    final List<Car> personCars =
        storIOSQLite
            .get()
            .listOfObjects(Car.class)
            .withQuery(
                Query.builder()
                    .table(CarsTable.TABLE_NAME)
                    //                            .where(CarsTable.COLUMN_PERSON_ID + "=?")
                    //                            .whereArgs(personId)
                    .where(CarsTable.COLUMN_PERSON_UUID + "=?")
                    .whereArgs(personUuid)
                    .build())
            .prepare()
            .executeAsBlocking();

    return new Person.Builder(personName).uuid(personUuid).cars(personCars).build();
  }
    @Test
    public void
        shouldThrowExceptionIfNoTypeMappingWasFoundWithTransactionWithoutAffectingDbAsObservable() {
      final StorIOSQLite storIOSQLite = mock(StorIOSQLite.class);
      final StorIOSQLite.Internal internal = mock(StorIOSQLite.Internal.class);

      when(storIOSQLite.internal()).thenReturn(internal);

      when(storIOSQLite.delete()).thenReturn(new PreparedDelete.Builder(storIOSQLite));

      final List<TestItem> items = asList(TestItem.newInstance(), TestItem.newInstance());

      final TestSubscriber<DeleteResults<TestItem>> testSubscriber =
          new TestSubscriber<DeleteResults<TestItem>>();

      storIOSQLite
          .delete()
          .objects(items)
          .useTransaction(true)
          .prepare()
          .createObservable()
          .subscribe(testSubscriber);

      testSubscriber.awaitTerminalEvent();
      testSubscriber.assertNoValues();
      assertThat(testSubscriber.getOnErrorEvents().get(0))
          .isInstanceOf(StorIOException.class)
          .hasCauseInstanceOf(IllegalStateException.class);

      verify(storIOSQLite).delete();
      verify(storIOSQLite).internal();
      verify(internal).typeMapping(TestItem.class);
      verify(internal, never()).delete(any(DeleteQuery.class));
      verifyNoMoreInteractions(storIOSQLite, internal);
    }
Example #5
0
  private GetObjectStub(boolean withTypeMapping) {
    this.withTypeMapping = withTypeMapping;

    storIOSQLite = mock(StorIOSQLite.class);
    internal = mock(StorIOSQLite.Internal.class);

    when(storIOSQLite.internal()).thenReturn(internal);

    query = Query.builder().table("test_table").build();

    rawQuery =
        RawQuery.builder().query("select * from who_cares").observesTables("test_table").build();

    //noinspection unchecked
    getResolver = mock(GetResolver.class);
    cursor = mock(Cursor.class);

    item = new TestItem();

    when(cursor.getCount()).thenReturn(1);

    when(cursor.moveToNext())
        .thenAnswer(
            new Answer<Boolean>() {
              int invocationsCount = 0;

              @Override
              public Boolean answer(InvocationOnMock invocation) throws Throwable {
                return invocationsCount++ < 1;
              }
            });

    when(storIOSQLite.get()).thenReturn(new PreparedGet.Builder(storIOSQLite));

    when(storIOSQLite.observeChangesInTables(eq(singleton(query.table()))))
        .thenReturn(Observable.<Changes>empty());

    assertThat(rawQuery.observesTables()).isNotNull();

    when(storIOSQLite.observeChangesInTables(rawQuery.observesTables()))
        .thenReturn(Observable.<Changes>empty());

    when(getResolver.performGet(storIOSQLite, query)).thenReturn(cursor);

    when(getResolver.performGet(storIOSQLite, rawQuery)).thenReturn(cursor);

    when(getResolver.mapFromCursor(cursor)).thenReturn(item);

    //noinspection unchecked
    typeMapping = mock(SQLiteTypeMapping.class);

    if (withTypeMapping) {
      when(internal.typeMapping(TestItem.class)).thenReturn(typeMapping);
      when(typeMapping.getResolver()).thenReturn(getResolver);
    }
  }
    @Test
    public void verifyBehaviorInCaseOfExceptionWithoutTransactionCompletable() {
      final StorIOSQLite storIOSQLite = mock(StorIOSQLite.class);
      final StorIOSQLite.Internal internal = mock(StorIOSQLite.Internal.class);

      when(storIOSQLite.lowLevel()).thenReturn(internal);

      //noinspection unchecked
      final PutResolver<Object> putResolver = mock(PutResolver.class);

      when(putResolver.performPut(same(storIOSQLite), anyObject()))
          .thenThrow(new IllegalStateException("test exception"));

      final List<Object> objects = singletonList(new Object());

      final TestSubscriber<PutResults<Object>> testSubscriber =
          new TestSubscriber<PutResults<Object>>();

      new PreparedPutCollectionOfObjects.Builder<Object>(storIOSQLite, objects)
          .useTransaction(false)
          .withPutResolver(putResolver)
          .prepare()
          .asRxCompletable()
          .subscribe(testSubscriber);

      testSubscriber.awaitTerminalEvent();
      testSubscriber.assertNoValues();
      testSubscriber.assertError(StorIOException.class);

      //noinspection ThrowableResultOfMethodCallIgnored
      StorIOException expected = (StorIOException) testSubscriber.getOnErrorEvents().get(0);

      IllegalStateException cause = (IllegalStateException) expected.getCause();
      assertThat(cause).hasMessage("test exception");

      // Main checks of this test
      verify(internal, never()).beginTransaction();
      verify(internal, never()).setTransactionSuccessful();
      verify(internal, never()).endTransaction();

      verify(storIOSQLite).lowLevel();
      verify(storIOSQLite).defaultScheduler();
      verify(putResolver).performPut(same(storIOSQLite), anyObject());
      verifyNoMoreInteractions(storIOSQLite, internal, putResolver);
    }
Example #7
0
  void reloadData() {
    uiStateController.setUiStateLoading();

    final Subscription subscription =
        storIOSQLite
            .get()
            .listOfObjects(Tweet.class)
            .withQuery(TweetsTable.QUERY_ALL)
            .prepare()
            .createObservable() // it will be subscribed to changes in tweets table!
            .delay(
                1,
                SECONDS) // for better User Experience :) Actually, StorIO is so fast that we need
                         // to delay emissions (it's a joke, or not)
            .observeOn(mainThread())
            .subscribe(
                new Action1<List<Tweet>>() {
                  @Override
                  public void call(List<Tweet> tweets) {
                    // Remember: subscriber will automatically receive updates
                    // Of tables from Query (tweets table in our case)
                    // This makes your code really Reactive and nice!

                    // We guarantee, that list of objects will never be null (also we use
                    // @NonNull/@Nullable)
                    // So you just need to check if it's empty or not
                    if (tweets.isEmpty()) {
                      uiStateController.setUiStateEmpty();
                      tweetsAdapter.setTweets(null);
                    } else {
                      uiStateController.setUiStateContent();
                      tweetsAdapter.setTweets(tweets);
                    }
                  }
                },
                new Action1<Throwable>() {
                  @Override
                  public void call(Throwable throwable) {
                    // In cases when you are not sure that query will be successful
                    // You can prevent crash of the application via error handler
                    Timber.e(throwable, "reloadData()");
                    uiStateController.setUiStateError();
                    tweetsAdapter.setTweets(null);
                  }
                });

    // Preventing memory leak (other Observables: Put, Delete emit result once so memory leak won't
    // live long)
    // Because rx.Observable from Get Operation is endless (it watches for changes of tables from
    // query)
    // You can easily create memory leak (in this case you'll leak the Fragment and all it's fields)
    // So please, PLEASE manage your subscriptions
    // We suggest same mechanism via storing all subscriptions that you want to unsubscribe
    // In something like CompositeSubscription and unsubscribe them in appropriate moment of
    // component lifecycle
    unsubscribeOnStop(subscription);
  }
  @Test
  public void shouldFinishTransactionIfExceptionHasOccurredSingle() {
    final StorIOSQLite storIOSQLite = mock(StorIOSQLite.class);
    final StorIOSQLite.Internal internal = mock(StorIOSQLite.Internal.class);

    when(storIOSQLite.internal()).thenReturn(internal);

    //noinspection unchecked
    final PutResolver<ContentValues> putResolver = mock(PutResolver.class);

    final List<ContentValues> contentValues = singletonList(mock(ContentValues.class));

    when(putResolver.performPut(same(storIOSQLite), any(ContentValues.class)))
        .thenThrow(new IllegalStateException("test exception"));

    final TestSubscriber<PutResults<ContentValues>> testSubscriber =
        new TestSubscriber<PutResults<ContentValues>>();

    new PreparedPutContentValuesIterable.Builder(storIOSQLite, contentValues)
        .withPutResolver(putResolver)
        .useTransaction(true)
        .prepare()
        .asRxSingle()
        .subscribe(testSubscriber);

    testSubscriber.awaitTerminalEvent();
    testSubscriber.assertNoValues();
    testSubscriber.assertError(StorIOException.class);

    //noinspection ThrowableResultOfMethodCallIgnored
    StorIOException expected = (StorIOException) testSubscriber.getOnErrorEvents().get(0);

    IllegalStateException cause = (IllegalStateException) expected.getCause();
    assertThat(cause).hasMessage("test exception");

    verify(internal).beginTransaction();
    verify(internal, never()).setTransactionSuccessful();
    verify(internal).endTransaction();

    verify(storIOSQLite).internal();
    verify(putResolver).performPut(same(storIOSQLite), any(ContentValues.class));
    verifyNoMoreInteractions(storIOSQLite, internal, putResolver);
  }
    @Test
    public void shouldFinishTransactionIfExceptionHasOccurredObservable() {
      final StorIOSQLite storIOSQLite = mock(StorIOSQLite.class);
      final StorIOSQLite.Internal internal = mock(StorIOSQLite.Internal.class);

      when(storIOSQLite.internal()).thenReturn(internal);

      //noinspection unchecked
      final DeleteResolver<Object> deleteResolver = mock(DeleteResolver.class);

      when(deleteResolver.performDelete(same(storIOSQLite), anyObject()))
          .thenThrow(new IllegalStateException("test exception"));

      final TestSubscriber<DeleteResults<Object>> testSubscriber =
          new TestSubscriber<DeleteResults<Object>>();

      new PreparedDeleteCollectionOfObjects.Builder<Object>(
              storIOSQLite, singletonList(new Object()))
          .useTransaction(true)
          .withDeleteResolver(deleteResolver)
          .prepare()
          .createObservable()
          .subscribe(testSubscriber);

      testSubscriber.awaitTerminalEvent();
      testSubscriber.assertNoValues();
      testSubscriber.assertError(StorIOException.class);

      //noinspection ThrowableResultOfMethodCallIgnored
      StorIOException expected = (StorIOException) testSubscriber.getOnErrorEvents().get(0);

      IllegalStateException cause = (IllegalStateException) expected.getCause();
      assertThat(cause).hasMessage("test exception");

      verify(internal).beginTransaction();
      verify(internal, never()).setTransactionSuccessful();
      verify(internal).endTransaction();

      verify(storIOSQLite).internal();
      verify(deleteResolver).performDelete(same(storIOSQLite), anyObject());
      verifyNoMoreInteractions(storIOSQLite, internal, deleteResolver);
    }
    @Test
    public void verifyBehaviorInCaseOfExceptionWithoutTransactionBlocking() {
      final StorIOSQLite storIOSQLite = mock(StorIOSQLite.class);
      final StorIOSQLite.Internal internal = mock(StorIOSQLite.Internal.class);

      when(storIOSQLite.lowLevel()).thenReturn(internal);

      //noinspection unchecked
      final PutResolver<Object> putResolver = mock(PutResolver.class);

      when(putResolver.performPut(same(storIOSQLite), anyObject()))
          .thenThrow(new IllegalStateException("test exception"));

      final List<Object> objects = singletonList(new Object());

      try {
        new PreparedPutCollectionOfObjects.Builder<Object>(storIOSQLite, objects)
            .useTransaction(false)
            .withPutResolver(putResolver)
            .prepare()
            .executeAsBlocking();

        failBecauseExceptionWasNotThrown(StorIOException.class);
      } catch (StorIOException expected) {
        IllegalStateException cause = (IllegalStateException) expected.getCause();
        assertThat(cause).hasMessage("test exception");

        // Main checks of this test
        verify(internal, never()).beginTransaction();
        verify(internal, never()).setTransactionSuccessful();
        verify(internal, never()).endTransaction();

        verify(storIOSQLite).lowLevel();
        verify(putResolver).performPut(same(storIOSQLite), anyObject());
        verifyNoMoreInteractions(storIOSQLite, internal, putResolver);
      }
    }
    @Test
    public void
        shouldThrowExceptionIfNoTypeMappingWasFoundWithoutTransactionWithoutAffectingDbAsCompletable() {
      final StorIOSQLite storIOSQLite = mock(StorIOSQLite.class);
      final StorIOSQLite.Internal internal = mock(StorIOSQLite.Internal.class);

      when(storIOSQLite.lowLevel()).thenReturn(internal);

      when(storIOSQLite.put()).thenReturn(new PreparedPut.Builder(storIOSQLite));

      final List<TestItem> items = asList(TestItem.newInstance(), TestItem.newInstance());

      final TestSubscriber<PutResults<TestItem>> testSubscriber =
          new TestSubscriber<PutResults<TestItem>>();

      storIOSQLite
          .put()
          .objects(items)
          .useTransaction(false)
          .prepare()
          .asRxCompletable()
          .subscribe(testSubscriber);

      testSubscriber.awaitTerminalEvent();
      testSubscriber.assertNoValues();
      assertThat(testSubscriber.getOnErrorEvents().get(0))
          .isInstanceOf(StorIOException.class)
          .hasCauseInstanceOf(IllegalStateException.class);

      verify(storIOSQLite).put();
      verify(storIOSQLite).lowLevel();
      verify(storIOSQLite).defaultScheduler();
      verify(internal).typeMapping(TestItem.class);
      verify(internal, never()).insert(any(InsertQuery.class), any(ContentValues.class));
      verify(internal, never()).update(any(UpdateQuery.class), any(ContentValues.class));
      verifyNoMoreInteractions(storIOSQLite, internal);
    }
  @Test
  public void shouldFinishTransactionIfExceptionHasOccurredBlocking() {
    final StorIOSQLite storIOSQLite = mock(StorIOSQLite.class);
    final StorIOSQLite.Internal internal = mock(StorIOSQLite.Internal.class);

    when(storIOSQLite.internal()).thenReturn(internal);

    //noinspection unchecked
    final PutResolver<ContentValues> putResolver = mock(PutResolver.class);

    final List<ContentValues> contentValues = singletonList(mock(ContentValues.class));

    when(putResolver.performPut(same(storIOSQLite), any(ContentValues.class)))
        .thenThrow(new IllegalStateException("test exception"));

    try {
      new PreparedPutContentValuesIterable.Builder(storIOSQLite, contentValues)
          .withPutResolver(putResolver)
          .useTransaction(true)
          .prepare()
          .executeAsBlocking();

      failBecauseExceptionWasNotThrown(StorIOException.class);
    } catch (StorIOException expected) {
      IllegalStateException cause = (IllegalStateException) expected.getCause();
      assertThat(cause).hasMessage("test exception");

      verify(internal).beginTransaction();
      verify(internal, never()).setTransactionSuccessful();
      verify(internal).endTransaction();

      verify(storIOSQLite).internal();
      verify(putResolver).performPut(same(storIOSQLite), any(ContentValues.class));
      verifyNoMoreInteractions(storIOSQLite, internal, putResolver);
    }
  }
  @NonNull
  @Override
  public PutResult performPut(
      @NonNull StorIOSQLite storIOSQLite, @NonNull TweetWithUser tweetWithUser) {
    // We can even reuse StorIO methods
    final PutResults<Object> putResults =
        storIOSQLite
            .put()
            .objects(asList(tweetWithUser.tweet(), tweetWithUser.user()))
            .prepare() // BTW: it will use transaction!
            .executeAsBlocking();

    final Set<String> affectedTables = new HashSet<String>(2);

    affectedTables.add(TweetsTable.TABLE);
    affectedTables.add(UsersTable.TABLE);

    // Actually, it's not very clear what PutResult should we return here…
    // Because there is no table for this pair of tweet and user
    // So, let's just return Update Result
    return PutResult.newUpdateResult(putResults.numberOfUpdates(), affectedTables);
  }
Example #14
0
  @OnClick(R.id.tweets_empty_ui_add_tweets_button)
  void addTweets() {
    final List<Tweet> tweets = new ArrayList<Tweet>();

    tweets.add(
        Tweet.newTweet(
            "artem_zin", "Checkout StorIO — modern API for SQLiteDatabase & ContentResolver"));
    tweets.add(
        Tweet.newTweet(
            "HackerNews",
            "It's revolution! Dolphins can write news on HackerNews with our new app!"));
    tweets.add(Tweet.newTweet("AndroidDevReddit", "Awesome library — StorIO"));
    tweets.add(
        Tweet.newTweet(
            "Facebook",
            "Facebook community in Twitter is more popular than Facebook community in Facebook and Instagram!"));
    tweets.add(
        Tweet.newTweet(
            "Google",
            "Android be together not the same: AOSP, AOSP + Google Apps, Samsung Android"));
    tweets.add(
        Tweet.newTweet(
            "Reddit", "Now we can send funny gifs directly into your brain via Oculus Rift app!"));
    tweets.add(
        Tweet.newTweet(
            "ElonMusk", "Tesla Model S OTA update with Android Auto 5.2, fixes for memory leaks"));
    tweets.add(
        Tweet.newTweet(
            "AndroidWeekly",
            "Special issue #1: StorIO — forget about SQLiteDatabase, ContentResolver APIs, ORMs suck!"));
    tweets.add(
        Tweet.newTweet("Apple", "Yosemite update: fixes for Wifi issues, yosemite-wifi-patch#142"));

    // Looks/reads nice, isn't it?
    storIOSQLite
        .put()
        .objects(tweets)
        .prepare()
        .createObservable()
        .observeOn(
            mainThread()) // Remember, all Observables in StorIO already subscribed on
                          // Schedulers.io(), you just need to set observeOn()
        .subscribe(
            new Observer<PutResults<Tweet>>() {
              @Override
              public void onError(Throwable e) {
                safeShowShortToast(getActivity(), R.string.tweets_add_error_toast);
              }

              @Override
              public void onNext(PutResults<Tweet> putResults) {
                // After successful Put Operation our subscriber in reloadData() will receive
                // update!
              }

              @Override
              public void onCompleted() {
                // no impl required
              }
            });
  }
Example #15
0
 @NonNull
 @Override
 public Cursor performGet(@NonNull StorIOSQLite storIOSQLite, @NonNull Query query) {
   storIOSQLiteFromPerformGet.set(storIOSQLite);
   return storIOSQLite.internal().query(query);
 }