def test_bad_git_config(local_docs_ghp):
    """Git commit fails.

    Need to do the crazy Popen thing since the local repo being committed to is the gh-pages temporary repo.

    :param local_docs_ghp: conftest fixture.
    """
    env = dict(os.environ, GIT_DIR=str(local_docs_ghp.join('.git')), HOME=str(local_docs_ghp.join('..')))
    command = ['sphinx-versioning', '-v', 'push', '.', 'gh-pages', '.']
    output_lines = list()
    caused = False

    # Run.
    proc = Popen(command, cwd=str(local_docs_ghp), env=env, stdout=PIPE, stderr=STDOUT)
    for line in iter(proc.stdout.readline, b''):
        output_lines.append(line)
        if b'"command": ["git", "clone"' in line:
            if not caused:
                # Invalidate lock file.
                tmp_repo = py.path.local(re.findall(r'"cwd": "([^"]+)"', line.decode('utf-8'))[0])
                assert tmp_repo.check(dir=True)
                pytest.run(tmp_repo, ['git', 'config', 'user.useConfigOnly', 'true'], retry=3)
                pytest.run(tmp_repo, ['git', 'config', 'user.email', '(none)'], retry=3)
                caused = True
    output_lines.append(proc.communicate()[0])
    output = b''.join(output_lines).decode('utf-8')
    assert proc.poll() != 0
    assert caused

    # Verify.
    assert 'Traceback' not in output
    assert 'Failed to commit locally.' in output
    assert 'Please tell me who you are.' in output or 'user.useConfigOnly set but no name given' in output
def test_bad_git_config(local_docs_ghp):
    """Git commit fails.

    Need to do the crazy Popen thing since the local repo being committed to is the gh-pages temporary repo.

    :param local_docs_ghp: conftest fixture.
    """
    env = dict(os.environ, GIT_DIR=str(local_docs_ghp.join('.git')), HOME=str(local_docs_ghp.join('..')))
    command = ['sphinx-versioning', '-v', 'push', '.', 'gh-pages', '.']
    output_lines = list()
    caused = False

    # Run.
    proc = Popen(command, cwd=str(local_docs_ghp), env=env, stdout=PIPE, stderr=STDOUT)
    for line in iter(proc.stdout.readline, b''):
        output_lines.append(line)
        if b'"command": ["git", "clone"' in line:
            if not caused:
                # Invalidate lock file.
                tmp_repo = py.path.local(re.findall(r'"cwd": "([^"]+)"', line.decode('utf-8'))[0])
                assert tmp_repo.check(dir=True)
                pytest.run(tmp_repo, ['git', 'config', 'user.useConfigOnly', 'true'], retry=3)
                pytest.run(tmp_repo, ['git', 'config', 'user.email', '(none)'], retry=3)
                caused = True
    output_lines.append(proc.communicate()[0])
    output = b''.join(output_lines).decode('utf-8')
    assert proc.poll() != 0
    assert caused

    # Verify.
    assert 'Traceback' not in output
    assert 'Failed to commit locally.' in output
    assert 'Please tell me who you are.' in output or 'user.useConfigOnly set but no name given' in output
예제 #3
0
def test_no_exclude(local_docs_ghp, urls):
    """Test with successful push to remote. Don't remove/exclude any files.

    :param local_docs_ghp: conftest fixture.
    :param urls: conftest fixture.
    """
    # Run.
    output = pytest.run(local_docs_ghp,
                        ['sphinx-versioning', 'push', '.', 'gh-pages', '.'])
    assert 'Traceback' not in output
    assert 'Failed to push to remote repository.' not in output

    # Check HTML.
    pytest.run(local_docs_ghp, ['git', 'checkout', 'gh-pages'])
    pytest.run(local_docs_ghp, ['git', 'pull', 'origin', 'gh-pages'])
    urls(local_docs_ghp.join('contents.html'),
         ['<li><a href="master/contents.html">master</a></li>'])
    urls(local_docs_ghp.join('master', 'contents.html'),
         ['<li><a href="contents.html">master</a></li>'])

    # Run again.
    output = pytest.run(local_docs_ghp,
                        ['sphinx-versioning', 'push', '.', 'gh-pages', '.'])
    assert 'Traceback' not in output
    assert 'Failed to push to remote repository.' not in output
    assert 'No significant changes to commit.' in output

    # Check SHAs.
    old_sha = pytest.run(local_docs_ghp, ['git', 'rev-parse', 'HEAD']).strip()
    pytest.run(local_docs_ghp, ['git', 'pull', 'origin', 'gh-pages'])
    sha = pytest.run(local_docs_ghp, ['git', 'rev-parse', 'HEAD']).strip()
    assert sha == old_sha
예제 #4
0
def test_wait(tmpdir, fail):
    """Test waiting for background jobs.

    :param py.path.local tmpdir: pytest fixture.
    :param bool fail: Cause a failure during the run.
    """
    with build_image(tmpdir.join('root')) as (root, _, image_ids):
        pre_rip = root.ensure('hook-pre-rip.sh')
        pre_rip.write('do_wait () {\n'
                      '    sleep 2\n'
                      '    echo do_wait done!\n'
                      '}\n'
                      'do_wait &\n')
        if fail:
            pre_rip.write('false\n', 'a')

    # Docker run.
    if fail:
        with pytest.raises(subprocess.CalledProcessError) as exc:
            pytest.run(image_id=image_ids[0])
        stdout, stderr = exc.value.output, exc.value.stderr
    else:
        stdout, stderr = pytest.run(image_id=image_ids[0])

    # Verify.
    assert b'do_wait done!' in stdout
def test_no_exclude(local_docs_ghp, urls):
    """Test with successful push to remote. Don't remove/exclude any files.

    :param local_docs_ghp: conftest fixture.
    :param urls: conftest fixture.
    """
    # Run.
    output = pytest.run(local_docs_ghp, ['sphinx-versioning', 'push', '.', 'gh-pages', '.'])
    assert 'Traceback' not in output
    assert 'Failed to push to remote repository.' not in output

    # Check HTML.
    pytest.run(local_docs_ghp, ['git', 'checkout', 'gh-pages'])
    pytest.run(local_docs_ghp, ['git', 'pull', 'origin', 'gh-pages'])
    urls(local_docs_ghp.join('contents.html'), ['<li><a href="master/contents.html">master</a></li>'])
    urls(local_docs_ghp.join('master', 'contents.html'), ['<li><a href="contents.html">master</a></li>'])

    # Run again.
    output = pytest.run(local_docs_ghp, ['sphinx-versioning', 'push', '.', 'gh-pages', '.'])
    assert 'Traceback' not in output
    assert 'Failed to push to remote repository.' not in output
    assert 'No significant changes to commit.' in output

    # Check SHAs.
    old_sha = pytest.run(local_docs_ghp, ['git', 'rev-parse', 'HEAD']).strip()
    pytest.run(local_docs_ghp, ['git', 'pull', 'origin', 'gh-pages'])
    sha = pytest.run(local_docs_ghp, ['git', 'rev-parse', 'HEAD']).strip()
    assert sha == old_sha
def test_error_bad_path(tmpdir):
    """Test handling of bad paths.

    :param tmpdir: pytest fixture.
    """
    with pytest.raises(CalledProcessError) as exc:
        pytest.run(tmpdir, ['sphinx-versioning', '-N', '-c', 'unknown', 'build', '.', str(tmpdir)])
    assert 'Directory "unknown" does not exist.' in exc.value.output

    tmpdir.ensure('is_file')
    with pytest.raises(CalledProcessError) as exc:
        pytest.run(tmpdir, ['sphinx-versioning', '-N', '-c', 'is_file', 'build', '.', str(tmpdir)])
    assert 'Directory "is_file" is a file.' in exc.value.output

    with pytest.raises(CalledProcessError) as exc:
        pytest.run(tmpdir, ['sphinx-versioning', '-N', 'build', '.', str(tmpdir)])
    assert 'Failed to find local git repository root in {}.'.format(repr(str(tmpdir))) in exc.value.output

    repo = tmpdir.ensure_dir('repo')
    pytest.run(repo, ['git', 'init'])
    empty = tmpdir.ensure_dir('empty1857')
    with pytest.raises(CalledProcessError) as exc:
        pytest.run(repo, ['sphinx-versioning', '-N', '-g', str(empty), 'build', '.', str(tmpdir)])
    assert 'Failed to find local git repository root in' in exc.value.output
    assert 'empty1857' in exc.value.output
