# Path: starlette/background.py
# class BackgroundTask:
#     def __init__(
#         self, func: typing.Callable[P, typing.Any], *args: P.args, **kwargs: P.kwargs
#     ) -> None:
#         self.func = func
#         self.args = args
#         self.kwargs = kwargs
#         self.is_async = asyncio.iscoroutinefunction(func)
# 
#     async def __call__(self) -> None:
#         if self.is_async:
#             await self.func(*self.args, **self.kwargs)
#         else:
#             await run_in_threadpool(self.func, *self.args, **self.kwargs)
# 
# class BackgroundTasks(BackgroundTask):
#     def __init__(self, tasks: typing.Sequence[BackgroundTask] = None):
#         self.tasks = list(tasks) if tasks else []
# 
#     def add_task(
#         self, func: typing.Callable[P, typing.Any], *args: P.args, **kwargs: P.kwargs
#     ) -> None:
#         task = BackgroundTask(func, *args, **kwargs)
#         self.tasks.append(task)
# 
#     async def __call__(self) -> None:
#         for task in self.tasks:
#             await task()
# 
# Path: starlette/responses.py
# class Response:
#     media_type = None
#     charset = "utf-8"
# 
#     def __init__(
#         self,
#         content: typing.Any = None,
#         status_code: int = 200,
#         headers: typing.Mapping[str, str] = None,
#         media_type: str = None,
#         background: BackgroundTask = None,
#     ) -> None:
#         self.status_code = status_code
#         if media_type is not None:
#             self.media_type = media_type
#         self.background = background
#         self.body = self.render(content)
#         self.init_headers(headers)
# 
#     def render(self, content: typing.Any) -> bytes:
#         if content is None:
#             return b""
#         if isinstance(content, bytes):
#             return content
#         return content.encode(self.charset)
# 
#     def init_headers(self, headers: typing.Mapping[str, str] = None) -> None:
#         if headers is None:
#             raw_headers: typing.List[typing.Tuple[bytes, bytes]] = []
#             populate_content_length = True
#             populate_content_type = True
#         else:
#             raw_headers = [
#                 (k.lower().encode("latin-1"), v.encode("latin-1"))
#                 for k, v in headers.items()
#             ]
#             keys = [h[0] for h in raw_headers]
#             populate_content_length = b"content-length" not in keys
#             populate_content_type = b"content-type" not in keys
# 
#         body = getattr(self, "body", None)
#         if (
#             body is not None
#             and populate_content_length
#             and not (self.status_code < 200 or self.status_code in (204, 304))
#         ):
#             content_length = str(len(body))
#             raw_headers.append((b"content-length", content_length.encode("latin-1")))
# 
#         content_type = self.media_type
#         if content_type is not None and populate_content_type:
#             if content_type.startswith("text/"):
#                 content_type += "; charset=" + self.charset
#             raw_headers.append((b"content-type", content_type.encode("latin-1")))
# 
#         self.raw_headers = raw_headers
# 
#     @property
#     def headers(self) -> MutableHeaders:
#         if not hasattr(self, "_headers"):
#             self._headers = MutableHeaders(raw=self.raw_headers)
#         return self._headers
# 
#     def set_cookie(
#         self,
#         key: str,
#         value: str = "",
#         max_age: int = None,
#         expires: int = None,
#         path: str = "/",
#         domain: str = None,
#         secure: bool = False,
#         httponly: bool = False,
#         samesite: str = "lax",
#     ) -> None:
#         cookie: http.cookies.BaseCookie = http.cookies.SimpleCookie()
#         cookie[key] = value
#         if max_age is not None:
#             cookie[key]["max-age"] = max_age
#         if expires is not None:
#             cookie[key]["expires"] = expires
#         if path is not None:
#             cookie[key]["path"] = path
#         if domain is not None:
#             cookie[key]["domain"] = domain
#         if secure:
#             cookie[key]["secure"] = True
#         if httponly:
#             cookie[key]["httponly"] = True
#         if samesite is not None:
#             assert samesite.lower() in [
#                 "strict",
#                 "lax",
#                 "none",
#             ], "samesite must be either 'strict', 'lax' or 'none'"
#             cookie[key]["samesite"] = samesite
#         cookie_val = cookie.output(header="").strip()
#         self.raw_headers.append((b"set-cookie", cookie_val.encode("latin-1")))
# 
#     def delete_cookie(
#         self,
#         key: str,
#         path: str = "/",
#         domain: str = None,
#         secure: bool = False,
#         httponly: bool = False,
#         samesite: str = "lax",
#     ) -> None:
#         self.set_cookie(
#             key,
#             max_age=0,
#             expires=0,
#             path=path,
#             domain=domain,
#             secure=secure,
#             httponly=httponly,
#             samesite=samesite,
#         )
# 
#     async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
#         await send(
#             {
#                 "type": "http.response.start",
#                 "status": self.status_code,
#                 "headers": self.raw_headers,
#             }
#         )
#         await send({"type": "http.response.body", "body": self.body})
# 
#         if self.background is not None:
#             await self.background()


from starlette.background import BackgroundTask, BackgroundTasks
from starlette.responses import Response


def test_async_task(test_client_factory):
    TASK_COMPLETE = False

    async def async_task():
        nonlocal TASK_COMPLETE
        TASK_COMPLETE = True


