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.apache.tools.ant.module.spi;
043:
044: import java.io.File;
045: import java.io.IOException;
046: import java.util.ArrayList;
047: import java.util.Arrays;
048: import java.util.HashSet;
049: import java.util.List;
050: import java.util.Set;
051: import org.apache.tools.ant.module.api.AntTargetExecutor;
052: import org.apache.tools.ant.module.xml.AntProjectSupport;
053: import org.netbeans.junit.MockServices;
054: import org.netbeans.junit.NbTestCase;
055: import org.openide.filesystems.FileObject;
056: import org.openide.filesystems.FileUtil;
057: import org.openide.modules.InstalledFileLocator;
058: import org.openide.util.Lookup;
059:
060: // For debugging info, add to nbproject/private/private.properties:
061: // test-unit-sys-prop.org.apache.tools.ant.module.bridge.impl.NbBuildLogger.LOG_AT_WARNING=true
062:
063: /**
064: * Tests functionality of {@link AntLogger}.
065: * Specifically, NbBuildLogger.
066: * @author Jesse Glick
067: */
068: public class AntLoggerTest extends NbTestCase {
069:
070: static {
071: AntLoggerTest.class.getClassLoader().setDefaultAssertionStatus(
072: true);
073: }
074:
075: public AntLoggerTest(String name) {
076: super (name);
077: }
078:
079: private File testdir;
080: private FileObject testdirFO;
081: private TestLogger LOGGER;
082:
083: @Override
084: protected void setUp() throws Exception {
085: super .setUp();
086: MockServices.setServices(IFL.class, TestLogger.class);
087: LOGGER = Lookup.getDefault().lookup(TestLogger.class);
088: LOGGER.reset();
089: testdir = new File(this .getDataDir(), "antlogger");
090: assertTrue("have a dir " + testdir, testdir.isDirectory());
091: testdirFO = FileUtil.toFileObject(testdir);
092: assertNotNull("have testdirFO", testdirFO);
093: }
094:
095: private void run(FileObject script) throws Exception {
096: run(script, null, AntEvent.LOG_INFO);
097: }
098:
099: private void run(FileObject script, String[] targets, int verbosity)
100: throws Exception {
101: AntTargetExecutor.Env env = new AntTargetExecutor.Env();
102: env.setVerbosity(verbosity);
103: int res = AntTargetExecutor.createTargetExecutor(env).execute(
104: new AntProjectSupport(script), targets).result();
105: if (res != 0) {
106: throw new IOException("Nonzero exit code: " + res
107: + "; messages: " + LOGGER.getMessages());
108: }
109: }
110:
111: public void testRunningAnt() throws Exception {
112: File something = new File(System.getProperty("java.io.tmpdir"),
113: "something");
114: if (something.exists()) {
115: something.delete();
116: }
117: run(testdirFO.getFileObject("trivial.xml"));
118: assertTrue("now " + something + " exists", something.isFile());
119: }
120:
121: public void testLocationOfImportedTargetsWithoutLineNumbers()
122: throws Exception {
123: LOGGER.interestedInSessionFlag = true;
124: LOGGER.interestedInAllScriptsFlag = true;
125: LOGGER.interestingTargets = AntLogger.ALL_TARGETS;
126: run(testdirFO.getFileObject("importing.xml"));
127: File importing = new File(testdir, "importing.xml");
128: File imported = new File(testdir, "imported.xml");
129: assertEquals(
130: "correct 2 targets run (NOTE: you need Ant 1.6.0+)",
131: Arrays.asList(new String[] { imported + "#subtarget",
132: importing + "#main", }), LOGGER
133: .getTargetsStarted());
134: }
135:
136: public void testLocationOfImportedTargetsWithLineNumbers()
137: throws Exception {
138: LOGGER.interestedInSessionFlag = true;
139: LOGGER.interestedInAllScriptsFlag = true;
140: LOGGER.interestingTargets = AntLogger.ALL_TARGETS;
141: LOGGER.collectLineNumbersForTargets = true;
142: run(testdirFO.getFileObject("importing.xml"));
143: File importing = new File(testdir, "importing.xml");
144: File imported = new File(testdir, "imported.xml");
145: assertEquals(
146: "correct 2 targets run (NOTE: you need Ant 1.6.3+)",
147: Arrays.asList(new String[] { imported + ":3#subtarget",
148: importing + ":4#main", }), LOGGER
149: .getTargetsStarted());
150: }
151:
152: public void testTaskdef() throws Exception {
153: LOGGER.interestedInSessionFlag = true;
154: LOGGER.interestedInAllScriptsFlag = true;
155: LOGGER.interestingTargets = AntLogger.ALL_TARGETS;
156: LOGGER.interestingTasks = AntLogger.ALL_TASKS;
157: LOGGER.interestingLogLevels = new int[] { AntEvent.LOG_INFO,
158: AntEvent.LOG_WARN };
159: run(testdirFO.getFileObject("taskdefs.xml"));
160: //System.err.println("messages=" + LOGGER.messages);
161: assertTrue("got info message", LOGGER.getMessages().contains(
162: "mytask:" + AntEvent.LOG_INFO + ":MyTask info message"));
163: assertFalse("did not get verbose message", LOGGER.getMessages()
164: .contains(
165: "mytask:" + AntEvent.LOG_VERBOSE
166: + ":MyTask verbose message"));
167: assertTrue("got warn message", LOGGER.getMessages().contains(
168: "mytask:" + AntEvent.LOG_WARN + ":MyTask warn message"));
169: }
170:
171: public void testCorrectTaskFromIndirectCall() throws Exception {
172: // #49464: if a task calls something which in turn does Project.log w/o the Task handle,
173: // you lose all useful information. But you can guess which Task it is - you know some
174: // task has been started and not yet finished. Within limits. Imports, <ant>, etc. can
175: // screw up the accounting.
176: LOGGER.interestedInSessionFlag = true;
177: LOGGER.interestedInAllScriptsFlag = true;
178: LOGGER.interestingTargets = AntLogger.ALL_TARGETS;
179: LOGGER.interestingTasks = AntLogger.ALL_TASKS;
180: LOGGER.interestingLogLevels = new int[] { AntEvent.LOG_DEBUG };
181: run(testdirFO.getFileObject("property.xml"));
182: //System.err.println("messages=" + LOGGER.messages);
183: List<String> messages = LOGGER.getMessages();
184: assertTrue(
185: "have message with task ID in " + messages,
186: messages
187: .contains("property:4:Setting project property: propname -> propval"));
188: }
189:
190: public void testAntEventDetails() throws Exception {
191: LOGGER.interestedInSessionFlag = true;
192: LOGGER.interestedInAllScriptsFlag = true;
193: LOGGER.interestingTargets = AntLogger.ALL_TARGETS;
194: LOGGER.interestingTasks = new String[] { "echo" };
195: LOGGER.interestingLogLevels = new int[] { AntEvent.LOG_INFO };
196: run(testdirFO.getFileObject("property.xml"));
197: assertTrue(LOGGER.antEventDetailsOK);
198: LOGGER.antEventDetailsOK = false;
199: run(testdirFO.getFileObject("property.xml"),
200: new String[] { "run2" }, AntEvent.LOG_INFO);
201: assertTrue("#71816: works even inside <antcall>",
202: LOGGER.antEventDetailsOK);
203: }
204:
205: public void testSimultaneousLogging() throws Exception {
206: // #84704: just because one log call is locked, ought not block others
207: LOGGER.interestedInSessionFlag = true;
208: LOGGER.interestedInAllScriptsFlag = true;
209: LOGGER.interestingTargets = AntLogger.ALL_TARGETS;
210: LOGGER.interestingTasks = AntLogger.ALL_TASKS;
211: LOGGER.interestingLogLevels = new int[] { AntEvent.LOG_VERBOSE };
212: LOGGER.halt = true;
213: run(testdirFO.getFileObject("trivial.xml"), null,
214: AntEvent.LOG_VERBOSE);
215: // see TestLogger.taskStarted for details
216: }
217:
218: /**
219: * Sample logger which collects results.
220: */
221: public static final class TestLogger extends AntLogger {
222:
223: public boolean interestedInSessionFlag;
224: public boolean interestedInAllScriptsFlag;
225: public Set<File> interestingScripts;
226: public String[] interestingTargets;
227: public String[] interestingTasks;
228: public int[] interestingLogLevels;
229: public boolean collectLineNumbersForTargets;
230: public boolean halt;
231: /** Format of each: "/path/to/file.xml:line#targetName" (line numbers only if collectLineNumbersForTargets) */
232: private List<String> targetsStarted;
233: /** Format of each: "taskname:level:message" */
234: private List<String> messages;
235: private boolean antEventDetailsOK;
236:
237: public TestLogger() {
238: }
239:
240: /** Set everything back to default values as in AntLogger base class. */
241: public synchronized void reset() {
242: interestedInSessionFlag = false;
243: interestedInAllScriptsFlag = false;
244: interestingScripts = new HashSet<File>();
245: interestingTargets = AntLogger.NO_TARGETS;
246: interestingTasks = AntLogger.NO_TASKS;
247: interestingLogLevels = new int[0];
248: collectLineNumbersForTargets = false;
249: targetsStarted = new ArrayList<String>();
250: messages = new ArrayList<String>();
251: antEventDetailsOK = false;
252: halt = false;
253: }
254:
255: public synchronized List<String> getTargetsStarted() {
256: return new ArrayList<String>(targetsStarted);
257: }
258:
259: public synchronized List<String> getMessages() {
260: return new ArrayList<String>(messages);
261: }
262:
263: @Override
264: public boolean interestedInAllScripts(AntSession session) {
265: return interestedInAllScriptsFlag;
266: }
267:
268: @Override
269: public String[] interestedInTasks(AntSession session) {
270: return interestingTasks;
271: }
272:
273: @Override
274: public boolean interestedInScript(File script,
275: AntSession session) {
276: return interestingScripts.contains(script);
277: }
278:
279: @Override
280: public String[] interestedInTargets(AntSession session) {
281: return interestingTargets;
282: }
283:
284: @Override
285: public boolean interestedInSession(AntSession session) {
286: return interestedInSessionFlag;
287: }
288:
289: @Override
290: public int[] interestedInLogLevels(AntSession session) {
291: return interestingLogLevels;
292: }
293:
294: @Override
295: public synchronized void targetStarted(AntEvent event) {
296: int line = event.getLine();
297: targetsStarted.add(event.getScriptLocation()
298: + (collectLineNumbersForTargets && line != -1 ? ":"
299: + line : "") + '#' + event.getTargetName());
300: }
301:
302: @Override
303: public synchronized void messageLogged(AntEvent event) {
304: String toadd = "" + event.getLogLevel() + ":"
305: + event.getMessage();
306: String taskname = event.getTaskName();
307: if (taskname != null) {
308: toadd = taskname + ":" + toadd;
309: }
310: messages.add(toadd);
311: }
312:
313: @Override
314: public synchronized void buildFinished(AntEvent event) {
315: Throwable t = event.getException();
316: if (t != null) {
317: messages.add("EXC:" + t);
318: }
319: }
320:
321: @Override
322: public synchronized void taskStarted(final AntEvent event) {
323: antEventDetailsOK |= "echo".equals(event.getTaskName())
324: && "meaningless".equals(event.getTaskStructure()
325: .getText())
326: && "info".equals(event.getTaskStructure()
327: .getAttribute("level"))
328: && event.getPropertyNames().contains("propname")
329: && "propval".equals(event.getProperty("propname"));
330: if (halt && event.getTaskName().equals("touch")) {
331: try {
332: Thread t = new Thread() {
333: public void run() {
334: synchronized (TestLogger.this ) {
335: assertEquals("${foobie}", event
336: .evaluate("${foobie}"));
337: TestLogger.this .notify();
338: }
339: }
340: };
341: t.start();
342: wait(9999);
343: t.join(9999);
344: boolean found = false;
345: for (String m : messages) {
346: if (m.contains("foobie")) {
347: found = true;
348: break;
349: }
350: }
351: assertTrue("message about ${foobie} exists", found);
352: } catch (InterruptedException x) {
353: fail(x.toString());
354: }
355: }
356: }
357:
358: }
359:
360: public static final class IFL extends InstalledFileLocator {
361: public IFL() {
362: }
363:
364: @Override
365: public File locate(String relativePath, String codeNameBase,
366: boolean localized) {
367: if (relativePath.equals("ant/nblib/bridge.jar")) {
368: String path = System.getProperty("test.bridge.jar");
369: assertNotNull("must set test.bridge.jar", path);
370: return new File(path);
371: } else if (relativePath.equals("ant")) {
372: String path = System.getProperty("test.ant.home");
373: assertNotNull("must set test.ant.home", path);
374: return new File(path);
375: } else if (relativePath.startsWith("ant/")) {
376: String path = System.getProperty("test.ant.home");
377: assertNotNull("must set test.ant.home", path);
378: return new File(path, relativePath.substring(4)
379: .replace('/', File.separatorChar));
380: } else {
381: return null;
382: }
383: }
384: }
385:
386: }
|