chromium/chrome/browser/password_manager/android/junit/src/org/chromium/chrome/browser/password_manager/settings/PasswordReauthenticationFragmentTest.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.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;

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

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;

/** Tests for the "Save Passwords" settings screen. */
@RunWith(BaseRobolectricTestRunner.class)
@Config(manifest = Config.NONE)
@LooperMode(LooperMode.Mode.LEGACY)
public class PasswordReauthenticationFragmentTest {
    // All reauthentication scopes to be checked in the tests.
    private static final int[] ALL_SCOPES = {
        ReauthenticationManager.ReauthScope.ONE_AT_A_TIME, ReauthenticationManager.ReauthScope.BULK
    };

    /**
     * Creates a dummy fragment, pushes the reauth fragment on top of it, then resolves the activity
     * for the reauth fragment and checks that back stack is in a correct state.
     *
     * @param resultCode The code which is passed to the reauth fragment as the result of the
     *     activity.
     * @param scope The scope of the reauthentication.
     */
    private void checkPopFromBackStackOnResult(
            int resultCode, @ReauthenticationManager.ReauthScope int scope) {
        PasswordReauthenticationFragment passwordReauthentication =
                new PasswordReauthenticationFragment();
        Bundle args = new Bundle();
        args.putInt(PasswordReauthenticationFragment.DESCRIPTION_ID, 0);
        args.putSerializable(PasswordReauthenticationFragment.SCOPE_ID, scope);
        passwordReauthentication.setArguments(args);

        // Replacement fragment for CredentialEntryFragment, which is the fragment that
        // replaces PasswordReauthentication after popBackStack is called.
        Fragment mockCredentialEntryFragment = new Fragment();

        FragmentActivity testActivity = Robolectric.setupActivity(FragmentActivity.class);
        Intent returnIntent = new Intent();
        returnIntent.putExtra("result", "This is the result");
        PasswordReauthenticationFragment.preventLockingForTesting();

        FragmentManager fragmentManager = testActivity.getSupportFragmentManager();
        FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
        fragmentTransaction.add(mockCredentialEntryFragment, "credential_edit_fragment");
        fragmentTransaction.addToBackStack("add_credential_edit_fragment");
        fragmentTransaction.commit();

        FragmentTransaction fragmentTransaction2 = fragmentManager.beginTransaction();
        fragmentTransaction2.add(passwordReauthentication, "password_reauthentication");
        fragmentTransaction2.addToBackStack("add_password_reauthentication");
        fragmentTransaction2.commit();

        passwordReauthentication.onActivityResult(
                PasswordReauthenticationFragment.CONFIRM_DEVICE_CREDENTIAL_REQUEST_CODE,
                resultCode,
                returnIntent);
        fragmentManager.executePendingTransactions();

        // Assert that the number of fragments in the Back Stack is equal to 1 after
        // reauthentication, as PasswordReauthenticationFragment is popped.
        assertEquals(1, fragmentManager.getBackStackEntryCount());

        // Assert that the remaining fragment in the Back Stack is CredentialEntryFragment.
        assertEquals(
                "add_credential_edit_fragment", fragmentManager.getBackStackEntryAt(0).getName());
    }

    /**
     * Ensure that upon successful reauthentication PasswordReauthenticationFragment is popped from
     * the FragmentManager backstack and the reauthentication is marked as valid.
     */
    @Test
    public void testOnOkActivityResult() {
        for (int scope : ALL_SCOPES) {
            // Ensure that the reauthentication state is changed by setting it to fail the final
            // expectation.
            ReauthenticationManager.resetLastReauth();

            checkPopFromBackStackOnResult(Activity.RESULT_OK, scope);
            assertTrue(ReauthenticationManager.authenticationStillValid(scope));
        }
    }

    /**
     * Ensure that upon canceled reauthentication PasswordReauthenticationFragment is popped from
     * the FragmentManager backstack and the reauthentication is marked as invalid.
     */
    @Test
    public void testOnCanceledActivityResult() {
        for (int scope : ALL_SCOPES) {
            // Ensure that the reauthentication state is changed by setting it to fail the final
            // expectation.
            ReauthenticationManager.recordLastReauth(System.currentTimeMillis(), scope);

            checkPopFromBackStackOnResult(Activity.RESULT_CANCELED, scope);
            assertFalse(ReauthenticationManager.authenticationStillValid(scope));
        }
    }
}