Cleanup and fix tests

build-pdf-ret-val
Anthony Johnson 2015-08-04 14:34:13 -07:00
parent 875624a022
commit 176742ba53
7 changed files with 52 additions and 146 deletions

View File

@ -1,41 +0,0 @@
import logging
import fileinput
import json
from django.core.management.base import BaseCommand
from readthedocs.projects import tasks
log = logging.getLogger(__name__)
class Command(BaseCommand):
"""Trigger build for project version"""
args = '<files>'
def handle(self, files=None, *args, **options):
def _return_json(output):
return json.dumps(output)
output = None
try:
input_data = self._get_input(files)
version_data = json.loads(input_data)
version = tasks.make_api_version(version_data)
log.info('Building %s', version)
output = _return_json(tasks.docker_build(version))
except Exception as e:
log.exception('Error starting docker build')
output = _return_json(
{'doc_builder': (-1, '', '{0}: {1}'.format(type(e).__name__,
str(e)))})
finally:
print(output)
def _get_input(self, files=None):
if files is None:
files = '-'
fh = fileinput.FileInput(files)
return ''.join([line for line in fh])

View File

@ -11,11 +11,14 @@ from django.template.loader import render_to_string
from django.conf import settings
from readthedocs.builds import utils as version_utils
from readthedocs.doc_builder.base import BaseBuilder, restoring_chdir
from readthedocs.projects.utils import safe_write
from readthedocs.projects.exceptions import ProjectImportError
from readthedocs.restapi.client import api
from ..base import BaseBuilder, restoring_chdir
from ..exceptions import BuildEnvironmentError
log = logging.getLogger(__name__)
TEMPLATE_DIR = '%s/readthedocs/templates/sphinx' % settings.SITE_ROOT

View File

