Compare commits
1 Commits
Author | SHA1 | Date |
---|---|---|
Nick Gauthier | 825368a7ec |
|
@ -17,8 +17,5 @@ tmtags
|
||||||
coverage
|
coverage
|
||||||
rdoc
|
rdoc
|
||||||
pkg
|
pkg
|
||||||
tags
|
|
||||||
.rvmrc
|
|
||||||
hydra-runner.log
|
|
||||||
|
|
||||||
## PROJECT::SPECIFIC
|
## PROJECT::SPECIFIC
|
||||||
|
|
5
Gemfile
5
Gemfile
|
@ -1,5 +0,0 @@
|
||||||
source :rubygems
|
|
||||||
|
|
||||||
gemspec
|
|
||||||
gem 'rake', '0.8.7'
|
|
||||||
gem 'test-unit', :require => 'test/unit'
|
|
48
Gemfile.lock
48
Gemfile.lock
|
@ -1,48 +0,0 @@
|
||||||
PATH
|
|
||||||
remote: .
|
|
||||||
specs:
|
|
||||||
hydra (0.23.3)
|
|
||||||
rake (= 0.8.7)
|
|
||||||
test-unit
|
|
||||||
|
|
||||||
GEM
|
|
||||||
remote: http://rubygems.org/
|
|
||||||
specs:
|
|
||||||
builder (2.1.2)
|
|
||||||
cucumber (0.9.2)
|
|
||||||
builder (~> 2.1.2)
|
|
||||||
diff-lcs (~> 1.1.2)
|
|
||||||
gherkin (~> 2.2.5)
|
|
||||||
json (~> 1.4.6)
|
|
||||||
term-ansicolor (~> 1.0.5)
|
|
||||||
diff-lcs (1.1.3)
|
|
||||||
gherkin (2.2.9)
|
|
||||||
json (~> 1.4.6)
|
|
||||||
term-ansicolor (~> 1.0.5)
|
|
||||||
json (1.4.6)
|
|
||||||
rake (0.8.7)
|
|
||||||
rspec (2.6.0)
|
|
||||||
rspec-core (~> 2.6.0)
|
|
||||||
rspec-expectations (~> 2.6.0)
|
|
||||||
rspec-mocks (~> 2.6.0)
|
|
||||||
rspec-core (2.6.4)
|
|
||||||
rspec-expectations (2.6.0)
|
|
||||||
diff-lcs (~> 1.1.2)
|
|
||||||
rspec-mocks (2.6.0)
|
|
||||||
shoulda (2.10.3)
|
|
||||||
term-ansicolor (1.0.6)
|
|
||||||
test-unit (2.4.0)
|
|
||||||
therubyracer (0.7.4)
|
|
||||||
|
|
||||||
PLATFORMS
|
|
||||||
ruby
|
|
||||||
|
|
||||||
DEPENDENCIES
|
|
||||||
cucumber (= 0.9.2)
|
|
||||||
hydra!
|
|
||||||
rake (= 0.8.7)
|
|
||||||
rspec (~> 2.6.0)
|
|
||||||
rspec-core (= 2.6.4)
|
|
||||||
shoulda (= 2.10.3)
|
|
||||||
test-unit
|
|
||||||
therubyracer (= 0.7.4)
|
|
2
LICENSE
2
LICENSE
|
@ -1,4 +1,4 @@
|
||||||
Copyright (c) 2009-2010 Nick Gauthier
|
Copyright (c) 2009 Nick Gauthier
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
a copy of this software and associated documentation files (the
|
a copy of this software and associated documentation files (the
|
||||||
|
|
10
Rakefile
10
Rakefile
|
@ -11,10 +11,8 @@ begin
|
||||||
gem.homepage = "http://github.com/ngauthier/hydra"
|
gem.homepage = "http://github.com/ngauthier/hydra"
|
||||||
gem.authors = ["Nick Gauthier"]
|
gem.authors = ["Nick Gauthier"]
|
||||||
gem.add_development_dependency "shoulda", "= 2.10.3"
|
gem.add_development_dependency "shoulda", "= 2.10.3"
|
||||||
gem.add_development_dependency "rspec", "~> 2.6.0"
|
gem.add_development_dependency "rspec", "= 1.3.0"
|
||||||
gem.add_development_dependency "rspec-core", "= 2.6.4"
|
gem.add_development_dependency "cucumber", "= 0.6.4"
|
||||||
gem.add_development_dependency "cucumber", "= 0.9.2"
|
|
||||||
gem.add_development_dependency "therubyracer", "= 0.7.4"
|
|
||||||
end
|
end
|
||||||
Jeweler::GemcutterTasks.new
|
Jeweler::GemcutterTasks.new
|
||||||
rescue LoadError
|
rescue LoadError
|
||||||
|
@ -23,7 +21,7 @@ end
|
||||||
|
|
||||||
require 'rake/testtask'
|
require 'rake/testtask'
|
||||||
Rake::TestTask.new(:test) do |test|
|
Rake::TestTask.new(:test) do |test|
|
||||||
test.libs << 'test'
|
test.libs << 'lib' << 'test'
|
||||||
test.pattern = 'test/**/*_test.rb'
|
test.pattern = 'test/**/*_test.rb'
|
||||||
test.verbose = true
|
test.verbose = true
|
||||||
end
|
end
|
||||||
|
@ -41,6 +39,8 @@ rescue LoadError
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
task :test => :check_dependencies
|
||||||
|
|
||||||
task :default => :test
|
task :default => :test
|
||||||
|
|
||||||
require 'rake/rdoctask'
|
require 'rake/rdoctask'
|
||||||
|
|
246
hydra.gemspec
246
hydra.gemspec
|
@ -1,191 +1,117 @@
|
||||||
# Generated by jeweler
|
# Generated by jeweler
|
||||||
# DO NOT EDIT THIS FILE DIRECTLY
|
# DO NOT EDIT THIS FILE DIRECTLY
|
||||||
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
||||||
# -*- encoding: utf-8 -*-
|
# -*- encoding: utf-8 -*-
|
||||||
|
|
||||||
Gem::Specification.new do |s|
|
Gem::Specification.new do |s|
|
||||||
s.name = %q{hydra}
|
s.name = %q{hydra}
|
||||||
s.version = "0.23.3"
|
s.version = "0.16.2"
|
||||||
|
|
||||||
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
||||||
s.authors = [%q{Nick Gauthier}]
|
s.authors = ["Nick Gauthier"]
|
||||||
s.date = %q{2011-08-31}
|
s.date = %q{2010-04-11}
|
||||||
s.description = %q{Spread your tests over multiple machines to test your code faster.}
|
s.description = %q{Spread your tests over multiple machines to test your code faster.}
|
||||||
s.email = %q{nick@smartlogicsolutions.com}
|
s.email = %q{nick@smartlogicsolutions.com}
|
||||||
s.extra_rdoc_files = [
|
s.extra_rdoc_files = [
|
||||||
"LICENSE",
|
"LICENSE",
|
||||||
"README.rdoc",
|
"README.rdoc",
|
||||||
"TODO"
|
"TODO"
|
||||||
]
|
]
|
||||||
s.files = [
|
s.files = [
|
||||||
".document",
|
".document",
|
||||||
"Gemfile",
|
".gitignore",
|
||||||
"Gemfile.lock",
|
"LICENSE",
|
||||||
"LICENSE",
|
"README.rdoc",
|
||||||
"README.rdoc",
|
"Rakefile",
|
||||||
"Rakefile",
|
"TODO",
|
||||||
"TODO",
|
"VERSION",
|
||||||
"VERSION",
|
"caliper.yml",
|
||||||
"caliper.yml",
|
"hydra-icon-64x64.png",
|
||||||
"hydra-icon-64x64.png",
|
"hydra.gemspec",
|
||||||
"hydra.gemspec",
|
"hydra_gray.png",
|
||||||
"hydra_gray.png",
|
"lib/hydra.rb",
|
||||||
"lib/hydra.rb",
|
"lib/hydra/cucumber/formatter.rb",
|
||||||
"lib/hydra/config.rb",
|
"lib/hydra/hash.rb",
|
||||||
"lib/hydra/cucumber/formatter.rb",
|
"lib/hydra/listener/abstract.rb",
|
||||||
"lib/hydra/cucumber/partial_html.rb",
|
"lib/hydra/listener/minimal_output.rb",
|
||||||
"lib/hydra/hash.rb",
|
"lib/hydra/listener/notifier.rb",
|
||||||
"lib/hydra/js/lint.js",
|
"lib/hydra/listener/progress_bar.rb",
|
||||||
"lib/hydra/listener/abstract.rb",
|
"lib/hydra/listener/report_generator.rb",
|
||||||
"lib/hydra/listener/cucumber.css",
|
"lib/hydra/master.rb",
|
||||||
"lib/hydra/listener/cucumber_html_report.rb",
|
"lib/hydra/message.rb",
|
||||||
"lib/hydra/listener/jquery-min.js",
|
"lib/hydra/message/master_messages.rb",
|
||||||
"lib/hydra/listener/minimal_output.rb",
|
"lib/hydra/message/runner_messages.rb",
|
||||||
"lib/hydra/listener/notifier.rb",
|
"lib/hydra/message/worker_messages.rb",
|
||||||
"lib/hydra/listener/progress_bar.rb",
|
"lib/hydra/messaging_io.rb",
|
||||||
"lib/hydra/listener/report_generator.rb",
|
"lib/hydra/pipe.rb",
|
||||||
"lib/hydra/master.rb",
|
"lib/hydra/runner.rb",
|
||||||
"lib/hydra/message.rb",
|
"lib/hydra/safe_fork.rb",
|
||||||
"lib/hydra/message/master_messages.rb",
|
"lib/hydra/spec/autorun_override.rb",
|
||||||
"lib/hydra/message/runner_messages.rb",
|
"lib/hydra/spec/hydra_formatter.rb",
|
||||||
"lib/hydra/message/worker_messages.rb",
|
"lib/hydra/ssh.rb",
|
||||||
"lib/hydra/messaging_io.rb",
|
"lib/hydra/stdio.rb",
|
||||||
"lib/hydra/pipe.rb",
|
"lib/hydra/tasks.rb",
|
||||||
"lib/hydra/runner.rb",
|
"lib/hydra/trace.rb",
|
||||||
"lib/hydra/runner_listener/abstract.rb",
|
"lib/hydra/worker.rb",
|
||||||
"lib/hydra/safe_fork.rb",
|
"test/fixtures/assert_true.rb",
|
||||||
"lib/hydra/spec/autorun_override.rb",
|
"test/fixtures/config.yml",
|
||||||
"lib/hydra/spec/hydra_formatter.rb",
|
"test/fixtures/features/step_definitions.rb",
|
||||||
"lib/hydra/ssh.rb",
|
"test/fixtures/features/write_alternate_file.feature",
|
||||||
"lib/hydra/stdio.rb",
|
"test/fixtures/features/write_file.feature",
|
||||||
"lib/hydra/sync.rb",
|
"test/fixtures/hello_world.rb",
|
||||||
"lib/hydra/tasks.rb",
|
"test/fixtures/slow.rb",
|
||||||
"lib/hydra/tmpdir.rb",
|
"test/fixtures/sync_test.rb",
|
||||||
"lib/hydra/trace.rb",
|
"test/fixtures/write_file.rb",
|
||||||
"lib/hydra/worker.rb",
|
"test/fixtures/write_file_alternate_spec.rb",
|
||||||
"test/fixtures/assert_true.rb",
|
"test/fixtures/write_file_spec.rb",
|
||||||
"test/fixtures/config.yml",
|
"test/master_test.rb",
|
||||||
"test/fixtures/conflicting.rb",
|
"test/message_test.rb",
|
||||||
"test/fixtures/features/step_definitions.rb",
|
"test/pipe_test.rb",
|
||||||
"test/fixtures/features/write_alternate_file.feature",
|
"test/runner_test.rb",
|
||||||
"test/fixtures/features/write_file.feature",
|
"test/ssh_test.rb",
|
||||||
"test/fixtures/hello_world.rb",
|
"test/test_helper.rb",
|
||||||
"test/fixtures/hydra_worker_init.rb",
|
"test/worker_test.rb"
|
||||||
"test/fixtures/js_file.js",
|
|
||||||
"test/fixtures/json_data.json",
|
|
||||||
"test/fixtures/many_outputs_to_console.rb",
|
|
||||||
"test/fixtures/master_listeners.rb",
|
|
||||||
"test/fixtures/runner_listeners.rb",
|
|
||||||
"test/fixtures/slow.rb",
|
|
||||||
"test/fixtures/sync_test.rb",
|
|
||||||
"test/fixtures/task_test_config.yml",
|
|
||||||
"test/fixtures/write_file.rb",
|
|
||||||
"test/fixtures/write_file_alternate_spec.rb",
|
|
||||||
"test/fixtures/write_file_spec.rb",
|
|
||||||
"test/fixtures/write_file_with_pending_spec.rb",
|
|
||||||
"test/master_test.rb",
|
|
||||||
"test/message_test.rb",
|
|
||||||
"test/pipe_test.rb",
|
|
||||||
"test/runner_test.rb",
|
|
||||||
"test/ssh_test.rb",
|
|
||||||
"test/sync_test.rb",
|
|
||||||
"test/task_test.rb",
|
|
||||||
"test/test_helper.rb",
|
|
||||||
"test/worker_test.rb"
|
|
||||||
]
|
]
|
||||||
s.homepage = %q{http://github.com/ngauthier/hydra}
|
s.homepage = %q{http://github.com/ngauthier/hydra}
|
||||||
s.require_paths = [%q{lib}]
|
s.rdoc_options = ["--charset=UTF-8"]
|
||||||
s.rubygems_version = %q{1.8.9}
|
s.require_paths = ["lib"]
|
||||||
|
s.rubygems_version = %q{1.3.6}
|
||||||
s.summary = %q{Distributed testing toolkit}
|
s.summary = %q{Distributed testing toolkit}
|
||||||
|
s.test_files = [
|
||||||
|
"test/pipe_test.rb",
|
||||||
|
"test/test_helper.rb",
|
||||||
|
"test/ssh_test.rb",
|
||||||
|
"test/message_test.rb",
|
||||||
|
"test/master_test.rb",
|
||||||
|
"test/fixtures/write_file.rb",
|
||||||
|
"test/fixtures/slow.rb",
|
||||||
|
"test/fixtures/write_file_spec.rb",
|
||||||
|
"test/fixtures/features/step_definitions.rb",
|
||||||
|
"test/fixtures/hello_world.rb",
|
||||||
|
"test/fixtures/write_file_alternate_spec.rb",
|
||||||
|
"test/fixtures/sync_test.rb",
|
||||||
|
"test/fixtures/assert_true.rb",
|
||||||
|
"test/runner_test.rb",
|
||||||
|
"test/worker_test.rb"
|
||||||
|
]
|
||||||
|
|
||||||
if s.respond_to? :specification_version then
|
if s.respond_to? :specification_version then
|
||||||
|
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
||||||
s.specification_version = 3
|
s.specification_version = 3
|
||||||
|
|
||||||
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
||||||
s.add_runtime_dependency(%q<rake>)
|
|
||||||
s.add_runtime_dependency(%q<test-unit>, [">= 0"])
|
|
||||||
s.add_development_dependency(%q<shoulda>, ["= 2.10.3"])
|
s.add_development_dependency(%q<shoulda>, ["= 2.10.3"])
|
||||||
s.add_development_dependency(%q<cucumber>, ["= 0.9.2"])
|
s.add_development_dependency(%q<rspec>, ["= 1.3.0"])
|
||||||
s.add_development_dependency(%q<therubyracer>, ["= 0.7.4"])
|
s.add_development_dependency(%q<cucumber>, ["= 0.6.4"])
|
||||||
s.add_development_dependency(%q<shoulda>, ["= 2.10.3"])
|
|
||||||
s.add_development_dependency(%q<cucumber>, ["= 0.9.2"])
|
|
||||||
s.add_development_dependency(%q<therubyracer>, ["= 0.7.4"])
|
|
||||||
s.add_development_dependency(%q<shoulda>, ["= 2.10.3"])
|
|
||||||
s.add_development_dependency(%q<rspec>, ["~> 2.6.0"])
|
|
||||||
s.add_development_dependency(%q<cucumber>, ["= 0.9.2"])
|
|
||||||
s.add_development_dependency(%q<therubyracer>, ["= 0.7.4"])
|
|
||||||
s.add_development_dependency(%q<shoulda>, ["= 2.10.3"])
|
|
||||||
s.add_development_dependency(%q<rspec>, ["~> 2.6.0"])
|
|
||||||
s.add_development_dependency(%q<rspec-core>, ["= 2.6.4"])
|
|
||||||
s.add_development_dependency(%q<cucumber>, ["= 0.9.2"])
|
|
||||||
s.add_development_dependency(%q<therubyracer>, ["= 0.7.4"])
|
|
||||||
s.add_development_dependency(%q<shoulda>, ["= 2.10.3"])
|
|
||||||
s.add_development_dependency(%q<rspec>, ["~> 2.6.0"])
|
|
||||||
s.add_development_dependency(%q<rspec-core>, ["= 2.6.4"])
|
|
||||||
s.add_development_dependency(%q<cucumber>, ["= 0.9.2"])
|
|
||||||
s.add_development_dependency(%q<therubyracer>, ["= 0.7.4"])
|
|
||||||
s.add_development_dependency(%q<shoulda>, ["= 2.10.3"])
|
|
||||||
s.add_development_dependency(%q<rspec>, ["~> 2.6.0"])
|
|
||||||
s.add_development_dependency(%q<rspec-core>, ["= 2.6.4"])
|
|
||||||
s.add_development_dependency(%q<cucumber>, ["= 0.9.2"])
|
|
||||||
s.add_development_dependency(%q<therubyracer>, ["= 0.7.4"])
|
|
||||||
else
|
else
|
||||||
s.add_dependency(%q<rake>)
|
|
||||||
s.add_dependency(%q<test-unit>, [">= 0"])
|
|
||||||
s.add_dependency(%q<shoulda>, ["= 2.10.3"])
|
s.add_dependency(%q<shoulda>, ["= 2.10.3"])
|
||||||
s.add_dependency(%q<cucumber>, ["= 0.9.2"])
|
s.add_dependency(%q<rspec>, ["= 1.3.0"])
|
||||||
s.add_dependency(%q<therubyracer>, ["= 0.7.4"])
|
s.add_dependency(%q<cucumber>, ["= 0.6.4"])
|
||||||
s.add_dependency(%q<shoulda>, ["= 2.10.3"])
|
|
||||||
s.add_dependency(%q<cucumber>, ["= 0.9.2"])
|
|
||||||
s.add_dependency(%q<therubyracer>, ["= 0.7.4"])
|
|
||||||
s.add_dependency(%q<shoulda>, ["= 2.10.3"])
|
|
||||||
s.add_dependency(%q<rspec>, ["~> 2.6.0"])
|
|
||||||
s.add_dependency(%q<cucumber>, ["= 0.9.2"])
|
|
||||||
s.add_dependency(%q<therubyracer>, ["= 0.7.4"])
|
|
||||||
s.add_dependency(%q<shoulda>, ["= 2.10.3"])
|
|
||||||
s.add_dependency(%q<rspec>, ["~> 2.6.0"])
|
|
||||||
s.add_dependency(%q<rspec-core>, ["= 2.6.4"])
|
|
||||||
s.add_dependency(%q<cucumber>, ["= 0.9.2"])
|
|
||||||
s.add_dependency(%q<therubyracer>, ["= 0.7.4"])
|
|
||||||
s.add_dependency(%q<shoulda>, ["= 2.10.3"])
|
|
||||||
s.add_dependency(%q<rspec>, ["~> 2.6.0"])
|
|
||||||
s.add_dependency(%q<rspec-core>, ["= 2.6.4"])
|
|
||||||
s.add_dependency(%q<cucumber>, ["= 0.9.2"])
|
|
||||||
s.add_dependency(%q<therubyracer>, ["= 0.7.4"])
|
|
||||||
s.add_dependency(%q<shoulda>, ["= 2.10.3"])
|
|
||||||
s.add_dependency(%q<rspec>, ["~> 2.6.0"])
|
|
||||||
s.add_dependency(%q<rspec-core>, ["= 2.6.4"])
|
|
||||||
s.add_dependency(%q<cucumber>, ["= 0.9.2"])
|
|
||||||
s.add_dependency(%q<therubyracer>, ["= 0.7.4"])
|
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
s.add_dependency(%q<rake>)
|
|
||||||
s.add_dependency(%q<test-unit>, [">= 0"])
|
|
||||||
s.add_dependency(%q<shoulda>, ["= 2.10.3"])
|
s.add_dependency(%q<shoulda>, ["= 2.10.3"])
|
||||||
s.add_dependency(%q<cucumber>, ["= 0.9.2"])
|
s.add_dependency(%q<rspec>, ["= 1.3.0"])
|
||||||
s.add_dependency(%q<therubyracer>, ["= 0.7.4"])
|
s.add_dependency(%q<cucumber>, ["= 0.6.4"])
|
||||||
s.add_dependency(%q<shoulda>, ["= 2.10.3"])
|
|
||||||
s.add_dependency(%q<cucumber>, ["= 0.9.2"])
|
|
||||||
s.add_dependency(%q<therubyracer>, ["= 0.7.4"])
|
|
||||||
s.add_dependency(%q<shoulda>, ["= 2.10.3"])
|
|
||||||
s.add_dependency(%q<rspec>, ["~> 2.6.0"])
|
|
||||||
s.add_dependency(%q<cucumber>, ["= 0.9.2"])
|
|
||||||
s.add_dependency(%q<therubyracer>, ["= 0.7.4"])
|
|
||||||
s.add_dependency(%q<shoulda>, ["= 2.10.3"])
|
|
||||||
s.add_dependency(%q<rspec>, ["~> 2.6.0"])
|
|
||||||
s.add_dependency(%q<rspec-core>, ["= 2.6.4"])
|
|
||||||
s.add_dependency(%q<cucumber>, ["= 0.9.2"])
|
|
||||||
s.add_dependency(%q<therubyracer>, ["= 0.7.4"])
|
|
||||||
s.add_dependency(%q<shoulda>, ["= 2.10.3"])
|
|
||||||
s.add_dependency(%q<rspec>, ["~> 2.6.0"])
|
|
||||||
s.add_dependency(%q<rspec-core>, ["= 2.6.4"])
|
|
||||||
s.add_dependency(%q<cucumber>, ["= 0.9.2"])
|
|
||||||
s.add_dependency(%q<therubyracer>, ["= 0.7.4"])
|
|
||||||
s.add_dependency(%q<shoulda>, ["= 2.10.3"])
|
|
||||||
s.add_dependency(%q<rspec>, ["~> 2.6.0"])
|
|
||||||
s.add_dependency(%q<rspec-core>, ["= 2.6.4"])
|
|
||||||
s.add_dependency(%q<cucumber>, ["= 0.9.2"])
|
|
||||||
s.add_dependency(%q<therubyracer>, ["= 0.7.4"])
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -7,10 +7,9 @@ require 'hydra/safe_fork'
|
||||||
require 'hydra/runner'
|
require 'hydra/runner'
|
||||||
require 'hydra/worker'
|
require 'hydra/worker'
|
||||||
require 'hydra/master'
|
require 'hydra/master'
|
||||||
require 'hydra/sync'
|
|
||||||
require 'hydra/listener/abstract'
|
require 'hydra/listener/abstract'
|
||||||
require 'hydra/listener/minimal_output'
|
require 'hydra/listener/minimal_output'
|
||||||
require 'hydra/listener/report_generator'
|
require 'hydra/listener/report_generator'
|
||||||
require 'hydra/listener/notifier'
|
require 'hydra/listener/notifier'
|
||||||
require 'hydra/listener/progress_bar'
|
require 'hydra/listener/progress_bar'
|
||||||
require 'hydra/runner_listener/abstract'
|
|
||||||
|
|
|
@ -1,21 +0,0 @@
|
||||||
module Hydra
|
|
||||||
class Config
|
|
||||||
class << self
|
|
||||||
def load(config_file)
|
|
||||||
begin
|
|
||||||
config_erb = ERB.new(IO.read(config_file)).result(binding)
|
|
||||||
rescue Exception => e
|
|
||||||
raise(YmlLoadError,"config file was found, but could not be parsed with ERB.\n#{$!.inspect}")
|
|
||||||
end
|
|
||||||
|
|
||||||
begin
|
|
||||||
config_yml = YAML::load(config_erb)
|
|
||||||
rescue StandardError => e
|
|
||||||
raise(YmlLoadError,"config file was found, but could not be parsed.\n#{$!.inspect}")
|
|
||||||
end
|
|
||||||
|
|
||||||
config_yml
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -19,6 +19,7 @@ module Cucumber #:nodoc:
|
||||||
print_steps(:failed)
|
print_steps(:failed)
|
||||||
print_snippets(@options)
|
print_snippets(@options)
|
||||||
print_passing_wip(@options)
|
print_passing_wip(@options)
|
||||||
|
print_tag_limit_warnings(features)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Removed all progress output
|
# Removed all progress output
|
||||||
|
|
|
@ -1,24 +0,0 @@
|
||||||
require 'cucumber/formatter/html'
|
|
||||||
|
|
||||||
module Hydra
|
|
||||||
module Formatter
|
|
||||||
class PartialHtml < Cucumber::Formatter::Html
|
|
||||||
|
|
||||||
def before_features(features)
|
|
||||||
# we do not want the default implementation as we will write our own header
|
|
||||||
end
|
|
||||||
|
|
||||||
def after_features(features)
|
|
||||||
# we do not want the default implementation as we will write our own footer
|
|
||||||
end
|
|
||||||
|
|
||||||
def after_step(step)
|
|
||||||
# need an alterantive way of incrementing progress/outputing stats
|
|
||||||
end
|
|
||||||
|
|
||||||
def percent_done
|
|
||||||
0
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
5150
lib/hydra/js/lint.js
5150
lib/hydra/js/lint.js
File diff suppressed because it is too large
Load Diff
|
@ -10,23 +10,14 @@ module Hydra #:nodoc:
|
||||||
def initialize(output = $stdout)
|
def initialize(output = $stdout)
|
||||||
@output = output
|
@output = output
|
||||||
end
|
end
|
||||||
|
|
||||||
# Fired when testing has started
|
# Fired when testing has started
|
||||||
def testing_begin(files)
|
def testing_begin(files)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Fired when testing finishes, after the workers shutdown
|
# Fired when testing finishes
|
||||||
def testing_end
|
def testing_end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Fired after runner processes have been started
|
|
||||||
def worker_begin(worker)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Fired before shutting down the worker
|
|
||||||
def worker_end(worker)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Fired when a file is started
|
# Fired when a file is started
|
||||||
def file_begin(file)
|
def file_begin(file)
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,279 +0,0 @@
|
||||||
/* cucumber.css is generated from cucumber.sass */
|
|
||||||
/* Regenerate with rake sass */
|
|
||||||
body {
|
|
||||||
font-size: 0px;
|
|
||||||
color: white;
|
|
||||||
margin: 0px;
|
|
||||||
padding: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cucumber, td, th {
|
|
||||||
font: normal 11px "Lucida Grande", Helvetica, sans-serif;
|
|
||||||
background: white;
|
|
||||||
color: black;
|
|
||||||
}
|
|
||||||
.cucumber #cucumber-header, td #cucumber-header, th #cucumber-header {
|
|
||||||
background: #65c400;
|
|
||||||
color: white;
|
|
||||||
height: 6em;
|
|
||||||
}
|
|
||||||
.cucumber #cucumber-header #expand-collapse p, td #cucumber-header #expand-collapse p, th #cucumber-header #expand-collapse p {
|
|
||||||
float: right;
|
|
||||||
margin: 0 0 0 10px;
|
|
||||||
}
|
|
||||||
.cucumber .scenario h3, td .scenario h3, th .scenario h3 {
|
|
||||||
font-size: 11px;
|
|
||||||
padding: 3px;
|
|
||||||
margin: 0;
|
|
||||||
background: #65c400;
|
|
||||||
color: white;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
.cucumber h1, td h1, th h1 {
|
|
||||||
margin: 0px 10px 0px 10px;
|
|
||||||
padding: 10px;
|
|
||||||
font-family: "Lucida Grande", Helvetica, sans-serif;
|
|
||||||
font-size: 2em;
|
|
||||||
position: absolute;
|
|
||||||
}
|
|
||||||
.cucumber h4, td h4, th h4 {
|
|
||||||
margin-bottom: 2px;
|
|
||||||
}
|
|
||||||
.cucumber div.feature, td div.feature, th div.feature {
|
|
||||||
padding: 2px;
|
|
||||||
margin: 0px 10px 5px 10px;
|
|
||||||
}
|
|
||||||
.cucumber div.examples, td div.examples, th div.examples {
|
|
||||||
padding: 0em 0em 0em 1em;
|
|
||||||
}
|
|
||||||
.cucumber .stats, td .stats, th .stats {
|
|
||||||
margin: 2em;
|
|
||||||
}
|
|
||||||
.cucumber .summary ul.features li, td .summary ul.features li, th .summary ul.features li {
|
|
||||||
display: inline;
|
|
||||||
}
|
|
||||||
.cucumber .step_name, td .step_name, th .step_name {
|
|
||||||
float: left;
|
|
||||||
}
|
|
||||||
.cucumber .step_file, td .step_file, th .step_file {
|
|
||||||
text-align: right;
|
|
||||||
color: #999999;
|
|
||||||
}
|
|
||||||
.cucumber .step_file a, td .step_file a, th .step_file a {
|
|
||||||
color: #999999;
|
|
||||||
}
|
|
||||||
.cucumber .scenario_file, td .scenario_file, th .scenario_file {
|
|
||||||
float: right;
|
|
||||||
color: #999999;
|
|
||||||
}
|
|
||||||
.cucumber .tag, td .tag, th .tag {
|
|
||||||
font-weight: bold;
|
|
||||||
color: #246ac1;
|
|
||||||
}
|
|
||||||
.cucumber .backtrace, td .backtrace, th .backtrace {
|
|
||||||
margin-top: 0;
|
|
||||||
margin-bottom: 0;
|
|
||||||
margin-left: 1em;
|
|
||||||
color: black;
|
|
||||||
}
|
|
||||||
.cucumber a, td a, th a {
|
|
||||||
text-decoration: none;
|
|
||||||
color: #be5c00;
|
|
||||||
}
|
|
||||||
.cucumber a:hover, td a:hover, th a:hover {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
.cucumber a:visited, td a:visited, th a:visited {
|
|
||||||
font-weight: normal;
|
|
||||||
}
|
|
||||||
.cucumber a div.examples, td a div.examples, th a div.examples {
|
|
||||||
margin: 5px 0px 5px 15px;
|
|
||||||
color: black;
|
|
||||||
}
|
|
||||||
.cucumber .outline table, td .outline table, th .outline table {
|
|
||||||
margin: 0px 0px 5px 10px;
|
|
||||||
}
|
|
||||||
.cucumber table, td table, th table {
|
|
||||||
border-collapse: collapse;
|
|
||||||
}
|
|
||||||
.cucumber table td, td table td, th table td {
|
|
||||||
padding: 3px 3px 3px 5px;
|
|
||||||
}
|
|
||||||
.cucumber table td.failed, .cucumber table td.passed, .cucumber table td.skipped, .cucumber table td.pending, .cucumber table td.undefined, td table td.failed, td table td.passed, td table td.skipped, td table td.pending, td table td.undefined, th table td.failed, th table td.passed, th table td.skipped, th table td.pending, th table td.undefined {
|
|
||||||
padding-left: 18px;
|
|
||||||
padding-right: 10px;
|
|
||||||
}
|
|
||||||
.cucumber table td.failed, td table td.failed, th table td.failed {
|
|
||||||
border-left: 5px solid #c20000;
|
|
||||||
border-bottom: 1px solid #c20000;
|
|
||||||
background: #fffbd3;
|
|
||||||
color: #c20000;
|
|
||||||
}
|
|
||||||
.cucumber table td.passed, td table td.passed, th table td.passed {
|
|
||||||
border-left: 5px solid #65c400;
|
|
||||||
border-bottom: 1px solid #65c400;
|
|
||||||
background: #dbffb4;
|
|
||||||
color: #3d7700;
|
|
||||||
}
|
|
||||||
.cucumber table td.skipped, td table td.skipped, th table td.skipped {
|
|
||||||
border-left: 5px solid aqua;
|
|
||||||
border-bottom: 1px solid aqua;
|
|
||||||
background: #e0ffff;
|
|
||||||
color: #001111;
|
|
||||||
}
|
|
||||||
.cucumber table td.pending, td table td.pending, th table td.pending {
|
|
||||||
border-left: 5px solid #faf834;
|
|
||||||
border-bottom: 1px solid #faf834;
|
|
||||||
background: #fcfb98;
|
|
||||||
color: #131313;
|
|
||||||
}
|
|
||||||
.cucumber table td.undefined, td table td.undefined, th table td.undefined {
|
|
||||||
border-left: 5px solid #faf834;
|
|
||||||
border-bottom: 1px solid #faf834;
|
|
||||||
background: #fcfb98;
|
|
||||||
color: #131313;
|
|
||||||
}
|
|
||||||
.cucumber table td.message, td table td.message, th table td.message {
|
|
||||||
border-left: 5px solid aqua;
|
|
||||||
border-bottom: 1px solid aqua;
|
|
||||||
background: #e0ffff;
|
|
||||||
color: #001111;
|
|
||||||
}
|
|
||||||
.cucumber ol, td ol, th ol {
|
|
||||||
list-style: none;
|
|
||||||
margin: 0px;
|
|
||||||
padding: 0px;
|
|
||||||
}
|
|
||||||
.cucumber ol li.step, td ol li.step, th ol li.step {
|
|
||||||
padding: 3px 3px 3px 18px;
|
|
||||||
margin: 5px 0px 5px 5px;
|
|
||||||
}
|
|
||||||
.cucumber ol li, td ol li, th ol li {
|
|
||||||
margin: 0em 0em 0em 1em;
|
|
||||||
padding: 0em 0em 0em 0.2em;
|
|
||||||
}
|
|
||||||
.cucumber ol li span.param, td ol li span.param, th ol li span.param {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
.cucumber ol li.failed, td ol li.failed, th ol li.failed {
|
|
||||||
border-left: 5px solid #c20000;
|
|
||||||
border-bottom: 1px solid #c20000;
|
|
||||||
background: #fffbd3;
|
|
||||||
color: #c20000;
|
|
||||||
}
|
|
||||||
.cucumber ol li.passed, td ol li.passed, th ol li.passed {
|
|
||||||
border-left: 5px solid #65c400;
|
|
||||||
border-bottom: 1px solid #65c400;
|
|
||||||
background: #dbffb4;
|
|
||||||
color: #3d7700;
|
|
||||||
}
|
|
||||||
.cucumber ol li.skipped, td ol li.skipped, th ol li.skipped {
|
|
||||||
border-left: 5px solid aqua;
|
|
||||||
border-bottom: 1px solid aqua;
|
|
||||||
background: #e0ffff;
|
|
||||||
color: #001111;
|
|
||||||
}
|
|
||||||
.cucumber ol li.pending, td ol li.pending, th ol li.pending {
|
|
||||||
border-left: 5px solid #faf834;
|
|
||||||
border-bottom: 1px solid #faf834;
|
|
||||||
background: #fcfb98;
|
|
||||||
color: #131313;
|
|
||||||
}
|
|
||||||
.cucumber ol li.undefined, td ol li.undefined, th ol li.undefined {
|
|
||||||
border-left: 5px solid #faf834;
|
|
||||||
border-bottom: 1px solid #faf834;
|
|
||||||
background: #fcfb98;
|
|
||||||
color: #131313;
|
|
||||||
}
|
|
||||||
.cucumber ol li.message, td ol li.message, th ol li.message {
|
|
||||||
border-left: 5px solid aqua;
|
|
||||||
border-bottom: 1px solid aqua;
|
|
||||||
background: #e0ffff;
|
|
||||||
color: #001111;
|
|
||||||
margin-left: 10px;
|
|
||||||
}
|
|
||||||
.cucumber #summary, td #summary, th #summary {
|
|
||||||
margin: 0px;
|
|
||||||
padding: 5px 10px;
|
|
||||||
text-align: right;
|
|
||||||
top: 0px;
|
|
||||||
right: 0px;
|
|
||||||
float: right;
|
|
||||||
}
|
|
||||||
.cucumber #summary p, td #summary p, th #summary p {
|
|
||||||
margin: 0 0 0 2px;
|
|
||||||
}
|
|
||||||
.cucumber #summary #totals, td #summary #totals, th #summary #totals {
|
|
||||||
font-size: 1.2em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ruby {
|
|
||||||
font-size: 12px;
|
|
||||||
font-family: monospace;
|
|
||||||
color: white;
|
|
||||||
background: black;
|
|
||||||
padding: 0.1em 0 0.2em 0;
|
|
||||||
}
|
|
||||||
.ruby .keyword {
|
|
||||||
color: #ff6600;
|
|
||||||
}
|
|
||||||
.ruby .constant {
|
|
||||||
color: #339999;
|
|
||||||
}
|
|
||||||
.ruby .attribute {
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
.ruby .global {
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
.ruby .module {
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
.ruby .class {
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
.ruby .string {
|
|
||||||
color: #66ff00;
|
|
||||||
}
|
|
||||||
.ruby .ident {
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
.ruby .method {
|
|
||||||
color: #ffcc00;
|
|
||||||
}
|
|
||||||
.ruby .number {
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
.ruby .char {
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
.ruby .comment {
|
|
||||||
color: #9933cc;
|
|
||||||
}
|
|
||||||
.ruby .symbol {
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
.ruby .regex {
|
|
||||||
color: #44b4cc;
|
|
||||||
}
|
|
||||||
.ruby .punct {
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
.ruby .escape {
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
.ruby .interp {
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
.ruby .expr {
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
.ruby .offending {
|
|
||||||
background: #333333;
|
|
||||||
}
|
|
||||||
.ruby .linenum {
|
|
||||||
width: 75px;
|
|
||||||
padding: 0.1em 1em 0.2em 0;
|
|
||||||
color: black;
|
|
||||||
background: #fffbd3;
|
|
||||||
}
|
|
|
@ -1,148 +0,0 @@
|
||||||
require 'cucumber/formatter/ordered_xml_markup'
|
|
||||||
module Hydra #:nodoc:
|
|
||||||
module Listener #:nodoc:
|
|
||||||
# Output a textual report at the end of testing
|
|
||||||
class CucumberHtmlReport < Hydra::Listener::Abstract
|
|
||||||
|
|
||||||
def testing_end
|
|
||||||
CombineHtml.new.generate
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class CombineHtml
|
|
||||||
def initialize(output_file = nil)
|
|
||||||
@results_path = File.join(Dir.pwd, 'results')
|
|
||||||
output_file = File.join(@results_path, 'html/index.html') if output_file.nil?
|
|
||||||
@io = File.open(output_file, "w")
|
|
||||||
@builder = create_builder(@io)
|
|
||||||
end
|
|
||||||
|
|
||||||
def generate
|
|
||||||
before_features
|
|
||||||
combine_features
|
|
||||||
after_features
|
|
||||||
@io.flush
|
|
||||||
@io.close
|
|
||||||
|
|
||||||
|
|
||||||
FileUtils.rm_r File.join(@results_path, 'features')
|
|
||||||
end
|
|
||||||
|
|
||||||
def wait_for_two_seconds_while_files_are_written
|
|
||||||
sleep 2
|
|
||||||
end
|
|
||||||
|
|
||||||
def combine_features
|
|
||||||
wait_for_two_seconds_while_files_are_written
|
|
||||||
Dir.glob(File.join(@results_path, 'features/*.html')).sort.each do |feature|
|
|
||||||
File.open( feature, "rb") do |f|
|
|
||||||
f.each_line do |line|
|
|
||||||
@builder << line
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def before_features
|
|
||||||
# <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
|
||||||
@builder.declare!(
|
|
||||||
:DOCTYPE,
|
|
||||||
:html,
|
|
||||||
:PUBLIC,
|
|
||||||
'-//W3C//DTD XHTML 1.0 Strict//EN',
|
|
||||||
'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd'
|
|
||||||
)
|
|
||||||
|
|
||||||
@builder << '<html xmlns ="http://www.w3.org/1999/xhtml">'
|
|
||||||
@builder.head do
|
|
||||||
@builder.meta(:content => 'text/html;charset=utf-8')
|
|
||||||
@builder.title 'Cucumber'
|
|
||||||
inline_css
|
|
||||||
inline_js
|
|
||||||
end
|
|
||||||
@builder << '<body>'
|
|
||||||
#@builder << "<!-- Step count #{@step_count}-->"
|
|
||||||
@builder << '<div class="cucumber">'
|
|
||||||
@builder.div(:id => 'cucumber-header') do
|
|
||||||
@builder.div(:id => 'label') do
|
|
||||||
@builder.h1('Cucumber Features')
|
|
||||||
end
|
|
||||||
@builder.div(:id => 'summary') do
|
|
||||||
@builder.p('',:id => 'totals')
|
|
||||||
@builder.p('',:id => 'duration')
|
|
||||||
@builder.div(:id => 'expand-collapse') do
|
|
||||||
@builder.p('Expand All', :id => 'expander')
|
|
||||||
@builder.p('Collapse All', :id => 'collapser')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def after_features
|
|
||||||
@builder << '</div>'
|
|
||||||
@builder << '</body>'
|
|
||||||
@builder << '</html>'
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
def inline_css
|
|
||||||
@builder.style(:type => 'text/css') do
|
|
||||||
@builder << File.read(File.dirname(__FILE__) + '/cucumber.css')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def inline_js
|
|
||||||
@builder.script(:type => 'text/javascript') do
|
|
||||||
@builder << inline_jquery
|
|
||||||
@builder << inline_js_content
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def inline_jquery
|
|
||||||
File.read(File.dirname(__FILE__) + '/jquery-min.js')
|
|
||||||
end
|
|
||||||
|
|
||||||
def inline_js_content
|
|
||||||
<<-EOF
|
|
||||||
|
|
||||||
SCENARIOS = "h3[id^='scenario_']";
|
|
||||||
|
|
||||||
$(document).ready(function() {
|
|
||||||
$(SCENARIOS).css('cursor', 'pointer');
|
|
||||||
$(SCENARIOS).click(function() {
|
|
||||||
$(this).siblings().toggle(250);
|
|
||||||
});
|
|
||||||
|
|
||||||
$("#collapser").css('cursor', 'pointer');
|
|
||||||
$("#collapser").click(function() {
|
|
||||||
$(SCENARIOS).siblings().hide();
|
|
||||||
});
|
|
||||||
|
|
||||||
$("#expander").css('cursor', 'pointer');
|
|
||||||
$("#expander").click(function() {
|
|
||||||
$(SCENARIOS).siblings().show();
|
|
||||||
});
|
|
||||||
})
|
|
||||||
|
|
||||||
function moveProgressBar(percentDone) {
|
|
||||||
$("cucumber-header").css('width', percentDone +"%");
|
|
||||||
}
|
|
||||||
function makeRed(element_id) {
|
|
||||||
$('#'+element_id).css('background', '#C40D0D');
|
|
||||||
$('#'+element_id).css('color', '#FFFFFF');
|
|
||||||
}
|
|
||||||
function makeYellow(element_id) {
|
|
||||||
$('#'+element_id).css('background', '#FAF834');
|
|
||||||
$('#'+element_id).css('color', '#000000');
|
|
||||||
}
|
|
||||||
|
|
||||||
EOF
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
def create_builder(io)
|
|
||||||
Cucumber::Formatter::OrderedXmlMarkup.new(:target => io, :indent => 0)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,154 +0,0 @@
|
||||||
/*!
|
|
||||||
* jQuery JavaScript Library v1.4.2
|
|
||||||
* http://jquery.com/
|
|
||||||
*
|
|
||||||
* Copyright 2010, John Resig
|
|
||||||
* Dual licensed under the MIT or GPL Version 2 licenses.
|
|
||||||
* http://jquery.org/license
|
|
||||||
*
|
|
||||||
* Includes Sizzle.js
|
|
||||||
* http://sizzlejs.com/
|
|
||||||
* Copyright 2010, The Dojo Foundation
|
|
||||||
* Released under the MIT, BSD, and GPL Licenses.
|
|
||||||
*
|
|
||||||
* Date: Sat Feb 13 22:33:48 2010 -0500
|
|
||||||
*/
|
|
||||||
(function(A,w){function ma(){if(!c.isReady){try{s.documentElement.doScroll("left")}catch(a){setTimeout(ma,1);return}c.ready()}}function Qa(a,b){b.src?c.ajax({url:b.src,async:false,dataType:"script"}):c.globalEval(b.text||b.textContent||b.innerHTML||"");b.parentNode&&b.parentNode.removeChild(b)}function X(a,b,d,f,e,j){var i=a.length;if(typeof b==="object"){for(var o in b)X(a,o,b[o],f,e,d);return a}if(d!==w){f=!j&&f&&c.isFunction(d);for(o=0;o<i;o++)e(a[o],b,f?d.call(a[o],o,e(a[o],b)):d,j);return a}return i?
|
|
||||||
e(a[0],b):w}function J(){return(new Date).getTime()}function Y(){return false}function Z(){return true}function na(a,b,d){d[0].type=a;return c.event.handle.apply(b,d)}function oa(a){var b,d=[],f=[],e=arguments,j,i,o,k,n,r;i=c.data(this,"events");if(!(a.liveFired===this||!i||!i.live||a.button&&a.type==="click")){a.liveFired=this;var u=i.live.slice(0);for(k=0;k<u.length;k++){i=u[k];i.origType.replace(O,"")===a.type?f.push(i.selector):u.splice(k--,1)}j=c(a.target).closest(f,a.currentTarget);n=0;for(r=
|
|
||||||
j.length;n<r;n++)for(k=0;k<u.length;k++){i=u[k];if(j[n].selector===i.selector){o=j[n].elem;f=null;if(i.preType==="mouseenter"||i.preType==="mouseleave")f=c(a.relatedTarget).closest(i.selector)[0];if(!f||f!==o)d.push({elem:o,handleObj:i})}}n=0;for(r=d.length;n<r;n++){j=d[n];a.currentTarget=j.elem;a.data=j.handleObj.data;a.handleObj=j.handleObj;if(j.handleObj.origHandler.apply(j.elem,e)===false){b=false;break}}return b}}function pa(a,b){return"live."+(a&&a!=="*"?a+".":"")+b.replace(/\./g,"`").replace(/ /g,
|
|
||||||
"&")}function qa(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function ra(a,b){var d=0;b.each(function(){if(this.nodeName===(a[d]&&a[d].nodeName)){var f=c.data(a[d++]),e=c.data(this,f);if(f=f&&f.events){delete e.handle;e.events={};for(var j in f)for(var i in f[j])c.event.add(this,j,f[j][i],f[j][i].data)}}})}function sa(a,b,d){var f,e,j;b=b&&b[0]?b[0].ownerDocument||b[0]:s;if(a.length===1&&typeof a[0]==="string"&&a[0].length<512&&b===s&&!ta.test(a[0])&&(c.support.checkClone||!ua.test(a[0]))){e=
|
|
||||||
true;if(j=c.fragments[a[0]])if(j!==1)f=j}if(!f){f=b.createDocumentFragment();c.clean(a,b,f,d)}if(e)c.fragments[a[0]]=j?f:1;return{fragment:f,cacheable:e}}function K(a,b){var d={};c.each(va.concat.apply([],va.slice(0,b)),function(){d[this]=a});return d}function wa(a){return"scrollTo"in a&&a.document?a:a.nodeType===9?a.defaultView||a.parentWindow:false}var c=function(a,b){return new c.fn.init(a,b)},Ra=A.jQuery,Sa=A.$,s=A.document,T,Ta=/^[^<]*(<[\w\W]+>)[^>]*$|^#([\w-]+)$/,Ua=/^.[^:#\[\.,]*$/,Va=/\S/,
|
|
||||||
Wa=/^(\s|\u00A0)+|(\s|\u00A0)+$/g,Xa=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,P=navigator.userAgent,xa=false,Q=[],L,$=Object.prototype.toString,aa=Object.prototype.hasOwnProperty,ba=Array.prototype.push,R=Array.prototype.slice,ya=Array.prototype.indexOf;c.fn=c.prototype={init:function(a,b){var d,f;if(!a)return this;if(a.nodeType){this.context=this[0]=a;this.length=1;return this}if(a==="body"&&!b){this.context=s;this[0]=s.body;this.selector="body";this.length=1;return this}if(typeof a==="string")if((d=Ta.exec(a))&&
|
|
||||||
(d[1]||!b))if(d[1]){f=b?b.ownerDocument||b:s;if(a=Xa.exec(a))if(c.isPlainObject(b)){a=[s.createElement(a[1])];c.fn.attr.call(a,b,true)}else a=[f.createElement(a[1])];else{a=sa([d[1]],[f]);a=(a.cacheable?a.fragment.cloneNode(true):a.fragment).childNodes}return c.merge(this,a)}else{if(b=s.getElementById(d[2])){if(b.id!==d[2])return T.find(a);this.length=1;this[0]=b}this.context=s;this.selector=a;return this}else if(!b&&/^\w+$/.test(a)){this.selector=a;this.context=s;a=s.getElementsByTagName(a);return c.merge(this,
|
|
||||||
a)}else return!b||b.jquery?(b||T).find(a):c(b).find(a);else if(c.isFunction(a))return T.ready(a);if(a.selector!==w){this.selector=a.selector;this.context=a.context}return c.makeArray(a,this)},selector:"",jquery:"1.4.2",length:0,size:function(){return this.length},toArray:function(){return R.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this.slice(a)[0]:this[a]},pushStack:function(a,b,d){var f=c();c.isArray(a)?ba.apply(f,a):c.merge(f,a);f.prevObject=this;f.context=this.context;if(b===
|
|
||||||
"find")f.selector=this.selector+(this.selector?" ":"")+d;else if(b)f.selector=this.selector+"."+b+"("+d+")";return f},each:function(a,b){return c.each(this,a,b)},ready:function(a){c.bindReady();if(c.isReady)a.call(s,c);else Q&&Q.push(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(R.apply(this,arguments),"slice",R.call(arguments).join(","))},map:function(a){return this.pushStack(c.map(this,
|
|
||||||
function(b,d){return a.call(b,d,b)}))},end:function(){return this.prevObject||c(null)},push:ba,sort:[].sort,splice:[].splice};c.fn.init.prototype=c.fn;c.extend=c.fn.extend=function(){var a=arguments[0]||{},b=1,d=arguments.length,f=false,e,j,i,o;if(typeof a==="boolean"){f=a;a=arguments[1]||{};b=2}if(typeof a!=="object"&&!c.isFunction(a))a={};if(d===b){a=this;--b}for(;b<d;b++)if((e=arguments[b])!=null)for(j in e){i=a[j];o=e[j];if(a!==o)if(f&&o&&(c.isPlainObject(o)||c.isArray(o))){i=i&&(c.isPlainObject(i)||
|
|
||||||
c.isArray(i))?i:c.isArray(o)?[]:{};a[j]=c.extend(f,i,o)}else if(o!==w)a[j]=o}return a};c.extend({noConflict:function(a){A.$=Sa;if(a)A.jQuery=Ra;return c},isReady:false,ready:function(){if(!c.isReady){if(!s.body)return setTimeout(c.ready,13);c.isReady=true;if(Q){for(var a,b=0;a=Q[b++];)a.call(s,c);Q=null}c.fn.triggerHandler&&c(s).triggerHandler("ready")}},bindReady:function(){if(!xa){xa=true;if(s.readyState==="complete")return c.ready();if(s.addEventListener){s.addEventListener("DOMContentLoaded",
|
|
||||||
L,false);A.addEventListener("load",c.ready,false)}else if(s.attachEvent){s.attachEvent("onreadystatechange",L);A.attachEvent("onload",c.ready);var a=false;try{a=A.frameElement==null}catch(b){}s.documentElement.doScroll&&a&&ma()}}},isFunction:function(a){return $.call(a)==="[object Function]"},isArray:function(a){return $.call(a)==="[object Array]"},isPlainObject:function(a){if(!a||$.call(a)!=="[object Object]"||a.nodeType||a.setInterval)return false;if(a.constructor&&!aa.call(a,"constructor")&&!aa.call(a.constructor.prototype,
|
|
||||||
"isPrototypeOf"))return false;var b;for(b in a);return b===w||aa.call(a,b)},isEmptyObject:function(a){for(var b in a)return false;return true},error:function(a){throw a;},parseJSON:function(a){if(typeof a!=="string"||!a)return null;a=c.trim(a);if(/^[\],:{}\s]*$/.test(a.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,"")))return A.JSON&&A.JSON.parse?A.JSON.parse(a):(new Function("return "+
|
|
||||||
a))();else c.error("Invalid JSON: "+a)},noop:function(){},globalEval:function(a){if(a&&Va.test(a)){var b=s.getElementsByTagName("head")[0]||s.documentElement,d=s.createElement("script");d.type="text/javascript";if(c.support.scriptEval)d.appendChild(s.createTextNode(a));else d.text=a;b.insertBefore(d,b.firstChild);b.removeChild(d)}},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,b,d){var f,e=0,j=a.length,i=j===w||c.isFunction(a);if(d)if(i)for(f in a){if(b.apply(a[f],
|
|
||||||
d)===false)break}else for(;e<j;){if(b.apply(a[e++],d)===false)break}else if(i)for(f in a){if(b.call(a[f],f,a[f])===false)break}else for(d=a[0];e<j&&b.call(d,e,d)!==false;d=a[++e]);return a},trim:function(a){return(a||"").replace(Wa,"")},makeArray:function(a,b){b=b||[];if(a!=null)a.length==null||typeof a==="string"||c.isFunction(a)||typeof a!=="function"&&a.setInterval?ba.call(b,a):c.merge(b,a);return b},inArray:function(a,b){if(b.indexOf)return b.indexOf(a);for(var d=0,f=b.length;d<f;d++)if(b[d]===
|
|
||||||
a)return d;return-1},merge:function(a,b){var d=a.length,f=0;if(typeof b.length==="number")for(var e=b.length;f<e;f++)a[d++]=b[f];else for(;b[f]!==w;)a[d++]=b[f++];a.length=d;return a},grep:function(a,b,d){for(var f=[],e=0,j=a.length;e<j;e++)!d!==!b(a[e],e)&&f.push(a[e]);return f},map:function(a,b,d){for(var f=[],e,j=0,i=a.length;j<i;j++){e=b(a[j],j,d);if(e!=null)f[f.length]=e}return f.concat.apply([],f)},guid:1,proxy:function(a,b,d){if(arguments.length===2)if(typeof b==="string"){d=a;a=d[b];b=w}else if(b&&
|
|
||||||
!c.isFunction(b)){d=b;b=w}if(!b&&a)b=function(){return a.apply(d||this,arguments)};if(a)b.guid=a.guid=a.guid||b.guid||c.guid++;return b},uaMatch:function(a){a=a.toLowerCase();a=/(webkit)[ \/]([\w.]+)/.exec(a)||/(opera)(?:.*version)?[ \/]([\w.]+)/.exec(a)||/(msie) ([\w.]+)/.exec(a)||!/compatible/.test(a)&&/(mozilla)(?:.*? rv:([\w.]+))?/.exec(a)||[];return{browser:a[1]||"",version:a[2]||"0"}},browser:{}});P=c.uaMatch(P);if(P.browser){c.browser[P.browser]=true;c.browser.version=P.version}if(c.browser.webkit)c.browser.safari=
|
|
||||||
true;if(ya)c.inArray=function(a,b){return ya.call(b,a)};T=c(s);if(s.addEventListener)L=function(){s.removeEventListener("DOMContentLoaded",L,false);c.ready()};else if(s.attachEvent)L=function(){if(s.readyState==="complete"){s.detachEvent("onreadystatechange",L);c.ready()}};(function(){c.support={};var a=s.documentElement,b=s.createElement("script"),d=s.createElement("div"),f="script"+J();d.style.display="none";d.innerHTML=" <link/><table></table><a href='/a' style='color:red;float:left;opacity:.55;'>a</a><input type='checkbox'/>";
|
|
||||||
var e=d.getElementsByTagName("*"),j=d.getElementsByTagName("a")[0];if(!(!e||!e.length||!j)){c.support={leadingWhitespace:d.firstChild.nodeType===3,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/red/.test(j.getAttribute("style")),hrefNormalized:j.getAttribute("href")==="/a",opacity:/^0.55$/.test(j.style.opacity),cssFloat:!!j.style.cssFloat,checkOn:d.getElementsByTagName("input")[0].value==="on",optSelected:s.createElement("select").appendChild(s.createElement("option")).selected,
|
|
||||||
parentNode:d.removeChild(d.appendChild(s.createElement("div"))).parentNode===null,deleteExpando:true,checkClone:false,scriptEval:false,noCloneEvent:true,boxModel:null};b.type="text/javascript";try{b.appendChild(s.createTextNode("window."+f+"=1;"))}catch(i){}a.insertBefore(b,a.firstChild);if(A[f]){c.support.scriptEval=true;delete A[f]}try{delete b.test}catch(o){c.support.deleteExpando=false}a.removeChild(b);if(d.attachEvent&&d.fireEvent){d.attachEvent("onclick",function k(){c.support.noCloneEvent=
|
|
||||||
false;d.detachEvent("onclick",k)});d.cloneNode(true).fireEvent("onclick")}d=s.createElement("div");d.innerHTML="<input type='radio' name='radiotest' checked='checked'/>";a=s.createDocumentFragment();a.appendChild(d.firstChild);c.support.checkClone=a.cloneNode(true).cloneNode(true).lastChild.checked;c(function(){var k=s.createElement("div");k.style.width=k.style.paddingLeft="1px";s.body.appendChild(k);c.boxModel=c.support.boxModel=k.offsetWidth===2;s.body.removeChild(k).style.display="none"});a=function(k){var n=
|
|
||||||
s.createElement("div");k="on"+k;var r=k in n;if(!r){n.setAttribute(k,"return;");r=typeof n[k]==="function"}return r};c.support.submitBubbles=a("submit");c.support.changeBubbles=a("change");a=b=d=e=j=null}})();c.props={"for":"htmlFor","class":"className",readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",colspan:"colSpan",tabindex:"tabIndex",usemap:"useMap",frameborder:"frameBorder"};var G="jQuery"+J(),Ya=0,za={};c.extend({cache:{},expando:G,noData:{embed:true,object:true,
|
|
||||||
applet:true},data:function(a,b,d){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var f=a[G],e=c.cache;if(!f&&typeof b==="string"&&d===w)return null;f||(f=++Ya);if(typeof b==="object"){a[G]=f;e[f]=c.extend(true,{},b)}else if(!e[f]){a[G]=f;e[f]={}}a=e[f];if(d!==w)a[b]=d;return typeof b==="string"?a[b]:a}},removeData:function(a,b){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var d=a[G],f=c.cache,e=f[d];if(b){if(e){delete e[b];c.isEmptyObject(e)&&c.removeData(a)}}else{if(c.support.deleteExpando)delete a[c.expando];
|
|
||||||
else a.removeAttribute&&a.removeAttribute(c.expando);delete f[d]}}}});c.fn.extend({data:function(a,b){if(typeof a==="undefined"&&this.length)return c.data(this[0]);else if(typeof a==="object")return this.each(function(){c.data(this,a)});var d=a.split(".");d[1]=d[1]?"."+d[1]:"";if(b===w){var f=this.triggerHandler("getData"+d[1]+"!",[d[0]]);if(f===w&&this.length)f=c.data(this[0],a);return f===w&&d[1]?this.data(d[0]):f}else return this.trigger("setData"+d[1]+"!",[d[0],b]).each(function(){c.data(this,
|
|
||||||
a,b)})},removeData:function(a){return this.each(function(){c.removeData(this,a)})}});c.extend({queue:function(a,b,d){if(a){b=(b||"fx")+"queue";var f=c.data(a,b);if(!d)return f||[];if(!f||c.isArray(d))f=c.data(a,b,c.makeArray(d));else f.push(d);return f}},dequeue:function(a,b){b=b||"fx";var d=c.queue(a,b),f=d.shift();if(f==="inprogress")f=d.shift();if(f){b==="fx"&&d.unshift("inprogress");f.call(a,function(){c.dequeue(a,b)})}}});c.fn.extend({queue:function(a,b){if(typeof a!=="string"){b=a;a="fx"}if(b===
|
|
||||||
w)return c.queue(this[0],a);return this.each(function(){var d=c.queue(this,a,b);a==="fx"&&d[0]!=="inprogress"&&c.dequeue(this,a)})},dequeue:function(a){return this.each(function(){c.dequeue(this,a)})},delay:function(a,b){a=c.fx?c.fx.speeds[a]||a:a;b=b||"fx";return this.queue(b,function(){var d=this;setTimeout(function(){c.dequeue(d,b)},a)})},clearQueue:function(a){return this.queue(a||"fx",[])}});var Aa=/[\n\t]/g,ca=/\s+/,Za=/\r/g,$a=/href|src|style/,ab=/(button|input)/i,bb=/(button|input|object|select|textarea)/i,
|
|
||||||
cb=/^(a|area)$/i,Ba=/radio|checkbox/;c.fn.extend({attr:function(a,b){return X(this,a,b,true,c.attr)},removeAttr:function(a){return this.each(function(){c.attr(this,a,"");this.nodeType===1&&this.removeAttribute(a)})},addClass:function(a){if(c.isFunction(a))return this.each(function(n){var r=c(this);r.addClass(a.call(this,n,r.attr("class")))});if(a&&typeof a==="string")for(var b=(a||"").split(ca),d=0,f=this.length;d<f;d++){var e=this[d];if(e.nodeType===1)if(e.className){for(var j=" "+e.className+" ",
|
|
||||||
i=e.className,o=0,k=b.length;o<k;o++)if(j.indexOf(" "+b[o]+" ")<0)i+=" "+b[o];e.className=c.trim(i)}else e.className=a}return this},removeClass:function(a){if(c.isFunction(a))return this.each(function(k){var n=c(this);n.removeClass(a.call(this,k,n.attr("class")))});if(a&&typeof a==="string"||a===w)for(var b=(a||"").split(ca),d=0,f=this.length;d<f;d++){var e=this[d];if(e.nodeType===1&&e.className)if(a){for(var j=(" "+e.className+" ").replace(Aa," "),i=0,o=b.length;i<o;i++)j=j.replace(" "+b[i]+" ",
|
|
||||||
" ");e.className=c.trim(j)}else e.className=""}return this},toggleClass:function(a,b){var d=typeof a,f=typeof b==="boolean";if(c.isFunction(a))return this.each(function(e){var j=c(this);j.toggleClass(a.call(this,e,j.attr("class"),b),b)});return this.each(function(){if(d==="string")for(var e,j=0,i=c(this),o=b,k=a.split(ca);e=k[j++];){o=f?o:!i.hasClass(e);i[o?"addClass":"removeClass"](e)}else if(d==="undefined"||d==="boolean"){this.className&&c.data(this,"__className__",this.className);this.className=
|
|
||||||
this.className||a===false?"":c.data(this,"__className__")||""}})},hasClass:function(a){a=" "+a+" ";for(var b=0,d=this.length;b<d;b++)if((" "+this[b].className+" ").replace(Aa," ").indexOf(a)>-1)return true;return false},val:function(a){if(a===w){var b=this[0];if(b){if(c.nodeName(b,"option"))return(b.attributes.value||{}).specified?b.value:b.text;if(c.nodeName(b,"select")){var d=b.selectedIndex,f=[],e=b.options;b=b.type==="select-one";if(d<0)return null;var j=b?d:0;for(d=b?d+1:e.length;j<d;j++){var i=
|
|
||||||
e[j];if(i.selected){a=c(i).val();if(b)return a;f.push(a)}}return f}if(Ba.test(b.type)&&!c.support.checkOn)return b.getAttribute("value")===null?"on":b.value;return(b.value||"").replace(Za,"")}return w}var o=c.isFunction(a);return this.each(function(k){var n=c(this),r=a;if(this.nodeType===1){if(o)r=a.call(this,k,n.val());if(typeof r==="number")r+="";if(c.isArray(r)&&Ba.test(this.type))this.checked=c.inArray(n.val(),r)>=0;else if(c.nodeName(this,"select")){var u=c.makeArray(r);c("option",this).each(function(){this.selected=
|
|
||||||
c.inArray(c(this).val(),u)>=0});if(!u.length)this.selectedIndex=-1}else this.value=r}})}});c.extend({attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true},attr:function(a,b,d,f){if(!a||a.nodeType===3||a.nodeType===8)return w;if(f&&b in c.attrFn)return c(a)[b](d);f=a.nodeType!==1||!c.isXMLDoc(a);var e=d!==w;b=f&&c.props[b]||b;if(a.nodeType===1){var j=$a.test(b);if(b in a&&f&&!j){if(e){b==="type"&&ab.test(a.nodeName)&&a.parentNode&&c.error("type property can't be changed");
|
|
||||||
a[b]=d}if(c.nodeName(a,"form")&&a.getAttributeNode(b))return a.getAttributeNode(b).nodeValue;if(b==="tabIndex")return(b=a.getAttributeNode("tabIndex"))&&b.specified?b.value:bb.test(a.nodeName)||cb.test(a.nodeName)&&a.href?0:w;return a[b]}if(!c.support.style&&f&&b==="style"){if(e)a.style.cssText=""+d;return a.style.cssText}e&&a.setAttribute(b,""+d);a=!c.support.hrefNormalized&&f&&j?a.getAttribute(b,2):a.getAttribute(b);return a===null?w:a}return c.style(a,b,d)}});var O=/\.(.*)$/,db=function(a){return a.replace(/[^\w\s\.\|`]/g,
|
|
||||||
function(b){return"\\"+b})};c.event={add:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){if(a.setInterval&&a!==A&&!a.frameElement)a=A;var e,j;if(d.handler){e=d;d=e.handler}if(!d.guid)d.guid=c.guid++;if(j=c.data(a)){var i=j.events=j.events||{},o=j.handle;if(!o)j.handle=o=function(){return typeof c!=="undefined"&&!c.event.triggered?c.event.handle.apply(o.elem,arguments):w};o.elem=a;b=b.split(" ");for(var k,n=0,r;k=b[n++];){j=e?c.extend({},e):{handler:d,data:f};if(k.indexOf(".")>-1){r=k.split(".");
|
|
||||||
k=r.shift();j.namespace=r.slice(0).sort().join(".")}else{r=[];j.namespace=""}j.type=k;j.guid=d.guid;var u=i[k],z=c.event.special[k]||{};if(!u){u=i[k]=[];if(!z.setup||z.setup.call(a,f,r,o)===false)if(a.addEventListener)a.addEventListener(k,o,false);else a.attachEvent&&a.attachEvent("on"+k,o)}if(z.add){z.add.call(a,j);if(!j.handler.guid)j.handler.guid=d.guid}u.push(j);c.event.global[k]=true}a=null}}},global:{},remove:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){var e,j=0,i,o,k,n,r,u,z=c.data(a),
|
|
||||||
C=z&&z.events;if(z&&C){if(b&&b.type){d=b.handler;b=b.type}if(!b||typeof b==="string"&&b.charAt(0)==="."){b=b||"";for(e in C)c.event.remove(a,e+b)}else{for(b=b.split(" ");e=b[j++];){n=e;i=e.indexOf(".")<0;o=[];if(!i){o=e.split(".");e=o.shift();k=new RegExp("(^|\\.)"+c.map(o.slice(0).sort(),db).join("\\.(?:.*\\.)?")+"(\\.|$)")}if(r=C[e])if(d){n=c.event.special[e]||{};for(B=f||0;B<r.length;B++){u=r[B];if(d.guid===u.guid){if(i||k.test(u.namespace)){f==null&&r.splice(B--,1);n.remove&&n.remove.call(a,u)}if(f!=
|
|
||||||
null)break}}if(r.length===0||f!=null&&r.length===1){if(!n.teardown||n.teardown.call(a,o)===false)Ca(a,e,z.handle);delete C[e]}}else for(var B=0;B<r.length;B++){u=r[B];if(i||k.test(u.namespace)){c.event.remove(a,n,u.handler,B);r.splice(B--,1)}}}if(c.isEmptyObject(C)){if(b=z.handle)b.elem=null;delete z.events;delete z.handle;c.isEmptyObject(z)&&c.removeData(a)}}}}},trigger:function(a,b,d,f){var e=a.type||a;if(!f){a=typeof a==="object"?a[G]?a:c.extend(c.Event(e),a):c.Event(e);if(e.indexOf("!")>=0){a.type=
|
|
||||||
e=e.slice(0,-1);a.exclusive=true}if(!d){a.stopPropagation();c.event.global[e]&&c.each(c.cache,function(){this.events&&this.events[e]&&c.event.trigger(a,b,this.handle.elem)})}if(!d||d.nodeType===3||d.nodeType===8)return w;a.result=w;a.target=d;b=c.makeArray(b);b.unshift(a)}a.currentTarget=d;(f=c.data(d,"handle"))&&f.apply(d,b);f=d.parentNode||d.ownerDocument;try{if(!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()]))if(d["on"+e]&&d["on"+e].apply(d,b)===false)a.result=false}catch(j){}if(!a.isPropagationStopped()&&
|
|
||||||
f)c.event.trigger(a,b,f,true);else if(!a.isDefaultPrevented()){f=a.target;var i,o=c.nodeName(f,"a")&&e==="click",k=c.event.special[e]||{};if((!k._default||k._default.call(d,a)===false)&&!o&&!(f&&f.nodeName&&c.noData[f.nodeName.toLowerCase()])){try{if(f[e]){if(i=f["on"+e])f["on"+e]=null;c.event.triggered=true;f[e]()}}catch(n){}if(i)f["on"+e]=i;c.event.triggered=false}}},handle:function(a){var b,d,f,e;a=arguments[0]=c.event.fix(a||A.event);a.currentTarget=this;b=a.type.indexOf(".")<0&&!a.exclusive;
|
|
||||||
if(!b){d=a.type.split(".");a.type=d.shift();f=new RegExp("(^|\\.)"+d.slice(0).sort().join("\\.(?:.*\\.)?")+"(\\.|$)")}e=c.data(this,"events");d=e[a.type];if(e&&d){d=d.slice(0);e=0;for(var j=d.length;e<j;e++){var i=d[e];if(b||f.test(i.namespace)){a.handler=i.handler;a.data=i.data;a.handleObj=i;i=i.handler.apply(this,arguments);if(i!==w){a.result=i;if(i===false){a.preventDefault();a.stopPropagation()}}if(a.isImmediatePropagationStopped())break}}}return a.result},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),
|
|
||||||
fix:function(a){if(a[G])return a;var b=a;a=c.Event(b);for(var d=this.props.length,f;d;){f=this.props[--d];a[f]=b[f]}if(!a.target)a.target=a.srcElement||s;if(a.target.nodeType===3)a.target=a.target.parentNode;if(!a.relatedTarget&&a.fromElement)a.relatedTarget=a.fromElement===a.target?a.toElement:a.fromElement;if(a.pageX==null&&a.clientX!=null){b=s.documentElement;d=s.body;a.pageX=a.clientX+(b&&b.scrollLeft||d&&d.scrollLeft||0)-(b&&b.clientLeft||d&&d.clientLeft||0);a.pageY=a.clientY+(b&&b.scrollTop||
|
|
||||||
d&&d.scrollTop||0)-(b&&b.clientTop||d&&d.clientTop||0)}if(!a.which&&(a.charCode||a.charCode===0?a.charCode:a.keyCode))a.which=a.charCode||a.keyCode;if(!a.metaKey&&a.ctrlKey)a.metaKey=a.ctrlKey;if(!a.which&&a.button!==w)a.which=a.button&1?1:a.button&2?3:a.button&4?2:0;return a},guid:1E8,proxy:c.proxy,special:{ready:{setup:c.bindReady,teardown:c.noop},live:{add:function(a){c.event.add(this,a.origType,c.extend({},a,{handler:oa}))},remove:function(a){var b=true,d=a.origType.replace(O,"");c.each(c.data(this,
|
|
||||||
"events").live||[],function(){if(d===this.origType.replace(O,""))return b=false});b&&c.event.remove(this,a.origType,oa)}},beforeunload:{setup:function(a,b,d){if(this.setInterval)this.onbeforeunload=d;return false},teardown:function(a,b){if(this.onbeforeunload===b)this.onbeforeunload=null}}}};var Ca=s.removeEventListener?function(a,b,d){a.removeEventListener(b,d,false)}:function(a,b,d){a.detachEvent("on"+b,d)};c.Event=function(a){if(!this.preventDefault)return new c.Event(a);if(a&&a.type){this.originalEvent=
|
|
||||||
a;this.type=a.type}else this.type=a;this.timeStamp=J();this[G]=true};c.Event.prototype={preventDefault:function(){this.isDefaultPrevented=Z;var a=this.originalEvent;if(a){a.preventDefault&&a.preventDefault();a.returnValue=false}},stopPropagation:function(){this.isPropagationStopped=Z;var a=this.originalEvent;if(a){a.stopPropagation&&a.stopPropagation();a.cancelBubble=true}},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=Z;this.stopPropagation()},isDefaultPrevented:Y,isPropagationStopped:Y,
|
|
||||||
isImmediatePropagationStopped:Y};var Da=function(a){var b=a.relatedTarget;try{for(;b&&b!==this;)b=b.parentNode;if(b!==this){a.type=a.data;c.event.handle.apply(this,arguments)}}catch(d){}},Ea=function(a){a.type=a.data;c.event.handle.apply(this,arguments)};c.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){c.event.special[a]={setup:function(d){c.event.add(this,b,d&&d.selector?Ea:Da,a)},teardown:function(d){c.event.remove(this,b,d&&d.selector?Ea:Da)}}});if(!c.support.submitBubbles)c.event.special.submit=
|
|
||||||
{setup:function(){if(this.nodeName.toLowerCase()!=="form"){c.event.add(this,"click.specialSubmit",function(a){var b=a.target,d=b.type;if((d==="submit"||d==="image")&&c(b).closest("form").length)return na("submit",this,arguments)});c.event.add(this,"keypress.specialSubmit",function(a){var b=a.target,d=b.type;if((d==="text"||d==="password")&&c(b).closest("form").length&&a.keyCode===13)return na("submit",this,arguments)})}else return false},teardown:function(){c.event.remove(this,".specialSubmit")}};
|
|
||||||
if(!c.support.changeBubbles){var da=/textarea|input|select/i,ea,Fa=function(a){var b=a.type,d=a.value;if(b==="radio"||b==="checkbox")d=a.checked;else if(b==="select-multiple")d=a.selectedIndex>-1?c.map(a.options,function(f){return f.selected}).join("-"):"";else if(a.nodeName.toLowerCase()==="select")d=a.selectedIndex;return d},fa=function(a,b){var d=a.target,f,e;if(!(!da.test(d.nodeName)||d.readOnly)){f=c.data(d,"_change_data");e=Fa(d);if(a.type!=="focusout"||d.type!=="radio")c.data(d,"_change_data",
|
|
||||||
e);if(!(f===w||e===f))if(f!=null||e){a.type="change";return c.event.trigger(a,b,d)}}};c.event.special.change={filters:{focusout:fa,click:function(a){var b=a.target,d=b.type;if(d==="radio"||d==="checkbox"||b.nodeName.toLowerCase()==="select")return fa.call(this,a)},keydown:function(a){var b=a.target,d=b.type;if(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(d==="checkbox"||d==="radio")||d==="select-multiple")return fa.call(this,a)},beforeactivate:function(a){a=a.target;c.data(a,
|
|
||||||
"_change_data",Fa(a))}},setup:function(){if(this.type==="file")return false;for(var a in ea)c.event.add(this,a+".specialChange",ea[a]);return da.test(this.nodeName)},teardown:function(){c.event.remove(this,".specialChange");return da.test(this.nodeName)}};ea=c.event.special.change.filters}s.addEventListener&&c.each({focus:"focusin",blur:"focusout"},function(a,b){function d(f){f=c.event.fix(f);f.type=b;return c.event.handle.call(this,f)}c.event.special[b]={setup:function(){this.addEventListener(a,
|
|
||||||
d,true)},teardown:function(){this.removeEventListener(a,d,true)}}});c.each(["bind","one"],function(a,b){c.fn[b]=function(d,f,e){if(typeof d==="object"){for(var j in d)this[b](j,f,d[j],e);return this}if(c.isFunction(f)){e=f;f=w}var i=b==="one"?c.proxy(e,function(k){c(this).unbind(k,i);return e.apply(this,arguments)}):e;if(d==="unload"&&b!=="one")this.one(d,f,e);else{j=0;for(var o=this.length;j<o;j++)c.event.add(this[j],d,i,f)}return this}});c.fn.extend({unbind:function(a,b){if(typeof a==="object"&&
|
|
||||||
!a.preventDefault)for(var d in a)this.unbind(d,a[d]);else{d=0;for(var f=this.length;d<f;d++)c.event.remove(this[d],a,b)}return this},delegate:function(a,b,d,f){return this.live(b,d,f,a)},undelegate:function(a,b,d){return arguments.length===0?this.unbind("live"):this.die(b,null,d,a)},trigger:function(a,b){return this.each(function(){c.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0]){a=c.Event(a);a.preventDefault();a.stopPropagation();c.event.trigger(a,b,this[0]);return a.result}},
|
|
||||||
toggle:function(a){for(var b=arguments,d=1;d<b.length;)c.proxy(a,b[d++]);return this.click(c.proxy(a,function(f){var e=(c.data(this,"lastToggle"+a.guid)||0)%d;c.data(this,"lastToggle"+a.guid,e+1);f.preventDefault();return b[e].apply(this,arguments)||false}))},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}});var Ga={focus:"focusin",blur:"focusout",mouseenter:"mouseover",mouseleave:"mouseout"};c.each(["live","die"],function(a,b){c.fn[b]=function(d,f,e,j){var i,o=0,k,n,r=j||this.selector,
|
|
||||||
u=j?this:c(this.context);if(c.isFunction(f)){e=f;f=w}for(d=(d||"").split(" ");(i=d[o++])!=null;){j=O.exec(i);k="";if(j){k=j[0];i=i.replace(O,"")}if(i==="hover")d.push("mouseenter"+k,"mouseleave"+k);else{n=i;if(i==="focus"||i==="blur"){d.push(Ga[i]+k);i+=k}else i=(Ga[i]||i)+k;b==="live"?u.each(function(){c.event.add(this,pa(i,r),{data:f,selector:r,handler:e,origType:i,origHandler:e,preType:n})}):u.unbind(pa(i,r),e)}}return this}});c.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error".split(" "),
|
|
||||||
function(a,b){c.fn[b]=function(d){return d?this.bind(b,d):this.trigger(b)};if(c.attrFn)c.attrFn[b]=true});A.attachEvent&&!A.addEventListener&&A.attachEvent("onunload",function(){for(var a in c.cache)if(c.cache[a].handle)try{c.event.remove(c.cache[a].handle.elem)}catch(b){}});(function(){function a(g){for(var h="",l,m=0;g[m];m++){l=g[m];if(l.nodeType===3||l.nodeType===4)h+=l.nodeValue;else if(l.nodeType!==8)h+=a(l.childNodes)}return h}function b(g,h,l,m,q,p){q=0;for(var v=m.length;q<v;q++){var t=m[q];
|
|
||||||
if(t){t=t[g];for(var y=false;t;){if(t.sizcache===l){y=m[t.sizset];break}if(t.nodeType===1&&!p){t.sizcache=l;t.sizset=q}if(t.nodeName.toLowerCase()===h){y=t;break}t=t[g]}m[q]=y}}}function d(g,h,l,m,q,p){q=0;for(var v=m.length;q<v;q++){var t=m[q];if(t){t=t[g];for(var y=false;t;){if(t.sizcache===l){y=m[t.sizset];break}if(t.nodeType===1){if(!p){t.sizcache=l;t.sizset=q}if(typeof h!=="string"){if(t===h){y=true;break}}else if(k.filter(h,[t]).length>0){y=t;break}}t=t[g]}m[q]=y}}}var f=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
|
|
||||||
e=0,j=Object.prototype.toString,i=false,o=true;[0,0].sort(function(){o=false;return 0});var k=function(g,h,l,m){l=l||[];var q=h=h||s;if(h.nodeType!==1&&h.nodeType!==9)return[];if(!g||typeof g!=="string")return l;for(var p=[],v,t,y,S,H=true,M=x(h),I=g;(f.exec(""),v=f.exec(I))!==null;){I=v[3];p.push(v[1]);if(v[2]){S=v[3];break}}if(p.length>1&&r.exec(g))if(p.length===2&&n.relative[p[0]])t=ga(p[0]+p[1],h);else for(t=n.relative[p[0]]?[h]:k(p.shift(),h);p.length;){g=p.shift();if(n.relative[g])g+=p.shift();
|
|
||||||
t=ga(g,t)}else{if(!m&&p.length>1&&h.nodeType===9&&!M&&n.match.ID.test(p[0])&&!n.match.ID.test(p[p.length-1])){v=k.find(p.shift(),h,M);h=v.expr?k.filter(v.expr,v.set)[0]:v.set[0]}if(h){v=m?{expr:p.pop(),set:z(m)}:k.find(p.pop(),p.length===1&&(p[0]==="~"||p[0]==="+")&&h.parentNode?h.parentNode:h,M);t=v.expr?k.filter(v.expr,v.set):v.set;if(p.length>0)y=z(t);else H=false;for(;p.length;){var D=p.pop();v=D;if(n.relative[D])v=p.pop();else D="";if(v==null)v=h;n.relative[D](y,v,M)}}else y=[]}y||(y=t);y||k.error(D||
|
|
||||||
g);if(j.call(y)==="[object Array]")if(H)if(h&&h.nodeType===1)for(g=0;y[g]!=null;g++){if(y[g]&&(y[g]===true||y[g].nodeType===1&&E(h,y[g])))l.push(t[g])}else for(g=0;y[g]!=null;g++)y[g]&&y[g].nodeType===1&&l.push(t[g]);else l.push.apply(l,y);else z(y,l);if(S){k(S,q,l,m);k.uniqueSort(l)}return l};k.uniqueSort=function(g){if(B){i=o;g.sort(B);if(i)for(var h=1;h<g.length;h++)g[h]===g[h-1]&&g.splice(h--,1)}return g};k.matches=function(g,h){return k(g,null,null,h)};k.find=function(g,h,l){var m,q;if(!g)return[];
|
|
||||||
for(var p=0,v=n.order.length;p<v;p++){var t=n.order[p];if(q=n.leftMatch[t].exec(g)){var y=q[1];q.splice(1,1);if(y.substr(y.length-1)!=="\\"){q[1]=(q[1]||"").replace(/\\/g,"");m=n.find[t](q,h,l);if(m!=null){g=g.replace(n.match[t],"");break}}}}m||(m=h.getElementsByTagName("*"));return{set:m,expr:g}};k.filter=function(g,h,l,m){for(var q=g,p=[],v=h,t,y,S=h&&h[0]&&x(h[0]);g&&h.length;){for(var H in n.filter)if((t=n.leftMatch[H].exec(g))!=null&&t[2]){var M=n.filter[H],I,D;D=t[1];y=false;t.splice(1,1);if(D.substr(D.length-
|
|
||||||
1)!=="\\"){if(v===p)p=[];if(n.preFilter[H])if(t=n.preFilter[H](t,v,l,p,m,S)){if(t===true)continue}else y=I=true;if(t)for(var U=0;(D=v[U])!=null;U++)if(D){I=M(D,t,U,v);var Ha=m^!!I;if(l&&I!=null)if(Ha)y=true;else v[U]=false;else if(Ha){p.push(D);y=true}}if(I!==w){l||(v=p);g=g.replace(n.match[H],"");if(!y)return[];break}}}if(g===q)if(y==null)k.error(g);else break;q=g}return v};k.error=function(g){throw"Syntax error, unrecognized expression: "+g;};var n=k.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF-]|\\.)+)/,
|
|
||||||
CLASS:/\.((?:[\w\u00c0-\uFFFF-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(g){return g.getAttribute("href")}},
|
|
||||||
relative:{"+":function(g,h){var l=typeof h==="string",m=l&&!/\W/.test(h);l=l&&!m;if(m)h=h.toLowerCase();m=0;for(var q=g.length,p;m<q;m++)if(p=g[m]){for(;(p=p.previousSibling)&&p.nodeType!==1;);g[m]=l||p&&p.nodeName.toLowerCase()===h?p||false:p===h}l&&k.filter(h,g,true)},">":function(g,h){var l=typeof h==="string";if(l&&!/\W/.test(h)){h=h.toLowerCase();for(var m=0,q=g.length;m<q;m++){var p=g[m];if(p){l=p.parentNode;g[m]=l.nodeName.toLowerCase()===h?l:false}}}else{m=0;for(q=g.length;m<q;m++)if(p=g[m])g[m]=
|
|
||||||
l?p.parentNode:p.parentNode===h;l&&k.filter(h,g,true)}},"":function(g,h,l){var m=e++,q=d;if(typeof h==="string"&&!/\W/.test(h)){var p=h=h.toLowerCase();q=b}q("parentNode",h,m,g,p,l)},"~":function(g,h,l){var m=e++,q=d;if(typeof h==="string"&&!/\W/.test(h)){var p=h=h.toLowerCase();q=b}q("previousSibling",h,m,g,p,l)}},find:{ID:function(g,h,l){if(typeof h.getElementById!=="undefined"&&!l)return(g=h.getElementById(g[1]))?[g]:[]},NAME:function(g,h){if(typeof h.getElementsByName!=="undefined"){var l=[];
|
|
||||||
h=h.getElementsByName(g[1]);for(var m=0,q=h.length;m<q;m++)h[m].getAttribute("name")===g[1]&&l.push(h[m]);return l.length===0?null:l}},TAG:function(g,h){return h.getElementsByTagName(g[1])}},preFilter:{CLASS:function(g,h,l,m,q,p){g=" "+g[1].replace(/\\/g,"")+" ";if(p)return g;p=0;for(var v;(v=h[p])!=null;p++)if(v)if(q^(v.className&&(" "+v.className+" ").replace(/[\t\n]/g," ").indexOf(g)>=0))l||m.push(v);else if(l)h[p]=false;return false},ID:function(g){return g[1].replace(/\\/g,"")},TAG:function(g){return g[1].toLowerCase()},
|
|
||||||
CHILD:function(g){if(g[1]==="nth"){var h=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(g[2]==="even"&&"2n"||g[2]==="odd"&&"2n+1"||!/\D/.test(g[2])&&"0n+"+g[2]||g[2]);g[2]=h[1]+(h[2]||1)-0;g[3]=h[3]-0}g[0]=e++;return g},ATTR:function(g,h,l,m,q,p){h=g[1].replace(/\\/g,"");if(!p&&n.attrMap[h])g[1]=n.attrMap[h];if(g[2]==="~=")g[4]=" "+g[4]+" ";return g},PSEUDO:function(g,h,l,m,q){if(g[1]==="not")if((f.exec(g[3])||"").length>1||/^\w/.test(g[3]))g[3]=k(g[3],null,null,h);else{g=k.filter(g[3],h,l,true^q);l||m.push.apply(m,
|
|
||||||
g);return false}else if(n.match.POS.test(g[0])||n.match.CHILD.test(g[0]))return true;return g},POS:function(g){g.unshift(true);return g}},filters:{enabled:function(g){return g.disabled===false&&g.type!=="hidden"},disabled:function(g){return g.disabled===true},checked:function(g){return g.checked===true},selected:function(g){return g.selected===true},parent:function(g){return!!g.firstChild},empty:function(g){return!g.firstChild},has:function(g,h,l){return!!k(l[3],g).length},header:function(g){return/h\d/i.test(g.nodeName)},
|
|
||||||
text:function(g){return"text"===g.type},radio:function(g){return"radio"===g.type},checkbox:function(g){return"checkbox"===g.type},file:function(g){return"file"===g.type},password:function(g){return"password"===g.type},submit:function(g){return"submit"===g.type},image:function(g){return"image"===g.type},reset:function(g){return"reset"===g.type},button:function(g){return"button"===g.type||g.nodeName.toLowerCase()==="button"},input:function(g){return/input|select|textarea|button/i.test(g.nodeName)}},
|
|
||||||
setFilters:{first:function(g,h){return h===0},last:function(g,h,l,m){return h===m.length-1},even:function(g,h){return h%2===0},odd:function(g,h){return h%2===1},lt:function(g,h,l){return h<l[3]-0},gt:function(g,h,l){return h>l[3]-0},nth:function(g,h,l){return l[3]-0===h},eq:function(g,h,l){return l[3]-0===h}},filter:{PSEUDO:function(g,h,l,m){var q=h[1],p=n.filters[q];if(p)return p(g,l,h,m);else if(q==="contains")return(g.textContent||g.innerText||a([g])||"").indexOf(h[3])>=0;else if(q==="not"){h=
|
|
||||||
h[3];l=0;for(m=h.length;l<m;l++)if(h[l]===g)return false;return true}else k.error("Syntax error, unrecognized expression: "+q)},CHILD:function(g,h){var l=h[1],m=g;switch(l){case "only":case "first":for(;m=m.previousSibling;)if(m.nodeType===1)return false;if(l==="first")return true;m=g;case "last":for(;m=m.nextSibling;)if(m.nodeType===1)return false;return true;case "nth":l=h[2];var q=h[3];if(l===1&&q===0)return true;h=h[0];var p=g.parentNode;if(p&&(p.sizcache!==h||!g.nodeIndex)){var v=0;for(m=p.firstChild;m;m=
|
|
||||||
m.nextSibling)if(m.nodeType===1)m.nodeIndex=++v;p.sizcache=h}g=g.nodeIndex-q;return l===0?g===0:g%l===0&&g/l>=0}},ID:function(g,h){return g.nodeType===1&&g.getAttribute("id")===h},TAG:function(g,h){return h==="*"&&g.nodeType===1||g.nodeName.toLowerCase()===h},CLASS:function(g,h){return(" "+(g.className||g.getAttribute("class"))+" ").indexOf(h)>-1},ATTR:function(g,h){var l=h[1];g=n.attrHandle[l]?n.attrHandle[l](g):g[l]!=null?g[l]:g.getAttribute(l);l=g+"";var m=h[2];h=h[4];return g==null?m==="!=":m===
|
|
||||||
"="?l===h:m==="*="?l.indexOf(h)>=0:m==="~="?(" "+l+" ").indexOf(h)>=0:!h?l&&g!==false:m==="!="?l!==h:m==="^="?l.indexOf(h)===0:m==="$="?l.substr(l.length-h.length)===h:m==="|="?l===h||l.substr(0,h.length+1)===h+"-":false},POS:function(g,h,l,m){var q=n.setFilters[h[2]];if(q)return q(g,l,h,m)}}},r=n.match.POS;for(var u in n.match){n.match[u]=new RegExp(n.match[u].source+/(?![^\[]*\])(?![^\(]*\))/.source);n.leftMatch[u]=new RegExp(/(^(?:.|\r|\n)*?)/.source+n.match[u].source.replace(/\\(\d+)/g,function(g,
|
|
||||||
h){return"\\"+(h-0+1)}))}var z=function(g,h){g=Array.prototype.slice.call(g,0);if(h){h.push.apply(h,g);return h}return g};try{Array.prototype.slice.call(s.documentElement.childNodes,0)}catch(C){z=function(g,h){h=h||[];if(j.call(g)==="[object Array]")Array.prototype.push.apply(h,g);else if(typeof g.length==="number")for(var l=0,m=g.length;l<m;l++)h.push(g[l]);else for(l=0;g[l];l++)h.push(g[l]);return h}}var B;if(s.documentElement.compareDocumentPosition)B=function(g,h){if(!g.compareDocumentPosition||
|
|
||||||
!h.compareDocumentPosition){if(g==h)i=true;return g.compareDocumentPosition?-1:1}g=g.compareDocumentPosition(h)&4?-1:g===h?0:1;if(g===0)i=true;return g};else if("sourceIndex"in s.documentElement)B=function(g,h){if(!g.sourceIndex||!h.sourceIndex){if(g==h)i=true;return g.sourceIndex?-1:1}g=g.sourceIndex-h.sourceIndex;if(g===0)i=true;return g};else if(s.createRange)B=function(g,h){if(!g.ownerDocument||!h.ownerDocument){if(g==h)i=true;return g.ownerDocument?-1:1}var l=g.ownerDocument.createRange(),m=
|
|
||||||
h.ownerDocument.createRange();l.setStart(g,0);l.setEnd(g,0);m.setStart(h,0);m.setEnd(h,0);g=l.compareBoundaryPoints(Range.START_TO_END,m);if(g===0)i=true;return g};(function(){var g=s.createElement("div"),h="script"+(new Date).getTime();g.innerHTML="<a name='"+h+"'/>";var l=s.documentElement;l.insertBefore(g,l.firstChild);if(s.getElementById(h)){n.find.ID=function(m,q,p){if(typeof q.getElementById!=="undefined"&&!p)return(q=q.getElementById(m[1]))?q.id===m[1]||typeof q.getAttributeNode!=="undefined"&&
|
|
||||||
q.getAttributeNode("id").nodeValue===m[1]?[q]:w:[]};n.filter.ID=function(m,q){var p=typeof m.getAttributeNode!=="undefined"&&m.getAttributeNode("id");return m.nodeType===1&&p&&p.nodeValue===q}}l.removeChild(g);l=g=null})();(function(){var g=s.createElement("div");g.appendChild(s.createComment(""));if(g.getElementsByTagName("*").length>0)n.find.TAG=function(h,l){l=l.getElementsByTagName(h[1]);if(h[1]==="*"){h=[];for(var m=0;l[m];m++)l[m].nodeType===1&&h.push(l[m]);l=h}return l};g.innerHTML="<a href='#'></a>";
|
|
||||||
if(g.firstChild&&typeof g.firstChild.getAttribute!=="undefined"&&g.firstChild.getAttribute("href")!=="#")n.attrHandle.href=function(h){return h.getAttribute("href",2)};g=null})();s.querySelectorAll&&function(){var g=k,h=s.createElement("div");h.innerHTML="<p class='TEST'></p>";if(!(h.querySelectorAll&&h.querySelectorAll(".TEST").length===0)){k=function(m,q,p,v){q=q||s;if(!v&&q.nodeType===9&&!x(q))try{return z(q.querySelectorAll(m),p)}catch(t){}return g(m,q,p,v)};for(var l in g)k[l]=g[l];h=null}}();
|
|
||||||
(function(){var g=s.createElement("div");g.innerHTML="<div class='test e'></div><div class='test'></div>";if(!(!g.getElementsByClassName||g.getElementsByClassName("e").length===0)){g.lastChild.className="e";if(g.getElementsByClassName("e").length!==1){n.order.splice(1,0,"CLASS");n.find.CLASS=function(h,l,m){if(typeof l.getElementsByClassName!=="undefined"&&!m)return l.getElementsByClassName(h[1])};g=null}}})();var E=s.compareDocumentPosition?function(g,h){return!!(g.compareDocumentPosition(h)&16)}:
|
|
||||||
function(g,h){return g!==h&&(g.contains?g.contains(h):true)},x=function(g){return(g=(g?g.ownerDocument||g:0).documentElement)?g.nodeName!=="HTML":false},ga=function(g,h){var l=[],m="",q;for(h=h.nodeType?[h]:h;q=n.match.PSEUDO.exec(g);){m+=q[0];g=g.replace(n.match.PSEUDO,"")}g=n.relative[g]?g+"*":g;q=0;for(var p=h.length;q<p;q++)k(g,h[q],l);return k.filter(m,l)};c.find=k;c.expr=k.selectors;c.expr[":"]=c.expr.filters;c.unique=k.uniqueSort;c.text=a;c.isXMLDoc=x;c.contains=E})();var eb=/Until$/,fb=/^(?:parents|prevUntil|prevAll)/,
|
|
||||||
gb=/,/;R=Array.prototype.slice;var Ia=function(a,b,d){if(c.isFunction(b))return c.grep(a,function(e,j){return!!b.call(e,j,e)===d});else if(b.nodeType)return c.grep(a,function(e){return e===b===d});else if(typeof b==="string"){var f=c.grep(a,function(e){return e.nodeType===1});if(Ua.test(b))return c.filter(b,f,!d);else b=c.filter(b,f)}return c.grep(a,function(e){return c.inArray(e,b)>=0===d})};c.fn.extend({find:function(a){for(var b=this.pushStack("","find",a),d=0,f=0,e=this.length;f<e;f++){d=b.length;
|
|
||||||
c.find(a,this[f],b);if(f>0)for(var j=d;j<b.length;j++)for(var i=0;i<d;i++)if(b[i]===b[j]){b.splice(j--,1);break}}return b},has:function(a){var b=c(a);return this.filter(function(){for(var d=0,f=b.length;d<f;d++)if(c.contains(this,b[d]))return true})},not:function(a){return this.pushStack(Ia(this,a,false),"not",a)},filter:function(a){return this.pushStack(Ia(this,a,true),"filter",a)},is:function(a){return!!a&&c.filter(a,this).length>0},closest:function(a,b){if(c.isArray(a)){var d=[],f=this[0],e,j=
|
|
||||||
{},i;if(f&&a.length){e=0;for(var o=a.length;e<o;e++){i=a[e];j[i]||(j[i]=c.expr.match.POS.test(i)?c(i,b||this.context):i)}for(;f&&f.ownerDocument&&f!==b;){for(i in j){e=j[i];if(e.jquery?e.index(f)>-1:c(f).is(e)){d.push({selector:i,elem:f});delete j[i]}}f=f.parentNode}}return d}var k=c.expr.match.POS.test(a)?c(a,b||this.context):null;return this.map(function(n,r){for(;r&&r.ownerDocument&&r!==b;){if(k?k.index(r)>-1:c(r).is(a))return r;r=r.parentNode}return null})},index:function(a){if(!a||typeof a===
|
|
||||||
"string")return c.inArray(this[0],a?c(a):this.parent().children());return c.inArray(a.jquery?a[0]:a,this)},add:function(a,b){a=typeof a==="string"?c(a,b||this.context):c.makeArray(a);b=c.merge(this.get(),a);return this.pushStack(qa(a[0])||qa(b[0])?b:c.unique(b))},andSelf:function(){return this.add(this.prevObject)}});c.each({parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},parents:function(a){return c.dir(a,"parentNode")},parentsUntil:function(a,b,d){return c.dir(a,"parentNode",
|
|
||||||
d)},next:function(a){return c.nth(a,2,"nextSibling")},prev:function(a){return c.nth(a,2,"previousSibling")},nextAll:function(a){return c.dir(a,"nextSibling")},prevAll:function(a){return c.dir(a,"previousSibling")},nextUntil:function(a,b,d){return c.dir(a,"nextSibling",d)},prevUntil:function(a,b,d){return c.dir(a,"previousSibling",d)},siblings:function(a){return c.sibling(a.parentNode.firstChild,a)},children:function(a){return c.sibling(a.firstChild)},contents:function(a){return c.nodeName(a,"iframe")?
|
|
||||||
a.contentDocument||a.contentWindow.document:c.makeArray(a.childNodes)}},function(a,b){c.fn[a]=function(d,f){var e=c.map(this,b,d);eb.test(a)||(f=d);if(f&&typeof f==="string")e=c.filter(f,e);e=this.length>1?c.unique(e):e;if((this.length>1||gb.test(f))&&fb.test(a))e=e.reverse();return this.pushStack(e,a,R.call(arguments).join(","))}});c.extend({filter:function(a,b,d){if(d)a=":not("+a+")";return c.find.matches(a,b)},dir:function(a,b,d){var f=[];for(a=a[b];a&&a.nodeType!==9&&(d===w||a.nodeType!==1||!c(a).is(d));){a.nodeType===
|
|
||||||
1&&f.push(a);a=a[b]}return f},nth:function(a,b,d){b=b||1;for(var f=0;a;a=a[d])if(a.nodeType===1&&++f===b)break;return a},sibling:function(a,b){for(var d=[];a;a=a.nextSibling)a.nodeType===1&&a!==b&&d.push(a);return d}});var Ja=/ jQuery\d+="(?:\d+|null)"/g,V=/^\s+/,Ka=/(<([\w:]+)[^>]*?)\/>/g,hb=/^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i,La=/<([\w:]+)/,ib=/<tbody/i,jb=/<|&#?\w+;/,ta=/<script|<object|<embed|<option|<style/i,ua=/checked\s*(?:[^=]|=\s*.checked.)/i,Ma=function(a,b,d){return hb.test(d)?
|
|
||||||
a:b+"></"+d+">"},F={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]};F.optgroup=F.option;F.tbody=F.tfoot=F.colgroup=F.caption=F.thead;F.th=F.td;if(!c.support.htmlSerialize)F._default=[1,"div<div>","</div>"];c.fn.extend({text:function(a){if(c.isFunction(a))return this.each(function(b){var d=
|
|
||||||
c(this);d.text(a.call(this,b,d.text()))});if(typeof a!=="object"&&a!==w)return this.empty().append((this[0]&&this[0].ownerDocument||s).createTextNode(a));return c.text(this)},wrapAll:function(a){if(c.isFunction(a))return this.each(function(d){c(this).wrapAll(a.call(this,d))});if(this[0]){var b=c(a,this[0].ownerDocument).eq(0).clone(true);this[0].parentNode&&b.insertBefore(this[0]);b.map(function(){for(var d=this;d.firstChild&&d.firstChild.nodeType===1;)d=d.firstChild;return d}).append(this)}return this},
|
|
||||||
wrapInner:function(a){if(c.isFunction(a))return this.each(function(b){c(this).wrapInner(a.call(this,b))});return this.each(function(){var b=c(this),d=b.contents();d.length?d.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){c(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){c.nodeName(this,"body")||c(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.appendChild(a)})},
|
|
||||||
prepend:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this)});else if(arguments.length){var a=c(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,
|
|
||||||
this.nextSibling)});else if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,c(arguments[0]).toArray());return a}},remove:function(a,b){for(var d=0,f;(f=this[d])!=null;d++)if(!a||c.filter(a,[f]).length){if(!b&&f.nodeType===1){c.cleanData(f.getElementsByTagName("*"));c.cleanData([f])}f.parentNode&&f.parentNode.removeChild(f)}return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++)for(b.nodeType===1&&c.cleanData(b.getElementsByTagName("*"));b.firstChild;)b.removeChild(b.firstChild);
|
|
||||||
return this},clone:function(a){var b=this.map(function(){if(!c.support.noCloneEvent&&!c.isXMLDoc(this)){var d=this.outerHTML,f=this.ownerDocument;if(!d){d=f.createElement("div");d.appendChild(this.cloneNode(true));d=d.innerHTML}return c.clean([d.replace(Ja,"").replace(/=([^="'>\s]+\/)>/g,'="$1">').replace(V,"")],f)[0]}else return this.cloneNode(true)});if(a===true){ra(this,b);ra(this.find("*"),b.find("*"))}return b},html:function(a){if(a===w)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(Ja,
|
|
||||||
""):null;else if(typeof a==="string"&&!ta.test(a)&&(c.support.leadingWhitespace||!V.test(a))&&!F[(La.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Ka,Ma);try{for(var b=0,d=this.length;b<d;b++)if(this[b].nodeType===1){c.cleanData(this[b].getElementsByTagName("*"));this[b].innerHTML=a}}catch(f){this.empty().append(a)}}else c.isFunction(a)?this.each(function(e){var j=c(this),i=j.html();j.empty().append(function(){return a.call(this,e,i)})}):this.empty().append(a);return this},replaceWith:function(a){if(this[0]&&
|
|
||||||
this[0].parentNode){if(c.isFunction(a))return this.each(function(b){var d=c(this),f=d.html();d.replaceWith(a.call(this,b,f))});if(typeof a!=="string")a=c(a).detach();return this.each(function(){var b=this.nextSibling,d=this.parentNode;c(this).remove();b?c(b).before(a):c(d).append(a)})}else return this.pushStack(c(c.isFunction(a)?a():a),"replaceWith",a)},detach:function(a){return this.remove(a,true)},domManip:function(a,b,d){function f(u){return c.nodeName(u,"table")?u.getElementsByTagName("tbody")[0]||
|
|
||||||
u.appendChild(u.ownerDocument.createElement("tbody")):u}var e,j,i=a[0],o=[],k;if(!c.support.checkClone&&arguments.length===3&&typeof i==="string"&&ua.test(i))return this.each(function(){c(this).domManip(a,b,d,true)});if(c.isFunction(i))return this.each(function(u){var z=c(this);a[0]=i.call(this,u,b?z.html():w);z.domManip(a,b,d)});if(this[0]){e=i&&i.parentNode;e=c.support.parentNode&&e&&e.nodeType===11&&e.childNodes.length===this.length?{fragment:e}:sa(a,this,o);k=e.fragment;if(j=k.childNodes.length===
|
|
||||||
1?(k=k.firstChild):k.firstChild){b=b&&c.nodeName(j,"tr");for(var n=0,r=this.length;n<r;n++)d.call(b?f(this[n],j):this[n],n>0||e.cacheable||this.length>1?k.cloneNode(true):k)}o.length&&c.each(o,Qa)}return this}});c.fragments={};c.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){c.fn[a]=function(d){var f=[];d=c(d);var e=this.length===1&&this[0].parentNode;if(e&&e.nodeType===11&&e.childNodes.length===1&&d.length===1){d[b](this[0]);
|
|
||||||
return this}else{e=0;for(var j=d.length;e<j;e++){var i=(e>0?this.clone(true):this).get();c.fn[b].apply(c(d[e]),i);f=f.concat(i)}return this.pushStack(f,a,d.selector)}}});c.extend({clean:function(a,b,d,f){b=b||s;if(typeof b.createElement==="undefined")b=b.ownerDocument||b[0]&&b[0].ownerDocument||s;for(var e=[],j=0,i;(i=a[j])!=null;j++){if(typeof i==="number")i+="";if(i){if(typeof i==="string"&&!jb.test(i))i=b.createTextNode(i);else if(typeof i==="string"){i=i.replace(Ka,Ma);var o=(La.exec(i)||["",
|
|
||||||
""])[1].toLowerCase(),k=F[o]||F._default,n=k[0],r=b.createElement("div");for(r.innerHTML=k[1]+i+k[2];n--;)r=r.lastChild;if(!c.support.tbody){n=ib.test(i);o=o==="table"&&!n?r.firstChild&&r.firstChild.childNodes:k[1]==="<table>"&&!n?r.childNodes:[];for(k=o.length-1;k>=0;--k)c.nodeName(o[k],"tbody")&&!o[k].childNodes.length&&o[k].parentNode.removeChild(o[k])}!c.support.leadingWhitespace&&V.test(i)&&r.insertBefore(b.createTextNode(V.exec(i)[0]),r.firstChild);i=r.childNodes}if(i.nodeType)e.push(i);else e=
|
|
||||||
c.merge(e,i)}}if(d)for(j=0;e[j];j++)if(f&&c.nodeName(e[j],"script")&&(!e[j].type||e[j].type.toLowerCase()==="text/javascript"))f.push(e[j].parentNode?e[j].parentNode.removeChild(e[j]):e[j]);else{e[j].nodeType===1&&e.splice.apply(e,[j+1,0].concat(c.makeArray(e[j].getElementsByTagName("script"))));d.appendChild(e[j])}return e},cleanData:function(a){for(var b,d,f=c.cache,e=c.event.special,j=c.support.deleteExpando,i=0,o;(o=a[i])!=null;i++)if(d=o[c.expando]){b=f[d];if(b.events)for(var k in b.events)e[k]?
|
|
||||||
c.event.remove(o,k):Ca(o,k,b.handle);if(j)delete o[c.expando];else o.removeAttribute&&o.removeAttribute(c.expando);delete f[d]}}});var kb=/z-?index|font-?weight|opacity|zoom|line-?height/i,Na=/alpha\([^)]*\)/,Oa=/opacity=([^)]*)/,ha=/float/i,ia=/-([a-z])/ig,lb=/([A-Z])/g,mb=/^-?\d+(?:px)?$/i,nb=/^-?\d/,ob={position:"absolute",visibility:"hidden",display:"block"},pb=["Left","Right"],qb=["Top","Bottom"],rb=s.defaultView&&s.defaultView.getComputedStyle,Pa=c.support.cssFloat?"cssFloat":"styleFloat",ja=
|
|
||||||
function(a,b){return b.toUpperCase()};c.fn.css=function(a,b){return X(this,a,b,true,function(d,f,e){if(e===w)return c.curCSS(d,f);if(typeof e==="number"&&!kb.test(f))e+="px";c.style(d,f,e)})};c.extend({style:function(a,b,d){if(!a||a.nodeType===3||a.nodeType===8)return w;if((b==="width"||b==="height")&&parseFloat(d)<0)d=w;var f=a.style||a,e=d!==w;if(!c.support.opacity&&b==="opacity"){if(e){f.zoom=1;b=parseInt(d,10)+""==="NaN"?"":"alpha(opacity="+d*100+")";a=f.filter||c.curCSS(a,"filter")||"";f.filter=
|
|
||||||
Na.test(a)?a.replace(Na,b):b}return f.filter&&f.filter.indexOf("opacity=")>=0?parseFloat(Oa.exec(f.filter)[1])/100+"":""}if(ha.test(b))b=Pa;b=b.replace(ia,ja);if(e)f[b]=d;return f[b]},css:function(a,b,d,f){if(b==="width"||b==="height"){var e,j=b==="width"?pb:qb;function i(){e=b==="width"?a.offsetWidth:a.offsetHeight;f!=="border"&&c.each(j,function(){f||(e-=parseFloat(c.curCSS(a,"padding"+this,true))||0);if(f==="margin")e+=parseFloat(c.curCSS(a,"margin"+this,true))||0;else e-=parseFloat(c.curCSS(a,
|
|
||||||
"border"+this+"Width",true))||0})}a.offsetWidth!==0?i():c.swap(a,ob,i);return Math.max(0,Math.round(e))}return c.curCSS(a,b,d)},curCSS:function(a,b,d){var f,e=a.style;if(!c.support.opacity&&b==="opacity"&&a.currentStyle){f=Oa.test(a.currentStyle.filter||"")?parseFloat(RegExp.$1)/100+"":"";return f===""?"1":f}if(ha.test(b))b=Pa;if(!d&&e&&e[b])f=e[b];else if(rb){if(ha.test(b))b="float";b=b.replace(lb,"-$1").toLowerCase();e=a.ownerDocument.defaultView;if(!e)return null;if(a=e.getComputedStyle(a,null))f=
|
|
||||||
a.getPropertyValue(b);if(b==="opacity"&&f==="")f="1"}else if(a.currentStyle){d=b.replace(ia,ja);f=a.currentStyle[b]||a.currentStyle[d];if(!mb.test(f)&&nb.test(f)){b=e.left;var j=a.runtimeStyle.left;a.runtimeStyle.left=a.currentStyle.left;e.left=d==="fontSize"?"1em":f||0;f=e.pixelLeft+"px";e.left=b;a.runtimeStyle.left=j}}return f},swap:function(a,b,d){var f={};for(var e in b){f[e]=a.style[e];a.style[e]=b[e]}d.call(a);for(e in b)a.style[e]=f[e]}});if(c.expr&&c.expr.filters){c.expr.filters.hidden=function(a){var b=
|
|
||||||
a.offsetWidth,d=a.offsetHeight,f=a.nodeName.toLowerCase()==="tr";return b===0&&d===0&&!f?true:b>0&&d>0&&!f?false:c.curCSS(a,"display")==="none"};c.expr.filters.visible=function(a){return!c.expr.filters.hidden(a)}}var sb=J(),tb=/<script(.|\s)*?\/script>/gi,ub=/select|textarea/i,vb=/color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week/i,N=/=\?(&|$)/,ka=/\?/,wb=/(\?|&)_=.*?(&|$)/,xb=/^(\w+:)?\/\/([^\/?#]+)/,yb=/%20/g,zb=c.fn.load;c.fn.extend({load:function(a,b,d){if(typeof a!==
|
|
||||||
"string")return zb.call(this,a);else if(!this.length)return this;var f=a.indexOf(" ");if(f>=0){var e=a.slice(f,a.length);a=a.slice(0,f)}f="GET";if(b)if(c.isFunction(b)){d=b;b=null}else if(typeof b==="object"){b=c.param(b,c.ajaxSettings.traditional);f="POST"}var j=this;c.ajax({url:a,type:f,dataType:"html",data:b,complete:function(i,o){if(o==="success"||o==="notmodified")j.html(e?c("<div />").append(i.responseText.replace(tb,"")).find(e):i.responseText);d&&j.each(d,[i.responseText,o,i])}});return this},
|
|
||||||
serialize:function(){return c.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?c.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||ub.test(this.nodeName)||vb.test(this.type))}).map(function(a,b){a=c(this).val();return a==null?null:c.isArray(a)?c.map(a,function(d){return{name:b.name,value:d}}):{name:b.name,value:a}}).get()}});c.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),
|
|
||||||
function(a,b){c.fn[b]=function(d){return this.bind(b,d)}});c.extend({get:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b=null}return c.ajax({type:"GET",url:a,data:b,success:d,dataType:f})},getScript:function(a,b){return c.get(a,null,b,"script")},getJSON:function(a,b,d){return c.get(a,b,d,"json")},post:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b={}}return c.ajax({type:"POST",url:a,data:b,success:d,dataType:f})},ajaxSetup:function(a){c.extend(c.ajaxSettings,a)},ajaxSettings:{url:location.href,
|
|
||||||
global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:A.XMLHttpRequest&&(A.location.protocol!=="file:"||!A.ActiveXObject)?function(){return new A.XMLHttpRequest}:function(){try{return new A.ActiveXObject("Microsoft.XMLHTTP")}catch(a){}},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},etag:{},ajax:function(a){function b(){e.success&&
|
|
||||||
e.success.call(k,o,i,x);e.global&&f("ajaxSuccess",[x,e])}function d(){e.complete&&e.complete.call(k,x,i);e.global&&f("ajaxComplete",[x,e]);e.global&&!--c.active&&c.event.trigger("ajaxStop")}function f(q,p){(e.context?c(e.context):c.event).trigger(q,p)}var e=c.extend(true,{},c.ajaxSettings,a),j,i,o,k=a&&a.context||e,n=e.type.toUpperCase();if(e.data&&e.processData&&typeof e.data!=="string")e.data=c.param(e.data,e.traditional);if(e.dataType==="jsonp"){if(n==="GET")N.test(e.url)||(e.url+=(ka.test(e.url)?
|
|
||||||
"&":"?")+(e.jsonp||"callback")+"=?");else if(!e.data||!N.test(e.data))e.data=(e.data?e.data+"&":"")+(e.jsonp||"callback")+"=?";e.dataType="json"}if(e.dataType==="json"&&(e.data&&N.test(e.data)||N.test(e.url))){j=e.jsonpCallback||"jsonp"+sb++;if(e.data)e.data=(e.data+"").replace(N,"="+j+"$1");e.url=e.url.replace(N,"="+j+"$1");e.dataType="script";A[j]=A[j]||function(q){o=q;b();d();A[j]=w;try{delete A[j]}catch(p){}z&&z.removeChild(C)}}if(e.dataType==="script"&&e.cache===null)e.cache=false;if(e.cache===
|
|
||||||
false&&n==="GET"){var r=J(),u=e.url.replace(wb,"$1_="+r+"$2");e.url=u+(u===e.url?(ka.test(e.url)?"&":"?")+"_="+r:"")}if(e.data&&n==="GET")e.url+=(ka.test(e.url)?"&":"?")+e.data;e.global&&!c.active++&&c.event.trigger("ajaxStart");r=(r=xb.exec(e.url))&&(r[1]&&r[1]!==location.protocol||r[2]!==location.host);if(e.dataType==="script"&&n==="GET"&&r){var z=s.getElementsByTagName("head")[0]||s.documentElement,C=s.createElement("script");C.src=e.url;if(e.scriptCharset)C.charset=e.scriptCharset;if(!j){var B=
|
|
||||||
false;C.onload=C.onreadystatechange=function(){if(!B&&(!this.readyState||this.readyState==="loaded"||this.readyState==="complete")){B=true;b();d();C.onload=C.onreadystatechange=null;z&&C.parentNode&&z.removeChild(C)}}}z.insertBefore(C,z.firstChild);return w}var E=false,x=e.xhr();if(x){e.username?x.open(n,e.url,e.async,e.username,e.password):x.open(n,e.url,e.async);try{if(e.data||a&&a.contentType)x.setRequestHeader("Content-Type",e.contentType);if(e.ifModified){c.lastModified[e.url]&&x.setRequestHeader("If-Modified-Since",
|
|
||||||
c.lastModified[e.url]);c.etag[e.url]&&x.setRequestHeader("If-None-Match",c.etag[e.url])}r||x.setRequestHeader("X-Requested-With","XMLHttpRequest");x.setRequestHeader("Accept",e.dataType&&e.accepts[e.dataType]?e.accepts[e.dataType]+", */*":e.accepts._default)}catch(ga){}if(e.beforeSend&&e.beforeSend.call(k,x,e)===false){e.global&&!--c.active&&c.event.trigger("ajaxStop");x.abort();return false}e.global&&f("ajaxSend",[x,e]);var g=x.onreadystatechange=function(q){if(!x||x.readyState===0||q==="abort"){E||
|
|
||||||
d();E=true;if(x)x.onreadystatechange=c.noop}else if(!E&&x&&(x.readyState===4||q==="timeout")){E=true;x.onreadystatechange=c.noop;i=q==="timeout"?"timeout":!c.httpSuccess(x)?"error":e.ifModified&&c.httpNotModified(x,e.url)?"notmodified":"success";var p;if(i==="success")try{o=c.httpData(x,e.dataType,e)}catch(v){i="parsererror";p=v}if(i==="success"||i==="notmodified")j||b();else c.handleError(e,x,i,p);d();q==="timeout"&&x.abort();if(e.async)x=null}};try{var h=x.abort;x.abort=function(){x&&h.call(x);
|
|
||||||
g("abort")}}catch(l){}e.async&&e.timeout>0&&setTimeout(function(){x&&!E&&g("timeout")},e.timeout);try{x.send(n==="POST"||n==="PUT"||n==="DELETE"?e.data:null)}catch(m){c.handleError(e,x,null,m);d()}e.async||g();return x}},handleError:function(a,b,d,f){if(a.error)a.error.call(a.context||a,b,d,f);if(a.global)(a.context?c(a.context):c.event).trigger("ajaxError",[b,a,f])},active:0,httpSuccess:function(a){try{return!a.status&&location.protocol==="file:"||a.status>=200&&a.status<300||a.status===304||a.status===
|
|
||||||
1223||a.status===0}catch(b){}return false},httpNotModified:function(a,b){var d=a.getResponseHeader("Last-Modified"),f=a.getResponseHeader("Etag");if(d)c.lastModified[b]=d;if(f)c.etag[b]=f;return a.status===304||a.status===0},httpData:function(a,b,d){var f=a.getResponseHeader("content-type")||"",e=b==="xml"||!b&&f.indexOf("xml")>=0;a=e?a.responseXML:a.responseText;e&&a.documentElement.nodeName==="parsererror"&&c.error("parsererror");if(d&&d.dataFilter)a=d.dataFilter(a,b);if(typeof a==="string")if(b===
|
|
||||||
"json"||!b&&f.indexOf("json")>=0)a=c.parseJSON(a);else if(b==="script"||!b&&f.indexOf("javascript")>=0)c.globalEval(a);return a},param:function(a,b){function d(i,o){if(c.isArray(o))c.each(o,function(k,n){b||/\[\]$/.test(i)?f(i,n):d(i+"["+(typeof n==="object"||c.isArray(n)?k:"")+"]",n)});else!b&&o!=null&&typeof o==="object"?c.each(o,function(k,n){d(i+"["+k+"]",n)}):f(i,o)}function f(i,o){o=c.isFunction(o)?o():o;e[e.length]=encodeURIComponent(i)+"="+encodeURIComponent(o)}var e=[];if(b===w)b=c.ajaxSettings.traditional;
|
|
||||||
if(c.isArray(a)||a.jquery)c.each(a,function(){f(this.name,this.value)});else for(var j in a)d(j,a[j]);return e.join("&").replace(yb,"+")}});var la={},Ab=/toggle|show|hide/,Bb=/^([+-]=)?([\d+-.]+)(.*)$/,W,va=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];c.fn.extend({show:function(a,b){if(a||a===0)return this.animate(K("show",3),a,b);else{a=0;for(b=this.length;a<b;a++){var d=c.data(this[a],"olddisplay");
|
|
||||||
this[a].style.display=d||"";if(c.css(this[a],"display")==="none"){d=this[a].nodeName;var f;if(la[d])f=la[d];else{var e=c("<"+d+" />").appendTo("body");f=e.css("display");if(f==="none")f="block";e.remove();la[d]=f}c.data(this[a],"olddisplay",f)}}a=0;for(b=this.length;a<b;a++)this[a].style.display=c.data(this[a],"olddisplay")||"";return this}},hide:function(a,b){if(a||a===0)return this.animate(K("hide",3),a,b);else{a=0;for(b=this.length;a<b;a++){var d=c.data(this[a],"olddisplay");!d&&d!=="none"&&c.data(this[a],
|
|
||||||
"olddisplay",c.css(this[a],"display"))}a=0;for(b=this.length;a<b;a++)this[a].style.display="none";return this}},_toggle:c.fn.toggle,toggle:function(a,b){var d=typeof a==="boolean";if(c.isFunction(a)&&c.isFunction(b))this._toggle.apply(this,arguments);else a==null||d?this.each(function(){var f=d?a:c(this).is(":hidden");c(this)[f?"show":"hide"]()}):this.animate(K("toggle",3),a,b);return this},fadeTo:function(a,b,d){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:b},a,d)},
|
|
||||||
animate:function(a,b,d,f){var e=c.speed(b,d,f);if(c.isEmptyObject(a))return this.each(e.complete);return this[e.queue===false?"each":"queue"](function(){var j=c.extend({},e),i,o=this.nodeType===1&&c(this).is(":hidden"),k=this;for(i in a){var n=i.replace(ia,ja);if(i!==n){a[n]=a[i];delete a[i];i=n}if(a[i]==="hide"&&o||a[i]==="show"&&!o)return j.complete.call(this);if((i==="height"||i==="width")&&this.style){j.display=c.css(this,"display");j.overflow=this.style.overflow}if(c.isArray(a[i])){(j.specialEasing=
|
|
||||||
j.specialEasing||{})[i]=a[i][1];a[i]=a[i][0]}}if(j.overflow!=null)this.style.overflow="hidden";j.curAnim=c.extend({},a);c.each(a,function(r,u){var z=new c.fx(k,j,r);if(Ab.test(u))z[u==="toggle"?o?"show":"hide":u](a);else{var C=Bb.exec(u),B=z.cur(true)||0;if(C){u=parseFloat(C[2]);var E=C[3]||"px";if(E!=="px"){k.style[r]=(u||1)+E;B=(u||1)/z.cur(true)*B;k.style[r]=B+E}if(C[1])u=(C[1]==="-="?-1:1)*u+B;z.custom(B,u,E)}else z.custom(B,u,"")}});return true})},stop:function(a,b){var d=c.timers;a&&this.queue([]);
|
|
||||||
this.each(function(){for(var f=d.length-1;f>=0;f--)if(d[f].elem===this){b&&d[f](true);d.splice(f,1)}});b||this.dequeue();return this}});c.each({slideDown:K("show",1),slideUp:K("hide",1),slideToggle:K("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(a,b){c.fn[a]=function(d,f){return this.animate(b,d,f)}});c.extend({speed:function(a,b,d){var f=a&&typeof a==="object"?a:{complete:d||!d&&b||c.isFunction(a)&&a,duration:a,easing:d&&b||b&&!c.isFunction(b)&&b};f.duration=c.fx.off?0:typeof f.duration===
|
|
||||||
"number"?f.duration:c.fx.speeds[f.duration]||c.fx.speeds._default;f.old=f.complete;f.complete=function(){f.queue!==false&&c(this).dequeue();c.isFunction(f.old)&&f.old.call(this)};return f},easing:{linear:function(a,b,d,f){return d+f*a},swing:function(a,b,d,f){return(-Math.cos(a*Math.PI)/2+0.5)*f+d}},timers:[],fx:function(a,b,d){this.options=b;this.elem=a;this.prop=d;if(!b.orig)b.orig={}}});c.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this);(c.fx.step[this.prop]||
|
|
||||||
c.fx.step._default)(this);if((this.prop==="height"||this.prop==="width")&&this.elem.style)this.elem.style.display="block"},cur:function(a){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];return(a=parseFloat(c.css(this.elem,this.prop,a)))&&a>-10000?a:parseFloat(c.curCSS(this.elem,this.prop))||0},custom:function(a,b,d){function f(j){return e.step(j)}this.startTime=J();this.start=a;this.end=b;this.unit=d||this.unit||"px";this.now=this.start;
|
|
||||||
this.pos=this.state=0;var e=this;f.elem=this.elem;if(f()&&c.timers.push(f)&&!W)W=setInterval(c.fx.tick,13)},show:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.show=true;this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur());c(this.elem).show()},hide:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.hide=true;this.custom(this.cur(),0)},step:function(a){var b=J(),d=true;if(a||b>=this.options.duration+this.startTime){this.now=
|
|
||||||
this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;for(var f in this.options.curAnim)if(this.options.curAnim[f]!==true)d=false;if(d){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;a=c.data(this.elem,"olddisplay");this.elem.style.display=a?a:this.options.display;if(c.css(this.elem,"display")==="none")this.elem.style.display="block"}this.options.hide&&c(this.elem).hide();if(this.options.hide||this.options.show)for(var e in this.options.curAnim)c.style(this.elem,
|
|
||||||
e,this.options.orig[e]);this.options.complete.call(this.elem)}return false}else{e=b-this.startTime;this.state=e/this.options.duration;a=this.options.easing||(c.easing.swing?"swing":"linear");this.pos=c.easing[this.options.specialEasing&&this.options.specialEasing[this.prop]||a](this.state,e,0,1,this.options.duration);this.now=this.start+(this.end-this.start)*this.pos;this.update()}return true}};c.extend(c.fx,{tick:function(){for(var a=c.timers,b=0;b<a.length;b++)a[b]()||a.splice(b--,1);a.length||
|
|
||||||
c.fx.stop()},stop:function(){clearInterval(W);W=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(a){c.style(a.elem,"opacity",a.now)},_default:function(a){if(a.elem.style&&a.elem.style[a.prop]!=null)a.elem.style[a.prop]=(a.prop==="width"||a.prop==="height"?Math.max(0,a.now):a.now)+a.unit;else a.elem[a.prop]=a.now}}});if(c.expr&&c.expr.filters)c.expr.filters.animated=function(a){return c.grep(c.timers,function(b){return a===b.elem}).length};c.fn.offset="getBoundingClientRect"in s.documentElement?
|
|
||||||
function(a){var b=this[0];if(a)return this.each(function(e){c.offset.setOffset(this,a,e)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);var d=b.getBoundingClientRect(),f=b.ownerDocument;b=f.body;f=f.documentElement;return{top:d.top+(self.pageYOffset||c.support.boxModel&&f.scrollTop||b.scrollTop)-(f.clientTop||b.clientTop||0),left:d.left+(self.pageXOffset||c.support.boxModel&&f.scrollLeft||b.scrollLeft)-(f.clientLeft||b.clientLeft||0)}}:function(a){var b=
|
|
||||||
this[0];if(a)return this.each(function(r){c.offset.setOffset(this,a,r)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);c.offset.initialize();var d=b.offsetParent,f=b,e=b.ownerDocument,j,i=e.documentElement,o=e.body;f=(e=e.defaultView)?e.getComputedStyle(b,null):b.currentStyle;for(var k=b.offsetTop,n=b.offsetLeft;(b=b.parentNode)&&b!==o&&b!==i;){if(c.offset.supportsFixedPosition&&f.position==="fixed")break;j=e?e.getComputedStyle(b,null):b.currentStyle;
|
|
||||||
k-=b.scrollTop;n-=b.scrollLeft;if(b===d){k+=b.offsetTop;n+=b.offsetLeft;if(c.offset.doesNotAddBorder&&!(c.offset.doesAddBorderForTableAndCells&&/^t(able|d|h)$/i.test(b.nodeName))){k+=parseFloat(j.borderTopWidth)||0;n+=parseFloat(j.borderLeftWidth)||0}f=d;d=b.offsetParent}if(c.offset.subtractsBorderForOverflowNotVisible&&j.overflow!=="visible"){k+=parseFloat(j.borderTopWidth)||0;n+=parseFloat(j.borderLeftWidth)||0}f=j}if(f.position==="relative"||f.position==="static"){k+=o.offsetTop;n+=o.offsetLeft}if(c.offset.supportsFixedPosition&&
|
|
||||||
f.position==="fixed"){k+=Math.max(i.scrollTop,o.scrollTop);n+=Math.max(i.scrollLeft,o.scrollLeft)}return{top:k,left:n}};c.offset={initialize:function(){var a=s.body,b=s.createElement("div"),d,f,e,j=parseFloat(c.curCSS(a,"marginTop",true))||0;c.extend(b.style,{position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"});b.innerHTML="<div style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;'><div></div></div><table style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;' cellpadding='0' cellspacing='0'><tr><td></td></tr></table>";
|
|
||||||
a.insertBefore(b,a.firstChild);d=b.firstChild;f=d.firstChild;e=d.nextSibling.firstChild.firstChild;this.doesNotAddBorder=f.offsetTop!==5;this.doesAddBorderForTableAndCells=e.offsetTop===5;f.style.position="fixed";f.style.top="20px";this.supportsFixedPosition=f.offsetTop===20||f.offsetTop===15;f.style.position=f.style.top="";d.style.overflow="hidden";d.style.position="relative";this.subtractsBorderForOverflowNotVisible=f.offsetTop===-5;this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==j;a.removeChild(b);
|
|
||||||
c.offset.initialize=c.noop},bodyOffset:function(a){var b=a.offsetTop,d=a.offsetLeft;c.offset.initialize();if(c.offset.doesNotIncludeMarginInBodyOffset){b+=parseFloat(c.curCSS(a,"marginTop",true))||0;d+=parseFloat(c.curCSS(a,"marginLeft",true))||0}return{top:b,left:d}},setOffset:function(a,b,d){if(/static/.test(c.curCSS(a,"position")))a.style.position="relative";var f=c(a),e=f.offset(),j=parseInt(c.curCSS(a,"top",true),10)||0,i=parseInt(c.curCSS(a,"left",true),10)||0;if(c.isFunction(b))b=b.call(a,
|
|
||||||
d,e);d={top:b.top-e.top+j,left:b.left-e.left+i};"using"in b?b.using.call(a,d):f.css(d)}};c.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),d=this.offset(),f=/^body|html$/i.test(b[0].nodeName)?{top:0,left:0}:b.offset();d.top-=parseFloat(c.curCSS(a,"marginTop",true))||0;d.left-=parseFloat(c.curCSS(a,"marginLeft",true))||0;f.top+=parseFloat(c.curCSS(b[0],"borderTopWidth",true))||0;f.left+=parseFloat(c.curCSS(b[0],"borderLeftWidth",true))||0;return{top:d.top-
|
|
||||||
f.top,left:d.left-f.left}},offsetParent:function(){return this.map(function(){for(var a=this.offsetParent||s.body;a&&!/^body|html$/i.test(a.nodeName)&&c.css(a,"position")==="static";)a=a.offsetParent;return a})}});c.each(["Left","Top"],function(a,b){var d="scroll"+b;c.fn[d]=function(f){var e=this[0],j;if(!e)return null;if(f!==w)return this.each(function(){if(j=wa(this))j.scrollTo(!a?f:c(j).scrollLeft(),a?f:c(j).scrollTop());else this[d]=f});else return(j=wa(e))?"pageXOffset"in j?j[a?"pageYOffset":
|
|
||||||
"pageXOffset"]:c.support.boxModel&&j.document.documentElement[d]||j.document.body[d]:e[d]}});c.each(["Height","Width"],function(a,b){var d=b.toLowerCase();c.fn["inner"+b]=function(){return this[0]?c.css(this[0],d,false,"padding"):null};c.fn["outer"+b]=function(f){return this[0]?c.css(this[0],d,false,f?"margin":"border"):null};c.fn[d]=function(f){var e=this[0];if(!e)return f==null?null:this;if(c.isFunction(f))return this.each(function(j){var i=c(this);i[d](f.call(this,j,i[d]()))});return"scrollTo"in
|
|
||||||
e&&e.document?e.document.compatMode==="CSS1Compat"&&e.document.documentElement["client"+b]||e.document.body["client"+b]:e.nodeType===9?Math.max(e.documentElement["client"+b],e.body["scroll"+b],e.documentElement["scroll"+b],e.body["offset"+b],e.documentElement["offset"+b]):f===w?c.css(e,d):this.css(d,typeof f==="string"?f:f+"px")}});A.jQuery=A.$=c})(window);
|
|
|
@ -34,7 +34,7 @@ module Hydra #:nodoc:
|
||||||
complete = ((@files_completed.to_f / @total_files.to_f) * width).to_i
|
complete = ((@files_completed.to_f / @total_files.to_f) * width).to_i
|
||||||
@output.write "\r" # move to beginning
|
@output.write "\r" # move to beginning
|
||||||
@output.write 'Hydra Testing ['
|
@output.write 'Hydra Testing ['
|
||||||
@output.write @errors ? "\033[0;31m" : "\033[0;32m"
|
@output.write @errors ? "\033[1;31m" : "\033[1;32m"
|
||||||
complete.times{@output.write '#'}
|
complete.times{@output.write '#'}
|
||||||
@output.write '>'
|
@output.write '>'
|
||||||
(width-complete).times{@output.write ' '}
|
(width-complete).times{@output.write ' '}
|
||||||
|
|
|
@ -18,7 +18,6 @@ module Hydra #:nodoc:
|
||||||
def file_end(file, output)
|
def file_end(file, output)
|
||||||
@report[file]['end'] = Time.now.to_f
|
@report[file]['end'] = Time.now.to_f
|
||||||
@report[file]['duration'] = @report[file]['end'] - @report[file]['start']
|
@report[file]['duration'] = @report[file]['end'] - @report[file]['start']
|
||||||
@report[file]['all_tests_passed_last_run'] = (output == '.')
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# output the report
|
# output the report
|
||||||
|
@ -29,5 +28,3 @@ module Hydra #:nodoc:
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,22 +1,15 @@
|
||||||
require 'hydra/hash'
|
require 'hydra/hash'
|
||||||
require 'hydra/config'
|
|
||||||
require 'open3'
|
require 'open3'
|
||||||
require 'hydra/tmpdir'
|
require 'tmpdir'
|
||||||
require 'erb'
|
|
||||||
require 'yaml'
|
require 'yaml'
|
||||||
|
|
||||||
module Hydra #:nodoc:
|
module Hydra #:nodoc:
|
||||||
# Hydra class responsible for delegate work down to workers.
|
# Hydra class responsible for delegate work down to workers.
|
||||||
#
|
#
|
||||||
# The Master is run once for any given testing session.
|
# The Master is run once for any given testing session.
|
||||||
class YmlLoadError < StandardError; end
|
|
||||||
|
|
||||||
class Master
|
class Master
|
||||||
include Hydra::Messages::Master
|
include Hydra::Messages::Master
|
||||||
include Open3
|
include Open3
|
||||||
traceable('MASTER')
|
traceable('MASTER')
|
||||||
attr_reader :failed_files
|
|
||||||
|
|
||||||
# Create a new Master
|
# Create a new Master
|
||||||
#
|
#
|
||||||
# Options:
|
# Options:
|
||||||
|
@ -38,14 +31,11 @@ module Hydra #:nodoc:
|
||||||
opts.stringify_keys!
|
opts.stringify_keys!
|
||||||
config_file = opts.delete('config') { nil }
|
config_file = opts.delete('config') { nil }
|
||||||
if config_file
|
if config_file
|
||||||
config_yml = Hydra::Config.load(config_file)
|
opts.merge!(YAML.load_file(config_file).stringify_keys!)
|
||||||
|
|
||||||
opts.merge!(config_yml.stringify_keys!)
|
|
||||||
end
|
end
|
||||||
@files = Array(opts.fetch('files') { nil })
|
@files = Array(opts.fetch('files') { nil })
|
||||||
raise "No files, nothing to do" if @files.empty?
|
raise "No files, nothing to do" if @files.empty?
|
||||||
@incomplete_files = @files.dup
|
@incomplete_files = @files.dup
|
||||||
@failed_files = []
|
|
||||||
@workers = []
|
@workers = []
|
||||||
@listeners = []
|
@listeners = []
|
||||||
@event_listeners = Array(opts.fetch('listeners') { nil } )
|
@event_listeners = Array(opts.fetch('listeners') { nil } )
|
||||||
|
@ -54,15 +44,9 @@ module Hydra #:nodoc:
|
||||||
listener = eval(l)
|
listener = eval(l)
|
||||||
@event_listeners << listener if listener.is_a?(Hydra::Listener::Abstract)
|
@event_listeners << listener if listener.is_a?(Hydra::Listener::Abstract)
|
||||||
end
|
end
|
||||||
|
|
||||||
@string_runner_event_listeners = Array( opts.fetch( 'runner_listeners' ) { nil } )
|
|
||||||
|
|
||||||
@runner_log_file = opts.fetch('runner_log_file') { nil }
|
|
||||||
@verbose = opts.fetch('verbose') { false }
|
@verbose = opts.fetch('verbose') { false }
|
||||||
@autosort = opts.fetch('autosort') { true }
|
@autosort = opts.fetch('autosort') { true }
|
||||||
@sync = opts.fetch('sync') { nil }
|
@sync = opts.fetch('sync') { nil }
|
||||||
@environment = opts.fetch('environment') { 'test' } || 'test'
|
|
||||||
@options = opts.fetch('options') { '' }
|
|
||||||
|
|
||||||
if @autosort
|
if @autosort
|
||||||
sort_files_from_report
|
sort_files_from_report
|
||||||
|
@ -72,18 +56,6 @@ module Hydra #:nodoc:
|
||||||
# default is one worker that is configured to use a pipe with one runner
|
# default is one worker that is configured to use a pipe with one runner
|
||||||
worker_cfg = opts.fetch('workers') { [ { 'type' => 'local', 'runners' => 1} ] }
|
worker_cfg = opts.fetch('workers') { [ { 'type' => 'local', 'runners' => 1} ] }
|
||||||
|
|
||||||
# if the number of files to run is equal to or less than the count of local
|
|
||||||
# runners, don't even bother with retmote workers.
|
|
||||||
locals, sshes = worker_cfg.partition { |l| l['type'] == 'local' }
|
|
||||||
|
|
||||||
if !locals.empty?
|
|
||||||
if @files.length <= locals.first['runners']
|
|
||||||
sshes = []
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
worker_cfg = locals + sshes
|
|
||||||
|
|
||||||
trace "Initialized"
|
trace "Initialized"
|
||||||
trace " Files: (#{@files.inspect})"
|
trace " Files: (#{@files.inspect})"
|
||||||
trace " Workers: (#{worker_cfg.inspect})"
|
trace " Workers: (#{worker_cfg.inspect})"
|
||||||
|
@ -96,9 +68,6 @@ module Hydra #:nodoc:
|
||||||
end
|
end
|
||||||
|
|
||||||
# Message handling
|
# Message handling
|
||||||
def worker_begin(worker)
|
|
||||||
@event_listeners.each {|l| l.worker_begin(worker) }
|
|
||||||
end
|
|
||||||
|
|
||||||
# Send a file down to a worker.
|
# Send a file down to a worker.
|
||||||
def send_file(worker)
|
def send_file(worker)
|
||||||
|
@ -114,29 +83,13 @@ module Hydra #:nodoc:
|
||||||
|
|
||||||
# Process the results coming back from the worker.
|
# Process the results coming back from the worker.
|
||||||
def process_results(worker, message)
|
def process_results(worker, message)
|
||||||
if message.output =~ /ActiveRecord::StatementInvalid(.*)[Dd]eadlock/ or
|
@incomplete_files.delete_at(@incomplete_files.index(message.file))
|
||||||
message.output =~ /PGError: ERROR(.*)[Dd]eadlock/ or
|
trace "#{@incomplete_files.size} Files Remaining"
|
||||||
message.output =~ /Mysql::Error: SAVEPOINT(.*)does not exist: ROLLBACK/ or
|
@event_listeners.each{|l| l.file_end(message.file, message.output) }
|
||||||
message.output =~ /Mysql::Error: Deadlock found/
|
if @incomplete_files.empty?
|
||||||
trace "Deadlock detected running [#{message.file}]. Will retry at the end"
|
shutdown_all_workers
|
||||||
@files.push(message.file)
|
|
||||||
send_file(worker)
|
|
||||||
else
|
else
|
||||||
@incomplete_files.delete_at(@incomplete_files.index(message.file))
|
send_file(worker)
|
||||||
trace "#{@incomplete_files.size} Files Remaining"
|
|
||||||
@event_listeners.each{|l| l.file_end(message.file, message.output) }
|
|
||||||
unless message.output == '.'
|
|
||||||
@failed_files << message.file
|
|
||||||
end
|
|
||||||
if @incomplete_files.empty?
|
|
||||||
@workers.each do |worker|
|
|
||||||
@event_listeners.each{|l| l.worker_end(worker) }
|
|
||||||
end
|
|
||||||
|
|
||||||
shutdown_all_workers
|
|
||||||
else
|
|
||||||
send_file(worker)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -167,28 +120,46 @@ module Hydra #:nodoc:
|
||||||
pipe = Hydra::Pipe.new
|
pipe = Hydra::Pipe.new
|
||||||
child = SafeFork.fork do
|
child = SafeFork.fork do
|
||||||
pipe.identify_as_child
|
pipe.identify_as_child
|
||||||
Hydra::Worker.new(:io => pipe, :runners => runners, :verbose => @verbose, :runner_listeners => @string_runner_event_listeners, :runner_log_file => @runner_log_file, :options => @options )
|
Hydra::Worker.new(:io => pipe, :runners => runners, :verbose => @verbose)
|
||||||
end
|
end
|
||||||
|
|
||||||
pipe.identify_as_parent
|
pipe.identify_as_parent
|
||||||
@workers << { :pid => child, :io => pipe, :idle => false, :type => :local }
|
@workers << { :pid => child, :io => pipe, :idle => false, :type => :local }
|
||||||
end
|
end
|
||||||
|
|
||||||
def boot_ssh_worker(worker)
|
def boot_ssh_worker(worker)
|
||||||
sync = Sync.new(worker, @sync, @verbose)
|
runners = worker.fetch('runners') { raise "You must specify the number of runners" }
|
||||||
if sync.result == 0
|
connect = worker.fetch('connect') { raise "You must specify an SSH connection target" }
|
||||||
runners = worker.fetch('runners') { raise "You must specify the number of runners" }
|
ssh_opts = worker.fetch('ssh_opts') { "" }
|
||||||
command = worker.fetch('command') {
|
directory = worker.fetch('directory') { raise "You must specify a remote directory" }
|
||||||
%{RAILS_ENV=#{@environment} ruby -rrubygems -e "require \\"bundler/setup\\"; require \\"hydra\\"; Hydra::Worker.new(:io => Hydra::Stdio.new, :runners => #{runners}, :verbose => #{@verbose}, :runner_listeners => \\"#{@string_runner_event_listeners}\\", :runner_log_file => \\"#{@runner_log_file}\\", :options => {} );"}
|
command = worker.fetch('command') {
|
||||||
}
|
"ruby -e \"require 'rubygems'; require 'hydra'; Hydra::Worker.new(:io => Hydra::Stdio.new, :runners => #{runners}, :verbose => #{@verbose});\""
|
||||||
|
}
|
||||||
|
|
||||||
trace "Booting SSH worker"
|
if @sync
|
||||||
trace command
|
@sync.stringify_keys!
|
||||||
ssh = Hydra::SSH.new("#{sync.ssh_opts} #{sync.connect}", sync.remote_dir, command, worker['timeout'])
|
trace "Synchronizing with #{connect}\n\t#{@sync.inspect}"
|
||||||
return { :io => ssh, :idle => false, :type => :ssh, :connect => sync.connect }
|
local_dir = @sync.fetch('directory') {
|
||||||
else
|
raise "You must specify a synchronization directory"
|
||||||
false
|
}
|
||||||
|
exclude_paths = @sync.fetch('exclude') { [] }
|
||||||
|
exclude_opts = exclude_paths.inject(''){|memo, path| memo += "--exclude=#{path} "}
|
||||||
|
|
||||||
|
rsync_command = [
|
||||||
|
'rsync',
|
||||||
|
'-avz',
|
||||||
|
'--delete',
|
||||||
|
exclude_opts,
|
||||||
|
File.expand_path(local_dir)+'/',
|
||||||
|
"-e \"ssh #{ssh_opts}\"",
|
||||||
|
"#{connect}:#{directory}"
|
||||||
|
].join(" ")
|
||||||
|
trace rsync_command
|
||||||
|
trace `#{rsync_command}`
|
||||||
end
|
end
|
||||||
|
|
||||||
|
trace "Booting SSH worker"
|
||||||
|
ssh = Hydra::SSH.new("#{ssh_opts} #{connect}", directory, command)
|
||||||
|
return { :io => ssh, :idle => false, :type => :ssh }
|
||||||
end
|
end
|
||||||
|
|
||||||
def shutdown_all_workers
|
def shutdown_all_workers
|
||||||
|
@ -210,26 +181,20 @@ module Hydra #:nodoc:
|
||||||
trace "Listening to #{worker.inspect}"
|
trace "Listening to #{worker.inspect}"
|
||||||
if worker.fetch('type') { 'local' }.to_s == 'ssh'
|
if worker.fetch('type') { 'local' }.to_s == 'ssh'
|
||||||
worker = boot_ssh_worker(worker)
|
worker = boot_ssh_worker(worker)
|
||||||
@workers << worker if worker
|
@workers << worker
|
||||||
end
|
end
|
||||||
if worker
|
while true
|
||||||
dead_count = 0
|
begin
|
||||||
while true
|
message = worker[:io].gets
|
||||||
begin
|
trace "got message: #{message}"
|
||||||
message = worker[:io].gets
|
# if it exists and its for me.
|
||||||
trace "got message: #{message}"
|
# SSH gives us back echoes, so we need to ignore our own messages
|
||||||
# if it exists and its for me.
|
if message and !message.class.to_s.index("Worker").nil?
|
||||||
# SSH gives us back echoes, so we need to ignore our own messages
|
message.handle(self, worker)
|
||||||
if message and !message.class.to_s.index("Worker").nil?
|
|
||||||
message.handle(self, worker)
|
|
||||||
else
|
|
||||||
dead_count += 1
|
|
||||||
raise IOError if dead_count > 100
|
|
||||||
end
|
|
||||||
rescue IOError
|
|
||||||
trace "lost Worker [#{worker.inspect}]"
|
|
||||||
Thread.exit
|
|
||||||
end
|
end
|
||||||
|
rescue IOError
|
||||||
|
trace "lost Worker [#{worker.inspect}]"
|
||||||
|
Thread.exit
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -254,7 +219,7 @@ module Hydra #:nodoc:
|
||||||
end
|
end
|
||||||
|
|
||||||
def heuristic_file
|
def heuristic_file
|
||||||
@heuristic_file ||= File.join(Dir.consistent_tmpdir, 'hydra_heuristics.yml')
|
@heuristic_file ||= File.join(Dir.tmpdir, 'hydra_heuristics.yml')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -8,12 +8,6 @@ module Hydra #:nodoc:
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
class WorkerBegin < Hydra::Message
|
|
||||||
def handle(master, worker)
|
|
||||||
master.worker_begin(worker)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Message telling the Runner to run a file
|
# Message telling the Runner to run a file
|
||||||
class RunFile < Hydra::Message
|
class RunFile < Hydra::Message
|
||||||
# The file that should be run
|
# The file that should be run
|
||||||
|
|
|
@ -8,21 +8,14 @@ module Hydra #:nodoc:
|
||||||
# IO.gets
|
# IO.gets
|
||||||
# => Hydra::Message # or subclass
|
# => Hydra::Message # or subclass
|
||||||
def gets
|
def gets
|
||||||
while true
|
raise IOError unless @reader
|
||||||
begin
|
message = @reader.gets
|
||||||
raise IOError unless @reader
|
return nil unless message
|
||||||
message = nil
|
return Message.build(eval(message.chomp))
|
||||||
if result = Kernel.select([@reader], [], [], @timeout)
|
rescue SyntaxError, NameError
|
||||||
message = @reader.gets
|
# uncomment to help catch remote errors by seeing all traffic
|
||||||
end
|
#$stderr.write "Not a message: [#{message.inspect}]\n"
|
||||||
return Message.build(:class => Hydra::Messages::Master::Shutdown) if result == nil
|
return gets
|
||||||
return nil unless message
|
|
||||||
return Message.build(eval(message.chomp))
|
|
||||||
rescue SyntaxError, NameError, Errno::EBADF
|
|
||||||
# uncomment to help catch remote errors by seeing all traffic
|
|
||||||
#$stderr.write "Not a message: [#{message.inspect}]\n"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Write a Message to the output IO object. It will automatically
|
# Write a Message to the output IO object. It will automatically
|
||||||
|
|
|
@ -31,10 +31,9 @@ module Hydra #:nodoc:
|
||||||
class Pipe
|
class Pipe
|
||||||
include Hydra::MessagingIO
|
include Hydra::MessagingIO
|
||||||
# Creates a new uninitialized pipe pair.
|
# Creates a new uninitialized pipe pair.
|
||||||
def initialize(timeout = nil)
|
def initialize
|
||||||
@child_read, @parent_write = IO.pipe
|
@child_read, @parent_write = IO.pipe
|
||||||
@parent_read, @child_write = IO.pipe
|
@parent_read, @child_write = IO.pipe
|
||||||
@timeout = timeout
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Identify this side of the pipe as the child.
|
# Identify this side of the pipe as the child.
|
||||||
|
|
|
@ -1,3 +1,16 @@
|
||||||
|
require 'minitest/unit'
|
||||||
|
require 'test/unit/assertions'
|
||||||
|
#require 'test/unit/testresult'
|
||||||
|
#Test::Unit.run = true
|
||||||
|
module MiniTest
|
||||||
|
class Unit
|
||||||
|
def run(args)
|
||||||
|
puts 'not running!'
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
module Hydra #:nodoc:
|
module Hydra #:nodoc:
|
||||||
# Hydra class responsible for running test files.
|
# Hydra class responsible for running test files.
|
||||||
#
|
#
|
||||||
|
@ -9,26 +22,14 @@ module Hydra #:nodoc:
|
||||||
class Runner
|
class Runner
|
||||||
include Hydra::Messages::Runner
|
include Hydra::Messages::Runner
|
||||||
traceable('RUNNER')
|
traceable('RUNNER')
|
||||||
|
|
||||||
DEFAULT_LOG_FILE = 'hydra-runner.log'
|
|
||||||
PING_COUNT_FAILURE_TIME = 5
|
|
||||||
WAIT_BETWEEN_PING = 0.1
|
|
||||||
|
|
||||||
# Boot up a runner. It takes an IO object (generally a pipe from its
|
# Boot up a runner. It takes an IO object (generally a pipe from its
|
||||||
# parent) to send it messages on which files to execute.
|
# parent) to send it messages on which files to execute.
|
||||||
def initialize(opts = {})
|
def initialize(opts = {})
|
||||||
redirect_output( opts.fetch( :runner_log_file ) { DEFAULT_LOG_FILE } )
|
|
||||||
reg_trap_sighup
|
|
||||||
|
|
||||||
@io = opts.fetch(:io) { raise "No IO Object" }
|
@io = opts.fetch(:io) { raise "No IO Object" }
|
||||||
@verbose = opts.fetch(:verbose) { false }
|
@verbose = opts.fetch(:verbose) { false }
|
||||||
@event_listeners = Array( opts.fetch( :runner_listeners ) { nil } )
|
|
||||||
@options = opts.fetch(:options)
|
|
||||||
|
|
||||||
$stdout.sync = true
|
$stdout.sync = true
|
||||||
runner_begin
|
|
||||||
|
|
||||||
trace 'Booted. Sending Request for file'
|
trace 'Booted. Sending Request for file'
|
||||||
|
|
||||||
@io.write RequestFile.new
|
@io.write RequestFile.new
|
||||||
begin
|
begin
|
||||||
process_messages
|
process_messages
|
||||||
|
@ -38,32 +39,15 @@ module Hydra #:nodoc:
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def reg_trap_sighup
|
|
||||||
for sign in [:SIGHUP, :INT]
|
|
||||||
trap sign do
|
|
||||||
stop
|
|
||||||
exit if @event_listeners.empty?
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@runner_began = true
|
|
||||||
end
|
|
||||||
|
|
||||||
def runner_begin
|
|
||||||
trace "Firing runner_begin event"
|
|
||||||
@event_listeners.each {|l| l.runner_begin( self ) }
|
|
||||||
end
|
|
||||||
|
|
||||||
# Run a test file and report the results
|
# Run a test file and report the results
|
||||||
def run_file(file)
|
def run_file(file)
|
||||||
trace "Running file: #{file}"
|
trace "Running file: #{file}"
|
||||||
|
|
||||||
output = ""
|
output = ""
|
||||||
if file =~ /_spec.rb$/i
|
if file =~ /_spec.rb$/
|
||||||
output = run_rspec_file(file)
|
output = run_rspec_file(file)
|
||||||
elsif file =~ /.feature$/i
|
elsif file =~ /.feature$/
|
||||||
output = run_cucumber_file(file)
|
output = run_cucumber_file(file)
|
||||||
elsif file =~ /.js$/i or file =~ /.json$/i
|
|
||||||
output = run_javascript_file(file)
|
|
||||||
else
|
else
|
||||||
output = run_test_unit_file(file)
|
output = run_test_unit_file(file)
|
||||||
end
|
end
|
||||||
|
@ -76,17 +60,11 @@ module Hydra #:nodoc:
|
||||||
|
|
||||||
# Stop running
|
# Stop running
|
||||||
def stop
|
def stop
|
||||||
runner_end if @runner_began
|
@running = false
|
||||||
@runner_began = @running = false
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def runner_end
|
def puke(klass, name, exception)
|
||||||
trace "Ending runner #{self.inspect}"
|
puts "puke! #{klass}, #{name}, #{exception}"
|
||||||
@event_listeners.each {|l| l.runner_end( self ) }
|
|
||||||
end
|
|
||||||
|
|
||||||
def format_exception(ex)
|
|
||||||
"#{ex.class.name}: #{ex.message}\n #{ex.backtrace.join("\n ")}"
|
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
@ -104,46 +82,46 @@ module Hydra #:nodoc:
|
||||||
message.handle(self)
|
message.handle(self)
|
||||||
else
|
else
|
||||||
@io.write Ping.new
|
@io.write Ping.new
|
||||||
sleep WAIT_BETWEEN_PING
|
|
||||||
end
|
end
|
||||||
rescue IOError => ex
|
rescue IOError => ex
|
||||||
trace "Runner lost Worker"
|
trace "Runner lost Worker"
|
||||||
stop
|
@running = false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def format_ex_in_file(file, ex)
|
|
||||||
"Error in #{file}:\n #{format_exception(ex)}"
|
|
||||||
end
|
|
||||||
|
|
||||||
# Run all the Test::Unit Suites in a ruby file
|
# Run all the Test::Unit Suites in a ruby file
|
||||||
def run_test_unit_file(file)
|
def run_test_unit_file(file)
|
||||||
begin
|
begin
|
||||||
gem 'test-unit'
|
|
||||||
require 'test/unit'
|
|
||||||
require 'test/unit/testresult'
|
|
||||||
Test::Unit.run = true
|
|
||||||
require file
|
require file
|
||||||
rescue LoadError => ex
|
rescue LoadError => ex
|
||||||
trace "#{file} does not exist [#{ex.to_s}]"
|
trace "#{file} does not exist [#{ex.to_s}]"
|
||||||
return ex.to_s
|
return ex.to_s
|
||||||
rescue Exception => ex
|
|
||||||
p ex
|
|
||||||
trace "Error requiring #{file} [#{ex.to_s}]"
|
|
||||||
return format_ex_in_file(file, ex)
|
|
||||||
end
|
end
|
||||||
output = []
|
output = []
|
||||||
@result = Test::Unit::TestResult.new
|
# @result = Test::Unit::TestResult.new
|
||||||
@result.add_listener(Test::Unit::TestResult::FAULT) do |value|
|
# @result.add_listener(Test::Unit::TestResult::FAULT) do |value|
|
||||||
output << value
|
# output << value
|
||||||
end
|
# end
|
||||||
|
|
||||||
|
|
||||||
klasses = Runner.find_classes_in_file(file)
|
klasses = Runner.find_classes_in_file(file)
|
||||||
|
puts "Klasses: #{klasses.inspect}"
|
||||||
begin
|
begin
|
||||||
klasses.each{|klass| klass.suite.run(@result){|status, name| ;}}
|
klasses.each{|klass|
|
||||||
|
#klass.suite.run(@result){|status, name| ;}
|
||||||
|
klass.test_suites.each do |suite|
|
||||||
|
suite.test_methods.each do |test|
|
||||||
|
inst = suite.new(test)
|
||||||
|
inst._assertions = 0
|
||||||
|
result = inst.run(self)
|
||||||
|
#puts result
|
||||||
|
end
|
||||||
|
end
|
||||||
|
}
|
||||||
rescue => ex
|
rescue => ex
|
||||||
output << format_ex_in_file(file, ex)
|
output << ex.to_s
|
||||||
end
|
end
|
||||||
|
|
||||||
return output.join("\n")
|
return output.join("\n")
|
||||||
|
@ -153,111 +131,74 @@ module Hydra #:nodoc:
|
||||||
def run_rspec_file(file)
|
def run_rspec_file(file)
|
||||||
# pull in rspec
|
# pull in rspec
|
||||||
begin
|
begin
|
||||||
require 'rspec'
|
require 'spec'
|
||||||
|
require 'hydra/spec/hydra_formatter'
|
||||||
# Ensure we override rspec's at_exit
|
# Ensure we override rspec's at_exit
|
||||||
RSpec::Core::Runner.disable_autorun!
|
require 'hydra/spec/autorun_override'
|
||||||
rescue LoadError => ex
|
rescue LoadError => ex
|
||||||
return ex.to_s
|
return ex.to_s
|
||||||
end
|
end
|
||||||
@hydra_output ||= StringIO.new
|
hydra_output = StringIO.new
|
||||||
@hydra_output.rewind
|
Spec::Runner.options.instance_variable_set(:@formatters, [
|
||||||
@hydra_output.truncate(0)
|
Spec::Runner::Formatter::HydraFormatter.new(
|
||||||
|
Spec::Runner.options.formatter_options,
|
||||||
|
hydra_output
|
||||||
|
)
|
||||||
|
])
|
||||||
|
Spec::Runner.options.instance_variable_set(
|
||||||
|
:@example_groups, []
|
||||||
|
)
|
||||||
|
Spec::Runner.options.instance_variable_set(
|
||||||
|
:@files, [file]
|
||||||
|
)
|
||||||
|
Spec::Runner.options.instance_variable_set(
|
||||||
|
:@files_loaded, false
|
||||||
|
)
|
||||||
|
Spec::Runner.options.run_examples
|
||||||
|
hydra_output.rewind
|
||||||
|
output = hydra_output.read.chomp
|
||||||
|
output = "" if output.gsub("\n","") =~ /^\.*$/
|
||||||
|
|
||||||
config = [ file ]
|
return output
|
||||||
RSpec.reset
|
|
||||||
begin
|
|
||||||
result = RSpec::Core::Runner.run(config, @hydra_output, @hydra_output)
|
|
||||||
rescue Exception => ex
|
|
||||||
return ex.to_s + "\n" + ex.backtrace.reject { |line| RSpec.configuration.cleaned_from_backtrace?(line) }.join("\n")
|
|
||||||
end
|
|
||||||
@hydra_output.rewind
|
|
||||||
|
|
||||||
return (result == 1) ? @hydra_output.read : ""
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# run all the scenarios in a cucumber feature file
|
# run all the scenarios in a cucumber feature file
|
||||||
def run_cucumber_file(file)
|
def run_cucumber_file(file)
|
||||||
|
|
||||||
|
files = [file]
|
||||||
|
dev_null = StringIO.new
|
||||||
hydra_response = StringIO.new
|
hydra_response = StringIO.new
|
||||||
|
|
||||||
options = @options if @options.is_a?(Array)
|
unless @step_mother
|
||||||
options = @options.split(' ') if @options.is_a?(String)
|
require 'cucumber'
|
||||||
|
|
||||||
fork_id = fork do
|
|
||||||
files = [file]
|
|
||||||
dev_null = StringIO.new
|
|
||||||
|
|
||||||
args = [file, options].flatten.compact
|
|
||||||
hydra_response.puts args.inspect
|
|
||||||
|
|
||||||
results_directory = "#{Dir.pwd}/results/features"
|
|
||||||
FileUtils.mkdir_p results_directory
|
|
||||||
|
|
||||||
require 'cucumber/cli/main'
|
|
||||||
require 'hydra/cucumber/formatter'
|
require 'hydra/cucumber/formatter'
|
||||||
require 'hydra/cucumber/partial_html'
|
@step_mother = Cucumber::StepMother.new
|
||||||
|
@cuke_configuration = Cucumber::Cli::Configuration.new(dev_null, dev_null)
|
||||||
|
@cuke_configuration.parse!(['features']+files)
|
||||||
|
|
||||||
Cucumber.logger.level = Logger::INFO
|
@step_mother.options = @cuke_configuration.options
|
||||||
|
@step_mother.log = @cuke_configuration.log
|
||||||
cuke = Cucumber::Cli::Main.new(args, dev_null, dev_null)
|
@step_mother.load_code_files(@cuke_configuration.support_to_load)
|
||||||
cuke.configuration.formats << ['Cucumber::Formatter::Hydra', hydra_response]
|
@step_mother.after_configuration(@cuke_configuration)
|
||||||
|
@step_mother.load_code_files(@cuke_configuration.step_defs_to_load)
|
||||||
html_output = cuke.configuration.formats.select{|format| format[0] == 'html'}
|
|
||||||
if html_output
|
|
||||||
cuke.configuration.formats.delete(html_output)
|
|
||||||
cuke.configuration.formats << ['Hydra::Formatter::PartialHtml', "#{results_directory}/#{file.split('/').last}.html"]
|
|
||||||
end
|
|
||||||
|
|
||||||
cuke_runtime = Cucumber::Runtime.new(cuke.configuration)
|
|
||||||
cuke_runtime.run!
|
|
||||||
exit 1 if cuke_runtime.results.failure?
|
|
||||||
end
|
end
|
||||||
Process.wait fork_id
|
cuke_formatter = Cucumber::Formatter::Hydra.new(
|
||||||
|
@step_mother, hydra_response, @cuke_configuration.options
|
||||||
|
)
|
||||||
|
|
||||||
|
cuke_runner ||= Cucumber::Ast::TreeWalker.new(
|
||||||
|
@step_mother, [cuke_formatter], @cuke_configuration.options, dev_null
|
||||||
|
)
|
||||||
|
@step_mother.visitor = cuke_runner
|
||||||
|
|
||||||
|
features = @step_mother.load_plain_text_features(files)
|
||||||
|
tag_excess = tag_excess(features, @cuke_configuration.options[:tag_expression].limits)
|
||||||
|
@cuke_configuration.options[:tag_excess] = tag_excess
|
||||||
|
|
||||||
|
cuke_runner.visit_features(features)
|
||||||
|
|
||||||
hydra_response.puts "." if not $?.exitstatus == 0
|
|
||||||
hydra_response.rewind
|
hydra_response.rewind
|
||||||
|
return hydra_response.read
|
||||||
hydra_response.read
|
|
||||||
end
|
|
||||||
|
|
||||||
def run_javascript_file(file)
|
|
||||||
errors = []
|
|
||||||
require 'v8'
|
|
||||||
V8::Context.new do |context|
|
|
||||||
context.load(File.expand_path(File.join(File.dirname(__FILE__), 'js', 'lint.js')))
|
|
||||||
context['input'] = lambda{
|
|
||||||
File.read(file)
|
|
||||||
}
|
|
||||||
context['reportErrors'] = lambda{|js_errors|
|
|
||||||
js_errors.each do |e|
|
|
||||||
e = V8::To.rb(e)
|
|
||||||
errors << "\n\e[1;31mJSLINT: #{file}\e[0m"
|
|
||||||
errors << " Error at line #{e['line'].to_i + 1} " +
|
|
||||||
"character #{e['character'].to_i + 1}: \e[1;33m#{e['reason']}\e[0m"
|
|
||||||
errors << "#{e['evidence']}"
|
|
||||||
end
|
|
||||||
}
|
|
||||||
context.eval %{
|
|
||||||
JSLINT(input(), {
|
|
||||||
sub: true,
|
|
||||||
onevar: true,
|
|
||||||
eqeqeq: true,
|
|
||||||
plusplus: true,
|
|
||||||
bitwise: true,
|
|
||||||
regexp: true,
|
|
||||||
newcap: true,
|
|
||||||
immed: true,
|
|
||||||
strict: true,
|
|
||||||
rhino: true
|
|
||||||
});
|
|
||||||
reportErrors(JSLINT.errors);
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
if errors.empty?
|
|
||||||
return '.'
|
|
||||||
else
|
|
||||||
return errors.join("\n")
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# find all the test unit classes in a given file, so we can run their suites
|
# find all the test unit classes in a given file, so we can run their suites
|
||||||
|
@ -281,7 +222,11 @@ module Hydra #:nodoc:
|
||||||
nil
|
nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return klasses.select{|k| k.respond_to? 'suite'}
|
puts "Pre klasses: #{klasses.inspect}"
|
||||||
|
puts klasses
|
||||||
|
|
||||||
|
|
||||||
|
return klasses.select{|k| k.respond_to? 'test_suites'}
|
||||||
end
|
end
|
||||||
|
|
||||||
# Yanked a method from Cucumber
|
# Yanked a method from Cucumber
|
||||||
|
@ -295,16 +240,5 @@ module Hydra #:nodoc:
|
||||||
end
|
end
|
||||||
end.compact
|
end.compact
|
||||||
end
|
end
|
||||||
|
|
||||||
def redirect_output file_name
|
|
||||||
begin
|
|
||||||
$stderr = $stdout = File.open(file_name, 'a')
|
|
||||||
rescue
|
|
||||||
# it should always redirect output in order to handle unexpected interruption
|
|
||||||
# successfully
|
|
||||||
$stderr = $stdout = File.open(DEFAULT_LOG_FILE, 'a')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,23 +0,0 @@
|
||||||
module Hydra #:nodoc:
|
|
||||||
module RunnerListener #:nodoc:
|
|
||||||
# Abstract listener that implements all the events
|
|
||||||
# but does nothing.
|
|
||||||
class Abstract
|
|
||||||
# Create a new listener.
|
|
||||||
#
|
|
||||||
# Output: The IO object for outputting any information.
|
|
||||||
# Defaults to STDOUT, but you could pass a file in, or STDERR
|
|
||||||
def initialize(output = $stdout)
|
|
||||||
@output = output
|
|
||||||
end
|
|
||||||
|
|
||||||
# Fired by the runner just before requesting the first file
|
|
||||||
def runner_begin( runner )
|
|
||||||
end
|
|
||||||
|
|
||||||
# Fired by the runner just after stoping
|
|
||||||
def runner_end( runner )
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -2,13 +2,13 @@ class SafeFork
|
||||||
def self.fork
|
def self.fork
|
||||||
begin
|
begin
|
||||||
# remove our connection so it doesn't get cloned
|
# remove our connection so it doesn't get cloned
|
||||||
connection = ActiveRecord::Base.remove_connection if defined?(ActiveRecord)
|
ActiveRecord::Base.remove_connection if defined?(ActiveRecord)
|
||||||
# fork a process
|
# fork a process
|
||||||
child = Process.fork do
|
child = Process.fork do
|
||||||
begin
|
begin
|
||||||
# create a new connection and perform the action
|
# create a new connection and perform the action
|
||||||
begin
|
begin
|
||||||
ActiveRecord::Base.establish_connection((connection || {}).merge({:allow_concurrency => true})) if defined?(ActiveRecord)
|
ActiveRecord::Base.establish_connection if defined?(ActiveRecord)
|
||||||
rescue ActiveRecord::AdapterNotSpecified
|
rescue ActiveRecord::AdapterNotSpecified
|
||||||
# AR was defined but we didn't have a connection
|
# AR was defined but we didn't have a connection
|
||||||
end
|
end
|
||||||
|
@ -21,7 +21,7 @@ class SafeFork
|
||||||
ensure
|
ensure
|
||||||
# make sure we re-establish the connection before returning to the main instance
|
# make sure we re-establish the connection before returning to the main instance
|
||||||
begin
|
begin
|
||||||
ActiveRecord::Base.establish_connection((connection || {}).merge({:allow_concurrency => true})) if defined?(ActiveRecord)
|
ActiveRecord::Base.establish_connection if defined?(ActiveRecord)
|
||||||
rescue ActiveRecord::AdapterNotSpecified
|
rescue ActiveRecord::AdapterNotSpecified
|
||||||
# AR was defined but we didn't have a connection
|
# AR was defined but we didn't have a connection
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,3 +1,12 @@
|
||||||
if defined?(RSpec)
|
if defined?(Spec)
|
||||||
RSpec::Core::Runner.disable_autorun!
|
module Spec
|
||||||
|
module Runner
|
||||||
|
class << self
|
||||||
|
# stop the auto-run at_exit
|
||||||
|
def run
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,22 +1,11 @@
|
||||||
require 'rspec/core/formatters/progress_formatter'
|
require 'spec/runner/formatter/progress_bar_formatter'
|
||||||
module RSpec
|
module Spec
|
||||||
module Core
|
module Runner
|
||||||
module Formatters
|
module Formatter
|
||||||
class HydraFormatter < ProgressFormatter
|
class HydraFormatter < ProgressBarFormatter
|
||||||
def example_passed(example)
|
|
||||||
output.print "."
|
|
||||||
end
|
|
||||||
|
|
||||||
def example_failed(example)
|
|
||||||
output.print "F"
|
|
||||||
end
|
|
||||||
# Stifle the post-test summary
|
# Stifle the post-test summary
|
||||||
def dump_summary(duration, example, failure, pending)
|
def dump_summary(duration, example, failure, pending)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Stifle pending specs
|
|
||||||
def dump_pending
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -25,9 +25,16 @@ module Hydra #:nodoc:
|
||||||
# Hydra::SSH.new('-p 3022 user@server.com', '/home/user/Desktop', 'ls -l')
|
# Hydra::SSH.new('-p 3022 user@server.com', '/home/user/Desktop', 'ls -l')
|
||||||
# To connect to server.com as user on port 3022, then CD to their desktop, then
|
# To connect to server.com as user on port 3022, then CD to their desktop, then
|
||||||
# list all the files.
|
# list all the files.
|
||||||
def initialize(connection_options, directory, command, timeout = nil)
|
def initialize(connection_options, directory, command)
|
||||||
@timeout = timeout
|
@writer, @reader, @error = popen3("ssh -tt #{connection_options}")
|
||||||
@writer, @reader, @error = popen3(%{ssh -tt #{connection_options} 'mkdir -p #{directory} && cd #{directory} && #{command}'})
|
@writer.write("cd #{directory}\n")
|
||||||
|
@writer.write(command+"\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
# Close the SSH connection
|
||||||
|
def close
|
||||||
|
@writer.write "exit\n"
|
||||||
|
super
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,107 +0,0 @@
|
||||||
require 'yaml'
|
|
||||||
require 'hydra/config'
|
|
||||||
|
|
||||||
module Hydra #:nodoc:
|
|
||||||
# Hydra class responsible for delegate work down to workers.
|
|
||||||
#
|
|
||||||
# The Sync is run once for each remote worker.
|
|
||||||
class Sync
|
|
||||||
traceable('SYNC')
|
|
||||||
self.class.traceable('SYNC MANY')
|
|
||||||
|
|
||||||
attr_reader :connect, :ssh_opts, :remote_dir, :result
|
|
||||||
|
|
||||||
# Create a new Sync instance to rsync source from the local machine to a remote worker
|
|
||||||
#
|
|
||||||
# Arguments:
|
|
||||||
# * :worker_opts
|
|
||||||
# * A hash of the configuration options for a worker.
|
|
||||||
# * :sync
|
|
||||||
# * A hash of settings specifically for copying the source directory to be tested
|
|
||||||
# to the remote worked
|
|
||||||
# * :verbose
|
|
||||||
# * Set to true to see lots of Hydra output (for debugging)
|
|
||||||
def initialize(worker_opts, sync_opts, verbose = false)
|
|
||||||
worker_opts ||= {}
|
|
||||||
worker_opts.stringify_keys!
|
|
||||||
@verbose = verbose
|
|
||||||
@connect = worker_opts.fetch('connect') { raise "You must specify an SSH connection target" }
|
|
||||||
@ssh_opts = worker_opts.fetch('ssh_opts') { "" }
|
|
||||||
@remote_dir = worker_opts.fetch('directory') { raise "You must specify a remote directory" }
|
|
||||||
@timeout = worker_opts.fetch('timeout') { 2 }
|
|
||||||
@result = 0
|
|
||||||
|
|
||||||
return unless sync_opts
|
|
||||||
sync_opts.stringify_keys!
|
|
||||||
@local_dir = sync_opts.fetch('directory') { raise "You must specify a synchronization directory" }
|
|
||||||
@exclude_paths = sync_opts.fetch('exclude') { [] }
|
|
||||||
|
|
||||||
trace "Initialized"
|
|
||||||
trace " Worker: (#{worker_opts.inspect})"
|
|
||||||
trace " Sync: (#{sync_opts.inspect})"
|
|
||||||
|
|
||||||
sync
|
|
||||||
end
|
|
||||||
|
|
||||||
def sync
|
|
||||||
#trace "Synchronizing with #{connect}\n\t#{sync_opts.inspect}"
|
|
||||||
exclude_opts = @exclude_paths.inject(''){|memo, path| memo += "--exclude=#{path} "}
|
|
||||||
|
|
||||||
rsync_command = [
|
|
||||||
'rsync',
|
|
||||||
'-avz',
|
|
||||||
'--delete',
|
|
||||||
"--timeout=#{@timeout}",
|
|
||||||
exclude_opts,
|
|
||||||
File.expand_path(@local_dir)+'/',
|
|
||||||
"-e \"ssh #{@ssh_opts}\"",
|
|
||||||
"#{@connect}:#{@remote_dir}"
|
|
||||||
].join(" ")
|
|
||||||
trace rsync_command
|
|
||||||
trace `#{rsync_command}`
|
|
||||||
|
|
||||||
@result = $?.exitstatus
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.sync_many opts
|
|
||||||
opts.stringify_keys!
|
|
||||||
config_file = opts.delete('config') { nil }
|
|
||||||
if config_file
|
|
||||||
config_yml = Hydra::Config.load(config_file)
|
|
||||||
opts.merge!(config_yml.stringify_keys!)
|
|
||||||
end
|
|
||||||
@verbose = opts.fetch('verbose') { false }
|
|
||||||
@sync = opts.fetch('sync') { {} }
|
|
||||||
|
|
||||||
workers_opts = opts.fetch('workers') { [] }
|
|
||||||
@remote_worker_opts = []
|
|
||||||
workers_opts.each do |worker_opts|
|
|
||||||
worker_opts.stringify_keys!
|
|
||||||
if worker_opts['type'].to_s == 'ssh'
|
|
||||||
@remote_worker_opts << worker_opts
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
trace "Initialized"
|
|
||||||
trace " Sync: (#{@sync.inspect})"
|
|
||||||
trace " Workers: (#{@remote_worker_opts.inspect})"
|
|
||||||
|
|
||||||
Thread.abort_on_exception = true
|
|
||||||
trace "Processing workers"
|
|
||||||
@listeners = []
|
|
||||||
@remote_worker_opts.each do |worker_opts|
|
|
||||||
@listeners << Thread.new do
|
|
||||||
begin
|
|
||||||
trace "Syncing #{worker_opts.inspect}"
|
|
||||||
Sync.new worker_opts, @sync, @verbose
|
|
||||||
rescue
|
|
||||||
trace "Syncing failed [#{worker_opts.inspect}]"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
@listeners.each{|l| l.join}
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,15 +1,10 @@
|
||||||
require 'open3'
|
require 'open3'
|
||||||
require 'hydra/config'
|
|
||||||
|
|
||||||
module Hydra #:nodoc:
|
module Hydra #:nodoc:
|
||||||
# Hydra Task Common attributes and methods
|
# Hydra Task Common attributes and methods
|
||||||
class Task
|
class Task
|
||||||
# Name of the task. Default 'hydra'
|
# Name of the task. Default 'hydra'
|
||||||
attr_accessor :name
|
attr_accessor :name
|
||||||
|
|
||||||
# Command line options
|
|
||||||
attr_accessor :options
|
|
||||||
|
|
||||||
# Files to test.
|
# Files to test.
|
||||||
# You can add files manually via:
|
# You can add files manually via:
|
||||||
# t.files << [file1, file2, etc]
|
# t.files << [file1, file2, etc]
|
||||||
|
@ -36,20 +31,6 @@ module Hydra #:nodoc:
|
||||||
# t.listeners << Hydra::Listener::Notifier.new
|
# t.listeners << Hydra::Listener::Notifier.new
|
||||||
attr_accessor :listeners
|
attr_accessor :listeners
|
||||||
|
|
||||||
# Set to true if you want to run this task only on the local
|
|
||||||
# machine with one runner. A "Safe Mode" for some test
|
|
||||||
# files that may not play nice with others.
|
|
||||||
attr_accessor :serial
|
|
||||||
|
|
||||||
attr_accessor :environment
|
|
||||||
|
|
||||||
# Set to false if you don't want to show the total running time
|
|
||||||
attr_accessor :show_time
|
|
||||||
|
|
||||||
# Set to a valid file path if you want to save the output of the runners
|
|
||||||
# in a log file
|
|
||||||
attr_accessor :runner_log_file
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Search for the hydra config file
|
# Search for the hydra config file
|
||||||
def find_config_file
|
def find_config_file
|
||||||
|
@ -87,30 +68,20 @@ module Hydra #:nodoc:
|
||||||
@files = []
|
@files = []
|
||||||
@verbose = false
|
@verbose = false
|
||||||
@autosort = true
|
@autosort = true
|
||||||
@serial = false
|
|
||||||
@listeners = [Hydra::Listener::ProgressBar.new]
|
@listeners = [Hydra::Listener::ProgressBar.new]
|
||||||
@show_time = true
|
|
||||||
@options = ''
|
|
||||||
|
|
||||||
yield self if block_given?
|
yield self if block_given?
|
||||||
|
|
||||||
# Ensure we override rspec's at_exit
|
# Ensure we override rspec's at_exit
|
||||||
if defined?(RSpec)
|
require 'hydra/spec/autorun_override'
|
||||||
RSpec::Core::Runner.disable_autorun!
|
|
||||||
end
|
|
||||||
|
|
||||||
unless @serial
|
@config = find_config_file
|
||||||
@config = find_config_file
|
|
||||||
end
|
|
||||||
|
|
||||||
@opts = {
|
@opts = {
|
||||||
:verbose => @verbose,
|
:verbose => @verbose,
|
||||||
:autosort => @autosort,
|
:autosort => @autosort,
|
||||||
:files => @files,
|
:files => @files,
|
||||||
:listeners => @listeners,
|
:listeners => @listeners
|
||||||
:environment => @environment,
|
|
||||||
:runner_log_file => @runner_log_file,
|
|
||||||
:options => @options
|
|
||||||
}
|
}
|
||||||
if @config
|
if @config
|
||||||
@opts.merge!(:config => @config)
|
@opts.merge!(:config => @config)
|
||||||
|
@ -126,132 +97,8 @@ module Hydra #:nodoc:
|
||||||
def define
|
def define
|
||||||
desc "Hydra Tests" + (@name == :hydra ? "" : " for #{@name}")
|
desc "Hydra Tests" + (@name == :hydra ? "" : " for #{@name}")
|
||||||
task @name do
|
task @name do
|
||||||
if Object.const_defined?('Rails') && Rails.env == 'development'
|
Hydra::Master.new(@opts)
|
||||||
$stderr.puts %{WARNING: Rails Environment is "development". Make sure to set it properly (ex: "RAILS_ENV=test rake hydra")}
|
#exit(0) #bypass test on_exit output
|
||||||
end
|
|
||||||
|
|
||||||
start = Time.now if @show_time
|
|
||||||
|
|
||||||
puts '********************'
|
|
||||||
puts @options.inspect
|
|
||||||
master = Hydra::Master.new(@opts)
|
|
||||||
|
|
||||||
$stdout.puts "\nFinished in #{'%.6f' % (Time.now - start)} seconds." if @show_time
|
|
||||||
|
|
||||||
unless master.failed_files.empty?
|
|
||||||
raise "Hydra: Not all tests passes"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Define a test task that uses hydra to profile your test files
|
|
||||||
#
|
|
||||||
# Hydra::ProfileTask.new('hydra:prof') do |t|
|
|
||||||
# t.add_files 'test/unit/**/*_test.rb'
|
|
||||||
# t.add_files 'test/functional/**/*_test.rb'
|
|
||||||
# t.add_files 'test/integration/**/*_test.rb'
|
|
||||||
# t.generate_html = true # defaults to false
|
|
||||||
# t.generate_text = true # defaults to true
|
|
||||||
# end
|
|
||||||
class ProfileTask < Hydra::Task
|
|
||||||
# boolean: generate html output from ruby-prof
|
|
||||||
attr_accessor :generate_html
|
|
||||||
# boolean: generate text output from ruby-prof
|
|
||||||
attr_accessor :generate_text
|
|
||||||
|
|
||||||
# Create a new Hydra ProfileTask
|
|
||||||
def initialize(name = 'hydra:profile')
|
|
||||||
@name = name
|
|
||||||
@files = []
|
|
||||||
@verbose = false
|
|
||||||
@generate_html = false
|
|
||||||
@generate_text = true
|
|
||||||
|
|
||||||
yield self if block_given?
|
|
||||||
|
|
||||||
# Ensure we override rspec's at_exit
|
|
||||||
require 'hydra/spec/autorun_override'
|
|
||||||
|
|
||||||
@config = find_config_file
|
|
||||||
|
|
||||||
@opts = {
|
|
||||||
:verbose => @verbose,
|
|
||||||
:files => @files
|
|
||||||
}
|
|
||||||
define
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
# Create the rake task defined by this HydraTestTask
|
|
||||||
def define
|
|
||||||
desc "Hydra Test Profile" + (@name == :hydra ? "" : " for #{@name}")
|
|
||||||
task @name do
|
|
||||||
require 'ruby-prof'
|
|
||||||
RubyProf.start
|
|
||||||
|
|
||||||
runner = Hydra::Runner.new(:io => File.new('/dev/null', 'w'))
|
|
||||||
@files.each do |file|
|
|
||||||
$stdout.write runner.run_file(file)
|
|
||||||
$stdout.flush
|
|
||||||
end
|
|
||||||
|
|
||||||
$stdout.write "\nTests complete. Generating profiling output\n"
|
|
||||||
$stdout.flush
|
|
||||||
|
|
||||||
result = RubyProf.stop
|
|
||||||
|
|
||||||
if @generate_html
|
|
||||||
printer = RubyProf::GraphHtmlPrinter.new(result)
|
|
||||||
out = File.new("ruby-prof.html", 'w')
|
|
||||||
printer.print(out, :min_self => 0.05)
|
|
||||||
out.close
|
|
||||||
$stdout.write "Profiling data written to [ruby-prof.html]\n"
|
|
||||||
end
|
|
||||||
|
|
||||||
if @generate_text
|
|
||||||
printer = RubyProf::FlatPrinter.new(result)
|
|
||||||
out = File.new("ruby-prof.txt", 'w')
|
|
||||||
printer.print(out, :min_self => 0.05)
|
|
||||||
out.close
|
|
||||||
$stdout.write "Profiling data written to [ruby-prof.txt]\n"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Define a sync task that uses hydra to rsync the source tree under test to remote workers.
|
|
||||||
#
|
|
||||||
# This task is very useful to run before a remote db:reset task to make sure the db/schema.rb
|
|
||||||
# file is up to date on the remote workers.
|
|
||||||
#
|
|
||||||
# Hydra::SyncTask.new('hydra:sync') do |t|
|
|
||||||
# t.verbose = false # optionally set to true for lots of debug messages
|
|
||||||
# end
|
|
||||||
class SyncTask < Hydra::Task
|
|
||||||
# Create a new SyncTestTask
|
|
||||||
def initialize(name = :sync)
|
|
||||||
@name = name
|
|
||||||
@verbose = false
|
|
||||||
|
|
||||||
yield self if block_given?
|
|
||||||
|
|
||||||
@config = find_config_file
|
|
||||||
|
|
||||||
@opts = {
|
|
||||||
:verbose => @verbose
|
|
||||||
}
|
|
||||||
@opts.merge!(:config => @config) if @config
|
|
||||||
|
|
||||||
define
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
# Create the rake task defined by this HydraSyncTask
|
|
||||||
def define
|
|
||||||
desc "Hydra Tests" + (@name == :hydra ? "" : " for #{@name}")
|
|
||||||
task @name do
|
|
||||||
Hydra::Sync.sync_many(@opts)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -265,9 +112,8 @@ module Hydra #:nodoc:
|
||||||
include Open3
|
include Open3
|
||||||
# Create a new hydra remote task with the given name.
|
# Create a new hydra remote task with the given name.
|
||||||
# The task will be named hydra:remote:<name>
|
# The task will be named hydra:remote:<name>
|
||||||
def initialize(name, command=nil)
|
def initialize(name)
|
||||||
@name = name
|
@name = name
|
||||||
@command = command
|
|
||||||
yield self if block_given?
|
yield self if block_given?
|
||||||
@config = find_config_file
|
@config = find_config_file
|
||||||
if @config
|
if @config
|
||||||
|
@ -281,42 +127,34 @@ module Hydra #:nodoc:
|
||||||
def define
|
def define
|
||||||
desc "Run #{@name} remotely on all workers"
|
desc "Run #{@name} remotely on all workers"
|
||||||
task "hydra:remote:#{@name}" do
|
task "hydra:remote:#{@name}" do
|
||||||
config = Hydra::Config.load(@config)
|
config = YAML.load_file(@config)
|
||||||
environment = config.fetch('environment') { 'test' }
|
|
||||||
workers = config.fetch('workers') { [] }
|
workers = config.fetch('workers') { [] }
|
||||||
workers = workers.select{|w| w['type'] == 'ssh'}
|
workers = workers.select{|w| w['type'] == 'ssh'}
|
||||||
@command = "RAILS_ENV=#{environment} rake #{@name}" unless @command
|
|
||||||
|
|
||||||
$stdout.write "==== Hydra Running #{@name} ====\n"
|
|
||||||
Thread.abort_on_exception = true
|
|
||||||
@listeners = []
|
|
||||||
@results = {}
|
|
||||||
workers.each do |worker|
|
workers.each do |worker|
|
||||||
@listeners << Thread.new do
|
$stdout.write "==== Hydra Running #{@name} on #{worker['connect']} ====\n"
|
||||||
begin
|
ssh_opts = worker.fetch('ssh_opts') { '' }
|
||||||
@results[worker] = if run_command(worker, @command)
|
writer, reader, error = popen3("ssh -tt #{ssh_opts} #{worker['connect']} ")
|
||||||
"==== #{@name} passed on #{worker['connect']} ====\n"
|
writer.write("cd #{worker['directory']}\n")
|
||||||
else
|
writer.write "echo BEGIN HYDRA\n"
|
||||||
"==== #{@name} failed on #{worker['connect']} ====\nPlease see above for more details.\n"
|
writer.write("RAILS_ENV=test rake #{@name}\n")
|
||||||
end
|
writer.write "echo END HYDRA\n"
|
||||||
rescue
|
writer.write("exit\n")
|
||||||
@results[worker] = "==== #{@name} failed for #{worker['connect']} ====\n#{$!.inspect}\n#{$!.backtrace.join("\n")}"
|
writer.close
|
||||||
|
ignoring = true
|
||||||
|
while line = reader.gets
|
||||||
|
line.chomp!
|
||||||
|
if line =~ /echo END HYDRA$/
|
||||||
|
ignoring = true
|
||||||
|
end
|
||||||
|
$stdout.write "#{line}\n" unless ignoring
|
||||||
|
if line == 'BEGIN HYDRA'
|
||||||
|
ignoring = false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
$stdout.write "\n==== Hydra Running #{@name} COMPLETE ====\n\n"
|
||||||
end
|
end
|
||||||
@listeners.each{|l| l.join}
|
|
||||||
$stdout.write "\n==== Hydra Running #{@name} COMPLETE ====\n\n"
|
|
||||||
$stdout.write @results.values.join("\n")
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def run_command worker, command
|
|
||||||
$stdout.write "==== Hydra Running #{@name} on #{worker['connect']} ====\n"
|
|
||||||
ssh_opts = worker.fetch('ssh_opts') { '' }
|
|
||||||
|
|
||||||
system %{ssh -tt #{ssh_opts} #{worker['connect']} 'cd #{worker['directory']} && #{command}'}
|
|
||||||
$?.exitstatus == 0
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# A Hydra global task is a task that is run both locally and remotely.
|
# A Hydra global task is a task that is run both locally and remotely.
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
require 'tmpdir'
|
|
||||||
|
|
||||||
class Dir
|
|
||||||
def self.consistent_tmpdir
|
|
||||||
if RUBY_PLATFORM =~ /darwin/i
|
|
||||||
'/tmp' # OS X normally returns a crazy tmpdir, BUT when logged in via SSH, it is '/tmp'. This unifies it.
|
|
||||||
else
|
|
||||||
Dir.tmpdir
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -9,8 +9,6 @@ module Hydra #:nodoc:
|
||||||
class Worker
|
class Worker
|
||||||
include Hydra::Messages::Worker
|
include Hydra::Messages::Worker
|
||||||
traceable('WORKER')
|
traceable('WORKER')
|
||||||
|
|
||||||
attr_reader :runners
|
|
||||||
# Create a new worker.
|
# Create a new worker.
|
||||||
# * io: The IO object to use to communicate with the master
|
# * io: The IO object to use to communicate with the master
|
||||||
# * num_runners: The number of runners to launch
|
# * num_runners: The number of runners to launch
|
||||||
|
@ -19,34 +17,13 @@ module Hydra #:nodoc:
|
||||||
@io = opts.fetch(:io) { raise "No IO Object" }
|
@io = opts.fetch(:io) { raise "No IO Object" }
|
||||||
@runners = []
|
@runners = []
|
||||||
@listeners = []
|
@listeners = []
|
||||||
@options = opts.fetch(:options)
|
|
||||||
|
|
||||||
load_worker_initializer
|
|
||||||
|
|
||||||
@runner_event_listeners = Array(opts.fetch(:runner_listeners) { nil })
|
|
||||||
@runner_event_listeners.select{|l| l.is_a? String}.each do |l|
|
|
||||||
@runner_event_listeners.delete_at(@runner_event_listeners.index(l))
|
|
||||||
listener = eval(l)
|
|
||||||
@runner_event_listeners << listener if listener.is_a?(Hydra::RunnerListener::Abstract)
|
|
||||||
end
|
|
||||||
@runner_log_file = opts.fetch(:runner_log_file) { nil }
|
|
||||||
|
|
||||||
boot_runners(opts.fetch(:runners) { 1 })
|
boot_runners(opts.fetch(:runners) { 1 })
|
||||||
@io.write(Hydra::Messages::Worker::WorkerBegin.new)
|
|
||||||
|
|
||||||
process_messages
|
process_messages
|
||||||
|
|
||||||
@runners.each{|r| Process.wait r[:pid] }
|
@runners.each{|r| Process.wait r[:pid] }
|
||||||
end
|
end
|
||||||
|
|
||||||
def load_worker_initializer
|
|
||||||
if File.exist?('./hydra_worker_init.rb')
|
|
||||||
trace('Requiring hydra_worker_init.rb')
|
|
||||||
require 'hydra_worker_init'
|
|
||||||
else
|
|
||||||
trace('hydra_worker_init.rb not present')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# message handling methods
|
# message handling methods
|
||||||
|
|
||||||
|
@ -91,10 +68,9 @@ module Hydra #:nodoc:
|
||||||
trace "Booting #{num_runners} Runners"
|
trace "Booting #{num_runners} Runners"
|
||||||
num_runners.times do
|
num_runners.times do
|
||||||
pipe = Hydra::Pipe.new
|
pipe = Hydra::Pipe.new
|
||||||
|
|
||||||
child = SafeFork.fork do
|
child = SafeFork.fork do
|
||||||
pipe.identify_as_child
|
pipe.identify_as_child
|
||||||
Hydra::Runner.new(:io => pipe, :verbose => @verbose, :runner_listeners => @runner_event_listeners, :runner_log_file => @runner_log_file, :options => @options)
|
Hydra::Runner.new(:io => pipe, :verbose => @verbose)
|
||||||
end
|
end
|
||||||
pipe.identify_as_parent
|
pipe.identify_as_parent
|
||||||
@runners << { :pid => child, :io => pipe, :idle => false }
|
@runners << { :pid => child, :io => pipe, :idle => false }
|
||||||
|
@ -132,7 +108,7 @@ module Hydra #:nodoc:
|
||||||
end
|
end
|
||||||
rescue IOError => ex
|
rescue IOError => ex
|
||||||
trace "Worker lost Master"
|
trace "Worker lost Master"
|
||||||
shutdown
|
Thread.exit
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
<div class="feature"><h2><span class="val">Feature: Write a file</span></h2><p class="narrative"></p><div class='scenario'><h3 id="scenario_1"><span class="keyword">Scenario:</span> <span class="val">Write to hydra_test.txt</span></h3><ol><li id='_Users_john_Projects_hydra_test_fixtures_features_write_alternate_file_feature_4' class='step passed'><div class="step_name"><span class="keyword">Given </span><span class="step val">an alternate target file</span></div><div class="step_file"><span>test/fixtures/features/step_definitions.rb:5</span></div></li><li id='_Users_john_Projects_hydra_test_fixtures_features_write_alternate_file_feature_5' class='step passed'><div class="step_name"><span class="keyword">When </span><span class="step val">I write "<span class="param">HYDRA</span>" to the file</span></div><div class="step_file"><span>test/fixtures/features/step_definitions.rb:9</span></div></li><li id='_Users_john_Projects_hydra_test_fixtures_features_write_alternate_file_feature_6' class='step passed'><div class="step_name"><span class="keyword">Then </span><span class="step val">"<span class="param">HYDRA</span>" should be written in the file</span></div><div class="step_file"><span>test/fixtures/features/step_definitions.rb:16</span></div></li></ol></div></div>
|
|
|
@ -1 +0,0 @@
|
||||||
<div class="feature"><h2><span class="val">Feature: Write a file</span></h2><p class="narrative"></p><div class='scenario'><h3 id="scenario_1"><span class="keyword">Scenario:</span> <span class="val">Write to hydra_test.txt</span></h3><ol><li id='_Users_john_Projects_hydra_test_fixtures_features_write_file_feature_4' class='step passed'><div class="step_name"><span class="keyword">Given </span><span class="step val">a target file</span></div><div class="step_file"><span>test/fixtures/features/step_definitions.rb:1</span></div></li><li id='_Users_john_Projects_hydra_test_fixtures_features_write_file_feature_5' class='step passed'><div class="step_name"><span class="keyword">When </span><span class="step val">I write "<span class="param">HYDRA</span>" to the file</span></div><div class="step_file"><span>test/fixtures/features/step_definitions.rb:9</span></div></li><li id='_Users_john_Projects_hydra_test_fixtures_features_write_file_feature_6' class='step passed'><div class="step_name"><span class="keyword">Then </span><span class="step val">"<span class="param">HYDRA</span>" should be written in the file</span></div><div class="step_file"><span>test/fixtures/features/step_definitions.rb:16</span></div></li></ol></div></div>
|
|
|
@ -1,10 +0,0 @@
|
||||||
require File.join(File.dirname(__FILE__), '..', 'test_helper')
|
|
||||||
|
|
||||||
# this test is around to make sure that we handle all the errors
|
|
||||||
# that can occur when 'require'ing a test file.
|
|
||||||
class SyncTest < Object
|
|
||||||
def test_it_again
|
|
||||||
assert true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
Given /^a target file$/ do
|
Given /^a target file$/ do
|
||||||
@target_file = File.expand_path(File.join(Dir.consistent_tmpdir, 'hydra_test.txt'))
|
@target_file = File.expand_path(File.join(Dir.tmpdir, 'hydra_test.txt'))
|
||||||
end
|
end
|
||||||
|
|
||||||
Given /^an alternate target file$/ do
|
Given /^an alternate target file$/ do
|
||||||
@target_file = File.expand_path(File.join(Dir.consistent_tmpdir, 'alternate_hydra_test.txt'))
|
@target_file = File.expand_path(File.join(Dir.tmpdir, 'alternate_hydra_test.txt'))
|
||||||
end
|
end
|
||||||
|
|
||||||
When /^I write "([^\"]*)" to the file$/ do |text|
|
When /^I write "([^\"]*)" to the file$/ do |text|
|
||||||
|
|
|
@ -1,2 +0,0 @@
|
||||||
require '../test/fixtures/runner_listeners.rb'
|
|
||||||
require '../test/fixtures/master_listeners.rb'
|
|
|
@ -1,4 +0,0 @@
|
||||||
"use strict";
|
|
||||||
var thisvar;
|
|
||||||
var thatvar
|
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
{
|
|
||||||
"var1": "something",
|
|
||||||
"var2": "trailing comma",
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
#!/usr/bin/env ruby
|
|
||||||
|
|
||||||
10000.times do
|
|
||||||
$stdout.write "A non-hydra message...\n"
|
|
||||||
$stdout.flush
|
|
||||||
end
|
|
||||||
|
|
||||||
$stdout.write "{:class=>Hydra::Messages::TestMessage, :text=>\"My message\"}\n"
|
|
||||||
$stdout.flush
|
|
|
@ -1,10 +0,0 @@
|
||||||
module HydraExtension
|
|
||||||
module Listener
|
|
||||||
class WorkerBeganFlag < Hydra::Listener::Abstract
|
|
||||||
# Fired after runner processes have been started
|
|
||||||
def worker_begin(worker)
|
|
||||||
FileUtils.touch File.expand_path(File.join(Dir.consistent_tmpdir, 'worker_began_flag'))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,23 +0,0 @@
|
||||||
module HydraExtension
|
|
||||||
module RunnerListener
|
|
||||||
class RunnerBeginTest < Hydra::RunnerListener::Abstract
|
|
||||||
# Fired by the runner just before requesting the first file
|
|
||||||
def runner_begin( runner )
|
|
||||||
FileUtils.touch File.expand_path(File.join(Dir.consistent_tmpdir, 'alternate_hydra_test.txt'))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class RunnerEndTest < Hydra::RunnerListener::Abstract
|
|
||||||
# Fired by the runner just before requesting the first file
|
|
||||||
def runner_begin( runner )
|
|
||||||
FileUtils.touch File.expand_path(File.join(Dir.consistent_tmpdir, 'runner_began_flag')) #used to know when the runner is ready
|
|
||||||
end
|
|
||||||
# Fired by the runner just after stoping
|
|
||||||
def runner_end( runner )
|
|
||||||
# NOTE: do not use trace here
|
|
||||||
#runner.trace "Ending runner"
|
|
||||||
FileUtils.touch File.expand_path(File.join(Dir.consistent_tmpdir, 'alternate_hydra_test.txt'))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,5 +1,3 @@
|
||||||
require 'rubygems'
|
|
||||||
gem 'test-unit'
|
|
||||||
require 'test/unit'
|
require 'test/unit'
|
||||||
|
|
||||||
class SyncTest < Test::Unit::TestCase
|
class SyncTest < Test::Unit::TestCase
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
---
|
|
||||||
workers:
|
|
||||||
- type: ssh
|
|
||||||
connect: localhost
|
|
||||||
directory: /tmp
|
|
||||||
runners: 1
|
|
|
@ -2,7 +2,7 @@ require File.join(File.dirname(__FILE__), '..', 'test_helper')
|
||||||
|
|
||||||
class WriteFileTest < Test::Unit::TestCase
|
class WriteFileTest < Test::Unit::TestCase
|
||||||
def test_write_a_file
|
def test_write_a_file
|
||||||
File.open(File.join(Dir.consistent_tmpdir, 'hydra_test.txt'), 'a') do |f|
|
File.open(File.join(Dir.tmpdir, 'hydra_test.txt'), 'a') do |f|
|
||||||
f.write "HYDRA"
|
f.write "HYDRA"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
require 'rspec'
|
require 'tmpdir'
|
||||||
require 'hydra/tmpdir'
|
require 'spec'
|
||||||
describe "file writing" do
|
context "file writing" do
|
||||||
it "writes to a file" do
|
it "writes to a file" do
|
||||||
File.open(File.join(Dir.consistent_tmpdir, 'alternate_hydra_test.txt'), 'a') do |f|
|
File.open(File.join(Dir.tmpdir, 'alternate_hydra_test.txt'), 'a') do |f|
|
||||||
f.write "HYDRA"
|
f.write "HYDRA"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
require 'rspec'
|
require 'tmpdir'
|
||||||
require 'hydra/tmpdir'
|
require 'spec'
|
||||||
describe "file writing" do
|
context "file writing" do
|
||||||
it "writes to a file" do
|
it "writes to a file" do
|
||||||
File.open(File.join(Dir.consistent_tmpdir, 'hydra_test.txt'), 'a') do |f|
|
File.open(File.join(Dir.tmpdir, 'hydra_test.txt'), 'a') do |f|
|
||||||
f.write "HYDRA"
|
f.write "HYDRA"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
require 'tmpdir'
|
|
||||||
require 'rspec'
|
|
||||||
|
|
||||||
describe "file writing" do
|
|
||||||
it "writes to a file" do
|
|
||||||
File.open(File.join(Dir.consistent_tmpdir, 'hydra_test.txt'), 'a') do |f|
|
|
||||||
f.write "HYDRA"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
it 'could do so much more' # pending spec
|
|
||||||
end
|
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
require 'test_helper'
|
require File.join(File.dirname(__FILE__), 'test_helper')
|
||||||
require 'fixtures/runner_listeners'
|
|
||||||
require 'fixtures/master_listeners'
|
|
||||||
|
|
||||||
class MasterTest < Test::Unit::TestCase
|
class MasterTest < Test::Unit::TestCase
|
||||||
context "with a file to test and a destination to verify" do
|
context "with a file to test and a destination to verify" do
|
||||||
|
@ -22,53 +20,11 @@ class MasterTest < Test::Unit::TestCase
|
||||||
assert_equal "HYDRA", File.read(target_file)
|
assert_equal "HYDRA", File.read(target_file)
|
||||||
end
|
end
|
||||||
|
|
||||||
# this test simulates what happens when we have 2 tests with the same
|
|
||||||
# class name but with different parent classes. This can happen when
|
|
||||||
# we have a functional and an integration test class with the same name.
|
|
||||||
#
|
|
||||||
# ...but I can't even get this test to work in the expected way (jb)
|
|
||||||
should_eventually "run even with a test that will not require" do
|
|
||||||
class FileOutputListener < Hydra::Listener::Abstract
|
|
||||||
attr_accessor :output
|
|
||||||
def initialize(&block)
|
|
||||||
self.output = {}
|
|
||||||
end
|
|
||||||
|
|
||||||
def file_end(file, output)
|
|
||||||
self.output[file] = output
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
listener = FileOutputListener.new
|
|
||||||
sync_test = File.join(File.dirname(__FILE__), 'fixtures', 'sync_test.rb')
|
|
||||||
Hydra::Master.new(
|
|
||||||
# we want the actual test to run last to make sure the runner can still run tests
|
|
||||||
:files => [sync_test, conflicting_test_file, test_file],
|
|
||||||
:autosort => false,
|
|
||||||
:listeners => [listener]
|
|
||||||
)
|
|
||||||
assert_match /superclass mismatch for class SyncTest/, listener.output[conflicting_test_file]
|
|
||||||
assert_match conflicting_test_file, listener.output[conflicting_test_file]
|
|
||||||
assert File.exists?(target_file)
|
|
||||||
assert_equal "HYDRA", File.read(target_file)
|
|
||||||
end
|
|
||||||
|
|
||||||
should "run a spec with pending examples" do
|
|
||||||
progress_bar = Hydra::Listener::ProgressBar.new(StringIO.new)
|
|
||||||
Hydra::Master.new(
|
|
||||||
:files => [rspec_file_with_pending],
|
|
||||||
:listeners => [progress_bar]
|
|
||||||
)
|
|
||||||
assert File.exists?(target_file)
|
|
||||||
assert_equal "HYDRA", File.read(target_file)
|
|
||||||
assert_equal false, progress_bar.instance_variable_get('@errors')
|
|
||||||
end
|
|
||||||
|
|
||||||
should "generate a report" do
|
should "generate a report" do
|
||||||
Hydra::Master.new(:files => [test_file])
|
Hydra::Master.new(:files => [test_file])
|
||||||
assert File.exists?(target_file)
|
assert File.exists?(target_file)
|
||||||
assert_equal "HYDRA", File.read(target_file)
|
assert_equal "HYDRA", File.read(target_file)
|
||||||
report_file = File.join(Dir.consistent_tmpdir, 'hydra_heuristics.yml')
|
report_file = File.join(Dir.tmpdir, 'hydra_heuristics.yml')
|
||||||
assert File.exists?(report_file)
|
assert File.exists?(report_file)
|
||||||
assert report = YAML.load_file(report_file)
|
assert report = YAML.load_file(report_file)
|
||||||
assert_not_nil report[test_file]
|
assert_not_nil report[test_file]
|
||||||
|
@ -119,7 +75,7 @@ class MasterTest < Test::Unit::TestCase
|
||||||
:workers => [{
|
:workers => [{
|
||||||
:type => :ssh,
|
:type => :ssh,
|
||||||
:connect => 'localhost',
|
:connect => 'localhost',
|
||||||
:directory => remote_dir_path,
|
:directory => File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib')),
|
||||||
:runners => 1
|
:runners => 1
|
||||||
}]
|
}]
|
||||||
)
|
)
|
||||||
|
@ -137,8 +93,8 @@ class MasterTest < Test::Unit::TestCase
|
||||||
end
|
end
|
||||||
|
|
||||||
should "synchronize a test file over ssh with rsync" do
|
should "synchronize a test file over ssh with rsync" do
|
||||||
local = File.join(Dir.consistent_tmpdir, 'hydra', 'local')
|
local = File.join(Dir.tmpdir, 'hydra', 'local')
|
||||||
remote = File.join(Dir.consistent_tmpdir, 'hydra', 'remote')
|
remote = File.join(Dir.tmpdir, 'hydra', 'remote')
|
||||||
sync_test = File.join(File.dirname(__FILE__), 'fixtures', 'sync_test.rb')
|
sync_test = File.join(File.dirname(__FILE__), 'fixtures', 'sync_test.rb')
|
||||||
[local, remote].each{|f| FileUtils.rm_rf f; FileUtils.mkdir_p f}
|
[local, remote].each{|f| FileUtils.rm_rf f; FileUtils.mkdir_p f}
|
||||||
|
|
||||||
|
@ -167,7 +123,6 @@ class MasterTest < Test::Unit::TestCase
|
||||||
:type => :ssh,
|
:type => :ssh,
|
||||||
:connect => 'localhost',
|
:connect => 'localhost',
|
||||||
:directory => remote,
|
:directory => remote,
|
||||||
:verbose => true,
|
|
||||||
:runners => 1
|
:runners => 1
|
||||||
}],
|
}],
|
||||||
:sync => {
|
:sync => {
|
||||||
|
@ -183,225 +138,4 @@ class MasterTest < Test::Unit::TestCase
|
||||||
assert !File.exists?(File.join(remote, 'test_b.rb')), "B was not deleted"
|
assert !File.exists?(File.join(remote, 'test_b.rb')), "B was not deleted"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context "with a runner_end event" do
|
|
||||||
setup do
|
|
||||||
# avoid having other tests interfering with us
|
|
||||||
sleep(0.2)
|
|
||||||
FileUtils.rm_f(target_file)
|
|
||||||
FileUtils.rm_f(alternate_target_file)
|
|
||||||
|
|
||||||
@runner_began_flag = File.expand_path(File.join(Dir.consistent_tmpdir, 'runner_began_flag')) #used to know when the worker is ready
|
|
||||||
FileUtils.rm_f(@runner_began_flag)
|
|
||||||
|
|
||||||
@runner_listener = 'HydraExtension::RunnerListener::RunnerEndTest.new' # runner_end method that creates alternate_target_file
|
|
||||||
@master_listener = HydraExtension::Listener::WorkerBeganFlag.new #used to know when the runner is up
|
|
||||||
end
|
|
||||||
|
|
||||||
teardown do
|
|
||||||
FileUtils.rm_f(target_file)
|
|
||||||
FileUtils.rm_f(alternate_target_file)
|
|
||||||
end
|
|
||||||
|
|
||||||
context "running a local worker" do
|
|
||||||
should "run runner_end on successful termination" do
|
|
||||||
@pid = Process.fork do
|
|
||||||
Hydra::Master.new(
|
|
||||||
:files => [test_file] * 6,
|
|
||||||
:autosort => false,
|
|
||||||
:listeners => [@master_listener],
|
|
||||||
:runner_listeners => [@runner_listener],
|
|
||||||
:verbose => false
|
|
||||||
)
|
|
||||||
end
|
|
||||||
Process.waitpid @pid
|
|
||||||
|
|
||||||
assert_file_exists alternate_target_file
|
|
||||||
end
|
|
||||||
|
|
||||||
should "run runner_end after interruption signal" do
|
|
||||||
add_infinite_worker_begin_to @master_listener
|
|
||||||
|
|
||||||
capture_stderr do # redirect stderr
|
|
||||||
@pid = Process.fork do
|
|
||||||
Hydra::Master.new(
|
|
||||||
:files => [test_file],
|
|
||||||
:autosort => false,
|
|
||||||
:listeners => [@master_listener],
|
|
||||||
:runner_listeners => [@runner_listener],
|
|
||||||
:verbose => false
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
wait_for_runner_to_begin
|
|
||||||
|
|
||||||
Process.kill 'SIGINT', @pid
|
|
||||||
Process.waitpid @pid
|
|
||||||
|
|
||||||
assert_file_exists alternate_target_file
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "running a remote worker" do
|
|
||||||
setup do
|
|
||||||
copy_worker_init_file # this method has a protection to avoid erasing an existing worker_init_file
|
|
||||||
end
|
|
||||||
|
|
||||||
teardown do
|
|
||||||
FileUtils.rm_f(@remote_init_file) unless @protect_init_file
|
|
||||||
end
|
|
||||||
|
|
||||||
should "run runner_end on successful termination" do
|
|
||||||
capture_stderr do # redirect stderr
|
|
||||||
@pid = Process.fork do
|
|
||||||
Hydra::Master.new(
|
|
||||||
:files => [test_file],
|
|
||||||
:autosort => false,
|
|
||||||
:listeners => [@master_listener],
|
|
||||||
:runner_listeners => [@runner_listener],
|
|
||||||
:workers => [{
|
|
||||||
:type => :ssh,
|
|
||||||
:connect => 'localhost -o ControlMaster=no',
|
|
||||||
:directory => remote_dir_path,
|
|
||||||
:runners => 1
|
|
||||||
}],
|
|
||||||
:verbose => false
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
Process.waitpid @pid
|
|
||||||
|
|
||||||
assert_file_exists target_file
|
|
||||||
end
|
|
||||||
|
|
||||||
should "not die horribly when the host cannot be reached" do
|
|
||||||
capture_stderr do # redirect stderr
|
|
||||||
@pid = Process.fork do
|
|
||||||
Hydra::Master.new(
|
|
||||||
:files => [test_file],
|
|
||||||
:autosort => false,
|
|
||||||
:listeners => [@master_listener],
|
|
||||||
:runner_listeners => [@runner_listener],
|
|
||||||
:workers => [{
|
|
||||||
:type => :ssh,
|
|
||||||
:connect => 'sdlsdkjfhadsfjsd',
|
|
||||||
:directory => remote_dir_path,
|
|
||||||
:runners => 1
|
|
||||||
}],
|
|
||||||
:verbose => false
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
Process.waitpid @pid
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "redirecting runner's output and errors" do
|
|
||||||
setup do
|
|
||||||
# avoid having other tests interfering with us
|
|
||||||
sleep(0.2)
|
|
||||||
FileUtils.rm_f(target_file)
|
|
||||||
FileUtils.rm_f(runner_log_file)
|
|
||||||
FileUtils.rm_f("#{remote_dir_path}/#{runner_log_file}")
|
|
||||||
end
|
|
||||||
|
|
||||||
teardown do
|
|
||||||
FileUtils.rm_f(target_file)
|
|
||||||
FileUtils.rm_f(runner_log_file)
|
|
||||||
FileUtils.rm_f("#{remote_dir_path}/#{runner_log_file}")
|
|
||||||
end
|
|
||||||
|
|
||||||
should "create a runner log file when usign local worker and passing a log file name" do
|
|
||||||
@pid = Process.fork do
|
|
||||||
Hydra::Master.new(
|
|
||||||
:files => [test_file],
|
|
||||||
:runner_log_file => runner_log_file,
|
|
||||||
:verbose => false
|
|
||||||
)
|
|
||||||
end
|
|
||||||
Process.waitpid @pid
|
|
||||||
|
|
||||||
assert_file_exists target_file # ensure the test was successfully ran
|
|
||||||
assert_file_exists runner_log_file
|
|
||||||
end
|
|
||||||
|
|
||||||
should "create a runner log file when usign remote worker and passing a log file name" do
|
|
||||||
@pid = Process.fork do
|
|
||||||
Hydra::Master.new(
|
|
||||||
:files => [test_file],
|
|
||||||
:workers => [{
|
|
||||||
:type => :ssh,
|
|
||||||
:connect => 'localhost',
|
|
||||||
:directory => remote_dir_path,
|
|
||||||
:runners => 1
|
|
||||||
}],
|
|
||||||
:verbose => false,
|
|
||||||
:runner_log_file => runner_log_file
|
|
||||||
)
|
|
||||||
end
|
|
||||||
Process.waitpid @pid
|
|
||||||
|
|
||||||
assert_file_exists target_file # ensure the test was successfully ran
|
|
||||||
assert_file_exists "#{remote_dir_path}/#{runner_log_file}"
|
|
||||||
end
|
|
||||||
|
|
||||||
should "create the default runner log file when passing an incorrect log file path" do
|
|
||||||
default_log_file = "#{remote_dir_path}/#{Hydra::Runner::DEFAULT_LOG_FILE}" # hydra-runner.log"
|
|
||||||
FileUtils.rm_f(default_log_file)
|
|
||||||
|
|
||||||
@pid = Process.fork do
|
|
||||||
Hydra::Master.new(
|
|
||||||
:files => [test_file],
|
|
||||||
:workers => [{
|
|
||||||
:type => :ssh,
|
|
||||||
:connect => 'localhost',
|
|
||||||
:directory => remote_dir_path,
|
|
||||||
:runners => 1
|
|
||||||
}],
|
|
||||||
:verbose => false,
|
|
||||||
:runner_log_file => "invalid-dir/#{runner_log_file}"
|
|
||||||
)
|
|
||||||
end
|
|
||||||
Process.waitpid @pid
|
|
||||||
|
|
||||||
assert_file_exists target_file # ensure the test was successfully ran
|
|
||||||
assert_file_exists default_log_file #default log file
|
|
||||||
assert !File.exists?( "#{remote_dir_path}/#{runner_log_file}" )
|
|
||||||
|
|
||||||
FileUtils.rm_f(default_log_file)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def runner_log_file
|
|
||||||
"my-hydra-runner.log"
|
|
||||||
end
|
|
||||||
|
|
||||||
def add_infinite_worker_begin_to master_listener
|
|
||||||
class << master_listener
|
|
||||||
def worker_begin( worker )
|
|
||||||
super
|
|
||||||
sleep 1 while true #ensure the process doesn't finish before killing it
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# this requires that a worker_begin listener creates a file named worker_began_flag in tmp directory
|
|
||||||
def wait_for_runner_to_begin
|
|
||||||
assert_file_exists @runner_began_flag
|
|
||||||
end
|
|
||||||
|
|
||||||
# with a protection to avoid erasing something important in lib
|
|
||||||
def copy_worker_init_file
|
|
||||||
@remote_init_file = "#{remote_dir_path}/#{File.basename( hydra_worker_init_file )}"
|
|
||||||
if File.exists?( @remote_init_file )
|
|
||||||
$stderr.puts "\nWARNING!!!: #{@remote_init_file} exits and this test needs to create a new file here. Make sure there is nothing inportant in that file and remove it before running this test\n\n"
|
|
||||||
@protect_init_file = true
|
|
||||||
exit
|
|
||||||
end
|
|
||||||
# copy the hydra_worker_init to the correct location
|
|
||||||
FileUtils.cp(hydra_worker_init_file, remote_dir_path)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
require 'test_helper'
|
require File.join(File.dirname(__FILE__), 'test_helper')
|
||||||
|
|
||||||
class MessageTest < Test::Unit::TestCase
|
class MessageTest < Test::Unit::TestCase
|
||||||
class MyMessage < Hydra::Message
|
class MyMessage < Hydra::Message
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
require 'test_helper'
|
require File.join(File.dirname(__FILE__), 'test_helper')
|
||||||
|
|
||||||
class PipeTest < Test::Unit::TestCase
|
class PipeTest < Test::Unit::TestCase
|
||||||
context "a pipe" do
|
context "a pipe" do
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
require 'test_helper'
|
require File.join(File.dirname(__FILE__), 'test_helper')
|
||||||
require 'fixtures/runner_listeners'
|
|
||||||
|
|
||||||
class RunnerTest < Test::Unit::TestCase
|
class RunnerTest < Test::Unit::TestCase
|
||||||
context "with a file to test and a destination to verify" do
|
context "with a file to test and a destination to verify" do
|
||||||
|
@ -38,20 +37,8 @@ class RunnerTest < Test::Unit::TestCase
|
||||||
Process.wait(child)
|
Process.wait(child)
|
||||||
end
|
end
|
||||||
|
|
||||||
should "run a js lint file and find errors" do
|
|
||||||
runner = Hydra::Runner.new(:options => {}, :io => File.new('/dev/null', 'w'))
|
|
||||||
results = runner.run_file(javascript_file)
|
|
||||||
assert results =~ /Missing semicolon/, results
|
|
||||||
end
|
|
||||||
|
|
||||||
should "run a json data file and find errors" do
|
|
||||||
runner = Hydra::Runner.new(:options => {}, :io => File.new('/dev/null', 'w'))
|
|
||||||
results = runner.run_file(json_file)
|
|
||||||
assert results =~ /trailing comma/, results
|
|
||||||
end
|
|
||||||
|
|
||||||
should "run two rspec tests" do
|
should "run two rspec tests" do
|
||||||
runner = Hydra::Runner.new(:options => {}, :io => File.new('/dev/null', 'w'))
|
runner = Hydra::Runner.new(:io => File.new('/dev/null', 'w'))
|
||||||
runner.run_file(rspec_file)
|
runner.run_file(rspec_file)
|
||||||
assert File.exists?(target_file)
|
assert File.exists?(target_file)
|
||||||
assert_equal "HYDRA", File.read(target_file)
|
assert_equal "HYDRA", File.read(target_file)
|
||||||
|
@ -61,48 +48,39 @@ class RunnerTest < Test::Unit::TestCase
|
||||||
runner.run_file(alternate_rspec_file)
|
runner.run_file(alternate_rspec_file)
|
||||||
assert File.exists?(alternate_target_file)
|
assert File.exists?(alternate_target_file)
|
||||||
assert_equal "HYDRA", File.read(alternate_target_file)
|
assert_equal "HYDRA", File.read(alternate_target_file)
|
||||||
assert !File.exists?(target_file), "Tests are double running!"
|
assert !File.exists?(target_file)
|
||||||
end
|
|
||||||
|
|
||||||
should "run rspec tests with pending examples" do
|
|
||||||
runner = Hydra::Runner.new(:options => {}, :io => File.new('/dev/null', 'w'))
|
|
||||||
assert File.exists?(rspec_file_with_pending)
|
|
||||||
|
|
||||||
runner.run_file(rspec_file_with_pending)
|
|
||||||
|
|
||||||
assert File.exists?(target_file)
|
|
||||||
assert_equal "HYDRA", File.read(target_file)
|
|
||||||
|
|
||||||
FileUtils.rm_f(target_file)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
should "run two cucumber tests" do
|
should "run two cucumber tests" do
|
||||||
# because of all the crap cucumber pulls in
|
# because of all the crap cucumber pulls in
|
||||||
# we run this in a fork to not contaminate
|
# we run this in a fork to not contaminate
|
||||||
# the main test environment
|
# the main test environment
|
||||||
capture_stderr do # redirect stderr
|
pid = Process.fork do
|
||||||
pid = Process.fork do
|
puts "THE FOLLOWING WARNINGS CAN BE IGNORED"
|
||||||
runner = Hydra::Runner.new(:options => {}, :io => File.new('/dev/null', 'w'))
|
puts "It is caused by Cucumber loading all rb files near its features"
|
||||||
runner.run_file(cucumber_feature_file)
|
|
||||||
assert File.exists?(target_file)
|
|
||||||
assert_equal "HYDRA", File.read(target_file)
|
|
||||||
|
|
||||||
FileUtils.rm_f(target_file)
|
runner = Hydra::Runner.new(:io => File.new('/dev/null', 'w'))
|
||||||
|
runner.run_file(cucumber_feature_file)
|
||||||
|
assert File.exists?(target_file)
|
||||||
|
assert_equal "HYDRA", File.read(target_file)
|
||||||
|
|
||||||
runner.run_file(alternate_cucumber_feature_file)
|
FileUtils.rm_f(target_file)
|
||||||
assert File.exists?(alternate_target_file)
|
|
||||||
assert_equal "HYDRA", File.read(alternate_target_file)
|
runner.run_file(alternate_cucumber_feature_file)
|
||||||
assert !File.exists?(target_file)
|
assert File.exists?(alternate_target_file)
|
||||||
end
|
assert_equal "HYDRA", File.read(alternate_target_file)
|
||||||
Process.wait pid
|
assert !File.exists?(target_file)
|
||||||
|
|
||||||
|
puts "END IGNORABLE OUTPUT"
|
||||||
end
|
end
|
||||||
|
Process.wait pid
|
||||||
end
|
end
|
||||||
|
|
||||||
should "be able to run a runner over ssh" do
|
should "be able to run a runner over ssh" do
|
||||||
ssh = Hydra::SSH.new(
|
ssh = Hydra::SSH.new(
|
||||||
'localhost -o ControlMaster=no',
|
'localhost',
|
||||||
File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib')),
|
File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib')),
|
||||||
%{ruby -rrubygems -e "require \\"bundler/setup\\"; require \\"hydra\\"; Hydra::Runner.new(:options => {}, :io => Hydra::Stdio.new, :verbose => true);"}
|
"ruby -e \"require 'rubygems'; require 'hydra'; Hydra::Runner.new(:io => Hydra::Stdio.new, :verbose => true);\""
|
||||||
)
|
)
|
||||||
assert ssh.gets.is_a?(Hydra::Messages::Runner::RequestFile)
|
assert ssh.gets.is_a?(Hydra::Messages::Runner::RequestFile)
|
||||||
ssh.write(Hydra::Messages::Worker::RunFile.new(:file => test_file))
|
ssh.write(Hydra::Messages::Worker::RunFile.new(:file => test_file))
|
||||||
|
@ -122,49 +100,6 @@ class RunnerTest < Test::Unit::TestCase
|
||||||
assert File.exists?(target_file)
|
assert File.exists?(target_file)
|
||||||
assert_equal "HYDRA", File.read(target_file)
|
assert_equal "HYDRA", File.read(target_file)
|
||||||
end
|
end
|
||||||
|
|
||||||
context "using runner events" do
|
|
||||||
context "on successful termination" do
|
|
||||||
setup do
|
|
||||||
@pipe = Hydra::Pipe.new
|
|
||||||
@parent = Process.fork do
|
|
||||||
request_a_file_and_verify_completion(@pipe, test_file)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
should "fire runner_begin event" do
|
|
||||||
run_the_runner(@pipe, [HydraExtension::RunnerListener::RunnerBeginTest.new] )
|
|
||||||
Process.wait(@parent)
|
|
||||||
|
|
||||||
# ensure runner_begin was fired
|
|
||||||
assert_file_exists alternate_target_file
|
|
||||||
end
|
|
||||||
|
|
||||||
should "fire runner_end event" do
|
|
||||||
run_the_runner(@pipe, [HydraExtension::RunnerListener::RunnerEndTest.new] )
|
|
||||||
Process.wait(@parent)
|
|
||||||
|
|
||||||
assert_file_exists alternate_target_file
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
should "fire runner_end event after losing communication with worker" do
|
|
||||||
pipe = Hydra::Pipe.new
|
|
||||||
parent = Process.fork do
|
|
||||||
pipe.identify_as_parent
|
|
||||||
|
|
||||||
# grab its response.
|
|
||||||
response = pipe.gets
|
|
||||||
pipe.close #this will be detected by the runner and it should call runner_end
|
|
||||||
end
|
|
||||||
|
|
||||||
run_the_runner(pipe, [HydraExtension::RunnerListener::RunnerEndTest.new] )
|
|
||||||
Process.wait(parent)
|
|
||||||
|
|
||||||
# ensure runner_end was fired
|
|
||||||
assert File.exists?( alternate_target_file )
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
module RunnerTestHelper
|
module RunnerTestHelper
|
||||||
|
@ -177,6 +112,7 @@ class RunnerTest < Test::Unit::TestCase
|
||||||
|
|
||||||
# grab its response. This makes us wait for it to finish
|
# grab its response. This makes us wait for it to finish
|
||||||
response = pipe.gets
|
response = pipe.gets
|
||||||
|
puts response.output
|
||||||
|
|
||||||
# tell it to shut down
|
# tell it to shut down
|
||||||
pipe.write(Hydra::Messages::Worker::Shutdown.new)
|
pipe.write(Hydra::Messages::Worker::Shutdown.new)
|
||||||
|
@ -186,9 +122,9 @@ class RunnerTest < Test::Unit::TestCase
|
||||||
assert_equal "HYDRA", File.read(target_file)
|
assert_equal "HYDRA", File.read(target_file)
|
||||||
end
|
end
|
||||||
|
|
||||||
def run_the_runner(pipe, listeners = [])
|
def run_the_runner(pipe)
|
||||||
pipe.identify_as_child
|
pipe.identify_as_child
|
||||||
Hydra::Runner.new( :io => pipe, :options => {}, :runner_listeners => listeners )
|
Hydra::Runner.new(:io => pipe)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
include RunnerTestHelper
|
include RunnerTestHelper
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
require 'test_helper'
|
require File.join(File.dirname(__FILE__), 'test_helper')
|
||||||
|
|
||||||
class SSHTest < Test::Unit::TestCase
|
class SSHTest < Test::Unit::TestCase
|
||||||
should "be able to execute a command over ssh" do
|
should "be able to execute a command over ssh" do
|
||||||
ssh = Hydra::SSH.new(
|
ssh = Hydra::SSH.new(
|
||||||
'localhost -o ControlMaster=no', # connect to this machine
|
'localhost', # connect to this machine
|
||||||
File.expand_path(File.join(File.dirname(__FILE__))), # move to the test directory
|
File.expand_path(File.join(File.dirname(__FILE__))), # move to the test directory
|
||||||
"ruby fixtures/hello_world.rb"
|
"ruby fixtures/hello_world.rb"
|
||||||
)
|
)
|
||||||
|
@ -11,16 +11,4 @@ class SSHTest < Test::Unit::TestCase
|
||||||
assert_equal "Hello World", response.text
|
assert_equal "Hello World", response.text
|
||||||
ssh.close
|
ssh.close
|
||||||
end
|
end
|
||||||
|
|
||||||
should "be able to handle a large number of non-Hydra console output" do
|
|
||||||
ssh = Hydra::SSH.new(
|
|
||||||
'localhost -o ControlMaster=no', # connect to this machine
|
|
||||||
File.expand_path(File.join(File.dirname(__FILE__))), # move to the test directory
|
|
||||||
"ruby fixtures/many_outputs_to_console.rb"
|
|
||||||
)
|
|
||||||
response = ssh.gets
|
|
||||||
assert_equal "My message", response.text
|
|
||||||
ssh.close
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,116 +0,0 @@
|
||||||
require 'test_helper'
|
|
||||||
|
|
||||||
class SyncTest < Test::Unit::TestCase
|
|
||||||
context "with a file to test and a destination to verify" do
|
|
||||||
setup do
|
|
||||||
# avoid having other tests interfering with us
|
|
||||||
sleep(0.2)
|
|
||||||
#FileUtils.rm_f(target_file)
|
|
||||||
end
|
|
||||||
|
|
||||||
teardown do
|
|
||||||
#FileUtils.rm_f(target_file)
|
|
||||||
end
|
|
||||||
|
|
||||||
should "synchronize a test file over ssh with rsync" do
|
|
||||||
local = File.join(Dir.consistent_tmpdir, 'hydra', 'local')
|
|
||||||
remote = File.join(Dir.consistent_tmpdir, 'hydra', 'remote')
|
|
||||||
sync_test = File.join(File.dirname(__FILE__), 'fixtures', 'sync_test.rb')
|
|
||||||
[local, remote].each{|f| FileUtils.rm_rf f; FileUtils.mkdir_p f}
|
|
||||||
|
|
||||||
# setup the folders:
|
|
||||||
# local:
|
|
||||||
# - test_a
|
|
||||||
# - test_c
|
|
||||||
# remote:
|
|
||||||
# - test_b
|
|
||||||
#
|
|
||||||
# add test_c to exludes
|
|
||||||
FileUtils.cp(sync_test, File.join(local, 'test_a.rb'))
|
|
||||||
FileUtils.cp(sync_test, File.join(local, 'test_c.rb'))
|
|
||||||
FileUtils.cp(sync_test, File.join(remote, 'test_b.rb'))
|
|
||||||
|
|
||||||
# ensure a is not on remote
|
|
||||||
assert !File.exists?(File.join(remote, 'test_a.rb')), "A should not be on remote"
|
|
||||||
# ensure c is not on remote
|
|
||||||
assert !File.exists?(File.join(remote, 'test_c.rb')), "C should not be on remote"
|
|
||||||
# ensure b is on remote
|
|
||||||
assert File.exists?(File.join(remote, 'test_b.rb')), "B should be on remote"
|
|
||||||
|
|
||||||
$stderr.puts local
|
|
||||||
$stderr.puts remote
|
|
||||||
|
|
||||||
Hydra::Sync.new(
|
|
||||||
{
|
|
||||||
:type => :ssh,
|
|
||||||
:connect => 'localhost',
|
|
||||||
:directory => remote,
|
|
||||||
:runners => 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
:directory => local,
|
|
||||||
:exclude => ['test_c.rb']
|
|
||||||
}
|
|
||||||
)
|
|
||||||
# ensure a is copied
|
|
||||||
assert File.exists?(File.join(remote, 'test_a.rb')), "A was not copied"
|
|
||||||
# ensure c is not copied
|
|
||||||
assert !File.exists?(File.join(remote, 'test_c.rb')), "C was copied, should be excluded"
|
|
||||||
# ensure b is deleted
|
|
||||||
assert !File.exists?(File.join(remote, 'test_b.rb')), "B was not deleted"
|
|
||||||
end
|
|
||||||
|
|
||||||
should "synchronize a test file over ssh with rsync to multiple workers" do
|
|
||||||
local = File.join(Dir.consistent_tmpdir, 'hydra', 'local')
|
|
||||||
remote_a = File.join(Dir.consistent_tmpdir, 'hydra', 'remote_a')
|
|
||||||
remote_b = File.join(Dir.consistent_tmpdir, 'hydra', 'remote_b')
|
|
||||||
sync_test = File.join(File.dirname(__FILE__), 'fixtures', 'sync_test.rb')
|
|
||||||
[local, remote_a, remote_b].each{|f| FileUtils.rm_rf f; FileUtils.mkdir_p f}
|
|
||||||
|
|
||||||
# setup the folders:
|
|
||||||
# local:
|
|
||||||
# - test_a
|
|
||||||
# remote_a:
|
|
||||||
# - test_b
|
|
||||||
# remote_b:
|
|
||||||
# - test_c
|
|
||||||
#
|
|
||||||
# add test_c to exludes
|
|
||||||
FileUtils.cp(sync_test, File.join(local, 'test_a.rb'))
|
|
||||||
FileUtils.cp(sync_test, File.join(remote_a, 'test_b.rb'))
|
|
||||||
FileUtils.cp(sync_test, File.join(remote_b, 'test_c.rb'))
|
|
||||||
|
|
||||||
# ensure a is not on remotes
|
|
||||||
assert !File.exists?(File.join(remote_a, 'test_a.rb')), "A should not be on remote_a"
|
|
||||||
assert !File.exists?(File.join(remote_b, 'test_a.rb')), "A should not be on remote_b"
|
|
||||||
# ensure b is on remote_a
|
|
||||||
assert File.exists?(File.join(remote_a, 'test_b.rb')), "B should be on remote_a"
|
|
||||||
# ensure c is on remote_b
|
|
||||||
assert File.exists?(File.join(remote_b, 'test_c.rb')), "C should be on remote_b"
|
|
||||||
|
|
||||||
Hydra::Sync.sync_many(
|
|
||||||
:workers => [{
|
|
||||||
:type => :ssh,
|
|
||||||
:connect => 'localhost',
|
|
||||||
:directory => remote_a,
|
|
||||||
:runners => 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
:type => :ssh,
|
|
||||||
:connect => 'localhost',
|
|
||||||
:directory => remote_b,
|
|
||||||
:runners => 1
|
|
||||||
}],
|
|
||||||
:sync => {
|
|
||||||
:directory => local
|
|
||||||
}
|
|
||||||
)
|
|
||||||
# ensure a is copied to both remotes
|
|
||||||
assert File.exists?(File.join(remote_a, 'test_a.rb')), "A was not copied to remote_a"
|
|
||||||
assert File.exists?(File.join(remote_b, 'test_a.rb')), "A was not copied to remote_b"
|
|
||||||
# ensure b and c are deleted from remotes
|
|
||||||
assert !File.exists?(File.join(remote_a, 'test_b.rb')), "B was not deleted from remote_a"
|
|
||||||
assert !File.exists?(File.join(remote_b, 'test_c.rb')), "C was not deleted from remote_b"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,21 +0,0 @@
|
||||||
require 'test_helper'
|
|
||||||
require 'hydra/tasks'
|
|
||||||
require 'rake'
|
|
||||||
|
|
||||||
class TaskTest < Test::Unit::TestCase
|
|
||||||
context "a task" do
|
|
||||||
should "execute the command in a remote machine" do
|
|
||||||
|
|
||||||
File.delete( "/tmp/new_file" ) if File.exists? "/tmp/new_file"
|
|
||||||
|
|
||||||
Hydra::RemoteTask.new('cat:text_file', 'touch new_file') do |t|
|
|
||||||
t.config = "test/fixtures/task_test_config.yml"
|
|
||||||
end
|
|
||||||
|
|
||||||
Rake.application['hydra:remote:cat:text_file'].invoke
|
|
||||||
|
|
||||||
assert( File.exists? "/tmp/new_file" )
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,24 +1,22 @@
|
||||||
require 'rubygems'
|
require 'rubygems'
|
||||||
gem 'test-unit'
|
|
||||||
require 'test/unit'
|
require 'test/unit'
|
||||||
require 'shoulda'
|
require 'shoulda'
|
||||||
require 'tmpdir'
|
require 'tmpdir'
|
||||||
require "stringio"
|
|
||||||
|
|
||||||
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
||||||
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
||||||
require 'hydra'
|
require 'hydra'
|
||||||
|
|
||||||
# Since Hydra turns off testing, we have to turn it back on
|
# Since Hydra turns off testing, we have to turn it back on
|
||||||
#Test::Unit.run = false
|
Test::Unit.run = false
|
||||||
|
|
||||||
class Test::Unit::TestCase
|
class Test::Unit::TestCase
|
||||||
def target_file
|
def target_file
|
||||||
File.expand_path(File.join(Dir.consistent_tmpdir, 'hydra_test.txt'))
|
File.expand_path(File.join(Dir.tmpdir, 'hydra_test.txt'))
|
||||||
end
|
end
|
||||||
|
|
||||||
def alternate_target_file
|
def alternate_target_file
|
||||||
File.expand_path(File.join(Dir.consistent_tmpdir, 'alternate_hydra_test.txt'))
|
File.expand_path(File.join(Dir.tmpdir, 'alternate_hydra_test.txt'))
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_file
|
def test_file
|
||||||
|
@ -33,10 +31,6 @@ class Test::Unit::TestCase
|
||||||
File.expand_path(File.join(File.dirname(__FILE__), 'fixtures', 'write_file_alternate_spec.rb'))
|
File.expand_path(File.join(File.dirname(__FILE__), 'fixtures', 'write_file_alternate_spec.rb'))
|
||||||
end
|
end
|
||||||
|
|
||||||
def rspec_file_with_pending
|
|
||||||
File.expand_path(File.join(File.dirname(__FILE__), 'fixtures', 'write_file_with_pending_spec.rb'))
|
|
||||||
end
|
|
||||||
|
|
||||||
def cucumber_feature_file
|
def cucumber_feature_file
|
||||||
File.expand_path(File.join(File.dirname(__FILE__), 'fixtures', 'features', 'write_file.feature'))
|
File.expand_path(File.join(File.dirname(__FILE__), 'fixtures', 'features', 'write_file.feature'))
|
||||||
end
|
end
|
||||||
|
@ -44,50 +38,6 @@ class Test::Unit::TestCase
|
||||||
def alternate_cucumber_feature_file
|
def alternate_cucumber_feature_file
|
||||||
File.expand_path(File.join(File.dirname(__FILE__), 'fixtures', 'features', 'write_alternate_file.feature'))
|
File.expand_path(File.join(File.dirname(__FILE__), 'fixtures', 'features', 'write_alternate_file.feature'))
|
||||||
end
|
end
|
||||||
|
|
||||||
def javascript_file
|
|
||||||
File.expand_path(File.join(File.dirname(__FILE__), 'fixtures', 'js_file.js'))
|
|
||||||
end
|
|
||||||
|
|
||||||
def json_file
|
|
||||||
File.expand_path(File.join(File.dirname(__FILE__), 'fixtures', 'json_data.json'))
|
|
||||||
end
|
|
||||||
|
|
||||||
def conflicting_test_file
|
|
||||||
File.expand_path(File.join(File.dirname(__FILE__), 'fixtures', 'conflicting.rb'))
|
|
||||||
end
|
|
||||||
|
|
||||||
def remote_dir_path
|
|
||||||
File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
|
|
||||||
end
|
|
||||||
|
|
||||||
def hydra_worker_init_file
|
|
||||||
File.expand_path(File.join(File.dirname(__FILE__), 'fixtures', 'hydra_worker_init.rb'))
|
|
||||||
end
|
|
||||||
|
|
||||||
def capture_stderr
|
|
||||||
# The output stream must be an IO-like object. In this case we capture it in
|
|
||||||
# an in-memory IO object so we can return the string value. You can assign any
|
|
||||||
# IO object here.
|
|
||||||
previous_stderr, $stderr = $stderr, StringIO.new
|
|
||||||
yield
|
|
||||||
$stderr.string
|
|
||||||
ensure
|
|
||||||
# Restore the previous value of stderr (typically equal to STDERR).
|
|
||||||
$stderr = previous_stderr
|
|
||||||
end
|
|
||||||
|
|
||||||
#this method allow us to wait for a file for a maximum number of time, so the
|
|
||||||
#test can pass in slower machines. This helps to speed up the tests
|
|
||||||
def assert_file_exists file, time_to_wait = 2
|
|
||||||
time_begin = Time.now
|
|
||||||
|
|
||||||
until Time.now - time_begin >= time_to_wait or File.exists?( file ) do
|
|
||||||
sleep 0.01
|
|
||||||
end
|
|
||||||
|
|
||||||
assert File.exists?( file )
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
module Hydra #:nodoc:
|
module Hydra #:nodoc:
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
require 'test_helper'
|
require File.join(File.dirname(__FILE__), 'test_helper')
|
||||||
|
|
||||||
class WorkerTest < Test::Unit::TestCase
|
class WorkerTest < Test::Unit::TestCase
|
||||||
context "with a file to test and a destination to verify" do
|
context "with a file to test and a destination to verify" do
|
||||||
|
@ -36,15 +36,13 @@ class WorkerTest < Test::Unit::TestCase
|
||||||
module WorkerTestHelper
|
module WorkerTestHelper
|
||||||
def run_the_worker(pipe, num_runners)
|
def run_the_worker(pipe, num_runners)
|
||||||
pipe.identify_as_child
|
pipe.identify_as_child
|
||||||
Hydra::Worker.new({:io => pipe, :runners => num_runners, :options => {}})
|
Hydra::Worker.new({:io => pipe, :runners => num_runners})
|
||||||
end
|
end
|
||||||
|
|
||||||
def request_a_file_and_verify_completion(pipe, num_runners)
|
def request_a_file_and_verify_completion(pipe, num_runners)
|
||||||
pipe.identify_as_parent
|
pipe.identify_as_parent
|
||||||
pipe.gets # grab the WorkerBegin
|
|
||||||
num_runners.times do
|
num_runners.times do
|
||||||
response = pipe.gets # grab the RequestFile
|
assert pipe.gets.is_a?(Hydra::Messages::Worker::RequestFile)
|
||||||
assert response.is_a?(Hydra::Messages::Worker::RequestFile), "Expected RequestFile but got #{response.class.to_s}"
|
|
||||||
end
|
end
|
||||||
pipe.write(Hydra::Messages::Master::RunFile.new(:file => test_file))
|
pipe.write(Hydra::Messages::Master::RunFile.new(:file => test_file))
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue