/* * Copyright 2011 Google Inc. * * 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.google.gwt.dev.javac; import com.google.gwt.core.ext.TreeLogger; import com.google.gwt.core.ext.UnableToCompleteException; import com.google.gwt.dev.util.Util; import junit.framework.TestCase; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; /** * Unit test for {@link PersistentUnitCache}. */ public class PersistentUnitCacheTest extends TestCase { private static class ThrowsClassNotFoundException implements Serializable { @SuppressWarnings("unused") private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { throw new ClassNotFoundException(); } } private static class ThrowsIOException implements Serializable { @SuppressWarnings("unused") private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { throw new IOException(); } } File lastCacheDir = null; public void tearDown() { if (lastCacheDir != null) { Util.recursiveDelete(lastCacheDir, false); } lastCacheDir = null; } /** * When a cache file encounters a serialization error, the logic should assume * the cache log is stale and remove it. */ public void testClassNotFoundException() throws IOException, UnableToCompleteException, InterruptedException { checkInvalidObjectInCache(new ThrowsClassNotFoundException()); } /** * Test if a file already exists with the name we want to put the cache dir * in. */ public void testFileInTheWay() throws IOException { TreeLogger logger = TreeLogger.NULL; File fileInTheWay = File.createTempFile("PersistentUnitTest-inTheWay", ""); assertNotNull(fileInTheWay); assertTrue(fileInTheWay.exists()); fileInTheWay.deleteOnExit(); try { new PersistentUnitCache(logger, fileInTheWay); fail("Expected an exception to be thrown"); } catch (UnableToCompleteException expected) { } } /** * If a cache file has some kind of IO exception, (this can happen with a * stale cache file), then the exception should be ignored and the cache file * removed. */ public void testIOException() throws IOException, UnableToCompleteException, InterruptedException { checkInvalidObjectInCache(new ThrowsIOException()); } /** * The cache should recursively create the directories it needs. */ public void testNewDir() throws IOException, UnableToCompleteException { TreeLogger logger = TreeLogger.NULL; File baseDir = File.createTempFile("PersistentUnitTest-newDir", ""); assertNotNull(baseDir); assertTrue(baseDir.exists()); assertTrue(baseDir.delete()); File newDir = lastCacheDir = new File(baseDir, "sHoUlDnOtExi57"); new PersistentUnitCache(logger, newDir); assertTrue(newDir.isDirectory()); } public void testPersistentCache() throws IOException, InterruptedException, UnableToCompleteException { TreeLogger logger = TreeLogger.NULL; File cacheDir = lastCacheDir = File.createTempFile("persistentCacheTest", ""); File unitCacheDir = mkCacheDir(cacheDir); PersistentUnitCache cache = new PersistentUnitCache(logger, cacheDir); MockCompilationUnit foo1 = new MockCompilationUnit("com.example.Foo", "Foo: source1"); cache.add(foo1); MockCompilationUnit bar1 = new MockCompilationUnit("com.example.Bar", "Bar: source1"); cache.add(bar1); CompilationUnit result; // Find by content Id result = cache.find(foo1.getContentId()); assertNotNull(result); assertEquals("com.example.Foo", result.getTypeName()); result = cache.find(bar1.getContentId()); assertNotNull(result); assertEquals("com.example.Bar", result.getTypeName()); // Find by type name result = cache.find("com/example/Foo.java"); assertNotNull(result); assertEquals("com.example.Foo", result.getTypeName()); result = cache.find("com/example/Bar.java"); assertNotNull(result); assertEquals("com.example.Bar", result.getTypeName()); // Replace Foo with a new version MockCompilationUnit foo2 = new MockCompilationUnit("com.example.Foo", "Foo: source2"); cache.add(foo2); result = cache.find(foo1.getContentId()); assertNull(result); result = cache.find(foo2.getContentId()); assertNotNull(result); assertEquals("com.example.Foo", result.getTypeName()); result = cache.find("com/example/Foo.java"); assertNotNull(result); assertEquals("com.example.Foo", result.getTypeName()); assertEquals(foo2.getContentId(), result.getContentId()); cache.cleanup(logger); // Shutdown the cache and re -load it cache.shutdown(); // There should be a single file in the cache dir. assertNumCacheFiles(unitCacheDir, 1); // Fire up the cache again. It be pre-populated. // Search by type name cache = new PersistentUnitCache(logger, cacheDir); result = cache.find("com/example/Foo.java"); assertNotNull(result); assertEquals("com.example.Foo", result.getTypeName()); assertEquals(foo2.getContentId(), result.getContentId()); result = cache.find("com/example/Bar.java"); assertNotNull(result); assertEquals("com.example.Bar", result.getTypeName()); assertEquals(bar1.getContentId(), result.getContentId()); // Search by Content ID. // old version of Foo should not be there. result = cache.find(foo1.getContentId()); assertNull(result); result = cache.find(bar1.getContentId()); assertNotNull(result); assertEquals(bar1.getTypeName(), result.getTypeName()); assertEquals(bar1.getContentId(), result.getContentId()); result = cache.find(foo2.getContentId()); assertNotNull(result); assertEquals(foo2.getTypeName(), result.getTypeName()); assertEquals(foo2.getContentId(), result.getContentId()); cache.cleanup(logger); // We didn't write anything, still 1 file. cache.shutdown(); assertNumCacheFiles(unitCacheDir, 1); // keep making more files MockCompilationUnit lastUnit = null; assertTrue(PersistentUnitCache.CACHE_FILE_THRESHOLD > 3); for (int i = 2; i < PersistentUnitCache.CACHE_FILE_THRESHOLD; ++i) { cache = new PersistentUnitCache(logger, cacheDir); lastUnit = new MockCompilationUnit("com.example.Foo", "Foo Source" + i); cache.add(lastUnit); cache.cleanup(logger); cache.shutdown(); assertNumCacheFiles(unitCacheDir, i); } // One last check, we should load the last unit added to the cache. cache = new PersistentUnitCache(logger, cacheDir); result = cache.find(lastUnit.getContentId()); assertNotNull(result); assertEquals("com.example.Foo", result.getTypeName()); assertEquals(lastUnit.getContentId(), result.getContentId()); result = cache.find(bar1.getContentId()); assertNotNull(result); assertEquals("com.example.Bar", result.getTypeName()); assertEquals(bar1.getContentId(), result.getContentId()); result = cache.find("com/example/Foo.java"); assertNotNull(result); assertEquals("com.example.Foo", result.getTypeName()); assertEquals(lastUnit.getContentId(), result.getContentId()); result = cache.find("com/example/Bar.java"); assertNotNull(result); assertEquals("com.example.Bar", result.getTypeName()); assertEquals(bar1.getContentId(), result.getContentId()); lastUnit = new MockCompilationUnit("com.example.Foo", "Foo Source"); cache.add(lastUnit); // This time, the cleanup logic should coalesce the logs into one file // again. cache.cleanup(logger); cache.shutdown(); assertNumCacheFiles(unitCacheDir, 1); } private void assertNumCacheFiles(File unitCacheDir, int expected) { assertEquals(expected, unitCacheDir.list().length); } private void checkInvalidObjectInCache(Object toSerialize) throws IOException, FileNotFoundException, UnableToCompleteException, InterruptedException { TreeLogger logger = TreeLogger.NULL; File cacheDir = lastCacheDir = File.createTempFile("PersistentUnitTest-CNF", ""); File unitCacheDir = mkCacheDir(cacheDir); /* * Create a cache file that has the right filename, but the wrong kind of * object in it. */ File errorFile = new File(unitCacheDir, PersistentUnitCache.CACHE_FILE_PREFIX + "12345"); ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream(errorFile)); os.writeObject(toSerialize); os.close(); assertNumCacheFiles(unitCacheDir, 1); PersistentUnitCache cache = new PersistentUnitCache(logger, cacheDir); cache.cleanup(logger); cache.shutdown(); // The bogus file should have been removed. assertNumCacheFiles(unitCacheDir, 0); } private File mkCacheDir(File cacheDir) { assertNotNull(cacheDir); assertTrue(cacheDir.exists()); cacheDir.delete(); File unitCacheDir = new File(cacheDir, PersistentUnitCache.UNIT_CACHE_PREFIX); unitCacheDir.mkdirs(); return unitCacheDir; } }