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.nbbuild;
043:
044: import java.io.File;
045: import java.io.FileInputStream;
046: import java.io.IOException;
047: import java.io.StringReader;
048: import java.util.ArrayList;
049: import java.util.Arrays;
050: import java.util.Collections;
051: import java.util.HashSet;
052: import java.util.Iterator;
053: import java.util.List;
054: import java.util.Properties;
055: import java.util.Set;
056: import java.util.StringTokenizer;
057: import javax.xml.parsers.DocumentBuilder;
058: import javax.xml.parsers.DocumentBuilderFactory;
059: import javax.xml.parsers.ParserConfigurationException;
060: import org.apache.tools.ant.*;
061: import org.w3c.dom.Element;
062: import org.w3c.dom.NodeList;
063: import org.xml.sax.EntityResolver;
064: import org.xml.sax.InputSource;
065: import org.xml.sax.SAXException;
066:
067: /**
068: * It scans test distribution ant sets to property with name defined by param
069: * 'testListProperty'path with filtered test. The TestDistFilter is used for running tests
070: * in test distribution.
071: * <br>
072: *
073: * Parameters :
074: * <ul>
075: * <li>harness - type of harness (junit,xtest). For junit harness are
076: * scanned only tests with unit tes type and 'code' executor name inside
077: * cfg-unit.xml.
078: * <li>testtype - unit|qa-functional|all testtype
079: * <li>attribs - xtest.attribs filter (attribs are declared in cfg-<xxx>.xml file
080: * <li>testlistproperty - store to property path with test folders (separated by ':')
081: * <li>testdistdir - root folder with test distribution
082: * <li>requiredmodules - list of module names required on runtime classpath example:
083: * org-netbeans-modules-masterfs.jar,org-openide-loaders.jar. Only tests
084: * which contains masterfs and loaders will be stored to testlistproperty value.
085: * </ul>
086: */
087: public class TestDistFilter extends Task {
088: public static final String TYPE_ALL = "all";
089: public static final String TYPE_UNIT = "unit";
090: public static final String TYPE_QA_FUNCTIONAL = "qa-functional";
091:
092: public static final String HARNESS_JUNIT = "junit";
093: public static final String HARNESS_XTEST = "xtest";
094:
095: private File testDistDir;
096: Set<TestConf> possibleTests = new HashSet<TestConf>();
097: // "unit|qa_functional|all
098: // default value is all
099: private String testtype = TYPE_ALL;
100: private String harness = HARNESS_XTEST;
101: // xtest attribs
102: private String attribs;
103: private String testListProperty;
104: private String requiredModules;
105:
106: // TODO customize method names to match custom task
107: // property and type (handled by inner class) names
108:
109: /** represents a test directory
110: */
111: private static class TestConf {
112: File moduleDir;
113: boolean unit;
114:
115: TestConf(File moduleDir, boolean unit) {
116: this .moduleDir = moduleDir;
117: this .unit = unit;
118: }
119:
120: public int hashCode() {
121: return moduleDir.hashCode();
122: }
123:
124: public boolean equals(Object obj) {
125: return (obj instanceof TestConf)
126: && moduleDir.equals(((TestConf) obj).moduleDir);
127: }
128:
129: /** check if cfg-<testtype>.xml contains xtest.attribs,
130: * ide executor is ignored for junit
131: */
132: boolean matchAttribs(String harness, String attribs) {
133: Element config;
134: try {
135: config = getConfig();
136: } catch (SAXException ex) {
137: throw new BuildException("Error in parsing "
138: + getConfigFile(), ex);
139: } catch (ParserConfigurationException ex) {
140: throw new BuildException("Error in parsing "
141: + getConfigFile(), ex);
142: } catch (IOException ex) {
143: throw new BuildException("Error in parsing "
144: + getConfigFile(), ex);
145: }
146: if (config == null) {
147: return false;
148: }
149: boolean junit = HARNESS_JUNIT.equals(harness);
150: NodeList elements = config.getElementsByTagName("testbag");
151: for (int n = 0; n < elements.getLength(); n++) {
152: Element testbag = (Element) elements.item(n);
153: if (junit
154: && "ide".equals(testbag
155: .getAttribute("executor"))) {
156: continue;
157: }
158: if (testAttr(testbag.getAttribute("testattribs"),
159: attribs)) {
160: return true;
161: }
162: }
163: return false;
164: }
165:
166: private static boolean testAttr(String xmlAttr, String userAttr) {
167: if (userAttr == null) {
168: return true;
169: }
170: if (xmlAttr == null) {
171: return false;
172: }
173: StringTokenizer tokenizer = new StringTokenizer(xmlAttr,
174: "&|, ");
175: while (tokenizer.hasMoreTokens()) {
176: String token = tokenizer.nextToken().trim();
177: if (token.equals(userAttr)) {
178: return true;
179: }
180: }
181: return false;
182: }
183:
184: File getModuleDir() {
185: return moduleDir;
186: }
187:
188: private File getConfigFile() {
189: String name = (unit) ? "cfg-unit.xml"
190: : "cfg-qa-functional.xml";
191: return new File(getModuleDir(), name);
192: }
193:
194: private Element getConfig()
195: throws ParserConfigurationException, SAXException,
196: IOException {
197: File xml = getConfigFile();
198: if (!xml.exists()) {
199: return null;
200: }
201: return getDocumentBuilder().parse(xml).getDocumentElement();
202: }
203:
204: }
205:
206: public void execute() throws BuildException {
207: possibleTests.clear();
208: if (getTestListProperty() == null) {
209: throw new BuildException(
210: "Param testlistproperty is not defined.");
211: }
212: if (getTestDistDir() == null || !getTestDistDir().exists()) {
213: throw new BuildException(
214: "Param testdistdir is not defined.");
215: }
216: if ("".equals(attribs)) {
217: attribs = null;
218: }
219: String tt = getTesttype();
220: if (getHarness().equals(HARNESS_JUNIT)) {
221: findCodeTests(HARNESS_JUNIT, TYPE_UNIT, getAttribs());
222: } else {
223: if (tt.equals(TYPE_QA_FUNCTIONAL) || tt.equals(TYPE_ALL)) {
224: findCodeTests(HARNESS_XTEST, TYPE_QA_FUNCTIONAL,
225: getAttribs());
226: }
227: if (tt.equals(TYPE_UNIT) || tt.equals(TYPE_ALL)) {
228: findCodeTests(HARNESS_XTEST, TYPE_UNIT, getAttribs());
229: }
230: }
231: define(getTestListProperty(), getTestList());
232: }
233:
234: /** get path with test dirs separated by :
235: */
236: private String getTestList() {
237: StringBuffer path = new StringBuffer();
238: for (Iterator it = possibleTests.iterator(); it.hasNext();) {
239: TestConf tc = (TestConf) it.next();
240: if (!matchRequiredModule(tc.getModuleDir())) {
241: continue;
242: }
243: if (path.length() > 0) {
244: path.append(':');
245: }
246: path.append(tc.getModuleDir().getAbsolutePath());
247: }
248: return path.toString();
249: }
250:
251: private void define(String prop, String val) {
252: log("Setting " + prop + "=" + val, Project.MSG_VERBOSE);
253: String old = getProject().getProperty(prop);
254: if (old != null && !old.equals(val)) {
255: getProject().log(
256: "Warning: " + prop + " was already set to " + old,
257: Project.MSG_WARN);
258: }
259: getProject().setNewProperty(prop, val);
260: }
261:
262: public String getTesttype() {
263: return testtype;
264: }
265:
266: public void setTesttype(String testtype) {
267: this .testtype = testtype;
268: }
269:
270: public String getHarness() {
271: return harness;
272: }
273:
274: public void setHarness(String harness) {
275: this .harness = harness;
276: }
277:
278: public String getAttribs() {
279: return attribs;
280: }
281:
282: public void setAttribs(String attribs) {
283: this .attribs = attribs;
284: }
285:
286: public String getTestListProperty() {
287: return testListProperty;
288: }
289:
290: public void setTestListProperty(String testListProperty) {
291: this .testListProperty = testListProperty;
292: }
293:
294: private void findCodeTests(String harness, String type,
295: String string) {
296: List tests = getTestList(type);
297: for (int i = 0; i < tests.size(); i++) {
298: TestConf test = (TestConf) tests.get(i);
299: if (test.matchAttribs(harness, attribs)) {
300: possibleTests.add(test);
301: }
302: }
303: }
304:
305: private List getTestList(String testtype) {
306: File root = new File(getTestDistDir(), testtype);
307: List<TestConf> testList = new ArrayList<TestConf>();
308: if (!root.exists()) {
309: return Collections.EMPTY_LIST;
310: }
311: File clusters[] = root.listFiles();
312: for (int c = 0; c < clusters.length; c++) {
313: File cluster = clusters[c];
314: if (cluster.isDirectory()) {
315: File modules[] = cluster.listFiles();
316: for (int m = 0; m < modules.length; m++) {
317: File module = modules[m];
318: if (module.isDirectory()) {
319: testList.add(new TestConf(module, testtype
320: .equals(TYPE_UNIT)));
321: }
322: }
323: }
324: }
325: return testList;
326: }
327:
328: // create document builder with empty EntityResolver
329: //
330: private static DocumentBuilder db;
331:
332: private static DocumentBuilder getDocumentBuilder()
333: throws ParserConfigurationException {
334: if (db == null) {
335: db = DocumentBuilderFactory.newInstance()
336: .newDocumentBuilder();
337: db.setEntityResolver(new EntityResolver() {
338: public InputSource resolveEntity(String publicId,
339: String systemId) throws SAXException,
340: IOException {
341: return new InputSource(new StringReader(""));
342: }
343:
344: });
345: }
346: return db;
347: }
348:
349: public File getTestDistDir() {
350: return testDistDir;
351: }
352:
353: public void setTestDistDir(File testDistDir) {
354: this .testDistDir = testDistDir;
355: }
356:
357: public String getRequiredModules() {
358: return requiredModules;
359: }
360:
361: public void setRequiredModules(String requiredModules) {
362: this .requiredModules = requiredModules;
363: }
364:
365: private boolean matchRequiredModule(File path) {
366: if (requiredModules == null
367: || requiredModules.trim().length() == 0) {
368: return true;
369: }
370: File pfile = new File(path, "test.properties");
371: if (pfile.exists()) {
372: Properties props = new Properties();
373: try {
374: FileInputStream fis = new FileInputStream(pfile);
375: try {
376: props.load(fis);
377:
378: String runCp = props
379: .getProperty("test.unit.run.cp");
380: if (runCp != null) {
381: String paths[] = runCp.split(":");
382: Set reqModules = getRequiredModulesSet();
383: if (reqModules.size() == 0) {
384: return true;
385: }
386: for (int i = 0; i < paths.length; i++) {
387: String p = paths[i];
388: int lastSlash = p.lastIndexOf('/');
389: if (lastSlash != -1) {
390: p = p.substring(lastSlash + 1);
391: }
392: if (reqModules.contains(p)) {
393: return true;
394: }
395: }
396: }
397: } finally {
398: fis.close();
399: }
400: } catch (IOException ioe) {
401: throw new BuildException(ioe);
402: }
403: }
404: return false;
405: }
406:
407: private Set<String> getRequiredModulesSet() {
408: String names[] = getRequiredModules().split(",");
409: return new HashSet<String>(Arrays.asList(names));
410: }
411: }
|