예제 #7
0
def test_wait_nested(tmpdir, fail):
    """Test waiting for background jobs created by background jobs.

    :param py.path.local tmpdir: pytest fixture.
    :param bool fail: Cause a failure during the run.
    """
    with build_image(tmpdir.join('root')) as (root, _, image_ids):
        post_title = root.ensure('hook-post-title.sh')
        post_title.write(
            'do_wait () {\n'
            '    for _ in {1..5}; do\n'
            '        if readlink /proc/*/exe |grep -q makemkvcon &> /dev/null; then sleep 1; else break; fi\n'
            '    done\n'
            '    sleep 5\n'
            '    echo do_wait done!\n'
            '}\n'
            'do_wait &\n')
        if fail:
            root.ensure('hook-post-rip.sh').write('false\n', 'a')

    # Docker run.
    if fail:
        with pytest.raises(subprocess.CalledProcessError) as exc:
            pytest.run(image_id=image_ids[0])
        stdout, stderr = exc.value.output, exc.value.stderr
    else:
        stdout, stderr = pytest.run(image_id=image_ids[0])

    # Verify.
    assert b'do_wait done!' in stdout
예제 #8
0
def test_manual(tmpdir):
    """Test example commands in "Run Manually" README section.

    :param py.path.local tmpdir: pytest fixture.
    """
    # Grab commands from README.
    with pytest.ROOT.join('README.md').open() as handle:
        for line in handle:
            if line.strip() == 'Now go ahead and run the image:':
                break
        partial = handle.read(1024)
    contents = re.compile(r'\n```bash\n(.+?)\n```\n',
                          re.DOTALL).findall(partial)[0]
    script = tmpdir.join('script.sh')
    script.write(contents)

    # Run.
    if OUTPUT.check():
        OUTPUT.remove()
    pytest.run(['bash', script])
    assert OUTPUT.check()

    # Verify.
    pytest.verify(OUTPUT,
                  gid=os.getgid(),
                  uid=os.getuid(),
                  modes=('drwxr-xr-x', '-rw-r--r--'))
예제 #9
0
def test_eject(tmpdir, fail, no_eject):
    """Test post and pre eject hooks.

    :param py.path.local tmpdir: pytest fixture.
    :param bool fail: Cause a failure during the run.
    :param bool no_eject: Set environment variable to 'true' or 'false'.
    """
    hooks = ('pre-success-eject', 'post-success-eject', 'pre-failed-eject', 'post-failed-eject')
    with build_image(tmpdir.join('root')) as (root, _, image_ids):
        for hook in hooks:
            root.ensure('hook-{}.sh'.format(hook)).write('echo eject hook fired!')
        if fail:
            root.ensure('hook-pre-rip.sh').write('false')

    # Docker run.
    args = ['-e', 'FAILED_EJECT=true'] + (['-e', 'NO_EJECT=true'] if no_eject else [])
    if fail:
        with pytest.raises(subprocess.CalledProcessError) as exc:
            pytest.run(args=args, image_id=image_ids[0])
        stdout, stderr = exc.value.output, exc.value.stderr
    else:
        stdout, stderr = pytest.run(args=args, image_id=image_ids[0])

    # Verify.
    if no_eject:
        assert stdout.count(b'eject hook fired!') == 0
    else:
        assert stdout.count(b'eject hook fired!') == 2
        if fail:
            assert b'\nFIRING HOOK: /hook-pre-failed-eject.sh' in stderr
            assert b'\nFIRING HOOK: /hook-post-failed-eject.sh' in stderr
        else:
            assert b'\nFIRING HOOK: /hook-pre-success-eject.sh' in stderr
            assert b'\nFIRING HOOK: /hook-post-success-eject.sh' in stderr
def test_new_branch_tags(local, local_light, clone_branch):
    """Test with new branches and tags unknown to local repo.

    :param local: conftest fixture.
    :param local_light: conftest fixture.
    :param bool clone_branch: Test with local repo cloned with --branch.
    """
    if clone_branch:
        local = local_light

    # Get SHAs and verify not fetched.
    remotes = list_remote(str(local))
    assert len(
        remotes
    ) == 7  # master feature light_tag annotated_tag nb_tag orphaned_branch ob_at
    shas = {r[0] for r in remotes}
    assert len(shas) == 3
    with pytest.raises(GitError):
        filter_and_date(str(local), ['README'], shas)

    # Fetch and verify.
    fetch_commits(str(local), remotes)
    dates = filter_and_date(str(local), ['README'], shas)
    assert len(dates) == 3
    pytest.run(local, ['git', 'diff-index', '--quiet', 'HEAD', '--'])
def test_fetch_existing(local):
    """Fetch commit that is already locally available.

    :param local: conftest fixture.
    """
    remotes = list_remote(str(local))
    fetch_commits(str(local), remotes)
    pytest.run(local, ['git', 'diff-index', '--quiet', 'HEAD', '--'])  # Exit 0 if nothing changed.
예제 #12
0
def write_udev(contents=''):
    """Write data to 85-makemkv.rules file then reload udev rules.

    :param str contents: Contents of file to write.
    """
    script = py.path.local('/etc/udev/rules.d/85-makemkv.rules')
    script.write(contents)
    pytest.run(['sudo', 'udevadm', 'control', '--reload'])
def test_banner_branch(tmpdir, banner, config, local_docs, show_banner):
    """Test banner messages without tags.

    :param tmpdir: pytest fixture.
    :param banner: conftest fixture.
    :param config: conftest fixture.
    :param local_docs: conftest fixture.
    :param bool show_banner: Show the banner.
    """
    pytest.run(local_docs, ['git', 'checkout', '-b', 'old_build', 'master'])
    pytest.run(local_docs, ['git', 'checkout', 'master'])
    pytest.run(local_docs, ['git', 'rm', 'two.rst'])
    local_docs.join('contents.rst').write(
        'Test\n'
        '====\n'
        '\n'
        'Sample documentation.\n'
        '\n'
        '.. toctree::\n'
        '    one\n'
    )
    pytest.run(local_docs, ['git', 'commit', '-am', 'Deleted.'])
    pytest.run(local_docs, ['git', 'push', 'origin', 'master', 'old_build'])

    config.show_banner = show_banner
    versions = Versions(gather_git_info(str(local_docs), ['conf.py'], tuple(), tuple()))
    versions['master']['found_docs'] = ('contents', 'one')
    versions['old_build']['found_docs'] = ('contents', 'one', 'two')

    # Export.
    exported_root = tmpdir.ensure_dir('exported_root')
    export(str(local_docs), versions['master']['sha'], str(exported_root.join(versions['master']['sha'])))
    export(str(local_docs), versions['old_build']['sha'], str(exported_root.join(versions['old_build']['sha'])))

    # Run and verify files.
    dst = tmpdir.ensure_dir('destination')
    build_all(str(exported_root), str(dst), versions)
    actual = sorted(f.relto(dst) for f in dst.visit(lambda p: p.basename in ('contents.html', 'one.html', 'two.html')))
    expected = [
        'contents.html', join('master', 'contents.html'), join('master', 'one.html'),
        join('old_build', 'contents.html'), join('old_build', 'one.html'), join('old_build', 'two.html'), 'one.html'
    ]
    assert actual == expected

    # Verify no banner.
    if not show_banner:
        for path in expected:
            banner(dst.join(path), None)
        return
    for path in ('contents.html', 'master/contents.html', 'master/one.html', 'one.html'):
        banner(dst.join(path), None)

    # Verify banner.
    banner(dst.join('old_build', 'contents.html'), '../master/contents.html',
           'the development version of Python. The main version is master')
    banner(dst.join('old_build', 'one.html'), '../master/one.html',
           'the development version of Python. The main version is master')
    banner(dst.join('old_build', 'two.html'), '', 'the development version of Python')
def test_error_no_docs_found(tmpdir, local):
    """Test no docs to build.

    :param tmpdir: pytest fixture.
    :param local: conftest fixture.
    """
    with pytest.raises(CalledProcessError) as exc:
        pytest.run(local, ['sphinx-versioning', '-N', '-v', 'build', '.', str(tmpdir)])
    assert 'No docs found in any remote branch/tag. Nothing to do.' in exc.value.output
def test_error_bad_root_ref(tmpdir, local_docs):
    """Test bad root ref.

    :param tmpdir: pytest fixture.
    :param local_docs: conftest fixture.
    """
    with pytest.raises(CalledProcessError) as exc:
        pytest.run(local_docs, ['sphinx-versioning', '-N', '-v', 'build', '.', str(tmpdir), '-r', 'unknown'])
    assert 'Root ref unknown not found in: master' in exc.value.output
def test_fetch_existing(local):
    """Fetch commit that is already locally available.

    :param local: conftest fixture.
    """
    remotes = list_remote(str(local))
    fetch_commits(str(local), remotes)
    pytest.run(local, ['git', 'diff-index', '--quiet', 'HEAD', '--'
                       ])  # Exit 0 if nothing changed.
