// Copyright 2020 The Chromium Authors
// 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.password_check;
import static androidx.test.espresso.matcher.ViewMatchers.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.chromium.chrome.browser.password_check.PasswordCheckProperties.CompromisedCredentialProperties.COMPROMISED_CREDENTIAL;
import static org.chromium.chrome.browser.password_check.PasswordCheckProperties.CompromisedCredentialProperties.CREDENTIAL_HANDLER;
import static org.chromium.chrome.browser.password_check.PasswordCheckProperties.CompromisedCredentialProperties.HAS_MANUAL_CHANGE_BUTTON;
import static org.chromium.chrome.browser.password_check.PasswordCheckProperties.DELETION_CONFIRMATION_HANDLER;
import static org.chromium.chrome.browser.password_check.PasswordCheckProperties.HeaderProperties.CHECK_PROGRESS;
import static org.chromium.chrome.browser.password_check.PasswordCheckProperties.HeaderProperties.CHECK_STATUS;
import static org.chromium.chrome.browser.password_check.PasswordCheckProperties.HeaderProperties.CHECK_TIMESTAMP;
import static org.chromium.chrome.browser.password_check.PasswordCheckProperties.HeaderProperties.COMPROMISED_CREDENTIALS_COUNT;
import static org.chromium.chrome.browser.password_check.PasswordCheckProperties.HeaderProperties.LAUNCH_ACCOUNT_CHECKUP_ACTION;
import static org.chromium.chrome.browser.password_check.PasswordCheckProperties.HeaderProperties.RESTART_BUTTON_ACTION;
import static org.chromium.chrome.browser.password_check.PasswordCheckProperties.HeaderProperties.SHOW_CHECK_SUBTITLE;
import static org.chromium.chrome.browser.password_check.PasswordCheckProperties.HeaderProperties.UNKNOWN_PROGRESS;
import static org.chromium.chrome.browser.password_check.PasswordCheckProperties.ITEMS;
import static org.chromium.chrome.browser.password_check.PasswordCheckUIStatus.CANCELED;
import static org.chromium.chrome.browser.password_check.PasswordCheckUIStatus.ERROR_OFFLINE;
import static org.chromium.chrome.browser.password_check.PasswordCheckUIStatus.ERROR_UNKNOWN;
import static org.chromium.chrome.browser.password_check.PasswordCheckUIStatus.IDLE;
import static org.chromium.chrome.browser.password_check.PasswordCheckUIStatus.RUNNING;
import android.content.DialogInterface;
import android.util.Pair;
import androidx.appcompat.app.AlertDialog;
import androidx.test.core.app.ApplicationProvider;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.annotation.Config;
import org.chromium.base.Callback;
import org.chromium.base.metrics.RecordHistogram;
import org.chromium.base.test.BaseRobolectricTestRunner;
import org.chromium.chrome.browser.password_check.PasswordCheckProperties.ItemType;
import org.chromium.chrome.browser.password_check.helper.PasswordCheckChangePasswordHelper;
import org.chromium.chrome.browser.password_check.helper.PasswordCheckIconHelper;
import org.chromium.chrome.browser.password_manager.PasswordCheckReferrer;
import org.chromium.chrome.browser.password_manager.settings.PasswordAccessReauthenticationHelper;
import org.chromium.ui.modelutil.ListModel;
import org.chromium.ui.modelutil.MVCListAdapter;
import org.chromium.ui.modelutil.PropertyModel;
import org.chromium.url.GURL;
/**
* Controller tests verify that the PasswordCheck controller modifies the model if the API is used
* properly.
*/
@RunWith(BaseRobolectricTestRunner.class)
@Config(manifest = Config.NONE)
@SuppressWarnings("DoNotMock") // Mocks GURL.
public class PasswordCheckControllerTest {
private static final CompromisedCredential ANA =
new CompromisedCredential(
"https://m.a.xyz/signin",
mock(GURL.class),
"Ana",
"m.a.xyz",
"Ana",
"password",
"",
"xyz.a.some.package",
2,
2,
true,
false);
private static final CompromisedCredential BOB =
new CompromisedCredential(
"http://www.b.ch/signin",
mock(GURL.class),
"",
"http://www.b.ch",
"(No username)",
"DoneSth",
"http://www.b.ch/.well-known/change-password",
"",
1,
1,
true,
false);
private static final CompromisedCredential CHARLIE =
new CompromisedCredential(
"http://www.c.de/login",
mock(GURL.class),
"",
"http://www.c.de",
"user1",
"secret",
"http://www.c.de/.well-known/change-password",
"",
1,
1,
true,
false);
private static final Pair<Integer, Integer> PROGRESS_UPDATE = new Pair<>(2, 19);
private static final String PASSWORD_CHECK_RESOLUTION_HISTOGRAM_WITHOUT_AUTO_BUTTON =
"PasswordManager.AutomaticChange.AcceptanceWithoutAutoButton";
private static final String PASSWORD_CHECK_REFERRER_HISTOGRAM =
"PasswordManager.BulkCheck.PasswordCheckReferrerAndroid2";
private static final String PASSWORD_CHECK_USER_ACTION_HISTOGRAM =
"PasswordManager.BulkCheck.UserActionAndroid";
private static final String PASSWORD_CHECK_COMPROMISED_CREDENTIALS_AFTER_CHECK_HISTOGRAM =
"PasswordManager.BulkCheck.CompromisedCredentialsCountAfterCheckAndroid";
@Mock private PasswordCheckComponentUi.Delegate mDelegate;
@Mock private PasswordCheckChangePasswordHelper mChangePasswordDelegate;
@Mock private PasswordCheck mPasswordCheck;
@Mock private PasswordAccessReauthenticationHelper mReauthenticationHelper;
@Mock private PasswordCheckIconHelper mIconHelper;
@Captor private ArgumentCaptor<Callback<Boolean>> mCallbackCaptor;
// DO NOT INITIALIZE HERE! The objects would be shared here which leaks state between tests.
private PasswordCheckMediator mMediator;
private PropertyModel mModel;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mModel = PasswordCheckProperties.createDefaultModel();
mMediator =
new PasswordCheckMediator(
mChangePasswordDelegate, mReauthenticationHelper, mIconHelper);
PasswordCheckFactory.setPasswordCheckForTesting(mPasswordCheck);
mMediator.initialize(mModel, mDelegate, PasswordCheckReferrer.PASSWORD_SETTINGS, () -> {});
PasswordCheckMediator.setStatusUpdateDelayMillis(0);
}
@Test
public void testRecordsStartCheckManually() {
// In order to start another check, the status of the current check needs to be IDLE.
mMediator.onPasswordCheckStatusChanged(IDLE);
mModel.get(ITEMS).get(0).model.get(RESTART_BUTTON_ACTION).run();
assertThat(
RecordHistogram.getHistogramValueCountForTesting(
PASSWORD_CHECK_USER_ACTION_HISTOGRAM,
PasswordCheckUserAction.START_CHECK_MANUALLY),
is(1));
}
@Test
public void testCreatesValidDefaultModel() {
verify(mPasswordCheck).addObserver(mMediator, true);
assertNotNull(mModel.get(ITEMS));
}
@Test
public void testAddsAndRemovesFromObserverList() {
mMediator.destroy();
verify(mPasswordCheck).removeObserver(mMediator);
}
@Test
public void testInitializeRunningHeaderAndTriggerRunAsSoonAsPossible() {
assertRunningHeader(mModel.get(ITEMS).get(0), UNKNOWN_PROGRESS);
verify(mPasswordCheck).startCheck();
}
@Test
public void testInitializeHeaderWithLastStatusWhenComingFromSafetyCheck() {
clearInvocations(mPasswordCheck); // Clear invocations from setup code.
when(mPasswordCheck.getCheckStatus()).thenReturn(PasswordCheckUIStatus.IDLE);
mMediator.initialize(mModel, mDelegate, PasswordCheckReferrer.SAFETY_CHECK, () -> {});
assertIdleHeader(mModel.get(ITEMS).get(0));
verify(mPasswordCheck, never()).startCheck();
}
@Test
public void testCreatesHeaderForStatus() {
mMediator.onPasswordCheckStatusChanged(IDLE);
ListModel<MVCListAdapter.ListItem> itemList = mModel.get(ITEMS);
assertThat(itemList.get(0).type, is(ItemType.HEADER));
assertThat(itemList.get(0).model.get(CHECK_STATUS), is(IDLE));
assertNotNull(itemList.get(0).model.get(RESTART_BUTTON_ACTION));
}
@Test
public void testUpdateStatusHeaderOnError() {
assertRunningHeader(mModel.get(ITEMS).get(0), UNKNOWN_PROGRESS);
mMediator.onPasswordCheckStatusChanged(ERROR_OFFLINE);
ListModel<MVCListAdapter.ListItem> itemList = mModel.get(ITEMS);
assertThat(itemList.size(), is(1));
MVCListAdapter.ListItem header = itemList.get(0);
assertHeaderTypeWithStatus(header, ERROR_OFFLINE);
assertNull(header.model.get(CHECK_PROGRESS));
assertNull(header.model.get(CHECK_TIMESTAMP));
assertNull(header.model.get(COMPROMISED_CREDENTIALS_COUNT));
}
@Test
public void testUpdateStatusHeaderOnIdle() {
assertRunningHeader(mModel.get(ITEMS).get(0), UNKNOWN_PROGRESS);
mMediator.onPasswordCheckStatusChanged(IDLE);
ListModel<MVCListAdapter.ListItem> itemList = mModel.get(ITEMS);
assertThat(itemList.size(), is(1));
assertIdleHeader(itemList.get(0));
}
@Test
public void testUpdateProgressHeader() {
assertRunningHeader(mModel.get(ITEMS).get(0), UNKNOWN_PROGRESS);
int already_processed = PROGRESS_UPDATE.first;
int remaining_in_queue = PROGRESS_UPDATE.second - already_processed;
mMediator.onPasswordCheckProgressChanged(already_processed, remaining_in_queue);
assertRunningHeader(mModel.get(ITEMS).get(0), PROGRESS_UPDATE);
}
@Test
public void testOnViewRecordsViewClick() {
mMediator.onView(ANA);
assertThat(
RecordHistogram.getHistogramValueCountForTesting(
PASSWORD_CHECK_USER_ACTION_HISTOGRAM,
PasswordCheckUserAction.VIEW_PASSWORD_CLICK),
is(1));
}
@Test
public void testOnEditRecordsEditClick() {
mMediator.onEdit(ANA, ApplicationProvider.getApplicationContext());
assertThat(
RecordHistogram.getHistogramValueCountForTesting(
PASSWORD_CHECK_USER_ACTION_HISTOGRAM,
PasswordCheckUserAction.EDIT_PASSWORD_CLICK),
is(1));
}
@Test
public void testCreatesEntryForExistingCredentials() {
when(mPasswordCheck.getCompromisedCredentials())
.thenReturn(new CompromisedCredential[] {ANA});
when(mChangePasswordDelegate.canManuallyChangeCredential(eq(ANA))).thenReturn(true);
mMediator.onPasswordCheckStatusChanged(IDLE);
mMediator.onCompromisedCredentialsFetchCompleted();
assertThat(mModel.get(ITEMS).get(1).type, is(ItemType.COMPROMISED_CREDENTIAL));
assertThat(mModel.get(ITEMS).get(1).model.get(COMPROMISED_CREDENTIAL), equalTo(ANA));
assertThat(mModel.get(ITEMS).get(1).model.get(CREDENTIAL_HANDLER), is(mMediator));
assertThat(mModel.get(ITEMS).get(1).model.get(HAS_MANUAL_CHANGE_BUTTON), is(true));
verify(mIconHelper).getLargeIcon(eq(ANA), any(Callback.class));
}
@Test
public void testHidesChangeButtonIfManualChangeIsNotPossible() {
when(mPasswordCheck.getCompromisedCredentials())
.thenReturn(new CompromisedCredential[] {BOB});
when(mChangePasswordDelegate.canManuallyChangeCredential(eq(BOB))).thenReturn(false);
mMediator.onPasswordCheckStatusChanged(IDLE);
mMediator.onCompromisedCredentialsFetchCompleted();
assertThat(mModel.get(ITEMS).get(1).model.get(COMPROMISED_CREDENTIAL), equalTo(BOB));
assertThat(mModel.get(ITEMS).get(1).model.get(CREDENTIAL_HANDLER), is(mMediator));
assertThat(mModel.get(ITEMS).get(1).model.get(HAS_MANUAL_CHANGE_BUTTON), is(false));
}
@Test
public void testReplacesEntriesForUpdateOfEntireList() {
mMediator.onPasswordCheckStatusChanged(IDLE);
// First call adds only ANA.
when(mPasswordCheck.getCompromisedCredentials())
.thenReturn(new CompromisedCredential[] {ANA});
mMediator.onCompromisedCredentialsFetchCompleted();
assertThat(mModel.get(ITEMS).size(), is(2)); // Header + existing credentials.
// Second call adds BOB and removes ANA.
when(mPasswordCheck.getCompromisedCredentials())
.thenReturn(new CompromisedCredential[] {BOB});
mMediator.onCompromisedCredentialsFetchCompleted();
assertThat(mModel.get(ITEMS).size(), is(2));
assertThat(mModel.get(ITEMS).get(1).model.get(COMPROMISED_CREDENTIAL), equalTo(BOB));
assertThat(mModel.get(ITEMS).get(1).model.get(CREDENTIAL_HANDLER), is(mMediator));
}
@Test
public void testIdleStatusUpdatedOnCredentialsFetchCompleted() {
// Set initial status to IDLE with no compromised credentials.
when(mPasswordCheck.getCompromisedCredentialsCount()).thenReturn(0);
mMediator.onPasswordCheckStatusChanged(IDLE);
assertThat(getHeaderModel().get(COMPROMISED_CREDENTIALS_COUNT), is(0));
// Add 2 compromised credentials.
when(mPasswordCheck.getCompromisedCredentials())
.thenReturn(new CompromisedCredential[] {ANA, BOB});
when(mPasswordCheck.getCompromisedCredentialsCount()).thenReturn(2);
mMediator.onCompromisedCredentialsFetchCompleted();
assertThat(mModel.get(ITEMS).size(), is(3)); // Header + existing credentials.
// Check the compromised credentials count updated.
assertThat(getHeaderModel().get(COMPROMISED_CREDENTIALS_COUNT), is(2));
}
@Test
public void testCanceledStatusDoesntUpdateModel() {
assertRunningHeader(mModel.get(ITEMS).get(0), UNKNOWN_PROGRESS);
mMediator.onPasswordCheckStatusChanged(CANCELED);
// Check that the header model didn't change.
assertRunningHeader(mModel.get(ITEMS).get(0), UNKNOWN_PROGRESS);
}
@Test
public void testNotIdleStatusNotUpdatedOnCredentialsFetchCompleted() {
mMediator.onPasswordCheckStatusChanged(RUNNING);
assertNull(getHeaderModel().get(COMPROMISED_CREDENTIALS_COUNT));
// Add ANA while the check is running.
when(mPasswordCheck.getCompromisedCredentials())
.thenReturn(new CompromisedCredential[] {ANA});
when(mPasswordCheck.getCompromisedCredentialsCount()).thenReturn(1);
mMediator.onCompromisedCredentialsFetchCompleted();
assertThat(mModel.get(ITEMS).size(), is(2)); // Header + existing credentials.
// Check the compromised credential count did not update.
assertNull(getHeaderModel().get(COMPROMISED_CREDENTIALS_COUNT));
}
@Test
public void testOnStatusUpdateAsIdleShowSubtitle() {
mMediator.onPasswordCheckStatusChanged(IDLE);
assertThat(getHeaderModel().get(SHOW_CHECK_SUBTITLE), is(true));
}
@Test
public void testOnStatusUpdateAsNotIdleNotShowSubtitle() {
mMediator.onPasswordCheckStatusChanged(ERROR_UNKNOWN);
assertThat(getHeaderModel().get(SHOW_CHECK_SUBTITLE), is(false));
}
@Test
public void testShowSubtitleOnCompromisedCredentialsFetched() {
when(mPasswordCheck.getCompromisedCredentials())
.thenReturn(new CompromisedCredential[] {ANA});
when(mPasswordCheck.getCompromisedCredentialsCount()).thenReturn(1);
mMediator.onCompromisedCredentialsFetchCompleted();
assertThat(getHeaderModel().get(SHOW_CHECK_SUBTITLE), is(true));
}
@Test
public void testShowSubtitleOnNoCompromisedCredentialsFetchedIfIdleStatus() {
mMediator.onPasswordCheckStatusChanged(IDLE);
when(mPasswordCheck.getCompromisedCredentials()).thenReturn(new CompromisedCredential[] {});
when(mPasswordCheck.getCompromisedCredentialsCount()).thenReturn(0);
mMediator.onCompromisedCredentialsFetchCompleted();
assertThat(getHeaderModel().get(SHOW_CHECK_SUBTITLE), is(true));
}
@Test
public void testNotShowSubtitleOnNoCompromisedCredentialsFetched() {
when(mPasswordCheck.getCompromisedCredentials()).thenReturn(new CompromisedCredential[] {});
when(mPasswordCheck.getCompromisedCredentialsCount()).thenReturn(0);
mMediator.onCompromisedCredentialsFetchCompleted();
assertThat(getHeaderModel().get(SHOW_CHECK_SUBTITLE), is(false));
}
@Test
public void testSortsInitialSetOfCredentals() {
mMediator.onPasswordCheckStatusChanged(IDLE);
CompromisedCredential phishedEarly =
makeCredential("example.com", "alice", 1, 1, false, true);
CompromisedCredential phishedLeakedLate =
makeCredential("test.com", "bob", 3, 3, true, true);
CompromisedCredential leakedEarly =
makeCredential("example.org", "alice", 2, 2, true, false);
CompromisedCredential leakedLate = makeCredential("site.com", "john", 4, 4, true, false);
when(mPasswordCheck.getCompromisedCredentials())
.thenReturn(
new CompromisedCredential[] {
phishedEarly, leakedEarly, leakedLate, phishedLeakedLate
});
mMediator.onCompromisedCredentialsFetchCompleted();
assertThat(mModel.get(ITEMS).size(), is(5));
assertThat(
mModel.get(ITEMS).get(1).model.get(COMPROMISED_CREDENTIAL), is(phishedLeakedLate));
assertThat(mModel.get(ITEMS).get(2).model.get(COMPROMISED_CREDENTIAL), is(phishedEarly));
assertThat(mModel.get(ITEMS).get(3).model.get(COMPROMISED_CREDENTIAL), is(leakedLate));
assertThat(mModel.get(ITEMS).get(4).model.get(COMPROMISED_CREDENTIAL), is(leakedEarly));
}
@Test
public void testSortsAppendedCredentials() {
mMediator.onPasswordCheckStatusChanged(IDLE);
CompromisedCredential phishedEarly =
makeCredential("example.com", "alice", 1, 1, false, true);
CompromisedCredential phishedLeakedLate =
makeCredential("test.com", "bob", 3, 3, true, true);
CompromisedCredential leakedEarly =
makeCredential("example.org", "alice", 2, 2, true, false);
CompromisedCredential leakedLate = makeCredential("site.com", "john", 4, 4, true, false);
// Send the initial set of credentials (to simulate loading them from disk).
when(mPasswordCheck.getCompromisedCredentials())
.thenReturn(
new CompromisedCredential[] {
phishedEarly, leakedEarly, leakedLate, phishedLeakedLate
});
mMediator.onCompromisedCredentialsFetchCompleted();
// Send an updated list simulating credentials found in the current check.
CompromisedCredential leakedNewEarly1 =
makeCredential("example.com", "john", 5, 5, true, false);
CompromisedCredential leakedNewEarly2 =
makeCredential("test.com", "john", 5, 5, true, false);
CompromisedCredential leakedNewLate =
makeCredential("site.org", "alice", 6, 6, true, false);
when(mPasswordCheck.getCompromisedCredentials())
.thenReturn(
new CompromisedCredential[] {
phishedEarly,
leakedEarly,
leakedLate,
leakedNewEarly2,
leakedNewLate,
leakedNewEarly1,
phishedLeakedLate
});
mMediator.onCompromisedCredentialsFetchCompleted();
// Expect that the order of the original set has been maintained and that the newly found
// leaked credentials appear at the end in ascending order of creation time (or in ascending
// alphabetical order for equal times).
assertThat(mModel.get(ITEMS).size(), is(8));
assertThat(
mModel.get(ITEMS).get(1).model.get(COMPROMISED_CREDENTIAL), is(phishedLeakedLate));
assertThat(mModel.get(ITEMS).get(2).model.get(COMPROMISED_CREDENTIAL), is(phishedEarly));
assertThat(mModel.get(ITEMS).get(3).model.get(COMPROMISED_CREDENTIAL), is(leakedLate));
assertThat(mModel.get(ITEMS).get(4).model.get(COMPROMISED_CREDENTIAL), is(leakedEarly));
assertThat(mModel.get(ITEMS).get(5).model.get(COMPROMISED_CREDENTIAL), is(leakedNewEarly1));
assertThat(mModel.get(ITEMS).get(6).model.get(COMPROMISED_CREDENTIAL), is(leakedNewEarly2));
assertThat(mModel.get(ITEMS).get(7).model.get(COMPROMISED_CREDENTIAL), is(leakedNewLate));
}
@Test
public void testOnRemoveRecordsDeleteClick() {
mMediator.onRemove(ANA);
assertThat(
RecordHistogram.getHistogramValueCountForTesting(
PASSWORD_CHECK_USER_ACTION_HISTOGRAM,
PasswordCheckUserAction.DELETE_PASSWORD_CLICK),
is(1));
}
@Test
public void testRemovingElementTriggersDelegate() {
// Removing sets a valid handler:
mMediator.onRemove(ANA);
assertNotNull(mModel.get(DELETION_CONFIRMATION_HANDLER));
// When the handler is triggered (because the dialog was confirmed), remove the credential:
mModel.get(DELETION_CONFIRMATION_HANDLER)
.onClick(mock(DialogInterface.class), AlertDialog.BUTTON_POSITIVE);
verify(mDelegate).removeCredential(eq(ANA));
assertNull(mModel.get(DELETION_CONFIRMATION_HANDLER));
}
@Test
public void testRemovingElementRecordsDeletedPassword() {
mMediator.onRemove(BOB);
assertNotNull(mModel.get(DELETION_CONFIRMATION_HANDLER));
// When the handler is triggered (because the dialog was confirmed), remove the credential:
mModel.get(DELETION_CONFIRMATION_HANDLER)
.onClick(mock(DialogInterface.class), AlertDialog.BUTTON_POSITIVE);
assertThat(
RecordHistogram.getHistogramValueCountForTesting(
PASSWORD_CHECK_USER_ACTION_HISTOGRAM,
PasswordCheckUserAction.DELETED_PASSWORD),
is(1));
assertThat(
RecordHistogram.getHistogramTotalCountForTesting(
PASSWORD_CHECK_RESOLUTION_HISTOGRAM_WITHOUT_AUTO_BUTTON),
is(1));
}
@Test
public void testOnChangePasswordButtonClick() {
mMediator.onChangePasswordButtonClick(ANA);
verify(mChangePasswordDelegate).launchAppOrCctWithChangePasswordUrl(eq(ANA));
assertThat(
RecordHistogram.getHistogramValueCountForTesting(
PASSWORD_CHECK_USER_ACTION_HISTOGRAM,
PasswordCheckUserAction.CHANGE_PASSWORD),
is(1));
assertThat(
RecordHistogram.getHistogramValueCountForTesting(
PASSWORD_CHECK_RESOLUTION_HISTOGRAM_WITHOUT_AUTO_BUTTON,
PasswordCheckResolutionAction.OPENED_SITE),
is(1));
}
@Test
public void testOnEditTriggersDelegateWhenNewEditEnabled() {
mMediator.onEdit(ANA, ApplicationProvider.getApplicationContext());
verify(mDelegate).onEditCredential(ANA, ApplicationProvider.getApplicationContext());
}
@Test
public void testRecordsPasswordCheckReferrer() {
assertThat(
RecordHistogram.getHistogramTotalCountForTesting(PASSWORD_CHECK_REFERRER_HISTOGRAM),
is(1));
assertThat(
RecordHistogram.getHistogramValueCountForTesting(
PASSWORD_CHECK_REFERRER_HISTOGRAM, PasswordCheckReferrer.PASSWORD_SETTINGS),
is(1));
}
@Test
public void testRecordsDidNothingOnLeavingPage() {
when(mPasswordCheck.getCompromisedCredentials())
.thenReturn(new CompromisedCredential[] {ANA, BOB, CHARLIE});
when(mChangePasswordDelegate.canManuallyChangeCredential(any(CompromisedCredential.class)))
.thenReturn(true);
mMediator.onPasswordCheckStatusChanged(IDLE);
mMediator.onCompromisedCredentialsFetchCompleted();
mMediator.onUserLeavesCheckPage();
assertThat(
RecordHistogram.getHistogramValueCountForTesting(
PASSWORD_CHECK_RESOLUTION_HISTOGRAM_WITHOUT_AUTO_BUTTON,
PasswordCheckResolutionAction.DID_NOTHING),
is(3));
}
@Test
public void testRecordCompromisedCredentialCount() {
when(mPasswordCheck.getCompromisedCredentials())
.thenReturn(new CompromisedCredential[] {ANA, BOB, CHARLIE});
when(mPasswordCheck.getCompromisedCredentialsCount()).thenReturn(3);
when(mChangePasswordDelegate.canManuallyChangeCredential(any(CompromisedCredential.class)))
.thenReturn(true);
mMediator.onPasswordCheckStatusChanged(IDLE);
mMediator.onCompromisedCredentialsFetchCompleted();
mMediator.onUserLeavesCheckPage();
assertThat(
RecordHistogram.getHistogramValueCountForTesting(
PASSWORD_CHECK_COMPROMISED_CREDENTIALS_AFTER_CHECK_HISTOGRAM, 3),
is(1));
}
private void assertIdleHeader(MVCListAdapter.ListItem header) {
assertHeaderTypeWithStatus(header, IDLE);
assertNull(header.model.get(CHECK_PROGRESS));
assertNotNull(header.model.get(CHECK_TIMESTAMP));
assertNotNull(header.model.get(COMPROMISED_CREDENTIALS_COUNT));
}
private void assertRunningHeader(
MVCListAdapter.ListItem header, Pair<Integer, Integer> progress) {
assertHeaderTypeWithStatus(header, RUNNING);
assertThat(header.model.get(CHECK_PROGRESS), is(progress));
assertNull(header.model.get(CHECK_TIMESTAMP));
assertNull(header.model.get(COMPROMISED_CREDENTIALS_COUNT));
}
private void assertHeaderTypeWithStatus(
MVCListAdapter.ListItem header, @PasswordCheckUIStatus int status) {
assertThat(header.type, is(ItemType.HEADER));
assertThat(header.model.get(CHECK_STATUS), is(status));
assertNotNull(header.model.get(RESTART_BUTTON_ACTION));
assertNotNull(header.model.get(LAUNCH_ACCOUNT_CHECKUP_ACTION));
}
private CompromisedCredential makeCredential(
String origin,
String username,
long creationTime,
long lastUsedTime,
boolean leaked,
boolean phished) {
return new CompromisedCredential(
origin,
mock(GURL.class),
username,
origin,
username,
"password",
origin,
new String(),
creationTime,
lastUsedTime,
leaked,
phished);
}
private PropertyModel getHeaderModel() {
return mModel.get(ITEMS).get(0).model;
}
}