import tempfile from unittest.mock import call, patch from unittest import mock import yaml import pydcop.commands.batch as batch_module from pydcop.commands.batch import ( parameters_configuration, regularize_parameters, run_batches, build_option_for_parameters, expand_variables, build_final_command, input_files_glob, input_files_re, ) def test_input_files_glob(tmpdir): dir_path = str(tmpdir.realpath()) tmpdir.join("dcop_ising_1.yaml").write("") tmpdir.join("dcop_ising_2.yaml").write("") tmpdir.join("dcop_coloring_3.yaml").write("") files = input_files_glob(f"{dir_path}/dcop_ising*.yaml") assert f"{dir_path}/dcop_ising_1.yaml" in files assert f"{dir_path}/dcop_ising_2.yaml" in files assert f"{dir_path}/dcop_coloring_3.yaml" not in files def test_input_files_re(tmpdir): dir_path = str(tmpdir.realpath()) tmpdir.join("dcop_ising_1.yaml").write("") tmpdir.join("dcop_ising_1_dist.yaml").write("") tmpdir.join("dcop_ising_2.yaml").write("") tmpdir.join("dcop_coloring_3.yaml").write("") tmpdir.join("dcop_coloring_3_dist.yaml").write("") files, extras, contexts = input_files_re( dir_path, "dcop_(?P<index>.*).yaml", ["dcop_{index}_dist.yaml"] ) assert "dcop_ising_1.yaml" in files assert "dcop_ising_2.yaml" not in files assert "dcop_coloring_3.yaml" in files index = files.index("dcop_ising_1.yaml") assert "dcop_ising_1_dist.yaml" in extras[index] for context in contexts: assert "index" in context def test_params_configuration_one_parameter(): confs = parameters_configuration({"param1": ["v1_1", "v_1"]}) assert len(confs) == 2 # two combinations for conf in confs: # One single param in each configuration assert len(conf) == 1 assert "param1" in conf assert conf["param1"] in ["v1_1", "v_1"] def test_params_configuration_two_parameters(): p1_values = ["v1_1", "v1_2"] p2_values = ["v2_1"] confs = parameters_configuration({"p1": p1_values, "p2": p2_values}) assert len(confs) == 2 # two combinations for conf in confs: # all parameters must be defined in each configuration assert len(conf) == 2 assert "p1" in conf assert conf["p1"] in p1_values assert "p2" in conf assert conf["p2"] in p2_values def test_params_configuration_two_parameters_2_3(): p1_values = ["v1_1", "v1_2"] p2_values = ["v2_1", "v2_1", "v2_2"] confs = parameters_configuration({"p1": p1_values, "p2": p2_values}) assert len(confs) == 6 for conf in confs: # all parameters must be defined in each configuration assert len(conf) == 2 assert "p1" in conf assert conf["p1"] in p1_values assert "p2" in conf assert conf["p2"] in p2_values def test_params_configuration_two_parameters_with_dict(): p1_values = ["v1_1", "v1_2"] p2_values = {"p21": ["a", "b"], "p22": ["c", "d"]} confs = parameters_configuration({"p1": p1_values, "p2": p2_values}) assert len(confs) == 8 for conf in confs: # all parameters must be defined in each configuration assert len(conf) == 2 assert "p1" in conf assert conf["p1"] in p1_values assert "p2" in conf assert isinstance(conf["p2"], dict) assert conf["p2"]["p21"] in ["a", "b"] assert conf["p2"]["p22"] in ["c", "d"] def test_params_configuration_list_with_single_element(): confs = parameters_configuration({"p1": ["1"], "p2": ["2"]}) assert len(confs) == 1 assert confs[0]["p1"] == "1" assert confs[0]["p2"] == "2" def test_params_configuration_order(): conf1 = parameters_configuration({"b": ["1", "2"], "a": ["3", "4"]}) conf2 = parameters_configuration({"b": ["2", "1"], "a": ["3", "4"]}) conf3 = parameters_configuration({"a": ["3", "4"], "b": ["1", "2"]}) conf4 = parameters_configuration({"a": ["4", "3"], "b": ["1", "2"]}) assert conf1 == conf2 assert conf1 == conf3 assert conf1 == conf4 def test_params_configuration_order_with_dict(): conf1 = parameters_configuration({"b": {"A": ["1", "2"]}, "a": ["3", "4"]}) conf2 = parameters_configuration({"a": ["3", "4"], "b": {"A": ["1", "2"]}}) conf3 = parameters_configuration({"a": ["4", "3"], "b": {"A": ["2", "1"]}}) assert conf1 == conf2 assert conf1 == conf3 def test_regularize_parameters(): params_yaml = """ params: stop_cycle: 100 variant: [A, B, C] probability: [0.1, 0.2, 0.3, 0.4, 0.5, 0.6] """ params = yaml.load(params_yaml, Loader=yaml.FullLoader) reg_params = regularize_parameters(params["params"]) assert len(reg_params) == 3 assert len(reg_params["stop_cycle"]) == 1 for v in reg_params["stop_cycle"]: assert isinstance(v, str) assert len(reg_params["variant"]) == 3 for v in reg_params["variant"]: assert isinstance(v, str) assert len(reg_params["probability"]) == 6 for v in reg_params["probability"]: assert isinstance(v, str) def test_regularize_parameters_imbricated(): params_yaml = """ command_options: algo: [dsa, mgm] algo_params: stop_cycle: [50, 100] probability: [0.5, 0.6] """ params = yaml.load(params_yaml, Loader=yaml.FullLoader) reg_params = regularize_parameters(params["command_options"]) assert len(reg_params) == 2 assert len(reg_params["algo"]) == 2 for v in reg_params["algo"]: assert isinstance(v, str) assert len(reg_params["algo_params"]) == 2 assert len(reg_params["algo_params"]["stop_cycle"]) == 2 for v in reg_params["algo_params"]["stop_cycle"]: assert isinstance(v, str) assert len(reg_params["algo_params"]["probability"]) == 2 for v in reg_params["algo_params"]["probability"]: assert isinstance(v, str) def test_build_option_for_parameters(): params = {"p1": "v1", "p2": "v2"} option_str = build_option_for_parameters(params) assert option_str == "--p1 v1 --p2 v2" def test_build_option_for_parameters_with_dict_as_value(): params = {"p1": "v1", "p2": {"p21": "v21", "p22": "v22"}} option_str = build_option_for_parameters(params) assert option_str == "--p1 v1 --p2 p21:v21 --p2 p22:v22" def test_build_option_for_algo_parameters(): params = {"p1": "v1", "p2": "v2"} option_str = build_option_for_parameters({"algo_params": params}) assert option_str == "--algo_params p1:v1 --algo_params p2:v2" def test_expand_variables_in_str(): assert "abc" == expand_variables("a{v}c", {"v": "b"}) assert "foo" == expand_variables("{bar}", {"bar": "foo"}) def test_expand_variables_in_str_with_subdir(): context = {"d": {"a": "_insubdict_"}} obtained = expand_variables("a{d[a]}c", context) assert "a_insubdict_c" == obtained def test_expand_variables_in_list(): to_expand = ["abc", "expanded_{foo}", "{foo2}_expanded"] obtained = expand_variables(to_expand, {"foo": "bar", "foo2": "bar2"}) assert obtained[0] == "abc" assert obtained[1] == "expanded_bar" assert obtained[2] == "bar2_expanded" def test_expand_variables_in_list_with_subdict(): to_expand = ["abc", "expanded_{foo}", "{foo2[a]}_expanded"] obtained = expand_variables(to_expand, {"foo": "bar", "foo2": {"a": "subdir"}}) assert obtained[0] == "abc" assert obtained[1] == "expanded_bar" assert obtained[2] == "subdir_expanded" def test_expand_variables_in_dict(): to_expand = {"a": "abc", "b": "expanded_{foo}", "c": "{foo2}_expanded"} obtained = expand_variables(to_expand, {"foo": "bar", "foo2": "bar2"}) assert obtained["a"] == "abc" assert obtained["b"] == "expanded_bar" assert obtained["c"] == "bar2_expanded" def test_build_final_command_file_only(): cmd, _ = build_final_command("cmd", {}, {}, {}, files=["file"]) assert cmd == "pydcop cmd file" def test_build_final_command_with_global_options(): cmd, _ = build_final_command( "cmd", {}, {"logs": "log_file.conf", "timeout": "10"}, {}, files=["file"] ) assert cmd == "pydcop --logs log_file.conf --timeout 10 cmd file" def test_build_final_command_with_command_options(): cmd, _ = build_final_command( "solve", {}, {}, {"algo": "dsa", "algo_params": {"variant": "A"}}, files=["file"], ) assert cmd == "pydcop solve --algo dsa --algo_params variant:A file" def test_build_final_command_with_command_options_and_expansion(): cmd, _ = build_final_command( "solve", {}, {}, {"algo": "dsa", "algo_params": {"variant": "A"}}, files=["file_{algo_params[variant]}.yaml"], ) assert cmd == "pydcop solve --algo dsa --algo_params variant:A file_A.yaml" @patch("pydcop.commands.batch.run_batch") def test_run_batches_iteration_only(mock_run_batch): definition = """ sets: set1: iterations: 5 batches: batch1: command: test """ conf = yaml.load(definition, Loader=yaml.FullLoader) run_batches(conf, simulate=False) assert mock_run_batch.call_count == 5 @patch("pydcop.commands.batch.run_batch") def test_run_batches_direct_and_iteration(mock_run_batch): with tempfile.TemporaryDirectory() as tmpdirname: definition = f""" sets: set1: path: {tmpdirname} iterations: 3 batches: batch1: command: test """ conf = yaml.load(definition, Loader=yaml.FullLoader) run_batches(conf, simulate=False) assert mock_run_batch.call_count == 3 @patch("pydcop.commands.batch.run_cli_command") def test_generate_ising_5_iteration(run_mock): definition = """ sets: set1: iterations: 5 batches: batch1: command: generate ising command_options: row_count: 3 col_count: 3 global_options: output: ising_{iteration}.yaml """ batches_def = yaml.load(definition, Loader=yaml.FullLoader) batch_module.run_batches(batches_def, simulate=False) assert run_mock.call_count == 5 print(run_mock.calls) run_mock.assert_has_calls( [ call( "pydcop --output ising_0.yaml generate ising --col_count 3 --row_count 3", "", None, ), call( "pydcop --output ising_1.yaml generate ising --col_count 3 --row_count 3", "", None, ), call( "pydcop --output ising_2.yaml generate ising --col_count 3 --row_count 3", "", None, ), call( "pydcop --output ising_3.yaml generate ising --col_count 3 --row_count 3", "", None, ), call( "pydcop --output ising_4.yaml generate ising --col_count 3 --row_count 3", "", None, ), ], any_order=True, ) @patch("pydcop.commands.batch.run_cli_command") def test_generate_ising_variable_row(run_mock): definition = """ sets: set1: iterations: 1 batches: batch1: command: generate ising command_options: row_count: [3, 4] col_count: 3 global_options: output: ising_{iteration}_{row_count}_{col_count}.yaml """ batches_def = yaml.load(definition, Loader=yaml.FullLoader) batch_module.run_batches(batches_def, simulate=False) assert run_mock.call_count == 2 run_mock.assert_has_calls( [ call( "pydcop --output ising_0_3_3.yaml generate ising --col_count 3 --row_count 3", "", None, ), call( "pydcop --output ising_0_4_3.yaml generate ising --col_count 3 --row_count 4", "", None, ), ], any_order=True, ) @patch("pydcop.commands.batch.run_cli_command") def test_generate_ising_variable_row_with_dir(run_mock): definition = """ sets: set1: iterations: 1 batches: batch1: command: generate ising current_dir: ~/tmp/ising/row{row_count}_col{col_count}/ command_options: row_count: [3, 4] col_count: 3 global_options: output: ising.yaml """ batches_def = yaml.load(definition, Loader=yaml.FullLoader) batch_module.run_batches(batches_def, simulate=False) assert run_mock.call_count == 2 run_mock.assert_has_calls( [ call( "pydcop --output ising.yaml generate ising --col_count 3 --row_count 3", "~/tmp/ising/row3_col3/", None, ), call( "pydcop --output ising.yaml generate ising --col_count 3 --row_count 4", "~/tmp/ising/row4_col3/", None, ), ], any_order=True, ) @mock.patch("pydcop.commands.batch.run_cli_command") def test_solve_variable_row_with_dir(run_mock, tmpdir): dir_path = str(tmpdir.realpath()) definition = ( """ sets: set1: path: """ + dir_path + """/*.yaml iterations: 1 batches: batch1: command: solve current_dir: ~/tmp/ising/{algo}/ command_options: algo: dsa """ ) tmpdir.join("pb1.yaml").write("") tmpdir.join("pb2.yaml").write("") batches_def = yaml.load(definition, Loader=yaml.FullLoader) batch_module.run_batches(batches_def, simulate=False) run_mock.assert_has_calls( [ call( "pydcop solve --algo dsa " + dir_path + "/pb1.yaml", "~/tmp/ising/dsa/", None, ), call( "pydcop solve --algo dsa " + dir_path + "/pb2.yaml", "~/tmp/ising/dsa/", None, ), ], any_order=True, )