예제 #17
0
def test_read_error(tmpdir):
    """Test disc opening error handling.

    :param py.path.local tmpdir: pytest fixture.
    """
    output = tmpdir.ensure_dir('output')
    with pytest.raises(subprocess.CalledProcessError) as exc:
        pytest.run(output=output)
    assert b'Failed to open disc' in exc.value.output
    pytest.verify_failed_file(output)
예제 #18
0
 def _test(verbose=False):
     """
     Invoke the Py-ART test suite.
     """
     import pytest
     pkg_dir = _osp.abspath(_osp.dirname(__file__))
     args = ['', pkg_dir, '--pyargs']
     if verbose:
         args.extend(['-v', '-s'])
     pytest.run('pyart', argv=args)
def test_race(tmpdir, local_docs_ghp, remote, urls, give_up):
    """Test with race condition where another process pushes to gh-pages causing a retry.

    :param tmpdir: pytest fixture.
    :param local_docs_ghp: conftest fixture.
    :param remote: conftest fixture.
    :param urls: conftest fixture.
    :param bool give_up: Cause multiple race conditions causing timeout/giveup.
    """
    local_other = tmpdir.ensure_dir('local_other')
    pytest.run(local_other, ['git', 'clone', remote, '--branch=gh-pages', '.'])

    # Prepare command.
    env = dict(os.environ, GIT_DIR=str(local_docs_ghp.join('.git')))
    command = ['sphinx-versioning', '--no-colors', 'push', '.', 'gh-pages', 'html/docs']
    output_lines = list()
    caused = False

    # Run.
    proc = Popen(command, cwd=str(local_docs_ghp), env=env, stdout=PIPE, stderr=STDOUT)
    for line in iter(proc.stdout.readline, b''):
        output_lines.append(line)
        if line.strip() == b'=> Building docs...':
            if give_up or not caused:
                # Cause race condition.
                local_other.join('README').write('changed', mode='a')
                pytest.run(local_other, ['git', 'commit', '-am', 'Cause race condition.'])
                pytest.run(local_other, ['git', 'push', 'origin', 'gh-pages'])
                caused = True
    output_lines.append(proc.communicate()[0])
    output = b''.join(output_lines).decode('utf-8')
    if give_up:
        assert proc.poll() != 0
    else:
        assert proc.poll() == 0
    assert caused

    # Verify.
    assert 'Traceback' not in output
    assert 'Building docs...' in output
    assert 'Failed to push to remote repository. Retrying in ' in output
    if give_up:
        assert 'Successfully pushed to remote repository.' not in output
        assert 'Ran out of retries, giving up.' in output
        return
    assert 'Successfully pushed to remote repository.' in output

    # Verify files.
    pytest.run(local_docs_ghp, ['git', 'checkout', 'gh-pages'])
    pytest.run(local_docs_ghp, ['git', 'pull', 'origin', 'gh-pages'])
    destination = local_docs_ghp.join('html', 'docs')
    urls(destination.join('contents.html'), ['<li><a href="master/contents.html">master</a></li>'])
    urls(destination.join('master', 'contents.html'), ['<li><a href="contents.html">master</a></li>'])
    actual = local_docs_ghp.join('README').read()
    assert actual == 'Orphaned branch for HTML docs.changed'
def test_race(tmpdir, local_docs_ghp, remote, urls, give_up):
    """Test with race condition where another process pushes to gh-pages causing a retry.

    :param tmpdir: pytest fixture.
    :param local_docs_ghp: conftest fixture.
    :param remote: conftest fixture.
    :param urls: conftest fixture.
    :param bool give_up: Cause multiple race conditions causing timeout/giveup.
    """
    local_other = tmpdir.ensure_dir('local_other')
    pytest.run(local_other, ['git', 'clone', remote, '--branch=gh-pages', '.'])

    # Prepare command.
    env = dict(os.environ, GIT_DIR=str(local_docs_ghp.join('.git')))
    command = ['sphinx-versioning', '--no-colors', 'push', '.', 'gh-pages', 'html/docs']
    output_lines = list()
    caused = False

    # Run.
    proc = Popen(command, cwd=str(local_docs_ghp), env=env, stdout=PIPE, stderr=STDOUT)
    for line in iter(proc.stdout.readline, b''):
        output_lines.append(line)
        if line.strip() == b'=> Building docs...':
            if give_up or not caused:
                # Cause race condition.
                local_other.join('README').write('changed', mode='a')
                pytest.run(local_other, ['git', 'commit', '-am', 'Cause race condition.'])
                pytest.run(local_other, ['git', 'push', 'origin', 'gh-pages'])
                caused = True
    output_lines.append(proc.communicate()[0])
    output = b''.join(output_lines).decode('utf-8')
    if give_up:
        assert proc.poll() != 0
    else:
        assert proc.poll() == 0
    assert caused

    # Verify.
    assert 'Traceback' not in output
    assert 'Building docs...' in output
    assert 'Failed to push to remote repository. Retrying in ' in output
    if give_up:
        assert 'Successfully pushed to remote repository.' not in output
        assert 'Ran out of retries, giving up.' in output
        return
    assert 'Successfully pushed to remote repository.' in output

    # Verify files.
    pytest.run(local_docs_ghp, ['git', 'checkout', 'gh-pages'])
    pytest.run(local_docs_ghp, ['git', 'pull', 'origin', 'gh-pages'])
    destination = local_docs_ghp.join('html', 'docs')
    urls(destination.join('contents.html'), ['<li><a href="master/contents.html">master</a></li>'])
    urls(destination.join('master', 'contents.html'), ['<li><a href="contents.html">master</a></li>'])
    actual = local_docs_ghp.join('README').read()
    assert actual == 'Orphaned branch for HTML docs.changed'
def test_error_clone_failure(local_docs):
    """Test DEST_BRANCH doesn't exist.

    :param local_docs: conftest fixture.
    """
    # Run.
    with pytest.raises(CalledProcessError) as exc:
        pytest.run(local_docs, ['sphinx-versioning', 'push', '.', 'gh-pages', '.'])
    assert 'Traceback' not in exc.value.output
    assert 'Cloning gh-pages into temporary directory...' in exc.value.output
    assert 'Failed to clone from remote repo URL.' in exc.value.output
    assert 'fatal: Remote branch gh-pages not found in upstream origin' in exc.value.output
예제 #22
0
def test_error_no_docs_found(tmpdir, local):
    """Test no docs to build.

    :param tmpdir: pytest fixture.
    :param local: conftest fixture.
    """
    with pytest.raises(CalledProcessError) as exc:
        pytest.run(
            local,
            ['sphinx-versioning', '-N', '-v', 'build', '.',
             str(tmpdir)])
    assert 'No docs found in any remote branch/tag. Nothing to do.' in exc.value.output
def test_error_clone_failure(local_docs):
    """Test DEST_BRANCH doesn't exist.

    :param local_docs: conftest fixture.
    """
    # Run.
    with pytest.raises(CalledProcessError) as exc:
        pytest.run(local_docs, ['sphinx-versioning', 'push', '.', 'gh-pages', '.'])
    assert 'Traceback' not in exc.value.output
    assert 'Cloning gh-pages into temporary directory...' in exc.value.output
    assert 'Failed to clone from remote repo URL.' in exc.value.output
    assert 'fatal: Remote branch gh-pages not found in upstream origin' in exc.value.output
예제 #24
0
def test_error_bad_root_ref(tmpdir, local_docs):
    """Test bad root ref.

    :param tmpdir: pytest fixture.
    :param local_docs: conftest fixture.
    """
    with pytest.raises(CalledProcessError) as exc:
        pytest.run(local_docs, [
            'sphinx-versioning', '-N', '-v', 'build', '.',
            str(tmpdir), '-r', 'unknown'
        ])
    assert 'Root ref unknown not found in: master' in exc.value.output
