blob: 0ce5448e48e908de6183d10497489716cca10314 [file] [log] [blame]
/*
* 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.awt.Component;
import java.awt.Graphics;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.util.Arrays;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.WeakHashMap;
import java.util.logging.Logger;
import javax.swing.Icon;
import junit.framework.Assert;
import org.netbeans.junit.NbTestCase;
import org.netbeans.spi.project.ProjectFactory;
import org.netbeans.spi.project.ProjectFactory2;
import org.netbeans.spi.project.ProjectState;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileUtil;
import org.openide.filesystems.URLMapper;
import org.openide.util.Lookup;
import org.openide.util.test.MockLookup;
/**
* Help set up org.netbeans.api.project.*Test.
* @author Jesse Glick
*/
public final class TestUtil {
private TestUtil() {}
/**
* Create a scratch directory for tests.
* Will be in /tmp or whatever, and will be empty.
* If you just need a java.io.File use clearWorkDir + getWorkDir.
*/
public static FileObject makeScratchDir(NbTestCase test) throws IOException {
test.clearWorkDir();
File root = test.getWorkDir();
assert root.isDirectory() : root;
assert root.list().length == 0 : Arrays.toString(root.list());
MockLookup.init(); // URLMapper asks for default lookup
FileObject fo = FileUtil.toFileObject(root);
Assert.assertNotNull(
"No FileObject for " + root + " found.\n" +
"Maybe you need ${openide/masterfs.dir}/modules/org-netbeans-modules-masterfs.jar\n" +
"in test.unit.run.cp.extra, or make sure Lookups.metaInfServices is included in Lookup.default, so that\n" +
"Lookup.default<URLMapper>=" + Lookup.getDefault().lookupAll(URLMapper.class) + " includes MasterURLMapper\n" +
"e.g. by using TestUtil.setLookup(Object[]) rather than TestUtil.setLookup(Lookup).",
fo);
return fo;
}
/**
* Delete a file and all subfiles.
*/
public static void deleteRec(File f) throws IOException {
if (f.isDirectory()) {
File[] kids = f.listFiles();
if (kids == null) {
throw new IOException("List " + f);
}
for (File kid : kids) {
deleteRec(kid);
}
}
if (!f.delete()) {
throw new IOException("Delete " + f);
}
}
/**
* Create a testing project factory which recognizes directories containing
* a subdirectory called "testproject".
* If that subdirectory contains a file named "broken" then loading the project
* will fail with an IOException.
*/
public static ProjectFactory testProjectFactory() {
return new TestProjectFactory();
}
/**
* Try to force a GC.
*/
public static void gc() {
System.gc();
System.runFinalization();
System.gc();
}
private static final Map<FileObject,Integer> loadCount = new WeakHashMap<FileObject,Integer>();
/**
* Check how many times {@link ProjectFactory#loadProject} has been called
* (with any outcome) on a given project directory.
*/
public static int projectLoadCount(FileObject dir) {
Integer i = loadCount.get(dir);
if (i != null) {
return i;
} else {
return 0;
}
}
/**
* Mark a test project to fail with a given error when it is next saved.
* The error only applies to the next save, not subsequent ones.
* @param p a test project
* @param error an error to throw (IOException or Error or RuntimeException),
* or null if it should succeed
*/
public static void setProjectSaveWillFail(Project p, Throwable error) {
((TestProject)p).error = error;
}
/**
* Get the number of times a test project was successfully saved with no error.
* @param p a test project
* @return the save count
*/
public static int projectSaveCount(Project p) {
return ((TestProject)p).saveCount;
}
/**
* Mark a test project as modified.
* @param p a test project
*/
public static void modify(Project p) {
((TestProject)p).state.markModified();
}
/**
* Mark a test project as modified.
* @param p a test project
*/
public static void notifyDeleted(Project p) {
((TestProject)p).state.notifyDeleted();
}
/**If non-null, use the value as the Lookup for newly created projects.
*
*/
public static Lookup LOOKUP = null;
public static final String TEST_PROJECT_TYPE = "test.project.type";
public static final Icon TEST_PROJECT_ICON = new Icon() {
@Override
public void paintIcon(Component c, Graphics g, int x, int y) {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public int getIconWidth() {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public int getIconHeight() {
throw new UnsupportedOperationException("Not supported yet.");
}
};
private static final class TestProjectFactory implements ProjectFactory, ProjectFactory2 {
TestProjectFactory() {}
public Project loadProject(FileObject projectDirectory, ProjectState state) throws IOException {
Integer i = loadCount.get(projectDirectory);
if (i == null) {
i = 1;
} else {
i = i + 1;
}
loadCount.put(projectDirectory, i);
FileObject testproject = projectDirectory.getFileObject("testproject");
if (testproject != null && testproject.isFolder()) {
if (testproject.getFileObject("broken") != null) {
throw new IOException("Load failed of " + projectDirectory);
} else {
return new TestProject(projectDirectory, LOOKUP != null ? LOOKUP : Lookup.EMPTY, state);
}
} else {
return null;
}
}
public void saveProject(Project project) throws IOException, ClassCastException {
TestProject p = (TestProject)project;
Throwable t = p.error;
if (t != null) {
p.error = null;
if (t instanceof IOException) {
throw (IOException)t;
} else if (t instanceof Error) {
throw (Error)t;
} else {
throw (RuntimeException)t;
}
}
p.saveCount++;
}
public boolean isProject(FileObject dir) {
FileObject testproject = dir.getFileObject("testproject");
return testproject != null && testproject.isFolder();
}
@Override
public ProjectManager.Result isProject2(FileObject dir) {
FileObject testproject = dir.getFileObject("testproject");
if(testproject != null && testproject.isFolder() && testproject.getFileObject("broken") != null) {
return new ProjectManager.Result(TEST_PROJECT_ICON);
}
if(testproject != null && testproject.isFolder()) {
return new ProjectManager.Result(
dir.getName(),
TEST_PROJECT_TYPE,
TEST_PROJECT_ICON);
}
return null;
}
}
private static final class TestProject implements Project {
private final FileObject dir;
private final Lookup lookup;
final ProjectState state;
Throwable error;
int saveCount = 0;
public TestProject(FileObject dir, Lookup lookup, ProjectState state) {
this.dir = dir;
this.lookup = lookup;
this.state = state;
}
public Lookup getLookup() {
return lookup;
}
public FileObject getProjectDirectory() {
return dir;
}
public String toString() {
return "testproject:" + getProjectDirectory().getNameExt();
}
/* Probably unnecessary to have a ProjectInformation here:
public String getName() {
return "testproject:" + getProjectDirectory().getNameExt();
}
public String getDisplayName() {
return "Test Project in " + getProjectDirectory().getNameExt();
}
public Image getIcon() {
return null;
}
public void addPropertyChangeListener(PropertyChangeListener listener) {}
public void removePropertyChangeListener(PropertyChangeListener listener) {}
*/
}
/**
* Open a URL of content (for example from {@link Class#getResource}) and copy it to a named file.
* The new file can be given as a parent directory plus a relative (slash-separated) path.
* The file may not already exist, but intermediate directories may or may not.
* If the content URL is null, the file is just created, no more; if it already existed
* it is touched (timestamp updated) and its contents are cleared.
* @return the file object
*/
public static FileObject createFileFromContent(URL content, FileObject parent, String path) throws IOException {
if (parent == null) {
throw new IllegalArgumentException("null parent");
}
Assert.assertTrue("folder", parent.isFolder());
FileObject fo = parent;
StringTokenizer tok = new StringTokenizer(path, "/");
boolean touch = false;
while (tok.hasMoreTokens()) {
Assert.assertNotNull("fo is null (parent=" + parent + " path=" + path + ")", fo);
String name = tok.nextToken();
if (tok.hasMoreTokens()) {
FileObject sub = fo.getFileObject(name);
if (sub == null) {
FileObject fo2 = fo.createFolder(name);
Assert.assertNotNull("createFolder(" + fo + ", " + name + ") -> null", fo2);
fo = fo2;
} else {
Assert.assertTrue("folder", sub.isFolder());
fo = sub;
}
} else {
FileObject sub = fo.getFileObject(name);
if (sub == null) {
FileObject fo2 = fo.createData(name);
Assert.assertNotNull("createData(" + fo + ", " + name + ") -> null", fo2);
fo = fo2;
} else {
fo = sub;
touch = true;
}
}
}
assert fo.isData();
if (content != null || touch) {
OutputStream os = fo.getOutputStream();
try {
if (content != null) {
InputStream is = content.openStream();
try {
FileUtil.copy(is, os);
} finally {
is.close();
}
}
} finally {
os.close();
}
}
return fo;
}
}