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

unittests for python API #249

Merged
merged 13 commits into from
Jun 21, 2018
Merged

unittests for python API #249

merged 13 commits into from
Jun 21, 2018

Conversation

CFGrote
Copy link
Contributor

@CFGrote CFGrote commented Jun 12, 2018

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, use tests/python/unittest/API/APITest.py as a template and don't forget to import new test modules Test.py and to add the corresponding test suite. I'll be happy to assist.

@ax3l
Copy link
Member

ax3l commented Jun 12, 2018

@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.?

ax3l
ax3l previously requested changes Jun 12, 2018
Copy link
Member

@ax3l ax3l left a 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.

@@ -174,6 +174,10 @@ class Mesh : public BaseRecord< MeshRecordComponent >
template< typename T >
Mesh& setTimeOffset(T timeOffset);

/** Access to records.
*/
Container< MeshRecordComponent > records;
Copy link
Member

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

@@ -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)
Copy link
Member

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

@@ -29,6 +29,9 @@
namespace py = pybind11;
using namespace openPMD;

using PyRecordsContainer = Container< MeshRecordComponent >;
Copy link
Member

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


### FIXME
## Get a record (fails)
#record = self.__series.iterations[100].particles['electrons'].properties['positions'].components['x']
Copy link
Member

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']


# TODO: add __getitem__ to openPMD.Mesh and openPMD.Particle object for
# non-scalar records: return a record component
# E_x = i.meshes["E"]["x"]
Copy link
Member

Choose a reason for hiding this comment

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

@CFGrote
Copy link
Contributor Author

CFGrote commented Jun 12, 2018

@ax3l can you add to .travis.yml that these test actrually run ? sth. like python test/unittest/Test.py -v should do

@ax3l
Copy link
Member

ax3l commented Jun 12, 2018

Just as a note, I am right now fixing a CI issue with HDF5. Please rebase your branch against dev as soon as we merged #250

Yes, I can push on your branch to address CMake and CI related features in the end :-)

@ax3l ax3l dismissed their stale review June 12, 2018 21:27

addressed

@ax3l
Copy link
Member

ax3l commented Jun 12, 2018

@CFGrote CI fixed, please fetch and rebase :-)
edit: never mind, my manual restart fixes this as well and merged onto the new dev.

Copy link
Member

@ax3l ax3l left a 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 :-)

self.assertRaises(TypeError, openPMD.Record)
# ### FIXME
# ## Get a record (fails)
# #record = self.__series.iterations[100].particles[
Copy link
Member

@ax3l ax3l Jun 12, 2018

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?

Copy link
Contributor Author

@CFGrote CFGrote Jun 13, 2018

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

Copy link
Member

@ax3l ax3l Jun 19, 2018

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']


# TODO: add __getitem__ to openPMD.Mesh and openPMD.Particle object for
# non-scalar records: return a record component
# E_x = i.meshes["E"]["x"]
Copy link
Member

Choose a reason for hiding this comment

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ok, works. test added

self.assertIsInstance(ps, str)
self.assertIsInstance(i.particles[ps], openPMD.ParticleSpecies)

def testAllocation(self):
Copy link
Member

@C0nsultant C0nsultant Jun 13, 2018

Choose a reason for hiding this comment

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

This can be removed together with the C++ code and bindings behind it. We do not allow the user to specify allocation strategy any more.
If you include this, I'll remove it when we get rid of the enum with #253.

Copy link
Contributor Author

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 .

@@ -0,0 +1,28 @@
""" :module: Top level test suite """
Copy link
Member

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 :)

Copy link
Contributor Author

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 """

Copy link
Member

Choose a reason for hiding this comment

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

please here as well

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

@ax3l
Copy link
Member

ax3l commented Jun 13, 2018

@CFGrote I think the rebase includes a little too many commits now. Just rebase on mainline's dev (fetch first) and remove the ones not coming from you on top :-)

@ax3l
Copy link
Member

ax3l commented Jun 20, 2018

@CFGrote can I help somehow to finish this PR? :-)

@CFGrote
Copy link
Contributor Author

CFGrote commented Jun 20, 2018

i fear i f***ed up my repo. @ax3l, can you solve the conflict from your end?

@ax3l
Copy link
Member

ax3l commented Jun 20, 2018

@CFGrote shall I fix the PEP issues and add CI?
(that means I will directly push on your PR branch, so don't forget to fetch & pull later on. Also, I think you forgot to branch before the PR ;-) )

@ax3l ax3l assigned C0nsultant and unassigned ax3l Jun 20, 2018
@ax3l
Copy link
Member

ax3l commented Jun 20, 2018

@C0nsultant feel free to review & merge this if all passes :)

Copy link
Member

@C0nsultant C0nsultant left a 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.

"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")
Copy link
Member

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).

series = self.__field_series

print("Read a Series with openPMD standard version %s" %
series.openPMD)
Copy link
Member

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.


print("The Series contains {0} iterations:".format(
len(series.iterations)))
for i in series.iterations:
Copy link
Member

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.

Copy link
Member

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.

Copy link
Member

@ax3l ax3l Jun 21, 2018

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 :)


self.assertEqual(len(series.iterations), 1)

i = series.iterations[400]
Copy link
Member

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.

Copy link
Member

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.

self.assertEqual(len(series.iterations), 1)

i = series.iterations[400]
print("Iteration 100 contains {0} meshes:".format(len(i.meshes)))
Copy link
Member

Choose a reason for hiding this comment

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

Iteration number is hardcoded.


iteration = self.__particle_series.iterations[400]

copy_iteration = openPMD.Iteration(iteration)
Copy link
Member

Choose a reason for hiding this comment

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

@ax3l does this return deep copies or are these shallow copies as intended with #144?
Assuming it is a shallow copy, we should check if

  • the contained elements and attributes are the same and
  • reflect modification in the other handle

(as well as in all other objects with this behaviour).

Copy link
Member

@ax3l ax3l Jun 21, 2018

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.

Copy link
Member

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


def testIteration_Container(self):
""" Test openPMD.Iteration_Container. """
obj = openPMD.Iteration_Container()
Copy link
Member

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):
Copy link
Member

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_Components.

@return : The absolute path to ../TestFiles/<file_name> .
"""

test_files_dir = path.join('../samples/', file_name)
Copy link
Member

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
Copy link
Member

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.

@ax3l
Copy link
Member

ax3l commented Jun 21, 2018

@C0nsultant I tried to address a whole bunch of review comments, but could not yet fix all.
print()s are removed, paths fixed and more verifies added. If you like, we could merge the current state and improve it ourselves further?

Address a whole bunch, yet not all review comments.
import numpy as np
found_numpy = True
except ImportError:
found_numpy = False
Copy link
Member

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. 🙂

Copy link
Member

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")
Copy link
Member

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
Copy link
Member

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.

@C0nsultant C0nsultant merged commit 275f3fb into openPMD:dev Jun 21, 2018
@C0nsultant
Copy link
Member

Thank you very much, @CFGrote!

@ax3l ax3l mentioned this pull request Aug 29, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants