| /* |
| * 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.openide; |
| |
| import java.io.ByteArrayOutputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.PrintStream; |
| import java.net.URL; |
| import java.util.Date; |
| import java.util.HashMap; |
| import java.util.LinkedList; |
| import java.util.regex.Pattern; |
| import junit.framework.AssertionFailedError; |
| import junit.framework.TestResult; |
| import org.netbeans.junit.MockServices; |
| import org.netbeans.junit.NbTestCase; |
| import org.openide.util.Lookup; |
| |
| /** Basic skeleton for logging test case. |
| * |
| * @author Jaroslav Tulach |
| */ |
| public abstract class LoggingTestCaseHid extends NbTestCase { |
| static { |
| MockServices.setServices(new Class[] {ErrManager.class}); |
| } |
| |
| protected LoggingTestCaseHid (String name) { |
| super (name); |
| } |
| |
| /** If execution fails we wrap the exception with |
| * new log message. |
| */ |
| protected void runTest () throws Throwable { |
| |
| assertNotNull ("ErrManager has to be in lookup", Lookup.getDefault().lookup(ErrManager.class)); |
| |
| ErrManager.clear(getName(), getLog()); |
| |
| try { |
| super.runTest (); |
| } catch (AssertionFailedError ex) { |
| AssertionFailedError ne = new AssertionFailedError (ex.getMessage () + " Log:\n" + ErrManager.messages); |
| ne.setStackTrace (ex.getStackTrace ()); |
| throw ne; |
| } catch (IOException iex) {//#66208 |
| IOException ne = new IOException (iex.getMessage () + " Log:\n" + ErrManager.messages); |
| ne.setStackTrace (iex.getStackTrace ()); |
| throw ne; |
| } finally { |
| // do not write to log files anymore |
| ErrManager.clear(getName(), System.err); |
| } |
| } |
| |
| /** Registers hints for controlling thread switching in multithreaded |
| * applications. |
| * @param url the url to read the file from |
| * @exception IOException thrown when there is problem reading the url |
| */ |
| protected final void registerSwitches(URL url, int timeout) throws IOException { |
| ByteArrayOutputStream os = new ByteArrayOutputStream(); |
| InputStream is = url.openStream(); |
| for (;;) { |
| int ch = is.read (); |
| if (ch == -1) break; |
| os.write (ch); |
| } |
| os.close(); |
| is.close(); |
| |
| registerSwitches(new String(os.toByteArray(), "utf-8"), timeout); |
| } |
| |
| /** Registers hints for controlling thread switching in multithreaded |
| * applications. |
| |
| */ |
| protected final void registerSwitches(String order, int timeout) { |
| ErrManager.timeout = timeout; |
| |
| LinkedList switches = new LinkedList(); |
| |
| HashMap exprs = new HashMap(); |
| |
| int pos = 0; |
| for(;;) { |
| int thr = order.indexOf("THREAD:", pos); |
| if (thr == -1) { |
| break; |
| } |
| int msg = order.indexOf("MSG:", thr); |
| if (msg == -1) { |
| fail("After THREAD: there must be MSG: " + order.substring(thr)); |
| } |
| int end = order.indexOf("THREAD:", msg); |
| if (end == -1) { |
| end = order.length(); |
| } |
| |
| String thrName = order.substring(pos + 7, msg).trim(); |
| String msgText = order.substring(msg + 4, end).trim(); |
| |
| Pattern p = (Pattern)exprs.get(msgText); |
| if (p == null) { |
| p = Pattern.compile(msgText); |
| exprs.put(msgText, p); |
| } |
| |
| Switch s = new Switch(thrName, p); |
| switches.add(s); |
| |
| pos = end; |
| } |
| |
| ErrManager.switches = switches; |
| } |
| |
| // |
| // Logging support |
| // |
| public static final class ErrManager extends ErrorManager { |
| public static final StringBuffer messages = new StringBuffer (); |
| static java.io.PrintStream log = System.err; |
| |
| private String prefix; |
| |
| private static LinkedList switches; |
| private static int timeout; |
| /** maps names of threads to their instances*/ |
| private static java.util.Map threads = new java.util.HashMap(); |
| |
| public ErrManager () { |
| this (null); |
| } |
| public ErrManager (String prefix) { |
| this.prefix = prefix; |
| } |
| |
| public Throwable annotate (Throwable t, int severity, String message, String localizedMessage, Throwable stackTrace, Date date) { |
| return t; |
| } |
| |
| public Throwable attachAnnotations (Throwable t, ErrorManager.Annotation[] arr) { |
| return t; |
| } |
| |
| public ErrorManager.Annotation[] findAnnotations (Throwable t) { |
| return null; |
| } |
| |
| public ErrorManager getInstance (String name) { |
| if ( |
| true |
| ) { |
| return new ErrManager ('[' + name + "] "); |
| } else { |
| // either new non-logging or myself if I am non-logging |
| return new ErrManager (); |
| } |
| } |
| |
| public void log (int severity, String s) { |
| StringBuffer oneMsg = new StringBuffer(); |
| if (prefix != null) { |
| oneMsg.append(prefix); |
| } else { |
| oneMsg.append("[default] "); |
| } |
| oneMsg.append("THREAD:"); |
| oneMsg.append(Thread.currentThread().getName()); |
| oneMsg.append(" MSG:"); |
| oneMsg.append(s); |
| |
| |
| messages.append(oneMsg.toString()); |
| messages.append ('\n'); |
| |
| if (messages.length() > 40000) { |
| messages.delete(0, 20000); |
| } |
| |
| log.println(oneMsg.toString()); |
| |
| if (switches != null) { |
| boolean log = true; |
| boolean expectingMsg = false; |
| for(;;) { |
| synchronized (switches) { |
| if (switches.isEmpty()) { |
| return; |
| } |
| |
| |
| Switch w = (Switch)switches.getFirst(); |
| String threadName = Thread.currentThread().getName(); |
| boolean foundMatch = false; |
| |
| if (w.matchesThread()) { |
| if (!w.matchesMessage(s)) { |
| // same thread but wrong message => go on |
| return; |
| } |
| // the correct message from the right thread found |
| switches.removeFirst(); |
| if (switches.isEmpty()) { |
| // end of sample, make all run |
| switches.notifyAll(); |
| return; |
| } |
| w = (Switch)switches.getFirst(); |
| if (w.matchesThread()) { |
| // next message is also from this thread, go on |
| return; |
| } |
| expectingMsg = true; |
| foundMatch = true; |
| } else { |
| // compute whether we shall wait or not |
| java.util.Iterator it = switches.iterator(); |
| while (it.hasNext()) { |
| Switch check = (Switch)it.next(); |
| if (check.matchesMessage(s)) { |
| expectingMsg = true; |
| break; |
| } |
| } |
| } |
| |
| // make it other thread run |
| Thread t = (Thread)threads.get(w.name); |
| if (t != null) { |
| if (log) { |
| messages.append("t: " + threadName + " interrupts: " + t.getName() + "\n"); |
| } |
| t.interrupt(); |
| } |
| threads.put(threadName, Thread.currentThread()); |
| |
| // |
| // if (log) { |
| // messages.append("t: " + Thread.currentThread().getName() + " log: " + s + " result: " + m + " for: " + w + "\n"); |
| // } |
| if (!expectingMsg) { |
| return; |
| } |
| |
| // clear any interrupt that happend before |
| Thread.interrupted(); |
| try { |
| if (log) { |
| messages.append("t: " + threadName + " log: " + s + " waiting\n"); |
| } |
| switches.wait(timeout); |
| if (log) { |
| messages.append("t: " + threadName + " log: " + s + " timeout\n"); |
| } |
| return; |
| } catch (InterruptedException ex) { |
| // ok, we love to be interrupted => go on |
| if (log) { |
| messages.append("t: " + threadName + " log: " + s + " interrupted\n"); |
| } |
| if (foundMatch) { |
| return; |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| public void notify (int severity, Throwable t) { |
| log (severity, t.getMessage ()); |
| } |
| |
| public boolean isNotifiable (int severity) { |
| return prefix != null; |
| } |
| |
| public boolean isLoggable (int severity) { |
| return prefix != null; |
| } |
| |
| private static void clear(String n, PrintStream printStream) { |
| ErrManager.log = printStream; |
| ErrManager.messages.setLength(0); |
| ErrManager.messages.append ("Starting test "); |
| ErrManager.messages.append (n); |
| ErrManager.messages.append ('\n'); |
| threads.clear(); |
| } |
| |
| } // end of ErrManager |
| |
| private static final class Switch { |
| private Pattern msg; |
| private String name; |
| |
| public Switch(String n, Pattern m) { |
| this.name = n; |
| this.msg = m; |
| } |
| |
| /** @return true if the thread name of the caller matches this switch |
| */ |
| public boolean matchesThread() { |
| String thr = Thread.currentThread().getName(); |
| return name.equals(thr); |
| } |
| |
| /** @return true if the message matches the one provided by this switch |
| */ |
| public boolean matchesMessage(String logMsg) { |
| return msg.matcher(logMsg).matches(); |
| } |
| |
| public String toString() { |
| return "Switch[" + name + "]: " + msg; |
| } |
| } |
| } |