blob: 042c40da376345a4261e49dd47e9f9d00ec66337 [file] [log] [blame]
/*
* 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);
}
}