| /* |
| * 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; |
| } |
| } |