micro_test 0.2.8 → 0.3.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.
@@ -1,69 +1,65 @@
1
1
  module MicroTest
2
- module Runner
2
+ class Runner
3
3
  class << self
4
- attr_reader :options, :current_test
4
+ attr_accessor :exit
5
+ end
5
6
 
6
- def update(event, arg)
7
- send(event, arg) if respond_to?(event)
8
- end
7
+ attr_reader :formatter, :options, :active_test, :duration, :passed, :failed
9
8
 
10
- def add_test_class(klass)
11
- test_classes << klass
12
- end
9
+ def initialize(formatter, options={})
10
+ @formatter = formatter
11
+ @options = options
12
+ reset
13
+ end
13
14
 
14
- def test_classes
15
- @test_classes ||= []
16
- end
15
+ def run
16
+ test_classes = MicroTest::Test.subclasses.shuffle
17
+ tests = test_classes.map{ |klass| klass.tests }.flatten
18
+ formatter.before_suite(test_classes)
19
+ start = Time.now
17
20
 
18
- def file(path)
19
- @files ||= {}
20
- @files[path] ||= File.read(path).split("\n")
21
+ test_classes.each do |test_class|
22
+ formatter.before_class(test_class)
23
+ test_class.tests.shuffle.each do |test|
24
+ stop && exit if MicroTest::Runner.exit
25
+ @active_test = test
26
+ if options[:async]
27
+ test.async.invoke(@formatter, @options)
28
+ else
29
+ test.invoke(@formatter, @options)
30
+ end
31
+ end
32
+ formatter.after_class(test_class)
21
33
  end
22
34
 
23
- def file_info(callstack_entry)
24
- path = callstack_entry[0, callstack_entry.index(/:[0-9]+:/)]
25
- file = file(path)
26
- line_num = callstack_entry.scan(/:[0-9]+:/).first.gsub(/:/, "").to_i
27
- line = file[line_num - 1].strip
28
- {
29
- :path => path,
30
- :file => file,
31
- :line => line,
32
- :line_num => line_num
33
- }
34
- end
35
+ sleep 0.1 while !finished?(tests)
36
+ @duration = Time.now - start
37
+ @passed = tests.select{ |test| test.passed? }.count
38
+ @failed = tests.select{ |test| !test.passed? }.count
39
+ formatter.after_results(self)
40
+ formatter.after_suite(test_classes)
41
+ end
35
42
 
36
- def assert(value)
37
- @failed ||= !value
38
- @asserts << file_info(caller[6]).merge(:passed => value)
43
+ def stop
44
+ MicroTest::Test.subclasses.each do |subclass|
45
+ subclass.tests.each { |test| test.terminate }
39
46
  end
47
+ puts
48
+ sleep 0.5
49
+ true
50
+ end
40
51
 
41
- def run(formatter, opts={})
42
- @options = opts
43
- @current_test = nil
44
- formatter.header
45
-
46
- test_classes.shuffle.each do |test_class|
47
- formatter.group test_class
48
- test_class.invoke :before, :all
49
-
50
- test_class.tests.keys.shuffle.each do |desc|
51
- @current_test = "#{test_class.name} -> test #{desc}"
52
- @failed = false
53
- @asserts = []
54
- test_class.invoke :before, :each
55
- start = Time.now
56
- test_class.tests[desc].call
57
- formatter.test :name => desc, :passed => !@failed, :asserts => @asserts, :duration => Time.now - start
58
- test_class.invoke :after, :each
59
- end
60
-
61
- test_class.invoke :after, :all
62
- end
52
+ def reset
53
+ @duration = 0
54
+ @passed = 0
55
+ @failed = 0
56
+ end
63
57
 
64
- formatter.footer
65
- end
58
+ private
66
59
 
60
+ def finished?(tests)
61
+ tests.empty? || tests.map{ |test| test.finished? }.uniq == [true]
67
62
  end
63
+
68
64
  end
69
65
  end
