Coverage for mlos_bench/mlos_bench/tests/launcher_run_test.py: 97%
35 statements
« prev ^ index » next coverage.py v7.6.9, created at 2024-12-20 00:44 +0000
« prev ^ index » next coverage.py v7.6.9, created at 2024-12-20 00:44 +0000
1#
2# Copyright (c) Microsoft Corporation.
3# Licensed under the MIT License.
4#
5"""Unit tests to check the main CLI launcher."""
6import os
7import re
8from typing import List
10import pytest
12from mlos_bench.services.config_persistence import ConfigPersistenceService
13from mlos_bench.services.local.local_exec import LocalExecService
14from mlos_bench.util import path_join
16# pylint: disable=redefined-outer-name
19@pytest.fixture
20def root_path() -> str:
21 """Root path of mlos_bench project."""
22 return path_join(os.path.dirname(__file__), "../../..", abs_path=True)
25@pytest.fixture
26def local_exec_service() -> LocalExecService:
27 """Test fixture for LocalExecService."""
28 return LocalExecService(
29 parent=ConfigPersistenceService(
30 {
31 "config_path": [
32 "mlos_bench/config",
33 "mlos_bench/examples",
34 ]
35 }
36 )
37 )
40def _launch_main_app(
41 root_path: str,
42 local_exec_service: LocalExecService,
43 cli_config: str,
44 re_expected: List[str],
45) -> None:
46 """Run mlos_bench command-line application with given config and check the results
47 in the log.
48 """
49 with local_exec_service.temp_dir_context() as temp_dir:
51 # Test developers note: for local debugging,
52 # uncomment the following line to use a known file path that can be examined:
53 # temp_dir = '/tmp'
54 log_path = path_join(temp_dir, "mock-test.log")
55 (return_code, _stdout, _stderr) = local_exec_service.local_exec(
56 [
57 "./mlos_bench/mlos_bench/run.py"
58 + " --config_path ./mlos_bench/mlos_bench/tests/config/"
59 + f" {cli_config} --log_file '{log_path}'"
60 ],
61 cwd=root_path,
62 )
63 assert return_code == 0
65 try:
66 iter_expected = iter(re_expected)
67 re_log = re.compile(next(iter_expected))
68 with open(log_path, "rt", encoding="utf-8") as fh_out:
69 for line in fh_out:
70 if re_log.match(line):
71 re_log = re.compile(next(iter_expected))
72 assert False, f"Pattern not found: '{re_log.pattern}'"
73 except StopIteration:
74 pass # Success: all patterns found
77_RE_DATE = r"\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2},\d{3}"
80def test_launch_main_app_bench(root_path: str, local_exec_service: LocalExecService) -> None:
81 """Run mlos_bench command-line application with mock benchmark config and default
82 tunable values and check the results in the log.
83 """
84 _launch_main_app(
85 root_path,
86 local_exec_service,
87 " --config cli/mock-bench.jsonc"
88 + " --trial_config_repeat_count 5"
89 + " --mock_env_seed -1", # Deterministic Mock Environment.
90 [
91 f"^{_RE_DATE} run\\.py:\\d+ " + r"_main INFO Final score: \{'score': 67\.40\d+\}\s*$",
92 ],
93 )
96def test_launch_main_app_bench_values(
97 root_path: str,
98 local_exec_service: LocalExecService,
99) -> None:
100 """Run mlos_bench command-line application with mock benchmark config and user-
101 specified tunable values and check the results in the log.
102 """
103 _launch_main_app(
104 root_path,
105 local_exec_service,
106 " --config cli/mock-bench.jsonc"
107 + " --tunable_values tunable-values/tunable-values-example.jsonc"
108 + " --trial_config_repeat_count 5"
109 + " --mock_env_seed -1", # Deterministic Mock Environment.
110 [
111 f"^{_RE_DATE} run\\.py:\\d+ " + r"_main INFO Final score: \{'score': 67\.11\d+\}\s*$",
112 ],
113 )
116def test_launch_main_app_opt(root_path: str, local_exec_service: LocalExecService) -> None:
117 """Run mlos_bench command-line application with mock optimization config and check
118 the results in the log.
119 """
120 _launch_main_app(
121 root_path,
122 local_exec_service,
123 "--config cli/mock-opt.jsonc"
124 + " --trial_config_repeat_count 3"
125 + " --max_suggestions 3"
126 + " --mock_env_seed 42", # Noisy Mock Environment.
127 [
128 # Iteration 1: Expect first value to be the baseline
129 f"^{_RE_DATE} mlos_core_optimizer\\.py:\\d+ "
130 + r"bulk_register DEBUG Warm-up END: .* :: \{'score': 64\.53\d+\}$",
131 # Iteration 2: The result may not always be deterministic
132 f"^{_RE_DATE} mlos_core_optimizer\\.py:\\d+ "
133 + r"bulk_register DEBUG Warm-up END: .* :: \{'score': \d+\.\d+\}$",
134 # Iteration 3: non-deterministic (depends on the optimizer)
135 f"^{_RE_DATE} mlos_core_optimizer\\.py:\\d+ "
136 + r"bulk_register DEBUG Warm-up END: .* :: \{'score': \d+\.\d+\}$",
137 # Final result: baseline is the optimum for the mock environment
138 f"^{_RE_DATE} run\\.py:\\d+ " + r"_main INFO Final score: \{'score': 64\.53\d+\}\s*$",
139 ],
140 )