com.izforge.izpack.installer.multiunpacker.MultiVolumeUnpackerTest.java Source code

Java tutorial

Introduction

Here is the source code for com.izforge.izpack.installer.multiunpacker.MultiVolumeUnpackerTest.java

Source

/*
 * IzPack - Copyright 2001-2012 Julien Ponge, All Rights Reserved.
 *
 * http://izpack.org/
 * http://izpack.codehaus.org/
 *
 * Copyright 2012 Tim Anderson
 *
 * 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.izforge.izpack.installer.multiunpacker;

import com.izforge.izpack.api.data.*;
import com.izforge.izpack.api.event.ProgressListener;
import com.izforge.izpack.api.handler.Prompt;
import com.izforge.izpack.api.resource.Locales;
import com.izforge.izpack.api.resource.Resources;
import com.izforge.izpack.api.rules.RulesEngine;
import com.izforge.izpack.api.substitutor.VariableSubstitutor;
import com.izforge.izpack.compiler.data.CompilerData;
import com.izforge.izpack.compiler.listener.PackagerListener;
import com.izforge.izpack.compiler.merge.CompilerPathResolver;
import com.izforge.izpack.compiler.packager.impl.MultiVolumePackager;
import com.izforge.izpack.core.data.DefaultVariables;
import com.izforge.izpack.core.io.VolumeLocator;
import com.izforge.izpack.core.resource.ResourceManager;
import com.izforge.izpack.core.substitutor.VariableSubstitutorImpl;
import com.izforge.izpack.installer.data.InstallData;
import com.izforge.izpack.installer.data.UninstallData;
import com.izforge.izpack.installer.event.InstallerListeners;
import com.izforge.izpack.installer.unpacker.ConsolePackResources;
import com.izforge.izpack.installer.unpacker.FileQueueFactory;
import com.izforge.izpack.installer.unpacker.PackResources;
import com.izforge.izpack.merge.MergeManager;
import com.izforge.izpack.merge.resolve.MergeableResolver;
import com.izforge.izpack.test.util.TestHelper;
import com.izforge.izpack.util.Housekeeper;
import com.izforge.izpack.util.Librarian;
import com.izforge.izpack.util.PlatformModelMatcher;
import com.izforge.izpack.util.Platforms;
import org.apache.commons.io.FilenameUtils;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.mockito.Mockito;

import java.io.*;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.jar.JarOutputStream;

import static com.izforge.izpack.test.util.TestHelper.assertFileEquals;
import static com.izforge.izpack.test.util.TestHelper.assertFileNotExists;
import static org.junit.Assert.*;

/**
 * Tests the {@link MultiVolumeUnpacker}.
 *
 * @author Tim Anderson
 */
public class MultiVolumeUnpackerTest {
    @Rule
    public TemporaryFolder temporaryFolder = new TemporaryFolder();

    /**
     * Tests unpacking of multiple volume installation.
     *
     * @throws Exception for any error
     */
    @Test
    public void testUnpack() throws Exception {
        File baseDir = temporaryFolder.getRoot();
        File packageDir = new File(baseDir, "package");
        File installerJar = new File(packageDir, "installer.jar");
        File installDir = new File(baseDir, "install");
        assertTrue(packageDir.mkdir());

        // create some packs
        File file1 = createFile(baseDir, "file1.dat", 1024);
        File file2 = createFile(baseDir, "file2.dat", 2048);
        File file3 = createFile(baseDir, "file3.dat", 4096);
        PackInfo base = createPack("base", baseDir, file1, file2, file3);

        File file4 = createFile(baseDir, "file4.dat", 8192);
        File file5 = createFile(baseDir, "file5.dat", 16384);
        File file6 = createFile(baseDir, "file6.dat", 32768);
        PackInfo pack1 = createPack("pack1", baseDir, file4, file5, file6);

        File file7 = createFile(baseDir, "file7.dat", 65536);
        File file8 = createFile(baseDir, "file8.dat", 131072);
        File file9 = createFile(baseDir, "file9.dat", 262144);
        PackInfo pack2 = createPack("pack2", baseDir, file7, file8, file9);

        // pack3 is loose - i.e. content should not be stored in the volumes
        File file10 = createFile(baseDir, "file10.dat", 100);
        PackInfo pack3 = createPack("pack3", baseDir, file10);
        pack3.getPack().setLoose(true);

        MultiVolumePackager packager = createPackager(baseDir, installerJar);

        long firstVolumeSize = 40000;
        long maxVolumeSize = 100000;
        packager.setMaxFirstVolumeSize(firstVolumeSize);
        packager.setMaxVolumeSize(maxVolumeSize);

        packager.addPack(base);
        packager.addPack(pack1);
        packager.addPack(pack2);
        packager.addPack(pack3);

        // package the installer
        packager.createInstaller();

        // verify the installer exists
        assertTrue(installerJar.exists());

        // verify the installer volumes have been created
        Resources resources = createResources(installerJar);
        checkVolumes(packageDir, resources, firstVolumeSize, maxVolumeSize);

        // verify the loose pack files are present
        assertTrue(new File(packageDir, file10.getName()).exists());

        // unpack the installer
        AutomatedInstallData installData = createInstallData(packageDir, installDir, resources);
        setSelectedPacks(installData, "base", "pack2", "pack3"); // exclude pack1 from installation
        MultiVolumeUnpacker unpacker = createUnpacker(resources, installData);
        unpacker.unpack();

        // verify the expected files exists in the installation directory
        checkInstalled(installDir, file1);
        checkInstalled(installDir, file2);
        checkInstalled(installDir, file3);
        checkInstalled(installDir, file7);
        checkInstalled(installDir, file8);
        checkInstalled(installDir, file9);
        checkInstalled(installDir, file10); // loose pack file

        // verify the pack1 files are not installed
        assertFileNotExists(installDir, file4.getName());
        assertFileNotExists(installDir, file5.getName());
        assertFileNotExists(installDir, file6.getName());
    }

