priority_test 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|