| /* |
| * Copyright 2008 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.resource.impl; |
| |
| import com.google.gwt.core.ext.TreeLogger; |
| import com.google.gwt.thirdparty.guava.common.collect.Lists; |
| import com.google.gwt.thirdparty.guava.common.io.Files; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.lang.ref.WeakReference; |
| import java.net.URISyntaxException; |
| import java.util.Collection; |
| import java.util.Map; |
| import java.util.Set; |
| |
| /** |
| * Resource related tests. |
| */ |
| public class ClassPathEntryTest extends AbstractResourceOrientedTestBase { |
| |
| /** |
| * This test will likely not work on Windows since directories that start with . are not |
| * implicitly hidden there. But since Java 6 does not have a File.setHidden() function, fixing |
| * this test for Windows is deferred till GWT officially depends on Java 7. |
| */ |
| public void testIgnoresHiddenDirectories() throws IOException { |
| // Setup a /tmp/.svn/ShouldNotBeFound.java folder structure. |
| File tempDir = Files.createTempDir(); |
| File nestedHiddenDir = new File(tempDir, ".svn"); |
| nestedHiddenDir.mkdir(); |
| File javaFile = new File(nestedHiddenDir, "ShouldNotBeFound.java"); |
| javaFile.createNewFile(); |
| |
| // Perform a class path directory inspection. |
| DirectoryClassPathEntry cpe = new DirectoryClassPathEntry(tempDir); |
| Map<AbstractResource, ResourceResolution> resources = |
| cpe.findApplicableResources(TreeLogger.NULL, createInclusivePathPrefixSet()); |
| |
| // Verify that even though we're using an ALL filter, we still didn't find any files inside the |
| // .svn dir, because we never even enumerate its contents. |
| assertTrue(resources.isEmpty()); |
| } |
| |
| public void testResourceCreated() throws IOException, InterruptedException { |
| // With just 1 filter definition. |
| testResourceCreated(Lists.newArrayList(createInclusivePathPrefixSet())); |
| // With multiple filter definitions. |
| testResourceCreated(Lists.newArrayList(createInclusivePathPrefixSet(), |
| createInclusivePathPrefixSet(), createInclusivePathPrefixSet())); |
| } |
| |
| public void testForResourceListenerLeaks_pathPrefixSetIsCollected() throws Exception { |
| // Create a folder an initially empty folder. |
| PathPrefixSet pathPrefixSet = createInclusivePathPrefixSet(); |
| DirectoryClassPathEntry classPathEntry = new DirectoryClassPathEntry(Files.createTempDir()); |
| |
| // Show that we are not listening. |
| awaitFullGc(); |
| assertEquals(0, ResourceAccumulatorManager.getActiveListenerCount()); |
| |
| // Start listening for updates. |
| ResourceAccumulatorManager.getResources(classPathEntry, pathPrefixSet); |
| |
| // Show that we are now listening for updates. |
| awaitFullGc(); |
| assertEquals(1, ResourceAccumulatorManager.getActiveListenerCount()); |
| |
| // Dereference the pathPrefixSet to give garbage collector an opportunity to clear any weak |
| // references. |
| pathPrefixSet = null; |
| |
| // Show that we are no longer listening for updates. |
| awaitFullGc(); |
| assertEquals(0, ResourceAccumulatorManager.getActiveListenerCount()); |
| |
| // Make sure classPathEntry is not GC'd until this point. |
| assertNotNull(classPathEntry); |
| } |
| |
| public void testForResourceListenerLeaks_classPathEntryIsCollected() throws Exception { |
| // Create a folder an initially empty folder. |
| PathPrefixSet pathPrefixSet = createInclusivePathPrefixSet(); |
| DirectoryClassPathEntry classPathEntry = new DirectoryClassPathEntry(Files.createTempDir()); |
| |
| // Show that we are not listening. |
| awaitFullGc(); |
| assertEquals(0, ResourceAccumulatorManager.getActiveListenerCount()); |
| |
| // Start listening for updates. |
| ResourceAccumulatorManager.getResources(classPathEntry, pathPrefixSet); |
| |
| // Show that we are now listening for updates. |
| awaitFullGc(); |
| assertEquals(1, ResourceAccumulatorManager.getActiveListenerCount()); |
| |
| // Dereference the classPathEntry to give the garbage collector an opportunity to clear any weak |
| // references. |
| classPathEntry = null; |
| |
| // Show that we are no longer listening for updates. |
| awaitFullGc(); |
| assertEquals(0, ResourceAccumulatorManager.getActiveListenerCount()); |
| |
| // Make sure pathPrefixSet is not GC'd until this point. |
| assertNotNull(pathPrefixSet); |
| } |
| |
| public void testResourceCreated(Collection<PathPrefixSet> pathPrefixSets) throws IOException, |
| InterruptedException { |
| // Create a folder an initially empty folder. |
| File tempDir = Files.createTempDir(); |
| DirectoryClassPathEntry cpe = new DirectoryClassPathEntry(tempDir); |
| |
| // Perform a class path directory inspection. |
| for (PathPrefixSet pathPrefixSet : pathPrefixSets) { |
| Map<AbstractResource, ResourceResolution> foundResources = |
| cpe.findApplicableResources(TreeLogger.NULL, pathPrefixSet); |
| |
| // Verify the directory is initially empty. |
| assertTrue(foundResources.isEmpty()); |
| } |
| |
| // Create a file and give file events time to fire. |
| File createdFile = new File(tempDir, "Created.java"); |
| createdFile.createNewFile(); |
| Thread.sleep(10); |
| |
| // Perform a class path directory inspection. |
| for (PathPrefixSet pathPrefixSet : pathPrefixSets) { |
| Map<AbstractResource, ResourceResolution> foundResources = |
| cpe.findApplicableResources(TreeLogger.NULL, pathPrefixSet); |
| |
| // Verify the directory is no longer empty. |
| assertEquals(1, foundResources.size()); |
| assertEquals("Created.java", foundResources.keySet().iterator().next().getPath()); |
| } |
| } |
| |
| public void testResourceDeleted() throws IOException, InterruptedException { |
| // With just 1 filter definition. |
| testResourceDeleted(Lists.newArrayList(createInclusivePathPrefixSet())); |
| // With multiple filter definitions. |
| testResourceDeleted(Lists.newArrayList(createInclusivePathPrefixSet(), |
| createInclusivePathPrefixSet(), createInclusivePathPrefixSet())); |
| } |
| |
| private void testResourceDeleted(Collection<PathPrefixSet> pathPrefixSets) throws IOException, |
| InterruptedException { |
| // Create a folder with one initial file, that can be deleted. |
| File tempDir = Files.createTempDir(); |
| File fileToDelete = new File(tempDir, "ToDelete.java"); |
| fileToDelete.createNewFile(); |
| DirectoryClassPathEntry cpe = new DirectoryClassPathEntry(tempDir); |
| |
| // Perform a class path directory inspection. |
| for (PathPrefixSet pathPrefixSet : pathPrefixSets) { |
| Map<AbstractResource, ResourceResolution> foundResources = |
| cpe.findApplicableResources(TreeLogger.NULL, pathPrefixSet); |
| |
| // Verify the directory is not initially empty. |
| assertEquals(1, foundResources.size()); |
| assertEquals("ToDelete.java", foundResources.keySet().iterator().next().getPath()); |
| } |
| |
| // Delete the file and give file events time to fire. |
| fileToDelete.delete(); |
| Thread.sleep(10); |
| |
| // Perform a class path directory inspection. |
| for (PathPrefixSet pathPrefixSet : pathPrefixSets) { |
| Map<AbstractResource, ResourceResolution> foundResources = |
| cpe.findApplicableResources(TreeLogger.NULL, pathPrefixSet); |
| |
| // Verify the directory is now empty. |
| assertTrue(foundResources.isEmpty()); |
| } |
| } |
| |
| public void testResourceRenamed() throws IOException, InterruptedException { |
| // With just 1 filter definition. |
| testResourceRenamed(Lists.newArrayList(createInclusivePathPrefixSet())); |
| // With multiple filter definitions. |
| testResourceRenamed(Lists.newArrayList(createInclusivePathPrefixSet(), |
| createInclusivePathPrefixSet(), createInclusivePathPrefixSet())); |
| } |
| |
| private void testResourceRenamed(Collection<PathPrefixSet> pathPrefixSets) throws IOException, |
| InterruptedException { |
| // Create a folder with one initial file, that can be renamed. |
| File tempDir = Files.createTempDir(); |
| File fileToRename = new File(tempDir, "ToRename.java"); |
| fileToRename.createNewFile(); |
| DirectoryClassPathEntry cpe = new DirectoryClassPathEntry(tempDir); |
| |
| // Perform class path directory inspections. |
| for (PathPrefixSet pathPrefixSet : pathPrefixSets) { |
| Map<AbstractResource, ResourceResolution> foundResources = |
| cpe.findApplicableResources(TreeLogger.NULL, pathPrefixSet); |
| |
| // Verify the directory is not initially empty. |
| assertEquals(1, foundResources.size()); |
| assertEquals("ToRename.java", foundResources.keySet().iterator().next().getPath()); |
| } |
| |
| // Rename the file and give file events time to fire. |
| fileToRename.renameTo(new File(tempDir, "Renamed.java")); |
| Thread.sleep(10); |
| |
| for (PathPrefixSet pathPrefixSet : pathPrefixSets) { |
| // Perform a class path directory inspection. |
| Map<AbstractResource, ResourceResolution> foundResources = |
| cpe.findApplicableResources(TreeLogger.NULL, pathPrefixSet); |
| |
| // Verify the file is seen as renamed. |
| assertEquals(1, foundResources.size()); |
| assertEquals("Renamed.java", foundResources.keySet().iterator().next().getPath()); |
| } |
| } |
| |
| public void testAllCpe1FilesFound() throws URISyntaxException, IOException { |
| testAllCpe1FilesFound(getClassPathEntry1AsJar()); |
| testAllCpe1FilesFound(getClassPathEntry1AsDirectory()); |
| testAllCpe1FilesFound(getClassPathEntry1AsZip()); |
| } |
| |
| public void testAllCpe2FilesFound() throws URISyntaxException, IOException { |
| testAllCpe2FilesFound(getClassPathEntry2AsJar()); |
| testAllCpe2FilesFound(getClassPathEntry2AsDirectory()); |
| testAllCpe2FilesFound(getClassPathEntry2AsZip()); |
| } |
| |
| public void testPathPrefixSetChanges() throws IOException, URISyntaxException { |
| ClassPathEntry cpe1jar = getClassPathEntry1AsJar(); |
| ClassPathEntry cpe1dir = getClassPathEntry1AsDirectory(); |
| ClassPathEntry cpe1zip = getClassPathEntry1AsZip(); |
| ClassPathEntry cpe2jar = getClassPathEntry2AsJar(); |
| ClassPathEntry cpe2dir = getClassPathEntry2AsDirectory(); |
| ClassPathEntry cpe2zip = getClassPathEntry2AsZip(); |
| |
| testPathPrefixSetChanges(cpe1jar, cpe2jar); |
| testPathPrefixSetChanges(cpe1dir, cpe2jar); |
| testPathPrefixSetChanges(cpe1zip, cpe2jar); |
| |
| testPathPrefixSetChanges(cpe1dir, cpe2dir); |
| testPathPrefixSetChanges(cpe1jar, cpe2dir); |
| testPathPrefixSetChanges(cpe1zip, cpe2dir); |
| |
| testPathPrefixSetChanges(cpe1dir, cpe2zip); |
| testPathPrefixSetChanges(cpe1jar, cpe2zip); |
| testPathPrefixSetChanges(cpe1zip, cpe2zip); |
| } |
| |
| public void testUseOfPrefixesWithFiltering() throws IOException, |
| URISyntaxException { |
| ClassPathEntry cpe1jar = getClassPathEntry1AsJar(); |
| ClassPathEntry cpe1dir = getClassPathEntry1AsDirectory(); |
| ClassPathEntry cpe1zip = getClassPathEntry1AsZip(); |
| ClassPathEntry cpe2jar = getClassPathEntry2AsJar(); |
| ClassPathEntry cpe2dir = getClassPathEntry2AsDirectory(); |
| ClassPathEntry cpe2zip = getClassPathEntry2AsZip(); |
| |
| testUseOfPrefixesWithFiltering(cpe1dir, cpe2jar); |
| testUseOfPrefixesWithFiltering(cpe1jar, cpe2jar); |
| testUseOfPrefixesWithFiltering(cpe1dir, cpe2jar); |
| testUseOfPrefixesWithFiltering(cpe1zip, cpe2jar); |
| |
| testUseOfPrefixesWithFiltering(cpe1dir, cpe2dir); |
| testUseOfPrefixesWithFiltering(cpe1jar, cpe2dir); |
| testUseOfPrefixesWithFiltering(cpe1zip, cpe2dir); |
| |
| testUseOfPrefixesWithFiltering(cpe1dir, cpe2zip); |
| testUseOfPrefixesWithFiltering(cpe1jar, cpe2zip); |
| testUseOfPrefixesWithFiltering(cpe1zip, cpe2zip); |
| } |
| |
| public void testUseOfPrefixesWithoutFiltering() throws URISyntaxException, |
| IOException { |
| ClassPathEntry cpe1jar = getClassPathEntry1AsJar(); |
| ClassPathEntry cpe1dir = getClassPathEntry1AsDirectory(); |
| ClassPathEntry cpe1zip = getClassPathEntry1AsZip(); |
| ClassPathEntry cpe2jar = getClassPathEntry2AsJar(); |
| ClassPathEntry cpe2dir = getClassPathEntry2AsDirectory(); |
| ClassPathEntry cpe2zip = getClassPathEntry2AsZip(); |
| |
| testUseOfPrefixesWithoutFiltering(cpe1dir, cpe2jar); |
| testUseOfPrefixesWithoutFiltering(cpe1jar, cpe2jar); |
| testUseOfPrefixesWithoutFiltering(cpe1dir, cpe2jar); |
| testUseOfPrefixesWithoutFiltering(cpe1zip, cpe2jar); |
| |
| testUseOfPrefixesWithoutFiltering(cpe1dir, cpe2dir); |
| testUseOfPrefixesWithoutFiltering(cpe1jar, cpe2dir); |
| testUseOfPrefixesWithoutFiltering(cpe1zip, cpe2dir); |
| |
| testUseOfPrefixesWithoutFiltering(cpe1dir, cpe2zip); |
| testUseOfPrefixesWithoutFiltering(cpe1jar, cpe2zip); |
| testUseOfPrefixesWithoutFiltering(cpe1zip, cpe2zip); |
| } |
| |
| public void testUseOfPrefixesWithoutFiltering(ClassPathEntry cpe1, |
| ClassPathEntry cpe2) { |
| TreeLogger logger = createTestTreeLogger(); |
| |
| PathPrefixSet pps = new PathPrefixSet(); |
| pps.add(new PathPrefix("com/google/gwt/user/client/", null)); |
| pps.add(new PathPrefix("com/google/gwt/i18n/client/", null)); |
| |
| { |
| // Examine cpe1. |
| Set<AbstractResource> r = cpe1.findApplicableResources(logger, pps).keySet(); |
| |
| assertEquals(3, r.size()); |
| assertPathIncluded(r, "com/google/gwt/user/client/Command.java"); |
| assertPathIncluded(r, "com/google/gwt/user/client/Timer.java"); |
| assertPathIncluded(r, "com/google/gwt/user/client/ui/Widget.java"); |
| } |
| |
| { |
| // Examine cpe2. |
| Set<AbstractResource> r = cpe2.findApplicableResources(logger, pps).keySet(); |
| |
| assertEquals(1, r.size()); |
| assertPathIncluded(r, "com/google/gwt/i18n/client/Messages.java"); |
| } |
| } |
| |
| private static void awaitFullGc() throws InterruptedException { |
| Object object = new Object(); |
| WeakReference<Object> objectReference = new WeakReference<Object>(object); |
| object = null; |
| System.gc(); |
| |
| while (objectReference.get() != null) { |
| Thread.sleep(10); |
| System.gc(); |
| } |
| } |
| |
| // NOTE: if this test fails, ensure that the source root containing this very |
| // source file is *FIRST* on the classpath |
| private void testAllCpe1FilesFound(ClassPathEntry cpe1) { |
| TreeLogger logger = createTestTreeLogger(); |
| |
| PathPrefixSet pps = new PathPrefixSet(); |
| pps.add(new PathPrefix("", null)); |
| |
| Set<AbstractResource> r = cpe1.findApplicableResources(logger, pps).keySet(); |
| |
| assertEquals(9, r.size()); |
| assertPathIncluded(r, "com/google/gwt/user/User.gwt.xml"); |
| assertPathIncluded(r, "com/google/gwt/user/client/Command.java"); |
| assertPathIncluded(r, "com/google/gwt/user/client/Timer.java"); |
| assertPathIncluded(r, "com/google/gwt/user/client/ui/Widget.java"); |
| assertPathIncluded(r, "org/example/bar/client/BarClient1.txt"); |
| assertPathIncluded(r, "org/example/bar/client/BarClient2.txt"); |
| assertPathIncluded(r, "org/example/bar/client/etc/BarEtc.txt"); |
| assertPathIncluded(r, "org/example/foo/client/FooClient.java"); |
| assertPathIncluded(r, "org/example/foo/server/FooServer.java"); |
| } |
| |
| // NOTE: if this test fails, ensure that the source root containing this very |
| // source file is on the classpath |
| private void testAllCpe2FilesFound(ClassPathEntry cpe2) { |
| TreeLogger logger = createTestTreeLogger(); |
| |
| PathPrefixSet pps = createInclusivePathPrefixSet(); |
| Set<AbstractResource> r = cpe2.findApplicableResources(logger, pps).keySet(); |
| |
| assertEquals(6, r.size()); |
| assertPathIncluded(r, "com/google/gwt/i18n/I18N.gwt.xml"); |
| assertPathIncluded(r, "com/google/gwt/i18n/client/Messages.java"); |
| assertPathIncluded(r, |
| "com/google/gwt/i18n/rebind/LocalizableGenerator.java"); |
| assertPathIncluded(r, "org/example/bar/client/BarClient2.txt"); |
| assertPathIncluded(r, "org/example/bar/client/BarClient3.txt"); |
| assertPathIncluded(r, "org/example/foo/client/BarClient1.txt"); |
| } |
| |
| private void testPathPrefixSetChanges(ClassPathEntry cpe1, ClassPathEntry cpe2) { |
| TreeLogger logger = createTestTreeLogger(); |
| |
| { |
| // Filter is not set yet. |
| PathPrefixSet pps = new PathPrefixSet(); |
| pps.add(new PathPrefix("com/google/gwt/user/", null)); |
| pps.add(new PathPrefix("com/google/gwt/i18n/", null)); |
| |
| // Examine cpe1 in the absence of the filter. |
| Set<AbstractResource> r1 = cpe1.findApplicableResources(logger, pps).keySet(); |
| |
| assertEquals(4, r1.size()); |
| assertPathIncluded(r1, "com/google/gwt/user/User.gwt.xml"); |
| assertPathIncluded(r1, "com/google/gwt/user/client/Command.java"); |
| assertPathIncluded(r1, "com/google/gwt/user/client/Timer.java"); |
| assertPathIncluded(r1, "com/google/gwt/user/client/ui/Widget.java"); |
| |
| // Examine cpe2 in the absence of the filter. |
| Set<AbstractResource> r2 = cpe2.findApplicableResources(logger, pps).keySet(); |
| |
| assertEquals(3, r2.size()); |
| assertPathIncluded(r2, "com/google/gwt/i18n/I18N.gwt.xml"); |
| assertPathIncluded(r2, "com/google/gwt/i18n/client/Messages.java"); |
| assertPathIncluded(r2, |
| "com/google/gwt/i18n/rebind/LocalizableGenerator.java"); |
| } |
| |
| { |
| // Create a pps with a filter. |
| ResourceFilter excludeXmlFiles = new ResourceFilter() { |
| @Override |
| public boolean allows(String path) { |
| return !path.endsWith(".xml"); |
| } |
| }; |
| |
| PathPrefixSet pps = new PathPrefixSet(); |
| pps.add(new PathPrefix("com/google/gwt/user/", excludeXmlFiles)); |
| pps.add(new PathPrefix("com/google/gwt/i18n/", excludeXmlFiles)); |
| |
| // Examine cpe1 in the presence of the filter. |
| Set<AbstractResource> r1 = cpe1.findApplicableResources(logger, pps).keySet(); |
| |
| assertEquals(3, r1.size()); |
| assertPathNotIncluded(r1, "com/google/gwt/user/User.gwt.xml"); |
| assertPathIncluded(r1, "com/google/gwt/user/client/Command.java"); |
| assertPathIncluded(r1, "com/google/gwt/user/client/Timer.java"); |
| assertPathIncluded(r1, "com/google/gwt/user/client/ui/Widget.java"); |
| |
| // Examine cpe2 in the presence of the filter. |
| Set<AbstractResource> r2 = cpe2.findApplicableResources(logger, pps).keySet(); |
| |
| assertEquals(2, r2.size()); |
| assertPathNotIncluded(r1, "com/google/gwt/user/User.gwt.xml"); |
| assertPathIncluded(r2, "com/google/gwt/i18n/client/Messages.java"); |
| assertPathIncluded(r2, |
| "com/google/gwt/i18n/rebind/LocalizableGenerator.java"); |
| } |
| |
| { |
| /* |
| * Change the prefix path set to the zero-lenth prefix (which allows |
| * everything), but specify a filter that disallows everything. |
| */ |
| PathPrefixSet pps = new PathPrefixSet(); |
| pps.add(new PathPrefix("", new ResourceFilter() { |
| @Override |
| public boolean allows(String path) { |
| // Exclude everything. |
| return false; |
| } |
| })); |
| |
| // Examine cpe1 in the presence of the filter. |
| Set<AbstractResource> r1 = cpe1.findApplicableResources(logger, pps).keySet(); |
| |
| assertEquals(0, r1.size()); |
| |
| // Examine cpe2 in the presence of the filter. |
| Set<AbstractResource> r2 = cpe2.findApplicableResources(logger, pps).keySet(); |
| |
| assertEquals(0, r2.size()); |
| } |
| } |
| |
| private void testUseOfPrefixesWithFiltering(ClassPathEntry cpe1, |
| ClassPathEntry cpe2) { |
| TreeLogger logger = createTestTreeLogger(); |
| |
| PathPrefixSet pps = new PathPrefixSet(); |
| ResourceFilter excludeXmlFiles = new ResourceFilter() { |
| @Override |
| public boolean allows(String path) { |
| return !path.endsWith(".xml"); |
| } |
| }; |
| // The prefix is intentionally starting at the module-level, not 'client'. |
| pps.add(new PathPrefix("com/google/gwt/user/", excludeXmlFiles)); |
| pps.add(new PathPrefix("com/google/gwt/i18n/", excludeXmlFiles)); |
| |
| { |
| // Examine cpe1. |
| Set<AbstractResource> r = cpe1.findApplicableResources(logger, pps).keySet(); |
| |
| assertEquals(3, r.size()); |
| // User.gwt.xml would be included but for the filter. |
| assertPathIncluded(r, "com/google/gwt/user/client/Command.java"); |
| assertPathIncluded(r, "com/google/gwt/user/client/Timer.java"); |
| assertPathIncluded(r, "com/google/gwt/user/client/ui/Widget.java"); |
| } |
| |
| { |
| // Examine cpe2. |
| Set<AbstractResource> r = cpe2.findApplicableResources(logger, pps).keySet(); |
| |
| assertEquals(2, r.size()); |
| // I18N.gwt.xml would be included but for the filter. |
| assertPathIncluded(r, "com/google/gwt/i18n/client/Messages.java"); |
| assertPathIncluded(r, |
| "com/google/gwt/i18n/rebind/LocalizableGenerator.java"); |
| } |
| } |
| |
| private static PathPrefixSet createInclusivePathPrefixSet() { |
| PathPrefixSet pathPrefixes = new PathPrefixSet(); |
| pathPrefixes.add(new PathPrefix("", null)); |
| return pathPrefixes; |
| } |
| } |