Skip to content

WIP: Add hypothesis testing for Time precision#9532

Closed
aarchiba wants to merge 9 commits into
astropy:masterfrom
aarchiba:time-hypothesis
Closed

WIP: Add hypothesis testing for Time precision#9532
aarchiba wants to merge 9 commits into
astropy:masterfrom
aarchiba:time-hypothesis

Conversation

@aarchiba

@aarchiba aarchiba commented Nov 5, 2019

Copy link
Copy Markdown
Contributor

Description

This pull request is to ensure that astropy.time is thoroughly tested and reliably maintains sub-nanosecond precision. It uses the testing package hypothesis to identify difficult corner cases.

@aarchiba

aarchiba commented Nov 5, 2019

Copy link
Copy Markdown
Contributor Author

Still need to figure out how to add a testing dependency to astropy.

@Zac-HD

Zac-HD commented Nov 6, 2019

Copy link
Copy Markdown
Contributor

Still need to figure out how to add a testing dependency to astropy.

On a temporary basis I got it working for #9211; longer term the Decemeber planning meeting will be considering adding Hypothesis as a dependency of pytest-astropy which would make it pretty easy 😄

@aarchiba

aarchiba commented Nov 6, 2019

Copy link
Copy Markdown
Contributor Author

Still need to figure out how to add a testing dependency to astropy.

Still need to figure out how to add a testing dependency to astropy.

On a temporary basis I got it working for #9211; longer term the Decemeber planning meeting will be considering adding Hypothesis as a dependency of pytest-astropy which would make it pretty easy smile

Yes, we had some discussion on Slack and I basically copied your changes from #9211 (including workarounds for astropy's wonky handling of dependencies in CI).

@aarchiba

aarchiba commented Nov 6, 2019

Copy link
Copy Markdown
Contributor Author

My suggestion was that we use this, and perhaps also #9211 , as test beds to show that hypothesis can (or can't) serve as an effective tool for ensuring that astropy provides correct and accurate results, and also as a way to evaluate the impact of adding hypothesis on CI loads.

@Zac-HD

Zac-HD commented Nov 7, 2019

Copy link
Copy Markdown
Contributor

From memory Hypothesis is in the conda-forge channel but not default.

@mhvk mhvk left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Haven't done a thorough review yet, but I really like where this is going! The tests are all clean examples of what we expect to hold.

My main worry is much more about balancing power usage (I really do care about minimizing that, and after some time, one should be able to know a piece of code works and it is no longer necessary to test all the time), and about possible false positives for unrelated PRs (this is probably less of a deal, since one would see it was an unrelated bug pretty quickly).

For the power usage, just to have some sense: how much does the time required to run all time tests increase due to this PR?

Comment thread astropy/time/tests/test_precision.py Outdated
jd1_b, jd2_b = jds_b
# not translation invariant so can't convert TimeDelta
assume(scale1 != 'utc')
assume(scale1 != 'ut1')

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

UT1 and UT1 should be OK (counted in earth rotation cycles, so continuous). But I guess you really are aiming for different scale1 and scale2 here - in that case, maybe make the test explicitly do that? (skip it if scales are equal?)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Well it surprises me a little that UT1-UT1 might work so I changed the assumptions to permit that (UTC-UTC does not).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

We had quite a bit of discussion about it at the time, but came to the conclusion that since UT1 is continuous, it should work. It mostly helps in that you can, therefore, just adds quantities in units of time to it (which have indeterminate scale, represented with scale=None in TimeDelta).

@aarchiba

aarchiba commented Nov 7, 2019

Copy link
Copy Markdown
Contributor Author

Any idea why we might be pulling in nose, of all things? One run is dying on a DeprecationWarning internal to nose, but I don't think we use it at all?

@aarchiba

aarchiba commented Nov 7, 2019

Copy link
Copy Markdown
Contributor Author

Haven't done a thorough review yet, but I really like where this is going! The tests are all clean examples of what we expect to hold.

This was my (admittedly related) experience with hypothesis on PINT as well.

My main worry is much more about balancing power usage (I really do care about minimizing that, and after some time, one should be able to know a piece of code works and it is no longer necessary to test all the time), and about possible false positives for unrelated PRs (this is probably less of a deal, since one would see it was an unrelated bug pretty quickly).

The power usage problem is not just restricted to hypothesis tests, of course - in principle one shouldn't need to run the entire test suite on every minor code change. But the usual understanding is that the number of times it catches the fact that new code broke something unexpected is worth it. Certainly hypothesis increases the cost of testing, though how much is an open question (which I will try to address). Note, though, that something like using a test fixture that creates and deletes a temporary directory can also add pretty substantially to the runtime of a test (because it waits for IO operations).

For the power usage, just to have some sense: how much does the time required to run all time tests increase due to this PR?

$ time pytest astropy/time
/home/archibald/.virtualenvs/astropy/lib/python3.7/site-packages/pep8.py:110: FutureWarning: Possible nested set at position 1
  EXTRANEOUS_WHITESPACE_REGEX = re.compile(r'[[({] | []}),;:]')
/home/archibald/projects/astropy/astropy/astropy/conftest.py:47: MatplotlibDeprecationWarning: 
The examples.directory rcparam was deprecated in Matplotlib 3.0 and will be removed in 3.2. In the future, examples will be found relative to the 'datapath' directory.
  matplotlibrc_cache.update(matplotlib.rcParams)
================================================= test session starts =================================================
platform linux -- Python 3.7.3, pytest-5.1.3, py-1.8.0, pluggy-0.13.0

Running tests with Astropy version 4.0.dev26769.
Running tests in astropy/time.

Date: 2019-11-07T12:34:30

Platform: Linux-5.0.0-32-generic-x86_64-with-Ubuntu-19.04-disco

Executable: /home/archibald/.virtualenvs/astropy/bin/python3

Full Python Version: 
3.7.3 (default, Apr  3 2019, 05:39:12) 
[GCC 8.3.0]

encodings: sys: utf-8, locale: UTF-8, filesystem: utf-8
byteorder: little
float info: dig: 15, mant_dig: 15

Package versions: 
Numpy: 1.17.1
Scipy: 1.3.1
Matplotlib: 3.1.1
h5py: 2.10.0
Pandas: 0.25.1
Asdf: 2.4.2
Cython: 0.29.13
Scikit-image: 0.15.0
asdf: 2.4.2
astropy-helpers: 3.2.1

Using Astropy options: remote_data: none.

Matplotlib: 3.1.1
Freetype: 2.6.1
rootdir: /home/archibald/projects/astropy/astropy, inifile: setup.cfg
plugins: arraydiff-0.3, xdist-1.30.0, astropy-header-0.1.1, openfiles-0.4.0, hypothesis-4.42.10, pep8-1.0.6, forked-1.1.1, remotedata-0.3.2, asdf-2.4.2, mpl-0.10, doctestplus-0.4.0
collected 493 items                                                                                                   

astropy/time/core.py .....                                                                                      [  1%]
astropy/time/formats.py ...                                                                                     [  1%]
astropy/time/tests/test_basic.py .............................................................................. [ 17%]
.........................s.................................................................                     [ 35%] 
astropy/time/tests/test_comparisons.py ....                                                                     [ 36%]
astropy/time/tests/test_corrs.py ...s                                                                           [ 37%]
astropy/time/tests/test_custom_formats.py ..................................                                    [ 44%]
astropy/time/tests/test_delta.py .............................................................................. [ 60%]
.................                                                                                               [ 63%] 
astropy/time/tests/test_guess.py ....                                                                           [ 64%]
astropy/time/tests/test_mask.py .........                                                                       [ 66%]
astropy/time/tests/test_methods.py ............................................................................ [ 81%]
.....................                                                                                           [ 86%] 
astropy/time/tests/test_pickle.py .                                                                             [ 86%]
astropy/time/tests/test_precision.py ...............                                                            [ 89%]
astropy/time/tests/test_quantity_interaction.py ......................                                          [ 93%]
astropy/time/tests/test_sidereal.py ...................                                                         [ 97%]
astropy/time/tests/test_update_leap_seconds.py .ss...                                                           [ 98%]
astropy/time/tests/test_ut1.py s..ss.                                                                           [100%]

=========================================== 486 passed, 7 skipped in 9.42s ============================================

real    0m11.261s
user    0m11.071s
sys     0m0.830s

$ git checkout time-hypothesis 
Switched to branch 'time-hypothesis'
Your branch is up to date with 'origin/time-hypothesis'.
$ time pytest astropy/time     
/home/archibald/.virtualenvs/astropy/lib/python3.7/site-packages/pep8.py:110: FutureWarning: Possible nested set at position 1
  EXTRANEOUS_WHITESPACE_REGEX = re.compile(r'[[({] | []}),;:]')
