def test_incremental_state_update_with_no_update(self): """Build updates to a non-finished state when update_on_success=True.""" build_envs = [ LocalEnvironment( version=self.version, project=self.project, build={'id': DUMMY_BUILD_ID}, ), LocalEnvironment( version=self.version, project=self.project, build={'id': DUMMY_BUILD_ID}, update_on_success=False, ), ] for build_env in build_envs: with build_env: build_env.update_build(BUILD_STATE_CLONING) self.mocks.mocks['api_v2.build']().put.assert_called_with({ 'id': DUMMY_BUILD_ID, 'version': self.version.pk, 'success': True, 'project': self.project.pk, 'setup_error': u'', 'length': mock.ANY, 'error': '', 'setup': u'', 'output': u'', 'state': BUILD_STATE_CLONING, 'builder': mock.ANY, 'exit_code': 0, })
def test_build_respects_epub_flag(self): '''Test build with epub enabled''' project = get(Project, slug='project-1', documentation_type='sphinx', conf_py_file='test_conf.py', enable_pdf_build=False, enable_epub_build=True, versions=[fixture()]) version = project.versions.all()[0] build_env = LocalEnvironment(project=project, version=version, build={}) python_env = Virtualenv(version=version, build_env=build_env) yaml_config = get_build_config({}) config = ConfigWrapper(version=version, yaml_config=yaml_config) task = UpdateDocsTask(build_env=build_env, project=project, python_env=python_env, version=version, search=False, localmedia=False, config=config) task.build_docs() # The HTML and the Epub format were built. self.mocks.html_build.assert_called_once_with() self.mocks.epub_build.assert_called_once_with() # PDF however was disabled and therefore not built. self.assertFalse(self.mocks.pdf_build.called)
def test_build_respects_pdf_flag(self): '''Build output format control''' project = get(Project, slug='project-1', documentation_type='sphinx', conf_py_file='test_conf.py', enable_pdf_build=True, enable_epub_build=False, versions=[fixture()]) version = project.versions.all()[0] build_env = LocalEnvironment(project=project, version=version, build={}) task = UpdateDocsTask(build_env=build_env, version=version, project=project, search=False, localmedia=False) built_docs = task.build_docs() # The HTML and the Epub format were built. self.mocks.html_build.assert_called_once_with() self.mocks.pdf_build.assert_called_once_with() # PDF however was disabled and therefore not built. self.assertFalse(self.mocks.epub_build.called)
def test_failing_execution_with_unexpected_exception(self): """Build in failing state with exception from code.""" build_env = LocalEnvironment( version=self.version, project=self.project, build={'id': DUMMY_BUILD_ID}, ) with build_env: raise ValueError('uncaught') self.assertFalse(self.mocks.process.communicate.called) self.assertTrue(build_env.done) self.assertTrue(build_env.failed) # api() is not called anymore, we use api_v2 instead self.assertFalse(self.mocks.api()(DUMMY_BUILD_ID).put.called) self.mocks.mocks['api_v2.build']().put.assert_called_with({ 'id': DUMMY_BUILD_ID, 'version': self.version.pk, 'success': False, 'project': self.project.pk, 'setup_error': u'', 'length': mock.ANY, 'error': ( 'There was a problem with Read the Docs while building your ' 'documentation. Please report this to us with your build id (123).' ), 'setup': u'', 'output': u'', 'state': u'finished', 'builder': mock.ANY, })
def test_failing_execution_with_caught_exception(self): """Build in failing state with BuildEnvironmentError exception.""" build_env = LocalEnvironment( version=self.version, project=self.project, build={'id': DUMMY_BUILD_ID}, ) with build_env: raise BuildEnvironmentError('Foobar') self.assertFalse(self.mocks.process.communicate.called) self.assertEqual(len(build_env.commands), 0) self.assertTrue(build_env.done) self.assertTrue(build_env.failed) # api() is not called anymore, we use api_v2 instead self.assertFalse(self.mocks.api()(DUMMY_BUILD_ID).put.called) self.mocks.mocks['api_v2.build']().put.assert_called_with({ 'id': DUMMY_BUILD_ID, 'version': self.version.pk, 'success': False, 'project': self.project.pk, 'setup_error': u'', 'length': mock.ANY, 'error': 'Foobar', 'setup': u'', 'output': u'', 'state': u'finished', 'builder': mock.ANY, 'exit_code': 1, })
def test_build(self): '''Test full build''' project = get(Project, slug='project-1', documentation_type='sphinx', conf_py_file='test_conf.py', versions=[fixture()]) version = project.versions.all()[0] self.mocks.configure_mock('api_versions', {'return_value': [version]}) self.mocks.configure_mock( 'api', {'get.return_value': { 'downloads': "no_url_here" }}) self.mocks.patches['html_build'].stop() build_env = LocalEnvironment(project=project, version=version, build={}) python_env = Virtualenv(version=version, build_env=build_env) yaml_config = get_build_config({}) config = ConfigWrapper(version=version, yaml_config=yaml_config) task = UpdateDocsTask(build_env=build_env, project=project, python_env=python_env, version=version, search=False, localmedia=False, config=config) task.build_docs() # Get command and check first part of command list is a call to sphinx self.assertEqual(self.mocks.popen.call_count, 1) cmd = self.mocks.popen.call_args_list[0][0] self.assertRegexpMatches(cmd[0][0], r'python') self.assertRegexpMatches(cmd[0][1], r'sphinx-build')
def __init__(self, project, version_slug, environment=None, **kwargs): self.default_branch = project.default_branch self.name = project.name self.repo_url = project.clean_repo self.working_dir = project.checkout_path(version_slug) from readthedocs.doc_builder.environments import LocalEnvironment self.environment = environment or LocalEnvironment(project) # Update the env variables with the proper VCS env variables self.environment.environment.update(self.env)
def __init__(self, project): self.project = project self.project_root = os.path.join( self.WEB_ROOT, project.slug, ) self.subproject_root = os.path.join( self.project_root, 'projects', ) self.environment = LocalEnvironment(project) self.sanity_check()
def test_failing_execution_with_caught_exception(self): '''Build in failing state with BuildEnvironmentError exception''' build_env = LocalEnvironment(version=self.version, project=self.project, build={}) with build_env: raise BuildEnvironmentError('Foobar') self.assertFalse(self.mocks.process.communicate.called) self.assertEqual(len(build_env.commands), 0) self.assertTrue(build_env.done) self.assertTrue(build_env.failed)
def test_builder_comments(self): '''Normal build with comments''' project = get(Project, documentation_type='sphinx', allow_comments=True, versions=[fixture()]) version = project.versions.all()[0] build_env = LocalEnvironment(version=version, project=project, build={}) python_env = Virtualenv(version=version, build_env=build_env) builder_class = get_builder_class(project.documentation_type) builder = builder_class(build_env, python_env) self.assertEqual(builder.sphinx_builder, 'readthedocs-comments')
def test_failing_execution_with_uncaught_exception(self): '''Build in failing state with exception from code''' build_env = LocalEnvironment(version=self.version, project=self.project, build={}) def _inner(): with build_env: raise Exception() self.assertRaises(Exception, _inner) self.assertFalse(self.mocks.process.communicate.called) self.assertTrue(build_env.done) self.assertTrue(build_env.failed)
def test_builder_no_comments(self): '''Test builder without comments''' project = get(Project, documentation_type='sphinx', allow_comments=False, versions=[fixture()]) version = project.versions.all()[0] build_env = LocalEnvironment(version=version, project=project, build={}) builder_class = get_builder_class(project.documentation_type) builder = builder_class(build_env) self.assertEqual(builder.sphinx_builder, 'readthedocs')
def test_failing_execution_with_unexpected_exception(self): '''Build in failing state with exception from code''' build_env = LocalEnvironment(version=self.version, project=self.project, build={'id': DUMMY_BUILD_ID}) with build_env: raise ValueError('uncaught') self.assertFalse(self.mocks.process.communicate.called) self.assertTrue(build_env.done) self.assertTrue(build_env.failed) self.assertFalse(self.mocks.api()(DUMMY_BUILD_ID).put.called)
def run_setup(self, record=True): """ Run setup in the local environment. Return True if successful. """ self.setup_env = LocalEnvironment( project=self.project, version=self.version, build=self.build, record=record, update_on_success=False, ) # Environment used for code checkout & initial configuration reading with self.setup_env: if self.project.skip: raise BuildEnvironmentError( _('Builds for this project are temporarily disabled')) try: self.setup_vcs() except vcs_support_utils.LockTimeout as e: self.retry(exc=e, throw=False) raise BuildEnvironmentError( 'Version locked, retrying in 5 minutes.', status_code=423 ) try: self.config = load_yaml_config(version=self.version) except ConfigError as e: raise BuildEnvironmentError( 'Problem parsing YAML configuration. {0}'.format(str(e)) ) if self.setup_env.failure or self.config is None: self._log('Failing build because of setup failure: %s' % self.setup_env.failure) # Send notification to users only if the build didn't fail because of # LockTimeout: this exception occurs when a build is triggered before the previous # one has finished (e.g. two webhooks, one after the other) if not isinstance(self.setup_env.failure, vcs_support_utils.LockTimeout): self.send_notifications() return False if self.setup_env.successful and not self.project.has_valid_clone: self.set_valid_clone() return True
def test_build_pdf_latex_not_failure(self): '''Test pass during PDF builds and bad latex failure status code''' self.mocks.patches['html_build'].stop() self.mocks.patches['pdf_build'].stop() project = get(Project, slug='project-2', documentation_type='sphinx', conf_py_file='test_conf.py', enable_pdf_build=True, enable_epub_build=False, versions=[fixture()]) version = project.versions.all()[0] assert project.conf_dir() == '/tmp/rtd' build_env = LocalEnvironment(project=project, version=version, build={}) python_env = Virtualenv(version=version, build_env=build_env) yaml_config = get_build_config({}) config = ConfigWrapper(version=version, yaml_config=yaml_config) task = UpdateDocsTask(build_env=build_env, project=project, python_env=python_env, version=version, search=False, localmedia=False, config=config) # Mock out the separate calls to Popen using an iterable side_effect returns = [ (('', ''), 0), # sphinx-build html (('', ''), 0), # sphinx-build pdf (('Output written on foo.pdf', ''), 1), # latex (('', ''), 0), # makeindex (('', ''), 0), # latex ] mock_obj = mock.Mock() mock_obj.communicate.side_effect = [ output for (output, status) in returns ] type(mock_obj).returncode = mock.PropertyMock( side_effect=[status for (output, status) in returns]) self.mocks.popen.return_value = mock_obj with build_env: task.build_docs() self.assertEqual(self.mocks.popen.call_count, 5) self.assertTrue(build_env.successful)
def test_failing_execution(self): """Build in failing state.""" self.mocks.configure_mock( 'process', {'communicate.return_value': (b'This is not okay', '')}) type(self.mocks.process).returncode = PropertyMock(return_value=1) build_env = LocalEnvironment( version=self.version, project=self.project, build={'id': DUMMY_BUILD_ID}, ) with build_env: build_env.run('echo', 'test') self.fail('This should be unreachable') self.assertTrue(self.mocks.process.communicate.called) self.assertTrue(build_env.done) self.assertTrue(build_env.failed) self.assertEqual(len(build_env.commands), 1) self.assertEqual(build_env.commands[0].output, u'This is not okay') # api() is not called anymore, we use api_v2 instead self.assertFalse(self.mocks.api()(DUMMY_BUILD_ID).put.called) self.mocks.mocks['api_v2.build']().put.assert_called_with({ 'id': DUMMY_BUILD_ID, 'version': self.version.pk, 'success': False, 'project': self.project.pk, 'setup_error': u'', 'length': mock.ANY, 'error': '', 'setup': u'', 'output': u'', 'state': u'finished', 'builder': mock.ANY, 'exit_code': 1, })
def test_normal_execution(self): '''Normal build in passing state''' self.mocks.configure_mock('process', { 'communicate.return_value': ('This is okay', '')}) type(self.mocks.process).returncode = PropertyMock(return_value=0) build_env = LocalEnvironment(version=self.version, project=self.project, build={}) with build_env: build_env.run('echo', 'test') self.assertTrue(self.mocks.process.communicate.called) self.assertTrue(build_env.done) self.assertTrue(build_env.successful) self.assertEqual(len(build_env.commands), 1) self.assertEqual(build_env.commands[0].output, u'This is okay')
def test_failing_execution(self): '''Build in failing state''' self.mocks.configure_mock('process', { 'communicate.return_value': ('This is not okay', '')}) type(self.mocks.process).returncode = PropertyMock(return_value=1) build_env = LocalEnvironment(version=self.version, project=self.project, build={}) with build_env: build_env.run('echo', 'test') self.fail('This should be unreachable') self.assertTrue(self.mocks.process.communicate.called) self.assertTrue(build_env.done) self.assertTrue(build_env.failed) self.assertEqual(len(build_env.commands), 1) self.assertEqual(build_env.commands[0].output, u'This is not okay')
def test_build_pdf_latex_failures(self): '''Build failure if latex fails''' if six.PY3: import pytest pytest.xfail( "test_build_pdf_latex_failures is known to fail on 3.6") self.mocks.patches['html_build'].stop() self.mocks.patches['pdf_build'].stop() project = get(Project, slug='project-1', documentation_type='sphinx', conf_py_file='test_conf.py', enable_pdf_build=True, enable_epub_build=False, versions=[fixture()]) version = project.versions.all()[0] assert project.conf_dir() == '/tmp/rtd' build_env = LocalEnvironment(project=project, version=version, build={}) python_env = Virtualenv(version=version, build_env=build_env) config = ConfigWrapper(version=version, yaml_config=create_load()()[0]) task = UpdateDocsTask(build_env=build_env, project=project, python_env=python_env, version=version, search=False, localmedia=False, config=config) # Mock out the separate calls to Popen using an iterable side_effect returns = [ (('', ''), 0), # sphinx-build html (('', ''), 0), # sphinx-build pdf (('', ''), 1), # latex (('', ''), 0), # makeindex (('', ''), 0), # latex ] mock_obj = mock.Mock() mock_obj.communicate.side_effect = [output for (output, status) in returns] type(mock_obj).returncode = mock.PropertyMock( side_effect=[status for (output, status) in returns]) self.mocks.popen.return_value = mock_obj with build_env: task.build_docs() self.assertEqual(self.mocks.popen.call_count, 7) self.assertTrue(build_env.failed)