Skip to content

Commit

Permalink
Merge pull request #628 from RocketPy-Team/tst/unit-integration-tests
Browse files Browse the repository at this point in the history
TST: Reorganizing testing modules into unit and integration tests
  • Loading branch information
Gui-FernandesBR authored Jul 4, 2024
2 parents d452664 + 9bdee7e commit d4166ac
Show file tree
Hide file tree
Showing 28 changed files with 1,325 additions and 1,273 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/test_pytest.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,12 @@ jobs:
- name: Run Unit Tests
run: pytest tests/unit --cov=rocketpy

- name: Run Integration Tests
run: pytest $(find tests -maxdepth 1 -name "*.py") --cov=rocketpy --cov-append

- name: Run Documentation Tests
run: pytest rocketpy --doctest-modules --cov=rocketpy --cov-append

- name: Run Integration Tests
run: pytest tests/integration --cov=rocketpy --cov-append

- name: Run Acceptance Tests
run: pytest tests/acceptance --cov=rocketpy --cov-append --cov-report=xml

Expand Down
59 changes: 59 additions & 0 deletions tests/integration/test_environment_analysis.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import copy
import os
from unittest.mock import patch

import matplotlib as plt
import pytest

from rocketpy.tools import import_optional_dependency

plt.rcParams.update({"figure.max_open_warning": 0})


@pytest.mark.slow
@patch("matplotlib.pyplot.show")
def test_all_info(mock_show, env_analysis):
"""Test the EnvironmentAnalysis.all_info() method, which already invokes
several other methods. It is a good way to test the whole class in a first view.
However, if it fails, it is hard to know which method is failing.
Parameters
----------
env_analysis : rocketpy.EnvironmentAnalysis
A simple object of the Environment Analysis class
Returns
-------
None
"""
assert env_analysis.info() == None
assert env_analysis.all_info() == None
assert env_analysis.plots.info() == None
os.remove("wind_rose.gif") # remove the files created by the method


@pytest.mark.slow
@patch("matplotlib.pyplot.show")
def test_exports(mock_show, env_analysis):
"""Check the export methods of the EnvironmentAnalysis class. It
only checks if the method runs without errors. It does not check if the
files are correct, as this would require a lot of work and would be
difficult to maintain.
Parameters
----------
env_analysis : EnvironmentAnalysis
A simple object of the EnvironmentAnalysis class.
"""

assert env_analysis.export_mean_profiles() == None
assert env_analysis.save("env_analysis_dict") == None

env2 = copy.deepcopy(env_analysis)
env2.load("env_analysis_dict")
assert env2.all_info() == None

# Delete file created by save method
os.remove("env_analysis_dict")
os.remove("wind_rose.gif")
os.remove("export_env_analysis.json")
File renamed without changes.
179 changes: 179 additions & 0 deletions tests/integration/test_function.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
import os
from unittest.mock import patch

import matplotlib as plt
import numpy as np
import pytest

from rocketpy import Function

plt.rcParams.update({"figure.max_open_warning": 0})


@pytest.mark.parametrize(
"func",
[
"linearly_interpolated_func",
"spline_interpolated_func",
"func_2d_from_csv",
"lambda_quad_func",
],
)
def test_savetxt(request, func):
"""Test the savetxt method of various Function objects.
This test function verifies that the `savetxt` method correctly writes the
function's data to a CSV file and that a new function object created from
this file has the same data as the original function object.
Notes
-----
The test performs the following steps:
1. It invokes the `savetxt` method of the given function object.
2. It then reads this file to create a new function object.
3. The test asserts that the data of the new function matches the original.
4. Finally, the test cleans up by removing the created CSV file.
Raises
------
AssertionError
If the `savetxt` method fails to save the file, or if the data of the
newly read function does not match the data of the original function.
"""
func = request.getfixturevalue(func)
assert (
func.savetxt(
filename="test_func.csv",
lower=0,
upper=9,
samples=10,
fmt="%.6f",
delimiter=",",
newline="\n",
encoding=None,
)
is None
), "Couldn't save the file using the Function.savetxt method."

read_func = Function(
"test_func.csv",
interpolation="linear" if func.get_domain_dim() == 1 else "shepard",
extrapolation="natural",
)
if callable(func.source):
source = np.column_stack(
(np.linspace(0, 9, 10), func.source(np.linspace(0, 9, 10)))
)
assert np.allclose(source, read_func.source)
else:
assert np.allclose(func.source, read_func.source)

# clean up the file
os.remove("test_func.csv")


# Test Function creation from .csv file
def test_function_from_csv(func_from_csv, func_2d_from_csv):
"""Test the Function class creation from a .csv file.
Parameters
----------
func_from_csv : rocketpy.Function
A Function object created from a .csv file.
func_2d_from_csv : rocketpy.Function
A Function object created from a .csv file with 2 inputs.
"""
# Assert the function is zero at 0 but with a certain tolerance
assert np.isclose(func_from_csv(0), 0.0, atol=1e-6)
assert np.isclose(func_2d_from_csv(0, 0), 0.0, atol=1e-6)
# Check the __str__ method
assert func_from_csv.__str__() == "Function from R1 to R1 : (Scalar) → (Scalar)"
assert (
func_2d_from_csv.__str__()
== "Function from R2 to R1 : (Input 1, Input 2) → (Scalar)"
)
# Check the __repr__ method
assert func_from_csv.__repr__() == "'Function from R1 to R1 : (Scalar) → (Scalar)'"
assert (
func_2d_from_csv.__repr__()
== "'Function from R2 to R1 : (Input 1, Input 2) → (Scalar)'"
)


