| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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 org.netbeans.api.project; |
| |
| import java.beans.PropertyChangeListener; |
| import java.io.File; |
| import java.io.IOException; |
| import java.util.Arrays; |
| import java.util.HashSet; |
| import java.util.Set; |
| import javax.swing.Icon; |
| import javax.swing.event.ChangeListener; |
| import org.netbeans.junit.NbTestCase; |
| import org.netbeans.junit.RandomlyFails; |
| import org.netbeans.spi.project.CacheDirectoryProvider; |
| import org.netbeans.spi.project.ProjectInformationProvider; |
| import org.netbeans.spi.project.SubprojectProvider; |
| import org.openide.filesystems.FileObject; |
| import org.openide.filesystems.FileUtil; |
| import org.openide.util.Lookup; |
| import org.openide.util.lookup.Lookups; |
| import org.openide.util.test.MockLookup; |
| |
| /** |
| * Test {@link ProjectUtils}. |
| * @author Jesse Glick |
| */ |
| public class ProjectUtilsTest extends NbTestCase { |
| |
| static { |
| MockLookup.setInstances( |
| TestUtil.testProjectFactory(), |
| new ProjectInformationProviderImpl()); |
| } |
| |
| public ProjectUtilsTest(String name) { |
| super(name); |
| } |
| |
| public void testHasSubprojectCycles() throws Exception { |
| // Check static cycle detection. |
| TestProj a = new TestProj("a"); |
| assertFalse("no cycles in a project with no declared subprojects", ProjectUtils.hasSubprojectCycles(a, null)); |
| a.subprojs = new Project[0]; |
| assertFalse("no cycles in a standalone project", ProjectUtils.hasSubprojectCycles(a, null)); |
| TestProj b = new TestProj("b"); |
| a.subprojs = new Project[] {b}; |
| b.subprojs = new Project[0]; |
| assertFalse("no cycles in a -> b", ProjectUtils.hasSubprojectCycles(a, null)); |
| TestProj c = new TestProj("c"); |
| c.subprojs = new Project[0]; |
| b.subprojs = new Project[] {c}; |
| assertFalse("no cycles in a -> b -> c", ProjectUtils.hasSubprojectCycles(a, null)); |
| TestProj d = new TestProj("d"); |
| d.subprojs = new Project[0]; |
| b.subprojs = new Project[] {d}; |
| c.subprojs = new Project[] {d}; |
| assertFalse("no cycles in a -> {b, c} -> d (DAG)", ProjectUtils.hasSubprojectCycles(a, null)); |
| a.subprojs = new Project[] {a}; |
| assertTrue("self-loop cycle in a -> a", ProjectUtils.hasSubprojectCycles(a, null)); |
| a.subprojs = new Project[] {b}; |
| b.subprojs = new Project[] {a}; |
| assertTrue("simple cycle in a -> b -> a", ProjectUtils.hasSubprojectCycles(a, null)); |
| b.subprojs = new Project[] {c}; |
| c.subprojs = new Project[] {b}; |
| assertTrue("simple cycle not involing master in a -> b -> c -> b", ProjectUtils.hasSubprojectCycles(a, null)); |
| c.subprojs = new Project[] {a}; |
| a.subprojs = new Project[] {b, d}; |
| d.subprojs = new Project[] {a}; |
| assertTrue("multiple cycles in a -> b -> c -> a, a -> d -> a", ProjectUtils.hasSubprojectCycles(a, null)); |
| a.subprojs = new Project[0]; |
| b.subprojs = new Project[0]; |
| assertFalse("no cycle introduced by a -> b in a, b", ProjectUtils.hasSubprojectCycles(a, b)); |
| c.subprojs = new Project[0]; |
| b.subprojs = new Project[] {c}; |
| assertFalse("no cycle introduced by a -> b in a, b -> c", ProjectUtils.hasSubprojectCycles(a, b)); |
| a.subprojs = new Project[] {b}; |
| assertFalse("no cycle introduced by no-op a -> b in a -> b -> c", ProjectUtils.hasSubprojectCycles(a, b)); |
| assertFalse("no cycle introduced by direct a -> c in a -> b -> c", ProjectUtils.hasSubprojectCycles(a, c)); |
| assertTrue("cycle introduced by a -> a in a -> b -> c", ProjectUtils.hasSubprojectCycles(a, a)); |
| assertTrue("cycle introduced by b -> a in a -> b -> c", ProjectUtils.hasSubprojectCycles(b, a)); |
| assertTrue("cycle introduced by c -> a in a -> b -> c", ProjectUtils.hasSubprojectCycles(c, a)); |
| c.subprojs = null; |
| assertTrue("cycle introduced by c -> a in a -> b -> c (no explicit subprojects in c)", ProjectUtils.hasSubprojectCycles(c, a)); |
| // Performance check. |
| a = new TestProj("a"); |
| b = new TestProj("b"); |
| c = new TestProj("c"); |
| d = new TestProj("d"); |
| a.subprojs = new Project[] {b, c}; |
| b.subprojs = new Project[] {d}; |
| c.subprojs = new Project[] {d}; |
| d.subprojs = new Project[] {}; |
| assertFalse("diamond, no cycles", ProjectUtils.hasSubprojectCycles(a, null)); |
| assertEquals("A asked for subprojects just once", 1, a.getSubprojectsCalled()); |
| assertEquals("B asked for subprojects just once", 1, b.getSubprojectsCalled()); |
| assertEquals("C asked for subprojects just once", 1, c.getSubprojectsCalled()); |
| assertEquals("D asked for subprojects just once", 1, d.getSubprojectsCalled()); |
| } |
| |
| @RandomlyFails // http://deadlock.netbeans.org/job/NB-Core-Build/9880/testReport/ |
| public void testGenericSources() throws Exception { |
| clearWorkDir(); |
| File topF = new File(getWorkDir(), "top"); |
| assertTrue(new File(topF, "testproject").mkdirs()); |
| assertTrue(new File(topF, "nested" + File.separator + "testproject").mkdirs()); |
| assertTrue(new File(topF, "file").createNewFile()); |
| FileObject top = FileUtil.toFileObject(topF); |
| assertNotNull(top); |
| Project p = ProjectManager.getDefault().findProject(top); |
| assertNotNull(p); |
| Sources s = ProjectUtils.getSources(p); |
| SourceGroup[] grps = s.getSourceGroups(Sources.TYPE_GENERIC); |
| assertEquals(1, grps.length); |
| assertEquals(top, grps[0].getRootFolder()); |
| assertEquals("top", grps[0].getDisplayName()); |
| assertTrue(grps[0].contains(top)); |
| FileObject file = top.getFileObject("file"); |
| assertNotNull(file); |
| assertTrue(grps[0].contains(file)); |
| FileObject nested = top.getFileObject("nested"); |
| assertNotNull(nested); |
| assertFalse(grps[0].contains(nested)); |
| assertEquals(1, TestUtil.projectLoadCount(top)); |
| assertEquals("#67450: did not have to load nested project", 0, TestUtil.projectLoadCount(nested)); |
| // XXX could also test contains(...) on unsharable files |
| } |
| |
| /** |
| * Fake project with subprojects. |
| */ |
| private static final class TestProj implements Project, SubprojectProvider { |
| |
| private final String name; |
| /** |
| * Subproject list. |
| * Use null to not have a SubprojectProvider at all. |
| */ |
| public Project[] subprojs = null; |
| private int getSubprojectsCalled; |
| |
| /** |
| * Create a fake project. |
| * @param name a name for debugging purposes |
| */ |
| public TestProj(String name) { |
| this.name = name; |
| } |
| |
| public Lookup getLookup() { |
| if (subprojs == null) { |
| return Lookup.EMPTY; |
| } else { |
| return Lookups.singleton(this); |
| } |
| } |
| |
| public Set<? extends Project> getSubprojects() { |
| getSubprojectsCalled++; |
| assert subprojs != null; |
| return new HashSet<Project>(Arrays.asList(subprojs)); |
| } |
| |
| /** |
| * Number of times {@link #getSubprojects} was called since last check. |
| */ |
| public int getSubprojectsCalled() { |
| int c = getSubprojectsCalled; |
| getSubprojectsCalled = 0; |
| return c; |
| } |
| |
| public FileObject getProjectDirectory() { |
| // irrelevant |
| return FileUtil.createMemoryFileSystem().getRoot(); |
| } |
| |
| public void addChangeListener(ChangeListener l) {} |
| |
| public void removeChangeListener(ChangeListener l) {} |
| |
| public @Override String toString() { |
| return name; |
| } |
| |
| } |
| |
| public void testGetCacheDirectory() throws Exception { |
| final FileObject pdir = FileUtil.createMemoryFileSystem().getRoot().createFolder("foo"); |
| final ProjectInformation info = new ProjectInformation() { |
| public String getName() { |
| return "/foo/project"; |
| } |
| public String getDisplayName() { |
| return getName(); |
| } |
| public Icon getIcon() { |
| return null; |
| } |
| public Project getProject() { |
| return null; |
| } |
| public void addPropertyChangeListener(PropertyChangeListener listener) {} |
| public void removePropertyChangeListener(PropertyChangeListener listener) {} |
| }; |
| Project p = new Project() { |
| public FileObject getProjectDirectory() { |
| return pdir; |
| } |
| public Lookup getLookup() { |
| return Lookups.fixed(info); |
| } |
| }; |
| FileObject d = ProjectUtils.getCacheDirectory(p, Object.class); |
| assertEquals(FileUtil.getConfigRoot().getFileSystem(), d.getFileSystem()); |
| assertEquals("Projects/extra/_foo_project-00018cc6/java-lang", d.getPath()); |
| d = ProjectUtils.getCacheDirectory(p, Object.class); |
| assertEquals("Projects/extra/_foo_project-00018cc6/java-lang", d.getPath()); |
| final FileObject cache = FileUtil.createMemoryFileSystem().getRoot(); |
| p = new Project() { |
| public FileObject getProjectDirectory() { |
| return pdir; |
| } |
| public Lookup getLookup() { |
| return Lookups.fixed(info, new CacheDirectoryProvider() { |
| public FileObject getCacheDirectory() throws IOException { |
| return cache; |
| } |
| }); |
| } |
| }; |
| d = ProjectUtils.getCacheDirectory(p, Object.class); |
| assertEquals("java-lang", d.getNameExt()); |
| assertEquals(cache, d.getParent()); |
| d = ProjectUtils.getCacheDirectory(p, Object.class); |
| assertEquals("java-lang", d.getNameExt()); |
| assertEquals(cache, d.getParent()); |
| } |
| |
| private static class ProjectInformationProviderImpl implements ProjectInformationProvider { |
| @Override |
| public ProjectInformation getProjectInformation(final Project project) { |
| ProjectInformation info = project.getLookup().lookup(ProjectInformation.class); |
| if (info == null) { |
| info = new ProjectInformation() { |
| @Override |
| public String getName() { |
| return project.getProjectDirectory().getName(); |
| } |
| |
| @Override |
| public String getDisplayName() { |
| return getName(); |
| } |
| |
| @Override |
| public Icon getIcon() { |
| return null; |
| } |
| |
| @Override |
| public Project getProject() { |
| return project; |
| } |
| |
| @Override |
| public void addPropertyChangeListener(PropertyChangeListener listener) { |
| } |
| |
| @Override |
| public void removePropertyChangeListener(PropertyChangeListener listener) { |
| } |
| }; |
| } |
| return info; |
| } |
| } |
| } |