text:
import collections
import os
import secrets
import time
from typing import Set

import pytest

from hailtop import httpx
from hailtop.auth import service_auth_headers
from hailtop.batch_client.client import BatchClient
from hailtop.config import get_deploy_config, get_user_config
from hailtop.test_utils import skip_in_azure
from hailtop.utils import external_requests_client_session, retry_response_returning_functions, sync_sleep_and_backoff

from .failure_injecting_client_session import FailureInjectingClientSession
from .utils import legacy_batch_status, smallest_machine_type

deploy_config = get_deploy_config()

DOCKER_PREFIX = os.environ['DOCKER_PREFIX']
DOCKER_ROOT_IMAGE = os.environ['DOCKER_ROOT_IMAGE']
UBUNTU_IMAGE = 'ubuntu:20.04'
DOMAIN = os.environ.get('HAIL_DOMAIN')
NAMESPACE = os.environ.get('HAIL_DEFAULT_NAMESPACE')
SCOPE = os.environ.get('HAIL_SCOPE', 'test')
CLOUD = os.environ.get('HAIL_CLOUD')

@pytest.fixture
def client():
    client = BatchClient('test')
    yield client
    client.close()

def test_job(client: BatchClient):
    bb = client.create_batch()
    j = bb.create_job(DOCKER_ROOT_IMAGE, ['echo', 'test'])
    b = bb.submit()

    status = j.wait()
    assert 'attributes' not in status, str((status, b.debug_info()))
    assert status['state'] == 'Success', str((status, b.debug_info()))
    assert status['exit_code'] == 0, str((status, b.debug_info()))
    assert j._get_exit_code(status, 'main') == 0, str((status, b.debug_info()))
    job_log = j.log()
    assert job_log['main'] == 'test\n', str((job_log, b.debug_info()))

def test_job_running_logs(client: BatchClient):
    bb = client.create_batch()
    j = bb.create_job(DOCKER_ROOT_IMAGE, ['bash', '-c', 'echo test && sleep 300'])
    b = bb.submit()

    delay = 1
    while True:
        status = j.status()
        if status['state'] == 'Running':
            log = j.log()
            if log is not None and log['main'] != '':
                assert log['main'] == 'test\n', str((log, b.debug_info()))
                break
        elif status['state'] != 'Ready':
            assert False, str((j.log(), b.debug_info()))
        delay = sync_sleep_and_backoff(delay)

    b.cancel()
    b.wait()

def test_exit_code_duration(client: BatchClient):
    bb = client.create_batch()
    j = bb.create_job(DOCKER_ROOT_IMAGE, ['bash', '-c', 'exit 7'])
    b = bb.submit()
    status = j.wait()
    assert status['exit_code'] == 7, str((status, b.debug_info()))
    assert isinstance(status['duration'], int), str((status, b.debug_info()))
    assert j._get_exit_code(status, 'main') == 7, str((status, b.debug_info()))

def test_attributes(client: BatchClient):
    a = {'name': 'test_attributes', 'foo': 'bar'}
    bb = client.create_batch()
    j = bb.create_job(DOCKER_ROOT_IMAGE, ['true'], attributes=a)
    b = bb.submit()
    assert j.attributes() == a, str(b.debug_info())

def test_garbage_image(client: BatchClient):
    bb = client.create_batch()
    j = bb.create_job('dsafaaadsf', ['echo', 'test'])
    b = bb.submit()
    status = j.wait()
    assert j._get_exit_codes(status) == {'main': None}, str((status, b.debug_info()))
    assert j._get_error(status, 'main') is not None, str((status, b.debug_info()))
    assert status['state'] == 'Error', str((status, b.debug_info()))

def test_bad_command(client: BatchClient):
    bb = client.create_batch()
    j = bb.create_job(DOCKER_ROOT_IMAGE, ['sleep 5'])
    b = bb.submit()
    status = j.wait()
    assert status['state'] == 'Failed', str((status, b.debug_info()))

def test_invalid_resource_requests(client: BatchClient):
    bb = client.create_batch()
    resources = {'cpu': '1', 'memory': '250Gi', 'storage': '1Gi'}
    bb.create_job(DOCKER_ROOT_IMAGE, ['true'], resources=resources)
    with pytest.raises(httpx.ClientResponseError, match='resource requests.*unsatisfiable'):
        bb.submit()

    bb = client.create_batch()
    resources = {'cpu': '0', 'memory': '1Gi', 'storage': '1Gi'}
    bb.create_job(DOCKER_ROOT_IMAGE, ['true'], resources=resources)
    with pytest.raises(
        httpx.ClientResponseError,
        match='bad resource request for job.*cpu must be a power of two with a min of 0.25; found.*',
    ):
        bb.submit()

    bb = client.create_batch()
    resources = {'cpu': '0.1', 'memory': '1Gi', 'storage': '1Gi'}
    bb.create_job(DOCKER_ROOT_IMAGE, ['true'], resources=resources)
    with pytest.raises(
        httpx.ClientResponseError,
        match='bad resource request for job.*cpu must be a power of two with a min of 0.25; found.*',
    ):
        bb.submit()

    bb = client.create_batch()
    resources = {'cpu': '0.25', 'memory': 'foo', 'storage': '1Gi'}
    bb.create_job(DOCKER_ROOT_IMAGE, ['true'], resources=resources)
    with pytest.raises(
        httpx.ClientResponseError,
        match=".*.resources.memory must match regex:.*.resources.memory must be one of:.*",
    ):
        bb.submit()

    bb = client.create_batch()
    resources = {'cpu': '0.25', 'memory': '500Mi', 'storage': '10000000Gi'}
    bb.create_job(DOCKER_ROOT_IMAGE, ['true'], resources=resources)
    with pytest.raises(httpx.ClientResponseError, match='resource requests.*unsatisfiable'):
        bb.submit()

    bb = client.create_batch()
    resources = {'storage': '10000000Gi', 'machine_type': smallest_machine_type(CLOUD)}
    bb.create_job(DOCKER_ROOT_IMAGE, ['true'], resources=resources)
    with pytest.raises(httpx.ClientResponseError, match='resource requests.*unsatisfiable'):
        bb.submit()

def test_out_of_memory(client: BatchClient):
    bb = client.create_batch()
    resources = {'cpu': '0.25', 'memory': '10M', 'storage': '10Gi'}
    j = bb.create_job('python:3.6-slim-stretch', ['python', '-c', 'x = "a" * 1000**3'], resources=resources)
    b = bb.submit()
    status = j.wait()
    assert j._get_out_of_memory(status, 'main'), str((status, b.debug_info()))

def test_out_of_storage(client: BatchClient):
    bb = client.create_batch()
    resources = {'cpu': '0.25', 'memory': '10M', 'storage': '5Gi'}
    j = bb.create_job(DOCKER_ROOT_IMAGE, ['/bin/sh', '-c', 'fallocate -l 100GiB /foo'], resources=resources)
    b = bb.submit()
    status = j.wait()
    assert status['state'] == 'Failed', str((status, b.debug_info()))
    job_log = j.log()
    assert "fallocate failed: No space left on device" in job_log['main']

def test_quota_applies_to_volume(client: BatchClient):
    bb = client.create_batch()
    resources = {'cpu': '0.25', 'memory': '10M', 'storage': '5Gi'}
    j = bb.create_job(
        os.environ['HAIL_VOLUME_IMAGE'], ['/bin/sh', '-c', 'fallocate -l 100GiB /data/foo'], resources=resources
    )
    b = bb.submit()
    status = j.wait()
    assert status['state'] == 'Failed', str((status, b.debug_info()))
    job_log = j.log()
    assert "fallocate failed: No space left on device" in job_log['main']

def test_quota_shared_by_io_and_rootfs(client: BatchClient):
    bb = client.create_batch()
    resources = {'cpu': '0.25', 'memory': '10M', 'storage': '10Gi'}
    j = bb.create_job(DOCKER_ROOT_IMAGE, ['/bin/sh', '-c', 'fallocate -l 7GiB /foo'], resources=resources)
    b = bb.submit()
    status = j.wait()
    assert status['state'] == 'Success', str((status, b.debug_info()))

    bb = client.create_batch()
    resources = {'cpu': '0.25', 'memory': '10M', 'storage': '10Gi'}
    j = bb.create_job(DOCKER_ROOT_IMAGE, ['/bin/sh', '-c', 'fallocate -l 7GiB /io/foo'], resources=resources)
    b = bb.submit()
    status = j.wait()
    assert status['state'] == 'Success', str((status, b.debug_info()))

    bb = client.create_batch()
    resources = {'cpu': '0.25', 'memory': '10M', 'storage': '10Gi'}
    j = bb.create_job(
        DOCKER_ROOT_IMAGE,
        ['/bin/sh', '-c', 'fallocate -l 7GiB /foo; fallocate -l 7GiB /io/foo'],
        resources=resources,
    )
    b = bb.submit()
    status = j.wait()
    assert status['state'] == 'Failed', str((status, b.debug_info()))
    job_log = j.log()
    assert "fallocate failed: No space left on device" in job_log['main'], str((job_log, b.debug_info()))

def test_nonzero_storage(client: BatchClient):
    bb = client.create_batch()
    resources = {'cpu': '0.25', 'memory': '10M', 'storage': '20Gi'}
    j = bb.create_job(UBUNTU_IMAGE, ['/bin/sh', '-c', 'true'], resources=resources)
    b = bb.submit()
    status = j.wait()
    assert status['state'] == 'Success', str((status, b.debug_info()))

@skip_in_azure()
def test_attached_disk(client: BatchClient):
    bb = client.create_batch()
    resources = {'cpu': '0.25', 'memory': '10M', 'storage': '400Gi'}
    j = bb.create_job(UBUNTU_IMAGE, ['/bin/sh', '-c', 'df -h; fallocate -l 390GiB /io/foo'], resources=resources)
    b = bb.submit()
    status = j.wait()
    assert status['state'] == 'Success', str((status, b.debug_info()))

def test_cwd_from_image_workdir(client: BatchClient):
    bb = client.create_batch()
    j = bb.create_job(os.environ['HAIL_WORKDIR_IMAGE'], ['/bin/sh', '-c', 'pwd'])
    b = bb.submit()
    status = j.wait()
    assert status['state'] == 'Success', str((status, b.debug_info()))
    job_log = j.log()
    assert "/work" in job_log['main'], str((job_log, b.debug_info()))

def test_unsubmitted_state(client: BatchClient):
    bb = client.create_batch()
    j = bb.create_job(DOCKER_ROOT_IMAGE, ['echo', 'test'])

    with pytest.raises(ValueError):
        j.batch_id
    with pytest.raises(ValueError):
        j.id
    with pytest.raises(ValueError):
        j.status()
    with pytest.raises(ValueError):
        j.is_complete()
    with pytest.raises(ValueError):
        j.log()
    with pytest.raises(ValueError):
        j.wait()

def test_list_batches(client: BatchClient):
    tag = secrets.token_urlsafe(64)
    bb1 = client.create_batch(attributes={'tag': tag, 'name': 'b1'})
    bb1.create_job(DOCKER_ROOT_IMAGE, ['sleep', '3600'])
    b1 = bb1.submit()

    bb2 = client.create_batch(attributes={'tag': tag, 'name': 'b2'})
    bb2.create_job(DOCKER_ROOT_IMAGE, ['echo', 'test'])
    b2 = bb2.submit()

    batch_id_test_universe = {b1.id, b2.id}

    def assert_batch_ids(expected: Set[int], q=None):
        assert expected.issubset(batch_id_test_universe)
        max_id = max(batch_id_test_universe)
        min_id = min(batch_id_test_universe)
        span = max_id - min_id + 1
        # list_batches returns all batches for all prev run tests so we set a limit
        batches = client.list_batches(q, last_batch_id=max_id + 1, limit=span)
        full_actual = {b.id for b in batches}
        actual = full_actual.intersection(batch_id_test_universe)
        assert actual == expected, str((full_actual, max_id, span, b1.debug_info(), b2.debug_info()))

    assert_batch_ids({b1.id, b2.id})

    assert_batch_ids({b1.id, b2.id}, f'tag={tag}')

    b2.wait()

    assert_batch_ids({b1.id}, f'!complete tag={tag}')
    assert_batch_ids({b2.id}, f'complete tag={tag}')

    assert_batch_ids({b1.id}, f'!success tag={tag}')
    assert_batch_ids({b2.id}, f'success tag={tag}')

    b1.cancel()
    b1.wait()

    assert_batch_ids({b1.id}, f'!success tag={tag}')
    assert_batch_ids({b2.id}, f'success tag={tag}')

    assert_batch_ids(set(), f'!complete tag={tag}')
    assert_batch_ids({b1.id, b2.id}, f'complete tag={tag}')

    assert_batch_ids({b2.id}, f'tag={tag} name=b2')

def test_list_jobs(client: BatchClient):
    bb = client.create_batch()
    j_success = bb.create_job(DOCKER_ROOT_IMAGE, ['true'])
    j_failure = bb.create_job(DOCKER_ROOT_IMAGE, ['false'])
    j_error = bb.create_job(DOCKER_ROOT_IMAGE, ['sleep 5'], attributes={'tag': 'bar'})
    j_running = bb.create_job(DOCKER_ROOT_IMAGE, ['sleep', '1800'], attributes={'tag': 'foo'})

    b = bb.submit()
    j_success.wait()
    j_failure.wait()
    j_error.wait()

    def assert_job_ids(expected, q=None):
        jobs = b.jobs(q=q)
        actual = set([j['job_id'] for j in jobs])
        assert actual == expected, str((jobs, b.debug_info()))

    assert_job_ids({j_success.job_id}, 'success')
    assert_job_ids({j_success.job_id, j_failure.job_id, j_error.job_id}, 'done')
    assert_job_ids({j_running.job_id}, '!done')
    assert_job_ids({j_running.job_id}, 'tag=foo')
    assert_job_ids({j_error.job_id, j_running.job_id}, 'has:tag')
    assert_job_ids({j_success.job_id, j_failure.job_id, j_error.job_id, j_running.job_id}, None)

    b.cancel()

