From 569e5fea12f3f3b3a2327031a153e6ff7f84a307 Mon Sep 17 00:00:00 2001 From: Edwin Lee Date: Mon, 14 Apr 2025 11:25:38 -0500 Subject: [PATCH 01/10] Add in switch for preferred build type debug/release --- energyplus_regressions/builds/base.py | 8 ++-- energyplus_regressions/builds/install.py | 3 +- energyplus_regressions/builds/makefile.py | 3 +- energyplus_regressions/builds/visualstudio.py | 34 ++++++++----- energyplus_regressions/energyplus.py | 48 +++++++++---------- energyplus_regressions/runtests.py | 13 ++--- energyplus_regressions/structures.py | 5 ++ .../tests/builds/test_visualstudio.py | 30 ++++++++++++ energyplus_regressions/tk_window.py | 47 ++++++++++++++---- requirements.txt | 1 + 10 files changed, 135 insertions(+), 57 deletions(-) diff --git a/energyplus_regressions/builds/base.py b/energyplus_regressions/builds/base.py index 7117a5d..18c227a 100644 --- a/energyplus_regressions/builds/base.py +++ b/energyplus_regressions/builds/base.py @@ -1,6 +1,8 @@ from pathlib import Path from typing import Optional, Set +from energyplus_regressions.structures import ConfigType + class KnownBuildTypes: Makefile = "makefile" @@ -87,10 +89,10 @@ def should_keep(file_path: Path): filtered_list = filter(should_keep, all_idfs_relative_path) return set(filtered_list) - def set_build_directory(self, build_directory: Path): + def set_build_directory(self, build_directory: Path) -> None: raise NotImplementedError('Must implement set_build_directory(str) in derived classes') - def verify(self): + def verify(self) -> list[tuple[str, Path, bool]]: results: list[tuple[str, Path, bool]] = [] if not self.build_directory: raise Exception('Build directory has not been set with set_build_directory()') @@ -140,5 +142,5 @@ def verify(self): def get_build_tree(self) -> BuildTree: raise NotImplementedError('Must implement get_build_tree() in derived classes') - def get_idf_directory(self): + def get_idf_directory(self) -> Path: raise NotImplementedError() diff --git a/energyplus_regressions/builds/install.py b/energyplus_regressions/builds/install.py index 225a1d8..dc66c6c 100644 --- a/energyplus_regressions/builds/install.py +++ b/energyplus_regressions/builds/install.py @@ -2,6 +2,7 @@ from energyplus_regressions.builds.base import BaseBuildDirectoryStructure, BuildTree from energyplus_regressions.ep_platform import exe_extension +from energyplus_regressions.structures import ConfigType class EPlusInstallDirectory(BaseBuildDirectoryStructure): @@ -21,7 +22,7 @@ def set_build_directory(self, build_directory: Path): # For an E+ install, the source directory is kinda just the root repo self.source_directory = build_directory - def get_idf_directory(self): + def get_idf_directory(self) -> Path: if not self.build_directory: raise Exception('Build directory has not been set with set_build_directory()') return self.source_directory / 'ExampleFiles' diff --git a/energyplus_regressions/builds/makefile.py b/energyplus_regressions/builds/makefile.py index b914a2f..6b043f3 100644 --- a/energyplus_regressions/builds/makefile.py +++ b/energyplus_regressions/builds/makefile.py @@ -2,6 +2,7 @@ from energyplus_regressions.builds.base import BaseBuildDirectoryStructure, BuildTree from energyplus_regressions.ep_platform import exe_extension +from energyplus_regressions.structures import ConfigType class CMakeCacheMakeFileBuildDirectory(BaseBuildDirectoryStructure): @@ -30,7 +31,7 @@ def set_build_directory(self, build_directory: Path): else: raise Exception('Could not find source directory spec in the CMakeCache file') - def get_idf_directory(self): + def get_idf_directory(self) -> Path: if not self.build_directory: raise Exception('Build directory has not been set with set_build_directory()') return self.source_directory / 'testfiles' diff --git a/energyplus_regressions/builds/visualstudio.py b/energyplus_regressions/builds/visualstudio.py index fceeeb6..607879e 100644 --- a/energyplus_regressions/builds/visualstudio.py +++ b/energyplus_regressions/builds/visualstudio.py @@ -1,6 +1,8 @@ from pathlib import Path +from typing import Callable from energyplus_regressions.builds.base import BaseBuildDirectoryStructure, BuildTree +from energyplus_regressions.structures import ConfigType class CMakeCacheVisualStudioBuildDirectory(BaseBuildDirectoryStructure): @@ -11,12 +13,27 @@ class CMakeCacheVisualStudioBuildDirectory(BaseBuildDirectoryStructure): def __init__(self): super(CMakeCacheVisualStudioBuildDirectory, self).__init__() - self.build_mode: str = 'Release' + self.build_mode: str = ConfigType.RELEASE.value - def set_build_mode(self, debug): - self.build_mode = 'Debug' if debug else 'Release' + def set_build_mode(self, config: ConfigType, error_callback: Callable[[str], None] | None = None) -> None: + build_mode_folder = config.value + desired_build_directory = self.build_directory / 'Products' / build_mode_folder + if desired_build_directory.exists(): + self.build_mode = config.value + else: + if error_callback: + error_callback( + f"Attempted to set build mode as {config.value} but did not detect build dir, use caution! :)" + ) + build_mode_folder = 'Release' + release_folder = self.build_directory / 'Products' / build_mode_folder + release_folder_exists = release_folder.exists() + if release_folder_exists: + self.build_mode = ConfigType.RELEASE.value + else: # Finally, if we can't find release either, just set it to debug and let the user deal with it + self.build_mode = ConfigType.DEBUG.value - def set_build_directory(self, build_directory: Path): + def set_build_directory(self, build_directory: Path) -> None: """ This method takes a build directory, and updates any dependent member variables, in this case the source dir. This method *does* allow an invalid build_directory, as could happen during program initialization @@ -37,15 +54,8 @@ def set_build_directory(self, build_directory: Path): break else: raise Exception('Could not find source directory spec in the CMakeCache file') - build_mode_folder = 'Release' - release_folder = self.build_directory / 'Products' / build_mode_folder - release_folder_exists = release_folder.exists() - if release_folder_exists: - self.set_build_mode(debug=False) - else: - self.set_build_mode(debug=True) - def get_idf_directory(self): + def get_idf_directory(self) -> Path: if not self.build_directory: raise Exception('Build directory has not been set with set_build_directory()') return self.source_directory / 'testfiles' diff --git a/energyplus_regressions/energyplus.py b/energyplus_regressions/energyplus.py index f687606..bdaefbc 100755 --- a/energyplus_regressions/energyplus.py +++ b/energyplus_regressions/energyplus.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python import glob from os import chdir, getcwd, rename, environ from pathlib import Path @@ -8,8 +7,6 @@ from energyplus_regressions.builds.base import BuildTree from energyplus_regressions.structures import ForceRunType -script_dir = Path(__file__).resolve().parent - class ExecutionArguments: def __init__(self, build_tree: BuildTree, entry_name: str, test_run_directory: Path, @@ -24,17 +21,17 @@ def __init__(self, build_tree: BuildTree, entry_name: str, test_run_directory: P # noinspection PyBroadException -def execute_energyplus(e_args: ExecutionArguments): +def execute_energyplus(e_args: ExecutionArguments) -> tuple[Path, str, bool, bool, str]: # set up a few paths energyplus = e_args.build_tree.energyplus basement = e_args.build_tree.basement idd_path = e_args.build_tree.idd_path slab = e_args.build_tree.slab - basementidd = e_args.build_tree.basementidd - slabidd = e_args.build_tree.slabidd - expandobjects = e_args.build_tree.expandobjects - epmacro = e_args.build_tree.epmacro - readvars = e_args.build_tree.readvars + basement_idd = e_args.build_tree.basementidd + slab_idd = e_args.build_tree.slabidd + expand_objects = e_args.build_tree.expandobjects + ep_macro = e_args.build_tree.epmacro + read_vars = e_args.build_tree.readvars parametric = e_args.build_tree.parametric # Save the current path so we can go back here @@ -61,7 +58,7 @@ def execute_energyplus(e_args: ExecutionArguments): imf_path = e_args.test_run_directory / 'in.imf' ght_file = e_args.test_run_directory / 'GHTIn.idf' basement_file = e_args.test_run_directory / 'BasementGHTIn.idf' - epjson_file = e_args.test_run_directory / 'in.epJSON' + ep_json_file = e_args.test_run_directory / 'in.epJSON' rvi_file = e_args.test_run_directory / 'in.rvi' mvi_file = e_args.test_run_directory / 'in.mvi' @@ -79,7 +76,7 @@ def execute_energyplus(e_args: ExecutionArguments): for line in newlines: f.write(line) macro_run = subprocess.Popen( - str(epmacro), shell=True, stdin=subprocess.DEVNULL, stdout=subprocess.PIPE, stderr=subprocess.PIPE + str(ep_macro), shell=True, stdin=subprocess.DEVNULL, stdout=subprocess.PIPE, stderr=subprocess.PIPE ) o, e = macro_run.communicate() std_out += o @@ -102,12 +99,12 @@ def execute_energyplus(e_args: ExecutionArguments): idf_file.unlink() rename(file_to_run_here, idf_file) else: - return [e_args.build_tree.build_dir, e_args.entry_name, False, False, "Issue with Parametrics"] + return e_args.build_tree.build_dir, e_args.entry_name, False, False, "Issue with Parametric" # Run ExpandObjects and process as necessary, but not for epJSON files! if idf_file.exists(): expand_objects_run = subprocess.Popen( - str(expandobjects), shell=True, stdin=subprocess.DEVNULL, stdout=subprocess.PIPE, stderr=subprocess.PIPE + str(expand_objects), shell=True, stdin=subprocess.DEVNULL, stdout=subprocess.PIPE, stderr=subprocess.PIPE ) o, e = expand_objects_run.communicate() std_out += o @@ -118,7 +115,7 @@ def execute_energyplus(e_args: ExecutionArguments): rename(expanded_file, idf_file) if basement_file.exists(): - shutil.copy(basementidd, e_args.test_run_directory) + shutil.copy(basement_idd, e_args.test_run_directory) basement_environment = environ.copy() basement_environment['CI_BASEMENT_NUMYEARS'] = '2' basement_run = subprocess.Popen( @@ -140,7 +137,7 @@ def execute_energyplus(e_args: ExecutionArguments): (e_args.test_run_directory / 'BasementGHT.idd').unlink() if ght_file.exists(): - shutil.copy(slabidd, e_args.test_run_directory) + shutil.copy(slab_idd, e_args.test_run_directory) slab_run = subprocess.Popen( str(slab), shell=True, stdin=subprocess.DEVNULL, stdout=subprocess.PIPE, stderr=subprocess.PIPE ) @@ -184,7 +181,7 @@ def execute_energyplus(e_args: ExecutionArguments): # Execute EnergyPlus try: command_line = str(energyplus) - if epjson_file.exists(): + if ep_json_file.exists(): command_line += ' in.epJSON' std_out += subprocess.check_output( command_line, shell=True, stdin=subprocess.DEVNULL, stderr=subprocess.PIPE @@ -193,23 +190,23 @@ def execute_energyplus(e_args: ExecutionArguments): ... # so I can verify that I hit this during the test_case_b_crash test, but if I just have the return in # here alone, it shows as missing on the coverage...wonky - return [e_args.build_tree.build_dir, e_args.entry_name, False, False, str(e)] + return e_args.build_tree.build_dir, e_args.entry_name, False, False, str(e) - # Execute readvars + # Execute read-vars if rvi_file.exists(): csv_run = subprocess.Popen( - str(readvars) + ' in.rvi', shell=True, stdin=subprocess.DEVNULL, + str(read_vars) + ' in.rvi', shell=True, stdin=subprocess.DEVNULL, stdout=subprocess.PIPE, stderr=subprocess.PIPE ) else: csv_run = subprocess.Popen( - str(readvars), shell=True, stdin=subprocess.DEVNULL, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + str(read_vars), shell=True, stdin=subprocess.DEVNULL, stdout=subprocess.PIPE, stderr=subprocess.PIPE) o, e = csv_run.communicate() std_out += o std_err += e if mvi_file.exists(): mtr_run = subprocess.Popen( - str(readvars) + ' in.mvi', shell=True, stdin=subprocess.DEVNULL, + str(read_vars) + ' in.mvi', shell=True, stdin=subprocess.DEVNULL, stdout=subprocess.PIPE, stderr=subprocess.PIPE ) else: @@ -217,7 +214,7 @@ def execute_energyplus(e_args: ExecutionArguments): f.write("eplusout.mtr\n") f.write("eplusmtr.csv\n") mtr_run = subprocess.Popen( - str(readvars) + ' in.mvi', shell=True, stdin=subprocess.DEVNULL, + str(read_vars) + ' in.mvi', shell=True, stdin=subprocess.DEVNULL, stdout=subprocess.PIPE, stderr=subprocess.PIPE ) o, e = mtr_run.communicate() @@ -232,11 +229,10 @@ def execute_energyplus(e_args: ExecutionArguments): f.write(std_err.decode('utf-8')) new_idd_path.unlink() - return [e_args.build_tree.build_dir, e_args.entry_name, True, False] + chdir(start_path) + return e_args.build_tree.build_dir, e_args.entry_name, True, False, "" except Exception as e: print("**" + str(e)) - return [e_args.build_tree.build_dir, e_args.entry_name, False, False, str(e)] - - finally: chdir(start_path) + return e_args.build_tree.build_dir, e_args.entry_name, False, False, str(e) diff --git a/energyplus_regressions/runtests.py b/energyplus_regressions/runtests.py index 4e6789d..35eaac9 100755 --- a/energyplus_regressions/runtests.py +++ b/energyplus_regressions/runtests.py @@ -17,7 +17,7 @@ from difflib import unified_diff # python's own diff library -from energyplus_regressions.builds.base import BuildTree +from energyplus_regressions.builds.base import BuildTree, BaseBuildDirectoryStructure from energyplus_regressions.diffs import math_diff, table_diff, thresh_dict as td from energyplus_regressions.energyplus import ExecutionArguments, execute_energyplus from energyplus_regressions.structures import ( @@ -41,8 +41,9 @@ class TestRunConfiguration: __test__ = False # so that PyTest doesn't try to run this as a class fixture - def __init__(self, force_run_type, num_threads, report_freq, build_a, build_b, single_test_run=False, - force_output_sql: ForceOutputSQL = ForceOutputSQL.NOFORCE, + def __init__(self, force_run_type: str, num_threads: int, report_freq: str, + build_a: BaseBuildDirectoryStructure, build_b: BaseBuildDirectoryStructure, + single_test_run: bool=False, force_output_sql: ForceOutputSQL = ForceOutputSQL.NOFORCE, force_output_sql_unitconv: ForceOutputSQLUnitConversion = ForceOutputSQLUnitConversion.NOFORCE): self.force_run_type = force_run_type self.TestOneFile = single_test_run @@ -57,7 +58,7 @@ def __init__(self, force_run_type, num_threads, report_freq, build_a, build_b, s class TestCaseCompleted: __test__ = False # so that PyTest doesn't try to run this as a class fixture - def __init__(self, run_directory, case_name, run_status, error_msg_reported_already, extra_message=""): + def __init__(self, run_directory: str, case_name: str, run_status, error_msg_reported_already, extra_message=""): self.run_directory = run_directory self.case_name = case_name self.run_success = run_status @@ -68,7 +69,7 @@ def __init__(self, run_directory, case_name, run_status, error_msg_reported_alre # the actual main test suite run class class SuiteRunner: - def __init__(self, run_config, these_entries, mute=False): + def __init__(self, run_config: TestRunConfiguration, these_entries, mute=False): # initialize the master mute button -- this is overridden by registering callbacks self.mute = mute @@ -446,7 +447,7 @@ def run_build(self, build_tree: BuildTree): local_run_type, self.min_reporting_freq, parametric_file, - epw_path + str(epw_path) ) ) diff --git a/energyplus_regressions/structures.py b/energyplus_regressions/structures.py index 554306a..5950090 100755 --- a/energyplus_regressions/structures.py +++ b/energyplus_regressions/structures.py @@ -52,6 +52,11 @@ class ForceOutputSQLUnitConversion(Enum): InchPound = 'InchPound' +class ConfigType(Enum): + RELEASE = "Release" + DEBUG = "Debug" + + class Results: def __init__(self): self.descriptions = {} diff --git a/energyplus_regressions/tests/builds/test_visualstudio.py b/energyplus_regressions/tests/builds/test_visualstudio.py index ccdb3cb..24d4ad9 100644 --- a/energyplus_regressions/tests/builds/test_visualstudio.py +++ b/energyplus_regressions/tests/builds/test_visualstudio.py @@ -5,6 +5,7 @@ from energyplus_regressions.builds.base import BuildTree from energyplus_regressions.builds.visualstudio import CMakeCacheVisualStudioBuildDirectory +from energyplus_regressions.structures import ConfigType class TestVisualStudioBuildMethods(unittest.TestCase): @@ -13,6 +14,7 @@ def setUp(self): self.build = CMakeCacheVisualStudioBuildDirectory() self.run_dir = Path(tempfile.mkdtemp()) self.dummy_source_dir = Path('/dummy/source/dir') + self.output_message: str = "" def set_cache_file(self): with open(os.path.join(self.run_dir, 'CMakeCache.txt'), 'w') as f: @@ -77,3 +79,31 @@ def test_get_idf_dir(self): self.build.set_build_directory(self.run_dir) idf_dir = self.build.get_idf_directory() self.assertEqual(self.dummy_source_dir / 'testfiles', idf_dir) + + def test_set_build_mode_folder_exists(self): + self.set_cache_file() + self.build.set_build_directory(self.run_dir) + os.makedirs(os.path.join(self.run_dir, 'Products', 'Release')) + self.build.set_build_mode(ConfigType.RELEASE) + self.assertEqual(ConfigType.RELEASE.value, self.build.build_mode) + os.makedirs(os.path.join(self.run_dir, 'Products', 'Debug')) + self.build.set_build_mode(ConfigType.DEBUG) + self.assertEqual(ConfigType.DEBUG.value, self.build.build_mode) + + def _message_capture(self, s: str): + self.output_message = s + + def test_set_build_debug_does_not_exist(self): + self.set_cache_file() + self.build.set_build_directory(self.run_dir) + self.build.set_build_mode(ConfigType.DEBUG, self._message_capture) + self.assertIn("caution", self.output_message.lower()) + self.assertEqual(ConfigType.DEBUG.value, self.build.build_mode) # should fall to debug type + + def test_set_build_debug_does_not_exist_release_does(self): + self.set_cache_file() + self.build.set_build_directory(self.run_dir) + os.makedirs(os.path.join(self.run_dir, 'Products', 'Release')) + self.build.set_build_mode(ConfigType.DEBUG, self._message_capture) + self.assertIn("caution", self.output_message.lower()) + self.assertEqual(ConfigType.RELEASE.value, self.build.build_mode) # should fall to release type diff --git a/energyplus_regressions/tk_window.py b/energyplus_regressions/tk_window.py index ddae5d7..f5d5882 100644 --- a/energyplus_regressions/tk_window.py +++ b/energyplus_regressions/tk_window.py @@ -35,6 +35,7 @@ from energyplus_regressions.runtests import TestRunConfiguration, SuiteRunner from energyplus_regressions.structures import ( CompletedStructure, + ConfigType, ForceOutputSQL, ForceOutputSQLUnitConversion, ForceRunType, @@ -185,6 +186,8 @@ def __init__(self): self.force_output_sql.set(ForceOutputSQL.NOFORCE.value) self.force_output_sql_unitconv = StringVar() self.force_output_sql_unitconv.set(ForceOutputSQLUnitConversion.NOFORCE.value) + self.preferred_build_type = StringVar() + self.preferred_build_type.set(ConfigType.RELEASE.value) self.num_threads_var = StringVar() # widgets that we might want to access later @@ -227,6 +230,7 @@ def __init__(self): self.reporting_frequency_option_menu = None self.force_output_sql_option_menu = None self.force_output_sql_unitconv_option_menu = None + self.set_preferred_build_type = None self.idf_select_from_containing_button = None # some data holders @@ -249,6 +253,9 @@ def __init__(self): # noinspection PyTypeChecker self.root.after(self.save_interval, self.auto_save) + # set up any Var traces here after the init is all done + self.preferred_build_type.trace_add("write", self.refresh_builds_for_build_type_change) + # wire up the background thread pub.subscribe(self.print_handler, PubSubMessageTypes.PRINT) pub.subscribe(self.starting_handler, PubSubMessageTypes.STARTING) @@ -259,7 +266,7 @@ def __init__(self): pub.subscribe(self.cancelled_handler, PubSubMessageTypes.CANCELLED) # on Linux, initialize the notification class instance - self.notification = None + self.notification: Notification | None = None if system() == 'Linux': self.notification_icon = Path(self.icon_path) self.notification = Notification('energyplus_regression_runner') @@ -267,7 +274,7 @@ def __init__(self): def init_window(self): # changing the title of our master widget self.root.title("EnergyPlus Regression Tool") - self.root.protocol("WM_DELETE_WINDOW", self.client_exit) + self.root.protocol("WM_DELETE_WINDOW", self.app_exit) # create the menu menu = Menu(self.root) @@ -275,7 +282,7 @@ def init_window(self): file_menu = Menu(menu) file_menu.add_command(label="Open Project...", command=self.client_open) file_menu.add_command(label="Save Project...", command=self.client_save) - file_menu.add_command(label="Exit", command=self.client_exit) + file_menu.add_command(label="Exit", command=self.app_exit) menu.add_cascade(label="File", menu=file_menu) help_menu = Menu(menu) help_menu.add_command(label="Open Documentation...", command=self.open_documentation) @@ -310,29 +317,38 @@ def init_window(self): self.build_dir_2_label.grid(row=1, column=2, sticky=E) group_run_options = LabelFrame(pane_run, text="Run Options") group_run_options.pack(fill=X, padx=5) + # row 1 Label(group_run_options, text="Number of threads for suite: ").grid(row=1, column=1, sticky=E) self.num_threads_spinner = Spinbox(group_run_options, from_=1, to=48, textvariable=self.num_threads_var) self.num_threads_spinner.grid(row=1, column=2, sticky=W) + # row 2 Label(group_run_options, text="Test suite run configuration: ").grid(row=2, column=1, sticky=E) self.run_period_option_menu = OptionMenu(group_run_options, self.run_period_option, *ForceRunType.get_all()) self.run_period_option_menu.grid(row=2, column=2, sticky=W) + # row 3 Label(group_run_options, text="Minimum reporting frequency: ").grid(row=3, column=1, sticky=E) self.reporting_frequency_option_menu = OptionMenu( group_run_options, self.reporting_frequency, *ReportingFreq.get_all() ) self.reporting_frequency_option_menu.grid(row=3, column=2, sticky=W) - + # row 4 Label(group_run_options, text="Force Output SQL: ").grid(row=4, column=1, sticky=E) self.force_output_sql_option_menu = OptionMenu( group_run_options, self.force_output_sql, *[x.value for x in ForceOutputSQL] ) self.force_output_sql_option_menu.grid(row=4, column=2, sticky=W) - + # row 5 Label(group_run_options, text="Force Output SQL UnitConv: ").grid(row=5, column=1, sticky=E) self.force_output_sql_unitconv_option_menu = OptionMenu( group_run_options, self.force_output_sql_unitconv, *[x.value for x in ForceOutputSQLUnitConversion] ) self.force_output_sql_unitconv_option_menu.grid(row=5, column=2, sticky=W) + # row 6 + Label(group_run_options, text="Multi-configuration Build Preference: ").grid(row=6, column=1, sticky=E) + self.set_preferred_build_type = OptionMenu( + group_run_options, self.preferred_build_type, *[x.value for x in ConfigType] + ) + self.set_preferred_build_type.grid(row=6, column=2, sticky=W) self.main_notebook.add(pane_run, text='Configuration') @@ -483,13 +499,18 @@ def client_open(self, auto_open=False): self.reporting_frequency.set(data['report_freq']) self.force_output_sql.set(data['force_output_sql']) self.force_output_sql_unitconv.set(data['force_output_sql_unitconv']) - + if 'preferred_build_type' in data: # it's initialized to RELEASE in the window __init__ + self.preferred_build_type.set(data['preferred_build_type']) status = self.try_to_set_build_1_to_dir(Path(data['build_1_build_dir'])) if status: self.build_dir_1_var.set(data['build_1_build_dir']) + if isinstance(self.build_1, CMakeCacheVisualStudioBuildDirectory): + self.build_1.set_build_mode(ConfigType(self.preferred_build_type.get())) status = self.try_to_set_build_2_to_dir(Path(data['build_2_build_dir'])) if status: self.build_dir_2_var.set(data['build_2_build_dir']) + if isinstance(self.build_2, CMakeCacheVisualStudioBuildDirectory): + self.build_2.set_build_mode(ConfigType(self.preferred_build_type.get())) self.build_idf_listing(False, data['idfs']) self.add_to_log("Project settings loaded") except Exception: @@ -534,6 +555,7 @@ def client_save(self, auto_save=False): 'build_1_build_dir': str(self.build_1.build_directory), 'build_2_build_dir': str(self.build_2.build_directory), 'last_results': these_results, + 'preferred_build_type': self.preferred_build_type.get(), } except Exception as e: # if we hit an exception, our action depends on whether we are manually saving or auto-saving @@ -762,7 +784,7 @@ def build_results_tree(self, results: CompletedStructure = None): ) self.last_results = results - def add_to_log(self, message): + def add_to_log(self, message: str): if self.log_message_listbox: self.log_message_listbox.insert(END, f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}]: {message}") self.log_message_listbox.yview(END) @@ -947,10 +969,16 @@ def set_gui_status_for_run(self, is_running: bool): self.reporting_frequency_option_menu.configure(state=run_button_state) self.force_output_sql_option_menu.configure(state=run_button_state) self.force_output_sql_unitconv_option_menu.configure(state=run_button_state) + self.set_preferred_build_type.configure(state=run_button_state) self.num_threads_spinner.configure(state=run_button_state) self.stop_button.configure(state=stop_button_state) self.main_notebook.tab(3, text=results_tab_title) + def refresh_builds_for_build_type_change(self, *_): + # just try to refresh both build directories, it will warn if the debug/release folders aren't there + self.try_to_set_build_1_to_dir(self.build_1.build_directory) + self.try_to_set_build_2_to_dir(self.build_2.build_directory) + def try_to_set_build_1_to_dir(self, selected_dir: Path) -> bool: probable_build_dir_type = autodetect_build_dir_type(selected_dir) if probable_build_dir_type == KnownBuildTypes.Unknown: @@ -964,6 +992,7 @@ def try_to_set_build_1_to_dir(self, selected_dir: Path) -> bool: self.add_to_log("Build 1 type detected as a Visual Studio build") self.build_1 = CMakeCacheVisualStudioBuildDirectory() self.build_1.set_build_directory(selected_dir) + self.build_1.set_build_mode(ConfigType(self.preferred_build_type.get()), self.add_to_log) elif probable_build_dir_type == KnownBuildTypes.Makefile: self.add_to_log("Build 1 type detected as a Makefile-style build") self.build_1 = CMakeCacheMakeFileBuildDirectory() @@ -999,6 +1028,7 @@ def try_to_set_build_2_to_dir(self, selected_dir: Path) -> bool: self.add_to_log("Build 2 type detected as a Visual Studio build") self.build_2 = CMakeCacheVisualStudioBuildDirectory() self.build_2.set_build_directory(selected_dir) + self.build_2.set_build_mode(ConfigType(self.preferred_build_type.get()), self.add_to_log) elif probable_build_dir_type == KnownBuildTypes.Makefile: self.add_to_log("Build 2 type detected as a Makefile-style build") self.build_2 = CMakeCacheMakeFileBuildDirectory() @@ -1175,10 +1205,11 @@ def client_stop(self): self.label_string.set("Attempting to cancel...") self.background_operator.interrupt_please() - def client_exit(self): + def app_exit(self): if self.long_thread: messagebox.showerror("Uh oh!", "Cannot exit program while operations are running; abort them then exit") return + self.client_save(auto_save=True) sys.exit() def client_done(self): diff --git a/requirements.txt b/requirements.txt index 57e2b54..1ad7e04 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,6 +3,7 @@ pypubsub beautifulsoup4==4.12.3 # for running tests +coverage coveralls flake8 pytest From 0ccbcc5d87243fc710c7b7c59d48b5a0b9471ef5 Mon Sep 17 00:00:00 2001 From: Edwin Lee Date: Mon, 14 Apr 2025 11:39:13 -0500 Subject: [PATCH 02/10] Add case insensitivity to tablediff entries --- energyplus_regressions/diffs/table_diff.py | 8 +- .../eplustbl_has_string_diff_case_only.htm | 100 ++++++++++++++++++ .../tests/diffs/test_table_diff.py | 21 ++++ 3 files changed, 127 insertions(+), 2 deletions(-) create mode 100644 energyplus_regressions/tests/diffs/tbl_resources/eplustbl_has_string_diff_case_only.htm diff --git a/energyplus_regressions/diffs/table_diff.py b/energyplus_regressions/diffs/table_diff.py index d1e3e82..b45e8a8 100755 --- a/energyplus_regressions/diffs/table_diff.py +++ b/energyplus_regressions/diffs/table_diff.py @@ -121,8 +121,12 @@ def thresh_abs_rel_diff(abs_thresh, rel_thresh, x, y): # else: # diff = 'equal' return abs_diff, rel_diff, diff - except: - return '%s vs %s' % (x, y), '%s vs %s' % (x, y), 'stringdiff' + except ValueError: + # if we couldn't get a float out of it, we are doing string comparison, check case-insensitively before leaving + if x.lower().strip() == y.lower().strip(): + return 0, 0, 'equal' + else: + return '%s vs %s' % (x, y), '%s vs %s' % (x, y), 'stringdiff' def prev_sib(entity): diff --git a/energyplus_regressions/tests/diffs/tbl_resources/eplustbl_has_string_diff_case_only.htm b/energyplus_regressions/tests/diffs/tbl_resources/eplustbl_has_string_diff_case_only.htm new file mode 100644 index 0000000..4ed2df1 --- /dev/null +++ b/energyplus_regressions/tests/diffs/tbl_resources/eplustbl_has_string_diff_case_only.htm @@ -0,0 +1,100 @@ + + + + Bldg DENVER CENTENNIAL ANN CLG 1% CONDNS DB=>MWB ** + 2018-11-20 + 17:26:37 + - EnergyPlus + + + +

