From 058ca4567b070fd6cfa02c4bcc104705f1f2e200 Mon Sep 17 00:00:00 2001 From: Sadineni Sai Abhinav Patel Date: Mon, 6 Jan 2025 12:49:18 +0530 Subject: [PATCH 1/3] Add comprehensive test suite for `mslib.utils.coordinate` module 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. --- tests/_test_utils/test_coordinate.py | 207 ++++++++++++--------------- 1 file changed, 94 insertions(+), 113 deletions(-) diff --git a/tests/_test_utils/test_coordinate.py b/tests/_test_utils/test_coordinate.py index 818b783f1..d5e874af2 100644 --- a/tests/_test_utils/test_coordinate.py +++ b/tests/_test_utils/test_coordinate.py @@ -24,9 +24,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 +39,127 @@ 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) - 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)), + ]) + def test_rotate_point(self, point, angle, rotated_point): + """ + Test rotating points around the origin. + """ + assert coordinate.rotate_point(point, angle) == rotated_point 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), + ]) + 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 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): + 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 +169,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] From 7beb9c2cd1eefa27699caf17e34708ddbfeb7e64 Mon Sep 17 00:00:00 2001 From: Sadineni Sai Abhinav Patel Date: Wed, 8 Jan 2025 21:27:50 +0530 Subject: [PATCH 2/3] Update test_coordinate.py --- tests/_test_utils/test_coordinate.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/_test_utils/test_coordinate.py b/tests/_test_utils/test_coordinate.py index d5e874af2..6f9b284d9 100644 --- a/tests/_test_utils/test_coordinate.py +++ b/tests/_test_utils/test_coordinate.py @@ -1,6 +1,5 @@ # -*- coding: utf-8 -*- """ - tests._test_utils.test_coordinate ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -103,12 +102,14 @@ def test_normalize_angle(self, angle, normalized): ([0, 0], 180, (0.0, 0.0)), ([1, 0], 0, (1.0, 0.0)), ([100, 90], 90, (-90.0, 100.0)), + ([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) == rotated_point + assert coordinate.rotate_point(point, angle) == pytest.approx(rotated_point) class TestLatLonPoints: From 066ca2db4957e1e27a1097a820f3ae6d535ab683 Mon Sep 17 00:00:00 2001 From: saiabhinav001 Date: Fri, 10 Jan 2025 15:01:22 +0530 Subject: [PATCH 3/3] Update test_coordinate.py --- tests/_test_utils/test_coordinate.py | 57 +++++++++++----------------- 1 file changed, 22 insertions(+), 35 deletions(-) diff --git a/tests/_test_utils/test_coordinate.py b/tests/_test_utils/test_coordinate.py index 6f9b284d9..4a9b67280 100644 --- a/tests/_test_utils/test_coordinate.py +++ b/tests/_test_utils/test_coordinate.py @@ -3,7 +3,7 @@ tests._test_utils.test_coordinate ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - This module provides pytest functions to tests mslib.utils.coordinate + This module provides pytest functions to test mslib.utils.coordinate This file is part of MSS. @@ -24,12 +24,11 @@ limitations under the License. """ -import logging import datetime import numpy as np import pytest - -import mslib.utils.coordinate as coordinate +import logging +from mslib.utils import coordinate from mslib.utils.find_location import find_location from mslib.utils.get_projection_params import get_projection_params @@ -38,7 +37,7 @@ class TestGetDistance: """ - Tests for distance-based calculations in `coordinate` module. + Tests for distance-based calculations in the `coordinate` module. """ @pytest.mark.parametrize("lat0, lon0, lat1, lon1, expected_distance", [ @@ -49,7 +48,8 @@ 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 + distance = coordinate.get_distance(lat0, lon0, lat1, lon1) + assert int(distance) == expected_distance @pytest.mark.parametrize("lat, lon, expected_location", [ (50.92, 6.36, ([50.92, 6.36], 'Juelich')), @@ -83,13 +83,7 @@ class TestAngles: """ @pytest.mark.parametrize("angle, normalized", [ - (0, 0), - (180, 180), - (270, 270), - (-90, 270), - (-180, 180), - (-181, 179), - (420, 60), + (0, 0), (180, 180), (270, 270), (-90, 270), (-180, 180), (-181, 179), (420, 60), ]) def test_normalize_angle(self, angle, normalized): """ @@ -102,8 +96,8 @@ def test_normalize_angle(self, angle, normalized): ([0, 0], 180, (0.0, 0.0)), ([1, 0], 0, (1.0, 0.0)), ([100, 90], 90, (-90.0, 100.0)), - ([1.5, 0.5], 90, (-0.5, 1.5)), - ([0.0, 2.5], 45, (-1.7677669529663689, 1.7677669529663689)), + ([1.5, 0.5], 90, (-0.5, 1.5)), + ([0.0, 2.5], 45, (-1.7678, 1.7678)), ]) def test_rotate_point(self, point, angle, rotated_point): """ @@ -126,8 +120,10 @@ def test_linear(self, ref_lats, ref_lons, numpoints, connection, expected_lats, """ Test generating linear lat/lon points. """ - lats, lons = coordinate.latlon_points(ref_lats[0], ref_lons[0], ref_lats[1], ref_lons[1], - numpoints=numpoints, connection=connection) + lats, lons = coordinate.latlon_points( + ref_lats[0], ref_lons[0], ref_lats[1], ref_lons[1], + numpoints=numpoints, connection=connection + ) np.testing.assert_array_equal(lats, expected_lats) np.testing.assert_array_equal(lons, expected_lons) @@ -139,8 +135,10 @@ 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=numpoints, connection="greatcircle") + lats, lons = coordinate.latlon_points( + ref_lats[0], ref_lons[0], ref_lats[1], ref_lons[1], + numpoints=numpoints, connection="greatcircle" + ) assert len(lats) == numpoints assert len(lons) == numpoints @@ -151,26 +149,15 @@ def test_pathpoints(): """ lats = [0, 10] lons = [0, 10] - times = [datetime.datetime(2012, 7, 1, 10, 30), - datetime.datetime(2012, 7, 1, 10, 40)] + times = [ + datetime.datetime(2012, 7, 1, 10, 30), + datetime.datetime(2012, 7, 1, 10, 40), + ] ref = [lats, lons, times] - for numpoints, connection in [(100, "linear"), (100, "greatcircle"), (200, "linear"), (200, "greatcircle")]: + 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): 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] - times = [datetime.datetime(2012, 7, 1, 10, 30), - datetime.datetime(2012, 7, 1, 10, 40), - datetime.datetime(2012, 7, 1, 10, 50)] - 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]