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

Heron unit testing #20

Merged
merged 11 commits into from
Jul 29, 2024

Conversation

caleb-sitton-inl
Copy link
Collaborator


Pull Request Description

What issue does this change request address? (Use "#" before the issue to link it, i.e., #42.)

Not Applicable

What are the significant changes in functionality due to this change request?

This implements a complete set of nine HERON unit tests. Specifically, the tests focus on the src/heron/ create_componentsets_in_HERON() function. The tests/unit_tests/test_heron.py file contains the tests, which use the unittest module to run. This file can be run directly and all unit tests will run. This set of tests is designed to cover the vast majority of potential errors in the function without the length that would be required for a strictly comprehensive test suite.

The tests/unit_tests/tests file contains the necessary content for the unit tests to be run through rook by running FORCE/run_tests. Rook does not have a significant role in the testing itself; each test is run as a "generic executable" using the unittest module, which reports back a failure, success, or error. Output is handled by, and thus formatted by, rook. An advantage to this system is that rook will run both unit and integration tests with run_tests.

The tests/unit_tests/README.md file contains a description of what each individual unit test is intended to check.


For Change Control Board: Change Request Review

The following review must be completed by an authorized member of the Change Control Board.

  • 1. Review all computer code.
  • 2. Make sure the Python code and commenting standards are respected (camelBack, etc.) - See on the wiki for details.
  • 3. Automated Tests should pass, including run_tests, pylint, and manual building tests.
  • 4. If significant functionality is added, there must be tests added to check this. Tests should cover all possible options. Multiple short tests are preferred over one large test.
  • 5. If the change modifies or adds a requirement or a requirement based test case, the Change Control Board's Chair or designee also needs to approve the change. The requirements and the requirements test shall be in sync.
  • 6. The merge request must reference an issue. If the issue is closed, the issue close checklist shall be done.

@GabrielSoto-INL GabrielSoto-INL added the Do Not Merge This is not ready to be merged label Jun 26, 2024
@caleb-sitton-inl caleb-sitton-inl removed the Do Not Merge This is not ready to be merged label Jul 19, 2024
@caleb-sitton-inl caleb-sitton-inl marked this pull request as ready for review July 19, 2024 14:31
@GabrielSoto-INL GabrielSoto-INL self-requested a review July 22, 2024 13:58
Copy link
Collaborator

@GabrielSoto-INL GabrielSoto-INL left a comment

Choose a reason for hiding this comment

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

Great work, VERY thorough addition to the FORCE testing suite! I have added a couple of comments and suggestions to improve the code so far, and had just a couple of questions.

I think overall, would try to consolidate some repeated lines of code wherever possible.

self.assertIn('type', cashflows[0].keys())
self.assertIn('taxable', cashflows[0].keys())
self.assertIn('inflation', cashflows[0].keys())
self.assertIn('mult_target', cashflows[0].keys())
Copy link
Collaborator

Choose a reason for hiding this comment

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

this is actually a deprecated attribute, so it should be ignored

self.assertIn('mult_target', cashflows[0].keys())

# Verify reference price
ref_price_value = cashflows[0].find('./reference_price/fixed_value')
Copy link
Collaborator

Choose a reason for hiding this comment

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

should this check first that reference_price exists? same for reference_driver a couple of lines down

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This should be addressed by the new methods being added. In either case, find() returns None if no matches are found; thus, the error is caught, albeit without a great deal of clarity as to the cause of the issue in this version.

tests/unit_tests/test_heron.py Show resolved Hide resolved
tests/unit_tests/test_heron.py Show resolved Hide resolved
result_tree = create_componentsets_in_HERON("/fake/folder", "/fake/heron_input.xml")

# Verify Case node was transferred
with (self.subTest("Case node has been corrupted")):
Copy link
Collaborator

Choose a reason for hiding this comment

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

is there a reason why the self.subTest( ... ) call is wrapped in extra parentheses? seems like this is not the case later in the file

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

No, there is no reason for this. The extra parentheses have been removed

self.assertEqual(len(cf_contents), 4)

# Reference driver
ref_driver = cashflow[0].findall('./reference_driver')
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm seeing some repetition of testing here, I wonder if it would be better to have some sort of method written which does these checks (maybe with the text value as an input) at the top of this file?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I think that could be really helpful for simplifying the tests and also improving the clarity of the results. I'll set up some methods/functions for grouping these frequently used parse-and-assert combinations.


# Verify ProjectTime was not corrupted
with self.subTest("Non-cashflow child node of economics has been corrupted"):
proj_time = economics[0].findall('./ProjectTime')
Copy link
Collaborator

Choose a reason for hiding this comment

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

this should also be lifetime

<reference_price>
<ROM rom_stuff="stuff">more_rom_stuff</ROM>
</reference_price>
<scaling_factor_x>
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think it's good to check these mixed-ValuedParam cases. From what I'm seeing here, every child node here gets replaced with a value from the mock_open callable below. Now this might be a question of ignorance, but: would it be possible to leave one of these parameters unchanged? By that I mean, for example, that the end goal for Component 2 is to replace the reference_driver, the reference_price, and the driver with values read in from Line 246-248, BUT we wanted to keep the scaling_factor_x as an uncertainty. Is that possible to test for/a current capability of the method?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The reason I chose not to modify the set of ValuedParams is that, in the current functionality of the src/heron.py script, all the ValuedParam inputs from ASPEN HYSYS and APEA are required. The src/heron.py script currently trusts that the ASPEN HYSYS and APEA inputs have been provided, so it does not reliably throw an error when one is missing. Thus, neither a functional nor an erroneous response to the omission of some of the ValuedParams can be tested.

mock_listdir.return_value = []

# Call the function
result_tree = create_componentsets_in_HERON("/fake/folder", "/fake/heron_input.xml")
Copy link
Collaborator

Choose a reason for hiding this comment

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

this might be out of the scope of this PR, but should this scenario cause an error or at least a warning?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This is a question whose answer could depend on some broader strategic goals for how this function will be used. A warning does sound like a safe middle-of-the-road option though. No matter the solution, I think it would be more appropriately addressed in a separate issue and PR. If functionality changes significantly, this test can also be updated then.

# Verify open function was called on correct files
for file in files_list:
# if file should have been opened
if file in ['componentSet.json', 'componentSetStuff.txt']:
Copy link
Collaborator

Choose a reason for hiding this comment

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

might be good to have a list before the for loop which has the acceptable filenames (e.g., acceptable_files) then refer to it in this line. That way it is easier to modify should we ever need to

Copy link
Collaborator

@GabrielSoto-INL GabrielSoto-INL left a comment

Choose a reason for hiding this comment

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

Sorry, one more request: could you add docstrings to the new methods at the top of the HERONTestCase class? otherwise, looks good!

class TestMinimalInput(unittest.TestCase):
class HERONTestCase(unittest.TestCase):

def check_reference_price(self, cashflow, correct_value, correct_content_length=1):
Copy link
Collaborator

Choose a reason for hiding this comment

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

would you be able to add docstrings to these new methods? For reference: https://github.com/idaholab/raven/wiki/RAVEN-Code-Standards#function-and-class-documentation-strings

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

No problem.

tests/unit_tests/test_heron.py Show resolved Hide resolved
with self.subTest("Non-capex CashFlow node was corrupted"):
cashflow_non_capex = economics.findall('./CashFlow[@name="other"]')
self.assertEqual(len(cashflow_non_capex), 1)
cf_noncap_contents = [e for e in cashflow_non_capex[0].findall('./')
Copy link
Collaborator

Choose a reason for hiding this comment

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

@caleb-sitton-inl seems like it's best to keep as is, but also it might be a good idea to start an issue for Feature Request regarding switching to dynamic TreeStructure elements rather than ET

Copy link
Collaborator

@GabrielSoto-INL GabrielSoto-INL left a comment

Choose a reason for hiding this comment

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

Changes LGTM, great work!

@GabrielSoto-INL GabrielSoto-INL merged commit 6e1af55 into idaholab:main Jul 29, 2024
1 check passed
@caleb-sitton-inl caleb-sitton-inl deleted the heron-unit-testing branch July 29, 2024 20:00
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