priority_test 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/.rspec +2 -0
- data/Gemfile +0 -1
- data/README.md +64 -1
- data/Rakefile +9 -12
- data/bin/pt +25 -0
- data/lib/priority_test/autorun.rb +2 -0
- data/lib/priority_test/core/all_tests.rb +30 -0
- data/lib/priority_test/core/configuration.rb +23 -0
- data/lib/priority_test/core/configuration_options.rb +19 -0
- data/lib/priority_test/core/option_parser.rb +84 -0
- data/lib/priority_test/core/priority.rb +62 -0
- data/lib/priority_test/core/priority_sort_element.rb +23 -0
- data/lib/priority_test/core/runner.rb +26 -0
- data/lib/priority_test/core/service.rb +14 -0
- data/lib/priority_test/core/test.rb +53 -0
- data/lib/priority_test/core/test_result.rb +25 -0
- data/lib/priority_test/core/test_result_collector.rb +40 -0
- data/lib/priority_test/core/validations_helper.rb +17 -0
- data/lib/priority_test/core.rb +41 -0
- data/lib/priority_test/gateway/migrations/001_create_tests.rb +11 -0
- data/lib/priority_test/gateway/migrations/002_create_test_results.rb +12 -0
- data/lib/priority_test/gateway/sequel.rb +33 -0
- data/lib/priority_test/gateway.rb +13 -0
- data/lib/priority_test/rspec.rb +19 -0
- data/lib/priority_test/rspec2/example_group_sorter.rb +24 -0
- data/lib/priority_test/rspec2/example_sorter.rb +12 -0
- data/lib/priority_test/rspec2/formatter.rb +35 -0
- data/lib/priority_test/rspec2/patch/example_group.rb +18 -0
- data/lib/priority_test/rspec2/patch/world.rb +7 -0
- data/lib/priority_test/rspec2/relative_path.rb +13 -0
- data/lib/priority_test/rspec2.rb +20 -0
- data/lib/priority_test/version.rb +1 -1
- data/lib/priority_test.rb +25 -2
- data/priority_test.gemspec +4 -5
- data/spec/core/all_tests_spec.rb +53 -0
- data/spec/core/config_spec.rb +53 -0
- data/spec/core/configuration_options_spec.rb +16 -0
- data/spec/core/option_parser_spec.rb +42 -0
- data/spec/core/priority_spec.rb +9 -0
- data/spec/core/service_spec.rb +55 -0
- data/spec/core/test_result_spec.rb +29 -0
- data/spec/core/test_spec.rb +121 -0
- data/spec/gateways/sequel_spec.rb +43 -0
- data/spec/rspec2/formatter_spec.rb +67 -0
- data/spec/spec_helper.rb +18 -0
- data/spec/support/rspec_factory.rb +17 -0
- metadata +80 -5
data/.gitignore
CHANGED
data/.rspec
ADDED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -3,8 +3,71 @@ PriorityTest
|
|
3
3
|
|
4
4
|
# DESCRIPTION
|
5
5
|
|
6
|
-
|
6
|
+
PriorityTest is a gem that delivers fast feedback for your tests by
|
7
|
+
prioritizing them.
|
8
|
+
It prioritizes tests based on two assumptions discovered by [Kent Beck](https://twitter.com/#!/kentbeck) in his tool [JUnit Max](http://junitmax.com/):
|
9
|
+
|
10
|
+
> Test run times generally follow a power law distribution - lots of very short tests and a few very long ones. This means that by running the short tests first you can get most of the feedback in a fraction of the runtime of the whole suite.
|
11
|
+
|
12
|
+
> Failures are not randomly distributed. A test that failed recently is more likely to fail than one that has run correctly a bazillion times in a row. By putting recently failed (and newly written) tests first in the queue, you maximize the information density of that critical first second of feedback.
|
13
|
+
|
14
|
+
PriorityTest inherits from these two assumptions with a simple
|
15
|
+
algorithm and prioritizes your tests by looking at the test running history.
|
16
|
+
|
17
|
+
# ALGORITHM
|
18
|
+
|
19
|
+
PriorityTest captures and stores your test running hisotry.
|
20
|
+
Before each test runs, it looks back X number of the previous test results to calculate the test's Degree of Significant (DoS).
|
21
|
+
It then prioritizes the running order of all the tests based on each test's DoS.
|
22
|
+
Two factors determines a test's DoS: test run time and recent failure times.
|
7
23
|
|
8
24
|
# INSTALLATION
|
9
25
|
|
26
|
+
## RubyGems
|
27
|
+
|
28
|
+
[sudo] gem install priority_test
|
29
|
+
|
30
|
+
## RSpec
|
31
|
+
|
32
|
+
In your ```Gemfile```, insert the following line:
|
33
|
+
|
34
|
+
```ruby
|
35
|
+
gem 'priority_test'
|
36
|
+
```
|
37
|
+
|
38
|
+
In ```spec_helper.rb```, require the RSpec adapter:
|
39
|
+
|
40
|
+
```ruby
|
41
|
+
require 'priority_test/rspec'
|
42
|
+
```
|
43
|
+
|
10
44
|
# USAGE
|
45
|
+
|
46
|
+
Getting help:
|
47
|
+
|
48
|
+
$ pt -h
|
49
|
+
Usage: pt <test-framework> [options] [files or directories]
|
50
|
+
|
51
|
+
Test framework:
|
52
|
+
* rspec
|
53
|
+
|
54
|
+
Options:
|
55
|
+
--priority Filter and run priority tests
|
56
|
+
-h, --help Show help
|
57
|
+
-v, --version Show version
|
58
|
+
|
59
|
+
Run tests in priority order:
|
60
|
+
|
61
|
+
$ pt rspec spec/a_spec
|
62
|
+
|
63
|
+
Filter and run priority tests:
|
64
|
+
|
65
|
+
$ pt rspec spec/a_spec --priority
|
66
|
+
|
67
|
+
Directly passing arguments to RSpec:
|
68
|
+
|
69
|
+
$ pt rspec spec/a_spec --priority -fp
|
70
|
+
|
71
|
+
Run tests in a Rake task:
|
72
|
+
|
73
|
+
$ rake spec PT_OPTS="--priority"
|
data/Rakefile
CHANGED
@@ -41,21 +41,18 @@ end
|
|
41
41
|
#
|
42
42
|
#############################################################################
|
43
43
|
|
44
|
-
task :default => :
|
44
|
+
task :default => :spec
|
45
45
|
|
46
|
-
require '
|
47
|
-
|
48
|
-
|
49
|
-
test.pattern = 'test/**/test_*.rb'
|
50
|
-
test.verbose = true
|
46
|
+
require 'rspec/core/rake_task'
|
47
|
+
RSpec::Core::RakeTask.new(:spec) do |t|
|
48
|
+
t.pattern = "./spec/**/*_spec.rb"
|
51
49
|
end
|
52
50
|
|
53
51
|
desc "Generate RCov test coverage and open in your browser"
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
sh "open coverage/index.html"
|
52
|
+
RSpec::Core::RakeTask.new(:rcov) do |t|
|
53
|
+
t.rcov = true
|
54
|
+
t.pattern = "./spec/**/*_spec.rb"
|
55
|
+
t.rcov_opts = '--exclude /gems/,/Library/,/usr/,lib/tasks,.bundle,config,/lib/rspec/,/lib/rspec-,spec'
|
59
56
|
end
|
60
57
|
|
61
58
|
require 'rdoc/task'
|
@@ -68,7 +65,7 @@ end
|
|
68
65
|
|
69
66
|
desc "Open an irb session preloaded with this library"
|
70
67
|
task :console do
|
71
|
-
sh "irb -rubygems -r ./lib/#{name}.rb"
|
68
|
+
sh "irb -rubygems -r ./lib/#{name}.rb -I ./lib"
|
72
69
|
end
|
73
70
|
|
74
71
|
#############################################################################
|
data/bin/pt
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'priority_test/autorun'
|
5
|
+
rescue LoadError
|
6
|
+
$stderr.puts <<-EOS
|
7
|
+
#{'*'*50}
|
8
|
+
Could not find 'priority/autorun'
|
9
|
+
|
10
|
+
This may happen if you're using rubygems as your package manager, but it is not
|
11
|
+
being required through some mechanism before executing the priority_test command.
|
12
|
+
|
13
|
+
You may need to do one of the following in your shell:
|
14
|
+
|
15
|
+
# for bash/zsh
|
16
|
+
export RUBYOPT=rubygems
|
17
|
+
|
18
|
+
# for csh, etc.
|
19
|
+
set RUBYOPT=rubygems
|
20
|
+
|
21
|
+
For background, please see http://gist.github.com/54177.
|
22
|
+
#{'*'*50}
|
23
|
+
EOS
|
24
|
+
exit(1)
|
25
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module PriorityTest
|
2
|
+
module Core
|
3
|
+
class AllTests
|
4
|
+
def initialize(tests=[])
|
5
|
+
@test_hash = Hash[tests.collect { |t| [t.identifier, t] }]
|
6
|
+
end
|
7
|
+
|
8
|
+
def add_test(test_params)
|
9
|
+
test = Test.create(test_params)
|
10
|
+
@test_hash[test.identifier] = test
|
11
|
+
end
|
12
|
+
|
13
|
+
def get_test(identifier)
|
14
|
+
@test_hash[identifier]
|
15
|
+
end
|
16
|
+
|
17
|
+
def add_test_result(identifier, test_result_params)
|
18
|
+
test = get_test(identifier)
|
19
|
+
if test
|
20
|
+
test.add_result(test_result_params)
|
21
|
+
test.update_statistics
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def size
|
26
|
+
@test_hash.size
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module PriorityTest
|
2
|
+
module Core
|
3
|
+
class Configuration
|
4
|
+
def self.add_setting(name, opts={})
|
5
|
+
define_method("#{name}=") { |val| settings[name] = val}
|
6
|
+
define_method(name) { settings.has_key?(name) ? settings[name] : opts[:default] }
|
7
|
+
define_method("#{name}?") { send name }
|
8
|
+
end
|
9
|
+
|
10
|
+
add_setting :test_framework
|
11
|
+
add_setting :database, :default => "sqlite://#{File.join(File.expand_path('.'), '.priority-test.db')}"
|
12
|
+
add_setting :priority, :default => false
|
13
|
+
|
14
|
+
def settings
|
15
|
+
@settings ||= {}
|
16
|
+
end
|
17
|
+
|
18
|
+
def add_setting(name, opts={})
|
19
|
+
self.class.add_setting(name, opts)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module PriorityTest
|
2
|
+
module Core
|
3
|
+
class ConfigurationOptions
|
4
|
+
def initialize(args=[])
|
5
|
+
@args = args
|
6
|
+
end
|
7
|
+
|
8
|
+
def configure(config)
|
9
|
+
options.each do |key, value|
|
10
|
+
config.send("#{key}=", options[key]) if config.respond_to?("#{key}=")
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def options
|
15
|
+
@options ||= OptionParser.parse!(@args)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
|
3
|
+
module PriorityTest
|
4
|
+
module Core
|
5
|
+
class OptionParser
|
6
|
+
AVAILABLE_TEST_FRAMEWORKS = ['rspec']
|
7
|
+
OPTIONS = ['--priority']
|
8
|
+
|
9
|
+
def self.parse!(args)
|
10
|
+
new.parse!(args)
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.parse_options(args)
|
14
|
+
new.parse_options(args)
|
15
|
+
end
|
16
|
+
|
17
|
+
def parse_options(args)
|
18
|
+
return {} if args.empty?
|
19
|
+
|
20
|
+
options = {}
|
21
|
+
opts_parser = parser(options)
|
22
|
+
begin
|
23
|
+
opts_parser.parse!(args.clone)
|
24
|
+
rescue ::OptionParser::InvalidOption
|
25
|
+
end
|
26
|
+
|
27
|
+
remove_parsed_options(args)
|
28
|
+
|
29
|
+
options
|
30
|
+
end
|
31
|
+
|
32
|
+
def parse!(args)
|
33
|
+
return {} if args.empty?
|
34
|
+
|
35
|
+
options = parse_options(args)
|
36
|
+
options[:test_framework] = parse_test_framework(args)
|
37
|
+
|
38
|
+
options
|
39
|
+
end
|
40
|
+
|
41
|
+
def parser(options)
|
42
|
+
::OptionParser.new do |parser|
|
43
|
+
parser.banner = <<-EOS
|
44
|
+
Usage: pt <test-framework> [options] [files or directories]
|
45
|
+
|
46
|
+
Test framework:
|
47
|
+
* rspec
|
48
|
+
|
49
|
+
Options:
|
50
|
+
EOS
|
51
|
+
|
52
|
+
parser.on('--priority', 'Filter and run priority tests') do |o|
|
53
|
+
options[:priority] = true
|
54
|
+
end
|
55
|
+
|
56
|
+
parser.on_tail("-h", "--help", "Show help") do
|
57
|
+
puts parser
|
58
|
+
exit
|
59
|
+
end
|
60
|
+
|
61
|
+
parser.on_tail('-v', '--version', 'Show version') do
|
62
|
+
puts PriorityTest::VERSION
|
63
|
+
exit
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def parse_test_framework(args)
|
69
|
+
test_framework = args.shift
|
70
|
+
if AVAILABLE_TEST_FRAMEWORKS.include?(test_framework)
|
71
|
+
test_framework
|
72
|
+
else
|
73
|
+
puts "Invalid test framework: #{test_framework}"
|
74
|
+
puts "Run `pt -h` for more info"
|
75
|
+
raise ::OptionParser::InvalidArgument
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def remove_parsed_options(args)
|
80
|
+
args.reject! { |arg| OPTIONS.include?(arg) }
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module PriorityTest
|
2
|
+
module Core
|
3
|
+
module Priority
|
4
|
+
PRIORITY_SET_RANKINGS = {
|
5
|
+
"FFFFF" => 1,
|
6
|
+
"FFFFP" => 2,
|
7
|
+
"FFFPF" => 3,
|
8
|
+
"FFFPP" => 4,
|
9
|
+
|
10
|
+
"FFPFF" => 5,
|
11
|
+
"FFPFP" => 6,
|
12
|
+
"FFPPF" => 7,
|
13
|
+
"FFPPP" => 8,
|
14
|
+
|
15
|
+
"FPFFF" => 9,
|
16
|
+
"FPFFP" => 10,
|
17
|
+
"FPFPF" => 11,
|
18
|
+
"FPFPP" => 12,
|
19
|
+
|
20
|
+
"FPPFF" => 13,
|
21
|
+
"FPPFP" => 14,
|
22
|
+
"FPPPF" => 15,
|
23
|
+
"FPPPP" => 16,
|
24
|
+
|
25
|
+
"PFFFF" => 17,
|
26
|
+
"PFFFP" => 18,
|
27
|
+
"PFFPF" => 19,
|
28
|
+
"PFFPP" => 20,
|
29
|
+
|
30
|
+
"PFPFF" => 21,
|
31
|
+
"PFPFP" => 22,
|
32
|
+
"PFPPF" => 23,
|
33
|
+
"PFPPP" => 24,
|
34
|
+
}
|
35
|
+
|
36
|
+
NON_PRIORITY_SET_RANKINGS = {
|
37
|
+
"PPFFF" => 25,
|
38
|
+
"PPFFP" => 26,
|
39
|
+
"PPFPF" => 27,
|
40
|
+
"PPFPP" => 28,
|
41
|
+
|
42
|
+
"PPPFF" => 29,
|
43
|
+
"PPPFP" => 30,
|
44
|
+
"PPPPF" => 31,
|
45
|
+
"PPPPP" => 32
|
46
|
+
}
|
47
|
+
|
48
|
+
# ranking for the last 5 test results
|
49
|
+
PRIORITY_RANKINGS = PRIORITY_SET_RANKINGS.merge(NON_PRIORITY_SET_RANKINGS)
|
50
|
+
|
51
|
+
PRIORITY_THRESHOLD = 24
|
52
|
+
|
53
|
+
def self.[](key)
|
54
|
+
PRIORITY_RANKINGS[key]
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.in_priority_set?(priority)
|
58
|
+
priority <= PRIORITY_THRESHOLD
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module PriorityTest
|
2
|
+
module Core
|
3
|
+
class PrioritySortElement
|
4
|
+
include Comparable
|
5
|
+
|
6
|
+
attr_reader :test
|
7
|
+
|
8
|
+
def initialize(identifier)
|
9
|
+
@test = PriorityTest.all_tests.get_test(identifier)
|
10
|
+
end
|
11
|
+
|
12
|
+
def <=>(other)
|
13
|
+
if test.nil? && !other.test.nil?
|
14
|
+
1
|
15
|
+
elsif !test.nil? && other.test.nil?
|
16
|
+
-1
|
17
|
+
else
|
18
|
+
test <=> other.test
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module PriorityTest
|
2
|
+
module Core
|
3
|
+
module Runner
|
4
|
+
def self.autorun
|
5
|
+
return if installed_at_exit?
|
6
|
+
run(ARGV)
|
7
|
+
@installed_at_exit = true
|
8
|
+
end
|
9
|
+
AT_EXIT_HOOK_BACKTRACE_LINE = "#{__FILE__}:#{__LINE__ - 2}:in `autorun'"
|
10
|
+
|
11
|
+
def self.installed_at_exit?
|
12
|
+
@installed_at_exit ||= false
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.run(args)
|
16
|
+
config_options = ConfigurationOptions.new(args)
|
17
|
+
config = PriorityTest.configuration
|
18
|
+
config_options.configure(config)
|
19
|
+
|
20
|
+
if config.test_framework == 'rspec'
|
21
|
+
require 'rspec/autorun'
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module PriorityTest
|
2
|
+
module Core
|
3
|
+
class Test < ::Sequel::Model
|
4
|
+
include PriorityTest::Core::ValidationsHelper
|
5
|
+
include Comparable
|
6
|
+
|
7
|
+
NUMBER_OF_RESULTS = 5
|
8
|
+
|
9
|
+
one_to_many :results, :class => PriorityTest::Core::TestResult.name, :class => PriorityTest::Core::TestResult.name do |ds|
|
10
|
+
ds.order(:started_at.desc).limit(NUMBER_OF_RESULTS) # make it configurable
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.all_in_priority_order
|
14
|
+
eager(:results).order(:priority, :avg_run_time).all
|
15
|
+
end
|
16
|
+
|
17
|
+
def <=>(other)
|
18
|
+
result = (priority <=> other.priority)
|
19
|
+
result = (avg_run_time <=> other.avg_run_time) if result == 0 || !result
|
20
|
+
result
|
21
|
+
end
|
22
|
+
|
23
|
+
def validate
|
24
|
+
validates_presence [ :identifier, :file_path ]
|
25
|
+
end
|
26
|
+
|
27
|
+
def results_key
|
28
|
+
results_key = results.collect { |r| r.passed? ? 'P' : 'F' }.join
|
29
|
+
results_key << 'P' * (NUMBER_OF_RESULTS - results_key.size) if results_key.size < NUMBER_OF_RESULTS
|
30
|
+
results_key[0..(NUMBER_OF_RESULTS-1)]
|
31
|
+
end
|
32
|
+
|
33
|
+
def update_statistics
|
34
|
+
stats_to_update = {}
|
35
|
+
prio = Priority[results_key]
|
36
|
+
stats_to_update.merge!(:priority => prio) if prio
|
37
|
+
stats_to_update.merge!(:avg_run_time => calculate_avg_run_time) if results.size > 0
|
38
|
+
|
39
|
+
self.update(stats_to_update)
|
40
|
+
end
|
41
|
+
|
42
|
+
def priority?
|
43
|
+
Priority.in_priority_set?(priority)
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def calculate_avg_run_time
|
49
|
+
results.collect(&:run_time).reduce(:+) / results.size
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module PriorityTest
|
2
|
+
module Core
|
3
|
+
class TestResult < ::Sequel::Model
|
4
|
+
include PriorityTest::Core::ValidationsHelper
|
5
|
+
|
6
|
+
PASSED_STATUS = 'passed'
|
7
|
+
FAILEDED_STATUS = 'failed'
|
8
|
+
|
9
|
+
many_to_one :context, :class => PriorityTest::Core::Test.name, :key => :test_id
|
10
|
+
|
11
|
+
def passed?
|
12
|
+
status == PASSED_STATUS
|
13
|
+
end
|
14
|
+
|
15
|
+
def failed?
|
16
|
+
not passed?
|
17
|
+
end
|
18
|
+
|
19
|
+
def validate
|
20
|
+
validates_presence [ :status, :started_at, :run_time ]
|
21
|
+
validates_includes [ PASSED_STATUS, FAILEDED_STATUS ], :status
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module PriorityTest
|
2
|
+
module Core
|
3
|
+
class TestResultCollector
|
4
|
+
attr_reader :all_tests
|
5
|
+
|
6
|
+
def initialize(all_tests)
|
7
|
+
@all_tests = all_tests
|
8
|
+
end
|
9
|
+
|
10
|
+
def add_result(test_result_hash)
|
11
|
+
identifier = test_result_hash[:identifier]
|
12
|
+
return unless identifier
|
13
|
+
|
14
|
+
@all_tests.get_test(identifier) || @all_tests.add_test(test_params(test_result_hash))
|
15
|
+
@all_tests.add_test_result(identifier, test_result_params(test_result_hash))
|
16
|
+
end
|
17
|
+
|
18
|
+
def finish
|
19
|
+
# nothing
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def test_params(test_result_hash)
|
25
|
+
{
|
26
|
+
:identifier => test_result_hash[:identifier],
|
27
|
+
:file_path => test_result_hash[:file_path]
|
28
|
+
}
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_result_params(test_result_hash)
|
32
|
+
{
|
33
|
+
:status => test_result_hash[:status],
|
34
|
+
:started_at => test_result_hash[:started_at],
|
35
|
+
:run_time => test_result_hash[:run_time]
|
36
|
+
}
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module PriorityTest
|
2
|
+
module Core
|
3
|
+
module ValidationsHelper
|
4
|
+
def validates_presence(args)
|
5
|
+
args.each do |arg|
|
6
|
+
value = send(arg)
|
7
|
+
errors.add(arg, 'cannot be empty') if !value || value.to_s.empty?
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def validates_includes(includes, arg)
|
12
|
+
value = send(arg)
|
13
|
+
errors.add(arg, "should be in list #{includes}") unless includes.include?(value)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require_path 'core/configuration'
|
2
|
+
require_path 'core/configuration_options'
|
3
|
+
require_path 'core/option_parser'
|
4
|
+
require_path 'core/runner'
|
5
|
+
require_path 'core/priority'
|
6
|
+
require_path 'core/validations_helper'
|
7
|
+
require_path 'core/all_tests'
|
8
|
+
require_path 'core/service'
|
9
|
+
require_path 'core/test_result_collector'
|
10
|
+
require_path 'core/priority_sort_element'
|
11
|
+
|
12
|
+
module PriorityTest
|
13
|
+
module Core
|
14
|
+
# Remove dependency of Sequel so that it can move out of autoload
|
15
|
+
autoload :Test, 'priority_test/core/test'
|
16
|
+
autoload :TestResult, 'priority_test/core/test_result'
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.configure
|
20
|
+
yield configuration if block_given?
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.configuration
|
24
|
+
@configuration ||= Core::Configuration.new
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.all_tests
|
28
|
+
@all_tests ||= begin
|
29
|
+
tests = Core::Test.all_in_priority_order
|
30
|
+
Core::AllTests.new(tests)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.service
|
35
|
+
@service ||= Core::Service.new(all_tests)
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.test_result_collector
|
39
|
+
@collector ||= Core::TestResultCollector.new(all_tests)
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
Sequel.migration do
|
2
|
+
change do
|
3
|
+
create_table(:tests) do
|
4
|
+
primary_key :id
|
5
|
+
String :identifier, :text => true, :null => false
|
6
|
+
String :file_path, :text => true, :null => false
|
7
|
+
Integer :priority, :default => 0, :null => false
|
8
|
+
Numeric :avg_run_time, :size => [10, 6], :default => 0, :null => false
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
Sequel.migration do
|
2
|
+
change do
|
3
|
+
create_table(:test_results) do
|
4
|
+
primary_key :id
|
5
|
+
String :status, :null => false
|
6
|
+
DateTime :started_at, :null => false
|
7
|
+
Numeric :run_time, :size => [10, 6], :null => false
|
8
|
+
foreign_key :test_id, :tests
|
9
|
+
index :test_id
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'sequel'
|
2
|
+
require 'sequel/extensions/migration'
|
3
|
+
|
4
|
+
module PriorityTest
|
5
|
+
module Gateway
|
6
|
+
class Sequel
|
7
|
+
class << self
|
8
|
+
attr_reader :database
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.setup
|
12
|
+
::Sequel.database_timezone = :utc
|
13
|
+
@database ||= ::Sequel.connect(PriorityTest.configuration.database)
|
14
|
+
run_migration(database)
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.teardown
|
18
|
+
@database.disconnect if @database
|
19
|
+
@database = nil
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.run_migration(database)
|
23
|
+
::Sequel::Migrator.apply(database, migrations_dir)
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def self.migrations_dir
|
29
|
+
File.join(File.expand_path(File.dirname(__FILE__)), 'migrations')
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|