Commit dde54552 by Will Daly

Added configuration for running tests using the bok-choy framework

parent 0d3c18df
...@@ -6,6 +6,7 @@ gem 'neat', '~> 1.4.0' ...@@ -6,6 +6,7 @@ gem 'neat', '~> 1.4.0'
gem 'colorize', '~> 0.5.8' gem 'colorize', '~> 0.5.8'
gem 'launchy', '~> 2.1.2' gem 'launchy', '~> 2.1.2'
gem 'sys-proctable', '~> 0.9.3' gem 'sys-proctable', '~> 0.9.3'
gem 'dalli', '~> 2.6.4'
# These gems aren't actually required; they are used by Linux and Mac to # These gems aren't actually required; they are used by Linux and Mac to
# detect when files change. If these gems are not installed, the system # detect when files change. If these gems are not installed, the system
# will fall back to polling files. # will fall back to polling files.
......
...@@ -6,6 +6,7 @@ GEM ...@@ -6,6 +6,7 @@ GEM
sass (>= 3.2.0) sass (>= 3.2.0)
thor thor
colorize (0.5.8) colorize (0.5.8)
dalli (2.6.4)
ffi (1.9.0) ffi (1.9.0)
launchy (2.1.2) launchy (2.1.2)
addressable (~> 2.3) addressable (~> 2.3)
...@@ -26,6 +27,7 @@ PLATFORMS ...@@ -26,6 +27,7 @@ PLATFORMS
DEPENDENCIES DEPENDENCIES
bourbon (~> 3.1.8) bourbon (~> 3.1.8)
colorize (~> 0.5.8) colorize (~> 0.5.8)
dalli (~> 2.6.4)
launchy (~> 2.1.2) launchy (~> 2.1.2)
neat (~> 1.4.0) neat (~> 1.4.0)
rake (~> 10.0.3) rake (~> 10.0.3)
......
{
"ANALYTICS_API_KEY": "",
"AWS_ACCESS_KEY_ID": "",
"AWS_SECRET_ACCESS_KEY": "",
"CELERY_BROKER_PASSWORD": "celery",
"CELERY_BROKER_USER": "celery",
"CONTENTSTORE": {
"DOC_STORE_CONFIG": {
"collection": "modulestore",
"db": "test",
"host": [
"localhost"
],
"password": "password",
"port": 27017,
"user": "edxapp"
},
"ENGINE": "xmodule.contentstore.mongo.MongoContentStore",
"OPTIONS": {
"db": "test",
"host": [
"localhost"
],
"password": "password",
"port": 27017,
"user": "edxapp"
}
},
"DATABASES": {
"default": {
"ENGINE": "django.db.backends.mysql",
"HOST": "localhost",
"NAME": "test",
"PASSWORD": "",
"PORT": "3306",
"USER": "root"
}
},
"DOC_STORE_CONFIG": {
"collection": "modulestore",
"db": "test",
"host": [
"localhost"
],
"password": "password",
"port": 27017,
"user": "edxapp"
},
"MODULESTORE": {
"default": {
"DOC_STORE_CONFIG": {
"collection": "modulestore",
"db": "test",
"host": [
"localhost"
],
"password": "password",
"port": 27017,
"user": "edxapp"
},
"ENGINE": "xmodule.modulestore.mongo.DraftMongoModuleStore",
"OPTIONS": {
"collection": "modulestore",
"db": "test",
"default_class": "xmodule.hidden_module.HiddenDescriptor",
"fs_root": "** OVERRIDDEN **",
"host": [
"localhost"
],
"password": "password",
"port": 27017,
"render_template": "edxmako.shortcuts.render_to_string",
"user": "edxapp"
}
},
"direct": {
"DOC_STORE_CONFIG": {
"collection": "modulestore",
"db": "test",
"host": [
"localhost"
],
"password": "password",
"port": 27017,
"user": "edxapp"
},
"ENGINE": "xmodule.modulestore.mongo.MongoModuleStore",
"OPTIONS": {
"collection": "modulestore",
"db": "test",
"default_class": "xmodule.hidden_module.HiddenDescriptor",
"fs_root": "** OVERRIDDEN **",
"host": [
"localhost"
],
"password": "password",
"port": 27017,
"render_template": "edxmako.shortcuts.render_to_string",
"user": "edxapp"
}
}
},
"OPEN_ENDED_GRADING_INTERFACE": {
"grading_controller": "grading_controller",
"password": "password",
"peer_grading": "peer_grading",
"staff_grading": "staff_grading",
"url": "http://localhost:18060/",
"username": "lms"
},
"SECRET_KEY": "",
"XQUEUE_INTERFACE": {
"basic_auth": [
"edx",
"edx"
],
"django_auth": {
"password": "password",
"username": "lms"
},
"url": "http://localhost:18040"
},
"ZENDESK_API_KEY": "",
"ZENDESK_USER": ""
}
{
"ANALYTICS_SERVER_URL": "",
"BOOK_URL": "",
"BUGS_EMAIL": "bugs@example.com",
"BULK_EMAIL_DEFAULT_FROM_EMAIL": "no-reply@example.com",
"CACHES": {
"celery": {
"BACKEND": "django.core.cache.backends.memcached.MemcachedCache",
"KEY_FUNCTION": "util.memcache.safe_key",
"KEY_PREFIX": "integration_celery",
"LOCATION": [
"localhost:11211"
]
},
"default": {
"BACKEND": "django.core.cache.backends.memcached.MemcachedCache",
"KEY_FUNCTION": "util.memcache.safe_key",
"KEY_PREFIX": "sandbox_default",
"LOCATION": [
"localhost:11211"
]
},
"general": {
"BACKEND": "django.core.cache.backends.memcached.MemcachedCache",
"KEY_FUNCTION": "util.memcache.safe_key",
"KEY_PREFIX": "sandbox_general",
"LOCATION": [
"localhost:11211"
]
},
"mongo_metadata_inheritance": {
"BACKEND": "django.core.cache.backends.memcached.MemcachedCache",
"KEY_FUNCTION": "util.memcache.safe_key",
"KEY_PREFIX": "integration_mongo_metadata_inheritance",
"LOCATION": [
"localhost:11211"
]
},
"staticfiles": {
"BACKEND": "django.core.cache.backends.memcached.MemcachedCache",
"KEY_FUNCTION": "util.memcache.safe_key",
"KEY_PREFIX": "integration_static_files",
"LOCATION": [
"localhost:11211"
]
}
},
"CELERY_BROKER_HOSTNAME": "localhost",
"CELERY_BROKER_TRANSPORT": "amqp",
"CERT_QUEUE": "certificates",
"CMS_BASE": "",
"CODE_JAIL": {
"limits": {
"REALTIME": 3,
"VMEM": 0
}
},
"COMMENTS_SERVICE_KEY": "password",
"COMMENTS_SERVICE_URL": "http://localhost:4567",
"CONTACT_EMAIL": "info@example.com",
"DEFAULT_FEEDBACK_EMAIL": "feedback@example.com",
"DEFAULT_FROM_EMAIL": "registration@example.com",
"EMAIL_BACKEND": "django.core.mail.backends.smtp.EmailBackend",
"FEATURES": {
"AUTH_USE_OPENID_PROVIDER": true,
"CERTIFICATES_ENABLED": true,
"ENABLE_DISCUSSION_SERVICE": true,
"ENABLE_INSTRUCTOR_ANALYTICS": true,
"ENABLE_S3_GRADE_DOWNLOADS": true,
"PREVIEW_LMS_BASE": "",
"SUBDOMAIN_BRANDING": false,
"SUBDOMAIN_COURSE_LISTINGS": false
},
"FEEDBACK_SUBMISSION_EMAIL": "",
"GITHUB_REPO_ROOT": "** OVERRIDDEN **",
"GRADES_DOWNLOAD": {
"BUCKET": "edx-grades",
"ROOT_PATH": "/tmp/edx-s3/grades",
"STORAGE_TYPE": "localfs"
},
"LMS_BASE": "",
"LOCAL_LOGLEVEL": "INFO",
"LOGGING_ENV": "sandbox",
"LOG_DIR": "** OVERRIDDEN **",
"MEDIA_URL": "",
"MKTG_URL_LINK_MAP": {},
"PLATFORM_NAME": "edX",
"SEGMENT_IO_LMS": true,
"SERVER_EMAIL": "devops@example.com",
"SESSION_COOKIE_DOMAIN": null,
"SITE_NAME": "localhost",
"STATIC_ROOT_BASE": "** OVERRIDDEN **",
"STATIC_URL_BASE": "/static/",
"SYSLOG_SERVER": "",
"TECH_SUPPORT_EMAIL": "technical@example.com",
"THEME_NAME": "",
"TIME_ZONE": "America/New_York",
"WIKI_ENABLED": true
}
# Settings for bok choy tests
import os
from path import path
########################## Prod-like settings ###################################
# These should be as close as possible to the settings we use in production.
# As in prod, we read in environment and auth variables from JSON files.
# Unlike in prod, we use the JSON files stored in this repo.
# This is a convenience for ensuring (a) that we can consistently find the files
# and (b) that the files are the same in Jenkins as in local dev.
os.environ['SERVICE_VARIANT'] = 'bok_choy'
os.environ['CONFIG_ROOT'] = path(__file__).abspath().dirname()
from aws import * # pylint: disable=W0401, W0614
######################### Testing overrides ####################################
# Needed for the `reset_db` management command
INSTALLED_APPS += ('django_extensions',)
# Redirect to the test_root folder within the repo
TEST_ROOT = CONFIG_ROOT.dirname().dirname() / "test_root"
GITHUB_REPO_ROOT = (TEST_ROOT / "data").abspath()
LOG_DIR = (TEST_ROOT / "log").abspath()
# Configure Mongo modulestore to use the test folder within the repo
for store in ["default", "direct"]:
MODULESTORE[store]['OPTIONS']['fs_root'] = (TEST_ROOT / "data").abspath()
# Enable django-pipeline and staticfiles
STATIC_ROOT = (TEST_ROOT / "staticfiles").abspath()
PIPELINE = True
# Silence noisy logs
import logging
LOG_OVERRIDES = [
('track.middleware', logging.CRITICAL)
]
for log_name, log_level in LOG_OVERRIDES:
logging.getLogger(log_name).setLevel(log_level)
# Unfortunately, we need to use debug mode to serve staticfiles
DEBUG = True
import os import os
# Get the URL of the instance under test # Get the URL of the instance under test
BASE_URL = os.environ.get('test_url', '') BASE_URL = os.environ.get('test_url', 'http://localhost:8003')
import os import os
# Get the URL of the instance under test # Get the URL of the instance under test
BASE_URL = os.environ.get('test_url', '') BASE_URL = os.environ.get('test_url', 'http://localhost:8031')
"""
Very simple test case to verify bok-choy integration.
"""
from bok_choy.web_app_test import WebAppTest
from edxapp_pages.lms.info import InfoPage
class InfoPageTest(WebAppTest):
"""
Test that the top-level pages in the LMS load.
"""
@property
def page_object_classes(self):
return [InfoPage]
def test_info(self):
for section_name in InfoPage.sections():
self.ui.visit('lms.info', section=section_name)
...@@ -20,6 +20,7 @@ set -e ...@@ -20,6 +20,7 @@ set -e
# because we couldn't think of a better place to put it) # because we couldn't think of a better place to put it)
# - "lms-acceptance": Run the acceptance (Selenium) tests for the LMS # - "lms-acceptance": Run the acceptance (Selenium) tests for the LMS
# - "cms-acceptance": Run the acceptance (Selenium) tests for Studio # - "cms-acceptance": Run the acceptance (Selenium) tests for Studio
# - "bok-choy": Run acceptance tests that use the bok-choy framework
# #
# `SHARD` is a number (1, 2, or 3) indicating which subset of the tests # `SHARD` is a number (1, 2, or 3) indicating which subset of the tests
# to build. Currently, "lms-acceptance" has two shards (1 and 2), # to build. Currently, "lms-acceptance" has two shards (1 and 2),
...@@ -95,4 +96,8 @@ END ...@@ -95,4 +96,8 @@ END
rake test:acceptance:cms["-v 3 --tag shard_${SHARD}"] rake test:acceptance:cms["-v 3 --tag shard_${SHARD}"]
;; ;;
"bok-choy")
rake test:bok_choy
;;
esac esac
{
"ANALYTICS_API_KEY": "",
"AWS_ACCESS_KEY_ID": "",
"AWS_SECRET_ACCESS_KEY": "",
"CELERY_BROKER_PASSWORD": "celery",
"CELERY_BROKER_USER": "celery",
"CONTENTSTORE": {
"DOC_STORE_CONFIG": {
"collection": "modulestore",
"db": "edxapp",
"host": [
"localhost"
],
"password": "password",
"port": 27017,
"user": "edxapp"
},
"ENGINE": "xmodule.contentstore.mongo.MongoContentStore",
"OPTIONS": {
"db": "edxapp",
"host": [
"localhost"
],
"password": "password",
"port": 27017,
"user": "edxapp"
}
},
"DATABASES": {
"default": {
"ENGINE": "django.db.backends.mysql",
"HOST": "localhost",
"NAME": "test",
"PASSWORD": "",
"PORT": "3306",
"USER": "root"
}
},
"DOC_STORE_CONFIG": {
"collection": "modulestore",
"db": "test",
"host": [
"localhost"
],
"password": "password",
"port": 27017,
"user": "edxapp"
},
"MODULESTORE": {
"default": {
"ENGINE": "xmodule.modulestore.mixed.MixedModuleStore",
"OPTIONS": {
"mappings": {},
"stores": {
"default": {
"DOC_STORE_CONFIG": {
"collection": "modulestore",
"db": "test",
"host": [
"localhost"
],
"password": "password",
"port": 27017,
"user": "edxapp"
},
"ENGINE": "xmodule.modulestore.mongo.MongoModuleStore",
"OPTIONS": {
"collection": "modulestore",
"db": "test",
"default_class": "xmodule.hidden_module.HiddenDescriptor",
"fs_root": "** OVERRIDDEN **",
"host": [
"localhost"
],
"password": "password",
"port": 27017,
"render_template": "edxmako.shortcuts.render_to_string",
"user": "edxapp"
}
},
"xml": {
"ENGINE": "xmodule.modulestore.xml.XMLModuleStore",
"OPTIONS": {
"data_dir": "** OVERRIDDEN **",
"default_class": "xmodule.hidden_module.HiddenDescriptor"
}
}
}
}
}
},
"OPEN_ENDED_GRADING_INTERFACE": {
"grading_controller": "grading_controller",
"password": "password",
"peer_grading": "peer_grading",
"staff_grading": "staff_grading",
"url": "http://localhost:18060/",
"username": "lms"
},
"SECRET_KEY": "",
"XQUEUE_INTERFACE": {
"basic_auth": [
"edx",
"edx"
],
"django_auth": {
"password": "password",
"username": "lms"
},
"url": "http://localhost:18040"
},
"ZENDESK_API_KEY": "",
"ZENDESK_USER": ""
}
{
"ANALYTICS_SERVER_URL": "",
"BOOK_URL": "",
"BUGS_EMAIL": "bugs@example.com",
"BULK_EMAIL_DEFAULT_FROM_EMAIL": "no-reply@example.com",
"CACHES": {
"celery": {
"BACKEND": "django.core.cache.backends.memcached.MemcachedCache",
"KEY_FUNCTION": "util.memcache.safe_key",
"KEY_PREFIX": "integration_celery",
"LOCATION": [
"localhost:11211"
]
},
"default": {
"BACKEND": "django.core.cache.backends.memcached.MemcachedCache",
"KEY_FUNCTION": "util.memcache.safe_key",
"KEY_PREFIX": "sandbox_default",
"LOCATION": [
"localhost:11211"
]
},
"general": {
"BACKEND": "django.core.cache.backends.memcached.MemcachedCache",
"KEY_FUNCTION": "util.memcache.safe_key",
"KEY_PREFIX": "sandbox_general",
"LOCATION": [
"localhost:11211"
]
},
"mongo_metadata_inheritance": {
"BACKEND": "django.core.cache.backends.memcached.MemcachedCache",
"KEY_FUNCTION": "util.memcache.safe_key",
"KEY_PREFIX": "integration_mongo_metadata_inheritance",
"LOCATION": [
"localhost:11211"
]
},
"staticfiles": {
"BACKEND": "django.core.cache.backends.memcached.MemcachedCache",
"KEY_FUNCTION": "util.memcache.safe_key",
"KEY_PREFIX": "integration_static_files",
"LOCATION": [
"localhost:11211"
]
}
},
"CELERY_BROKER_HOSTNAME": "localhost",
"CELERY_BROKER_TRANSPORT": "amqp",
"CERT_QUEUE": "certificates",
"CMS_BASE": "",
"CODE_JAIL": {
"limits": {
"REALTIME": 3,
"VMEM": 0
}
},
"COMMENTS_SERVICE_KEY": "password",
"COMMENTS_SERVICE_URL": "http://localhost:4567",
"CONTACT_EMAIL": "info@example.com",
"DEFAULT_FEEDBACK_EMAIL": "feedback@example.com",
"DEFAULT_FROM_EMAIL": "registration@example.com",
"EMAIL_BACKEND": "django.core.mail.backends.smtp.EmailBackend",
"FEATURES": {
"AUTH_USE_OPENID_PROVIDER": true,
"CERTIFICATES_ENABLED": true,
"ENABLE_DISCUSSION_SERVICE": true,
"ENABLE_INSTRUCTOR_ANALYTICS": true,
"ENABLE_S3_GRADE_DOWNLOADS": true,
"PREVIEW_LMS_BASE": "",
"SUBDOMAIN_BRANDING": false,
"SUBDOMAIN_COURSE_LISTINGS": false
},
"FEEDBACK_SUBMISSION_EMAIL": "",
"GITHUB_REPO_ROOT": "** OVERRIDDEN **",
"GRADES_DOWNLOAD": {
"BUCKET": "edx-grades",
"ROOT_PATH": "/tmp/edx-s3/grades",
"STORAGE_TYPE": "localfs"
},
"LMS_BASE": "",
"LOCAL_LOGLEVEL": "INFO",
"LOGGING_ENV": "sandbox",
"LOG_DIR": "** OVERRIDDEN **",
"MEDIA_URL": "",
"MKTG_URL_LINK_MAP": {},
"PLATFORM_NAME": "edX",
"SEGMENT_IO_LMS": true,
"SERVER_EMAIL": "devops@example.com",
"SESSION_COOKIE_DOMAIN": null,
"SITE_NAME": "localhost",
"STATIC_ROOT_BASE": "** OVERRIDDEN **",
"STATIC_URL_BASE": "/static/",
"SYSLOG_SERVER": "",
"TECH_SUPPORT_EMAIL": "technical@example.com",
"THEME_NAME": "",
"TIME_ZONE": "America/New_York",
"WIKI_ENABLED": true
}
# Settings for bok choy tests
import os
from path import path
CONFIG_ROOT = path(__file__).abspath().dirname()
TEST_ROOT = CONFIG_ROOT.dirname().dirname() / "test_root"
########################## Prod-like settings ###################################
# These should be as close as possible to the settings we use in production.
# As in prod, we read in environment and auth variables from JSON files.
# Unlike in prod, we use the JSON files stored in this repo.
# This is a convenience for ensuring (a) that we can consistently find the files
# and (b) that the files are the same in Jenkins as in local dev.
os.environ['SERVICE_VARIANT'] = 'bok_choy'
os.environ['CONFIG_ROOT'] = CONFIG_ROOT
from aws import * # pylint: disable=W0401, W0614
######################### Testing overrides ####################################
# Needed for the `reset_db` management command
INSTALLED_APPS += ('django_extensions',)
# Redirect to the test_root folder within the repo
GITHUB_REPO_ROOT = (TEST_ROOT / "data").abspath()
LOG_DIR = (TEST_ROOT / "log").abspath()
# Configure Mongo modulestore to use the test folder within the repo
MONGO_MODULESTORE = MODULESTORE['default']['OPTIONS']['stores']['default']
MONGO_MODULESTORE['OPTIONS']['fs_root'] = (TEST_ROOT / "data").abspath()
# Configure XML modulestore to use test root data dir
XML_MODULESTORE = MODULESTORE['default']['OPTIONS']['stores']['xml']
XML_MODULESTORE['OPTIONS']['data_dir'] = (TEST_ROOT / "data").abspath()
# Enable django-pipeline and staticfiles
STATIC_ROOT = (TEST_ROOT / "staticfiles").abspath()
PIPELINE = True
# Silence noisy logs
import logging
LOG_OVERRIDES = [
('track.middleware', logging.CRITICAL),
('edxmako.shortcuts', logging.ERROR),
('dd.dogapi', logging.ERROR)
]
for log_name, log_level in LOG_OVERRIDES:
logging.getLogger(log_name).setLevel(log_level)
# Unfortunately, we need to use debug mode to serve staticfiles
DEBUG = True
# Run acceptance tests that use the bok-choy framework
# http://bok-choy.readthedocs.org/en/latest/
require 'dalli'
# Mongo databases that will be dropped before/after the tests run
BOK_CHOY_MONGO_DATABASE = "test"
# Control parallel test execution with environment variables
# Process timeout is the maximum amount of time to wait for results from a particular test case
BOK_CHOY_NUM_PARALLEL = ENV.fetch('NUM_PARALLEL', 1).to_i
BOK_CHOY_TEST_TIMEOUT = ENV.fetch("TEST_TIMEOUT", 300).to_f
# Ensure that we have a directory to put logs and reports
BOK_CHOY_DIR = File.join(REPO_ROOT, "common", "test", "bok_choy")
BOK_CHOY_TEST_DIR = File.join(BOK_CHOY_DIR, "tests")
BOK_CHOY_LOG_DIR = File.join(REPO_ROOT, "test_root", "log")
directory BOK_CHOY_LOG_DIR
BOK_CHOY_SERVERS = {
:lms => { :port => 8003, :log => File.join(BOK_CHOY_LOG_DIR, "bok_choy_lms.log") },
:cms => { :port => 8031, :log => File.join(BOK_CHOY_LOG_DIR, "bok_choy_studio.log") }
}
BOK_CHOY_CACHE = Dalli::Client.new('localhost:11211')
# Start the server we will run tests on
def start_servers()
BOK_CHOY_SERVERS.each do | service, info |
address = "0.0.0.0:#{info[:port]}"
singleton_process(
django_admin(service, 'bok_choy', 'runserver', address),
logfile=info[:log]
)
end
end
# Wait until we get a successful response from the servers or time out
def wait_for_test_servers()
BOK_CHOY_SERVERS.each do | service, info |
ready = wait_for_server("0.0.0.0", info[:port])
if not ready
fail("Could not contact #{service} test server")
end
end
end
def is_mongo_running()
# The mongo command will connect to the service,
# failing with a non-zero exit code if it cannot connect.
output = `mongo --eval "print('running')"`
return (output and output.include? "running")
end
def is_memcache_running()
# We use a Ruby memcache client to attempt to set a key
# in memcache. If we cannot do so because the service is not
# available, then this will raise an exception.
BOK_CHOY_CACHE.set('test', 'test')
return true
rescue Dalli::DalliError
return false
end
def is_mysql_running()
# We use the MySQL CLI client and capture its stderr
# If the client cannot connect successfully, stderr will be non-empty
output = `mysql -e "" 2>&1`
return output == ""
end
def nose_cmd(test_spec)
cmd = ["PYTHONPATH=#{BOK_CHOY_DIR}:$PYTHONPATH", "SCREENSHOT_DIR=#{BOK_CHOY_LOG_DIR}", "nosetests", test_spec]
if BOK_CHOY_NUM_PARALLEL > 1
cmd += ["--processes=#{BOK_CHOY_NUM_PARALLEL}", "--process-timeout=#{BOK_CHOY_TEST_TIMEOUT}"]
end
return cmd.join(" ")
end
# Run the bok choy tests
# `test_spec` is a nose-style test specifier relative to the test directory
# Examples:
# - path/to/test.py
# - path/to/test.py:TestFoo
# - path/to/test.py:TestFoo.test_bar
# It can also be left blank to run all tests in the suite.
def run_bok_choy(test_spec)
if test_spec.nil?
sh(nose_cmd(BOK_CHOY_TEST_DIR))
else
sh(nose_cmd(File.join(BOK_CHOY_TEST_DIR, test_spec)))
end
end
def clear_mongo()
sh("mongo #{BOK_CHOY_MONGO_DATABASE} --eval 'db.dropDatabase()' > /dev/null")
end
# Clean up data we created in the databases
def cleanup()
sh(django_admin('lms', 'bok_choy', 'flush', '--noinput'))
clear_mongo()
end
namespace :'test:bok_choy' do
# Check that required services are running
task :check_services do
if not is_mongo_running()
fail("Mongo is not running locally.")
end
if not is_memcache_running()
fail("Memcache is not running locally.")
end
if not is_mysql_running()
fail("MySQL is not running locally.")
end
end
desc "Process assets and set up database for bok-choy tests"
task :setup => [:check_services, :install_prereqs, BOK_CHOY_LOG_DIR] do
# Clear any test data already in Mongo
clear_mongo()
# Invalidate the cache
BOK_CHOY_CACHE.flush()
# HACK: Since the CMS depends on the existence of some database tables
# that are now in common but used to be in LMS (Role/Permissions for Forums)
# we need to create/migrate the database tables defined in the LMS.
# We might be able to address this by moving out the migrations from
# lms/django_comment_client, but then we'd have to repair all the existing
# migrations from the upgrade tables in the DB.
# But for now for either system (lms or cms), use the lms
# definitions to sync and migrate.
sh(django_admin('lms', 'bok_choy', 'reset_db', '--noinput'))
sh(django_admin('lms', 'bok_choy', 'syncdb', '--noinput'))
sh(django_admin('lms', 'bok_choy', 'migrate', '--noinput'))
# Collect static assets
Rake::Task["gather_assets"].invoke('lms', 'bok_choy')
Rake::Task["gather_assets"].reenable
Rake::Task["gather_assets"].invoke('cms', 'bok_choy')
end
desc "Run acceptance tests that use the bok-choy framework but skip setup"
task :fast, [:test_spec] => [:check_services, BOK_CHOY_LOG_DIR] do |t, args|
# Ensure the test servers are available
puts "Starting test servers...".red
start_servers()
puts "Waiting for servers to start...".red
wait_for_test_servers()
begin
puts "Running test suite...".red
run_bok_choy(args.test_spec)
rescue
puts "Tests failed!".red
exit 1
ensure
puts "Cleaning up databases...".red
cleanup()
end
end
end
# Default: set up and run the tests
desc "Run acceptance tests that use the bok-choy framework"
task :'test:bok_choy', [:test_spec] => [:'test:bok_choy:setup'] do |t, args|
Rake::Task["test:bok_choy:fast"].invoke(args.test_spec)
end
...@@ -2,6 +2,7 @@ require 'digest/md5' ...@@ -2,6 +2,7 @@ require 'digest/md5'
require 'sys/proctable' require 'sys/proctable'
require 'colorize' require 'colorize'
require 'timeout' require 'timeout'
require 'net/http'
def find_executable(exec) def find_executable(exec)
path = %x(which #{exec}).strip path = %x(which #{exec}).strip
...@@ -55,38 +56,28 @@ end ...@@ -55,38 +56,28 @@ end
def background_process(command, logfile=nil) def background_process(command, logfile=nil)
spawn_opts = {:pgroup => true} spawn_opts = {:pgroup => true}
if !logfile.nil? if !logfile.nil?
puts "Running '#{command.join(' ')}', redirecting output to #{logfile}".red puts "Running '#{command.join(' ')}', redirecting output to #{logfile}"
spawn_opts[[:err, :out]] = [logfile, 'a'] spawn_opts[[:err, :out]] = [logfile, 'a']
end end
pid = Process.spawn({}, *command, spawn_opts) pid = Process.spawn({}, *command, spawn_opts)
command = [*command] command = [*command]
at_exit do at_exit do
puts "Ending process and children"
pgid = Process.getpgid(pid) pgid = Process.getpgid(pid)
begin begin
Timeout.timeout(5) do Timeout.timeout(5) do
puts "Interrupting process group #{pgid}"
Process.kill(:SIGINT, -pgid) Process.kill(:SIGINT, -pgid)
puts "Waiting on process group #{pgid}"
Process.wait(-pgid) Process.wait(-pgid)
puts "Done waiting on process group #{pgid}"
end end
rescue Timeout::Error rescue Timeout::Error
begin begin
Timeout.timeout(5) do Timeout.timeout(5) do
puts "Terminating process group #{pgid}"
Process.kill(:SIGTERM, -pgid) Process.kill(:SIGTERM, -pgid)
puts "Waiting on process group #{pgid}"
Process.wait(-pgid) Process.wait(-pgid)
puts "Done waiting on process group #{pgid}"
end end
rescue Timeout::Error rescue Timeout::Error
puts "Killing process group #{pgid}"
Process.kill(:SIGKILL, -pgid) Process.kill(:SIGKILL, -pgid)
puts "Waiting on process group #{pgid}"
Process.wait(-pgid) Process.wait(-pgid)
puts "Done waiting on process group #{pgid}"
end end
end end
end end
...@@ -103,6 +94,25 @@ def singleton_process(command, logfile=nil) ...@@ -103,6 +94,25 @@ def singleton_process(command, logfile=nil)
end end
end end
# Wait for a server to respond with status 200 at "/"
def wait_for_server(server, port)
attempts = 0
begin
http = Net::HTTP.start(server, port, {open_timeout: 10, read_timeout: 10})
response = http.head("/")
response.code == "200"
true
rescue
sleep(1)
attempts += 1
if attempts < 20
retry
else
false
end
end
end
def environments(system) def environments(system)
Dir["#{system}/envs/**/*.py"].select{|file| ! (/__init__.py$/ =~ file)}.map do |env_file| Dir["#{system}/envs/**/*.py"].select{|file| ! (/__init__.py$/ =~ file)}.map do |env_file|
env_file.gsub("#{system}/envs/", '').gsub(/\.py/, '').gsub('/', '.') env_file.gsub("#{system}/envs/", '').gsub(/\.py/, '').gsub('/', '.')
...@@ -135,4 +145,3 @@ if !ENV['TESTS_FAIL_FAST'] ...@@ -135,4 +145,3 @@ if !ENV['TESTS_FAIL_FAST']
Rake.application.top_level_tasks << :fail_tests Rake.application.top_level_tasks << :fail_tests
end end
...@@ -16,6 +16,7 @@ dealer==0.2.3 ...@@ -16,6 +16,7 @@ dealer==0.2.3
distribute>=0.6.28, <0.7 distribute>=0.6.28, <0.7
django-celery==3.0.17 django-celery==3.0.17
django-countries==1.5 django-countries==1.5
django-extensions==1.2.5
django-filter==0.6.0 django-filter==0.6.0
django-followit==0.0.3 django-followit==0.0.3
django-keyedcache==1.4-6 django-keyedcache==1.4-6
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment