statistics.py 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. # coding=utf-8
  2. #
  3. # This file is part of Hypothesis, which may be found at
  4. # https://github.com/HypothesisWorks/hypothesis-python
  5. #
  6. # Most of this work is copyright (C) 2013-2018 David R. MacIver
  7. # (david@drmaciver.com), but it contains contributions by others. See
  8. # CONTRIBUTING.rst for a full list of people who may hold copyright, and
  9. # consult the git log if you need to determine who owns an individual
  10. # contribution.
  11. #
  12. # This Source Code Form is subject to the terms of the Mozilla Public License,
  13. # v. 2.0. If a copy of the MPL was not distributed with this file, You can
  14. # obtain one at http://mozilla.org/MPL/2.0/.
  15. #
  16. # END HEADER
  17. from __future__ import division, print_function, absolute_import
  18. import math
  19. from hypothesis.utils.dynamicvariables import DynamicVariable
  20. from hypothesis.internal.conjecture.data import Status
  21. from hypothesis.internal.conjecture.engine import ExitReason
  22. collector = DynamicVariable(None)
  23. class Statistics(object):
  24. def __init__(self, engine):
  25. self.passing_examples = len(
  26. engine.status_runtimes.get(Status.VALID, ()))
  27. self.invalid_examples = len(
  28. engine.status_runtimes.get(Status.INVALID, []) +
  29. engine.status_runtimes.get(Status.OVERRUN, [])
  30. )
  31. self.failing_examples = len(engine.status_runtimes.get(
  32. Status.INTERESTING, ()))
  33. runtimes = sorted(
  34. engine.status_runtimes.get(Status.VALID, []) +
  35. engine.status_runtimes.get(Status.INVALID, []) +
  36. engine.status_runtimes.get(Status.INTERESTING, [])
  37. )
  38. self.has_runs = bool(runtimes)
  39. if not self.has_runs:
  40. return
  41. n = max(0, len(runtimes) - 1)
  42. lower = int(runtimes[int(math.floor(n * 0.05))] * 1000)
  43. upper = int(runtimes[int(math.ceil(n * 0.95))] * 1000)
  44. if upper == 0:
  45. self.runtimes = '< 1ms'
  46. elif lower == upper:
  47. self.runtimes = '~ %dms' % (lower,)
  48. else:
  49. self.runtimes = '%d-%d ms' % (lower, upper)
  50. if engine.exit_reason == ExitReason.finished:
  51. self.exit_reason = 'nothing left to do'
  52. elif engine.exit_reason == ExitReason.flaky:
  53. self.exit_reason = 'test was flaky'
  54. else:
  55. self.exit_reason = (
  56. 'settings.%s=%r' % (
  57. engine.exit_reason.name,
  58. getattr(engine.settings, engine.exit_reason.name)
  59. )
  60. )
  61. self.events = [
  62. '%.2f%%, %s' % (
  63. c / engine.call_count * 100, e
  64. ) for e, c in sorted(
  65. engine.event_call_counts.items(), key=lambda x: -x[1])
  66. ]
  67. total_runtime = math.fsum(engine.all_runtimes)
  68. total_drawtime = math.fsum(engine.all_drawtimes)
  69. if total_drawtime == 0.0:
  70. self.draw_time_percentage = '~ 0%'
  71. else:
  72. draw_time_percentage = 100.0 * min(
  73. 1, total_drawtime / total_runtime)
  74. self.draw_time_percentage = '~ %d%%' % (
  75. round(draw_time_percentage),)
  76. def note_engine_for_statistics(engine):
  77. callback = collector.value
  78. if callback is not None:
  79. callback(Statistics(engine))