Migration from PageAnnotationService to Cacao
Also - previous integration path is still maintained. Binary-Size: Increase is due to new protobufs for shopping projects. Bug: 1196765 Change-Id: I4c0fec7c80b2ded95797a0c886943e15fd9dc498 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2810532 Commit-Queue: David Maunder <davidjm@chromium.org> Reviewed-by: Sophie Chang <sophiechang@chromium.org> Reviewed-by: Yusuf Ozuysal <yusufo@chromium.org> Cr-Commit-Position: refs/heads/master@{#876447}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
271625d704
commit
bbbb7436da
chrome
android
BUILD.gnchrome_test_java_sources.gni
features
tab_ui
junit
src
org
chromium
chrome
browser
tasks
tab_management
java
src
org
chromium
chrome
browser
app
javatests
browser
components/optimization_guide
tools/metrics/histograms/histograms_xml
@@ -910,6 +910,7 @@ junit_binary("chrome_junit_tests") {
|
||||
"//chrome/browser/signin/ui/android:junit",
|
||||
"//chrome/browser/tab:java",
|
||||
"//chrome/browser/tab:junit",
|
||||
"//chrome/browser/tab:optimization_guide_protos_java",
|
||||
"//chrome/browser/tab_group:java",
|
||||
"//chrome/browser/tab_group:junit",
|
||||
"//chrome/browser/tabmodel:factory_java",
|
||||
@@ -1210,6 +1211,7 @@ android_library("chrome_test_java") {
|
||||
"//chrome/browser/signin/ui/android:javatests",
|
||||
"//chrome/browser/tab:critical_persisted_tab_data_proto_java",
|
||||
"//chrome/browser/tab:java",
|
||||
"//chrome/browser/tab:optimization_guide_protos_java",
|
||||
"//chrome/browser/tab_group:java",
|
||||
"//chrome/browser/tabmodel:java",
|
||||
"//chrome/browser/tabmodel/internal:java",
|
||||
|
@@ -565,8 +565,11 @@ chrome_test_java_sources = [
|
||||
"javatests/src/org/chromium/chrome/browser/tab/state/LoadCallbackHelper.java",
|
||||
"javatests/src/org/chromium/chrome/browser/tab/state/PersistedTabDataTest.java",
|
||||
"javatests/src/org/chromium/chrome/browser/tab/state/ShoppingPersistedTabDataLegacyTest.java",
|
||||
"javatests/src/org/chromium/chrome/browser/tab/state/ShoppingPersistedTabDataLegacyWithPASTest.java",
|
||||
"javatests/src/org/chromium/chrome/browser/tab/state/ShoppingPersistedTabDataTest.java",
|
||||
"javatests/src/org/chromium/chrome/browser/tab/state/ShoppingPersistedTabDataTestUtils.java",
|
||||
"javatests/src/org/chromium/chrome/browser/tab/state/ShoppingPersistedTabDataWithPASTest.java",
|
||||
"javatests/src/org/chromium/chrome/browser/tab/state/ShoppingPersistedTabDataWithPASTestUtils.java",
|
||||
"javatests/src/org/chromium/chrome/browser/tabbed_mode/TabbedNavigationBarColorControllerTest.java",
|
||||
"javatests/src/org/chromium/chrome/browser/tabmodel/AsyncTabCreationParamsManagerTest.java",
|
||||
"javatests/src/org/chromium/chrome/browser/tabmodel/ChromeTabCreatorTest.java",
|
||||
|
@@ -64,6 +64,8 @@ import androidx.recyclerview.widget.GridLayoutManager;
|
||||
import androidx.recyclerview.widget.ItemTouchHelper;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.google.protobuf.ByteString;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Assume;
|
||||
import org.junit.Before;
|
||||
@@ -104,6 +106,9 @@ import org.chromium.chrome.browser.tab.TabCreationState;
|
||||
import org.chromium.chrome.browser.tab.TabImpl;
|
||||
import org.chromium.chrome.browser.tab.TabLaunchType;
|
||||
import org.chromium.chrome.browser.tab.TabObserver;
|
||||
import org.chromium.chrome.browser.tab.proto.PriceTracking.BuyableProduct;
|
||||
import org.chromium.chrome.browser.tab.proto.PriceTracking.PriceTrackingData;
|
||||
import org.chromium.chrome.browser.tab.proto.PriceTracking.ProductPrice;
|
||||
import org.chromium.chrome.browser.tab.state.CriticalPersistedTabData;
|
||||
import org.chromium.chrome.browser.tab.state.PersistedTabDataConfiguration;
|
||||
import org.chromium.chrome.browser.tab.state.ShoppingPersistedTabData;
|
||||
@@ -128,6 +133,7 @@ import org.chromium.components.embedder_support.util.UrlUtilitiesJni;
|
||||
import org.chromium.components.feature_engagement.EventConstants;
|
||||
import org.chromium.components.feature_engagement.Tracker;
|
||||
import org.chromium.components.optimization_guide.OptimizationGuideDecision;
|
||||
import org.chromium.components.optimization_guide.proto.CommonTypesProto.Any;
|
||||
import org.chromium.components.search_engines.TemplateUrlService;
|
||||
import org.chromium.content_public.browser.LoadUrlParams;
|
||||
import org.chromium.content_public.browser.NavigationController;
|
||||
@@ -147,7 +153,6 @@ import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Tests for {@link TabListMediator}.
|
||||
*/
|
||||
@@ -181,10 +186,25 @@ public class TabListMediatorUnitTest {
|
||||
private static final int TAB3_ID = 123;
|
||||
private static final int POSITION1 = 0;
|
||||
private static final int POSITION2 = 1;
|
||||
private static final String EMPTY_ENDPOINT_RESPONSE = "{}";
|
||||
private static final String ENDPOINT_RESPONSE =
|
||||
"{\"representations\" : [{\"type\" : \"SHOPPING\", \"productTitle\" : \"Book of Pie\","
|
||||
+ "\"price\" : 123456789012345, \"currency\" : \"USD\"}]}";
|
||||
|
||||
private static final BuyableProduct BUYABLE_PRODUCT_PROTO_INITIAL =
|
||||
BuyableProduct.newBuilder()
|
||||
.setCurrentPrice(createProductPrice(123456789012345L, "USD"))
|
||||
.build();
|
||||
private static ProductPrice createProductPrice(long amountMicros, String currencyCode) {
|
||||
return ProductPrice.newBuilder()
|
||||
.setCurrencyCode(currencyCode)
|
||||
.setAmountMicros(amountMicros)
|
||||
.build();
|
||||
}
|
||||
private static final PriceTrackingData PRICE_TRACKING_BUYABLE_PRODUCT_INITIAL =
|
||||
PriceTrackingData.newBuilder().setBuyableProduct(BUYABLE_PRODUCT_PROTO_INITIAL).build();
|
||||
private static final Any ANY_BUYABLE_PRODUCT_INITIAL =
|
||||
Any.newBuilder()
|
||||
.setValue(ByteString.copyFrom(
|
||||
PRICE_TRACKING_BUYABLE_PRODUCT_INITIAL.toByteArray()))
|
||||
.build();
|
||||
private static final Any ANY_EMPTY = Any.newBuilder().build();
|
||||
|
||||
@IntDef({TabListMediatorType.TAB_SWITCHER, TabListMediatorType.TAB_STRIP,
|
||||
TabListMediatorType.TAB_GRID_DIALOG})
|
||||
@@ -294,7 +314,6 @@ public class TabListMediatorUnitTest {
|
||||
mMocker.mock(OptimizationGuideBridgeJni.TEST_HOOKS, mOptimizationGuideBridgeJniMock);
|
||||
// Ensure native pointer is initialized
|
||||
doReturn(1L).when(mOptimizationGuideBridgeJniMock).init();
|
||||
mockOptimizationGuideResponse(OptimizationGuideDecision.TRUE);
|
||||
|
||||
mTab1Domain = JUnitTestGURLs.getGURL(TAB1_URL).getHost().replace("www.", "");
|
||||
mTab2Domain = JUnitTestGURLs.getGURL(TAB2_URL).getHost().replace("www.", "");
|
||||
@@ -1899,10 +1918,12 @@ public class TabListMediatorUnitTest {
|
||||
PriceTrackingUtilities.SHARED_PREFERENCES_MANAGER.writeBoolean(
|
||||
PriceTrackingUtilities.TRACK_PRICES_ON_TABS, priceTrackingEnabled);
|
||||
Profile.setLastUsedProfileForTesting(mProfile);
|
||||
Map<String, String> responses = new HashMap<>();
|
||||
responses.put(TAB1_URL, ENDPOINT_RESPONSE);
|
||||
responses.put(TAB2_URL, EMPTY_ENDPOINT_RESPONSE);
|
||||
mockEndpointResponse(responses);
|
||||
Map<GURL, Any> responses = new HashMap<>();
|
||||
GURL gurl1 = JUnitTestGURLs.getGURL(TAB1_URL);
|
||||
GURL gurl2 = JUnitTestGURLs.getGURL(TAB2_URL);
|
||||
responses.put(gurl1, ANY_BUYABLE_PRODUCT_INITIAL);
|
||||
responses.put(gurl2, ANY_EMPTY);
|
||||
mockOptimizationGuideResponse(OptimizationGuideDecision.TRUE, responses);
|
||||
PersistedTabDataConfiguration.setUseTestConfig(true);
|
||||
initAndAssertAllProperties(mMediatorSpy);
|
||||
List<Tab> tabs = new ArrayList<>();
|
||||
@@ -2783,19 +2804,22 @@ public class TabListMediatorUnitTest {
|
||||
}
|
||||
}
|
||||
|
||||
private void mockOptimizationGuideResponse(@OptimizationGuideDecision int decision) {
|
||||
doAnswer(new Answer<Void>() {
|
||||
@Override
|
||||
public Void answer(InvocationOnMock invocation) {
|
||||
OptimizationGuideCallback callback =
|
||||
(OptimizationGuideCallback) invocation.getArguments()[3];
|
||||
callback.onOptimizationGuideDecision(decision, null);
|
||||
return null;
|
||||
}
|
||||
})
|
||||
.when(mOptimizationGuideBridgeJniMock)
|
||||
.canApplyOptimization(
|
||||
anyLong(), any(GURL.class), anyInt(), any(OptimizationGuideCallback.class));
|
||||
private void mockOptimizationGuideResponse(
|
||||
@OptimizationGuideDecision int decision, Map<GURL, Any> responses) {
|
||||
for (Map.Entry<GURL, Any> responseEntry : responses.entrySet()) {
|
||||
doAnswer(new Answer<Void>() {
|
||||
@Override
|
||||
public Void answer(InvocationOnMock invocation) {
|
||||
OptimizationGuideCallback callback =
|
||||
(OptimizationGuideCallback) invocation.getArguments()[3];
|
||||
callback.onOptimizationGuideDecision(decision, responseEntry.getValue());
|
||||
return null;
|
||||
}
|
||||
})
|
||||
.when(mOptimizationGuideBridgeJniMock)
|
||||
.canApplyOptimization(anyLong(), eq(responseEntry.getKey()), anyInt(),
|
||||
any(OptimizationGuideCallback.class));
|
||||
}
|
||||
}
|
||||
|
||||
private void initWithThreeTabs() {
|
||||
|
@@ -121,6 +121,7 @@ public class ChromeCachedFlags {
|
||||
ShoppingPersistedTabData.TIME_TO_LIVE_MS,
|
||||
ShoppingPersistedTabData.DISPLAY_TIME_MS,
|
||||
ShoppingPersistedTabData.STALE_TAB_THRESHOLD_SECONDS,
|
||||
ShoppingPersistedTabData.PRICE_TRACKING_WITH_OPTIMIZATION_GUIDE,
|
||||
StartSurfaceConfiguration.HOME_BUTTON_ON_GRID_TAB_SWITCHER,
|
||||
StartSurfaceConfiguration.NEW_SURFACE_FROM_HOME_BUTTON,
|
||||
StartSurfaceConfiguration.OMNIBOX_FOCUSED_ON_NEW_TAB,
|
||||
|
@@ -21,20 +21,16 @@ import org.chromium.base.test.BaseJUnit4ClassRunner;
|
||||
import org.chromium.base.test.UiThreadTest;
|
||||
import org.chromium.base.test.util.CommandLineFlags;
|
||||
import org.chromium.base.test.util.JniMocker;
|
||||
import org.chromium.chrome.browser.endpoint_fetcher.EndpointFetcher;
|
||||
import org.chromium.chrome.browser.endpoint_fetcher.EndpointFetcherJni;
|
||||
import org.chromium.chrome.browser.flags.ChromeFeatureList;
|
||||
import org.chromium.chrome.browser.flags.ChromeSwitches;
|
||||
import org.chromium.chrome.browser.optimization_guide.OptimizationGuideBridge;
|
||||
import org.chromium.chrome.browser.optimization_guide.OptimizationGuideBridgeJni;
|
||||
import org.chromium.chrome.browser.page_annotations.PageAnnotationsService;
|
||||
import org.chromium.chrome.browser.page_annotations.PageAnnotationsServiceFactory;
|
||||
import org.chromium.chrome.browser.profiles.Profile;
|
||||
import org.chromium.chrome.browser.tab.Tab;
|
||||
import org.chromium.chrome.test.ChromeBrowserTestRule;
|
||||
import org.chromium.chrome.test.util.browser.Features;
|
||||
import org.chromium.chrome.test.util.browser.Features.EnableFeatures;
|
||||
import org.chromium.components.optimization_guide.OptimizationGuideDecision;
|
||||
import org.chromium.components.optimization_guide.proto.HintsProto;
|
||||
import org.chromium.content_public.browser.test.util.TestThreadUtils;
|
||||
|
||||
import java.util.concurrent.Semaphore;
|
||||
@@ -58,42 +54,29 @@ public class ShoppingPersistedTabDataLegacyTest {
|
||||
@Rule
|
||||
public TestRule mProcessor = new Features.InstrumentationProcessor();
|
||||
|
||||
@Mock
|
||||
protected EndpointFetcher.Natives mEndpointFetcherJniMock;
|
||||
|
||||
@Mock
|
||||
protected OptimizationGuideBridge.Natives mOptimizationGuideBridgeJniMock;
|
||||
|
||||
@Mock
|
||||
protected Profile mProfileMock;
|
||||
|
||||
@Mock
|
||||
protected PageAnnotationsServiceFactory mServiceFactoryMock;
|
||||
|
||||
@Mock
|
||||
protected PageAnnotationsService mPageAnnotationsServiceMock;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mMocker.mock(EndpointFetcherJni.TEST_HOOKS, mEndpointFetcherJniMock);
|
||||
mMocker.mock(OptimizationGuideBridgeJni.TEST_HOOKS, mOptimizationGuideBridgeJniMock);
|
||||
// Ensure native pointer is initialized
|
||||
doReturn(1L).when(mOptimizationGuideBridgeJniMock).init();
|
||||
ShoppingPersistedTabDataTestUtils.mockOptimizationGuideResponse(
|
||||
mOptimizationGuideBridgeJniMock, OptimizationGuideDecision.TRUE);
|
||||
mOptimizationGuideBridgeJniMock,
|
||||
HintsProto.OptimizationType.SHOPPING_PAGE_PREDICTOR.getNumber(),
|
||||
OptimizationGuideDecision.TRUE, null);
|
||||
PersistedTabDataConfiguration.setUseTestConfig(true);
|
||||
|
||||
Profile.setLastUsedProfileForTesting(mProfileMock);
|
||||
doReturn(mPageAnnotationsServiceMock).when(mServiceFactoryMock).getForLastUsedProfile();
|
||||
ShoppingPersistedTabData.sPageAnnotationsServiceFactory = mServiceFactoryMock;
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
@Test
|
||||
@CommandLineFlags.
|
||||
Add({"force-fieldtrial-params=Study.Group:price_tracking_time_to_live_ms/-1000"})
|
||||
public void testShoppingPriceChange() {
|
||||
Add({"force-fieldtrial-params=Study.Group:price_tracking_time_to_live_ms/-1000/"
|
||||
+ "price_tracking_with_optimization_guide/true"})
|
||||
public void
|
||||
testShoppingPriceChange() {
|
||||
shoppingPriceChange(ShoppingPersistedTabDataTestUtils.createTabOnUiThread(
|
||||
ShoppingPersistedTabDataTestUtils.TAB_ID,
|
||||
ShoppingPersistedTabDataTestUtils.IS_INCOGNITO));
|
||||
@@ -102,8 +85,10 @@ public class ShoppingPersistedTabDataLegacyTest {
|
||||
@SmallTest
|
||||
@Test
|
||||
@CommandLineFlags.
|
||||
Add({"force-fieldtrial-params=Study.Group:price_tracking_time_to_live_ms/-1000"})
|
||||
public void testShoppingPriceChangeExtraFetchAfterChange() {
|
||||
Add({"force-fieldtrial-params=Study.Group:price_tracking_time_to_live_ms/-1000/"
|
||||
+ "price_tracking_with_optimization_guide/true"})
|
||||
public void
|
||||
testShoppingPriceChangeExtraFetchAfterChange() {
|
||||
Tab tab = ShoppingPersistedTabDataTestUtils.createTabOnUiThread(
|
||||
ShoppingPersistedTabDataTestUtils.TAB_ID,
|
||||
ShoppingPersistedTabDataTestUtils.IS_INCOGNITO);
|
||||
@@ -111,8 +96,8 @@ public class ShoppingPersistedTabDataLegacyTest {
|
||||
final Semaphore semaphore = new Semaphore(0);
|
||||
TestThreadUtils.runOnUiThreadBlocking(() -> {
|
||||
ShoppingPersistedTabData.from(tab, (shoppingPersistedTabData) -> {
|
||||
ShoppingPersistedTabDataTestUtils.verifyGetPageAnnotationsCalled(
|
||||
mPageAnnotationsServiceMock, 3);
|
||||
ShoppingPersistedTabDataTestUtils.verifyPriceTrackingOptimizationTypeCalled(
|
||||
mOptimizationGuideBridgeJniMock, 3);
|
||||
Assert.assertEquals(ShoppingPersistedTabDataTestUtils.UPDATED_PRICE_MICROS,
|
||||
shoppingPersistedTabData.getPriceMicros());
|
||||
Assert.assertEquals(ShoppingPersistedTabDataTestUtils.PRICE_MICROS,
|
||||
@@ -130,13 +115,15 @@ public class ShoppingPersistedTabDataLegacyTest {
|
||||
private long shoppingPriceChange(Tab tab) {
|
||||
final Semaphore initialSemaphore = new Semaphore(0);
|
||||
final Semaphore updateSemaphore = new Semaphore(0);
|
||||
ShoppingPersistedTabDataTestUtils.mockPageAnnotationsResponse(mPageAnnotationsServiceMock,
|
||||
ShoppingPersistedTabDataTestUtils.MockPageAnnotationsResponse
|
||||
ShoppingPersistedTabDataTestUtils.mockOptimizationGuideResponse(
|
||||
mOptimizationGuideBridgeJniMock,
|
||||
HintsProto.OptimizationType.PRICE_TRACKING.getNumber(),
|
||||
ShoppingPersistedTabDataTestUtils.MockPriceTrackingResponse
|
||||
.BUYABLE_PRODUCT_INITIAL);
|
||||
TestThreadUtils.runOnUiThreadBlocking(() -> {
|
||||
ShoppingPersistedTabData.from(tab, (shoppingPersistedTabData) -> {
|
||||
ShoppingPersistedTabDataTestUtils.verifyGetPageAnnotationsCalled(
|
||||
mPageAnnotationsServiceMock, 1);
|
||||
ShoppingPersistedTabDataTestUtils.verifyPriceTrackingOptimizationTypeCalled(
|
||||
mOptimizationGuideBridgeJniMock, 1);
|
||||
Assert.assertEquals(ShoppingPersistedTabDataTestUtils.PRICE_MICROS,
|
||||
shoppingPersistedTabData.getPriceMicros());
|
||||
Assert.assertEquals(ShoppingPersistedTabDataTestUtils.UNITED_STATES_CURRENCY_CODE,
|
||||
@@ -150,13 +137,15 @@ public class ShoppingPersistedTabDataLegacyTest {
|
||||
});
|
||||
ShoppingPersistedTabDataTestUtils.acquireSemaphore(initialSemaphore);
|
||||
long firstUpdateTime = ShoppingPersistedTabDataTestUtils.getTimeLastUpdatedOnUiThread(tab);
|
||||
ShoppingPersistedTabDataTestUtils.mockPageAnnotationsResponse(mPageAnnotationsServiceMock,
|
||||
ShoppingPersistedTabDataTestUtils.MockPageAnnotationsResponse
|
||||
ShoppingPersistedTabDataTestUtils.mockOptimizationGuideResponse(
|
||||
mOptimizationGuideBridgeJniMock,
|
||||
HintsProto.OptimizationType.PRICE_TRACKING.getNumber(),
|
||||
ShoppingPersistedTabDataTestUtils.MockPriceTrackingResponse
|
||||
.BUYABLE_PRODUCT_PRICE_UPDATED);
|
||||
TestThreadUtils.runOnUiThreadBlocking(() -> {
|
||||
ShoppingPersistedTabData.from(tab, (updatedShoppingPersistedTabData) -> {
|
||||
ShoppingPersistedTabDataTestUtils.verifyGetPageAnnotationsCalled(
|
||||
mPageAnnotationsServiceMock, 2);
|
||||
ShoppingPersistedTabDataTestUtils.verifyPriceTrackingOptimizationTypeCalled(
|
||||
mOptimizationGuideBridgeJniMock, 2);
|
||||
Assert.assertEquals(ShoppingPersistedTabDataTestUtils.UPDATED_PRICE_MICROS,
|
||||
updatedShoppingPersistedTabData.getPriceMicros());
|
||||
Assert.assertEquals(ShoppingPersistedTabDataTestUtils.PRICE_MICROS,
|
||||
@@ -172,6 +161,8 @@ public class ShoppingPersistedTabDataLegacyTest {
|
||||
@UiThreadTest
|
||||
@SmallTest
|
||||
@Test
|
||||
@CommandLineFlags.
|
||||
Add({"force-fieldtrial-params=Study.Group:price_tracking_with_optimization_guide/true"})
|
||||
public void testNoRefetch() {
|
||||
final Semaphore initialSemaphore = new Semaphore(0);
|
||||
final Semaphore updateSemaphore = new Semaphore(0);
|
||||
@@ -179,8 +170,10 @@ public class ShoppingPersistedTabDataLegacyTest {
|
||||
ShoppingPersistedTabDataTestUtils.TAB_ID,
|
||||
ShoppingPersistedTabDataTestUtils.IS_INCOGNITO);
|
||||
// Mock annotations response.
|
||||
ShoppingPersistedTabDataTestUtils.mockPageAnnotationsResponse(mPageAnnotationsServiceMock,
|
||||
ShoppingPersistedTabDataTestUtils.MockPageAnnotationsResponse
|
||||
ShoppingPersistedTabDataTestUtils.mockOptimizationGuideResponse(
|
||||
mOptimizationGuideBridgeJniMock,
|
||||
HintsProto.OptimizationType.PRICE_TRACKING.getNumber(),
|
||||
ShoppingPersistedTabDataTestUtils.MockPriceTrackingResponse
|
||||
.BUYABLE_PRODUCT_INITIAL);
|
||||
|
||||
TestThreadUtils.runOnUiThreadBlocking(() -> {
|
||||
@@ -197,10 +190,12 @@ public class ShoppingPersistedTabDataLegacyTest {
|
||||
});
|
||||
});
|
||||
ShoppingPersistedTabDataTestUtils.acquireSemaphore(initialSemaphore);
|
||||
ShoppingPersistedTabDataTestUtils.verifyGetPageAnnotationsCalled(
|
||||
mPageAnnotationsServiceMock, 1);
|
||||
ShoppingPersistedTabDataTestUtils.mockPageAnnotationsResponse(mPageAnnotationsServiceMock,
|
||||
ShoppingPersistedTabDataTestUtils.MockPageAnnotationsResponse
|
||||
ShoppingPersistedTabDataTestUtils.verifyPriceTrackingOptimizationTypeCalled(
|
||||
mOptimizationGuideBridgeJniMock, 1);
|
||||
ShoppingPersistedTabDataTestUtils.mockOptimizationGuideResponse(
|
||||
mOptimizationGuideBridgeJniMock,
|
||||
HintsProto.OptimizationType.PRICE_TRACKING.getNumber(),
|
||||
ShoppingPersistedTabDataTestUtils.MockPriceTrackingResponse
|
||||
.BUYABLE_PRODUCT_PRICE_UPDATED);
|
||||
TestThreadUtils.runOnUiThreadBlocking(() -> {
|
||||
ShoppingPersistedTabData.from(tab, (shoppingPersistedTabData) -> {
|
||||
@@ -217,8 +212,8 @@ public class ShoppingPersistedTabDataLegacyTest {
|
||||
ShoppingPersistedTabDataTestUtils.acquireSemaphore(updateSemaphore);
|
||||
// PageAnnotationsService should not have been called a second time - because we haven't
|
||||
// passed the time to live
|
||||
ShoppingPersistedTabDataTestUtils.verifyGetPageAnnotationsCalled(
|
||||
mPageAnnotationsServiceMock, 1);
|
||||
ShoppingPersistedTabDataTestUtils.verifyPriceTrackingOptimizationTypeCalled(
|
||||
mOptimizationGuideBridgeJniMock, 1);
|
||||
}
|
||||
|
||||
@UiThreadTest
|
||||
|
231
chrome/android/javatests/src/org/chromium/chrome/browser/tab/state/ShoppingPersistedTabDataLegacyWithPASTest.java
Normal file
231
chrome/android/javatests/src/org/chromium/chrome/browser/tab/state/ShoppingPersistedTabDataLegacyWithPASTest.java
Normal file
@@ -0,0 +1,231 @@
|
||||
// Copyright 2021 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.tab.state;
|
||||
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
|
||||
import android.support.test.filters.SmallTest;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TestRule;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import org.chromium.base.test.BaseJUnit4ClassRunner;
|
||||
import org.chromium.base.test.UiThreadTest;
|
||||
import org.chromium.base.test.util.CommandLineFlags;
|
||||
import org.chromium.base.test.util.JniMocker;
|
||||
import org.chromium.chrome.browser.endpoint_fetcher.EndpointFetcher;
|
||||
import org.chromium.chrome.browser.endpoint_fetcher.EndpointFetcherJni;
|
||||
import org.chromium.chrome.browser.flags.ChromeFeatureList;
|
||||
import org.chromium.chrome.browser.flags.ChromeSwitches;
|
||||
import org.chromium.chrome.browser.optimization_guide.OptimizationGuideBridge;
|
||||
import org.chromium.chrome.browser.optimization_guide.OptimizationGuideBridgeJni;
|
||||
import org.chromium.chrome.browser.page_annotations.PageAnnotationsService;
|
||||
import org.chromium.chrome.browser.page_annotations.PageAnnotationsServiceFactory;
|
||||
import org.chromium.chrome.browser.profiles.Profile;
|
||||
import org.chromium.chrome.browser.tab.Tab;
|
||||
import org.chromium.chrome.test.ChromeBrowserTestRule;
|
||||
import org.chromium.chrome.test.util.browser.Features;
|
||||
import org.chromium.chrome.test.util.browser.Features.EnableFeatures;
|
||||
import org.chromium.components.optimization_guide.OptimizationGuideDecision;
|
||||
import org.chromium.components.optimization_guide.proto.HintsProto;
|
||||
import org.chromium.content_public.browser.test.util.TestThreadUtils;
|
||||
|
||||
import java.util.concurrent.Semaphore;
|
||||
/**
|
||||
* Legacy test relating to {@link ShoppingPersistedTabData} and {@link PageAnnotationService}
|
||||
*/
|
||||
@RunWith(BaseJUnit4ClassRunner.class)
|
||||
@EnableFeatures({ChromeFeatureList.TAB_GRID_LAYOUT_ANDROID + "<Study"})
|
||||
@CommandLineFlags.
|
||||
Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE, "force-fieldtrials=Study/Group"})
|
||||
public class ShoppingPersistedTabDataLegacyWithPASTest {
|
||||
@Rule
|
||||
public final ChromeBrowserTestRule mBrowserTestRule = new ChromeBrowserTestRule();
|
||||
|
||||
@Rule
|
||||
public JniMocker mMocker = new JniMocker();
|
||||
|
||||
@Rule
|
||||
public TestRule mProcessor = new Features.InstrumentationProcessor();
|
||||
|
||||
@Mock
|
||||
protected EndpointFetcher.Natives mEndpointFetcherJniMock;
|
||||
|
||||
@Mock
|
||||
protected OptimizationGuideBridge.Natives mOptimizationGuideBridgeJniMock;
|
||||
|
||||
@Mock
|
||||
protected Profile mProfileMock;
|
||||
|
||||
@Mock
|
||||
protected PageAnnotationsServiceFactory mServiceFactoryMock;
|
||||
|
||||
@Mock
|
||||
protected PageAnnotationsService mPageAnnotationsServiceMock;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mMocker.mock(EndpointFetcherJni.TEST_HOOKS, mEndpointFetcherJniMock);
|
||||
mMocker.mock(OptimizationGuideBridgeJni.TEST_HOOKS, mOptimizationGuideBridgeJniMock);
|
||||
// Ensure native pointer is initialized
|
||||
doReturn(1L).when(mOptimizationGuideBridgeJniMock).init();
|
||||
ShoppingPersistedTabDataTestUtils.mockOptimizationGuideResponse(
|
||||
mOptimizationGuideBridgeJniMock,
|
||||
HintsProto.OptimizationType.SHOPPING_PAGE_PREDICTOR.getNumber(),
|
||||
OptimizationGuideDecision.TRUE, null);
|
||||
PersistedTabDataConfiguration.setUseTestConfig(true);
|
||||
|
||||
Profile.setLastUsedProfileForTesting(mProfileMock);
|
||||
doReturn(mPageAnnotationsServiceMock).when(mServiceFactoryMock).getForLastUsedProfile();
|
||||
ShoppingPersistedTabData.sPageAnnotationsServiceFactory = mServiceFactoryMock;
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
@Test
|
||||
@CommandLineFlags.
|
||||
Add({"force-fieldtrial-params=Study.Group:price_tracking_time_to_live_ms/-1000/"
|
||||
+ "price_tracking_with_optimization_guide/false"})
|
||||
public void
|
||||
testShoppingPriceChange() {
|
||||
shoppingPriceChange(ShoppingPersistedTabDataTestUtils.createTabOnUiThread(
|
||||
ShoppingPersistedTabDataTestUtils.TAB_ID,
|
||||
ShoppingPersistedTabDataTestUtils.IS_INCOGNITO));
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
@Test
|
||||
@CommandLineFlags.
|
||||
Add({"force-fieldtrial-params=Study.Group:price_tracking_time_to_live_ms/-1000/"
|
||||
+ "price_tracking_with_optimization_guide/false"})
|
||||
public void
|
||||
testShoppingPriceChangeExtraFetchAfterChange() {
|
||||
Tab tab = ShoppingPersistedTabDataTestUtils.createTabOnUiThread(
|
||||
ShoppingPersistedTabDataTestUtils.TAB_ID,
|
||||
ShoppingPersistedTabDataTestUtils.IS_INCOGNITO);
|
||||
long mLastPriceChangeTimeMs = shoppingPriceChange(tab);
|
||||
final Semaphore semaphore = new Semaphore(0);
|
||||
TestThreadUtils.runOnUiThreadBlocking(() -> {
|
||||
ShoppingPersistedTabData.from(tab, (shoppingPersistedTabData) -> {
|
||||
ShoppingPersistedTabDataWithPASTestUtils.verifyGetPageAnnotationsCalled(
|
||||
mPageAnnotationsServiceMock, 3);
|
||||
Assert.assertEquals(ShoppingPersistedTabDataTestUtils.UPDATED_PRICE_MICROS,
|
||||
shoppingPersistedTabData.getPriceMicros());
|
||||
Assert.assertEquals(ShoppingPersistedTabDataTestUtils.PRICE_MICROS,
|
||||
shoppingPersistedTabData.getPreviousPriceMicros());
|
||||
Assert.assertEquals(mLastPriceChangeTimeMs,
|
||||
shoppingPersistedTabData.getLastPriceChangeTimeMs());
|
||||
Assert.assertEquals(ShoppingPersistedTabDataTestUtils.UNITED_STATES_CURRENCY_CODE,
|
||||
shoppingPersistedTabData.getCurrencyCode());
|
||||
semaphore.release();
|
||||
});
|
||||
});
|
||||
ShoppingPersistedTabDataTestUtils.acquireSemaphore(semaphore);
|
||||
}
|
||||
|
||||
private long shoppingPriceChange(Tab tab) {
|
||||
final Semaphore initialSemaphore = new Semaphore(0);
|
||||
final Semaphore updateSemaphore = new Semaphore(0);
|
||||
ShoppingPersistedTabDataWithPASTestUtils.mockPageAnnotationsResponse(
|
||||
mPageAnnotationsServiceMock,
|
||||
ShoppingPersistedTabDataWithPASTestUtils.MockPageAnnotationsResponse
|
||||
.BUYABLE_PRODUCT_INITIAL);
|
||||
TestThreadUtils.runOnUiThreadBlocking(() -> {
|
||||
ShoppingPersistedTabData.from(tab, (shoppingPersistedTabData) -> {
|
||||
ShoppingPersistedTabDataWithPASTestUtils.verifyGetPageAnnotationsCalled(
|
||||
mPageAnnotationsServiceMock, 1);
|
||||
Assert.assertEquals(ShoppingPersistedTabDataTestUtils.PRICE_MICROS,
|
||||
shoppingPersistedTabData.getPriceMicros());
|
||||
Assert.assertEquals(ShoppingPersistedTabDataTestUtils.UNITED_STATES_CURRENCY_CODE,
|
||||
shoppingPersistedTabData.getCurrencyCode());
|
||||
Assert.assertEquals(ShoppingPersistedTabData.NO_PRICE_KNOWN,
|
||||
shoppingPersistedTabData.getPreviousPriceMicros());
|
||||
Assert.assertEquals(ShoppingPersistedTabData.NO_TRANSITIONS_OCCURRED,
|
||||
shoppingPersistedTabData.getLastPriceChangeTimeMs());
|
||||
initialSemaphore.release();
|
||||
});
|
||||
});
|
||||
ShoppingPersistedTabDataTestUtils.acquireSemaphore(initialSemaphore);
|
||||
long firstUpdateTime = ShoppingPersistedTabDataTestUtils.getTimeLastUpdatedOnUiThread(tab);
|
||||
ShoppingPersistedTabDataWithPASTestUtils.mockPageAnnotationsResponse(
|
||||
mPageAnnotationsServiceMock,
|
||||
ShoppingPersistedTabDataWithPASTestUtils.MockPageAnnotationsResponse
|
||||
.BUYABLE_PRODUCT_PRICE_UPDATED);
|
||||
TestThreadUtils.runOnUiThreadBlocking(() -> {
|
||||
ShoppingPersistedTabData.from(tab, (updatedShoppingPersistedTabData) -> {
|
||||
ShoppingPersistedTabDataWithPASTestUtils.verifyGetPageAnnotationsCalled(
|
||||
mPageAnnotationsServiceMock, 2);
|
||||
Assert.assertEquals(ShoppingPersistedTabDataTestUtils.UPDATED_PRICE_MICROS,
|
||||
updatedShoppingPersistedTabData.getPriceMicros());
|
||||
Assert.assertEquals(ShoppingPersistedTabDataTestUtils.PRICE_MICROS,
|
||||
updatedShoppingPersistedTabData.getPreviousPriceMicros());
|
||||
Assert.assertTrue(firstUpdateTime
|
||||
< updatedShoppingPersistedTabData.getLastPriceChangeTimeMs());
|
||||
updateSemaphore.release();
|
||||
});
|
||||
});
|
||||
ShoppingPersistedTabDataTestUtils.acquireSemaphore(updateSemaphore);
|
||||
return ShoppingPersistedTabDataTestUtils.getTimeLastUpdatedOnUiThread(tab);
|
||||
}
|
||||
@UiThreadTest
|
||||
@SmallTest
|
||||
@Test
|
||||
public void testNoRefetch() {
|
||||
final Semaphore initialSemaphore = new Semaphore(0);
|
||||
final Semaphore updateSemaphore = new Semaphore(0);
|
||||
Tab tab = ShoppingPersistedTabDataTestUtils.createTabOnUiThread(
|
||||
ShoppingPersistedTabDataTestUtils.TAB_ID,
|
||||
ShoppingPersistedTabDataTestUtils.IS_INCOGNITO);
|
||||
// Mock annotations response.
|
||||
ShoppingPersistedTabDataWithPASTestUtils.mockPageAnnotationsResponse(
|
||||
mPageAnnotationsServiceMock,
|
||||
ShoppingPersistedTabDataWithPASTestUtils.MockPageAnnotationsResponse
|
||||
.BUYABLE_PRODUCT_INITIAL);
|
||||
|
||||
TestThreadUtils.runOnUiThreadBlocking(() -> {
|
||||
ShoppingPersistedTabData.from(tab, (shoppingPersistedTabData) -> {
|
||||
Assert.assertEquals(ShoppingPersistedTabDataTestUtils.PRICE_MICROS,
|
||||
shoppingPersistedTabData.getPriceMicros());
|
||||
Assert.assertEquals(ShoppingPersistedTabData.NO_PRICE_KNOWN,
|
||||
shoppingPersistedTabData.getPreviousPriceMicros());
|
||||
Assert.assertEquals(ShoppingPersistedTabDataTestUtils.UNITED_STATES_CURRENCY_CODE,
|
||||
shoppingPersistedTabData.getCurrencyCode());
|
||||
// By setting time to live to be a negative number, an update
|
||||
// will be forced in the subsequent call
|
||||
initialSemaphore.release();
|
||||
});
|
||||
});
|
||||
ShoppingPersistedTabDataTestUtils.acquireSemaphore(initialSemaphore);
|
||||
ShoppingPersistedTabDataWithPASTestUtils.verifyGetPageAnnotationsCalled(
|
||||
mPageAnnotationsServiceMock, 1);
|
||||
ShoppingPersistedTabDataWithPASTestUtils.mockPageAnnotationsResponse(
|
||||
mPageAnnotationsServiceMock,
|
||||
ShoppingPersistedTabDataWithPASTestUtils.MockPageAnnotationsResponse
|
||||
.BUYABLE_PRODUCT_PRICE_UPDATED);
|
||||
TestThreadUtils.runOnUiThreadBlocking(() -> {
|
||||
ShoppingPersistedTabData.from(tab, (shoppingPersistedTabData) -> {
|
||||
Assert.assertEquals(ShoppingPersistedTabDataTestUtils.PRICE_MICROS,
|
||||
shoppingPersistedTabData.getPriceMicros());
|
||||
Assert.assertEquals(ShoppingPersistedTabData.NO_PRICE_KNOWN,
|
||||
shoppingPersistedTabData.getPreviousPriceMicros());
|
||||
|
||||
// By setting time to live to be a negative number, an update
|
||||
// will be forced in the subsequent call
|
||||
updateSemaphore.release();
|
||||
});
|
||||
});
|
||||
ShoppingPersistedTabDataTestUtils.acquireSemaphore(updateSemaphore);
|
||||
// PageAnnotationsService should not have been called a second time - because we haven't
|
||||
// passed the time to live
|
||||
ShoppingPersistedTabDataWithPASTestUtils.verifyGetPageAnnotationsCalled(
|
||||
mPageAnnotationsServiceMock, 1);
|
||||
}
|
||||
}
|
149
chrome/android/javatests/src/org/chromium/chrome/browser/tab/state/ShoppingPersistedTabDataTest.java
149
chrome/android/javatests/src/org/chromium/chrome/browser/tab/state/ShoppingPersistedTabDataTest.java
@@ -28,8 +28,6 @@ import org.chromium.chrome.browser.flags.ChromeFeatureList;
|
||||
import org.chromium.chrome.browser.flags.ChromeSwitches;
|
||||
import org.chromium.chrome.browser.optimization_guide.OptimizationGuideBridge;
|
||||
import org.chromium.chrome.browser.optimization_guide.OptimizationGuideBridgeJni;
|
||||
import org.chromium.chrome.browser.page_annotations.PageAnnotationsService;
|
||||
import org.chromium.chrome.browser.page_annotations.PageAnnotationsServiceFactory;
|
||||
import org.chromium.chrome.browser.profiles.Profile;
|
||||
import org.chromium.chrome.browser.tab.MockTab;
|
||||
import org.chromium.chrome.browser.tab.Tab;
|
||||
@@ -37,6 +35,7 @@ import org.chromium.chrome.test.ChromeBrowserTestRule;
|
||||
import org.chromium.chrome.test.util.browser.Features;
|
||||
import org.chromium.chrome.test.util.browser.Features.EnableFeatures;
|
||||
import org.chromium.components.optimization_guide.OptimizationGuideDecision;
|
||||
import org.chromium.components.optimization_guide.proto.HintsProto;
|
||||
import org.chromium.content_public.browser.test.util.TestThreadUtils;
|
||||
|
||||
import java.util.concurrent.Semaphore;
|
||||
@@ -68,12 +67,6 @@ public class ShoppingPersistedTabDataTest {
|
||||
@Mock
|
||||
protected Profile mProfileMock;
|
||||
|
||||
@Mock
|
||||
protected PageAnnotationsServiceFactory mServiceFactoryMock;
|
||||
|
||||
@Mock
|
||||
protected PageAnnotationsService mPageAnnotationsServiceMock;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
@@ -82,12 +75,11 @@ public class ShoppingPersistedTabDataTest {
|
||||
// Ensure native pointer is initialized
|
||||
doReturn(1L).when(mOptimizationGuideBridgeJniMock).init();
|
||||
ShoppingPersistedTabDataTestUtils.mockOptimizationGuideResponse(
|
||||
mOptimizationGuideBridgeJniMock, OptimizationGuideDecision.TRUE);
|
||||
mOptimizationGuideBridgeJniMock,
|
||||
HintsProto.OptimizationType.SHOPPING_PAGE_PREDICTOR.getNumber(),
|
||||
OptimizationGuideDecision.TRUE, null);
|
||||
PersistedTabDataConfiguration.setUseTestConfig(true);
|
||||
|
||||
Profile.setLastUsedProfileForTesting(mProfileMock);
|
||||
doReturn(mPageAnnotationsServiceMock).when(mServiceFactoryMock).getForLastUsedProfile();
|
||||
ShoppingPersistedTabData.sPageAnnotationsServiceFactory = mServiceFactoryMock;
|
||||
}
|
||||
|
||||
@UiThreadTest
|
||||
@@ -114,11 +106,15 @@ public class ShoppingPersistedTabDataTest {
|
||||
@SmallTest
|
||||
@Test
|
||||
public void testShoppingBloomFilterNotShoppingWebsite() {
|
||||
ShoppingPersistedTabDataTestUtils.mockPageAnnotationsResponse(mPageAnnotationsServiceMock,
|
||||
ShoppingPersistedTabDataTestUtils.MockPageAnnotationsResponse
|
||||
ShoppingPersistedTabDataTestUtils.mockOptimizationGuideResponse(
|
||||
mOptimizationGuideBridgeJniMock,
|
||||
HintsProto.OptimizationType.PRICE_TRACKING.getNumber(),
|
||||
ShoppingPersistedTabDataTestUtils.MockPriceTrackingResponse
|
||||
.BUYABLE_PRODUCT_INITIAL);
|
||||
ShoppingPersistedTabDataTestUtils.mockOptimizationGuideResponse(
|
||||
mOptimizationGuideBridgeJniMock, OptimizationGuideDecision.FALSE);
|
||||
mOptimizationGuideBridgeJniMock,
|
||||
HintsProto.OptimizationType.SHOPPING_PAGE_PREDICTOR.getNumber(),
|
||||
OptimizationGuideDecision.FALSE, null);
|
||||
Tab tab = ShoppingPersistedTabDataTestUtils.createTabOnUiThread(
|
||||
ShoppingPersistedTabDataTestUtils.TAB_ID,
|
||||
ShoppingPersistedTabDataTestUtils.IS_INCOGNITO);
|
||||
@@ -128,21 +124,26 @@ public class ShoppingPersistedTabDataTest {
|
||||
tab, (shoppingPersistedTabData) -> { semaphore.release(); });
|
||||
});
|
||||
ShoppingPersistedTabDataTestUtils.acquireSemaphore(semaphore);
|
||||
ShoppingPersistedTabDataTestUtils.verifyGetPageAnnotationsCalled(
|
||||
mPageAnnotationsServiceMock, 0);
|
||||
ShoppingPersistedTabDataTestUtils.verifyPriceTrackingOptimizationTypeCalled(
|
||||
mOptimizationGuideBridgeJniMock, 0);
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
@Test
|
||||
@CommandLineFlags.
|
||||
Add({"force-fieldtrial-params=Study.Group:price_tracking_with_optimization_guide/true"})
|
||||
public void testShoppingBloomFilterShoppingWebsite() {
|
||||
for (@OptimizationGuideDecision int decision :
|
||||
new int[] {OptimizationGuideDecision.TRUE, OptimizationGuideDecision.UNKNOWN}) {
|
||||
ShoppingPersistedTabDataTestUtils.mockPageAnnotationsResponse(
|
||||
mPageAnnotationsServiceMock,
|
||||
ShoppingPersistedTabDataTestUtils.MockPageAnnotationsResponse
|
||||
ShoppingPersistedTabDataTestUtils.mockOptimizationGuideResponse(
|
||||
mOptimizationGuideBridgeJniMock,
|
||||
HintsProto.OptimizationType.PRICE_TRACKING.getNumber(),
|
||||
ShoppingPersistedTabDataTestUtils.MockPriceTrackingResponse
|
||||
.BUYABLE_PRODUCT_INITIAL);
|
||||
ShoppingPersistedTabDataTestUtils.mockOptimizationGuideResponse(
|
||||
mOptimizationGuideBridgeJniMock, decision);
|
||||
mOptimizationGuideBridgeJniMock,
|
||||
HintsProto.OptimizationType.SHOPPING_PAGE_PREDICTOR.getNumber(), decision,
|
||||
null);
|
||||
Tab tab = ShoppingPersistedTabDataTestUtils.createTabOnUiThread(
|
||||
ShoppingPersistedTabDataTestUtils.TAB_ID,
|
||||
ShoppingPersistedTabDataTestUtils.IS_INCOGNITO);
|
||||
@@ -152,8 +153,8 @@ public class ShoppingPersistedTabDataTest {
|
||||
tab, (shoppingPersistedTabData) -> { semaphore.release(); });
|
||||
});
|
||||
ShoppingPersistedTabDataTestUtils.acquireSemaphore(semaphore);
|
||||
ShoppingPersistedTabDataTestUtils.verifyGetPageAnnotationsCalled(
|
||||
mPageAnnotationsServiceMock, 1);
|
||||
ShoppingPersistedTabDataTestUtils.verifyPriceTrackingOptimizationTypeCalled(
|
||||
mOptimizationGuideBridgeJniMock, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,15 +172,17 @@ public class ShoppingPersistedTabDataTest {
|
||||
tab, (shoppingPersistedTabData) -> { semaphore.release(); });
|
||||
});
|
||||
ShoppingPersistedTabDataTestUtils.acquireSemaphore(semaphore);
|
||||
ShoppingPersistedTabDataTestUtils.verifyGetPageAnnotationsCalled(
|
||||
mPageAnnotationsServiceMock, 0);
|
||||
ShoppingPersistedTabDataTestUtils.verifyPriceTrackingOptimizationTypeCalled(
|
||||
mOptimizationGuideBridgeJniMock, 0);
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
@Test
|
||||
@CommandLineFlags.
|
||||
Add({"force-fieldtrial-params=Study.Group:price_tracking_stale_tab_threshold_seconds/86400"})
|
||||
public void test2DayTabWithStaleOverride1day() {
|
||||
Add({"force-fieldtrial-params=Study.Group:price_tracking_stale_tab_threshold_seconds/86400/"
|
||||
+ "price_tracking_with_optimization_guide/true"})
|
||||
public void
|
||||
test2DayTabWithStaleOverride1day() {
|
||||
Tab tab = ShoppingPersistedTabDataTestUtils.createTabOnUiThread(
|
||||
ShoppingPersistedTabDataTestUtils.TAB_ID,
|
||||
ShoppingPersistedTabDataTestUtils.IS_INCOGNITO);
|
||||
@@ -191,20 +194,26 @@ public class ShoppingPersistedTabDataTest {
|
||||
tab, (shoppingPersistedTabData) -> { semaphore.release(); });
|
||||
});
|
||||
ShoppingPersistedTabDataTestUtils.acquireSemaphore(semaphore);
|
||||
ShoppingPersistedTabDataTestUtils.verifyGetPageAnnotationsCalled(
|
||||
mPageAnnotationsServiceMock, 0);
|
||||
ShoppingPersistedTabDataTestUtils.verifyPriceTrackingOptimizationTypeCalled(
|
||||
mOptimizationGuideBridgeJniMock, 0);
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
@Test
|
||||
@CommandLineFlags.
|
||||
Add({"force-fieldtrial-params=Study.Group:price_tracking_stale_tab_threshold_seconds/86400"})
|
||||
public void testHalfDayTabWithStaleOverride1day() {
|
||||
ShoppingPersistedTabDataTestUtils.mockPageAnnotationsResponse(mPageAnnotationsServiceMock,
|
||||
ShoppingPersistedTabDataTestUtils.MockPageAnnotationsResponse
|
||||
Add({"force-fieldtrial-params=Study.Group:price_tracking_stale_tab_threshold_seconds/86400/"
|
||||
+ "price_tracking_with_optimization_guide/true"})
|
||||
public void
|
||||
testHalfDayTabWithStaleOverride1day() {
|
||||
ShoppingPersistedTabDataTestUtils.mockOptimizationGuideResponse(
|
||||
mOptimizationGuideBridgeJniMock,
|
||||
HintsProto.OptimizationType.PRICE_TRACKING.getNumber(),
|
||||
ShoppingPersistedTabDataTestUtils.MockPriceTrackingResponse
|
||||
.BUYABLE_PRODUCT_INITIAL);
|
||||
ShoppingPersistedTabDataTestUtils.mockOptimizationGuideResponse(
|
||||
mOptimizationGuideBridgeJniMock, OptimizationGuideDecision.TRUE);
|
||||
mOptimizationGuideBridgeJniMock,
|
||||
HintsProto.OptimizationType.SHOPPING_PAGE_PREDICTOR.getNumber(),
|
||||
OptimizationGuideDecision.TRUE, null);
|
||||
Tab tab = ShoppingPersistedTabDataTestUtils.createTabOnUiThread(
|
||||
ShoppingPersistedTabDataTestUtils.TAB_ID,
|
||||
ShoppingPersistedTabDataTestUtils.IS_INCOGNITO);
|
||||
@@ -216,24 +225,26 @@ public class ShoppingPersistedTabDataTest {
|
||||
tab, (shoppingPersistedTabData) -> { semaphore.release(); });
|
||||
});
|
||||
ShoppingPersistedTabDataTestUtils.acquireSemaphore(semaphore);
|
||||
ShoppingPersistedTabDataTestUtils.verifyGetPageAnnotationsCalled(
|
||||
mPageAnnotationsServiceMock, 1);
|
||||
ShoppingPersistedTabDataTestUtils.verifyPriceTrackingOptimizationTypeCalled(
|
||||
mOptimizationGuideBridgeJniMock, 1);
|
||||
}
|
||||
|
||||
@UiThreadTest
|
||||
@SmallTest
|
||||
@Test
|
||||
@CommandLineFlags.
|
||||
Add({"force-fieldtrial-params=Study.Group:price_tracking_with_optimization_guide/true"})
|
||||
public void testNoRefetch() {
|
||||
final Semaphore initialSemaphore = new Semaphore(0);
|
||||
final Semaphore updateSemaphore = new Semaphore(0);
|
||||
Tab tab = ShoppingPersistedTabDataTestUtils.createTabOnUiThread(
|
||||
ShoppingPersistedTabDataTestUtils.TAB_ID,
|
||||
ShoppingPersistedTabDataTestUtils.IS_INCOGNITO);
|
||||
// Mock annotations response.
|
||||
ShoppingPersistedTabDataTestUtils.mockPageAnnotationsResponse(mPageAnnotationsServiceMock,
|
||||
ShoppingPersistedTabDataTestUtils.MockPageAnnotationsResponse
|
||||
ShoppingPersistedTabDataTestUtils.mockOptimizationGuideResponse(
|
||||
mOptimizationGuideBridgeJniMock,
|
||||
HintsProto.OptimizationType.PRICE_TRACKING.getNumber(),
|
||||
ShoppingPersistedTabDataTestUtils.MockPriceTrackingResponse
|
||||
.BUYABLE_PRODUCT_INITIAL);
|
||||
|
||||
TestThreadUtils.runOnUiThreadBlocking(() -> {
|
||||
ShoppingPersistedTabData.from(tab, (shoppingPersistedTabData) -> {
|
||||
Assert.assertEquals(ShoppingPersistedTabDataTestUtils.PRICE_MICROS,
|
||||
@@ -248,10 +259,12 @@ public class ShoppingPersistedTabDataTest {
|
||||
});
|
||||
});
|
||||
ShoppingPersistedTabDataTestUtils.acquireSemaphore(initialSemaphore);
|
||||
ShoppingPersistedTabDataTestUtils.verifyGetPageAnnotationsCalled(
|
||||
mPageAnnotationsServiceMock, 1);
|
||||
ShoppingPersistedTabDataTestUtils.mockPageAnnotationsResponse(mPageAnnotationsServiceMock,
|
||||
ShoppingPersistedTabDataTestUtils.MockPageAnnotationsResponse
|
||||
ShoppingPersistedTabDataTestUtils.verifyPriceTrackingOptimizationTypeCalled(
|
||||
mOptimizationGuideBridgeJniMock, 1);
|
||||
ShoppingPersistedTabDataTestUtils.mockOptimizationGuideResponse(
|
||||
mOptimizationGuideBridgeJniMock,
|
||||
HintsProto.OptimizationType.PRICE_TRACKING.getNumber(),
|
||||
ShoppingPersistedTabDataTestUtils.MockPriceTrackingResponse
|
||||
.BUYABLE_PRODUCT_PRICE_UPDATED);
|
||||
TestThreadUtils.runOnUiThreadBlocking(() -> {
|
||||
ShoppingPersistedTabData.from(tab, (shoppingPersistedTabData) -> {
|
||||
@@ -268,8 +281,8 @@ public class ShoppingPersistedTabDataTest {
|
||||
ShoppingPersistedTabDataTestUtils.acquireSemaphore(updateSemaphore);
|
||||
// EndpointFetcher should not have been called a second time - because we haven't passed the
|
||||
// time to live
|
||||
ShoppingPersistedTabDataTestUtils.verifyGetPageAnnotationsCalled(
|
||||
mPageAnnotationsServiceMock, 1);
|
||||
ShoppingPersistedTabDataTestUtils.verifyPriceTrackingOptimizationTypeCalled(
|
||||
mOptimizationGuideBridgeJniMock, 1);
|
||||
}
|
||||
|
||||
@UiThreadTest
|
||||
@@ -416,13 +429,17 @@ public class ShoppingPersistedTabDataTest {
|
||||
@UiThreadTest
|
||||
@SmallTest
|
||||
@Test
|
||||
@CommandLineFlags.
|
||||
Add({"force-fieldtrial-params=Study.Group:price_tracking_with_optimization_guide/true"})
|
||||
public void testSPTDSavingEnabledUponSuccessfulResponse() {
|
||||
final Semaphore semaphore = new Semaphore(0);
|
||||
Tab tab = ShoppingPersistedTabDataTestUtils.createTabOnUiThread(
|
||||
ShoppingPersistedTabDataTestUtils.TAB_ID,
|
||||
ShoppingPersistedTabDataTestUtils.IS_INCOGNITO);
|
||||
ShoppingPersistedTabDataTestUtils.mockPageAnnotationsResponse(mPageAnnotationsServiceMock,
|
||||
ShoppingPersistedTabDataTestUtils.MockPageAnnotationsResponse
|
||||
ShoppingPersistedTabDataTestUtils.mockOptimizationGuideResponse(
|
||||
mOptimizationGuideBridgeJniMock,
|
||||
HintsProto.OptimizationType.PRICE_TRACKING.getNumber(),
|
||||
ShoppingPersistedTabDataTestUtils.MockPriceTrackingResponse
|
||||
.BUYABLE_PRODUCT_INITIAL);
|
||||
TestThreadUtils.runOnUiThreadBlocking(() -> {
|
||||
ShoppingPersistedTabData.from(tab, (shoppingPersistedTabData) -> {
|
||||
@@ -436,13 +453,17 @@ public class ShoppingPersistedTabDataTest {
|
||||
@UiThreadTest
|
||||
@SmallTest
|
||||
@Test
|
||||
@CommandLineFlags.
|
||||
Add({"force-fieldtrial-params=Study.Group:price_tracking_with_optimization_guide/true"})
|
||||
public void testSPTDSavingEnabledUponSuccessfulProductUpdateResponse() {
|
||||
final Semaphore semaphore = new Semaphore(0);
|
||||
Tab tab = ShoppingPersistedTabDataTestUtils.createTabOnUiThread(
|
||||
ShoppingPersistedTabDataTestUtils.TAB_ID,
|
||||
ShoppingPersistedTabDataTestUtils.IS_INCOGNITO);
|
||||
ShoppingPersistedTabDataTestUtils.mockPageAnnotationsResponse(mPageAnnotationsServiceMock,
|
||||
ShoppingPersistedTabDataTestUtils.MockPageAnnotationsResponse
|
||||
ShoppingPersistedTabDataTestUtils.mockOptimizationGuideResponse(
|
||||
mOptimizationGuideBridgeJniMock,
|
||||
HintsProto.OptimizationType.PRICE_TRACKING.getNumber(),
|
||||
ShoppingPersistedTabDataTestUtils.MockPriceTrackingResponse
|
||||
.BUYABLE_PRODUCT_AND_PRODUCT_UPDATE);
|
||||
TestThreadUtils.runOnUiThreadBlocking(() -> {
|
||||
ShoppingPersistedTabData.from(tab, (shoppingPersistedTabData) -> {
|
||||
@@ -461,9 +482,31 @@ public class ShoppingPersistedTabDataTest {
|
||||
Tab tab = ShoppingPersistedTabDataTestUtils.createTabOnUiThread(
|
||||
ShoppingPersistedTabDataTestUtils.TAB_ID,
|
||||
ShoppingPersistedTabDataTestUtils.IS_INCOGNITO);
|
||||
ShoppingPersistedTabDataTestUtils.mockPageAnnotationsResponse(mPageAnnotationsServiceMock,
|
||||
ShoppingPersistedTabDataTestUtils.MockPageAnnotationsResponse
|
||||
.BUYABLE_PRODUCT_EMPTY);
|
||||
ShoppingPersistedTabDataTestUtils.mockOptimizationGuideResponse(
|
||||
mOptimizationGuideBridgeJniMock,
|
||||
HintsProto.OptimizationType.PRICE_TRACKING.getNumber(),
|
||||
ShoppingPersistedTabDataTestUtils.MockPriceTrackingResponse.BUYABLE_PRODUCT_EMPTY);
|
||||
TestThreadUtils.runOnUiThreadBlocking(() -> {
|
||||
ShoppingPersistedTabData.from(tab, (shoppingPersistedTabData) -> {
|
||||
Assert.assertNull(shoppingPersistedTabData);
|
||||
semaphore.release();
|
||||
});
|
||||
});
|
||||
ShoppingPersistedTabDataTestUtils.acquireSemaphore(semaphore);
|
||||
}
|
||||
|
||||
@UiThreadTest
|
||||
@SmallTest
|
||||
@Test
|
||||
public void testSPTDNullOptimizationGuideFalse() {
|
||||
final Semaphore semaphore = new Semaphore(0);
|
||||
Tab tab = ShoppingPersistedTabDataTestUtils.createTabOnUiThread(
|
||||
ShoppingPersistedTabDataTestUtils.TAB_ID,
|
||||
ShoppingPersistedTabDataTestUtils.IS_INCOGNITO);
|
||||
ShoppingPersistedTabDataTestUtils.mockOptimizationGuideResponse(
|
||||
mOptimizationGuideBridgeJniMock,
|
||||
HintsProto.OptimizationType.PRICE_TRACKING.getNumber(),
|
||||
ShoppingPersistedTabDataTestUtils.MockPriceTrackingResponse.NONE);
|
||||
TestThreadUtils.runOnUiThreadBlocking(() -> {
|
||||
ShoppingPersistedTabData.from(tab, (shoppingPersistedTabData) -> {
|
||||
Assert.assertNull(shoppingPersistedTabData);
|
||||
|
@@ -8,11 +8,15 @@ import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.anyLong;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import androidx.annotation.IntDef;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.google.protobuf.ByteString;
|
||||
|
||||
import org.mockito.invocation.InvocationOnMock;
|
||||
import org.mockito.stubbing.Answer;
|
||||
@@ -21,39 +25,42 @@ import org.chromium.base.Callback;
|
||||
import org.chromium.chrome.browser.endpoint_fetcher.EndpointFetcher;
|
||||
import org.chromium.chrome.browser.optimization_guide.OptimizationGuideBridge;
|
||||
import org.chromium.chrome.browser.optimization_guide.OptimizationGuideBridge.OptimizationGuideCallback;
|
||||
import org.chromium.chrome.browser.page_annotations.BuyableProductPageAnnotation;
|
||||
import org.chromium.chrome.browser.page_annotations.PageAnnotation;
|
||||
import org.chromium.chrome.browser.page_annotations.PageAnnotationsService;
|
||||
import org.chromium.chrome.browser.page_annotations.ProductPriceUpdatePageAnnotation;
|
||||
import org.chromium.chrome.browser.profiles.Profile;
|
||||
import org.chromium.chrome.browser.tab.MockTab;
|
||||
import org.chromium.chrome.browser.tab.Tab;
|
||||
import org.chromium.chrome.browser.tab.proto.PriceTracking.BuyableProduct;
|
||||
import org.chromium.chrome.browser.tab.proto.PriceTracking.PriceTrackingData;
|
||||
import org.chromium.chrome.browser.tab.proto.PriceTracking.ProductPrice;
|
||||
import org.chromium.chrome.browser.tab.proto.PriceTracking.ProductPriceUpdate;
|
||||
import org.chromium.components.optimization_guide.OptimizationGuideDecision;
|
||||
import org.chromium.components.optimization_guide.proto.CommonTypesProto.Any;
|
||||
import org.chromium.components.optimization_guide.proto.HintsProto;
|
||||
import org.chromium.content_public.browser.test.util.TestThreadUtils;
|
||||
import org.chromium.url.GURL;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.LinkedList;
|
||||
import java.util.concurrent.Semaphore;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
/**
|
||||
* Helper class for {@link ShoppingPersistedTabDataTest} & {@link
|
||||
* ShoppingPersistedTabDataLegacyTest}.
|
||||
*/
|
||||
public abstract class ShoppingPersistedTabDataTestUtils {
|
||||
@IntDef({MockPageAnnotationsResponse.BUYABLE_PRODUCT_INITIAL,
|
||||
MockPageAnnotationsResponse.BUYABLE_PRODUCT_PRICE_UPDATED,
|
||||
MockPageAnnotationsResponse.BUYABLE_PRODUCT_AND_PRODUCT_UPDATE,
|
||||
MockPageAnnotationsResponse.PRODUCT_PRICE_UPDATE,
|
||||
MockPageAnnotationsResponse.BUYABLE_PRODUCT_EMPTY})
|
||||
@IntDef({MockPriceTrackingResponse.BUYABLE_PRODUCT_INITIAL,
|
||||
MockPriceTrackingResponse.BUYABLE_PRODUCT_PRICE_UPDATED,
|
||||
MockPriceTrackingResponse.BUYABLE_PRODUCT_AND_PRODUCT_UPDATE,
|
||||
MockPriceTrackingResponse.PRODUCT_PRICE_UPDATE,
|
||||
MockPriceTrackingResponse.BUYABLE_PRODUCT_EMPTY, MockPriceTrackingResponse.NONE})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@interface MockPageAnnotationsResponse {
|
||||
@interface MockPriceTrackingResponse {
|
||||
int BUYABLE_PRODUCT_INITIAL = 0;
|
||||
int BUYABLE_PRODUCT_PRICE_UPDATED = 1;
|
||||
int BUYABLE_PRODUCT_AND_PRODUCT_UPDATE = 2;
|
||||
int PRODUCT_PRICE_UPDATE = 3;
|
||||
int BUYABLE_PRODUCT_EMPTY = 4;
|
||||
int NONE = 5;
|
||||
}
|
||||
|
||||
static final long PRICE_MICROS = 123456789012345L;
|
||||
@@ -69,6 +76,72 @@ public abstract class ShoppingPersistedTabDataTestUtils {
|
||||
static final boolean IS_INCOGNITO = false;
|
||||
static final String FAKE_OFFER_ID = "100";
|
||||
|
||||
static final BuyableProduct BUYABLE_PRODUCT_PROTO_INITIAL =
|
||||
BuyableProduct.newBuilder()
|
||||
.setCurrentPrice(createProductPrice(PRICE_MICROS, UNITED_STATES_CURRENCY_CODE))
|
||||
.build();
|
||||
static final BuyableProduct BUYABLE_PRODUCT_PROTO_PRICE_UPDATED =
|
||||
BuyableProduct.newBuilder()
|
||||
.setCurrentPrice(
|
||||
createProductPrice(UPDATED_PRICE_MICROS, UNITED_STATES_CURRENCY_CODE))
|
||||
.build();
|
||||
static final ProductPriceUpdate PRODUCT_UPDATE_PROTO =
|
||||
ProductPriceUpdate.newBuilder()
|
||||
.setOldPrice(createProductPrice(PRICE_MICROS, UNITED_STATES_CURRENCY_CODE))
|
||||
.setNewPrice(
|
||||
createProductPrice(UPDATED_PRICE_MICROS, UNITED_STATES_CURRENCY_CODE))
|
||||
.build();
|
||||
|
||||
static final PriceTrackingData PRICE_TRACKING_BUYABLE_PRODUCT_INITIAL =
|
||||
PriceTrackingData.newBuilder().setBuyableProduct(BUYABLE_PRODUCT_PROTO_INITIAL).build();
|
||||
static final Any ANY_BUYABLE_PRODUCT_INITIAL =
|
||||
Any.newBuilder()
|
||||
.setValue(ByteString.copyFrom(
|
||||
PRICE_TRACKING_BUYABLE_PRODUCT_INITIAL.toByteArray()))
|
||||
.build();
|
||||
|
||||
static final PriceTrackingData PRICE_TRACKING_BUYABLE_PRODUCT_UPDATE =
|
||||
PriceTrackingData.newBuilder()
|
||||
.setBuyableProduct(BUYABLE_PRODUCT_PROTO_PRICE_UPDATED)
|
||||
.build();
|
||||
static final Any ANY_BUYABLE_PRODUCT_UPDATE =
|
||||
Any.newBuilder()
|
||||
.setValue(ByteString.copyFrom(
|
||||
PRICE_TRACKING_BUYABLE_PRODUCT_UPDATE.toByteArray()))
|
||||
.build();
|
||||
|
||||
static final PriceTrackingData PRICE_TRACKING_BUYABLE_PRODUCT_AND_PRODUCT_UPDATE =
|
||||
PriceTrackingData.newBuilder()
|
||||
.setBuyableProduct(BUYABLE_PRODUCT_PROTO_INITIAL)
|
||||
.setProductUpdate(PRODUCT_UPDATE_PROTO)
|
||||
.build();
|
||||
static final Any ANY_PRICE_TRACKING_BUYABLE_PRODUCT_AND_PRODUCT_UPDATE =
|
||||
Any.newBuilder()
|
||||
.setValue(ByteString.copyFrom(
|
||||
PRICE_TRACKING_BUYABLE_PRODUCT_AND_PRODUCT_UPDATE.toByteArray()))
|
||||
.build();
|
||||
|
||||
static final PriceTrackingData PRICE_TRACKING_PRODUCT_UPDATE =
|
||||
PriceTrackingData.newBuilder().setProductUpdate(PRODUCT_UPDATE_PROTO).build();
|
||||
static final Any ANY_PRICE_TRACKING_PRODUCT_UPDATE =
|
||||
Any.newBuilder()
|
||||
.setValue(ByteString.copyFrom(PRICE_TRACKING_PRODUCT_UPDATE.toByteArray()))
|
||||
.build();
|
||||
|
||||
static final PriceTrackingData PRICE_TRACKING_EMPTY = PriceTrackingData.newBuilder().build();
|
||||
static final Any ANY_PRICE_TRACKING_EMPTY =
|
||||
Any.newBuilder()
|
||||
.setValue(ByteString.copyFrom(PRICE_TRACKING_EMPTY.toByteArray()))
|
||||
.build();
|
||||
static final Any ANY_EMPTY = Any.newBuilder().build();
|
||||
|
||||
static ProductPrice createProductPrice(long amountMicros, String currencyCode) {
|
||||
return ProductPrice.newBuilder()
|
||||
.setCurrencyCode(currencyCode)
|
||||
.setAmountMicros(amountMicros)
|
||||
.build();
|
||||
}
|
||||
|
||||
static ShoppingPersistedTabData createShoppingPersistedTabDataWithDefaults() {
|
||||
ShoppingPersistedTabData shoppingPersistedTabData =
|
||||
new ShoppingPersistedTabData(createTabOnUiThread(TAB_ID, IS_INCOGNITO));
|
||||
@@ -113,13 +186,56 @@ public abstract class ShoppingPersistedTabDataTestUtils {
|
||||
}
|
||||
|
||||
static void mockOptimizationGuideResponse(OptimizationGuideBridge.Natives optimizationGuideJni,
|
||||
@OptimizationGuideDecision int decision) {
|
||||
int optimizationType, @OptimizationGuideDecision int decision, @Nullable Any metadata) {
|
||||
doAnswer(new Answer<Void>() {
|
||||
@Override
|
||||
public Void answer(InvocationOnMock invocation) {
|
||||
OptimizationGuideCallback callback =
|
||||
(OptimizationGuideCallback) invocation.getArguments()[3];
|
||||
callback.onOptimizationGuideDecision(decision, null);
|
||||
callback.onOptimizationGuideDecision(decision, metadata);
|
||||
return null;
|
||||
}
|
||||
})
|
||||
.when(optimizationGuideJni)
|
||||
.canApplyOptimization(anyLong(), any(GURL.class), eq(optimizationType),
|
||||
any(OptimizationGuideCallback.class));
|
||||
}
|
||||
|
||||
static void mockOptimizationGuideResponse(OptimizationGuideBridge.Natives optimizationGuideJni,
|
||||
int optimizationType, @MockPriceTrackingResponse int expectedResponse) {
|
||||
doAnswer(new Answer<Void>() {
|
||||
@Override
|
||||
public Void answer(InvocationOnMock invocation) {
|
||||
OptimizationGuideCallback callback =
|
||||
(OptimizationGuideCallback) invocation.getArguments()[3];
|
||||
switch (expectedResponse) {
|
||||
case MockPriceTrackingResponse.BUYABLE_PRODUCT_INITIAL:
|
||||
callback.onOptimizationGuideDecision(
|
||||
OptimizationGuideDecision.TRUE, ANY_BUYABLE_PRODUCT_INITIAL);
|
||||
break;
|
||||
case MockPriceTrackingResponse.BUYABLE_PRODUCT_PRICE_UPDATED:
|
||||
callback.onOptimizationGuideDecision(
|
||||
OptimizationGuideDecision.TRUE, ANY_BUYABLE_PRODUCT_UPDATE);
|
||||
break;
|
||||
case MockPriceTrackingResponse.BUYABLE_PRODUCT_AND_PRODUCT_UPDATE:
|
||||
callback.onOptimizationGuideDecision(OptimizationGuideDecision.TRUE,
|
||||
ANY_PRICE_TRACKING_BUYABLE_PRODUCT_AND_PRODUCT_UPDATE);
|
||||
break;
|
||||
case MockPriceTrackingResponse.PRODUCT_PRICE_UPDATE:
|
||||
callback.onOptimizationGuideDecision(
|
||||
OptimizationGuideDecision.TRUE, ANY_PRICE_TRACKING_PRODUCT_UPDATE);
|
||||
break;
|
||||
case MockPriceTrackingResponse.BUYABLE_PRODUCT_EMPTY:
|
||||
callback.onOptimizationGuideDecision(
|
||||
OptimizationGuideDecision.TRUE, ANY_PRICE_TRACKING_EMPTY);
|
||||
break;
|
||||
case MockPriceTrackingResponse.NONE:
|
||||
callback.onOptimizationGuideDecision(
|
||||
OptimizationGuideDecision.FALSE, ANY_EMPTY);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
})
|
||||
@@ -128,55 +244,17 @@ public abstract class ShoppingPersistedTabDataTestUtils {
|
||||
anyLong(), any(GURL.class), anyInt(), any(OptimizationGuideCallback.class));
|
||||
}
|
||||
|
||||
static void mockPageAnnotationsResponse(PageAnnotationsService pageAnnotationsService,
|
||||
@MockPageAnnotationsResponse int expectedResponse) {
|
||||
doAnswer(new Answer<Void>() {
|
||||
@Override
|
||||
public Void answer(InvocationOnMock invocation) {
|
||||
Callback callback = (Callback) invocation.getArguments()[1];
|
||||
callback.onResult(new LinkedList<PageAnnotation>() {
|
||||
{
|
||||
switch (expectedResponse) {
|
||||
case MockPageAnnotationsResponse.BUYABLE_PRODUCT_INITIAL:
|
||||
add(new BuyableProductPageAnnotation(
|
||||
PRICE_MICROS, UNITED_STATES_CURRENCY_CODE, FAKE_OFFER_ID));
|
||||
break;
|
||||
case MockPageAnnotationsResponse.BUYABLE_PRODUCT_PRICE_UPDATED:
|
||||
add(new BuyableProductPageAnnotation(UPDATED_PRICE_MICROS,
|
||||
UNITED_STATES_CURRENCY_CODE, FAKE_OFFER_ID));
|
||||
break;
|
||||
case MockPageAnnotationsResponse.BUYABLE_PRODUCT_AND_PRODUCT_UPDATE:
|
||||
add(new BuyableProductPageAnnotation(
|
||||
PRICE_MICROS, UNITED_STATES_CURRENCY_CODE, FAKE_OFFER_ID));
|
||||
add(new ProductPriceUpdatePageAnnotation(PRICE_MICROS,
|
||||
UPDATED_PRICE_MICROS, UNITED_STATES_CURRENCY_CODE));
|
||||
break;
|
||||
case MockPageAnnotationsResponse.PRODUCT_PRICE_UPDATE:
|
||||
add(new ProductPriceUpdatePageAnnotation(PRICE_MICROS,
|
||||
UPDATED_PRICE_MICROS, UNITED_STATES_CURRENCY_CODE));
|
||||
break;
|
||||
case MockPageAnnotationsResponse.BUYABLE_PRODUCT_EMPTY:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
return null;
|
||||
}
|
||||
})
|
||||
.when(pageAnnotationsService)
|
||||
.getAnnotations(any(GURL.class), any(Callback.class));
|
||||
}
|
||||
|
||||
static void verifyEndpointFetcherCalled(EndpointFetcher.Natives endpointFetcher, int numTimes) {
|
||||
verify(endpointFetcher, times(numTimes))
|
||||
.nativeFetchChromeAPIKey(any(Profile.class), anyString(), anyString(), anyString(),
|
||||
anyString(), anyLong(), any(String[].class), any(Callback.class));
|
||||
}
|
||||
|
||||
static void verifyGetPageAnnotationsCalled(
|
||||
PageAnnotationsService pageAnnotationsService, int numTimes) {
|
||||
verify(pageAnnotationsService, times(numTimes))
|
||||
.getAnnotations(any(GURL.class), any(Callback.class));
|
||||
static void verifyPriceTrackingOptimizationTypeCalled(
|
||||
OptimizationGuideBridge.Natives optimizationGuideJni, int numTimes) {
|
||||
verify(optimizationGuideJni, times(numTimes))
|
||||
.canApplyOptimization(anyLong(), any(GURL.class),
|
||||
eq(HintsProto.OptimizationType.PRICE_TRACKING.getNumber()),
|
||||
any(OptimizationGuideCallback.class));
|
||||
}
|
||||
}
|
187
chrome/android/javatests/src/org/chromium/chrome/browser/tab/state/ShoppingPersistedTabDataWithPASTest.java
Normal file
187
chrome/android/javatests/src/org/chromium/chrome/browser/tab/state/ShoppingPersistedTabDataWithPASTest.java
Normal file
@@ -0,0 +1,187 @@
|
||||
// Copyright 2021 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.tab.state;
|
||||
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
|
||||
import android.support.test.filters.SmallTest;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TestRule;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import org.chromium.base.test.BaseJUnit4ClassRunner;
|
||||
import org.chromium.base.test.UiThreadTest;
|
||||
import org.chromium.base.test.util.CommandLineFlags;
|
||||
import org.chromium.base.test.util.JniMocker;
|
||||
import org.chromium.chrome.browser.endpoint_fetcher.EndpointFetcher;
|
||||
import org.chromium.chrome.browser.endpoint_fetcher.EndpointFetcherJni;
|
||||
import org.chromium.chrome.browser.flags.ChromeFeatureList;
|
||||
import org.chromium.chrome.browser.flags.ChromeSwitches;
|
||||
import org.chromium.chrome.browser.optimization_guide.OptimizationGuideBridge;
|
||||
import org.chromium.chrome.browser.optimization_guide.OptimizationGuideBridgeJni;
|
||||
import org.chromium.chrome.browser.page_annotations.PageAnnotationsService;
|
||||
import org.chromium.chrome.browser.page_annotations.PageAnnotationsServiceFactory;
|
||||
import org.chromium.chrome.browser.profiles.Profile;
|
||||
import org.chromium.chrome.browser.tab.Tab;
|
||||
import org.chromium.chrome.test.ChromeBrowserTestRule;
|
||||
import org.chromium.chrome.test.util.browser.Features;
|
||||
import org.chromium.chrome.test.util.browser.Features.EnableFeatures;
|
||||
import org.chromium.components.optimization_guide.OptimizationGuideDecision;
|
||||
import org.chromium.components.optimization_guide.proto.HintsProto;
|
||||
import org.chromium.content_public.browser.test.util.TestThreadUtils;
|
||||
|
||||
import java.util.concurrent.Semaphore;
|
||||
|
||||
/**
|
||||
* Test relating to {@link ShoppingPersistedTabData} and {@link PageAnnotationService}
|
||||
*/
|
||||
@RunWith(BaseJUnit4ClassRunner.class)
|
||||
@EnableFeatures({ChromeFeatureList.TAB_GRID_LAYOUT_ANDROID + "<Study"})
|
||||
@CommandLineFlags.
|
||||
Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE, "force-fieldtrials=Study/Group"})
|
||||
public class ShoppingPersistedTabDataWithPASTest {
|
||||
@Rule
|
||||
public final ChromeBrowserTestRule mBrowserTestRule = new ChromeBrowserTestRule();
|
||||
|
||||
@Rule
|
||||
public JniMocker mMocker = new JniMocker();
|
||||
|
||||
@Rule
|
||||
public TestRule mProcessor = new Features.InstrumentationProcessor();
|
||||
|
||||
@Mock
|
||||
protected EndpointFetcher.Natives mEndpointFetcherJniMock;
|
||||
|
||||
@Mock
|
||||
protected OptimizationGuideBridge.Natives mOptimizationGuideBridgeJniMock;
|
||||
|
||||
@Mock
|
||||
protected Profile mProfileMock;
|
||||
|
||||
@Mock
|
||||
protected PageAnnotationsServiceFactory mServiceFactoryMock;
|
||||
|
||||
@Mock
|
||||
protected PageAnnotationsService mPageAnnotationsServiceMock;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mMocker.mock(EndpointFetcherJni.TEST_HOOKS, mEndpointFetcherJniMock);
|
||||
mMocker.mock(OptimizationGuideBridgeJni.TEST_HOOKS, mOptimizationGuideBridgeJniMock);
|
||||
// Ensure native pointer is initialized
|
||||
doReturn(1L).when(mOptimizationGuideBridgeJniMock).init();
|
||||
ShoppingPersistedTabDataTestUtils.mockOptimizationGuideResponse(
|
||||
mOptimizationGuideBridgeJniMock,
|
||||
HintsProto.OptimizationType.SHOPPING_PAGE_PREDICTOR.getNumber(),
|
||||
OptimizationGuideDecision.TRUE, null);
|
||||
PersistedTabDataConfiguration.setUseTestConfig(true);
|
||||
|
||||
Profile.setLastUsedProfileForTesting(mProfileMock);
|
||||
doReturn(mPageAnnotationsServiceMock).when(mServiceFactoryMock).getForLastUsedProfile();
|
||||
ShoppingPersistedTabData.sPageAnnotationsServiceFactory = mServiceFactoryMock;
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
@Test
|
||||
@CommandLineFlags.
|
||||
Add({"force-fieldtrial-params=Study.Group:price_tracking_with_optimization_guide/false"})
|
||||
public void testShoppingBloomFilterNotShoppingWebsite() {
|
||||
ShoppingPersistedTabDataWithPASTestUtils.mockPageAnnotationsResponse(
|
||||
mPageAnnotationsServiceMock,
|
||||
ShoppingPersistedTabDataWithPASTestUtils.MockPageAnnotationsResponse
|
||||
.BUYABLE_PRODUCT_INITIAL);
|
||||
ShoppingPersistedTabDataTestUtils.mockOptimizationGuideResponse(
|
||||
mOptimizationGuideBridgeJniMock,
|
||||
HintsProto.OptimizationType.SHOPPING_PAGE_PREDICTOR.getNumber(),
|
||||
OptimizationGuideDecision.FALSE, null);
|
||||
Tab tab = ShoppingPersistedTabDataTestUtils.createTabOnUiThread(
|
||||
ShoppingPersistedTabDataTestUtils.TAB_ID,
|
||||
ShoppingPersistedTabDataTestUtils.IS_INCOGNITO);
|
||||
Semaphore semaphore = new Semaphore(0);
|
||||
TestThreadUtils.runOnUiThreadBlocking(() -> {
|
||||
ShoppingPersistedTabData.from(
|
||||
tab, (shoppingPersistedTabData) -> { semaphore.release(); });
|
||||
});
|
||||
ShoppingPersistedTabDataTestUtils.acquireSemaphore(semaphore);
|
||||
ShoppingPersistedTabDataWithPASTestUtils.verifyGetPageAnnotationsCalled(
|
||||
mPageAnnotationsServiceMock, 0);
|
||||
}
|
||||
|
||||
@SmallTest
|
||||
@Test
|
||||
public void testShoppingBloomFilterShoppingWebsite() {
|
||||
for (@OptimizationGuideDecision int decision :
|
||||
new int[] {OptimizationGuideDecision.TRUE, OptimizationGuideDecision.UNKNOWN}) {
|
||||
ShoppingPersistedTabDataWithPASTestUtils.mockPageAnnotationsResponse(
|
||||
mPageAnnotationsServiceMock,
|
||||
ShoppingPersistedTabDataWithPASTestUtils.MockPageAnnotationsResponse
|
||||
.BUYABLE_PRODUCT_INITIAL);
|
||||
ShoppingPersistedTabDataTestUtils.mockOptimizationGuideResponse(
|
||||
mOptimizationGuideBridgeJniMock,
|
||||
HintsProto.OptimizationType.SHOPPING_PAGE_PREDICTOR.getNumber(), decision,
|
||||
null);
|
||||
Tab tab = ShoppingPersistedTabDataTestUtils.createTabOnUiThread(
|
||||
ShoppingPersistedTabDataTestUtils.TAB_ID,
|
||||
ShoppingPersistedTabDataTestUtils.IS_INCOGNITO);
|
||||
Semaphore semaphore = new Semaphore(0);
|
||||
TestThreadUtils.runOnUiThreadBlocking(() -> {
|
||||
ShoppingPersistedTabData.from(
|
||||
tab, (shoppingPersistedTabData) -> { semaphore.release(); });
|
||||
});
|
||||
ShoppingPersistedTabDataTestUtils.acquireSemaphore(semaphore);
|
||||
ShoppingPersistedTabDataWithPASTestUtils.verifyGetPageAnnotationsCalled(
|
||||
mPageAnnotationsServiceMock, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@UiThreadTest
|
||||
@SmallTest
|
||||
@Test
|
||||
public void testSPTDSavingEnabledUponSuccessfulProductUpdateResponse() {
|
||||
final Semaphore semaphore = new Semaphore(0);
|
||||
Tab tab = ShoppingPersistedTabDataTestUtils.createTabOnUiThread(
|
||||
ShoppingPersistedTabDataTestUtils.TAB_ID,
|
||||
ShoppingPersistedTabDataTestUtils.IS_INCOGNITO);
|
||||
ShoppingPersistedTabDataWithPASTestUtils.mockPageAnnotationsResponse(
|
||||
mPageAnnotationsServiceMock,
|
||||
ShoppingPersistedTabDataWithPASTestUtils.MockPageAnnotationsResponse
|
||||
.BUYABLE_PRODUCT_AND_PRODUCT_UPDATE);
|
||||
TestThreadUtils.runOnUiThreadBlocking(() -> {
|
||||
ShoppingPersistedTabData.from(tab, (shoppingPersistedTabData) -> {
|
||||
Assert.assertTrue(shoppingPersistedTabData.mIsTabSaveEnabledSupplier.get());
|
||||
semaphore.release();
|
||||
});
|
||||
});
|
||||
ShoppingPersistedTabDataTestUtils.acquireSemaphore(semaphore);
|
||||
}
|
||||
|
||||
@UiThreadTest
|
||||
@SmallTest
|
||||
@Test
|
||||
public void testSPTDNullUponUnsuccessfulResponse() {
|
||||
final Semaphore semaphore = new Semaphore(0);
|
||||
Tab tab = ShoppingPersistedTabDataTestUtils.createTabOnUiThread(
|
||||
ShoppingPersistedTabDataTestUtils.TAB_ID,
|
||||
ShoppingPersistedTabDataTestUtils.IS_INCOGNITO);
|
||||
ShoppingPersistedTabDataWithPASTestUtils.mockPageAnnotationsResponse(
|
||||
mPageAnnotationsServiceMock,
|
||||
ShoppingPersistedTabDataWithPASTestUtils.MockPageAnnotationsResponse
|
||||
.BUYABLE_PRODUCT_EMPTY);
|
||||
TestThreadUtils.runOnUiThreadBlocking(() -> {
|
||||
ShoppingPersistedTabData.from(tab, (shoppingPersistedTabData) -> {
|
||||
Assert.assertNull(shoppingPersistedTabData);
|
||||
semaphore.release();
|
||||
});
|
||||
});
|
||||
ShoppingPersistedTabDataTestUtils.acquireSemaphore(semaphore);
|
||||
}
|
||||
}
|
98
chrome/android/javatests/src/org/chromium/chrome/browser/tab/state/ShoppingPersistedTabDataWithPASTestUtils.java
Normal file
98
chrome/android/javatests/src/org/chromium/chrome/browser/tab/state/ShoppingPersistedTabDataWithPASTestUtils.java
Normal file
@@ -0,0 +1,98 @@
|
||||
// Copyright 2021 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.tab.state;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import androidx.annotation.IntDef;
|
||||
|
||||
import org.mockito.invocation.InvocationOnMock;
|
||||
import org.mockito.stubbing.Answer;
|
||||
|
||||
import org.chromium.base.Callback;
|
||||
import org.chromium.chrome.browser.page_annotations.BuyableProductPageAnnotation;
|
||||
import org.chromium.chrome.browser.page_annotations.PageAnnotation;
|
||||
import org.chromium.chrome.browser.page_annotations.PageAnnotationsService;
|
||||
import org.chromium.chrome.browser.page_annotations.ProductPriceUpdatePageAnnotation;
|
||||
import org.chromium.url.GURL;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.LinkedList;
|
||||
/**
|
||||
* Helper class for {@link ShoppingPersistedTabDataWithPASTest} & {@link
|
||||
* ShoppingPersistedTabDataLegacyWithPASTest}.
|
||||
*/
|
||||
public abstract class ShoppingPersistedTabDataWithPASTestUtils {
|
||||
@IntDef({MockPageAnnotationsResponse.BUYABLE_PRODUCT_INITIAL,
|
||||
MockPageAnnotationsResponse.BUYABLE_PRODUCT_PRICE_UPDATED,
|
||||
MockPageAnnotationsResponse.BUYABLE_PRODUCT_AND_PRODUCT_UPDATE,
|
||||
MockPageAnnotationsResponse.PRODUCT_PRICE_UPDATE,
|
||||
MockPageAnnotationsResponse.BUYABLE_PRODUCT_EMPTY})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@interface MockPageAnnotationsResponse {
|
||||
int BUYABLE_PRODUCT_INITIAL = 0;
|
||||
int BUYABLE_PRODUCT_PRICE_UPDATED = 1;
|
||||
int BUYABLE_PRODUCT_AND_PRODUCT_UPDATE = 2;
|
||||
int PRODUCT_PRICE_UPDATE = 3;
|
||||
int BUYABLE_PRODUCT_EMPTY = 4;
|
||||
}
|
||||
|
||||
static final long PRICE_MICROS = 123456789012345L;
|
||||
static final long UPDATED_PRICE_MICROS = 287000000L;
|
||||
static final long HIGH_PRICE_MICROS = 141000000L;
|
||||
static final long LOW_PRICE_MICROS = 100000000L;
|
||||
static final String UNITED_STATES_CURRENCY_CODE = "USD";
|
||||
static final String FAKE_OFFER_ID = "100";
|
||||
|
||||
static void mockPageAnnotationsResponse(PageAnnotationsService pageAnnotationsService,
|
||||
@MockPageAnnotationsResponse int expectedResponse) {
|
||||
doAnswer(new Answer<Void>() {
|
||||
@Override
|
||||
public Void answer(InvocationOnMock invocation) {
|
||||
Callback callback = (Callback) invocation.getArguments()[1];
|
||||
callback.onResult(new LinkedList<PageAnnotation>() {
|
||||
{
|
||||
switch (expectedResponse) {
|
||||
case MockPageAnnotationsResponse.BUYABLE_PRODUCT_INITIAL:
|
||||
add(new BuyableProductPageAnnotation(
|
||||
PRICE_MICROS, UNITED_STATES_CURRENCY_CODE, FAKE_OFFER_ID));
|
||||
break;
|
||||
case MockPageAnnotationsResponse.BUYABLE_PRODUCT_PRICE_UPDATED:
|
||||
add(new BuyableProductPageAnnotation(UPDATED_PRICE_MICROS,
|
||||
UNITED_STATES_CURRENCY_CODE, FAKE_OFFER_ID));
|
||||
break;
|
||||
case MockPageAnnotationsResponse.BUYABLE_PRODUCT_AND_PRODUCT_UPDATE:
|
||||
add(new BuyableProductPageAnnotation(
|
||||
PRICE_MICROS, UNITED_STATES_CURRENCY_CODE, FAKE_OFFER_ID));
|
||||
add(new ProductPriceUpdatePageAnnotation(PRICE_MICROS,
|
||||
UPDATED_PRICE_MICROS, UNITED_STATES_CURRENCY_CODE));
|
||||
break;
|
||||
case MockPageAnnotationsResponse.PRODUCT_PRICE_UPDATE:
|
||||
add(new ProductPriceUpdatePageAnnotation(PRICE_MICROS,
|
||||
UPDATED_PRICE_MICROS, UNITED_STATES_CURRENCY_CODE));
|
||||
break;
|
||||
case MockPageAnnotationsResponse.BUYABLE_PRODUCT_EMPTY:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
return null;
|
||||
}
|
||||
})
|
||||
.when(pageAnnotationsService)
|
||||
.getAnnotations(any(GURL.class), any(Callback.class));
|
||||
}
|
||||
|
||||
static void verifyGetPageAnnotationsCalled(
|
||||
PageAnnotationsService pageAnnotationsService, int numTimes) {
|
||||
verify(pageAnnotationsService, times(numTimes))
|
||||
.getAnnotations(any(GURL.class), any(Callback.class));
|
||||
}
|
||||
}
|
@@ -1732,7 +1732,13 @@ const FeatureEntry::FeatureParam kTabGridLayoutAndroid_SearchChip[] = {
|
||||
{"enable_search_term_chip", "true"}};
|
||||
|
||||
const FeatureEntry::FeatureParam kTabGridLayoutAndroid_PriceAlerts[] = {
|
||||
{"enable_price_tracking", "true"}};
|
||||
{"enable_price_tracking", "true"},
|
||||
{"price_tracking_with_optimization_guide", "false"}};
|
||||
|
||||
const FeatureEntry::FeatureParam
|
||||
kTabGridLayoutAndroid_PriceAlerts_WithOptimizationGuide[] = {
|
||||
{"enable_price_tracking", "true"},
|
||||
{"price_tracking_with_optimization_guide", "true"}};
|
||||
|
||||
const FeatureEntry::FeatureParam kTabGridLayoutAndroid_TabGroupAutoCreation[] =
|
||||
{{"enable_tab_group_auto_creation", "false"}};
|
||||
@@ -1751,6 +1757,10 @@ const FeatureEntry::FeatureVariation kTabGridLayoutAndroidVariations[] = {
|
||||
base::size(kTabGridLayoutAndroid_SearchChip), nullptr},
|
||||
{"Price alerts", kTabGridLayoutAndroid_PriceAlerts,
|
||||
base::size(kTabGridLayoutAndroid_PriceAlerts), nullptr},
|
||||
{"Price alerts with OptimizationGuide",
|
||||
kTabGridLayoutAndroid_PriceAlerts_WithOptimizationGuide,
|
||||
base::size(kTabGridLayoutAndroid_PriceAlerts_WithOptimizationGuide),
|
||||
nullptr},
|
||||
{"Without auto group", kTabGridLayoutAndroid_TabGroupAutoCreation,
|
||||
base::size(kTabGridLayoutAndroid_TabGroupAutoCreation), nullptr},
|
||||
{"Price notifications", kTabGridLayoutAndroid_PriceNotifications,
|
||||
|
@@ -59,6 +59,7 @@ android_library("java") {
|
||||
deps = [
|
||||
":critical_persisted_tab_data_proto_java",
|
||||
":java_resources",
|
||||
":optimization_guide_protos_java",
|
||||
"//base:base_java",
|
||||
"//chrome/browser/android/crypto:java",
|
||||
"//chrome/browser/contextmenu:java",
|
||||
@@ -124,6 +125,11 @@ proto_java_library("critical_persisted_tab_data_proto_java") {
|
||||
]
|
||||
}
|
||||
|
||||
proto_java_library("optimization_guide_protos_java") {
|
||||
proto_path = "java/src/org/chromium/chrome/browser/tab/state/proto"
|
||||
sources = [ "$proto_path/price_tracking.proto" ]
|
||||
}
|
||||
|
||||
android_library("junit") {
|
||||
bypass_platform_checks = true
|
||||
testonly = true
|
||||
|
@@ -15,6 +15,7 @@ import org.chromium.base.Log;
|
||||
import org.chromium.base.metrics.RecordHistogram;
|
||||
import org.chromium.base.supplier.ObservableSupplierImpl;
|
||||
import org.chromium.base.supplier.Supplier;
|
||||
import org.chromium.chrome.browser.flags.BooleanCachedFieldTrialParameter;
|
||||
import org.chromium.chrome.browser.flags.ChromeFeatureList;
|
||||
import org.chromium.chrome.browser.flags.IntCachedFieldTrialParameter;
|
||||
import org.chromium.chrome.browser.optimization_guide.OptimizationGuideBridgeFactory;
|
||||
@@ -25,6 +26,9 @@ import org.chromium.chrome.browser.page_annotations.PageAnnotationsServiceFactor
|
||||
import org.chromium.chrome.browser.page_annotations.ProductPriceUpdatePageAnnotation;
|
||||
import org.chromium.chrome.browser.tab.EmptyTabObserver;
|
||||
import org.chromium.chrome.browser.tab.Tab;
|
||||
import org.chromium.chrome.browser.tab.proto.PriceTracking.BuyableProduct;
|
||||
import org.chromium.chrome.browser.tab.proto.PriceTracking.PriceTrackingData;
|
||||
import org.chromium.chrome.browser.tab.proto.PriceTracking.ProductPriceUpdate;
|
||||
import org.chromium.chrome.browser.tab.proto.ShoppingPersistedTabData.ShoppingPersistedTabDataProto;
|
||||
import org.chromium.components.optimization_guide.OptimizationGuideDecision;
|
||||
import org.chromium.components.optimization_guide.proto.HintsProto;
|
||||
@@ -47,6 +51,8 @@ public class ShoppingPersistedTabData extends PersistedTabData {
|
||||
"price_tracking_stale_tab_threshold_seconds";
|
||||
private static final String TIME_TO_LIVE_MS_PARAM = "price_tracking_time_to_live_ms";
|
||||
private static final String DISPLAY_TIME_MS_PARAM = "price_tracking_display_time_ms";
|
||||
private static final String PRICE_TRACKING_WITH_OPTIMIZATION_GUIDE_PARAM =
|
||||
"price_tracking_with_optimization_guide";
|
||||
|
||||
private static final int FRACTIONAL_DIGITS_LESS_THAN_TEN_UNITS = 2;
|
||||
private static final int FRACTIONAL_DIGITS_GREATER_THAN_TEN_UNITS = 0;
|
||||
@@ -76,12 +82,20 @@ public class ShoppingPersistedTabData extends PersistedTabData {
|
||||
new IntCachedFieldTrialParameter(ChromeFeatureList.TAB_GRID_LAYOUT_ANDROID,
|
||||
DISPLAY_TIME_MS_PARAM, (int) ONE_WEEK_MS);
|
||||
|
||||
public static final BooleanCachedFieldTrialParameter PRICE_TRACKING_WITH_OPTIMIZATION_GUIDE =
|
||||
new BooleanCachedFieldTrialParameter(ChromeFeatureList.TAB_GRID_LAYOUT_ANDROID,
|
||||
PRICE_TRACKING_WITH_OPTIMIZATION_GUIDE_PARAM, false);
|
||||
|
||||
@VisibleForTesting
|
||||
public static final long NO_TRANSITIONS_OCCURRED = -1;
|
||||
|
||||
@VisibleForTesting
|
||||
public static final long NO_PRICE_KNOWN = -1;
|
||||
|
||||
@VisibleForTesting
|
||||
protected static PageAnnotationsServiceFactory sPageAnnotationsServiceFactory =
|
||||
new PageAnnotationsServiceFactory();
|
||||
|
||||
public long mLastPriceChangeTimeMs = NO_TRANSITIONS_OCCURRED;
|
||||
|
||||
private long mPriceMicros = NO_PRICE_KNOWN;
|
||||
@@ -94,10 +108,6 @@ public class ShoppingPersistedTabData extends PersistedTabData {
|
||||
protected ObservableSupplierImpl<Boolean> mIsTabSaveEnabledSupplier =
|
||||
new ObservableSupplierImpl<>();
|
||||
|
||||
@VisibleForTesting
|
||||
protected static PageAnnotationsServiceFactory sPageAnnotationsServiceFactory =
|
||||
new PageAnnotationsServiceFactory();
|
||||
|
||||
@VisibleForTesting
|
||||
protected EmptyTabObserver mUrlUpdatedObserver;
|
||||
|
||||
@@ -216,11 +226,42 @@ public class ShoppingPersistedTabData extends PersistedTabData {
|
||||
return;
|
||||
}
|
||||
|
||||
sPageAnnotationsServiceFactory.getForLastUsedProfile().getAnnotations(
|
||||
tab.getUrl(), (result) -> {
|
||||
supplierCallback.onResult(
|
||||
build(tab, result, previousShoppingPersistedTabData));
|
||||
});
|
||||
if (PRICE_TRACKING_WITH_OPTIMIZATION_GUIDE.getValue()) {
|
||||
OptimizationGuideBridgeFactoryHolder.sOptimizationGuideBridgeFactory
|
||||
.create()
|
||||
.canApplyOptimization(tab.getUrl(),
|
||||
HintsProto.OptimizationType.PRICE_TRACKING,
|
||||
(decision, metadata) -> {
|
||||
if (decision != OptimizationGuideDecision.TRUE) {
|
||||
supplierCallback.onResult(null);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
PriceTrackingData priceTrackingDataProto =
|
||||
PriceTrackingData.parseFrom(
|
||||
metadata.getValue());
|
||||
supplierCallback.onResult(build(tab,
|
||||
priceTrackingDataProto,
|
||||
previousShoppingPersistedTabData));
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
Log.i(TAG,
|
||||
String.format(Locale.US,
|
||||
"There was a problem "
|
||||
+ "parsing "
|
||||
+ "PriceTracking"
|
||||
+ "DataProto. "
|
||||
+ "Details %s.",
|
||||
e));
|
||||
supplierCallback.onResult(null);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
sPageAnnotationsServiceFactory.getForLastUsedProfile().getAnnotations(
|
||||
tab.getUrl(), (result) -> {
|
||||
supplierCallback.onResult(build(
|
||||
tab, result, previousShoppingPersistedTabData));
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
ShoppingPersistedTabData.class, callback);
|
||||
@@ -236,6 +277,16 @@ public class ShoppingPersistedTabData extends PersistedTabData {
|
||||
/**
|
||||
* Whether a BuyableProductAnnotation was found or not
|
||||
*/
|
||||
@IntDef({FoundBuyableProduct.NOT_FOUND, FoundBuyableProduct.FOUND,
|
||||
FoundBuyableProduct.FOUND_WITH_PRICE_UPDATE})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@interface FoundBuyableProduct {
|
||||
int NOT_FOUND = 0;
|
||||
int FOUND = 1;
|
||||
int FOUND_WITH_PRICE_UPDATE = 2;
|
||||
int NUM_ENTRIES = 3;
|
||||
}
|
||||
|
||||
@IntDef({FoundBuyableProductAnnotation.NOT_FOUND, FoundBuyableProductAnnotation.FOUND,
|
||||
FoundBuyableProductAnnotation.FOUND_WITH_PRICE_UPDATE})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@@ -303,6 +354,80 @@ public class ShoppingPersistedTabData extends PersistedTabData {
|
||||
return null;
|
||||
}
|
||||
|
||||
private static ShoppingPersistedTabData build(Tab tab, PriceTrackingData priceTrackingData,
|
||||
ShoppingPersistedTabData previousShoppingPersistedTabData) {
|
||||
ShoppingPersistedTabData res = new ShoppingPersistedTabData(tab);
|
||||
@FoundBuyableProduct
|
||||
int foundBuyableProduct = FoundBuyableProduct.NOT_FOUND;
|
||||
|
||||
ProductPriceUpdate productUpdate = priceTrackingData.getProductUpdate();
|
||||
BuyableProduct buyableProduct = priceTrackingData.getBuyableProduct();
|
||||
|
||||
if (hasPriceUpdate(priceTrackingData)) {
|
||||
res.setPriceMicros(productUpdate.getNewPrice().getAmountMicros());
|
||||
res.setPreviousPriceMicros(productUpdate.getOldPrice().getAmountMicros());
|
||||
res.setCurrencyCode(productUpdate.getOldPrice().getCurrencyCode());
|
||||
res.setLastUpdatedMs(System.currentTimeMillis());
|
||||
res.setMainOfferId(String.valueOf(buyableProduct.getOfferId()));
|
||||
foundBuyableProduct = FoundBuyableProduct.FOUND_WITH_PRICE_UPDATE;
|
||||
} else if (hasPrice(priceTrackingData)) {
|
||||
res.setPriceMicros(buyableProduct.getCurrentPrice().getAmountMicros(),
|
||||
previousShoppingPersistedTabData);
|
||||
res.setCurrencyCode(buyableProduct.getCurrentPrice().getCurrencyCode());
|
||||
res.setLastUpdatedMs(System.currentTimeMillis());
|
||||
res.setMainOfferId(String.valueOf(buyableProduct.getOfferId()));
|
||||
foundBuyableProduct = FoundBuyableProduct.FOUND;
|
||||
}
|
||||
|
||||
RecordHistogram.recordEnumeratedHistogram(
|
||||
"Tabs.ShoppingPersistedTabData.FoundBuyableProduct", foundBuyableProduct,
|
||||
FoundBuyableProduct.NUM_ENTRIES);
|
||||
// Only persist this ShoppingPersistedTabData if it was correctly populated from the
|
||||
// response
|
||||
if (foundBuyableProduct == FoundBuyableProduct.FOUND
|
||||
|| foundBuyableProduct == FoundBuyableProduct.FOUND_WITH_PRICE_UPDATE) {
|
||||
res.enableSaving();
|
||||
return res;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean hasPriceUpdate(PriceTrackingData priceTrackingDataProto) {
|
||||
if (!priceTrackingDataProto.hasBuyableProduct()
|
||||
|| !priceTrackingDataProto.hasProductUpdate()) {
|
||||
return false;
|
||||
}
|
||||
ProductPriceUpdate productUpdateProto = priceTrackingDataProto.getProductUpdate();
|
||||
if (!productUpdateProto.hasNewPrice() || !productUpdateProto.hasOldPrice()) {
|
||||
return false;
|
||||
}
|
||||
if (!productUpdateProto.getNewPrice().hasCurrencyCode()
|
||||
|| !productUpdateProto.getOldPrice().hasCurrencyCode()) {
|
||||
return false;
|
||||
}
|
||||
if (!productUpdateProto.getNewPrice().getCurrencyCode().equals(
|
||||
productUpdateProto.getOldPrice().getCurrencyCode())) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean hasPrice(PriceTrackingData priceTrackingDataProto) {
|
||||
if (!priceTrackingDataProto.hasBuyableProduct()) {
|
||||
return false;
|
||||
}
|
||||
if (!priceTrackingDataProto.getBuyableProduct().hasCurrentPrice()) {
|
||||
return false;
|
||||
}
|
||||
if (!priceTrackingDataProto.getBuyableProduct().getCurrentPrice().hasAmountMicros()
|
||||
|| !priceTrackingDataProto.getBuyableProduct()
|
||||
.getCurrentPrice()
|
||||
.hasCurrencyCode()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the price string
|
||||
* @param priceString a string representing the price of the shopping offer
|
||||
@@ -539,6 +664,8 @@ public class ShoppingPersistedTabData extends PersistedTabData {
|
||||
return System.currentTimeMillis() - CriticalPersistedTabData.from(tab).getTimestampMillis();
|
||||
}
|
||||
|
||||
// TODO(crbug.com/1196860) remove as OptimizationType.PRICE_TRACKING deprecates the need for
|
||||
// this
|
||||
private static void isShoppingPage(GURL url, Callback<Boolean> callback) {
|
||||
OptimizationGuideBridgeFactoryHolder.sOptimizationGuideBridgeFactory.create()
|
||||
.canApplyOptimization(url, HintsProto.OptimizationType.SHOPPING_PAGE_PREDICTOR,
|
||||
|
59
chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/proto/price_tracking.proto
Normal file
59
chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/state/proto/price_tracking.proto
Normal file
@@ -0,0 +1,59 @@
|
||||
// Copyright 2021 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.
|
||||
|
||||
syntax = "proto2";
|
||||
|
||||
package org.chromium.chrome.browser.tab.proto;
|
||||
|
||||
option java_package = "org.chromium.chrome.browser.tab.proto";
|
||||
|
||||
message PriceTrackingData {
|
||||
// metadata relating to the product
|
||||
optional BuyableProduct buyable_product = 1;
|
||||
|
||||
// price update data for the product
|
||||
optional ProductPriceUpdate product_update = 2;
|
||||
}
|
||||
|
||||
message BuyableProduct {
|
||||
enum ProductReferenceType {
|
||||
UNKNOWN = 0;
|
||||
// The product referenced in the product details page.
|
||||
MAIN_PRODUCT = 1;
|
||||
}
|
||||
|
||||
// The title of the product.
|
||||
optional string title = 1;
|
||||
|
||||
// Direct link to the product image.
|
||||
optional string image_url = 2;
|
||||
|
||||
// Price as shown in the page.
|
||||
optional ProductPrice current_price = 3;
|
||||
|
||||
// Determines how the product is referenced in the current page.
|
||||
optional ProductReferenceType reference_type = 4;
|
||||
|
||||
// Docid of the offer.
|
||||
optional fixed64 offer_id = 5;
|
||||
}
|
||||
|
||||
message ProductPriceUpdate {
|
||||
// Docid of the offer represented by this update.
|
||||
optional fixed64 offer_id = 1;
|
||||
|
||||
// Old price as seen on the shopping backend.
|
||||
optional ProductPrice old_price = 2;
|
||||
|
||||
// New price as seen on the shopping backend.
|
||||
optional ProductPrice new_price = 3;
|
||||
}
|
||||
|
||||
message ProductPrice {
|
||||
// Code for the currency e.g. USD.
|
||||
optional string currency_code = 1;
|
||||
|
||||
// Price in micros.
|
||||
optional int64 amount_micros = 2;
|
||||
}
|
@@ -59,6 +59,8 @@ std::string GetStringNameForOptimizationType(
|
||||
return "LoginDetection";
|
||||
case proto::OptimizationType::MERCHANT_TRUST_SIGNALS:
|
||||
return "MerchantTrustSignals";
|
||||
case proto::OptimizationType::PRICE_TRACKING:
|
||||
return "PriceTracking";
|
||||
}
|
||||
NOTREACHED();
|
||||
return std::string();
|
||||
|
@@ -151,6 +151,8 @@ enum OptimizationType {
|
||||
// Provides key information about the merchant represented by the current
|
||||
// host.
|
||||
MERCHANT_TRUST_SIGNALS = 17;
|
||||
// Provides pricing data so the user can track prices and price updates.
|
||||
PRICE_TRACKING = 18;
|
||||
}
|
||||
|
||||
// Presents semantics for how page load URLs should be matched.
|
||||
|
@@ -12534,6 +12534,8 @@ reviews. Googlers can read more about this at go/gwsq-gerrit.
|
||||
</suffix>
|
||||
<suffix name="PerformanceHints"
|
||||
label="Provides aggregated performance information about the page"/>
|
||||
<suffix name="PriceTracking"
|
||||
label="Returns price related data for shopping websites"/>
|
||||
<suffix name="ResourceLoading"
|
||||
label="Applies a set of resource loading hints to load the page">
|
||||
<obsolete>
|
||||
|
Reference in New Issue
Block a user