001 /*
002 * Copyright 2007-2008 The Kuali Foundation
003 *
004 * Licensed under the Educational Community License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.opensource.org/licenses/ecl2.php
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016 package org.kuali.rice.test;
017
018 import java.lang.annotation.ElementType;
019 import java.lang.annotation.Inherited;
020 import java.lang.annotation.Retention;
021 import java.lang.annotation.RetentionPolicy;
022 import java.lang.annotation.Target;
023 import java.util.ArrayList;
024 import java.util.List;
025
026 import org.kuali.rice.core.api.lifecycle.Lifecycle;
027
028 /**
029 * Test case which supports common styles of "baselining" the test environment before/after running
030 * a unit test.
031 * Currently supports three modes, which are specifyiable either via constructor, {@link #getMode()} override,
032 * or by annotation:
033 * <dl>
034 * <dt>NONE</dt>
035 * <dd>No baselining is performed. Because the base RiceTestCase includes the ClearDatabaseLifecycle by default, this lifecycle is
036 * explicitly omitted</dd>
037 * <dt>CLEAR_DB</dt>
038 * <dd>The database is cleared for each test. The suite ClearDatabaseLifecycle is omitted (since it's getting cleared each test
039 * anyway)</dd>
040 * <dt>ROLLBACK</dt>
041 * <dd>A TransactionalLifecycle is installed that wraps each test and rolls back data. The suite ClearDatabaseLifecycle will be
042 * invoked once initially, and subsequently if the test has detected that the environment has been left "dirty" by a previous
043 * test. After a successful rollback, the test environment is marked clean again.</dd>
044 * </dl>
045 *
046 * The BaselineMode annotation can be used on a per-test-class basis to indicate to the base class which mode to use for test
047 * subclass. It accepts a {@link Mode} value.
048 *
049 * @author Kuali Rice Team (rice.collab@kuali.org)
050 *
051 */
052 public class BaselineTestCase extends BaseModuleTestCase {
053 /**
054 * Enum of "baselining" modes that this test case supports
055 */
056 public static enum Mode {
057 CLEAR_DB, ROLLBACK, NONE
058 }
059
060 @Target({ElementType.TYPE})
061 @Inherited
062 @Retention(RetentionPolicy.RUNTIME)
063 public @interface BaselineMode {
064 Mode value();
065 }
066
067 private Mode mode = Mode.NONE;
068
069 /**
070 * Whether the test environment is in a "dirty" state. Each time the unit test starts up
071 * dirty is set to true. If a subclass installs the {@link TransactionalLifecycle} then
072 * it should clear the dirty flag. This flag can be used to perform cleanup in case a previous
073 * test left the test environment in a "dirty" state.
074 */
075 protected static boolean dirty = false;
076
077 // propagate constructors
078 public BaselineTestCase(String moduleName) {
079 super(moduleName);
080 readModeAnnotation();
081 }
082
083 /**
084 * Adds the ability to specify Mode
085 */
086 public BaselineTestCase(String moduleName, Mode mode) {
087 super(moduleName);
088 if (mode == null) throw new IllegalArgumentException("Mode cannot be null");
089 this.mode = mode;
090 }
091
092 private void readModeAnnotation() {
093 BaselineMode m = this.getClass().getAnnotation(BaselineMode.class);
094 if (m != null) {
095 if (m.value() != null) {
096 mode = m.value();
097 }
098 }
099 }
100
101 /**
102 * @return the configured mode
103 */
104 protected Mode getMode() {
105 return mode;
106 }
107
108 /**
109 * Overridden to set dirty=true each time
110 * @see org.kuali.rice.test.RiceTestCase#setUp()
111 */
112 @Override
113 public void setUp() throws Exception {
114 super.setUp();
115 dirty = true;
116 }
117
118 @Override
119 protected List<Lifecycle> getPerTestLifecycles() {
120 switch (mode) {
121 case ROLLBACK: return getRollbackPerTestLifecycles();
122 case CLEAR_DB: return getClearDbPerTestLifecycles();
123 case NONE: return super.getPerTestLifecycles();
124 default:
125 throw new RuntimeException("Invalid mode specified: " + mode);
126 }
127 }
128
129 /**
130 * @return the per-test lifecycles for clearing the database
131 */
132 protected List<Lifecycle> getClearDbPerTestLifecycles() {
133 List<Lifecycle> lifecycles = super.getPerTestLifecycles();
134 lifecycles.add(0, new ClearDatabaseLifecycle(getPerTestTablesToClear(), getPerTestTablesNotToClear()));
135 return lifecycles;
136 }
137
138 protected List<String> getPerTestTablesToClear() {
139 return new ArrayList<String>();
140 }
141
142 protected List<String> getPerTestTablesNotToClear() {
143 return new ArrayList<String>();
144 }
145
146 /**
147 * @return the per-test lifecycles for rolling back the database
148 */
149 protected List<Lifecycle> getRollbackPerTestLifecycles() {
150 List<Lifecycle> lifecycles = super.getPerTestLifecycles();
151 lifecycles.add(0, new TransactionalLifecycle() {
152 @Override
153 public void stop() throws Exception {
154 super.stop();
155 dirty = false;
156 }
157
158 });
159 // if some previous test case did not roll back the data
160 // clear the db
161 if (dirty) {
162 log.warn("Previous test case did not clean up the database; clearing database...");
163 lifecycles.add(0, new ClearDatabaseLifecycle(getPerTestTablesToClear(), getPerTestTablesNotToClear()));
164 }
165 return lifecycles;
166 }
167 }