Compare commits

..

1 Commits

Author SHA1 Message Date
Nick Gauthier 825368a7ec first spike on ruby 1.9 w/ minitest 2010-04-16 10:12:15 -04:00
57 changed files with 384 additions and 7354 deletions

3
.gitignore vendored
View File

@ -17,8 +17,5 @@ tmtags
coverage
rdoc
pkg
tags
.rvmrc
hydra-runner.log
## PROJECT::SPECIFIC

View File

@ -1,5 +0,0 @@
source :rubygems
gemspec
gem 'rake', '0.8.7'
gem 'test-unit', :require => 'test/unit'

View File

@ -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)

View File

@ -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
a copy of this software and associated documentation files (the

View File

@ -11,10 +11,8 @@ begin
gem.homepage = "http://github.com/ngauthier/hydra"
gem.authors = ["Nick Gauthier"]
gem.add_development_dependency "shoulda", "= 2.10.3"
gem.add_development_dependency "rspec", "~> 2.6.0"
gem.add_development_dependency "rspec-core", "= 2.6.4"
gem.add_development_dependency "cucumber", "= 0.9.2"
gem.add_development_dependency "therubyracer", "= 0.7.4"
gem.add_development_dependency "rspec", "= 1.3.0"
gem.add_development_dependency "cucumber", "= 0.6.4"
end
Jeweler::GemcutterTasks.new
rescue LoadError
@ -23,7 +21,7 @@ end
require 'rake/testtask'
Rake::TestTask.new(:test) do |test|
test.libs << 'test'
test.libs << 'lib' << 'test'
test.pattern = 'test/**/*_test.rb'
test.verbose = true
end
@ -41,6 +39,8 @@ rescue LoadError
end
end
task :test => :check_dependencies
task :default => :test
require 'rake/rdoctask'

View File

@ -1 +1 @@
0.23.3
0.16.2

View File

@ -1,191 +1,117 @@
# Generated by jeweler
# 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 -*-
Gem::Specification.new do |s|
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.authors = [%q{Nick Gauthier}]
s.date = %q{2011-08-31}
s.authors = ["Nick Gauthier"]
s.date = %q{2010-04-11}
s.description = %q{Spread your tests over multiple machines to test your code faster.}
s.email = %q{nick@smartlogicsolutions.com}
s.extra_rdoc_files = [
"LICENSE",
"README.rdoc",
"TODO"
"README.rdoc",
"TODO"
]
s.files = [
".document",
"Gemfile",
"Gemfile.lock",
"LICENSE",
"README.rdoc",
"Rakefile",
"TODO",
"VERSION",
"caliper.yml",
"hydra-icon-64x64.png",
"hydra.gemspec",
"hydra_gray.png",
"lib/hydra.rb",
"lib/hydra/config.rb",
"lib/hydra/cucumber/formatter.rb",
"lib/hydra/cucumber/partial_html.rb",
"lib/hydra/hash.rb",
"lib/hydra/js/lint.js",
"lib/hydra/listener/abstract.rb",
"lib/hydra/listener/cucumber.css",
"lib/hydra/listener/cucumber_html_report.rb",
"lib/hydra/listener/jquery-min.js",
"lib/hydra/listener/minimal_output.rb",
"lib/hydra/listener/notifier.rb",
"lib/hydra/listener/progress_bar.rb",
"lib/hydra/listener/report_generator.rb",
"lib/hydra/master.rb",
"lib/hydra/message.rb",
"lib/hydra/message/master_messages.rb",
"lib/hydra/message/runner_messages.rb",
"lib/hydra/message/worker_messages.rb",
"lib/hydra/messaging_io.rb",
"lib/hydra/pipe.rb",
"lib/hydra/runner.rb",
"lib/hydra/runner_listener/abstract.rb",
"lib/hydra/safe_fork.rb",
"lib/hydra/spec/autorun_override.rb",
"lib/hydra/spec/hydra_formatter.rb",
"lib/hydra/ssh.rb",
"lib/hydra/stdio.rb",
"lib/hydra/sync.rb",
"lib/hydra/tasks.rb",
"lib/hydra/tmpdir.rb",
"lib/hydra/trace.rb",
"lib/hydra/worker.rb",
"test/fixtures/assert_true.rb",
"test/fixtures/config.yml",
"test/fixtures/conflicting.rb",
"test/fixtures/features/step_definitions.rb",
"test/fixtures/features/write_alternate_file.feature",
"test/fixtures/features/write_file.feature",
"test/fixtures/hello_world.rb",
"test/fixtures/hydra_worker_init.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"
".gitignore",
"LICENSE",
"README.rdoc",
"Rakefile",
"TODO",
"VERSION",
"caliper.yml",
"hydra-icon-64x64.png",
"hydra.gemspec",
"hydra_gray.png",
"lib/hydra.rb",
"lib/hydra/cucumber/formatter.rb",
"lib/hydra/hash.rb",
"lib/hydra/listener/abstract.rb",
"lib/hydra/listener/minimal_output.rb",
"lib/hydra/listener/notifier.rb",
"lib/hydra/listener/progress_bar.rb",
"lib/hydra/listener/report_generator.rb",
"lib/hydra/master.rb",
"lib/hydra/message.rb",
"lib/hydra/message/master_messages.rb",
"lib/hydra/message/runner_messages.rb",
"lib/hydra/message/worker_messages.rb",
"lib/hydra/messaging_io.rb",
"lib/hydra/pipe.rb",
"lib/hydra/runner.rb",
"lib/hydra/safe_fork.rb",
"lib/hydra/spec/autorun_override.rb",
"lib/hydra/spec/hydra_formatter.rb",
"lib/hydra/ssh.rb",
"lib/hydra/stdio.rb",
"lib/hydra/tasks.rb",
"lib/hydra/trace.rb",
"lib/hydra/worker.rb",
"test/fixtures/assert_true.rb",
"test/fixtures/config.yml",
"test/fixtures/features/step_definitions.rb",
"test/fixtures/features/write_alternate_file.feature",
"test/fixtures/features/write_file.feature",
"test/fixtures/hello_world.rb",
"test/fixtures/slow.rb",
"test/fixtures/sync_test.rb",
"test/fixtures/write_file.rb",
"test/fixtures/write_file_alternate_spec.rb",
"test/fixtures/write_file_spec.rb",
"test/master_test.rb",
"test/message_test.rb",
"test/pipe_test.rb",
"test/runner_test.rb",
"test/ssh_test.rb",
"test/test_helper.rb",
"test/worker_test.rb"
]
s.homepage = %q{http://github.com/ngauthier/hydra}
s.require_paths = [%q{lib}]
s.rubygems_version = %q{1.8.9}
s.rdoc_options = ["--charset=UTF-8"]
s.require_paths = ["lib"]
s.rubygems_version = %q{1.3.6}
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
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
s.specification_version = 3
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
s.add_runtime_dependency(%q<rake>)
s.add_runtime_dependency(%q<test-unit>, [">= 0"])
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
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<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"])
s.add_development_dependency(%q<rspec>, ["= 1.3.0"])
s.add_development_dependency(%q<cucumber>, ["= 0.6.4"])
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<cucumber>, ["= 0.9.2"])
s.add_dependency(%q<therubyracer>, ["= 0.7.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"])
s.add_dependency(%q<rspec>, ["= 1.3.0"])
s.add_dependency(%q<cucumber>, ["= 0.6.4"])
end
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<cucumber>, ["= 0.9.2"])
s.add_dependency(%q<therubyracer>, ["= 0.7.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"])
s.add_dependency(%q<rspec>, ["= 1.3.0"])
s.add_dependency(%q<cucumber>, ["= 0.6.4"])
end
end

