/* * 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.tradefed.build; import com.android.ddmlib.Log; import com.android.tradefed.util.FileUtil; import com.android.tradefed.util.StreamUtil; import junit.framework.TestCase; import org.easymock.EasyMock; import org.easymock.IAnswer; import java.io.File; import java.io.FileInputStream; import java.util.ArrayList; import java.util.List; /** * Longer running, concurrency based tests for {@link FileDownloadCache}. */ public class FileDownloadCacheFuncTest extends TestCase { private static final String REMOTE_PATH = "path"; private static final String DOWNLOADED_CONTENTS = "downloaded contents"; protected static final String LOG_TAG = "FileDownloadCacheFuncTest"; private IFileDownloader mMockDownloader; private FileDownloadCache mCache; private List<File> mReturnedFiles; @Override protected void setUp() throws Exception { super.setUp(); mMockDownloader = EasyMock.createStrictMock(IFileDownloader.class); mCache = new FileDownloadCache(FileUtil.createTempDir("functest")); mReturnedFiles = new ArrayList<File>(2); } @Override protected void tearDown() throws Exception { for (File file : mReturnedFiles) { file.delete(); } super.tearDown(); } /** * Test {@link FileDownloadCache#fetchRemoteFile(IFileDownloader, String)} being called * concurrently by two separate threads. */ @SuppressWarnings("unchecked") public void testFetchRemoteFile_concurrent() throws Exception { // Simulate a relatively slow file download IAnswer slowDownloadAnswer = new IAnswer() { @Override public Object answer() throws Throwable { Thread.sleep(500); File fileArg = (File) EasyMock.getCurrentArguments()[1]; FileUtil.writeToFile(DOWNLOADED_CONTENTS, fileArg); return null; } }; mMockDownloader.downloadFile(EasyMock.eq(REMOTE_PATH), (File)EasyMock.anyObject()); EasyMock.expectLastCall().andAnswer(slowDownloadAnswer); EasyMock.replay(mMockDownloader); Thread downloadThread1 = new Thread() { @Override public void run() { try { mReturnedFiles.add(mCache.fetchRemoteFile(mMockDownloader, REMOTE_PATH)); } catch (BuildRetrievalError e) { Log.e(LOG_TAG, e); } } }; Thread downloadThread2 = new Thread() { @Override public void run() { try { mReturnedFiles.add(mCache.fetchRemoteFile(mMockDownloader, REMOTE_PATH)); } catch (BuildRetrievalError e) { Log.e(LOG_TAG, e); } } }; downloadThread1.start(); downloadThread2.start(); downloadThread1.join(); downloadThread2.join(); assertNotNull(mCache.getCachedFile(REMOTE_PATH)); assertEquals(2, mReturnedFiles.size()); // returned files should be identical in content, but be different files assertTrue(mReturnedFiles.get(0) != mReturnedFiles.get(1)); assertEquals(DOWNLOADED_CONTENTS, StreamUtil.getStringFromStream(new FileInputStream( mReturnedFiles.get(0)))); assertEquals(DOWNLOADED_CONTENTS, StreamUtil.getStringFromStream(new FileInputStream( mReturnedFiles.get(1)))); EasyMock.verify(mMockDownloader); } /** * Verify the cache is built from disk contents on creation */ public void testConstructor_createCache() throws Exception { // create cache contents on disk File cacheRoot = FileUtil.createTempDir("constructorTest"); try { final String filecontents = "these are the file contents"; File file1 = new File(cacheRoot, REMOTE_PATH); FileUtil.writeToFile(filecontents, file1); // this is lame, but sleep for a small amount to ensure nestedFile has later timestamp // TODO: use mock File instead Thread.sleep(1000); File nestedDir = new File(cacheRoot, "aa"); nestedDir.mkdir(); File nestedFile = new File(nestedDir, "anotherpath"); FileUtil.writeToFile(filecontents, nestedFile); FileDownloadCache cache = new FileDownloadCache(cacheRoot); assertNotNull(cache.getCachedFile(REMOTE_PATH)); assertNotNull(cache.getCachedFile("aa/anotherpath")); assertEquals(REMOTE_PATH, cache.getOldestEntry()); } finally { FileUtil.recursiveDelete(cacheRoot); } } /** * Test scenario where an already too large cache is built from disk contents. */ public void testConstructor_cacheExceeded() throws Exception { File cacheRoot = FileUtil.createTempDir("testConstructor_cacheExceeded"); try { // create a couple existing files in cache final String filecontents = "these are the file contents"; final File file1 = new File(cacheRoot, REMOTE_PATH); FileUtil.writeToFile(filecontents, file1); // sleep for a small amount to ensure file2 has later timestamp // TODO: use mock File instead Thread.sleep(1000); final File file2 = new File(cacheRoot, "anotherpath"); FileUtil.writeToFile(filecontents, file2); new FileDownloadCache(cacheRoot) { @Override long getMaxFileCacheSize() { return file2.length() + 1; } }; // expect cache to be cleaned on startup, with oldest file1 deleted, but newest file // retained assertFalse(file1.exists()); assertTrue(file2.exists()); } finally { FileUtil.recursiveDelete(cacheRoot); } } }