butterfly_net 0.0.1

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