diff --git a/DaVinciExamples/python/DaVinciExamples/tupling/option_davinci_tupling_from_hlt2_gaudirun.py b/DaVinciExamples/python/DaVinciExamples/tupling/option_davinci_tupling_from_hlt2_gaudirun.py index 3bc124216646f766ea7fd9686c397c8c4b80309b..3365f1475414d796f96ec68703f5c25440e7b2fa 100644 --- a/DaVinciExamples/python/DaVinciExamples/tupling/option_davinci_tupling_from_hlt2_gaudirun.py +++ b/DaVinciExamples/python/DaVinciExamples/tupling/option_davinci_tupling_from_hlt2_gaudirun.py @@ -1,5 +1,5 @@ ############################################################################### -# (c) Copyright 2021-2022 CERN for the benefit of the LHCb Collaboration # +# (c) Copyright 2021-2022 CERN for the benefit of the LHCb Collaboration # # # # This software is distributed under the terms of the GNU General Public # # Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". # @@ -15,7 +15,7 @@ import Functors as F from FunTuple import FunctorCollection from FunTuple import FunTuple_Particles as Funtuple from PyConf.application import make_data_with_FetchDataFromFile -from DaVinci.ConfigurationUpgrade import run_davinci_app +from DaVinci.Configuration import run_davinci_app from DaVinci.reco_objects import make_pvs_for from DaVinci.algorithms import add_filter from DaVinci import options diff --git a/DaVinciSys/scripts/davinci b/DaVinciSys/scripts/davinci index 8277a839b192c357835da1c2af93cec193214aff..930e3b7226512ec9327272602b66ddc8b5303958 100755 --- a/DaVinciSys/scripts/davinci +++ b/DaVinciSys/scripts/davinci @@ -30,7 +30,7 @@ import os, sys, click from DaVinci.utilities_script import (dump_call, get_configurable_opts, set_testfiledb, create_inputdb_template, create_jobopt_template, get_dummy_config) -from DaVinci.ConfigurationUpgrade import run_davinci_app +from DaVinci.Configuration import run_davinci_app from DaVinci.config import options from DaVinci.optionChecker import log_click diff --git a/DaVinciSys/tests/test_davinci_script.py b/DaVinciSys/tests/test_davinci_script.py index 7be0a26397fbc61cf4c1ec934c0c21adbf9e17f4..cfe35640245c00d09b9c5448a4b211c37eab95fd 100644 --- a/DaVinciSys/tests/test_davinci_script.py +++ b/DaVinciSys/tests/test_davinci_script.py @@ -12,6 +12,12 @@ import subprocess, os import pytest +from pathlib import Path +DIR = Path(__file__).parent.resolve() + +from unittest.mock import Mock, patch +input_mock = Mock() + def test_default(): """ @@ -34,7 +40,8 @@ def test_override_job_option(): Verify the overriding of a DaVinci option on the fly. No need to actually run the job, hence the dry run. """ - cmd = 'davinci --export test_override_job_option.opts --dry-run run-mc --override_data_options --process "Turbo" --inputfiledb Upgrade_Bd2KstarMuMu -' + cmd = 'davinci --export test_override_job_option.opts --dry-run run-mc --override_data_options '\ + ' --process "Turbo" --inputfiledb Upgrade_Bd2KstarMuMu -' result = subprocess.run(cmd, shell=True) # Just be maniac - the command should work ;-) @@ -43,7 +50,6 @@ def test_override_job_option(): # Inspect the produced .opts file and check for the option value overridden with open("test_override_job_option.opts") as f: assert any('.process = "Turbo";' in line for line in f.readlines()) - os.remove("test_override_job_option.opts") @@ -54,7 +60,8 @@ def test_override_job_option_bis(): (see related test "test_override_job_option"). No need to actually run the job, hence the dry run. """ - cmd = 'davinci --export test_override_job_option_bis.opts --dry-run run-mc --process "Turbo" --override_data_options --inputfiledb Upgrade_Bd2KstarMuMu -' + cmd = 'davinci --export test_override_job_option_bis.opts --dry-run run-mc --process "Turbo" '\ + ' --override_data_options --inputfiledb Upgrade_Bd2KstarMuMu -' result = subprocess.run(cmd, shell=True) # Just be maniac - the command should work ;-) @@ -69,8 +76,8 @@ def test_override_job_option_bis(): def test_run_option_shortcut(): """ - Verify that the option values passed to the davinci command are accepted also - when not using the full name (e.g. --inputfiledb ==> -i). + Verify that the option values passed to the davinci command are accepted + also when not using the full name (e.g. --inputfiledb ==> -i). """ cmd = 'davinci --dry-run -o test_run_option_shortcut.opts run-mc -i Upgrade_Bd2KstarMuMu -' result = subprocess.run(cmd, shell=True) @@ -87,7 +94,8 @@ def test_run_option_shortcut(): def test_create_options_templates(): """ - Verify that the 'create-options-templates' command produces a template for the inputfiledb and joboptfile option files. + Verify that the 'create-options-templates' command produces a template + for the inputfiledb and joboptfile option files. """ cmd = 'davinci create-options-templates -f test_inputdb_template.yaml test_jobopt_template.yaml' result = subprocess.run(cmd, shell=True) @@ -96,12 +104,89 @@ def test_create_options_templates(): assert result.returncode == 0 import yaml - with open( - os.path.expandvars("test_inputdb_template.yaml")) as inputdb_file: + with open("test_inputdb_template.yaml") as inputdb_file: assert yaml.safe_load(inputdb_file) - with open(os.path.expandvars("test_jobopt_template.yaml")) as jobopt_file: + with open("test_jobopt_template.yaml") as jobopt_file: assert yaml.safe_load(jobopt_file) os.remove('test_inputdb_template.yaml') os.remove('test_jobopt_template.yaml') + + +def test_create_template_interactively_changing_defaults(pytestconfig): + """ + Verify that the interactive method of 'create-joboption-template' command produces + a correct job option template with the values defined by the user. + """ + import yaml, os + from DaVinci.utilities_script import create_jobopt_template + from DaVinci.options_default import __optsDict__ as dv_defaults + + #Changing some property values from the default for dumping the option file. + tmp_values = {"annsvc_config": "test_annsvc.tck.json"} + + list_data_qualifiers = [ + "data_type", "input_type", "simulation", "conddb_tag", "dddb_tag", + "input_files" + ] + + list_of_args = [] + for name, config in dv_defaults.items(): + if name not in list_data_qualifiers: + val = tmp_values[name] if name in tmp_values else config["value"] + list_of_args.append(val) + + input_mock.side_effect = list_of_args + + def test_prompt_values(): + with patch('builtins.input', input_mock): + create_jobopt_template(DIR / "test_jobopt_template.yaml", True) + + capmanager = pytestconfig.pluginmanager.getplugin('capturemanager') + capmanager.suspend_global_capture(in_=True) + test_prompt_values() + capmanager.resume_global_capture() + + config = [] + with open(DIR / "test_jobopt_template.yaml") as jobopt_file: + config = yaml.safe_load(jobopt_file) + assert isinstance(config, dict) + assert config["annsvc_config"] == "test_annsvc.tck.json" + + os.remove(DIR / 'test_jobopt_template.yaml') + input_mock.reset_mock(return_value=True, side_effect=True) + + +def test_create_template_interactively_with_defaults(pytestconfig): + """ + Verify that the interactive method of 'create-joboption-template' command doesn't produce + a job option template if all the values are equal to the default ones. + """ + import os + from DaVinci.utilities_script import create_jobopt_template + from DaVinci.options_default import __optsDict__ as dv_defaults + + list_data_qualifiers = [ + "data_type", "input_type", "simulation", "conddb_tag", "dddb_tag", + "input_files" + ] + + list_of_args = [] + for name, config in dv_defaults.items(): + if name not in list_data_qualifiers: + list_of_args.append(config["value"]) + + input_mock.side_effect = list_of_args + + def test_prompt_values(): + with patch('builtins.input', input_mock): + create_jobopt_template(DIR / "test_jobopt_template.yaml", True) + + capmanager = pytestconfig.pluginmanager.getplugin('capturemanager') + capmanager.suspend_global_capture(in_=True) + test_prompt_values() + capmanager.resume_global_capture() + + assert not os.path.exists(DIR / "test_jobopt_template.yaml") + input_mock.reset_mock(return_value=True, side_effect=True) diff --git a/DaVinciTests/tests/options/option_davinci_gaudirun_job.py b/DaVinciTests/tests/options/option_davinci_gaudirun_job.py index 86a491155688aa82380c12998e9e196fb61cc2f2..6f8b4dc45cf7c93cae2509aa93b55a82e4b64693 100644 --- a/DaVinciTests/tests/options/option_davinci_gaudirun_job.py +++ b/DaVinciTests/tests/options/option_davinci_gaudirun_job.py @@ -1,5 +1,5 @@ ############################################################################### -# (c) Copyright 2021 CERN for the benefit of the LHCb Collaboration # +# (c) Copyright 2021-2022 CERN for the benefit of the LHCb Collaboration # # # # This software is distributed under the terms of the GNU General Public # # Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". # @@ -34,7 +34,7 @@ options.enable_unpack = False options.user_algorithms = "../options/option_davinci_gaudirun_job:main" options.write_fsr = False -from DaVinci.ConfigurationUpgrade import run_davinci_app +from DaVinci.Configuration import run_davinci_app fileDB_key = "Upgrade_Bd2KstarMuMu_ldst" fileDB_path = "$DAVINCIROOT/options/DaVinciDB-Example.yaml" run_davinci_app(fileDB_key, fileDB_path) diff --git a/DaVinciTests/tests/options/prod_conf/options_reference.py b/DaVinciTests/tests/options/prod_conf/options_reference.py index c9216b661ac620f0cf541df80c713b47a5e4b3e9..b00d02c0b82b4df9a2fc1931292af5c616031478 100644 --- a/DaVinciTests/tests/options/prod_conf/options_reference.py +++ b/DaVinciTests/tests/options/prod_conf/options_reference.py @@ -1,5 +1,5 @@ ############################################################################### -# (c) Copyright 2021 CERN for the benefit of the LHCb Collaboration # +# (c) Copyright 2021-2022 CERN for the benefit of the LHCb Collaboration # # # # This software is distributed under the terms of the GNU General Public # # Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". # @@ -9,7 +9,7 @@ # or submit itself to any jurisdiction. # ############################################################################### from DaVinci import options -from DaVinci.ConfigurationUpgrade import run_davinci_app +from DaVinci.Configuration import run_davinci_app from DaVinci.compat.tests.params import prod_conf_params options.conddb_tag = prod_conf_params["CondDBTag"] diff --git a/Phys/DaVinci/python/DaVinci/ConfigurationUpgrade.py b/Phys/DaVinci/python/DaVinci/Configuration.py similarity index 92% rename from Phys/DaVinci/python/DaVinci/ConfigurationUpgrade.py rename to Phys/DaVinci/python/DaVinci/Configuration.py index cb63e58f4190a9921fb744e07c45a33cf41be993..a108ba34815c1392a90968fac7a224bc8fb0daf2 100644 --- a/Phys/DaVinci/python/DaVinci/ConfigurationUpgrade.py +++ b/Phys/DaVinci/python/DaVinci/Configuration.py @@ -1,5 +1,5 @@ ############################################################################### -# (c) Copyright 2020-2021 CERN for the benefit of the LHCb Collaboration # +# (c) Copyright 2020-2022 CERN for the benefit of the LHCb Collaboration # # # # This software is distributed under the terms of the GNU General Public # # Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". # @@ -15,7 +15,8 @@ from PyConf.application import ComponentConfig, configure, configure_input from PyConf.Algorithms import EventAccounting from GaudiConf import reading from DaVinci.configOptions import (check_options, set_job_options, - set_args_options, set_input_file_options) + set_properties, set_args_options, + set_input_file_options) from DaVinci.algorithms import (setup_algorithms, define_fsr_writer, apply_filters_and_unpacking) from DaVinci.config import davinci_control_flow, prepare_davinci_nodes @@ -57,6 +58,11 @@ def run_davinci_app(fileDB_key="", """ from DaVinci import options + # Workaround ConfigurableUser limitation: options.<name> cannot be called if a value is not + # explicitely assigned and the related property is not set. + # Initialization in DVAppOptions class seems to be not sufficient. + set_properties(options) + if fileDB_key and not prod_conf: set_input_file_options(options, fileDB_key, fileDB_file) if jobOpt_file: diff --git a/Phys/DaVinci/python/DaVinci/algorithms.py b/Phys/DaVinci/python/DaVinci/algorithms.py index 3b5940cf57007cecccd24893758c8cc17c619bb6..ca4ad5432e79b4cfa9a2373c477027429fa16da3 100644 --- a/Phys/DaVinci/python/DaVinci/algorithms.py +++ b/Phys/DaVinci/python/DaVinci/algorithms.py @@ -13,7 +13,6 @@ import os, sys, importlib from GaudiKernel.ProcessJobOptions import importOptions from PyConf.Algorithms import (LoKi__HDRFilter as HDRFilter, LoKi__VoidFilter as VoidFilter) -from DaVinci.configOptions import get_option_value from DaVinci.optionChecker import DVImportError, log_click @@ -28,7 +27,7 @@ def setup_algorithms(options): - Dict of the algorithm instances to be run in the job. - List of public tool instances to configure. """ - opts = get_option_value(options, "main_options") + opts = options.main_options publicTools = [] dvAlgs = {} @@ -39,7 +38,7 @@ def setup_algorithms(options): "INFO", "No MainOptions specified. DaVinci will import no options file!") - userAlgName = get_option_value(options, 'user_algorithms') + userAlgName = options.user_algorithms if not userAlgName: log_click( "WARNING", @@ -51,9 +50,6 @@ def setup_algorithms(options): dvAlgs = {"UserAlgorithms": [VoidConsumer(OutputLevel=ERROR)]} else: userAlgs, publicTools = setup_user_algorithms(userAlgName) - - if type(userAlgs) == list: - userAlgs = {"UserAlgorithms": userAlgs} dvAlgs.update(userAlgs) return dvAlgs, publicTools @@ -148,7 +144,6 @@ def define_fsr_writer(options): Returns: - list of FSR algorithm instances to be configured. """ - from Gaudi.Configuration import INFO from PyConf.Algorithms import GenFSRMerge, RecordStream algs = [] @@ -156,10 +151,7 @@ def define_fsr_writer(options): mergeGenfsr = GenFSRMerge(name="GenFSRMerge") algs.append(mergeGenfsr) - outputLevel = get_option_value(options, "output_level") - if outputLevel == '': - outputLevel = INFO - + outputLevel = options.output_level recStream = RecordStream( name="FSROutputStreamDstWriter", OutputLevel=outputLevel, @@ -201,10 +193,6 @@ def setup_user_algorithms(userAlgPath): modulePath = os.path.expandvars(modulePath) sys.path.append(modulePath) - _, ext = os.path.splitext(algName) - if ext == ".py": - algName = algName.rsplit('.', 1)[0] - if ":" in algName: moduleName = algName.rsplit(":", 1)[0] funcName = ".%s" % algName.rsplit(":", 1)[1] @@ -212,6 +200,10 @@ def setup_user_algorithms(userAlgPath): moduleName = algName funcName = "" + _, ext = os.path.splitext(moduleName) + if ext == ".py": + moduleName = moduleName.rsplit('.', 1)[0] + # Import the module containing the user algorithms try: module = importlib.import_module(moduleName) # noqa: F841 @@ -252,8 +244,8 @@ def unpack_locations(options, unpack_only_mc): """ from GaudiConf import reading - process = get_option_value(options, "process") - stream = get_option_value(options, "stream") + process = options.process + stream = options.stream unpack_raw_event = reading.unpack_rawevent( bank_types=['ODIN', 'DstData', 'HltDecReports'], @@ -296,8 +288,8 @@ def get_hlt_reports(options, source=''): """ from GaudiConf import reading - process = get_option_value(options, "process") - stream = get_option_value(options, "stream") + process = options.process + stream = options.stream if source == 'Hlt2': dec_reports = reading.hlt2_decisions( diff --git a/Phys/DaVinci/python/DaVinci/compat/DaVinciConf.py b/Phys/DaVinci/python/DaVinci/compat/DaVinciConf.py index a15afd165a2147af54e8832e691827fa2546e472..922081b15c4162b831e1c6c39da5e0c6430f5140 100644 --- a/Phys/DaVinci/python/DaVinci/compat/DaVinciConf.py +++ b/Phys/DaVinci/python/DaVinci/compat/DaVinciConf.py @@ -1,5 +1,5 @@ ############################################################################### -# (c) Copyright 2021 CERN for the benefit of the LHCb Collaboration # +# (c) Copyright 2021-2022 CERN for the benefit of the LHCb Collaboration # # # # This software is distributed under the terms of the GNU General Public # # Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". # @@ -15,7 +15,7 @@ import re from Gaudi.Configuration import appendPostConfigAction from Configurables import (LHCbApp, EventSelector, DDDBConf, CondDB, LHCbConfigurableUser, HistogramPersistencySvc) -from DaVinci.ConfigurationUpgrade import run_davinci_app +from DaVinci.Configuration import run_davinci_app from DaVinci.configOptions import set_option_value from DaVinci import options diff --git a/Phys/DaVinci/python/DaVinci/configOptions.py b/Phys/DaVinci/python/DaVinci/configOptions.py index 61eab8e03a5a41bd8080930b903722dcf0761a4b..fcab94fc12f9f6c82f66354e81c8b683b27a50b5 100644 --- a/Phys/DaVinci/python/DaVinci/configOptions.py +++ b/Phys/DaVinci/python/DaVinci/configOptions.py @@ -1,5 +1,5 @@ ############################################################################### -# (c) Copyright 2020-2021 CERN for the benefit of the LHCb Collaboration # +# (c) Copyright 2020-2022 CERN for the benefit of the LHCb Collaboration # # # # This software is distributed under the terms of the GNU General Public # # Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". # @@ -17,20 +17,6 @@ from DaVinci.optionChecker import DVOptionError, DVRuntimeError from DaVinci.optionChecker import option_checker, log_click -def get_option_value(options, name): - """ - Get option value for a given name. - - Args: - - options: list of DaVinci options. - - name: name of the option to be retrieved. - - Returns: - - Value of the option of interest. - """ - return options.getProp(name) - - def set_option_value(options, name, value): """ Set option value for a given name. @@ -104,7 +90,8 @@ def set_input_file_options(options, fileDB_key, fileDB_file): set_option_value(options, "input_files", obj) -def set_job_options(options, jobOptFile, fileDB_key, fileDB_file, override_data_options): +def set_job_options(options, jobOptFile, fileDB_key, fileDB_file, + override_data_options): """ Set the job properties required by the user. The method checks if there are options related to the input files selected with key and file of the @@ -117,31 +104,29 @@ def set_job_options(options, jobOptFile, fileDB_key, fileDB_file, override_data_ - fileDB_file: file containing the testfileDB. - override_data_options: boolean for enabling data options override. """ - if jobOptFile == '': - log_click("WARNING", - "No jobOption file selected, the default values are used.") - else: - dataOptions = list_data_options(fileDB_key.split(":")[0], fileDB_file) - - with open(os.path.expandvars(jobOptFile)) as config_file: - _, ext = os.path.splitext(jobOptFile) - if ext in (".yaml", ".yml", ".json"): - import yaml - config = yaml.safe_load(config_file) - elif ext == '.py': - import ast - config = ast.literal_eval(config_file.read()) - else: - raise ValueError( - 'JobOption file extension not known! Please use only a .py, .json or .yaml (.yml) file!' - ) - - for key, value in config.items(): - if is_option_settable(options, key, dataOptions, override_data_options): - set_option_value(options, key, value) - - -def set_args_options(options, ctx_args, fileDB_key, fileDB_file, override_data_options): + dataOptions = list_data_options(fileDB_key.split(":")[0], fileDB_file) + + with open(os.path.expandvars(jobOptFile)) as config_file: + _, ext = os.path.splitext(jobOptFile) + if ext in (".yaml", ".yml", ".json"): + import yaml + config = yaml.safe_load(config_file) + elif ext == '.py': + import ast + config = ast.literal_eval(config_file.read()) + else: + raise ValueError( + 'JobOption file extension not known! Please use only a .py, .json or .yaml (.yml) file!' + ) + + for key, value in config.items(): + if is_option_settable(options, key, dataOptions, + override_data_options): + set_option_value(options, key, value) + + +def set_args_options(options, ctx_args, fileDB_key, fileDB_file, + override_data_options): """ Set the extra arguments required by the user. @@ -159,7 +144,8 @@ def set_args_options(options, ctx_args, fileDB_key, fileDB_file, override_data_o key = ctx_args[i][2:] value = ctx_args[i + 1] - if is_option_settable(options, key, dataOptions, override_data_options): + if is_option_settable(options, key, dataOptions, + override_data_options): set_option_value(options, key, value) @@ -230,33 +216,39 @@ def check_options(options): Args: - options: list of DaVinci options. """ - dataType = get_option_value(options, "data_type") + dataType = options.data_type option_checker("data_type", dataType) - inputType = get_option_value(options, "input_type").upper() + inputType = (options.input_type).upper() option_checker("input_type", inputType) if inputType != "MDF": set_option_value(options, "input_type", "ROOT") - flagMC = get_option_value(options, "simulation") + flagMC = options.simulation if not flagMC: - if get_option_value(options, "merge_genfsr"): + if options.merge_genfsr: raise DVRuntimeError( "merge_genfsr", "simulation", "GenFSR are not available in real data so merge_genfsr cannot be run! Set merge_genfsr = False." ) else: - if get_option_value(options, "lumi"): + if options.lumi: raise DVRuntimeError( "Lumi", "simulation", "Lumi not valid for simulation! Set lumi = False.") ## for simulation, it is very important to specify proper DB-tags: - if not get_option_value(options, 'dddb_tag'): + if not options.dddb_tag: raise DVOptionError( "dddb_tag", "``dddb_tag'' is not specified for simulated data!") - if not get_option_value(options, 'conddb_tag'): + if not options.conddb_tag: raise DVOptionError( "conddb_tag", "``conddb_tag'' is not specified for simulated data!") + + +def set_properties(options): + for name, default in options.getDefaultProperties().items(): + if not options.isPropertySet(name): + options.setProp(name, default) diff --git a/Phys/DaVinci/python/DaVinci/optionChecker.py b/Phys/DaVinci/python/DaVinci/optionChecker.py index 71e8e4398a7cf80a9f9b58f48c8c88b2400fe76d..d52165800fa30e40fe2dc123b909adcf98b84354 100644 --- a/Phys/DaVinci/python/DaVinci/optionChecker.py +++ b/Phys/DaVinci/python/DaVinci/optionChecker.py @@ -1,5 +1,5 @@ ############################################################################### -# (c) Copyright 2020-2021 CERN for the benefit of the LHCb Collaboration # +# (c) Copyright 2020-2022 CERN for the benefit of the LHCb Collaboration # # # # This software is distributed under the terms of the GNU General Public # # Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". # @@ -11,6 +11,7 @@ """ Define value and run-time errors used in DaVinci application. """ +import click class DVOptionError(ValueError): @@ -28,7 +29,7 @@ class DVOptionError(ValueError): """ String representation. """ - return (set_color("red") + self.message + set_color("plain")) + return f"{click.style('DVOptionError', bold=True, fg='red')} {click.style(self.message, fg='red')}" class DVRuntimeError(RuntimeError): @@ -46,7 +47,7 @@ class DVRuntimeError(RuntimeError): """ String representation. """ - return (set_color("red") + self.message + set_color("plain")) + return f"{click.style('DVRuntimeError', bold=True, fg='red')} {click.style(self.message, fg='red')}" class DVImportError(ImportError): @@ -64,24 +65,48 @@ class DVImportError(ImportError): """ String representation. """ - return (set_color("red") + self.message + set_color("plain")) + return f"{click.style('DVImportError', bold=True, fg='red')} {click.style(self.message, fg='red')}" + + +class DVKeyError(KeyError): + """ + DaVinci exception class for accessing to an invalid key option. + """ + + def __init__(self, optName): + """ + Constructor. + """ + self.message = f"There is no option named {optName} in DaVinci!" + + def __str__(self): + """ + String representation. + """ + return f"{click.style('DVKeyError', bold=True, fg='red')} {click.style(self.message, fg='red')}" def option_checker(name, value): """ Check the correctness of a DaVinci configurable option. """ - try: - allowedValues = get_allowed_option_values() - if value not in allowedValues[name]: - print_allowed_option_values(allowedValues, name) - raise DVOptionError(name, value) - except KeyError: - print( - set_color("red") + - f"You are trying to check the unknown option {name}!" + - set_color("plain")) - print_allowed_option_values() + from DaVinci.options_default import __optsDict__ as dv_default + + allowedValues = get_allowed_option_values() + if name in dv_default.keys() and name not in allowedValues.keys(): + log_click( + "INFO", + f"The option {name} has no restrinctions on the allowed values.") + else: + try: + if value not in allowedValues[name]: + print_allowed_option_values(allowedValues, name) + raise DVOptionError(name, value) + except KeyError: + log_click("ERROR", + f"You are trying to check the unknown option {name}!") + print_allowed_option_values(allowedValues) + raise DVKeyError(name) def get_allowed_option_values(): @@ -96,7 +121,6 @@ def get_allowed_option_values(): "ROOT", "XGEN" ] } - return allowedValues @@ -105,29 +129,24 @@ def print_allowed_option_values(allowedValues, name=None): Print the allowed values for the DaVinci option configurations. """ if name is None: - print( - set_color("green") + - "Known job option configurations and allowed values:") - for name, values in allowedValues.iteritems(): - print(f"{name} \t : {values}", set_color("plain")) + log_click("", "Known job option configurations and allowed values:", + "green") + for name, values in allowedValues.items(): + log_click("", f"{name} \t : {values}") else: - print( - set_color("green") + f"Allowed values for DaVinci option {name}:") - print(allowedValues[name], set_color("plain")) + log_click("", f"Allowed values for DaVinci option {name}:", "green") + log_click("", f"{allowedValues[name]}") -def set_color(key): - colors = { - "plain": "\x1b[00m", - "red": "\x1b[01;31m", - "green": "\x1b[01;32m" - } - - return colors[key] - - -def log_click(mode, message): +def log_click(mode, message, color_text="white"): import click - colors = {"INFO": "blue", "WARNING": "yellow", "ERROR": "red"} + config_colors = { + "INFO": "blue", + "WARNING": "yellow", + "ERROR": "red", + } - click.echo(f"{click.style(mode, bold=True, fg=colors[mode])} {message}") + color_mode = config_colors[mode] if mode else "white" + click.echo( + f"{click.style(mode, bold=True, fg=color_mode)} {click.style(message, fg=color_text)}" + ) diff --git a/Phys/DaVinci/tests/config/test_DVAppOptions.py b/Phys/DaVinci/tests/config/test_DVAppOptions.py index ad75df5673f010f310bb01feaa3b650743261e4c..d3e321b31bdc152c243e33332579089d776de6f8 100644 --- a/Phys/DaVinci/tests/config/test_DVAppOptions.py +++ b/Phys/DaVinci/tests/config/test_DVAppOptions.py @@ -1,5 +1,5 @@ ############################################################################### -# (c) Copyright 2021 CERN for the benefit of the LHCb Collaboration # +# (c) Copyright 2021-2022 CERN for the benefit of the LHCb Collaboration # # # # This software is distributed under the terms of the GNU General Public # # Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". # @@ -8,27 +8,37 @@ # granted to it by virtue of its status as an Intergovernmental Organization # # or submit itself to any jurisdiction. # ############################################################################### - import pytest - from DaVinci import options def test_option_set(): + """ + Check if it's possible to access directly to an option if set. + """ print(options.detectors) def test_option_unset(): + """ + Check that it's impossible to access to an option if it has not been set. + """ with pytest.raises(AttributeError): print(options.evt_max) def test_option_get_after_set(): + """ + Check that after assigning a value the option can be accessed correctly. + """ options.evt_max = 1 print(options.evt_max) def test_option_unknown(): + """ + Check that it's impossible to access to an unknow option. + """ with pytest.raises(AttributeError): print(options.unknown) @@ -40,10 +50,8 @@ def test_ApplicationOptions_consistency_with_PyConf(): from PyConf.application import ApplicationOptions from DaVinci import options from DaVinci.options_default import __optsDict__ as dv_defaults - lhcb_options = ApplicationOptions.getDefaultProperties() dv_options = options.getDefaultProperties() - for name, _ in lhcb_options.items(): # Exclude Moore-specific options since they will be moved into a # dedicated Moore-specific ApplicationOptions class. diff --git a/Phys/DaVinci/tests/config/test_algorithms.py b/Phys/DaVinci/tests/config/test_algorithms.py new file mode 100644 index 0000000000000000000000000000000000000000..1c70d7355c45f821ada7ed82ad6086c1ba777e30 --- /dev/null +++ b/Phys/DaVinci/tests/config/test_algorithms.py @@ -0,0 +1,269 @@ +############################################################################### +# (c) Copyright 2022 CERN for the benefit of the LHCb Collaboration # +# # +# This software is distributed under the terms of the GNU General Public # +# Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". # +# # +# In applying this licence, CERN does not waive the privileges and immunities # +# granted to it by virtue of its status as an Intergovernmental Organization # +# or submit itself to any jurisdiction. # +############################################################################### +import pytest, os +from PyConf.Algorithms import Gaudi__Examples__VoidConsumer as VoidConsumer +from DaVinci import options +from DaVinci.optionChecker import DVImportError +from DaVinci.algorithms import (setup_algorithms, define_fsr_writer, + add_filter, apply_filters_and_unpacking, + setup_user_algorithms, unpack_locations, + ConfiguredFuntuple) + + +def test_import_main_options(): + """ + Check if a configurable .opts file can be imported correctly in DaVinci. + """ + filename = "test_options.opts" + with open(filename, "w") as f: + f.write("DVAppOptions.enable_unpack = True;\n") + f.write("DVAppOptions.evt_max = 5;\n") + f.close() + + options.main_options = filename + options.user_algorithms = "" + setup_algorithms(options) + os.remove(filename) + + # Restore default value avoid failure in the next tests + options.main_options = "" + + +def test_set_user_algorithms(): + """ + Check if DaVinci can import correctly an external user algorithm implemented in a given python module. + """ + filename = "test_useralgs" + with open(f"{filename}.py", "w") as f: + f.write(""" +from PyConf.Algorithms import Gaudi__Examples__VoidConsumer as VoidConsumer +from DaVinci import options + +options.data_type = 'Upgrade' +options.enable_unpack = False +options.evt_max = 10 + +def main(): + algs = {'testAlg': [VoidConsumer()]} + return algs, [] +""") + f.close() + + options.user_algorithms = f"{filename}:main" + test_algs, _ = setup_algorithms(options) + assert "testAlg" in test_algs + os.remove(f"{filename}.py") + + # Restore default value avoid failure in the next tests + options.user_algorithms = "" + + +def test_no_user_algorithms(): + """ + Check that no algorithms are imported if user doens't provide a python module. + """ + test_algs, _ = setup_user_algorithms("") + assert len(test_algs) == 0 + + +def test_user_algorithms_with_ext(): + """ + Check that a python module can be imported correcty + even if the '.py' extension is passed by mistake from the user. + """ + filename = "./test_useralg.py" + with open(filename, "w") as f: + f.write(""" +from PyConf.Algorithms import Gaudi__Examples__VoidConsumer as VoidConsumer +from DaVinci import options + +options.data_type = 'Upgrade' +options.enable_unpack = False +options.evt_max = 10 + +def main(): + algs = [VoidConsumer()] + return algs, [] +""") + f.close() + + test_algs, _ = setup_user_algorithms(f"{filename}:main") + assert "UserAlgorithms" in test_algs + os.remove(filename) + + +def test_user_algorithms_no_module(): + """ + Check DaVinci returns the correct error if the python module define by the user doesn't exist. + """ + filename = "test_useralg.py" + with pytest.raises((TypeError, DVImportError)): + test_algs, _ = setup_user_algorithms(filename) + + +def test_user_algorithms_no_function(): + """ + Check DaVinci returns the correct error if the user provide only the module + and not the name of the function to be run. + """ + filename = "test_useralg" + with open(f"{filename}.py", "w") as f: + f.write(""" +rom PyConf.Algorithms import Gaudi__Examples__VoidConsumer as VoidConsumer +from DaVinci import options + +options.data_type = 'Upgrade' +options.enable_unpack = False +options.evt_max = 10 +""") + f.close() + + with pytest.raises((TypeError, DVImportError)): + test_algs, _ = setup_user_algorithms(filename) + os.remove(f"{filename}.py") + + +def test_user_algorithms_wrong_function(): + """ + Check DaVinci returns the correct error if the function specified by the user doesn't exist. + """ + filename = "test_useralg" + with open(f"{filename}.py", "w") as f: + f.write(""" +from PyConf.Algorithms import Gaudi__Examples__VoidConsumer as VoidConsumer +from DaVinci import options + +options.data_type = 'Upgrade' +options.enable_unpack = False +options.evt_max = 10 + +def main(): + algs = {'testAlg': [VoidConsumer()]} + return algs, [] +""") + f.close() + + with pytest.raises((AttributeError, DVImportError)): + test_algs, _ = setup_user_algorithms(f"{filename}:main2") + os.remove(f"{filename}.py") + + +def test_define_write_fsr(): + """ + Check if DaVinci import correctly the algorithm to merge and write FSRs. + """ + options.output_level = 3 + options.merge_genfsr = True + options.simulation = True + test_algs = define_fsr_writer(options) + assert any("GenFSRMerge" == x.name for x in test_algs) + + +def test_add_hlt2_filter(): + """ + Check if DaVinci is able to implement correctly a fliter to a Hlt2 line. + """ + options.process = "Hlt2" + options.stream = "default" + test_filter = add_filter("test_filter", "HLT_PASS('Hlt2TESTLineDecision')") + assert "HDRFilter" in test_filter.fullname + + +def test_add_spruce_filter(): + """ + Check if DaVinci is able to implement correctly a fliter to a Sprucing line. + """ + options.process = "Spruce" + options.stream = "default" + test_filter = add_filter("test_filter", + "HLT_PASS('SpruceTESTLineDecision')") + assert "HDRFilter" in test_filter.fullname + + +def test_add_void_filter(): + """ + Check if DaVinci is able to implement correcty a Void filter + if 'HLT_PASS' string is not found in the filter code." + """ + test_filter = add_filter("test_filter", "VOIDTEST_Filter") + assert "VoidFilter" in test_filter.fullname + + +def test_unpack_locations(): + """ + Check if the unpacking algorithms are retrieved correctly from GaudiConf. + TO BE REMOVED WHEN THE UNPACKING WILL BECOME FULLY FUNCTIONAL. + """ + options.simulation = True + test_algs = unpack_locations(options, False) + + assert isinstance(test_algs, list) + assert any("Unpack" in alg.fullname for alg in test_algs) + + +def test_unpack_locations_xgen(): + """ + Check if DaVinci unpacks only the MC locations if run on an xgen file. + (i.e. 'unpack_only_mc' is set to True.) + TO BE REMOVED WHEN THE UNPACKING WILL BECOME FULLY FUNCTIONAL. + """ + options.simulation = True + unpack_only_mc = True + test_algs = unpack_locations(options, unpack_only_mc) + + assert isinstance(test_algs, list) + assert any("UnpackMCParticle" in alg.fullname for alg in test_algs) + assert not any("UnpackTrack" in alg.fullname for alg in test_algs) + + +def test_apply_filters_and_unpack(): + """ + Check if DaVinci applies correctly a filter in front of a given algorithm + and instantiates correctly the main unpacking algorithms. + TO BE UPDATED WHEN THE UNPACKING WILL BECOME FULLY FUNCTIONAL. + """ + options.evt_pre_filters = {"test_filter": "EVT_PREFILTER"} + options.enable_unpack = True + alg_dict = {"test_alg": [VoidConsumer()]} + test_alg_dict = apply_filters_and_unpacking(options, alg_dict, False) + list_of_main_expected_algs = [ + "LoKi__VoidFilter", "LHCb__UnpackRawEvent", "HltPackedDataDecoder", + "UnpackMCParticle", "UnpackTrack" + ] + + for exp_alg in list_of_main_expected_algs: + assert any( + exp_alg in alg.fullname for alg in test_alg_dict["test_alg"]) + + +def test_configured_funtuple(): + """ + Check if the ConfiguredFuntuple provides a correct instance of FunTuple. + """ + from FunTuple import FunctorCollection + from FunTuple.functorcollections import Kinematics + branches = {'B0': "[B0 -> D_s- pi+]CC"} + variables = {'B0': FunctorCollection(Kinematics)} + config = { + "TestTuple": { + "location": "/Event/Spruce/SpruceTestLine/Particles", + "filters": + ["HLT_PASS('SpruceTestLine1Decision')", "VoidTest_Filter"], + "preamble": ['TRACK_MAX_PT = MAXTREE(ISBASIC & HASTRACK, PT, -1)'], + "tree": "DecayTree", + "branches": branches, + "variables": variables, + } + } + + test_dict = ConfiguredFuntuple(config) + assert any("FunTupleBase_Particles/Tuple_TestTuple" in alg.fullname + for alg in test_dict["TestTuple"]) diff --git a/Phys/DaVinci/tests/config/test_configOptions.py b/Phys/DaVinci/tests/config/test_configOptions.py index ca07b39c6b8492fc7f92848794bfabf648b76493..4ed69482b1aa51165731f75c544a4c4bbc2a1f01 100644 --- a/Phys/DaVinci/tests/config/test_configOptions.py +++ b/Phys/DaVinci/tests/config/test_configOptions.py @@ -1,5 +1,5 @@ -############################################################################## -# (c) Copyright 2021 CERN for the benefit of the LHCb Collaboration # +############################################################################### +# (c) Copyright 2021-2022 CERN for the benefit of the LHCb Collaboration # # # # This software is distributed under the terms of the GNU General Public # # Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". # @@ -11,19 +11,211 @@ import pytest -from DaVinci.configOptions import list_data_options +from DaVinci.optionChecker import DVOptionError, DVRuntimeError +from DaVinci.configOptions import (list_data_options, set_option_value, + set_input_file_options, set_job_options, + check_options, is_option_settable) +from DaVinci import options +from pathlib import Path +DIR = Path(__file__).parent.resolve() + +fileDB_file = DIR / "../../options/DaVinciDB-Example.yaml" +fileDB_key = "Upgrade_Bd2KstarMuMu_ldst" +fileDB = "TestFileDB" +jobOpt_file = DIR / "../../options/jobOptions-Example" def test_list_data_options_key_in_TestFileDB(): - fileDB_key = "Upgrade_Bd2KstarMuMu" - fileDB_file = "TestFileDB" - assert list_data_options(fileDB_key, fileDB_file) == [ + """ + Check if the list of data options in TestFileDB matches the one expected by DaVinci. + """ + key = "Upgrade_Bd2KstarMuMu" + assert list_data_options(key, fileDB) == [ 'Author', 'Format', 'DataType', 'Date', 'Simulation', 'CondDB', 'DDDB' ] +def test_list_data_options_key_in_userDB(): + """ + Check if the list of data options in a TestFileDB-like file prepared as example is updated. + """ + assert list_data_options(fileDB_key, fileDB_file) == [ + 'data_type', 'input_type', 'simulation', 'conddb_tag', 'dddb_tag' + ] + + def test_list_data_options_unknown_key_in_TestFileDB(): + """ + Check if DaVinci returns an error if the key used for the TestFileDB is unknown. + """ with pytest.raises(KeyError): - fileDB_key = "UnknownKey" - fileDB_file = "TestFileDB" - _ = list_data_options(fileDB_key, fileDB_file) + key = "UnknownKey" + _ = list_data_options(key, fileDB) + + +def test_handle_options(): + """ + Check if DaVinci is able to set correctly the value of a given option. + """ + opt_name = "ntuple_file" + opt_value = "ntupleDV_test.root" + set_option_value(options, opt_name, opt_value) + assert options.getProp(opt_name) == opt_value + + +def test_option_correct_str2bool_conversion(): + """ + Check if DaVinci converts correctly the value of a boolean option even if the value is passed as string. + """ + opt_name = "enable_unpack" + opt_value = "True" + set_option_value(options, opt_name, opt_value) + assert options.getProp(opt_name) + + +def test_option_wrong_str2bool_conversion(): + """ + Check if DaVinci raises an error if the string-to-bool conversion is applied on a generic string content. + """ + opt_name = "enable_unpack" + opt_value = "unknown_value" + with pytest.raises(DVOptionError): + set_option_value(options, opt_name, opt_value) + + +def test_set_input_file_from_TestFileDB(): + """ + Check if DaVinci is able to set correctly input data options given a key and the TestFileDB. + """ + key = "Upgrade_Bd2KstarMuMu" + set_input_file_options(options, key, fileDB) + assert not options.input_files == [] + + +def test_set_input_file_from_userDB(): + """ + Check if DaVinci is able to set correctly input data options + given a key and TestFileDB-like file database defined by the user. + """ + set_input_file_options(options, fileDB_key, fileDB_file) + assert not options.input_files == [] + + +def test_select_input_file_by_index(): + """ + Check if DaVinci selects only a specific file from the ones listed in the database + if the file index is provided after the database key. + """ + key = f"{fileDB_key}:3" + set_input_file_options(options, key, fileDB_file) + assert len(options.input_files) == 1 + + +def test_wrong_input_file_index(): + """ + Check that DaVinci raises an error if the file index is higher than the number of + input files available in the TestFileDB-like database. + """ + key = f"{fileDB_key}:100" + with pytest.raises(ValueError): + set_input_file_options(options, key, fileDB_file) + + +def test_job_options_from_py_file(): + """ + Check if DaVinci sets correctly an option value when reading a .py job option file. + Check also the .py example is well formatted and readable by DaVinci. + """ + set_job_options(options, f"{jobOpt_file}.py", "", "", False) + assert options.evt_max == 100 + + +def test_job_options_from_yaml_file(): + """ + Check if DaVinci sets correctly an option value when reading a .yaml job option file. + Check also the .yaml example is well formatted and readable by DaVinci. + """ + set_job_options(options, f"{jobOpt_file}.yaml", "", "", False) + assert options.evt_max == 100 + + +def test_job_options_from_unknown_file(): + """ + Check that DaVinci raises an error if the job option file passed by the user doesn't exist. + """ + import os + job_opt_name = "test_job_options.txt" + with open(job_opt_name, "w") as f: + f.write("evt_max: 100") + f.close() + with pytest.raises(ValueError): + set_job_options(options, job_opt_name, "", "", False) + os.remove(job_opt_name) + + +def test_lumi_fails_on_MC(): + """ + Check if enabling lumi counters fails on simulated input files. + """ + options.simulation = True + options.lumi = True + with pytest.raises(DVRuntimeError): + check_options(options) + # Restore default value avoid failure in the next tests + options.lumi = False + + +def test_merge_genfsr_fails_on_data(): + """ + Check that DaVinci raises a runtime error when trying to merge genFSRs on real data. + """ + options.simulation = False + options.merge_genfsr = True + with pytest.raises(DVRuntimeError): + check_options(options) + # Restore default value avoid failure in the next tests + options.simulation = True + options.merge_genfsr = False + + +def test_mcJob_fails_without_dddb(): + """ + Check that DaVinci job fails on simulated data if no dddb_tag condition is set. + """ + options.dddb_tag = "" + with pytest.raises(DVOptionError): + check_options(options) + + +def test_mcJob_fails_without_conddb(): + """ + Check that DaVinci job fails on simulated data if no conddb_tag condition is set. + """ + options.dddb_tag = "dddb-20171126" + options.conddb_tag = "" + with pytest.raises(DVOptionError): + check_options(options) + # Restore default value avoid failure in the next tests + options.dddb_tag = "" + + +def test_set_data_option_without_override(): + """ + Check that DaVinci raises an error if the user tries to change the value of an input data option + without explicitely enabling the '--override' flag. + """ + override_data_options = False + data_options = list_data_options(fileDB_key, fileDB_file) + with pytest.raises(DVRuntimeError): + is_option_settable(options, "data_type", data_options, + override_data_options) + + +def test_set_data_option_with_override(): + """ + Check that DaVinci updates the value of an input data option if the '--override' flag is enabled. + """ + override_data_options = True + data_options = list_data_options(fileDB_key, fileDB_file) + assert is_option_settable(options, "data_type", data_options, + override_data_options) diff --git a/Phys/DaVinci/tests/config/test_configuration.py b/Phys/DaVinci/tests/config/test_configuration.py new file mode 100644 index 0000000000000000000000000000000000000000..36caaea546b7106999ad02d3fa55727e93d4b564 --- /dev/null +++ b/Phys/DaVinci/tests/config/test_configuration.py @@ -0,0 +1,81 @@ +############################################################################### +# (c) Copyright 2022 CERN for the benefit of the LHCb Collaboration # +# # +# This software is distributed under the terms of the GNU General Public # +# Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". # +# # +# In applying this licence, CERN does not waive the privileges and immunities # +# granted to it by virtue of its status as an Intergovernmental Organization # +# or submit itself to any jurisdiction. # +############################################################################### + +import pytest + +from DaVinci.Configuration import run_davinci_app +from DaVinci.config import prepare_davinci_nodes + +from pathlib import Path +DIR = Path(__file__).parent.resolve() + + +def test_run_app_dummy(): + """ + Check if DaVinci returns a ComponentConfig object even when passing only the input data. + """ + from PyConf.application import ComponentConfig + configurables = run_davinci_app( + fileDB_key="Upgrade_Bd2KstarMuMu", fileDB_file="TestFileDB") + assert isinstance(configurables, ComponentConfig) + + +def test_run_simple_job(): + """ + Check if DaVinci is able to set correctly the control flow application when running a simple job. + """ + config = run_davinci_app( + fileDB_key="Upgrade_Bd2KstarMuMu", + fileDB_file="TestFileDB", + jobOpt_file=DIR / "../../options/jobOptions-Example.yaml", + simple_job=True) + assert "HLTControlFlowMgr/HLTControlFlowMgr" in config + + +def test_set_hltAnnSvc(): + """ + Check if DaVinci is able to set correctly the HltAnnSvc given a specific .tck.json file. + """ + config = run_davinci_app( + fileDB_key="Upgrade_Bd2KstarMuMu", + fileDB_file="TestFileDB", + ctx_args=[ + "--annsvc_config", + "root://eoslhcb.cern.ch//eos/lhcb/wg/dpa/wp3/tests/spruce_all_lines_realtime.tck.json" + ]) + assert "HltANNSvc/HltANNSvc" in config + + +def test_lumi_counters_in_data(): + """ + Check that Lumi Counters are correctly added in the control flow when running on real data. + """ + config = run_davinci_app( + fileDB_key="Upgrade_Bd2KstarMuMu", + fileDB_file="TestFileDB", + ctx_args=[ + "--simulation", "False", "--write_fsr", "True", "--lumi", "True" + ], + override_data_options=True) + nodes = config["HLTControlFlowMgr/HLTControlFlowMgr"].CompositeCFNodes + # Node of the HLTControlFlowMgr are tuple containing : (<node_name>, <logic>, list_of_algs, <force_order>) + # E.g. : ('DaVinci', 'LAZY_AND', ['FileSummaryRecords', 'UserAnalysis'], True), + assert any("EventAccounting/EventAccount" in algs for algs in nodes[2]) + + +def test_prepare_davinci_nodes(): + """ + Check that DaVinci raises an error when trying to prepare a node with a set of algorithms different from a list. + """ + from PyConf.Algorithms import Gaudi__Examples__VoidConsumer as VoidConsumer + list_of_algs = {"alg": VoidConsumer()} + with pytest.raises(ValueError): + prepare_davinci_nodes(list_of_algs) diff --git a/Phys/DaVinci/tests/config/test_optionChecker.py b/Phys/DaVinci/tests/config/test_optionChecker.py new file mode 100644 index 0000000000000000000000000000000000000000..2394bdba0680cb18e14c12c2e9b78185f21d30ae --- /dev/null +++ b/Phys/DaVinci/tests/config/test_optionChecker.py @@ -0,0 +1,41 @@ +############################################################################### +# (c) Copyright 2022 CERN for the benefit of the LHCb Collaboration # +# # +# This software is distributed under the terms of the GNU General Public # +# Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". # +# # +# In applying this licence, CERN does not waive the privileges and immunities # +# granted to it by virtue of its status as an Intergovernmental Organization # +# or submit itself to any jurisdiction. # +############################################################################### +import pytest +from DaVinci.optionChecker import option_checker, DVOptionError, DVKeyError + + +def test_get_allowed_values_for_known_option(): + """ + Check if DaVinci raises an error if the data_type is not allowed. + """ + opt_name = "data_type" + opt_value = "2018" + with pytest.raises(DVOptionError): + option_checker(opt_name, opt_value) + + +def test_get_allowed_values_for_unknown_option(): + """ + Check that DaVinci raise an error if checking allowed values on an unknown option. + """ + opt_name = "data" + opt_value = "2018" + with pytest.raises(DVKeyError): + option_checker(opt_name, opt_value) + + +def test_get_allowed_values_for_unrestricted_option(): + """ + Check that DaVinci does't raise an error if not allowed values are expected for a given option. + """ + opt_name = "evt_max" + opt_value = "200" + option_checker(opt_name, opt_value)