@pytest.mark.parametrize(
"csv_file",
[
"tests/fixtures/function/1d_quotes.csv",
"tests/fixtures/function/1d_no_quotes.csv",
],
)
def test_func_from_csv_with_header(csv_file):
"""Tests if a Function can be created from a CSV file with a single header
line. It tests cases where the fields are separated by quotes and without
quotes."""
f = Function(csv_file)
assert f.__repr__() == "'Function from R1 to R1 : (time) → (value)'"
assert np.isclose(f(0), 100)
assert np.isclose(f(0) + f(1), 300), "Error summing the values of the function"


@patch("matplotlib.pyplot.show")
def test_plots(mock_show, func_from_csv, func_2d_from_csv):
"""Test different plot methods of the Function class.
Parameters
----------
mock_show : Mock
Mock of the matplotlib.pyplot.show method.
func_from_csv : rocketpy.Function
A Function object created from a .csv file.
"""
# Test plot methods
assert func_from_csv.plot() == None
assert func_2d_from_csv.plot() == None
# Test plot methods with limits
assert func_from_csv.plot(-1, 1) == None
assert func_2d_from_csv.plot(-1, 1) == None
# Test compare_plots
func2 = Function(
source="tests/fixtures/airfoils/e473-10e6-degrees.csv",
inputs=["Scalar"],
outputs=["Scalar"],
interpolation="linear",
extrapolation="natural",
)
assert (
func_from_csv.compare_plots([func_from_csv, func2], return_object=False) == None
)


@patch("matplotlib.pyplot.show")
def test_multivariable_dataset_plot(mock_show):
"""Test the plot method of the Function class with a multivariable dataset."""
# Test plane f(x,y) = x - y
source = [
(-1, -1, -1),
(-1, 0, -1),
(-1, 1, -2),
(0, 1, 1),
(0, 0, 0),
(0, 1, -1),
(1, -1, 2),
(1, 0, 1),
(1, 1, 0),
]
func = Function(source=source, inputs=["x", "y"], outputs=["z"])

# Assert plot
assert func.plot() == None


@patch("matplotlib.pyplot.show")
def test_multivariable_function_plot(mock_show):
"""Test the plot method of the Function class with a multivariable function."""
# Test plane f(x,y) = sin(x + y)
source = lambda x, y: np.sin(x * y)
func = Function(source=source, inputs=["x", "y"], outputs=["z"])

# Assert plot
assert func.plot() == None
31 changes: 31 additions & 0 deletions tests/integration/test_genericmotor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
from unittest.mock import patch

import numpy as np
import pytest
import scipy.integrate

burn_time = (2, 7)
thrust_source = lambda t: 2000 - 100 * (t - 2)
chamber_height = 0.5
chamber_radius = 0.075
chamber_position = -0.25
propellant_initial_mass = 5.0
nozzle_position = -0.5
nozzle_radius = 0.075
dry_mass = 8.0
dry_inertia = (0.2, 0.2, 0.08)


@patch("matplotlib.pyplot.show")
def test_generic_motor_info(mock_show, generic_motor):
"""Tests the GenericMotor.all_info() method.
Parameters
----------
mock_show : mock
Mock of the matplotlib.pyplot.show function.
generic_motor : rocketpy.GenericMotor
The GenericMotor object to be used in the tests.
"""
assert generic_motor.info() == None
assert generic_motor.all_info() == None
34 changes: 34 additions & 0 deletions tests/integration/test_hybridmotor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
from unittest.mock import patch

import numpy as np

thrust_function = lambda t: 2000 - 100 * t
burn_time = 10
center_of_dry_mass = 0
dry_inertia = (4, 4, 0.1)
dry_mass = 8
grain_density = 1700
grain_number = 4
grain_initial_height = 0.1
grain_separation = 0
grain_initial_inner_radius = 0.04
grain_outer_radius = 0.1
nozzle_position = -0.4
nozzle_radius = 0.07
grains_center_of_mass_position = -0.1
oxidizer_tank_position = 0.3


@patch("matplotlib.pyplot.show")
def test_hybrid_motor_info(mock_show, hybrid_motor):
"""Tests the HybridMotor.all_info() method.
Parameters
----------
mock_show : mock
Mock of the matplotlib.pyplot.show function.
hybrid_motor : rocketpy.HybridMotor
The HybridMotor object to be used in the tests.
"""
assert hybrid_motor.info() == None
assert hybrid_motor.all_info() == None
32 changes: 32 additions & 0 deletions tests/integration/test_liquidmotor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
from unittest.mock import patch

import numpy as np
import pytest
import scipy.integrate

from rocketpy import Function

burn_time = (8, 20)
dry_mass = 10
dry_inertia = (5, 5, 0.2)
center_of_dry_mass = 0
nozzle_position = -1.364
nozzle_radius = 0.069 / 2
pressurant_tank_position = 2.007
fuel_tank_position = -1.048
oxidizer_tank_position = 0.711


@patch("matplotlib.pyplot.show")
def test_liquid_motor_info(mock_show, liquid_motor):
"""Tests the LiquidMotor.all_info() method.
Parameters
----------
mock_show : mock
Mock of the matplotlib.pyplot.show function.
liquid_motor : rocketpy.LiquidMotor
The LiquidMotor object to be used in the tests.
"""
assert liquid_motor.info() == None
assert liquid_motor.all_info() == None
Loading

0 comments on commit d4166ac

Please sign in to comment.