def test_supported(tmpdir, config, local_docs, theme):
    """Test with different themes. Verify not much changed between sphinx-build and sphinx-versioning.

    :param tmpdir: pytest fixture.
    :param sphinxcontrib.versioning.lib.Config config: conftest fixture.
    :param local_docs: conftest fixture.
    :param str theme: Theme name to use.
    """
    config.overflow = ('-D', 'html_theme=' + theme)
    target_n = tmpdir.ensure_dir('target_n')
    target_y = tmpdir.ensure_dir('target_y')
    versions = Versions([
        ('', 'master', 'heads', 1, 'conf.py'),
        ('', 'feature', 'heads', 2, 'conf.py'),
        ('', 'v1.0.0', 'tags', 3, 'conf.py'),
        ('', 'v1.2.0', 'tags', 4, 'conf.py'),
        ('', 'v2.0.0', 'tags', 5, 'conf.py'),
        ('', 'v2.1.0', 'tags', 6, 'conf.py'),
        ('', 'v2.2.0', 'tags', 7, 'conf.py'),
        ('', 'v2.3.0', 'tags', 8, 'conf.py'),
        ('', 'v2.4.0', 'tags', 9, 'conf.py'),
        ('', 'v2.5.0', 'tags', 10, 'conf.py'),
        ('', 'v2.6.0', 'tags', 11, 'conf.py'),
        ('', 'v2.7.0', 'tags', 12, 'conf.py'),
        ('', 'testing_branch', 'heads', 13, 'conf.py'),
    ],
                        sort=['semver'])

    # Build with normal sphinx-build.
    pytest.run(
        local_docs,
        ['sphinx-build', '.',
         str(target_n), '-D', 'html_theme=' + theme])
    contents_n = target_n.join('contents.html').read()
    assert 'master' not in contents_n

    # Build with versions.
    build(str(local_docs), str(target_y), versions, 'master', True)
    contents_y = target_y.join('contents.html').read()
    assert 'master' in contents_y

    # Verify nothing removed.
    diff = list(
        difflib.unified_diff(contents_n.splitlines(True),
                             contents_y.splitlines(True)))[2:]
    assert diff
    for line in diff:
        assert not line.startswith('-')

    # Verify added.
    for name in (r['name'] for r in versions.remotes):
        assert any(name in line for line in diff if line.startswith('+'))
def test_timezones(tmpdir, local):
    """Test mtime on RST files with different git commit timezones.

    :param tmpdir: pytest fixture.
    :param local: conftest fixture.
    """
    files_dates = [
        ('local.rst', ''),
        ('UTC.rst', ' +0000'),
        ('PDT.rst', ' -0700'),
        ('PST.rst', ' -0800'),
    ]

    # Commit files.
    for name, offset in files_dates:
        local.ensure(name)
        pytest.run(local, ['git', 'add', name])
        env = pytest.author_committer_dates(0)
        env['GIT_AUTHOR_DATE'] += offset
        env['GIT_COMMITTER_DATE'] += offset
        pytest.run(local, ['git', 'commit', '-m', 'Added ' + name],
                   environ=env)

    # Run.
    target = tmpdir.ensure_dir('target')
    sha = pytest.run(local, ['git', 'rev-parse', 'HEAD']).strip()
    export(str(local), sha, str(target))

    # Validate.
    actual = {
        i[0]: str(datetime.fromtimestamp(target.join(i[0]).mtime()))
        for i in files_dates
    }
    if -time.timezone == -28800:
        expected = {
            'local.rst': '2016-12-05 03:17:05',
            'UTC.rst': '2016-12-04 19:17:05',
            'PDT.rst': '2016-12-05 02:17:05',
            'PST.rst': '2016-12-05 03:17:05',
        }
    elif -time.timezone == 0:
        expected = {
            'local.rst': '2016-12-05 03:17:05',
            'UTC.rst': '2016-12-05 03:17:05',
            'PDT.rst': '2016-12-05 10:17:05',
            'PST.rst': '2016-12-05 11:17:05',
        }
    else:
        return pytest.skip(
            'Need to add expected for {} timezone.'.format(-time.timezone))
    assert actual == expected
예제 #27
0
def test_sub_page_and_tag(tmpdir, local_docs, urls):
    """Test with sub pages and one git tag. Testing from local git repo.

    :param tmpdir: pytest fixture.
    :param local_docs: conftest fixture.
    :param urls: conftest fixture.
    """
    local_docs.ensure('subdir', 'sub.rst').write(
        '.. _sub:\n'
        '\n'
        'Sub\n'
        '===\n'
        '\n'
        'Sub directory sub page documentation.\n')
    local_docs.join('contents.rst').write('    subdir/sub\n', mode='a')
    pytest.run(local_docs, ['git', 'add', 'subdir', 'contents.rst'])
    pytest.run(local_docs, ['git', 'commit', '-m', 'Adding subdir docs.'])
    pytest.run(local_docs, ['git', 'tag', 'v1.0.0'])
    pytest.run(local_docs, ['git', 'push', 'origin', 'master', 'v1.0.0'])

    # Run.
    destination = tmpdir.ensure_dir('destination')
    output = pytest.run(local_docs,
                        ['sphinx-versioning', 'build', '.',
                         str(destination)])
    assert 'Traceback' not in output

    # Check root.
    urls(destination.join('contents.html'), [
        '<li><a href="master/contents.html">master</a></li>',
        '<li><a href="v1.0.0/contents.html">v1.0.0</a></li>'
    ])
    urls(destination.join('subdir', 'sub.html'), [
        '<li><a href="../master/subdir/sub.html">master</a></li>',
        '<li><a href="../v1.0.0/subdir/sub.html">v1.0.0</a></li>',
    ])

    # Check master.
    urls(destination.join('master', 'contents.html'), [
        '<li><a href="contents.html">master</a></li>',
        '<li><a href="../v1.0.0/contents.html">v1.0.0</a></li>',
    ])
    urls(destination.join('master', 'subdir', 'sub.html'), [
        '<li><a href="sub.html">master</a></li>',
        '<li><a href="../../v1.0.0/subdir/sub.html">v1.0.0</a></li>',
    ])

    # Check v1.0.0.
    urls(destination.join('v1.0.0', 'contents.html'), [
        '<li><a href="../master/contents.html">master</a></li>',
        '<li><a href="contents.html">v1.0.0</a></li>',
    ])
    urls(destination.join('v1.0.0', 'subdir', 'sub.html'), [
        '<li><a href="../../master/subdir/sub.html">master</a></li>',
        '<li><a href="sub.html">v1.0.0</a></li>',
    ])
def test_all_errors(tmpdir, local_docs, urls):
    """Test good root ref with all bad non-root refs.

    :param tmpdir: pytest fixture.
    :param local_docs: conftest fixture.
    :param urls: conftest fixture.
    """
    pytest.run(local_docs, ['git', 'checkout', '-b', 'a_broken', 'master'])
    local_docs.join('conf.py').write('master_doc = exception\n')
    pytest.run(local_docs, ['git', 'commit', '-am', 'Broken version.'])
    pytest.run(local_docs, ['git', 'checkout', '-b', 'b_broken', 'a_broken'])
    pytest.run(local_docs, ['git', 'push', 'origin', 'a_broken', 'b_broken'])

    versions = Versions(gather_git_info(str(local_docs), ['conf.py'], tuple(), tuple()))

    exported_root = tmpdir.ensure_dir('exported_root')
    export(str(local_docs), versions['master']['sha'], str(exported_root.join(versions['master']['sha'])))
    export(str(local_docs), versions['b_broken']['sha'], str(exported_root.join(versions['b_broken']['sha'])))

    # Run.
    destination = tmpdir.ensure_dir('destination')
    build_all(str(exported_root), str(destination), versions)
    assert [r['name'] for r in versions.remotes] == ['master']

    # Verify root HTML links.
    urls(destination.join('contents.html'), ['<li><a href="master/contents.html">master</a></li>'])
    urls(destination.join('master', 'contents.html'), ['<li><a href="contents.html">master</a></li>'])
def test_simple(tmpdir, local):
    """Test with just the README in one commit.

    :param tmpdir: pytest fixture.
    :param local: conftest fixture.
    """
    target = tmpdir.ensure_dir('target')
    sha = pytest.run(local, ['git', 'rev-parse', 'HEAD']).strip()

    export(str(local), sha, str(target))
    pytest.run(local, ['git', 'diff-index', '--quiet', 'HEAD', '--'
                       ])  # Exit 0 if nothing changed.
    files = [f.relto(target) for f in target.listdir()]
    assert files == ['README']
예제 #30
0
def test_empty_remote(local_commit, remote):
    """Test with valid but empty remote.

    :param local_commit: conftest fixture.
    :param remote: conftest fixture.
    """
    pytest.run(local_commit, ['git', 'remote', 'add', 'origin', remote])
    remotes = list_remote(str(local_commit))
    assert not remotes

    # Push.
    pytest.run(local_commit, ['git', 'push', 'origin', 'master'])
    remotes = list_remote(str(local_commit))
    assert [i[1:] for i in remotes] == [['master', 'heads']]
