com.android.tradefed.targetprep.InstallAllTestZipAppsSetup.java Source code

Java tutorial

Introduction

Here is the source code for com.android.tradefed.targetprep.InstallAllTestZipAppsSetup.java

Source

/*
 * 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));
                }
            }
        }
    }
}