def test_include_jobs(client: BatchClient):
    bb1 = client.create_batch()
    for _ in range(2):
        bb1.create_job(DOCKER_ROOT_IMAGE, ['true'])
    b1 = bb1.submit()
    s = b1.status()
    assert 'jobs' not in s, str((s, b1.debug_info()))

def test_fail(client: BatchClient):
    bb = client.create_batch()
    j = bb.create_job(DOCKER_ROOT_IMAGE, ['false'])
    b = bb.submit()
    status = j.wait()
    assert j._get_exit_code(status, 'main') == 1, str((status, b.debug_info()))

def test_unknown_image(client: BatchClient):
    bb = client.create_batch()
    j = bb.create_job(f'{DOCKER_PREFIX}/does-not-exist', ['echo', 'test'])
    b = bb.submit()
    status = j.wait()
    try:
        assert j._get_exit_code(status, 'main') is None
        assert status['status']['container_statuses']['main']['short_error'] == 'image not found', str(
            (status, b.debug_info())
        )
    except Exception as e:
        raise AssertionError(str((status, b.debug_info())), e)

@skip_in_azure
def test_invalid_gcr(client: BatchClient):
    b = client.create_batch()
    # GCP projects can't be strictly numeric
    j = b.create_job(f'gcr.io/1/does-not-exist', ['echo', 'test'])
    b = b.submit()
    status = j.wait()
    try:
        assert j._get_exit_code(status, 'main') is None
        assert status['status']['container_statuses']['main']['short_error'] == 'image repository is invalid', str(
            (status, b.debug_info())
        )
    except Exception as e:
        raise AssertionError(str((status, b.debug_info())), e)

def test_running_job_log_and_status(client: BatchClient):
    bb = client.create_batch()
    j = bb.create_job(DOCKER_ROOT_IMAGE, ['sleep', '300'])
    b = bb.submit()

    while True:
        if j.status()['state'] == 'Running' or j.is_complete():
            break

    j.log()
    # FIXME after batch1 goes away, check running status
    b.cancel()

def test_deleted_job_log(client: BatchClient):
    bb = client.create_batch()
    j = bb.create_job(DOCKER_ROOT_IMAGE, ['echo', 'test'])
    b = bb.submit()
    j.wait()
    b.delete()

    try:
        j.log()
    except httpx.ClientResponseError as e:
        if e.status == 404:
            pass
        else:
            assert False, str((e, b.debug_info()))

def test_delete_batch(client: BatchClient):
    bb = client.create_batch()
    j = bb.create_job(DOCKER_ROOT_IMAGE, ['sleep', '30'])
    b = bb.submit()
    b.delete()

    # verify doesn't exist
    try:
        client.get_job(*j.id)
    except httpx.ClientResponseError as e:
        if e.status == 404:
            pass
        else:
            raise

def test_cancel_batch(client: BatchClient):
    bb = client.create_batch()
    j = bb.create_job(DOCKER_ROOT_IMAGE, ['sleep', '30'])
    b = bb.submit()

    status = j.status()
    assert status['state'] in ('Ready', 'Running'), str((status, b.debug_info()))

    b.cancel()

    status = j.wait()
    assert status['state'] == 'Cancelled', str((status, b.debug_info()))
    assert 'log' not in status, str((status, b.debug_info()))

    # cancelled job has no log
    try:
        j.log()
    except httpx.ClientResponseError as e:
        if e.status == 404:
            pass
        else:
            raise

def test_get_nonexistent_job(client: BatchClient):
    try:
        client.get_job(1, 666)
    except httpx.ClientResponseError as e:
        if e.status == 404:
            pass
        else:
            raise

def test_get_job(client: BatchClient):
    bb = client.create_batch()
    j = bb.create_job(DOCKER_ROOT_IMAGE, ['true'])
    b = bb.submit()

    j2 = client.get_job(*j.id)
    status2 = j2.status()
    assert (status2['batch_id'], status2['job_id']) == j.id, str((status2, b.debug_info()))

def test_batch(client: BatchClient):
    bb = client.create_batch()
    j1 = bb.create_job(DOCKER_ROOT_IMAGE, ['false'])
    j2 = bb.create_job(DOCKER_ROOT_IMAGE, ['sleep', '1'])
    bb.create_job(DOCKER_ROOT_IMAGE, ['sleep', '30'])
    b = bb.submit()

    j1.wait()
    j2.wait()
    b.cancel()
    b.wait()
    bstatus = legacy_batch_status(b)

    assert len(bstatus['jobs']) == 3, str((bstatus, b.debug_info()))
    state_count = collections.Counter([j['state'] for j in bstatus['jobs']])
    n_cancelled = state_count['Cancelled']
    n_complete = state_count['Error'] + state_count['Failed'] + state_count['Success']
    assert n_cancelled <= 1, str((bstatus, b.debug_info()))
    assert n_cancelled + n_complete == 3, str((bstatus, b.debug_info()))

    n_failed = sum([j['exit_code'] > 0 for j in bstatus['jobs'] if j['state'] in ('Failed', 'Error')])
    assert n_failed == 1, str((bstatus, b.debug_info()))

def test_batch_status(client: BatchClient):
    bb1 = client.create_batch()
    bb1.create_job(DOCKER_ROOT_IMAGE, ['true'])
    b1 = bb1.submit()
    b1.wait()
    b1s = b1.status()
    assert b1s['complete'] and b1s['state'] == 'success', str((b1s, b1.debug_info()))

    bb2 = client.create_batch()
    bb2.create_job(DOCKER_ROOT_IMAGE, ['false'])
    bb2.create_job(DOCKER_ROOT_IMAGE, ['true'])
    b2 = bb2.submit()
    b2.wait()
    b2s = b2.status()
    assert b2s['complete'] and b2s['state'] == 'failure', str((b2s, b2.debug_info()))

    bb3 = client.create_batch()
    bb3.create_job(DOCKER_ROOT_IMAGE, ['sleep', '30'])
    b3 = bb3.submit()
    b3s = b3.status()
    assert not b3s['complete'] and b3s['state'] == 'running', str((b3s, b3.debug_info()))
    b3.cancel()

    bb4 = client.create_batch()
    bb4.create_job(DOCKER_ROOT_IMAGE, ['sleep', '30'])
    b4 = bb4.submit()
    b4.cancel()
    b4.wait()
    b4s = b4.status()
    assert b4s['complete'] and b4s['state'] == 'cancelled', str((b4s, b4.debug_info()))

def test_log_after_failing_job(client: BatchClient):
    bb = client.create_batch()
    j = bb.create_job(DOCKER_ROOT_IMAGE, ['/bin/sh', '-c', 'echo test; exit 127'])
    b = bb.submit()
    status = j.wait()
    assert 'attributes' not in status, str((status, b.debug_info()))
    assert status['state'] == 'Failed', str((status, b.debug_info()))
    assert j._get_exit_code(status, 'main') == 127, str((status, b.debug_info()))

    job_log = j.log()
    assert job_log['main'] == 'test\n', str((job_log, b.debug_info()))

    assert j.is_complete(), str(b.debug_info())

def test_long_log_line(client: BatchClient):
    bb = client.create_batch()
    j = bb.create_job(DOCKER_ROOT_IMAGE, ['/bin/sh', '-c', 'for _ in {0..70000}; do echo -n a; done'])
    b = bb.submit()
    status = j.wait()
    assert status['state'] == 'Success', str((status, b.debug_info()))

def test_authorized_users_only():
    session = external_requests_client_session()
    endpoints = [
        (session.get, '/api/v1alpha/billing_projects', 401),
        (session.get, '/api/v1alpha/billing_projects/foo', 401),
        (session.post, '/api/v1alpha/billing_projects/foo/users/foo/add', 401),
        (session.post, '/api/v1alpha/billing_projects/foo/users/foo/remove', 401),
        (session.post, '/api/v1alpha/billing_projects/foo/create', 401),
        (session.post, '/api/v1alpha/billing_projects/foo/close', 401),
        (session.post, '/api/v1alpha/billing_projects/foo/reopen', 401),
        (session.post, '/api/v1alpha/billing_projects/foo/delete', 401),
        (session.post, '/api/v1alpha/billing_limits/foo/edit', 401),
        (session.get, '/api/v1alpha/batches/0/jobs/0', 401),
        (session.get, '/api/v1alpha/batches/0/jobs/0/log', 401),
        (session.get, '/api/v1alpha/batches', 401),
        (session.post, '/api/v1alpha/batches/create', 401),
        (session.post, '/api/v1alpha/batches/0/jobs/create', 401),
        (session.get, '/api/v1alpha/batches/0', 401),
        (session.delete, '/api/v1alpha/batches/0', 401),
        (session.patch, '/api/v1alpha/batches/0/close', 401),
        # redirect to auth/login
        (session.get, '/batches', 302),
        (session.get, '/batches/0', 302),
        (session.post, '/batches/0/cancel', 401),
        (session.get, '/batches/0/jobs/0', 302),
    ]
    for method, url, expected in endpoints:
        full_url = deploy_config.url('batch', url)
        r = retry_response_returning_functions(method, full_url, allow_redirects=False)
        assert r.status_code == expected, (full_url, r, expected)

def test_cloud_image(client: BatchClient):
    bb = client.create_batch()
    j = bb.create_job(os.environ['HAIL_CURL_IMAGE'], ['echo', 'test'])
    b = bb.submit()
    status = j.wait()
    assert status['state'] == 'Success', str((status, b.debug_info()))

def test_service_account(client: BatchClient):
    bb = client.create_batch()
    j = bb.create_job(
        os.environ['CI_UTILS_IMAGE'],
        ['/bin/sh', '-c', 'kubectl version'],
        service_account={'namespace': NAMESPACE, 'name': 'test-batch-sa'},
    )
    b = bb.submit()
    status = j.wait()
    assert j._get_exit_code(status, 'main') == 0, str((status, b.debug_info()))

def test_port(client: BatchClient):
    bb = client.create_batch()
    bb.create_job(
        DOCKER_ROOT_IMAGE,
        [
            'bash',
            '-c',
            '''
echo $HAIL_BATCH_WORKER_PORT
echo $HAIL_BATCH_WORKER_IP
''',
        ],
        port=5000,
    )
    b = bb.submit()
    batch = b.wait()
    assert batch['state'] == 'success', str((batch, b.debug_info()))

def test_timeout(client: BatchClient):
    bb = client.create_batch()
    j = bb.create_job(DOCKER_ROOT_IMAGE, ['sleep', '30'], timeout=5)
    b = bb.submit()
    status = j.wait()
    assert status['state'] == 'Error', str((status, b.debug_info()))
    error_msg = j._get_error(status, 'main')
    assert error_msg and 'ContainerTimeoutError' in error_msg, str((error_msg, b.debug_info()))
    assert j.exit_code(status) is None, str((status, b.debug_info()))

def test_client_max_size(client: BatchClient):
    bb = client.create_batch()
    for _ in range(4):
        bb.create_job(DOCKER_ROOT_IMAGE, ['echo', 'a' * (900 * 1024)])
    bb.submit()

def test_restartable_insert(client: BatchClient):
    i = 0

    def every_third_time():
        nonlocal i
        i += 1
        if i % 3 == 0:
            return True
        return False

    with FailureInjectingClientSession(every_third_time) as session:
        client = BatchClient('test', session=session)
        bb = client.create_batch()

        for _ in range(9):
            bb.create_job(DOCKER_ROOT_IMAGE, ['echo', 'a'])

        b = bb.submit(max_bunch_size=1)
        b = client.get_batch(b.id)  # get a batch untainted by the FailureInjectingClientSession
        status = b.wait()
        assert status['state'] == 'success', str((status, b.debug_info()))
        jobs = list(b.jobs())
        assert len(jobs) == 9, str((jobs, b.debug_info()))

def test_create_idempotence(client: BatchClient):
    token = secrets.token_urlsafe(32)
    bb1 = client.create_batch(token=token)
    bb2 = client.create_batch(token=token)
    b1 = bb1._open_batch()
    b2 = bb2._open_batch()
    assert b1.id == b2.id

def test_batch_create_validation():
    bad_configs = [
        # unexpected field fleep
        {'billing_project': 'foo', 'n_jobs': 5, 'token': 'baz', 'fleep': 'quam'},
        # billing project None/missing
        {'billing_project': None, 'n_jobs': 5, 'token': 'baz'},
        {'n_jobs': 5, 'token': 'baz'},
        # n_jobs None/missing
        {'billing_project': 'foo', 'n_jobs': None, 'token': 'baz'},
        {'billing_project': 'foo', 'token': 'baz'},
        # n_jobs wrong type
        {'billing_project': 'foo', 'n_jobs': '5', 'token': 'baz'},
        # token None/missing
        {'billing_project': 'foo', 'n_jobs': 5, 'token': None},
        {'billing_project': 'foo', 'n_jobs': 5},
        # empty gcsfuse bucket name
        {
            'billing_project': 'foo',
            'n_jobs': 5,
            'token': 'baz',
            'gcsfuse': [{'bucket': '', 'mount_path': '/bucket', 'read_only': False}],
        },
        # empty gcsfuse mount_path name
        {
            'billing_project': 'foo',
            'n_jobs': 5,
            'token': 'baz',
            'gcsfuse': [{'bucket': 'foo', 'mount_path': '', 'read_only': False}],
        },
        # attribute key/value None
        {'attributes': {'k': None}, 'billing_project': 'foo', 'n_jobs': 5, 'token': 'baz'},
    ]
    url = deploy_config.url('batch', '/api/v1alpha/batches/create')
    headers = service_auth_headers(deploy_config, 'batch')
    session = external_requests_client_session()
    for config in bad_configs:
        r = retry_response_returning_functions(session.post, url, json=config, allow_redirects=True, headers=headers)
        assert r.status_code == 400, (config, r)

