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
025package com.ninja_squad.dbsetup;
026
027import 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 */
082public 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}