| /* |
| * Copyright (C) 2020 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.experimentalcar; |
| |
| import static com.google.common.truth.Truth.assertThat; |
| |
| import static org.mockito.Mockito.spy; |
| import static org.mockito.Mockito.times; |
| import static org.mockito.Mockito.verify; |
| |
| import android.car.experimental.DriverAwarenessEvent; |
| import android.car.occupantawareness.GazeDetection; |
| import android.car.occupantawareness.OccupantAwarenessDetection; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.os.IBinder; |
| |
| import androidx.test.core.app.ApplicationProvider; |
| import androidx.test.platform.app.InstrumentationRegistry; |
| import androidx.test.rule.ServiceTestRule; |
| |
| import org.junit.Before; |
| import org.junit.Rule; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.mockito.junit.MockitoJUnitRunner; |
| |
| @RunWith(MockitoJUnitRunner.class) |
| public class GazeDriverAwarenessSupplierTest { |
| |
| private static final long START_TIME_MILLIS = 1234L; |
| private static final long FRAME_TIME_MILLIS = 1000L; |
| |
| private Context mSpyContext; |
| private GazeDriverAwarenessSupplier mGazeSupplier; |
| private float mInitialValue; |
| private float mGrowthRate; |
| private float mDecayRate; |
| private FakeTimeSource mTimeSource; |
| |
| @Rule |
| public final ServiceTestRule serviceRule = new ServiceTestRule(); |
| |
| @Before |
| public void setUp() throws Exception { |
| mSpyContext = spy(InstrumentationRegistry.getInstrumentation().getTargetContext()); |
| |
| mInitialValue = |
| mSpyContext |
| .getResources() |
| .getFloat(R.fraction.driverAwarenessGazeModelInitialValue); |
| mGrowthRate = |
| mSpyContext.getResources().getFloat(R.fraction.driverAwarenessGazeModelGrowthRate); |
| mDecayRate = |
| mSpyContext.getResources().getFloat(R.fraction.driverAwarenessGazeModelDecayRate); |
| |
| mTimeSource = new FakeTimeSource(START_TIME_MILLIS); |
| mGazeSupplier = spy(new GazeDriverAwarenessSupplier(mSpyContext, mTimeSource)); |
| } |
| |
| @Test |
| public void testWithBoundService() throws Exception { |
| Intent serviceIntent = |
| new Intent(ApplicationProvider.getApplicationContext(), |
| GazeDriverAwarenessSupplier.class); |
| |
| // Bind the service and grab a reference to the binder. |
| IBinder binder = serviceRule.bindService(serviceIntent); |
| assertThat(binder instanceof GazeDriverAwarenessSupplier.SupplierBinder).isTrue(); |
| } |
| |
| @Test |
| public void testonReady_initialCallbackIsGenerated() throws Exception { |
| // Supplier should return an initial callback after onReady(). |
| mGazeSupplier.onReady(); |
| |
| verify(mGazeSupplier) |
| .emitAwarenessEvent(new DriverAwarenessEvent(START_TIME_MILLIS, mInitialValue)); |
| } |
| |
| @Test |
| public void testprocessDetectionEvent_noGazeDataProvided() throws Exception { |
| // If detection events happen with *no gaze*, no further events should be generated by the |
| // attention supplier. |
| mGazeSupplier.onReady(); |
| |
| mGazeSupplier.processDetectionEvent(buildEmptyDetection(START_TIME_MILLIS)); |
| |
| // Should have exactly one call from the initial onReady(), but no further events. |
| verify(mGazeSupplier, times(1)) |
| .emitAwarenessEvent(new DriverAwarenessEvent(START_TIME_MILLIS, mInitialValue)); |
| } |
| |
| @Test |
| public void testprocessDetectionEvent_neverExceedsOne() throws Exception { |
| // Attention value should never exceed '1' no matter how long the driver looks on-road. |
| mGazeSupplier.onReady(); |
| |
| // Should have initial callback from onReady(). |
| verify(mGazeSupplier) |
| .emitAwarenessEvent(new DriverAwarenessEvent(START_TIME_MILLIS, mInitialValue)); |
| |
| long timestamp = START_TIME_MILLIS + FRAME_TIME_MILLIS; |
| float attention = mInitialValue; |
| |
| for (int i = 0; i < 100; i++) { |
| OccupantAwarenessDetection detection = |
| buildGazeDetection(timestamp, GazeDetection.VEHICLE_REGION_FORWARD_ROADWAY); |
| mGazeSupplier.processDetectionEvent(detection); |
| |
| verify(mGazeSupplier) |
| .emitAwarenessEvent(new DriverAwarenessEvent(timestamp, attention)); |
| |
| // Increase attention, but not past 1. |
| attention = Math.min(attention + mGrowthRate, 1.0f); |
| timestamp += FRAME_TIME_MILLIS; |
| } |
| } |
| |
| @Test |
| public void testprocessDetectionEvent_neverFallsBelowZero() throws Exception { |
| // Attention value should never fall below '0' no matter how long the driver looks off-road. |
| mGazeSupplier.onReady(); |
| |
| // Should have initial callback from onReady(). |
| verify(mGazeSupplier) |
| .emitAwarenessEvent(new DriverAwarenessEvent(START_TIME_MILLIS, mInitialValue)); |
| |
| long timestamp = START_TIME_MILLIS + FRAME_TIME_MILLIS; |
| float attention = mInitialValue; |
| |
| for (int i = 0; i < 100; i++) { |
| OccupantAwarenessDetection detection = |
| buildGazeDetection(timestamp, GazeDetection.VEHICLE_REGION_HEAD_UNIT_DISPLAY); |
| mGazeSupplier.processDetectionEvent(detection); |
| |
| verify(mGazeSupplier) |
| .emitAwarenessEvent(new DriverAwarenessEvent(timestamp, attention)); |
| |
| // Decrement the attention, but not past 0. |
| attention = Math.max(attention - mDecayRate, 0); |
| timestamp += FRAME_TIME_MILLIS; |
| } |
| } |
| |
| /** Builds a {link OccupantAwarenessDetection} with the specified target for testing. */ |
| private OccupantAwarenessDetection buildGazeDetection( |
| long timestamp, @GazeDetection.VehicleRegion int gazeTarget) { |
| GazeDetection gaze = |
| new GazeDetection( |
| OccupantAwarenessDetection.CONFIDENCE_LEVEL_HIGH, |
| null /*leftEyePosition*/, |
| null /*rightEyePosition*/, |
| null /*headAngleUnitVector*/, |
| null /*gazeAngleUnitVector*/, |
| gazeTarget, |
| FRAME_TIME_MILLIS); |
| |
| return new OccupantAwarenessDetection( |
| OccupantAwarenessDetection.VEHICLE_OCCUPANT_DRIVER, timestamp, true, gaze, null); |
| } |
| |
| /** Builds a {link OccupantAwarenessDetection} with the specified target for testing. */ |
| private OccupantAwarenessDetection buildEmptyDetection(long timestamp) { |
| return new OccupantAwarenessDetection( |
| OccupantAwarenessDetection.VEHICLE_OCCUPANT_DRIVER, timestamp, true, null, null); |
| } |
| } |