Table of Contents

+ +

Program Version:EnergyPlus, Version 9.0.1-a7c9cc14ce, YMD=2018.11.20 17:26

+

Tabular Output Report in Format: HTML

+

Building: Bldg

+

Environment: DENVER CENTENNIAL ANN CLG 1% CONDNS DB=>MWB **

+

Simulation Timestamp: 2018-11-20 + 17:26:37

+
+

Table of Contents

+ +

Report: Annual Building Utility Performance Summary

+

For: Entire Facility

+

Timestamp: 2018-11-20 + 17:26:37

+Values gathered over 0.00 hours

+WARNING: THE REPORT DOES NOT REPRESENT A FULL ANNUAL SIMULATION.

+

+Site and Source Energy

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Total Energy [GJ]Energy Per Total Building Area [MJ/m2]Energy Per Conditioned Building Area [MJ/m2]
Total Site Energy hello 0.00 0.00
Net Site Energy 0.00 0.00 0.00
Total Source Energy 0.00 0.00 0.00
Net Source Energy 0.00 0.00 0.00
+

+Site to Source Energy Conversion Factors

+ + + + + + + + + + + + + +
Site=>Source Conversion Factor
Electricity 3.167
Natural Gas 1.084
+

+Building Area

+ + + + + + + + + + + + + + + + + +
Area [m2]
Total Building Area 232.26
Net Conditioned Building Area 232.26
Unconditioned Building Area 0.00
+

