From df6addceee042dff0fa9bddb42106808e4a43471 Mon Sep 17 00:00:00 2001 From: Matjaz Gregoric Date: Tue, 17 Nov 2015 09:23:05 +0100 Subject: [PATCH 1/6] Fix flaky scroll_into_view test. The scrolling is not entirely precise, it's sometimes off by a pixel or two. Increase the tolerance to 2 pixels. --- problem_builder/tests/integration/test_step_builder.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/problem_builder/tests/integration/test_step_builder.py b/problem_builder/tests/integration/test_step_builder.py index 37c542dc..438864da 100644 --- a/problem_builder/tests/integration/test_step_builder.py +++ b/problem_builder/tests/integration/test_step_builder.py @@ -1,5 +1,3 @@ -import time - from mock import patch from ddt import ddt, data from selenium.webdriver.support.ui import WebDriverWait @@ -1332,7 +1330,8 @@ def check_viewport(self): def is_scrolled_to_top(driver): scroll_top = int(driver.execute_script("return $(window).scrollTop()")) - return abs(scroll_top - step_builder_offset) < 1 + tolerance = 2 + return abs(scroll_top - step_builder_offset) <= tolerance wait = WebDriverWait(self.browser, 5) wait.until(is_scrolled_to_top) @@ -1342,7 +1341,7 @@ def scroll_down(self): def test_scroll_into_view(self): # Make window small, so that we have to scroll. - self.browser.set_window_size(400, 400) + self.browser.set_window_size(600, 400) step_builder, controls = self.load_assessment_scenario("step_builder_long_steps.xml", {}) # First step self.check_viewport() From 6838aa8bdfa1ee52e3243e756ab26226afb4eb50 Mon Sep 17 00:00:00 2001 From: Matjaz Gregoric Date: Tue, 17 Nov 2015 10:11:52 +0100 Subject: [PATCH 2/6] Patch wsgi handler to ignore broken pipe errors. We would often see 'Broken pipe' errors pulluting the logs when running tests. Broken pipe errors occur when browser aborts connection. It can be because the browser was navigated to a different url before the current page finished loading, but it could also be because it started downloading an image, but decided that the image didn't change after loading part of it, and aborts the connection to use the version from the cache. The broken pipe errors are benign and are actually suppressed by default in Django 1.8. --- run_tests.py | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/run_tests.py b/run_tests.py index d34ddff9..7be53749 100755 --- a/run_tests.py +++ b/run_tests.py @@ -9,7 +9,6 @@ import os import sys - import logging logging_level_overrides = { @@ -18,7 +17,35 @@ 'workbench.runtime': logging.ERROR, } +def patch_broken_pipe_error(): + """Monkey Patch BaseServer.handle_error to not write a stacktrace to stderr on broken pipe. + This message is automatically suppressed in Django 1.8, so this monkey patch can be + removed once the workbench upgrades to Django >= 1.8. + http://stackoverflow.com/a/22618740/51397""" + + from SocketServer import BaseServer + from wsgiref import handlers + + handle_error = BaseServer.handle_error + log_exception = handlers.BaseHandler.log_exception + + def is_broken_pipe_error(): + type, err, tb = sys.exc_info() + return repr(err) == "error(32, 'Broken pipe')" + + def my_handle_error(self, request, client_address): + if not is_broken_pipe_error(): + handle_error(self, request, client_address) + + def my_log_exception(self, exc_info): + if not is_broken_pipe_error(): + log_exception(self, exc_info) + + BaseServer.handle_error = my_handle_error + handlers.BaseHandler.log_exception = my_log_exception + if __name__ == "__main__": + patch_broken_pipe_error() # Use the workbench settings file: os.environ.setdefault("DJANGO_SETTINGS_MODULE", "workbench.settings") # Configure a range of ports in case the default port of 8081 is in use From 7211bb30d183abb34fc0211aa1ddfe536d8f71be Mon Sep 17 00:00:00 2001 From: Matjaz Gregoric Date: Wed, 18 Nov 2015 09:17:32 +0100 Subject: [PATCH 3/6] Add circle.yml file. --- circle.yml | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 circle.yml diff --git a/circle.yml b/circle.yml new file mode 100644 index 00000000..a2c1aa64 --- /dev/null +++ b/circle.yml @@ -0,0 +1,24 @@ +machine: + python: + version: 2.7.10 +dependencies: + override: + - "pip install -U pip wheel setuptools" + - "pip install -e git://github.com/edx/xblock-sdk.git@22c1b2f173919bef22f2d9d9295ec5396d02dffd#egg=xblock-sdk" + - "pip install -r requirements.txt" + - "pip install -r $VIRTUAL_ENV/src/xblock-sdk/requirements/base.txt" + - "pip install -r $VIRTUAL_ENV/src/xblock-sdk/requirements/test.txt" + - "pip uninstall -y xblock-problem-builder && python setup.py sdist && pip install dist/xblock-problem-builder-2.0.tar.gz" + - "pip install -r test_requirements.txt" + - "mkdir var" +test: + override: + - "if [ $CIRCLE_NODE_INDEX = '0' ]; then pep8 problem_builder --max-line-length=120; fi": + parallel: true + - "if [ $CIRCLE_NODE_INDEX = '1' ]; then pylint problem_builder --disable=all --enable=function-redefined,undefined-variable,unused-variable; fi": + parallel: true + - "python run_tests.py": + parallel: true + files: + - "problem_builder/v1/tests/**/*.py" + - "problem_builder/tests/**/*.py" From 6789d9e5db1c475bf4965de6cdaee6e20fd59249 Mon Sep 17 00:00:00 2001 From: Matjaz Gregoric Date: Thu, 19 Nov 2015 17:22:46 +0100 Subject: [PATCH 4/6] Sleep a while before reloading xblock. See code comment for more info. Unfortunately, I wasn't able to find anything better than time.sleep(). --- problem_builder/tests/integration/test_author_changes.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/problem_builder/tests/integration/test_author_changes.py b/problem_builder/tests/integration/test_author_changes.py index be4e116d..c3de97d1 100644 --- a/problem_builder/tests/integration/test_author_changes.py +++ b/problem_builder/tests/integration/test_author_changes.py @@ -2,6 +2,7 @@ If an author makes changes to the block after students have started using it, will bad things happen? """ +import time from .base_test import ProblemBuilderBaseTest, MentoringAssessmentBaseTest import re @@ -20,6 +21,12 @@ def refresh_page(self): [Re]load the page with our scenario """ self.pb_block_dom = self.go_to_view("student_view") + # At this point the ajax request that initializes the Mentoring block + # might be still in progres. Race conditions resulting in duplicate field data + # can occur if we try to reload the block at the same time. + # Sleep 200ms to wait for the ajax request to finish, unfortunately I wasn't + # able to find a better way. + time.sleep(0.2) self.reload_pb_block() def reload_pb_block(self): From 5833d5398ca539547a17cc4de1aeadf2df053349 Mon Sep 17 00:00:00 2001 From: Matjaz Gregoric Date: Fri, 20 Nov 2015 08:29:18 +0100 Subject: [PATCH 5/6] More robust pipe error check. Copied from https://github.com/django/django/pull/3121/files#diff-f6d1c75ec606389da5af6558bf57f171 --- run_tests.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/run_tests.py b/run_tests.py index 7be53749..8078e492 100755 --- a/run_tests.py +++ b/run_tests.py @@ -23,6 +23,7 @@ def patch_broken_pipe_error(): removed once the workbench upgrades to Django >= 1.8. http://stackoverflow.com/a/22618740/51397""" + import socket from SocketServer import BaseServer from wsgiref import handlers @@ -30,8 +31,10 @@ def patch_broken_pipe_error(): log_exception = handlers.BaseHandler.log_exception def is_broken_pipe_error(): - type, err, tb = sys.exc_info() - return repr(err) == "error(32, 'Broken pipe')" + exc_type, exc_value = sys.exc_info()[:2] + if issubclass(exc_type, socket.error) and exc_value.args[0] == 32: + return True + return False def my_handle_error(self, request, client_address): if not is_broken_pipe_error(): From dea774812c4859022aaf8b0c171f6bc081753ca0 Mon Sep 17 00:00:00 2001 From: Matjaz Gregoric Date: Fri, 20 Nov 2015 08:33:54 +0100 Subject: [PATCH 6/6] Replace TravisCI build status icon with CircleCI. And remove .travis.yml file. --- .travis.yml | 22 ---------------------- README.md | 2 +- 2 files changed, 1 insertion(+), 23 deletions(-) delete mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index dbb65671..00000000 --- a/.travis.yml +++ /dev/null @@ -1,22 +0,0 @@ -language: python -python: - - "2.7" -before_install: - - "export DISPLAY=:99" - - "sh -e /etc/init.d/xvfb start" -install: - - "pip install -e git://github.com/edx/xblock-sdk.git@22c1b2f173919bef22f2d9d9295ec5396d02dffd#egg=xblock-sdk" - - "pip install -r requirements.txt" - - "pip install -r $VIRTUAL_ENV/src/xblock-sdk/requirements/base.txt" - - "pip install -r $VIRTUAL_ENV/src/xblock-sdk/requirements/test.txt" - - "pip uninstall -y xblock-problem-builder && python setup.py sdist && pip install dist/xblock-problem-builder-2.0.tar.gz" - - "pip install -r test_requirements.txt" - - "mkdir var" -script: - - pep8 problem_builder --max-line-length=120 - - pylint problem_builder --disable=all --enable=function-redefined,undefined-variable,unused-variable - - python run_tests.py --with-coverage --cover-package=problem_builder -notifications: - email: false -addons: - firefox: "36.0" diff --git a/README.md b/README.md index be9b351d..521196c7 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ Problem Builder and Step Builder -------------------------------- -[![Build Status](https://travis-ci.org/open-craft/problem-builder.svg?branch=master)](https://travis-ci.org/open-craft/problem-builder) +[![Circle CI](https://circleci.com/gh/open-craft/problem-builder.svg?style=svg)](https://circleci.com/gh/open-craft/problem-builder) This repository provides two XBlocks: Problem Builder and Step Builder.