@ -19,7 +19,6 @@ def restoring_chdir(fn):
class BaseBuilder(object):
"""
The Base for all Builders. Defines the API for subclasses.
@ -31,9 +30,9 @@ class BaseBuilder(object):
# old_artifact_path = ..
def __init__(self, version, force=False, build_env=None):
self.project = build_env.project
# TODO do we really need to push the version in here?
self.version = version
self.project = version.project
self._force = force
self.target = self.version.project.artifact_path(
version=self.version.slug,

View File

@ -20,7 +20,6 @@ from docker.utils import create_host_config
from docker.errors import APIError as DockerAPIError, DockerException
from readthedocs.builds.constants import BUILD_STATE_FINISHED
from readthedocs.projects.utils import run
from readthedocs.restapi.serializers import VersionFullSerializer
from readthedocs.projects.constants import LOG_TEMPLATE
from readthedocs.api.client import api as api_v1
@ -139,7 +138,7 @@ class DockerBuildCommand(BuildCommand):
Build command to execute in docker container
'''
def run(self, cmd_input=None, combine_output=True):
def run(self):
'''Execute command in existing Docker container
:param cmd_input: input to pass to command in STDIN
@ -208,9 +207,10 @@ class BuildEnvironment(object):
def __exit__(self, exc_type, exc_value, tb):
ret = self.handle_exception(exc_type, exc_value, tb)
self.update_build(state=BUILD_STATE_FINISHED)
log.info(LOG_TEMPLATE.format(project=self.project.slug,
version=self.version.slug,
msg='Build finished'))
log.info(LOG_TEMPLATE
.format(project=self.project.slug,
version=self.version.slug,
msg='Build finished'))
return ret
def handle_exception(self, exc_type, exc_value, tb):
@ -225,13 +225,11 @@ class BuildEnvironment(object):
exception will bubble up.
"""
if exc_type is not None:
log.error(
LOG_TEMPLATE.format(
project=self.project.slug,
version=self.version.slug,
msg=exc_value),
exc_info=True
)
log.error(LOG_TEMPLATE
.format(project=self.project.slug,
version=self.version.slug,
msg=exc_value),
exc_info=True)
if issubclass(exc_type, BuildEnvironmentWarning):
return True
else:
@ -240,7 +238,6 @@ class BuildEnvironment(object):
return True
return False
def run(self, *cmd, **kwargs):
'''Run command'''
kwargs['build_env'] = self
@ -258,18 +255,18 @@ class BuildEnvironment(object):
@property
def successful(self):
# TODO should this include a check for finished state?
return (self.failure is None
and all(cmd.successful for cmd in self.commands))
return (self.failure is None and
all(cmd.successful for cmd in self.commands))
@property
def failed(self):
return (self.failure is not None
or any(cmd.failed for cmd in self.commands))
return (self.failure is not None or
any(cmd.failed for cmd in self.commands))
@property
def done(self):
return (self.build is not None
and self.build.state == BUILD_STATE_FINISHED)
return (self.build is not None and
self.build['state'] == BUILD_STATE_FINISHED)
def update_build(self, state=None):
"""
@ -302,8 +299,8 @@ class BuildEnvironment(object):
# TODO Replace this with per-command output tracking in the db
self.build['output'] = '\n'.join([str(cmd)
for cmd in self.commands
if cmd is not None])
for cmd in self.commands
if cmd is not None])
# Attempt to stop unicode errors on build reporting
for key, val in self.build.items():

View File

@ -36,6 +36,17 @@ def build_subprocess_side_effect(*args, **kwargs):
class BuildTests(TestCase):
def setUp(self):
self.patches = {}
self.mocks = {}
self.patches['update_build'] = mock.patch.object(LocalEnvironment,
'update_build')
self.mocks['update_build'] = self.patches['update_build'].start()
def tearDown(self):
for patch in self.patches:
self.patches[patch].stop()
@mock.patch('slumber.Resource')
@mock.patch('os.chdir')
@mock.patch('readthedocs.projects.models.Project.api_versions')
@ -51,10 +62,12 @@ class BuildTests(TestCase):
# subprocess mock logic
mock_process = mock.Mock()
process_return_dict = {'communicate.return_value': ('SOMEGITHASH', '')}
process_return_dict = {
'communicate.return_value': ('SOMEGITHASH', ''),
'returncode': 0
}
mock_process.configure_mock(**process_return_dict)
mock_Popen.return_value = mock_process
mock_Popen.side_effect = build_subprocess_side_effect
project = get(Project,
slug='project-1',
@ -79,18 +92,15 @@ class BuildTests(TestCase):
task.project = project
with mock.patch('codecs.open', mock.mock_open(), create=True):
with fake_paths_lookup({conf_path: True}):
built_docs = task.build_docs(version,
False,
False)
built_docs = task.build_docs_html(False)
builder_class = get_builder_class(project.documentation_type)
builder = builder_class(version)
with build_env:
builder_class = get_builder_class(project.documentation_type)
builder = builder_class(version)
self.assertIn(builder.sphinx_builder,
str(mock_Popen.call_args_list[1])
)
# We are using the comment builder
def test_builder_comments(self):
# Normal build

View File

@ -1,9 +1,11 @@
import os
from os.path import exists
import shutil
from tempfile import mkdtemp
from django.contrib.auth.models import User
import json
import shutil
from os.path import exists
from tempfile import mkdtemp
from django.contrib.auth.models import User
from mock import patch, MagicMock
from readthedocs.projects.models import Project
from readthedocs.projects import tasks
@ -59,6 +61,10 @@ class TestCeleryBuilding(RTDTestCase):
self.assertTrue(result.successful())
self.assertFalse(exists(directory))
@patch('readthedocs.projects.tasks.UpdateDocsTask.build_docs',
new=MagicMock)
@patch('readthedocs.projects.tasks.UpdateDocsTask.setup_vcs',
new=MagicMock)
def test_update_docs(self):
with mock_api(self.repo):
update_docs = tasks.UpdateDocsTask()

View File

@ -1,68 +0,0 @@
from StringIO import StringIO
from django.test import TestCase
from mock import patch
from readthedocs.core.management.commands import run_docker
from readthedocs.projects.models import Project
from readthedocs.builds.models import Version
class TestRunDocker(TestCase):
'''Test run_docker command with good input and output'''
fixtures = ['test_data']
def setUp(self):
self.project = Project.objects.get(slug='pip')
self.version = Version(slug='foo', verbose_name='foobar')
self.project.versions.add(self.version)
def _get_input(self, files=None):
return ('{"project": {"id": 6, "name": "Pip", "slug": "pip"},'
'"id": 71, "type": "tag", "identifier": "437fb316fbbdba1acdd22e07dbe7c4809ffd97e6",'
'"verbose_name": "stable", "slug": "stable"}')
def _docker_build(data):
if isinstance(data, Version):
return {'html': (0, 'DOCKER PASS', '')}
else:
return {'html': (1, '', 'DOCKER FAIL')}
def test_stdin(self):
'''Test docker build command'''
def _input(_, files=None):
return '{"test": "foobar"}'
with patch.object(run_docker.Command, '_get_input', _input):
cmd = run_docker.Command()
assert cmd._get_input() == '{"test": "foobar"}'
@patch.object(run_docker.Command, '_get_input', _get_input)
@patch('readthedocs.projects.tasks.docker_build', _docker_build)
@patch('sys.stdout', new_callable=StringIO)
def test_good_input(self, mock_output):
'''Test docker build command'''
cmd = run_docker.Command()
self.assertEqual(cmd._get_input(), self._get_input())
cmd.handle()
self.assertEqual(
mock_output.getvalue(),
'{"html": [0, "DOCKER PASS", ""]}\n'
)
@patch('readthedocs.projects.tasks.docker_build', _docker_build)
def test_bad_input(self):
'''Test docker build command'''
with patch.object(run_docker.Command, '_get_input') as mock_input:
with patch('sys.stdout', new_callable=StringIO) as mock_output:
mock_input.return_value = 'BAD JSON'
cmd = run_docker.Command()
cmd.handle()
self.assertEqual(
mock_output.getvalue(),
('{"doc_builder": '
'[-1, "", "ValueError: No JSON object could be decoded"]}'
'\n')
)