@@ -1,45 +1,83 @@
1
- require "observer"
2
-
3
1
  module MicroTest
2
+
3
+ # Superclass for all test classes.
4
+ # @example Create a subclass with a test.
5
+ # class SimpleTest < MicroTest::Test
6
+ # test "common sense" do
7
+ # assert 1 > 0
8
+ # end
9
+ # end
4
10
  class Test
5
11
  class << self
6
- include Observable
7
12
 
8
- def inherited(subclass)
9
- notify(:add_test_class, subclass)
13
+ # All subclasses of this class.
14
+ # @return [Array<MicroTest::Test>]
15
+ def subclasses
16
+ @subclasses ||= []
10
17
  end
11
18
 
12
- def notify(event, arg)
13
- MicroTest::Test.send :changed
14
- MicroTest::Test.send :notify_observers, event, arg
19
+ # All individual tests defined in this class.
20
+ # @return [Array<MicroTest::TestWrapper>]
21
+ def tests
22
+ @tests ||= []
15
23
  end
16
24
 
17
- def callbacks
18
- @callbacks ||= { :before => {}, :after => {} }
25
+ # All files that define subclasses of this class.
26
+ # @note Primarily used in the context of MicroTest::Test.
27
+ # @example Files are stored in a Hash with the following format.
28
+ # {
29
+ # "path/to/file1.rb" => ["line 1", "line 2", "line 3", ...],
30
+ # "path/to/file2.rb" => ["line 1", "line 2", "line 3", ...]
31
+ # }
32
+ # @return [Hash]
33
+ def files
34
+ @files ||= {}
19
35
  end
20
36
 
21
- def invoke(which, what)
22
- callbacks[which][what].call if callbacks[which][what]
37
+ # Resets the state in preparation for a new test run.
38
+ def reset
39
+ tests.each { |test| test.reset }
40
+ subclasses.each { |subclass| subclass.reset }
23
41
  end
24
42
 
25
- def before(what, &block)
26
- callbacks[:before][what] = block
43
+ # A callback provided by Ruby that is invoked whenever a subclass is created.
44
+ def inherited(subclass)
45
+ file_path = caller[0][0, caller[0].index(/:[0-9]+:/)]
46
+ files[file_path] = File.open(file_path).readlines
47
+ subclasses << subclass
27
48
  end
28
49
 
29
- def after(what, &block)
30
- callbacks[:after][what] = block
50
+ # Defines a setup method that will run before each individual test.
51
+ # @param [Symbol] what Deprecated but maintained for backwards compatibility.
52
+ # @yield A block of code that will serve as the setup method.
53
+ def before(what=nil, &block)
54
+ @before = block
31
55
  end
32
56
 
33
- def tests
34
- @tests ||= {}
57
+ # Defines a teardown method that will run after each individual test.
58
+ # @param [Symbol] what Deprecated but maintained for backwards compatibility.
59
+ # @yield A block of code that will serve as the teardown method.
60
+ def after(what=nil, &block)
61
+ @after = block
35
62
  end
36
63
 
64
+ # Defines a test.
65
+ # Allows subclasses to define tests in their class definition.
66
+ #
67
+ # @param [String] desc A description for the test.
68
+ # @yield A block that defines the test code.
69
+ #
70
+ # @example
71
+ # class SimpleTest < MicroTest::Test
72
+ # test "common sense" do
73
+ # assert 1 > 0
74
+ # end
75
+ # end
37
76
  def test(desc, &block)
38
- tests[desc] = block
39
- end
40
-
41
- def assert(value)
42
- notify(:assert, value)
77
+ wrapper = MicroTest::TestWrapper.new(self, desc, &block)
78
+ wrapper.create_method(:before, &@before) if @before
79
+ wrapper.create_method(:after, &@after) if @after
80
+ tests << wrapper
43
81
  end
44
82
 
45
83
  end
