Coverage Report - pt.digitalis.dif.sanitycheck.manager.SanityCheckManager
 
Classes in this File Line Coverage Branch Coverage Complexity
SanityCheckManager
0%
0/75
0%
0/62
4,25
 
 1  
 /**
 2  
  * 2009, Digitalis Informatica. All rights reserved. Distribuicao e Gestao de Informatica, Lda. Estrada de Paco de Arcos
 3  
  * num.9 - Piso -1 2780-666 Paco de Arcos Telefone: (351) 21 4408990 Fax: (351) 21 4408999 http://www.digitalis.pt
 4  
  */
 5  
 
 6  
 package pt.digitalis.dif.sanitycheck.manager;
 7  
 
 8  
 import java.util.ArrayList;
 9  
 import java.util.Date;
 10  
 import java.util.HashMap;
 11  
 import java.util.List;
 12  
 import java.util.Map;
 13  
 import java.util.Map.Entry;
 14  
 
 15  
 import pt.digitalis.dif.controller.interfaces.IDIFContext;
 16  
 import pt.digitalis.dif.ioc.DIFIoCRegistry;
 17  
 import pt.digitalis.dif.sanitycheck.ExecutionResult;
 18  
 import pt.digitalis.dif.sanitycheck.ISanityCheckTestSuite;
 19  
 import pt.digitalis.dif.sanitycheck.SanityCheckResult;
 20  
 import pt.digitalis.dif.sanitycheck.TestResult;
 21  
 import pt.digitalis.dif.utils.logging.DIFLogger;
 22  
 import pt.digitalis.log.LogLevel;
 23  
 
 24  
 /**
 25  
  * @author Pedro Viegas <a href="mailto:pviegas@digitalis.pt">pviegas@digitalis.pt</a><br/>
 26  
  * @created 14 de Jul de 2011
 27  
  */
 28  0
 public class SanityCheckManager {
 29  
 
 30  
     /** Sanity check Logger Message Prefix */
 31  
     protected static final String _SANITY_CHECK_LOGGER_PREFIX = "Sanity Check: ";
 32  
 
 33  
     /** Sanity check Logger Message */
 34  
     protected static final String CIRCULAR_DEPENDENCY = _SANITY_CHECK_LOGGER_PREFIX
 35  
             + "Circular dependency detected on TestSuite \"%1\" and \"%2\"";
 36  
 
 37  
     /** the test execution order according to the declared dependencies */
 38  0
     private static List<String> testSuiteExecutionOrder = null;
 39  
 
 40  
     /** the test suite ordered list after parsing all contributed ISanityCheckTestSuite */
 41  0
     static private Map<String, SanityCheckTestSuiteDefinition> testSuites = null;
 42  
 
 43  
     /**
 44  
      * Inspector for the 'testSuiteExecutionOrder' attribute.
 45  
      * 
 46  
      * @return the testSuiteExecutionOrder value
 47  
      */
 48  
     private static List<String> getTestSuiteExecutionOrder()
 49  
     {
 50  0
         if (testSuiteExecutionOrder == null)
 51  0
             initialize();
 52  
 
 53  0
         return testSuiteExecutionOrder;
 54  
     }
 55  
 
 56  
     /**
 57  
      * Inspector for the 'testSuites' attribute.
 58  
      * 
 59  
      * @return the testSuites value
 60  
      */
 61  
     public static Map<String, SanityCheckTestSuiteDefinition> getTestSuites()
 62  
     {
 63  0
         if (testSuites == null)
 64  0
             initialize();
 65  
 
 66  0
         return testSuites;
 67  
     }
 68  
 
 69  
     /**
 70  
      * Initializes the manager. <br/>
 71  
      * Will read all declared {@link ISanityCheckTestSuite} classes and mount the test suites and their attributes and
 72  
      * dependencies.
 73  
      */
 74  
     private static void initialize()
 75  
     {
 76  
         // Test suite registry holder
 77  0
         testSuites = new HashMap<String, SanityCheckTestSuiteDefinition>();
 78  
 
 79  
         // Get all ISanityCheckTestSuite contributions from the IoC
 80  0
         Map<String, ISanityCheckTestSuite> suiteEntries = DIFIoCRegistry.getRegistry().getImplementationsMap(
 81  0
                 ISanityCheckTestSuite.class);
 82  
 
 83  
         // Add all to the registry attribute "testSuites"
 84  0
         for (Entry<String, ISanityCheckTestSuite> suiteEntry: suiteEntries.entrySet())
 85  
         {
 86  
             // Create a definition object, that will parse and determine execution for each test suite
 87  0
             if (suiteEntry.getValue() instanceof ISanityCheckTestSuite)
 88  0
                 testSuites.put(suiteEntry.getKey(),
 89  0
                         new SanityCheckTestSuiteDefinition(suiteEntry.getKey(), suiteEntry.getValue()));
 90  
             else
 91  0
                 DIFLogger.getLogger().warn(
 92  0
                         _SANITY_CHECK_LOGGER_PREFIX + "Class \"" + suiteEntry.getValue().getClass().getSimpleName()
 93  0
                                 + "\" must implement " + ISanityCheckTestSuite.class.getSimpleName());
 94  
         }
 95  
 
 96  
         // Determine the running order according to the determined dependencies
 97  0
         testSuiteExecutionOrder = new ArrayList<String>();
 98  
 
 99  
         // For all existing parse recursively through all dependencies
 100  0
         for (SanityCheckTestSuiteDefinition testSuite: testSuites.values())
 101  0
             if (!testSuiteExecutionOrder.contains(testSuite.getId()))
 102  
                 // New testSuite. Add all dependencies which are not already declared
 103  0
                 testSuiteExecutionOrder = parseInheritedDependencies(testSuiteExecutionOrder, testSuite);
 104  0
     }
 105  
 
 106  
     /**
 107  
      * Parses for all dependent test suites and their recursive dependencies.<br/>
 108  
      * Will detect and ignore circular dependencies.<br/>
 109  
      * <br/>
 110  
      * The provided temporaryDependencies attribute is used to detect already declared testSuites and as such no need to
 111  
      * crawl inside their dependency tree.
 112  
      * 
 113  
      * @param temporaryDependencies
 114  
      *            currently already parsed dependencies
 115  
      * @param stackDependencyParsing
 116  
      *            a stack of the current dependencies in recursive analisis. Used to prevent circular dependencies
 117  
      * @param suiteDefinition
 118  
      *            the suite to check dependencies
 119  
      * @return the current test suite recursive dependencies list
 120  
      */
 121  
     private static List<String> parseInheritedDependencies(List<String> temporaryDependencies,
 122  
             List<String> stackDependencyParsing, SanityCheckTestSuiteDefinition suiteDefinition)
 123  
     {
 124  0
         for (SanityCheckTestSuiteDefinition dependentSuide: suiteDefinition.getDependencies())
 125  
         {
 126  0
             if (stackDependencyParsing.contains(dependentSuide.getId()))
 127  
             {
 128  
                 // The dependency is already referenced in the currently executing stack. Circular dependency.
 129  
                 // Report and ignore
 130  0
                 DIFLogger.getLogger().warn(
 131  0
                         CIRCULAR_DEPENDENCY.replace("%1", suiteDefinition.getId())
 132  0
                                 .replace("%2", dependentSuide.getId()));
 133  
 
 134  
             }
 135  0
             else if (!temporaryDependencies.contains(dependentSuide.getId()))
 136  
             {
 137  
                 // Add recursive dependencies
 138  0
                 temporaryDependencies = parseInheritedDependencies(temporaryDependencies, dependentSuide);
 139  
 
 140  
                 // Add the current dependency
 141  0
                 temporaryDependencies.add(dependentSuide.getId());
 142  
             }
 143  
         }
 144  
 
 145  0
         temporaryDependencies.add(suiteDefinition.getId());
 146  
 
 147  0
         return temporaryDependencies;
 148  
     }
 149  
 
 150  
     /**
 151  
      * Parses for all dependent test suites and their recursive dependencies.<br/>
 152  
      * Will detect and ignore circular dependencies.<br/>
 153  
      * <br/>
 154  
      * The provided temporaryDependencies attribute is used to detect already declared testSuites and as such no need to
 155  
      * crawl inside their dependency tree.
 156  
      * 
 157  
      * @param temporaryDependencies
 158  
      *            currently already parsed dependencies
 159  
      * @param suiteDefinition
 160  
      *            the suite to check dependencies
 161  
      * @return the current test suite recursive dependencies list
 162  
      */
 163  
     private static List<String> parseInheritedDependencies(List<String> temporaryDependencies,
 164  
             SanityCheckTestSuiteDefinition suiteDefinition)
 165  
     {
 166  0
         return parseInheritedDependencies(temporaryDependencies, new ArrayList<String>(), suiteDefinition);
 167  
     }
 168  
 
 169  
     /**
 170  
      * Execute all tests
 171  
      * 
 172  
      * @param context
 173  
      *            the current DIF context
 174  
      * @return T if all tests ran successfully
 175  
      */
 176  
     public static boolean runAllTests(IDIFContext context)
 177  
     {
 178  0
         return runAllTests(context, false);
 179  
     }
 180  
 
 181  
     /**
 182  
      * Execute all tests.
 183  
      * 
 184  
      * @param context
 185  
      *            the current DIF context
 186  
      * @param testOnlyErrors
 187  
      *            the test only errors
 188  
      * @return T if all tests ran successfully
 189  
      */
 190  
     public static boolean runAllTests(IDIFContext context, boolean testOnlyErrors)
 191  
     {
 192  0
         boolean result = true;
 193  
 
 194  0
         DIFLogger.getLogger().info(_SANITY_CHECK_LOGGER_PREFIX + "Starting Sanity Check tests...");
 195  
 
 196  
         // Execute all testSuites, by the correct order
 197  0
         for (String testSuiteID: getTestSuiteExecutionOrder())
 198  
         {
 199  0
             SanityCheckTestSuiteDefinition testSuide = getTestSuites().get(testSuiteID);
 200  0
             ExecutionResult testsResult = runTestSuiteTests(testSuide, context).getExecutionsResult();
 201  0
             boolean testsOK = (testOnlyErrors ? (testsResult != ExecutionResult.FAILED)
 202  0
                     : (testsResult == ExecutionResult.PASSED));
 203  
 
 204  
             // First error, and log is not in INFO, so start tests message not rendered previously.
 205  
             // Print warn heading...
 206  0
             if (!DIFLogger.getLogger().isInfoEnabled() && result && !testsOK)
 207  0
                 DIFLogger.getLogger().warn(_SANITY_CHECK_LOGGER_PREFIX + "Starting Sanity Check tests...");
 208  
 
 209  
             // Log according to result...
 210  0
             DIFLogger.getLogger().log(testsOK ? LogLevel.INFO : LogLevel.WARN,
 211  0
                     "    ยป " + testSuide.getName() + " (" + testSuiteID + "): " + testsResult.toString());
 212  
 
 213  0
             result = result && testsOK;
 214  0
             if (testOnlyErrors && !result)
 215  
             {
 216  0
                 break;
 217  
             }
 218  
         }
 219  
 
 220  
         // Feedback, according to the result
 221  0
         if (result)
 222  0
             DIFLogger.getLogger().info(_SANITY_CHECK_LOGGER_PREFIX + "All Sanity Checks ran with Success.");
 223  
         else
 224  0
             DIFLogger.getLogger().warn(_SANITY_CHECK_LOGGER_PREFIX + "Errors while running Sanity Checks.");
 225  
 
 226  0
         return result;
 227  
     }
 228  
 
 229  
     /**
 230  
      * Run all tests of the {@link ISanityCheckTestSuite}
 231  
      * 
 232  
      * @param suiteDefinition
 233  
      *            the test suite to run the tests
 234  
      * @param context
 235  
      *            the current DIF context
 236  
      * @return the executing result
 237  
      */
 238  
     private static SanityCheckResult runTestSuiteTests(SanityCheckTestSuiteDefinition suiteDefinition,
 239  
             IDIFContext context)
 240  
     {
 241  0
         suiteDefinition.setExecutionResult(new SanityCheckResult(suiteDefinition, ExecutionResult.EXECUTING));
 242  0
         suiteDefinition.getExecutionResult().setStartTime(new Date());
 243  
 
 244  
         try
 245  
         {
 246  0
             suiteDefinition.runTestsSetup();
 247  
 
 248  0
             boolean warnings = false;
 249  0
             boolean errors = false;
 250  
 
 251  
             // Run all tests, adding each result to the execution result...
 252  0
             for (TestMethodDefinition test: suiteDefinition.getTestMethods())
 253  
             {
 254  0
                 TestResult testResult = test.run(context);
 255  
 
 256  
                 // Set the test name if empty to the test method
 257  0
                 if (testResult.getName() == null)
 258  0
                     testResult.setName(test.getName());
 259  
 
 260  0
                 errors = errors || testResult.getExecutionResult() == ExecutionResult.FAILED;
 261  0
                 warnings = warnings || testResult.getExecutionResult() == ExecutionResult.WARNING;
 262  
 
 263  0
                 suiteDefinition.getExecutionResult().addTestResult(testResult);
 264  
             }
 265  
 
 266  0
             suiteDefinition.runTestsFinalize();
 267  
 
 268  0
             suiteDefinition.getExecutionResult().setExecutionsResult(
 269  0
                     errors ? ExecutionResult.FAILED : warnings ? ExecutionResult.WARNING : ExecutionResult.PASSED);
 270  
         }
 271  0
         catch (Exception e)
 272  
         {
 273  0
             e.printStackTrace();
 274  
 
 275  0
             suiteDefinition.getExecutionResult().setExecutionsResult(ExecutionResult.FAILED);
 276  0
             suiteDefinition.getExecutionResult().setExecutionError(e.getMessage());
 277  
         }
 278  0
         suiteDefinition.getExecutionResult().setEndTime(new Date());
 279  
 
 280  0
         return suiteDefinition.getExecutionResult();
 281  
     }
 282  
 }