    /**
     * Tests unpacking of multiple volume installation with executables in pack.
     *
     * @throws Exception for any error
     */
    @Test
    public void testUnpackWithExecutables() throws Exception {
        File baseDir = temporaryFolder.getRoot();
        File packageDir = new File(baseDir, "package");
        File installerJar = new File(packageDir, "installer.jar");
        File installDir = new File(baseDir, "install");
        assertTrue(packageDir.mkdir());

        // create some packs
        File file1 = createFile(baseDir, "file1.dat", 1024);
        File file2 = createFile(baseDir, "file2.dat", 2048);
        File file3 = createFile(baseDir, "file3.dat", 4096);
        // this is an executable
        File file3e = createFile(baseDir, "file3.exe", 1024);
        PackInfo base = createPack("base", baseDir, file1, file2, file3, file3e);

        File file4 = createFile(baseDir, "file4.dat", 8192);
        File file5 = createFile(baseDir, "file5.dat", 16384);
        File file6 = createFile(baseDir, "file6.dat", 32768);
        // this is an executable
        File file6e = createFile(baseDir, "file6.exe", 1024);
        PackInfo pack1 = createPack("pack1", baseDir, file4, file5, file6, file6e);

        File file7 = createFile(baseDir, "file7.dat", 65536);
        File file8 = createFile(baseDir, "file8.dat", 131072);
        File file9 = createFile(baseDir, "file9.dat", 262144);
        // this is an executable
        File file9e = createFile(baseDir, "file9.exe", 1024);
        PackInfo pack2 = createPack("pack2", baseDir, file7, file8, file9, file9e);

        // pack3 is loose - i.e. content should not be stored in the volumes
        File file10 = createFile(baseDir, "file10.dat", 100);
        PackInfo pack3 = createPack("pack3", baseDir, file10);
        pack3.getPack().setLoose(true);

        MultiVolumePackager packager = createPackager(baseDir, installerJar);

        long firstVolumeSize = 40000;
        long maxVolumeSize = 100000;
        packager.setMaxFirstVolumeSize(firstVolumeSize);
        packager.setMaxVolumeSize(maxVolumeSize);

        packager.addPack(base);
        packager.addPack(pack1);
        packager.addPack(pack2);
        packager.addPack(pack3);

        // package the installer
        packager.createInstaller();

        // verify the installer exists
        assertTrue(installerJar.exists());

        // verify the installer volumes have been created
        Resources resources = createResources(installerJar);
        checkVolumes(packageDir, resources, firstVolumeSize, maxVolumeSize);

        // verify the loose pack files are present
        assertTrue(new File(packageDir, file10.getName()).exists());

        // unpack the installer
        AutomatedInstallData installData = createInstallData(packageDir, installDir, resources);
        setSelectedPacks(installData, "base", "pack2", "pack3"); // exclude pack1 from installation
        TestMultiVolumeUnpacker unpacker = createUnpacker(resources, installData);

        TestMultiVolumeUnpacker spy = Mockito.spy(unpacker);
        spy.unpack();

        Mockito.verify(spy, Mockito.times(3)).readExecutableFiles(Mockito.any(PackInfo.class), Mockito.anyList());

        // verify the expected files exists in the installation directory
        checkInstalled(installDir, file1);
        checkInstalled(installDir, file2);
        checkInstalled(installDir, file3);
        checkInstalled(installDir, file7);
        checkInstalled(installDir, file8);
        checkInstalled(installDir, file9);
        checkInstalled(installDir, file10); // loose pack file

        // verify the pack1 files are not installed
        assertFileNotExists(installDir, file3e.getName());
        assertFileNotExists(installDir, file4.getName());
        assertFileNotExists(installDir, file5.getName());
        assertFileNotExists(installDir, file6.getName());
        assertFileNotExists(installDir, file6e.getName());
        assertFileNotExists(installDir, file9e.getName());
    }

