Skip to content

Commit

Permalink
Code coverage in SonarQube (#448)
Browse files Browse the repository at this point in the history
* write sounding_data.dat file in output directory instead of current working directory

* refactored plot classes - renamed logger class variables to be consistent and call utility functions to greatly simplify the main functions of each plot that are handled in the same way

* Refactored unit tests so they can be run from any directory, not just the directory containing the test. Added pytest fixtures to handle setup of environment and remove files and intermed_files directories. Set env var TEST_DIR to the current test directory and reference it in yaml files to ensure that test input/output is read/written from the correct test directory. Changed file paths to be relative to test directory instead of current working directory

* added PyCharm-generated files that shouldn't change

* run pytests from top directory and generate code coverage report to send to SonarQube server

* clone METcalcpy into different directory to prevent pytest to running its tests

* install METcalcpy into different directory

* cleanup

* remove unnecessary call to remove sounding_data.dat file that is removed when scrubbing the output dir

* remove unused variable

* removed unused variable and whitespace

* clean up linter complaints

* add prints to help debugging if test fails

* clean up tests to run faster by running plot script once and checking for all files
  • Loading branch information
georgemccabe authored Jun 17, 2024
1 parent feb0d07 commit f332db6
Show file tree
Hide file tree
Showing 121 changed files with 1,168 additions and 1,931 deletions.
2 changes: 2 additions & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[run]
relative_files = True
30 changes: 30 additions & 0 deletions .github/workflows/sonarqube.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,36 @@ jobs:
# Disable shallow clones for better analysis
fetch-depth: 0

- name: Set up Python 3.10
uses: actions/setup-python@v5
with:
python-version: "3.10"

- name: Retrieve METcalcpy repository develop branch
run: |
metcalcpy_dir="$RUNNER_WORKSPACE/METcalcpy"
git clone https://github.com/dtcenter/METcalcpy ${metcalcpy_dir}
cd ${metcalcpy_dir}
git -C ${metcalcpy_dir} checkout develop
python -m pip install -e ${metcalcpy_dir}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
python -m pip install pytest-cov
- name: Run Pytests
run: coverage run -m pytest

- name: Output coverage report
run: coverage report -m
if: always()

- name: Generate XML coverage report
run: coverage xml
if: always()

- name: Get branch name
id: get_branch_name
run: echo branch_name=${GITHUB_REF#refs/heads/} >> $GITHUB_OUTPUT
Expand Down
46 changes: 6 additions & 40 deletions .github/workflows/unit_tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,11 @@ jobs:

- name: Retrieve METcalcpy repository develop branch
run: |
/usr/bin/git clone https://github.com/dtcenter/METcalcpy
cd METcalcpy
/usr/bin/git checkout develop
python -m pip install -e .
metcalcpy_dir="$RUNNER_WORKSPACE/METcalcpy"
git clone https://github.com/dtcenter/METcalcpy ${metcalcpy_dir}
cd ${metcalcpy_dir}
git -C ${metcalcpy_dir} checkout develop
python -m pip install -e ${metcalcpy_dir}
- name: Install dependencies
run: |
Expand All @@ -55,43 +56,8 @@ jobs:
# run: echo "##[set-output name=branch;]$(echo ${GITHUB_REF#refs/heads/})"
# id: extract_branch


- name: Test with pytest
run: |
cd test
cd bar
pytest test_bar.py
cd ../box
pytest test_box.py
cd ../contour
pytest test_contour.py
cd ../eclv
pytest test_eclv.py
cd ../ens_ss
pytest test_ens_ss.py
cd ../equivalence_testing_bounds
pytest test_equivalence_testing_bounds.py
cd ../line
pytest test_line_groups_plot.py
pytest test_line_plot.py
cd ../mpr_plot
pytest test_mpr_plot.py
cd ../performance_diagram
pytest test_performance_diagram.py
cd ../reliability_diagram
pytest test_reliability_diagram.py
cd ../roc_diagram
pytest test_roc_diagram.py
cd ../taylor_diagram
pytest test_taylor_diagram.py
cd ../wind_rose
pytest test_wind_rose.py
cd ../histogram
pytest test_prob_hist.py
pytest test_rank_hist.py
pytest test_rel_hist.py
cd ../tcmpr_plots
pytest --capture=fd test_tcmpr_plots.py
run: pytest



9 changes: 9 additions & 0 deletions .idea/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 15 additions & 0 deletions .idea/METplotpy.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 14 additions & 0 deletions .idea/inspectionProfiles/Project_Default.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/inspectionProfiles/profiles_settings.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions .idea/modules.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions internal/scripts/sonarqube/sonar-project.properties
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ sonar.projectVersion=SONAR_PROJECT_VERSION
sonar.branch.name=SONAR_BRANCH_NAME
sonar.sources=metplotpy,test
sonar.coverage.exclusions=test/**
sonar.python.coverage.reportPaths=coverage.xml
sonar.sourceEncoding=UTF-8

# SonarQube server
Expand Down
47 changes: 12 additions & 35 deletions metplotpy/plots/bar/bar.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@

import pandas as pd
import plotly.graph_objects as go
import yaml
from plotly.graph_objects import Figure
from plotly.subplots import make_subplots

Expand Down Expand Up @@ -53,11 +52,11 @@ def __init__(self, parameters: dict) -> None:
# the
# config file that represents the BasePlot object (Bar).
self.config_obj = BarConfig(self.parameters)
self.bar_logger = self.config_obj.logger
self.bar_logger.info(f"Start bar plot: {datetime.now()}")
self.logger = self.config_obj.logger
self.logger.info(f"Start bar plot: {datetime.now()}")
# Check that we have all the necessary settings for each series
self.bar_logger.info(f"Consistency checking of config settings for colors, "
f"legends, etc.")
self.logger.info("Consistency checking of config settings for colors, "
"legends, etc.")
is_config_consistent = self.config_obj._config_consistency_check()
if not is_config_consistent:
value_error_msg = ("ValueError: The number of series defined by series_val_1 and "
Expand All @@ -66,20 +65,19 @@ def __init__(self, parameters: dict) -> None:
"check the number of your configuration file's "
"plot_i, plot_disp, series_order, user_legend, show_legend and "
"colors settings.")
self.bar_logger.error(value_error_msg)
self.logger.error(value_error_msg)
raise ValueError(value_error_msg)


# Read in input data, location specified in config file
self.bar_logger.info(f"Begin reading input data: {datetime.now()}")
self.logger.info(f"Begin reading input data: {datetime.now()}")
self.input_df = self._read_input_data()

# Apply event equalization, if requested
if self.config_obj.use_ee is True:
self.bar_logger.info(f"Performing event equalization: {datetime.now()}")
self.logger.info(f"Performing event equalization: {datetime.now()}")
self.input_df = calc_util.perform_event_equalization(self.parameters,
self.input_df)
self.bar_logger.info(f"End event equalization: {datetime.now()}")
self.logger.info(f"End event equalization: {datetime.now()}")

# Create a list of series objects.
# Each series object contains all the necessary information for plotting,
Expand All @@ -91,9 +89,9 @@ def __init__(self, parameters: dict) -> None:
# Need to have a self.figure that we can pass along to
# the methods in base_plot.py (BasePlot class methods) to
# create binary versions of the plot.
self.bar_logger.info(f"Begin creating the figure: {datetime.now()}")
self.logger.info(f"Begin creating the figure: {datetime.now()}")
self._create_figure()
self.bar_logger.info(f"End creating the figure: {datetime.now()}")
self.logger.info(f"End creating the figure: {datetime.now()}")

def __repr__(self):
""" Implement repr which can be useful for debugging this
Expand All @@ -114,7 +112,7 @@ def _read_input_data(self):
Returns:
"""
self.bar_logger.info(f"Finished reading input data: "
self.logger.info(f"Finished reading input data: "
f"{datetime.now()}")
return pd.read_csv(self.config_obj.parameters['stat_input'], sep='\t',
header='infer', float_precision='round_trip')
Expand Down Expand Up @@ -516,28 +514,7 @@ def main(config_filename=None):
@param config_filename: default is None, the name of the custom
config file to apply
"""

# Retrieve the contents of the custom config file to over-ride
# or augment settings defined by the default config file.
if not config_filename:
config_file = util.read_config_from_command_line()
else:
config_file = config_filename
with open(config_file, 'r') as stream:
try:
docs = yaml.load(stream, Loader=yaml.FullLoader)
except yaml.YAMLError as exc:
print(exc)

try:
plot = Bar(docs)
plot.save_to_file()
# plot.show_in_browser()
plot.write_html()
plot.write_output_file()
plot.bar_logger.info(f"Finished bar plot at {datetime.now()}")
except ValueError as val_er:
print(val_er)
util.make_plot(config_filename, Bar)


if __name__ == "__main__":
Expand Down
7 changes: 2 additions & 5 deletions metplotpy/plots/base_plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,6 @@ def __init__(self, parameters, default_conf_filename):
self.remove_file()
self.config_obj = Config(self.parameters)


def get_image_format(self):
"""Reads the image format type from user provided image name.
Uses file extension as a type. If the file extension is not valid -
Expand Down Expand Up @@ -340,8 +339,6 @@ def save_to_file(self):
Returns:
"""


image_name = self.get_config_value('plot_filename')

# Suppress deprecation warnings from third-party packages that are not in our control.
Expand Down Expand Up @@ -388,8 +385,8 @@ def show_in_browser(self):
if self.figure:
self.figure.show()
else:
self.logger.error(f" Figure not created. Nothing to show in the "
f"browser. ")
self.logger.error(" Figure not created. Nothing to show in the "
"browser. ")
print("Oops! The figure was not created. Can't show")

def _add_lines(self, config_obj: Config, x_points_index: Union[list, None] = None) -> None:
Expand Down
Loading

0 comments on commit f332db6

Please sign in to comment.