butterfly_net 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/README ADDED
@@ -0,0 +1,50 @@
1
+ butterfly_net
2
+
3
+ by Chris Smith
4
+
5
+ http://github.com/quartzmo/butterfly_net
6
+
7
+ DESCRIPTION
8
+
9
+ IRB history captured as Test::Unit tests. (RSpec and others hopefully soon to come.)
10
+
11
+ INSTALL
12
+
13
+ 1. gem sources -a http://gemcutter.org
14
+ 2. sudo gem install butterfly_net
15
+ 3. To automatically require butterfly_net on every IRB session, add the following to your ~/.irbrc:
16
+
17
+ require 'rubygems'
18
+ require 'butterfly_net'
19
+
20
+ USAGE
21
+
22
+ Command methods:
23
+
24
+ bn, bn_open - Open an active test case, with optional string arg file_name ('.rb' is appended if needed)
25
+ bnc, bn_close - Close the active test case, and write the output to a file
26
+
27
+ LICENSE
28
+
29
+ (The MIT License)
30
+
31
+ Copyright (c) 2010 Chris Smith
32
+
33
+ Permission is hereby granted, free of charge, to any person obtaining
34
+ a copy of this software and associated documentation files (the
35
+ 'Software'), to deal in the Software without restriction, including
36
+ without limitation the rights to use, copy, modify, merge, publish,
37
+ distribute, sublicense, and/or sell copies of the Software, and to
38
+ permit persons to whom the Software is furnished to do so, subject to
39
+ the following conditions:
40
+
41
+ The above copyright notice and this permission notice shall be
42
+ included in all copies or substantial portions of the Software.
43
+
44
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
45
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
46
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
47
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
48
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
49
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
50
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,6 @@
1
+ require 'butterfly_net/file_writer'
2
+ require 'butterfly_net/test_unit_method'
3
+ require 'butterfly_net/test_unit_adapter'
4
+ require 'butterfly_net/commands'
5
+
6
+ include ButterflyNet::Commands
@@ -0,0 +1,47 @@
1
+ #
2
+ # Command shortcuts:
3
+ #
4
+ # bn - open a new Butterfly Net test case, with optional name
5
+ # bnc - close Butterfly Net test case, writing output file
6
+ # bns - add preceding line to setup method, instead of current assertion set # todo
7
+ # bnt - add preceding line to teardown method, instead of current assertion set # todo
8
+ #
9
+ module ButterflyNet
10
+ module Commands
11
+
12
+ def bn_open(file_name=nil)
13
+ @file.close if @file
14
+ check_for_readline
15
+ start_index = Readline::HISTORY.empty? ? 0 : (Readline::HISTORY.size - 1)
16
+ file_name ||= "butterfly_net_#{Time.now.strftime("%Y%m%d%H%M%S")}.rb"
17
+ file_name += ".rb" unless file_name =~ /.rb$/
18
+ @file = FileWriter.new(file_name, start_index)
19
+ Kernel.at_exit { puts @file.close if @file; @file = nil }
20
+ puts "butterfly_net: #{file_name} opened at Readline::HISTORY ##{start_index}"
21
+ true
22
+ end
23
+
24
+ def bn_close
25
+ check_for_readline
26
+ if @file
27
+ status = @file.close
28
+ @file = nil
29
+ status
30
+ else
31
+ puts "butterfly_net: First invoke 'bn' or 'bn_open' to begin a session"
32
+ false
33
+ end
34
+ end
35
+
36
+
37
+ alias :bn :bn_open
38
+ alias :bnc :bn_close
39
+
40
+ private
41
+
42
+ def check_for_readline
43
+ raise "butterfly_net: Readline::HISTORY required!" unless defined? Readline::HISTORY
44
+ end
45
+
46
+ end
47
+ end
@@ -0,0 +1,42 @@
1
+ module ButterflyNet
2
+ class FileWriter
3
+
4
+ attr_accessor :end_index
5
+ attr_reader :start_index
6
+
7
+ def initialize(file_name, start_index)
8
+ @file_name = file_name
9
+ @start_index = start_index
10
+ end
11
+
12
+ def new_assertion_set
13
+
14
+ end
15
+
16
+
17
+ def close
18
+ lines = Readline::HISTORY.to_a
19
+ @end_index ||= Readline::HISTORY.size - 3 # in case of no call to 'bn_close'
20
+ if @end_index < 0 || lines[@start_index..@end_index].empty?
21
+ puts "butterfly_net: #{@file_name} closed, no file written to disk"
22
+ false
23
+ else
24
+ adapter = TestUnitAdapter.new
25
+
26
+ lines[@start_index..@end_index].each do |line|
27
+ adapter.add_command(line)
28
+ end
29
+
30
+ adapter.create_file(@file_name)
31
+
32
+ puts "butterfly_net: #{@file_name} closed after Readline::HISTORY ##{@end_index}"
33
+ true
34
+ end
35
+ rescue Exception => msg
36
+ puts "butterfly_net: Error generating tests: #{msg}"
37
+ false
38
+ end
39
+
40
+ end
41
+
42
+ end
@@ -0,0 +1,44 @@
1
+ module ButterflyNet
2
+ class TestUnitAdapter
3
+
4
+ attr_reader :assertion_sets
5
+
6
+ def initialize
7
+ @assertion_sets = []
8
+ end
9
+
10
+ def add_command(line)
11
+ @assertion_sets << TestUnitMethod.new unless @assertion_sets.last
12
+ @assertion_sets.last << line
13
+ end
14
+
15
+ def close_assertion_set
16
+ @assertion_sets << TestUnitMethod.new
17
+ end
18
+
19
+ def create_file(filename)
20
+ return nil if @assertion_sets.empty?
21
+ file = File.open(filename, 'w+')
22
+ file.puts "require \"test/unit\"\n\nclass TempTest < Test::Unit::TestCase"
23
+
24
+ test_methods.each do |test_method|
25
+ file.puts "\n #{test_method}"
26
+ end
27
+
28
+ file.puts "\nend"
29
+ ensure
30
+ file.close if file # todo: closing is good, but prevent writing partial data in case of exception
31
+ end
32
+
33
+ def test_methods
34
+ method_strings = []
35
+ @assertion_sets.each_with_index do |assertions, i|
36
+
37
+
38
+ method_strings << assertions.text(i + 1)
39
+ end
40
+ method_strings
41
+ end
42
+
43
+ end
44
+ end
@@ -0,0 +1,89 @@
1
+ module ButterflyNet
2
+ class TestUnitMethod
3
+
4
+ def initialize
5
+ @lines = []
6
+ end
7
+
8
+ def <<(line)
9
+ @lines << line
10
+ end
11
+
12
+ def self.simple_assignment_only?(line) # todo: extract to line class
13
+ line =~ /[^=<>!*%\/+-\\|&]=[^=~]/
14
+ end
15
+
16
+ def self.assertion(expected, line) # todo: extract to assertion class
17
+ if expected && simple_assignment_only?(line)
18
+ line
19
+ elsif expected && expected == line # type not supported, assume object inequality
20
+ "assert_not_equal((#{expected}), #{line})"
21
+ elsif expected == "true" # use simple assert() for true boolean expressions
22
+ "assert(#{line})"
23
+ elsif expected
24
+ "assert_equal(#{expected}, #{line})"
25
+ else
26
+ nil
27
+ end
28
+ end
29
+
30
+ def text_from_expression(line, i)
31
+ expected_assertion(i)
32
+ end
33
+
34
+ def text(method_name)
35
+ method_string = "def test_#{method_name}\n"
36
+ @lines.each_with_index do |line, i|
37
+ text = text_from_expression(line, i)
38
+ method_string += " #{text}\n" if text
39
+ end
40
+ method_string += " end"
41
+ end
42
+
43
+ def expected_assertion(current_i)
44
+
45
+ current_line = @lines[current_i]
46
+ commands = current_line
47
+ start_i = current_i
48
+
49
+ begin
50
+ retval = eval commands
51
+ rescue
52
+ start_i -= 1
53
+ commands = @lines[start_i..current_i].join("\n")
54
+
55
+ if start_i < 0
56
+ puts "failure evaluating: eval[#{start_i}..#{current_i}] : #{commands}\n"
57
+ return nil
58
+ else
59
+ retry
60
+ end
61
+ end
62
+ if retval && TestUnitMethod.simple_assignment_only?(current_line)
63
+ current_line
64
+
65
+ elsif instances_equal_by_value?(retval) # expression result supports value equality
66
+
67
+ if retval == true # use simple assert() for true boolean expressions
68
+ "assert(#{current_line})"
69
+ elsif retval.nil?
70
+ "assert_nil(#{current_line})"
71
+ else
72
+ "assert_equal(#{retval.inspect}, #{current_line})"
73
+ end
74
+
75
+ else
76
+
77
+ # any other sort of object is handled as a not equal assertion
78
+ "assert_not_equal((#{current_line}), #{current_line})"
79
+ end
80
+
81
+ end
82
+
83
+ def instances_equal_by_value?(instance)
84
+ instance == instance.dup rescue true # can't dup Fixnum, et al...
85
+ end
86
+
87
+
88
+ end
89
+ end
@@ -0,0 +1,5 @@
1
+ require "test/unit"
2
+ $: << File.expand_path(File.dirname(__FILE__))
3
+ $: << File.join(File.expand_path(File.dirname(__FILE__)), File.join("..", "lib"))
4
+ require "test_unit_method_test"
5
+ require "test_unit_adapter_test"
@@ -0,0 +1,71 @@
1
+ require "test/unit"
2
+ $: << File.expand_path(File.dirname(__FILE__))
3
+ $: << File.join(File.expand_path(File.dirname(__FILE__)), File.join("..", "lib"))
4
+ require "butterfly_net/test_unit_method"
5
+ require "butterfly_net/test_unit_adapter"
6
+
7
+ class TestUnitAdapterTest < Test::Unit::TestCase
8
+ include ButterflyNet
9
+
10
+ def setup
11
+ @adapter = TestUnitAdapter.new
12
+ end
13
+
14
+ def teardown
15
+ File.delete 'temp_test.rb' if File.exists? 'temp_test.rb' # delete old in test, since impl should append, not overwrite
16
+ end
17
+
18
+ def test_expression_eval
19
+ # assert eval("assert_equal(2, 1 + 1)") todo figure out how to eval assertions here
20
+ end
21
+
22
+ def test_test_methods_single
23
+ @adapter.add_command("1 + 1")
24
+ expected = "def test_1\n assert_equal(2, 1 + 1)\n end"
25
+ @adapter.test_methods
26
+ assert_equal expected, @adapter.test_methods.first
27
+ end
28
+
29
+ def test_test_methods_2_assertions_1_method
30
+ @adapter.add_command("1 + 1")
31
+ @adapter.add_command("1 + 2")
32
+ expected = "def test_1\n assert_equal(2, 1 + 1)\n assert_equal(3, 1 + 2)\n end"
33
+ @adapter.test_methods
34
+ assert_equal expected, @adapter.test_methods.last
35
+ end
36
+
37
+ def test_test_methods_variable_in_assertion
38
+ @adapter.add_command("a = 1")
39
+ @adapter.add_command("a + 2")
40
+ expected = "def test_1\n a = 1\n assert_equal(3, a + 2)\n end"
41
+ @adapter.test_methods
42
+ assert_equal expected, @adapter.test_methods.last
43
+ end
44
+
45
+ def test_test_methods_numbering
46
+ @adapter.add_command("1 + 1")
47
+ @adapter.close_assertion_set
48
+ @adapter.add_command("1 + 2")
49
+ expected = "def test_2\n assert_equal(3, 1 + 2)\n end"
50
+ @adapter.test_methods
51
+ assert_equal expected, @adapter.test_methods.last
52
+ end
53
+
54
+ def test_create_file
55
+ @adapter.add_command("1 + 1")
56
+ expected = <<-EOF
57
+ require "test/unit"
58
+
59
+ class TempTest < Test::Unit::TestCase
60
+
61
+ def test_1
62
+ assert_equal(2, 1 + 1)
63
+ end
64
+
65
+ end
66
+ EOF
67
+ @adapter.create_file('temp_test.rb')
68
+ assert_equal expected, File.open('temp_test.rb').readlines.join('')
69
+ end
70
+
71
+ end
@@ -0,0 +1,187 @@
1
+ require "test/unit"
2
+ $: << File.expand_path(File.dirname(__FILE__))
3
+ $: << File.join(File.expand_path(File.dirname(__FILE__)), File.join("..", "lib"))
4
+ require "butterfly_net/test_unit_method"
5
+
6
+ class TestUnitMethodTest < Test::Unit::TestCase
7
+ include ButterflyNet
8
+
9
+ def setup
10
+ @method = TestUnitMethod.new
11
+ end
12
+
13
+ def test_simple_assignment_only_true
14
+ assert TestUnitMethod.simple_assignment_only? "a=1"
15
+ end
16
+
17
+ # def test_simple_assignment_only_true_orequals todo: solve orequals in regex
18
+ # assert TestUnitMethod.simple_assignment_only? "a ||= 1"
19
+ # end
20
+
21
+ def test_simple_assignment_only_true_whitespace
22
+ assert TestUnitMethod.simple_assignment_only? "a = 1"
23
+ end
24
+
25
+ def test_simple_assignment_only_true_contains_equals
26
+ assert TestUnitMethod.simple_assignment_only? "a = (1 == 1)"
27
+ end
28
+
29
+ def test_simple_assignment_only_false
30
+ assert !TestUnitMethod.simple_assignment_only?("a + 1")
31
+ end
32
+
33
+ def test_simple_assignment_only_false_with_equals
34
+ assert !TestUnitMethod.simple_assignment_only?("a == 1")
35
+ end
36
+
37
+ def test_simple_assignment_only_false_threequals
38
+ assert !TestUnitMethod.simple_assignment_only?("a===1")
39
+ end
40
+
41
+ def test_simple_assignment_only_false_regex
42
+ assert !TestUnitMethod.simple_assignment_only?("'a' =~ /a/")
43
+ end
44
+
45
+ def test_simple_assignment_only_false_regex
46
+ assert !TestUnitMethod.simple_assignment_only?("'a' =~ /a/")
47
+ end
48
+
49
+ def test_simple_assignment_only_false_lessthanequals
50
+ assert !TestUnitMethod.simple_assignment_only?("1 <= 1")
51
+ end
52
+
53
+ def test_simple_assignment_only_false_greaterthanequals
54
+ assert !TestUnitMethod.simple_assignment_only?("1 >= 1")
55
+ end
56
+
57
+ def test_simple_assignment_only_false_flyingsaucer
58
+ assert !TestUnitMethod.simple_assignment_only?("1 <=> 1")
59
+ end
60
+
61
+ def test_simple_assignment_only_false_notequal
62
+ assert !TestUnitMethod.simple_assignment_only?("1 != 0")
63
+ end
64
+
65
+ def test_simple_assignment_only_false_modequal
66
+ assert !TestUnitMethod.simple_assignment_only?("a %= 1")
67
+ end
68
+
69
+ def test_simple_assignment_only_false_orequal
70
+ assert !TestUnitMethod.simple_assignment_only?("a |= 1")
71
+ end
72
+
73
+ def test_simple_assignment_only_false_plusequal
74
+ assert !TestUnitMethod.simple_assignment_only?("a += 1")
75
+ end
76
+
77
+ def test_simple_assignment_only_false_minusequal
78
+ assert !TestUnitMethod.simple_assignment_only?("a -= 1")
79
+ end
80
+
81
+ def test_simple_assignment_only_false_divideequal
82
+ assert !TestUnitMethod.simple_assignment_only?("a /= 1")
83
+ end
84
+
85
+ def test_simple_assignment_only_false_andequal
86
+ assert !TestUnitMethod.simple_assignment_only?("a &= 1")
87
+ end
88
+
89
+ def test_simple_assignment_only_false_shiftrightequal
90
+ assert !TestUnitMethod.simple_assignment_only?("a >>= 1")
91
+ end
92
+
93
+ def test_simple_assignment_only_false_shiftleftequal
94
+ assert !TestUnitMethod.simple_assignment_only?("a <<= 1")
95
+ end
96
+
97
+ def test_simple_assignment_only_false_timesequal
98
+ assert !TestUnitMethod.simple_assignment_only?("a *= 1")
99
+ end
100
+
101
+
102
+ def test_expected_boolean
103
+ line = "1 == 1"
104
+ @method << line
105
+ assert_equal("assert(#{line})", @method.expected_assertion(0))
106
+ end
107
+
108
+ def test_assertion_fixnum
109
+ line = "1"
110
+ @method << line
111
+ assert_equal("assert_equal(#{line}, #{line})", @method.expected_assertion(0))
112
+ end
113
+
114
+ def test_assertion_boolean_false
115
+ line = "1 != 1"
116
+ @method << line
117
+ assert_equal("assert_equal(false, #{line})", @method.expected_assertion(0))
118
+ end
119
+
120
+
121
+
122
+ def test_array_add
123
+ @method << "a = []"
124
+ line2 = "a << \"1\""
125
+ @method << line2
126
+ assert_equal("assert_equal([\"1\"], #{line2})", @method.expected_assertion(1))
127
+ end
128
+
129
+ def test_butterfly_net
130
+ @method << "method = TestUnitMethod.new"
131
+ @method << "method << \"1 + 1\""
132
+ assert_equal("assert_equal([\"1 + 1\"], method << \"1 + 1\")", @method.expected_assertion(1))
133
+ end
134
+
135
+
136
+
137
+ def test_text_from_expression_boolean
138
+ line = "1 == 1"
139
+ @method << line
140
+ assert_equal("assert(#{line})", @method.text_from_expression(line, 0))
141
+ end
142
+
143
+ def test_text_from_expression_string
144
+ line = "'a' + 'b'"
145
+ @method << line
146
+ assert_equal("assert_equal(\"ab\", #{line})", @method.text_from_expression(line, 0))
147
+ end
148
+
149
+ def test_text_from_expression_nil
150
+ line = "[].first"
151
+ @method << line
152
+ assert_equal("assert_nil(#{line})", @method.text_from_expression(line, 0))
153
+ end
154
+
155
+ def test_text_from_expression_boolean
156
+ line = "1 == 1"
157
+ @method << line
158
+ assert_equal("assert(#{line})", @method.text_from_expression(line, 0))
159
+ end
160
+
161
+ def test_text_from_expression_object_not_equal
162
+ line = "Object.new"
163
+ @method << line
164
+ assert_equal("assert_not_equal((#{line}), #{line})", @method.text_from_expression(line, 0))
165
+ end
166
+
167
+ def test_text_from_expression_array
168
+ line = "([1,2,3])[0..0]"
169
+ @method << line
170
+ assert_equal("assert_equal([1], #{line})", @method.text_from_expression(line, 0))
171
+ end
172
+
173
+ def test_text_from_expression_illegal_input
174
+ line = "BADCOMMAND"
175
+ @method << line
176
+ assert_nil @method.text_from_expression(line, 0)
177
+ end
178
+
179
+ # non-project tests (scratchpad)
180
+
181
+ def test_eval_scope
182
+ value = eval "a = []\na << 1", Proc.new {}.binding
183
+ assert_equal([1], value)
184
+ assert_equal([1,1], eval("a << 1", Proc.new {}.binding))
185
+ end
186
+
187
+ end
metadata ADDED
@@ -0,0 +1,70 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: butterfly_net
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Chris Smith
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2010-02-20 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: |+
17
+ IRB history captured as Test::Unit tests. (RSpec and others hopefully soon to come.)
18
+
19
+ Command methods:
20
+
21
+ bn, bn_open - Open an active test case, with optional string arg file_name ('.rb' is appended if needed)
22
+ bnc, bn_close - Close the active test case, and write the output to a file
23
+
24
+ email: quartzmo@gmail.com
25
+ executables: []
26
+
27
+ extensions: []
28
+
29
+ extra_rdoc_files: []
30
+
31
+ files:
32
+ - README
33
+ - lib/butterfly_net/commands.rb
34
+ - lib/butterfly_net/file_writer.rb
35
+ - lib/butterfly_net/test_unit_adapter.rb
36
+ - lib/butterfly_net/test_unit_method.rb
37
+ - lib/butterfly_net.rb
38
+ - test/butterfly_net_tests.rb
39
+ - test/test_unit_adapter_test.rb
40
+ - test/test_unit_method_test.rb
41
+ has_rdoc: true
42
+ homepage: http://github.com/quartzmo/butterfly_net
43
+ licenses: []
44
+
45
+ post_install_message:
46
+ rdoc_options: []
47
+
48
+ require_paths:
49
+ - lib
50
+ required_ruby_version: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: "0"
55
+ version:
56
+ required_rubygems_version: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: "0"
61
+ version:
62
+ requirements: []
63
+
64
+ rubyforge_project:
65
+ rubygems_version: 1.3.5
66
+ signing_key:
67
+ specification_version: 3
68
+ summary: IRB history captured as Test::Unit tests. (RSpec and others hopefully soon to come.)
69
+ test_files:
70
+ - test/butterfly_net_tests.rb