+ + diff --git a/energyplus_regressions/tests/diffs/test_table_diff.py b/energyplus_regressions/tests/diffs/test_table_diff.py index e9c9b1e..6747d3c 100644 --- a/energyplus_regressions/tests/diffs/test_table_diff.py +++ b/energyplus_regressions/tests/diffs/test_table_diff.py @@ -194,6 +194,27 @@ def test_string_diff(self): self.assertEqual(0, response[7]) # in file 2 but not in file 1 self.assertEqual(0, response[8]) # in file 1 but not in file 2 + def test_string_diff_case_change_only(self): + # should be no diffs for case-insensitive comparison + response = table_diff( + self.thresh_dict, + os.path.join(self.diff_files_dir, 'eplustbl_has_string_diff_base.htm'), + os.path.join(self.diff_files_dir, 'eplustbl_has_string_diff_case_only.htm'), + os.path.join(self.temp_output_dir, 'abs_diff.htm'), + os.path.join(self.temp_output_dir, 'rel_diff.htm'), + os.path.join(self.temp_output_dir, 'math_diff.log'), + os.path.join(self.temp_output_dir, 'summary.htm'), + ) + self.assertEqual('', response[0]) # diff status + self.assertEqual(3, response[1]) # count_of_tables + self.assertEqual(0, response[2]) # big diffs + self.assertEqual(0, response[3]) # small diffs + self.assertEqual(17, response[4]) # equals + self.assertEqual(0, response[5]) # string diffs + self.assertEqual(0, response[6]) # size errors + self.assertEqual(0, response[7]) # in file 2 but not in file 1 + self.assertEqual(0, response[8]) # in file 1 but not in file 2 + def test_malformed_table_heading_in_file_1(self): response = table_diff( self.thresh_dict, From 79eac87a70554bb695eddcbc153576c0d39ab8f2 Mon Sep 17 00:00:00 2001 From: Edwin Lee Date: Mon, 14 Apr 2025 11:43:35 -0500 Subject: [PATCH 03/10] flake8 --- energyplus_regressions/builds/base.py | 2 -- energyplus_regressions/builds/install.py | 1 - energyplus_regressions/builds/makefile.py | 1 - energyplus_regressions/energyplus.py | 3 ++- energyplus_regressions/runtests.py | 2 +- 5 files changed, 3 insertions(+), 6 deletions(-) diff --git a/energyplus_regressions/builds/base.py b/energyplus_regressions/builds/base.py index 18c227a..411f67c 100644 --- a/energyplus_regressions/builds/base.py +++ b/energyplus_regressions/builds/base.py @@ -1,8 +1,6 @@ from pathlib import Path from typing import Optional, Set -from energyplus_regressions.structures import ConfigType - class KnownBuildTypes: Makefile = "makefile" diff --git a/energyplus_regressions/builds/install.py b/energyplus_regressions/builds/install.py index dc66c6c..81d02d2 100644 --- a/energyplus_regressions/builds/install.py +++ b/energyplus_regressions/builds/install.py @@ -2,7 +2,6 @@ from energyplus_regressions.builds.base import BaseBuildDirectoryStructure, BuildTree from energyplus_regressions.ep_platform import exe_extension -from energyplus_regressions.structures import ConfigType class EPlusInstallDirectory(BaseBuildDirectoryStructure): diff --git a/energyplus_regressions/builds/makefile.py b/energyplus_regressions/builds/makefile.py index 6b043f3..4091283 100644 --- a/energyplus_regressions/builds/makefile.py +++ b/energyplus_regressions/builds/makefile.py @@ -2,7 +2,6 @@ from energyplus_regressions.builds.base import BaseBuildDirectoryStructure, BuildTree from energyplus_regressions.ep_platform import exe_extension -from energyplus_regressions.structures import ConfigType class CMakeCacheMakeFileBuildDirectory(BaseBuildDirectoryStructure): diff --git a/energyplus_regressions/energyplus.py b/energyplus_regressions/energyplus.py index bdaefbc..70d7f27 100755 --- a/energyplus_regressions/energyplus.py +++ b/energyplus_regressions/energyplus.py @@ -104,7 +104,8 @@ def execute_energyplus(e_args: ExecutionArguments) -> tuple[Path, str, bool, boo # Run ExpandObjects and process as necessary, but not for epJSON files! if idf_file.exists(): expand_objects_run = subprocess.Popen( - str(expand_objects), shell=True, stdin=subprocess.DEVNULL, stdout=subprocess.PIPE, stderr=subprocess.PIPE + str(expand_objects), shell=True, stdin=subprocess.DEVNULL, + stdout=subprocess.PIPE, stderr=subprocess.PIPE ) o, e = expand_objects_run.communicate() std_out += o diff --git a/energyplus_regressions/runtests.py b/energyplus_regressions/runtests.py index 35eaac9..182f204 100755 --- a/energyplus_regressions/runtests.py +++ b/energyplus_regressions/runtests.py @@ -43,7 +43,7 @@ class TestRunConfiguration: def __init__(self, force_run_type: str, num_threads: int, report_freq: str, build_a: BaseBuildDirectoryStructure, build_b: BaseBuildDirectoryStructure, - single_test_run: bool=False, force_output_sql: ForceOutputSQL = ForceOutputSQL.NOFORCE, + single_test_run: bool = False, force_output_sql: ForceOutputSQL = ForceOutputSQL.NOFORCE, force_output_sql_unitconv: ForceOutputSQLUnitConversion = ForceOutputSQLUnitConversion.NOFORCE): self.force_run_type = force_run_type self.TestOneFile = single_test_run From dffcf1dccb6e0ef2970ddf8ff0c4328eab912e1a Mon Sep 17 00:00:00 2001 From: Edwin Lee Date: Mon, 14 Apr 2025 15:32:06 -0500 Subject: [PATCH 04/10] Just cleaning up some code, handling build mode a little better --- energyplus_regressions/tk_window.py | 72 +++++++++++++++-------------- 1 file changed, 38 insertions(+), 34 deletions(-) diff --git a/energyplus_regressions/tk_window.py b/energyplus_regressions/tk_window.py index f5d5882..cacc1da 100644 --- a/energyplus_regressions/tk_window.py +++ b/energyplus_regressions/tk_window.py @@ -195,23 +195,17 @@ def __init__(self): self.build_dir_2_button = None self.run_button = None self.stop_button = None - self.build_dir_1_label = None if system() == 'Windows': - self.build_dir_1_var.set(r'C:\EnergyPlus\repos\1eplus\builds\VS64') # "") + self.build_dir_1_var.set('/Users/elee/eplus/repos/1eplus/builds/r') + self.build_dir_2_var.set('/Users/elee/eplus/repos/2eplus/builds/r') elif system() == 'Linux': - self.build_dir_1_var.set('/eplus/repos/1eplus/builds/r') # "") - self.build_dir_2_label = None - if system() == 'Windows': - self.build_dir_2_var.set(r'C:\EnergyPlus\repos\2eplus\builds\VS64') # "") - elif system() == 'Linux': - self.build_dir_2_var.set('/eplus/repos/2eplus/builds/r') # "") self.progress = None self.log_message_listbox = None @@ -236,12 +230,12 @@ def __init__(self): # some data holders self.tree_folders = dict() self.valid_idfs_in_listing = False - self.build_1 = None - self.build_2 = None + self.build_1: BaseBuildDirectoryStructure | None = None + self.build_2: BaseBuildDirectoryStructure | None = None self.last_results = None self.auto_saving = False self.manually_saving = False - self.save_interval = 10000 # ms, so 1 minute + self.save_interval_ms = 60_000 # ms, so 1 minute # initialize the GUI self.main_notebook = None @@ -251,7 +245,7 @@ def __init__(self): self.client_open(auto_open=True) # PyCharm is relentlessly complaining the unused *args parameter to root.after, when it's not needed # noinspection PyTypeChecker - self.root.after(self.save_interval, self.auto_save) + self.root.after(self.save_interval_ms, self.auto_save) # set up any Var traces here after the init is all done self.preferred_build_type.trace_add("write", self.refresh_builds_for_build_type_change) @@ -306,15 +300,15 @@ def init_window(self): self.build_dir_1_button = ttk.Button(group_build_dir_1, text="Change...", command=self.client_build_dir_1, style="C.TButton") self.build_dir_1_button.grid(row=1, column=1, sticky=W) - self.build_dir_1_label = Label(group_build_dir_1, textvariable=self.build_dir_1_var) - self.build_dir_1_label.grid(row=1, column=2, sticky=E) + build_dir_1_label = Label(group_build_dir_1, textvariable=self.build_dir_1_var) + build_dir_1_label.grid(row=1, column=2, sticky=E) group_build_dir_2 = LabelFrame(pane_run, text="Build Directory 2") group_build_dir_2.pack(fill=X, padx=5) self.build_dir_2_button = ttk.Button(group_build_dir_2, text="Change...", command=self.client_build_dir_2, style="C.TButton") self.build_dir_2_button.grid(row=1, column=1, sticky=W) - self.build_dir_2_label = Label(group_build_dir_2, textvariable=self.build_dir_2_var) - self.build_dir_2_label.grid(row=1, column=2, sticky=E) + build_dir_2_label = Label(group_build_dir_2, textvariable=self.build_dir_2_var) + build_dir_2_label.grid(row=1, column=2, sticky=E) group_run_options = LabelFrame(pane_run, text="Run Options") group_run_options.pack(fill=X, padx=5) # row 1 @@ -499,18 +493,21 @@ def client_open(self, auto_open=False): self.reporting_frequency.set(data['report_freq']) self.force_output_sql.set(data['force_output_sql']) self.force_output_sql_unitconv.set(data['force_output_sql_unitconv']) - if 'preferred_build_type' in data: # it's initialized to RELEASE in the window __init__ + if 'preferred_build_type' in data: # it's initialized to RELEASE in the window __init__, override if found self.preferred_build_type.set(data['preferred_build_type']) - status = self.try_to_set_build_1_to_dir(Path(data['build_1_build_dir'])) + # try to set build 1 object, where it will try to use the preferred build type + status = self.try_to_set_build_1_to_dir(Path(data['build_1_build_dir']), init_mode=True) if status: self.build_dir_1_var.set(data['build_1_build_dir']) if isinstance(self.build_1, CMakeCacheVisualStudioBuildDirectory): self.build_1.set_build_mode(ConfigType(self.preferred_build_type.get())) - status = self.try_to_set_build_2_to_dir(Path(data['build_2_build_dir'])) + # try to set build 2 object, where it will try to use the preferred build type + status = self.try_to_set_build_2_to_dir(Path(data['build_2_build_dir']), init_mode=True) if status: self.build_dir_2_var.set(data['build_2_build_dir']) if isinstance(self.build_2, CMakeCacheVisualStudioBuildDirectory): self.build_2.set_build_mode(ConfigType(self.preferred_build_type.get())) + # at this point we should have build dirs, but it's OK if they are invalid self.build_idf_listing(False, data['idfs']) self.add_to_log("Project settings loaded") except Exception: @@ -524,7 +521,7 @@ def auto_save(self): self.client_save(auto_save=True) # PyCharm is relentlessly complaining the unused *args parameter to root.after, when it's not needed # noinspection PyTypeChecker - self.root.after(self.save_interval, self.auto_save) + self.root.after(self.save_interval_ms, self.auto_save) def client_save(self, auto_save=False): # we shouldn't come into this function from the auto_save if any other saving is going on already @@ -605,7 +602,7 @@ def results_popup(self, event): if title.startswith('Case '): if title.endswith('(0)'): context_menu = Menu(self, tearoff=0) - context_menu.add_command(label="Selected Node Has No Children", command=self.dummy) + context_menu.add_command(label="Selected Node Has No Children", command=lambda: None) context_menu.post(event.x_root, event.y_root) else: tags = self.results_tree.item(iid, "tags") @@ -620,9 +617,6 @@ def copy_lambda(): # ignoring anything but the tree root nodes pass - def dummy(self): - pass - def copy_selected_node(self, tags): string = ';'.join(tags) self.root.clipboard_clear() @@ -979,7 +973,11 @@ def refresh_builds_for_build_type_change(self, *_): self.try_to_set_build_1_to_dir(self.build_1.build_directory) self.try_to_set_build_2_to_dir(self.build_2.build_directory) - def try_to_set_build_1_to_dir(self, selected_dir: Path) -> bool: + def add_to_log_and_alert(self, message: str): + self.add_to_log(message) + messagebox.showerror("Error", message) + + def try_to_set_build_1_to_dir(self, selected_dir: Path, init_mode: bool = False) -> bool: probable_build_dir_type = autodetect_build_dir_type(selected_dir) if probable_build_dir_type == KnownBuildTypes.Unknown: self.add_to_log("Could not detect build 1 type") @@ -992,7 +990,10 @@ def try_to_set_build_1_to_dir(self, selected_dir: Path) -> bool: self.add_to_log("Build 1 type detected as a Visual Studio build") self.build_1 = CMakeCacheVisualStudioBuildDirectory() self.build_1.set_build_directory(selected_dir) - self.build_1.set_build_mode(ConfigType(self.preferred_build_type.get()), self.add_to_log) + if init_mode: + self.build_1.set_build_mode(ConfigType(self.preferred_build_type.get()), self.add_to_log) + else: + self.build_1.set_build_mode(ConfigType(self.preferred_build_type.get()), self.add_to_log_and_alert) elif probable_build_dir_type == KnownBuildTypes.Makefile: self.add_to_log("Build 1 type detected as a Makefile-style build") self.build_1 = CMakeCacheMakeFileBuildDirectory() @@ -1015,7 +1016,7 @@ def client_build_dir_1(self): self.build_dir_1_var.set(str(selected_dir)) self.build_idf_listing() - def try_to_set_build_2_to_dir(self, selected_dir: Path) -> bool: + def try_to_set_build_2_to_dir(self, selected_dir: Path, init_mode: bool = False) -> bool: probable_build_dir_type = autodetect_build_dir_type(selected_dir) if probable_build_dir_type == KnownBuildTypes.Unknown: self.add_to_log("Could not detect build 2 type") @@ -1028,7 +1029,10 @@ def try_to_set_build_2_to_dir(self, selected_dir: Path) -> bool: self.add_to_log("Build 2 type detected as a Visual Studio build") self.build_2 = CMakeCacheVisualStudioBuildDirectory() self.build_2.set_build_directory(selected_dir) - self.build_2.set_build_mode(ConfigType(self.preferred_build_type.get()), self.add_to_log) + if init_mode: + self.build_2.set_build_mode(ConfigType(self.preferred_build_type.get()), self.add_to_log) + else: + self.build_2.set_build_mode(ConfigType(self.preferred_build_type.get()), self.add_to_log_and_alert) elif probable_build_dir_type == KnownBuildTypes.Makefile: self.add_to_log("Build 2 type detected as a Makefile-style build") self.build_2 = CMakeCacheMakeFileBuildDirectory() @@ -1064,7 +1068,7 @@ def client_run(self): return ok_or_cancel_msg = "Press OK to continue anyway (risky!), or press Cancel to abort" build_1_valid = self.build_1.verify() - build_1_problem_files = [b[1] for b in build_1_valid if not b[2]] + build_1_problem_files = [str(b[1]) for b in build_1_valid if not b[2]] if len(build_1_problem_files): missing_files = '\n'.join(build_1_problem_files) r = messagebox.askokcancel("Build folder 1 problem", f"Missing files:\n{missing_files}\n{ok_or_cancel_msg}") @@ -1074,7 +1078,7 @@ def client_run(self): messagebox.showerror("Build folder 2 problem", "Select a valid build folder 2 prior to running") return build_2_valid = self.build_2.verify() - build_2_problem_files = [b[1] for b in build_2_valid if not b[2]] + build_2_problem_files = [str(b[1]) for b in build_2_valid if not b[2]] if len(build_2_problem_files): missing_files = '\n'.join(build_2_problem_files) r = messagebox.askokcancel("Build folder 2 problem", f"Missing files:\n{missing_files}\n{ok_or_cancel_msg}") From d4997ceef1e269788829ef665e95085880e7c71a Mon Sep 17 00:00:00 2001 From: Edwin Lee Date: Mon, 14 Apr 2025 15:34:32 -0500 Subject: [PATCH 05/10] Comment unneeded code --- energyplus_regressions/tk_window.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/energyplus_regressions/tk_window.py b/energyplus_regressions/tk_window.py index cacc1da..96d8e6b 100644 --- a/energyplus_regressions/tk_window.py +++ b/energyplus_regressions/tk_window.py @@ -499,14 +499,14 @@ def client_open(self, auto_open=False): status = self.try_to_set_build_1_to_dir(Path(data['build_1_build_dir']), init_mode=True) if status: self.build_dir_1_var.set(data['build_1_build_dir']) - if isinstance(self.build_1, CMakeCacheVisualStudioBuildDirectory): - self.build_1.set_build_mode(ConfigType(self.preferred_build_type.get())) + # if isinstance(self.build_1, CMakeCacheVisualStudioBuildDirectory): + # self.build_1.set_build_mode(ConfigType(self.preferred_build_type.get())) # try to set build 2 object, where it will try to use the preferred build type status = self.try_to_set_build_2_to_dir(Path(data['build_2_build_dir']), init_mode=True) if status: self.build_dir_2_var.set(data['build_2_build_dir']) - if isinstance(self.build_2, CMakeCacheVisualStudioBuildDirectory): - self.build_2.set_build_mode(ConfigType(self.preferred_build_type.get())) + # if isinstance(self.build_2, CMakeCacheVisualStudioBuildDirectory): + # self.build_2.set_build_mode(ConfigType(self.preferred_build_type.get())) # at this point we should have build dirs, but it's OK if they are invalid self.build_idf_listing(False, data['idfs']) self.add_to_log("Project settings loaded") From 72ea3d287dc7fd1a765949cbedd040523484b175 Mon Sep 17 00:00:00 2001 From: Edwin Lee Date: Mon, 14 Apr 2025 16:16:48 -0500 Subject: [PATCH 06/10] Print statements for debugging --- energyplus_regressions/tk_window.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/energyplus_regressions/tk_window.py b/energyplus_regressions/tk_window.py index 96d8e6b..d1cb465 100644 --- a/energyplus_regressions/tk_window.py +++ b/energyplus_regressions/tk_window.py @@ -503,6 +503,7 @@ def client_open(self, auto_open=False): # self.build_1.set_build_mode(ConfigType(self.preferred_build_type.get())) # try to set build 2 object, where it will try to use the preferred build type status = self.try_to_set_build_2_to_dir(Path(data['build_2_build_dir']), init_mode=True) + print("B") if status: self.build_dir_2_var.set(data['build_2_build_dir']) # if isinstance(self.build_2, CMakeCacheVisualStudioBuildDirectory): @@ -689,6 +690,7 @@ def build_idf_listing(self, initialize=False, desired_selected_idfs: list[str] = return if not self.build_2: bd2 = Path(self.build_dir_2_var.get()) + print("C") status = self.try_to_set_build_2_to_dir(bd2) if not status: self.full_idf_listbox.insert(END, "Cannot update master list master list") @@ -970,6 +972,7 @@ def set_gui_status_for_run(self, is_running: bool): def refresh_builds_for_build_type_change(self, *_): # just try to refresh both build directories, it will warn if the debug/release folders aren't there + print("A") self.try_to_set_build_1_to_dir(self.build_1.build_directory) self.try_to_set_build_2_to_dir(self.build_2.build_directory) @@ -1046,6 +1049,7 @@ def client_build_dir_2(self): p = Path(selected_dir) if not p.exists(): return + print("D") status = self.try_to_set_build_2_to_dir(p) if not status: messagebox.showerror("Could not determine build type for build 2!") From 932d47083102059e44376b8d8abb6094a7637c73 Mon Sep 17 00:00:00 2001 From: Edwin Lee Date: Tue, 15 Apr 2025 11:10:16 -0500 Subject: [PATCH 07/10] Override verify() method in the VS build class to check for debug/release folder --- energyplus_regressions/builds/visualstudio.py | 9 +++++++++ energyplus_regressions/tk_window.py | 4 ---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/energyplus_regressions/builds/visualstudio.py b/energyplus_regressions/builds/visualstudio.py index 607879e..b72a3a0 100644 --- a/energyplus_regressions/builds/visualstudio.py +++ b/energyplus_regressions/builds/visualstudio.py @@ -80,3 +80,12 @@ def get_build_tree(self) -> BuildTree: b.weather_dir = self.source_directory / 'weather' b.data_sets_dir = self.source_directory / 'datasets' return b + + def verify(self) -> list[tuple[str, Path, bool]]: + desired_build_directory = self.build_directory / 'Products' / self.build_mode + full_results = list() + full_results.append( + ("Case %s Build Mode Directory Exists? ", desired_build_directory, desired_build_directory.exists()) + ) + base_results = super().verify() + return full_results + base_results diff --git a/energyplus_regressions/tk_window.py b/energyplus_regressions/tk_window.py index d1cb465..96d8e6b 100644 --- a/energyplus_regressions/tk_window.py +++ b/energyplus_regressions/tk_window.py @@ -503,7 +503,6 @@ def client_open(self, auto_open=False): # self.build_1.set_build_mode(ConfigType(self.preferred_build_type.get())) # try to set build 2 object, where it will try to use the preferred build type status = self.try_to_set_build_2_to_dir(Path(data['build_2_build_dir']), init_mode=True) - print("B") if status: self.build_dir_2_var.set(data['build_2_build_dir']) # if isinstance(self.build_2, CMakeCacheVisualStudioBuildDirectory): @@ -690,7 +689,6 @@ def build_idf_listing(self, initialize=False, desired_selected_idfs: list[str] = return if not self.build_2: bd2 = Path(self.build_dir_2_var.get()) - print("C") status = self.try_to_set_build_2_to_dir(bd2) if not status: self.full_idf_listbox.insert(END, "Cannot update master list master list") @@ -972,7 +970,6 @@ def set_gui_status_for_run(self, is_running: bool): def refresh_builds_for_build_type_change(self, *_): # just try to refresh both build directories, it will warn if the debug/release folders aren't there - print("A") self.try_to_set_build_1_to_dir(self.build_1.build_directory) self.try_to_set_build_2_to_dir(self.build_2.build_directory) @@ -1049,7 +1046,6 @@ def client_build_dir_2(self): p = Path(selected_dir) if not p.exists(): return - print("D") status = self.try_to_set_build_2_to_dir(p) if not status: messagebox.showerror("Could not determine build type for build 2!") From 3e449cb024a326c69c83c93356b634373bee4962 Mon Sep 17 00:00:00 2001 From: Edwin Lee Date: Tue, 15 Apr 2025 11:12:04 -0500 Subject: [PATCH 08/10] Debug message --- energyplus_regressions/tk_window.py | 1 + 1 file changed, 1 insertion(+) diff --git a/energyplus_regressions/tk_window.py b/energyplus_regressions/tk_window.py index 96d8e6b..6330f87 100644 --- a/energyplus_regressions/tk_window.py +++ b/energyplus_regressions/tk_window.py @@ -1068,6 +1068,7 @@ def client_run(self): return ok_or_cancel_msg = "Press OK to continue anyway (risky!), or press Cancel to abort" build_1_valid = self.build_1.verify() + print(build_1_valid) build_1_problem_files = [str(b[1]) for b in build_1_valid if not b[2]] if len(build_1_problem_files): missing_files = '\n'.join(build_1_problem_files) From 11ddb83e0200b40fa028de883f52dfaeb3c09cf0 Mon Sep 17 00:00:00 2001 From: Edwin Lee Date: Tue, 15 Apr 2025 11:17:20 -0500 Subject: [PATCH 09/10] Attempt to re-set build mode when running --- energyplus_regressions/builds/visualstudio.py | 9 --------- energyplus_regressions/tk_window.py | 9 ++++----- 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/energyplus_regressions/builds/visualstudio.py b/energyplus_regressions/builds/visualstudio.py index b72a3a0..607879e 100644 --- a/energyplus_regressions/builds/visualstudio.py +++ b/energyplus_regressions/builds/visualstudio.py @@ -80,12 +80,3 @@ def get_build_tree(self) -> BuildTree: b.weather_dir = self.source_directory / 'weather' b.data_sets_dir = self.source_directory / 'datasets' return b - - def verify(self) -> list[tuple[str, Path, bool]]: - desired_build_directory = self.build_directory / 'Products' / self.build_mode - full_results = list() - full_results.append( - ("Case %s Build Mode Directory Exists? ", desired_build_directory, desired_build_directory.exists()) - ) - base_results = super().verify() - return full_results + base_results diff --git a/energyplus_regressions/tk_window.py b/energyplus_regressions/tk_window.py index 6330f87..cf7bddb 100644 --- a/energyplus_regressions/tk_window.py +++ b/energyplus_regressions/tk_window.py @@ -499,14 +499,10 @@ def client_open(self, auto_open=False): status = self.try_to_set_build_1_to_dir(Path(data['build_1_build_dir']), init_mode=True) if status: self.build_dir_1_var.set(data['build_1_build_dir']) - # if isinstance(self.build_1, CMakeCacheVisualStudioBuildDirectory): - # self.build_1.set_build_mode(ConfigType(self.preferred_build_type.get())) # try to set build 2 object, where it will try to use the preferred build type status = self.try_to_set_build_2_to_dir(Path(data['build_2_build_dir']), init_mode=True) if status: self.build_dir_2_var.set(data['build_2_build_dir']) - # if isinstance(self.build_2, CMakeCacheVisualStudioBuildDirectory): - # self.build_2.set_build_mode(ConfigType(self.preferred_build_type.get())) # at this point we should have build dirs, but it's OK if they are invalid self.build_idf_listing(False, data['idfs']) self.add_to_log("Project settings loaded") @@ -1068,7 +1064,8 @@ def client_run(self): return ok_or_cancel_msg = "Press OK to continue anyway (risky!), or press Cancel to abort" build_1_valid = self.build_1.verify() - print(build_1_valid) + if isinstance(self.build_1, CMakeCacheVisualStudioBuildDirectory): + self.build_1.set_build_mode(ConfigType(self.preferred_build_type.get()), self.add_to_log_and_alert) build_1_problem_files = [str(b[1]) for b in build_1_valid if not b[2]] if len(build_1_problem_files): missing_files = '\n'.join(build_1_problem_files) @@ -1079,6 +1076,8 @@ def client_run(self): messagebox.showerror("Build folder 2 problem", "Select a valid build folder 2 prior to running") return build_2_valid = self.build_2.verify() + if isinstance(self.build_2, CMakeCacheVisualStudioBuildDirectory): + self.build_2.set_build_mode(ConfigType(self.preferred_build_type.get()), self.add_to_log_and_alert) build_2_problem_files = [str(b[1]) for b in build_2_valid if not b[2]] if len(build_2_problem_files): missing_files = '\n'.join(build_2_problem_files) From e3d39b43bf8a06d75e70f296181cbc2d6ac793e1 Mon Sep 17 00:00:00 2001 From: Edwin Lee Date: Tue, 15 Apr 2025 11:20:10 -0500 Subject: [PATCH 10/10] Bump version --- energyplus_regressions/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/energyplus_regressions/__init__.py b/energyplus_regressions/__init__.py index 262e46c..caaa52c 100644 --- a/energyplus_regressions/__init__.py +++ b/energyplus_regressions/__init__.py @@ -1,2 +1,2 @@ NAME = 'energyplus_regressions' -VERSION = '2.1.2' +VERSION = '2.1.3'