/** * Copyright (C) 2009-2014 FoundationDB, LLC * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.foundationdb.server.test.it.isolation; import com.foundationdb.server.error.ErrorCode; import com.foundationdb.sql.embedded.EmbeddedJDBCITBase; import java.sql.Connection; import java.sql.SQLException; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.junit.Assert; import org.junit.Assume; import org.junit.Rule; import org.junit.rules.TestWatcher; import org.junit.runner.Description; import org.junit.runners.model.Statement; public abstract class IsolationITBase extends EmbeddedJDBCITBase { @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface Isolation { public int value(); } @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface SQLExceptionExpected { public ErrorCode errorCode(); } @Override protected boolean retryException(Throwable t) { // Turn off default retry in ApiTestBase. return false; } protected static class ExpectingSQLException extends Statement { private final Statement base; private final SQLExceptionExpected expected; public ExpectingSQLException(Statement base, SQLExceptionExpected expected) { this.base = base; this.expected = expected; } @Override public void evaluate() throws Throwable { try { base.evaluate(); } catch (SQLException ex) { ErrorCode errorCode = expected.errorCode(); if (errorCode != null) { Assert.assertEquals("expected SQL state", errorCode.getFormattedValue(), ex.getSQLState()); } return; } Assert.fail("Expected a SQL exception, but none was thrown"); } } protected static class IsolationWatcher extends TestWatcher { private Isolation testIsolation; @Override protected void starting(Description desc) { testIsolation = desc.getAnnotation(Isolation.class); } @Override public Statement apply(Statement base, Description desc) { base = super.apply(base, desc); SQLExceptionExpected expected = desc.getAnnotation(SQLExceptionExpected.class); if (expected == null) return base; else return new ExpectingSQLException(base, expected); } public int getTestIsolationLevel() { if (testIsolation != null) return testIsolation.value(); else return Connection.TRANSACTION_NONE; } } @Rule public IsolationWatcher watcher = new IsolationWatcher(); public Connection getAutoCommitConnection() throws SQLException { return super.getConnection(); } @Override public Connection getConnection() throws SQLException { Connection conn = super.getConnection(); int isolation = watcher.getTestIsolationLevel(); if (isolation != Connection.TRANSACTION_NONE) { conn.setAutoCommit(false); conn.setTransactionIsolation(isolation); // If the current store does not actually support the isolation // level needed for the test, skip it. Assume.assumeTrue(isolation == conn.getTransactionIsolation()); } return conn; } }