Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve Test Coverage and Refactor Test for mslib.utils.coordinate #2596

Open
wants to merge 8 commits into
base: develop
Choose a base branch
from

Conversation

saiabhinav001
Copy link

This PR enhances the test suite for the mslib.utils.coordinate module.

Key Updates:

  • Refactored and added new test cases for functions in the coordinate module.
  • Used pytest.mark.parametrize to simplify and expand parameterized testing.
  • Improved edge case coverage for distance, projection, and angle calculations.
  • Enhanced test readability and maintainability with detailed docstrings.
  • Validated linear and great circle path generation for lat/lon points.

Purpose of PR?:

Fixes #

Does this PR introduce a breaking change?
This PR does not introduce any breaking changes.

If the changes in this PR are manually verified, list down the scenarios covered::

If manually verified, the following scenarios were tested:

  1. Distance calculations across diverse lat/lon coordinates.
  2. Angle normalization and edge cases, including negative and large angles.
  3. Path generation for both linear and great circle connections with varied numpoints.
  4. Projection parameter retrieval for valid and invalid projection strings.

Additional information for reviewer? :
This PR is an independent contribution and does not depend on or continue from previous PRs.

Does this PR results in some Documentation changes?
No documentation changes are required.

Checklist:

  • Bug fix. Fixes
  • New feature (Non-API breaking changes that adds functionality)
  • PR Title follows the convention of <type>: <subject>
  • Commit has unit tests

Add comprehensive test suite for `mslib.utils.coordinate` module  

- Refactored and added new test cases for functions in `coordinate` module.  
- Used `pytest.mark.parametrize` for parameterized testing.  
- Improved edge case coverage for distance, projection, and angle calculations.  
- Enhanced test readability and maintainability with detailed docstrings.  
- Validated linear and great circle path generation for lat/lon points.
@ReimarBauer ReimarBauer requested review from matrss and joernu76 January 6, 2025 07:42