def test_duplicate_parents(client: BatchClient):
    bb = client.create_batch()
    head = bb.create_job(DOCKER_ROOT_IMAGE, command=['echo', 'head'])
    bb.create_job(DOCKER_ROOT_IMAGE, command=['echo', 'tail'], parents=[head, head])
    try:
        batch = bb.submit()
    except httpx.ClientResponseError as e:
        assert e.status == 400
    else:
        assert False, f'should receive a 400 Bad Request {batch.id}'

@skip_in_azure()
def test_verify_no_access_to_google_metadata_server(client: BatchClient):
    bb = client.create_batch()
    j = bb.create_job(os.environ['HAIL_CURL_IMAGE'], ['curl', '-fsSL', 'metadata.google.internal', '--max-time', '10'])
    b = bb.submit()
    status = j.wait()
    assert status['state'] == 'Failed', str((status, b.debug_info()))
    job_log = j.log()
    assert "Could not resolve host" in job_log['main'], str((job_log, b.debug_info()))

def test_verify_no_access_to_metadata_server(client: BatchClient):
    bb = client.create_batch()
    j = bb.create_job(os.environ['HAIL_CURL_IMAGE'], ['curl', '-fsSL', '169.254.169.254', '--max-time', '10'])
    b = bb.submit()
    status = j.wait()
    assert status['state'] == 'Failed', str((status, b.debug_info()))
    job_log = j.log()
    assert "Connection timed out" in job_log['main'], str((job_log, b.debug_info()))

def test_submit_batch_in_job(client: BatchClient):
    bb = client.create_batch()
    remote_tmpdir = get_user_config().get('batch', 'remote_tmpdir')
    script = f'''import hailtop.batch as hb
backend = hb.ServiceBackend("test", remote_tmpdir="{remote_tmpdir}")
b = hb.Batch(backend=backend)
j = b.new_bash_job()
j.command("echo hi")
b.run()
backend.close()
'''
    j = bb.create_job(
        os.environ['HAIL_HAIL_BASE_IMAGE'],
        ['/bin/bash', '-c', f'''python3 -c \'{script}\''''],
        mount_tokens=True,
    )
    b = bb.submit()
    status = j.wait()
    assert status['state'] == 'Success', str((status, b.debug_info()))

def test_cant_submit_to_default_with_other_ns_creds(client: BatchClient):
    remote_tmpdir = get_user_config().get('batch', 'remote_tmpdir')
    script = f'''import hailtop.batch as hb
backend = hb.ServiceBackend("test", remote_tmpdir="{remote_tmpdir}")
b = hb.Batch(backend=backend)
j = b.new_bash_job()
j.command("echo hi")
b.run()
backend.close()
'''

    bb = client.create_batch()
    j = bb.create_job(
        os.environ['HAIL_HAIL_BASE_IMAGE'],
        [
            '/bin/bash',
            '-c',
            f'''
hailctl config set domain {DOMAIN}
rm /deploy-config/deploy-config.json
python3 -c \'{script}\'''',
        ],
        mount_tokens=True,
    )
    b = bb.submit()
    status = j.wait()
    if NAMESPACE == 'default':
        assert status['state'] == 'Success', str((status, b.debug_info()))
    else:
        assert status['state'] == 'Failed', str((status, b.debug_info()))
        assert "Please log in" in j.log()['main'], (str(j.log()['main']), status)

    bb = client.create_batch()
    j = bb.create_job(
        os.environ['HAIL_HAIL_BASE_IMAGE'],
        [
            '/bin/bash',
            '-c',
            f'''
jq '.default_namespace = "default"' /deploy-config/deploy-config.json > tmp.json
mv tmp.json /deploy-config/deploy-config.json
python3 -c \'{script}\'''',
        ],
        mount_tokens=True,
    )
    b = bb.submit()
    status = j.wait()
    if NAMESPACE == 'default':
        assert status['state'] == 'Success', str((status, b.debug_info()))
    else:
        assert status['state'] == 'Failed', str((status, b.debug_info()))
        job_log = j.log()
        assert "Please log in" in job_log['main'], str((job_log, b.debug_info()))

def test_cannot_contact_other_internal_ips(client: BatchClient):
    internal_ips = [f'10.128.0.{i}' for i in (10, 11, 12)]
    bb = client.create_batch()
    script = f'''
if [ "$HAIL_BATCH_WORKER_IP" != "{internal_ips[0]}" ] && ! grep -Fq {internal_ips[0]} /etc/hosts; then
    OTHER_IP={internal_ips[0]}
elif [ "$HAIL_BATCH_WORKER_IP" != "{internal_ips[1]}" ] && ! grep -Fq {internal_ips[1]} /etc/hosts; then
    OTHER_IP={internal_ips[1]}
else
    OTHER_IP={internal_ips[2]}
fi

curl -fsSL -m 5 $OTHER_IP
'''
    j = bb.create_job(os.environ['HAIL_CURL_IMAGE'], ['/bin/bash', '-c', script], port=5000)
    b = bb.submit()
    status = j.wait()
    assert status['state'] == 'Failed', str((status, b.debug_info()))
    job_log = j.log()
    assert "Connection timed out" in job_log['main'], str((job_log, b.debug_info()))

@skip_in_azure()
def test_can_use_google_credentials(client: BatchClient):
    token = os.environ["HAIL_TOKEN"]
    remote_tmpdir = get_user_config().get('batch', 'remote_tmpdir')
    bb = client.create_batch()
    script = f'''import hail as hl
import secrets
attempt_token = secrets.token_urlsafe(5)
location = f"{remote_tmpdir}/{ token }/{{ attempt_token }}/test_can_use_hailctl_auth.t"
hl.utils.range_table(10).write(location)
hl.read_table(location).show()
'''
    j = bb.create_job(
        os.environ['HAIL_HAIL_BASE_IMAGE'], ['/bin/bash', '-c', f'python3 -c >out 2>err \'{script}\'; cat out err']
    )
    b = bb.submit()
    status = j.wait()
    assert status['state'] == 'Success', f'{j.log(), status}'
    expected_log = '''+-------+
|   idx |
+-------+
| int32 |
+-------+
|     0 |
|     1 |
|     2 |
|     3 |
|     4 |
|     5 |
|     6 |
|     7 |
|     8 |
|     9 |
+-------+
'''
    log = j.log()
    assert expected_log in log['main'], str((log, b.debug_info()))

def test_user_authentication_within_job(client: BatchClient):
    bb = client.create_batch()
    cmd = ['bash', '-c', 'hailctl auth user']
    no_token = bb.create_job(os.environ['CI_UTILS_IMAGE'], cmd, mount_tokens=False)
    b = bb.submit()

    no_token_status = no_token.wait()
    assert no_token_status['state'] == 'Failed', str((no_token_status, b.debug_info()))

def test_verify_access_to_public_internet(client: BatchClient):
    bb = client.create_batch()
    j = bb.create_job(os.environ['HAIL_CURL_IMAGE'], ['curl', '-fsSL', 'example.com'])
    b = bb.submit()
    status = j.wait()
    assert status['state'] == 'Success', str((status, b.debug_info()))

def test_verify_can_tcp_to_localhost(client: BatchClient):
    bb = client.create_batch()
    script = '''
set -e
nc -l -p 5000 &
sleep 5
echo "hello" | nc -q 1 localhost 5000
'''.lstrip(
        '\n'
    )
    j = bb.create_job(os.environ['HAIL_NETCAT_UBUNTU_IMAGE'], command=['/bin/bash', '-c', script])
    b = bb.submit()
    status = j.wait()
    assert status['state'] == 'Success', str((status, b.debug_info()))
    job_log = j.log()
    assert 'hello\n' == job_log['main'], str((job_log, b.debug_info()))

def test_verify_can_tcp_to_127_0_0_1(client: BatchClient):
    bb = client.create_batch()
    script = '''
set -e
nc -l -p 5000 &
sleep 5
echo "hello" | nc -q 1 127.0.0.1 5000
'''.lstrip(
        '\n'
    )
    j = bb.create_job(os.environ['HAIL_NETCAT_UBUNTU_IMAGE'], command=['/bin/bash', '-c', script])
    b = bb.submit()
    status = j.wait()
    assert status['state'] == 'Success', str((status, b.debug_info()))
    job_log = j.log()
    assert 'hello\n' == job_log['main'], str((job_log, b.debug_info()))

def test_verify_can_tcp_to_self_ip(client: BatchClient):
    bb = client.create_batch()
    script = '''
set -e
nc -l -p 5000 &
sleep 5
echo "hello" | nc -q 1 $(hostname -i) 5000
'''.lstrip(
        '\n'
    )
    j = bb.create_job(os.environ['HAIL_NETCAT_UBUNTU_IMAGE'], command=['/bin/sh', '-c', script])
    b = bb.submit()
    status = j.wait()
    assert status['state'] == 'Success', str((status, b.debug_info()))
    job_log = j.log()
    assert 'hello\n' == job_log['main'], str((job_log, b.debug_info()))

def test_verify_private_network_is_restricted(client: BatchClient):
    bb = client.create_batch()
    bb.create_job(
        os.environ['HAIL_CURL_IMAGE'], command=['curl', 'internal.hail', '--connect-timeout', '60'], network='private'
    )
    try:
        bb.submit()
    except httpx.ClientResponseError as err:
        assert err.status == 400
        assert 'unauthorized network private' in err.body
    else:
        assert False

def test_pool_highmem_instance(client: BatchClient):
    bb = client.create_batch()
    resources = {'cpu': '0.25', 'memory': 'highmem'}
    j = bb.create_job(DOCKER_ROOT_IMAGE, ['true'], resources=resources)
    b = bb.submit()
    status = j.wait()
    assert status['state'] == 'Success', str((status, b.debug_info()))
    assert 'highmem' in status['status']['worker'], str((status, b.debug_info()))

def test_pool_highmem_instance_cheapest(client: BatchClient):
    bb = client.create_batch()
    resources = {'cpu': '1', 'memory': '5Gi'}
    j = bb.create_job(DOCKER_ROOT_IMAGE, ['true'], resources=resources)
    b = bb.submit()
    status = j.wait()
    assert status['state'] == 'Success', str((status, b.debug_info()))
    assert 'highmem' in status['status']['worker'], str((status, b.debug_info()))

def test_pool_highcpu_instance(client: BatchClient):
    bb = client.create_batch()
    resources = {'cpu': '0.25', 'memory': 'lowmem'}
    j = bb.create_job(DOCKER_ROOT_IMAGE, ['true'], resources=resources)
    b = bb.submit()
    status = j.wait()
    assert status['state'] == 'Success', str((status, b.debug_info()))
    assert 'highcpu' in status['status']['worker'], str((status, b.debug_info()))

def test_pool_highcpu_instance_cheapest(client: BatchClient):
    bb = client.create_batch()
    resources = {'cpu': '0.25', 'memory': '50Mi'}
    j = bb.create_job(DOCKER_ROOT_IMAGE, ['true'], resources=resources)
    b = bb.submit()
    status = j.wait()
    assert status['state'] == 'Success', str((status, b.debug_info()))
    assert 'highcpu' in status['status']['worker'], str((status, b.debug_info()))

def test_pool_standard_instance(client: BatchClient):
    bb = client.create_batch()
    resources = {'cpu': '0.25', 'memory': 'standard'}
    j = bb.create_job(DOCKER_ROOT_IMAGE, ['true'], resources=resources)
    b = bb.submit()
    status = j.wait()
    assert status['state'] == 'Success', str((status, b.debug_info()))
    assert 'standard' in status['status']['worker'], str((status, b.debug_info()))

def test_pool_standard_instance_cheapest(client: BatchClient):
    bb = client.create_batch()
    resources = {'cpu': '1', 'memory': '2.5Gi'}
    j = bb.create_job(DOCKER_ROOT_IMAGE, ['true'], resources=resources)
    b = bb.submit()
    status = j.wait()
    assert status['state'] == 'Success', str((status, b.debug_info()))
    assert 'standard' in status['status']['worker'], str((status, b.debug_info()))

def test_job_private_instance_preemptible(client: BatchClient):
    bb = client.create_batch()
    resources = {'machine_type': smallest_machine_type(CLOUD)}
    j = bb.create_job(DOCKER_ROOT_IMAGE, ['true'], resources=resources)
    b = bb.submit()
    status = j.wait()
    assert status['state'] == 'Success', str((status, b.debug_info()))
    assert 'job-private' in status['status']['worker'], str((status, b.debug_info()))

def test_job_private_instance_nonpreemptible(client: BatchClient):
    bb = client.create_batch()
    resources = {'machine_type': smallest_machine_type(CLOUD), 'preemptible': False}
    j = bb.create_job(DOCKER_ROOT_IMAGE, ['true'], resources=resources)
    b = bb.submit()
    status = j.wait()
    assert status['state'] == 'Success', str((status, b.debug_info()))
    assert 'job-private' in status['status']['worker'], str((status, b.debug_info()))

