/* * ModeShape (http://www.modeshape.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.modeshape.jdbc; import static org.hamcrest.core.Is.is; import static org.hamcrest.core.IsNull.notNullValue; import static org.junit.Assert.assertThat; import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.Driver; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Properties; import javax.jcr.ImportUUIDBehavior; import javax.jcr.Node; import javax.jcr.Session; import org.junit.After; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import org.modeshape.jcr.JcrRepository; import org.modeshape.jcr.JcrRepository.QueryLanguage; import org.modeshape.jcr.ModeShapeEngine; import org.modeshape.jcr.MultiUseAbstractTest; import org.modeshape.jcr.RepositoryConfiguration; /** * This is a test suite that operates against a complete JcrRepository instance created and managed using the * {@link ModeShapeEngine}. Essentially this is an integration test, but it does test lower-level functionality of the * implementation of the JCR interfaces related to querying. (It is simply more difficult to unit test these implementations * because of the difficulty in mocking the many other components to replicate the same functionality.) * <p> * Also, because queries are read-only, the engine is set up once and used for the entire set of test methods. * </p> * <p> * The following are the SQL semantics that the tests will be covering: * <li>variations of simple SELECT * FROM</li> * <li>JOIN * </p> * <p> * To create the expected results to be used to run a test, use the test and print method: example: * DriverTestUtil.executeTestAndPrint(this.connection, "SELECT * FROM [nt:base]"); This will print the expected results like this: * String[] expected = { "jcr:primaryType[STRING]", "mode:root", "car:Car", "car:Car", "nt:unstructured" } Now copy the expected * results to the test method. Then change the test to run the executeTest method passing in the <code>expected</code> results: * example: DriverTestUtil.executeTest(this.connection, "SELECT * FROM [nt:base]", expected); * </p> */ public abstract class AbstractJdbcDriverTest extends MultiUseAbstractTest { protected Driver driver; protected Connection connection; protected DatabaseMetaData dbmd; protected ConnectionResultsComparator resultsComparator; @BeforeClass public static void beforeAll() throws Exception { RepositoryConfiguration config = new RepositoryConfiguration("cars"); startRepository(config); registerNodeTypes("cars.cnd"); importContent("/", "cars-system-view.xml", ImportUUIDBehavior.IMPORT_UUID_COLLISION_THROW); // Use a session to load the contents ... Session session = repository.login(); try { // Create a branch that contains some same-name-siblings ... Node other = session.getRootNode().addNode("Other", "nt:unstructured"); other.addNode("NodeA", "nt:unstructured").setProperty("something", "value3 quick brown fox"); other.addNode("NodeA", "nt:unstructured").setProperty("something", "value2 quick brown cat"); other.addNode("NodeA", "nt:unstructured").setProperty("something", "value1 quick black dog"); session.getRootNode().addNode("NodeB", "nt:unstructured").setProperty("myUrl", "http://www.acme.com/foo/bar"); session.save(); } finally { session.logout(); } } @Before @Override public void beforeEach() throws Exception { super.beforeEach(); this.resultsComparator = new ConnectionResultsComparator(); Properties properties = createConnectionProperties(repository); String url = createConnectionUrl(repository); driver = createDriver(repository); connect(url, properties); dbmd = this.connection.getMetaData(); // only test were comparing metadata is not available at this time resultsComparator.compareColumns = true; } @Override @After public void afterEach() throws Exception { try { if (connection != null) { try { connection.close(); } finally { connection = null; dbmd = null; driver = null; } } super.afterEach(); } finally { DriverManager.deregisterDriver(driver); } } protected Properties createConnectionProperties( JcrRepository repository ) throws Exception { return new Properties(); } /** * Subclasses should override this method and return a default URL for connecting the JDBC driver to the given repository. * * @param repository the repository instance to which the driver should connect * @return the connection URL * @throws Exception if there is an exception */ protected abstract String createConnectionUrl( JcrRepository repository ) throws Exception; /** * Subclasses should override this method and return a default URL for connecting the JDBC driver to the given repository. * * @param repository the repository instance to which the driver should connect * @return the connection URL * @throws Exception if there is an exception */ protected abstract Driver createDriver( JcrRepository repository ) throws Exception; /** * Establish a JDBC connection. This can be called by test methods, although it is called by default in the * {@link #beforeEach()} method. * * @param url the connection URL; may not be null * @return the connection * @throws Exception if there is an exception */ protected Connection connect( String url ) throws Exception { return connect(url, null); } /** * Establish a JDBC connection. This can be called by test methods, although it is called by default in the * {@link #beforeEach()} method. * * @param url the connection URL; may not be null * @param properties the connection properties; may be null * @return the connection * @throws Exception if there is an exception */ protected Connection connect( String url, Properties properties ) throws Exception { if (properties == null) properties = new Properties(); Connection newConnection = driver.connect(url, properties); if (connection != null) { connection.close(); } connection = newConnection; return connection; } @Test public void shouldStartUp() { assertThat(session, is(notNullValue())); assertThat(connection, is(notNullValue())); } public void executeQuery( String sql, String[] expected, int expectedRowCount ) throws SQLException { executeQuery(sql, expected, expectedRowCount, QueryLanguage.JCR_SQL2); } public void executeQuery( String sql, String[] expected, int expectedRowCount, String language ) throws SQLException { ConnectionResultsComparator.executeTest(connection, sql, expected, expectedRowCount, language); } public void assertResultsSetEquals( final ResultSet resultSet, final String expected ) { resultsComparator.assertResultsSetEquals(resultSet, expected); } public void assertResultsSetEquals( ResultSet resultSet, String[] expected ) { resultsComparator.assertResultsSetEquals(resultSet, expected); } public void assertRowCount( int expected ) { resultsComparator.assertRowCount(expected); } }