micro_test 0.2.8 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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