/* * Copyright (C) 2013 SeqWare * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package net.sourceforge.seqware.pipeline.plugins; import java.io.IOException; import java.util.List; import net.sourceforge.seqware.common.module.ReturnValue; import net.sourceforge.seqware.common.util.Log; import org.apache.commons.dbutils.handlers.ArrayHandler; import org.apache.commons.dbutils.handlers.ArrayListHandler; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; /** * These tests support command-line tools found in the SeqWare User Tutorial, in this case, AttributeAnnotator * * @author dyuen */ public class AttributeAnnotatorET { public static final String COUNT_DB_SIZE = "SELECT (SELECT COUNT(*) FROM workflow), (SELECT COUNT(*) FROM workflow_run), (SELECT COUNT(*) FROM sequencer_run), (SELECT COUNT(*) FROM experiment), (SELECT COUNT(*) FROM ius), (SELECT COUNT(*) FROM lane), (SELECT COUNT(*) FROM processing), (SELECT COUNT(*) FROM sample), (SELECT COUNT(*) FROM sample_hierarchy), (SELECT COUNT(*) FROM processing_ius), (SELECT COUNT(*) FROM processing_files), (SELECT COUNT(*) FROM processing_relationship), (SELECT COUNT(*) FROM file), (SELECT COUNT(*) FROM study)"; private final ExtendedTestDatabaseCreator dbCreator = new ExtendedTestDatabaseCreator(); public enum AttributeType { FILE("file", "file", "file", true), SEQUENCER_RUN("sequencer-run", "sequencer_run", "sample", true), LANE("lane", "lane", "lane", true), IUS("ius", "ius", "ius", true), EXPERIMENT("experiment", "experiment", "experiment", false), PROCESSING( "processing", "processing", "processing", false), SAMPLE("sample", "sample", "sample", true), STUDY("study", "study", "study", false), WORKFLOW("workflow", "workflow", "workflow", false), WORKFLOW_RUN("workflow-run", "workflow_run", "workflow_run", false); protected final String parameter_prefix; protected final String table_name; protected final String attribute_id_prefix; /** * SEQWARE-1676 */ protected final boolean skippable; AttributeType(String prefix, String table_name, String attribute_id_prefix, boolean skippable) { this.parameter_prefix = prefix; this.table_name = table_name; this.skippable = skippable; this.attribute_id_prefix = attribute_id_prefix; } } @BeforeClass public static void resetDatabase() { ExtendedTestDatabaseCreator.resetDatabaseWithUsers(); } @Test public void testFileSkipOnly() throws IOException { toggleSkipOnly(AttributeType.FILE, 835); } @Test public void testSequencerRunSkipOnly() throws IOException { // need to reset database due to repeated accession ExtendedTestDatabaseCreator.resetDatabaseWithUsers(); toggleSkipOnly(AttributeType.SEQUENCER_RUN, 47150); } @Test public void testLaneSkipOnly() throws IOException { toggleSkipOnly(AttributeType.LANE, 4708); } @Test public void testIUSSkipOnly() throws IOException { toggleSkipOnly(AttributeType.IUS, 4789); } @Test public void testExperimentSkipOnly() throws IOException { toggleSkipOnly(AttributeType.EXPERIMENT, 2587); } @Test public void testProcessingSkipOnly() throws IOException { toggleSkipOnly(AttributeType.PROCESSING, 16); } @Test public void testSampleSkipOnly() throws IOException { toggleSkipOnly(AttributeType.SAMPLE, 6200); } @Test public void testStudySkipOnly() throws IOException { toggleSkipOnly(AttributeType.STUDY, 4758); } @Test public void testWorkflowSkipOnly() throws IOException { toggleSkipOnly(AttributeType.WORKFLOW, 2861); } @Test public void testWorkflowRunSkipOnly() throws IOException { toggleSkipOnly(AttributeType.WORKFLOW_RUN, 863); } @Test public void testFileSkipValue() throws IOException { annotateSkipImplicitly(AttributeType.FILE, 838); } @Test public void testSequencerRunSkipValue() throws IOException { // need to reset database due to repeated accession ExtendedTestDatabaseCreator.resetDatabaseWithUsers(); annotateSkipImplicitly(AttributeType.SEQUENCER_RUN, 4715); } @Test public void testLaneSkipValue() throws IOException { annotateSkipImplicitly(AttributeType.LANE, 4709); } @Test public void testIUSSkipValue() throws IOException { annotateSkipImplicitly(AttributeType.IUS, 6077); } @Test public void testExperimentSkipValue() throws IOException { annotateSkipImplicitly(AttributeType.EXPERIMENT, 4759); } @Test public void testProcessingSkipValue() throws IOException { annotateSkipImplicitly(AttributeType.PROCESSING, 2524); } @Test public void testSampleSkipValue() throws IOException { annotateSkipImplicitly(AttributeType.SAMPLE, 6207); } @Test public void testStudySkipValue() throws IOException { annotateSkipImplicitly(AttributeType.STUDY, 6144); } @Test public void testWorkflowSkipValue() throws IOException { annotateSkipImplicitly(AttributeType.WORKFLOW, 4767); } @Test public void testWorkflowRunSkipValue() throws IOException { annotateSkipImplicitly(AttributeType.WORKFLOW_RUN, 6654); } @Test public void testFileAnnotateArbitrary() throws IOException { annotateAndReannotate(AttributeType.FILE, 6120); } @Test public void testSequencerRunAnnotateArbitrary() throws IOException { // need to reset database due to repeated accession ExtendedTestDatabaseCreator.resetDatabaseWithUsers(); annotateAndReannotate(AttributeType.SEQUENCER_RUN, 47150); } @Test public void testLaneAnnotateArbitrary() throws IOException { annotateAndReannotate(AttributeType.LANE, 6123); } @Test public void testIUSAnnotateArbitrary() throws IOException { annotateAndReannotate(AttributeType.IUS, 6124); } @Test public void testExperimentAnnotateArbitrary() throws IOException { annotateAndReannotate(AttributeType.EXPERIMENT, 4759); } @Test public void testProcessingAnnotateArbitrary() throws IOException { annotateAndReannotate(AttributeType.PROCESSING, 6122); } @Test public void testSampleAnnotateArbitrary() throws IOException { annotateAndReannotate(AttributeType.SAMPLE, 6200); } @Test public void testStudyAnnotateArbitrary() throws IOException { annotateAndReannotate(AttributeType.STUDY, 4758); } @Test public void testWorkflowAnnotateArbitrary() throws IOException { annotateAndReannotate(AttributeType.WORKFLOW, 2861); } @Test public void testWorkflowRunAnnotateArbitrary() throws IOException { annotateAndReannotate(AttributeType.WORKFLOW_RUN, 863); } /** * Toggle just the skip column on a selected table type * * @param type * @param accession * @throws IOException */ public void toggleSkipOnly(AttributeType type, int accession) throws IOException { String listCommand = "-p net.sourceforge.seqware.pipeline.plugins.AttributeAnnotator " + "-- --" + type.parameter_prefix + "-accession " + accession + " --skip true"; int expectedReturnValue = type.skippable ? ReturnValue.SUCCESS : ReturnValue.INVALIDPARAMETERS; ITUtility.runSeqWareJar(listCommand, expectedReturnValue, null); if (type.skippable) { Object[] runQuery = dbCreator.runQuery(new ArrayHandler(), "SELECT skip FROM " + type.table_name + " WHERE sw_accession=?", accession); Assert.assertTrue("skip value incorrect", runQuery.length == 1 && runQuery[0].equals(true)); } listCommand = "-p net.sourceforge.seqware.pipeline.plugins.AttributeAnnotator " + "-- --" + type.parameter_prefix + "-accession " + accession + " --skip false"; ITUtility.runSeqWareJar(listCommand, expectedReturnValue, null); if (type.skippable) { Object[] runQuery = dbCreator.runQuery(new ArrayHandler(), "SELECT skip FROM " + type.table_name + " WHERE sw_accession=?", accession); Assert.assertTrue("skip value incorrect", runQuery.length == 1 && runQuery[0].equals(false)); } } /** * Annotate skip with an implicit key of "skip" * * @param type * @param accession * @throws IOException */ public void annotateSkipImplicitly(AttributeType type, int accession) throws IOException { String query = "SELECT t2." + type.table_name + "_attribute_id, t2.tag, t2.value FROM " + type.table_name + "_attribute t2, " + type.table_name + " t1 WHERE " + "t1." + type.table_name + "_id=t2." + type.attribute_id_prefix + "_id AND t1.sw_accession=? ORDER BY " + type.table_name + "_attribute_id"; Log.info(query); String value = "\"Improperly entered into the LIMS\""; String listCommand = "-p net.sourceforge.seqware.pipeline.plugins.AttributeAnnotator " + "-- --" + type.parameter_prefix + "-accession " + accession + " --skip true --value " + value; int expectedReturnValue = type.skippable ? ReturnValue.SUCCESS : ReturnValue.INVALIDPARAMETERS; ITUtility.runSeqWareJar(listCommand, expectedReturnValue, null); if (type.skippable) { Object[] runQuery = dbCreator.runQuery(new ArrayHandler(), "SELECT skip FROM " + type.table_name + " WHERE sw_accession=?", accession); Assert.assertTrue("skip value incorrect", runQuery.length == 1 && runQuery[0].equals(true)); List<Object[]> runQuery1 = dbCreator.runQuery(new ArrayListHandler(), query, accession); Assert.assertTrue("first annotation incorrect, found " + runQuery1.size(), runQuery1.size() == 1); Assert.assertTrue("first tag incorrect, found " + runQuery1.get(0)[1], runQuery1.get(0)[1].equals("skip")); Assert.assertTrue("first value incorrect, found " + runQuery1.get(0)[2], runQuery1.get(0)[2].equals(value)); } } /** * Annotate an attribute with both a key and value. Re-annotate and ensure that no duplicates are formed. * * @param type * @param accession * @throws IOException */ public void annotateAndReannotate(AttributeType type, int accession) throws IOException { final String funky_key = "funky_key"; final String funky_second_value = "funky_second_value"; final String funky_initial_value = "funky_initial_value"; final String groovy_key = "groovy_key"; final String groovy_value = "groovy_value"; String listCommand = "-p net.sourceforge.seqware.pipeline.plugins.AttributeAnnotator " + "-- --" + type.parameter_prefix + "-accession " + accession + " --key " + funky_key + " --value " + funky_initial_value; ITUtility.runSeqWareJar(listCommand, ReturnValue.SUCCESS, null); String query = "SELECT t2." + type.table_name + "_attribute_id, t2.tag, t2.value FROM " + type.table_name + "_attribute t2, " + type.table_name + " t1 WHERE " + "t1." + type.table_name + "_id=t2." + type.attribute_id_prefix + "_id AND t1.sw_accession=? ORDER BY " + type.table_name + "_attribute_id"; Log.info(query); List<Object[]> runQuery = dbCreator.runQuery(new ArrayListHandler(), query, accession); Assert.assertTrue("first annotation incorrect", runQuery.size() == 1); Assert.assertTrue("first tag incorrect", runQuery.get(0)[1].equals(funky_key)); Assert.assertTrue("first value incorrect", runQuery.get(0)[2].equals(funky_initial_value)); // count records in the database to check for cascading deletes List<Object[]> count1 = dbCreator.runQuery(new ArrayListHandler(), COUNT_DB_SIZE); runAnnotationWithSecondValue(type, accession, funky_key, funky_second_value, query); // seqware-1945: test reannotating with the same key and same value runAnnotationWithSecondValue(type, accession, funky_key, funky_second_value, query); // test reannotating with original value ITUtility.runSeqWareJar(listCommand, ReturnValue.SUCCESS, null); runAnnotationWithSecondValue(type, accession, funky_key, funky_second_value, query); // check against cascading deletes List<Object[]> count2 = dbCreator.runQuery(new ArrayListHandler(), COUNT_DB_SIZE); compareTwoCounts(count1.get(0), count2.get(0)); // try unrelated annotation listCommand = "-p net.sourceforge.seqware.pipeline.plugins.AttributeAnnotator " + "-- --" + type.parameter_prefix + "-accession " + accession + " --key " + groovy_key + " --value " + groovy_value; ITUtility.runSeqWareJar(listCommand, ReturnValue.SUCCESS, null); // check results of unrelated annotation runQuery = dbCreator.runQuery(new ArrayListHandler(), query, accession); Assert.assertTrue("incorrect resulting number of unrelated annotations, found: " + runQuery.size(), runQuery.size() == 2); Assert.assertTrue("second tag incorrect", runQuery.get(0)[1].equals(funky_key)); Assert.assertTrue("second value incorrect", runQuery.get(0)[2].equals(funky_second_value)); Assert.assertTrue("third tag incorrect", runQuery.get(1)[1].equals(groovy_key)); Assert.assertTrue("third value incorrect", runQuery.get(1)[2].equals(groovy_value)); List<Object[]> count3 = dbCreator.runQuery(new ArrayListHandler(), COUNT_DB_SIZE); compareTwoCounts(count2.get(0), count3.get(0)); } private void runAnnotationWithSecondValue(AttributeType type, int accession, final String funky_key, final String funky_second_value, String query) throws IOException { String listCommand; List<Object[]> runQuery; // reannotate with same key, different value listCommand = "-p net.sourceforge.seqware.pipeline.plugins.AttributeAnnotator " + "-- --" + type.parameter_prefix + "-accession " + accession + " --key " + funky_key + " --value " + funky_second_value; ITUtility.runSeqWareJar(listCommand, ReturnValue.SUCCESS, null); // ensure that duplicates are not formed in the database runQuery = dbCreator.runQuery(new ArrayListHandler(), query, accession); Assert.assertTrue("incorrect resulting number of duplicate annotations, found " + runQuery.size(), runQuery.size() == 1); Assert.assertTrue("second tag incorrect", runQuery.get(0)[1].equals(funky_key)); Assert.assertTrue("second value incorrect", runQuery.get(0)[2].equals(funky_second_value)); } /** * Compare two object arrays element by element and output which element fails * * @param count1 * @param count2 */ private void compareTwoCounts(Object[] count1, Object[] count2) { Assert.assertTrue("size of arrays is different", count1.length == count2.length); for (int i = 0; i < count1.length; i++) { Assert.assertTrue("element " + i + " did not match", count1[i].equals(count2[i])); } } /** * Test various forms of invalid parameters SEQWARE-1678 * * @throws java.io.IOException */ @Test public void testInvalidParameters() throws IOException { // invalid value String listCommand = "-p net.sourceforge.seqware.pipeline.plugins.AttributeAnnotator " + "-- --ius 4789 --key funky_key --funky_change_value"; ITUtility.runSeqWareJar(listCommand, ReturnValue.INVALIDARGUMENT, null); // key with no valid value listCommand = "-p net.sourceforge.seqware.pipeline.plugins.AttributeAnnotator " + "-- --ius 4789 --key funky_key"; ITUtility.runSeqWareJar(listCommand, ReturnValue.INVALIDPARAMETERS, null); } @Test public void testRejectDoubleAnnotation() throws IOException { String listCommand = "-p net.sourceforge.seqware.pipeline.plugins.AttributeAnnotator " + "-- --file-accession 6650 --key funky_key --value funky_value"; ITUtility.runSeqWareJar(listCommand, ReturnValue.SUCCESS, null); listCommand = "-p net.sourceforge.seqware.pipeline.plugins.AttributeAnnotator " + "-- --file-accession 6650 --key funky_key --value funky_value"; // seqware-1945: rejecting double annotation with the same key and value seems confusing to users ITUtility.runSeqWareJar(listCommand, ReturnValue.SUCCESS, null); } @Test public void testBulkInsert() throws IOException { String path = AttributeAnnotatorET.class.getResource("attributeAnnotator.csv").getPath(); String listCommand = "-p net.sourceforge.seqware.pipeline.plugins.AttributeAnnotator " + "-- --file " + path; ITUtility.runSeqWareJar(listCommand, ReturnValue.SUCCESS, null); } }