Commit 7f48b354 authored by Gang Wu's avatar Gang Wu Committed by Commit Bot

[Feed] Implement Journal storage API in java

Implement Journal storage API in java

Bug:875438

Change-Id: Iaaede743f66182c3bec7ffc945c37fc9204c1475
Reviewed-on: https://chromium-review.googlesource.com/1179252
Commit-Queue: Gang Wu <gangwu@chromium.org>
Reviewed-by: default avatarFilip Gorski <fgorski@chromium.org>
Cr-Commit-Position: refs/heads/master@{#584307}
parent 6005b6ab
......@@ -34,7 +34,7 @@ public class FeedContentBridge {
public FeedContentBridge() {}
/**
* Inits native side bridge.
* Initializes the native side of this bridge.
*
* @param profile {@link Profile} of the user we are rendering the Feed for.
*/
......
......@@ -99,8 +99,7 @@ public class FeedContentStorage implements ContentStorage {
@Override
public void commit(ContentMutation mutation, Consumer<CommitResult> consumer) {
assert mFeedContentBridge != null;
CommitCallback callback = new CommitCallback(consumer);
mFeedContentBridge.commitContentMutation(mutation, callback);
mFeedContentBridge.commitContentMutation(mutation, new CommitCallback(consumer));
}
@Override
......
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.browser.feed;
import com.google.android.libraries.feed.host.storage.JournalMutation;
import org.chromium.base.Callback;
import org.chromium.base.annotations.JNINamespace;
import org.chromium.chrome.browser.profiles.Profile;
import java.util.List;
/**
* Provides access to native implementations of journal storage.
*/
@JNINamespace("feed")
public class FeedJournalBridge {
private long mNativeFeedJournalBridge;
/**
* Creates a {@link FeedJournalBridge} for accessing native journal storage
* implementation for the current user, and initial native side bridge.
*/
public FeedJournalBridge() {}
/**
* Initializes the native side of this bridge.
*
* @param profile {@link Profile} of the user we are rendering the Feed for.
*/
public void init(Profile profile) {}
/** Cleans up native half of this bridge. */
public void destroy() {}
/** Loads the journal and asynchronously returns the contents. */
public void loadJournal(String journalName, Callback<List<String>> callback) {}
/**
* Commits the operations in {@link JournalMutation} in order and asynchronously reports the
* {@link CommitResult}. If all the operations succeed, {@code callback} is called with a
* success result. If any operation fails, {@code callback} is called with a failure result and
* the remaining operations are not processed.
*/
public void commitJournalMutation(JournalMutation mutation, Callback<Boolean> callback) {}
/** Determines whether a journal exists and asynchronously responds. */
public void doesJournalExist(String journalName, Callback<Boolean> callback) {}
/** Asynchronously retrieve a list of all current journals' name. */
public void loadAllJournalKeys(Callback<List<String>> callback) {}
/** Delete all journals. Reports success or failure. */
public void deleteAllJournals(Callback<Boolean> callback) {}
}
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.browser.feed;
import com.google.android.libraries.feed.common.Result;
import com.google.android.libraries.feed.common.functional.Consumer;
import com.google.android.libraries.feed.host.storage.CommitResult;
import com.google.android.libraries.feed.host.storage.JournalMutation;
import com.google.android.libraries.feed.host.storage.JournalStorage;
import org.chromium.base.Callback;
import java.util.ArrayList;
import java.util.List;
/**
* Implementation of {@link JournalStorage} that persisits data on native side.
*/
public class FeedJournalStorage implements JournalStorage {
private FeedJournalBridge mFeedJournalBridge;
private static class StorageCallback<T> implements Callback<T> {
private final Consumer<Result<T>> mConsumer;
public StorageCallback(Consumer<Result<T>> consumer) {
mConsumer = consumer;
}
@Override
public void onResult(T data) {
// TODO(gangwu): Need to handle failure case.
mConsumer.accept(Result.success(data));
}
}
private static class ReadCallback implements Callback<List<String>> {
private final Consumer < Result < List<byte[]>>> mConsumer;
public ReadCallback(Consumer < Result < List<byte[]>>> consumer) {
mConsumer = consumer;
}
@Override
public void onResult(List<String> entries) {
// TODO(gangwu): Need to handle failure case.
List<byte[]> journal = new ArrayList<byte[]>();
for (String entry : entries) {
journal.add(entry.getBytes());
}
mConsumer.accept(Result.success(journal));
}
}
private static class CommitCallback implements Callback<Boolean> {
private final Consumer<CommitResult> mConsumer;
CommitCallback(Consumer<CommitResult> consumer) {
mConsumer = consumer;
}
@Override
public void onResult(Boolean result) {
if (result) {
mConsumer.accept(CommitResult.SUCCESS);
} else {
mConsumer.accept(CommitResult.FAILURE);
}
}
}
/**
* Creates a {@link FeedJournalStorage} for storing journals for the current user.
*
* @param bridge {@link FeedJournalBridge} implementation can handle journal storage request.
*/
public FeedJournalStorage(FeedJournalBridge bridge) {
mFeedJournalBridge = bridge;
}
/** Cleans up {@link FeedJournalStorage}. */
public void destroy() {
assert mFeedJournalBridge != null;
mFeedJournalBridge.destroy();
mFeedJournalBridge = null;
}
@Override
public void read(String journalName, Consumer < Result < List<byte[]>>> consumer) {
assert mFeedJournalBridge != null;
mFeedJournalBridge.loadJournal(journalName, new ReadCallback(consumer));
}
@Override
public void commit(JournalMutation mutation, Consumer<CommitResult> consumer) {
assert mFeedJournalBridge != null;
mFeedJournalBridge.commitJournalMutation(mutation, new CommitCallback(consumer));
}
@Override
public void exists(String journalName, Consumer<Result<Boolean>> consumer) {
assert mFeedJournalBridge != null;
mFeedJournalBridge.doesJournalExist(journalName, new StorageCallback<Boolean>(consumer));
}
@Override
public void getAllJournals(Consumer < Result < List<String>>> consumer) {
assert mFeedJournalBridge != null;
mFeedJournalBridge.loadAllJournalKeys(new StorageCallback<List<String>>(consumer));
}
@Override
public void deleteAll(Consumer<CommitResult> consumer) {
assert mFeedJournalBridge != null;
mFeedJournalBridge.deleteAllJournals(new CommitCallback(consumer));
}
}
......@@ -15,6 +15,8 @@ if (enable_feed_in_chrome) {
"//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedEventReporter.java",
"//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedImageLoader.java",
"//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedImageLoaderBridge.java",
"//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedJournalBridge.java",
"//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedJournalStorage.java",
"//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedNetworkBridge.java",
"//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedNewTabPage.java",
"//chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedNewTabPageMediator.java",
......@@ -34,6 +36,7 @@ if (enable_feed_in_chrome) {
feed_junit_test_java_sources = [
"junit/src/org/chromium/chrome/browser/feed/FeedContentStorageTest.java",
"junit/src/org/chromium/chrome/browser/feed/FeedImageLoaderTest.java",
"junit/src/org/chromium/chrome/browser/feed/FeedJournalStorageTest.java",
"junit/src/org/chromium/chrome/browser/feed/StreamLifecycleManagerTest.java",
]
......
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.browser.feed;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.support.test.filters.SmallTest;
import com.google.android.libraries.feed.common.Result;
import com.google.android.libraries.feed.common.functional.Consumer;
import com.google.android.libraries.feed.host.storage.CommitResult;
import com.google.android.libraries.feed.host.storage.JournalMutation;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.robolectric.annotation.Config;
import org.chromium.base.Callback;
import org.chromium.base.test.BaseRobolectricTestRunner;
import org.chromium.chrome.browser.profiles.Profile;
import java.util.Arrays;
import java.util.List;
/**
* Unit tests for {@link FeedJournalStorage}.
*/
@RunWith(BaseRobolectricTestRunner.class)
@Config(manifest = Config.NONE)
public class FeedJournalStorageTest {
public static final String JOURNAL_KEY1 = "JOURNAL_KEY_1";
public static final String JOURNAL_KEY2 = "JOURNAL_KEY_2";
public static final String JOURNAL_KEY3 = "JOURNAL_KEY_3";
public static final String JOURNAL_DATA1 = "JOURNAL_DATA_1";
public static final String JOURNAL_DATA2 = "JOURNAL_DATA_2";
public static final String JOURNAL_DATA3 = "JOURNAL_DATA_3";
@Mock
private FeedJournalBridge mBridge;
@Mock
private Consumer<CommitResult> mCommitResultConsumer;
@Mock
private Consumer<Result<Boolean>> mBooleanConsumer;
@Mock
private Consumer < Result < List<byte[]>>> mListOfByteArrayConsumer;
@Mock
private Consumer < Result < List<String>>> mListOfStringConsumer;
@Mock
private Profile mProfile;
@Captor
private ArgumentCaptor<CommitResult> mCommitResultCaptor;
@Captor
private ArgumentCaptor<Result<Boolean>> mBooleanCaptor;
@Captor
private ArgumentCaptor < Result < List<String>>> mListOfStringCaptor;
@Captor
private ArgumentCaptor < Result < List<byte[]>>> mListOfByteArrayCaptor;
@Captor
private ArgumentCaptor<String> mStringArgument;
@Captor
private ArgumentCaptor<Callback<Boolean>> mBooleanCallbackArgument;
@Captor
private ArgumentCaptor < Callback < List<String>>> mListOfStringCallbackArgument;
@Captor
private ArgumentCaptor<JournalMutation> mJournalMutationArgument;
private FeedJournalStorage mJournalStorage;
private Answer<Void> createStringListAnswer(List<String> stringList) {
return new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) {
mListOfStringCallbackArgument.getValue().onResult(stringList);
return null;
}
};
}
private Answer<Void> createBooleanAnswer(Boolean bool) {
return new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) {
mBooleanCallbackArgument.getValue().onResult(bool);
return null;
}
};
}
private void verifyListOfBytesResult(
List<String> expectedList, boolean expectedBoolean, Result<List<byte[]>> actualResult) {
assertEquals(expectedBoolean, actualResult.isSuccessful());
if (!expectedBoolean) return;
List<byte[]> actualList = actualResult.getValue();
assertEquals(expectedList.size(), actualList.size());
for (byte[] actualString : actualList) {
assertTrue(expectedList.contains(new String(actualString)));
}
}
private void verifyListOfStringResult(
List<String> expectedList, boolean expectedBoolean, Result<List<String>> actualResult) {
assertEquals(expectedBoolean, actualResult.isSuccessful());
if (!expectedBoolean) return;
List<String> actualList = actualResult.getValue();
assertEquals(expectedList.size(), actualList.size());
for (String expectedString : expectedList) {
assertTrue(actualList.contains(expectedString));
}
}
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
doNothing().when(mBridge).init(eq(mProfile));
mBridge.init(mProfile);
mJournalStorage = new FeedJournalStorage(mBridge);
}
@Test
@SmallTest
public void readTest() {
List<String> answerStrings = Arrays.asList(JOURNAL_DATA1, JOURNAL_DATA2, JOURNAL_DATA3);
Answer<Void> answer = createStringListAnswer(answerStrings);
doAnswer(answer).when(mBridge).loadJournal(
mStringArgument.capture(), mListOfStringCallbackArgument.capture());
mJournalStorage.read(JOURNAL_KEY1, mListOfByteArrayConsumer);
verify(mBridge, times(1))
.loadJournal(eq(JOURNAL_KEY1), mListOfStringCallbackArgument.capture());
verify(mListOfByteArrayConsumer, times(1)).accept(mListOfByteArrayCaptor.capture());
verifyListOfBytesResult(answerStrings, true, mListOfByteArrayCaptor.getValue());
}
@Test
@SmallTest
public void existsTest() {
Answer<Void> answer = createBooleanAnswer(true);
doAnswer(answer).when(mBridge).doesJournalExist(
mStringArgument.capture(), mBooleanCallbackArgument.capture());
mJournalStorage.exists(JOURNAL_KEY1, mBooleanConsumer);
verify(mBridge, times(1))
.doesJournalExist(eq(JOURNAL_KEY1), mBooleanCallbackArgument.capture());
verify(mBooleanConsumer, times(1)).accept(mBooleanCaptor.capture());
assertTrue(mBooleanCaptor.getValue().getValue());
}
@Test
@SmallTest
public void getAllJournalsTest() {
List<String> answerStrings = Arrays.asList(JOURNAL_KEY1, JOURNAL_KEY2, JOURNAL_KEY3);
Answer<Void> answer = createStringListAnswer(answerStrings);
doAnswer(answer).when(mBridge).loadAllJournalKeys(mListOfStringCallbackArgument.capture());
mJournalStorage.getAllJournals(mListOfStringConsumer);
verify(mBridge, times(1)).loadAllJournalKeys(mListOfStringCallbackArgument.capture());
verify(mListOfStringConsumer, times(1)).accept(mListOfStringCaptor.capture());
verifyListOfStringResult(answerStrings, true, mListOfStringCaptor.getValue());
}
@Test
@SmallTest
public void deleteAllTest() {
Answer<Void> answer = createBooleanAnswer(true);
doAnswer(answer).when(mBridge).deleteAllJournals(mBooleanCallbackArgument.capture());
mJournalStorage.deleteAll(mCommitResultConsumer);
verify(mBridge, times(1)).deleteAllJournals(mBooleanCallbackArgument.capture());
verify(mCommitResultConsumer, times(1)).accept(mCommitResultCaptor.capture());
CommitResult commitResult = mCommitResultCaptor.getValue();
assertEquals(CommitResult.SUCCESS, commitResult);
}
@Test
@SmallTest
public void commitTest() {
Answer<Void> answerCommitJournal = createBooleanAnswer(true);
doAnswer(answerCommitJournal)
.when(mBridge)
.commitJournalMutation(
mJournalMutationArgument.capture(), mBooleanCallbackArgument.capture());
mJournalStorage.commit(new JournalMutation.Builder(JOURNAL_KEY1)
.append(JOURNAL_DATA1.getBytes())
.copy(JOURNAL_KEY2)
.delete()
.build(),
mCommitResultConsumer);
verify(mBridge, times(1))
.commitJournalMutation(
mJournalMutationArgument.capture(), mBooleanCallbackArgument.capture());
verify(mCommitResultConsumer, times(1)).accept(mCommitResultCaptor.capture());
CommitResult commitResult = mCommitResultCaptor.getValue();
assertEquals(CommitResult.SUCCESS, commitResult);
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment