blob: 837e2196c8cb8c338d0e66dd089141418ebe970a [file] [log] [blame]
/*
* Copyright (C) 2019 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;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doThrow;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import android.car.Car;
import android.car.test.mocks.AbstractExtendMockitoTestCase;
import android.content.Context;
import android.content.res.Resources;
import android.hardware.automotive.vehicle.V2_0.IVehicle;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.Log;
import androidx.test.platform.app.InstrumentationRegistry;
import com.android.car.systeminterface.ActivityManagerInterface;
import com.android.car.systeminterface.DisplayInterface;
import com.android.car.systeminterface.IOInterface;
import com.android.car.systeminterface.StorageMonitoringInterface;
import com.android.car.systeminterface.SystemInterface;
import com.android.car.systeminterface.SystemInterface.Builder;
import com.android.car.systeminterface.SystemStateInterface;
import com.android.car.systeminterface.TimeInterface;
import com.android.car.systeminterface.WakeLockInterface;
import com.android.car.test.utils.TemporaryDirectory;
import com.android.car.watchdog.CarWatchdogService;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import java.io.File;
import java.io.IOException;
/**
* This class contains unit tests for the {@link ICarImpl}.
* It tests that services started with {@link ICarImpl} are initialized properly.
*
* The following mocks are used:
* 1. {@link ActivityManagerInterface} broadcasts intent for a user.
* 2. {@link DisplayInterface} provides access to display operations.
* 3. {@link IVehicle} provides access to vehicle properties.
* 4. {@link StorageMonitoringInterface} provides access to storage monitoring operations.
* 5. {@link SystemStateInterface} provides system statuses (booting, sleeping, ...).
* 6. {@link TimeInterface} provides access to time operations.
* 7. {@link TimeInterface} provides access to wake lock operations.
*/
@RunWith(MockitoJUnitRunner.class)
public class ICarImplTest extends AbstractExtendMockitoTestCase {
private static final String TAG = ICarImplTest.class.getSimpleName();
@Mock private ActivityManagerInterface mMockActivityManagerInterface;
@Mock private DisplayInterface mMockDisplayInterface;
@Mock private IVehicle mMockVehicle;
@Mock private StorageMonitoringInterface mMockStorageMonitoringInterface;
@Mock private SystemStateInterface mMockSystemStateInterface;
@Mock private TimeInterface mMockTimeInterface;
@Mock private WakeLockInterface mMockWakeLockInterface;
@Mock private CarWatchdogService mCarWatchdogService;
private Context mContext;
private SystemInterface mFakeSystemInterface;
private UserManager mUserManager;
private final MockIOInterface mMockIOInterface = new MockIOInterface();
/**
* Initialize all of the objects with the @Mock annotation.
*/
@Before
public void setUp() throws Exception {
// InstrumentationTestRunner prepares a looper, but AndroidJUnitRunner does not.
// http://b/25897652.
if (Looper.myLooper() == null) {
Looper.prepare();
}
mContext = spy(InstrumentationRegistry.getInstrumentation().getTargetContext());
mUserManager = spy(mContext.getSystemService(UserManager.class));
doReturn(mUserManager).when(mContext).getSystemService(eq(UserManager.class));
doReturn(mUserManager).when(mContext).getSystemService(eq(Context.USER_SERVICE));
Resources resources = spy(mContext.getResources());
doReturn("").when(resources).getString(
eq(com.android.car.R.string.instrumentClusterRendererService));
doReturn(false).when(resources).getBoolean(
eq(com.android.car.R.bool.audioUseDynamicRouting));
doReturn(new String[0]).when(resources).getStringArray(
eq(com.android.car.R.array.config_earlyStartupServices));
doReturn(resources).when(mContext).getResources();
mFakeSystemInterface = Builder.newSystemInterface()
.withSystemStateInterface(mMockSystemStateInterface)
.withActivityManagerInterface(mMockActivityManagerInterface)
.withDisplayInterface(mMockDisplayInterface)
.withIOInterface(mMockIOInterface)
.withStorageMonitoringInterface(mMockStorageMonitoringInterface)
.withTimeInterface(mMockTimeInterface)
.withWakeLockInterface(mMockWakeLockInterface).build();
// ICarImpl will register new CarLocalServices services.
// This prevents one test failure in tearDown from triggering assertion failure for single
// CarLocalServices service.
CarLocalServices.removeAllServices();
}
/**
* Clean up before running the next test.
*/
@After
public void tearDown() {
try {
if (mMockIOInterface != null) {
mMockIOInterface.tearDown();
}
} finally {
CarLocalServices.removeAllServices();
}
}
@Test
public void testNoShardedPreferencesAccessedBeforeUserZeroUnlock() {
doReturn(true).when(mContext).isCredentialProtectedStorage();
doReturn(false).when(mUserManager).isUserUnlockingOrUnlocked(anyInt());
doReturn(false).when(mUserManager).isUserUnlocked();
doReturn(false).when(mUserManager).isUserUnlocked(anyInt());
doReturn(false).when(mUserManager).isUserUnlocked(any(UserHandle.class));
doReturn(false).when(mUserManager).isUserUnlockingOrUnlocked(any(UserHandle.class));
doThrow(new NullPointerException()).when(mContext).getSharedPrefsFile(anyString());
doThrow(new NullPointerException()).when(mContext).getSharedPreferencesPath(any());
doThrow(new NullPointerException()).when(mContext).getSharedPreferences(
anyString(), anyInt());
doThrow(new NullPointerException()).when(mContext).getSharedPreferences(
any(File.class), anyInt());
doThrow(new NullPointerException()).when(mContext).getDataDir();
ICarImpl carImpl = new ICarImpl(mContext, mMockVehicle, mFakeSystemInterface,
/* errorNotifier= */ null, "MockedCar", /* carUserService= */ null,
mCarWatchdogService);
carImpl.init();
Car mCar = new Car(mContext, carImpl, /* handler= */ null);
// Post tasks for Handler Threads to ensure all the tasks that will be queued inside init
// will be done.
for (Thread t : Thread.getAllStackTraces().keySet()) {
if (!HandlerThread.class.isInstance(t)) {
continue;
}
HandlerThread ht = (HandlerThread) t;
CarServiceUtils.runOnLooperSync(ht.getLooper(), () -> {
// Do nothing, just need to make sure looper finishes current task.
});
}
mCar.disconnect();
carImpl.release();
}
static final class MockIOInterface implements IOInterface {
private TemporaryDirectory mFilesDir = null;
@Override
public File getSystemCarDir() {
if (mFilesDir == null) {
try {
mFilesDir = new TemporaryDirectory(TAG);
} catch (IOException e) {
Log.e(TAG, "failed to create temporary directory", e);
fail("failed to create temporary directory. exception was: " + e);
}
}
return mFilesDir.getDirectory();
}
public void tearDown() {
if (mFilesDir != null) {
try {
mFilesDir.close();
} catch (Exception e) {
Log.w(TAG, "could not remove temporary directory", e);
}
}
}
}
}