chromium/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/settings/ReauthenticationManagerTest.java

// Copyright 2017 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_manager.settings;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;

import android.app.Activity;
import android.content.Intent;
import android.view.View;

import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
import org.robolectric.annotation.Config;
import org.robolectric.annotation.LooperMode;

import org.chromium.base.test.BaseRobolectricTestRunner;
import org.chromium.chrome.R;

/** Tests for the "Save Passwords" settings screen. */
@RunWith(BaseRobolectricTestRunner.class)
@Config(manifest = Config.NONE)
@LooperMode(LooperMode.Mode.LEGACY)
public class ReauthenticationManagerTest {
    private FragmentManager mFragmentManager;

    private FragmentActivity mTestActivity;

    @Before
    public void setUp() {
        mTestActivity = Robolectric.setupActivity(FragmentActivity.class);
        PasswordReauthenticationFragment.preventLockingForTesting();

        mFragmentManager = mTestActivity.getSupportFragmentManager();

        // Prepare a dummy Fragment and commit a FragmentTransaction with it.
        FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();
        // Replacement fragment for CredentialEntryFragment, which is the fragment that
        // replaces PasswordReauthentication after popBackStack is called.
        Fragment mockCredentialEntryFragment = new Fragment();
        fragmentTransaction.add(mockCredentialEntryFragment, "credential_entry_fragment");
        fragmentTransaction.addToBackStack(null);
        fragmentTransaction.commit();
    }

    /**
     * Prepares a dummy Intent to pass to PasswordReauthenticationFragment as a fake result of the
     * reauthentication screen.
     *
     * @return The dummy Intent.
     */
    private Intent prepareDummyDataForActivityResult() {
        Intent data = new Intent();
        data.putExtra("result", "This is the result");
        return data;
    }

    /**
     * Ensure that displayReauthenticationFragment puts the reauthentication fragment on the
     * transaction stack and updates the validity of the reauth when reauth passed.
     */
    @Test
    public void testDisplayReauthenticationFragment_Passed() {
        ReauthenticationManager.resetLastReauth();
        assertFalse(
                ReauthenticationManager.authenticationStillValid(
                        ReauthenticationManager.ReauthScope.ONE_AT_A_TIME));
        assertFalse(
                ReauthenticationManager.authenticationStillValid(
                        ReauthenticationManager.ReauthScope.BULK));

        ReauthenticationManager.displayReauthenticationFragment(
                R.string.lockscreen_description_view,
                View.NO_ID,
                mFragmentManager,
                ReauthenticationManager.ReauthScope.ONE_AT_A_TIME);
        Fragment reauthFragment =
                mFragmentManager.findFragmentByTag(ReauthenticationManager.FRAGMENT_TAG);
        assertNotNull(reauthFragment);

        reauthFragment.onActivityResult(
                PasswordReauthenticationFragment.CONFIRM_DEVICE_CREDENTIAL_REQUEST_CODE,
                Activity.RESULT_OK,
                prepareDummyDataForActivityResult());
        mFragmentManager.executePendingTransactions();

        assertTrue(
                ReauthenticationManager.authenticationStillValid(
                        ReauthenticationManager.ReauthScope.ONE_AT_A_TIME));
    }

    /**
     * Ensure that displayReauthenticationFragment puts the reauthentication fragment on the
     * transaction stack and updates the validity of the reauth when reauth failed.
     */
    @Test
    public void testDisplayReauthenticationFragment_Failed() {
        ReauthenticationManager.resetLastReauth();
        assertFalse(
                ReauthenticationManager.authenticationStillValid(
                        ReauthenticationManager.ReauthScope.ONE_AT_A_TIME));
        assertFalse(
                ReauthenticationManager.authenticationStillValid(
                        ReauthenticationManager.ReauthScope.BULK));

        ReauthenticationManager.displayReauthenticationFragment(
                R.string.lockscreen_description_view,
                View.NO_ID,
                mFragmentManager,
                ReauthenticationManager.ReauthScope.ONE_AT_A_TIME);
        Fragment reauthFragment =
                mFragmentManager.findFragmentByTag(ReauthenticationManager.FRAGMENT_TAG);
        assertNotNull(reauthFragment);

        reauthFragment.onActivityResult(
                PasswordReauthenticationFragment.CONFIRM_DEVICE_CREDENTIAL_REQUEST_CODE,
                Activity.RESULT_CANCELED,
                prepareDummyDataForActivityResult());
        mFragmentManager.executePendingTransactions();

        assertFalse(
                ReauthenticationManager.authenticationStillValid(
                        ReauthenticationManager.ReauthScope.ONE_AT_A_TIME));
        assertFalse(
                ReauthenticationManager.authenticationStillValid(
                        ReauthenticationManager.ReauthScope.BULK));
    }

