View Javadoc

1   /*
2    * Copyright 2006-2011 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.test;
17  
18  import org.kuali.rice.core.api.lifecycle.Lifecycle;
19  
20  import java.lang.annotation.ElementType;
21  import java.lang.annotation.Inherited;
22  import java.lang.annotation.Retention;
23  import java.lang.annotation.RetentionPolicy;
24  import java.lang.annotation.Target;
25  import java.util.ArrayList;
26  import java.util.List;
27  
28  /**
29   * Test case which supports common styles of "baselining" the test environment before/after running
30   * a unit test.
31   * Currently supports three modes, which are specifyiable either via constructor, {@link #getMode()} override,
32   * or by annotation:
33   * <dl>
34   *   <dt>NONE</dt>
35   *   <dd>No baselining is performed.  Because the base RiceTestCase includes the ClearDatabaseLifecycle by default, this lifecycle is
36   *       explicitly omitted</dd>
37   *   <dt>CLEAR_DB</dt>
38   *   <dd>The database is cleared for each test.  The suite ClearDatabaseLifecycle is omitted (since it's getting cleared each test
39   *       anyway)</dd>
40   *   <dt>ROLLBACK_CLEAR_DB</dt>
41   *   <dd>A TransactionalLifecycle is installed that wraps each test and rolls back data.  The suite ClearDatabaseLifecycle will be
42   *       invoked once initially, and subsequently if the test has detected that the environment has been left "dirty" by a previous
43   *       test.  After a successful rollback, the test environment is marked clean again.</dd>
44   *   <dd>A TransactionalLifecycle is installed that wraps each test and rolls back data.</dd>
45   * </dl>
46   * 
47   * The BaselineMode annotation can be used on a per-test-class basis to indicate to the base class which mode to use for test
48   * subclass.  It accepts a {@link Mode} value.
49   * 
50   * @author Kuali Rice Team (rice.collab@kuali.org)
51   *
52   */
53  public class BaselineTestCase extends BaseModuleTestCase {
54      /**
55       * Enum of "baselining" modes that this test case supports
56       */
57      public static enum Mode {
58          CLEAR_DB, ROLLBACK_CLEAR_DB, ROLLBACK, NONE
59      }
60  
61      @Target({ElementType.TYPE})
62      @Inherited
63      @Retention(RetentionPolicy.RUNTIME)
64      public @interface BaselineMode {
65          Mode value();
66      }
67  
68      private Mode mode = Mode.NONE;
69      
70      /**
71       * Whether the test environment is in a "dirty" state.  Each time the unit test starts up
72       * dirty is set to true.  If a subclass installs the {@link TransactionalLifecycle} then
73       * it should clear the dirty flag.  This flag can be used to perform cleanup in case a previous
74       * test left the test environment in a "dirty" state.
75       */
76      protected static boolean dirty = false;
77  
78      // propagate constructors
79      public BaselineTestCase(String moduleName) {
80          super(moduleName);
81          readModeAnnotation();
82      }
83  
84      /**
85       * Adds the ability to specify Mode
86       */
87      public BaselineTestCase(String moduleName, Mode mode) {
88          super(moduleName);
89          if (mode == null) throw new IllegalArgumentException("Mode cannot be null");
90          this.mode = mode;
91      }
92  
93      private void readModeAnnotation() {
94          BaselineMode m = this.getClass().getAnnotation(BaselineMode.class);
95          if (m != null) {
96              if (m.value() != null) {
97                  mode = m.value();
98              }
99          }
100     }
101 
102     /**
103      * @return the configured mode
104      */
105     protected Mode getMode() {
106         return mode;
107     }
108 
109     /**
110      * Overridden to set dirty=true each time
111      * @see org.kuali.rice.test.RiceTestCase#setUp()
112      */
113     @Override
114     public void setUp() throws Exception {
115         super.setUp();
116         dirty = true;
117     }
118 
119     @Override
120     protected List<Lifecycle> getPerTestLifecycles() {
121         switch (mode) {
122             case ROLLBACK_CLEAR_DB: return getRollbackClearDbPerTestLifecycles();
123             case ROLLBACK: return getRollbackTestLifecycles();
124             case CLEAR_DB: return getClearDbPerTestLifecycles();
125             case NONE: return super.getPerTestLifecycles();
126             default:
127                 throw new RuntimeException("Invalid mode specified: " + mode);        
128         }
129     }
130 
131     /**
132      * @return the per-test lifecycles for clearing the database
133      */
134     protected List<Lifecycle> getClearDbPerTestLifecycles() {
135         List<Lifecycle> lifecycles = super.getPerTestLifecycles();
136         lifecycles.add(0, new ClearDatabaseLifecycle(getPerTestTablesToClear(), getPerTestTablesNotToClear()));
137         return lifecycles;
138     }
139     
140     protected List<String> getPerTestTablesToClear() {
141     	return new ArrayList<String>();
142     }
143 
144     protected List<String> getPerTestTablesNotToClear() {
145     	return new ArrayList<String>();
146     }
147     
148     /**
149      * @return the per-test lifecycles for rolling back & clearing the database
150      */
151     protected List<Lifecycle> getRollbackClearDbPerTestLifecycles() {
152         List<Lifecycle> lifecycles = super.getPerTestLifecycles();
153         lifecycles.add(0, new TransactionalLifecycle() {
154             @Override
155             public void stop() throws Exception {
156                 super.stop();
157                 dirty = false;
158             }
159             
160         });
161         // if some previous test case did not roll back the data
162         // clear the db
163         if (dirty) {
164             log.warn("Previous test case did not clean up the database; clearing database...");
165             lifecycles.add(0, new ClearDatabaseLifecycle(getPerTestTablesToClear(), getPerTestTablesNotToClear()));
166         }
167         return lifecycles;
168     }
169 
170     /**
171      * @return the per-test lifecycles for rolling back the database
172      */
173     protected List<Lifecycle> getRollbackTestLifecycles() {
174         List<Lifecycle> lifecycles = super.getPerTestLifecycles();
175         lifecycles.add(0, new TransactionalLifecycle());
176         return lifecycles;
177     }
178 }