View File

@ -7,10 +7,9 @@ require 'hydra/safe_fork'
require 'hydra/runner'
require 'hydra/worker'
require 'hydra/master'
require 'hydra/sync'
require 'hydra/listener/abstract'
require 'hydra/listener/minimal_output'
require 'hydra/listener/report_generator'
require 'hydra/listener/notifier'
require 'hydra/listener/progress_bar'
require 'hydra/runner_listener/abstract'

View File

@ -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

View File

@ -19,6 +19,7 @@ module Cucumber #:nodoc:
print_steps(:failed)
print_snippets(@options)
print_passing_wip(@options)
print_tag_limit_warnings(features)
end
# Removed all progress output

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -2,7 +2,7 @@ module Hydra #:nodoc:
module Listener #:nodoc:
# Abstract listener that implements all the events
# but does nothing.
class Abstract
class Abstract
# Create a new listener.
#
# Output: The IO object for outputting any information.
@ -10,23 +10,14 @@ module Hydra #:nodoc:
def initialize(output = $stdout)
@output = output
end
# Fired when testing has started
def testing_begin(files)
end
# Fired when testing finishes, after the workers shutdown
# Fired when testing finishes
def testing_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
def file_begin(file)
end

View File

@ -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;
}

View File

@ -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

View File

@ -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);

View File

@ -34,7 +34,7 @@ module Hydra #:nodoc:
complete = ((@files_completed.to_f / @total_files.to_f) * width).to_i
@output.write "\r" # move to beginning
@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 '#'}
@output.write '>'
(width-complete).times{@output.write ' '}

View File

@ -18,7 +18,6 @@ module Hydra #:nodoc:
def file_end(file, output)
@report[file]['end'] = Time.now.to_f
@report[file]['duration'] = @report[file]['end'] - @report[file]['start']
@report[file]['all_tests_passed_last_run'] = (output == '.')
end
# output the report
@ -29,5 +28,3 @@ module Hydra #:nodoc:
end
end
end

View File

