001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.api.project;
043:
044: import java.io.File;
045: import java.util.Arrays;
046: import java.util.HashSet;
047: import java.util.Set;
048: import javax.swing.event.ChangeListener;
049: import org.netbeans.junit.NbTestCase;
050: import org.netbeans.spi.project.SubprojectProvider;
051: import org.openide.filesystems.FileObject;
052: import org.openide.filesystems.FileUtil;
053: import org.openide.util.Lookup;
054: import org.openide.util.lookup.Lookups;
055: import org.openide.util.test.MockLookup;
056:
057: /**
058: * Test {@link ProjectUtils}.
059: * @author Jesse Glick
060: */
061: public class ProjectUtilsTest extends NbTestCase {
062:
063: static {
064: MockLookup.setInstances(TestUtil.testProjectFactory());
065: }
066:
067: public ProjectUtilsTest(String name) {
068: super (name);
069: }
070:
071: public void testHasSubprojectCycles() throws Exception {
072: // Check static cycle detection.
073: TestProj a = new TestProj("a");
074: assertFalse(
075: "no cycles in a project with no declared subprojects",
076: ProjectUtils.hasSubprojectCycles(a, null));
077: a.subprojs = new Project[0];
078: assertFalse("no cycles in a standalone project", ProjectUtils
079: .hasSubprojectCycles(a, null));
080: TestProj b = new TestProj("b");
081: a.subprojs = new Project[] { b };
082: b.subprojs = new Project[0];
083: assertFalse("no cycles in a -> b", ProjectUtils
084: .hasSubprojectCycles(a, null));
085: TestProj c = new TestProj("c");
086: c.subprojs = new Project[0];
087: b.subprojs = new Project[] { c };
088: assertFalse("no cycles in a -> b -> c", ProjectUtils
089: .hasSubprojectCycles(a, null));
090: TestProj d = new TestProj("d");
091: d.subprojs = new Project[0];
092: b.subprojs = new Project[] { d };
093: c.subprojs = new Project[] { d };
094: assertFalse("no cycles in a -> {b, c} -> d (DAG)", ProjectUtils
095: .hasSubprojectCycles(a, null));
096: a.subprojs = new Project[] { a };
097: assertTrue("self-loop cycle in a -> a", ProjectUtils
098: .hasSubprojectCycles(a, null));
099: a.subprojs = new Project[] { b };
100: b.subprojs = new Project[] { a };
101: assertTrue("simple cycle in a -> b -> a", ProjectUtils
102: .hasSubprojectCycles(a, null));
103: b.subprojs = new Project[] { c };
104: c.subprojs = new Project[] { b };
105: assertTrue(
106: "simple cycle not involing master in a -> b -> c -> b",
107: ProjectUtils.hasSubprojectCycles(a, null));
108: c.subprojs = new Project[] { a };
109: a.subprojs = new Project[] { b, d };
110: d.subprojs = new Project[] { a };
111: assertTrue("multiple cycles in a -> b -> c -> a, a -> d -> a",
112: ProjectUtils.hasSubprojectCycles(a, null));
113: a.subprojs = new Project[0];
114: b.subprojs = new Project[0];
115: assertFalse("no cycle introduced by a -> b in a, b",
116: ProjectUtils.hasSubprojectCycles(a, b));
117: c.subprojs = new Project[0];
118: b.subprojs = new Project[] { c };
119: assertFalse("no cycle introduced by a -> b in a, b -> c",
120: ProjectUtils.hasSubprojectCycles(a, b));
121: a.subprojs = new Project[] { b };
122: assertFalse(
123: "no cycle introduced by no-op a -> b in a -> b -> c",
124: ProjectUtils.hasSubprojectCycles(a, b));
125: assertFalse(
126: "no cycle introduced by direct a -> c in a -> b -> c",
127: ProjectUtils.hasSubprojectCycles(a, c));
128: assertTrue("cycle introduced by a -> a in a -> b -> c",
129: ProjectUtils.hasSubprojectCycles(a, a));
130: assertTrue("cycle introduced by b -> a in a -> b -> c",
131: ProjectUtils.hasSubprojectCycles(b, a));
132: assertTrue("cycle introduced by c -> a in a -> b -> c",
133: ProjectUtils.hasSubprojectCycles(c, a));
134: c.subprojs = null;
135: assertTrue(
136: "cycle introduced by c -> a in a -> b -> c (no explicit subprojects in c)",
137: ProjectUtils.hasSubprojectCycles(c, a));
138: // Performance check.
139: a = new TestProj("a");
140: b = new TestProj("b");
141: c = new TestProj("c");
142: d = new TestProj("d");
143: a.subprojs = new Project[] { b, c };
144: b.subprojs = new Project[] { d };
145: c.subprojs = new Project[] { d };
146: d.subprojs = new Project[] {};
147: assertFalse("diamond, no cycles", ProjectUtils
148: .hasSubprojectCycles(a, null));
149: assertEquals("A asked for subprojects just once", 1, a
150: .getSubprojectsCalled());
151: assertEquals("B asked for subprojects just once", 1, b
152: .getSubprojectsCalled());
153: assertEquals("C asked for subprojects just once", 1, c
154: .getSubprojectsCalled());
155: assertEquals("D asked for subprojects just once", 1, d
156: .getSubprojectsCalled());
157: }
158:
159: public void testGenericSources() throws Exception {
160: clearWorkDir();
161: File topF = new File(getWorkDir(), "top");
162: assertTrue(new File(topF, "testproject").mkdirs());
163: assertTrue(new File(topF, "nested" + File.separator
164: + "testproject").mkdirs());
165: assertTrue(new File(topF, "file").createNewFile());
166: FileObject top = FileUtil.toFileObject(topF);
167: assertNotNull(top);
168: Project p = ProjectManager.getDefault().findProject(top);
169: assertNotNull(p);
170: Sources s = ProjectUtils.getSources(p);
171: SourceGroup[] grps = s.getSourceGroups(Sources.TYPE_GENERIC);
172: assertEquals(1, grps.length);
173: assertEquals(top, grps[0].getRootFolder());
174: assertEquals("top", grps[0].getDisplayName());
175: assertTrue(grps[0].contains(top));
176: FileObject file = top.getFileObject("file");
177: assertNotNull(file);
178: assertTrue(grps[0].contains(file));
179: FileObject nested = top.getFileObject("nested");
180: assertNotNull(nested);
181: assertFalse(grps[0].contains(nested));
182: assertEquals(1, TestUtil.projectLoadCount(top));
183: assertEquals("#67450: did not have to load nested project", 0,
184: TestUtil.projectLoadCount(nested));
185: // XXX could also test contains(...) on unsharable files
186: }
187:
188: /**
189: * Fake project with subprojects.
190: */
191: private static final class TestProj implements Project,
192: SubprojectProvider {
193:
194: private final String name;
195: /**
196: * Subproject list.
197: * Use null to not have a SubprojectProvider at all.
198: */
199: public Project[] subprojs = null;
200: private int getSubprojectsCalled;
201:
202: /**
203: * Create a fake project.
204: * @param name a name for debugging purposes
205: */
206: public TestProj(String name) {
207: this .name = name;
208: }
209:
210: public Lookup getLookup() {
211: if (subprojs == null) {
212: return Lookup.EMPTY;
213: } else {
214: return Lookups.singleton(this );
215: }
216: }
217:
218: public Set<? extends Project> getSubprojects() {
219: getSubprojectsCalled++;
220: assert subprojs != null;
221: return new HashSet<Project>(Arrays.asList(subprojs));
222: }
223:
224: /**
225: * Number of times {@link #getSubprojects} was called since last check.
226: */
227: public int getSubprojectsCalled() {
228: int c = getSubprojectsCalled;
229: getSubprojectsCalled = 0;
230: return c;
231: }
232:
233: public FileObject getProjectDirectory() {
234: // irrelevant
235: return FileUtil.createMemoryFileSystem().getRoot();
236: }
237:
238: public void addChangeListener(ChangeListener l) {
239: }
240:
241: public void removeChangeListener(ChangeListener l) {
242: }
243:
244: public String toString() {
245: return name;
246: }
247:
248: }
249:
250: }
|