docstr: Checks the tree to see if we've tested this buffer, and returns the
  previous result if we have.
  
  Otherwise we call through to ``test_function``, and return a
  fresh result.
  
  If ``error_on_discard`` is set to True this will raise ``ContainsDiscard``
  in preference to running the actual test function. This is to allow us
  to skip test cases we expect to be redundant in some cases. Note that
  it may be the case that we don't raise ``ContainsDiscard`` even if the
  result has discards if we cannot determine from previous runs whether
  it will have a discard.
docstr_tok: ["Checks","the","tree","to","see","if","we","'ve","tested","this","buffer",",","and","returns","the","previous","result","if","we","have",".","Otherwise","we","call","through","to","``","test_function","``",",","and","return","a","fresh","result",".","If","``","error_on_discard","``","is","set","to","True","this","will","raise","``","ContainsDiscard","``","in","preference","to","running","the","actual","test","function",".","This","is","to","allow","us","to","skip","test","cases","we","expect","to","be","redundant","in","some","cases",".","Note","that","it","may","be","the","case","that","we","do","n't","raise","``","ContainsDiscard","``","even","if","the","result","has","discards","if","we","can","not","determine","from","previous","runs","whether","it","will","have","a","discard","."]
code: def cached_test_function(
      self, buffer, *, error_on_discard=False, extend=0
  ):
      """Checks the tree to see if we've tested this buffer, and returns the
      previous result if we have.
  
      Otherwise we call through to ``test_function``, and return a
      fresh result.
  
      If ``error_on_discard`` is set to True this will raise ``ContainsDiscard``
      in preference to running the actual test function. This is to allow us
      to skip test cases we expect to be redundant in some cases. Note that
      it may be the case that we don't raise ``ContainsDiscard`` even if the
      result has discards if we cannot determine from previous runs whether
      it will have a discard.
      """
      buffer = bytes(buffer)[:BUFFER_SIZE]
  
      max_length = min(BUFFER_SIZE, len(buffer) + extend)
  
      def check_result(result):
          assert result is Overrun or (
              isinstance(result, ConjectureResult)
              and result.status != Status.OVERRUN
          )
          return result
  
      try:
          cached = check_result(self.__data_cache[buffer])
          if cached.status > Status.OVERRUN or extend == 0:
              return cached
      except KeyError:
          pass
  
      if error_on_discard:
  
          class DiscardObserver(DataObserver):
              def kill_branch(self):
                  raise ContainsDiscard
  
          observer = DiscardObserver()
      else:
          observer = DataObserver()
  
      dummy_data = self.new_conjecture_data(
          prefix=buffer, max_length=max_length, observer=observer
      )
  
      try:
          self.tree.simulate_test_function(dummy_data)
      except PreviouslyUnseenBehaviour:
          pass
      else:
          if dummy_data.status > Status.OVERRUN:
              dummy_data.freeze()
              try:
                  return self.__data_cache[dummy_data.buffer]
              except KeyError:
                  pass
          else:
              self.__data_cache[buffer] = Overrun
              return Overrun
  
      # We didn't find a match in the tree, so we need to run the test
      # function normally. Note that test_function will automatically
      # add this to the tree so we don't need to update the cache.
  
      result = None
  
      data = self.new_conjecture_data(
          prefix=max((buffer, dummy_data.buffer), key=len),
          max_length=max_length,
      )
      self.test_function(data)
      result = check_result(data.as_result())
      if extend == 0 or (
          result is not Overrun and len(result.buffer) <= len(buffer)
      ):
          self.__data_cache[buffer] = result
      return result
  
imports: import math
  import time
  from collections import defaultdict
  from contextlib import contextmanager
  from datetime import timedelta
  from enum import Enum
  from random import Random, getrandbits
  import attr
  from hypothesis import HealthCheck, Phase, Verbosity, settings
  from hypothesis._settings import local_settings
  from hypothesis.errors import StopTest
  from hypothesis.internal.cache import LRUReusedCache
  from hypothesis.internal.compat import ceil, int_from_bytes
  from hypothesis.internal.conjecture.data import ConjectureData, ConjectureResult, DataObserver, Overrun, Status
  from hypothesis.internal.conjecture.datatree import DataTree, PreviouslyUnseenBehaviour, TreeRecordingObserver
  from hypothesis.internal.conjecture.junkdrawer import clamp, ensure_free_stackframes
  from hypothesis.internal.conjecture.pareto import NO_SCORE, ParetoFront, ParetoOptimiser
  from hypothesis.internal.conjecture.shrinker import Shrinker, sort_key
  from hypothesis.internal.healthcheck import fail_health_check
  from hypothesis.reporting import base_report, report
label_desc: Use image node_id 37 for calling the ConjectureRunner obj's underlying member method code with example usage: obj.cached_test_function(buffer) and returns: result, result, cached, Overrun, self