def test_job_private_instance_cancel(client: BatchClient):
    bb = client.create_batch()
    resources = {'machine_type': smallest_machine_type(CLOUD)}
    j = bb.create_job(DOCKER_ROOT_IMAGE, ['true'], resources=resources)
    b = bb.submit()

    delay = 0.1
    start = time.time()
    while True:
        status = j.status()
        if status['state'] == 'Creating':
            break
        now = time.time()
        if now + delay - start > 60:
            assert False, str((status, b.debug_info()))
        delay = sync_sleep_and_backoff(delay)
    b.cancel()
    status = j.wait()
    assert status['state'] == 'Cancelled', str((status, b.debug_info()))

def test_update_batch_no_deps(client: BatchClient):
    bb = client.create_batch()
    j1 = bb.create_job(DOCKER_ROOT_IMAGE, ['false'])
    bb.submit()
    j1.wait()

    j2 = bb.create_job(DOCKER_ROOT_IMAGE, ['true'])
    b = bb.submit()
    j2_status = j2.wait()

    assert j2_status['state'] == 'Success', str((j2_status, b.debug_info()))
    assert b.status()['state'] == 'failure', str((b.status(), b.debug_info()))

def test_update_batch_w_submitted_job_deps(client: BatchClient):
    bb = client.create_batch()
    j1 = bb.create_job(DOCKER_ROOT_IMAGE, ['true'])
    b = bb.submit()
    j1.wait()
    j2 = bb.create_job(DOCKER_ROOT_IMAGE, ['true'], parents=[j1])
    b = bb.submit()
    status = j2.wait()

    assert status['state'] == 'Success', str((status, b.debug_info()))
    assert b.status()['state'] == 'success', str((b.status(), b.debug_info()))

def test_update_batch_w_failing_submitted_job_deps(client: BatchClient):
    bb = client.create_batch()
    j1 = bb.create_job(DOCKER_ROOT_IMAGE, ['false'])
    bb.submit()

    j2 = bb.create_job(DOCKER_ROOT_IMAGE, ['true'], parents=[j1])
    b = bb.submit()
    status = j2.wait()

    assert status['state'] == 'Cancelled', str((status, b.debug_info()))
    assert b.status()['state'] == 'failure', str((b.status(), b.debug_info()))

def test_update_batch_w_deps_in_update(client: BatchClient):
    bb = client.create_batch()
    j = bb.create_job(DOCKER_ROOT_IMAGE, ['true'])
    bb.submit()

    j1 = bb.create_job(DOCKER_ROOT_IMAGE, ['true'])
    j2 = bb.create_job(DOCKER_ROOT_IMAGE, ['true'], parents=[j, j1])
    b = bb.submit()
    status = j2.wait()

    assert status['state'] == 'Success', str((status, b.debug_info()))
    assert b.status()['state'] == 'success', str((b.status(), b.debug_info()))
    assert b.status()['n_jobs'] == 3, str(b.debug_info())

def test_update_batch_w_deps_in_update_always_run(client: BatchClient):
    bb = client.create_batch()
    j = bb.create_job(DOCKER_ROOT_IMAGE, ['false'])
    bb.submit()

    j.wait()

    j1 = bb.create_job(DOCKER_ROOT_IMAGE, ['true'], always_run=True)
    j2 = bb.create_job(DOCKER_ROOT_IMAGE, ['true'], parents=[j, j1])
    b = bb.submit()
    j2.wait()

    assert j1.status()['state'] == 'Success', str((j1.status(), b.debug_info()))
    assert j2.status()['state'] == 'Cancelled', str((j2.status(), b.debug_info()))
    assert b.status()['state'] == 'failure', str((b.status(), b.debug_info()))

def test_update_batch_w_failing_deps_in_same_update_and_deps_across_updates(client: BatchClient):
    bb = client.create_batch()
    j = bb.create_job(DOCKER_ROOT_IMAGE, ['true'])
    bb.submit()

    j1 = bb.create_job(DOCKER_ROOT_IMAGE, ['false'], parents=[j])
    j2 = bb.create_job(DOCKER_ROOT_IMAGE, ['true'], parents=[j1])
    b = bb.submit()
    j2_status = j2.wait()

    assert j.status()['state'] == 'Success', str((j.status(), b.debug_info()))
    assert j2_status['state'] == 'Cancelled', str((j2_status, b.debug_info()))
    assert b.status()['state'] == 'failure', str((b.status(), b.debug_info()))

def test_update_with_always_run(client: BatchClient):
    bb = client.create_batch()
    j1 = bb.create_job(DOCKER_ROOT_IMAGE, ['sleep', '3600'])
    b = bb.submit()

    j2 = bb.create_job(DOCKER_ROOT_IMAGE, ['true'], always_run=True, parents=[j1])
    b = bb.submit()

    delay = 0.1
    while True:
        if j1.is_complete():
            assert False, str(j1.status(), b.debug_info())
        if j1.is_running():
            break
        delay = sync_sleep_and_backoff(delay)

    assert j2.is_pending(), str(j2.status(), b.debug_info())

    b.cancel()
    j2.wait()

    assert j2.status()['state'] == 'Success', str((j2.status(), b.debug_info()))
    assert b.status()['state'] == 'cancelled', str((b.status(), b.debug_info()))

def test_update_jobs_are_not_serialized(client: BatchClient):
    bb = client.create_batch()
    j1 = bb.create_job(DOCKER_ROOT_IMAGE, ['sleep', '3600'])
    b = bb.submit()

    j2 = bb.create_job(DOCKER_ROOT_IMAGE, ['true'])
    b = bb.submit()

    j2.wait()

    delay = 0.1
    while True:
        if j1.is_complete():
            assert False, str(j1.status(), b.debug_info())
        if j1.is_running():
            break
        delay = sync_sleep_and_backoff(delay)

    b.cancel()

    assert j2.status()['state'] == 'Success', str((j2.status(), b.debug_info()))
    assert b.status()['state'] == 'cancelled', str((b.status(), b.debug_info()))

def test_update_batch_w_empty_initial_batch(client: BatchClient):
    bb = client.create_batch()
    b = bb.submit()

    bb = client.update_batch(b.id)
    j1 = bb.create_job(DOCKER_ROOT_IMAGE, ['true'])
    b = bb.submit()
    status = j1.wait()

    assert status['state'] == 'Success', str((status, b.debug_info()))

def test_update_batch_w_multiple_empty_updates(client: BatchClient):
    bb = client.create_batch()
    b = bb.submit()
    b = bb.submit()
    j = bb.create_job(DOCKER_ROOT_IMAGE, ['true'])
    b = bb.submit()
    status = b.wait()

    assert status['state'] == 'success', str((status, b.debug_info()))

def test_update_batch_w_new_batch_builder(client: BatchClient):
    bb = client.create_batch()
    b = bb.submit()
    bb = client.update_batch(b.id)
    j = bb.create_job(DOCKER_ROOT_IMAGE, ['true'])
    b = bb.submit()
    status = b.wait()

    assert status['state'] == 'success', str((status, b.debug_info()))

def test_update_batch_wout_fast_path(client: BatchClient):
    bb = client.create_batch()
    bb.submit()
    for _ in range(4):
        bb.create_job(DOCKER_ROOT_IMAGE, ['echo', 'a' * (900 * 1024)])
    b = bb.submit()
    assert not next(iter(b.submission_info.used_fast_update.values()))

def test_update_cancelled_batch_wout_fast_path(client: BatchClient):
    bb = client.create_batch()
    bb.create_job(DOCKER_ROOT_IMAGE, ['sleep', '3600'])
    b = bb.submit()
    b.cancel()

    try:
        for _ in range(4):
            bb.create_job(DOCKER_ROOT_IMAGE, ['echo', 'a' * (900 * 1024)])
        b = bb.submit()
    except httpx.ClientResponseError as err:
        assert err.status == 400
        assert 'Cannot submit new jobs to a cancelled batch' in err.body
    else:
        assert False

def test_submit_update_to_cancelled_batch(client: BatchClient):
    bb = client.create_batch()
    bb.create_job(DOCKER_ROOT_IMAGE, ['sleep', '3600'])
    b = bb.submit()
    b.cancel()

    try:
        bb.create_job(DOCKER_ROOT_IMAGE, ['true'])
        b = bb.submit()
    except httpx.ClientResponseError as err:
        assert err.status == 400
        assert 'Cannot submit new jobs to a cancelled batch' in err.body
    else:
        assert False

def test_submit_update_to_deleted_batch(client: BatchClient):
    bb = client.create_batch()
    b = bb.submit()
    b.cancel()
    b.delete()

    try:
        bb.create_job(DOCKER_ROOT_IMAGE, ['true'])
        b = bb.submit()
    except httpx.ClientResponseError as err:
        assert err.status == 404
    else:
        assert False


text:
var path = require('path');
var subProcess = require('child_process');
var chokidar = require('chokidar');

var CodePlay = function() {
  this.targets = [];
  this.cmdPtn = '';
  this.noneAsArgs = false;
  this.allAsArgs = false;

  this.watcher = null;
};
CodePlay.prototype = {
  'logger': console,
  'log': function() {
    this.logger.log.apply(this.logger, arguments);
  },
  'start': function(options, callback) {
    this.noneAsArgs = options.noneAsArgs;
    this.allAsArgs = options.allAsArgs;
    this.cmdPtn = options.cmdPtn;
    if (options.logger) {
      this.logger = options.logger;
    }

    var self = this;
    this.watcher = chokidar.watch(options.filesPtn);
    this.watcher
      .on('add', function(path) {
        self.targets.push(path);
      })
      .on('ready', function() {
        self.cmdPtn = self.composeCmdWithArgs(self.cmdPtn);
        self.log('start watching');
        self.log(self.targets);
        self.log();
        self.log('with command: ');
        self.log(self.cmdPtn);

        if (callback) {
          callback();
        }
      })
      .on('change', function(path) {
        try {
          self.callback(path);
        } catch (e) {
          self.log('[ERROR] ' + e);
          // self.stop();
        }
      });
  },
  'stop': function() {
    this.watcher.close();
  },
  'composeCmdWithArgs': function(cmdPtn) {
    if (this.noneAsArgs) {
      return cmdPtn;
    } else if (this.allAsArgs) {
      return [cmdPtn].concat(this.targets).join(' ');
    } else {
      return cmdPtn + ' !';
    }
  },
  'completeCmd': function(fn) {
    var cmdPtn = this.cmdPtn;
    if (cmdPtn.indexOf('?') !== -1) {
      cmdPtn = this.guessExecutable(cmdPtn, fn);
    }
    return cmdPtn.replace(/\!/g, fn);
  },
  'guessExecutable': function(cmd, fn) {
    var main = '';
    var ext = path.extname(fn);
    if (ext == '') {
      msg = 'no file extension. please give callback explicitly';
      throw msg;
    } else if (ext == '.py') {
      main = 'python';
    } else if (ext == '.php') {
      main = 'php -f';
    } else if (ext == '.js') {
      main = 'node';
    } else {
      msg = 'no corresponding executable for *' + ext + ' file. ' +
            'please give callback explicitly';
      throw msg;
    }
    return cmd.replace('?', main);
  },
  'callback': function(fn) {
    var self = this;
    var cmd = this.completeCmd(fn);
    subProcess.exec(cmd, function(error, stdout, stderr) {
      if (error) {
        self.log(error);
      } else {
        self.log(stdout);
        self.log(stderr);
      }
    });

    self.log();
    self.log('> (' + new Date().toISOString() + ') ' + cmd);
  }
};

module.exports.start = function(options, callback) {
  var codePlay = new CodePlay();
  codePlay.start(options, callback);
  return codePlay;
};

redpajama_set_name: RedPajamaGithub


text:
package ru.qa.rtsoft.addressbook.model;

import com.google.gson.annotations.Expose;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import com.thoughtworks.xstream.annotations.XStreamOmitField;
import org.hibernate.annotations.Type;

import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;

@XStreamAlias("group")
@Entity
@Table (name = "group_list")
public class GroupData {
  @XStreamOmitField
  @Id
  @Column (name = "group_id")
  private int id = Integer.MAX_VALUE;

  @Expose
  @Column (name = "group_name")
  private String groupname;

  @Expose
  @Column (name = "group_header")
  @Type(type = "text")
  private String groupheader;

  @Expose
  @Column (name = "group_footer")
  @Type(type = "text")
  private String groupfooter;

  @ManyToMany (mappedBy = "groups")
  private Set<UserData> users = new HashSet<UserData>();

  public GroupData withId(int id) {
    this.id = id;
    return this;
  }

  public GroupData withGroupname(String groupname) {
    this.groupname = groupname;
    return this;
  }

  public GroupData withGroupheader(String groupheader) {
    this.groupheader = groupheader;
    return this;
  }

  public GroupData withGroupfooter(String groupfooter) {
    this.groupfooter = groupfooter;
    return this;
  }

  public int getId() {
    return id;
  }

  public String getGroupname() {
    return groupname;
  }

  public String getGroupheader() {
    return groupheader;
  }

  public String getGroupfooter() {
    return groupfooter;
  }

  public Users getUsers() {
    return new Users(users);
  }

  @Override
  public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;

    GroupData groupData = (GroupData) o;