# Path: starlette/background.py
# class BackgroundTask:
#     def __init__(
#         self, func: typing.Callable[P, typing.Any], *args: P.args, **kwargs: P.kwargs
#     ) -> None:
#         self.func = func
#         self.args = args
#         self.kwargs = kwargs
#         self.is_async = asyncio.iscoroutinefunction(func)
# 
#     async def __call__(self) -> None:
#         if self.is_async:
#             await self.func(*self.args, **self.kwargs)
#         else:
#             await run_in_threadpool(self.func, *self.args, **self.kwargs)
# 
# class BackgroundTasks(BackgroundTask):
#     def __init__(self, tasks: typing.Sequence[BackgroundTask] = None):
#         self.tasks = list(tasks) if tasks else []
# 
#     def add_task(
#         self, func: typing.Callable[P, typing.Any], *args: P.args, **kwargs: P.kwargs
#     ) -> None:
#         task = BackgroundTask(func, *args, **kwargs)
#         self.tasks.append(task)
# 
#     async def __call__(self) -> None:
#         for task in self.tasks:
#             await task()
# 
# Path: starlette/responses.py
# class Response:
#     media_type = None
#     charset = "utf-8"
# 
#     def __init__(
#         self,
#         content: typing.Any = None,
#         status_code: int = 200,
#         headers: typing.Mapping[str, str] = None,
#         media_type: str = None,
#         background: BackgroundTask = None,
#     ) -> None:
#         self.status_code = status_code
#         if media_type is not None:
#             self.media_type = media_type
#         self.background = background
#         self.body = self.render(content)
#         self.init_headers(headers)
# 
#     def render(self, content: typing.Any) -> bytes:
#         if content is None:
#             return b""
#         if isinstance(content, bytes):
#             return content
#         return content.encode(self.charset)
# 
#     def init_headers(self, headers: typing.Mapping[str, str] = None) -> None:
#         if headers is None:
#             raw_headers: typing.List[typing.Tuple[bytes, bytes]] = []
#             populate_content_length = True
#             populate_content_type = True
#         else:
#             raw_headers = [
#                 (k.lower().encode("latin-1"), v.encode("latin-1"))
#                 for k, v in headers.items()
#             ]
#             keys = [h[0] for h in raw_headers]
#             populate_content_length = b"content-length" not in keys
#             populate_content_type = b"content-type" not in keys
# 
#         body = getattr(self, "body", None)
#         if (
#             body is not None
#             and populate_content_length
#             and not (self.status_code < 200 or self.status_code in (204, 304))
#         ):
#             content_length = str(len(body))
#             raw_headers.append((b"content-length", content_length.encode("latin-1")))
# 
#         content_type = self.media_type
#         if content_type is not None and populate_content_type:
#             if content_type.startswith("text/"):
#                 content_type += "; charset=" + self.charset
#             raw_headers.append((b"content-type", content_type.encode("latin-1")))
# 
#         self.raw_headers = raw_headers
# 
#     @property
#     def headers(self) -> MutableHeaders:
#         if not hasattr(self, "_headers"):
#             self._headers = MutableHeaders(raw=self.raw_headers)
#         return self._headers
# 
#     def set_cookie(
#         self,
#         key: str,
#         value: str = "",
#         max_age: int = None,
#         expires: int = None,
#         path: str = "/",
#         domain: str = None,
#         secure: bool = False,
#         httponly: bool = False,
#         samesite: str = "lax",
#     ) -> None:
#         cookie: http.cookies.BaseCookie = http.cookies.SimpleCookie()
#         cookie[key] = value
#         if max_age is not None:
#             cookie[key]["max-age"] = max_age
#         if expires is not None:
#             cookie[key]["expires"] = expires
#         if path is not None:
#             cookie[key]["path"] = path
#         if domain is not None:
#             cookie[key]["domain"] = domain
#         if secure:
#             cookie[key]["secure"] = True
#         if httponly:
#             cookie[key]["httponly"] = True
#         if samesite is not None:
#             assert samesite.lower() in [
#                 "strict",
#                 "lax",
#                 "none",
#             ], "samesite must be either 'strict', 'lax' or 'none'"
#             cookie[key]["samesite"] = samesite
#         cookie_val = cookie.output(header="").strip()
#         self.raw_headers.append((b"set-cookie", cookie_val.encode("latin-1")))
# 
#     def delete_cookie(
#         self,
#         key: str,
#         path: str = "/",
#         domain: str = None,
#         secure: bool = False,
#         httponly: bool = False,
#         samesite: str = "lax",
#     ) -> None:
#         self.set_cookie(
#             key,
#             max_age=0,
#             expires=0,
#             path=path,
#             domain=domain,
#             secure=secure,
#             httponly=httponly,
#             samesite=samesite,
#         )
# 
#     async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
#         await send(
#             {
#                 "type": "http.response.start",
#                 "status": self.status_code,
#                 "headers": self.raw_headers,
#             }
#         )
#         await send({"type": "http.response.body", "body": self.body})
# 
#         if self.background is not None:
#             await self.background()


# Path: tests/test_background.py
from starlette.background import BackgroundTask, BackgroundTasks
from starlette.responses import Response



def test_async_task(test_client_factory):
    TASK_COMPLETE = False

    async def async_task():
        nonlocal TASK_COMPLETE
        TASK_COMPLETE = True


    task = BackgroundTask(async_task)