blob: 23655a08397d9fee456c1ba81e8096ee76deb7b5 [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.internal.content;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.when;
import android.content.pm.parsing.AndroidPackage;
import android.os.Build;
import android.util.ArrayMap;
import com.android.internal.content.om.OverlayConfig.AndroidPackageProvider;
import com.android.internal.content.om.OverlayScanner;
import com.android.internal.content.om.OverlayScanner.ParsedOverlayInfo;
import org.junit.Assert;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import java.io.File;
import java.io.IOException;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Supplier;
/**
* A {@link TestRule} that runs a test case twice. First, the test case runs with a non-null
* {@link OverlayScanner} as if the zygote process is scanning the overlay packages
* and parsing configuration files. The test case then runs with a non-null
* {@link AndroidPackageProvider} as if the system server is parsing configuration files.
*
* This simulates what will happen on device. If an exception would be thrown in the zygote, then
* the exception should be thrown in the first run of the test case.
*/
public class OverlayConfigIterationRule implements TestRule {
enum Iteration {
ZYGOTE,
SYSTEM_SERVER,
}
private final ArrayMap<File, ParsedOverlayInfo> mOverlayStubResults = new ArrayMap<>();
private Supplier<OverlayScanner> mOverlayScanner;
private AndroidPackageProvider mAndroidPackageProvider;
private Iteration mIteration;
/**
* Mocks the parsing of the file to make it appear to the scanner that the file is a valid
* overlay APK.
**/
void addOverlay(File path, String packageName, String targetPackage, int targetSdkVersion,
boolean isStatic, int priority) {
try {
final File canonicalPath = new File(path.getCanonicalPath());
mOverlayStubResults.put(canonicalPath, new ParsedOverlayInfo(
packageName, targetPackage, targetSdkVersion, isStatic, priority,
canonicalPath));
} catch (IOException e) {
Assert.fail("Failed to add overlay " + e);
}
}
void addOverlay(File path, String packageName) {
addOverlay(path, packageName, "target");
}
void addOverlay(File path, String packageName, String targetPackage) {
addOverlay(path, packageName, targetPackage, Build.VERSION_CODES.CUR_DEVELOPMENT);
}
void addOverlay(File path, String packageName, String targetPackage, int targetSdkVersion) {
addOverlay(path, packageName, targetPackage, targetSdkVersion, false, 0);
}
/** Retrieves the {@link OverlayScanner} for the current run of the test. */
Supplier<OverlayScanner> getScannerFactory() {
return mOverlayScanner;
}
/** Retrieves the {@link AndroidPackageProvider} for the current run of the test. */
AndroidPackageProvider getPackageProvider() {
return mAndroidPackageProvider;
}
/** Retrieves the current iteration of the test. */
Iteration getIteration() {
return mIteration;
}
@Override
public Statement apply(Statement base, Description description) {
return new Statement() {
@Override
public void evaluate() throws Throwable {
// Run the test once as if the zygote process is scanning the overlay packages
// and parsing configuration files.
mOverlayScanner = () -> {
OverlayScanner scanner = Mockito.spy(new OverlayScanner());
for (Map.Entry<File, ParsedOverlayInfo> overlay :
mOverlayStubResults.entrySet()) {
doReturn(overlay.getValue()).when(scanner)
.parseOverlayManifest(overlay.getKey());
}
return scanner;
};
mAndroidPackageProvider = null;
mIteration = Iteration.ZYGOTE;
base.evaluate();
// Run the test once more (if the first test did not throw an exception) as if
// the system server is parsing the configuration files and using PackageManager to
// retrieving information of overlays.
mOverlayScanner = null;
mAndroidPackageProvider = Mockito.mock(AndroidPackageProvider.class);
mIteration = Iteration.SYSTEM_SERVER;
doAnswer((InvocationOnMock invocation) -> {
final Object[] args = invocation.getArguments();
final Consumer<AndroidPackage> f = (Consumer<AndroidPackage>) args[0];
for (Map.Entry<File, ParsedOverlayInfo> overlay :
mOverlayStubResults.entrySet()) {
final AndroidPackage a = Mockito.mock(AndroidPackage.class);
final ParsedOverlayInfo info = overlay.getValue();
when(a.getPackageName()).thenReturn(info.packageName);
when(a.getOverlayTarget()).thenReturn(info.targetPackageName);
when(a.getTargetSdkVersion()).thenReturn(info.targetSdkVersion);
when(a.isOverlayIsStatic()).thenReturn(info.isStatic);
when(a.getOverlayPriority()).thenReturn(info.priority);
when(a.getBaseCodePath()).thenReturn(info.path.getPath());
when(a.isSystem()).thenReturn(
!info.path.getPath().contains("data/overlay"));
f.accept(a);
}
return null;
}).when(mAndroidPackageProvider).forEachPackage(any());
base.evaluate();
}
};
}
}