@@ -0,0 +1,136 @@
1
+ module MicroTest
2
+
3
+ # A wrapper class for individual tests.
4
+ # Exists for the purpose of isolating the test method inside of a
5
+ # Celluloid Actor to support asynchronous test runs.
6
+ class TestWrapper
7
+ include Celluloid
8
+ attr_reader :test_class, :desc, :asserts, :duration
9
+
10
+ # Constructor.
11
+ # @param [MicroTest::Test] test_class The test class that defines the test being wrapped.
12
+ # @param [String] desc The test description.
13
+ # @yield The block that defines the test code.
14
+ def initialize(test_class, desc, &block)
15
+ @test_class = test_class
16
+ @desc = desc
17
+ create_method(:test, &block)
18
+ reset
19
+ end
20
+
21
+ # Creates a method on this instance.
22
+ # @param [Symbol] name The name of the method.
23
+ # @yield The block of code that will serve as the method's implementation.
24
+ def create_method(name, &block)
25
+ eigen = class << self; self; end
26
+ eigen.send(:define_method, name, &block)
27
+ end
28
+
29
+ # callback stubs
30
+ def before; end
31
+ def after; end
32
+
33
+ # Runs the test code.
34
+ # @formatter [MicroTest::Formatter] The formatter to use.
35
+ # @options [Hash]
36
+ def invoke(formatter, options={})
37
+ reset
38
+ @formatter = formatter
39
+ @options = options
40
+ @formatter.before_test(self)
41
+ start = Time.now
42
+ before
43
+ test
44
+ @invoked = true
45
+ after
46
+ @duration = Time.now - start
47
+ @formatter.after_test(self)
48
+ end
49
+
50
+ # A basic assert method to be used within tests.
51
+ #
52
+ # @param [Object] value The value to assert.
53
+ #
54
+ # @example
55
+ # class SimpleTest < MicroTest::Test
56
+ # test "common sense" do
57
+ # assert 1 > 0
58
+ # end
59
+ # end
60
+ def assert(value)
61
+ @asserts << assert_info(caller).merge(:value => value)
62
+
63
+ if !value
64
+ binding.pry(:quiet => true) if @options[:pry]
65
+ # I don't really like the coupling to the runner here
66
+ # but I couldn't get an observer to work with celluloid & pry
67
+ MicroTest::Runner.exit = true if @options[:fail_fast]
68
+ end
69
+
70
+ value
71
+ end
72
+
73
+ # Indicates if this test has finished running.
74
+ # @return [Boolean]
75
+ def finished?
76
+ !@duration.nil?
77
+ end
78
+
79
+ # Indicates if this test passed.
80
+ def passed?
81
+ return true if !@invoked || @asserts.empty?
82
+ return false if @asserts.empty?
83
+ @asserts.map{ |a| !!a[:value] }.uniq == [true]
84
+ end
85
+
86
+ # Returns a list of all failed asserts.
87
+ def failed_asserts
88
+ return [] if passed?
89
+ @asserts.select { |a| !a[:value] }
90
+ end
91
+
92
+ # Resets this test in preparation for a clean test run.
93
+ def reset
94
+ @invoked = false
95
+ @asserts = []
96
+ @duration = nil
97
+ end
98
+
99
+ private
100
+
101
+ # Builds a Hash of assert information for the given call stack.
102
+ #
103
+ # @param [Array<String>] stack The call stack to extract info from.
104
+ #
105
+ # @example
106
+ # {
107
+ # :file_path => "/path/to/test_file.rb",
108
+ # :line_num => 100,
109
+ # :line => " assert 'something' do"
110
+ # }
111
+ #
112
+ # @return [Hash]
113
+ def assert_info(stack)
114
+ file_path = stack[0][0, stack[0].index(/:[0-9]+:/)]
115
+ lines = MicroTest::Test.files[file_path]
116
+ line_num = line_number(stack, 0)
117
+ line_index = line_num - 1
118
+ line = lines[line_index]
119
+ {
120
+ :file_path => file_path,
121
+ :lines => lines,
122
+ :line_num => line_num,
123
+ :line => line
124
+ }
125
+ end
126
+
127
+ # Returns a line number from a call stack.
128
+ # @param [Array<String>] stack The call stack to pull a path from.
129
+ # @param [Integer] index The index of the call stack entry to use.
130
+ # @return [String]
131
+ def line_number(stack, index)
132
+ stack[index].scan(/:[0-9]+:/).first.gsub(/:/, "").to_i
133
+ end
134
+
135
+ end
136
+ end
@@ -1,3 +1,3 @@
1
1
  module MicroTest