/home/archibald/projects/astropy/astropy/astropy/conftest.py:47: MatplotlibDeprecationWarning: 
The examples.directory rcparam was deprecated in Matplotlib 3.0 and will be removed in 3.2. In the future, examples will be found relative to the 'datapath' directory.
  matplotlibrc_cache.update(matplotlib.rcParams)
================================================= test session starts =================================================
platform linux -- Python 3.7.3, pytest-5.1.3, py-1.8.0, pluggy-0.13.0

Running tests with Astropy version 4.0.dev26775.
Running tests in astropy/time.

Date: 2019-11-07T12:35:03

Platform: Linux-5.0.0-32-generic-x86_64-with-Ubuntu-19.04-disco

Executable: /home/archibald/.virtualenvs/astropy/bin/python3

Full Python Version: 
3.7.3 (default, Apr  3 2019, 05:39:12) 
[GCC 8.3.0]

encodings: sys: utf-8, locale: UTF-8, filesystem: utf-8
byteorder: little
float info: dig: 15, mant_dig: 15

Package versions: 
Numpy: 1.17.1
Scipy: 1.3.1
Matplotlib: 3.1.1
h5py: 2.10.0
Pandas: 0.25.1
Asdf: 2.4.2
Cython: 0.29.13
Scikit-image: 0.15.0
asdf: 2.4.2
astropy-helpers: 3.2.1

Using Astropy options: remote_data: none.

Matplotlib: 3.1.1
Freetype: 2.6.1
rootdir: /home/archibald/projects/astropy/astropy, inifile: setup.cfg
plugins: arraydiff-0.3, xdist-1.30.0, astropy-header-0.1.1, openfiles-0.4.0, hypothesis-4.42.10, pep8-1.0.6, forked-1.1.1, remotedata-0.3.2, asdf-2.4.2, mpl-0.10, doctestplus-0.4.0
collected 508 items                                                                                                   

astropy/time/core.py .....                                                                                      [  0%]
astropy/time/formats.py ...                                                                                     [  1%]
astropy/time/tests/test_basic.py .............................................................................. [ 16%]
.........................s.................................................................                     [ 34%]
astropy/time/tests/test_comparisons.py ....                                                                     [ 35%]
astropy/time/tests/test_corrs.py ...s                                                                           [ 36%]
astropy/time/tests/test_custom_formats.py ..................................                                    [ 43%]
astropy/time/tests/test_delta.py .............................................................................. [ 58%]
.................                                                                                               [ 61%]
astropy/time/tests/test_guess.py ....                                                                           [ 62%]
astropy/time/tests/test_mask.py .........                                                                       [ 64%]
astropy/time/tests/test_methods.py ............................................................................ [ 79%]
.....................                                                                                           [ 83%]
astropy/time/tests/test_pickle.py .                                                                             [ 83%]
astropy/time/tests/test_precision.py .......................x......                                             [ 89%]
astropy/time/tests/test_quantity_interaction.py ......................                                          [ 93%]
astropy/time/tests/test_sidereal.py ...................                                                         [ 97%]
astropy/time/tests/test_update_leap_seconds.py .ss...                                                           [ 98%]
astropy/time/tests/test_ut1.py s..ss.                                                                           [100%]

===================================== 500 passed, 7 skipped, 1 xfailed in 15.45s ======================================

real    0m17.339s
user    0m16.993s
sys     0m0.921s

@aarchiba

aarchiba commented Nov 7, 2019

Copy link
Copy Markdown
Contributor Author

As should be visible in the diff, hypothesis supports profiles, and (if I got it right) all CI runs use the ci profile, which could be set to fewer examples (I think the default might be 100?), while the normal and fuzzing profiles could be set more aggressively if desired.

Certainly I've done some fuzzing runs here as checks, and if there's a particular test I'm worried about I can run it with pytest astropy/time/test_precision.py::test_dubious --hypothesis-profile=fuzzing.

@Zac-HD

Zac-HD commented Nov 7, 2019

Copy link
Copy Markdown
Contributor

Yep, 100 runs by default. This is the main knob to tune for test runtime and power consumption - the only impact (assuming that you persist the example database) is that reducing the number of examples gives you a somewhat-more-than-linear decrease in probability of finding a bug in any given run.