예제 #31
0
def test_dual(local_docs):
    """With two versions, one with master_doc defined.

    :param local_docs: conftest fixture.
    """
    pytest.run(local_docs, ['git', 'checkout', 'feature'])
    local_docs.join('conf.py').write('master_doc = "index"\n')
    local_docs.join('index.rst').write('Test\n'
                                       '====\n'
                                       '\n'
                                       'Sample documentation.\n')
    pytest.run(local_docs, ['git', 'add', 'conf.py', 'index.rst'])
    pytest.run(local_docs,
               ['git', 'commit', '-m', 'Adding docs with master_doc'])
    pytest.run(local_docs, ['git', 'push', 'origin', 'feature'])

    versions = Versions(
        gather_git_info(str(local_docs), ['conf.py'], tuple(), tuple()))
    assert len(versions) == 2

    # Run and verify directory.
    exported_root = py.path.local(pre_build(str(local_docs), versions))
    assert len(exported_root.listdir()) == 2
    assert exported_root.join(versions['master']['sha'],
                              'conf.py').read() == ''
    assert exported_root.join(versions['feature']['sha'],
                              'conf.py').read() == 'master_doc = "index"\n'

    # Verify versions root_dirs and master_docs.
    expected = ['feature/index', 'master/contents']
    assert sorted(
        posixpath.join(r['root_dir'], r['master_doc'])
        for r in versions.remotes) == expected
예제 #32
0
def test_all_errors(tmpdir, local_docs, urls):
    """Test good root ref with all bad non-root refs.

    :param tmpdir: pytest fixture.
    :param local_docs: conftest fixture.
    :param urls: conftest fixture.
    """
    pytest.run(local_docs, ['git', 'checkout', '-b', 'a_broken', 'master'])
    local_docs.join('conf.py').write('master_doc = exception\n')
    pytest.run(local_docs, ['git', 'commit', '-am', 'Broken version.'])
    pytest.run(local_docs, ['git', 'checkout', '-b', 'b_broken', 'a_broken'])
    pytest.run(local_docs, ['git', 'push', 'origin', 'a_broken', 'b_broken'])

    versions = Versions(
        gather_git_info(str(local_docs), ['conf.py'], tuple(), tuple()))

    exported_root = tmpdir.ensure_dir('exported_root')
    export(str(local_docs), versions['master']['sha'],
           str(exported_root.join(versions['master']['sha'])))
    export(str(local_docs), versions['b_broken']['sha'],
           str(exported_root.join(versions['b_broken']['sha'])))

    # Run.
    destination = tmpdir.ensure_dir('destination')
    build_all(str(exported_root), str(destination), versions)
    assert [r['name'] for r in versions.remotes] == ['master']

    # Verify root HTML links.
    urls(destination.join('contents.html'),
         ['<li><a href="master/contents.html">master</a></li>'])
    urls(destination.join('master', 'contents.html'),
         ['<li><a href="contents.html">master</a></li>'])
def test_sub_page_and_tag(tmpdir, local_docs, urls):
    """Test with sub pages and one git tag. Testing from local git repo.

    :param tmpdir: pytest fixture.
    :param local_docs: conftest fixture.
    :param urls: conftest fixture.
    """
    local_docs.ensure('subdir', 'sub.rst').write(
        '.. _sub:\n'
        '\n'
        'Sub\n'
        '===\n'
        '\n'
        'Sub directory sub page documentation.\n'
    )
    local_docs.join('contents.rst').write('    subdir/sub\n', mode='a')
    pytest.run(local_docs, ['git', 'add', 'subdir', 'contents.rst'])
    pytest.run(local_docs, ['git', 'commit', '-m', 'Adding subdir docs.'])
    pytest.run(local_docs, ['git', 'tag', 'v1.0.0'])
    pytest.run(local_docs, ['git', 'push', 'origin', 'master', 'v1.0.0'])

    # Run.
    destination = tmpdir.ensure_dir('destination')
    output = pytest.run(local_docs, ['sphinx-versioning', 'build', '.', str(destination)])
    assert 'Traceback' not in output

    # Check root.
    urls(destination.join('contents.html'), [
        '<li><a href="master/contents.html">master</a></li>',
        '<li><a href="v1.0.0/contents.html">v1.0.0</a></li>'
    ])
    urls(destination.join('subdir', 'sub.html'), [
        '<li><a href="../master/subdir/sub.html">master</a></li>',
        '<li><a href="../v1.0.0/subdir/sub.html">v1.0.0</a></li>',
    ])

    # Check master.
    urls(destination.join('master', 'contents.html'), [
        '<li><a href="contents.html">master</a></li>',
        '<li><a href="../v1.0.0/contents.html">v1.0.0</a></li>',
    ])
    urls(destination.join('master', 'subdir', 'sub.html'), [
        '<li><a href="sub.html">master</a></li>',
        '<li><a href="../../v1.0.0/subdir/sub.html">v1.0.0</a></li>',
    ])

    # Check v1.0.0.
    urls(destination.join('v1.0.0', 'contents.html'), [
        '<li><a href="../master/contents.html">master</a></li>',
        '<li><a href="contents.html">v1.0.0</a></li>',
    ])
    urls(destination.join('v1.0.0', 'subdir', 'sub.html'), [
        '<li><a href="../../master/subdir/sub.html">master</a></li>',
        '<li><a href="sub.html">v1.0.0</a></li>',
    ])
예제 #34
0
def test_no_exclude(tmpdir, local_docs):
    """Simple test without "git rm".

    :param tmpdir: pytest fixture.
    :param local_docs: conftest fixture.
    """
    new_root = tmpdir.ensure_dir('new_root')
    clone(str(local_docs), str(new_root), 'origin', 'master', '', None)
    assert new_root.join('conf.py').check(file=True)
    assert new_root.join('contents.rst').check(file=True)
    assert new_root.join('README').check(file=True)
    branch = pytest.run(new_root, ['git', 'rev-parse', '--abbrev-ref', 'HEAD']).strip()
    assert branch == 'master'
    pytest.run(local_docs, ['git', 'diff-index', '--quiet', 'HEAD', '--'])  # Exit 0 if nothing changed.
    pytest.run(new_root, ['git', 'diff-index', '--quiet', 'HEAD', '--'])  # Exit 0 if nothing changed.
def test_root_ref(local_docs_ghp):
    """Test passing root_ref value from push Click command to build Click command.

    :param local_docs_ghp: conftest fixture.
    """
    pytest.run(local_docs_ghp, ['git', 'tag', 'v1.0.0'])
    pytest.run(local_docs_ghp, ['git', 'push', 'origin', 'v1.0.0'])

    # Run.
    output = pytest.run(local_docs_ghp, ['sphinx-versioning', '-N', 'push', '-t', '.', 'gh-pages', '.'])
    assert 'Traceback' not in output
    assert 'Failed to push to remote repository.' not in output

    # Check output.
    assert 'Root ref is: v1.0.0' in output
def test_root_ref(local_docs_ghp):
    """Test passing root_ref value from push Click command to build Click command.

    :param local_docs_ghp: conftest fixture.
    """
    pytest.run(local_docs_ghp, ['git', 'tag', 'v1.0.0'])
    pytest.run(local_docs_ghp, ['git', 'push', 'origin', 'v1.0.0'])

    # Run.
    output = pytest.run(local_docs_ghp, ['sphinx-versioning', '-N', 'push', '-t', '.', 'gh-pages', '.'])
    assert 'Traceback' not in output
    assert 'Failed to push to remote repository.' not in output

    # Check output.
    assert 'Root ref is: v1.0.0' in output
예제 #37
0
def test_no_disc(tmpdir):
    """Test no disc in device handling.

    :param py.path.local tmpdir: pytest fixture.
    """
    output = tmpdir.ensure_dir('output')
    command = [
        'docker', 'run', '--device=/dev/sr0', '-v',
        '{}:/output'.format(output), '-e', 'DEBUG=true', 'robpol86/makemkv'
    ]
    pytest.cdunload()

    with pytest.raises(subprocess.CalledProcessError) as exc:
        pytest.run(command)
    assert b'Failed to open disc' in exc.value.output
예제 #38
0
def test_devname(tmpdir, devname):
    """Test DEVNAME environment variable.

    :param py.path.local tmpdir: pytest fixture.
    :param str devname: Set environment variable to this.
    """
    output = tmpdir.ensure_dir('output')
    command = [
        'docker', 'run', '-v', '{}:/output'.format(output), '-e', 'DEBUG=true',
        'mboman/makemkv'
    ]

    # Docker acts weird with /dev/cdrom and /dev/sr0 specified at the same time. Workaround:
    if devname == '/dev/sr0':
        command = command[:-1] + ['--device=/dev/sr0'] + command[-1:]
    else:
        command = command[:-1] + ['--device=/dev/cdrom', '--device=/dev/sr0'
                                  ] + command[-1:]

    # Add env variable.
    if devname:
        command = command[:-1] + ['-e', 'DEVNAME={}'.format(devname)
                                  ] + command[-1:]

    # Docker run.
    stdout, stderr = pytest.run(command)

    # Verify.
    assert b'--directio true dev:%s all' % (
        devname or '/dev/cdrom').encode('utf8') in stderr
    assert b'+ eject --verbose %s' % (devname
                                      or '/dev/cdrom').encode('utf8') in stderr
    assert b'\nDone after 00:00:' in stdout
    pytest.verify(output, gid=1000, uid=1000)