2
- VERSION = "0.2.8"
2
+ VERSION = "0.3.0"
3
3
  end
data/lib/micro_test.rb CHANGED
@@ -1,9 +1,3 @@
1
- require "bundler"
2
- Bundler.require :default
3
- Bundler.require :debug
4
-
1
+ require "celluloid"
5
2
  path = File.join(File.dirname(__FILE__), "micro_test", "*.rb")
6
3
  Dir[path].each { |file| require file }
7
-
8
- MicroTest::Test.add_observer MicroTest::Runner
9
-
@@ -0,0 +1,45 @@
1
+ unless ENV["MT_DEMO"]
2
+ class ColorTest < MicroTest::Test
3
+ class Crayon
4
+ include MicroTest::Color
5
+ end
6
+ CRAYON = Crayon.new
7
+
8
+ test "red" do
9
+ assert MicroTest::Color.red("foo") == "\e[31mfoo\e[0m"
10
+ assert ColorTest::CRAYON.red("foo") == "\e[31mfoo\e[0m"
11
+ end
12
+
13
+ test "green" do
14
+ assert MicroTest::Color.green("foo") == "\e[32mfoo\e[0m"
15
+ assert ColorTest::CRAYON.green("foo") == "\e[32mfoo\e[0m"
16
+ end
17
+
18
+ test "yellow" do
19
+ assert MicroTest::Color.yellow("foo") == "\e[33mfoo\e[0m"
20
+ assert ColorTest::CRAYON.yellow("foo") == "\e[33mfoo\e[0m"
21
+ end
22
+
23
+ test "blue" do
24
+ assert MicroTest::Color.blue("foo") == "\e[34mfoo\e[0m"
25
+ assert ColorTest::CRAYON.blue("foo") == "\e[34mfoo\e[0m"
26
+ end
27
+
28
+ test "magenta" do
29
+ assert MicroTest::Color.magenta("foo") == "\e[35mfoo\e[0m"
30
+ assert ColorTest::CRAYON.magenta("foo") == "\e[35mfoo\e[0m"
31
+ end
32
+
33
+ test "cyan" do
34
+ assert MicroTest::Color.cyan("foo") == "\e[36mfoo\e[0m"
35
+ assert ColorTest::CRAYON.cyan("foo") == "\e[36mfoo\e[0m"
36
+ end
37
+
38
+ test "white" do
39
+ assert MicroTest::Color.white("foo") == "\e[37mfoo\e[0m"
40
+ assert ColorTest::CRAYON.white("foo") == "\e[37mfoo\e[0m"
41
+ end
42
+
43
+ end
44
+
45
+ end
@@ -0,0 +1,28 @@
1
+ if ENV["MT_DEMO"]
2
+ class TestCPULatency < MicroTest::Test
3
+
4
+ before do
5
+ @count = 35
6
+ end
7
+
8
+ test "fibonacci recursion 1" do
9
+ @count.times { |i| TestCPULatency.fib(i) }
10
+ assert true
11
+ end
12
+
13
+ test "fibonacci recursion 2" do
14
+ @count.times { |i| TestCPULatency.fib(i) }
15
+ assert true
16
+ end
17
+
18
+ test "fibonacci recursion 3" do
19
+ @count.times { |i| TestCPULatency.fib(i) }
20
+ assert true
21
+ end
22
+
23
+ def self.fib(n)
24
+ n < 2 ? n : fib(n-1) + fib(n-2)
25
+ end
26
+
27
+ end
28
+ end
data/test/fail_test.rb ADDED
@@ -0,0 +1,16 @@
1
+ if ENV["MT_DEMO"]
2
+ class Fail < MicroTest::Test
3
+
4
+ before do
5
+ @var = "fubar"
6
+ end
7
+
8
+ test "fail on purpose" do
9
+ # Failing on purpose for the demo.
10
+ # Use pry to check out the current binding.
11
+ # For example, type @var.
12
+ assert false
13
+ end
14
+
15
+ end
16
+ end
@@ -0,0 +1,20 @@
1
+ if ENV["MT_DEMO"]
2
+ class TestIOLatency < MicroTest::Test
3
+
4
+ test "sleep 1 sec" do
5
+ sleep 1
6
+ assert true
7
+ end
8
+
9
+ test "sleep another 1 sec" do
10
+ sleep 1
11
+ assert true
12
+ end
13
+
14
+ test "sleep a third 1 sec" do
15
+ sleep 1
16
+ assert true
17
+ end
18
+
19
+ end
20
+ end
@@ -0,0 +1,13 @@
1
+ unless ENV["MT_DEMO"]
2
+ class TestRunner < MicroTest::Test
3
+
4
+ before do
5
+ @runner = MicroTest::Runner.new(MicroTest::Formatter.new)
6
+ end
7
+
8
+ test ".running" do
9
+ assert true
10
+ end
11
+
12
+ end
13
+ end
data/test/test_test.rb CHANGED
@@ -1,117 +1,67 @@
1
- class TestTest < MicroTest::Test
1
+ unless ENV["MT_DEMO"]
2
+ class TestTest < MicroTest::Test
2
3
 
