/*****************************************************************
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.cayenne.tools;
import org.apache.cayenne.dbsync.reverse.dbimport.DbImportConfiguration;
import org.apache.cayenne.test.file.FileUtil;
import org.apache.cayenne.test.jdbc.SQLReader;
import org.apache.cayenne.test.resource.ResourceUtil;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.ProjectHelper;
import org.apache.tools.ant.UnknownElement;
import org.apache.tools.ant.util.FileUtils;
import org.custommonkey.xmlunit.DetailedDiff;
import org.custommonkey.xmlunit.Diff;
import org.custommonkey.xmlunit.Difference;
import org.custommonkey.xmlunit.XMLUnit;
import org.junit.Test;
import org.xml.sax.SAXException;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.net.URL;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;
import static org.apache.cayenne.dbsync.reverse.dbimport.ReverseEngineeringUtils.*;
import static org.apache.commons.lang.StringUtils.isBlank;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
// TODO: we are only testing on Derby. We may need to dynamically switch between DBs
// based on "cayenneTestConnection", like we do in cayenne-server, etc.
public class DbImporterTaskTest {
static {
XMLUnit.setIgnoreWhitespace(true);
}
private static File distDir(String name) {
File distDir = new File(FileUtil.baseTestDirectory(), "cdbImport");
File file = new File(distDir, name);
distDir = file.getParentFile();
// prepare destination directory
if (!distDir.exists()) {
assertTrue(distDir.mkdirs());
}
return file;
}
@Test
public void testLoadCatalog() throws Exception {
assertCatalog(getCdbImport("build-catalog.xml").getReverseEngineering());
}
@Test
public void testLoadSchema() throws Exception {
assertSchema(getCdbImport("build-schema.xml").getReverseEngineering());
}
@Test
public void testLoadCatalogAndSchema() throws Exception {
assertCatalogAndSchema(getCdbImport("build-catalog-and-schema.xml").getReverseEngineering());
}
@Test
public void testLoadFlat() throws Exception {
assertFlat(getCdbImport("build-flat.xml").getReverseEngineering());
}
@Test
public void testSkipRelationshipsLoading() throws Exception {
assertSkipRelationshipsLoading(getCdbImport("build-skip-relationships-loading.xml").getReverseEngineering());
}
@Test
public void testTableTypes() throws Exception {
assertTableTypes(getCdbImport("build-table-types.xml").getReverseEngineering());
}
@Test
public void testIncludeTable() throws Exception {
test("build-include-table.xml");
}
private DbImporterTask getCdbImport(String buildFile) {
Project project = new Project();
File map = distDir(buildFile);
ResourceUtil.copyResourceToFile(getPackagePath() + "/" + buildFile, map);
ProjectHelper.configureProject(project, map);
UnknownElement task = (UnknownElement) project.getTargets().get("dist").getTasks()[0];
task.maybeConfigure();
return (DbImporterTask) task.getRealThing();
}
private String getPackagePath() {
return getClass().getPackage().getName().replace('.', '/');
}
private void test(String name) throws Exception {
DbImporterTask cdbImport = getCdbImport(name);
File mapFile = cdbImport.getMap();
URL mapUrlRes = this.getClass().getResource(mapFile.getName() + "-result");
assertTrue(mapUrlRes != null && new File(mapUrlRes.toURI()).exists());
assertTrue(ResourceUtil
.copyResourceToFile(mapUrlRes, new File(mapFile.getParentFile(), mapFile.getName() + "-result")));
File mapFileCopy = distDir("copy-" + mapFile.getName());
if (mapFile.exists()) {
FileUtils.getFileUtils().copyFile(mapFile, mapFileCopy);
cdbImport.setMap(mapFileCopy);
} else {
mapFileCopy = mapFile;
}
prepareDatabase(name, cdbImport.toParameters());
try {
cdbImport.execute();
verifyResult(mapFile, mapFileCopy);
} finally {
cleanDb(cdbImport.toParameters());
}
}
private void cleanDb(DbImportConfiguration dbImportConfiguration) throws ClassNotFoundException,
IllegalAccessException, InstantiationException, SQLException {
Class.forName(dbImportConfiguration.getDriver()).newInstance();
// Get a connection
Connection connection = DriverManager.getConnection(dbImportConfiguration.getUrl());
Statement stmt = connection.createStatement();
ResultSet tables = connection.getMetaData().getTables(null, null, null, new String[]{"TABLE"});
while (tables.next()) {
String schema = tables.getString("TABLE_SCHEM");
System.out.println("DROP TABLE " + (isBlank(schema) ? "" : schema + ".") + tables.getString("TABLE_NAME"));
stmt.execute("DROP TABLE " + (isBlank(schema) ? "" : schema + ".") + tables.getString("TABLE_NAME"));
}
ResultSet schemas = connection.getMetaData().getSchemas();
while (schemas.next()) {
String schem = schemas.getString("TABLE_SCHEM");
if (schem.startsWith("SCHEMA")) {
System.out.println("DROP SCHEMA " + schem);
stmt.execute("DROP SCHEMA " + schem + " RESTRICT");
}
}
}
@SuppressWarnings("unchecked")
private void verifyResult(File map, File mapFileCopy) {
try {
FileReader control = new FileReader(map.getAbsolutePath() + "-result");
FileReader test = new FileReader(mapFileCopy);
DetailedDiff diff = new DetailedDiff(new Diff(control, test));
if (!diff.similar()) {
for (Difference d : ((List<Difference>) diff.getAllDifferences())) {
System.out.println("-------------------------------------------");
System.out.println(d.getTestNodeDetail().getNode());
System.out.println(d.getControlNodeDetail().getValue());
}
fail(diff.toString());
}
} catch (SAXException e) {
e.printStackTrace();
fail();
} catch (IOException e) {
e.printStackTrace();
fail();
}
}
private void prepareDatabase(String sqlFile, DbImportConfiguration dbImportConfiguration) throws Exception {
URL sqlUrl = ResourceUtil.getResource(getClass(), sqlFile + ".sql");
assertNotNull(sqlUrl);
Class.forName(dbImportConfiguration.getDriver()).newInstance();
try (Connection c = DriverManager.getConnection(dbImportConfiguration.getUrl());) {
// TODO: move parsing SQL files to a common utility (DBHelper?) .
// ALso see UnitDbApater.executeDDL - this should use the same
// utility
try (Statement stmt = c.createStatement();) {
for (String sql : SQLReader.statements(sqlUrl, ";")) {
// skip comments
if (sql.startsWith("-- ")) {
continue;
}
stmt.execute(sql);
}
}
}
}
}