    /**
     * Helper to set the selected packs.
     *
     * @param installData the installation data
     * @param names       the names of the packs to select
     */
    private void setSelectedPacks(AutomatedInstallData installData, String... names) {
        for (String name : names) {
            for (Pack pack : installData.getAvailablePacks()) {
                if (pack.getName().equals(name)) {
                    installData.getSelectedPacks().add(pack);
                    break;
                }
            }
        }
        assertEquals(names.length, installData.getSelectedPacks().size());
    }

    /**
     * Creates a new pack.
     *
     * @param name the pack name
     * @return a new pack
     */
    private PackInfo createPack(String name, File baseDir, File... files) throws IOException {
        PackInfo pack = new PackInfo(name, name, "The " + name + " package", false, false, null, true, 0);
        addFiles(pack, baseDir, files);
        return pack;
    }

    /**
     * Verifies that there are multiple volumes of the expected size.
     *
     * @param dir                the directory to find the volumes in
     * @param resources          the resources used to determine the volume name and count
     * @param maxFirstVolumeSize the maximum size of the first volume
     * @param maxVolumeSize      the maximum volume size for subsequent volumes
     * @throws IOException for any I/O error
     */
    private void checkVolumes(File dir, Resources resources, long maxFirstVolumeSize, long maxVolumeSize)
            throws IOException {
        // get the volume information
        ObjectInputStream info = new ObjectInputStream(resources.getInputStream(MultiVolumeUnpacker.VOLUMES_INFO));
        int count = info.readInt();
        String name = info.readUTF();
        info.close();
        assertTrue(count >= 1);

        // verify the primary volume exists, with the expected size
        File volume = new File(dir, name);
        assertTrue(volume.exists());
        assertEquals(maxFirstVolumeSize, volume.length());

        // check the existence and size of the remaining volumes
        for (int i = 1; i < count; ++i) {
            volume = new File(dir, name + "." + i);
            assertTrue(volume.exists());
            if (i < count - 1) {
                // can't check the size of the last volume
                assertEquals(maxVolumeSize, volume.length());
            }
        }
    }

    /**
     * Creates a new unpacker.
     *
     * @param resources   the resources
     * @param installData the installation data
     * @return a new unpacker
     */
    private TestMultiVolumeUnpacker createUnpacker(Resources resources, AutomatedInstallData installData) {
        VariableSubstitutor replacer = new VariableSubstitutorImpl(installData.getVariables());
        Housekeeper housekeeper = Mockito.mock(Housekeeper.class);
        RulesEngine rules = Mockito.mock(RulesEngine.class);
        UninstallData uninstallData = new UninstallData();
        Librarian librarian = Mockito.mock(Librarian.class);
        VolumeLocator locator = Mockito.mock(VolumeLocator.class);
        PackResources packResources = new ConsolePackResources(resources, installData);
        FileQueueFactory queue = new FileQueueFactory(Platforms.WINDOWS, librarian);
        Prompt prompt = Mockito.mock(Prompt.class);
        InstallerListeners listeners = new InstallerListeners(installData, prompt);
        PlatformModelMatcher matcher = new PlatformModelMatcher(new Platforms(), Platforms.WINDOWS);
        TestMultiVolumeUnpacker unpacker = new TestMultiVolumeUnpacker(installData, packResources, rules, replacer,
                uninstallData, queue, housekeeper, listeners, prompt, locator, matcher);
        unpacker.setProgressListener(Mockito.mock(ProgressListener.class));
        return unpacker;
    }

    /**
     * This unpacker has a validation of the executables.
     *
     */
    private static class TestMultiVolumeUnpacker extends MultiVolumeUnpacker {

        public TestMultiVolumeUnpacker(com.izforge.izpack.api.data.InstallData installData, PackResources resources,
                RulesEngine rules, VariableSubstitutor variableSubstitutor, UninstallData uninstallData,
                FileQueueFactory queue, Housekeeper housekeeper, InstallerListeners listeners, Prompt prompt,
                VolumeLocator locator, PlatformModelMatcher matcher) {
            super(installData, resources, rules, variableSubstitutor, uninstallData, queue, housekeeper, listeners,
                    prompt, locator, matcher);
        }

        @Override
        protected void readExecutableFiles(PackInfo packInfo, List<ExecutableFile> executables) {
            super.readExecutableFiles(packInfo, executables);

            assertTrue(executables.size() < 2);
        }
    }