3
- before :each do
4
- @Test = Class.new(MicroTest::Test)
5
- end
6
-
7
- test "interface" do
8
- assert @Test.respond_to?(:inherited)
9
- assert @Test.respond_to?(:notify)
10
- assert @Test.respond_to?(:callbacks)
11
- assert @Test.respond_to?(:invoke)
12
- assert @Test.respond_to?(:before)
13
- assert @Test.respond_to?(:after)
14
- assert @Test.respond_to?(:tests)
15
- assert @Test.respond_to?(:test)
16
- assert @Test.respond_to?(:assert)
17
- end
18
-
19
- test "callbacks data structure" do
20
- assert @Test.callbacks.is_a?(Hash)
21
- assert @Test.callbacks[:before].is_a?(Hash)
22
- assert @Test.callbacks[:after].is_a?(Hash)
23
- end
24
-
25
- test "notify" do
26
- observer = Class.new do
27
- def update(event, arg)
28
- (@events ||= {})[event] = arg
4
+ before do
5
+ TestTest.instance_eval { @subclasses = [] }
6
+ TestTest.instance_eval { @tests = [] }
7
+ @Example = Class.new(TestTest) do
8
+ before {}
9
+ after {}
10
+ test("truth") { assert true }
29
11
  end
12
+ @before_callback = @Example.before
13
+ @after_callback = @Example.after
30
14
  end
31
- o = observer.new
32
- MicroTest::Test.add_observer o
33
- MicroTest::Test.notify(:foo, :bar)
34
- events = o.instance_eval { @events }
35
- assert events[:foo] == :bar
36
- end
37
15
 
38
- test "notification when inherited" do
39
- observer = Class.new do
40
- def update(event, arg)
41
- (@events ||= {})[event] = arg
42
- end
16
+ test "adds subclass" do
17
+ assert TestTest.subclasses.length == 1
18
+ assert TestTest.subclasses[0] == @Example
43
19
  end
44
- o = observer.new
45
- MicroTest::Test.add_observer o
46
- t = Class.new(MicroTest::Test)
47
- events = o.instance_eval { @events }
48
- assert events[:add_test_class] == t
49
- end
50
20
 
