Clover coverage report - Code Coverage for hivemind release 1.1-beta-2
Coverage timestamp: Tue Jun 28 2005 10:28:23 EDT
file stats: LOC: 595   Methods: 0
NCLOC: 324   Classes: 5
 
 Source file Conditionals Statements Methods TOTAL
HiveMindTestCase.java - - - -
coverage
 1    // Copyright 2004, 2005 The Apache Software Foundation
 2    //
 3    // Licensed under the Apache License, Version 2.0 (the "License");
 4    // you may not use this file except in compliance with the License.
 5    // You may obtain a copy of the License at
 6    //
 7    // http://www.apache.org/licenses/LICENSE-2.0
 8    //
 9    // Unless required by applicable law or agreed to in writing, software
 10    // distributed under the License is distributed on an "AS IS" BASIS,
 11    // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12    // See the License for the specific language governing permissions and
 13    // limitations under the License.
 14   
 15    package org.apache.hivemind.test;
 16   
 17    import java.net.URL;
 18    import java.util.ArrayList;
 19    import java.util.Iterator;
 20    import java.util.List;
 21    import java.util.Locale;
 22   
 23    import junit.framework.AssertionFailedError;
 24    import junit.framework.TestCase;
 25   
 26    import org.apache.hivemind.ApplicationRuntimeException;
 27    import org.apache.hivemind.ClassResolver;
 28    import org.apache.hivemind.Location;
 29    import org.apache.hivemind.ModuleDescriptorProvider;
 30    import org.apache.hivemind.Registry;
 31    import org.apache.hivemind.Resource;
 32    import org.apache.hivemind.impl.DefaultClassResolver;
 33    import org.apache.hivemind.impl.LocationImpl;
 34    import org.apache.hivemind.impl.RegistryBuilder;
 35    import org.apache.hivemind.impl.XmlModuleDescriptorProvider;
 36    import org.apache.hivemind.internal.ser.ServiceSerializationHelper;
 37    import org.apache.hivemind.util.ClasspathResource;
 38    import org.apache.hivemind.util.PropertyUtils;
 39    import org.apache.hivemind.util.URLResource;
 40    import org.apache.log4j.Level;
 41    import org.apache.log4j.LogManager;
 42    import org.apache.log4j.Logger;
 43    import org.apache.log4j.spi.LoggingEvent;
 44    import org.apache.oro.text.regex.Pattern;
 45    import org.apache.oro.text.regex.Perl5Compiler;
 46    import org.apache.oro.text.regex.Perl5Matcher;
 47    import org.easymock.MockControl;
 48    import org.easymock.classextension.MockClassControl;
 49   
 50    /**
 51    * Contains some support for creating HiveMind tests; this is useful enough that has been moved into
 52    * the main framework, to simplify creation of tests in the dependent libraries.
 53    *
 54    * @author Howard Lewis Ship
 55    */
 56    public abstract class HiveMindTestCase extends TestCase
 57    {
 58    ///CLOVER:OFF
 59   
 60    /**
 61    * An instance of {@link DefaultClassResolver} that can be used by tests.
 62    */
 63   
 64    private ClassResolver _classResolver;
 65   
 66    protected String _interceptedLoggerName;
 67   
 68    protected StoreAppender _appender;
 69   
 70    private static Perl5Compiler _compiler;
 71   
 72    private static Perl5Matcher _matcher;
 73   
 74    /** List of {@link org.easymock.MockControl}. */
 75   
 76    private List _controls = new ArrayList();
 77   
 78    /** @since 1.1 */
 79    interface MockControlFactory
 80    {
 81    public MockControl newControl(Class mockClass);
 82    }
 83   
 84    /** @since 1.1 */
 85    private static class InterfaceMockControlFactory implements MockControlFactory
 86    {
 87    public MockControl newControl(Class mockClass)
 88    {
 89    return MockControl.createStrictControl(mockClass);
 90    }
 91    }
 92   
 93    /** @since 1.1 */
 94    private static class ClassMockControlFactory implements MockControlFactory
 95    {
 96    public MockControl newControl(Class mockClass)
 97    {
 98    return MockClassControl.createStrictControl(mockClass);
 99    }
 100    }
 101   
 102    /** @since 1.1 */
 103    static class PlaceholderClassMockControlFactory implements MockControlFactory
 104    {
 105    public MockControl newControl(Class mockClass)
 106    {
 107    throw new RuntimeException(
 108    "Unable to instantiate EasyMock control for "
 109    + mockClass
 110    + "; ensure that easymockclassextension-1.1.jar and cglib-full-2.0.1.jar are on the classpath.");
 111    }
 112    }
 113   
 114    /** @since 1.1 */
 115    private static final MockControlFactory _interfaceMockControlFactory = new InterfaceMockControlFactory();
 116   
 117    /** @since 1.1 */
 118    private static MockControlFactory _classMockControlFactory;
 119   
 120    static
 121    {
 122    try
 123    {
 124    _classMockControlFactory = new ClassMockControlFactory();
 125    }
 126    catch (NoClassDefFoundError ex)
 127    {
 128    _classMockControlFactory = new PlaceholderClassMockControlFactory();
 129    }
 130    }
 131   
 132    /**
 133    * Returns the given file as a {@link Resource} from the classpath. Typically, this is to find
 134    * files in the same folder as the invoking class.
 135    */
 136    protected Resource getResource(String file)
 137    {
 138    URL url = getClass().getResource(file);
 139   
 140    if (url == null)
 141    throw new NullPointerException("No resource named '" + file + "'.");
 142   
 143    return new URLResource(url);
 144    }
 145   
 146    /**
 147    * Converts the actual list to an array and invokes
 148    * {@link #assertListsEqual(Object[], Object[])}.
 149    */
 150    protected static void assertListsEqual(Object[] expected, List actual)
 151    {
 152    assertListsEqual(expected, actual.toArray());
 153    }
 154   
 155    /**
 156    * Asserts that the two arrays are equal; same length and all elements equal. Checks the
 157    * elements first, then the length.
 158    */
 159    protected static void assertListsEqual(Object[] expected, Object[] actual)
 160    {
 161    assertNotNull(actual);
 162   
 163    int min = Math.min(expected.length, actual.length);
 164   
 165    for (int i = 0; i < min; i++)
 166    assertEquals("list[" + i + "]", expected[i], actual[i]);
 167   
 168    assertEquals("list length", expected.length, actual.length);
 169    }
 170   
 171    /**
 172    * Called when code should not be reachable (because a test is expected to throw an exception);
 173    * throws AssertionFailedError always.
 174    */
 175    protected static void unreachable()
 176    {
 177    throw new AssertionFailedError("This code should be unreachable.");
 178    }
 179   
 180    /**
 181    * Sets up an appender to intercept logging for the specified logger. Captured log events can be
 182    * recovered via {@link #getInterceptedLogEvents()}.
 183    */
 184    protected void interceptLogging(String loggerName)
 185    {
 186    Logger logger = LogManager.getLogger(loggerName);
 187   
 188    logger.removeAllAppenders();
 189   
 190    _interceptedLoggerName = loggerName;
 191    _appender = new StoreAppender();
 192    _appender.activateOptions();
 193   
 194    logger.setLevel(Level.DEBUG);
 195    logger.setAdditivity(false);
 196    logger.addAppender(_appender);
 197    }
 198   
 199    /**
 200    * Gets the list of events most recently intercepted. This resets the appender, clearing the
 201    * list of stored events.
 202    *
 203    * @see #interceptLogging(String)
 204    */
 205   
 206    protected List getInterceptedLogEvents()
 207    {
 208    return _appender.getEvents();
 209    }
 210   
 211    /**
 212    * Removes the appender that may have been setup by {@link #interceptLogging(String)}. Also,
 213    * invokes {@link org.apache.hivemind.util.PropertyUtils#clearCache()}.
 214    */
 215    protected void tearDown() throws Exception
 216    {
 217    super.tearDown();
 218   
 219    if (_appender != null)
 220    {
 221    _appender = null;
 222   
 223    Logger logger = LogManager.getLogger(_interceptedLoggerName);
 224    logger.setLevel(null);
 225    logger.setAdditivity(true);
 226    logger.removeAllAppenders();
 227    }
 228   
 229    PropertyUtils.clearCache();
 230   
 231    ServiceSerializationHelper.setServiceSerializationSupport(null);
 232    }
 233   
 234    /**
 235    * Checks that the provided substring exists in the exceptions message.
 236    */
 237    protected void assertExceptionSubstring(Throwable ex, String substring)
 238    {
 239    String message = ex.getMessage();
 240    assertNotNull(message);
 241   
 242    int pos = message.indexOf(substring);
 243   
 244    if (pos < 0)
 245    throw new AssertionFailedError("Exception message (" + message + ") does not contain ["
 246    + substring + "]");
 247    }
 248   
 249    /**
 250    * Checks that the message for an exception matches a regular expression.
 251    */
 252   
 253    protected void assertExceptionRegexp(Throwable ex, String pattern) throws Exception
 254    {
 255    String message = ex.getMessage();
 256    assertNotNull(message);
 257   
 258    setupMatcher();
 259   
 260    Pattern compiled = _compiler.compile(pattern);
 261   
 262    if (_matcher.contains(message, compiled))
 263    return;
 264   
 265    throw new AssertionFailedError("Exception message (" + message
 266    + ") does not contain regular expression [" + pattern + "].");
 267    }
 268   
 269    protected void assertRegexp(String pattern, String actual) throws Exception
 270    {
 271    setupMatcher();
 272   
 273    Pattern compiled = _compiler.compile(pattern);
 274   
 275    if (_matcher.contains(actual, compiled))
 276    return;
 277   
 278    throw new AssertionFailedError("\"" + actual + "\" does not contain regular expression["
 279    + pattern + "].");
 280    }
 281   
 282    /**
 283    * Digs down through (potentially) a stack of ApplicationRuntimeExceptions until it reaches the
 284    * originating exception, which is returned.
 285    */
 286    protected Throwable findNestedException(ApplicationRuntimeException ex)
 287    {
 288    Throwable cause = ex.getRootCause();
 289   
 290    if (cause == null || cause == ex)
 291    return ex;
 292   
 293    if (cause instanceof ApplicationRuntimeException)
 294    return findNestedException((ApplicationRuntimeException) cause);
 295   
 296    return cause;
 297    }
 298   
 299    /**
 300    * Checks to see if a specific event matches the name and message.
 301    *
 302    * @param message
 303    * exact message to search for
 304    * @param events
 305    * the list of events {@link #getInterceptedLogEvents()}
 306    * @param index
 307    * the index to check at
 308    */
 309    private void assertLoggedMessage(String message, List events, int index)
 310    {
 311    LoggingEvent e = (LoggingEvent) events.get(index);
 312   
 313    assertEquals("Message", message, e.getMessage());
 314    }
 315   
 316    /**
 317    * Checks the messages for all logged events for exact match against the supplied list.
 318    */
 319    protected void assertLoggedMessages(String[] messages)
 320    {
 321    List events = getInterceptedLogEvents();
 322   
 323    for (int i = 0; i < messages.length; i++)
 324    {
 325    assertLoggedMessage(messages[i], events, i);
 326    }
 327    }
 328   
 329    /**
 330    * Asserts that some capture log event matches the given message exactly.
 331    */
 332    protected void assertLoggedMessage(String message)
 333    {
 334    assertLoggedMessage(message, getInterceptedLogEvents());
 335    }
 336   
 337    /**
 338    * Asserts that some capture log event matches the given message exactly.
 339    *
 340    * @param message
 341    * to search for; success is finding a logged message contain the parameter as a
 342    * substring
 343    * @param events
 344    * from {@link #getInterceptedLogEvents()}
 345    */
 346    protected void assertLoggedMessage(String message, List events)
 347    {
 348    int count = events.size();
 349   
 350    for (int i = 0; i < count; i++)
 351    {
 352    LoggingEvent e = (LoggingEvent) events.get(i);
 353   
 354    String eventMessage = String.valueOf(e.getMessage());
 355   
 356    if (eventMessage.indexOf(message) >= 0)
 357    return;
 358    }
 359   
 360    throw new AssertionFailedError("Could not find logged message: " + message);
 361    }
 362   
 363    protected void assertLoggedMessagePattern(String pattern) throws Exception
 364    {
 365    assertLoggedMessagePattern(pattern, getInterceptedLogEvents());
 366    }
 367   
 368    protected void assertLoggedMessagePattern(String pattern, List events) throws Exception
 369    {
 370    setupMatcher();
 371   
 372    Pattern compiled = null;
 373   
 374    int count = events.size();
 375   
 376    for (int i = 0; i < count; i++)
 377    {
 378    LoggingEvent e = (LoggingEvent) events.get(i);
 379   
 380    String eventMessage = e.getMessage().toString();
 381   
 382    if (compiled == null)
 383    compiled = _compiler.compile(pattern);
 384   
 385    if (_matcher.contains(eventMessage, compiled))
 386    return;
 387   
 388    }
 389   
 390    throw new AssertionFailedError("Could not find logged message with pattern: " + pattern);
 391    }
 392   
 393    private void setupMatcher()
 394    {
 395    if (_compiler == null)
 396    _compiler = new Perl5Compiler();
 397   
 398    if (_matcher == null)
 399    _matcher = new Perl5Matcher();
 400    }
 401   
 402    /**
 403    * Convienience method for invoking {@link #buildFrameworkRegistry(String[])} with only a single
 404    * file.
 405    */
 406    protected Registry buildFrameworkRegistry(String file) throws Exception
 407    {
 408    return buildFrameworkRegistry(new String[]
 409    { file });
 410    }
 411   
 412    /**
 413    * Builds a minimal registry, containing only the specified files, plus the master module
 414    * descriptor (i.e., those visible on the classpath). Files are resolved using
 415    * {@link HiveMindTestCase#getResource(String)}.
 416    */
 417    protected Registry buildFrameworkRegistry(String[] files) throws Exception
 418    {
 419    ClassResolver resolver = getClassResolver();
 420   
 421    List descriptorResources = new ArrayList();
 422    for (int i = 0; i < files.length; i++)
 423    {
 424    Resource resource = getResource(files[i]);
 425   
 426    descriptorResources.add(resource);
 427    }
 428   
 429    ModuleDescriptorProvider provider = new XmlModuleDescriptorProvider(resolver,
 430    descriptorResources);
 431   
 432    return buildFrameworkRegistry(provider);
 433    }
 434   
 435    /**
 436    * Builds a registry, containing only the modules delivered by the specified
 437    * {@link org.apache.hivemind.ModuleDescriptorProvider}, plus the master module descriptor
 438    * (i.e., those visible on the classpath).
 439    */
 440    protected Registry buildFrameworkRegistry(ModuleDescriptorProvider customProvider)
 441    {
 442    ClassResolver resolver = getClassResolver();
 443   
 444    RegistryBuilder builder = new RegistryBuilder();
 445   
 446    builder.addModuleDescriptorProvider(new XmlModuleDescriptorProvider(resolver));
 447    builder.addModuleDescriptorProvider(customProvider);
 448   
 449    return builder.constructRegistry(Locale.getDefault());
 450    }
 451   
 452    /**
 453    * Builds a registry from exactly the provided resource; this registry will not include the
 454    * <code>hivemind</code> module.
 455    */
 456    protected Registry buildMinimalRegistry(Resource l) throws Exception
 457    {
 458    RegistryBuilder builder = new RegistryBuilder();
 459   
 460    return builder.constructRegistry(Locale.getDefault());
 461    }
 462   
 463    /**
 464    * Creates a <em>managed</em> control via
 465    * {@link MockControl#createStrictControl(java.lang.Class)}. The created control is remembered,
 466    * and will be invoked by {@link #replayControls()}, {@link #verifyControls()}, etc.
 467    * <p>
 468    * The class to mock may be either an interface or a class. The EasyMock class extension
 469    * (easymockclassextension-1.1.jar) and CGLIB (cglib-full-2.01.jar) must be present in the
 470    * latter case (new since 1.1).
 471    */
 472    protected MockControl newControl(Class mockClass)
 473    {
 474    MockControlFactory factory = mockClass.isInterface() ? _interfaceMockControlFactory
 475    : _classMockControlFactory;
 476   
 477    MockControl result = factory.newControl(mockClass);
 478   
 479    addControl(result);
 480   
 481    return result;
 482    }
 483   
 484    /**
 485    * Adds the control to the list of managed controls used by {@link #replayControls()} and
 486    * {@link #verifyControls()}.
 487    */
 488    protected void addControl(MockControl control)
 489    {
 490    _controls.add(control);
 491    }
 492   
 493    /**
 494    * Convienience for invoking {@link #newControl(Class)} and then invoking
 495    * {@link MockControl#getMock()} on the result.
 496    */
 497    protected Object newMock(Class mockClass)
 498    {
 499    return newControl(mockClass).getMock();
 500    }
 501   
 502    /**
 503    * Invokes {@link MockControl#replay()} on all controls created by {@link #newControl(Class)}.
 504    */
 505    protected void replayControls()
 506    {
 507    Iterator i = _controls.iterator();
 508    while (i.hasNext())
 509    {
 510    MockControl c = (MockControl) i.next();
 511    c.replay();
 512    }
 513    }
 514   
 515    /**
 516    * Invokes {@link org.easymock.MockControl#verify()} and {@link MockControl#reset()} on all
 517    * controls created by {@link #newControl(Class)}.
 518    */
 519   
 520    protected void verifyControls()
 521    {
 522    Iterator i = _controls.iterator();
 523    while (i.hasNext())
 524    {
 525    MockControl c = (MockControl) i.next();
 526    c.verify();
 527    c.reset();
 528    }
 529    }
 530   
 531    /**
 532    * Invokes {@link org.easymock.MockControl#reset()} on all controls.
 533    */
 534   
 535    protected void resetControls()
 536    {
 537    Iterator i = _controls.iterator();
 538    while (i.hasNext())
 539    {
 540    MockControl c = (MockControl) i.next();
 541    c.reset();
 542    }
 543    }
 544   
 545    /**
 546    * @deprecated To be removed in 1.2. Use {@link #newLocation()} instead.
 547    */
 548    protected Location fabricateLocation(int line)
 549    {
 550    String path = "/" + getClass().getName().replace('.', '/');
 551   
 552    Resource r = new ClasspathResource(getClassResolver(), path);
 553   
 554    return new LocationImpl(r, line);
 555    }
 556   
 557    private int _line = 1;
 558   
 559    /**
 560    * Returns a new {@link Location} instance. The resource is the test class, and the line
 561    * number increments by one from one for each invocation (thus each call will get a unique
 562    * instance not equal to any previously obtained instance).
 563    *
 564    * @since 1.1
 565    */
 566    protected Location newLocation()
 567    {
 568    return fabricateLocation(_line++);
 569    }
 570   
 571    /**
 572    * Returns a {@link DefaultClassResolver}. Repeated calls in the same test return the same
 573    * value.
 574    *
 575    * @since 1.1
 576    */
 577   
 578    protected ClassResolver getClassResolver()
 579    {
 580    if (_classResolver == null)
 581    _classResolver = new DefaultClassResolver();
 582   
 583    return _classResolver;
 584    }
 585   
 586    protected boolean matches(String input, String pattern) throws Exception
 587    {
 588    setupMatcher();
 589   
 590    Pattern compiled = _compiler.compile(pattern);
 591   
 592    return _matcher.matches(input, compiled);
 593    }
 594   
 595    }