Java tutorial
/* * Copyright (C) 2017 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.tradefed.targetprep; import com.android.tradefed.build.IBuildInfo; import com.android.tradefed.config.Option; import com.android.tradefed.config.OptionClass; import com.android.tradefed.device.DeviceNotAvailableException; import com.android.tradefed.device.ITestDevice; import com.android.tradefed.log.LogUtil.CLog; import com.android.tradefed.util.AaptParser; import com.android.tradefed.util.FileUtil; import com.android.tradefed.util.ZipUtil2; import org.apache.commons.compress.archivers.zip.ZipFile; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.List; /** * A {@link ITargetPreparer} that installs all apps in a test zip. For individual test app install * please look at {@link TestAppInstallSetup}. */ @OptionClass(alias = "all-tests-zip-installer") public class InstallAllTestZipAppsSetup implements ITargetCleaner { @Option(name = "install-arg", description = "Additional arguments to be passed to install command, " + "including leading dash, e.g. \"-d\"") private Collection<String> mInstallArgs = new ArrayList<>(); @Option(name = "cleanup-apks", description = "Whether apks installed should be uninstalled after test. Note that the " + "preparer does not verify if the apks are successfully removed.") private boolean mCleanup = true; @Option(name = "stop-install-on-failure", description = "Whether to stop the preparer by throwing an exception or only log the " + "error on continue.") private boolean mStopInstallOnFailure = true; @Option(name = "test-zip-name", description = "File name for test zip containing APKs.") private String mTestZipName; @Option(name = "disable", description = "Disable this target preparer.") private boolean mDisable; List<String> mPackagesInstalled = new ArrayList<>(); public void setTestZipName(String testZipName) { mTestZipName = testZipName; } public void setStopInstallOnFailure(boolean stopInstallOnFailure) { mStopInstallOnFailure = stopInstallOnFailure; } public void setDisable(boolean disable) { mDisable = disable; } public void setCleanup(boolean cleanup) { mCleanup = cleanup; } /** {@inheritDoc} */ @Override public void setUp(ITestDevice device, IBuildInfo buildInfo) throws TargetSetupError, DeviceNotAvailableException { if (mDisable) { CLog.d("InstallAllTestZipAppsSetup disabled, skipping setUp"); return; } File testsZip = getZipFile(device, buildInfo); // Locate test dir where the test zip file was unzip to. if (testsZip == null) { throw new TargetSetupError("Failed to find a valid test zip directory.", device.getDeviceDescriptor()); } File testsDir; try { testsDir = extractZip(testsZip); } catch (IOException e) { throw new TargetSetupError("Failed to extract test zip.", e, device.getDeviceDescriptor()); } try { installApksRecursively(testsDir, device); } finally { FileUtil.recursiveDelete(testsDir); } } /** * Returns the zip file. * * @param buildInfo {@link IBuildInfo} containing files. * @return the {@link File} for the zip file. */ File getZipFile(ITestDevice device, IBuildInfo buildInfo) throws TargetSetupError { if (mTestZipName == null) { throw new TargetSetupError("test-zip-name is null.", device.getDeviceDescriptor()); } return buildInfo.getFile(mTestZipName); } /** * Install all apks found in a given directory. * * @param directory {@link File} directory to install from. * @param device {@link ITestDevice} to install all apks to. * @throws TargetSetupError * @throws DeviceNotAvailableException */ void installApksRecursively(File directory, ITestDevice device) throws TargetSetupError, DeviceNotAvailableException { if (directory == null || !directory.isDirectory()) { throw new TargetSetupError("Invalid test directory!", device.getDeviceDescriptor()); } CLog.d("Installing all apks found in dir %s ...", directory.getAbsolutePath()); File[] files = directory.listFiles(); for (File f : files) { if (f.isDirectory()) { installApksRecursively(f, device); } else if (FileUtil.getExtension(f.getAbsolutePath()).toLowerCase().equals(".apk")) { installApk(f, device); } else { CLog.d("Skipping %s because it is not an apk", f.getAbsolutePath()); } } } /** * Extract the given zip file to a local dir. * * <p>Exposed so unit tests can mock * * @param testsZip * @return the {@link File} referencing the zip output. * @throws IOException */ File extractZip(File testsZip) throws IOException { File testsDir = null; try (ZipFile zip = new ZipFile(testsZip)) { testsDir = FileUtil.createTempDir("tests-zip_"); ZipUtil2.extractZip(zip, testsDir); } catch (IOException e) { FileUtil.recursiveDelete(testsDir); throw e; } return testsDir; } /** * Installs a single app to the device. * * @param appFile {@link File} of the apk to install. * @param device {@link ITestDevice} to install the apk to. * @throws TargetSetupError * @throws DeviceNotAvailableException */ void installApk(File appFile, ITestDevice device) throws TargetSetupError, DeviceNotAvailableException { CLog.d("Installing apk from %s ...", appFile.getAbsolutePath()); String result = device.installPackage(appFile, true, mInstallArgs.toArray(new String[] {})); if (result == null) { // only consider cleanup if install was successful if (mCleanup) { addApkToInstalledList(appFile, device); } } else if (mStopInstallOnFailure) { // if flag is true, we stop the sequence for an exception. throw new TargetSetupError(String.format("Failed to install %s on %s. Reason: '%s'", appFile, device.getSerialNumber(), result), device.getDeviceDescriptor()); } else { CLog.e("Failed to install %s on %s. Reason: '%s'", appFile, device.getSerialNumber(), result); } } /** * Adds an app to the list of apps to uninstall in tearDown() if necessary. * * @param appFile {@link File} of apk. * @param device {@link ITestDevice} apk was installed on. * @throws TargetSetupError */ void addApkToInstalledList(File appFile, ITestDevice device) throws TargetSetupError { String packageName = getAppPackageName(appFile); if (packageName == null) { throw new TargetSetupError("apk installed but AaptParser failed", device.getDeviceDescriptor()); } mPackagesInstalled.add(packageName); } /** * Returns the package name for a an apk. Returns null if any errors. * * @param appFile {@link File} of apk. * @return Package name of appFile, null if errors. */ String getAppPackageName(File appFile) { AaptParser parser = AaptParser.parse(appFile); if (parser == null) { return null; } return parser.getPackageName(); } /** {@inheritDoc} */ @Override public void tearDown(ITestDevice device, IBuildInfo buildInfo, Throwable e) throws DeviceNotAvailableException { if (mDisable) { CLog.d("InstallAllTestZipAppsSetup disabled, skipping tearDown"); return; } if (mCleanup && !(e instanceof DeviceNotAvailableException)) { for (String packageName : mPackagesInstalled) { String msg = device.uninstallPackage(packageName); if (msg != null) { CLog.w(String.format("error uninstalling package '%s': %s", packageName, msg)); } } } } }