예제 #39
0
def build_image(root):
    """Build a new Docker image with any file in root coped to /.

    :param py.path.local root: Root directory of files to copy.

    :return: Same root variable, Dockerfile path, and Docker image ID in a list.
    :rtype: iter
    """
    images = list()

    # Create Dockerfile.
    docker_file = root.ensure('Dockerfile')
    docker_file.write('FROM mboman/makemkv\n')

    # Let caller add files or modify Dockerfile.
    yield root, docker_file, images

    # Append to Dockerfile.
    for path in (p for p in root.listdir()
                 if p.isfile() and p.basename != 'Dockerfile'):
        docker_file.write('COPY {} /\n'.format(path.basename), 'a')

    # Build.
    stdout = pytest.run(['docker', 'build', '.'], cwd=root)[0]
    matches = re.compile(br'^Successfully built ([a-f0-9]+)$',
                         re.MULTILINE).findall(stdout)
    assert matches
    images.extend(m.decode('utf8') for m in matches)
def test_passing_verbose(local_docs, urls, verbosity):
    """Test setting sphinx-build verbosity.

    :param local_docs: conftest fixture.
    :param urls: conftest fixture.
    :param int verbosity: Number of -v to use.
    """
    command = ['sphinx-versioning'] + (['-v'] * verbosity) + ['build', '.', 'destination']

    # Run.
    output = pytest.run(local_docs, command)
    assert 'Traceback' not in output

    # Check master.
    destination = local_docs.join('destination')
    urls(destination.join('contents.html'), ['<li><a href="master/contents.html">master</a></li>'])
    urls(destination.join('master', 'contents.html'), ['<li><a href="contents.html">master</a></li>'])

    # Check output.
    if verbosity == 0:
        assert 'INFO     sphinxcontrib.versioning.__main__' not in output
        assert 'docnames to write:' not in output
    elif verbosity == 1:
        assert 'INFO     sphinxcontrib.versioning.__main__' in output
        assert 'docnames to write:' not in output
    else:
        assert 'INFO     sphinxcontrib.versioning.__main__' in output
        assert 'docnames to write:' in output
예제 #41
0
def test_success(tmpdir):
    """Test most hooks in one test during a successful rip.

    :param py.path.local tmpdir: pytest fixture.
    """
    hooks = ('post-env', 'pre-prepare', 'post-prepare', 'pre-rip',
             'post-title', 'post-rip', 'end')
    with build_image(tmpdir.join('root')) as (root, _, image_ids):
        for hook in hooks:
            root.ensure('hook-{}.sh'.format(hook)).write('env |sort')

    # Docker run.
    output = tmpdir.ensure_dir('output')
    stdout, stderr = pytest.run(output=output, image_id=image_ids[0])

    # Verify.
    for hook in hooks:
        assert b'FIRING HOOK: /hook-%s.sh' % hook.encode('utf8') in stderr
        assert b'_HOOK_SCRIPT=/hook-%s.sh' % hook.encode('utf8') in stdout
        assert b'END OF HOOK: /hook-%s.sh' % hook.encode('utf8') in stderr
        if hook == 'post-title':
            assert re.compile(
                br'^TITLE_PATH=/output/Sample[a-zA-Z0-9_/.-]+/title00\.mkv$',
                re.MULTILINE).search(stdout)
    assert stderr.count(b'\nEND OF HOOK: ') == len(
        hooks)  # Verify no other hooks fired.
    pytest.verify(output)
예제 #42
0
def test_debug(debug):
    """Test DEBUG environment variable.

    :param bool debug: Set environment variable to 'true', 'false', or don't set.
    """
    command = ['docker', 'run', '--device=/dev/cdrom', 'mboman/makemkv']
    if debug is True:
        command = command[:-1] + ['-e', 'DEBUG=true'] + command[-1:]
    elif debug is False:
        command = command[:-1] + ['-e', 'DEBUG=false'] + command[-1:]

    # Docker run.
    stdout, stderr = pytest.run(command)

    # Verify.
    if debug is True:
        # Assert env is called.
        assert b'+ env' in stderr
        assert b'\nPATH=/' in stdout
        # Assert set +x is called.
        assert b'makemkvcon mkv' in stderr
        # Assert eject is verbose.
        assert b'\neject: device name is' in stdout
    else:
        assert b'+ env' not in stderr
        assert b'\nID_FS_TYPE=udf' not in stdout
        assert b'makemkvcon mkv' not in stderr
        assert b'\neject: device name is' not in stdout
    assert b'\nCurrent operation: Scanning CD-ROM devices' in stdout
    assert b'\nDone after 00:00:' in stdout
예제 #43
0
def test_passing_verbose(local_docs, urls, verbosity):
    """Test setting sphinx-build verbosity.

    :param local_docs: conftest fixture.
    :param urls: conftest fixture.
    :param int verbosity: Number of -v to use.
    """
    command = ['sphinx-versioning'
               ] + (['-v'] * verbosity) + ['build', '.', 'destination']

    # Run.
    output = pytest.run(local_docs, command)
    assert 'Traceback' not in output

    # Check master.
    destination = local_docs.join('destination')
    urls(destination.join('contents.html'),
         ['<li><a href="master/contents.html">master</a></li>'])
    urls(destination.join('master', 'contents.html'),
         ['<li><a href="contents.html">master</a></li>'])

    # Check output.
    if verbosity == 0:
        assert 'INFO     sphinxcontrib.versioning.__main__' not in output
        assert 'docnames to write:' not in output
    elif verbosity == 1:
        assert 'INFO     sphinxcontrib.versioning.__main__' in output
        assert 'docnames to write:' not in output
    else:
        assert 'INFO     sphinxcontrib.versioning.__main__' in output
        assert 'docnames to write:' in output
def test_multiple_local_repos(tmpdir, urls):
    """Test from another git repo as the current working directory.

    :param tmpdir: pytest fixture.
    :param urls: conftest fixture.
    """
    other = tmpdir.ensure_dir('other')
    pytest.run(other, ['git', 'init'])

    # Run.
    destination = tmpdir.ensure_dir('destination')
    output = pytest.run(other, ['sphinx-versioning', '-c', '../local', '-v', 'build', '.', str(destination)])
    assert 'Traceback' not in output

    # Check.
    urls(destination.join('contents.html'), ['<li><a href="master/contents.html">master</a></li>'])
    urls(destination.join('master', 'contents.html'), ['<li><a href="contents.html">master</a></li>'])
def test_supported(tmpdir, config, local_docs, theme):
    """Test with different themes. Verify not much changed between sphinx-build and sphinx-versioning.

    :param tmpdir: pytest fixture.
    :param sphinxcontrib.versioning.lib.Config config: conftest fixture.
    :param local_docs: conftest fixture.
    :param str theme: Theme name to use.
    """
    config.overflow = ('-D', 'html_theme=' + theme)
    target_n = tmpdir.ensure_dir('target_n')
    target_y = tmpdir.ensure_dir('target_y')
    versions = Versions([
        ('', 'master', 'heads', 1, 'conf.py'),
        ('', 'feature', 'heads', 2, 'conf.py'),
        ('', 'v1.0.0', 'tags', 3, 'conf.py'),
        ('', 'v1.2.0', 'tags', 4, 'conf.py'),
        ('', 'v2.0.0', 'tags', 5, 'conf.py'),
        ('', 'v2.1.0', 'tags', 6, 'conf.py'),
        ('', 'v2.2.0', 'tags', 7, 'conf.py'),
        ('', 'v2.3.0', 'tags', 8, 'conf.py'),
        ('', 'v2.4.0', 'tags', 9, 'conf.py'),
        ('', 'v2.5.0', 'tags', 10, 'conf.py'),
        ('', 'v2.6.0', 'tags', 11, 'conf.py'),
        ('', 'v2.7.0', 'tags', 12, 'conf.py'),
        ('', 'testing_branch', 'heads', 13, 'conf.py'),
    ], sort=['semver'])

    # Build with normal sphinx-build.
    pytest.run(local_docs, ['sphinx-build', '.', str(target_n), '-D', 'html_theme=' + theme])
    contents_n = target_n.join('contents.html').read()
    assert 'master' not in contents_n

    # Build with versions.
    build(str(local_docs), str(target_y), versions, 'master', True)
    contents_y = target_y.join('contents.html').read()
    assert 'master' in contents_y

    # Verify nothing removed.
    diff = list(difflib.unified_diff(contents_n.splitlines(True), contents_y.splitlines(True)))[2:]
    assert diff
    for line in diff:
        assert not line.startswith('-')

    # Verify added.
    for name in (r['name'] for r in versions.remotes):
        assert any(name in line for line in diff if line.startswith('+'))
