001    /*
002     * The MIT License
003     *
004     * Copyright (c) 2012, Ninja Squad
005     *
006     * Permission is hereby granted, free of charge, to any person obtaining a copy
007     * of this software and associated documentation files (the "Software"), to deal
008     * in the Software without restriction, including without limitation the rights
009     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
010     * copies of the Software, and to permit persons to whom the Software is
011     * furnished to do so, subject to the following conditions:
012     *
013     * The above copyright notice and this permission notice shall be included in
014     * all copies or substantial portions of the Software.
015     *
016     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
017     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
018     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
019     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
020     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
021     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
022     * THE SOFTWARE.
023     */
024    
025    package com.ninja_squad.dbsetup;
026    
027    import javax.annotation.Nonnull;
028    
029    
030    /**
031     * <p>
032     * This class allows speeding up test execution, by avoiding re-executing the same sequence of database operations
033     * before each test method even if each of these test methods leaves the database as it is (and only performs read-only
034     * operations, which is the most frequent case).
035     * </p>
036     * <p>Example usage:</p>
037     * <pre>
038     * // the tracker is static because JUnit uses a separate Test instance for every test method.
039     * private static DbSetupTracker dbSetupTracker = new DbSetupTracker();
040     *
041     * &#064;Before
042     * public void setUp() throws Exception {
043     *     Operation operation =
044     *         Operations.sequenceOf(
045     *             CommonOperations.DELETE_ALL,
046     *             CommonOperations.INSERT_REFERENCE_DATA,
047     *             Operations.insertInto("CLIENT")
048     *                       .columns("CLIENT_ID", "FIRST_NAME", "LAST_NAME", "DATE_OF_BIRTH", "COUNTRY_ID")
049     *                       .values(1L, "John", "Doe", "1975-07-19", 1L)
050     *                       .values(2L, "Jack", "Smith", "1969-08-22", 2L)
051     *                       .build());
052     *     DbSetup dbSetup = new DbSetup(new DataSourceDestination(dataSource), operation);
053     *     dbSetupTracker.launchIfNecessary(dbSetup);
054     * }
055     *
056     * &#064;Test
057     * public void readOnlyTest1() {
058     *     dbSetupTracker.skipNextLaunch();
059     *     ...
060     * }
061     *
062     * &#064;Test
063     * public void readOnlyTest2() {
064     *     dbSetupTracker.skipNextLaunch();
065     *     ...
066     * }
067     *
068     * &#064;Test
069     * public void readOnlyTest3() {
070     *     dbSetupTracker.skipNextLaunch();
071     *     ...
072     * }
073     *
074     * &#064;Test
075     * public void readWriteTest1() {
076     *     // No call to dbSetupTracker.skipNextLaunch();
077     *     ...
078     * }
079     * </pre>
080     * @author JB Nizet
081     */
082    public final class DbSetupTracker {
083        private DbSetup lastSetupLaunched;
084        private boolean nextLaunchSkipped;
085    
086        /**
087         * Executes the given DbSetup unless all the following conditions are <code>true</code>:
088         * <ul>
089         *   <li>{@link #skipNextLaunch()} has been called since the last call to this method;</li>
090         *   <li>the given <code>dbSetup</code> is equal to the last DbSetup launched by this method</li>
091         * </ul>
092         * This method resets the <code>skipNextLaunch</code> flag to <code>false</code>.
093         * @param dbSetup the DbSetup to execute (or skip)
094         */
095        public void launchIfNecessary(@Nonnull DbSetup dbSetup) {
096            boolean skipLaunch = nextLaunchSkipped && dbSetup.equals(lastSetupLaunched);
097            nextLaunchSkipped = false;
098            if (skipLaunch) {
099                return;
100            }
101            dbSetup.launch();
102            lastSetupLaunched = dbSetup;
103        }
104    
105        /**
106         * Marks the current test method as read-only, and thus the need for the next test method to re-execute the same
107         * sequence of database setup operations.
108         */
109        public void skipNextLaunch() {
110            this.nextLaunchSkipped = true;
111        }
112    
113        @Override
114        public String toString() {
115            return "DbSetupTracker [lastSetupLaunched="
116                    + lastSetupLaunched
117                    + ", nextLaunchSkipped="
118                    + nextLaunchSkipped
119                    + "]";
120        }
121    }