    if (id != groupData.id) return false;
    if (groupname != null ? !groupname.equals(groupData.groupname) : groupData.groupname != null) return false;
    if (groupheader != null ? !groupheader.equals(groupData.groupheader) : groupData.groupheader != null) return false;
    return groupfooter != null ? groupfooter.equals(groupData.groupfooter) : groupData.groupfooter == null;
  }

  @Override
  public int hashCode() {
    int result = id;
    result = 31 * result + (groupname != null ? groupname.hashCode() : 0);
    result = 31 * result + (groupheader != null ? groupheader.hashCode() : 0);
    result = 31 * result + (groupfooter != null ? groupfooter.hashCode() : 0);
    return result;
  }

  @Override
  public String toString() {
    return "GroupData{" +
            "id=" + id +
            ", groupname='" + groupname + '\'' +
            ", groupheader='" + groupheader + '\'' +
            ", groupfooter='" + groupfooter + '\'' +
            '}';
  }
}


text:
var expect = require("chai").expect;
var PorterKeywords = require('../index');

describe('porter-keywords', function() {

    it('Should return all of the expected keywords without stop words', function(done) {

        var keywords = PorterKeywords.extract('this is a tasty burger');

        expect(keywords.length).to.equal(2);

        done();

    });

    it('Should return only the topmost keyword', function(done) {

        var keywords = PorterKeywords.extract('this is a tasty burger', 1);

        expect(keywords.length).to.equal(1);

        done();

    });

});


text:
namespace ap {
  class MenuStateViewport;

  class MainMenuState : public GameState, public GuiMainMenuReceiver{
public:
    MainMenuState( GameStateManager *gameStateManager,
		   Ogre::SceneManager *sceneManager,
		   ap::Gui *gui);
    ~MainMenuState( void );

    void enter( void );
    void exit( void );

    void pause( void ) {};
    void resume( void ) {};
    void update( unsigned long lTimeElapsed ) {};

//--KeyboardListener-(inherited through GameState)-----------------------------
    bool keyPressed( const ooinput::KeyEvent &e );
    bool keyReleased( const ooinput::KeyEvent &e );

    // MouseListener
    bool mousePressed(const ooinput::MouseClickedEvent &e);
    bool mouseReleased(const ooinput::MouseClickedEvent &e);
    bool mouseMoved(const ooinput::MouseMovedEvent &e);

    // GuiMainMenuReceiver
    void receiveQuitGame();
    void receiveConfigureKeyboard();
    void receivePlayGame();

private:
    ap::Gui                     *pGui;
    ap::MenuStateViewport       *mViewport;
    Ogre::SceneManager          *pSceneManager;

    void createGUIWindow();
    void terminateGUIWindow();
};

} // namespace ap
#endif


text:
<!doctype html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<iframe id="i" src="/common/blank.html"></iframe>
<script>
async_test(t => {
  window.onload = t.step_func(() => {
    i.contentWindow.appHistory.updateCurrent({ state: { data: "value" } });
    assert_equals(i.contentWindow.appHistory.entries().length, 1);
    assert_equals(i.contentWindow.appHistory.current.getState().data, "value");

    i.contentWindow.location.reload();
    i.onload = t.step_func_done(() => {
      assert_equals(i.contentWindow.appHistory.entries().length, 1);
      assert_equals(i.contentWindow.appHistory.current.getState().data, "value");
    });
  });
}, "appHistoryEntry.getState() after location.reload()");
</script>


text:
{% extends "other.html" %}
{% block title %} Other - URL encoding{% endblock %}
{% block pagetitle %}Brian R. Bondy - URL encoding{% endblock %}
{% block subtitle %}%20{% endblock %}

{% block main_content %}

Unrelated topics

Select from the list of unrelated topics on the right hand side.

{% endblock %}


text:
package org.elasticsearch.rest.action.admin.indices.upgrade;

import org.elasticsearch.Version;
import org.elasticsearch.action.admin.indices.upgrade.get.UpgradeStatusResponse;
import org.elasticsearch.action.admin.indices.upgrade.post.UpgradeRequest;
import org.elasticsearch.action.admin.indices.upgrade.post.UpgradeResponse;
import org.elasticsearch.client.Client;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.collect.Tuple;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.rest.BaseRestHandler;
import org.elasticsearch.rest.BytesRestResponse;
import org.elasticsearch.rest.RestChannel;
import org.elasticsearch.rest.RestController;
import org.elasticsearch.rest.RestRequest;
import org.elasticsearch.rest.RestResponse;
import org.elasticsearch.rest.action.support.RestBuilderListener;

import java.util.Map;

import static org.elasticsearch.rest.RestRequest.Method.GET;
import static org.elasticsearch.rest.RestRequest.Method.POST;
import static org.elasticsearch.rest.RestStatus.OK;
import static org.elasticsearch.rest.action.support.RestActions.buildBroadcastShardsHeader;

public class RestUpgradeAction extends BaseRestHandler {

    @Inject
    public RestUpgradeAction(Settings settings, RestController controller, Client client) {
        super(settings, controller, client);
        controller.registerHandler(POST, "/_upgrade", this);
        controller.registerHandler(POST, "/{index}/_upgrade", this);

        controller.registerHandler(GET, "/_upgrade", this);
        controller.registerHandler(GET, "/{index}/_upgrade", this);
    }

    @Override
    protected void handleRequest(RestRequest request, RestChannel channel, Client client) throws Exception {
        if (request.method().equals(RestRequest.Method.GET)) {
            handleGet(request, channel, client);
        } else if (request.method().equals(RestRequest.Method.POST)) {
            handlePost(request, channel, client);
        }
    }

    void handleGet(final RestRequest request, RestChannel channel, Client client) {
        client.admin().indices().prepareUpgradeStatus(Strings.splitStringByCommaToArray(request.param("index")))
                .execute(new RestBuilderListener<UpgradeStatusResponse>(channel) {
                    @Override
                    public RestResponse buildResponse(UpgradeStatusResponse response, XContentBuilder builder) throws Exception {
                        builder.startObject();
                        response.toXContent(builder, request);
                        builder.endObject();
                        return new BytesRestResponse(OK, builder);
                    }
                });
    }

    void handlePost(final RestRequest request, RestChannel channel, Client client) {
        UpgradeRequest upgradeReq = new UpgradeRequest(Strings.splitStringByCommaToArray(request.param("index")));
        upgradeReq.upgradeOnlyAncientSegments(request.paramAsBoolean("only_ancient_segments", false));
        client.admin().indices().upgrade(upgradeReq, new RestBuilderListener<UpgradeResponse>(channel) {
            @Override
            public RestResponse buildResponse(UpgradeResponse response, XContentBuilder builder) throws Exception {
                builder.startObject();
                buildBroadcastShardsHeader(builder, request, response);
                builder.startObject("upgraded_indices");
                for (Map.Entry<String, Tuple<Version, String>> entry : response.versions().entrySet()) {
                    builder.startObject(entry.getKey(), XContentBuilder.FieldCaseConversion.NONE);
                    builder.field("upgrade_version", entry.getValue().v1());
                    builder.field("oldest_lucene_segment_version", entry.getValue().v2());
                    builder.endObject();
                }
                builder.endObject();
                builder.endObject();
                return new BytesRestResponse(OK, builder);
            }
        });
    }

}


text:
<?xml version="1.0" encoding="UTF-8"?>
<deploy
 date="2018.01.09.14:30:47"
 outputDirectory="/home/tianzhao/quartus_17_fringe_ref/a10_soc_devkit_ghrd/ip/pr_region_default/pr_region_default_sysid_qsys_0/">
 <perimeter>
  <parameter
     name="AUTO_GENERATION_ID"
     type="Integer"
     defaultValue="0"
     onHdl="0"
     affectsHdl="1" />
  <parameter
     name="AUTO_UNIQUE_ID"
     type="String"
     defaultValue=""
     onHdl="0"
     affectsHdl="1" />
  <parameter
     name="AUTO_DEVICE_FAMILY"
     type="String"
     defaultValue="Arria 10"
     onHdl="0"
     affectsHdl="1" />
  <parameter
     name="AUTO_DEVICE"
     type="String"
     defaultValue="10AS066N3F40E2SG"
     onHdl="0"
     affectsHdl="1" />
  <parameter
     name="AUTO_DEVICE_SPEEDGRADE"
     type="String"
     defaultValue="2"
     onHdl="0"
     affectsHdl="1" />
  <parameter
     name="AUTO_CLK_CLOCK_RATE"
     type="Long"
     defaultValue="-1"
     onHdl="0"
     affectsHdl="1" />
  <parameter
     name="AUTO_CLK_CLOCK_DOMAIN"
     type="Integer"
     defaultValue="-1"
     onHdl="0"
     affectsHdl="1" />
  <parameter
     name="AUTO_CLK_RESET_DOMAIN"
     type="Integer"
     defaultValue="-1"
     onHdl="0"
     affectsHdl="1" />
  <interface name="clk" kind="clock" start="0">
   <property name="clockRate" value="0" />
   <property name="externallyDriven" value="false" />
   <property name="ptfSchematicName" value="" />
   <port name="clock" direction="input" role="clk" width="1" />
  </interface>
  <interface name="control_slave" kind="avalon" start="0">
   <property name="addressAlignment" value="DYNAMIC" />
   <property name="addressGroup" value="0" />
   <property name="addressSpan" value="8" />
   <property name="addressUnits" value="WORDS" />
   <property name="alwaysBurstMaxBurst" value="false" />
   <property name="associatedClock" value="clk" />
   <property name="associatedReset" value="reset" />
   <property name="bitsPerSymbol" value="8" />
   <property name="bridgedAddressOffset" value="0" />
   <property name="bridgesToMaster" value="" />
   <property name="burstOnBurstBoundariesOnly" value="false" />
   <property name="burstcountUnits" value="WORDS" />
   <property name="constantBurstBehavior" value="false" />
   <property name="explicitAddressSpan" value="0" />
   <property name="holdTime" value="0" />
   <property name="interleaveBursts" value="false" />
   <property name="isBigEndian" value="false" />
   <property name="isFlash" value="false" />
   <property name="isMemoryDevice" value="false" />
   <property name="isNonVolatileStorage" value="false" />
   <property name="linewrapBursts" value="false" />
   <property name="maximumPendingReadTransactions" value="0" />
   <property name="maximumPendingWriteTransactions" value="0" />
   <property name="minimumReadLatency" value="1" />
   <property name="minimumResponseLatency" value="1" />
   <property name="minimumUninterruptedRunLength" value="1" />
   <property name="prSafe" value="false" />
   <property name="printableDevice" value="false" />
   <property name="readLatency" value="0" />
   <property name="readWaitStates" value="1" />
   <property name="readWaitTime" value="1" />
   <property name="registerIncomingSignals" value="false" />
   <property name="registerOutgoingSignals" value="false" />
   <property name="setupTime" value="0" />
   <property name="timingUnits" value="Cycles" />
   <property name="transparentBridge" value="false" />
   <property name="waitrequestAllowance" value="0" />
   <property name="wellBehavedWaitrequest" value="false" />
   <property name="writeLatency" value="0" />
   <property name="writeWaitStates" value="0" />
   <property name="writeWaitTime" value="0" />
   <port name="readdata" direction="output" role="readdata" width="32" />
   <port name="address" direction="input" role="address" width="1" />
  </interface>
  <interface name="reset" kind="reset" start="0">
   <property name="associatedClock" value="clk" />
   <property name="synchronousEdges" value="DEASSERT" />
   <port name="reset_n" direction="input" role="reset_n" width="1" />
  </interface>
 </perimeter>
 <entity
   kind="pr_region_default_sysid_qsys_0"
   version="1.0"
   name="pr_region_default_sysid_qsys_0">
  <parameter name="AUTO_CLK_CLOCK_RATE" value="-1" />
  <parameter name="AUTO_GENERATION_ID" value="0" />
  <parameter name="AUTO_DEVICE" value="10AS066N3F40E2SG" />
  <parameter name="AUTO_DEVICE_FAMILY" value="Arria 10" />
  <parameter name="AUTO_CLK_RESET_DOMAIN" value="-1" />
  <parameter name="AUTO_CLK_CLOCK_DOMAIN" value="-1" />
  <parameter name="AUTO_UNIQUE_ID" value="" />
  <parameter name="AUTO_DEVICE_SPEEDGRADE" value="2" />
  <generatedFiles>
   <file
       path="/home/tianzhao/quartus_17_fringe_ref/a10_soc_devkit_ghrd/ip/pr_region_default/pr_region_default_sysid_qsys_0/synth/pr_region_default_sysid_qsys_0.v"
       attributes="CONTAINS_INLINE_CONFIGURATION" />
  </generatedFiles>
  <childGeneratedFiles>
   <file
       path="/home/tianzhao/quartus_17_fringe_ref/a10_soc_devkit_ghrd/ip/pr_region_default/pr_region_default_sysid_qsys_0/synth/pr_region_default_sysid_qsys_0.v"
       attributes="CONTAINS_INLINE_CONFIGURATION" />
  </childGeneratedFiles>
  <sourceFiles>
   <file
       path="/home/tianzhao/quartus_17_fringe_ref/a10_soc_devkit_ghrd/ip/pr_region_default/pr_region_default_sysid_qsys_0.ip" />
  </sourceFiles>
  <childSourceFiles>
   <file
       path="/opt/intelFPGA_pro/17.1/ip/altera/sopc_builder_ip/altera_avalon_sysid_qsys/altera_avalon_sysid_qsys_hw.tcl" />
  </childSourceFiles>
  <messages>
   <message level="Info" culprit="pr_region_default_sysid_qsys_0">"Generating: pr_region_default_sysid_qsys_0"</message>
   <message level="Info" culprit="pr_region_default_sysid_qsys_0">"Generating: altera_avalon_sysid_qsys"</message>
  </messages>
 </entity>
 <entity
   kind="altera_avalon_sysid_qsys"
   version="17.1"
   name="altera_avalon_sysid_qsys">
  <parameter name="TIMESTAMP" value="0" />
  <parameter name="ID_VALUE" value="-87110914" />
  <parameter name="id" value="-87110914" />
  <generatedFiles>
   <file
       path="/home/tianzhao/quartus_17_fringe_ref/a10_soc_devkit_ghrd/ip/pr_region_default/pr_region_default_sysid_qsys_0/altera_avalon_sysid_qsys_171/synth/altera_avalon_sysid_qsys.v"
       attributes="TOP_LEVEL_FILE" />
  </generatedFiles>
  <childGeneratedFiles>
   <file
       path="/home/tianzhao/quartus_17_fringe_ref/a10_soc_devkit_ghrd/ip/pr_region_default/pr_region_default_sysid_qsys_0/altera_avalon_sysid_qsys_171/synth/altera_avalon_sysid_qsys.v"
       attributes="TOP_LEVEL_FILE" />
  </childGeneratedFiles>
  <sourceFiles>
   <file
       path="/opt/intelFPGA_pro/17.1/ip/altera/sopc_builder_ip/altera_avalon_sysid_qsys/altera_avalon_sysid_qsys_hw.tcl" />
  </sourceFiles>
  <childSourceFiles/>
  <instantiator instantiator="pr_region_default_sysid_qsys_0" as="sysid_qsys_0" />
  <messages>
   <message level="Info" culprit="pr_region_default_sysid_qsys_0">"Generating: altera_avalon_sysid_qsys"</message>
  </messages>
 </entity>
