Files
edx-platform/rakelib/bok_choy.rake

258 lines
7.9 KiB
Ruby

# 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", "acceptance")
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
# Reports
BOK_CHOY_REPORT_DIR = report_dir_path("bok_choy")
BOK_CHOY_XUNIT_REPORT = File.join(BOK_CHOY_REPORT_DIR, "xunit.xml")
BOK_CHOY_COVERAGE_RC = File.join(BOK_CHOY_DIR, ".coveragerc")
directory BOK_CHOY_REPORT_DIR
# Directory that videos are served from
VIDEO_SOURCE_DIR = File.join(REPO_ROOT, "test_root", "data", "video")
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_STUBS = {
:xqueue => {
:port => 8040,
:log => File.join(BOK_CHOY_LOG_DIR, "bok_choy_xqueue.log"),
:config => 'register_submission_url=http://0.0.0.0:8041/test/register_submission'
},
:ora => {
:port => 8041,
:log => File.join(BOK_CHOY_LOG_DIR, "bok_choy_ora.log"),
:config => ''
},
:comments => {
:port => 4567,
:log => File.join(BOK_CHOY_LOG_DIR, "bok_choy_comments.log")
},
:video => {
:port => 8777,
:log => File.join(BOK_CHOY_LOG_DIR, "bok_choy_video_sources.log"),
:config => "root_dir=#{VIDEO_SOURCE_DIR}"
},
:youtube => {
:port => 9080,
:log => File.join(BOK_CHOY_LOG_DIR, "bok_choy_youtube.log")
}
}
# For the time being, stubs are used by both the bok-choy and lettuce acceptance tests
# For this reason, the stubs package is currently located in the Django app called "terrain"
# where other lettuce configuration is stored.
BOK_CHOY_STUB_DIR = File.join(REPO_ROOT, "common", "djangoapps", "terrain")
BOK_CHOY_CACHE = Dalli::Client.new('localhost:11211')
# Start the servers we will run tests on
def start_servers()
BOK_CHOY_SERVERS.each do | service, info |
address = "0.0.0.0:#{info[:port]}"
cmd = "coverage run --rcfile=#{BOK_CHOY_COVERAGE_RC} -m manage #{service} --settings bok_choy runserver #{address} --traceback --noreload"
singleton_process(cmd, logfile=info[:log])
end
BOK_CHOY_STUBS.each do | service, info |
Dir.chdir(BOK_CHOY_STUB_DIR) do
singleton_process(
"python -m stubs.start #{service} #{info[:port]} #{info[:config]}",
logfile=info[:log]
)
end
end
end
# Wait until we get a successful response from the servers or time out
def wait_for_test_servers()
BOK_CHOY_SERVERS.merge(BOK_CHOY_STUBS).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
# 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)
# Default to running all tests if no specific test is specified
if test_spec.nil?
test_spec = BOK_CHOY_TEST_DIR
else
test_spec = File.join(BOK_CHOY_TEST_DIR, test_spec)
end
# Construct the nosetests command, specifying where to save screenshots and XUnit XML reports
cmd = [
"SCREENSHOT_DIR='#{BOK_CHOY_LOG_DIR}'", "nosetests", test_spec,
"--with-xunit", "--with-flaky", "--xunit-file=#{BOK_CHOY_XUNIT_REPORT}", "--verbosity=2"
]
# Configure parallel test execution, if specified
if BOK_CHOY_NUM_PARALLEL > 1
cmd += ["--processes=#{BOK_CHOY_NUM_PARALLEL}", "--process-timeout=#{BOK_CHOY_TEST_TIMEOUT}"]
end
# Run the nosetests command
sh(cmd.join(" "))
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 mongo is running
task :check_mongo do
if not is_mongo_running()
fail("Mongo is not running locally.")
end
end
# Check that memcache is running
task :check_memcache do
if not is_memcache_running()
fail("Memcache is not running locally.")
end
end
# Check that mysql is running
task :check_mysql do
if not is_mysql_running()
fail("MySQL is not running locally.")
end
end
# Check that all required services are running
task :check_services => [:check_mongo, :check_memcache, :check_mysql]
desc "Process assets and set up database for bok-choy tests"
task :setup => [:check_mysql, :install_prereqs, BOK_CHOY_LOG_DIR] do
# Reset the database
sh("#{REPO_ROOT}/scripts/reset-test-db.sh")
# Collect static assets
sh("paver update_assets --settings=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, BOK_CHOY_REPORT_DIR, :clean_reports_dir
] do |t, args|
# Clear any test data already in Mongo or MySQL and invalidate the cache
clear_mongo()
BOK_CHOY_CACHE.flush()
sh(django_admin('lms', 'bok_choy', 'loaddata', 'common/test/db_fixtures/*.json'))
# Ensure the test servers are available
puts "Starting test servers...".green
start_servers()
puts "Waiting for servers to start...".green
wait_for_test_servers()
begin
puts "Running test suite...".green
run_bok_choy(args.test_spec)
rescue
puts "Tests failed!".red
exit 1
ensure
puts "Cleaning up databases...".green
cleanup()
end
end
desc "Generate coverage reports for bok-choy tests"
task :coverage => BOK_CHOY_REPORT_DIR do | t, args |
puts "Combining coverage reports".red
sh("coverage combine --rcfile=#{BOK_CHOY_COVERAGE_RC}")
puts "Generating coverage reports".red
sh("coverage html --rcfile=#{BOK_CHOY_COVERAGE_RC}")
sh("coverage xml --rcfile=#{BOK_CHOY_COVERAGE_RC}")
sh("coverage report --rcfile=#{BOK_CHOY_COVERAGE_RC}")
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