blob: 0f032d8436334752b40f03363117b6df9bcf4e5a [file] [log] [blame]
/*
* 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;
}
}