</deploy>


text:
package lfs

import (
	"crypto/sha256"
	"encoding/base64"
	"encoding/hex"
	"errors"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"path"
	"regexp"
	"strconv"
	"strings"

	git_model "code.gitea.io/gitea/models/git"
	"code.gitea.io/gitea/models/perm"
	access_model "code.gitea.io/gitea/models/perm/access"
	repo_model "code.gitea.io/gitea/models/repo"
	"code.gitea.io/gitea/models/unit"
	user_model "code.gitea.io/gitea/models/user"
	"code.gitea.io/gitea/modules/context"
	"code.gitea.io/gitea/modules/json"
	lfs_module "code.gitea.io/gitea/modules/lfs"
	"code.gitea.io/gitea/modules/log"
	"code.gitea.io/gitea/modules/setting"
	"code.gitea.io/gitea/modules/storage"

	"github.com/golang-jwt/jwt/v4"
)

// requestContext contain variables from the HTTP request.
type requestContext struct {
	User          string
	Repo          string
	Authorization string
}

// Claims is a JWT Token Claims
type Claims struct {
	RepoID int64
	Op     string
	UserID int64
	jwt.RegisteredClaims
}

// DownloadLink builds a URL to download the object.
func (rc *requestContext) DownloadLink(p lfs_module.Pointer) string {
	return setting.AppURL + path.Join(url.PathEscape(rc.User), url.PathEscape(rc.Repo+".git"), "info/lfs/objects", url.PathEscape(p.Oid))
}

// UploadLink builds a URL to upload the object.
func (rc *requestContext) UploadLink(p lfs_module.Pointer) string {
	return setting.AppURL + path.Join(url.PathEscape(rc.User), url.PathEscape(rc.Repo+".git"), "info/lfs/objects", url.PathEscape(p.Oid), strconv.FormatInt(p.Size, 10))
}

// VerifyLink builds a URL for verifying the object.
func (rc *requestContext) VerifyLink(p lfs_module.Pointer) string {
	return setting.AppURL + path.Join(url.PathEscape(rc.User), url.PathEscape(rc.Repo+".git"), "info/lfs/verify")
}

// CheckAcceptMediaType checks if the client accepts the LFS media type.
func CheckAcceptMediaType(ctx *context.Context) {
	mediaParts := strings.Split(ctx.Req.Header.Get("Accept"), ";")

	if mediaParts[0] != lfs_module.MediaType {
		log.Trace("Calling a LFS method without accepting the correct media type: %s", lfs_module.MediaType)
		writeStatus(ctx, http.StatusUnsupportedMediaType)
		return
	}
}

// DownloadHandler gets the content from the content store
func DownloadHandler(ctx *context.Context) {
	rc := getRequestContext(ctx)
	p := lfs_module.Pointer{Oid: ctx.Params("oid")}

	meta := getAuthenticatedMeta(ctx, rc, p, false)
	if meta == nil {
		return
	}

	// Support resume download using Range header
	var fromByte, toByte int64
	toByte = meta.Size - 1
	statusCode := http.StatusOK
	if rangeHdr := ctx.Req.Header.Get("Range"); rangeHdr != "" {
		regex := regexp.MustCompile(`bytes=(\d+)\-(\d*).*`)
		match := regex.FindStringSubmatch(rangeHdr)
		if len(match) > 1 {
			statusCode = http.StatusPartialContent
			fromByte, _ = strconv.ParseInt(match[1], 10, 32)

			if fromByte >= meta.Size {
				writeStatus(ctx, http.StatusRequestedRangeNotSatisfiable)
				return
			}

			if match[2] != "" {
				_toByte, _ := strconv.ParseInt(match[2], 10, 32)
				if _toByte >= fromByte && _toByte < toByte {
					toByte = _toByte
				}
			}

			ctx.Resp.Header().Set("Content-Range", fmt.Sprintf("bytes %d-%d/%d", fromByte, toByte, meta.Size-fromByte))
			ctx.Resp.Header().Set("Access-Control-Expose-Headers", "Content-Range")
		}
	}

	contentStore := lfs_module.NewContentStore()
	content, err := contentStore.Get(meta.Pointer)
	if err != nil {
		writeStatus(ctx, http.StatusNotFound)
		return
	}
	defer content.Close()

	if fromByte > 0 {
		_, err = content.Seek(fromByte, io.SeekStart)
		if err != nil {
			log.Error("Whilst trying to read LFS OID[%s]: Unable to seek to %d Error: %v", meta.Oid, fromByte, err)

			writeStatus(ctx, http.StatusInternalServerError)
			return
		}
	}

	contentLength := toByte + 1 - fromByte
	ctx.Resp.Header().Set("Content-Length", strconv.FormatInt(contentLength, 10))
	ctx.Resp.Header().Set("Content-Type", "application/octet-stream")

	filename := ctx.Params("filename")
	if len(filename) > 0 {
		decodedFilename, err := base64.RawURLEncoding.DecodeString(filename)
		if err == nil {
			ctx.Resp.Header().Set("Content-Disposition", "attachment; filename=\""+string(decodedFilename)+"\"")
			ctx.Resp.Header().Set("Access-Control-Expose-Headers", "Content-Disposition")
		}
	}

	ctx.Resp.WriteHeader(statusCode)
	if written, err := io.CopyN(ctx.Resp, content, contentLength); err != nil {
		log.Error("Error whilst copying LFS OID[%s] to the response after %d bytes. Error: %v", meta.Oid, written, err)
	}
}

// BatchHandler provides the batch api
func BatchHandler(ctx *context.Context) {
	var br lfs_module.BatchRequest
	if err := decodeJSON(ctx.Req, &br); err != nil {
		log.Trace("Unable to decode BATCH request vars: Error: %v", err)
		writeStatus(ctx, http.StatusBadRequest)
		return
	}

	var isUpload bool
	if br.Operation == "upload" {
		isUpload = true
	} else if br.Operation == "download" {
		isUpload = false
	} else {
		log.Trace("Attempt to BATCH with invalid operation: %s", br.Operation)
		writeStatus(ctx, http.StatusBadRequest)
		return
	}

	rc := getRequestContext(ctx)

	repository := getAuthenticatedRepository(ctx, rc, isUpload)
	if repository == nil {
		return
	}

	contentStore := lfs_module.NewContentStore()

	var responseObjects []*lfs_module.ObjectResponse

	for _, p := range br.Objects {
		if !p.IsValid() {
			responseObjects = append(responseObjects, buildObjectResponse(rc, p, false, false, &lfs_module.ObjectError{
				Code:    http.StatusUnprocessableEntity,
				Message: "Oid or size are invalid",
			}))
			continue
		}

		exists, err := contentStore.Exists(p)
		if err != nil {
			log.Error("Unable to check if LFS OID[%s] exist. Error: %v", p.Oid, rc.User, rc.Repo, err)
			writeStatus(ctx, http.StatusInternalServerError)
			return
		}

		meta, err := git_model.GetLFSMetaObjectByOid(repository.ID, p.Oid)
		if err != nil && err != git_model.ErrLFSObjectNotExist {
			log.Error("Unable to get LFS MetaObject [%s] for %s/%s. Error: %v", p.Oid, rc.User, rc.Repo, err)
			writeStatus(ctx, http.StatusInternalServerError)
			return
		}

		if meta != nil && p.Size != meta.Size {
			responseObjects = append(responseObjects, buildObjectResponse(rc, p, false, false, &lfs_module.ObjectError{
				Code:    http.StatusUnprocessableEntity,
				Message: fmt.Sprintf("Object %s is not %d bytes", p.Oid, p.Size),
			}))
			continue
		}

		var responseObject *lfs_module.ObjectResponse
		if isUpload {
			var err *lfs_module.ObjectError
			if !exists && setting.LFS.MaxFileSize > 0 && p.Size > setting.LFS.MaxFileSize {
				err = &lfs_module.ObjectError{
					Code:    http.StatusUnprocessableEntity,
					Message: fmt.Sprintf("Size must be less than or equal to %d", setting.LFS.MaxFileSize),
				}
			}

			if exists && meta == nil {
				accessible, err := git_model.LFSObjectAccessible(ctx.Doer, p.Oid)
				if err != nil {
					log.Error("Unable to check if LFS MetaObject [%s] is accessible. Error: %v", p.Oid, err)
					writeStatus(ctx, http.StatusInternalServerError)
					return
				}
				if accessible {
					_, err := git_model.NewLFSMetaObject(&git_model.LFSMetaObject{Pointer: p, RepositoryID: repository.ID})
					if err != nil {
						log.Error("Unable to create LFS MetaObject [%s] for %s/%s. Error: %v", p.Oid, rc.User, rc.Repo, err)
						writeStatus(ctx, http.StatusInternalServerError)
						return
					}
				} else {
					exists = false
				}
			}

			responseObject = buildObjectResponse(rc, p, false, !exists, err)
		} else {
			var err *lfs_module.ObjectError
			if !exists || meta == nil {
				err = &lfs_module.ObjectError{
					Code:    http.StatusNotFound,
					Message: http.StatusText(http.StatusNotFound),
				}
			}

			responseObject = buildObjectResponse(rc, p, true, false, err)
		}
		responseObjects = append(responseObjects, responseObject)
	}

	respobj := &lfs_module.BatchResponse{Objects: responseObjects}

	ctx.Resp.Header().Set("Content-Type", lfs_module.MediaType)

	enc := json.NewEncoder(ctx.Resp)
	if err := enc.Encode(respobj); err != nil {
		log.Error("Failed to encode representation as json. Error: %v", err)
	}
}

// UploadHandler receives data from the client and puts it into the content store
func UploadHandler(ctx *context.Context) {
	rc := getRequestContext(ctx)

	p := lfs_module.Pointer{Oid: ctx.Params("oid")}
	var err error
	if p.Size, err = strconv.ParseInt(ctx.Params("size"), 10, 64); err != nil {
		writeStatusMessage(ctx, http.StatusUnprocessableEntity, err.Error())
	}

	if !p.IsValid() {
		log.Trace("Attempt to access invalid LFS OID[%s] in %s/%s", p.Oid, rc.User, rc.Repo)
		writeStatus(ctx, http.StatusUnprocessableEntity)
		return
	}

	repository := getAuthenticatedRepository(ctx, rc, true)
	if repository == nil {
		return
	}

	contentStore := lfs_module.NewContentStore()
	exists, err := contentStore.Exists(p)
	if err != nil {
		log.Error("Unable to check if LFS OID[%s] exist. Error: %v", p.Oid, err)
		writeStatus(ctx, http.StatusInternalServerError)
		return
	}

	uploadOrVerify := func() error {
		if exists {
			accessible, err := git_model.LFSObjectAccessible(ctx.Doer, p.Oid)
			if err != nil {
				log.Error("Unable to check if LFS MetaObject [%s] is accessible. Error: %v", p.Oid, err)
				return err
			}
			if !accessible {
				// The file exists but the user has no access to it.
				// The upload gets verified by hashing and size comparison to prove access to it.
				hash := sha256.New()
				written, err := io.Copy(hash, ctx.Req.Body)
				if err != nil {
					log.Error("Error creating hash. Error: %v", err)
					return err
				}

				if written != p.Size {
					return lfs_module.ErrSizeMismatch
				}
				if hex.EncodeToString(hash.Sum(nil)) != p.Oid {
					return lfs_module.ErrHashMismatch
				}
			}
		} else if err := contentStore.Put(p, ctx.Req.Body); err != nil {
			log.Error("Error putting LFS MetaObject [%s] into content store. Error: %v", p.Oid, err)
			return err
		}
		_, err := git_model.NewLFSMetaObject(&git_model.LFSMetaObject{Pointer: p, RepositoryID: repository.ID})
		return err
	}

	defer ctx.Req.Body.Close()
	if err := uploadOrVerify(); err != nil {
		if errors.Is(err, lfs_module.ErrSizeMismatch) || errors.Is(err, lfs_module.ErrHashMismatch) {
			log.Error("Upload does not match LFS MetaObject [%s]. Error: %v", p.Oid, err)
			writeStatusMessage(ctx, http.StatusUnprocessableEntity, err.Error())
		} else {
			writeStatus(ctx, http.StatusInternalServerError)
		}
		if _, err = git_model.RemoveLFSMetaObjectByOid(repository.ID, p.Oid); err != nil {
			log.Error("Error whilst removing metaobject for LFS OID[%s]: %v", p.Oid, err)
		}
		return
	}

	writeStatus(ctx, http.StatusOK)
}

