micron 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +19 -0
  3. data/Gemfile.lock +88 -0
  4. data/Rakefile +40 -0
  5. data/VERSION +1 -0
  6. data/bin/micron +4 -0
  7. data/lib/micron.rb +29 -0
  8. data/lib/micron/app.rb +127 -0
  9. data/lib/micron/app/options.rb +73 -0
  10. data/lib/micron/assertion.rb +10 -0
  11. data/lib/micron/fork_runner.rb +55 -0
  12. data/lib/micron/minitest.rb +45 -0
  13. data/lib/micron/proc_runner.rb +114 -0
  14. data/lib/micron/rake.rb +29 -0
  15. data/lib/micron/reporter.rb +30 -0
  16. data/lib/micron/reporter/console.rb +146 -0
  17. data/lib/micron/reporter/coverage.rb +37 -0
  18. data/lib/micron/runner.rb +95 -0
  19. data/lib/micron/runner/backtrace_filter.rb +39 -0
  20. data/lib/micron/runner/clazz.rb +45 -0
  21. data/lib/micron/runner/clazz19.rb +24 -0
  22. data/lib/micron/runner/debug.rb +22 -0
  23. data/lib/micron/runner/exception_info.rb +16 -0
  24. data/lib/micron/runner/fork_worker.rb +185 -0
  25. data/lib/micron/runner/forking_clazz.rb +40 -0
  26. data/lib/micron/runner/liveness_checker.rb +40 -0
  27. data/lib/micron/runner/liveness_checker/ping.rb +65 -0
  28. data/lib/micron/runner/liveness_checker/pong.rb +36 -0
  29. data/lib/micron/runner/method.rb +124 -0
  30. data/lib/micron/runner/parallel_clazz.rb +135 -0
  31. data/lib/micron/runner/proc_clazz.rb +48 -0
  32. data/lib/micron/runner/process_reaper.rb +98 -0
  33. data/lib/micron/runner/shim.rb +68 -0
  34. data/lib/micron/runner/test_file.rb +79 -0
  35. data/lib/micron/test_case.rb +36 -0
  36. data/lib/micron/test_case/assertions.rb +701 -0
  37. data/lib/micron/test_case/lifecycle_hooks.rb +74 -0
  38. data/lib/micron/test_case/redir_logging.rb +85 -0
  39. data/lib/micron/test_case/teardown_coverage.rb +13 -0
  40. data/lib/micron/util/ex.rb +23 -0
  41. data/lib/micron/util/io.rb +54 -0
  42. data/lib/micron/util/thread_dump.rb +29 -0
  43. data/micron.gemspec +97 -0
  44. 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
@@ -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