001/**
002 * Copyright 2005-2014 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 */
016package org.kuali.rice.test;
017
018import org.kuali.rice.core.api.lifecycle.Lifecycle;
019
020import java.lang.annotation.ElementType;
021import java.lang.annotation.Inherited;
022import java.lang.annotation.Retention;
023import java.lang.annotation.RetentionPolicy;
024import java.lang.annotation.Target;
025import java.util.ArrayList;
026import java.util.List;
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_CLEAR_DB</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 *   <dd>A TransactionalLifecycle is installed that wraps each test and rolls back data.</dd>
045 * </dl>
046 * 
047 * The BaselineMode annotation can be used on a per-test-class basis to indicate to the base class which mode to use for test
048 * subclass.  It accepts a {@link Mode} value.
049 * 
050 * @author Kuali Rice Team (rice.collab@kuali.org)
051 *
052 */
053public class BaselineTestCase extends BaseModuleTestCase {
054    /**
055     * Enum of "baselining" modes that this test case supports
056     */
057    public static enum Mode {
058        CLEAR_DB, ROLLBACK_CLEAR_DB, ROLLBACK, NONE
059    }
060
061    @Target({ElementType.TYPE})
062    @Inherited
063    @Retention(RetentionPolicy.RUNTIME)
064    public @interface BaselineMode {
065        Mode value();
066    }
067
068    private Mode mode = Mode.NONE;
069    
070    /**
071     * Whether the test environment is in a "dirty" state.  Each time the unit test starts up
072     * dirty is set to true.  If a subclass installs the {@link TransactionalLifecycle} then
073     * it should clear the dirty flag.  This flag can be used to perform cleanup in case a previous
074     * test left the test environment in a "dirty" state.
075     */
076    protected static boolean dirty = false;
077
078    // propagate constructors
079    public BaselineTestCase(String moduleName) {
080        super(moduleName);
081        readModeAnnotation();
082    }
083
084    /**
085     * Adds the ability to specify Mode
086     */
087    public BaselineTestCase(String moduleName, Mode mode) {
088        super(moduleName);
089        if (mode == null) throw new IllegalArgumentException("Mode cannot be null");
090        this.mode = mode;
091    }
092
093    private void readModeAnnotation() {
094        BaselineMode m = this.getClass().getAnnotation(BaselineMode.class);
095        if (m != null) {
096            if (m.value() != null) {
097                mode = m.value();
098            }
099        }
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}