@ -1,22 +1,15 @@
require 'hydra/hash'
require 'hydra/config'
require 'open3'
require 'hydra/tmpdir'
require 'erb'
require 'tmpdir'
require 'yaml'
module Hydra #:nodoc:
# Hydra class responsible for delegate work down to workers.
#
# The Master is run once for any given testing session.
class YmlLoadError < StandardError; end
class Master
include Hydra::Messages::Master
include Open3
traceable('MASTER')
attr_reader :failed_files
# Create a new Master
#
# Options:
@ -38,14 +31,11 @@ module Hydra #:nodoc:
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!)
opts.merge!(YAML.load_file(config_file).stringify_keys!)
end
@files = Array(opts.fetch('files') { nil })
raise "No files, nothing to do" if @files.empty?
@incomplete_files = @files.dup
@failed_files = []
@workers = []
@listeners = []
@event_listeners = Array(opts.fetch('listeners') { nil } )
@ -54,36 +44,18 @@ module Hydra #:nodoc:
listener = eval(l)
@event_listeners << listener if listener.is_a?(Hydra::Listener::Abstract)
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 }
@autosort = opts.fetch('autosort') { true }
@sync = opts.fetch('sync') { nil }
@environment = opts.fetch('environment') { 'test' } || 'test'
@options = opts.fetch('options') { '' }
if @autosort
sort_files_from_report
sort_files_from_report
@event_listeners << Hydra::Listener::ReportGenerator.new(File.new(heuristic_file, 'w'))
end
# default is one worker that is configured to use a pipe with one runner
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 " Files: (#{@files.inspect})"
trace " Workers: (#{worker_cfg.inspect})"
@ -96,11 +68,8 @@ module Hydra #:nodoc:
end
# 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)
f = @files.shift
if f
@ -114,29 +83,13 @@ module Hydra #:nodoc:
# Process the results coming back from the worker.
def process_results(worker, message)
if message.output =~ /ActiveRecord::StatementInvalid(.*)[Dd]eadlock/ or
message.output =~ /PGError: ERROR(.*)[Dd]eadlock/ or
message.output =~ /Mysql::Error: SAVEPOINT(.*)does not exist: ROLLBACK/ or
message.output =~ /Mysql::Error: Deadlock found/
trace "Deadlock detected running [#{message.file}]. Will retry at the end"
@files.push(message.file)
send_file(worker)
@incomplete_files.delete_at(@incomplete_files.index(message.file))
trace "#{@incomplete_files.size} Files Remaining"
@event_listeners.each{|l| l.file_end(message.file, message.output) }
if @incomplete_files.empty?
shutdown_all_workers
else
@incomplete_files.delete_at(@incomplete_files.index(message.file))
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
send_file(worker)
end
end
@ -144,7 +97,7 @@ module Hydra #:nodoc:
attr_reader :report_text
private
def boot_workers(workers)
trace "Booting #{workers.size} workers"
workers.each do |worker|
@ -163,39 +116,57 @@ module Hydra #:nodoc:
def boot_local_worker(worker)
runners = worker.fetch('runners') { raise "You must specify the number of runners" }
trace "Booting local worker"
trace "Booting local worker"
pipe = Hydra::Pipe.new
child = SafeFork.fork do
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
pipe.identify_as_parent
@workers << { :pid => child, :io => pipe, :idle => false, :type => :local }
end
def boot_ssh_worker(worker)
sync = Sync.new(worker, @sync, @verbose)
if sync.result == 0
runners = worker.fetch('runners') { raise "You must specify the number of runners" }
command = worker.fetch('command') {
%{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 => {} );"}
}
runners = worker.fetch('runners') { raise "You must specify the number of runners" }
connect = worker.fetch('connect') { raise "You must specify an SSH connection target" }
ssh_opts = worker.fetch('ssh_opts') { "" }
directory = worker.fetch('directory') { raise "You must specify a remote directory" }
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"
trace command
ssh = Hydra::SSH.new("#{sync.ssh_opts} #{sync.connect}", sync.remote_dir, command, worker['timeout'])
return { :io => ssh, :idle => false, :type => :ssh, :connect => sync.connect }
else
false
if @sync
@sync.stringify_keys!
trace "Synchronizing with #{connect}\n\t#{@sync.inspect}"
local_dir = @sync.fetch('directory') {
raise "You must specify a synchronization directory"
}
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
trace "Booting SSH worker"
ssh = Hydra::SSH.new("#{ssh_opts} #{connect}", directory, command)
return { :io => ssh, :idle => false, :type => :ssh }
end
def shutdown_all_workers
trace "Shutting down all workers"
@workers.each do |worker|
worker[:io].write(Shutdown.new) if worker[:io]
worker[:io].close if worker[:io]
worker[:io].close if worker[:io]
end
@listeners.each{|t| t.exit}
end
@ -210,31 +181,25 @@ module Hydra #:nodoc:
trace "Listening to #{worker.inspect}"
if worker.fetch('type') { 'local' }.to_s == 'ssh'
worker = boot_ssh_worker(worker)
@workers << worker if worker
@workers << worker
end
if worker
dead_count = 0
while true
begin
message = worker[:io].gets
trace "got message: #{message}"
# if it exists and its for me.
# SSH gives us back echoes, so we need to ignore our own messages
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
while true
begin
message = worker[:io].gets
trace "got message: #{message}"
# if it exists and its for me.
# SSH gives us back echoes, so we need to ignore our own messages
if message and !message.class.to_s.index("Worker").nil?
message.handle(self, worker)
end
rescue IOError
trace "lost Worker [#{worker.inspect}]"
Thread.exit
end
end
end
end
@listeners.each{|l| l.join}
@event_listeners.each{|l| l.testing_end}
end
@ -254,7 +219,7 @@ module Hydra #:nodoc:
end
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

