--- a +++ b/qiita_db/test/test_software.py @@ -0,0 +1,1236 @@ +# ----------------------------------------------------------------------------- +# Copyright (c) 2014--, The Qiita Development Team. +# +# Distributed under the terms of the BSD 3-clause License. +# +# The full license is in the file LICENSE, distributed with this software. +# ----------------------------------------------------------------------------- + +from unittest import TestCase, main +from copy import deepcopy +from os.path import exists +from os import remove, close +from tempfile import mkstemp +import warnings + +import networkx as nx + +from qiita_core.util import qiita_test_checker +import qiita_db as qdb + +from json import dumps + + +@qiita_test_checker() +class CommandTests(TestCase): + def setUp(self): + self.software = qdb.software.Software(1) + self.parameters = { + 'req_art': ['artifact:["BIOM"]', None], + 'req_param': ['string', None], + 'opt_int_param': ['integer', '4'], + 'opt_choice_param': ['choice:["opt1", "opt2"]', 'opt1'], + 'opt_mchoice_param': ['mchoice:["opt1", "opt2", "opt3"]', + ['opt1', 'opt2']], + 'opt_bool': ['boolean', 'False']} + self.outputs = {'out1': 'BIOM'} + + def test_get_commands_by_input_type(self): + qdb.software.Software.deactivate_all() + obs = list(qdb.software.Command.get_commands_by_input_type(['FASTQ'])) + self.assertEqual(obs, []) + + cmd = qdb.software.Command(1) + cmd.activate() + obs = list(qdb.software.Command.get_commands_by_input_type(['FASTQ'])) + exp = [cmd] + self.assertCountEqual(obs, exp) + + obs = list(qdb.software.Command.get_commands_by_input_type( + ['FASTQ', 'per_sample_FASTQ'])) + self.assertCountEqual(obs, exp) + + obs = list(qdb.software.Command.get_commands_by_input_type( + ['FASTQ', 'SFF'])) + self.assertEqual(obs, exp) + + obs = list(qdb.software.Command.get_commands_by_input_type( + ['FASTQ', 'SFF'], active_only=False)) + exp = [qdb.software.Command(1), qdb.software.Command(2)] + self.assertCountEqual(obs, exp) + + new_cmd = qdb.software.Command.create( + self.software, "Analysis Only Command", + "This is a command for testing", + {'req_art': ['artifact:["FASTQ"]', None]}, + analysis_only=True) + obs = list(qdb.software.Command.get_commands_by_input_type( + ['FASTQ', 'SFF'], active_only=False)) + exp = [qdb.software.Command(1), qdb.software.Command(2)] + self.assertCountEqual(obs, exp) + + obs = list(qdb.software.Command.get_commands_by_input_type( + ['FASTQ', 'SFF'], active_only=False, exclude_analysis=False)) + exp = [qdb.software.Command(1), qdb.software.Command(2), new_cmd] + self.assertCountEqual(obs, exp) + + obs = list(qdb.software.Command.get_commands_by_input_type( + ['FASTQ'], active_only=False, exclude_analysis=False, + prep_type='Metagenomic')) + exp = [qdb.software.Command(1), new_cmd] + self.assertCountEqual(obs, exp) + + obs = list(qdb.software.Command.get_commands_by_input_type( + ['FASTQ'], active_only=False, exclude_analysis=False, + prep_type='18S')) + exp = [qdb.software.Command(1)] + self.assertCountEqual(obs, exp) + + def test_get_html_artifact(self): + with self.assertRaises(qdb.exceptions.QiitaDBError): + qdb.software.Command.get_html_generator('BIOM') + + exp = qdb.software.Command(5) + exp.activate() + obs = qdb.software.Command.get_html_generator('BIOM') + self.assertEqual(obs, exp) + + with self.assertRaises(qdb.exceptions.QiitaDBError): + qdb.software.Command.get_html_generator('Demultiplexed') + + exp = qdb.software.Command(7) + exp.activate() + obs = qdb.software.Command.get_html_generator('Demultiplexed') + self.assertEqual(obs, exp) + + with self.assertRaises(qdb.exceptions.QiitaDBError): + qdb.software.Command.get_html_generator('Unknown') + + def test_get_validator(self): + with self.assertRaises(qdb.exceptions.QiitaDBError): + qdb.software.Command.get_validator('BIOM') + + exp = qdb.software.Command(4) + exp.activate() + obs = qdb.software.Command.get_validator('BIOM') + self.assertEqual(obs, exp) + + with self.assertRaises(qdb.exceptions.QiitaDBError): + qdb.software.Command.get_validator('Demultiplexed') + + exp = qdb.software.Command(6) + exp.activate() + obs = qdb.software.Command.get_validator('Demultiplexed') + self.assertEqual(obs, exp) + + with self.assertRaises(qdb.exceptions.QiitaDBError): + qdb.software.Command.get_validator('Unknown') + + def test_exists(self): + self.assertFalse(qdb.software.Command.exists( + self.software, "donotexists")) + self.assertTrue(qdb.software.Command.exists( + self.software, "Split libraries")) + + def test_software(self): + self.assertEqual(qdb.software.Command(1).software, + qdb.software.Software(1)) + self.assertEqual(qdb.software.Command(2).software, + qdb.software.Software(1)) + + def test_name(self): + self.assertEqual(qdb.software.Command(1).name, "Split libraries FASTQ") + self.assertEqual(qdb.software.Command(2).name, "Split libraries") + + def test_post_processing_cmd(self): + # initial test + self.assertEqual(qdb.software.Command(1).post_processing_cmd, None) + + results = {} + results['script_env'] = 'source deactivate; source activate qiita' + results['script_path'] = 'qiita_db/test/support_files/worker.py' + results['script_params'] = {'a': 'A', 'b': 'B'} + + results = dumps(results) + + # modify table directly, in order to test method + sql = """UPDATE qiita.software_command + SET post_processing_cmd = %s + WHERE command_id = 1""" + qdb.sql_connection.perform_as_transaction(sql, [results]) + + results = qdb.software.Command(1).post_processing_cmd + + # test method returns 'ls' + self.assertEqual(results['script_env'], + 'source deactivate; source activate qiita') + self.assertEqual(results['script_path'], + 'qiita_db/test/support_files/worker.py') + self.assertEqual(results['script_params'], {'a': 'A', 'b': 'B'}) + + # clean up table + sql = """UPDATE qiita.software_command + SET post_processing_cmd = NULL + WHERE command_id = 1""" + qdb.sql_connection.perform_as_transaction(sql) + + def test_description(self): + self.assertEqual( + qdb.software.Command(1).description, + "Demultiplexes and applies quality control to FASTQ data") + self.assertEqual( + qdb.software.Command(2).description, + "Demultiplexes and applies quality control to FASTA data") + + def test_parameters(self): + exp_params = {'barcode_type': ['string', 'golay_12'], + 'input_data': ['artifact', None], + 'max_bad_run_length': ['integer', '3'], + 'max_barcode_errors': ['float', '1.5'], + 'min_per_read_length_fraction': ['float', '0.75'], + 'phred_quality_threshold': ['integer', '3'], + 'rev_comp': ['bool', 'False'], + 'rev_comp_barcode': ['bool', 'False'], + 'rev_comp_mapping_barcodes': ['bool', 'False'], + 'sequence_max_n': ['integer', '0'], + 'phred_offset': ['choice:["auto", "33", "64"]', 'auto']} + self.assertEqual(qdb.software.Command(1).parameters, exp_params) + exp_params = { + 'barcode_type': ['string', 'golay_12'], + 'disable_bc_correction': ['bool', 'False'], + 'disable_primers': ['bool', 'False'], + 'input_data': ['artifact', None], + 'max_ambig': ['integer', '6'], + 'max_barcode_errors': ['float', '1.5'], + 'max_homopolymer': ['integer', '6'], + 'max_primer_mismatch': ['integer', '0'], + 'max_seq_len': ['integer', '1000'], + 'min_qual_score': ['integer', '25'], + 'min_seq_len': ['integer', '200'], + 'qual_score_window': ['integer', '0'], + 'reverse_primer_mismatches': ['integer', '0'], + 'reverse_primers': + ['choice:["disable", "truncate_only", "truncate_remove"]', + 'disable'], + 'trim_seq_length': ['bool', 'False'], + 'truncate_ambi_bases': ['bool', 'False']} + self.assertEqual(qdb.software.Command(2).parameters, exp_params) + + def test_required_parameters(self): + exp_params = { + 'input_data': ('artifact', ['FASTQ', 'per_sample_FASTQ'])} + obs = qdb.software.Command(1).required_parameters + self.assertCountEqual(list(obs.keys()), exp_params.keys()) + self.assertEqual(obs['input_data'][0], exp_params['input_data'][0]) + self.assertCountEqual(obs['input_data'][1], + exp_params['input_data'][1]) + + exp_params = { + 'input_data': ('artifact', ['SFF', 'FASTA', 'FASTA_Sanger'])} + obs = qdb.software.Command(2).required_parameters + self.assertCountEqual(list(obs.keys()), exp_params.keys()) + self.assertEqual(obs['input_data'][0], exp_params['input_data'][0]) + self.assertCountEqual(obs['input_data'][1], + exp_params['input_data'][1]) + + def test_optional_parameters(self): + exp_params = {'barcode_type': ['string', 'golay_12'], + 'max_bad_run_length': ['integer', '3'], + 'max_barcode_errors': ['float', '1.5'], + 'min_per_read_length_fraction': ['float', '0.75'], + 'phred_quality_threshold': ['integer', '3'], + 'rev_comp': ['bool', 'False'], + 'rev_comp_barcode': ['bool', 'False'], + 'rev_comp_mapping_barcodes': ['bool', 'False'], + 'sequence_max_n': ['integer', '0'], + 'phred_offset': ['choice:["auto", "33", "64"]', 'auto']} + self.assertEqual(qdb.software.Command(1).optional_parameters, + exp_params) + exp_params = exp_params = { + 'barcode_type': ['string', 'golay_12'], + 'disable_bc_correction': ['bool', 'False'], + 'disable_primers': ['bool', 'False'], + 'max_ambig': ['integer', '6'], + 'max_barcode_errors': ['float', '1.5'], + 'max_homopolymer': ['integer', '6'], + 'max_primer_mismatch': ['integer', '0'], + 'max_seq_len': ['integer', '1000'], + 'min_qual_score': ['integer', '25'], + 'min_seq_len': ['integer', '200'], + 'qual_score_window': ['integer', '0'], + 'reverse_primer_mismatches': ['integer', '0'], + 'reverse_primers': + ['choice:["disable", "truncate_only", "truncate_remove"]', + 'disable'], + 'trim_seq_length': ['bool', 'False'], + 'truncate_ambi_bases': ['bool', 'False']} + self.assertEqual(qdb.software.Command(2).optional_parameters, + exp_params) + + def test_default_parameter_sets(self): + obs = list(qdb.software.Command(1).default_parameter_sets) + exp = [qdb.software.DefaultParameters(1), + qdb.software.DefaultParameters(2), + qdb.software.DefaultParameters(3), + qdb.software.DefaultParameters(4), + qdb.software.DefaultParameters(5), + qdb.software.DefaultParameters(6), + qdb.software.DefaultParameters(7), + qdb.software.DefaultParameters(11), + qdb.software.DefaultParameters(12)] + self.assertEqual(obs, exp) + + obs = list(qdb.software.Command(2).default_parameter_sets) + exp = [qdb.software.DefaultParameters(8), + qdb.software.DefaultParameters(9)] + self.assertEqual(obs, exp) + + def test_outputs(self): + obs = qdb.software.Command(1).outputs + exp = [['demultiplexed', 'Demultiplexed']] + self.assertEqual(obs, exp) + + obs = qdb.software.Command(2).outputs + exp = [['demultiplexed', 'Demultiplexed']] + self.assertEqual(obs, exp) + + obs = qdb.software.Command(3).outputs + exp = [['OTU table', 'BIOM']] + self.assertEqual(obs, exp) + + def test_create_error(self): + # no parameters + with self.assertRaises(qdb.exceptions.QiitaDBError): + qdb.software.Command.create( + self.software, "Test command", "Testing command", {}, + self.outputs) + + with self.assertRaises(qdb.exceptions.QiitaDBError): + qdb.software.Command.create( + self.software, "Test command", "Testing command", None, + self.outputs) + + # malformed params + parameters = deepcopy(self.parameters) + parameters['req_param'].append('breaking_the_format') + with self.assertRaises(qdb.exceptions.QiitaDBError): + qdb.software.Command.create( + self.software, "Test command", "Testing command", + parameters, self.outputs) + + # unsupported parameter type + parameters = deepcopy(self.parameters) + parameters['opt_int_param'][0] = 'unsupported_type' + with self.assertRaises(qdb.exceptions.QiitaDBError): + qdb.software.Command.create( + self.software, "Test command", "Testing command", + parameters, self.outputs) + + # bad default choice + parameters = deepcopy(self.parameters) + parameters['opt_choice_param'][1] = 'unsupported_choice' + with self.assertRaises(qdb.exceptions.QiitaDBError): + qdb.software.Command.create( + self.software, "Test command", "Testing command", + parameters, self.outputs) + + # duplicate + with self.assertRaises(qdb.exceptions.QiitaDBDuplicateError): + qdb.software.Command.create( + self.software, "Split libraries", + "This is a command for testing", self.parameters, + self.outputs) + + # the output type doesn't exist + with self.assertRaisesRegex(ValueError, "Error creating QIIMEq2, Split" + " libraries - wrong output, This is a " + "command for testing - Unknown " + "artifact_type: BLA!"): + qdb.software.Command.create( + self.software, "Split libraries - wrong output", + "This is a command for testing", self.parameters, + {'out': 'BLA!'}) + + def test_create(self): + # let's deactivate all current plugins and commands; this is not + # important to test the creation but it is important to test if a + # command is active as the new commands should take precedence and + # should make the old commands active if they have the same name + qdb.software.Software.deactivate_all() + + # note that here we are adding commands to an existing software + obs = qdb.software.Command.create( + self.software, "Test Command", "This is a command for testing", + self.parameters, self.outputs) + self.assertEqual(obs.name, "Test Command") + self.assertEqual(obs.description, "This is a command for testing") + exp_required = {'req_param': ('string', [None]), + 'req_art': ('artifact', ['BIOM'])} + self.assertEqual(obs.required_parameters, exp_required) + exp_optional = { + 'opt_int_param': ['integer', '4'], + 'opt_choice_param': ['choice:["opt1", "opt2"]', 'opt1'], + 'opt_mchoice_param': ['mchoice:["opt1", "opt2", "opt3"]', + ['opt1', 'opt2']], + 'opt_bool': ['boolean', 'False']} + self.assertEqual(obs.optional_parameters, exp_optional) + self.assertFalse(obs.analysis_only) + self.assertEqual(obs.naming_order, []) + self.assertEqual(obs.merging_scheme, + {'parameters': [], 'outputs': [], + 'ignore_parent_command': False}) + + # here we are creating a new software that we will add new commads to + obs = qdb.software.Command.create( + self.software, "Test Command 2", "This is a command for testing", + self.parameters, analysis_only=True) + self.assertEqual(obs.name, "Test Command 2") + self.assertEqual(obs.description, "This is a command for testing") + self.assertEqual(obs.required_parameters, exp_required) + self.assertEqual(obs.optional_parameters, exp_optional) + self.assertTrue(obs.analysis_only) + self.assertEqual(obs.naming_order, []) + self.assertEqual(obs.merging_scheme, + {'parameters': [], 'outputs': [], + 'ignore_parent_command': False}) + + # Test that the internal parameters in "Validate" + # are created automatically + software = qdb.software.Software.create( + "New Type Software", "1.0.0", + "This is adding a new software for testing", "env_name", + "start_plugin", "artifact definition") + parameters = { + 'template': ('prep_template', None), + 'analysis': ('analysis', None), + 'files': ('string', None), + 'artifact_type': ('string', None)} + validate = qdb.software.Command.create( + software, "Validate", "Test creating a validate command", + parameters) + self.assertEqual(validate.name, "Validate") + self.assertEqual( + validate.description, "Test creating a validate command") + exp_required = { + 'template': ('prep_template', [None]), + 'analysis': ('analysis', [None]), + 'files': ('string', [None]), + 'artifact_type': ('string', [None])} + self.assertEqual(validate.required_parameters, exp_required) + exp_optional = {'name': ['string', 'dflt_name'], + 'provenance': ['string', None]} + self.assertEqual(validate.optional_parameters, exp_optional) + self.assertFalse(validate.analysis_only) + self.assertEqual(validate.naming_order, []) + self.assertEqual(validate.merging_scheme, + {'parameters': [], 'outputs': [], + 'ignore_parent_command': False}) + + # Test that the naming and merge information is provided + parameters = { + 'req_art': ['artifact:["BIOM"]', None], + 'opt_int_param': ['integer', '4', 1, True], + 'opt_choice_param': ['choice:["opt1", "opt2"]', 'opt1', 2, True], + 'opt_bool': ['boolean', 'False', None, False]} + outputs = {'out1': ('BIOM', True)} + obs = qdb.software.Command.create( + self.software, "Test Command Merge", "Testing cmd", parameters, + outputs=outputs) + self.assertEqual(obs.name, "Test Command Merge") + self.assertEqual(obs.description, "Testing cmd") + exp_required = {'req_art': ('artifact', ['BIOM'])} + self.assertEqual(obs.required_parameters, exp_required) + exp_optional = { + 'opt_int_param': ['integer', '4'], + 'opt_choice_param': ['choice:["opt1", "opt2"]', 'opt1'], + 'opt_bool': ['boolean', 'False']} + self.assertEqual(obs.optional_parameters, exp_optional) + self.assertFalse(obs.analysis_only) + self.assertEqual(obs.naming_order, + ['opt_int_param', 'opt_choice_param']) + exp = {'parameters': ['opt_choice_param', 'opt_int_param'], + 'outputs': ['out1'], + 'ignore_parent_command': False} + self.assertEqual(obs.merging_scheme, exp) + + # now that we are done with the regular creation testing we can create + # a new command with the name of an old deprecated command and make + # sure that is not deprecated now + # 1. let's find the previous command and make sure is deprecated + cmd_name = 'Split libraries FASTQ' + old_cmd = [cmd for cmd in self.software.commands + if cmd.name == cmd_name][0] + self.assertFalse(old_cmd.active) + + # 2. let's create a new command with the same name and check that now + # the old and the new are active. Remember the new command is going + # to be created in a new software that has a Validate command which + # is an 'artifact definition', so this will allow us to test that + # a previous Validate command is not active + new_cmd = qdb.software.Command.create( + software, cmd_name, cmd_name, parameters, outputs=outputs) + self.assertEqual(old_cmd.name, new_cmd.name) + self.assertTrue(old_cmd.active) + self.assertTrue(new_cmd.active) + # find an old Validate command + old_validate = [c for c in qdb.software.Software.from_name_and_version( + 'BIOM type', '2.1.4 - Qiime2').commands if c.name == 'Validate'][0] + self.assertEqual(old_validate.name, validate.name) + self.assertTrue(validate.active) + self.assertFalse(old_validate.active) + + def test_activate(self): + qdb.software.Software.deactivate_all() + tester = qdb.software.Command(1) + self.assertFalse(tester.active) + tester.activate() + self.assertTrue(tester.active) + + def test_processing_jobs(self): + exp_jids = ['6d368e16-2242-4cf8-87b4-a5dc40bb890b', + '4c7115e8-4c8e-424c-bf25-96c292ca1931', + 'b72369f9-a886-4193-8d3d-f7b504168e75', + '46b76f74-e100-47aa-9bf2-c0208bcea52d', + '6ad4d590-4fa3-44d3-9a8f-ddbb472b1b5f', + '063e553b-327c-4818-ab4a-adfe58e49860', + 'ac653cb5-76a6-4a45-929e-eb9b2dee6b63'] + + jobs = qdb.software.Command(1).processing_jobs + set_jobs = set(jobs) + + # comparing the length of jobs and set_jobs, since there could've been + # duplicates in the tests + self.assertEqual(len(jobs), len(set_jobs)) + + exp = set([qdb.processing_job.ProcessingJob(j) for j in exp_jids]) + self.assertEqual(len(set_jobs & exp), len(exp_jids)) + + exp_jids = ['bcc7ebcd-39c1-43e4-af2d-822e3589f14d'] + exp = [qdb.processing_job.ProcessingJob(j) for j in exp_jids] + self.assertCountEqual(qdb.software.Command(2).processing_jobs, exp) + self.assertCountEqual(qdb.software.Command(4).processing_jobs, []) + + +@qiita_test_checker() +class SoftwareTestsIter(TestCase): + # different class to assure integrity of database + + def test_iter(self): + s1 = qdb.software.Software(1) + s2 = qdb.software.Software(2) + s3 = qdb.software.Software(3) + s4 = qdb.software.Software(4) + + qdb.software.Software.deactivate_all() + obs = list(qdb.software.Software.iter()) + self.assertEqual(obs, []) + obs = list(qdb.software.Software.iter(False)) + self.assertEqual(obs, [s1, s2, s3, s4]) + + s2.activate() + obs = list(qdb.software.Software.iter()) + self.assertEqual(obs, [s2]) + obs = list(qdb.software.Software.iter(False)) + self.assertEqual(obs, [s1, s2, s3, s4]) + + s1.activate() + s3.activate() + obs = list(qdb.software.Software.iter()) + self.assertEqual(obs, [s1, s2, s3]) + obs = list(qdb.software.Software.iter(False)) + self.assertEqual(obs, [s1, s2, s3, s4]) + + # test command resouce allocations here to be able to delete + # allocations so we can tests errors. + + # Command 2 is Split libraries and has defined resources + self.assertEqual( + qdb.software.Command(2).resource_allocation, + '-p qiita -N 1 -n 1 --mem 60gb --time 25:00:00') + + # Command 9 is Summarize Taxa and has no defined resources so it goes + # to defaults + self.assertEqual( + qdb.software.Command(9).resource_allocation, + '-p qiita -N 1 -n 5 --mem-per-cpu 8gb --time 168:00:00') + + # delete allocations to test errors + qdb.sql_connection.perform_as_transaction( + "DELETE FROM qiita.processing_job_resource_allocation") + + with self.assertRaisesRegex(ValueError, "Could not match 'Split " + "libraries' to a resource allocation!"): + qdb.software.Command(2).resource_allocation + + +@qiita_test_checker() +class SoftwareTests(TestCase): + def setUp(self): + self._clean_up_files = [] + + def tearDown(self): + for f in self._clean_up_files: + if exists(f): + remove(f) + + def test_from_name_and_version(self): + obs = qdb.software.Software.from_name_and_version('QIIMEq2', '1.9.1') + exp = qdb.software.Software(1) + self.assertEqual(obs, exp) + + obs = qdb.software.Software.from_name_and_version( + 'BIOM type', '2.1.4 - Qiime2') + exp = qdb.software.Software(2) + self.assertEqual(obs, exp) + + # Wrong name + with self.assertRaises(qdb.exceptions.QiitaDBUnknownIDError): + qdb.software.Software.from_name_and_version('QiIMEq2', '1.9.1') + # Wrong version + with self.assertRaises(qdb.exceptions.QiitaDBUnknownIDError): + qdb.software.Software.from_name_and_version('QIIMEq2', '1.9.0') + + def test_name(self): + self.assertEqual(qdb.software.Software(1).name, "QIIMEq2") + + def test_version(self): + self.assertEqual(qdb.software.Software(1).version, "1.9.1") + + def test_description(self): + exp = ("Quantitative Insights Into Microbial Ecology (QIIME) is an " + "open-source bioinformatics pipeline for performing microbiome " + "analysis from raw DNA sequencing data") + self.assertEqual(qdb.software.Software(1).description, exp) + + def test_commands(self): + exp = [qdb.software.Command(1), qdb.software.Command(2), + qdb.software.Command(3)] + obs = qdb.software.Software(1).commands + self.assertEqual(len(obs), 7) + for e in exp: + self.assertIn(e, obs) + + def test_get_command(self): + s = qdb.software.Software(1) + obs = s.get_command('Split libraries FASTQ') + self.assertEqual(obs, qdb.software.Command(1)) + + with self.assertRaises(qdb.exceptions.QiitaDBUnknownIDError): + s.get_command('UNKNOWN') + + def test_publications(self): + self.assertEqual(qdb.software.Software(1).publications, + [['10.1038/nmeth.f.303', '20383131']]) + + def test_environment_script(self): + tester = qdb.software.Software(1) + self.assertEqual(tester.environment_script, 'source activate qiita') + + def test_start_script(self): + tester = qdb.software.Software(2) + self.assertEqual(tester.start_script, 'start_biom') + + def test_default_workflows(self): + obs = list(qdb.software.DefaultWorkflow.iter(True)) + exp = [qdb.software.DefaultWorkflow(1), + qdb.software.DefaultWorkflow(2), + qdb.software.DefaultWorkflow(3)] + self.assertEqual(obs, exp) + obs = list(qdb.software.DefaultWorkflow.iter(False)) + self.assertEqual(obs, exp) + + self.assertEqual( + qdb.software.DefaultWorkflow(1).artifact_type, 'FASTQ') + + qdb.software.DefaultWorkflow(1).active = False + obs = list(qdb.software.DefaultWorkflow.iter(False)) + self.assertEqual(obs, exp) + + obs = list(qdb.software.DefaultWorkflow.iter(True)) + exp = [qdb.software.DefaultWorkflow(2), + qdb.software.DefaultWorkflow(3)] + self.assertEqual(obs, exp) + + obs = qdb.software.DefaultWorkflow(1).data_type + exp = ['16S', '18S'] + self.assertEqual(obs, exp) + obs = qdb.software.DefaultWorkflow(2).data_type + exp = ['18S'] + self.assertEqual(obs, exp) + + dw = qdb.software.DefaultWorkflow(1) + exp = ('This accepts html <a href="https://qiita.ucsd.edu">Qiita!</a>' + '<br/><br/><b>BYE!</b>') + self.assertEqual(dw.description, exp) + exp = 'bla!' + dw.description = exp + self.assertEqual(dw.description, exp) + + def test_type(self): + self.assertEqual(qdb.software.Software(1).type, + "artifact transformation") + + def test_active(self): + self.assertTrue(qdb.software.Software(1).active) + + def test_client_id(self): + self.assertEqual( + qdb.software.Software(1).client_id, + 'yKDgajoKn5xlOA8tpo48Rq8mWJkH9z4LBCx2SvqWYLIryaan2u') + + def test_client_secret(self): + self.assertEqual( + qdb.software.Software(1).client_secret, + '9xhU5rvzq8dHCEI5sSN95jesUULrZi6pT6Wuc71fDbFbsrnWarcSq56TJLN4kP4hH' + ) + + def test_deactivate_all(self): + obs = qdb.software.Software(1) + self.assertTrue(obs.active) + qdb.software.Software.deactivate_all() + self.assertFalse(obs.active) + + def test_from_file(self): + exp = qdb.software.Software(1) + client_id = 'yKDgajoKn5xlOA8tpo48Rq8mWJkH9z4LBCx2SvqWYLIryaan2u' + client_secret = ('9xhU5rvzq8dHCEI5sSN95jesUULrZi6pT6Wuc71fDbFbsrnWarc' + 'Sq56TJLN4kP4hH') + # Activate existing plugin + fd, fp = mkstemp(suffix='.conf') + close(fd) + self._clean_up_files.append(fp) + with open(fp, 'w') as f: + f.write(CONF_TEMPLATE % + ('QIIMEq2', '1.9.1', + 'Quantitative Insights Into Microbial Ecology (QIIME) ' + 'is an open-source bioinformatics pipeline for ' + 'performing microbiome analysis from raw DNA ' + 'sequencing data', 'source activate qiita', + 'start_target_gene', 'artifact transformation', + '[["10.1038/nmeth.f.303", "20383131"]]', client_id, + client_secret)) + obs = qdb.software.Software.from_file(fp) + self.assertEqual(obs, exp) + + # Activate an existing plugin with a warning + fd, fp = mkstemp(suffix='.conf') + close(fd) + self._clean_up_files.append(fp) + with open(fp, 'w') as f: + f.write(CONF_TEMPLATE % + ('QIIMEq2', '1.9.1', 'Different description', + 'source activate qiime', 'start_qiime', + 'artifact transformation', + '[["10.1038/nmeth.f.303", "20383131"]]', client_id, + client_secret)) + with warnings.catch_warnings(record=True) as warns: + obs = qdb.software.Software.from_file(fp) + obs_warns = [str(w.message) for w in warns] + exp_warns = ['Plugin "QIIMEq2" version "1.9.1" config file does ' + 'not match with stored information. Check the config ' + 'file or run "qiita plugin update" to update the ' + 'plugin information. Offending values: description, ' + 'environment_script, start_script'] + self.assertCountEqual(obs_warns, exp_warns) + + self.assertEqual(obs, exp) + self.assertEqual( + obs.description, + 'Quantitative Insights Into Microbial Ecology (QIIME) is an ' + 'open-source bioinformatics pipeline for performing microbiome ' + 'analysis from raw DNA sequencing data') + self.assertEqual(obs.environment_script, 'source activate qiita') + self.assertEqual(obs.start_script, 'start_target_gene') + + # Update an existing plugin + obs = qdb.software.Software.from_file(fp, update=True) + self.assertEqual(obs, exp) + self.assertEqual(obs.description, 'Different description') + self.assertEqual(obs.environment_script, 'source activate qiime') + self.assertEqual(obs.start_script, 'start_qiime') + + # Create a new plugin + fd, fp = mkstemp(suffix='.conf') + close(fd) + self._clean_up_files.append(fp) + with open(fp, 'w') as f: + f.write(CONF_TEMPLATE % + ('NewPlugin', '0.0.1', 'Some description', + 'source activate newplug', 'start_new_plugin', + 'artifact definition', '', client_id, + client_secret)) + obs = qdb.software.Software.from_file(fp) + self.assertNotEqual(obs, exp) + self.assertEqual(obs.name, 'NewPlugin') + + # Update publications + fd, fp = mkstemp(suffix='.conf') + close(fd) + self._clean_up_files.append(fp) + exp = obs + with open(fp, 'w') as f: + f.write(CONF_TEMPLATE % + ('NewPlugin', '0.0.1', 'Some description', + 'source activate newplug', 'start_new_plugin', + 'artifact definition', '[["10.1039/nmeth.f.303", null]]', + client_id, client_secret)) + obs = qdb.software.Software.from_file(fp, update=True) + self.assertEqual(obs, exp) + self.assertEqual(obs.publications, [["10.1039/nmeth.f.303", None]]) + + # Correctly ignores if there are no publications + fd, fp = mkstemp(suffix='.conf') + close(fd) + self._clean_up_files.append(fp) + with open(fp, 'w') as f: + f.write(CONF_TEMPLATE % + ('Target Gene type', '0.1.0', + 'Target gene artifact types plugin', 'source ' + '~/virtualenv/python2.7/bin/activate; export ' + 'PATH=$HOME/miniconda3/bin/:$PATH; source activate qiita', + 'start_target_gene_types', 'artifact definition', '', + '4MOBzUBHBtUmwhaC258H7PS0rBBLyGQrVxGPgc9g305bvVhf6h', + 'rFb7jwAb3UmSUN57Bjlsi4DTl2owLwRpwCc0SggRNEVb2Ebae2p5Umnq' + '20rNMhmqN')) + with warnings.catch_warnings(record=True) as warns: + obs = qdb.software.Software.from_file(fp) + obs_warns = [str(w.message) for w in warns] + exp_warns = [] + self.assertCountEqual(obs_warns, exp_warns) + + self.assertEqual(obs, qdb.software.Software(3)) + self.assertEqual(obs.publications, []) + + # Raise an error if changing plugin type + fd, fp = mkstemp(suffix='.conf') + close(fd) + self._clean_up_files.append(fp) + with open(fp, 'w') as f: + f.write(CONF_TEMPLATE % + ("NewPlugin", "0.0.1", "Some description", + "source activate newplug", "start_new_plugin", + "artifact transformation", "", client_id, + client_secret)) + QE = qdb.exceptions + with self.assertRaises(QE.QiitaDBOperationNotPermittedError): + qdb.software.Software.from_file(fp) + + # Raise an error if client_id or client_secret are different + fd, fp = mkstemp(suffix='.conf') + close(fd) + self._clean_up_files.append(fp) + with open(fp, 'w') as f: + f.write(CONF_TEMPLATE % + ('Target Gene type', '0.1.0', + 'Target gene artifact types plugin', + 'source activate qiita', 'start_target_gene_types', + 'artifact definition', '', 'client_id', 'client_secret')) + + with self.assertRaises(QE.QiitaDBOperationNotPermittedError): + qdb.software.Software.from_file(fp) + + # But allow to update if update = True + obs = qdb.software.Software.from_file(fp, update=True) + self.assertEqual(obs, qdb.software.Software(3)) + self.assertEqual(obs.client_id, 'client_id') + self.assertEqual(obs.client_secret, 'client_secret') + + def test_exists(self): + self.assertTrue(qdb.software.Software.exists("QIIMEq2", "1.9.1")) + self.assertFalse(qdb.software.Software.exists("NewPlugin", "1.9.1")) + self.assertFalse(qdb.software.Software.exists("QIIME", "2.0.0")) + + def test_create(self): + obs = qdb.software.Software.create( + "New Software", "0.1.0", + "This is adding a new software for testing", "env_name", + "start_plugin", "artifact transformation") + self.assertEqual(obs.name, "New Software") + self.assertEqual(obs.version, "0.1.0") + self.assertEqual(obs.description, + "This is adding a new software for testing") + self.assertEqual(obs.commands, []) + self.assertEqual(obs.publications, []) + self.assertEqual(obs.environment_script, 'env_name') + self.assertEqual(obs.start_script, 'start_plugin') + self.assertEqual(obs.type, 'artifact transformation') + self.assertIsNotNone(obs.client_id) + self.assertIsNotNone(obs.client_secret) + self.assertFalse(obs.active) + + # create with publications + exp_publications = [['10.1000/nmeth.f.101', '12345678'], + ['10.1001/nmeth.f.101', '23456789']] + obs = qdb.software.Software.create( + "Published Software", "1.0.0", "Another testing software", + "env_name", "start_plugin", "artifact transformation", + publications=exp_publications) + self.assertEqual(obs.name, "Published Software") + self.assertEqual(obs.version, "1.0.0") + self.assertEqual(obs.description, "Another testing software") + self.assertEqual(obs.commands, []) + self.assertEqual(obs.publications, exp_publications) + self.assertEqual(obs.environment_script, 'env_name') + self.assertEqual(obs.start_script, 'start_plugin') + self.assertEqual(obs.type, 'artifact transformation') + self.assertIsNotNone(obs.client_id) + self.assertIsNotNone(obs.client_secret) + self.assertFalse(obs.active) + + # Create with client_id, client_secret + obs = qdb.software.Software.create( + "Another Software", "0.1.0", + "This is adding another software for testing", "env_a_name", + "start_plugin_script", "artifact transformation", + client_id='SomeNewClientId', client_secret='SomeNewClientSecret') + self.assertEqual(obs.name, "Another Software") + self.assertEqual(obs.version, "0.1.0") + self.assertEqual(obs.description, + "This is adding another software for testing") + self.assertEqual(obs.commands, []) + self.assertEqual(obs.publications, []) + self.assertEqual(obs.environment_script, 'env_a_name') + self.assertEqual(obs.start_script, 'start_plugin_script') + self.assertEqual(obs.type, 'artifact transformation') + self.assertEqual(obs.client_id, 'SomeNewClientId') + self.assertEqual(obs.client_secret, 'SomeNewClientSecret') + self.assertFalse(obs.active) + + def test_add_publications(self): + obs = qdb.software.Software.create( + "New Software", "0.1.0", + "This is adding a new software for testing", "env_name", + "start_plugin", "artifact transformation") + self.assertEqual(obs.publications, []) + obs.add_publications([['10.1000/nmeth.f.101', '12345678']]) + exp = [['10.1000/nmeth.f.101', '12345678']] + self.assertCountEqual(obs.publications, exp) + + # Add a publication that already exists + obs.add_publications([['10.1000/nmeth.f.101', '12345678']]) + self.assertCountEqual(obs.publications, exp) + + def test_activate(self): + qdb.software.Software.deactivate_all() + obs = qdb.software.Software(1) + self.assertFalse(obs.active) + obs.activate() + self.assertTrue(obs.active) + + def test_deprecated(self): + tester = qdb.software.Software(1) + self.assertFalse(tester.deprecated) + + tester.deprecated = True + self.assertTrue(tester.deprecated) + + tester.deprecated = False + self.assertFalse(tester.deprecated) + + with self.assertRaises(ValueError): + tester.deprecated = 'error!' + + +@qiita_test_checker() +class DefaultParametersTests(TestCase): + def test_exists(self): + cmd = qdb.software.Command(1) + obs = qdb.software.DefaultParameters.exists( + cmd, max_bad_run_length=3, min_per_read_length_fraction=0.75, + sequence_max_n=0, rev_comp_barcode=False, + rev_comp_mapping_barcodes=False, rev_comp=False, + phred_quality_threshold=3, barcode_type="golay_12", + max_barcode_errors=1.5, phred_offset='auto') + self.assertTrue(obs) + + obs = qdb.software.DefaultParameters.exists( + cmd, max_bad_run_length=3, min_per_read_length_fraction=0.65, + sequence_max_n=0, rev_comp_barcode=False, + rev_comp_mapping_barcodes=False, rev_comp=False, + phred_quality_threshold=3, barcode_type="hamming_8", + max_barcode_errors=1.5, phred_offset='auto') + self.assertFalse(obs) + + def test_name(self): + self.assertEqual(qdb.software.DefaultParameters(1).name, "Defaults") + + def test_values(self): + exp = {'min_per_read_length_fraction': 0.75, + 'max_barcode_errors': 1.5, 'max_bad_run_length': 3, + 'rev_comp': False, 'phred_quality_threshold': 3, + 'rev_comp_barcode': False, 'sequence_max_n': 0, + 'barcode_type': 'golay_12', 'rev_comp_mapping_barcodes': False, + 'phred_offset': 'auto'} + self.assertEqual(qdb.software.DefaultParameters(1).values, exp) + + def test_command(self): + self.assertEqual( + qdb.software.DefaultParameters(1).command, qdb.software.Command(1)) + + def test_create(self): + cmd = qdb.software.Command(1) + obs = qdb.software.DefaultParameters.create( + "test_create", cmd, max_bad_run_length=3, + min_per_read_length_fraction=0.75, sequence_max_n=0, + rev_comp_barcode=False, rev_comp_mapping_barcodes=False, + rev_comp=False, phred_quality_threshold=3, + barcode_type="hamming_8", max_barcode_errors=1.5, + phred_offset='auto') + self.assertEqual(obs.name, "test_create") + + exp = {'max_bad_run_length': 3, 'min_per_read_length_fraction': 0.75, + 'sequence_max_n': 0, 'rev_comp_barcode': False, + 'rev_comp_mapping_barcodes': False, 'rev_comp': False, + 'phred_quality_threshold': 3, 'barcode_type': "hamming_8", + 'max_barcode_errors': 1.5, 'phred_offset': 'auto'} + self.assertEqual(obs.values, exp) + self.assertEqual(obs.command, cmd) + + +class ParametersTests(TestCase): + def test_init_error(self): + with self.assertRaises( + qdb.exceptions.QiitaDBOperationNotPermittedError): + qdb.software.Parameters({'a': 1}, None) + + def test_eq(self): + # Test difference due to type + a = qdb.software.Parameters.from_default_params( + qdb.software.DefaultParameters(1), {'input_data': 1}) + b = qdb.software.DefaultParameters(1) + self.assertFalse(a == b) + # Test difference due to command + b = qdb.software.Parameters.from_default_params( + next(qdb.software.Command(2).default_parameter_sets), + {'input_data': 1}) + self.assertFalse(a == b) + # Test difference due to values + b = qdb.software.Parameters.from_default_params( + qdb.software.DefaultParameters(1), {'input_data': 2}) + self.assertFalse(a == b) + # Test equality + b = qdb.software.Parameters.from_default_params( + qdb.software.DefaultParameters(1), {'input_data': 1}) + self.assertTrue(a == b) + + def test_load_json(self): + json_str = ('{"barcode_type": "golay_12", "input_data": 1, ' + '"max_bad_run_length": 3, "max_barcode_errors": 1.5, ' + '"min_per_read_length_fraction": 0.75, ' + '"phred_quality_threshold": 3, "rev_comp": false, ' + '"rev_comp_barcode": false, "phred_offset": "auto", ' + '"rev_comp_mapping_barcodes": false, "sequence_max_n": 0}') + cmd = qdb.software.Command(1) + obs = qdb.software.Parameters.load(cmd, json_str=json_str) + exp_values = { + "barcode_type": "golay_12", "input_data": 1, + "max_bad_run_length": 3, "max_barcode_errors": 1.5, + "min_per_read_length_fraction": 0.75, + "phred_quality_threshold": 3, "rev_comp": False, + "rev_comp_barcode": False, "rev_comp_mapping_barcodes": False, + "sequence_max_n": 0, "phred_offset": "auto"} + self.assertEqual(obs.values, exp_values) + + def test_load_dictionary(self): + exp_values = { + "barcode_type": "golay_12", "input_data": 1, + "max_bad_run_length": 3, "max_barcode_errors": 1.5, + "min_per_read_length_fraction": 0.75, + "phred_quality_threshold": 3, "rev_comp": False, + "rev_comp_barcode": False, "rev_comp_mapping_barcodes": False, + "sequence_max_n": 0, "phred_offset": "auto"} + cmd = qdb.software.Command(1) + obs = qdb.software.Parameters.load(cmd, values_dict=exp_values) + self.assertEqual(obs.values, exp_values) + + def test_load_error_missing_required(self): + json_str = ('{"barcode_type": "golay_12",' + '"max_bad_run_length": 3, "max_barcode_errors": 1.5, ' + '"min_per_read_length_fraction": 0.75, ' + '"phred_quality_threshold": 3, "rev_comp": false, ' + '"rev_comp_barcode": false, "phred_offset": "auto", ' + '"rev_comp_mapping_barcodes": false, "sequence_max_n": 0}') + cmd = qdb.software.Command(1) + with self.assertRaises(qdb.exceptions.QiitaDBError): + qdb.software.Parameters.load(cmd, json_str=json_str) + + def test_load_loads_defaults(self): + values = { + "barcode_type": "golay_12", "input_data": 1, + "phred_quality_threshold": 3, "rev_comp": False, + "rev_comp_barcode": False, "rev_comp_mapping_barcodes": False, + "sequence_max_n": 0, "phred_offset": "auto"} + cmd = qdb.software.Command(1) + obs = qdb.software.Parameters.load(cmd, values_dict=values) + values.update({ + "max_bad_run_length": '3', "max_barcode_errors": '1.5', + "min_per_read_length_fraction": '0.75'}) + self.assertEqual(obs.values, values) + + def test_load_error_extra_parameters(self): + json_str = ('{"barcode_type": "golay_12", "input_data": 1, ' + '"max_bad_run_length": 3, "max_barcode_errors": 1.5, ' + '"min_per_read_length_fraction": 0.75, ' + '"phred_quality_threshold": 3, "rev_comp": false, ' + '"rev_comp_barcode": false, "phred_offset": "auto",' + '"rev_comp_mapping_barcodes": false, "sequence_max_n": 0,' + '"extra_param": 1}') + cmd = qdb.software.Command(1) + with self.assertRaises(qdb.exceptions.QiitaDBError): + qdb.software.Parameters.load(cmd, json_str=json_str) + + def test_from_default_parameters(self): + obs = qdb.software.Parameters.from_default_params( + qdb.software.DefaultParameters(1), {'input_data': 1}) + self.assertEqual(obs._command, qdb.software.Command(1)) + exp = {'min_per_read_length_fraction': 0.75, + 'max_barcode_errors': 1.5, 'max_bad_run_length': 3, + 'rev_comp': False, 'phred_quality_threshold': 3, + 'rev_comp_barcode': False, 'sequence_max_n': 0, + 'barcode_type': 'golay_12', 'rev_comp_mapping_barcodes': False, + 'input_data': 1, 'phred_offset': 'auto'} + self.assertEqual(obs._values, exp) + + obs = qdb.software.Parameters.from_default_params( + qdb.software.DefaultParameters(1), {'input_data': 1}, + opt_params={'max_bad_run_length': 5}) + self.assertEqual(obs._command, qdb.software.Command(1)) + exp = {'min_per_read_length_fraction': 0.75, + 'max_barcode_errors': 1.5, 'max_bad_run_length': 5, + 'rev_comp': False, 'phred_quality_threshold': 3, + 'rev_comp_barcode': False, 'sequence_max_n': 0, + 'barcode_type': 'golay_12', 'rev_comp_mapping_barcodes': False, + 'input_data': 1, 'phred_offset': 'auto'} + self.assertEqual(obs._values, exp) + + def test_from_default_params_error_missing_reqd(self): + with self.assertRaises(qdb.exceptions.QiitaDBError): + qdb.software.Parameters.from_default_params( + qdb.software.DefaultParameters(1), {}) + + def test_from_default_params_error_extra_reqd(self): + with self.assertRaises(qdb.exceptions.QiitaDBError): + qdb.software.Parameters.from_default_params( + qdb.software.DefaultParameters(1), + {'input_data': 1, 'another_one': 2}) + + def test_from_default_params_error_extra_opts(self): + with self.assertRaises(qdb.exceptions.QiitaDBError): + qdb.software.Parameters.from_default_params( + qdb.software.DefaultParameters(1), {'input_data': 1}, + opt_params={'Unknown': 'foo'}) + + def test_command(self): + obs = qdb.software.Parameters.from_default_params( + qdb.software.DefaultParameters(1), {'input_data': 1}).command + self.assertEqual(obs, qdb.software.Command(1)) + + def test_values(self): + obs = qdb.software.Parameters.from_default_params( + qdb.software.DefaultParameters(1), {'input_data': 1}).values + exp = {'min_per_read_length_fraction': 0.75, + 'max_barcode_errors': 1.5, 'max_bad_run_length': 3, + 'rev_comp': False, 'phred_quality_threshold': 3, + 'rev_comp_barcode': False, 'sequence_max_n': 0, + 'barcode_type': 'golay_12', 'rev_comp_mapping_barcodes': False, + 'phred_offset': 'auto', 'input_data': 1} + self.assertEqual(obs, exp) + + def test_dumps(self): + obs = qdb.software.Parameters.from_default_params( + qdb.software.DefaultParameters(1), {'input_data': 1}).dump() + exp = ('{"barcode_type": "golay_12", "input_data": 1, ' + '"max_bad_run_length": 3, "max_barcode_errors": 1.5, ' + '"min_per_read_length_fraction": 0.75, "phred_offset": "auto", ' + '"phred_quality_threshold": 3, "rev_comp": false, ' + '"rev_comp_barcode": false, ' + '"rev_comp_mapping_barcodes": false, "sequence_max_n": 0}') + self.assertEqual(obs, exp) + + +class DefaultWorkflowNodeTests(TestCase): + def test_default_parameter(self): + obs = qdb.software.DefaultWorkflowNode(1) + self.assertEqual( + obs.default_parameter, qdb.software.DefaultParameters(1)) + + obs = qdb.software.DefaultWorkflowNode(2) + self.assertEqual( + obs.default_parameter, qdb.software.DefaultParameters(10)) + + +class DefaultWorkflowEdgeTests(TestCase): + def test_connections(self): + tester = qdb.software.DefaultWorkflowEdge(1) + obs = tester.connections + self.assertEqual( + obs, [['demultiplexed', 'input_data', 'Demultiplexed']]) + + +class DefaultWorkflowTests(TestCase): + def test_name(self): + self.assertEqual(qdb.software.DefaultWorkflow(1).name, + "FASTQ upstream workflow") + self.assertEqual(qdb.software.DefaultWorkflow(2).name, + "FASTA upstream workflow") + self.assertEqual(qdb.software.DefaultWorkflow(3).name, + "Per sample FASTQ upstream workflow") + + def test_graph(self): + obs = qdb.software.DefaultWorkflow(1).graph + self.assertTrue(isinstance(obs, nx.DiGraph)) + exp = [qdb.software.DefaultWorkflowNode(1), + qdb.software.DefaultWorkflowNode(2)] + self.assertCountEqual(obs.nodes(), exp) + exp = [(qdb.software.DefaultWorkflowNode(1), + qdb.software.DefaultWorkflowNode(2), + {'connections': qdb.software.DefaultWorkflowEdge(1)})] + self.assertCountEqual(obs.edges(data=True), exp) + + obs = qdb.software.DefaultWorkflow(2).graph + self.assertTrue(isinstance(obs, nx.DiGraph)) + exp = [qdb.software.DefaultWorkflowNode(3), + qdb.software.DefaultWorkflowNode(4)] + self.assertCountEqual(obs.nodes(), exp) + exp = [(qdb.software.DefaultWorkflowNode(3), + qdb.software.DefaultWorkflowNode(4), + {'connections': qdb.software.DefaultWorkflowEdge(2)})] + self.assertCountEqual(obs.edges(data=True), exp) + + def test_parameters(self): + empty_params = {'prep': {}, 'sample': {}} + dw = qdb.software.DefaultWorkflow(1) + self.assertEqual(dw.parameters, empty_params) + + values = { + 'sample': {'environment_name': 'human'}, + 'prep': {'instrument_mode': 'MiSeq'}, + 'extra': {'x': 'y'} + } + with self.assertRaises(ValueError): + dw.parameters = values + + del values['extra'] + dw.parameters = values + self.assertEqual(values, dw.parameters) + dw.parameters = empty_params + self.assertEqual(empty_params, dw.parameters) + + +CONF_TEMPLATE = """[main] +NAME = %s +VERSION = %s +DESCRIPTION = %s +ENVIRONMENT_SCRIPT = %s +START_SCRIPT = %s +PLUGIN_TYPE = %s +PUBLICATIONS = %s + +[oauth2] +CLIENT_ID = %s +CLIENT_SECRET = %s +""" + + +if __name__ == '__main__': + main()