-
Notifications
You must be signed in to change notification settings - Fork 103
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
base: develop
Are you sure you want to change the base?
Changes from 2 commits
058ca45
7beb9c2
066ca2d
77c51f9
4416fd5
0573718
c6b3c03
5c5258a
907fb3b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,5 @@ | ||
# -*- coding: utf-8 -*- | ||
""" | ||
|
||
tests._test_utils.test_coordinate | ||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
|
||
|
@@ -24,9 +23,9 @@ | |
See the License for the specific language governing permissions and | ||
limitations under the License. | ||
""" | ||
|
||
import logging | ||
import datetime | ||
|
||
import numpy as np | ||
import pytest | ||
|
||
|
@@ -39,140 +38,129 @@ | |
|
||
class TestGetDistance: | ||
""" | ||
tests for distance based calculations | ||
Tests for distance-based calculations in `coordinate` module. | ||
""" | ||
# we don't test the utils method here, may be that method should me refactored off | ||
|
||
def test_get_distance(self): | ||
coordinates_distance = [(50.355136, 7.566077, 50.353968, 4.577915, 212), | ||
(-5.135943, -42.792442, 4.606085, 120.028077, 18130)] | ||
for lat0, lon0, lat1, lon1, distance in coordinates_distance: | ||
assert int(coordinate.get_distance(lat0, lon0, lat1, lon1)) == distance | ||
|
||
def test_find_location(self): | ||
assert find_location(50.92, 6.36) == ([50.92, 6.36], 'Juelich') | ||
assert find_location(50.9200002, 6.36) == ([50.92, 6.36], 'Juelich') | ||
@pytest.mark.parametrize("lat0, lon0, lat1, lon1, expected_distance", [ | ||
(50.355136, 7.566077, 50.353968, 4.577915, 212), | ||
(-5.135943, -42.792442, 4.606085, 120.028077, 18130), | ||
]) | ||
def test_get_distance(self, lat0, lon0, lat1, lon1, expected_distance): | ||
""" | ||
Test the calculation of distances between coordinate pairs. | ||
""" | ||
assert int(coordinate.get_distance(lat0, lon0, lat1, lon1)) == expected_distance | ||
|
||
@pytest.mark.parametrize("lat, lon, expected_location", [ | ||
(50.92, 6.36, ([50.92, 6.36], 'Juelich')), | ||
(50.9200002, 6.36, ([50.92, 6.36], 'Juelich')), | ||
]) | ||
def test_find_location(self, lat, lon, expected_location): | ||
""" | ||
Test finding the location from coordinates. | ||
""" | ||
assert find_location(lat, lon) == expected_location | ||
|
||
|
||
class TestProjections: | ||
""" | ||
Tests for handling coordinate projections. | ||
""" | ||
|
||
def test_get_projection_params(self): | ||
""" | ||
Test fetching projection parameters for various EPSG codes. | ||
""" | ||
assert get_projection_params("epsg:4839") == {'basemap': {'epsg': '4839'}, 'bbox': 'meter(10.5,51)'} | ||
with pytest.raises(ValueError): | ||
get_projection_params('auto2:42005') | ||
with pytest.raises(ValueError): | ||
get_projection_params('auto:42001') | ||
with pytest.raises(ValueError): | ||
get_projection_params('crs:83') | ||
for invalid_code in ['auto2:42005', 'auto:42001', 'crs:83']: | ||
with pytest.raises(ValueError): | ||
get_projection_params(invalid_code) | ||
|
||
|
||
class TestAngles: | ||
""" | ||
tests about angles | ||
Tests for angle normalization and point rotation. | ||
""" | ||
|
||
def test_normalize_angle(self): | ||
assert coordinate.fix_angle(0) == 0 | ||
assert coordinate.fix_angle(180) == 180 | ||
assert coordinate.fix_angle(270) == 270 | ||
assert coordinate.fix_angle(-90) == 270 | ||
assert coordinate.fix_angle(-180) == 180 | ||
assert coordinate.fix_angle(-181) == 179 | ||
assert coordinate.fix_angle(420) == 60 | ||
|
||
def test_rotate_point(self): | ||
assert coordinate.rotate_point([0, 0], 0) == (0.0, 0.0) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. |
||
assert coordinate.rotate_point([0, 0], 180) == (0.0, 0.0) | ||
assert coordinate.rotate_point([1, 0], 0) == (1.0, 0.0) | ||
assert coordinate.rotate_point([100, 90], 90) == (-90, 100) | ||
@pytest.mark.parametrize("angle, normalized", [ | ||
(0, 0), | ||
(180, 180), | ||
(270, 270), | ||
(-90, 270), | ||
(-180, 180), | ||
(-181, 179), | ||
(420, 60), | ||
]) | ||
def test_normalize_angle(self, angle, normalized): | ||
""" | ||
Test normalizing angles to the range [0, 360). | ||
""" | ||
assert coordinate.fix_angle(angle) == normalized | ||
|
||
@pytest.mark.parametrize("point, angle, rotated_point", [ | ||
([0, 0], 0, (0.0, 0.0)), | ||
([0, 0], 180, (0.0, 0.0)), | ||
([1, 0], 0, (1.0, 0.0)), | ||
([100, 90], 90, (-90.0, 100.0)), | ||
saiabhinav001 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
([1.5, 0.5], 90, (-0.5, 1.5)), | ||
([0.0, 2.5], 45, (-1.7677669529663689, 1.7677669529663689)), | ||
]) | ||
def test_rotate_point(self, point, angle, rotated_point): | ||
""" | ||
Test rotating points around the origin. | ||
""" | ||
assert coordinate.rotate_point(point, angle) == pytest.approx(rotated_point) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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? |
||
|
||
|
||
class TestLatLonPoints: | ||
def test_linear(self): | ||
ref_lats = [0, 10] | ||
ref_lons = [0, 0] | ||
|
||
lats, lons = coordinate.latlon_points(ref_lats[0], ref_lons[0], ref_lats[1], ref_lons[1], | ||
numpoints=2, connection="linear") | ||
assert len(lats) == len(ref_lats) | ||
assert all(lats == ref_lats) | ||
assert len(lons) == len(ref_lons) | ||
assert all(lons == ref_lons) | ||
|
||
lats, lons = coordinate.latlon_points(ref_lats[0], ref_lons[0], ref_lats[1], ref_lons[1], | ||
numpoints=3, connection="linear") | ||
assert len(lats) == 3 | ||
assert len(lons) == 3 | ||
assert all(lats == [0, 5, 10]) | ||
|
||
ref_lats = [0, 0] | ||
ref_lons = [0, 10] | ||
lats, lons = coordinate.latlon_points(ref_lats[0], ref_lons[0], ref_lats[1], ref_lons[1], | ||
numpoints=3, connection="linear") | ||
assert len(lats) == 3 | ||
assert len(lons) == 3 | ||
assert all(lons == [0, 5, 10]) | ||
|
||
lats, lons = coordinate.latlon_points(ref_lats[0], ref_lons[0], ref_lats[1], ref_lons[1], | ||
numpoints=3, connection="linear") | ||
assert len(lats) == 3 | ||
assert len(lons) == 3 | ||
assert all(lons == [0, 5, 10]) | ||
|
||
def test_greatcircle(self): | ||
ref_lats = [0, 10] | ||
ref_lons = [0, 0] | ||
|
||
lats, lons = coordinate.latlon_points(ref_lats[0], ref_lons[0], ref_lats[1], ref_lons[1], | ||
numpoints=2, connection="greatcircle") | ||
assert len(lats) == len(ref_lats) | ||
assert lats == ref_lats | ||
assert len(lons) == len(ref_lons) | ||
assert lons == ref_lons | ||
""" | ||
Tests for generating lat/lon points along paths. | ||
""" | ||
|
||
@pytest.mark.parametrize("ref_lats, ref_lons, numpoints, connection, expected_lats, expected_lons", [ | ||
([0, 10], [0, 0], 2, "linear", [0, 10], [0, 0]), | ||
([0, 10], [0, 0], 3, "linear", [0, 5, 10], [0, 0, 0]), | ||
([0, 0], [0, 10], 3, "linear", [0, 0, 0], [0, 5, 10]), | ||
]) | ||
def test_linear(self, ref_lats, ref_lons, numpoints, connection, expected_lats, expected_lons): | ||
""" | ||
Test generating linear lat/lon points. | ||
""" | ||
lats, lons = coordinate.latlon_points(ref_lats[0], ref_lons[0], ref_lats[1], ref_lons[1], | ||
numpoints=3, connection="linear") | ||
assert len(lats) == 3 | ||
assert len(lons) == 3 | ||
assert all(np.asarray(lats) == [0, 5, 10]) | ||
|
||
ref_lats = [0, 0] | ||
ref_lons = [0, 10] | ||
numpoints=numpoints, connection=connection) | ||
np.testing.assert_array_equal(lats, expected_lats) | ||
np.testing.assert_array_equal(lons, expected_lons) | ||
|
||
@pytest.mark.parametrize("ref_lats, ref_lons, numpoints", [ | ||
([0, 10], [0, 0], 2), | ||
([0, 10], [0, 0], 3), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the second one had before ([0, 0], [0, 10] ,3) |
||
]) | ||
def test_greatcircle(self, ref_lats, ref_lons, numpoints): | ||
""" | ||
Test generating lat/lon points along a great circle path. | ||
""" | ||
lats, lons = coordinate.latlon_points(ref_lats[0], ref_lons[0], ref_lats[1], ref_lons[1], | ||
numpoints=3, connection="linear") | ||
assert len(lats) == 3 | ||
assert len(lons) == 3 | ||
assert all(np.asarray(lons) == [0, 5, 10]) | ||
numpoints=numpoints, connection="greatcircle") | ||
assert len(lats) == numpoints | ||
assert len(lons) == numpoints | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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]) |
||
|
||
|
||
def test_pathpoints(): | ||
""" | ||
Test generating path points with timestamps for different connections. | ||
""" | ||
lats = [0, 10] | ||
lons = [0, 10] | ||
times = [datetime.datetime(2012, 7, 1, 10, 30), | ||
datetime.datetime(2012, 7, 1, 10, 40)] | ||
ref = [lats, lons, times] | ||
result = coordinate.path_points(lats, lons, 100, times=times, connection="linear") | ||
assert all(len(_x) == 100 for _x in result) | ||
for i in range(3): | ||
assert pytest.approx(result[i][0]) == ref[i][0] | ||
assert pytest.approx(result[i][-1]) == ref[i][-1] | ||
|
||
result = coordinate.path_points(lats, lons, 100, times=times, connection="greatcircle") | ||
assert all(len(_x) == 100 for _x in result) | ||
for i in range(3): | ||
assert pytest.approx(result[i][0]) == ref[i][0] | ||
assert pytest.approx(result[i][-1]) == ref[i][-1] | ||
|
||
result = coordinate.path_points(lats, lons, 200, times=times, connection="linear") | ||
assert all(len(_x) == 200 for _x in result) | ||
for i in range(3): | ||
assert pytest.approx(result[i][0]) == ref[i][0] | ||
assert pytest.approx(result[i][-1]) == ref[i][-1] | ||
|
||
result = coordinate.path_points(lats, lons, 200, times=times, connection="greatcircle") | ||
assert all(len(_x) == 200 for _x in result) | ||
for i in range(3): | ||
assert pytest.approx(result[i][0]) == ref[i][0] | ||
assert pytest.approx(result[i][-1]) == ref[i][-1] | ||
for numpoints, connection in [(100, "linear"), (100, "greatcircle"), (200, "linear"), (200, "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): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 pytest.approx(result[i][0]) == ref[i][0] | ||
assert pytest.approx(result[i][-1]) == ref[i][-1] | ||
|
||
lats = [0, 10, -20] | ||
lons = [0, 10, 20] | ||
|
@@ -182,12 +170,6 @@ def test_pathpoints(): | |
ref = [lats, lons, times] | ||
|
||
result = coordinate.path_points(lats, lons, 100, times=times, connection="linear") | ||
assert all([len(_x) == 100 for _x in result]) | ||
for i in range(3): | ||
assert pytest.approx(result[i][0]) == ref[i][0] | ||
assert pytest.approx(result[i][-1]) == ref[i][-1] | ||
|
||
result = coordinate.path_points(lats, lons, 100, times=times, connection="greatcircle") | ||
assert all(len(_x) == 100 for _x in result) | ||
for i in range(3): | ||
assert pytest.approx(result[i][0]) == ref[i][0] | ||
|
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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?