@pytest.mark.parametrize("ref_lats, ref_lons, numpoints", [
([0, 10], [0, 0], 2),
([0, 10], [0, 0], 3),
Copy link
Member

@ReimarBauer ReimarBauer Jan 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the second one had before ([0, 0], [0, 10] ,3)

assert all(np.asarray(lons) == [0, 5, 10])
numpoints=numpoints, connection="greatcircle")
assert len(lats) == numpoints
assert len(lons) == numpoints
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should also check for the expected value, like in assert all(np.asarray(lons) == [0, 5, 10])

Copy link
Member

@ReimarBauer ReimarBauer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @saiabhinav001
that makes it much better readable, thanks!

while the module we test here has no class definition we use a class level organization as namespace grouping. When test_linear maybe get named test_latlon_points_linear and the other test_latlon_points_greatcircle we maybe can remove all the grouping class names, what do you think?

Copy link
Author

@saiabhinav001 saiabhinav001 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @ReimarBauer
That's a great suggestion! Renaming the test functions makes them more descriptive, and removing the class-level grouping could simplify the structure.

I’ve updated the code by renaming test_linear to test_latlon_points_linear and test_greatcircle to test_latlon_points_greatcircle. I also removed the class-level groupings for simplicity and better readability. Let me know if there’s anything else to adjust!

@ReimarBauer
Copy link
Member

Hi @saiabhinav001 Maybe a push to the repo is missing? I do see the class names here and also there is a lint failure.

@ReimarBauer ReimarBauer changed the title Improve Test Coverage and Refactor for mslib.utils.coordinate Improve Test Coverage and Refactor Test for mslib.utils.coordinate Jan 9, 2025
Copy link
Author

@saiabhinav001 saiabhinav001 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @ReimarBauer, I ensured there were no lint failures and pushed the repo.

@ReimarBauer
Copy link
Member

ReimarBauer commented Jan 11, 2025

@saiabhinav001 why do you have closed it?

@saiabhinav001 saiabhinav001 reopened this Jan 12, 2025
@ReimarBauer
Copy link
Member

interesting how pytest.approx does this.

FAILED tests/_test_utils/test_coordinate.py::TestAngles::test_rotate_point[point5-45-rotated_point5] - assert (-1.767766952...7669529663689) == approx((-1.76...78 ± 1.8e-06))
  
  comparison failed. Mismatched elements: 2 / 2:
  Max absolute difference: 3.3047033631383727e-05
  Max relative difference: 1.8694225263081064e-05
  Index | Obtained            | Expected         
  0     | -1.7677669529663687 | -1.7678 ± 1.8e-06
  1     | 1.7677669529663689  | 1.7678 ± 1.8e-06
====== 1 failed, 588 passed, 13 skipped, 34 warnings in 470.39s (0:07:50) ======
ERROR conda.cli.main_run:execute(124): `conda run xvfb-run pytest -v -n 6 --dist loadfile --max-worker-restart 4 --durations=20 --cov=mslib tests` failed. (See above for error)

for numpoints, connection in [(100, "linear"), (100, "greatcircle")]:
result = coordinate.path_points(lats, lons, numpoints, times=times, connection=connection)
assert all(len(_x) == numpoints for _x in result)
for i in range(3):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From the description of eric:

https://pytest-with-eric.com/pytest-advanced/pytest-approx/#Example-Code-To-Test-Pytest-Approx

I would expect:

assert result[i][0] == pytest.approx(ref[i][0])
assert result[i][-1] == pytest.approx(ref[i][-1])

"""
Test rotating points around the origin.
"""
assert coordinate.rotate_point(point, angle) == pytest.approx(rotated_point)
Copy link
Member

@ReimarBauer ReimarBauer Jan 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks like you maybe found a bug by the ([0.0, 2.5], 45, (-1.7678, 1.7678)) example or the example has a problem.

How did you got to the -1.7678, 1.7678?

@ReimarBauer
Copy link
Member

ReimarBauer commented Jan 13, 2025

interesting how pytest.approx does this.

FAILED tests/_test_utils/test_coordinate.py::TestAngles::test_rotate_point[point5-45-rotated_point5] - assert (-1.767766952...7669529663689) == approx((-1.76...78 ± 1.8e-06))
  
  comparison failed. Mismatched elements: 2 / 2:
  Max absolute difference: 3.3047033631383727e-05
  Max relative difference: 1.8694225263081064e-05
  Index | Obtained            | Expected         
  0     | -1.7677669529663687 | -1.7678 ± 1.8e-06
  1     | 1.7677669529663689  | 1.7678 ± 1.8e-06
====== 1 failed, 588 passed, 13 skipped, 34 warnings in 470.39s (0:07:50) ======
ERROR conda.cli.main_run:execute(124): `conda run xvfb-run pytest -v -n 6 --dist loadfile --max-worker-restart 4 --durations=20 --cov=mslib tests` failed. (See above for error)

The test needs more digits, "pytest.approx considers the small variations due to floating-point imprecision."
Try with 7 digits again

@ReimarBauer
Copy link
Member

@saiabhinav001 Hi, have you seen my suggestions? Please have a look.

@saiabhinav001
Copy link
Author

Hi @ReimarBauer,

Thank you for your valuable feedback and suggestions.
Regarding the issue with the pytest.approx comparison, I understand that floating-point precision can sometimes lead to small mismatches in values, as seen in this case.

The updated code for the rotate_point test:

    point = [0.0, 2.5]
    angle = 45
    rotated_point = (-1.7678, 1.7678)

    # Use pytest.approx with the desired tolerance
    assert coordinate.rotate_point(point, angle) == pytest.approx(rotated_point, rel=1e-6, abs=1e-6)

Please let me know if you have any further suggestions or questions.

@saiabhinav001
Copy link
Author

Hi @ReimarBauer,

Thank you for pointing this out!

I calculated the expected rotated point (-1.7678, 1.7678) using the standard 2D rotation formula:

x' = x * cos(θ) - y * sin(θ)  
y' = x * sin(θ) + y * cos(θ)  

For the point [0.0, 2.5] rotated by 45°, the calculation is:

x' = 0 * cos(45°) - 2.5 * sin(45°) = -1.7678  
y' = 0 * sin(45°) + 2.5 * cos(45°) = 1.7678  

This matches the expected result. However, I agree that using pytest.approx with more precision would better handle floating-point discrepancies. I'll update the test accordingly.

Please let me know if you'd like any further changes!

Thanks again for your feedback.

import logging
import datetime

import numpy as np
import pytest
import pytest # type: ignore

Copy link
Member

@ReimarBauer ReimarBauer Jan 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this type: ignore is new here. What is it doing, just my interests in it. What is the reason for this?

the linter will likly want two blanks to seperate the comment

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that is not good to add it here. When we have type related issues, we won't see that now, correnct?

when we have type related problems indicated by pytest we have to review again.
whitespace findings from the linter
@ReimarBauer
Copy link
Member

the codespell news are addressed in #2599

on github we need more digital places

comparison failed. Mismatched elements: 2 / 2:

  Max absolute difference: 3.3047033631383727e-05

  Max relative difference: 1.8694225263081064e-05

  Index | Obtained            | Expected         
  0     | -1.7677669529663687 | -1.7678 ± 1.8e-06
  1     | 1.7677669529663689  | 1.7678 ± 1.8e-06
@@ -80,10 +80,12 @@ def test_normalize_angle(self):
assert coordinate.fix_angle(420) == 60

def test_rotate_point(self):
assert coordinate.rotate_point([0, 0], 0) == (0.0, 0.0)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not see why the old tests should be removed. They provide coverage for corner cases and have no issues with decimal points. The new test is a good addition, though.

@ReimarBauer
Copy link
Member

@saiabhinav001 please have a look on the request by @joernu76

Copy link
Author

@saiabhinav001 saiabhinav001 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @joernu76,
Thank you for highlighting the importance of retaining the old test cases. I have restored the original test cases alongside the new ones to ensure comprehensive coverage of corner cases. The combination of old and new tests provides a robust validation of the functionality while addressing potential edge cases.

Copy link
Author

@saiabhinav001 saiabhinav001 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @ReimarBauer,
The # type: ignore comment was unnecessary and has been removed. I also resolved other linting issues, such as duplicate imports and missing blank lines, to ensure compliance with formatting

@saiabhinav001
Copy link
Author

@saiabhinav001 please have a look on the request by @joernu76

Hi @ReimarBauer,

Thank you for pointing this out. I’ve reviewed @joernu76's feedback and addressed their concern regarding retaining the old tests.

I have retained the original test cases alongside the new ones to ensure comprehensive test coverage. This ensures that both general scenarios and edge cases are thoroughly validated.

@ReimarBauer ReimarBauer requested a review from joernu76 February 8, 2025 12:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants