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.openide;
043:
044: import java.io.ByteArrayOutputStream;
045: import java.io.IOException;
046: import java.io.InputStream;
047: import java.io.PrintStream;
048: import java.net.URL;
049: import java.util.Date;
050: import java.util.HashMap;
051: import java.util.LinkedList;
052: import java.util.regex.Pattern;
053: import junit.framework.AssertionFailedError;
054: import junit.framework.TestResult;
055: import org.netbeans.junit.MockServices;
056: import org.netbeans.junit.NbTestCase;
057: import org.openide.util.Lookup;
058:
059: /** Basic skeleton for logging test case.
060: *
061: * @author Jaroslav Tulach
062: */
063: public abstract class LoggingTestCaseHid extends NbTestCase {
064: static {
065: MockServices.setServices(new Class[] { ErrManager.class });
066: }
067:
068: protected LoggingTestCaseHid(String name) {
069: super (name);
070: }
071:
072: /** If execution fails we wrap the exception with
073: * new log message.
074: */
075: protected void runTest() throws Throwable {
076:
077: assertNotNull("ErrManager has to be in lookup", Lookup
078: .getDefault().lookup(ErrManager.class));
079:
080: ErrManager.clear(getName(), getLog());
081:
082: try {
083: super .runTest();
084: } catch (AssertionFailedError ex) {
085: AssertionFailedError ne = new AssertionFailedError(ex
086: .getMessage()
087: + " Log:\n" + ErrManager.messages);
088: ne.setStackTrace(ex.getStackTrace());
089: throw ne;
090: } catch (IOException iex) {//#66208
091: IOException ne = new IOException(iex.getMessage()
092: + " Log:\n" + ErrManager.messages);
093: ne.setStackTrace(iex.getStackTrace());
094: throw ne;
095: } finally {
096: // do not write to log files anymore
097: ErrManager.clear(getName(), System.err);
098: }
099: }
100:
101: /** Registers hints for controlling thread switching in multithreaded
102: * applications.
103: * @param url the url to read the file from
104: * @exception IOException thrown when there is problem reading the url
105: */
106: protected final void registerSwitches(URL url, int timeout)
107: throws IOException {
108: ByteArrayOutputStream os = new ByteArrayOutputStream();
109: InputStream is = url.openStream();
110: for (;;) {
111: int ch = is.read();
112: if (ch == -1)
113: break;
114: os.write(ch);
115: }
116: os.close();
117: is.close();
118:
119: registerSwitches(new String(os.toByteArray(), "utf-8"), timeout);
120: }
121:
122: /** Registers hints for controlling thread switching in multithreaded
123: * applications.
124:
125: */
126: protected final void registerSwitches(String order, int timeout) {
127: ErrManager.timeout = timeout;
128:
129: LinkedList switches = new LinkedList();
130:
131: HashMap exprs = new HashMap();
132:
133: int pos = 0;
134: for (;;) {
135: int thr = order.indexOf("THREAD:", pos);
136: if (thr == -1) {
137: break;
138: }
139: int msg = order.indexOf("MSG:", thr);
140: if (msg == -1) {
141: fail("After THREAD: there must be MSG: "
142: + order.substring(thr));
143: }
144: int end = order.indexOf("THREAD:", msg);
145: if (end == -1) {
146: end = order.length();
147: }
148:
149: String thrName = order.substring(pos + 7, msg).trim();
150: String msgText = order.substring(msg + 4, end).trim();
151:
152: Pattern p = (Pattern) exprs.get(msgText);
153: if (p == null) {
154: p = Pattern.compile(msgText);
155: exprs.put(msgText, p);
156: }
157:
158: Switch s = new Switch(thrName, p);
159: switches.add(s);
160:
161: pos = end;
162: }
163:
164: ErrManager.switches = switches;
165: }
166:
167: //
168: // Logging support
169: //
170: public static final class ErrManager extends ErrorManager {
171: public static final StringBuffer messages = new StringBuffer();
172: static java.io.PrintStream log = System.err;
173:
174: private String prefix;
175:
176: private static LinkedList switches;
177: private static int timeout;
178: /** maps names of threads to their instances*/
179: private static java.util.Map threads = new java.util.HashMap();
180:
181: public ErrManager() {
182: this (null);
183: }
184:
185: public ErrManager(String prefix) {
186: this .prefix = prefix;
187: }
188:
189: public Throwable annotate(Throwable t, int severity,
190: String message, String localizedMessage,
191: Throwable stackTrace, Date date) {
192: return t;
193: }
194:
195: public Throwable attachAnnotations(Throwable t,
196: ErrorManager.Annotation[] arr) {
197: return t;
198: }
199:
200: public ErrorManager.Annotation[] findAnnotations(Throwable t) {
201: return null;
202: }
203:
204: public ErrorManager getInstance(String name) {
205: if (true) {
206: return new ErrManager('[' + name + "] ");
207: } else {
208: // either new non-logging or myself if I am non-logging
209: return new ErrManager();
210: }
211: }
212:
213: public void log(int severity, String s) {
214: StringBuffer oneMsg = new StringBuffer();
215: if (prefix != null) {
216: oneMsg.append(prefix);
217: } else {
218: oneMsg.append("[default] ");
219: }
220: oneMsg.append("THREAD:");
221: oneMsg.append(Thread.currentThread().getName());
222: oneMsg.append(" MSG:");
223: oneMsg.append(s);
224:
225: messages.append(oneMsg.toString());
226: messages.append('\n');
227:
228: if (messages.length() > 40000) {
229: messages.delete(0, 20000);
230: }
231:
232: log.println(oneMsg.toString());
233:
234: if (switches != null) {
235: boolean log = true;
236: boolean expectingMsg = false;
237: for (;;) {
238: synchronized (switches) {
239: if (switches.isEmpty()) {
240: return;
241: }
242:
243: Switch w = (Switch) switches.getFirst();
244: String threadName = Thread.currentThread()
245: .getName();
246: boolean foundMatch = false;
247:
248: if (w.matchesThread()) {
249: if (!w.matchesMessage(s)) {
250: // same thread but wrong message => go on
251: return;
252: }
253: // the correct message from the right thread found
254: switches.removeFirst();
255: if (switches.isEmpty()) {
256: // end of sample, make all run
257: switches.notifyAll();
258: return;
259: }
260: w = (Switch) switches.getFirst();
261: if (w.matchesThread()) {
262: // next message is also from this thread, go on
263: return;
264: }
265: expectingMsg = true;
266: foundMatch = true;
267: } else {
268: // compute whether we shall wait or not
269: java.util.Iterator it = switches.iterator();
270: while (it.hasNext()) {
271: Switch check = (Switch) it.next();
272: if (check.matchesMessage(s)) {
273: expectingMsg = true;
274: break;
275: }
276: }
277: }
278:
279: // make it other thread run
280: Thread t = (Thread) threads.get(w.name);
281: if (t != null) {
282: if (log) {
283: messages.append("t: " + threadName
284: + " interrupts: " + t.getName()
285: + "\n");
286: }
287: t.interrupt();
288: }
289: threads.put(threadName, Thread.currentThread());
290:
291: //
292: // if (log) {
293: // messages.append("t: " + Thread.currentThread().getName() + " log: " + s + " result: " + m + " for: " + w + "\n");
294: // }
295: if (!expectingMsg) {
296: return;
297: }
298:
299: // clear any interrupt that happend before
300: Thread.interrupted();
301: try {
302: if (log) {
303: messages.append("t: " + threadName
304: + " log: " + s + " waiting\n");
305: }
306: switches.wait(timeout);
307: if (log) {
308: messages.append("t: " + threadName
309: + " log: " + s + " timeout\n");
310: }
311: return;
312: } catch (InterruptedException ex) {
313: // ok, we love to be interrupted => go on
314: if (log) {
315: messages.append("t: " + threadName
316: + " log: " + s
317: + " interrupted\n");
318: }
319: if (foundMatch) {
320: return;
321: }
322: }
323: }
324: }
325: }
326: }
327:
328: public void notify(int severity, Throwable t) {
329: log(severity, t.getMessage());
330: }
331:
332: public boolean isNotifiable(int severity) {
333: return prefix != null;
334: }
335:
336: public boolean isLoggable(int severity) {
337: return prefix != null;
338: }
339:
340: private static void clear(String n, PrintStream printStream) {
341: ErrManager.log = printStream;
342: ErrManager.messages.setLength(0);
343: ErrManager.messages.append("Starting test ");
344: ErrManager.messages.append(n);
345: ErrManager.messages.append('\n');
346: threads.clear();
347: }
348:
349: } // end of ErrManager
350:
351: private static final class Switch {
352: private Pattern msg;
353: private String name;
354:
355: public Switch(String n, Pattern m) {
356: this .name = n;
357: this .msg = m;
358: }
359:
360: /** @return true if the thread name of the caller matches this switch
361: */
362: public boolean matchesThread() {
363: String thr = Thread.currentThread().getName();
364: return name.equals(thr);
365: }
366:
367: /** @return true if the message matches the one provided by this switch
368: */
369: public boolean matchesMessage(String logMsg) {
370: return msg.matcher(logMsg).matches();
371: }
372:
373: public String toString() {
374: return "Switch[" + name + "]: " + msg;
375: }
376: }
377: }
|