    /**
     * Ensure that displayReauthenticationFragment considers BULK scope to cover the ONE_AT_A_TIME
     * scope as well.
     */
    @Test
    public void testDisplayReauthenticationFragment_OneAtATimeCovered() {
        ReauthenticationManager.resetLastReauth();
        assertFalse(
                ReauthenticationManager.authenticationStillValid(
                        ReauthenticationManager.ReauthScope.ONE_AT_A_TIME));
        assertFalse(
                ReauthenticationManager.authenticationStillValid(
                        ReauthenticationManager.ReauthScope.BULK));

        ReauthenticationManager.displayReauthenticationFragment(
                R.string.lockscreen_description_export,
                View.NO_ID,
                mFragmentManager,
                ReauthenticationManager.ReauthScope.BULK);
        Fragment reauthFragment =
                mFragmentManager.findFragmentByTag(ReauthenticationManager.FRAGMENT_TAG);
        assertNotNull(reauthFragment);

        reauthFragment.onActivityResult(
                PasswordReauthenticationFragment.CONFIRM_DEVICE_CREDENTIAL_REQUEST_CODE,
                Activity.RESULT_OK,
                prepareDummyDataForActivityResult());
        mFragmentManager.executePendingTransactions();

        // Both BULK and ONE_AT_A_TIME scopes should be covered by the BULK request above.
        assertTrue(
                ReauthenticationManager.authenticationStillValid(
                        ReauthenticationManager.ReauthScope.ONE_AT_A_TIME));
        assertTrue(
                ReauthenticationManager.authenticationStillValid(
                        ReauthenticationManager.ReauthScope.BULK));
    }

    /**
     * Ensure that displayReauthenticationFragment does not consider ONE_AT_A_TIME scope to cover
     * the BULK scope.
     */
    @Test
    public void testDisplayReauthenticationFragment_BulkNotCovered() {
        ReauthenticationManager.resetLastReauth();
        assertFalse(
                ReauthenticationManager.authenticationStillValid(
                        ReauthenticationManager.ReauthScope.ONE_AT_A_TIME));
        assertFalse(
                ReauthenticationManager.authenticationStillValid(
                        ReauthenticationManager.ReauthScope.BULK));

        ReauthenticationManager.displayReauthenticationFragment(
                R.string.lockscreen_description_view,
                View.NO_ID,
                mFragmentManager,
                ReauthenticationManager.ReauthScope.ONE_AT_A_TIME);
        Fragment reauthFragment =
                mFragmentManager.findFragmentByTag(ReauthenticationManager.FRAGMENT_TAG);
        assertNotNull(reauthFragment);

        reauthFragment.onActivityResult(
                PasswordReauthenticationFragment.CONFIRM_DEVICE_CREDENTIAL_REQUEST_CODE,
                Activity.RESULT_OK,
                prepareDummyDataForActivityResult());
        mFragmentManager.executePendingTransactions();

        // Only ONE_AT_A_TIME scope should be covered by the ONE_AT_A_TIME request above.
        assertTrue(
                ReauthenticationManager.authenticationStillValid(
                        ReauthenticationManager.ReauthScope.ONE_AT_A_TIME));
        assertFalse(
                ReauthenticationManager.authenticationStillValid(
                        ReauthenticationManager.ReauthScope.BULK));
    }
}