(mostly because the first few examples let Hypothesis discover the structure of the strategy and start generating valid data, and when you're using target() that lets further examples evolve towards higher-scoring inputs)

@aarchiba

aarchiba commented Nov 7, 2019

Copy link
Copy Markdown
Contributor Author

Yep, 100 runs by default. This is the main knob to tune for test runtime and power consumption - the only impact (assuming that you persist the example database) is that reducing the number of examples gives you a somewhat-more-than-linear decrease in probability of finding a bug in any given run.

Oh! I guess we could use the Travis cache on the .hypothesis directory to get (opportunistic) persistence?

@Zac-HD

Zac-HD commented Nov 7, 2019

Copy link
Copy Markdown
Contributor

Oh! I guess we could use the Travis cache on the .hypothesis directory to get (opportunistic) persistence?

Exactly! Obviously Hypothesis doesn't need persistence, but it does give some nice workflows for caching slow-to-compute values, replaying some passing and failed examples to jump-start generation, and so on.

@bsipocz bsipocz added this to the v4.0.1 milestone Nov 7, 2019
@pllim

pllim commented Nov 7, 2019

Copy link
Copy Markdown
Member

Any idea why we might be pulling in nose, of all things?

What.... Does hypothesis use nose?

@Zac-HD

Zac-HD commented Nov 7, 2019

Copy link
Copy Markdown
Contributor

Nope, we only depend on attrs

@mhvk

mhvk commented Nov 7, 2019

Copy link
Copy Markdown
Contributor

The cache with somewhat fewer runs in default PR testing sounds like a good compromise. And maybe hammer it a bit more before a release or so (or for the cron job?).

@pllim

pllim commented Nov 7, 2019

Copy link
Copy Markdown
Member

The nose mystery -- It only effects one job. So somehow in that particular CI setting, using hypothesis somehow calls nose. How is this possible? Something doesn't smell right.

@aarchiba

aarchiba commented Nov 7, 2019

Copy link
Copy Markdown
Contributor Author

The nose mystery -- It only effects one job. So somehow in that particular CI setting, using hypothesis somehow calls nose. How is this possible? Something doesn't smell right.

Maybe doesn't even call; nose fails on first import, but I don't know who is importing it or why. I thought it might be that having nose installed shadowed a name and resulted in an unintended import, but installing it in my local virtualenv doesn't result in any obvious warnings.

I believe nose is installed at all in that environment for some conda-related reason, but I don't know. miniconda isn't supposed to pull in all sorts of extras, is it?

@pllim

pllim commented Nov 7, 2019

Copy link
Copy Markdown
Member

The job that failed, even though we run things in conda env, that job pulls in all the dependency using pip and the [test,all] specification. The namespace confusion is a possibility but it seems to be happening deep down somewhere beyond our reach at astropy.

@Zac-HD

Zac-HD commented Nov 7, 2019

Copy link
Copy Markdown
Contributor

And maybe hammer it a bit more before a release or so (or for the cron job?).

Sounds good, I'd probably do both.

@aarchiba

aarchiba commented Nov 8, 2019

Copy link
Copy Markdown
Contributor Author

So I'm not quite sure what to expect in terms of interaction between Time/TimeDelta and datetime/timedelta. I get differences by integer seconds with UTC, but nontrivial differences with other time scales as well. I can file an issue but I'm not sure what behaviour I should expect.

@aarchiba

Copy link
Copy Markdown
Contributor Author

I'm a little confused by sidereal_time - I thought it should always be between 0 and about 24h*(1-1/365)? It is represented as an Angle, though, so automatic wrapping happens at 24h, which could be trouble, certainly if the user ever does any arithmetic. Maybe "sidereal time" doesn't mean what I think it does?

@aarchiba

aarchiba commented Nov 30, 2019

Copy link
Copy Markdown
Contributor Author

@Zac-HD - any idea why there is a deadline to fail? both the default profile "normal" and the "ci" profile it is supposed to be using set deadline=None and yet we are still failing on a deadline mismatch: https://travis-ci.org/astropy/astropy/jobs/618791455?utm_medium=notification&utm_source=github_status

Perhaps we can ask hypothesis to emit more status information? I haven't seen a deadline failure in a while on my local machine (and they were somewhat regular occurrences until I used the profile to disable them).

@Zac-HD

Zac-HD commented Dec 2, 2019

Copy link
Copy Markdown
Contributor

Unfortunately I don't know what's happening there - my guess would be that the environment variable isn't being picked up properly?

Aside from printing the actual env var value, running either pytest or Hypothesis in verbose mode (-v / --hypothesis-verbosity=verbose respectively) will add Hypothesis profile info to the pytest header (HypothesisWorks/hypothesis#2165).

@aarchiba

aarchiba commented Dec 9, 2019

Copy link
Copy Markdown
Contributor Author

OK meeting discussion seemed to agree that hypothesis testing can go into astropy. How do we want to handle this?

  • I could tidy this a little and it could go into mainline as is, then we could tidy up how hypothesis is supported in astropy.

  • Someone with Astropy Powers could merge hypothesis support into pytest-astropy, then I could pull the hypothesis setup stuff out of here, then we could merge this.

  • I could keep this as an exprerimental branch, gradually pushing tests over to mainline piece by piece as mainline acquires hypothesis support.

The main question is how to handle interaction with pytest-astropy; I don't really know how to work on related changes between it and mainline astropy (and the git modules thing just seems like a black box that wrecks things randomly).

@Zac-HD

Zac-HD commented Dec 9, 2019

Copy link
Copy Markdown
Contributor

Someone with Astropy Powers could merge hypothesis support into pytest-astropy, then I could pull the hypothesis setup stuff out of here, then we could merge this.

I think making pytest-astropy depend on Hypothesis would be useful, since it's then in everyone's test environments (and it seems to exist to avert packaging pain), but IMO the profiles and other config should probably remain a per-project thing.

@aarchiba

aarchiba commented Dec 9, 2019

Copy link
Copy Markdown
Contributor Author

Someone with Astropy Powers could merge hypothesis support into pytest-astropy, then I could pull the hypothesis setup stuff out of here, then we could merge this.

I think making pytest-astropy depend on Hypothesis would be useful, since it's then in everyone's test environments (and it seems to exist to avert packaging pain), but IMO the profiles and other config should probably remain a per-project thing.

I think it would be useful for pytest-astropy to pass through some hypothesis-specific options from "python setup.py test" to the inner "pytest" so one could avoid the "--args" mechanism.

There doesn't seem to be any documentation on how this quoting should
work...
wouldn't docs be nice?
Shells. Ugh.
@pllim

pllim commented Dec 10, 2019

Copy link
Copy Markdown
Member

hypothesis-specific options from "python setup.py test"

I think this will be a non-issue when APE 17 is accepted and implemented, as we will no longer be using setup.py to run the tests. @astrofrog et al. can correct me if I misunderstood.

@pllim

pllim commented Dec 10, 2019

Copy link
Copy Markdown
Member

I added astropy/pytest-astropy#24 for discussion of hypothesis on the pytest-astropy side.

@Zac-HD

Zac-HD commented Dec 12, 2019

Copy link
Copy Markdown
Contributor

pytest-astropy >= 0.7 now pulls in hypothesis 😄

@bsipocz

bsipocz commented Feb 26, 2020

Copy link
Copy Markdown
Member

@aarchiba - I'm remilestoning this to the next bugfix as this may or may not require some more work after the rebase (tests seemed to passed before, so hopefully it won't be too bad).

@astrofrog

Copy link
Copy Markdown
Member

@aarchiba @mhvk @taldcroft - how do we want to proceed with this? I guess it could be included in a bugfix release after 4.1?

@aarchiba - would you be able to rebase this?

@bsipocz

bsipocz commented May 1, 2020

Copy link
Copy Markdown
Member

@astrofrog - my thinking was also the bugfix release. And also, that it may be worth splitting this up into smaller chunks, with the perfect is the enemy of good motto, having a few tests are better than nothing (note that we already install hypothesis for the CI, so going gradual is totally reasonable).

@astropy-bot

astropy-bot Bot commented May 13, 2020

Copy link
Copy Markdown

Hi humans 👋 - this pull request hasn't had any new commits for approximately 5 months. I plan to close this in a month if the pull request doesn't have any new commits by then.

In lieu of a stalled pull request, please consider closing this and open an issue instead if a reminder is needed to revisit in the future. Maintainers may also choose to add keep-open label to keep this PR open but it is discouraged unless absolutely necessary.

If this PR still needs to be reviewed, as an author, you can rebase it to reset the clock.

If you believe I commented on this pull request incorrectly, please report this here.

@Zac-HD

Zac-HD commented May 15, 2020

Copy link
Copy Markdown
Contributor

Since pytest-astropy now pulls in hypothesis, the CI configuration is much simpler -I've squash-rebased the PR over at master...Zac-HD:time-hypothesis and you could cherry-pick 338b766.

There are actually very few merge conflicts in test_precision.py, but while I think "accept PR changes" would work for all of them it's @aarchiba's project not mine 🙂

@pllim

pllim commented May 15, 2020

Copy link
Copy Markdown
Member

@Zac-HD , it is not unprecedented for someone else to submit a new PR that supersedes a stale PR if you wish to pursue this. FYI.

UPDATE: See #10373

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.

7 participants