def test_banner_css_override(banner, local_docs):
    """Test the banner CSS being present even if user overrides html_context['css_files'].

    :param banner: conftest fixture.
    :param local_docs: conftest fixture.
    """
    local_docs.join('conf.py').write("html_context = {'css_files': ['_static/theme_overrides.css']}\n", mode='a')
    local_docs.join('conf.py').write("html_static_path = ['_static']\n", mode='a')
    pytest.run(local_docs, ['git', 'commit', '-am', 'Setting override.'])
    pytest.run(local_docs, ['git', 'checkout', '-b', 'other', 'master'])
    pytest.run(local_docs, ['git', 'push', 'origin', 'master', 'other'])

    # Run.
    destination = local_docs.ensure_dir('..', 'destination')
    output = pytest.run(local_docs, ['sphinx-versioning', 'build', '.', str(destination), '--show-banner'])
    assert 'Traceback' not in output
    assert 'Disabling banner.' not in output
    assert 'Banner main ref is: master' in output

    # Check banner.
    banner(destination.join('master', 'contents.html'), None)  # No banner in main ref.
    banner(destination.join('other', 'contents.html'), '../master/contents.html',
           'the development version of Python. The main version is master')

    # Check CSS.
    contents = destination.join('other', 'contents.html').read()
    assert 'rel="stylesheet" href="_static/banner.css"' in contents
    assert destination.join('other', '_static', 'banner.css').check(file=True)
def test_nothing_significant_to_commit(caplog, local, subdirs):
    """Test ignoring of always-changing generated Sphinx files.

    :param caplog: pytest extension fixture.
    :param local: conftest fixture.
    :param bool subdirs: Test these files from sub directories.
    """
    local.ensure('sub' if subdirs else '', '.doctrees', 'file.bin').write('data')
    local.ensure('sub' if subdirs else '', 'searchindex.js').write('data')
    old_sha = pytest.run(local, ['git', 'rev-parse', 'HEAD']).strip()
    actual = commit_and_push(str(local), 'origin', Versions(REMOTES))
    assert actual is True
    sha = pytest.run(local, ['git', 'rev-parse', 'HEAD']).strip()
    assert sha != old_sha
    pytest.run(local, ['git', 'diff-index', '--quiet', 'HEAD', '--'])  # Exit 0 if nothing changed.
    records = [(r.levelname, r.message) for r in caplog.records]
    assert ('INFO', 'No changes to commit.') not in records
    assert ('INFO', 'No significant changes to commit.') not in records

    local.ensure('sub' if subdirs else '', '.doctrees', 'file.bin').write('changed')
    local.ensure('sub' if subdirs else '', 'searchindex.js').write('changed')
    old_sha = sha
    records_seek = len(caplog.records)
    actual = commit_and_push(str(local), 'origin', Versions(REMOTES))
    assert actual is True
    sha = pytest.run(local, ['git', 'rev-parse', 'HEAD']).strip()
    assert sha == old_sha
    with pytest.raises(CalledProcessError):
        pytest.run(local, ['git', 'diff-index', '--quiet', 'HEAD', '--'])
    records = [(r.levelname, r.message) for r in caplog.records][records_seek:]
    assert ('INFO', 'No changes to commit.') not in records
    assert ('INFO', 'No significant changes to commit.') in records

    local.join('README').write('changed')  # Should cause other two to be committed.
    old_sha = sha
    records_seek = len(caplog.records)
    actual = commit_and_push(str(local), 'origin', Versions(REMOTES))
    assert actual is True
    sha = pytest.run(local, ['git', 'rev-parse', 'HEAD']).strip()
    assert sha != old_sha
    pytest.run(local, ['git', 'diff-index', '--quiet', 'HEAD', '--'])  # Exit 0 if nothing changed.
    records = [(r.levelname, r.message) for r in caplog.records][records_seek:]
    assert ('INFO', 'No changes to commit.') not in records
    assert ('INFO', 'No significant changes to commit.') not in records
def test_nothing_to_commit(caplog, local, exclude):
    """Test with no changes to commit.

    :param caplog: pytest extension fixture.
    :param local: conftest fixture.
    :param bool exclude: Test with exclude support (aka files staged for deletion). Else clean repo.
    """
    if exclude:
        contents = local.join('README').read()
        pytest.run(local, ['git', 'rm', 'README'])  # Stages removal of README.
        local.join('README').write(contents)  # Unstaged restore.
    old_sha = pytest.run(local, ['git', 'rev-parse', 'HEAD']).strip()

    actual = commit_and_push(str(local), 'origin', Versions(REMOTES))
    assert actual is True
    sha = pytest.run(local, ['git', 'rev-parse', 'HEAD']).strip()
    assert sha == old_sha

    records = [(r.levelname, r.message) for r in caplog.records]
    assert ('INFO', 'No changes to commit.') in records
def test_branch_tags(local):
    """Test with branches and tags.

    :param local: conftest fixture.
    """
    sha = pytest.run(local, ['git', 'rev-parse', 'HEAD']).strip()
    remotes = list_remote(str(local))
    expected = [
        [sha, 'feature', 'heads'],
        [sha, 'master', 'heads'],
        [sha, 'annotated_tag', 'tags'],
        [sha, 'light_tag', 'tags'],
    ]
    assert remotes == expected

    # New commit to master locally.
    local.join('README').write('changed')
    pytest.run(local, ['git', 'commit', '-am', 'Changed'])
    remotes = list_remote(str(local))
    assert remotes == expected

    # Push.
    pytest.run(local, ['git', 'push', 'origin', 'master'])
    sha2 = pytest.run(local, ['git', 'rev-parse', 'HEAD']).strip()
    remotes = list_remote(str(local))
    expected = [
        [sha, 'feature', 'heads'],
        [sha2, 'master', 'heads'],
        [sha, 'annotated_tag', 'tags'],
        [sha, 'light_tag', 'tags'],
    ]
    assert remotes == expected
def test_bad_remote(tmpdir, local_empty):
    """Test with no/invalid remote.

    :param tmpdir: pytest fixture.
    :param local_empty: conftest fixture.
    """
    # Test no remotes.
    with pytest.raises(GitError) as exc:
        list_remote(str(local_empty))
    assert 'No remote configured to list refs from.' in exc.value.output

    # Test wrong name.
    pytest.run(local_empty, ['git', 'remote', 'add', 'something', tmpdir.ensure_dir('empty')])
    with pytest.raises(GitError) as exc:
        list_remote(str(local_empty))
    assert 'No remote configured to list refs from.' in exc.value.output

    # Invalid remote.
    pytest.run(local_empty, ['git', 'remote', 'rename', 'something', 'origin'])
    with pytest.raises(GitError) as exc:
        list_remote(str(local_empty))
    assert 'does not appear to be a git repository' in exc.value.output
def test_changes(monkeypatch, local):
    """Test with changes to commit and push successfully.

    :param monkeypatch: pytest fixture.
    :param local: conftest fixture.
    """
    monkeypatch.setenv('LANG', 'en_US.UTF-8')
    monkeypatch.setenv('TRAVIS_BUILD_ID', '12345')
    monkeypatch.setenv('TRAVIS_BRANCH', 'master')
    old_sha = pytest.run(local, ['git', 'rev-parse', 'HEAD']).strip()
    local.ensure('new', 'new.txt')
    local.join('README').write('test\n', mode='a')

    actual = commit_and_push(str(local), 'origin', Versions(REMOTES))
    assert actual is True
    sha = pytest.run(local, ['git', 'rev-parse', 'HEAD']).strip()
    assert sha != old_sha
    pytest.run(local, ['git', 'diff-index', '--quiet', 'HEAD', '--'])  # Exit 0 if nothing changed.

    # Verify commit message.
    subject, body = pytest.run(local, ['git', 'log', '-n1', '--pretty=%B']).strip().split('\n', 2)[::2]
    assert subject == 'AUTO sphinxcontrib-versioning 20160722 0772e5ff32a'
    assert body == 'LANG: en_US.UTF-8\nTRAVIS_BRANCH: master\nTRAVIS_BUILD_ID: 12345'