View File

@ -8,12 +8,6 @@ module Hydra #:nodoc:
end
end
class WorkerBegin < Hydra::Message
def handle(master, worker)
master.worker_begin(worker)
end
end
# Message telling the Runner to run a file
class RunFile < Hydra::Message
# The file that should be run

View File

@ -8,21 +8,14 @@ module Hydra #:nodoc:
# IO.gets
# => Hydra::Message # or subclass
def gets
while true
begin
raise IOError unless @reader
message = nil
if result = Kernel.select([@reader], [], [], @timeout)
message = @reader.gets
end
return Message.build(:class => Hydra::Messages::Master::Shutdown) if result == nil
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
raise IOError unless @reader
message = @reader.gets
return nil unless message
return Message.build(eval(message.chomp))
rescue SyntaxError, NameError
# uncomment to help catch remote errors by seeing all traffic
#$stderr.write "Not a message: [#{message.inspect}]\n"
return gets
end
# Write a Message to the output IO object. It will automatically

View File

@ -31,10 +31,9 @@ module Hydra #:nodoc:
class Pipe
include Hydra::MessagingIO
# Creates a new uninitialized pipe pair.
def initialize(timeout = nil)
def initialize
@child_read, @parent_write = IO.pipe
@parent_read, @child_write = IO.pipe
@timeout = timeout
end
# Identify this side of the pipe as the child.

View File