// VerifyHandler verify oid and its size from the content store
func VerifyHandler(ctx *context.Context) {
	var p lfs_module.Pointer
	if err := decodeJSON(ctx.Req, &p); err != nil {
		writeStatus(ctx, http.StatusUnprocessableEntity)
		return
	}

	rc := getRequestContext(ctx)

	meta := getAuthenticatedMeta(ctx, rc, p, true)
	if meta == nil {
		return
	}

	contentStore := lfs_module.NewContentStore()
	ok, err := contentStore.Verify(meta.Pointer)

	status := http.StatusOK
	if err != nil {
		status = http.StatusInternalServerError
	} else if !ok {
		status = http.StatusNotFound
	}
	writeStatus(ctx, status)
}

func decodeJSON(req *http.Request, v interface{}) error {
	defer req.Body.Close()

	dec := json.NewDecoder(req.Body)
	return dec.Decode(v)
}

func getRequestContext(ctx *context.Context) *requestContext {
	return &requestContext{
		User:          ctx.Params("username"),
		Repo:          strings.TrimSuffix(ctx.Params("reponame"), ".git"),
		Authorization: ctx.Req.Header.Get("Authorization"),
	}
}

func getAuthenticatedMeta(ctx *context.Context, rc *requestContext, p lfs_module.Pointer, requireWrite bool) *git_model.LFSMetaObject {
	if !p.IsValid() {
		log.Info("Attempt to access invalid LFS OID[%s] in %s/%s", p.Oid, rc.User, rc.Repo)
		writeStatusMessage(ctx, http.StatusUnprocessableEntity, "Oid or size are invalid")
		return nil
	}

	repository := getAuthenticatedRepository(ctx, rc, requireWrite)
	if repository == nil {
		return nil
	}

	meta, err := git_model.GetLFSMetaObjectByOid(repository.ID, p.Oid)
	if err != nil {
		log.Error("Unable to get LFS OID[%s] Error: %v", p.Oid, err)
		writeStatus(ctx, http.StatusNotFound)
		return nil
	}

	return meta
}

func getAuthenticatedRepository(ctx *context.Context, rc *requestContext, requireWrite bool) *repo_model.Repository {
	repository, err := repo_model.GetRepositoryByOwnerAndName(rc.User, rc.Repo)
	if err != nil {
		log.Error("Unable to get repository: %s/%s Error: %v", rc.User, rc.Repo, err)
		writeStatus(ctx, http.StatusNotFound)
		return nil
	}

	if !authenticate(ctx, repository, rc.Authorization, false, requireWrite) {
		requireAuth(ctx)
		return nil
	}

	return repository
}

func buildObjectResponse(rc *requestContext, pointer lfs_module.Pointer, download, upload bool, err *lfs_module.ObjectError) *lfs_module.ObjectResponse {
	rep := &lfs_module.ObjectResponse{Pointer: pointer}
	if err != nil {
		rep.Error = err
	} else {
		rep.Actions = make(map[string]*lfs_module.Link)

		header := make(map[string]string)

		if len(rc.Authorization) > 0 {
			header["Authorization"] = rc.Authorization
		}

		if download {
			var link *lfs_module.Link
			if setting.LFS.ServeDirect {
				// If we have a signed url (S3, object storage), redirect to this directly.
				u, err := storage.LFS.URL(pointer.RelativePath(), pointer.Oid)
				if u != nil && err == nil {
					// Presigned url does not need the Authorization header
					// https://github.com/go-gitea/gitea/issues/21525
					delete(header, "Authorization")
					link = &lfs_module.Link{Href: u.String(), Header: header}
				}
			}
			if link == nil {
				link = &lfs_module.Link{Href: rc.DownloadLink(pointer), Header: header}
			}
			rep.Actions["download"] = link
		}
		if upload {
			rep.Actions["upload"] = &lfs_module.Link{Href: rc.UploadLink(pointer), Header: header}

			verifyHeader := make(map[string]string)
			for key, value := range header {
				verifyHeader[key] = value
			}

			// This is only needed to workaround https://github.com/git-lfs/git-lfs/issues/3662
			verifyHeader["Accept"] = lfs_module.MediaType

			rep.Actions["verify"] = &lfs_module.Link{Href: rc.VerifyLink(pointer), Header: verifyHeader}
		}
	}
	return rep
}

func writeStatus(ctx *context.Context, status int) {
	writeStatusMessage(ctx, status, http.StatusText(status))
}

func writeStatusMessage(ctx *context.Context, status int, message string) {
	ctx.Resp.Header().Set("Content-Type", lfs_module.MediaType)
	ctx.Resp.WriteHeader(status)

	er := lfs_module.ErrorResponse{Message: message}

	enc := json.NewEncoder(ctx.Resp)
	if err := enc.Encode(er); err != nil {
		log.Error("Failed to encode error response as json. Error: %v", err)
	}
}

// authenticate uses the authorization string to determine whether
// or not to proceed. This server assumes an HTTP Basic auth format.
func authenticate(ctx *context.Context, repository *repo_model.Repository, authorization string, requireSigned, requireWrite bool) bool {
	accessMode := perm.AccessModeRead
	if requireWrite {
		accessMode = perm.AccessModeWrite
	}

	// ctx.IsSigned is unnecessary here, this will be checked in perm.CanAccess
	perm, err := access_model.GetUserRepoPermission(ctx, repository, ctx.Doer)
	if err != nil {
		log.Error("Unable to GetUserRepoPermission for user %-v in repo %-v Error: %v", ctx.Doer, repository)
		return false
	}

	canRead := perm.CanAccess(accessMode, unit.TypeCode)
	if canRead && (!requireSigned || ctx.IsSigned) {
		return true
	}

	user, err := parseToken(authorization, repository, accessMode)
	if err != nil {
		// Most of these are Warn level - the true internal server errors are logged in parseToken already
		log.Warn("Authentication failure for provided token with Error: %v", err)
		return false
	}
	ctx.Doer = user
	return true
}

func handleLFSToken(tokenSHA string, target *repo_model.Repository, mode perm.AccessMode) (*user_model.User, error) {
	if !strings.Contains(tokenSHA, ".") {
		return nil, nil
	}
	token, err := jwt.ParseWithClaims(tokenSHA, &Claims{}, func(t *jwt.Token) (interface{}, error) {
		if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok {
			return nil, fmt.Errorf("unexpected signing method: %v", t.Header["alg"])
		}
		return setting.LFS.JWTSecretBytes, nil
	})
	if err != nil {
		return nil, nil
	}

	claims, claimsOk := token.Claims.(*Claims)
	if !token.Valid || !claimsOk {
		return nil, fmt.Errorf("invalid token claim")
	}

	if claims.RepoID != target.ID {
		return nil, fmt.Errorf("invalid token claim")
	}

	if mode == perm.AccessModeWrite && claims.Op != "upload" {
		return nil, fmt.Errorf("invalid token claim")
	}

	u, err := user_model.GetUserByID(claims.UserID)
	if err != nil {
		log.Error("Unable to GetUserById[%d]: Error: %v", claims.UserID, err)
		return nil, err
	}
	return u, nil
}

func parseToken(authorization string, target *repo_model.Repository, mode perm.AccessMode) (*user_model.User, error) {
	if authorization == "" {
		return nil, fmt.Errorf("no token")
	}

	parts := strings.SplitN(authorization, " ", 2)
	if len(parts) != 2 {
		return nil, fmt.Errorf("no token")
	}
	tokenSHA := parts[1]
	switch strings.ToLower(parts[0]) {
	case "bearer":
		fallthrough
	case "token":
		return handleLFSToken(tokenSHA, target, mode)
	}
	return nil, fmt.Errorf("token not found")
}

func requireAuth(ctx *context.Context) {
	ctx.Resp.Header().Set("WWW-Authenticate", "Basic realm=gitea-lfs")
	writeStatus(ctx, http.StatusUnauthorized)
}


text:
include(CheckCXXSourceCompiles)

check_cxx_source_compiles("
namespace Outer {
  namespace Inner {
    int i = 0;
  }
}

int main() {
  using namespace Outer::Inner;
  return i;
}"
  HAVE_NAMESPACES)

set(XERCES_HAS_CPP_NAMESPACE ${HAVE_NAMESPACES})


text:
using System;
using System.Linq;
using System.Text;

namespace git.jedinja.monomyo
{
	internal class Bytes
	{
		private readonly byte[] _bytes;

		public Bytes (params byte[] values)
		{
			if (values == null || values.Length < 1)
			{
				throw new ArgumentOutOfRangeException ();
			}
			_bytes = values;
		}

		public int Length {
			get {
				return _bytes.Length;
			}
		}

		public override bool Equals (object obj)
		{
			return obj is Bytes && this._bytes.SequenceEqual ((obj as Bytes)._bytes);
		}

		public override int GetHashCode ()
		{
			return (_bytes.Length > 2 ? _bytes[_bytes.Length - 3] : _bytes[0]) * _bytes.Length;
		}

		public override string ToString ()
		{
			return string.Format ("[Bytes {0}]", ByteArrayToHexString (_bytes));
		}

		public string ToText ()
		{
			return ByteToChar (_bytes);
		}

		public static implicit operator Bytes (byte[] value)
		{
			return new Bytes (value);
		}

		public static explicit operator byte[] (Bytes value)
		{
			return value.ToArray ();
		}

		public static Bytes FromString (string str)
		{
			byte[] b = new byte[str.Length];

			for (int i = 0; i < b.Length; i++)
			{
				b[i] = (byte) str[i];
			}

			return b;
		}

		public byte[] ToArray ()
		{
			return (byte[]) _bytes.Clone ();
		}

		private string ByteArrayToHexString (Byte[] ba)
		{
			StringBuilder hex = new StringBuilder (ba.Length * 2);
			foreach (byte b in ba)
			{
				hex.AppendFormat ("{0:x2} ", b);
			}
			return hex.ToString ();
		}

		private string ByteToChar (Byte[] ba)
		{
			return string.Join (string.Empty, ba.Select (b => ((char) b).ToString ()));
		}
	}
}


text:
<?php
class State extends Eloquent{

	protected $table='estados';
  /*
  *
  ## Un estado puede tiene muchas ciudades
  */
    public function City(){
        return $this->hasMany('ciudad','estado_id');
    }
  /*
  *
  ## Un estado pertenece a una pais
  */
    public function Country(){
        return $this->belongsTo('pais');
    }
}


text:
This tool is used to manipulate an ECC-less EECACHE file.
See "eecache_editor.pl --help" for usage examples.

If you have an EECACHE file with ECC, you can use some of HB tools to remove ECC:
- <hb_repo>/standalone/ffs/ecc/ecc
  - This file is placed in your HB repo when you run "hb prime" and your dev environment is using a
    non-FSP config file.
- src/build/simics/ecc.py

e.g.:
<hb_repo>/standalone/ffs/ecc/ecc --remove EECACHE.bin.ecc --output EECACHE.bin --p8

see "<hb_repo>/standalone/ffs/ecc/ecc --help" for more info.


text:
namespace TomsToolbox.Essentials;

using System;
using System.Collections.Generic;
using System.Reflection;

/// <inheritdoc />
/// <summary>
/// <see cref="T:System.Collections.Generic.IComparer`1" /> implementation using a delegate function to compare the values.
/// </summary>
public class DelegateComparer<T> : IComparer<T>
{
    private readonly Func<T?, T?, int> _comparer;

    /// <summary>
    /// Initializes a new instance of the <see cref="DelegateComparer{T}"/> class.
    /// </summary>
    /// <param name="comparer">The comparer.</param>
    public DelegateComparer(Func<T?, T?, int> comparer)
    {
        _comparer = comparer;
    }

    /// <inheritdoc />
    public int Compare(T? x, T? y)
    {
        if (!typeof(T).GetTypeInfo().IsValueType)
        {
            if (x is null)
                return y is null ? 0 : -1;

            if (y is null)
                return 1;
        }

        return _comparer(x, y);
    }
}


text:
@implementation BalanceDetailCell

+ (instancetype) balanceDetailCellWith:(UITableView *)tableView{
    static NSString *strID = @"balanceDetailCell";
    BalanceDetailCell *cell = [tableView dequeueReusableCellWithIdentifier:strID];
    if (!cell) {
        cell = [[[NSBundle mainBundle] loadNibNamed:@"BalanceDetailCell" owner:nil options:nil] firstObject];
    }
    return cell;
}
- (void)awakeFromNib {
    [super awakeFromNib];
    // Initialization code
}

- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
    [super setSelected:selected animated:animated];

    // Configure the view for the selected state
}

@end


text:
//--------------------------------------------------------------------
// <copyright file="UnsupportedWindowsException.cs" company="MyAPKapp">
//     Copyright (c) MyAPKapp. All rights reserved.
// </copyright>
//--------------------------------------------------------------------
// This open-source project is licensed under Apache License 2.0
//--------------------------------------------------------------------

