Clover coverage report - Drools - 2.0-rc2
Coverage timestamp: Wed May 11 2005 07:12:26 BST
file stats: LOC: 572   Methods: 14
NCLOC: 396   Classes: 2
 
 Source file Conditionals Statements Methods TOTAL
SMFTestFrameWork.java 80% 96.6% 92.9% 95.6%
coverage coverage
 1    package org.drools.smf;
 2   
 3    /*
 4    * $Id: SMFTestFrameWork.java,v 1.29.2.3 2005/05/03 23:45:47 mproctor Exp $
 5    *
 6    * Copyright 2004 (C) The Werken Company. All Rights Reserved.
 7    *
 8    * Redistribution and use of this software and associated documentation
 9    * ("Software"), with or without modification, are permitted provided that the
 10    * following conditions are met:
 11    *
 12    * 1. Redistributions of source code must retain copyright statements and
 13    * notices. Redistributions must also contain a copy of this document.
 14    *
 15    * 2. Redistributions in binary form must reproduce the above copyright notice,
 16    * this list of conditions and the following disclaimer in the documentation
 17    * and/or other materials provided with the distribution.
 18    *
 19    * 3. The name "drools" must not be used to endorse or promote products derived
 20    * from this Software without prior written permission of The Werken Company.
 21    * For written permission, please contact bob@werken.com.
 22    *
 23    * 4. Products derived from this Software may not be called "drools" nor may
 24    * "drools" appear in their names without prior written permission of The Werken
 25    * Company. "drools" is a trademark of The Werken Company.
 26    *
 27    * 5. Due credit should be given to The Werken Company. (http://werken.com/)
 28    *
 29    * THIS SOFTWARE IS PROVIDED BY THE WERKEN COMPANY AND CONTRIBUTORS ``AS IS''
 30    * AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 31    * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 32    * ARE DISCLAIMED. IN NO EVENT SHALL THE WERKEN COMPANY OR ITS CONTRIBUTORS BE
 33    * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 34    * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 35    * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 36    * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 37    * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 38    * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 39    * POSSIBILITY OF SUCH DAMAGE.
 40    *
 41    */
 42   
 43    import java.io.BufferedReader;
 44    import java.io.InputStreamReader;
 45    import java.net.URL;
 46    import java.util.ArrayList;
 47    import java.util.HashMap;
 48    import java.util.HashSet;
 49    import java.util.List;
 50    import java.util.Map;
 51    import java.util.Set;
 52   
 53    import junit.framework.TestCase;
 54   
 55    import org.drools.MockWorkingMemory;
 56    import org.drools.WorkingMemory;
 57    import org.drools.rule.Declaration;
 58    import org.drools.rule.Rule;
 59    import org.drools.rule.RuleSet;
 60    import org.drools.spi.Condition;
 61    import org.drools.spi.ConditionException;
 62    import org.drools.spi.Consequence;
 63    import org.drools.spi.ConsequenceException;
 64    import org.drools.spi.Importer;
 65    import org.drools.spi.MockTuple;
 66    import org.drools.spi.ObjectType;
 67    import org.drools.spi.RuleBaseContext;
 68    import org.drools.spi.Tuple;
 69   
 70    /**
 71    * @author mproctor
 72    *
 73    * SMTTestFrameWork is a base class for unit testing Semantic Implementations
 74    * The semantic implementation unit test simply needs to extend this class along
 75    * with setup method that instructs SMFTEstFrameWork which semantic url to
 76    * instantiate for testing. public class JavaSemanticTest extends
 77    * SMFTestFrameWork { public JavaSemanticTest( String name ) { super( name ); }
 78    *
 79    * public void setUp() throws Exception { super.setUp("java"); } }
 80    *
 81    * Each class that extends SMFTestFrameWork must create 3 data files;
 82    * conditions.data, consequences.data. Each file is read depending the testType,
 83    * a List of the specified tests extracted from the file; usig the delimeter
 84    * <!--drools-test--!>to seperate each test block.
 85    *
 86    * Each testType has a corresponding private helper method to instantiate a
 87    * Condition, Consequence for each test using the specified parameters
 88    */
 89    public abstract class SMFTestFrameWork extends TestCase
 90    {
 91    /** The List of tests extracted from the data file */
 92    private List tests;
 93   
 94    /** The test type; conditions, consequences */
 95    private String testType;
 96   
 97    /** The SemanticModule implementation return from the SemanticRepository */
 98    private SemanticModule module;
 99   
 100    /** The SemanticRepository */
 101    private SemanticsRepository repository;
 102   
 103    private String newline = System.getProperty( "line.separator" );
 104   
 105    private Importer importer;
 106   
 107    private RuleBaseContext ruleBaseContext;
 108   
 109  6 public SMFTestFrameWork(String name)
 110    {
 111  6 super( name );
 112    // use the method name, minus the "test" string, to specify the testType
 113  6 this.testType = name.substring( 4 ).toLowerCase( );
 114    }
 115   
 116    /**
 117    * Reads in the specified data file and extracts to a List of tests using
 118    * the delimter <!--drools-test--!>
 119    */
 120  6 public void setUp(String semantic,
 121    Importer importer) throws Exception
 122    {
 123  6 if ( !"conditions".equals( testType ) && !"consequences".equals( testType ) )
 124    {
 125  0 return;
 126    }
 127   
 128  6 ClassLoader cl = Thread.currentThread( ).getContextClassLoader( );
 129  6 URL semanticTests = cl.getResource( "data/" + semantic + "-" + testType + ".data" );
 130  6 BufferedReader in = new BufferedReader( new InputStreamReader( semanticTests.openStream( ) ) );
 131  6 StringBuffer buffer = new StringBuffer( );
 132  6 String blockMarker = "<!--drools-test--!>";
 133  6 String line;
 134  6 tests = new ArrayList( );
 135  6 while ( in.ready( ) )
 136    {
 137  253 line = in.readLine( ) + newline;
 138  253 if ( line.startsWith( blockMarker ) )
 139    {
 140  81 tests.add( buffer.toString( ) );
 141  81 buffer = new StringBuffer( );
 142    }
 143    else
 144    {
 145  172 buffer.append( line );
 146    }
 147    }
 148  6 tests.add( buffer.toString( ) );
 149   
 150  6 this.repository = DefaultSemanticsRepository.getInstance( );
 151  6 module = this.repository.lookupSemanticModule( "http://drools.org/semantics/" + semantic );
 152   
 153  6 this.importer = importer;
 154   
 155  6 this.ruleBaseContext = new RuleBaseContext( );
 156   
 157    }
 158   
 159    /**
 160    * Tests each of the extracted tests from conditions.data
 161    */
 162  3 public void testConditions() throws Exception
 163    {
 164    // Setup
 165  3 int testNumber = 0;
 166  3 MockTuple tuple;
 167   
 168  3 DefaultConfiguration cheeseConfiguration = new DefaultConfiguration( "test1" );
 169  3 cheeseConfiguration.setText( Cheese.class.getName( ) );
 170  3 ObjectTypeFactory objectTypeFactory = module.getObjectTypeFactory( "class" );
 171   
 172  3 final RuleSet ruleSet = new RuleSet( "test RuleSet",
 173    this.ruleBaseContext );
 174  3 final Rule rule = new Rule( "Test Rule 1",
 175    ruleSet );
 176  3 rule.setImporter( new DefaultImporter( ) );
 177  3 ObjectType cheeseType = objectTypeFactory.newObjectType( rule,
 178    this.ruleBaseContext,
 179    cheeseConfiguration );
 180   
 181  3 tuple = new MockTuple( );
 182  3 rule.setImporter( new DefaultImporter( ) );
 183  3 tuple.setRule( rule );
 184  3 tuple.setWorkingMemory( new MockWorkingMemory( ) );
 185   
 186    // simple condition checks
 187  3 assertTrue( testCondition( testNumber++,
 188    tuple,
 189    rule ) ); // 0
 190  3 assertFalse( testCondition( testNumber++,
 191    tuple,
 192    rule ) ); // 1
 193  3 assertTrue( testCondition( testNumber++,
 194    tuple,
 195    rule ) ); // 2
 196  3 assertTrue( testCondition( testNumber++,
 197    tuple,
 198    rule ) ); // 3
 199  3 assertTrue( testCondition( testNumber++,
 200    tuple,
 201    rule ) ); // 4
 202   
 203  3 Declaration camembertDecl = rule.addParameterDeclaration( "camembert",
 204    cheeseType );
 205  3 Declaration stiltonDecl = rule.addParameterDeclaration( "stilton",
 206    cheeseType );
 207  3 rule.setImporter( this.importer );
 208    // condition check with a single declaration
 209  3 tuple.put( camembertDecl,
 210    new Cheese( "camembert" ) );
 211  3 assertTrue( testCondition( testNumber++,
 212    tuple,
 213    rule ) ); // 5
 214  3 assertFalse( testCondition( testNumber++,
 215    tuple,
 216    rule ) ); // 6
 217   
 218    // condition check with a single declaration
 219  3 tuple = new MockTuple( );
 220  3 rule.setImporter( this.importer );
 221  3 tuple.setRule( rule );
 222  3 tuple.setWorkingMemory( new MockWorkingMemory( ) );
 223  3 tuple.put( stiltonDecl,
 224    new Cheese( "stilton" ) );
 225  3 assertTrue( testCondition( testNumber++,
 226    tuple,
 227    rule ) ); // 7
 228  3 assertFalse( testCondition( testNumber++,
 229    tuple,
 230    rule ) ); // 8
 231   
 232    // condition check with two declarations
 233  3 tuple = new MockTuple( );
 234  3 rule.setImporter( this.importer );
 235  3 tuple.setRule( rule );
 236  3 tuple.setWorkingMemory( new MockWorkingMemory( ) );
 237  3 tuple.put( stiltonDecl,
 238    new Cheese( "stilton" ) );
 239  3 tuple.put( camembertDecl,
 240    new Cheese( "camembert" ) );
 241  3 assertFalse( testCondition( testNumber++,
 242    tuple,
 243    rule ) ); // 9
 244  3 assertTrue( testCondition( testNumber++,
 245    tuple,
 246    rule ) ); // 10
 247  3 assertTrue( testCondition( testNumber++,
 248    tuple,
 249    rule ) ); // 11
 250  3 assertFalse( testCondition( testNumber++,
 251    tuple,
 252    rule ) ); // 12
 253   
 254    // condition check with 2 declarations and application data
 255  3 WorkingMemory workingMemory = new MockWorkingMemory( );
 256  3 workingMemory.setApplicationData( "bites",
 257    new Integer( 3 ) );
 258  3 workingMemory.setApplicationData( "favouriteCheese",
 259    new Cheese( "camembert" ) );
 260  3 tuple.setWorkingMemory( workingMemory );
 261   
 262  3 HashMap applicationData = new HashMap( );
 263  3 applicationData.put( "bites",
 264    Integer.class );
 265  3 applicationData.put( "favouriteCheese",
 266    Cheese.class );
 267   
 268  3 rule.setApplicationData( applicationData );
 269   
 270  3 assertTrue( testCondition( testNumber++,
 271    tuple,
 272    rule ) ); // 13
 273  3 assertFalse( testCondition( testNumber++,
 274    tuple,
 275    rule ) ); // 14
 276  3 assertTrue( testCondition( testNumber++,
 277    tuple,
 278    rule ) ); // 15
 279   
 280    // test code works no matter what the order of decl are
 281  3 tuple = new MockTuple( );
 282  3 rule.setImporter( this.importer );
 283  3 rule.setApplicationData( new HashMap( ) );
 284  3 tuple.setRule( rule );
 285  3 workingMemory = new MockWorkingMemory( );
 286  3 tuple.setWorkingMemory( workingMemory );
 287   
 288  3 DefaultConfiguration stringConfiguration = new DefaultConfiguration( "test2" );
 289  3 stringConfiguration.setText( String.class.getName( ) );
 290  3 ObjectType stringType = objectTypeFactory.newObjectType( rule,
 291    this.ruleBaseContext,
 292    stringConfiguration );
 293  3 Declaration favouriteCheeseDecl = rule.addParameterDeclaration( "favouriteCheese",
 294    stringType );
 295   
 296  3 tuple.put( favouriteCheeseDecl,
 297    "camembert" );
 298  3 tuple.put( camembertDecl,
 299    new Cheese( "camembert" ) );
 300  3 assertTrue( testCondition( testNumber++,
 301    tuple,
 302    rule ) ); // 16
 303  3 assertTrue( testCondition( testNumber++,
 304    tuple,
 305    rule ) ); // 17
 306   
 307    // test condition syntax with commas - Drools Issue #77
 308  3 assertTrue( testCondition( testNumber++,
 309    tuple,
 310    rule ) ); // 18
 311   
 312    // test exceptions
 313  3 rule.setImporter( this.importer );
 314  3 tuple.setRule( rule );
 315  3 try
 316    {
 317  3 testCondition( testNumber++,
 318    tuple,
 319    rule );
 320  0 fail( "Condition should throw an exception" );
 321    }
 322    catch ( ConditionException e )
 323    {
 324  3 assertEquals( rule,
 325    e.getRule( ) );
 326  3 assertEquals( tests.get( testNumber - 1 ),
 327    e.getInfo( ) );
 328    }
 329   
 330    // need to add a test for declaration order
 331   
 332    // 20
 333    // test imports
 334  3 tuple = new MockTuple( );
 335  3 rule.setImporter( this.importer );
 336  3 tuple.setRule( rule );
 337  3 workingMemory = new MockWorkingMemory( );
 338  3 tuple.setWorkingMemory( workingMemory );
 339   
 340  3 assertTrue( testCondition( testNumber++,
 341    tuple,
 342    rule ) ); // 20
 343    }
 344   
 345    /**
 346    * private helper method to test each of the extracted conditions
 347    */
 348  63 private boolean testCondition(int testNumber,
 349    Tuple tuple,
 350    Rule rule) throws Exception
 351    {
 352  63 ConditionFactory conditionFactory = module.getConditionFactory( "condition" );
 353  63 DefaultConfiguration conditionConfiguration = new DefaultConfiguration( "test" + testNumber );
 354  63 conditionConfiguration.setText( (String) tests.get( testNumber ) );
 355  63 Condition condition = conditionFactory.newCondition( rule,
 356    this.ruleBaseContext,
 357    conditionConfiguration )[0];
 358  63 return condition.isAllowed( tuple );
 359    }
 360   
 361    /**
 362    * Tests each of the extracted tests from consequences.data
 363    */
 364  3 public void testConsequences() throws Exception
 365    {
 366  3 MockTuple tuple;
 367   
 368  3 DefaultConfiguration cheeseConfiguration = new DefaultConfiguration( "test1" );
 369  3 cheeseConfiguration.setText( Cheese.class.getName( ) );
 370  3 ObjectTypeFactory objectTypeFactory = module.getObjectTypeFactory( "class" );
 371   
 372  3 final RuleSet ruleSet = new RuleSet( "test RuleSet",
 373    this.ruleBaseContext );
 374  3 Rule rule = new Rule( "Test Rule 1",
 375    ruleSet );
 376   
 377  3 rule.setImporter( new DefaultImporter( ) );
 378  3 ObjectType cheeseType = objectTypeFactory.newObjectType( rule,
 379    this.ruleBaseContext,
 380    cheeseConfiguration );
 381   
 382  3 tuple = new MockTuple( );
 383  3 rule.setImporter( this.importer );
 384  3 tuple.setRule( rule );
 385  3 tuple.setWorkingMemory( new MockWorkingMemory( ) );
 386   
 387    // simple condition, no declrations
 388  3 testConsequence( 0,
 389    tuple,
 390    rule );
 391   
 392    // need to declare so that the tests have SMFTestFrameWork.Cheese
 393    // imported
 394  3 Declaration camembertDecl = rule.addParameterDeclaration( "camembert",
 395    cheeseType );
 396  3 Declaration stiltonDecl = rule.addParameterDeclaration( "stilton",
 397    cheeseType );
 398   
 399  3 Cheese camembert = new Cheese( "camembert" );
 400  3 Cheese stilton = new Cheese( "stilton" );
 401  3 tuple.put( camembertDecl,
 402    camembert );
 403  3 tuple.put( stiltonDecl,
 404    stilton );
 405   
 406    // tests nested classes, public static class SMFTestFrameWork.Cheese,
 407    // works
 408  3 testConsequence( 1,
 409    tuple,
 410    rule );
 411   
 412    // now start doing tests with declarations
 413    // first confirm that biteLeft is 3
 414  3 assertEquals( 3,
 415    camembert.getBitesLeft( ) );
 416  3 assertEquals( 3,
 417    stilton.getBitesLeft( ) );
 418    // execute consequence that calles eatCheese()
 419  3 testConsequence( 2,
 420    tuple,
 421    rule );
 422    // camembert should be eaten once, and stilton twice
 423  3 assertEquals( 2,
 424    camembert.getBitesLeft( ) );
 425  3 assertEquals( 1,
 426    stilton.getBitesLeft( ) );
 427   
 428    // test condition with declarations and application data
 429  3 WorkingMemory workingMemory = new MockWorkingMemory( );
 430  3 workingMemory.setApplicationData( "bites",
 431    new Integer( 3 ) );
 432  3 workingMemory.setApplicationData( "cheeses",
 433    new HashMap( ) );
 434   
 435  3 HashMap applicationData = new HashMap( );
 436  3 applicationData.put( "bites",
 437    Integer.class );
 438  3 applicationData.put( "cheeses",
 439    HashMap.class );
 440   
 441  3 rule.setApplicationData( applicationData );
 442   
 443  3 tuple.setWorkingMemory( workingMemory );
 444  3 testConsequence( 3,
 445    tuple,
 446    rule );
 447  3 assertEquals( 1,
 448    camembert.getBitesLeft( ) );
 449  3 assertEquals( 0,
 450    stilton.getBitesLeft( ) );
 451  3 Map map = (Map) workingMemory.getApplicationData( "cheeses" );
 452  3 assertEquals( camembert,
 453    map.get( "favourite cheese" ) );
 454  3 assertEquals( 3,
 455    ((Integer) map.get( "bites" )).intValue( ) );
 456   
 457    // 4
 458    // test exceptions
 459  3 rule = new Rule( "Test Rule 1",
 460    ruleSet );
 461  3 rule.setImporter( this.importer );
 462  3 tuple.setRule( rule );
 463  3 try
 464    {
 465  3 testConsequence( 6,
 466    tuple,
 467    rule );
 468  0 fail( "Consequence should throw an exception" );
 469    }
 470    catch ( ConsequenceException e )
 471    {
 472  3 assertEquals( rule,
 473    e.getRule( ) );
 474    }
 475   
 476    // 7
 477    // test imports
 478  3 tuple = new MockTuple( );
 479  3 rule = new Rule( "Test Rule 1",
 480    ruleSet );
 481  3 rule.setImporter( this.importer );
 482  3 tuple.setRule( rule );
 483  3 workingMemory = new MockWorkingMemory( );
 484  3 tuple.setWorkingMemory( workingMemory );
 485  3 try
 486    {
 487  3 testConsequence( 7,
 488    tuple,
 489    rule );
 490    }
 491    catch ( ConsequenceException e )
 492    {
 493  0 fail( "Consequence should execute without errors" );
 494    }
 495    }
 496   
 497    /**
 498    * private helper method to test each of the extracted consequences
 499    */
 500  18 private void testConsequence(int testNumber,
 501    Tuple tuple,
 502    Rule rule) throws Exception
 503    {
 504  18 ConsequenceFactory consequenceFactory = module.getConsequenceFactory( "consequence" );
 505  18 DefaultConfiguration consequenceConfiguration = new DefaultConfiguration( "test" + testNumber );
 506  18 consequenceConfiguration.setText( (String) tests.get( testNumber ) );
 507  18 Consequence consequence = consequenceFactory.newConsequence( rule,
 508    this.ruleBaseContext,
 509    consequenceConfiguration );
 510  18 consequence.invoke( tuple );
 511    }
 512   
 513  2 public static boolean conditionExceptionTest() throws Exception
 514    {
 515  2 throw new Exception( "this is a condition exception" );
 516    }
 517   
 518  2 public static void consequenceExceptionTest() throws Exception
 519    {
 520  2 throw new Exception( "this is a consequence exception" );
 521    }
 522   
 523    /**
 524    * Simple nested class used with testing
 525    */
 526    public static class Cheese
 527    {
 528    private String name;
 529   
 530    private int bitesLeft = 3;
 531   
 532  36 public Cheese(String name)
 533    {
 534  36 this.name = name;
 535    }
 536   
 537  15 public String getName()
 538    {
 539  15 return this.name;
 540    }
 541   
 542  18 public void eatCheese()
 543    {
 544  18 bitesLeft--;
 545    }
 546   
 547  39 public int getBitesLeft()
 548    {
 549  39 return this.bitesLeft;
 550    }
 551   
 552  18 public boolean equals(Object object)
 553    {
 554  18 if ( this == object )
 555    {
 556  3 return true;
 557    }
 558   
 559  15 if ( object == null || getClass( ) != object.getClass( ) )
 560    {
 561  0 return false;
 562    }
 563   
 564  15 return this.name.equals( ((Cheese) object).name );
 565    }
 566   
 567  0 public int hashCode()
 568    {
 569  0 return this.name.hashCode( );
 570    }
 571    }
 572    }