text: import collections
  import re
  from traceback import format_exception
  from unittest import mock
  
  import pytest
  
  from aiohttp import helpers, signals, web
  from aiohttp.test_utils import make_mocked_request
  
  
  @pytest.fixture
  def buf():
      return bytearray()
  
  
  @pytest.fixture
  def http_request(buf):
      method = 'GET'
      path = '/'
      writer = mock.Mock()
      writer.drain.return_value = ()
  
      def append(data=b''):
          buf.extend(data)
          return helpers.noop()
  
      async def write_headers(status_line, headers):
          headers = status_line + '\r\n' + ''.join(
              [k + ': ' + v + '\r\n' for k, v in headers.items()])
          headers = headers.encode('utf-8') + b'\r\n'
          buf.extend(headers)
  
      writer.buffer_data.side_effect = append
      writer.write.side_effect = append
      writer.write_eof.side_effect = append
      writer.write_headers.side_effect = write_headers
  
      app = mock.Mock()
      app._debug = False
      app.on_response_prepare = signals.Signal(app)
      app.on_response_prepare.freeze()
      req = make_mocked_request(method, path, app=app, writer=writer)
      return req
  
  
  def test_all_http_exceptions_exported() -> None:
      assert 'HTTPException' in web.__all__
      for name in dir(web):
          if name.startswith('_'):
              continue
          obj = getattr(web, name)
          if isinstance(obj, type) and issubclass(obj, web.HTTPException):
              assert name in web.__all__
  
  
  async def test_HTTPOk(buf, http_request) -> None:
      resp = web.HTTPOk()
      await resp.prepare(http_request)
      await resp.write_eof()
      txt = buf.decode('utf8')
      assert re.match(('HTTP/1.1 200 OK\r\n'
                       'Content-Type: text/plain; charset=utf-8\r\n'
                       'Content-Length: 7\r\n'
                       'Date: .+\r\n'
                       'Server: .+\r\n\r\n'
                       '200: OK'), txt)
  
  
  def test_terminal_classes_has_status_code() -> None:
      terminals = set()
      for name in dir(web):
          obj = getattr(web, name)
          if isinstance(obj, type) and issubclass(obj, web.HTTPException):
              terminals.add(obj)
  
      dup = frozenset(terminals)
      for cls1 in dup:
          for cls2 in dup:
              if cls1 in cls2.__bases__:
                  terminals.discard(cls1)
  
      for cls in terminals:
          assert cls.status_code is not None
      codes = collections.Counter(cls.status_code for cls in terminals)
      assert None not in codes
      assert 1 == codes.most_common(1)[0][1]
  
  
  async def test_HTTPFound(buf, http_request) -> None:
      resp = web.HTTPFound(location='/redirect')
      assert '/redirect' == resp.location
      assert '/redirect' == resp.headers['location']
      await resp.prepare(http_request)
      await resp.write_eof()
      txt = buf.decode('utf8')
      assert re.match('HTTP/1.1 302 Found\r\n'
                      'Content-Type: text/plain; charset=utf-8\r\n'
                      'Location: /redirect\r\n'
                      'Content-Length: 10\r\n'
                      'Date: .+\r\n'
                      'Server: .+\r\n\r\n'
                      '302: Found', txt)
  
  
  def test_HTTPFound_empty_location() -> None:
      with pytest.raises(ValueError):
          web.HTTPFound(location='')
  
      with pytest.raises(ValueError):
          web.HTTPFound(location=None)
  
  
  def test_HTTPFound_location_CRLF() -> None:
      exc = web.HTTPFound(location='/redirect\r\n')
      assert '\r\n' not in exc.headers['Location']
  
  
  async def test_HTTPMethodNotAllowed(buf, http_request) -> None:
      resp = web.HTTPMethodNotAllowed('get', ['POST', 'PUT'])
      assert 'GET' == resp.method
      assert {'POST', 'PUT'} == resp.allowed_methods
      assert 'POST,PUT' == resp.headers['allow']
      await resp.prepare(http_request)
      await resp.write_eof()
      txt = buf.decode('utf8')
      assert re.match('HTTP/1.1 405 Method Not Allowed\r\n'
                      'Content-Type: text/plain; charset=utf-8\r\n'
                      'Allow: POST,PUT\r\n'
                      'Content-Length: 23\r\n'
                      'Date: .+\r\n'
                      'Server: .+\r\n\r\n'
                      '405: Method Not Allowed', txt)
  
  
  def test_override_body_with_text() -> None:
      resp = web.HTTPNotFound(text="Page not found")
      assert 404 == resp.status
      assert "Page not found".encode('utf-8') == resp.body
      assert "Page not found" == resp.text
      assert "text/plain" == resp.content_type
      assert "utf-8" == resp.charset
  
  
  def test_override_body_with_binary() -> None:
      txt = "<html><body>Page not found</body></html>"
      with pytest.warns(DeprecationWarning):
          resp = web.HTTPNotFound(body=txt.encode('utf-8'),
                                  content_type="text/html")
      assert 404 == resp.status
      assert txt.encode('utf-8') == resp.body
      assert txt == resp.text
      assert "text/html" == resp.content_type
      assert resp.charset is None
  
  
  def test_default_body() -> None:
      resp = web.HTTPOk()
      assert b'200: OK' == resp.body
  
  
  def test_empty_body_204() -> None:
      resp = web.HTTPNoContent()
      assert resp.body is None
  
  
  def test_empty_body_205() -> None:
      resp = web.HTTPNoContent()
      assert resp.body is None
  
  
  def test_empty_body_304() -> None:
      resp = web.HTTPNoContent()
      resp.body is None
  
  
  def test_link_header_451(buf) -> None:
      resp = web.HTTPUnavailableForLegalReasons(link='http://warning.or.kr/')
  
      assert 'http://warning.or.kr/' == resp.link
      assert '<http://warning.or.kr/>; rel="blocked-by"' == resp.headers['Link']
  
  
  def test_HTTPException_retains_cause() -> None:
      with pytest.raises(web.HTTPException) as ei:
          try:
              raise Exception('CustomException')
          except Exception as exc:
              raise web.HTTPException() from exc
      tb = ''.join(format_exception(ei.type, ei.value, ei.tb))
      assert 'CustomException' in tb
      assert 'direct cause' in tb
  