@ -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:
# Hydra class responsible for running test files.
#
@ -9,26 +22,14 @@ module Hydra #:nodoc:
class Runner
include Hydra::Messages::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
# parent) to send it messages on which files to execute.
def initialize(opts = {})
redirect_output( opts.fetch( :runner_log_file ) { DEFAULT_LOG_FILE } )
reg_trap_sighup
@io = opts.fetch(:io) { raise "No IO Object" }
@verbose = opts.fetch(:verbose) { false }
@event_listeners = Array( opts.fetch( :runner_listeners ) { nil } )
@options = opts.fetch(:options)
@io = opts.fetch(:io) { raise "No IO Object" }
@verbose = opts.fetch(:verbose) { false }
$stdout.sync = true
runner_begin
trace 'Booted. Sending Request for file'
@io.write RequestFile.new
begin
process_messages
@ -38,32 +39,15 @@ module Hydra #:nodoc:
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
def run_file(file)
trace "Running file: #{file}"
output = ""
if file =~ /_spec.rb$/i
if file =~ /_spec.rb$/
output = run_rspec_file(file)
elsif file =~ /.feature$/i
elsif file =~ /.feature$/
output = run_cucumber_file(file)
elsif file =~ /.js$/i or file =~ /.json$/i
output = run_javascript_file(file)
else
output = run_test_unit_file(file)
end
@ -76,17 +60,11 @@ module Hydra #:nodoc:
# Stop running
def stop
runner_end if @runner_began
@runner_began = @running = false
@running = false
end
def runner_end
trace "Ending runner #{self.inspect}"
@event_listeners.each {|l| l.runner_end( self ) }
end
def format_exception(ex)
"#{ex.class.name}: #{ex.message}\n #{ex.backtrace.join("\n ")}"
def puke(klass, name, exception)
puts "puke! #{klass}, #{name}, #{exception}"
end
private
@ -104,46 +82,46 @@ module Hydra #:nodoc:
message.handle(self)
else
@io.write Ping.new
sleep WAIT_BETWEEN_PING
end
rescue IOError => ex
trace "Runner lost Worker"
stop
@running = false
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
def run_test_unit_file(file)
begin
gem 'test-unit'
require 'test/unit'
require 'test/unit/testresult'
Test::Unit.run = true
require file
rescue LoadError => ex
trace "#{file} does not exist [#{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
output = []
@result = Test::Unit::TestResult.new
@result.add_listener(Test::Unit::TestResult::FAULT) do |value|
output << value
end
# @result = Test::Unit::TestResult.new
# @result.add_listener(Test::Unit::TestResult::FAULT) do |value|
# output << value
# end
klasses = Runner.find_classes_in_file(file)
puts "Klasses: #{klasses.inspect}"
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
output << format_ex_in_file(file, ex)
output << ex.to_s
end
return output.join("\n")
@ -153,111 +131,74 @@ module Hydra #:nodoc:
def run_rspec_file(file)
# pull in rspec
begin
require 'rspec'
require 'spec'
require 'hydra/spec/hydra_formatter'
# Ensure we override rspec's at_exit
RSpec::Core::Runner.disable_autorun!
require 'hydra/spec/autorun_override'
rescue LoadError => ex
return ex.to_s
end
@hydra_output ||= StringIO.new
@hydra_output.rewind
@hydra_output.truncate(0)
hydra_output = StringIO.new
Spec::Runner.options.instance_variable_set(:@formatters, [
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 ]
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 : ""
return output
end
# run all the scenarios in a cucumber feature file
def run_cucumber_file(file)
files = [file]
dev_null = StringIO.new
hydra_response = StringIO.new
options = @options if @options.is_a?(Array)
options = @options.split(' ') if @options.is_a?(String)
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'
unless @step_mother
require 'cucumber'
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
cuke = Cucumber::Cli::Main.new(args, dev_null, dev_null)
cuke.configuration.formats << ['Cucumber::Formatter::Hydra', hydra_response]
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?
@step_mother.options = @cuke_configuration.options
@step_mother.log = @cuke_configuration.log
@step_mother.load_code_files(@cuke_configuration.support_to_load)
@step_mother.after_configuration(@cuke_configuration)
@step_mother.load_code_files(@cuke_configuration.step_defs_to_load)
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.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
return hydra_response.read
end
# find all the test unit classes in a given file, so we can run their suites
@ -281,7 +222,11 @@ module Hydra #:nodoc:
nil
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
# Yanked a method from Cucumber
@ -295,16 +240,5 @@ module Hydra #:nodoc:
end
end.compact
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

View File

@ -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

View File

@ -2,13 +2,13 @@ class SafeFork
def self.fork
begin
# 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
child = Process.fork do
begin
# create a new connection and perform the action
begin
ActiveRecord::Base.establish_connection((connection || {}).merge({:allow_concurrency => true})) if defined?(ActiveRecord)
ActiveRecord::Base.establish_connection if defined?(ActiveRecord)
rescue ActiveRecord::AdapterNotSpecified
# AR was defined but we didn't have a connection
end
@ -21,7 +21,7 @@ class SafeFork
ensure
# make sure we re-establish the connection before returning to the main instance
begin
ActiveRecord::Base.establish_connection((connection || {}).merge({:allow_concurrency => true})) if defined?(ActiveRecord)
ActiveRecord::Base.establish_connection if defined?(ActiveRecord)
rescue ActiveRecord::AdapterNotSpecified
# AR was defined but we didn't have a connection
end

View File

@ -1,3 +1,12 @@
if defined?(RSpec)
RSpec::Core::Runner.disable_autorun!
if defined?(Spec)
module Spec
module Runner
class << self
# stop the auto-run at_exit
def run
return 0
end
end
end
end
end

View File

@ -1,22 +1,11 @@
require 'rspec/core/formatters/progress_formatter'
module RSpec
module Core
module Formatters
class HydraFormatter < ProgressFormatter
def example_passed(example)
output.print "."
end
def example_failed(example)
output.print "F"
end
require 'spec/runner/formatter/progress_bar_formatter'
module Spec
module Runner
module Formatter
class HydraFormatter < ProgressBarFormatter
# Stifle the post-test summary
def dump_summary(duration, example, failure, pending)
end
# Stifle pending specs
def dump_pending
end
end
end
end

View File

@ -25,9 +25,16 @@ module Hydra #:nodoc:
# 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
# list all the files.
def initialize(connection_options, directory, command, timeout = nil)
@timeout = timeout
@writer, @reader, @error = popen3(%{ssh -tt #{connection_options} 'mkdir -p #{directory} && cd #{directory} && #{command}'})
def initialize(connection_options, directory, command)
@writer, @reader, @error = popen3("ssh -tt #{connection_options}")
@writer.write("cd #{directory}\n")
@writer.write(command+"\n")
end
# Close the SSH connection
def close
@writer.write "exit\n"
super
end
end
end

View File

@ -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

View File

@ -1,15 +1,10 @@
require 'open3'
require 'hydra/config'
module Hydra #:nodoc:
# Hydra Task Common attributes and methods
class Task
# Name of the task. Default 'hydra'
attr_accessor :name
# Command line options
attr_accessor :options
# Files to test.
# You can add files manually via:
# t.files << [file1, file2, etc]
@ -36,20 +31,6 @@ module Hydra #:nodoc:
# t.listeners << Hydra::Listener::Notifier.new
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
def find_config_file
@ -59,7 +40,7 @@ module Hydra #:nodoc:
return @config if File.exists?(@config)
@config = nil
end
# Add files to test by passing in a string to be run through Dir.glob.
# For example:
#
@ -78,7 +59,7 @@ module Hydra #:nodoc:
# t.add_files 'test/integration/**/*_test.rb'
# t.verbose = false # optionally set to true for lots of debug messages
# t.autosort = false # disable automatic sorting based on runtime of tests
# end
# end
class TestTask < Hydra::Task
# Create a new HydraTestTask
@ -87,30 +68,20 @@ module Hydra #:nodoc:
@files = []
@verbose = false
@autosort = true
@serial = false
@listeners = [Hydra::Listener::ProgressBar.new]
@show_time = true
@options = ''
yield self if block_given?
# Ensure we override rspec's at_exit
if defined?(RSpec)
RSpec::Core::Runner.disable_autorun!
end
require 'hydra/spec/autorun_override'
unless @serial
@config = find_config_file
end
@config = find_config_file
@opts = {
:verbose => @verbose,
:autosort => @autosort,
:files => @files,
:listeners => @listeners,
:environment => @environment,
:runner_log_file => @runner_log_file,
:options => @options
:listeners => @listeners
}
if @config
@opts.merge!(:config => @config)
@ -126,132 +97,8 @@ module Hydra #:nodoc:
def define
desc "Hydra Tests" + (@name == :hydra ? "" : " for #{@name}")
task @name do
if Object.const_defined?('Rails') && Rails.env == 'development'
$stderr.puts %{WARNING: Rails Environment is "development". Make sure to set it properly (ex: "RAILS_ENV=test rake hydra")}
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)
Hydra::Master.new(@opts)
#exit(0) #bypass test on_exit output
end
end
end
@ -265,9 +112,8 @@ module Hydra #:nodoc:
include Open3
# Create a new hydra remote task with the given name.
# The task will be named hydra:remote:<name>
def initialize(name, command=nil)
def initialize(name)
@name = name
@command = command
yield self if block_given?
@config = find_config_file
if @config
@ -281,42 +127,34 @@ module Hydra #:nodoc:
def define
desc "Run #{@name} remotely on all workers"
task "hydra:remote:#{@name}" do
config = Hydra::Config.load(@config)
environment = config.fetch('environment') { 'test' }
config = YAML.load_file(@config)
workers = config.fetch('workers') { [] }
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|
@listeners << Thread.new do
begin
@results[worker] = if run_command(worker, @command)
"==== #{@name} passed on #{worker['connect']} ====\n"
else
"==== #{@name} failed on #{worker['connect']} ====\nPlease see above for more details.\n"
end
rescue
@results[worker] = "==== #{@name} failed for #{worker['connect']} ====\n#{$!.inspect}\n#{$!.backtrace.join("\n")}"
$stdout.write "==== Hydra Running #{@name} on #{worker['connect']} ====\n"
ssh_opts = worker.fetch('ssh_opts') { '' }
writer, reader, error = popen3("ssh -tt #{ssh_opts} #{worker['connect']} ")
writer.write("cd #{worker['directory']}\n")
writer.write "echo BEGIN HYDRA\n"
writer.write("RAILS_ENV=test rake #{@name}\n")
writer.write "echo END HYDRA\n"
writer.write("exit\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
$stdout.write "\n==== Hydra Running #{@name} COMPLETE ====\n\n"
end
@listeners.each{|l| l.join}
$stdout.write "\n==== Hydra Running #{@name} COMPLETE ====\n\n"
$stdout.write @results.values.join("\n")
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
# A Hydra global task is a task that is run both locally and remotely.
@ -326,7 +164,7 @@ module Hydra #:nodoc:
# Hydra::GlobalTask.new('db:reset')
#
# Allows you to run:
#
#
# rake hydra:db:reset
#
# Then, db:reset will be run locally and on all remote workers. This
@ -348,7 +186,7 @@ module Hydra #:nodoc:
def define
Hydra::RemoteTask.new(@name)
desc "Run #{@name.to_s} Locally and Remotely across all Workers"
task "hydra:#{@name.to_s}" => [@name.to_s, "hydra:remote:#{@name.to_s}"]
task "hydra:#{@name.to_s}" => [@name.to_s, "hydra:remote:#{@name.to_s}"]
end
end
end

View File

@ -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

View File

@ -9,8 +9,6 @@ module Hydra #:nodoc:
class Worker
include Hydra::Messages::Worker
traceable('WORKER')
attr_reader :runners
# Create a new worker.
# * io: The IO object to use to communicate with the master
# * num_runners: The number of runners to launch
@ -19,37 +17,16 @@ module Hydra #:nodoc:
@io = opts.fetch(:io) { raise "No IO Object" }
@runners = []
@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 })
@io.write(Hydra::Messages::Worker::WorkerBegin.new)
process_messages
@runners.each{|r| Process.wait r[:pid] }
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
# When a runner wants a file, it hits this method with a message.
# Then the worker bubbles the file request up to the master.
def request_file(message, runner)
@ -91,10 +68,9 @@ module Hydra #:nodoc:
trace "Booting #{num_runners} Runners"
num_runners.times do
pipe = Hydra::Pipe.new
child = SafeFork.fork do
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
pipe.identify_as_parent
@runners << { :pid => child, :io => pipe, :idle => false }
@ -123,7 +99,7 @@ module Hydra #:nodoc:
begin
message = @io.gets
if message and !message.class.to_s.index("Master").nil?
trace "Received Message from Master"
trace "Received Message from Master"
trace "\t#{message.inspect}"
message.handle(self)
else
@ -132,7 +108,7 @@ module Hydra #:nodoc:
end
rescue IOError => ex
trace "Worker lost Master"
shutdown
Thread.exit
end
end
end

View File

@ -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 &quot;<span class="param">HYDRA</span>&quot; 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">&quot;<span class="param">HYDRA</span>&quot; 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>

View File

@ -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 &quot;<span class="param">HYDRA</span>&quot; 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">&quot;<span class="param">HYDRA</span>&quot; 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>

View File

@ -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

View File

@ -1,9 +1,9 @@
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
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
When /^I write "([^\"]*)" to the file$/ do |text|

View File

@ -1,2 +0,0 @@
require '../test/fixtures/runner_listeners.rb'
require '../test/fixtures/master_listeners.rb'

View File

@ -1,4 +0,0 @@
"use strict";
var thisvar;
var thatvar

View File

@ -1,4 +0,0 @@
{
"var1": "something",
"var2": "trailing comma",
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -1,5 +1,3 @@
require 'rubygems'
gem 'test-unit'
require 'test/unit'
class SyncTest < Test::Unit::TestCase

View File

@ -1,6 +0,0 @@
---
workers:
- type: ssh
connect: localhost
directory: /tmp
runners: 1

View File

@ -2,7 +2,7 @@ require File.join(File.dirname(__FILE__), '..', 'test_helper')
class WriteFileTest < Test::Unit::TestCase
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"
end
end

View File

@ -1,8 +1,8 @@
require 'rspec'
require 'hydra/tmpdir'
describe "file writing" do
require 'tmpdir'
require 'spec'
context "file writing" 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"
end
end

View File

@ -1,8 +1,8 @@
require 'rspec'
require 'hydra/tmpdir'
describe "file writing" do
require 'tmpdir'
require 'spec'
context "file writing" 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"
end
end

View File

@ -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

View File

@ -1,6 +1,4 @@
require 'test_helper'
require 'fixtures/runner_listeners'
require 'fixtures/master_listeners'
require File.join(File.dirname(__FILE__), 'test_helper')
class MasterTest < Test::Unit::TestCase
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)
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
Hydra::Master.new(:files => [test_file])
assert File.exists?(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 report = YAML.load_file(report_file)
assert_not_nil report[test_file]
@ -119,8 +75,8 @@ class MasterTest < Test::Unit::TestCase
:workers => [{
:type => :ssh,
:connect => 'localhost',
:directory => remote_dir_path,
:runners => 1
:directory => File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib')),
:runners => 1
}]
)
assert File.exists?(target_file)
@ -137,8 +93,8 @@ class MasterTest < Test::Unit::TestCase
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')
local = File.join(Dir.tmpdir, 'hydra', 'local')
remote = File.join(Dir.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}
@ -167,7 +123,6 @@ class MasterTest < Test::Unit::TestCase
:type => :ssh,
:connect => 'localhost',
:directory => remote,
:verbose => true,
:runners => 1
}],
:sync => {
@ -183,225 +138,4 @@ class MasterTest < Test::Unit::TestCase
assert !File.exists?(File.join(remote, 'test_b.rb')), "B was not deleted"
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

View File

@ -1,4 +1,4 @@
require 'test_helper'
require File.join(File.dirname(__FILE__), 'test_helper')
class MessageTest < Test::Unit::TestCase
class MyMessage < Hydra::Message

View File

@ -1,4 +1,4 @@
require 'test_helper'
require File.join(File.dirname(__FILE__), 'test_helper')
class PipeTest < Test::Unit::TestCase
context "a pipe" do

View File

@ -1,5 +1,4 @@
require 'test_helper'
require 'fixtures/runner_listeners'
require File.join(File.dirname(__FILE__), 'test_helper')
class RunnerTest < Test::Unit::TestCase
context "with a file to test and a destination to verify" do
@ -38,20 +37,8 @@ class RunnerTest < Test::Unit::TestCase
Process.wait(child)
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
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)
assert File.exists?(target_file)
assert_equal "HYDRA", File.read(target_file)
@ -61,110 +48,58 @@ class RunnerTest < Test::Unit::TestCase
runner.run_file(alternate_rspec_file)
assert File.exists?(alternate_target_file)
assert_equal "HYDRA", File.read(alternate_target_file)
assert !File.exists?(target_file), "Tests are double running!"
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)
assert !File.exists?(target_file)
end
should "run two cucumber tests" do
# because of all the crap cucumber pulls in
# we run this in a fork to not contaminate
# the main test environment
capture_stderr do # redirect stderr
pid = Process.fork do
runner = Hydra::Runner.new(:options => {}, :io => File.new('/dev/null', 'w'))
runner.run_file(cucumber_feature_file)
assert File.exists?(target_file)
assert_equal "HYDRA", File.read(target_file)
pid = Process.fork do
puts "THE FOLLOWING WARNINGS CAN BE IGNORED"
puts "It is caused by Cucumber loading all rb files near its features"
FileUtils.rm_f(target_file)
runner.run_file(alternate_cucumber_feature_file)
assert File.exists?(alternate_target_file)
assert_equal "HYDRA", File.read(alternate_target_file)
assert !File.exists?(target_file)
end
Process.wait pid
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)
FileUtils.rm_f(target_file)
runner.run_file(alternate_cucumber_feature_file)
assert File.exists?(alternate_target_file)
assert_equal "HYDRA", File.read(alternate_target_file)
assert !File.exists?(target_file)
puts "END IGNORABLE OUTPUT"
end
Process.wait pid
end
should "be able to run a runner over ssh" do
ssh = Hydra::SSH.new(
'localhost -o ControlMaster=no',
'localhost',
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)
ssh.write(Hydra::Messages::Worker::RunFile.new(:file => test_file))
# grab its response. This makes us wait for it to finish
echo = ssh.gets # get the ssh echo
response = ssh.gets
assert_equal Hydra::Messages::Runner::Results, response.class
# tell it to shut down
ssh.write(Hydra::Messages::Worker::Shutdown.new)
ssh.close
# ensure it ran
assert File.exists?(target_file)
assert_equal "HYDRA", File.read(target_file)
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
module RunnerTestHelper
@ -177,6 +112,7 @@ class RunnerTest < Test::Unit::TestCase
# grab its response. This makes us wait for it to finish
response = pipe.gets
puts response.output
# tell it to shut down
pipe.write(Hydra::Messages::Worker::Shutdown.new)
@ -186,9 +122,9 @@ class RunnerTest < Test::Unit::TestCase
assert_equal "HYDRA", File.read(target_file)
end
def run_the_runner(pipe, listeners = [])
def run_the_runner(pipe)
pipe.identify_as_child
Hydra::Runner.new( :io => pipe, :options => {}, :runner_listeners => listeners )
Hydra::Runner.new(:io => pipe)
end
end
include RunnerTestHelper

