blob: 5e2314e372c7cdcdd53e319f1b65db1c1bd17c88 [file] [log] [blame]
/*
* Copyright (C) 2022 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.server.art;
import static com.android.server.art.model.OptimizeResult.DexFileOptimizeResult;
import static com.android.server.art.model.OptimizeResult.PackageOptimizeResult;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyLong;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.same;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.apphibernation.AppHibernationManager;
import android.os.CancellationSignal;
import android.os.PowerManager;
import androidx.test.filters.SmallTest;
import com.android.server.art.model.OptimizeParams;
import com.android.server.art.model.OptimizeResult;
import com.android.server.art.testing.OnSuccessRule;
import com.android.server.art.wrapper.AndroidPackageApi;
import com.android.server.art.wrapper.PackageState;
import com.android.server.pm.snapshot.PackageDataSnapshot;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import java.util.List;
@SmallTest
@RunWith(MockitoJUnitRunner.StrictStubs.class)
public class DexOptHelperTest {
private static final String PKG_NAME = "com.example.foo";
@Mock private DexOptHelper.Injector mInjector;
@Mock private PrimaryDexOptimizer mPrimaryDexOptimizer;
@Mock private AppHibernationManager mAhm;
@Mock private PowerManager mPowerManager;
@Mock private PowerManager.WakeLock mWakeLock;
private PackageState mPkgState;
private AndroidPackageApi mPkg;
private CancellationSignal mCancellationSignal;
@Rule
public OnSuccessRule onSuccessRule = new OnSuccessRule(() -> {
// Don't do this on failure because it will make the failure hard to understand.
verifyNoMoreInteractions(mPrimaryDexOptimizer);
});
private final OptimizeParams mParams =
new OptimizeParams.Builder("install").setCompilerFilter("speed-profile").build();
private final List<DexFileOptimizeResult> mPrimaryResults =
List.of(new DexFileOptimizeResult("/data/app/foo/base.apk", "arm64", "verify",
OptimizeResult.OPTIMIZE_PERFORMED, 100 /* dex2oatWallTimeMillis */,
400 /* dex2oatCpuTimeMillis */),
new DexFileOptimizeResult("/data/app/foo/base.apk", "arm", "verify",
OptimizeResult.OPTIMIZE_FAILED, 100 /* dex2oatWallTimeMillis */,
400 /* dex2oatCpuTimeMillis */));
private DexOptHelper mDexOptHelper;
@Before
public void setUp() throws Exception {
lenient().when(mInjector.getPrimaryDexOptimizer()).thenReturn(mPrimaryDexOptimizer);
lenient().when(mInjector.getAppHibernationManager()).thenReturn(mAhm);
lenient().when(mInjector.getPowerManager()).thenReturn(mPowerManager);
lenient()
.when(mPowerManager.newWakeLock(eq(PowerManager.PARTIAL_WAKE_LOCK), any()))
.thenReturn(mWakeLock);
lenient().when(mAhm.isHibernatingGlobally(PKG_NAME)).thenReturn(false);
lenient().when(mAhm.isOatArtifactDeletionEnabled()).thenReturn(true);
mPkgState = createPackageState();
mPkg = mPkgState.getAndroidPackage();
mCancellationSignal = new CancellationSignal();
mDexOptHelper = new DexOptHelper(mInjector);
}
@Test
public void testDexopt() throws Exception {
when(mPrimaryDexOptimizer.dexopt(
same(mPkgState), same(mPkg), same(mParams), same(mCancellationSignal)))
.thenReturn(mPrimaryResults);
OptimizeResult result = mDexOptHelper.dexopt(
mock(PackageDataSnapshot.class), mPkgState, mPkg, mParams, mCancellationSignal);
assertThat(result.getRequestedCompilerFilter()).isEqualTo("speed-profile");
assertThat(result.getReason()).isEqualTo("install");
assertThat(result.getFinalStatus()).isEqualTo(OptimizeResult.OPTIMIZE_FAILED);
assertThat(result.getPackageOptimizeResults()).hasSize(1);
PackageOptimizeResult packageResult = result.getPackageOptimizeResults().get(0);
assertThat(packageResult.getPackageName()).isEqualTo(PKG_NAME);
assertThat(packageResult.getDexFileOptimizeResults())
.containsExactlyElementsIn(mPrimaryResults);
InOrder inOrder = inOrder(mPrimaryDexOptimizer, mWakeLock);
inOrder.verify(mWakeLock).acquire(anyLong());
inOrder.verify(mPrimaryDexOptimizer).dexopt(any(), any(), any(), any());
inOrder.verify(mWakeLock).release();
}
@Test
public void testDexoptNoCode() throws Exception {
when(mPkg.isHasCode()).thenReturn(false);
OptimizeResult result = mDexOptHelper.dexopt(
mock(PackageDataSnapshot.class), mPkgState, mPkg, mParams, mCancellationSignal);
assertThat(result.getFinalStatus()).isEqualTo(OptimizeResult.OPTIMIZE_SKIPPED);
assertThat(result.getPackageOptimizeResults().get(0).getDexFileOptimizeResults()).isEmpty();
}
@Test
public void testDexoptIsHibernating() throws Exception {
lenient().when(mAhm.isHibernatingGlobally(PKG_NAME)).thenReturn(true);
OptimizeResult result = mDexOptHelper.dexopt(
mock(PackageDataSnapshot.class), mPkgState, mPkg, mParams, mCancellationSignal);
assertThat(result.getFinalStatus()).isEqualTo(OptimizeResult.OPTIMIZE_SKIPPED);
assertThat(result.getPackageOptimizeResults().get(0).getDexFileOptimizeResults()).isEmpty();
}
@Test
public void testDexoptIsHibernatingButOatArtifactDeletionDisabled() throws Exception {
lenient().when(mAhm.isHibernatingGlobally(PKG_NAME)).thenReturn(true);
lenient().when(mAhm.isOatArtifactDeletionEnabled()).thenReturn(false);
when(mPrimaryDexOptimizer.dexopt(
same(mPkgState), same(mPkg), same(mParams), same(mCancellationSignal)))
.thenReturn(mPrimaryResults);
OptimizeResult result = mDexOptHelper.dexopt(
mock(PackageDataSnapshot.class), mPkgState, mPkg, mParams, mCancellationSignal);
assertThat(result.getPackageOptimizeResults().get(0).getDexFileOptimizeResults())
.containsExactlyElementsIn(mPrimaryResults);
}
@Test
public void testDexoptAlwaysReleasesWakeLock() throws Exception {
when(mPrimaryDexOptimizer.dexopt(
same(mPkgState), same(mPkg), same(mParams), same(mCancellationSignal)))
.thenThrow(IllegalStateException.class);
try {
OptimizeResult result = mDexOptHelper.dexopt(
mock(PackageDataSnapshot.class), mPkgState, mPkg, mParams, mCancellationSignal);
} catch (Exception e) {
}
verify(mWakeLock).release();
}
private AndroidPackageApi createPackage() {
AndroidPackageApi pkg = mock(AndroidPackageApi.class);
lenient().when(pkg.getUid()).thenReturn(12345);
lenient().when(pkg.isHasCode()).thenReturn(true);
return pkg;
}
private PackageState createPackageState() {
PackageState pkgState = mock(PackageState.class);
lenient().when(pkgState.getPackageName()).thenReturn(PKG_NAME);
AndroidPackageApi pkg = createPackage();
lenient().when(pkgState.getAndroidPackage()).thenReturn(pkg);
return pkgState;
}
}