Commit 0bf7c71e by Calen Pennington

Run all tests on jenkins

We used to specify specific rake test tasks so that we could run all of
them even if early ones failed. However, that meant that as new tasks
were added, they weren't being run on jenkins.

Now, there is a facility in the rake scripts so that tests can run using
the test_sh function, which will delay failure until the end of the rake
run, unless the TESTS_FAIL_FAST environment variable is set.

Furthermore, this reorganizes the jasmine test tasks so that we can run
those as part of `rake test` as well.
parent fdf213f9
...@@ -141,21 +141,36 @@ Very handy: if you uncomment the `pdb=1` line in `setup.cfg`, it will drop you i ...@@ -141,21 +141,36 @@ Very handy: if you uncomment the `pdb=1` line in `setup.cfg`, it will drop you i
### Running Javascript Unit Tests ### Running Javascript Unit Tests
These commands start a development server with jasmine testing enabled, and launch your default browser To run all of the javascript unit tests, use
pointing to those tests
rake browse_jasmine_{lms,cms} rake jasmine
To run the tests headless, you must install [phantomjs](http://phantomjs.org/download.html), then run: If the `phantomjs` binary is on the path, or the `PHANTOMJS_PATH` environment variable is
set to point to it, then the tests will be run headless. Otherwise, they will be run in
your default browser
rake phantomjs_jasmine_{lms,cms} export PATH=/path/to/phantomjs:$PATH
rake jasmine # Runs headless
If the `phantomjs` binary is not on the path, set the `PHANTOMJS_PATH` environment variable to point to it or
PHANTOMJS_PATH=/path/to/phantomjs rake jasmine # Runs headless
or
rake jasmine # Runs in browser
You can also force a run using phantomjs or the browser using the commands
rake jasmine:browser # Runs in browser
rake jasmine:phantomjs # Runs headless
You can run tests for a specific subsystems as well
PHANTOMJS_PATH=/path/to/phantomjs rake phantomjs_jasmine_{lms,cms} rake jasmine:lms # Runs all lms javascript unit tests using the default method
rake jasmine:cms:browser # Runs all cms javascript unit tests in the browser
Once you have run the `rake` command, your browser should open to Use `rake -T` to get a list of all available subsystems
to `http://localhost/_jasmine/`, which displays the test results.
**Troubleshooting**: If you get an error message while running the `rake` task, **Troubleshooting**: If you get an error message while running the `rake` task,
try running `bundle install` to install the required ruby gems. try running `bundle install` to install the required ruby gems.
......
...@@ -70,23 +70,11 @@ rake clobber ...@@ -70,23 +70,11 @@ rake clobber
rake pep8 > pep8.log || cat pep8.log rake pep8 > pep8.log || cat pep8.log
rake pylint > pylint.log || cat pylint.log rake pylint > pylint.log || cat pylint.log
TESTS_FAILED=0 # Run the unit tests (use phantomjs for javascript unit tests)
rake test
# Run the python unit tests
rake test_cms || TESTS_FAILED=1
rake test_lms || TESTS_FAILED=1
rake test_common/lib/capa || TESTS_FAILED=1
rake test_common/lib/xmodule || TESTS_FAILED=1
# Run the javascript unit tests
rake phantomjs_jasmine_lms || TESTS_FAILED=1
rake phantomjs_jasmine_cms || TESTS_FAILED=1
rake phantomjs_jasmine_common/lib/xmodule || TESTS_FAILED=1
rake phantomjs_jasmine_common/static/coffee || TESTS_FAILED=1
rake coverage:xml coverage:html rake coverage:xml coverage:html
[ $TESTS_FAILED == '0' ]
rake autodeploy_properties rake autodeploy_properties
github_status state:success "passed" github_status state:success "passed"
require 'colorize'
def deprecated(deprecated, deprecated_by)
task deprecated do
puts("Task #{deprecated} has been deprecated. Use #{deprecated_by} instead. Waiting 5 seconds...".red)
sleep(5)
Rake::Task[deprecated_by].invoke
end
end
[:lms, :cms].each do |system|
deprecated("browse_jasmine_#{system}", "jasmine:#{system}:browser")
deprecated("phantomjs_jasmine_#{system}", "jasmine:#{system}:phantomjs")
end
Dir["common/lib/*"].select{|lib| File.directory?(lib)}.each do |lib|
deprecated("browse_jasmine_#{lib}", "jasmine:#{lib}:browser")
deprecated("phantomjs_jasmine_#{lib}", "jasmine:#{lib}:phantomjs")
end
deprecated("browse_jasmine_discussion", "jasmine:common/static/coffee:browser")
deprecated("phantomjs_jasmine_discussion", "jasmine:common/static/coffee:phantomjs")
\ No newline at end of file
require 'digest/md5' require 'digest/md5'
def find_executable(exec)
path = %x(which #{exec}).strip
$?.exitstatus == 0 ? path : nil
end
def select_executable(*cmds) def select_executable(*cmds)
cmds.find_all{ |cmd| system("which #{cmd} > /dev/null 2>&1") }[0] || fail("No executables found from #{cmds.join(', ')}") cmds.find_all{ |cmd| !find_executable(cmd).nil? }[0] || fail("No executables found from #{cmds.join(', ')}")
end end
def django_admin(system, env, command, *args) def django_admin(system, env, command, *args)
...@@ -85,3 +89,31 @@ def environments(system) ...@@ -85,3 +89,31 @@ def environments(system)
env_file.gsub("#{system}/envs/", '').gsub(/\.py/, '').gsub('/', '.') env_file.gsub("#{system}/envs/", '').gsub(/\.py/, '').gsub('/', '.')
end end
end end
$failed_tests = 0
# Run sh on args. If TESTS_FAIL_FAST is set, then stop on the first shell failure.
# Otherwise, a final task will be added that will fail if any tests have failed
def test_sh(*args)
sh(*args) do |ok, res|
if ok
return
end
if ENV['TESTS_FAIL_FAST']
fail("Test failed!")
else
$failed_tests += 1
end
end
end
# Add a task after all other tasks that fails if any tests have failed
if !ENV['TESTS_FAIL_FAST']
task :fail_tests do
fail("#{$failed_tests} tests failed!") if $failed_tests > 0
end
Rake.application.top_level_tasks << :fail_tests
end
...@@ -3,6 +3,11 @@ require 'erb' ...@@ -3,6 +3,11 @@ require 'erb'
require 'launchy' require 'launchy'
require 'net/http' require 'net/http'
PHANTOMJS_PATH = find_executable(ENV['PHANTOMJS_PATH'] || 'phantomjs')
PREFERRED_METHOD = PHANTOMJS_PATH.nil? ? 'browser' : 'phantomjs'
if PHANTOMJS_PATH.nil?
puts("phantomjs not found on path. Set $PHANTOMJS_PATH. Using browser for jasmine tests".blue)
end
def django_for_jasmine(system, django_reload) def django_for_jasmine(system, django_reload)
if !django_reload if !django_reload
...@@ -35,18 +40,6 @@ def django_for_jasmine(system, django_reload) ...@@ -35,18 +40,6 @@ def django_for_jasmine(system, django_reload)
end end
def template_jasmine_runner(lib) def template_jasmine_runner(lib)
case lib
when /common\/lib\/.+/
coffee_files = Dir["#{lib}/**/js/**/*.coffee", "common/static/coffee/src/**/*.coffee"]
when /common\/static\/coffee/
coffee_files = Dir["#{lib}/**/*.coffee"]
else
puts('I do not know how to run jasmine tests for #{lib}')
exit
end
if !coffee_files.empty?
sh("node_modules/.bin/coffee -c #{coffee_files.join(' ')}")
end
phantom_jasmine_path = File.expand_path("node_modules/phantom-jasmine") phantom_jasmine_path = File.expand_path("node_modules/phantom-jasmine")
jasmine_reporters_path = File.expand_path("node_modules/jasmine-reporters") jasmine_reporters_path = File.expand_path("node_modules/jasmine-reporters")
common_js_root = File.expand_path("common/static/js") common_js_root = File.expand_path("common/static/js")
...@@ -54,8 +47,8 @@ def template_jasmine_runner(lib) ...@@ -54,8 +47,8 @@ def template_jasmine_runner(lib)
# Get arrays of spec and source files, ordered by how deep they are nested below the library # Get arrays of spec and source files, ordered by how deep they are nested below the library
# (and then alphabetically) and expanded from a relative to an absolute path # (and then alphabetically) and expanded from a relative to an absolute path
spec_glob = File.join("#{lib}", "**", "spec", "**", "*.js") spec_glob = File.join(lib, "**", "spec", "**", "*.js")
src_glob = File.join("#{lib}", "**", "src", "**", "*.js") src_glob = File.join(lib, "**", "src", "**", "*.js")
js_specs = Dir[spec_glob].sort_by {|p| [p.split('/').length, p]} .map {|f| File.expand_path(f)} js_specs = Dir[spec_glob].sort_by {|p| [p.split('/').length, p]} .map {|f| File.expand_path(f)}
js_source = Dir[src_glob].sort_by {|p| [p.split('/').length, p]} .map {|f| File.expand_path(f)} js_source = Dir[src_glob].sort_by {|p| [p.split('/').length, p]} .map {|f| File.expand_path(f)}
...@@ -68,74 +61,90 @@ def template_jasmine_runner(lib) ...@@ -68,74 +61,90 @@ def template_jasmine_runner(lib)
yield File.expand_path(template_output) yield File.expand_path(template_output)
end end
def run_phantom_js(url) def jasmine_browser(url, wait=10)
phantomjs = ENV['PHANTOMJS_PATH'] || 'phantomjs' # Jitter starting the browser so that the tests don't all try and
sh("#{phantomjs} node_modules/jasmine-reporters/test/phantomjs-testrunner.js #{url}") # start the browser simultaneously
end sleep(rand(3))
sh("python -m webbrowser -t '#{url}'")
# Open jasmine tests for :system in the default browser. The :env sleep(wait)
# should (always?) be 'jasmine', but it's passed as an arg so that
# the :assets dependency gets it.
#
# This task should be invoked via the wrapper below, so we don't
# include a description to keep it from showing up in rake -T.
task :browse_jasmine, [:system, :env] => :assets do |t, args|
django_for_jasmine(args.system, true) do |jasmine_url|
Launchy.open(jasmine_url)
puts "Press ENTER to terminate".red
$stdin.gets
end
end end
# Use phantomjs to run jasmine tests from the console. The :env def jasmine_phantomjs(url)
# should (always?) be 'jasmine', but it's passed as an arg so that fail("phantomjs not found. Add it to your path, or set $PHANTOMJS_PATH") if PHANTOMJS_PATH.nil?
# the :assets dependency gets it. test_sh("#{PHANTOMJS_PATH} node_modules/jasmine-reporters/test/phantomjs-testrunner.js #{url}")
#
# This task should be invoked via the wrapper below, so we don't
# include a description to keep it from showing up in rake -T.
task :phantomjs_jasmine, [:system, :env] => :assets do |t, args|
django_for_jasmine(args.system, false) do |jasmine_url|
run_phantom_js(jasmine_url)
end
end end
# Wrapper tasks for the real browse_jasmine and phantomjs_jasmine # Wrapper tasks for the real browse_jasmine and phantomjs_jasmine
# tasks above. These have a nicer UI since there's no arg passing. # tasks above. These have a nicer UI since there's no arg passing.
[:lms, :cms].each do |system| [:lms, :cms].each do |system|
desc "Open jasmine tests for #{system} in your default browser" namespace :jasmine do
task "browse_jasmine_#{system}" do namespace system do
Rake::Task[:browse_jasmine].invoke(system, 'jasmine') desc "Open jasmine tests for #{system} in your default browser"
end task :browser do
Rake::Task[:assets].invoke(system, 'jasmine')
django_for_jasmine(system, true) do |jasmine_url|
jasmine_browser(jasmine_url)
end
end
desc "Use phantomjs to run jasmine tests for #{system} from the console"
task :phantomjs do
Rake::Task[:assets].invoke(system, 'jasmine')
phantomjs = ENV['PHANTOMJS_PATH'] || 'phantomjs'
django_for_jasmine(system, false) do |jasmine_url|
jasmine_phantomjs(jasmine_url)
end
end
end
desc "Run jasmine tests for #{system} using #{PREFERRED_METHOD}"
task system => "jasmine:#{system}:#{PREFERRED_METHOD}"
desc "Use phantomjs to run jasmine tests for #{system} from the console" task :phantomjs => "jasmine:#{system}:phantomjs"
task "phantomjs_jasmine_#{system}" do multitask :browser => "jasmine:#{system}:browser"
Rake::Task[:phantomjs_jasmine].invoke(system, 'jasmine')
end end
end end
STATIC_JASMINE_TESTS = Dir["common/lib/*"].select{|lib| File.directory?(lib)} static_js_dirs = Dir["common/lib/*"].select{|lib| File.directory?(lib)}
STATIC_JASMINE_TESTS << 'common/static/coffee' static_js_dirs << 'common/static/coffee'
static_js_dirs.select!{|lib| !Dir["#{lib}/**/spec"].empty?}
STATIC_JASMINE_TESTS.each do |lib|
desc "Open jasmine tests for #{lib} in your default browser" static_js_dirs.each do |dir|
task "browse_jasmine_#{lib}" do namespace :jasmine do
template_jasmine_runner(lib) do |f| namespace dir do
sh("python -m webbrowser -t 'file://#{f}'") desc "Open jasmine tests for #{dir} in your default browser"
puts "Press ENTER to terminate".red task :browser do
$stdin.gets # We need to use either CMS or LMS to preprocess files. Use LMS by default
Rake::Task['assets:coffee'].invoke('lms', 'jasmine')
template_jasmine_runner(dir) do |f|
jasmine_browser("file://#{f}")
end
end
desc "Use phantomjs to run jasmine tests for #{dir} from the console"
task :phantomjs do
# We need to use either CMS or LMS to preprocess files. Use LMS by default
Rake::Task[:assets].invoke('lms', 'jasmine')
template_jasmine_runner(dir) do |f|
jasmine_phantomjs(f)
end
end
end end
end
desc "Use phantomjs to run jasmine tests for #{lib} from the console" desc "Run jasmine tests for #{dir} using #{PREFERRED_METHOD}"
task "phantomjs_jasmine_#{lib}" do task dir => "jasmine:#{dir}:#{PREFERRED_METHOD}"
template_jasmine_runner(lib) do |f|
run_phantom_js(f) task :phantomjs => "jasmine:#{dir}:phantomjs"
end multitask :browser => "jasmine:#{dir}:browser"
end end
end end
desc "Open jasmine tests for discussion in your default browser" desc "Run all jasmine tests using #{PREFERRED_METHOD}"
task "browse_jasmine_discussion" => "browse_jasmine_common/static/coffee" task :jasmine => "jasmine:#{PREFERRED_METHOD}"
['phantomjs', 'browser'].each do |method|
desc "Run all jasmine tests using #{method}"
task "jasmine:#{method}"
end
desc "Use phantomjs to run jasmine tests for discussion from the console" task :test => :jasmine
task "phantomjs_jasmine_discussion" => "phantomjs_jasmine_common/static/coffee"
# Set up the clean and clobber tasks # Set up the clean and clobber tasks
CLOBBER.include(REPORT_DIR, 'test_root/*_repo', 'test_root/staticfiles') CLOBBER.include(REPORT_DIR, 'test_root/*_repo', 'test_root/staticfiles')
$failed_tests = 0
def run_under_coverage(cmd, root) def run_under_coverage(cmd, root)
cmd0, cmd_rest = cmd.split(" ", 2) cmd0, cmd_rest = cmd.split(" ", 2)
# We use "python -m coverage" so that the proper python will run the importable coverage # We use "python -m coverage" so that the proper python will run the importable coverage
...@@ -17,12 +14,7 @@ def run_tests(system, report_dir, test_id=nil, stop_on_failure=true) ...@@ -17,12 +14,7 @@ def run_tests(system, report_dir, test_id=nil, stop_on_failure=true)
dirs = Dir["common/djangoapps/*"] + Dir["#{system}/djangoapps/*"] dirs = Dir["common/djangoapps/*"] + Dir["#{system}/djangoapps/*"]
test_id = dirs.join(' ') if test_id.nil? or test_id == '' test_id = dirs.join(' ') if test_id.nil? or test_id == ''
cmd = django_admin(system, :test, 'test', '--logging-clear-handlers', test_id) cmd = django_admin(system, :test, 'test', '--logging-clear-handlers', test_id)
sh(run_under_coverage(cmd, system)) do |ok, res| test_sh(run_under_coverage(cmd, system))
if !ok and stop_on_failure
abort "Test failed!"
end
$failed_tests += 1 unless ok
end
end end
def run_acceptance_tests(system, report_dir, harvest_args) def run_acceptance_tests(system, report_dir, harvest_args)
...@@ -38,7 +30,7 @@ def run_acceptance_tests(system, report_dir, harvest_args) ...@@ -38,7 +30,7 @@ def run_acceptance_tests(system, report_dir, harvest_args)
end end
sh(django_admin(system, 'acceptance', 'syncdb', '--noinput')) sh(django_admin(system, 'acceptance', 'syncdb', '--noinput'))
sh(django_admin(system, 'acceptance', 'migrate', '--noinput')) sh(django_admin(system, 'acceptance', 'migrate', '--noinput'))
sh(django_admin(system, 'acceptance', 'harvest', '--debug-mode', '--tag -skip', harvest_args)) test_sh(django_admin(system, 'acceptance', 'harvest', '--debug-mode', '--tag -skip', harvest_args))
end end
...@@ -55,13 +47,13 @@ TEST_TASK_DIRS = [] ...@@ -55,13 +47,13 @@ TEST_TASK_DIRS = []
# Per System tasks # Per System tasks
desc "Run all django tests on our djangoapps for the #{system}" desc "Run all django tests on our djangoapps for the #{system}"
task "test_#{system}", [:test_id, :stop_on_failure] => ["clean_test_files", :predjango, "#{system}:gather_assets:test", "fasttest_#{system}"] task "test_#{system}", [:test_id] => ["clean_test_files", :predjango, "#{system}:gather_assets:test", "fasttest_#{system}"]
# Have a way to run the tests without running collectstatic -- useful when debugging without # Have a way to run the tests without running collectstatic -- useful when debugging without
# messing with static files. # messing with static files.
task "fasttest_#{system}", [:test_id, :stop_on_failure] => [report_dir, :install_prereqs, :predjango] do |t, args| task "fasttest_#{system}", [:test_id] => [report_dir, :install_prereqs, :predjango] do |t, args|
args.with_defaults(:stop_on_failure => 'true', :test_id => nil) args.with_defaults(:test_id => nil)
run_tests(system, report_dir, args.test_id, args.stop_on_failure) run_tests(system, report_dir, args.test_id)
end end
# Run acceptance tests # Run acceptance tests
...@@ -89,9 +81,7 @@ Dir["common/lib/*"].select{|lib| File.directory?(lib)}.each do |lib| ...@@ -89,9 +81,7 @@ Dir["common/lib/*"].select{|lib| File.directory?(lib)}.each do |lib|
task task_name => report_dir do task task_name => report_dir do
ENV['NOSE_XUNIT_FILE'] = File.join(report_dir, "nosetests.xml") ENV['NOSE_XUNIT_FILE'] = File.join(report_dir, "nosetests.xml")
cmd = "nosetests #{lib}" cmd = "nosetests #{lib}"
sh(run_under_coverage(cmd, lib)) do |ok, res| test_sh(run_under_coverage(cmd, lib))
$failed_tests += 1 unless ok
end
end end
TEST_TASK_DIRS << lib TEST_TASK_DIRS << lib
...@@ -107,17 +97,11 @@ TEST_TASK_DIRS.each do |dir| ...@@ -107,17 +97,11 @@ TEST_TASK_DIRS.each do |dir|
report_dir = report_dir_path(dir) report_dir = report_dir_path(dir)
directory report_dir directory report_dir
task :report_dirs => [REPORT_DIR, report_dir] task :report_dirs => [REPORT_DIR, report_dir]
task :test => "test_#{dir}"
end end
task :test do desc "Run all tests"
TEST_TASK_DIRS.each do |dir| task :test
Rake::Task["test_#{dir}"].invoke(nil, false)
end
if $failed_tests > 0
abort "Tests failed!"
end
end
namespace :coverage do namespace :coverage do
desc "Build the html coverage reports" desc "Build the html coverage reports"
......
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