View File

@ -1,9 +1,9 @@
require 'test_helper'
require File.join(File.dirname(__FILE__), 'test_helper')
class SSHTest < Test::Unit::TestCase
should "be able to execute a command over ssh" do
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
"ruby fixtures/hello_world.rb"
)
@ -11,16 +11,4 @@ class SSHTest < Test::Unit::TestCase
assert_equal "Hello World", response.text
ssh.close
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

View File

@ -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

View File

@ -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

View File

@ -1,24 +1,22 @@
require 'rubygems'
gem 'test-unit'
require 'test/unit'
require 'shoulda'
require 'tmpdir'
require "stringio"
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
$LOAD_PATH.unshift(File.dirname(__FILE__))
require 'hydra'
# Since Hydra turns off testing, we have to turn it back on
#Test::Unit.run = false
Test::Unit.run = false
class Test::Unit::TestCase
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
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
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'))
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
File.expand_path(File.join(File.dirname(__FILE__), 'fixtures', 'features', 'write_file.feature'))
end
@ -44,50 +38,6 @@ class Test::Unit::TestCase
def alternate_cucumber_feature_file
File.expand_path(File.join(File.dirname(__FILE__), 'fixtures', 'features', 'write_alternate_file.feature'))
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
module Hydra #:nodoc:

View File

@ -1,4 +1,4 @@
require 'test_helper'
require File.join(File.dirname(__FILE__), 'test_helper')
class WorkerTest < Test::Unit::TestCase
context "with a file to test and a destination to verify" do
@ -36,15 +36,13 @@ class WorkerTest < Test::Unit::TestCase
module WorkerTestHelper
def run_the_worker(pipe, num_runners)
pipe.identify_as_child
Hydra::Worker.new({:io => pipe, :runners => num_runners, :options => {}})
Hydra::Worker.new({:io => pipe, :runners => num_runners})
end
def request_a_file_and_verify_completion(pipe, num_runners)
pipe.identify_as_parent
pipe.gets # grab the WorkerBegin
num_runners.times do
response = pipe.gets # grab the RequestFile
assert response.is_a?(Hydra::Messages::Worker::RequestFile), "Expected RequestFile but got #{response.class.to_s}"
assert pipe.gets.is_a?(Hydra::Messages::Worker::RequestFile)
end
pipe.write(Hydra::Messages::Master::RunFile.new(:file => test_file))