-
Notifications
You must be signed in to change notification settings - Fork 51
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
unittests for python API #249
Conversation
@CFGrote hurray, thank you so much for the PR, Carsten! Can you please add a PR description? Especially, can you highlight what dependencies are needed for the tests, e.g. are they part of the python standard libraries or external? How to call the tests manually, how to add more tests (e.g. in separate files?), etc.? |
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.
Very nice draft!
I added some inline comments on already implemented parts that can be used.
include/openPMD/Mesh.hpp
Outdated
@@ -174,6 +174,10 @@ class Mesh : public BaseRecord< MeshRecordComponent > | |||
template< typename T > | |||
Mesh& setTimeOffset(T timeOffset); | |||
|
|||
/** Access to records. | |||
*/ | |||
Container< MeshRecordComponent > records; |
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.
Please revert the addition in this file since it's a bit outdated.
Records and record components are accessed like this instead:
https://github.com/openPMD/openPMD-api/blob/0.2.0-alpha/examples/2_read_serial.py#L34-L44
src/binding/python/Mesh.cpp
Outdated
@@ -63,6 +66,7 @@ void init_Mesh(py::module &m) { | |||
.def_property_readonly("time_offset", &Mesh::timeOffset<double>) | |||
.def_property_readonly("time_offset", &Mesh::timeOffset<long double>) | |||
|
|||
.def_readwrite("records", &Mesh::records) |
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.
same here, this should not be needed to be exposed
src/binding/python/Mesh.cpp
Outdated
@@ -29,6 +29,9 @@ | |||
namespace py = pybind11; | |||
using namespace openPMD; | |||
|
|||
using PyRecordsContainer = Container< MeshRecordComponent >; |
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.
same here, this should not be needed
test/python/unittest/API/APITest.py
Outdated
|
||
### FIXME | ||
## Get a record (fails) | ||
#record = self.__series.iterations[100].particles['electrons'].properties['positions'].components['x'] |
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.
Should be accessible via:
record = self.__series.iterations[100].particles['electrons']['positions']['x']
test/python/unittest/API/APITest.py
Outdated
|
||
# TODO: add __getitem__ to openPMD.Mesh and openPMD.Particle object for | ||
# non-scalar records: return a record component | ||
# E_x = i.meshes["E"]["x"] |
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.
you can do that now, see the example here :-)
https://github.com/openPMD/openPMD-api/blob/0.2.0-alpha/examples/2_read_serial.py#L34-L44
@ax3l can you add to .travis.yml that these test actrually run ? sth. like python test/unittest/Test.py -v should do |
Just as a note, I am right now fixing a CI issue with HDF5. Please rebase your branch against Yes, I can push on your branch to address CMake and CI related features in the end :-) |
@CFGrote CI fixed, please fetch and rebase :-) |
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.
Forgotten inline comments re-added :-)
test/python/unittest/API/APITest.py
Outdated
self.assertRaises(TypeError, openPMD.Record) | ||
# ### FIXME | ||
# ## Get a record (fails) | ||
# #record = self.__series.iterations[100].particles[ |
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.
can you try if
record = self.__series.iterations[100].particles['electrons']['position']['x']
works?
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.
ERROR: testRecord (__main__.APITest)
Test openPMD.Record.
----------------------------------------------------------------------
Traceback (most recent call last):
File "APITest.py", line 293, in testRecord
record = self.__series.iterations[100].particles['electrons']['positions']['x']
TypeError: 'openPMD.ParticleSpecies' object is not subscriptable
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.
Ah, alright. Not implemented yet, a FIXME
note is great.
If you rebase against the latest dev
, the particle access should work up to:
self.__series.iterations[100].particles['electrons']['position']
and still lacks access to the record component ['x']
test/python/unittest/API/APITest.py
Outdated
|
||
# TODO: add __getitem__ to openPMD.Mesh and openPMD.Particle object for | ||
# non-scalar records: return a record component | ||
# E_x = i.meshes["E"]["x"] |
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.
works now, please see example in https://github.com/openPMD/openPMD-api/blob/0.2.0-alpha/examples/2_read_serial.py#L34-L47
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.
ok, works. test added
test/python/unittest/API/APITest.py
Outdated
self.assertIsInstance(ps, str) | ||
self.assertIsInstance(i.particles[ps], openPMD.ParticleSpecies) | ||
|
||
def testAllocation(self): |
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.
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'll leave it in but modify: now asserts AttributeError on openPMD.Allocation .
test/python/unittest/Test.py
Outdated
@@ -0,0 +1,28 @@ | |||
""" :module: Top level test suite """ |
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.
please add the same copyright line as in the other file :)
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.
done
@@ -0,0 +1,24 @@ | |||
""" :module: Test Utilities """ | |||
|
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.
please here as well
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.
done
@CFGrote I think the rebase includes a little too many commits now. Just rebase on mainline's |
@CFGrote can I help somehow to finish this PR? :-) |
i fear i f***ed up my repo. @ax3l, can you solve the conflict from your end? |
@CFGrote shall I fix the PEP issues and add CI? |
@C0nsultant feel free to review & merge this if all passes :) |
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 noted a lot of stuff. Do not despair, you don't need to do all of it.
The annotations are there so any leftovers can be done in a follow-up.
The iterationFormat regex and the print()
noise are a blocker, though.
test/python/unittest/API/APITest.py
Outdated
"issue-sample/no_particles/data00000400.h5") | ||
path_to_particle_data = generateTestFilePath( | ||
"issue-sample/no_fields/data00000400.h5") | ||
path_to_data = generateTestFilePath("git-sample/data00000100.h5") |
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.
To suppress all warnings
Series constructor called with explicit iteration suggests loading a single file with groupBased iteration encoding. Loaded file is fileBased.
plase replace the explicit iteration numbers in these filenames (i.e. 00000400
and 00000100
) with the iterationFormat regex %T
.
This will still load your desired iterations (additionally to all other iterations in that Series).
test/python/unittest/API/APITest.py
Outdated
series = self.__field_series | ||
|
||
print("Read a Series with openPMD standard version %s" % | ||
series.openPMD) |
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.
If the print()
statements are used for file inspection, replace them with assertions where possible.
Reflecting the logic from the Python examples may be helpful during development of test cases, but only adds noise when running the tests.
test/python/unittest/API/APITest.py
Outdated
|
||
print("The Series contains {0} iterations:".format( | ||
len(series.iterations))) | ||
for i in series.iterations: |
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 is a good candidate for a testContainer
case where you check if
iter(series.iterations)
returns a sane result.
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.
In that testcase, we should also check what happens when you access invalid keys.
series.iterations['definitely not an int']
In C++, this is caught by the compiler. I'm not sure about the result in Python.
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.
In python I throw on that as soon as the series is read_only. That's also the place where it originally triggered me that we need an asset in C++ as well :)
test/python/unittest/API/APITest.py
Outdated
|
||
self.assertEqual(len(series.iterations), 1) | ||
|
||
i = series.iterations[400] |
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.
Before accessing with []
, you should assert that Iteration 400 is actually contained. This API creates objects (with sane openPMD defaults) for missing keys instead of raising KeyError
.
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 should be fine as the Series is opened with openPMD.Access_Type.read_only
.
test/python/unittest/API/APITest.py
Outdated
self.assertEqual(len(series.iterations), 1) | ||
|
||
i = series.iterations[400] | ||
print("Iteration 100 contains {0} meshes:".format(len(i.meshes))) |
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.
Iteration number is hardcoded.
test/python/unittest/API/APITest.py
Outdated
|
||
iteration = self.__particle_series.iterations[400] | ||
|
||
copy_iteration = openPMD.Iteration(iteration) |
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.
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 have to check, I hope it's a reference in the first access and a shallow-copy / alias in the second case.
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.
That's not an ideal place to check, here it uses a readonly series which means I can't modify the iteration for proper inspection :S
test/python/unittest/API/APITest.py
Outdated
|
||
def testIteration_Container(self): | ||
""" Test openPMD.Iteration_Container. """ | ||
obj = openPMD.Iteration_Container() |
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 is a mis-design in the C++ API. Container
should not have a publicly available constructor. #280
""" Test openPMD.Record_Component. """ | ||
self.assertRaises(TypeError, openPMD.Record_Component) | ||
|
||
def testFieldRecord(self): |
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.
A Mesh
is a Record
by design. What this actually tests are Mesh_Record_Component
s.
@return : The absolute path to ../TestFiles/<file_name> . | ||
""" | ||
|
||
test_files_dir = path.join('../samples/', file_name) |
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.
Hard-coded directory separators defeat the purpose of using os.path.join
.
License: LGPLv3+ | ||
""" | ||
|
||
import openPMD |
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 should be moved to the third-party imports.
@C0nsultant I tried to address a whole bunch of review comments, but could not yet fix all. |
Address a whole bunch, yet not all review comments.
import numpy as np | ||
found_numpy = True | ||
except ImportError: | ||
found_numpy = False |
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.
Ah yes, a simple runtime try
-except
to turn off features instead of CMake magic with #ifdef
conditional compilation. 🙂
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.
What could possibly go wrong... ;)
"issue-sample/no_particles/data%T.h5") | ||
path_to_particle_data = generateTestFilePath( | ||
"issue-sample/no_fields/data%T.h5") | ||
path_to_data = generateTestFilePath("git-sample/data%T.h5") |
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.
Ideally, these paths would be created with os.path.join
as well.
""" Testing serial IO on a pure particle dataset. """ | ||
|
||
# Get reference to series stored on test case. | ||
series = self.__field_series |
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 does not use the pure particle dataset referred to in the docstring.
Thank you very much, @CFGrote! |
Related to #32
This PR introduces unittesting of the python wrappers. Only new dependency is the unittest library which comes with the standard python libraries. Some tests use the sample files that have to be pulled with the .travis/download_samples.sh script.
To run the tests, do
python test/python/unittest/Test.py -v
. To add more tests, usetests/python/unittest/API/APITest.py
as a template and don't forget to import new test modulesTest.py
and to add the corresponding test suite. I'll be happy to assist.