blob: 095d8284d503c129aca267e09a4026458b1bedf9 [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.DexContainerFileOptimizeResult;
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<DexContainerFileOptimizeResult> mPrimaryResults = List.of(
new DexContainerFileOptimizeResult("/data/app/foo/base.apk", true /* isPrimaryAbi */,
"arm64-v8a", "verify", OptimizeResult.OPTIMIZE_PERFORMED,
100 /* dex2oatWallTimeMillis */, 400 /* dex2oatCpuTimeMillis */),
new DexContainerFileOptimizeResult("/data/app/foo/base.apk", false /* isPrimaryAbi */,
"armeabi-v7a", "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.getDexContainerFileOptimizeResults())
.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).getDexContainerFileOptimizeResults())
.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).getDexContainerFileOptimizeResults())
.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).getDexContainerFileOptimizeResults())
.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;
}
}