diff options
author | Andy Clement <andrew.clement@gmail.com> | 2012-10-29 12:46:03 -0700 |
---|---|---|
committer | Andy Clement <andrew.clement@gmail.com> | 2012-10-29 12:46:03 -0700 |
commit | df1823b297d7b543c1917804ac650502bd4601b2 (patch) | |
tree | 145af00113f611598a09c5970c390fdc6f8ca716 /weaver/testsrc | |
parent | 8a6608f4d5d1a2aa8aa49a0a38da66a54d53c917 (diff) | |
download | aspectj-df1823b297d7b543c1917804ac650502bd4601b2.tar.gz aspectj-df1823b297d7b543c1917804ac650502bd4601b2.zip |
391123: ltw cache improvements
Diffstat (limited to 'weaver/testsrc')
9 files changed, 900 insertions, 53 deletions
diff --git a/weaver/testsrc/org/aspectj/weaver/tools/cache/AbstractCacheBackingTestSupport.java b/weaver/testsrc/org/aspectj/weaver/tools/cache/AbstractCacheBackingTestSupport.java new file mode 100644 index 000000000..d8c1f27b6 --- /dev/null +++ b/weaver/testsrc/org/aspectj/weaver/tools/cache/AbstractCacheBackingTestSupport.java @@ -0,0 +1,379 @@ +/******************************************************************************* + * Copyright (c) 2012 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Lyor Goldstein (vmware) add support for weaved class being re-defined + *******************************************************************************/ +package org.aspectj.weaver.tools.cache; + +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.ObjectOutputStream; +import java.io.StreamCorruptedException; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.security.CodeSource; +import java.security.ProtectionDomain; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; + +import junit.framework.TestCase; + +import org.aspectj.util.FileUtil; +import org.aspectj.util.LangUtil; +import org.aspectj.weaver.tools.cache.AbstractIndexedFileCacheBacking.IndexEntry; + +/** + */ +public abstract class AbstractCacheBackingTestSupport extends TestCase { + public static final String JAR_FILE_SUFFIX=".jar"; + /** + * Prefix used in URL(s) that reference a resource inside a JAR + */ + public static final String JAR_URL_PREFIX="jar:"; + /** + * Separator used in URL(s) that reference a resource inside a JAR + * to denote the sub-path inside the JAR + */ + public static final char RESOURCE_SUBPATH_SEPARATOR='!'; + + private File targetFolder; + private File testTempFolder; + protected File root; + + public static final String TEMP_SUBFOLDER_NAME="temp"; + + protected AbstractCacheBackingTestSupport() { + super(); + } + + protected AbstractCacheBackingTestSupport(String name) { + super(name); + } + + @Override + public void setUp() throws Exception { + super.setUp(); + if (root == null) { + root = createTempFile("aspectj", "testdir"); + FileUtil.deleteContents(root); + } + } + + @Override + public void tearDown() throws Exception { + if (root != null) { + FileUtil.deleteContents(root); + root = null; + } + + if (targetFolder != null) { + FileUtil.deleteContents(targetFolder); + } + + super.tearDown(); + } + + protected File ensureTempFolderExists () throws IllegalStateException { + synchronized(TEMP_SUBFOLDER_NAME) { + if (testTempFolder == null) { + File parent=detectTargetFolder(); + testTempFolder = new File(parent, TEMP_SUBFOLDER_NAME); + } + } + + return ensureFolderExists(testTempFolder); + } + + protected File detectTargetFolder () throws IllegalStateException { + synchronized(TEMP_SUBFOLDER_NAME) { + if (targetFolder == null) { + if ((targetFolder=detectTargetFolder(getClass())) == null) { + throw new IllegalStateException("Failed to detect target folder"); + } + } + } + + return targetFolder; + } + + protected File createTempFile (String prefix, String suffix) throws IOException { + File destFolder=ensureTempFolderExists(); + return File.createTempFile(prefix, suffix, destFolder); + } + + public static final File ensureFolderExists (File folder) throws IllegalStateException { + if (folder == null) { + throw new IllegalArgumentException("No folder to ensure existence"); + } + + if ((!folder.exists()) && (!folder.mkdirs())) { + throw new IllegalStateException("Failed to create " + folder.getAbsolutePath()); + } + + return folder; + } + /** + * @param anchor An anchor {@link Class} whose container we want to use + * as the starting point for the "target" folder lookup up the + * hierarchy + * @return The "target" <U>folder</U> - <code>null</code> if not found + * @see #detectTargetFolder(File) + */ + public static final File detectTargetFolder (Class<?> anchor) { + return detectTargetFolder(getClassContainerLocationFile(anchor)); + } + + /** + * @param anchorFile An anchor {@link File) we want to use + * as the starting point for the "target" folder lookup up the + * hierarchy + * @return The "target" <U>folder</U> - <code>null</code> if not found + */ + public static final File detectTargetFolder (File anchorFile) { + for (File file=anchorFile; file != null; file=file.getParentFile()) { + if (!file.isDirectory()) { + continue; + } + + String name=file.getName(); + if ("bin".equals(name) || "src".equals(name)) { + File parent=file.getParentFile(); + return new File(parent, "target"); + } + } + + return null; + } + + /** + * @param clazz A {@link Class} object + * @return A {@link File} of the location of the class bytes container + * - e.g., the root folder, the containing JAR, etc.. Returns + * <code>null</code> if location could not be resolved + * @throws IllegalArgumentException If location is not a valid + * {@link File} location + * @see #getClassContainerLocationURI(Class) + * @see File#File(URI) + */ + public static File getClassContainerLocationFile (Class<?> clazz) + throws IllegalArgumentException { + try { + URI uri=getClassContainerLocationURI(clazz); + return (uri == null) ? null : new File(uri); + } catch(URISyntaxException e) { + throw new IllegalArgumentException(e.getClass().getSimpleName() + ": " + e.getMessage(), e); + } + } + + /** + * @param clazz A {@link Class} object + * @return A {@link URI} to the location of the class bytes container + * - e.g., the root folder, the containing JAR, etc.. Returns + * <code>null</code> if location could not be resolved + * @throws URISyntaxException if location is not a valid URI + * @see #getClassContainerLocationURL(Class) + */ + public static URI getClassContainerLocationURI (Class<?> clazz) throws URISyntaxException { + URL url=getClassContainerLocationURL(clazz); + return (url == null) ? null : url.toURI(); + } + + /** + * @param clazz A {@link Class} object + * @return A {@link URL} to the location of the class bytes container + * - e.g., the root folder, the containing JAR, etc.. Returns + * <code>null</code> if location could not be resolved + */ + public static URL getClassContainerLocationURL (Class<?> clazz) { + ProtectionDomain pd=clazz.getProtectionDomain(); + CodeSource cs=(pd == null) ? null : pd.getCodeSource(); + URL url=(cs == null) ? null : cs.getLocation(); + if (url == null) { + ClassLoader cl=getDefaultClassLoader(clazz); + String className=clazz.getName().replace('.', '/') + ".class"; + if ((url=cl.getResource(className)) == null) { + return null; + } + + String srcForm=getURLSource(url); + if (LangUtil.isEmpty(srcForm)) { + return null; + } + + try { + url = new URL(srcForm); + } catch(MalformedURLException e) { + throw new IllegalArgumentException("getClassContainerLocationURL(" + clazz.getName() + ")" + + "Failed to create URL=" + srcForm + " from " + url.toExternalForm() + + ": " + e.getMessage()); + } + } + + return url; + } + /** + * @param anchor An "anchor" {@link Class} to be used in case + * no thread context loader is available + * @return A {@link ClassLoader} to be used by the caller. The loader is + * resolved in the following manner:</P></BR> + * <UL> + * <LI> + * If a non-<code>null</code> loader is returned from the + * {@link Thread#getContextClassLoader()} call then use it. + * </LI> + * + * <LI> + * Otherwise, use the same loader that was used to load the anchor class. + * </LI> + * </UL> + * @throws IllegalArgumentException if no anchor class provided (regardless of + * whether it is used or not) + */ + public static ClassLoader getDefaultClassLoader(Class<?> anchor) { + if (anchor == null) { + throw new IllegalArgumentException("No anchor class provided"); + } + + Thread t=Thread.currentThread(); + ClassLoader cl=t.getContextClassLoader(); + if (cl == null) { + // No thread context class loader -> use class loader of this class. + cl = anchor.getClassLoader(); + } + + if (cl == null) { // no class loader - assume system + cl = ClassLoader.getSystemClassLoader(); + } + + return cl; + + } + public static final String getURLSource (File file) { + return getURLSource((file == null) ? null : file.toURI()); + } + + public static final String getURLSource (URI uri) { + return getURLSource((uri == null) ? null : uri.toString()); + } + + /** + * @param url The {@link URL} value - ignored if <code>null</code> + * @return The URL(s) source path where {@link #JAR_URL_PREFIX} and + * any sub-resource are stripped + * @see #getURLSource(String) + */ + public static final String getURLSource (URL url) { + return getURLSource((url == null) ? null : url.toExternalForm()); + } + + /** + * @param externalForm The {@link URL#toExternalForm()} string - ignored if + * <code>null</code>/empty + * @return The URL(s) source path where {@link #JAR_URL_PREFIX} and + * any sub-resource are stripped + */ + public static final String getURLSource (String externalForm) { + String url=externalForm; + if (LangUtil.isEmpty(url)) { + return url; + } + + url = stripJarURLPrefix(externalForm); + if (LangUtil.isEmpty(url)){ + return url; + } + + int sepPos=url.indexOf(RESOURCE_SUBPATH_SEPARATOR); + if (sepPos < 0) { + return adjustURLPathValue(url); + } else { + return adjustURLPathValue(url.substring(0, sepPos)); + } + } + + /** + * @param path A URL path value + * @return The path after stripping any trailing '/' provided the path + * is not '/' itself + */ + public static final String adjustURLPathValue(final String path) { + final int pathLen=LangUtil.isEmpty(path) ? 0 : path.length(); + if ((pathLen <= 1) || (path.charAt(pathLen - 1) != '/')) { + return path; + } + + return path.substring(0, pathLen - 1); + } + + public static final String adjustURLPathValue(URL url) { + return adjustURLPathValue((url == null) ? null : url.getPath()); + } + + public static String stripJarURLPrefix(String externalForm) { + String url=externalForm; + if (LangUtil.isEmpty(url)) { + return url; + } + + if (url.startsWith(JAR_URL_PREFIX)) { + return url.substring(JAR_URL_PREFIX.length()); + } + + return url; + } + + protected static final void writeIndex (File indexFile, IndexEntry ... entries) throws IOException { + writeIndex(indexFile, LangUtil.isEmpty(entries) ? Collections.<IndexEntry>emptyList() : Arrays.asList(entries)); + } + + protected static final void writeIndex (File indexFile, Collection<? extends IndexEntry> entries) throws IOException { + File indexDir=indexFile.getParentFile(); + if ((!indexDir.exists()) && (!indexDir.mkdirs())) { + throw new IOException("Failed to create path to " + indexFile.getAbsolutePath()); + } + + int numEntries=LangUtil.isEmpty(entries) ? 0 : entries.size(); + IndexEntry[] entryValues=(numEntries <= 0) ? null : entries.toArray(new IndexEntry[numEntries]); + // if no entries, simply delete the index file + if (LangUtil.isEmpty(entryValues)) { + if (indexFile.exists() && (!indexFile.delete())) { + throw new StreamCorruptedException("Failed to clean up index file at " + indexFile.getAbsolutePath()); + } + + return; + } + + ObjectOutputStream oos=new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(indexFile), 4096)); + try { + oos.writeObject(entryValues); + } finally { + oos.close(); + } + } + + public static final void assertArrayEquals (String msg, byte[] expected, byte[] actual) { + int eLen=LangUtil.isEmpty(expected) ? 0 : expected.length; + int aLen=LangUtil.isEmpty(actual) ? 0 : expected.length; + assertEquals(msg + "[mismatched length]", eLen, aLen); + + for (int index=0; index < eLen; index++) { + byte eb=expected[index], ab=actual[index]; + if (eb != ab) { + fail(msg + ": Mismatched value at index=" + index + + " - " + ab + " instead of " + eb + + ": expected=" + Arrays.toString(expected) + ", actual=" + Arrays.toString(actual)); + } + } + } +} diff --git a/weaver/testsrc/org/aspectj/weaver/tools/cache/AsynchronousFileCacheBackingTestSupport.java b/weaver/testsrc/org/aspectj/weaver/tools/cache/AsynchronousFileCacheBackingTestSupport.java new file mode 100644 index 000000000..7d1b66407 --- /dev/null +++ b/weaver/testsrc/org/aspectj/weaver/tools/cache/AsynchronousFileCacheBackingTestSupport.java @@ -0,0 +1,193 @@ +/******************************************************************************* + * Copyright (c) 2012 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Lyor Goldstein (vmware) add support for weaved class being re-defined + *******************************************************************************/ +package org.aspectj.weaver.tools.cache; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import java.util.Random; +import java.util.TreeMap; + +import org.aspectj.util.FileUtil; +import org.aspectj.util.LangUtil; +import org.aspectj.weaver.tools.cache.AbstractIndexedFileCacheBacking.IndexEntry; + +/** + */ +public abstract class AsynchronousFileCacheBackingTestSupport + extends AbstractCacheBackingTestSupport { + private File cacheDir, indexFile; + protected final byte[] bytes=new byte[Byte.MAX_VALUE]; + protected final Random random=new Random(System.nanoTime()); + + protected AsynchronousFileCacheBackingTestSupport() { + super(); + } + + protected AsynchronousFileCacheBackingTestSupport(String name) { + super(name); + } + + @Override + public void setUp () throws Exception { + super.setUp(); + cleanupCache(); + + random.nextBytes(bytes); + } + + @Override + public void tearDown () throws Exception { + cleanupCache(); + super.tearDown(); + } + + protected void cleanupCache() { + if (indexFile != null) { + if (FileUtil.deleteContents(indexFile) > 0) { + System.out.println("Deleted " + indexFile); + } + indexFile = null; + } + + if (cacheDir != null) { + if (FileUtil.deleteContents(cacheDir) > 0) { + System.out.println("Deleted " + cacheDir); + } + cacheDir = null; + } + } + + protected File getIndexFile () { + if (indexFile == null) { + File parent=getCacheDir(); + indexFile=new File(parent, AbstractIndexedFileCacheBacking.INDEX_FILE); + } + + return indexFile; + } + + protected File getCacheDir () { + if (cacheDir == null) { + File targetDir=detectTargetFolder(); + cacheDir = new File(targetDir, "dir-" + String.valueOf(Math.random())); + } + + return ensureFolderExists(cacheDir); + } + + protected abstract AsynchronousFileCacheBacking createFileBacking (File dir); + + public void testDeleteIndexFileOnEmptyIndex () throws Exception { + IndexEntry[] entries={ + createIndexEntry("weaved-empty", false, false, bytes, bytes), + createIndexEntry("generated-empty", true, false, bytes, bytes) + }; + File cacheIndex=getIndexFile(); + writeIndex(cacheIndex, entries); + assertTrue("No initial index file available: " + cacheIndex, cacheIndex.canRead()); + + AsynchronousFileCacheBacking cache=createFileBacking(getCacheDir()); + // the call should read an empty index since no data files exist + Map<String, IndexEntry> indexMap=cache.getIndexMap(); + assertEquals("Mismatched index size", 0, indexMap.size()); + + // no data files were created + Map<String, byte[]> bytesMap=cache.getBytesMap(); + assertEquals("Mismatched bytes size", 0, bytesMap.size()); + + writeIndex(cache.getIndexFile(), cache.getIndexEntries()); + + assertFalse("Index file still available: " + cacheIndex, cacheIndex.canRead()); + } + + protected long generateNewBytes () { + final long CRC=AbstractCacheBacking.crc(bytes); + long crc=CRC; + // 8 tries should be enough to find a non-matching CRC... + for (int index=0; (index < Byte.SIZE) && (CRC == crc) && (crc != -1L); index++) { + random.nextBytes(bytes); + crc = AbstractCacheBacking.crc(bytes); + } + assertTrue("Could not generate different CRC for " + CRC, crc != CRC); + + return crc; + } + + protected Map<String, File> createDataFiles (IndexEntry ... entries) throws IOException { + return createDataFiles(LangUtil.isEmpty(entries) ? Collections.<IndexEntry>emptyList() : Arrays.asList(entries)); + } + + protected Map<String, File> createDataFiles (Collection<? extends IndexEntry> entries) throws IOException { + if (LangUtil.isEmpty(entries)) { + return Collections.emptyMap(); + } + + Map<String, File> files=new TreeMap<String, File>(); + for (IndexEntry entry : entries) { + File file=createDataFile(entry); + if (file != null) { + files.put(entry.key, file); + } + } + + return files; + } + + protected File createDataFile (IndexEntry entry) throws IOException { + return createDataFile(entry, entry.ignored ? null : bytes); + } + + protected File createDataFile (IndexEntry entry, byte[] dataBytes) throws IOException { + return createDataFile(entry.key, dataBytes); + } + + protected File createDataFile (String key, byte[] dataBytes) throws IOException { + if (LangUtil.isEmpty(dataBytes)) { + return null; + } + + File parent=getCacheDir(), file=new File(parent, key); + OutputStream out=new FileOutputStream(file); + try { + out.write(dataBytes); + } finally { + out.close(); + } + + return file; + } + + protected static final IndexEntry createIgnoredEntry (String key) { + return createIndexEntry(key, false, true, null, null); + } + + protected static final IndexEntry createIndexEntry (String key, boolean generated, boolean ignored, byte[] bytes, byte[] originalBytes) { + IndexEntry entry=new IndexEntry(); + entry.key = key; + entry.generated = generated; + entry.ignored = ignored; + if (ignored) { + assertFalse(key + " ignored cannot be generated", generated); + } else { + entry.crcClass = AbstractCacheBacking.crc(originalBytes); + entry.crcWeaved = AbstractCacheBacking.crc(bytes); + } + + return entry; + } +} diff --git a/weaver/testsrc/org/aspectj/weaver/tools/cache/CacheTests.java b/weaver/testsrc/org/aspectj/weaver/tools/cache/CacheTests.java index 861f637fc..b59665b4e 100644 --- a/weaver/testsrc/org/aspectj/weaver/tools/cache/CacheTests.java +++ b/weaver/testsrc/org/aspectj/weaver/tools/cache/CacheTests.java @@ -24,6 +24,8 @@ public class CacheTests { suite.addTestSuite(WeavedClassCacheTest.class); suite.addTestSuite(DefaultCacheKeyResolverTest.class); suite.addTestSuite(DefaultFileCacheBackingTest.class); + suite.addTestSuite(FlatFileCacheBackingTest.class); + suite.addTestSuite(ZippedFileCacheBackingTest.class); return suite; } } diff --git a/weaver/testsrc/org/aspectj/weaver/tools/cache/DefaultCacheKeyResolverTest.java b/weaver/testsrc/org/aspectj/weaver/tools/cache/DefaultCacheKeyResolverTest.java index 8dbe31463..139488b3c 100644 --- a/weaver/testsrc/org/aspectj/weaver/tools/cache/DefaultCacheKeyResolverTest.java +++ b/weaver/testsrc/org/aspectj/weaver/tools/cache/DefaultCacheKeyResolverTest.java @@ -28,6 +28,9 @@ public class DefaultCacheKeyResolverTest extends TestCase { DefaultCacheKeyResolver resolver = new DefaultCacheKeyResolver(); class BasicTestCL extends ClassLoader { + public BasicTestCL () { + super(); + } } class URLTestCL extends URLClassLoader { diff --git a/weaver/testsrc/org/aspectj/weaver/tools/cache/DefaultFileCacheBackingTest.java b/weaver/testsrc/org/aspectj/weaver/tools/cache/DefaultFileCacheBackingTest.java index 682869750..2d5ec0c77 100644 --- a/weaver/testsrc/org/aspectj/weaver/tools/cache/DefaultFileCacheBackingTest.java +++ b/weaver/testsrc/org/aspectj/weaver/tools/cache/DefaultFileCacheBackingTest.java @@ -16,42 +16,22 @@ package org.aspectj.weaver.tools.cache; import java.io.File; import java.util.zip.CRC32; -import junit.framework.TestCase; - -import org.aspectj.util.FileUtil; import org.aspectj.util.LangUtil; import org.aspectj.weaver.tools.cache.AbstractIndexedFileCacheBacking.IndexEntry; /** */ -public class DefaultFileCacheBackingTest extends TestCase { +public class DefaultFileCacheBackingTest extends AbstractCacheBackingTestSupport { private final byte[] FAKE_BYTES = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; private final String FAKE_CLASS = "com.example.foo.Bar"; private final CacheKeyResolver resolver = new DefaultCacheKeyResolver(); private final CachedClassReference fakeRef = resolver.weavedKey(FAKE_CLASS, FAKE_BYTES); private final String fakeKey=fakeRef.getKey(); - private File root; - public DefaultFileCacheBackingTest () { super(); } - @Override - public void setUp() throws Exception { - if (root == null) { - File tempFile = File.createTempFile("aspectj", "test"); - File tempDir = tempFile.getParentFile(); - root = new File(tempDir, "aspectj-test-cache"); - } - } - - @Override - public void tearDown() throws Exception { - FileUtil.deleteContents(root); - root = null; - } - public void testCreateBacking() throws Exception { CacheBacking backing = DefaultFileCacheBacking.createBacking(root); assertNotNull(backing); diff --git a/weaver/testsrc/org/aspectj/weaver/tools/cache/FlatFileCacheBackingTest.java b/weaver/testsrc/org/aspectj/weaver/tools/cache/FlatFileCacheBackingTest.java new file mode 100644 index 000000000..8c6df7ad8 --- /dev/null +++ b/weaver/testsrc/org/aspectj/weaver/tools/cache/FlatFileCacheBackingTest.java @@ -0,0 +1,154 @@ +/******************************************************************************* + * Copyright (c) 2012 VMware, Inc. + * + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Lyor Goldstein + *******************************************************************************/ + +package org.aspectj.weaver.tools.cache; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.Map; +import java.util.TreeMap; + +import org.aspectj.weaver.tools.cache.AbstractIndexedFileCacheBacking.IndexEntry; + +/** + * @author Lyor Goldstein + */ +public class FlatFileCacheBackingTest extends AsynchronousFileCacheBackingTestSupport { + public FlatFileCacheBackingTest() { + super(); + } + + @Override + protected FlatFileCacheBacking createFileBacking(File dir) { + return new FlatFileCacheBacking(dir); + } + + public void testReadIndex () throws IOException { + IndexEntry[] entries={ + createIgnoredEntry("ignored"), + createIndexEntry("weaved", false, false, bytes, bytes), + createIndexEntry("generated", true, false, bytes, bytes) + }; + File indexFile=getIndexFile(); + writeIndex(indexFile, entries); + Map<String, File> dataFiles=createDataFiles(entries); + + File cacheDir=getCacheDir(); + AsynchronousFileCacheBacking cache=createFileBacking(cacheDir); + Map<String, IndexEntry> indexMap=cache.getIndexMap(); + assertEquals("Mismatched index size", entries.length, indexMap.size()); + + Map<String, byte[]> bytesMap=cache.getBytesMap(); + assertEquals("Mismatched bytes size", dataFiles.size() /* the ignored one has no file */, bytesMap.size()); + + for (IndexEntry entry : entries) { + String key=entry.key; + assertNotNull("Missing entry for key=" + key, indexMap.get(key)); + + if (entry.ignored) { + assertNull("Unexpected bytes for ignored key=" + key, bytesMap.get(key)); + } else { + assertArrayEquals("Mismatched contents for key=" + key, bytes, bytesMap.get(key)); + } + } + } + + public void testIgnoredBadCrcDataFiles () throws Exception { + IndexEntry[] entries={ + createIndexEntry("weaved-goodData", false, false, bytes, bytes), + createIndexEntry("badData-weaved", false, false, bytes, bytes), + createIndexEntry("generated-goodData", true, false, bytes, bytes), + createIndexEntry("badData-generated", true, false, bytes, bytes) + }; + File indexFile=getIndexFile(); + writeIndex(indexFile, entries); + + Map<String,File> dataFiles=createDataFiles(entries); + long newCrc=generateNewBytes(); + assertTrue("Bad new CRC", newCrc != (-1L)); + + Map<String,File> badFiles=new TreeMap<String, File>(); + for (IndexEntry entry : entries) { + String key=entry.key; + if (key.startsWith("badData")) { + File file=dataFiles.get(key); + OutputStream out=new FileOutputStream(file); + try { + out.write(bytes); + } finally { + out.close(); + } + dataFiles.remove(key); + badFiles.put(key, file); + } + } + + File cacheDir=getCacheDir(); + FlatFileCacheBacking cache=createFileBacking(cacheDir); + Map<String, IndexEntry> indexMap=cache.getIndexMap(); + assertEquals("Mismatched index size", dataFiles.size(), indexMap.size()); + + Map<String, byte[]> bytesMap=cache.getBytesMap(); + assertEquals("Mismatched bytes size", dataFiles.size(), bytesMap.size()); + + for (Map.Entry<String,File> badEntry : badFiles.entrySet()) { + String key=badEntry.getKey(); + assertFalse("Unexpectedly indexed: " + key, indexMap.containsKey(key)); + assertFalse("Unexpectedly loaded: " + key, bytesMap.containsKey(key)); + + File file=badEntry.getValue(); + assertFalse("Unexpectedly still readable: " + key, file.canRead()); + } + } + + public void testSkipMissingDataFileOnReadIndex () throws IOException { + IndexEntry[] entries={ + createIndexEntry("weaved-noData", false, false, null, null), + createIndexEntry("withData-weaved", false, false, bytes, bytes), + createIndexEntry("generated-noData", true, false, null, null), + createIndexEntry("withData-generated", true, false, bytes, bytes) + }; + File indexFile=getIndexFile(); + writeIndex(indexFile, entries); + + Map<String,File> dataFiles=new TreeMap<String, File>(); + for (IndexEntry entry : entries) { + String key=entry.key; + if (key.startsWith("withData")) { + dataFiles.put(key, createDataFile(entry, bytes)); + } + } + + File cacheDir=getCacheDir(); + FlatFileCacheBacking cache=createFileBacking(cacheDir); + Map<String, IndexEntry> indexMap=cache.getIndexMap(); + assertEquals("Mismatched index size", dataFiles.size(), indexMap.size()); + + Map<String, byte[]> bytesMap=cache.getBytesMap(); + assertEquals("Mismatched bytes size", dataFiles.size(), bytesMap.size()); + + for (IndexEntry entry : entries) { + String key=entry.key; + if (key.startsWith("withData")) { + assertTrue("Not indexed: " + key, indexMap.containsKey(key)); + assertTrue("Not loaded: " + key, bytesMap.containsKey(key)); + } else { + assertFalse("Unexpectedly indexed: " + key, indexMap.containsKey(key)); + assertFalse("Unexpectedly loaded: " + key, bytesMap.containsKey(key)); + } + } + } + +} diff --git a/weaver/testsrc/org/aspectj/weaver/tools/cache/SimpleClassCacheTest.java b/weaver/testsrc/org/aspectj/weaver/tools/cache/SimpleClassCacheTest.java index 0624d0ff0..68fac6913 100644 --- a/weaver/testsrc/org/aspectj/weaver/tools/cache/SimpleClassCacheTest.java +++ b/weaver/testsrc/org/aspectj/weaver/tools/cache/SimpleClassCacheTest.java @@ -12,17 +12,9 @@ package org.aspectj.weaver.tools.cache; -import junit.framework.TestCase; -import org.aspectj.bridge.AbortException; -import org.aspectj.bridge.IMessage; -import org.aspectj.bridge.IMessageHandler; -import org.aspectj.weaver.tools.GeneratedClassHandler; - import java.io.File; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Set; + +import junit.framework.TestCase; /** */ diff --git a/weaver/testsrc/org/aspectj/weaver/tools/cache/WeavedClassCacheTest.java b/weaver/testsrc/org/aspectj/weaver/tools/cache/WeavedClassCacheTest.java index d972bff8b..a02400eb8 100644 --- a/weaver/testsrc/org/aspectj/weaver/tools/cache/WeavedClassCacheTest.java +++ b/weaver/testsrc/org/aspectj/weaver/tools/cache/WeavedClassCacheTest.java @@ -12,23 +12,26 @@ package org.aspectj.weaver.tools.cache; -import junit.framework.TestCase; -import org.aspectj.bridge.AbortException; -import org.aspectj.bridge.IMessage; -import org.aspectj.bridge.IMessageHandler; -import org.aspectj.weaver.tools.GeneratedClassHandler; - import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Set; +import org.aspectj.bridge.AbortException; +import org.aspectj.bridge.IMessage; +import org.aspectj.bridge.IMessageHandler; +import org.aspectj.weaver.tools.GeneratedClassHandler; + /** */ -public class WeavedClassCacheTest extends TestCase { +public class WeavedClassCacheTest extends AbstractCacheBackingTestSupport { String FAKE_CLASS = "com.example.foo.Bar"; byte[] FAKE_BYTES = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + public WeavedClassCacheTest () { + super(); + } + public class MemoryCacheBacking implements CacheBacking { HashMap<String, CachedClassEntry> cache = new HashMap<String, CachedClassEntry>(); @@ -73,9 +76,11 @@ public class WeavedClassCacheTest extends TestCase { } public void dontIgnore(IMessage.Kind kind) { + // do nothing } public void ignore(IMessage.Kind kind) { + // do nothing } }; @@ -83,7 +88,6 @@ public class WeavedClassCacheTest extends TestCase { public int accepts = 0; public List<String> classesISaw = new LinkedList<String>(); - @Override public void acceptClass (String name, byte[] originalBytes, byte[] wovenBytes) { accepts++; classesISaw.add(name); @@ -111,20 +115,6 @@ public class WeavedClassCacheTest extends TestCase { assertTrue(newHandle instanceof GeneratedCachedClassHandler); } - -// public void testExistingGeneratedClassesPassedThroughHandler() throws Exception { -// String classA = "com.generated.A"; -// String classB = "com.generated.B"; -// reset(); -// memoryBacking.put(new CachedClassEntry(resolver.generatedKey(classA), FAKE_BYTES, CachedClassEntry.EntryType.GENERATED), FAKE_BYTES); -// memoryBacking.put(new CachedClassEntry(resolver.generatedKey(classB), FAKE_BYTES, CachedClassEntry.EntryType.GENERATED), FAKE_BYTES); -// createCache(); -// assertEquals(2, generatedClassHandler.accepts); -// for (String cName : generatedClassHandler.classesISaw) { -// assertTrue("Got: " + cName, cName.equals(classA) || cName.equals(classB)); -// } -// } - public void testCache() throws Exception { reset(); WeavedClassCache cache = createCache(); diff --git a/weaver/testsrc/org/aspectj/weaver/tools/cache/ZippedFileCacheBackingTest.java b/weaver/testsrc/org/aspectj/weaver/tools/cache/ZippedFileCacheBackingTest.java new file mode 100644 index 000000000..4c41c1807 --- /dev/null +++ b/weaver/testsrc/org/aspectj/weaver/tools/cache/ZippedFileCacheBackingTest.java @@ -0,0 +1,154 @@ +/** + * Copyright (c) 2012 VMware, Inc. + * + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Lyor Goldstein + */ + +package org.aspectj.weaver.tools.cache; + +import java.io.File; +import java.io.IOException; +import java.util.Map; +import java.util.TreeMap; + +import org.aspectj.util.FileUtil; +import org.aspectj.weaver.tools.cache.AbstractIndexedFileCacheBacking.IndexEntry; + +/** + * + */ +public class ZippedFileCacheBackingTest extends AsynchronousFileCacheBackingTestSupport { + private File zipTestFile; + + public ZippedFileCacheBackingTest() { + super(); + } + + public void testReadIndex () throws Exception { + IndexEntry[] entries={ + createIgnoredEntry("ignored"), + createIndexEntry("weaved", false, false, bytes, bytes), + createIndexEntry("generated", true, false, bytes, bytes) + }; + File indexFile=getIndexFile(); + writeIndex(indexFile, entries); + + Map<String,byte[]> entriesMap=new TreeMap<String,byte[]>(); + for (IndexEntry ie : entries) { + if (ie.ignored) { + continue; + } + + entriesMap.put(ie.key, bytes); + } + + File zipFile=getZipFile(); + ZippedFileCacheBacking.writeZipClassBytes(zipFile, entriesMap); + + File cacheDir=getCacheDir(); + AsynchronousFileCacheBacking cache=createFileBacking(cacheDir); + Map<String, IndexEntry> indexMap=cache.getIndexMap(); + assertEquals("Mismatched index size", entries.length, indexMap.size()); + + Map<String, byte[]> bytesMap=cache.getBytesMap(); + assertEquals("Mismatched bytes size", entriesMap.size() /* the ignored one has no file */, bytesMap.size()); + + for (IndexEntry entry : entries) { + String key=entry.key; + assertNotNull("Missing entry for key=" + key, indexMap.get(key)); + + if (entry.ignored) { + assertNull("Unexpected bytes for ignored key=" + key, bytesMap.get(key)); + } else { + assertArrayEquals("Mismatched contents for key=" + key, bytes, bytesMap.get(key)); + } + } + } + + public void testReadWriteZipClassBytes () throws IOException { + Map<String,byte[]> entriesMap=new TreeMap<String,byte[]>(); + for (int index=0; index < Byte.SIZE; index++) { + String name="classBytes#" + index; + random.nextBytes(bytes); + entriesMap.put(name, bytes); + } + + File zipFile=getZipFile(); + ZippedFileCacheBacking.writeZipClassBytes(zipFile, entriesMap); + + Map<String, byte[]> bytesMap=ZippedFileCacheBacking.readZipClassBytes(zipFile); + assertEquals("Mismatched recovered entries size", entriesMap.size(), bytesMap.size()); + for (Map.Entry<String,byte[]> bytesEntry : entriesMap.entrySet()) { + String key=bytesEntry.getKey(); + byte[] expected=bytesEntry.getValue(), actual=bytesMap.get(key); + assertArrayEquals("Mismatched data for " + key, expected, actual); + } + } + + public void testReadClassBytes () throws IOException { + IndexEntry[] entries={ + createIgnoredEntry("ignoredReadClassBytes"), + createIndexEntry("weavedReadClassBytes", false, false, bytes, bytes), + createIndexEntry("generatedReadClassBytes", true, false, bytes, bytes) + }; + File indexFile=getIndexFile(); + writeIndex(indexFile, entries); + + long newCrc=generateNewBytes(); + assertTrue("Bad new CRC", newCrc != (-1L)); + + Map<String,byte[]> entriesMap=new TreeMap<String,byte[]>(); + for (IndexEntry ie : entries) { + if (ie.ignored) { + continue; + } + + entriesMap.put(ie.key, bytes); + } + + File zipFile=getZipFile(); + ZippedFileCacheBacking.writeZipClassBytes(zipFile, entriesMap); + + File cacheDir=getCacheDir(); + AsynchronousFileCacheBacking cache=createFileBacking(cacheDir); + Map<String, IndexEntry> indexMap=cache.getIndexMap(); + assertEquals("Mismatched index size", 1 /* only the ignored entry */, indexMap.size()); + + Map<String, byte[]> bytesMap=cache.getBytesMap(); + assertEquals("Non empty data bytes", 0, bytesMap.size()); + assertFalse("Zip file not deleted: " + zipFile, zipFile.canRead()); + } + + protected File getZipFile () { + if (zipTestFile == null) { + File cacheDir=getCacheDir(); + zipTestFile = new File(cacheDir, ZippedFileCacheBacking.ZIP_FILE); + } + + return zipTestFile; + } + + @Override + protected void cleanupCache() { + if (zipTestFile != null) { + if (FileUtil.deleteContents(zipTestFile) > 0) { + System.out.println("Deleted " + zipTestFile); + } + zipTestFile = null; + } + + super.cleanupCache(); + } + + @Override + protected ZippedFileCacheBacking createFileBacking(File dir) { + return new ZippedFileCacheBacking(dir); + } +}
\ No newline at end of file |