Added unit tests for PerUserCarDevicePolicyService

Test: atest CarServiceUnitTest:com.android.car.admin.PerUserCarDevicePolicyServiceTest
Fixes: 190838580
Bug: 175057848

Change-Id: Ica2f5654e544adc7c9680dd4041e3a502576bf80
diff --git a/service/src/com/android/car/PerUserCarService.java b/service/src/com/android/car/PerUserCarService.java
index ee4dafe..6a9291a 100644
--- a/service/src/com/android/car/PerUserCarService.java
+++ b/service/src/com/android/car/PerUserCarService.java
@@ -76,7 +76,7 @@
 
         if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN)) {
             mPerUserCarDevicePolicyService = PerUserCarDevicePolicyService.getInstance(context);
-            mPerUserCarDevicePolicyService.registerBroadcastReceiver();
+            mPerUserCarDevicePolicyService.onCreate();
         } else if (DBG) {
             Slogf.d(TAG, "Not setting PerUserCarDevicePolicyService because device doesn't have %s",
                     PackageManager.FEATURE_DEVICE_ADMIN);
diff --git a/service/src/com/android/car/admin/PerUserCarDevicePolicyService.java b/service/src/com/android/car/admin/PerUserCarDevicePolicyService.java
index 4373a74..50ff4f7 100644
--- a/service/src/com/android/car/admin/PerUserCarDevicePolicyService.java
+++ b/service/src/com/android/car/admin/PerUserCarDevicePolicyService.java
@@ -16,6 +16,7 @@
 package com.android.car.admin;
 
 import static com.android.car.admin.CarDevicePolicyService.DEBUG;
+import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO;
 
 import android.annotation.IntDef;
 import android.app.admin.DevicePolicyManager;
@@ -28,12 +29,14 @@
 import android.util.Slog;
 
 import com.android.car.CarLog;
+import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport;
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
 
-// TODO(b/175057848) add unit tests
 /**
  * User-specific {@code CarDevicePolicyManagerService}.
  */
@@ -92,6 +95,8 @@
      * Gests the singleton instance, creating it if necessary.
      */
     public static PerUserCarDevicePolicyService getInstance(Context context) {
+        Objects.requireNonNull(context, "context cannot be null");
+
         synchronized (SLOCK) {
             if (sInstance == null) {
                 sInstance = new PerUserCarDevicePolicyService(context.getApplicationContext());
@@ -102,14 +107,15 @@
         }
     }
 
-    private PerUserCarDevicePolicyService(Context context) {
+    @VisibleForTesting
+    PerUserCarDevicePolicyService(Context context) {
         mContext = context;
     }
 
     /**
-     * Register a broadcast receiver to receive the proper events.
+     * Callback for when the service is created.
      */
-    public void registerBroadcastReceiver() {
+    public void onCreate() {
         if (DEBUG) Slog.d(TAG, "registering BroadcastReceiver");
 
         mContext.registerReceiver(mBroadcastReceiver, new IntentFilter(
@@ -130,6 +136,7 @@
     /**
      * Dump its contents.
      */
+    @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO)
     public void dump(IndentingPrintWriter pw) {
         synchronized (SLOCK) {
             pw.printf("mNewUserDisclaimerStatus: %s\n",
@@ -166,7 +173,16 @@
         }
     }
 
-    private String newUserDisclaimerStatusToString(@NewUserDisclaimerStatus int status) {
+    @VisibleForTesting
+    @NewUserDisclaimerStatus
+    int getNewUserDisclaimerStatus() {
+        synchronized (SLOCK) {
+            return mNewUserDisclaimerStatus;
+        }
+    }
+
+    @VisibleForTesting
+    static String newUserDisclaimerStatusToString(@NewUserDisclaimerStatus int status) {
         return DebugUtils.constantToString(PerUserCarDevicePolicyService.class,
                 PREFIX_NEW_USER_DISCLAIMER_STATUS, status);
     }
diff --git a/tests/carservice_unit_test/src/com/android/car/admin/NotificationHelperTest.java b/tests/carservice_unit_test/src/com/android/car/admin/NotificationHelperTest.java
index c4c481f..8610265 100644
--- a/tests/carservice_unit_test/src/com/android/car/admin/NotificationHelperTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/admin/NotificationHelperTest.java
@@ -30,7 +30,6 @@
 import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnitRunner;
 
-
 @RunWith(MockitoJUnitRunner.class)
 public final class NotificationHelperTest {
 
diff --git a/tests/carservice_unit_test/src/com/android/car/admin/PerUserCarDevicePolicyServiceTest.java b/tests/carservice_unit_test/src/com/android/car/admin/PerUserCarDevicePolicyServiceTest.java
new file mode 100644
index 0000000..796afd5
--- /dev/null
+++ b/tests/carservice_unit_test/src/com/android/car/admin/PerUserCarDevicePolicyServiceTest.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.car.admin;
+
+import static android.app.admin.DevicePolicyManager.ACTION_SHOW_NEW_USER_DISCLAIMER;
+
+import static com.android.car.admin.PerUserCarDevicePolicyService.NEW_USER_DISCLAIMER_STATUS_ACKED;
+import static com.android.car.admin.PerUserCarDevicePolicyService.NEW_USER_DISCLAIMER_STATUS_NEVER_RECEIVED;
+import static com.android.car.admin.PerUserCarDevicePolicyService.NEW_USER_DISCLAIMER_STATUS_NOTIFICATION_SENT;
+import static com.android.car.admin.PerUserCarDevicePolicyService.NEW_USER_DISCLAIMER_STATUS_RECEIVED;
+import static com.android.car.admin.PerUserCarDevicePolicyService.NEW_USER_DISCLAIMER_STATUS_SHOWN;
+import static com.android.car.admin.PerUserCarDevicePolicyService.newUserDisclaimerStatusToString;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.testng.Assert.expectThrows;
+
+import android.app.admin.DevicePolicyManager;
+import android.car.test.mocks.AbstractExtendedMockitoTestCase;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+import com.android.car.admin.PerUserCarDevicePolicyService.NewUserDisclaimerStatus;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+
+public final class PerUserCarDevicePolicyServiceTest extends AbstractExtendedMockitoTestCase {
+
+    @Mock
+    private Context mContext;
+
+    private PerUserCarDevicePolicyService mInstance;
+
+    @Mock
+    private DevicePolicyManager mDpm;
+
+    @Override
+    protected void onSessionBuilder(CustomMockitoSessionBuilder session) {
+        session.spyStatic(NewUserDisclaimerActivity.class);
+    }
+
+    @Before
+    public void setFixtures() {
+        when(mContext.getApplicationContext()).thenReturn(mContext);
+        when(mContext.getSystemService(DevicePolicyManager.class)).thenReturn(mDpm);
+
+        mInstance = new PerUserCarDevicePolicyService(mContext);
+        assertStatusString(NEW_USER_DISCLAIMER_STATUS_NEVER_RECEIVED);
+    }
+
+    @Test
+    public void testGetInstance() {
+        PerUserCarDevicePolicyService instance1 = PerUserCarDevicePolicyService
+                .getInstance(mContext);
+        assertWithMessage("getInstance()#1").that(instance1).isNotNull();
+        assertWithMessage("getInstance()#1").that(instance1).isNotSameInstanceAs(mInstance);
+
+        PerUserCarDevicePolicyService instance2 = PerUserCarDevicePolicyService
+                .getInstance(mContext);
+        assertWithMessage("getInstance()#2").that(instance2).isNotNull();
+        assertWithMessage("getInstance()#2").that(instance2).isNotSameInstanceAs(mInstance);
+
+        assertWithMessage("getInstance()#2").that(instance2).isSameInstanceAs(instance1);
+        assertWithMessage("getInstance()#1").that(instance1).isSameInstanceAs(instance2);
+    }
+
+    @Test
+    public void testGetInstance_nullContext() {
+        NullPointerException exception = expectThrows(NullPointerException.class,
+                () -> PerUserCarDevicePolicyService.getInstance(null));
+        assertWithMessage("exception message").that(exception.getMessage()).contains("context");
+    }
+
+    @Test
+    public void testCreateAndDestroy() {
+        BroadcastReceiver receiver = callOnCreate();
+
+        callOnDestroy(receiver);
+    }
+
+    @Test
+    public void testShowWhenIntentReceived() {
+        doAnswer((inv) -> {
+            assertStatusString(NEW_USER_DISCLAIMER_STATUS_RECEIVED);
+            return null;
+        }).when(() -> NewUserDisclaimerActivity.showNotification(any()));
+        BroadcastReceiver receiver  = callOnCreate();
+
+        sendShowNewUserDisclaimerBroadcast(receiver);
+
+        assertStatusString(NEW_USER_DISCLAIMER_STATUS_NOTIFICATION_SENT);
+        verify(() -> NewUserDisclaimerActivity.showNotification(mContext));
+    }
+
+    @Test
+    public void testSetShown() {
+        mInstance.setShown();
+
+        assertStatusString(NEW_USER_DISCLAIMER_STATUS_SHOWN);
+    }
+
+    @Test
+    public void testSetAcknowledged() {
+        doNothing().when(() -> NewUserDisclaimerActivity.cancelNotification(any()));
+
+        mInstance.setAcknowledged();
+
+        assertStatusString(NEW_USER_DISCLAIMER_STATUS_ACKED);
+        verify(() -> NewUserDisclaimerActivity.cancelNotification(mContext));
+
+        verify(mDpm).resetNewUserDisclaimer();
+    }
+
+    private BroadcastReceiver callOnCreate() {
+        ArgumentCaptor<BroadcastReceiver> captor = ArgumentCaptor.forClass(BroadcastReceiver.class);
+
+        mInstance.onCreate();
+
+        verify(mContext).registerReceiver(captor.capture(), any());
+        BroadcastReceiver receiver = captor.getValue();
+        assertWithMessage("BroadcastReceiver captured on onCreate()").that(receiver).isNotNull();
+
+        return receiver;
+    }
+
+    private void callOnDestroy(BroadcastReceiver receiver) {
+        mInstance.onDestroy();
+
+        verify(mContext).unregisterReceiver(receiver);
+    }
+
+    private void sendShowNewUserDisclaimerBroadcast(BroadcastReceiver receiver) {
+        receiver.onReceive(mContext, new Intent(ACTION_SHOW_NEW_USER_DISCLAIMER));
+    }
+
+    private void assertStatusString(@NewUserDisclaimerStatus int expectedStatus) {
+        int actualStatus = mInstance.getNewUserDisclaimerStatus();
+        assertWithMessage("newUserDisclaimerStatus (%s=%s, %s=%s)",
+                expectedStatus, newUserDisclaimerStatusToString(expectedStatus),
+                actualStatus, newUserDisclaimerStatusToString(actualStatus))
+                        .that(actualStatus).isEqualTo(expectedStatus);
+    }
+}