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