/* * Copyright (C) 2011 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.sdklib.internal.repository.archives; import com.android.sdklib.internal.repository.DownloadCache; import com.android.sdklib.internal.repository.ITaskMonitor; import com.android.sdklib.internal.repository.MockEmptySdkManager; import com.android.sdklib.internal.repository.MockMonitor; import com.android.sdklib.internal.repository.packages.ExtraPackage; import com.android.sdklib.internal.repository.packages.MockEmptyPackage; import com.android.sdklib.internal.repository.packages.MockExtraPackage; import com.android.sdklib.internal.repository.sources.SdkRepoSource; import com.android.sdklib.internal.repository.sources.SdkSource; import com.android.sdklib.io.IFileOp; import com.android.sdklib.io.MockFileOp; import com.android.sdklib.repository.PkgProps; import com.android.utils.Pair; import java.io.File; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.Properties; import java.util.regex.Pattern; import junit.framework.TestCase; /** * Unit tests for {@link ArchiveInstaller}. */ public class ArchiveInstallerTest extends TestCase { private MockMonitor mMon; private String mSdkRoot; private MockFileOp mFile; private MockArchiveInstaller mArchInst; private MockEmptySdkManager mSdkMan; private class MockArchiveInstaller extends ArchiveInstaller { private Map<Archive, File> mDownloadMap = new HashMap<Archive, File>(); public MockArchiveInstaller(IFileOp fileUtils) { super(fileUtils); } public void setDownloadResponse(Archive archive, File response) { mDownloadMap.put(archive, response); } @Override protected Pair<File, File> downloadFile( Archive archive, String osSdkRoot, DownloadCache cache, ITaskMonitor monitor, boolean forceHttp) { File file = mDownloadMap.get(archive); // register the file as "created" ArchiveInstallerTest.this.mFile.recordExistingFile(file); return Pair.of(file, null); } @Override protected boolean unzipFolder( ArchiveReplacement archiveInfo, File archiveFile, File unzipDestFolder, ITaskMonitor monitor) { // Claim the unzip works if the input archiveFile is one we know about // and the destination actually exists. if (getFileOp().isDirectory(unzipDestFolder) && mDownloadMap.values().contains(archiveFile)) { return true; } return false; } } @Override protected void setUp() throws Exception { super.setUp(); mFile = new MockFileOp(); mArchInst = new MockArchiveInstaller(mFile); mSdkRoot = "/sdk"; mSdkMan = new MockEmptySdkManager(mSdkRoot); mMon = new MockMonitor(); } // ---- /** Test we don't try to install a local archive. */ public void testInstall_SkipLocalArchive() throws Exception { MockEmptyPackage p = new MockEmptyPackage("testPkg"); ArchiveReplacement ar = new ArchiveReplacement(p.getArchives()[0], null /*replaced*/); assertFalse(mArchInst.install(ar, mSdkRoot, false /*forceHttp*/, mSdkMan, null /*UrlCache*/, mMon)); assertTrue(mMon.getCapturedLog().indexOf("Skipping already installed archive") != -1); } /** Test we can install a simple new archive. */ public void testInstall_NewArchive() throws Exception { SdkSource src1 = new SdkRepoSource("http://repo.example.com/url", "repo1"); MockEmptyPackage p = createRemoteEmptyPackage(src1, "testPkg"); ArchiveReplacement ar = new ArchiveReplacement(p.getArchives()[0], null /*replaced*/); // associate the File that will be "downloaded" for this archive mArchInst.setDownloadResponse( p.getArchives()[0], createFile("/sdk", "tmp", "download1.zip")); assertTrue(mArchInst.install(ar, mSdkRoot, false /*forceHttp*/, mSdkMan, null /*UrlCache*/, mMon)); // check what was created assertEquals( "[/sdk/mock/testPkg/source.properties]", Arrays.toString(mFile.getExistingFiles())); assertEquals( "[/, /sdk, /sdk/mock, /sdk/mock/testPkg]", Arrays.toString(mFile.getExistingFolders())); assertEquals( "[</sdk/mock/testPkg/source.properties: '### Android Tool: Source of this archive.\n" + "#...date...\n" + "Pkg.Revision=0\n" + "Pkg.SourceUrl=http\\://repo.example.com/url\n" + "'>]", stripDate(Arrays.toString(mFile.getOutputStreams()))); assertEquals( "Installing 'testPkg'\n" + "Installed 'testPkg'\n", mMon.getCapturedLog()); } /** Test we can replace and rename an Extra package. */ public void testInstall_InstallExtraArchive() throws Exception { SdkSource src1 = new SdkRepoSource("http://repo.example.com/url", "repo1"); MockExtraPackage newPkg = createRemoteExtraPackage(src1, "vendor1", "oldPath", 2, 1); MockExtraPackage oldPkg = new MockExtraPackage(src1, "vendor1", "oldPath", 1, 1); // old pkg is installed, so its directory & files exists mFile.recordExistingFile("/sdk/extras/vendor1/oldPath/source.properties"); mFile.recordExistingFolder("/sdk/extras/vendor1/oldPath"); ArchiveReplacement ar = new ArchiveReplacement( newPkg.getArchives()[0], oldPkg.getArchives()[0]); // associate the File that will be "downloaded" for this archive mArchInst.setDownloadResponse( newPkg.getArchives()[0], createFile("/sdk", "tmp", "download1.zip")); assertTrue(mArchInst.install(ar, mSdkRoot, false /*forceHttp*/, mSdkMan, null /*UrlCache*/, mMon)); // check what was created assertEquals( "[/sdk/extras/vendor1/oldPath/source.properties]", Arrays.toString(mFile.getExistingFiles())); // This created the /sdk/temp folder to put the oldPath package whilst we unzipped // the new one. The oldPath dir was then cleaned up but we still leave the root // temp dir around. assertEquals( "[/, /sdk, /sdk/extras, /sdk/extras/vendor1, /sdk/extras/vendor1/oldPath, /sdk/temp]", Arrays.toString(mFile.getExistingFolders())); assertEquals( ( "[</sdk/extras/vendor1/oldPath/source.properties: " + "'### Android Tool: Source of this archive.\n" + "#...date...\n" + "Extra.NameDisplay=Vendor1 OldPath\n" + "Extra.Path=oldPath\n" + "Extra.VendorDisplay=vendor1\n" + "Extra.VendorId=vendor1\n" + "Pkg.Desc=desc\n" + "Pkg.DescUrl=url\n" + "Pkg.Revision=2.0.0\n" + "Pkg.SourceUrl=http\\://repo.example.com/url\n" + "'>]"), stripDate(Arrays.toString(mFile.getOutputStreams()))); assertEquals( "Installing Vendor1 OldPath, revision 2\n" + "Installed Vendor1 OldPath, revision 2\n", mMon.getCapturedLog()); } /** Test we can replace and rename an Extra package. */ public void testInstall_InstallRenamedExtraArchive() throws Exception { SdkSource src1 = new SdkRepoSource("http://repo.example.com/url", "repo1"); MockExtraPackage newPkg = createRemoteExtraPackage( src1, "vendor1", "newPath", "oldPath", 2, // revision 1); // min_platform_tools_rev ExtraPackage oldPkg = (ExtraPackage) ExtraPackage.create( src1, // source null, // props "vendor1", // vendor "oldPath", // path 1, // revision null, // license null, // description null, // descUrl "/sdk/extras/vendor1/oldPath" // archiveOsPath ); // old pkg is installed, so its directory & files exists mFile.recordExistingFile("/sdk/extras/vendor1/oldPath/source.properties"); mFile.recordExistingFolder("/sdk/extras/vendor1/oldPath"); ArchiveReplacement ar = new ArchiveReplacement( newPkg.getArchives()[0], oldPkg.getArchives()[0]); // associate the File that will be "downloaded" for this archive mArchInst.setDownloadResponse( newPkg.getArchives()[0], createFile("/sdk", "tmp", "download1.zip")); assertTrue(mArchInst.install(ar, mSdkRoot, false /*forceHttp*/, mSdkMan, null /*UrlCache*/, mMon)); // check what was created assertEquals( "[/sdk/extras/vendor1/newPath/source.properties]", Arrays.toString(mFile.getExistingFiles())); // oldPath directory has been deleted, we only have newPath now. // No sdk/temp dir was created since we didn't have to move the old package dir out // of the way. assertEquals( "[/, /sdk, /sdk/extras, /sdk/extras/vendor1, /sdk/extras/vendor1/newPath]", Arrays.toString(mFile.getExistingFolders())); assertEquals( ( "[</sdk/extras/vendor1/newPath/source.properties: " + "'### Android Tool: Source of this archive.\n" + "#...date...\n" + "Extra.NameDisplay=Vendor1 NewPath\n" + "Extra.OldPaths=oldPath\n" + "Extra.Path=newPath\n" + "Extra.VendorDisplay=vendor1\n" + "Extra.VendorId=vendor1\n" + "Pkg.Desc=desc\n" + "Pkg.DescUrl=url\n" + "Pkg.Revision=2.0.0\n" + "Pkg.SourceUrl=http\\://repo.example.com/url\n" + "'>]"), stripDate(Arrays.toString(mFile.getOutputStreams()))); assertEquals( "Installing Vendor1 NewPath, revision 2\n" + "Installed Vendor1 NewPath, revision 2\n", mMon.getCapturedLog()); } // ---- /** * Helper creator method to create a {@link MockEmptyPackage} with no local * archive associated. */ private static MockEmptyPackage createRemoteEmptyPackage(SdkSource source, String testHandle) { return new MockEmptyPackage(source, testHandle, 0 /*revision*/) { @Override protected Archive[] initializeArchives( Properties props, String archiveOsPath) { // Create one remote archive for this package return new Archive[] { new Archive( this, null, // arch "http://some.source/some_url", 1234, // size "abcdef") // sha1 }; } }; } /** * Helper creator method to create a {@link MockExtraPackage} with no local * archive associated. */ private static MockExtraPackage createRemoteExtraPackage( SdkSource source, String vendor, String path, int revision, int min_platform_tools_rev) { return new MockExtraPackage(source, vendor, path, revision, min_platform_tools_rev) { @Override protected Archive[] initializeArchives( Properties props, String archiveOsPath) { // Create one remote archive for this package return new Archive[] { new Archive( this, null, // arch "http://some.source/some_url", 1234, // size "abcdef") // sha1 }; } }; } /** * Helper creator method to create a {@link MockExtraPackage} with no local * archive associated and a specific oldPaths attribute. */ private static MockExtraPackage createRemoteExtraPackage( SdkSource source, String vendor, String newPath, String oldPaths, int revision, int min_platform_tools_rev) { Properties props = new Properties(); props.setProperty(PkgProps.EXTRA_OLD_PATHS, oldPaths); props.setProperty(PkgProps.MIN_PLATFORM_TOOLS_REV, Integer.toString((min_platform_tools_rev))); return new MockExtraPackage(source, props, vendor, newPath, revision) { @Override protected Archive[] initializeArchives( Properties props2, String archiveOsPath) { // Create one remote archive for this package return new Archive[] { new Archive( this, null, // arch "http://some.source/some_url", 1234, // size "abcdef") // sha1 }; } }; } private File createFile(String...segments) { File f = null; for (String segment : segments) { if (f == null) { f = new File(segment); } else { f = new File(f, segment); } } return f; } /** * Strips the second line of the string. * The source.properties generated by Java contain the generation data on the * second line and this is of course not suitable for unit tests. */ private String stripDate(String string) { // We know it's a date if it looks like: // \n # ... YYYY\n Pattern p = Pattern.compile("\n#[^#][^\n]*[0-9]{4}\\w*\n", Pattern.DOTALL); string = p.matcher(string.replaceAll("\r\n", "\n")).replaceAll("\n#...date...\n"); return string; } }