micron 0.5.0
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.
- checksums.yaml +7 -0
- data/Gemfile +19 -0
- data/Gemfile.lock +88 -0
- data/Rakefile +40 -0
- data/VERSION +1 -0
- data/bin/micron +4 -0
- data/lib/micron.rb +29 -0
- data/lib/micron/app.rb +127 -0
- data/lib/micron/app/options.rb +73 -0
- data/lib/micron/assertion.rb +10 -0
- data/lib/micron/fork_runner.rb +55 -0
- data/lib/micron/minitest.rb +45 -0
- data/lib/micron/proc_runner.rb +114 -0
- data/lib/micron/rake.rb +29 -0
- data/lib/micron/reporter.rb +30 -0
- data/lib/micron/reporter/console.rb +146 -0
- data/lib/micron/reporter/coverage.rb +37 -0
- data/lib/micron/runner.rb +95 -0
- data/lib/micron/runner/backtrace_filter.rb +39 -0
- data/lib/micron/runner/clazz.rb +45 -0
- data/lib/micron/runner/clazz19.rb +24 -0
- data/lib/micron/runner/debug.rb +22 -0
- data/lib/micron/runner/exception_info.rb +16 -0
- data/lib/micron/runner/fork_worker.rb +185 -0
- data/lib/micron/runner/forking_clazz.rb +40 -0
- data/lib/micron/runner/liveness_checker.rb +40 -0
- data/lib/micron/runner/liveness_checker/ping.rb +65 -0
- data/lib/micron/runner/liveness_checker/pong.rb +36 -0
- data/lib/micron/runner/method.rb +124 -0
- data/lib/micron/runner/parallel_clazz.rb +135 -0
- data/lib/micron/runner/proc_clazz.rb +48 -0
- data/lib/micron/runner/process_reaper.rb +98 -0
- data/lib/micron/runner/shim.rb +68 -0
- data/lib/micron/runner/test_file.rb +79 -0
- data/lib/micron/test_case.rb +36 -0
- data/lib/micron/test_case/assertions.rb +701 -0
- data/lib/micron/test_case/lifecycle_hooks.rb +74 -0
- data/lib/micron/test_case/redir_logging.rb +85 -0
- data/lib/micron/test_case/teardown_coverage.rb +13 -0
- data/lib/micron/util/ex.rb +23 -0
- data/lib/micron/util/io.rb +54 -0
- data/lib/micron/util/thread_dump.rb +29 -0
- data/micron.gemspec +97 -0
- metadata +184 -0
@@ -0,0 +1,45 @@
|
|
1
|
+
|
2
|
+
# Compatibility layer for MiniTest
|
3
|
+
|
4
|
+
require "micron"
|
5
|
+
|
6
|
+
old_verbose = $VERBOSE
|
7
|
+
$VERBOSE = nil
|
8
|
+
|
9
|
+
module MiniTest
|
10
|
+
Assertion = Micron::Assertion
|
11
|
+
|
12
|
+
class Unit
|
13
|
+
|
14
|
+
VERSION = "4.7"
|
15
|
+
TestCase = Micron::TestCase
|
16
|
+
|
17
|
+
def self.autorun
|
18
|
+
# noop
|
19
|
+
end
|
20
|
+
|
21
|
+
class TestCase
|
22
|
+
|
23
|
+
def name
|
24
|
+
self.class.name
|
25
|
+
end
|
26
|
+
alias_method :__name__, :name
|
27
|
+
|
28
|
+
def micron_method=(method)
|
29
|
+
@micron_method = method
|
30
|
+
end
|
31
|
+
|
32
|
+
def passed?
|
33
|
+
@micron_method.passed?
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.parallelize_me!
|
37
|
+
# noop
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
$VERBOSE = old_verbose
|
@@ -0,0 +1,114 @@
|
|
1
|
+
|
2
|
+
require "micron/runner/parallel_clazz"
|
3
|
+
require "micron/runner/fork_worker"
|
4
|
+
require "micron/runner/proc_clazz"
|
5
|
+
require "micron/test_case/teardown_coverage"
|
6
|
+
|
7
|
+
module Micron
|
8
|
+
class ProcRunner < Runner
|
9
|
+
|
10
|
+
def initialize(files=nil, reporters=nil)
|
11
|
+
super(files, reporters)
|
12
|
+
end
|
13
|
+
|
14
|
+
def run
|
15
|
+
$0 = "micron: proc_runner"
|
16
|
+
# ERR.puts "#{$0} (#{$$})"
|
17
|
+
|
18
|
+
state_path = ENV["MICRON_PATH"]
|
19
|
+
FileUtils.mkdir_p(state_path)
|
20
|
+
|
21
|
+
report(:start_tests, @files)
|
22
|
+
@files.each do |file|
|
23
|
+
|
24
|
+
ENV["MICRON_TEST_FILE"] = file
|
25
|
+
pid = fork do
|
26
|
+
exec("bundle exec micron --runclass")
|
27
|
+
end
|
28
|
+
Process.wait
|
29
|
+
|
30
|
+
# puts "got stdout: #{cmd.stdout}"
|
31
|
+
# puts "got stderr: #{cmd.stderr}"
|
32
|
+
# puts "status: #{cmd.status}"
|
33
|
+
# puts "exitstatus: #{cmd.exitstatus.inspect}"
|
34
|
+
|
35
|
+
data_file = File.join(state_path, "#{pid}.data")
|
36
|
+
File.open(data_file) do |f|
|
37
|
+
while !f.eof
|
38
|
+
clazz = Marshal.load(f) # read Clazz from child via file
|
39
|
+
if clazz.kind_of? Exception then
|
40
|
+
puts "Error loading test file: #{file}"
|
41
|
+
puts clazz
|
42
|
+
puts clazz.backtrace
|
43
|
+
exit 1
|
44
|
+
end
|
45
|
+
|
46
|
+
# should be a Clazz
|
47
|
+
@results << clazz
|
48
|
+
end
|
49
|
+
end
|
50
|
+
File.delete(data_file)
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
report(:end_tests, @files, @results)
|
55
|
+
|
56
|
+
return @results
|
57
|
+
end # run
|
58
|
+
|
59
|
+
|
60
|
+
# Child process which runs an entire test file/class
|
61
|
+
def run_class
|
62
|
+
$0 = "micron:proc_run_class"
|
63
|
+
# ERR.puts "micron: proc_run_class (#{$$})"
|
64
|
+
|
65
|
+
test_file = TestFile.new(test_filename)
|
66
|
+
report(:start_file, test_file)
|
67
|
+
|
68
|
+
begin
|
69
|
+
test_file.load(false)
|
70
|
+
results = test_file.run(ProcClazz)
|
71
|
+
rescue Exception => ex
|
72
|
+
results = [ex]
|
73
|
+
end
|
74
|
+
|
75
|
+
# pass data back to parent process
|
76
|
+
data_file = File.join(ENV["MICRON_PATH"], "#{$$}.data")
|
77
|
+
File.open(data_file, "w") do |f|
|
78
|
+
results.each { |r| Marshal.dump(r, f) }
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# Child process which runs a single method in a given file & class
|
83
|
+
def run_method
|
84
|
+
$0 = "micron:proc_run_method"
|
85
|
+
# ERR.puts "#{$0} (#{$$})"
|
86
|
+
|
87
|
+
test_clazz = ENV["MICRON_TEST_CLASS"]
|
88
|
+
test_method = ENV["MICRON_TEST_METHOD"]
|
89
|
+
|
90
|
+
# ERR.puts "test_clazz: #{test_clazz}"
|
91
|
+
# ERR.puts "test_method: #{test_method}"
|
92
|
+
|
93
|
+
test_file = TestFile.new(test_filename)
|
94
|
+
test_file.load(true)
|
95
|
+
# load and run a specific method only
|
96
|
+
result = test_file.run_method(test_clazz, test_method, Clazz)
|
97
|
+
|
98
|
+
# pass data back to parent process
|
99
|
+
data_file = File.join(ENV["MICRON_PATH"], "#{$$}.data")
|
100
|
+
File.open(data_file, "w") do |f|
|
101
|
+
Marshal.dump(result, f)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
|
106
|
+
private
|
107
|
+
|
108
|
+
def test_filename
|
109
|
+
ENV["MICRON_TEST_FILE"]
|
110
|
+
end
|
111
|
+
|
112
|
+
|
113
|
+
end
|
114
|
+
end
|
data/lib/micron/rake.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
|
2
|
+
module Micron
|
3
|
+
class Rake < ::Rake::TaskLib
|
4
|
+
|
5
|
+
|
6
|
+
def initialize(&block)
|
7
|
+
@config = {
|
8
|
+
:path => File.dirname(ENV["BUNDLE_GEMFILE"])
|
9
|
+
}
|
10
|
+
block.call(@config) if block
|
11
|
+
|
12
|
+
define()
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def define
|
18
|
+
|
19
|
+
desc "Run micron tests"
|
20
|
+
task :test do
|
21
|
+
require "micron/app"
|
22
|
+
ARGV.clear
|
23
|
+
Micron::App.new.run(@config)
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
|
2
|
+
module Micron
|
3
|
+
class Reporter
|
4
|
+
|
5
|
+
def start_tests(files)
|
6
|
+
end
|
7
|
+
|
8
|
+
def start_file(test_file)
|
9
|
+
end
|
10
|
+
|
11
|
+
def start_class(clazz)
|
12
|
+
end
|
13
|
+
|
14
|
+
def end_method(method)
|
15
|
+
end
|
16
|
+
|
17
|
+
def end_class(clazz)
|
18
|
+
end
|
19
|
+
|
20
|
+
def end_file(test_file)
|
21
|
+
end
|
22
|
+
|
23
|
+
def end_tests(files, results)
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
require "micron/reporter/console"
|
30
|
+
require "micron/reporter/coverage"
|
@@ -0,0 +1,146 @@
|
|
1
|
+
|
2
|
+
require "colorize"
|
3
|
+
|
4
|
+
module Micron
|
5
|
+
class Reporter
|
6
|
+
class Console < Reporter
|
7
|
+
|
8
|
+
CONSOLE_WIDTH = 100
|
9
|
+
|
10
|
+
def start_tests(files)
|
11
|
+
@runtime = Hitimes::Interval.now
|
12
|
+
puts "="*CONSOLE_WIDTH
|
13
|
+
puts ralign("START TESTS (#{files.size} files)", "#{Time.new}")
|
14
|
+
puts "="*CONSOLE_WIDTH
|
15
|
+
puts
|
16
|
+
end
|
17
|
+
|
18
|
+
def start_class(clazz)
|
19
|
+
puts clazz.name
|
20
|
+
end
|
21
|
+
|
22
|
+
def end_method(m)
|
23
|
+
name = m.name.to_s
|
24
|
+
duration = sprintf("%0.3f", m.total_duration)
|
25
|
+
|
26
|
+
status = m.status.upcase
|
27
|
+
|
28
|
+
str = ralign(indent(name), "#{duration} #{status}")
|
29
|
+
|
30
|
+
# inject color after so we don't screw up the alignment
|
31
|
+
if m.skipped? then
|
32
|
+
str.gsub!(/#{status}$/, status.colorize(:light_yellow))
|
33
|
+
elsif m.passed? then
|
34
|
+
str.gsub!(/#{status}$/, status.colorize(:light_green))
|
35
|
+
else
|
36
|
+
str.gsub!(/#{status}$/, status.colorize(:light_red))
|
37
|
+
end
|
38
|
+
puts str
|
39
|
+
|
40
|
+
if m.failed? and !m.skipped? then
|
41
|
+
|
42
|
+
puts
|
43
|
+
puts indent(underline("Exception:"))
|
44
|
+
if m.ex.kind_of? Array then
|
45
|
+
m.ex.each{ |ex|
|
46
|
+
next if m.ex.nil?
|
47
|
+
puts indent(Micron.dump_ex(ex, true))
|
48
|
+
puts
|
49
|
+
}
|
50
|
+
elsif m.ex.nil? then
|
51
|
+
puts indent("nil")
|
52
|
+
puts
|
53
|
+
else
|
54
|
+
puts indent(Micron.dump_ex(m.ex, true))
|
55
|
+
puts
|
56
|
+
end
|
57
|
+
|
58
|
+
if not m.stdout.empty? then
|
59
|
+
puts indent(underline("STDOUT:"))
|
60
|
+
puts indent(m.stdout.rstrip)
|
61
|
+
puts
|
62
|
+
end
|
63
|
+
|
64
|
+
if not m.stderr.empty? then
|
65
|
+
puts indent(underline("STDERR:"))
|
66
|
+
puts indent(m.stderr.rstrip)
|
67
|
+
puts
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def end_class(clazz)
|
74
|
+
puts
|
75
|
+
end
|
76
|
+
|
77
|
+
def end_tests(files, results)
|
78
|
+
|
79
|
+
@runtime.stop
|
80
|
+
|
81
|
+
total = pass = fail = skip = 0
|
82
|
+
total_duration = 0.0
|
83
|
+
total_assertions = 0
|
84
|
+
|
85
|
+
results.each { |c|
|
86
|
+
c.methods.each { |m|
|
87
|
+
total += 1
|
88
|
+
total_duration += m.total_duration
|
89
|
+
total_assertions += m.assertions
|
90
|
+
if m.skipped? then
|
91
|
+
skip += 1
|
92
|
+
elsif m.passed? then
|
93
|
+
pass += 1
|
94
|
+
else
|
95
|
+
fail += 1
|
96
|
+
end
|
97
|
+
}
|
98
|
+
}
|
99
|
+
|
100
|
+
total_duration = sprintf("%0.3f", total_duration)
|
101
|
+
real_runtime = sprintf("%0.3f", @runtime.duration)
|
102
|
+
|
103
|
+
puts
|
104
|
+
puts ("="*CONSOLE_WIDTH).colorize((fail > 0 ? :light_red : :light_green))
|
105
|
+
puts " PASS: #{pass}, FAIL: #{fail}, SKIP: #{skip}"
|
106
|
+
puts " TOTAL: #{total} with #{total_assertions} assertions in #{total_duration} seconds (wall time: #{real_runtime})"
|
107
|
+
|
108
|
+
if fail > 0 then
|
109
|
+
puts
|
110
|
+
puts " Failed tests:"
|
111
|
+
results.each { |c|
|
112
|
+
c.methods.each { |m|
|
113
|
+
if !m.skipped? and m.failed? then
|
114
|
+
puts " #{c.name}##{m.name}"
|
115
|
+
end
|
116
|
+
}
|
117
|
+
}
|
118
|
+
end
|
119
|
+
puts ("="*CONSOLE_WIDTH).colorize((fail > 0 ? :light_red : :light_green))
|
120
|
+
end
|
121
|
+
|
122
|
+
|
123
|
+
private
|
124
|
+
|
125
|
+
# Right align 'b' with padding after a
|
126
|
+
#
|
127
|
+
# e.g.:
|
128
|
+
# a <padding> b
|
129
|
+
def ralign(a, b, length=CONSOLE_WIDTH)
|
130
|
+
p = length-a.length-b.length
|
131
|
+
a + (" "*p) + b
|
132
|
+
end
|
133
|
+
|
134
|
+
# Indent the string by the given amount
|
135
|
+
def indent(str, amount=2)
|
136
|
+
i = (" "*amount)
|
137
|
+
(i + str.gsub(/\n/, "\n#{i}")).rstrip
|
138
|
+
end
|
139
|
+
|
140
|
+
def underline(str)
|
141
|
+
str += "\n" + "-"*str.length
|
142
|
+
end
|
143
|
+
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
|
2
|
+
module Micron
|
3
|
+
class Reporter
|
4
|
+
|
5
|
+
# Reporter which generates coverage report using SimpleCov
|
6
|
+
class Coverage < Reporter
|
7
|
+
|
8
|
+
def end_tests(files, results)
|
9
|
+
generate_coverage_report()
|
10
|
+
end
|
11
|
+
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def generate_coverage_report
|
16
|
+
path = File.dirname(ENV["MICRON_PATH"])
|
17
|
+
|
18
|
+
# Locate easycov path used in tests
|
19
|
+
%w{coverage .coverage}.each do |t|
|
20
|
+
t = File.join(path, t)
|
21
|
+
if File.directory?(t) && !Dir.glob(File.join(t, ".tmp.*.resultset.json")).empty? then
|
22
|
+
EasyCov.path = t
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Merge coverage
|
27
|
+
EasyCov.merge!
|
28
|
+
|
29
|
+
# Write coverage
|
30
|
+
Micron.capture_io {
|
31
|
+
SimpleCov::ResultMerger.merged_result.format!
|
32
|
+
}
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
|
2
|
+
require "micron/runner/shim"
|
3
|
+
|
4
|
+
require "micron/runner/backtrace_filter"
|
5
|
+
require "micron/runner/debug"
|
6
|
+
|
7
|
+
require "micron/runner/test_file"
|
8
|
+
require "micron/runner/clazz"
|
9
|
+
require "micron/runner/method"
|
10
|
+
require "micron/runner/exception_info"
|
11
|
+
|
12
|
+
require "micron/reporter"
|
13
|
+
|
14
|
+
module Micron
|
15
|
+
|
16
|
+
# Default Runner - forks for each file
|
17
|
+
class Runner
|
18
|
+
|
19
|
+
OUT = $stdout
|
20
|
+
ERR = $stderr
|
21
|
+
|
22
|
+
# these exceptions, if caught while running, will be re-raised
|
23
|
+
PASSTHROUGH_EXCEPTIONS = [
|
24
|
+
NoMemoryError, SignalException, Interrupt, SystemExit
|
25
|
+
]
|
26
|
+
|
27
|
+
attr_reader :results
|
28
|
+
|
29
|
+
def initialize(files, reporters)
|
30
|
+
@files = files
|
31
|
+
@results = []
|
32
|
+
@reporters = reporters || []
|
33
|
+
|
34
|
+
@mutex = Mutex.new
|
35
|
+
|
36
|
+
if self.class.to_s != "Micron::Runner" then
|
37
|
+
# Only needed in fork/proc runners
|
38
|
+
TestCase.class_eval do
|
39
|
+
include TestCase::TeardownCoverage
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def run
|
45
|
+
report(:start_tests, @files)
|
46
|
+
|
47
|
+
@files.each do |file|
|
48
|
+
|
49
|
+
test_file = TestFile.new(file)
|
50
|
+
report(:start_file, test_file)
|
51
|
+
|
52
|
+
begin
|
53
|
+
test_file.load(true)
|
54
|
+
results = test_file.run(Clazz)
|
55
|
+
rescue Exception => ex
|
56
|
+
results = [ex]
|
57
|
+
end
|
58
|
+
|
59
|
+
results.each do |clazz|
|
60
|
+
if clazz.kind_of? Exception then
|
61
|
+
puts "Error loading test file: #{file}"
|
62
|
+
puts clazz
|
63
|
+
puts clazz.backtrace
|
64
|
+
exit 1
|
65
|
+
end
|
66
|
+
|
67
|
+
# should be a Clazz
|
68
|
+
@results << clazz
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
EasyCov.dump
|
74
|
+
report(:end_tests, @files, @results)
|
75
|
+
|
76
|
+
return @results
|
77
|
+
end
|
78
|
+
|
79
|
+
# Fire the given report event on all reporters
|
80
|
+
#
|
81
|
+
# @param [Symbol] method
|
82
|
+
# @param [*args]
|
83
|
+
def report(method, *args)
|
84
|
+
synchronize {
|
85
|
+
@reporters.each { |r| r.send(method, *args) }
|
86
|
+
}
|
87
|
+
end
|
88
|
+
|
89
|
+
# Output synchronization helper. Will NOT work across forks!
|
90
|
+
def synchronize(&block)
|
91
|
+
@mutex.synchronize(&block)
|
92
|
+
end
|
93
|
+
|
94
|
+
end # Runner
|
95
|
+
end # Micron
|