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 }