    /**
     * Creates the installation data.
     *
     * @param mediaDir   the directory to locate volumes
     * @param installDir the installation directory
     * @param resources  the resources
     * @return the installation data
     * @throws IOException for any I/O error
     * @throws ClassNotFoundException a class has not been found
     */
    private AutomatedInstallData createInstallData(File mediaDir, File installDir, Resources resources)
            throws IOException, ClassNotFoundException {
        AutomatedInstallData installData = new InstallData(new DefaultVariables(), Platforms.LINUX);

        installData.setInstallPath(installDir.getPath());
        installData.setMediaPath(mediaDir.getPath());
        installData.setInfo(new Info());
        InputStream langPack = getClass()
                .getResourceAsStream("/com/izforge/izpack/bin/langpacks/installer/eng.xml");
        assertNotNull(langPack);
        installData.setMessages(new LocaleDatabase(langPack, Mockito.mock(Locales.class)));
        List<Pack> packs = getPacks(resources);
        installData.setAvailablePacks(packs);
        return installData;
    }

    /**
     * Creates a new {@link Resources} that reads resources from the supplied jar.
     *
     * @param installerJar the installer jar.
     * @return a new resource manager
     * @throws IOException for any I/O error
     */
    private Resources createResources(File installerJar) throws IOException {
        final URLClassLoader loader = new URLClassLoader(new URL[] { installerJar.toURI().toURL() },
                getClass().getClassLoader());
        return new ResourceManager(loader);
    }

    /**
     * Creates a {@link MultiVolumePackager}.
     *
     * @param baseDir      the base directory
     * @param installerJar the jar to create
     * @return a new packager
     * @throws IOException for any I/O error
     */
    private MultiVolumePackager createPackager(File baseDir, File installerJar) throws IOException {
        Properties properties = new Properties();
        PackagerListener packagerListener = Mockito.mock(PackagerListener.class);
        JarOutputStream jar = new JarOutputStream(new FileOutputStream(installerJar));
        MergeManager mergeManager = Mockito.mock(MergeManager.class);
        CompilerPathResolver resolver = Mockito.mock(CompilerPathResolver.class);
        MergeableResolver mergeableResolver = Mockito.mock(MergeableResolver.class);
        CompilerData data = new CompilerData(null, baseDir.getPath(), installerJar.getPath(), true);
        RulesEngine rulesEngine = Mockito.mock(RulesEngine.class);
        MultiVolumePackager packager = new MultiVolumePackager(properties, packagerListener, jar, mergeManager,
                resolver, mergeableResolver, data, rulesEngine);
        packager.setInfo(new Info());
        return packager;
    }

    /**
     * Helper to add files to a pack.
     *
     * @param pack    the pack to add the files to
     * @param baseDir the base directory
     * @param files   the files to add
     * @throws IOException for any I/O error
     */
    private void addFiles(PackInfo pack, File baseDir, File... files) throws IOException {
        for (File file : files) {
            if ("exe".equals(FilenameUtils.getExtension(file.getName()))) {
                ExecutableFile executable = new ExecutableFile();
                executable.path = file.getPath();
                executable.executionStage = ExecutableFile.UNINSTALL;

                pack.addExecutable(executable);
            } else {
                pack.addFile(baseDir, file, "$INSTALL_PATH/" + file.getName(), null, OverrideType.OVERRIDE_FALSE,
                        null, Blockable.BLOCKABLE_NONE, null, null, null);
            }
        }
    }

    /**
     * Helper to read the pack meta-data.
     *
     * @param resources the resources
     * @return the pack meta-data
     * @throws IOException            for any I/O error
     * @throws ClassNotFoundException if the class of a serialized object cannot be found
     */
    private List<Pack> getPacks(Resources resources) throws IOException, ClassNotFoundException {
        // We read the packs data
        InputStream in = resources.getInputStream("packs.info");
        ObjectInputStream objIn = new ObjectInputStream(in);
        List<PackInfo> packsInfo = (List<PackInfo>) objIn.readObject();
        objIn.close();
        List<Pack> packs = new ArrayList<Pack>();
        for (PackInfo packInfo : packsInfo) {
            Pack pack = packInfo.getPack();
            packs.add(pack);
        }
        return packs;
    }

    /**
     * Helper to create a file with the specified size containing random data.
     *
     * @param baseDir the base directory
     * @param name    the file name
     * @param size    the file size
     * @return a new file
     * @throws IOException for any I/O error
     */
    private File createFile(File baseDir, String name, int size) throws IOException {
        return TestHelper.createFile(new File(baseDir, name), size);
    }

    /**
     * Verifies a file has been installed.
     *
     * @param installDir the installation directory
     * @param expected   the file that should be installed
     */
    private void checkInstalled(File installDir, File expected) {
        File file = new File(installDir, expected.getName());
        assertFileEquals(expected, file);
    }

}