Switch to ephemeral storage when FeedStore reports success but is missing a
number of items that exceeds the configurable threshold. Also report errors
when editing content in FeedSessionManagerImpl.
PiperOrigin-RevId: 258432483
Change-Id: Ied6152ef1ea8d79f175486362fc0e161bc4469d1
diff --git a/src/main/java/com/google/android/libraries/feed/api/client/scope/ProcessScopeBuilder.java b/src/main/java/com/google/android/libraries/feed/api/client/scope/ProcessScopeBuilder.java
index 0a5910a..fbde1df 100644
--- a/src/main/java/com/google/android/libraries/feed/api/client/scope/ProcessScopeBuilder.java
+++ b/src/main/java/com/google/android/libraries/feed/api/client/scope/ProcessScopeBuilder.java
@@ -268,7 +268,9 @@
schedulerApi,
configuration,
clock,
- lifecycleListener);
+ lifecycleListener,
+ mainThreadRunner,
+ basicLoggingApi);
FeedSessionManager feedSessionManager = fsmFactory.create();
RequestManagerImpl clientRequestManager =
new RequestManagerImpl(feedRequestManager, feedSessionManager);
diff --git a/src/main/java/com/google/android/libraries/feed/api/host/config/Configuration.java b/src/main/java/com/google/android/libraries/feed/api/host/config/Configuration.java
index ff510c7..5aefa7e 100644
--- a/src/main/java/com/google/android/libraries/feed/api/host/config/Configuration.java
+++ b/src/main/java/com/google/android/libraries/feed/api/host/config/Configuration.java
@@ -83,6 +83,8 @@
// Minimum time before a spinner should show before disappearing. Only used for feed wide
// spinners, not for more button spinners.
ConfigKey.SPINNER_MINIMUM_SHOW_TIME_MS,
+ // The number of items that can be missing from a call to FeedStore before failing.
+ ConfigKey.STORAGE_MISS_THRESHOLD,
// Time in ms for the length of the timeout
ConfigKey.TIMEOUT_TIMEOUT_MS,
ConfigKey.TRIGGER_IMMEDIATE_PAGINATION,
@@ -135,6 +137,7 @@
String SNIPPETS_ENABLED = "snippets_enabled";
String SPINNER_DELAY_MS = "spinner_delay";
String SPINNER_MINIMUM_SHOW_TIME_MS = "spinner_minimum_show_time";
+ String STORAGE_MISS_THRESHOLD = "storage_miss_threshold";
String TIMEOUT_TIMEOUT_MS = "timeout_timeout_ms";
String TRIGGER_IMMEDIATE_PAGINATION = "trigger_immediate_pagination_bool";
String UNDOABLE_ACTIONS_ENABLED = "undoable_actions_enabled";
diff --git a/src/main/java/com/google/android/libraries/feed/api/host/logging/InternalFeedError.java b/src/main/java/com/google/android/libraries/feed/api/host/logging/InternalFeedError.java
index 22888ab..e566d22 100644
--- a/src/main/java/com/google/android/libraries/feed/api/host/logging/InternalFeedError.java
+++ b/src/main/java/com/google/android/libraries/feed/api/host/logging/InternalFeedError.java
@@ -37,6 +37,10 @@
InternalFeedError.FAILED_TO_CREATE_LEAF,
InternalFeedError.UNHANDLED_TOKEN,
InternalFeedError.TASK_QUEUE_STARVATION,
+ InternalFeedError.CONTENT_STORAGE_MISSING_ITEM,
+ InternalFeedError.ITEM_NOT_PARSED,
+ InternalFeedError.STORAGE_MISS_BEYOND_THRESHOLD,
+ InternalFeedError.CONTENT_MUTATION_FAILED,
InternalFeedError.NEXT_VALUE
})
public @interface InternalFeedError {
@@ -89,6 +93,15 @@
/** Represents bytes from ContentStorage that cannot be parsed into the proto. */
int ITEM_NOT_PARSED = 14;
+ /**
+ * Represents a storage miss where FeedStore reports success but is missing a number of items that
+ * exceeds the configurable threshold.
+ */
+ int STORAGE_MISS_BEYOND_THRESHOLD = 15;
+
+ /** Represents a failed content mutation in FeedSessionManager. */
+ int CONTENT_MUTATION_FAILED = 16;
+
/** The next value that should be used when adding additional values to the IntDef. */
- int NEXT_VALUE = 15;
+ int NEXT_VALUE = 17;
}
diff --git a/src/main/java/com/google/android/libraries/feed/common/testing/InfraIntegrationScope.java b/src/main/java/com/google/android/libraries/feed/common/testing/InfraIntegrationScope.java
index a2057a7..0697bbb 100644
--- a/src/main/java/com/google/android/libraries/feed/common/testing/InfraIntegrationScope.java
+++ b/src/main/java/com/google/android/libraries/feed/common/testing/InfraIntegrationScope.java
@@ -138,7 +138,9 @@
schedulerApi,
configuration,
fakeClock,
- appLifecycleListener)
+ appLifecycleListener,
+ fakeMainThreadRunner,
+ fakeBasicLoggingApi)
.create();
new ClearAllListener(
taskQueue, feedSessionManager, store, fakeThreadUtils, appLifecycleListener);
diff --git a/src/main/java/com/google/android/libraries/feed/feedsessionmanager/FeedSessionManagerFactory.java b/src/main/java/com/google/android/libraries/feed/feedsessionmanager/FeedSessionManagerFactory.java
index 22bcf53..505bde8 100644
--- a/src/main/java/com/google/android/libraries/feed/feedsessionmanager/FeedSessionManagerFactory.java
+++ b/src/main/java/com/google/android/libraries/feed/feedsessionmanager/FeedSessionManagerFactory.java
@@ -16,6 +16,7 @@
import com.google.android.libraries.feed.api.host.config.Configuration;
import com.google.android.libraries.feed.api.host.config.Configuration.ConfigKey;
+import com.google.android.libraries.feed.api.host.logging.BasicLoggingApi;
import com.google.android.libraries.feed.api.host.scheduler.SchedulerApi;
import com.google.android.libraries.feed.api.internal.common.ThreadUtils;
import com.google.android.libraries.feed.api.internal.protocoladapter.ProtocolAdapter;
@@ -23,6 +24,7 @@
import com.google.android.libraries.feed.api.internal.requestmanager.FeedRequestManager;
import com.google.android.libraries.feed.api.internal.sessionmanager.FeedSessionManager;
import com.google.android.libraries.feed.api.internal.store.Store;
+import com.google.android.libraries.feed.common.concurrent.MainThreadRunner;
import com.google.android.libraries.feed.common.concurrent.TaskQueue;
import com.google.android.libraries.feed.common.feedobservable.FeedObservable;
import com.google.android.libraries.feed.common.time.Clock;
@@ -53,6 +55,8 @@
private final Configuration configuration;
private final Clock clock;
private final FeedObservable<FeedLifecycleListener> lifecycleListenerObservable;
+ private final MainThreadRunner mainThreadRunner;
+ private final BasicLoggingApi basicLoggingApi;
public FeedSessionManagerFactory(
TaskQueue taskQueue,
@@ -65,7 +69,9 @@
SchedulerApi schedulerApi,
Configuration configuration,
Clock clock,
- FeedObservable<FeedLifecycleListener> lifecycleListenerObservable) {
+ FeedObservable<FeedLifecycleListener> lifecycleListenerObservable,
+ MainThreadRunner mainThreadRunner,
+ BasicLoggingApi basicLoggingApi) {
this.taskQueue = taskQueue;
this.store = store;
this.timingUtils = timingUtils;
@@ -77,6 +83,8 @@
this.configuration = configuration;
this.clock = clock;
this.lifecycleListenerObservable = lifecycleListenerObservable;
+ this.mainThreadRunner = mainThreadRunner;
+ this.basicLoggingApi = basicLoggingApi;
}
/** Creates a new FeedSessionManager and initializes it */
@@ -98,7 +106,9 @@
schedulerApi,
threadUtils,
timingUtils,
- clock);
+ clock,
+ mainThreadRunner,
+ basicLoggingApi);
return new FeedSessionManagerImpl(
taskQueue,
@@ -115,6 +125,8 @@
schedulerApi,
configuration,
clock,
- lifecycleListenerObservable);
+ lifecycleListenerObservable,
+ mainThreadRunner,
+ basicLoggingApi);
}
}
diff --git a/src/main/java/com/google/android/libraries/feed/feedsessionmanager/FeedSessionManagerImpl.java b/src/main/java/com/google/android/libraries/feed/feedsessionmanager/FeedSessionManagerImpl.java
index 6e9cf4c..4d83dd0 100644
--- a/src/main/java/com/google/android/libraries/feed/feedsessionmanager/FeedSessionManagerImpl.java
+++ b/src/main/java/com/google/android/libraries/feed/feedsessionmanager/FeedSessionManagerImpl.java
@@ -20,6 +20,8 @@
import com.google.android.libraries.feed.api.common.MutationContext;
import com.google.android.libraries.feed.api.host.config.Configuration;
import com.google.android.libraries.feed.api.host.config.Configuration.ConfigKey;
+import com.google.android.libraries.feed.api.host.logging.BasicLoggingApi;
+import com.google.android.libraries.feed.api.host.logging.InternalFeedError;
import com.google.android.libraries.feed.api.host.logging.RequestReason;
import com.google.android.libraries.feed.api.host.logging.Task;
import com.google.android.libraries.feed.api.host.scheduler.SchedulerApi;
@@ -40,6 +42,7 @@
import com.google.android.libraries.feed.api.internal.store.StoreListener;
import com.google.android.libraries.feed.common.Result;
import com.google.android.libraries.feed.common.Validators;
+import com.google.android.libraries.feed.common.concurrent.MainThreadRunner;
import com.google.android.libraries.feed.common.concurrent.TaskQueue;
import com.google.android.libraries.feed.common.concurrent.TaskQueue.TaskType;
import com.google.android.libraries.feed.common.feedobservable.FeedObservable;
@@ -135,6 +138,9 @@
private final SchedulerApi schedulerApi;
private final TaskQueue taskQueue;
private final Clock clock;
+ private final Configuration configuration;
+ private final MainThreadRunner mainThreadRunner;
+ private final BasicLoggingApi basicLoggingApi;
private final long sessionPopulationTimeoutMs;
private final boolean uploadingActionsEnabled;
@@ -162,7 +168,9 @@
SchedulerApi schedulerApi,
Configuration configuration,
Clock clock,
- FeedObservable<FeedLifecycleListener> lifecycleListenerObservable) {
+ FeedObservable<FeedLifecycleListener> lifecycleListenerObservable,
+ MainThreadRunner mainThreadRunner,
+ BasicLoggingApi basicLoggingApi) {
this.taskQueue = taskQueue;
this.sessionFactory = sessionFactory;
this.sessionCache = sessionCache;
@@ -177,6 +185,9 @@
this.actionUploadRequestManager = actionUploadRequestManager;
this.schedulerApi = schedulerApi;
this.clock = clock;
+ this.configuration = configuration;
+ this.mainThreadRunner = mainThreadRunner;
+ this.basicLoggingApi = basicLoggingApi;
uploadingActionsEnabled =
configuration.getValueOrDefault(ConfigKey.UNDOABLE_ACTIONS_ENABLED, false);
sessionPopulationTimeoutMs =
@@ -635,9 +646,32 @@
if (!cacheMisses.isEmpty()) {
Result<List<PayloadWithId>> contentResult = store.getPayloads(cacheMisses);
- if (contentResult.isSuccessful()) {
+ boolean successfulRead =
+ contentResult.isSuccessful()
+ && (contentResult.getValue().size()
+ + configuration.getValueOrDefault(ConfigKey.STORAGE_MISS_THRESHOLD, 4L)
+ >= cacheMisses.size());
+ if (successfulRead) {
+ Logger.i(
+ TAG,
+ "getStreamFeatures; requestedItems(%d), result(%d)",
+ cacheMisses.size(),
+ contentResult.getValue().size());
results.addAll(contentResult.getValue());
} else {
+ if (contentResult.isSuccessful()) {
+ Logger.e(
+ TAG,
+ "Storage miss beyond threshold; requestedItems(%d), returned(%d)",
+ cacheMisses.size(),
+ contentResult.getValue().size());
+ mainThreadRunner.execute(
+ "STORAGE_MISS_BEYOND_THRESHOLD",
+ () -> {
+ basicLoggingApi.onInternalError(InternalFeedError.STORAGE_MISS_BEYOND_THRESHOLD);
+ });
+ }
+
// since we couldn't populate the content, switch to ephemeral mode
switchToEphemeralMode("Unable to get the payloads in getStreamFeatures");
return Result.failure();
diff --git a/src/main/java/com/google/android/libraries/feed/feedsessionmanager/internal/SessionManagerMutation.java b/src/main/java/com/google/android/libraries/feed/feedsessionmanager/internal/SessionManagerMutation.java
index d6777be..02bccb0 100644
--- a/src/main/java/com/google/android/libraries/feed/feedsessionmanager/internal/SessionManagerMutation.java
+++ b/src/main/java/com/google/android/libraries/feed/feedsessionmanager/internal/SessionManagerMutation.java
@@ -18,6 +18,8 @@
import android.text.TextUtils;
import com.google.android.libraries.feed.api.client.knowncontent.KnownContent;
import com.google.android.libraries.feed.api.common.MutationContext;
+import com.google.android.libraries.feed.api.host.logging.BasicLoggingApi;
+import com.google.android.libraries.feed.api.host.logging.InternalFeedError;
import com.google.android.libraries.feed.api.host.logging.Task;
import com.google.android.libraries.feed.api.host.scheduler.SchedulerApi;
import com.google.android.libraries.feed.api.host.storage.CommitResult;
@@ -31,6 +33,7 @@
import com.google.android.libraries.feed.api.internal.store.SemanticPropertiesMutation;
import com.google.android.libraries.feed.api.internal.store.Store;
import com.google.android.libraries.feed.common.Result;
+import com.google.android.libraries.feed.common.concurrent.MainThreadRunner;
import com.google.android.libraries.feed.common.concurrent.TaskQueue;
import com.google.android.libraries.feed.common.concurrent.TaskQueue.TaskType;
import com.google.android.libraries.feed.common.functional.Consumer;
@@ -68,6 +71,8 @@
private final ThreadUtils threadUtils;
private final TimingUtils timingUtils;
private final Clock clock;
+ private final MainThreadRunner mainThreadRunner;
+ private final BasicLoggingApi basicLoggingApi;
// operation counts for the dumper
private int createCount = 0;
@@ -89,7 +94,9 @@
SchedulerApi schedulerApi,
ThreadUtils threadUtils,
TimingUtils timingUtils,
- Clock clock) {
+ Clock clock,
+ MainThreadRunner mainThreadRunner,
+ BasicLoggingApi basicLoggingApi) {
this.store = store;
this.sessionCache = sessionCache;
this.contentCache = contentCache;
@@ -98,6 +105,8 @@
this.threadUtils = threadUtils;
this.timingUtils = timingUtils;
this.clock = clock;
+ this.mainThreadRunner = mainThreadRunner;
+ this.basicLoggingApi = basicLoggingApi;
}
/**
@@ -110,7 +119,13 @@
ModelErrorObserver modelErrorObserver,
KnownContent./*@Nullable*/ Listener knownContentListener) {
createCount++;
- return new MutationCommitter(task, mutationContext, modelErrorObserver, knownContentListener);
+ return new MutationCommitter(
+ task,
+ mutationContext,
+ modelErrorObserver,
+ knownContentListener,
+ mainThreadRunner,
+ basicLoggingApi);
}
public void resetHead() {
@@ -217,6 +232,8 @@
private final KnownContent./*@Nullable*/ Listener knownContentListener;
private final List<StreamStructure> streamStructures = new ArrayList<>();
+ private final MainThreadRunner mainThreadRunner;
+ private final BasicLoggingApi basicLoggingApi;
@VisibleForTesting boolean clearedHead = false;
private Model model;
@@ -225,11 +242,15 @@
String task,
MutationContext mutationContext,
ModelErrorObserver modelErrorObserver,
- KnownContent./*@Nullable*/ Listener knownContentListener) {
+ KnownContent./*@Nullable*/ Listener knownContentListener,
+ MainThreadRunner mainThreadRunner,
+ BasicLoggingApi basicLoggingApi) {
this.task = task;
this.mutationContext = mutationContext;
this.modelErrorObserver = modelErrorObserver;
this.knownContentListener = knownContentListener;
+ this.mainThreadRunner = mainThreadRunner;
+ this.basicLoggingApi = basicLoggingApi;
}
@Override
@@ -348,6 +369,11 @@
if (contentMutation.commit().getResult() == CommitResult.Result.FAILURE) {
contentCommitErrorCount++;
Logger.e(TAG, "contentMutation failed");
+ mainThreadRunner.execute(
+ "CONTENT_MUTATION_FAILED",
+ () -> {
+ basicLoggingApi.onInternalError(InternalFeedError.CONTENT_MUTATION_FAILED);
+ });
}
if (semanticPropertiesMutation.commit().getResult() == CommitResult.Result.FAILURE) {
semanticPropertiesCommitErrorCount++;
diff --git a/src/main/java/com/google/android/libraries/feed/feedstore/FeedStore.java b/src/main/java/com/google/android/libraries/feed/feedstore/FeedStore.java
index 9cec44f..30fe916 100644
--- a/src/main/java/com/google/android/libraries/feed/feedstore/FeedStore.java
+++ b/src/main/java/com/google/android/libraries/feed/feedstore/FeedStore.java
@@ -87,6 +87,9 @@
private boolean isEphemeralMode = false;
+ protected final ContentStorageDirect contentStorage;
+ protected final JournalStorageDirect journalStorage;
+
public FeedStore(
TimingUtils timingUtils,
FeedExtensionRegistry extensionRegistry,
@@ -103,6 +106,8 @@
this.threadUtils = threadUtils;
this.basicLoggingApi = basicLoggingApi;
this.mainThreadRunner = mainThreadRunner;
+ this.contentStorage = contentStorage;
+ this.journalStorage = journalStorage;
this.persistentStore =
new PersistentFeedStore(
diff --git a/src/main/java/com/google/android/libraries/feed/testing/store/BUILD b/src/main/java/com/google/android/libraries/feed/testing/store/BUILD
index 9631384..8673c55 100644
--- a/src/main/java/com/google/android/libraries/feed/testing/store/BUILD
+++ b/src/main/java/com/google/android/libraries/feed/testing/store/BUILD
@@ -7,6 +7,7 @@
testonly = True,
srcs = glob(["*.java"]),
deps = [
+ "//src/main/java/com/google/android/libraries/feed/api/host/storage",
"//src/main/java/com/google/android/libraries/feed/api/internal/common",
"//src/main/java/com/google/android/libraries/feed/api/internal/store",
"//src/main/java/com/google/android/libraries/feed/common",
diff --git a/src/main/java/com/google/android/libraries/feed/testing/store/FakeStore.java b/src/main/java/com/google/android/libraries/feed/testing/store/FakeStore.java
index d7a96f7..5674398 100644
--- a/src/main/java/com/google/android/libraries/feed/testing/store/FakeStore.java
+++ b/src/main/java/com/google/android/libraries/feed/testing/store/FakeStore.java
@@ -14,6 +14,7 @@
package com.google.android.libraries.feed.testing.store;
+import com.google.android.libraries.feed.api.host.storage.CommitResult;
import com.google.android.libraries.feed.api.internal.common.PayloadWithId;
import com.google.android.libraries.feed.api.internal.store.ContentMutation;
import com.google.android.libraries.feed.api.internal.store.SessionMutation;
@@ -41,9 +42,11 @@
public final class FakeStore extends FeedStore {
private final FakeThreadUtils fakeThreadUtils;
private boolean allowCreateNewSession = true;
+ private boolean allowEditContent = true;
private boolean allowGetPayloads = true;
private boolean allowGetStreamStructures = true;
private boolean allowGetSharedStates = true;
+ private boolean clearHeadCalled = false;
public FakeStore(FakeThreadUtils fakeThreadUtils, TaskQueue taskQueue, Clock clock) {
super(
@@ -78,6 +81,12 @@
}
@Override
+ public void clearHead() {
+ clearHeadCalled = true;
+ super.clearHead();
+ }
+
+ @Override
public Result<String> createNewSession() {
if (!allowCreateNewSession) {
return Result.failure();
@@ -95,12 +104,51 @@
return super.getSharedStates();
}
+ @Override
+ public ContentMutation editContent() {
+ if (!allowEditContent) {
+ return new ContentMutation() {
+ @Override
+ public ContentMutation add(String contentId, StreamPayload payload) {
+ return this;
+ }
+
+ @Override
+ public CommitResult commit() {
+ return CommitResult.FAILURE;
+ }
+ };
+ }
+
+ return super.editContent();
+ }
+
+ /** Returns if {@link FeedStore#clearAll()} was called. */
+ public boolean getClearHeadCalled() {
+ return clearHeadCalled;
+ }
+
+ /** Clears all content storage. */
+ public FakeStore clearContent() {
+ contentStorage.commit(
+ new com.google.android.libraries.feed.api.host.storage.ContentMutation.Builder()
+ .deleteAll()
+ .build());
+ return this;
+ }
+
/** Sets whether to fail on calls to {@link getStreamStructures(String)}. */
public FakeStore setAllowGetStreamStructures(boolean value) {
allowGetStreamStructures = value;
return this;
}
+ /** Sets whether to fail on calls to {@link editContent()}. */
+ public FakeStore setAllowEditContent(boolean value) {
+ allowEditContent = value;
+ return this;
+ }
+
/** Sets whether to fail on calls to {@link createNewSession()}. */
public FakeStore setAllowCreateNewSession(boolean value) {
allowCreateNewSession = value;
diff --git a/src/test/java/com/google/android/libraries/feed/feedsessionmanager/BUILD b/src/test/java/com/google/android/libraries/feed/feedsessionmanager/BUILD
index 9b03cee..21f80a2 100644
--- a/src/test/java/com/google/android/libraries/feed/feedsessionmanager/BUILD
+++ b/src/test/java/com/google/android/libraries/feed/feedsessionmanager/BUILD
@@ -29,6 +29,7 @@
"//src/main/java/com/google/android/libraries/feed/feedmodelprovider",
"//src/main/java/com/google/android/libraries/feed/feedsessionmanager",
"//src/main/java/com/google/android/libraries/feed/feedsessionmanager/internal",
+ "//src/main/java/com/google/android/libraries/feed/testing/host/logging",
"//src/main/java/com/google/android/libraries/feed/testing/protocoladapter",
"//src/main/java/com/google/android/libraries/feed/testing/requestmanager",
"//src/main/java/com/google/android/libraries/feed/testing/store",
diff --git a/src/test/java/com/google/android/libraries/feed/feedsessionmanager/FeedSessionManagerImplTest.java b/src/test/java/com/google/android/libraries/feed/feedsessionmanager/FeedSessionManagerImplTest.java
index 18704ec..4b40168 100644
--- a/src/test/java/com/google/android/libraries/feed/feedsessionmanager/FeedSessionManagerImplTest.java
+++ b/src/test/java/com/google/android/libraries/feed/feedsessionmanager/FeedSessionManagerImplTest.java
@@ -25,6 +25,7 @@
import com.google.android.libraries.feed.api.common.MutationContext;
import com.google.android.libraries.feed.api.host.config.Configuration;
import com.google.android.libraries.feed.api.host.config.Configuration.ConfigKey;
+import com.google.android.libraries.feed.api.host.logging.InternalFeedError;
import com.google.android.libraries.feed.api.host.logging.RequestReason;
import com.google.android.libraries.feed.api.host.scheduler.SchedulerApi;
import com.google.android.libraries.feed.api.host.scheduler.SchedulerApi.RequestBehavior;
@@ -56,6 +57,7 @@
import com.google.android.libraries.feed.feedsessionmanager.internal.HeadSessionImpl;
import com.google.android.libraries.feed.feedsessionmanager.internal.Session;
import com.google.android.libraries.feed.feedsessionmanager.internal.SessionCache;
+import com.google.android.libraries.feed.testing.host.logging.FakeBasicLoggingApi;
import com.google.android.libraries.feed.testing.proto.UiContextForTestProto.UiContextForTest;
import com.google.android.libraries.feed.testing.protocoladapter.FakeProtocolAdapter;
import com.google.android.libraries.feed.testing.requestmanager.FakeActionUploadRequestManager;
@@ -102,6 +104,7 @@
.setTable("piet-shared-state")
.build();
private static final String SESSION_ID = "session:1";
+ private static final int STORAGE_MISS_THRESHOLD = 4;
private final ContentIdGenerators contentIdGenerators = new ContentIdGenerators();
private final ContentIdGenerators idGenerators = new ContentIdGenerators();
@@ -111,6 +114,7 @@
private Configuration configuration;
private FakeActionUploadRequestManager fakeActionUploadRequestManager;
+ private FakeBasicLoggingApi fakeBasicLoggingApi;
private FakeMainThreadRunner fakeMainThreadRunner;
private FakeProtocolAdapter fakeProtocolAdapter;
private FakeFeedRequestManager fakeRequestManager;
@@ -134,7 +138,9 @@
configuration =
new Configuration.Builder()
.put(ConfigKey.UNDOABLE_ACTIONS_ENABLED, uploadingActionsEnabled)
+ .put(ConfigKey.STORAGE_MISS_THRESHOLD, STORAGE_MISS_THRESHOLD)
.build();
+ fakeBasicLoggingApi = new FakeBasicLoggingApi();
fakeThreadUtils = FakeThreadUtils.withThreadChecks();
fakeMainThreadRunner =
FakeMainThreadRunner.runTasksImmediatelyWithThreadChecks(fakeThreadUtils);
@@ -351,6 +357,30 @@
}
@Test
+ public void testMissingFeaturesBeyondThreshold_switchToEphemeralMode() {
+ FeedSessionManagerImpl sessionManager = getInitializedSessionManager();
+ populateSession(sessionManager, STORAGE_MISS_THRESHOLD, 1, true, null);
+ fakeStore.clearContent();
+
+ ModelProvider modelProvider = getModelProvider(sessionManager);
+ assertThat(modelProvider).isNotNull();
+ assertThat(fakeStore.isEphemeralMode()).isTrue();
+ assertThat(fakeBasicLoggingApi.lastInternalError)
+ .isEqualTo(InternalFeedError.STORAGE_MISS_BEYOND_THRESHOLD);
+ }
+
+ @Test
+ public void testMissingFeaturesAtThreshold_doesNotSwitchToEphemeralMode() {
+ FeedSessionManagerImpl sessionManager = getInitializedSessionManager();
+ populateSession(sessionManager, STORAGE_MISS_THRESHOLD - 1, 1, true, null);
+ fakeStore.clearContent();
+
+ ModelProvider modelProvider = getModelProvider(sessionManager);
+ assertThat(modelProvider).isNotNull();
+ assertThat(fakeStore.isEphemeralMode()).isFalse();
+ }
+
+ @Test
public void testNoCardsError() {
FeedSessionManagerImpl sessionManager = getInitializedSessionManager();
sessionManager.getUpdateConsumer(EMPTY_MUTATION).accept(Result.failure());
@@ -802,7 +832,9 @@
schedulerApi,
configuration,
fakeClock,
- appLifecycleListener)
+ appLifecycleListener,
+ fakeMainThreadRunner,
+ fakeBasicLoggingApi)
.create();
}
}
diff --git a/src/test/java/com/google/android/libraries/feed/feedsessionmanager/internal/BUILD b/src/test/java/com/google/android/libraries/feed/feedsessionmanager/internal/BUILD
index 5460249..b289e18 100644
--- a/src/test/java/com/google/android/libraries/feed/feedsessionmanager/internal/BUILD
+++ b/src/test/java/com/google/android/libraries/feed/feedsessionmanager/internal/BUILD
@@ -82,23 +82,19 @@
"//src/main/java/com/google/android/libraries/feed/api/client/knowncontent",
"//src/main/java/com/google/android/libraries/feed/api/common",
"//src/main/java/com/google/android/libraries/feed/api/host/config",
+ "//src/main/java/com/google/android/libraries/feed/api/host/logging",
"//src/main/java/com/google/android/libraries/feed/api/host/scheduler",
- "//src/main/java/com/google/android/libraries/feed/api/host/storage",
"//src/main/java/com/google/android/libraries/feed/api/internal/common",
"//src/main/java/com/google/android/libraries/feed/api/internal/common/testing",
"//src/main/java/com/google/android/libraries/feed/api/internal/modelprovider",
- "//src/main/java/com/google/android/libraries/feed/api/internal/store",
"//src/main/java/com/google/android/libraries/feed/common",
"//src/main/java/com/google/android/libraries/feed/common/concurrent/testing",
"//src/main/java/com/google/android/libraries/feed/common/functional",
- "//src/main/java/com/google/android/libraries/feed/common/protoextensions",
"//src/main/java/com/google/android/libraries/feed/common/time",
"//src/main/java/com/google/android/libraries/feed/common/time/testing",
"//src/main/java/com/google/android/libraries/feed/feedsessionmanager/internal",
- "//src/main/java/com/google/android/libraries/feed/feedstore",
- "//src/main/java/com/google/android/libraries/feed/feedstore/testing",
- "//src/main/java/com/google/android/libraries/feed/hostimpl/storage/testing",
"//src/main/java/com/google/android/libraries/feed/testing/host/logging",
+ "//src/main/java/com/google/android/libraries/feed/testing/store",
"//src/main/proto/com/google/android/libraries/feed/api/internal/proto:client_feed_java_proto_lite",
"//third_party:robolectric",
"@com_google_protobuf_javalite//:protobuf_java_lite",
diff --git a/src/test/java/com/google/android/libraries/feed/feedsessionmanager/internal/SessionManagerMutationTest.java b/src/test/java/com/google/android/libraries/feed/feedsessionmanager/internal/SessionManagerMutationTest.java
index bd8f1fc..7084545 100644
--- a/src/test/java/com/google/android/libraries/feed/feedsessionmanager/internal/SessionManagerMutationTest.java
+++ b/src/test/java/com/google/android/libraries/feed/feedsessionmanager/internal/SessionManagerMutationTest.java
@@ -19,7 +19,6 @@
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.mockito.MockitoAnnotations.initMocks;
@@ -28,8 +27,8 @@
import com.google.android.libraries.feed.api.client.knowncontent.KnownContent;
import com.google.android.libraries.feed.api.common.MutationContext;
import com.google.android.libraries.feed.api.host.config.Configuration;
+import com.google.android.libraries.feed.api.host.logging.InternalFeedError;
import com.google.android.libraries.feed.api.host.scheduler.SchedulerApi;
-import com.google.android.libraries.feed.api.host.storage.ContentStorageDirect;
import com.google.android.libraries.feed.api.internal.common.Model;
import com.google.android.libraries.feed.api.internal.common.PayloadWithId;
import com.google.android.libraries.feed.api.internal.common.SemanticPropertiesWithId;
@@ -38,21 +37,16 @@
import com.google.android.libraries.feed.api.internal.modelprovider.ModelError.ErrorType;
import com.google.android.libraries.feed.api.internal.modelprovider.ModelProvider;
import com.google.android.libraries.feed.api.internal.modelprovider.ModelProvider.State;
-import com.google.android.libraries.feed.api.internal.store.Store;
import com.google.android.libraries.feed.common.Result;
import com.google.android.libraries.feed.common.concurrent.testing.FakeMainThreadRunner;
import com.google.android.libraries.feed.common.concurrent.testing.FakeTaskQueue;
import com.google.android.libraries.feed.common.concurrent.testing.FakeThreadUtils;
import com.google.android.libraries.feed.common.functional.Supplier;
-import com.google.android.libraries.feed.common.protoextensions.FeedExtensionRegistry;
import com.google.android.libraries.feed.common.time.TimingUtils;
import com.google.android.libraries.feed.common.time.testing.FakeClock;
import com.google.android.libraries.feed.feedsessionmanager.internal.SessionManagerMutation.MutationCommitter;
-import com.google.android.libraries.feed.feedstore.FeedStore;
-import com.google.android.libraries.feed.feedstore.testing.DelegatingStore;
-import com.google.android.libraries.feed.hostimpl.storage.testing.InMemoryContentStorage;
-import com.google.android.libraries.feed.hostimpl.storage.testing.InMemoryJournalStorage;
import com.google.android.libraries.feed.testing.host.logging.FakeBasicLoggingApi;
+import com.google.android.libraries.feed.testing.store.FakeStore;
import com.google.common.collect.ImmutableList;
import com.google.protobuf.ByteString;
import com.google.search.now.feed.client.StreamDataProto.StreamDataOperation;
@@ -78,11 +72,11 @@
public class SessionManagerMutationTest {
private final Configuration configuration = new Configuration.Builder().build();
private final ContentIdGenerators idGenerators = new ContentIdGenerators();
- private final ContentStorageDirect contentStorage = new InMemoryContentStorage();
private final FakeBasicLoggingApi fakeBasicLoggingApi = new FakeBasicLoggingApi();
private final FakeClock fakeClock = new FakeClock();
+ private final FakeMainThreadRunner fakeMainThreadRunner =
+ FakeMainThreadRunner.runTasksImmediately();
private final FakeThreadUtils fakeThreadUtils = FakeThreadUtils.withThreadChecks();
- private final FeedExtensionRegistry extensionRegistry = new FeedExtensionRegistry(ArrayList::new);
private final String rootContentId = idGenerators.createRootContentId(0);
private final TimingUtils timingUtils = new TimingUtils();
@@ -93,33 +87,21 @@
private ModelError notifyError;
private Session notifySession;
private SessionCache sessionCache;
- private Store storeSpy;
+ private FakeStore fakeStore;
@Before
public void setUp() {
initMocks(this);
- storeSpy = null;
notifySession = null;
fakeTaskQueue = new FakeTaskQueue(fakeClock, fakeThreadUtils);
fakeTaskQueue.initialize(() -> {});
fakeThreadUtils.enforceMainThread(false);
- Store store =
- new FeedStore(
- timingUtils,
- extensionRegistry,
- contentStorage,
- new InMemoryJournalStorage(),
- fakeThreadUtils,
- fakeTaskQueue,
- fakeClock,
- fakeBasicLoggingApi,
- FakeMainThreadRunner.runTasksImmediately());
- storeSpy = spy(new DelegatingStore(store));
+ fakeStore = new FakeStore(fakeThreadUtils, fakeTaskQueue, fakeClock);
SessionFactory sessionFactory =
- new SessionFactory(storeSpy, fakeTaskQueue, timingUtils, fakeThreadUtils, configuration);
+ new SessionFactory(fakeStore, fakeTaskQueue, timingUtils, fakeThreadUtils, configuration);
sessionCache =
new SessionCache(
- storeSpy, fakeTaskQueue, sessionFactory, 10L, timingUtils, fakeThreadUtils, fakeClock);
+ fakeStore, fakeTaskQueue, sessionFactory, 10L, timingUtils, fakeThreadUtils, fakeClock);
sessionCache.initialize();
contentCache = new ContentCache();
}
@@ -202,7 +184,7 @@
MutationCommitter mutationCommitter = getMutationCommitter(EMPTY_CONTEXT);
mutationCommitter.accept(Result.success(Model.of(dataOperations)));
- Result<List<PayloadWithId>> result = storeSpy.getPayloads(contentIds);
+ Result<List<PayloadWithId>> result = fakeStore.getPayloads(contentIds);
assertThat(result.isSuccessful()).isTrue();
assertThat(result.getValue()).hasSize(contentIds.size());
for (PayloadWithId payload : result.getValue()) {
@@ -214,6 +196,29 @@
}
@Test
+ public void testUpdateContent_failedCommitLogsError() {
+ fakeStore.setAllowEditContent(false);
+
+ List<String> contentIds = getContentIds(3);
+ List<Pair<StreamStructure, StreamPayload>> features =
+ getFeatures(
+ contentIds,
+ () ->
+ StreamPayload.newBuilder()
+ .setStreamFeature(StreamFeature.getDefaultInstance())
+ .build());
+ List<StreamDataOperation> dataOperations = new ArrayList<>();
+ for (Pair<StreamStructure, StreamPayload> feature : features) {
+ dataOperations.add(getStreamDataOperation(feature.first, feature.second));
+ }
+ MutationCommitter mutationCommitter = getMutationCommitter(EMPTY_CONTEXT);
+ mutationCommitter.accept(Result.success(Model.of(dataOperations)));
+
+ assertThat(fakeBasicLoggingApi.lastInternalError)
+ .isEqualTo(InternalFeedError.CONTENT_MUTATION_FAILED);
+ }
+
+ @Test
public void testSemanticData() {
List<String> contentIds = getContentIds(2);
List<Pair<StreamStructure, StreamPayload>> features =
@@ -230,7 +235,7 @@
MutationCommitter mutationCommitter = getMutationCommitter(EMPTY_CONTEXT);
mutationCommitter.accept(Result.success(Model.of(dataOperations)));
- Result<List<SemanticPropertiesWithId>> result = storeSpy.getSemanticProperties(contentIds);
+ Result<List<SemanticPropertiesWithId>> result = fakeStore.getSemanticProperties(contentIds);
assertThat(result.isSuccessful()).isTrue();
assertThat(result.getValue()).hasSize(contentIds.size());
for (SemanticPropertiesWithId payload : result.getValue()) {
@@ -274,7 +279,7 @@
public void testInvalidateHead() {
MutationCommitter mutationCommitter = getMutationCommitter(EMPTY_CONTEXT);
mutationCommitter.resetHead(null);
- verify(storeSpy).clearHead();
+ assertThat(fakeStore.getClearHeadCalled()).isTrue();
}
@Test
@@ -332,14 +337,16 @@
private MutationCommitter getMutationCommitter(MutationContext mutationContext) {
SessionManagerMutation mutation =
new SessionManagerMutation(
- storeSpy,
+ fakeStore,
sessionCache,
contentCache,
fakeTaskQueue,
schedulerApi,
fakeThreadUtils,
timingUtils,
- fakeClock);
+ fakeClock,
+ fakeMainThreadRunner,
+ fakeBasicLoggingApi);
return (MutationCommitter)
mutation.createCommitter(
"task", mutationContext, this::notifySessionError, knownContentListener);