51
- test "callbacks" do
52
- a = lambda {}
53
- b = lambda {}
54
- c = lambda {}
55
- d = lambda {}
56
- @Test.before(:all, &a)
57
- @Test.before(:each, &b)
58
- @Test.after(:each, &c)
59
- @Test.after(:all, &d)
60
- assert @Test.callbacks[:before][:all].equal?(a)
61
- assert @Test.callbacks[:before][:each].equal?(b)
62
- assert @Test.callbacks[:after][:each].equal?(c)
63
- assert @Test.callbacks[:after][:all].equal?(d)
64
- end
65
-
66
- test "invoke callbacks" do
67
- before_all = false
68
- before_each = false
69
- after_each = false
70
- after_all = false
71
- @Test.before(:all) { before_all = true }
72
- @Test.before(:each) { before_each = true }
73
- @Test.after(:each) { after_each = true }
74
- @Test.after(:all) { after_all = true }
75
- @Test.invoke(:before, :all)
76
- @Test.invoke(:before, :each)
77
- @Test.invoke(:after, :each)
78
- @Test.invoke(:after, :all)
79
- assert before_all
80
- assert before_each
81
- assert after_each
82
- assert after_all
83
- end
21
+ test "stores subclasses" do
22
+ assert TestTest.subclasses.is_a?(Array)
23
+ assert TestTest.subclasses.length == 1
24
+ assert TestTest.subclasses.first == @Example
25
+ end
84
26
 
85
- test "tests" do
86
- assert MicroTest::Test.tests.is_a?(Hash)
87
- end
27
+ test "stores tests" do
28
+ assert @Example.tests.is_a?(Array)
29
+ assert @Example.tests.length == 1
30
+ assert @Example.tests.first.is_a?(MicroTest::TestWrapper)
31
+ end
88
32
 
89
- test "add tests" do
90
- a = lambda {}
91
- b = lambda {}
92
- c = lambda {}
93
- @Test.test("a", &a)
94
- @Test.test("b", &b)
95
- @Test.test("c", &c)
96
- assert @Test.tests["a"].equal?(a)
97
- assert @Test.tests["b"].equal?(b)
98
- assert @Test.tests["c"].equal?(c)
99
- end
33
+ test "stores files" do
34
+ file = __FILE__
35
+ assert TestTest.files[file].is_a?(Array)
36
+ assert TestTest.files[file].select{ |l| l.start_with?(" assert TestTest.files[file].select{ |l| l.start_with?(") }.length > 0
37
+ end
100
38
 
101
- test "assert" do
102
- observer = Class.new do
103
- def update(event, arg)
104
- (@events ||= {})[event] = arg
39
+ test ".reset" do
40
+ @Example.tests.first.instance_eval do
41
+ @asserts = nil
42
+ @duration = nil
105
43
  end
44
+ @Example.reset
45
+ assert @Example.tests.length == 1
46
+ assert @Example.tests.first.asserts == []
47
+ assert @Example.tests.first.duration == nil
48
+ end
49
+
50
+ test ".before" do
51
+ assert @Example.instance_eval { @before } == @before_callback
106
52
  end
107
- o = observer.new
108
- MicroTest::Test.add_observer o
109
- @Test.test "assert test" do
110
- assert true
53
+
54
+ test ".after" do
55
+ assert @Example.instance_eval { @after } == @after_callback
111
56
  end
112
- @Test.tests["assert test"].call
113
- events = o.instance_eval { @events }
114
- assert events[:assert] == true
115
- end
116
57
 
58
+ test "tests" do
59
+ t = lambda {}
60
+ @Example.send :test, :add_a_test, &t
61
+ assert @Example.tests.length == 2
62
+ assert @Example.tests.last.is_a?(MicroTest::TestWrapper)
63
+ assert @Example.tests.last.desc == :add_a_test
64
+ end
65
+
66
+ end
117
67
  end