using System;

namespace MyAPKapp.VistaUIFramework {
    public class UnsupportedWindowsException : Exception {

        public UnsupportedWindowsException() : base() {}
        public UnsupportedWindowsException(string os) : base("It requires" + os + " or later") {}

    }
}


text:
<!DOCTYPE html>

    <meta http-equiv="refresh" content="0;URL=../../../../../../../libc/type.suseconds_t.html">

Redirecting to ../../../../../../../libc/type.suseconds_t.html...


text:
import { TRANSITION_COMPONENT_STATE } from 'binary-ui-styles';
import styled from 'styled-components';

export default styled.div.attrs({
  style: (props) => (props.isChecked
    ? { backgroundColor: props.theme.colorBlue40 }
    : { backgroundColor: props.theme.colorGray80 }
  ),
})`
  border-radius: ${props => props.theme.sizeListItemHeight / 2}px;
  height: ${props => props.theme.sizeListItemHeight / 2}px;
  position: absolute;
  top: 0px;
  transition: ${TRANSITION_COMPONENT_STATE};
  width: ${props => props.theme.sizeListItemHeight}px;
`;


text:
Game Play

            <button class="btn"
                    title="Add Player"
                    data-ng-click="gamePlay.service.addPlayer()"
                    data-ng-disabled="gamePlay.service.getPlayers().length === 4">

            </button>
            <button class="btn"
                    title="End Game"
                    data-ng-click="gamePlay.service.endGame()">

            </button>
            <button class="btn"
                    title="Reset Game"
                    data-ng-click="gamePlay.service.resetGame()">

            </button>

Players

                {{player.id}}

*maximum 4 players at a time

Roll Dice

                {{gamePlay.service.getDiceRoll()}}


text:
using System;
using System.Collections.Generic;
using MVCForum.Domain.DomainModel;

namespace MVCForum.Domain.Interfaces.Services
{
    public interface ILuceneService
    {
        void UpdateIndex();
        bool CheckIndexExists();
        void AddUpdate(LuceneSearchModel luceneSearchModel);
        void DeleteEntireIndex();
        void Delete(Guid id);
        void OptimiseIndex();
        LuceneSearchModel MapToModel(Post post);
        LuceneSearchModel MapToModel(Topic topic);
        IEnumerable<LuceneSearchModel> Search(string searchTerm, string fieldName, bool doFuzzySearch = false);
        IEnumerable<LuceneSearchModel> Search(string searchTerm, string fieldName, int amountToTake, bool doFuzzySearch = false);
        IEnumerable<LuceneSearchModel> Search(string searchTerm, bool doFuzzySearch = false);
        IEnumerable<LuceneSearchModel> GetAll();
        PagedList<LuceneSearchModel> Search(string searchTerm, int pageIndex, int pageSize, bool doFuzzySearch = false);
   }
}


text:
#include "../../stdafx.h"
#include "fontTypes.h"

using namespace std;

class Texture;
class CachedText;

class Font
{
	friend class FontManager;

public:
	Font();
	void Initialize();
	void Dispose();
	FONT_LETTER GetCharacter(char c);
	int GetHeight() { return size; }
	void Print(std::string text, TEXT_ALIGNMENT alignment, float width, bool _cache);
	D3DXVECTOR2 MeasureString(std::string text, bool _cache);
	void Uncache(std::string text, TEXT_ALIGNMENT alignment, float width);

private:
	CachedText* getCachedText(std::string text, TEXT_ALIGNMENT alignment, float width, bool& isCached);

private:
	FONT_LETTER* fontMap;
	int fontMapSize;
	Texture* texture;
	char* name;
	int size;
	byte style;
	int tWidth, tHeight;
	vector<CachedText*> cache;

public:
	static Font* LoadFont(const char* folder, const char* filename);
};


text:
<?php

class CM_Model_StreamChannelArchive_VideoTest extends CMTest_TestCase {

    public function tearDown() {
        CMTest_TH::clearEnv();
    }

    public function testCreate() {
        /** @var CM_Model_StreamChannel_Video $streamChannel */
        $streamChannel = CMTest_TH::createStreamChannel();
        $user = CMTest_TH::createUser();
        $streamPublish = CMTest_TH::createStreamPublish($user, $streamChannel);
        CMTest_TH::timeForward(10);
        /** @var CM_Model_StreamChannelArchive_Video $archive */
        $archive = CM_Model_StreamChannelArchive_Video::createStatic(array('streamChannel' => $streamChannel));
        $this->assertInstanceOf('CM_Model_StreamChannelArchive_Video', $archive);
        $this->assertSame($streamChannel->getId(), $archive->getId());
        $this->assertSame($user->getId(), $archive->getUserId());
        $this->assertEquals($user, $archive->getUser());
        $this->assertSame($streamChannel->getWidth(), $archive->getWidth());
        $this->assertSame($streamChannel->getHeight(), $archive->getHeight());
        $this->assertSame($streamPublish->getStart(), $archive->getCreated());
        $this->assertEquals(10, $archive->getDuration(), '', 1);
        $this->assertSame($streamChannel->getThumbnailCount(), $archive->getThumbnailCount());
        $this->assertSame(md5($streamPublish->getKey()), $archive->getHash());
        $this->assertSame($streamChannel->getType(), $archive->getStreamChannelType());

        $streamChannel = CMTest_TH::createStreamChannel();
        try {
            CM_Model_StreamChannelArchive_Video::createStatic(array('streamChannel' => $streamChannel));
            $this->fail('StreamChannelArchive_Video created without StreamPublish.');
        } catch (CM_Exception_Invalid $ex) {
            $this->assertTrue(true);
        }
    }

    public function testNoUser() {
        /** @var CM_Model_StreamChannel_Video $streamChannel */
        $streamChannel = CMTest_TH::createStreamChannel();
        $user = CMTest_TH::createUser();
        $streamPublish = CMTest_TH::createStreamPublish($user, $streamChannel);
        $streamPublish->unsetUser();

        /** @var CM_Model_StreamChannelArchive_Video $archive */
        $archive = CM_Model_StreamChannelArchive_Video::createStatic(array('streamChannel' => $streamChannel));

        $this->assertNull($archive->getUser());
        $this->assertNull($archive->getUserId());
    }

    public function testGetUser() {
        $user = CMTest_TH::createUser();
        $streamChannel = CMTest_TH::createStreamChannel();
        CMTest_TH::createStreamPublish($user, $streamChannel);
        $archive = CMTest_TH::createStreamChannelVideoArchive($streamChannel);
        $this->assertEquals($user, $archive->getUser());
        $user->delete();
        $this->assertNull($archive->getUser());
    }

    public function testGetVideo() {
        $archive = CMTest_TH::createStreamChannelVideoArchive();
        $videoFile = $archive->getVideo();
        $this->assertSame('streamChannels/' . $archive->getId() . '/' . $archive->getId() . '-' . $archive->getHash() .
            '-original.mp4', $videoFile->getPathRelative());
    }

    public function testGetThumbnails() {
        $archive = CMTest_TH::createStreamChannelVideoArchive();
        $this->assertSame(array(), $archive->getThumbnails()->getItems());

        /** @var CM_Model_StreamChannel_Video $streamChannel */
        $streamChannel = CMTest_TH::createStreamChannel();
        $streamChannel->setThumbnailCount(2);
        $archive = CMTest_TH::createStreamChannelVideoArchive($streamChannel);
        $thumb1 = new CM_File_UserContent('streamChannels', $archive->getId() . '-' . $archive->getHash() . '-thumbs/1.png', $streamChannel->getId());
        $thumb2 = new CM_File_UserContent('streamChannels', $archive->getId() . '-' . $archive->getHash() . '-thumbs/2.png', $streamChannel->getId());
        $this->assertEquals(array($thumb1, $thumb2), $archive->getThumbnails()->getItems());
    }

    public function testGetThumbnail() {
        $archive = CMTest_TH::createStreamChannelVideoArchive();
        $thumbnail = $archive->getThumbnail(3);
        $this->assertInstanceOf('CM_File_UserContent', $thumbnail);
        $this->assertSame(
            'streamChannels/' . $archive->getId() . '/' . $archive->getId() . '-' . $archive->getHash() . '-thumbs/3.png',
            $thumbnail->getPathRelative());
    }

    public function testOnDelete() {
        /** @var CM_Model_StreamChannel_Video $streamChannel */
        $streamChannel = CMTest_TH::createStreamChannel();
        $streamChannel->setThumbnailCount(3);
        $archive = CMTest_TH::createStreamChannelVideoArchive($streamChannel);
        $files = $this->_createArchiveFiles($archive);
        foreach ($files as $file) {
            $this->assertTrue($file->getExists());
        }

        $archive->delete();
        foreach ($files as $file) {
            $this->assertFalse($file->getExists());
        }
        try {
            new CM_Model_StreamChannelArchive_Video($archive->getId());
            $this->fail('StreamChannelArchive not deleted.');
        } catch (CM_Exception_Nonexistent $ex) {
            $this->assertTrue(true);
        }
    }

    public function testDeleteOlder() {
        $time = time();
        /** @var CM_Model_StreamChannel_Video $streamChannel */
        $streamChannelsDeleted = array();
        $archivesDeleted = array();
        /** @var $filesDeleted CM_File[] */
        $filesDeleted = array();
        for ($i = 0; $i < 2; $i++) {
            $streamChannel = CMTest_TH::createStreamChannel();
            $streamChannel->setThumbnailCount(4);
            $streamChannelsDeleted[] = $streamChannel;
            $archive = CMTest_TH::createStreamChannelVideoArchive($streamChannel);
            $archivesDeleted[] = $archive;
            $filesDeleted = array_merge($filesDeleted, $this->_createArchiveFiles($archive));
        }

        $streamChannelsNotDeleted = array();
        $archivesNotDeleted = array();
        /** @var $filesNotDeleted CM_File[] */
        $filesNotDeleted = array();
        $streamChannel = CMTest_TH::createStreamChannel();
        $streamChannel = $this->getMock('CM_Model_StreamChannel_Video', array('getType'), array($streamChannel->getId()));
        $streamChannel->expects($this->any())->method('getType')->will($this->returnValue(3));
        $streamChannelsNotDeleted[] = $streamChannel;
        $archive = CMTest_TH::createStreamChannelVideoArchive($streamChannel);
        $archivesNotDeleted[] = $archive;
        $filesNotDeleted = array_merge($filesNotDeleted, $this->_createArchiveFiles($archive));

        CMTest_TH::timeForward(20);
        for ($i = 0; $i < 3; $i++) {
            $streamChannel = CMTest_TH::createStreamChannel();
            $streamChannel->setThumbnailCount(4);
            $streamChannelsNotDeleted[] = $streamChannel;
            $archive = CMTest_TH::createStreamChannelVideoArchive($streamChannel);
            $archivesNotDeleted[] = $archive;
            $filesNotDeleted = array_merge($filesNotDeleted, $this->_createArchiveFiles($archive));
        }

        foreach ($filesNotDeleted as $file) {
            $this->assertTrue($file->getExists());
        }
        foreach ($filesDeleted as $file) {
            $this->assertTrue($file->getExists());
        }
        CM_Model_StreamChannelArchive_Video::deleteOlder(10, CM_Model_StreamChannel_Video::getTypeStatic());
        foreach ($filesNotDeleted as $file) {
            $this->assertTrue($file->getExists());
        }
        foreach ($filesDeleted as $file) {
            $this->assertFalse($file->getExists());
        }
        foreach ($archivesNotDeleted as $archive) {
            try {
                CMTest_TH::reinstantiateModel($archive);
                $this->assertTrue(true);
            } catch (CM_Exception_Nonexistent $ex) {
                $this->fail('Young streamchannelArchive deleted');
            }
        }
        foreach ($archivesDeleted as $archive) {
            try {
                CMTest_TH::reinstantiateModel($archive);
                $this->fail('Old streamchannelArchive not deleted');
            } catch (CM_Exception_Nonexistent $ex) {
                $this->assertTrue(true);
            }
        }
    }

    /**
     * @param CM_Model_StreamChannelArchive_Video $archive
     * @return CM_File[]
     */
    private function _createArchiveFiles(CM_Model_StreamChannelArchive_Video $archive) {
        $files = array();
        if ($archive->getThumbnailCount() > 0) {
            /** @var CM_File_UserContent $thumbnailFirst */
            $thumbnailFirst = $archive->getThumbnails()->getItem(0);
            $thumbnailFirst->ensureParentDirectory();
            $files[] = $thumbnailFirst->getParentDirectory();
        }
        for ($i = 0; $i < $archive->getThumbnailCount(); $i++) {
            /** @var CM_File_UserContent $file */
            $file = $archive->getThumbnails()->getItem($i);
            $file->write('');
            $files[] = $file;
        }
        $video = $archive->getVideo();
        $video->ensureParentDirectory();
        $video->write('');
        $files[] = $video;
        return $files;
    }
}


text:
<?php

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

namespace Application\Mapper;

use Application\Model\PageReportInterface;

/**
 *
 * @author Alexander
 */
interface PageReportMapperInterface extends SimpleMapperInterface
{
   /**
    * @param \Application\Model\PageReportInterface $report
    *
    * @return \Application\Model\PageReportInterface
    * @throws \Exception
    */
   public function save(PageReportInterface $report);

   /**
    * @param \Application\Model\PageReportInterface $report
    * @return \Application\Model\PageReportInterface
    * @throws \Exception
    */
   public function apply(PageReportInterface $report);
}