def test_new_branch_tags(local, local_light, clone_branch):
    """Test with new branches and tags unknown to local repo.

    :param local: conftest fixture.
    :param local_light: conftest fixture.
    :param bool clone_branch: Test with local repo cloned with --branch.
    """
    if clone_branch:
        local = local_light

    # Get SHAs and verify not fetched.
    remotes = list_remote(str(local))
    assert len(remotes) == 7  # master feature light_tag annotated_tag nb_tag orphaned_branch ob_at
    shas = {r[0] for r in remotes}
    assert len(shas) == 3
    with pytest.raises(GitError):
        filter_and_date(str(local), ['README'], shas)

    # Fetch and verify.
    fetch_commits(str(local), remotes)
    dates = filter_and_date(str(local), ['README'], shas)
    assert len(dates) == 3
    pytest.run(local, ['git', 'diff-index', '--quiet', 'HEAD', '--'])
def test_last_updated(tmpdir, local_docs):
    """Test last updated timestamp derived from git authored time.

    :param tmpdir: pytest fixture.
    :param local_docs: conftest fixture.
    """
    local_docs.join('conf.py').write(
        'html_last_updated_fmt = "%c"\n'
        'html_theme="sphinx_rtd_theme"\n'
    )
    local_docs.join('two.rst').write('Changed\n', mode='a')
    pytest.run(local_docs, ['git', 'commit', '-am', 'Changed two.'], environ=pytest.author_committer_dates(10))
    pytest.run(local_docs, ['git', 'checkout', '-b', 'other', 'master'])
    local_docs.join('three.rst').write('Changed\n', mode='a')
    pytest.run(local_docs, ['git', 'commit', '-am', 'Changed three.'], environ=pytest.author_committer_dates(11))
    pytest.run(local_docs, ['git', 'push', 'origin', 'master', 'other'])

    versions = Versions(gather_git_info(str(local_docs), ['conf.py'], tuple(), tuple()))

    # Export.
    exported_root = tmpdir.ensure_dir('exported_root')
    export(str(local_docs), versions['master']['sha'], str(exported_root.join(versions['master']['sha'])))
    export(str(local_docs), versions['other']['sha'], str(exported_root.join(versions['other']['sha'])))

    # Run.
    destination = tmpdir.ensure_dir('destination')
    build_all(str(exported_root), str(destination), versions)

    # Verify master.
    one = RE_LAST_UPDATED.findall(destination.join('master', 'one.html').read())
    two = RE_LAST_UPDATED.findall(destination.join('master', 'two.html').read())
    three = RE_LAST_UPDATED.findall(destination.join('master', 'three.html').read())
    assert one == ['Last updated on Dec 5, 2016, 3:20:05 AM.\n']
    assert two == ['Last updated on Dec 5, 2016, 3:27:05 AM.\n']
    assert three == ['Last updated on Dec 5, 2016, 3:20:05 AM.\n']

    # Verify other.
    one = RE_LAST_UPDATED.findall(destination.join('other', 'one.html').read())
    two = RE_LAST_UPDATED.findall(destination.join('other', 'two.html').read())
    three = RE_LAST_UPDATED.findall(destination.join('other', 'three.html').read())
    assert one == ['Last updated on Dec 5, 2016, 3:20:05 AM.\n']
    assert two == ['Last updated on Dec 5, 2016, 3:27:05 AM.\n']
    assert three == ['Last updated on Dec 5, 2016, 3:28:05 AM.\n']
def test_branch_deleted(local):
    """Test scenario where branch is deleted by someone.

    :param local: conftest fixture.
    """
    pytest.run(local, ['git', 'checkout', 'feature'])
    pytest.run(local, ['git', 'push', 'origin', '--delete', 'feature'])
    local.join('README').write('Changed by local.')

    # Run.
    actual = commit_and_push(str(local), 'origin', Versions(REMOTES))
    assert actual is True
    pytest.run(local, ['git', 'diff-index', '--quiet', 'HEAD', '--'])  # Exit 0 if nothing changed.
    assert local.join('README').read() == 'Changed by local.'
def test_bad_branch_rel_dest_exclude(tmpdir, local):
    """Test bad data.

    :param tmpdir: pytest fixture.
    :param local: conftest fixture.
    """
    # Unknown branch.
    with pytest.raises(GitError) as exc:
        clone(str(local), str(tmpdir.ensure_dir('new_root')), 'origin', 'unknown_branch', '.', None)
    assert 'Remote branch unknown_branch not found in upstream origin' in exc.value.output

    # Not a branch.
    with pytest.raises(GitError) as exc:
        clone(str(local), str(tmpdir.ensure_dir('new_root')), 'origin', 'light_tag', '.', None)
    assert 'fatal: ref HEAD is not a symbolic ref' in exc.value.output

    # rel_dest outside of repo.
    with pytest.raises(GitError) as exc:
        clone(str(local), str(tmpdir.ensure_dir('new_root2')), 'origin', 'master', '..', ['README'])
    assert "'..' is outside repository" in exc.value.output

    # rel_dest invalid.
    with pytest.raises(GitError) as exc:
        clone(str(local), str(tmpdir.ensure_dir('new_root3')), 'origin', 'master', 'unknown', ['README'])
    assert "pathspec 'unknown' did not match any files" in exc.value.output

    # No origin.
    pytest.run(local, ['git', 'remote', 'rename', 'origin', 'origin2'])
    with pytest.raises(GitError) as exc:
        clone(str(local), str(tmpdir.ensure_dir('new_root3')), 'origin', 'master', '.', None)
    assert 'Git repo missing remote "origin".' in exc.value.message
    assert 'origin2\t' in exc.value.output
    assert 'origin\t' not in exc.value.output

    # No remote.
    pytest.run(local, ['git', 'remote', 'rm', 'origin2'])
    with pytest.raises(GitError) as exc:
        clone(str(local), str(tmpdir.ensure_dir('new_root3')), 'origin', 'master', '.', None)
    assert 'Git repo has no remotes.' in exc.value.message
    assert not exc.value.output

    # Bad remote.
    pytest.run(local, ['git', 'remote', 'add', 'origin', local.join('does_not_exist')])
    with pytest.raises(GitError) as exc:
        clone(str(local), str(tmpdir.ensure_dir('new_root4')), 'origin', 'master', '.', None)
    if IS_WINDOWS:
        assert "'{}' does not appear to be a git repository".format(local.join('does_not_exist')) in exc.value.output
    else:
        assert "repository '{}' does not exist".format(local.join('does_not_exist')) in exc.value.output
def test_exclude_subdir(tmpdir, local):
    """Test with grm_dir set to a subdirectory.

    :param tmpdir: pytest fixture.
    :param local: conftest fixture.
    """
    local.ensure('sub', 'three.txt').write('three')
    local.ensure('sub', 'four.txt').write('four')
    pytest.run(local, ['git', 'add', 'sub'])
    pytest.run(local, ['git', 'commit', '-m', 'Adding new files.'])
    pytest.run(local, ['git', 'push', 'origin', 'master'])

    new_root = tmpdir.ensure_dir('new_root')
    clone(str(local), str(new_root), 'origin', 'master', 'sub', ['three.txt'])
    paths = sorted(f.relto(new_root) for f in new_root.visit() if new_root.join('.git') not in f.parts())
    assert paths == ['README', 'sub', join('sub', 'three.txt')]

    status = pytest.run(new_root, ['git', 'status', '--porcelain'])
    assert status == 'D  sub/four.txt\n'
def test_retryable_race(tmpdir, local, remote, collision):
    """Test race condition scenario where another CI build pushes changes first.

    :param tmpdir: pytest fixture.
    :param local: conftest fixture.
    :param remote: conftest fixture.
    :param bool collision: Have other repo make changes to the same file as this one.
    """
    local_other = tmpdir.ensure_dir('local_other')
    pytest.run(local_other, ['git', 'clone', remote, '.'])
    local_other.ensure('sub', 'ignored.txt').write('Added by other. Should be ignored by commit_and_push().')
    if collision:
        local_other.ensure('sub', 'added.txt').write('Added by other.')
    pytest.run(local_other, ['git', 'add', 'sub'])
    pytest.run(local_other, ['git', 'commit', '-m', 'Added by other.'])
    pytest.run(local_other, ['git', 'push', 'origin', 'master'])

    # Make unstaged changes and then run.
    local.ensure('sub', 'added.txt').write('Added by local.')
    actual = commit_and_push(str(local), 'origin', Versions(REMOTES))

    # Verify.
    assert actual is False